summaryrefslogtreecommitdiff
path: root/experiments/length-RFast-grapheme.csv
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-26 09:56:28 -0300
committerGabriel Schneider <[email protected]>2026-08-26 09:56:28 -0300
commit55743cc5d564f6ef6d6f8a0eb1a614a74b021d3a (patch)
tree2d98cba30a8620605babb791e95dc0a381d4384c /experiments/length-RFast-grapheme.csv
parented8c5632e0228b1b821c87b511beb474c6a41f0c (diff)
downloadesp32p4-55743cc5d564f6ef6d6f8a0eb1a614a74b021d3a.tar.gz
esp32p4-55743cc5d564f6ef6d6f8a0eb1a614a74b021d3a.zip
A test suite that runs on the die, and two build steps for reading an image
## zig build selftest Seventeen checks, on the board, chosen by one rule: a check belongs there only if the die can answer it and a host cannot. Every serious bug this port has produced was invisible to a host test. `std.mem.eql` compares a byte at a time on this target, so the firmware's largest read was three times slower than it needed to be and nothing on a laptop could tell. A lone ESC resolves to the Escape key, which is right when a kernel hands over a whole sequence and wrong when a 115200 line hands over one byte every 87 us. A full transmit FIFO stopped anything draining the receiver, and the FIFO depth is a hardware number. So: the volatile promise (two reads of a live counter are two reads, which is what `Peek` rests on), word-wise equality checked against `std.mem.eql` itself at every difference position and both alignments, the input rescue against a fake port with a FIFO that loses what arrives into a full one, the allocator on real L2MEM, and the cycle counter against the systimer - which is clocked from the crystal and therefore cannot be flattered by a wrong CPU divider. Anything that is pure logic stays in `zig build test`, which is faster and needs no hardware. Duplicating those here would make the suite longer and no stronger. Not a `zig test` binary, deliberately: Zig's runner wants an OS and `std.testing.allocator` is a debug allocator over the page allocator, which on freestanding is either a compile error or a lie. The harness is thirty lines. `selftest` has its own application, image and flash chain so it is one command with no flags to remember, and it makes the board's verdict the build's - a suite whose result a human has to read out of a scrolling log is a suite that gets ignored on the first busy afternoon. Proven both ways: 17/17 with exit 0, and exit 1 naming the check when one is deliberately inverted. The clock check earned its place immediately. The first version read `config.cpu_mhz` and compared the die against it without ever performing the raise, so `-Dcpu-mhz=360` failed with `khz=90001 want=360000`. The check was right and the expectation was wrong; it now calls `setCpuFreq` itself, which makes it a test of the raise rather than a tautology. 90001 kHz at 90, 360004 at 360. ## zig build layout, and a loader error that explains itself Both of these exist because of an hour I spent that they would have saved. `NotTwoMappedSegments` said the count was wrong and nothing about what the segments were, which is the only thing that says which section grew, shrank or stopped being emitted. I diagnosed one by hand with readelf on an artifact that turned out to be a stale install, then guessing at the linker script. The image step now prints the segment table with the error - it has to happen there, because a rejected image is never written, so no later step can show it - and `zig build layout` prints the same table for an ELF plus the loader's verdict as TEXT, which `size` cannot do because it reads a finished image and the moment you need the table is when there isn't one. They immediately paid for themselves. The reason the suite would not build was that it had no `_start`: `-fentry=_start` found no such symbol, `--gc-sections` discarded every function as unreachable, and `.flash.text` was empty. `layout` prints `entry 0x0` for exactly that, in one line. The second failure - a silent board - was a missing app descriptor, which is the same class of thing and now has a comment where it happened. ## The panic handler already existed, and was printing past the end of its message `msg` is a Zig slice and `%s` reads until a NUL, so handing `msg.ptr` to the ROM's printf printed the message and then whatever followed it in memory. String literals get away with it; std's own panics do not, because they are formatted into a buffer - "index out of bounds: index 5, len 3" - and carry no terminator. It now goes out through `uart.write`, which takes a length, with the fault address after it so addr2line can find the line.
Diffstat (limited to 'experiments/length-RFast-grapheme.csv')
0 files changed, 0 insertions, 0 deletions