fpga: dsp: Reduce CIC comb bus width for cic_filter_interp

We can reduce the bus width of the comb filter stage in the CIC
interpolation filter, as it is the first module in the CIC filter and
only adds one bit per order stage. Therefore, we do not need to
immediately go to the full accumulation bit width needed for the later
integrator stages of the CIC interpolation filter, and can save some
FPGA resources by only extending to the required width for each CIC
stage.

The same logic also applies to the interpolation stage, which only adds
zeros into the bitstream and therefore does not change the bus width.
This commit is contained in:
Niels Garibaldi 2026-09-02 12:34:16 +02:00 committed by mkoop-ni
parent 57df0ca685
commit 4904d06da2

View File

@ -49,8 +49,10 @@
// Finally, ORDER integrator stages accumulate partial sums across SPC
// parallel sample lanes at the full output rate.
//
// Internally, all stages operate at ACCUM_W bits, which is automatically
// computed to accommodate the full CIC bit growth of N*log2(R_max*D_max).
// Comb and interpolator stages use COMB_W bits. COMB_W provides ORDER bits
// of growth per I/Q component for the comb cascade. Integrator stages use
// ACCUM_W bits, automatically computed to provide
// ORDER*ceil(log2(MAX_INTERP*MAX_DELAY)) bits of growth per component.
// Internal stages do not clip or round. The output is normalized back to
// SAMP_W with signed round-to-nearest quantization.
//
@ -102,9 +104,14 @@ module cic_filter_interp #(
//--------------------------------------------------------------------------
localparam int MAX_DELAY = 1; // Maximum comb delay in samples
// Accumulator width to accommodate max CIC growth without overflow:
// Accumulator width for the maximum integrator growth without overflow.
localparam int ACCUM_W = 2 * (SAMP_W/2 + $clog2(MAX_INTERP*MAX_DELAY)*ORDER);
localparam int COMP_W = ACCUM_W/2; // Width of each I/Q component in the accumulator
// A cascade of ORDER comb stages has coefficients whose absolute sum is
// 2**ORDER, so each component needs ORDER bits of growth. The comb stages
// precede the interpolator and can therefore use less width than the full
// accumulation path.
localparam int COMB_W = 2 * (SAMP_W/2 + ORDER);
//---------------------------------------------------------------------------
// Internal AXI-Stream buses
@ -112,11 +119,16 @@ module cic_filter_interp #(
// Comb chain: ORDER+1 buses connecting comb filter inputs/outputs.
// comb_bus[0] is driven by the sign-extended input.
// comb_bus[i] for i > 0 carries the output of comb stage i-1.
AxiStreamIf #(SPC * ACCUM_W) comb_bus [ORDER+1] (.clk(clk), .rst(rst));
AxiStreamIf #(SPC * COMB_W) comb_bus [ORDER+1] (.clk(clk), .rst(rst));
// Integrator chain: ORDER+1 buses connecting integrator outputs.
// integ_bus[0] is driven by the interpolator output.
// integ_bus[i] for i > 0 carries the output of integrator stage i-1.
// The interpolator operates at COMB_W bits. Its output is sign-extended to
// ACCUM_W bits before entering the integrator chain.
AxiStreamIf #(SPC * COMB_W) interp_bus (.clk(clk), .rst(rst));
// Integrator path: two buses connecting the interpolator to the integrator
// chain and carrying the chain output.
// integ_bus[0] is driven by the interpolator.
// integ_bus[1] carries the output of the ORDER-stage integrator chain.
AxiStreamIf #(SPC * ACCUM_W) integ_bus [2] (.clk(clk), .rst(rst));
//---------------------------------------------------------------------------
@ -139,11 +151,11 @@ module cic_filter_interp #(
//--------------------------------------------------------------------------
// Sign extension to ACCUM_W bits for the input samples
// Sign extension to COMB_W bits for the input samples
//
// Each sample in data_in is SAMP_W bits wide ({I, Q}). Sign-extend each I
// and Q component independently from SAMP_W/2 to COMP_W bits, then pack into
// comb_bus[0].tdata as ACCUM_W-wide samples.
// and Q component independently from SAMP_W/2 to COMB_W/2 bits, then pack
// into comb_bus[0].tdata as COMB_W-wide samples.
//--------------------------------------------------------------------------
assign comb_bus[0].tvalid = data_in.tvalid;
@ -154,18 +166,18 @@ module cic_filter_interp #(
// Q component (lower half of each sample)
sign_extend #(
.bits_in (SAMP_W/2),
.bits_out(COMP_W)
.bits_out(COMB_W/2)
) sign_ext_q (
.in (data_in.tdata[SAMP_W*samp_idx +: SAMP_W/2]),
.out(comb_bus[0].tdata[ACCUM_W*samp_idx +: COMP_W])
.out(comb_bus[0].tdata[COMB_W*samp_idx +: COMB_W/2])
);
// I component (upper half of each sample)
sign_extend #(
.bits_in (SAMP_W/2),
.bits_out(COMP_W)
.bits_out(COMB_W/2)
) sign_ext_i (
.in (data_in.tdata[SAMP_W*samp_idx + SAMP_W/2 +: SAMP_W/2]),
.out(comb_bus[0].tdata[ACCUM_W*samp_idx + COMP_W +: COMP_W])
.out(comb_bus[0].tdata[COMB_W*samp_idx + COMB_W/2 +: COMB_W/2])
);
end : gen_sign_ext
@ -175,7 +187,7 @@ module cic_filter_interp #(
//---------------------------------------------------------------------------
for (genvar stage = 0; stage < ORDER; stage++) begin : gen_combs
cic_comb_filter #(
.ACCUM_W (ACCUM_W),
.ACCUM_W (COMB_W),
.SPC (SPC),
.FIFO_SIZE((stage == ORDER-1)? 1 : 0)
) cic_comb_filter_i (
@ -192,18 +204,46 @@ module cic_filter_interp #(
// Interpolator: increases sample rate by interp_factor
//---------------------------------------------------------------------------
cic_interpolator #(
.SAMP_W (ACCUM_W),
.SAMP_W (COMB_W),
.SPC (SPC),
.R_MAX (MAX_INTERP)
) cic_interpolator_i (
.clk (clk),
.rst (rst || clear),
.data_in (comb_bus[ORDER]),
.data_out (integ_bus[0]),
.data_out (interp_bus),
.interp_factor (interp_factor_reg),
.interp_changed(config_changed_reg)
);
//--------------------------------------------------------------------------
// Sign extension from COMB_W to ACCUM_W bits.
//
// The interpolator does not increase the numeric width. The integrator
// stages require ACCUM_W bits to accommodate their accumulated growth.
//--------------------------------------------------------------------------
assign integ_bus[0].tvalid = interp_bus.tvalid;
assign integ_bus[0].tlast = interp_bus.tlast;
assign interp_bus.tready = integ_bus[0].tready;
for (genvar samp_idx = 0; samp_idx < SPC; samp_idx++) begin : gen_interp_ext
sign_extend #(
.bits_in (COMB_W/2),
.bits_out(ACCUM_W/2)
) sign_ext_q (
.in (interp_bus.tdata[COMB_W*samp_idx +: COMB_W/2]),
.out(integ_bus[0].tdata[ACCUM_W*samp_idx +: ACCUM_W/2])
);
sign_extend #(
.bits_in (COMB_W/2),
.bits_out(ACCUM_W/2)
) sign_ext_i (
.in (interp_bus.tdata[COMB_W*samp_idx + COMB_W/2 +: COMB_W/2]),
.out(integ_bus[0].tdata[ACCUM_W*samp_idx + ACCUM_W/2 +: ACCUM_W/2])
);
end : gen_interp_ext
//---------------------------------------------------------------------------
// Integrator chain: ORDER stages at full output rate