From 31bac4e21f10f63d81e353a566fb04b5a88ef4f3 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:56:33 -0700 Subject: [PATCH 01/11] Reorganize FIFO adapter wrappers Signed-off-by: Alex Forencich --- rtl/axis_async_fifo_adapter.v | 161 +++++++++++++++------------------- rtl/axis_fifo_adapter.v | 160 +++++++++++++++------------------ 2 files changed, 144 insertions(+), 177 deletions(-) diff --git a/rtl/axis_async_fifo_adapter.v b/rtl/axis_async_fifo_adapter.v index 8bb69d2bc..ab373a42d 100644 --- a/rtl/axis_async_fifo_adapter.v +++ b/rtl/axis_async_fifo_adapter.v @@ -132,32 +132,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 +182,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 +223,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,47 +234,8 @@ end else begin assign pre_fifo_axis_tdest = s_axis_tdest; assign pre_fifo_axis_tuser = s_axis_tuser; - axis_adapter #( - .S_DATA_WIDTH(S_DATA_WIDTH), - .S_KEEP_ENABLE(S_KEEP_ENABLE), - .S_KEEP_WIDTH(S_KEEP_WIDTH), - .M_DATA_WIDTH(M_DATA_WIDTH), - .M_KEEP_ENABLE(M_KEEP_ENABLE), - .M_KEEP_WIDTH(M_KEEP_WIDTH), - .ID_ENABLE(ID_ENABLE), - .ID_WIDTH(ID_WIDTH), - .DEST_ENABLE(DEST_ENABLE), - .DEST_WIDTH(DEST_WIDTH), - .USER_ENABLE(USER_ENABLE), - .USER_WIDTH(USER_WIDTH) - ) - adapter_inst ( - .clk(m_clk), - .rst(m_rst), - // AXI input - .s_axis_tdata(post_fifo_axis_tdata), - .s_axis_tkeep(post_fifo_axis_tkeep), - .s_axis_tvalid(post_fifo_axis_tvalid), - .s_axis_tready(post_fifo_axis_tready), - .s_axis_tlast(post_fifo_axis_tlast), - .s_axis_tid(post_fifo_axis_tid), - .s_axis_tdest(post_fifo_axis_tdest), - .s_axis_tuser(post_fifo_axis_tuser), - // AXI output - .m_axis_tdata(m_axis_tdata), - .m_axis_tkeep(m_axis_tkeep), - .m_axis_tvalid(m_axis_tvalid), - .m_axis_tready(m_axis_tready), - .m_axis_tlast(m_axis_tlast), - .m_axis_tid(m_axis_tid), - .m_axis_tdest(m_axis_tdest), - .m_axis_tuser(m_axis_tuser) - ); - end -endgenerate - axis_async_fifo #( .DEPTH(DEPTH), .DATA_WIDTH(DATA_WIDTH), @@ -366,6 +293,62 @@ fifo_inst ( .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), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(m_clk), + .rst(m_rst), + // AXI input + .s_axis_tdata(post_fifo_axis_tdata), + .s_axis_tkeep(post_fifo_axis_tkeep), + .s_axis_tvalid(post_fifo_axis_tvalid), + .s_axis_tready(post_fifo_axis_tready), + .s_axis_tlast(post_fifo_axis_tlast), + .s_axis_tid(post_fifo_axis_tid), + .s_axis_tdest(post_fifo_axis_tdest), + .s_axis_tuser(post_fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .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 + endmodule `resetall diff --git a/rtl/axis_fifo_adapter.v b/rtl/axis_fifo_adapter.v index 4fa6776ae..9cfeaa3cd 100644 --- a/rtl/axis_fifo_adapter.v +++ b/rtl/axis_fifo_adapter.v @@ -126,32 +126,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 +176,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 +217,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,47 +228,8 @@ end else begin assign pre_fifo_axis_tdest = s_axis_tdest; assign pre_fifo_axis_tuser = s_axis_tuser; - axis_adapter #( - .S_DATA_WIDTH(S_DATA_WIDTH), - .S_KEEP_ENABLE(S_KEEP_ENABLE), - .S_KEEP_WIDTH(S_KEEP_WIDTH), - .M_DATA_WIDTH(M_DATA_WIDTH), - .M_KEEP_ENABLE(M_KEEP_ENABLE), - .M_KEEP_WIDTH(M_KEEP_WIDTH), - .ID_ENABLE(ID_ENABLE), - .ID_WIDTH(ID_WIDTH), - .DEST_ENABLE(DEST_ENABLE), - .DEST_WIDTH(DEST_WIDTH), - .USER_ENABLE(USER_ENABLE), - .USER_WIDTH(USER_WIDTH) - ) - adapter_inst ( - .clk(clk), - .rst(rst), - // AXI input - .s_axis_tdata(post_fifo_axis_tdata), - .s_axis_tkeep(post_fifo_axis_tkeep), - .s_axis_tvalid(post_fifo_axis_tvalid), - .s_axis_tready(post_fifo_axis_tready), - .s_axis_tlast(post_fifo_axis_tlast), - .s_axis_tid(post_fifo_axis_tid), - .s_axis_tdest(post_fifo_axis_tdest), - .s_axis_tuser(post_fifo_axis_tuser), - // AXI output - .m_axis_tdata(m_axis_tdata), - .m_axis_tkeep(m_axis_tkeep), - .m_axis_tvalid(m_axis_tvalid), - .m_axis_tready(m_axis_tready), - .m_axis_tlast(m_axis_tlast), - .m_axis_tid(m_axis_tid), - .m_axis_tdest(m_axis_tdest), - .m_axis_tuser(m_axis_tuser) - ); - end -endgenerate - axis_fifo #( .DEPTH(DEPTH), .DATA_WIDTH(DATA_WIDTH), @@ -352,6 +280,62 @@ fifo_inst ( .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), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(post_fifo_axis_tdata), + .s_axis_tkeep(post_fifo_axis_tkeep), + .s_axis_tvalid(post_fifo_axis_tvalid), + .s_axis_tready(post_fifo_axis_tready), + .s_axis_tlast(post_fifo_axis_tlast), + .s_axis_tid(post_fifo_axis_tid), + .s_axis_tdest(post_fifo_axis_tdest), + .s_axis_tuser(post_fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .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 + endmodule `resetall From e308c9559ac3f3f5386ce5fc5273ce3601561a7e Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:56:54 -0700 Subject: [PATCH 02/11] Rewrite width converter to reduce resource consumption Signed-off-by: Alex Forencich --- rtl/axis_adapter.v | 581 ++++++++++++++------------------------------- 1 file changed, 174 insertions(+), 407 deletions(-) diff --git a/rtl/axis_adapter.v b/rtl/axis_adapter.v index 288fa16c6..0dedfac0f 100644 --- a/rtl/axis_adapter.v +++ b/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 From 62c2148c8f33a8030a8c834f96abc1e8ff3fe6b2 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:57:16 -0700 Subject: [PATCH 03/11] Add pause functionality to FIFO modules Signed-off-by: Alex Forencich --- rtl/axis_async_fifo.v | 151 +++++++++++++++--- rtl/axis_async_fifo_adapter.v | 23 ++- rtl/axis_fifo.v | 124 +++++++++++--- rtl/axis_fifo_adapter.v | 19 ++- syn/vivado/axis_async_fifo.tcl | 17 ++ tb/axis_async_fifo/Makefile | 2 + tb/axis_async_fifo/test_axis_async_fifo.py | 56 +++++++ tb/axis_async_fifo_adapter/Makefile | 2 + .../test_axis_async_fifo_adapter.py | 56 +++++++ tb/axis_fifo/Makefile | 2 + tb/axis_fifo/test_axis_fifo.py | 43 +++++ tb/axis_fifo_adapter/Makefile | 2 + .../test_axis_fifo_adapter.py | 43 +++++ 13 files changed, 492 insertions(+), 48 deletions(-) diff --git a/rtl/axis_async_fifo.v b/rtl/axis_async_fifo.v index b56af5b02..a02ab565b 100644 --- a/rtl/axis_async_fifo.v +++ b/rtl/axis_async_fifo.v @@ -80,7 +80,11 @@ 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, + // Enable pause request input + parameter PAUSE_ENABLE = 0, + // Pause between frames + parameter FRAME_PAUSE = FRAME_FIFO ) ( /* @@ -111,6 +115,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 */ @@ -291,6 +303,7 @@ 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 +313,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; @@ -574,14 +597,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 +612,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 +627,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 +685,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 +728,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 +753,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 +773,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/rtl/axis_async_fifo_adapter.v b/rtl/axis_async_fifo_adapter.v index ab373a42d..291fd9212 100644 --- a/rtl/axis_async_fifo_adapter.v +++ b/rtl/axis_async_fifo_adapter.v @@ -85,7 +85,11 @@ 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, + // Enable pause request input + parameter PAUSE_ENABLE = 0, + // Pause between frames + parameter FRAME_PAUSE = FRAME_FIFO ) ( /* @@ -116,6 +120,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 */ @@ -255,7 +267,9 @@ axis_async_fifo #( .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) + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .PAUSE_ENABLE(PAUSE_ENABLE), + .FRAME_PAUSE(FRAME_PAUSE) ) fifo_inst ( // AXI input @@ -280,6 +294,11 @@ fifo_inst ( .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), diff --git a/rtl/axis_fifo.v b/rtl/axis_fifo.v index a0f2df395..e91059a6d 100644 --- a/rtl/axis_fifo.v +++ b/rtl/axis_fifo.v @@ -80,7 +80,11 @@ 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, + // Enable pause request input + parameter PAUSE_ENABLE = 0, + // Pause between frames + parameter FRAME_PAUSE = FRAME_FIFO ) ( input wire clk, @@ -110,6 +114,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 */ @@ -201,6 +211,7 @@ 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 +221,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; @@ -288,13 +309,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 +323,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 +346,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 +389,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 +414,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 +434,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/rtl/axis_fifo_adapter.v b/rtl/axis_fifo_adapter.v index 9cfeaa3cd..7054cd266 100644 --- a/rtl/axis_fifo_adapter.v +++ b/rtl/axis_fifo_adapter.v @@ -85,7 +85,11 @@ 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, + // Enable pause request input + parameter PAUSE_ENABLE = 0, + // Pause between frames + parameter FRAME_PAUSE = FRAME_FIFO ) ( input wire clk, @@ -115,6 +119,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 */ @@ -249,7 +259,9 @@ axis_fifo #( .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) + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .PAUSE_ENABLE(PAUSE_ENABLE), + .FRAME_PAUSE(FRAME_PAUSE) ) fifo_inst ( .clk(clk), @@ -272,6 +284,9 @@ fifo_inst ( .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), diff --git a/syn/vivado/axis_async_fifo.tcl b/syn/vivado/axis_async_fifo.tcl index 88218ccb9..5654295e0 100644 --- a/syn/vivado/axis_async_fifo.tcl +++ b/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/tb/axis_async_fifo/Makefile b/tb/axis_async_fifo/Makefile index 70c29fd77..a6c9149cb 100644 --- a/tb/axis_async_fifo/Makefile +++ b/tb/axis_async_fifo/Makefile @@ -51,6 +51,8 @@ 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_PAUSE_ENABLE := 1 +export PARAM_FRAME_PAUSE := 1 ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index c4f13e5f1..60e3422a0 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/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()) @@ -393,6 +396,56 @@ 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) @@ -501,6 +554,7 @@ if cocotb.SIM_NAME: run_test_shift_in_sink_reset, run_test_shift_out_source_reset, run_test_shift_out_sink_reset, + run_test_pause, run_test_overflow ]: @@ -557,6 +611,8 @@ 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['PAUSE_ENABLE'] = 1 + parameters['FRAME_PAUSE'] = 1 extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} diff --git a/tb/axis_async_fifo_adapter/Makefile b/tb/axis_async_fifo_adapter/Makefile index 655c2d195..f3006e350 100644 --- a/tb/axis_async_fifo_adapter/Makefile +++ b/tb/axis_async_fifo_adapter/Makefile @@ -55,6 +55,8 @@ 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_PAUSE_ENABLE := 1 +export PARAM_FRAME_PAUSE := 1 ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index 0e62fcbd8..3b50e00d2 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/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()) @@ -390,6 +393,56 @@ 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) @@ -498,6 +551,7 @@ if cocotb.SIM_NAME: run_test_shift_in_sink_reset, run_test_shift_out_source_reset, run_test_shift_out_sink_reset, + run_test_pause, run_test_overflow ]: @@ -554,6 +608,8 @@ 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['PAUSE_ENABLE'] = 1 + parameters['FRAME_PAUSE'] = 1 extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} diff --git a/tb/axis_fifo/Makefile b/tb/axis_fifo/Makefile index d22bb2aa8..ecbaf9601 100644 --- a/tb/axis_fifo/Makefile +++ b/tb/axis_fifo/Makefile @@ -51,6 +51,8 @@ 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_PAUSE_ENABLE := 1 +export PARAM_FRAME_PAUSE := 1 ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index 5a5fc6fd7..ef9f099fc 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/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()) @@ -192,6 +194,44 @@ 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) @@ -294,6 +334,7 @@ if cocotb.SIM_NAME: run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, + run_test_pause, run_test_overflow ]: @@ -349,6 +390,8 @@ 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['PAUSE_ENABLE'] = 1 + parameters['FRAME_PAUSE'] = 1 extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} diff --git a/tb/axis_fifo_adapter/Makefile b/tb/axis_fifo_adapter/Makefile index fee2a01bb..3493ed10e 100644 --- a/tb/axis_fifo_adapter/Makefile +++ b/tb/axis_fifo_adapter/Makefile @@ -55,6 +55,8 @@ 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_PAUSE_ENABLE := 1 +export PARAM_FRAME_PAUSE := 1 ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index b22310634..8363ec332 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/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()) @@ -192,6 +194,44 @@ 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) @@ -294,6 +334,7 @@ if cocotb.SIM_NAME: run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, + run_test_pause, run_test_overflow ]: @@ -350,6 +391,8 @@ 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['PAUSE_ENABLE'] = 1 + parameters['FRAME_PAUSE'] = 1 extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} From ac2c0fdac80a8ff75d806e93d262335ac2de2549 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:57:30 -0700 Subject: [PATCH 04/11] Read configuration directly from DUT Signed-off-by: Alex Forencich --- tb/axis_async_fifo/test_axis_async_fifo.py | 6 +++--- tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py | 6 +++--- tb/axis_fifo/test_axis_fifo.py | 4 ++-- tb/axis_fifo_adapter/test_axis_fifo_adapter.py | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index 60e3422a0..1b00e51f3 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/tb/axis_async_fifo/test_axis_async_fifo.py @@ -152,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) @@ -302,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() @@ -463,7 +463,7 @@ async def run_test_overflow(dut): tb.sink.pause = False - if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")): + if dut.DROP_OVERSIZE_FRAME.value: for k in range(2048): await RisingEdge(dut.s_clk) diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index 3b50e00d2..a62319c16 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -149,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) @@ -299,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() @@ -460,7 +460,7 @@ async def run_test_overflow(dut): tb.sink.pause = False - if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")): + if dut.DROP_OVERSIZE_FRAME.value: for k in range(2048): await RisingEdge(dut.s_clk) diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index ef9f099fc..9314c5248 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/tb/axis_fifo/test_axis_fifo.py @@ -122,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) @@ -249,7 +249,7 @@ async def run_test_overflow(dut): tb.sink.pause = False - if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")): + if dut.DROP_OVERSIZE_FRAME.value: for k in range(2048): await RisingEdge(dut.clk) diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index 8363ec332..f072a6211 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -122,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) @@ -249,7 +249,7 @@ async def run_test_overflow(dut): tb.sink.pause = False - if int(os.getenv("PARAM_DROP_OVERSIZE_FRAME")): + if dut.DROP_OVERSIZE_FRAME.value: for k in range(2048): await RisingEdge(dut.clk) From 7febd080c9ff1e40b135f53e87a1eb3cbbd34292 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:58:22 -0700 Subject: [PATCH 05/11] Use FIFO depth in overflow test Signed-off-by: Alex Forencich --- tb/axis_async_fifo/test_axis_async_fifo.py | 9 ++++++--- .../test_axis_async_fifo_adapter.py | 9 ++++++--- tb/axis_fifo/test_axis_fifo.py | 9 ++++++--- tb/axis_fifo_adapter/test_axis_fifo_adapter.py | 9 ++++++--- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index 1b00e51f3..4d847e0ce 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/tb/axis_async_fifo/test_axis_async_fifo.py @@ -450,21 +450,24 @@ 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)) + 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 dut.DROP_OVERSIZE_FRAME.value: - for k in range(2048): + for k in range((depth//byte_lanes)*2): await RisingEdge(dut.s_clk) else: diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index a62319c16..a93f4bb91 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -447,21 +447,24 @@ 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)) + 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 dut.DROP_OVERSIZE_FRAME.value: - for k in range(2048): + for k in range((depth//byte_lanes)*2): await RisingEdge(dut.s_clk) else: diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index 9314c5248..95d76be8b 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/tb/axis_fifo/test_axis_fifo.py @@ -236,21 +236,24 @@ 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)) + 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 dut.DROP_OVERSIZE_FRAME.value: - for k in range(2048): + for k in range((depth//byte_lanes)*2): await RisingEdge(dut.clk) else: diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index f072a6211..aaec4f30a 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -236,21 +236,24 @@ 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)) + 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 dut.DROP_OVERSIZE_FRAME.value: - for k in range(2048): + for k in range((depth//byte_lanes)*2): await RisingEdge(dut.clk) else: From 3a665f0ded9bc89090c0521c5997e51a0d6c2a5b Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:58:35 -0700 Subject: [PATCH 06/11] Compute DEPTH based on FIFO data width Signed-off-by: Alex Forencich --- tb/axis_async_fifo/Makefile | 6 +++--- tb/axis_async_fifo/test_axis_async_fifo.py | 2 +- tb/axis_async_fifo_adapter/Makefile | 10 +++++----- .../test_axis_async_fifo_adapter.py | 2 +- tb/axis_fifo/Makefile | 6 +++--- tb/axis_fifo/test_axis_fifo.py | 2 +- tb/axis_fifo_adapter/Makefile | 10 +++++----- tb/axis_fifo_adapter/test_axis_fifo_adapter.py | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tb/axis_async_fifo/Makefile b/tb/axis_async_fifo/Makefile index a6c9149cb..ef0aaef36 100644 --- a/tb/axis_async_fifo/Makefile +++ b/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 diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index 4d847e0ce..cf5448e64 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/tb/axis_async_fifo/test_axis_async_fifo.py @@ -595,10 +595,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 diff --git a/tb/axis_async_fifo_adapter/Makefile b/tb/axis_async_fifo_adapter/Makefile index f3006e350..fb757817a 100644 --- a/tb/axis_async_fifo_adapter/Makefile +++ b/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 diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index a93f4bb91..1a17e2808 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -590,13 +590,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 diff --git a/tb/axis_fifo/Makefile b/tb/axis_fifo/Makefile index ecbaf9601..41641c7be 100644 --- a/tb/axis_fifo/Makefile +++ b/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 diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index 95d76be8b..026b34f00 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/tb/axis_fifo/test_axis_fifo.py @@ -374,10 +374,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 diff --git a/tb/axis_fifo_adapter/Makefile b/tb/axis_fifo_adapter/Makefile index 3493ed10e..0a995cde7 100644 --- a/tb/axis_fifo_adapter/Makefile +++ b/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 diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index aaec4f30a..f82b61951 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -373,13 +373,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 From 330d6f41fc8622cb5c02923ef6baf28740b7f19d Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:59:14 -0700 Subject: [PATCH 07/11] Send more data in stress tests Signed-off-by: Alex Forencich --- tb/axis_async_fifo/test_axis_async_fifo.py | 2 +- tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py | 2 +- tb/axis_fifo/test_axis_fifo.py | 2 +- tb/axis_fifo_adapter/test_axis_fifo_adapter.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index cf5448e64..8952edb04 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/tb/axis_async_fifo/test_axis_async_fifo.py @@ -498,7 +498,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) diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index 1a17e2808..80d4157e1 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -495,7 +495,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) diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index 026b34f00..63d9f98a9 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/tb/axis_fifo/test_axis_fifo.py @@ -284,7 +284,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) diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index f82b61951..2178970a8 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -284,7 +284,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) From c4f298de6f15bd43bc00543712567cb6a21f8a24 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:59:30 -0700 Subject: [PATCH 08/11] Add overflow test, previous test is actually an oversize frame test Signed-off-by: Alex Forencich --- tb/axis_async_fifo/test_axis_async_fifo.py | 41 ++++++++++++++++++- .../test_axis_async_fifo_adapter.py | 41 ++++++++++++++++++- tb/axis_fifo/test_axis_fifo.py | 41 ++++++++++++++++++- .../test_axis_fifo_adapter.py | 41 ++++++++++++++++++- 4 files changed, 160 insertions(+), 4 deletions(-) diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index 8952edb04..86f51fb18 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/tb/axis_async_fifo/test_axis_async_fifo.py @@ -457,6 +457,44 @@ async def run_test_overflow(dut): tb.sink.pause = True + 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)*2): + await RisingEdge(dut.s_clk) + + assert not tb.source.idle() + + tb.sink.pause = False + + 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) @@ -558,7 +596,8 @@ if cocotb.SIM_NAME: run_test_shift_out_source_reset, run_test_shift_out_sink_reset, run_test_pause, - run_test_overflow + run_test_overflow, + run_test_oversize ]: factory = TestFactory(test) diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index 80d4157e1..d54af5fe0 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -454,6 +454,44 @@ async def run_test_overflow(dut): tb.sink.pause = True + 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)*2): + await RisingEdge(dut.s_clk) + + assert not tb.source.idle() + + tb.sink.pause = False + + 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) @@ -555,7 +593,8 @@ if cocotb.SIM_NAME: run_test_shift_out_source_reset, run_test_shift_out_sink_reset, run_test_pause, - run_test_overflow + run_test_overflow, + run_test_oversize ]: factory = TestFactory(test) diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index 63d9f98a9..843490be7 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/tb/axis_fifo/test_axis_fifo.py @@ -243,6 +243,44 @@ async def run_test_overflow(dut): tb.sink.pause = True + 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)*2): + await RisingEdge(dut.clk) + + assert not tb.source.idle() + + tb.sink.pause = False + + 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) @@ -338,7 +376,8 @@ if cocotb.SIM_NAME: run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_pause, - run_test_overflow + run_test_overflow, + run_test_oversize ]: factory = TestFactory(test) diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index 2178970a8..b6f611b2a 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -243,6 +243,44 @@ async def run_test_overflow(dut): tb.sink.pause = True + 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)*2): + await RisingEdge(dut.clk) + + assert not tb.source.idle() + + tb.sink.pause = False + + 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) @@ -338,7 +376,8 @@ if cocotb.SIM_NAME: run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_pause, - run_test_overflow + run_test_overflow, + run_test_oversize ]: factory = TestFactory(test) From c3cd676c5da3f3397c1ccdb559a5d1bbde74c4e6 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 16:59:57 -0700 Subject: [PATCH 09/11] Test DROP_WHEN_FULL parameter Signed-off-by: Alex Forencich --- tb/axis_async_fifo/test_axis_async_fifo.py | 63 ++++++++++++++---- .../test_axis_async_fifo_adapter.py | 63 ++++++++++++++---- tb/axis_fifo/test_axis_fifo.py | 65 +++++++++++++++---- .../test_axis_fifo_adapter.py | 63 ++++++++++++++---- 4 files changed, 202 insertions(+), 52 deletions(-) diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index 86f51fb18..d99969790 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/tb/axis_async_fifo/test_axis_async_fifo.py @@ -465,18 +465,36 @@ async def run_test_overflow(dut): for k in range(count): await tb.source.send(test_frame) - for k in range((depth//byte_lanes)*2): + for k in range((depth//byte_lanes)*3): await RisingEdge(dut.s_clk) - assert not tb.source.idle() + if dut.DROP_WHEN_FULL.value: + assert tb.source.idle() + else: + assert not tb.source.idle() tb.sink.pause = False - for k in range(count): - rx_frame = await tb.sink.recv() + if dut.DROP_WHEN_FULL.value: + for k in range((depth//byte_lanes)*3): + await RisingEdge(dut.s_clk) - assert rx_frame.tdata == test_data - assert not rx_frame.tuser + rx_count = 0 + + while not tb.sink.empty(): + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert not rx_frame.tuser + + 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() @@ -548,13 +566,32 @@ 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: + 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() + + while True: + test_frame = test_frames.pop(0) + if not rx_frame.tuser and 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() @@ -617,7 +654,7 @@ 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)]) + [(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 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]) diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index d54af5fe0..8533c685a 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -462,18 +462,36 @@ async def run_test_overflow(dut): for k in range(count): await tb.source.send(test_frame) - for k in range((depth//byte_lanes)*2): + for k in range((depth//byte_lanes)*3): await RisingEdge(dut.s_clk) - assert not tb.source.idle() + if dut.DROP_WHEN_FULL.value: + assert tb.source.idle() + else: + assert not tb.source.idle() tb.sink.pause = False - for k in range(count): - rx_frame = await tb.sink.recv() + if dut.DROP_WHEN_FULL.value: + for k in range((depth//byte_lanes)*3): + await RisingEdge(dut.s_clk) - assert rx_frame.tdata == test_data - assert not rx_frame.tuser + rx_count = 0 + + while not tb.sink.empty(): + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert not rx_frame.tuser + + 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() @@ -545,13 +563,32 @@ 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: + 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() + + while True: + test_frame = test_frames.pop(0) + if not rx_frame.tuser and 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() @@ -613,7 +650,7 @@ 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)]) + [(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 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): diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index 843490be7..7a4199481 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/tb/axis_fifo/test_axis_fifo.py @@ -251,18 +251,36 @@ async def run_test_overflow(dut): for k in range(count): await tb.source.send(test_frame) - for k in range((depth//byte_lanes)*2): + for k in range((depth//byte_lanes)*3): await RisingEdge(dut.clk) - assert not tb.source.idle() + if dut.DROP_WHEN_FULL.value: + assert tb.source.idle() + else: + assert not tb.source.idle() tb.sink.pause = False - for k in range(count): - rx_frame = await tb.sink.recv() + if dut.DROP_WHEN_FULL.value: + for k in range((depth//byte_lanes)*3): + await RisingEdge(dut.clk) - assert rx_frame.tdata == test_data - assert not rx_frame.tuser + rx_count = 0 + + while not tb.sink.empty(): + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert not rx_frame.tuser + + 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() @@ -334,13 +352,34 @@ 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: + 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() + + assert len(test_frames) > 0 + + while True: + test_frame = test_frames.pop(0) + if not rx_frame.tuser and 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() @@ -396,7 +435,7 @@ 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)]) + [(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 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]) diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index b6f611b2a..03c2a3440 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -251,18 +251,36 @@ async def run_test_overflow(dut): for k in range(count): await tb.source.send(test_frame) - for k in range((depth//byte_lanes)*2): + for k in range((depth//byte_lanes)*3): await RisingEdge(dut.clk) - assert not tb.source.idle() + if dut.DROP_WHEN_FULL.value: + assert tb.source.idle() + else: + assert not tb.source.idle() tb.sink.pause = False - for k in range(count): - rx_frame = await tb.sink.recv() + if dut.DROP_WHEN_FULL.value: + for k in range((depth//byte_lanes)*3): + await RisingEdge(dut.clk) - assert rx_frame.tdata == test_data - assert not rx_frame.tuser + rx_count = 0 + + while not tb.sink.empty(): + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert not rx_frame.tuser + + 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() @@ -334,13 +352,32 @@ 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: + 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() + + while True: + test_frame = test_frames.pop(0) + if not rx_frame.tuser and 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() @@ -396,7 +433,7 @@ 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)]) + [(0, 0, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 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): From 6020d092149c032c213558f190521eb5cc32da20 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 14 Aug 2023 18:55:02 -0700 Subject: [PATCH 10/11] Reorganize FIFO write logic Signed-off-by: Alex Forencich --- rtl/axis_async_fifo.v | 142 ++++++++++++++++++++++-------------------- rtl/axis_fifo.v | 78 ++++++++++++----------- 2 files changed, 116 insertions(+), 104 deletions(-) diff --git a/rtl/axis_async_fifo.v b/rtl/axis_async_fifo.v index a02ab565b..4d793d951 100644 --- a/rtl/axis_async_fifo.v +++ b/rtl/axis_async_fifo.v @@ -395,10 +395,80 @@ always @(posedge s_clk) begin end end - if (s_axis_tready && s_axis_tvalid) begin - // transfer in - if (!FRAME_FIFO) begin - // normal FIFO mode + 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_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 + 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 + // transfer in mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis; if (drop_frame_reg && LAST_ENABLE) begin // currently dropping frame @@ -414,70 +484,6 @@ always @(posedge s_clk) begin 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 - 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 diff --git a/rtl/axis_fifo.v b/rtl/axis_fifo.v index e91059a6d..f185d4088 100644 --- a/rtl/axis_fifo.v +++ b/rtl/axis_fifo.v @@ -245,46 +245,52 @@ 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 (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 + // 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 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 From 7823b916bfd298441d5fdabf0b03d0ae8dc210e8 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Wed, 16 Aug 2023 12:50:12 -0700 Subject: [PATCH 11/11] Implement MARK_WHEN_FULL option in FIFOs Signed-off-by: Alex Forencich --- rtl/axis_async_fifo.v | 58 +++++++++++++--- rtl/axis_async_fifo_adapter.v | 5 ++ rtl/axis_fifo.v | 67 +++++++++++++++++-- rtl/axis_fifo_adapter.v | 5 ++ tb/axis_async_fifo/Makefile | 1 + tb/axis_async_fifo/test_axis_async_fifo.py | 35 +++++++--- tb/axis_async_fifo_adapter/Makefile | 1 + .../test_axis_async_fifo_adapter.py | 36 +++++++--- tb/axis_fifo/Makefile | 1 + tb/axis_fifo/test_axis_fifo.py | 35 +++++++--- tb/axis_fifo_adapter/Makefile | 1 + .../test_axis_fifo_adapter.py | 36 +++++++--- 12 files changed, 232 insertions(+), 49 deletions(-) diff --git a/rtl/axis_async_fifo.v b/rtl/axis_async_fifo.v index 4d793d951..76eeb8d85 100644 --- a/rtl/axis_async_fifo.v +++ b/rtl/axis_async_fifo.v @@ -81,6 +81,10 @@ module axis_async_fifo # // When set, s_axis_tready is always asserted // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set 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 @@ -164,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; @@ -262,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; @@ -288,17 +303,17 @@ 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]; @@ -468,22 +483,48 @@ always @(posedge s_clk) begin end else begin // normal FIFO mode if (s_axis_tready && s_axis_tvalid) begin - // transfer in - 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 (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 - // update pointers + // 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_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); end end @@ -509,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; diff --git a/rtl/axis_async_fifo_adapter.v b/rtl/axis_async_fifo_adapter.v index 291fd9212..77969960a 100644 --- a/rtl/axis_async_fifo_adapter.v +++ b/rtl/axis_async_fifo_adapter.v @@ -86,6 +86,10 @@ module axis_async_fifo_adapter # // When set, s_axis_tready is always asserted // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set 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 @@ -268,6 +272,7 @@ axis_async_fifo #( .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) ) diff --git a/rtl/axis_fifo.v b/rtl/axis_fifo.v index f185d4088..e9c9258eb 100644 --- a/rtl/axis_fifo.v +++ b/rtl/axis_fifo.v @@ -81,6 +81,10 @@ module axis_fifo # // When set, s_axis_tready is always asserted // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set 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 @@ -156,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; @@ -188,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; @@ -196,17 +213,17 @@ 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]; @@ -245,6 +262,11 @@ always @(posedge clk) begin bad_frame_reg <= 1'b0; good_frame_reg <= 1'b0; + 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 @@ -286,7 +308,39 @@ always @(posedge clk) begin end else begin // normal FIFO mode if (s_axis_tready && s_axis_tvalid) begin - // transfer in + 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; @@ -297,7 +351,10 @@ always @(posedge clk) 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; diff --git a/rtl/axis_fifo_adapter.v b/rtl/axis_fifo_adapter.v index 7054cd266..44ecc5c66 100644 --- a/rtl/axis_fifo_adapter.v +++ b/rtl/axis_fifo_adapter.v @@ -86,6 +86,10 @@ module axis_fifo_adapter # // When set, s_axis_tready is always asserted // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set 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 @@ -260,6 +264,7 @@ axis_fifo #( .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) ) diff --git a/tb/axis_async_fifo/Makefile b/tb/axis_async_fifo/Makefile index ef0aaef36..10ffd633f 100644 --- a/tb/axis_async_fifo/Makefile +++ b/tb/axis_async_fifo/Makefile @@ -51,6 +51,7 @@ 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 diff --git a/tb/axis_async_fifo/test_axis_async_fifo.py b/tb/axis_async_fifo/test_axis_async_fifo.py index d99969790..4b126a793 100644 --- a/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/tb/axis_async_fifo/test_axis_async_fifo.py @@ -468,14 +468,14 @@ async def run_test_overflow(dut): for k in range((depth//byte_lanes)*3): await RisingEdge(dut.s_clk) - if dut.DROP_WHEN_FULL.value: + 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: + 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) @@ -484,9 +484,14 @@ async def run_test_overflow(dut): 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: @@ -529,8 +534,11 @@ async def run_test_oversize(dut): 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() @@ -566,7 +574,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): cur_id = (cur_id + 1) % id_count - if dut.DROP_WHEN_FULL.value: + if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value: cycles = 0 while cycles < 100: cycles += 1 @@ -577,9 +585,14 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): 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 not rx_frame.tuser and rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata: + 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 @@ -653,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), (1, 1, 1, 1)]) +@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] @@ -690,6 +706,7 @@ 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 diff --git a/tb/axis_async_fifo_adapter/Makefile b/tb/axis_async_fifo_adapter/Makefile index fb757817a..e70946194 100644 --- a/tb/axis_async_fifo_adapter/Makefile +++ b/tb/axis_async_fifo_adapter/Makefile @@ -55,6 +55,7 @@ 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 diff --git a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index 8533c685a..5d09b0703 100644 --- a/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -465,14 +465,14 @@ async def run_test_overflow(dut): for k in range((depth//byte_lanes)*3): await RisingEdge(dut.s_clk) - if dut.DROP_WHEN_FULL.value: + 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: + 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) @@ -481,9 +481,14 @@ async def run_test_overflow(dut): 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: @@ -526,8 +531,11 @@ async def run_test_oversize(dut): 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() @@ -563,7 +571,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): cur_id = (cur_id + 1) % id_count - if dut.DROP_WHEN_FULL.value: + if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value: cycles = 0 while cycles < 100: cycles += 1 @@ -574,9 +582,14 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): 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 not rx_frame.tuser and rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata: + 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 @@ -649,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), (1, 1, 1, 1)]) +@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 @@ -687,6 +704,7 @@ 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 diff --git a/tb/axis_fifo/Makefile b/tb/axis_fifo/Makefile index 41641c7be..c10508ee8 100644 --- a/tb/axis_fifo/Makefile +++ b/tb/axis_fifo/Makefile @@ -51,6 +51,7 @@ 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 diff --git a/tb/axis_fifo/test_axis_fifo.py b/tb/axis_fifo/test_axis_fifo.py index 7a4199481..e89c0a5ff 100644 --- a/tb/axis_fifo/test_axis_fifo.py +++ b/tb/axis_fifo/test_axis_fifo.py @@ -254,14 +254,14 @@ async def run_test_overflow(dut): for k in range((depth//byte_lanes)*3): await RisingEdge(dut.clk) - if dut.DROP_WHEN_FULL.value: + 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: + if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value: for k in range((depth//byte_lanes)*3): await RisingEdge(dut.clk) @@ -270,9 +270,14 @@ async def run_test_overflow(dut): 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: @@ -315,8 +320,11 @@ async def run_test_oversize(dut): 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() @@ -352,7 +360,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): cur_id = (cur_id + 1) % id_count - if dut.DROP_WHEN_FULL.value: + if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value: cycles = 0 while cycles < 100: cycles += 1 @@ -363,11 +371,16 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): 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 not rx_frame.tuser and rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata: + 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 @@ -434,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), (1, 1, 1, 1)]) +@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] @@ -471,6 +487,7 @@ 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 diff --git a/tb/axis_fifo_adapter/Makefile b/tb/axis_fifo_adapter/Makefile index 0a995cde7..65234e80b 100644 --- a/tb/axis_fifo_adapter/Makefile +++ b/tb/axis_fifo_adapter/Makefile @@ -55,6 +55,7 @@ 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 diff --git a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index 03c2a3440..5b4b992ad 100644 --- a/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -254,14 +254,14 @@ async def run_test_overflow(dut): for k in range((depth//byte_lanes)*3): await RisingEdge(dut.clk) - if dut.DROP_WHEN_FULL.value: + 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: + if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value: for k in range((depth//byte_lanes)*3): await RisingEdge(dut.clk) @@ -270,9 +270,14 @@ async def run_test_overflow(dut): 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: @@ -315,8 +320,11 @@ async def run_test_oversize(dut): 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() @@ -352,7 +360,7 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): cur_id = (cur_id + 1) % id_count - if dut.DROP_WHEN_FULL.value: + if dut.DROP_WHEN_FULL.value or dut.MARK_WHEN_FULL.value: cycles = 0 while cycles < 100: cycles += 1 @@ -363,9 +371,14 @@ async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): 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 not rx_frame.tuser and rx_frame.tid == test_frame.tid and rx_frame.tdest == test_frame.tdest and rx_frame.tdata == test_frame.tdata: + 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 @@ -432,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), (1, 1, 1, 1)]) +@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 @@ -470,6 +487,7 @@ 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