docs(sd): record Spike 3 hardware verification and findings

Mark Spike 3 resolved (genuine 32 GB SDHC mounts, round-trips clean on the
shared SPI2 bus; the 133 GB SDXC's CMD59 rejection was the sole fault).
Capture the FatFS f_rename caveat and the ≤32 GB card-compatibility note in
ADR-007, the resolution in the postmortem, and the verified writeup in the
firmware README.
This commit is contained in:
Julien Calixte
2026-07-11 11:14:31 +02:00
parent b899286639
commit 5335751323
3 changed files with 91 additions and 14 deletions

View File

@@ -1,8 +1,10 @@
# Spike 3 (SD) — blocked on a card that rejects CMD59 (SPI-mode CRC)
> Date: 2026-07-05 · Build at time of failure: `07-05 16:07Z @f77f669-dirty`
> Status: **paused, not failed** — bench card is incompatible; awaiting a
> compliant microSD. Wiring and firmware are proven good.
> Status: **RESOLVED 2026-07-11** — a genuine 32 GB SDHC card mounts, negotiates
> 10 MHz, and round-trips cleanly on the shared SPI2 bus. Wiring and firmware
> were always good; the 133 GB SDXC card's CMD59 rejection was the sole fault.
> See [Resolution](#resolution-2026-07-11) below.
>
> Context: Spike 3 in
> [`../v0.1-mvp-technical.md`](../v0.1-mvp-technical.md#hardware-bring-up-order),
@@ -10,6 +12,39 @@
> firmware notes [`../../firmware/README.md`](../../firmware/README.md).
> Spike program: [`../../firmware/src/bin/sd_fat.rs`](../../firmware/src/bin/sd_fat.rs).
## Resolution (2026-07-11)
Re-ran `sd_fat` with a genuine 32 GB SDHC card (build `07-11 08:53Z
@195311a-dirty`). Everything the paused write predicted held:
- `sdspi_host: data CRC set=1`**CMD59 now succeeds**. Init runs clean through
card identification (`sdmmc_card_init: card type is SD`).
- `card mounted at /sd — max 10000 kHz, negotiated 10000 kHz`.
- `FAT usage — 29806 MiB total, 29802 MiB free` (~29.8 GiB of the 32 GB card).
- `round-trip OK — 92 bytes: create …tmp → fsync → rename → read back identical`.
Wiring, pull-ups, bus sharing, and the hand-rolled FFI mount path all confirmed
correct — the only variable that changed was the card, exactly as diagnosed. The
`cmd=52` / `cmd=5` "command not supported" lines still print and are still
normal (SDIO probes a memory card ignores).
### New finding: FatFS `f_rename` does not overwrite (atomic-save is not POSIX)
The first run created `/sd/spike3.md`; the **second** run failed at `rename tmp
-> final` with `File exists (os error 17)`. Root cause is a FatFS semantics
difference, not a fault: **`f_rename` returns `FR_EXIST` when the destination
already exists** — unlike POSIX `rename(2)`, which atomically replaces it. So the
textbook write-`*.tmp` → rename-over-target idiom from ADR-007 does **not** work
as-is on FAT; the destination must be unlinked first.
That unlink opens a crash window: between `f_unlink(target)` and `f_rename`, the
target is momentarily gone while `*.tmp` holds the complete, fsync'd new content.
The consequence for the real persistence module is a **boot-recovery** rule: a
lingering `*.tmp` at startup means the last save didn't finish → promote it
(it's the newest complete copy). The spike now does unlink-then-rename and
tolerates a missing target, so re-runs are idempotent; recorded in
[ADR-007](../adr.md#adr-007-storage-split--fat-on-sd-for-working-copy-littlefs-on-flash-for-config).
## Summary
Built the Spike 3 bench program (`sd_fat`): bring up the SD card over the EPD's
@@ -143,16 +178,22 @@ it is explicitly **not recommended** and not applied.
## Follow-ups
- [ ] Re-run with a genuine ≤32 GB card → expect clean mount + round-trip.
- [ ] Strip the debug logging once green: remove `CONFIG_LOG_MAXIMUM_LEVEL_DEBUG`
from `sdkconfig.defaults` and the `esp_log_level_set` block in `sd_fat.rs`.
- [ ] Write up Spike 3 as verified in
[`../../firmware/README.md`](../../firmware/README.md) (wiring, LFN
requirement, card-compatibility note), matching the other spikes.
- [ ] Record a **recommended-SD-card note** for v0.1 (genuine, ≤32 GB;
large/counterfeit SDXC may fail CMD59) — product doc / ADR-007.
- [ ] Settle the **shared-bus arbitration** decision (EPD lock vs. SPI3 for SD).
- [ ] Enable PSRAM, then build Spike 7 (gitoxide push) for the push leg.
- [x] Re-run with a genuine ≤32 GB card → clean mount + round-trip (2026-07-11).
- [x] Strip the debug logging once green: removed `CONFIG_LOG_MAXIMUM_LEVEL_DEBUG`
from `sdkconfig.defaults` and the `esp_log_level_set` block in `sd_fat.rs`
(2026-07-11).
- [x] Write up Spike 3 as verified in
[`../../firmware/README.md`](../../firmware/README.md) (2026-07-11).
- [x] Record a **recommended-SD-card note** for v0.1 (genuine, ≤32 GB;
large/counterfeit SDXC may fail CMD59) — in
[ADR-007](../adr.md#adr-007-storage-split--fat-on-sd-for-working-copy-littlefs-on-flash-for-config)
(2026-07-11).
- [x] Enable PSRAM (done, `CONFIG_SPIRAM`) and build Spike 7 (git push) — both
complete; see [Spike 7 postmortem](2026-07-05-spike7-gix-https-push.md).
- [ ] **Still open:** settle the **shared-bus arbitration** decision (EPD lock
vs. SPI3 for SD) before wiring persistence into `main.rs`.
- [ ] **Still open:** implement the FatFS atomic-save (unlink-then-rename +
`*.tmp` boot-recovery) in the real `persistence` module.
## Artifacts (this session)