// // 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 // // // // // Revision number // // // // // Build datestamp (32-bit) // // // // // This is the year number after 2000 (e.g. 2019 = d19). // // // // // // Git hash of source commit. // // // Scratch register for testing. // // // Register that contains the motherboard's device ID. // // Set to 1 if PCI-Express core is present in FPGA design. // // // // // Register that provides information on the RFNoC protocol. // // // // // // Control register for clocking resources. // // // // // // // // 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. // // // // // RESERVED. This bit is not implemented on X4xx and reads as 0. // // // // // RESERVED. This bit is not implemented on X4xx and reads as 0. // // // // // IMPORTANT! SW must ensure any TRIG_IO consumers (downstream devices) ignore // and/or re-sync after enabling this port, since the output-enable is basically // asynchronous to the actual TRIG_IO driver. // // // // // // // Select the direction and content of the trigger inout signal. // // // // // 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. // // // // Indicates the success of the PLL reset started by @.PLL_SYNC_TRIGGER. Reset on deassertion of @.PLL_SYNC_TRIGGER. // // // // 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).
// 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. //
//
// // // 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).
// The number written to this register has to be reduced by 1 due to // HDL implementation. //
//
//
// // Control registers for PPS generation. // // // 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).
// The value written to the register has to be reduced by 4 due to // HDL implementation. //
//
// // // 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. // // // // // 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. // // //
// // Returns the RFNoC bus clock rate (CHDR). // // // // // // // // // Returns the count value of a free-running counter driven by the RFNoC // CHDR bus clock. // // // // // Returns information from the QSFP0 Lane0. // // // // // Returns information from the QSFP0 Lane1. // // // // // Returns information from the QSFP0 Lane2. // // // // // Returns information from the QSFP0 Lane3. // // // // // Returns information from the QSFP1 Lane0. // // // // // Returns information from the QSFP1 Lane1. // // // // // Returns information from the QSFP1 Lane2. // // // // // Returns information from the QSFP1 Lane3. // // // // // RESERVED. This register is not implemented on X4xx. GPS is connected // to the PS via a UART. // // // // // RESERVED. This register is not implemented on X4xx. GPS is connected // to the PS via a UART. // // // // // RESERVED. This register is not implemented on X4xx. // // // // // RESERVED. This register is not implemented on X4xx. // // // // Register that specifies the number of timekeepers in the core. // // // Least significant bytes of 8 byte serial number // // // Most significant bytes of 8 byte serial number // // // Control register for mfg_test functions. // // // When enabled, routes data_clk to GTY_RCV_CLK output port. // When disabled, the GTY_RCV_CLK output is driven to 0. // // // // // When enabled, routes data_clk to FPGA_REF_CLK output port. // When disabled, the FPGA_REF_CLK output is driven to 0. // // // // // Status register for mfg_test functions. // // // 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. // // // //
//
//XmlParse xml_off