-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
axis: Add AXI stream combination async FIFO/adapter module and testbench
Signed-off-by: Alex Forencich <[email protected]>
- Loading branch information
1 parent
b1dcd8c
commit d52aa2f
Showing
5 changed files
with
1,201 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.