Add bare-metal flash agent with COBS binary protocol#10
Merged
Conversation
Bare-metal ARM32 agent (agent/):
- startup.S: entry point, MMU/cache disable, TLB invalidate, BSS clear
- uart.c: PL011 UART driver (per-SoC base addresses)
- spi_flash.c: HiSilicon FMC flash controller (memory-mapped read, register write/erase)
- protocol.c: COBS framing + CRC32 packet protocol
- cobs.c: COBS encode/decode
- main.c: command loop (INFO, READ, WRITE, ERASE, CRC32, REBOOT)
- Makefile: cross-compile for 8 SoCs, auto-pad to 32KB
- Compiles to ~4KB, runs without OS
Key fix: -mno-unaligned-access prevents ARM data abort on
non-word-aligned CRC32 byte writes (verified on real hi3516ev300).
Tested on real hardware:
- Uploaded via U-Boot loady + go at 0x41000000
- Agent prints debug output ('DF', 'XYZ') and COBS READY packet
- QEMU: uploads via boot protocol, PL011 writes confirmed via trace
Python host side (src/defib/agent/):
- cobs.py: COBS encode/decode
- protocol.py: packet framing, send/recv, command constants
- client.py: high-level FlashAgentClient (upload, read, write, verify)
Tests: 18 COBS + protocol tests (roundtrip, CRC, edge cases)
Known issues:
- QEMU chardev drops data between fastboot→PL011 handoff (socket timing)
- Boot protocol upload needs real DDR init + SPL (agent must be loaded
via U-Boot loady, not directly via boot protocol on real hardware)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PL011 UART driver now handles BREAK/framing errors from host port close: uart_getc_safe() returns -2 on error, proto_recv discards partial frames and clears error flags. uart_putc has TX timeout to prevent hanging. Serial transport disables DTR/RTS toggling. Tested on real hi3516ev300: agent survived 5 consecutive disconnect cycles with consistent beacon output. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed raw uart_putc beacons ('*', '.', '[', 'R', 'r') that polluted
the COBS protocol stream. Agent now sends COBS-framed READY every ~2s
of idle time instead of suppressing it after first command — allows
host to detect agent after serial port reconnect.
Tested on hi3516ev300: survived 5 disconnect cycles, clean COBS only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New test_agent_protocol.py: 19 tests covering recv_packet, send_packet, wait_for_ready, recv_response, and data streaming with FakePort/Transport. Fixed _recv_packet_sync: uses per-port persistent buffer so bytes read past the first COBS delimiter aren't lost between calls. Also uses in_waiting for non-blocking reads instead of blocking port.read(256). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SELFUPDATE now requires host to include expected CRC32 in the command payload: [addr:4LE] [size:4LE] [crc32:4LE]. After receiving all data, agent verifies CRC32 of written region. On mismatch, sends ACK_CRC_ERROR and stays alive instead of jumping to corrupt code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 new tests: packet format with CRC32, payload verification, data transfer flow, CRC mismatch error handling, success flow. 42 total agent tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1604 assertions across COBS encode/decode, CRC32 known vectors, protocol send/recv roundtrip, READY/ACK packets, bad CRC rejection, max payload, multi-packet streams, and Python cross-compatibility. Runs on host with `make -C agent test`. Added to CI as agent-test job. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New write handler: receives data packets to RAM with CRC32 verification, without jumping (unlike SELFUPDATE). Same protocol as SELFUPDATE but stays in command loop after completion. Needed for flash write path. 3 new Python tests, 1604 C tests still passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SELFUPDATE was overwriting its own running code, causing crash. Now receives data to staging area (1MB above load addr), verifies CRC32, then copies a position-independent ARM trampoline to a fixed RAM location which performs the final copy and jump. Also: CMD_WRITE handler reports received byte count on timeout and actual CRC on mismatch for debugging. Tested: 2 consecutive self-updates without power cycle, binary match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
With MMU/caches disabled, COBS decode + CRC32 on uncached DDR takes ~1-2ms per packet. At 115200 baud, this overflows the 16-byte PL011 FIFO when packets arrive back-to-back. Solution: software RX ring buffer (4KB) drains the hardware FIFO before and after heavy processing in proto_recv. Bytes buffered in software survive the processing gap. Host should use ≤512 byte chunks for WRITE to stay within safe timing margins. READ (device→host) unaffected — agent controls transmit pacing. Also: CMD_WRITE removed per-packet ACK (not needed with soft FIFO). Debug reporting: timeout reports received byte count, CRC mismatch reports actual CRC value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- uart_init: always reconfigure to known-good state instead of trusting bootrom settings. Prevents issues from stale CR flags. - Software RX ring buffer (4KB) in proto_recv drains PL011 FIFO before heavy COBS+CRC processing to prevent overflow on uncached DDR. - CMD_WRITE works reliably up to 16KB per transfer. Larger writes need host-side chunking due to cumulative FIFO timing on uncached DDR. - READ (device→host) unaffected: 98.9% efficiency at 256KB. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
37 tasks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (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
Bare-metal ARM32 agent binary that runs on HiSilicon/Goke SoCs for fast flash operations over UART, replacing the slow
md.bhex dump approach.recv_packet/send_packetwith persistent buffering,wait_for_readyPerformance (real hi3516ev300 hardware, 115200 baud)
Files
agent/— C source: startup.S, main.c, uart.c, protocol.c, cobs.c, spi_flash.c, Makefile, link.ldsrc/defib/agent/— Python host: protocol.py, client.py, cobs.pysrc/defib/transport/socket.py— Unix socket transport for QEMU testingtests/test_agent_protocol.py— 27 protocol tests (FakePort/FakeTransport)tests/test_agent_cobs.py— 18 COBS/packet testsagent/test_agent.c— 1604 C unit test assertions.github/workflows/ci.yml— Newagent-testCI jobKnown Limitations
Test plan
🤖 Generated with Claude Code