1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00

merged changes in axis

This commit is contained in:
Alex Forencich 2023-08-16 16:19:04 -07:00
commit f92a94d278
14 changed files with 1558 additions and 867 deletions

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Copyright (c) 2014-2023 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -90,468 +90,235 @@ module axis_adapter #
);
// force keep width to 1 when disabled
parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
localparam S_BYTE_LANES = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
localparam M_BYTE_LANES = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
// bus word sizes (must be identical)
parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT;
parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT;
// output bus is wider
parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT;
// total data and keep widths
parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH;
parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT;
// required number of segments in wider bus
parameter SEGMENT_COUNT = EXPAND_BUS ? (M_KEEP_WIDTH_INT / S_KEEP_WIDTH_INT) : (S_KEEP_WIDTH_INT / M_KEEP_WIDTH_INT);
parameter SEGMENT_COUNT_WIDTH = SEGMENT_COUNT == 1 ? 1 : $clog2(SEGMENT_COUNT);
// data width and keep width per segment
parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT;
parameter SEGMENT_KEEP_WIDTH = KEEP_WIDTH / SEGMENT_COUNT;
// bus byte sizes (must be identical)
localparam S_BYTE_SIZE = S_DATA_WIDTH / S_BYTE_LANES;
localparam M_BYTE_SIZE = M_DATA_WIDTH / M_BYTE_LANES;
// bus width assertions
initial begin
if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisble (instance %m)");
if (S_BYTE_SIZE * S_BYTE_LANES != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisible (instance %m)");
$finish;
end
if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisble (instance %m)");
if (M_BYTE_SIZE * M_BYTE_LANES != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisible (instance %m)");
$finish;
end
if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin
$error("Error: word size mismatch (instance %m)");
if (S_BYTE_SIZE != M_BYTE_SIZE) begin
$error("Error: byte size mismatch (instance %m)");
$finish;
end
end
// state register
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_TRANSFER_IN = 3'd1,
STATE_TRANSFER_OUT = 3'd2;
generate
reg [2:0] state_reg = STATE_IDLE, state_next;
if (M_BYTE_LANES == S_BYTE_LANES) begin : bypass
// same width; bypass
reg [SEGMENT_COUNT_WIDTH-1:0] segment_count_reg = 0, segment_count_next;
assign s_axis_tready = m_axis_tready;
reg last_segment;
assign m_axis_tdata = s_axis_tdata;
assign m_axis_tkeep = M_KEEP_ENABLE ? s_axis_tkeep : {M_KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = s_axis_tvalid;
assign m_axis_tlast = s_axis_tlast;
assign m_axis_tid = ID_ENABLE ? s_axis_tid : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? s_axis_tdest : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? s_axis_tuser : {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_tdata_reg = {DATA_WIDTH{1'b0}}, temp_tdata_next;
reg [KEEP_WIDTH-1:0] temp_tkeep_reg = {KEEP_WIDTH{1'b0}}, temp_tkeep_next;
reg temp_tlast_reg = 1'b0, temp_tlast_next;
reg [ID_WIDTH-1:0] temp_tid_reg = {ID_WIDTH{1'b0}}, temp_tid_next;
reg [DEST_WIDTH-1:0] temp_tdest_reg = {DEST_WIDTH{1'b0}}, temp_tdest_next;
reg [USER_WIDTH-1:0] temp_tuser_reg = {USER_WIDTH{1'b0}}, temp_tuser_next;
end else if (M_BYTE_LANES > S_BYTE_LANES) begin : upsize
// output is wider; upsize
// internal datapath
reg [M_DATA_WIDTH-1:0] m_axis_tdata_int;
reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_int;
reg m_axis_tvalid_int;
reg m_axis_tready_int_reg = 1'b0;
reg m_axis_tlast_int;
reg [ID_WIDTH-1:0] m_axis_tid_int;
reg [DEST_WIDTH-1:0] m_axis_tdest_int;
reg [USER_WIDTH-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
// required number of segments in wider bus
localparam SEG_COUNT = M_BYTE_LANES / S_BYTE_LANES;
// data width and keep width per segment
localparam SEG_DATA_WIDTH = M_DATA_WIDTH / SEG_COUNT;
localparam SEG_KEEP_WIDTH = M_BYTE_LANES / SEG_COUNT;
reg s_axis_tready_reg = 1'b0, s_axis_tready_next;
reg [$clog2(SEG_COUNT)-1:0] seg_reg = 0;
assign s_axis_tready = s_axis_tready_reg;
reg [S_DATA_WIDTH-1:0] s_axis_tdata_reg = {S_DATA_WIDTH{1'b0}};
reg [S_KEEP_WIDTH-1:0] s_axis_tkeep_reg = {S_KEEP_WIDTH{1'b0}};
reg s_axis_tvalid_reg = 1'b0;
reg s_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] s_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] s_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] s_axis_tuser_reg = {USER_WIDTH{1'b0}};
always @* begin
state_next = STATE_IDLE;
reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}};
reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
segment_count_next = segment_count_reg;
assign s_axis_tready = !s_axis_tvalid_reg;
last_segment = 0;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
temp_tdata_next = temp_tdata_reg;
temp_tkeep_next = temp_tkeep_reg;
temp_tlast_next = temp_tlast_reg;
temp_tid_next = temp_tid_reg;
temp_tdest_next = temp_tdest_reg;
temp_tuser_next = temp_tuser_reg;
always @(posedge clk) begin
m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready;
if (EXPAND_BUS) begin
m_axis_tdata_int = temp_tdata_reg;
m_axis_tkeep_int = temp_tkeep_reg;
m_axis_tlast_int = temp_tlast_reg;
end else begin
m_axis_tdata_int = {M_DATA_WIDTH{1'b0}};
m_axis_tkeep_int = {M_KEEP_WIDTH{1'b0}};
m_axis_tlast_int = 1'b0;
end
m_axis_tvalid_int = 1'b0;
m_axis_tid_int = temp_tid_reg;
m_axis_tdest_int = temp_tdest_reg;
m_axis_tuser_int = temp_tuser_reg;
if (!m_axis_tvalid_reg || m_axis_tready) begin
// output register empty
s_axis_tready_next = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - no data in registers
if (SEGMENT_COUNT == 1) begin
// output and input same width - just act like a register
// accept data next cycle if output register ready next cycle
s_axis_tready_next = m_axis_tready_int_early;
// transfer through
m_axis_tdata_int = s_axis_tdata;
m_axis_tkeep_int = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1;
m_axis_tvalid_int = s_axis_tvalid;
m_axis_tlast_int = s_axis_tlast;
m_axis_tid_int = s_axis_tid;
m_axis_tdest_int = s_axis_tdest;
m_axis_tuser_int = s_axis_tuser;
state_next = STATE_IDLE;
end else if (EXPAND_BUS) begin
// output bus is wider
// accept new data
s_axis_tready_next = 1'b1;
if (s_axis_tready && s_axis_tvalid) begin
// word transfer in - store it in data register
// pass complete input word, zero-extended to temp register
temp_tdata_next = s_axis_tdata;
temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1;
temp_tlast_next = s_axis_tlast;
temp_tid_next = s_axis_tid;
temp_tdest_next = s_axis_tdest;
temp_tuser_next = s_axis_tuser;
// first input segment complete
segment_count_next = 1;
if (s_axis_tlast) begin
// got last signal on first segment, so output it
s_axis_tready_next = 1'b0;
state_next = STATE_TRANSFER_OUT;
end else begin
// otherwise, transfer in the rest of the words
s_axis_tready_next = 1'b1;
state_next = STATE_TRANSFER_IN;
end
end else begin
state_next = STATE_IDLE;
end
if (seg_reg == 0) begin
m_axis_tdata_reg[seg_reg*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] <= s_axis_tvalid_reg ? s_axis_tdata_reg : s_axis_tdata;
m_axis_tkeep_reg <= s_axis_tvalid_reg ? s_axis_tkeep_reg : s_axis_tkeep;
end else begin
// output bus is narrower
m_axis_tdata_reg[seg_reg*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] <= s_axis_tdata;
m_axis_tkeep_reg[seg_reg*SEG_KEEP_WIDTH +: SEG_KEEP_WIDTH] <= s_axis_tkeep;
end
m_axis_tlast_reg <= s_axis_tvalid_reg ? s_axis_tlast_reg : s_axis_tlast;
m_axis_tid_reg <= s_axis_tvalid_reg ? s_axis_tid_reg : s_axis_tid;
m_axis_tdest_reg <= s_axis_tvalid_reg ? s_axis_tdest_reg : s_axis_tdest;
m_axis_tuser_reg <= s_axis_tvalid_reg ? s_axis_tuser_reg : s_axis_tuser;
// accept new data
s_axis_tready_next = 1'b1;
if (s_axis_tvalid_reg) begin
// consume data from buffer
s_axis_tvalid_reg <= 1'b0;
if (s_axis_tready && s_axis_tvalid) begin
// word transfer in - store it in data register
segment_count_next = 0;
// is this the last segment?
if (SEGMENT_COUNT == 1) begin
// last segment by counter value
last_segment = 1'b1;
end else if (S_KEEP_ENABLE && s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin
// last segment by tkeep fall in current segment
last_segment = 1'b1;
end else if (S_KEEP_ENABLE && s_axis_tkeep[(SEGMENT_KEEP_WIDTH*2)-1:SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin
// last segment by tkeep fall at end of current segment
last_segment = 1'b1;
end else begin
last_segment = 1'b0;
end
// pass complete input word, zero-extended to temp register
temp_tdata_next = s_axis_tdata;
temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1;
temp_tlast_next = s_axis_tlast;
temp_tid_next = s_axis_tid;
temp_tdest_next = s_axis_tdest;
temp_tuser_next = s_axis_tuser;
// short-circuit and get first word out the door
m_axis_tdata_int = s_axis_tdata[SEGMENT_DATA_WIDTH-1:0];
m_axis_tkeep_int = s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0];
m_axis_tvalid_int = 1'b1;
m_axis_tlast_int = s_axis_tlast & last_segment;
m_axis_tid_int = s_axis_tid;
m_axis_tdest_int = s_axis_tdest;
m_axis_tuser_int = s_axis_tuser;
if (m_axis_tready_int_reg) begin
// if output register is ready for first word, then move on to the next one
segment_count_next = 1;
end
if (!last_segment || !m_axis_tready_int_reg) begin
// continue outputting words
s_axis_tready_next = 1'b0;
state_next = STATE_TRANSFER_OUT;
end else begin
state_next = STATE_IDLE;
end
if (s_axis_tlast_reg || seg_reg == SEG_COUNT-1) begin
seg_reg <= 0;
m_axis_tvalid_reg <= 1'b1;
end else begin
state_next = STATE_IDLE;
seg_reg <= seg_reg + 1;
end
end else if (s_axis_tvalid) begin
// data direct from input
if (s_axis_tlast || seg_reg == SEG_COUNT-1) begin
seg_reg <= 0;
m_axis_tvalid_reg <= 1'b1;
end else begin
seg_reg <= seg_reg + 1;
end
end
end else if (s_axis_tvalid && s_axis_tready) begin
// store input data in skid buffer
s_axis_tdata_reg <= s_axis_tdata;
s_axis_tkeep_reg <= s_axis_tkeep;
s_axis_tvalid_reg <= 1'b1;
s_axis_tlast_reg <= s_axis_tlast;
s_axis_tid_reg <= s_axis_tid;
s_axis_tdest_reg <= s_axis_tdest;
s_axis_tuser_reg <= s_axis_tuser;
end
STATE_TRANSFER_IN: begin
// transfer word to temp registers
// only used when output is wider
// accept new data
s_axis_tready_next = 1'b1;
if (s_axis_tready && s_axis_tvalid) begin
// word transfer in - store in data register
temp_tdata_next[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = s_axis_tdata;
temp_tkeep_next[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1;
temp_tlast_next = s_axis_tlast;
temp_tid_next = s_axis_tid;
temp_tdest_next = s_axis_tdest;
temp_tuser_next = s_axis_tuser;
segment_count_next = segment_count_reg + 1;
if ((segment_count_reg == SEGMENT_COUNT-1) || s_axis_tlast) begin
// terminated by counter or tlast signal, output complete word
// read input word next cycle if output will be ready
s_axis_tready_next = m_axis_tready_int_early;
state_next = STATE_TRANSFER_OUT;
end else begin
// more words to read
s_axis_tready_next = 1'b1;
state_next = STATE_TRANSFER_IN;
end
end else begin
state_next = STATE_TRANSFER_IN;
end
if (rst) begin
seg_reg <= 0;
s_axis_tvalid_reg <= 1'b0;
m_axis_tvalid_reg <= 1'b0;
end
STATE_TRANSFER_OUT: begin
// transfer word to output registers
end
if (EXPAND_BUS) begin
// output bus is wider
end else begin : downsize
// output is narrower; downsize
// do not accept new data
s_axis_tready_next = 1'b0;
// required number of segments in wider bus
localparam SEG_COUNT = S_BYTE_LANES / M_BYTE_LANES;
// data width and keep width per segment
localparam SEG_DATA_WIDTH = S_DATA_WIDTH / SEG_COUNT;
localparam SEG_KEEP_WIDTH = S_BYTE_LANES / SEG_COUNT;
// single-cycle output of entire stored word (output wider)
m_axis_tdata_int = temp_tdata_reg;
m_axis_tkeep_int = temp_tkeep_reg;
m_axis_tvalid_int = 1'b1;
m_axis_tlast_int = temp_tlast_reg;
m_axis_tid_int = temp_tid_reg;
m_axis_tdest_int = temp_tdest_reg;
m_axis_tuser_int = temp_tuser_reg;
reg [S_DATA_WIDTH-1:0] s_axis_tdata_reg = {S_DATA_WIDTH{1'b0}};
reg [S_KEEP_WIDTH-1:0] s_axis_tkeep_reg = {S_KEEP_WIDTH{1'b0}};
reg s_axis_tvalid_reg = 1'b0;
reg s_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] s_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] s_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] s_axis_tuser_reg = {USER_WIDTH{1'b0}};
if (m_axis_tready_int_reg) begin
// word transfer out
reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}};
reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
if (s_axis_tready && s_axis_tvalid) begin
// word transfer in
assign s_axis_tready = !s_axis_tvalid_reg;
// pass complete input word, zero-extended to temp register
temp_tdata_next = s_axis_tdata;
temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1;
temp_tlast_next = s_axis_tlast;
temp_tid_next = s_axis_tid;
temp_tdest_next = s_axis_tdest;
temp_tuser_next = s_axis_tuser;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// first input segment complete
segment_count_next = 1;
always @(posedge clk) begin
m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready;
if (s_axis_tlast) begin
// got last signal on first segment, so output it
s_axis_tready_next = 1'b0;
state_next = STATE_TRANSFER_OUT;
end else begin
// otherwise, transfer in the rest of the words
s_axis_tready_next = 1'b1;
state_next = STATE_TRANSFER_IN;
end
end else begin
s_axis_tready_next = 1'b1;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_TRANSFER_OUT;
if (!m_axis_tvalid_reg || m_axis_tready) begin
// output register empty
m_axis_tdata_reg <= s_axis_tvalid_reg ? s_axis_tdata_reg : s_axis_tdata;
m_axis_tkeep_reg <= s_axis_tvalid_reg ? s_axis_tkeep_reg : s_axis_tkeep;
m_axis_tlast_reg <= 1'b0;
m_axis_tid_reg <= s_axis_tvalid_reg ? s_axis_tid_reg : s_axis_tid;
m_axis_tdest_reg <= s_axis_tvalid_reg ? s_axis_tdest_reg : s_axis_tdest;
m_axis_tuser_reg <= s_axis_tvalid_reg ? s_axis_tuser_reg : s_axis_tuser;
if (s_axis_tvalid_reg) begin
// buffer has data; shift out from buffer
s_axis_tdata_reg <= s_axis_tdata_reg >> SEG_DATA_WIDTH;
s_axis_tkeep_reg <= s_axis_tkeep_reg >> SEG_KEEP_WIDTH;
m_axis_tvalid_reg <= 1'b1;
if ((s_axis_tkeep_reg >> SEG_KEEP_WIDTH) == 0) begin
s_axis_tvalid_reg <= 1'b0;
m_axis_tlast_reg <= s_axis_tlast_reg;
end
end else begin
// output bus is narrower
end else if (s_axis_tvalid && s_axis_tready) begin
// buffer is empty; store from input
s_axis_tdata_reg <= s_axis_tdata >> SEG_DATA_WIDTH;
s_axis_tkeep_reg <= s_axis_tkeep >> SEG_KEEP_WIDTH;
s_axis_tlast_reg <= s_axis_tlast;
s_axis_tid_reg <= s_axis_tid;
s_axis_tdest_reg <= s_axis_tdest;
s_axis_tuser_reg <= s_axis_tuser;
// do not accept new data
s_axis_tready_next = 1'b0;
m_axis_tvalid_reg <= 1'b1;
// is this the last segment?
if (segment_count_reg == SEGMENT_COUNT-1) begin
// last segment by counter value
last_segment = 1'b1;
end else if (temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin
// last segment by tkeep fall in current segment
last_segment = 1'b1;
end else if (temp_tkeep_reg[(segment_count_reg+1)*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin
// last segment by tkeep fall at end of current segment
last_segment = 1'b1;
if ((s_axis_tkeep >> SEG_KEEP_WIDTH) == 0) begin
s_axis_tvalid_reg <= 1'b0;
m_axis_tlast_reg <= s_axis_tlast;
end else begin
last_segment = 1'b0;
end
// output current part of stored word (output narrower)
m_axis_tdata_int = temp_tdata_reg[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH];
m_axis_tkeep_int = temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH];
m_axis_tvalid_int = 1'b1;
m_axis_tlast_int = temp_tlast_reg && last_segment;
m_axis_tid_int = temp_tid_reg;
m_axis_tdest_int = temp_tdest_reg;
m_axis_tuser_int = temp_tuser_reg;
if (m_axis_tready_int_reg) begin
// word transfer out
segment_count_next = segment_count_reg + 1;
if (last_segment) begin
// terminated by counter or tlast signal
s_axis_tready_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// more words to write
state_next = STATE_TRANSFER_OUT;
end
end else begin
state_next = STATE_TRANSFER_OUT;
s_axis_tvalid_reg <= 1'b1;
end
end
end else if (s_axis_tvalid && s_axis_tready) begin
// store input data
s_axis_tdata_reg <= s_axis_tdata;
s_axis_tkeep_reg <= s_axis_tkeep;
s_axis_tvalid_reg <= 1'b1;
s_axis_tlast_reg <= s_axis_tlast;
s_axis_tid_reg <= s_axis_tid;
s_axis_tdest_reg <= s_axis_tdest;
s_axis_tuser_reg <= s_axis_tuser;
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
s_axis_tready_reg <= 1'b0;
end else begin
state_reg <= state_next;
s_axis_tready_reg <= s_axis_tready_next;
end
segment_count_reg <= segment_count_next;
temp_tdata_reg <= temp_tdata_next;
temp_tkeep_reg <= temp_tkeep_next;
temp_tlast_reg <= temp_tlast_next;
temp_tid_reg <= temp_tid_next;
temp_tdest_reg <= temp_tdest_next;
temp_tuser_reg <= temp_tuser_next;
end
// output datapath logic
reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}};
reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}};
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [M_DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}};
reg [M_KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}};
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
if (rst) begin
s_axis_tvalid_reg <= 1'b0;
m_axis_tvalid_reg <= 1'b0;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tkeep_reg <= m_axis_tkeep_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tid_reg <= m_axis_tid_int;
m_axis_tdest_reg <= m_axis_tdest_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tkeep_reg <= m_axis_tkeep_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tid_reg <= m_axis_tid_int;
temp_m_axis_tdest_reg <= m_axis_tdest_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end
end
endgenerate
endmodule

View File

@ -80,7 +80,15 @@ module axis_async_fifo #
// Drop incoming frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set
parameter DROP_WHEN_FULL = 0
parameter DROP_WHEN_FULL = 0,
// Mark incoming frames as bad frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO to be clear
parameter MARK_WHEN_FULL = 0,
// Enable pause request input
parameter PAUSE_ENABLE = 0,
// Pause between frames
parameter FRAME_PAUSE = FRAME_FIFO
)
(
/*
@ -111,6 +119,14 @@ module axis_async_fifo #
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Pause
*/
input wire s_pause_req,
output wire s_pause_ack,
input wire m_pause_req,
output wire m_pause_ack,
/*
* Status
*/
@ -152,10 +168,20 @@ initial begin
$finish;
end
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
if ((DROP_BAD_FRAME || MARK_WHEN_FULL) && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
$error("Error: Invalid USER_BAD_FRAME_MASK value (instance %m)");
$finish;
end
if (MARK_WHEN_FULL && FRAME_FIFO) begin
$error("Error: MARK_WHEN_FULL is not compatible with FRAME_FIFO (instance %m)");
$finish;
end
if (MARK_WHEN_FULL && !LAST_ENABLE) begin
$error("Error: MARK_WHEN_FULL set requires LAST_ENABLE set (instance %m)");
$finish;
end
end
localparam KEEP_OFFSET = DATA_WIDTH;
@ -250,6 +276,7 @@ reg s_frame_reg = 1'b0;
reg m_frame_reg = 1'b0;
reg drop_frame_reg = 1'b0;
reg mark_frame_reg = 1'b0;
reg send_frame_reg = 1'b0;
reg overflow_reg = 1'b0;
reg bad_frame_reg = 1'b0;
@ -276,21 +303,22 @@ reg good_frame_sync2_reg = 1'b0;
reg good_frame_sync3_reg = 1'b0;
reg good_frame_sync4_reg = 1'b0;
assign s_axis_tready = (FRAME_FIFO ? (!full || (full_wr && DROP_OVERSIZE_FRAME) || DROP_WHEN_FULL) : !full) && !s_rst_sync3_reg;
assign s_axis_tready = (FRAME_FIFO ? (!full || (full_wr && DROP_OVERSIZE_FRAME) || DROP_WHEN_FULL) : (!full || MARK_WHEN_FULL)) && !s_rst_sync3_reg;
wire [WIDTH-1:0] s_axis;
generate
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast | mark_frame_reg;
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = mark_frame_reg ? USER_BAD_FRAME_VALUE : s_axis_tuser;
endgenerate
wire [WIDTH-1:0] m_axis = m_axis_pipe_reg[RAM_PIPELINE+1-1];
wire m_axis_tready_pipe;
wire m_axis_tvalid_pipe = m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1];
wire [DATA_WIDTH-1:0] m_axis_tdata_pipe = m_axis[DATA_WIDTH-1:0];
@ -300,6 +328,16 @@ wire [ID_WIDTH-1:0] m_axis_tid_pipe = ID_ENABLE ? m_axis[ID_OFFSET +: ID
wire [DEST_WIDTH-1:0] m_axis_tdest_pipe = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
wire [USER_WIDTH-1:0] m_axis_tuser_pipe = USER_ENABLE ? (m_terminate_frame_reg ? USER_BAD_FRAME_VALUE : m_axis[USER_OFFSET +: USER_WIDTH]) : {USER_WIDTH{1'b0}};
wire m_axis_tready_out;
wire m_axis_tvalid_out;
wire [DATA_WIDTH-1:0] m_axis_tdata_out;
wire [KEEP_WIDTH-1:0] m_axis_tkeep_out;
wire m_axis_tlast_out;
wire [ID_WIDTH-1:0] m_axis_tid_out;
wire [DEST_WIDTH-1:0] m_axis_tdest_out;
wire [USER_WIDTH-1:0] m_axis_tuser_out;
wire pipe_ready;
assign s_status_depth = (KEEP_ENABLE && KEEP_WIDTH > 1) ? {s_depth_reg, {$clog2(KEEP_WIDTH){1'b0}}} : s_depth_reg;
@ -372,89 +410,121 @@ always @(posedge s_clk) begin
end
end
if (s_axis_tready && s_axis_tvalid) begin
// transfer in
if (!FRAME_FIFO) begin
// normal FIFO mode
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
if (drop_frame_reg && LAST_ENABLE) begin
// currently dropping frame
// (only for frame transfers interrupted by sink reset)
if (FRAME_FIFO) begin
// frame FIFO mode
if (s_axis_tready && s_axis_tvalid) begin
// transfer in
if ((full && DROP_WHEN_FULL) || (full_wr && DROP_OVERSIZE_FRAME) || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_reg <= 1'b1;
if (s_axis_tlast) begin
// end of frame, clear drop flag
// end of frame, reset write pointer
wr_ptr_temp = wr_ptr_commit_reg;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else begin
// update pointers
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_temp = wr_ptr_reg + 1;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
if (s_axis_tlast || (!DROP_OVERSIZE_FRAME && (full_wr || send_frame_reg))) begin
// end of frame or send frame
send_frame_reg <= !s_axis_tlast;
if (s_axis_tlast && DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_temp = wr_ptr_commit_reg;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
bad_frame_reg <= 1'b1;
end else begin
// good packet or packet overflow, update write pointer
wr_ptr_temp = wr_ptr_reg + 1;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_commit_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
if (wr_ptr_update_reg == wr_ptr_update_ack_sync2_reg) begin
// no sync in progress; sync update
wr_ptr_update_valid_reg <= 1'b0;
wr_ptr_sync_commit_reg <= wr_ptr_temp;
wr_ptr_update_reg <= !wr_ptr_update_ack_sync2_reg;
end else begin
// sync in progress; flag it for later
wr_ptr_update_valid_reg <= 1'b1;
end
good_frame_reg <= s_axis_tlast;
end
end
end
end else if (s_axis_tvalid && full_wr && FRAME_FIFO && !DROP_OVERSIZE_FRAME) begin
// data valid with packet overflow
// update write pointer
send_frame_reg <= 1'b1;
wr_ptr_temp = wr_ptr_reg;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_commit_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
if (wr_ptr_update_reg == wr_ptr_update_ack_sync2_reg) begin
// no sync in progress; sync update
wr_ptr_update_valid_reg <= 1'b0;
wr_ptr_sync_commit_reg <= wr_ptr_temp;
wr_ptr_update_reg <= !wr_ptr_update_ack_sync2_reg;
end else begin
// sync in progress; flag it for later
wr_ptr_update_valid_reg <= 1'b1;
end
end
end else begin
// normal FIFO mode
if (s_axis_tready && s_axis_tvalid) begin
if (drop_frame_reg && LAST_ENABLE) begin
// currently dropping frame
if (s_axis_tlast) begin
// end of frame
if (!full && mark_frame_reg && MARK_WHEN_FULL) begin
// terminate marked frame
mark_frame_reg <= 1'b0;
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_temp = wr_ptr_reg + 1;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_commit_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
end
// end of frame, clear drop flag
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else if ((full || mark_frame_reg) && MARK_WHEN_FULL) begin
// full or marking frame
// drop frame; mark if this isn't the first cycle
drop_frame_reg <= 1'b1;
mark_frame_reg <= mark_frame_reg || s_frame_reg;
if (s_axis_tlast) begin
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else begin
// transfer in
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_temp = wr_ptr_reg + 1;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_commit_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
end
end else if ((full && DROP_WHEN_FULL) || (full_wr && DROP_OVERSIZE_FRAME) || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_reg <= 1'b1;
if (s_axis_tlast) begin
// end of frame, reset write pointer
wr_ptr_temp = wr_ptr_commit_reg;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else begin
end else if ((!full && !drop_frame_reg && mark_frame_reg) && MARK_WHEN_FULL) begin
// terminate marked frame
mark_frame_reg <= 1'b0;
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_temp = wr_ptr_reg + 1;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_commit_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
if (s_axis_tlast || (!DROP_OVERSIZE_FRAME && (full_wr || send_frame_reg))) begin
// end of frame or send frame
send_frame_reg <= !s_axis_tlast;
if (s_axis_tlast && DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_temp = wr_ptr_commit_reg;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
bad_frame_reg <= 1'b1;
end else begin
// good packet or packet overflow, update write pointer
wr_ptr_temp = wr_ptr_reg + 1;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_commit_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
if (wr_ptr_update_reg == wr_ptr_update_ack_sync2_reg) begin
// no sync in progress; sync update
wr_ptr_update_valid_reg <= 1'b0;
wr_ptr_sync_commit_reg <= wr_ptr_temp;
wr_ptr_update_reg <= !wr_ptr_update_ack_sync2_reg;
end else begin
// sync in progress; flag it for later
wr_ptr_update_valid_reg <= 1'b1;
end
good_frame_reg <= s_axis_tlast;
end
end
end
end else if (s_axis_tvalid && full_wr && FRAME_FIFO && !DROP_OVERSIZE_FRAME) begin
// data valid with packet overflow
// update write pointer
send_frame_reg <= 1'b1;
wr_ptr_temp = wr_ptr_reg;
wr_ptr_reg <= wr_ptr_temp;
wr_ptr_commit_reg <= wr_ptr_temp;
wr_ptr_gray_reg <= bin2gray(wr_ptr_temp);
if (wr_ptr_update_reg == wr_ptr_update_ack_sync2_reg) begin
// no sync in progress; sync update
wr_ptr_update_valid_reg <= 1'b0;
wr_ptr_sync_commit_reg <= wr_ptr_temp;
wr_ptr_update_reg <= !wr_ptr_update_ack_sync2_reg;
end else begin
// sync in progress; flag it for later
wr_ptr_update_valid_reg <= 1'b1;
end
end
@ -480,6 +550,7 @@ always @(posedge s_clk) begin
s_frame_reg <= 1'b0;
drop_frame_reg <= 1'b0;
mark_frame_reg <= 1'b0;
send_frame_reg <= 1'b0;
overflow_reg <= 1'b0;
bad_frame_reg <= 1'b0;
@ -574,14 +645,14 @@ end
integer j;
always @(posedge m_clk) begin
if (OUTPUT_FIFO_ENABLE || m_axis_tready) begin
if (m_axis_tready_pipe) begin
// output ready; invalidate stage
m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1] <= 1'b0;
m_terminate_frame_reg <= 1'b0;
end
for (j = RAM_PIPELINE+1-1; j > 0; j = j - 1) begin
if (OUTPUT_FIFO_ENABLE || m_axis_tready || ((~m_axis_tvalid_pipe_reg) >> j)) begin
if (m_axis_tready_pipe || ((~m_axis_tvalid_pipe_reg) >> j)) begin
// output ready or bubble in pipeline; transfer down pipeline
m_axis_tvalid_pipe_reg[j] <= m_axis_tvalid_pipe_reg[j-1];
m_axis_pipe_reg[j] <= m_axis_pipe_reg[j-1];
@ -589,7 +660,7 @@ always @(posedge m_clk) begin
end
end
if (OUTPUT_FIFO_ENABLE || m_axis_tready || ~m_axis_tvalid_pipe_reg) begin
if (m_axis_tready_pipe || ~m_axis_tvalid_pipe_reg) begin
// output ready or bubble in pipeline; read new data from FIFO
m_axis_tvalid_pipe_reg[0] <= 1'b0;
m_axis_pipe_reg[0] <= mem[rd_ptr_reg[ADDR_WIDTH-1:0]];
@ -604,14 +675,14 @@ always @(posedge m_clk) begin
if (m_axis_tvalid_pipe && LAST_ENABLE) begin
// track output frame status
if (m_axis_tlast_pipe && (OUTPUT_FIFO_ENABLE || m_axis_tready)) begin
if (m_axis_tlast_pipe && m_axis_tready_pipe) begin
m_frame_reg <= 1'b0;
end else begin
m_frame_reg <= 1'b1;
end
end
if (m_drop_frame_reg && (OUTPUT_FIFO_ENABLE ? pipe_ready : m_axis_tready || !m_axis_tvalid_pipe) && LAST_ENABLE) begin
if (m_drop_frame_reg && (OUTPUT_FIFO_ENABLE ? pipe_ready : m_axis_tready_pipe || !m_axis_tvalid_pipe) && LAST_ENABLE) begin
// terminate frame
// (only for frame transfers interrupted by source reset)
m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1] <= 1'b1;
@ -662,16 +733,17 @@ if (!OUTPUT_FIFO_ENABLE) begin
assign pipe_ready = 1'b1;
assign m_axis_tvalid = m_axis_tvalid_pipe;
assign m_axis_tready_pipe = m_axis_tready_out;
assign m_axis_tvalid_out = m_axis_tvalid_pipe;
assign m_axis_tdata = m_axis_tdata_pipe;
assign m_axis_tkeep = m_axis_tkeep_pipe;
assign m_axis_tlast = m_axis_tlast_pipe;
assign m_axis_tid = m_axis_tid_pipe;
assign m_axis_tdest = m_axis_tdest_pipe;
assign m_axis_tuser = m_axis_tuser_pipe;
assign m_axis_tdata_out = m_axis_tdata_pipe;
assign m_axis_tkeep_out = m_axis_tkeep_pipe;
assign m_axis_tlast_out = m_axis_tlast_pipe;
assign m_axis_tid_out = m_axis_tid_pipe;
assign m_axis_tdest_out = m_axis_tdest_pipe;
assign m_axis_tuser_out = m_axis_tuser_pipe;
end else begin
end else begin : output_fifo
// output datapath logic
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
@ -704,16 +776,18 @@ end else begin
assign pipe_ready = !out_fifo_half_full_reg;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
assign m_axis_tready_pipe = 1'b1;
assign m_axis_tdata_out = m_axis_tdata_reg;
assign m_axis_tkeep_out = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid_out = m_axis_tvalid_reg;
assign m_axis_tlast_out = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
assign m_axis_tid_out = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest_out = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser_out = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
always @(posedge m_clk) begin
m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready;
m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready_out;
out_fifo_half_full_reg <= $unsigned(out_fifo_wr_ptr_reg - out_fifo_rd_ptr_reg) >= 2**(OUTPUT_FIFO_ADDR_WIDTH-1);
@ -727,7 +801,7 @@ end else begin
out_fifo_wr_ptr_reg <= out_fifo_wr_ptr_reg + 1;
end
if (!out_fifo_empty && (!m_axis_tvalid_reg || m_axis_tready)) begin
if (!out_fifo_empty && (!m_axis_tvalid_reg || m_axis_tready_out)) begin
m_axis_tdata_reg <= out_fifo_tdata[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]];
m_axis_tkeep_reg <= out_fifo_tkeep[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]];
m_axis_tvalid_reg <= 1'b1;
@ -747,6 +821,85 @@ end else begin
end
if (PAUSE_ENABLE) begin : pause
// Pause logic
reg pause_reg = 1'b0;
reg pause_frame_reg = 1'b0;
reg s_pause_req_sync1_reg;
reg s_pause_req_sync2_reg;
reg s_pause_req_sync3_reg;
reg s_pause_ack_sync1_reg;
reg s_pause_ack_sync2_reg;
reg s_pause_ack_sync3_reg;
always @(posedge s_clk) begin
s_pause_req_sync1_reg <= s_pause_req;
s_pause_ack_sync2_reg <= s_pause_ack_sync1_reg;
s_pause_ack_sync3_reg <= s_pause_ack_sync2_reg;
end
always @(posedge m_clk) begin
s_pause_req_sync2_reg <= s_pause_req_sync1_reg;
s_pause_req_sync3_reg <= s_pause_req_sync2_reg;
s_pause_ack_sync1_reg <= pause_reg;
end
assign m_axis_tready_out = m_axis_tready && !pause_reg;
assign m_axis_tvalid = m_axis_tvalid_out && !pause_reg;
assign m_axis_tdata = m_axis_tdata_out;
assign m_axis_tkeep = m_axis_tkeep_out;
assign m_axis_tlast = m_axis_tlast_out;
assign m_axis_tid = m_axis_tid_out;
assign m_axis_tdest = m_axis_tdest_out;
assign m_axis_tuser = m_axis_tuser_out;
assign s_pause_ack = s_pause_ack_sync3_reg;
assign m_pause_ack = pause_reg;
always @(posedge m_clk) begin
if (FRAME_PAUSE) begin
if (m_axis_tvalid && m_axis_tready) begin
if (m_axis_tlast) begin
pause_frame_reg <= 1'b0;
pause_reg <= m_pause_req || s_pause_req_sync3_reg;
end else begin
pause_frame_reg <= 1'b1;
end
end else begin
if (!pause_frame_reg) begin
pause_reg <= m_pause_req || s_pause_req_sync3_reg;
end
end
end else begin
pause_reg <= m_pause_req || s_pause_req_sync3_reg;
end
if (m_rst) begin
pause_frame_reg <= 1'b0;
pause_reg <= 1'b0;
end
end
end else begin
assign m_axis_tready_out = m_axis_tready;
assign m_axis_tvalid = m_axis_tvalid_out;
assign m_axis_tdata = m_axis_tdata_out;
assign m_axis_tkeep = m_axis_tkeep_out;
assign m_axis_tlast = m_axis_tlast_out;
assign m_axis_tid = m_axis_tid_out;
assign m_axis_tdest = m_axis_tdest_out;
assign m_axis_tuser = m_axis_tuser_out;
assign s_pause_ack = 1'b0;
assign m_pause_ack = 1'b0;
end
endgenerate
endmodule

View File

@ -85,7 +85,15 @@ module axis_async_fifo_adapter #
// Drop incoming frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set
parameter DROP_WHEN_FULL = 0
parameter DROP_WHEN_FULL = 0,
// Mark incoming frames as bad frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO to be clear
parameter MARK_WHEN_FULL = 0,
// Enable pause request input
parameter PAUSE_ENABLE = 0,
// Pause between frames
parameter FRAME_PAUSE = FRAME_FIFO
)
(
/*
@ -116,6 +124,14 @@ module axis_async_fifo_adapter #
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Pause
*/
input wire s_pause_req,
output wire s_pause_ack,
input wire m_pause_req,
output wire m_pause_ack,
/*
* Status
*/
@ -132,32 +148,32 @@ module axis_async_fifo_adapter #
);
// force keep width to 1 when disabled
parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
localparam S_BYTE_LANES = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
localparam M_BYTE_LANES = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
// bus word sizes (must be identical)
parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT;
parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT;
// bus byte sizes (must be identical)
localparam S_BYTE_SIZE = S_DATA_WIDTH / S_BYTE_LANES;
localparam M_BYTE_SIZE = M_DATA_WIDTH / M_BYTE_LANES;
// output bus is wider
parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT;
localparam EXPAND_BUS = M_BYTE_LANES > S_BYTE_LANES;
// total data and keep widths
parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH;
parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT;
localparam DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH;
localparam KEEP_WIDTH = EXPAND_BUS ? M_BYTE_LANES : S_BYTE_LANES;
// bus width assertions
initial begin
if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisble (instance %m)");
if (S_BYTE_SIZE * S_BYTE_LANES != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisible (instance %m)");
$finish;
end
if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisble (instance %m)");
if (M_BYTE_SIZE * M_BYTE_LANES != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisible (instance %m)");
$finish;
end
if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin
$error("Error: word size mismatch (instance %m)");
if (S_BYTE_SIZE != M_BYTE_SIZE) begin
$error("Error: byte size mismatch (instance %m)");
$finish;
end
end
@ -182,30 +198,7 @@ wire [USER_WIDTH-1:0] post_fifo_axis_tuser;
generate
if (M_KEEP_WIDTH == S_KEEP_WIDTH) begin
// same width, no adapter needed
assign pre_fifo_axis_tdata = s_axis_tdata;
assign pre_fifo_axis_tkeep = s_axis_tkeep;
assign pre_fifo_axis_tvalid = s_axis_tvalid;
assign s_axis_tready = pre_fifo_axis_tready;
assign pre_fifo_axis_tlast = s_axis_tlast;
assign pre_fifo_axis_tid = s_axis_tid;
assign pre_fifo_axis_tdest = s_axis_tdest;
assign pre_fifo_axis_tuser = s_axis_tuser;
assign m_axis_tdata = post_fifo_axis_tdata;
assign m_axis_tkeep = post_fifo_axis_tkeep;
assign m_axis_tvalid = post_fifo_axis_tvalid;
assign post_fifo_axis_tready = m_axis_tready;
assign m_axis_tlast = post_fifo_axis_tlast;
assign m_axis_tid = post_fifo_axis_tid;
assign m_axis_tdest = post_fifo_axis_tdest;
assign m_axis_tuser = post_fifo_axis_tuser;
end else if (EXPAND_BUS) begin
if (M_BYTE_LANES > S_BYTE_LANES) begin : upsize_pre
// output wider, adapt width before FIFO
@ -246,19 +239,8 @@ end else if (EXPAND_BUS) begin
.m_axis_tuser(pre_fifo_axis_tuser)
);
assign m_axis_tdata = post_fifo_axis_tdata;
assign m_axis_tkeep = post_fifo_axis_tkeep;
assign m_axis_tvalid = post_fifo_axis_tvalid;
assign post_fifo_axis_tready = m_axis_tready;
assign m_axis_tlast = post_fifo_axis_tlast;
assign m_axis_tid = post_fifo_axis_tid;
assign m_axis_tdest = post_fifo_axis_tdest;
assign m_axis_tuser = post_fifo_axis_tuser;
end else begin
end else begin : bypass_pre
// input wider, adapt width after FIFO
assign pre_fifo_axis_tdata = s_axis_tdata;
assign pre_fifo_axis_tkeep = s_axis_tkeep;
assign pre_fifo_axis_tvalid = s_axis_tvalid;
@ -268,6 +250,77 @@ end else begin
assign pre_fifo_axis_tdest = s_axis_tdest;
assign pre_fifo_axis_tuser = s_axis_tuser;
end
axis_async_fifo #(
.DEPTH(DEPTH),
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(1),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.RAM_PIPELINE(RAM_PIPELINE),
.OUTPUT_FIFO_ENABLE(OUTPUT_FIFO_ENABLE),
.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_ENABLE(PAUSE_ENABLE),
.FRAME_PAUSE(FRAME_PAUSE)
)
fifo_inst (
// AXI input
.s_clk(s_clk),
.s_rst(s_rst),
.s_axis_tdata(pre_fifo_axis_tdata),
.s_axis_tkeep(pre_fifo_axis_tkeep),
.s_axis_tvalid(pre_fifo_axis_tvalid),
.s_axis_tready(pre_fifo_axis_tready),
.s_axis_tlast(pre_fifo_axis_tlast),
.s_axis_tid(pre_fifo_axis_tid),
.s_axis_tdest(pre_fifo_axis_tdest),
.s_axis_tuser(pre_fifo_axis_tuser),
// AXI output
.m_clk(m_clk),
.m_rst(m_rst),
.m_axis_tdata(post_fifo_axis_tdata),
.m_axis_tkeep(post_fifo_axis_tkeep),
.m_axis_tvalid(post_fifo_axis_tvalid),
.m_axis_tready(post_fifo_axis_tready),
.m_axis_tlast(post_fifo_axis_tlast),
.m_axis_tid(post_fifo_axis_tid),
.m_axis_tdest(post_fifo_axis_tdest),
.m_axis_tuser(post_fifo_axis_tuser),
// 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)
);
if (M_BYTE_LANES < S_BYTE_LANES) begin : downsize_post
// input wider, adapt width after FIFO
axis_adapter #(
.S_DATA_WIDTH(S_DATA_WIDTH),
.S_KEEP_ENABLE(S_KEEP_ENABLE),
@ -305,67 +358,21 @@ end else begin
.m_axis_tuser(m_axis_tuser)
);
end else begin : bypass_post
assign m_axis_tdata = post_fifo_axis_tdata;
assign m_axis_tkeep = post_fifo_axis_tkeep;
assign m_axis_tvalid = post_fifo_axis_tvalid;
assign post_fifo_axis_tready = m_axis_tready;
assign m_axis_tlast = post_fifo_axis_tlast;
assign m_axis_tid = post_fifo_axis_tid;
assign m_axis_tdest = post_fifo_axis_tdest;
assign m_axis_tuser = post_fifo_axis_tuser;
end
endgenerate
axis_async_fifo #(
.DEPTH(DEPTH),
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(1),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.RAM_PIPELINE(RAM_PIPELINE),
.OUTPUT_FIFO_ENABLE(OUTPUT_FIFO_ENABLE),
.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)
)
fifo_inst (
// AXI input
.s_clk(s_clk),
.s_rst(s_rst),
.s_axis_tdata(pre_fifo_axis_tdata),
.s_axis_tkeep(pre_fifo_axis_tkeep),
.s_axis_tvalid(pre_fifo_axis_tvalid),
.s_axis_tready(pre_fifo_axis_tready),
.s_axis_tlast(pre_fifo_axis_tlast),
.s_axis_tid(pre_fifo_axis_tid),
.s_axis_tdest(pre_fifo_axis_tdest),
.s_axis_tuser(pre_fifo_axis_tuser),
// AXI output
.m_clk(m_clk),
.m_rst(m_rst),
.m_axis_tdata(post_fifo_axis_tdata),
.m_axis_tkeep(post_fifo_axis_tkeep),
.m_axis_tvalid(post_fifo_axis_tvalid),
.m_axis_tready(post_fifo_axis_tready),
.m_axis_tlast(post_fifo_axis_tlast),
.m_axis_tid(post_fifo_axis_tid),
.m_axis_tdest(post_fifo_axis_tdest),
.m_axis_tuser(post_fifo_axis_tuser),
// 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)
);
endmodule
`resetall

View File

@ -80,7 +80,15 @@ module axis_fifo #
// Drop incoming frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set
parameter DROP_WHEN_FULL = 0
parameter DROP_WHEN_FULL = 0,
// Mark incoming frames as bad frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO to be clear
parameter MARK_WHEN_FULL = 0,
// Enable pause request input
parameter PAUSE_ENABLE = 0,
// Pause between frames
parameter FRAME_PAUSE = FRAME_FIFO
)
(
input wire clk,
@ -110,6 +118,12 @@ module axis_fifo #
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Pause
*/
input wire pause_req,
output wire pause_ack,
/*
* Status
*/
@ -146,10 +160,20 @@ initial begin
$finish;
end
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
if ((DROP_BAD_FRAME || MARK_WHEN_FULL) && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
$error("Error: Invalid USER_BAD_FRAME_MASK value (instance %m)");
$finish;
end
if (MARK_WHEN_FULL && FRAME_FIFO) begin
$error("Error: MARK_WHEN_FULL is not compatible with FRAME_FIFO (instance %m)");
$finish;
end
if (MARK_WHEN_FULL && !LAST_ENABLE) begin
$error("Error: MARK_WHEN_FULL set requires LAST_ENABLE set (instance %m)");
$finish;
end
end
localparam KEEP_OFFSET = DATA_WIDTH;
@ -178,7 +202,10 @@ wire empty = wr_ptr_commit_reg == rd_ptr_reg;
// overflow within packet
wire full_wr = wr_ptr_reg == (wr_ptr_commit_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}});
reg s_frame_reg = 1'b0;
reg drop_frame_reg = 1'b0;
reg mark_frame_reg = 1'b0;
reg send_frame_reg = 1'b0;
reg [ADDR_WIDTH:0] depth_reg = 0;
reg [ADDR_WIDTH:0] depth_commit_reg = 0;
@ -186,21 +213,22 @@ reg overflow_reg = 1'b0;
reg bad_frame_reg = 1'b0;
reg good_frame_reg = 1'b0;
assign s_axis_tready = FRAME_FIFO ? (!full || (full_wr && DROP_OVERSIZE_FRAME) || DROP_WHEN_FULL) : !full;
assign s_axis_tready = FRAME_FIFO ? (!full || (full_wr && DROP_OVERSIZE_FRAME) || DROP_WHEN_FULL) : (!full || MARK_WHEN_FULL);
wire [WIDTH-1:0] s_axis;
generate
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast | mark_frame_reg;
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = mark_frame_reg ? USER_BAD_FRAME_VALUE : s_axis_tuser;
endgenerate
wire [WIDTH-1:0] m_axis = m_axis_pipe_reg[RAM_PIPELINE+1-1];
wire m_axis_tready_pipe;
wire m_axis_tvalid_pipe = m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1];
wire [DATA_WIDTH-1:0] m_axis_tdata_pipe = m_axis[DATA_WIDTH-1:0];
@ -210,6 +238,16 @@ wire [ID_WIDTH-1:0] m_axis_tid_pipe = ID_ENABLE ? m_axis[ID_OFFSET +: ID
wire [DEST_WIDTH-1:0] m_axis_tdest_pipe = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
wire [USER_WIDTH-1:0] m_axis_tuser_pipe = USER_ENABLE ? m_axis[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}};
wire m_axis_tready_out;
wire m_axis_tvalid_out;
wire [DATA_WIDTH-1:0] m_axis_tdata_out;
wire [KEEP_WIDTH-1:0] m_axis_tkeep_out;
wire m_axis_tlast_out;
wire [ID_WIDTH-1:0] m_axis_tid_out;
wire [DEST_WIDTH-1:0] m_axis_tdest_out;
wire [USER_WIDTH-1:0] m_axis_tuser_out;
wire pipe_ready;
assign status_depth = (KEEP_ENABLE && KEEP_WIDTH > 1) ? {depth_reg, {$clog2(KEEP_WIDTH){1'b0}}} : depth_reg;
@ -224,53 +262,99 @@ always @(posedge clk) begin
bad_frame_reg <= 1'b0;
good_frame_reg <= 1'b0;
if (s_axis_tready && s_axis_tvalid) begin
// transfer in
if (!FRAME_FIFO) begin
// normal FIFO mode
if (s_axis_tready && s_axis_tvalid && LAST_ENABLE) begin
// track input frame status
s_frame_reg <= !s_axis_tlast;
end
if (FRAME_FIFO) begin
// frame FIFO mode
if (s_axis_tready && s_axis_tvalid) begin
// transfer in
if ((full && DROP_WHEN_FULL) || (full_wr && DROP_OVERSIZE_FRAME) || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_reg <= 1'b1;
if (s_axis_tlast) begin
// end of frame, reset write pointer
wr_ptr_reg <= wr_ptr_commit_reg;
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else begin
// store it
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_reg <= wr_ptr_reg + 1;
if (s_axis_tlast || (!DROP_OVERSIZE_FRAME && (full_wr || send_frame_reg))) begin
// end of frame or send frame
send_frame_reg <= !s_axis_tlast;
if (s_axis_tlast && DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_reg <= wr_ptr_commit_reg;
bad_frame_reg <= 1'b1;
end else begin
// good packet or packet overflow, update write pointer
wr_ptr_commit_reg <= wr_ptr_reg + 1;
good_frame_reg <= s_axis_tlast;
end
end
end
end else if (s_axis_tvalid && full_wr && !DROP_OVERSIZE_FRAME) begin
// data valid with packet overflow
// update write pointer
send_frame_reg <= 1'b1;
wr_ptr_commit_reg <= wr_ptr_reg;
end
end else begin
// normal FIFO mode
if (s_axis_tready && s_axis_tvalid) begin
if (drop_frame_reg && MARK_WHEN_FULL) begin
// currently dropping frame
if (s_axis_tlast) begin
// end of frame
if (!full && mark_frame_reg) begin
// terminate marked frame
mark_frame_reg <= 1'b0;
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_reg <= wr_ptr_reg + 1;
wr_ptr_commit_reg <= wr_ptr_reg + 1;
end
// end of frame, clear drop flag
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else if ((full || mark_frame_reg) && MARK_WHEN_FULL) begin
// full or marking frame
// drop frame; mark if this isn't the first cycle
drop_frame_reg <= 1'b1;
mark_frame_reg <= mark_frame_reg || s_frame_reg;
if (s_axis_tlast) begin
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else begin
// transfer in
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_reg <= wr_ptr_reg + 1;
wr_ptr_commit_reg <= wr_ptr_reg + 1;
end
end else if ((!full && !drop_frame_reg && mark_frame_reg) && MARK_WHEN_FULL) begin
// terminate marked frame
mark_frame_reg <= 1'b0;
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_reg <= wr_ptr_reg + 1;
wr_ptr_commit_reg <= wr_ptr_reg + 1;
end else if ((full && DROP_WHEN_FULL) || (full_wr && DROP_OVERSIZE_FRAME) || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_reg <= 1'b1;
if (s_axis_tlast) begin
// end of frame, reset write pointer
wr_ptr_reg <= wr_ptr_commit_reg;
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b1;
end
end else begin
// store it
mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
wr_ptr_reg <= wr_ptr_reg + 1;
if (s_axis_tlast || (!DROP_OVERSIZE_FRAME && (full_wr || send_frame_reg))) begin
// end of frame or send frame
send_frame_reg <= !s_axis_tlast;
if (s_axis_tlast && DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_reg <= wr_ptr_commit_reg;
bad_frame_reg <= 1'b1;
end else begin
// good packet or packet overflow, update write pointer
wr_ptr_commit_reg <= wr_ptr_reg + 1;
good_frame_reg <= s_axis_tlast;
end
end
end
end else if (s_axis_tvalid && full_wr && FRAME_FIFO && !DROP_OVERSIZE_FRAME) begin
// data valid with packet overflow
// update write pointer
send_frame_reg <= 1'b1;
wr_ptr_commit_reg <= wr_ptr_reg;
end
if (rst) begin
wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_commit_reg <= {ADDR_WIDTH+1{1'b0}};
s_frame_reg <= 1'b0;
drop_frame_reg <= 1'b0;
mark_frame_reg <= 1'b0;
send_frame_reg <= 1'b0;
overflow_reg <= 1'b0;
bad_frame_reg <= 1'b0;
@ -288,13 +372,13 @@ end
integer j;
always @(posedge clk) begin
if (OUTPUT_FIFO_ENABLE || m_axis_tready) begin
if (m_axis_tready_pipe) begin
// output ready; invalidate stage
m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1] <= 1'b0;
end
for (j = RAM_PIPELINE+1-1; j > 0; j = j - 1) begin
if (OUTPUT_FIFO_ENABLE || m_axis_tready || ((~m_axis_tvalid_pipe_reg) >> j)) begin
if (m_axis_tready_pipe || ((~m_axis_tvalid_pipe_reg) >> j)) begin
// output ready or bubble in pipeline; transfer down pipeline
m_axis_tvalid_pipe_reg[j] <= m_axis_tvalid_pipe_reg[j-1];
m_axis_pipe_reg[j] <= m_axis_pipe_reg[j-1];
@ -302,7 +386,7 @@ always @(posedge clk) begin
end
end
if (OUTPUT_FIFO_ENABLE || m_axis_tready || ~m_axis_tvalid_pipe_reg) begin
if (m_axis_tready_pipe || ~m_axis_tvalid_pipe_reg) begin
// output ready or bubble in pipeline; read new data from FIFO
m_axis_tvalid_pipe_reg[0] <= 1'b0;
m_axis_pipe_reg[0] <= mem[rd_ptr_reg[ADDR_WIDTH-1:0]];
@ -325,16 +409,17 @@ if (!OUTPUT_FIFO_ENABLE) begin
assign pipe_ready = 1'b1;
assign m_axis_tvalid = m_axis_tvalid_pipe;
assign m_axis_tready_pipe = m_axis_tready_out;
assign m_axis_tvalid_out = m_axis_tvalid_pipe;
assign m_axis_tdata = m_axis_tdata_pipe;
assign m_axis_tkeep = m_axis_tkeep_pipe;
assign m_axis_tlast = m_axis_tlast_pipe;
assign m_axis_tid = m_axis_tid_pipe;
assign m_axis_tdest = m_axis_tdest_pipe;
assign m_axis_tuser = m_axis_tuser_pipe;
assign m_axis_tdata_out = m_axis_tdata_pipe;
assign m_axis_tkeep_out = m_axis_tkeep_pipe;
assign m_axis_tlast_out = m_axis_tlast_pipe;
assign m_axis_tid_out = m_axis_tid_pipe;
assign m_axis_tdest_out = m_axis_tdest_pipe;
assign m_axis_tuser_out = m_axis_tuser_pipe;
end else begin
end else begin : output_fifo
// output datapath logic
reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}};
@ -367,16 +452,18 @@ end else begin
assign pipe_ready = !out_fifo_half_full_reg;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
assign m_axis_tready_pipe = 1'b1;
assign m_axis_tdata_out = m_axis_tdata_reg;
assign m_axis_tkeep_out = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_axis_tvalid_out = m_axis_tvalid_reg;
assign m_axis_tlast_out = LAST_ENABLE ? m_axis_tlast_reg : 1'b1;
assign m_axis_tid_out = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_axis_tdest_out = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_axis_tuser_out = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}};
always @(posedge clk) begin
m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready;
m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready_out;
out_fifo_half_full_reg <= $unsigned(out_fifo_wr_ptr_reg - out_fifo_rd_ptr_reg) >= 2**(OUTPUT_FIFO_ADDR_WIDTH-1);
@ -390,7 +477,7 @@ end else begin
out_fifo_wr_ptr_reg <= out_fifo_wr_ptr_reg + 1;
end
if (!out_fifo_empty && (!m_axis_tvalid_reg || m_axis_tready)) begin
if (!out_fifo_empty && (!m_axis_tvalid_reg || m_axis_tready_out)) begin
m_axis_tdata_reg <= out_fifo_tdata[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]];
m_axis_tkeep_reg <= out_fifo_tkeep[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]];
m_axis_tvalid_reg <= 1'b1;
@ -410,6 +497,64 @@ end else begin
end
if (PAUSE_ENABLE) begin : pause
// Pause logic
reg pause_reg = 1'b0;
reg pause_frame_reg = 1'b0;
assign m_axis_tready_out = m_axis_tready && !pause_reg;
assign m_axis_tvalid = m_axis_tvalid_out && !pause_reg;
assign m_axis_tdata = m_axis_tdata_out;
assign m_axis_tkeep = m_axis_tkeep_out;
assign m_axis_tlast = m_axis_tlast_out;
assign m_axis_tid = m_axis_tid_out;
assign m_axis_tdest = m_axis_tdest_out;
assign m_axis_tuser = m_axis_tuser_out;
assign pause_ack = pause_reg;
always @(posedge clk) begin
if (FRAME_PAUSE) begin
if (m_axis_tvalid && m_axis_tready) begin
if (m_axis_tlast) begin
pause_frame_reg <= 1'b0;
pause_reg <= pause_req;
end else begin
pause_frame_reg <= 1'b1;
end
end else begin
if (!pause_frame_reg) begin
pause_reg <= pause_req;
end
end
end else begin
pause_reg <= pause_req;
end
if (rst) begin
pause_frame_reg <= 1'b0;
pause_reg <= 1'b0;
end
end
end else begin
assign m_axis_tready_out = m_axis_tready;
assign m_axis_tvalid = m_axis_tvalid_out;
assign m_axis_tdata = m_axis_tdata_out;
assign m_axis_tkeep = m_axis_tkeep_out;
assign m_axis_tlast = m_axis_tlast_out;
assign m_axis_tid = m_axis_tid_out;
assign m_axis_tdest = m_axis_tdest_out;
assign m_axis_tuser = m_axis_tuser_out;
assign pause_ack = 1'b0;
end
endgenerate
endmodule

View File

@ -85,7 +85,15 @@ module axis_fifo_adapter #
// Drop incoming frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set
parameter DROP_WHEN_FULL = 0
parameter DROP_WHEN_FULL = 0,
// Mark incoming frames as bad frames when full
// When set, s_axis_tready is always asserted
// Requires FRAME_FIFO to be clear
parameter MARK_WHEN_FULL = 0,
// Enable pause request input
parameter PAUSE_ENABLE = 0,
// Pause between frames
parameter FRAME_PAUSE = FRAME_FIFO
)
(
input wire clk,
@ -115,6 +123,12 @@ module axis_fifo_adapter #
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Pause
*/
input wire pause_req,
output wire pause_ack,
/*
* Status
*/
@ -126,32 +140,32 @@ module axis_fifo_adapter #
);
// force keep width to 1 when disabled
parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
localparam S_BYTE_LANES = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
localparam M_BYTE_LANES = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
// bus word sizes (must be identical)
parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT;
parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT;
// bus byte sizes (must be identical)
localparam S_BYTE_SIZE = S_DATA_WIDTH / S_BYTE_LANES;
localparam M_BYTE_SIZE = M_DATA_WIDTH / M_BYTE_LANES;
// output bus is wider
parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT;
localparam EXPAND_BUS = M_BYTE_LANES > S_BYTE_LANES;
// total data and keep widths
parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH;
parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT;
localparam DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH;
localparam KEEP_WIDTH = EXPAND_BUS ? M_BYTE_LANES : S_BYTE_LANES;
// bus width assertions
initial begin
if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisble (instance %m)");
if (S_BYTE_SIZE * S_BYTE_LANES != S_DATA_WIDTH) begin
$error("Error: input data width not evenly divisible (instance %m)");
$finish;
end
if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisble (instance %m)");
if (M_BYTE_SIZE * M_BYTE_LANES != M_DATA_WIDTH) begin
$error("Error: output data width not evenly divisible (instance %m)");
$finish;
end
if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin
$error("Error: word size mismatch (instance %m)");
if (S_BYTE_SIZE != M_BYTE_SIZE) begin
$error("Error: byte size mismatch (instance %m)");
$finish;
end
end
@ -176,29 +190,7 @@ wire [USER_WIDTH-1:0] post_fifo_axis_tuser;
generate
if (M_KEEP_WIDTH_INT == S_KEEP_WIDTH_INT) begin
// same width, no adapter needed
assign pre_fifo_axis_tdata = s_axis_tdata;
assign pre_fifo_axis_tkeep = s_axis_tkeep;
assign pre_fifo_axis_tvalid = s_axis_tvalid;
assign s_axis_tready = pre_fifo_axis_tready;
assign pre_fifo_axis_tlast = s_axis_tlast;
assign pre_fifo_axis_tid = s_axis_tid;
assign pre_fifo_axis_tdest = s_axis_tdest;
assign pre_fifo_axis_tuser = s_axis_tuser;
assign m_axis_tdata = post_fifo_axis_tdata;
assign m_axis_tkeep = post_fifo_axis_tkeep;
assign m_axis_tvalid = post_fifo_axis_tvalid;
assign post_fifo_axis_tready = m_axis_tready;
assign m_axis_tlast = post_fifo_axis_tlast;
assign m_axis_tid = post_fifo_axis_tid;
assign m_axis_tdest = post_fifo_axis_tdest;
assign m_axis_tuser = post_fifo_axis_tuser;
end else if (EXPAND_BUS) begin
if (M_BYTE_LANES > S_BYTE_LANES) begin : upsize_pre
// output wider, adapt width before FIFO
@ -239,19 +231,8 @@ end else if (EXPAND_BUS) begin
.m_axis_tuser(pre_fifo_axis_tuser)
);
assign m_axis_tdata = post_fifo_axis_tdata;
assign m_axis_tkeep = post_fifo_axis_tkeep;
assign m_axis_tvalid = post_fifo_axis_tvalid;
assign post_fifo_axis_tready = m_axis_tready;
assign m_axis_tlast = post_fifo_axis_tlast;
assign m_axis_tid = post_fifo_axis_tid;
assign m_axis_tdest = post_fifo_axis_tdest;
assign m_axis_tuser = post_fifo_axis_tuser;
end else begin
end else begin : bypass_pre
// input wider, adapt width after FIFO
assign pre_fifo_axis_tdata = s_axis_tdata;
assign pre_fifo_axis_tkeep = s_axis_tkeep;
assign pre_fifo_axis_tvalid = s_axis_tvalid;
@ -261,6 +242,68 @@ end else begin
assign pre_fifo_axis_tdest = s_axis_tdest;
assign pre_fifo_axis_tuser = s_axis_tuser;
end
axis_fifo #(
.DEPTH(DEPTH),
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(1),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.RAM_PIPELINE(RAM_PIPELINE),
.OUTPUT_FIFO_ENABLE(OUTPUT_FIFO_ENABLE),
.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_ENABLE(PAUSE_ENABLE),
.FRAME_PAUSE(FRAME_PAUSE)
)
fifo_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(pre_fifo_axis_tdata),
.s_axis_tkeep(pre_fifo_axis_tkeep),
.s_axis_tvalid(pre_fifo_axis_tvalid),
.s_axis_tready(pre_fifo_axis_tready),
.s_axis_tlast(pre_fifo_axis_tlast),
.s_axis_tid(pre_fifo_axis_tid),
.s_axis_tdest(pre_fifo_axis_tdest),
.s_axis_tuser(pre_fifo_axis_tuser),
// AXI output
.m_axis_tdata(post_fifo_axis_tdata),
.m_axis_tkeep(post_fifo_axis_tkeep),
.m_axis_tvalid(post_fifo_axis_tvalid),
.m_axis_tready(post_fifo_axis_tready),
.m_axis_tlast(post_fifo_axis_tlast),
.m_axis_tid(post_fifo_axis_tid),
.m_axis_tdest(post_fifo_axis_tdest),
.m_axis_tuser(post_fifo_axis_tuser),
// Pause
.pause_req(pause_req),
.pause_ack(pause_ack),
// Status
.status_depth(status_depth),
.status_depth_commit(status_depth_commit),
.status_overflow(status_overflow),
.status_bad_frame(status_bad_frame),
.status_good_frame(status_good_frame)
);
if (M_BYTE_LANES < S_BYTE_LANES) begin : downsize_post
// input wider, adapt width after FIFO
axis_adapter #(
.S_DATA_WIDTH(S_DATA_WIDTH),
.S_KEEP_ENABLE(S_KEEP_ENABLE),
@ -298,60 +341,21 @@ end else begin
.m_axis_tuser(m_axis_tuser)
);
end else begin : bypass_post
assign m_axis_tdata = post_fifo_axis_tdata;
assign m_axis_tkeep = post_fifo_axis_tkeep;
assign m_axis_tvalid = post_fifo_axis_tvalid;
assign post_fifo_axis_tready = m_axis_tready;
assign m_axis_tlast = post_fifo_axis_tlast;
assign m_axis_tid = post_fifo_axis_tid;
assign m_axis_tdest = post_fifo_axis_tdest;
assign m_axis_tuser = post_fifo_axis_tuser;
end
endgenerate
axis_fifo #(
.DEPTH(DEPTH),
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.LAST_ENABLE(1),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH),
.RAM_PIPELINE(RAM_PIPELINE),
.OUTPUT_FIFO_ENABLE(OUTPUT_FIFO_ENABLE),
.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)
)
fifo_inst (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(pre_fifo_axis_tdata),
.s_axis_tkeep(pre_fifo_axis_tkeep),
.s_axis_tvalid(pre_fifo_axis_tvalid),
.s_axis_tready(pre_fifo_axis_tready),
.s_axis_tlast(pre_fifo_axis_tlast),
.s_axis_tid(pre_fifo_axis_tid),
.s_axis_tdest(pre_fifo_axis_tdest),
.s_axis_tuser(pre_fifo_axis_tuser),
// AXI output
.m_axis_tdata(post_fifo_axis_tdata),
.m_axis_tkeep(post_fifo_axis_tkeep),
.m_axis_tvalid(post_fifo_axis_tvalid),
.m_axis_tready(post_fifo_axis_tready),
.m_axis_tlast(post_fifo_axis_tlast),
.m_axis_tid(post_fifo_axis_tid),
.m_axis_tdest(post_fifo_axis_tdest),
.m_axis_tuser(post_fifo_axis_tuser),
// Status
.status_depth(status_depth),
.status_depth_commit(status_depth_commit),
.status_overflow(status_overflow),
.status_bad_frame(status_bad_frame),
.status_good_frame(status_good_frame)
);
endmodule
`resetall

View File

@ -120,4 +120,21 @@ foreach fifo_inst [get_cells -hier -filter {(ORIG_REF_NAME == axis_async_fifo ||
set_max_delay -from [get_cells "$fifo_inst/${i}_sync1_reg_reg"] -to [get_cells "$fifo_inst/${i}_sync2_reg_reg"] -datapath_only $read_clk_period
}
}
# pause sync
set sync_ffs [get_cells -quiet -hier -regexp ".*/pause.s_pause_req_sync\[123\]_reg_reg" -filter "PARENT == $fifo_inst"]
if {[llength $sync_ffs]} {
set_property ASYNC_REG TRUE $sync_ffs
set_max_delay -from [get_cells "$fifo_inst/pause.s_pause_req_sync1_reg_reg"] -to [get_cells "$fifo_inst/pause.s_pause_req_sync2_reg_reg"] -datapath_only $read_clk_period
}
set sync_ffs [get_cells -quiet -hier -regexp ".*/pause.s_pause_ack_sync\[123\]_reg_reg" -filter "PARENT == $fifo_inst"]
if {[llength $sync_ffs]} {
set_property ASYNC_REG TRUE $sync_ffs
set_max_delay -from [get_cells "$fifo_inst/pause.s_pause_ack_sync1_reg_reg"] -to [get_cells "$fifo_inst/pause.s_pause_ack_sync2_reg_reg"] -datapath_only $write_clk_period
}
}

View File

@ -32,10 +32,10 @@ MODULE = test_$(DUT)
VERILOG_SOURCES += ../../rtl/$(DUT).v
# module parameters
export PARAM_DEPTH := 1024
export PARAM_DATA_WIDTH := 8
export PARAM_KEEP_ENABLE := $(shell expr $(PARAM_DATA_WIDTH) \> 8 )
export PARAM_KEEP_WIDTH := $(shell expr \( $(PARAM_DATA_WIDTH) + 7 \) / 8 )
export PARAM_KEEP_ENABLE := $(shell echo $$(( $(PARAM_DATA_WIDTH) > 8 )))
export PARAM_KEEP_WIDTH := $(shell echo $$(( ( $(PARAM_DATA_WIDTH) + 7 ) / 8 )))
export PARAM_DEPTH := $(shell echo $$(( 1024 * $(PARAM_KEEP_WIDTH) )))
export PARAM_LAST_ENABLE := 1
export PARAM_ID_ENABLE := 1
export PARAM_ID_WIDTH := 8
@ -51,6 +51,9 @@ 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_ENABLE := 1
export PARAM_FRAME_PAUSE := 1
ifeq ($(SIM), icarus)
PLUSARGS += -fst

View File

@ -55,6 +55,9 @@ class TB(object):
self.source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.s_clk, dut.s_rst)
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.m_clk, dut.m_rst)
dut.s_pause_req.setimmediatevalue(0)
dut.m_pause_req.setimmediatevalue(0)
def set_idle_generator(self, generator=None):
if generator:
self.source.set_pause_generator(generator())
@ -149,7 +152,7 @@ async def run_test_tuser_assert(dut):
test_frame = AxiStreamFrame(test_data, tuser=1)
await tb.source.send(test_frame)
if int(os.getenv("PARAM_DROP_BAD_FRAME")):
if dut.DROP_BAD_FRAME.value:
for k in range(64):
await RisingEdge(dut.s_clk)
@ -299,7 +302,7 @@ async def run_test_shift_in_source_reset(dut):
for k in range(64):
await RisingEdge(dut.s_clk)
if int(os.getenv("PARAM_FRAME_FIFO")):
if dut.FRAME_FIFO.value:
assert tb.sink.empty()
else:
rx_frame = await tb.sink.recv()
@ -393,32 +396,149 @@ async def run_test_shift_out_sink_reset(dut):
await RisingEdge(dut.s_clk)
async def run_test_pause(dut):
tb = TB(dut)
byte_lanes = tb.source.byte_lanes
await tb.reset()
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 16*byte_lanes))
test_frame = AxiStreamFrame(test_data)
for k in range(16):
await tb.source.send(test_frame)
for k in range(60):
await RisingEdge(dut.s_clk)
dut.m_pause_req.value = 1
for k in range(64):
await RisingEdge(dut.s_clk)
assert tb.sink.idle()
dut.m_pause_req.value = 0
for k in range(60):
await RisingEdge(dut.s_clk)
dut.s_pause_req.value = 1
for k in range(64):
await RisingEdge(dut.s_clk)
assert tb.sink.idle()
dut.s_pause_req.value = 0
for k in range(16):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.s_clk)
await RisingEdge(dut.s_clk)
async def run_test_overflow(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = tb.source.byte_lanes
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
size = (16*byte_lanes)
count = depth*2 // size
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), size))
test_frame = AxiStreamFrame(test_data)
for k in range(count):
await tb.source.send(test_frame)
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.s_clk)
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
assert tb.source.idle()
else:
assert not tb.source.idle()
tb.sink.pause = False
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.s_clk)
rx_count = 0
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
rx_count += 1
assert rx_count < count
else:
for k in range(count):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.s_clk)
await RisingEdge(dut.s_clk)
async def run_test_oversize(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = tb.source.byte_lanes
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), depth*2))
test_frame = AxiStreamFrame(test_data)
await tb.source.send(test_frame)
for k in range(2048):
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.s_clk)
tb.sink.pause = False
if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")):
for k in range(2048):
if dut.DROP_OVERSIZE_FRAME.value:
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.s_clk)
else:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
if dut.MARK_WHEN_FULL.value:
assert rx_frame.tuser
else:
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
@ -442,7 +562,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
test_frames = []
for k in range(128):
for k in range(512):
length = random.randint(1, byte_lanes*16)
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length))
test_frame = AxiStreamFrame(test_data)
@ -454,13 +574,37 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
cur_id = (cur_id + 1) % id_count
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
cycles = 0
while cycles < 100:
cycles += 1
if not tb.source.idle() or dut.s_axis_tvalid.value.integer or dut.m_axis_tvalid.value.integer or dut.m_status_depth.value.integer:
cycles = 0
await RisingEdge(dut.m_clk)
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert not rx_frame.tuser
while True:
test_frame = test_frames.pop(0)
if rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata:
break
assert len(test_frames) < 512
else:
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
assert tb.sink.empty()
@ -501,7 +645,9 @@ if cocotb.SIM_NAME:
run_test_shift_in_sink_reset,
run_test_shift_out_source_reset,
run_test_shift_out_sink_reset,
run_test_overflow
run_test_pause,
run_test_overflow,
run_test_oversize
]:
factory = TestFactory(test)
@ -520,13 +666,16 @@ rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
@pytest.mark.parametrize(("s_clk", "m_clk"), [(10, 10), (10, 11), (11, 10)])
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame", "drop_when_full"),
[(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0)])
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame",
"drop_when_full", "mark_when_full"),
[(0, 0, 0, 0, 0), (1, 0, 0, 0, 0), (1, 1, 0, 0, 0), (1, 1, 1, 0, 0),
(1, 1, 1, 1, 0), (0, 0, 0, 0, 1)])
@pytest.mark.parametrize(("ram_pipeline", "output_fifo"),
[(0, 0), (1, 0), (4, 0), (0, 1), (1, 1), (4, 1)])
@pytest.mark.parametrize("data_width", [8, 16, 32, 64])
def test_axis_async_fifo(request, data_width, ram_pipeline, output_fifo,
frame_fifo, drop_oversize_frame, drop_bad_frame, drop_when_full, s_clk, m_clk):
frame_fifo, drop_oversize_frame, drop_bad_frame,
drop_when_full, mark_when_full, s_clk, m_clk):
dut = "axis_async_fifo"
module = os.path.splitext(os.path.basename(__file__))[0]
@ -538,10 +687,10 @@ def test_axis_async_fifo(request, data_width, ram_pipeline, output_fifo,
parameters = {}
parameters['DEPTH'] = 1024
parameters['DATA_WIDTH'] = data_width
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
parameters['KEEP_WIDTH'] = (parameters['DATA_WIDTH'] + 7) // 8
parameters['DEPTH'] = 1024 * parameters['KEEP_WIDTH']
parameters['LAST_ENABLE'] = 1
parameters['ID_ENABLE'] = 1
parameters['ID_WIDTH'] = 8
@ -557,6 +706,9 @@ def test_axis_async_fifo(request, data_width, ram_pipeline, output_fifo,
parameters['DROP_OVERSIZE_FRAME'] = drop_oversize_frame
parameters['DROP_BAD_FRAME'] = drop_bad_frame
parameters['DROP_WHEN_FULL'] = drop_when_full
parameters['MARK_WHEN_FULL'] = mark_when_full
parameters['PAUSE_ENABLE'] = 1
parameters['FRAME_PAUSE'] = 1
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}

View File

@ -34,13 +34,13 @@ VERILOG_SOURCES += ../../rtl/axis_async_fifo.v
VERILOG_SOURCES += ../../rtl/axis_adapter.v
# module parameters
export PARAM_DEPTH := 1024
export PARAM_S_DATA_WIDTH := 8
export PARAM_S_KEEP_ENABLE := $(shell expr $(PARAM_S_DATA_WIDTH) \> 8 )
export PARAM_S_KEEP_WIDTH := $(shell expr \( $(PARAM_S_DATA_WIDTH) + 7 \) / 8 )
export PARAM_S_KEEP_ENABLE := $(shell echo $$(( $(PARAM_S_DATA_WIDTH) > 8 )))
export PARAM_S_KEEP_WIDTH := $(shell echo $$(( ( $(PARAM_S_DATA_WIDTH) + 7 ) / 8 )))
export PARAM_M_DATA_WIDTH := 8
export PARAM_M_KEEP_ENABLE := $(shell expr $(PARAM_M_DATA_WIDTH) \> 8 )
export PARAM_M_KEEP_WIDTH := $(shell expr \( $(PARAM_M_DATA_WIDTH) + 7 \) / 8 )
export PARAM_M_KEEP_ENABLE := $(shell echo $$(( $(PARAM_M_DATA_WIDTH) > 8 )))
export PARAM_M_KEEP_WIDTH := $(shell echo $$(( ( $(PARAM_M_DATA_WIDTH) + 7 ) / 8 )))
export PARAM_DEPTH := $(shell echo $$(( 1024 * ($(PARAM_S_KEEP_WIDTH) > $(PARAM_M_KEEP_WIDTH) ? $(PARAM_S_KEEP_WIDTH) : $(PARAM_M_KEEP_WIDTH)) )))
export PARAM_ID_ENABLE := 1
export PARAM_ID_WIDTH := 8
export PARAM_DEST_ENABLE := 1
@ -55,6 +55,9 @@ 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_ENABLE := 1
export PARAM_FRAME_PAUSE := 1
ifeq ($(SIM), icarus)
PLUSARGS += -fst

View File

@ -52,6 +52,9 @@ class TB(object):
self.source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.s_clk, dut.s_rst)
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.m_clk, dut.m_rst)
dut.s_pause_req.setimmediatevalue(0)
dut.m_pause_req.setimmediatevalue(0)
def set_idle_generator(self, generator=None):
if generator:
self.source.set_pause_generator(generator())
@ -146,7 +149,7 @@ async def run_test_tuser_assert(dut):
test_frame = AxiStreamFrame(test_data, tuser=1)
await tb.source.send(test_frame)
if int(os.getenv("PARAM_DROP_BAD_FRAME")):
if dut.DROP_BAD_FRAME.value:
for k in range(64):
await RisingEdge(dut.s_clk)
@ -296,7 +299,7 @@ async def run_test_shift_in_source_reset(dut):
for k in range(64):
await RisingEdge(dut.s_clk)
if int(os.getenv("PARAM_FRAME_FIFO")):
if dut.FRAME_FIFO.value:
assert tb.sink.empty()
else:
rx_frame = await tb.sink.recv()
@ -390,32 +393,149 @@ async def run_test_shift_out_sink_reset(dut):
await RisingEdge(dut.s_clk)
async def run_test_pause(dut):
tb = TB(dut)
byte_lanes = max(tb.source.byte_lanes, tb.sink.byte_lanes)
await tb.reset()
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 16*byte_lanes))
test_frame = AxiStreamFrame(test_data)
for k in range(16):
await tb.source.send(test_frame)
for k in range(60):
await RisingEdge(dut.s_clk)
dut.m_pause_req.value = 1
for k in range(64):
await RisingEdge(dut.s_clk)
assert tb.sink.idle()
dut.m_pause_req.value = 0
for k in range(60):
await RisingEdge(dut.s_clk)
dut.s_pause_req.value = 1
for k in range(64):
await RisingEdge(dut.s_clk)
assert tb.sink.idle()
dut.s_pause_req.value = 0
for k in range(16):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.s_clk)
await RisingEdge(dut.s_clk)
async def run_test_overflow(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = min(tb.source.byte_lanes, tb.sink.byte_lanes)
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
size = (16*byte_lanes)
count = depth*2 // size
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), size))
test_frame = AxiStreamFrame(test_data)
for k in range(count):
await tb.source.send(test_frame)
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.s_clk)
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
assert tb.source.idle()
else:
assert not tb.source.idle()
tb.sink.pause = False
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.s_clk)
rx_count = 0
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
rx_count += 1
assert rx_count < count
else:
for k in range(count):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.s_clk)
await RisingEdge(dut.s_clk)
async def run_test_oversize(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = min(tb.source.byte_lanes, tb.sink.byte_lanes)
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), depth*2))
test_frame = AxiStreamFrame(test_data)
await tb.source.send(test_frame)
for k in range(2048):
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.s_clk)
tb.sink.pause = False
if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")):
for k in range(2048):
if dut.DROP_OVERSIZE_FRAME.value:
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.s_clk)
else:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
if dut.MARK_WHEN_FULL.value:
assert rx_frame.tuser
else:
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
@ -439,7 +559,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
test_frames = []
for k in range(128):
for k in range(512):
length = random.randint(1, byte_lanes*16)
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length))
test_frame = AxiStreamFrame(test_data)
@ -451,13 +571,37 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
cur_id = (cur_id + 1) % id_count
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
cycles = 0
while cycles < 100:
cycles += 1
if not tb.source.idle() or dut.s_axis_tvalid.value.integer or dut.m_axis_tvalid.value.integer or dut.m_status_depth.value.integer:
cycles = 0
await RisingEdge(dut.m_clk)
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert not rx_frame.tuser
while True:
test_frame = test_frames.pop(0)
if rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata:
break
assert len(test_frames) < 512
else:
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
assert tb.sink.empty()
@ -498,7 +642,9 @@ if cocotb.SIM_NAME:
run_test_shift_in_sink_reset,
run_test_shift_out_source_reset,
run_test_shift_out_sink_reset,
run_test_overflow
run_test_pause,
run_test_overflow,
run_test_oversize
]:
factory = TestFactory(test)
@ -516,11 +662,15 @@ tests_dir = os.path.dirname(__file__)
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame", "drop_when_full"),
[(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0)])
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame",
"drop_when_full", "mark_when_full"),
[(0, 0, 0, 0, 0), (1, 0, 0, 0, 0), (1, 1, 0, 0, 0), (1, 1, 1, 0, 0),
(1, 1, 1, 1, 0), (0, 0, 0, 0, 1)])
@pytest.mark.parametrize("m_data_width", [8, 16, 32])
@pytest.mark.parametrize("s_data_width", [8, 16, 32])
def test_axis_async_fifo_adapter(request, s_data_width, m_data_width, frame_fifo, drop_oversize_frame, drop_bad_frame, drop_when_full):
def test_axis_async_fifo_adapter(request, s_data_width, m_data_width,
frame_fifo, drop_oversize_frame, drop_bad_frame,
drop_when_full, mark_when_full):
dut = "axis_async_fifo_adapter"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut
@ -533,13 +683,13 @@ def test_axis_async_fifo_adapter(request, s_data_width, m_data_width, frame_fifo
parameters = {}
parameters['DEPTH'] = 1024
parameters['S_DATA_WIDTH'] = s_data_width
parameters['S_KEEP_ENABLE'] = int(parameters['S_DATA_WIDTH'] > 8)
parameters['S_KEEP_WIDTH'] = (parameters['S_DATA_WIDTH'] + 7) // 8
parameters['M_DATA_WIDTH'] = m_data_width
parameters['M_KEEP_ENABLE'] = int(parameters['M_DATA_WIDTH'] > 8)
parameters['M_KEEP_WIDTH'] = (parameters['M_DATA_WIDTH'] + 7) // 8
parameters['DEPTH'] = 1024 * max(parameters['S_KEEP_WIDTH'], parameters['M_KEEP_WIDTH'])
parameters['ID_ENABLE'] = 1
parameters['ID_WIDTH'] = 8
parameters['DEST_ENABLE'] = 1
@ -554,6 +704,9 @@ def test_axis_async_fifo_adapter(request, s_data_width, m_data_width, frame_fifo
parameters['DROP_OVERSIZE_FRAME'] = drop_oversize_frame
parameters['DROP_BAD_FRAME'] = drop_bad_frame
parameters['DROP_WHEN_FULL'] = drop_when_full
parameters['MARK_WHEN_FULL'] = mark_when_full
parameters['PAUSE_ENABLE'] = 1
parameters['FRAME_PAUSE'] = 1
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}

View File

@ -32,10 +32,10 @@ MODULE = test_$(DUT)
VERILOG_SOURCES += ../../rtl/$(DUT).v
# module parameters
export PARAM_DEPTH := 1024
export PARAM_DATA_WIDTH := 8
export PARAM_KEEP_ENABLE := $(shell expr $(PARAM_DATA_WIDTH) \> 8 )
export PARAM_KEEP_WIDTH := $(shell expr \( $(PARAM_DATA_WIDTH) + 7 \) / 8 )
export PARAM_KEEP_ENABLE := $(shell echo $$(( $(PARAM_DATA_WIDTH) > 8 )))
export PARAM_KEEP_WIDTH := $(shell echo $$(( ( $(PARAM_DATA_WIDTH) + 7 ) / 8 )))
export PARAM_DEPTH := $(shell echo $$(( 1024 * $(PARAM_KEEP_WIDTH) )))
export PARAM_LAST_ENABLE := 1
export PARAM_ID_ENABLE := 1
export PARAM_ID_WIDTH := 8
@ -51,6 +51,9 @@ 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_ENABLE := 1
export PARAM_FRAME_PAUSE := 1
ifeq ($(SIM), icarus)
PLUSARGS += -fst

View File

@ -51,6 +51,8 @@ class TB(object):
self.source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.clk, dut.rst)
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
dut.pause_req.setimmediatevalue(0)
def set_idle_generator(self, generator=None):
if generator:
self.source.set_pause_generator(generator())
@ -120,7 +122,7 @@ async def run_test_tuser_assert(dut):
test_frame = AxiStreamFrame(test_data, tuser=1)
await tb.source.send(test_frame)
if int(os.getenv("PARAM_DROP_BAD_FRAME")):
if dut.DROP_BAD_FRAME.value:
for k in range(64):
await RisingEdge(dut.clk)
@ -192,32 +194,137 @@ async def run_test_init_sink_pause_reset(dut):
await RisingEdge(dut.clk)
async def run_test_pause(dut):
tb = TB(dut)
byte_lanes = tb.source.byte_lanes
await tb.reset()
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 16*byte_lanes))
test_frame = AxiStreamFrame(test_data)
for k in range(16):
await tb.source.send(test_frame)
for k in range(60):
await RisingEdge(dut.clk)
dut.pause_req.value = 1
for k in range(64):
await RisingEdge(dut.clk)
assert tb.sink.idle()
dut.pause_req.value = 0
for k in range(16):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
async def run_test_overflow(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = tb.source.byte_lanes
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
size = (16*byte_lanes)
count = depth*2 // size
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), size))
test_frame = AxiStreamFrame(test_data)
for k in range(count):
await tb.source.send(test_frame)
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.clk)
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
assert tb.source.idle()
else:
assert not tb.source.idle()
tb.sink.pause = False
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.clk)
rx_count = 0
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
rx_count += 1
assert rx_count < count
else:
for k in range(count):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
async def run_test_oversize(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = tb.source.byte_lanes
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), depth*2))
test_frame = AxiStreamFrame(test_data)
await tb.source.send(test_frame)
for k in range(2048):
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.clk)
tb.sink.pause = False
if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")):
for k in range(2048):
if dut.DROP_OVERSIZE_FRAME.value:
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.clk)
else:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
if dut.MARK_WHEN_FULL.value:
assert rx_frame.tuser
else:
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
@ -241,7 +348,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
test_frames = []
for k in range(128):
for k in range(512):
length = random.randint(1, byte_lanes*16)
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length))
test_frame = AxiStreamFrame(test_data)
@ -253,13 +360,39 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
cur_id = (cur_id + 1) % id_count
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
cycles = 0
while cycles < 100:
cycles += 1
if not tb.source.idle() or dut.s_axis_tvalid.value.integer or dut.m_axis_tvalid.value.integer or dut.status_depth.value.integer:
cycles = 0
await RisingEdge(dut.clk)
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert not rx_frame.tuser
assert len(test_frames) > 0
while True:
test_frame = test_frames.pop(0)
if rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata:
break
assert len(test_frames) < 512
else:
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
assert tb.sink.empty()
@ -294,7 +427,9 @@ if cocotb.SIM_NAME:
run_test_tuser_assert,
run_test_init_sink_pause,
run_test_init_sink_pause_reset,
run_test_overflow
run_test_pause,
run_test_overflow,
run_test_oversize
]:
factory = TestFactory(test)
@ -312,13 +447,16 @@ tests_dir = os.path.dirname(__file__)
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame", "drop_when_full"),
[(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0)])
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame",
"drop_when_full", "mark_when_full"),
[(0, 0, 0, 0, 0), (1, 0, 0, 0, 0), (1, 1, 0, 0, 0), (1, 1, 1, 0, 0),
(1, 1, 1, 1, 0), (0, 0, 0, 0, 1)])
@pytest.mark.parametrize(("ram_pipeline", "output_fifo"),
[(0, 0), (1, 0), (4, 0), (0, 1), (1, 1), (4, 1)])
@pytest.mark.parametrize("data_width", [8, 16, 32, 64])
def test_axis_fifo(request, data_width, ram_pipeline, output_fifo,
frame_fifo, drop_oversize_frame, drop_bad_frame, drop_when_full):
frame_fifo, drop_oversize_frame, drop_bad_frame,
drop_when_full, mark_when_full):
dut = "axis_fifo"
module = os.path.splitext(os.path.basename(__file__))[0]
@ -330,10 +468,10 @@ def test_axis_fifo(request, data_width, ram_pipeline, output_fifo,
parameters = {}
parameters['DEPTH'] = 1024
parameters['DATA_WIDTH'] = data_width
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
parameters['KEEP_WIDTH'] = (parameters['DATA_WIDTH'] + 7) // 8
parameters['DEPTH'] = 1024 * parameters['KEEP_WIDTH']
parameters['LAST_ENABLE'] = 1
parameters['ID_ENABLE'] = 1
parameters['ID_WIDTH'] = 8
@ -349,6 +487,9 @@ def test_axis_fifo(request, data_width, ram_pipeline, output_fifo,
parameters['DROP_OVERSIZE_FRAME'] = drop_oversize_frame
parameters['DROP_BAD_FRAME'] = drop_bad_frame
parameters['DROP_WHEN_FULL'] = drop_when_full
parameters['MARK_WHEN_FULL'] = mark_when_full
parameters['PAUSE_ENABLE'] = 1
parameters['FRAME_PAUSE'] = 1
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}

View File

@ -34,13 +34,13 @@ VERILOG_SOURCES += ../../rtl/axis_fifo.v
VERILOG_SOURCES += ../../rtl/axis_adapter.v
# module parameters
export PARAM_DEPTH := 1024
export PARAM_S_DATA_WIDTH := 8
export PARAM_S_KEEP_ENABLE := $(shell expr $(PARAM_S_DATA_WIDTH) \> 8 )
export PARAM_S_KEEP_WIDTH := $(shell expr \( $(PARAM_S_DATA_WIDTH) + 7 \) / 8 )
export PARAM_S_KEEP_ENABLE := $(shell echo $$(( $(PARAM_S_DATA_WIDTH) > 8 )))
export PARAM_S_KEEP_WIDTH := $(shell echo $$(( ( $(PARAM_S_DATA_WIDTH) + 7 ) / 8 )))
export PARAM_M_DATA_WIDTH := 8
export PARAM_M_KEEP_ENABLE := $(shell expr $(PARAM_M_DATA_WIDTH) \> 8 )
export PARAM_M_KEEP_WIDTH := $(shell expr \( $(PARAM_M_DATA_WIDTH) + 7 \) / 8 )
export PARAM_M_KEEP_ENABLE := $(shell echo $$(( $(PARAM_M_DATA_WIDTH) > 8 )))
export PARAM_M_KEEP_WIDTH := $(shell echo $$(( ( $(PARAM_M_DATA_WIDTH) + 7 ) / 8 )))
export PARAM_DEPTH := $(shell echo $$(( 1024 * ($(PARAM_S_KEEP_WIDTH) > $(PARAM_M_KEEP_WIDTH) ? $(PARAM_S_KEEP_WIDTH) : $(PARAM_M_KEEP_WIDTH)) )))
export PARAM_ID_ENABLE := 1
export PARAM_ID_WIDTH := 8
export PARAM_DEST_ENABLE := 1
@ -55,6 +55,9 @@ 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_ENABLE := 1
export PARAM_FRAME_PAUSE := 1
ifeq ($(SIM), icarus)
PLUSARGS += -fst

View File

@ -51,6 +51,8 @@ class TB(object):
self.source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.clk, dut.rst)
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
dut.pause_req.setimmediatevalue(0)
def set_idle_generator(self, generator=None):
if generator:
self.source.set_pause_generator(generator())
@ -120,7 +122,7 @@ async def run_test_tuser_assert(dut):
test_frame = AxiStreamFrame(test_data, tuser=1)
await tb.source.send(test_frame)
if int(os.getenv("PARAM_DROP_BAD_FRAME")):
if dut.DROP_BAD_FRAME.value:
for k in range(64):
await RisingEdge(dut.clk)
@ -192,32 +194,137 @@ async def run_test_init_sink_pause_reset(dut):
await RisingEdge(dut.clk)
async def run_test_pause(dut):
tb = TB(dut)
byte_lanes = max(tb.source.byte_lanes, tb.sink.byte_lanes)
await tb.reset()
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 16*byte_lanes))
test_frame = AxiStreamFrame(test_data)
for k in range(16):
await tb.source.send(test_frame)
for k in range(60):
await RisingEdge(dut.clk)
dut.pause_req.value = 1
for k in range(64):
await RisingEdge(dut.clk)
assert tb.sink.idle()
dut.pause_req.value = 0
for k in range(16):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
async def run_test_overflow(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = min(tb.source.byte_lanes, tb.sink.byte_lanes)
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
size = (16*byte_lanes)
count = depth*2 // size
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), size))
test_frame = AxiStreamFrame(test_data)
for k in range(count):
await tb.source.send(test_frame)
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.clk)
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
assert tb.source.idle()
else:
assert not tb.source.idle()
tb.sink.pause = False
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
for k in range((depth//byte_lanes)*3):
await RisingEdge(dut.clk)
rx_count = 0
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
rx_count += 1
assert rx_count < count
else:
for k in range(count):
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
async def run_test_oversize(dut):
tb = TB(dut)
depth = dut.DEPTH.value
byte_lanes = min(tb.source.byte_lanes, tb.sink.byte_lanes)
await tb.reset()
tb.sink.pause = True
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), depth*2))
test_frame = AxiStreamFrame(test_data)
await tb.source.send(test_frame)
for k in range(2048):
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.clk)
tb.sink.pause = False
if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")):
for k in range(2048):
if dut.DROP_OVERSIZE_FRAME.value:
for k in range((depth//byte_lanes)*2):
await RisingEdge(dut.clk)
else:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
if dut.MARK_WHEN_FULL.value:
assert rx_frame.tuser
else:
assert rx_frame.tdata == test_data
assert not rx_frame.tuser
assert tb.sink.empty()
@ -241,7 +348,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
test_frames = []
for k in range(128):
for k in range(512):
length = random.randint(1, byte_lanes*16)
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length))
test_frame = AxiStreamFrame(test_data)
@ -253,13 +360,37 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
cur_id = (cur_id + 1) % id_count
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value:
cycles = 0
while cycles < 100:
cycles += 1
if not tb.source.idle() or dut.s_axis_tvalid.value.integer or dut.m_axis_tvalid.value.integer or dut.status_depth.value.integer:
cycles = 0
await RisingEdge(dut.clk)
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
while not tb.sink.empty():
rx_frame = await tb.sink.recv()
if dut.MARK_WHEN_FULL.value and rx_frame.tuser:
continue
assert not rx_frame.tuser
while True:
test_frame = test_frames.pop(0)
if rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata:
break
assert len(test_frames) < 512
else:
for test_frame in test_frames:
rx_frame = await tb.sink.recv()
assert rx_frame.tdata == test_frame.tdata
assert rx_frame.tid == test_frame.tid
assert rx_frame.tdest == test_frame.tdest
assert not rx_frame.tuser
assert tb.sink.empty()
@ -294,7 +425,9 @@ if cocotb.SIM_NAME:
run_test_tuser_assert,
run_test_init_sink_pause,
run_test_init_sink_pause_reset,
run_test_overflow
run_test_pause,
run_test_overflow,
run_test_oversize
]:
factory = TestFactory(test)
@ -312,11 +445,15 @@ tests_dir = os.path.dirname(__file__)
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame", "drop_when_full"),
[(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0)])
@pytest.mark.parametrize(("frame_fifo", "drop_oversize_frame", "drop_bad_frame",
"drop_when_full", "mark_when_full"),
[(0, 0, 0, 0, 0), (1, 0, 0, 0, 0), (1, 1, 0, 0, 0), (1, 1, 1, 0, 0),
(1, 1, 1, 1, 0), (0, 0, 0, 0, 1)])
@pytest.mark.parametrize("m_data_width", [8, 16, 32])
@pytest.mark.parametrize("s_data_width", [8, 16, 32])
def test_axis_fifo_adapter(request, s_data_width, m_data_width, frame_fifo, drop_oversize_frame, drop_bad_frame, drop_when_full):
def test_axis_fifo_adapter(request, s_data_width, m_data_width,
frame_fifo, drop_oversize_frame, drop_bad_frame,
drop_when_full, mark_when_full):
dut = "axis_fifo_adapter"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut
@ -329,13 +466,13 @@ def test_axis_fifo_adapter(request, s_data_width, m_data_width, frame_fifo, drop
parameters = {}
parameters['DEPTH'] = 1024
parameters['S_DATA_WIDTH'] = s_data_width
parameters['S_KEEP_ENABLE'] = int(parameters['S_DATA_WIDTH'] > 8)
parameters['S_KEEP_WIDTH'] = (parameters['S_DATA_WIDTH'] + 7) // 8
parameters['M_DATA_WIDTH'] = m_data_width
parameters['M_KEEP_ENABLE'] = int(parameters['M_DATA_WIDTH'] > 8)
parameters['M_KEEP_WIDTH'] = (parameters['M_DATA_WIDTH'] + 7) // 8
parameters['DEPTH'] = 1024 * max(parameters['S_KEEP_WIDTH'], parameters['M_KEEP_WIDTH'])
parameters['ID_ENABLE'] = 1
parameters['ID_WIDTH'] = 8
parameters['DEST_ENABLE'] = 1
@ -350,6 +487,9 @@ def test_axis_fifo_adapter(request, s_data_width, m_data_width, frame_fifo, drop
parameters['DROP_OVERSIZE_FRAME'] = drop_oversize_frame
parameters['DROP_BAD_FRAME'] = drop_bad_frame
parameters['DROP_WHEN_FULL'] = drop_when_full
parameters['MARK_WHEN_FULL'] = mark_when_full
parameters['PAUSE_ENABLE'] = 1
parameters['FRAME_PAUSE'] = 1
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}