[spi_host] Low-level SD card support#446
Conversation
08ccd27 to
db2ad39
Compare
Replace the initial SPI Host loopback connections with connections to the microSD card slot for Genesys2 and an SD card model for Verilator. This includes allocating a GPI pin for card presence detection and a GPO pin for SD card reset (power supply switching). Also output the SPI Host signals on an otherwise unused Genesys2 PMOD header for easier debugging using a logic analyser.
a0fdd00 to
c1e2865
Compare
Add SD card and minimal filesystem helpers for SPI Host, and some tests. These have been ported from sonata-system, which required changes for the different spi_host hardware and translating from C++ to C. Only a subset of the tests will run in Verilator unless an SD card image is provided.
|
Reduced the level of UART logging when |
| ## 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 |
There was a problem hiding this comment.
Any reason to keep the commented line?
There was a problem hiding this comment.
It keeps a record of how to connect up the other SD card pins, should they be needed in future.
| void uart_dump_bytes(uart_t uart, const uint8_t buf[], size_t len) | ||
| { | ||
| for (size_t off = 0u; off < len; ++off) { | ||
| uart_putchar(uart, '0' + (buf[off] >> 4)); |
There was a problem hiding this comment.
Please use the printf functions to implement the dump:
https://github.com/lowRISC/mocha/blob/main/sw/device/lib/runtime/print.h
There was a problem hiding this comment.
Wouldn't that create a circular reference?
| #include "runtime/filesys_utils.h" | ||
| #include "runtime/print.h" | ||
| #include "runtime/sdcard.h" | ||
| // #include <assert.h> |
There was a problem hiding this comment.
Are we not going to be adding support for asserts in the future?
|
|
||
|
|
||
| // Copy a sequence of bytes; destination and source must _not_ overlap. | ||
| static void copy_bytes(uint8_t *dst, const uint8_t *src, size_t len) |
There was a problem hiding this comment.
There was a problem hiding this comment.
Thanks, I'll give this a go next time I have access to an FPGA
| // public: | ||
| // Unfortunately FAT stores the literal values for bytes/sector and sectors/cluster but only | ||
| // powers of two are permitted. | ||
| static inline uint8_t floor_log2(uint16_t n) |
There was a problem hiding this comment.
I think you can use a builtin_ctz here:
Line 14 in 5a99b1d
There was a problem hiding this comment.
I think we want to find the leading set bit, not the trailing set bit
| uint8_t *dataBuffer = fs->buf.dataBuffer; | ||
| if (!read_blocks(fs->spi, 0, dataBuffer, 1u, fs->uart)) { | ||
| if (fs->uart) { | ||
| uart_puts(fs->uart, "Unable to read the MBR of the SD card\n"); |
There was a problem hiding this comment.
I would use uprintf everywhere for consistency
There was a problem hiding this comment.
Why use a formatting printer just to output a string?
| } | ||
|
|
||
| // Open a directory object that started in the given cluster. | ||
| fs_utils_dir_handle_t dir_open(fs_utils_state_t *fs, uint32_t cluster) |
There was a problem hiding this comment.
This is a lot of code to maintain, for the purpose of testing the sdcard, handling directories is not important. So I suggest droping the directory handling and keeping the bare minimum flat file handling.
Specially because the real use case of the SDCard will be done in Uboot which already has a filesystem implementation.
There was a problem hiding this comment.
I'm confused. This code is used in the SD Card test I added. Are you suggesting performing a lesser test?
In any case, I'd have thought it useful to keep this code in case we want to to use bare-metal SD card access in future, such as for a demo.
| { | ||
| // Every card tried seems to be more than capable of keeping up with 12.5Mbps. | ||
| const unsigned kSpiSpeed = 1u; // 50 MHz / (1 + 1) = 12.5 MHz | ||
| DEV_WRITE(spi + SPI_HOST_CONFIGOPTS_REG, 0xFFFF & kSpiSpeed); |
There was a problem hiding this comment.
Why not use the spi_host driver instead of directly accessing the mmios?
There was a problem hiding this comment.
The spi_host HAL does not presently support many of the features of the spi_host block that this code needs to make the SD card communications work. Perhaps the sdcard code could be re-written if the HAL is sufficiently improved, but until then I thought it better to just stick to using the spi_host block directly rather than only partially using the HAL.
marnovandermaas
left a comment
There was a problem hiding this comment.
Thanks for working on this! I've left some initial comments.
| # 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 |
There was a problem hiding this comment.
What does "PROG_RXFN" mean?
There was a problem hiding this comment.
It's the name in the Genesys2 schematic for the net connected to this pin. Unlike Sonata, we're not using the schematic names for the FPGA-top signal names (see #411), so I'm adding them as a comment instead.
| .rst_ni, | ||
|
|
||
| .gpio_i (gpio_inputs), | ||
| .gpio_i ({gpio_inputs[31:10], microsd_det, gpio_inputs[8:0]}), |
There was a problem hiding this comment.
This is a little bit ugly because the gpio_input now will just ignore any writes to bit index 9. I'm not sure how to solve it more cleanly though.
There was a problem hiding this comment.
Indeed, I'm open to better ideas
| logic spi_device_sdi; | ||
|
|
||
| // SPI host signals | ||
| logic spi_host_sck_output, spi_host_csb_output; |
There was a problem hiding this comment.
I'm not sure I think the "output" tag is more clear here. If you don't like just calling the signals spi_host_* then maybe we can label it h2d for host to device or something similar to indicate direction.
There was a problem hiding this comment.
Ah right, I think I was following something like what I did for GPIO, but this is not bidirectional so things can be simpler
| openFPGALoader -b genesys2 build/lowrisc_mocha_chip_mocha_genesys2_0/synth-vivado/lowrisc_mocha_chip_mocha_genesys2_0.bit | ||
| ``` | ||
|
|
||
| For the SD card tests, you will also need to insert a FAT32-formatted microSD card containing the "lorem.ips" file (see sw/device/tests/spi_host/lorem_text.h). |
There was a problem hiding this comment.
Nit: remove "will"
Replace the initial SPI Host loopback connections with connections to the microSD card slot for Genesys2 and an SD card model for Verilator. This includes allocating a GPI pin for card presence detection and a GPO pin for SD card reset (power supply switching).
Also output the SPI Host signals on an otherwise unused Genesys2 PMOD header for easier debugging using a logic analyser.
Add SD card and minimal filesystem helpers for SPI Host, and some tests. These have been ported from sonata-system, which required changes for the different spi_host hardware and translating from C++ to C.
Only a subset of the tests will run in Verilator unless an SD card image is provided.