From 0e2e11c92d9c3841977e9bf5ec9dba5e825e375d 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 e49132e4bc289c712f6a5f07dd8cecfb4a17f14e 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 05c41f08b5becc3a9539a026c69b2a2ea276a622 Mon Sep 17 00:00:00 2001 From: Laez Barbosa Date: Wed, 24 Sep 2025 16:00:18 -0300 Subject: [PATCH 3/3] review: typo Signed-off-by: Laez Barbosa --- library/util_axis_fifo_asym/util_axis_fifo_asym.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..ef661b504b 100644 --- a/library/util_axis_fifo_asym/util_axis_fifo_asym.v +++ b/library/util_axis_fifo_asym/util_axis_fifo_asym.v @@ -155,7 +155,7 @@ module util_axis_fifo_asym #( // 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 least significant 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