From 2549f22a97d11b31c62c7490b62cd41ff76e996f Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin Date: Tue, 5 May 2026 15:58:00 +0300 Subject: [PATCH] Add hi3519v101 + hi3516av200 (V3A family) to flash agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- agent/Makefile | 16 +++++++++++++++- src/defib/agent/client.py | 2 ++ src/defib/cli/app.py | 14 +++++++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/agent/Makefile b/agent/Makefile index d6de9cc..bbba356 100644 --- a/agent/Makefile +++ b/agent/Makefile @@ -105,8 +105,22 @@ else ifeq ($(SOC),hi3516cv610) WDT_BASE = 0x12030000 CRG_BASE = 0x12010000 SYSCTRL_REBOOT = 0x12020004 +else ifeq ($(SOC),hi3519v101) + # V3A generation (3519v101 family: hi3519v101, hi3516av200) — Cortex-A7 + # with V3-era peripheral addresses (UART 0x12100000, WDT 0x12080000) + # but DDR at 0x80000000 like cv500-family. Memory map per + # qemu-hisilicon hi3519v101_soc. + UART_BASE = 0x12100000 + UART_CLOCK = 24000000 + LOAD_ADDR = 0x81000000 + FLASH_MEM = 0x14000000 + FMC_BASE = 0x10000000 + RAM_BASE = 0x80000000 + WDT_BASE = 0x12080000 + CRG_BASE = 0x12010000 + SYSCTRL_REBOOT = 0x12020004 else - $(error Unknown SOC: $(SOC). Supported: hi3516ev300 hi3516ev200 gk7205v200 gk7205v300 hi3516cv300 hi3516cv500 hi3518ev200 hi3516cv610) + $(error Unknown SOC: $(SOC). Supported: hi3516ev300 hi3516ev200 gk7205v200 gk7205v300 hi3516cv300 hi3516cv500 hi3518ev200 hi3516cv610 hi3519v101) endif # Per-SoC CPU. V3 chips (cv300) are ARM926EJ-S (ARMv5TEJ); diff --git a/src/defib/agent/client.py b/src/defib/agent/client.py index c47838d..f81e691 100644 --- a/src/defib/agent/client.py +++ b/src/defib/agent/client.py @@ -115,6 +115,8 @@ def get_agent_binary(chip: str) -> Path | None: "hi3516cv500": "hi3516cv500", "hi3516av300": "hi3516cv500", # cv500-family, same memory map "hi3516dv300": "hi3516cv500", # cv500-family, same memory map + "hi3519v101": "hi3519v101", + "hi3516av200": "hi3519v101", # 3519v101 family, same memory map "hi3516cv610": "hi3516cv610", "hi3518ev200": "hi3518ev200", } diff --git a/src/defib/cli/app.py b/src/defib/cli/app.py index c6df023..20662c6 100644 --- a/src/defib/cli/app.py +++ b/src/defib/cli/app.py @@ -933,11 +933,16 @@ async def _agent_upload_async(chip: str, port: str, output: str) -> None: if output == "human": console.print("Downloading U-Boot for SPL...") cached_fw = download_firmware(chip) - spl_data = cached_fw.read_bytes()[:profile.spl_max_size] + # Pass the full U-Boot binary as spl_override; _send_spl detects the + # actual SPL/u-boot boundary via LZMA/gzip signature scan and slices + # accordingly. Pre-truncating to profile.spl_max_size hides the real + # boundary on chips where the OpenIPC SPL is larger than the HiTool + # reference (e.g. hi3516av200's SVB-enabled SPL is 0x6800, not 0x4F00). + spl_data = cached_fw.read_bytes() if output == "human": console.print(f"Agent: [cyan]{agent_path.name}[/cyan] ({len(agent_data)} bytes)") - console.print(f"SPL: {len(spl_data)} bytes (from OpenIPC U-Boot)") + console.print(f"SPL: full U-Boot ({len(spl_data)} bytes — boundary auto-detected)") console.print("\n[yellow]Power-cycle the camera now![/yellow]\n") transport = await SerialTransport.create(port) @@ -1073,7 +1078,10 @@ async def _agent_flash_async( if output == "human": console.print("Downloading U-Boot for SPL...") cached_fw = download_firmware(chip) - spl_data = cached_fw.read_bytes()[:profile.spl_max_size] + # Pass full u-boot; _send_spl detects the actual SPL boundary so chips + # where OpenIPC SPL is larger than HiTool's profile_max (e.g. av200's + # SVB-enabled SPL = 0x6800 vs profile_max = 0x4F00) get the right size. + spl_data = cached_fw.read_bytes() if output == "human": console.print(f"Firmware: [cyan]{fw_path.name}[/cyan] ({len(firmware)} bytes, CRC {fw_crc:#010x})")