diff --git a/lib/axis/rtl/axis_adapter.v b/lib/axis/rtl/axis_adapter.v index 288fa16c6..0dedfac0f 100644 --- a/lib/axis/rtl/axis_adapter.v +++ b/lib/axis/rtl/axis_adapter.v @@ -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 diff --git a/lib/axis/rtl/axis_async_fifo.v b/lib/axis/rtl/axis_async_fifo.v index b56af5b02..76eeb8d85 100644 --- a/lib/axis/rtl/axis_async_fifo.v +++ b/lib/axis/rtl/axis_async_fifo.v @@ -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 diff --git a/lib/axis/rtl/axis_async_fifo_adapter.v b/lib/axis/rtl/axis_async_fifo_adapter.v index 8bb69d2bc..77969960a 100644 --- a/lib/axis/rtl/axis_async_fifo_adapter.v +++ b/lib/axis/rtl/axis_async_fifo_adapter.v @@ -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 diff --git a/lib/axis/rtl/axis_fifo.v b/lib/axis/rtl/axis_fifo.v index a0f2df395..e9c9258eb 100644 --- a/lib/axis/rtl/axis_fifo.v +++ b/lib/axis/rtl/axis_fifo.v @@ -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 diff --git a/lib/axis/rtl/axis_fifo_adapter.v b/lib/axis/rtl/axis_fifo_adapter.v index 4fa6776ae..44ecc5c66 100644 --- a/lib/axis/rtl/axis_fifo_adapter.v +++ b/lib/axis/rtl/axis_fifo_adapter.v @@ -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 diff --git a/lib/axis/syn/vivado/axis_async_fifo.tcl b/lib/axis/syn/vivado/axis_async_fifo.tcl index 88218ccb9..5654295e0 100644 --- a/lib/axis/syn/vivado/axis_async_fifo.tcl +++ b/lib/axis/syn/vivado/axis_async_fifo.tcl @@ -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 + } } diff --git a/lib/axis/tb/axis_async_fifo/Makefile b/lib/axis/tb/axis_async_fifo/Makefile index 70c29fd77..10ffd633f 100644 --- a/lib/axis/tb/axis_async_fifo/Makefile +++ b/lib/axis/tb/axis_async_fifo/Makefile @@ -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 diff --git a/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py b/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py index c4f13e5f1..4b126a793 100644 --- a/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py @@ -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()} diff --git a/lib/axis/tb/axis_async_fifo_adapter/Makefile b/lib/axis/tb/axis_async_fifo_adapter/Makefile index 655c2d195..e70946194 100644 --- a/lib/axis/tb/axis_async_fifo_adapter/Makefile +++ b/lib/axis/tb/axis_async_fifo_adapter/Makefile @@ -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 diff --git a/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index 0e62fcbd8..5d09b0703 100644 --- a/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -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()} diff --git a/lib/axis/tb/axis_fifo/Makefile b/lib/axis/tb/axis_fifo/Makefile index d22bb2aa8..c10508ee8 100644 --- a/lib/axis/tb/axis_fifo/Makefile +++ b/lib/axis/tb/axis_fifo/Makefile @@ -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 diff --git a/lib/axis/tb/axis_fifo/test_axis_fifo.py b/lib/axis/tb/axis_fifo/test_axis_fifo.py index 5a5fc6fd7..e89c0a5ff 100644 --- a/lib/axis/tb/axis_fifo/test_axis_fifo.py +++ b/lib/axis/tb/axis_fifo/test_axis_fifo.py @@ -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()} diff --git a/lib/axis/tb/axis_fifo_adapter/Makefile b/lib/axis/tb/axis_fifo_adapter/Makefile index fee2a01bb..65234e80b 100644 --- a/lib/axis/tb/axis_fifo_adapter/Makefile +++ b/lib/axis/tb/axis_fifo_adapter/Makefile @@ -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 diff --git a/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index b22310634..5b4b992ad 100644 --- a/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -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()}