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