Skip to content

eMMC storage and drive mode (#49) - #51

Closed
Worldwave wants to merge 8 commits into
mainfrom
issue43-presets
Closed

Worldwave wants to merge 8 commits into
mainfrom
issue43-presets

Conversation

@Worldwave

Copy link
Copy Markdown
Owner

The storage half of #43: #49 (eMMC storage and drive mode, done and closed). PRST itself continues in #50, which this PR does not close.

What's in it

  • eMMC driver (sp1_emmc.c, ported from chattock/sp1-tape-looper and Tim Knapen's SP-1-dev, MIT): bit-banged commands, SPIM3 DMA payloads at 16 MHz, CRC16 on every read, an "accepted" token on every write.
    • Closed-ended burst reads (CMD23 + CMD18) fix the card returning internal-buffer junk with a valid CRC.
    • Nothing that erases, partitions or writes the card's own settings is ever sent.
  • FAT32 across the card (ChaN's FatFs via Zephyr's fatfs module): MBR, one partition at 4 MB, long file names, volume label "SP-1".
  • wakes-sp1-fresh formats the eMMC once per build. The UI is held with a progress bar, and only the 30 s backstop can power off meanwhile. A normal image contains no formatter, and check_image.py enforces that.
  • Formatted check: with no filesystem, Wakes plays normally and the storage features stay off.
  • Drive mode: in STANDBY with a computer attached, the card is a USB drive.
    • The interface is always present, so there's no re-enumeration and MIDI/audio are never disturbed; bcdDevice is 0x0500.
    • The activity chase runs T1, T3, T2, T4.
    • The T2/T3 status lights are removed from STANDBY.
    • Holding •• waits for the computer: every transfer restarts the power-on fill, with no time limit.
  • Release images: storage and drive mode are on in every Plaits build.
    • CI builds wakes-sp1.bin (never formats), wakes-sp1-fresh.bin and the fallback recovery image (no storage, never touches the eMMC).
    • CI checks each image's configuration against its name.
    • Releases attach the fresh image and explain what it erases.
  • Manual updated (Adara).

Testing

Every round is listed in #49 with its log.

  • Probe and format: the probe, the fresh format and the normal image's first mount all passed.
  • Fast transfer: ~295 KB/s write / ~318 KB/s read, 0 errors.
  • Drive mode:
    • Copies both ways passed; so did eject, ON without ejecting, unplugging, and the OP-XY.
    • CPU in ON: unmeasurable.
  • The read fix: G1 gave 4 ONs identical, 0 bad blocks. G2 compared a 337-file / 114 MB folder against the original by SHA-256: 0 differences.
  • Smoke test: this image passed (Adara).
  • Carried to FEATURE: PRST presets #50: A5, B4, C8 (Windows in ON), C9 (macOS), H1–H6 (the power-on wait).

Sizes: wakes-sp1.bin 449 308 B, wakes-sp1-fresh.bin 454 372 B, fallback 86 624 B. All 0 warnings, check_image.py passes, the image links at 0x20000. firmware/VERSION is unchanged (0.7.2).

🤖 Generated with Claude Code

Worldwave and others added 8 commits October 7, 2026 17:54
Storage for PRST (presets), off by default: the release image is
unchanged at 427 772 B.

- CONFIG_SP1_STORAGE: the eMMC driver (sp1_emmc.c, 1-bit, bit-banged;
  protocol from sp1-tape-looper and SP-1-dev), FatFs through Zephyr's
  fs layer, and a storage thread at K_PRIO_PREEMPT(5) -- below audio,
  main and USB, so it only uses idle CPU. It never feeds the watchdog.
  Commands sent: 0 1 2 3 7 8 13 16 17 24 only; nothing writes EXT_CSD,
  erases or partitions. Reads are CRC16-checked; writes need the card's
  "accepted" token and are retried.
- CONFIG_SP1_FRESH (wakes-sp1-fresh): formats the card on its first
  ON -- one MBR partition, FAT32 at block 8192 (the 4 MB erase group),
  32 KB clusters, 2 FATs, block 0 cleared first and the MBR written
  last -- and stamps WAKES/FRESH.ID with its build ID; it formats again
  only when that stamp is missing or names another build (Adara: option
  A). The format holds ON entry with a progress bar on T1-T4, then two
  0-100 % flickers and a quick fade into the page. Only the 30 s "••"
  backstop powers off meanwhile; it runs every tick, first and
  unconditionally, and stops the format via sp1_quiesce_peripherals.
- Only the fresh image can format, by construction: a normal image has
  no f_mkfs, every mount is NO_FORMAT, our Kconfig defaults
  FS_FATFS_MOUNT_MKFS to n (Zephyr's y would format any unmountable
  card), and check_image.py fails an image that breaks either rule.
- CONFIG_SP1_STORAGE_TEST: the card report ("EMMC" lines) and a file
  test ("STORE" lines) on every ON.
- west.yml: the fatfs module (Adara). FatFs R0.16 in NOTICE and
  LICENSES/FatFs.txt.

On hardware (logs/sp1-20261007-173828/-174058/-174227): format 11.0 s,
FAT32 as above; second ON did not format; a normal image mounted the
card with no format and the ON counter went 1, 2, 3; 64 KB written,
read back identical; 0 retries, CRC errors, rejects or timeouts; write
busy <= 7.1 ms; storage stack <= 1168 of 4096 B; SYSTEM_OFF reached.
The format's write estimate now counts the WAKES folder's cluster
(2035 writes measured against 1966 estimated).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tape-looper's method, with a slow path behind every fast step:
- 512-byte payloads and their CRC16 ride SPIM3 + EasyDMA at 16 MHz
  (inside the card's 26 MHz default-speed limit); the start-bit hunt,
  the CRC-status token, the busy and every command stay bit-banged.
  SPIM3 is otherwise unused in Wakes.
- Commands at register speed once the card is identified; the first
  four tries are fast, the rest at the 1 us identification clock.
- CMD18 / CMD25 bursts with CMD12 (R1b waited after writes). A burst
  that fails part-way finishes block by block; a block is retried
  bit-banged, then at 1 us. The device's last block never ends a CMD18.
- Table-driven CRC16, checked against "123456789" = 0x31C3 at init.
- Anomaly 195 workaround on power-down; anomaly 198 (SPIM3 TX) is caught
  by the card's own CRC check and retried.
- spim_xfer re-reads EVENTS_END after its 5 ms bound: the storage
  thread is the lowest priority there is, and a preemption between the
  two reads made finished transfers look timed out (38 in 512 KB on
  hardware, each dropping a burst to the slow path).
- Timeouts are counted by cause (data / busy / spim); the report
  counts burst blocks and fallbacks.
- The storage test moves 512 KB in 16 KB chunks (32-block bursts).

On hardware (logs/sp1-20261007-194750.log), audio running at ~62 %:
512 KB written at 294-297 KB/s and read at 315-321 KB/s, identical;
2048 blocks in bursts, 0 fallbacks; retries, CRC errors, rejects and
timeouts all 0; write busy <= 6.3 ms. Bit-banged was 25/28 KB/s.
Audio fail 0 / rst 0. Release image unchanged (427 772 B).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drive mode (CONFIG_SP1_DRIVE, off by default):
- In STANDBY with a host attached (not a charger) the eMMC is a USB
  drive, automatically; turning ON takes it back after the transfer
  in flight. The mass-storage interface is always in the descriptor and
  reads "no medium" while ON, so STANDBY <-> ON never re-enumerates.
  It is registered last, so CDC/MIDI/audio interface numbers do not
  move; bcdDevice 0x0500 with the drive (SP1_USB_BCD_DEVICE), 0x0403
  without, as every release so far.
- Zephyr's usbd_msc thread starts cooperative, above audio: moved to
  PREEMPT(main + 1).
- Two disks over one card ("EMMC" for Wakes, "DRIVE" for the host) and
  an owner under a lock every transfer holds. A card with no
  filesystem is not offered (a host would offer to format it).
- STANDBY: the T2/T3 plugged/charging lights are gone in every build
  (the play-row bar still breathes while charging); host reads and
  writes chase T1, T3, T2, T4 from 50 %.
- Measured: with a host attached and ON, usbd_msc 0.0 % CPU.

Burst READS are closed-ended (CMD23 + CMD18):
- Open-ended CMD18 stopped by CMD12 made this card return, as the
  SECOND block of a later burst, its internal buffer with a valid CRC.
  Reproduced on the device: 4 ON entries x 3 rounds of FAT scan + 512 KB
  read, open-ended 5 bad blocks (all at position 1), closed-ended 0.
  Writes were never affected (~10 000 verified, 0 wrong) and stay
  CMD25 ... CMD12.

Formatted check (Adara): every ON looks for a FAT volume. Without one
Wakes boots with PRST off ("STORE PRST off"); sp1_store_volume() is the
check every PRST feature and key combo must pass, and UNKNOWN counts as
no. Only wakes-sp1-fresh formats.

Long file names on (presets will be "<name>.prst", ini inside); the
fresh format labels the volume "SP-1", which is what a host shows.

Diagnostics, test builds only: CONFIG_SP1_EMMC_VERIFY (read every write
back), host-activity counters per STANDBY visit, the mismatch report,
and the open- vs closed-ended reproduction in the storage test.

Release image 427 844 B (+72: bcdDevice set explicitly, STANDBY).
Test image m6-8 (drive + diagnostics) 459 312 B; 0 warnings,
check_image PASS. The fix image is not yet confirmed on hardware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Drive mode: in plugged-in STANDBY, every block the computer reads or
  writes restarts the power-on fill, so ON needs 1 s without a transfer
  with "••" held. No time limit (Adara). Power-off and the 30 s backstop
  are untouched.
- Closed-ended burst reads are the only read path: the open-ended switch,
  the stress comparison and the host-activity counters are removed.
  SP1_STORAGE_TEST and SP1_EMMC_VERIFY stay, off by default.
- SP1_STORAGE and SP1_DRIVE default on in Plaits builds: the plain build
  is the release wakes-sp1.bin, which never formats.
- CI builds wakes-sp1, wakes-sp1-fresh (renamed, never a plain-named
  copy) and the fallback recovery image (no storage), and checks each
  image's .config against its name. Releases attach the fresh image and
  explain what it erases.
- Manual updated (Adara).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Worldwave

Copy link
Copy Markdown
Owner Author

Closed without merging (Adara): storage ships together with PRST (#50) in one combined PR. PRST is built on branch issue50-prst, which starts from this branch (issue43-presets @ 09900db).

@Worldwave Worldwave closed this Oct 8, 2026
@Worldwave
Worldwave deleted the issue43-presets branch October 9, 2026 03:24
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.

1 participant