1// 2// Copyright 2018-2019 Ettus Research, A National Instruments Company 3// 4// SPDX-License-Identifier: LGPL-3.0-or-later 5// 6// Module: axis_packet_flush 7// Description: 8// When this module is inserted in an AXI-Stream link, it allows 9// the client to flip a bit to make the stream lossy. When enable=1 10// all data coming through the input is dropped. This module can 11// start and stop flushing at packet boundaries to ensure no partial 12// packets are introduces into the stream. Set FLUSH_PARTIAL_PKTS = 1 13// to disable that behavior. An optional timeout can be set to 14// determine if flushing was done (without turning it off). 15// 16// Parameters: 17// - WIDTH: The bitwidth of the AXI-Stream bus 18// - TIMEOUT_W: Width of the timeout counter 19// - FLUSH_PARTIAL_PKTS: Start flusing immediately even if a packet is in flight 20// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT} 21// 22// Signals: 23// - s_axis_* : Input AXI-Stream 24// - m_axis_* : Output AXI-Stream 25// - enable : Enable flush mode 26// - timeout : Flush timeout (# of cycles of inactivity until done) 27// - flushing : The module is currently flushing 28// - done : Finished flushing (but is still active) 29 30module axis_packet_flush #( 31 parameter WIDTH = 64, 32 parameter TIMEOUT_W = 32, 33 parameter FLUSH_PARTIAL_PKTS = 0, 34 parameter PIPELINE = "NONE" 35)( 36 // Clock and reset 37 input wire clk, 38 input wire reset, 39 // Control and status 40 input wire enable, 41 input wire [TIMEOUT_W-1:0] timeout, 42 output wire flushing, 43 output reg done = 1'b0, 44 // Input stream 45 input wire [WIDTH-1:0] s_axis_tdata, 46 input wire s_axis_tlast, 47 input wire s_axis_tvalid, 48 output wire s_axis_tready, 49 // Output stream 50 output wire [WIDTH-1:0] m_axis_tdata, 51 output wire m_axis_tlast, 52 output wire m_axis_tvalid, 53 input wire m_axis_tready 54); 55 56 //---------------------------------------------- 57 // Pipeline Logic 58 //---------------------------------------------- 59 60 wire [WIDTH-1:0] i_pipe_tdata, o_pipe_tdata; 61 wire i_pipe_tlast, o_pipe_tlast; 62 wire i_pipe_tvalid, o_pipe_tvalid; 63 wire i_pipe_tready, o_pipe_tready; 64 65 generate 66 if (PIPELINE == "IN" || PIPELINE == "INOUT") begin 67 axi_fifo_flop2 #(.WIDTH(WIDTH+1)) in_pipe_i ( 68 .clk(clk), .reset(reset), .clear(1'b0), 69 .i_tdata({s_axis_tlast, s_axis_tdata}), .i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready), 70 .o_tdata({i_pipe_tlast, i_pipe_tdata}), .o_tvalid(i_pipe_tvalid), .o_tready(i_pipe_tready), 71 .space(), .occupied() 72 ); 73 end else begin 74 assign {i_pipe_tlast, i_pipe_tdata, i_pipe_tvalid} = {s_axis_tlast, s_axis_tdata, s_axis_tvalid}; 75 assign s_axis_tready = i_pipe_tready; 76 end 77 78 if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin 79 axi_fifo_flop2 #(.WIDTH(WIDTH+1)) out_pipe_i ( 80 .clk(clk), .reset(reset), .clear(1'b0), 81 .i_tdata({o_pipe_tlast, o_pipe_tdata}), .i_tvalid(o_pipe_tvalid), .i_tready(o_pipe_tready), 82 .o_tdata({m_axis_tlast, m_axis_tdata}), .o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready), 83 .space(), .occupied() 84 ); 85 end else begin 86 assign {m_axis_tlast, m_axis_tdata, m_axis_tvalid} = {o_pipe_tlast, o_pipe_tdata, o_pipe_tvalid}; 87 assign o_pipe_tready = m_axis_tready; 88 end 89 endgenerate 90 91 //---------------------------------------------- 92 // Flushing Logic 93 //---------------------------------------------- 94 95 // Shortcuts 96 wire xfer_stb = i_pipe_tvalid & i_pipe_tready; 97 wire pkt_stb = xfer_stb & i_pipe_tlast; 98 99 // Packet boundary detector 100 reg mid_pkt = 1'b0; 101 always @(posedge clk) begin 102 if (reset) begin 103 mid_pkt <= 1'b0; 104 end else if (xfer_stb) begin 105 mid_pkt <= ~pkt_stb; 106 end 107 end 108 109 // Flush startup state machine 110 reg active = 1'b0; 111 always @(posedge clk) begin 112 if (reset) begin 113 active <= 1'b0; 114 end else begin 115 if (enable & (pkt_stb | (~mid_pkt & ~xfer_stb))) begin 116 active <= 1'b1; 117 end else if (~enable) begin 118 active <= 1'b0; 119 end 120 end 121 end 122 assign flushing = (FLUSH_PARTIAL_PKTS == 0) ? active : enable; 123 124 // Flush done detector based on timeout 125 reg [TIMEOUT_W-1:0] cyc_to_go = {TIMEOUT_W{1'b1}}; 126 wire done_tmp = (cyc_to_go == {TIMEOUT_W{1'b0}}); 127 always @(posedge clk) begin 128 if (reset | ~enable) begin 129 cyc_to_go <= {TIMEOUT_W{1'b1}}; 130 done <= 1'b0; 131 end else if (enable & ~active) begin 132 cyc_to_go <= timeout; 133 end else begin 134 if (~done_tmp) begin 135 cyc_to_go <= xfer_stb ? timeout : (cyc_to_go - 1'b1); 136 end 137 done <= done_tmp; 138 end 139 end 140 141 // When flushing, drop all input data and quiet output data 142 // When no flushing, pass data without interruption 143 assign o_pipe_tdata = i_pipe_tdata; 144 assign o_pipe_tlast = i_pipe_tlast; 145 assign o_pipe_tvalid = flushing ? 1'b0 : i_pipe_tvalid; 146 assign i_pipe_tready = flushing ? 1'b1 : o_pipe_tready; 147 148endmodule