Skip to content

Commit 6a030f5

Browse files
committed
Add axis_pipeline_fifo
1 parent 92681fa commit 6a030f5

File tree

3 files changed

+591
-0
lines changed

3 files changed

+591
-0
lines changed

rtl/axis_pipeline_fifo.v

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
/*
2+
3+
Copyright (c) 2021 Alex Forencich
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
22+
23+
*/
24+
25+
// Language: Verilog 2001
26+
27+
`timescale 1ns / 1ps
28+
29+
/*
30+
* AXI4-Stream pipeline FIFO
31+
*/
32+
module axis_pipeline_fifo #
33+
(
34+
// Width of AXI stream interfaces in bits
35+
parameter DATA_WIDTH = 8,
36+
// Propagate tkeep signal
37+
parameter KEEP_ENABLE = (DATA_WIDTH>8),
38+
// tkeep signal width (words per cycle)
39+
parameter KEEP_WIDTH = (DATA_WIDTH/8),
40+
// Propagate tlast signal
41+
parameter LAST_ENABLE = 1,
42+
// Propagate tid signal
43+
parameter ID_ENABLE = 0,
44+
// tid signal width
45+
parameter ID_WIDTH = 8,
46+
// Propagate tdest signal
47+
parameter DEST_ENABLE = 0,
48+
// tdest signal width
49+
parameter DEST_WIDTH = 8,
50+
// Propagate tuser signal
51+
parameter USER_ENABLE = 1,
52+
// tuser signal width
53+
parameter USER_WIDTH = 1,
54+
// Number of registers in pipeline
55+
parameter LENGTH = 2
56+
)
57+
(
58+
input wire clk,
59+
input wire rst,
60+
61+
/*
62+
* AXI input
63+
*/
64+
input wire [DATA_WIDTH-1:0] s_axis_tdata,
65+
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
66+
input wire s_axis_tvalid,
67+
output wire s_axis_tready,
68+
input wire s_axis_tlast,
69+
input wire [ID_WIDTH-1:0] s_axis_tid,
70+
input wire [DEST_WIDTH-1:0] s_axis_tdest,
71+
input wire [USER_WIDTH-1:0] s_axis_tuser,
72+
73+
/*
74+
* AXI output
75+
*/
76+
output wire [DATA_WIDTH-1:0] m_axis_tdata,
77+
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
78+
output wire m_axis_tvalid,
79+
input wire m_axis_tready,
80+
output wire m_axis_tlast,
81+
output wire [ID_WIDTH-1:0] m_axis_tid,
82+
output wire [DEST_WIDTH-1:0] m_axis_tdest,
83+
output wire [USER_WIDTH-1:0] m_axis_tuser
84+
);
85+
86+
parameter FIFO_ADDR_WIDTH = LENGTH < 2 ? 3 : $clog2(LENGTH*4);
87+
88+
initial begin
89+
if (LENGTH < 1) begin
90+
$error("Error: LENGTH must be at least 1 (instance %m)");
91+
$finish;
92+
end
93+
end
94+
95+
// pipeline
96+
(* shreg_extract = "no" *)
97+
reg [DATA_WIDTH-1:0] axis_tdata_reg[0:LENGTH-1];
98+
(* shreg_extract = "no" *)
99+
reg [KEEP_WIDTH-1:0] axis_tkeep_reg[0:LENGTH-1];
100+
(* shreg_extract = "no" *)
101+
reg axis_tvalid_reg[0:LENGTH-1];
102+
(* shreg_extract = "no" *)
103+
reg axis_tready_reg[0:LENGTH-1];
104+
(* shreg_extract = "no" *)
105+
reg axis_tlast_reg[0:LENGTH-1];
106+
(* shreg_extract = "no" *)
107+
reg [ID_WIDTH-1:0] axis_tid_reg[0:LENGTH-1];
108+
(* shreg_extract = "no" *)
109+
reg [DEST_WIDTH-1:0] axis_tdest_reg[0:LENGTH-1];
110+
(* shreg_extract = "no" *)
111+
reg [USER_WIDTH-1:0] axis_tuser_reg[0:LENGTH-1];
112+
113+
wire [DATA_WIDTH-1:0] m_axis_tdata_int = axis_tdata_reg[LENGTH-1];
114+
wire [KEEP_WIDTH-1:0] m_axis_tkeep_int = axis_tkeep_reg[LENGTH-1];
115+
wire m_axis_tvalid_int = axis_tvalid_reg[LENGTH-1];
116+
wire m_axis_tready_int;
117+
wire m_axis_tlast_int = axis_tlast_reg[LENGTH-1];
118+
wire [ID_WIDTH-1:0] m_axis_tid_int = axis_tid_reg[LENGTH-1];
119+
wire [DEST_WIDTH-1:0] m_axis_tdest_int = axis_tdest_reg[LENGTH-1];
120+
wire [USER_WIDTH-1:0] m_axis_tuser_int = axis_tuser_reg[LENGTH-1];
121+
122+
assign s_axis_tready = axis_tready_reg[0];
123+
124+
integer i;
125+
126+
initial begin
127+
for (i = 0; i < LENGTH; i = i + 1) begin
128+
axis_tdata_reg[i] = {DATA_WIDTH{1'b0}};
129+
axis_tkeep_reg[i] = {KEEP_WIDTH{1'b0}};
130+
axis_tvalid_reg[i] = 1'b0;
131+
axis_tready_reg[i] = 1'b0;
132+
axis_tlast_reg[i] = 1'b0;
133+
axis_tid_reg[i] = {ID_WIDTH{1'b0}};
134+
axis_tdest_reg[i] = {DEST_WIDTH{1'b0}};
135+
axis_tuser_reg[i] = {USER_WIDTH{1'b0}};
136+
end
137+
end
138+
139+
always @(posedge clk) begin
140+
axis_tdata_reg[0] <= s_axis_tdata;
141+
axis_tkeep_reg[0] <= s_axis_tkeep;
142+
axis_tvalid_reg[0] <= s_axis_tvalid && s_axis_tready;
143+
axis_tready_reg[LENGTH-1] <= m_axis_tready_int;
144+
axis_tlast_reg[0] <= s_axis_tlast;
145+
axis_tid_reg[0] <= s_axis_tid;
146+
axis_tdest_reg[0] <= s_axis_tdest;
147+
axis_tuser_reg[0] <= s_axis_tuser;
148+
149+
for (i = 0; i < LENGTH-1; i = i + 1) begin
150+
axis_tdata_reg[i+1] <= axis_tdata_reg[i];
151+
axis_tkeep_reg[i+1] <= axis_tkeep_reg[i];
152+
axis_tvalid_reg[i+1] <= axis_tvalid_reg[i];
153+
axis_tready_reg[i] <= axis_tready_reg[i+1];
154+
axis_tlast_reg[i+1] <= axis_tlast_reg[i];
155+
axis_tid_reg[i+1] <= axis_tid_reg[i];
156+
axis_tdest_reg[i+1] <= axis_tdest_reg[i];
157+
axis_tuser_reg[i+1] <= axis_tuser_reg[i];
158+
end
159+
160+
if (rst) begin
161+
for (i = 0; i < LENGTH; i = i + 1) begin
162+
axis_tvalid_reg[i] = 1'b0;
163+
axis_tready_reg[i] = 1'b0;
164+
end
165+
end
166+
end
167+
168+
// output datapath logic
169+
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
170+
reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
171+
reg m_axis_tvalid_reg = 1'b0;
172+
reg m_axis_tlast_reg = 1'b0;
173+
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
174+
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
175+
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
176+
177+
reg [FIFO_ADDR_WIDTH+1-1:0] out_fifo_wr_ptr_reg = 0;
178+
reg [FIFO_ADDR_WIDTH+1-1:0] out_fifo_rd_ptr_reg = 0;
179+
reg out_fifo_half_full_reg = 1'b0;
180+
181+
wire out_fifo_full = out_fifo_wr_ptr_reg == (out_fifo_rd_ptr_reg ^ {1'b1, {FIFO_ADDR_WIDTH{1'b0}}});
182+
wire out_fifo_empty = out_fifo_wr_ptr_reg == out_fifo_rd_ptr_reg;
183+
184+
(* ram_style = "distributed" *)
185+
reg [DATA_WIDTH-1:0] out_fifo_tdata[2**FIFO_ADDR_WIDTH-1:0];
186+
(* ram_style = "distributed" *)
187+
reg [KEEP_WIDTH-1:0] out_fifo_tkeep[2**FIFO_ADDR_WIDTH-1:0];
188+
(* ram_style = "distributed" *)
189+
reg out_fifo_tlast[2**FIFO_ADDR_WIDTH-1:0];
190+
(* ram_style = "distributed" *)
191+
reg [ID_WIDTH-1:0] out_fifo_tid[2**FIFO_ADDR_WIDTH-1:0];
192+
(* ram_style = "distributed" *)
193+
reg [DEST_WIDTH-1:0] out_fifo_tdest[2**FIFO_ADDR_WIDTH-1:0];
194+
(* ram_style = "distributed" *)
195+
reg [USER_WIDTH-1:0] out_fifo_tuser[2**FIFO_ADDR_WIDTH-1:0];
196+
197+
assign m_axis_tready_int = !out_fifo_half_full_reg;
198+
199+
assign m_axis_tdata = m_axis_tdata_reg;
200+
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
201+
assign m_axis_tvalid = m_axis_tvalid_reg;
202+
assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
203+
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
204+
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
205+
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
206+
207+
always @(posedge clk) begin
208+
m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready;
209+
210+
out_fifo_half_full_reg <= $unsigned(out_fifo_wr_ptr_reg - out_fifo_rd_ptr_reg) >= 2**(FIFO_ADDR_WIDTH-1);
211+
212+
if (!out_fifo_full && m_axis_tvalid_int) begin
213+
out_fifo_tdata[out_fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= m_axis_tdata_int;
214+
out_fifo_tkeep[out_fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= m_axis_tkeep_int;
215+
out_fifo_tlast[out_fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= m_axis_tlast_int;
216+
out_fifo_tid[out_fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= m_axis_tid_int;
217+
out_fifo_tdest[out_fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= m_axis_tdest_int;
218+
out_fifo_tuser[out_fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= m_axis_tuser_int;
219+
out_fifo_wr_ptr_reg <= out_fifo_wr_ptr_reg + 1;
220+
end
221+
222+
if (!out_fifo_empty && (!m_axis_tvalid_reg || m_axis_tready)) begin
223+
m_axis_tdata_reg <= out_fifo_tdata[out_fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]];
224+
m_axis_tkeep_reg <= out_fifo_tkeep[out_fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]];
225+
m_axis_tvalid_reg <= 1'b1;
226+
m_axis_tlast_reg <= out_fifo_tlast[out_fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]];
227+
m_axis_tid_reg <= out_fifo_tid[out_fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]];
228+
m_axis_tdest_reg <= out_fifo_tdest[out_fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]];
229+
m_axis_tuser_reg <= out_fifo_tuser[out_fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]];
230+
out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1;
231+
end
232+
233+
if (rst) begin
234+
out_fifo_wr_ptr_reg <= 0;
235+
out_fifo_rd_ptr_reg <= 0;
236+
m_axis_tvalid_reg <= 1'b0;
237+
end
238+
end
239+
240+
endmodule

tb/axis_pipeline_fifo/Makefile

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright (c) 2021 Alex Forencich
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
21+
TOPLEVEL_LANG = verilog
22+
23+
SIM ?= icarus
24+
WAVES ?= 0
25+
26+
COCOTB_HDL_TIMEUNIT = 1ns
27+
COCOTB_HDL_TIMEPRECISION = 1ps
28+
29+
DUT = axis_pipeline_fifo
30+
TOPLEVEL = $(DUT)
31+
MODULE = test_$(DUT)
32+
VERILOG_SOURCES += ../../rtl/$(DUT).v
33+
34+
# module parameters
35+
export PARAM_DATA_WIDTH ?= 8
36+
export PARAM_KEEP_ENABLE ?= $(shell expr $(PARAM_DATA_WIDTH) \> 8 )
37+
export PARAM_KEEP_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
38+
export PARAM_LAST_ENABLE ?= 1
39+
export PARAM_ID_ENABLE ?= 1
40+
export PARAM_ID_WIDTH ?= 8
41+
export PARAM_DEST_ENABLE ?= 1
42+
export PARAM_DEST_WIDTH ?= 8
43+
export PARAM_USER_ENABLE ?= 1
44+
export PARAM_USER_WIDTH ?= 1
45+
export PARAM_LENGTH ?= 2
46+
47+
ifeq ($(SIM), icarus)
48+
PLUSARGS += -fst
49+
50+
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
51+
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_ENABLE=$(PARAM_KEEP_ENABLE)
52+
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH)
53+
COMPILE_ARGS += -P $(TOPLEVEL).LAST_ENABLE=$(PARAM_LAST_ENABLE)
54+
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
55+
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
56+
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
57+
COMPILE_ARGS += -P $(TOPLEVEL).DEST_WIDTH=$(PARAM_DEST_WIDTH)
58+
COMPILE_ARGS += -P $(TOPLEVEL).USER_ENABLE=$(PARAM_USER_ENABLE)
59+
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
60+
COMPILE_ARGS += -P $(TOPLEVEL).LENGTH=$(PARAM_LENGTH)
61+
62+
ifeq ($(WAVES), 1)
63+
VERILOG_SOURCES += iverilog_dump.v
64+
COMPILE_ARGS += -s iverilog_dump
65+
endif
66+
else ifeq ($(SIM), verilator)
67+
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
68+
69+
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
70+
COMPILE_ARGS += -GKEEP_ENABLE=$(PARAM_KEEP_ENABLE)
71+
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
72+
COMPILE_ARGS += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
73+
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
74+
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
75+
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
76+
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
77+
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
78+
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
79+
COMPILE_ARGS += -GLENGTH=$(PARAM_LENGTH)
80+
81+
ifeq ($(WAVES), 1)
82+
COMPILE_ARGS += --trace-fst
83+
endif
84+
endif
85+
86+
include $(shell cocotb-config --makefiles)/Makefile.sim
87+
88+
iverilog_dump.v:
89+
echo 'module iverilog_dump();' > $@
90+
echo 'initial begin' >> $@
91+
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
92+
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
93+
echo 'end' >> $@
94+
echo 'endmodule' >> $@
95+
96+
clean::
97+
@rm -rf iverilog_dump.v
98+
@rm -rf dump.fst $(TOPLEVEL).fst

0 commit comments

Comments
 (0)