Added mul tb and fixed
This commit is contained in:
@@ -41,9 +41,11 @@ files_xco = boards/mimas_v1/ip/clk_gen.xco
|
|||||||
[target.sim]
|
[target.sim]
|
||||||
toolchain = iverilog
|
toolchain = iverilog
|
||||||
runtime = all
|
runtime = all
|
||||||
toplevel = tb_nco_q15
|
toplevel = tb_sigmadelta
|
||||||
ivl_opts = -Irtl/util
|
ivl_opts = -Irtl/util
|
||||||
files_verilog = sim/tb/tb_nco_q15.v
|
files_verilog = sim/tb/tb_nco_q15.v
|
||||||
|
sim/tb/tb_sigmadelta.v
|
||||||
|
sim/tb/tb_mul_const.v
|
||||||
rtl/core/nco_q15.v
|
rtl/core/nco_q15.v
|
||||||
rtl/core/lvds_comparator.v
|
rtl/core/lvds_comparator.v
|
||||||
rtl/core/sigmadelta_rcmodel_q15.v
|
rtl/core/sigmadelta_rcmodel_q15.v
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ module mul_const_shiftadd#(
|
|||||||
input wire signed [W-1:0] x,
|
input wire signed [W-1:0] x,
|
||||||
output wire signed [2*W-1:0] y
|
output wire signed [2*W-1:0] y
|
||||||
);
|
);
|
||||||
// Absolute value and sign of C
|
// Sign and magnitude of constant
|
||||||
localparam integer C_NEG = (C < 0) ? 1 : 0;
|
localparam integer C_NEG = (C < 0) ? 1 : 0;
|
||||||
localparam integer C_ABS = (C < 0) ? -C : C;
|
localparam integer C_ABS = (C < 0) ? -C : C;
|
||||||
|
|
||||||
// Find MSB index of C_ABS to size the network
|
// MSB index of |C| (0-based). Keeps network minimal.
|
||||||
function integer msb_index;
|
function integer msb_index;
|
||||||
input integer v;
|
input integer v;
|
||||||
integer i;
|
integer i;
|
||||||
@@ -31,19 +31,26 @@ module mul_const_shiftadd#(
|
|||||||
if (v >> i) msb_index = i;
|
if (v >> i) msb_index = i;
|
||||||
end
|
end
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
localparam integer I_MAX = (C_ABS == 0) ? 0 : msb_index(C_ABS);
|
localparam integer I_MAX = (C_ABS == 0) ? 0 : msb_index(C_ABS);
|
||||||
|
|
||||||
// Partial products
|
// Width big enough for the largest partial product: W bits shifted by I_MAX
|
||||||
wire signed [W+I_MAX:0] part [0:I_MAX];
|
localparam integer PPW = W + I_MAX + 1;
|
||||||
|
|
||||||
|
// Pre-extend x to PPW so shifts don’t truncate high/sign bits
|
||||||
|
wire signed [PPW-1:0] x_ext = {{(PPW-W){x[W-1]}}, x};
|
||||||
|
|
||||||
|
// Partial products (only where C’s bit is 1)
|
||||||
|
wire signed [PPW-1:0] part [0:I_MAX];
|
||||||
genvar i;
|
genvar i;
|
||||||
generate
|
generate
|
||||||
for (i = 0; i <= I_MAX; i = i + 1) begin : GEN_PARTS
|
for (i = 0; i <= I_MAX; i = i + 1) begin : GEN_PARTS
|
||||||
assign part[i] = (C_ABS[i]) ? ($signed(x) <<< i) : { (W+I_MAX+1){1'b0} };
|
assign part[i] = C_ABS[i] ? (x_ext <<< i) : {PPW{1'b0}};
|
||||||
end
|
end
|
||||||
endgenerate
|
endgenerate
|
||||||
|
|
||||||
// Adder chain (simple; replace with tree if you want higher performance)
|
// Adder chain (you can replace with a balanced tree for speed)
|
||||||
wire signed [W+I_MAX:0] sum [0:I_MAX];
|
wire signed [PPW-1:0] sum [0:I_MAX];
|
||||||
generate
|
generate
|
||||||
if (I_MAX == 0) begin
|
if (I_MAX == 0) begin
|
||||||
assign sum[0] = part[0];
|
assign sum[0] = part[0];
|
||||||
@@ -56,10 +63,9 @@ module mul_const_shiftadd#(
|
|||||||
endgenerate
|
endgenerate
|
||||||
|
|
||||||
// Apply sign of C
|
// Apply sign of C
|
||||||
wire signed [W+I_MAX:0] mag = (I_MAX==0) ? part[0] : sum[I_MAX];
|
wire signed [PPW-1:0] mag = (I_MAX == 0) ? part[0] : sum[I_MAX];
|
||||||
wire signed [W+I_MAX:0] prod = C_NEG ? -mag : mag;
|
wire signed [PPW-1:0] prod = C_NEG ? -mag : mag;
|
||||||
|
|
||||||
// Stretch to fixed y width (truncate/extend as you wish outside)
|
|
||||||
assign y = prod;
|
|
||||||
|
|
||||||
|
// Stretch/extend to 2W result width
|
||||||
|
assign y = {{((2*W)-PPW){prod[PPW-1]}}, prod};
|
||||||
endmodule
|
endmodule
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
// -- clk : input clock
|
// -- clk : input clock
|
||||||
// -- resetn : reset signal
|
// -- resetn : reset signal
|
||||||
// -- sd_sample : 1 bit sample output from sd sampler
|
// -- sd_sample : 1 bit sample output from sd sampler
|
||||||
// -- sample_q15 : output samples in q1.15
|
// -- sample_q15 : output samples in q.15
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
module sigmadelta_rcmodel_q15 #(
|
module sigmadelta_rcmodel_q15 #(
|
||||||
parameter integer alpha_q15 = 16'sh0b00
|
parameter integer alpha_q15 = 16'sh0b00
|
||||||
@@ -25,9 +25,10 @@ module sigmadelta_rcmodel_q15 #(
|
|||||||
reg signed [15:0] y_q15;
|
reg signed [15:0] y_q15;
|
||||||
wire signed [15:0] sd_q15 = sd_sample ? 16'sh7fff : 16'sh0000;
|
wire signed [15:0] sd_q15 = sd_sample ? 16'sh7fff : 16'sh0000;
|
||||||
wire signed [15:0] e_q15 = sd_q15 - y_q15;
|
wire signed [15:0] e_q15 = sd_q15 - y_q15;
|
||||||
|
// wire signed [31:0] prod_q30 = $signed(e_q15) * $signed(alpha_q15);
|
||||||
wire signed [31:0] prod_q30;
|
wire signed [31:0] prod_q30;
|
||||||
// Use shift-add algorithm for multiplication
|
// Use shift-add algorithm for multiplication
|
||||||
mul_const_shiftadd #(.C(alpha_q15)) alpha_times_e (e_q15, prod_q30);
|
mul_const_shiftadd #(.C($signed(alpha_q15))) alpha_times_e ($signed(e_q15), prod_q30);
|
||||||
wire signed [15:0] y_next_q15 = y_q15 + (prod_q30>>>15);
|
wire signed [15:0] y_next_q15 = y_q15 + (prod_q30>>>15);
|
||||||
|
|
||||||
// clamp to [0, 0x7FFF] (keeps signal view tidy)
|
// clamp to [0, 0x7FFF] (keeps signal view tidy)
|
||||||
|
|||||||
72
sim/tb/tb_mul_const.v
Normal file
72
sim/tb/tb_mul_const.v
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb_mul_const();
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Parameters
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
localparam integer W = 16;
|
||||||
|
localparam integer C = 16'sh0B3B; // alpha_q15 ≈ 0.08774
|
||||||
|
localparam signed [W-1:0] C_S = C[W-1:0];
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// DUT I/O
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
reg signed [W-1:0] x;
|
||||||
|
wire signed [(2*W)-1:0] y;
|
||||||
|
|
||||||
|
// Instantiate DUT
|
||||||
|
mul_const_shiftadd #(
|
||||||
|
.W(W),
|
||||||
|
.C(C)
|
||||||
|
) dut (
|
||||||
|
.x(x),
|
||||||
|
.y(y)
|
||||||
|
);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Reference and verification
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
reg signed [(2*W)-1:0] expected;
|
||||||
|
integer i;
|
||||||
|
integer errors;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$display("------------------------------------------------------");
|
||||||
|
$display(" Testbench: mul_const_shiftadd");
|
||||||
|
$display(" W = %0d, C = %0d (0x%0h)", W, $signed(C_S), C_S);
|
||||||
|
$display("------------------------------------------------------");
|
||||||
|
|
||||||
|
errors = 0;
|
||||||
|
|
||||||
|
// Exhaustively test all 16-bit signed values
|
||||||
|
for (i = -(1<<(W-1)); i < (1<<(W-1)); i = i + 1) begin
|
||||||
|
x = i;
|
||||||
|
#1; // let combinational logic settle
|
||||||
|
|
||||||
|
expected = $signed(x) * $signed(C_S);
|
||||||
|
|
||||||
|
if (y !== expected) begin
|
||||||
|
$display("FAIL: x=%6d (0x%04h) * C=%6d -> y=%10d (0x%08h), expected=%10d (0x%08h)",
|
||||||
|
$signed(x), x, $signed(C_S),
|
||||||
|
$signed(y), y, $signed(expected), expected);
|
||||||
|
errors = errors + 1;
|
||||||
|
// Uncomment next line if you want to stop on first mismatch
|
||||||
|
// $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// progress message every 4096 iterations
|
||||||
|
if (((i + (1<<(W-1))) % 4096) == 0)
|
||||||
|
$display("Progress: %5d / %5d values tested...",
|
||||||
|
i + (1<<(W-1)), (1<<W));
|
||||||
|
end
|
||||||
|
|
||||||
|
if (errors == 0)
|
||||||
|
$display("✅ PASS: All %0d test cases matched perfectly.", (1<<W));
|
||||||
|
else
|
||||||
|
$display("❌ FAIL: %0d mismatches found out of %0d cases.",
|
||||||
|
errors, (1<<W));
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
38
sim/tb/tb_sigmadelta.v
Normal file
38
sim/tb/tb_sigmadelta.v
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb_sigmadelta();
|
||||||
|
// Clock and reset generation
|
||||||
|
reg clk;
|
||||||
|
reg resetn;
|
||||||
|
initial clk <= 1'b0;
|
||||||
|
initial resetn <= 1'b0;
|
||||||
|
always #6.667 clk <= !clk;
|
||||||
|
initial #40 resetn <= 1'b1;
|
||||||
|
|
||||||
|
// Default run
|
||||||
|
initial begin
|
||||||
|
$dumpfile("out.vcd");
|
||||||
|
$dumpvars;
|
||||||
|
#1_000_000
|
||||||
|
$finish;
|
||||||
|
end;
|
||||||
|
|
||||||
|
wire sd_a;
|
||||||
|
wire sd_b;
|
||||||
|
wire sd_o;
|
||||||
|
// 3K3R 220PC 15MHZT
|
||||||
|
sigmadelta_sampler sd_sampler(
|
||||||
|
.clk(clk),
|
||||||
|
.a(sd_a), .b(sd_b),
|
||||||
|
.o(sd_o)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
wire signed [15:0] sample_q15;
|
||||||
|
sigmadelta_rcmodel_q15 rc_model(
|
||||||
|
.clk(clk), .resetn(resetn),
|
||||||
|
.sd_sample(sd_o),
|
||||||
|
.sample_q15(sample_q15)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
Reference in New Issue
Block a user