mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
dsp: cic: Add FIFO in CIC decimation to allow tready bypass
Added a Fifo to bypass handshaking inside of the cic filter decim data pipeline.
This commit is contained in:
parent
5b1bcabcf8
commit
58ab7a88cc
@ -7,63 +7,89 @@
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// CIC decimation filter for multiple samples per clock cycle, implementing
|
||||
// the classic Hogenauer (CIC) decimation topology:
|
||||
// CIC decimation filter for SPC complex samples per clock cycle. The datapath
|
||||
// implements the classic Hogenauer topology and adds rate-dependent gain
|
||||
// normalization and an output FIFO:
|
||||
//
|
||||
// data_in
|
||||
// │
|
||||
// v
|
||||
// ┌───────────────────────────────┐
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Integrator 0 │ │
|
||||
// │ └───────────┬────────────┘ │
|
||||
// │ │ │
|
||||
// │ ··· │ ORDER stages
|
||||
// │ │ │ (full input rate)
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Integrator ORDER-1 │ │
|
||||
// │ └───────────┬────────────┘ │
|
||||
// └──────────────┼────────────────┘
|
||||
// │
|
||||
// ┌────────────────────────┐
|
||||
// decim_factor─┤ Decimator (÷R) │
|
||||
// └───────────┬────────────┘
|
||||
// │
|
||||
// ┌──────────────┼────────────────┐
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Comb 0 │ │
|
||||
// │ └───────────┬────────────┘ │ ORDER stages
|
||||
// │ │ │ (decimated rate)
|
||||
// │ ... │
|
||||
// │ │ │
|
||||
// │ │ │
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Comb ORDER-1 │ │
|
||||
// │ └───────────┬────────────┘ │
|
||||
// └──────────────┼────────────────┘
|
||||
// (SPC x SAMP_W)
|
||||
// │ tready
|
||||
// v<─────────────────────────────────┐
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ I/Q sign extension │ │
|
||||
// │ SAMP_W -> ACCUM_W │ │
|
||||
// └──────────────┬────────────────┘ │
|
||||
// │ │
|
||||
// ┌──────────────┼────────────────┐ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator 0 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// │ │ │ ORDER stages │
|
||||
// │ ... │ (full input rate)│
|
||||
// │ │ │ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator ORDER-1 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// └──────────────┼────────────────┘ │
|
||||
// │ │
|
||||
// ┌────────────────────────┐ │
|
||||
// decim_factor ─┤ Decimator (÷R) │ │
|
||||
// └───────────┬────────────┘ │
|
||||
// │ │
|
||||
// ┌──────────────┼────────────────┐ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Comb 0 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// │ │ │ ORDER stages │
|
||||
// │ ... │ (decimated rate) │
|
||||
// │ │ │ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Comb ORDER-1 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// └──────────────┼────────────────┘ │
|
||||
// │ │
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ Normalize and round │ │
|
||||
// │ ACCUM_W -> SAMP_W │ │
|
||||
// └──────────────┬────────────────┘ │
|
||||
// │ │
|
||||
// ┌───────────────────────────────┐ fifo_space │
|
||||
// │ Output FIFO │──────────────────┘
|
||||
// └──────────────┬────────────────┘
|
||||
// │
|
||||
// v
|
||||
// data_out
|
||||
// data_out
|
||||
// (SPC x SAMP_W)
|
||||
//
|
||||
// ORDER integrator stages run at the full input rate, accumulating partial
|
||||
// sums across SPC parallel sample lanes. The single decimator then selects
|
||||
// every R-th sample group (keeping SPC output samples per word). Finally,
|
||||
// ORDER comb stages (y[n] = x[n] - x[n-D]) operate at the decimated rate.
|
||||
// Each input word contains SPC packed complex samples. The module sign-extends
|
||||
// each I and Q component to ACCUM_W, which includes the maximum CIC bit growth.
|
||||
// The ORDER integrator stages process every input sample. For decimation factor
|
||||
// R, the decimator consumes R input words and constructs one SPC-sample output
|
||||
// word by selecting every R-th integrated sample. The ORDER comb stages then
|
||||
// apply y[n] = x[n] - x[n-1] at the decimated rate.
|
||||
//
|
||||
// All stages use the same SAMP_W, which must be pre-sized to accommodate
|
||||
// the full CIC bit growth of N*log2(R_max*D_max). Internal stages do not
|
||||
// clip or round; the final rate-dependent output conversion rounds before
|
||||
// reducing the result to SAMP_W.
|
||||
// The barrel shifter normalizes the CIC gain by right-shifting each accumulator
|
||||
// component by ceil(log2(R**ORDER)). It rounds each result to SAMP_W/2 bits
|
||||
// before packing the SPC output samples.
|
||||
//
|
||||
// NOTE: This module is expected to run within the context of the axi_rate_change
|
||||
// module, which ensures that the number of input words is always a multiple
|
||||
// of the decimation factor and that the output tlast signals are correctly
|
||||
// aligned.
|
||||
// Downstream backpressure stops at the output FIFO. A registered credit gate
|
||||
// admits input words only while the FIFO has room for every possible output
|
||||
// already in flight, using the worst-case R=1 rate. Once accepted, a word can
|
||||
// traverse the datapath without stalling on data_out.tready. If the reserved
|
||||
// capacity runs low, data_in.tready deasserts until FIFO space becomes available.
|
||||
//
|
||||
// The decimation factor is driven directly on decim_factor. It must be valid
|
||||
// before reset is released and remain stable while data is actively
|
||||
// processed.
|
||||
//
|
||||
// NOTE: This module is expected to run within axi_rate_change, which ensures
|
||||
// that each packet contains a multiple of R input words and that output tlast
|
||||
// signals are correctly aligned.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// SPC : Number of samples per clock cycle (must be power of 2, >= 1).
|
||||
// SAMP_W : Bit width of each sample (I + Q packed, each SAMP_W/2 bits).
|
||||
// SPC : Number of samples per clock cycle (must be power of 2, >= 1).
|
||||
// SAMP_W : External sample width (I + Q packed, each SAMP_W/2 bits).
|
||||
// MAX_DECIM : Maximum decimation factor supported. Sizes internal registers.
|
||||
// ORDER : CIC filter order (number of integrator and comb stages).
|
||||
//
|
||||
@ -97,6 +123,22 @@ module cic_filter_decim #(
|
||||
localparam int ACCUM_W = 2 * (SAMP_W/2 + $clog2(MAX_DECIM*MAX_DELAY)*ORDER);
|
||||
localparam int COMP_W = ACCUM_W/2; // Width of each I/Q component in the accumulator
|
||||
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
// Reserve for the worst-case R=1 path, where every input word produces an
|
||||
// output word, plus one cycle each for the registered FIFO-space decision
|
||||
// and input_blocked state.
|
||||
localparam int DATAPATH_LATENCY = integrator_latency(SPC, ORDER)
|
||||
+ decimator_latency()
|
||||
+ comb_filter_latency(ORDER)
|
||||
+ barrel_shift_latency(ACCUM_W, SAMP_W)
|
||||
+ 2; // Additional cycles for fifo_ready and input_blocked
|
||||
|
||||
// Set the output FIFO size to twice the datapath latency to allow continuous
|
||||
// downstream traffic.
|
||||
localparam int OUTPUT_FIFO_SIZE = $clog2(2 * DATAPATH_LATENCY);
|
||||
localparam int OUTPUT_FIFO_DEPTH = 1 << OUTPUT_FIFO_SIZE;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Internal AXI-Stream buses
|
||||
//---------------------------------------------------------------------------
|
||||
@ -109,6 +151,10 @@ module cic_filter_decim #(
|
||||
// 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));
|
||||
|
||||
// The output of the barrel shift stage after the bus width is reduced back
|
||||
// to SAMP_W bits from ACCUM_W bits.
|
||||
AxiStreamIf #(SPC * SAMP_W) barrel_shift_out (.clk(clk), .rst(rst));
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Sign extension to ACCUM_W bits for the input samples
|
||||
@ -118,9 +164,27 @@ module cic_filter_decim #(
|
||||
// bits, then pack into integ_bus[0].tdata as ACCUM_W-wide samples.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
assign integ_bus[0].tvalid = data_in.tvalid;
|
||||
assign integ_bus[0].tlast = data_in.tlast;
|
||||
assign data_in.tready = integ_bus[0].tready;
|
||||
logic fifo_ready;
|
||||
logic input_blocked;
|
||||
|
||||
// Hold the input blocked until FIFO credit is restored.
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clear) begin
|
||||
input_blocked <= 1'b0;
|
||||
end else begin
|
||||
if (fifo_ready) begin
|
||||
input_blocked <= 1'b0;
|
||||
end else if (integ_bus[0].tvalid && integ_bus[0].tready) begin
|
||||
input_blocked <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
integ_bus[0].tvalid = data_in.tvalid & ~input_blocked;
|
||||
integ_bus[0].tlast = data_in.tlast;
|
||||
data_in.tready = integ_bus[0].tready & ~input_blocked;
|
||||
end
|
||||
|
||||
for (genvar samp_idx = 0; samp_idx < SPC; samp_idx++) begin : gen_sign_ext
|
||||
// Q component (lower half of each sample)
|
||||
@ -200,9 +264,55 @@ module cic_filter_decim #(
|
||||
.clear (clear),
|
||||
.rate_factor (decim_factor),
|
||||
.data_in (comb_bus[ORDER]),
|
||||
.data_out (data_out)
|
||||
.data_out (barrel_shift_out)
|
||||
);
|
||||
|
||||
// Downstream backpressure terminates at the output FIFO. The credit gate at
|
||||
// data_in reserves enough room for every word already in the datapath.
|
||||
assign barrel_shift_out.tready = 1'b1;
|
||||
|
||||
localparam FIFO_WIDTH = data_out.DATA_WIDTH + 1;
|
||||
logic [15:0] fifo_space;
|
||||
logic output_fifo_i_tready;
|
||||
logic [FIFO_WIDTH-1:0] fifo_data_in;
|
||||
logic [FIFO_WIDTH-1:0] fifo_data_out;
|
||||
|
||||
assign fifo_data_in = {barrel_shift_out.tlast, barrel_shift_out.tdata};
|
||||
assign {data_out.tlast, data_out.tdata} = fifo_data_out;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clear) begin
|
||||
fifo_ready <= 1'b0;
|
||||
end else begin
|
||||
fifo_ready <= (fifo_space > DATAPATH_LATENCY);
|
||||
end
|
||||
end
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(FIFO_WIDTH),
|
||||
.SIZE (OUTPUT_FIFO_SIZE)
|
||||
) output_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (clear),
|
||||
.i_tdata (fifo_data_in),
|
||||
.i_tvalid (barrel_shift_out.tvalid),
|
||||
.i_tready (output_fifo_i_tready),
|
||||
.o_tdata (fifo_data_out),
|
||||
.o_tvalid (data_out.tvalid),
|
||||
.o_tready (data_out.tready),
|
||||
.space (fifo_space),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
// synthesis translate_off
|
||||
assert property (
|
||||
@(posedge clk) disable iff (rst || clear)
|
||||
barrel_shift_out.tvalid |-> output_fifo_i_tready
|
||||
) else
|
||||
$error("cic_filter_decim: output FIFO not ready for valid data");
|
||||
// synthesis translate_on
|
||||
|
||||
endmodule : cic_filter_decim
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
@ -155,6 +155,27 @@ module cic_filter_decim_tb #(
|
||||
.data_out (o_data)
|
||||
);
|
||||
|
||||
// Monitor output packet activity during drain phase of backpressure test.
|
||||
bit monitor_output_tvalid;
|
||||
bit output_packet_active;
|
||||
|
||||
always_ff @(posedge clk) begin : track_output_packet
|
||||
if (rst || clr) begin
|
||||
output_packet_active <= 1'b0;
|
||||
end else if (o_data.tvalid && o_data.tready) begin
|
||||
output_packet_active <= !o_data.tlast;
|
||||
end
|
||||
end
|
||||
|
||||
always @(negedge o_data.tvalid) begin : assert_output_tvalid
|
||||
if (monitor_output_tvalid && output_packet_active) begin
|
||||
`ASSERT_ERROR(0, "Output tvalid deasserted before packet end")
|
||||
end
|
||||
end
|
||||
|
||||
// Saturation threshold for the output FIFO, used in backpressure tests.
|
||||
localparam int SATURATION_WORDS = (2 ** (dut.OUTPUT_FIFO_SIZE + 1)) ;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Reference Model
|
||||
//---------------------------------------------------------------------------
|
||||
@ -292,6 +313,70 @@ module cic_filter_decim_tb #(
|
||||
@(negedge rst);
|
||||
endtask : test_cic_decim
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Fill the output FIFO, observe input backpressure, then drain all data.
|
||||
//--------------------------------------------------------------------------
|
||||
task automatic test_output_fifo_backpressure();
|
||||
axis_pkt_t input_pkt;
|
||||
axis_pkt_t rx_pkt;
|
||||
sample_queue_t input_samples;
|
||||
sample_queue_t expected_samples;
|
||||
sample_queue_t actual_samples;
|
||||
model_sample_queue_t model_in;
|
||||
model_sample_queue_t model_out;
|
||||
|
||||
set_decim_factor(1);
|
||||
input_pkt = test_utils_c::generate_random_packet(SATURATION_WORDS);
|
||||
input_samples = test_utils_c::pkt_to_samples(input_pkt.copy());
|
||||
|
||||
model_in = {};
|
||||
foreach (input_samples[i]) begin
|
||||
model_in.push_back(sign_extend_sample(input_samples[i]));
|
||||
end
|
||||
model.process(model_in, 1, model_out);
|
||||
expected_samples = {};
|
||||
foreach (model_out[i]) begin
|
||||
expected_samples.push_back(shift_sample(model_out[i], 1));
|
||||
end
|
||||
|
||||
bfm.set_master_stall_prob(0);
|
||||
bfm.set_slave_stall_prob(100);
|
||||
bfm.put(input_pkt);
|
||||
|
||||
// Ignore the initial credit startup delay, then wait for the FIFO credits
|
||||
// to be exhausted while input data remains pending.
|
||||
do begin
|
||||
@(posedge clk);
|
||||
end while (!i_data.tvalid || !i_data.tready);
|
||||
@(negedge i_data.tready);
|
||||
`ASSERT_ERROR(i_data.tvalid, "Input packet ended before backpressure was observed")
|
||||
|
||||
// Allow all admitted words to reach the output FIFO while it remains
|
||||
// blocked. No new input should be accepted until output space is released.
|
||||
clk_gen.clk_wait_r(SATURATION_WORDS);
|
||||
`ASSERT_ERROR(!i_data.tready, "Input credits reopened while output remained blocked")
|
||||
|
||||
monitor_output_tvalid = 1;
|
||||
bfm.set_slave_stall_prob(0);
|
||||
bfm.get(rx_pkt);
|
||||
monitor_output_tvalid = 0;
|
||||
actual_samples = test_utils_c::pkt_to_samples(rx_pkt.copy());
|
||||
|
||||
`ASSERT_ERROR(actual_samples.size() == expected_samples.size(),
|
||||
$sformatf("Expected %0d output samples, received %0d",
|
||||
expected_samples.size(), actual_samples.size()))
|
||||
foreach (expected_samples[i]) begin
|
||||
`ASSERT_ERROR(actual_samples[i] === expected_samples[i],
|
||||
$sformatf("Backpressure sample %0d: expected 0x%0h, got 0x%0h",
|
||||
i, expected_samples[i], actual_samples[i]))
|
||||
end
|
||||
|
||||
bfm.set_master_stall_prob(STALL_PROB_DEFAULT);
|
||||
bfm.set_slave_stall_prob(STALL_PROB_DEFAULT);
|
||||
clk_gen.reset();
|
||||
@(negedge rst);
|
||||
endtask : test_output_fifo_backpressure
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Main Test Process
|
||||
//---------------------------------------------------------------------------
|
||||
@ -325,6 +410,13 @@ module cic_filter_decim_tb #(
|
||||
end
|
||||
test.end_test();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Test: output FIFO saturation and recovery
|
||||
//-------------------------------------------------------------------------
|
||||
test.start_test("CIC decim output FIFO backpressure", 1ms);
|
||||
test_output_fifo_backpressure();
|
||||
test.end_test();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Test: ramp input (I=Q=1,2,3,...) with random decimation factor
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@ -34,6 +34,17 @@ package cic_utils_pkg;
|
||||
return integrator_dsp_latency(spc, order) + 1;
|
||||
endfunction
|
||||
|
||||
// The decimator has a registered compute_output stage and a 1-cycle
|
||||
// output FIFO.
|
||||
function automatic int decimator_latency();
|
||||
return 2;
|
||||
endfunction
|
||||
|
||||
// Each comb stage contains one registered output FIFO stage.
|
||||
function automatic int comb_filter_latency(input int order);
|
||||
return order;
|
||||
endfunction
|
||||
|
||||
class cic_utils #(
|
||||
parameter int SPC = 8, // Number of samples processed per clock cycle
|
||||
parameter int SAMP_W = 48 // Input/output data width
|
||||
|
||||
Loading…
Reference in New Issue
Block a user