[BUG] [SF32LB52] Add a collision-safe encoded transport for RAM-stub flash writes
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 18
- Forks
- 13
- PR merge metrics
- No merged PRs in 30d
Description
Pre-submission checklist
- I have read the SiFli documentation and the issue is not addressed there.
- I have updated to the latest version of sftool and confirmed the issue still exists.
- I have searched the issue tracker for a similar issue and not found a duplicate.
Bug Category
Flash Operations - Problems with write_flash, read_flash, erase operations
sftool Version
sftool 0.2.5
Operating System
Linux (Arch/Manjaro)
Target SiFli Chip Model
SF32LB52
Command Executed
sftool -c SF32LB52 -p /dev/ttyACM0 --after no_reset \
erase_region 0x12088000:32768
RUST_LOG=sftool_lib::common::ram_command=debug \
sftool config write-only-40-72-noverify.json
Bug Description
The SF32LB52 RAM flashing stub receives firmware as an unencoded binary stream over USART1. The same USART1 input is continuously monitored by the chip's integrated Debug IP. A valid Debug IP frame occurring naturally inside firmware can therefore be interpreted as an out-of-band hardware command during a raw flash transfer.
The minimized reproducer has two writes:
- The first payload contains a complete SF32LB52
EnterDebugModeframe. - The second payload ends with bytes matching the documented
MEMReadpayload grammar.
The second burn_erase_write returns RX_WAIT, after which the host transmits all declared bytes. The stub never prints run thread, never starts programming the target, and never returns OK.
The immediate sftool problem is that the bulk protocol does not encode or escape arbitrary firmware bytes. The precise internal Debug IP/USART1/DMAC1 behavior after the embedded frame is being reported separately to SiFli-SDK as a hardware-coexistence question: https://github.com/OpenSiFli/SiFli-SDK/issues/251
What is the expected behavior?
sftool` should be able to write arbitrary binary contents to flash, including binaries that contain complete SF32LB52 Debug IP request frames. Firmware contents must not change the control state of the flashing transport or prevent the RAM stub from receiving the declared number of bytes.
The complete target should be programmed, the stub should return OK, and optional verification/readback should match the input exactly.
What is the actual behavior?
The predecessor is written successfully. For the second file:
- The mandatory pre-write checksum comparison reports a mismatch, so downloading is required.
burn_erase_write 0x1208c000 0x00003371returnsRX_WAIT.- The host sends all 13,169 bytes.
- The stub does not print
run threadand does not returnOK. - After approximately four seconds,
sftooltimes out waiting for the RAM command response.
After a physical power cycle, readback shows that the first file was written correctly while the entire second 16 KiB target region is still erased. This places the failure after RX_WAIT but before the stub releases the received block to its flash-programming worker.
Steps to reproduce
You can try building and flashing a sample project hazardous_hello_world.zip which demonstrates this problem end-to-end in a real-world application.
For deeper troubleshooting of the sftool, you can use the provider minimal reproducer flashing_problem_sample.zip
- Download and extract the attached minimal reproducer flashing_problem_sample.zip.
It contains:
| File | Size | SHA-256 |
|---|---|---|
current-preceding-16k.bin |
16,384 | b347e0afb8b07770dc3a301bd36781164cdf2a66bbabc7b824cc6ded01fd0828 |
target-only-40-72.bin |
13,169 | a88329e1e615c04c2f2df9fe5900215b331e30c05e800ca3e039b14752d944dd |
write-only-40-72-noverify.json |
454 | configuration for both writes |
run_minimal.sh |
214 | convenience runner; adjust its sftool path and serial port |
-
Adjust the serial port in
write-only-40-72-noverify.json. If usingrun_minimal.sh, also adjust the executable and port there. -
Physically power-cycle the board into a state where
sftoolcan connect. -
Erase the full test range:
sftool -c SF32LB52 -p /dev/ttyACM0 --after no_reset \ erase_region 0x12088000:32768 -
Run the two-file write with RAM-command debug logging:
RUST_LOG=sftool_lib::common::ram_command=debug \ sftool config write-only-40-72-noverify.json -
Observe that the first write returns
OKbut the second write times out afterRX_WAITwithout printingrun thread. -
Physically power-cycle again and read back the second target:
sftool -c SF32LB52 -p /dev/ttyACM0 --after no_reset \ read_flash readback.bin@0x1208c000:16384 sha256sum readback.bin -
An entirely erased 16 KiB result has this SHA-256:
0fbba07a833d4dcfc7024eaf313661a0ba8f80a05c6d29b8801c612e10e60dee
Bug Severity
High - Cannot flash/connect to chip, major functionality broken
Hardware Setup
- SoC family: SF32LB52
- Board definition:
sf32lb52-lcd_n16r8 - Memory type: NOR
- Connection: USART1 debug/flashing port through USB-to-UART
- Serial device on the documented Linux reproduction:
/dev/ttyACM0 - Baud rate: 1,000,000
- Host operating system: Linux
sftoolversion: 0.2.5- SiFli-SDK environment used to produce the original firmware: v2.5.0
The original failure reproduced across different cables, boards, and host computers.
Installation/Build Details (for installation/build issues)
Environment Details
Error Messages and Complete Output
The final error is:
timeout while waiting for RAM command response
The important end of the observed sequence is:
burn_erase_write 0x12088000 0x00004000
RX_WAIT
... first write completes ...
OK
burn_erase_write 0x1208c000 0x00003371
RX_WAIT
... host sends 13169 bytes ...
... no "run thread" and no "OK" ...
timeout while waiting for RAM command response
Verbose/Debug Output (if available)
Attempted Solutions
- Upgraded to
sftool0.2.5: still fails. - Tested different USB cables, boards, and host computers: still fails.
- Disabled optional post-write verification: still fails during the write itself.
- Explicitly erased the entire test range before each measured attempt: still fails.
- Tested larger unrelated firmware images: they can flash successfully, excluding total size as the cause.
- Tested the original target data at a scratch address and isolated target-area writes: they can succeed, showing that content alone is insufficient and prior transaction state matters.
- Replaced the target with all zeros at the same address and length: passes and reads back exactly.
- Replaced the target's
0x40byte with zero: both writes pass. - Reduced the target from 13,169 to 13,168 bytes: the write completes, but the immediately following
burn_verifytimes out. - Split the affected image into separate 4 KiB files: flashing succeeds and can restore the board.
--compat is not considered a complete solution because source inspection shows that it sends the same bytes in 256-byte writes with delays; it does not encode the stream. Compression is likewise not a guarantee that Debug IP frames cannot occur. In sftool 0.2.5, no_compress is parsed and stored but is not consulted by the write implementation.
Working Configuration (if any)
A baseline firmware build at the same target addresses flashes successfully. It also contains an embedded EnterDebugMode frame, but its first subsequent 40 72 bytes are followed by an unaligned address and do not reproduce the failure.
For the affected firmware, splitting the main image into separate 4 KiB files in one sftool write_flash invocation has worked as a recovery method:
main=main.bin
chunks=$(mktemp -d /tmp/sftool-main-restore.XXXXXX)
split -b 4096 -d -a 4 "$main" "$chunks/chunk-"
args=()
index=0
for chunk in "$chunks"/chunk-*; do
address=$(printf '0x%x' $((0x12020000 + index * 4096)))
args+=("$chunk@$address")
index=$((index + 1))
done
sftool -c SF32LB52 -p /dev/ttyACM0 --after no_reset \
write_flash --verify "${args[@]}"
This is an empirical mitigation, not a protocol guarantee: it changes the transfer boundaries and intervening shell traffic but leaves the raw transport ambiguous.
Additional Information
Embedded Debug IP frame
At offset 0x0af0, current-preceding-16k.bin contains:
7e 79 08 00 10 00 41 54 53 46 33 32 05 21
This is byte-for-byte the documented SF32LB52 Debug IP frame generated by sftool for EnterDebugMode:
header: 7e 79 08 00 10 00
payload: 41 54 53 46 33 32 05 21
This is not the intentional EnterDebugMode used by sftool when connecting. It is a second, accidental occurrence inside the ordinary firmware payload after the RAM stub has started.
Later memory-read-shaped bytes
target-only-40-72.bin is all zero except for 40 72 at offsets 0x3369..0x336a. Its final eight bytes are:
40 72 00 00 00 00 00 00
This matches the documented SF32LB52 MEMRead payload grammar:
40 72 <address: little-endian u32> <word-count: little-endian u16>
The minimized target therefore resembles a read from address zero with word count zero. The exact undocumented hardware treatment of these headerless bytes after the accidental enter frame needs confirmation from SiFli, but the stateful byte combination and resulting write failure are reproducible.
Requested tool/stub fix
Please add an unambiguous encoded bulk-transfer protocol implemented by both sftool and the SF32LB52 RAM stub. COBS framing, SLIP/HDLC-style byte stuffing with a length and CRC, or another negotiated encoding would prevent Debug IP frame-start bytes from appearing on USART1 during bulk transfer.
The protocol should be versioned or negotiated so the host can detect whether a downloaded stub supports it. Acceptance testing should flash images deliberately containing complete EnterDebugMode, ExitDebugMode, MEMRead, and MEMWrite requests, including sequences crossing host chunk boundaries, and verify the complete flash range by CRC/readback.
Possible compatibility workaround for the existing stub
The current ram_patch_52X.bin exports:
burn_write_muti_bytes <length> <address> <hex-data>
Static analysis indicates that it accepts up to 80 decoded bytes and represents the data as ASCII hex on USART1. A deterministic fallback for the existing compiled stub could detect a file containing the Debug IP start word 7e 79, erase its destination range in a controlled step, program that affected file through ASCII-hex commands, and verify it normally. An all-hex fallback for an affected file is initially safer than attempting to enumerate only currently known complete commands or mixing raw and encoded spans.
This fallback would be slower, but it would remove the hazardous binary frame bytes from the wire instead of relying on arbitrary chunk sizes, pacing, or compression.
References
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with sftool-lib/src/common/write_flash.rs and sftool-lib/src/sf32lb52/sifli_debug.rs, then reproduce the failure with the supplied write-only-40-72-noverify.json and minimal archive. Determine how the host transfer must coordinate with the SF32LB52 RAM stub, including the existing ram_patch_52X.bin behavior. Done means arbitrary payloads, including embedded Debug IP frames and chunk-boundary crossings, flash and verify successfully.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- embedded-iot, tooling
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100