diff options
Diffstat (limited to 'fpga/usrp2/sdr_lib')
75 files changed, 6381 insertions, 0 deletions
| diff --git a/fpga/usrp2/sdr_lib/.gitignore b/fpga/usrp2/sdr_lib/.gitignore new file mode 100644 index 000000000..3c782d589 --- /dev/null +++ b/fpga/usrp2/sdr_lib/.gitignore @@ -0,0 +1,3 @@ +/a.out +/db +/*.vcd diff --git a/fpga/usrp2/sdr_lib/HB.sav b/fpga/usrp2/sdr_lib/HB.sav new file mode 100644 index 000000000..c5087e8a6 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/Makefile.srcs b/fpga/usrp2/sdr_lib/Makefile.srcs new file mode 100644 index 000000000..defbced17 --- /dev/null +++ b/fpga/usrp2/sdr_lib/Makefile.srcs @@ -0,0 +1,39 @@ +# +# Copyright 2010 Ettus Research LLC +# + +################################################## +# FIFO Sources +################################################## +SDR_LIB_SRCS = $(abspath $(addprefix $(BASE_DIR)/../sdr_lib/, \ +acc.v \ +add2.v \ +add2_and_clip.v \ +add2_and_clip_reg.v \ +add2_and_round.v \ +add2_and_round_reg.v \ +add2_reg.v \ +cic_dec_shifter.v \ +cic_decim.v \ +cic_int_shifter.v \ +cic_interp.v \ +cic_strober.v \ +clip.v \ +clip_reg.v \ +cordic.v \ +cordic_z24.v \ +cordic_stage.v \ +dsp_core_rx.v \ +dsp_core_tx.v \ +hb_dec.v \ +hb_interp.v \ +round.v \ +round_reg.v \ +round_sd.v \ +rx_dcoffset.v \ +rx_frontend.v \ +sign_extend.v \ +small_hb_dec.v \ +small_hb_int.v \ +tx_frontend.v \ +)) diff --git a/fpga/usrp2/sdr_lib/SMALL_HB.sav b/fpga/usrp2/sdr_lib/SMALL_HB.sav new file mode 100644 index 000000000..96ba00636 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/acc.v b/fpga/usrp2/sdr_lib/acc.v new file mode 100644 index 000000000..d5fc4b910 --- /dev/null +++ b/fpga/usrp2/sdr_lib/acc.v @@ -0,0 +1,45 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +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/fpga/usrp2/sdr_lib/add2.v b/fpga/usrp2/sdr_lib/add2.v new file mode 100644 index 000000000..dcca84fd3 --- /dev/null +++ b/fpga/usrp2/sdr_lib/add2.v @@ -0,0 +1,28 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +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/fpga/usrp2/sdr_lib/add2_and_clip.v b/fpga/usrp2/sdr_lib/add2_and_clip.v new file mode 100644 index 000000000..663f5d004 --- /dev/null +++ b/fpga/usrp2/sdr_lib/add2_and_clip.v @@ -0,0 +1,12 @@ + +module add2_and_clip +  #(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}; +   clip #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip +     (.in(sum_int),.out(sum)); +    +endmodule // add2_and_clip diff --git a/fpga/usrp2/sdr_lib/add2_and_clip_reg.v b/fpga/usrp2/sdr_lib/add2_and_clip_reg.v new file mode 100644 index 000000000..8073b3b54 --- /dev/null +++ b/fpga/usrp2/sdr_lib/add2_and_clip_reg.v @@ -0,0 +1,25 @@ + +module add2_and_clip_reg +  #(parameter WIDTH=16) +    (input clk, +     input rst, +     input [WIDTH-1:0] in1, +     input [WIDTH-1:0] in2, +     input strobe_in, +     output reg [WIDTH-1:0] sum, +     output reg strobe_out); + +   wire [WIDTH-1:0] sum_int; +    +   add2_and_clip #(.WIDTH(WIDTH)) add2_and_clip (.in1(in1),.in2(in2),.sum(sum_int)); + +   always @(posedge clk) +     if(rst) +       sum <= 0; +     else if(strobe_in) +       sum <= sum_int; + +   always @(posedge clk) +     strobe_out <= strobe_in; +    +endmodule // add2_and_clip_reg diff --git a/fpga/usrp2/sdr_lib/add2_and_round.v b/fpga/usrp2/sdr_lib/add2_and_round.v new file mode 100644 index 000000000..7c347527c --- /dev/null +++ b/fpga/usrp2/sdr_lib/add2_and_round.v @@ -0,0 +1,28 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +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/fpga/usrp2/sdr_lib/add2_and_round_reg.v b/fpga/usrp2/sdr_lib/add2_and_round_reg.v new file mode 100644 index 000000000..5c783bda3 --- /dev/null +++ b/fpga/usrp2/sdr_lib/add2_and_round_reg.v @@ -0,0 +1,33 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +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/fpga/usrp2/sdr_lib/add2_reg.v b/fpga/usrp2/sdr_lib/add2_reg.v new file mode 100644 index 000000000..58d822a61 --- /dev/null +++ b/fpga/usrp2/sdr_lib/add2_reg.v @@ -0,0 +1,34 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +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/fpga/usrp2/sdr_lib/cic_dec_shifter.v b/fpga/usrp2/sdr_lib/cic_dec_shifter.v new file mode 100644 index 000000000..aa5ac895b --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/cic_decim.v b/fpga/usrp2/sdr_lib/cic_decim.v new file mode 100755 index 000000000..e6b6e9590 --- /dev/null +++ b/fpga/usrp2/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(~enable) +       for(i=0;i<N;i=i+1) +	 integrator[i] <= 0; +     else if (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(~enable) +       begin +	  sampler <= 0; +	  for(i=0;i<N;i=i+1) +	    begin +	       pipeline[i] <= 0; +	       differentiator[i] <= 0; +	    end +       end +     else if (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/fpga/usrp2/sdr_lib/cic_int_shifter.v b/fpga/usrp2/sdr_lib/cic_int_shifter.v new file mode 100644 index 000000000..18587fa8b --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/cic_interp.v b/fpga/usrp2/sdr_lib/cic_interp.v new file mode 100755 index 000000000..9b6928aa1 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/cic_strober.v b/fpga/usrp2/sdr_lib/cic_strober.v new file mode 100644 index 000000000..40d76bdd9 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/clip.v b/fpga/usrp2/sdr_lib/clip.v new file mode 100644 index 000000000..3e6b3a2e2 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/clip_and_round.v b/fpga/usrp2/sdr_lib/clip_and_round.v new file mode 100644 index 000000000..4546283a3 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/clip_and_round_reg.v b/fpga/usrp2/sdr_lib/clip_and_round_reg.v new file mode 100644 index 000000000..66fb155fb --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/clip_reg.v b/fpga/usrp2/sdr_lib/clip_reg.v new file mode 100644 index 000000000..d5e98d982 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/cordic.v b/fpga/usrp2/sdr_lib/cordic.v new file mode 100755 index 000000000..b73e7acf1 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/cordic_stage.v b/fpga/usrp2/sdr_lib/cordic_stage.v new file mode 100755 index 000000000..641ff9108 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/cordic_z24.v b/fpga/usrp2/sdr_lib/cordic_z24.v new file mode 100644 index 000000000..97b7beaf7 --- /dev/null +++ b/fpga/usrp2/sdr_lib/cordic_z24.v @@ -0,0 +1,126 @@ +// -*- 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_z24(clock, reset, enable, xi, yi, zi, xo, yo, zo ); +   parameter bitwidth = 16; +   parameter stages = 19; +   localparam zwidth = 24; +    +   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,x13,x14,x15,x16,x17,x18,x19,x20; +   wire [bitwidth+1:0] 	 y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12,y13,y14,y15,y16,y17,y18,y19,y20; +   wire [zwidth-2:0] 	 z1,z2,z3,z4,z5,z6,z7,z8,z9,z10,z11,z12,z13,z14,z15,z16,z17,z18,z19,z20; +    +   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 + +   // constants for 24 bit wide phase +   localparam 	       c00 = 23'd2097152; +   localparam 	       c01 = 23'd1238021; +   localparam 	       c02 = 23'd654136; +   localparam 	       c03 = 23'd332050; +   localparam 	       c04 = 23'd166669; +   localparam 	       c05 = 23'd83416; +   localparam 	       c06 = 23'd41718; +   localparam 	       c07 = 23'd20860; +   localparam 	       c08 = 23'd10430; +   localparam 	       c09 = 23'd5215; +   localparam 	       c10 = 23'd2608; +   localparam 	       c11 = 23'd1304; +   localparam 	       c12 = 23'd652; +   localparam 	       c13 = 23'd326; +   localparam 	       c14 = 23'd163; +   localparam 	       c15 = 23'd81; +   localparam 	       c16 = 23'd41; +   localparam 	       c17 = 23'd20; +   localparam 	       c18 = 23'd10; +   localparam 	       c19 = 23'd5; +   localparam 	       c20 = 23'd3; +   localparam 	       c21 = 23'd1; +   localparam 	       c22 = 23'd1; +   localparam 	       c23 = 23'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 +   // 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); +   cordic_stage #(bitwidth+2,zwidth-1,12) cordic_stage12 (clock,reset,enable,x12,y12,z12,c12,x13,y13,z13); +   cordic_stage #(bitwidth+2,zwidth-1,13) cordic_stage13 (clock,reset,enable,x13,y13,z13,c13,x14,y14,z14); +   cordic_stage #(bitwidth+2,zwidth-1,14) cordic_stage14 (clock,reset,enable,x14,y14,z14,c14,x15,y15,z15); +   cordic_stage #(bitwidth+2,zwidth-1,15) cordic_stage15 (clock,reset,enable,x15,y15,z15,c15,x16,y16,z16); +   cordic_stage #(bitwidth+2,zwidth-1,16) cordic_stage16 (clock,reset,enable,x16,y16,z16,c16,x17,y17,z17); +   cordic_stage #(bitwidth+2,zwidth-1,17) cordic_stage17 (clock,reset,enable,x17,y17,z17,c17,x18,y18,z18); +   cordic_stage #(bitwidth+2,zwidth-1,18) cordic_stage18 (clock,reset,enable,x18,y18,z18,c18,x19,y19,z19); +   cordic_stage #(bitwidth+2,zwidth-1,19) cordic_stage19 (clock,reset,enable,x19,y19,z19,c19,x20,y20,z20); + +   assign xo = x20[bitwidth:1];   +   assign yo = y20[bitwidth:1]; +   assign zo = z20;		   +   //assign xo = x20[bitwidth+1:2];  // CORDIC gain is ~1.6, plus gain from rotating vectors +   //assign yo = y20[bitwidth+1:2]; + +endmodule // cordic + diff --git a/fpga/usrp2/sdr_lib/ddc.v b/fpga/usrp2/sdr_lib/ddc.v new file mode 100755 index 000000000..0d4da9bbc --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/dsp_core_rx.v b/fpga/usrp2/sdr_lib/dsp_core_rx.v new file mode 100644 index 000000000..639744de7 --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_core_rx.v @@ -0,0 +1,142 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +module dsp_core_rx +  #(parameter BASE = 160) +  (input clk, input rst, +   input set_stb, input [7:0] set_addr, input [31:0] set_data, + +   input [23:0] adc_i, input adc_ovf_i, +   input [23:0] adc_q, input adc_ovf_q, +    +   output [31:0] sample, +   input run, +   output strobe, +   output [31:0] debug +   ); + +   wire [31:0] phase_inc; +   reg [31:0]  phase; + +   wire [24:0] i_cordic, q_cordic; +   wire [23:0] i_cordic_clip, q_cordic_clip; +   wire [23:0] i_cic, q_cic; +   wire [23:0] i_hb1, q_hb1; +   wire [23:0] i_hb2, q_hb2; +    +   wire        strobe_cic, strobe_hb1, strobe_hb2; +   wire        enable_hb1, enable_hb2; +   wire [7:0]  cic_decim_rate; + +   reg [23:0]  adc_i_mux, adc_q_mux; +   wire        realmode; +   wire        swap_iq; +    +   setting_reg #(.my_addr(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(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(BASE+2), .width(10)) sr_2 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({enable_hb1, enable_hb2, cic_decim_rate}),.changed()); + +   setting_reg #(.my_addr(BASE+3), .width(2)) sr_3 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({realmode,swap_iq}),.changed()); + +   // MUX so we can do realmode signals on either input +    +   always @(posedge clk) +     if(swap_iq) +       begin +	  adc_i_mux <= adc_q; +	  adc_q_mux <= realmode ? 24'd0 : adc_i; +       end +     else +       begin +	  adc_i_mux <= adc_i; +	  adc_q_mux <= realmode ? 24'd0 : adc_q; +       end + +   // NCO +   always @(posedge clk) +     if(rst) +       phase <= 0; +     else if(~run) +       phase <= 0; +     else +       phase <= phase + phase_inc; + +   // CORDIC  24-bit I/O +   cordic_z24 #(.bitwidth(25)) +     cordic(.clock(clk), .reset(rst), .enable(run), +	    .xi({adc_i_mux[23],adc_i_mux}),. yi({adc_q_mux[23],adc_q_mux}), .zi(phase[31:8]), +	    .xo(i_cordic),.yo(q_cordic),.zo() ); + +   clip_reg #(.bits_in(25), .bits_out(24)) clip_i (.clk(clk), .in(i_cordic), .out(i_cordic_clip)); +   clip_reg #(.bits_in(25), .bits_out(24)) clip_q (.clk(clk), .in(q_cordic), .out(q_cordic_clip)); + +   // CIC decimator  24 bit I/O +   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_clip),.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_clip),.signal_out(q_cic)); + +   // First (small) halfband  24 bit I/O +   small_hb_dec #(.WIDTH(24)) small_hb_i +     (.clk(clk),.rst(rst),.bypass(~enable_hb1),.run(run), +      .stb_in(strobe_cic),.data_in(i_cic),.stb_out(strobe_hb1),.data_out(i_hb1)); +    +   small_hb_dec #(.WIDTH(24)) small_hb_q +     (.clk(clk),.rst(rst),.bypass(~enable_hb1),.run(run), +      .stb_in(strobe_cic),.data_in(q_cic),.stb_out(),.data_out(q_hb1)); + +   // Second (large) halfband  24 bit I/O +   wire [8:0]  cpi_hb = enable_hb1 ? {cic_decim_rate,1'b0} : {1'b0,cic_decim_rate}; +   hb_dec #(.WIDTH(24)) hb_i +     (.clk(clk),.rst(rst),.bypass(~enable_hb2),.run(run),.cpi(cpi_hb), +      .stb_in(strobe_hb1),.data_in(i_hb1),.stb_out(strobe_hb2),.data_out(i_hb2)); + +   hb_dec #(.WIDTH(24)) hb_q +     (.clk(clk),.rst(rst),.bypass(~enable_hb2),.run(run),.cpi(cpi_hb), +      .stb_in(strobe_hb1),.data_in(q_hb1),.stb_out(),.data_out(q_hb2)); + +   // Round final answer to 16 bits +   round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_i +     (.clk(clk),.reset(rst), .in(i_hb2),.strobe_in(strobe_hb2), .out(sample[31:16]), .strobe_out(strobe)); + +   round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_q +     (.clk(clk),.reset(rst), .in(q_hb2),.strobe_in(strobe_hb2), .out(sample[15:0]), .strobe_out()); +    +   assign      debug = {enable_hb1, enable_hb2, run, strobe, strobe_cic, strobe_hb1, strobe_hb2}; +    +endmodule // dsp_core_rx diff --git a/fpga/usrp2/sdr_lib/dsp_core_rx_old.v b/fpga/usrp2/sdr_lib/dsp_core_rx_old.v new file mode 100644 index 000000000..90d5d839f --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_core_rx_old.v @@ -0,0 +1,200 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +`define DSP_CORE_RX_BASE 160 +module dsp_core_rx_old +  (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, +    +   input [15:0] io_rx, + +   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; + +   wire [31:10] UNUSED_1; +   wire [31:4] 	UNUSED_2; +   wire [31:2] 	UNUSED_3; +    +   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({UNUSED_1, 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({UNUSED_2,muxctrl}),.changed()); + +   wire [1:0] gpio_ena; +   setting_reg #(.my_addr(`DSP_CORE_RX_BASE+9)) sr_9 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({UNUSED_3,gpio_ena}),.changed()); + +   // The TVRX connects to what is called adc_b, thus A and B are +   // swapped throughout the design. +   // +   // In the interest of expediency and keeping the s/w sane, we just remap them here. +   // The I & Q fields are mapped the same: +   // 0 -> "the real A" (as determined by the TVRX) +   // 1 -> "the real B" +   // 2 -> const zero +    +   always @(posedge clk) +     case(muxctrl[1:0])		// The I mapping +       0: adc_i <= adc_b_ofs;	// "the real A" +       1: adc_i <= adc_a_ofs; +       2: adc_i <= 0; +       default: adc_i <= 0; +     endcase // case(muxctrl[1:0]) +           +   always @(posedge clk) +     case(muxctrl[3:2])		// The Q mapping +       0: adc_q <= adc_b_ofs;	// "the real A" +       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 <= 0; +     else +       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_z24 #(.bitwidth(24)) +     cordic(.clock(clk), .reset(rst), .enable(run), +	    .xi(prod_i[23:0]),. yi(prod_q[23:0]), .zi(phase[31:8]), +	    .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),.run(run), +      .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),.run(run), +      .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),.run(run),.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),.run(run),.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)); + +   // Streaming GPIO +   // +   // io_rx[15] => I channel LSB if gpio_ena[0] high +   // io_rx[14] => Q channel LSB if gpio_ena[1] high + +   reg [31:0] sample_reg; +   always @(posedge clk) +     begin +	sample_reg[31:17] <= i_out[15:1]; +	sample_reg[15:1]  <= q_out[15:1]; +	sample_reg[16]    <= gpio_ena[0] ? io_rx[15] : i_out[0];  +	sample_reg[0]     <= gpio_ena[1] ? io_rx[14] : q_out[0]; +     end +    +   assign      sample = sample_reg; +   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/fpga/usrp2/sdr_lib/dsp_core_rx_tb.v b/fpga/usrp2/sdr_lib/dsp_core_rx_tb.v new file mode 100644 index 000000000..271db8cef --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_core_rx_tb.v @@ -0,0 +1,73 @@ + +`timescale 1ns/1ns +module dsp_core_rx_tb(); +    +   reg clk, rst; + +   initial rst = 1; +   initial #1000 rst = 0; +   initial clk = 0; +   always #5 clk = ~clk; +    +   initial $dumpfile("dsp_core_rx_tb.vcd"); +   initial $dumpvars(0,dsp_core_rx_tb); +    +   reg signed [23:0] adc_in; +   wire signed [15:0] adc_out_i, adc_out_q; + +   always @(posedge clk) +     begin +	$display(adc_in); +	$display(adc_out_i); +	$display(adc_out_q); +     end	 + +   reg run; +   reg set_stb; +   reg [7:0] set_addr; +   reg [31:0] set_data; +    +   dsp_core_rx #(.BASE(0)) dsp_core_rx +     (.clk(clk),.rst(rst), +      .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +      .adc_i(adc_in), .adc_ovf_i(0), +      .adc_q(0), .adc_ovf_q(0), +      .sample({adc_out_i,adc_out_q}), +      .run(run), .strobe(), .debug()); + +   initial +     begin +	run <= 0; +	@(negedge rst); +	@(posedge clk); +	set_addr <= 1; +	set_data <= {16'd64,16'd64};  // set gains +	set_stb <= 1; +	@(posedge clk); +	set_addr <= 2; +	set_data <= {16'd0,8'd3,8'd1}; // set decim +	set_stb <= 1; +	@(posedge clk); +	set_addr <= 0; +	//set_data <= {32'h0000_0000}; +	set_data <= {32'h01CA_C083}; // 700 kHz +	set_stb <= 1; +	@(posedge clk); +	set_stb <= 0; +	@(posedge clk); +	run <= 1; +     end + +   always @(posedge clk) +     //adc_in <= 24'd1000000; +     adc_in <= 24'h80_0000; +    +   /* +   always @(posedge clk) +     if(rst) +       adc_in <= 0; +     else +       adc_in <= adc_in + 4; +   //adc_in <= (($random % 473) + 23)/4; +*/    +endmodule // dsp_core_rx_tb diff --git a/fpga/usrp2/sdr_lib/dsp_core_rx_udp.v b/fpga/usrp2/sdr_lib/dsp_core_rx_udp.v new file mode 100644 index 000000000..08dab37e6 --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_core_rx_udp.v @@ -0,0 +1,200 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +module dsp_core_rx +  #(parameter BASE = 160) +  (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, +    +   input [15:0] io_rx, + +   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; + +   wire [31:10] UNUSED_1; +   wire [31:4] 	UNUSED_2; +   wire [31:2] 	UNUSED_3; +    +   setting_reg #(.my_addr(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(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(BASE+2)) sr_2 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({UNUSED_1, enable_hb1, enable_hb2, cic_decim_rate}),.changed()); + +   rx_dcoffset #(.WIDTH(14),.ADDR(BASE+3)) 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(BASE+4)) 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(BASE+5)) sr_8 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({UNUSED_2,muxctrl}),.changed()); + +   wire [1:0] gpio_ena; +   setting_reg #(.my_addr(BASE+6)) sr_9 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({UNUSED_3,gpio_ena}),.changed()); + +   // The TVRX connects to what is called adc_b, thus A and B are +   // swapped throughout the design. +   // +   // In the interest of expediency and keeping the s/w sane, we just remap them here. +   // The I & Q fields are mapped the same: +   // 0 -> "the real A" (as determined by the TVRX) +   // 1 -> "the real B" +   // 2 -> const zero +    +   always @(posedge clk) +     case(muxctrl[1:0])		// The I mapping +       0: adc_i <= adc_b_ofs;	// "the real A" +       1: adc_i <= adc_a_ofs; +       2: adc_i <= 0; +       default: adc_i <= 0; +     endcase // case(muxctrl[1:0]) +           +   always @(posedge clk) +     case(muxctrl[3:2])		// The Q mapping +       0: adc_q <= adc_b_ofs;	// "the real A" +       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 <= 0; +     else +       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_z24 #(.bitwidth(24)) +     cordic(.clock(clk), .reset(rst), .enable(run), +	    .xi(prod_i[23:0]),. yi(prod_q[23:0]), .zi(phase[31:8]), +	    .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),.run(run), +      .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),.run(run), +      .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),.run(run),.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),.run(run),.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)); + +   // Streaming GPIO +   // +   // io_rx[15] => I channel LSB if gpio_ena[0] high +   // io_rx[14] => Q channel LSB if gpio_ena[1] high + +   reg [31:0] sample_reg; +   always @(posedge clk) +     begin +	sample_reg[31:17] <= i_out[15:1]; +	sample_reg[15:1]  <= q_out[15:1]; +	sample_reg[16]    <= gpio_ena[0] ? io_rx[15] : i_out[0];  +	sample_reg[0]     <= gpio_ena[1] ? io_rx[14] : q_out[0]; +     end +    +   assign      sample = sample_reg; +   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/fpga/usrp2/sdr_lib/dsp_core_tx.v b/fpga/usrp2/sdr_lib/dsp_core_tx.v new file mode 100644 index 000000000..4e0163e0a --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_core_tx.v @@ -0,0 +1,153 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +module dsp_core_tx +  #(parameter BASE=0) +  (input clk, input rst, +   input set_stb, input [7:0] set_addr, input [31:0] set_data, + +   output [23:0] tx_i, output [23:0] tx_q, + +   // 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 [3:0]  dacmux_a, dacmux_b; +   wire        enable_hb1, enable_hb2; +   wire        rate_change; +    +   setting_reg #(.my_addr(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(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(BASE+2), .width(10)) sr_2 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({enable_hb1, enable_hb2, interp_rate}),.changed(rate_change)); + +   // 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_change),.rate(interp_rate), +		 .strobe_fast(1),.strobe_slow(strobe_cic_pre) ); +   cic_strober #(.WIDTH(2)) +     hb2_strober(.clock(clk),.reset(rst),.enable(run & ~rate_change),.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_change),.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; + +   // NCO +   always @(posedge clk) +     if(rst) +       phase <= 0; +     else if(~run) +       phase <= 0; +     else +       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_change),.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_change),.rate(interp_rate), +		  .strobe_in(strobe_cic),.strobe_out(1), +		  .signal_in(hb2_q),.signal_out(q_interp)); + +   assign      strobe = strobe_hb1; + +   localparam  cwidth = 24;  // was 18 +   localparam  zwidth = 24;  // was 16 + +   wire [cwidth-1:0] da_c, db_c; +    +   cordic_z24 #(.bitwidth(cwidth)) +     cordic(.clock(clk), .reset(rst), .enable(run), +	    .xi({i_interp,{(cwidth-18){1'b0}}}),.yi({q_interp,{(cwidth-18){1'b0}}}), +	    .zi(phase[31:32-zwidth]), +	    .xo(da_c),.yo(db_c),.zo() ); +    +   MULT18X18S MULT18X18S_inst  +     (.P(prod_i),    // 36-bit multiplier output +      .A(da_c[cwidth-1:cwidth-18]),    // 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_c[cwidth-1:cwidth-18]),    // 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 +      ); + +   assign tx_i = prod_i[28:5]; +   assign tx_q = prod_q[28:5]; +    +   assign      debug = {strobe_cic, strobe_hb1, strobe_hb2,run}; + +endmodule // dsp_core diff --git a/fpga/usrp2/sdr_lib/duc.v b/fpga/usrp2/sdr_lib/duc.v new file mode 100755 index 000000000..6dac95b49 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/dummy_rx.v b/fpga/usrp2/sdr_lib/dummy_rx.v new file mode 100644 index 000000000..b22d5f896 --- /dev/null +++ b/fpga/usrp2/sdr_lib/dummy_rx.v @@ -0,0 +1,79 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +`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/fpga/usrp2/sdr_lib/gen_cordic_consts.py b/fpga/usrp2/sdr_lib/gen_cordic_consts.py new file mode 100755 index 000000000..261e8c223 --- /dev/null +++ b/fpga/usrp2/sdr_lib/gen_cordic_consts.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +import math + +zwidth = 24 + +for i in range(24): +    c = math.atan (1.0/(2**i)) / (2 * math.pi) * (1 << zwidth) +    print "localparam c%02d = %d'd%d;" % (i, zwidth, round (c)) +     diff --git a/fpga/usrp2/sdr_lib/halfband_ideal.v b/fpga/usrp2/sdr_lib/halfband_ideal.v new file mode 100644 index 000000000..e0b04cf86 --- /dev/null +++ b/fpga/usrp2/sdr_lib/halfband_ideal.v @@ -0,0 +1,101 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +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/fpga/usrp2/sdr_lib/halfband_tb.v b/fpga/usrp2/sdr_lib/halfband_tb.v new file mode 100644 index 000000000..80f46fe36 --- /dev/null +++ b/fpga/usrp2/sdr_lib/halfband_tb.v @@ -0,0 +1,137 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +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/fpga/usrp2/sdr_lib/hb/acc.v b/fpga/usrp2/sdr_lib/hb/acc.v new file mode 100644 index 000000000..d7be895c6 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/acc.v @@ -0,0 +1,39 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/coeff_ram.v b/fpga/usrp2/sdr_lib/hb/coeff_ram.v new file mode 100644 index 000000000..525c22abc --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/coeff_ram.v @@ -0,0 +1,43 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/coeff_rom.v b/fpga/usrp2/sdr_lib/hb/coeff_rom.v new file mode 100644 index 000000000..a43c8391a --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/coeff_rom.v @@ -0,0 +1,36 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/halfband_decim.v b/fpga/usrp2/sdr_lib/hb/halfband_decim.v new file mode 100644 index 000000000..dff4d902c --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/hb/halfband_interp.v b/fpga/usrp2/sdr_lib/hb/halfband_interp.v new file mode 100644 index 000000000..83bdc9fad --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/halfband_interp.v @@ -0,0 +1,138 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/hbd_tb/HBD b/fpga/usrp2/sdr_lib/hb/hbd_tb/HBD new file mode 100644 index 000000000..574fbba91 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/hb/hbd_tb/really_golden b/fpga/usrp2/sdr_lib/hb/hbd_tb/really_golden new file mode 100644 index 000000000..2d24a9e14 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/hb/hbd_tb/regression b/fpga/usrp2/sdr_lib/hb/hbd_tb/regression new file mode 100644 index 000000000..fc279c2f2 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/hb/hbd_tb/run_hbd b/fpga/usrp2/sdr_lib/hb/hbd_tb/run_hbd new file mode 100755 index 000000000..b8aec7574 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/hb/hbd_tb/test_hbd.v b/fpga/usrp2/sdr_lib/hb/hbd_tb/test_hbd.v new file mode 100644 index 000000000..450f90e66 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/hbd_tb/test_hbd.v @@ -0,0 +1,92 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/mac.v b/fpga/usrp2/sdr_lib/hb/mac.v new file mode 100644 index 000000000..8058a6db4 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/mac.v @@ -0,0 +1,75 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/mult.v b/fpga/usrp2/sdr_lib/hb/mult.v new file mode 100644 index 000000000..a50ae69e2 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/mult.v @@ -0,0 +1,33 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/ram16_2port.v b/fpga/usrp2/sdr_lib/hb/ram16_2port.v new file mode 100644 index 000000000..631cf5a41 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/ram16_2port.v @@ -0,0 +1,39 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/ram16_2sum.v b/fpga/usrp2/sdr_lib/hb/ram16_2sum.v new file mode 100644 index 000000000..f9ec1837e --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/ram16_2sum.v @@ -0,0 +1,44 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb/ram32_2sum.v b/fpga/usrp2/sdr_lib/hb/ram32_2sum.v new file mode 100644 index 000000000..f7032835e --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb/ram32_2sum.v @@ -0,0 +1,39 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/hb_dec.v b/fpga/usrp2/sdr_lib/hb_dec.v new file mode 100644 index 000000000..8d21c21c0 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb_dec.v @@ -0,0 +1,189 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +// 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 WIDTH=24) +    (input clk, +     input rst, +     input bypass, +     input run, +     input [8:0] cpi,  // Clocks per input -- equal to the decimation ratio ahead of this block +     input stb_in, +     input [WIDTH-1:0] data_in, +     output reg stb_out, +     output reg [WIDTH-1:0] data_out); + +   localparam INTWIDTH = 17; +   localparam ACCWIDTH = WIDTH + 3; +    +   // Round off inputs to 17 bits because of 18 bit multipliers +   wire [INTWIDTH-1:0] 	     data_rnd; +   wire 		     stb_rnd; +    +   round_sd #(.WIDTH_IN(WIDTH),.WIDTH_OUT(INTWIDTH)) round_in +     (.clk(clk),.reset(rst),.in(data_in),.strobe_in(stb_in),.out(data_rnd),.strobe_out(stb_rnd)); +    +   // 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 | ~run) +       odd <= 0; +     else if(stb_rnd) +       odd <= ~odd; + +   assign 		write_odd = stb_rnd & odd; +   assign 		write_even = stb_rnd & ~odd; + +   always @(posedge clk) +     if(rst | ~run) +       phase <= 0; +     else if(stb_rnd & 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_rnd & 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 [INTWIDTH-1:0] data_odd_a, data_odd_b, data_odd_c, data_odd_d; +   reg [INTWIDTH:0]    sum1, sum2;    // these are 18-bit inputs to mult +   reg [WIDTH:0]       final_sum; +   wire [WIDTH-1:0]    final_sum_clip; +   reg [17: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(INTWIDTH)) srl_odd_a +     (.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_a),.out(data_odd_a)); +   srl #(.WIDTH(INTWIDTH)) srl_odd_b +     (.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_b),.out(data_odd_b)); +   srl #(.WIDTH(INTWIDTH)) srl_odd_c +     (.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_c),.out(data_odd_c)); +   srl #(.WIDTH(INTWIDTH)) srl_odd_d +     (.clk(clk),.write(write_odd),.in(data_rnd),.addr(addr_odd_d),.out(data_odd_d)); + +   always @(posedge clk) sum1 <= {data_odd_a[INTWIDTH-1],data_odd_a} + {data_odd_b[INTWIDTH-1],data_odd_b}; +   always @(posedge clk) sum2 <= {data_odd_c[INTWIDTH-1],data_odd_c} + {data_odd_d[INTWIDTH-1],data_odd_d}; +    +   wire [INTWIDTH-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(INTWIDTH)) srl_even +     (.clk(clk),.write(write_even),.in(data_rnd),.addr(addr_even),.out(data_even)); + +   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) ); + +   reg [35:0] 	     sum_of_prod; +   always @(posedge clk) sum_of_prod <= prod1 + prod2;   // Can't overflow +    +   wire [ACCWIDTH-1:0] 	acc_out; +   acc #(.IWIDTH(ACCWIDTH-2),.OWIDTH(ACCWIDTH)) +     acc (.clk(clk),.clear(clear),.acc(do_acc),.in(sum_of_prod[35:38-ACCWIDTH]),.out(acc_out)); + +   wire [ACCWIDTH-1:0] 	data_even_signext; + +   localparam SHIFT_FACTOR = 6; +    +   sign_extend #(.bits_in(INTWIDTH),.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; + +   always @(posedge clk) final_sum <= acc_out + data_even_signext; +    +   clip #(.bits_in(WIDTH+1), .bits_out(WIDTH)) clip (.in(final_sum), .out(final_sum_clip)); +    +   // Output MUX to allow for bypass +   wire 		selected_stb = bypass ? stb_in : stb_out_pre[8]; +    +   always @(posedge clk) +     begin +	stb_out  <= selected_stb; +	if(selected_stb) +	  data_out <= bypass ? data_in : final_sum_clip; +     end +    +endmodule // hb_dec diff --git a/fpga/usrp2/sdr_lib/hb_dec_tb.v b/fpga/usrp2/sdr_lib/hb_dec_tb.v new file mode 100644 index 000000000..153cfba76 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb_dec_tb.v @@ -0,0 +1,157 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +module hb_dec_tb( ) ; +    +    // Parameters for instantiation +    parameter               clocks  = 9'd12 ; // 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  [23:0]  data_in ; +    wire                    strobe_out ; +    wire    signed  [23: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 #(.WIDTH(24)) uut +     (.clk(clock),.rst(reset),.bypass(0),.run(1),.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/fpga/usrp2/sdr_lib/hb_interp.v b/fpga/usrp2/sdr_lib/hb_interp.v new file mode 100644 index 000000000..deb4fe056 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb_interp.v @@ -0,0 +1,174 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +// 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/fpga/usrp2/sdr_lib/hb_interp_tb.v b/fpga/usrp2/sdr_lib/hb_interp_tb.v new file mode 100644 index 000000000..239412155 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb_interp_tb.v @@ -0,0 +1,149 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +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/fpga/usrp2/sdr_lib/hb_tb.v b/fpga/usrp2/sdr_lib/hb_tb.v new file mode 100644 index 000000000..3260ac738 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb_tb.v @@ -0,0 +1,172 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +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/fpga/usrp2/sdr_lib/input.dat b/fpga/usrp2/sdr_lib/input.dat new file mode 100644 index 000000000..85b5887e8 --- /dev/null +++ b/fpga/usrp2/sdr_lib/input.dat @@ -0,0 +1,171 @@ +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +8388607 +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 +8388607 +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 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +8388607 +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/fpga/usrp2/sdr_lib/integrate.v b/fpga/usrp2/sdr_lib/integrate.v new file mode 100644 index 000000000..ce674d470 --- /dev/null +++ b/fpga/usrp2/sdr_lib/integrate.v @@ -0,0 +1,55 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +module integrate +  #(parameter INPUTW = 16, +    parameter ACCUMW = 32, +    parameter OUTPUTW = 16) + +   (input clk_i, +    input rst_i, +    input ena_i, + +    input dump_i, +    input [INPUTW-1:0] data_i, + +    output reg stb_o, +    output reg [OUTPUTW-1:0] integ_o +   ); +    +   wire [ACCUMW-1:0] data_ext = {{ACCUMW-INPUTW{data_i[INPUTW-1]}},data_i}; +   reg  [ACCUMW-1:0] accum; + +   always @(posedge clk_i) +     if (rst_i | ~ena_i) +       begin +	  accum <= 0; +	  integ_o <= 0; +       end +     else +       if (dump_i) +	 begin +	    integ_o <= accum[ACCUMW-1:ACCUMW-OUTPUTW]; +	    accum <= data_ext; +	 end +       else +	 accum <= accum + data_ext; + +   always @(posedge clk_i) +     stb_o <= dump_i; +    +endmodule // integrate diff --git a/fpga/usrp2/sdr_lib/med_hb_int.v b/fpga/usrp2/sdr_lib/med_hb_int.v new file mode 100644 index 000000000..f619dc81b --- /dev/null +++ b/fpga/usrp2/sdr_lib/med_hb_int.v @@ -0,0 +1,112 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +// Medium halfband decimator (intended to be followed by another stage) +// Implements impulse responses of the form [A 0 B 0 C 0 D 0.5 D 0 C 0 B 0 A] +// +// These taps designed by halfgen_test: +//   2 * 131072 * halfgen_test(.8/8,4,1) +// -597, 0, 4283, 0, -17516, 0, 79365, 131072, 79365, 0, -17516, 0, 4283, 0, -597 + + +module med_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); + +   localparam coeff_a = -597; +   localparam coeff_b = 4283; +   localparam coeff_c = -17516; +   localparam coeff_d = 79365; +    +   reg 	      phase; +   reg [WIDTH-1:0] d1, d2, d3, d4, d5, d6, d7, d8; +    +   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; +	  d7 <= d6; +	  d8 <= d7; +       end + +   wire [WIDTH-1:0] sum_a, sum_b, sum_c, sum_d; +   add2_and_round_reg #(.WIDTH(WIDTH)) add_a (.clk(clk),.in1(d1),.in2(d8),.sum(sum_a)); +   add2_and_round_reg #(.WIDTH(WIDTH)) add_b (.clk(clk),.in1(d2),.in2(d7),.sum(sum_b)); +   add2_and_round_reg #(.WIDTH(WIDTH)) add_c (.clk(clk),.in1(d3),.in2(d6),.sum(sum_c)); +   add2_and_round_reg #(.WIDTH(WIDTH)) add_d (.clk(clk),.in1(d4),.in2(d5),.sum(sum_d)); + +   MULT18X18S mult1(.C(clk), .CE(1), .R(rst), .P(prod1), .A(stbin_d[1] ? coeff_a : coeff_b),  +		    .B(stbin_d[1] ? sum_a : sum_b) ); +   MULT18X18S mult2(.C(clk), .CE(1), .R(rst), .P(prod2), .A(stbin_d[1] ? coeff_c : coeff_d),  +		    .B(stbin_d[1] ? sum_c : sum_d) ); + +   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/fpga/usrp2/sdr_lib/output.dat b/fpga/usrp2/sdr_lib/output.dat new file mode 100644 index 000000000..15db3ced4 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/round.v b/fpga/usrp2/sdr_lib/round.v new file mode 100644 index 000000000..7a137d702 --- /dev/null +++ b/fpga/usrp2/sdr_lib/round.v @@ -0,0 +1,35 @@ +// -*- 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 round to zero (unbiased truncation) + +module round +  #(parameter bits_in=0, +    parameter bits_out=0) +    (input [bits_in-1:0] in, +     output [bits_out-1:0] out, +     output [bits_in-bits_out:0] err); + +   assign out = in[bits_in-1:bits_in-bits_out] + (in[bits_in-1] & |in[bits_in-bits_out-1:0]); +   assign err = in - {out,{(bits_in-bits_out){1'b0}}}; +    +endmodule // round diff --git a/fpga/usrp2/sdr_lib/round_reg.v b/fpga/usrp2/sdr_lib/round_reg.v new file mode 100644 index 000000000..6f2e974d7 --- /dev/null +++ b/fpga/usrp2/sdr_lib/round_reg.v @@ -0,0 +1,44 @@ +// -*- 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, +     output reg [bits_in-bits_out:0] err); + +   wire [bits_out-1:0] temp; +   wire [bits_in-bits_out:0] err_temp; +    +   round #(.bits_in(bits_in),.bits_out(bits_out)) round (.in(in),.out(temp), .err(err_temp)); +    +   always @(posedge clk) +     out <= temp; + +   always @(posedge clk) +     err <= err_temp; +    +endmodule // round_reg diff --git a/fpga/usrp2/sdr_lib/round_sd.v b/fpga/usrp2/sdr_lib/round_sd.v new file mode 100644 index 000000000..aeeb3502f --- /dev/null +++ b/fpga/usrp2/sdr_lib/round_sd.v @@ -0,0 +1,22 @@ + + +module round_sd +  #(parameter WIDTH_IN=18, +    parameter WIDTH_OUT=16) +   (input clk, input reset, +    input [WIDTH_IN-1:0] in, input strobe_in, +    output [WIDTH_OUT-1:0] out, output strobe_out); + +   localparam ERR_WIDTH = WIDTH_IN - WIDTH_OUT + 1; + +   wire [ERR_WIDTH-1:0]  err; +   wire [WIDTH_IN-1:0] 	 err_ext, sum; +   +   sign_extend #(.bits_in(ERR_WIDTH),.bits_out(WIDTH_IN)) ext_err (.in(err), .out(err_ext)); +    +   add2_and_clip_reg #(.WIDTH(WIDTH_IN)) add2_and_clip_reg +     (.clk(clk), .rst(reset), .in1(in), .in2(err_ext), .strobe_in(strobe_in), .sum(sum), .strobe_out(strobe_out)); +    +   round #(.bits_in(WIDTH_IN),.bits_out(WIDTH_OUT)) round_sum (.in(sum), .out(out), .err(err)); +    +endmodule // round_sd diff --git a/fpga/usrp2/sdr_lib/round_sd_tb.v b/fpga/usrp2/sdr_lib/round_sd_tb.v new file mode 100644 index 000000000..1e8e9a323 --- /dev/null +++ b/fpga/usrp2/sdr_lib/round_sd_tb.v @@ -0,0 +1,58 @@ + +module round_sd_tb(); +    +   reg clk, rst; + +   initial rst = 1; +   initial #1000 rst = 0; +   initial clk = 0; +   always #5 clk = ~clk; +    +   initial $dumpfile("round_sd_tb.vcd"); +   initial $dumpvars(0,round_sd_tb); + +   localparam WIDTH_IN = 8; +   localparam WIDTH_OUT = 5; +    +   reg [WIDTH_IN-1:0] adc_in, adc_in_del; +   wire [WIDTH_OUT-1:0] adc_out; + +   integer 		factor = 1<<(WIDTH_IN-WIDTH_OUT); +    +   always @(posedge clk) +     if(~rst) +	begin   +	   if(adc_in_del[WIDTH_IN-1]) +	     $write("-%d\t",-adc_in_del); +	   else +	     $write("%d\t",adc_in_del); +	   if(adc_out[WIDTH_OUT-1]) +	     $write("-%d\t",-adc_out); +	   else +	     $write("%d\t",adc_out); +	   $write("\n"); +	    +	   //$write("%f\t",adc_in_del/factor); +	   //$write("%f\n",adc_in_del/factor-adc_out); +	end	 +    +   round_sd #(.WIDTH_IN(WIDTH_IN),.WIDTH_OUT(WIDTH_OUT))  +   round_sd(.clk(clk),.reset(rst), .in(adc_in), .strobe_in(1'b1), .out(adc_out), .strobe_out()); + +   reg [5:0] counter = 0; +    +   always @(posedge clk) +     counter <= counter+1; +    +   always @(posedge clk) +     adc_in_del <= adc_in; + +   always @(posedge clk) +     if(rst) +       adc_in <= 0; +     else if(counter == 63) +       adc_in <= adc_in + 1; +    +   initial #300000 $finish; +    +endmodule // longfifo_tb diff --git a/fpga/usrp2/sdr_lib/rssi.v b/fpga/usrp2/sdr_lib/rssi.v new file mode 100644 index 000000000..e931ff865 --- /dev/null +++ b/fpga/usrp2/sdr_lib/rssi.v @@ -0,0 +1,47 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +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/fpga/usrp2/sdr_lib/rx_control.v b/fpga/usrp2/sdr_lib/rx_control.v new file mode 100644 index 000000000..12f411ffe --- /dev/null +++ b/fpga/usrp2/sdr_lib/rx_control.v @@ -0,0 +1,197 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +`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 FIFO interface of Buffer Pool +     output [31:0] wr_dat_o, +     output [3:0] wr_flags_o, +     input wr_ready_i, +     output wr_ready_o, + +     // 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        have_space, write; +   wire [35:0] fifo_line; +    +   // Internal FIFO, size 9 is 2K, size 10 is 4K +   fifo_cascade #(.WIDTH(36),.SIZE(FIFOSIZE)) rxfifo +     (.clk(clk),.reset(rst),.clear(clear_overrun), +      .datain(fifo_line), .src_rdy_i(write), .dst_rdy_o(have_space), +      .dataout({wr_flags_o,wr_dat_o}), .src_rdy_o(wr_ready_o), .dst_rdy_i(wr_ready_i), +      .space(),.occupied(fifo_occupied) ); +   assign      fifo_full = ~have_space; +   assign      fifo_empty = ~wr_ready_o; + +   // 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(~have_space | strobe) +	       ibs_state <= IBS_OVERRUN; +	     else +	       ibs_state <= IBS_RUNNING; +	   IBS_RUNNING : +	     if(strobe) +	       if(~have_space) +		 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(~have_space) +	 endcase // case(ibs_state) +    +   assign fifo_line = (ibs_state == IBS_FIRSTLINE) ? {2'b0,1'b0,1'b1,master_time} : +		      {2'b0,((lines_left==1)|(lines_left_frame==1)),1'b0,sample}; +    +   assign write = ((ibs_state == IBS_FIRSTLINE) | strobe) & have_space;  // & (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 & have_space & (lines_left==1) & chain) ) +	  & ~empty_ctrl; +    +   assign debug_rx = { 8'd0, +		       1'd0, send_imm, chain, wr_ready_i,wr_ready_o, 2'b0, run, +		       write,have_space,wr_flags_o[1:0],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/fpga/usrp2/sdr_lib/rx_dcoffset.v b/fpga/usrp2/sdr_lib/rx_dcoffset.v new file mode 100644 index 000000000..e43461261 --- /dev/null +++ b/fpga/usrp2/sdr_lib/rx_dcoffset.v @@ -0,0 +1,57 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + + +module rx_dcoffset  +  #(parameter WIDTH=16, +    parameter ADDR=8'd0, +    parameter alpha_shift=16) +   (input clk, input rst,  +    input set_stb, input [7:0] set_addr, input [31:0] set_data, +    input [WIDTH-1:0] in, output [WIDTH-1:0] out); +    +   wire 	      set_now = set_stb & (ADDR == set_addr); +    +   reg 		      fixed;  // uses fixed offset +   wire [WIDTH-1:0]   fixed_dco; + +   localparam int_width = WIDTH + alpha_shift; +   reg [int_width-1:0] integrator; +   wire [WIDTH-1:0]    quantized; + +   always @(posedge clk) +     if(rst) +       begin +	  fixed <= 0; +	  integrator <= {int_width{1'b0}}; +       end +     else if(set_now) +       begin +	  //integrator <= {set_data[30:0],{(31-int_width){1'b0}}}; +	  fixed <= set_data[31]; +       end +     else if(~fixed) +       integrator <= integrator +  {{(alpha_shift){out[WIDTH-1]}},out}; + +   round_sd #(.WIDTH_IN(int_width),.WIDTH_OUT(WIDTH)) round_sd +     (.clk(clk), .reset(rst), .in(integrator), .strobe_in(1'b1), .out(quantized), .strobe_out()); +    +   add2_and_clip_reg #(.WIDTH(WIDTH)) add2_and_clip_reg +     (.clk(clk), .rst(rst), .in1(in), .in2(-quantized), .strobe_in(1'b1), .sum(out), .strobe_out()); + +endmodule // rx_dcoffset diff --git a/fpga/usrp2/sdr_lib/rx_dcoffset_tb.v b/fpga/usrp2/sdr_lib/rx_dcoffset_tb.v new file mode 100644 index 000000000..b4fb66ad7 --- /dev/null +++ b/fpga/usrp2/sdr_lib/rx_dcoffset_tb.v @@ -0,0 +1,54 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +`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; +   wire [13:0] adc_out; + +   always @(posedge clk) +     begin +	if(adc_in[13]) +	  $write("-%d,",-adc_in); +	else +	  $write("%d,",adc_in); +	if(adc_out[13]) +	  $write("-%d\n",-adc_out); +	else +	  $write("%d\n",adc_out); +     end	 +    +   rx_dcoffset #(.WIDTH(14),.ADDR(0), .alpha_shift(8)) +     rx_dcoffset(.clk(clk),.rst(rst),.set_stb(0),.set_addr(0),.set_data(0), +		 .in(adc_in),.out(adc_out)); + +   always @(posedge clk) +     adc_in <= (($random % 473) + 23)/4; +    +endmodule // longfifo_tb diff --git a/fpga/usrp2/sdr_lib/rx_frontend.v b/fpga/usrp2/sdr_lib/rx_frontend.v new file mode 100644 index 000000000..edfbe62df --- /dev/null +++ b/fpga/usrp2/sdr_lib/rx_frontend.v @@ -0,0 +1,78 @@ + +module rx_frontend +  #(parameter BASE = 0, +    parameter IQCOMP_EN = 0) +   (input clk, input rst, +    input set_stb, input [7:0] set_addr, input [31:0] set_data, + +    input [15:0] adc_a, input adc_ovf_a, +    input [15:0] adc_b, input adc_ovf_b, + +    output [23:0] i_out, output [23:0] q_out, +    input run, +    output [31:0] debug +    ); +    +   reg [15:0] 	  adc_i, adc_q; +   wire [17:0] 	  adc_i_ofs, adc_q_ofs; +   wire [35:0] 	  corr_i, corr_q; wire [17:0] 	  mag_corr,phase_corr; +   wire 	  swap_iq; +    +   setting_reg #(.my_addr(BASE), .width(1)) sr_8 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(swap_iq),.changed()); + +   always @(posedge clk) +     if(swap_iq) // Swap +       {adc_i,adc_q} <= {adc_b,adc_a}; +     else +       {adc_i,adc_q} <= {adc_a,adc_b}; +        +   setting_reg #(.my_addr(BASE+1),.width(18)) sr_1 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(mag_corr),.changed()); +    +   setting_reg #(.my_addr(BASE+2),.width(18)) sr_2 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(phase_corr),.changed()); + +   generate +      if(IQCOMP_EN == 1) +	begin +	   rx_dcoffset #(.WIDTH(18),.ADDR(BASE+3)) rx_dcoffset_i +	     (.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +	      .in({adc_i,2'b00}),.out(adc_i_ofs)); +	    +	   rx_dcoffset #(.WIDTH(18),.ADDR(BASE+4)) rx_dcoffset_q +	     (.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +	      .in({adc_q,2'b00}),.out(adc_q_ofs)); +	    +	   MULT18X18S mult_mag_corr +	     (.P(corr_i), .A(adc_i_ofs), .B(mag_corr), .C(clk), .CE(1), .R(rst) );  +	    +	   MULT18X18S mult_phase_corr +	     (.P(corr_q), .A(adc_i_ofs), .B(phase_corr), .C(clk), .CE(1), .R(rst) ); +	    +	   add2_and_clip_reg #(.WIDTH(24)) add_clip_i +	     (.clk(clk), .rst(rst),  +	      .in1({adc_i_ofs,6'd0}), .in2({{4{corr_i[35]}},corr_i[35:16]}), .strobe_in(1'b1),  +	      .sum(i_out), .strobe_out()); +	    +	   add2_and_clip_reg #(.WIDTH(24)) add_clip_q +	     (.clk(clk), .rst(rst),  +	      .in1({adc_q_ofs,6'd0}), .in2({{4{corr_q[35]}},corr_q[35:16]}), .strobe_in(1'b1), +	      .sum(q_out), .strobe_out()); +	end // if (IQCOMP_EN == 1) +      else +	begin +	   rx_dcoffset #(.WIDTH(24),.ADDR(BASE+3)) rx_dcoffset_i +	     (.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +	      .in({adc_i,8'b00}),.out(i_out)); +	    +	   rx_dcoffset #(.WIDTH(24),.ADDR(BASE+4)) rx_dcoffset_q +	     (.clk(clk),.rst(rst),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +	      .in({adc_q,8'b00}),.out(q_out)); +	end // else: !if(IQCOMP_EN == 1) +      endgenerate +    +endmodule // rx_frontend diff --git a/fpga/usrp2/sdr_lib/rx_frontend_tb.v b/fpga/usrp2/sdr_lib/rx_frontend_tb.v new file mode 100644 index 000000000..487b72687 --- /dev/null +++ b/fpga/usrp2/sdr_lib/rx_frontend_tb.v @@ -0,0 +1,45 @@ + +`timescale 1ns/1ns +module rx_frontend_tb(); +    +   reg clk, rst; + +   initial rst = 1; +   initial #1000 rst = 0; +   initial clk = 0; +   always #5 clk = ~clk; +    +   initial $dumpfile("rx_frontend_tb.vcd"); +   initial $dumpvars(0,rx_frontend_tb); + +   reg [15:0] adc_in; +   wire [17:0] adc_out; + +   always @(posedge clk) +     begin +	if(adc_in[13]) +	  $write("-%d,",-adc_in); +	else +	  $write("%d,",adc_in); +	if(adc_out[13]) +	  $write("-%d\n",-adc_out); +	else +	  $write("%d\n",adc_out); +     end	 +    +   rx_frontend #(.BASE(0)) rx_frontend +     (.clk(clk),.rst(rst), +      .set_stb(0),.set_addr(0),.set_data(0), +      .adc_a(adc_in), .adc_ovf_a(0), +      .adc_b(0), .adc_ovf_b(0), +      .i_out(adc_out),.q_out(), +      .run(), .debug()); + +   always @(posedge clk) +     if(rst) +       adc_in <= 0; +     else +       adc_in <= adc_in + 4; +   //adc_in <= (($random % 473) + 23)/4; +    +endmodule // rx_frontend_tb diff --git a/fpga/usrp2/sdr_lib/sign_extend.v b/fpga/usrp2/sdr_lib/sign_extend.v new file mode 100644 index 000000000..eae67faf2 --- /dev/null +++ b/fpga/usrp2/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/fpga/usrp2/sdr_lib/small_hb_dec.v b/fpga/usrp2/sdr_lib/small_hb_dec.v new file mode 100644 index 000000000..c05a48b81 --- /dev/null +++ b/fpga/usrp2/sdr_lib/small_hb_dec.v @@ -0,0 +1,136 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +// 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 run, +     input stb_in, +     input [WIDTH-1:0] data_in, +     output reg stb_out, +     output reg [WIDTH-1:0] data_out); + +   // Round off inputs to 17 bits because of 18 bit multipliers +   localparam INTWIDTH = 17; +   wire [INTWIDTH-1:0] 	data_rnd; +   wire 		stb_rnd; +    +   round_sd #(.WIDTH_IN(WIDTH),.WIDTH_OUT(INTWIDTH)) round_in +     (.clk(clk),.reset(rst),.in(data_in),.strobe_in(stb_in),.out(data_rnd),.strobe_out(stb_rnd)); +   	 +    +   reg 			stb_rnd_d1; +   reg [INTWIDTH-1:0] 	data_rnd_d1; +   always @(posedge clk) stb_rnd_d1 <= stb_rnd; +   always @(posedge clk) data_rnd_d1 <= data_rnd; +    +   wire 		go; +   reg 			phase, go_d1, go_d2, go_d3, go_d4; +   always @(posedge clk) +     if(rst | ~run) +       phase <= 0; +     else if(stb_rnd_d1) +       phase <= ~phase; +   assign 		go = stb_rnd_d1 & phase; +   always @(posedge clk)  +     if(rst | ~run) +       begin +	  go_d1 <= 0; +	  go_d2 <= 0; +	  go_d3 <= 0; +	  go_d4 <= 0; +       end +     else +       begin +	  go_d1 <= go; +	  go_d2 <= go_d1; +	  go_d3 <= go_d2; +	  go_d4 <= go_d3; +       end + +   wire [17:0] 		coeff_a = -10690; +   wire [17:0] 		coeff_b = 75809; +    +   reg [INTWIDTH-1:0] 	d1, d2, d3, d4 , d5, d6; +   always @(posedge clk) +     if(stb_rnd_d1 | rst) +       begin +	  d1 <= data_rnd_d1; +	  d2 <= d1; +	  d3 <= d2; +	  d4 <= d3; +	  d5 <= d4; +	  d6 <= d5; +       end + +   reg [17:0] sum_a, sum_b, middle, middle_d1; + +   always @(posedge clk) +     if(go) +       begin +	  sum_a <= {data_rnd_d1[INTWIDTH-1],data_rnd_d1} + {d6[INTWIDTH-1],d6}; +	  sum_b <= {d2[INTWIDTH-1],d2} + {d4[INTWIDTH-1],d4}; +	  //middle <= {d3[INTWIDTH-1],d3}; +	  middle <= {d3,1'b0}; +       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) ); + +   localparam ACCWIDTH = 30; +   reg [ACCWIDTH-1:0] 	 accum; +    +   always @(posedge clk) +     if(rst) +       accum <= 0; +     else if(go_d2) +       accum <= {middle_d1[17],middle_d1[17],middle_d1,{(16+ACCWIDTH-36){1'b0}}} + {prod[35:36-ACCWIDTH]}; +     else if(go_d3) +       accum <= accum + {prod[35:36-ACCWIDTH]}; +    +   wire [WIDTH:0] 	 accum_rnd; +   wire [WIDTH-1:0] 	 accum_rnd_clip; +    +   wire 	 stb_round; +    +   round_sd #(.WIDTH_IN(ACCWIDTH),.WIDTH_OUT(WIDTH+1)) round_acc  +     (.clk(clk), .reset(rst), .in(accum), .strobe_in(go_d4), .out(accum_rnd), .strobe_out(stb_round)); + +   clip #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip (.in(accum_rnd), .out(accum_rnd_clip)); +    +   // Output +   always @(posedge clk) +     begin +	stb_out  <= bypass ? stb_in : stb_round; +	data_out <= bypass ? data_in : accum_rnd_clip; +     end + + +endmodule // small_hb_dec diff --git a/fpga/usrp2/sdr_lib/small_hb_dec_tb.v b/fpga/usrp2/sdr_lib/small_hb_dec_tb.v new file mode 100644 index 000000000..1e713321a --- /dev/null +++ b/fpga/usrp2/sdr_lib/small_hb_dec_tb.v @@ -0,0 +1,157 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +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/fpga/usrp2/sdr_lib/small_hb_int.v b/fpga/usrp2/sdr_lib/small_hb_int.v new file mode 100644 index 000000000..387f9e1cb --- /dev/null +++ b/fpga/usrp2/sdr_lib/small_hb_int.v @@ -0,0 +1,102 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +// 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/fpga/usrp2/sdr_lib/small_hb_int_tb.v b/fpga/usrp2/sdr_lib/small_hb_int_tb.v new file mode 100644 index 000000000..fe1e1a7dd --- /dev/null +++ b/fpga/usrp2/sdr_lib/small_hb_int_tb.v @@ -0,0 +1,149 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + +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/fpga/usrp2/sdr_lib/tx_control.v b/fpga/usrp2/sdr_lib/tx_control.v new file mode 100644 index 000000000..e6866a40c --- /dev/null +++ b/fpga/usrp2/sdr_lib/tx_control.v @@ -0,0 +1,185 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 3 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, see <http://www.gnu.org/licenses/>. +// + + +`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 FIFO interface from Buffer Pool +     input [31:0] rd_dat_i, +     input [3:0] rd_flags_i, +     input rd_ready_i, +     output rd_ready_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 +     ); + +   wire 	   rd_sop_i  = rd_flags_i[0];  // Unused +   wire 	   rd_eop_i  = rd_flags_i[1]; +   wire 	   rd_occ_i = rd_flags_i[3:2]; // Unused, should always be 0 + +   // 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_CTRL = 1; +   localparam XFER_PKT = 2; +   // Add underrun state? +    +   always @(posedge clk) +     if(rst) +       xfer_state <= XFER_IDLE; +     else if(clear_state) +       xfer_state <= XFER_IDLE; +     else +       if(rd_ready_i & rd_ready_o) +	 case(xfer_state) +	   XFER_IDLE : +	     begin +		xfer_state <= XFER_CTRL; +		held_flags <= rd_dat_i[2:0]; +	     end +	   XFER_CTRL : +	     xfer_state <= XFER_PKT; +	   XFER_PKT : +	     if(rd_eop_i) +	       xfer_state <= XFER_IDLE; +	 endcase // case(xfer_state) + +   wire have_data_space; +   assign full_data   = ~have_data_space; +    +   assign write_data  = (xfer_state == XFER_PKT) & rd_ready_i & rd_ready_o; +   assign write_ctrl  = (xfer_state == XFER_CTRL) & rd_ready_i & rd_ready_o; + +   assign rd_ready_o  = ~full_data & ~full_ctrl; +    +   wire [31:0] data_o; +   wire        eop_o, eob, sob, send_imm; +   wire [31:0] sendtime; +   wire [4:0]  occ_ctrl; +/*    +   cascadefifo2 #(.WIDTH(33),.SIZE(FIFOSIZE)) txctrlfifo +     (.clk(clk),.rst(rst),.clear(clear_state), +      .datain({rd_eop_i,rd_dat_i[31:0]}), .write(write_data), .full(full_data), +      .dataout({eop_o,data_o}), .read(read_data), .empty(empty_data), +      .space(), .occupied(fifo_occupied) ); +*/ +   wire        have_data; +   assign empty_data  = ~have_data; +    +   fifo_cascade #(.WIDTH(33),.SIZE(FIFOSIZE)) txctrlfifo +     (.clk(clk),.reset(rst),.clear(clear_state), +      .datain({rd_eop_i,rd_dat_i[31:0]}), .src_rdy_i(write_data), .dst_rdy_o(have_data_space), +      .dataout({eop_o,data_o}), .src_rdy_o(have_data), .dst_rdy_i(read_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[31:0]}), .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 diff --git a/fpga/usrp2/sdr_lib/tx_frontend.v b/fpga/usrp2/sdr_lib/tx_frontend.v new file mode 100644 index 000000000..1e7f0bf31 --- /dev/null +++ b/fpga/usrp2/sdr_lib/tx_frontend.v @@ -0,0 +1,101 @@ + +module tx_frontend +  #(parameter BASE=0, +    parameter WIDTH_OUT=16, +    parameter IQCOMP_EN=0) +   (input clk, input rst, +    input set_stb, input [7:0] set_addr, input [31:0] set_data, +    input [23:0] tx_i, input [23:0] tx_q, input run, +    output reg [WIDTH_OUT-1:0] dac_a, output reg [WIDTH_OUT-1:0] dac_b +    ); + +   // IQ balance --> DC offset --> rounding --> mux + +   wire [23:0] i_dco, q_dco, i_ofs, q_ofs; +   wire [WIDTH_OUT-1:0] i_final, q_final; +   wire [7:0]  mux_ctrl; +   wire [35:0] corr_i, corr_q; +   wire [23:0] i_bal, q_bal; +   wire [17:0] mag_corr, phase_corr; +    +   setting_reg #(.my_addr(BASE+0), .width(24)) sr_0 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(i_dco),.changed()); + +   setting_reg #(.my_addr(BASE+1), .width(24)) sr_1 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(q_dco),.changed()); + +   setting_reg #(.my_addr(BASE+2),.width(18)) sr_2 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(mag_corr),.changed()); +    +   setting_reg #(.my_addr(BASE+3),.width(18)) sr_3 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(phase_corr),.changed()); +   +   setting_reg #(.my_addr(BASE+4), .width(8)) sr_4 +     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(mux_ctrl),.changed()); + +   generate +      if(IQCOMP_EN==1) +	begin +	   // IQ Balance +	   MULT18X18S mult_mag_corr +	     (.P(corr_i), .A(tx_i[23:6]), .B(mag_corr), .C(clk), .CE(1), .R(rst) );  +	    +	   MULT18X18S mult_phase_corr +	     (.P(corr_q), .A(tx_i[23:6]), .B(phase_corr), .C(clk), .CE(1), .R(rst) ); +	    +	   add2_and_clip_reg #(.WIDTH(24)) add_clip_i +	     (.clk(clk), .rst(rst),  +	      .in1(tx_i), .in2({{4{corr_i[35]}},corr_i[35:16]}), .strobe_in(1'b1),  +	      .sum(i_bal), .strobe_out()); +	    +	   add2_and_clip_reg #(.WIDTH(24)) add_clip_q +	     (.clk(clk), .rst(rst),  +	      .in1(tx_q), .in2({{4{corr_q[35]}},corr_q[35:16]}), .strobe_in(1'b1), +	      .sum(q_bal), .strobe_out()); + +	   // DC Offset +	   add2_and_clip_reg #(.WIDTH(24)) add_dco_i +	     (.clk(clk), .rst(rst), .in1(i_dco), .in2(i_bal), .strobe_in(1'b1), .sum(i_ofs), .strobe_out()); +	    +	   add2_and_clip_reg #(.WIDTH(24)) add_dco_q +	     (.clk(clk), .rst(rst), .in1(q_dco), .in2(q_bal), .strobe_in(1'b1), .sum(q_ofs), .strobe_out()); +	end // if (IQCOMP_EN==1) +      else +	begin +	   // DC Offset +	   add2_and_clip_reg #(.WIDTH(24)) add_dco_i +	     (.clk(clk), .rst(rst), .in1(i_dco), .in2(tx_i), .strobe_in(1'b1), .sum(i_ofs), .strobe_out()); +	    +	   add2_and_clip_reg #(.WIDTH(24)) add_dco_q +	     (.clk(clk), .rst(rst), .in1(q_dco), .in2(tx_q), .strobe_in(1'b1), .sum(q_ofs), .strobe_out()); +	end // else: !if(IQCOMP_EN==1) +   endgenerate +    +   // Rounding +   round_sd #(.WIDTH_IN(24),.WIDTH_OUT(WIDTH_OUT)) round_i +     (.clk(clk), .reset(rst), .in(i_ofs),.strobe_in(1'b1), .out(i_final), .strobe_out()); + +   round_sd #(.WIDTH_IN(24),.WIDTH_OUT(WIDTH_OUT)) round_q +     (.clk(clk), .reset(rst), .in(q_ofs),.strobe_in(1'b1), .out(q_final), .strobe_out()); + +   // Mux +   always @(posedge clk) +     case(mux_ctrl[3:0]) +       0 : dac_a <= i_final; +       1 : dac_a <= q_final; +       default : dac_a <= 0; +     endcase // case (mux_ctrl[3:0]) +       +   always @(posedge clk) +     case(mux_ctrl[7:4]) +       0 : dac_b <= i_final; +       1 : dac_b <= q_final; +       default : dac_b <= 0; +     endcase // case (mux_ctrl[7:4]) +       +endmodule // tx_frontend | 
