Where this comes from
While building tests/rig_ip65_rrnet_hw.py (the first ip65 hardware rig) I measured a sporadic single-byte corruption in the harness's bulk machine:writemem POST path — filed as c64-test-harness#231. That prompted the obvious question: has any hardware result this project has produced been running silently corrupted code?
I checked, and I want the answer written down, including the part that says "no".
What I found
The measured defect does not touch any existing rig. Two separate reasons:
- Every UCI rig loads its PRG with
client.run_prg(prg) — POST /v1/runners:run_prg, a different endpoint from machine:writemem. My measurement says nothing about that path, in either direction.
- Every
transport.write_memory() call in tools/uci/ is small: 64-byte trampoline slices (rig_https_bad_finished.py, rig_https_local.py), 32-byte host strings, 48-byte scalars and 1-4 byte sentinels (bench_ecdsa_u64e.py). All far under the 2,048 bytes the harness docstring says the POST form was verified at, and far under the ~2.7-3.2 kB offsets where I saw corruption.
But nothing verifies. No rig in tools/uci/ or tests/ reads the loaded image back and compares it to the file. So the class of fault is undetectable by our tooling, whatever transfer path it might arrive on. If a run_prg upload ever dropped or flipped a byte, the failure mode is:
a one-byte change inside 6502 code surfaces minutes or tens of minutes later as a crash, a hang, or a failed handshake — and gets attributed to the code under test.
That is this project's single most-recorded misdiagnosis shape (the …KEYS ENC1 RX screen with two unrelated causes, the "U64E firmware wedged" conclusions that were nothing of the kind, the NO_SOCKET ladder). It costs one read to exclude.
Proposal
Add a post-load verify to the rigs that load a PRG, along the lines of what tests/rig_ip65_rrnet_hw.py::load_prg_verified already does:
- read the loaded span back and compare to the file, before the program is started or exercised;
- report the first differing offset, not just "mismatch";
- for images that reach
$A000, verify only up to $9FFF and say so — the tail is BSS zero fill and a host read of it returns the BASIC ROM until the program banks it out (check_shadow_ram_readable in tools/ip65_hw_checks.py is the discriminator);
- allow one retry, then fail rather than start a run on an image known to be wrong.
run_prg complicates this slightly: it loads and starts, so a verify would race the running program's own writes. Verifying the code/rodata span immediately after, or moving to a write-then-SYS load, are both options; that design choice is the substance of this issue.
Confidence
Honest limits: n=2 on one device (U64E, fw v3.15-78-g71480a9d) for the corruption itself, and zero observations of a corrupted run_prg load. I am not claiming any past result is wrong. I am claiming we could not tell, and that the check is cheap.
One result that is explicitly not affected, so nobody re-opens it: the issue #181 hardware validation never loaded a PRG at all — bench_ecdsa_u64e.py exited 4 at the REU preflight, before run_prg, and the master-vs-branch comparison was pure Python against live firmware responses. That result stands.
Out of scope for the RR-Net PR; filed separately on purpose.
Where this comes from
While building
tests/rig_ip65_rrnet_hw.py(the first ip65 hardware rig) I measured a sporadic single-byte corruption in the harness's bulkmachine:writememPOST path — filed as c64-test-harness#231. That prompted the obvious question: has any hardware result this project has produced been running silently corrupted code?I checked, and I want the answer written down, including the part that says "no".
What I found
The measured defect does not touch any existing rig. Two separate reasons:
client.run_prg(prg)—POST /v1/runners:run_prg, a different endpoint frommachine:writemem. My measurement says nothing about that path, in either direction.transport.write_memory()call intools/uci/is small: 64-byte trampoline slices (rig_https_bad_finished.py,rig_https_local.py), 32-byte host strings, 48-byte scalars and 1-4 byte sentinels (bench_ecdsa_u64e.py). All far under the 2,048 bytes the harness docstring says the POST form was verified at, and far under the ~2.7-3.2 kB offsets where I saw corruption.But nothing verifies. No rig in
tools/uci/ortests/reads the loaded image back and compares it to the file. So the class of fault is undetectable by our tooling, whatever transfer path it might arrive on. If arun_prgupload ever dropped or flipped a byte, the failure mode is:That is this project's single most-recorded misdiagnosis shape (the
…KEYS ENC1 RXscreen with two unrelated causes, the "U64E firmware wedged" conclusions that were nothing of the kind, the NO_SOCKET ladder). It costs one read to exclude.Proposal
Add a post-load verify to the rigs that load a PRG, along the lines of what
tests/rig_ip65_rrnet_hw.py::load_prg_verifiedalready does:$A000, verify only up to$9FFFand say so — the tail is BSS zero fill and a host read of it returns the BASIC ROM until the program banks it out (check_shadow_ram_readableintools/ip65_hw_checks.pyis the discriminator);run_prgcomplicates this slightly: it loads and starts, so a verify would race the running program's own writes. Verifying the code/rodata span immediately after, or moving to a write-then-SYSload, are both options; that design choice is the substance of this issue.Confidence
Honest limits: n=2 on one device (U64E, fw
v3.15-78-g71480a9d) for the corruption itself, and zero observations of a corruptedrun_prgload. I am not claiming any past result is wrong. I am claiming we could not tell, and that the check is cheap.One result that is explicitly not affected, so nobody re-opens it: the issue #181 hardware validation never loaded a PRG at all —
bench_ecdsa_u64e.pyexited 4 at the REU preflight, beforerun_prg, and the master-vs-branch comparison was pure Python against live firmware responses. That result stands.Out of scope for the RR-Net PR; filed separately on purpose.