Add hi3519v101 + hi3516av200 (V3A family) to flash agent#69
Merged
Conversation
Fourth agent platform after ev300 (V4), cv300 (V3), and av300/dv300/cv500 (V5/cv500-family). V3A is 3519v101 + av200 — Cortex-A7 with V3-era peripheral addresses (UART 0x12100000, WDT 0x12080000) but DDR at 0x80000000 like cv500-family. Memory map per qemu-hisilicon's hi3519v101_soc. The bootrom-protocol quirks (sendFrameForStart handshake, PRESTEP1 DDR training step, non-fatal TAILs) were already landed for the install/burn flow in #47 + #48 + #65, so this is just the agent build wiring plus one real protocol fix the agent path was missing. ## Fix: don't pre-truncate spl_override at the call site `defib agent upload` and `agent flash` were doing \`spl_data = uboot[:profile.spl_max_size]\` before passing to `send_firmware()`. When `_send_spl()` then scans this truncated buffer for the LZMA/gzip SPL boundary, it can't find anything past profile_max — so for chips where the OpenIPC SPL is *larger* than the HiTool reference (e.g. av200's SVB-enabled SPL is 0x6800, profile_max is 0x4F00), we send 0x1900 too few bytes. The SPL never reaches its post-DDR-init code, hangs after the SPL TAIL, and the agent HEAD frame for 0x81000000 gets `0x08` rejection. Fix: pass the full u-boot binary as `spl_override`. `_send_spl()` already handles the slicing via its detected boundary. Verified on real hardware: - hi3516av200 (NAND board, on /dev/ttyUSB1, ether8): SPL detected at 0x6800, agent uploads, runs, READY received. Flash JEDEC reads byte-shifted (this board has SPI NAND; the agent's NOR-only flash driver is a separate, larger limitation). - hi3516cv300 regression (on /dev/uart-IVGHP203Y-AF, ether3): SPL now detected at 0x5400 (was being clamped to 0x4F00 pre-fix). Agent loads identically, jedec=ef4018, 256 KiB read at 921600 baud = 84.9 KB/s — same as before. ## Aliasing Match the existing `gk7205v300 → gk7205v200` shape: one agent binary serves the V3A family, multiple chip names route to it. Add `hi3519v101 → hi3519v101` (own binary) and `hi3516av200 → hi3519v101` to `chip_to_agent`. ## Verification QEMU `qemu-system-arm -M hi3519v101 -kernel agent-hi3519v101.elf`: agent boots cleanly, READY/DEFIB packet stream, no faults. Real hi3516av200 board: \`\`\` upload ok=True agent ready: ram=0x80000000 caps=0x7f version=2 \`\`\` cv300 regression (testing that the spl_override fix doesn't break what landed in #66/#67): jedec_id, ram_base, caps, throughput unchanged. make test HOST_CC=gcc: 5406/5406. pytest: 402 passed, 2 skipped. ruff & mypy clean. ## Known limitation The av200 board in this lab has SPI NAND. The agent's flash driver (`agent/spi_flash.c`) supports SPI NOR only — uses memory-mapped reads at 0x14000000 and direct FMC register commands. On NAND, JEDEC reads return shifted bytes and `read_memory` returns 0 bytes. The agent still loads, runs, and emits READY on NAND boards; just the read/erase/write/scan operations don't work. SPI NAND support is a separate larger piece of work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
2 tasks
widgetii
added a commit
that referenced
this pull request
May 5, 2026
## Summary The agent's flash driver was NOR-only. This PR adds full **read + erase + program** for SPI NAND, so `defib agent read/erase/write` works on NAND boards (hi3516av200 we just landed in #69 ships with a Macronix MX35LF1GE4AB, 1Gbit / 128 MiB). ## Two commits ### 1. Add SPI NAND read support to flash agent (`777538a`) - `nand_identify` recognizes Macronix `0xc2 0x12` (MX35LF1GE4AB), tolerating the leading dummy byte some SPI NAND chips emit during 0x9F READ_ID. - `flash_init` dispatches by JEDEC: NAND skips `fmc_enter_boot` (no memory-mapped boot mode on NAND) and reports 128 MiB total / 128 KiB block / 2 KiB page. - `nand_read` issues PAGE_READ (0x13) → wait OIP → READ_FROM_CACHE (0x03) chunked through the FMC's 256-byte I/O buffer. - `flash_read` early-dispatches to NAND on chip type; NOR path unchanged. - `flash_info.flash_type` reported in CMD_INFO so the host can branch on chip type. ### 2. Add SPI NAND erase + program — byte-perfect (`563cb14`) - **Read-side fix**: the FMC captures the chip's post-address dummy byte at `iobuf[0]` (always 0x00 because the chip drives the dummy line low) rather than consuming it transparently. Reading `iobuf[0..N-1]` gave the dummy byte at position 0 of every 2 KiB page — the off-by-one bug from the read commit. Fix: request `chunk + 1` bytes, set `OP_CFG_DUMMY_NUM(0)`, copy `iobuf[1..N]`. - `nand_get_feature` / `nand_set_feature` for GET_FEATURES 0x0F / SET_FEATURE 0x1F. - `nand_write_enable` (0x06). - `nand_erase_block`: WE → BLOCK_ERASE 0xD8 (3-byte row) → wait OIP → check E_FAIL bit. - `nand_program_page`: WE → PROGRAM_LOAD 0x02 (first chunk, resets cache) → PROGRAM_LOAD_RANDOM 0x84 (rest, preserves cache) → PROGRAM_EXECUTE 0x10 → wait OIP → check P_FAIL bit. - `flash_init` for NAND now clears block-protect bits via SET_FEATURE 0xA0 = 0x00 (NAND equivalent of NOR's `flash_unlock`). - `flash_erase_sector` and `flash_write_page` dispatch to NAND helpers when type=NAND. - NAND guard early-returns in `main.c` (added in commit 1) are removed: erase/write/scan/flash_program/flash_stream now flow to the right path. ## Verification on real hi3516av200 (Macronix MX35LF1GE4AB) End-to-end test cycle on a sacrificial block (flash offset `0x800000`): **backup → erase → write 64 pages × 2 KiB pattern → read-back verify → erase → restore backup → final verify**. \`\`\` ERASE block (128 KiB): 0.02 s, 100.0 % bytes are 0xFF post-erase WRITE 64 pages × 2 KiB: 1.53 s = 83.4 KB/s, P_FAIL=0 READ-BACK verify: byte-for-byte match (131072 B) ✓ RESTORE original: byte-for-byte match against backup ✓ \`\`\` Plus the 16 MiB factory-firmware dump from the earlier read-only iteration shows real, structured content with byte-perfect strings: | Offset | String | Significance | |---|---|---| | `0x0080a04` | `"U-Boot 2010.06-dirty (Apr 22 2…"` | u-boot env entry | | `0x0200021` | `"Linux-3.18.20-hi3516av2.0…"` | Linux kernel header | | `0x03da91d` | `"Hisilicon HI3516AV200 DEMO Board"` | exact board ID | | `0x03de19d` | `"spi-nand@0"` | DT node confirming SPI NAND | Throughput at 921600 baud is the same as NOR — UART is the bottleneck, the per-page PAGE_READ→READ_FROM_CACHE overhead is negligible. \`\`\` make -C agent test HOST_CC=gcc: 5406/5406 pytest tests/ -x --ignore=tests/fuzz: 402 passed, 2 skipped ruff & mypy: clean make SOC=hi3516ev300/cv300/cv500/3519v101: all build \`\`\` ## Implementation table | Concept | NOR path | NAND path | |---|---|---| | Read | mem-mapped at FLASH_MEM (or normal-mode 0x03 cmd) | PAGE_READ 0x13 (row addr) → wait OIP → chunked READ_FROM_CACHE 0x03 with iobuf[0] dummy skip | | Erase | WE → SECTOR_ERASE 0xD8 (3-byte byte addr) → poll RDSR.WIP | WE → BLOCK_ERASE 0xD8 (3-byte row) → poll feature 0xC0 OIP → check E_FAIL | | Program | WE → PAGE_PROGRAM 0x02 (col addr + 1–256 B data) → poll RDSR.WIP | WE → PROGRAM_LOAD 0x02 (first 256 B, resets cache) → PROGRAM_LOAD_RANDOM 0x84 (rest) → PROGRAM_EXECUTE 0x10 (row) → poll OIP → check P_FAIL | | Write protection | RDSR.BP_MASK clear via WRSR | SET_FEATURE 0xA0 = 0x00 (clear BP0..BP3 + BRWD) | | Block size | 64 KiB sector | 128 KiB block | | Page size | 256 B | 2 KiB | | ECC | none | on-chip 4-bit/512 (chip handles transparently) | ## Out of scope (separate follow-ups) - Bad-block management (skip blocks marked as bad in OOB byte 0 of page 0). Currently any erase/write hits all blocks; bad blocks would show as ECC errors / write failures. - Other SPI NAND chip IDs — currently only Macronix MX35LF1GE4AB recognized. Winbond W25N, GigaDevice GD5F, Micron MT29F, Toshiba TC58 each need their JEDEC + geometry added. - Quad-IO read/write (not needed at 921600 baud — UART is the bottleneck). - ECC mismatch reporting back to host. ## Test plan - [x] Real hi3516av200 hardware: erase + write + byte-perfect read-back + restore cycle - [x] All test suites green 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Dmitry Ilyin <widgetii@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fourth agent platform after ev300 (V4), cv300 (V3), and av300/dv300/cv500 (V5/cv500-family). V3A =
3519v101 + av200— Cortex-A7 with V3-era peripheral addresses (UART0x12100000, WDT0x12080000) but DDR at0x80000000like cv500-family, per qemu-hisilicon'shi3519v101_soc.The bootrom-protocol quirks (
sendFrameForStarthandshake,PRESTEP1DDR training step, non-fatal TAILs) were already landed fordefib install/defib burnin #47 + #48 + #65. This PR is just the agent build wiring plus one real protocol fix the agent-flash path was missing.The fix: don't pre-truncate
spl_overrideat the call sitedefib agent upload/agent flashwere doing:```python
spl_data = cached_fw.read_bytes()[:profile.spl_max_size]
```
before passing to
send_firmware(). When_send_spl()then scans this truncated buffer for the LZMA/gzip SPL boundary, it can't find anything pastprofile_max— so for chips where the OpenIPC SPL is larger than the HiTool reference (e.g. av200's SVB-enabled SPL is0x6800, butprofile_maxis0x4F00), we send0x1900too few bytes. The SPL never finishes its post-DDR-init code, the SPL TAIL completes with no follow-through, and the agent HEAD frame for0x81000000gets0x08rejection.Fix: pass the full u-boot binary as
spl_override._send_spl()already handles the slicing via its detected LZMA/gzip boundary.Verification
QEMU
qemu-system-arm -M hi3519v101 -kernel agent-hi3519v101.elf:agent boots cleanly, READY/DEFIB packet stream, no faults.
Real hi3516av200 board (
/dev/ttyUSB1, MikroTikether8):```
upload ok=True
agent ready: ram_base=0x80000000 caps=0x7f version=2
```
The board in our lab has SPI NAND and the agent's NOR-only flash driver
returns shifted JEDEC bytes / 0-byte reads on NAND; that's a separate
larger limitation noted at the bottom.
hi3516cv300 regression (
/dev/uart-IVGHP203Y-AF, MikroTikether3):```
agent ready: jedec=ef4018 flash=16384KiB ram=0x80000000 caps=0x7f
256 KiB @ 921600: 3.02 s = 84.9 KB/s
```
The spl_override-truncation fix changes cv300's SPL size from
0x4F00(clamped) to0x5400(LZMA-detected at offset0x54c8). Agent loads identically, throughput unchanged — the previous undersized SPL was working coincidentally because cv300's SPL pieces happened to fit in the smaller window.```
make -C agent test HOST_CC=gcc: 5406/5406
pytest tests/ -x --ignore=tests/fuzz: 402 passed, 2 skipped
ruff & mypy: clean
```
All four agent SoCs (ev300, cv300, cv500, 3519v101) build clean.
Aliasing
Following the existing
gk7205v300 → gk7205v200shape: one binary serves the family, multiple chip names route to it.```python
"hi3519v101": "hi3519v101",
"hi3516av200": "hi3519v101", # 3519v101 family, same memory map
```
Known limitation: SPI NAND on av200 boards
The av200 board in our lab has SPI NAND flash. The agent's flash driver (
agent/spi_flash.c) supports SPI NOR only — uses the memory-mapped read window at0x14000000and direct FMC register commands. On a NAND board:agent inforeturns shifted JEDEC bytes (e.g.00c212instead of validc2 XX YY)agent readreturns 0 bytesThis affects all V3-and-later HiSilicon chips that ship with SPI NAND (some av200, some av300, some cv500). Adding SPI NAND support to the agent is its own piece of work. This PR ships the platform wiring; a follow-up can address NAND.
Test plan
-M hi3519v101: agent boots cleanly🤖 Generated with Claude Code