From 38c549d1f7672e38773fc6624539cc166285a1df Mon Sep 17 00:00:00 2001 From: Javier Valenzuela Date: Wed, 29 Sep 2021 10:04:04 -0500 Subject: fpga: x400: Add SPI bus support for GPIO ports --- fpga/usrp3/top/x400/Makefile.x4xx.inc | 1 + fpga/usrp3/top/x400/doc/X4XX_FPGA_left.htm | 18 +- fpga/usrp3/top/x400/doc/X4XX_FPGA_right.htm | 553 ++++++++++++++++++++- fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh | 88 ++++ .../top/x400/regmap/radio_dio_regmap_utils.vh | 5 + fpga/usrp3/top/x400/x4xx_core.v | 1 + fpga/usrp3/top/x400/x4xx_core_common.v | 194 ++++++-- fpga/usrp3/top/x400/x4xx_gpio_atr.v | 4 +- fpga/usrp3/top/x400/x4xx_gpio_spi.v | 534 ++++++++++++++++++++ 9 files changed, 1338 insertions(+), 60 deletions(-) create mode 100644 fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh create mode 100644 fpga/usrp3/top/x400/x4xx_gpio_spi.v (limited to 'fpga') diff --git a/fpga/usrp3/top/x400/Makefile.x4xx.inc b/fpga/usrp3/top/x400/Makefile.x4xx.inc index 2c6de75ec..fa6208088 100644 --- a/fpga/usrp3/top/x400/Makefile.x4xx.inc +++ b/fpga/usrp3/top/x400/Makefile.x4xx.inc @@ -74,6 +74,7 @@ x4xx_global_regs.v \ x4xx_versioning_regs.v \ x4xx_dio.v \ x4xx_gpio_atr.v \ +x4xx_gpio_spi.v \ rf/100m/rf_core_100m.v \ rf/200m/rf_core_200m.v \ rf/200m/rf_down_4to2.v \ diff --git a/fpga/usrp3/top/x400/doc/X4XX_FPGA_left.htm b/fpga/usrp3/top/x400/doc/X4XX_FPGA_left.htm index c302c2f9c..0a101710e 100644 --- a/fpga/usrp3/top/x400/doc/X4XX_FPGA_left.htm +++ b/fpga/usrp3/top/x400/doc/X4XX_FPGA_left.htm @@ -269,6 +269,21 @@

IPASS_CONTROL

+

+ + + DIG_IFC_REGMAP +

+

+ + + SPI_OVER_GPIO_REGS +

+
+

SPI_SLAVE_CONFIG

+

SPI_TRANSACTION_CONFIG

+

SPI_TRANSACTION_GO

+

SPI_STATUS

+
+

+ DIO_REGMAP @@ -579,7 +594,8 @@

RADIO_GPIO_ATR_REGS

-

DIO_SOURCE_CONTROL

+

DIO_SOURCE_CONTROL

+

DIGITAL_IFC_REGS

diff --git a/fpga/usrp3/top/x400/doc/X4XX_FPGA_right.htm b/fpga/usrp3/top/x400/doc/X4XX_FPGA_right.htm index 4e3b3c3a8..2827c1a93 100644 --- a/fpga/usrp3/top/x400/doc/X4XX_FPGA_right.htm +++ b/fpga/usrp3/top/x400/doc/X4XX_FPGA_right.htm @@ -3229,6 +3229,492 @@ Total Offset = + + +

+ +

DIG_IFC_REGMAP

+ +

SPI_OVER_GPIO_REGS

+ +
+ + +

Offset 0x0000: SPI_SLAVE_CONFIG(3:0) Register Array (R|W)

+ + (show extended info) +
+ + + + + + + + + + + + + + + +
+ + + + +
RADIO_CTRLPORT_REGMAP|DIO_WINDOW
  0x00C000
+ +
+ + + + +
RADIO_DIO_REGMAP|DIGITAL_IFC_REGS
  0x002000
+ +
+ + + + +
SPI_SLAVE_CONFIG
  offset=0x0000 + i*4
+ +
+ + + + + + +
+ + +Cannot determine accessibility through this path
+Total Offset =
  0x00E000 + i*4 + +
+ +

+ +

Initial Values
+ + +
default=>0x00000000
+

+ +

This register is defined in HDL source file x4xx_gpio_spi.v.
+It uses RegType SPI_SETUP which is defined in HDL source file x4xx_gpio_spi.v.

+ +
+ +
+ +Controls SPI Transaction
+Set of configuration registers for the supported slaves. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BitsName
31..28 +

Reserved

+

+ +
27 +

MOSI_EDGE   (initialvalue=0)

+

Controls the edge in which the MOSI line is updated.
+ 0 = falling edge of SCLK.
+ 1 = rising edge of SCLK.

+ +
26 +

MISO_EDGE   (initialvalue=0)

+

Controls the edge in which the MISO line is latched.
+ 0 = falling edge of SCLK.
+ 1 = rising edge of SCLK.

+ +
25..20 +

SPI_LENGTH   (initialvalue=0)

+

Indicates the length of SPI transactions to this slave.

+ +
19..15 +

SLAVE_CS   (initialvalue=0)

+

Indicates which GPIO line to use for the CS signal.
+ 0-11 : Port A GPIO
+ 16-27: Port B GPIO

+ +
14..10 +

SLAVE_MISO   (initialvalue=0)

+

Indicates which GPIO line to use for the MISO signal.
+ 0-11 : Port A GPIO
+ 16-27: Port B GPIO

+ +
9..5 +

SLAVE_MOSI   (initialvalue=0)

+

Indicates which GPIO line to use for the MOSI signal.
+ 0-11 : Port A GPIO
+ 16-27: Port B GPIO

+ +
4..0 +

SLAVE_CLK   (initialvalue=0)

+

Indicates which GPIO line to use for the SCLK signal.
+ 0-11 : Port A GPIO
+ 16-27: Port B GPIO

+ +
+ +
+ +
+ + +

Offset 0x0010: SPI_TRANSACTION_CONFIG Register (R|W)

+ + (show extended info) +
+ + + + + + + + + + + + + + + +
+ + + + +
RADIO_CTRLPORT_REGMAP|DIO_WINDOW
  0x00C000
+ +
+ + + + +
RADIO_DIO_REGMAP|DIGITAL_IFC_REGS
  0x002000
+ +
+ + + + +
SPI_TRANSACTION_CONFIG
  offset=0x0010
+ +
+ + + + + +
+ + +Total Offset =
  0x00E010 + +
+ +

+ +

Initial Value = 0x00000000 +

+ +

This register is defined in HDL source file x4xx_gpio_spi.v.

+ +
+ +
+ +Controls clock rate and target for subsequent SPI transactions. + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
BitsName
31..24 +

Reserved

+

+ +
23..18 +

Reserved

+

+ +
17..16 +

SPI_SLAVE_SELECT   (initialvalue=0)

+

+ +
15..0 +

SPI_CLK_DIV   (initialvalue=0)

+

Controls the rate for subsequent SPI transactions. SCLK = DataClk/[(SPI_CLK_DIV+1)]

+ +
+ +
+ +
+ + +

Offset 0x0014: SPI_TRANSACTION_GO Register (W)

+ + (show extended info) +
+ + + + + + + + + + + + + + + +
+ + + + +
RADIO_CTRLPORT_REGMAP|DIO_WINDOW
  0x00C000
+ +
+ + + + +
RADIO_DIO_REGMAP|DIGITAL_IFC_REGS
  0x002000
+ +
+ + + + +
SPI_TRANSACTION_GO
  offset=0x0014
+ +
+ + + + + +
+ + +Total Offset =
  0x00E014 + +
+ +

+ +

Initial Value = 0x00000000 +

+ +

This register is defined in HDL source file x4xx_gpio_spi.v.

+ +
+ +
+ +Starts a SPI transaction + +
+ + + + + + + + + +
BitsName
31..0w +

SPI_DATA   (initialvalue=0)

+

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.

+ +
+ +
+ +
+ + +

Offset 0x0018: SPI_STATUS Register (R)

+ + (show extended info) +
+ + + + + + + + + + + + + + + +
+ + + + +
RADIO_CTRLPORT_REGMAP|DIO_WINDOW
  0x00C000
+ +
+ + + + +
RADIO_DIO_REGMAP|DIGITAL_IFC_REGS
  0x002000
+ +
+ + + + +
SPI_STATUS
  offset=0x0018
+ +
+ + + + + +
+ + +Total Offset =
  0x00E018 + +
+ +

+ +

Initial Value = 0x00000000 +

+ +

This register is defined in HDL source file x4xx_gpio_spi.v.

+ +
+ +
+ +Contains the status of the SPI engine. + +
+ + + + + + + + + + + + + + + + + + + +
BitsName
31..25 +

Reserved

+

+ +
24 +

SPI_READY   (initialvalue=0)

+

Indicates the SPI engine is ready to start a new SPI transaction.

+ +
23..0 +

SPI_RESPONSE   (initialvalue=0)

+

Records the response of the last completed SPI transaction.

+ +
+ +
+ +
+
@@ -8020,7 +8506,7 @@ Total Offset = Controls whether GPIO lines use the TX and RX state of an RF channel (Classic ATR) or the daughterboard state the selector for the - @.GPIO_ATR_STATE. + ATR_STATE.
@@ -8069,7 +8555,7 @@ Controls whether GPIO lines use the TX and RX state of an RF channel

ATR_OPTION   (initialvalue=0)

Sets the scheme in which RF states in the radio will control GPIO lines. 0 = DB state is used. RF states are combined and the - GPIO state is driven based on all 16 @.GPIO_ATR_STATE registers. + GPIO state is driven based on all 16 ATR_STATE registers. 1 = Each RF channel has its separate ATR state(Classic ATR). Use register CLASSIC_ATR_CONFIG to indicate the RF channel to which each GPIO line responds to.

@@ -16322,6 +16808,69 @@ Window to access the DIO register map through the control port from the radio bl + + +
+ + +

Offset 0x2000: DIGITAL_IFC_REGS Window (R|W)

+

  Target regmap = DIG_IFC_REGMAP

+ (show extended info) +
+ + + + + + + + + + + + + +
+ + + + +
RADIO_CTRLPORT_REGMAP|DIO_WINDOW
  0x00C000
+ +
+ + + + + +
DIGITAL_IFC_REGS
  offset=0x2000
  size=0x1000 (4 Kbytes)
+ +
+ + + + + +
+ + +Total Offset =
  0x00E000 + +
+ +

+ +

This window is defined in HDL source file x4xx_core_common.v.

+ +
+ +
+ +Register space reserved for configuring a digital interface over the GPIO lines. + Currently, SPI is the only supported protocol. + +
+
diff --git a/fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh b/fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh new file mode 100644 index 000000000..7ead185d7 --- /dev/null +++ b/fpga/usrp3/top/x400/regmap/dig_ifc_regmap_utils.vh @@ -0,0 +1,88 @@ +// +// Copyright 2021 Ettus Research, A National Instruments Company +// +// 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) + +//=============================================================================== +// 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 + + // Return the offset of an element of register array SPI_SLAVE_CONFIG + function integer SPI_SLAVE_CONFIG (input integer i); + SPI_SLAVE_CONFIG = (i * 'h4) + 'h0; + endfunction diff --git a/fpga/usrp3/top/x400/regmap/radio_dio_regmap_utils.vh b/fpga/usrp3/top/x400/regmap/radio_dio_regmap_utils.vh index 62de3d75e..6916dc79c 100644 --- a/fpga/usrp3/top/x400/regmap/radio_dio_regmap_utils.vh +++ b/fpga/usrp3/top/x400/regmap/radio_dio_regmap_utils.vh @@ -13,6 +13,7 @@ // RADIO_GPIO_ATR_REGS : 0x0 (x4xx_core_common.v) // DIO_SOURCE_CONTROL : 0x1000 (x4xx_core_common.v) + // DIGITAL_IFC_REGS : 0x2000 (x4xx_core_common.v) //=============================================================================== // RegTypes @@ -29,3 +30,7 @@ // DIO_SOURCE_CONTROL Window (from x4xx_core_common.v) localparam DIO_SOURCE_CONTROL = 'h1000; // Window Offset localparam DIO_SOURCE_CONTROL_SIZE = 'h1000; // size in bytes + + // DIGITAL_IFC_REGS Window (from x4xx_core_common.v) + localparam DIGITAL_IFC_REGS = 'h2000; // Window Offset + localparam DIGITAL_IFC_REGS_SIZE = 'h1000; // size in bytes diff --git a/fpga/usrp3/top/x400/x4xx_core.v b/fpga/usrp3/top/x400/x4xx_core.v index 4e944e335..609892581 100644 --- a/fpga/usrp3/top/x400/x4xx_core.v +++ b/fpga/usrp3/top/x400/x4xx_core.v @@ -249,6 +249,7 @@ module x4xx_core #( .PCIE_PRESENT (0) ) x4xx_core_common_i ( .radio_clk (radio_clk), + .radio_clk_2x (radio_clk_2x), .radio_rst (radio_rst), .rfnoc_chdr_clk (rfnoc_chdr_clk), .rfnoc_chdr_rst (rfnoc_chdr_rst), diff --git a/fpga/usrp3/top/x400/x4xx_core_common.v b/fpga/usrp3/top/x400/x4xx_core_common.v index 3dac18ad5..89dd49a49 100644 --- a/fpga/usrp3/top/x400/x4xx_core_common.v +++ b/fpga/usrp3/top/x400/x4xx_core_common.v @@ -38,6 +38,7 @@ module x4xx_core_common #( ) ( // Clocks and resets input wire radio_clk, + input wire radio_clk_2x, input wire radio_rst, input wire rfnoc_chdr_clk, @@ -234,16 +235,16 @@ module x4xx_core_common #( .s_ctrlport_resp_ack (m_resp_ack), .s_ctrlport_resp_status (m_resp_status), .s_ctrlport_resp_data (m_resp_data), - .m_ctrlport_req_wr ({timekeeper_req_wr, versioning_req_wr, global_regs_req_wr, dio_req_wr}), - .m_ctrlport_req_rd ({timekeeper_req_rd, versioning_req_rd, global_regs_req_rd, dio_req_rd}), - .m_ctrlport_req_addr ({timekeeper_req_addr, versioning_req_addr, global_regs_req_addr, dio_req_addr}), - .m_ctrlport_req_data ({timekeeper_req_data, versioning_req_data, global_regs_req_data, dio_req_data}), + .m_ctrlport_req_wr ({timekeeper_req_wr, versioning_req_wr, global_regs_req_wr, dio_req_wr}), + .m_ctrlport_req_rd ({timekeeper_req_rd, versioning_req_rd, global_regs_req_rd, dio_req_rd}), + .m_ctrlport_req_addr ({timekeeper_req_addr, versioning_req_addr, global_regs_req_addr, dio_req_addr}), + .m_ctrlport_req_data ({timekeeper_req_data, versioning_req_data, global_regs_req_data, dio_req_data}), .m_ctrlport_req_byte_en (), .m_ctrlport_req_has_time (), .m_ctrlport_req_time (), - .m_ctrlport_resp_ack ({timekeeper_resp_ack, versioning_resp_ack, global_regs_resp_ack, dio_resp_ack}), + .m_ctrlport_resp_ack ({timekeeper_resp_ack, versioning_resp_ack, global_regs_resp_ack, dio_resp_ack}), .m_ctrlport_resp_status ({timekeeper_resp_status, versioning_resp_status, global_regs_resp_status, dio_resp_status}), - .m_ctrlport_resp_data ({timekeeper_resp_data, versioning_resp_data, global_regs_resp_data, dio_resp_data}) + .m_ctrlport_resp_data ({timekeeper_resp_data, versioning_resp_data, global_regs_resp_data, dio_resp_data}) ); @@ -349,6 +350,7 @@ module x4xx_core_common #( // Radio CtrlPort Splitter //----------------------------------------------------------------------- + // Radio CtrlPort endpoints wire [ 1*NUM_DBOARDS-1:0] rf_ctrlport_req_wr; wire [ 1*NUM_DBOARDS-1:0] rf_ctrlport_req_rd; wire [ 20*NUM_DBOARDS-1:0] rf_ctrlport_req_addr; @@ -371,9 +373,36 @@ module x4xx_core_common #( wire [ 2*NUM_DBOARDS-1:0] radio_dio_resp_status; wire [ 32*NUM_DBOARDS-1:0] radio_dio_resp_data; + wire [ 1*NUM_DBOARDS-1:0] gpio_atr_ctrlport_req_wr; + wire [ 1*NUM_DBOARDS-1:0] gpio_atr_ctrlport_req_rd; + wire [ 20*NUM_DBOARDS-1:0] gpio_atr_ctrlport_req_addr; + wire [ 32*NUM_DBOARDS-1:0] gpio_atr_ctrlport_req_data; + wire [ 4*NUM_DBOARDS-1:0] gpio_atr_ctrlport_req_byte_en; + wire [ 1*NUM_DBOARDS-1:0] gpio_atr_ctrlport_req_has_time; + wire [ 64*NUM_DBOARDS-1:0] gpio_atr_ctrlport_req_time; + wire [ 1*NUM_DBOARDS-1:0] gpio_atr_ctrlport_resp_ack; + wire [ 2*NUM_DBOARDS-1:0] gpio_atr_ctrlport_resp_status; + wire [ 32*NUM_DBOARDS-1:0] gpio_atr_ctrlport_resp_data; + + wire [ 1*NUM_DBOARDS-1:0] gpio_spi_ctrlport_req_wr; + wire [ 1*NUM_DBOARDS-1:0] gpio_spi_ctrlport_req_rd; + wire [ 20*NUM_DBOARDS-1:0] gpio_spi_ctrlport_req_addr; + wire [ 32*NUM_DBOARDS-1:0] gpio_spi_ctrlport_req_data; + wire [ 4*NUM_DBOARDS-1:0] gpio_spi_ctrlport_req_byte_en; + wire [ 1*NUM_DBOARDS-1:0] gpio_spi_ctrlport_req_has_time; + wire [ 64*NUM_DBOARDS-1:0] gpio_spi_ctrlport_req_time; + wire [ 1*NUM_DBOARDS-1:0] gpio_spi_ctrlport_resp_ack; + wire [ 2*NUM_DBOARDS-1:0] gpio_spi_ctrlport_resp_status; + wire [ 32*NUM_DBOARDS-1:0] gpio_spi_ctrlport_resp_data; + + // GPIO control signals from radio ctrlport endpoints to + // x4xx_dio. wire [NUM_DBOARDS*32-1:0] atr_gpio_out; wire [NUM_DBOARDS*32-1:0] atr_gpio_ddr; + wire [NUM_DBOARDS*32-1:0] spi_gpio_out; + wire [NUM_DBOARDS*32-1:0] spi_gpio_ddr; + genvar db; generate for (db = 0; db < NUM_DBOARDS; db = db+1) begin : gen_radio_ctrlport @@ -385,67 +414,100 @@ module x4xx_core_common #( // This section takes the CtrlPort master from each radio block and splits it // into a CtrlPort bus for the associated daughter(m_radio_ctrlport_*), the // RFDC timing control (rf_ctrlport_*), the ATR GPIO control for the DB state - // the current radio(db) and DIO main control block(x4xx_dio). + // the current radio(db), the SPI controller of the radio, and DIO main + // control block(x4xx_dio). // Refer to diagram in the RADIO_CTRLPORT_REGMAP Register map for a // visual representation on how these interfaces are distributed. // Register space offset calculation localparam [19:0] DIO_SOURCE_CONTROL_OFFSET = DIO_WINDOW + DIO_SOURCE_CONTROL; localparam [19:0] RADIO_GPIO_ATR_OFFSET = DIO_WINDOW + RADIO_GPIO_ATR_REGS; + localparam [19:0] DIGITAL_IFC_OFFSET = DIO_WINDOW + DIGITAL_IFC_REGS; // Register space size calculation localparam [31:0] RFDC_TIMING_WINDOW_SIZE_W = $clog2(RFDC_TIMING_WINDOW_SIZE); localparam [31:0] DB_WINDOW_SIZE_W = $clog2(DB_WINDOW_SIZE); localparam [31:0] DIO_SOURCE_CONTROL_SIZE_W = $clog2(DIO_SOURCE_CONTROL_SIZE); localparam [31:0] RADIO_GPIO_ATR_SIZE_W = $clog2(RADIO_GPIO_ATR_REGS_SIZE); - - wire gpio_atr_ctrlport_req_wr; - wire gpio_atr_ctrlport_req_rd; - wire [19:0] gpio_atr_ctrlport_req_addr; - wire [31:0] gpio_atr_ctrlport_req_data; - wire [ 3:0] gpio_atr_ctrlport_req_byte_en; - wire gpio_atr_ctrlport_req_has_time; - wire [63:0] gpio_atr_ctrlport_req_time; - wire gpio_atr_ctrlport_resp_ack; - wire [ 1:0] gpio_atr_ctrlport_resp_status; - wire [31:0] gpio_atr_ctrlport_resp_data; - + localparam [31:0] DIGITAL_IFC_REGS_SIZE_W = $clog2(DIGITAL_IFC_REGS_SIZE); ctrlport_decoder_param #( - .NUM_SLAVES (4), - .PORT_BASE ({ DIO_SOURCE_CONTROL_OFFSET, + .NUM_SLAVES (5), + .PORT_BASE ({ DIGITAL_IFC_OFFSET, + DIO_SOURCE_CONTROL_OFFSET, RADIO_GPIO_ATR_OFFSET, RFDC_TIMING_WINDOW[19:0], DB_WINDOW[19:0] }), - .PORT_ADDR_W ({ DIO_SOURCE_CONTROL_SIZE_W, + .PORT_ADDR_W ({ DIGITAL_IFC_REGS_SIZE_W, + DIO_SOURCE_CONTROL_SIZE_W, RADIO_GPIO_ATR_SIZE_W, RFDC_TIMING_WINDOW_SIZE_W, DB_WINDOW_SIZE_W }) ) ctrlport_decoder_param_i ( - .ctrlport_clk (radio_clk), - .ctrlport_rst (radio_rst), - .s_ctrlport_req_wr (s_radio_ctrlport_req_wr [ 1*db+: 1]), - .s_ctrlport_req_rd (s_radio_ctrlport_req_rd [ 1*db+: 1]), - .s_ctrlport_req_addr (s_radio_ctrlport_req_addr [20*db+:20]), - .s_ctrlport_req_data (s_radio_ctrlport_req_data [32*db+:32]), - .s_ctrlport_req_byte_en (s_radio_ctrlport_req_byte_en [ 4*db+: 4]), - .s_ctrlport_req_has_time (s_radio_ctrlport_req_has_time [ 1*db+: 1]), - .s_ctrlport_req_time (s_radio_ctrlport_req_time [64*db+:64]), - .s_ctrlport_resp_ack (s_radio_ctrlport_resp_ack [ 1*db+: 1]), - .s_ctrlport_resp_status (s_radio_ctrlport_resp_status [ 2*db+: 2]), - .s_ctrlport_resp_data (s_radio_ctrlport_resp_data [32*db+:32]), - .m_ctrlport_req_wr ({ radio_dio_req_wr [ 1*db+: 1], gpio_atr_ctrlport_req_wr, rf_ctrlport_req_wr [ 1*db+: 1], m_radio_ctrlport_req_wr [ 1*db+: 1] }), - .m_ctrlport_req_rd ({ radio_dio_req_rd [ 1*db+: 1], gpio_atr_ctrlport_req_rd, rf_ctrlport_req_rd [ 1*db+: 1], m_radio_ctrlport_req_rd [ 1*db+: 1] }), - .m_ctrlport_req_addr ({ radio_dio_req_addr [20*db+:20], gpio_atr_ctrlport_req_addr, rf_ctrlport_req_addr [20*db+:20], m_radio_ctrlport_req_addr [20*db+:20] }), - .m_ctrlport_req_data ({ radio_dio_req_data [32*db+:32], gpio_atr_ctrlport_req_data, rf_ctrlport_req_data [32*db+:32], m_radio_ctrlport_req_data [32*db+:32] }), - .m_ctrlport_req_byte_en ({ radio_dio_req_byte_en [ 4*db+: 4], gpio_atr_ctrlport_req_byte_en, rf_ctrlport_req_byte_en [ 4*db+: 4], m_radio_ctrlport_req_byte_en [ 4*db+: 4] }), - .m_ctrlport_req_has_time ({ radio_dio_req_has_time [ 1*db+: 1], gpio_atr_ctrlport_req_has_time, rf_ctrlport_req_has_time [ 1*db+: 1], m_radio_ctrlport_req_has_time [ 1*db+: 1] }), - .m_ctrlport_req_time ({ radio_dio_req_time [64*db+:64], gpio_atr_ctrlport_req_time, rf_ctrlport_req_time [64*db+:64], m_radio_ctrlport_req_time [64*db+:64] }), - .m_ctrlport_resp_ack ({ radio_dio_resp_ack [ 1*db+: 1], gpio_atr_ctrlport_resp_ack, rf_ctrlport_resp_ack [ 1*db+: 1], m_radio_ctrlport_resp_ack [ 1*db+: 1] }), - .m_ctrlport_resp_status ({ radio_dio_resp_status [ 2*db+: 2], gpio_atr_ctrlport_resp_status, rf_ctrlport_resp_status [ 2*db+: 2], m_radio_ctrlport_resp_status [ 2*db+: 2] }), - .m_ctrlport_resp_data ({ radio_dio_resp_data [32*db+:32], gpio_atr_ctrlport_resp_data, rf_ctrlport_resp_data [32*db+:32], m_radio_ctrlport_resp_data [32*db+:32] }) + .ctrlport_clk ( radio_clk ), + .ctrlport_rst ( radio_rst ), + .s_ctrlport_req_wr ( s_radio_ctrlport_req_wr [ 1*db+: 1] ), + .s_ctrlport_req_rd ( s_radio_ctrlport_req_rd [ 1*db+: 1] ), + .s_ctrlport_req_addr ( s_radio_ctrlport_req_addr [20*db+:20] ), + .s_ctrlport_req_data ( s_radio_ctrlport_req_data [32*db+:32] ), + .s_ctrlport_req_byte_en ( s_radio_ctrlport_req_byte_en [ 4*db+: 4] ), + .s_ctrlport_req_has_time ( s_radio_ctrlport_req_has_time [ 1*db+: 1] ), + .s_ctrlport_req_time ( s_radio_ctrlport_req_time [64*db+:64] ), + .s_ctrlport_resp_ack ( s_radio_ctrlport_resp_ack [ 1*db+: 1] ), + .s_ctrlport_resp_status ( s_radio_ctrlport_resp_status [ 2*db+: 2] ), + .s_ctrlport_resp_data ( s_radio_ctrlport_resp_data [32*db+:32] ), + .m_ctrlport_req_wr ({ gpio_spi_ctrlport_req_wr [ 1*db+: 1], + radio_dio_req_wr [ 1*db+: 1], + gpio_atr_ctrlport_req_wr [ 1*db+: 1], + rf_ctrlport_req_wr [ 1*db+: 1], + m_radio_ctrlport_req_wr [ 1*db+: 1] }), + .m_ctrlport_req_rd ({ gpio_spi_ctrlport_req_rd [ 1*db+: 1], + radio_dio_req_rd [ 1*db+: 1], + gpio_atr_ctrlport_req_rd [ 1*db+: 1], + rf_ctrlport_req_rd [ 1*db+: 1], + m_radio_ctrlport_req_rd [ 1*db+: 1] }), + .m_ctrlport_req_addr ({ gpio_spi_ctrlport_req_addr [20*db+:20], + radio_dio_req_addr [20*db+:20], + gpio_atr_ctrlport_req_addr [20*db+:20], + rf_ctrlport_req_addr [20*db+:20], + m_radio_ctrlport_req_addr [20*db+:20] }), + .m_ctrlport_req_data ({ gpio_spi_ctrlport_req_data [32*db+:32], + radio_dio_req_data [32*db+:32], + gpio_atr_ctrlport_req_data [32*db+:32], + rf_ctrlport_req_data [32*db+:32], + m_radio_ctrlport_req_data [32*db+:32] }), + .m_ctrlport_req_byte_en ({ gpio_spi_ctrlport_req_byte_en [ 4*db+: 4], + radio_dio_req_byte_en [ 4*db+: 4], + gpio_atr_ctrlport_req_byte_en [ 4*db+: 4], + rf_ctrlport_req_byte_en [ 4*db+: 4], + m_radio_ctrlport_req_byte_en [ 4*db+: 4] }), + .m_ctrlport_req_has_time ({ gpio_spi_ctrlport_req_has_time [ 1*db+: 1], + radio_dio_req_has_time [ 1*db+: 1], + gpio_atr_ctrlport_req_has_time [ 1*db+: 1], + rf_ctrlport_req_has_time [ 1*db+: 1], + m_radio_ctrlport_req_has_time [ 1*db+: 1] }), + .m_ctrlport_req_time ({ gpio_spi_ctrlport_req_time [64*db+:64], + radio_dio_req_time [64*db+:64], + gpio_atr_ctrlport_req_time [64*db+:64], + rf_ctrlport_req_time [64*db+:64], + m_radio_ctrlport_req_time [64*db+:64] }), + .m_ctrlport_resp_ack ({ gpio_spi_ctrlport_resp_ack [ 1*db+: 1], + radio_dio_resp_ack [ 1*db+: 1], + gpio_atr_ctrlport_resp_ack [ 1*db+: 1], + rf_ctrlport_resp_ack [ 1*db+: 1], + m_radio_ctrlport_resp_ack [ 1*db+: 1] }), + .m_ctrlport_resp_status ({ gpio_spi_ctrlport_resp_status [ 2*db+: 2], + radio_dio_resp_status [ 2*db+: 2], + gpio_atr_ctrlport_resp_status [ 2*db+: 2], + rf_ctrlport_resp_status [ 2*db+: 2], + m_radio_ctrlport_resp_status [ 2*db+: 2] }), + .m_ctrlport_resp_data ({ gpio_spi_ctrlport_resp_data [32*db+:32], + radio_dio_resp_data [32*db+:32], + gpio_atr_ctrlport_resp_data [32*db+:32], + rf_ctrlport_resp_data [32*db+:32], + m_radio_ctrlport_resp_data [32*db+:32] }) ); // Compute ATR state for this radio @@ -459,19 +521,37 @@ module x4xx_core_common #( ) x4xx_gpio_atr_i ( .ctrlport_clk (radio_clk), .ctrlport_rst (radio_rst), - .s_ctrlport_req_wr (gpio_atr_ctrlport_req_wr), - .s_ctrlport_req_rd (gpio_atr_ctrlport_req_rd), - .s_ctrlport_req_addr (gpio_atr_ctrlport_req_addr), - .s_ctrlport_req_data (gpio_atr_ctrlport_req_data), - .s_ctrlport_resp_ack (gpio_atr_ctrlport_resp_ack), - .s_ctrlport_resp_status (gpio_atr_ctrlport_resp_status), - .s_ctrlport_resp_data (gpio_atr_ctrlport_resp_data), + .s_ctrlport_req_wr (gpio_atr_ctrlport_req_wr [ 1*db+: 1]), + .s_ctrlport_req_rd (gpio_atr_ctrlport_req_rd [ 1*db+: 1]), + .s_ctrlport_req_addr (gpio_atr_ctrlport_req_addr [20*db+:20]), + .s_ctrlport_req_data (gpio_atr_ctrlport_req_data [32*db+:32]), + .s_ctrlport_resp_ack (gpio_atr_ctrlport_resp_ack [ 1*db+: 1]), + .s_ctrlport_resp_status (gpio_atr_ctrlport_resp_status [ 2*db+: 2]), + .s_ctrlport_resp_data (gpio_atr_ctrlport_resp_data [32*db+:32]), .db_state (db_state), .gpio_in ({4'b0, gpio_in_b, 4'b0, gpio_in_a}), .gpio_out (atr_gpio_out[db*32+: 32]), .gpio_ddr (atr_gpio_ddr[db*32+: 32]) ); + x4xx_gpio_spi #( + .NUM_SLAVES (2) + ) x4xx_gpio_spi_i( + .ctrlport_clk (radio_clk), + .ctrlport_clk_2x (radio_clk_2x), + .ctrlport_rst (radio_rst), + .s_ctrlport_req_wr (gpio_spi_ctrlport_req_wr [ 1*db+: 1]), + .s_ctrlport_req_rd (gpio_spi_ctrlport_req_rd [ 1*db+: 1]), + .s_ctrlport_req_addr (gpio_spi_ctrlport_req_addr [20*db+:20]), + .s_ctrlport_req_data (gpio_spi_ctrlport_req_data [32*db+:32]), + .s_ctrlport_resp_ack (gpio_spi_ctrlport_resp_ack [ 1*db+: 1]), + .s_ctrlport_resp_status (gpio_spi_ctrlport_resp_status [ 2*db+: 2]), + .s_ctrlport_resp_data (gpio_spi_ctrlport_resp_data [32*db+:32]), + .gpio_out (spi_gpio_out[db*32+: 32]), + .gpio_ddr (spi_gpio_ddr[db*32+: 32]), + .gpio_in ({4'b0, gpio_in_b, 4'b0, gpio_in_a}) + ); + end endgenerate @@ -686,10 +766,10 @@ module x4xx_core_common #( .atr_gpio_ddr (atr_gpio_ddr), .ps_gpio_out ({4'b0, ps_gpio_out_b, 4'b0, ps_gpio_out_a}), .ps_gpio_ddr ({4'b0, ps_gpio_ddr_b, 4'b0, ps_gpio_ddr_a}), - .digital_ifc_gpio_out_radio0 (32'b0), - .digital_ifc_gpio_ddr_radio0 (32'b0), - .digital_ifc_gpio_out_radio1 (32'b0), - .digital_ifc_gpio_ddr_radio1 (32'b0), + .digital_ifc_gpio_out_radio0 (spi_gpio_out[31:0]), + .digital_ifc_gpio_ddr_radio0 (spi_gpio_ddr[31:0]), + .digital_ifc_gpio_out_radio1 (spi_gpio_out[63:32]), + .digital_ifc_gpio_ddr_radio1 (spi_gpio_ddr[63:32]), .user_app_in_a (gpio_in_fabric_a), .user_app_in_b (gpio_in_fabric_b), .user_app_out_a (gpio_out_fabric_a), @@ -771,6 +851,10 @@ endmodule // // Window to access the DIO register map through the control port from the radio blocks. // +// +// Register space reserved for configuring a digital interface over the GPIO lines. +// Currently, SPI is the only supported protocol. +// // // diff --git a/fpga/usrp3/top/x400/x4xx_gpio_atr.v b/fpga/usrp3/top/x400/x4xx_gpio_atr.v index 98ba881f3..86e1a6afe 100644 --- a/fpga/usrp3/top/x400/x4xx_gpio_atr.v +++ b/fpga/usrp3/top/x400/x4xx_gpio_atr.v @@ -312,13 +312,13 @@ endmodule // // Controls whether GPIO lines use the TX and RX state of an RF channel // (Classic ATR) or the daughterboard state the selector for the -// @.GPIO_ATR_STATE. +// @.ATR_STATE. // // // // Sets the scheme in which RF states in the radio will control GPIO // lines. 0 = DB state is used. RF states are combined and the -// GPIO state is driven based on all 16 @.GPIO_ATR_STATE registers. +// GPIO state is driven based on all 16 @.ATR_STATE registers. // 1 = Each RF channel has its separate ATR state(Classic ATR). // Use register @.CLASSIC_ATR_CONFIG to indicate the RF channel // to which each GPIO line responds to. diff --git a/fpga/usrp3/top/x400/x4xx_gpio_spi.v b/fpga/usrp3/top/x400/x4xx_gpio_spi.v new file mode 100644 index 000000000..b5866f867 --- /dev/null +++ b/fpga/usrp3/top/x400/x4xx_gpio_spi.v @@ -0,0 +1,534 @@ +// +// Copyright 2021 Ettus Research, A National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: x4xx_gpio_spi +// +// Description: +// +// This block enables control of a SPI master engine via CtrlPort +// transactions. +// It also enables customization on how signals from the SPI buses +// connected to the master are mapped to the GPIO Ports. +// This block supports configuring communication to up to 4 SPI slaves. +// +// Parameters: +// +// NUM_SLAVES : Number of SPI slaves to be supported. Values from 1 to 4 +// are supported. SPI transfers can only target one slave +// at a time. +// BASE_ADDRESS : Start address for this register block. +// SIZE_ADDRESS : Size of the CtrlPort window to consider in this block. +// + +`default_nettype none + + +module x4xx_gpio_spi #( + parameter NUM_SLAVES = 4, + parameter [19:0] BASE_ADDRESS = 0, + parameter [19:0] SIZE_ADDRESS = 19'h20 +) ( + input wire ctrlport_clk, + input wire ctrlport_clk_2x, + 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 = 1'b0, + output reg [ 1:0] s_ctrlport_resp_status = 2'b0, + output reg [31:0] s_ctrlport_resp_data = 32'b0, + + // GPIO control/status + output wire [31:0] gpio_out, + output wire [31:0] gpio_ddr, + input wire [31:0] gpio_in +); + + `include "../../lib/rfnoc/core/ctrlport.vh" + `include "regmap/dig_ifc_regmap_utils.vh" + + // 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 readback_stb; + wire readback_stb_extended; + + wire sclk; + wire mosi; + wire miso; + // This array is 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. + wire [3:0] ss; + + // Auxiliary signals to compute which GPIO lines are outputs + reg [31:0] gpio_is_mosi = 32'b0; + reg [31:0] gpio_is_sclk = 32'b0; + reg [31:0] gpio_is_cs = 32'b0; + + // SPI-to-GPIO mapping signals. + // 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]; + + //--------------------------------------------------------------------------- + // Address calculation + //--------------------------------------------------------------------------- + + wire address_in_range = + (s_ctrlport_req_addr >= BASE_ADDRESS) && + (s_ctrlport_req_addr < BASE_ADDRESS + SIZE_ADDRESS); + + // 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)); + + // Decode the slave being addressed. + wire [1:0]slave_address = s_ctrlport_req_addr[3:2]; + + //--------------------------------------------------------------------------- + // Slave configuration signals + //--------------------------------------------------------------------------- + + // 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]; + + // One-hot encoding to indicate active slave + reg [NUM_SLAVES-1:0] slave_select = {NUM_SLAVES{1'b0}}; + + //--------------------------------------------------------------------------- + // FSM to handle transfers + //--------------------------------------------------------------------------- + + 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 DIVIDER_ADDRESS = 8'd0; + localparam CTRL_ADDRESS = 8'd1; + localparam DATA_ADDRESS = 8'd2; + + reg [ 2:0] state = IDLE; + reg [ 31:0] data_cache; + reg [SPI_CLK_DIV_SIZE-1:0] divider; + reg [ 1:0] cs; + reg spi_go = 1'b0; + reg spi_ready = 1'b0; + + integer slave_i; + + //--------------------------------------------------------------------------- + // CtrlPort Register endpoints + //--------------------------------------------------------------------------- + + always @ (posedge ctrlport_clk) begin + + if (ctrlport_rst) begin + s_ctrlport_resp_ack <= 1'b0; + spi_go <= 1'b0; + spi_ready <= 1'b0; + divider <= {SPI_CLK_DIV_SIZE{1'b0}}; + cs <= 2'b0; + + // Assigned to unassigned mapping. 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; + end + + for (slave_i=0; slave_i +// +// +// +// +// +// Controls SPI Transaction +// +// +// +// Indicates which GPIO line to use for the SCLK signal.
+// 0-11 : Port A GPIO
+// 16-27: Port B GPIO +//
+//
+// +// +// Indicates which GPIO line to use for the MOSI signal.
+// 0-11 : Port A GPIO
+// 16-27: Port B GPIO +//
+//
+// +// +// Indicates which GPIO line to use for the MISO signal.
+// 0-11 : Port A GPIO
+// 16-27: Port B GPIO +//
+//
+// +// +// Indicates which GPIO line to use for the CS signal.
+// 0-11 : Port A GPIO
+// 16-27: Port B GPIO +//
+//
+// +// +// Indicates the length of SPI transactions to this slave. +// +// +// +// +// 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.
+// 1 = rising edge of SCLK. +//
+//
+//
+// +// Set of configuration registers for the supported slaves. +// +// +// +// Controls clock rate and target for subsequent SPI transactions. +// +// +// Controls the rate for subsequent SPI transactions. SCLK = DataClk/[(SPI_CLK_DIV+1)] +// +// +// +// +// +// Starts a SPI transaction +// +// +// 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. +// +// +// +// +// Contains the status of the SPI engine. +// +// +// Indicates the SPI engine is ready to start a new SPI transaction. +// +// +// Records the response of the last completed SPI transaction. +// +// +//
+// +//XmlParse xml_off -- cgit v1.2.3