mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
fpga: lib: optimizations for zero/symmetric coefficients for static FIR
Optimizations aim to reduce number of DSP slices: For static FIR filter generation (RELOADABLE_COEFFS = 0): - skip fir_filter_slice instantiation for zero coefficient FIR taps. Positiion of zero taps derived from COEFFS_VEC. (cherry pick from Max prototype) - use fir_filter_slice preadder input to jointly process symmetric FIR filter taps. Saves almost half the number of fir_filter_slices. Symmetry automatically inferred from COEFFS_VEC. TB Changes: TB has been extended to validate optimizations for zero/symmetric coefficients depending on the provided filter coefficients. - axi_fir_multisample_filter_tb.sv: changed interface to pass FIR filter coefficients from outside. - axi_fir_multisample_filter_tb_wrapper.sv: splitted into separate generate loop to test general behavior and specific optimizations
This commit is contained in:
parent
125e3284c3
commit
734f260534
@ -31,10 +31,15 @@
|
||||
// BLANK_OUTPUT - Disable (1) or enable (0) output when initially filling internal pipeline
|
||||
// USE_EMBEDDED_REGS_COEFFS - Reduce register usage by only using embedded registers in DSP slices.
|
||||
// Updating taps while streaming will cause temporary output corruption!
|
||||
//
|
||||
// Notes:
|
||||
// - If using USE_EMBEDDED_REGS_COEFFS, coefficients must be written at least once as COEFFS_VEC is ignored!
|
||||
// - If using RELOADABLE_COEFFS, coefficients must be written in reverse order!
|
||||
|
||||
// - Optimizations: For static coefficients (RELOADABLE_COEFFS = 0), zero and symmetric coefficient
|
||||
// optimizations are automatically enabled to reduce DSP slice utilization.
|
||||
// Symmetric coefficients save ~50% of DSP slices by reusing computations.
|
||||
// Zero coefficients are skipped, saving DSP slices proportionally
|
||||
// (e.g., halfband filters save ~50%).
|
||||
|
||||
module axi_fir_multisample_filter #(
|
||||
int IN_WIDTH = 16,
|
||||
@ -44,8 +49,7 @@ module axi_fir_multisample_filter #(
|
||||
int NUM_COEFFS = 41,
|
||||
int CLIP_BITS = $clog2(NUM_COEFFS),
|
||||
int ACCUM_WIDTH = IN_WIDTH+COEFF_WIDTH+$clog2(NUM_COEFFS)-1,
|
||||
bit [NUM_COEFFS*COEFF_WIDTH-1:0] COEFFS_VEC =
|
||||
{{1'b0,{(COEFF_WIDTH-1){1'b1}}},{(COEFF_WIDTH*(NUM_COEFFS-1)){1'b0}}},
|
||||
bit [COEFF_WIDTH-1:0] COEFFS_VEC [NUM_COEFFS] = '{0: (1 << (COEFF_WIDTH-1)) - 1, default: 0},
|
||||
bit RELOADABLE_COEFFS = 1,
|
||||
bit BLANK_OUTPUT = 1,
|
||||
bit USE_EMBEDDED_REGS_COEFFS = 1
|
||||
@ -74,7 +78,42 @@ module axi_fir_multisample_filter #(
|
||||
output logic s_axis_reload_tready
|
||||
);
|
||||
|
||||
localparam int PIPELINE_DELAY = NUM_COEFFS + 5; // +4 pipeline depth in fir_filter_slice.v, +1 of shift register
|
||||
// define function to calculate the number of zero coefficients up to the
|
||||
// given index
|
||||
function int count_zeros_up_to_index (
|
||||
input int index
|
||||
);
|
||||
int count;
|
||||
begin
|
||||
count = 0;
|
||||
for (int i = 0; i < index; i = i + 1) begin
|
||||
if (COEFFS_VEC[i] == '0) begin
|
||||
count = count + 1;
|
||||
end
|
||||
end
|
||||
return count;
|
||||
end
|
||||
endfunction
|
||||
// function to check coefficient symmetry
|
||||
function bit check_coeffs_symmetric();
|
||||
begin
|
||||
check_coeffs_symmetric = 1'b1;
|
||||
for (int i = 0; i < NUM_COEFFS/2; i = i + 1) begin
|
||||
if (COEFFS_VEC[i] != COEFFS_VEC[NUM_COEFFS-1-i]) begin
|
||||
check_coeffs_symmetric = 1'b0;
|
||||
end
|
||||
end
|
||||
return check_coeffs_symmetric;
|
||||
end
|
||||
endfunction
|
||||
|
||||
localparam bit OPTIMIZE_FOR_ZERO_COEFFS = (RELOADABLE_COEFFS == 0);
|
||||
localparam bit OPTIMIZE_FOR_SYMMETRIC_COEFFS = (RELOADABLE_COEFFS == 0)
|
||||
? check_coeffs_symmetric() : 0;
|
||||
localparam int NUM_SLICES = OPTIMIZE_FOR_SYMMETRIC_COEFFS
|
||||
? NUM_COEFFS/2 + NUM_COEFFS[0] : NUM_COEFFS;
|
||||
localparam int PIPELINE_DELAY = NUM_SLICES + 5 // +4 pipeline depth in fir_filter_slice.v, +1 of shift register
|
||||
- (OPTIMIZE_FOR_ZERO_COEFFS ? count_zeros_up_to_index(NUM_SLICES) : 0);
|
||||
localparam int SHIFT_REG_WIDTH = (NUM_SPC + 1) * NUM_COEFFS; // length of shift register
|
||||
|
||||
logic [ACCUM_WIDTH-1:0] m_axis_data_tdata_int [NUM_SPC-1:0];
|
||||
@ -105,7 +144,7 @@ module axi_fir_multisample_filter #(
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
for (int k = 0; k < NUM_COEFFS; k = k + 1) begin
|
||||
coeffs[k] <= COEFFS_VEC[COEFF_WIDTH*k +: COEFF_WIDTH];
|
||||
coeffs[k] <= COEFFS_VEC[k];
|
||||
end
|
||||
// Initialize coefficients at reset
|
||||
coeff_load_stb <= 1'b1;
|
||||
@ -125,7 +164,7 @@ module axi_fir_multisample_filter #(
|
||||
// Coefficients are static
|
||||
initial begin
|
||||
for (int k = NUM_COEFFS-1; k >= 0; k = k - 1) begin
|
||||
coeffs[k] <= COEFFS_VEC[COEFF_WIDTH*k +: COEFF_WIDTH];
|
||||
coeffs[k] <= COEFFS_VEC[k];
|
||||
coeff_load_stb <= 1'b1;
|
||||
end
|
||||
end
|
||||
@ -201,39 +240,64 @@ module axi_fir_multisample_filter #(
|
||||
// K_FLIPPED: refer to the documentation above of data_shift_reg
|
||||
localparam int K_FLIPPED = NUM_SPC-k-1;
|
||||
|
||||
wire [ACCUM_WIDTH-1:0] sample_accum [0 : NUM_COEFFS]; // [0:NUM_COEFFS] to make the
|
||||
wire [COEFF_WIDTH-1:0] coeff_forward [0 : NUM_COEFFS]; // generate loop easier to read
|
||||
logic [ACCUM_WIDTH-1:0] sample_accum [0 : NUM_SLICES]; // [0:NUM_COEFFS] to make the
|
||||
logic [COEFF_WIDTH-1:0] coeff_forward [0 : NUM_SLICES]; // generate loop easier to read
|
||||
|
||||
assign sample_accum[0] = 0;
|
||||
assign coeff_forward[0] = s_axis_reload_tdata;
|
||||
|
||||
// Build up FIR filter with multiply-accumulate slices (fir_filter_slice).
|
||||
// Now generate the slices for each chain
|
||||
for (genvar j = 0; j < NUM_COEFFS ; j = j + 1) begin : gen_slice
|
||||
fir_filter_slice #(
|
||||
.IN_WIDTH(IN_WIDTH),
|
||||
.COEFF_WIDTH(COEFF_WIDTH),
|
||||
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||||
.OUT_WIDTH(ACCUM_WIDTH))
|
||||
fir_filter_slice (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.sample_in_stb(s_axis_data_tvalid & s_axis_data_tready),
|
||||
// NUM_SPC is added j times due to the pipeline delay.
|
||||
// +1 for selecting the next index for the FIR result calculation
|
||||
// K_FLIPPED is the offset into data_shift_reg
|
||||
.sample_in_a(data_shift_reg[ j*(NUM_SPC+1) + K_FLIPPED ]),
|
||||
// sample_in_b is used to implement symmetric coefficients, always 0 if SYMMETRIC_COEFFS = 0
|
||||
.sample_in_b('0), // symmetric disabled, thus empty
|
||||
.sample_forward(),
|
||||
// For proper coeffient loading, coeff_forward must be shifted in backwards. coeffs[] is already backwards
|
||||
.coeff_in(((USE_EMBEDDED_REGS_COEFFS == 1) && (RELOADABLE_COEFFS == 1)) ? coeff_forward[j] : coeffs[j]),
|
||||
.coeff_forward(coeff_forward[j+1]),
|
||||
.coeff_load_stb(coeff_load_stb),
|
||||
.sample_accum(sample_accum[j]),
|
||||
.sample_out(sample_accum[j+1])
|
||||
);
|
||||
for (genvar j = 0; j < NUM_SLICES ; j = j + 1) begin : gen_slice
|
||||
// skip zero coefficients: always for static coeffs
|
||||
if (OPTIMIZE_FOR_ZERO_COEFFS && COEFFS_VEC[j] == '0) begin
|
||||
assign sample_accum[j+1] = sample_accum[j];
|
||||
always_ff @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
coeff_forward[j+1] <= '0;
|
||||
end else if (coeff_load_stb) begin
|
||||
coeff_forward[j+1] <= coeff_forward[j];
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
localparam int GAP = OPTIMIZE_FOR_ZERO_COEFFS ? count_zeros_up_to_index(j) : 0;
|
||||
localparam bit APPLY_SYMMETRY = (OPTIMIZE_FOR_SYMMETRIC_COEFFS == 1) // enabled?
|
||||
&& !((NUM_COEFFS[0] == 1) && (j == NUM_SLICES-1)); // not the center tap for odd NUM_COEFFS
|
||||
|
||||
fir_filter_slice #(
|
||||
.IN_WIDTH(IN_WIDTH),
|
||||
.COEFF_WIDTH(COEFF_WIDTH),
|
||||
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||||
.OUT_WIDTH(ACCUM_WIDTH))
|
||||
fir_filter_slice (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.sample_in_stb(s_axis_data_tvalid & s_axis_data_tready),
|
||||
// NUM_SPC is added j times due to the pipeline delay.
|
||||
// +1 for selecting the next index for the FIR result calculation
|
||||
// K_FLIPPED is the offset into data_shift_reg.
|
||||
// If zero tap filter slices are skipped, we need to pick samples
|
||||
// GAP cycles earlier to keep the correct alignment which corresponds
|
||||
// to subtracting GAP*NUM_SPC from the buffer index.
|
||||
.sample_in_a(data_shift_reg[ (j)*(NUM_SPC+1) - GAP*NUM_SPC + K_FLIPPED ]),
|
||||
// sample_in_b is used to implement symmetric coefficients
|
||||
// If not symmetric or middle tap of odd number of coeffs, tie to zero
|
||||
// otherwise select the mirrored sample from the shift register.
|
||||
// Mirror index is NUM_COEFFS-1-j, since sample_in_b inside fir_filter_slice
|
||||
// has one cycle delay less that sample_in_a, we need to take it one cycle
|
||||
// later such that both values are in-sync. Thus we add another NUM_SPC to the index.
|
||||
.sample_in_b(APPLY_SYMMETRY ? data_shift_reg[ (NUM_COEFFS-1-j) + (j-GAP+1)*NUM_SPC + K_FLIPPED ] : '0),
|
||||
.sample_forward(),
|
||||
// For proper coeffient loading, coeff_forward must be shifted in backwards. coeffs[] is already backwards
|
||||
.coeff_in(((USE_EMBEDDED_REGS_COEFFS == 1) && (RELOADABLE_COEFFS == 1)) ? coeff_forward[j] : coeffs[j]),
|
||||
.coeff_forward(coeff_forward[j+1]),
|
||||
.coeff_load_stb(coeff_load_stb),
|
||||
.sample_accum(sample_accum[j]),
|
||||
// Connect the accumulator chain from previous to next slice instance
|
||||
.sample_out(sample_accum[j+1])
|
||||
);
|
||||
end
|
||||
end : gen_slice
|
||||
|
||||
always_comb begin
|
||||
@ -242,7 +306,7 @@ module axi_fir_multisample_filter #(
|
||||
m_axis_data_tdata_int[k] = '0;
|
||||
m_axis_data_tvalid_int[k] = '0;
|
||||
end else begin
|
||||
m_axis_data_tdata_int[k] = sample_accum[NUM_COEFFS];
|
||||
m_axis_data_tdata_int[k] = sample_accum[NUM_SLICES];
|
||||
m_axis_data_tvalid_int[k] = s_axis_data_tvalid;
|
||||
end
|
||||
// tlast is masked the same way during ring-in.
|
||||
|
||||
@ -16,14 +16,18 @@
|
||||
// BLANK_OUTPUT - Disable (1) or enable (0) output when initially filling internal pipeline
|
||||
// USE_EMBEDDED_REGS_COEFFS - Reduce register usage by only using embedded registers in DSP slices.
|
||||
// Updating taps while streaming will cause temporary output corruption!
|
||||
//
|
||||
// COEFF_WIDTH - Width of filter coefficients
|
||||
// COEFFS_VEC - Vector of filter coefficients
|
||||
|
||||
module axi_fir_multisample_filter_tb #(
|
||||
parameter NUM_SPC = 8,
|
||||
parameter NUM_COEFFS = 41,
|
||||
parameter RELOADABLE_COEFFS = 1,
|
||||
parameter BLANK_OUTPUT = 1,
|
||||
parameter USE_EMBEDDED_REGS_COEFFS = 1
|
||||
parameter int NUM_SPC = 8,
|
||||
parameter int NUM_COEFFS = 41,
|
||||
parameter bit RELOADABLE_COEFFS = 1,
|
||||
parameter bit BLANK_OUTPUT = 1,
|
||||
parameter bit USE_EMBEDDED_REGS_COEFFS = 1,
|
||||
parameter int COEFF_WIDTH = 16,
|
||||
parameter bit [COEFF_WIDTH-1:0] COEFFS_VEC [NUM_COEFFS] =
|
||||
'{0: (1 << (COEFF_WIDTH-1)) - 1, default: 0}
|
||||
) ();
|
||||
|
||||
`include "test_exec.svh"
|
||||
@ -46,29 +50,13 @@ module axi_fir_multisample_filter_tb #(
|
||||
// DUT parameters to test
|
||||
localparam int IN_WIDTH = 16;
|
||||
localparam int AXI_WIDTH = IN_WIDTH * NUM_SPC;
|
||||
localparam int COEFF_WIDTH = 16;
|
||||
localparam int OUT_WIDTH = 16;
|
||||
localparam int CLIP_BITS = $clog2(NUM_COEFFS);
|
||||
localparam int ACCUM_WIDTH = IN_WIDTH + COEFF_WIDTH + CLIP_BITS - 1;
|
||||
localparam int PIPELINE_DELAY = NUM_COEFFS + 5;
|
||||
|
||||
// How many groups of multisampled-input needed
|
||||
localparam int IN_GROUP_NUM = $ceil(NUM_COEFFS*1.0/NUM_SPC);
|
||||
// +10 is pipeline compensation
|
||||
localparam int FLUSH_CYCLE = ((NUM_SPC < NUM_COEFFS) ? NUM_COEFFS : NUM_SPC ) +10;
|
||||
|
||||
localparam logic [COEFF_WIDTH*NUM_COEFFS-1:0] COEFFS_VEC_0 = {
|
||||
16'sd158, 16'sd0, 16'sd33, -16'sd0, -16'sd256,
|
||||
16'sd553, 16'sd573, -16'sd542, -16'sd1012, 16'sd349,
|
||||
16'sd1536, 16'sd123, -16'sd2097, -16'sd1012, 16'sd1633,
|
||||
16'sd1608, -16'sd3077, -16'sd5946, 16'sd3370, 16'sd10513,
|
||||
-16'sd19295, // 16'sd19295, change to negative to avoid clipping
|
||||
16'sd10513, 16'sd3370, -16'sd5946, -16'sd3077, 16'sd1608,
|
||||
16'sd1633, -16'sd1012, -16'sd2097, 16'sd123, 16'sd1536,
|
||||
16'sd349, -16'sd1012, -16'sd542, 16'sd573, 16'sd553,
|
||||
-16'sd256, -16'sd0, 16'sd33, 16'sd0, 16'sd158
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Clocks and Resets
|
||||
//---------------------------------------------------------------------------
|
||||
@ -146,7 +134,7 @@ module axi_fir_multisample_filter_tb #(
|
||||
.NUM_COEFFS(NUM_COEFFS),
|
||||
.CLIP_BITS(CLIP_BITS),
|
||||
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||||
.COEFFS_VEC(COEFFS_VEC_0),
|
||||
.COEFFS_VEC(COEFFS_VEC),
|
||||
.RELOADABLE_COEFFS(RELOADABLE_COEFFS),
|
||||
.BLANK_OUTPUT(BLANK_OUTPUT),
|
||||
.USE_EMBEDDED_REGS_COEFFS(USE_EMBEDDED_REGS_COEFFS)
|
||||
@ -314,7 +302,7 @@ module axi_fir_multisample_filter_tb #(
|
||||
packet_reload.empty();
|
||||
// Reload must send data in reverse direction
|
||||
for (int i= NUM_COEFFS-1 ; i>=0; i--) begin
|
||||
packet_reload.data.push_back(COEFFS_VEC_0[COEFF_WIDTH*i +: COEFF_WIDTH]);
|
||||
packet_reload.data.push_back(COEFFS_VEC[i]);
|
||||
end
|
||||
AxisIf_coeff_bfm.put(packet_reload);
|
||||
AxisIf_coeff_bfm.wait_complete();
|
||||
@ -368,7 +356,7 @@ module axi_fir_multisample_filter_tb #(
|
||||
end else begin
|
||||
get_sample(i_samp_int, i == 0); // ask one single sample from output
|
||||
end
|
||||
i_coeff = $signed(COEFFS_VEC_0[COEFF_WIDTH*i +: COEFF_WIDTH]);
|
||||
i_coeff = $signed(COEFFS_VEC[i]);
|
||||
|
||||
$sformat(
|
||||
s, "Incorrect I value received on sample %0d! Expected: %0d, Received: %0d",
|
||||
|
||||
@ -7,43 +7,230 @@
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// testing various configurations of the filter
|
||||
// Top-level testbench wrapper for axi_fir_multisample_filter. This module
|
||||
// runs multiple test configurations to verify:
|
||||
// - Base functionality with various NUM_SPC, NUM_COEFFS, and feature combinations
|
||||
// (RELOADABLE_COEFFS, BLANK_OUTPUT, USE_EMBEDDED_REGS_COEFFS)
|
||||
// - Symmetric coefficient optimization with odd tap counts (non-reloadable only)
|
||||
// - Symmetric coefficient optimization with even tap counts (non-reloadable only)
|
||||
// - Zero coefficient skipping optimization combined with symmetry (non-reloadable only)
|
||||
//
|
||||
// Each test configuration uses nested generate loops to create comprehensive
|
||||
// parameter coverage across different filter configurations.
|
||||
//
|
||||
|
||||
module axi_fir_multisample_filter_tb_wrapper();
|
||||
// Common Parameters
|
||||
`include "test_exec.svh"
|
||||
localparam int COEFF_WIDTH = 16;
|
||||
localparam integer NUM_SPC_TEST[2:0] = {2 , 4 , 8};
|
||||
localparam integer NUM_COEFFS_TEST[2:0] = {5 , 17 , 41};
|
||||
localparam integer PARA_TEST[1:0] = {0 , 1};
|
||||
|
||||
// General tests:
|
||||
// - General tests covering all combinations of NUM_SPC, NUM_COEFFS,
|
||||
// RELOADABLE_COEFFS, BLANK_OUTPUT, and USE_EMBEDDED_REGS_COEFFS parameters.
|
||||
//
|
||||
// Test parameters:
|
||||
// - NUM_SPC: 2, 4, 8 samples per cycle
|
||||
// - NUM_COEFFS: 5, 17, 41 taps
|
||||
// - RELOADABLE_COEFFS: 0, 1
|
||||
// - BLANK_OUTPUT: 0, 1
|
||||
// - USE_EMBEDDED_REGS_COEFFS: 0, 1
|
||||
generate
|
||||
//---------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//---------------------------------------------------------------------------
|
||||
localparam bit [COEFF_WIDTH-1:0] MAX_COEFF_ARRAY [41] = '{
|
||||
16'sd158, 16'sd0, 16'sd33, -16'sd0, -16'sd256,
|
||||
16'sd553, 16'sd573, -16'sd542, -16'sd1012, 16'sd349,
|
||||
16'sd1536, 16'sd123, -16'sd2097, -16'sd1012, 16'sd1633,
|
||||
16'sd1608, -16'sd3077, -16'sd5946, 16'sd3370, 16'sd10513,
|
||||
-16'sd19295, // 16'sd19295, change to negative to avoid clipping
|
||||
16'sd10513, 16'sd3370, -16'sd5946, -16'sd3077, 16'sd1608,
|
||||
16'sd1633, -16'sd1012, -16'sd2097, 16'sd123, 16'sd1536,
|
||||
16'sd349, -16'sd1012, -16'sd542, 16'sd573, 16'sd553,
|
||||
-16'sd256, -16'sd0, 16'sd33, 16'sd0, 16'sd158
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
// Nested test cases
|
||||
//---------------------------------------------------------------------------
|
||||
// Full coverage nested test loop
|
||||
for (genvar i = 0; i < $size(NUM_SPC_TEST); i = i + 1) begin : test_NUM_SPC
|
||||
for (genvar j = 0; j < $size(NUM_COEFFS_TEST); j = j + 1) begin : test_NUM_COEFFS
|
||||
for (genvar k = 0; k < $size(PARA_TEST); k = k + 1) begin : test_RELOADABLE
|
||||
for (genvar m = 0; m < $size(PARA_TEST); m = m + 1) begin : test_BLANK
|
||||
for (genvar n = 0; n < $size(PARA_TEST); n = n + 1) begin : test_EMBEDDED
|
||||
axi_fir_multisample_filter_tb #(
|
||||
.NUM_SPC(NUM_SPC_TEST[i]),
|
||||
.NUM_COEFFS(NUM_COEFFS_TEST[j]),
|
||||
.RELOADABLE_COEFFS(PARA_TEST[k]),
|
||||
.BLANK_OUTPUT(PARA_TEST[m]),
|
||||
.USE_EMBEDDED_REGS_COEFFS(PARA_TEST[n]),
|
||||
.COEFF_WIDTH(COEFF_WIDTH),
|
||||
.COEFFS_VEC(MAX_COEFF_ARRAY[0 +: NUM_COEFFS_TEST[j]])
|
||||
) tb_i ();
|
||||
end : test_EMBEDDED
|
||||
end : test_BLANK
|
||||
end : test_RELOADABLE
|
||||
end : test_NUM_COEFFS
|
||||
end : test_NUM_SPC
|
||||
endgenerate
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Local Parameters
|
||||
//---------------------------------------------------------------------------
|
||||
timeunit 1ns / 1ps;
|
||||
//
|
||||
// Tests for symmetric coefficient optimization with odd tap counts.
|
||||
// - Uses symmetric coefficient arrays to enable OPTIMIZE_FOR_SYMMETRIC_COEFFS.
|
||||
// - Only non-reloadable configurations (RELOADABLE_COEFFS=0) are tested.
|
||||
//
|
||||
// Test parameters:
|
||||
// - NUM_SPC: 2, 4, 8 samples per cycle
|
||||
// - NUM_COEFFS: 5, 17, 41 taps (all odd)
|
||||
// - BLANK_OUTPUT: 0, 1
|
||||
// - RELOADABLE_COEFFS: 0 (fixed)
|
||||
// - USE_EMBEDDED_REGS_COEFFS: 0 (fixed)
|
||||
//
|
||||
generate
|
||||
//---------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//---------------------------------------------------------------------------
|
||||
localparam bit [COEFF_WIDTH-1:0] MAX_COEFF_ARRAY_SYMMETRIC_ODD [41] = '{
|
||||
16'sd158, 16'sd158, 16'sd33, -16'sd256, -16'sd256,
|
||||
16'sd553, 16'sd573, -16'sd542, -16'sd1012, 16'sd349,
|
||||
16'sd1536, 16'sd123, -16'sd2097, -16'sd1012, 16'sd1633,
|
||||
16'sd1608, -16'sd3077, -16'sd5946, 16'sd3370, 16'sd10513,
|
||||
-16'sd19295, // 16'sd19295, change to negative to avoid clipping
|
||||
16'sd10513, 16'sd3370, -16'sd5946, -16'sd3077, 16'sd1608,
|
||||
16'sd1633, -16'sd1012, -16'sd2097, 16'sd123, 16'sd1536,
|
||||
16'sd349, -16'sd1012, -16'sd542, 16'sd573, 16'sd553,
|
||||
-16'sd256, -16'sd256, 16'sd33, 16'sd158, 16'sd158
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
// Nested test cases
|
||||
//---------------------------------------------------------------------------
|
||||
for (genvar i = 0; i < $size(NUM_SPC_TEST); i = i + 1) begin : test_NUM_SPC_SYM
|
||||
for (genvar j = 0; j < $size(NUM_COEFFS_TEST); j = j + 1) begin : test_NUM_COEFFS_SYM
|
||||
for (genvar m = 0; m < $size(PARA_TEST); m = m + 1) begin : test_BLANK_SYM
|
||||
// ... extract symmetric coefficients for odd number of taps
|
||||
localparam bit [COEFF_WIDTH-1:0] COEFFS_SYMM_ODD [NUM_COEFFS_TEST[j]] =
|
||||
MAX_COEFF_ARRAY_SYMMETRIC_ODD[($size(MAX_COEFF_ARRAY_SYMMETRIC_ODD)-NUM_COEFFS_TEST[j])/2 +: NUM_COEFFS_TEST[j]];
|
||||
// ... instantiate testbench
|
||||
axi_fir_multisample_filter_tb #(
|
||||
.NUM_SPC(NUM_SPC_TEST[i]),
|
||||
.NUM_COEFFS(NUM_COEFFS_TEST[j]),
|
||||
.RELOADABLE_COEFFS(0),
|
||||
.BLANK_OUTPUT(PARA_TEST[m]),
|
||||
.USE_EMBEDDED_REGS_COEFFS(0),
|
||||
.COEFF_WIDTH(COEFF_WIDTH),
|
||||
.COEFFS_VEC(COEFFS_SYMM_ODD)
|
||||
) tb_sym_i ();
|
||||
end : test_BLANK_SYM
|
||||
end : test_NUM_COEFFS_SYM
|
||||
end : test_NUM_SPC_SYM
|
||||
endgenerate
|
||||
|
||||
localparam integer NUM_SPC_TEST[2:0] = {2 , 4 , 8};
|
||||
localparam integer NUM_COEFFS_TEST[2:0] = {5 , 17 , 41};
|
||||
localparam integer PARA_TEST[1:0] = {0 , 1};
|
||||
//
|
||||
// Tests for symmetric coefficient optimization with even tap counts.
|
||||
// - Uses symmetric coefficient arrays to enable OPTIMIZE_FOR_SYMMETRIC_COEFFS.
|
||||
// - Only non-reloadable configurations (RELOADABLE_COEFFS=0) are tested.
|
||||
//
|
||||
// Test parameters:
|
||||
// - NUM_SPC: 2, 4, 8 samples per cycle
|
||||
// - NUM_COEFFS: 6, 18, 40 taps (all even)
|
||||
// - BLANK_OUTPUT: 0, 1
|
||||
// - RELOADABLE_COEFFS: 0 (fixed)
|
||||
// - USE_EMBEDDED_REGS_COEFFS: 0 (fixed)
|
||||
//
|
||||
generate
|
||||
//---------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//---------------------------------------------------------------------------
|
||||
localparam integer SYM_EVEN_NUM_COEFFS_TEST[2:0] = {6, 18, 40};
|
||||
// ... symmetric coefficients with even number of taps
|
||||
localparam bit [COEFF_WIDTH-1:0] MAX_COEFF_ARRAY_SYMMETRIC_EVEN [40] = '{
|
||||
16'sd158, 16'sd158, 16'sd33, -16'sd256, -16'sd256,
|
||||
16'sd553, 16'sd573, -16'sd542, -16'sd1012, 16'sd349,
|
||||
16'sd1536, 16'sd123, -16'sd2097, -16'sd1012, 16'sd1633,
|
||||
16'sd1608, -16'sd3077, -16'sd5946, 16'sd3370, 16'sd10513,
|
||||
16'sd10513, 16'sd3370, -16'sd5946, -16'sd3077, 16'sd1608,
|
||||
16'sd1633, -16'sd1012, -16'sd2097, 16'sd123, 16'sd1536,
|
||||
16'sd349, -16'sd1012, -16'sd542, 16'sd573, 16'sd553,
|
||||
-16'sd256, -16'sd256, 16'sd33, 16'sd158, 16'sd158
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
// Nested test cases
|
||||
//---------------------------------------------------------------------------
|
||||
for (genvar i = 0; i < $size(NUM_SPC_TEST); i = i + 1) begin : test_NUM_SPC_SYM_EVEN
|
||||
for (genvar j = 0; j < $size(SYM_EVEN_NUM_COEFFS_TEST); j = j + 1) begin : test_NUM_COEFFS_SYM_EVEN
|
||||
for (genvar m = 0; m < $size(PARA_TEST); m = m + 1) begin : test_BLANK_SYM_EVEN
|
||||
// ... extract symmetric coefficients for even number of taps
|
||||
localparam bit [COEFF_WIDTH-1:0] COEFFS_SYMM_EVEN [SYM_EVEN_NUM_COEFFS_TEST[j]] =
|
||||
MAX_COEFF_ARRAY_SYMMETRIC_EVEN[($size(MAX_COEFF_ARRAY_SYMMETRIC_EVEN)-SYM_EVEN_NUM_COEFFS_TEST[j])/2 +: SYM_EVEN_NUM_COEFFS_TEST[j]];
|
||||
// ... instantiate testbench
|
||||
axi_fir_multisample_filter_tb #(
|
||||
.NUM_SPC(NUM_SPC_TEST[i]),
|
||||
.NUM_COEFFS(SYM_EVEN_NUM_COEFFS_TEST[j]),
|
||||
.RELOADABLE_COEFFS(0),
|
||||
.BLANK_OUTPUT(PARA_TEST[m]),
|
||||
.USE_EMBEDDED_REGS_COEFFS(0),
|
||||
.COEFF_WIDTH(COEFF_WIDTH),
|
||||
.COEFFS_VEC(COEFFS_SYMM_EVEN)
|
||||
) tb_sym_even_i ();
|
||||
end : test_BLANK_SYM_EVEN
|
||||
end : test_NUM_COEFFS_SYM_EVEN
|
||||
end : test_NUM_SPC_SYM_EVEN
|
||||
endgenerate
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Nested test cases
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Full coverage nested test loop
|
||||
generate
|
||||
for (genvar i = 0; i < $size(NUM_SPC_TEST); i = i + 1) begin : test_NUM_SPC
|
||||
for (genvar j = 0; j < $size(NUM_COEFFS_TEST); j = j + 1) begin : test_NUM_COEFFS
|
||||
for (genvar k = 0; k < $size(PARA_TEST); k = k + 1) begin : test_RELOADABLE
|
||||
for (genvar m = 0; m < $size(PARA_TEST); m = m + 1) begin : test_BLANK
|
||||
for (genvar n = 0; n < $size(PARA_TEST); n = n + 1) begin : test_EMBEDDED
|
||||
axi_fir_multisample_filter_tb #(
|
||||
.NUM_SPC(NUM_SPC_TEST[i]),
|
||||
.NUM_COEFFS(NUM_COEFFS_TEST[j]),
|
||||
.RELOADABLE_COEFFS(PARA_TEST[k]),
|
||||
.BLANK_OUTPUT(PARA_TEST[m]),
|
||||
.USE_EMBEDDED_REGS_COEFFS(PARA_TEST[n])
|
||||
) tb_i ();
|
||||
end : test_EMBEDDED
|
||||
end : test_BLANK
|
||||
end : test_RELOADABLE
|
||||
end : test_NUM_COEFFS
|
||||
end : test_NUM_SPC
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
//
|
||||
// Tests for combined symmetric and zero coefficient optimizations.
|
||||
// - Uses symmetric coefficient arrays with every 2nd coefficient set to zero
|
||||
// to enable both OPTIMIZE_FOR_SYMMETRIC_COEFFS and OPTIMIZE_FOR_ZERO_COEFFS.
|
||||
// - Only non-reloadable configurations (RELOADABLE_COEFFS=0) are tested.
|
||||
//
|
||||
// Test parameters:
|
||||
// - NUM_SPC: 1, 2, 3, 4, 8, 16 samples per cycle
|
||||
// - NUM_COEFFS: 5, 17, 41 taps (all odd)
|
||||
// - BLANK_OUTPUT: 0, 1
|
||||
// - RELOADABLE_COEFFS: 0 (fixed)
|
||||
// - USE_EMBEDDED_REGS_COEFFS: 0 (fixed)
|
||||
//
|
||||
generate
|
||||
//---------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//---------------------------------------------------------------------------
|
||||
localparam integer SYM_ZEROS_NUM_SPC_TEST[3:0] = {1, 2, 4, 8};
|
||||
// ... every 2nd coefficient is zero for testing zero-coefficient skipping optimization
|
||||
localparam bit [COEFF_WIDTH-1:0] MAX_COEFF_ARRAY_SYMMETRIC_ZEROS [41] = '{
|
||||
16'sd158, 16'sd0, 16'sd33, 16'sd0, -16'sd256,
|
||||
16'sd0, 16'sd573, 16'sd0, -16'sd1012, 16'sd0,
|
||||
16'sd1536, 16'sd0, -16'sd2097, 16'sd0, 16'sd1633,
|
||||
16'sd0, -16'sd3077, 16'sd0, 16'sd3370, 16'sd0,
|
||||
-16'sd19295, // center coefficient (odd number of taps)
|
||||
16'sd0, 16'sd3370, 16'sd0, -16'sd3077, 16'sd0,
|
||||
16'sd1633, 16'sd0, -16'sd2097, 16'sd0, 16'sd1536,
|
||||
16'sd0, -16'sd1012, 16'sd0, 16'sd573, 16'sd0,
|
||||
-16'sd256, 16'sd0, 16'sd33, 16'sd0, 16'sd158
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
// Nested test cases
|
||||
//---------------------------------------------------------------------------
|
||||
for (genvar i = 0; i < $size(SYM_ZEROS_NUM_SPC_TEST); i = i + 1) begin : test_NUM_SPC_SYM_ZEROS
|
||||
for (genvar j = 0; j < $size(NUM_COEFFS_TEST); j = j + 1) begin : test_NUM_COEFFS_SYM_ZEROS
|
||||
for (genvar m = 0; m < $size(PARA_TEST); m = m + 1) begin : test_BLANK_SYM_ZEROS
|
||||
// ... extract symmetric coefficients for odd number of taps
|
||||
localparam bit [COEFF_WIDTH-1:0] COEFFS_SYMM_ODD [NUM_COEFFS_TEST[j]] =
|
||||
MAX_COEFF_ARRAY_SYMMETRIC_ZEROS[($size(MAX_COEFF_ARRAY_SYMMETRIC_ZEROS)-NUM_COEFFS_TEST[j])/2 +: NUM_COEFFS_TEST[j]];
|
||||
// ... instantiate testbench
|
||||
axi_fir_multisample_filter_tb #(
|
||||
.NUM_SPC(SYM_ZEROS_NUM_SPC_TEST[i]),
|
||||
.NUM_COEFFS(NUM_COEFFS_TEST[j]),
|
||||
.RELOADABLE_COEFFS(0),
|
||||
.BLANK_OUTPUT(PARA_TEST[m]),
|
||||
.USE_EMBEDDED_REGS_COEFFS(0),
|
||||
.COEFF_WIDTH(COEFF_WIDTH),
|
||||
.COEFFS_VEC(COEFFS_SYMM_ODD)
|
||||
) tb_sym_odd_zeros_i ();
|
||||
end : test_BLANK_SYM_ZEROS
|
||||
end : test_NUM_COEFFS_SYM_ZEROS
|
||||
end : test_NUM_SPC_SYM_ZEROS
|
||||
endgenerate
|
||||
endmodule : axi_fir_multisample_filter_tb_wrapper
|
||||
Loading…
Reference in New Issue
Block a user