journal 2026-05-05

P75 AtomVM Tetris

P75 turns the P74 host-input bridge into a small real game. The host viewer is intentionally dumb: it sends key events and displays frames. Tetris runs in AtomVM Erlang on the simulated chip.

The path now looks like this:

pygame key event
  -> captured/input.fifo
  -> Verilator harness
  -> host_input_valid/data
  -> 16-entry RTL MMIO FIFO
  -> chip:input/0
  -> {input, Event}
  -> Erlang Tetris process
  -> chip:draw_tetris_frame/9 framebuffer rendering

The first P75 smoke exposed a useful bug. The RTL status register changed from a single valid bit to a count field, but the C NIF was still checking only bit 0. Six scripted events gave a FIFO count of 6, whose low bit is zero, so chip:input/0 reported no input. The fix was to test whether the low five-bit count is nonzero.

Verification:

cd projects/75_atomvm_tetris/test
make all
make verilator-run-input-smoke

The passing smoke delivered six host events, rendered a Tetris frame, consumed the scripted quit, and reported FIFO status 0.

The first live game was playable but slow enough to hit the 120M-cycle budget after about 78 frames. The renderer was crossing from Erlang into C repeatedly for every block rectangle. Moving the hot path into one coarse chip:draw_tetris_frame/9 NIF made the same live path quit cleanly at 71,030,364 post-load cycles after 138 rendered frames and 106 input events.

There was one hang-looking failure during that cleanup: the coarse renderer was too strict about piece coordinates and could return badarg during a transient off-board state. The final NIF clamps framebuffer writes and treats malformed/off-board cells as empty, so the render path does not crash the Erlang game process.

Honest status: RTL simulation only. F/D compliance was NOT RUN and LibreLane hardening was NOT RUN.