File: source_flow_control_legacy.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 (150 lines) | stat: -rw-r--r-- 6,266 bytes parent folder | download | duplicates (5)
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
//
// Copyright 2014-2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//  This block passes the in_* AXI port to the out_* AXI port only when it has
//  enough flow control credits. Data is held when there are not enough credits.
//  Credits are replenished with extension context packets which update the 
//  last_consumed packet register. Max credits are controlled by settings regs.
//  The 2nd line of the packet contains the sequence number in the low 12 bits.
//  These packets should not have a time value, but if they do it will be ignored.

module source_flow_control_legacy #(
   parameter BASE=0
) (
   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 in_tready,
   output [63:0] out_tdata, output out_tlast, output out_tvalid, input out_tready,
   output busy
);
   reg [31:0]     last_seqnum_consumed;
   wire [31:0]    window_size;
   wire [31:0]    go_until_seqnum = last_seqnum_consumed + window_size + 1;
   reg [31:0]     current_seqnum;
   wire           window_reset;
   wire           window_enable;

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

   //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(BASE+1), .width(1)) sr_window_enable
     (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
      .out(window_enable),.changed(window_reset));

   reg         go;
   reg         window_reseting;
   reg [11:0]  window_reset_cnt; //Counter to make reset tolerant to bubble cycles
   reg [1:0]   sfc_state;
   
   always @(posedge clk) begin
      if (reset | clear) begin
         window_reseting  <= 1'b0;
      end else if (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
   
   localparam SFC_HEAD = 2'd0;
   localparam SFC_TIME = 2'd1;
   localparam SFC_BODY = 2'd2;
   localparam SFC_DUMP = 2'd3;

   always @(posedge clk)
     if (reset | clear | window_reset) begin
         last_seqnum_consumed <= 32'hFFFFFFFF;
         sfc_state <= SFC_HEAD;
     end else if (fc_tvalid & fc_tready)
         case(sfc_state)
         SFC_HEAD :
            if(fc_tlast)
               sfc_state <= SFC_HEAD; // Error. CHDR packet with only a header is an error.
            else if(~fc_tdata[63])   // Is this NOT an extension context packet?
               sfc_state <= SFC_DUMP; // Error. Only extension context packets should come in on this interface.
            else if(fc_tdata[61])    // Does this packet have time?
               sfc_state <= SFC_TIME;
            else
               sfc_state <= SFC_BODY;

         SFC_TIME :
            if(fc_tlast)
               sfc_state <= SFC_HEAD; // Error, CHDR packet with only header and time is an error.
            else
               sfc_state <= SFC_BODY;
         
         SFC_BODY :
         begin
            last_seqnum_consumed <= fc_tdata[31:0]; // Sequence number is in lower 32bits.
            if(fc_tlast)
               sfc_state <= SFC_HEAD;
            else
               sfc_state <= SFC_DUMP; // Error. Not expecting any more data in a CHDR packet.
         end

         SFC_DUMP :   // shouldn't ever need to be here, this is an error condition
           if(fc_tlast)
             sfc_state <= SFC_HEAD;

         endcase // case (sfc_state)

   assign busy       = window_reseting;
   assign fc_tready  = ~window_reseting;      // Consume FC if not in reset
   assign out_tdata  = in_tdata;              // CHDR data flows through combinatorially.
   assign out_tlast  = in_tlast;
   assign in_tready  = (go ? out_tready : 1'b0) | window_reseting;
   assign out_tvalid = (go & ~window_reseting) ? in_tvalid : 1'b0;

   //
   // Each time we receive the end of an IF data packet increment the current_seqnum.
   // We bravely assume that no packets go missing...or at least that they will be detected elsewhere
   // and then handled appropriately.
   // The SEQNUM needs to be initialized every time we start a new stream. In new_rx_framer this is done
   // as a side effect of writing a new SID value to the setting reg.
   //
   // By incrementing current_seqnum on the last signal we get the nice effect that packet flow is 
   // always suspended between packets rather than within a packet.
   //
   always @(posedge clk)
      if(reset | clear | window_reseting)
         current_seqnum <= 32'd0;
      else if (in_tvalid && in_tready && in_tlast)
         current_seqnum <= current_seqnum + 32'd1;

   always @(posedge clk)
      if(reset | clear) begin
         go <= 1'b0;
      end else begin
         if(~window_enable)
            go <= 1'b1;
         else
            case(go)
            1'b0:
               // This test assumes the host is well behaved in sending good numbers for packets consumed
               // and that current_seqnum increments always by 1 only.
               // This way wraps are dealt with without a large logic penalty.
               if (in_tvalid & (go_until_seqnum - current_seqnum != 0))
                  go <= 1'b1;
                  //if(in_tvalid & (go_until_seqnum > current_seqnum))  // FIXME will need to handle wrap of 32-bit seqnum

            1'b1:
               if(in_tvalid & in_tready & in_tlast)
                  go <= 1'b0;
            endcase // case (go)      
      end

endmodule // source_flow_control_legacy