Skip to content

hi3536dv100: flash agent, and the NBD8010S board's own SPL as a variant - #150

Merged
widgetii merged 2 commits into
masterfrom
agent-hi3536dv100
Oct 15, 2026
Merged

widgetii merged 2 commits into
masterfrom
agent-hi3536dv100

Conversation

@widgetii

Copy link
Copy Markdown
Member

The Xiongmai NBD8010S-KL-V2 NVR, sold as "Hi3536E", is a Hi3536DV100 die
(SCSYSID 0x3536D100). This makes it recoverable through the bootrom and
gives it a flash agent.

Agent (SOC=hi3536dv100). The memory map comes from vendor
arch-hi3536dv100/platform.h: UART0 PL011 at 0x12080000, FMC 0x10000000
with its window at 0x14000000, CRG 0x12040000, FMC clock gate in CRG48
(+0xC0), SP805 WDT 0x12070000, reboot via sysctl+4. The PL011 runs off
the fixed 24 MHz XTAL (CONFIG_PL011_CLOCK), so SET_BAUD reaches 921600
with no clock switching. That is unlike hi3520dv200 (#97), whose
bootrom leaves the UART on a ~2 MHz reference. This SoC has no SPI pad
block at 0x100C0000, and the vendor FMC driver never touches the pads,
so SPI_PINS_SKIP leaves them as the bootrom set them.

Variant hi3536dv100:nbd8010s. With the OpenIPC SPL, every bootrom stage
is ACKed but the jump to 0x81000000 resets the chip: fresh 0x20 markers,
then the stock U-Boot boots from flash. That looks like a successful
burn. The OpenIPC SPL's DDR/PLL table (222 entries) was built for
another board; this board's stock U-Boot has a different one (185
entries). The variant ships the stock SPL, flash bytes 0..0x5840. The
SPL code runs right up to the LZMA payload at 0x5840, so FILELEN grows
to 0x5840; the boundary scan would otherwise round down to 0x5800 and
cut its tail.

burn now honours SPL_BLOB. RecoverySession ignored it, so only the agent
paths used a variant's SPL. It now passes the blob as spl_override,
which lets burn -c hi3536dv100:nbd8010s put the OpenIPC U-Boot in RAM
on top of the board's own DDR init.

Verified on the NBD8010S-KL-V2 (W25Q128, ef4018, 16 MiB) with Tasmota
power cycling:

defib burn -c hi3536dv100:nbd8010s -p /dev/ttyUSB2 --power-cycle
-> "OpenIPC #", U-Boot 2010.06 (May 09 2026)
defib agent upload -c hi3536dv100:nbd8010s -p /dev/ttyUSB2 --power-cycle
-> Agent ready, RAM 0x80000000, Flash 16384KB
agent info: JEDEC ef4018, all capabilities
full-chip read in 256 KiB chunks at 921600: 86 KB/s, 0 retries,
CRC 0xaedae175, the same as stock U-Boot sf read + crc32
(whole chip and each of the 16 MiB slices)
agent scan: 256/256 sectors good
write a pattern to blank sector 0xF50000, read back bit-exact,
erase back to all-0xFF; neighbours and logo partition unchanged

Without the variant, agent upload never gets READY and burn ends in the
stock U-Boot. A single 16 MiB read stream at 921600 still drops one COBS
frame in ~10k (the known seq-gap failure); 256 KiB chunks are clean.

The Xiongmai NBD8010S-KL-V2 NVR, sold as "Hi3536E", is a Hi3536DV100 die
(SCSYSID 0x3536D100). This makes it recoverable through the bootrom and
gives it a flash agent.

Agent (SOC=hi3536dv100). The memory map comes from vendor
arch-hi3536dv100/platform.h: UART0 PL011 at 0x12080000, FMC 0x10000000
with its window at 0x14000000, CRG 0x12040000, FMC clock gate in CRG48
(+0xC0), SP805 WDT 0x12070000, reboot via sysctl+4. The PL011 runs off
the fixed 24 MHz XTAL (CONFIG_PL011_CLOCK), so SET_BAUD reaches 921600
with no clock switching. That is unlike hi3520dv200 (#97), whose
bootrom leaves the UART on a ~2 MHz reference. This SoC has no SPI pad
block at 0x100C0000, and the vendor FMC driver never touches the pads,
so SPI_PINS_SKIP leaves them as the bootrom set them.

Variant hi3536dv100:nbd8010s. With the OpenIPC SPL, every bootrom stage
is ACKed but the jump to 0x81000000 resets the chip: fresh 0x20 markers,
then the stock U-Boot boots from flash. That looks like a successful
burn. The OpenIPC SPL's DDR/PLL table (222 entries) was built for
another board; this board's stock U-Boot has a different one (185
entries). The variant ships the stock SPL, flash bytes 0..0x5840. The
SPL code runs right up to the LZMA payload at 0x5840, so FILELEN grows
to 0x5840; the boundary scan would otherwise round down to 0x5800 and
cut its tail.

burn now honours SPL_BLOB. RecoverySession ignored it, so only the agent
paths used a variant's SPL. It now passes the blob as spl_override,
which lets `burn -c hi3536dv100:nbd8010s` put the OpenIPC U-Boot in RAM
on top of the board's own DDR init.

Verified on the NBD8010S-KL-V2 (W25Q128, ef4018, 16 MiB) with Tasmota
power cycling:

  defib burn -c hi3536dv100:nbd8010s -p /dev/ttyUSB2 --power-cycle
    -> "OpenIPC #", U-Boot 2010.06 (May 09 2026)
  defib agent upload -c hi3536dv100:nbd8010s -p /dev/ttyUSB2 --power-cycle
    -> Agent ready, RAM 0x80000000, Flash 16384KB
  agent info: JEDEC ef4018, all capabilities
  full-chip read in 256 KiB chunks at 921600: 86 KB/s, 0 retries,
    CRC 0xaedae175, the same as stock U-Boot `sf read` + `crc32`
    (whole chip and each of the 16 MiB slices)
  agent scan: 256/256 sectors good
  write a pattern to blank sector 0xF50000, read back bit-exact,
    erase back to all-0xFF; neighbours and logo partition unchanged

Without the variant, agent upload never gets READY and burn ends in the
stock U-Boot. A single 16 MiB read stream at 921600 still drops one COBS
frame in ~10k (the known seq-gap failure); 256 KiB chunks are clean.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add Hi3536DV100 flash agent and NBD8010S recovery variant

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Add Hi3536DV100 flash-agent support for UART recovery and SPI flash operations.
• Use the NBD8010S board’s SPL to initialize its DDR during recovery.
• Make burn honor variant SPL blobs, with profile and recovery tests.
Diagram

graph TD
  P["Board profile"] --> B["Burn session"] --> S["Standard protocol"] --> R["Boot ROM"] --> L["Board SPL"] --> M["RAM payload"] --> F[("SPI flash")]
  P --> A["Agent upload"] --> S
Loading
High-Level Assessment

Reuse the existing SPL_BLOB variant mechanism and standard transfer protocol. Rebuilding the OpenIPC SPL with this board’s DDR table would require board-specific initialization work without removing the need for a board variant; passing the existing blob through burn is the more direct approach.

Files changed (10) +87 / -8

Enhancement (3) +25 / -2
MakefileConfigure the Hi3536DV100 agent build +20/-1

Configure the Hi3536DV100 agent build

• Adds the SoC’s UART, FMC, RAM, watchdog, clock-gate, and reboot addresses. Enables a build flag that preserves boot-ROM SPI pad configuration.

agent/Makefile

spi_flash.cPreserve SPI pads when requested +4/-1

Preserve SPI pads when requested

• Skips FMC SPI pad writes for builds defining SPI_PINS_SKIP, allowing Hi3536DV100 to retain its boot-ROM pad setup.

agent/spi_flash.c

client.pyResolve Hi3536DV100 to its agent build +1/-0

Resolve Hi3536DV100 to its agent build

• Registers Hi3536DV100 in the chip-to-agent mapping so agent upload can locate its binary, including when a board variant is specified.

src/defib/agent/client.py

Bug fix (1) +14 / -4
session.pyApply variant SPL blobs during burn +14/-4

Apply variant SPL blobs during burn

• Loads SPL data from the selected standard-protocol profile and passes it as a firmware-transfer override. Other protocols and profiles without an SPL blob retain their existing call path.

src/defib/recovery/session.py

Tests (3) +39 / -0
test_agent_binary_lookup.pyTest agent lookup with the board suffix +3/-0

Test agent lookup with the board suffix

• Checks that hi3536dv100:nbd8010s resolves to the Hi3536DV100 agent build.

tests/test_agent_binary_lookup.py

test_profiles.pyTest NBD8010S SPL profile data +13/-0

Test NBD8010S SPL profile data

• Verifies the variant loads the expected SPL header and full 0x5840-byte blob, while the base SoC profile has no SPL override.

tests/test_profiles.py

test_recovery_session.pyTest variant SPL transfer during recovery +23/-0

Test variant SPL transfer during recovery

• Runs a mock standard-protocol recovery and checks that transmitted data includes the NBD8010S SPL rather than only the supplied firmware bytes.

tests/test_recovery_session.py

Documentation (2) +8 / -1
CLAUDE.mdUpdate supported-SoC count +1/-1

Update supported-SoC count

• Increases the documented flash-agent SoC count from twelve to thirteen.

CLAUDE.md

README.mdDocument Hi3536DV100 addresses and board variant +7/-0

Document Hi3536DV100 addresses and board variant

• Adds the SoC to the agent address table and explains why NBD8010S recovery requires its board-specific SPL.

agent/README.md

Other (1) +1 / -1
hi3536dv100.jsonDeclare the NBD8010S SPL variant +1/-1

Declare the NBD8010S SPL variant

• Adds a variant referencing the board SPL and raises its SPL transfer limit to 0x5840 so the loader’s tail is retained.

src/defib/profiles/data/hi3536dv100.json

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. eMMC burns send an oversized SPL ✓ Resolved
Description
RecoverySession.run now passes the eMMC variant’s 0x5000-byte SPL blob to _send_spl, which falls
back to the inherited 0x6000-byte limit and pads the transfer with 0x1000 zero bytes. When burning
hi3516av300:emmc, those extra bytes extend beyond the extracted SPL into SRAM the protocol
identifies as bootrom state, putting the handoff to U-Boot at risk.
Code

src/defib/recovery/session.py[R255-258]

+            if spl_override is not None and isinstance(protocol, HiSiliconStandard):
+                send_result = await protocol.send_firmware(
+                    transport, firmware, on_progress, spl_override=spl_override,
+                )
Evidence
The eMMC profile inherits FILELEN 0x6000 while its bundled SPL is verified as 0x5000 bytes,
extracted before the compressed payload. The new session path forwards that blob; the protocol uses
the profile maximum when boundary detection finds no payload marker and pads a shorter override to
the selected size. Its boundary-detection documentation identifies transfers past the SPL boundary
as capable of corrupting bootrom state.

src/defib/recovery/session.py[94-98]
src/defib/recovery/session.py[255-258]
src/defib/profiles/data/hi3516av300.json[7-12]
tests/test_profiles.py[215-225]
src/defib/protocol/hisilicon_standard.py[438-469]
src/defib/protocol/hisilicon_standard.py[510-523]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
RecoverySession now forwards board SPL blobs to the standard protocol. For `hi3516av300:emmc`, the blob is 0x5000 bytes but the inherited SPL limit is 0x6000, so the fallback size pads and sends bytes beyond the blob.
## Fix Focus Areas
- src/defib/recovery/session.py[94-98]
- src/defib/protocol/hisilicon_standard.py[510-523]
## Recommended Fix
When an SPL override has no detected payload boundary, use its actual length rather than padding it to the profile maximum. Preserve the existing boundary detection and SRAM-limit behavior, and test the transfer length for both the eMMC and new NBD8010S variants.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/defib/recovery/session.py
Qodo review on this branch. Now that burn forwards SPL_BLOB, the
0x5000-byte hi3516av300:emmc blob ran into the same sizing the agent
paths already had. A blob carries no LZMA/gzip marker, so
_detect_spl_size falls back to the inherited FILELEN of 0x6000, and
ljust() zero-pads 4 KiB past the SPL into SRAM the bootrom still uses.
The SPL size is now capped at the override's length. Boundary detection
and the SRAM limit work as before. A full U-Boot passed as the override
is always longer than its detected boundary, so it is unaffected.

The session test now checks the SPL HEAD length for both variants:
0x5840 for hi3536dv100:nbd8010s and 0x5000 for hi3516av300:emmc. The
emmc case fails without this change.

Re-verified agent upload -c hi3536dv100:nbd8010s on the NBD8010S-KL-V2
(READY; flash 0..0xF80000 CRC 0xa63bd6ca = backup). hi3516av300:emmc
now sends 0x5000 instead of 0x6000. That is the extracted SPL's real
size, but it has not been re-run on an eMMC av300 here.
@widgetii
widgetii merged commit e0a234c into master Oct 15, 2026
13 checks passed
@widgetii
widgetii deleted the agent-hi3536dv100 branch October 15, 2026 14:40
widgetii added a commit that referenced this pull request Oct 15, 2026
…s the E (#151)

The variant (#150) uploaded the Xiongmai NBD8010S-KL-V2's stock SPL
because the OpenIPC SPL reset that board mid-upload. The real cause was
in the U-Boot, not the board: Xiongmai's "Hi3536E" is a Hi3536DV100 die
that reads 0x02000000 at SYSCTRL+0xEE0 and needs its own PLL, DMC timing
and PHY settings. Without them, software DDR training fails and calls
reset_cpu(). OpenIPC/u-boot-hi3536dv100#1 now applies those settings on
that chip ID and is published as u-boot-hi3536dv100-universal.bin in
OpenIPC/firmware latest.

This removes the variant, the 0x5840-byte vendor SPL blob and the tests
that pinned it. burn keeps honouring SPL_BLOB, which hi3516av300:emmc
still uses, and the session test still checks that blob's 0x5000 length.

Verified on the NBD8010S-KL-V2 with plain -c hi3536dv100 and the
published U-Boot (its SPL window is byte-identical to the build tested
on hardware before merging):

  defib agent upload -c hi3536dv100 -p /dev/ttyUSB2 --power-cycle
    -> Agent ready; flash 0..0xF80000 CRC 0xa63bd6ca = backup
  defib burn -c hi3536dv100 -p /dev/ttyUSB2 --power-cycle
    -> "OpenIPC #", U-Boot 2010.06 (Oct 15 2026 - 17:06:41),
       DDR PLL CRG+0x2c = 0x209b (the E setting)
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