Skip to content

x4prosim: SD card and SPI bus timing calibrated to the X3 - #4

Merged
prokrypt merged 3 commits into
prokrypt:x4prosimfrom
serialx:serialx/sd-timing-model
Oct 9, 2026
Merged

prokrypt merged 3 commits into
prokrypt:x4prosimfrom
serialx:serialx/sd-timing-model

Conversation

@serialx

@serialx serialx commented Oct 9, 2026

Copy link
Copy Markdown

The X3 machine's SD card was free: every GPSPI transaction and every card command completed in zero guest time, so firmware stages that wait on the card (section prefetch, SD-font glyph reads, framebuffer write-back) ran 5 to 50 times faster than on the device. This models the bus and the card and calibrates both against a real X3, so SD-bound work takes the same guest time on the emulator as on the device.

Model

  • hw/ssi/esp32s3_gpspi.c: SCLK is decoded from SPI_CLOCK per transaction; the bytes move in a virtual-clock timer callback after bits/SCLK plus a per-transaction setup cost, and CMD.USR stays set until then. The panel and the card share the bus, so both get their wire time.
  • hw/sd/ssi-sd.c: card latencies as QOM properties (microseconds, default 0 so other boards are unchanged): first-token time after CMD17/18 by start pattern (repeat / sequential / random), inter-block time inside CMD18, busy after a CMD24 block, per-block busy inside CMD25 and the busy after STOP_TRAN. Polling returns 0xFF before a token and 0x00 while busy, so SdFat's own loops spend the time. Trace events show every decision.
  • hw/riscv/esp32c3.c: the X3 board sets the calibrated defaults; -global ssi-sd.<prop>=<us> overrides them.

Calibration

A throwaway probe build of CrossPoint (-DSD_PROBE=1) timed read/write x sequential/random/repeated x 1/2/4/8/32 sectors through SdFat's card layer, 2,880 operations per boot, three boots on the device and then on the emulator. All 30 rows match within 3 % for reads and 13 % for writes; the device table, the comparison script and the notes are in x4prosim/sdcal/.

op pattern device n=1 emulator n=1 device n=32 emulator n=32
read sequential 331 us 323 us 5307 us 5244 us
read random 398 us 392 us 5330 us 5313 us
write sequential 747 us 738 us 5280 us 5175 us
write random 813 us 803 us 5279 us 5175 us

Effect on a reading workload

Same firmware, same card contents and settings (Pretendard 16 pt from the card, anti-aliasing on), same pages, CrossPoint's own Page render (tiled) stages, median of 6 page turns in ms:

book side prewarm gray_lsb gray_msb cleanup display total
Aesop device 32 88 86 102 588 1150
Aesop emulator before 4 14 14 2 502 683
Aesop emulator after 26 60 60 95 595 988
Demian device 114 108 108 102 587 1282
Demian emulator before 10 19 19 2 502 704
Demian emulator after 105 65 65 95 596 1080

What remains is not the card: gray_display is 140 ms against 228 on the device (the panel model's grayscale refresh), and CPU-bound stages run about 2.5x faster under -icount shift=2 (bw_render 13 vs 35 ms). Those are separate calibrations; both would shift the GPSPI setup terms slightly, which is why they are properties.

Limits

  • One card, one device. The model reproduces medians, not the card's tails: random single reads reach 1.2 ms at p99, and one probe boot saw 68 ms garbage-collection stalls on fresh sequential writes.
  • Host wall time per page turn rises from about 1 s to 4 to 5 s because the polling loops now run instruction by instruction.
  • The X4 Pro keeps zero card latency; its card was not measured.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz

serialx and others added 3 commits October 9, 2026 15:13
Decode SPI_CLOCK for every CPU transaction and hold CMD.USR until a virtual
clock timer expires. Transfer the latched bytes to the SSI slaves at expiry,
then clear USR and raise TRANS_DONE so polling firmware observes bus time.

Expose a transaction-overhead-us property with a zero default and trace the
selected clock, byte count and duration. Ignore duplicate starts while busy
and cancel pending transfers on reset.

Both system targets build; X4 Pro qtest checks clock dividers, setup overhead,
pending USR and UPDATE writes, and the completion interrupt boundary.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz
Hold read tokens and write readiness until virtual-clock deadlines instead of
making each block immediately available. Track completed read and write sectors
separately to select sequential first-block access and random first-write busy
time, including CMD18 inter-block delays and CMD25 stop-token busy time.

Expose six uint32 microsecond properties with zero generic defaults, and apply
the provisional X3 timings without overriding explicit -global settings.
Preserve the existing R1 idle, immediate write-token dispatch and CMD13 SPI R2
behavior; retain timed adapter state in an optional migration subsection.

Trace latency decisions and document the properties, calibration limits and
command-line overrides. Both targets build without warnings in changed files;
isolated adapter tests cover exact SDSC/SDHC deadlines and zero defaults, and
QMP checks the X3 properties and explicit overrides.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz
The X3 defaults for the GPSPI setup costs and the ssi-sd card latencies now
come from a 3-boot probe on a real X3 (40 MHz SCLK, SdFat SHARED mode,
read/write x sequential/random/repeated x 1..32 sectors). All 30 median
totals match the device: reads within 3 %, writes within 13 %. The probe's
device table, the comparison script and the notes live in x4prosim/sdcal/;
the raw emulator log stays with the session evidence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@prokrypt
prokrypt merged commit 3495538 into prokrypt:x4prosim Oct 9, 2026
12 checks passed
@serialx
serialx deleted the serialx/sd-timing-model branch October 9, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants