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()}