aboutsummaryrefslogtreecommitdiffstats
path: root/sdr_lib/hb
diff options
context:
space:
mode:
Diffstat (limited to 'sdr_lib/hb')
-rw-r--r--sdr_lib/hb/acc.v22
-rw-r--r--sdr_lib/hb/coeff_ram.v26
-rw-r--r--sdr_lib/hb/coeff_rom.v19
-rw-r--r--sdr_lib/hb/halfband_decim.v163
-rw-r--r--sdr_lib/hb/halfband_interp.v121
-rw-r--r--sdr_lib/hb/hbd_tb/HBD80
-rw-r--r--sdr_lib/hb/hbd_tb/really_golden142
-rw-r--r--sdr_lib/hb/hbd_tb/regression95
-rwxr-xr-xsdr_lib/hb/hbd_tb/run_hbd4
-rw-r--r--sdr_lib/hb/hbd_tb/test_hbd.v75
-rw-r--r--sdr_lib/hb/mac.v58
-rw-r--r--sdr_lib/hb/mult.v16
-rw-r--r--sdr_lib/hb/ram16_2port.v22
-rw-r--r--sdr_lib/hb/ram16_2sum.v27
-rw-r--r--sdr_lib/hb/ram32_2sum.v22
15 files changed, 892 insertions, 0 deletions
diff --git a/sdr_lib/hb/acc.v b/sdr_lib/hb/acc.v
new file mode 100644
index 000000000..195d5ea94
--- /dev/null
+++ b/sdr_lib/hb/acc.v
@@ -0,0 +1,22 @@
+
+
+module acc (input clock, input reset, input clear, input enable_in, output reg enable_out,
+ input signed [30:0] addend, output reg signed [33:0] sum );
+
+ always @(posedge clock)
+ if(reset)
+ sum <= #1 34'd0;
+ //else if(clear & enable_in)
+ // sum <= #1 addend;
+ //else if(clear)
+ // sum <= #1 34'd0;
+ else if(clear)
+ sum <= #1 addend;
+ else if(enable_in)
+ sum <= #1 sum + addend;
+
+ always @(posedge clock)
+ enable_out <= #1 enable_in;
+
+endmodule // acc
+
diff --git a/sdr_lib/hb/coeff_ram.v b/sdr_lib/hb/coeff_ram.v
new file mode 100644
index 000000000..65460822f
--- /dev/null
+++ b/sdr_lib/hb/coeff_ram.v
@@ -0,0 +1,26 @@
+
+
+module coeff_ram (input clock, input [3:0] rd_addr, output reg [15:0] rd_data);
+
+ always @(posedge clock)
+ case (rd_addr)
+ 4'd0 : rd_data <= #1 -16'd16;
+ 4'd1 : rd_data <= #1 16'd74;
+ 4'd2 : rd_data <= #1 -16'd254;
+ 4'd3 : rd_data <= #1 16'd669;
+ 4'd4 : rd_data <= #1 -16'd1468;
+ 4'd5 : rd_data <= #1 16'd2950;
+ 4'd6 : rd_data <= #1 -16'd6158;
+ 4'd7 : rd_data <= #1 16'd20585;
+ 4'd8 : rd_data <= #1 16'd20585;
+ 4'd9 : rd_data <= #1 -16'd6158;
+ 4'd10 : rd_data <= #1 16'd2950;
+ 4'd11 : rd_data <= #1 -16'd1468;
+ 4'd12 : rd_data <= #1 16'd669;
+ 4'd13 : rd_data <= #1 -16'd254;
+ 4'd14 : rd_data <= #1 16'd74;
+ 4'd15 : rd_data <= #1 -16'd16;
+ default : rd_data <= #1 16'd0;
+ endcase // case(rd_addr)
+
+endmodule // ram
diff --git a/sdr_lib/hb/coeff_rom.v b/sdr_lib/hb/coeff_rom.v
new file mode 100644
index 000000000..7f8886b4e
--- /dev/null
+++ b/sdr_lib/hb/coeff_rom.v
@@ -0,0 +1,19 @@
+
+
+module coeff_rom (input clock, input [2:0] addr, output reg [15:0] data);
+
+ always @(posedge clock)
+ case (addr)
+ 3'd0 : data <= #1 -16'd49;
+ 3'd1 : data <= #1 16'd165;
+ 3'd2 : data <= #1 -16'd412;
+ 3'd3 : data <= #1 16'd873;
+ 3'd4 : data <= #1 -16'd1681;
+ 3'd5 : data <= #1 16'd3135;
+ 3'd6 : data <= #1 -16'd6282;
+ 3'd7 : data <= #1 16'd20628;
+ endcase // case(addr)
+
+endmodule // coeff_rom
+
+
diff --git a/sdr_lib/hb/halfband_decim.v b/sdr_lib/hb/halfband_decim.v
new file mode 100644
index 000000000..dff4d902c
--- /dev/null
+++ b/sdr_lib/hb/halfband_decim.v
@@ -0,0 +1,163 @@
+/* -*- verilog -*-
+ *
+ * USRP - Universal Software Radio Peripheral
+ *
+ * Copyright (C) 2005 Matt Ettus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * This implements a 31-tap halfband filter that decimates by two.
+ * The coefficients are symmetric, and with the exception of the middle tap,
+ * every other coefficient is zero. The middle section of taps looks like this:
+ *
+ * ..., -1468, 0, 2950, 0, -6158, 0, 20585, 32768, 20585, 0, -6158, 0, 2950, 0, -1468, ...
+ * |
+ * middle tap -------+
+ *
+ * See coeff_rom.v for the full set. The taps are scaled relative to 32768,
+ * thus the middle tap equals 1.0. Not counting the middle tap, there are 8
+ * non-zero taps on each side, and they are symmetric. A naive implementation
+ * requires a mulitply for each non-zero tap. Because of symmetry, we can
+ * replace 2 multiplies with 1 add and 1 multiply. Thus, to compute each output
+ * sample, we need to perform 8 multiplications. Since the middle tap is 1.0,
+ * we just add the corresponding delay line value.
+ *
+ * About timing: We implement this with a single multiplier, so it takes
+ * 8 cycles to compute a single output. However, since we're decimating by two
+ * we can accept a new input value every 4 cycles. strobe_in is asserted when
+ * there's a new input sample available. Depending on the overall decimation
+ * rate, strobe_in may be asserted less frequently than once every 4 clocks.
+ * On the output side, we assert strobe_out when output contains a new sample.
+ *
+ * Implementation: Every time strobe_in is asserted we store the new data into
+ * the delay line. We split the delay line into two components, one for the
+ * even samples, and one for the odd samples. ram16_odd is the delay line for
+ * the odd samples. This ram is written on each odd assertion of strobe_in, and
+ * is read on each clock when we're computing the dot product. ram16_even is
+ * similar, although because it holds the even samples we must be able to read
+ * two samples from different addresses at the same time, while writing the incoming
+ * even samples. Thus it's "triple-ported".
+ */
+
+module halfband_decim
+ (input clock, input reset, input enable, input strobe_in, output wire strobe_out,
+ input wire [15:0] data_in, output reg [15:0] data_out,output wire [15:0] debugctrl);
+
+ reg [3:0] rd_addr1;
+ reg [3:0] rd_addr2;
+ reg [3:0] phase;
+ reg [3:0] base_addr;
+
+ wire signed [15:0] mac_out,middle_data, sum, coeff;
+ wire signed [30:0] product;
+ wire signed [33:0] sum_even;
+ wire clear;
+ reg store_odd;
+
+ always @(posedge clock)
+ if(reset)
+ store_odd <= #1 1'b0;
+ else
+ if(strobe_in)
+ store_odd <= #1 ~store_odd;
+
+ wire start = strobe_in & store_odd;
+ always @(posedge clock)
+ if(reset)
+ base_addr <= #1 4'd0;
+ else if(start)
+ base_addr <= #1 base_addr + 4'd1;
+
+ always @(posedge clock)
+ if(reset)
+ phase <= #1 4'd8;
+ else if (start)
+ phase <= #1 4'd0;
+ else if(phase != 4'd8)
+ phase <= #1 phase + 4'd1;
+
+ reg start_d1,start_d2,start_d3,start_d4,start_d5,start_d6,start_d7,start_d8,start_d9,start_dA,start_dB,start_dC,start_dD;
+ always @(posedge clock)
+ begin
+ start_d1 <= #1 start;
+ start_d2 <= #1 start_d1;
+ start_d3 <= #1 start_d2;
+ start_d4 <= #1 start_d3;
+ start_d5 <= #1 start_d4;
+ start_d6 <= #1 start_d5;
+ start_d7 <= #1 start_d6;
+ start_d8 <= #1 start_d7;
+ start_d9 <= #1 start_d8;
+ start_dA <= #1 start_d9;
+ start_dB <= #1 start_dA;
+ start_dC <= #1 start_dB;
+ start_dD <= #1 start_dC;
+ end // always @ (posedge clock)
+
+ reg mult_en, mult_en_pre;
+ always @(posedge clock)
+ begin
+ mult_en_pre <= #1 phase!=8;
+ mult_en <= #1 mult_en_pre;
+ end
+
+ assign clear = start_d4; // was dC
+ wire latch_result = start_d4; // was dC
+ assign strobe_out = start_d5; // was dD
+ wire acc_en;
+
+ always @*
+ case(phase[2:0])
+ 3'd0 : begin rd_addr1 = base_addr + 4'd0; rd_addr2 = base_addr + 4'd15; end
+ 3'd1 : begin rd_addr1 = base_addr + 4'd1; rd_addr2 = base_addr + 4'd14; end
+ 3'd2 : begin rd_addr1 = base_addr + 4'd2; rd_addr2 = base_addr + 4'd13; end
+ 3'd3 : begin rd_addr1 = base_addr + 4'd3; rd_addr2 = base_addr + 4'd12; end
+ 3'd4 : begin rd_addr1 = base_addr + 4'd4; rd_addr2 = base_addr + 4'd11; end
+ 3'd5 : begin rd_addr1 = base_addr + 4'd5; rd_addr2 = base_addr + 4'd10; end
+ 3'd6 : begin rd_addr1 = base_addr + 4'd6; rd_addr2 = base_addr + 4'd9; end
+ 3'd7 : begin rd_addr1 = base_addr + 4'd7; rd_addr2 = base_addr + 4'd8; end
+ default: begin rd_addr1 = base_addr + 4'd0; rd_addr2 = base_addr + 4'd15; end
+ endcase // case(phase)
+
+ coeff_rom coeff_rom (.clock(clock),.addr(phase[2:0]-3'd1),.data(coeff));
+
+ ram16_2sum ram16_even (.clock(clock),.write(strobe_in & ~store_odd),
+ .wr_addr(base_addr),.wr_data(data_in),
+ .rd_addr1(rd_addr1),.rd_addr2(rd_addr2),
+ .sum(sum));
+
+ ram16 ram16_odd (.clock(clock),.write(strobe_in & store_odd), // Holds middle items
+ .wr_addr(base_addr),.wr_data(data_in),
+ //.rd_addr(base_addr+4'd7),.rd_data(middle_data));
+ .rd_addr(base_addr+4'd6),.rd_data(middle_data));
+
+ mult mult(.clock(clock),.x(coeff),.y(sum),.product(product),.enable_in(mult_en),.enable_out(acc_en));
+
+ acc acc(.clock(clock),.reset(reset),.enable_in(acc_en),.enable_out(),
+ .clear(clear),.addend(product),.sum(sum_even));
+
+ wire signed [33:0] dout = sum_even + {{4{middle_data[15]}},middle_data,14'b0}; // We already divided product by 2!!!!
+
+ always @(posedge clock)
+ if(reset)
+ data_out <= #1 16'd0;
+ else if(latch_result)
+ data_out <= #1 dout[30:15] + (dout[33]& |dout[14:0]);
+
+ assign debugctrl = { clock,reset,acc_en,mult_en,clear,latch_result,store_odd,strobe_in,strobe_out,phase};
+
+endmodule // halfband_decim
diff --git a/sdr_lib/hb/halfband_interp.v b/sdr_lib/hb/halfband_interp.v
new file mode 100644
index 000000000..cdb11c1f6
--- /dev/null
+++ b/sdr_lib/hb/halfband_interp.v
@@ -0,0 +1,121 @@
+
+
+module halfband_interp
+ (input clock, input reset, input enable,
+ input strobe_in, input strobe_out,
+ input [15:0] signal_in_i, input [15:0] signal_in_q,
+ output reg [15:0] signal_out_i, output reg [15:0] signal_out_q,
+ output wire [12:0] debug);
+
+ wire [15:0] coeff_ram_out;
+ wire [15:0] data_ram_out_i;
+ wire [15:0] data_ram_out_q;
+
+ wire [3:0] data_rd_addr;
+ reg [3:0] data_wr_addr;
+ reg [2:0] coeff_rd_addr;
+
+ wire filt_done;
+
+ wire [15:0] mac_out_i;
+ wire [15:0] mac_out_q;
+ reg [15:0] delayed_middle_i, delayed_middle_q;
+ wire [7:0] shift = 8'd9;
+
+ reg stb_out_happened;
+
+ wire [15:0] data_ram_out_i_b;
+
+ always @(posedge clock)
+ if(strobe_in)
+ stb_out_happened <= #1 1'b0;
+ else if(strobe_out)
+ stb_out_happened <= #1 1'b1;
+
+assign debug = {filt_done,data_rd_addr,data_wr_addr,coeff_rd_addr};
+
+ wire [15:0] signal_out_i = stb_out_happened ? mac_out_i : delayed_middle_i;
+ wire [15:0] signal_out_q = stb_out_happened ? mac_out_q : delayed_middle_q;
+
+/* always @(posedge clock)
+ if(reset)
+ begin
+ signal_out_i <= #1 16'd0;
+ signal_out_q <= #1 16'd0;
+ end
+ else if(strobe_in)
+ begin
+ signal_out_i <= #1 delayed_middle_i; // Multiply by 1 for middle coeff
+ signal_out_q <= #1 delayed_middle_q;
+ end
+ //else if(filt_done&stb_out_happened)
+ else if(stb_out_happened)
+ begin
+ signal_out_i <= #1 mac_out_i;
+ signal_out_q <= #1 mac_out_q;
+ end
+*/
+
+ always @(posedge clock)
+ if(reset)
+ coeff_rd_addr <= #1 3'd0;
+ else if(coeff_rd_addr != 3'd0)
+ coeff_rd_addr <= #1 coeff_rd_addr + 3'd1;
+ else if(strobe_in)
+ coeff_rd_addr <= #1 3'd1;
+
+ reg filt_done_d1;
+ always@(posedge clock)
+ filt_done_d1 <= #1 filt_done;
+
+ always @(posedge clock)
+ if(reset)
+ data_wr_addr <= #1 4'd0;
+ //else if(strobe_in)
+ else if(filt_done & ~filt_done_d1)
+ data_wr_addr <= #1 data_wr_addr + 4'd1;
+
+ always @(posedge clock)
+ if(coeff_rd_addr == 3'd7)
+ begin
+ delayed_middle_i <= #1 data_ram_out_i_b;
+ // delayed_middle_q <= #1 data_ram_out_q_b;
+ end
+
+// always @(posedge clock)
+// if(reset)
+// data_rd_addr <= #1 4'd0;
+// else if(strobe_in)
+// data_rd_addr <= #1 data_wr_addr + 4'd1;
+// else if(!filt_done)
+// data_rd_addr <= #1 data_rd_addr + 4'd1;
+// else
+// data_rd_addr <= #1 data_wr_addr;
+
+ wire [3:0] data_rd_addr1 = data_wr_addr + {1'b0,coeff_rd_addr};
+ wire [3:0] data_rd_addr2 = data_wr_addr + 15 - {1'b0,coeff_rd_addr};
+// always @(posedge clock)
+// if(reset)
+// filt_done <= #1 1'b1;
+// else if(strobe_in)
+ // filt_done <= #1 1'b0;
+// else if(coeff_rd_addr == 4'd0)
+// filt_done <= #1 1'b1;
+
+ assign filt_done = (coeff_rd_addr == 3'd0);
+
+ coeff_ram coeff_ram ( .clock(clock),.rd_addr({1'b0,coeff_rd_addr}),.rd_data(coeff_ram_out) );
+
+ ram16_2sum data_ram_i ( .clock(clock),.write(strobe_in),.wr_addr(data_wr_addr),.wr_data(signal_in_i),
+ .rd_addr1(data_rd_addr1),.rd_addr2(data_rd_addr2),.rd_data(data_ram_out_i_b),.sum(data_ram_out_i));
+
+ ram16_2sum data_ram_q ( .clock(clock),.write(strobe_in),.wr_addr(data_wr_addr),.wr_data(signal_in_q),
+ .rd_addr1(data_rd_addr1),.rd_addr2(data_rd_addr2),.rd_data(data_ram_out_q));
+
+ mac mac_i (.clock(clock),.reset(reset),.enable(~filt_done),.clear(strobe_in),
+ .x(data_ram_out_i),.y(coeff_ram_out),.shift(shift),.z(mac_out_i) );
+
+ mac mac_q (.clock(clock),.reset(reset),.enable(~filt_done),.clear(strobe_in),
+ .x(data_ram_out_q),.y(coeff_ram_out),.shift(shift),.z(mac_out_q) );
+
+endmodule // halfband_interp
diff --git a/sdr_lib/hb/hbd_tb/HBD b/sdr_lib/hb/hbd_tb/HBD
new file mode 100644
index 000000000..574fbba91
--- /dev/null
+++ b/sdr_lib/hb/hbd_tb/HBD
@@ -0,0 +1,80 @@
+*-6.432683 5736 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+@28
+test_hbd.clock
+test_hbd.reset
+@420
+test_hbd.halfband_decim.middle_data[15:0]
+@22
+test_hbd.halfband_decim.sum_even[33:0]
+test_hbd.halfband_decim.base_addr[3:0]
+@420
+test_hbd.i_in[15:0]
+@24
+test_hbd.halfband_decim.phase[3:0]
+test_hbd.halfband_decim.ram16_even.rd_addr1[3:0]
+test_hbd.halfband_decim.ram16_even.rd_addr2[3:0]
+test_hbd.halfband_decim.ram16_even.wr_addr[3:0]
+test_hbd.halfband_decim.ram16_even.wr_data[15:0]
+@28
+test_hbd.halfband_decim.ram16_even.write
+@420
+test_hbd.halfband_decim.sum[15:0]
+test_hbd.halfband_decim.product[30:0]
+test_hbd.halfband_decim.dout[33:0]
+test_hbd.halfband_decim.sum_even[33:0]
+@22
+test_hbd.halfband_decim.acc.addend[30:0]
+@28
+test_hbd.halfband_decim.acc.reset
+@420
+test_hbd.halfband_decim.acc.sum[33:0]
+test_hbd.halfband_decim.mult.x[15:0]
+test_hbd.halfband_decim.mult.y[15:0]
+@28
+test_hbd.halfband_decim.acc.clear
+test_hbd.strobe_in
+test_hbd.strobe_out
+test_hbd.halfband_decim.acc_en
+@420
+test_hbd.i_out[15:0]
+@28
+test_hbd.halfband_decim.mult_en
+test_hbd.halfband_decim.latch_result
+@420
+test_hbd.halfband_decim.sum[15:0]
+test_hbd.halfband_decim.sum_even[33:0]
+test_hbd.halfband_decim.dout[33:0]
+test_hbd.halfband_decim.data_out[15:0]
+@22
+test_hbd.halfband_decim.data_out[15:0]
+@28
+test_hbd.halfband_decim.dout[33:0]
+@29
+test_hbd.halfband_decim.acc_en
+@22
+test_hbd.halfband_decim.base_addr[3:0]
+@28
+test_hbd.halfband_decim.clear
+test_hbd.halfband_decim.latch_result
+test_hbd.halfband_decim.mult_en
+test_hbd.halfband_decim.mult_en_pre
+@22
+test_hbd.halfband_decim.phase[3:0]
+@28
+test_hbd.halfband_decim.start
+test_hbd.halfband_decim.start_d1
+test_hbd.halfband_decim.start_d2
+test_hbd.halfband_decim.start_d3
+test_hbd.halfband_decim.start_d4
+test_hbd.halfband_decim.start_d5
+test_hbd.halfband_decim.start_d6
+test_hbd.halfband_decim.start_d7
+test_hbd.halfband_decim.start_d8
+test_hbd.halfband_decim.start_d9
+test_hbd.halfband_decim.start_dA
+test_hbd.halfband_decim.start_dB
+test_hbd.halfband_decim.start_dC
+test_hbd.halfband_decim.start_dD
+test_hbd.halfband_decim.store_odd
+test_hbd.halfband_decim.strobe_in
+test_hbd.halfband_decim.strobe_out
diff --git a/sdr_lib/hb/hbd_tb/really_golden b/sdr_lib/hb/hbd_tb/really_golden
new file mode 100644
index 000000000..2d24a9e14
--- /dev/null
+++ b/sdr_lib/hb/hbd_tb/really_golden
@@ -0,0 +1,142 @@
+VCD info: dumpfile test_hbd.vcd opened for output.
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ x
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 8192
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+- 4
+ 18
+- 63
+ 167
+- 367
+ 737
+- 1539
+ 5146
+ 5146
+- 1539
+ 737
+- 367
+ 167
+- 63
+ 18
+- 4
+ 0
+ 0
+ 0
+ 0
+ 0
+- 4
+ 14
+- 49
+ 118
+- 249
+ 488
+ 7141
+12287
+17433
+15894
+16631
+16264
+16432
+16368
+16387
+16383
+16383
+16383
+16383
+16383
+16387
+16368
+16432
+16264
+16631
+15894
+ 9241
+ 4095
+- 1051
+ 488
+- 249
+ 118
+- 49
+ 14
+- 4
+ 0
+ 0
+ 0
+ 0
+ 0
+- 4
+ 14
+- 49
+ 118
+- 249
+ 488
+- 1051
+12287
+17433
+15894
+16631
+16264
+16432
+16368
+16387
+16383
+16383
+16383
+16383
+16383
+16387
+16368
+16432
+16264
+16631
+15894
+17433
+ 4095
+- 1051
+ 488
+- 249
+ 118
+- 49
+ 14
+- 4
+ 0
+ 0
+ 0
+ 0
diff --git a/sdr_lib/hb/hbd_tb/regression b/sdr_lib/hb/hbd_tb/regression
new file mode 100644
index 000000000..fc279c2f2
--- /dev/null
+++ b/sdr_lib/hb/hbd_tb/regression
@@ -0,0 +1,95 @@
+echo "Baseline 1000"
+iverilog -y .. -o test_hbd -DRATE=1000 test_hbd.v ; ./test_hbd >golden
+diff golden really_golden
+
+echo
+echo "Test 100"
+iverilog -y .. -o test_hbd -DRATE=100 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 50"
+iverilog -y .. -o test_hbd -DRATE=50 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 40"
+iverilog -y .. -o test_hbd -DRATE=40 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 30"
+iverilog -y .. -o test_hbd -DRATE=30 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 25"
+iverilog -y .. -o test_hbd -DRATE=25 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 20"
+iverilog -y .. -o test_hbd -DRATE=20 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 19"
+iverilog -y .. -o test_hbd -DRATE=19 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 18"
+iverilog -y .. -o test_hbd -DRATE=18 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 17"
+iverilog -y .. -o test_hbd -DRATE=17 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 16"
+iverilog -y .. -o test_hbd -DRATE=16 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 15"
+iverilog -y .. -o test_hbd -DRATE=15 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 14"
+iverilog -y .. -o test_hbd -DRATE=14 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 13"
+iverilog -y .. -o test_hbd -DRATE=13 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 12"
+iverilog -y .. -o test_hbd -DRATE=12 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 11"
+iverilog -y .. -o test_hbd -DRATE=11 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 10"
+iverilog -y .. -o test_hbd -DRATE=10 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 9"
+iverilog -y .. -o test_hbd -DRATE=9 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 8"
+iverilog -y .. -o test_hbd -DRATE=8 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 7"
+iverilog -y .. -o test_hbd -DRATE=7 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 6"
+iverilog -y .. -o test_hbd -DRATE=6 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 5"
+iverilog -y .. -o test_hbd -DRATE=5 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 4"
+iverilog -y .. -o test_hbd -DRATE=4 test_hbd.v ; ./test_hbd >output ; diff output golden
+
+echo
+echo "Test 3"
+iverilog -y .. -o test_hbd -DRATE=3 test_hbd.v ; ./test_hbd >output ; diff output golden
diff --git a/sdr_lib/hb/hbd_tb/run_hbd b/sdr_lib/hb/hbd_tb/run_hbd
new file mode 100755
index 000000000..b8aec7574
--- /dev/null
+++ b/sdr_lib/hb/hbd_tb/run_hbd
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+iverilog -y .. -o test_hbd test_hbd.v
+./test_hbd
diff --git a/sdr_lib/hb/hbd_tb/test_hbd.v b/sdr_lib/hb/hbd_tb/test_hbd.v
new file mode 100644
index 000000000..01ab5e7e0
--- /dev/null
+++ b/sdr_lib/hb/hbd_tb/test_hbd.v
@@ -0,0 +1,75 @@
+
+
+module test_hbd();
+
+ reg clock;
+ initial clock = 1'b0;
+ always #5 clock <= ~clock;
+
+ reg reset;
+ initial reset = 1'b1;
+ initial #1000 reset = 1'b0;
+
+ initial $dumpfile("test_hbd.vcd");
+ initial $dumpvars(0,test_hbd);
+
+ reg [15:0] i_in, q_in;
+ wire [15:0] i_out, q_out;
+
+ reg strobe_in;
+ wire strobe_out;
+ reg coeff_write;
+ reg [15:0] coeff_data;
+ reg [4:0] coeff_addr;
+
+ halfband_decim halfband_decim
+ ( .clock(clock),.reset(reset),.enable(),.strobe_in(strobe_in),.strobe_out(strobe_out),
+ .data_in(i_in),.data_out(i_out) );
+
+ always @(posedge strobe_out)
+ if(i_out[15])
+ $display("-%d",65536-i_out);
+ else
+ $display("%d",i_out);
+
+ initial
+ begin
+ strobe_in = 1'b0;
+ @(negedge reset);
+ @(posedge clock);
+ while(1)
+ begin
+ strobe_in <= #1 1'b1;
+ @(posedge clock);
+ strobe_in <= #1 1'b0;
+ repeat (`RATE)
+ @(posedge clock);
+ end
+ end
+
+ initial #10000000 $finish; // Just in case...
+
+ initial
+ begin
+ i_in <= #1 16'd0;
+ repeat (40) @(posedge strobe_in);
+ i_in <= #1 16'd16384;
+ @(posedge strobe_in);
+ i_in <= #1 16'd0;
+ repeat (40) @(posedge strobe_in);
+ i_in <= #1 16'd16384;
+ @(posedge strobe_in);
+ i_in <= #1 16'd0;
+ repeat (40) @(posedge strobe_in);
+ i_in <= #1 16'd16384;
+ repeat (40) @(posedge strobe_in);
+ i_in <= #1 16'd0;
+ repeat (41) @(posedge strobe_in);
+ i_in <= #1 16'd16384;
+ repeat (40) @(posedge strobe_in);
+ i_in <= #1 16'd0;
+ repeat (40) @(posedge strobe_in);
+ repeat (7) @(posedge clock);
+ $finish;
+ end // initial begin
+endmodule // test_hb
diff --git a/sdr_lib/hb/mac.v b/sdr_lib/hb/mac.v
new file mode 100644
index 000000000..5a270bc73
--- /dev/null
+++ b/sdr_lib/hb/mac.v
@@ -0,0 +1,58 @@
+
+
+module mac (input clock, input reset, input enable, input clear,
+ input signed [15:0] x, input signed [15:0] y,
+ input [7:0] shift, output [15:0] z );
+
+ reg signed [30:0] product;
+ reg signed [39:0] z_int;
+ reg signed [15:0] z_shift;
+
+ reg enable_d1;
+ always @(posedge clock)
+ enable_d1 <= #1 enable;
+
+ always @(posedge clock)
+ if(reset | clear)
+ z_int <= #1 40'd0;
+ else if(enable_d1)
+ z_int <= #1 z_int + {{9{product[30]}},product};
+
+ always @(posedge clock)
+ product <= #1 x*y;
+
+ always @* // FIXME full case? parallel case?
+ case(shift)
+ //8'd0 : z_shift <= z_int[39:24];
+ //8'd1 : z_shift <= z_int[38:23];
+ //8'd2 : z_shift <= z_int[37:22];
+ //8'd3 : z_shift <= z_int[36:21];
+ //8'd4 : z_shift <= z_int[35:20];
+ //8'd5 : z_shift <= z_int[34:19];
+ 8'd6 : z_shift <= z_int[33:18];
+ 8'd7 : z_shift <= z_int[32:17];
+ 8'd8 : z_shift <= z_int[31:16];
+ 8'd9 : z_shift <= z_int[30:15];
+ 8'd10 : z_shift <= z_int[29:14];
+ 8'd11 : z_shift <= z_int[28:13];
+ //8'd12 : z_shift <= z_int[27:12];
+ //8'd13 : z_shift <= z_int[26:11];
+ //8'd14 : z_shift <= z_int[25:10];
+ //8'd15 : z_shift <= z_int[24:9];
+ //8'd16 : z_shift <= z_int[23:8];
+ //8'd17 : z_shift <= z_int[22:7];
+ //8'd18 : z_shift <= z_int[21:6];
+ //8'd19 : z_shift <= z_int[20:5];
+ //8'd20 : z_shift <= z_int[19:4];
+ //8'd21 : z_shift <= z_int[18:3];
+ //8'd22 : z_shift <= z_int[17:2];
+ //8'd23 : z_shift <= z_int[16:1];
+ //8'd24 : z_shift <= z_int[15:0];
+ default : z_shift <= z_int[15:0];
+ endcase // case(shift)
+
+ // FIXME do we need to saturate?
+ //assign z = z_shift;
+ assign z = z_int[15:0];
+
+endmodule // mac
diff --git a/sdr_lib/hb/mult.v b/sdr_lib/hb/mult.v
new file mode 100644
index 000000000..a8d4cb1b7
--- /dev/null
+++ b/sdr_lib/hb/mult.v
@@ -0,0 +1,16 @@
+
+
+module mult (input clock, input signed [15:0] x, input signed [15:0] y, output reg signed [30:0] product,
+ input enable_in, output reg enable_out );
+
+ always @(posedge clock)
+ if(enable_in)
+ product <= #1 x*y;
+ else
+ product <= #1 31'd0;
+
+ always @(posedge clock)
+ enable_out <= #1 enable_in;
+
+endmodule // mult
+
diff --git a/sdr_lib/hb/ram16_2port.v b/sdr_lib/hb/ram16_2port.v
new file mode 100644
index 000000000..e1761a926
--- /dev/null
+++ b/sdr_lib/hb/ram16_2port.v
@@ -0,0 +1,22 @@
+
+
+module ram16_2port (input clock, input write,
+ input [3:0] wr_addr, input [15:0] wr_data,
+ input [3:0] rd_addr1, output reg [15:0] rd_data1,
+ input [3:0] rd_addr2, output reg [15:0] rd_data2);
+
+ reg [15:0] ram_array [0:31];
+
+ always @(posedge clock)
+ rd_data1 <= #1 ram_array[rd_addr1];
+
+ always @(posedge clock)
+ rd_data2 <= #1 ram_array[rd_addr2];
+
+ always @(posedge clock)
+ if(write)
+ ram_array[wr_addr] <= #1 wr_data;
+
+endmodule // ram16_2port
+
+
diff --git a/sdr_lib/hb/ram16_2sum.v b/sdr_lib/hb/ram16_2sum.v
new file mode 100644
index 000000000..559b06fd5
--- /dev/null
+++ b/sdr_lib/hb/ram16_2sum.v
@@ -0,0 +1,27 @@
+
+
+module ram16_2sum (input clock, input write,
+ input [3:0] wr_addr, input [15:0] wr_data,
+ input [3:0] rd_addr1, input [3:0] rd_addr2,
+ output reg [15:0] sum);
+
+ reg signed [15:0] ram_array [0:15];
+ reg signed [15:0] a,b;
+ wire signed [16:0] sum_int;
+
+ always @(posedge clock)
+ if(write)
+ ram_array[wr_addr] <= #1 wr_data;
+
+ always @(posedge clock)
+ begin
+ a <= #1 ram_array[rd_addr1];
+ b <= #1 ram_array[rd_addr2];
+ end
+
+ assign sum_int = {a[15],a} + {b[15],b};
+
+ always @(posedge clock)
+ sum <= #1 sum_int[16:1] + (sum_int[16]&sum_int[0]);
+
+endmodule // ram16_2sum
diff --git a/sdr_lib/hb/ram32_2sum.v b/sdr_lib/hb/ram32_2sum.v
new file mode 100644
index 000000000..d1f55b7d0
--- /dev/null
+++ b/sdr_lib/hb/ram32_2sum.v
@@ -0,0 +1,22 @@
+
+
+module ram32_2sum (input clock, input write,
+ input [4:0] wr_addr, input [15:0] wr_data,
+ input [4:0] rd_addr1, input [4:0] rd_addr2,
+ output reg [15:0] sum);
+
+ reg [15:0] ram_array [0:31];
+ wire [16:0] sum_int;
+
+ always @(posedge clock)
+ if(write)
+ ram_array[wr_addr] <= #1 wr_data;
+
+ assign sum_int = ram_array[rd_addr1] + ram_array[rd_addr2];
+
+ always @(posedge clock)
+ sum <= #1 sum_int[16:1] + (sum_int[16]&sum_int[0]);
+
+
+endmodule // ram32_2sum
+