Repository navigation
Conversation
The FPGA boot ROM (sdboot, shared by vcu118, vc707 and zcu104 through symlinks) can drive the SD card at the wrong SPI clock for four independent reasons. Each is enough on its own; this fixes all of them. 1. The card is initialized at full speed. sd_poweron() set SCKDIV to the 25 MHz divisor before CMD0, but the SD specification requires the identification phase at 100-400 kHz. Since ucb-bar#1685 both divisor writes have used SPI_DIV, and SPI_CLK was then raised to 25 MHz. Initialization now uses SPI_INIT_CLK, 250 kHz, with margin from both ends of the range; SPI_DIV is used from the copy onward. The card's own power-up, not the SPI clock, sets how long identification takes, so this costs a few milliseconds. 2. The divisor rounds down, so SCLK rounds up. SCLK = F / (2 (div + 1)), and integer division truncated div, running the card above SPI_CLK whenever the clock is not a multiple of 50 MHz: 30 MHz at 120 MHz, 31.25 MHz at 125 MHz, 37.25 MHz at 149 MHz. The divisor now rounds up, so SCLK never exceeds the requested frequency. 3. The ROM was built from the system bus clock. The SPI controller sits on the peripheral bus (SPIAttachParams.controlWhere = PBUS), so its divisor must come from the peripheral bus frequency. They only agree when all buses run at one frequency. 4. The image is cached across configs. The make rule depended only on the sources, not on PBUS_CLK, so the first config elaborated in a tree fixed the ROM for every later one, whatever its clock (and, through the symlinks, whatever its board). The image takes about 0.1 s to build, so it is now always rebuilt. Divisors written by the built image, before and after: clock init SCLK (before -> after) copy SCLK (before -> after) 50 MHz 25.000 -> 0.250 MHz 25.000 -> 25.000 MHz 100 MHz 25.000 -> 0.250 MHz 25.000 -> 25.000 MHz 120 MHz 30.000 -> 0.250 MHz 30.000 -> 20.000 MHz 125 MHz 31.250 -> 0.250 MHz 31.250 -> 20.833 MHz 149 MHz 37.250 -> 0.250 MHz 37.250 -> 24.833 MHz 150 MHz 25.000 -> 0.250 MHz 25.000 -> 25.000 MHz
The divisors are derived from the peripheral bus clock, which is the SPI controller's clock when it is attached through PeripherySPIKey. A design that attaches the controller another way, to another bus or across a clock crossing, can now set both divisors instead: new WithSDBootSPIDivisors(init = 249, copy = 3) `init` is used for card identification (100-400 kHz), `copy` for the payload. Each is checked against the controller's sckdiv width (SPIParams.divisorBits). sd.c takes them as SPI_INIT_DIV and SPI_DIV when defined at build time, and derives them otherwise; the Makefile passes either through when set. The make invocation for sdboot moves into one helper, SDBoot.makeVars, shared by vcu118, vc707 and zcu104, since the three build the same sources through symlinks.
Contributor
|
Thank you for this PR. Can you kindly let us know when you test this on a hardware board? Thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The FPGA boot ROM (
sdboot, used by the VCU118, VC707 and ZCU104 targets) can drive the SD card at thewrong SPI clock, for four independent reasons. Any one of them is enough to break or slow SD boot.
This PR fixes all four, and lets a design set the divisors directly when its SPI controller is not on
the peripheral bus.
Related: #1875 and #2119 report SD timeouts and slow or failed boots on the VCU118; this is a likely
cause. The diff posted in #2119 (2025-06-15), which lowers the initialization clock, points the same
way; this PR derives both clocks from the configuration instead of fixing divisors.
1. The card is initialized at full speed.
sd_poweron()setsSCKDIVto the 25 MHz divisorbefore CMD0. The SD specification requires the identification phase (CMD0 to ACMD41) at 100–400 kHz.
Since #1685, both
SCKDIVwrites useSPI_DIV, andSPI_CLKwas then raised to 25 MHz, so from 1.11.0the card is initialized at 25 MHz or more. #1875 reports SD worked with 1.8.1. Initialization now uses
SPI_INIT_CLK, 250 kHz, which keeps a margin from both ends of the range;SPI_DIVis used from thecopy onward. The card's own power-up, not the SPI clock, sets how long identification takes (the
ACMD41 loop polls until the card is ready), so the slower clock costs a few milliseconds.
2. The divisor rounds down, so SCLK rounds up. The controller produces SCLK = F / (2 (div + 1)).
Integer division truncated
div, which runs the card aboveSPI_CLKwhenever the clock is not amultiple of 50 MHz: 30 MHz at 120 MHz, 31.25 MHz at 125 MHz, 37.25 MHz at 149 MHz. The divisor now
rounds up, so SCLK never exceeds the requested frequency.
3. The ROM is built from the system bus clock. The SPI controller sits on the peripheral bus
(
SPIAttachParams.controlWhere = PBUS), so its divisor must come from the peripheral bus frequency.They agree only when all buses run at one frequency; with
WithPeripheryBusFrequencybelow the systembus, the card is clocked too slowly. A design that attaches the SPI controller another way, to another
bus or across a clock crossing, can now set both divisors itself:
Each is checked against the controller's
sckdivwidth (SPIParams.divisorBits) at elaboration.4. The image is cached across configurations. The make rule depended only on the sources, not on
PBUS_CLK, so the first configuration elaborated in a tree fixed the ROM for every later one,whatever its clock, and, through the
vc707/zcu104symlinks, whatever its board. The image builds inabout 0.1 s, so it is now always rebuilt. The
makeinvocation moves into one helper,SDBoot.makeVars, which the three boards share, as they share the sources.Evidence. Divisors written by the built image (
riscv64-unknown-elf-gcc13.2.0), read from itsdisassembly, before and after:
Cause 4, reproduced in one tree:
make bin PBUS_CLK=50thenmake bin PBUS_CLK=125leaves the 50 MHzimage before this change; after it, the second image matches a fresh build at 125 MHz.
Elaborated with
RocketVCU118Config(100 MHz): the ROM writes divisors 199 (250 kHz) and 1 (25 MHz).With
WithSDBootSPIDivisors(150, 5)it writes 150 and 5; with(5000, 5)elaboration fails with"each divisor must fit the SPI controller's 12-bit sckdiv (0 to 4095)". Checked on a 1.14.0 tree, whose
sdboot,vcu118andvc707sources matchmain;zcu104is not in 1.14.0, so its two-line changeis not compiled here.
A more general option, not in this PR. The boot ROM could read the SPI controller's clock from the
device tree appended to it (the
sifive,spi0node'sclocks), which makes the image independent ofthe configuration and right for any attachment. We have a working version; it adds about 1.6 KB to the
image, which for a typical device tree takes the ROM past 8 KiB to its next power of two. Happy to
offer it separately if that tradeoff is wanted.
Testing. Found while preparing an FPGA bring-up for hardware we are still waiting on, so this has
not yet been tested on a board. The evidence above is from the built images, the make flow and
elaboration. We will add a board result when the hardware arrives; in the meantime, testing on a VCU118
that currently times out would be very welcome.
Type of change: bug fix. Impact: software (RISC-V boot ROM) and build system.