mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
dsp: cic: Add FIFO in CIC interpolation to allow tready bypass
This commit is contained in:
parent
5ea031a946
commit
d394ae626a
@ -33,10 +33,6 @@
|
||||
// Parameters:
|
||||
//
|
||||
// SPC : Number of samples per clock cycle (power of 2, >= 1).
|
||||
// OUT_WIDTH : Output sample width in bits (I + Q packed, each OUT_WIDTH/2 bits).
|
||||
// data_out.tdata must be SPC * OUT_WIDTH bits wide.
|
||||
// IN_WIDTH : Input sample width in bits (I + Q packed, each IN_WIDTH/2 bits).
|
||||
// data_in.tdata must be SPC * IN_WIDTH bits wide.
|
||||
// MAX_RATE : Maximum rate factor (decimation or interpolation ratio).
|
||||
// Sizes the compile-time shift lookup table.
|
||||
// ORDER : CIC filter order (number of integrator/comb stages).
|
||||
@ -57,8 +53,6 @@
|
||||
|
||||
module cic_barrel_shift #(
|
||||
parameter int SPC = 4,
|
||||
parameter int OUT_WIDTH = 32,
|
||||
parameter int IN_WIDTH = 128,
|
||||
parameter int MAX_RATE = 255,
|
||||
parameter int ORDER = 4
|
||||
)(
|
||||
@ -72,9 +66,11 @@ module cic_barrel_shift #(
|
||||
// Data out: SPC samples, each OUT_WIDTH bits wide ({I, Q} each OUT_WIDTH/2 bits)
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
localparam int MAX_SHIFT = IN_WIDTH/2 - OUT_WIDTH/2;
|
||||
localparam int SHIFT_W = $clog2(MAX_SHIFT + 1);
|
||||
localparam int IN_WIDTH = data_in.DATA_WIDTH / SPC;
|
||||
localparam int OUT_WIDTH = data_out.DATA_WIDTH / SPC;
|
||||
localparam int SHIFT_W = barrel_shift_width(IN_WIDTH, OUT_WIDTH);
|
||||
localparam int IN_COMP_W = IN_WIDTH/2;
|
||||
localparam int OUT_COMP_W = OUT_WIDTH/2;
|
||||
// One extra LSB so the bit dropped by the shift is available for rounding.
|
||||
|
||||
@ -217,8 +217,6 @@ module cic_filter_decim #(
|
||||
// Barrel-shift each I/Q component and extract SAMP_W/2 output bits.
|
||||
cic_barrel_shift #(
|
||||
.SPC (SPC),
|
||||
.IN_WIDTH (ACCUM_W),
|
||||
.OUT_WIDTH (SAMP_W),
|
||||
.MAX_RATE (MAX_DECIM),
|
||||
.ORDER (ORDER)
|
||||
) cic_barrel_shift_i (
|
||||
|
||||
@ -25,21 +25,31 @@
|
||||
// │ └───────────┬────────────┘ │
|
||||
// └──────────────┼────────────────┘
|
||||
// │
|
||||
// ┌────────────────────────┐
|
||||
// interp_factor─┤ Interpolator (×R) │
|
||||
// └───────────┬────────────┘
|
||||
// │
|
||||
// ┌──────────────┼────────────────┐
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Integrator 0 │ │
|
||||
// │ └───────────┬────────────┘ │
|
||||
// │ │ │ ORDER stages
|
||||
// │ ··· │ (full output rate)
|
||||
// │ │ │
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Integrator ORDER-1 │ │
|
||||
// │ └───────────┬────────────┘ │
|
||||
// └──────────────┼────────────────┘
|
||||
// ┌────────────────────────┐ tready
|
||||
// interp_factor─┤ Interpolator (×R) │<──────────────┐
|
||||
// └───────────┬────────────┘ │
|
||||
// │ │
|
||||
// ┌──────────────┼────────────────┐ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator 0 │ │ │
|
||||
// │ └────────────────────────┘ │ │
|
||||
// │ │ │ ORDER │
|
||||
// │ ··· │ stages │
|
||||
// │ │ │ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator ORDER-1 │ │ │
|
||||
// │ └────────────────────────┘ │ │
|
||||
// └──────────────┼────────────────┘ │
|
||||
// │ │
|
||||
// v │
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ Barrel shift │ │
|
||||
// └──────────────┬────────────────┘ │
|
||||
// │ │
|
||||
// v │
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ Output FIFO │───────────┘
|
||||
// └───────────────────────────────┘
|
||||
// │
|
||||
// v
|
||||
// data_out
|
||||
@ -99,6 +109,8 @@ module cic_filter_interp #(
|
||||
AxiStreamIf.slave data_in,
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
`include "../axi4s_sv/axi4s.vh"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//--------------------------------------------------------------------------
|
||||
@ -124,6 +136,7 @@ module cic_filter_interp #(
|
||||
// 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));
|
||||
AxiStreamIf #(SPC * ACCUM_W) interp_bus_extended (.clk(clk), .rst(rst));
|
||||
|
||||
// Integrator path: two buses connecting the interpolator to the integrator
|
||||
// chain and carrying the chain output.
|
||||
@ -131,6 +144,10 @@ module cic_filter_interp #(
|
||||
// integ_bus[1] carries the output of the ORDER-stage integrator chain.
|
||||
AxiStreamIf #(SPC * ACCUM_W) integ_bus [2] (.clk(clk), .rst(rst));
|
||||
|
||||
// Barrel Shift output bus
|
||||
AxiStreamIf #(data_out.DATA_WIDTH) barrel_shift_out (.clk(clk), .rst(rst));
|
||||
AxiStreamIf #(data_out.DATA_WIDTH) output_fifo_in (.clk(clk), .rst(rst));
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Register configuration parameters
|
||||
//---------------------------------------------------------------------------
|
||||
@ -223,9 +240,9 @@ module cic_filter_interp #(
|
||||
// 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;
|
||||
assign interp_bus_extended.tvalid = interp_bus.tvalid;
|
||||
assign interp_bus_extended.tlast = interp_bus.tlast;
|
||||
assign interp_bus.tready = interp_bus_extended.tready;
|
||||
|
||||
for (genvar samp_idx = 0; samp_idx < SPC; samp_idx++) begin : gen_interp_ext
|
||||
sign_extend #(
|
||||
@ -233,14 +250,14 @@ module cic_filter_interp #(
|
||||
.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])
|
||||
.out(interp_bus_extended.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])
|
||||
.out(interp_bus_extended.tdata[ACCUM_W*samp_idx + ACCUM_W/2 +: ACCUM_W/2])
|
||||
);
|
||||
end : gen_interp_ext
|
||||
|
||||
@ -248,6 +265,32 @@ module cic_filter_interp #(
|
||||
//---------------------------------------------------------------------------
|
||||
// Integrator chain: ORDER stages at full output rate
|
||||
//---------------------------------------------------------------------------
|
||||
// Starting here until the output FIFO the data flow is controlled by the
|
||||
// output FIFO backpressure.
|
||||
logic fifo_ready;
|
||||
logic input_blocked;
|
||||
|
||||
// Align input locking with AXI transfer.
|
||||
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 (interp_bus_extended.tvalid && interp_bus_extended.tready) begin
|
||||
input_blocked <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
`AXI4S_ASSIGN(integ_bus[0], interp_bus_extended)
|
||||
|
||||
// overwrite handshake signal to block input when FIFO is full
|
||||
interp_bus_extended.tready = integ_bus[0].tready & ~input_blocked;
|
||||
integ_bus[0].tvalid = interp_bus_extended.tvalid & ~input_blocked;
|
||||
end
|
||||
|
||||
cic_integrator #(
|
||||
.SAMP_W(ACCUM_W),
|
||||
.SPC (SPC),
|
||||
@ -264,8 +307,6 @@ module cic_filter_interp #(
|
||||
// Barrel-shift each I/Q component and extract SAMP_W/2 output bits.
|
||||
cic_barrel_shift #(
|
||||
.SPC (SPC),
|
||||
.IN_WIDTH (ACCUM_W),
|
||||
.OUT_WIDTH (SAMP_W),
|
||||
.MAX_RATE (MAX_INTERP),
|
||||
.ORDER (ORDER-1) // An Nth order CIC interpolation filter has a Gain of
|
||||
// R^(N-1) at DC, so we shift by ceil(log2(R^(N-1))) bits.
|
||||
@ -275,7 +316,63 @@ module cic_filter_interp #(
|
||||
.clear (clear),
|
||||
.rate_factor (interp_factor_reg),
|
||||
.data_in (integ_bus[1]),
|
||||
.data_out (data_out)
|
||||
.data_out (barrel_shift_out)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Output FIFO to absorb latency of bypassed modules
|
||||
//---------------------------------------------------------------------------
|
||||
import cic_utils_pkg::*;
|
||||
localparam int LATENCY = integrator_latency(SPC, ORDER)
|
||||
+ barrel_shift_latency(ACCUM_W, SAMP_W)
|
||||
+ 1 // fifo_ready register
|
||||
+ 1 // input_blocked register
|
||||
+ 1; // output FIFO output register offset for space calculation
|
||||
|
||||
|
||||
// assign the barrel shift output to the FIFO input, and always ready to
|
||||
// accept data. The FIFO will backpressure the integrator chain when it is full.
|
||||
always_comb begin
|
||||
`AXI4S_ASSIGN(output_fifo_in, barrel_shift_out)
|
||||
barrel_shift_out.tready = 1'b1;
|
||||
end
|
||||
|
||||
// Allow the integrator chain to continue processing as long as the FIFO has
|
||||
// enough space for the entire latency of the bypassed modules.
|
||||
logic [15:0] fifo_space;
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clear) begin
|
||||
fifo_ready <= 1'b0;
|
||||
end else begin
|
||||
fifo_ready <= (fifo_space > LATENCY);
|
||||
end
|
||||
end
|
||||
|
||||
// FIFO depth is the next power of two at or above twice the calculated
|
||||
// latency, ensuring sufficient buffering for constant data flow downstream
|
||||
// after stalls.
|
||||
localparam int FIFO_WIDTH = data_out.DATA_WIDTH + 1;
|
||||
logic [FIFO_WIDTH-1:0] fifo_i_tdata;
|
||||
logic [FIFO_WIDTH-1:0] fifo_o_tdata;
|
||||
|
||||
assign fifo_i_tdata = {output_fifo_in.tlast, output_fifo_in.tdata};
|
||||
assign {data_out.tlast, data_out.tdata} = fifo_o_tdata;
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(FIFO_WIDTH),
|
||||
.SIZE ($clog2(2*LATENCY))
|
||||
) output_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst | clear),
|
||||
.clear (1'b0),
|
||||
.i_tdata (fifo_i_tdata),
|
||||
.i_tvalid (output_fifo_in.tvalid),
|
||||
.i_tready (output_fifo_in.tready),
|
||||
.o_tdata (fifo_o_tdata),
|
||||
.o_tvalid (data_out.tvalid),
|
||||
.o_tready (data_out.tready),
|
||||
.space (fifo_space),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
endmodule : cic_filter_interp
|
||||
|
||||
@ -70,6 +70,8 @@ module cic_integrator #(
|
||||
AxiStreamIf.slave data_in,
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
// compile time check
|
||||
if (SAMP_W % 2 != 0) begin
|
||||
$error("cic_integrator: SAMP_W must be a multiple of 2");
|
||||
@ -82,7 +84,7 @@ module cic_integrator #(
|
||||
// final prefix_accum : 2 cycles
|
||||
// de-staircase : SPC-1 cycles
|
||||
// Total = ORDER*(SPC+1) + SPC + 1 cycles.
|
||||
localparam int CIC_LATENCY = ORDER * (SPC + 2-1) + SPC + 1;
|
||||
localparam int CIC_LATENCY = integrator_dsp_latency(SPC, ORDER);
|
||||
// OUT_FIFO_SIZE: The pipeline is gated by fifo_in_ready so it stalls in
|
||||
// lock-step with data_in.tready; the FIFO only needs to absorb the one
|
||||
// word in transit between the pipeline output register and the FIFO input.
|
||||
|
||||
@ -308,6 +308,8 @@ module cic_interpolator #(
|
||||
//---------------------------------------------------------------------------
|
||||
// Output Register
|
||||
//---------------------------------------------------------------------------
|
||||
logic [SPC*SAMP_W:0] fifo_out;
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (SPC*SAMP_W + 1 ),
|
||||
.SIZE (1 )
|
||||
@ -318,13 +320,15 @@ module cic_interpolator #(
|
||||
.i_tdata ({tlast_pipe[1], gen_tdata}),
|
||||
.i_tvalid (tvalid_pipe[1] ),
|
||||
.i_tready (gen_tready ),
|
||||
.o_tdata ({data_out.tlast, data_out.tdata}),
|
||||
.o_tdata (fifo_out ),
|
||||
.o_tvalid (data_out.tvalid ),
|
||||
.o_tready (data_out.tready ),
|
||||
.space ( ),
|
||||
.occupied ( )
|
||||
);
|
||||
|
||||
assign {data_out.tlast, data_out.tdata} = fifo_out;
|
||||
|
||||
endmodule : cic_interpolator
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
@ -96,8 +96,6 @@ module cic_barrel_shift_tb #(
|
||||
|
||||
cic_barrel_shift #(
|
||||
.SPC (SPC),
|
||||
.OUT_WIDTH(OUT_WIDTH),
|
||||
.IN_WIDTH (IN_WIDTH),
|
||||
.MAX_RATE (MAX_RATE),
|
||||
.ORDER (ORDER)
|
||||
) dut (
|
||||
|
||||
@ -12,6 +12,28 @@
|
||||
|
||||
package cic_utils_pkg;
|
||||
|
||||
function automatic int barrel_shift_width(
|
||||
input int in_width,
|
||||
input int out_width
|
||||
);
|
||||
return $clog2(in_width/2 - out_width/2 + 1);
|
||||
endfunction
|
||||
|
||||
// The latency of the barrel shift stage is the number of stages in the barrel
|
||||
// shifter plus one cycle for the output register.
|
||||
function automatic int barrel_shift_latency(input int in_width, input int out_width);
|
||||
return barrel_shift_width(in_width, out_width) + 1;
|
||||
endfunction
|
||||
|
||||
function automatic int integrator_dsp_latency(input int spc, input int order);
|
||||
return order * (spc + 1) + spc + 1;
|
||||
endfunction
|
||||
|
||||
// The latency of the integrator is the DSP latency plus one cycle for the output register.
|
||||
function automatic int integrator_latency(input int spc, input int order);
|
||||
return integrator_dsp_latency(spc, order) + 1;
|
||||
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