+ Contains bits 62 through 31 of the last completed SPI response.
+ For transactions shorter than 31 bits, the upper bits in this register are undefined.
+ The regmap does not expose the most significant bit (bit 63) of the response.
+
+
+
- This register is defined in HDL source file x4xx_gpio_spi.v.
+ This register is defined in HDL source file x4xx_gpio_spi.sv.
@@ -27548,8 +27761,8 @@ used with 100Gb ethernet
- | 637675285 |
- 0x26022715 |
+ 638124305 |
+ 0x26090111 |
diff --git a/fpga/usrp3/top/x400/regmap/XmlSvPkgDIG_IFC_REGMAP.sv b/fpga/usrp3/top/x400/regmap/XmlSvPkgDIG_IFC_REGMAP.sv
new file mode 100644
index 000000000..661d765b1
--- /dev/null
+++ b/fpga/usrp3/top/x400/regmap/XmlSvPkgDIG_IFC_REGMAP.sv
@@ -0,0 +1,163 @@
+//-----------------------------------------------------------------------------
+// File: XmlSvPkgDIG_IFC_REGMAP.sv
+// Author: Autogenerated by XmlParse
+// Original Project: --
+// Date: --
+//-----------------------------------------------------------------------------
+// (c) 2026 National Instruments Corporation. All Rights Reserved.
+//-----------------------------------------------------------------------------
+// Purpose:
+// The constants in this file are autogenerated by XmlParse.
+//-----------------------------------------------------------------------------
+
+package XmlSvPkgDIG_IFC_REGMAP;
+
+//===============================================================================
+// A numerically ordered list of registers and their HDL source files
+//===============================================================================
+
+ // SPI_SLAVE_CONFIG : 0x0 (x4xx_gpio_spi.sv)
+ // SPI_TRANSACTION_CONFIG : 0x10 (x4xx_gpio_spi.sv)
+ // SPI_TRANSACTION_LSB : 0x14 (x4xx_gpio_spi.sv)
+ // SPI_TRANSACTION_GO : 0x18 (x4xx_gpio_spi.sv)
+ // SPI_STATUS : 0x1C (x4xx_gpio_spi.sv)
+ // SPI_FULL_RESPONSE_MSB : 0x20 (x4xx_gpio_spi.sv)
+ // CONTROLLER_INFO : 0x24 (x4xx_gpio_spi.sv)
+
+//===============================================================================
+// Register Group SPI_OVER_GPIO_REGS
+//===============================================================================
+
+ // SPI_SLAVE_CONFIG Register (from x4xx_gpio_spi.sv)
+ localparam kSPI_SLAVE_CONFIGCount = 4; // Number of elements in array
+
+ // SPI_TRANSACTION_CONFIG Register (from x4xx_gpio_spi.sv)
+ localparam kSPI_TRANSACTION_CONFIG = 'h10; // Register Offset
+ localparam kSPI_TRANSACTION_CONFIGSize = 32; // register width in bits
+ localparam kSPI_TRANSACTION_CONFIGMask = 32'h3FFFF;
+ localparam kSPI_CLK_DIVSize = 16; //SPI_TRANSACTION_CONFIG:SPI_CLK_DIV
+ localparam kSPI_CLK_DIVMsb = 15; //SPI_TRANSACTION_CONFIG:SPI_CLK_DIV
+ localparam kSPI_CLK_DIV = 0; //SPI_TRANSACTION_CONFIG:SPI_CLK_DIV
+ localparam kSPI_SLAVE_SELECTSize = 2; //SPI_TRANSACTION_CONFIG:SPI_SLAVE_SELECT
+ localparam kSPI_SLAVE_SELECTMsb = 17; //SPI_TRANSACTION_CONFIG:SPI_SLAVE_SELECT
+ localparam kSPI_SLAVE_SELECT = 16; //SPI_TRANSACTION_CONFIG:SPI_SLAVE_SELECT
+
+ typedef struct packed {
+ logic [13:0] Rsvd31_18; // [31:18] 14 bit spacer
+ logic [1:0] SPI_SLAVE_SELECT; // [17:16] 2 bit bitfield
+ logic [15:0] SPI_CLK_DIV; // [15: 0] 16 bit bitfield
+ } SPI_TRANSACTION_CONFIG;
+
+ // SPI_TRANSACTION_LSB Register (from x4xx_gpio_spi.sv)
+ localparam kSPI_TRANSACTION_LSB = 'h14; // Register Offset
+ localparam kSPI_TRANSACTION_LSBSize = 32; // register width in bits
+ localparam kSPI_TRANSACTION_LSBMask = 32'hFFFFFFFF;
+ localparam kSPI_DATA_LSBSize = 32; //SPI_TRANSACTION_LSB:SPI_DATA_LSB
+ localparam kSPI_DATA_LSBMsb = 31; //SPI_TRANSACTION_LSB:SPI_DATA_LSB
+ localparam kSPI_DATA_LSB = 0; //SPI_TRANSACTION_LSB:SPI_DATA_LSB
+
+ typedef struct packed {
+ logic [31:0] SPI_DATA_LSB; // [31: 0] 32 bit bitfield
+ } SPI_TRANSACTION_LSB;
+
+ // SPI_TRANSACTION_GO Register (from x4xx_gpio_spi.sv)
+ localparam kSPI_TRANSACTION_GO = 'h18; // Register Offset
+ localparam kSPI_TRANSACTION_GOSize = 32; // register width in bits
+ localparam kSPI_TRANSACTION_GOMask = 32'hFFFFFFFF;
+ localparam kSPI_DATASize = 32; //SPI_TRANSACTION_GO:SPI_DATA
+ localparam kSPI_DATAMsb = 31; //SPI_TRANSACTION_GO:SPI_DATA
+ localparam kSPI_DATA = 0; //SPI_TRANSACTION_GO:SPI_DATA
+
+ typedef struct packed {
+ logic [31:0] SPI_DATA; // [31: 0] 32 bit bitfield
+ } SPI_TRANSACTION_GO;
+
+ // SPI_STATUS Register (from x4xx_gpio_spi.sv)
+ localparam kSPI_STATUS = 'h1C; // Register Offset
+ localparam kSPI_STATUSSize = 32; // register width in bits
+ localparam kSPI_STATUSMask = 32'hFFFFFFFF;
+ localparam kSPI_RESPONSESize = 31; //SPI_STATUS:SPI_RESPONSE
+ localparam kSPI_RESPONSEMsb = 30; //SPI_STATUS:SPI_RESPONSE
+ localparam kSPI_RESPONSE = 0; //SPI_STATUS:SPI_RESPONSE
+ localparam kSPI_READYSize = 1; //SPI_STATUS:SPI_READY
+ localparam kSPI_READYMsb = 31; //SPI_STATUS:SPI_READY
+ localparam kSPI_READY = 31; //SPI_STATUS:SPI_READY
+
+ typedef struct packed {
+ logic SPI_READY; // [31:31] 1 bit bitfield
+ logic [30:0] SPI_RESPONSE; // [30: 0] 31 bit bitfield
+ } SPI_STATUS;
+
+ // SPI_FULL_RESPONSE_MSB Register (from x4xx_gpio_spi.sv)
+ localparam kSPI_FULL_RESPONSE_MSB = 'h20; // Register Offset
+ localparam kSPI_FULL_RESPONSE_MSBSize = 32; // register width in bits
+ localparam kSPI_FULL_RESPONSE_MSBMask = 32'hFFFFFFFF;
+ localparam kSPI_FULL_RESPONSE_MSB_DATASize = 32; //SPI_FULL_RESPONSE_MSB:SPI_FULL_RESPONSE_MSB_DATA
+ localparam kSPI_FULL_RESPONSE_MSB_DATAMsb = 31; //SPI_FULL_RESPONSE_MSB:SPI_FULL_RESPONSE_MSB_DATA
+ localparam kSPI_FULL_RESPONSE_MSB_DATA = 0; //SPI_FULL_RESPONSE_MSB:SPI_FULL_RESPONSE_MSB_DATA
+
+ typedef struct packed {
+ logic [31:0] SPI_FULL_RESPONSE_MSB_DATA; // [31: 0] 32 bit bitfield
+ } SPI_FULL_RESPONSE_MSB;
+
+ // CONTROLLER_INFO Register (from x4xx_gpio_spi.sv)
+ localparam kCONTROLLER_INFO = 'h24; // Register Offset
+ localparam kCONTROLLER_INFOSize = 32; // register width in bits
+ localparam kCONTROLLER_INFOMask = 32'hF;
+ localparam kSLAVE_COUNTSize = 4; //CONTROLLER_INFO:SLAVE_COUNT
+ localparam kSLAVE_COUNTMsb = 3; //CONTROLLER_INFO:SLAVE_COUNT
+ localparam kSLAVE_COUNT = 0; //CONTROLLER_INFO:SLAVE_COUNT
+
+ typedef struct packed {
+ logic [27:0] Rsvd31_4; // [31: 4] 28 bit spacer
+ logic [3:0] SLAVE_COUNT; // [ 3: 0] 4 bit bitfield
+ } CONTROLLER_INFO;
+
+//===============================================================================
+// RegTypes
+//===============================================================================
+
+ // SPI_SETUP Type (from x4xx_gpio_spi.sv)
+ localparam kSPI_SETUPSize = 32;
+ localparam kSPI_SETUPMask = 32'h1FFFFFFF;
+ localparam kSLAVE_CLKSize = 5; //SPI_SETUP:SLAVE_CLK
+ localparam kSLAVE_CLKMsb = 4; //SPI_SETUP:SLAVE_CLK
+ localparam kSLAVE_CLK = 0; //SPI_SETUP:SLAVE_CLK
+ localparam kSLAVE_MOSISize = 5; //SPI_SETUP:SLAVE_MOSI
+ localparam kSLAVE_MOSIMsb = 9; //SPI_SETUP:SLAVE_MOSI
+ localparam kSLAVE_MOSI = 5; //SPI_SETUP:SLAVE_MOSI
+ localparam kSLAVE_MISOSize = 5; //SPI_SETUP:SLAVE_MISO
+ localparam kSLAVE_MISOMsb = 14; //SPI_SETUP:SLAVE_MISO
+ localparam kSLAVE_MISO = 10; //SPI_SETUP:SLAVE_MISO
+ localparam kSLAVE_CSSize = 5; //SPI_SETUP:SLAVE_CS
+ localparam kSLAVE_CSMsb = 19; //SPI_SETUP:SLAVE_CS
+ localparam kSLAVE_CS = 15; //SPI_SETUP:SLAVE_CS
+ localparam kSPI_LENGTHSize = 7; //SPI_SETUP:SPI_LENGTH
+ localparam kSPI_LENGTHMsb = 26; //SPI_SETUP:SPI_LENGTH
+ localparam kSPI_LENGTH = 20; //SPI_SETUP:SPI_LENGTH
+ localparam kMISO_EDGESize = 1; //SPI_SETUP:MISO_EDGE
+ localparam kMISO_EDGEMsb = 27; //SPI_SETUP:MISO_EDGE
+ localparam kMISO_EDGE = 27; //SPI_SETUP:MISO_EDGE
+ localparam kMOSI_EDGESize = 1; //SPI_SETUP:MOSI_EDGE
+ localparam kMOSI_EDGEMsb = 28; //SPI_SETUP:MOSI_EDGE
+ localparam kMOSI_EDGE = 28; //SPI_SETUP:MOSI_EDGE
+
+ typedef struct packed {
+ logic [2:0] Rsvd31_29; // [31:29] 3 bit spacer
+ logic MOSI_EDGE; // [28:28] 1 bit bitfield
+ logic MISO_EDGE; // [27:27] 1 bit bitfield
+ logic [6:0] SPI_LENGTH; // [26:20] 7 bit bitfield
+ logic [4:0] SLAVE_CS; // [19:15] 5 bit bitfield
+ logic [4:0] SLAVE_MISO; // [14:10] 5 bit bitfield
+ logic [4:0] SLAVE_MOSI; // [ 9: 5] 5 bit bitfield
+ logic [4:0] SLAVE_CLK; // [ 4: 0] 5 bit bitfield
+ } SPI_SETUP;
+
+ // Return the offset of an element of register array kSPI_SLAVE_CONFIG
+ function automatic integer kSPI_SLAVE_CONFIG (input integer i);
+ begin
+ kSPI_SLAVE_CONFIG = (i * 'h4) + 'h0;
+ end
+ endfunction
+
+endpackage : XmlSvPkgDIG_IFC_REGMAP
diff --git a/fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh b/fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh
deleted file mode 100644
index 7f75caf48..000000000
--- a/fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-// Copyright 2025 Ettus Research, a National Instruments Brand
-//
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// Module: dig_ifc_regmap_utils.vh
-//
-// Description:
-//
-// The constants in this file are autogenerated by XmlParse.
-//
-
-//===============================================================================
-// A numerically ordered list of registers and their HDL source files
-//===============================================================================
-
- // SPI_SLAVE_CONFIG : 0x0 (x4xx_gpio_spi.v)
- // SPI_TRANSACTION_CONFIG : 0x10 (x4xx_gpio_spi.v)
- // SPI_TRANSACTION_GO : 0x14 (x4xx_gpio_spi.v)
- // SPI_STATUS : 0x18 (x4xx_gpio_spi.v)
- // CONTROLLER_INFO : 0x1C (x4xx_gpio_spi.v)
-
-//===============================================================================
-// RegTypes
-//===============================================================================
-
- // SPI_SETUP Type (from x4xx_gpio_spi.v)
- localparam SPI_SETUP_SIZE = 32;
- localparam SPI_SETUP_MASK = 32'hFFFFFFF;
- localparam SLAVE_CLK_SIZE = 5; //SPI_SETUP:SLAVE_CLK
- localparam SLAVE_CLK_MSB = 4; //SPI_SETUP:SLAVE_CLK
- localparam SLAVE_CLK = 0; //SPI_SETUP:SLAVE_CLK
- localparam SLAVE_MOSI_SIZE = 5; //SPI_SETUP:SLAVE_MOSI
- localparam SLAVE_MOSI_MSB = 9; //SPI_SETUP:SLAVE_MOSI
- localparam SLAVE_MOSI = 5; //SPI_SETUP:SLAVE_MOSI
- localparam SLAVE_MISO_SIZE = 5; //SPI_SETUP:SLAVE_MISO
- localparam SLAVE_MISO_MSB = 14; //SPI_SETUP:SLAVE_MISO
- localparam SLAVE_MISO = 10; //SPI_SETUP:SLAVE_MISO
- localparam SLAVE_CS_SIZE = 5; //SPI_SETUP:SLAVE_CS
- localparam SLAVE_CS_MSB = 19; //SPI_SETUP:SLAVE_CS
- localparam SLAVE_CS = 15; //SPI_SETUP:SLAVE_CS
- localparam SPI_LENGTH_SIZE = 6; //SPI_SETUP:SPI_LENGTH
- localparam SPI_LENGTH_MSB = 25; //SPI_SETUP:SPI_LENGTH
- localparam SPI_LENGTH = 20; //SPI_SETUP:SPI_LENGTH
- localparam MISO_EDGE_SIZE = 1; //SPI_SETUP:MISO_EDGE
- localparam MISO_EDGE_MSB = 26; //SPI_SETUP:MISO_EDGE
- localparam MISO_EDGE = 26; //SPI_SETUP:MISO_EDGE
- localparam MOSI_EDGE_SIZE = 1; //SPI_SETUP:MOSI_EDGE
- localparam MOSI_EDGE_MSB = 27; //SPI_SETUP:MOSI_EDGE
- localparam MOSI_EDGE = 27; //SPI_SETUP:MOSI_EDGE
-
-//===============================================================================
-// Register Group SPI_OVER_GPIO_REGS
-//===============================================================================
-
- // SPI_SLAVE_CONFIG Register (from x4xx_gpio_spi.v)
- localparam SPI_SLAVE_CONFIG_COUNT = 4; // Number of elements in array
-
- // SPI_TRANSACTION_CONFIG Register (from x4xx_gpio_spi.v)
- localparam SPI_TRANSACTION_CONFIG = 'h10; // Register Offset
- localparam SPI_TRANSACTION_CONFIG_SIZE = 32; // register width in bits
- localparam SPI_TRANSACTION_CONFIG_MASK = 32'h3FFFF;
- localparam SPI_CLK_DIV_SIZE = 16; //SPI_TRANSACTION_CONFIG:SPI_CLK_DIV
- localparam SPI_CLK_DIV_MSB = 15; //SPI_TRANSACTION_CONFIG:SPI_CLK_DIV
- localparam SPI_CLK_DIV = 0; //SPI_TRANSACTION_CONFIG:SPI_CLK_DIV
- localparam SPI_SLAVE_SELECT_SIZE = 2; //SPI_TRANSACTION_CONFIG:SPI_SLAVE_SELECT
- localparam SPI_SLAVE_SELECT_MSB = 17; //SPI_TRANSACTION_CONFIG:SPI_SLAVE_SELECT
- localparam SPI_SLAVE_SELECT = 16; //SPI_TRANSACTION_CONFIG:SPI_SLAVE_SELECT
-
- // SPI_TRANSACTION_GO Register (from x4xx_gpio_spi.v)
- localparam SPI_TRANSACTION_GO = 'h14; // Register Offset
- localparam SPI_TRANSACTION_GO_SIZE = 32; // register width in bits
- localparam SPI_TRANSACTION_GO_MASK = 32'hFFFFFFFF;
- localparam SPI_DATA_SIZE = 32; //SPI_TRANSACTION_GO:SPI_DATA
- localparam SPI_DATA_MSB = 31; //SPI_TRANSACTION_GO:SPI_DATA
- localparam SPI_DATA = 0; //SPI_TRANSACTION_GO:SPI_DATA
-
- // SPI_STATUS Register (from x4xx_gpio_spi.v)
- localparam SPI_STATUS = 'h18; // Register Offset
- localparam SPI_STATUS_SIZE = 32; // register width in bits
- localparam SPI_STATUS_MASK = 32'h1FFFFFF;
- localparam SPI_RESPONSE_SIZE = 24; //SPI_STATUS:SPI_RESPONSE
- localparam SPI_RESPONSE_MSB = 23; //SPI_STATUS:SPI_RESPONSE
- localparam SPI_RESPONSE = 0; //SPI_STATUS:SPI_RESPONSE
- localparam SPI_READY_SIZE = 1; //SPI_STATUS:SPI_READY
- localparam SPI_READY_MSB = 24; //SPI_STATUS:SPI_READY
- localparam SPI_READY = 24; //SPI_STATUS:SPI_READY
-
- // CONTROLLER_INFO Register (from x4xx_gpio_spi.v)
- localparam CONTROLLER_INFO = 'h1C; // Register Offset
- localparam CONTROLLER_INFO_SIZE = 32; // register width in bits
- localparam CONTROLLER_INFO_MASK = 32'hF;
- localparam SLAVE_COUNT_SIZE = 4; //CONTROLLER_INFO:SLAVE_COUNT
- localparam SLAVE_COUNT_MSB = 3; //CONTROLLER_INFO:SLAVE_COUNT
- localparam SLAVE_COUNT = 0; //CONTROLLER_INFO:SLAVE_COUNT
-
- // Return the offset of an element of register array SPI_SLAVE_CONFIG
- function automatic integer SPI_SLAVE_CONFIG (input integer i);
- begin
- SPI_SLAVE_CONFIG = (i * 'h4) + 'h0;
- end
- endfunction
diff --git a/fpga/usrp3/top/x400/regmap/x410/versioning_regs_regmap_utils.vh b/fpga/usrp3/top/x400/regmap/x410/versioning_regs_regmap_utils.vh
index 0c6a71da3..69b7b120f 100644
--- a/fpga/usrp3/top/x400/regmap/x410/versioning_regs_regmap_utils.vh
+++ b/fpga/usrp3/top/x400/regmap/x410/versioning_regs_regmap_utils.vh
@@ -86,9 +86,9 @@
localparam FPGA_CURRENT_VERSION_BUILD = 'h0; // FPGA_VERSION:FPGA_CURRENT_VERSION_BUILD
localparam FPGA_OLDEST_COMPATIBLE_VERSION_MINOR = 'h0; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MINOR
localparam FPGA_OLDEST_COMPATIBLE_VERSION_BUILD = 'h0; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_BUILD
- localparam FPGA_CURRENT_VERSION_MAJOR = 'hB; // FPGA_VERSION:FPGA_CURRENT_VERSION_MAJOR
- localparam FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR = 'hB; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR
- localparam FPGA_VERSION_LAST_MODIFIED_TIME = 'h26022715; // FPGA_VERSION:FPGA_VERSION_LAST_MODIFIED_TIME
+ localparam FPGA_CURRENT_VERSION_MAJOR = 'hC; // FPGA_VERSION:FPGA_CURRENT_VERSION_MAJOR
+ localparam FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR = 'hC; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR
+ localparam FPGA_VERSION_LAST_MODIFIED_TIME = 'h26090111; // FPGA_VERSION:FPGA_VERSION_LAST_MODIFIED_TIME
// Enumerated type RF_CORE_100M_VERSION
localparam RF_CORE_100M_VERSION_SIZE = 7;
diff --git a/fpga/usrp3/top/x400/regmap/x420/versioning_regs_regmap_utils.vh b/fpga/usrp3/top/x400/regmap/x420/versioning_regs_regmap_utils.vh
index 13d39ab37..13555555d 100644
--- a/fpga/usrp3/top/x400/regmap/x420/versioning_regs_regmap_utils.vh
+++ b/fpga/usrp3/top/x400/regmap/x420/versioning_regs_regmap_utils.vh
@@ -86,9 +86,9 @@
localparam FPGA_CURRENT_VERSION_BUILD = 'h0; // FPGA_VERSION:FPGA_CURRENT_VERSION_BUILD
localparam FPGA_OLDEST_COMPATIBLE_VERSION_MINOR = 'h0; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MINOR
localparam FPGA_OLDEST_COMPATIBLE_VERSION_BUILD = 'h0; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_BUILD
- localparam FPGA_CURRENT_VERSION_MAJOR = 'hB; // FPGA_VERSION:FPGA_CURRENT_VERSION_MAJOR
- localparam FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR = 'hB; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR
- localparam FPGA_VERSION_LAST_MODIFIED_TIME = 'h26022715; // FPGA_VERSION:FPGA_VERSION_LAST_MODIFIED_TIME
+ localparam FPGA_CURRENT_VERSION_MAJOR = 'hC; // FPGA_VERSION:FPGA_CURRENT_VERSION_MAJOR
+ localparam FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR = 'hC; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR
+ localparam FPGA_VERSION_LAST_MODIFIED_TIME = 'h26090111; // FPGA_VERSION:FPGA_VERSION_LAST_MODIFIED_TIME
// Enumerated type RF_CORE_1000M_VERSION
localparam RF_CORE_1000M_VERSION_SIZE = 7;
diff --git a/fpga/usrp3/top/x400/regmap/x440/versioning_regs_regmap_utils.vh b/fpga/usrp3/top/x400/regmap/x440/versioning_regs_regmap_utils.vh
index cba6f18ba..bf25eec24 100644
--- a/fpga/usrp3/top/x400/regmap/x440/versioning_regs_regmap_utils.vh
+++ b/fpga/usrp3/top/x400/regmap/x440/versioning_regs_regmap_utils.vh
@@ -86,9 +86,9 @@
localparam FPGA_CURRENT_VERSION_BUILD = 'h0; // FPGA_VERSION:FPGA_CURRENT_VERSION_BUILD
localparam FPGA_OLDEST_COMPATIBLE_VERSION_MINOR = 'h0; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MINOR
localparam FPGA_OLDEST_COMPATIBLE_VERSION_BUILD = 'h0; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_BUILD
- localparam FPGA_CURRENT_VERSION_MAJOR = 'hB; // FPGA_VERSION:FPGA_CURRENT_VERSION_MAJOR
- localparam FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR = 'hB; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR
- localparam FPGA_VERSION_LAST_MODIFIED_TIME = 'h26022715; // FPGA_VERSION:FPGA_VERSION_LAST_MODIFIED_TIME
+ localparam FPGA_CURRENT_VERSION_MAJOR = 'hC; // FPGA_VERSION:FPGA_CURRENT_VERSION_MAJOR
+ localparam FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR = 'hC; // FPGA_VERSION:FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR
+ localparam FPGA_VERSION_LAST_MODIFIED_TIME = 'h26090111; // FPGA_VERSION:FPGA_VERSION_LAST_MODIFIED_TIME
// Enumerated type RF_CORE_200M_VERSION
localparam RF_CORE_200M_VERSION_SIZE = 7;
diff --git a/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x410/Makefile b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x410/Makefile
new file mode 100644
index 000000000..4d706680d
--- /dev/null
+++ b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x410/Makefile
@@ -0,0 +1,23 @@
+#
+# Copyright 2026 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+# Description: X410 variant of the X4xx GPIO SPI simulation shell.
+#
+
+VLOG_ARGS += +define+X410
+SVLOG_ARGS += +define+X410
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+BASE_DIR = $(abspath ../../../../../top)
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+include ../x4xx/Makefile.common
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx/Makefile b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx/Makefile
new file mode 100644
index 000000000..17ff340f0
--- /dev/null
+++ b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx/Makefile
@@ -0,0 +1,20 @@
+#
+# Copyright 2026 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+# Description: X4xx variant of the X4xx GPIO SPI simulation shell.
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+BASE_DIR = $(abspath ../../../../../top)
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+include ./Makefile.common
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx/Makefile.common b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx/Makefile.common
new file mode 100644
index 000000000..c1512da29
--- /dev/null
+++ b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx/Makefile.common
@@ -0,0 +1,21 @@
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+include $(BASE_DIR)/../lib/control/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(CONTROL_LIB_SRCS) \
+$(RFNOC_FRAMEWORK_SRCS) \
+../../../regmap/XmlSvPkgDIG_IFC_REGMAP.sv \
+../../../x4xx_gpio_spi.sv \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+SIM_TOP = x4xx_gpio_spi_tb
+
+SIM_SRCS = $(abspath \
+../x4xx_gpio_spi_tb.sv \
+)
diff --git a/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx_gpio_spi_tb.sv b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx_gpio_spi_tb.sv
new file mode 100644
index 000000000..662112b9b
--- /dev/null
+++ b/fpga/usrp3/top/x400/sim/x4xx_gpio_spi_tb/x4xx_gpio_spi_tb.sv
@@ -0,0 +1,364 @@
+//
+// Copyright 2026 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: x4xx_gpio_spi_tb
+//
+// Description: X4xx GPIO SPI transaction testbench for four peripheral slots.
+//
+// Validates GPIO pin configuration, CS idle state, and SPI write/readback for
+// 20-, 32-, 40-, and 64-bit transactions. Tests cover consecutive transfers,
+// switching peripheral slots and clock dividers, randomized payloads, the
+// status and extended response registers, and SPI timing checks.
+//
+// This testbench does not cover invalid register configurations, unsupported
+// transaction lengths, concurrent transactions.
+// The DUT does not properly handle these unsupported scenarios.
+//
+// For testing X410, run with VLOG_ARGS='+define+X410' SVLOG_ARGS='+define+X410'
+//
+
+`default_nettype none
+
+module x4xx_gpio_spi_tb;
+ `include "test_exec.svh"
+ import PkgTestExec::*;
+ import ctrlport_pkg::*;
+ import ctrlport_bfm_pkg::*;
+ import spi_slave_bfm_pkg::*;
+
+ import XmlSvPkgDIG_IFC_REGMAP::*;
+
+ localparam int unsigned NUM_PERIPHERALS = 4;
+
+ // Compile-time BFM widths for the peripheral slots.
+ localparam int unsigned TRANSACTION_LENGTH_0 = 64;
+ localparam int unsigned TRANSACTION_LENGTH_1 = 40;
+ localparam int unsigned TRANSACTION_LENGTH_2 = 32;
+ localparam int unsigned TRANSACTION_LENGTH_3 = 20;
+
+
+ localparam time CLK_2X_PERIOD = 12ns;
+ `ifdef X410
+ localparam bit DOUBLE_CLOCK = 1;
+ `else
+ localparam bit DOUBLE_CLOCK = 0;
+ `endif
+ localparam int unsigned CLK_DIV_1 = 2;
+ localparam int unsigned CLK_DIV_2 = 6;
+ // SPI edge toggle: 0 updates on falling and samples on rising; 1 reverses it.
+ localparam bit SPI_UPDATE_ON_RISING = 1;
+ localparam int unsigned SDI_PIN = 1;
+ localparam int unsigned SDO_PIN = 2;
+ localparam int unsigned CLK_PIN = 3;
+ localparam int unsigned CS_PINS [0:NUM_PERIPHERALS-1] = '{0, 4, 5, 6};
+ localparam int unsigned TRANSACTION_LENGTHS [0:NUM_PERIPHERALS-1] = '{64, 40, 32, 20};
+
+ // The DUT uses a control clock and a 2x SPI engine clock.
+ logic clk, clkx2, reset;
+ sim_clock_gen #(.PERIOD(CLK_2X_PERIOD*2), .AUTOSTART(0)) clk_gen (
+ .clk(clk), .rst(reset));
+ sim_clock_gen #(.PERIOD(CLK_2X_PERIOD), .AUTOSTART(0)) clk_gen_x2 (
+ .clk(clkx2), .rst());
+
+ // GPIO interface signals
+ logic [31:0] gpio_out;
+ logic [31:0] gpio_ddr;
+ wire logic [31:0] gpio_in;
+
+ // SPI interfaces for the peripheral slots.
+ spi_if spi_if [0:NUM_PERIPHERALS-1] ();
+
+ // All slots share SCK, MOSI, and MISO GPIO pins; each has its own CS pin.
+ for (genvar spi_idx = 0; spi_idx < NUM_PERIPHERALS; spi_idx++) begin : gen_spi_wiring
+ assign spi_if[spi_idx].cs_n = gpio_out[CS_PINS[spi_idx]];
+ assign spi_if[spi_idx].sck = gpio_out[CLK_PIN];
+ assign spi_if[spi_idx].mosi = gpio_out[SDO_PIN];
+ assign gpio_in[SDI_PIN] = spi_if[spi_idx].miso;
+ end
+
+ // The BFMs must remain explicit because length is a class specialization parameter.
+ spi_slave_bfm #(.TRANSACTION_LENGTH(TRANSACTION_LENGTH_0)) spi_bfm_0 = new(spi_if[0]);
+ spi_slave_bfm #(.TRANSACTION_LENGTH(TRANSACTION_LENGTH_1)) spi_bfm_1 = new(spi_if[1]);
+ spi_slave_bfm #(.TRANSACTION_LENGTH(TRANSACTION_LENGTH_2)) spi_bfm_2 = new(spi_if[2]);
+ spi_slave_bfm #(.TRANSACTION_LENGTH(TRANSACTION_LENGTH_3)) spi_bfm_3 = new(spi_if[3]);
+
+ // Each slot has an independent monitor so timing expectations follow the
+ // selected transaction clock divider.
+ spi_timing_monitor_bfm spi_timing_mon [0:NUM_PERIPHERALS-1];
+
+ for (genvar spi_idx = 0; spi_idx < NUM_PERIPHERALS; spi_idx++) begin : gen_spi_timing_monitor
+ initial begin
+ spi_timing_mon[spi_idx] = new(
+ .iface(spi_if[spi_idx]), .half_per_time(get_half_per_time(CLK_DIV_1)),
+ .cs_hold_time(0), .cs_to_first_rise_time(0), .cs_guard_time(0));
+ end
+ end
+
+ // Control-port connection to the GPIO SPI DUT.
+ logic [1:0] spi_ctrlport_resp_status;
+ ctrlport_if gpio_ctrlport_if (.clk(clk), .rst(reset));
+ ctrlport_bfm gpio_ctrlport_bfm = new(gpio_ctrlport_if);
+
+ x4xx_gpio_spi dut (
+ .ctrlport_clk (clk),
+ .ctrlport_clk_2x (clkx2),
+ .ctrlport_rst (reset),
+ .s_ctrlport_req_wr (gpio_ctrlport_if.req.wr),
+ .s_ctrlport_req_rd (gpio_ctrlport_if.req.rd),
+ .s_ctrlport_req_addr (gpio_ctrlport_if.req.addr),
+ .s_ctrlport_req_data (gpio_ctrlport_if.req.data),
+ .s_ctrlport_resp_ack (gpio_ctrlport_if.resp.ack),
+ .s_ctrlport_resp_status(spi_ctrlport_resp_status),
+ .s_ctrlport_resp_data (gpio_ctrlport_if.resp.data),
+ .gpio_out (gpio_out),
+ .gpio_ddr (gpio_ddr),
+ .gpio_in (gpio_in)
+ );
+
+ assign gpio_ctrlport_if.resp.status = ctrlport_status_t'(spi_ctrlport_resp_status);
+
+ // Return the SPI half-period expected by the timing monitor. X410 runs the
+ // SPI engine at twice the clock rate used by the other X4xx variants.
+ function automatic time get_half_per_time(input int unsigned clk_div);
+ return (clk_div + 1) * CLK_2X_PERIOD * (DOUBLE_CLOCK ? 1 : 2);
+ endfunction
+
+ function automatic logic [63:0] get_response_mask(
+ input int unsigned transaction_length
+ );
+ // SPI_FULL_RESPONSE_MSB exposes bits 62:31; bit 63 is not returned.
+ return (64'h1 << ((transaction_length < 63) ? transaction_length : 63)) - 1;
+ endfunction
+
+ function automatic logic [31:0] get_status_mask(
+ input int unsigned transaction_length
+ );
+ // SPI_STATUS exposes at most 31 response bits below SPI_READY.
+ return (32'h1 << ((transaction_length < 31) ? transaction_length : 31)) - 1;
+ endfunction
+
+ // Program the pin mapping and fixed transaction length for one SPI slot.
+ task automatic configure_peripheral(input int unsigned selected_peripheral);
+ SPI_SETUP peripheral_config_fields;
+
+ peripheral_config_fields = '0;
+ peripheral_config_fields.MOSI_EDGE = SPI_UPDATE_ON_RISING;
+ peripheral_config_fields.MISO_EDGE = !SPI_UPDATE_ON_RISING;
+ peripheral_config_fields.SPI_LENGTH = TRANSACTION_LENGTHS[selected_peripheral];
+ peripheral_config_fields.SLAVE_CS = CS_PINS[selected_peripheral];
+ peripheral_config_fields.SLAVE_MISO = SDI_PIN;
+ peripheral_config_fields.SLAVE_MOSI = SDO_PIN;
+ peripheral_config_fields.SLAVE_CLK = CLK_PIN;
+ gpio_ctrlport_bfm.write(kSPI_SLAVE_CONFIG(selected_peripheral), peripheral_config_fields);
+ endtask
+
+ task automatic configure_transaction(
+ input int unsigned selected_peripheral,
+ input int unsigned clk_div
+ );
+ SPI_TRANSACTION_CONFIG transaction_config_fields;
+
+ // The selected selected_peripheral and divider apply to the next transaction.
+ transaction_config_fields = '0;
+ transaction_config_fields.SPI_CLK_DIV = clk_div;
+ transaction_config_fields.SPI_SLAVE_SELECT = selected_peripheral;
+ gpio_ctrlport_bfm.write(kSPI_TRANSACTION_CONFIG, transaction_config_fields);
+ spi_timing_mon[selected_peripheral].set_timing(
+ .half_per_time(get_half_per_time(clk_div)),
+ .cs_hold_time(0), .cs_to_first_rise_time(0), .cs_guard_time(0));
+ endtask
+
+ task automatic queue_bfm_transaction(
+ input int unsigned selected_peripheral,
+ input logic [63:0] write_data,
+ input logic [63:0] expected_response
+ );
+ // Keep the typed BFM dispatch in one place for simulator portability.
+ case (selected_peripheral)
+ 0: spi_bfm_0.async_write_read(write_data, expected_response);
+ 1: spi_bfm_1.async_write_read(write_data, expected_response);
+ 2: spi_bfm_2.async_write_read(write_data, expected_response);
+ 3: spi_bfm_3.async_write_read(write_data, expected_response);
+ default: $fatal(1, "Invalid SPI peripheral index: %0d", selected_peripheral);
+ endcase
+ endtask
+
+ task automatic wait_for_cs(input int unsigned selected_peripheral);
+ // Dynamic bit-select event controls are avoided for Vivado XSim.
+ case (selected_peripheral)
+ 0: @(posedge gpio_out[CS_PINS[0]]);
+ 1: @(posedge gpio_out[CS_PINS[1]]);
+ 2: @(posedge gpio_out[CS_PINS[2]]);
+ 3: @(posedge gpio_out[CS_PINS[3]]);
+ default: $fatal(1, "Invalid SPI peripheral index: %0d", selected_peripheral);
+ endcase
+ endtask
+
+ task automatic get_bfm_error_count(input int unsigned selected_peripheral, output int count);
+ case (selected_peripheral)
+ 0: count = spi_bfm_0.get_error_count();
+ 1: count = spi_bfm_1.get_error_count();
+ 2: count = spi_bfm_2.get_error_count();
+ 3: count = spi_bfm_3.get_error_count();
+ default: $fatal(1, "Invalid SPI peripheral index: %0d", selected_peripheral);
+ endcase
+ endtask
+
+ task automatic start_bfms();
+ // Start all models, including inactive slots. Inactive CS lines remain
+ // high and therefore do not consume queued transactions.
+ spi_bfm_0.run();
+ spi_bfm_1.run();
+ spi_bfm_2.run();
+ spi_bfm_3.run();
+ for (int unsigned spi_idx = 0; spi_idx < NUM_PERIPHERALS; spi_idx++) begin
+ spi_timing_mon[spi_idx].run();
+ end
+ endtask
+
+ task automatic start_transaction(
+ input int unsigned transaction_length,
+ input logic [63:0] write_data
+ );
+ if (transaction_length > 32) begin
+ gpio_ctrlport_bfm.write(
+ kSPI_TRANSACTION_LSB,
+ (write_data << (64 - transaction_length)) & kSPI_TRANSACTION_LSBMask);
+ gpio_ctrlport_bfm.write(
+ kSPI_TRANSACTION_GO,
+ (write_data >> (transaction_length - 32)) & kSPI_TRANSACTION_GOMask);
+ end else begin
+ gpio_ctrlport_bfm.write(
+ kSPI_TRANSACTION_GO,
+ (write_data << (32 - transaction_length)) & kSPI_TRANSACTION_GOMask);
+ end
+ endtask
+
+ task automatic execute_transaction(
+ input int unsigned selected_peripheral,
+ input logic [63:0] write_data,
+ input logic [63:0] expected_response
+ );
+ int unsigned spi_status_read;
+ int unsigned spi_response_msb;
+ int bfm_error_count;
+ int timing_error_count;
+ int unsigned transaction_length;
+ logic [62:0] full_response;
+ logic [63:0] response_mask;
+ logic [31:0] status_mask;
+
+
+ transaction_length = TRANSACTION_LENGTHS[selected_peripheral];
+ response_mask = get_response_mask(transaction_length);
+ status_mask = get_status_mask(transaction_length);
+ queue_bfm_transaction(selected_peripheral, write_data, expected_response);
+ start_transaction(transaction_length, write_data);
+ wait_for_cs(selected_peripheral);
+ repeat (20) @(posedge clk);
+ gpio_ctrlport_bfm.read(kSPI_STATUS, spi_status_read);
+
+ test.assert_error(
+ spi_status_read[kSPI_READY],
+ $sformatf("SPI%0d transaction not complete", transaction_length));
+ test.assert_error(
+ (spi_status_read & status_mask) == (expected_response & status_mask),
+ $sformatf("SPI%0d status response failed. Expected 0x%0h, got 0x%0h",
+ transaction_length, expected_response & status_mask,
+ spi_status_read & status_mask));
+
+ full_response = {32'b0, spi_status_read[30:0]};
+ if (transaction_length > 31) begin
+ gpio_ctrlport_bfm.read(kSPI_FULL_RESPONSE_MSB, spi_response_msb);
+ full_response[62:31] = spi_response_msb;
+ end
+ test.assert_error(
+ (full_response & response_mask) == (expected_response & response_mask),
+ $sformatf("SPI%0d full response failed. Expected 0x%0h, got 0x%0h, mask 0x%0h",
+ transaction_length, expected_response, full_response, response_mask));
+
+ get_bfm_error_count(selected_peripheral, bfm_error_count);
+ test.assert_error(
+ bfm_error_count == 0,
+ $sformatf("SPI%0d BFM reported %0d errors", transaction_length, bfm_error_count));
+ timing_error_count = spi_timing_mon[selected_peripheral].get_error_count();
+ test.assert_error(
+ timing_error_count == 0,
+ $sformatf("SPI%0d timing monitor reported %0d errors",
+ transaction_length, timing_error_count));
+ endtask
+
+ initial begin : main
+ string tb_name;
+ int unsigned selected_peripheral;
+ int unsigned previous_peripheral;
+
+ `ifdef X410
+ tb_name = "x410_gpio_spi_tb";
+ `else
+ tb_name = "x4xx_gpio_spi_tb";
+ `endif
+ test.start_tb(tb_name);
+ gpio_ctrlport_bfm.run();
+ clk_gen.start();
+ clk_gen_x2.start();
+ clk_gen.reset(50);
+ //-------------------------------------------------------------------------
+
+ test.start_test("Full configuration and transaction", 100us);
+
+ for (int unsigned selected_peripheral = 0; selected_peripheral < NUM_PERIPHERALS; selected_peripheral++) begin
+ configure_peripheral(selected_peripheral);
+ test.assert_error(
+ gpio_out[CS_PINS[selected_peripheral]] === 1'b1,
+ $sformatf("SPI%0d CS is not high after configuration", TRANSACTION_LENGTHS[selected_peripheral]));
+ test.assert_error(
+ gpio_ddr[CS_PINS[selected_peripheral]] === 1'b1,
+ $sformatf("SPI%0d CS is not configured as an output", TRANSACTION_LENGTHS[selected_peripheral]));
+ end
+ test.assert_error(gpio_ddr[CLK_PIN] === 1'b1, "SPI clock is not configured as an output");
+ test.assert_error(gpio_ddr[SDO_PIN] === 1'b1, "SPI SDO is not configured as an output");
+ test.assert_error(gpio_ddr[SDI_PIN] === 1'b0, "SPI SDI is not configured as an input");
+
+ // Only start SPI BFM after configuration is done (CS is high)
+ start_bfms();
+
+ configure_transaction(0, CLK_DIV_1);
+ execute_transaction(0, 64'hFAFAFAFA00000001, 64'h0123456789ABCDEF);
+ //Consecutive transaction with new divider
+ configure_transaction(0, CLK_DIV_2);
+ execute_transaction(0, 64'hFAFAFAFA00000001, 64'h0123456789ABCDEF);
+ //Transaction on different peripheral and new divider
+ configure_transaction(2, CLK_DIV_1);
+ execute_transaction(2, 64'hBA4B0EB4, 64'h98765432);
+ //Transaction on different peripheral and new divider
+ configure_transaction(1, CLK_DIV_1);
+ execute_transaction(1, 64'h0000000A010FA7BF, 64'h000000DCBA987654);
+ //Consecutive transaction without changing the clock divider
+ configure_transaction(3, CLK_DIV_1);
+ execute_transaction(3, 64'h00000000000FA7BF, 64'h0000000000054321);
+ execute_transaction(3, 64'h00000000000FA00F, 64'h0000000000054001);
+
+ test.end_test();
+ //-------------------------------------------------------------------------
+
+ test.start_test("Random transactions", 2ms);
+ previous_peripheral = NUM_PERIPHERALS;
+ for (int unsigned transaction = 0; transaction < 100; transaction++) begin
+ selected_peripheral = $urandom_range(NUM_PERIPHERALS - 1, 0);
+ if (selected_peripheral != previous_peripheral) begin
+ configure_transaction(selected_peripheral, CLK_DIV_2);
+ end
+ execute_transaction(selected_peripheral, {$urandom(), $urandom()}, {$urandom(), $urandom()});
+ previous_peripheral = selected_peripheral;
+ end
+ test.end_test();
+
+ test.end_tb();
+ end
+
+endmodule
+
+`default_nettype wire
diff --git a/fpga/usrp3/top/x400/x4xx.sv b/fpga/usrp3/top/x400/x4xx.sv
index 7906db898..aed5df6d0 100644
--- a/fpga/usrp3/top/x400/x4xx.sv
+++ b/fpga/usrp3/top/x400/x4xx.sv
@@ -3843,13 +3843,13 @@ endmodule
// Oldest compatible version: @.VERSIONING_REGS_REGMAP..OLDEST_COMPATIBLE_VERSION
// Version last modified: @.VERSIONING_REGS_REGMAP..VERSION_LAST_MODIFIED
//
-//
+//
//
//
-//
+//
//
//
-//
+//
//
//
//
diff --git a/fpga/usrp3/top/x400/x4xx_gpio_spi.v b/fpga/usrp3/top/x400/x4xx_gpio_spi.sv
similarity index 71%
rename from fpga/usrp3/top/x400/x4xx_gpio_spi.v
rename to fpga/usrp3/top/x400/x4xx_gpio_spi.sv
index 08eacd108..c857d7b17 100644
--- a/fpga/usrp3/top/x400/x4xx_gpio_spi.v
+++ b/fpga/usrp3/top/x400/x4xx_gpio_spi.sv
@@ -1,5 +1,5 @@
//
-// Copyright 2021 Ettus Research, A National Instruments Brand
+// Copyright 2026 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
@@ -28,7 +28,7 @@
module x4xx_gpio_spi #(
parameter NUM_SLAVES = 4,
parameter [19:0] BASE_ADDRESS = 0,
- parameter [19:0] SIZE_ADDRESS = 19'h20
+ parameter [19:0] SIZE_ADDRESS = 19'h2C
) (
input wire ctrlport_clk,
input wire ctrlport_clk_2x,
@@ -52,14 +52,14 @@ module x4xx_gpio_spi #(
);
`include "../../lib/rfnoc/core/ctrlport.vh"
- `include "regmap/dig_ifc_regmap_utils.vh"
+ import XmlSvPkgDIG_IFC_REGMAP::*;
// Registers / wires for SPI core communication
reg [31:0] set_data = 0;
reg [ 7:0] set_addr = 0;
reg set_stb = 1'b0;
- wire [31:0] readback;
+ wire [63:0] readback;
wire readback_stb;
wire readback_stb_extended;
@@ -80,10 +80,10 @@ module x4xx_gpio_spi #(
// These arrays are set to the maximum supported SPI slaves instead of
// the provided NUM_SLAVES to facilitate concurrent re-mapping.
// See section(GPIO Mapping) of this file.
- reg [ SLAVE_CLK_SIZE-1:0] sclk_mapping [3:0];
- reg [SLAVE_MOSI_SIZE-1:0] mosi_mapping [3:0];
- reg [SLAVE_MISO_SIZE-1:0] miso_mapping [3:0];
- reg [ SLAVE_CS_SIZE-1:0] ss_mapping [3:0];
+ reg [kSLAVE_CLKSize-1:0] sclk_mapping [3:0];
+ reg [kSLAVE_MOSISize-1:0] mosi_mapping [3:0];
+ reg [kSLAVE_MISOSize-1:0] miso_mapping [3:0];
+ reg [ kSLAVE_CSSize-1:0] ss_mapping [3:0];
//---------------------------------------------------------------------------
// Address calculation
@@ -95,8 +95,8 @@ module x4xx_gpio_spi #(
// Check that address is targeting slave configuration.
wire address_is_slave =
- (s_ctrlport_req_addr >= BASE_ADDRESS + SPI_SLAVE_CONFIG(0)) &&
- (s_ctrlport_req_addr <= BASE_ADDRESS + SPI_SLAVE_CONFIG(3));
+ (s_ctrlport_req_addr >= BASE_ADDRESS + kSPI_SLAVE_CONFIG(0)) &&
+ (s_ctrlport_req_addr <= BASE_ADDRESS + kSPI_SLAVE_CONFIG(3));
// Decode the slave being addressed.
wire [1:0]slave_address = s_ctrlport_req_addr[3:2];
@@ -108,7 +108,7 @@ module x4xx_gpio_spi #(
// These settings are registered individually for each slave
reg [ NUM_SLAVES-1:0] data_in_edge = {NUM_SLAVES{1'b0}};
reg [ NUM_SLAVES-1:0] data_out_edge = {NUM_SLAVES{1'b0}};
- reg [SPI_LENGTH_SIZE-1:0] slave_spi_length [NUM_SLAVES-1:0];
+ reg [kSPI_LENGTHSize-1:0] slave_spi_length [NUM_SLAVES-1:0];
// One-hot encoding to indicate active slave
reg [NUM_SLAVES-1:0] slave_select = {NUM_SLAVES{1'b0}};
@@ -119,20 +119,25 @@ module x4xx_gpio_spi #(
localparam IDLE = 3'd0;
localparam SET_DIVIDER = 3'd1;
- localparam WRITE_SPI = 3'd2;
- localparam CONFIG_TRANSFER = 3'd3;
- localparam WAIT_SPI = 3'd4;
+ localparam WRITE_SPI_LSB = 3'd2;
+ localparam WRITE_SPI = 3'd3;
+ localparam CONFIG_TRANSFER = 3'd4;
+ localparam WAIT_SPI = 3'd5;
+ // Addresses in simple_spi_core_64bit.v
localparam DIVIDER_ADDRESS = 8'd0;
localparam CTRL_ADDRESS = 8'd1;
localparam DATA_ADDRESS = 8'd2;
+ localparam DATA_LSB_ADDRESS = 8'd3;
reg [ 2:0] state = IDLE;
reg [ 31:0] data_cache;
- reg [SPI_CLK_DIV_SIZE-1:0] divider;
+ reg [kSPI_CLK_DIVSize-1:0] divider;
reg [ 1:0] cs;
reg spi_go = 1'b0;
reg spi_ready = 1'b0;
+ // Additional bits for optional 64 bit transaction
+ reg [ 31:0] data_cache_lsb;
integer slave_i;
@@ -146,20 +151,21 @@ module x4xx_gpio_spi #(
s_ctrlport_resp_ack <= 1'b0;
spi_go <= 1'b0;
spi_ready <= 1'b0;
- divider <= {SPI_CLK_DIV_SIZE{1'b0}};
+ divider <= {kSPI_CLK_DIVSize{1'b0}};
cs <= 2'b0;
+ data_cache <= 32'h0;
- // Assigned to unassigned mapping. This avoids overwriting
- // signals with those from uninitialized slaves.
+ // Assigned to unassigned mapping (pin 31 is unused). This avoids
+ // overwriting signals with those from uninitialized slaves.
for ( slave_i = 0; slave_i < 4; slave_i = slave_i + 1 ) begin
- sclk_mapping[slave_i] <= 5'h31;
- mosi_mapping[slave_i] <= 5'h31;
- miso_mapping[slave_i] <= 5'h31;
- ss_mapping [slave_i] <= 5'h31;
+ sclk_mapping[slave_i] <= 5'd31;
+ mosi_mapping[slave_i] <= 5'd31;
+ miso_mapping[slave_i] <= 5'd31;
+ ss_mapping [slave_i] <= 5'd31;
end
for ( slave_i = 0; slave_i < NUM_SLAVES; slave_i = slave_i + 1) begin
- slave_spi_length[slave_i] <= {SPI_LENGTH_SIZE{1'b0}};
+ slave_spi_length[slave_i] <= {kSPI_LENGTHSize{1'b0}};
end
end else begin
@@ -175,26 +181,33 @@ module x4xx_gpio_spi #(
// Address spi configuration writes
if(address_is_slave) begin
+ automatic SPI_SETUP spi_setup = s_ctrlport_req_data;
// GPIO mapping
- sclk_mapping [slave_address] <= s_ctrlport_req_data[SLAVE_CLK_MSB:SLAVE_CLK];
- mosi_mapping [slave_address] <= s_ctrlport_req_data[SLAVE_MOSI_MSB:SLAVE_MOSI];
- miso_mapping [slave_address] <= s_ctrlport_req_data[SLAVE_MISO_MSB:SLAVE_MISO];
- ss_mapping [slave_address] <= s_ctrlport_req_data[SLAVE_CS_MSB:SLAVE_CS];
+ sclk_mapping [slave_address] <= spi_setup.SLAVE_CLK;
+ mosi_mapping [slave_address] <= spi_setup.SLAVE_MOSI;
+ miso_mapping [slave_address] <= spi_setup.SLAVE_MISO;
+ ss_mapping [slave_address] <= spi_setup.SLAVE_CS;
// Transfer Configuration
- slave_spi_length [slave_address] <= s_ctrlport_req_data[SPI_LENGTH_MSB:SPI_LENGTH];
- data_in_edge [slave_address] <= s_ctrlport_req_data[MISO_EDGE];
- data_out_edge [slave_address] <= s_ctrlport_req_data[MOSI_EDGE];
+ slave_spi_length [slave_address] <= spi_setup.SPI_LENGTH;
+ data_in_edge [slave_address] <= spi_setup.MISO_EDGE;
+ data_out_edge [slave_address] <= spi_setup.MOSI_EDGE;
end else begin
case (s_ctrlport_req_addr)
- BASE_ADDRESS + SPI_TRANSACTION_CONFIG: begin
- divider <= s_ctrlport_req_data[SPI_CLK_DIV_MSB:SPI_CLK_DIV];
- cs <= s_ctrlport_req_data[SPI_SLAVE_SELECT_MSB:SPI_SLAVE_SELECT];
+ BASE_ADDRESS + kSPI_TRANSACTION_CONFIG: begin
+ automatic SPI_TRANSACTION_CONFIG spi_transaction_config = s_ctrlport_req_data;
+ divider <= spi_transaction_config.SPI_CLK_DIV;
+ cs <= spi_transaction_config.SPI_SLAVE_SELECT;
end
- BASE_ADDRESS + SPI_TRANSACTION_GO: begin
+ BASE_ADDRESS + kSPI_TRANSACTION_LSB: begin
+ data_cache_lsb <= s_ctrlport_req_data;
+ end
+
+ BASE_ADDRESS + kSPI_TRANSACTION_GO: begin
spi_ready <= 1'b0;
spi_go <= 1'b1;
+ data_cache <= s_ctrlport_req_data;
end
// No register implementation for provided address
@@ -214,22 +227,34 @@ module x4xx_gpio_spi #(
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}};
+ 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 + SPI_TRANSACTION_CONFIG: begin
- s_ctrlport_resp_data[SPI_CLK_DIV_MSB:SPI_CLK_DIV] <= divider;
+ BASE_ADDRESS + kSPI_TRANSACTION_CONFIG: begin
+ automatic SPI_TRANSACTION_CONFIG spi_transaction_config = '0;
+ spi_transaction_config.SPI_CLK_DIV = divider;
+ s_ctrlport_resp_data <= spi_transaction_config;
end
- BASE_ADDRESS + SPI_STATUS: begin
- s_ctrlport_resp_data[SPI_READY] <= spi_ready;
- s_ctrlport_resp_data[SPI_RESPONSE_MSB:SPI_RESPONSE] <= readback[SPI_RESPONSE_MSB:SPI_RESPONSE];
+ BASE_ADDRESS + kSPI_STATUS: begin
+ automatic SPI_STATUS spi_status = '0;
+ spi_status.SPI_READY = spi_ready;
+ spi_status.SPI_RESPONSE = readback[30:0];
+ s_ctrlport_resp_data <= spi_status;
end
- BASE_ADDRESS + CONTROLLER_INFO: begin
- s_ctrlport_resp_data[SLAVE_COUNT_MSB:SLAVE_COUNT] <= NUM_SLAVES;
+ BASE_ADDRESS + kSPI_FULL_RESPONSE_MSB: begin
+ automatic SPI_FULL_RESPONSE_MSB spi_full_response = '0;
+ spi_full_response.SPI_FULL_RESPONSE_MSB_DATA = readback[62:31];
+ s_ctrlport_resp_data <= spi_full_response;
+ end
+
+ BASE_ADDRESS + kCONTROLLER_INFO: begin
+ automatic CONTROLLER_INFO controller_info = '0;
+ controller_info.SLAVE_COUNT = NUM_SLAVES;
+ s_ctrlport_resp_data <= controller_info;
end
// No register implementation for provided address
@@ -260,9 +285,8 @@ module x4xx_gpio_spi #(
always @ (posedge ctrlport_clk) begin
if (ctrlport_rst) begin
- state <= IDLE;
- set_stb <= 1'b0;
- data_cache <= 32'h0;
+ state <= IDLE;
+ set_stb <= 1'b0;
end else begin
// Default Assignments
@@ -270,8 +294,6 @@ module x4xx_gpio_spi #(
case (state)
IDLE: begin
- // Save data and address for further steps
- data_cache <= s_ctrlport_req_data;
if (spi_go) begin
state <= CONFIG_TRANSFER;
@@ -286,23 +308,37 @@ module x4xx_gpio_spi #(
set_stb <= 1'b1;
set_addr <= CTRL_ADDRESS;
- set_data <= { data_out_edge[cs], // 1 bit
- data_in_edge[cs], // 1 bit
- slave_spi_length[cs], // 6 bits
- {24-NUM_SLAVES{1'b0}}, // Padding for slaves
- slave_select // NUM_SLAVES bits
+ set_data <= { data_out_edge[cs], // 1 bit
+ data_in_edge[cs], // 1 bit
+ slave_spi_length[cs][5:0], // 6 bits, ignore MSB, 0 is considered as 64
+ {24-NUM_SLAVES{1'b0}}, // Padding for slaves
+ slave_select // NUM_SLAVES bits
};
end
// Write divider to SPI core
SET_DIVIDER: begin
- state <= WRITE_SPI;
+ if (slave_spi_length[cs] > 32) begin
+ state <= WRITE_SPI_LSB;
+ end else begin
+ state <= WRITE_SPI;
+ end
set_stb <= 1'b1;
set_addr <= DIVIDER_ADDRESS;
set_data <= {16'b0, divider};
end
+
+ // Write data bits to SPI core (aligned to MSB)
+ WRITE_SPI_LSB: begin
+ state <= WRITE_SPI;
+
+ set_stb <= 1'b1;
+ set_addr <= DATA_LSB_ADDRESS;
+ set_data <= data_cache_lsb;
+ end
+
// Write data bits to SPI core (aligned to MSB)
WRITE_SPI: begin
state <= WAIT_SPI;
@@ -326,14 +362,14 @@ module x4xx_gpio_spi #(
endcase
// Update Auxiliary signals
- gpio_is_mosi <= 32'h0;
- gpio_is_sclk <= 32'h0;
- gpio_is_cs <= 32'h0;
+ gpio_is_mosi <= 32'h0;
+ gpio_is_sclk <= 32'h0;
+ gpio_is_cs <= 32'h0;
for ( slave_i = 0; slave_i < NUM_SLAVES; slave_i = slave_i + 1) begin
- gpio_is_mosi [mosi_mapping[slave_i]] <= 1'b1;
- gpio_is_sclk [sclk_mapping[slave_i]] <= 1'b1;
- gpio_is_cs [ ss_mapping[slave_i]] <= 1'b1;
+ gpio_is_mosi[mosi_mapping[slave_i]] <= 1'b1;
+ gpio_is_sclk[sclk_mapping[slave_i]] <= 1'b1;
+ gpio_is_cs [ ss_mapping[slave_i]] <= 1'b1;
end
end
@@ -345,11 +381,12 @@ module x4xx_gpio_spi #(
`ifndef X410
- simple_spi_core #(
+ simple_spi_core_64bit #(
.BASE (0),
.WIDTH (NUM_SLAVES),
.CLK_IDLE (0),
- .SEN_IDLE ({NUM_SLAVES{1'b1}})
+ .SEN_IDLE ({NUM_SLAVES{1'b1}}),
+ .MAX_BITS (64)
) simple_spi_core_i (
.clock (ctrlport_clk),
.reset (ctrlport_rst),
@@ -414,11 +451,12 @@ module x4xx_gpio_spi #(
end
end
- simple_spi_core #(
+ simple_spi_core_64bit #(
.BASE (0),
.WIDTH (NUM_SLAVES),
.CLK_IDLE (0),
- .SEN_IDLE ({NUM_SLAVES{1'b1}})
+ .SEN_IDLE ({NUM_SLAVES{1'b1}}),
+ .MAX_BITS (64)
) simple_spi_core_i (
.clock (ctrlport_clk_2x),
.reset (ctrlport_rst),
@@ -514,7 +552,7 @@ endmodule
`default_nettype wire
//XmlParse xml_on
-//
+//
//
//
//
@@ -550,19 +588,22 @@ endmodule
// 16-27: Port B GPIO
//
//
-//
+//
//
-// Indicates the length of SPI transactions to this slave.
+// Indicates the length of SPI transactions to this slave.
+// Valid transaction lengths are 1 through 64 bits.
+// This is not checked on FPGA and other values will
+// lead to undefined behavior.
//
//
-//
+//
//
// Controls the edge in which the MISO line is latched.
// 0 = falling edge of SCLK.
// 1 = rising edge of SCLK.
//
//
-//
+//
//
// Controls the edge in which the MOSI line is updated.
// 0 = falling edge of SCLK.
@@ -588,27 +629,51 @@ endmodule
//
//
//
-//
+//
//
-// Starts a SPI transaction
+// For SPI transactions with @.SPI_SLAVE_CONFIG.SPI_LENGTH > 32, this must be written
+// before @.SPI_TRANSACTION_GO.
+//
+//
+// Left-aligned LSB payload for SPI transaction with @.SPI_SLAVE_CONFIG.SPI_LENGTH > 32.
+// See @.SPI_TRANSACTION_GO for more information.
+//
+//
+//
+//
+// Starts a SPI transaction. Does not check whether a previous transaction is still ongoing.
+// If so, the running transaction may be corrupted.
//
//
-// Payload to be sent for the SPI transaction. If the payload is shorter than 32 bits,
-// it must be aligned to the MSbs in this field. LSbs are ignored in this scenario.
+// Payload to be sent for the SPI transaction.
+// If the payload is shorter than 32 bits, it must be left-aligned.
+// The first transmitted bit is the MSB and data is shifted out to the left.
+// If the desired payload is longer than 32 bits, the remaining LSBs
+// must be written to @.SPI_TRANSACTION_LSB.SPI_DATA_LSB (left-aligned) before starting
+// the transaction.
//
//
-//
+//
//
-// Contains the status of the SPI engine.
+// Contains the status of the SPI engine and the least-significant 31 bits of the response.
+// Also read SPI_FULL_RESPONSE_MSB for bit 32 or for transactions longer than 32 bits.
//
-//
+//
// Indicates the SPI engine is ready to start a new SPI transaction.
//
-//
-// Records the response of the last completed SPI transaction.
+//
+// Records only the least-significant 31 bits of the last completed SPI response.
//
//
-//
+//
+//
+// Contains bits 62 through 31 of the last completed SPI response.
+// For transactions shorter than 31 bits, the upper bits in this register are undefined.
+// The regmap does not expose the most significant bit (bit 63) of the response.
+//
+//
+//
+//
//
// Contains information pertaining this SPI controller block.
//
|