Compare commits
2 Commits
4bc9236bd7
...
5540f901de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5540f901de | ||
|
|
1b9987c4ee |
@@ -30,5 +30,5 @@ live in [`spikes.md`](spikes.md) and
|
||||
[`v0.1-mvp-technical.md`](v0.1-mvp-technical.md#hardware-bring-up-order),
|
||||
with failure write-ups in [`postmortems/`](postmortems/README.md). Notable: the
|
||||
keyboard runs bus-powered on the S3's native USB port, and the SD/FAT stack is
|
||||
verified on a 32 GB card (2026-07-11), now moving to its own SPI3 host per
|
||||
ADR-012 ([postmortem](postmortems/2026-07-05-spike3-sd-cmd59.md)).
|
||||
verified on a 32 GB card on its own SPI3 host (2026-07-11, ADR-012)
|
||||
([postmortem](postmortems/2026-07-05-spike3-sd-cmd59.md)).
|
||||
|
||||
@@ -191,8 +191,8 @@ it is explicitly **not recommended** and not applied.
|
||||
- [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).
|
||||
- [x] Settle the **shared-bus arbitration** decision → **SD on its own SPI3**
|
||||
(ADR-012, 2026-07-11); spike rewired to SPI3 (SCK 14 / MOSI 15), pending a
|
||||
bench re-run on the new wiring.
|
||||
(ADR-012, 2026-07-11); spike rewired to SPI3 (SCK 14 / MOSI 15) and
|
||||
**re-verified on hardware 2026-07-11** — same mount + round-trip result.
|
||||
- [ ] **Still open:** implement the FatFS atomic-save (unlink-then-rename +
|
||||
`*.tmp` boot-recovery) in the real `persistence` module.
|
||||
|
||||
|
||||
@@ -71,9 +71,8 @@ binary — [`src/bin/sd_fat.rs`](src/bin/sd_fat.rs), flashed with `just flash-sd
|
||||
brings up the SD card, mounts FAT at `/sd`, and exercises the persistence
|
||||
module's atomic save (write `*.tmp` → fsync → rename → read-back). Per ADR-012
|
||||
the SD runs on its **own SPI3 host** — **SCK 14 · MOSI 15 · MISO 13 · SD CS 10**
|
||||
— leaving the EPD alone on SPI2. (The 2026-07-11 bench proof below ran on the
|
||||
earlier shared-SPI2 wiring; the code is now rewired to SPI3, pending a re-run
|
||||
after moving the SCK/MOSI jumpers to 14/15.)
|
||||
— leaving the EPD alone on SPI2. Verified on the dedicated SPI3 bus 2026-07-11
|
||||
(same mount + round-trip result as the initial shared-SPI2 bring-up).
|
||||
|
||||
Bench result (genuine 32 GB SDHC card): mounts at 10 MHz, `29806 MiB total`,
|
||||
atomic round-trip byte-identical. Two findings baked into the code:
|
||||
@@ -91,9 +90,8 @@ atomic round-trip byte-identical. Two findings baked into the code:
|
||||
for its whole lifetime, and persistence runs on its own thread, so a shared bus
|
||||
would need an EPD rewrite plus a cross-thread mutex on the save path. Instead the
|
||||
SD gets its own SPI3 — the EPD stays untouched, no arbitration. Remaining before
|
||||
persistence lands in `main.rs`: re-run the spike on the SPI3 wiring, then wire
|
||||
the atomic save (unlink-then-rename + `*.tmp` boot-recovery) into a `persistence`
|
||||
module.
|
||||
persistence lands in `main.rs`: wire the atomic save (unlink-then-rename +
|
||||
`*.tmp` boot-recovery) into a `persistence` module.
|
||||
|
||||
**Spike 5 — partial refresh + typing: verified 2026-07-04.** `main.rs` wires
|
||||
the keyboard to the panel: [`src/usb_kbd.rs`](src/usb_kbd.rs) feeds decoded
|
||||
|
||||
@@ -77,11 +77,11 @@ fn main() -> Result<()> {
|
||||
esp_idf_svc::sys::link_patches();
|
||||
esp_idf_svc::log::EspLogger::initialize_default();
|
||||
|
||||
log::info!("Typoena — Spike 3 (SD/FAT on shared SPI2), {BUILD_TAG}");
|
||||
log::info!("Typoena — Spike 3 (SD/FAT on dedicated SPI3), {BUILD_TAG}");
|
||||
|
||||
match run() {
|
||||
Ok(()) => {
|
||||
log::info!("✅ Spike 3 complete — mount + atomic write/fsync/rename/read-back on shared bus")
|
||||
log::info!("✅ Spike 3 complete — mount + atomic write/fsync/rename/read-back on dedicated SPI3")
|
||||
}
|
||||
Err(e) => log::error!("❌ Spike 3 failed: {e:?}"),
|
||||
}
|
||||
@@ -93,7 +93,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let card = mount_sd().context("mounting SD over SPI2")?;
|
||||
let card = mount_sd().context("mounting SD over SPI3")?;
|
||||
|
||||
// SAFETY: `card` is a live handle returned by a successful mount.
|
||||
let (max_khz, real_khz) = unsafe { ((*card).max_freq_khz, (*card).real_freq_khz) };
|
||||
@@ -111,7 +111,7 @@ fn run() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Init the shared SPI2 bus and mount the card. Returns the card handle (kept
|
||||
/// Init the dedicated SPI3 bus and mount the card. Returns the card handle (kept
|
||||
/// alive for the program's lifetime; the spike never unmounts).
|
||||
fn mount_sd() -> Result<*mut sys::sdmmc_card_t> {
|
||||
// 1) Initialize SPI3 with the SD's four lines. Dedicated bus (ADR-012) — no
|
||||
@@ -224,7 +224,7 @@ fn file_roundtrip() -> Result<()> {
|
||||
let path = format!("{MOUNT_STR}/spike3.md");
|
||||
let tmp = format!("{path}.tmp"); // two dots → exercises long-filename support
|
||||
let payload =
|
||||
format!("typoena spike 3\n{BUILD_TAG}\nshared SPI2: SCK12 MOSI11 MISO13, SD CS10\n");
|
||||
format!("typoena spike 3\n{BUILD_TAG}\ndedicated SPI3: SCK14 MOSI15 MISO13, SD CS10\n");
|
||||
|
||||
{
|
||||
let mut f = fs::File::create(&tmp).context("create tmp")?;
|
||||
|
||||
Reference in New Issue
Block a user