aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/axi/axis_packetize.v
blob: bb1297e5ccb3d7b5640dc525cdf87c905c18d6b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//
// Copyright 2020 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_packetize
//
// Description:
//
//   This module takes in an axi_stream without packet boundaries (i.e.,
//   without tlast) and groups the data into packets by adding tlast at the
//   appropriate time. The size of the packet is controlled by the "size"
//   input, which is sampled at the beginning of each packet to be output. The
//   packet_size input indicates the number of i_tdata words to group into a
//   packet. The i_tlength output indicates the length of the packet being
//   output.
//
//   The gate input causes data transfers to be stopped at the end of the
//   current packet when the gate input is asserted. It is not legal to
//   deassert tvalid once it has been asserted until the next transfer is
//   completed, so this module monitors the state of the AXI-Stream protocol
//   so that no protocol violations occur.
//
//   Note that the current transfer may still complete after gate has been
//   asserted, so the downstream logic must be able to account for at least
//   one more transfer.
//
// Parameters:
//
//   DATA_W       : Width of the tdata signals.
//   SIZE_W       : The width of the packet size port. This dictates the
//                  maximum packet size.
//   FLUSH        : Controls whether or not the input should be stalled or
//                  flushed. That is, when FLUSH=0, the input data is stalled
//                  whenever the gate is on (i_tready becomes 0). When
//                  FLUSH=1, the input data is dropped whenever the gate is on
//                  (i_tready becomes 1).
//   DEFAULT_SIZE : The default packet size to use, if it doesn't need to be
//                  changed at run time.
//


module axis_packetize #(
  parameter DATA_W       = 32,
  parameter SIZE_W       = 16,
  parameter FLUSH        = 0,
  parameter DEFAULT_SIZE = 2**SIZE_W-1
) (
  input wire clk,
  input wire rst,

  input wire              gate,       // Stop or "gate" packet output
  input wire [SIZE_W-1:0] size,       // Size to use for the next packet

  // Input data stream
  input  wire [DATA_W-1:0] i_tdata,
  input  wire              i_tvalid,
  output wire              i_tready,

  // Output data stream
  output wire [DATA_W-1:0] o_tdata,
  output reg               o_tlast,
  output wire              o_tvalid,
  input  wire              o_tready,
  output wire [SIZE_W-1:0] o_tuser     // Current packet's size
);

  reg              start_of_packet = 1;    // Next sample is start of a packet
  reg [SIZE_W-1:0] word_count      = 0;    // Count of output words
  reg [SIZE_W-1:0] current_size    = DEFAULT_SIZE;  // Current packet size

  reg gating     = 1'b0;     // Indicate if output is blocked
  reg mid_packet = 1'b0;     // Indicate if we're in the middle of a packet

  //---------------------------------------------------------------------------
  // Packet Size Logic
  //---------------------------------------------------------------------------

  assign o_tuser = current_size;

  always @(posedge clk) begin
    if (rst) begin
      start_of_packet <= 1'b1;
      current_size    <= DEFAULT_SIZE;
      word_count      <= 0;
      o_tlast         <= (DEFAULT_SIZE == 1);
    end else begin
      if (gating) begin
        // Wait until we're enabled. Setup for the start of the next packet.
        start_of_packet <= 1'b1;
        current_size    <= size;
        word_count      <= size;
        o_tlast         <= (size == 1);
      end else if (o_tvalid && o_tready) begin
        start_of_packet <= 1'b0;
        word_count      <= word_count - 1;
        if (o_tlast) begin
          // This is the last sample, so restart everything for a new packet.
          start_of_packet <= 1'b1;
          current_size    <= size;
          word_count      <= size;
          o_tlast         <= (size == 1);
        end else if (word_count == 2) begin
          // This is the second to last sample, so we assert tlast for the
          // last sample.
          o_tlast <= 1'b1;
        end
      end else if (start_of_packet) begin
        // We're waiting for the start of the next packet. Keep checking the
        // size input until the next packet starts.
        current_size <= size;
        word_count   <= size;
        o_tlast      <= (size == 1);
      end
    end
  end

  //---------------------------------------------------------------------------
  // Handshake Monitor
  //---------------------------------------------------------------------------
  
  // Monitor the state of the handshake so we know when it's OK to
  // enable/disable data transfer.
  always @(posedge clk) begin
    if (rst) begin
      gating     = 1'b0;
      mid_packet = 1'b0;
    end else begin
      // Keep track of if we are in the middle of a packet or not. Note that
      // mid_packet will be 0 for the first transfer of a packet.
      if (o_tvalid && o_tready) begin
        if (o_tlast) begin
          mid_packet = 1'b0;
        end else begin
          mid_packet = 1'b1;
        end
      end

      if (gating) begin
        // We can stop gating any time
        if (!gate) gating <= 0;
      end else begin
        // Only start gating between packets when the output is idle, or after
        // the output transfer completes at the end of packet.
        if ((!mid_packet && !o_tvalid) || (o_tvalid && o_tready && o_tlast)) begin
          gating <= gate;
        end
      end
    end
  end

  //---------------------------------------------------------------------------
  // Data Pass-Through
  //---------------------------------------------------------------------------

  // Note that "gating" only asserts when a transfer completes at the end of a
  // packet, or between packets when the output is idle. This ensures that
  // o_tvalid won't deassert during a transfer and cause a handshake protocol
  // violation.

  assign o_tdata  = i_tdata;
  assign o_tvalid = i_tvalid && !gating;
  assign i_tready = FLUSH ? (o_tready || gating) : (o_tready && !gating);

endmodule