aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/x4xx_global_regs.v
blob: 5373c0a7fa2a4794c89c91bffe76e2471e67178b (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
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
//
// Copyright 2021 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0
//
// Module: x4xx_global_regs
//
// Description:
//
//   This module contains the motherboard registers for the USRP.
//
// Parameters:
//
//   REG_BASE        : Base address to use for the registers
//   CHDR_CLK_RATE   : Rate of rfnoc_chdr_clk in Hz
//   CHDR_W          : CHDR protocol width
//   RFNOC_PROTOVER  : RFNoC protocol version (major in most-significant byte,
//                     Minor is least significant byte).
//   NUM_TIMEKEEPERS : Number of timekeeper modules
//   PCIE_PRESENT    : Indicates if PCIe is present in this image
//

`default_nettype none


module x4xx_global_regs #(
  parameter REG_BASE        = 0,
  parameter REG_SIZE        = 'hC00,
  parameter CHDR_CLK_RATE   = 200000000,
  parameter CHDR_W          = 64,
  parameter RFNOC_PROTOVER  = {8'd1, 8'd0},
  parameter NUM_TIMEKEEPERS = 32'd1,
  parameter PCIE_PRESENT    = 0
) (
  // Slave ctrlport interface
  input  wire        s_ctrlport_clk,
  input  wire        s_ctrlport_rst,
  input  wire        s_ctrlport_req_wr,
  input  wire        s_ctrlport_req_rd,
  input  wire [19:0] s_ctrlport_req_addr,
  input  wire [31:0] s_ctrlport_req_data,
  output reg         s_ctrlport_resp_ack    = 1'b0,
  output reg  [ 1:0] s_ctrlport_resp_status = 2'b00,
  output reg  [31:0] s_ctrlport_resp_data   = {32 {1'bX}},

  // RFNoC CHDR clock/reset
  input wire rfnoc_chdr_clk,
  input wire rfnoc_chdr_rst,

  // PPS and clock control signals (domain: s_ctrlport_clk)
  output wire [ 1:0] pps_select,
  output wire [ 1:0] trig_io_select,
  output reg         pll_sync_trigger  = 1'b0,
  output reg  [ 7:0] pll_sync_delay    = 8'b0,
  input  wire        pll_sync_done,
  output reg  [ 7:0] pps_brc_delay     = 8'b0,
  output reg  [25:0] pps_prc_delay     = 26'b0,
  output reg  [ 1:0] prc_rc_divider    = 2'b0,
  output reg         pps_rc_enabled    = 1'b0,

  // Misc control and status signals (domain: s_ctrlport_clk)
  input  wire [31:0] qsfp_port_0_0_info,
  input  wire [31:0] qsfp_port_0_1_info,
  input  wire [31:0] qsfp_port_0_2_info,
  input  wire [31:0] qsfp_port_0_3_info,
  input  wire [31:0] qsfp_port_1_0_info,
  input  wire [31:0] qsfp_port_1_1_info,
  input  wire [31:0] qsfp_port_1_2_info,
  input  wire [31:0] qsfp_port_1_3_info,
  output reg         mfg_test_en_fabric_clk   = 1'b0,
  output reg         mfg_test_en_gty_rcv_clk  = 1'b0,
  input  wire        fpga_aux_ref,

  // Device ID used by RFNoC, transports, etc. (Domain: rfnoc_chdr_clk)
  output reg [15:0] device_id
);

  `include "../../lib/rfnoc/core/ctrlport.vh"
  `include "regmap/global_regs_regmap_utils.vh"
  `include "regmap/versioning_regs_regmap_utils.vh"

  // Make DEVICE_ID default to anything but 0, since that has special meaning
  localparam [DEVICE_ID_SIZE-1:0] DEFAULT_DEVICE_ID = 1;

  // Internal registers (Domain: s_ctrlport_clk)
  reg [  DEVICE_ID_SIZE-1:0] device_id_reg = DEFAULT_DEVICE_ID;
  reg [SCRATCH_REG_SIZE-1:0] scratch_reg   = {SCRATCH_REG_SIZE{1'b0}};
  reg [SERIAL_NUM_HIGH_REG_SIZE + SERIAL_NUM_LOW_REG_SIZE-1:0] serial_num_reg = 0;

  // CHDR clock counter (Domain: rfnoc_chdr_clk)
  reg  [CHDR_CLK_COUNT_REG_SIZE-1:0] chdr_counter = {CHDR_CLK_COUNT_REG_SIZE{1'b0}};

  // CHDR clock counter register (Domain: s_ctrlport_clk)
  wire chdr_counter_fifo_valid;
  wire [CHDR_CLK_COUNT_REG_SIZE-1:0] chdr_counter_fifo_data;
  reg  [CHDR_CLK_COUNT_REG_SIZE-1:0] chdr_counter_reg = 0;

  // Measure PPS for manufacturing test
  reg [MFG_TEST_FPGA_AUX_REF_FREQ_SIZE-1:0] fpga_aux_ref_freq = 0;

  reg [PPS_SELECT_SIZE-1:0] pps_select_reg = PPS_INT_25MHZ;
  assign pps_select = pps_select_reg;

  reg [TRIGGER_IO_SELECT_SIZE-1:0] trig_io_select_reg = TRIG_IO_INPUT;
  assign trig_io_select = trig_io_select_reg;

  // Bus counter in the rfnoc_chdr_clk domain.
  always @(posedge rfnoc_chdr_clk) begin
    if (rfnoc_chdr_rst) begin
      chdr_counter <= {CHDR_CLK_COUNT_REG_SIZE{1'b0}};
    end else begin
      chdr_counter <= chdr_counter + 1;
    end
  end

  // Safely cross clock domains for the CHDR counter.
  handshake #(
    .WIDTH (CHDR_CLK_COUNT_REG_SIZE)
  ) handshake_chdr_counter (
    .clk_a   (rfnoc_chdr_clk),
    .rst_a   (rfnoc_chdr_rst),
    .valid_a (1'b1),
    .data_a  (chdr_counter),
    .busy_a  (),
    .clk_b   (s_ctrlport_clk),
    .valid_b (chdr_counter_fifo_valid),
    .data_b  (chdr_counter_fifo_data)
  );

  // Register a valid FIFO output to ensure the counter is always valid.
  always @(posedge s_ctrlport_clk) begin
    if (s_ctrlport_rst) begin
      chdr_counter_reg <= 0;
    end else begin
      if (chdr_counter_fifo_valid) begin
        chdr_counter_reg <= chdr_counter_fifo_data;
      end
    end
  end

  wire [31:0] build_datestamp;

  USR_ACCESSE2 usr_access_i (
    .DATA(build_datestamp), .CFGCLK(), .DATAVALID()
  );


  //---------------------------------------------------------------------------
  // Global Registers
  //---------------------------------------------------------------------------

  // Check that address is within this module's range.
  wire address_in_range = (s_ctrlport_req_addr >= REG_BASE) && (s_ctrlport_req_addr < REG_BASE + REG_SIZE);

  // Registers implementation
  always @ (posedge s_ctrlport_clk) begin
    if (s_ctrlport_rst) begin
      s_ctrlport_resp_ack    <= 1'b0;
      s_ctrlport_resp_data   <= {32 {1'bX}};
      s_ctrlport_resp_status <= 2'b00;
      scratch_reg            <= {SCRATCH_REG_SIZE{1'b0}};
      serial_num_reg         <= 0;
      pps_select_reg         <= PPS_INT_25MHZ;
      trig_io_select_reg     <= TRIG_IO_INPUT;
      device_id_reg          <= DEFAULT_DEVICE_ID;

    end else begin
      // Write registers
      if (s_ctrlport_req_wr) begin
        // Acknowledge by default
        s_ctrlport_resp_ack    <= 1'b1;
        s_ctrlport_resp_data   <= 32'h0;
        s_ctrlport_resp_status <= CTRL_STS_OKAY;

        case (s_ctrlport_req_addr)
          REG_BASE + SCRATCH_REG: begin
            scratch_reg <= s_ctrlport_req_data;
          end

          REG_BASE + DEVICE_ID_REG: begin
            device_id_reg <= s_ctrlport_req_data[DEVICE_ID_MSB:DEVICE_ID];
          end

          REG_BASE + CLOCK_CTRL_REG: begin
            pps_select_reg     <= s_ctrlport_req_data[PPS_SELECT_MSB:PPS_SELECT];
            trig_io_select_reg <= s_ctrlport_req_data[TRIGGER_IO_SELECT_MSB:TRIGGER_IO_SELECT];
            pll_sync_delay     <= s_ctrlport_req_data[PLL_SYNC_DELAY_MSB:PLL_SYNC_DELAY];
            pll_sync_trigger   <= s_ctrlport_req_data[PLL_SYNC_TRIGGER];
            pps_brc_delay      <= s_ctrlport_req_data[PPS_BRC_DELAY_MSB:PPS_BRC_DELAY];
          end

          REG_BASE + PPS_CTRL_REG: begin
            pps_prc_delay  <= s_ctrlport_req_data[PPS_PRC_DELAY_MSB:PPS_PRC_DELAY];
            prc_rc_divider <= s_ctrlport_req_data[PRC_RC_DIVIDER_MSB:PRC_RC_DIVIDER];
            pps_rc_enabled <= s_ctrlport_req_data[PPS_RC_ENABLED];
          end

          REG_BASE + SERIAL_NUM_LOW_REG: begin
            serial_num_reg[SERIAL_NUM_LOW_REG_SIZE-1:0] <= s_ctrlport_req_data;
          end

          REG_BASE + SERIAL_NUM_HIGH_REG: begin
            serial_num_reg[SERIAL_NUM_LOW_REG_SIZE +: SERIAL_NUM_HIGH_REG_SIZE] <= s_ctrlport_req_data;
          end

          REG_BASE + MFG_TEST_CTRL_REG: begin
            mfg_test_en_fabric_clk  <= s_ctrlport_req_data[MFG_TEST_EN_FABRIC_CLK];
            mfg_test_en_gty_rcv_clk <= s_ctrlport_req_data[MFG_TEST_EN_GTY_RCV_CLK];
          end

          // No register implementation for provided address
          default: begin
            // Acknowledge and provide error status if address is in range
            if (address_in_range) begin
              s_ctrlport_resp_status <= CTRL_STS_CMDERR;

            // No response if out of range
            end else begin
              s_ctrlport_resp_ack <= 1'b0;
            end
          end
        endcase

      // Read registers
      end else if (s_ctrlport_req_rd) begin
        // Acknowledge by default
        s_ctrlport_resp_ack    <= 1'b1;
        s_ctrlport_resp_data   <= 32'h0;
        s_ctrlport_resp_status <= CTRL_STS_OKAY;

        case (s_ctrlport_req_addr)
          REG_BASE + COMPAT_NUM_REG: begin
            s_ctrlport_resp_data[COMPAT_MAJOR_MSB:COMPAT_MAJOR] <= FPGA_CURRENT_VERSION_MAJOR;
            s_ctrlport_resp_data[COMPAT_MINOR_MSB:COMPAT_MINOR] <= FPGA_CURRENT_VERSION_MINOR;
          end

          REG_BASE + DATESTAMP_REG: begin
            s_ctrlport_resp_data <= build_datestamp;
          end

          REG_BASE + GIT_HASH_REG: begin
            `ifndef GIT_HASH
              `define GIT_HASH 32'h0BADC0DE
            `endif
            s_ctrlport_resp_data <= `GIT_HASH;
          end

          REG_BASE + SCRATCH_REG: begin
            s_ctrlport_resp_data <= scratch_reg;
          end

          REG_BASE + DEVICE_ID_REG: begin
            if (PCIE_PRESENT) begin
              s_ctrlport_resp_data[PCIE_PRESENT_BIT] <= 1'b1;
            end
            s_ctrlport_resp_data[DEVICE_ID_MSB:DEVICE_ID] <= device_id_reg;
          end

          REG_BASE + RFNOC_INFO_REG: begin
            s_ctrlport_resp_data[CHDR_WIDTH_MSB:CHDR_WIDTH]               <= CHDR_W[CHDR_WIDTH_SIZE-1:0];
            s_ctrlport_resp_data[RFNOC_PROTO_MAJOR_MSB:RFNOC_PROTO_MAJOR] <= RFNOC_PROTOVER[RFNOC_PROTO_MAJOR_MSB:RFNOC_PROTO_MAJOR];
            s_ctrlport_resp_data[RFNOC_PROTO_MINOR_MSB:RFNOC_PROTO_MINOR] <= RFNOC_PROTOVER[RFNOC_PROTO_MINOR_MSB:RFNOC_PROTO_MINOR];
          end

          REG_BASE + CLOCK_CTRL_REG: begin
            s_ctrlport_resp_data[PPS_SELECT_MSB:PPS_SELECT]               <= pps_select_reg;
            s_ctrlport_resp_data[PLL_SYNC_DELAY_MSB:PLL_SYNC_DELAY]       <= pll_sync_delay;
            s_ctrlport_resp_data[PLL_SYNC_DONE]                           <= pll_sync_done;
            s_ctrlport_resp_data[TRIGGER_IO_SELECT_MSB:TRIGGER_IO_SELECT] <= trig_io_select_reg;
            s_ctrlport_resp_data[PPS_BRC_DELAY_MSB:PPS_BRC_DELAY]         <= pps_brc_delay;
          end

          REG_BASE + PPS_CTRL_REG: begin
            s_ctrlport_resp_data[PPS_RC_ENABLED]                    <= pps_rc_enabled;
            s_ctrlport_resp_data[PRC_RC_DIVIDER_MSB:PRC_RC_DIVIDER] <= prc_rc_divider;
            s_ctrlport_resp_data[PPS_PRC_DELAY_MSB:PPS_PRC_DELAY]   <= pps_prc_delay;
          end

          REG_BASE + CHDR_CLK_RATE_REG: begin
            s_ctrlport_resp_data <= CHDR_CLK_RATE[CHDR_CLK_RATE_REG_SIZE-1:0];
          end

          REG_BASE + CHDR_CLK_COUNT_REG: begin
            s_ctrlport_resp_data <= chdr_counter_reg;
          end

          REG_BASE + QSFP_PORT_0_0_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_0_0_info;
          end

          REG_BASE + QSFP_PORT_0_1_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_0_1_info;
          end

          REG_BASE + QSFP_PORT_0_2_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_0_2_info;
          end

          REG_BASE + QSFP_PORT_0_3_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_0_3_info;
          end

          REG_BASE + QSFP_PORT_1_0_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_1_0_info;
          end

          REG_BASE + QSFP_PORT_1_1_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_1_1_info;
          end

          REG_BASE + QSFP_PORT_1_2_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_1_2_info;
          end

          REG_BASE + QSFP_PORT_1_3_INFO_REG: begin
            s_ctrlport_resp_data <= qsfp_port_1_3_info;
          end

          REG_BASE + NUM_TIMEKEEPERS_REG: begin
            s_ctrlport_resp_data <= NUM_TIMEKEEPERS[NUM_TIMEKEEPERS_REG_SIZE-1:0];
          end

          REG_BASE + SERIAL_NUM_LOW_REG: begin
            s_ctrlport_resp_data <= serial_num_reg[SERIAL_NUM_LOW_REG_SIZE-1:0];
          end

          REG_BASE + SERIAL_NUM_HIGH_REG: begin
            s_ctrlport_resp_data <= serial_num_reg[SERIAL_NUM_LOW_REG_SIZE +: SERIAL_NUM_HIGH_REG_SIZE];
          end

          REG_BASE + MFG_TEST_CTRL_REG: begin
            s_ctrlport_resp_data[MFG_TEST_EN_FABRIC_CLK]  <= mfg_test_en_fabric_clk;
            s_ctrlport_resp_data[MFG_TEST_EN_GTY_RCV_CLK] <= mfg_test_en_gty_rcv_clk;
          end

          REG_BASE + MFG_TEST_STATUS_REG: begin
            s_ctrlport_resp_data[MFG_TEST_FPGA_AUX_REF_FREQ_MSB:MFG_TEST_FPGA_AUX_REF_FREQ] <= fpga_aux_ref_freq;
          end

          // No register implementation for provided address
          default: begin
            // Acknowledge and provide error status if address is in range
            if (address_in_range) begin
              s_ctrlport_resp_status <= CTRL_STS_CMDERR;

            // No response if out of range
            end else begin
              s_ctrlport_resp_ack <= 1'b0;
            end
          end
        endcase

      end else begin
        s_ctrlport_resp_ack <= 1'b0;
      end
    end
  end

  // Assign Device ID register (Domain: s_ctrlport_clk) to module
  // output (Domain: rfnoc_chdr_clk).

  wire device_id_fifo_valid;
  wire [DEVICE_ID_SIZE-1:0] device_id_fifo_data;

  // Clock-crossing for device_id.
  handshake #(
    .WIDTH (DEVICE_ID_SIZE)
  ) handshake_device_id (
    .clk_a   (s_ctrlport_clk),
    .rst_a   (s_ctrlport_rst),
    .valid_a (1'b1),
    .data_a  (device_id_reg),
    .busy_a  (),
    .clk_b   (rfnoc_chdr_clk),
    .valid_b (device_id_fifo_valid),
    .data_b  (device_id_fifo_data)
  );

  // Register the handshake output to ensure device_id is always valid.
  always @(posedge rfnoc_chdr_clk) begin
    if (rfnoc_chdr_rst) begin
      device_id <= 'bX;
    end else begin
      if (device_id_fifo_valid) begin
        device_id <= device_id_fifo_data;
      end
    end
  end


  //---------------------------------------------------------------------------
  // FPGA_REF_CLK Test
  //---------------------------------------------------------------------------

  // Count the number of clocks on the incoming PPS for manufacturing test
  // validation.
  reg [25:0] fpga_aux_ref_cnt = 0;
  wire       fpga_aux_ref_sc1;
  reg        fpga_aux_ref_sc2 = 1'b0;

  synchronizer #(
    .STAGES      (2),
    .WIDTH       (1),
    .INITIAL_VAL (1'h0)
  ) synchronizer_fpga_aux_ref (
    .clk (s_ctrlport_clk),
    .rst (1'b0),
    .in  (fpga_aux_ref),
    .out (fpga_aux_ref_sc1)
  );

  // 1.2 seconds with a 40 Mhz clock
  localparam FPGA_AUX_REF_CNT_MAX = 48*1000*1000;

  // Registers implementation
  always @ (posedge s_ctrlport_clk) begin
    if (s_ctrlport_rst) begin
      fpga_aux_ref_sc2  <= 1'b0;
      fpga_aux_ref_freq <= 0;
      fpga_aux_ref_cnt  <= 0;
    end else begin
      fpga_aux_ref_sc2 <= fpga_aux_ref_sc1;
      // Detect rising edge (Was low, now is high)
      if (!fpga_aux_ref_sc2 && fpga_aux_ref_sc1) begin
        // if the count is less than max
        if (fpga_aux_ref_cnt < FPGA_AUX_REF_CNT_MAX) begin
          fpga_aux_ref_freq <= fpga_aux_ref_cnt;
        // if count reached max
        end else begin
          fpga_aux_ref_freq <= 0;
        end
        // reset the counter at each rising edge
        fpga_aux_ref_cnt  <= 0;
      end else begin
        //stop incrementing at the max value
        if (fpga_aux_ref_cnt < FPGA_AUX_REF_CNT_MAX) begin
          fpga_aux_ref_cnt  <= fpga_aux_ref_cnt+1;
        end
      end
    end
  end

endmodule


`default_nettype wire


//XmlParse xml_on
//<regmap name="GLOBAL_REGS_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
//  <group name="GLOBAL_REGS">
//
//    <register name="COMPAT_NUM_REG"      offset="0x00" size="32" writable="false">
//      <info>Revision number</info>
//      <bitfield name="COMPAT_MINOR" range="15..0"/>
//      <bitfield name="COMPAT_MAJOR" range="31..16"/>
//    </register>
//    <register name="DATESTAMP_REG"       offset="0x04" size="32" writable="false">
//      <info>Build datestamp (32-bit)</info>
//      <bitfield name="SECONDS" range="5..0"/>
//      <bitfield name="MINUTES" range="11..6"/>
//      <bitfield name="HOUR"    range="16..12"/>
//      <bitfield name="YEAR"    range="22..17">
//        <info>This is the year number after 2000 (e.g. 2019 = d19).</info>
//      </bitfield>
//      <bitfield name="MONTH"   range="26..23"/>
//      <bitfield name="DAY"     range="31..27"/>
//    </register>
//    <register name="GIT_HASH_REG"        offset="0x08" size="32" writable="false">
//      <info>Git hash of source commit.</info>
//    </register>
//    <register name="SCRATCH_REG"         offset="0x0C" size="32">
//      <info>Scratch register for testing.</info>
//    </register>
//    <register name="DEVICE_ID_REG"       offset="0x10" size="32">
//      <info>Register that contains the motherboard's device ID.</info>
//      <bitfield name="PCIE_PRESENT_BIT" range="31">
//        <info>Set to 1 if PCI-Express core is present in FPGA design.</info>
//      </bitfield>
//      <bitfield name="DEVICE_ID" range="15..0"/>
//    </register>
//    <register name="RFNOC_INFO_REG"      offset="0x14" size="32" writable="false">
//      <info>Register that provides information on the RFNoC protocol.</info>
//      <bitfield name="RFNOC_PROTO_MINOR" range="7..0"/>
//      <bitfield name="RFNOC_PROTO_MAJOR" range="15..8"/>
//      <bitfield name="CHDR_WIDTH"        range="31..16"/>
//    </register>
//    <register name="CLOCK_CTRL_REG"      offset="0x18" size="32">
//      <info>Control register for clocking resources.</info>
//      <bitfield name="PPS_SELECT" range="1..0" initialvalue="PPS_INT_25MHZ">
//        <enumeratedtype name="PPS_ENUM">
//          <value name="PPS_INT_25MHZ" integer="0"/>
//          <value name="PPS_INT_10MHZ" integer="1"/>
//          <value name="PPS_EXT" integer="2"/>
//        </enumeratedtype>
//        <info>
//          Select the source of the PPS signal.
//          For the internal generation the value depending on the base reference clock has to be chosen.
//          The external reference is taken from the PPS_IN pin and is independent of the base reference clock.
//        </info>
//      </bitfield>
//      <bitfield name="REF_SELECT"    range="2">
//        <info>
//          RESERVED. This bit is not implemented on X4xx and reads as 0.
//        </info>
//      </bitfield>
//      <bitfield name="REFCLK_LOCKED" range="3" writable="false">
//        <info>
//          RESERVED. This bit is not implemented on X4xx and reads as 0.
//        </info>
//      </bitfield>
//      <bitfield name="TRIGGER_IO_SELECT" range="5..4" initialvalue="TRIG_IO_INPUT">
//        <info>
//          <b>IMPORTANT!</b> SW must ensure any TRIG_IO consumers (downstream devices) <b>ignore
//          and/or re-sync after enabling this port</b>, since the output-enable is basically
//          asynchronous to the actual TRIG_IO driver.
//        </info>
//        <enumeratedtype name="TRIG_IO_ENUM">
//          <value name="TRIG_IO_INPUT" integer="0"/>
//          <value name="TRIG_IO_PPS_OUTPUT" integer="1"/>
//        </enumeratedtype>
//        <info>
//          Select the direction and content of the trigger inout signal.
//        </info>
//      </bitfield>
//      <bitfield name="PLL_SYNC_TRIGGER" range="8" readable="false">
//        <info>
//          Assertion triggers the SYNC signal generation for LMK04832 after the next appearance of the PPS rising edge.
//          There is no self reset on this trigger.
//          Keep this trigger asserted until @.PLL_SYNC_DONE is asserted.
//        </info>
//      </bitfield>
//      <bitfield name="PLL_SYNC_DONE" range="9" writable="false">
//        <info>Indicates the success of the PLL reset started by @.PLL_SYNC_TRIGGER. Reset on deassertion of @.PLL_SYNC_TRIGGER.</info>
//      </bitfield>
//      <bitfield name="PLL_SYNC_DELAY" range="16..23">
//        <info>
//          Due to the HDL implementation the rising edge of the SYNC signal for
//          the LMK04832 is generated 2 clock cycles after the PPS rising edge.
//          This delay can be further increased by setting this delay value
//          (e.g. PLL_SYNC_DELAY=3 will result in a total delay of 5 clock cycles).<br>
//          In case two X400 devices are connected using the PPS and reference clock the master delay value needs to be 3 clock cycles
//          higher than the slave delay value to align the LMK sync edges in time.
//        </info>
//      </bitfield>
//      <bitfield name="PPS_BRC_DELAY" range="31..24">
//        <info>
//          Number of base reference clock cycles from appearance of the PPS
//          rising edge to the occurrence of the aligned edge of base reference
//          clock and PLL reference clock at the sample PLL output. This number
//          is the sum of the actual value based on @.PLL_SYNC_DELAY (also
//          accumulate the fixed amount of clock cycles) and if any the number of
//          cycles the SPLL requires from issuing of the SYNC signal to the
//          aligned edge (with LMK04832 = 0).<br>
//          The number written to this register has to be reduced by 1 due to
//          HDL implementation.
//        </info>
//      </bitfield>
//    </register>
//    <register name="PPS_CTRL_REG" offset="0x1C" size="32">
//      <info>Control registers for PPS generation.</info>
//      <bitfield name="PPS_PRC_DELAY" range="25..0">
//        <info>
//          The number of PLL reference clock cycles from one aligned edge to the
//          desired aligned edge to issue the PPS in radio clock domain. This
//          delay is configurable to any aligned edge within a maximum delay of 1
//          second (period of PPS). <br>
//          The value written to the register has to be reduced by 4 due to
//          HDL implementation.
//        </info>
//      </bitfield>
//      <bitfield name="PRC_RC_DIVIDER" range="29..28">
//        <info>
//          Clock multiplier used to generate radio clock from PLL reference clock.
//          The value written to the register has to be reduced by 2 due to
//          HDL implementation.
//        </info>
//      </bitfield>
//      <bitfield name="PPS_RC_ENABLED" range="31">
//        <info>
//          Enables the PPS signal in radio clock domain. Please make sure that
//          the values of @.PPS_BRC_DELAY, @.PPS_PRC_DELAY and @.PRC_RC_DIVIDER are
//          set before enabling this bit. It is recommended to disable the PPS
//          for changes on the other values. Use a wait time of at least 1 second
//          before changing this value to ensure the values are stable for the
//          next PPS edge.
//        </info>
//      </bitfield>
//    </register>
//    <register name="CHDR_CLK_RATE_REG"   offset="0x20" size="32" writable="false">
//      <info>Returns the RFNoC bus clock rate (CHDR).</info>
//      <bitfield name="CHDR_CLK" range="31..0" initialvalue="CHDR_CLK_VALUE">
//        <enumeratedtype name="CHDR_CLK_ENUM" showhex="true">
//          <value name="CHDR_CLK_VALUE" integer="200000000"/>
//        </enumeratedtype>
//      </bitfield>
//    </register>
//    <register name="CHDR_CLK_COUNT_REG"  offset="0x24" size="32" writable="false">
//      <info>
//        Returns the count value of a free-running counter driven by the RFNoC
//        CHDR bus clock.
//      </info>
//    </register>
//    <register name="QSFP_PORT_0_0_INFO_REG"   offset="0x60" size="32" writable="false">
//      <info>
//        Returns information from the QSFP0 Lane0.
//      </info>
//    </register>
//    <register name="QSFP_PORT_0_1_INFO_REG"   offset="0x64" size="32" writable="false">
//      <info>
//        Returns information from the QSFP0 Lane1.
//      </info>
//    </register>
//    <register name="QSFP_PORT_0_2_INFO_REG"   offset="0x68" size="32" writable="false">
//      <info>
//        Returns information from the QSFP0 Lane2.
//      </info>
//    </register>
//    <register name="QSFP_PORT_0_3_INFO_REG"   offset="0x6C" size="32" writable="false">
//      <info>
//        Returns information from the QSFP0 Lane3.
//      </info>
//    </register>
//    <register name="QSFP_PORT_1_0_INFO_REG"   offset="0x70" size="32" writable="false">
//      <info>
//        Returns information from the QSFP1 Lane0.
//      </info>
//    </register>
//    <register name="QSFP_PORT_1_1_INFO_REG"   offset="0x74" size="32" writable="false">
//      <info>
//        Returns information from the QSFP1 Lane1.
//      </info>
//    </register>
//    <register name="QSFP_PORT_1_2_INFO_REG"   offset="0x78" size="32" writable="false">
//      <info>
//        Returns information from the QSFP1 Lane2.
//      </info>
//    </register>
//    <register name="QSFP_PORT_1_3_INFO_REG"   offset="0x7C" size="32" writable="false">
//      <info>
//        Returns information from the QSFP1 Lane3.
//      </info>
//    </register>
//    <register name="GPS_CTRL_REG"        offset="0x38" size="32">
//      <info>
//        RESERVED. This register is not implemented on X4xx. GPS is connected 
//        to the PS via a UART.
//      </info>
//    </register>
//    <register name="GPS_STATUS_REG"      offset="0x3C" size="32" writable="false">
//      <info>
//        RESERVED. This register is not implemented on X4xx. GPS is connected 
//        to the PS via a UART.
//      </info>
//    </register>
//    <register name="DBOARD_CTRL_REG"     offset="0x40" size="32">
//      <info>
//        RESERVED. This register is not implemented on X4xx.
//      </info>
//    </register>
//    <register name="DBOARD_STATUS_REG"   offset="0x44" size="32" writable="false">
//      <info>
//        RESERVED. This register is not implemented on X4xx.
//      </info>
//    </register>
//    <register name="NUM_TIMEKEEPERS_REG" offset="0x48" size="32" writable="false">
//      <info>Register that specifies the number of timekeepers in the core.</info>
//    </register>
//    <register name="SERIAL_NUM_LOW_REG"         offset="0x4C" size="32">
//      <info>Least significant bytes of 8 byte serial number</info>
//    </register>
//    <register name="SERIAL_NUM_HIGH_REG"         offset="0x50" size="32">
//      <info>Most significant bytes of 8 byte serial number</info>
//    </register>
//    <register name="MFG_TEST_CTRL_REG"      offset="0x54" size="32">
//      <info>Control register for mfg_test functions.</info>
//      <bitfield name="MFG_TEST_EN_GTY_RCV_CLK" range="0">
//        <info>
//          When enabled, routes data_clk to GTY_RCV_CLK output port.
//          When disabled, the GTY_RCV_CLK output is driven to 0.
//        </info>
//      </bitfield>
//      <bitfield name="MFG_TEST_EN_FABRIC_CLK" range="1">
//        <info>
//          When enabled, routes data_clk to FPGA_REF_CLK output port.
//          When disabled, the FPGA_REF_CLK output is driven to 0.
//        </info>
//      </bitfield>
//    </register>
//    <register name="MFG_TEST_STATUS_REG" offset="0x58" size="32">
//      <info>Status register for mfg_test functions.</info>
//      <bitfield name="MFG_TEST_FPGA_AUX_REF_FREQ" range="25..0">
//        <info>
//          Report the time between rising edges on the FPGA_REF_CLK
//          input port in 40 MHz Clock ticks. If the count extends
//          to 1.2 seconds without an edge, the value reported is set
//          to zero.
//        </info>
//      </bitfield>
//    </register>
//  </group>
//</regmap>
//XmlParse xml_off