File: cmd_pkt_proc.v

package info (click to toggle)
uhd 3.13.1.0-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 207,120 kB
  • sloc: cpp: 167,245; ansic: 86,841; vhdl: 53,420; python: 40,839; xml: 13,167; tcl: 5,688; makefile: 2,167; sh: 1,719; pascal: 230; csh: 94; asm: 20; perl: 11
file content (290 lines) | stat: -rw-r--r-- 10,683 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
//
// Copyright 2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

// Command Packet Processor
//   Accepts 64-bit AXI stream with compressed VITA (CVITA) command packet data:
//     0: Header
//     1: Optional 64 bit VITA time
//     2: { 24'h0, settings bus address [7:0], data [31:0] }   Note: Address / data width are parameterized
//     Note: If long command packet (i.e. more than one settings bus transaction in payload),
//           additional settings bus transactions can follow.
//
// Generates a response packet with the same sequence number, the src/dst stream ID swapped,
// (optional) the actual time the setting was sent, and readback data as the payload.
//
// TODO: Should long command packets have one readback per settings bus transaction?
//       Should the response be a 'long' reponse packet or one packet per transaction?
//
// Settings register bus:
// - The settings register bus is a simple strobed interface.
// - Transactions include both a write and a readback.
// - The write occurs when set_stb is asserted.
//   The settings register with the address matching set_addr will
//   be loaded with the data on set_data.
// - Readback occurs when rb_stb is asserted. The read back strobe
//   must assert at least one clock cycle after set_stb asserts /
//   rb_stb is ignored if asserted on the same clock cycle of set_stb.
//   Example valid and invalid timing:
//              __    __    __    __
//   clk     __|  |__|  |__|  |__|  |__
//               _____
//   set_stb ___|     |________________
//                    _____
//   rb_stb  ________|     |___________     (Valid)
//                           _____
//   rb_stb  _______________|     |____     (Valid)
//           __________________________
//   rb_stb                                 (Valid if readback data is a constant)
//               _____
//   rb_stb  ___|     |________________     (Invalid / ignored, same cycle as set_stb)

module cmd_pkt_proc #(
  parameter SR_AWIDTH = 8,
  parameter SR_DWIDTH = 32,
  parameter RB_AWIDTH = 8,
  parameter RB_USER_AWIDTH = 8,
  parameter RB_DWIDTH = 64,
  parameter USE_TIME = 1,           // 0: Ignore command packet time, 1: Support timed command packets
  // TODO: Eliminate extra readback address output once NoC Shell / user register readback address spaces are merged
  parameter SR_RB_ADDR = 0,         // Settings bus address to set NoC Shell readback address register
  parameter SR_RB_ADDR_USER = 1,    // Settings bus address to set user readback address register
  parameter FIFO_SIZE = 5           // Depth of command FIFO
)(
  input clk, input reset, input clear,
  input [63:0] cmd_tdata, input cmd_tlast, input cmd_tvalid, output cmd_tready,
  output reg [63:0] resp_tdata, output reg resp_tlast, output reg resp_tvalid, input resp_tready,
  input [63:0] vita_time,
  output reg set_stb, output reg [SR_AWIDTH-1:0] set_addr, output reg [SR_DWIDTH-1:0] set_data,
  output reg [63:0] set_time, output reg set_has_time,
  input rb_stb, input [RB_DWIDTH-1:0] rb_data, output reg [RB_AWIDTH-1:0] rb_addr, output reg [RB_USER_AWIDTH-1:0] rb_addr_user
);

  // Input FIFO
  wire [63:0] cmd_fifo_tdata;
  wire cmd_fifo_tlast, cmd_fifo_tvalid, cmd_fifo_tready;
  axi_fifo #(.WIDTH(65), .SIZE(FIFO_SIZE)) axi_fifo (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata({cmd_tlast,cmd_tdata}), .i_tvalid(cmd_tvalid), .i_tready(cmd_tready),
    .o_tdata({cmd_fifo_tlast,cmd_fifo_tdata}), .o_tvalid(cmd_fifo_tvalid), .o_tready(cmd_fifo_tready),
    .space(), .occupied());

  wire [63:0] pkt_vita_time;
  wire [1:0] pkt_type;
  wire has_time, eob;
  wire [11:0] seqnum;
  wire [15:0] src_sid, dst_sid;
  reg [63:0] pkt_vita_time_hold;
  reg has_time_hold;
  reg [11:0] seqnum_hold;
  reg [15:0] src_sid_hold, dst_sid_hold;
  wire [63:0] int_tdata;
  reg int_tready;
  wire int_tlast, int_tvalid;

  // Extracts header fields
  cvita_hdr_parser #(.REGISTER(0)) cvita_hdr_parser (
    .clk(clk), .reset(reset), .clear(clear),
    .hdr_stb(),
    .pkt_type(pkt_type), .eob(eob), .has_time(has_time),
    .seqnum(seqnum), .length(), .payload_length(),
    .src_sid(src_sid), .dst_sid(dst_sid),
    .vita_time_stb(), .vita_time(pkt_vita_time),
    .i_tdata(cmd_fifo_tdata), .i_tlast(cmd_fifo_tlast), .i_tvalid(cmd_fifo_tvalid), .i_tready(cmd_fifo_tready),
    .o_tdata(int_tdata), .o_tlast(int_tlast), .o_tvalid(int_tvalid), .o_tready(int_tready));

  wire is_cmd_pkt = {pkt_type,eob} == 3'b100;
  reg is_long_cmd_pkt;

  wire now, late;
  wire go = (USE_TIME[0] & has_time_hold) ? (now | late) : 1'b1;
  time_compare time_compare (
    .clk(clk), .reset(reset), .time_now(vita_time), .trigger_time(pkt_vita_time_hold),
    .now(now), .early(), .late(late), .too_early());

  reg [3:0] state;
  localparam S_CMD_HEAD         = 4'd0;
  localparam S_CMD_TIME         = 4'd1;
  localparam S_CMD_DATA         = 4'd2;
  localparam S_SET_WAIT         = 4'd3;
  localparam S_READBACK         = 4'd4;
  localparam S_RESP_HEAD        = 4'd5;
  localparam S_RESP_TIME        = 4'd6;
  localparam S_RESP_DATA        = 4'd7;
  localparam S_DROP             = 4'd8;

  // Setting the readback address requires special handling in the state machine
  wire set_rb_addr      = int_tdata[SR_AWIDTH-1+32:32] == SR_RB_ADDR[SR_AWIDTH-1:0];
  wire set_rb_addr_user = int_tdata[SR_AWIDTH-1+32:32] == SR_RB_ADDR_USER[SR_AWIDTH-1:0];

  wire [127:0] header;
  cvita_hdr_encoder cvita_hdr_encoder (
    .header(header),
    .pkt_type(2'b11), .has_time(USE_TIME[0]), .eob(1'b0),
    .seqnum(seqnum_hold), .payload_length(16'd8),
    .src_sid(dst_sid_hold), .dst_sid(src_sid_hold), // Flip dst / src sids
    .vita_time(pkt_vita_time_hold));

  reg [63:0] resp_time;
  wire [63:0] resp_header = header[127:64];

  reg [63:0] rb_data_hold;

  always @(*) begin
    case (state)
      S_CMD_HEAD : int_tready <= 1'b1;
      S_CMD_TIME : int_tready <= 1'b1;
      S_CMD_DATA : int_tready <= go;
      S_DROP     : int_tready <= 1'b1;
      default    : int_tready <= 1'b0;
    endcase
  end

  // State machine
  always @(posedge clk) begin
    if (reset) begin
      state               <= S_CMD_HEAD;
      resp_tvalid         <= 1'b0;
      set_stb             <= 1'b0;
      set_has_time        <= 1'b0;
      rb_addr             <= 'd0;
      rb_addr_user        <= 'd0;
    end else begin
      case (state)
        // Wait for packet header to arrive
        S_CMD_HEAD : begin
          resp_tvalid     <= 1'b0;
          resp_tlast      <= 1'b0;
          set_stb         <= 1'b0;
          if (int_tvalid) begin
            // Register packet header fields for later use
            has_time_hold <= has_time;
            seqnum_hold   <= seqnum;
            src_sid_hold  <= src_sid;
            dst_sid_hold  <= dst_sid;
            // Packet must be of correct type and for an existing block port
            // and this must be the header.
            if (is_cmd_pkt) begin
              if (has_time) begin
                state               <= S_CMD_TIME;
              end else begin
                pkt_vita_time_hold  <= 64'd0;
                state               <= S_CMD_DATA;
              end
            end else begin
              // Drop all non-command packets.
              if (~int_tlast) begin
                state    <= S_DROP;
              end
            end
          end
        end

        // Consume packet time
        S_CMD_TIME : begin
          if (int_tvalid) begin
            if (int_tlast) begin
              // Invalid -- Short packet
              state               <= S_CMD_HEAD;
            end else begin
              pkt_vita_time_hold  <= pkt_vita_time;
              state               <= S_CMD_DATA;
            end
          end
        end

        // Write to settings bus using addr & data from packet payload.
        // If timed, wait in this state until time to 'go'.
        // Note: Output of timed settings bus transactions will be delayed by
        //       one clock cycle due to registered outputs.
        S_CMD_DATA : begin
          if (int_tvalid & go) begin
            is_long_cmd_pkt <= ~int_tlast;
            set_addr        <= int_tdata[SR_AWIDTH-1+32:32];
            set_data        <= int_tdata[SR_DWIDTH-1:0];
            set_time        <= pkt_vita_time_hold;
            set_has_time    <= has_time_hold;
            // Update rb_addr on same clock cycle as asserting set_stb
            if (set_rb_addr) begin
              rb_addr       <= int_tdata[RB_AWIDTH-1:0];
            end else if (set_rb_addr_user) begin
              rb_addr_user  <= int_tdata[RB_USER_AWIDTH-1:0];
            end
            set_stb         <= 1'b1;
            if (int_tlast) begin
              state         <= S_SET_WAIT;
            // Long command packet support
            end else begin
              state         <= S_CMD_DATA;
            end
          end
        end

        // Wait a clock cycle to allow settings register to output
        S_SET_WAIT : begin
          set_stb    <= 1'b0;
          state      <= S_READBACK;
        end

        // Wait for readback data
        S_READBACK : begin
          if (rb_stb) begin
            resp_time    <= vita_time;
            rb_data_hold <= rb_data;
            resp_tlast   <= 1'b0;
            resp_tdata   <= resp_header;
            resp_tvalid  <= 1'b1;
            state        <= S_RESP_HEAD;
          end
        end

        S_RESP_HEAD : begin
          if (resp_tvalid & resp_tready) begin
            resp_tvalid <= 1'b1;
            if (USE_TIME[0]) begin
              resp_tlast <= 1'b0;
              resp_tdata <= resp_time;
              state      <= S_RESP_TIME;
            end else begin
              resp_tlast <= 1'b1;
              resp_tdata <= rb_data_hold;
              state      <= S_RESP_DATA;
            end
          end
        end

        S_RESP_TIME : begin
          if (resp_tvalid & resp_tready) begin
            resp_tlast  <= 1'b1;
            resp_tdata  <= rb_data_hold;
            resp_tvalid <= 1'b1;
            state       <= S_RESP_DATA;
          end
        end

        S_RESP_DATA : begin
          if (resp_tvalid & resp_tready) begin
            resp_tlast  <= 1'b0;
            resp_tvalid <= 1'b0;
            state       <= S_CMD_HEAD;
          end
        end

        // Drop malformed / non-command packets
        S_DROP : begin
          if (int_tvalid) begin
            if (int_tlast) begin
              state      <= S_CMD_HEAD;
            end
          end
        end

        default : state <= S_CMD_HEAD;
      endcase
    end
  end

endmodule