agent: CMD_NAND, SPI NAND study ops on the FMC100 page engine - #146
Conversation
Groundwork for OpenIPC/firmware#2285 (hi3516ev300 + W25N01GV) and #2519 (GK7205V5x0 + GD5F1GM7): the rootfs rots with uncorrectable ECC that Linux never reports, and the question is which writes leave pages with unusable parity. Answering it needs the controller driven below the OS, the way the Linux hifmc100/xmedia_fmc100 drivers drive it, one knob at a time. Agent (v5, CAP_NAND bit 8, FMC100 builds only): - CMD_NAND 0x0E with sub-ops INFO, FEATURE_GET/SET, READ_PAGES, PROGRAM_PAGE, ERASE_BLOCK and FMC_REG (any FMC register, read/write). - Page I/O in two modes: REG (PAGE_READ/READ_FROM_CACHE or PROGRAM_LOAD/EXECUTE through register ops, page + full OOB, no controller ECC — raw array with on-die ECC off) and DMA (the controller's own OP_CTRL page engine, ported from hifmc100_send_cmd_read/write, with an explicit FMC_CFG so ECC type is the caller's). Each read records ECC_ERR_NUM0_BUF0, the chip status and FMC_INT. - Pages DMA into a 1 MiB RAM buffer mapped uncached in startup.S (no cache maintenance needed); per-page records go to a second buffer. The host moves both with the existing CMD_READ/CMD_WRITE stream, so CMD_NAND only carries parameters. READ_PAGES without storing data is a full-speed ECC scan (1024 pages in under 2 s). - The NAND chip table now carries OOB size (64 W25N/MX35LF, 128 GD5F). - Fixes found on the way: flash_crc32() sent NOR read opcodes to a NAND, so CMD_CRC32 and `agent read` verify were wrong on every SPI NAND; the flash-write readback verified through the FLASH_MEM window, which NAND does not have and which wraps at 1 MiB on NOR; CMD_CRC32 over FMC/CRG registers used byte loads where CMD_READ uses word loads, so verifying a register dump always "mismatched". Host: defib.agent.nand (NandStudy, records, FMC_CFG composition, the OOB a kernel write sends) and `defib agent nand info|feature|fmc-reg| read-page|ecc-scan|dump|write-page|erase-block` (the last two are destructive, like every writing command). `dump` is raw by default and turns the chip's on-die ECC off for the duration. Verified on hardware: - hi3516ev300 + W25N01GV and GK7205V510 + GD5F1GM7: INFO reports 2048+64 and 2048+128; REG reads and DMA reads with ECC off return identical page + OOB bytes (same MD5), i.e. both are raw. - Both chips power up with on-die ECC enabled (B0 = 0x18 / 0x10). - First result on the V510 running its vendor firmware: an ECC scan of the vendor UBI root with its 24-bit ECC finds four uncorrectable pages, all 0x0000FF00 (step 1 only — the #2519 signature), on pages 3-4 of PEBs 51 and 52; with on-die ECC on, 45 pages need correction, with it off 25 — the chip's own ECC flips bits when fed foreign parity. Host tests use a simulated agent that answers CMD_NAND frames as handle_nand() does. No C-level simulator of the FMC page engine: it would only restate the port, and the hardware runs above cover it.
PR Summary by QodoAdd FMC100 SPI NAND page-engine study operations
AI Description
Diagram
High-Level Assessment
Files changed (14)
|
Code Review by Qodo
1.
|
…y failed Review (Qodo) of CMD_NAND: - A dump that hit a controller timeout wrote the failed page's stale buffer bytes, a sidecar, and exited 0. It now keeps only pages that read, marks the sidecar complete=false with the failed page, and exits 1. - The raw dump assumed the on-die ECC switched off; it now checks the feature read-back and refuses rather than label corrected data raw. - REG-mode reads could not tell a stalled FMC register op from success (fmc_wait_ready() just stopped polling). It now reports the timeout, and the page record carries NAND_REC_TIMEOUT. - Records sampled FMC_INT after the following status read had already cleared it and run its own op; FMC_INT is now captured right after the page op. - A DMA op's FMC_CFG outlived the request and changed how later CMD_READ, CRC32 and raw reads ran; handle_nand restores it after every page op (FMC_REG still writes on purpose). - CMD_CRC32 over registers now uses exactly CMD_READ's I/O windows and per-byte word extraction, so unaligned ranges and the V1 FMC/CRG/UART windows verify too.
A 140 MB raw dump is ~265 one-megabyte reads over the UART. One sequence gap made read_memory raise while the agent kept streaming the rest of that megabyte; the dump's cleanup then sent FEATURE_SET, read a stale RSP_DATA as its answer, and that error replaced the real one — on hi3516ev300 after 23k of 65k pages. CMD_NAND calls now skip the tail of an abandoned read (RSP_DATA frames and the ACK that closes them; an ACK with no data before it is still a real answer), dump batches are retried up to three times after waiting for the line to go quiet, and restoring the on-die ECC setting can no longer mask the dump's own error.
With the SPI NAND interface selected in FMC_CFG, a non-zero ECC type also applies to register-path READ_FROM_CACHE data: the controller "corrects" it, and with an ECC type the page was not written with it flips bits that were right. Measured on a GK7205V510 + GD5F1GM7: register reads under FMC_CFG 0x1863 (24-bit) and 0x1823 (8-bit) differ from reads under 0x1803 (ECC off) by 1-6 bits on ~29% of pages, deterministically, while DMA reads with ECC off match the ECC-off register reads exactly. A raw dump taken after a DMA scan had left 0x1863 behind was therefore not raw. REG-mode page reads and programs now clear the ECC type for the op; handle_nand restores the caller's FMC_CFG afterwards as before.
Fault-injected on a hi3516ev300 (agent at 921600, host forced to 115200) after real dumps on two boards died the same way: - _recv_packet_sync() relied on the port's read timeout to recheck its deadline, but SerialTransport opens with timeout=None, so read(1) blocked until a byte arrived: with the agent silent (wrong baud, crashed), every agent command on a plain serial port waited forever, whatever timeout it was given. It now polls a blocking port. The existing timeout test missed it because its fake port never blocked; a new one does. - recv_response() restarts its timeout on every READY, so a request lost to a baud mismatch kept CMD_NAND waiting forever once the agent idled back to 115200 and started announcing itself. _call() now uses recv_packet() against its own deadline and skips READY itself, as well as any other leftover frame (a late answer to an earlier request, the tail of an abandoned read). - NandStudy.resync() treats READY as quiet, re-probes the agent at the current rate, falls back to 115200 + READY when it does not answer, and leaves the client's idea of the baud matching the port. On hardware the forced desync now recovers in 1.1 s instead of hanging.
On a loaded host the FT232R link at 921600 dropped frames three times in a row on the same batch, and identical retries failed identically. A retry now moves the data at 115200 (restoring it first if the link is still fast); slow, but it gets the batch.
Phase 1 of the NAND ECC study for OpenIPC/firmware#2285 (hi3516ev300 + W25N01GV) and #2519 (GK7205V5x0 + GD5F1GM7). It adds tooling to drive the FMC100 SPI NAND path below the OS, both the way the Linux drivers do and deliberately differently.
Agent (protocol v5,
CAP_NAND)CMD_NAND 0x0Esub-ops:INFO,FEATURE_GET/SET,READ_PAGES,PROGRAM_PAGE,ERASE_BLOCK,FMC_REG.OP_CTRLpage engine, ported fromhifmc100_send_cmd_read/write, with an explicitFMC_CFGso the caller picks the ECC type.ECC_ERR_NUM0_BUF0, the chip status andFMC_INT.CMD_READ/CMD_WRITE. RunningREAD_PAGESwithout storing data is a fast ECC scan (1024 pages in under 2 s).flash_crc32()sent NOR opcodes to NAND, which brokeCMD_CRC32and read-verify on every SPI NAND.Host:
defib.agent.nandplusdefib agent nand info|feature|fmc-reg|read-page|ecc-scan|dump|write-page|erase-block.write-pageanderase-blockare destructive and listed in CLAUDE.md's safety table.dumpis raw by default, with on-die ECC off for its duration.Verified on hardware
B0=0x18/0x10).0x0000FF00, i.e. only step 1 is uncorrectable. They sit at pages 3–4 of PEBs 51 and 52, which is consistent with UBIFS master LEBs (still to be confirmed).Checks: pytest 975 passed, ruff, mypy,
make -C agent test, clean builds for ev300 / gk7205v500 / hi3520dv200 (HISFC350, feature compiled out) / hi3516cv300 (ARMv5) / cv610.There's no C-level simulator of the page engine; it would only restate the port. Host tests use a simulated agent that answers
CMD_NANDframes.