diff options
Diffstat (limited to 'usrp/fpga/sdr_lib')
51 files changed, 3492 insertions, 0 deletions
diff --git a/usrp/fpga/sdr_lib/adc_interface.v b/usrp/fpga/sdr_lib/adc_interface.v new file mode 100644 index 000000000..f18ffc104 --- /dev/null +++ b/usrp/fpga/sdr_lib/adc_interface.v @@ -0,0 +1,71 @@ + + +`include "../../firmware/include/fpga_regs_common.v" +`include "../../firmware/include/fpga_regs_standard.v" + +module adc_interface + (input clock, input reset, input enable, + input wire [6:0] serial_addr, input wire [31:0] serial_data, input serial_strobe, + input wire [11:0] rx_a_a, input wire [11:0] rx_b_a, input wire [11:0] rx_a_b, input wire [11:0] rx_b_b, + output wire [31:0] rssi_0, output wire [31:0] rssi_1, output wire [31:0] rssi_2, output wire [31:0] rssi_3, + output reg [15:0] ddc0_in_i, output reg [15:0] ddc0_in_q, + output reg [15:0] ddc1_in_i, output reg [15:0] ddc1_in_q, + output reg [15:0] ddc2_in_i, output reg [15:0] ddc2_in_q, + output reg [15:0] ddc3_in_i, output reg [15:0] ddc3_in_q, + output wire [3:0] rx_numchan); + + // Buffer at input to chip + reg [11:0] adc0,adc1,adc2,adc3; + always @(posedge clock) + begin + adc0 <= #1 rx_a_a; + adc1 <= #1 rx_b_a; + adc2 <= #1 rx_a_b; + adc3 <= #1 rx_b_b; + end + + // then scale and subtract dc offset + wire [3:0] dco_en; + wire [15:0] adc0_corr,adc1_corr,adc2_corr,adc3_corr; + + setting_reg #(`FR_DC_OFFSET_CL_EN) sr_dco_en(.clock(clock),.reset(reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data), + .out(dco_en)); + + rx_dcoffset #(`FR_ADC_OFFSET_0) rx_dcoffset0(.clock(clock),.enable(dco_en[0]),.reset(reset),.adc_in({adc0[11],adc0,3'b0}),.adc_out(adc0_corr), + .serial_addr(serial_addr),.serial_data(serial_data),.serial_strobe(serial_strobe)); + rx_dcoffset #(`FR_ADC_OFFSET_1) rx_dcoffset1(.clock(clock),.enable(dco_en[1]),.reset(reset),.adc_in({adc1[11],adc1,3'b0}),.adc_out(adc1_corr), + .serial_addr(serial_addr),.serial_data(serial_data),.serial_strobe(serial_strobe)); + rx_dcoffset #(`FR_ADC_OFFSET_2) rx_dcoffset2(.clock(clock),.enable(dco_en[2]),.reset(reset),.adc_in({adc2[11],adc2,3'b0}),.adc_out(adc2_corr), + .serial_addr(serial_addr),.serial_data(serial_data),.serial_strobe(serial_strobe)); + rx_dcoffset #(`FR_ADC_OFFSET_3) rx_dcoffset3(.clock(clock),.enable(dco_en[3]),.reset(reset),.adc_in({adc3[11],adc3,3'b0}),.adc_out(adc3_corr), + .serial_addr(serial_addr),.serial_data(serial_data),.serial_strobe(serial_strobe)); + + // Level sensing for AGC + rssi rssi_block_0 (.clock(clock),.reset(reset),.enable(enable),.adc(adc0),.rssi(rssi_0[15:0]),.over_count(rssi_0[31:16])); + rssi rssi_block_1 (.clock(clock),.reset(reset),.enable(enable),.adc(adc1),.rssi(rssi_1[15:0]),.over_count(rssi_1[31:16])); + rssi rssi_block_2 (.clock(clock),.reset(reset),.enable(enable),.adc(adc2),.rssi(rssi_2[15:0]),.over_count(rssi_2[31:16])); + rssi rssi_block_3 (.clock(clock),.reset(reset),.enable(enable),.adc(adc3),.rssi(rssi_3[15:0]),.over_count(rssi_3[31:16])); + + // And mux to the appropriate outputs + wire [3:0] ddc3mux,ddc2mux,ddc1mux,ddc0mux; + wire rx_realsignals; + + setting_reg #(`FR_RX_MUX) sr_rxmux(.clock(clock),.reset(reset),.strobe(serial_strobe),.addr(serial_addr), + .in(serial_data),.out({ddc3mux,ddc2mux,ddc1mux,ddc0mux,rx_realsignals,rx_numchan[3:1]})); + assign rx_numchan[0] = 1'b0; + + always @(posedge clock) + begin + ddc0_in_i <= #1 ddc0mux[1] ? (ddc0mux[0] ? adc3_corr : adc2_corr) : (ddc0mux[0] ? adc1_corr : adc0_corr); + ddc0_in_q <= #1 rx_realsignals ? 16'd0 : ddc0mux[3] ? (ddc0mux[2] ? adc3_corr : adc2_corr) : (ddc0mux[2] ? adc1_corr : adc0_corr); + ddc1_in_i <= #1 ddc1mux[1] ? (ddc1mux[0] ? adc3_corr : adc2_corr) : (ddc1mux[0] ? adc1_corr : adc0_corr); + ddc1_in_q <= #1 rx_realsignals ? 16'd0 : ddc1mux[3] ? (ddc1mux[2] ? adc3_corr : adc2_corr) : (ddc1mux[2] ? adc1_corr : adc0_corr); + ddc2_in_i <= #1 ddc2mux[1] ? (ddc2mux[0] ? adc3_corr : adc2_corr) : (ddc2mux[0] ? adc1_corr : adc0_corr); + ddc2_in_q <= #1 rx_realsignals ? 16'd0 : ddc2mux[3] ? (ddc2mux[2] ? adc3_corr : adc2_corr) : (ddc2mux[2] ? adc1_corr : adc0_corr); + ddc3_in_i <= #1 ddc3mux[1] ? (ddc3mux[0] ? adc3_corr : adc2_corr) : (ddc3mux[0] ? adc1_corr : adc0_corr); + ddc3_in_q <= #1 rx_realsignals ? 16'd0 : ddc3mux[3] ? (ddc3mux[2] ? adc3_corr : adc2_corr) : (ddc3mux[2] ? adc1_corr : adc0_corr); + end + +endmodule // adc_interface + + diff --git a/usrp/fpga/sdr_lib/bidir_reg.v b/usrp/fpga/sdr_lib/bidir_reg.v new file mode 100644 index 000000000..b12441252 --- /dev/null +++ b/usrp/fpga/sdr_lib/bidir_reg.v @@ -0,0 +1,29 @@ +// Bidirectional registers + +module bidir_reg + ( inout wire [15:0] tristate, + input wire [15:0] oe, + input wire [15:0] reg_val ); + + // This would be much cleaner if all the tools + // supported "for generate"........ + + assign tristate[0] = oe[0] ? reg_val[0] : 1'bz; + assign tristate[1] = oe[1] ? reg_val[1] : 1'bz; + assign tristate[2] = oe[2] ? reg_val[2] : 1'bz; + assign tristate[3] = oe[3] ? reg_val[3] : 1'bz; + assign tristate[4] = oe[4] ? reg_val[4] : 1'bz; + assign tristate[5] = oe[5] ? reg_val[5] : 1'bz; + assign tristate[6] = oe[6] ? reg_val[6] : 1'bz; + assign tristate[7] = oe[7] ? reg_val[7] : 1'bz; + assign tristate[8] = oe[8] ? reg_val[8] : 1'bz; + assign tristate[9] = oe[9] ? reg_val[9] : 1'bz; + assign tristate[10] = oe[10] ? reg_val[10] : 1'bz; + assign tristate[11] = oe[11] ? reg_val[11] : 1'bz; + assign tristate[12] = oe[12] ? reg_val[12] : 1'bz; + assign tristate[13] = oe[13] ? reg_val[13] : 1'bz; + assign tristate[14] = oe[14] ? reg_val[14] : 1'bz; + assign tristate[15] = oe[15] ? reg_val[15] : 1'bz; + +endmodule // bidir_reg + diff --git a/usrp/fpga/sdr_lib/bus_interface.v b/usrp/fpga/sdr_lib/bus_interface.v new file mode 100755 index 000000000..3f5f748d5 --- /dev/null +++ b/usrp/fpga/sdr_lib/bus_interface.v @@ -0,0 +1,213 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +// Interface to Cypress FX2 bus +// A packet is 512 Bytes. Each fifo line is 4 bytes +// Fifo has 1024 or 2048 lines + +module bus_interface + ( input usbclk, + input reset, + inout [15:0] usbdata, // TRISTATE + input wire [5:0] usbctl, + output wire [5:0] usbrdy, + output [31:0] txdata, + input [31:0] rxdata, + input txclk, + input txstrobe, + input rxclk, + input rxstrobe, + output [11:0] debugbus, + input clear_status + ); + + parameter IN_CHANNELS = 1; + parameter OUT_CHANNELS = 1; + parameter bitmask = (IN_CHANNELS*2)-1; + + wire have_space, have_pkt_rdy; + wire WR, RD, OE; + reg tx_underrun, rx_overrun; + + assign WR = usbctl[0]; + assign RD = usbctl[1]; + assign OE = usbctl[2]; + + assign usbrdy[0] = have_space; + assign usbrdy[1] = have_pkt_rdy; + assign usbrdy[2] = tx_underrun; + assign usbrdy[3] = rx_overrun; + + reg [IN_CHANNELS*2*16-1:0] fifo_in; + wire [OUT_CHANNELS*2*16-1:0] fifo_out; + + wire [15:0] usbdata_in = usbdata; + + reg select_out; + reg select_in; + + reg commit; + reg rd_next; + reg [15:0] usbdata_out; + wire [10:0] txfifolevel,rxfifolevel; + reg [8:0] write_count; + wire tx_empty; + wire tx_full; + wire rx_empty; + wire rx_full; + wire [31:0] txd; + wire rdreq; + + // Tri-state bus macro + bustri bustri(.data(usbdata_out), + .enabledt(OE), + .tridata(usbdata) ); + + ////////////////////////////////////////////// + // TX Side (USB --> DAC) + always @(posedge usbclk, posedge reset) + begin + if(reset) + begin + fifo_in <= #1 0; + write_count <= #1 0; + end + else + if(WR & ~write_count[8]) + begin + case(write_count[0]) + 1'b0 : fifo_in[31:16] <= #1 usbdata_in; // I + 1'b1 : fifo_in[15:0] <= #1 usbdata_in; // Q + endcase + write_count <= #1 write_count + 9'd1; + end + else + write_count <= #1 WR ? write_count : 9'b0; + end + + always @(posedge usbclk) + if(reset) + commit <= #1 1'b0; + else + if(write_count[0] && ~write_count[8] && WR) + commit <= #1 1'b1; + else + commit <= #1 1'b0; + + assign rdreq = txstrobe & !tx_empty; + assign txdata = tx_empty ? 32'b0 : txd; + + always @(posedge txclk) + if(reset) + tx_underrun <= 1'b0; + else if(txstrobe & tx_empty) + tx_underrun <= 1'b1; + else if(clear_status) + tx_underrun <= 1'b0; + + fifo_1c_2k txfifo (.data ( fifo_in ), + .wrreq ( commit ), + .wrclk ( usbclk ), + + .q ( txd ), + .rdreq ( rdreq), + .rdclk ( txclk ), + + .aclr ( reset ), + + .rdempty ( tx_empty ), + .rdusedw ( ), + .wrfull ( tx_full ), + .wrusedw ( txfifolevel ) + ); + + assign have_space = (txfifolevel <= (2048-128)); + + ////////////////////////////// + // Receive FIFO (ADC --> USB) + + always @(posedge rxclk) + if(reset) + rx_overrun <= 1'b0; + else if(rxstrobe & rx_full) + rx_overrun <= 1'b1; + else if(clear_status) + rx_overrun <= 1'b0; + + always @(select_out, fifo_out) + case(select_out) + 0 : usbdata_out = fifo_out[31:16]; // I + 1 : usbdata_out = fifo_out[15:0]; // Q + endcase + +/* + always @(posedge usbclk, posedge reset) + if(reset) + usbdata_out <= #1 16'b0; + else + if(select_out) + usbdata_out = fifo_out[31:16]; + else + usbdata_out = fifo_out[15:0]; + */ + + always @(negedge usbclk, posedge reset) + if(reset) + select_out <= #1 1'b0; + else if(~RD) + select_out <= #1 1'b0; + else + select_out <= #1 ~select_out; + + fifo_1c_2k rxfifo (.data ( rxdata ), // counter ), + .wrreq (rxstrobe & ~rx_full ), + .wrclk ( rxclk ), + + .q ( fifo_out ), + .rdreq ( select_out ),// & RD ), // FIXME + .rdclk ( usbclk ), + + .aclr ( reset ), + + .rdempty ( rx_empty ), + .rdusedw ( rxfifolevel ), + .wrfull ( rx_full ), + .wrusedw ( ) + ); + + assign have_pkt_rdy = (rxfifolevel >= 128); + + // Debugging Aids + assign debugbus[0] = tx_underrun; + assign debugbus[1] = rx_overrun; + assign debugbus[2] = tx_empty; + assign debugbus[3] = tx_full; + assign debugbus[4] = rx_empty; + assign debugbus[5] = rx_full; + assign debugbus[6] = txstrobe; + assign debugbus[7] = rxstrobe; + assign debugbus[8] = select_out; + assign debugbus[9] = rxstrobe & ~rx_full; + assign debugbus[10] = have_space; + assign debugbus[11] = have_pkt_rdy; + +endmodule // bus_interface + diff --git a/usrp/fpga/sdr_lib/cic_decim.v b/usrp/fpga/sdr_lib/cic_decim.v new file mode 100755 index 000000000..45b863f16 --- /dev/null +++ b/usrp/fpga/sdr_lib/cic_decim.v @@ -0,0 +1,106 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + +module cic_decim + ( clock,reset,enable,rate,strobe_in,strobe_out,signal_in,signal_out); + parameter bw = 16; + parameter N = 4; + parameter log2_of_max_rate = 8; + parameter maxbitgain = N * log2_of_max_rate; + + input clock; + input reset; + input enable; + input [7:0] rate; + input strobe_in,strobe_out; + input [bw-1:0] signal_in; + output [bw-1:0] signal_out; + reg [bw-1:0] signal_out; + + wire [bw+maxbitgain-1:0] signal_in_ext; + reg [bw+maxbitgain-1:0] integrator [0:N-1]; + reg [bw+maxbitgain-1:0] differentiator [0:N-1]; + reg [bw+maxbitgain-1:0] pipeline [0:N-1]; + reg [bw+maxbitgain-1:0] sampler; + + integer i; + + sign_extend #(bw,bw+maxbitgain) + ext_input (.in(signal_in),.out(signal_in_ext)); + + always @(posedge clock) + if(reset) + for(i=0;i<N;i=i+1) + integrator[i] <= #1 0; + else if (enable && strobe_in) + begin + integrator[0] <= #1 integrator[0] + signal_in_ext; + for(i=1;i<N;i=i+1) + integrator[i] <= #1 integrator[i] + integrator[i-1]; + end + + always @(posedge clock) + if(reset) + begin + sampler <= #1 0; + for(i=0;i<N;i=i+1) + begin + pipeline[i] <= #1 0; + differentiator[i] <= #1 0; + end + end + else if (enable && strobe_out) + begin + sampler <= #1 integrator[N-1]; + differentiator[0] <= #1 sampler; + pipeline[0] <= #1 sampler - differentiator[0]; + for(i=1;i<N;i=i+1) + begin + differentiator[i] <= #1 pipeline[i-1]; + pipeline[i] <= #1 pipeline[i-1] - differentiator[i]; + end + end // if (enable && strobe_out) + + wire [bw+maxbitgain-1:0] signal_out_unnorm = pipeline[N-1]; + + // Output Scaling to same width as input + function [2:0] log_ceil; + input [7:0] val; + log_ceil = val[6] ? 3'd7 : val[5] ? 3'd6 : val[4] ? 3'd5 : + val[3] ? 3'd4 : val[2] ? 3'd3 : val[1] ? 3'd2 : 3'd1; + endfunction // log_ceil + + wire [2:0] shift = log_ceil(rate); + + always @* + case(shift) + 3'd2 : signal_out = signal_out_unnorm[2*N+bw-1:2*N]; // Decim by 4 + 3'd3 : signal_out = signal_out_unnorm[3*N+bw-1:3*N]; + 3'd4 : signal_out = signal_out_unnorm[4*N+bw-1:4*N]; + 3'd5 : signal_out = signal_out_unnorm[5*N+bw-1:5*N]; + 3'd6 : signal_out = signal_out_unnorm[6*N+bw-1:6*N]; + 3'd7 : signal_out = signal_out_unnorm[7*N+bw-1:7*N]; + default : signal_out = signal_out_unnorm[7*N+bw-1:7*N]; + endcase // case(shift) + +endmodule // cic_decim + diff --git a/usrp/fpga/sdr_lib/cic_int_shifter.v b/usrp/fpga/sdr_lib/cic_int_shifter.v new file mode 100644 index 000000000..112d8712b --- /dev/null +++ b/usrp/fpga/sdr_lib/cic_int_shifter.v @@ -0,0 +1,98 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + +module cic_int_shifter(rate,signal_in,signal_out); + parameter bw = 16; + parameter N = 4; + parameter log2_of_max_rate = 7; + parameter maxbitgain = (N-1)*log2_of_max_rate; + + input [7:0] rate; + input wire [bw+maxbitgain-1:0] signal_in; + output reg [bw-1:0] signal_out; + + function [2:0] log_ceil; + input [7:0] val; + log_ceil = val[6] ? 3'd7 : val[5] ? 3'd6 : val[4] ? 3'd5 : + val[3] ? 3'd4 : val[2] ? 3'd3 : val[1] ? 3'd2 : 3'd1; + endfunction // log_ceil + + function [4:0] bitgain; + input [7:0] rate; + case(rate) + 8'd4 : bitgain = 2*(N-1); + 8'd8 : bitgain = 3*(N-1); + 8'd16 : bitgain = 4*(N-1); + 8'd32 : bitgain = 5*(N-1); + 8'd64 : bitgain = 6*(N-1); + 8'd128 : bitgain = 7*(N-1); + + 8'd5 : bitgain = 7; + 8'd6 : bitgain = 8; + 8'd7 : bitgain = 9; + 8'd9,8'd10 : bitgain = 10; + 8'd12 : bitgain = 11; + 8'd13,8'd14,8'd15 : bitgain = 12; + 8'd17,8'd18,8'd19,8'd20 : bitgain = 13; + 8'd21,8'd22,8'd23,8'd24,8'd25 : bitgain = 14; + 8'd26,8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 15; + 8'd33,8'd34,8'd35,8'd36,8'd37,8'd38,8'd39,8'd40 : bitgain = 16; + 8'd41,8'd42,8'd43,8'd44,8'd45,8'd46,8'd47,8'd48,8'd49,8'd50 : bitgain = 17; + 8'd51,8'd52,8'd53,8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 18; + 8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76,8'd77,8'd78,8'd79,8'd80 : bitgain = 19; + 8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90,8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101 : bitgain = 20; + + default : bitgain = 21; + endcase // case(rate) + endfunction // bitgain + + wire [4:0] shift = bitgain(rate+1); + + // We should be able to do this, but can't .... + // assign signal_out = signal_in[shift+bw-1:shift]; + + always @* + case(shift) + 5'd6 : signal_out = signal_in[6+bw-1:6]; + 5'd9 : signal_out = signal_in[9+bw-1:9]; + 5'd12 : signal_out = signal_in[12+bw-1:12]; + 5'd15 : signal_out = signal_in[15+bw-1:15]; + 5'd18 : signal_out = signal_in[18+bw-1:18]; + 5'd21 : signal_out = signal_in[21+bw-1:21]; + + 5'd7 : signal_out = signal_in[7+bw-1:7]; + 5'd8 : signal_out = signal_in[8+bw-1:8]; + 5'd10 : signal_out = signal_in[10+bw-1:10]; + 5'd11 : signal_out = signal_in[11+bw-1:11]; + 5'd13 : signal_out = signal_in[13+bw-1:13]; + 5'd14 : signal_out = signal_in[14+bw-1:14]; + 5'd16 : signal_out = signal_in[16+bw-1:16]; + 5'd17 : signal_out = signal_in[17+bw-1:17]; + 5'd19 : signal_out = signal_in[19+bw-1:19]; + 5'd20 : signal_out = signal_in[20+bw-1:20]; + + + default : signal_out = signal_in[21+bw-1:21]; + endcase // case(shift) + +endmodule // cic_int_shifter + diff --git a/usrp/fpga/sdr_lib/cic_interp.v b/usrp/fpga/sdr_lib/cic_interp.v new file mode 100755 index 000000000..43ab17d3b --- /dev/null +++ b/usrp/fpga/sdr_lib/cic_interp.v @@ -0,0 +1,88 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + +module cic_interp(clock,reset,enable,rate,strobe_in,strobe_out,signal_in,signal_out); + parameter bw = 16; + parameter N = 4; + parameter log2_of_max_rate = 7; + parameter maxbitgain = (N-1)*log2_of_max_rate; + + input clock; + input reset; + input enable; + input [7:0] rate; + input strobe_in,strobe_out; + input [bw-1:0] signal_in; + wire [bw-1:0] signal_in; + output [bw-1:0] signal_out; + wire [bw-1:0] signal_out; + + wire [bw+maxbitgain-1:0] signal_in_ext; + reg [bw+maxbitgain-1:0] integrator [0:N-1]; + reg [bw+maxbitgain-1:0] differentiator [0:N-1]; + reg [bw+maxbitgain-1:0] pipeline [0:N-1]; + + integer i; + + sign_extend #(bw,bw+maxbitgain) + ext_input (.in(signal_in),.out(signal_in_ext)); + + //FIXME Note that this section has pipe and diff reversed + // It still works, but is confusing + always @(posedge clock) + if(reset) + for(i=0;i<N;i=i+1) + integrator[i] <= #1 0; + else if (enable & strobe_out) + begin + if(strobe_in) + integrator[0] <= #1 integrator[0] + pipeline[N-1]; + for(i=1;i<N;i=i+1) + integrator[i] <= #1 integrator[i] + integrator[i-1]; + end + + always @(posedge clock) + if(reset) + begin + for(i=0;i<N;i=i+1) + begin + differentiator[i] <= #1 0; + pipeline[i] <= #1 0; + end + end + else if (enable && strobe_in) + begin + differentiator[0] <= #1 signal_in_ext; + pipeline[0] <= #1 signal_in_ext - differentiator[0]; + for(i=1;i<N;i=i+1) + begin + differentiator[i] <= #1 pipeline[i-1]; + pipeline[i] <= #1 pipeline[i-1] - differentiator[i]; + end + end + + wire [bw+maxbitgain-1:0] signal_out_unnorm = integrator[N-1]; + + cic_int_shifter cic_int_shifter(rate,signal_out_unnorm,signal_out); + +endmodule // cic_interp + diff --git a/usrp/fpga/sdr_lib/clk_divider.v b/usrp/fpga/sdr_lib/clk_divider.v new file mode 100755 index 000000000..a687297b4 --- /dev/null +++ b/usrp/fpga/sdr_lib/clk_divider.v @@ -0,0 +1,43 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + +module clk_divider(input reset, input wire in_clk,output reg out_clk, input [7:0] ratio); + reg [7:0] counter; + + // FIXME maybe should use PLL or switch to double edge version + + always @(posedge in_clk or posedge reset) + if(reset) + counter <= #1 8'd0; + else if(counter == 0) + counter <= #1 ratio[7:1] + (ratio[0] & out_clk) - 8'b1; + else + counter <= #1 counter-8'd1; + + always @(posedge in_clk or posedge reset) + if(reset) + out_clk <= #1 1'b0; + else if(counter == 0) + out_clk <= #1 ~out_clk; + +endmodule // clk_divider + diff --git a/usrp/fpga/sdr_lib/cordic.v b/usrp/fpga/sdr_lib/cordic.v new file mode 100755 index 000000000..8c8c0ab0d --- /dev/null +++ b/usrp/fpga/sdr_lib/cordic.v @@ -0,0 +1,109 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +module cordic(clock, reset, enable, xi, yi, zi, xo, yo, zo ); + parameter bitwidth = 16; + parameter zwidth = 16; + + input clock; + input reset; + input enable; + input [bitwidth-1:0] xi, yi; + output [bitwidth-1:0] xo, yo; + input [zwidth-1:0] zi; + output [zwidth-1:0] zo; + + reg [bitwidth+1:0] x0,y0; + reg [zwidth-2:0] z0; + wire [bitwidth+1:0] x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12; + wire [bitwidth+1:0] y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12; + wire [zwidth-2:0] z1,z2,z3,z4,z5,z6,z7,z8,z9,z10,z11,z12; + + wire [bitwidth+1:0] xi_ext = {{2{xi[bitwidth-1]}},xi}; + wire [bitwidth+1:0] yi_ext = {{2{yi[bitwidth-1]}},yi}; + + // Compute consts. Would be easier if vlog had atan... + // see gen_cordic_consts.py + +`define c00 16'd8192 +`define c01 16'd4836 +`define c02 16'd2555 +`define c03 16'd1297 +`define c04 16'd651 +`define c05 16'd326 +`define c06 16'd163 +`define c07 16'd81 +`define c08 16'd41 +`define c09 16'd20 +`define c10 16'd10 +`define c11 16'd5 +`define c12 16'd3 +`define c13 16'd1 +`define c14 16'd1 +`define c15 16'd0 +`define c16 16'd0 + + always @(posedge clock) + if(reset) + begin + x0 <= #1 0; y0 <= #1 0; z0 <= #1 0; + end + else// if(enable) + begin + z0 <= #1 zi[zwidth-2:0]; + case (zi[zwidth-1:zwidth-2]) + 2'b00, 2'b11 : + begin + x0 <= #1 xi_ext; + y0 <= #1 yi_ext; + end + 2'b01, 2'b10 : + begin + x0 <= #1 -xi_ext; + y0 <= #1 -yi_ext; + end + endcase // case(zi[zwidth-1:zwidth-2]) + end // else: !if(reset) + + // FIXME need to handle variable number of stages + // FIXME should be able to narrow zwidth but quartus makes it bigger... + // This would be easier if arrays worked better in vlog... + cordic_stage #(bitwidth+2,zwidth-1,0) cordic_stage0 (clock,reset,enable,x0,y0,z0,`c00,x1,y1,z1); + cordic_stage #(bitwidth+2,zwidth-1,1) cordic_stage1 (clock,reset,enable,x1,y1,z1,`c01,x2,y2,z2); + cordic_stage #(bitwidth+2,zwidth-1,2) cordic_stage2 (clock,reset,enable,x2,y2,z2,`c02,x3,y3,z3); + cordic_stage #(bitwidth+2,zwidth-1,3) cordic_stage3 (clock,reset,enable,x3,y3,z3,`c03,x4,y4,z4); + cordic_stage #(bitwidth+2,zwidth-1,4) cordic_stage4 (clock,reset,enable,x4,y4,z4,`c04,x5,y5,z5); + cordic_stage #(bitwidth+2,zwidth-1,5) cordic_stage5 (clock,reset,enable,x5,y5,z5,`c05,x6,y6,z6); + cordic_stage #(bitwidth+2,zwidth-1,6) cordic_stage6 (clock,reset,enable,x6,y6,z6,`c06,x7,y7,z7); + cordic_stage #(bitwidth+2,zwidth-1,7) cordic_stage7 (clock,reset,enable,x7,y7,z7,`c07,x8,y8,z8); + cordic_stage #(bitwidth+2,zwidth-1,8) cordic_stage8 (clock,reset,enable,x8,y8,z8,`c08,x9,y9,z9); + cordic_stage #(bitwidth+2,zwidth-1,9) cordic_stage9 (clock,reset,enable,x9,y9,z9,`c09,x10,y10,z10); + cordic_stage #(bitwidth+2,zwidth-1,10) cordic_stage10 (clock,reset,enable,x10,y10,z10,`c10,x11,y11,z11); + cordic_stage #(bitwidth+2,zwidth-1,11) cordic_stage11 (clock,reset,enable,x11,y11,z11,`c11,x12,y12,z12); + + assign xo = x12[bitwidth:1]; + assign yo = y12[bitwidth:1]; + //assign xo = x12[bitwidth+1:2]; // CORDIC gain is ~1.6, plus gain from rotating vectors + //assign yo = y12[bitwidth+1:2]; + assign zo = z12; + +endmodule // cordic + diff --git a/usrp/fpga/sdr_lib/cordic_stage.v b/usrp/fpga/sdr_lib/cordic_stage.v new file mode 100755 index 000000000..c9c0ef9a3 --- /dev/null +++ b/usrp/fpga/sdr_lib/cordic_stage.v @@ -0,0 +1,60 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +module cordic_stage( clock, reset, enable, xi,yi,zi,constant,xo,yo,zo); + parameter bitwidth = 16; + parameter zwidth = 16; + parameter shift = 1; + + input clock; + input reset; + input enable; + input [bitwidth-1:0] xi,yi; + input [zwidth-1:0] zi; + input [zwidth-1:0] constant; + output [bitwidth-1:0] xo,yo; + output [zwidth-1:0] zo; + + wire z_is_pos = ~zi[zwidth-1]; + + reg [bitwidth-1:0] xo,yo; + reg [zwidth-1:0] zo; + + always @(posedge clock) + if(reset) + begin + xo <= #1 0; + yo <= #1 0; + zo <= #1 0; + end + else //if(enable) + begin + xo <= #1 z_is_pos ? + xi - {{shift+1{yi[bitwidth-1]}},yi[bitwidth-2:shift]} : + xi + {{shift+1{yi[bitwidth-1]}},yi[bitwidth-2:shift]}; + yo <= #1 z_is_pos ? + yi + {{shift+1{xi[bitwidth-1]}},xi[bitwidth-2:shift]} : + yi - {{shift+1{xi[bitwidth-1]}},xi[bitwidth-2:shift]}; + zo <= #1 z_is_pos ? + zi - constant : + zi + constant; + end +endmodule diff --git a/usrp/fpga/sdr_lib/ddc.v b/usrp/fpga/sdr_lib/ddc.v new file mode 100755 index 000000000..48bca9a79 --- /dev/null +++ b/usrp/fpga/sdr_lib/ddc.v @@ -0,0 +1,97 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + + +// DDC block + +module ddc(input clock, + input reset, + input enable, + input [3:0] rate1, + input [3:0] rate2, + output strobe, + input [31:0] freq, + input [15:0] i_in, + input [15:0] q_in, + output [15:0] i_out, + output [15:0] q_out + ); + parameter bw = 16; + parameter zw = 16; + + wire [15:0] i_cordic_out, q_cordic_out; + wire [31:0] phase; + + wire strobe1, strobe2; + reg [3:0] strobe_ctr1,strobe_ctr2; + + always @(posedge clock) + if(reset | ~enable) + strobe_ctr2 <= #1 4'd0; + else if(strobe2) + strobe_ctr2 <= #1 4'd0; + else + strobe_ctr2 <= #1 strobe_ctr2 + 4'd1; + + always @(posedge clock) + if(reset | ~enable) + strobe_ctr1 <= #1 4'd0; + else if(strobe1) + strobe_ctr1 <= #1 4'd0; + else if(strobe2) + strobe_ctr1 <= #1 strobe_ctr1 + 4'd1; + + + assign strobe2 = enable & ( strobe_ctr2 == rate2 ); + assign strobe1 = strobe2 & ( strobe_ctr1 == rate1 ); + + assign strobe = strobe1; + + function [2:0] log_ceil; + input [3:0] val; + + log_ceil = val[3] ? 3'd4 : val[2] ? 3'd3 : val[1] ? 3'd2 : 3'd1; + endfunction + + wire [2:0] shift1 = log_ceil(rate1); + wire [2:0] shift2 = log_ceil(rate2); + + cordic #(.bitwidth(bw),.zwidth(zw),.stages(16)) + cordic(.clock(clock), .reset(reset), .enable(enable), + .xi(i_in), .yi(q_in), .zi(phase[31:32-zw]), + .xo(i_cordic_out), .yo(q_cordic_out), .zo() ); + + cic_decim_2stage #(.bw(bw),.N(4)) + decim_i(.clock(clock),.reset(reset),.enable(enable), + .strobe1(1'b1),.strobe2(strobe2),.strobe3(strobe1),.shift1(shift2),.shift2(shift1), + .signal_in(i_cordic_out),.signal_out(i_out)); + + cic_decim_2stage #(.bw(bw),.N(4)) + decim_q(.clock(clock),.reset(reset),.enable(enable), + .strobe1(1'b1),.strobe2(strobe2),.strobe3(strobe1),.shift1(shift2),.shift2(shift1), + .signal_in(q_cordic_out),.signal_out(q_out)); + + phase_acc #(.resolution(32)) + nco (.clk(clock),.reset(reset),.enable(enable), + .freq(freq),.phase(phase)); + +endmodule diff --git a/usrp/fpga/sdr_lib/dpram.v b/usrp/fpga/sdr_lib/dpram.v new file mode 100644 index 000000000..5c38decce --- /dev/null +++ b/usrp/fpga/sdr_lib/dpram.v @@ -0,0 +1,47 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + + +module dpram(wclk,wdata,waddr,wen,rclk,rdata,raddr); + parameter depth = 4; + parameter width = 16; + parameter size = 16; + + input wclk; + input [width-1:0] wdata; + input [depth-1:0] waddr; + input wen; + + input rclk; + output reg [width-1:0] rdata; + input [depth-1:0] raddr; + + reg [width-1:0] ram [0:size-1]; + + always @(posedge wclk) + if(wen) + ram[waddr] <= #1 wdata; + + always @(posedge rclk) + rdata <= #1 ram[raddr]; + +endmodule // dpram diff --git a/usrp/fpga/sdr_lib/duc.v b/usrp/fpga/sdr_lib/duc.v new file mode 100755 index 000000000..780fc9f23 --- /dev/null +++ b/usrp/fpga/sdr_lib/duc.v @@ -0,0 +1,95 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +// DUC block + +module duc(input clock, + input reset, + input enable, + input [3:0] rate1, + input [3:0] rate2, + output strobe, + input [31:0] freq, + input [15:0] i_in, + input [15:0] q_in, + output [15:0] i_out, + output [15:0] q_out + ); + parameter bw = 16; + parameter zw = 16; + + wire [15:0] i_interp_out, q_interp_out; + wire [31:0] phase; + + wire strobe1, strobe2; + reg [3:0] strobe_ctr1,strobe_ctr2; + + always @(posedge clock) + if(reset | ~enable) + strobe_ctr2 <= #1 4'd0; + else if(strobe2) + strobe_ctr2 <= #1 4'd0; + else + strobe_ctr2 <= #1 strobe_ctr2 + 4'd1; + + always @(posedge clock) + if(reset | ~enable) + strobe_ctr1 <= #1 4'd0; + else if(strobe1) + strobe_ctr1 <= #1 4'd0; + else if(strobe2) + strobe_ctr1 <= #1 strobe_ctr1 + 4'd1; + + + assign strobe2 = enable & ( strobe_ctr2 == rate2 ); + assign strobe1 = strobe2 & ( strobe_ctr1 == rate1 ); + + assign strobe = strobe1; + + function [2:0] log_ceil; + input [3:0] val; + + log_ceil = val[3] ? 3'd4 : val[2] ? 3'd3 : val[1] ? 3'd2 : 3'd1; + endfunction + + wire [2:0] shift1 = log_ceil(rate1); + wire [2:0] shift2 = log_ceil(rate2); + + cordic #(.bitwidth(bw),.zwidth(zw),.stages(16)) + cordic(.clock(clock), .reset(reset), .enable(enable), + .xi(i_interp_out), .yi(q_interp_out), .zi(phase[31:32-zw]), + .xo(i_out), .yo(q_out), .zo() ); + + cic_interp_2stage #(.bw(bw),.N(4)) + interp_i(.clock(clock),.reset(reset),.enable(enable), + .strobe1(strobe1),.strobe2(strobe2),.strobe3(1'b1),.shift1(shift1),.shift2(shift2), + .signal_in(i_in),.signal_out(i_interp_out)); + + cic_interp_2stage #(.bw(bw),.N(4)) + interp_q(.clock(clock),.reset(reset),.enable(enable), + .strobe1(strobe1),.strobe2(strobe2),.strobe3(1'b1),.shift1(shift1),.shift2(shift2), + .signal_in(q_in),.signal_out(q_interp_out)); + + phase_acc #(.resolution(32)) + nco (.clk(clock),.reset(reset),.enable(enable), + .freq(freq),.phase(phase)); + +endmodule diff --git a/usrp/fpga/sdr_lib/ext_fifo.v b/usrp/fpga/sdr_lib/ext_fifo.v new file mode 100644 index 000000000..dfe1f2fe7 --- /dev/null +++ b/usrp/fpga/sdr_lib/ext_fifo.v @@ -0,0 +1,126 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + +// Vendor Independent FIFO module +// Width and Depth should be parameterizable +// Asynchronous clocks for each side +// Read side is read-acknowledge, not read-request +// FIFO does not enforce "don't write when full, don't read when empty" +// That is up to the connecting modules +// The FIFO only holds 2^N-1 entries, not 2^N + +module fifo (reset,data,write,wrclk,wr_used,q,read_ack,rdclk,rd_used); + parameter width=32; + parameter depth=10; + + input reset; // Asynchronous + input [width-1:0] data; + input write; + input wrclk; + output [depth-1:0] wr_used; + output [width-1:0] q; + input read_ack; + input rdclk; + output [depth-1:0] rd_used; + + reg [depth-1:0] read_addr, write_addr, + read_addr_gray, read_addr_gray_sync, + write_addr_gray, write_addr_gray_sync; + + // Pseudo-dual-port RAM + dpram #(.depth(10),.width(width),.size(1024)) + fifo_ram (.wclk(wrclk),.wdata(data),.waddr(write_addr),.wen(write), + .rclk(rdclk), .rdata(q),.raddr(read_addr) ); + + wire [depth-1:0] wag,rag; + + // Keep track of own side's pointer + always @(posedge wrclk or posedge reset) + if(reset) write_addr <= #1 0; + else if(write) write_addr <= #1 write_addr + 1; + + always @(posedge rdclk or posedge reset) + if(reset) read_addr <= #1 0; + else if(read_ack) read_addr <= #1 read_addr + 1; + + // Convert own side pointer to gray + bin2gray #(depth) write_b2g (write_addr,wag); + bin2gray #(depth) read_b2g (read_addr,rag); + + // Latch it + always @(posedge wrclk or posedge reset) + if(reset) write_addr_gray <= #1 0; + else write_addr_gray <= #1 wag; + + always @(posedge rdclk or posedge reset) + if(reset) read_addr_gray <= #1 0; + else read_addr_gray <= #1 rag; + + // Send it to other side and latch + always @(posedge wrclk or posedge reset) + if(reset) read_addr_gray_sync <= #1 0; + else read_addr_gray_sync <= #1 read_addr_gray; + + always @(posedge rdclk or posedge reset) + if(reset) write_addr_gray_sync <= #1 0; + else write_addr_gray_sync <= #1 write_addr_gray; + + wire [depth-1:0] write_addr_sync, read_addr_sync; + + // Convert back to binary + gray2bin #(depth) write_g2b (write_addr_gray_sync, write_addr_sync); + gray2bin #(depth) read_g2b (read_addr_gray_sync, read_addr_sync); + + assign rd_used = write_addr_sync - read_addr; + assign wr_used = write_addr - read_addr_sync; + +endmodule // fifo + +module bin2gray(bin_val,gray_val); + parameter width = 8; + input [width-1:0] bin_val; + output reg [width-1:0] gray_val; + + integer i; + + always @* + begin + gray_val[width-1] = bin_val[width-1]; + for(i=0;i<width-1;i=i+1) + gray_val[i] = bin_val[i] ^ bin_val[i+1]; + end +endmodule // bin2gray + +module gray2bin(gray_val,bin_val); + parameter width = 8; + input [width-1:0] gray_val; + output reg [width-1:0] bin_val; + + integer i; + + always @* + begin + bin_val[width-1] = gray_val[width-1]; + for(i=width-2;i>=0;i=i-1) + bin_val[i] = bin_val[i+1] ^ gray_val[i]; + end +endmodule // gray2bin diff --git a/usrp/fpga/sdr_lib/gen_cordic_consts.py b/usrp/fpga/sdr_lib/gen_cordic_consts.py new file mode 100755 index 000000000..ab66cfe01 --- /dev/null +++ b/usrp/fpga/sdr_lib/gen_cordic_consts.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +import math + +zwidth = 16 + +for i in range(17): + c = math.atan (1.0/(2**i)) / (2 * math.pi) * (1 << zwidth) + print "`define c%02d %d'd%d" % (i, zwidth, round (c)) + diff --git a/usrp/fpga/sdr_lib/gen_sync.v b/usrp/fpga/sdr_lib/gen_sync.v new file mode 100644 index 000000000..d72b39d56 --- /dev/null +++ b/usrp/fpga/sdr_lib/gen_sync.v @@ -0,0 +1,43 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +module gen_sync + ( input clock, + input reset, + input enable, + input [7:0] rate, + output wire sync ); + +// parameter width = 8; + + reg [7:0] counter; + assign sync = |(((rate+1)>>1)& counter); + + always @(posedge clock) + if(reset || ~enable) + counter <= #1 0; + else if(counter == rate) + counter <= #1 0; + else + counter <= #1 counter + 8'd1; + +endmodule // gen_sync + diff --git a/usrp/fpga/sdr_lib/hb/acc.v b/usrp/fpga/sdr_lib/hb/acc.v new file mode 100644 index 000000000..195d5ea94 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/acc.v @@ -0,0 +1,22 @@ + + +module acc (input clock, input reset, input clear, input enable_in, output reg enable_out, + input signed [30:0] addend, output reg signed [33:0] sum ); + + always @(posedge clock) + if(reset) + sum <= #1 34'd0; + //else if(clear & enable_in) + // sum <= #1 addend; + //else if(clear) + // sum <= #1 34'd0; + else if(clear) + sum <= #1 addend; + else if(enable_in) + sum <= #1 sum + addend; + + always @(posedge clock) + enable_out <= #1 enable_in; + +endmodule // acc + diff --git a/usrp/fpga/sdr_lib/hb/coeff_ram.v b/usrp/fpga/sdr_lib/hb/coeff_ram.v new file mode 100644 index 000000000..65460822f --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/coeff_ram.v @@ -0,0 +1,26 @@ + + +module coeff_ram (input clock, input [3:0] rd_addr, output reg [15:0] rd_data); + + always @(posedge clock) + case (rd_addr) + 4'd0 : rd_data <= #1 -16'd16; + 4'd1 : rd_data <= #1 16'd74; + 4'd2 : rd_data <= #1 -16'd254; + 4'd3 : rd_data <= #1 16'd669; + 4'd4 : rd_data <= #1 -16'd1468; + 4'd5 : rd_data <= #1 16'd2950; + 4'd6 : rd_data <= #1 -16'd6158; + 4'd7 : rd_data <= #1 16'd20585; + 4'd8 : rd_data <= #1 16'd20585; + 4'd9 : rd_data <= #1 -16'd6158; + 4'd10 : rd_data <= #1 16'd2950; + 4'd11 : rd_data <= #1 -16'd1468; + 4'd12 : rd_data <= #1 16'd669; + 4'd13 : rd_data <= #1 -16'd254; + 4'd14 : rd_data <= #1 16'd74; + 4'd15 : rd_data <= #1 -16'd16; + default : rd_data <= #1 16'd0; + endcase // case(rd_addr) + +endmodule // ram diff --git a/usrp/fpga/sdr_lib/hb/coeff_rom.v b/usrp/fpga/sdr_lib/hb/coeff_rom.v new file mode 100644 index 000000000..c287eaaad --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/coeff_rom.v @@ -0,0 +1,19 @@ + + +module coeff_rom (input clock, input [2:0] addr, output reg [15:0] data); + + always @(posedge clock) + case (addr) + 3'd0 : data <= #1 -16'd16; + 3'd1 : data <= #1 16'd74; + 3'd2 : data <= #1 -16'd254; + 3'd3 : data <= #1 16'd669; + 3'd4 : data <= #1 -16'd1468; + 3'd5 : data <= #1 16'd2950; + 3'd6 : data <= #1 -16'd6158; + 3'd7 : data <= #1 16'd20585; + endcase // case(addr) + +endmodule // coeff_rom + + diff --git a/usrp/fpga/sdr_lib/hb/halfband_decim.v b/usrp/fpga/sdr_lib/hb/halfband_decim.v new file mode 100644 index 000000000..2a05ce52c --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/halfband_decim.v @@ -0,0 +1,163 @@ +/* -*- verilog -*- + * + * USRP - Universal Software Radio Peripheral + * + * Copyright (C) 2005 Matt Ettus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This implements a 31-tap halfband filter that decimates by two. + * The coefficients are symmetric, and with the exception of the middle tap, + * every other coefficient is zero. The middle section of taps looks like this: + * + * ..., -1468, 0, 2950, 0, -6158, 0, 20585, 32768, 20585, 0, -6158, 0, 2950, 0, -1468, ... + * | + * middle tap -------+ + * + * See coeff_rom.v for the full set. The taps are scaled relative to 32768, + * thus the middle tap equals 1.0. Not counting the middle tap, there are 8 + * non-zero taps on each side, and they are symmetric. A naive implementation + * requires a mulitply for each non-zero tap. Because of symmetry, we can + * replace 2 multiplies with 1 add and 1 multiply. Thus, to compute each output + * sample, we need to perform 8 multiplications. Since the middle tap is 1.0, + * we just add the corresponding delay line value. + * + * About timing: We implement this with a single multiplier, so it takes + * 8 cycles to compute a single output. However, since we're decimating by two + * we can accept a new input value every 4 cycles. strobe_in is asserted when + * there's a new input sample available. Depending on the overall decimation + * rate, strobe_in may be asserted less frequently than once every 4 clocks. + * On the output side, we assert strobe_out when output contains a new sample. + * + * Implementation: Every time strobe_in is asserted we store the new data into + * the delay line. We split the delay line into two components, one for the + * even samples, and one for the odd samples. ram16_odd is the delay line for + * the odd samples. This ram is written on each odd assertion of strobe_in, and + * is read on each clock when we're computing the dot product. ram16_even is + * similar, although because it holds the even samples we must be able to read + * two samples from different addresses at the same time, while writing the incoming + * even samples. Thus it's "triple-ported". + */ + +module halfband_decim + (input clock, input reset, input enable, input strobe_in, output wire strobe_out, + input wire [15:0] data_in, output reg [15:0] data_out,output wire [15:0] debugctrl); + + reg [3:0] rd_addr1; + reg [3:0] rd_addr2; + reg [3:0] phase; + reg [3:0] base_addr; + + wire signed [15:0] mac_out,middle_data, sum, coeff; + wire signed [30:0] product; + wire signed [33:0] sum_even; + wire clear; + reg store_odd; + + always @(posedge clock) + if(reset) + store_odd <= #1 1'b0; + else + if(strobe_in) + store_odd <= #1 ~store_odd; + + wire start = strobe_in & store_odd; + always @(posedge clock) + if(reset) + base_addr <= #1 4'd0; + else if(start) + base_addr <= #1 base_addr + 4'd1; + + always @(posedge clock) + if(reset) + phase <= #1 4'd8; + else if (start) + phase <= #1 4'd0; + else if(phase != 4'd8) + phase <= #1 phase + 4'd1; + + reg start_d1,start_d2,start_d3,start_d4,start_d5,start_d6,start_d7,start_d8,start_d9,start_dA,start_dB,start_dC,start_dD; + always @(posedge clock) + begin + start_d1 <= #1 start; + start_d2 <= #1 start_d1; + start_d3 <= #1 start_d2; + start_d4 <= #1 start_d3; + start_d5 <= #1 start_d4; + start_d6 <= #1 start_d5; + start_d7 <= #1 start_d6; + start_d8 <= #1 start_d7; + start_d9 <= #1 start_d8; + start_dA <= #1 start_d9; + start_dB <= #1 start_dA; + start_dC <= #1 start_dB; + start_dD <= #1 start_dC; + end // always @ (posedge clock) + + reg mult_en, mult_en_pre; + always @(posedge clock) + begin + mult_en_pre <= #1 phase!=8; + mult_en <= #1 mult_en_pre; + end + + assign clear = start_d4; // was dC + wire latch_result = start_d4; // was dC + assign strobe_out = start_d5; // was dD + wire acc_en; + + always @* + case(phase[2:0]) + 3'd0 : begin rd_addr1 = base_addr + 4'd0; rd_addr2 = base_addr + 4'd15; end + 3'd1 : begin rd_addr1 = base_addr + 4'd1; rd_addr2 = base_addr + 4'd14; end + 3'd2 : begin rd_addr1 = base_addr + 4'd2; rd_addr2 = base_addr + 4'd13; end + 3'd3 : begin rd_addr1 = base_addr + 4'd3; rd_addr2 = base_addr + 4'd12; end + 3'd4 : begin rd_addr1 = base_addr + 4'd4; rd_addr2 = base_addr + 4'd11; end + 3'd5 : begin rd_addr1 = base_addr + 4'd5; rd_addr2 = base_addr + 4'd10; end + 3'd6 : begin rd_addr1 = base_addr + 4'd6; rd_addr2 = base_addr + 4'd9; end + 3'd7 : begin rd_addr1 = base_addr + 4'd7; rd_addr2 = base_addr + 4'd8; end + default: begin rd_addr1 = base_addr + 4'd0; rd_addr2 = base_addr + 4'd15; end + endcase // case(phase) + + coeff_rom coeff_rom (.clock(clock),.addr(phase[2:0]-3'd1),.data(coeff)); + + ram16_2sum ram16_even (.clock(clock),.write(strobe_in & ~store_odd), + .wr_addr(base_addr),.wr_data(data_in), + .rd_addr1(rd_addr1),.rd_addr2(rd_addr2), + .sum(sum)); + + ram16 ram16_odd (.clock(clock),.write(strobe_in & store_odd), // Holds middle items + .wr_addr(base_addr),.wr_data(data_in), + //.rd_addr(base_addr+4'd7),.rd_data(middle_data)); + .rd_addr(base_addr+4'd6),.rd_data(middle_data)); + + mult mult(.clock(clock),.x(coeff),.y(sum),.product(product),.enable_in(mult_en),.enable_out(acc_en)); + + acc acc(.clock(clock),.reset(reset),.enable_in(acc_en),.enable_out(), + .clear(clear),.addend(product),.sum(sum_even)); + + wire signed [33:0] dout = sum_even + {{4{middle_data[15]}},middle_data,14'b0}; // We already divided product by 2!!!! + + always @(posedge clock) + if(reset) + data_out <= #1 16'd0; + else if(latch_result) + data_out <= #1 dout[30:15] + (dout[33]& |dout[14:0]); + + assign debugctrl = { clock,reset,acc_en,mult_en,clear,latch_result,store_odd,strobe_in,strobe_out,phase}; + +endmodule // halfband_decim diff --git a/usrp/fpga/sdr_lib/hb/halfband_interp.v b/usrp/fpga/sdr_lib/hb/halfband_interp.v new file mode 100644 index 000000000..cdb11c1f6 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/halfband_interp.v @@ -0,0 +1,121 @@ + + +module halfband_interp + (input clock, input reset, input enable, + input strobe_in, input strobe_out, + input [15:0] signal_in_i, input [15:0] signal_in_q, + output reg [15:0] signal_out_i, output reg [15:0] signal_out_q, + output wire [12:0] debug); + + wire [15:0] coeff_ram_out; + wire [15:0] data_ram_out_i; + wire [15:0] data_ram_out_q; + + wire [3:0] data_rd_addr; + reg [3:0] data_wr_addr; + reg [2:0] coeff_rd_addr; + + wire filt_done; + + wire [15:0] mac_out_i; + wire [15:0] mac_out_q; + reg [15:0] delayed_middle_i, delayed_middle_q; + wire [7:0] shift = 8'd9; + + reg stb_out_happened; + + wire [15:0] data_ram_out_i_b; + + always @(posedge clock) + if(strobe_in) + stb_out_happened <= #1 1'b0; + else if(strobe_out) + stb_out_happened <= #1 1'b1; + +assign debug = {filt_done,data_rd_addr,data_wr_addr,coeff_rd_addr}; + + wire [15:0] signal_out_i = stb_out_happened ? mac_out_i : delayed_middle_i; + wire [15:0] signal_out_q = stb_out_happened ? mac_out_q : delayed_middle_q; + +/* always @(posedge clock) + if(reset) + begin + signal_out_i <= #1 16'd0; + signal_out_q <= #1 16'd0; + end + else if(strobe_in) + begin + signal_out_i <= #1 delayed_middle_i; // Multiply by 1 for middle coeff + signal_out_q <= #1 delayed_middle_q; + end + //else if(filt_done&stb_out_happened) + else if(stb_out_happened) + begin + signal_out_i <= #1 mac_out_i; + signal_out_q <= #1 mac_out_q; + end +*/ + + always @(posedge clock) + if(reset) + coeff_rd_addr <= #1 3'd0; + else if(coeff_rd_addr != 3'd0) + coeff_rd_addr <= #1 coeff_rd_addr + 3'd1; + else if(strobe_in) + coeff_rd_addr <= #1 3'd1; + + reg filt_done_d1; + always@(posedge clock) + filt_done_d1 <= #1 filt_done; + + always @(posedge clock) + if(reset) + data_wr_addr <= #1 4'd0; + //else if(strobe_in) + else if(filt_done & ~filt_done_d1) + data_wr_addr <= #1 data_wr_addr + 4'd1; + + always @(posedge clock) + if(coeff_rd_addr == 3'd7) + begin + delayed_middle_i <= #1 data_ram_out_i_b; + // delayed_middle_q <= #1 data_ram_out_q_b; + end + +// always @(posedge clock) +// if(reset) +// data_rd_addr <= #1 4'd0; +// else if(strobe_in) +// data_rd_addr <= #1 data_wr_addr + 4'd1; +// else if(!filt_done) +// data_rd_addr <= #1 data_rd_addr + 4'd1; +// else +// data_rd_addr <= #1 data_wr_addr; + + wire [3:0] data_rd_addr1 = data_wr_addr + {1'b0,coeff_rd_addr}; + wire [3:0] data_rd_addr2 = data_wr_addr + 15 - {1'b0,coeff_rd_addr}; +// always @(posedge clock) +// if(reset) +// filt_done <= #1 1'b1; +// else if(strobe_in) + // filt_done <= #1 1'b0; +// else if(coeff_rd_addr == 4'd0) +// filt_done <= #1 1'b1; + + assign filt_done = (coeff_rd_addr == 3'd0); + + coeff_ram coeff_ram ( .clock(clock),.rd_addr({1'b0,coeff_rd_addr}),.rd_data(coeff_ram_out) ); + + ram16_2sum data_ram_i ( .clock(clock),.write(strobe_in),.wr_addr(data_wr_addr),.wr_data(signal_in_i), + .rd_addr1(data_rd_addr1),.rd_addr2(data_rd_addr2),.rd_data(data_ram_out_i_b),.sum(data_ram_out_i)); + + ram16_2sum data_ram_q ( .clock(clock),.write(strobe_in),.wr_addr(data_wr_addr),.wr_data(signal_in_q), + .rd_addr1(data_rd_addr1),.rd_addr2(data_rd_addr2),.rd_data(data_ram_out_q)); + + mac mac_i (.clock(clock),.reset(reset),.enable(~filt_done),.clear(strobe_in), + .x(data_ram_out_i),.y(coeff_ram_out),.shift(shift),.z(mac_out_i) ); + + mac mac_q (.clock(clock),.reset(reset),.enable(~filt_done),.clear(strobe_in), + .x(data_ram_out_q),.y(coeff_ram_out),.shift(shift),.z(mac_out_q) ); + +endmodule // halfband_interp diff --git a/usrp/fpga/sdr_lib/hb/hbd_tb/HBD b/usrp/fpga/sdr_lib/hb/hbd_tb/HBD new file mode 100644 index 000000000..574fbba91 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/hbd_tb/HBD @@ -0,0 +1,80 @@ +*-6.432683 5736 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +@28 +test_hbd.clock +test_hbd.reset +@420 +test_hbd.halfband_decim.middle_data[15:0] +@22 +test_hbd.halfband_decim.sum_even[33:0] +test_hbd.halfband_decim.base_addr[3:0] +@420 +test_hbd.i_in[15:0] +@24 +test_hbd.halfband_decim.phase[3:0] +test_hbd.halfband_decim.ram16_even.rd_addr1[3:0] +test_hbd.halfband_decim.ram16_even.rd_addr2[3:0] +test_hbd.halfband_decim.ram16_even.wr_addr[3:0] +test_hbd.halfband_decim.ram16_even.wr_data[15:0] +@28 +test_hbd.halfband_decim.ram16_even.write +@420 +test_hbd.halfband_decim.sum[15:0] +test_hbd.halfband_decim.product[30:0] +test_hbd.halfband_decim.dout[33:0] +test_hbd.halfband_decim.sum_even[33:0] +@22 +test_hbd.halfband_decim.acc.addend[30:0] +@28 +test_hbd.halfband_decim.acc.reset +@420 +test_hbd.halfband_decim.acc.sum[33:0] +test_hbd.halfband_decim.mult.x[15:0] +test_hbd.halfband_decim.mult.y[15:0] +@28 +test_hbd.halfband_decim.acc.clear +test_hbd.strobe_in +test_hbd.strobe_out +test_hbd.halfband_decim.acc_en +@420 +test_hbd.i_out[15:0] +@28 +test_hbd.halfband_decim.mult_en +test_hbd.halfband_decim.latch_result +@420 +test_hbd.halfband_decim.sum[15:0] +test_hbd.halfband_decim.sum_even[33:0] +test_hbd.halfband_decim.dout[33:0] +test_hbd.halfband_decim.data_out[15:0] +@22 +test_hbd.halfband_decim.data_out[15:0] +@28 +test_hbd.halfband_decim.dout[33:0] +@29 +test_hbd.halfband_decim.acc_en +@22 +test_hbd.halfband_decim.base_addr[3:0] +@28 +test_hbd.halfband_decim.clear +test_hbd.halfband_decim.latch_result +test_hbd.halfband_decim.mult_en +test_hbd.halfband_decim.mult_en_pre +@22 +test_hbd.halfband_decim.phase[3:0] +@28 +test_hbd.halfband_decim.start +test_hbd.halfband_decim.start_d1 +test_hbd.halfband_decim.start_d2 +test_hbd.halfband_decim.start_d3 +test_hbd.halfband_decim.start_d4 +test_hbd.halfband_decim.start_d5 +test_hbd.halfband_decim.start_d6 +test_hbd.halfband_decim.start_d7 +test_hbd.halfband_decim.start_d8 +test_hbd.halfband_decim.start_d9 +test_hbd.halfband_decim.start_dA +test_hbd.halfband_decim.start_dB +test_hbd.halfband_decim.start_dC +test_hbd.halfband_decim.start_dD +test_hbd.halfband_decim.store_odd +test_hbd.halfband_decim.strobe_in +test_hbd.halfband_decim.strobe_out diff --git a/usrp/fpga/sdr_lib/hb/hbd_tb/really_golden b/usrp/fpga/sdr_lib/hb/hbd_tb/really_golden new file mode 100644 index 000000000..2d24a9e14 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/hbd_tb/really_golden @@ -0,0 +1,142 @@ +VCD info: dumpfile test_hbd.vcd opened for output. + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 8192 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 +- 4 + 18 +- 63 + 167 +- 367 + 737 +- 1539 + 5146 + 5146 +- 1539 + 737 +- 367 + 167 +- 63 + 18 +- 4 + 0 + 0 + 0 + 0 + 0 +- 4 + 14 +- 49 + 118 +- 249 + 488 + 7141 +12287 +17433 +15894 +16631 +16264 +16432 +16368 +16387 +16383 +16383 +16383 +16383 +16383 +16387 +16368 +16432 +16264 +16631 +15894 + 9241 + 4095 +- 1051 + 488 +- 249 + 118 +- 49 + 14 +- 4 + 0 + 0 + 0 + 0 + 0 +- 4 + 14 +- 49 + 118 +- 249 + 488 +- 1051 +12287 +17433 +15894 +16631 +16264 +16432 +16368 +16387 +16383 +16383 +16383 +16383 +16383 +16387 +16368 +16432 +16264 +16631 +15894 +17433 + 4095 +- 1051 + 488 +- 249 + 118 +- 49 + 14 +- 4 + 0 + 0 + 0 + 0 diff --git a/usrp/fpga/sdr_lib/hb/hbd_tb/regression b/usrp/fpga/sdr_lib/hb/hbd_tb/regression new file mode 100644 index 000000000..fc279c2f2 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/hbd_tb/regression @@ -0,0 +1,95 @@ +echo "Baseline 1000" +iverilog -y .. -o test_hbd -DRATE=1000 test_hbd.v ; ./test_hbd >golden +diff golden really_golden + +echo +echo "Test 100" +iverilog -y .. -o test_hbd -DRATE=100 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 50" +iverilog -y .. -o test_hbd -DRATE=50 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 40" +iverilog -y .. -o test_hbd -DRATE=40 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 30" +iverilog -y .. -o test_hbd -DRATE=30 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 25" +iverilog -y .. -o test_hbd -DRATE=25 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 20" +iverilog -y .. -o test_hbd -DRATE=20 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 19" +iverilog -y .. -o test_hbd -DRATE=19 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 18" +iverilog -y .. -o test_hbd -DRATE=18 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 17" +iverilog -y .. -o test_hbd -DRATE=17 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 16" +iverilog -y .. -o test_hbd -DRATE=16 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 15" +iverilog -y .. -o test_hbd -DRATE=15 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 14" +iverilog -y .. -o test_hbd -DRATE=14 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 13" +iverilog -y .. -o test_hbd -DRATE=13 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 12" +iverilog -y .. -o test_hbd -DRATE=12 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 11" +iverilog -y .. -o test_hbd -DRATE=11 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 10" +iverilog -y .. -o test_hbd -DRATE=10 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 9" +iverilog -y .. -o test_hbd -DRATE=9 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 8" +iverilog -y .. -o test_hbd -DRATE=8 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 7" +iverilog -y .. -o test_hbd -DRATE=7 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 6" +iverilog -y .. -o test_hbd -DRATE=6 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 5" +iverilog -y .. -o test_hbd -DRATE=5 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 4" +iverilog -y .. -o test_hbd -DRATE=4 test_hbd.v ; ./test_hbd >output ; diff output golden + +echo +echo "Test 3" +iverilog -y .. -o test_hbd -DRATE=3 test_hbd.v ; ./test_hbd >output ; diff output golden diff --git a/usrp/fpga/sdr_lib/hb/hbd_tb/run_hbd b/usrp/fpga/sdr_lib/hb/hbd_tb/run_hbd new file mode 100755 index 000000000..b8aec7574 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/hbd_tb/run_hbd @@ -0,0 +1,4 @@ +#!/bin/sh + +iverilog -y .. -o test_hbd test_hbd.v +./test_hbd diff --git a/usrp/fpga/sdr_lib/hb/hbd_tb/test_hbd.v b/usrp/fpga/sdr_lib/hb/hbd_tb/test_hbd.v new file mode 100644 index 000000000..01ab5e7e0 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/hbd_tb/test_hbd.v @@ -0,0 +1,75 @@ + + +module test_hbd(); + + reg clock; + initial clock = 1'b0; + always #5 clock <= ~clock; + + reg reset; + initial reset = 1'b1; + initial #1000 reset = 1'b0; + + initial $dumpfile("test_hbd.vcd"); + initial $dumpvars(0,test_hbd); + + reg [15:0] i_in, q_in; + wire [15:0] i_out, q_out; + + reg strobe_in; + wire strobe_out; + reg coeff_write; + reg [15:0] coeff_data; + reg [4:0] coeff_addr; + + halfband_decim halfband_decim + ( .clock(clock),.reset(reset),.enable(),.strobe_in(strobe_in),.strobe_out(strobe_out), + .data_in(i_in),.data_out(i_out) ); + + always @(posedge strobe_out) + if(i_out[15]) + $display("-%d",65536-i_out); + else + $display("%d",i_out); + + initial + begin + strobe_in = 1'b0; + @(negedge reset); + @(posedge clock); + while(1) + begin + strobe_in <= #1 1'b1; + @(posedge clock); + strobe_in <= #1 1'b0; + repeat (`RATE) + @(posedge clock); + end + end + + initial #10000000 $finish; // Just in case... + + initial + begin + i_in <= #1 16'd0; + repeat (40) @(posedge strobe_in); + i_in <= #1 16'd16384; + @(posedge strobe_in); + i_in <= #1 16'd0; + repeat (40) @(posedge strobe_in); + i_in <= #1 16'd16384; + @(posedge strobe_in); + i_in <= #1 16'd0; + repeat (40) @(posedge strobe_in); + i_in <= #1 16'd16384; + repeat (40) @(posedge strobe_in); + i_in <= #1 16'd0; + repeat (41) @(posedge strobe_in); + i_in <= #1 16'd16384; + repeat (40) @(posedge strobe_in); + i_in <= #1 16'd0; + repeat (40) @(posedge strobe_in); + repeat (7) @(posedge clock); + $finish; + end // initial begin +endmodule // test_hb diff --git a/usrp/fpga/sdr_lib/hb/mac.v b/usrp/fpga/sdr_lib/hb/mac.v new file mode 100644 index 000000000..5a270bc73 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/mac.v @@ -0,0 +1,58 @@ + + +module mac (input clock, input reset, input enable, input clear, + input signed [15:0] x, input signed [15:0] y, + input [7:0] shift, output [15:0] z ); + + reg signed [30:0] product; + reg signed [39:0] z_int; + reg signed [15:0] z_shift; + + reg enable_d1; + always @(posedge clock) + enable_d1 <= #1 enable; + + always @(posedge clock) + if(reset | clear) + z_int <= #1 40'd0; + else if(enable_d1) + z_int <= #1 z_int + {{9{product[30]}},product}; + + always @(posedge clock) + product <= #1 x*y; + + always @* // FIXME full case? parallel case? + case(shift) + //8'd0 : z_shift <= z_int[39:24]; + //8'd1 : z_shift <= z_int[38:23]; + //8'd2 : z_shift <= z_int[37:22]; + //8'd3 : z_shift <= z_int[36:21]; + //8'd4 : z_shift <= z_int[35:20]; + //8'd5 : z_shift <= z_int[34:19]; + 8'd6 : z_shift <= z_int[33:18]; + 8'd7 : z_shift <= z_int[32:17]; + 8'd8 : z_shift <= z_int[31:16]; + 8'd9 : z_shift <= z_int[30:15]; + 8'd10 : z_shift <= z_int[29:14]; + 8'd11 : z_shift <= z_int[28:13]; + //8'd12 : z_shift <= z_int[27:12]; + //8'd13 : z_shift <= z_int[26:11]; + //8'd14 : z_shift <= z_int[25:10]; + //8'd15 : z_shift <= z_int[24:9]; + //8'd16 : z_shift <= z_int[23:8]; + //8'd17 : z_shift <= z_int[22:7]; + //8'd18 : z_shift <= z_int[21:6]; + //8'd19 : z_shift <= z_int[20:5]; + //8'd20 : z_shift <= z_int[19:4]; + //8'd21 : z_shift <= z_int[18:3]; + //8'd22 : z_shift <= z_int[17:2]; + //8'd23 : z_shift <= z_int[16:1]; + //8'd24 : z_shift <= z_int[15:0]; + default : z_shift <= z_int[15:0]; + endcase // case(shift) + + // FIXME do we need to saturate? + //assign z = z_shift; + assign z = z_int[15:0]; + +endmodule // mac diff --git a/usrp/fpga/sdr_lib/hb/mult.v b/usrp/fpga/sdr_lib/hb/mult.v new file mode 100644 index 000000000..a8d4cb1b7 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/mult.v @@ -0,0 +1,16 @@ + + +module mult (input clock, input signed [15:0] x, input signed [15:0] y, output reg signed [30:0] product, + input enable_in, output reg enable_out ); + + always @(posedge clock) + if(enable_in) + product <= #1 x*y; + else + product <= #1 31'd0; + + always @(posedge clock) + enable_out <= #1 enable_in; + +endmodule // mult + diff --git a/usrp/fpga/sdr_lib/hb/ram16_2port.v b/usrp/fpga/sdr_lib/hb/ram16_2port.v new file mode 100644 index 000000000..e1761a926 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/ram16_2port.v @@ -0,0 +1,22 @@ + + +module ram16_2port (input clock, input write, + input [3:0] wr_addr, input [15:0] wr_data, + input [3:0] rd_addr1, output reg [15:0] rd_data1, + input [3:0] rd_addr2, output reg [15:0] rd_data2); + + reg [15:0] ram_array [0:31]; + + always @(posedge clock) + rd_data1 <= #1 ram_array[rd_addr1]; + + always @(posedge clock) + rd_data2 <= #1 ram_array[rd_addr2]; + + always @(posedge clock) + if(write) + ram_array[wr_addr] <= #1 wr_data; + +endmodule // ram16_2port + + diff --git a/usrp/fpga/sdr_lib/hb/ram16_2sum.v b/usrp/fpga/sdr_lib/hb/ram16_2sum.v new file mode 100644 index 000000000..559b06fd5 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/ram16_2sum.v @@ -0,0 +1,27 @@ + + +module ram16_2sum (input clock, input write, + input [3:0] wr_addr, input [15:0] wr_data, + input [3:0] rd_addr1, input [3:0] rd_addr2, + output reg [15:0] sum); + + reg signed [15:0] ram_array [0:15]; + reg signed [15:0] a,b; + wire signed [16:0] sum_int; + + always @(posedge clock) + if(write) + ram_array[wr_addr] <= #1 wr_data; + + always @(posedge clock) + begin + a <= #1 ram_array[rd_addr1]; + b <= #1 ram_array[rd_addr2]; + end + + assign sum_int = {a[15],a} + {b[15],b}; + + always @(posedge clock) + sum <= #1 sum_int[16:1] + (sum_int[16]&sum_int[0]); + +endmodule // ram16_2sum diff --git a/usrp/fpga/sdr_lib/hb/ram32_2sum.v b/usrp/fpga/sdr_lib/hb/ram32_2sum.v new file mode 100644 index 000000000..d1f55b7d0 --- /dev/null +++ b/usrp/fpga/sdr_lib/hb/ram32_2sum.v @@ -0,0 +1,22 @@ + + +module ram32_2sum (input clock, input write, + input [4:0] wr_addr, input [15:0] wr_data, + input [4:0] rd_addr1, input [4:0] rd_addr2, + output reg [15:0] sum); + + reg [15:0] ram_array [0:31]; + wire [16:0] sum_int; + + always @(posedge clock) + if(write) + ram_array[wr_addr] <= #1 wr_data; + + assign sum_int = ram_array[rd_addr1] + ram_array[rd_addr2]; + + always @(posedge clock) + sum <= #1 sum_int[16:1] + (sum_int[16]&sum_int[0]); + + +endmodule // ram32_2sum + diff --git a/usrp/fpga/sdr_lib/io_pins.v b/usrp/fpga/sdr_lib/io_pins.v new file mode 100644 index 000000000..da20b3b03 --- /dev/null +++ b/usrp/fpga/sdr_lib/io_pins.v @@ -0,0 +1,52 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2005,2006 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +`include "../../firmware/include/fpga_regs_common.v" +`include "../../firmware/include/fpga_regs_standard.v" + +module io_pins + ( inout wire [15:0] io_0, inout wire [15:0] io_1, inout wire [15:0] io_2, inout wire [15:0] io_3, + input wire [15:0] reg_0, input wire [15:0] reg_1, input wire [15:0] reg_2, input wire [15:0] reg_3, + input clock, input rx_reset, input tx_reset, + input [6:0] serial_addr, input [31:0] serial_data, input serial_strobe); + + reg [15:0] io_0_oe,io_1_oe,io_2_oe,io_3_oe; + + bidir_reg bidir_reg_0 (.tristate(io_0),.oe(io_0_oe),.reg_val(reg_0)); + bidir_reg bidir_reg_1 (.tristate(io_1),.oe(io_1_oe),.reg_val(reg_1)); + bidir_reg bidir_reg_2 (.tristate(io_2),.oe(io_2_oe),.reg_val(reg_2)); + bidir_reg bidir_reg_3 (.tristate(io_3),.oe(io_3_oe),.reg_val(reg_3)); + + // Upper 16 bits are mask for lower 16 + always @(posedge clock) + if(serial_strobe) + case(serial_addr) + `FR_OE_0 : io_0_oe + <= #1 (io_0_oe & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + `FR_OE_1 : io_1_oe + <= #1 (io_1_oe & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + `FR_OE_2 : io_2_oe + <= #1 (io_2_oe & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + `FR_OE_3 : io_3_oe + <= #1 (io_3_oe & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + endcase // case(serial_addr) + +endmodule // io_pins diff --git a/usrp/fpga/sdr_lib/master_control.v b/usrp/fpga/sdr_lib/master_control.v new file mode 100644 index 000000000..d42817c72 --- /dev/null +++ b/usrp/fpga/sdr_lib/master_control.v @@ -0,0 +1,155 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003,2005 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +// Clock, enable, and reset controls for whole system + +module master_control + ( input master_clk, input usbclk, + input wire [6:0] serial_addr, input wire [31:0] serial_data, input wire serial_strobe, + output tx_bus_reset, output rx_bus_reset, + output wire tx_dsp_reset, output wire rx_dsp_reset, + output wire enable_tx, output wire enable_rx, + output wire [7:0] interp_rate, output wire [7:0] decim_rate, + output tx_sample_strobe, output strobe_interp, + output rx_sample_strobe, output strobe_decim, + input tx_empty, + input wire [15:0] debug_0,input wire [15:0] debug_1,input wire [15:0] debug_2,input wire [15:0] debug_3, + output wire [15:0] reg_0, output wire [15:0] reg_1, output wire [15:0] reg_2, output wire [15:0] reg_3 + ); + + // FIXME need a separate reset for all control settings + // Master Controls assignments + wire [7:0] master_controls; + setting_reg #(`FR_MASTER_CTRL) sr_mstr_ctrl(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(master_controls)); + assign enable_tx = master_controls[0]; + assign enable_rx = master_controls[1]; + assign tx_dsp_reset = master_controls[2]; + assign rx_dsp_reset = master_controls[3]; + // Unused - 4-7 + + // Strobe Generators + setting_reg #(`FR_INTERP_RATE) sr_interp(.clock(master_clk),.reset(tx_dsp_reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(interp_rate)); + setting_reg #(`FR_DECIM_RATE) sr_decim(.clock(master_clk),.reset(rx_dsp_reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(decim_rate)); + + strobe_gen da_strobe_gen + ( .clock(master_clk),.reset(tx_dsp_reset),.enable(enable_tx), + .rate(8'd1),.strobe_in(1'b1),.strobe(tx_sample_strobe) ); + + strobe_gen tx_strobe_gen + ( .clock(master_clk),.reset(tx_dsp_reset),.enable(enable_tx), + .rate(interp_rate),.strobe_in(tx_sample_strobe),.strobe(strobe_interp) ); + + assign rx_sample_strobe = 1'b1; + + strobe_gen decim_strobe_gen + ( .clock(master_clk),.reset(rx_dsp_reset),.enable(enable_rx), + .rate(decim_rate),.strobe_in(rx_sample_strobe),.strobe(strobe_decim) ); + + // Reset syncs for bus (usbclk) side + // The RX bus side reset isn't used, the TX bus side one may not be needed + reg tx_reset_bus_sync1, rx_reset_bus_sync1, tx_reset_bus_sync2, rx_reset_bus_sync2; + + always @(posedge usbclk) + begin + tx_reset_bus_sync1 <= #1 tx_dsp_reset; + rx_reset_bus_sync1 <= #1 rx_dsp_reset; + tx_reset_bus_sync2 <= #1 tx_reset_bus_sync1; + rx_reset_bus_sync2 <= #1 rx_reset_bus_sync1; + end + + assign tx_bus_reset = tx_reset_bus_sync2; + assign rx_bus_reset = rx_reset_bus_sync2; + + wire [7:0] txa_refclk, rxa_refclk, txb_refclk, rxb_refclk; + wire txaclk,txbclk,rxaclk,rxbclk; + wire [3:0] debug_en, txcvr_ctrl; + + wire [31:0] txcvr_rxlines, txcvr_txlines; + + setting_reg #(`FR_TX_A_REFCLK) sr_txaref(.clock(master_clk),.reset(tx_dsp_reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(txa_refclk)); + setting_reg #(`FR_RX_A_REFCLK) sr_rxaref(.clock(master_clk),.reset(rx_dsp_reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(rxa_refclk)); + setting_reg #(`FR_TX_B_REFCLK) sr_txbref(.clock(master_clk),.reset(tx_dsp_reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(txb_refclk)); + setting_reg #(`FR_RX_B_REFCLK) sr_rxbref(.clock(master_clk),.reset(rx_dsp_reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(rxb_refclk)); + + setting_reg #(`FR_DEBUG_EN) sr_debugen(.clock(master_clk),.reset(rx_dsp_reset|tx_dsp_reset),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(debug_en)); + + clk_divider clk_div_0 (.reset(tx_dsp_reset),.in_clk(master_clk),.out_clk(txaclk),.ratio(txa_refclk[6:0])); + clk_divider clk_div_1 (.reset(rx_dsp_reset),.in_clk(master_clk),.out_clk(rxaclk),.ratio(rxa_refclk[6:0])); + clk_divider clk_div_2 (.reset(tx_dsp_reset),.in_clk(master_clk),.out_clk(txbclk),.ratio(txb_refclk[6:0])); + clk_divider clk_div_3 (.reset(rx_dsp_reset),.in_clk(master_clk),.out_clk(rxbclk),.ratio(rxb_refclk[6:0])); + + reg [15:0] io_0_reg,io_1_reg,io_2_reg,io_3_reg; + // Upper 16 bits are mask for lower 16 + always @(posedge master_clk) + if(serial_strobe) + case(serial_addr) + `FR_IO_0 : io_0_reg + <= #1 (io_0_reg & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + `FR_IO_1 : io_1_reg + <= #1 (io_1_reg & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + `FR_IO_2 : io_2_reg + <= #1 (io_2_reg & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + `FR_IO_3 : io_3_reg + <= #1 (io_3_reg & ~serial_data[31:16]) | (serial_data[15:0] & serial_data[31:16] ); + endcase // case(serial_addr) + + wire transmit_now = !tx_empty & enable_tx; + wire atr_ctl; + wire [15:0] atr_mask_0, atr_txval_0, atr_rxval_0, atr_mask_1, atr_txval_1, atr_rxval_1, atr_mask_2, atr_txval_2, atr_rxval_2, atr_mask_3, atr_txval_3, atr_rxval_3; + + setting_reg #(`FR_ATR_MASK_0) sr_atr_mask_0(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_mask_0)); + setting_reg #(`FR_ATR_TXVAL_0) sr_atr_txval_0(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_txval_0)); + setting_reg #(`FR_ATR_RXVAL_0) sr_atr_rxval_0(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_rxval_0)); + + setting_reg #(`FR_ATR_MASK_1) sr_atr_mask_1(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_mask_1)); + setting_reg #(`FR_ATR_TXVAL_1) sr_atr_txval_1(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_txval_1)); + setting_reg #(`FR_ATR_RXVAL_1) sr_atr_rxval_1(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_rxval_1)); + + setting_reg #(`FR_ATR_MASK_2) sr_atr_mask_2(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_mask_2)); + setting_reg #(`FR_ATR_TXVAL_2) sr_atr_txval_2(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_txval_2)); + setting_reg #(`FR_ATR_RXVAL_2) sr_atr_rxval_2(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_rxval_2)); + + setting_reg #(`FR_ATR_MASK_3) sr_atr_mask_3(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_mask_3)); + setting_reg #(`FR_ATR_TXVAL_3) sr_atr_txval_3(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_txval_3)); + setting_reg #(`FR_ATR_RXVAL_3) sr_atr_rxval_3(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_rxval_3)); + + //setting_reg #(`FR_ATR_CTL) sr_atr_ctl(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(atr_ctl)); + assign atr_ctl = 1'b1; + + wire [15:0] atr_selected_0 = transmit_now ? atr_txval_0 : atr_rxval_0; + wire [15:0] io_0 = ({{16{atr_ctl}}} & atr_mask_0 & atr_selected_0) | (~({{16{atr_ctl}}} & atr_mask_0) & io_0_reg); + + wire [15:0] atr_selected_1 = transmit_now ? atr_txval_1 : atr_rxval_1; + wire [15:0] io_1 = ({{16{atr_ctl}}} & atr_mask_1 & atr_selected_1) | (~({{16{atr_ctl}}} & atr_mask_1) & io_1_reg); + + wire [15:0] atr_selected_2 = transmit_now ? atr_txval_2 : atr_rxval_2; + wire [15:0] io_2 = ({{16{atr_ctl}}} & atr_mask_2 & atr_selected_2) | (~({{16{atr_ctl}}} & atr_mask_2) & io_2_reg); + + wire [15:0] atr_selected_3 = transmit_now ? atr_txval_3 : atr_rxval_3; + wire [15:0] io_3 = ({{16{atr_ctl}}} & atr_mask_3 & atr_selected_3) | (~({{16{atr_ctl}}} & atr_mask_3) & io_3_reg); + + assign reg_0 = debug_en[0] ? debug_0 : txa_refclk[7] ? {io_0[15:1],txaclk} : io_0; + assign reg_1 = debug_en[1] ? debug_1 : rxa_refclk[7] ? {io_1[15:1],rxaclk} : io_1; + assign reg_2 = debug_en[2] ? debug_2 : txb_refclk[7] ? {io_2[15:1],txbclk} : io_2; + assign reg_3 = debug_en[3] ? debug_3 : rxb_refclk[7] ? {io_3[15:1],rxbclk} : io_3; + + +endmodule // master_control diff --git a/usrp/fpga/sdr_lib/master_control_multi.v b/usrp/fpga/sdr_lib/master_control_multi.v new file mode 100644 index 000000000..af1e0b1f1 --- /dev/null +++ b/usrp/fpga/sdr_lib/master_control_multi.v @@ -0,0 +1,73 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2006 Martin Dudok van Heel +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +`include "usrp_multi.vh" +`include "../../../firmware/include/fpga_regs_common.v" +`include "../../../firmware/include/fpga_regs_standard.v" +// Clock, enable, and reset controls for whole system +// Modified version to enable multi_usrp synchronisation + +module master_control_multi + ( input master_clk, input usbclk, + input wire [6:0] serial_addr, input wire [31:0] serial_data, input wire serial_strobe, + input wire rx_slave_sync, + output tx_bus_reset, output rx_bus_reset, + output wire tx_dsp_reset, output wire rx_dsp_reset, + output wire enable_tx, output wire enable_rx, + output wire sync_rx, + output wire [7:0] interp_rate, output wire [7:0] decim_rate, + output tx_sample_strobe, output strobe_interp, + output rx_sample_strobe, output strobe_decim, + input tx_empty, + input wire [15:0] debug_0,input wire [15:0] debug_1,input wire [15:0] debug_2,input wire [15:0] debug_3, + output wire [15:0] reg_0, output wire [15:0] reg_1, output wire [15:0] reg_2, output wire [15:0] reg_3 + ); + + wire [15:0] reg_1_std; + + master_control master_control_standard + ( .master_clk(master_clk),.usbclk(usbclk), + .serial_addr(serial_addr),.serial_data(serial_data),.serial_strobe(serial_strobe), + .tx_bus_reset(tx_bus_reset),.rx_bus_reset(rx_bus_reset), + .tx_dsp_reset(tx_dsp_reset),.rx_dsp_reset(rx_dsp_reset), + .enable_tx(enable_tx),.enable_rx(enable_rx), + .interp_rate(interp_rate),.decim_rate(decim_rate), + .tx_sample_strobe(tx_sample_strobe),.strobe_interp(strobe_interp), + .rx_sample_strobe(rx_sample_strobe),.strobe_decim(strobe_decim), + .tx_empty(tx_empty), + .debug_0(debug_0),.debug_1(debug_1), + .debug_2(debug_2),.debug_3(debug_3), + .reg_0(reg_0),.reg_1(reg_1_std),.reg_2(reg_2),.reg_3(reg_3) ); + + // FIXME need a separate reset for all control settings + // Master/slave Controls assignments + wire [7:0] rx_master_slave_controls; + setting_reg_masked #(`FR_RX_MASTER_SLAVE) sr_rx_mstr_slv_ctrl(.clock(master_clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(rx_master_slave_controls)); + + assign sync_rx = rx_master_slave_controls[`bitnoFR_RX_SYNC] | (rx_master_slave_controls[`bitnoFR_RX_SYNC_SLAVE] & rx_slave_sync); + //sync if we are told by master_control or if we get a hardware slave sync + //TODO There can be a one sample difference between master and slave sync. + // Maybe use a register for sync_rx which uses the (neg or pos) edge of master_clock and/or rx_slave_sync to trigger + // Or even use a seperate sync_rx_out and sync_rx_internal (which lags behind) + //TODO make output pin not hardwired +assign reg_1 ={(rx_master_slave_controls[`bitnoFR_RX_SYNC_MASTER])? sync_rx:reg_1_std[15],reg_1_std[14:0]}; + + +endmodule // master_control diff --git a/usrp/fpga/sdr_lib/phase_acc.v b/usrp/fpga/sdr_lib/phase_acc.v new file mode 100755 index 000000000..d00716fd0 --- /dev/null +++ b/usrp/fpga/sdr_lib/phase_acc.v @@ -0,0 +1,52 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + + +// Basic Phase accumulator for DDS + + +module phase_acc (clk,reset,enable,strobe,serial_addr,serial_data,serial_strobe,phase); + parameter FREQADDR = 0; + parameter PHASEADDR = 0; + parameter resolution = 32; + + input clk, reset, enable, strobe; + input [6:0] serial_addr; + input [31:0] serial_data; + input serial_strobe; + + output reg [resolution-1:0] phase; + wire [resolution-1:0] freq; + + setting_reg #(FREQADDR) sr_rxfreq0(.clock(clk),.reset(1'b0),.strobe(serial_strobe),.addr(serial_addr),.in(serial_data),.out(freq)); + + always @(posedge clk) + if(reset) + phase <= #1 32'b0; + else if(serial_strobe & (serial_addr == PHASEADDR)) + phase <= #1 serial_data; + else if(enable & strobe) + phase <= #1 phase + freq; + +endmodule // phase_acc + + diff --git a/usrp/fpga/sdr_lib/ram.v b/usrp/fpga/sdr_lib/ram.v new file mode 100644 index 000000000..fb64cdeae --- /dev/null +++ b/usrp/fpga/sdr_lib/ram.v @@ -0,0 +1,16 @@ + + +module ram (input clock, input write, + input [4:0] wr_addr, input [15:0] wr_data, + input [4:0] rd_addr, output reg [15:0] rd_data); + + reg [15:0] ram_array [0:31]; + + always @(posedge clock) + rd_data <= #1 ram_array[rd_addr]; + + always @(posedge clock) + if(write) + ram_array[wr_addr] <= #1 wr_data; + +endmodule // ram diff --git a/usrp/fpga/sdr_lib/ram16.v b/usrp/fpga/sdr_lib/ram16.v new file mode 100644 index 000000000..0c93da2be --- /dev/null +++ b/usrp/fpga/sdr_lib/ram16.v @@ -0,0 +1,17 @@ + + +module ram16 (input clock, input write, + input [3:0] wr_addr, input [15:0] wr_data, + input [3:0] rd_addr, output reg [15:0] rd_data); + + reg [15:0] ram_array [0:15]; + + always @(posedge clock) + rd_data <= #1 ram_array[rd_addr]; + + always @(posedge clock) + if(write) + ram_array[wr_addr] <= #1 wr_data; + +endmodule // ram16 + diff --git a/usrp/fpga/sdr_lib/ram32.v b/usrp/fpga/sdr_lib/ram32.v new file mode 100644 index 000000000..064e2735a --- /dev/null +++ b/usrp/fpga/sdr_lib/ram32.v @@ -0,0 +1,17 @@ + + +module ram32 (input clock, input write, + input [4:0] wr_addr, input [15:0] wr_data, + input [4:0] rd_addr, output reg [15:0] rd_data); + + reg [15:0] ram_array [0:31]; + + always @(posedge clock) + rd_data <= #1 ram_array[rd_addr]; + + always @(posedge clock) + if(write) + ram_array[wr_addr] <= #1 wr_data; + +endmodule // ram32 + diff --git a/usrp/fpga/sdr_lib/ram64.v b/usrp/fpga/sdr_lib/ram64.v new file mode 100644 index 000000000..084545808 --- /dev/null +++ b/usrp/fpga/sdr_lib/ram64.v @@ -0,0 +1,16 @@ + + +module ram64 (input clock, input write, + input [5:0] wr_addr, input [15:0] wr_data, + input [5:0] rd_addr, output reg [15:0] rd_data); + + reg [15:0] ram_array [0:63]; + + always @(posedge clock) + rd_data <= #1 ram_array[rd_addr]; + + always @(posedge clock) + if(write) + ram_array[wr_addr] <= #1 wr_data; + +endmodule // ram64 diff --git a/usrp/fpga/sdr_lib/rssi.v b/usrp/fpga/sdr_lib/rssi.v new file mode 100644 index 000000000..e45e2148c --- /dev/null +++ b/usrp/fpga/sdr_lib/rssi.v @@ -0,0 +1,30 @@ + + +module rssi (input clock, input reset, input enable, + input [11:0] adc, output [15:0] rssi, output [15:0] over_count); + + wire over_hi = (adc == 12'h7FF); + wire over_lo = (adc == 12'h800); + wire over = over_hi | over_lo; + + reg [25:0] over_count_int; + always @(posedge clock) + if(reset | ~enable) + over_count_int <= #1 26'd0; + else + over_count_int <= #1 over_count_int + (over ? 26'd65535 : 26'd0) - over_count_int[25:10]; + + assign over_count = over_count_int[25:10]; + + wire [11:0] abs_adc = adc[11] ? ~adc : adc; + + reg [25:0] rssi_int; + always @(posedge clock) + if(reset | ~enable) + rssi_int <= #1 26'd0; + else + rssi_int <= #1 rssi_int + abs_adc - rssi_int[25:10]; + + assign rssi = rssi_int[25:10]; + +endmodule // rssi diff --git a/usrp/fpga/sdr_lib/rx_buffer.v b/usrp/fpga/sdr_lib/rx_buffer.v new file mode 100644 index 000000000..70c800e3d --- /dev/null +++ b/usrp/fpga/sdr_lib/rx_buffer.v @@ -0,0 +1,182 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +// Interface to Cypress FX2 bus +// A packet is 512 Bytes. Each fifo line is 2 bytes +// Fifo has 1024 or 2048 lines + +`include "../../firmware/include/fpga_regs_common.v" +`include "../../firmware/include/fpga_regs_standard.v" + +module rx_buffer + ( input usbclk, + input bus_reset, // Not used in RX + input reset, // DSP side reset (used here), do not reset registers + input reset_regs, //Only reset registers + output [15:0] usbdata, + input RD, + output wire have_pkt_rdy, + output reg rx_overrun, + input wire [3:0] channels, + input wire [15:0] ch_0, + input wire [15:0] ch_1, + input wire [15:0] ch_2, + input wire [15:0] ch_3, + input wire [15:0] ch_4, + input wire [15:0] ch_5, + input wire [15:0] ch_6, + input wire [15:0] ch_7, + input rxclk, + input rxstrobe, + input clear_status, + input [6:0] serial_addr, input [31:0] serial_data, input serial_strobe, + output [15:0] debugbus + ); + + wire [15:0] fifodata, fifodata_8; + reg [15:0] fifodata_16; + + wire [11:0] rxfifolevel; + wire rx_empty, rx_full; + + wire bypass_hb, want_q; + wire [4:0] bitwidth; + wire [3:0] bitshift; + + setting_reg #(`FR_RX_FORMAT) sr_rxformat(.clock(rxclk),.reset(reset_regs), + .strobe(serial_strobe),.addr(serial_addr),.in(serial_data), + .out({bypass_hb,want_q,bitwidth,bitshift})); + + // Receive FIFO (ADC --> USB) + + // 257 Bug Fix + reg [8:0] read_count; + always @(negedge usbclk) + if(bus_reset) + read_count <= #1 9'd0; + else if(RD & ~read_count[8]) + read_count <= #1 read_count + 9'd1; + else + read_count <= #1 RD ? read_count : 9'b0; + + // Detect overrun + always @(posedge rxclk) + if(reset) + rx_overrun <= 1'b0; + else if(rxstrobe & (store_next != 0)) + rx_overrun <= 1'b1; + else if(clear_status) + rx_overrun <= 1'b0; + + reg [3:0] store_next; + always @(posedge rxclk) + if(reset) + store_next <= #1 4'd0; + else if(rxstrobe & (store_next == 0)) + store_next <= #1 4'd1; + else if(~rx_full & (store_next == channels)) + store_next <= #1 4'd0; + else if(~rx_full & (bitwidth == 5'd8) & (store_next == (channels>>1))) + store_next <= #1 4'd0; + else if(~rx_full & (store_next != 0)) + store_next <= #1 store_next + 4'd1; + + assign fifodata = (bitwidth == 5'd8) ? fifodata_8 : fifodata_16; + + assign fifodata_8 = {round_8(top),round_8(bottom)}; + reg [15:0] top,bottom; + + function [7:0] round_8; + input [15:0] in_val; + + round_8 = in_val[15:8] + (in_val[15] & |in_val[7:0]); + endfunction // round_8 + + always @* + case(store_next) + 4'd1 : begin + bottom = ch_0; + top = ch_1; + end + 4'd2 : begin + bottom = ch_2; + top = ch_3; + end + 4'd3 : begin + bottom = ch_4; + top = ch_5; + end + 4'd4 : begin + bottom = ch_6; + top = ch_7; + end + default : begin + top = 16'hFFFF; + bottom = 16'hFFFF; + end + endcase // case(store_next) + + always @* + case(store_next) + 4'd1 : fifodata_16 = ch_0; + 4'd2 : fifodata_16 = ch_1; + 4'd3 : fifodata_16 = ch_2; + 4'd4 : fifodata_16 = ch_3; + 4'd5 : fifodata_16 = ch_4; + 4'd6 : fifodata_16 = ch_5; + 4'd7 : fifodata_16 = ch_6; + 4'd8 : fifodata_16 = ch_7; + default : fifodata_16 = 16'hFFFF; + endcase // case(store_next) + + fifo_4k rxfifo + ( .data ( fifodata ), + .wrreq (~rx_full & (store_next != 0)), + .wrclk ( rxclk ), + + .q ( usbdata ), + .rdreq ( RD & ~read_count[8] ), + .rdclk ( ~usbclk ), + + .aclr ( reset ), // This one is asynchronous, so we can use either reset + + .rdempty ( rx_empty ), + .rdusedw ( rxfifolevel ), + .wrfull ( rx_full ), + .wrusedw ( ) + ); + + assign have_pkt_rdy = (rxfifolevel >= 256); + + // Debugging Aids + assign debugbus[0] = RD; + assign debugbus[1] = rx_overrun; + assign debugbus[2] = read_count[8]; + assign debugbus[3] = rx_full; + assign debugbus[4] = rxstrobe; + assign debugbus[5] = usbclk; + assign debugbus[6] = have_pkt_rdy; + assign debugbus[10:7] = store_next; + //assign debugbus[15:11] = rxfifolevel[4:0]; + assign debugbus[15:11] = bitwidth; + +endmodule // rx_buffer + diff --git a/usrp/fpga/sdr_lib/rx_chain.v b/usrp/fpga/sdr_lib/rx_chain.v new file mode 100644 index 000000000..4031e6bfb --- /dev/null +++ b/usrp/fpga/sdr_lib/rx_chain.v @@ -0,0 +1,105 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +// Following defines conditionally include RX path circuitry + +`include "usrp_std.vh" +module rx_chain + (input clock, + input reset, + input enable, + input wire [7:0] decim_rate, + input sample_strobe, + input decimator_strobe, + output wire hb_strobe, + input [6:0] serial_addr, input [31:0] serial_data, input serial_strobe, + input wire [15:0] i_in, + input wire [15:0] q_in, + output wire [15:0] i_out, + output wire [15:0] q_out, + output wire [15:0] debugdata,output wire [15:0] debugctrl + ); + + parameter FREQADDR = 0; + parameter PHASEADDR = 0; + + wire [31:0] phase; + wire [15:0] bb_i, bb_q; + wire [15:0] hb_in_i, hb_in_q; + + assign debugdata = hb_in_i; + +`ifdef RX_NCO_ON + phase_acc #(FREQADDR,PHASEADDR,32) rx_phase_acc + (.clk(clock),.reset(reset),.enable(enable), + .serial_addr(serial_addr),.serial_data(serial_data),.serial_strobe(serial_strobe), + .strobe(sample_strobe),.phase(phase) ); + + cordic rx_cordic + ( .clock(clock),.reset(reset),.enable(enable), + .xi(i_in),.yi(q_in),.zi(phase[31:16]), + .xo(bb_i),.yo(bb_q),.zo() ); +`else + assign bb_i = i_in; + assign bb_q = q_in; + assign sample_strobe = 1; +`endif // !`ifdef RX_NCO_ON + +`ifdef RX_CIC_ON + cic_decim cic_decim_i_0 + ( .clock(clock),.reset(reset),.enable(enable), + .rate(decim_rate),.strobe_in(sample_strobe),.strobe_out(decimator_strobe), + .signal_in(bb_i),.signal_out(hb_in_i) ); +`else + assign hb_in_i = bb_i; + assign decimator_strobe = sample_strobe; +`endif + +`ifdef RX_HB_ON + halfband_decim hbd_i_0 + ( .clock(clock),.reset(reset),.enable(enable), + .strobe_in(decimator_strobe),.strobe_out(hb_strobe), + .data_in(hb_in_i),.data_out(i_out),.debugctrl(debugctrl) ); +`else + assign i_out = hb_in_i; + assign hb_strobe = decimator_strobe; +`endif + +`ifdef RX_CIC_ON + cic_decim cic_decim_q_0 + ( .clock(clock),.reset(reset),.enable(enable), + .rate(decim_rate),.strobe_in(sample_strobe),.strobe_out(decimator_strobe), + .signal_in(bb_q),.signal_out(hb_in_q) ); +`else + assign hb_in_q = bb_q; +`endif + +`ifdef RX_HB_ON + halfband_decim hbd_q_0 + ( .clock(clock),.reset(reset),.enable(enable), + .strobe_in(decimator_strobe),.strobe_out(), + .data_in(hb_in_q),.data_out(q_out) ); +`else + assign q_out = hb_in_q; +`endif + + +endmodule // rx_chain diff --git a/usrp/fpga/sdr_lib/rx_chain_dual.v b/usrp/fpga/sdr_lib/rx_chain_dual.v new file mode 100644 index 000000000..698859468 --- /dev/null +++ b/usrp/fpga/sdr_lib/rx_chain_dual.v @@ -0,0 +1,103 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +module rx_chain_dual + (input clock, + input clock_2x, + input reset, + input enable, + input wire [7:0] decim_rate, + input sample_strobe, + input decimator_strobe, + input wire [31:0] freq0, + input wire [15:0] i_in0, + input wire [15:0] q_in0, + output wire [15:0] i_out0, + output wire [15:0] q_out0, + input wire [31:0] freq1, + input wire [15:0] i_in1, + input wire [15:0] q_in1, + output wire [15:0] i_out1, + output wire [15:0] q_out1 + ); + + wire [15:0] phase; + wire [15:0] bb_i, bb_q; + wire [15:0] i_in, q_in; + + wire [31:0] phase0; + wire [31:0] phase1; + reg [15:0] bb_i0, bb_q0; + reg [15:0] bb_i1, bb_q1; + + // We want to time-share the CORDIC by double-clocking it + + phase_acc rx_phase_acc_0 + (.clk(clock),.reset(reset),.enable(enable), + .strobe(sample_strobe),.freq(freq0),.phase(phase0) ); + + phase_acc rx_phase_acc_1 + (.clk(clock),.reset(reset),.enable(enable), + .strobe(sample_strobe),.freq(freq1),.phase(phase1) ); + + assign phase = clock ? phase0[31:16] : phase1[31:16]; + assign i_in = clock ? i_in0 : i_in1; + assign q_in = clock ? q_in0 : q_in1; + +// This appears reversed because of the number of CORDIC stages + always @(posedge clock_2x) + if(clock) + begin + bb_i1 <= #1 bb_i; + bb_q1 <= #1 bb_q; + end + else + begin + bb_i0 <= #1 bb_i; + bb_q0 <= #1 bb_q; + end + + cordic rx_cordic + ( .clock(clock_2x),.reset(reset),.enable(enable), + .xi(i_in),.yi(q_in),.zi(phase), + .xo(bb_i),.yo(bb_q),.zo() ); + + cic_decim cic_decim_i_0 + ( .clock(clock),.reset(reset),.enable(enable), + .rate(decim_rate),.strobe_in(sample_strobe),.strobe_out(decimator_strobe), + .signal_in(bb_i0),.signal_out(i_out0) ); + + cic_decim cic_decim_q_0 + ( .clock(clock),.reset(reset),.enable(enable), + .rate(decim_rate),.strobe_in(sample_strobe),.strobe_out(decimator_strobe), + .signal_in(bb_q0),.signal_out(q_out0) ); + + cic_decim cic_decim_i_1 + ( .clock(clock),.reset(reset),.enable(enable), + .rate(decim_rate),.strobe_in(sample_strobe),.strobe_out(decimator_strobe), + .signal_in(bb_i1),.signal_out(i_out1) ); + + cic_decim cic_decim_q_1 + ( .clock(clock),.reset(reset),.enable(enable), + .rate(decim_rate),.strobe_in(sample_strobe),.strobe_out(decimator_strobe), + .signal_in(bb_q1),.signal_out(q_out1) ); + +endmodule // rx_chain diff --git a/usrp/fpga/sdr_lib/rx_dcoffset.v b/usrp/fpga/sdr_lib/rx_dcoffset.v new file mode 100644 index 000000000..3be475ed6 --- /dev/null +++ b/usrp/fpga/sdr_lib/rx_dcoffset.v @@ -0,0 +1,22 @@ + + +module rx_dcoffset (input clock, input enable, input reset, + input signed [15:0] adc_in, output signed [15:0] adc_out, + input wire [6:0] serial_addr, input wire [31:0] serial_data, input serial_strobe); + parameter MYADDR = 0; + + reg signed [31:0] integrator; + wire signed [15:0] scaled_integrator = integrator[31:16] + (integrator[31] & |integrator[15:0]); + assign adc_out = adc_in - scaled_integrator; + + // FIXME do we need signed? + //FIXME What do we do when clipping? + always @(posedge clock) + if(reset) + integrator <= #1 32'd0; + else if(serial_strobe & (MYADDR == serial_addr)) + integrator <= #1 {serial_data[15:0],16'd0}; + else if(enable) + integrator <= #1 integrator + adc_out; + +endmodule // rx_dcoffset diff --git a/usrp/fpga/sdr_lib/serial_io.v b/usrp/fpga/sdr_lib/serial_io.v new file mode 100644 index 000000000..1fe43c959 --- /dev/null +++ b/usrp/fpga/sdr_lib/serial_io.v @@ -0,0 +1,118 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003,2004 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + + +// Serial Control Bus from Cypress chip + +module serial_io + ( input master_clk, + input serial_clock, + input serial_data_in, + input enable, + input reset, + inout wire serial_data_out, + output reg [6:0] serial_addr, + output reg [31:0] serial_data, + output wire serial_strobe, + input wire [31:0] readback_0, + input wire [31:0] readback_1, + input wire [31:0] readback_2, + input wire [31:0] readback_3, + input wire [31:0] readback_4, + input wire [31:0] readback_5, + input wire [31:0] readback_6, + input wire [31:0] readback_7 + ); + + reg is_read; + reg [7:0] ser_ctr; + reg write_done; + + assign serial_data_out = is_read ? serial_data[31] : 1'bz; + + always @(posedge serial_clock, posedge reset, negedge enable) + if(reset) + ser_ctr <= #1 8'd0; + else if(~enable) + ser_ctr <= #1 8'd0; + else if(ser_ctr == 39) + ser_ctr <= #1 8'd0; + else + ser_ctr <= #1 ser_ctr + 8'd1; + + always @(posedge serial_clock, posedge reset, negedge enable) + if(reset) + is_read <= #1 1'b0; + else if(~enable) + is_read <= #1 1'b0; + else if((ser_ctr == 7)&&(serial_addr[6]==1)) + is_read <= #1 1'b1; + + always @(posedge serial_clock, posedge reset) + if(reset) + begin + serial_addr <= #1 7'b0; + serial_data <= #1 32'b0; + write_done <= #1 1'b0; + end + else if(~enable) + begin + //serial_addr <= #1 7'b0; + //serial_data <= #1 32'b0; + write_done <= #1 1'b0; + end + else + begin + if(~is_read && (ser_ctr == 39)) + write_done <= #1 1'b1; + else + write_done <= #1 1'b0; + if(is_read & (ser_ctr==8)) + case (serial_addr) + 7'd1: serial_data <= #1 readback_0; + 7'd2: serial_data <= #1 readback_1; + 7'd3: serial_data <= #1 readback_2; + 7'd4: serial_data <= #1 readback_3; + 7'd5: serial_data <= #1 readback_4; + 7'd6: serial_data <= #1 readback_5; + 7'd7: serial_data <= #1 readback_6; + 7'd8: serial_data <= #1 readback_7; + default: serial_data <= #1 32'd0; + endcase // case(serial_addr) + else if(ser_ctr >= 8) + serial_data <= #1 {serial_data[30:0],serial_data_in}; + else if(ser_ctr < 8) + serial_addr <= #1 {serial_addr[5:0],serial_data_in}; + end // else: !if(~enable) + + reg enable_d1, enable_d2; + always @(posedge master_clk) + begin + enable_d1 <= #1 enable; + enable_d2 <= #1 enable_d1; + end + + assign serial_strobe = enable_d2 & ~enable_d1; + +endmodule // serial_io + + diff --git a/usrp/fpga/sdr_lib/setting_reg.v b/usrp/fpga/sdr_lib/setting_reg.v new file mode 100644 index 000000000..3d31a9efb --- /dev/null +++ b/usrp/fpga/sdr_lib/setting_reg.v @@ -0,0 +1,23 @@ + + +module setting_reg + ( input clock, input reset, input strobe, input wire [6:0] addr, + input wire [31:0] in, output reg [31:0] out, output reg changed); + parameter my_addr = 0; + + always @(posedge clock) + if(reset) + begin + out <= #1 32'd0; + changed <= #1 1'b0; + end + else + if(strobe & (my_addr==addr)) + begin + out <= #1 in; + changed <= #1 1'b1; + end + else + changed <= #1 1'b0; + +endmodule // setting_reg diff --git a/usrp/fpga/sdr_lib/setting_reg_masked.v b/usrp/fpga/sdr_lib/setting_reg_masked.v new file mode 100644 index 000000000..72f7e21eb --- /dev/null +++ b/usrp/fpga/sdr_lib/setting_reg_masked.v @@ -0,0 +1,26 @@ + + +module setting_reg_masked + ( input clock, input reset, input strobe, input wire [6:0] addr, + input wire [31:0] in, output reg [31:0] out, output reg changed); +/* upper 16 bits are mask, lower 16 bits are value + * Note that you get a 16 bit register, not a 32 bit one */ + + parameter my_addr = 0; + + always @(posedge clock) + if(reset) + begin + out <= #1 32'd0; + changed <= #1 1'b0; + end + else + if(strobe & (my_addr==addr)) + begin + out <= #1 (out & ~in[31:16]) | (in[15:0] & in[31:16] ); + changed <= #1 1'b1; + end + else + changed <= #1 1'b0; + +endmodule // setting_reg_masked diff --git a/usrp/fpga/sdr_lib/sign_extend.v b/usrp/fpga/sdr_lib/sign_extend.v new file mode 100644 index 000000000..2417909bd --- /dev/null +++ b/usrp/fpga/sdr_lib/sign_extend.v @@ -0,0 +1,35 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + + +// Sign extension "macro" +// bits_out should be greater than bits_in + +module sign_extend (in,out); + parameter bits_in=0; // FIXME Quartus insists on a default + parameter bits_out=0; + + input [bits_in-1:0] in; + output [bits_out-1:0] out; + + assign out = {{(bits_out-bits_in){in[bits_in-1]}},in}; + +endmodule diff --git a/usrp/fpga/sdr_lib/strobe_gen.v b/usrp/fpga/sdr_lib/strobe_gen.v new file mode 100644 index 000000000..0511b6a1d --- /dev/null +++ b/usrp/fpga/sdr_lib/strobe_gen.v @@ -0,0 +1,44 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +module strobe_gen + ( input clock, + input reset, + input enable, + input [7:0] rate, + input strobe_in, + output wire strobe ); + +// parameter width = 8; + + reg [7:0] counter; + assign strobe = ~|counter && enable && strobe_in; + + always @(posedge clock) + if(reset | ~enable) + counter <= #1 8'd0; + else if(strobe_in) + if(counter == 0) + counter <= #1 rate; + else + counter <= #1 counter - 8'd1; + +endmodule // strobe_gen diff --git a/usrp/fpga/sdr_lib/tx_buffer.v b/usrp/fpga/sdr_lib/tx_buffer.v new file mode 100644 index 000000000..cae6607b3 --- /dev/null +++ b/usrp/fpga/sdr_lib/tx_buffer.v @@ -0,0 +1,138 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +// Interface to Cypress FX2 bus +// A packet is 512 Bytes. Each fifo line is 2 bytes +// Fifo has 1024 or 2048 lines + +module tx_buffer + ( input usbclk, + input bus_reset, // Used here for the 257-Hack to fix the FX2 bug + input reset, // standard DSP-side reset + input [15:0] usbdata, + input wire WR, + output wire have_space, + output reg tx_underrun, + input wire [3:0] channels, + output reg [15:0] tx_i_0, + output reg [15:0] tx_q_0, + output reg [15:0] tx_i_1, + output reg [15:0] tx_q_1, + output reg [15:0] tx_i_2, + output reg [15:0] tx_q_2, + output reg [15:0] tx_i_3, + output reg [15:0] tx_q_3, + input txclk, + input txstrobe, + input clear_status, + output wire tx_empty, + output [11:0] debugbus + ); + + wire [11:0] txfifolevel; + reg [8:0] write_count; + wire tx_full; + wire [15:0] fifodata; + wire rdreq; + + reg [3:0] load_next; + + // DAC Side of FIFO + assign rdreq = ((load_next != channels) & !tx_empty); + + always @(posedge txclk) + if(reset) + begin + {tx_i_0,tx_q_0,tx_i_1,tx_q_1,tx_i_2,tx_q_2,tx_i_3,tx_q_3} + <= #1 128'h0; + load_next <= #1 4'd0; + end + else + if((load_next != channels) & !tx_empty) + begin + load_next <= #1 load_next + 4'd1; + case(load_next) + 4'd0 : tx_i_0 <= #1 fifodata; + 4'd1 : tx_q_0 <= #1 fifodata; + 4'd2 : tx_i_1 <= #1 fifodata; + 4'd3 : tx_q_1 <= #1 fifodata; + 4'd4 : tx_i_2 <= #1 fifodata; + 4'd5 : tx_q_2 <= #1 fifodata; + 4'd6 : tx_i_3 <= #1 fifodata; + 4'd7 : tx_q_3 <= #1 fifodata; + endcase // case(load_next) + end // if ((load_next != channels) & !tx_empty) + else if(txstrobe & (load_next == channels)) + begin + load_next <= #1 4'd0; + end + + // USB Side of FIFO + assign have_space = (txfifolevel <= (4095-256)); + + always @(posedge usbclk) + if(bus_reset) // Use bus reset because this is on usbclk + write_count <= #1 0; + else if(WR & ~write_count[8]) + write_count <= #1 write_count + 9'd1; + else + write_count <= #1 WR ? write_count : 9'b0; + + // Detect Underruns + always @(posedge txclk) + if(reset) + tx_underrun <= 1'b0; + else if(txstrobe & (load_next != channels)) + tx_underrun <= 1'b1; + else if(clear_status) + tx_underrun <= 1'b0; + + // FIFO + fifo_4k txfifo + ( .data ( usbdata ), + .wrreq ( WR & ~write_count[8] ), + .wrclk ( usbclk ), + + .q ( fifodata ), + .rdreq ( rdreq ), + .rdclk ( txclk ), + + .aclr ( reset ), // asynch, so we can use either + + .rdempty ( tx_empty ), + .rdusedw ( ), + .wrfull ( tx_full ), + .wrusedw ( txfifolevel ) + ); + + // Debugging Aids + assign debugbus[0] = WR; + assign debugbus[1] = have_space; + assign debugbus[2] = tx_empty; + assign debugbus[3] = tx_full; + assign debugbus[4] = tx_underrun; + assign debugbus[5] = write_count[8]; + assign debugbus[6] = txstrobe; + assign debugbus[7] = rdreq; + assign debugbus[11:8] = load_next; + +endmodule // tx_buffer + diff --git a/usrp/fpga/sdr_lib/tx_chain.v b/usrp/fpga/sdr_lib/tx_chain.v new file mode 100644 index 000000000..8f0a17a52 --- /dev/null +++ b/usrp/fpga/sdr_lib/tx_chain.v @@ -0,0 +1,65 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +module tx_chain + (input clock, + input reset, + input enable, + input wire [7:0] interp_rate, + input sample_strobe, + input interpolator_strobe, + input wire [31:0] freq, + input wire [15:0] i_in, + input wire [15:0] q_in, + output wire [15:0] i_out, + output wire [15:0] q_out + ); + + wire [15:0] bb_i, bb_q; + + cic_interp cic_interp_i + ( .clock(clock),.reset(reset),.enable(enable), + .rate(interp_rate),.strobe_in(interpolator_strobe),.strobe_out(sample_strobe), + .signal_in(i_in),.signal_out(bb_i) ); + + cic_interp cic_interp_q + ( .clock(clock),.reset(reset),.enable(enable), + .rate(interp_rate),.strobe_in(interpolator_strobe),.strobe_out(sample_strobe), + .signal_in(q_in),.signal_out(bb_q) ); + +`define NOCORDIC_TX +`ifdef NOCORDIC_TX + assign i_out = bb_i; + assign q_out = bb_q; +`else + wire [31:0] phase; + + phase_acc phase_acc_tx + (.clk(clock),.reset(reset),.enable(enable), + .strobe(sample_strobe),.freq(freq),.phase(phase) ); + + cordic tx_cordic_0 + ( .clock(clock),.reset(reset),.enable(sample_strobe), + .xi(bb_i),.yi(bb_q),.zi(phase[31:16]), + .xo(i_out),.yo(q_out),.zo() ); +`endif + +endmodule // tx_chain diff --git a/usrp/fpga/sdr_lib/tx_chain_hb.v b/usrp/fpga/sdr_lib/tx_chain_hb.v new file mode 100644 index 000000000..6cbe29c00 --- /dev/null +++ b/usrp/fpga/sdr_lib/tx_chain_hb.v @@ -0,0 +1,76 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003 Matt Ettus +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// + +module tx_chain_hb + (input clock, + input reset, + input enable, + input wire [7:0] interp_rate, + input sample_strobe, + input interpolator_strobe, + input hb_strobe, + input wire [31:0] freq, + input wire [15:0] i_in, + input wire [15:0] q_in, + output wire [15:0] i_out, + output wire [15:0] q_out, +output wire [15:0] debug, output [15:0] hb_i_out + ); +assign debug[15:13] = {sample_strobe,hb_strobe,interpolator_strobe}; + + wire [15:0] bb_i, bb_q; + wire [15:0] hb_i_out, hb_q_out; + + halfband_interp hb + (.clock(clock),.reset(reset),.enable(enable), + .strobe_in(interpolator_strobe),.strobe_out(hb_strobe), + .signal_in_i(i_in),.signal_in_q(q_in), + .signal_out_i(hb_i_out),.signal_out_q(hb_q_out), + .debug(debug[12:0])); + + cic_interp cic_interp_i + ( .clock(clock),.reset(reset),.enable(enable), + .rate(interp_rate),.strobe_in(hb_strobe),.strobe_out(sample_strobe), + .signal_in(hb_i_out),.signal_out(bb_i) ); + + cic_interp cic_interp_q + ( .clock(clock),.reset(reset),.enable(enable), + .rate(interp_rate),.strobe_in(hb_strobe),.strobe_out(sample_strobe), + .signal_in(hb_q_out),.signal_out(bb_q) ); + +`define NOCORDIC_TX +`ifdef NOCORDIC_TX + assign i_out = bb_i; + assign q_out = bb_q; +`else + wire [31:0] phase; + + phase_acc phase_acc_tx + (.clk(clock),.reset(reset),.enable(enable), + .strobe(sample_strobe),.freq(freq),.phase(phase) ); + + cordic tx_cordic_0 + ( .clock(clock),.reset(reset),.enable(sample_strobe), + .xi(bb_i),.yi(bb_q),.zi(phase[31:16]), + .xo(i_out),.yo(q_out),.zo() ); +`endif + +endmodule // tx_chain |