# wai-quantum-landscape-fpga An FPGA board as a member of [`wai.quantum.landscape/1`](../extensions/quantum-landscape.md): the two kernels of the energy-landscape job, the settle sweep and the sampling sweep, in programmable logic, with the host that runs jobs on the board, holds it to the conformance set and meters its power. It is a reference design: the logic, the host, the scripts and the measurements are here, under this repository's Apache-2.0. It is classical throughout. The cells are three-state variables in logic, the randomness is a counter-based generator, and nothing here is a quantum effect. Every answer says so. ## The board | | | |---|---| | board | KV260 starter kit, `SK-KV260-G` (a K26 module on a carrier) | | programmable logic | `xck26-sfvc784-2LV-c`; board part `xilinx.com:kv260_som:part0:2.0` | | processor running the host | the module's four Cortex-A53 cores, Ubuntu 24.04 from a microSD card (kernel `6.8.0-*-xilinx`) | | power sensor read | the carrier's INA260 (`hwmon` name `ina260_u14`), 10 mW steps: the board's input, so every regulator's loss is inside every figure. A sample is one read of its register, once a second, not an average over the second. | | clock | the processing system's `pl_clk0`, 100 MHz | | link to the processor | the AXI link (the default build): AXI-Lite registers on the processing system's master port `M_AXI_HPM0_FPD` at `0xA000_0000`, bulk loads read by DMA through its high-performance port `S_AXI_HP0_FPD`, and a chain sequencer in the logic; or, in the EMIO build, 95 EMIO lines of its GPIO controller. Both through `/dev/mem`. | | the two ports' widths | 128 bits, as this board's boot leaves them; the FPGA manager does not change them, so the AXI build meets them with width converters and writes no processor register. `ps-ports` reads them. | The register map is `src/regs.rs` (the engine) and `src/axi.rs` (the link). Nothing else is board-specific except `landscape.tcl` (part and board part), `src/axi.rs` and `src/emio.rs` (the link's address, the GPIO controller's), and `src/board.rs` (the FPGA manager, the clock register, the sensor's name). ## What it does One engine (`rtl/landscape_core.v`) holds up to 64 cells. Each cell's field `h_i = b_i + Σ_j W_ij s_j` is computed every clock by its own adder cone. The couplings `W` (−1, 0, +1), the fields `b` (20 bits signed), the cost `c` (20 bits) and the cut-point table are registers the host writes. | mode | what it is | |---|---| | T0 | the settle kernel, §4.1: every cell at once to `threshold(h, c)`, one sweep a clock, stopping at a fixed point, a two-cycle or the bound. Whole-number arithmetic. | | GIBBS | the sample kernel, §4.2: one cell at a time in order, each drawn given the cells before it have just moved. Four clocks a cell. | | JACOBI | every cell drawn from the same old state and moved together. **Not** a kernel of the spec: it does not sample the joint distribution. It is here only as the negative control the spec's coupled test must catch. | **The draws are the reference's.** The draw of cell `i` in sweep `k` of a chain with seed `C` is the top 53 bits of `out(C, k·n + i + 1) = mix(C + (k·n + i + 1)·0x9E3779B97F4A7C15)`. The index rises by one per cell, so the engine keeps a counter, adds the constant once a cell, and runs SplitMix64's `mix` in a nine-stage pipeline (two 64-bit products in sixteen-bit limbs, 20 DSP slices). The host writes the counter's start, `C + 0x9E3779B97F4A7C15`. **The cut points come from the host.** For each sweep's temperature the host computes `(a, b) = cuts(h, c, 1/T)` with the reference's own function, so the logic needs no exponential; a cell returns −1 if `u < a`, 0 if `u < b`, else +1. The table holds 8,192 pairs of 54 bits, as `2^(13−WBITS)` temperature levels of `2^WBITS` fields from `WIN_LO`. A field outside the window takes the threshold, which is the reference's draw only where that draw is certain, so the host places the window over every field a cell can reach whose draw is not certain at some temperature of the run, and refuses a job for which no window fits. A run longer than the table goes in segments: the host rewrites the table and starts every chain on the next segment from where it ended, its counter at the draw the reference takes next. **The member.** `src/member.rs` implements `wai_quantum::quantum_landscape::member::Member`. It is named `fpga:kv260-landscape-64`. It says nothing of its own parity: the host certifies it (`member::certify`), running the conformance set's `settle` and `chains` cases on the device and granting `bit-exact` to a kernel only if every case within its declared capacity comes back bit for bit, and every case the host generates for it: by rule, every cell count to 64 (each power of two, one less and one more), every call size to 32 chains, each band of a wavefront and a whole batch, a row of 63 couplings, fields at the wrap points of 16-bit lanes, a run past draw index 2²⁴, and a corner with every number at its most; and at each number it declares, a wave down 64 cells, fields at ±524,287 and a cost of 1,048,575 drawn hot, cold and hot then at 0, and two fields spanning 8,192 drawn at temperature 8,192. For a sampling kernel that misses, the host tries the statistical battery within it. After that, the host audits its answers (§7.3 of the extension) and demotes it at the first that differs. This host keeps the demotion in `~/.local/state/wai-quantum-landscape-fpga/demotions.json` (or the file `WAI_LANDSCAPE_DEMOTIONS` names), under the board's id and its engine's ID and version, so a demoted board is not certified again on its next job; a new engine is. It reads the file when it certifies the board, after it holds the board's lock, with every demotion kept in a file of its own beside it, and rewrites it as a demotion would; it keeps a demotion at once in a file of its own and merges it into the file under the file's own lock, waited for at most 5 s (where that runs out, the demotion stays in its own file, which every certification reads), so jobs and hosts sharing it keep one another's; a link is followed to its target, except a link in a sticky folder anyone may write. Where the file cannot be read, parsed or rewritten (a full disk, a read-only folder, a file another user owns in a sticky folder, one marked immutable), it has a second name (a hard link), the lock is held past 5 s, or `HOME` is unset and no file is named (an empty `WAI_LANDSCAPE_DEMOTIONS` names none), it says so on standard error and grades no kernel `bit-exact`. **Its declared capacity** (`member::CAPACITY`, §5 of the extension): 64 cells; above temperature 0, fields to ±524,287 and a cost to 1,048,575, its 20-bit registers, and uncertain fields within a span of 8,192 at a temperature, its table's window. At temperature 0 it takes any field (below). The host runs it on no job past those numbers, and a test holds the declared numbers to the engine's own limits, case by case over the whole set and the generated cases (`the_declared_capacity_is_exactly_what_the_engine_holds`), and on 3,000 random workloads and schedules (`the_capacity_and_the_engine_agree_on_random_jobs`). **What it cannot hold.** The conformance set holds cases past all three of its limits (settle cases of 65 to 1,000 cells, chains of 128 and 1,024 cells, sampled fields up to 2^31 − 1, a battery workload whose uncertain fields span 135,000). Those are past its declared capacity, so the host neither runs them on it nor counts them against it: on the register-level model, which the testbenches hold the logic to, the host certifies both kernels `bit-exact` within that capacity against the set as it stands (`the_model_is_certified_bit_exact_within_its_declared_capacity`), and every job's answer states the capacity. The board itself was certified `bit-exact` for both kernels under the set of 2026-11-06 (`vectors.json` sha256 `43e543a634e0ffdc...`), before the set grew; the measurements below were taken then, and it is to be certified again on the set as it stands. That set is frozen since 2026-11-10 (sha256 `1da13d2570eb5b13...`, BLAKE3 `e629cc914e93d257...`, `conformance2::VECTORS_1_BLAKE3`, which a test holds the embedded file to), and every answer's label names it as the set the member is graded on. **The first version only.** The engine reads one cost for every cell, so it runs the kernels of `wai.quantum.landscape/1` and not those of `wai.quantum.landscape/2`, whose cells each have a cost of their own ([quantum-landscape-2](../extensions/quantum-landscape-2.md) §5). The host gives it no job of the second version, placed or asked for by name, and passes it over for one, saying why. A test holds that (`the_board_is_graded_on_the_frozen_first_set_and_runs_no_second_version_job`). It runs whatever schedule the job gives, one temperature a sweep: a run of sweeps above 0 as sampled segments, a run at 0 in T0 mode, sweep `k` drawing from `k·n + 1` whatever came before it. The reader, the truth, the energy, the verdict, the checks on what a member returns, the replay digest and the seal stay on the host (`wai_quantum::quantum_landscape`), so a job's answer on the board is the spec's answer, with `member` naming the board and the receipt naming the key that sealed it. **Large fields at temperature 0.** The spec allows fields and costs far beyond the engine's 20-bit registers. At temperature 0 a cell compares its coupling sum `S`, which lies in `[−d, d]` (`d` its couplings, at most 63), with two thresholds, `c − b` and `−c − b`; only where a threshold falls in `[−d − 1, d + 1]` does its exact place matter. So for sweeps at 0 the host maps a field or cost too large for the engine to one that leaves every comparison the same, and the settle is the same bit for bit (`member::lowered_settle`; the conformance set's `fields-at-the-bound` case, fields of 2^40, reaches three of its five cases, and the crate's tests hold all five, with 300 random workloads of fields and costs up to 2^40, to the reference's settle). Sampling with such fields is refused. **Limits, and what the host refuses with a sentence:** more than 64 variables; sampling with a field beyond ±524,287 or a cost beyond 1,048,575; a job whose uncertain fields span more than 8,192. ## The link **AXI-Lite registers** (`rtl/landscape_axi.v`). Word `k` of the engine's map (`src/regs.rs`) is at `0xA000_0000 + 4k`; the link's own registers start at word `0x8000` (`src/axi.rs`). **Bulk loads by DMA.** The host writes a list of 64-bit entries, `{address, value}`, into locked pages, cleans them from its caches to DDR (the high-performance port is not coherent), and gives the link the page's physical address; the link reads the list in 16-beat bursts and applies one entry every two clocks. Couplings, fields, a whole cut-point table and a batch's chain starts all go this way. A read error stops the applying and is reported; the list is still drained. **The anneal loop in fabric.** A batch of up to 256 chains goes into the link's chain RAM (each chain's start and draw counter); the sequencer writes each into the engine, starts it, waits for it, and copies its state, sweeps and status into the result RAM, chain after chain, with nothing from the processor between them. The host fills the chain RAM by DMA. **Results by DMA.** A write master on the same port sends the result RAM to DDR, 32 bytes a chain, 128 chains a page. The host fills the page with all ones first (the illegal cell pair), cleans and invalidates it before the write-back and invalidates it after, because the port is not coherent. An error is reported, the write-back's done count proves it ran, and a result word still all ones proves it did not write there. **Safe on a shared board.** A processor access to a logic address that nothing answers never completes, and it holds the board until someone power-cycles it; another session may replace the design between two accesses. So the link puts a constant signature on eight EMIO lines, and the host reads it, through the GPIO controller, before every AXI access: a GPIO read always answers, so a replaced design is caught before an AXI access is made. And the link answers every access within a few clocks whatever the engine is doing: an engine access while the DMA or the sequencer holds the engine is dropped and counted, never queued, and the host checks the count after every batch. **One session at a time, and a reload seen.** Every board command takes one lock before it touches the board, `load` included, so a reload waits for the job that holds the board. Before a job, the host looks for another session by name (this tool, its two scripts, the vendor's loader, and what a site names in `WAI_QUANTUM_LANDSCAPE_FPGA_FOREIGN`). And a reload that comes anyway is seen: a reload of the same design passes the signature and the engine's ID, but it starts the engine's uptime counter again and resets its BOUND register, so the host checks both before it takes a workload or a table as already loaded and after every job, and refuses a result read across a reload instead of keeping it. **The EMIO build** keeps the first link: every register access a handshake over the GPIO lines. The multi-core build has only that link. ## What it costs to build | item | note | |---|---| | the board kit | `SK-KV260-G`, with its power supply. Its price is the vendor's list price; this README does not quote one. | | a microSD card | 16 GB or more, with the board's Ubuntu 24.04 image | | a network connection | the host is reached over SSH | | the vendor's FPGA tool | Vivado 2026.1, which this design was built and measured with. Its no-charge licence tier covers this part. | | a Linux x86-64 machine for that tool | ours had 15 GB of memory; the tool reported a peak of 5.1 GB for the AXI build | | a Rust toolchain | the repository's pinned version (`rust-toolchain.toml`), on an aarch64 Linux machine, or with the `aarch64-unknown-linux-gnu` target and `aarch64-linux-gnu-gcc` to cross-compile | The design uses no licensed IP: its vendor blocks are the processing system's configuration (`zynq_ultra_ps_e` 3.5) and, in the AXI build, the tool's AXI width and protocol converters; the build checks out only the tool's own synthesis and implementation features. ## Build and load ```sh # 1. the bitstream, on the tool's machine, in an empty directory: [MHz] [cores] [axi|emio] sh wai-quantum-landscape-fpga/scripts/build_bitstream.sh # the AXI build -> landscape.bit.bin, and its sha256 # 2. the host, for the board's processor sh wai-quantum-landscape-fpga/scripts/build_host.sh # -> wai-quantum-landscape-fpga (aarch64) # 3. on the board, as root ./wai-quantum-landscape-fpga load landscape.bit.bin # proves the load by its kernel-log line, then reads the engine's ID ./wai-quantum-landscape-fpga status # every board command finds the loaded link by its signature ``` If the FPGA manager's `flags` file reads non-zero (this board boots with `0x20`), a plain bitstream load needs it set to 0 first; `scripts/run_all.sh` does that and puts it back. **No bitstream is distributed here.** The tool's licence (Vivado 2026.1's end-user licence agreement, §3.1(3)C) allows a bitstream holding the vendor's components to be distributed "only for use to program a Xilinx Device", and §4(a) grants no right to use one for other devices. That is a field-of-use condition this repository's Apache-2.0 does not carry, so the build steps are given instead. The bitstreams measured below had sha256 `bb82505526e33403073924373a64d3519394344e9bf6d0193634c157e42b6dfb` (the AXI build, as these steps make it), `3d84dbd355c9552c7593d3d5aa823284a4941acd25e5f3b7c92d5c0bc36ee441` (the first AXI build, before the result write-back) and `feea5015b2f8d85049fd48bcc66493a411d551326365f67b1db2ae65ec00074c` (the EMIO build); whether a rebuild gives the same bytes has not been checked. ## Run the checks On the board, as root: ```sh ./wai-quantum-landscape-fpga conform # the conformance set, the battery and its control, the jobs, random settles, field read-back ./wai-quantum-landscape-fpga agree --seeds 200 # every worked example, board against the CPU reference ./wai-quantum-landscape-fpga joint # joint statistics against exact enumeration, at the registered seed ./wai-quantum-landscape-fpga latency # processor-to-logic latency of the loaded link ./wai-quantum-landscape-fpga throughput echo '{"program": "vars 2\noppose 0 1\nsolve\n", "seed": "7"}' | ./wai-quantum-landscape-fpga landscape run - ``` `landscape` takes the spec's JSON request and prints its answer, sealed with the key in `$WAI_QUANTUM_KEY` or `~/.config/wai/quantum-key` (made on first use; `--demo-key` and `--unsealed` as for `wai-quantum`). Anywhere, with no board: ```sh cargo run --release -- selftest # every check against the register-level model (src/model.rs) cargo run --release -- joint --model # the joint-statistics test on the model cargo run --release -- landscape run req.json --model --demo-key # a job on the model, certified and sealed sh scripts/run_tb.sh # both testbenches against the logic, in Icarus Verilog ``` The testbenches (plain Verilog) replay a session recorded from the model (`gen-tb`) against the logic and compare every read with the model's. `tb/tb_landscape.v` goes through the EMIO port and its synchroniser. `tb/tb_landscape_axi.v` drives the AXI link: register accesses over AXI-Lite, every bulk load and every batch's chain starts through the DMA from a memory model that answers with irregular delays and several bursts outstanding, every batch through the sequencer with its results read from the result RAM and then written back to the memory model, and in every batch an engine read that must come back at once, dropped and counted. `scripts/run_tb.sh` records both scripts and runs both in Icarus Verilog, and CI runs it on every change: the EMIO script, 174,345 lines and 2,668 checked reads, and the AXI script, 175,830 lines, 171 write lists, 50 batches and 4,464 checked values, both with 0 mismatches, and the vendor's simulator (xsim 2026.1; the command is in the script) gives the same on the same scripts (`measurements/2026-11-06/build.txt`). A flipped expectation is reported. `scripts/run_all.sh` does all of it on the board in order, then the power campaign. ## The power campaign `scripts/power_campaign.sh`, by the rules that follow from a whole-board sensor: - **Run minus halt on one bitstream**: no reload between the two states, so placement and clock tree cancel and the difference is the job's dynamic power. - **Randomised order** of run and halt in every pass, so drift cannot alias onto it. - **Sampling on the board, in the measuring process**, nothing else running: the processor is inside the measurement, so any login or file copy during sampling moves the number. - **Reload detection at every point**: the engine's uptime counter must keep counting, its ID must read back, and the kernel log's count of FPGA writes must not move. A flagged pass is dropped. - **Statistic**: the mean of per-pass differences of medians; error, the standard error across passes. - **Controls**: a run/halt step response for the settle time (`scripts/step_settle.py`), a null (halt against halt), the same job at half the clock (dynamic power must halve), and a known-answer vehicle where one exists. The vehicle used here is a design of the maintainers' own, not in this repository; a builder's own known answer goes in `KNOWN_ANSWER`. - **A prediction is committed before each campaign** (`measurements/*/PREDICTION*.txt`). ## What it measured Every figure: **board-level, one board, one meter, this design.** The sensor reads the whole board at its input (about 3.1 W idle), so the power figures are differences of a few percent of the total, regulator losses included. The figures in nJ and pJ are derived, not read: a mean power divided by the rate the engine's own counters give. The build sizes, the tool and block versions, the bitstreams' sha256 and the simulator results are in `measurements/2026-11-06/build.txt`. ### The builds | build | LUTs | registers | block RAM | UltraRAM | DSP | worst slack at 100 MHz | |---|---|---|---|---|---|---| | AXI link, one engine (the default) | 28,145 (24.0%) | 13,773 | 4 | 4 | 20 | +0.993 ns | | EMIO link, one engine | 25,539 (21.8%) | 11,121 | 0 | 4 | 20 | +0.294 ns | | EMIO link, four engines | 80,927 (69.1%; 95% of slices) | 44,355 | 0 | 16 | 80 | +0.441 ns | ### Checks on the board (2026-11-06) On the final AXI build and the first AXI build, the same results on each; the conformance and agreement checks also on the EMIO build, with the same results (`measurements/2026-11-06/`): - **Certified by the host: settle `bit-exact`, sample `bit-exact`, under the set of that day.** The 13 settle cases (the one with fields of 2^40 included) and the 42 chains of the `chains` cases, bit for bit, each answer also held to the job by `check_settle` and `check_chain`. Under the set as it stands now, on the model, which the testbenches hold the logic to: every case within its declared capacity bit for bit, and so `bit-exact` within it (above). - **Cut points and draws:** the 650 `cells` cut points the host computes equal the set's; 29 of those cells drawn 100,000 times each on the device, every draw the reference's and every frequency within 4 standard errors. - **The statistical battery:** all 8 workloads pass (20,000 chains each against the exact distribution), and the all-at-once control fails 7 of them. - **Jobs:** 31 of 31 requests answer as the set says, refusals included. The 13 worked examples that run, over 200 seeds each, at their own temperature and at 0, answer as the CPU reference does (26 of 26, `agree`), and the one written to be refused is refused. 2,000 random settles up to 64 cells are the reference's; 4,096 fields read back right. - **Joint statistics against exact enumeration** (pre-registered, `measurements/2026-11-06/ JOINT-PREREG.txt`, seed 2027, through the sequencer): every case passes every rule. | case | chains | bins | chi-square p | G p | cell p | pair p | control chi-square | |---|---|---|---|---|---|---|---| | ring 8, fixed T = 1.5, 4 sweeps | 200,000 | 2,930 | 0.095 | 0.037 | 1.000 | 1.000 | 244,079 on 3,247 bins, p < 1e-300 | | ring 8, the job's anneal from T = 2.5, 10 sweeps | 200,000 | 45 | 0.708 | 0.702 | 1.000 | 1.000 | 1,933,336 on 181 bins, p < 1e-300 | | dense 6, fixed T = 4, 2 sweeps | 200,000 | 729 | 0.884 | 0.870 | 1.000 | 0.047 | 91,995 on 729 bins, p < 1e-300 | | sparse 10, the job's anneal from T = 1.5, 8 sweeps | 400,000 | 190 | 0.346 | 0.167 | 1.000 | 0.773 | 14,139,391 on 1,531 bins, p < 1e-300 | Every chain's final state equals the reference's (1,000,000 of 1,000,000), so this holds the shared kernel to its exact law on the board's own outputs. The control has the exact marginals and independent cells: its per-cell test passes (p 1.000 in every case) while its joint test fails by hundreds of standard errors, which is what per-cell statistics alone would miss. ### Processor-to-logic latency (`latency`, the AXI build; the EMIO lines on the EMIO build) Each access timed alone with the processor's generic timer (10 ns a tick), `n` times; the median, the 99th percentile and the largest seen (`measurements/2026-11-06/axi-final/latency-axi.txt`, `emio-checks/latency-emio.txt`). | access | n | median | p99 | largest | |---|---|---|---|---| | EMIO register read, handshake over GPIO (EMIO build) | 5,000 | 930 ns | 940 ns | 104 µs | | EMIO register write, handshake over GPIO (EMIO build) | 5,000 | 790 ns | 800 ns | 30.5 µs | | a GPIO register read (the AXI link's signature) | 100,000 | 230 ns | 240 ns | 89.9 µs | | AXI-Lite read, a link register / an engine register | 100,000 | 230 / 240 ns | 230 / 240 ns | 19.9 / 25.7 µs | | AXI-Lite write, to its response (store, then DSB) | 100,000 | 250 ns | 380 ns | 56.8 µs | | a guarded read, as every job access goes (signature, then read) | 100,000 | 470 ns | 470 ns | 21.5 µs | | a write list by DMA, 1 entry, kick to done | 5,000 | 7.6 µs | 9.3 µs | 25.8 µs | | a write list by DMA, 512 entries (one page), kick to done | 2,000 | 20.3 µs | 23.5 µs | 54.7 µs | | a batch's results by DMA, 128 chains (one page), kick to read | 2,000 | 37.6 µs | 47.9 µs | 49.7 µs | Posted AXI-Lite writes, back to back with nothing between them, came to 51 ns each: one loop of 100,000 timed whole, a mean. A DMA list's entries cost (20.3 − 7.6) µs / 511, about 25 ns each, beyond the fixed cost of a kick; whole, a 512-entry list comes to 39.7 ns an entry. End to end, each timed once over the whole run (the table loads: the median of 6): | | EMIO, register accesses | AXI, register accesses | AXI, DMA, sequencer and write-back | |---|---|---|---| | a full cut-point table (64 levels × 128 fields, 32,835 writes) | 29.0 ms | 17.5 ms | 3.81 ms | | chains of 8 cells × 4 sweeps | 14.6 µs a chain (4,096) | 9.4 µs a chain (16,384) | 4.34 µs a chain (65,536; 230,000 a second) | Of a chain's 4.34 µs, the engine is busy for 139 clocks, 1.39 µs, by its own count (the crate's tests hold the model to that count, and the testbenches hold the logic to the model). The sequencer's state machine adds about 33 clocks around it (`rtl/landscape_axi.v`: 12 to write the chain in, 18 to copy its result out, and the start). The rest is the host's lists, kicks and polls, and the DMA's transfers. ### Power Each class by run minus halt on one bitstream, the pre-registered statistic, all predictions committed first (`measurements/2026-11-06/`). The classes ran on the EMIO build (12:39 to 14:55 UTC); the AXI builds, with the same engine, were checked against them. | measurement | build | result | prediction | | |---|---|---|---|---| | settle, dense 64-cell recall, 12.5 M settles a second | EMIO | 162.0 ± 3.3 mW (10 passes): 13.0 nJ a settle, 0.88 pJ an accumulate | 160 (130 to 200) | within | | sequential sampling, 64 cells, 24.9 M cell draws a second | EMIO | 11.0 ± 1.8 mW (10 passes): 0.44 ± 0.07 nJ a cell draw, within the instrument's floor, not relied on | 12 (4 to 30) | within; see the null | | the idle loop, control and counters only | EMIO | 1.9 ± 4.0 mW (8 passes) | 4 (0 to 12) | within | | the settle job at 50 MHz | EMIO | 80.0 ± 2.6 mW: 0.494 ± 0.019 of 100 MHz | 0.5 within 2 SE | passes | | null, halt against halt | EMIO | 8.1 ± 1.9 mW (8 passes) | \|Δ\| < 2 SE | **fails** | | null, repeated as pre-registered (`NULL-REPEAT.txt`) | AXI, first | −1.9 ± 2.7 mW (8 passes) | \|Δ\| < 2 SE | passes | | known-answer vehicle (the 2026-09 design), at 08:11 UTC, before the class campaign | its own | 73.8 ± 10.3 mW (8 passes; one pass's own spread 29 mW) | 61.7 ± 6.5, within 2 combined σ | passes, weakly: 37 to 86 mW would have passed | | settle job, the AXI build's check | AXI, first | 158.3 ± 4.4 mW (3 passes) | the EMIO result within 2 combined SE | passes | | settle job, the final build's check | AXI, final | 166.7 ± 6.7 mW (3 passes) | the EMIO result within 2 combined SE | passes | The board drew about 3.14 W halted and 3.30 W running the settle job. **One of the two nulls failed** (8.1 ± 1.9 mW, no job in either slot, and not following which slot came first), so the instrument's floor is not shown to be below about 8 mW: the sampling and idle figures, 11 and 2 mW, are within it and are reported, not relied on. The settle figures are twenty times that floor. The class campaign's first attempt (08:01 UTC) measured the step and the known answer, then stopped: the session check mistook its own parent process for another session; that is fixed (`src/board.rs`). The class campaign itself ran without the known answer, so its own instrument checks are the null that failed and the clock-scaling check that passed. During it a status read over SSH touched the board once, at about 13:25 UTC, during the settle class. **The step response** (`emio-classes/step.dat`, 300 s each way, no settle): the level is flat from the first 15 seconds on, 3,300 mW running and 3,140 mW halted (`step_settle.py`: every 15-sample median within 20 mW of the final level), so the 60 s settle is enough. Single samples stray all through both segments, the last 299 s in: spikes of the sensor or the processor, which the statistic's medians do not follow. **The first version of this design** (2026-11-05, fields of 11 bits, a 128-bit xoroshiro register for draws of its own, 16-bit cut points; sampling `statistical`, not bit-exact) measured, with its known-answer control at 70.4 ± 7.5 mW against 61.7 ± 6.5 expected and a null of −1.2 ± 2.3 mW (`measurements/2026-11-05/`): | job | one core | four cores | |---|---|---| | settle, dense 64-cell recall | 146.7 ± 2.0 mW: 11.7 nJ a settle, about 0.80 pJ an accumulate | 585.8 ± 1.8 mW, 3.99 ± 0.06 times one core | | sequential sampling, 8 sweeps at T = 0.25 | 6.7 ± 1.8 mW: 0.20 ± 0.05 nJ a cell draw | 42.1 ± 1.9 mW: 0.32 ± 0.01 nJ a cell draw, 2.0 σ from the one-core figure | Neither is the 0.16 pJ an accumulate of the spec's price table, which comes from a different design with its couplings fixed in the logic. The second version of the protocol prices a run at temperature 0 at this member's settle figure, about 0.88 pJ an accumulate, with this section as its evidence, and prices no run above it: the sampling figure above is not relied on ([quantum-landscape-2](../extensions/quantum-landscape-2.md) §4.3). ## What is not done, and what came out worse - **Bitstream reproducibility** is unchecked, and no bitstream is published (above). - **No hardware-in-the-loop CI.** The board has a lock between jobs (`src/board.rs`) but no networked power relay, so a runner cannot power-cycle it. - **One board, one meter.** No second unit has repeated these numbers, and the sensor reads the whole board, not the logic's supply. - **Four cores fit, only on the EMIO link, and the host does not use them as four.** It addresses one core at a time or all at once (the same job on each). The AXI link drives one engine. - **The guard leaves a window.** The host reads the signature and then makes the AXI access, usually under half a microsecond later; the longest gap seen was 21.5 µs, and preemption can stretch it without bound. A reload by another session landing inside that window would still hang the board. The lock keeps this tool's own sessions out of it; another tool's reload is not excluded. - **The DMA's pages are locked, not pinned.** The kernel may still move a locked page (compaction and contiguous allocation do). The host reads each page's physical address before every transfer and again after, and refuses the transfer if it moved, but a page moved during a write-back has had its 4 KiB written where the page used to be. A buffer the kernel owns (a reserved region, a CMA or dma-heap buffer behind a driver) would rule this out; this host has none. - **The port widths are the boot's.** The build assumes the processing system's master port and its high-performance port at 128 bits, as this board's boot sets them; the host reads both and refuses otherwise, and does not set them. Another boot image may need another build. - **Certified within a capacity, not beyond it.** 64 cells, sampled fields of ±524,287 and a cost of 1,048,575, and a window of 8,192 fields a temperature: its grade holds within those, and the host runs it on no job past them. A host offered other members gives such a job to the next; this board's own host, with one member, refuses it, saying which number it is past. At temperature 0 large fields are mapped exactly; more cells would need the engine to tile its cells, wider sampling fields wider registers, and a wider spread of uncertain fields more than one window. - **The joint test's histogram is the reference's.** The board is bit-exact, so the test holds the shared kernel to exact enumeration on the board's outputs, not an independent sampler. - **A cell update takes four clocks**, and in sequential mode one of 64 cones is used at a time. - **The AXI build reports one critical warning**: a false-path constraint written for the EMIO port's synchroniser finds no such cell in it. It constrains nothing there. - **A rebuild is not checked against the measured one.** The build stops on a tool other than 2026.1 and takes each block at the version it was measured with, but whether the same steps give the same bytes is unknown. A rebuild with a different sha256 is accepted by its own checks on the board: `conform`, `agree --seeds 200` and `joint` (seed 2027), reported with the tool version and the sha256. ## Files | | | |---|---| | `rtl/landscape_core.v` | the engine: cones, the T0, GIBBS and JACOBI modes, the draw pipeline, the table, counters | | `rtl/landscape_axi.v`, `rtl/landscape_top_axi.v` | the AXI link (AXI-Lite port, write-list DMA, chain sequencer) and its top level | | `rtl/landscape_port.v`, `rtl/landscape_top.v` | the EMIO port and its top level | | `rtl/landscape_multi.v`, `rtl/landscape_top_multi.v` | several engines behind one EMIO port | | `landscape.tcl`, `rtl/landscape.xdc` | the build (`LINK axi` or `emio`), and the constraints on the EMIO crossing | | `src/` | the host: register maps, the links (`axi.rs`, `emio.rs`), driver, member, model, checks, the joint-statistics test (`exact.rs`), latency (`latency.rs`), power meter, command line | | `tb/tb_landscape.v`, `tb/tb_landscape_axi.v` | the testbenches, for the EMIO port and for the AXI link; `scripts/run_tb.sh` runs both | | `scripts/` | build, run-everything, power campaign, and the two analysis scripts | | `measurements/` | each campaign's prediction and raw logs | Where the design came from: [PROVENANCE.md](PROVENANCE.md).