T27.AI

Blog

A WASM interpreter ran on the board: 134 of 134, from a spec

2026-10-12 · 4 min read

[the programs live in a ROM built from the spec; the wire carries requests, not .wasm binaries; one board, one night, six runs] A t27 spec in gHashTag/t27 (branch wasm-board, 351e3cdf4) became a Verilog core through t27c gen-verilog, passed through openXC7 onto an ALINX AX7203, and answered 134 of 134 requests exactly -- six runs in a row, 804 of 804. 21 opcodes of the WASM MVP set, a 14-program 101-byte ROM, i32.mul as a 32-clock shift-add microsequence; timing went from 18.57 to 70.41 MHz against a 68.7 bar at one percent of the LUTs -- every frequency here is a nextpnr estimate, not sign-off timing. The self-tested conformance harness caught a wire-offset bug that would have failed 76 checks against a perfectly working board.

Title card, no illustration yet, for: A WASM interpreter ran on the board: 134 of 134, from a spec
View the title card at full size
#t27#FPGA#WASM

Overnight on 12 October one t27 spec became a Verilog core, openXC7 placed it on an ALINX AX7203 (Artix-7 XC7A200T-2), and the chip answered every request exactly: 134 of 134, six runs in a row, 804 of 804 answers bit for bit. This post is the receipt trail -- what runs, what it took, and what it is not.

The bench session: flash, self-test, run

Recorded on the bench, 12 October 2026, 04:14 UTC: five commands, all exit 0, 32.5 seconds. The page with the player is at t27.ai/term/wasm-board-ax7203/. Open the recording on its own page.

What the silicon runs

The core on the chip is fpga_wasm_board, generated by t27c gen-verilog from specs/fpga/wasm_board_ax7203.t27 in gHashTag/t27 (branch wasm-board, commit 351e3cdf4). It speaks UART at 1,144,744 baud, parses AA 55 frames, and interprets 21 opcodes of the WebAssembly MVP set. Fourteen programs -- 101 bytes of them -- sit in a ROM built from the same spec; the wire carries requests and answers, not binaries.

The bring-up top -- STARTUPE2 for the configuration clock, a three-flop UART synchroniser, the LEDs -- is hand-written Verilog (fpga/vivado/wasm_board_ax7203.v), under the owner-approved-foreign exception in that repository. Everything that computes is generated from the spec; everything that only brings the chip up is not.

Timing: a nextpnr estimate, 18.57 to 70.41 MHz

The toolchain is openXC7: yosys with -flatten -abc9 -nocarry -nodsp -nosrl, then nextpnr (heap placer, router1, seed 1). The first cut was a nextpnr estimate of 18.57 MHz. Rewriting i32.mul into a 32-clock shift-add microsequence took the estimate to 70.41 MHz against a 68.7 MHz bar. The core costs 3,910 of 269,200 LUTs on the XC7A200T -- one percent -- and 1,127 flip-flops. The local build takes about 65 seconds without Docker, and the bitstream is 9,730,755 bytes.

The self-test caught the first bug, and it was ours

Before any hardware run, the conformance harness in trinity-fpga checks a model of the board against a golden interpreter -- an independent WASM MVP interpreter with its own opcode table and a general signed-LEB128 decoder, sharing no code with the spec. Fifteen pin cross-checks tie the spec port list to the wires.

The harness self-test injects six faults -- a lie, a bad opcode, a sequence break, a dropped frame, a bit slip, the wrong program -- and proves every check fails on its own fault. That is what caught the real bug before the board did: X_OFF and Y_OFF in the spec are offsets into the frame body, but on the wire x sits at byte six, two sync bytes in. Read the offsets the wrong way and 76 of 134 checks fail -- against a board that is working perfectly. The self-test turned that into a fix made before the first flash.

On the board

openFPGALoader flashes the bitstream over the FT232H cable in about 15 seconds, with no sudo, and reports done. Then the board answered 134 of 134 requests exactly -- six consecutive runs, 804 of 804 answers, zero mismatches, zero lost frames, 416 answers per second at the pace of the link. The vector set is pinned by hash:

89527478cf813f99b9e76fce77f1b846b5f3ddbf22cb212f08b5ac0eec108f8f
WhatResult
Timing (nextpnr estimate)estimate 70.41 MHz against a 68.7 bar; 18.57 before the i32.mul microsequence
Area on XC7A200T3,910 of 269,200 LUTs (1%); 1,127 FFs
Board runs6 x 134/134 exact = 804/804; 416 answers/s
BuildopenXC7, local, ~65 s, no Docker; bitstream 9,730,755 bytes

The neighbourhood

Software interpreters -- wasm3, WAMR, wasmi -- run WASM on CPUs and run it well; WASM in hardware is a much thinner file. A small academic set of hand-written WASM-on-FPGA designs appeared through 2024-2026, and eBPF offload on smart NICs plus the old Java-in-silicon line (picoJava, JOP) are the precedent that a bytecode ISA can be a hardware target at all. What is different here is not speed -- none is claimed against any of them -- but provenance and order of work: the computing core is generated from a spec, and the conformance harness, with its self-tested fault injection, was written and passing before the first bitstream touched the board.

What this is not

Epic trinity-fpga#917 -- AI-generated FPGA hardware that executes WASM -- has its MVP closed on silicon. Next: widen the opcode set, load programs over the wire, and shrink the hand-written top toward zero.

What this does not settle

Receipts

Work with me

Need an FPGA/RTL problem taken to measured hardware?

I work contract and part-time on hardware-AI, FPGA/RTL and ML systems — from specification and open toolchains to reproducible measurements.