aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/axi/axi_chdr_test_pattern.v
blob: e73eaaa9d70acf12fc802c9bf256fbf8b6ee806d (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
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
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
//
// Copyright 2014 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Synthesizable test pattern generator and checker
// for AXI-Stream that can be used to test transparent blocks
// (FIFOs, switches, etc)
//

module axi_chdr_test_pattern #(
  parameter SR_BASE     = 8'h0,         //Base address for settings in this module
  parameter DELAY_MODE  = "DYNAMIC",    //Are delays configurable at runtime {STATIC, DYNAMIC}
  parameter SID_MODE    = "DYNAMIC",    //Is the SID configurable at runtime {STATIC, DYNAMIC}
  parameter STATIC_SID  = 32'h0,        //SID Value if it is static
  parameter BW_COUNTER  = 1             //Instantiate counters to measure bandwidth (Cycles of Data Xfer / Total cycles)
) (
  input             clk,
  input             reset,

  // AXI stream to hook up to input of DUT
  output reg [63:0] i_tdata,
  output reg        i_tlast,
  output reg        i_tvalid,
  input             i_tready,

  // AXI stream to hook up to output of DUT
  input      [63:0] o_tdata,
  input             o_tlast,
  input             o_tvalid,
  output reg        o_tready,

  //Settings bus interface
  input             set_stb,
  input       [7:0] set_addr,
  input      [31:0] set_data,

  // Test flags
  output reg        running,    //Test is currently in progress
  output reg        done,       //(Sticky) Test has finished executing
  output reg  [1:0] error,      //Error code from last test execution

  output    [127:0] status_vtr, //More information about test failure.
  output     [95:0] bw_ratio    //Bandwidth counter info
);

  //
  // Error Codes
  //
  localparam ERR_SUCCESS                  = 0;
  localparam ERR_DATA_MISMATCH            = 1;
  localparam ERR_SIZE_MISMATCH_TOO_LONG   = 2;
  localparam ERR_SIZE_MISMATCH_TOO_SHORT  = 3;
  
  localparam ERR_TIMEOUT_LOG2             = 10;

  //
  // Settings
  //
  wire        bist_size_ramp;
  wire [1:0]  bist_test_patt;
  wire [12:0] bist_max_pkt_size;
  wire        bist_go, bist_cont, bist_ctrl_wr;
  wire [1:0]  bist_ctrl_reserved;
  wire [17:0] bist_max_pkts;
  wire [15:0] bist_tx_pkt_delay;
  wire [7:0]  bist_rx_samp_delay;
  wire [31:0] bist_cvita_sid;

  localparam TEST_PATT_ZERO_ONE     = 2'd0;
  localparam TEST_PATT_CHECKERBOARD = 2'd1;
  localparam TEST_PATT_COUNT        = 2'd2;
  localparam TEST_PATT_COUNT_INV    = 2'd3;

  // SETTING: Test Control Register
  // Fields:
  // - [0]    : (Strobe) Start the test if 1, otherwise stop a running test.
  //            If no test is running then reset the status. (Reseting a
  //            continuously running test requires two writes to this reg)
  // - [1]    : Start the test in continuous mode. (Run until reset or failure)
  // - [3:2]  : <Unused>
  // - [5:4]  : Test pattern:
  //            * 00 = Zeros and Ones (0x0000000000000000 <-> 0xFFFFFFFFFFFFFFFF)
  //            * 01 = Checkerboard   (0x5555555555555555 <-> 0xAAAAAAAAAAAAAAAA)
  //            * 10 = Counter        (Each byte will count up)
  //            * 11 = Invert Counter (Each byte will count up and invert)
  setting_reg #(
    .my_addr(SR_BASE + 0), .width(6), .at_reset(3'b0)
  ) reg_ctrl (
    .clk(clk), .rst(reset),
    .strobe(set_stb), .addr(set_addr), .in(set_data),
    .out({bist_test_patt, bist_ctrl_reserved, bist_cont, bist_go}),.changed(bist_ctrl_wr)
  );
  
  wire bist_start = bist_ctrl_wr & bist_go;
  wire bist_clear = bist_ctrl_wr & ~bist_go;

  // SETTING: Test Packet Configuration Register
  // Fields:
  // - [17:0]  : Number of packets to transfer per BIST execution
  // - [30:18] : Max number of bytes of payload per packet
  // - [31]    : Send variable (ramping) sized packets
  setting_reg #(
    .my_addr(SR_BASE + 1), .width(32), .at_reset(32'b0)
  ) reg_pkt_config (
    .clk(clk), .rst(reset),
    .strobe(set_stb), .addr(set_addr), .in(set_data),
    .out({bist_size_ramp, bist_max_pkt_size, bist_max_pkts}),.changed()
  );

  generate if (DELAY_MODE == "DYNAMIC") begin
    // SETTING: Delay Register
    // Fields:
    // - [15:0]   : Number of cycles to wait between generating consecutive *packets*
    // - [23:16]  : Number of cycles to wait between consuming consecutive *samples*
    setting_reg #(
      .my_addr(SR_BASE + 2), .width(24), .at_reset(24'b0)
    ) reg_delay (
      .clk(clk), .rst(reset),
      .strobe(set_stb), .addr(set_addr), .in(set_data),
      .out({bist_rx_samp_delay, bist_tx_pkt_delay}),.changed()
    );
  end else begin
    assign {bist_rx_samp_delay, bist_tx_pkt_delay} = 24'h0;
  end endgenerate

  generate if (SID_MODE == "DYNAMIC") begin
    // SETTING: CHDR Stream ID Register
    // Fields:
    // - [31:0]   : Stream ID to attach to CHDR packets
    setting_reg #(
      .my_addr(SR_BASE + 3), .width(32), .at_reset(32'b0)
    ) reg_sid (
      .clk(clk), .rst(reset),
      .strobe(set_stb), .addr(set_addr), .in(set_data),
      .out(bist_cvita_sid),.changed()
    );
  end else begin
    assign bist_cvita_sid = STATIC_SID;
  end endgenerate

  //
  // State
  //
  localparam TX_IDLE    = 3'd0;
  localparam TX_START   = 3'd1;
  localparam TX_ACTIVE  = 3'd2;
  localparam TX_GAP     = 3'd3;
  localparam TX_DONE    = 3'd4;
  localparam TX_WAIT    = 3'd5;

  localparam RX_IDLE    = 3'd0;
  localparam RX_ACTIVE  = 3'd1;
  localparam RX_FAIL    = 3'd2;
  localparam RX_DONE    = 3'd3;
  localparam RX_WAIT    = 3'd4;

  reg [2:0]                   tx_state, rx_state;
  reg [ERR_TIMEOUT_LOG2-1:0]  err_timeout;
  reg [1:0]                   test_pattern;
  reg                         rearm_test;

  reg [17:0]  tx_pkt_cnt, rx_pkt_cnt;
  reg [13:0]  tx_byte_cnt, rx_byte_cnt;
  reg [23:0]  test_run_cnt;
  reg [15:0]  tx_delay;
  reg [7:0]   rx_delay;
  wire [63:0] tx_cvita_hdr, rx_cvita_hdr;

  wire tx_next_pkt_cond, rx_next_pkt_cond;
  assign tx_next_pkt_cond = (tx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) ||               //Packet size reaches max OR
                            (bist_size_ramp && ({7'h0, tx_byte_cnt[13:3]} == tx_pkt_cnt));  //Packet size / 8 == Packet Count
  assign rx_next_pkt_cond = (rx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) || 
                            (bist_size_ramp && ({7'h0, rx_byte_cnt[13:3]} == rx_pkt_cnt));

  wire tx_test_done_cond, rx_test_done_cond;
  assign tx_test_done_cond = (tx_pkt_cnt == bist_max_pkts);
  assign rx_test_done_cond = (rx_pkt_cnt == bist_max_pkts);
  
  reg [63:0] tx_data_next, rx_data_exp;
  always @(*) begin
    case (test_pattern)
      TEST_PATT_ZERO_ONE: begin
        tx_data_next  <= {8{tx_byte_cnt[3] ? 8'h00 : 8'hFF}};
        rx_data_exp   <= {8{rx_byte_cnt[3] ? 8'h00 : 8'hFF}};
      end
      TEST_PATT_CHECKERBOARD: begin
        tx_data_next  <= {32{tx_byte_cnt[3] ? 2'b01 : 2'b10}};
        rx_data_exp   <= {32{rx_byte_cnt[3] ? 2'b01 : 2'b10}};
      end
      TEST_PATT_COUNT: begin
        tx_data_next  <= {8{tx_byte_cnt[10:3]}};
        rx_data_exp   <= {8{rx_byte_cnt[10:3]}};
      end
      TEST_PATT_COUNT_INV: begin
        tx_data_next  <= {8{(tx_byte_cnt[3] ? 8'hFF : 8'h00) ^ tx_byte_cnt[10:3]}};
        rx_data_exp   <= {8{(rx_byte_cnt[3] ? 8'hFF : 8'h00) ^ rx_byte_cnt[10:3]}};
      end
      default: begin
        tx_data_next  <= 64'd0;
        rx_data_exp   <= 64'd0;
      end
    endcase
  end

  //NOTE: We always attach the max size in the packet header for simplicity.
  //      This will not work with state machines that validate the packet length in the
  //      header with the tlast position.
  assign tx_cvita_hdr = {4'h0, tx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};
  assign rx_cvita_hdr = {4'h0, rx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};

  reg [63:0] o_tdata_fail;
  assign status_vtr = {     //Status at the time of failure
    o_tdata_fail,           //[127:64]
    test_run_cnt,           //[63:40]
    rx_data_exp[7:0],       //[39:32]
    rx_pkt_cnt,             //[31:14]
    rx_byte_cnt             //[13:0]
  };

  //-------------------------------------------------------
  // Transmitter
  //-------------------------------------------------------
  always @(posedge clk) begin
    if (reset | (bist_clear & ~rearm_test)) begin
      tx_delay      <= 0;
      tx_pkt_cnt    <= 0;
      tx_byte_cnt   <= 0;
      i_tdata       <= 64'h0;
      i_tlast       <= 1'b0;
      i_tvalid      <= 1'b0;
      tx_state      <= TX_IDLE;
    end else begin
      case(tx_state)
        TX_IDLE: begin
          tx_delay      <= 0;
          tx_pkt_cnt    <= 1;
          tx_byte_cnt   <= 0;
          i_tdata       <= 64'h0;
          i_tlast       <= 1'b0;
          i_tvalid      <= 1'b0;
          // Run when bist_start asserted.
          if (bist_start | rearm_test) begin
            tx_state      <= TX_START;
            test_pattern  <= bist_test_patt;
          end
        end // case: TX_IDLE

        // START signal is asserted.
        // Now need to start transmiting a packet.
        TX_START: begin
          // At the next clock edge drive first beat of new packet onto HDR bus.
          i_tlast     <= 1'b0;
          i_tvalid    <= 1'b1;
          tx_byte_cnt <= tx_byte_cnt + 8;
          i_tdata     <= tx_cvita_hdr;
          tx_state    <= TX_ACTIVE;
        end

        // Valid data is (already) being driven onto the CHDR bus.
        // i_tlast may also be driven asserted if current data count has reached EOP.
        // Watch i_tready to see when it's consumed.
        // When packets are consumed increment data counter or transition state if
        // EOP has sucsesfully concluded.
        TX_ACTIVE: begin
          i_tvalid <= 1'b1; // Always assert tvalid
          if (i_tready) begin
            i_tdata <= tx_data_next;
            // Will this next beat be the last in a packet?
            if (tx_next_pkt_cond) begin
              tx_byte_cnt <= 0;
              i_tlast     <= 1'b1;
              tx_state    <= TX_GAP;
            end else begin
              tx_byte_cnt <= tx_byte_cnt + 8;
              i_tlast     <= 1'b0;
              tx_state    <= TX_ACTIVE;
            end
          end else begin
            //Keep driving all CHDR bus signals as-is until i_tready is asserted.
            tx_state <= TX_ACTIVE;
          end
        end // case: TX_ACTIVE

        // Force an inter-packet gap between packets in a BIST sequence where tvalid is driven low.
        // As we leave this state check if all packets in BIST sequence have been generated yet,
        // and if so go to done state.
        TX_GAP: begin
          if (i_tready) begin
            i_tvalid    <= 1'b0;
            i_tdata     <= 64'h0;
            i_tlast     <= 1'b0;
            tx_pkt_cnt  <= tx_pkt_cnt + 1;

            if (tx_test_done_cond) begin
              tx_state <= TX_DONE;
            end else begin
              tx_state <= TX_WAIT;
              tx_delay <= bist_tx_pkt_delay;
            end
          end else begin // if (i_tready)
            tx_state <= TX_GAP;
          end
        end // case: TX_GAP

        // Simulate inter packet gap in real UHD system
        TX_WAIT: begin
          if (tx_delay == 0)
            tx_state <= TX_START;
          else begin
            tx_delay <= tx_delay - 1;
            tx_state <= TX_WAIT;
          end
        end

        // Complete test pattern BIST sequence has been transmitted.
        // Sit in this state until the RX side consumes all packets except
        // for when the test is running in continuous mode.
        TX_DONE: begin
          i_tvalid  <= 1'b0;
          i_tlast   <= 1'b0;
          i_tdata   <= 64'd0;

          if (running & ~rearm_test) begin
            tx_state <= TX_DONE;
          end else begin
            tx_state <= TX_IDLE;
          end
        end
      endcase // case (tx_state)
    end
  end

  //-------------------------------------------------------
  // Receiver
  //-------------------------------------------------------
  always @(posedge clk) begin
    if (reset | (bist_clear & ~rearm_test)) begin
      rx_delay      <= 0;
      rx_pkt_cnt    <= 0;
      rx_byte_cnt   <= 0;
      o_tdata_fail  <= 64'h0;
      o_tready      <= 1'b0;
      error         <= ERR_SUCCESS;
      done          <= 1'b0;
      rx_state      <= RX_IDLE;
      err_timeout   <= {ERR_TIMEOUT_LOG2{1'b0}};
      test_run_cnt  <= 0;
    end else begin
      case(rx_state)
        RX_IDLE: begin
          rx_delay      <= 0;
          rx_pkt_cnt    <= 1;
          rx_byte_cnt   <= 0;
          o_tdata_fail  <= 64'h0;
          o_tready      <= 1'b0;
          error         <= ERR_SUCCESS;
          done          <= 1'b0;
          err_timeout   <= {ERR_TIMEOUT_LOG2{1'b0}};
          // Not accepting data whilst Idle,
          // switch to active when packet arrives
          if (o_tvalid) begin
            o_tready <= 1'b1;
            rx_state <= RX_ACTIVE;
          end else begin
            rx_state <= RX_IDLE;
          end
        end

        RX_ACTIVE: begin
          o_tready <= 1'b1;
          if (o_tvalid) begin
            if (o_tdata != (rx_byte_cnt == 0 ? rx_cvita_hdr : rx_data_exp)) begin
              $display("axis_test_pattern: o_tdata: %x  !=  expected:  %x @ time: %d", o_tdata, rx_data_exp, $time);
              error         <= ERR_DATA_MISMATCH;
              rx_state      <= RX_FAIL;
              o_tdata_fail  <= o_tdata;
            end else if (rx_next_pkt_cond) begin
              // Last not asserted when it should be!
              if (~(o_tlast === 1)) begin
                $display("axis_test_pattern: o_tlast not asserted when it should be @ time: %d", $time);
                error    <= ERR_SIZE_MISMATCH_TOO_LONG;
                rx_state <= RX_FAIL;
              end else begin
                // End of packet, set up to RX next
                rx_byte_cnt <= 0;
                rx_pkt_cnt  <= rx_pkt_cnt + 1;
                rx_delay    <= bist_rx_samp_delay;
                if (rx_test_done_cond) begin
                  rx_state      <= rearm_test ? RX_IDLE : RX_DONE;
                  error         <= ERR_SUCCESS;
                  test_run_cnt  <= test_run_cnt + 1;
                end else begin
                  rx_state      <= RX_WAIT;
                end
                o_tready        <= 1'b0;
              end
            end else begin
              // ...last asserted when it should not be!
              if (~(o_tlast === 0)) begin
                $display("axis_test_pattern: o_tlast asserted when it should not be @ time: %d", $time);
                error       <= ERR_SIZE_MISMATCH_TOO_SHORT;
                rx_state    <= RX_FAIL;
              end else begin
                // Still in packet body
                rx_byte_cnt <= rx_byte_cnt + 8;
                rx_delay    <= bist_rx_samp_delay;
                if (bist_rx_samp_delay == 0) begin
                  rx_state    <= RX_ACTIVE;
                end else begin
                  rx_state    <= RX_WAIT;
                  o_tready    <= 1'b0;
                end
              end
            end
          end else begin
            // Nothing to do this cycle
            rx_state <= RX_ACTIVE;
          end
        end // case: RX_ACTIVE

        // To simulate the radio consuming samples at a steady rate set by the decimation
        // have a programable delay here
        RX_WAIT: begin
          if (rx_delay == 0) begin
            rx_state <= RX_ACTIVE;
            o_tready <= 1'b1;
          end else begin
            rx_delay <= rx_delay - 1;
            rx_state <= RX_WAIT;
          end
        end

        RX_FAIL: begin
          //The test has failed but the sender still has packets en route
          //Consume all of them before asserting done. Packets could be
          //malformed so just blindly consume lines and count cycles of
          //gaps. If non-valid cycles are more than 2^ERR_TIMEOUT_LOG2 then stop.
          o_tready <= 1'b1;
          if (~o_tvalid) begin
            if (err_timeout == {ERR_TIMEOUT_LOG2{1'b1}}) begin
              rx_state <= RX_DONE;
            end
            err_timeout <= err_timeout + 1;
          end
        end

        RX_DONE: begin
          o_tready    <= 1'b0;
          done        <= 1'b1;
          //The only way to exit this state is by asserting bist_clear
        end
      endcase // case (rx_state)
    end
  end

  //-------------------------------------------------------
  // Status Monitor
  //-------------------------------------------------------
  always @(posedge clk) begin
    if (reset)
      running <= 1'b0;
    else if (tx_state == TX_START)
      running <= 1'b1;
    else if (rx_state == RX_DONE)
      running <= 1'b0;
  end

  always @(posedge clk) begin
    if (reset | bist_clear)
      rearm_test  <= 1'b0;
    else if (bist_start & bist_cont)
      rearm_test  <= 1'b1;
    else if (rx_state == RX_FAIL)
      rearm_test  <= 1'b0;
  end
  
  //-------------------------------------------------------
  // Bandwidth Counter
  //-------------------------------------------------------
  generate if (BW_COUNTER) begin
    reg [47:0]         word_count, cyc_count;
    assign bw_ratio = {word_count, cyc_count};

    //Count number of lines transferred
    always @(posedge clk) begin
      if (reset| (bist_clear & ~rearm_test) | bist_start)
        word_count  <= 48'd0;
      else if (o_tvalid && rx_state == RX_ACTIVE)
        word_count  <= word_count + 48'd1;
    end

    //Count cycles as long as test is running
    always @(posedge clk) begin
      if (reset| (bist_clear & ~rearm_test) | bist_start)
        cyc_count   <= 48'd0;
      else if (rx_state == RX_ACTIVE || rx_state == RX_WAIT)
        cyc_count   <= cyc_count + 48'd1;
    end
  end else begin
    assign bw_ratio = 96'h0;
  end endgenerate

endmodule