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:
commit
f92a94d278
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user