diff options
author | Martin Braun <martin.braun@ettus.com> | 2020-01-23 16:10:22 -0800 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2020-01-28 09:35:36 -0800 |
commit | bafa9d95453387814ef25e6b6256ba8db2df612f (patch) | |
tree | 39ba24b5b67072d354775272e687796bb511848d /fpga/usrp2/sdr_lib | |
parent | 3075b981503002df3115d5f1d0b97d2619ba30f2 (diff) | |
download | uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2 uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip |
Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.
This commit also updates the license files and paths therein.
We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.
== Original Codebase and Rebasing ==
The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/
It can be used for bisecting, reference, and a more detailed history.
The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.
If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:
- Create a directory to store patches (this should be an empty
directory):
mkdir ~/patches
- Now make sure that your FPGA codebase is based on the same state as
the code that was merged:
cd src/fpga # Or wherever your FPGA code is stored
git rebase v4.0.0.0-pre-uhd-merge
Note: The rebase command may look slightly different depending on what
exactly you're trying to rebase.
- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:
git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches
Note: Make sure that only patches are stored in your output directory.
It should otherwise be empty. Make sure that you picked the correct
range of commits, and only commits you wanted to rebase were exported
as patch files.
- Go to the UHD repository and apply the patches:
cd src/uhd # Or wherever your UHD repository is stored
git am --directory fpga ~/patches/*
rm -rf ~/patches # This is for cleanup
== Contributors ==
The following people have contributed mainly to these files (this list
is not complete):
Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>
Diffstat (limited to 'fpga/usrp2/sdr_lib')
80 files changed, 6887 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..e6c4c5343 --- /dev/null +++ b/fpga/usrp2/sdr_lib/Makefile.srcs @@ -0,0 +1,45 @@ +# +# Copyright 2010-2012 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 \ +ddc_chain.v \ +duc_chain.v \ +dspengine_16to8.v \ +dspengine_8to16.v \ +hb_dec.v \ +hb_interp.v \ +pipectrl.v \ +pipestage.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 \ +dsp_tx_glue.v \ +dsp_rx_glue.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..9098fd5b8 --- /dev/null +++ b/fpga/usrp2/sdr_lib/clip_reg.v @@ -0,0 +1,46 @@ +// -*- 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, + parameter STROBED=1'b0) + (input clk, + input [bits_in-1:0] in, + output reg [bits_out-1:0] out, + input strobe_in, + output reg strobe_out); + + wire [bits_out-1:0] temp; + + clip #(.bits_in(bits_in),.bits_out(bits_out)) clip (.in(in),.out(temp)); + + always @(posedge clk) + strobe_out <= strobe_in; + + always @(posedge clk) + if(strobe_in | ~STROBED) + 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..51b074a33 --- /dev/null +++ b/fpga/usrp2/sdr_lib/cordic_z24.v @@ -0,0 +1,124 @@ +// -*- 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; + +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/ddc_chain.v b/fpga/usrp2/sdr_lib/ddc_chain.v new file mode 100644 index 000000000..dff3d4e97 --- /dev/null +++ b/fpga/usrp2/sdr_lib/ddc_chain.v @@ -0,0 +1,194 @@ +// +// Copyright 2011-2012 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/>. +// + +//! The USRP digital down-conversion chain + +module ddc_chain + #( + parameter BASE = 0, + parameter DSPNO = 0, + parameter WIDTH = 24 + ) + (input clk, input rst, input clr, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input set_stb_user, input [7:0] set_addr_user, input [31:0] set_data_user, + + // From RX frontend + input [WIDTH-1:0] rx_fe_i, + input [WIDTH-1:0] rx_fe_q, + + // To RX control + output [31:0] sample, + input run, + output strobe, + output [31:0] debug + ); + + localparam cwidth = 25; + localparam zwidth = 24; + + wire ddc_enb; + wire [31:0] phase_inc; + reg [31:0] phase; + + wire [17:0] scale_factor; + wire [cwidth-1:0] i_cordic, q_cordic; + wire [WIDTH-1:0] i_cordic_clip, q_cordic_clip; + wire [WIDTH-1:0] i_cic, q_cic; + wire [WIDTH-1:0] i_hb1, q_hb1; + wire [WIDTH-1:0] i_hb2, q_hb2; + + wire strobe_cic, strobe_hb1, strobe_hb2; + wire enable_hb1, enable_hb2; + wire [7:0] cic_decim_rate; + + reg [WIDTH-1:0] rx_fe_i_mux, rx_fe_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), .width(18)) sr_1 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(scale_factor),.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 + rx_fe_i_mux <= rx_fe_q; + rx_fe_q_mux <= realmode ? 0 : rx_fe_i; + end + else + begin + rx_fe_i_mux <= rx_fe_i; + rx_fe_q_mux <= realmode ? 0 : rx_fe_q; + end + + // NCO + always @(posedge clk) + if(rst) + phase <= 0; + else if(~ddc_enb) + phase <= 0; + else + phase <= phase + phase_inc; + + //sign extension of cordic input + wire [WIDTH-1:0] to_ddc_chain_i, to_ddc_chain_q; + wire [cwidth-1:0] to_cordic_i, to_cordic_q; + sign_extend #(.bits_in(WIDTH), .bits_out(cwidth)) sign_extend_cordic_i (.in(to_ddc_chain_i), .out(to_cordic_i)); + sign_extend #(.bits_in(WIDTH), .bits_out(cwidth)) sign_extend_cordic_q (.in(to_ddc_chain_q), .out(to_cordic_q)); + + // CORDIC 24-bit I/O + cordic_z24 #(.bitwidth(cwidth)) + cordic(.clock(clk), .reset(rst), .enable(ddc_enb), + .xi(to_cordic_i),. yi(to_cordic_q), .zi(phase[31:32-zwidth]), + .xo(i_cordic),.yo(q_cordic),.zo() ); + + clip_reg #(.bits_in(cwidth), .bits_out(WIDTH)) clip_i + (.clk(clk), .in(i_cordic), .strobe_in(1'b1), .out(i_cordic_clip)); + clip_reg #(.bits_in(cwidth), .bits_out(WIDTH)) clip_q + (.clk(clk), .in(q_cordic), .strobe_in(1'b1), .out(q_cordic_clip)); + + // CIC decimator 24 bit I/O + cic_strober cic_strober(.clock(clk),.reset(rst),.enable(ddc_enb),.rate(cic_decim_rate), + .strobe_fast(1),.strobe_slow(strobe_cic) ); + + cic_decim #(.bw(WIDTH)) + decim_i (.clock(clk),.reset(rst),.enable(ddc_enb), + .rate(cic_decim_rate),.strobe_in(1'b1),.strobe_out(strobe_cic), + .signal_in(i_cordic_clip),.signal_out(i_cic)); + + cic_decim #(.bw(WIDTH)) + decim_q (.clock(clk),.reset(rst),.enable(ddc_enb), + .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(WIDTH)) small_hb_i + (.clk(clk),.rst(rst),.bypass(~enable_hb1),.run(ddc_enb), + .stb_in(strobe_cic),.data_in(i_cic),.stb_out(strobe_hb1),.data_out(i_hb1)); + + small_hb_dec #(.WIDTH(WIDTH)) small_hb_q + (.clk(clk),.rst(rst),.bypass(~enable_hb1),.run(ddc_enb), + .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(WIDTH)) hb_i + (.clk(clk),.rst(rst),.bypass(~enable_hb2),.run(ddc_enb),.cpi(cpi_hb), + .stb_in(strobe_hb1),.data_in(i_hb1),.stb_out(strobe_hb2),.data_out(i_hb2)); + + hb_dec #(.WIDTH(WIDTH)) hb_q + (.clk(clk),.rst(rst),.bypass(~enable_hb2),.run(ddc_enb),.cpi(cpi_hb), + .stb_in(strobe_hb1),.data_in(q_hb1),.stb_out(),.data_out(q_hb2)); + + // round to 18 bits for multiplication (gain of 6 bits) + wire [17:0] i_hb2_rnd, q_hb2_rnd; + + round #(.bits_in(WIDTH),.bits_out(18)) round_i_hb2 (.in(i_hb2), .out(i_hb2_rnd)); + round #(.bits_in(WIDTH),.bits_out(18)) round_q_hb2 (.in(q_hb2), .out(q_hb2_rnd)); + + //scalar operation + wire [35:0] prod_i, prod_q; + + MULT18X18S mult_i + (.P(prod_i), .A(i_hb2_rnd), .B(scale_factor), .C(clk), .CE(strobe_hb2), .R(rst) ); + MULT18X18S mult_q + (.P(prod_q), .A(q_hb2_rnd), .B(scale_factor), .C(clk), .CE(strobe_hb2), .R(rst) ); + + //pipeline for the multiplier with clipping to 34 bits + wire [33:0] prod_reg_i, prod_reg_q; + wire strobe_mult; + clip_reg #(.bits_in(36),.bits_out(34)) clip_prod_i + (.clk(clk),.in(prod_i),.out(prod_reg_i),.strobe_in(strobe_hb2),.strobe_out(strobe_mult)); + clip_reg #(.bits_in(36),.bits_out(34)) clip_prod_q + (.clk(clk),.in(prod_q),.out(prod_reg_q),.strobe_in(strobe_hb2),.strobe_out()); + + // Round final answer to 16 bits + wire [31:0] ddc_chain_out; + wire ddc_chain_stb; + + round_sd #(.WIDTH_IN(34),.WIDTH_OUT(16)) round_i + (.clk(clk),.reset(rst), .in(prod_reg_i),.strobe_in(strobe_mult), .out(ddc_chain_out[31:16]), .strobe_out(ddc_chain_stb)); + + round_sd #(.WIDTH_IN(34),.WIDTH_OUT(16)) round_q + (.clk(clk),.reset(rst), .in(prod_reg_q),.strobe_in(strobe_mult), .out(ddc_chain_out[15:0]), .strobe_out()); + + dsp_rx_glue #(.DSPNO(DSPNO), .WIDTH(WIDTH)) custom( + .clock(clk), .reset(rst), .clear(clr), .enable(run), + .set_stb(set_stb_user), .set_addr(set_addr_user), .set_data(set_data_user), + .frontend_i(rx_fe_i_mux), .frontend_q(rx_fe_q_mux), + .ddc_in_i(to_ddc_chain_i), .ddc_in_q(to_ddc_chain_q), + .ddc_out_sample(ddc_chain_out), .ddc_out_strobe(ddc_chain_stb), .ddc_out_enable(ddc_enb), + .bb_sample(sample), .bb_strobe(strobe)); + + assign debug = {enable_hb1, enable_hb2, run, strobe, strobe_cic, strobe_hb1, strobe_hb2}; + +endmodule // ddc_chain 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..a221bed44 --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_core_rx_tb.v @@ -0,0 +1,73 @@ + +`timescale 1ns/1ns +module ddc_chain_tb(); + + reg clk, rst; + + initial rst = 1; + initial #1000 rst = 0; + initial clk = 0; + always #5 clk = ~clk; + + initial $dumpfile("ddc_chain_tb.vcd"); + initial $dumpvars(0,ddc_chain_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; + + ddc_chain #(.BASE(0)) ddc_chain + (.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 // ddc_chain_tb diff --git a/fpga/usrp2/sdr_lib/dsp_rx_glue.v b/fpga/usrp2/sdr_lib/dsp_rx_glue.v new file mode 100644 index 000000000..038a67a29 --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_rx_glue.v @@ -0,0 +1,98 @@ +// +// Copyright 2012 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/>. +// + +//The following module effects the IO of the DDC chain. +//By default, this entire module is a simple pass-through. + +module dsp_rx_glue +#( + //the dsp unit number: 0, 1, 2... + parameter DSPNO = 0, + + //frontend bus width + parameter WIDTH = 24 +) +( + //control signals + input clock, input reset, input clear, input enable, + + //user settings bus, controlled through user setting regs API + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + //full rate inputs directly from the RX frontend + input [WIDTH-1:0] frontend_i, + input [WIDTH-1:0] frontend_q, + + //full rate outputs directly to the DDC chain + output [WIDTH-1:0] ddc_in_i, + output [WIDTH-1:0] ddc_in_q, + + //strobed samples {I16,Q16} from the RX DDC chain + input [31:0] ddc_out_sample, + input ddc_out_strobe, //high on valid sample + output ddc_out_enable, //enables DDC module + + //strobbed baseband samples {I16,Q16} from this module + output [31:0] bb_sample, + output bb_strobe, //high on valid sample + + //debug output (optional) + output [31:0] debug +); + + generate + if (DSPNO==0) begin + `ifndef RX_DSP0_MODULE + assign ddc_in_i = frontend_i; + assign ddc_in_q = frontend_q; + assign bb_sample = ddc_out_sample; + assign bb_strobe = ddc_out_strobe; + assign ddc_out_enable = enable; + `else + `RX_DSP0_MODULE #(.WIDTH(WIDTH)) rx_dsp0_custom + ( + .clock(clock), .reset(reset), .clear(clear), .enable(enable), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .frontend_i(frontend_i), .frontend_q(frontend_q), + .ddc_in_i(ddc_in_i), .ddc_in_q(ddc_in_q), + .ddc_out_sample(ddc_out_sample), .ddc_out_strobe(ddc_out_strobe), .ddc_out_enable(ddc_out_enable), + .bb_sample(bb_sample), .bb_strobe(bb_strobe) + ); + `endif + end + else begin + `ifndef RX_DSP1_MODULE + assign ddc_in_i = frontend_i; + assign ddc_in_q = frontend_q; + assign bb_sample = ddc_out_sample; + assign bb_strobe = ddc_out_strobe; + assign ddc_out_enable = enable; + `else + `RX_DSP1_MODULE #(.WIDTH(WIDTH)) rx_dsp1_custom + ( + .clock(clock), .reset(reset), .clear(clear), .enable(enable), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .frontend_i(frontend_i), .frontend_q(frontend_q), + .ddc_in_i(ddc_in_i), .ddc_in_q(ddc_in_q), + .ddc_out_sample(ddc_out_sample), .ddc_out_strobe(ddc_out_strobe), .ddc_out_enable(ddc_out_enable), + .bb_sample(bb_sample), .bb_strobe(bb_strobe) + ); + `endif + end + endgenerate + +endmodule //dsp_rx_glue diff --git a/fpga/usrp2/sdr_lib/dsp_tx_glue.v b/fpga/usrp2/sdr_lib/dsp_tx_glue.v new file mode 100644 index 000000000..46f6789ee --- /dev/null +++ b/fpga/usrp2/sdr_lib/dsp_tx_glue.v @@ -0,0 +1,98 @@ +// +// Copyright 2012 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/>. +// + +//The following module effects the IO of the DUC chain. +//By default, this entire module is a simple pass-through. + +module dsp_tx_glue +#( + //the dsp unit number: 0, 1, 2... + parameter DSPNO = 0, + + //frontend bus width + parameter WIDTH = 24 +) +( + //control signals + input clock, input reset, input clear, input enable, + + //user settings bus, controlled through user setting regs API + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + //full rate outputs directly to the TX frontend + output [WIDTH-1:0] frontend_i, + output [WIDTH-1:0] frontend_q, + + //full rate outputs directly from the DUC chain + input [WIDTH-1:0] duc_out_i, + input [WIDTH-1:0] duc_out_q, + + //strobed samples {I16,Q16} to the TX DUC chain + output [31:0] duc_in_sample, + input duc_in_strobe, //this is a backpressure signal + output duc_in_enable, //enables DUC module + + //strobbed baseband samples {I16,Q16} to this module + input [31:0] bb_sample, + output bb_strobe, //this is a backpressure signal + + //debug output (optional) + output [31:0] debug +); + + generate + if (DSPNO==0) begin + `ifndef TX_DSP0_MODULE + assign frontend_i = duc_out_i; + assign frontend_q = duc_out_q; + assign duc_in_sample = bb_sample; + assign bb_strobe = duc_in_strobe; + assign duc_in_enable = enable; + `else + `TX_DSP0_MODULE #(.WIDTH(WIDTH)) tx_dsp0_custom + ( + .clock(clock), .reset(reset), .clear(clear), .enable(enable), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .frontend_i(frontend_i), .frontend_q(frontend_q), + .duc_out_i(duc_out_i), .duc_out_q(duc_out_q), + .duc_in_sample(duc_in_sample), .duc_in_strobe(duc_in_strobe), .duc_in_enable(duc_in_enable), + .bb_sample(bb_sample), .bb_strobe(bb_strobe) + ); + `endif + end + else begin + `ifndef TX_DSP1_MODULE + assign frontend_i = duc_out_i; + assign frontend_q = duc_out_q; + assign duc_in_sample = bb_sample; + assign bb_strobe = duc_in_strobe; + assign duc_in_enable = enable; + `else + `TX_DSP1_MODULE #(.WIDTH(WIDTH)) tx_dsp1_custom + ( + .clock(clock), .reset(reset), .clear(clear), .enable(enable), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .frontend_i(frontend_i), .frontend_q(frontend_q), + .duc_out_i(duc_out_i), .duc_out_q(duc_out_q), + .duc_in_sample(duc_in_sample), .duc_in_strobe(duc_in_strobe), .duc_in_enable(duc_in_enable), + .bb_sample(bb_sample), .bb_strobe(bb_strobe) + ); + `endif + end + endgenerate + +endmodule //dsp_tx_glue diff --git a/fpga/usrp2/sdr_lib/dspengine_16to8.v b/fpga/usrp2/sdr_lib/dspengine_16to8.v new file mode 100644 index 000000000..1d6746dd1 --- /dev/null +++ b/fpga/usrp2/sdr_lib/dspengine_16to8.v @@ -0,0 +1,211 @@ + +// Copyright 2011-2013 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 dspengine_16to8 + #(parameter BASE = 0, + parameter BUF_SIZE = 9) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + output access_we, + output access_stb, + input access_ok, + output access_done, + output access_skip_read, + output [BUF_SIZE-1:0] access_adr, + input [BUF_SIZE-1:0] access_len, + output [35:0] access_dat_o, + input [35:0] access_dat_i + ); + + wire convert; + setting_reg #(.my_addr(BASE),.width(1)) sr_16to8 + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(convert),.changed()); + + reg [2:0] dsp_state; + localparam DSP_IDLE = 0; + localparam DSP_PARSE_HEADER = 1; + localparam DSP_CONVERT = 2; + localparam DSP_CONVERT_DRAIN_PIPE = 3; + localparam DSP_READ_TRAILER = 4; + localparam DSP_WRITE_TRAILER = 5; + localparam DSP_WRITE_HEADER = 6; + localparam DSP_DONE = 7; + + // Parse VITA header + wire is_if_data = (access_dat_i[31:29] == 3'b000); + wire has_streamid = access_dat_i[28]; + wire has_classid = access_dat_i[27]; + wire has_trailer = access_dat_i[26]; + // 25:24 reserved, aka SOB/EOB + wire has_secs = |access_dat_i[23:22]; + wire has_tics = |access_dat_i[21:20]; + wire [3:0] hdr_length = 1 + has_streamid + has_classid + has_classid + has_secs + has_tics + has_tics; + + wire [35:0] prod_i, prod_q; + wire [15:0] scaled_i, scaled_q; + wire [7:0] i8, q8; + reg [7:0] i8_reg, q8_reg; + wire stb_read, stb_clip, val_read, val_clip; + wire stb_out, stb_reg; + reg even; + + reg [BUF_SIZE-1:0] read_adr, write_adr; + reg has_trailer_reg; + + wire last = (read_adr + 1) == (access_len - has_trailer_reg); + wire last_o, even_o; + + wire stb_write = stb_out & (even_o | last_o); + wire send_to_pipe = ~stb_write & (dsp_state == DSP_CONVERT); + reg [31:0] new_header, new_trailer, trailer_mask; + reg [15:0] length; + reg wait_for_trailer; + + always @(posedge clk) + if(reset | clear) + dsp_state <= DSP_IDLE; + else + case(dsp_state) + DSP_IDLE : + begin + read_adr <= 0; + write_adr <= 0; + even <= 0; + if(access_ok) + dsp_state <= DSP_PARSE_HEADER; + end + + DSP_PARSE_HEADER : + begin + has_trailer_reg <= has_trailer; + new_header[31:16] <= access_dat_i[31:16]; + new_header[15:0] <= access_len; + length <= access_len; + if(is_if_data & convert) + begin + read_adr <= hdr_length; + write_adr <= hdr_length; + dsp_state <= DSP_CONVERT; + end + else + dsp_state <= DSP_WRITE_HEADER; + end + + DSP_CONVERT: + begin + new_header[26] <= 1'b1; // all converted packets have a trailer + if(stb_write) + write_adr <= write_adr + 1; + else if(stb_read) // should always be 1 if we are here + begin + read_adr <= read_adr + 1; + even <= ~even; + if(last) + begin + dsp_state <= DSP_CONVERT_DRAIN_PIPE; + if(~even) + trailer_mask <= 32'h00400400; + else + trailer_mask <= 32'h00400000; + end + end + end + + DSP_CONVERT_DRAIN_PIPE : + if(stb_write) + begin + write_adr <= write_adr + 1; + if(last_o) + if(has_trailer_reg) + begin + dsp_state <= DSP_READ_TRAILER; + wait_for_trailer <= 0; + end + else + begin + dsp_state <= DSP_WRITE_TRAILER; + new_trailer <= trailer_mask; + end + end + + DSP_READ_TRAILER : + begin + wait_for_trailer <= 1; + if(wait_for_trailer) + dsp_state <= DSP_WRITE_TRAILER; + new_trailer <= access_dat_i[31:0] | trailer_mask; + end + + DSP_WRITE_TRAILER : + begin + dsp_state <= DSP_WRITE_HEADER; + write_adr <= 0; + new_header[15:0] <= write_adr + 1; + end + + DSP_WRITE_HEADER : + dsp_state <= DSP_DONE; + + DSP_DONE : + begin + read_adr <= 0; + write_adr <= 0; + dsp_state <= DSP_IDLE; + end + endcase // case (dsp_state) + + assign access_skip_read = 0; + assign access_done = (dsp_state == DSP_DONE); + + assign access_stb = 1; + + assign access_we = (dsp_state == DSP_WRITE_HEADER) | + (dsp_state == DSP_WRITE_TRAILER) | + stb_write; + + assign access_dat_o = (dsp_state == DSP_WRITE_HEADER) ? { 4'h1, new_header } : + (dsp_state == DSP_WRITE_TRAILER) ? { 4'h2, new_trailer } : + (last_o&~even_o) ? {4'h0, i8, q8, 16'd0 } : + {4'h0, i8_reg, q8_reg, i8, q8 }; + + assign access_adr = (stb_write|(dsp_state == DSP_WRITE_HEADER)|(dsp_state == DSP_WRITE_TRAILER)) ? write_adr : read_adr; + + // DSP Pipeline + + wire [15:0] i16 = access_dat_i[31:16]; + wire [15:0] q16 = access_dat_i[15:0]; + + pipectrl #(.STAGES(2), .TAGWIDTH(2)) pipectrl + (.clk(clk), .reset(reset), + .src_rdy_i(send_to_pipe), .dst_rdy_o(), // dst_rdy_o will always be 1 since dst_rdy_i is 1, below + .src_rdy_o(stb_out), .dst_rdy_i(1), // always accept output of chain + .strobes({stb_clip,stb_read}), .valids({val_clip,val_read}), + .tag_i({last,even}), .tag_o({last_o,even_o})); + + always @(posedge clk) + if(stb_out & ~even_o) + {i8_reg,q8_reg} <= {i8,q8}; + + clip_reg #(.bits_in(16),.bits_out(8),.STROBED(1)) clip_i + (.clk(clk), .in(i16), .out(i8), .strobe_in(stb_clip), .strobe_out()); + + clip_reg #(.bits_in(16),.bits_out(8),.STROBED(1)) clip_q + (.clk(clk), .in(q16), .out(q8), .strobe_in(stb_clip), .strobe_out()); + +endmodule // dspengine_16to8 diff --git a/fpga/usrp2/sdr_lib/dspengine_8to16.v b/fpga/usrp2/sdr_lib/dspengine_8to16.v new file mode 100644 index 000000000..64246ac13 --- /dev/null +++ b/fpga/usrp2/sdr_lib/dspengine_8to16.v @@ -0,0 +1,203 @@ + +// Copyright 2012-2013 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 dspengine_8to16 + #(parameter BASE = 0, + parameter BUF_SIZE = 9, + parameter HEADER_OFFSET = 0) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + output access_we, + output access_stb, + input access_ok, + output access_done, + output access_skip_read, + output [BUF_SIZE-1:0] access_adr, + input [BUF_SIZE-1:0] access_len, + output [35:0] access_dat_o, + input [35:0] access_dat_i + ); + + wire convert; + + setting_reg #(.my_addr(BASE),.width(1)) sr_8to16 + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(convert),.changed()); + + reg [3:0] dsp_state; + localparam DSP_IDLE = 0; + localparam DSP_IDLE_RD = 1; + localparam DSP_PARSE_HEADER = 2; + localparam DSP_READ = 3; + localparam DSP_READ_WAIT = 4; + localparam DSP_WRITE_1 = 5; + localparam DSP_WRITE_0 = 6; + localparam DSP_READ_TRAILER = 7; + localparam DSP_WRITE_TRAILER = 8; + localparam DSP_WRITE_HEADER = 9; + localparam DSP_DONE = 10; + + // Parse VITA header + wire is_if_data = (access_dat_i[31:29] == 3'b000); + wire has_streamid = access_dat_i[28]; + wire has_classid = access_dat_i[27]; + wire has_trailer = access_dat_i[26]; + // 25:24 reserved, aka SOB/EOB + wire has_secs = |access_dat_i[23:22]; + wire has_tics = |access_dat_i[21:20]; + wire [3:0] hdr_length = 1 + has_streamid + has_classid + has_classid + has_secs + has_tics + has_tics; + reg [15:0] hdr_length_reg; + + reg odd; + + reg [BUF_SIZE-1:0] read_adr, write_adr; + reg has_trailer_reg; + + reg [31:0] new_header, new_trailer, trailer_mask; + reg wait_for_trailer; + reg [15:0] data_in_len; + wire is_odd = access_dat_i[22] & access_dat_i[10]; + wire [15:0] data_in_lenx2 = {data_in_len[14:0], 1'b0} - is_odd; + + reg [7:0] i8_0, q8_0; + wire [7:0] i8_1 = access_dat_i[15:8]; + wire [7:0] q8_1 = access_dat_i[7:0]; + reg skip; + + + always @(posedge clk) + { i8_0, q8_0 } <= access_dat_i[31:16]; + + always @(posedge clk) + if(reset | clear) + dsp_state <= DSP_IDLE; + else + case(dsp_state) + DSP_IDLE : + begin + read_adr <= HEADER_OFFSET; + write_adr <= HEADER_OFFSET; + if(access_ok) + dsp_state <= DSP_IDLE_RD; + end + + DSP_IDLE_RD: //extra idle state for read to become valid + dsp_state <= DSP_PARSE_HEADER; + + DSP_PARSE_HEADER : + begin + has_trailer_reg <= has_trailer; + new_header[31:0] <= access_dat_i[31:0]; + hdr_length_reg <= hdr_length; + if(~is_if_data | ~convert | ~has_trailer) + // ~convert is valid (16 bit mode) but both ~trailer and ~is_if_data are both + // really error conditions on the TX side. We shouldn't ever see them in the TX chain + dsp_state <= DSP_WRITE_HEADER; + else + begin + read_adr <= access_dat_i[15:0] + HEADER_OFFSET - 1; // point to trailer + dsp_state <= DSP_READ_TRAILER; + wait_for_trailer <= 0; + data_in_len <= access_dat_i[15:0] - hdr_length - 1 /*trailer*/; + end + end + + DSP_READ_TRAILER : + begin + wait_for_trailer <= 1; + if(wait_for_trailer) + dsp_state <= DSP_WRITE_TRAILER; + new_trailer <= access_dat_i[31:0]; // Leave trailer unchanged + odd <= is_odd; + write_adr <= hdr_length_reg + data_in_lenx2 + HEADER_OFFSET; + end + + DSP_WRITE_TRAILER : + begin + dsp_state <= DSP_READ; + write_adr <= write_adr - 1; + read_adr <= read_adr - 1; + new_header[15:0] <= write_adr + (1 - HEADER_OFFSET); // length = addr of trailer + 1 + end + + DSP_READ : + begin + read_adr <= read_adr - 1; + if(odd) + dsp_state <= DSP_READ_WAIT; + else + dsp_state <= DSP_WRITE_1; + odd <= 0; + end + + DSP_READ_WAIT : + dsp_state <= DSP_WRITE_0; + + DSP_WRITE_1 : + begin + write_adr <= write_adr - 1; + if(write_adr == (hdr_length_reg+HEADER_OFFSET)) + begin + write_adr <= HEADER_OFFSET; + dsp_state <= DSP_WRITE_HEADER; + end + dsp_state <= DSP_WRITE_0; + end + + DSP_WRITE_0 : + begin + write_adr <= write_adr - 1; + if(write_adr == (hdr_length_reg+HEADER_OFFSET)) + begin + write_adr <= HEADER_OFFSET; + dsp_state <= DSP_WRITE_HEADER; + end + else + dsp_state <= DSP_READ; + end + + DSP_WRITE_HEADER : + dsp_state <= DSP_DONE; + + DSP_DONE : + begin + read_adr <= HEADER_OFFSET; + write_adr <= HEADER_OFFSET; + dsp_state <= DSP_IDLE; + end + endcase // case (dsp_state) + + assign access_skip_read = 0; + assign access_done = (dsp_state == DSP_DONE); + + assign access_stb = 1; + + assign access_we = (dsp_state == DSP_WRITE_HEADER) | + (dsp_state == DSP_WRITE_TRAILER) | + (dsp_state == DSP_WRITE_0) | + (dsp_state == DSP_WRITE_1); + + assign access_dat_o = (dsp_state == DSP_WRITE_HEADER) ? { 4'h0, new_header } : + (dsp_state == DSP_WRITE_TRAILER) ? { 4'h2, new_trailer } : + (dsp_state == DSP_WRITE_0) ? { 4'h0, i8_0, 8'd0, q8_0, 8'd0 } : + (dsp_state == DSP_WRITE_1) ? { 4'h0, i8_1, 8'd0, q8_1, 8'd0 } : + 34'h0DEADBEEF; + + assign access_adr = access_we ? write_adr : read_adr; + +endmodule // dspengine_16to8 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/duc_chain.v b/fpga/usrp2/sdr_lib/duc_chain.v new file mode 100644 index 000000000..bd3402a1f --- /dev/null +++ b/fpga/usrp2/sdr_lib/duc_chain.v @@ -0,0 +1,165 @@ +// +// Copyright 2011-2012 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/>. +// + +//! The USRP digital up-conversion chain + +module duc_chain + #( + parameter BASE = 0, + parameter DSPNO = 0, + parameter WIDTH = 24 + ) + (input clk, input rst, input clr, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input set_stb_user, input [7:0] set_addr_user, input [31:0] set_data_user, + + // To TX frontend + output [WIDTH-1:0] tx_fe_i, + output [WIDTH-1:0] tx_fe_q, + + // From TX control + input [31:0] sample, + input run, + output strobe, + output [31:0] debug + ); + + wire duc_enb; + wire [17:0] scale_factor; + wire [31:0] phase_inc; + reg [31:0] phase; + wire [7:0] interp_rate; + wire [3:0] tx_femux_a, tx_femux_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), .width(18)) sr_1 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(scale_factor),.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(duc_enb & ~rate_change),.rate(interp_rate), + .strobe_fast(1),.strobe_slow(strobe_cic_pre) ); + cic_strober #(.WIDTH(2)) + hb2_strober(.clock(clk),.reset(rst),.enable(duc_enb & ~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(duc_enb & ~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(~duc_enb) + phase <= 0; + else + phase <= phase + phase_inc; + + wire signed [17:0] da, db; + wire signed [35:0] prod_i, prod_q; + + wire [15:0] bb_i; + wire [15:0] bb_q; + 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(WIDTH)) hb_interp_i + (.clk(clk),.rst(rst),.bypass(~enable_hb1),.cpo(cpo),.stb_in(strobe_hb1),.data_in({bb_i, 2'b0}),.stb_out(strobe_hb2),.data_out(hb1_i)); + hb_interp #(.IWIDTH(18),.OWIDTH(18),.ACCWIDTH(WIDTH)) hb_interp_q + (.clk(clk),.rst(rst),.bypass(~enable_hb1),.cpo(cpo),.stb_in(strobe_hb1),.data_in({bb_q, 2'b0}),.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(duc_enb & ~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(duc_enb & ~rate_change),.rate(interp_rate), + .strobe_in(strobe_cic),.strobe_out(1), + .signal_in(hb2_q),.signal_out(q_interp)); + + localparam cwidth = WIDTH; // 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(duc_enb), + .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(scale_factor), // 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(scale_factor), // 18-bit multiplier input + .C(clk), // Clock input + .CE(1), // Clock enable input + .R(rst) // Synchronous reset input + ); + + dsp_tx_glue #(.DSPNO(DSPNO), .WIDTH(WIDTH)) dsp_tx_glue( + .clock(clk), .reset(rst), .clear(clr), .enable(run), + .set_stb(set_stb_user), .set_addr(set_addr_user), .set_data(set_data_user), + .frontend_i(tx_fe_i), .frontend_q(tx_fe_q), + .duc_out_i(prod_i[33:34-WIDTH]), .duc_out_q(prod_q[33:34-WIDTH]), + .duc_in_sample({bb_i, bb_q}), .duc_in_strobe(strobe_hb1), .duc_in_enable(duc_enb), + .bb_sample(sample), .bb_strobe(strobe)); + + assign debug = {strobe_cic, strobe_hb1, strobe_hb2,run}; + +endmodule // dsp_core diff --git a/fpga/usrp2/sdr_lib/dummy_rx.v b/fpga/usrp2/sdr_lib/dummy_rx.v new file mode 100644 index 000000000..42bbe36b2 --- /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 // ddc_chain 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..31b8a40e4 --- /dev/null +++ b/fpga/usrp2/sdr_lib/hb_dec.v @@ -0,0 +1,194 @@ +// +// 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 = 30; + + // 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 [35:0] acc_out; + acc #(.IWIDTH(36),.OWIDTH(36)) + acc (.clk(clk),.clear(clear),.acc(do_acc),.in(sum_of_prod),.out(acc_out)); + + wire [WIDTH:0] acc_out_rnd; + round #(.bits_in(36),.bits_out(WIDTH+1)) round_acc + (.in(acc_out), .out(acc_out_rnd)); + + wire [WIDTH:0] data_even_signext; + + localparam SHIFT_FACTOR = 17 - (36 - (WIDTH+1)); + + sign_extend #(.bits_in(INTWIDTH),.bits_out(WIDTH+1-SHIFT_FACTOR)) signext_data_even + (.in(data_even),.out(data_even_signext[WIDTH:SHIFT_FACTOR])); + assign data_even_signext[SHIFT_FACTOR-1:0] = 0; + + always @(posedge clk) final_sum <= acc_out_rnd + data_even_signext; + + clip #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip_finalsum + (.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/pipectrl.v b/fpga/usrp2/sdr_lib/pipectrl.v new file mode 100644 index 000000000..85d0ce04f --- /dev/null +++ b/fpga/usrp2/sdr_lib/pipectrl.v @@ -0,0 +1,66 @@ +// +// 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/>. +// + +// Control DSP pipeline with 1 cycle per stage. Minimum 2 stages or this won't work +module pipectrl + #(parameter STAGES = 2, + parameter TAGWIDTH = 1) + (input clk, + input reset, + input src_rdy_i, + output dst_rdy_o, + output src_rdy_o, + input dst_rdy_i, + output [STAGES-1:0] strobes, + output [STAGES-1:0] valids, + input [TAGWIDTH-1:0] tag_i, + output [TAGWIDTH-1:0] tag_o); + + wire new_input = src_rdy_i & dst_rdy_o; + wire new_output = src_rdy_o & dst_rdy_i; + wire [TAGWIDTH-1:0] tags [STAGES-1:0]; + + assign dst_rdy_o = ~valids[0] | strobes[1]; + + pipestage #(.TAGWIDTH(TAGWIDTH)) head + (.clk(clk),.reset(reset), .stb_in(strobes[0]), .stb_out(strobes[1]),.valid(valids[0]), + .tag_in(tag_i), .tag_out(tags[0])); + assign strobes[0] = src_rdy_i & (~valids[0] | strobes[1]); + + genvar i; + generate + for(i = 1; i < STAGES - 1; i = i + 1) + begin : gen_stages + pipestage #(.TAGWIDTH(TAGWIDTH)) pipestage + (.clk(clk),.reset(reset), .stb_in(strobes[i]),.stb_out(strobes[i+1]),.valid(valids[i]), + .tag_in(tags[i-1]),.tag_out(tags[i])); + assign strobes[i] = valids[i-1] & (~valids[i] | strobes[i+1]); + end + endgenerate + + pipestage #(.TAGWIDTH(TAGWIDTH)) tail + (.clk(clk),.reset(reset), .stb_in(strobes[STAGES-1]), .stb_out(dst_rdy_i),.valid(valids[STAGES-1]), + .tag_in(tags[STAGES-2]), .tag_out(tags[STAGES-1])); + assign strobes[STAGES-1] = valids[STAGES-2] & (~valids[STAGES-1] | new_output); + + assign src_rdy_o = valids[STAGES-1]; + + assign tag_o = tags[STAGES-1]; + +endmodule // pipectrl + + diff --git a/fpga/usrp2/sdr_lib/pipestage.v b/fpga/usrp2/sdr_lib/pipestage.v new file mode 100644 index 000000000..011afb1ba --- /dev/null +++ b/fpga/usrp2/sdr_lib/pipestage.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 pipestage + #(parameter TAGWIDTH = 1) + (input clk, + input reset, + input stb_in, + input stb_out, + output reg valid, + input [TAGWIDTH-1:0] tag_in, + output reg [TAGWIDTH-1:0] tag_out); + + always @(posedge clk) + if(reset) + begin + valid <= 0; + tag_out <= 0; + end + else if(stb_in) + begin + valid <= 1; + tag_out <= tag_in; + end + else if(stb_out) + begin + valid <= 0; + tag_out <= 0; + end + +endmodule // pipestage diff --git a/fpga/usrp2/sdr_lib/round.v b/fpga/usrp2/sdr_lib/round.v new file mode 100644 index 000000000..26d5a4cf4 --- /dev/null +++ b/fpga/usrp2/sdr_lib/round.v @@ -0,0 +1,59 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2011 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, + parameter round_to_zero=0, // original behavior + parameter round_to_nearest=1, // lowest noise + parameter trunc=0) // round to negative infinity + (input [bits_in-1:0] in, + output [bits_out-1:0] out, + output [bits_in-bits_out:0] err); + + wire round_corr,round_corr_trunc,round_corr_rtz,round_corr_nearest,round_corr_nearest_safe; + + assign round_corr_trunc = 0; + assign round_corr_rtz = (in[bits_in-1] & |in[bits_in-bits_out-1:0]); + assign round_corr_nearest = in[bits_in-bits_out-1]; + + generate + if(bits_in-bits_out > 1) + assign round_corr_nearest_safe = (~in[bits_in-1] & (&in[bits_in-2:bits_out])) ? 0 : + round_corr_nearest; + else + assign round_corr_nearest_safe = round_corr_nearest; + endgenerate + + + assign round_corr = round_to_nearest ? round_corr_nearest_safe : + trunc ? round_corr_trunc : + round_to_zero ? round_corr_rtz : + 0; // default to trunc + + assign out = in[bits_in-1:bits_in-bits_out] + round_corr; + + 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..94584f6ef --- /dev/null +++ b/fpga/usrp2/sdr_lib/round_sd.v @@ -0,0 +1,23 @@ + + +module round_sd + #(parameter WIDTH_IN=18, + parameter WIDTH_OUT=16, + parameter DISABLE_SD=0) + (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((DISABLE_SD == 0) ? err_ext : 0), .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/round_tb.v b/fpga/usrp2/sdr_lib/round_tb.v new file mode 100644 index 000000000..ddc464f4a --- /dev/null +++ b/fpga/usrp2/sdr_lib/round_tb.v @@ -0,0 +1,61 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2011 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_tb(); + + localparam IW=8; + localparam OW=4; + localparam EW=IW-OW+1; + + reg signed [IW-1:0] in; + wire signed [OW-1:0] out; + wire signed [EW-1:0] err; + + round #(.bits_in(IW), + .bits_out(OW), + .round_to_zero(0), // original behavior + .round_to_nearest(1), // lowest noise + .trunc(0)) // round to negative infinity + round (.in(in),.out(out),.err(err)); + + initial $dumpfile("round_tb.vcd"); + initial $dumpvars(0,round_tb); + + wire signed [IW-1:0] out_round = {out,{IW-OW{1'b0}}}; + + initial + begin + in <= -129; + #1; + repeat (260) + begin + in <= in + 1; + #1; + $display("In %d, out %d, out_rnd %d, err %d, real err %d",in,out,out_round,-err,out_round-in); + #1; + end + $finish; + end + +endmodule // round 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..04d7795c0 --- /dev/null +++ b/fpga/usrp2/sdr_lib/rx_dcoffset.v @@ -0,0 +1,58 @@ +// +// 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=20) + (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 + fixed <= set_data[31]; + if(set_data[30]) + integrator <= {set_data[29:0],{(int_width-30){1'b0}}}; + 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..4b626f5a7 --- /dev/null +++ b/fpga/usrp2/sdr_lib/rx_frontend.v @@ -0,0 +1,84 @@ + +module rx_frontend + #(parameter BASE = 0, + parameter IQCOMP_EN = 1) + (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 + reg [17:0] adc_i_ofs_dly, adc_q_ofs_dly; + always @(posedge clk) begin + adc_i_ofs_dly <= adc_i_ofs; + adc_q_ofs_dly <= adc_q_ofs; + end + + 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_dly,6'd0}), .in2(corr_i[35:12]), .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_dly,6'd0}), .in2(corr_q[35:12]), .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..f4f927b22 --- /dev/null +++ b/fpga/usrp2/sdr_lib/small_hb_dec.v @@ -0,0 +1,141 @@ +// +// 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; + + wire [ACCWIDTH-1:0] prod_acc_rnd; + round #(.bits_in(36),.bits_out(ACCWIDTH), + .round_to_zero(1),.round_to_nearest(0),.trunc(0)) round_prod + (.in(prod), .out(prod_acc_rnd)); + + 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_acc_rnd; + else if(go_d3) + accum <= accum + prod_acc_rnd; + + 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..b69c45413 --- /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),.strobe_in(1'b1), .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..b4ca38310 --- /dev/null +++ b/fpga/usrp2/sdr_lib/tx_frontend.v @@ -0,0 +1,107 @@ + +module tx_frontend + #(parameter BASE=0, + parameter WIDTH_OUT=16, + parameter IQCOMP_EN=1) + (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 + reg [23:0] tx_i_dly, tx_q_dly; + always @(posedge clk) begin + tx_i_dly <= tx_i; + tx_q_dly <= tx_q; + end + + // 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_dly), .in2(corr_i[35:12]), .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_dly), .in2(corr_q[35:12]), .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 |