From be6b015195241f06b2afbee4d85ee290484a5308 Mon Sep 17 00:00:00 2001 From: Laez Barbosa Date: Mon, 11 Aug 2025 12:19:23 -0300 Subject: [PATCH 1/3] library/util_axis_fifo_asym: fix behavior when tkeep=0 Fix a bug where m_axis_valid waits on m_axis_ready to be asserted a few times in order to skip over data for which tkeep was all zeroes. This was a violation of the AXI Streaming protocol. Signed-off-by: Laez Barbosa --- library/util_axis_fifo_asym/util_axis_fifo_asym.v | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/util_axis_fifo_asym/util_axis_fifo_asym.v b/library/util_axis_fifo_asym/util_axis_fifo_asym.v index 9fe6fa1a62..0d9af08fe7 100644 --- a/library/util_axis_fifo_asym/util_axis_fifo_asym.v +++ b/library/util_axis_fifo_asym/util_axis_fifo_asym.v @@ -216,7 +216,9 @@ module util_axis_fifo_asym #( if (RATIO_TYPE) begin : small_master for (i=0; i> (m_axis_counter*A_WIDTH) ; @@ -317,7 +319,10 @@ module util_axis_fifo_asym #( if (!m_axis_aresetn) begin m_axis_counter <= 0; end else begin - if (m_axis_ready && m_axis_valid_int) begin + // When using tkeep, we might have an internally "valid" data beat without any actually valid bytes. + // This will result in m_axis_valid being actually low for the external world. + // In this case (tkeep is all 0), we need to increment the counter without waiting for m_axis_ready. + if ((m_axis_ready && m_axis_valid) || (TKEEP_EN && m_axis_valid_int && !(|m_axis_tkeep))) begin m_axis_counter <= m_axis_counter + 1'b1; end end From b4e15c4da4a020e55cb33f174c420b0c91691d68 Mon Sep 17 00:00:00 2001 From: Laez Barbosa Date: Fri, 12 Sep 2025 17:49:45 -0300 Subject: [PATCH 2/3] library/util_axis_fifo_asym: fix tlast behavior when tkeep=0 Fix tvalid low when tlast is present. Before this commit, util_axis_fifo would suppress all transfers with tkeep=0, even if tlast was asserted. This commit makes it so transfers with tlast asserted are preserved. The AXI-Streaming spec allows us to suppress transfers for which all tkeep bits are 0 (all bytes are null bytes), but only in the case when tlast=0 as well. Transfers where tlast is asserted can't be suppressed even when all bytes are null. Signed-off-by: Laez Barbosa --- .../util_axis_fifo_asym/util_axis_fifo_asym.v | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/library/util_axis_fifo_asym/util_axis_fifo_asym.v b/library/util_axis_fifo_asym/util_axis_fifo_asym.v index 0d9af08fe7..f6c8a9e12b 100644 --- a/library/util_axis_fifo_asym/util_axis_fifo_asym.v +++ b/library/util_axis_fifo_asym/util_axis_fifo_asym.v @@ -153,8 +153,18 @@ module util_axis_fifo_asym #( if (TKEEP_EN) begin - assign s_axis_tlast_int_s[i] = (i==RATIO-1) ? ((|s_axis_tkeep[M_DATA_WIDTH/8*i+:M_DATA_WIDTH/8]) ? s_axis_tlast : 1'b0) : - (((|s_axis_tkeep[M_DATA_WIDTH/8*i+:M_DATA_WIDTH/8]) & (~(|s_axis_tkeep[M_DATA_WIDTH/8*(i+1)+:M_DATA_WIDTH/8]))) ? s_axis_tlast : 1'b0); + // tlast is asserted for the atomic fifo instances on the following conditions: + // - for the most significant instance, tlast is the input tlast if any of the tkeep bits for the instance is asserted (so we are not suppressing this transfer) + // - for the least significat instance, tlast is the input tlast if no tkeep bits are asserted for any instance (transfer is all null bytes) + // - for the other instances, we store the input tlast if all the following instances have tkeep=0 for all bits and no less significant instance has stored it + // thus, the tlast is stored on the most significant instance that has non-null bytes (meaning not all tkeep bits are 0), if there are any. + if (i==RATIO-1) begin + assign s_axis_tlast_int_s[i] = (|s_axis_tkeep[M_DATA_WIDTH/8*i+:M_DATA_WIDTH/8]) ? s_axis_tlast : 1'b0; + end else if (i==0) begin + assign s_axis_tlast_int_s[i] = (~|s_axis_tkeep) ? s_axis_tlast : 1'b0; + end else begin + assign s_axis_tlast_int_s[i] = (~(|s_axis_tkeep[(S_DATA_WIDTH/8)-1:(M_DATA_WIDTH/8)*(i+1)]) && ~|s_axis_tlast_int_s[i-1:0]) ? s_axis_tlast : 1'b0; + end end else begin @@ -226,7 +236,17 @@ module util_axis_fifo_asym #( // VALID/EMPTY/ALMOST_EMPTY is driven by the current atomic instance assign m_axis_valid_int = m_axis_valid_int_s >> m_axis_counter; - assign m_axis_valid = m_axis_valid_int & (|m_axis_tkeep); + + // When TLAST_EN=1, we still have to assert m_axis_valid when tlast is + // high even if all bytes are null due to tkeep. AXI-Streaming only allows + // us to suppress transfers with all tkeep bits deasserted if tlast is + // also deasserted. + if (TLAST_EN) begin + assign m_axis_valid = m_axis_valid_int & ((|m_axis_tkeep) || m_axis_tlast); + end else begin + assign m_axis_valid = m_axis_valid_int & (|m_axis_tkeep); + end + assign m_axis_tlast = m_axis_tlast_int_s >> m_axis_counter; // the FIFO has the same level as the last atomic instance From b4e690a05975433618e740ab8ba62fbd41012c56 Mon Sep 17 00:00:00 2001 From: caosjr Date: Tue, 16 Sep 2025 11:18:41 -0300 Subject: [PATCH 3/3] library/util_axis_fifo_asym: fix room and level signals ROOM and LEVEL signals are incorrect when there is an imbalance between the size of the input and output and TKEEP_EN == 1. This commit fixes this issue by inserting two counters, which are better for timing than performing a sum reduce ROOM and LEVEL of the internal symmetrical FIFOs Signed-off-by: Carlos Souza --- .../util_axis_fifo_asym/util_axis_fifo_asym.v | 110 +++++++++++++++--- 1 file changed, 96 insertions(+), 14 deletions(-) diff --git a/library/util_axis_fifo_asym/util_axis_fifo_asym.v b/library/util_axis_fifo_asym/util_axis_fifo_asym.v index f6c8a9e12b..ed660b0035 100644 --- a/library/util_axis_fifo_asym/util_axis_fifo_asym.v +++ b/library/util_axis_fifo_asym/util_axis_fifo_asym.v @@ -85,6 +85,18 @@ module util_axis_fifo_asym #( reg [$clog2(RATIO)-1:0] s_axis_counter; reg [$clog2(RATIO)-1:0] m_axis_counter; + // FIFO level signals + reg s_axis_ready_d; + reg s_axis_valid_d; + reg [ 2:0] word_counter; + reg [ 2:0] num_of_words; + reg [ADDRESS_WIDTH-1:0] s_input_level; + reg [ADDRESS_WIDTH-1:0] s_input_room; + reg [ADDRESS_WIDTH-1:0] s_input_level_next; + reg [ADDRESS_WIDTH-1:0] s_input_room_next; + wire [ADDRESS_WIDTH-1:0] m_output_level; + wire sdi_output_read_sync; + wire [RATIO-1:0] m_axis_ready_int_s; wire [RATIO-1:0] m_axis_valid_int_s; wire [RATIO*A_WIDTH-1:0] m_axis_data_int_s; @@ -146,6 +158,46 @@ module util_axis_fifo_asym #( // write or slave logic generate + integer j; + always @(posedge s_axis_aclk) begin + if (!s_axis_aresetn) begin + num_of_words <= 1; + s_axis_ready_d <= 0; + s_axis_valid_d <= 0; + end else begin + s_axis_ready_d <= s_axis_ready; + s_axis_valid_d <= s_axis_valid; + word_counter = 0; + for (j = 0; j < RATIO; j = j + 1) begin + word_counter = word_counter + (|s_axis_tkeep[M_DATA_WIDTH/8*j+:M_DATA_WIDTH/8]); + end + num_of_words <= word_counter; + end + end + + always @(posedge s_axis_aclk) begin + if (!s_axis_aresetn) begin + s_input_level <= 0; + s_input_room <= {ADDRESS_WIDTH{1'b1}}; + end else begin + s_input_room <= s_input_room_next; + s_input_level <= s_input_level_next; + end + end + + always @(*) begin + s_input_level_next = s_input_level; + s_input_room_next = s_input_room; + if (s_axis_ready_d && s_axis_valid_d) begin + s_input_level_next = s_input_level_next + num_of_words; + s_input_room_next = s_input_room_next - 1; + end + if (sdi_output_read_sync) begin + s_input_level_next = s_input_level_next - 1; + s_input_room_next = s_input_room_next + num_of_words; + end + end + if (RATIO_TYPE) begin : big_slave for (i=0; i> s_axis_counter; - - // the FIFO has the same room as the last atomic instance - // (NOTE: this is not the real room value, rather the value will be updated - // after every RATIO number of writes) assign s_axis_full = s_axis_full_int_s[RATIO-1]; - assign s_axis_room = {s_axis_room_int_s[A_ADDRESS*(RATIO-1)+:A_ADDRESS], {$clog2(RATIO){1'b1}}}; + + // FIFO room behavior relies on the TKEEP_EN parameter + // When TKEEP_EN == 0, the FIFO has the same room as + // the last atomic instance multiplied by RATIO. The + // value of the room is only updated every RATIO reads. + // s_axis_room == {s_axis_room_int_s[A_ADDRESS*(RATIO-1)+:A_ADDRESS], {$clog2(RATIO){1'b1}}} + // + // When TKEEP_EN == 1, it is using a counter whose decrement + // relies on the tkeep. + // s_axis_room == s_input_room + assign s_axis_room = (TKEEP_EN) ? s_input_room : {s_axis_room_int_s[A_ADDRESS*(RATIO-1)+:A_ADDRESS], {$clog2(RATIO){1'b1}}}; end @@ -232,7 +290,7 @@ module util_axis_fifo_asym #( end assign m_axis_data = m_axis_data_int_s >> (m_axis_counter*A_WIDTH) ; - assign m_axis_tkeep = m_axis_tkeep_int_s >> (m_axis_counter*A_WIDTH/8) ; + assign m_axis_tkeep = m_axis_tkeep_int_s >> (m_axis_counter*A_WIDTH/8); //m_axis_tkeep is always high when TKEEP_EN == 0 // VALID/EMPTY/ALMOST_EMPTY is driven by the current atomic instance assign m_axis_valid_int = m_axis_valid_int_s >> m_axis_counter; @@ -249,13 +307,29 @@ module util_axis_fifo_asym #( assign m_axis_tlast = m_axis_tlast_int_s >> m_axis_counter; - // the FIFO has the same level as the last atomic instance - // (NOTE: this is not the real level value, rather the value will be updated - // after every RATIO number of reads) - assign m_axis_level = {m_axis_level_int_s[A_ADDRESS-1:0], {$clog2(RATIO){1'b0}}}; + // FIFO level behavior relies on the TKEEP_EN parameter + // When TKEEP_EN == 0, the FIFO has the same level as + // the last atomic instance multiplied by RATIO. The + // value of the level is only updated every RATIO reads. + // m_axis_level == {m_axis_level_int_s[A_ADDRESS-1:0], {$clog2(RATIO){1'b0}}} + // + // When TKEEP_EN == 1, it is using a counter whose increment + // relies on the tkeep. + // m_axis_level == m_output_level + assign m_axis_level = (TKEEP_EN) ? m_output_level : {m_axis_level_int_s[A_ADDRESS-1:0], {$clog2(RATIO){1'b0}}}; assign m_axis_almost_empty = m_axis_almost_empty_int_s[RATIO-1]; assign m_axis_empty = m_axis_empty_int_s[RATIO-1]; + sync_data #( + .NUM_OF_BITS (ADDRESS_WIDTH), + .ASYNC_CLK (ASYNC_CLK) + ) i_sdi_level_sync ( + .in_clk (s_axis_aclk), + .in_data (s_input_level), + .out_clk (m_axis_aclk), + .out_data (m_output_level) + ); + end else begin : big_master for (i=0; i 1) begin if (RATIO_TYPE) begin