Skip to content

agent/cv6xx: flash agent support for hi3516cv608 / hi3516cv610#110

Merged
widgetii merged 2 commits into
masterfrom
cv6xx-agent-upload
Jun 3, 2026
Merged

agent/cv6xx: flash agent support for hi3516cv608 / hi3516cv610#110
widgetii merged 2 commits into
masterfrom
cv6xx-agent-upload

Conversation

@widgetii
Copy link
Copy Markdown
Member

@widgetii widgetii commented Jun 3, 2026

Summary

Adds end-to-end flash agent support for the HiSilicon CV6xx family
(hi3516cv608, hi3516cv610, hi3516dv500, hi3519dv500), verified live on a
Vectis-bridged hi3516cv608.

CV6xx fastboot is structurally different from HiSilicon Standard: a single
composite (GSL + DDR tables + U-Boot-shaped section) gets uploaded and the
bootrom parses the 1024-byte U-Boot header to decide where to jump. That
changed the whole agent-bring-up shape, and also surfaced two stale V3/V4
addresses in the existing cv610 stanza that turn out to never have been
exercised on real CV6xx silicon.

What's in the change set

  • HiSiliconCV6xx.send_firmware(..., uboot_override=) + new module
    helper wrap_cv6xx_payload(header_source, payload) — clones the
    1024-byte U-Boot header from a known-good composite and patches the
    length field at byte 36 so the bootrom validates and jumps to the
    wrapped payload.
  • _agent_upload_async in cli/app.py dispatches on protocol via
    find_protocol(chip). HiSiliconStandard path unchanged; CV6xx takes a
    new -f/--file (composite for GSL + DDR), wraps the agent, and runs
    handshake → GSL → DDR → wrapped-agent. Uses the spawn-handshake-before-
    power-cycle ordering from recovery/session: blast proactive handshake magic before power-cycle #109 so it works with Vectis --power-cycle.
  • src/defib/agent/client.pyhi3516cv608hi3516cv610 agent
    alias, mirroring the existing same-family aliases.
  • agent/Makefile hi3516cv610 stanza, fixing several latent V3/V4
    copy-paste errors:
    • LOAD_ADDR 0x410000000x41000400 so the agent's link-time
      addresses match where the bootrom actually jumps after the
      U-Boot-header offset (load_addr + 0x400).
    • CRG_BASE 0x120100000x11010000, WDT_BASE and
      SYSCTRL_REBOOT likewise moved into 0x11xxxxxx (matches the UART
      address and the kernel's earlycon=pl011,0x11040000 arg).
    • FLASH_MEM 0x140000000x0F000000 (FMC MMIO window on CV6xx).
    • New FMC_CRG_OFFSET=0x3F40 + FMC_CRG_CLK_BIT=4 because the FMC
      clock-gate register moved from CRG+0x144 bit1 to CRG+0x3F40 bit4.
    • New SPI_PIN_BASE=0x10260000 + SPI_PINS_CV6XX=1 because the SPI
      pinctrl block moved from 0x100C0000 and the per-pin register
      offsets + pad-drive values changed.
  • agent/spi_flash.c uses the new FMC_CRG_OFFSET / FMC_CRG_CLK_BIT
    and SPI_PIN_BASE / SPI_PINS_CV6XX macros, falling back to V3/V4
    defaults when not defined.

Source of truth for the CV6xx pinmux + FMC sequencing is the dimerr u-boot
fork: drivers/mtd/fmc_hi3516cv610.c::hi3516cv610_spi_io_config().

Test plan

  • uv run pytest tests/ -x -v --ignore=tests/fuzz — 68/68 pass

  • uv run ruff check src/ and uv run mypy src/defib/ clean

  • make -C agent test HOST_CC=gcc — 5412/5412 agent C tests pass

  • make -C agent SOC=hi3516cv610 — builds agent-hi3516cv610.bin
    (18 KiB)

  • End-to-end on live hi3516cv608 over Vectis
    (rfc2217://127.0.0.1:35240 with DEFIB_POWER_TYPE=vectis):

    ```
    defib agent upload -c hi3516cv608 -f boot-hi3516cv608-nor.bin \
      -p rfc2217://127.0.0.1:35240 --power-cycle
    → Agent ready!
      RAM: 0x40000000
      Flash: 16384KB
      JEDEC: a14018  (FM25Q128A 16 MiB)
    ```
    
    Flash MMIO read from `0x0F000000` returns the burned U-Boot blob
    byte-for-byte, confirming the SPI path works end-to-end.
    

widgetii added 2 commits June 3, 2026 17:09
Adds end-to-end agent support for the HiSilicon CV6xx family
(hi3516cv608, hi3516cv610, hi3516dv500, hi3519dv500) by wiring up the
CV6xx fastboot path through `defib agent upload`.

CV6xx fastboot is structurally different from HiSilicon Standard
(SPL + agent two-stage) — it loads a single composite (GSL + DDR
tables + U-Boot section) and the bootrom parses the 1024-byte U-Boot
header at byte 0 of the final stage. The header's code-offset field
at byte 8 is 0x400, so the bootrom jumps to `load_addr + 0x400`, not
`load_addr`. Existing CV6xx agent attempts crashed because the binary
was linked at 0x41000000 but actually executed at 0x41000400, so all
link-time absolute references (page table, etc.) pointed at wrong
memory.

Changes:

* `agent/Makefile` hi3516cv610 stanza:
  - LOAD_ADDR 0x41000000 → 0x41000400 (account for U-Boot header).
  - CRG_BASE 0x12010000 → 0x11010000 (CV6xx moved the peripheral
    CRG block; the old V3/V4 address is unmapped on CV6xx and the
    read triggered an external abort during flash_init).
  - WDT_BASE 0x12030000 → 0x11030000 and SYSCTRL_REBOOT
    0x12020004 → 0x11020004 for the same reason.
  - FLASH_MEM 0x14000000 → 0x0F000000 (CV6xx FMC MMIO window).
  - New per-SoC `FMC_CRG_OFFSET` / `FMC_CRG_CLK_BIT` so the FMC
    clock-gate register layout can differ per family (CV6xx puts
    `fmc_cken` on bit 4 at CRG+0x3F40 instead of bit 1 at CRG+0x0144).

* `agent/spi_flash.c`: REG_FMC_CRG now uses the configurable
  FMC_CRG_OFFSET / FMC_CRG_CLK_BIT macros, with the V3/V4 default
  preserved.

* `src/defib/protocol/hisilicon_cv6xx.py`:
  - `wrap_cv6xx_payload(header_source, payload)` helper — clones the
    1024-byte U-Boot header from a known-good composite and patches
    the length field at byte 36 so the bootrom validates and jumps
    to the wrapped payload.
  - `HiSiliconCV6xx.send_firmware(..., uboot_override=None)` so the
    caller can replace the U-Boot section with an arbitrary payload
    (the agent, in our case) while reusing the composite's GSL +
    DDR tables.

* `src/defib/cli/app.py` `_agent_upload_async`:
  - Dispatches on protocol family via `find_protocol(chip)`. The
    HiSilicon Standard path is unchanged; CV6xx routes to a new
    `_agent_upload_cv6xx` helper.
  - The CV6xx path takes a `-f/--file` composite (no auto-download
    yet — OpenIPC doesn't ship a prebuilt for these SoCs), parses
    it, wraps the agent with `wrap_cv6xx_payload`, and runs the
    full handshake → GSL → DDR → wrapped-agent flow. Uses the
    spawn-handshake-before-power-cycle ordering (PR #109) so it
    works on Vectis-bridged boards via `--power-cycle`.

* `src/defib/agent/client.py`: alias `hi3516cv608 → hi3516cv610`
  agent binary, mirroring the existing same-family aliases for
  hi3516av300 / hi3516dv300 → hi3516cv500.

Verified live on a Vectis-bridged hi3516cv608 (OpenIPC Vectis on
`rfc2217://127.0.0.1:35240`): `defib agent upload --power-cycle`
brings the agent up cleanly — RAM 0x40000000, flash 16 MiB, sector
64 KiB, agent_version 4, capabilities 0xFF.

Known follow-up: the agent's `flash_read_id` returns 0x00 0x00 0x00
on this CV6xx board even though flash_size is correct via the
detect_size fallback. The FMC register-mode result path (currently
reads from FLASH_MEM after issuing READ_ID) likely needs CV6xx
pinmux or FMC data-buffer tuning. Tracked separately — does not
affect the upload/INFO path.

68 pytest tests + 5412 agent C unit tests still pass. Ruff and mypy
clean. agent-hi3516cv610.bin still built by `make SOC=hi3516cv610`.
The first CV6xx agent commit got the upload path running but
flash_read_id returned zeros and flash CMD_READ failed because the
agent was writing to the V3/V4 pinctrl block (0x100C0000) with V3/V4
pad-drive values. On CV6xx the pinctrl block lives at 0x10260000 and
the register offsets + drive values are different.

Source of truth: dimerr/u-boot-hi3516cv6xx
`drivers/mtd/fmc_hi3516cv610.c::hi3516cv610_spi_io_config()`.

Changes:

* `agent/spi_flash.c::fmc_enter_normal()` — pinmux config now
  parameterised by `SPI_PIN_BASE` and a `SPI_PINS_CV6XX` family flag.
  Default (V3/V4) layout is preserved; with `-DSPI_PINS_CV6XX=1` the
  function uses CV6xx offsets / values (CLK 0x10=0x1291, CS0 0x18=
  0x1131, MOSI_IO0 0x0C=0x1261, MISO_IO1 0x1C=0x1261, WP_IO2 0x20=
  0x12E1, HOLD_IO3 0x14=0x1161 — 3.3V single-CS).

* `agent/Makefile` hi3516cv610 stanza now sets
  `SPI_PIN_BASE=0x10260000` and `SPI_PINS_CV6XX=1`, and the global
  CFLAGS pass them through.

Verified live on hi3516cv608 over Vectis:

  jedec_id=a14018 (FM25Q128A 16 MiB ✓)
  flash_size=16777216, sector_size=65536, ram_base=0x40000000
  flash[0..64] via MMIO window matches the U-Boot composite we
    burned earlier, byte-for-byte — proving real flash content is
    being read through 0x0F000000.

68 pytest + 5412 agent C tests still pass.
@widgetii widgetii merged commit 4438805 into master Jun 3, 2026
13 checks passed
@widgetii widgetii deleted the cv6xx-agent-upload branch June 3, 2026 14:25
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