From 9c81a5dcc893b51312c8b2ba3fd13d27aeb7e657 Mon Sep 17 00:00:00 2001 From: Elliot Baptist Date: Fri, 17 Apr 2026 11:43:27 +0100 Subject: [PATCH] [spi_host] Switch from loopback to SD card access Rework the connections to the SPI Host peripheral to remove the bring-up loopback and replace it with connections to the SD card or a model. For FPGA, this means connecting to the Genesys2 micro-SD card slot. For Verilator, this mean connecting to the SD card model from Sonata. Not that there is currently no test for spi_host, as the previous smoketest relied on the loopback connection, and an SD card test has not been ported yet. Outstanding TODOs remain relating to the card detect signal. --- hw/top_chip/data/pins_genesys2.xdc | 22 +++++- .../dv/verilator/top_chip_verilator.core | 1 + .../dv/verilator/top_chip_verilator.sv | 56 +++++++++----- hw/top_chip/rtl/chip_mocha_genesys2.sv | 73 ++++++++++++------- sw/device/tests/CMakeLists.txt | 3 +- 5 files changed, 106 insertions(+), 49 deletions(-) diff --git a/hw/top_chip/data/pins_genesys2.xdc b/hw/top_chip/data/pins_genesys2.xdc index f2169a66..80539577 100644 --- a/hw/top_chip/data/pins_genesys2.xdc +++ b/hw/top_chip/data/pins_genesys2.xdc @@ -12,6 +12,7 @@ set_property -dict { PACKAGE_PIN AB25 IOSTANDARD LVCMOS33 } [get_ports { ftdi_r ## GPIO # Inputs +# User switches set_property -dict { PACKAGE_PIN G19 IOSTANDARD LVCMOS18 } [get_ports { gpio_i[0] }]; # SW0 (VADJ) set_property -dict { PACKAGE_PIN G25 IOSTANDARD LVCMOS18 } [get_ports { gpio_i[1] }]; # SW1 (VADJ) set_property -dict { PACKAGE_PIN H24 IOSTANDARD LVCMOS18 } [get_ports { gpio_i[2] }]; # SW2 (VADJ) @@ -20,11 +21,14 @@ set_property -dict { PACKAGE_PIN N19 IOSTANDARD LVCMOS18 } [get_ports { gpio_i set_property -dict { PACKAGE_PIN P19 IOSTANDARD LVCMOS18 } [get_ports { gpio_i[5] }]; # SW5 (VADJ) set_property -dict { PACKAGE_PIN P26 IOSTANDARD LVCMOS33 } [get_ports { gpio_i[6] }]; # SW6 (VCC3V3) set_property -dict { PACKAGE_PIN P27 IOSTANDARD LVCMOS33 } [get_ports { gpio_i[7] }]; # SW7 (VCC3V3) - -# Bootstrap pin, should be pulled down during boot to enter bootstrap mode. -set_property -dict { PACKAGE_PIN AB29 IOSTANDARD LVCMOS33 PULLTYPE PULLUP } [get_ports { gpio_i[8] }]; +# Bootstrap pin, should be pulled down during boot to enter bootstrap mode. +set_property -dict { PACKAGE_PIN AB29 IOSTANDARD LVCMOS33 PULLTYPE PULLUP } [get_ports { gpio_i[8] }]; # PROG_RXFN +# Micro SD card presence detect line +# TODO(elliotb): is this active-low or active-high? +set_property -dict { PACKAGE_PIN P28 IOSTANDARD LVCMOS33 } [get_ports { gpio_i[9] }]; # SD_CD # Outputs +# User LEDs set_property -dict { PACKAGE_PIN T28 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[0] }]; # LED0 set_property -dict { PACKAGE_PIN V19 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[1] }]; # LED1 set_property -dict { PACKAGE_PIN U30 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[2] }]; # LED2 @@ -33,6 +37,8 @@ set_property -dict { PACKAGE_PIN V20 IOSTANDARD LVCMOS33 } [get_ports { gpio_o set_property -dict { PACKAGE_PIN V26 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[5] }]; # LED5 set_property -dict { PACKAGE_PIN W24 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[6] }]; # LED6 set_property -dict { PACKAGE_PIN W23 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[7] }]; # LED7 +# Micro SD card power (VDD) control (active-low, ext. pull-up) +set_property -dict { PACKAGE_PIN AE24 IOSTANDARD LVCMOS33 } [get_ports { gpio_o[8] }]; # SD_RESET ## UART set_property -dict { PACKAGE_PIN Y20 IOSTANDARD LVCMOS33 } [get_ports { uart_rx_i }]; @@ -48,9 +54,17 @@ set_property -dict { PACKAGE_PIN Y23 IOSTANDARD LVCMOS33 } [get_ports { uart_t set_property -dict { PACKAGE_PIN T26 IOSTANDARD LVCMOS33 PULLTYPE PULLUP } [get_ports { i2c_scl_io }]; set_property -dict { PACKAGE_PIN T27 IOSTANDARD LVCMOS33 PULLTYPE PULLUP } [get_ports { i2c_sda_io }]; -## SPI (PMOD Header JD) +## SPI Device (PMOD Header JD) set_property -dict { PACKAGE_PIN W28 IOSTANDARD LVCMOS33 PULLTYPE PULLDOWN } [get_ports { spi_device_sd_o }]; set_property -dict { PACKAGE_PIN W27 IOSTANDARD LVCMOS33 PULLTYPE PULLDOWN } [get_ports { spi_device_sd_i }]; set_property -dict { PACKAGE_PIN W29 IOSTANDARD LVCMOS33 PULLTYPE PULLUP } [get_ports { spi_device_csb_i }]; set_property -dict { PACKAGE_PIN AD27 IOSTANDARD LVCMOS33 PULLTYPE PULLDOWN } [get_ports { spi_device_sck_i }]; set_property -dict { PACKAGE_PIN AD29 IOSTANDARD LVCMOS33 } [get_ports { spien }]; + +## SPI Host (MicroSD card slot) +set_property -dict { PACKAGE_PIN R28 IOSTANDARD LVCMOS33 } [get_ports { spi_host_sck_o }]; # SD_SCLK +set_property -dict { PACKAGE_PIN R26 IOSTANDARD LVCMOS33 } [get_ports { spi_host_sd_i }]; # SD_DAT0 +# set_property -dict { PACKAGE_PIN R30 IOSTANDARD LVCMOS33 } [get_ports { microsd_dat1 }]; # SD_DAT1 unused in SPI bus mode +# set_property -dict { PACKAGE_PIN P29 IOSTANDARD LVCMOS33 } [get_ports { microsd_dat2 }]; # SD_DAT2 unused in SPI bus mode +set_property -dict { PACKAGE_PIN T30 IOSTANDARD LVCMOS33 } [get_ports { spi_host_csb_o }]; # SD_DAT3 +set_property -dict { PACKAGE_PIN R29 IOSTANDARD LVCMOS33 } [get_ports { spi_host_sd_o }]; # SD_CMD diff --git a/hw/top_chip/dv/verilator/top_chip_verilator.core b/hw/top_chip/dv/verilator/top_chip_verilator.core index e5310efc..41b132fd 100644 --- a/hw/top_chip/dv/verilator/top_chip_verilator.core +++ b/hw/top_chip/dv/verilator/top_chip_verilator.core @@ -26,6 +26,7 @@ filesets: - lowrisc:dv:sw_test_status - lowrisc:dv:dv_test_status - lowrisc:sonata:i2cdpi + - lowrisc:sonata:spidevicedpi files: - dram_wrapper_sim.sv: { file_type: systemVerilogSource } diff --git a/hw/top_chip/dv/verilator/top_chip_verilator.sv b/hw/top_chip/dv/verilator/top_chip_verilator.sv index c56bf9d2..bdbec9b5 100644 --- a/hw/top_chip/dv/verilator/top_chip_verilator.sv +++ b/hw/top_chip/dv/verilator/top_chip_verilator.sv @@ -27,20 +27,24 @@ module top_chip_verilator (input logic clk_i, rst_ni); logic uart_rx; logic uart_tx; - // SPI signals + // SPI device signals logic spi_device_sck; logic spi_device_csb; logic [3:0] qspi_device_sdo; logic [3:0] qspi_device_sdo_en; logic spi_device_sdi; + // SPI host signals + logic spi_host_sck_output, spi_host_csb_output; + logic spi_host_sck_en_output, spi_host_csb_en_output; + logic spi_host_input; + logic [3:0] spi_host_sd_output; + logic [3:0] spi_host_sd_en_output; + // AXI signals top_pkg::axi_dram_req_t dram_req; top_pkg::axi_dram_resp_t dram_resp; - logic [3:0] spi_host_sd; - logic [3:0] spi_host_sd_en; - // CHERI Mocha top top_chip_system #( ) u_top_chip_system ( @@ -72,25 +76,22 @@ module top_chip_verilator (input logic clk_i, rst_ni); .spi_device_sd_i ({3'h0, spi_device_sdi}), // SPI MOSI = QSPI DQ0 .spi_device_tpm_csb_i ('0), - .spi_host_sck_o ( ), - .spi_host_sck_en_o ( ), - .spi_host_csb_o ( ), - .spi_host_csb_en_o ( ), - .spi_host_sd_o (spi_host_sd), - .spi_host_sd_en_o (spi_host_sd_en), - // Mapping output 0 to input 1 because legacy SPI does not allow - // bi-directional wires. - // This only works in standard mode where sd_o[0]=COPI and - // sd_i[1]=CIPO. - .spi_host_sd_i ({2'b0, spi_host_sd_en[0] ? spi_host_sd[0] : 1'b0, 1'b0}), + .spi_host_sck_o (spi_host_sck_output), + .spi_host_sck_en_o (spi_host_sck_en_output), + .spi_host_csb_o (spi_host_csb_output), + .spi_host_csb_en_o (spi_host_csb_en_output), + .spi_host_sd_o (spi_host_sd_output), + .spi_host_sd_en_o (spi_host_sd_en_output), + // Legacy SPI present in SD cards does not allow bi-directional wires. + // Work in standard mode where sd_o[0]=COPI and sd_i[1]=CIPO. + .spi_host_sd_i ({2'b0, spi_host_input, 1'b0}), // SPI CIPO = QSPI DQ1 .dram_req_o (dram_req), .dram_resp_i (dram_resp) ); // No support for dual or quad SPI in loopback mode right now. - logic unused_spi_host = (|spi_host_sd[3:2]) | spi_host_sd[0] | - (|spi_host_sd_en[3:2]) | spi_host_sd_en[0]; + logic unused_spi_host = |{spi_host_sd_output[3:1], spi_host_sd_en_output[3:1]}; // Virtual GPIO gpiodpi #( @@ -142,8 +143,25 @@ module top_chip_verilator (input logic clk_i, rst_ni); .spi_device_sck_o (spi_device_sck), .spi_device_csb_o (spi_device_csb), .spi_device_sdi_o (spi_device_sdi), - .spi_device_sdo_i (qspi_device_sdo[1]), // SPI MISO = QSPI DQ1 - .spi_device_sdo_en_i(qspi_device_sdo_en[1]) // SPI MISO = QSPI DQ1 + .spi_device_sdo_i (qspi_device_sdo[1]), // SPI CIPO = QSPI DQ1 + .spi_device_sdo_en_i(qspi_device_sdo_en[1]) // SPI CIPO = QSPI DQ1 + ); + + // Virtual SPI Device - model an SD card (in a limited way) + spidevicedpi #( + .ID ("microsd"), + .NDevices (1), + .DataW (1), + .OOB_InW (1), + .OOB_OutW (1) + ) u_spidevicedpi_microsd ( + .rst_ni, + .sck (spi_host_sck_en_output ? spi_host_sck_output : 1'b0), + .cs (spi_host_csb_en_output ? spi_host_csb_output : 1'b0), + .copi (spi_host_sd_en_output[0] ? spi_host_sd_output[0] : 1'b0), // SPI COPI = QSPI DQ0 + .cipo (spi_host_input), + .oob_in ( ), // not used + .oob_out( ) // TODO(elliotb): figure out how to drive gpio_inputs[9] here alongside gpiodpi ); `define DUT u_top_chip_system diff --git a/hw/top_chip/rtl/chip_mocha_genesys2.sv b/hw/top_chip/rtl/chip_mocha_genesys2.sv index 7b9dff63..9ec2b8f0 100644 --- a/hw/top_chip/rtl/chip_mocha_genesys2.sv +++ b/hw/top_chip/rtl/chip_mocha_genesys2.sv @@ -13,9 +13,9 @@ module chip_mocha_genesys2 #( input logic ext_rst_ni, input logic ftdi_rst_ni, - // GPIO - enough for the user switches and LEDs as a starting point - input logic [8:0] gpio_i, - output logic [7:0] gpio_o, + // GPIO + input logic [9:0] gpio_i, + output logic [8:0] gpio_o, // UART input logic uart_rx_i, @@ -25,13 +25,19 @@ module chip_mocha_genesys2 #( inout logic i2c_scl_io, inout logic i2c_sda_io, - // SPI + // SPI Device input logic spi_device_sck_i, input logic spi_device_csb_i, input logic spi_device_sd_i, output logic spi_device_sd_o, output logic spien, + // SPI Host + input logic spi_host_sck_o, + input logic spi_host_csb_o, + input logic spi_host_sd_i, + output logic spi_host_sd_o, + // DDR3 inout wire [31:0] ddr3_dq, inout wire [ 3:0] ddr3_dqs_n, @@ -78,9 +84,10 @@ module chip_mocha_genesys2 #( logic i2c_scl_en_output, i2c_sda_en_output; logic [3:0] qspi_device_sdo; logic [3:0] qspi_device_sdo_en; - - logic [3:0] spi_host_sd; - logic [3:0] spi_host_sd_en; + logic spi_host_sck_output, spi_host_csb_output; + logic spi_host_sck_en_output, spi_host_csb_en_output; + logic [3:0] spi_host_sd_output; + logic [3:0] spi_host_sd_en_output; // AXI signals // Tag controller to CDC FIFO, synchronous to u_top_chip_system.clkmgr_clocks.clk_main_infra @@ -154,7 +161,7 @@ module chip_mocha_genesys2 #( .rst_ni (rst_n_sync_50m), // GPIO - .gpio_i ({23'd0, gpio_i}), + .gpio_i (32'(gpio_i)), .gpio_o (gpio_outputs), .gpio_en_o (gpio_en_outputs), @@ -180,21 +187,17 @@ module chip_mocha_genesys2 #( .spi_device_csb_i (spi_device_csb_i), .spi_device_sd_o (qspi_device_sdo), .spi_device_sd_en_o (qspi_device_sdo_en), - .spi_device_sd_i ({3'h0, spi_device_sd_i}), // SPI MOSI = QSPI DQ0 + .spi_device_sd_i ({3'h0, spi_device_sd_i}), // SPI COPI = QSPI DQ0 .spi_device_tpm_csb_i ('0), // SPI host - .spi_host_sck_o ( ), - .spi_host_sck_en_o ( ), - .spi_host_csb_o ( ), - .spi_host_csb_en_o ( ), - .spi_host_sd_o (spi_host_sd), - .spi_host_sd_en_o (spi_host_sd_en), - // Mapping output 0 to input 1 because legacy SPI does not allow - // bi-directional wires. - // This only works in standard mode where sd_o[0]=COPI and - // sd_i[1]=CIPO. - .spi_host_sd_i ({2'b0, spi_host_sd_en[0] ? spi_host_sd[0] : 1'b0, 1'b0}), + .spi_host_sck_o (spi_host_sck_output), + .spi_host_sck_en_o (spi_host_sck_output_en), + .spi_host_csb_o (spi_host_csb_output), + .spi_host_csb_en_o (spi_host_csb_output_en), + .spi_host_sd_o (spi_host_sd_output), + .spi_host_sd_en_o (spi_host_sd_en_output), + .spi_host_sd_i ({2'b00, spi_host_sd_i, 1'b0}), // SPI CIPO = QSPI DQ1 // DRAM .dram_req_o (dram_req), @@ -203,7 +206,7 @@ module chip_mocha_genesys2 #( // GPIO tri-state output drivers // Instantiate for only the outputs connected to an FPGA pin - for (genvar ii = 0; ii < 8; ii++) begin : gen_gpio_o + for (genvar ii = 0; ii < $bits(gpio_o); ii++) begin : gen_gpio_o OBUFT obuft ( .I(gpio_outputs[ii]), .T(~gpio_en_outputs[ii]), @@ -225,13 +228,33 @@ module chip_mocha_genesys2 #( .O(i2c_sda_input) ); - // SPI tri-state output driver - OBUFT spi_obuft ( - .I(qspi_device_sdo[1]), // SPI MISO = QSPI DQ1 - .T(~qspi_device_sdo_en[1]), // SPI MISO = QSPI DQ1 + // SPI device tri-state output driver + OBUFT spi_device_obuft ( + .I(qspi_device_sdo[1]), // SPI CIPO = QSPI DQ1 + .T(~qspi_device_sdo_en[1]), // SPI CIPO = QSPI DQ1 .O(spi_device_sd_o) ); + // SPI host tri-state output drivers + OBUFT spi_host_sck_obuft ( + .I(spi_host_sck_output), + .T(~spi_host_sck_output_en), + .O(spi_host_sck_o) + ); + OBUFT spi_host_csb_obuft ( + .I(spi_host_csb_output), + .T(~spi_host_csb_output_en), + .O(spi_host_csb_o) + ); + // Legacy SPI present in SD cards does not allow bi-directional wires. + // Work in standard mode where sd_o[0]=COPI and sd_i[1]=CIPO. + // Other data outputs are unused. + OBUFT spi_host_sd_obuft ( + .I(spi_host_sd_output[0]), // SPI COPI = QSPI DQ0 + .T(~spi_host_sd_en_output[0]), // SPI COPI = QSPI DQ0 + .O(spi_host_sd_o) + ); + // Async AXI FIFO from tag controller to MIG axi_cdc #( .aw_chan_t (top_pkg::axi_dram_aw_chan_t), diff --git a/sw/device/tests/CMakeLists.txt b/sw/device/tests/CMakeLists.txt index 059ed967..45fb37d5 100644 --- a/sw/device/tests/CMakeLists.txt +++ b/sw/device/tests/CMakeLists.txt @@ -15,7 +15,8 @@ mocha_add_test(NAME plic_smoketest SOURCES plic/smoketest.c LIBRARIES ${LIBS} FP # Cannot currently run this on FPGA because the boot ROM expects the test to be provided on the SPI again. mocha_add_test(NAME rstmgr_software_reset SOURCES rstmgr/software_reset.c LIBRARIES ${LIBS}) mocha_add_test(NAME spi_device_smoketest SOURCES spi_device/smoketest.c LIBRARIES ${LIBS} FPGA) -mocha_add_test(NAME spi_host_smoketest SOURCES spi_host/smoketest.c LIBRARIES ${LIBS} FPGA) +# SPI Host currently has no working smoketest following switch from loopback to SD card connections +# mocha_add_test(NAME spi_host_smoketest SOURCES spi_host/smoketest.c LIBRARIES ${LIBS} FPGA) mocha_add_test(NAME tag_controller_smoketest SOURCES tag_controller/smoketest.c LIBRARIES ${LIBS} FPGA) mocha_add_test(NAME tag_controller_tag_test SOURCES tag_controller/tag_test.c LIBRARIES ${LIBS} FPGA) mocha_add_test(NAME timer_smoketest SOURCES timer/smoketest.c LIBRARIES ${LIBS} FPGA)