Skip to content

Commit

Permalink
axis: Add AXI stream combination async FIFO/adapter module and testbench
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Forencich <[email protected]>
  • Loading branch information
alexforencich committed Feb 6, 2025
1 parent b1dcd8c commit d52aa2f
Show file tree
Hide file tree
Showing 5 changed files with 1,201 additions and 0 deletions.
4 changes: 4 additions & 0 deletions rtl/axis/taxi_axis_async_fifo_adapter.f
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
taxi_axis_async_fifo_adapter.sv
taxi_axis_async_fifo.sv
taxi_axis_adapter.sv
taxi_axis_if.sv
244 changes: 244 additions & 0 deletions rtl/axis/taxi_axis_async_fifo_adapter.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/

`resetall
`timescale 1ns / 1ps
`default_nettype none

/*
* AXI4-Stream asynchronous FIFO with width converter
*/
module taxi_axis_async_fifo_adapter #
(
// FIFO depth in words
// KEEP_W words per cycle if KEEP_EN set
// Rounded up to nearest power of 2 cycles
parameter DEPTH = 4096,
// number of RAM pipeline registers in FIFO
parameter RAM_PIPELINE = 1,
// use output FIFO
// When set, the RAM read enable and pipeline clock enables are removed
parameter logic OUTPUT_FIFO_EN = 1'b0,
// Frame FIFO mode - operate on frames instead of cycles
// When set, m_axis_tvalid will not be deasserted within a frame
// Requires LAST_EN set
parameter logic FRAME_FIFO = 1'b0,
// tuser value for bad frame marker
parameter USER_BAD_FRAME_VALUE = 1'b1,
// tuser mask for bad frame marker
parameter USER_BAD_FRAME_MASK = 1'b1,
// Drop frames larger than FIFO
// Requires FRAME_FIFO set
parameter logic DROP_OVERSIZE_FRAME = FRAME_FIFO,
// Drop frames marked bad
// Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set
parameter logic DROP_BAD_FRAME = 1'b0,
// Drop incoming frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set
parameter logic DROP_WHEN_FULL = 1'b0,
// Mark incoming frames as bad frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO to be clear
parameter logic MARK_WHEN_FULL = 1'b0,
// Enable pause request input
parameter logic PAUSE_EN = 1'b0,
// Pause between frames
parameter logic FRAME_PAUSE = FRAME_FIFO
)
(
/*
* AXI4-Stream input (sink)
*/
input wire logic s_clk,
input wire logic s_rst,
taxi_axis_if.snk s_axis,

/*
* AXI4-Stream output (source)
*/
input wire logic m_clk,
input wire logic m_rst,
taxi_axis_if.src m_axis,

/*
* Pause
*/
input wire logic s_pause_req = 1'b0,
output wire logic s_pause_ack,
input wire logic m_pause_req = 1'b0,
output wire logic m_pause_ack,

/*
* Status
*/
output wire logic [$clog2(DEPTH):0] s_status_depth,
output wire logic [$clog2(DEPTH):0] s_status_depth_commit,
output wire logic s_status_overflow,
output wire logic s_status_bad_frame,
output wire logic s_status_good_frame,
output wire logic [$clog2(DEPTH):0] m_status_depth,
output wire logic [$clog2(DEPTH):0] m_status_depth_commit,
output wire logic m_status_overflow,
output wire logic m_status_bad_frame,
output wire logic m_status_good_frame
);

// extract parameters
localparam S_DATA_W = s_axis.DATA_W;
localparam logic S_KEEP_EN = s_axis.KEEP_EN;
localparam S_KEEP_W = s_axis.KEEP_W;
localparam logic S_STRB_EN = s_axis.STRB_EN;

localparam M_DATA_W = m_axis.DATA_W;
localparam logic M_KEEP_EN = m_axis.KEEP_EN;
localparam M_KEEP_W = m_axis.KEEP_W;
localparam logic M_STRB_EN = m_axis.STRB_EN;

// force keep width to 1 when disabled
localparam S_BYTE_LANES = S_KEEP_EN ? S_KEEP_W : 1;
localparam M_BYTE_LANES = M_KEEP_EN ? M_KEEP_W : 1;

// bus byte sizes (must be identical)
localparam S_BYTE_SIZE = S_DATA_W / S_BYTE_LANES;
localparam M_BYTE_SIZE = M_DATA_W / M_BYTE_LANES;
// output bus is wider
localparam EXPAND_BUS = M_BYTE_LANES > S_BYTE_LANES;
// total data and keep widths
localparam DATA_W = EXPAND_BUS ? M_DATA_W : S_DATA_W;
localparam KEEP_W = EXPAND_BUS ? M_BYTE_LANES : S_BYTE_LANES;
localparam KEEP_EN = EXPAND_BUS ? M_KEEP_EN : S_KEEP_EN;
localparam STRB_EN = M_STRB_EN && S_STRB_EN;

// check configuration
if (S_BYTE_SIZE * S_BYTE_LANES != S_DATA_W)
$fatal(0, "Error: input data width not evenly divisible (instance %m)");

if (M_BYTE_SIZE * M_BYTE_LANES != M_DATA_W)
$fatal(0, "Error: output data width not evenly divisible (instance %m)");

if (S_BYTE_SIZE != M_BYTE_SIZE)
$fatal(0, "Error: byte size mismatch (instance %m)");

taxi_axis_if #(
.DATA_W(DATA_W),
.KEEP_EN(KEEP_EN),
.KEEP_W(KEEP_W),
.STRB_EN(s_axis.STRB_EN),
.LAST_EN(s_axis.LAST_EN),
.ID_EN(s_axis.ID_EN),
.ID_W(s_axis.ID_W),
.DEST_EN(s_axis.DEST_EN),
.DEST_W(s_axis.DEST_W),
.USER_EN(s_axis.USER_EN),
.USER_W(s_axis.USER_W)
) axis_pre_fifo();

taxi_axis_if #(
.DATA_W(DATA_W),
.KEEP_EN(KEEP_EN),
.KEEP_W(KEEP_W),
.STRB_EN(m_axis.STRB_EN),
.LAST_EN(m_axis.LAST_EN),
.ID_EN(m_axis.ID_EN),
.ID_W(m_axis.ID_W),
.DEST_EN(m_axis.DEST_EN),
.DEST_W(m_axis.DEST_W),
.USER_EN(m_axis.USER_EN),
.USER_W(m_axis.USER_W)
) axis_post_fifo();

taxi_axis_adapter
pre_fifo_adapter_inst (
.clk(s_clk),
.rst(s_rst),

/*
* AXI4-Stream input (sink)
*/
.s_axis(s_axis),

/*
* AXI4-Stream output (source)
*/
.m_axis(axis_pre_fifo)
);

taxi_axis_async_fifo #(
.DEPTH(DEPTH),
.RAM_PIPELINE(RAM_PIPELINE),
.OUTPUT_FIFO_EN(OUTPUT_FIFO_EN),
.FRAME_FIFO(FRAME_FIFO),
.USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE),
.USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK),
.DROP_OVERSIZE_FRAME(DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(DROP_BAD_FRAME),
.DROP_WHEN_FULL(DROP_WHEN_FULL),
.MARK_WHEN_FULL(MARK_WHEN_FULL),
.PAUSE_EN(PAUSE_EN),
.FRAME_PAUSE(FRAME_PAUSE)
)
fifo_inst (
/*
* AXI4-Stream input (sink)
*/
.s_clk(s_clk),
.s_rst(s_rst),
.s_axis(axis_pre_fifo),

/*
* AXI4-Stream output (source)
*/
.m_clk(m_clk),
.m_rst(m_rst),
.m_axis(axis_post_fifo),

/*
* Pause
*/
.s_pause_req(s_pause_req),
.s_pause_ack(s_pause_ack),
.m_pause_req(m_pause_req),
.m_pause_ack(m_pause_ack),

/*
* Status
*/
.s_status_depth(s_status_depth),
.s_status_depth_commit(s_status_depth_commit),
.s_status_overflow(s_status_overflow),
.s_status_bad_frame(s_status_bad_frame),
.s_status_good_frame(s_status_good_frame),
.m_status_depth(m_status_depth),
.m_status_depth_commit(m_status_depth_commit),
.m_status_overflow(m_status_overflow),
.m_status_bad_frame(m_status_bad_frame),
.m_status_good_frame(m_status_good_frame)
);

taxi_axis_adapter
post_fifo_adapter_inst (
.clk(m_clk),
.rst(m_rst),

/*
* AXI4-Stream input (sink)
*/
.s_axis(axis_post_fifo),

/*
* AXI4-Stream output (source)
*/
.m_axis(m_axis)
);

endmodule

`resetall
71 changes: 71 additions & 0 deletions tb/axis/taxi_axis_async_fifo_adapter/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2021-2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich

TOPLEVEL_LANG = verilog

SIM ?= verilator
WAVES ?= 0

COCOTB_HDL_TIMEUNIT = 1ns
COCOTB_HDL_TIMEPRECISION = 1ps

DUT = taxi_axis_async_fifo_adapter
COCOTB_TEST_MODULES = test_$(DUT)
COCOTB_TOPLEVEL = test_$(DUT)
MODULE = $(COCOTB_TEST_MODULES)
TOPLEVEL = $(COCOTB_TOPLEVEL)
VERILOG_SOURCES += $(COCOTB_TOPLEVEL).sv
VERILOG_SOURCES += ../../../rtl/axis/$(DUT).f

# handle file list files
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))

# module parameters
export PARAM_S_DATA_W := 8
export PARAM_S_KEEP_EN := $(shell echo $$(( $(PARAM_S_DATA_W) > 8 )))
export PARAM_S_KEEP_W := $(shell echo $$(( ( $(PARAM_S_DATA_W) + 7 ) / 8 )))
export PARAM_S_STRB_EN := 0
export PARAM_M_DATA_W := 8
export PARAM_M_KEEP_EN := $(shell echo $$(( $(PARAM_M_DATA_W) > 8 )))
export PARAM_M_KEEP_W := $(shell echo $$(( ( $(PARAM_M_DATA_W) + 7 ) / 8 )))
export PARAM_M_STRB_EN := $(PARAM_S_STRB_EN)
export PARAM_DEPTH := $(shell echo $$(( 1024 * ($(PARAM_S_KEEP_W) > $(PARAM_M_KEEP_W) ? $(PARAM_S_KEEP_W) : $(PARAM_M_KEEP_W)) )))
export PARAM_ID_EN := 1
export PARAM_ID_W := 8
export PARAM_DEST_EN := 1
export PARAM_DEST_W := 8
export PARAM_USER_EN := 1
export PARAM_USER_W := 1
export PARAM_RAM_PIPELINE := 1
export PARAM_OUTPUT_FIFO_EN := 0
export PARAM_FRAME_FIFO := 1
export PARAM_USER_BAD_FRAME_VALUE := 1
export PARAM_USER_BAD_FRAME_MASK := 1
export PARAM_DROP_OVERSIZE_FRAME := $(PARAM_FRAME_FIFO)
export PARAM_DROP_BAD_FRAME := $(PARAM_DROP_OVERSIZE_FRAME)
export PARAM_DROP_WHEN_FULL := 0
export PARAM_MARK_WHEN_FULL := 0
export PARAM_PAUSE_EN := 1
export PARAM_FRAME_PAUSE := 1

ifeq ($(SIM), icarus)
PLUSARGS += -fst

COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
else ifeq ($(SIM), verilator)
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))

ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst
VERILATOR_TRACE = 1
endif
endif

include $(shell cocotb-config --makefiles)/Makefile.sim
Loading

0 comments on commit d52aa2f

Please sign in to comment.