FIR Filter
More Complicated AXI Streaming
Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.
Fitting a FIR into an AXIS Module
We've already gotten some practice testing our simple j_math
module with the AXIS standard on the previous page. We'd now like to merge in the FIR filter from the previous week and make it AXI-Streaming Compliant. It'll have exactly the same input and output interfaces (two AXIS channels, one in (slave), one out (master)). However the calculation of an FIR on any given timestep involves a lot more math and so requires a carefully laid out design as you did last week.
I hesitate to say this, since I know it can be hard, but in my opinion, the difficulty of designing an AXIS FIR has less to do with the FIR itself (watch your signed math and pipeline your signals appropriately) and more with handling some of the specs of AXIS. In particular:
Using the concepts on the previous page (both Drivers, Monitors, and Scoreboards as well as the way to handle back-pressure) and your previously verified FIR_15 module, create a AXIS-compliant one on this page.
Note while the math you're doing with the FIR is far more nasty (and stateful) than the simpler j_math
on the previous page, you should be able to take almost all the ideas and strategies of how that handled the control/flow signals and apply them directly to the FIR since we're ok with our FIR immediately having outputs one clock cycle after it starts "from rest".
Here's a skeleton to work with:
`default_nettype none
module axis_fir_15 #
(
parameter integer C_S00_AXIS_TDATA_WIDTH = 32,
parameter integer C_M00_AXIS_TDATA_WIDTH = 32
)
(
// Ports of Axi Slave Bus Interface S00_AXIS
input wire s00_axis_aclk, s00_axis_aresetn,
input wire s00_axis_tlast, s00_axis_tvalid,
input wire [C_S00_AXIS_TDATA_WIDTH-1 : 0] s00_axis_tdata,
input wire [(C_S00_AXIS_TDATA_WIDTH/8)-1: 0] s00_axis_tstrb,
output logic s00_axis_tready,
//FIR coefficients:
input wire signed [NUM_COEFFS-1:0][7:0] coeffs,
// Ports of Axi Master Bus Interface M00_AXIS
input wire m00_axis_aclk, m00_axis_aresetn,
input wire m00_axis_tready,
output logic m00_axis_tvalid, m00_axis_tlast,
output logic [C_M00_AXIS_TDATA_WIDTH-1 : 0] m00_axis_tdata,
output logic [(C_M00_AXIS_TDATA_WIDTH/8)-1: 0] m00_axis_tstrb
);
localparam NUM_COEFFS = 15;
//i previously used some intermediate terms and then inialized them all
//to zero
logic signed [31:0] intmdt_term [NUM_COEFFS -1:0];
initial begin
for(int i=0; i<NUM_COEFFS; i++)begin
intmdt_term[i] = 0;
end
$display("DONE!");
end
assign s00_axis_tready = m00_axis_tready; //immediate (for now)
endmodule
`default_nettype wire
Verification and TLAST
On the previous page you already built up some driver and monitor infrastructure to poke at this system. In particular, you need to make sure that given the set of data you drove into on the previous page. Honestly, that testbench is probably pretty good to use for this one with a couple of exceptions:
- It would be good to check the values that come through this filter against lfilter like you did last week. Using the input and output arrays as they are might be useful for plotting, which I encourage like last time, but the Scoreboard is going to want a stateful model callback to work correctly. To do that, read lfilter's documentation. There is an argument
zi
that you should be able to use to set and update the filter delay values to use it in a stateful manner one sample at a time! - Pay close attention to the
TLAST
. In the next assignment on the Pynq, missing TLAST signals will cause catastrophic freeze/crashes on the Pynq board due to the DMA going into a deadlock or something. You may want to update what you pass back as values to make sure not on the values are correct but also if the samples are accompanied byTLAST=1
when they should!
axis_fir_15.sv
axis_fir_15