diff options
author | jcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5> | 2008-09-08 01:00:12 +0000 |
---|---|---|
committer | jcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5> | 2008-09-08 01:00:12 +0000 |
commit | 61f2f0214c5999ea42a368a4fc99f03d8eb28d1e (patch) | |
tree | e7e24a9adc05ff1422fe3ada9926a51634741b47 /sdr_lib | |
download | uhd-61f2f0214c5999ea42a368a4fc99f03d8eb28d1e.tar.gz uhd-61f2f0214c5999ea42a368a4fc99f03d8eb28d1e.tar.bz2 uhd-61f2f0214c5999ea42a368a4fc99f03d8eb28d1e.zip |
Merged r9433:9527 from features/gr-usrp2 into trunk. Adds usrp2 and gr-usrp2 top-level components. Trunk passes distcheck with mb-gcc installed, but currently not without them. The key issue is that when mb-gcc is not installed, the build system skips over the usrp2/firmware directory, and the firmware include files don't get put into the dist tarball. But we can't do the usual DIST_SUBDIRS method as the firmware is a subpackage.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@9528 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'sdr_lib')
60 files changed, 4828 insertions, 0 deletions
diff --git a/sdr_lib/HB.sav b/sdr_lib/HB.sav new file mode 100644 index 000000000..c5087e8a6 --- /dev/null +++ b/sdr_lib/HB.sav @@ -0,0 +1,56 @@ +[size] 1400 967 +[pos] -1 -1 +*-46.395245 2565000000000000 -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 +[treeopen] hb_dec_tb. +@420 +hb_dec_tb.data_in[17:0] +@28 +hb_dec_tb.strobe_in +hb_dec_tb.strobe_out +hb_dec_tb.uut.write_even +@22 +hb_dec_tb.uut.addr_even[3:0] +@420 +hb_dec_tb.uut.data_even[17:0] +hb_dec_tb.uut.data_odd_a[17:0] +hb_dec_tb.uut.data_odd_b[17:0] +hb_dec_tb.uut.data_odd_c[17:0] +hb_dec_tb.uut.data_odd_d[17:0] +@28 +hb_dec_tb.uut.write_odd +@420 +hb_dec_tb.uut.prod1[35:0] +hb_dec_tb.uut.prod2[35:0] +@24 +hb_dec_tb.uut.phase[2:0] +@28 +hb_dec_tb.uut.stb_in +hb_dec_tb.uut.stb_out +@420 +hb_dec_tb.uut.sum2[17:0] +hb_dec_tb.uut.stb_out_pre[15:0] +@28 +hb_dec_tb.uut.do_acc +hb_dec_tb.uut.clear +@420 +hb_dec_tb.uut.sum1[17:0] +hb_dec_tb.uut.coeff1[17:0] +hb_dec_tb.uut.prod1[35:0] +hb_dec_tb.uut.prod2[35:0] +hb_dec_tb.uut.final_sum[17:0] +hb_dec_tb.uut.coeff2[17:0] +hb_dec_tb.uut.sum_of_prod[21:0] +hb_dec_tb.data_out[17:0] +@28 +hb_dec_tb.uut.do_acc +hb_dec_tb.uut.clear +@24 +hb_dec_tb.uut.addr_odd_a[3:0] +hb_dec_tb.uut.addr_odd_b[3:0] +hb_dec_tb.uut.addr_odd_c[3:0] +hb_dec_tb.uut.addr_odd_d[3:0] +@28 +hb_dec_tb.uut.write_odd +hb_dec_tb.uut.write_even +@22 +hb_dec_tb.uut.data_even[17:0] diff --git a/sdr_lib/SMALL_HB.sav b/sdr_lib/SMALL_HB.sav new file mode 100644 index 000000000..96ba00636 --- /dev/null +++ b/sdr_lib/SMALL_HB.sav @@ -0,0 +1,40 @@ +[size] 1400 967 +[pos] -1 -1 +*-11.608687 1834 -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 +[treeopen] small_hb_dec_tb. +[treeopen] small_hb_dec_tb.uut. +@28 +small_hb_dec_tb.uut.clk +small_hb_dec_tb.uut.phase +@10421 +small_hb_dec_tb.uut.data_in[17:0] +@420 +small_hb_dec_tb.uut.d1[17:0] +small_hb_dec_tb.uut.d2[17:0] +small_hb_dec_tb.uut.d3[17:0] +small_hb_dec_tb.uut.d4[17:0] +small_hb_dec_tb.uut.d5[17:0] +small_hb_dec_tb.uut.d6[17:0] +small_hb_dec_tb.uut.coeff[17:0] +small_hb_dec_tb.uut.sum[17:0] +small_hb_dec_tb.uut.prod[35:0] +small_hb_dec_tb.uut.accum_rnd[17:0] +@28 +small_hb_dec_tb.uut.stb_in +@420 +small_hb_dec_tb.uut.final_sum[17:0] +@28 +small_hb_dec_tb.uut.go +small_hb_dec_tb.uut.go_d1 +small_hb_dec_tb.uut.go_d2 +small_hb_dec_tb.uut.go_d3 +small_hb_dec_tb.uut.go_d4 +small_hb_dec_tb.uut.stb_out +@420 +small_hb_dec_tb.uut.data_out[17:0] +small_hb_dec_tb.uut.prod[35:0] +small_hb_dec_tb.uut.accum_rnd[17:0] +small_hb_dec_tb.uut.final_sum[17:0] +@10421 +small_hb_dec_tb.uut.round_acc.out[17:0] +small_hb_dec_tb.uut.data_out[17:0] diff --git a/sdr_lib/acc.v b/sdr_lib/acc.v new file mode 100644 index 000000000..a2da9c86d --- /dev/null +++ b/sdr_lib/acc.v @@ -0,0 +1,28 @@ + +module acc + #(parameter IWIDTH=16, OWIDTH=30) + (input clk, + input clear, + input acc, + input [IWIDTH-1:0] in, + output reg [OWIDTH-1:0] out); + + wire [OWIDTH-1:0] in_signext; + sign_extend #(.bits_in(IWIDTH),.bits_out(OWIDTH)) + acc_signext (.in(in),.out(in_signext)); + + // CLEAR & ~ACC --> clears the accumulator + // CLEAR & ACC --> loads the accumulator + // ~CLEAR & ACC --> accumulates + // ~CLEAR & ~ACC --> hold + + wire [OWIDTH-1:0] addend1 = clear ? 0 : out; + wire [OWIDTH-1:0] addend2 = ~acc ? 0 : in_signext; + wire [OWIDTH-1:0] sum_int = addend1 + addend2; + + always @(posedge clk) + out <= sum_int; + +endmodule // acc + + diff --git a/sdr_lib/add2.v b/sdr_lib/add2.v new file mode 100644 index 000000000..13fff803e --- /dev/null +++ b/sdr_lib/add2.v @@ -0,0 +1,11 @@ + +module add2 + #(parameter WIDTH=16) + (input [WIDTH-1:0] in1, + input [WIDTH-1:0] in2, + output [WIDTH-1:0] sum); + + wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2}; + assign sum = sum_int[WIDTH:1]; // Note -- will have some bias + +endmodule // add2 diff --git a/sdr_lib/add2_and_round.v b/sdr_lib/add2_and_round.v new file mode 100644 index 000000000..146af28da --- /dev/null +++ b/sdr_lib/add2_and_round.v @@ -0,0 +1,11 @@ + +module add2_and_round + #(parameter WIDTH=16) + (input [WIDTH-1:0] in1, + input [WIDTH-1:0] in2, + output [WIDTH-1:0] sum); + + wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2}; + assign sum = sum_int[WIDTH:1] + (sum_int[WIDTH] & sum_int[0]); + +endmodule // add2_and_round diff --git a/sdr_lib/add2_and_round_reg.v b/sdr_lib/add2_and_round_reg.v new file mode 100644 index 000000000..e7fcbf1a1 --- /dev/null +++ b/sdr_lib/add2_and_round_reg.v @@ -0,0 +1,16 @@ + +module add2_and_round_reg + #(parameter WIDTH=16) + (input clk, + input [WIDTH-1:0] in1, + input [WIDTH-1:0] in2, + output reg [WIDTH-1:0] sum); + + wire [WIDTH-1:0] sum_int; + + add2_and_round #(.WIDTH(WIDTH)) add2_n_rnd (.in1(in1),.in2(in2),.sum(sum_int)); + + always @(posedge clk) + sum <= sum_int; + +endmodule // add2_and_round_reg diff --git a/sdr_lib/add2_reg.v b/sdr_lib/add2_reg.v new file mode 100644 index 000000000..456cf315b --- /dev/null +++ b/sdr_lib/add2_reg.v @@ -0,0 +1,17 @@ + +module add2_reg + #(parameter WIDTH=16) + (input clk, + input [WIDTH-1:0] in1, + input [WIDTH-1:0] in2, + output reg [WIDTH-1:0] sum); + + wire [WIDTH-1:0] sum_int; + + add2 #(.WIDTH(WIDTH)) add2 (.in1(in1),.in2(in2),.sum(sum_int)); + + always @(posedge clk) + sum <= sum_int; + +endmodule // add2_reg + diff --git a/sdr_lib/cic_dec_shifter.v b/sdr_lib/cic_dec_shifter.v new file mode 100644 index 000000000..aa5ac895b --- /dev/null +++ b/sdr_lib/cic_dec_shifter.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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + + +// NOTE This only works for N=4, max decim rate of 128 +// NOTE signal "rate" is EQUAL TO the actual rate, no more -1 BS + +module cic_dec_shifter(rate,signal_in,signal_out); + parameter bw = 16; + parameter maxbitgain = 28; + + input [7:0] rate; + input wire [bw+maxbitgain-1:0] signal_in; + output reg [bw-1:0] signal_out; + + function [4:0] bitgain; + input [7:0] rate; + case(rate) + // Exact Cases -- N*log2(rate) + 8'd1 : bitgain = 0; + 8'd2 : bitgain = 4; + 8'd4 : bitgain = 8; + 8'd8 : bitgain = 12; + 8'd16 : bitgain = 16; + 8'd32 : bitgain = 20; + 8'd64 : bitgain = 24; + 8'd128 : bitgain = 28; + + // Nearest without overflow -- ceil(N*log2(rate)) + 8'd3 : bitgain = 7; + 8'd5 : bitgain = 10; + 8'd6 : bitgain = 11; + 8'd7 : bitgain = 12; + 8'd9 : bitgain = 13; + 8'd10,8'd11 : bitgain = 14; + 8'd12,8'd13 : bitgain = 15; + 8'd14,8'd15 : bitgain = 16; + 8'd17,8'd18,8'd19 : bitgain = 17; + 8'd20,8'd21,8'd22 : bitgain = 18; + 8'd23,8'd24,8'd25,8'd26 : bitgain = 19; + 8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 20; + 8'd33,8'd34,8'd35,8'd36,8'd37,8'd38 : bitgain = 21; + 8'd39,8'd40,8'd41,8'd42,8'd43,8'd44,8'd45 : bitgain = 22; + 8'd46,8'd47,8'd48,8'd49,8'd50,8'd51,8'd52,8'd53 : bitgain = 23; + 8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 24; + 8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76 : bitgain = 25; + 8'd77,8'd78,8'd79,8'd80,8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90 : bitgain = 26; + 8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101,8'd102,8'd103,8'd104,8'd105,8'd106,8'd107 : bitgain = 27; + default : bitgain = 28; + endcase // case(rate) + endfunction // bitgain + + wire [4:0] shift = bitgain(rate); + + // We should be able to do this, but can't .... + // assign signal_out = signal_in[shift+bw-1:shift]; + + always @* + case(shift) + 5'd0 : signal_out = signal_in[0+bw-1:0]; + 5'd4 : signal_out = signal_in[4+bw-1:4]; + 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'd12 : signal_out = signal_in[12+bw-1:12]; + 5'd13 : signal_out = signal_in[13+bw-1:13]; + 5'd14 : signal_out = signal_in[14+bw-1:14]; + 5'd15 : signal_out = signal_in[15+bw-1:15]; + 5'd16 : signal_out = signal_in[16+bw-1:16]; + 5'd17 : signal_out = signal_in[17+bw-1:17]; + 5'd18 : signal_out = signal_in[18+bw-1:18]; + 5'd19 : signal_out = signal_in[19+bw-1:19]; + 5'd20 : signal_out = signal_in[20+bw-1:20]; + 5'd21 : signal_out = signal_in[21+bw-1:21]; + 5'd22 : signal_out = signal_in[22+bw-1:22]; + 5'd23 : signal_out = signal_in[23+bw-1:23]; + 5'd24 : signal_out = signal_in[24+bw-1:24]; + 5'd25 : signal_out = signal_in[25+bw-1:25]; + 5'd26 : signal_out = signal_in[26+bw-1:26]; + 5'd27 : signal_out = signal_in[27+bw-1:27]; + 5'd28 : signal_out = signal_in[28+bw-1:28]; + + default : signal_out = signal_in[28+bw-1:28]; + endcase // case(shift) + +endmodule // cic_dec_shifter + diff --git a/sdr_lib/cic_decim.v b/sdr_lib/cic_decim.v new file mode 100755 index 000000000..9a03081b0 --- /dev/null +++ b/sdr_lib/cic_decim.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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + + +module cic_decim + #(parameter bw = 16, parameter N = 4, parameter log2_of_max_rate = 7) + (input clock, + input reset, + input enable, + input [7:0] rate, + input strobe_in, + input strobe_out, + input [bw-1:0] signal_in, + output reg [bw-1:0] signal_out); + + localparam maxbitgain = N * log2_of_max_rate; + + 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] <= 0; + else if (enable && strobe_in) + begin + integrator[0] <= integrator[0] + signal_in_ext; + for(i=1;i<N;i=i+1) + integrator[i] <= integrator[i] + integrator[i-1]; + end + + always @(posedge clock) + if(reset) + begin + sampler <= 0; + for(i=0;i<N;i=i+1) + begin + pipeline[i] <= 0; + differentiator[i] <= 0; + end + end + else if (enable && strobe_out) + begin + sampler <= integrator[N-1]; + differentiator[0] <= sampler; + pipeline[0] <= sampler - differentiator[0]; + for(i=1;i<N;i=i+1) + begin + differentiator[i] <= pipeline[i-1]; + pipeline[i] <= pipeline[i-1] - differentiator[i]; + end + end // if (enable && strobe_out) + + wire [bw-1:0] signal_out_unreg; + + cic_dec_shifter #(bw) + cic_dec_shifter(rate,pipeline[N-1],signal_out_unreg); + + always @(posedge clock) + signal_out <= signal_out_unreg; + +endmodule // cic_decim diff --git a/sdr_lib/cic_int_shifter.v b/sdr_lib/cic_int_shifter.v new file mode 100644 index 000000000..18587fa8b --- /dev/null +++ b/sdr_lib/cic_int_shifter.v @@ -0,0 +1,100 @@ +// -*- 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + + +// NOTE This only works for N=4, max interp rate of 128 +// NOTE signal "rate" is EQUAL TO the actual rate (no more -1 BS) + +module cic_int_shifter(rate,signal_in,signal_out); + parameter bw = 16; + parameter maxbitgain = 21; + + input [7:0] rate; + input wire [bw+maxbitgain-1:0] signal_in; + output reg [bw-1:0] signal_out; + + function [4:0] bitgain; + input [7:0] rate; + case(rate) + // Exact Cases + 8'd1 : bitgain = 0; + 8'd2 : bitgain = 3; + 8'd4 : bitgain = 6; + 8'd8 : bitgain = 9; + 8'd16 : bitgain = 12; + 8'd32 : bitgain = 15; + 8'd64 : bitgain = 18; + 8'd128 : bitgain = 21; + + // Nearest without overflow + 8'd3 : bitgain = 5; + 8'd5 : bitgain = 7; + 8'd6 : bitgain = 8; + 8'd7 : bitgain = 9; + 8'd9,8'd10 : bitgain = 10; + 8'd11,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); + + // We should be able to do this, but can't .... + // assign signal_out = signal_in[shift+bw-1:shift]; + + always @* + case(shift) + 5'd0 : signal_out = signal_in[0+bw-1:0]; + 5'd3 : signal_out = signal_in[3+bw-1:3]; + 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'd5 : signal_out = signal_in[5+bw-1:5]; + 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/sdr_lib/cic_interp.v b/sdr_lib/cic_interp.v new file mode 100755 index 000000000..9b6928aa1 --- /dev/null +++ b/sdr_lib/cic_interp.v @@ -0,0 +1,87 @@ +// -*- 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + + +module cic_interp + #(parameter bw = 16, parameter N = 4, parameter log2_of_max_rate = 7) + (input clock, + input reset, + input enable, + input [7:0] rate, + input strobe_in, + input strobe_out, + input [bw-1:0] signal_in, + output reg [bw-1:0] signal_out); + + integer i; + localparam maxbitgain = (N-1)*log2_of_max_rate; + + 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]; + + 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 | ~enable) + for(i=0;i<N;i=i+1) + integrator[i] <= 0; + else if (enable & strobe_out) + begin + if(strobe_in) + integrator[0] <= integrator[0] + pipeline[N-1]; + for(i=1;i<N;i=i+1) + integrator[i] <= integrator[i] + integrator[i-1]; + end + + always @(posedge clock) + if(reset | ~enable) + begin + for(i=0;i<N;i=i+1) + begin + differentiator[i] <= 0; + pipeline[i] <= 0; + end + end + else if (enable && strobe_in) + begin + differentiator[0] <= signal_in_ext; + pipeline[0] <= signal_in_ext - differentiator[0]; + for(i=1;i<N;i=i+1) + begin + differentiator[i] <= pipeline[i-1]; + pipeline[i] <= pipeline[i-1] - differentiator[i]; + end + end + + wire [bw-1:0] signal_out_unreg; + cic_int_shifter #(bw) + cic_int_shifter(rate,integrator[N-1],signal_out_unreg); + + always @(posedge clock) + signal_out <= signal_out_unreg; + +endmodule // cic_interp + diff --git a/sdr_lib/cic_strober.v b/sdr_lib/cic_strober.v new file mode 100644 index 000000000..40d76bdd9 --- /dev/null +++ b/sdr_lib/cic_strober.v @@ -0,0 +1,45 @@ +// +// USRP2 - Universal Software Radio Peripheral Mk II +// +// Copyright (C) 2008 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +module cic_strober + #(parameter WIDTH=8) + ( input clock, + input reset, + input enable, + input [WIDTH-1:0] rate, // Rate should EQUAL to your desired divide ratio, no more -1 BS + input strobe_fast, + output wire strobe_slow ); + + reg [WIDTH-1:0] counter; + wire now = (counter==1); + assign strobe_slow = now && enable && strobe_fast; + + always @(posedge clock) + if(reset) + counter <= 0; + else if (~enable) + counter <= rate; + else if(strobe_fast) + if(now) + counter <= rate; + else + counter <= counter - 1; + +endmodule // cic_strober diff --git a/sdr_lib/clip.v b/sdr_lib/clip.v new file mode 100644 index 000000000..3e6b3a2e2 --- /dev/null +++ b/sdr_lib/clip.v @@ -0,0 +1,36 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2008 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Clipping "macro", keeps the bottom bits + +module clip + #(parameter bits_in=0, + parameter bits_out=0) + (input [bits_in-1:0] in, + output [bits_out-1:0] out); + + wire overflow = |in[bits_in-1:bits_out-1] & ~(&in[bits_in-1:bits_out-1]); + assign out = overflow ? + (in[bits_in-1] ? {1'b1,{(bits_out-1){1'b0}}} : {1'b0,{(bits_out-1){1'b1}}}) : + in[bits_out-1:0]; + +endmodule // clip + diff --git a/sdr_lib/clip_and_round.v b/sdr_lib/clip_and_round.v new file mode 100644 index 000000000..4546283a3 --- /dev/null +++ b/sdr_lib/clip_and_round.v @@ -0,0 +1,43 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2008 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Clipping "macro", keeps the bottom bits + +module clip_and_round + #(parameter bits_in=0, + parameter bits_out=0, + parameter clip_bits=0) + (input [bits_in-1:0] in, + output [bits_out-1:0] out); + + wire [bits_out-1:0] rounded; + + round #(.bits_in(bits_in-clip_bits),.bits_out(bits_out)) + round (.in(in[bits_in-clip_bits-1:0]),.out(rounded)); + + wire overflow = |in[bits_in-1:bits_in-clip_bits-1] + & ~(&in[bits_in-1:bits_in-clip_bits-1]); + + assign out = overflow ? + (in[bits_in-1] ? {1'b1,{(bits_out-1){1'b0}}} : {1'b0,{(bits_out-1){1'b1}}}) : + rounded; + +endmodule // clip_and_round diff --git a/sdr_lib/clip_and_round_reg.v b/sdr_lib/clip_and_round_reg.v new file mode 100644 index 000000000..66fb155fb --- /dev/null +++ b/sdr_lib/clip_and_round_reg.v @@ -0,0 +1,40 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2008 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Clipping "macro", keeps the bottom bits + +module clip_and_round_reg + #(parameter bits_in=0, + parameter bits_out=0, + parameter clip_bits=0) + (input clk, + input [bits_in-1:0] in, + output reg [bits_out-1:0] out); + + wire [bits_out-1:0] temp; + + clip_and_round #(.bits_in(bits_in),.bits_out(bits_out),.clip_bits(clip_bits)) + clip_and_round (.in(in),.out(temp)); + + always@(posedge clk) + out <= temp; + +endmodule // clip_and_round_reg diff --git a/sdr_lib/clip_reg.v b/sdr_lib/clip_reg.v new file mode 100644 index 000000000..d5e98d982 --- /dev/null +++ b/sdr_lib/clip_reg.v @@ -0,0 +1,38 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2008 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Clipping "macro", keeps the bottom bits + +module clip_reg + #(parameter bits_in=0, + parameter bits_out=0) + (input clk, + input [bits_in-1:0] in, + output reg [bits_out-1:0] out); + + wire [bits_out-1:0] temp; + + clip #(.bits_in(bits_in),.bits_out(bits_out)) clip (.in(in),.out(temp)); + always @(posedge clk) + out <= temp; + +endmodule // clip_reg + diff --git a/sdr_lib/cordic.v b/sdr_lib/cordic.v new file mode 100755 index 000000000..b73e7acf1 --- /dev/null +++ b/sdr_lib/cordic.v @@ -0,0 +1,109 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2003, 2007 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., 51 Franklin Street, Boston, MA 02110-1301 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 + + localparam c00 = 15'd8192; + localparam c01 = 15'd4836; + localparam c02 = 15'd2555; + localparam c03 = 15'd1297; + localparam c04 = 15'd651; + localparam c05 = 15'd326; + localparam c06 = 15'd163; + localparam c07 = 15'd81; + localparam c08 = 15'd41; + localparam c09 = 15'd20; + localparam c10 = 15'd10; + localparam c11 = 15'd5; + localparam c12 = 15'd3; + localparam c13 = 15'd1; + localparam c14 = 15'd1; + localparam c15 = 15'd0; + localparam c16 = 15'd0; + + always @(posedge clock) + if(reset) + begin + x0 <= 0; y0 <= 0; z0 <= 0; + end + else// if(enable) + begin + z0 <= zi[zwidth-2:0]; + case (zi[zwidth-1:zwidth-2]) + 2'b00, 2'b11 : + begin + x0 <= xi_ext; + y0 <= yi_ext; + end + 2'b01, 2'b10 : + begin + x0 <= -xi_ext; + y0 <= -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/sdr_lib/cordic_stage.v b/sdr_lib/cordic_stage.v new file mode 100755 index 000000000..641ff9108 --- /dev/null +++ b/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., 51 Franklin Street, Boston, MA 02110-1301 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 <= 0; + yo <= 0; + zo <= 0; + end + else //if(enable) + begin + xo <= z_is_pos ? + xi - {{shift+1{yi[bitwidth-1]}},yi[bitwidth-2:shift]} : + xi + {{shift+1{yi[bitwidth-1]}},yi[bitwidth-2:shift]}; + yo <= z_is_pos ? + yi + {{shift+1{xi[bitwidth-1]}},xi[bitwidth-2:shift]} : + yi - {{shift+1{xi[bitwidth-1]}},xi[bitwidth-2:shift]}; + zo <= z_is_pos ? + zi - constant : + zi + constant; + end +endmodule diff --git a/sdr_lib/ddc.v b/sdr_lib/ddc.v new file mode 100755 index 000000000..0d4da9bbc --- /dev/null +++ b/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., 51 Franklin Street, Boston, MA 02110-1301 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/sdr_lib/dsp_core_rx.v b/sdr_lib/dsp_core_rx.v new file mode 100644 index 000000000..a2569a1dc --- /dev/null +++ b/sdr_lib/dsp_core_rx.v @@ -0,0 +1,146 @@ + +`define DSP_CORE_RX_BASE 160 +module dsp_core_rx + (input clk, input rst, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + input [13:0] adc_a, input adc_ovf_a, + input [13:0] adc_b, input adc_ovf_b, + + output [31:0] sample, + input run, + output strobe, + output [31:0] debug + ); + + wire [15:0] scale_i, scale_q; + wire [13:0] adc_a_ofs, adc_b_ofs; + reg [13:0] adc_i, adc_q; + wire [31:0] phase_inc; + reg [31:0] phase; + + wire [35:0] prod_i, prod_q; + wire [23:0] i_cordic, q_cordic; + wire [23:0] i_cic, q_cic; + wire [17:0] i_cic_scaled, q_cic_scaled; + wire [17:0] i_hb1, q_hb1; + wire [17:0] i_hb2, q_hb2; + wire [15:0] i_out, q_out; + + wire strobe_cic, strobe_hb1, strobe_hb2; + wire enable_hb1, enable_hb2; + wire [7:0] cic_decim_rate; + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+0)) sr_0 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(phase_inc),.changed()); + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+1)) sr_1 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({scale_i,scale_q}),.changed()); + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+2)) sr_2 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({enable_hb1, enable_hb2, cic_decim_rate}),.changed()); + + rx_dcoffset #(.WIDTH(14),.ADDR(`DSP_CORE_RX_BASE+6)) rx_dcoffset_a + (.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .adc_in(adc_a),.adc_out(adc_a_ofs)); + + rx_dcoffset #(.WIDTH(14),.ADDR(`DSP_CORE_RX_BASE+7)) rx_dcoffset_b + (.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .adc_in(adc_b),.adc_out(adc_b_ofs)); + + wire [3:0] muxctrl; + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+8)) sr_8 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(muxctrl),.changed()); + + always @(posedge clk) + case(muxctrl[1:0]) + 0: adc_i <= adc_a_ofs; + 1: adc_i <= adc_b_ofs; + 2: adc_i <= 0; + default: adc_i <= 0; + endcase // case(muxctrl[1:0]) + + always @(posedge clk) + case(muxctrl[3:2]) + 0: adc_q <= adc_b_ofs; + 1: adc_q <= adc_a_ofs; + 2: adc_q <= 0; + default: adc_q <= 0; + endcase // case(muxctrl[3:2]) + + always @(posedge clk) + if(rst) + phase <= 0; + else if(run) + phase <= phase + phase_inc; + + MULT18X18S mult_i + (.P(prod_i), // 36-bit multiplier output + .A({{4{adc_i[13]}},adc_i} ), // 18-bit multiplier input + .B({{2{scale_i[15]}},scale_i}), // 18-bit multiplier input + .C(clk), // Clock input + .CE(1), // Clock enable input + .R(rst) // Synchronous reset input + ); + + MULT18X18S mult_q + (.P(prod_q), // 36-bit multiplier output + .A({{4{adc_q[13]}},adc_q} ), // 18-bit multiplier input + .B({{2{scale_q[15]}},scale_q}), // 18-bit multiplier input + .C(clk), // Clock input + .CE(1), // Clock enable input + .R(rst) // Synchronous reset input + ); + + cordic #(.bitwidth(24)) + cordic(.clock(clk), .reset(rst), .enable(run), + .xi(prod_i[23:0]),. yi(prod_q[23:0]), .zi(phase[31:16]), + .xo(i_cordic),.yo(q_cordic),.zo() ); + + cic_strober cic_strober(.clock(clk),.reset(rst),.enable(run),.rate(cic_decim_rate), + .strobe_fast(1),.strobe_slow(strobe_cic) ); + + cic_decim #(.bw(24)) + decim_i (.clock(clk),.reset(rst),.enable(run), + .rate(cic_decim_rate),.strobe_in(1'b1),.strobe_out(strobe_cic), + .signal_in(i_cordic),.signal_out(i_cic)); + + cic_decim #(.bw(24)) + decim_q (.clock(clk),.reset(rst),.enable(run), + .rate(cic_decim_rate),.strobe_in(1'b1),.strobe_out(strobe_cic), + .signal_in(q_cordic),.signal_out(q_cic)); + + round_reg #(.bits_in(24),.bits_out(18)) round_icic (.clk(clk),.in(i_cic),.out(i_cic_scaled)); + round_reg #(.bits_in(24),.bits_out(18)) round_qcic (.clk(clk),.in(q_cic),.out(q_cic_scaled)); + reg strobe_cic_d1; + always @(posedge clk) strobe_cic_d1 <= strobe_cic; + + small_hb_dec #(.WIDTH(18)) small_hb_i + (.clk(clk),.rst(rst),.bypass(~enable_hb1), + .stb_in(strobe_cic_d1),.data_in(i_cic_scaled),.stb_out(strobe_hb1),.data_out(i_hb1)); + + small_hb_dec #(.WIDTH(18)) small_hb_q + (.clk(clk),.rst(rst),.bypass(~enable_hb1), + .stb_in(strobe_cic_d1),.data_in(q_cic_scaled),.stb_out(),.data_out(q_hb1)); + + wire [8:0] cpi_hb = enable_hb1 ? {cic_decim_rate,1'b0} : {1'b0,cic_decim_rate}; + hb_dec #(.IWIDTH(18), .OWIDTH(18), .CWIDTH(18), .ACCWIDTH(24)) hb_i + (.clk(clk),.rst(rst),.bypass(~enable_hb2),.cpi(cpi_hb), + .stb_in(strobe_hb1),.data_in(i_hb1),.stb_out(strobe_hb2),.data_out(i_hb2)); + + hb_dec #(.IWIDTH(18), .OWIDTH(18), .CWIDTH(18), .ACCWIDTH(24)) hb_q + (.clk(clk),.rst(rst),.bypass(~enable_hb2),.cpi(cpi_hb), + .stb_in(strobe_hb1),.data_in(q_hb1),.stb_out(),.data_out(q_hb2)); + + round #(.bits_in(18),.bits_out(16)) round_iout (.in(i_hb2),.out(i_out)); + round #(.bits_in(18),.bits_out(16)) round_qout (.in(q_hb2),.out(q_out)); + + assign sample = {i_out,q_out}; + assign strobe = strobe_hb2; + assign debug = {enable_hb1, enable_hb2, run, strobe, strobe_cic, strobe_cic_d1, strobe_hb1, strobe_hb2}; + +endmodule // dsp_core_rx diff --git a/sdr_lib/dsp_core_tx.v b/sdr_lib/dsp_core_tx.v new file mode 100644 index 000000000..c311e79f1 --- /dev/null +++ b/sdr_lib/dsp_core_tx.v @@ -0,0 +1,132 @@ + +`define DSP_CORE_TX_BASE 128 + +module dsp_core_tx + (input clk, input rst, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + output reg [15:0] dac_a, + output reg [15:0] dac_b, + + // To tx_control + input [31:0] sample, + input run, + output strobe, + output [31:0] debug + ); + + wire [15:0] i, q, scale_i, scale_q; + wire [31:0] phase_inc; + reg [31:0] phase; + wire [7:0] interp_rate; + + wire enable_hb1, enable_hb2; + + setting_reg #(.my_addr(`DSP_CORE_TX_BASE+0)) sr_0 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(phase_inc),.changed()); + + setting_reg #(.my_addr(`DSP_CORE_TX_BASE+1)) sr_1 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({scale_i,scale_q}),.changed()); + + setting_reg #(.my_addr(`DSP_CORE_TX_BASE+2)) sr_2 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({enable_hb1, enable_hb2, interp_rate}),.changed()); + + // Strobes are all now delayed by 1 cycle for timing reasons + wire strobe_cic_pre, strobe_hb1_pre, strobe_hb2_pre; + reg strobe_cic = 1; + reg strobe_hb1 = 1; + reg strobe_hb2 = 1; + + cic_strober #(.WIDTH(8)) + cic_strober(.clock(clk),.reset(rst),.enable(run),.rate(interp_rate), + .strobe_fast(1),.strobe_slow(strobe_cic_pre) ); + cic_strober #(.WIDTH(2)) + hb2_strober(.clock(clk),.reset(rst),.enable(run),.rate(enable_hb2 ? 2 : 1), + .strobe_fast(strobe_cic_pre),.strobe_slow(strobe_hb2_pre) ); + cic_strober #(.WIDTH(2)) + hb1_strober(.clock(clk),.reset(rst),.enable(run),.rate(enable_hb1 ? 2 : 1), + .strobe_fast(strobe_hb2_pre),.strobe_slow(strobe_hb1_pre) ); + + always @(posedge clk) strobe_hb1 <= strobe_hb1_pre; + always @(posedge clk) strobe_hb2 <= strobe_hb2_pre; + always @(posedge clk) strobe_cic <= strobe_cic_pre; + + // DDC + always @(posedge clk) + if(rst) + phase <= 0; + else if(run) + phase <= phase + phase_inc; + + wire signed [17:0] da, db; + wire signed [35:0] prod_i, prod_q; + + wire [17:0] bb_i = {sample[31:16],2'b0}; + wire [17:0] bb_q = {sample[15:0],2'b0}; + wire [17:0] i_interp, q_interp; + + wire [17:0] hb1_i, hb1_q, hb2_i, hb2_q; + + wire [7:0] cpo = enable_hb2 ? ({interp_rate,1'b0}) : interp_rate; + // Note that max CIC rate is 128, which would give an overflow on cpo if enable_hb2 is true, + // but the default case inside hb_interp handles this + + hb_interp #(.IWIDTH(18),.OWIDTH(18),.ACCWIDTH(24)) hb_interp_i + (.clk(clk),.rst(rst),.bypass(~enable_hb1),.cpo(cpo),.stb_in(strobe_hb1),.data_in(bb_i),.stb_out(strobe_hb2),.data_out(hb1_i)); + hb_interp #(.IWIDTH(18),.OWIDTH(18),.ACCWIDTH(24)) hb_interp_q + (.clk(clk),.rst(rst),.bypass(~enable_hb1),.cpo(cpo),.stb_in(strobe_hb1),.data_in(bb_q),.stb_out(strobe_hb2),.data_out(hb1_q)); + + small_hb_int #(.WIDTH(18)) small_hb_interp_i + (.clk(clk),.rst(rst),.bypass(~enable_hb2),.stb_in(strobe_hb2),.data_in(hb1_i), + .output_rate(interp_rate),.stb_out(strobe_cic),.data_out(hb2_i)); + small_hb_int #(.WIDTH(18)) small_hb_interp_q + (.clk(clk),.rst(rst),.bypass(~enable_hb2),.stb_in(strobe_hb2),.data_in(hb1_q), + .output_rate(interp_rate),.stb_out(strobe_cic),.data_out(hb2_q)); + + cic_interp #(.bw(18),.N(4),.log2_of_max_rate(7)) + cic_interp_i(.clock(clk),.reset(rst),.enable(run),.rate(interp_rate), + .strobe_in(strobe_cic),.strobe_out(1), + .signal_in(hb2_i),.signal_out(i_interp)); + + cic_interp #(.bw(18),.N(4),.log2_of_max_rate(7)) + cic_interp_q(.clock(clk),.reset(rst),.enable(run),.rate(interp_rate), + .strobe_in(strobe_cic),.strobe_out(1), + .signal_in(hb2_q),.signal_out(q_interp)); + + assign strobe = strobe_hb1; + + cordic #(.bitwidth(18),.zwidth(16)) + cordic(.clock(clk), .reset(rst), .enable(run), + .xi(i_interp),.yi(q_interp),.zi(phase[31:16]), + .xo(da),.yo(db),.zo() ); + + MULT18X18S MULT18X18S_inst + (.P(prod_i), // 36-bit multiplier output + .A(da), // 18-bit multiplier input + .B({{2{scale_i[15]}},scale_i}), // 18-bit multiplier input + .C(clk), // Clock input + .CE(1), // Clock enable input + .R(rst) // Synchronous reset input + ); + + MULT18X18S MULT18X18S_inst_2 + (.P(prod_q), // 36-bit multiplier output + .A(db), // 18-bit multiplier input + .B({{2{scale_q[15]}},scale_q}), // 18-bit multiplier input + .C(clk), // Clock input + .CE(1), // Clock enable input + .R(rst) // Synchronous reset input + ); + + always @(posedge clk) + dac_a <= prod_i[28:13]; + + always @(posedge clk) + dac_b <= prod_q[28:13]; + + assign debug = {strobe_cic, strobe_hb1, strobe_hb2,run}; + +endmodule // dsp_core diff --git a/sdr_lib/duc.v b/sdr_lib/duc.v new file mode 100755 index 000000000..6dac95b49 --- /dev/null +++ b/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., 51 Franklin Street, Boston, MA 02110-1301 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/sdr_lib/dummy_rx.v b/sdr_lib/dummy_rx.v new file mode 100644 index 000000000..99290ecec --- /dev/null +++ b/sdr_lib/dummy_rx.v @@ -0,0 +1,62 @@ + +`define DSP_CORE_RX_BASE 160 +module dummy_rx + (input clk, input rst, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + input [13:0] adc_a, input adc_ovf_a, + input [13:0] adc_b, input adc_ovf_b, + + output [31:0] sample, + input run, + output strobe + ); + + wire [15:0] scale_i, scale_q; + wire [31:0] phase_inc; + reg [31:0] phase; + + wire [23:0] i_decim, q_decim; + wire [7:0] decim_rate; + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+0)) sr_0 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(phase_inc),.changed()); + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+1)) sr_1 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({scale_i,scale_q}),.changed()); + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+2)) sr_2 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(decim_rate),.changed()); + + strobe_gen strobe_gen(.clock(clk),.reset(rst),.enable(run),.rate(decim_rate), + .strobe_in(1),.strobe(strobe) ); + + reg [15:0] i_out, q_out; + assign sample = {i_out,q_out}; + + always @(posedge clk) + if(rst) + i_out <= 0; + else if(~run) + i_out <= 0; + else if(strobe) + i_out <= i_out + 1; + + reg run_d1; + always @(posedge clk) + if(rst) + run_d1 <= 0; + else + run_d1 <= run; + + always @(posedge clk) + if(rst) + q_out <= 0; + else if (run & ~run_d1) + q_out <= q_out + 1; + + +endmodule // dsp_core_rx diff --git a/sdr_lib/gen_cordic_consts.py b/sdr_lib/gen_cordic_consts.py new file mode 100755 index 000000000..ab66cfe01 --- /dev/null +++ b/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/sdr_lib/halfband_ideal.v b/sdr_lib/halfband_ideal.v new file mode 100644 index 000000000..484cfff2a --- /dev/null +++ b/sdr_lib/halfband_ideal.v @@ -0,0 +1,84 @@ +module halfband_ideal ( + input clock, + input reset, + input enable, + input strobe_in, + input wire signed [17:0] data_in, + output reg strobe_out, + output reg signed [17:0] data_out +) ; + + parameter decim = 1 ; + parameter rate = 2 ; + + reg signed [40:0] temp ; + reg signed [17:0] delay[30:0] ; + reg signed [17:0] coeffs[30:0] ; + reg [7:0] count ; + integer i ; + + initial begin + for( i = 0 ; i < 31 ; i = i + 1 ) begin + coeffs[i] = 18'd0 ; + end + coeffs[0] = -1390 ; + coeffs[2] = 1604 ; + coeffs[4] = -1896 ; + coeffs[6] = 2317 ; + coeffs[8] = -2979 ; + coeffs[10] = 4172 ; + coeffs[12] = -6953 ; + coeffs[14] = 20860 ; + coeffs[15] = 32768 ; + coeffs[16] = 20860 ; + coeffs[18] = -6953 ; + coeffs[20] = 4172 ; + coeffs[22] = -2979 ; + coeffs[24] = 2317 ; + coeffs[26] = -1896 ; + coeffs[28] = 1604 ; + coeffs[30] = -1390 ; + end + + always @(posedge clock) begin + if( reset ) begin + count <= 0 ; + for( i = 0 ; i < 31 ; i = i + 1 ) begin + delay[i] <= 18'd0 ; + end + temp <= 41'd0 ; + data_out <= 18'd0 ; + strobe_out <= 1'b0 ; + end else if( enable ) begin + + if( (decim && (count == rate-1)) || !decim ) + strobe_out <= strobe_in ; + else + strobe_out <= 1'b0 ; + + + if( strobe_in ) begin + // Increment decimation count + count <= count + 1 ; + + // Shift the input + for( i = 30 ; i > 0 ; i = i - 1 ) begin + delay[i] = delay[i-1] ; + end + delay[0] = data_in ; + + // clear the temp reg + temp = 18'd0 ; + if( (decim && (count == rate-1)) || !decim ) begin + count <= 0 ; + for( i = 0 ; i < 31 ; i = i + 1 ) begin + // Multiply Accumulate + temp = temp + delay[i]*coeffs[i] ; + end + // Assign data output + data_out <= temp >>> 15 ; + end + end + end + end +endmodule diff --git a/sdr_lib/halfband_tb.v b/sdr_lib/halfband_tb.v new file mode 100644 index 000000000..231dd00d7 --- /dev/null +++ b/sdr_lib/halfband_tb.v @@ -0,0 +1,120 @@ +module halfband_tb( ) ; + + // Parameters for instantiation + parameter clocks = 2 ; // Number of clocks per input + parameter decim = 0 ; // Sets the filter to decimate + parameter rate = 2 ; // Sets the decimation rate + + reg clock ; + reg reset ; + reg enable ; + reg strobe_in ; + reg signed [17:0] data_in ; + wire strobe_out ; + wire signed [17:0] data_out ; + + // Setup the clock + initial clock = 1'b0 ; + always #5 clock <= ~clock ; + + // Come out of reset after a while + initial reset = 1'b1 ; + initial #100 reset = 1'b0 ; + + // Enable the entire system + initial enable = 1'b1 ; + + // Instantiate UUT + halfband_ideal + #( + .decim ( decim ), + .rate ( rate ) + ) uut( + .clock ( clock ), + .reset ( reset ), + .enable ( enable ), + .strobe_in ( strobe_in ), + .data_in ( data_in ), + .strobe_out ( strobe_out ), + .data_out ( data_out ) + ) ; + + integer i, ri, ro, infile, outfile ; + + // Setup file IO + initial begin + infile = $fopen("input.dat","r") ; + outfile = $fopen("output.dat","r") ; + $timeformat(-9, 2, " ns", 10) ; + end + + reg endofsim ; + reg signed [17:0] compare ; + integer noe ; + initial noe = 0 ; + + initial begin + // Initialize inputs + strobe_in <= 1'd0 ; + data_in <= 18'd0 ; + + // Wait for reset to go away + @(negedge reset) #0 ; + + // While we're still simulating ... + while( !endofsim ) begin + + // Write the input from the file or 0 if EOF... + @( posedge clock ) begin + #1 ; + strobe_in <= 1'b1 ; + if( !$feof(infile) ) + ri = $fscanf( infile, "%d", data_in ) ; + else + data_in <= 18'd0 ; + end + + // Clocked in - set the strobe to 0 if the number of + // clocks per sample is greater than 1 + if( clocks > 1 ) begin + @(posedge clock) begin + strobe_in <= 1'b0 ; + end + + // Wait for the specified number of cycles + for( i = 0 ; i < (clocks-2) ; i = i + 1 ) begin + @(posedge clock) #1 ; + end + end + end + + // Print out the number of errors that occured + if( noe ) + $display( "FAILED: %d errors during simulation", noe ) ; + else + $display( "PASSED: Simulation successful" ) ; + + $stop ; + end + + // Output comparison of simulated values versus known good values + always @ (posedge clock) begin + if( reset ) + endofsim <= 1'b0 ; + else begin + if( !$feof(outfile) ) begin + if( strobe_out ) begin + ro = $fscanf( outfile, "%d\n", compare ) ; + if( compare != data_out ) begin + $display( "%t: %d != %d", $realtime, data_out, compare ) ; + noe = noe + 1 ; + end + end + end else begin + // Signal end of simulation when no more outputs + endofsim <= 1'b1 ; + end + end + end + +endmodule diff --git a/sdr_lib/hb/acc.v b/sdr_lib/hb/acc.v new file mode 100644 index 000000000..195d5ea94 --- /dev/null +++ b/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/sdr_lib/hb/coeff_ram.v b/sdr_lib/hb/coeff_ram.v new file mode 100644 index 000000000..65460822f --- /dev/null +++ b/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/sdr_lib/hb/coeff_rom.v b/sdr_lib/hb/coeff_rom.v new file mode 100644 index 000000000..7f8886b4e --- /dev/null +++ b/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'd49; + 3'd1 : data <= #1 16'd165; + 3'd2 : data <= #1 -16'd412; + 3'd3 : data <= #1 16'd873; + 3'd4 : data <= #1 -16'd1681; + 3'd5 : data <= #1 16'd3135; + 3'd6 : data <= #1 -16'd6282; + 3'd7 : data <= #1 16'd20628; + endcase // case(addr) + +endmodule // coeff_rom + + diff --git a/sdr_lib/hb/halfband_decim.v b/sdr_lib/hb/halfband_decim.v new file mode 100644 index 000000000..dff4d902c --- /dev/null +++ b/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., 51 Franklin Street, Boston, MA 02110-1301 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/sdr_lib/hb/halfband_interp.v b/sdr_lib/hb/halfband_interp.v new file mode 100644 index 000000000..cdb11c1f6 --- /dev/null +++ b/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/sdr_lib/hb/hbd_tb/HBD b/sdr_lib/hb/hbd_tb/HBD new file mode 100644 index 000000000..574fbba91 --- /dev/null +++ b/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/sdr_lib/hb/hbd_tb/really_golden b/sdr_lib/hb/hbd_tb/really_golden new file mode 100644 index 000000000..2d24a9e14 --- /dev/null +++ b/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/sdr_lib/hb/hbd_tb/regression b/sdr_lib/hb/hbd_tb/regression new file mode 100644 index 000000000..fc279c2f2 --- /dev/null +++ b/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/sdr_lib/hb/hbd_tb/run_hbd b/sdr_lib/hb/hbd_tb/run_hbd new file mode 100755 index 000000000..b8aec7574 --- /dev/null +++ b/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/sdr_lib/hb/hbd_tb/test_hbd.v b/sdr_lib/hb/hbd_tb/test_hbd.v new file mode 100644 index 000000000..01ab5e7e0 --- /dev/null +++ b/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/sdr_lib/hb/mac.v b/sdr_lib/hb/mac.v new file mode 100644 index 000000000..5a270bc73 --- /dev/null +++ b/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/sdr_lib/hb/mult.v b/sdr_lib/hb/mult.v new file mode 100644 index 000000000..a8d4cb1b7 --- /dev/null +++ b/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/sdr_lib/hb/ram16_2port.v b/sdr_lib/hb/ram16_2port.v new file mode 100644 index 000000000..e1761a926 --- /dev/null +++ b/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/sdr_lib/hb/ram16_2sum.v b/sdr_lib/hb/ram16_2sum.v new file mode 100644 index 000000000..559b06fd5 --- /dev/null +++ b/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/sdr_lib/hb/ram32_2sum.v b/sdr_lib/hb/ram32_2sum.v new file mode 100644 index 000000000..d1f55b7d0 --- /dev/null +++ b/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/sdr_lib/hb_dec.v b/sdr_lib/hb_dec.v new file mode 100644 index 000000000..b256eb57f --- /dev/null +++ b/sdr_lib/hb_dec.v @@ -0,0 +1,170 @@ +// Final halfband decimator +// Implements impulse responses of the form [A 0 B 0 C .. 0 H 0.5 H 0 .. C 0 B 0 A] +// Strobe in cannot come faster than every 2nd clock cycle +// These taps designed by halfgen4 from ldoolittle +// myfilt = round(2^18 * halfgen4(.7/4,8)) + +module hb_dec + #(parameter IWIDTH=18, OWIDTH=18, CWIDTH=18, ACCWIDTH=24) + (input clk, + input rst, + input bypass, + input [8:0] cpi, // Clocks per input -- equal to the decimation ratio ahead of this block + input stb_in, + input [IWIDTH-1:0] data_in, + output reg stb_out, + output reg [OWIDTH-1:0] data_out); + + // Control + reg [3:0] addr_odd_a, addr_odd_b, addr_odd_c, addr_odd_d; + wire write_odd, write_even, do_mult; + reg odd; + reg [2:0] phase, phase_d1; + reg stb_out_int; + wire clear, do_acc; + assign do_mult = 1; + + always @(posedge clk) + if(rst) + odd <= 0; + else if(stb_in) + odd <= ~odd; + + assign write_odd = stb_in & odd; + assign write_even = stb_in & ~odd; + + always @(posedge clk) + if(rst) + phase <= 0; + else if(stb_in & odd) + phase <= 1; + else if(phase == 4) + phase <= 0; + else if(phase != 0) + phase <= phase + 1; + + always @(posedge clk) + phase_d1 <= phase; + + reg [15:0] stb_out_pre; + always @(posedge clk) + if(rst) + stb_out_pre <= 0; + else + stb_out_pre <= {stb_out_pre[14:0],(stb_in & odd)}; + + always @* + case(phase) + 1 : begin addr_odd_a = 0; addr_odd_b = 15; end + 2 : begin addr_odd_a = 1; addr_odd_b = 14; end + 3 : begin addr_odd_a = 2; addr_odd_b = 13; end + 4 : begin addr_odd_a = 3; addr_odd_b = 12; end + default : begin addr_odd_a = 0; addr_odd_b = 15; end + endcase // case(phase) + + always @* + case(phase) + 1 : begin addr_odd_c = 4; addr_odd_d = 11; end + 2 : begin addr_odd_c = 5; addr_odd_d = 10; end + 3 : begin addr_odd_c = 6; addr_odd_d = 9; end + 4 : begin addr_odd_c = 7; addr_odd_d = 8; end + default : begin addr_odd_c = 4; addr_odd_d = 11; end + endcase // case(phase) + + assign do_acc = |stb_out_pre[6:3]; + assign clear = stb_out_pre[3]; + + // Data + wire [IWIDTH-1:0] data_odd_a, data_odd_b, data_odd_c, data_odd_d; + wire [IWIDTH-1:0] sum1, sum2; + wire [OWIDTH-1:0] final_sum; + reg [CWIDTH-1:0] coeff1, coeff2; + wire [35:0] prod1, prod2; + + always @* // Outer coeffs + case(phase_d1) + 1 : coeff1 = -107; + 2 : coeff1 = 445; + 3 : coeff1 = -1271; + 4 : coeff1 = 2959; + default : coeff1 = -107; + endcase // case(phase) + + always @* // Inner coeffs + case(phase_d1) + 1 : coeff2 = -6107; + 2 : coeff2 = 11953; + 3 : coeff2 = -24706; + 4 : coeff2 = 82359; + default : coeff2 = -6107; + endcase // case(phase) + + srl #(.WIDTH(IWIDTH)) srl_odd_a + (.clk(clk),.write(write_odd),.in(data_in),.addr(addr_odd_a),.out(data_odd_a)); + srl #(.WIDTH(IWIDTH)) srl_odd_b + (.clk(clk),.write(write_odd),.in(data_in),.addr(addr_odd_b),.out(data_odd_b)); + srl #(.WIDTH(IWIDTH)) srl_odd_c + (.clk(clk),.write(write_odd),.in(data_in),.addr(addr_odd_c),.out(data_odd_c)); + srl #(.WIDTH(IWIDTH)) srl_odd_d + (.clk(clk),.write(write_odd),.in(data_in),.addr(addr_odd_d),.out(data_odd_d)); + + add2_reg /*_and_round_reg*/ #(.WIDTH(IWIDTH)) add1 (.clk(clk),.in1(data_odd_a),.in2(data_odd_b),.sum(sum1)); + add2_reg /*_and_round_reg*/ #(.WIDTH(IWIDTH)) add2 (.clk(clk),.in1(data_odd_c),.in2(data_odd_d),.sum(sum2)); + + wire [IWIDTH-1:0] data_even; + reg [3:0] addr_even; + + always @(posedge clk) + case(cpi) + // 1 is an error + 2 : addr_even <= 9; // Maximum speed (overall decim by 4) + 3, 4, 5, 6, 7 : addr_even <= 8; + default : addr_even <= 7; + endcase // case(cpi) + + srl #(.WIDTH(IWIDTH)) srl_even + (.clk(clk),.write(write_even),.in(data_in),.addr(addr_even),.out(data_even)); + + localparam MWIDTH = ACCWIDTH-2; + wire [MWIDTH-1:0] sum_of_prod; + + MULT18X18S mult1(.C(clk), .CE(do_mult), .R(rst), .P(prod1), .A(coeff1), .B(sum1) ); + MULT18X18S mult2(.C(clk), .CE(do_mult), .R(rst), .P(prod2), .A(coeff2), .B(sum2) ); + add2_and_round_reg #(.WIDTH(MWIDTH)) + add3 (.clk(clk),.in1(prod1[35:36-MWIDTH]),.in2(prod2[35:36-MWIDTH]),.sum(sum_of_prod)); + + wire [ACCWIDTH-1:0] acc_out; + + acc #(.IWIDTH(MWIDTH),.OWIDTH(ACCWIDTH)) + acc (.clk(clk),.clear(clear),.acc(do_acc),.in(sum_of_prod),.out(acc_out)); + + localparam SHIFT_FACTOR = ACCWIDTH-IWIDTH-5; + wire [ACCWIDTH-1:0] data_even_signext; + wire [ACCWIDTH:0] final_sum_unrounded; + + sign_extend #(.bits_in(IWIDTH),.bits_out(ACCWIDTH-SHIFT_FACTOR)) + signext_data_even (.in(data_even),.out(data_even_signext[ACCWIDTH-1:SHIFT_FACTOR])); + assign data_even_signext[SHIFT_FACTOR-1:0] = 0; + + add2_reg /* add2_and_round_reg */ #(.WIDTH(ACCWIDTH+1)) + final_adder (.clk(clk), .in1({acc_out,1'b0}), .in2({data_even_signext,1'b0}), .sum(final_sum_unrounded)); + + round_reg #(.bits_in(ACCWIDTH-4),.bits_out(OWIDTH)) + final_round (.clk(clk),.in(final_sum_unrounded[ACCWIDTH-5:0]),.out(final_sum)); + + // Output + always @(posedge clk) + if(bypass) + data_out <= data_in; + else if(stb_out_pre[9]) + data_out <= final_sum; + + always @(posedge clk) + if(rst) + stb_out <= 0; + else if(bypass) + stb_out <= stb_in; + else + stb_out <= stb_out_pre[9]; + +endmodule // hb_dec diff --git a/sdr_lib/hb_dec_tb.v b/sdr_lib/hb_dec_tb.v new file mode 100644 index 000000000..3e5faa80a --- /dev/null +++ b/sdr_lib/hb_dec_tb.v @@ -0,0 +1,140 @@ +module hb_dec_tb( ) ; + + // Parameters for instantiation + parameter clocks = 9'd2 ; // Number of clocks per input + parameter decim = 1 ; // Sets the filter to decimate + parameter rate = 2 ; // Sets the decimation rate + + reg clock ; + reg reset ; + reg enable ; + reg strobe_in ; + reg signed [17:0] data_in ; + wire strobe_out ; + wire signed [17:0] data_out ; + + initial + begin + $dumpfile("hb_dec_tb.vcd"); + $dumpvars(0,hb_dec_tb); + end + + // Setup the clock + initial clock = 1'b0 ; + always #5 clock <= ~clock ; + + // Come out of reset after a while + initial reset = 1'b1 ; + initial #1000 reset = 1'b0 ; + + // Enable the entire system + initial enable = 1'b1 ; + + // Instantiate UUT + /* + halfband_ideal + #( + .decim ( decim ), + .rate ( rate ) + ) uut( + .clock ( clock ), + .reset ( reset ), + .enable ( enable ), + .strobe_in ( strobe_in ), + .data_in ( data_in ), + .strobe_out ( strobe_out ), + .data_out ( data_out ) + ) ; + */ + + + hb_dec #(.IWIDTH(18),.OWIDTH(18),.CWIDTH(18),.ACCWIDTH(24)) uut + (.clk(clock),.rst(reset),.bypass(0),.cpi(clocks),.stb_in(strobe_in),.data_in(data_in), + .stb_out(strobe_out),.data_out(data_out) ); + + integer i, ri, ro, infile, outfile ; + + always @(posedge clock) + begin + if(strobe_out) + $display(data_out); + end + + // Setup file IO + initial begin + infile = $fopen("input.dat","r") ; + outfile = $fopen("output.dat","r") ; + $timeformat(-9, 2, " ns", 10) ; + end + + reg endofsim ; + reg signed [17:0] compare ; + integer noe ; + initial noe = 0 ; + + initial begin + // Initialize inputs + strobe_in <= 1'd0 ; + data_in <= 18'd0 ; + + // Wait for reset to go away + @(negedge reset) #0 ; + + // While we're still simulating ... + while( !endofsim ) begin + + // Write the input from the file or 0 if EOF... + @( posedge clock ) begin + //#1 ; + strobe_in <= 1'b1 ; + if( !$feof(infile) ) + ri = $fscanf( infile, "%d", data_in ) ; + else + data_in <= 18'd0 ; + end + + // Clocked in - set the strobe to 0 if the number of + // clocks per sample is greater than 1 + if( clocks > 1 ) begin + @(posedge clock) begin + strobe_in <= 1'b0 ; + end + + // Wait for the specified number of cycles + for( i = 0 ; i < (clocks-2) ; i = i + 1 ) begin + @(posedge clock) #1 ; + end + end + end + + // Print out the number of errors that occured + if( noe ) + $display( "FAILED: %d errors during simulation", noe ) ; + else + $display( "PASSED: Simulation successful" ) ; + + $finish ; + end + + // Output comparison of simulated values versus known good values + always @ (posedge clock) begin + if( reset ) + endofsim <= 1'b0 ; + else begin + if( !$feof(outfile) ) begin + if( strobe_out ) begin + ro = $fscanf( outfile, "%d\n", compare ) ; + if( compare != data_out ) begin + //$display( "%t: %d != %d", $realtime, data_out, compare ) ; + noe = noe + 1 ; + end + end + end else begin + // Signal end of simulation when no more outputs + endofsim <= 1'b1 ; + end + end + end + +endmodule // hb_dec_tb + diff --git a/sdr_lib/hb_interp.v b/sdr_lib/hb_interp.v new file mode 100644 index 000000000..d16807e15 --- /dev/null +++ b/sdr_lib/hb_interp.v @@ -0,0 +1,157 @@ +// First halfband iterpolator +// Implements impulse responses of the form [A 0 B 0 C .. 0 H 0.5 H 0 .. C 0 B 0 A] +// Strobe in cannot come faster than every 4th clock cycle, +// Strobe out cannot come faster than every 2nd clock cycle + +// These taps designed by halfgen4 from ldoolittle +// myfilt = round(2^18 * halfgen4(.7/4,8)) + +module hb_interp + #(parameter IWIDTH=18, OWIDTH=18, ACCWIDTH=24) + (input clk, + input rst, + input bypass, + input [7:0] cpo, // Clocks per output, must be at least 2 + input stb_in, + input [IWIDTH-1:0] data_in, + input stb_out, + output reg [OWIDTH-1:0] data_out); + + localparam MWIDTH = ACCWIDTH-2; + localparam CWIDTH = 18; + + reg [CWIDTH-1:0] coeff1, coeff2; + reg [3:0] addr_a, addr_b, addr_c, addr_d, addr_e; + wire [IWIDTH-1:0] data_a, data_b, data_c, data_d, data_e, sum1, sum2; + wire [35:0] prod1, prod2; + + reg [2:0] phase, phase_d1, phase_d2, phase_d3, phase_d4, phase_d5; + + always @(posedge clk) + if(rst) + phase <= 0; + else + if(stb_in) + phase <= 1; + else if(phase==4) + phase <= 0; + else if(phase!=0) + phase <= phase + 1; + always @(posedge clk) phase_d1 <= phase; + always @(posedge clk) phase_d2 <= phase_d1; + always @(posedge clk) phase_d3 <= phase_d2; + always @(posedge clk) phase_d4 <= phase_d3; + always @(posedge clk) phase_d5 <= phase_d4; + + srl #(.WIDTH(IWIDTH)) srl_a + (.clk(clk),.write(stb_in),.in(data_in),.addr(addr_a),.out(data_a)); + srl #(.WIDTH(IWIDTH)) srl_b + (.clk(clk),.write(stb_in),.in(data_in),.addr(addr_b),.out(data_b)); + srl #(.WIDTH(IWIDTH)) srl_c + (.clk(clk),.write(stb_in),.in(data_in),.addr(addr_c),.out(data_c)); + srl #(.WIDTH(IWIDTH)) srl_d + (.clk(clk),.write(stb_in),.in(data_in),.addr(addr_d),.out(data_d)); + srl #(.WIDTH(IWIDTH)) srl_e + (.clk(clk),.write(stb_in),.in(data_in),.addr(addr_e),.out(data_e)); + + always @* + case(phase) + 1 : begin addr_a = 0; addr_b = 15; end + 2 : begin addr_a = 1; addr_b = 14; end + 3 : begin addr_a = 2; addr_b = 13; end + 4 : begin addr_a = 3; addr_b = 12; end + default : begin addr_a = 0; addr_b = 15; end + endcase // case(phase) + + always @* + case(phase) + 1 : begin addr_c = 4; addr_d = 11; end + 2 : begin addr_c = 5; addr_d = 10; end + 3 : begin addr_c = 6; addr_d = 9; end + 4 : begin addr_c = 7; addr_d = 8; end + default : begin addr_c = 4; addr_d = 11; end + endcase // case(phase) + + always @* + case(cpo) + 2 : addr_e <= 9; + 3,4,5,6,7,8 : addr_e <= 8; + default : addr_e <= 7; // This case works for 256, which = 0 due to overflow outside this block + endcase // case(cpo) + + always @* // Outer coeffs + case(phase_d1) + 1 : coeff1 = -107; + 2 : coeff1 = 445; + 3 : coeff1 = -1271; + 4 : coeff1 = 2959; + default : coeff1 = -107; + endcase // case(phase) + + always @* // Inner coeffs + case(phase_d1) + 1 : coeff2 = -6107; + 2 : coeff2 = 11953; + 3 : coeff2 = -24706; + 4 : coeff2 = 82359; + default : coeff2 = -6107; + endcase // case(phase) + + add2_reg /*_and_round_reg*/ #(.WIDTH(IWIDTH)) add1 (.clk(clk),.in1(data_a),.in2(data_b),.sum(sum1)); + add2_reg /*_and_round_reg*/ #(.WIDTH(IWIDTH)) add2 (.clk(clk),.in1(data_c),.in2(data_d),.sum(sum2)); + // sum1, sum2 available on phase_d1 + + wire do_mult = 1; + MULT18X18S mult1(.C(clk), .CE(do_mult), .R(rst), .P(prod1), .A(coeff1), .B(sum1) ); + MULT18X18S mult2(.C(clk), .CE(do_mult), .R(rst), .P(prod2), .A(coeff2), .B(sum2) ); + // prod1, prod2 available on phase_d2 + + wire [MWIDTH-1:0] sum_of_prod; + + add2_and_round_reg #(.WIDTH(MWIDTH)) + add3 (.clk(clk),.in1(prod1[35:36-MWIDTH]),.in2(prod2[35:36-MWIDTH]),.sum(sum_of_prod)); + // sum_of_prod available on phase_d3 + + wire [ACCWIDTH-1:0] acc_out; + wire [OWIDTH-1:0] acc_round; + + wire clear = (phase_d3 == 1); + wire do_acc = (phase_d3 != 0); + + acc #(.IWIDTH(MWIDTH),.OWIDTH(ACCWIDTH)) + acc (.clk(clk),.clear(clear),.acc(do_acc),.in(sum_of_prod),.out(acc_out)); + // acc_out available on phase_d4 + + wire [ACCWIDTH-6:0] clipped_acc; + clip #(.bits_in(ACCWIDTH),.bits_out(ACCWIDTH-5)) final_clip(.in(acc_out),.out(clipped_acc)); + + reg [ACCWIDTH-6:0] clipped_reg; + always @(posedge clk) + if(phase_d4 == 4) + clipped_reg <= clipped_acc; + // clipped_reg available on phase_d5 + + wire [OWIDTH-1:0] data_out_round; + round #(.bits_in(ACCWIDTH-5),.bits_out(OWIDTH)) final_round (.in(clipped_reg),.out(data_out_round)); + + reg odd; + always @(posedge clk) + if(rst) + odd <= 0; + else if(stb_in) + odd <= 0; + else if(stb_out) + odd <= 1; + + always @(posedge clk) + if(bypass) + data_out <= data_in; + else if(stb_out) + if(odd) + data_out <= data_e; + else + data_out <= data_out_round; + + // data_out available on phase_d6 + +endmodule // hb_interp diff --git a/sdr_lib/hb_interp_tb.v b/sdr_lib/hb_interp_tb.v new file mode 100644 index 000000000..52f137f28 --- /dev/null +++ b/sdr_lib/hb_interp_tb.v @@ -0,0 +1,132 @@ +module hb_interp_tb( ) ; + + // Parameters for instantiation + parameter clocks = 8'd2 ; // Number of clocks per output + parameter decim = 1 ; // Sets the filter to decimate + parameter rate = 2 ; // Sets the decimation rate + + reg clock ; + reg reset ; + reg enable ; + wire strobe_in ; + reg signed [17:0] data_in ; + wire strobe_out ; + wire signed [17:0] data_out ; + + initial + begin + $dumpfile("hb_interp_tb.vcd"); + $dumpvars(0,hb_interp_tb); + end + + // Setup the clock + initial clock = 1'b0 ; + always #5 clock <= ~clock ; + + // Come out of reset after a while + initial reset = 1'b1 ; + initial #1000 reset = 1'b0 ; + + always @(posedge clock) + enable <= ~reset; + + // Instantiate UUT + /* + halfband_ideal + #( + .decim ( decim ), + .rate ( rate ) + ) uut( + .clock ( clock ), + .reset ( reset ), + .enable ( enable ), + .strobe_in ( strobe_in ), + .data_in ( data_in ), + .strobe_out ( strobe_out ), + .data_out ( data_out ) + ) ; + */ + + cic_strober #(.WIDTH(8)) + out_strober(.clock(clock),.reset(reset),.enable(enable),.rate(clocks), + .strobe_fast(1),.strobe_slow(strobe_out) ); + + cic_strober #(.WIDTH(8)) + in_strober(.clock(clock),.reset(reset),.enable(enable),.rate(2), + .strobe_fast(strobe_out),.strobe_slow(strobe_in) ); + + hb_interp #() uut + (.clk(clock),.rst(reset),.bypass(0),.cpo(clocks),.stb_in(strobe_in),.data_in(data_in), + .stb_out(strobe_out),/* .output_rate(clocks), */ .data_out(data_out) ); + + integer i, ri, ro, infile, outfile ; + + always @(posedge clock) + begin + if(strobe_out) + $display(data_out); + end + + // Setup file IO + initial begin + infile = $fopen("input.dat","r") ; + outfile = $fopen("output.dat","r") ; + $timeformat(-9, 2, " ns", 10) ; + end + + reg endofsim ; + reg signed [17:0] compare ; + integer noe ; + initial noe = 0 ; + + initial begin + // Initialize inputs + data_in <= 18'd0 ; + + // Wait for reset to go away + @(negedge reset) #0 ; + + // While we're still simulating ... + while( !endofsim ) begin + + // Write the input from the file or 0 if EOF... + @( negedge clock ) begin + if(strobe_in) + if( !$feof(infile) ) + ri <= #1 $fscanf( infile, "%d", data_in ) ; + else + data_in <= 18'd0 ; + end + end + + // Print out the number of errors that occured + if( noe ) + $display( "FAILED: %d errors during simulation", noe ) ; + else + $display( "PASSED: Simulation successful" ) ; + + $finish ; + end + + // Output comparison of simulated values versus known good values + always @ (posedge clock) begin + if( reset ) + endofsim <= 1'b0 ; + else begin + if( !$feof(outfile) ) begin + if( strobe_out ) begin + ro = $fscanf( outfile, "%d\n", compare ) ; + if( compare != data_out ) begin + //$display( "%t: %d != %d", $realtime, data_out, compare ) ; + noe = noe + 1 ; + end + end + end else begin + // Signal end of simulation when no more outputs + if($feof(infile)) + endofsim <= 1'b1 ; + end + end + end + +endmodule // small_hb_int_tb diff --git a/sdr_lib/hb_tb.v b/sdr_lib/hb_tb.v new file mode 100644 index 000000000..7e960fd13 --- /dev/null +++ b/sdr_lib/hb_tb.v @@ -0,0 +1,155 @@ + +module hb_tb(); + + localparam SWIDTH = 17; + localparam CWIDTH = 18; + localparam TWIDTH = 20; + localparam ACC_WIDTH = 40; + + reg clk = 0, rst = 1; + wire strobe_in, strobe_out; + reg [SWIDTH-1:0] sample_in; + wire signed [SWIDTH:0] sample_out; + + reg set_stb; + reg [7:0] set_addr; + reg [31:0] set_data; + + localparam DECIM = 3; + + initial $dumpfile("hb_tb.vcd"); + initial $dumpvars(0,hb_tb); + + always #5 clk <= ~clk; + initial + begin + @(posedge clk); + @(negedge clk); + rst <= 0; + end + + reg [7:0] stb_counter; + always @(posedge clk) + if(rst) + stb_counter <= 0; + else + if(stb_counter == 0) + stb_counter <= DECIM; + else + stb_counter <= stb_counter - 1; + assign strobe_in = (stb_counter == 0); + + hb_decim #(.SWIDTH(SWIDTH),.CWIDTH(CWIDTH), + .TWIDTH(TWIDTH),.ACC_WIDTH(ACC_WIDTH)) hb_decim + (.clk(clk), .rst(rst), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .sample_in(sample_in), + .strobe_in(strobe_in), + .sample_out(sample_out), + .strobe_out(strobe_out) + ); + + initial + begin : load_coeffs + @(negedge rst); + @(posedge clk); + set_addr <= 124; // load coeffs + set_stb <= 1; + set_data <= -18'd49; + @(posedge clk); + set_data <= 18'd165; + @(posedge clk); + set_data <= -18'd412; + @(posedge clk); + set_data <= 18'd873; + @(posedge clk); + set_data <= -18'd1681; + @(posedge clk); + set_data <= 18'd3135; + @(posedge clk); + set_data <= -18'd6282; + @(posedge clk); + set_data <= 18'd20628; + @(posedge clk); + set_addr <=125; // load table + // { stb_out, accum, load_accum, done, even_addr, odd_addr_a, odd_addr_b, coeff_addr } + set_data <= {1'b1,1'b1,1'b0,1'b1,4'd15,4'd15,4'd0,4'd0}; // Phase 8 + @(posedge clk); + set_data <= {1'b0,1'b1,1'b0,1'b0,4'd15,4'd14,4'd1,4'd1}; // Phase 7 + @(posedge clk); + set_data <= {1'b0,1'b1,1'b0,1'b0,4'd15,4'd13,4'd2,4'd2}; // Phase 6 + @(posedge clk); + set_data <= {1'b0,1'b1,1'b0,1'b0,4'd15,4'd12,4'd3,4'd3}; // Phase 5 + @(posedge clk); + set_data <= {1'b0,1'b1,1'b0,1'b0,4'd15,4'd11,4'd4,4'd4}; // Phase 4 + @(posedge clk); + set_data <= {1'b0,1'b1,1'b0,1'b0,4'd15,4'd10,4'd5,4'd5}; // Phase 3 + @(posedge clk); + set_data <= {1'b0,1'b1,1'b0,1'b0,4'd15,4'd9,4'd6,4'd6}; // Phase 2 + @(posedge clk); + set_data <= {1'b0,1'b0,1'b1,1'b0,4'd15,4'd8,4'd7,4'd7}; // Phase 1 + @(posedge clk); + set_data <= {1'b0,1'b0,1'b0,1'b0,4'd15,4'd8,4'd7,4'd7}; // Phase 0 + @(posedge clk); + set_stb <= 0; + end // block: load_coeffs + + initial + begin + sample_in <= 0; + repeat(40) + @(posedge strobe_in); + $display("EVEN"); + sample_in <= 0; + repeat(10) + @(posedge strobe_in); + sample_in <= 1; + @(posedge strobe_in); + sample_in <= 0; + repeat(40) + @(posedge strobe_in); + sample_in <= 1; + repeat(40) + @(posedge strobe_in); + sample_in <= 0; + repeat(60) + @(posedge strobe_in); + sample_in <= 1; + repeat(2) + @(posedge strobe_in); + sample_in <= 0; + repeat(60) + @(posedge strobe_in); + $display("ODD"); + sample_in <= 0; + repeat(10) + @(posedge strobe_in); + sample_in <= 1; + @(posedge strobe_in); + sample_in <= 0; + repeat(40) + @(posedge strobe_in); + sample_in <= 1; + repeat(40) + @(posedge strobe_in); + sample_in <= 0; + repeat(60) + @(posedge strobe_in); + sample_in <= 1; + repeat(2) + @(posedge strobe_in); + sample_in <= 0; + repeat(60) + @(posedge strobe_in); + $finish; + end + + always @(posedge clk) + if(strobe_in) + $display(sample_in); + + always @(posedge clk) + if(strobe_out) + $display("\t",sample_out); + +endmodule // hb_tb diff --git a/sdr_lib/input.dat b/sdr_lib/input.dat new file mode 100644 index 000000000..1e649ac2e --- /dev/null +++ b/sdr_lib/input.dat @@ -0,0 +1,341 @@ +0 +0 +0 +0 +0 +0 +0 +0 +-131072 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +131071 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +131071 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +100000 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +-131072 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +-131072 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 diff --git a/sdr_lib/output.dat b/sdr_lib/output.dat new file mode 100644 index 000000000..15db3ced4 --- /dev/null +++ b/sdr_lib/output.dat @@ -0,0 +1,130 @@ +-1390 +0 +1604 +0 +-1896 +0 +2317 +0 +-2979 +0 +4172 +0 +-6953 +0 +20860 +32768 +20860 +0 +-6953 +0 +4172 +0 +-2979 +0 +2317 +0 +-1896 +0 +1604 +0 +-1390 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + diff --git a/sdr_lib/round.v b/sdr_lib/round.v new file mode 100644 index 000000000..bff088831 --- /dev/null +++ b/sdr_lib/round.v @@ -0,0 +1,33 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2007 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Rounding "macro" +// Keeps the topmost bits, does proper 2s comp rounding - round to zero + +module round + #(parameter bits_in=0, + parameter bits_out=0) + (input [bits_in-1:0] in, + output [bits_out-1:0] out); + + assign out = in[bits_in-1:bits_in-bits_out] + (in[bits_in-1] & |in[bits_in-bits_out-1:0]); + +endmodule // round diff --git a/sdr_lib/round_reg.v b/sdr_lib/round_reg.v new file mode 100644 index 000000000..aa0972dab --- /dev/null +++ b/sdr_lib/round_reg.v @@ -0,0 +1,39 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2008 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., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Rounding "macro" +// Keeps the topmost bits, does proper 2s comp rounding (round-to-zero) + +module round_reg + #(parameter bits_in=0, + parameter bits_out=0) + (input clk, + input [bits_in-1:0] in, + output reg [bits_out-1:0] out); + + wire [bits_out-1:0] temp; + + round #(.bits_in(bits_in),.bits_out(bits_out)) round (.in(in),.out(temp)); + + always @(posedge clk) + out <= temp; + +endmodule // round diff --git a/sdr_lib/rssi.v b/sdr_lib/rssi.v new file mode 100644 index 000000000..e45e2148c --- /dev/null +++ b/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/sdr_lib/rx_control.v b/sdr_lib/rx_control.v new file mode 100644 index 000000000..d41a28bcf --- /dev/null +++ b/sdr_lib/rx_control.v @@ -0,0 +1,213 @@ + +`define DSP_CORE_RX_BASE 160 + +module rx_control + #(parameter FIFOSIZE = 10) + (input clk, input rst, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + input [31:0] master_time, + output overrun, + + // To Buffer interface + output [31:0] wr_dat_o, + output wr_write_o, + output wr_done_o, + output wr_error_o, + + input wr_ready_i, + input wr_full_i, + + // From DSP Core + input [31:0] sample, + output run, + input strobe, + + // FIFO Levels + output [15:0] fifo_occupied, + output fifo_full, + output fifo_empty, + + // Debug + output [31:0] debug_rx + ); + + wire [31:0] new_time, new_command; + wire sc_pre1, clear_overrun; + wire [31:0] rcvtime_pre; + reg [31:0] rcvtime; + wire [8:0] lines_per_frame; + wire [20:0] numlines; + wire send_imm_pre, chain_pre; + reg send_imm, chain; + wire full_ctrl, read_ctrl, empty_ctrl, write_ctrl; + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+3)) sr_3 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(new_time),.changed(sc_pre1)); + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+4)) sr_4 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(new_command),.changed()); + + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+5)) sr_5 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(),.changed(clear_overrun)); + + reg sc_pre2; + always @(posedge clk) + sc_pre2 <= sc_pre1; + assign write_ctrl = sc_pre1 & ~sc_pre2; + + shortfifo #(.WIDTH(64)) commandfifo + (.clk(clk),.rst(rst),.clear(clear_overrun), + .datain({new_command,new_time}), .write(write_ctrl), .full(full_ctrl), + .dataout({send_imm_pre,chain_pre,numlines,lines_per_frame,rcvtime_pre}), + .read(read_ctrl), .empty(empty_ctrl) ); + + // Buffer interface to internal FIFO + wire write, full, read, empty; + wire sop_o, eop_o; + + reg xfer_state; + localparam XFER_IDLE = 1'b0; + localparam XFER_GO = 1'b1; + + always @(posedge clk) + if(rst) + xfer_state <= XFER_IDLE; + else + if(clear_overrun) + xfer_state <= XFER_IDLE; + else + case(xfer_state) + XFER_IDLE : + if(wr_ready_i) + xfer_state <= XFER_GO; + XFER_GO : + if((eop_o | wr_full_i) & wr_write_o) + xfer_state <= XFER_IDLE; + default : + xfer_state <= XFER_IDLE; + endcase // case(xfer_state) + + assign wr_write_o = (xfer_state == XFER_GO) & ~empty; + assign wr_done_o = (eop_o & wr_write_o); + assign wr_error_o = 0; // FIXME add check here for eop if we have wr_full_i once we have IBS + + assign read = wr_write_o | (~empty & ~sop_o); // FIXME what if there is junk between packets? + + wire [33:0] fifo_line; + + // Internal FIFO, size 9 is 2K, size 10 is 4K + cascadefifo2 #(.WIDTH(34),.SIZE(FIFOSIZE)) rxfifo + (.clk(clk),.rst(rst),.clear(clear_overrun), + .datain(fifo_line), .write(write), .full(full), + .dataout({sop_o,eop_o,wr_dat_o}), .read(read), .empty(empty), + .space(),.occupied(fifo_occupied) ); + assign fifo_full = full; + assign fifo_empty = empty; + + // Internal FIFO to DSP interface + reg [22:0] lines_left; + reg [8:0] lines_left_frame; + localparam IBS_IDLE = 0; + localparam IBS_WAITING = 1; + localparam IBS_FIRSTLINE = 2; + localparam IBS_RUNNING = 3; + localparam IBS_OVERRUN = 4; + + reg [2:0] ibs_state; + + wire [32:0] delta_time = {1'b0,rcvtime}-{1'b0,master_time}; + wire too_late = (delta_time[32:31] == 2'b11) & ~send_imm; + wire go_now = send_imm | ( master_time == rcvtime ); + + always @(posedge clk) + if(rst) + begin + ibs_state <= IBS_IDLE; + lines_left <= 0; + lines_left_frame <= 0; + rcvtime <= 0; + send_imm <= 0; + chain <= 0; + end + else + if(clear_overrun) + begin + ibs_state <= IBS_IDLE; + lines_left <= 0; + lines_left_frame <= 0; + rcvtime <= 0; + send_imm <= 0; + chain <= 0; + end + else + case(ibs_state) + IBS_IDLE : + if(~empty_ctrl) + begin + lines_left <= numlines; + lines_left_frame <= lines_per_frame; + rcvtime <= rcvtime_pre; + ibs_state <= IBS_WAITING; + send_imm <= send_imm_pre; + chain <= chain_pre; + end + IBS_WAITING : + if(go_now) + ibs_state <= IBS_FIRSTLINE; + else if(too_late) + ibs_state <= IBS_OVERRUN; + IBS_FIRSTLINE : + if(full | strobe) + ibs_state <= IBS_OVERRUN; + else + ibs_state <= IBS_RUNNING; + IBS_RUNNING : + if(strobe) + if(full) + ibs_state <= IBS_OVERRUN; + else + begin + lines_left <= lines_left - 1; + if(lines_left == 1) + if(~chain) + ibs_state <= IBS_IDLE; + else if(empty_ctrl) + ibs_state <= IBS_OVERRUN; + else + begin + lines_left <= numlines; + lines_left_frame <= lines_per_frame; + rcvtime <= rcvtime_pre; + ibs_state <= IBS_FIRSTLINE; + send_imm <= send_imm_pre; + chain <= chain_pre; + end + else if(lines_left_frame == 1) + begin + lines_left_frame <= lines_per_frame; + ibs_state <= IBS_FIRSTLINE; + end + else + lines_left_frame <= lines_left_frame - 1; + end // else: !if(full) + endcase // case(ibs_state) + + assign fifo_line = (ibs_state == IBS_FIRSTLINE) ? {1'b1,1'b0,master_time} : + {1'b0,((lines_left==1)|(lines_left_frame==1)),sample}; + + assign write = ((ibs_state == IBS_FIRSTLINE) | strobe) & ~full; // & (ibs_state == IBS_RUNNING) should strobe only when running + assign overrun = (ibs_state == IBS_OVERRUN); + assign run = (ibs_state == IBS_RUNNING) | (ibs_state == IBS_FIRSTLINE); + assign read_ctrl = ( (ibs_state == IBS_IDLE) | + ((ibs_state == IBS_RUNNING) & strobe & ~full & (lines_left==1) & chain) ) + & ~empty_ctrl; + + assign debug_rx = { 6'd0,send_imm,chain, + wr_write_o, wr_done_o, wr_ready_i, wr_full_i,xfer_state,eop_o, sop_o, run, + write,full,read,empty,write_ctrl,full_ctrl,read_ctrl,empty_ctrl, + sc_pre1, clear_overrun, go_now, too_late, overrun, ibs_state[2:0] }; +endmodule // rx_control diff --git a/sdr_lib/rx_dcoffset.v b/sdr_lib/rx_dcoffset.v new file mode 100644 index 000000000..bedbd40e6 --- /dev/null +++ b/sdr_lib/rx_dcoffset.v @@ -0,0 +1,43 @@ + + +module rx_dcoffset + #(parameter WIDTH=14, + parameter ADDR=8'd0) + (input clk, input rst, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input signed [WIDTH-1:0] adc_in, output signed [WIDTH-1:0] adc_out); + + // Because of some extra delays to make timing easier, the transfer function is: + // (z-1)/(z^2-z-alpha) where alpha is 1/2^n + + wire set_now = set_stb & (ADDR == set_addr); + + reg fixed; // uses fixed offset + wire signed [WIDTH-1:0] fixed_dco; + reg signed [31:0] integrator; + + always @(posedge clk) + if(rst) + begin + fixed <= 0; + integrator <= 32'd0; + end + else if(set_now) + begin + integrator <= {set_data[WIDTH-1:0],{(32-WIDTH){1'b0}}}; + fixed <= set_data[31]; + end + else if(~fixed) + integrator <= integrator + adc_out; + + wire [WIDTH:0] scaled_integrator; + + round #(.bits_in(33),.bits_out(15)) round (.in({integrator[31],integrator}),.out(scaled_integrator)); + + wire [WIDTH:0] adc_out_int = {adc_in[WIDTH-1],adc_in} - scaled_integrator; + + clip_reg #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip_adc + (.clk(clk),.in(adc_out_int),.out(adc_out)); + + +endmodule // rx_dcoffset diff --git a/sdr_lib/rx_dcoffset_tb.v b/sdr_lib/rx_dcoffset_tb.v new file mode 100644 index 000000000..a8b4ec20f --- /dev/null +++ b/sdr_lib/rx_dcoffset_tb.v @@ -0,0 +1,25 @@ + +`timescale 1ns/1ns +module rx_dcoffset_tb(); + + reg clk, rst; + + initial rst = 1; + initial #1000 rst = 0; + initial clk = 0; + always #5 clk = ~clk; + + initial $dumpfile("rx_dcoffset_tb.vcd"); + initial $dumpvars(0,rx_dcoffset_tb); + + reg [13:0] adc_in = 7; + wire [13:0] adc_out; + + always @(posedge clk) + $display("%d\t%d",adc_in,adc_out); + + rx_dcoffset #(.WIDTH(14),.ADDR(0)) + rx_dcoffset(.clk(clk),.rst(rst),.set_stb(0),.set_addr(0),.set_data(0), + .adc_in(adc_in),.adc_out(adc_out)); + +endmodule // longfifo_tb diff --git a/sdr_lib/sign_extend.v b/sdr_lib/sign_extend.v new file mode 100644 index 000000000..eae67faf2 --- /dev/null +++ b/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., 51 Franklin Street, Boston, MA 02110-1301 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/sdr_lib/small_hb_dec.v b/sdr_lib/small_hb_dec.v new file mode 100644 index 000000000..9957de15a --- /dev/null +++ b/sdr_lib/small_hb_dec.v @@ -0,0 +1,99 @@ +// Short halfband decimator (intended to be followed by another stage) +// Implements impulse responses of the form [A 0 B 0.5 B 0 A] +// +// These taps designed by halfgen4 from ldoolittle: +// 2 * 131072 * halfgen4(.75/8,2) +module small_hb_dec + #(parameter WIDTH=18) + (input clk, + input rst, + input bypass, + input stb_in, + input [WIDTH-1:0] data_in, + output reg stb_out, + output [WIDTH-1:0] data_out); + + reg stb_in_d1; + reg [WIDTH-1:0] data_in_d1; + always @(posedge clk) stb_in_d1 <= stb_in; + always @(posedge clk) data_in_d1 <= data_in; + + wire go; + reg phase, go_d1, go_d2, go_d3, go_d4; + always @(posedge clk) + if(rst) + phase <= 0; + else if(stb_in_d1) + phase <= ~phase; + assign go = stb_in_d1 & phase; + always @(posedge clk) go_d1 <= go; + always @(posedge clk) go_d2 <= go_d1; + always @(posedge clk) go_d3 <= go_d2; + always @(posedge clk) go_d4 <= go_d3; + + wire [17:0] coeff_a = -10690; + wire [17:0] coeff_b = 75809; + + reg [WIDTH-1:0] d1, d2, d3, d4 , d5, d6; + always @(posedge clk) + if(stb_in_d1 | rst) + begin + d1 <= data_in_d1; + d2 <= d1; + d3 <= d2; + d4 <= d3; + d5 <= d4; + d6 <= d5; + end + + reg [17:0] sum_a, sum_b, middle, middle_d1; + wire [17:0] sum_a_unreg, sum_b_unreg; + add2 #(.WIDTH(18)) add2_a (.in1(data_in_d1),.in2(d6),.sum(sum_a_unreg)); + add2 #(.WIDTH(18)) add2_b (.in1(d2),.in2(d4),.sum(sum_b_unreg)); + + always @(posedge clk) + if(go) + begin + sum_a <= sum_a_unreg; + sum_b <= sum_b_unreg; + middle <= d3; + end + + always @(posedge clk) + if(go_d1) + middle_d1 <= middle; + + wire [17:0] sum = go_d1 ? sum_b : sum_a; + wire [17:0] coeff = go_d1 ? coeff_b : coeff_a; + wire [35:0] prod; + MULT18X18S mult(.C(clk), .CE(go_d1 | go_d2), .R(rst), .P(prod), .A(coeff), .B(sum) ); + + reg [35:0] accum; + always @(posedge clk) + if(rst) + accum <= 0; + else if(go_d2) + accum <= {middle_d1[17],middle_d1[17],middle_d1,16'd0} + {prod}; + else if(go_d3) + accum <= accum + {prod}; + + wire [17:0] accum_rnd; + round #(.bits_in(36),.bits_out(18)) round_acc (.in(accum),.out(accum_rnd)); + + reg [17:0] final_sum; + always @(posedge clk) + if(bypass) + final_sum <= data_in_d1; + else if(go_d4) + final_sum <= accum_rnd; + + assign data_out = final_sum; + + always @(posedge clk) + if(rst) + stb_out <= 0; + else if(bypass) + stb_out <= stb_in_d1; + else + stb_out <= go_d4; +endmodule // small_hb_dec diff --git a/sdr_lib/small_hb_dec_tb.v b/sdr_lib/small_hb_dec_tb.v new file mode 100644 index 000000000..0d6a0689e --- /dev/null +++ b/sdr_lib/small_hb_dec_tb.v @@ -0,0 +1,140 @@ +module hb_dec_tb( ) ; + + // Parameters for instantiation + parameter clocks = 9'd2 ; // Number of clocks per input + parameter decim = 1 ; // Sets the filter to decimate + parameter rate = 2 ; // Sets the decimation rate + + reg clock ; + reg reset ; + reg enable ; + reg strobe_in ; + reg signed [17:0] data_in ; + wire strobe_out ; + wire signed [17:0] data_out ; + + initial + begin + $dumpfile("hb_dec_tb.vcd"); + $dumpvars(0,hb_dec_tb); + end + + // Setup the clock + initial clock = 1'b0 ; + always #5 clock <= ~clock ; + + // Come out of reset after a while + initial reset = 1'b1 ; + initial #1000 reset = 1'b0 ; + + // Enable the entire system + initial enable = 1'b1 ; + + // Instantiate UUT + /* + halfband_ideal + #( + .decim ( decim ), + .rate ( rate ) + ) uut( + .clock ( clock ), + .reset ( reset ), + .enable ( enable ), + .strobe_in ( strobe_in ), + .data_in ( data_in ), + .strobe_out ( strobe_out ), + .data_out ( data_out ) + ) ; + */ + + + small_hb_dec #(.WIDTH(18)) uut + (.clk(clock),.rst(reset),.bypass(0),.stb_in(strobe_in),.data_in(data_in), + .stb_out(strobe_out),.data_out(data_out) ); + + integer i, ri, ro, infile, outfile ; + + always @(posedge clock) + begin + if(strobe_out) + $display(data_out); + end + + // Setup file IO + initial begin + infile = $fopen("input.dat","r") ; + outfile = $fopen("output.dat","r") ; + $timeformat(-9, 2, " ns", 10) ; + end + + reg endofsim ; + reg signed [17:0] compare ; + integer noe ; + initial noe = 0 ; + + initial begin + // Initialize inputs + strobe_in <= 1'd0 ; + data_in <= 18'd0 ; + + // Wait for reset to go away + @(negedge reset) #0 ; + + // While we're still simulating ... + while( !endofsim ) begin + + // Write the input from the file or 0 if EOF... + @( posedge clock ) begin + //#1 ; + strobe_in <= 1'b1 ; + if( !$feof(infile) ) + ri = $fscanf( infile, "%d", data_in ) ; + else + data_in <= 18'd0 ; + end + + // Clocked in - set the strobe to 0 if the number of + // clocks per sample is greater than 1 + if( clocks > 1 ) begin + @(posedge clock) begin + strobe_in <= 1'b0 ; + end + + // Wait for the specified number of cycles + for( i = 0 ; i < (clocks-2) ; i = i + 1 ) begin + @(posedge clock) #1 ; + end + end + end + + // Print out the number of errors that occured + if( noe ) + $display( "FAILED: %d errors during simulation", noe ) ; + else + $display( "PASSED: Simulation successful" ) ; + + $finish ; + end + + // Output comparison of simulated values versus known good values + always @ (posedge clock) begin + if( reset ) + endofsim <= 1'b0 ; + else begin + if( !$feof(outfile) ) begin + if( strobe_out ) begin + ro = $fscanf( outfile, "%d\n", compare ) ; + if( compare != data_out ) begin + //$display( "%t: %d != %d", $realtime, data_out, compare ) ; + noe = noe + 1 ; + end + end + end else begin + // Signal end of simulation when no more outputs + endofsim <= 1'b1 ; + end + end + end + +endmodule // hb_dec_tb + diff --git a/sdr_lib/small_hb_int.v b/sdr_lib/small_hb_int.v new file mode 100644 index 000000000..f80d3cac3 --- /dev/null +++ b/sdr_lib/small_hb_int.v @@ -0,0 +1,85 @@ +// Short halfband decimator (intended to be followed by another stage) +// Implements impulse responses of the form [A 0 B 0.5 B 0 A] +// +// These taps designed by halfgen4 from ldoolittle: +// 2 * 131072 * halfgen4(.75/8,2) + +module small_hb_int + #(parameter WIDTH=18) + (input clk, + input rst, + input bypass, + input stb_in, + input [WIDTH-1:0] data_in, + input [7:0] output_rate, + input stb_out, + output reg [WIDTH-1:0] data_out); + + reg phase; + reg [WIDTH-1:0] d1, d2, d3, d4, d5, d6; + + localparam MWIDTH = 36; + wire [MWIDTH-1:0] prod; + + reg [6:0] stbin_d; + + always @(posedge clk) + stbin_d <= {stbin_d[5:0],stb_in}; + + always @(posedge clk) + if(stb_in) + begin + d1 <= data_in; + d2 <= d1; + d3 <= d2; + d4 <= d3; + d5 <= d4; + d6 <= d5; + end + + wire [WIDTH-1:0] sum_outer, sum_inner; + add2_and_round_reg #(.WIDTH(WIDTH)) add_outer (.clk(clk),.in1(d1),.in2(d4),.sum(sum_outer)); + add2_and_round_reg #(.WIDTH(WIDTH)) add_inner (.clk(clk),.in1(d2),.in2(d3),.sum(sum_inner)); + + wire [17:0] coeff_outer = -10690; + wire [17:0] coeff_inner = 75809; + + MULT18X18S mult(.C(clk), .CE(1), .R(rst), .P(prod), .A(stbin_d[1] ? coeff_outer : coeff_inner), + .B(stbin_d[1] ? sum_outer : sum_inner) ); + + wire [MWIDTH:0] accum; + acc #(.IWIDTH(MWIDTH),.OWIDTH(MWIDTH+1)) + acc (.clk(clk),.clear(stbin_d[2]),.acc(|stbin_d[3:2]),.in(prod),.out(accum)); + + wire [WIDTH+2:0] accum_rnd; + round_reg #(.bits_in(MWIDTH+1),.bits_out(WIDTH+3)) + final_round (.clk(clk),.in(accum),.out(accum_rnd)); + + wire [WIDTH-1:0] clipped; + clip_reg #(.bits_in(WIDTH+3),.bits_out(WIDTH)) + final_clip (.clk(clk),.in(accum_rnd),.out(clipped)); + + reg [WIDTH-1:0] saved, saved_d3; + always @(posedge clk) + if(stbin_d[6]) + saved <= clipped; + + always @(posedge clk) + if(stbin_d[3]) + saved_d3 <= d3; + + always @(posedge clk) + if(bypass) + data_out <= data_in; + else if(stb_in & stb_out) + case(output_rate) + 1 : data_out <= d6; + 2 : data_out <= d4; + 3, 4, 5, 6, 7 : data_out <= d3; + default : data_out <= d2; + endcase // case(output_rate) + else if(stb_out) + data_out <= saved; + +endmodule // small_hb_int + diff --git a/sdr_lib/small_hb_int_tb.v b/sdr_lib/small_hb_int_tb.v new file mode 100644 index 000000000..71d77f0a8 --- /dev/null +++ b/sdr_lib/small_hb_int_tb.v @@ -0,0 +1,132 @@ +module small_hb_int_tb( ) ; + + // Parameters for instantiation + parameter clocks = 8'd1 ; // Number of clocks per output + parameter decim = 1 ; // Sets the filter to decimate + parameter rate = 2 ; // Sets the decimation rate + + reg clock ; + reg reset ; + reg enable ; + wire strobe_in ; + reg signed [17:0] data_in ; + wire strobe_out ; + wire signed [17:0] data_out ; + + initial + begin + $dumpfile("small_hb_int_tb.vcd"); + $dumpvars(0,small_hb_int_tb); + end + + // Setup the clock + initial clock = 1'b0 ; + always #5 clock <= ~clock ; + + // Come out of reset after a while + initial reset = 1'b1 ; + initial #1000 reset = 1'b0 ; + + always @(posedge clock) + enable <= ~reset; + + // Instantiate UUT + /* + halfband_ideal + #( + .decim ( decim ), + .rate ( rate ) + ) uut( + .clock ( clock ), + .reset ( reset ), + .enable ( enable ), + .strobe_in ( strobe_in ), + .data_in ( data_in ), + .strobe_out ( strobe_out ), + .data_out ( data_out ) + ) ; + */ + + cic_strober #(.WIDTH(8)) + out_strober(.clock(clock),.reset(reset),.enable(enable),.rate(clocks), + .strobe_fast(1),.strobe_slow(strobe_out) ); + + cic_strober #(.WIDTH(8)) + in_strober(.clock(clock),.reset(reset),.enable(enable),.rate(2), + .strobe_fast(strobe_out),.strobe_slow(strobe_in) ); + + small_hb_int #(.WIDTH(18)) uut + (.clk(clock),.rst(reset),.bypass(0),.stb_in(strobe_in),.data_in(data_in), + .stb_out(strobe_out),.output_rate(clocks),.data_out(data_out) ); + + integer i, ri, ro, infile, outfile ; + + always @(posedge clock) + begin + if(strobe_out) + $display(data_out); + end + + // Setup file IO + initial begin + infile = $fopen("input.dat","r") ; + outfile = $fopen("output.dat","r") ; + $timeformat(-9, 2, " ns", 10) ; + end + + reg endofsim ; + reg signed [17:0] compare ; + integer noe ; + initial noe = 0 ; + + initial begin + // Initialize inputs + data_in <= 18'd0 ; + + // Wait for reset to go away + @(negedge reset) #0 ; + + // While we're still simulating ... + while( !endofsim ) begin + + // Write the input from the file or 0 if EOF... + @( negedge clock ) begin + if(strobe_in) + if( !$feof(infile) ) + ri <= #1 $fscanf( infile, "%d", data_in ) ; + else + data_in <= 18'd0 ; + end + end + + // Print out the number of errors that occured + if( noe ) + $display( "FAILED: %d errors during simulation", noe ) ; + else + $display( "PASSED: Simulation successful" ) ; + + $finish ; + end + + // Output comparison of simulated values versus known good values + always @ (posedge clock) begin + if( reset ) + endofsim <= 1'b0 ; + else begin + if( !$feof(outfile) ) begin + if( strobe_out ) begin + ro = $fscanf( outfile, "%d\n", compare ) ; + if( compare != data_out ) begin + //$display( "%t: %d != %d", $realtime, data_out, compare ) ; + noe = noe + 1 ; + end + end + end else begin + // Signal end of simulation when no more outputs + if($feof(infile)) + endofsim <= 1'b1 ; + end + end + end + +endmodule // small_hb_int_tb diff --git a/sdr_lib/tx_control.v b/sdr_lib/tx_control.v new file mode 100644 index 000000000..0c4ab1a52 --- /dev/null +++ b/sdr_lib/tx_control.v @@ -0,0 +1,160 @@ + +`define DSP_CORE_TX_BASE 128 + +module tx_control + #(parameter FIFOSIZE = 10) + (input clk, input rst, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + input [31:0] master_time, + output underrun, + + // To Buffer interface + input [31:0] rd_dat_i, + input rd_sop_i, + input rd_eop_i, + output rd_read_o, + output rd_done_o, + output rd_error_o, + + // To DSP Core + output [31:0] sample, + output run, + input strobe, + + // FIFO Levels + output [15:0] fifo_occupied, + output fifo_full, + output fifo_empty, + + // Debug + output [31:0] debug + ); + + // Buffer interface to internal FIFO + wire write_data, write_ctrl, full_data, full_ctrl; + wire read_data, read_ctrl, empty_data, empty_ctrl; + wire clear_state; + reg [1:0] xfer_state; + reg [2:0] held_flags; + + localparam XFER_IDLE = 0; + localparam XFER_1 = 1; + localparam XFER_2 = 2; + localparam XFER_DATA = 3; + + always @(posedge clk) + if(rst) + xfer_state <= XFER_IDLE; + else + if(clear_state) + xfer_state <= XFER_IDLE; + else + case(xfer_state) + XFER_IDLE : + if(rd_sop_i) + xfer_state <= XFER_1; + XFER_1 : + begin + xfer_state <= XFER_2; + held_flags <= rd_dat_i[2:0]; + end + XFER_2 : + if(~full_ctrl) + xfer_state <= XFER_DATA; + XFER_DATA : + if(rd_eop_i & ~full_data) + xfer_state <= XFER_IDLE; + endcase // case(xfer_state) + + assign write_data = (xfer_state == XFER_DATA) & ~full_data; + assign write_ctrl = (xfer_state == XFER_2) & ~full_ctrl; + + assign rd_read_o = (xfer_state == XFER_1) | write_data | write_ctrl; + assign rd_done_o = 0; // Always take everything we're given + assign rd_error_o = 0; // Should we indicate overruns here? + + wire [31:0] data_o; + wire sop_o, eop_o, eob, sob, send_imm; + wire [31:0] sendtime; + wire [4:0] occ_ctrl; + + cascadefifo2 #(.WIDTH(34),.SIZE(FIFOSIZE)) txctrlfifo + (.clk(clk),.rst(rst),.clear(clear_state), + .datain({rd_sop_i,rd_eop_i,rd_dat_i}), .write(write_data), .full(full_data), + .dataout({sop_o,eop_o,data_o}), .read(read_data), .empty(empty_data), + .space(), .occupied(fifo_occupied) ); + assign fifo_full = full_data; + assign fifo_empty = empty_data; + + shortfifo #(.WIDTH(35)) ctrlfifo + (.clk(clk),.rst(rst),.clear(clear_state), + .datain({held_flags[2:0],rd_dat_i}), .write(write_ctrl), .full(full_ctrl), + .dataout({send_imm,sob,eob,sendtime}), .read(read_ctrl), .empty(empty_ctrl), + .space(), .occupied(occ_ctrl) ); + + // Internal FIFO to DSP interface + reg [2:0] ibs_state; + + localparam IBS_IDLE = 0; + localparam IBS_WAIT = 1; + localparam IBS_RUNNING = 2; + localparam IBS_CONT_BURST = 3; + localparam IBS_UNDERRUN = 7; + + wire [32:0] delta_time = {1'b0,sendtime}-{1'b0,master_time}; + + wire too_late = (delta_time[32:31] == 2'b11); + wire go_now = ( master_time == sendtime ); + + always @(posedge clk) + if(rst) + ibs_state <= IBS_IDLE; + else + case(ibs_state) + IBS_IDLE : + if(~empty_ctrl & ~empty_data) + ibs_state <= IBS_WAIT; + IBS_WAIT : + if(send_imm) + ibs_state <= IBS_RUNNING; + else if(too_late) + ibs_state <= IBS_UNDERRUN; + else if(go_now) + ibs_state <= IBS_RUNNING; + IBS_RUNNING : + if(strobe) + if(empty_data) + ibs_state <= IBS_UNDERRUN; + else if(eop_o) + if(eob) + ibs_state <= IBS_IDLE; + else + ibs_state <= IBS_CONT_BURST; + IBS_CONT_BURST : + if(~empty_ctrl) // & ~empty_data) + ibs_state <= IBS_RUNNING; + else if(strobe) + ibs_state <= IBS_UNDERRUN; + IBS_UNDERRUN : // FIXME Should probably clean everything out + if(clear_state) + ibs_state <= IBS_IDLE; + endcase // case(ibs_state) + + assign read_ctrl = (ibs_state == IBS_RUNNING) & strobe & eop_o; // & ~empty_ctrl; + assign read_data = (ibs_state == IBS_RUNNING) & strobe & ~empty_data; + assign run = (ibs_state == IBS_RUNNING) | (ibs_state == IBS_CONT_BURST); + assign underrun = (ibs_state == IBS_UNDERRUN); + + wire [7:0] interp_rate; + setting_reg #(.my_addr(`DSP_CORE_TX_BASE+3)) sr_3 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(),.changed(clear_state)); + + assign sample = data_o; + + assign debug = { {16'b0}, + { read_data, write_data, read_ctrl, write_ctrl, xfer_state[1:0],full_ctrl,empty_ctrl }, + { occ_ctrl, eop_o, clear_state, underrun} }; + +endmodule // tx_control |