aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints
diff options
context:
space:
mode:
authorJavier Valenzuela <javier.valenzuela@ni.com>2021-02-05 13:15:02 -0600
committerAaron Rossetto <aaron.rossetto@ni.com>2021-06-10 11:56:58 -0500
commit99b841c75aa91709090cbf4046bf51b7ffb4f612 (patch)
treecb8d370162f7cec179f738a52ab4d86f8a2e9d98 /fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints
parent7015f5ed2d495f3908773b7c7d74864d0cc3871a (diff)
downloaduhd-99b841c75aa91709090cbf4046bf51b7ffb4f612.tar.gz
uhd-99b841c75aa91709090cbf4046bf51b7ffb4f612.tar.bz2
uhd-99b841c75aa91709090cbf4046bf51b7ffb4f612.zip
fpga: x400: zbx: Add support for ZBX CPLD
Co-authored-by: Cherwa Vang <cherwa.vang@ni.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Paul Butler <paul.butler@ni.com>
Diffstat (limited to 'fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints')
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/README.md10
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/atr_controller.v341
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/basic_regs.v219
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/dsa_control.v736
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/led_control.v259
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/lo_control.v568
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/.gitignore2
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/gen_defaults.py65
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/power_regs.v211
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/switch_control.v918
-rw-r--r--fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/utils/spi_control_utils.vh30
11 files changed, 3359 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/README.md b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/README.md
new file mode 100644
index 000000000..d83d11a19
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/README.md
@@ -0,0 +1,10 @@
+# CPLD Default values
+
+The memory of the CPLD is configured to set all the RF components to safe
+values. That means:
+- LEDs off
+- DSAs are set to maximum attenuation
+
+The Python script `memory_init_files/gen_defaults.py` creates the .hex files
+that get read into the CPLD bitfile. This is run as part of the Makefile
+process, and the script does not have to be executed manually.
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/atr_controller.v b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/atr_controller.v
new file mode 100644
index 000000000..abdf9983f
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/atr_controller.v
@@ -0,0 +1,341 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: atr_controller
+//
+// Description:
+// Controller of the ATR state configuration for the other register endpoints.
+//
+
+`default_nettype none
+
+module atr_controller #(
+ parameter [19:0] BASE_ADDRESS = 0,
+ parameter [19:0] SIZE_ADDRESS = 0
+) (
+ // Clock and reset
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+
+ // Request
+ 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,
+
+ // Response
+ output reg s_ctrlport_resp_ack,
+ output reg [ 1:0] s_ctrlport_resp_status,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ // ATR state of FPGA
+ // Assumes the following assignment on the FPGA:
+ // {tx_running[1], rx_running[1], tx_running[0], rx_running[0]}
+ // where the array index indicates the RF chain.
+ input wire [ 3:0] atr_fpga_state,
+
+ // derived configuration
+ output reg [ 7:0] atr_config_dsa_rf0 = 8'b0,
+ output reg [ 7:0] atr_config_dsa_rf1 = 8'b0,
+ output reg [ 7:0] atr_config_rf0 = 8'b0,
+ output reg [ 7:0] atr_config_rf1 = 8'b0
+);
+
+ `include "../regmap/atr_regmap_utils.vh"
+ `include "../../../../../../lib/rfnoc/core/ctrlport.vh"
+
+ //----------------------------------------------------------
+ // Internal registers
+ //----------------------------------------------------------
+ reg [ RF0_OPTION_SIZE-1:0] option_rf0 = SW_DEFINED;
+ reg [ RF1_OPTION_SIZE-1:0] option_rf1 = SW_DEFINED;
+ reg [RF0_DSA_OPTION_SIZE-1:0] option_dsa_rf0 = SW_DEFINED;
+ reg [RF1_DSA_OPTION_SIZE-1:0] option_dsa_rf1 = SW_DEFINED;
+
+ reg [ SW_RF0_CONFIG_SIZE-1:0] sw_atr_config_rf0 = {SW_RF0_CONFIG_SIZE {1'b0}};
+ reg [ SW_RF1_CONFIG_SIZE-1:0] sw_atr_config_rf1 = {SW_RF1_CONFIG_SIZE {1'b0}};
+ reg [SW_RF0_DSA_CONFIG_SIZE-1:0] sw_atr_config_dsa_rf0 = {SW_RF0_DSA_CONFIG_SIZE {1'b0}};
+ reg [SW_RF1_DSA_CONFIG_SIZE-1:0] sw_atr_config_dsa_rf1 = {SW_RF1_DSA_CONFIG_SIZE {1'b0}};
+
+ //----------------------------------------------------------
+ // Handling of CtrlPort
+ //----------------------------------------------------------
+ wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) && (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS);
+
+ always @(posedge ctrlport_clk) begin
+ // reset internal registers and responses
+ if (ctrlport_rst) begin
+ option_rf0 <= SW_DEFINED;
+ option_rf1 <= SW_DEFINED;
+ option_dsa_rf0 <= SW_DEFINED;
+ option_dsa_rf1 <= SW_DEFINED;
+
+ sw_atr_config_rf0 <= {SW_RF0_CONFIG_SIZE {1'b0}};
+ sw_atr_config_rf1 <= {SW_RF1_CONFIG_SIZE {1'b0}};
+ sw_atr_config_dsa_rf0 <= {SW_RF0_DSA_CONFIG_SIZE {1'b0}};
+ sw_atr_config_dsa_rf1 <= {SW_RF1_DSA_CONFIG_SIZE {1'b0}};
+
+ s_ctrlport_resp_ack <= 1'b0;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ end else begin
+
+ // write requests
+ if (s_ctrlport_req_wr) begin
+ // always issue an ack and no data
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + OPTION_REG: begin
+ option_rf0 <= s_ctrlport_req_data[RF0_OPTION_MSB : RF0_OPTION];
+ option_rf1 <= s_ctrlport_req_data[RF1_OPTION_MSB : RF1_OPTION];
+ option_dsa_rf0 <= s_ctrlport_req_data[RF0_DSA_OPTION_MSB : RF0_DSA_OPTION];
+ option_dsa_rf1 <= s_ctrlport_req_data[RF1_DSA_OPTION_MSB : RF1_DSA_OPTION];
+ end
+
+ BASE_ADDRESS + SW_CONFIG_REG: begin
+ sw_atr_config_rf0 <= s_ctrlport_req_data[SW_RF0_CONFIG_MSB : SW_RF0_CONFIG];
+ sw_atr_config_rf1 <= s_ctrlport_req_data[SW_RF1_CONFIG_MSB : SW_RF1_CONFIG];
+ sw_atr_config_dsa_rf0 <= s_ctrlport_req_data[SW_RF0_DSA_CONFIG_MSB : SW_RF0_DSA_CONFIG];
+ sw_atr_config_dsa_rf1 <= s_ctrlport_req_data[SW_RF1_DSA_CONFIG_MSB : SW_RF1_DSA_CONFIG];
+ end
+
+ // error on undefined address
+ default: begin
+ 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 requests
+ end else if (s_ctrlport_req_rd) begin
+ // default assumption: valid request
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+ s_ctrlport_resp_data <= {32{1'b0}};
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + CURRENT_CONFIG_REG: begin
+ s_ctrlport_resp_data[CURRENT_RF0_CONFIG_MSB : CURRENT_RF0_CONFIG] <= atr_config_rf0;
+ s_ctrlport_resp_data[CURRENT_RF1_CONFIG_MSB : CURRENT_RF1_CONFIG] <= atr_config_rf1;
+ s_ctrlport_resp_data[CURRENT_RF0_DSA_CONFIG_MSB : CURRENT_RF0_DSA_CONFIG] <= atr_config_dsa_rf0;
+ s_ctrlport_resp_data[CURRENT_RF1_DSA_CONFIG_MSB : CURRENT_RF1_DSA_CONFIG] <= atr_config_dsa_rf1;
+ end
+
+ BASE_ADDRESS + OPTION_REG: begin
+ s_ctrlport_resp_data[RF0_OPTION_MSB : RF0_OPTION] <= option_rf0;
+ s_ctrlport_resp_data[RF1_OPTION_MSB : RF1_OPTION] <= option_rf1;
+ s_ctrlport_resp_data[RF0_DSA_OPTION_MSB : RF0_DSA_OPTION] <= option_dsa_rf0;
+ s_ctrlport_resp_data[RF1_DSA_OPTION_MSB : RF1_DSA_OPTION] <= option_dsa_rf1;
+ end
+
+ BASE_ADDRESS + SW_CONFIG_REG: begin
+ s_ctrlport_resp_data[SW_RF0_CONFIG_MSB : SW_RF0_CONFIG] <= sw_atr_config_rf0;
+ s_ctrlport_resp_data[SW_RF1_CONFIG_MSB : SW_RF1_CONFIG] <= sw_atr_config_rf1;
+ s_ctrlport_resp_data[SW_RF0_DSA_CONFIG_MSB : SW_RF0_DSA_CONFIG] <= sw_atr_config_dsa_rf0;
+ s_ctrlport_resp_data[SW_RF1_DSA_CONFIG_MSB : SW_RF1_DSA_CONFIG] <= sw_atr_config_dsa_rf1;
+ end
+
+ // error on undefined address
+ default: begin
+ s_ctrlport_resp_data <= {32{1'b0}};
+ 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
+
+ // no request
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+ //----------------------------------------------------------
+ // derive configuration
+ //----------------------------------------------------------
+ always @(posedge ctrlport_clk) begin
+ case (option_rf0)
+ SW_DEFINED: begin
+ atr_config_rf0 <= sw_atr_config_rf0;
+ end
+ CLASSIC_ATR: begin
+ atr_config_rf0 <= {6'b0, atr_fpga_state[1:0]};
+ end
+ FPGA_STATE: begin
+ atr_config_rf0 <= {4'b0, atr_fpga_state};
+ end
+ endcase
+
+ case (option_rf1)
+ SW_DEFINED: begin
+ atr_config_rf1 <= sw_atr_config_rf1;
+ end
+ CLASSIC_ATR: begin
+ atr_config_rf1 <= {6'b0, atr_fpga_state[3:2]};
+ end
+ FPGA_STATE: begin
+ atr_config_rf1 <= {4'b0, atr_fpga_state};
+ end
+ endcase
+
+ case (option_dsa_rf0)
+ SW_DEFINED: begin
+ atr_config_dsa_rf0 <= sw_atr_config_dsa_rf0;
+ end
+ CLASSIC_ATR: begin
+ atr_config_dsa_rf0 <= {6'b0, atr_fpga_state[1:0]};
+ end
+ FPGA_STATE: begin
+ atr_config_dsa_rf0 <= {4'b0, atr_fpga_state};
+ end
+ endcase
+
+ case (option_dsa_rf1)
+ SW_DEFINED: begin
+ atr_config_dsa_rf1 <= sw_atr_config_dsa_rf1;
+ end
+ CLASSIC_ATR: begin
+ atr_config_dsa_rf1 <= {6'b0, atr_fpga_state[3:2]};
+ end
+ FPGA_STATE: begin
+ atr_config_dsa_rf1 <= {4'b0, atr_fpga_state};
+ end
+ endcase
+ end
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="ATR_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
+// <group name="ATR_REGISTERS">
+// <info>
+// This regmap contains settings for the active configuration of RF 0 and 1.
+// There are two sets of configurations. One set comprises RF switches and
+// LEDs, the other set comprises the attenuators (DSA).
+// </info>
+//
+// <enumeratedtype name="ATR_OPTIONS">
+// <info>
+// Contains the options available for RF 0 and RF 1. The chosen setting
+// affects how the active configuration of up to 8 bits is derived.
+// </info>
+// <value name="SW_DEFINED" integer="0">
+// <info>
+// Uses the respective value of @.SW_CONFIG_REG as configuration.
+// </info>
+// </value>
+// <value name="CLASSIC_ATR" integer="1">
+// <info>
+// This option assumes the FPGA state to be assigned with: Bit 0 = RF 0
+// RX running, Bit 1 = RF 0 TX running, Bit 2 = RF 1 RX running, Bit 3
+// = RF 1 TX running. The configuration for each RF chain is built
+// up of the 2 bits for the RF chain (4 possible states: IDLE, RX only,
+// TX only, TX/RX).
+// </info>0
+// </value>
+// <value name="FPGA_STATE" integer="2">
+// <info>
+// The 4 bit wide ATR FPGA state is used as configuration. This enables 16 states.
+// </info>
+// </value>
+// </enumeratedtype>
+//
+// <register name="CURRENT_CONFIG_REG" size="32" offset="0x00" attributes="Readable">
+// <info>
+// Contains the current active configuration.
+// </info>
+// <bitfield name="CURRENT_RF0_CONFIG" range="7..0" type="integer">
+// <info>
+// Current active configuration for switches and LEDs of RF 0.
+// </info>
+// </bitfield>
+// <bitfield name="CURRENT_RF1_CONFIG" range="15..8" type="integer">
+// <info>
+// Current active configuration for switches and LEDs of RF 1.
+// </info>
+// </bitfield>
+// <bitfield name="CURRENT_RF0_DSA_CONFIG" range="23..16" type="integer">
+// <info>
+// Current active configuration for DSAs of RF 0.
+// </info>
+// </bitfield>
+// <bitfield name="CURRENT_RF1_DSA_CONFIG" range="31..24" type="integer">
+// <info>
+// Current active configuration for DSAs of RF 1.
+// </info>
+// </bitfield>
+// </register>
+//
+// <register name="OPTION_REG" size="32" offset="0x04" attributes="Readable|Writable">
+// <info>
+// Set the option to be used for the RF chains.
+// </info>
+// <bitfield name="RF0_OPTION" range="1..0" type="ATR_OPTIONS" initialvalue="SW_DEFINED">
+// <info>
+// Option used for switches and LEDs of RF 0.
+// </info>
+// </bitfield>
+// <bitfield name="RF1_OPTION" range="9..8" type="ATR_OPTIONS" initialvalue="SW_DEFINED">
+// <info>
+// Option used for switches and LEDs of RF 1.
+// </info>
+// </bitfield>
+// <bitfield name="RF0_DSA_OPTION" range="17..16" type="ATR_OPTIONS" initialvalue="SW_DEFINED">
+// <info>
+// Option used for DSAs of RF 0.
+// </info>
+// </bitfield>
+// <bitfield name="RF1_DSA_OPTION" range="25..24" type="ATR_OPTIONS" initialvalue="SW_DEFINED">
+// <info>
+// Option used for DSAs of RF 1.
+// </info>
+// </bitfield>
+// </register>
+//
+// <register name="SW_CONFIG_REG" size="32" offset="0x08" attributes="Readable|Writable">
+// <info>
+// Contains the configuration to be applied in case SW_DEFINED option is
+// chosen.
+// </info>
+// <bitfield name="SW_RF0_CONFIG" range="7..0" type="integer" initialvalue="0">
+// <info>
+// SW defined configuration for switches and LEDs of RF 0.
+// </info>
+// </bitfield>
+// <bitfield name="SW_RF1_CONFIG" range="15..8" type="integer" initialvalue="0">
+// <info>
+// SW defined configuration for switches and LEDs of RF 1.
+// </info>
+// </bitfield>
+// <bitfield name="SW_RF0_DSA_CONFIG" range="23..16" type="integer" initialvalue="0">
+// <info>
+// SW defined configuration for DSAs of RF 0.
+// </info>
+// </bitfield>
+// <bitfield name="SW_RF1_DSA_CONFIG" range="31..24" type="integer" initialvalue="0">
+// <info>
+// SW defined configuration for DSAs of RF 1.
+// </info>
+// </bitfield>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/basic_regs.v b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/basic_regs.v
new file mode 100644
index 000000000..3c218de17
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/basic_regs.v
@@ -0,0 +1,219 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: basic_regs
+//
+// Description:
+// Basic Registers to inform software about version and capabilities.
+//
+
+`default_nettype none
+
+module basic_regs #(
+ parameter [19:0] BASE_ADDRESS = 0,
+ parameter [19:0] SIZE_ADDRESS = 0
+) (
+ // Request
+ 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,
+ // Response
+ output reg s_ctrlport_resp_ack,
+ output reg [ 1:0] s_ctrlport_resp_status,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ //reg clk domain
+ input wire ctrlport_clk,
+ input wire ctrlport_rst
+);
+
+ `include "../regmap/basic_regs_regmap_utils.vh"
+ `include "../../../../../../lib/rfnoc/core/ctrlport.vh"
+
+ //----------------------------------------------------------
+ // Internal registers
+ //----------------------------------------------------------
+ reg [SCRATCH_REG_SIZE-1:0] scratch_reg = {SCRATCH_REG_SIZE {1'b0}};
+
+ //----------------------------------------------------------
+ // Handling of CtrlPort
+ //----------------------------------------------------------
+ wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) && (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS);
+
+ always @(posedge ctrlport_clk) begin
+ // reset internal registers and responses
+ if (ctrlport_rst) begin
+ scratch_reg <= {SCRATCH_REG_SIZE {1'b0}};
+
+ s_ctrlport_resp_ack <= 1'b0;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ end else begin
+
+ // write requests
+ if (s_ctrlport_req_wr) begin
+ // always issue an ack and no data
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + SLAVE_SCRATCH: begin
+ scratch_reg <= s_ctrlport_req_data[ SCRATCH_REG_MSB : SCRATCH_REG];
+ end
+
+ // error on undefined address
+ default: begin
+ 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 requests
+ end else if (s_ctrlport_req_rd) begin
+ // default assumption: valid request
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+ s_ctrlport_resp_data <= {32{1'b0}};
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + SLAVE_SIGNATURE: begin
+ s_ctrlport_resp_data[BOARD_ID_MSB : BOARD_ID]
+ <= BOARD_ID_VALUE[BOARD_ID_SIZE-1:0];
+ end
+
+ BASE_ADDRESS + SLAVE_REVISION: begin
+ s_ctrlport_resp_data[REVISION_REG_MSB : REVISION_REG]
+ <= CPLD_REVISION[REVISION_REG_SIZE-1:0];
+ end
+
+ BASE_ADDRESS + SLAVE_OLDEST_REVISION: begin
+ s_ctrlport_resp_data[OLDEST_REVISION_REG_MSB : OLDEST_REVISION_REG]
+ <= OLDEST_CPLD_REVISION[OLDEST_REVISION_REG_SIZE-1:0];
+ end
+
+ BASE_ADDRESS + SLAVE_SCRATCH: begin
+ s_ctrlport_resp_data[SCRATCH_REG_MSB : SCRATCH_REG] <= scratch_reg;
+ end
+
+
+ BASE_ADDRESS + GIT_HASH_REGISTER: begin
+ `ifdef GIT_HASH
+ s_ctrlport_resp_data <= `GIT_HASH;
+ `else
+ s_ctrlport_resp_data <= 32'hDEADBEEF;
+ `endif
+ end
+
+ // error on undefined address
+ default: begin
+ s_ctrlport_resp_data <= {32{1'b0}};
+ 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
+
+ // no request
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="BASIC_REGS_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
+// <group name="BASIC_REGS_REGISTERS" size="0x010">
+// <info>
+// This regmap contains the revision registers, signature register, a scratch register, and a slave control reg.
+// </info>
+//
+// <enumeratedtype name="BASIC_REGISTERS_VALUES" showhexvalue="true">
+// <info>
+// This enum is used to create the constants held in the basic registers in both verilog and vhdl.
+// </info>
+// <value name="BOARD_ID_VALUE" integer="0x4002"/>
+// <value name="CPLD_REVISION" integer="0x21031009"/>
+// <value name="OLDEST_CPLD_REVISION" integer="0x20110611"/>
+// </enumeratedtype>
+//
+// <register name="SLAVE_SIGNATURE" size="32" offset="0x00" attributes="Readable">
+// <info>
+// This register contains the unique signature of the DB. This signature is the same value as the one
+// stored on the board ID EEPROM
+// </info>
+// <bitfield name="BOARD_ID" range="15..0" type="integer">
+// <info>
+// Board ID corresponds to the las 16 digits of the daughterboard part number.
+// </info>
+// </bitfield>
+// </register>
+//
+// <register name="SLAVE_REVISION" size="32" offset="0x04" attributes="Readable">
+// <info>
+// This register contains the revision number of the current build
+// </info>
+// <bitfield name="REVISION_REG" range="31..0" type="integer">
+// <info>
+// Returns the revision in YYMMDDHH format
+// </info>
+// </bitfield>
+// </register>
+//
+// <register name="SLAVE_OLDEST_REVISION" size="32" offset="0x08" attributes="Readable">
+// <info>
+// This register contains the revision number of the oldest compatible revision
+// </info>
+// <bitfield name="OLDEST_REVISION_REG" range="31..0" type="integer">
+// <info>
+// Returns the oldest compatible revision in YYMMDDHH format
+// </info>
+// </bitfield>
+// </register>
+//
+// <register name="SLAVE_SCRATCH" size="32" offset="0x0C" attributes="Readable|Writable">
+// <info>
+// Read/write scratch register
+// </info>
+// <bitfield name="SCRATCH_REG" range="31..0" initialvalue="0">
+// <info>
+// Returns the value written here previously.
+// </info>
+// </bitfield>
+// </register>
+//
+// <register name="GIT_HASH_REGISTER" offset="0x10" size="32" writable="false">
+// <info>
+// Git hash of commit used to build this image.{br}
+// Value equals 0xDEADBEEF if the git hash was not used during synthesis.
+// </info>
+// <bitfield name="GIT_CLEAN" range="31..28">
+// <info>
+// 0x0 in case the git status was clean{br}
+// 0xF in case there were uncommitted changes
+// </info>
+// </bitfield>
+// <bitfield name="GIT_HASH" range="27..0">
+// <info>7 hex digit hash code of the commit</info>
+// </bitfield>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/dsa_control.v b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/dsa_control.v
new file mode 100644
index 000000000..abc4b62f9
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/dsa_control.v
@@ -0,0 +1,736 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: dsa_control
+//
+// Description:
+// Implements control over Digital Step Attenuators via CtrlPort. Uses RAM to
+// store multiple ATR configurations. Provides gain table to abstract from raw
+// DSA values.
+//
+// IMPORTANT: The default values here must be synchronized with the default
+// values in gen_defaults.py, they are not automatically kept in sync.
+//
+
+`default_nettype none
+
+module dsa_control #(
+ parameter [19:0] BASE_ADDRESS = 0,
+ parameter [19:0] SIZE_ADDRESS = 0
+) (
+ // Clock and reset
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+
+ // Request
+ 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,
+
+ // Response
+ output reg s_ctrlport_resp_ack,
+ output reg [ 1:0] s_ctrlport_resp_status = 2'b0,
+ output reg [31:0] s_ctrlport_resp_data = 32'b0,
+
+ // ATR switching
+ input wire [ 7:0] atr_config_rf0,
+ input wire [ 7:0] atr_config_rf1,
+
+ // The attenuation setting for TX paths is indexed from two,
+ // to match schematic naming. In this case, the two LSBs
+ // for parallel control going into the DSA chips are connected
+ // to ground(those bits control fractional attenuation).
+
+ //Tx0 DSA control (domain: ctrl_reg_clk)
+ output wire [6:2] tx0_dsa1,
+ output wire [6:2] tx0_dsa2,
+
+ //Tx1 DSA control (domain: ctrl_reg_clk)
+ output wire [6:2] tx1_dsa1,
+ output wire [6:2] tx1_dsa2,
+
+ // The attenuation setting for RX paths is indexed from one,
+ // to match schematic naming. In this case, the LSB controls
+ // the highest value, so re reverse the order of the vector.
+ // Note the these signals are active low.
+
+ //Rx0 DSA control (domain: ctrl_reg_clk)
+ output wire [1:4] rx0_dsa1_n,
+ output wire [1:4] rx0_dsa2_n,
+ output wire [1:4] rx0_dsa3_a_n,
+ output wire [1:4] rx0_dsa3_b_n,
+
+ //Rx1 DSA control (domain: ctrl_reg_clk)
+ output wire [1:4] rx1_dsa1_n,
+ output wire [1:4] rx1_dsa2_n,
+ output wire [1:4] rx1_dsa3_a_n,
+ output wire [1:4] rx1_dsa3_b_n
+);
+
+ `include "../regmap/dsa_setup_regmap_utils.vh"
+ `include "../../../../../../lib/rfnoc/core/ctrlport.vh"
+
+ //---------------------------------------------------------------
+ // register bitfields
+ //---------------------------------------------------------------
+ reg [TX_DSA1_SIZE -1:0] tx0_dsa_1_reg = {TX_DSA1_SIZE{1'b1}};
+ reg [TX_DSA2_SIZE -1:0] tx0_dsa_2_reg = {TX_DSA2_SIZE{1'b1}};
+
+ reg [TX_DSA1_SIZE -1:0] tx1_dsa_1_reg = {TX_DSA1_SIZE{1'b1}};
+ reg [TX_DSA2_SIZE -1:0] tx1_dsa_2_reg = {TX_DSA2_SIZE{1'b1}};
+
+ reg [RX_DSA1_SIZE -1:0] rx0_dsa_1_reg = {RX_DSA1_SIZE{1'b1}};
+ reg [RX_DSA2_SIZE -1:0] rx0_dsa_2_reg = {RX_DSA2_SIZE{1'b1}};
+ reg [RX_DSA3_A_SIZE -1:0] rx0_dsa_3_a_reg = {RX_DSA3_A_SIZE{1'b1}};
+ reg [RX_DSA3_B_SIZE -1:0] rx0_dsa_3_b_reg = {RX_DSA3_B_SIZE{1'b1}};
+
+ reg [RX_DSA1_SIZE -1:0] rx1_dsa_1_reg = {RX_DSA1_SIZE{1'b1}};
+ reg [RX_DSA2_SIZE -1:0] rx1_dsa_2_reg = {RX_DSA2_SIZE{1'b1}};
+ reg [RX_DSA3_A_SIZE -1:0] rx1_dsa_3_a_reg = {RX_DSA3_A_SIZE{1'b1}};
+ reg [RX_DSA3_B_SIZE -1:0] rx1_dsa_3_b_reg = {RX_DSA3_B_SIZE{1'b1}};
+
+ //---------------------------------------------------------------
+ // ATR memory signals
+ //---------------------------------------------------------------
+ reg ram_tx0_wea = 1'b0;
+ wire [31:0] ram_tx0_doa;
+ wire [31:0] ram_tx0_dob;
+ reg ram_tx1_wea = 1'b0;
+ wire [31:0] ram_tx1_doa;
+ wire [31:0] ram_tx1_dob;
+ reg ram_rx0_wea = 1'b0;
+ wire [31:0] ram_rx0_doa;
+ wire [31:0] ram_rx0_dob;
+ reg ram_rx1_wea = 1'b0;
+ wire [31:0] ram_rx1_doa;
+ wire [31:0] ram_rx1_dob;
+
+ reg table_tx0_wea = 1'b0;
+ wire [31:0] table_tx0_doa;
+ reg table_tx1_wea = 1'b0;
+ wire [31:0] table_tx1_doa;
+ reg table_rx0_wea = 1'b0;
+ wire [31:0] table_rx0_doa;
+ reg table_rx1_wea = 1'b0;
+ wire [31:0] table_rx1_doa;
+
+ //---------------------------------------------------------------
+ // Handling of CtrlPort
+ //---------------------------------------------------------------
+ // Check of request address is targeted for this module.
+ wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) && (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS);
+ // Read request shift register to align memory read and response generation.
+ reg [ 1:0] read_req_shift_reg = 2'b0;
+ // Write request shift register to align gain table memory read and ATR memory
+ // write operation.
+ reg [ 1:0] write_req_shift_reg = 2'b0;
+ // Mask out 8 bits for ATR configurations to be able to compare all ATR
+ // configurations against the same base register address.
+ wire [31:0] register_base_address = {s_ctrlport_req_addr[19:10], 8'b0, s_ctrlport_req_addr[1:0]};
+ // Extract masked out bits from the address, which represent the register
+ // array index = ATR configuration index
+ wire [ 7:0] register_index = s_ctrlport_req_addr[9:2];
+ // switch between CtrlPort data and gain table data for ATR memories
+ reg select_gain_table = 1'b0;
+
+ always @(posedge ctrlport_clk) begin
+ // reset internal registers and responses
+ if (ctrlport_rst) begin
+ s_ctrlport_resp_ack <= 1'b0;
+
+ read_req_shift_reg <= 2'b0;
+ write_req_shift_reg <= 2'b0;
+
+ ram_tx0_wea <= 1'b0;
+ ram_tx1_wea <= 1'b0;
+ ram_rx0_wea <= 1'b0;
+ ram_rx1_wea <= 1'b0;
+
+ table_tx0_wea <= 1'b0;
+ table_tx1_wea <= 1'b0;
+ table_rx0_wea <= 1'b0;
+ table_rx1_wea <= 1'b0;
+
+ select_gain_table <= 1'b0;
+
+ end else begin
+ // default assignments
+ read_req_shift_reg <= { read_req_shift_reg[0], s_ctrlport_req_rd};
+ write_req_shift_reg <= {write_req_shift_reg[0], s_ctrlport_req_wr};
+
+ ram_tx0_wea <= 1'b0;
+ ram_tx1_wea <= 1'b0;
+ ram_rx0_wea <= 1'b0;
+ ram_rx1_wea <= 1'b0;
+
+ table_tx0_wea <= 1'b0;
+ table_tx1_wea <= 1'b0;
+ table_rx0_wea <= 1'b0;
+ table_rx1_wea <= 1'b0;
+
+ select_gain_table <= 1'b0;
+
+ // Answer write requests delayed by 2 clock cycles. This compensated for
+ // register ram_addr and the memory internal address register to make sure
+ // gain table output data is up to date when forwarding data to ATR memory
+ if (write_req_shift_reg[1]) begin
+ // always issue an ack and no data
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ case (register_base_address)
+ BASE_ADDRESS + TX0_DSA_ATR(0): begin
+ ram_tx0_wea <= 1'b1;
+ end
+ BASE_ADDRESS + TX1_DSA_ATR(0): begin
+ ram_tx1_wea <= 1'b1;
+ end
+ BASE_ADDRESS + RX0_DSA_ATR(0): begin
+ ram_rx0_wea <= 1'b1;
+ end
+ BASE_ADDRESS + RX1_DSA_ATR(0): begin
+ ram_rx1_wea <= 1'b1;
+ end
+
+ BASE_ADDRESS + TX0_DSA_TABLE(0): begin
+ table_tx0_wea <= 1'b1;
+ end
+ BASE_ADDRESS + TX1_DSA_TABLE(0): begin
+ table_tx1_wea <= 1'b1;
+ end
+ BASE_ADDRESS + RX0_DSA_TABLE(0): begin
+ table_rx0_wea <= 1'b1;
+ end
+ BASE_ADDRESS + RX1_DSA_TABLE(0): begin
+ table_rx1_wea <= 1'b1;
+ end
+
+ BASE_ADDRESS + TX0_DSA_TABLE_SELECT(0): begin
+ ram_tx0_wea <= 1'b1;
+ select_gain_table <= 1'b1;
+ end
+ BASE_ADDRESS + TX1_DSA_TABLE_SELECT(0): begin
+ ram_tx1_wea <= 1'b1;
+ select_gain_table <= 1'b1;
+ end
+ BASE_ADDRESS + RX0_DSA_TABLE_SELECT(0): begin
+ ram_rx0_wea <= 1'b1;
+ select_gain_table <= 1'b1;
+ end
+ BASE_ADDRESS + RX1_DSA_TABLE_SELECT(0): begin
+ ram_rx1_wea <= 1'b1;
+ select_gain_table <= 1'b1;
+ end
+
+ // error on undefined address
+ default: begin
+ 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
+
+ // Answer read requests delayed by 2 clock cycles. This compensated for
+ // register ram_addr and the memory internal address register to make sure
+ // ram_ch0_doa is up to date when generating the response.
+ end else if (read_req_shift_reg[1]) begin
+ // default assumption: valid request
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+ s_ctrlport_resp_data <= {32{1'b0}};
+
+ case (register_base_address)
+ BASE_ADDRESS + TX0_DSA_ATR(0): begin
+ s_ctrlport_resp_data <= ram_tx0_doa & TX_DSA_CONTROL_MASK;
+ end
+ BASE_ADDRESS + TX1_DSA_ATR(0): begin
+ s_ctrlport_resp_data <= ram_tx1_doa & TX_DSA_CONTROL_MASK;
+ end
+ BASE_ADDRESS + RX0_DSA_ATR(0): begin
+ s_ctrlport_resp_data <= ram_rx0_doa & RX_DSA_CONTROL_MASK;
+ end
+ BASE_ADDRESS + RX1_DSA_ATR(0): begin
+ s_ctrlport_resp_data <= ram_rx1_doa & RX_DSA_CONTROL_MASK;
+ end
+ BASE_ADDRESS + TX0_DSA_TABLE(0): begin
+ s_ctrlport_resp_data <= table_tx0_doa & TX_DSA_CONTROL_MASK;
+ end
+ BASE_ADDRESS + TX1_DSA_TABLE(0): begin
+ s_ctrlport_resp_data <= table_tx1_doa & TX_DSA_CONTROL_MASK;
+ end
+ BASE_ADDRESS + RX0_DSA_TABLE(0): begin
+ s_ctrlport_resp_data <= table_rx0_doa & RX_DSA_CONTROL_MASK;
+ end
+ BASE_ADDRESS + RX1_DSA_TABLE(0): begin
+ s_ctrlport_resp_data <= table_rx1_doa & RX_DSA_CONTROL_MASK;
+ end
+
+ default: begin
+ 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
+
+ // no request
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+ // register without reset
+ reg [ 7:0] ram_addr = 8'b0;
+ reg [ 7:0] gain_table_addr = 8'b0;
+ reg [31:0] ram_datain = 32'b0;
+ always @(posedge ctrlport_clk) begin
+ // Capture CtrlPort data and address on requests as only in this clock cycle
+ // the data is valid.
+ if (s_ctrlport_req_wr || s_ctrlport_req_rd) begin
+ ram_addr <= register_index;
+ ram_datain <= s_ctrlport_req_data;
+
+ case (register_base_address)
+ BASE_ADDRESS + TX0_DSA_TABLE_SELECT(0),
+ BASE_ADDRESS + TX1_DSA_TABLE_SELECT(0),
+ BASE_ADDRESS + RX0_DSA_TABLE_SELECT(0),
+ BASE_ADDRESS + RX1_DSA_TABLE_SELECT(0): begin
+ gain_table_addr <= s_ctrlport_req_data[TABLE_INDEX_MSB:TABLE_INDEX];
+ end
+ default: begin
+ gain_table_addr <= register_index;
+ end
+ endcase
+ end
+
+ // outputs
+ tx0_dsa_1_reg <= ram_tx0_dob[ TX_DSA1_MSB : TX_DSA1];
+ tx0_dsa_2_reg <= ram_tx0_dob[ TX_DSA2_MSB : TX_DSA2];
+
+ tx1_dsa_1_reg <= ram_tx1_dob[ TX_DSA1_MSB : TX_DSA1];
+ tx1_dsa_2_reg <= ram_tx1_dob[ TX_DSA2_MSB : TX_DSA2];
+
+ rx0_dsa_1_reg <= ram_rx0_dob[ RX_DSA1_MSB : RX_DSA1];
+ rx0_dsa_2_reg <= ram_rx0_dob[ RX_DSA2_MSB : RX_DSA2];
+ rx0_dsa_3_a_reg <= ram_rx0_dob[RX_DSA3_A_MSB : RX_DSA3_A];
+ rx0_dsa_3_b_reg <= ram_rx0_dob[RX_DSA3_B_MSB : RX_DSA3_B];
+
+ rx1_dsa_1_reg <= ram_rx1_dob[ RX_DSA1_MSB : RX_DSA1];
+ rx1_dsa_2_reg <= ram_rx1_dob[ RX_DSA2_MSB : RX_DSA2];
+ rx1_dsa_3_a_reg <= ram_rx1_dob[RX_DSA3_A_MSB : RX_DSA3_A];
+ rx1_dsa_3_b_reg <= ram_rx1_dob[RX_DSA3_B_MSB : RX_DSA3_B];
+ end
+
+ assign tx0_dsa1[6:2] = tx0_dsa_1_reg;
+ assign tx0_dsa2[6:2] = tx0_dsa_2_reg;
+
+ assign tx1_dsa1[6:2] = tx1_dsa_1_reg;
+ assign tx1_dsa2[6:2] = tx1_dsa_2_reg;
+
+ //Rx DSAs behave differently from Tx DSAs
+ //Flip MSB/LSB, and invert
+
+ genvar vi;
+ // take care of inverting the active low logic and bit-reversing
+ // the DSA controls for RX paths.
+ generate
+ for (vi=1; vi<=4; vi=vi+1) begin : reverselogic
+ // [1:4] [3:0]
+ assign rx0_dsa1_n[vi] = !rx0_dsa_1_reg[4-vi];
+ assign rx0_dsa2_n[vi] = !rx0_dsa_2_reg[4-vi];
+ assign rx0_dsa3_a_n[vi] = !rx0_dsa_3_a_reg[4-vi];
+ assign rx0_dsa3_b_n[vi] = !rx0_dsa_3_b_reg[4-vi];
+
+ assign rx1_dsa1_n[vi] = !rx1_dsa_1_reg[4-vi];
+ assign rx1_dsa2_n[vi] = !rx1_dsa_2_reg[4-vi];
+ assign rx1_dsa3_a_n[vi] = !rx1_dsa_3_a_reg[4-vi];
+ assign rx1_dsa3_b_n[vi] = !rx1_dsa_3_b_reg[4-vi];
+ end
+ endgenerate
+
+ //---------------------------------------------------------------
+ // ATR memories
+ //---------------------------------------------------------------
+ // Choose data source for ATR configurations from CtrlPort or gain table.
+ wire [31:0] ram_rx0_dia = select_gain_table ? table_rx0_doa : ram_datain;
+ wire [31:0] ram_rx1_dia = select_gain_table ? table_rx1_doa : ram_datain;
+ wire [31:0] ram_tx0_dia = select_gain_table ? table_tx0_doa : ram_datain;
+ wire [31:0] ram_tx1_dia = select_gain_table ? table_tx1_doa : ram_datain;
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/tx_dsa_defaults.hex")
+ ) ram_tx0_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_tx0_wea),
+ .addra (ram_addr),
+ .dia (ram_tx0_dia),
+ .doa (ram_tx0_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf0),
+ .dib (0),
+ .dob (ram_tx0_dob)
+ );
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/tx_dsa_defaults.hex")
+ ) ram_tx1_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_tx1_wea),
+ .addra (ram_addr),
+ .dia (ram_tx1_dia),
+ .doa (ram_tx1_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf1),
+ .dib (0),
+ .dob (ram_tx1_dob)
+ );
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/rx_dsa_defaults.hex")
+ ) ram_rx0_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_rx0_wea),
+ .addra (ram_addr),
+ .dia (ram_rx0_dia),
+ .doa (ram_rx0_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf0),
+ .dib (0),
+ .dob (ram_rx0_dob)
+ );
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/rx_dsa_defaults.hex")
+ ) ram_rx1_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_rx1_wea),
+ .addra (ram_addr),
+ .dia (ram_rx1_dia),
+ .doa (ram_rx1_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf1),
+ .dib (0),
+ .dob (ram_rx1_dob)
+ );
+
+ //---------------------------------------------------------------
+ // Gain tables
+ //---------------------------------------------------------------
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/tx_dsa_defaults.hex")
+ ) table_tx0_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (table_tx0_wea),
+ .addra (gain_table_addr),
+ .dia (ram_datain),
+ .doa (table_tx0_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (8'b0),
+ .dib (32'b0),
+ .dob ()
+ );
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/tx_dsa_defaults.hex")
+ ) table_tx1_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (table_tx1_wea),
+ .addra (gain_table_addr),
+ .dia (ram_datain),
+ .doa (table_tx1_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (8'b0),
+ .dib (32'b0),
+ .dob ()
+ );
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/rx_dsa_defaults.hex")
+ ) table_rx0_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (table_rx0_wea),
+ .addra (gain_table_addr),
+ .dia (ram_datain),
+ .doa (table_rx0_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (8'b0),
+ .dib (32'b0),
+ .dob ()
+ );
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/rx_dsa_defaults.hex")
+ ) table_rx1_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (table_rx1_wea),
+ .addra (gain_table_addr),
+ .dia (ram_datain),
+ .doa (table_rx1_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (8'b0),
+ .dib (32'b0),
+ .dob ()
+ );
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="DSA_SETUP_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true" markdown="true">
+// <group name="DSA_SETUP_REGISTERS">
+// <info>
+// The following registers control the digital step attenuators (DSA).
+//
+// There are two ways to set the DSA values, which are applied to the DB ICs.
+//
+// 1. The ...DSA_ATR registers can be used to access the raw
+// values of each ATR configuration.
+//
+// 2. Gain tables can be used as intermediate step to abstract from the
+// raw DB values. This gain table can be modified using the ...DSA_TABLE
+// registers according to the content of the registers from the first
+// option. Initially each gain table is empty (all zeros). Each gain
+// table entry can be accessed at any time. Once the table is filled with
+// values the ...DSA_TABLE_SELECT registers can be used to get one gain
+// table entry with index TABLE_INDEX and write it to the appropriate ATR
+// configuration given by the address (see _show extended info_ link below
+// the register array headlines)
+// </info>
+// <regtype name="TX_DSA_CONTROL" size="32" attributes="Readable|Writable">
+// <bitfield name="TX_DSA1" range="4..0" initialvalue="31">
+// <info>
+// Sets the attenuation level for Tx DSA1. The resolution attenuation is 1 dB, with an attenuation range from 1 to 31 dB. Write this field with the
+// attenuation setting desired. Writing zero to this field results in no attenuation (different insertion loss expected for different frequency ranges).
+// </info>
+// </bitfield>
+// <bitfield name="TX_DSA2" range="12..8" initialvalue="31">
+// <info>
+// Sets the attenuation level for Tx DSA2. The resolution attenuation is 1 dB, with an attenuation range from 1 to 31 dB. Write this field with the
+// attenuation setting desired. Writing zero to this field results in no attenuation (different insertion loss expected for different frequency ranges).
+// </info>
+// </bitfield>
+// </regtype>
+//
+// <regtype name="RX_DSA_CONTROL" size="32" attributes="Readable|Writable">
+// <bitfield name="RX_DSA1" range="3..0" initialvalue="15">
+// <info>
+// Sets the attenuation level for Rx DSA1. The resolution attenuation is 1 dB, with an attenuation range from 1 to 15 dB. Write this field with the
+// attenuation setting desired. Writing zero to this field results in no attenuation (different insertion loss expected for different frequency ranges).
+// </info>
+// </bitfield>
+// <bitfield name="RX_DSA2" range="7..4" initialvalue="15">
+// <info>
+// Sets the attenuation level for Rx DSA2. The resolution attenuation is 1 dB, with an attenuation range from 1 to 15 dB. Write this field with the
+// attenuation setting desired. Writing zero to this field results in no attenuation (different insertion loss expected for different frequency ranges).
+// </info>
+// </bitfield>
+// <bitfield name="RX_DSA3_A" range="11..8" initialvalue="15">
+// <info>
+// Sets the attenuation level for Rx DSA 3a and 3b. The resolution attenuation is 1 dB, with an attenuation range from 1 to 15 dB. Write this field with the
+// attenuation setting desired. Writing zero to this field results in no attenuation (different insertion loss expected for different frequency ranges).
+// </info>
+// </bitfield>
+// <bitfield name="RX_DSA3_B" range="15..12" initialvalue="15">
+// <info>
+// Sets the attenuation level for Rx DSA 3b(to input of IF1 Amplifier 2). The resolution attenuation is 1 dB, with an attenuation range from 1 to 15 dB. Write this field with the
+// attenuation setting desired. Writing zero to this field results in no attenuation (different insertion loss expected for different frequency ranges).. {BR/}
+// </info>
+// </bitfield>
+// </regtype>
+//
+// <regtype name="DSA_TABLE_CONTROL" size="32" attributes="Writable">
+// <bitfield name="TABLE_INDEX" range="7..0">
+// <info>
+// Gain table index to be used for getting the raw attenuation values.
+// </info>
+// </bitfield>
+// </regtype>
+//
+// <register name="TX0_DSA_ATR" offset="0x000" count="256" step="4" typename="TX_DSA_CONTROL">
+// <info>
+// Controls the Tx0 DSAs by accessing the raw attenuation levels.
+//
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+// <register name="TX1_DSA_ATR" offset="0x400" count="256" step="4" typename="TX_DSA_CONTROL">
+// <info>
+// Controls the Tx1 DSAs by accessing the raw attenuation levels.
+//
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+//
+// <register name="RX0_DSA_ATR" offset="0x800" count="256" step="4" typename="RX_DSA_CONTROL">
+// <info>
+// Controls the Rx0 DSAs by accessing the raw attenuation levels.
+//
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+// <register name="RX1_DSA_ATR" offset="0xC00" count="256" step="4" typename="RX_DSA_CONTROL">
+// <info>
+// Controls the Rx1 DSAs by accessing the raw attenuation levels.
+//
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+//
+// <register name="TX0_DSA_TABLE_SELECT" offset="0x1000" count="256" step="4" typename="DSA_TABLE_CONTROL">
+// <info>
+// Controls the Tx0 DSAs by using the gain table to translate the table
+// index to raw attenuation levels. The register offset (i) is targeting
+// an ATR configuration to store the values from the gain table.
+// </info>
+// </register>
+// <register name="TX1_DSA_TABLE_SELECT" offset="0x1400" count="256" step="4" typename="DSA_TABLE_CONTROL">
+// <info>
+// Controls the Tx1 DSAs by using the gain table to translate the table
+// index to raw attenuation levels. The register offset (i) is targeting
+// an ATR configuration to store the values from the gain table.
+// </info>
+// </register>
+// <register name="RX0_DSA_TABLE_SELECT" offset="0x1800" count="256" step="4" typename="DSA_TABLE_CONTROL">
+// <info>
+// Controls the Rx0 DSAs by using the gain table to translate the table
+// index to raw attenuation levels. The register offset (i) is targeting
+// an ATR configuration to store the values from the gain table.
+// </info>
+// </register>
+// <register name="RX1_DSA_TABLE_SELECT" offset="0x1C00" count="256" step="4" typename="DSA_TABLE_CONTROL">
+// <info>
+// Controls the Rx1 DSAs by using the gain table to translate the table
+// index to raw attenuation levels. The register offset (i) is targeting
+// an ATR configuration to store the values from the gain table.
+// </info>
+// </register>
+//
+// <register name="TX0_DSA_TABLE" offset="0x2000" count="256" step="4" typename="TX_DSA_CONTROL">
+// <info>
+// Provides access to the gain table for Tx0.
+//
+// Each entry i will be saved in the gain table without any implications
+// on HW. Enables SW to use the table index in @.TX0_DSA_TABLE_SELECT to
+// modify the ATR configurations.
+// </info>
+// </register>
+// <register name="TX1_DSA_TABLE" offset="0x2400" count="256" step="4" typename="TX_DSA_CONTROL">
+// <info>
+// Provides access to the gain table for Tx1.
+//
+// Each entry i will be saved in the gain table without any implications
+// on HW. Enables SW to use the table index in @.TX1_DSA_TABLE_SELECT to
+// modify the ATR configurations.
+// </info>
+// </register>
+// <register name="RX0_DSA_TABLE" offset="0x2800" count="256" step="4" typename="RX_DSA_CONTROL">
+// <info>
+// Provides access to the gain table for Rx0.
+//
+// Each entry i will be saved in the gain table without any implications
+// on HW. Enables SW to use the table index in @.RX0_DSA_TABLE_SELECT to
+// modify the ATR configurations.
+// </info>
+// </register>
+// <register name="RX1_DSA_TABLE" offset="0x2C00" count="256" step="4" typename="RX_DSA_CONTROL">
+// <info>
+// Provides access to the gain table for Rx1.
+//
+// Each entry i will be saved in the gain table without any implications
+// on HW. Enables SW to use the table index in @.RX1_DSA_TABLE_SELECT to
+// modify the ATR configurations.
+// </info>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/led_control.v b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/led_control.v
new file mode 100644
index 000000000..5663d348e
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/led_control.v
@@ -0,0 +1,259 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: led_control
+//
+// Description:
+// Implements control over LED state via CtrlPort. The default state
+// has the LEDs disabled. Uses RAM to store multiple ATR configurations.
+//
+
+`default_nettype none
+
+module led_control #(
+ parameter [19:0] BASE_ADDRESS = 0,
+ parameter [19:0] SIZE_ADDRESS = 0
+) (
+ // Clock and reset
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+
+ // Request
+ 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,
+ // Response
+ output reg s_ctrlport_resp_ack,
+ output reg [ 1:0] s_ctrlport_resp_status = 2'b0,
+ output reg [31:0] s_ctrlport_resp_data = 32'b0,
+
+ // LED Control (domain: ctrlport_clk)
+ output reg ch0_rx2_led,
+ output reg ch0_tx_led,
+ output reg ch0_rx_led,
+ output reg ch1_rx2_led,
+ output reg ch1_tx_led,
+ output reg ch1_rx_led,
+
+ // ATR switching
+ input wire [ 7:0] atr_config_rf0,
+ input wire [ 7:0] atr_config_rf1
+);
+
+ `include "../regmap/led_setup_regmap_utils.vh"
+ `include "../../../../../../lib/rfnoc/core/ctrlport.vh"
+
+ //---------------------------------------------------------------
+ // ATR memory signals
+ //---------------------------------------------------------------
+ reg ram_ch0_wea;
+ wire [31:0] ram_ch0_doa;
+ wire [31:0] ram_ch0_dob;
+
+ reg ram_ch1_wea;
+ wire [31:0] ram_ch1_dob;
+
+ //---------------------------------------------------------------
+ // Handling of CtrlPort
+ //---------------------------------------------------------------
+ // Check of request address is targeted for this module.
+ wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) && (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS);
+ // Read request shift register to align memory read and response generation.
+ reg [ 1:0] read_req_shift_reg = 2'b0;
+ // Mask out 8 bits for ATR configurations to be able to compare all ATR
+ // configurations against the same base register address.
+ wire [31:0] register_base_address = {s_ctrlport_req_addr[19:10], 8'b0, s_ctrlport_req_addr[1:0]};
+ // Extract masked out bits from the address, which represent the register
+ // array index = ATR configuration index
+ wire [ 7:0] register_index = s_ctrlport_req_addr[9:2];
+
+ always @(posedge ctrlport_clk) begin
+ // reset internal registers and responses
+ if (ctrlport_rst) begin
+ s_ctrlport_resp_ack <= 1'b0;
+
+ read_req_shift_reg <= 2'b0;
+
+ ram_ch0_wea <= 1'b0;
+ ram_ch1_wea <= 1'b0;
+
+ end else begin
+ // default assignments
+ read_req_shift_reg <= {read_req_shift_reg[0], s_ctrlport_req_rd};
+
+ ram_ch0_wea <= 1'b0;
+ ram_ch1_wea <= 1'b0;
+
+ // write requests
+ if (s_ctrlport_req_wr) begin
+ // always issue an ack and no data
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ case (register_base_address)
+ BASE_ADDRESS + LED_CONTROL(0): begin
+ ram_ch0_wea <= 1'b1;
+ ram_ch1_wea <= 1'b1;
+ end
+
+ // error on undefined address
+ default: begin
+ 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
+
+ // Answer read requests delayed by 2 clock cycles. This compensated for
+ // register ram_addr and the memory internal address register to make sure
+ // ram_ch0_doa is up to date when generating the response.
+ end else if (read_req_shift_reg[1]) begin
+ // default assumption: valid request
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+ s_ctrlport_resp_data <= {32{1'b0}};
+
+ case (register_base_address)
+ BASE_ADDRESS + LED_CONTROL(0): begin
+ s_ctrlport_resp_data <= ram_ch0_doa & LED_CONTROL_TYPE_MASK;
+ end
+
+ // error on undefined address
+ default: begin
+ 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
+
+ // no request
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+ // register without reset
+ reg [ 7:0] ram_addr = 8'b0;
+ reg [31:0] ram_datain = 32'b0;
+ always @(posedge ctrlport_clk) begin
+ // memories
+ ram_addr <= register_index;
+ ram_datain <= s_ctrlport_req_data;
+
+ //outputs
+ ch0_rx2_led <= ram_ch0_dob[CH0_RX2_LED_EN];
+ ch0_tx_led <= ram_ch0_dob[CH0_TRX1_LED_EN + 1];
+ ch0_rx_led <= ram_ch0_dob[CH0_TRX1_LED_EN + 0];
+
+ ch1_rx2_led <= ram_ch1_dob[CH1_RX2_LED_EN];
+ ch1_tx_led <= ram_ch1_dob[CH1_TRX1_LED_EN + 1];
+ ch1_rx_led <= ram_ch1_dob[CH1_TRX1_LED_EN + 0];
+ end
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("")
+ ) ram_ch0_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_ch0_wea),
+ .addra (ram_addr),
+ .dia (ram_datain),
+ .doa (ram_ch0_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf0),
+ .dib (0),
+ .dob (ram_ch0_dob)
+ );
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("")
+ ) ram_ch1_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_ch1_wea),
+ .addra (ram_addr),
+ .dia (ram_datain),
+ .doa (),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf1),
+ .dib (0),
+ .dob (ram_ch1_dob));
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="LED_SETUP_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
+// <group name="LED_SETUP_REGISTERS">
+// <info>
+// Contains registers that control the LEDs.
+// </info>
+// <regtype name="LED_CONTROL_TYPE" size="32" attributes="Readable|Writable">
+// <info>
+// Defines LED functionality.
+// </info>
+// <bitfield name="CH0_RX2_LED_EN" range="0" initialvalue="0">
+// <info>
+// Enables the Ch0 Rx2 Green LED
+// </info>
+// </bitfield>
+// <bitfield name="CH0_TRX1_LED_EN" range="2..1" initialvalue="0">
+// <info>
+// This bitfield controls the RG LED{BR/}
+// Bit 6 controls the Ch0 Rx Green LED{BR/}
+// Bit 7 controls the Ch0 Tx Red LED{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="CH1_RX2_LED_EN" range="16" initialvalue="0">
+// <info>
+// Enables the Ch1 Rx2 Green LED
+// </info>
+// </bitfield>
+// <bitfield name="CH1_TRX1_LED_EN" range="18..17" initialvalue="0">
+// <info>
+// This bitfield controls the RG LED{BR/}
+// Bit 15 controls the Ch1 Rx Green LED{BR/}
+// Bit 14 controls the Ch1 Tx Red LED{BR/}
+// </info>
+// </bitfield>
+// </regtype>
+//
+// <register name="LED_CONTROL" offset="0x0" count="256" step="4" typename="LED_CONTROL_TYPE">
+// <info>
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/lo_control.v b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/lo_control.v
new file mode 100644
index 000000000..67a2e8c79
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/lo_control.v
@@ -0,0 +1,568 @@
+//
+// Copyright 2021 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: lo_control
+//
+// Description:
+// Implements control over signals interacting with LMX2572 chips on the
+// daughterboard. This includes a CtrlPort based control over a SPI master
+// that distributes transactions across the SPI buses of the LMX2572, as
+// well as the capability to synchronously generate pulses to their SYNC pins.
+//
+
+`default_nettype none
+
+module lo_control #(
+ parameter [19:0] BASE_ADDRESS = 0,
+ parameter [19:0] SIZE_ADDRESS = 0
+) (
+ // Request
+ 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,
+ // Response
+ output reg s_ctrlport_resp_ack,
+ output reg [ 1:0] s_ctrlport_resp_status,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ //reg clk domain
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+
+ // LO SPI for LMX2572
+ input wire [7:0] miso,
+ output reg [7:0] ss = {8{1'b1}},
+ output wire sclk,
+ output reg mosi = 1'b0,
+
+ // Incoming SYNC
+ input wire mb_synth_sync,
+
+ // SYNC for LMX2572
+ output wire tx0_lo1_sync,
+ output wire tx0_lo2_sync,
+ output wire tx1_lo1_sync,
+ output wire tx1_lo2_sync,
+ output wire rx0_lo1_sync,
+ output wire rx0_lo2_sync,
+ output wire rx1_lo1_sync,
+ output wire rx1_lo2_sync
+);
+
+ `include "../regmap/lo_control_regmap_utils.vh"
+ `include "../../../../../../lib/rfnoc/core/ctrlport.vh"
+
+ //---------------------------------------------------------------
+ // register bitfields
+ //---------------------------------------------------------------
+
+ reg spi_start;
+ reg [LO_SELECT_SIZE-1:0] spi_cs;
+ reg spi_rd;
+ reg [LO_SPI_WT_ADDR_SIZE-1:0] spi_addr;
+ reg [LO_SPI_WT_DATA_SIZE-1:0] spi_data;
+ reg spi_data_valid;
+ reg spi_ready = 1'b1;
+ reg [LO_SPI_RD_DATA_SIZE-1:0] spi_rd_data;
+ reg [LO_CHIP_SELECT_SIZE-1:0] lo_sync_reg = 8'b0;
+ reg bypass_sync = 1'b0;
+
+ //---------------------------------------------------------------
+ // Handling of CtrlPort
+ //---------------------------------------------------------------
+ wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) && (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS);
+
+ always @(posedge ctrlport_clk) begin
+ // reset internal registers and responses
+ if (ctrlport_rst) begin
+ spi_start <= 1'b0;
+ spi_cs <= 3'b0;
+ spi_rd <= 1'b0;
+ spi_addr <= {LO_SPI_WT_ADDR_SIZE{1'b0}};
+ spi_data <= {LO_SPI_WT_DATA_SIZE{1'b0}};
+ lo_sync_reg <= 8'b0;
+ bypass_sync <= 1'b0;
+
+ s_ctrlport_resp_ack <= 1'b0;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ end else begin
+
+ //send only a pulse
+ spi_start <= 1'b0;
+
+ //pulse sync lines for a maximum of one cycle
+ lo_sync_reg <= 8'b0;
+
+ // write requests
+ if (s_ctrlport_req_wr) begin
+ // always issue an ack and no data
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + LO_SPI_SETUP: begin
+ spi_start <= s_ctrlport_req_data[LO_SPI_START_TRANSACTION];
+ spi_cs <= s_ctrlport_req_data[LO_SELECT_MSB : LO_SELECT];
+ spi_rd <= s_ctrlport_req_data[LO_SPI_RD];
+ spi_addr <= s_ctrlport_req_data[LO_SPI_WT_ADDR_MSB : LO_SPI_WT_ADDR];
+ spi_data <= s_ctrlport_req_data[LO_SPI_WT_DATA_MSB : LO_SPI_WT_DATA];
+ end
+
+ BASE_ADDRESS + LO_PULSE_SYNC: begin
+ bypass_sync <= s_ctrlport_req_data[BYPASS_SYNC_REGISTER];
+ lo_sync_reg[TX0_LO1] <= s_ctrlport_req_data[PULSE_TX0_LO1_SYNC];
+ lo_sync_reg[TX0_LO2] <= s_ctrlport_req_data[PULSE_TX0_LO2_SYNC];
+ lo_sync_reg[TX1_LO1] <= s_ctrlport_req_data[PULSE_TX1_LO1_SYNC];
+ lo_sync_reg[TX1_LO2] <= s_ctrlport_req_data[PULSE_TX1_LO2_SYNC];
+ lo_sync_reg[RX0_LO1] <= s_ctrlport_req_data[PULSE_RX0_LO1_SYNC];
+ lo_sync_reg[RX0_LO2] <= s_ctrlport_req_data[PULSE_RX0_LO2_SYNC];
+ lo_sync_reg[RX1_LO1] <= s_ctrlport_req_data[PULSE_RX1_LO1_SYNC];
+ lo_sync_reg[RX1_LO2] <= s_ctrlport_req_data[PULSE_RX1_LO2_SYNC];
+ end
+
+ // error on undefined address
+ default: begin
+ 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 requests
+ end else if (s_ctrlport_req_rd) begin
+ // default assumption: valid request
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+ s_ctrlport_resp_data <= {32{1'b0}};
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + LO_SPI_STATUS: begin //same address as *_status form regmap
+ s_ctrlport_resp_data[LO_SPI_DATA_VALID] <= spi_data_valid;
+ s_ctrlport_resp_data[LO_SELECT_MSB : LO_SELECT] <= spi_cs;
+ s_ctrlport_resp_data[LO_SPI_READY] <= spi_ready;
+ s_ctrlport_resp_data[LO_SPI_RD_ADDR_MSB : LO_SPI_RD_ADDR] <= spi_addr;
+ s_ctrlport_resp_data[LO_SPI_RD_DATA_MSB : LO_SPI_RD_DATA] <= spi_rd_data;
+ end
+
+ // error on undefined address
+ default: begin
+ s_ctrlport_resp_data <= {32{1'b0}};
+ 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
+
+ // no request
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+ // Spi_top controls
+ reg [4:0] wb_adr_i;
+ reg wb_cyc_i;
+ reg [31:0] wb_dat_i;
+ reg wb_we_i;
+
+ // Spi_top outputs
+ wire wb_ack_o;
+ wire [31:0] wb_dat_o;
+ wire wb_int_o;
+
+ wire [15:0] ss_pad_o;
+ wire mosi_pad_o;
+ wire miso_pad_i;
+
+ // There is a hold requirement of 10ns on the output path.
+ // To ease meeting this requirement without to much routing added to the lines
+ // these registers shift the output by 10ns (half 50 MHz period).
+ always @(negedge ctrlport_clk) begin
+ if (ctrlport_rst) begin
+ ss <= {8{1'b1}};
+ mosi <= 1'b0;
+ end
+ else begin
+ ss <= ss_pad_o[LO_CHIP_SELECT_SIZE-1:0];
+ mosi <= mosi_pad_o;
+ end
+ end
+
+ assign miso_pad_i = ( ~ss[TX0_LO1] ) ? miso[TX0_LO1] :
+ ( ~ss[TX0_LO2] ) ? miso[TX0_LO2] :
+ ( ~ss[TX1_LO1] ) ? miso[TX1_LO1] :
+ ( ~ss[TX1_LO2] ) ? miso[TX1_LO2] :
+ ( ~ss[RX0_LO1] ) ? miso[RX0_LO1] :
+ ( ~ss[RX0_LO2] ) ? miso[RX0_LO2] :
+ ( ~ss[RX1_LO1] ) ? miso[RX1_LO1] :
+ ( ~ss[RX1_LO2] ) ? miso[RX1_LO2] :
+ 1'b0;
+
+ // Import offsets and functions to interact with spi_top
+ `include "utils/spi_control_utils.vh"
+
+ // The sclk signal generated by this spi engine will be constrained to be 1/4 of the
+ // ctrl port frequency. The effective frequency of the clock output of spi_top is determined
+ // by the equation wb_clk_i/((CLOCK_DIVIDER_VALUE+1)*2), so the value required for the clock
+ // divider register is 1.
+ localparam CLOCK_DIVIDER_VALUE = 32'h1;
+
+ // Base Configuration for the control register. To start a transaction
+ // modify this value to include the GO_BUSY bit set to high. The mapping of the
+ // macro goes as follows:
+ `define CONTROL_DATA(GO_BUSY) { 18'b0, /* Reserved */ \
+ 1'b1, /* Automatic SS(13) */ \
+ 1'b1, /* Interrupt Enable */ \
+ 1'b0, /* LSB */ \
+ 1'b1, /* TX_NEG (10) */ \
+ 1'b0, /* RX_NEG (9) */ \
+ GO_BUSY, /* GO_BUSY (8) */ \
+ 1'b0, /* Reserved (7) */ \
+ 7'd24} /* Length of spi transaction */
+
+ // Declare the different state-machine states.
+ localparam RESET_STATE = 0;
+ localparam CONFIG_DIVIDER = 1;
+ localparam INIT_CONTROL = 2;
+ localparam IDLE = 3;
+ localparam LOAD_CS = 4;
+ localparam LOAD_DATA = 5;
+ localparam SEND_TRANSACTION = 6;
+ localparam WAIT_FOR_COMPLETION = 7;
+ localparam RETRIEVE_DATA = 8;
+
+ // FSM state variable
+ reg [3:0] spi_state = RESET_STATE;
+
+ always @(posedge ctrlport_clk) begin
+ if (ctrlport_rst) begin
+ // SPI_STATUS for CtrlPort
+ spi_state <= RESET_STATE;
+ spi_ready <= 1'b1;
+ spi_data_valid <= 1'b0;
+ spi_rd_data <= {LO_SPI_RD_DATA_SIZE{1'b0}};
+
+ // SPI_TOP Bus access control.
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ wb_adr_i <= 5'h00;
+ wb_dat_i <= 32'h00;
+ end else begin
+ case (spi_state)
+ RESET_STATE:
+ if (spi_start) begin
+ spi_state <= CONFIG_DIVIDER;
+ end
+
+ // keep driving a write to the CLOCK_DIVIDER Register until access is acknowledged.
+ CONFIG_DIVIDER:
+ if (wb_ack_o) begin
+ spi_state <= INIT_CONTROL;
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end else begin
+ wb_we_i <= 1'b1;
+ wb_cyc_i <= 1'b1;
+ wb_adr_i <= CLOCK_DIVIDER_REG; // CLOCK DIVIDER register offset
+ wb_dat_i <= CLOCK_DIVIDER_VALUE;
+ end
+
+ // keep driving a write to the CONTROL Register until access is acknowledged.
+ INIT_CONTROL:
+ if (wb_ack_o) begin
+ spi_state <= LOAD_CS;
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end else begin
+ wb_we_i <= 1'b1;
+ wb_cyc_i <= 1'b1;
+ wb_adr_i <= CONTROL_REG; //CONTROL register offset
+ wb_dat_i <= `CONTROL_DATA(1'b0); //Write control register value with no GO_BUSY
+ end
+
+ // Wait for CtrlPort operation to trigger a SPI transaction.
+ IDLE :
+ if (spi_start) begin
+ spi_state <= LOAD_CS;
+ spi_ready <= 1'b0;
+ spi_data_valid <= 1'b0;
+ // Clear data to be written next. This will make it so that the next state(LOAD_CS)
+ // will only have to set the bits of the pertinent SS lines.
+ wb_dat_i <= 32'h00;
+ end else begin
+ spi_ready <= 1'b1;
+ wb_dat_i <= 32'h00;
+ end
+
+ // keep driving a write to the SLAVE SELECT Register until access is acknowledged.
+ LOAD_CS :
+ if (wb_ack_o) begin
+ spi_state <= LOAD_DATA;
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end else begin
+ wb_we_i <= 1'b1;
+ wb_cyc_i <= 1'b1;
+ wb_adr_i <= SS_REG; // SS Register offset.
+ wb_dat_i <= set_ss_bit(spi_cs); // Assign single bit.
+ end
+
+ // keep driving a write to the DATA TRANSMIT Register until access is acknowledged.
+ // This includes the combination of CMD+ADDR+DATA to be driven on the MOSI lines.
+ LOAD_DATA :
+ if (wb_ack_o) begin
+ spi_state <= SEND_TRANSACTION;
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end else begin
+ wb_we_i <= 1'b1;
+ wb_cyc_i <= 1'b1;
+ wb_adr_i <= TX_DATA_REG; // Data Transmit register offset.
+ wb_dat_i <= {
+ 8'b0,
+ spi_rd,
+ spi_addr,
+ spi_data
+ };
+ end
+
+ // Per indication in the SPI_TOP documentation, we write the same configuration as before to the
+ // CONTROL register, with the addition of the GO bit.
+ SEND_TRANSACTION :
+ if (wb_ack_o) begin
+ spi_state <= WAIT_FOR_COMPLETION;
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end else begin
+ wb_we_i <= 1'b1;
+ wb_cyc_i <= 1'b1;
+ wb_adr_i <= CONTROL_REG;
+ wb_dat_i <= `CONTROL_DATA(1'b1); //Write control register value with GO_BUSY set.
+ end
+
+ // This state waits until SPI access is complete
+ WAIT_FOR_COMPLETION:
+ if (wb_int_o) begin
+ if (spi_rd) begin // If reading, do an extra step
+ spi_state <= RETRIEVE_DATA;
+ end else begin
+ spi_state <= IDLE; // If not reading, wait for next transaction
+ end
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end else begin // Keep polling CONTROL register.
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end
+
+ RETRIEVE_DATA:
+ if (wb_ack_o) begin
+ spi_state <= IDLE; // as soon as data is available, record it and go back
+ spi_rd_data <= wb_dat_o[LO_SPI_RD_DATA_SIZE-1:0]; // to idle.
+ spi_data_valid <= 1'b1;
+ wb_we_i <= 1'b0;
+ wb_cyc_i <= 1'b0;
+ end else begin
+ wb_we_i <= 1'b0; // Drive bus access.
+ wb_cyc_i <= 1'b1;
+ wb_adr_i <= RX_DATA_REG; // DATA RETRIEVE Register offset
+ wb_dat_i <= {LO_SPI_WT_DATA_SIZE{1'b0}};
+ end
+
+ endcase
+ end
+ end
+
+ spi_top spi_top_i (
+ .wb_clk_i (ctrlport_clk),
+ .wb_rst_i (ctrlport_rst),
+ .wb_adr_i (wb_adr_i),
+ .wb_dat_i (wb_dat_i),
+ .wb_dat_o (wb_dat_o),
+ .wb_sel_i (4'hF),
+ .wb_we_i (wb_we_i),
+ .wb_stb_i (wb_cyc_i),
+ .wb_cyc_i (wb_cyc_i),
+ .wb_ack_o (wb_ack_o),
+ .wb_err_o (),
+ .wb_int_o (wb_int_o),
+ .ss_pad_o (ss_pad_o),
+ .sclk_pad_o (sclk),
+ .mosi_pad_o (mosi_pad_o),
+ .miso_pad_i (miso_pad_i));
+
+ reg mb_sync_reg = 1'b0;
+
+ // align incoming signal to clock
+ always @(posedge ctrlport_clk) begin
+ mb_sync_reg <= mb_synth_sync;
+ end
+
+ // Select between bypassing into input signal or registered pulse
+ assign tx0_lo1_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[TX0_LO1];
+ assign tx0_lo2_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[TX0_LO2];
+ assign tx1_lo1_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[TX1_LO1];
+ assign tx1_lo2_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[TX1_LO2];
+ assign rx0_lo1_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[RX0_LO1];
+ assign rx0_lo2_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[RX0_LO2];
+ assign rx1_lo1_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[RX1_LO1];
+ assign rx1_lo2_sync = bypass_sync ? mb_sync_reg : lo_sync_reg[RX1_LO2];
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="LO_CONTROL_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
+// <group name="LO_SPI_REGISTERS">
+// <info>
+// Controls the SPI transaction to the LMX2572
+// </info>
+// <enumeratedtype name="LO_CHIP_SELECT">
+// <value name="TX0_LO1" integer="0"/>
+// <value name="TX0_LO2" integer="1"/>
+// <value name="TX1_LO1" integer="2"/>
+// <value name="TX1_LO2" integer="3"/>
+// <value name="RX0_LO1" integer="4"/>
+// <value name="RX0_LO2" integer="5"/>
+// <value name="RX1_LO1" integer="6"/>
+// <value name="RX1_LO2" integer="7"/>
+// </enumeratedtype>
+// <register name="LO_SPI_SETUP" size="32" offset="0x00" attributes="Writable">
+// <info>
+// This register sets up the SPI transaction to read/write to/from to the LMX2572.
+// </info>
+// <bitfield name="LO_SPI_START_TRANSACTION" range="28" initialvalue="0" attributes="Strobe">
+// <info>
+// Strobe this bit high to start the SPI transaction with the bitfields below
+// </info>
+// </bitfield>
+// <bitfield name="LO_SELECT" range="26..24" type="LO_CHIP_SELECT" initialvalue="TX0_LO1" attributes="Strobe">
+// <info>
+// Sets the CS to the selected LO. The CS will assert until after @.LO_SPI_START_TRANSACTION has been asserted.
+// </info>
+// </bitfield>
+// <bitfield name="LO_SPI_RD" range="23" initialvalue="0">
+// <info>
+// Set this bit to '1' to read from the LMX2572. Set this bit to '0' to write to the LMX2572.
+// </info>
+// </bitfield>
+// <bitfield name="LO_SPI_WT_ADDR" range="22..16" initialvalue="0">
+// <info>
+// 7 bit address of the LMX2572
+// </info>
+// </bitfield>
+// <bitfield name="LO_SPI_WT_DATA" range="15..0" initialvalue="0">
+// <info>
+// Write Data to the LMX2572
+// </info>
+// </bitfield>
+// </register>
+//
+// <register name="LO_SPI_STATUS" size="32" offset="0x00" attributes="Readable">
+// <info>
+// This register returns the SPI master status, and also returns the read data from the LMX2572
+// </info>
+// <bitfield name="LO_SPI_DATA_VALID" range="31" initialvalue="0">
+// <info>
+// Returns '1' when a read SPI transaction is complete. This bit will remain high until a new SPI transaction has started.
+// i.e. @.LO_SPI_START_TRANSACTION is strobed. Poll this when expecting data from a read transaction.
+// </info>
+// </bitfield>
+// <bitfield name="LO_SPI_READY" range="30" initialvalue="0">
+// <info>
+// If this bit returns '1' then LMX2572 is ready for transaction. If it returns '0' then it is busy with a previous SPI transaction.
+// Poll this bit before starting a SPI transaction.
+// </info>
+// </bitfield>
+// <bitfield name="LO_SELECT_STATUS" range="26..24" type="LO_CHIP_SELECT" initialvalue="TX0_LO1">
+// <info>
+// Returns the current selected CS. This bitfield will return the value written to @.LO_SELECT bitfield in the @.LO_SPI_SETUP reg.
+// </info>
+// </bitfield>
+// <bitfield name="LO_SPI_RD_ADDR" range="22..16" initialvalue="0">
+// <info>
+// Returns the address of the current SPI address setup
+// </info>
+// </bitfield>
+// <bitfield name="LO_SPI_RD_DATA" range="15..0" initialvalue="0">
+// <info>
+// Returns the data of the SPI read. This bitfield will return 0x0000 until @.LO_SPI_DATA_VALID is true. This bit field will maintain it's
+// read value until a new SPI transaction has started. i.e. @.LO_SPI_START_TRANSACTION is strobed.
+// </info>
+// </bitfield>
+// </register>
+// </group>
+// <group name="LO_SYNC_REGS" offset="0x04">
+// <info>
+// Contains registers that control the logic lines in charge of synchronization
+// </info>
+// <register name="LO_PULSE_SYNC" size="32" offset="0x00" attributes="Writable">
+// <info>
+// Controls pulses driven to the SYNC pins of the LMX2572 chips
+// </info>
+// <bitfield name="PULSE_TX0_LO1_SYNC" range="0" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the TX0_LO1_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="PULSE_TX0_LO2_SYNC" range="1" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the TX0_LO2_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="PULSE_TX1_LO1_SYNC" range="2" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the TX1_LO1_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="PULSE_TX1_LO2_SYNC" range="3" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the TX1_LO2_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="PULSE_RX0_LO1_SYNC" range="4" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the RX0_LO1_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="PULSE_RX0_LO2_SYNC" range="5" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the RX0_LO2_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="PULSE_RX1_LO1_SYNC" range="6" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the RX1_LO1_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="PULSE_RX1_LO2_SYNC" range="7" initialvalue="0" attributes="Strobe">
+// <info>
+// Creates a single cycle pulse on the RX1_LO2_SYNC line.
+// </info>
+// </bitfield>
+// <bitfield name="BYPASS_SYNC_REGISTER" range="8" initialvalue="0">
+// <info>
+// Setting this bit to '1' will ignore writes to the PULSE_X_SYNC fields and allow
+// a buffered input SYNC pulse to be driven out instead.
+// </info>
+// </bitfield>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/.gitignore b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/.gitignore
new file mode 100644
index 000000000..3eee46e45
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/.gitignore
@@ -0,0 +1,2 @@
+zbx_cpld_regs_t.py
+*.hex
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/gen_defaults.py b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/gen_defaults.py
new file mode 100644
index 000000000..35e1e74bd
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/memory_init_files/gen_defaults.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# Copyright 2021 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+Helper script to generate memory init files for the ZBX CPLD.
+"""
+
+import os
+import pathlib
+
+# This is generated by running
+# $ python3 /path/to/gen_zbx_cpld_regs.py zbx_cpld_regs_t.py
+import zbx_cpld_regs_t
+
+def main():
+ """
+ Main function
+ """
+ cur_dir = str(pathlib.Path(__file__).resolve().parent)
+
+ zbx_regs = zbx_cpld_regs_t.zbx_cpld_regs_t()
+
+ # The file dsa_control.v lists default values for documentation purposes. When
+ # changing these values, make sure to also update the 'initialvalue' documentation
+ # attributes in dsa_control.v
+ zbx_regs.TX0_DSA1[0] = 31
+ zbx_regs.TX0_DSA2[0] = 31
+
+ zbx_regs.RX0_DSA1[0] = 15
+ zbx_regs.RX0_DSA2[0] = 15
+ zbx_regs.RX0_DSA3_A[0] = 15
+ zbx_regs.RX0_DSA3_B[0] = 15
+
+ # Path control defaults
+ with open(os.path.join(cur_dir, "tx0_path_defaults.hex"), "w") as outfile:
+ outfile.write("\n".join([
+ "{:08X}".format(zbx_regs.get_reg(zbx_regs.get_addr('TX0_IF2_1_2'))),
+ ] * len(zbx_regs.TX0_IF2_1_2)))
+ with open(os.path.join(cur_dir, "tx1_path_defaults.hex"), "w") as outfile:
+ outfile.write("\n".join([
+ "{:08X}".format(zbx_regs.get_reg(zbx_regs.get_addr('TX1_IF2_1_2'))),
+ ] * len(zbx_regs.TX1_IF2_1_2)))
+ with open(os.path.join(cur_dir, "rx0_path_defaults.hex"), "w") as outfile:
+ outfile.write("\n".join([
+ "{:08X}".format(zbx_regs.get_reg(zbx_regs.get_addr('RX0_ANT_1'))),
+ ] * len(zbx_regs.RX0_ANT_1)))
+ with open(os.path.join(cur_dir, "rx1_path_defaults.hex"), "w") as outfile:
+ outfile.write("\n".join([
+ "{:08X}".format(zbx_regs.get_reg(zbx_regs.get_addr('RX1_ANT_1'))),
+ ] * len(zbx_regs.RX1_ANT_1)))
+ # DSA defaults
+ with open(os.path.join(cur_dir, "tx_dsa_defaults.hex"), "w") as outfile:
+ outfile.write("\n".join([
+ "{:08X}".format(zbx_regs.get_reg(zbx_regs.get_addr('TX0_DSA1'))),
+ ] * len(zbx_regs.TX0_DSA1)))
+ with open(os.path.join(cur_dir, "rx_dsa_defaults.hex"), "w") as outfile:
+ outfile.write("\n".join([
+ "{:08X}".format(zbx_regs.get_reg(zbx_regs.get_addr('RX0_DSA1'))),
+ ] * len(zbx_regs.RX0_DSA1)))
+
+if __name__ == "__main__":
+ main()
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/power_regs.v b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/power_regs.v
new file mode 100644
index 000000000..f5fb6b81e
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/power_regs.v
@@ -0,0 +1,211 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: power_regs
+//
+// Description:
+// Registers to control the power supplies and the PLL ref clock buffer.
+//
+
+`default_nettype none
+
+module power_regs #(
+ parameter [19:0] BASE_ADDRESS = 0,
+ parameter [19:0] SIZE_ADDRESS = 0
+) (
+ // Request
+ 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,
+ // Response
+ output reg s_ctrlport_resp_ack,
+ output reg [ 1:0] s_ctrlport_resp_status,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ //reg clk domain
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+
+ //Power Control
+ output wire enable_tx_7v0,
+ output wire enable_rx_7v0,
+ output wire enable_3v3,
+
+ //Power Good Indicators
+ input wire p7v_pg_b,
+ input wire p7v_pg_a,
+
+ //PRC buffer
+ output reg pll_ref_clk_enable = 1'b0
+);
+
+ `include "../regmap/power_regs_regmap_utils.vh"
+ `include "../../../../../../lib/rfnoc/core/ctrlport.vh"
+
+ //----------------------------------------------------------
+ // Internal registers
+ //----------------------------------------------------------
+ reg enable_3v3_reg = {ENABLE_3V3_SIZE {1'b0}};
+ reg enable_rx_7v0_reg = {ENABLE_RX_7V0_SIZE {1'b0}};
+ reg enable_tx_7v0_reg = {ENABLE_TX_7V0_SIZE {1'b0}};
+
+ //----------------------------------------------------------
+ // Handling of CtrlPort
+ //----------------------------------------------------------
+ wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) && (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS);
+
+ always @(posedge ctrlport_clk) begin
+ // reset internal registers and responses
+ if (ctrlport_rst) begin
+ s_ctrlport_resp_ack <= 1'b0;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ enable_3v3_reg <= {ENABLE_3V3_SIZE {1'b0}};
+ enable_rx_7v0_reg <= {ENABLE_RX_7V0_SIZE {1'b0}};
+ enable_tx_7v0_reg <= {ENABLE_TX_7V0_SIZE {1'b0}};
+
+ end else begin
+
+ // write requests
+ if (s_ctrlport_req_wr) begin
+ // always issue an ack and no data
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + RF_POWER_CONTROL: begin
+ enable_3v3_reg <= s_ctrlport_req_data[ENABLE_3V3];
+ enable_rx_7v0_reg <= s_ctrlport_req_data[ENABLE_RX_7V0];
+ enable_tx_7v0_reg <= s_ctrlport_req_data[ENABLE_TX_7V0];
+ end
+
+ BASE_ADDRESS + PRC_CONTROL: begin
+ pll_ref_clk_enable <= s_ctrlport_req_data[PLL_REF_CLOCK_ENABLE];
+ end
+
+ // error on undefined address
+ default: begin
+ 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 requests
+ end else if (s_ctrlport_req_rd) begin
+ // default assumption: valid request
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+ s_ctrlport_resp_data <= {32{1'b0}};
+
+ case (s_ctrlport_req_addr)
+ BASE_ADDRESS + RF_POWER_CONTROL: begin
+ s_ctrlport_resp_data[ENABLE_3V3] <= enable_3v3_reg;
+ s_ctrlport_resp_data[ENABLE_RX_7V0] <= enable_rx_7v0_reg;
+ s_ctrlport_resp_data[ENABLE_TX_7V0] <= enable_tx_7v0_reg;
+ end
+
+ BASE_ADDRESS + RF_POWER_STATUS: begin
+ s_ctrlport_resp_data[P7V_A_STATUS] <= p7v_pg_a;
+ s_ctrlport_resp_data[P7V_B_STATUS] <= p7v_pg_b;
+ end
+
+ BASE_ADDRESS + PRC_CONTROL: begin
+ s_ctrlport_resp_data[PLL_REF_CLOCK_ENABLE] <= pll_ref_clk_enable;
+ end
+
+ // error on undefined address
+ default: begin
+ s_ctrlport_resp_data <= {32{1'b0}};
+ 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
+
+ // no request
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+ assign enable_tx_7v0 = enable_tx_7v0_reg;
+ assign enable_rx_7v0 = enable_rx_7v0_reg;
+ assign enable_3v3 = enable_3v3_reg;
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="POWER_REGS_REGMAP" generatevhdl="true" ettusguidelines="true">
+// <group name="POWER_REGS_REGISTERS" size="0x010">
+// <info>
+// This regmap contains the registers to control the power supplies and the clock buffer for PLL reference clock.
+// </info>
+// <register name="RF_POWER_CONTROL" size="32" offset="0x00" attributes="Readable|Writable">
+// <info>
+// This register controls power supply enables to the Tx/Rx amps, switch control, and clk buffers. During normal
+// operations, all three power supplies should be enabled.
+// </info>
+// <bitfield name="ENABLE_3v3" range="2" initialvalue="0">
+// <info>
+// This power supply sources the switch control, and the clock buffers. By default this power supply is off.
+// The internal LOs will not work unless this bit is enabled.{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="ENABLE_RX_7V0" range="1" initialvalue="0">
+// <info>
+// This power supply sources the Rx0 and Rx1 amps. By default this power supply is off.The Rx0/1 path will not
+// be active unless this power supply is enabled. Disabling this bit is similar to RX RF blanking{BR/}
+// {font color="red"}note to digital engineer, this is Pos7v0B{/font}
+// </info>
+// </bitfield>
+// <bitfield name="ENABLE_TX_7V0" range="0" initialvalue="0">
+// <info>
+// This power supply sources the Tx0 and Tx1 amps. By default this power supply is off. The Tx0/1 path will not
+// be active unless this power supply is enabled. Disabling this bit is similar to TX RF blanking{BR/}
+// {font color="red"}note to digital engineer, this is Pos7v0A{/font}
+// </info>
+// </bitfield>
+// </register>
+// <register name="RF_POWER_STATUS" size="32" offset="0x04" attributes="Readable">
+// <info>
+// Returns status of PowerGood indicators across the daughterboard.
+// </info>
+// <bitfield name="P7V_A_STATUS" range="0">
+// <info>
+// Returns status of 7V switching regulator A.{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="P7V_B_STATUS" range="1">
+// <info>
+// Returns status of 7V switching regulator B.{BR/}
+// </info>
+// </bitfield>
+// </register>
+// <register name="PRC_CONTROL" size="32" offset="0x08" attributes="Readable|Writable">
+// <info>
+// Offers ability to enable or disable the PLL reference clock.
+// </info>
+// <bitfield name="PLL_REF_CLOCK_ENABLE" range="0" initialvalue="0">
+// <info>If set PLL reference clock is enabled.</info>
+// </bitfield>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/switch_control.v b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/switch_control.v
new file mode 100644
index 000000000..63ce251b3
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/switch_control.v
@@ -0,0 +1,918 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: switch_control
+//
+// Description:
+// Implements control over RF switches via CtrlPort. Uses RAM to store multiple
+// ATR configurations.
+//
+
+`default_nettype none
+
+module switch_control #(
+ parameter [19:0] BASE_ADDRESS = 0,
+ parameter [19:0] SIZE_ADDRESS = 0
+) (
+ // Clock and reset
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+
+ // Request
+ 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,
+
+ // Response
+ output reg s_ctrlport_resp_ack,
+ output reg [ 1:0] s_ctrlport_resp_status = 2'b0,
+ output reg [31:0] s_ctrlport_resp_data = 32'b0,
+
+ // ATR switching
+ input wire [ 7:0] atr_config_rf0,
+ input wire [ 7:0] atr_config_rf1,
+
+ //Tx0 Switch control (domain: ctrl_reg_clk)
+ output wire tx0_sw1_sw2_ctrl,
+ output wire tx0_sw3_a,
+ output wire tx0_sw3_b,
+ output wire tx0_sw4_a,
+ output wire tx0_sw4_b,
+ output wire tx0_sw5_a,
+ output wire tx0_sw5_b,
+ output wire tx0_sw6_a,
+ output wire tx0_sw6_b,
+ output wire tx0_sw7_a,
+ output wire tx0_sw7_b,
+ output wire tx0_sw8_v1,
+ output wire tx0_sw8_v2,
+ output wire tx0_sw8_v3,
+ output wire tx0_sw9_a,
+ output wire tx0_sw9_b,
+ output wire tx0_sw10_a,
+ output wire tx0_sw10_b,
+ output wire tx0_sw11_a,
+ output wire tx0_sw11_b,
+ output wire tx0_sw13_v1,
+ output wire tx0_sw14_v1,
+
+ //Tx1 Switch control (domain: ctrl_reg_clk)
+ output wire tx1_sw1_sw2_ctrl,
+ output wire tx1_sw3_a,
+ output wire tx1_sw3_b,
+ output wire tx1_sw4_a,
+ output wire tx1_sw4_b,
+ output wire tx1_sw5_a,
+ output wire tx1_sw5_b,
+ output wire tx1_sw6_a,
+ output wire tx1_sw6_b,
+ output wire tx1_sw7_a,
+ output wire tx1_sw7_b,
+ output wire tx1_sw8_v1,
+ output wire tx1_sw8_v2,
+ output wire tx1_sw8_v3,
+ output wire tx1_sw9_a,
+ output wire tx1_sw9_b,
+ output wire tx1_sw10_a,
+ output wire tx1_sw10_b,
+ output wire tx1_sw11_a,
+ output wire tx1_sw11_b,
+ output wire tx1_sw13_v1,
+ output wire tx1_sw14_v1,
+
+ //Rx0 Switch control (domain: ctrl_reg_clk)
+ output wire rx0_sw1_a,
+ output wire rx0_sw1_b,
+ output wire rx0_sw2_a,
+ output wire rx0_sw3_v1,
+ output wire rx0_sw3_v2,
+ output wire rx0_sw3_v3,
+ output wire rx0_sw4_a,
+ output wire rx0_sw5_a,
+ output wire rx0_sw5_b,
+ output wire rx0_sw6_a,
+ output wire rx0_sw6_b,
+ output wire rx0_sw7_sw8_ctrl,
+ output wire rx0_sw9_v1,
+ output wire rx0_sw10_v1,
+ output wire rx0_sw11_v3,
+ output wire rx0_sw11_v2,
+ output wire rx0_sw11_v1,
+
+ //Rx1 Switch control (domain: ctrl_reg_clk)
+ output wire rx1_sw1_a,
+ output wire rx1_sw1_b,
+ output wire rx1_sw2_a,
+ output wire rx1_sw3_v1,
+ output wire rx1_sw3_v2,
+ output wire rx1_sw3_v3,
+ output wire rx1_sw4_a,
+ output wire rx1_sw5_a,
+ output wire rx1_sw5_b,
+ output wire rx1_sw6_a,
+ output wire rx1_sw6_b,
+ output wire rx1_sw7_sw8_ctrl,
+ output wire rx1_sw9_v1,
+ output wire rx1_sw10_v1,
+ output wire rx1_sw11_v3,
+ output wire rx1_sw11_v2,
+ output wire rx1_sw11_v1
+);
+
+ `include "../regmap/switch_setup_regmap_utils.vh"
+ `include "../../../../../../lib/rfnoc/core/ctrlport.vh"
+
+ //---------------------------------------------------------------
+ // register bitfields
+ //---------------------------------------------------------------
+ //Tx0PathControl Register
+ reg [ TX_SWITCH_1_2_SIZE-1:0] tx0_switch_1_2_reg;
+ reg [ TX_SWITCH_3_SIZE-1:0] tx0_switch_3_reg;
+ reg [ TX_SWITCH_4_SIZE-1:0] tx0_switch_4_reg;
+ reg [ TX_SWITCH_5_SIZE-1:0] tx0_switch_5_reg;
+ reg [ TX_SWITCH_6_SIZE-1:0] tx0_switch_6_reg;
+ reg [ TX_SWITCH_7_SIZE-1:0] tx0_switch_7_reg;
+ reg [ TX_SWITCH_8_SIZE-1:0] tx0_switch_8_reg;
+ reg [ TX_SWITCH_9_SIZE-1:0] tx0_switch_9_reg;
+ reg [ TX_SWITCH_10_SIZE-1:0] tx0_switch_10_reg;
+ reg [ TX_SWITCH_11_SIZE-1:0] tx0_switch_11_reg;
+ reg [ TX_SWITCH_13_SIZE-1:0] tx0_switch_13_reg;
+ reg [ TX_SWITCH_14_SIZE-1:0] tx0_switch_14_reg;
+
+ //Tx1PathControl Register
+ reg [ TX_SWITCH_1_2_SIZE-1:0] tx1_switch_1_2_reg;
+ reg [ TX_SWITCH_3_SIZE-1:0] tx1_switch_3_reg;
+ reg [ TX_SWITCH_4_SIZE-1:0] tx1_switch_4_reg;
+ reg [ TX_SWITCH_5_SIZE-1:0] tx1_switch_5_reg;
+ reg [ TX_SWITCH_6_SIZE-1:0] tx1_switch_6_reg;
+ reg [ TX_SWITCH_7_SIZE-1:0] tx1_switch_7_reg;
+ reg [ TX_SWITCH_8_SIZE-1:0] tx1_switch_8_reg;
+ reg [ TX_SWITCH_9_SIZE-1:0] tx1_switch_9_reg;
+ reg [ TX_SWITCH_10_SIZE-1:0] tx1_switch_10_reg;
+ reg [ TX_SWITCH_11_SIZE-1:0] tx1_switch_11_reg;
+ reg [ TX_SWITCH_13_SIZE-1:0] tx1_switch_13_reg;
+ reg [ TX_SWITCH_14_SIZE-1:0] tx1_switch_14_reg;
+
+ //Rx0PathControl Register
+ reg [ RX_SWITCH_1_SIZE-1:0] rx0_switch_1_reg;
+ reg [ RX_SWITCH_2_SIZE-1:0] rx0_switch_2_reg;
+ reg [ RX_SWITCH_3_SIZE-1:0] rx0_switch_3_reg;
+ reg [ RX_SWITCH_4_SIZE-1:0] rx0_switch_4_reg;
+ reg [ RX_SWITCH_5_SIZE-1:0] rx0_switch_5_reg;
+ reg [ RX_SWITCH_6_SIZE-1:0] rx0_switch_6_reg;
+ reg [ RX_SWITCH_7_8_SIZE-1:0] rx0_switch_7_8_reg;
+ reg [ RX_SWITCH_9_SIZE-1:0] rx0_switch_9_reg;
+ reg [ RX_SWITCH_10_SIZE-1:0] rx0_switch_10_reg;
+ reg [ RX_SWITCH_11_SIZE-1:0] rx0_switch_11_reg;
+
+ //Rx1PathControl Register
+ reg [ RX_SWITCH_1_SIZE-1:0] rx1_switch_1_reg;
+ reg [ RX_SWITCH_2_SIZE-1:0] rx1_switch_2_reg;
+ reg [ RX_SWITCH_3_SIZE-1:0] rx1_switch_3_reg;
+ reg [ RX_SWITCH_4_SIZE-1:0] rx1_switch_4_reg;
+ reg [ RX_SWITCH_5_SIZE-1:0] rx1_switch_5_reg;
+ reg [ RX_SWITCH_6_SIZE-1:0] rx1_switch_6_reg;
+ reg [ RX_SWITCH_7_8_SIZE-1:0] rx1_switch_7_8_reg;
+ reg [ RX_SWITCH_9_SIZE-1:0] rx1_switch_9_reg;
+ reg [ RX_SWITCH_10_SIZE-1:0] rx1_switch_10_reg;
+ reg [ RX_SWITCH_11_SIZE-1:0] rx1_switch_11_reg;
+
+ //---------------------------------------------------------------
+ // ATR memory signals
+ //---------------------------------------------------------------
+ reg ram_rx0_wea;
+ wire [31:0] ram_rx0_doa;
+ wire [31:0] ram_rx0_dob;
+
+ reg ram_rx1_wea;
+ wire [31:0] ram_rx1_doa;
+ wire [31:0] ram_rx1_dob;
+
+ reg ram_tx0_wea;
+ wire [31:0] ram_tx0_doa;
+ wire [31:0] ram_tx0_dob;
+
+ reg ram_tx1_wea;
+ wire [31:0] ram_tx1_doa;
+ wire [31:0] ram_tx1_dob;
+
+
+ //---------------------------------------------------------------
+ // Handling of CtrlPort
+ //---------------------------------------------------------------
+ // Check of request address is targeted for this module.
+ wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) && (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS);
+ // Read request shift register to align memory read and response generation.
+ reg [ 1:0] read_req_shift_reg = 2'b0;
+ // Mask out 8 bits for ATR configurations to be able to compare all ATR
+ // configurations against the same base register address.
+ wire [31:0] register_base_address = {s_ctrlport_req_addr[19:10], 8'b0, s_ctrlport_req_addr[1:0]};
+ // Extract masked out bits from the address, which represent the register
+ // array index = ATR configuration index
+ wire [ 7:0] register_index = s_ctrlport_req_addr[9:2];
+
+ always @(posedge ctrlport_clk) begin
+ // reset internal registers and responses
+ if (ctrlport_rst) begin
+ s_ctrlport_resp_ack <= 1'b0;
+
+ read_req_shift_reg <= 2'b0;
+
+ ram_tx0_wea <= 1'b0;
+ ram_tx1_wea <= 1'b0;
+ ram_rx0_wea <= 1'b0;
+ ram_rx1_wea <= 1'b0;
+
+ end else begin
+ // default assignments
+ read_req_shift_reg <= {read_req_shift_reg[0], s_ctrlport_req_rd};
+
+ ram_tx0_wea <= 1'b0;
+ ram_tx1_wea <= 1'b0;
+ ram_rx0_wea <= 1'b0;
+ ram_rx1_wea <= 1'b0;
+
+ // write requests
+ if (s_ctrlport_req_wr) begin
+ // always issue an ack and no data
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {32{1'bx}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ case (register_base_address)
+ BASE_ADDRESS + TX0_PATH_CONTROL(0): begin
+ ram_tx0_wea <= 1'b1;
+ end
+
+ BASE_ADDRESS + TX1_PATH_CONTROL(0): begin
+ ram_tx1_wea <= 1'b1;
+ end
+
+ BASE_ADDRESS + RX0_PATH_CONTROL(0): begin
+ ram_rx0_wea <= 1'b1;
+ end
+
+ BASE_ADDRESS + RX1_PATH_CONTROL(0): begin
+ ram_rx1_wea <= 1'b1;
+ end
+
+ // error on undefined address
+ default: begin
+ 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
+
+ // Answer read requests delayed by 2 clock cycles. This compensated for
+ // register ram_addr and the memory internal address register to make sure
+ // ram_ch0_doa is up to date when generating the response.
+ end else if (read_req_shift_reg[1]) begin
+ // default assumption: valid request
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+ s_ctrlport_resp_data <= {32{1'b0}};
+
+ case (register_base_address)
+ BASE_ADDRESS + TX0_PATH_CONTROL(0): begin
+ s_ctrlport_resp_data <= ram_tx0_doa & TX_PATH_CONTROL_MASK;
+ end
+ BASE_ADDRESS + TX1_PATH_CONTROL(0): begin
+ s_ctrlport_resp_data <= ram_tx1_doa & TX_PATH_CONTROL_MASK;
+ end
+ BASE_ADDRESS + RX0_PATH_CONTROL(0): begin
+ s_ctrlport_resp_data <= ram_rx0_doa & RX_PATH_CONTROL_MASK;
+ end
+ BASE_ADDRESS + RX1_PATH_CONTROL(0): begin
+ s_ctrlport_resp_data <= ram_rx1_doa & RX_PATH_CONTROL_MASK;
+ end
+
+ default: begin
+ 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
+
+ // no request
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+ // register without reset
+ reg [ 7:0] ram_addr = 8'b0;
+ reg [31:0] ram_datain = 32'b0;
+ always @(posedge ctrlport_clk) begin
+ // memories
+ ram_addr <= register_index;
+ ram_datain <= s_ctrlport_req_data;
+
+ // outputs
+ tx0_switch_1_2_reg <= ram_tx0_dob[TX_SWITCH_1_2];
+ tx0_switch_3_reg <= ram_tx0_dob[TX_SWITCH_3_MSB : TX_SWITCH_3];
+ tx0_switch_4_reg <= ram_tx0_dob[TX_SWITCH_4_MSB : TX_SWITCH_4];
+ tx0_switch_5_reg <= ram_tx0_dob[TX_SWITCH_5_MSB : TX_SWITCH_5];
+ tx0_switch_6_reg <= ram_tx0_dob[TX_SWITCH_6_MSB : TX_SWITCH_6];
+ tx0_switch_7_reg <= ram_tx0_dob[TX_SWITCH_7_MSB : TX_SWITCH_7];
+ tx0_switch_8_reg <= ram_tx0_dob[TX_SWITCH_8_MSB : TX_SWITCH_8];
+ tx0_switch_9_reg <= ram_tx0_dob[TX_SWITCH_9_MSB : TX_SWITCH_9];
+ tx0_switch_10_reg <= ram_tx0_dob[TX_SWITCH_10_MSB : TX_SWITCH_10];
+ tx0_switch_11_reg <= ram_tx0_dob[TX_SWITCH_11_MSB : TX_SWITCH_11];
+ tx0_switch_13_reg <= ram_tx0_dob[TX_SWITCH_13];
+ tx0_switch_14_reg <= ram_tx0_dob[TX_SWITCH_14];
+
+ tx1_switch_1_2_reg <= ram_tx1_dob[TX_SWITCH_1_2];
+ tx1_switch_3_reg <= ram_tx1_dob[TX_SWITCH_3_MSB : TX_SWITCH_3];
+ tx1_switch_4_reg <= ram_tx1_dob[TX_SWITCH_4_MSB : TX_SWITCH_4];
+ tx1_switch_5_reg <= ram_tx1_dob[TX_SWITCH_5_MSB : TX_SWITCH_5];
+ tx1_switch_6_reg <= ram_tx1_dob[TX_SWITCH_6_MSB : TX_SWITCH_6];
+ tx1_switch_7_reg <= ram_tx1_dob[TX_SWITCH_7_MSB : TX_SWITCH_7];
+ tx1_switch_8_reg <= ram_tx1_dob[TX_SWITCH_8_MSB : TX_SWITCH_8];
+ tx1_switch_9_reg <= ram_tx1_dob[TX_SWITCH_9_MSB : TX_SWITCH_9];
+ tx1_switch_10_reg <= ram_tx1_dob[TX_SWITCH_10_MSB : TX_SWITCH_10];
+ tx1_switch_11_reg <= ram_tx1_dob[TX_SWITCH_11_MSB : TX_SWITCH_11];
+ tx1_switch_13_reg <= ram_tx1_dob[TX_SWITCH_13];
+ tx1_switch_14_reg <= ram_tx1_dob[TX_SWITCH_14];
+
+ rx0_switch_1_reg <= ram_rx0_dob[RX_SWITCH_1_MSB : RX_SWITCH_1];
+ rx0_switch_2_reg <= ram_rx0_dob[RX_SWITCH_2];
+ rx0_switch_3_reg <= ram_rx0_dob[RX_SWITCH_3_MSB : RX_SWITCH_3];
+ rx0_switch_4_reg <= ram_rx0_dob[RX_SWITCH_4];
+ rx0_switch_5_reg <= ram_rx0_dob[RX_SWITCH_5_MSB : RX_SWITCH_5];
+ rx0_switch_6_reg <= ram_rx0_dob[RX_SWITCH_6_MSB : RX_SWITCH_6];
+ rx0_switch_7_8_reg <= ram_rx0_dob[RX_SWITCH_7_8];
+ rx0_switch_9_reg <= ram_rx0_dob[RX_SWITCH_9];
+ rx0_switch_10_reg <= ram_rx0_dob[RX_SWITCH_10];
+ rx0_switch_11_reg <= ram_rx0_dob[RX_SWITCH_11_MSB: RX_SWITCH_11];
+
+ rx1_switch_1_reg <= ram_rx1_dob[RX_SWITCH_1_MSB : RX_SWITCH_1];
+ rx1_switch_2_reg <= ram_rx1_dob[RX_SWITCH_2];
+ rx1_switch_3_reg <= ram_rx1_dob[RX_SWITCH_3_MSB : RX_SWITCH_3];
+ rx1_switch_4_reg <= ram_rx1_dob[RX_SWITCH_4];
+ rx1_switch_5_reg <= ram_rx1_dob[RX_SWITCH_5_MSB : RX_SWITCH_5];
+ rx1_switch_6_reg <= ram_rx1_dob[RX_SWITCH_6_MSB : RX_SWITCH_6];
+ rx1_switch_7_8_reg <= ram_rx1_dob[RX_SWITCH_7_8];
+ rx1_switch_9_reg <= ram_rx1_dob[RX_SWITCH_9];
+ rx1_switch_10_reg <= ram_rx1_dob[RX_SWITCH_10];
+ rx1_switch_11_reg <= ram_rx1_dob[RX_SWITCH_11_MSB: RX_SWITCH_11];
+ end
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/tx0_path_defaults.hex")
+ ) ram_tx0_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_tx0_wea),
+ .addra (ram_addr),
+ .dia (ram_datain),
+ .doa (ram_tx0_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf0),
+ .dib (0),
+ .dob (ram_tx0_dob));
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/tx1_path_defaults.hex")
+ ) ram_tx1_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_tx1_wea),
+ .addra (ram_addr),
+ .dia (ram_datain),
+ .doa (ram_tx1_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf1),
+ .dib (0),
+ .dob (ram_tx1_dob));
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/rx0_path_defaults.hex")
+ ) ram_rx0_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_rx0_wea),
+ .addra (ram_addr),
+ .dia (ram_datain),
+ .doa (ram_rx0_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf0),
+ .dib (0),
+ .dob (ram_rx0_dob));
+
+ ram_2port #(
+ .DWIDTH (32),
+ .AWIDTH (8),
+ .RW_MODE ("READ-FIRST"),
+ .RAM_TYPE ("AUTOMATIC"),
+ .OUT_REG (0),
+ .INIT_FILE ("register_endpoints/memory_init_files/rx1_path_defaults.hex")
+ ) ram_rx1_i (
+ .clka (ctrlport_clk),
+ .ena (1'b1),
+ .wea (ram_rx1_wea),
+ .addra (ram_addr),
+ .dia (ram_datain),
+ .doa (ram_rx1_doa),
+ .clkb (ctrlport_clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (atr_config_rf1),
+ .dib (0),
+ .dob (ram_rx1_dob));
+
+ assign tx0_sw1_sw2_ctrl = tx0_switch_1_2_reg;
+ assign tx0_sw3_a = tx0_switch_3_reg[0];
+ assign tx0_sw3_b = tx0_switch_3_reg[1];
+ assign tx0_sw4_a = tx0_switch_4_reg[0];
+ assign tx0_sw4_b = tx0_switch_4_reg[1];
+ assign tx0_sw5_a = tx0_switch_5_reg[0];
+ assign tx0_sw5_b = tx0_switch_5_reg[1];
+ assign tx0_sw6_a = tx0_switch_6_reg[0];
+ assign tx0_sw6_b = tx0_switch_6_reg[1];
+ assign tx0_sw7_a = tx0_switch_7_reg[0];
+ assign tx0_sw7_b = tx0_switch_7_reg[1];
+ assign tx0_sw8_v1 = tx0_switch_8_reg[0];
+ assign tx0_sw8_v2 = tx0_switch_8_reg[1];
+ assign tx0_sw8_v3 = tx0_switch_8_reg[2];
+ assign tx0_sw9_a = tx0_switch_9_reg[0];
+ assign tx0_sw9_b = tx0_switch_9_reg[1];
+ assign tx0_sw10_a = tx0_switch_10_reg[0];
+ assign tx0_sw10_b = tx0_switch_10_reg[1];
+ assign tx0_sw11_a = tx0_switch_11_reg[0];
+ assign tx0_sw11_b = tx0_switch_11_reg[1];
+ assign tx0_sw13_v1 = tx0_switch_13_reg;
+ assign tx0_sw14_v1 = tx0_switch_14_reg;
+
+ assign tx1_sw1_sw2_ctrl = tx1_switch_1_2_reg;
+ assign tx1_sw3_a = tx1_switch_3_reg[0];
+ assign tx1_sw3_b = tx1_switch_3_reg[1];
+ assign tx1_sw4_a = tx1_switch_4_reg[0];
+ assign tx1_sw4_b = tx1_switch_4_reg[1];
+ assign tx1_sw5_a = tx1_switch_5_reg[0];
+ assign tx1_sw5_b = tx1_switch_5_reg[1];
+ assign tx1_sw6_a = tx1_switch_6_reg[0];
+ assign tx1_sw6_b = tx1_switch_6_reg[1];
+ assign tx1_sw7_a = tx1_switch_7_reg[0];
+ assign tx1_sw7_b = tx1_switch_7_reg[1];
+ assign tx1_sw8_v1 = tx1_switch_8_reg[0];
+ assign tx1_sw8_v2 = tx1_switch_8_reg[1];
+ assign tx1_sw8_v3 = tx1_switch_8_reg[2];
+ assign tx1_sw9_a = tx1_switch_9_reg[0];
+ assign tx1_sw9_b = tx1_switch_9_reg[1];
+ assign tx1_sw10_a = tx1_switch_10_reg[0];
+ assign tx1_sw10_b = tx1_switch_10_reg[1];
+ assign tx1_sw11_a = tx1_switch_11_reg[0];
+ assign tx1_sw11_b = tx1_switch_11_reg[1];
+ assign tx1_sw13_v1 = tx1_switch_13_reg;
+ assign tx1_sw14_v1 = tx1_switch_14_reg;
+
+ assign rx0_sw1_a = rx0_switch_1_reg[0];
+ assign rx0_sw1_b = rx0_switch_1_reg[1];
+ assign rx0_sw2_a = rx0_switch_2_reg;
+ assign rx0_sw3_v1 = rx0_switch_3_reg[0];
+ assign rx0_sw3_v2 = rx0_switch_3_reg[1];
+ assign rx0_sw3_v3 = rx0_switch_3_reg[2];
+ assign rx0_sw4_a = rx0_switch_4_reg;
+ assign rx0_sw5_a = rx0_switch_5_reg[0];
+ assign rx0_sw5_b = rx0_switch_5_reg[1];
+ assign rx0_sw6_a = rx0_switch_6_reg[0];
+ assign rx0_sw6_b = rx0_switch_6_reg[1];
+ assign rx0_sw7_sw8_ctrl = rx0_switch_7_8_reg;
+ assign rx0_sw9_v1 = rx0_switch_9_reg;
+ assign rx0_sw10_v1 = rx0_switch_10_reg;
+ assign rx0_sw11_v1 = rx0_switch_11_reg[0];
+ assign rx0_sw11_v2 = rx0_switch_11_reg[1];
+ assign rx0_sw11_v3 = rx0_switch_11_reg[2];
+
+ assign rx1_sw1_a = rx1_switch_1_reg[0];
+ assign rx1_sw1_b = rx1_switch_1_reg[1];
+ assign rx1_sw2_a = rx1_switch_2_reg;
+ assign rx1_sw3_v1 = rx1_switch_3_reg[0];
+ assign rx1_sw3_v2 = rx1_switch_3_reg[1];
+ assign rx1_sw3_v3 = rx1_switch_3_reg[2];
+ assign rx1_sw4_a = rx1_switch_4_reg;
+ assign rx1_sw5_a = rx1_switch_5_reg[0];
+ assign rx1_sw5_b = rx1_switch_5_reg[1];
+ assign rx1_sw6_a = rx1_switch_6_reg[0];
+ assign rx1_sw6_b = rx1_switch_6_reg[1];
+ assign rx1_sw7_sw8_ctrl = rx1_switch_7_8_reg;
+ assign rx1_sw9_v1 = rx1_switch_9_reg;
+ assign rx1_sw10_v1 = rx1_switch_10_reg;
+ assign rx1_sw11_v1 = rx1_switch_11_reg[0];
+ assign rx1_sw11_v2 = rx1_switch_11_reg[1];
+ assign rx1_sw11_v3 = rx1_switch_11_reg[2];
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="SWITCH_SETUP_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
+// <group name="SWITCH_SETUP_REGISTERS">
+// <info>
+// The following registers are used to control the path that the RF signal
+// takes for both Tx and Rx{BR/}{BR/}
+// </info>
+// <regtype name="TX_PATH_CONTROL" size="32" attributes="Readable|Writable">
+// <info>
+// This Register controls the switches along the Tx path. Note: default
+// values refer to the RX0 path. RX1 has the same defaults, but their
+// bit values may differ.
+// </info>
+// <bitfield name="TX_SWITCH_1_2" range="0" initialvalue="0">
+// <info>
+// Write 0 to select Tx IF2 filter 2, CF = 2050 MHz, BW = 400 MHz{BR/}
+// Write 1 to select Tx IF2 filter 1, CF = 1060 MHz, BW = 400 MHz{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_3" range="3..2" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 3. The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx If1 Filter 1,2,3, or 50 ohm termination. See @.TX_SWITCH_4 for those controls{BR/}
+// Write 1 to select Tx If1 Filter 4, 5.7 GHz to 6.4 GHz{BR/}
+// Write 2 to select Tx If1 Filter 6, 7.0 GHz to 8.0 GHz{BR/}
+// Write 3 to select Tx If1 Filter 5, 6.4 GHz to 7.0 GHz{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx If1 Filter 6, 7.0 GHz to 8.0 GHz{BR/}
+// Write 1 to select Tx If1 Filter 5, 6.4 GHz to 7.0 GHz{BR/}
+// Write 2 to select Tx If1 Filter 4, 5.7 GHz to 6.4 GHz{BR/}
+// Write 3 to select Tx If1 Filter 1,2,3, or 50 ohm termination. See @.TX_SWITCH_4 for those controls{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_4" range="5..4" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 4. This switch path is only taken if @.TX_SWITCH_4 is set to 0.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select 50 ohm termination{BR/}
+// Write 1 to select Tx If1 Filter 1, 3.1 GHz to 4.3 GHz{BR/}
+// Write 2 to select Tx If1 Filter 2, 4.3 GHz to 5.1 GHz{BR/}
+// Write 3 to select Tx If1 Filter 3, 5.1 GHz to 5.7 GHz{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx If1 Filter 3, 5.1 GHz to 5.7 GHz{BR/}
+// Write 1 to select Tx If1 Filter 2, 4.3 GHz to 5.1 GHz{BR/}
+// Write 2 to select Tx If1 Filter 1, 3.1 GHz to 4.3 GHz{BR/}
+// Write 3 to select 50 ohm termination{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_5" range="7..6" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 5. This switch path is only taken if @.TX_SWITCH_6 is set to 0.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx If1 Filter 3, 5.1 GHz to 5.7 GHz{BR/}
+// Write 1 to select Tx If1 Filter 2, 4.3 GHz to 5.1 GHz{BR/}
+// Write 2 to select Tx If1 Filter 1, 3.1 GHz to 4.3 GHz{BR/}
+// Write 3 to select Tx If1 Filter 50 ohm termination{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx If1 Filter 50 ohm termination{BR/}
+// Write 1 to select Tx If1 Filter 1, 3.1 GHz to 4.3 GHz{BR/}
+// Write 2 to select Tx If1 Filter 2, 4.3 GHz to 5.1 GHz{BR/}
+// Write 3 to select Tx If1 Filter 3, 5.1 GHz to 5.7 GHz{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_6" range="9..8" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 6.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx If1 Filter 6, 7.0 GHz to 8.0 GHz{BR/}
+// Write 1 to select Tx If1 Filter 5, 6.4 GHz to 7.0 GHz{BR/}
+// Write 2 to select Tx If1 Filter 4, 5.7 GHz to 6.4 GHz{BR/}
+// Write 3 to select Tx If1 Filter 1, 2, 3, or 50 ohm termination. See @.TX_SWITCH_5 for those controls{/font}{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx If1 Filter 1, 2, 3, or 50 ohm termination. See @.TX_SWITCH_5 for those controls{/font}{BR/}
+// Write 1 to select Tx If1 Filter 4, 5.7 GHz to 6.4 GHz{BR/}
+// Write 2 to select Tx If1 Filter 5, 6.4 GHz to 7.0 GHz{BR/}
+// Write 3 to select Tx If1 Filter 6, 7.0 GHz to 8.0 GHz{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_7" range="11..10" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 7.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select 50 ohm termination{BR/}
+// Write 1 to select no connect{BR/}
+// Write 2 to select Tx highBand RF4 path, 3.1 GHz to 8 GHz{BR/}
+// Write 3 to select Tx lowbands RF1, RF2, RF3 path. See @.TX_SWITCH_8 for those controls{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx lowbands RF1, RF2, RF3 path. See @.TX_SWITCH_8 for those controls{BR/}
+// Write 1 to select Tx highBand RF4 path, 3.1 GHz to 8 GHz{BR/}
+// Write 2 to select no connect{BR/}
+// Write 3 to select 50 ohm termination{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_8" range="14..12" initialvalue="0">
+// <info>
+// Control for Tx Switch 8, note this is one hot encoding and not binary.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 1 to select Tx RF3 path, 2.3 GHz to 3.1 GHz{BR/}
+// Write 2 to select Tx RF1 path, 1.0 MHz to 1.95 GHz{BR/}
+// Write 4 to select Tx RF2 path, 1.95 GHz to 2.3 GHz{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 1 to select Tx RF2 path, 1.95 GHz to 2.3 GHz{BR/}
+// Write 2 to select Tx RF1 path, 1.0 MHz to 1.95 GHz{BR/}
+// Write 4 to select Tx RF3 path, 2.3 GHz to 3.1 GHz{BR/}
+// {i}*All other values are invalid{/i}{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_9" range="17..16" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 9.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx RF3 path, 2.3 GHz to 3.1 GHz{BR/}
+// Write 1 to select Tx RF1 path, 1.0 MHz to 1.95 GHz{BR/}
+// Write 2 to select Tx RF2 path, 1.95 GHz to 2.3 GHz{BR/}
+// Write 3 to select Tx RF4 path, 3.1 GHz to 8.0 GHz{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx RF4 path, 3.1 GHz to 8.0 GHz{BR/}
+// Write 1 to select Tx RF2 path, 1.95 GHz to 2.3 GHz{BR/}
+// Write 2 to select Tx RF1 path, 1.0 MHz to 1.95 GHz{BR/}
+// Write 3 to select Tx RF3 path, 2.3 GHz to 3.1 GHz{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_10" range="19..18" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 10.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx amplifier bypass path{BR/}
+// Write 1 to select Tx calibration loopback path{BR/}
+// Write 2 to select Tx lowband amp path. @.TX_Switch_11 must also match this path.{BR/}
+// Write 3 to select Tx highband amp path. @.TX_Switch_11 must also match this path.{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx highband amp path. @.TX_Switch_11 must also match this path.{BR/}
+// Write 1 to select Tx lowband amp path. @.TX_Switch_11 must also match this path.{BR/}
+// Write 2 to select Tx amplifier bypass path{BR/}
+// Write 3 to select Tx calibration loopback path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_11" range="21..20" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Tx Switch 11.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx Rx path, @.RX_SWITCH_1 must also select the correct path{BR/}
+// Write 1 to select Tx highband amp path. @.TX_SWITCH_10 must also match this path.{BR/}
+// Write 2 to select Tx lowband amp path. @.TX_SWITCH_10 must also match this path.{BR/}
+// Write 3 to select Tx amplifier bypass path{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx Rx path, @.RX_SWITCH_1 must also select the correct path{BR/}
+// Write 1 to select Tx amplifier bypass path{BR/}
+// Write 2 to select Tx lowband amp path. @.TX_SWITCH_10 must also match this path.{BR/}
+// Write 3 to select Tx highband amp path. @.TX_SWITCH_10 must also match this path.{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_13" range="24" initialvalue="0">
+// <info>
+// Control for Tx0 Switch 13 LO path.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx0 internal LO path{BR/}
+// Write 1 to select Tx0 external LO path{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx0 external LO path{BR/}
+// Write 1 to select Tx0 internal LO path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="TX_SWITCH_14" range="26" initialvalue="0">
+// <info>
+// Control for Tx Switch 13 LO path.
+// The configuration of this switch changes between TX paths.{BR/}
+// {b}FOR TX0:{/b}{BR/}
+// Write 0 to select Tx external LO path{BR/}
+// Write 1 to select Tx internal LO path{BR/}
+// {b}FOR TX1:{/b}{BR/}
+// Write 0 to select Tx internal LO path{BR/}
+// Write 1 to select Tx external LO path{BR/}
+// </info>
+// </bitfield>
+// </regtype>
+//
+// <regtype name="RX_PATH_CONTROL" size="32" attributes="Readable|Writable">
+// <info>
+// This Register controls switches along Rx paths. Note: default
+// values refer to the RX0 path. RX1 has the same defaults, but their
+// bit values may differ.
+// </info>
+// <bitfield name="RX_SWITCH_1" range="1..0" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}
+// Control for Rx Switch 1.
+// The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 0 to select Rx calibration loopback{BR/}
+// Write 1 to select Rx 50 ohm termination path{BR/}
+// Write 2 to select Tx Rx path, @.TX_SWITCH_11 must also select the correct path{BR/}
+// Write 3 to select Rx input port{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 0 to select Rx calibration loopback{BR/}
+// Write 1 to select Tx Rx path, @.TX_SWITCH_11 must also select the correct path{BR/}
+// Write 2 to select Rx input port{BR/}
+// Write 3 to select Rx 50 ohm termination path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_2" range="2" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is the only control, and control B is pulled high{/font}{BR/}
+// Control for Rx Switch 2. The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 0 to select Rx RF3 highband path{BR/}
+// Write 1 to select Rx RF1/2 lowband path{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 0 to select Rx RF1/2 lowband path{BR/}
+// Write 1 to select Rx RF3 highband path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_3" range="6..4" initialvalue="0">
+// <info>
+// Control for Rx Switch 3, note this is one hot encoding and not binary.
+// The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 1 to select Rx RF filter 2 path, 1.80 GHz - 2.30 GHz{BR/}
+// Write 2 to select Rx RF filter 1 path, 1.00 MHz - 1.80 GHz{BR/}
+// Write 4 to select Rx RF filter 3 path, 2.30 MHz - 3.00 GHz{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 1 to select Rx RF filter 3 path, 2.30 MHz - 3.00 GHz{BR/}
+// Write 2 to select Rx RF filter 1 path, 1.00 MHz - 1.80 GHz{BR/}
+// Write 4 to select Rx RF filter 2 path, 1.80 GHz - 2.30 GHz{BR/}
+// {i}*All other values are invalid{/i}{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_4" range="8" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is the only control, and control B is tied to ground{/font}{BR/}
+// Control for Rx Switch 4.{BR/}
+// Write 0 to select Rx RF1/2 lowband path{BR/}
+// Write 1 to select Rx RF3 highband path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_5" range="11..10" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}{BR/}
+//
+// Control for Rx Switch 5. The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 0 to select Rx RF filter 4 path, 7.0 - 8 GHz GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 1 to select Rx RF filter 3 path, 5.6 - 8 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 2 to select Rx RF filter 2 path, 4.2 - 5.6 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 3 to select Rx RF filter 1 path, 3.0 - 4.2 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 0 to select Rx RF filter 1 path, 3.0 - 4.2 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 1 to select Rx RF filter 2 path, 4.2 - 5.6 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 2 to select Rx RF filter 3 path, 5.6 - 8 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 3 to select Rx RF filter 4 path, 7.0 - 8 GHz GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_6" range="13..12" initialvalue="0">
+// <info>
+// {font color="red"}note to digital designer: control A is LSB, and control B is MSB{/font}{BR/}{BR/}
+//
+// Control for Rx Switch 6. The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 0 to select Rx RF filter 1 path, 3.0 - 4.2 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 1 to select Rx RF filter 2 path, 4.2 - 5.6 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 2 to select Rx RF filter 3 path, 5.6 - 8 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 3 to select Rx RF filter 4 path, 7.0 - 8 GHz GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 0 to select Rx RF filter 4 path, 7.0 - 8 GHz GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 1 to select Rx RF filter 3 path, 5.6 - 8 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 2 to select Rx RF filter 2 path, 4.2 - 5.6 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// Write 3 to select Rx RF filter 1 path, 3.0 - 4.2 GHz, @.RX_SWITCH_6 must also select this path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_7_8" range="14" initialvalue="0">
+// <info>
+// Shared control for Rx switch 7 and switch 8.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 0 to select Rx IF2 filter 2, CF = 2050 MHz, BW = 400 MHz{BR/}
+// Write 1 to select Rx IF2 filter 1, CF = 1060 MHz, BW = 400 MHz{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 0 to select Rx IF2 filter 1, CF = 1060 MHz, BW = 400 MHz{BR/}
+// Write 1 to select Rx IF2 filter 2, CF = 2050 MHz, BW = 400 MHz{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_9" range="16" initialvalue="0">
+// <info>
+// Control for Rx Switch 9 LO path. The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 0 to select Rx internal LO path{BR/}
+// Write 1 to select Rx external LO path{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 0 to select Rx external LO path{BR/}
+// Write 1 to select Rx internal LO path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_10" range="18" initialvalue="0">
+// <info>
+// Control for Rx Switch 10 LO path. The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 0 to select Rx internal LO path{BR/}
+// Write 1 to select Rx external LO path{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 0 to select Rx external LO path{BR/}
+// Write 1 to select Rx internal LO path{BR/}
+// </info>
+// </bitfield>
+// <bitfield name="RX_SWITCH_11" range="22..20" initialvalue="0">
+// <info>
+// Control for Rx Switch 11, note to digital designer: Control V2 is pulled to ground.{BR/}{BR/}
+// The configuration of this switch changes between RX paths.{BR/}
+// {b}FOR RX0:{/b}{BR/}
+// Write 1 to select Rx1 RF filter 3 path, 2.30 MHz - 3.00 GHz{BR/}
+// Write 2 to select Rx1 RF filter 1 path, 1.00 MHz - 1.80 GHz{BR/}
+// Write 4 to select Rx1 RF filter 2 path, 1.80 GHz - 2.30 GHz{BR/}
+// {b}FOR RX1:{/b}{BR/}
+// Write 1 to select Rx1 RF filter 2 path, 1.80 GHz - 2.30 GHz{BR/}
+// Write 2 to select Rx1 RF filter 1 path, 1.00 MHz - 1.80 GHz{BR/}
+// Write 4 to select Rx1 RF filter 3 path, 2.30 MHz - 3.00 GHz{BR/}
+// </info>
+// </bitfield>
+// </regtype>
+//
+// <register name="TX0_PATH_CONTROL" typename="TX_PATH_CONTROL" offset="0x00" count="256" step="4">
+// <info>
+// This Register controls the Tx0 paths.{br}
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+// <register name="TX1_PATH_CONTROL" typename="TX_PATH_CONTROL" offset="0x400" count="256" step="4">
+// <info>
+// This Register controls the Tx1 paths.{br}
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+//
+// <register name="RX0_PATH_CONTROL" typename="RX_PATH_CONTROL" offset="0x800" count="256" step="4">
+// <info>
+// This Register controls the Rx0 paths.{br}
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+// <register name="RX1_PATH_CONTROL" typename="RX_PATH_CONTROL" offset="0xC00" count="256" step="4">
+// <info>
+// This Register controls the Rx1 paths.{br}
+// This register array can hold settings for all ATR configurations.
+// The register index equals the ATR configuration.
+// The active configuration can be selected in @.ATR_REGMAP.
+// Independently all configurations can be read/written at any time.
+// </info>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off
diff --git a/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/utils/spi_control_utils.vh b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/utils/spi_control_utils.vh
new file mode 100644
index 000000000..5a6ab27e2
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/zbx/cpld/register_endpoints/utils/spi_control_utils.vh
@@ -0,0 +1,30 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: spi_control_utils
+//
+// Description:
+// Contains constants and functions common to blocks that interact
+// with the wishbone spi engine.
+//
+
+// SPI Master Control Register Offsets
+localparam TX_DATA_REG = 5'h00;
+localparam RX_DATA_REG = 5'h00;
+localparam CLOCK_DIVIDER_REG = 5'h14;
+localparam CONTROL_REG = 5'h10;
+localparam SS_REG = 5'h18;
+
+// Simple function to return a vector of 0's with only position
+// ss_input set to 1.
+function automatic [31:0] set_ss_bit;
+input [3:0] ss_input;
+reg [31:0] ss_data;
+begin
+ ss_data = 32'h0;
+ ss_data[ss_input] = 1'b1;
+ set_ss_bit = ss_data;
+end
+endfunction