File: source_flow_control.v

package info (click to toggle)
uhd 3.15.0.0-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 202,252 kB
  • sloc: cpp: 182,756; ansic: 94,109; vhdl: 53,420; python: 45,849; xml: 12,956; tcl: 7,046; makefile: 2,248; sh: 1,741; pascal: 230; javascript: 120; csh: 94; asm: 20; perl: 11
file content (397 lines) | stat: -rw-r--r-- 14,213 bytes parent folder | download
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
//
// Copyright 2014-2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This block implements credit based flow control using bytes as the
// credits.
// It throttles the input AXI Stream bus until the downstream RFNoC
// block has enough room in its receive window buffer to store the packet.
// If the downstream receive window buffer has enough free space to store
// the input packet, this block will release.
// Tracking the receive window buffer free space is done via flow control
// packets. The downstream block shares its current byte count via
// flow control packets to this block.
//
// This module can also perform flow control using a packet count. Effectively,
// this logic limits the number of packets in flight. In some cases this is
// necessary due to limitations in the transport and can be enabled/disabled as needed.
//
// Limitations:
// - Byte count is only 32 bits. Bit width will need to be increased
//   if receive buffer has a size greater than 2GB (i.e. 2^31).
//
// Parameters:
//   WIDTH                       - Input / Output width in bits
//                                 (must be a power of 2 that is at least 8)
//   SR_FLOW_CTRL_EN             - Settings register address for enabling flow control
//   SR_FLOW_CTRL_WINDOW_SIZE    - Settings register address for window_size
//   SR_FLOW_CTRL_PKT_LIMIT      - Settings register address for pkt_limit
//
// Settings Registers:
//   sfc_enable                 - Enable source flow control
//   window_enable              - Enable byte based flow control
//   pkt_limit_en               - Enable seqnum based flow control
//   window_size                - Size of receive window buffer / FIFO in bytes
//   pkt_limit                  - Maximum number of unack'd packets / packets in flight
//

module source_flow_control #(
  parameter WIDTH = 64,
  parameter SR_FLOW_CTRL_EN           = 0,
  parameter SR_FLOW_CTRL_WINDOW_SIZE  = 1,
  parameter SR_FLOW_CTRL_PKT_LIMIT    = 2
)(
  input clk, input reset, input clear,
  input set_stb, input [7:0] set_addr, input [31:0] set_data,
  input [63:0] fc_tdata, input fc_tlast, input fc_tvalid, output fc_tready,
  input [63:0] in_tdata, input in_tlast, input in_tvalid, output reg in_tready,
  output reg [63:0] out_tdata, output reg out_tlast, output reg out_tvalid, input out_tready,
  output busy,
  output [31:0] debug
);

  `include "chdr_pkt_types.vh"

  wire        window_reset;
  wire        sfc_enable;
  wire        window_enable;
  wire        pkt_limit_enable;
  wire        fc_ack_disable;

  reg  [15:0] last_pkt_consumed;
  reg  [31:0] last_byte_consumed;
  wire [31:0] window_size;

  wire [15:0] pkt_limit;

  // Setting to enable/disable the flow control window
  // When this register is hit, the window will reset.
  // As a part of the reset process, all FC blocked data upstream will be
  // dropped by this module and it will reset to the SFC_HEAD head state.
  // The reset sequence can take more than one cycle during which this
  // module will hold off all flow control data.
  setting_reg #(.my_addr(SR_FLOW_CTRL_EN), .width(4)) sr_enable (
    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
    .out({fc_ack_disable,pkt_limit_enable,window_enable,sfc_enable}),.changed(window_reset));

  // Sets the size of the flow control window
  setting_reg #(.my_addr(SR_FLOW_CTRL_WINDOW_SIZE)) sr_window_size (
    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
    .out(window_size),.changed());

  // Limit number of unack'd packets / packets in flight
  setting_reg #(.my_addr(SR_FLOW_CTRL_PKT_LIMIT), .width(16)) sr_pkt_limit (
    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
    .out(pkt_limit),.changed());

  reg        window_reseting;
  reg [11:0] window_reset_cnt; //Counter to make reset tolerant to bubble cycles

  always @(posedge clk) begin
    if (reset | clear | window_reset) begin // Reset start
      window_reseting  <= 1'b1;
      window_reset_cnt <= 12'd0;
    end else if (window_reseting & ~in_tvalid) begin  // Reset end
      window_reset_cnt <= window_reset_cnt + 12'd1;
      window_reseting  <= (window_reset_cnt == 12'hFFF);
    end
  end

  wire fc_ack_inc, fc_ack_dec;
  reg [5:0] fc_ack_cnt;

  wire [1:0] fc_pkt_type;
  wire fc_eob;
  wire fc_has_time;
  wire [11:0] fc_pkt_seqnum;
  wire [15:0] fc_src_sid, fc_dst_sid;
  wire is_fc_resp_pkt = ({fc_pkt_type,fc_eob} == FC_RESP_PKT);
  cvita_hdr_parser #(.REGISTER(0)) cvita_hdr_parser_fc (
    .clk(clk), .reset(reset), .clear(clear),
    .hdr_stb(),
    .pkt_type(fc_pkt_type), .eob(fc_eob), .has_time(fc_has_time),
    .seqnum(fc_pkt_seqnum), .length(),
    .src_sid(fc_src_sid), .dst_sid(fc_dst_sid),
    .vita_time_stb(), .vita_time(),
    .i_tdata(fc_tdata), .i_tlast(fc_tlast), .i_tvalid(fc_tvalid), .i_tready(),
    .o_tdata(), .o_tlast(), .o_tvalid(), .o_tready(fc_tready));

  reg [1:0] sfc_state;
  localparam SFC_HEAD = 2'd0;
  localparam SFC_TIME = 2'd1;
  localparam SFC_BODY = 2'd2;
  localparam SFC_DUMP = 2'd3;

  reg [15:0] fc_ack_dst_sid, fc_ack_src_sid;
  reg [15:0] fc_resp_dst_sid, fc_resp_src_sid;

  always @(posedge clk) begin
    if (reset | clear | window_reseting) begin
      last_pkt_consumed     <= 0;
      last_byte_consumed    <= 0;
      sfc_state             <= SFC_HEAD;
    end else begin
      case(sfc_state)
        SFC_HEAD : begin
          if (fc_tvalid & fc_tready) begin
            if (fc_tlast) begin
              sfc_state <= SFC_HEAD; // Ignore CHDR packet with only a header.
            end else if (~is_fc_resp_pkt) begin
              sfc_state <= SFC_DUMP; // Drop non-FC packets
            end else begin
              // Hold on to these until the packet is verified
              fc_resp_dst_sid <= fc_dst_sid;
              fc_resp_src_sid <= fc_src_sid;
              if (fc_has_time) begin
                sfc_state <= SFC_TIME;
              end else begin
                sfc_state <= SFC_BODY;
              end
            end
          end
        end

        SFC_TIME : begin
          if (fc_tvalid & fc_tready) begin
            if (fc_tlast) begin
              sfc_state <= SFC_HEAD; // Error, FC response packet with only header and time is an error.
            end else begin
              sfc_state <= SFC_BODY;
            end
          end
        end

        SFC_BODY : begin
          if (fc_tvalid & fc_tready) begin
            if (fc_tlast) begin
              fc_ack_src_sid        <= fc_resp_dst_sid;
              fc_ack_dst_sid        <= fc_resp_src_sid;
              last_pkt_consumed     <= fc_tdata[47:32]; // 16-bit packet count is in upper 32 bits.
              last_byte_consumed    <= fc_tdata[31:0];  // Byte count is in lower 32 bits.
              sfc_state             <= SFC_HEAD;
            end else begin
              sfc_state             <= SFC_DUMP;       // Error. Malformed FC RESP packet?
            end
          end
        end

        SFC_DUMP : begin  // shouldn't ever need to be here, this is an error condition
          if (fc_tvalid & fc_tready & fc_tlast) begin
            sfc_state <= SFC_HEAD;
          end
        end
      endcase // case (sfc_state)
    end
  end

  reg [31:0] window_end, current_byte, window_free_space;
  reg [31:0] pkt_cnt_end, current_pkt_cnt, pkts_free_space;

  always @(posedge clk) begin
    // Calculate end byte / packet of downstream receive window buffer
    window_end        <= last_byte_consumed + window_size;
    pkt_cnt_end       <= last_pkt_consumed + pkt_limit;
    // Calculate downstream receive window buffer free space in both
    // bytes and packets. This works even with wrap around
    window_free_space <= window_end - current_byte;
    pkts_free_space   <= pkt_cnt_end - current_pkt_cnt;
  end

  wire [1:0] pkt_type;
  wire [15:0] pkt_len;
  wire is_data_pkt = (pkt_type == DATA_PKT[2:1]);
  cvita_hdr_parser #(.REGISTER(0)) cvita_hdr_parser_in (
    .clk(clk), .reset(reset), .clear(clear),
    .hdr_stb(),
    .pkt_type(pkt_type), .eob(), .has_time(),
    .seqnum(), .length(pkt_len),
    .src_sid(), .dst_sid(),
    .vita_time_stb(), .vita_time(),
    .i_tdata(in_tdata), .i_tlast(1'b0), .i_tvalid(1'b0), .i_tready(),
    .o_tdata(), .o_tlast(), .o_tvalid(), .o_tready(1'b0));

  reg [2:0] state;
  localparam ST_IDLE           = 0;
  localparam ST_FC_ACK_HEAD    = 1;
  localparam ST_FC_ACK_PAYLOAD = 2;
  localparam ST_DATA_PKT       = 3;
  localparam ST_IGNORE         = 4;

  reg        go;
  reg [31:0] pkt_len_reg;
  reg [11:0] fc_ack_seqnum;

   always @(posedge clk) begin
    if (reset | clear | window_reseting) begin
      go              <= 1'b0;
      current_pkt_cnt <= 0;
      current_byte    <= 0;
      fc_ack_seqnum   <= 0;
      state           <= ST_IDLE;
      pkt_len_reg     <= 0;
    end else begin
      case (state)
        ST_IDLE : begin
          // Round packet length up to nearest word
          pkt_len_reg <=  (pkt_len[15:$clog2(WIDTH/8)] +
                          (|pkt_len[$clog2(WIDTH/8)-1:0]))
                          << $clog2(WIDTH/8);
          if (sfc_enable) begin
            // Received a FC RESP packet, send a FC ACK packet
            if (~fc_ack_disable && fc_ack_cnt != 0) begin
              state         <= ST_FC_ACK_HEAD;
            end else if (in_tvalid) begin
              if (is_data_pkt) begin
                state       <= ST_DATA_PKT;
              end else begin
                state       <= ST_IGNORE;
              end
            end
          end
        end
        // Respond with flow control ack in line
        ST_FC_ACK_HEAD : begin
          // FC ACK packet requires 16 bytes of free space in window buffer
          go <= (~window_enable | (window_free_space >= 16)) & (~pkt_limit_enable | (pkts_free_space >= 1));
          if (out_tvalid & out_tready) begin
            // Header + 8 byte payload
            // Update counters early so they are available for the FC ACK payload
            current_byte    <= current_byte + 16;
            current_pkt_cnt <= current_pkt_cnt + 1;
            state           <= ST_FC_ACK_PAYLOAD;
          end
        end
        ST_FC_ACK_PAYLOAD : begin
          if (out_tvalid & out_tready) begin
            fc_ack_seqnum <= fc_ack_seqnum + 1;
            go            <= 1'b0;
            state         <= ST_IDLE;
          end
        end
        ST_DATA_PKT : begin
          go <= (~window_enable | (window_free_space >= pkt_len_reg)) & (~pkt_limit_enable | (pkts_free_space >= 1));
          if (out_tvalid & out_tready) begin
            if (out_tlast) begin
              go              <= 1'b0;
              current_byte    <= current_byte + pkt_len_reg;
              current_pkt_cnt <= current_pkt_cnt + 1;
              state           <= ST_IDLE;
            end
          end
        end
        // Not a data packet, don't track
        ST_IGNORE : begin
          if (out_tvalid & out_tready & out_tlast) begin
            state    <= ST_IDLE;
          end
        end
      endcase
    end
  end

  assign fc_ack_inc = (sfc_state == SFC_BODY) & fc_tvalid & fc_tready & fc_tlast;
  assign fc_ack_dec = (state == ST_FC_ACK_PAYLOAD) & out_tvalid & out_tready;

  always @(posedge clk) begin
    if (reset | clear | window_reseting) begin
      fc_ack_cnt <= 0;
    end else begin
      if (fc_ack_inc & ~fc_ack_dec & fc_ack_cnt < 63) begin
        fc_ack_cnt <= fc_ack_cnt + 1;
      end else if (~fc_ack_inc & fc_ack_dec) begin
        fc_ack_cnt <= fc_ack_cnt - 1;
      end
    end
  end


  wire [63:0] fc_ack_header;
  wire [63:0] fc_ack_time;
  cvita_hdr_encoder cvita_hdr_encoder_fc_ack (
    .pkt_type(FC_ACK_PKT[2:1]), .eob(FC_ACK_PKT[0]), .has_time(1'b0),
    .seqnum(fc_ack_seqnum),
    .payload_length(16'd8),
    .src_sid(fc_ack_src_sid),
    .dst_sid(fc_ack_dst_sid),
    .vita_time(64'd0),
    .header({fc_ack_header,fc_ack_time}));

  always @(*) begin
    if (window_reseting) begin
      out_tdata  <= in_tdata; // Don't care
      out_tlast  <= in_tlast; // Don't care
      out_tvalid <= 1'b0;
      in_tready  <= 1'b1;
    end else begin
      case (state)
        ST_IDLE : begin
          out_tdata  <= in_tdata;
          out_tlast  <= in_tlast;
          out_tvalid <= 1'b0;
          in_tready  <= 1'b0;
        end
        ST_FC_ACK_HEAD : begin
          out_tdata  <= fc_ack_header;
          out_tlast  <= 1'b0;
          out_tvalid <= go;
          in_tready  <= 1'b0;
        end
        ST_FC_ACK_PAYLOAD : begin
          out_tdata  <= {current_pkt_cnt,current_byte};
          out_tlast  <= 1'b1;
          out_tvalid <= 1'b1;
          in_tready  <= 1'b0;
        end
        ST_DATA_PKT : begin
          out_tdata  <= in_tdata;
          out_tlast  <= in_tlast;
          out_tvalid <= in_tvalid  & go;
          in_tready  <= out_tready & go;
        end
        default : begin  // ST_IGNORE
          out_tdata  <= in_tdata;
          out_tlast  <= in_tlast;
          out_tvalid <= in_tvalid;
          in_tready  <= out_tready;
        end
      endcase
    end
  end

  assign busy      = window_reseting;
  assign fc_tready = 1'b1; // FC RESP is a non-flow controlled path, so always accept packets

  // Debug signals
  reg fc_first_line;
  reg [47:0] fc_recv_cnt;
  reg [63:0] last_fc_header_recv;
  reg [31:0] last_byte_count_recv;
  reg [15:0] last_pkt_count_recv;
  always @(posedge clk) begin
    if (reset | clear | window_reseting) begin
      fc_recv_cnt           <= 48'd0;
      fc_first_line         <= 1'b1;
      last_fc_header_recv   <= 64'd0;
      last_byte_count_recv  <= 32'd0;
      last_pkt_count_recv   <= 16'd0;
    end else begin
      if (fc_tvalid & fc_tready) begin
        if (fc_first_line) begin
          fc_first_line        <= 1'b0;
          last_fc_header_recv  <= fc_tdata;
        end
        if (fc_tlast) begin
          fc_first_line        <= 1'b1;
          fc_recv_cnt          <= fc_recv_cnt + 1;
          last_pkt_count_recv  <= fc_tdata[47:32];
          last_byte_count_recv <= fc_tdata[31:0];
        end
      end
    end
  end

  assign debug     = 32'd0;

endmodule // source_flow_control