wai-quantum-landscape-fpga: an FPGA board as an open reference member of wai.quantum.landscape/1 #677

Merged
dcharlot merged 16 commits from wai/quantum-landscape-fpga into main 2026-11-07 03:46:41 -05:00
92 changed files with 19928 additions and 2 deletions

View file

@ -0,0 +1,146 @@
name: wai-quantum-landscape-fpga reference design
# The FPGA board's member of energy-landscape jobs (wai/wai-quantum-landscape-fpga): its unit tests and
# clippy, warnings as errors; the member certified on the model of the logic (src/model.rs) and every
# check of the conformance set, every worked example and the joint-statistics test run on it; the
# board's code built and tested on the board's processor's architecture (aarch64, linux-arm64),
# where its cache maintenance compiles;
# and the logic itself: both testbenches replay the model's session against the RTL in an
# open-source simulator, every read checked. No board and no vendor tool here: the board's own runs
# are in the crate's measurements and README.
#
# Push filters match the push's delta, so every path the jobs read is listed.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-quantum-landscape-fpga/**'
- 'wai/wai-quantum/src/quantum_landscape/**'
- 'wai/wai-quantum/src/quantum_landscape.rs'
- 'wai/wai-quantum/src/repro.rs'
- 'wai/wai-quantum/Cargo.toml'
- 'wai/quantum-conformance/landscape/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-landscape-fpga.yml'
pull_request:
paths:
- 'wai/wai-quantum-landscape-fpga/**'
- 'wai/wai-quantum/src/quantum_landscape/**'
- 'wai/wai-quantum/src/quantum_landscape.rs'
- 'wai/wai-quantum/src/repro.rs'
- 'wai/wai-quantum/Cargo.toml'
- 'wai/quantum-conformance/landscape/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-landscape-fpga.yml'
jobs:
relevant:
name: does this change touch it
runs-on: gate
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-landscape-fpga.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-quantum-landscape-fpga: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
model:
name: the member against the model of the logic, and the logic against the model · linux, release
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.99.1 and the simulator
shell: bash
run: |
export PATH="$HOME/.cargo/bin:$PATH"
command -v rustup >/dev/null 2>&1 || \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.99.1
rustup toolchain install 1.99.1 --profile minimal --component clippy
rustup default 1.99.1
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
command -v iverilog >/dev/null || { $SUDO apt-get update -qq; $SUDO apt-get install -y -qq iverilog; }
- name: Unit tests and clippy
shell: bash
working-directory: wai/wai-quantum-landscape-fpga
run: |
set -euo pipefail
cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
cargo clippy --locked --release --all-targets -- -D warnings
# Every case the engine holds must give the reference's bits and every one it cannot must be
# refused (the checks); the host's certification is printed, whatever it is: under the set
# as it stands, cases beyond the engine's 64 cells cost it its certificate (README).
- name: The member certified, the conformance set and the worked examples, on the model
shell: bash
working-directory: wai/wai-quantum-landscape-fpga
run: |
set -euo pipefail
cargo run --locked --release -- selftest --scale 0.05 --seeds 5 | tee "$RUNNER_TEMP/selftest.log"
grep -q 'certified by the host on the conformance set: settle ' "$RUNNER_TEMP/selftest.log"
grep -qE '^settle cases the engine holds: ([0-9]+)/\1 bit for bit' "$RUNNER_TEMP/selftest.log"
grep -qE '^chains the engine holds: ([0-9]+)/\1 bit for bit' "$RUNNER_TEMP/selftest.log"
grep -qE '^[0-9]+ checks, 0 failed$' "$RUNNER_TEMP/selftest.log"
- name: A job on the model, certified and sealed, with the reference's answer
shell: bash
working-directory: wai/wai-quantum-landscape-fpga
run: |
set -euo pipefail
printf '{"program": "vars 2\\noppose 0 1\\nsolve\\n", "seed": "7"}' | cargo run --locked --release -- landscape run - --model --demo-key > "$RUNNER_TEMP/job.json"
grep -q '"id": "fpga:model-landscape-64"' "$RUNNER_TEMP/job.json"
grep -q '"state": "-+"' "$RUNNER_TEMP/job.json"
- name: Joint statistics against exact enumeration, on the model
shell: bash
working-directory: wai/wai-quantum-landscape-fpga
run: |
set -euo pipefail
cargo run --locked --release -- joint --model --scale 0.05 | tee "$RUNNER_TEMP/joint.log"
grep -qE '^[0-9]+ checks, 0 failed$' "$RUNNER_TEMP/joint.log"
- name: The logic against the model, both testbenches
shell: bash
working-directory: wai/wai-quantum-landscape-fpga
run: sh scripts/run_tb.sh both
board-target:
name: the board's code on the board's processor's architecture · linux-arm64, release
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux-arm64
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.99.1
shell: bash
run: |
export PATH="$HOME/.cargo/bin:$PATH"
command -v rustup >/dev/null 2>&1 || \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.99.1
rustup toolchain install 1.99.1 --profile minimal --component clippy
rustup default 1.99.1
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
# The cache maintenance the DMA needs (dc cvac, dc civac, dsb) compiles only for aarch64.
- name: Unit tests and clippy
shell: bash
working-directory: wai/wai-quantum-landscape-fpga
run: |
set -euo pipefail
cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
cargo clippy --locked --release --all-targets -- -D warnings

View file

@ -31,7 +31,9 @@
# CI runs it so on every change, a pull request included.
#
# Scope: the tracked files under wai/ with the extensions .md .rs .toml .py
# .sh .mjs .js .json .html .css .ttl .h .c .txt, and wai/'s .gitignore files;
# .sh .mjs .js .json .html .css .ttl .h .c .txt .wgsl, the logic's sources and
# build files (.v .tcl .xdc) and the measurements' logs (.dat), and wai/'s
# .gitignore files;
# or, when files are given as arguments, those files (the self-test,
# scripts/test-public-text-checks.sh, uses this), each a path relative to the
# directory the script is run from, or absolute. No file is excluded: the
@ -116,7 +118,7 @@ else
files+=("$f")
done < <(git ls-files 'wai/*.md' 'wai/*.rs' 'wai/*.toml' 'wai/*.py' 'wai/*.sh' 'wai/*.mjs' 'wai/*.js' \
'wai/*.json' 'wai/*.html' 'wai/*.css' 'wai/*.ttl' 'wai/*.h' 'wai/*.c' 'wai/*.txt' \
'wai/*.gitignore' 'wai/*.wgsl')
'wai/*.gitignore' 'wai/*.wgsl' 'wai/*.v' 'wai/*.tcl' 'wai/*.xdc' 'wai/*.dat')
fi
if [ ${#files[@]} -eq 0 ]; then
echo "::error::no file to check" >&2

View file

@ -0,0 +1,11 @@
### wai-quantum-landscape-fpga: an FPGA board as a member of energy-landscape jobs
- **`wai-quantum-landscape-fpga`** is a reference design: the settle and sample kernels of `wai.quantum.landscape/1` in programmable logic for up to 64 cells (`rtl/`), the register-level model the logic is tested against, a testbench that replays the model's session against the logic, and the host that runs jobs on the board as a `Member`, holds it to the conformance set and meters its power by run minus halt. The board, its part and its sensor are named as data in its README.
- **The sample kernel takes the reference's draws**: the logic counts and mixes them as SplitMix64 does and compares 53 bits against cut points the host computes with the reference's `cuts`, so it needs no exponential. On the board both kernels were certified bit-exact against the `settle` and `chains` vectors of 2026-11-06, and every worked example over 200 seeds answers as the CPU reference does. The set has since grown past what the engine holds (64 cells, 20-bit sampled fields, a window of 8,192 fields), and a member may not decline those cases, so under the current set the host certifies both kernels `uncertified`; every case the engine holds still gives the reference's bits.
- **An AXI link** (`rtl/landscape_axi.v`) replaces the EMIO register port as the default build: the engine's registers on AXI-Lite, bulk loads read from DDR by a write-list DMA, a sequencer that runs a job's chains in fabric, start to result, and a write-back that puts the results in DDR. The link answers every access within a few clocks whatever the engine is doing, and the host reads a signature over EMIO before every AXI access, so a design replaced by another session is caught before an access could go unanswered. A second testbench drives the link and its DMA against the model. The processor-to-logic latency is measured on the board (`latency`).
- **The sample kernel is held to its exact joint law** (`joint`, `src/exact.rs`): small workloads enumerated over every state, chains on the board against that law by chi-square and G over all states, every cell's and every pair's frequencies, and a control with the right marginals and independent cells that must be rejected.
- **The member is certified by the host** on the conformance set and the statistical battery, runs any schedule of temperatures the job gives, and maps fields and costs beyond its registers exactly for sweeps at 0.
- **One session at a time, and a reload seen:** every board command, `load` included, takes the board's lock first, and the session check sees other runs of this tool. A reload of the same design under a job, which passes the signature and the engine's ID, is seen by the engine's uptime counter and its reset registers, and the job's result is refused instead of kept.
- **CI** builds and tests the host on x86-64 and on aarch64, the board's architecture, certifies the member on the model, and replays both testbenches against the logic in an open-source simulator. `scripts/check-public-names.sh` now also reads `.v`, `.tcl`, `.xdc` and `.dat` files.
- **The measurements** are board-level, one board and one meter, each campaign with its prediction committed first (`measurements/`). No bitstream is distributed: the vendor tool's licence limits a bitstream to that vendor's devices, so the README gives the build steps.
- The design came from a private repository and is released under `Apache-2.0` by its copyright holder (`wai-quantum-landscape-fpga/PROVENANCE.md`).

459
wai/wai-quantum-landscape-fpga/Cargo.lock generated Normal file
View file

@ -0,0 +1,459 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "arrayvec"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "base64ct"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "blake3"
version = "1.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae"
dependencies = [
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"cpufeatures 0.3.1",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cc"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375989decec1a0fa424442bed520fa12ede43a48475ce8c7417d2c69b32b5f6b"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "constant_time_eq"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
"rustc_version",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "der"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"zeroize",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
dependencies = [
"curve25519-dalek",
"ed25519",
"serde",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "equivalent"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
[[package]]
name = "fiat-crypto"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "find-msvc-tools"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "indexmap"
version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "libc"
version = "0.2.191"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.9",
]
[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
"foldhash",
"indexmap",
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"rand_core",
]
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wai-quantum"
version = "0.8.1"
dependencies = [
"blake3",
"ed25519-dalek",
"serde",
"serde_json",
]
[[package]]
name = "wai-quantum-landscape-fpga"
version = "0.1.0"
dependencies = [
"blake3",
"serde_json",
"wai-quantum",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "zeroize"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879"
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"

View file

@ -0,0 +1,19 @@
[package]
name = "wai-quantum-landscape-fpga"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
license = "Apache-2.0"
publish = false
description = "An FPGA board as a member of wai.quantum.landscape/1: the two kernels of the energy-landscape job (the settle sweep and the sampling sweep) in programmable logic, the register-level model the logic is tested against, and the host that runs jobs on the board, holds it to the conformance set and meters its power."
repository = "https://git.transaction.science/Transaction-Science/open-standards"
[dependencies]
# The job form, the reader, the reference kernels, the answer and its seal: the board is a member,
# so everything but the kernels stays in the reference crate.
wai-quantum = { version = "0.8.1", path = "../wai-quantum", default-features = false, features = ["quantum_landscape", "quantum_engine", "quantum_receipt"] }
serde_json = { version = "1", features = ["preserve_order"] }
blake3 = "1"
[profile.release]
opt-level = 3

View file

@ -0,0 +1,42 @@
# Where this design came from
The engine's logic, its register-level model, the host and the power-measurement scripts were
written in a private repository, where the first version of the design was built and measured on
the board, at commit `24bb5c8e8032e95a614a9db1e6f2e98ac7ea1233`. There the host crate's manifest
declared `CC0-1.0`, and the logic, the build script and the shell scripts carried no licence. They
are released here under this repository's `Apache-2.0`, by their copyright holder's decision of
2026-11-05. This repository's `LICENSE` governs everything under this directory. The logic, the
testbenches, the build script, the constraints and the scripts carry
`SPDX-License-Identifier: Apache-2.0`; the Rust sources, as elsewhere in this repository, carry no
header and are covered by `LICENSE` and by `license = "Apache-2.0"` in `Cargo.toml`.
## What changed from that code
- **The draws.** The first version drew its own randomness from a 128-bit xoroshiro128+ register and
compared 16 bits against 16-bit cut points, so its sampling was statistical: right in distribution,
different in every bit. Here the engine computes the reference's draws, SplitMix64 over a counter, and
compares 53 bits against 54-bit cut points the host computes with the reference's `cuts`, so its
sampling kernel gives the reference's bits.
- **The table window.** The first version held fields −64 to 63 and refused a job whose uncertain
fields fell outside. Here the window's start and width are registers, and the host places them.
- **Fields and cost** widen from 11 and 10 bits to 20, and a cell update takes four clocks, not three.
- **The job form.** The first host read a request of `key=value` lines and printed a replay line of its
own. Here the host is a `Member` of `wai_quantum::quantum_landscape`, so a job is the spec's JSON
request, its answer the spec's answer, sealed, and the starts, default temperature, limits and
refusals are the reference's, because they are the reference's code.
- **The link.** The first version had only the EMIO port. The AXI link (`rtl/landscape_axi.v`,
`src/axi.rs`), its testbench, the joint-statistics test (`src/exact.rs`) and the latency
measurement (`src/latency.rs`) were written here, after the release.
- **The member's contract.** The host certifies the member and gives it a schedule of temperatures,
as the spec's member trait now says; the first host stated its own parity and took a start
temperature and a sweep count.
- **Names.** Modules, files and the engine's ID (`LAND`) are named for what they do.
## What is not here
No bitstream (README, "Build and load"). Nor the known-answer vehicle the power campaigns checked
the meter with: a design of the maintainers' own from 2026-09 and the script that runs it, both in
the private repository. A builder uses a known answer of their own (`KNOWN_ANSWER` in
`scripts/power_campaign.sh`) or none; the null and the clock-scaling check need nothing else. The first version's raw power logs are in
`measurements/2026-11-05/` as they were written, under that version's tool name; its predictions are
the files committed before that measurement, copied unchanged.

View file

@ -0,0 +1,418 @@
# 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 it takes comes back bit for bit (and, for a
sampling kernel that misses, trying the statistical battery). It declines only what the spec lets a
member decline (§5: sampling fields above 2^31 − 1).
**What it cannot hold, and what that costs.** The engine has 64 cells, 20-bit field and cost
registers for sampling, and a table window of 8,192 fields a temperature. The conformance set as it
stands now holds cases past all three (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), and
the spec does not let a member decline them. The board refuses each with a sentence, never with
wrong bits, and every case it can hold it gives bit for bit (`conform`). But a refused case is a
missed vector, so the host certifies both kernels `uncertified` under the current set, and a job
on the board says so. The board 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. 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 the engine can
hold, 21 settle cases and 227 chains, bit for bit, and 13 cases refused as beyond it, so
`uncertified` (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.
## 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.
- **Not certified under the current set.** 64 cells, sampled fields of ±524,287 and a cost of
1,048,575, and a window of 8,192 fields a temperature: the set now holds cases past each, which a
member may not decline, so both kernels come out `uncertified` (above). 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).

View file

@ -0,0 +1,121 @@
# SPDX-License-Identifier: Apache-2.0
# Bitstream build for the energy-landscape engine. Vivado batch mode:
# vivado -mode batch -source landscape.tcl -tclargs [FREQ <MHz>] [TAG <name>] [JOBS <n>] [CORES <k>] [LINK emio|axi]
# The part and board are data: PART and BOARD below. There is no programmable-logic I/O: the
# processing system provides the clock and the link. LINK axi (the default on one engine) gives an
# AXI-Lite register port, a write-list DMA from DDR and an in-fabric chain sequencer
# (rtl/landscape_axi.v); LINK emio carries register accesses over the EMIO GPIO lines, and is the
# only link of the multi-core build. Every block below is the vendor's no-charge processing-system
# configuration or AXI infrastructure; no licensed IP. The tool and block versions are the ones this
# design was built and measured with, and a different tool stops the build: set ANY_TOOL to go on,
# and expect a different bitstream.
set part xck26-sfvc784-2LV-c
set board xilinx.com:kv260_som:part0:2.0
proc arg {name dflt} {
global argv
set i [lsearch $argv $name]
if {$i >= 0 && [llength $argv] > $i + 1} { return [lindex $argv [expr {$i + 1}]] }
return $dflt
}
set freq [arg FREQ 100]
set tag [arg TAG landscape]
set jobs [arg JOBS 4]
set cores [arg CORES 1]
set link [arg LINK [expr {$cores > 1 ? "emio" : "axi"}]]
if {$cores > 1 && $link ne "emio"} { error "the multi-core build has the EMIO link only" }
set tool 2026.1
if {[version -short] ne $tool && ![info exists ::env(ANY_TOOL)]} {
error "this design was built and measured with Vivado $tool, not [version -short]: set ANY_TOOL to build anyway"
}
# Each block by its exact version.
proc block {name version} {
set d [get_ipdefs -all xilinx.com:ip:$name:$version]
if {$d eq ""} { error "no $name $version in this tool" }
return [lindex $d 0]
}
set here [file dirname [file normalize [info script]]]
puts "BUILD: tag=$tag freq=$freq jobs=$jobs cores=$cores link=$link here=$here"
create_project ev_$tag ./ev_$tag -part $part -force
set_property board_part $board [current_project]
create_bd_design sys
create_bd_cell -type ip -vlnv [block zynq_ultra_ps_e 3.5] ps
apply_bd_automation -rule xilinx.com:bd_rule:zynq_ultra_ps_e -config {apply_board_preset 1} [get_bd_cells ps]
set_property -dict [list \
CONFIG.PSU__FPGA_PL0_ENABLE {1} \
CONFIG.PSU__CRL_APB__PL0_REF_CTRL__FREQMHZ $freq \
CONFIG.PSU__USE__M_AXI_GP0 {0} CONFIG.PSU__USE__M_AXI_GP1 {0} CONFIG.PSU__USE__M_AXI_GP2 {0} \
CONFIG.PSU__GPIO_EMIO__PERIPHERAL__ENABLE {1} \
CONFIG.PSU__GPIO_EMIO__PERIPHERAL__IO {95} \
] [get_bd_cells ps]
create_bd_port -dir O -from 94 -to 0 emio_o
create_bd_port -dir I -from 94 -to 0 emio_i
connect_bd_net [get_bd_pins ps/emio_gpio_o] [get_bd_ports emio_o]
connect_bd_net [get_bd_pins ps/emio_gpio_i] [get_bd_ports emio_i]
create_bd_port -dir O -type clk pl_clk0
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_ports pl_clk0]
if {$link eq "axi"} {
# The two port widths are the ones this board's boot leaves set (src/axi.rs, ps_ports): loading
# a bitstream through the FPGA manager does not change them, so the design meets them as they
# are and writes no processor register. M_AXI_HPM0_FPD (128-bit) -> width converter -> protocol
# converter -> LITE (32-bit) at 0xA000_0000, 256 KiB. DMA (AXI4, 64-bit) -> width converter ->
# S_AXI_HP0_FPD (128-bit) -> DDR. The reset is the fabric's own (aresetn).
set_property -dict [list CONFIG.PSU__USE__M_AXI_GP0 {1} CONFIG.PSU__MAXIGP0__DATA_WIDTH {128} \
CONFIG.PSU__USE__S_AXI_GP2 {1} CONFIG.PSU__SAXIGP2__DATA_WIDTH {128}] [get_bd_cells ps]
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins ps/maxihpm0_fpd_aclk]
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins ps/saxihp0_fpd_aclk]
create_bd_port -dir I -type rst aresetn
set dw [block axi_dwidth_converter 2.1]
create_bd_cell -type ip -vlnv $dw down
create_bd_cell -type ip -vlnv $dw up
set_property -dict [list CONFIG.SI_DATA_WIDTH {128} CONFIG.MI_DATA_WIDTH {32}] [get_bd_cells down]
set_property -dict [list CONFIG.SI_DATA_WIDTH {64} CONFIG.MI_DATA_WIDTH {128}] [get_bd_cells up]
create_bd_cell -type ip -vlnv [block axi_protocol_converter 2.1] conv
set_property -dict [list CONFIG.SI_PROTOCOL {AXI4} CONFIG.MI_PROTOCOL {AXI4LITE} CONFIG.DATA_WIDTH {32}] [get_bd_cells conv]
foreach c {down up} {
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins $c/s_axi_aclk]
connect_bd_net [get_bd_ports aresetn] [get_bd_pins $c/s_axi_aresetn]
}
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins conv/aclk]
connect_bd_net [get_bd_ports aresetn] [get_bd_pins conv/aresetn]
connect_bd_intf_net [get_bd_intf_pins ps/M_AXI_HPM0_FPD] [get_bd_intf_pins down/S_AXI]
connect_bd_intf_net [get_bd_intf_pins down/M_AXI] [get_bd_intf_pins conv/S_AXI]
create_bd_intf_port -mode Master -vlnv xilinx.com:interface:aximm_rtl:1.0 LITE
set_property -dict [list CONFIG.PROTOCOL {AXI4LITE} CONFIG.ADDR_WIDTH {32} CONFIG.DATA_WIDTH {32}] [get_bd_intf_ports LITE]
connect_bd_intf_net [get_bd_intf_pins conv/M_AXI] [get_bd_intf_ports LITE]
create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:aximm_rtl:1.0 DMA
set_property -dict [list CONFIG.PROTOCOL {AXI4} CONFIG.ADDR_WIDTH {49} CONFIG.DATA_WIDTH {64} CONFIG.ID_WIDTH {0} \
CONFIG.HAS_REGION {0} CONFIG.HAS_QOS {0} CONFIG.HAS_LOCK {0}] [get_bd_intf_ports DMA]
connect_bd_intf_net [get_bd_intf_ports DMA] [get_bd_intf_pins up/S_AXI]
connect_bd_intf_net [get_bd_intf_pins up/M_AXI] [get_bd_intf_pins ps/S_AXI_HP0_FPD]
set_property CONFIG.ASSOCIATED_BUSIF {LITE:DMA} [get_bd_ports pl_clk0]
set_property CONFIG.ASSOCIATED_RESET {aresetn} [get_bd_ports pl_clk0]
assign_bd_address
set lite [get_bd_addr_segs -of_objects [get_bd_addr_spaces ps/Data] -filter {NAME =~ *LITE*}]
set_property offset 0xA0000000 $lite
set_property range 256K $lite
foreach seg [get_bd_addr_segs -of_objects [get_bd_addr_spaces ps/Data]] { puts "ADDR $seg [get_property offset $seg] [get_property range $seg]" }
}
validate_bd_design
make_wrapper -files [get_files sys.bd] -top -import
generate_target all [get_files sys.bd]
if {$cores > 1} {
add_files [list $here/rtl/landscape_top_multi.v $here/rtl/landscape_multi.v $here/rtl/landscape_core.v]
set_property verilog_define [list LANDSCAPE_K=$cores] [current_fileset]
} elseif {$link eq "axi"} {
add_files [list $here/rtl/landscape_top_axi.v $here/rtl/landscape_axi.v $here/rtl/landscape_core.v]
} else {
add_files [list $here/rtl/landscape_top.v $here/rtl/landscape_port.v $here/rtl/landscape_core.v]
}
add_files -fileset constrs_1 $here/rtl/landscape.xdc
set_property top design_top [current_fileset]
update_compile_order -fileset sources_1
launch_runs impl_1 -to_step write_bitstream -jobs $jobs
wait_on_run impl_1
puts "IMPL_STATUS: [get_property STATUS [get_runs impl_1]] PROGRESS: [get_property PROGRESS [get_runs impl_1]]"
open_run impl_1
report_utilization -file ev_$tag.util.rpt
report_timing_summary -max_paths 5 -file ev_$tag.timing.rpt
puts "BITSTREAM: [glob -nocomplain ./ev_$tag/ev_$tag.runs/impl_1/*.bit]"

View file

@ -0,0 +1,54 @@
PRE-REGISTERED, 2026-11-05, before the vQPU-EDGE bitstream had been loaded on the board, and before
any power was read from it. Written from the RTL, the software model's activity counts
(`vqpu-edge activity`) and the board's own earlier measurements. Not edited after the data.
WHAT IS MEASURED
Run minus halt on ONE bitstream (kv260/edge, 100 MHz pl_clk0), whole-board INA260, by the board's
written rules: no reload between states, randomised order each pass, sampling on the board in the
measuring process with nothing else running, reload detection per point (engine uptime counter must
keep counting; dmesg FPGA-write count must not move; ID must read back), pass dropped if flagged,
statistic = mean of per-pass differences of medians, error = SE across passes. Label of every
number: board-level, this KV260, this design. It is NOT the 0.1596 pJ/accumulate of the old
constant-folded vehicle and must not be quoted as that: this lowering keeps W in flops and adds a
product LUT, a compressor and a runtime-theta comparator to every cone, so it has its own rate.
THE INSTRUMENT CHECKS, which gate everything else
K1 known answer: the September vehicle ev_rdet.bit.bin, run/halt by its own script kv_runhalt.sh,
must reproduce 61.7 mW (its 2026-09-28 run/halt value, +/- 6.5). PASS if within 2 combined sigma.
If it fails, no number below is reported as measured.
K2 null: on the vQPU-EDGE bitstream, halt against halt (one slot labelled run): 0 mW.
PASS if |delta| < 2 SE.
PREDICTIONS (point value, and the range outside which I am wrong)
Reasoning: the old det vehicle drew 61.7 mW at 8.68 state flips per clock, i.e. ~71 pJ per flip with
its constant-folded cones. This lowering has ~3x the LUTs per cone (product + compressor + tree +
comparators), and a flip drives all 64 cones, so I take 2.5x per flip, uncertain by 2x either way.
class what repeats flips/clock predicted P_dyn range
t0-recall dense Hebbian n=64 (8 patterns), T=0 settle 6.88 122 mW 60..250
from a 30%-corrupted cue, 5 steps, reload
gibbs-recall same landscape, 8 sequential Gibbs sweeps at 0.028 5 mW 0..20
beta=4 + one T=0 step, reload (1539 clocks/job)
- cones barely move; what is left is PRNG, table
read, field mux and counters
solve-assign the 3x3 equality landscape (n=9), 39-sweep anneal ~0.1 3 mW 0..15
from T=1, final T=0 step; 55 of the 64 cones have
W=0 and cannot switch
t0-idle W=0, bound 0: the job FSM looping with a static 0 1 mW 0..10
datapath (control and counters only)
Derived, for t0-recall: 12.5 M settles/s at 8 clocks each => ~10 nJ per settle; with its accumulate
count (5 steps x nnz) that is about 0.5 pJ per accumulate, range 0.25..1.0. I expect it to be WORSE
than the old 0.16 pJ/accumulate, because the couplings are now data, not wiring.
WHAT WOULD REFUTE THE REASONING
- t0-recall outside 60..250 mW: the per-flip cost of this lowering is not 1-5x the old one.
- gibbs-recall above 20 mW: something other than state flips (the field mux, the table, the PRNG)
costs far more than the toggle study's numbers say.
- t0-idle above 10 mW: control is not negligible, and every per-job figure carries it.
WHAT THIS RUN CANNOT RESOLVE (said now, so a null is not read as a finding)
gibbs-recall, solve-assign and t0-idle are predicted at or below the protocol's floor (paired SE
~2-7 mW at 8-12 passes). A result consistent with 0 there means "below what one core on this
meter resolves", not "free". A second meter on the PL rail (or K replicated cores) is what would
resolve them.

View file

@ -0,0 +1,23 @@
PRE-REGISTERED, 2026-11-05, written while the single-core campaign was still running on the board
and before any of its numbers had been read, and before the four-core bitstream was ever loaded.
WHY. The single-core prediction put gibbs-recall at ~5 mW, at or below the paired protocol's
floor. The notes' own lesson (FEASIBILITY_parity_2026-10-04.txt): amplify the effect, don't average
the noise. rtl/edge_multi.v puts four engines behind one port; `vqpu-edge meter <class> ... --cores 4`
runs the same job on all four at once (BroadcastPort), so the effect is ~4x while the sensor quantum
and drift stay put.
WHAT IS MEASURED. The same run/halt protocol as the single-core campaign, on the four-core
bitstream, 100 MHz, classes t0-recall and gibbs-recall.
PREDICTIONS
1. Scaling: P_dyn(4 cores) / P_dyn(1 core, this day's campaign) = 4.0, PASS range 3.4 .. 4.6 for
t0-recall. Outside it, cores do not add independently (shared clock tree, placement density,
rail), and the per-core numbers from the four-core build must not be divided by four.
2. t0-recall, four cores: 4 x 122 = 488 mW (range 240 .. 1000), i.e. four times the single-core
prediction, whatever the single-core measurement turns out to be.
3. gibbs-recall, four cores: 4 x 5 = 20 mW (range 0 .. 80). If the single-core run could not
resolve it, this is the number that can: at 20 mW and SE ~6 mW it is ~3 sigma.
LABEL. Board-level, this KV260, this (four-core) design. Per-core figures from it are the four-core
value divided by four only if prediction 1 passes.

View file

@ -0,0 +1,16 @@
# 2026-11-05: the first version of the design
Raw logs from the first version (draws of its own, 16-bit cut points, fields of 11 bits), as the
meter wrote them, under that version's tool name. `PREDICTION.txt` and `PREDICTION_4core.txt` are the
predictions committed before each measurement, copied unchanged.
| file | what |
|---|---|
| `step.dat` | the run/halt step response, 300 s each way |
| `k1_rdet.dat` | the known-answer vehicle (an earlier design measured in September at 61.7 ± 6.5 mW), by its own script |
| `null.dat` | halt against halt |
| `t0-recall.dat`, `t0-recall-50MHz.dat` | the settle job at 100 and 50 MHz |
| `gibbs-recall.dat`, `t0-idle.dat`, `solve-assign.dat` | the sampling job, the idle loop, a small annealing job |
| `t0-recall-4core.dat`, `gibbs-recall-4core.dat` | the same jobs on four cores at once |
Summarise any of them with `wai-quantum-landscape-fpga analyze <file>` or `python3 scripts/power_tables.py <files>`.

View file

@ -0,0 +1,754 @@
# class gibbs-recall cores 4
# vqpu-edge meter passes=12 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793922349, tv_nsec: 240818196 }
# pass state sample power_uW
1 run 1 3290000
1 run 2 3540000
1 run 3 3290000
1 run 4 3310000
1 run 5 3300000
1 run 6 3280000
1 run 7 3700000
1 run 8 3290000
1 run 9 3280000
1 run 10 3290000
1 run 11 3280000
1 run 12 3280000
1 run 13 3300000
1 run 14 3280000
1 run 15 3280000
1 run 16 3280000
1 run 17 3280000
1 run 18 3290000
1 run 19 3280000
1 run 20 3270000
1 run 21 3280000
1 run 22 3290000
1 run 23 3290000
1 run 24 3290000
1 run 25 3280000
1 run 26 3280000
1 run 27 3290000
1 run 28 3290000
1 run 29 3280000
1 run 30 3290000
# point pass=1 state=run jobs_per_s=64976.0 updates_per_s=33267709.4 clock_hz=99998038
1 halt 1 3260000
1 halt 2 3240000
1 halt 3 3240000
1 halt 4 3250000
1 halt 5 3240000
1 halt 6 3240000
1 halt 7 3250000
1 halt 8 3250000
1 halt 9 3240000
1 halt 10 3240000
1 halt 11 3240000
1 halt 12 3240000
1 halt 13 3240000
1 halt 14 3240000
1 halt 15 3240000
1 halt 16 3250000
1 halt 17 3240000
1 halt 18 3240000
1 halt 19 3240000
1 halt 20 3240000
1 halt 21 3250000
1 halt 22 3240000
1 halt 23 3240000
1 halt 24 3260000
1 halt 25 3260000
1 halt 26 3240000
1 halt 27 3240000
1 halt 28 3260000
1 halt 29 3240000
1 halt 30 3240000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998032
2 run 1 3290000
2 run 2 3290000
2 run 3 3290000
2 run 4 3280000
2 run 5 3280000
2 run 6 3270000
2 run 7 3270000
2 run 8 3290000
2 run 9 3280000
2 run 10 3280000
2 run 11 3280000
2 run 12 3280000
2 run 13 3300000
2 run 14 3280000
2 run 15 3280000
2 run 16 3280000
2 run 17 3290000
2 run 18 3280000
2 run 19 3280000
2 run 20 3280000
2 run 21 3290000
2 run 22 3290000
2 run 23 3280000
2 run 24 3270000
2 run 25 3280000
2 run 26 3280000
2 run 27 3280000
2 run 28 3270000
2 run 29 3270000
2 run 30 3280000
# point pass=2 state=run jobs_per_s=64976.0 updates_per_s=33267706.0 clock_hz=99998028
2 halt 1 3250000
2 halt 2 3240000
2 halt 3 3240000
2 halt 4 3240000
2 halt 5 3240000
2 halt 6 3240000
2 halt 7 3240000
2 halt 8 3240000
2 halt 9 3260000
2 halt 10 3260000
2 halt 11 3240000
2 halt 12 3240000
2 halt 13 3260000
2 halt 14 3240000
2 halt 15 3250000
2 halt 16 3250000
2 halt 17 3240000
2 halt 18 3250000
2 halt 19 3240000
2 halt 20 3240000
2 halt 21 3240000
2 halt 22 3240000
2 halt 23 3240000
2 halt 24 3240000
2 halt 25 3240000
2 halt 26 3250000
2 halt 27 3240000
2 halt 28 3240000
2 halt 29 3240000
2 halt 30 3240000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998024
3 run 1 3290000
3 run 2 3280000
3 run 3 3280000
3 run 4 3280000
3 run 5 3290000
3 run 6 3300000
3 run 7 3290000
3 run 8 3290000
3 run 9 3290000
3 run 10 3290000
3 run 11 3290000
3 run 12 3280000
3 run 13 3300000
3 run 14 3290000
3 run 15 3310000
3 run 16 3290000
3 run 17 3290000
3 run 18 3290000
3 run 19 3290000
3 run 20 3290000
3 run 21 3290000
3 run 22 3290000
3 run 23 3280000
3 run 24 3280000
3 run 25 3300000
3 run 26 3290000
3 run 27 3290000
3 run 28 3290000
3 run 29 3290000
3 run 30 3300000
# point pass=3 state=run jobs_per_s=64976.0 updates_per_s=33267703.0 clock_hz=99998019
3 halt 1 3250000
3 halt 2 3260000
3 halt 3 3270000
3 halt 4 3260000
3 halt 5 3270000
3 halt 6 3260000
3 halt 7 3250000
3 halt 8 3250000
3 halt 9 3260000
3 halt 10 3250000
3 halt 11 3250000
3 halt 12 3250000
3 halt 13 3250000
3 halt 14 3250000
3 halt 15 3250000
3 halt 16 3250000
3 halt 17 3250000
3 halt 18 3270000
3 halt 19 3260000
3 halt 20 3250000
3 halt 21 3240000
3 halt 22 3250000
3 halt 23 3250000
3 halt 24 3250000
3 halt 25 3250000
3 halt 26 3280000
3 halt 27 3270000
3 halt 28 3240000
3 halt 29 3250000
3 halt 30 3250000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998016
4 halt 1 3250000
4 halt 2 3260000
4 halt 3 3260000
4 halt 4 3250000
4 halt 5 3250000
4 halt 6 3250000
4 halt 7 3280000
4 halt 8 3250000
4 halt 9 3260000
4 halt 10 3270000
4 halt 11 3280000
4 halt 12 3260000
4 halt 13 3250000
4 halt 14 3260000
4 halt 15 3250000
4 halt 16 3250000
4 halt 17 3250000
4 halt 18 3260000
4 halt 19 3270000
4 halt 20 3260000
4 halt 21 3250000
4 halt 22 3250000
4 halt 23 3280000
4 halt 24 3260000
4 halt 25 3250000
4 halt 26 3250000
4 halt 27 3250000
4 halt 28 3250000
4 halt 29 3250000
4 halt 30 3270000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998014
4 run 1 3290000
4 run 2 3290000
4 run 3 3280000
4 run 4 3280000
4 run 5 3300000
4 run 6 3280000
4 run 7 3290000
4 run 8 3280000
4 run 9 3290000
4 run 10 3290000
4 run 11 3290000
4 run 12 3280000
4 run 13 3280000
4 run 14 3280000
4 run 15 3290000
4 run 16 3290000
4 run 17 3290000
4 run 18 3280000
4 run 19 3290000
4 run 20 3300000
4 run 21 3280000
4 run 22 3290000
4 run 23 3280000
4 run 24 3290000
4 run 25 3280000
4 run 26 3290000
4 run 27 3280000
4 run 28 3300000
4 run 29 3290000
4 run 30 3300000
# point pass=4 state=run jobs_per_s=64976.0 updates_per_s=33267700.6 clock_hz=99998011
5 halt 1 3250000
5 halt 2 3250000
5 halt 3 3250000
5 halt 4 3240000
5 halt 5 3240000
5 halt 6 3250000
5 halt 7 3250000
5 halt 8 3260000
5 halt 9 3250000
5 halt 10 3240000
5 halt 11 3240000
5 halt 12 3250000
5 halt 13 3240000
5 halt 14 3250000
5 halt 15 3250000
5 halt 16 3260000
5 halt 17 3260000
5 halt 18 3250000
5 halt 19 3250000
5 halt 20 3240000
5 halt 21 3240000
5 halt 22 3240000
5 halt 23 3250000
5 halt 24 3240000
5 halt 25 3260000
5 halt 26 3240000
5 halt 27 3250000
5 halt 28 3250000
5 halt 29 3250000
5 halt 30 3240000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998011
5 run 1 3280000
5 run 2 3300000
5 run 3 3290000
5 run 4 3290000
5 run 5 3340000
5 run 6 3320000
5 run 7 3290000
5 run 8 3290000
5 run 9 3290000
5 run 10 3290000
5 run 11 3290000
5 run 12 3290000
5 run 13 3320000
5 run 14 3300000
5 run 15 3290000
5 run 16 3290000
5 run 17 3290000
5 run 18 3280000
5 run 19 3290000
5 run 20 3300000
5 run 21 3290000
5 run 22 3290000
5 run 23 3320000
5 run 24 3290000
5 run 25 3290000
5 run 26 3290000
5 run 27 3290000
5 run 28 3300000
5 run 29 3280000
5 run 30 3290000
# point pass=5 state=run jobs_per_s=64976.0 updates_per_s=33267699.6 clock_hz=99998008
6 run 1 3300000
6 run 2 3300000
6 run 3 3290000
6 run 4 3290000
6 run 5 3280000
6 run 6 3280000
6 run 7 3280000
6 run 8 3280000
6 run 9 3280000
6 run 10 3300000
6 run 11 3280000
6 run 12 3280000
6 run 13 3280000
6 run 14 3280000
6 run 15 3280000
6 run 16 3280000
6 run 17 3280000
6 run 18 3280000
6 run 19 3290000
6 run 20 3280000
6 run 21 3280000
6 run 22 3290000
6 run 23 3280000
6 run 24 3290000
6 run 25 3310000
6 run 26 3290000
6 run 27 3290000
6 run 28 3290000
6 run 29 3280000
6 run 30 3280000
# point pass=6 state=run jobs_per_s=64976.0 updates_per_s=33267699.2 clock_hz=99998007
6 halt 1 3230000
6 halt 2 3240000
6 halt 3 3240000
6 halt 4 3250000
6 halt 5 3240000
6 halt 6 3240000
6 halt 7 3250000
6 halt 8 3240000
6 halt 9 3240000
6 halt 10 3250000
6 halt 11 3250000
6 halt 12 3250000
6 halt 13 3240000
6 halt 14 3240000
6 halt 15 3340000
6 halt 16 3250000
6 halt 17 3240000
6 halt 18 3240000
6 halt 19 3250000
6 halt 20 3580000
6 halt 21 3250000
6 halt 22 3260000
6 halt 23 3240000
6 halt 24 3250000
6 halt 25 3240000
6 halt 26 3240000
6 halt 27 3240000
6 halt 28 3250000
6 halt 29 3240000
6 halt 30 3250000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998006
7 halt 1 3250000
7 halt 2 3240000
7 halt 3 3240000
7 halt 4 3250000
7 halt 5 3240000
7 halt 6 3240000
7 halt 7 3240000
7 halt 8 3240000
7 halt 9 3240000
7 halt 10 3250000
7 halt 11 3240000
7 halt 12 3250000
7 halt 13 3690000
7 halt 14 3240000
7 halt 15 3240000
7 halt 16 3250000
7 halt 17 3250000
7 halt 18 3260000
7 halt 19 3260000
7 halt 20 3240000
7 halt 21 3240000
7 halt 22 3250000
7 halt 23 3240000
7 halt 24 3240000
7 halt 25 3240000
7 halt 26 3240000
7 halt 27 3240000
7 halt 28 3240000
7 halt 29 3260000
7 halt 30 3240000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997761
7 run 1 3290000
7 run 2 3280000
7 run 3 3280000
7 run 4 3290000
7 run 5 3280000
7 run 6 3280000
7 run 7 3290000
7 run 8 3280000
7 run 9 3280000
7 run 10 3290000
7 run 11 3280000
7 run 12 3280000
7 run 13 3290000
7 run 14 3290000
7 run 15 3290000
7 run 16 3290000
7 run 17 3290000
7 run 18 3300000
7 run 19 3300000
7 run 20 3290000
7 run 21 3290000
7 run 22 3280000
7 run 23 3280000
7 run 24 3290000
7 run 25 3280000
7 run 26 3280000
7 run 27 3290000
7 run 28 3280000
7 run 29 3280000
7 run 30 3290000
# point pass=7 state=run jobs_per_s=64975.8 updates_per_s=33267628.3 clock_hz=99997794
8 run 1 3280000
8 run 2 3290000
8 run 3 3280000
8 run 4 3280000
8 run 5 3280000
8 run 6 3280000
8 run 7 3300000
8 run 8 3280000
8 run 9 3290000
8 run 10 3280000
8 run 11 3320000
8 run 12 3290000
8 run 13 3290000
8 run 14 3280000
8 run 15 3310000
8 run 16 3290000
8 run 17 3280000
8 run 18 3270000
8 run 19 3290000
8 run 20 3290000
8 run 21 3270000
8 run 22 3290000
8 run 23 3280000
8 run 24 3290000
8 run 25 3280000
8 run 26 3280000
8 run 27 3290000
8 run 28 3280000
8 run 29 3290000
8 run 30 3310000
# point pass=8 state=run jobs_per_s=64975.8 updates_per_s=33267638.1 clock_hz=99997823
8 halt 1 3250000
8 halt 2 3240000
8 halt 3 3260000
8 halt 4 3260000
8 halt 5 3240000
8 halt 6 3240000
8 halt 7 3240000
8 halt 8 3250000
8 halt 9 3240000
8 halt 10 3240000
8 halt 11 3250000
8 halt 12 3260000
8 halt 13 3240000
8 halt 14 3250000
8 halt 15 3230000
8 halt 16 3240000
8 halt 17 3250000
8 halt 18 3250000
8 halt 19 3250000
8 halt 20 3250000
8 halt 21 3240000
8 halt 22 3260000
8 halt 23 3250000
8 halt 24 3240000
8 halt 25 3250000
8 halt 26 3240000
8 halt 27 3260000
8 halt 28 3250000
8 halt 29 3250000
8 halt 30 3250000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997846
9 halt 1 3250000
9 halt 2 3240000
9 halt 3 3240000
9 halt 4 3250000
9 halt 5 3240000
9 halt 6 3250000
9 halt 7 3250000
9 halt 8 3240000
9 halt 9 3240000
9 halt 10 3240000
9 halt 11 3250000
9 halt 12 3240000
9 halt 13 3240000
9 halt 14 3250000
9 halt 15 3250000
9 halt 16 3240000
9 halt 17 3240000
9 halt 18 3240000
9 halt 19 3240000
9 halt 20 3240000
9 halt 21 3240000
9 halt 22 3240000
9 halt 23 3250000
9 halt 24 3260000
9 halt 25 3240000
9 halt 26 3240000
9 halt 27 3240000
9 halt 28 3240000
9 halt 29 3240000
9 halt 30 3240000
# point pass=9 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997867
9 run 1 3280000
9 run 2 3290000
9 run 3 3290000
9 run 4 3290000
9 run 5 3290000
9 run 6 3300000
9 run 7 3290000
9 run 8 3290000
9 run 9 3290000
9 run 10 3290000
9 run 11 3290000
9 run 12 3310000
9 run 13 3290000
9 run 14 3290000
9 run 15 3290000
9 run 16 3290000
9 run 17 3290000
9 run 18 3280000
9 run 19 3300000
9 run 20 3310000
9 run 21 3300000
9 run 22 3290000
9 run 23 3280000
9 run 24 3290000
9 run 25 3280000
9 run 26 3290000
9 run 27 3290000
9 run 28 3290000
9 run 29 3300000
9 run 30 3290000
# point pass=9 state=run jobs_per_s=64975.9 updates_per_s=33267658.0 clock_hz=99997883
10 halt 1 3250000
10 halt 2 3250000
10 halt 3 3250000
10 halt 4 3240000
10 halt 5 3250000
10 halt 6 3240000
10 halt 7 3240000
10 halt 8 3250000
10 halt 9 3260000
10 halt 10 3240000
10 halt 11 3260000
10 halt 12 3250000
10 halt 13 3240000
10 halt 14 3240000
10 halt 15 3240000
10 halt 16 3250000
10 halt 17 3250000
10 halt 18 3240000
10 halt 19 3250000
10 halt 20 3250000
10 halt 21 3240000
10 halt 22 3240000
10 halt 23 3240000
10 halt 24 3250000
10 halt 25 3230000
10 halt 26 3230000
10 halt 27 3240000
10 halt 28 3240000
10 halt 29 3240000
10 halt 30 3240000
# point pass=10 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997898
10 run 1 3280000
10 run 2 3280000
10 run 3 3290000
10 run 4 3270000
10 run 5 3280000
10 run 6 3290000
10 run 7 3290000
10 run 8 3290000
10 run 9 3290000
10 run 10 3290000
10 run 11 3290000
10 run 12 3290000
10 run 13 3290000
10 run 14 3290000
10 run 15 3290000
10 run 16 3300000
10 run 17 3290000
10 run 18 3290000
10 run 19 3290000
10 run 20 3310000
10 run 21 3290000
10 run 22 3280000
10 run 23 3290000
10 run 24 3290000
10 run 25 3290000
10 run 26 3280000
10 run 27 3300000
10 run 28 3290000
10 run 29 3290000
10 run 30 3290000
# point pass=10 state=run jobs_per_s=64975.9 updates_per_s=33267667.1 clock_hz=99997910
11 run 1 3290000
11 run 2 3290000
11 run 3 3300000
11 run 4 3280000
11 run 5 3280000
11 run 6 3280000
11 run 7 3280000
11 run 8 3290000
11 run 9 3290000
11 run 10 3280000
11 run 11 3310000
11 run 12 3280000
11 run 13 3290000
11 run 14 3280000
11 run 15 3280000
11 run 16 3290000
11 run 17 3280000
11 run 18 3280000
11 run 19 3280000
11 run 20 3280000
11 run 21 3290000
11 run 22 3280000
11 run 23 3280000
11 run 24 3280000
11 run 25 3290000
11 run 26 3290000
11 run 27 3280000
11 run 28 3280000
11 run 29 3280000
11 run 30 3290000
# point pass=11 state=run jobs_per_s=64975.9 updates_per_s=33267670.1 clock_hz=99997920
11 halt 1 3240000
11 halt 2 3240000
11 halt 3 3250000
11 halt 4 3240000
11 halt 5 3240000
11 halt 6 3240000
11 halt 7 3230000
11 halt 8 3270000
11 halt 9 3250000
11 halt 10 3250000
11 halt 11 3250000
11 halt 12 3250000
11 halt 13 3250000
11 halt 14 3240000
11 halt 15 3240000
11 halt 16 3240000
11 halt 17 3250000
11 halt 18 3250000
11 halt 19 3240000
11 halt 20 3250000
11 halt 21 3240000
11 halt 22 3250000
11 halt 23 3260000
11 halt 24 3240000
11 halt 25 3240000
11 halt 26 3250000
11 halt 27 3240000
11 halt 28 3240000
11 halt 29 3240000
11 halt 30 3250000
# point pass=11 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997928
12 halt 1 3250000
12 halt 2 3260000
12 halt 3 3260000
12 halt 4 3250000
12 halt 5 3250000
12 halt 6 3250000
12 halt 7 3250000
12 halt 8 3250000
12 halt 9 3280000
12 halt 10 3250000
12 halt 11 3260000
12 halt 12 3250000
12 halt 13 3260000
12 halt 14 3260000
12 halt 15 3250000
12 halt 16 3250000
12 halt 17 3280000
12 halt 18 3270000
12 halt 19 3260000
12 halt 20 3260000
12 halt 21 3250000
12 halt 22 3250000
12 halt 23 3270000
12 halt 24 3250000
12 halt 25 3250000
12 halt 26 3280000
12 halt 27 3250000
12 halt 28 3250000
12 halt 29 3260000
12 halt 30 3260000
# point pass=12 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997935
12 run 1 3280000
12 run 2 3290000
12 run 3 3280000
12 run 4 3280000
12 run 5 3280000
12 run 6 3300000
12 run 7 3280000
12 run 8 3290000
12 run 9 3280000
12 run 10 3280000
12 run 11 3290000
12 run 12 3290000
12 run 13 3280000
12 run 14 3300000
12 run 15 3280000
12 run 16 3280000
12 run 17 3290000
12 run 18 3280000
12 run 19 3280000
12 run 20 3280000
12 run 21 3290000
12 run 22 3290000
12 run 23 3280000
12 run 24 3280000
12 run 25 3280000
12 run 26 3290000
12 run 27 3290000
12 run 28 3280000
12 run 29 3300000
12 run 30 3280000
# point pass=12 state=run jobs_per_s=64975.9 updates_per_s=33267677.2 clock_hz=99997941
# done SystemTime { tv_sec: 1793924510, tv_nsec: 651905562 }
# passes used 12 of 12 (dropped: [])
# run - halt = 42.1 +/- 1.9 mW (per-pass sd 6.6); 12/12 passes positive; 7 sample excursions beyond 6 MAD
# per-pass differences (mW): 50.0 40.0 40.0 40.0 40.0 40.0 50.0 35.0 50.0 50.0 40.0 30.0
# board power while running 3286 mW; jobs/s 64975.9; updates/s 33267679.7; measured PL clock 99.998 MHz
# pre-registered prediction 20.0 mW: measured/predicted = 2.10, 11.7 sigma from it
# jobs/s and updates/s above are core 0's; all 4 cores run the same job

View file

@ -0,0 +1,753 @@
# class gibbs-recall
# vqpu-edge meter passes=12 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793913111, tv_nsec: 946190018 }
# pass state sample power_uW
1 run 1 3120000
1 run 2 3110000
1 run 3 3120000
1 run 4 3100000
1 run 5 3110000
1 run 6 3120000
1 run 7 3110000
1 run 8 3110000
1 run 9 3110000
1 run 10 3110000
1 run 11 3110000
1 run 12 3120000
1 run 13 3110000
1 run 14 3110000
1 run 15 3110000
1 run 16 3120000
1 run 17 3120000
1 run 18 3140000
1 run 19 3120000
1 run 20 3110000
1 run 21 3120000
1 run 22 3130000
1 run 23 3130000
1 run 24 3120000
1 run 25 3120000
1 run 26 3140000
1 run 27 3120000
1 run 28 3110000
1 run 29 3120000
1 run 30 3130000
# point pass=1 state=run jobs_per_s=64975.9 updates_per_s=33267684.8 clock_hz=99997963
1 halt 1 3120000
1 halt 2 3110000
1 halt 3 3110000
1 halt 4 3100000
1 halt 5 3100000
1 halt 6 3130000
1 halt 7 3110000
1 halt 8 3100000
1 halt 9 3110000
1 halt 10 3110000
1 halt 11 3490000
1 halt 12 3110000
1 halt 13 3110000
1 halt 14 3120000
1 halt 15 3130000
1 halt 16 3100000
1 halt 17 3110000
1 halt 18 3100000
1 halt 19 3110000
1 halt 20 3100000
1 halt 21 3100000
1 halt 22 3110000
1 halt 23 3110000
1 halt 24 3100000
1 halt 25 3110000
1 halt 26 3100000
1 halt 27 3100000
1 halt 28 3100000
1 halt 29 3100000
1 halt 30 3100000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997942
2 run 1 3130000
2 run 2 3110000
2 run 3 3140000
2 run 4 3130000
2 run 5 3120000
2 run 6 3120000
2 run 7 3120000
2 run 8 3120000
2 run 9 3120000
2 run 10 3120000
2 run 11 3130000
2 run 12 3140000
2 run 13 3120000
2 run 14 3120000
2 run 15 3110000
2 run 16 3120000
2 run 17 3120000
2 run 18 3110000
2 run 19 3120000
2 run 20 3130000
2 run 21 3120000
2 run 22 3110000
2 run 23 3120000
2 run 24 3120000
2 run 25 3150000
2 run 26 3120000
2 run 27 3120000
2 run 28 3120000
2 run 29 3110000
2 run 30 3110000
# point pass=2 state=run jobs_per_s=64975.9 updates_per_s=33267678.8 clock_hz=99997946
2 halt 1 3120000
2 halt 2 3120000
2 halt 3 3100000
2 halt 4 3110000
2 halt 5 3110000
2 halt 6 3120000
2 halt 7 3110000
2 halt 8 3140000
2 halt 9 3120000
2 halt 10 3100000
2 halt 11 3110000
2 halt 12 3110000
2 halt 13 3110000
2 halt 14 3110000
2 halt 15 3110000
2 halt 16 3110000
2 halt 17 3110000
2 halt 18 3110000
2 halt 19 3110000
2 halt 20 3110000
2 halt 21 3110000
2 halt 22 3110000
2 halt 23 3120000
2 halt 24 3120000
2 halt 25 3110000
2 halt 26 3100000
2 halt 27 3100000
2 halt 28 3110000
2 halt 29 3120000
2 halt 30 3110000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997947
3 halt 1 3110000
3 halt 2 3110000
3 halt 3 3120000
3 halt 4 3110000
3 halt 5 3120000
3 halt 6 3110000
3 halt 7 3110000
3 halt 8 3110000
3 halt 9 3110000
3 halt 10 3110000
3 halt 11 3120000
3 halt 12 3110000
3 halt 13 3110000
3 halt 14 3110000
3 halt 15 3110000
3 halt 16 3110000
3 halt 17 3100000
3 halt 18 3110000
3 halt 19 3110000
3 halt 20 3130000
3 halt 21 3110000
3 halt 22 3110000
3 halt 23 3110000
3 halt 24 3110000
3 halt 25 3110000
3 halt 26 3110000
3 halt 27 3110000
3 halt 28 3120000
3 halt 29 3130000
3 halt 30 3100000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997949
3 run 1 3110000
3 run 2 3120000
3 run 3 3120000
3 run 4 3120000
3 run 5 3120000
3 run 6 3110000
3 run 7 3110000
3 run 8 3120000
3 run 9 3110000
3 run 10 3120000
3 run 11 3110000
3 run 12 3110000
3 run 13 3120000
3 run 14 3110000
3 run 15 3110000
3 run 16 3130000
3 run 17 3140000
3 run 18 3120000
3 run 19 3110000
3 run 20 3110000
3 run 21 3100000
3 run 22 3120000
3 run 23 3110000
3 run 24 3120000
3 run 25 3140000
3 run 26 3110000
3 run 27 3110000
3 run 28 3120000
3 run 29 3120000
3 run 30 3100000
# point pass=3 state=run jobs_per_s=64976.0 updates_per_s=33267680.8 clock_hz=99997951
4 halt 1 3110000
4 halt 2 3110000
4 halt 3 3110000
4 halt 4 3100000
4 halt 5 3130000
4 halt 6 3110000
4 halt 7 3110000
4 halt 8 3120000
4 halt 9 3110000
4 halt 10 3110000
4 halt 11 3120000
4 halt 12 3110000
4 halt 13 3110000
4 halt 14 3130000
4 halt 15 3110000
4 halt 16 3110000
4 halt 17 3110000
4 halt 18 3140000
4 halt 19 3110000
4 halt 20 3110000
4 halt 21 3100000
4 halt 22 3120000
4 halt 23 3110000
4 halt 24 3110000
4 halt 25 3110000
4 halt 26 3110000
4 halt 27 3110000
4 halt 28 3110000
4 halt 29 3110000
4 halt 30 3110000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997953
4 run 1 3110000
4 run 2 3140000
4 run 3 3130000
4 run 4 3130000
4 run 5 3110000
4 run 6 3110000
4 run 7 3110000
4 run 8 3110000
4 run 9 3110000
4 run 10 3140000
4 run 11 3120000
4 run 12 3110000
4 run 13 3120000
4 run 14 3110000
4 run 15 3110000
4 run 16 3110000
4 run 17 3110000
4 run 18 3110000
4 run 19 3110000
4 run 20 3110000
4 run 21 3120000
4 run 22 3100000
4 run 23 3110000
4 run 24 3120000
4 run 25 3120000
4 run 26 3100000
4 run 27 3110000
4 run 28 3120000
4 run 29 3120000
4 run 30 3110000
# point pass=4 state=run jobs_per_s=64976.0 updates_per_s=33267681.8 clock_hz=99997954
5 halt 1 3110000
5 halt 2 3110000
5 halt 3 3110000
5 halt 4 3110000
5 halt 5 3120000
5 halt 6 3120000
5 halt 7 3130000
5 halt 8 3110000
5 halt 9 3120000
5 halt 10 3110000
5 halt 11 3110000
5 halt 12 3110000
5 halt 13 3110000
5 halt 14 3120000
5 halt 15 3110000
5 halt 16 3120000
5 halt 17 3110000
5 halt 18 3120000
5 halt 19 3110000
5 halt 20 3110000
5 halt 21 3120000
5 halt 22 3130000
5 halt 23 3120000
5 halt 24 3110000
5 halt 25 3110000
5 halt 26 3110000
5 halt 27 3110000
5 halt 28 3120000
5 halt 29 3120000
5 halt 30 3130000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997955
5 run 1 3110000
5 run 2 3110000
5 run 3 3110000
5 run 4 3130000
5 run 5 3110000
5 run 6 3110000
5 run 7 3110000
5 run 8 3110000
5 run 9 3270000
5 run 10 3100000
5 run 11 3110000
5 run 12 3110000
5 run 13 3110000
5 run 14 3110000
5 run 15 3110000
5 run 16 3110000
5 run 17 3110000
5 run 18 3110000
5 run 19 3120000
5 run 20 3110000
5 run 21 3100000
5 run 22 3120000
5 run 23 3120000
5 run 24 3100000
5 run 25 3100000
5 run 26 3120000
5 run 27 3120000
5 run 28 3110000
5 run 29 3110000
5 run 30 3120000
# point pass=5 state=run jobs_per_s=64975.9 updates_per_s=33267682.4 clock_hz=99997956
6 run 1 3120000
6 run 2 3120000
6 run 3 3110000
6 run 4 3140000
6 run 5 3120000
6 run 6 3130000
6 run 7 3140000
6 run 8 3120000
6 run 9 3120000
6 run 10 3120000
6 run 11 3120000
6 run 12 3140000
6 run 13 3120000
6 run 14 3120000
6 run 15 3150000
6 run 16 3140000
6 run 17 3130000
6 run 18 3130000
6 run 19 3120000
6 run 20 3120000
6 run 21 3120000
6 run 22 3130000
6 run 23 3120000
6 run 24 3130000
6 run 25 3120000
6 run 26 3120000
6 run 27 3110000
6 run 28 3110000
6 run 29 3120000
6 run 30 3120000
# point pass=6 state=run jobs_per_s=64976.0 updates_per_s=33267682.5 clock_hz=99997957
6 halt 1 3110000
6 halt 2 3120000
6 halt 3 3110000
6 halt 4 3130000
6 halt 5 3140000
6 halt 6 3120000
6 halt 7 3110000
6 halt 8 3110000
6 halt 9 3120000
6 halt 10 3120000
6 halt 11 3120000
6 halt 12 3110000
6 halt 13 3140000
6 halt 14 3110000
6 halt 15 3120000
6 halt 16 3110000
6 halt 17 3110000
6 halt 18 3100000
6 halt 19 3110000
6 halt 20 3110000
6 halt 21 3100000
6 halt 22 3100000
6 halt 23 3110000
6 halt 24 3110000
6 halt 25 3110000
6 halt 26 3110000
6 halt 27 3100000
6 halt 28 3120000
6 halt 29 3110000
6 halt 30 3110000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997957
7 run 1 3140000
7 run 2 3130000
7 run 3 3120000
7 run 4 3120000
7 run 5 3120000
7 run 6 3130000
7 run 7 3130000
7 run 8 3110000
7 run 9 3110000
7 run 10 3120000
7 run 11 3110000
7 run 12 3110000
7 run 13 3110000
7 run 14 3120000
7 run 15 3110000
7 run 16 3110000
7 run 17 3110000
7 run 18 3120000
7 run 19 3450000
7 run 20 3110000
7 run 21 3110000
7 run 22 3120000
7 run 23 3110000
7 run 24 3170000
7 run 25 3140000
7 run 26 3120000
7 run 27 3110000
7 run 28 3120000
7 run 29 3110000
7 run 30 3120000
# point pass=7 state=run jobs_per_s=64976.0 updates_per_s=33267683.0 clock_hz=99997958
7 halt 1 3120000
7 halt 2 3110000
7 halt 3 3110000
7 halt 4 3120000
7 halt 5 3110000
7 halt 6 3130000
7 halt 7 3110000
7 halt 8 3110000
7 halt 9 3110000
7 halt 10 3110000
7 halt 11 3110000
7 halt 12 3120000
7 halt 13 3150000
7 halt 14 3120000
7 halt 15 3120000
7 halt 16 3120000
7 halt 17 3110000
7 halt 18 3120000
7 halt 19 3120000
7 halt 20 3110000
7 halt 21 3140000
7 halt 22 3130000
7 halt 23 3120000
7 halt 24 3120000
7 halt 25 3120000
7 halt 26 3120000
7 halt 27 3120000
7 halt 28 3110000
7 halt 29 3110000
7 halt 30 3130000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997959
8 halt 1 3120000
8 halt 2 3120000
8 halt 3 3120000
8 halt 4 3110000
8 halt 5 3110000
8 halt 6 3110000
8 halt 7 3120000
8 halt 8 3110000
8 halt 9 3120000
8 halt 10 3140000
8 halt 11 3120000
8 halt 12 3110000
8 halt 13 3110000
8 halt 14 3110000
8 halt 15 3110000
8 halt 16 3120000
8 halt 17 3130000
8 halt 18 3120000
8 halt 19 3130000
8 halt 20 3110000
8 halt 21 3110000
8 halt 22 3110000
8 halt 23 3120000
8 halt 24 3120000
8 halt 25 3110000
8 halt 26 3120000
8 halt 27 3120000
8 halt 28 3110000
8 halt 29 3110000
8 halt 30 3120000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997959
8 run 1 3120000
8 run 2 3120000
8 run 3 3120000
8 run 4 3110000
8 run 5 3120000
8 run 6 3130000
8 run 7 3140000
8 run 8 3120000
8 run 9 3130000
8 run 10 3110000
8 run 11 3120000
8 run 12 3110000
8 run 13 3120000
8 run 14 3120000
8 run 15 3140000
8 run 16 3120000
8 run 17 3130000
8 run 18 3110000
8 run 19 3120000
8 run 20 3130000
8 run 21 3130000
8 run 22 3120000
8 run 23 3120000
8 run 24 3120000
8 run 25 3120000
8 run 26 3120000
8 run 27 3130000
8 run 28 3120000
8 run 29 3120000
8 run 30 3140000
# point pass=8 state=run jobs_per_s=64975.9 updates_per_s=33267683.7 clock_hz=99997960
9 halt 1 3120000
9 halt 2 3110000
9 halt 3 3130000
9 halt 4 3120000
9 halt 5 3110000
9 halt 6 3110000
9 halt 7 3110000
9 halt 8 3110000
9 halt 9 3120000
9 halt 10 3110000
9 halt 11 3110000
9 halt 12 3130000
9 halt 13 3110000
9 halt 14 3120000
9 halt 15 3110000
9 halt 16 3110000
9 halt 17 3110000
9 halt 18 3110000
9 halt 19 3120000
9 halt 20 3110000
9 halt 21 3110000
9 halt 22 3110000
9 halt 23 3110000
9 halt 24 3110000
9 halt 25 3110000
9 halt 26 3110000
9 halt 27 3130000
9 halt 28 3110000
9 halt 29 3100000
9 halt 30 3110000
# point pass=9 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997960
9 run 1 3130000
9 run 2 3120000
9 run 3 3110000
9 run 4 3120000
9 run 5 3110000
9 run 6 3120000
9 run 7 3120000
9 run 8 3130000
9 run 9 3130000
9 run 10 3110000
9 run 11 3110000
9 run 12 3120000
9 run 13 3120000
9 run 14 3130000
9 run 15 3130000
9 run 16 3120000
9 run 17 3120000
9 run 18 3120000
9 run 19 3120000
9 run 20 3120000
9 run 21 3120000
9 run 22 3110000
9 run 23 3130000
9 run 24 3140000
9 run 25 3120000
9 run 26 3120000
9 run 27 3110000
9 run 28 3120000
9 run 29 3110000
9 run 30 3110000
# point pass=9 state=run jobs_per_s=64976.0 updates_per_s=33267683.6 clock_hz=99997960
10 run 1 3120000
10 run 2 3110000
10 run 3 3380000
10 run 4 3110000
10 run 5 3110000
10 run 6 3110000
10 run 7 3100000
10 run 8 3110000
10 run 9 3110000
10 run 10 3110000
10 run 11 3100000
10 run 12 3130000
10 run 13 3110000
10 run 14 3110000
10 run 15 3110000
10 run 16 3110000
10 run 17 3110000
10 run 18 3110000
10 run 19 3110000
10 run 20 3120000
10 run 21 3130000
10 run 22 3110000
10 run 23 3110000
10 run 24 3110000
10 run 25 3110000
10 run 26 3110000
10 run 27 3110000
10 run 28 3100000
10 run 29 3120000
10 run 30 3110000
# point pass=10 state=run jobs_per_s=64975.9 updates_per_s=33267683.9 clock_hz=99997961
10 halt 1 3100000
10 halt 2 3100000
10 halt 3 3110000
10 halt 4 3100000
10 halt 5 3100000
10 halt 6 3100000
10 halt 7 3110000
10 halt 8 3100000
10 halt 9 3120000
10 halt 10 3110000
10 halt 11 3120000
10 halt 12 3100000
10 halt 13 3100000
10 halt 14 3100000
10 halt 15 3110000
10 halt 16 3120000
10 halt 17 3120000
10 halt 18 3110000
10 halt 19 3100000
10 halt 20 3110000
10 halt 21 3100000
10 halt 22 3110000
10 halt 23 3100000
10 halt 24 3100000
10 halt 25 3110000
10 halt 26 3110000
10 halt 27 3100000
10 halt 28 3100000
10 halt 29 3100000
10 halt 30 3090000
# point pass=10 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997961
11 halt 1 3120000
11 halt 2 3110000
11 halt 3 3090000
11 halt 4 3100000
11 halt 5 3130000
11 halt 6 3110000
11 halt 7 3100000
11 halt 8 3110000
11 halt 9 3100000
11 halt 10 3090000
11 halt 11 3100000
11 halt 12 3110000
11 halt 13 3100000
11 halt 14 3120000
11 halt 15 3100000
11 halt 16 3120000
11 halt 17 3100000
11 halt 18 3100000
11 halt 19 3100000
11 halt 20 3100000
11 halt 21 3100000
11 halt 22 3110000
11 halt 23 3100000
11 halt 24 3110000
11 halt 25 3100000
11 halt 26 3110000
11 halt 27 3100000
11 halt 28 3110000
11 halt 29 3110000
11 halt 30 3110000
# point pass=11 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997961
11 run 1 3110000
11 run 2 3140000
11 run 3 3150000
11 run 4 3120000
11 run 5 3120000
11 run 6 3120000
11 run 7 3120000
11 run 8 3120000
11 run 9 3120000
11 run 10 3120000
11 run 11 3130000
11 run 12 3110000
11 run 13 3130000
11 run 14 3120000
11 run 15 3130000
11 run 16 3120000
11 run 17 3130000
11 run 18 3120000
11 run 19 3120000
11 run 20 3120000
11 run 21 3120000
11 run 22 3130000
11 run 23 3120000
11 run 24 3120000
11 run 25 3120000
11 run 26 3140000
11 run 27 3120000
11 run 28 3120000
11 run 29 3120000
11 run 30 3130000
# point pass=11 state=run jobs_per_s=64976.0 updates_per_s=33267683.6 clock_hz=99997960
12 halt 1 3110000
12 halt 2 3110000
12 halt 3 3110000
12 halt 4 3110000
12 halt 5 3110000
12 halt 6 3100000
12 halt 7 3110000
12 halt 8 3100000
12 halt 9 3100000
12 halt 10 3110000
12 halt 11 3110000
12 halt 12 3110000
12 halt 13 3110000
12 halt 14 3120000
12 halt 15 3110000
12 halt 16 3100000
12 halt 17 3110000
12 halt 18 3110000
12 halt 19 3110000
12 halt 20 3100000
12 halt 21 3120000
12 halt 22 3120000
12 halt 23 3130000
12 halt 24 3100000
12 halt 25 3110000
12 halt 26 3100000
12 halt 27 3110000
12 halt 28 3110000
12 halt 29 3110000
12 halt 30 3110000
# point pass=12 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997961
12 run 1 3110000
12 run 2 3110000
12 run 3 3110000
12 run 4 3130000
12 run 5 3110000
12 run 6 3110000
12 run 7 3110000
12 run 8 3110000
12 run 9 3120000
12 run 10 3110000
12 run 11 3150000
12 run 12 3110000
12 run 13 3120000
12 run 14 3110000
12 run 15 3120000
12 run 16 3120000
12 run 17 3110000
12 run 18 3120000
12 run 19 3130000
12 run 20 3140000
12 run 21 3120000
12 run 22 3110000
12 run 23 3120000
12 run 24 3110000
12 run 25 3120000
12 run 26 3120000
12 run 27 3120000
12 run 28 3120000
12 run 29 3110000
12 run 30 3110000
# point pass=12 state=run jobs_per_s=64976.0 updates_per_s=33267687.8 clock_hz=99997972
# done SystemTime { tv_sec: 1793915273, tv_nsec: 319036681 }
# passes used 12 of 12 (dropped: [])
# run - halt = 6.7 +/- 1.8 mW (per-pass sd 6.2); 8/12 passes positive; 5 sample excursions beyond 6 MAD
# per-pass differences (mW): 10.0 10.0 5.0 0.0 0.0 10.0 0.0 0.0 10.0 10.0 20.0 5.0
# board power while running 3117 mW; jobs/s 64976.0; updates/s 33267683.1; measured PL clock 99.998 MHz
# pre-registered prediction 5.0 mW: measured/predicted = 1.33, 0.9 sigma from it

View file

@ -0,0 +1,723 @@
# kv_runhalt bitstream=ev_rdet.bit.bin gpio=594 settle=60s passes=12 samples=30
# pass vehicle sample power_uW
1 run 1 3180000
1 run 2 3170000
1 run 3 3170000
1 run 4 3160000
1 run 5 3170000
1 run 6 3150000
1 run 7 3180000
1 run 8 3150000
1 run 9 3190000
1 run 10 3140000
1 run 11 3190000
1 run 12 3140000
1 run 13 3180000
1 run 14 3140000
1 run 15 3190000
1 run 16 3140000
1 run 17 3210000
1 run 18 3140000
1 run 19 3210000
1 run 20 3140000
1 run 21 3230000
1 run 22 3150000
1 run 23 3220000
1 run 24 3150000
1 run 25 3190000
1 run 26 3140000
1 run 27 3190000
1 run 28 3150000
1 run 29 3190000
1 run 30 3140000
1 halt 1 3070000
1 halt 2 3080000
1 halt 3 3080000
1 halt 4 3110000
1 halt 5 3070000
1 halt 6 3080000
1 halt 7 3080000
1 halt 8 3070000
1 halt 9 3080000
1 halt 10 3090000
1 halt 11 3080000
1 halt 12 3080000
1 halt 13 3080000
1 halt 14 3080000
1 halt 15 3140000
1 halt 16 3080000
1 halt 17 3100000
1 halt 18 3080000
1 halt 19 3080000
1 halt 20 3090000
1 halt 21 3090000
1 halt 22 3080000
1 halt 23 3140000
1 halt 24 3100000
1 halt 25 3100000
1 halt 26 3080000
1 halt 27 3090000
1 halt 28 3080000
1 halt 29 3110000
1 halt 30 3080000
2 run 1 3150000
2 run 2 3150000
2 run 3 3160000
2 run 4 3150000
2 run 5 3150000
2 run 6 3140000
2 run 7 3150000
2 run 8 3150000
2 run 9 3150000
2 run 10 3150000
2 run 11 3160000
2 run 12 3170000
2 run 13 3170000
2 run 14 3150000
2 run 15 3160000
2 run 16 3150000
2 run 17 3190000
2 run 18 3150000
2 run 19 3170000
2 run 20 3140000
2 run 21 3160000
2 run 22 3160000
2 run 23 3150000
2 run 24 3150000
2 run 25 3150000
2 run 26 3160000
2 run 27 3170000
2 run 28 3160000
2 run 29 3160000
2 run 30 3150000
2 halt 1 3190000
2 halt 2 3100000
2 halt 3 3170000
2 halt 4 3080000
2 halt 5 3180000
2 halt 6 3080000
2 halt 7 3210000
2 halt 8 3090000
2 halt 9 3180000
2 halt 10 3080000
2 halt 11 3160000
2 halt 12 3080000
2 halt 13 3080000
2 halt 14 3200000
2 halt 15 3090000
2 halt 16 3190000
2 halt 17 3080000
2 halt 18 3210000
2 halt 19 3080000
2 halt 20 3200000
2 halt 21 3100000
2 halt 22 3220000
2 halt 23 3080000
2 halt 24 3200000
2 halt 25 3080000
2 halt 26 3220000
2 halt 27 3080000
2 halt 28 3230000
2 halt 29 3080000
2 halt 30 3090000
3 run 1 3150000
3 run 2 3240000
3 run 3 3150000
3 run 4 3240000
3 run 5 3140000
3 run 6 3250000
3 run 7 3140000
3 run 8 3240000
3 run 9 3140000
3 run 10 3250000
3 run 11 3140000
3 run 12 3240000
3 run 13 3140000
3 run 14 3240000
3 run 15 3140000
3 run 16 3240000
3 run 17 3150000
3 run 18 3250000
3 run 19 3150000
3 run 20 3250000
3 run 21 3140000
3 run 22 3260000
3 run 23 3160000
3 run 24 3250000
3 run 25 3140000
3 run 26 3260000
3 run 27 3150000
3 run 28 3260000
3 run 29 3160000
3 run 30 3280000
3 halt 1 3170000
3 halt 2 3070000
3 halt 3 3180000
3 halt 4 3090000
3 halt 5 3160000
3 halt 6 3090000
3 halt 7 3170000
3 halt 8 3080000
3 halt 9 3150000
3 halt 10 3090000
3 halt 11 3140000
3 halt 12 3100000
3 halt 13 3130000
3 halt 14 3080000
3 halt 15 3130000
3 halt 16 3080000
3 halt 17 3160000
3 halt 18 3100000
3 halt 19 3140000
3 halt 20 3100000
3 halt 21 3160000
3 halt 22 3090000
3 halt 23 3170000
3 halt 24 3080000
3 halt 25 3160000
3 halt 26 3090000
3 halt 27 3170000
3 halt 28 3100000
3 halt 29 3170000
3 halt 30 3090000
4 halt 1 3100000
4 halt 2 3170000
4 halt 3 3090000
4 halt 4 3190000
4 halt 5 3090000
4 halt 6 3190000
4 halt 7 3100000
4 halt 8 3170000
4 halt 9 3080000
4 halt 10 3220000
4 halt 11 3080000
4 halt 12 3240000
4 halt 13 3090000
4 halt 14 3230000
4 halt 15 3090000
4 halt 16 3250000
4 halt 17 3090000
4 halt 18 3240000
4 halt 19 3080000
4 halt 20 3270000
4 halt 21 3480000
4 halt 22 3080000
4 halt 23 3090000
4 halt 24 3100000
4 halt 25 3090000
4 halt 26 3110000
4 halt 27 3080000
4 halt 28 3080000
4 halt 29 3090000
4 halt 30 3090000
4 run 1 3150000
4 run 2 3150000
4 run 3 3150000
4 run 4 3140000
4 run 5 3150000
4 run 6 3160000
4 run 7 3150000
4 run 8 3150000
4 run 9 3150000
4 run 10 3160000
4 run 11 3150000
4 run 12 3150000
4 run 13 3150000
4 run 14 3160000
4 run 15 3150000
4 run 16 3190000
4 run 17 3140000
4 run 18 3190000
4 run 19 3140000
4 run 20 3180000
4 run 21 3150000
4 run 22 3180000
4 run 23 3160000
4 run 24 3170000
4 run 25 3140000
4 run 26 3180000
4 run 27 3140000
4 run 28 3150000
4 run 29 3180000
4 run 30 3160000
5 run 1 3160000
5 run 2 3160000
5 run 3 3150000
5 run 4 3170000
5 run 5 3150000
5 run 6 3160000
5 run 7 3150000
5 run 8 3150000
5 run 9 3150000
5 run 10 3170000
5 run 11 3160000
5 run 12 3180000
5 run 13 3150000
5 run 14 3160000
5 run 15 3150000
5 run 16 3180000
5 run 17 3150000
5 run 18 3200000
5 run 19 3150000
5 run 20 3180000
5 run 21 3160000
5 run 22 3180000
5 run 23 3150000
5 run 24 3180000
5 run 25 3160000
5 run 26 3210000
5 run 27 3160000
5 run 28 3190000
5 run 29 3160000
5 run 30 3200000
5 halt 1 3170000
5 halt 2 3090000
5 halt 3 3160000
5 halt 4 3070000
5 halt 5 3140000
5 halt 6 3090000
5 halt 7 3150000
5 halt 8 3070000
5 halt 9 3130000
5 halt 10 3070000
5 halt 11 3160000
5 halt 12 3070000
5 halt 13 3150000
5 halt 14 3070000
5 halt 15 3160000
5 halt 16 3080000
5 halt 17 3160000
5 halt 18 3070000
5 halt 19 3160000
5 halt 20 3070000
5 halt 21 3190000
5 halt 22 3080000
5 halt 23 3170000
5 halt 24 3070000
5 halt 25 3170000
5 halt 26 3070000
5 halt 27 3170000
5 halt 28 3070000
5 halt 29 3170000
5 halt 30 3070000
6 run 1 3250000
6 run 2 3150000
6 run 3 3240000
6 run 4 3150000
6 run 5 3250000
6 run 6 3150000
6 run 7 3240000
6 run 8 3150000
6 run 9 3250000
6 run 10 3160000
6 run 11 3240000
6 run 12 3150000
6 run 13 3240000
6 run 14 3150000
6 run 15 3240000
6 run 16 3160000
6 run 17 3240000
6 run 18 3150000
6 run 19 3250000
6 run 20 3160000
6 run 21 3230000
6 run 22 3150000
6 run 23 3220000
6 run 24 3150000
6 run 25 3210000
6 run 26 3150000
6 run 27 3220000
6 run 28 3150000
6 run 29 3260000
6 run 30 3150000
6 halt 1 3070000
6 halt 2 3070000
6 halt 3 3080000
6 halt 4 3080000
6 halt 5 3070000
6 halt 6 3080000
6 halt 7 3070000
6 halt 8 3070000
6 halt 9 3080000
6 halt 10 3100000
6 halt 11 3070000
6 halt 12 3120000
6 halt 13 3070000
6 halt 14 3090000
6 halt 15 3070000
6 halt 16 3120000
6 halt 17 3070000
6 halt 18 3130000
6 halt 19 3070000
6 halt 20 3110000
6 halt 21 3080000
6 halt 22 3120000
6 halt 23 3070000
6 halt 24 3100000
6 halt 25 3070000
6 halt 26 3110000
6 halt 27 3070000
6 halt 28 3130000
6 halt 29 3070000
6 halt 30 3140000
7 halt 1 3090000
7 halt 2 3080000
7 halt 3 3070000
7 halt 4 3070000
7 halt 5 3070000
7 halt 6 3070000
7 halt 7 3250000
7 halt 8 3070000
7 halt 9 3080000
7 halt 10 3070000
7 halt 11 3240000
7 halt 12 3070000
7 halt 13 3230000
7 halt 14 3070000
7 halt 15 3220000
7 halt 16 3080000
7 halt 17 3210000
7 halt 18 3070000
7 halt 19 3200000
7 halt 20 3080000
7 halt 21 3200000
7 halt 22 3070000
7 halt 23 3210000
7 halt 24 3070000
7 halt 25 3190000
7 halt 26 3080000
7 halt 27 3220000
7 halt 28 3070000
7 halt 29 3180000
7 halt 30 3080000
7 run 1 3140000
7 run 2 3210000
7 run 3 3140000
7 run 4 3210000
7 run 5 3140000
7 run 6 3210000
7 run 7 3140000
7 run 8 3220000
7 run 9 3150000
7 run 10 3230000
7 run 11 3140000
7 run 12 3220000
7 run 13 3140000
7 run 14 3210000
7 run 15 3150000
7 run 16 3230000
7 run 17 3150000
7 run 18 3220000
7 run 19 3150000
7 run 20 3200000
7 run 21 3150000
7 run 22 3210000
7 run 23 3160000
7 run 24 3210000
7 run 25 3150000
7 run 26 3220000
7 run 27 3150000
7 run 28 3200000
7 run 29 3160000
7 run 30 3190000
8 halt 1 3070000
8 halt 2 3070000
8 halt 3 3080000
8 halt 4 3070000
8 halt 5 3240000
8 halt 6 3070000
8 halt 7 3210000
8 halt 8 3080000
8 halt 9 3200000
8 halt 10 3080000
8 halt 11 3210000
8 halt 12 3090000
8 halt 13 3210000
8 halt 14 3080000
8 halt 15 3540000
8 halt 16 3080000
8 halt 17 3170000
8 halt 18 3100000
8 halt 19 3160000
8 halt 20 3140000
8 halt 21 3190000
8 halt 22 3080000
8 halt 23 3190000
8 halt 24 3070000
8 halt 25 3170000
8 halt 26 3080000
8 halt 27 3170000
8 halt 28 3080000
8 halt 29 3160000
8 halt 30 3080000
8 run 1 3280000
8 run 2 3130000
8 run 3 3250000
8 run 4 3140000
8 run 5 3260000
8 run 6 3140000
8 run 7 3260000
8 run 8 3150000
8 run 9 3240000
8 run 10 3140000
8 run 11 3260000
8 run 12 3140000
8 run 13 3260000
8 run 14 3140000
8 run 15 3260000
8 run 16 3140000
8 run 17 3270000
8 run 18 3140000
8 run 19 3260000
8 run 20 3150000
8 run 21 3280000
8 run 22 3150000
8 run 23 3260000
8 run 24 3140000
8 run 25 3270000
8 run 26 3140000
8 run 27 3290000
8 run 28 3140000
8 run 29 3290000
8 run 30 3150000
9 halt 1 3170000
9 halt 2 3090000
9 halt 3 3170000
9 halt 4 3080000
9 halt 5 3180000
9 halt 6 3080000
9 halt 7 3170000
9 halt 8 3070000
9 halt 9 3190000
9 halt 10 3090000
9 halt 11 3160000
9 halt 12 3080000
9 halt 13 3190000
9 halt 14 3080000
9 halt 15 3180000
9 halt 16 3080000
9 halt 17 3160000
9 halt 18 3080000
9 halt 19 3170000
9 halt 20 3090000
9 halt 21 3170000
9 halt 22 3090000
9 halt 23 3170000
9 halt 24 3080000
9 halt 25 3180000
9 halt 26 3090000
9 halt 27 3190000
9 halt 28 3080000
9 halt 29 3220000
9 halt 30 3080000
9 run 1 3150000
9 run 2 3190000
9 run 3 3140000
9 run 4 3220000
9 run 5 3140000
9 run 6 3220000
9 run 7 3140000
9 run 8 3220000
9 run 9 3140000
9 run 10 3220000
9 run 11 3150000
9 run 12 3240000
9 run 13 3140000
9 run 14 3230000
9 run 15 3140000
9 run 16 3230000
9 run 17 3130000
9 run 18 3230000
9 run 19 3140000
9 run 20 3210000
9 run 21 3150000
9 run 22 3230000
9 run 23 3160000
9 run 24 3240000
9 run 25 3160000
9 run 26 3240000
9 run 27 3150000
9 run 28 3240000
9 run 29 3150000
9 run 30 3240000
10 halt 1 3080000
10 halt 2 3110000
10 halt 3 3070000
10 halt 4 3130000
10 halt 5 3070000
10 halt 6 3150000
10 halt 7 3080000
10 halt 8 3130000
10 halt 9 3070000
10 halt 10 3120000
10 halt 11 3070000
10 halt 12 3130000
10 halt 13 3080000
10 halt 14 3110000
10 halt 15 3070000
10 halt 16 3110000
10 halt 17 3070000
10 halt 18 3120000
10 halt 19 3070000
10 halt 20 3100000
10 halt 21 3080000
10 halt 22 3120000
10 halt 23 3070000
10 halt 24 3110000
10 halt 25 3170000
10 halt 26 3070000
10 halt 27 3170000
10 halt 28 3100000
10 halt 29 3170000
10 halt 30 3080000
10 run 1 3150000
10 run 2 3180000
10 run 3 3150000
10 run 4 3180000
10 run 5 3160000
10 run 6 3170000
10 run 7 3160000
10 run 8 3190000
10 run 9 3150000
10 run 10 3200000
10 run 11 3150000
10 run 12 3210000
10 run 13 3150000
10 run 14 3200000
10 run 15 3150000
10 run 16 3240000
10 run 17 3160000
10 run 18 3240000
10 run 19 3150000
10 run 20 3240000
10 run 21 3160000
10 run 22 3240000
10 run 23 3150000
10 run 24 3250000
10 run 25 3270000
10 run 26 3150000
10 run 27 3280000
10 run 28 3150000
10 run 29 3320000
10 run 30 3160000
11 halt 1 3180000
11 halt 2 3090000
11 halt 3 3180000
11 halt 4 3090000
11 halt 5 3200000
11 halt 6 3080000
11 halt 7 3190000
11 halt 8 3080000
11 halt 9 3180000
11 halt 10 3090000
11 halt 11 3170000
11 halt 12 3120000
11 halt 13 3180000
11 halt 14 3090000
11 halt 15 3180000
11 halt 16 3110000
11 halt 17 3170000
11 halt 18 3110000
11 halt 19 3190000
11 halt 20 3090000
11 halt 21 3170000
11 halt 22 3090000
11 halt 23 3170000
11 halt 24 3100000
11 halt 25 3170000
11 halt 26 3100000
11 halt 27 3170000
11 halt 28 3090000
11 halt 29 3160000
11 halt 30 3090000
11 run 1 3160000
11 run 2 3160000
11 run 3 3160000
11 run 4 3150000
11 run 5 3160000
11 run 6 3160000
11 run 7 3150000
11 run 8 3170000
11 run 9 3180000
11 run 10 3180000
11 run 11 3160000
11 run 12 3180000
11 run 13 3160000
11 run 14 3170000
11 run 15 3150000
11 run 16 3180000
11 run 17 3150000
11 run 18 3190000
11 run 19 3170000
11 run 20 3190000
11 run 21 3160000
11 run 22 3220000
11 run 23 3150000
11 run 24 3220000
11 run 25 3160000
11 run 26 3220000
11 run 27 3160000
11 run 28 3230000
11 run 29 3150000
11 run 30 3220000
12 halt 1 3080000
12 halt 2 3080000
12 halt 3 3080000
12 halt 4 3080000
12 halt 5 3080000
12 halt 6 3090000
12 halt 7 3070000
12 halt 8 3080000
12 halt 9 3070000
12 halt 10 3070000
12 halt 11 3100000
12 halt 12 3070000
12 halt 13 3070000
12 halt 14 3190000
12 halt 15 3080000
12 halt 16 3070000
12 halt 17 3100000
12 halt 18 3070000
12 halt 19 3090000
12 halt 20 3070000
12 halt 21 3070000
12 halt 22 3070000
12 halt 23 3070000
12 halt 24 3080000
12 halt 25 3070000
12 halt 26 3070000
12 halt 27 3080000
12 halt 28 3080000
12 halt 29 3080000
12 halt 30 3090000
12 run 1 3150000
12 run 2 3260000
12 run 3 3150000
12 run 4 3260000
12 run 5 3150000
12 run 6 3290000
12 run 7 3150000
12 run 8 3160000
12 run 9 3150000
12 run 10 3160000
12 run 11 3160000
12 run 12 3170000
12 run 13 3160000
12 run 14 3150000
12 run 15 3150000
12 run 16 3150000
12 run 17 3180000
12 run 18 3150000
12 run 19 3170000
12 run 20 3160000
12 run 21 3150000
12 run 22 3170000
12 run 23 3150000
12 run 24 3200000
12 run 25 3150000
12 run 26 3180000
12 run 27 3150000
12 run 28 3210000
12 run 29 3150000
12 run 30 3190000
# done

View file

@ -0,0 +1,505 @@
# class t0-recall
# vqpu-edge meter passes=8 settle=60s samples=30 null=true sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793909509, tv_nsec: 550481081 }
# pass state sample power_uW
1 halt 1 3110000
1 halt 2 3110000
1 halt 3 3140000
1 halt 4 3110000
1 halt 5 3110000
1 halt 6 3110000
1 halt 7 3110000
1 halt 8 3100000
1 halt 9 3120000
1 halt 10 3110000
1 halt 11 3110000
1 halt 12 3110000
1 halt 13 3120000
1 halt 14 3120000
1 halt 15 3110000
1 halt 16 3110000
1 halt 17 3110000
1 halt 18 3130000
1 halt 19 3120000
1 halt 20 3110000
1 halt 21 3120000
1 halt 22 3110000
1 halt 23 3120000
1 halt 24 3120000
1 halt 25 3120000
1 halt 26 3120000
1 halt 27 3120000
1 halt 28 3110000
1 halt 29 3120000
1 halt 30 3110000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998042
1 run 1 3120000
1 run 2 3110000
1 run 3 3120000
1 run 4 3110000
1 run 5 3110000
1 run 6 3110000
1 run 7 3120000
1 run 8 3110000
1 run 9 3100000
1 run 10 3110000
1 run 11 3110000
1 run 12 3110000
1 run 13 3110000
1 run 14 3120000
1 run 15 3140000
1 run 16 3120000
1 run 17 3110000
1 run 18 3110000
1 run 19 3110000
1 run 20 3110000
1 run 21 3110000
1 run 22 3100000
1 run 23 3120000
1 run 24 3140000
1 run 25 3110000
1 run 26 3110000
1 run 27 3110000
1 run 28 3120000
1 run 29 3100000
1 run 30 3120000
# point pass=1 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998030
2 halt 1 3120000
2 halt 2 3110000
2 halt 3 3110000
2 halt 4 3110000
2 halt 5 3110000
2 halt 6 3180000
2 halt 7 3110000
2 halt 8 3110000
2 halt 9 3110000
2 halt 10 3100000
2 halt 11 3120000
2 halt 12 3130000
2 halt 13 3110000
2 halt 14 3110000
2 halt 15 3110000
2 halt 16 3120000
2 halt 17 3110000
2 halt 18 3110000
2 halt 19 3110000
2 halt 20 3120000
2 halt 21 3110000
2 halt 22 3110000
2 halt 23 3110000
2 halt 24 3120000
2 halt 25 3110000
2 halt 26 3120000
2 halt 27 3100000
2 halt 28 3110000
2 halt 29 3110000
2 halt 30 3110000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998021
2 run 1 3110000
2 run 2 3110000
2 run 3 3110000
2 run 4 3110000
2 run 5 3110000
2 run 6 3110000
2 run 7 3110000
2 run 8 3120000
2 run 9 3130000
2 run 10 3110000
2 run 11 3110000
2 run 12 3110000
2 run 13 3110000
2 run 14 3110000
2 run 15 3110000
2 run 16 3110000
2 run 17 3130000
2 run 18 3130000
2 run 19 3100000
2 run 20 3110000
2 run 21 3110000
2 run 22 3120000
2 run 23 3110000
2 run 24 3110000
2 run 25 3110000
2 run 26 3120000
2 run 27 3110000
2 run 28 3110000
2 run 29 3110000
2 run 30 3110000
# point pass=2 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998014
3 halt 1 3110000
3 halt 2 3110000
3 halt 3 3110000
3 halt 4 3100000
3 halt 5 3120000
3 halt 6 3130000
3 halt 7 3110000
3 halt 8 3110000
3 halt 9 3110000
3 halt 10 3110000
3 halt 11 3110000
3 halt 12 3110000
3 halt 13 3110000
3 halt 14 3130000
3 halt 15 3110000
3 halt 16 3110000
3 halt 17 3110000
3 halt 18 3110000
3 halt 19 3100000
3 halt 20 3110000
3 halt 21 3100000
3 halt 22 3110000
3 halt 23 3110000
3 halt 24 3110000
3 halt 25 3110000
3 halt 26 3110000
3 halt 27 3110000
3 halt 28 3130000
3 halt 29 3130000
3 halt 30 3110000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998007
3 run 1 3110000
3 run 2 3130000
3 run 3 3110000
3 run 4 3110000
3 run 5 3110000
3 run 6 3110000
3 run 7 3120000
3 run 8 3110000
3 run 9 3110000
3 run 10 3110000
3 run 11 3110000
3 run 12 3110000
3 run 13 3110000
3 run 14 3110000
3 run 15 3110000
3 run 16 3110000
3 run 17 3120000
3 run 18 3110000
3 run 19 3110000
3 run 20 3110000
3 run 21 3110000
3 run 22 3110000
3 run 23 3110000
3 run 24 3110000
3 run 25 3130000
3 run 26 3130000
3 run 27 3100000
3 run 28 3120000
3 run 29 3110000
3 run 30 3110000
# point pass=3 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998002
4 halt 1 3110000
4 halt 2 3110000
4 halt 3 3120000
4 halt 4 3120000
4 halt 5 3110000
4 halt 6 3110000
4 halt 7 3110000
4 halt 8 3110000
4 halt 9 3110000
4 halt 10 3110000
4 halt 11 3110000
4 halt 12 3110000
4 halt 13 3100000
4 halt 14 3120000
4 halt 15 3110000
4 halt 16 3110000
4 halt 17 3100000
4 halt 18 3110000
4 halt 19 3110000
4 halt 20 3110000
4 halt 21 3100000
4 halt 22 3120000
4 halt 23 3110000
4 halt 24 3100000
4 halt 25 3110000
4 halt 26 3100000
4 halt 27 3110000
4 halt 28 3100000
4 halt 29 3100000
4 halt 30 3110000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997997
4 run 1 3100000
4 run 2 3110000
4 run 3 3110000
4 run 4 3100000
4 run 5 3110000
4 run 6 3110000
4 run 7 3100000
4 run 8 3110000
4 run 9 3110000
4 run 10 3120000
4 run 11 3120000
4 run 12 3110000
4 run 13 3100000
4 run 14 3110000
4 run 15 3100000
4 run 16 3110000
4 run 17 3110000
4 run 18 3100000
4 run 19 3130000
4 run 20 3110000
4 run 21 3100000
4 run 22 3130000
4 run 23 3120000
4 run 24 3100000
4 run 25 3100000
4 run 26 3110000
4 run 27 3490000
4 run 28 3100000
4 run 29 3110000
4 run 30 3110000
# point pass=4 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997993
5 run 1 3110000
5 run 2 3110000
5 run 3 3100000
5 run 4 3110000
5 run 5 3110000
5 run 6 3110000
5 run 7 3120000
5 run 8 3140000
5 run 9 3100000
5 run 10 3110000
5 run 11 3110000
5 run 12 3110000
5 run 13 3110000
5 run 14 3110000
5 run 15 3100000
5 run 16 3140000
5 run 17 3110000
5 run 18 3100000
5 run 19 3100000
5 run 20 3110000
5 run 21 3100000
5 run 22 3110000
5 run 23 3110000
5 run 24 3100000
5 run 25 3110000
5 run 26 3100000
5 run 27 3110000
5 run 28 3110000
5 run 29 3110000
5 run 30 3110000
# point pass=5 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997990
5 halt 1 3130000
5 halt 2 3120000
5 halt 3 3120000
5 halt 4 3140000
5 halt 5 3140000
5 halt 6 3120000
5 halt 7 3110000
5 halt 8 3120000
5 halt 9 3130000
5 halt 10 3110000
5 halt 11 3120000
5 halt 12 3130000
5 halt 13 3140000
5 halt 14 3120000
5 halt 15 3120000
5 halt 16 3120000
5 halt 17 3110000
5 halt 18 3110000
5 halt 19 3120000
5 halt 20 3110000
5 halt 21 3110000
5 halt 22 3120000
5 halt 23 3110000
5 halt 24 3120000
5 halt 25 3120000
5 halt 26 3110000
5 halt 27 3120000
5 halt 28 3140000
5 halt 29 3120000
5 halt 30 3120000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997987
6 halt 1 3140000
6 halt 2 3120000
6 halt 3 3130000
6 halt 4 3110000
6 halt 5 3120000
6 halt 6 3110000
6 halt 7 3110000
6 halt 8 3120000
6 halt 9 3120000
6 halt 10 3110000
6 halt 11 3120000
6 halt 12 3100000
6 halt 13 3110000
6 halt 14 3120000
6 halt 15 3120000
6 halt 16 3140000
6 halt 17 3120000
6 halt 18 3120000
6 halt 19 3130000
6 halt 20 3120000
6 halt 21 3120000
6 halt 22 3120000
6 halt 23 3110000
6 halt 24 3140000
6 halt 25 3140000
6 halt 26 3120000
6 halt 27 3120000
6 halt 28 3110000
6 halt 29 3110000
6 halt 30 3120000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997985
6 run 1 3110000
6 run 2 3110000
6 run 3 3290000
6 run 4 3100000
6 run 5 3100000
6 run 6 3110000
6 run 7 3110000
6 run 8 3110000
6 run 9 3110000
6 run 10 3110000
6 run 11 3110000
6 run 12 3110000
6 run 13 3130000
6 run 14 3110000
6 run 15 3120000
6 run 16 3110000
6 run 17 3110000
6 run 18 3110000
6 run 19 3130000
6 run 20 3110000
6 run 21 3140000
6 run 22 3130000
6 run 23 3110000
6 run 24 3110000
6 run 25 3110000
6 run 26 3110000
6 run 27 3110000
6 run 28 3110000
6 run 29 3110000
6 run 30 3120000
# point pass=6 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997983
7 run 1 3110000
7 run 2 3120000
7 run 3 3120000
7 run 4 3120000
7 run 5 3110000
7 run 6 3120000
7 run 7 3110000
7 run 8 3130000
7 run 9 3110000
7 run 10 3140000
7 run 11 3110000
7 run 12 3120000
7 run 13 3120000
7 run 14 3110000
7 run 15 3120000
7 run 16 3120000
7 run 17 3120000
7 run 18 3140000
7 run 19 3120000
7 run 20 3110000
7 run 21 3120000
7 run 22 3110000
7 run 23 3110000
7 run 24 3130000
7 run 25 3110000
7 run 26 3110000
7 run 27 3120000
7 run 28 3110000
7 run 29 3120000
7 run 30 3120000
# point pass=7 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997982
7 halt 1 3110000
7 halt 2 3110000
7 halt 3 3110000
7 halt 4 3110000
7 halt 5 3110000
7 halt 6 3120000
7 halt 7 3130000
7 halt 8 3130000
7 halt 9 3110000
7 halt 10 3110000
7 halt 11 3110000
7 halt 12 3110000
7 halt 13 3120000
7 halt 14 3110000
7 halt 15 3140000
7 halt 16 3120000
7 halt 17 3110000
7 halt 18 3120000
7 halt 19 3110000
7 halt 20 3120000
7 halt 21 3110000
7 halt 22 3120000
7 halt 23 3110000
7 halt 24 3110000
7 halt 25 3110000
7 halt 26 3110000
7 halt 27 3110000
7 halt 28 3110000
7 halt 29 3110000
7 halt 30 3140000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997980
8 run 1 3110000
8 run 2 3100000
8 run 3 3120000
8 run 4 3120000
8 run 5 3110000
8 run 6 3100000
8 run 7 3100000
8 run 8 3100000
8 run 9 3100000
8 run 10 3110000
8 run 11 3110000
8 run 12 3110000
8 run 13 3100000
8 run 14 3100000
8 run 15 3110000
8 run 16 3110000
8 run 17 3100000
8 run 18 3110000
8 run 19 3110000
8 run 20 3100000
8 run 21 3100000
8 run 22 3110000
8 run 23 3100000
8 run 24 3110000
8 run 25 3100000
8 run 26 3110000
8 run 27 3110000
8 run 28 3110000
8 run 29 3110000
8 run 30 3110000
# point pass=8 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997980
8 halt 1 3100000
8 halt 2 3110000
8 halt 3 3110000
8 halt 4 3110000
8 halt 5 3120000
8 halt 6 3110000
8 halt 7 3100000
8 halt 8 3120000
8 halt 9 3120000
8 halt 10 3110000
8 halt 11 3110000
8 halt 12 3120000
8 halt 13 3110000
8 halt 14 3110000
8 halt 15 3130000
8 halt 16 3120000
8 halt 17 3110000
8 halt 18 3100000
8 halt 19 3110000
8 halt 20 3120000
8 halt 21 3150000
8 halt 22 3110000
8 halt 23 3140000
8 halt 24 3130000
8 halt 25 3120000
8 halt 26 3110000
8 halt 27 3110000
8 halt 28 3110000
8 halt 29 3110000
8 halt 30 3110000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997978
# done SystemTime { tv_sec: 1793910950, tv_nsec: 445594110 }
# passes used 8 of 8 (dropped: [])
# run - halt = -1.2 +/- 2.3 mW (per-pass sd 6.4); 1/8 passes positive; 4 sample excursions beyond 6 MAD
# per-pass differences (mW): 0.0 0.0 0.0 0.0 -10.0 -10.0 10.0 0.0
# board power while running 3111 mW; jobs/s 0.0; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 0.0 mW: measured/predicted = -inf, -0.6 sigma from it

View file

@ -0,0 +1,505 @@
# class solve-assign
# vqpu-edge meter passes=8 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793916714, tv_nsec: 341051123 }
# pass state sample power_uW
1 halt 1 3140000
1 halt 2 3110000
1 halt 3 3130000
1 halt 4 3110000
1 halt 5 3130000
1 halt 6 3120000
1 halt 7 3110000
1 halt 8 3120000
1 halt 9 3140000
1 halt 10 3130000
1 halt 11 3110000
1 halt 12 3120000
1 halt 13 3110000
1 halt 14 3120000
1 halt 15 3130000
1 halt 16 3110000
1 halt 17 3120000
1 halt 18 3140000
1 halt 19 3120000
1 halt 20 3120000
1 halt 21 3120000
1 halt 22 3110000
1 halt 23 3120000
1 halt 24 3110000
1 halt 25 3110000
1 halt 26 3110000
1 halt 27 3110000
1 halt 28 3110000
1 halt 29 3110000
1 halt 30 3120000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998005
1 run 1 3110000
1 run 2 3110000
1 run 3 3110000
1 run 4 3110000
1 run 5 3130000
1 run 6 3140000
1 run 7 3120000
1 run 8 3100000
1 run 9 3110000
1 run 10 3110000
1 run 11 3110000
1 run 12 3110000
1 run 13 3120000
1 run 14 3100000
1 run 15 3110000
1 run 16 3110000
1 run 17 3110000
1 run 18 3110000
1 run 19 3110000
1 run 20 3110000
1 run 21 3130000
1 run 22 3110000
1 run 23 3110000
1 run 24 3110000
1 run 25 3110000
1 run 26 3110000
1 run 27 3120000
1 run 28 3110000
1 run 29 3130000
1 run 30 3130000
# point pass=1 state=run jobs_per_s=94695.1 updates_per_s=33237978.4 clock_hz=99998002
2 halt 1 3100000
2 halt 2 3100000
2 halt 3 3120000
2 halt 4 3100000
2 halt 5 3100000
2 halt 6 3110000
2 halt 7 3100000
2 halt 8 3110000
2 halt 9 3110000
2 halt 10 3110000
2 halt 11 3110000
2 halt 12 3110000
2 halt 13 3110000
2 halt 14 3100000
2 halt 15 3100000
2 halt 16 3110000
2 halt 17 3100000
2 halt 18 3120000
2 halt 19 3100000
2 halt 20 3110000
2 halt 21 3120000
2 halt 22 3120000
2 halt 23 3110000
2 halt 24 3110000
2 halt 25 3110000
2 halt 26 3130000
2 halt 27 3310000
2 halt 28 3110000
2 halt 29 3110000
2 halt 30 3110000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998000
2 run 1 3120000
2 run 2 3120000
2 run 3 3110000
2 run 4 3110000
2 run 5 3110000
2 run 6 3120000
2 run 7 3120000
2 run 8 3110000
2 run 9 3120000
2 run 10 3110000
2 run 11 3110000
2 run 12 3110000
2 run 13 3110000
2 run 14 3110000
2 run 15 3140000
2 run 16 3120000
2 run 17 3120000
2 run 18 3110000
2 run 19 3130000
2 run 20 3110000
2 run 21 3130000
2 run 22 3110000
2 run 23 3140000
2 run 24 3110000
2 run 25 3120000
2 run 26 3110000
2 run 27 3120000
2 run 28 3120000
2 run 29 3110000
2 run 30 3110000
# point pass=2 state=run jobs_per_s=94695.1 updates_per_s=33237977.4 clock_hz=99997999
3 run 1 3110000
3 run 2 3120000
3 run 3 3130000
3 run 4 3120000
3 run 5 3110000
3 run 6 3120000
3 run 7 3120000
3 run 8 3110000
3 run 9 3120000
3 run 10 3110000
3 run 11 3140000
3 run 12 3140000
3 run 13 3110000
3 run 14 3120000
3 run 15 3120000
3 run 16 3120000
3 run 17 3110000
3 run 18 3130000
3 run 19 3120000
3 run 20 3130000
3 run 21 3120000
3 run 22 3110000
3 run 23 3130000
3 run 24 3110000
3 run 25 3110000
3 run 26 3120000
3 run 27 3110000
3 run 28 3120000
3 run 29 3120000
3 run 30 3120000
# point pass=3 state=run jobs_per_s=94695.1 updates_per_s=33237977.1 clock_hz=99997998
3 halt 1 3110000
3 halt 2 3110000
3 halt 3 3100000
3 halt 4 3100000
3 halt 5 3100000
3 halt 6 3100000
3 halt 7 3100000
3 halt 8 3120000
3 halt 9 3120000
3 halt 10 3100000
3 halt 11 3100000
3 halt 12 3100000
3 halt 13 3100000
3 halt 14 3100000
3 halt 15 3100000
3 halt 16 3110000
3 halt 17 3100000
3 halt 18 3100000
3 halt 19 3100000
3 halt 20 3100000
3 halt 21 3100000
3 halt 22 3100000
3 halt 23 3110000
3 halt 24 3100000
3 halt 25 3110000
3 halt 26 3110000
3 halt 27 3110000
3 halt 28 3090000
3 halt 29 3110000
3 halt 30 3100000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997997
4 run 1 3110000
4 run 2 3110000
4 run 3 3140000
4 run 4 3120000
4 run 5 3120000
4 run 6 3110000
4 run 7 3110000
4 run 8 3530000
4 run 9 3110000
4 run 10 3100000
4 run 11 3110000
4 run 12 3110000
4 run 13 3100000
4 run 14 3110000
4 run 15 3100000
4 run 16 3100000
4 run 17 3110000
4 run 18 3110000
4 run 19 3100000
4 run 20 3130000
4 run 21 3110000
4 run 22 3110000
4 run 23 3110000
4 run 24 3110000
4 run 25 3100000
4 run 26 3100000
4 run 27 3110000
4 run 28 3120000
4 run 29 3130000
4 run 30 3110000
# point pass=4 state=run jobs_per_s=94695.1 updates_per_s=33237993.2 clock_hz=99998046
4 halt 1 3110000
4 halt 2 3110000
4 halt 3 3100000
4 halt 4 3110000
4 halt 5 3110000
4 halt 6 3090000
4 halt 7 3100000
4 halt 8 3100000
4 halt 9 3110000
4 halt 10 3100000
4 halt 11 3110000
4 halt 12 3110000
4 halt 13 3100000
4 halt 14 3110000
4 halt 15 3100000
4 halt 16 3100000
4 halt 17 3120000
4 halt 18 3090000
4 halt 19 3100000
4 halt 20 3100000
4 halt 21 3100000
4 halt 22 3100000
4 halt 23 3100000
4 halt 24 3100000
4 halt 25 3110000
4 halt 26 3110000
4 halt 27 3100000
4 halt 28 3100000
4 halt 29 3110000
4 halt 30 3110000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998049
5 halt 1 3170000
5 halt 2 3110000
5 halt 3 3120000
5 halt 4 3100000
5 halt 5 3130000
5 halt 6 3120000
5 halt 7 3120000
5 halt 8 3100000
5 halt 9 3120000
5 halt 10 3120000
5 halt 11 3120000
5 halt 12 3120000
5 halt 13 3130000
5 halt 14 3140000
5 halt 15 3120000
5 halt 16 3110000
5 halt 17 3120000
5 halt 18 3110000
5 halt 19 3110000
5 halt 20 3110000
5 halt 21 3110000
5 halt 22 3130000
5 halt 23 3110000
5 halt 24 3110000
5 halt 25 3120000
5 halt 26 3110000
5 halt 27 3120000
5 halt 28 3110000
5 halt 29 3120000
5 halt 30 3120000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998040
5 run 1 3120000
5 run 2 3130000
5 run 3 3120000
5 run 4 3130000
5 run 5 3130000
5 run 6 3110000
5 run 7 3110000
5 run 8 3120000
5 run 9 3130000
5 run 10 3140000
5 run 11 3140000
5 run 12 3120000
5 run 13 3110000
5 run 14 3120000
5 run 15 3120000
5 run 16 3120000
5 run 17 3120000
5 run 18 3110000
5 run 19 3140000
5 run 20 3120000
5 run 21 3110000
5 run 22 3140000
5 run 23 3120000
5 run 24 3120000
5 run 25 3130000
5 run 26 3120000
5 run 27 3120000
5 run 28 3130000
5 run 29 3120000
5 run 30 3130000
# point pass=5 state=run jobs_per_s=94695.1 updates_per_s=33237988.9 clock_hz=99998033
6 run 1 3120000
6 run 2 3120000
6 run 3 3140000
6 run 4 3140000
6 run 5 3120000
6 run 6 3120000
6 run 7 3130000
6 run 8 3110000
6 run 9 3110000
6 run 10 3120000
6 run 11 3110000
6 run 12 3110000
6 run 13 3110000
6 run 14 3120000
6 run 15 3110000
6 run 16 3110000
6 run 17 3110000
6 run 18 3110000
6 run 19 3110000
6 run 20 3120000
6 run 21 3120000
6 run 22 3140000
6 run 23 3120000
6 run 24 3120000
6 run 25 3120000
6 run 26 3110000
6 run 27 3110000
6 run 28 3120000
6 run 29 3110000
6 run 30 3130000
# point pass=6 state=run jobs_per_s=94695.1 updates_per_s=33237987.0 clock_hz=99998028
6 halt 1 3110000
6 halt 2 3110000
6 halt 3 3110000
6 halt 4 3100000
6 halt 5 3150000
6 halt 6 3120000
6 halt 7 3110000
6 halt 8 3120000
6 halt 9 3110000
6 halt 10 3110000
6 halt 11 3100000
6 halt 12 3120000
6 halt 13 3110000
6 halt 14 3110000
6 halt 15 3120000
6 halt 16 3110000
6 halt 17 3110000
6 halt 18 3110000
6 halt 19 3110000
6 halt 20 3110000
6 halt 21 3110000
6 halt 22 3110000
6 halt 23 3110000
6 halt 24 3110000
6 halt 25 3110000
6 halt 26 3110000
6 halt 27 3120000
6 halt 28 3130000
6 halt 29 3130000
6 halt 30 3100000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998023
7 halt 1 3100000
7 halt 2 3110000
7 halt 3 3130000
7 halt 4 3110000
7 halt 5 3100000
7 halt 6 3110000
7 halt 7 3120000
7 halt 8 3120000
7 halt 9 3120000
7 halt 10 3120000
7 halt 11 3130000
7 halt 12 3110000
7 halt 13 3110000
7 halt 14 3130000
7 halt 15 3110000
7 halt 16 3110000
7 halt 17 3120000
7 halt 18 3120000
7 halt 19 3120000
7 halt 20 3140000
7 halt 21 3120000
7 halt 22 3120000
7 halt 23 3130000
7 halt 24 3110000
7 halt 25 3140000
7 halt 26 3130000
7 halt 27 3120000
7 halt 28 3120000
7 halt 29 3120000
7 halt 30 3110000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998019
7 run 1 3110000
7 run 2 3120000
7 run 3 3120000
7 run 4 3120000
7 run 5 3110000
7 run 6 3120000
7 run 7 3140000
7 run 8 3110000
7 run 9 3110000
7 run 10 3120000
7 run 11 3110000
7 run 12 3110000
7 run 13 3110000
7 run 14 3110000
7 run 15 3120000
7 run 16 3120000
7 run 17 3110000
7 run 18 3120000
7 run 19 3110000
7 run 20 3110000
7 run 21 3120000
7 run 22 3130000
7 run 23 3110000
7 run 24 3110000
7 run 25 3110000
7 run 26 3110000
7 run 27 3120000
7 run 28 3110000
7 run 29 3120000
7 run 30 3140000
# point pass=7 state=run jobs_per_s=94695.1 updates_per_s=33237983.0 clock_hz=99998016
8 run 1 3120000
8 run 2 3110000
8 run 3 3120000
8 run 4 3130000
8 run 5 3110000
8 run 6 3130000
8 run 7 3130000
8 run 8 3120000
8 run 9 3120000
8 run 10 3140000
8 run 11 3120000
8 run 12 3120000
8 run 13 3110000
8 run 14 3120000
8 run 15 3110000
8 run 16 3120000
8 run 17 3120000
8 run 18 3130000
8 run 19 3140000
8 run 20 3110000
8 run 21 3120000
8 run 22 3120000
8 run 23 3110000
8 run 24 3110000
8 run 25 3110000
8 run 26 3110000
8 run 27 3140000
8 run 28 3120000
8 run 29 3120000
8 run 30 3110000
# point pass=8 state=run jobs_per_s=94695.1 updates_per_s=33237982.4 clock_hz=99998013
8 halt 1 3090000
8 halt 2 3110000
8 halt 3 3110000
8 halt 4 3100000
8 halt 5 3110000
8 halt 6 3110000
8 halt 7 3120000
8 halt 8 3110000
8 halt 9 3110000
8 halt 10 3110000
8 halt 11 3110000
8 halt 12 3110000
8 halt 13 3110000
8 halt 14 3100000
8 halt 15 3120000
8 halt 16 3120000
8 halt 17 3110000
8 halt 18 3110000
8 halt 19 3110000
8 halt 20 3110000
8 halt 21 3120000
8 halt 22 3110000
8 halt 23 3110000
8 halt 24 3120000
8 halt 25 3100000
8 halt 26 3110000
8 halt 27 3100000
8 halt 28 3100000
8 halt 29 3100000
8 halt 30 3110000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998011
# done SystemTime { tv_sec: 1793918155, tv_nsec: 266403554 }
# passes used 8 of 8 (dropped: [])
# run - halt = 3.8 +/- 3.8 mW (per-pass sd 10.6); 4/8 passes positive; 3 sample excursions beyond 6 MAD
# per-pass differences (mW): -10.0 0.0 20.0 10.0 0.0 10.0 -10.0 10.0
# board power while running 3115 mW; jobs/s 94695.1; updates/s 33237983.4; measured PL clock 99.998 MHz
# pre-registered prediction 3.0 mW: measured/predicted = 1.25, 0.2 sigma from it

View file

@ -0,0 +1,610 @@
# class t0-recall
# vqpu-edge meter passes=1 settle=0s samples=300 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793906728, tv_nsec: 609413995 }
# pass state sample power_uW
1 halt 1 3360000
1 halt 2 3130000
1 halt 3 3120000
1 halt 4 3120000
1 halt 5 3120000
1 halt 6 3120000
1 halt 7 3130000
1 halt 8 3120000
1 halt 9 3120000
1 halt 10 3120000
1 halt 11 3120000
1 halt 12 3120000
1 halt 13 3120000
1 halt 14 3120000
1 halt 15 3120000
1 halt 16 3130000
1 halt 17 3120000
1 halt 18 3110000
1 halt 19 3120000
1 halt 20 3120000
1 halt 21 3110000
1 halt 22 3130000
1 halt 23 3120000
1 halt 24 3120000
1 halt 25 3120000
1 halt 26 3120000
1 halt 27 3120000
1 halt 28 3120000
1 halt 29 3130000
1 halt 30 3130000
1 halt 31 3130000
1 halt 32 3130000
1 halt 33 3110000
1 halt 34 3120000
1 halt 35 3140000
1 halt 36 3110000
1 halt 37 3120000
1 halt 38 3120000
1 halt 39 3110000
1 halt 40 3120000
1 halt 41 3120000
1 halt 42 3140000
1 halt 43 3110000
1 halt 44 3120000
1 halt 45 3140000
1 halt 46 3120000
1 halt 47 3110000
1 halt 48 3140000
1 halt 49 3120000
1 halt 50 3110000
1 halt 51 3110000
1 halt 52 3120000
1 halt 53 3120000
1 halt 54 3120000
1 halt 55 3110000
1 halt 56 3120000
1 halt 57 3140000
1 halt 58 3110000
1 halt 59 3150000
1 halt 60 3120000
1 halt 61 3120000
1 halt 62 3120000
1 halt 63 3110000
1 halt 64 3120000
1 halt 65 3130000
1 halt 66 3120000
1 halt 67 3130000
1 halt 68 3120000
1 halt 69 3120000
1 halt 70 3120000
1 halt 71 3120000
1 halt 72 3130000
1 halt 73 3120000
1 halt 74 3140000
1 halt 75 3110000
1 halt 76 3110000
1 halt 77 3110000
1 halt 78 3110000
1 halt 79 3170000
1 halt 80 3130000
1 halt 81 3110000
1 halt 82 3100000
1 halt 83 3110000
1 halt 84 3110000
1 halt 85 3110000
1 halt 86 3110000
1 halt 87 3120000
1 halt 88 3110000
1 halt 89 3110000
1 halt 90 3110000
1 halt 91 3110000
1 halt 92 3110000
1 halt 93 3130000
1 halt 94 3110000
1 halt 95 3110000
1 halt 96 3130000
1 halt 97 3110000
1 halt 98 3110000
1 halt 99 3110000
1 halt 100 3120000
1 halt 101 3130000
1 halt 102 3110000
1 halt 103 3090000
1 halt 104 3120000
1 halt 105 3110000
1 halt 106 3490000
1 halt 107 3120000
1 halt 108 3110000
1 halt 109 3110000
1 halt 110 3100000
1 halt 111 3110000
1 halt 112 3110000
1 halt 113 3120000
1 halt 114 3110000
1 halt 115 3100000
1 halt 116 3110000
1 halt 117 3130000
1 halt 118 3110000
1 halt 119 3110000
1 halt 120 3110000
1 halt 121 3120000
1 halt 122 3110000
1 halt 123 3110000
1 halt 124 3120000
1 halt 125 3130000
1 halt 126 3110000
1 halt 127 3120000
1 halt 128 3140000
1 halt 129 3120000
1 halt 130 3110000
1 halt 131 3110000
1 halt 132 3110000
1 halt 133 3110000
1 halt 134 3100000
1 halt 135 3110000
1 halt 136 3120000
1 halt 137 3120000
1 halt 138 3150000
1 halt 139 3110000
1 halt 140 3120000
1 halt 141 3120000
1 halt 142 3110000
1 halt 143 3130000
1 halt 144 3110000
1 halt 145 3140000
1 halt 146 3110000
1 halt 147 3120000
1 halt 148 3120000
1 halt 149 3110000
1 halt 150 3120000
1 halt 151 3120000
1 halt 152 3130000
1 halt 153 3120000
1 halt 154 3120000
1 halt 155 3140000
1 halt 156 3120000
1 halt 157 3120000
1 halt 158 3120000
1 halt 159 3120000
1 halt 160 3120000
1 halt 161 3120000
1 halt 162 3130000
1 halt 163 3130000
1 halt 164 3130000
1 halt 165 3110000
1 halt 166 3110000
1 halt 167 3120000
1 halt 168 3120000
1 halt 169 3120000
1 halt 170 3120000
1 halt 171 3120000
1 halt 172 3150000
1 halt 173 3110000
1 halt 174 3120000
1 halt 175 3120000
1 halt 176 3120000
1 halt 177 3120000
1 halt 178 3120000
1 halt 179 3140000
1 halt 180 3120000
1 halt 181 3120000
1 halt 182 3110000
1 halt 183 3140000
1 halt 184 3110000
1 halt 185 3120000
1 halt 186 3120000
1 halt 187 3120000
1 halt 188 3120000
1 halt 189 3120000
1 halt 190 3130000
1 halt 191 3120000
1 halt 192 3120000
1 halt 193 3110000
1 halt 194 3120000
1 halt 195 3120000
1 halt 196 3120000
1 halt 197 3110000
1 halt 198 3110000
1 halt 199 3110000
1 halt 200 3130000
1 halt 201 3130000
1 halt 202 3110000
1 halt 203 3110000
1 halt 204 3120000
1 halt 205 3110000
1 halt 206 3110000
1 halt 207 3130000
1 halt 208 3110000
1 halt 209 3110000
1 halt 210 3110000
1 halt 211 3110000
1 halt 212 3110000
1 halt 213 3120000
1 halt 214 3110000
1 halt 215 3110000
1 halt 216 3120000
1 halt 217 3120000
1 halt 218 3110000
1 halt 219 3110000
1 halt 220 3110000
1 halt 221 3110000
1 halt 222 3110000
1 halt 223 3110000
1 halt 224 3110000
1 halt 225 3110000
1 halt 226 3110000
1 halt 227 3110000
1 halt 228 3110000
1 halt 229 3110000
1 halt 230 3120000
1 halt 231 3120000
1 halt 232 3110000
1 halt 233 3110000
1 halt 234 3100000
1 halt 235 3110000
1 halt 236 3110000
1 halt 237 3100000
1 halt 238 3100000
1 halt 239 3120000
1 halt 240 3120000
1 halt 241 3110000
1 halt 242 3110000
1 halt 243 3110000
1 halt 244 3110000
1 halt 245 3100000
1 halt 246 3100000
1 halt 247 3110000
1 halt 248 3120000
1 halt 249 3100000
1 halt 250 3110000
1 halt 251 3110000
1 halt 252 3100000
1 halt 253 3110000
1 halt 254 3110000
1 halt 255 3110000
1 halt 256 3110000
1 halt 257 3100000
1 halt 258 3110000
1 halt 259 3110000
1 halt 260 3110000
1 halt 261 3120000
1 halt 262 3120000
1 halt 263 3140000
1 halt 264 3120000
1 halt 265 3120000
1 halt 266 3100000
1 halt 267 3120000
1 halt 268 3110000
1 halt 269 3120000
1 halt 270 3110000
1 halt 271 3140000
1 halt 272 3140000
1 halt 273 3120000
1 halt 274 3110000
1 halt 275 3120000
1 halt 276 3120000
1 halt 277 3110000
1 halt 278 3110000
1 halt 279 3110000
1 halt 280 3140000
1 halt 281 3110000
1 halt 282 3110000
1 halt 283 3110000
1 halt 284 3120000
1 halt 285 3110000
1 halt 286 3110000
1 halt 287 3120000
1 halt 288 3120000
1 halt 289 3110000
1 halt 290 3110000
1 halt 291 3110000
1 halt 292 3120000
1 halt 293 3120000
1 halt 294 3110000
1 halt 295 3120000
1 halt 296 3120000
1 halt 297 3110000
1 halt 298 3110000
1 halt 299 3130000
1 halt 300 3120000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997939
1 run 1 3490000
1 run 2 3260000
1 run 3 3260000
1 run 4 3260000
1 run 5 3280000
1 run 6 3270000
1 run 7 3260000
1 run 8 3260000
1 run 9 3270000
1 run 10 3260000
1 run 11 3260000
1 run 12 3260000
1 run 13 3260000
1 run 14 3270000
1 run 15 3290000
1 run 16 3260000
1 run 17 3260000
1 run 18 3260000
1 run 19 3260000
1 run 20 3280000
1 run 21 3260000
1 run 22 3260000
1 run 23 3270000
1 run 24 3250000
1 run 25 3250000
1 run 26 3250000
1 run 27 3250000
1 run 28 3260000
1 run 29 3280000
1 run 30 3260000
1 run 31 3250000
1 run 32 3260000
1 run 33 3250000
1 run 34 3260000
1 run 35 3250000
1 run 36 3260000
1 run 37 3250000
1 run 38 3250000
1 run 39 3250000
1 run 40 3250000
1 run 41 3250000
1 run 42 3250000
1 run 43 3250000
1 run 44 3260000
1 run 45 3250000
1 run 46 3250000
1 run 47 3250000
1 run 48 3260000
1 run 49 3250000
1 run 50 3250000
1 run 51 3250000
1 run 52 3280000
1 run 53 3260000
1 run 54 3260000
1 run 55 3260000
1 run 56 3260000
1 run 57 3260000
1 run 58 3250000
1 run 59 3260000
1 run 60 3250000
1 run 61 3260000
1 run 62 3250000
1 run 63 3260000
1 run 64 3310000
1 run 65 3260000
1 run 66 3250000
1 run 67 3250000
1 run 68 3260000
1 run 69 3250000
1 run 70 3250000
1 run 71 3250000
1 run 72 3250000
1 run 73 3250000
1 run 74 3250000
1 run 75 3250000
1 run 76 3270000
1 run 77 3250000
1 run 78 3250000
1 run 79 3250000
1 run 80 3250000
1 run 81 3250000
1 run 82 3260000
1 run 83 3280000
1 run 84 3280000
1 run 85 3280000
1 run 86 3280000
1 run 87 3270000
1 run 88 3260000
1 run 89 3260000
1 run 90 3250000
1 run 91 3270000
1 run 92 3260000
1 run 93 3280000
1 run 94 3280000
1 run 95 3260000
1 run 96 3260000
1 run 97 3270000
1 run 98 3260000
1 run 99 3270000
1 run 100 3260000
1 run 101 3260000
1 run 102 3270000
1 run 103 3260000
1 run 104 3260000
1 run 105 3250000
1 run 106 3260000
1 run 107 3260000
1 run 108 3280000
1 run 109 3260000
1 run 110 3260000
1 run 111 3260000
1 run 112 3270000
1 run 113 3280000
1 run 114 3270000
1 run 115 3260000
1 run 116 3290000
1 run 117 3280000
1 run 118 3260000
1 run 119 3260000
1 run 120 3270000
1 run 121 3260000
1 run 122 3270000
1 run 123 3260000
1 run 124 3260000
1 run 125 3270000
1 run 126 3250000
1 run 127 3500000
1 run 128 3260000
1 run 129 3260000
1 run 130 3260000
1 run 131 3270000
1 run 132 3460000
1 run 133 3260000
1 run 134 3260000
1 run 135 3260000
1 run 136 3280000
1 run 137 3260000
1 run 138 3260000
1 run 139 3280000
1 run 140 3280000
1 run 141 3260000
1 run 142 3260000
1 run 143 3260000
1 run 144 3270000
1 run 145 3270000
1 run 146 3260000
1 run 147 3250000
1 run 148 3280000
1 run 149 3260000
1 run 150 3260000
1 run 151 3260000
1 run 152 3260000
1 run 153 3260000
1 run 154 3250000
1 run 155 3260000
1 run 156 3260000
1 run 157 3260000
1 run 158 3260000
1 run 159 3260000
1 run 160 3260000
1 run 161 3260000
1 run 162 3260000
1 run 163 3280000
1 run 164 3260000
1 run 165 3270000
1 run 166 3260000
1 run 167 3260000
1 run 168 3260000
1 run 169 3260000
1 run 170 3260000
1 run 171 3290000
1 run 172 3290000
1 run 173 3260000
1 run 174 3270000
1 run 175 3260000
1 run 176 3260000
1 run 177 3260000
1 run 178 3270000
1 run 179 3260000
1 run 180 3280000
1 run 181 3260000
1 run 182 3260000
1 run 183 3260000
1 run 184 3270000
1 run 185 3260000
1 run 186 3260000
1 run 187 3260000
1 run 188 3270000
1 run 189 3260000
1 run 190 3260000
1 run 191 3260000
1 run 192 3260000
1 run 193 3260000
1 run 194 3250000
1 run 195 3280000
1 run 196 3260000
1 run 197 3260000
1 run 198 3260000
1 run 199 3270000
1 run 200 3260000
1 run 201 3260000
1 run 202 3260000
1 run 203 3290000
1 run 204 3280000
1 run 205 3260000
1 run 206 3250000
1 run 207 3280000
1 run 208 3260000
1 run 209 3260000
1 run 210 3260000
1 run 211 3260000
1 run 212 3260000
1 run 213 3260000
1 run 214 3270000
1 run 215 3270000
1 run 216 3250000
1 run 217 3250000
1 run 218 3280000
1 run 219 3260000
1 run 220 3280000
1 run 221 3250000
1 run 222 3250000
1 run 223 3260000
1 run 224 3270000
1 run 225 3260000
1 run 226 3270000
1 run 227 3280000
1 run 228 3260000
1 run 229 3260000
1 run 230 3260000
1 run 231 3260000
1 run 232 3270000
1 run 233 3260000
1 run 234 3260000
1 run 235 3280000
1 run 236 3260000
1 run 237 3260000
1 run 238 3260000
1 run 239 3260000
1 run 240 3260000
1 run 241 3260000
1 run 242 3260000
1 run 243 3250000
1 run 244 3260000
1 run 245 3260000
1 run 246 3260000
1 run 247 3260000
1 run 248 3260000
1 run 249 3280000
1 run 250 3270000
1 run 251 3260000
1 run 252 3260000
1 run 253 3260000
1 run 254 3260000
1 run 255 3260000
1 run 256 3260000
1 run 257 3270000
1 run 258 3280000
1 run 259 3290000
1 run 260 3260000
1 run 261 3260000
1 run 262 3260000
1 run 263 3260000
1 run 264 3260000
1 run 265 3260000
1 run 266 3260000
1 run 267 3270000
1 run 268 3260000
1 run 269 3260000
1 run 270 3260000
1 run 271 3260000
1 run 272 3250000
1 run 273 3260000
1 run 274 3270000
1 run 275 3260000
1 run 276 3260000
1 run 277 3260000
1 run 278 3260000
1 run 279 3260000
1 run 280 3290000
1 run 281 3250000
1 run 282 3280000
1 run 283 3260000
1 run 284 3270000
1 run 285 3260000
1 run 286 3250000
1 run 287 3260000
1 run 288 3260000
1 run 289 3260000
1 run 290 3280000
1 run 291 3260000
1 run 292 3250000
1 run 293 3260000
1 run 294 3270000
1 run 295 3250000
1 run 296 3260000
1 run 297 3260000
1 run 298 3260000
1 run 299 3260000
1 run 300 3260000
# point pass=1 state=run jobs_per_s=12499749.7 updates_per_s=0.0 clock_hz=99997996
# done SystemTime { tv_sec: 1793907328, tv_nsec: 966768242 }
# passes used 1 of 1 (dropped: [])
# run - halt = 140.0 +/- NaN mW (per-pass sd NaN); 1/1 passes positive; 6 sample excursions beyond 6 MAD
# per-pass differences (mW): 140.0
# board power while running 3260 mW; jobs/s 12499749.7; updates/s 0.0; measured PL clock 99.998 MHz

View file

@ -0,0 +1,505 @@
# class t0-idle
# vqpu-edge meter passes=8 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793915273, tv_nsec: 378650480 }
# pass state sample power_uW
1 run 1 3110000
1 run 2 3110000
1 run 3 3120000
1 run 4 3120000
1 run 5 3110000
1 run 6 3110000
1 run 7 3130000
1 run 8 3120000
1 run 9 3110000
1 run 10 3110000
1 run 11 3110000
1 run 12 3110000
1 run 13 3110000
1 run 14 3170000
1 run 15 3120000
1 run 16 3110000
1 run 17 3110000
1 run 18 3120000
1 run 19 3120000
1 run 20 3180000
1 run 21 3130000
1 run 22 3130000
1 run 23 3120000
1 run 24 3110000
1 run 25 3110000
1 run 26 3110000
1 run 27 3110000
1 run 28 3100000
1 run 29 3110000
1 run 30 3120000
# point pass=1 state=run jobs_per_s=33332741.4 updates_per_s=0.0 clock_hz=99998201
1 halt 1 3140000
1 halt 2 3110000
1 halt 3 3110000
1 halt 4 3110000
1 halt 5 3130000
1 halt 6 3110000
1 halt 7 3110000
1 halt 8 3110000
1 halt 9 3110000
1 halt 10 3110000
1 halt 11 3110000
1 halt 12 3110000
1 halt 13 3130000
1 halt 14 3110000
1 halt 15 3110000
1 halt 16 3110000
1 halt 17 3110000
1 halt 18 3120000
1 halt 19 3120000
1 halt 20 3120000
1 halt 21 3110000
1 halt 22 3110000
1 halt 23 3110000
1 halt 24 3100000
1 halt 25 3120000
1 halt 26 3110000
1 halt 27 3140000
1 halt 28 3110000
1 halt 29 3120000
1 halt 30 3120000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998166
2 run 1 3120000
2 run 2 3130000
2 run 3 3120000
2 run 4 3130000
2 run 5 3130000
2 run 6 3120000
2 run 7 3140000
2 run 8 3120000
2 run 9 3120000
2 run 10 3120000
2 run 11 3130000
2 run 12 3120000
2 run 13 3140000
2 run 14 3150000
2 run 15 3130000
2 run 16 3140000
2 run 17 3120000
2 run 18 3110000
2 run 19 3120000
2 run 20 3110000
2 run 21 3120000
2 run 22 3120000
2 run 23 3110000
2 run 24 3140000
2 run 25 3120000
2 run 26 3120000
2 run 27 3120000
2 run 28 3110000
2 run 29 3120000
2 run 30 3110000
# point pass=2 state=run jobs_per_s=33332720.0 updates_per_s=0.0 clock_hz=99998138
2 halt 1 3110000
2 halt 2 3110000
2 halt 3 3110000
2 halt 4 3140000
2 halt 5 3100000
2 halt 6 3110000
2 halt 7 3120000
2 halt 8 3110000
2 halt 9 3120000
2 halt 10 3120000
2 halt 11 3110000
2 halt 12 3120000
2 halt 13 3120000
2 halt 14 3110000
2 halt 15 3110000
2 halt 16 3100000
2 halt 17 3110000
2 halt 18 3110000
2 halt 19 3100000
2 halt 20 3110000
2 halt 21 3110000
2 halt 22 3110000
2 halt 23 3270000
2 halt 24 3110000
2 halt 25 3110000
2 halt 26 3100000
2 halt 27 3120000
2 halt 28 3460000
2 halt 29 3110000
2 halt 30 3110000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998114
3 halt 1 3120000
3 halt 2 3110000
3 halt 3 3110000
3 halt 4 3100000
3 halt 5 3110000
3 halt 6 3110000
3 halt 7 3100000
3 halt 8 3100000
3 halt 9 3150000
3 halt 10 3110000
3 halt 11 3100000
3 halt 12 3100000
3 halt 13 3110000
3 halt 14 3100000
3 halt 15 3110000
3 halt 16 3110000
3 halt 17 3110000
3 halt 18 3100000
3 halt 19 3110000
3 halt 20 3120000
3 halt 21 3120000
3 halt 22 3120000
3 halt 23 3110000
3 halt 24 3150000
3 halt 25 3110000
3 halt 26 3120000
3 halt 27 3120000
3 halt 28 3110000
3 halt 29 3110000
3 halt 30 3120000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998094
3 run 1 3110000
3 run 2 3120000
3 run 3 3120000
3 run 4 3110000
3 run 5 3120000
3 run 6 3140000
3 run 7 3110000
3 run 8 3110000
3 run 9 3120000
3 run 10 3110000
3 run 11 3120000
3 run 12 3120000
3 run 13 3120000
3 run 14 3120000
3 run 15 3110000
3 run 16 3120000
3 run 17 3120000
3 run 18 3120000
3 run 19 3120000
3 run 20 3120000
3 run 21 3140000
3 run 22 3110000
3 run 23 3110000
3 run 24 3120000
3 run 25 3110000
3 run 26 3120000
3 run 27 3120000
3 run 28 3110000
3 run 29 3140000
3 run 30 3140000
# point pass=3 state=run jobs_per_s=33332700.4 updates_per_s=0.0 clock_hz=99998079
4 run 1 3110000
4 run 2 3110000
4 run 3 3130000
4 run 4 3110000
4 run 5 3120000
4 run 6 3110000
4 run 7 3110000
4 run 8 3110000
4 run 9 3110000
4 run 10 3110000
4 run 11 3120000
4 run 12 3110000
4 run 13 3130000
4 run 14 3120000
4 run 15 3120000
4 run 16 3110000
4 run 17 3130000
4 run 18 3130000
4 run 19 3110000
4 run 20 3110000
4 run 21 3120000
4 run 22 3100000
4 run 23 3120000
4 run 24 3120000
4 run 25 3120000
4 run 26 3130000
4 run 27 3110000
4 run 28 3110000
4 run 29 3120000
4 run 30 3110000
# point pass=4 state=run jobs_per_s=33332695.3 updates_per_s=0.0 clock_hz=99998064
4 halt 1 3100000
4 halt 2 3110000
4 halt 3 3100000
4 halt 4 3110000
4 halt 5 3110000
4 halt 6 3120000
4 halt 7 3110000
4 halt 8 3110000
4 halt 9 3100000
4 halt 10 3110000
4 halt 11 3100000
4 halt 12 3110000
4 halt 13 3100000
4 halt 14 3120000
4 halt 15 3110000
4 halt 16 3110000
4 halt 17 3110000
4 halt 18 3110000
4 halt 19 3110000
4 halt 20 3100000
4 halt 21 3110000
4 halt 22 3110000
4 halt 23 3120000
4 halt 24 3100000
4 halt 25 3110000
4 halt 26 3110000
4 halt 27 3090000
4 halt 28 3100000
4 halt 29 3090000
4 halt 30 3110000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998052
5 run 1 3140000
5 run 2 3120000
5 run 3 3110000
5 run 4 3110000
5 run 5 3120000
5 run 6 3140000
5 run 7 3120000
5 run 8 3110000
5 run 9 3110000
5 run 10 3110000
5 run 11 3110000
5 run 12 3110000
5 run 13 3120000
5 run 14 3120000
5 run 15 3130000
5 run 16 3120000
5 run 17 3110000
5 run 18 3120000
5 run 19 3120000
5 run 20 3110000
5 run 21 3110000
5 run 22 3110000
5 run 23 3130000
5 run 24 3110000
5 run 25 3110000
5 run 26 3120000
5 run 27 3120000
5 run 28 3120000
5 run 29 3110000
5 run 30 3110000
# point pass=5 state=run jobs_per_s=33332688.2 updates_per_s=0.0 clock_hz=99998042
5 halt 1 3110000
5 halt 2 3120000
5 halt 3 3130000
5 halt 4 3110000
5 halt 5 3110000
5 halt 6 3100000
5 halt 7 3100000
5 halt 8 3110000
5 halt 9 3110000
5 halt 10 3120000
5 halt 11 3120000
5 halt 12 3110000
5 halt 13 3110000
5 halt 14 3120000
5 halt 15 3110000
5 halt 16 3100000
5 halt 17 3110000
5 halt 18 3120000
5 halt 19 3110000
5 halt 20 3100000
5 halt 21 3110000
5 halt 22 3110000
5 halt 23 3110000
5 halt 24 3110000
5 halt 25 3110000
5 halt 26 3120000
5 halt 27 3110000
5 halt 28 3110000
5 halt 29 3110000
5 halt 30 3110000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998034
6 halt 1 3120000
6 halt 2 3110000
6 halt 3 3110000
6 halt 4 3110000
6 halt 5 3110000
6 halt 6 3110000
6 halt 7 3120000
6 halt 8 3140000
6 halt 9 3110000
6 halt 10 3120000
6 halt 11 3110000
6 halt 12 3110000
6 halt 13 3110000
6 halt 14 3110000
6 halt 15 3110000
6 halt 16 3120000
6 halt 17 3120000
6 halt 18 3110000
6 halt 19 3110000
6 halt 20 3120000
6 halt 21 3120000
6 halt 22 3100000
6 halt 23 3130000
6 halt 24 3110000
6 halt 25 3110000
6 halt 26 3110000
6 halt 27 3110000
6 halt 28 3110000
6 halt 29 3110000
6 halt 30 3110000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998028
6 run 1 3110000
6 run 2 3110000
6 run 3 3110000
6 run 4 3120000
6 run 5 3130000
6 run 6 3110000
6 run 7 3110000
6 run 8 3110000
6 run 9 3110000
6 run 10 3110000
6 run 11 3120000
6 run 12 3110000
6 run 13 3120000
6 run 14 3110000
6 run 15 3110000
6 run 16 3120000
6 run 17 3120000
6 run 18 3110000
6 run 19 3110000
6 run 20 3140000
6 run 21 3120000
6 run 22 3120000
6 run 23 3120000
6 run 24 3110000
6 run 25 3120000
6 run 26 3120000
6 run 27 3110000
6 run 28 3140000
6 run 29 3120000
6 run 30 3120000
# point pass=6 state=run jobs_per_s=33332681.5 updates_per_s=0.0 clock_hz=99998021
7 run 1 3110000
7 run 2 3110000
7 run 3 3110000
7 run 4 3110000
7 run 5 3110000
7 run 6 3110000
7 run 7 3110000
7 run 8 3120000
7 run 9 3110000
7 run 10 3110000
7 run 11 3110000
7 run 12 3110000
7 run 13 3110000
7 run 14 3100000
7 run 15 3100000
7 run 16 3120000
7 run 17 3120000
7 run 18 3110000
7 run 19 3100000
7 run 20 3110000
7 run 21 3110000
7 run 22 3120000
7 run 23 3110000
7 run 24 3110000
7 run 25 3140000
7 run 26 3110000
7 run 27 3100000
7 run 28 3110000
7 run 29 3110000
7 run 30 3110000
# point pass=7 state=run jobs_per_s=33332680.2 updates_per_s=0.0 clock_hz=99998017
7 halt 1 3110000
7 halt 2 3110000
7 halt 3 3110000
7 halt 4 3130000
7 halt 5 3130000
7 halt 6 3110000
7 halt 7 3110000
7 halt 8 3120000
7 halt 9 3110000
7 halt 10 3120000
7 halt 11 3110000
7 halt 12 3100000
7 halt 13 3130000
7 halt 14 3130000
7 halt 15 3100000
7 halt 16 3110000
7 halt 17 3120000
7 halt 18 3120000
7 halt 19 3120000
7 halt 20 3110000
7 halt 21 3110000
7 halt 22 3140000
7 halt 23 3110000
7 halt 24 3110000
7 halt 25 3110000
7 halt 26 3110000
7 halt 27 3120000
7 halt 28 3120000
7 halt 29 3120000
7 halt 30 3110000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998014
8 run 1 3110000
8 run 2 3130000
8 run 3 3110000
8 run 4 3110000
8 run 5 3110000
8 run 6 3110000
8 run 7 3110000
8 run 8 3110000
8 run 9 3110000
8 run 10 3130000
8 run 11 3110000
8 run 12 3110000
8 run 13 3110000
8 run 14 3110000
8 run 15 3120000
8 run 16 3110000
8 run 17 3110000
8 run 18 3110000
8 run 19 3110000
8 run 20 3120000
8 run 21 3110000
8 run 22 3110000
8 run 23 3110000
8 run 24 3110000
8 run 25 3130000
8 run 26 3120000
8 run 27 3110000
8 run 28 3120000
8 run 29 3110000
8 run 30 3130000
# point pass=8 state=run jobs_per_s=33332678.3 updates_per_s=0.0 clock_hz=99998009
8 halt 1 3110000
8 halt 2 3110000
8 halt 3 3110000
8 halt 4 3110000
8 halt 5 3110000
8 halt 6 3110000
8 halt 7 3120000
8 halt 8 3120000
8 halt 9 3110000
8 halt 10 3100000
8 halt 11 3110000
8 halt 12 3110000
8 halt 13 3110000
8 halt 14 3110000
8 halt 15 3110000
8 halt 16 3110000
8 halt 17 3100000
8 halt 18 3100000
8 halt 19 3110000
8 halt 20 3110000
8 halt 21 3120000
8 halt 22 3130000
8 halt 23 3110000
8 halt 24 3110000
8 halt 25 3110000
8 halt 26 3110000
8 halt 27 3110000
8 halt 28 3110000
8 halt 29 3110000
8 halt 30 3140000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998006
# done SystemTime { tv_sec: 1793916714, tv_nsec: 281430358 }
# passes used 8 of 8 (dropped: [])
# run - halt = 3.8 +/- 1.6 mW (per-pass sd 4.4); 4/8 passes positive; 4 sample excursions beyond 6 MAD
# per-pass differences (mW): 0.0 10.0 10.0 0.0 5.0 5.0 0.0 0.0
# board power while running 3114 mW; jobs/s 33332698.2; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 1.0 mW: measured/predicted = 3.75, 1.8 sigma from it

View file

@ -0,0 +1,754 @@
# class t0-recall cores 4
# vqpu-edge meter passes=12 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793920187, tv_nsec: 861113367 }
# pass state sample power_uW
1 halt 1 3250000
1 halt 2 3250000
1 halt 3 3250000
1 halt 4 3250000
1 halt 5 3250000
1 halt 6 3280000
1 halt 7 3250000
1 halt 8 3250000
1 halt 9 3260000
1 halt 10 3260000
1 halt 11 3250000
1 halt 12 3260000
1 halt 13 3240000
1 halt 14 3260000
1 halt 15 3240000
1 halt 16 3250000
1 halt 17 3250000
1 halt 18 3250000
1 halt 19 3250000
1 halt 20 3240000
1 halt 21 3240000
1 halt 22 3250000
1 halt 23 3250000
1 halt 24 3250000
1 halt 25 3250000
1 halt 26 3240000
1 halt 27 3260000
1 halt 28 3250000
1 halt 29 3260000
1 halt 30 3250000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997938
1 run 1 3840000
1 run 2 3820000
1 run 3 3840000
1 run 4 3850000
1 run 5 3830000
1 run 6 3840000
1 run 7 3830000
1 run 8 4070000
1 run 9 3830000
1 run 10 3830000
1 run 11 3830000
1 run 12 3830000
1 run 13 3840000
1 run 14 3830000
1 run 15 3840000
1 run 16 3830000
1 run 17 3840000
1 run 18 3850000
1 run 19 3830000
1 run 20 3840000
1 run 21 3830000
1 run 22 3830000
1 run 23 3830000
1 run 24 3840000
1 run 25 3840000
1 run 26 3820000
1 run 27 3830000
1 run 28 3840000
1 run 29 3850000
1 run 30 3830000
# point pass=1 state=run jobs_per_s=12499746.0 updates_per_s=0.0 clock_hz=99997944
2 halt 1 3260000
2 halt 2 3260000
2 halt 3 3260000
2 halt 4 3260000
2 halt 5 3250000
2 halt 6 3250000
2 halt 7 3260000
2 halt 8 3250000
2 halt 9 3260000
2 halt 10 3260000
2 halt 11 3270000
2 halt 12 3260000
2 halt 13 3250000
2 halt 14 3260000
2 halt 15 3260000
2 halt 16 3260000
2 halt 17 3260000
2 halt 18 3250000
2 halt 19 3280000
2 halt 20 3250000
2 halt 21 3260000
2 halt 22 3250000
2 halt 23 3250000
2 halt 24 3260000
2 halt 25 3260000
2 halt 26 3260000
2 halt 27 3260000
2 halt 28 3260000
2 halt 29 3260000
2 halt 30 3260000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997946
2 run 1 3840000
2 run 2 3830000
2 run 3 3840000
2 run 4 3840000
2 run 5 3850000
2 run 6 3840000
2 run 7 3850000
2 run 8 3830000
2 run 9 3840000
2 run 10 3850000
2 run 11 3910000
2 run 12 3830000
2 run 13 3840000
2 run 14 3840000
2 run 15 3840000
2 run 16 3830000
2 run 17 3850000
2 run 18 3840000
2 run 19 3830000
2 run 20 3850000
2 run 21 3840000
2 run 22 3840000
2 run 23 3840000
2 run 24 3840000
2 run 25 3850000
2 run 26 3840000
2 run 27 3840000
2 run 28 3850000
2 run 29 3840000
2 run 30 3840000
# point pass=2 state=run jobs_per_s=12499746.6 updates_per_s=0.0 clock_hz=99997951
3 halt 1 3240000
3 halt 2 3250000
3 halt 3 3250000
3 halt 4 3250000
3 halt 5 3250000
3 halt 6 3240000
3 halt 7 3250000
3 halt 8 3250000
3 halt 9 3250000
3 halt 10 3250000
3 halt 11 3240000
3 halt 12 3240000
3 halt 13 3250000
3 halt 14 3250000
3 halt 15 3250000
3 halt 16 3260000
3 halt 17 3260000
3 halt 18 3250000
3 halt 19 3250000
3 halt 20 3270000
3 halt 21 3250000
3 halt 22 3270000
3 halt 23 3240000
3 halt 24 3250000
3 halt 25 3250000
3 halt 26 3240000
3 halt 27 3250000
3 halt 28 3250000
3 halt 29 3240000
3 halt 30 3250000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997953
3 run 1 3850000
3 run 2 3830000
3 run 3 3840000
3 run 4 3830000
3 run 5 3850000
3 run 6 3870000
3 run 7 3840000
3 run 8 3830000
3 run 9 3840000
3 run 10 3840000
3 run 11 3820000
3 run 12 3840000
3 run 13 3840000
3 run 14 3860000
3 run 15 3830000
3 run 16 3840000
3 run 17 3830000
3 run 18 3830000
3 run 19 3840000
3 run 20 3830000
3 run 21 3830000
3 run 22 3860000
3 run 23 3830000
3 run 24 3840000
3 run 25 3840000
3 run 26 3830000
3 run 27 3840000
3 run 28 3830000
3 run 29 3840000
3 run 30 3850000
# point pass=3 state=run jobs_per_s=12499747.2 updates_per_s=0.0 clock_hz=99997955
4 run 1 3830000
4 run 2 3840000
4 run 3 3830000
4 run 4 3830000
4 run 5 3830000
4 run 6 3840000
4 run 7 3830000
4 run 8 3820000
4 run 9 3820000
4 run 10 3820000
4 run 11 3830000
4 run 12 3820000
4 run 13 3830000
4 run 14 3850000
4 run 15 3830000
4 run 16 3820000
4 run 17 3840000
4 run 18 3830000
4 run 19 3830000
4 run 20 3820000
4 run 21 3830000
4 run 22 3840000
4 run 23 3820000
4 run 24 3830000
4 run 25 3840000
4 run 26 3840000
4 run 27 3820000
4 run 28 3830000
4 run 29 3820000
4 run 30 3820000
# point pass=4 state=run jobs_per_s=12499747.5 updates_per_s=0.0 clock_hz=99997957
4 halt 1 3240000
4 halt 2 3240000
4 halt 3 3240000
4 halt 4 3230000
4 halt 5 3240000
4 halt 6 3240000
4 halt 7 3240000
4 halt 8 3250000
4 halt 9 3250000
4 halt 10 3240000
4 halt 11 3240000
4 halt 12 3240000
4 halt 13 3230000
4 halt 14 3250000
4 halt 15 3240000
4 halt 16 3240000
4 halt 17 3230000
4 halt 18 3240000
4 halt 19 3230000
4 halt 20 3240000
4 halt 21 3240000
4 halt 22 3260000
4 halt 23 3250000
4 halt 24 3240000
4 halt 25 3230000
4 halt 26 3240000
4 halt 27 3250000
4 halt 28 3240000
4 halt 29 3240000
4 halt 30 3240000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997959
5 halt 1 3250000
5 halt 2 3240000
5 halt 3 3250000
5 halt 4 3250000
5 halt 5 3240000
5 halt 6 3250000
5 halt 7 3230000
5 halt 8 3240000
5 halt 9 3250000
5 halt 10 3250000
5 halt 11 3260000
5 halt 12 3240000
5 halt 13 3250000
5 halt 14 3240000
5 halt 15 3240000
5 halt 16 3240000
5 halt 17 3250000
5 halt 18 3240000
5 halt 19 3270000
5 halt 20 3250000
5 halt 21 3240000
5 halt 22 3240000
5 halt 23 3250000
5 halt 24 3250000
5 halt 25 3250000
5 halt 26 3250000
5 halt 27 3260000
5 halt 28 3240000
5 halt 29 3250000
5 halt 30 3240000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997960
5 run 1 3830000
5 run 2 3830000
5 run 3 3840000
5 run 4 3840000
5 run 5 3830000
5 run 6 3830000
5 run 7 3850000
5 run 8 3840000
5 run 9 3820000
5 run 10 3830000
5 run 11 3840000
5 run 12 3830000
5 run 13 3840000
5 run 14 3830000
5 run 15 3830000
5 run 16 3840000
5 run 17 3830000
5 run 18 3910000
5 run 19 3830000
5 run 20 3830000
5 run 21 3830000
5 run 22 3830000
5 run 23 4190000
5 run 24 3830000
5 run 25 3830000
5 run 26 3840000
5 run 27 3830000
5 run 28 3830000
5 run 29 3830000
5 run 30 3830000
# point pass=5 state=run jobs_per_s=12499747.9 updates_per_s=0.0 clock_hz=99997961
6 halt 1 3240000
6 halt 2 3250000
6 halt 3 3260000
6 halt 4 3260000
6 halt 5 3280000
6 halt 6 3250000
6 halt 7 3250000
6 halt 8 3250000
6 halt 9 3250000
6 halt 10 3250000
6 halt 11 3240000
6 halt 12 3240000
6 halt 13 3260000
6 halt 14 3260000
6 halt 15 3240000
6 halt 16 3250000
6 halt 17 3250000
6 halt 18 3250000
6 halt 19 3250000
6 halt 20 3250000
6 halt 21 3250000
6 halt 22 3250000
6 halt 23 3250000
6 halt 24 3250000
6 halt 25 3250000
6 halt 26 3250000
6 halt 27 3250000
6 halt 28 3260000
6 halt 29 3250000
6 halt 30 3250000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997962
6 run 1 3840000
6 run 2 3830000
6 run 3 3830000
6 run 4 3830000
6 run 5 3830000
6 run 6 3820000
6 run 7 3820000
6 run 8 3840000
6 run 9 3820000
6 run 10 3830000
6 run 11 3830000
6 run 12 3830000
6 run 13 3830000
6 run 14 3830000
6 run 15 3830000
6 run 16 3840000
6 run 17 3820000
6 run 18 3830000
6 run 19 3840000
6 run 20 3830000
6 run 21 3830000
6 run 22 3830000
6 run 23 3830000
6 run 24 3850000
6 run 25 3850000
6 run 26 3840000
6 run 27 3830000
6 run 28 3830000
6 run 29 3820000
6 run 30 3840000
# point pass=6 state=run jobs_per_s=12499748.0 updates_per_s=0.0 clock_hz=99997963
7 run 1 3830000
7 run 2 3830000
7 run 3 3830000
7 run 4 3830000
7 run 5 3840000
7 run 6 3830000
7 run 7 3830000
7 run 8 3830000
7 run 9 3820000
7 run 10 3830000
7 run 11 3830000
7 run 12 3830000
7 run 13 3850000
7 run 14 3830000
7 run 15 3840000
7 run 16 3830000
7 run 17 3840000
7 run 18 3830000
7 run 19 3830000
7 run 20 3830000
7 run 21 3850000
7 run 22 3850000
7 run 23 3830000
7 run 24 3830000
7 run 25 3840000
7 run 26 3840000
7 run 27 3830000
7 run 28 3830000
7 run 29 3830000
7 run 30 3840000
# point pass=7 state=run jobs_per_s=12499748.1 updates_per_s=0.0 clock_hz=99997963
7 halt 1 3250000
7 halt 2 3250000
7 halt 3 3240000
7 halt 4 3240000
7 halt 5 3250000
7 halt 6 3240000
7 halt 7 3240000
7 halt 8 3240000
7 halt 9 3250000
7 halt 10 3270000
7 halt 11 3250000
7 halt 12 3250000
7 halt 13 3240000
7 halt 14 3240000
7 halt 15 3240000
7 halt 16 3240000
7 halt 17 3250000
7 halt 18 3260000
7 halt 19 3250000
7 halt 20 3240000
7 halt 21 3240000
7 halt 22 3250000
7 halt 23 3250000
7 halt 24 3240000
7 halt 25 3250000
7 halt 26 3250000
7 halt 27 3240000
7 halt 28 3240000
7 halt 29 3240000
7 halt 30 3250000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998258
8 run 1 3840000
8 run 2 3850000
8 run 3 3830000
8 run 4 3840000
8 run 5 3840000
8 run 6 3860000
8 run 7 3850000
8 run 8 3840000
8 run 9 3840000
8 run 10 3850000
8 run 11 3840000
8 run 12 3840000
8 run 13 3840000
8 run 14 3840000
8 run 15 3870000
8 run 16 3840000
8 run 17 3820000
8 run 18 3840000
8 run 19 3840000
8 run 20 3840000
8 run 21 3840000
8 run 22 3850000
8 run 23 3840000
8 run 24 3830000
8 run 25 3840000
8 run 26 3850000
8 run 27 3840000
8 run 28 3840000
8 run 29 3840000
8 run 30 3860000
# point pass=8 state=run jobs_per_s=12499779.7 updates_per_s=0.0 clock_hz=99998216
8 halt 1 3240000
8 halt 2 3260000
8 halt 3 3260000
8 halt 4 3260000
8 halt 5 3240000
8 halt 6 3240000
8 halt 7 3250000
8 halt 8 3240000
8 halt 9 3240000
8 halt 10 3250000
8 halt 11 3240000
8 halt 12 3260000
8 halt 13 3240000
8 halt 14 3250000
8 halt 15 3250000
8 halt 16 3240000
8 halt 17 3250000
8 halt 18 3250000
8 halt 19 3240000
8 halt 20 3250000
8 halt 21 3240000
8 halt 22 3250000
8 halt 23 3240000
8 halt 24 3240000
8 halt 25 3250000
8 halt 26 3240000
8 halt 27 3260000
8 halt 28 3240000
8 halt 29 3240000
8 halt 30 3240000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998181
9 run 1 3840000
9 run 2 3840000
9 run 3 3830000
9 run 4 3840000
9 run 5 3830000
9 run 6 3830000
9 run 7 3840000
9 run 8 3840000
9 run 9 3840000
9 run 10 3830000
9 run 11 3830000
9 run 12 3830000
9 run 13 3840000
9 run 14 3830000
9 run 15 3850000
9 run 16 3830000
9 run 17 3830000
9 run 18 3840000
9 run 19 3840000
9 run 20 3850000
9 run 21 3830000
9 run 22 3830000
9 run 23 3850000
9 run 24 3850000
9 run 25 3840000
9 run 26 3840000
9 run 27 3830000
9 run 28 3830000
9 run 29 3840000
9 run 30 3840000
# point pass=9 state=run jobs_per_s=12499771.7 updates_per_s=0.0 clock_hz=99998152
9 halt 1 3250000
9 halt 2 3260000
9 halt 3 3260000
9 halt 4 3270000
9 halt 5 3250000
9 halt 6 3250000
9 halt 7 3250000
9 halt 8 3250000
9 halt 9 3250000
9 halt 10 3250000
9 halt 11 3260000
9 halt 12 3270000
9 halt 13 3250000
9 halt 14 3250000
9 halt 15 3260000
9 halt 16 3250000
9 halt 17 3260000
9 halt 18 3260000
9 halt 19 3260000
9 halt 20 3280000
9 halt 21 3280000
9 halt 22 3250000
9 halt 23 3250000
9 halt 24 3260000
9 halt 25 3260000
9 halt 26 3250000
9 halt 27 3250000
9 halt 28 3250000
9 halt 29 3280000
9 halt 30 3260000
# point pass=9 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998128
10 run 1 3840000
10 run 2 3830000
10 run 3 3840000
10 run 4 3850000
10 run 5 3830000
10 run 6 3840000
10 run 7 3840000
10 run 8 3840000
10 run 9 3860000
10 run 10 3840000
10 run 11 3840000
10 run 12 3830000
10 run 13 3840000
10 run 14 3840000
10 run 15 3840000
10 run 16 3850000
10 run 17 3870000
10 run 18 3840000
10 run 19 3840000
10 run 20 3840000
10 run 21 3840000
10 run 22 3840000
10 run 23 3840000
10 run 24 3840000
10 run 25 3840000
10 run 26 3840000
10 run 27 3840000
10 run 28 3840000
10 run 29 3840000
10 run 30 3850000
# point pass=10 state=run jobs_per_s=12499766.3 updates_per_s=0.0 clock_hz=99998107
10 halt 1 3250000
10 halt 2 3260000
10 halt 3 3250000
10 halt 4 3260000
10 halt 5 3270000
10 halt 6 3270000
10 halt 7 3240000
10 halt 8 3250000
10 halt 9 3260000
10 halt 10 3250000
10 halt 11 3250000
10 halt 12 3250000
10 halt 13 3250000
10 halt 14 3270000
10 halt 15 3250000
10 halt 16 3240000
10 halt 17 3250000
10 halt 18 3250000
10 halt 19 3250000
10 halt 20 3250000
10 halt 21 3240000
10 halt 22 3250000
10 halt 23 3240000
10 halt 24 3240000
10 halt 25 3240000
10 halt 26 3240000
10 halt 27 3240000
10 halt 28 3250000
10 halt 29 3250000
10 halt 30 3250000
# point pass=10 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998091
11 run 1 3850000
11 run 2 3850000
11 run 3 3840000
11 run 4 3840000
11 run 5 3840000
11 run 6 3840000
11 run 7 3830000
11 run 8 3840000
11 run 9 3830000
11 run 10 3830000
11 run 11 3850000
11 run 12 3840000
11 run 13 3830000
11 run 14 3850000
11 run 15 3830000
11 run 16 3840000
11 run 17 3840000
11 run 18 3840000
11 run 19 3840000
11 run 20 3840000
11 run 21 3850000
11 run 22 3830000
11 run 23 3830000
11 run 24 3830000
11 run 25 3840000
11 run 26 4180000
11 run 27 3840000
11 run 28 3820000
11 run 29 3830000
11 run 30 3830000
# point pass=11 state=run jobs_per_s=12499762.5 updates_per_s=0.0 clock_hz=99998076
11 halt 1 3250000
11 halt 2 3250000
11 halt 3 3390000
11 halt 4 3240000
11 halt 5 3250000
11 halt 6 3250000
11 halt 7 3240000
11 halt 8 3250000
11 halt 9 3250000
11 halt 10 3240000
11 halt 11 3250000
11 halt 12 3250000
11 halt 13 3250000
11 halt 14 3260000
11 halt 15 3250000
11 halt 16 3250000
11 halt 17 3250000
11 halt 18 3250000
11 halt 19 3250000
11 halt 20 3250000
11 halt 21 3260000
11 halt 22 3270000
11 halt 23 3270000
11 halt 24 3250000
11 halt 25 3250000
11 halt 26 3250000
11 halt 27 3240000
11 halt 28 3250000
11 halt 29 3250000
11 halt 30 3250000
# point pass=11 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998063
12 run 1 3820000
12 run 2 3820000
12 run 3 3840000
12 run 4 3820000
12 run 5 3830000
12 run 6 3830000
12 run 7 3830000
12 run 8 3830000
12 run 9 3820000
12 run 10 3830000
12 run 11 3840000
12 run 12 3820000
12 run 13 3830000
12 run 14 3820000
12 run 15 3820000
12 run 16 3830000
12 run 17 3830000
12 run 18 3820000
12 run 19 3840000
12 run 20 3830000
12 run 21 3820000
12 run 22 3830000
12 run 23 3830000
12 run 24 3830000
12 run 25 3810000
12 run 26 3810000
12 run 27 3820000
12 run 28 3840000
12 run 29 3820000
12 run 30 3810000
# point pass=12 state=run jobs_per_s=12499759.3 updates_per_s=0.0 clock_hz=99998053
12 halt 1 3250000
12 halt 2 3250000
12 halt 3 3250000
12 halt 4 3250000
12 halt 5 3250000
12 halt 6 3250000
12 halt 7 3250000
12 halt 8 3270000
12 halt 9 3250000
12 halt 10 3240000
12 halt 11 3240000
12 halt 12 3240000
12 halt 13 3250000
12 halt 14 3240000
12 halt 15 3250000
12 halt 16 3260000
12 halt 17 3250000
12 halt 18 3250000
12 halt 19 3240000
12 halt 20 3240000
12 halt 21 3250000
12 halt 22 3240000
12 halt 23 3250000
12 halt 24 3250000
12 halt 25 3240000
12 halt 26 3250000
12 halt 27 3240000
12 halt 28 3240000
12 halt 29 3250000
12 halt 30 3240000
# point pass=12 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998045
# done SystemTime { tv_sec: 1793922349, tv_nsec: 231452861 }
# passes used 12 of 12 (dropped: [])
# run - halt = 585.8 +/- 1.8 mW (per-pass sd 6.3); 12/12 passes positive; 6 sample excursions beyond 6 MAD
# per-pass differences (mW): 580.0 580.0 590.0 590.0 580.0 580.0 585.0 600.0 585.0 590.0 590.0 580.0
# board power while running 3835 mW; jobs/s 12499755.9; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 488.0 mW: measured/predicted = 1.20, 53.5 sigma from it
# jobs/s and updates/s above are core 0's; all 4 cores run the same job

View file

@ -0,0 +1,505 @@
# class t0-recall
# vqpu-edge meter passes=8 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793918156, tv_nsec: 433522612 }
# pass state sample power_uW
1 halt 1 3100000
1 halt 2 3090000
1 halt 3 3110000
1 halt 4 3100000
1 halt 5 3100000
1 halt 6 3100000
1 halt 7 3130000
1 halt 8 3110000
1 halt 9 3100000
1 halt 10 3100000
1 halt 11 3110000
1 halt 12 3110000
1 halt 13 3100000
1 halt 14 3090000
1 halt 15 3090000
1 halt 16 3100000
1 halt 17 3100000
1 halt 18 3100000
1 halt 19 3080000
1 halt 20 3110000
1 halt 21 3100000
1 halt 22 3110000
1 halt 23 3100000
1 halt 24 3090000
1 halt 25 3090000
1 halt 26 3080000
1 halt 27 3090000
1 halt 28 3090000
1 halt 29 3090000
1 halt 30 3110000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999004
1 run 1 3160000
1 run 2 3160000
1 run 3 3160000
1 run 4 3170000
1 run 5 3160000
1 run 6 3160000
1 run 7 3160000
1 run 8 3160000
1 run 9 3160000
1 run 10 3160000
1 run 11 3160000
1 run 12 3160000
1 run 13 3160000
1 run 14 3160000
1 run 15 3160000
1 run 16 3160000
1 run 17 3160000
1 run 18 3160000
1 run 19 3170000
1 run 20 3160000
1 run 21 3160000
1 run 22 3160000
1 run 23 3160000
1 run 24 3160000
1 run 25 3160000
1 run 26 3160000
1 run 27 3160000
1 run 28 3180000
1 run 29 3170000
1 run 30 3160000
# point pass=1 state=run jobs_per_s=6249876.9 updates_per_s=0.0 clock_hz=49999004
2 halt 1 3100000
2 halt 2 3080000
2 halt 3 3090000
2 halt 4 3090000
2 halt 5 3080000
2 halt 6 3080000
2 halt 7 3080000
2 halt 8 3080000
2 halt 9 3080000
2 halt 10 3080000
2 halt 11 3080000
2 halt 12 3090000
2 halt 13 3080000
2 halt 14 3080000
2 halt 15 3100000
2 halt 16 3100000
2 halt 17 3080000
2 halt 18 3080000
2 halt 19 3080000
2 halt 20 3080000
2 halt 21 3080000
2 halt 22 3080000
2 halt 23 3090000
2 halt 24 3110000
2 halt 25 3080000
2 halt 26 3090000
2 halt 27 3080000
2 halt 28 3080000
2 halt 29 3100000
2 halt 30 3080000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999003
2 run 1 3150000
2 run 2 3150000
2 run 3 3150000
2 run 4 3170000
2 run 5 3150000
2 run 6 3150000
2 run 7 3160000
2 run 8 3150000
2 run 9 3160000
2 run 10 3150000
2 run 11 3150000
2 run 12 3180000
2 run 13 3170000
2 run 14 3150000
2 run 15 3160000
2 run 16 3150000
2 run 17 3160000
2 run 18 3150000
2 run 19 3150000
2 run 20 3160000
2 run 21 3180000
2 run 22 3160000
2 run 23 3160000
2 run 24 3160000
2 run 25 3160000
2 run 26 3160000
2 run 27 3160000
2 run 28 3160000
2 run 29 3160000
2 run 30 3160000
# point pass=2 state=run jobs_per_s=6249876.7 updates_per_s=0.0 clock_hz=49999003
3 run 1 3180000
3 run 2 3170000
3 run 3 3160000
3 run 4 3160000
3 run 5 3160000
3 run 6 3170000
3 run 7 3170000
3 run 8 3160000
3 run 9 3180000
3 run 10 3200000
3 run 11 3160000
3 run 12 3170000
3 run 13 3170000
3 run 14 3170000
3 run 15 3170000
3 run 16 3160000
3 run 17 3160000
3 run 18 3180000
3 run 19 3160000
3 run 20 3160000
3 run 21 3160000
3 run 22 3160000
3 run 23 3160000
3 run 24 3150000
3 run 25 3160000
3 run 26 3160000
3 run 27 3150000
3 run 28 3160000
3 run 29 3160000
3 run 30 3160000
# point pass=3 state=run jobs_per_s=6249876.7 updates_per_s=0.0 clock_hz=49999002
3 halt 1 3100000
3 halt 2 3080000
3 halt 3 3100000
3 halt 4 3090000
3 halt 5 3100000
3 halt 6 3110000
3 halt 7 3100000
3 halt 8 3100000
3 halt 9 3100000
3 halt 10 3090000
3 halt 11 3100000
3 halt 12 3100000
3 halt 13 3090000
3 halt 14 3100000
3 halt 15 3100000
3 halt 16 3110000
3 halt 17 3100000
3 halt 18 3100000
3 halt 19 3090000
3 halt 20 3100000
3 halt 21 3100000
3 halt 22 3090000
3 halt 23 3100000
3 halt 24 3100000
3 halt 25 3100000
3 halt 26 3100000
3 halt 27 3090000
3 halt 28 3100000
3 halt 29 3110000
3 halt 30 3110000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999003
4 run 1 3170000
4 run 2 3170000
4 run 3 3190000
4 run 4 3170000
4 run 5 3170000
4 run 6 3170000
4 run 7 3170000
4 run 8 3170000
4 run 9 3170000
4 run 10 3170000
4 run 11 3170000
4 run 12 3160000
4 run 13 3170000
4 run 14 3160000
4 run 15 3160000
4 run 16 3170000
4 run 17 3170000
4 run 18 3180000
4 run 19 3160000
4 run 20 3180000
4 run 21 3170000
4 run 22 3170000
4 run 23 3160000
4 run 24 3170000
4 run 25 3170000
4 run 26 3190000
4 run 27 3190000
4 run 28 3160000
4 run 29 3160000
4 run 30 3160000
# point pass=4 state=run jobs_per_s=6249876.6 updates_per_s=0.0 clock_hz=49999001
4 halt 1 3090000
4 halt 2 3080000
4 halt 3 3100000
4 halt 4 3090000
4 halt 5 3100000
4 halt 6 3080000
4 halt 7 3090000
4 halt 8 3090000
4 halt 9 3100000
4 halt 10 3100000
4 halt 11 3100000
4 halt 12 3090000
4 halt 13 3080000
4 halt 14 3090000
4 halt 15 3110000
4 halt 16 3080000
4 halt 17 3080000
4 halt 18 3100000
4 halt 19 3090000
4 halt 20 3080000
4 halt 21 3090000
4 halt 22 3090000
4 halt 23 3110000
4 halt 24 3100000
4 halt 25 3110000
4 halt 26 3100000
4 halt 27 3080000
4 halt 28 3090000
4 halt 29 3080000
4 halt 30 3090000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999001
5 run 1 3170000
5 run 2 3170000
5 run 3 3180000
5 run 4 3180000
5 run 5 3160000
5 run 6 3160000
5 run 7 3180000
5 run 8 3170000
5 run 9 3160000
5 run 10 3170000
5 run 11 3160000
5 run 12 3180000
5 run 13 3190000
5 run 14 3170000
5 run 15 3160000
5 run 16 3170000
5 run 17 3180000
5 run 18 3170000
5 run 19 3170000
5 run 20 3170000
5 run 21 3190000
5 run 22 3200000
5 run 23 3170000
5 run 24 3170000
5 run 25 3170000
5 run 26 3170000
5 run 27 3180000
5 run 28 3170000
5 run 29 3170000
5 run 30 3180000
# point pass=5 state=run jobs_per_s=6249876.4 updates_per_s=0.0 clock_hz=49999001
5 halt 1 3090000
5 halt 2 3100000
5 halt 3 3090000
5 halt 4 3100000
5 halt 5 3090000
5 halt 6 3090000
5 halt 7 3100000
5 halt 8 3100000
5 halt 9 3090000
5 halt 10 3100000
5 halt 11 3100000
5 halt 12 3420000
5 halt 13 3090000
5 halt 14 3120000
5 halt 15 3080000
5 halt 16 3090000
5 halt 17 3180000
5 halt 18 3110000
5 halt 19 3090000
5 halt 20 3090000
5 halt 21 3080000
5 halt 22 3090000
5 halt 23 3090000
5 halt 24 3090000
5 halt 25 3090000
5 halt 26 3080000
5 halt 27 3090000
5 halt 28 3100000
5 halt 29 3090000
5 halt 30 3100000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999000
6 halt 1 3090000
6 halt 2 3090000
6 halt 3 3080000
6 halt 4 3090000
6 halt 5 3080000
6 halt 6 3120000
6 halt 7 3100000
6 halt 8 3080000
6 halt 9 3090000
6 halt 10 3090000
6 halt 11 3080000
6 halt 12 3090000
6 halt 13 3100000
6 halt 14 3100000
6 halt 15 3110000
6 halt 16 3100000
6 halt 17 3100000
6 halt 18 3100000
6 halt 19 3090000
6 halt 20 3100000
6 halt 21 3100000
6 halt 22 3090000
6 halt 23 3100000
6 halt 24 3080000
6 halt 25 3090000
6 halt 26 3100000
6 halt 27 3100000
6 halt 28 3090000
6 halt 29 3200000
6 halt 30 3110000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999000
6 run 1 3170000
6 run 2 3160000
6 run 3 3190000
6 run 4 3190000
6 run 5 3160000
6 run 6 3160000
6 run 7 3160000
6 run 8 3170000
6 run 9 3170000
6 run 10 3160000
6 run 11 3180000
6 run 12 3190000
6 run 13 3160000
6 run 14 3170000
6 run 15 3160000
6 run 16 3170000
6 run 17 3160000
6 run 18 3170000
6 run 19 3160000
6 run 20 3170000
6 run 21 3170000
6 run 22 3160000
6 run 23 3160000
6 run 24 3160000
6 run 25 3160000
6 run 26 3170000
6 run 27 3170000
6 run 28 3170000
6 run 29 3170000
6 run 30 3170000
# point pass=6 state=run jobs_per_s=6249876.5 updates_per_s=0.0 clock_hz=49999000
7 halt 1 3100000
7 halt 2 3080000
7 halt 3 3080000
7 halt 4 3090000
7 halt 5 3090000
7 halt 6 3100000
7 halt 7 3100000
7 halt 8 3100000
7 halt 9 3100000
7 halt 10 3090000
7 halt 11 3100000
7 halt 12 3100000
7 halt 13 3090000
7 halt 14 3100000
7 halt 15 3110000
7 halt 16 3100000
7 halt 17 3080000
7 halt 18 3090000
7 halt 19 3090000
7 halt 20 3080000
7 halt 21 3090000
7 halt 22 3090000
7 halt 23 3110000
7 halt 24 3110000
7 halt 25 3100000
7 halt 26 3090000
7 halt 27 3090000
7 halt 28 3100000
7 halt 29 3100000
7 halt 30 3100000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999001
7 run 1 3160000
7 run 2 3160000
7 run 3 3160000
7 run 4 3160000
7 run 5 3160000
7 run 6 3160000
7 run 7 3160000
7 run 8 3170000
7 run 9 3160000
7 run 10 3160000
7 run 11 3160000
7 run 12 3180000
7 run 13 3160000
7 run 14 3160000
7 run 15 3170000
7 run 16 3160000
7 run 17 3160000
7 run 18 3160000
7 run 19 3160000
7 run 20 3170000
7 run 21 3190000
7 run 22 3160000
7 run 23 3150000
7 run 24 3160000
7 run 25 3160000
7 run 26 3160000
7 run 27 3160000
7 run 28 3160000
7 run 29 3180000
7 run 30 3160000
# point pass=7 state=run jobs_per_s=6249864.5 updates_per_s=0.0 clock_hz=49998905
8 run 1 3170000
8 run 2 3170000
8 run 3 3170000
8 run 4 3170000
8 run 5 3170000
8 run 6 3160000
8 run 7 3170000
8 run 8 3170000
8 run 9 3190000
8 run 10 3160000
8 run 11 3160000
8 run 12 3160000
8 run 13 3170000
8 run 14 3160000
8 run 15 3170000
8 run 16 3160000
8 run 17 3190000
8 run 18 3170000
8 run 19 3160000
8 run 20 3160000
8 run 21 3160000
8 run 22 3170000
8 run 23 3160000
8 run 24 3160000
8 run 25 3160000
8 run 26 3160000
8 run 27 3170000
8 run 28 3170000
8 run 29 3160000
8 run 30 3170000
# point pass=8 state=run jobs_per_s=6249866.0 updates_per_s=0.0 clock_hz=49998917
8 halt 1 3090000
8 halt 2 3100000
8 halt 3 3090000
8 halt 4 3100000
8 halt 5 3110000
8 halt 6 3110000
8 halt 7 3100000
8 halt 8 3100000
8 halt 9 3090000
8 halt 10 3100000
8 halt 11 3090000
8 halt 12 3100000
8 halt 13 3090000
8 halt 14 3110000
8 halt 15 3100000
8 halt 16 3090000
8 halt 17 3100000
8 halt 18 3100000
8 halt 19 3090000
8 halt 20 3100000
8 halt 21 3090000
8 halt 22 3100000
8 halt 23 3090000
8 halt 24 3090000
8 halt 25 3100000
8 halt 26 3090000
8 halt 27 3110000
8 halt 28 3100000
8 halt 29 3110000
8 halt 30 3090000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49998928
# done SystemTime { tv_sec: 1793919597, tv_nsec: 315661140 }
# passes used 8 of 8 (dropped: [])
# run - halt = 70.6 +/- 3.3 mW (per-pass sd 9.4); 8/8 passes positive; 4 sample excursions beyond 6 MAD
# per-pass differences (mW): 60.0 80.0 60.0 80.0 80.0 75.0 60.0 70.0
# board power while running 3165 mW; jobs/s 6249873.8; updates/s 0.0; measured PL clock 49.999 MHz
# pre-registered prediction 61.0 mW: measured/predicted = 1.16, 2.9 sigma from it

View file

@ -0,0 +1,753 @@
# class t0-recall
# vqpu-edge meter passes=12 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793910950, tv_nsec: 505668490 }
# pass state sample power_uW
1 run 1 3260000
1 run 2 3250000
1 run 3 3270000
1 run 4 3260000
1 run 5 3260000
1 run 6 3260000
1 run 7 3260000
1 run 8 3260000
1 run 9 3250000
1 run 10 3250000
1 run 11 3260000
1 run 12 3250000
1 run 13 3250000
1 run 14 3260000
1 run 15 3260000
1 run 16 3260000
1 run 17 3250000
1 run 18 3260000
1 run 19 3260000
1 run 20 3250000
1 run 21 3250000
1 run 22 3260000
1 run 23 3260000
1 run 24 3250000
1 run 25 3270000
1 run 26 3280000
1 run 27 3250000
1 run 28 3260000
1 run 29 3260000
1 run 30 3260000
# point pass=1 state=run jobs_per_s=12499750.0 updates_per_s=0.0 clock_hz=99997978
1 halt 1 3120000
1 halt 2 3110000
1 halt 3 3100000
1 halt 4 3110000
1 halt 5 3110000
1 halt 6 3110000
1 halt 7 3120000
1 halt 8 3130000
1 halt 9 3110000
1 halt 10 3110000
1 halt 11 3110000
1 halt 12 3110000
1 halt 13 3100000
1 halt 14 3120000
1 halt 15 3110000
1 halt 16 3110000
1 halt 17 3110000
1 halt 18 3110000
1 halt 19 3110000
1 halt 20 3110000
1 halt 21 3100000
1 halt 22 3110000
1 halt 23 3130000
1 halt 24 3110000
1 halt 25 3110000
1 halt 26 3110000
1 halt 27 3100000
1 halt 28 3110000
1 halt 29 3100000
1 halt 30 3110000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997978
2 run 1 3260000
2 run 2 3260000
2 run 3 3260000
2 run 4 3260000
2 run 5 3270000
2 run 6 3250000
2 run 7 3250000
2 run 8 3260000
2 run 9 3250000
2 run 10 3250000
2 run 11 3260000
2 run 12 3250000
2 run 13 3260000
2 run 14 3260000
2 run 15 3260000
2 run 16 3260000
2 run 17 3260000
2 run 18 3250000
2 run 19 3260000
2 run 20 3290000
2 run 21 3250000
2 run 22 3250000
2 run 23 3250000
2 run 24 3260000
2 run 25 3260000
2 run 26 3260000
2 run 27 3270000
2 run 28 3280000
2 run 29 3260000
2 run 30 3260000
# point pass=2 state=run jobs_per_s=12499741.8 updates_per_s=0.0 clock_hz=99997912
2 halt 1 3110000
2 halt 2 3110000
2 halt 3 3110000
2 halt 4 3100000
2 halt 5 3100000
2 halt 6 3110000
2 halt 7 3110000
2 halt 8 3120000
2 halt 9 3100000
2 halt 10 3110000
2 halt 11 3120000
2 halt 12 3110000
2 halt 13 3110000
2 halt 14 3120000
2 halt 15 3110000
2 halt 16 3120000
2 halt 17 3140000
2 halt 18 3110000
2 halt 19 3110000
2 halt 20 3110000
2 halt 21 3110000
2 halt 22 3110000
2 halt 23 3110000
2 halt 24 3110000
2 halt 25 3120000
2 halt 26 3110000
2 halt 27 3110000
2 halt 28 3110000
2 halt 29 3110000
2 halt 30 3110000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997921
3 halt 1 3120000
3 halt 2 3110000
3 halt 3 3110000
3 halt 4 3110000
3 halt 5 3130000
3 halt 6 3110000
3 halt 7 3110000
3 halt 8 3100000
3 halt 9 3110000
3 halt 10 3110000
3 halt 11 3120000
3 halt 12 3110000
3 halt 13 3120000
3 halt 14 3110000
3 halt 15 3120000
3 halt 16 3110000
3 halt 17 3110000
3 halt 18 3100000
3 halt 19 3110000
3 halt 20 3110000
3 halt 21 3120000
3 halt 22 3110000
3 halt 23 3110000
3 halt 24 3110000
3 halt 25 3110000
3 halt 26 3110000
3 halt 27 3120000
3 halt 28 3110000
3 halt 29 3100000
3 halt 30 3110000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997928
3 run 1 3260000
3 run 2 3280000
3 run 3 3250000
3 run 4 3250000
3 run 5 3250000
3 run 6 3260000
3 run 7 3250000
3 run 8 3260000
3 run 9 3250000
3 run 10 3270000
3 run 11 3250000
3 run 12 3260000
3 run 13 3270000
3 run 14 3260000
3 run 15 3260000
3 run 16 3260000
3 run 17 3260000
3 run 18 3270000
3 run 19 3270000
3 run 20 3260000
3 run 21 3260000
3 run 22 3260000
3 run 23 3270000
3 run 24 3250000
3 run 25 3290000
3 run 26 3270000
3 run 27 3260000
3 run 28 3260000
3 run 29 3260000
3 run 30 3260000
# point pass=3 state=run jobs_per_s=12499744.6 updates_per_s=0.0 clock_hz=99997935
4 run 1 3250000
4 run 2 3260000
4 run 3 3260000
4 run 4 3250000
4 run 5 3260000
4 run 6 3250000
4 run 7 3290000
4 run 8 3260000
4 run 9 3250000
4 run 10 3260000
4 run 11 3260000
4 run 12 3250000
4 run 13 3250000
4 run 14 3250000
4 run 15 3250000
4 run 16 3260000
4 run 17 3260000
4 run 18 3260000
4 run 19 3260000
4 run 20 3250000
4 run 21 3270000
4 run 22 3260000
4 run 23 3250000
4 run 24 3260000
4 run 25 3250000
4 run 26 3500000
4 run 27 3260000
4 run 28 3260000
4 run 29 3260000
4 run 30 3270000
# point pass=4 state=run jobs_per_s=12499745.3 updates_per_s=0.0 clock_hz=99997940
4 halt 1 3100000
4 halt 2 3100000
4 halt 3 3110000
4 halt 4 3110000
4 halt 5 3100000
4 halt 6 3100000
4 halt 7 3110000
4 halt 8 3110000
4 halt 9 3100000
4 halt 10 3110000
4 halt 11 3110000
4 halt 12 3100000
4 halt 13 3100000
4 halt 14 3110000
4 halt 15 3110000
4 halt 16 3120000
4 halt 17 3120000
4 halt 18 3130000
4 halt 19 3140000
4 halt 20 3110000
4 halt 21 3110000
4 halt 22 3110000
4 halt 23 3110000
4 halt 24 3110000
4 halt 25 3110000
4 halt 26 3110000
4 halt 27 3130000
4 halt 28 3110000
4 halt 29 3110000
4 halt 30 3110000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997942
5 run 1 3260000
5 run 2 3260000
5 run 3 3280000
5 run 4 3260000
5 run 5 3260000
5 run 6 3270000
5 run 7 3290000
5 run 8 3270000
5 run 9 3260000
5 run 10 3270000
5 run 11 3260000
5 run 12 3260000
5 run 13 3270000
5 run 14 3260000
5 run 15 3290000
5 run 16 3290000
5 run 17 3270000
5 run 18 3260000
5 run 19 3260000
5 run 20 3270000
5 run 21 3270000
5 run 22 3260000
5 run 23 3270000
5 run 24 3280000
5 run 25 3260000
5 run 26 3260000
5 run 27 3270000
5 run 28 3270000
5 run 29 3260000
5 run 30 3270000
# point pass=5 state=run jobs_per_s=12499746.0 updates_per_s=0.0 clock_hz=99997946
5 halt 1 3120000
5 halt 2 3120000
5 halt 3 3120000
5 halt 4 3140000
5 halt 5 3120000
5 halt 6 3110000
5 halt 7 3130000
5 halt 8 3120000
5 halt 9 3110000
5 halt 10 3130000
5 halt 11 3130000
5 halt 12 3140000
5 halt 13 3120000
5 halt 14 3110000
5 halt 15 3130000
5 halt 16 3110000
5 halt 17 3110000
5 halt 18 3110000
5 halt 19 3110000
5 halt 20 3120000
5 halt 21 3110000
5 halt 22 3120000
5 halt 23 3110000
5 halt 24 3110000
5 halt 25 3100000
5 halt 26 3110000
5 halt 27 3120000
5 halt 28 3100000
5 halt 29 3100000
5 halt 30 3110000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997949
6 halt 1 3140000
6 halt 2 3500000
6 halt 3 3100000
6 halt 4 3110000
6 halt 5 3120000
6 halt 6 3120000
6 halt 7 3110000
6 halt 8 3120000
6 halt 9 3130000
6 halt 10 3110000
6 halt 11 3110000
6 halt 12 3110000
6 halt 13 3120000
6 halt 14 3110000
6 halt 15 3120000
6 halt 16 3120000
6 halt 17 3110000
6 halt 18 3120000
6 halt 19 3120000
6 halt 20 3110000
6 halt 21 3110000
6 halt 22 3110000
6 halt 23 3120000
6 halt 24 3130000
6 halt 25 3120000
6 halt 26 3110000
6 halt 27 3120000
6 halt 28 3120000
6 halt 29 3110000
6 halt 30 3110000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997951
6 run 1 3250000
6 run 2 3260000
6 run 3 3250000
6 run 4 3260000
6 run 5 3250000
6 run 6 3280000
6 run 7 3250000
6 run 8 3250000
6 run 9 3260000
6 run 10 3260000
6 run 11 3250000
6 run 12 3250000
6 run 13 3250000
6 run 14 3260000
6 run 15 3250000
6 run 16 3250000
6 run 17 3250000
6 run 18 3250000
6 run 19 3250000
6 run 20 3260000
6 run 21 3270000
6 run 22 3250000
6 run 23 3260000
6 run 24 3260000
6 run 25 3260000
6 run 26 3260000
6 run 27 3270000
6 run 28 3260000
6 run 29 3290000
6 run 30 3260000
# point pass=6 state=run jobs_per_s=12499746.9 updates_per_s=0.0 clock_hz=99997953
7 halt 1 3100000
7 halt 2 3110000
7 halt 3 3100000
7 halt 4 3110000
7 halt 5 3100000
7 halt 6 3100000
7 halt 7 3090000
7 halt 8 3110000
7 halt 9 3110000
7 halt 10 3100000
7 halt 11 3100000
7 halt 12 3110000
7 halt 13 3100000
7 halt 14 3110000
7 halt 15 3110000
7 halt 16 3110000
7 halt 17 3120000
7 halt 18 3120000
7 halt 19 3100000
7 halt 20 3100000
7 halt 21 3090000
7 halt 22 3100000
7 halt 23 3110000
7 halt 24 3110000
7 halt 25 3110000
7 halt 26 3120000
7 halt 27 3100000
7 halt 28 3100000
7 halt 29 3100000
7 halt 30 3110000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997955
7 run 1 3240000
7 run 2 3250000
7 run 3 3250000
7 run 4 3270000
7 run 5 3250000
7 run 6 3270000
7 run 7 3250000
7 run 8 3260000
7 run 9 3240000
7 run 10 3250000
7 run 11 3250000
7 run 12 3240000
7 run 13 3250000
7 run 14 3270000
7 run 15 3270000
7 run 16 3250000
7 run 17 3260000
7 run 18 3250000
7 run 19 3240000
7 run 20 3250000
7 run 21 3240000
7 run 22 3250000
7 run 23 3270000
7 run 24 3250000
7 run 25 3250000
7 run 26 3250000
7 run 27 3250000
7 run 28 3250000
7 run 29 3250000
7 run 30 3250000
# point pass=7 state=run jobs_per_s=12499747.3 updates_per_s=0.0 clock_hz=99997956
8 halt 1 3110000
8 halt 2 3110000
8 halt 3 3120000
8 halt 4 3110000
8 halt 5 3110000
8 halt 6 3110000
8 halt 7 3110000
8 halt 8 3110000
8 halt 9 3130000
8 halt 10 3100000
8 halt 11 3120000
8 halt 12 3110000
8 halt 13 3110000
8 halt 14 3100000
8 halt 15 3100000
8 halt 16 3100000
8 halt 17 3110000
8 halt 18 3100000
8 halt 19 3110000
8 halt 20 3120000
8 halt 21 3110000
8 halt 22 3110000
8 halt 23 3110000
8 halt 24 3110000
8 halt 25 3110000
8 halt 26 3120000
8 halt 27 3110000
8 halt 28 3120000
8 halt 29 3120000
8 halt 30 3120000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997958
8 run 1 3250000
8 run 2 3250000
8 run 3 3250000
8 run 4 3260000
8 run 5 3250000
8 run 6 3250000
8 run 7 3250000
8 run 8 3260000
8 run 9 3250000
8 run 10 3250000
8 run 11 3250000
8 run 12 3250000
8 run 13 3250000
8 run 14 3250000
8 run 15 3250000
8 run 16 3250000
8 run 17 3240000
8 run 18 3250000
8 run 19 3250000
8 run 20 3250000
8 run 21 3250000
8 run 22 3260000
8 run 23 3270000
8 run 24 3250000
8 run 25 3250000
8 run 26 3250000
8 run 27 3250000
8 run 28 3240000
8 run 29 3250000
8 run 30 3260000
# point pass=8 state=run jobs_per_s=12499747.6 updates_per_s=0.0 clock_hz=99997959
9 halt 1 3110000
9 halt 2 3100000
9 halt 3 3100000
9 halt 4 3100000
9 halt 5 3120000
9 halt 6 3110000
9 halt 7 3110000
9 halt 8 3100000
9 halt 9 3100000
9 halt 10 3100000
9 halt 11 3100000
9 halt 12 3100000
9 halt 13 3100000
9 halt 14 3110000
9 halt 15 3100000
9 halt 16 3100000
9 halt 17 3110000
9 halt 18 3100000
9 halt 19 3090000
9 halt 20 3110000
9 halt 21 3100000
9 halt 22 3100000
9 halt 23 3100000
9 halt 24 3100000
9 halt 25 3100000
9 halt 26 3090000
9 halt 27 3100000
9 halt 28 3120000
9 halt 29 3100000
9 halt 30 3100000
# point pass=9 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997960
9 run 1 3260000
9 run 2 3250000
9 run 3 3260000
9 run 4 3260000
9 run 5 3260000
9 run 6 3260000
9 run 7 3260000
9 run 8 3280000
9 run 9 3260000
9 run 10 3250000
9 run 11 3260000
9 run 12 3270000
9 run 13 3260000
9 run 14 3270000
9 run 15 3260000
9 run 16 3280000
9 run 17 3280000
9 run 18 3260000
9 run 19 3260000
9 run 20 3260000
9 run 21 3260000
9 run 22 3260000
9 run 23 3260000
9 run 24 3260000
9 run 25 3280000
9 run 26 3260000
9 run 27 3270000
9 run 28 3260000
9 run 29 3260000
9 run 30 3260000
# point pass=9 state=run jobs_per_s=12499747.8 updates_per_s=0.0 clock_hz=99997960
10 run 1 3260000
10 run 2 3260000
10 run 3 3260000
10 run 4 3260000
10 run 5 3270000
10 run 6 3260000
10 run 7 3260000
10 run 8 3260000
10 run 9 3260000
10 run 10 3270000
10 run 11 3260000
10 run 12 3260000
10 run 13 3290000
10 run 14 3280000
10 run 15 3260000
10 run 16 3260000
10 run 17 3260000
10 run 18 3260000
10 run 19 3260000
10 run 20 3270000
10 run 21 3270000
10 run 22 3270000
10 run 23 3260000
10 run 24 3270000
10 run 25 3270000
10 run 26 3260000
10 run 27 3270000
10 run 28 3270000
10 run 29 3260000
10 run 30 3260000
# point pass=10 state=run jobs_per_s=12499747.9 updates_per_s=0.0 clock_hz=99997961
10 halt 1 3140000
10 halt 2 3140000
10 halt 3 3120000
10 halt 4 3120000
10 halt 5 3120000
10 halt 6 3120000
10 halt 7 3120000
10 halt 8 3120000
10 halt 9 3120000
10 halt 10 3130000
10 halt 11 3120000
10 halt 12 3120000
10 halt 13 3110000
10 halt 14 3110000
10 halt 15 3110000
10 halt 16 3110000
10 halt 17 3110000
10 halt 18 3110000
10 halt 19 3120000
10 halt 20 3110000
10 halt 21 3110000
10 halt 22 3110000
10 halt 23 3110000
10 halt 24 3110000
10 halt 25 3140000
10 halt 26 3110000
10 halt 27 3110000
10 halt 28 3120000
10 halt 29 3120000
10 halt 30 3110000
# point pass=10 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997961
11 halt 1 3120000
11 halt 2 3120000
11 halt 3 3120000
11 halt 4 3120000
11 halt 5 3110000
11 halt 6 3120000
11 halt 7 3130000
11 halt 8 3110000
11 halt 9 3120000
11 halt 10 3110000
11 halt 11 3110000
11 halt 12 3120000
11 halt 13 3120000
11 halt 14 3120000
11 halt 15 3130000
11 halt 16 3120000
11 halt 17 3120000
11 halt 18 3120000
11 halt 19 3120000
11 halt 20 3120000
11 halt 21 3120000
11 halt 22 3140000
11 halt 23 3110000
11 halt 24 3130000
11 halt 25 3130000
11 halt 26 3110000
11 halt 27 3120000
11 halt 28 3130000
11 halt 29 3120000
11 halt 30 3140000
# point pass=11 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997961
11 run 1 3250000
11 run 2 3260000
11 run 3 3260000
11 run 4 3260000
11 run 5 3260000
11 run 6 3260000
11 run 7 3250000
11 run 8 3250000
11 run 9 3260000
11 run 10 3290000
11 run 11 3270000
11 run 12 3260000
11 run 13 3260000
11 run 14 3260000
11 run 15 3590000
11 run 16 3260000
11 run 17 3260000
11 run 18 3280000
11 run 19 3280000
11 run 20 3270000
11 run 21 3260000
11 run 22 3260000
11 run 23 3250000
11 run 24 3250000
11 run 25 3260000
11 run 26 3270000
11 run 27 3280000
11 run 28 3250000
11 run 29 3260000
11 run 30 3260000
# point pass=11 state=run jobs_per_s=12499748.2 updates_per_s=0.0 clock_hz=99997964
12 run 1 3250000
12 run 2 3260000
12 run 3 3260000
12 run 4 3260000
12 run 5 3260000
12 run 6 3280000
12 run 7 3260000
12 run 8 3260000
12 run 9 3250000
12 run 10 3260000
12 run 11 3260000
12 run 12 3260000
12 run 13 3300000
12 run 14 3260000
12 run 15 3250000
12 run 16 3260000
12 run 17 3260000
12 run 18 3270000
12 run 19 3260000
12 run 20 3270000
12 run 21 3270000
12 run 22 3280000
12 run 23 3260000
12 run 24 3280000
12 run 25 3270000
12 run 26 3260000
12 run 27 3290000
12 run 28 3270000
12 run 29 3260000
12 run 30 3260000
# point pass=12 state=run jobs_per_s=12499748.3 updates_per_s=0.0 clock_hz=99997964
12 halt 1 3130000
12 halt 2 3130000
12 halt 3 3110000
12 halt 4 3110000
12 halt 5 3130000
12 halt 6 3120000
12 halt 7 3130000
12 halt 8 3120000
12 halt 9 3110000
12 halt 10 3110000
12 halt 11 3120000
12 halt 12 3120000
12 halt 13 3120000
12 halt 14 3110000
12 halt 15 3140000
12 halt 16 3140000
12 halt 17 3110000
12 halt 18 3120000
12 halt 19 3120000
12 halt 20 3120000
12 halt 21 3110000
12 halt 22 3110000
12 halt 23 3120000
12 halt 24 3120000
12 halt 25 3110000
12 halt 26 3130000
12 halt 27 3110000
12 halt 28 3110000
12 halt 29 3110000
12 halt 30 3130000
# point pass=12 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997964
# done SystemTime { tv_sec: 1793913111, tv_nsec: 884855982 }
# passes used 12 of 12 (dropped: [])
# run - halt = 146.7 +/- 2.0 mW (per-pass sd 6.9); 12/12 passes positive; 4 sample excursions beyond 6 MAD
# per-pass differences (mW): 150.0 150.0 150.0 150.0 155.0 140.0 145.0 140.0 160.0 140.0 140.0 140.0
# board power while running 3259 mW; jobs/s 12499746.8; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 122.0 mW: measured/predicted = 1.20, 12.5 sigma from it

View file

@ -0,0 +1,9 @@
Written 2026-11-06, before the build with the result write-back was metered, and committed before
the measurement. Not edited after the data.
The AXI build metered first (sha256 3d84dbd3..., AXI-PREDICTION.txt) read a batch's results by
register reads. The final build adds a write master that sends them to DDR. The engine is the same,
and the write master is idle while the meter reads.
PREDICTION: t0-recall run minus halt on the final build, 3 passes, 60 s settle, 30 samples, equals
the EMIO build's class result, 162.0 +/- 3.3 mW, within 2 combined standard errors.

View file

@ -0,0 +1,10 @@
Written 2026-11-06, before the AXI-link build was metered, and committed before the measurement.
Not edited after the data.
The AXI-link build (rtl/landscape_axi.v) carries the same engine (rtl/landscape_core.v, rtl version 2)
as the EMIO build the class campaign measured (measurements/2026-11-06/PREDICTION.txt). The link is
idle while the meter reads: the run state is the engine in repeat mode with no processor access.
PREDICTION: t0-recall run minus halt on the AXI build, 3 passes, 60 s settle, 30 samples, equals the
EMIO build's t0-recall class result within 2 combined standard errors. If it does not, the power
figures quoted for the design are the AXI build's, and the class campaign is repeated on it.

View file

@ -0,0 +1,38 @@
Written 2026-11-06, before any board run of the joint test, and committed before it. Not edited
after the data.
WHAT IS TESTED. The sample kernel's joint law, not only its per-cell frequencies: a sampler that drew
each cell from its right marginal, independently, would pass every per-cell test. Four cases, each a
workload small enough that the exact law of a chain's final state can be enumerated over all 3^n
states (src/exact.rs, `exact::cases`):
ring 8, fixed T = 1.5, 4 sweeps 200,000 chains 6,561 states
ring 8, the job's anneal from T = 2.5, 10 sweeps 200,000 chains 6,561 states
dense 6, fixed T = 4, 2 sweeps 200,000 chains 729 states
sparse 10, the job's anneal from T = 1.5, 8 sweeps 400,000 chains 59,049 states
Chain r of a case has the seed chain_seed(2027, r). (Seed 2026 was used while writing the test, on
the model; 2027 has not been run anywhere before this file was committed.) Every chain runs on the
board through the AXI link's sequencer, and on the CPU reference.
THE EXACT LAW. A probability vector over every state, pushed through each cell update of each sweep
with the three probabilities a/2^53, (b-a)/2^53 and 1 - b/2^53 from the reference's cut points
(a, b) = cuts(h, cost, beta), then through the synchronous sweep at temperature 0 where the job's
anneal ends with one. It assumes ideal uniform 53-bit draws; nothing is sampled.
STATISTICS AND THE RULE, per case:
1. Pearson chi-square and the G-test over every state expected at least 5 times, the rest pooled
into one bin: p >= 0.001 for both.
2. Every cell's three marginal frequencies (z scores) and every pair of cells' nine joint
frequencies: Bonferroni p >= 0.001 for each family.
3. Every chain's final state on the board equals the CPU reference's.
4. Control: the same histogram against the law with the exact marginals and independent cells
must be rejected, chi-square p < 1e-6, while its cell family still passes. This shows the test
sees the joint structure that per-cell statistics cannot.
A case passes if 1-4 hold. Sixteen p values are taken at 0.001 each; the chance that a correct
kernel fails one by chance is about 1.6%.
WHAT IT DOES NOT SHOW. Because the board is bit-exact (rule 3), its histogram is the reference's:
the test holds the shared kernel (the reference's algorithm and its counter-based draws) to exact
enumeration on the board's own outputs; it is not an independent sample of a different machine.
Board-level, one board.

View file

@ -0,0 +1,15 @@
Written 2026-11-06 after the class campaign's data were read, and committed before the repeat
below runs. Not edited after its data.
The class campaign's null (EMIO build, 12:49 to 13:13 UTC) came to 8.1 +/- 1.9 mW, and so failed its
pre-registered rule, |delta| < 2 SE. The two slots of a null are the same state (halted), so no job
explains it, and it does not follow the order of the slots: 3 of the 8 passes put the slot labelled
halt first and 5 the slot labelled run, and the differences are alike in both.
WHAT IS DONE. The null runs once more, on the AXI build, by the same protocol: 8 passes, 60 s settle,
30 samples, nothing else on the board, the same rule. Both nulls are reported, whichever way the
second comes out. This is not a second chance for the first: its failure stands in the README.
WHAT FOLLOWS. If the repeat also fails, nothing below about 10 mW is claimed above the instrument's
floor, which covers gibbs-recall and t0-idle. If it passes, those two are still reported with the
first null beside them, since one failure in two says the floor is not known to be under 8 mW.

View file

@ -0,0 +1,25 @@
Written 2026-11-06, before this design (rtl version 2) was metered on the board, and committed
before the measurement began. Not edited after the data.
WHAT IS MEASURED. Run minus halt on one bitstream, whole-board power sensor, 100 MHz, by the
protocol in scripts/power_campaign.sh: no reload between the two states, run/halt order randomised
in each pass, 60 s settle and 30 samples at 1 Hz read in the measuring process with nothing else
running, every point checked for a reload (the engine's uptime counter, its ID, the kernel log's
count of FPGA writes), statistic the mean of per-pass differences of medians, error the standard
error across passes. Label: board-level, one board, one meter, this design.
WHAT CHANGED SINCE THE LAST MEASUREMENT (rtl version 1, 2026-11-05, measurements/2026-11-05):
fields and the cost widen from 11 and 10 bits to 20; a cell update takes four clocks, not three;
the sampler's draw is a SplitMix64 pipeline (two 64-bit products, 20 DSP slices) instead of a
128-bit xoroshiro register; the table entry is 108 bits, not 34.
PREDICTIONS, point and range:
t0-recall 160 mW (130..200) version 1 measured 146.7 +/- 2.0; the wider field adders and
comparators add switching when a field changes sign
gibbs-recall 12 mW (4..30) version 1 measured 6.7 +/- 1.8 (one core), 10.5 +/- 0.5 a core
(four-core build); fewer updates a second (25 M, not 33 M) but
the multipliers and a wider table read on each
t0-idle 4 mW (0..12) control and counters only, as version 1 (3.8 +/- 1.6)
null 0 mW, pass if |delta| < 2 SE
t0-recall at 50 MHz: half the 100 MHz value, pass if the ratio is within 2 SE of 0.5
known answer (the 2026-09 vehicle, where present): 61.7 mW, pass if within 2 combined sigma

View file

@ -0,0 +1,20 @@
# 2026-11-06: rtl version 2, on the EMIO link and on the AXI link
Raw output as the board wrote it. Board-level, one board, one meter. The `*.txt` files at the top
are the predictions and pre-registrations, each committed before the measurement it covers.
| file | what |
|---|---|
| `PREDICTION.txt` | the class predictions for rtl version 2 |
| `first-attempt/` | 08:01 UTC on the EMIO build: the run/halt step and the known-answer vehicle; then the reload refused (the session check matched its own parent process, since fixed) |
| `emio-classes/` | 12:39 to 14:55 UTC on the EMIO build (sha256 `feea5015...`): step, null, settle, sampling, idle, the settle job at 50 MHz |
| `emio-checks/` | the same build afterwards: conformance, agreement, latency, throughput, the processor's port widths |
| `AXI-PREDICTION.txt` | committed at 13:06 UTC, during the class campaign and before its null had finished, and before the AXI build was metered |
| `NULL-REPEAT.txt` | committed at 15:03 UTC, after the class campaign ended and its null had failed, before the AXI build was metered |
| `axi-first/` | the first AXI build (sha256 `3d84dbd3...`, results read back by register reads): checks, then `power/` (the settle check and the repeated null) |
| `JOINT-PREREG.txt` | the joint-statistics test, registered before its first board run (`axi-first/joint.txt`) |
| `AXI-FINAL-PREDICTION.txt`, `axi-final/` | the final AXI build (sha256 `bb825055...`, results written back by DMA): checks, then `power/` (the settle check) |
| `build.txt` | the builds: tool and block versions, sizes, timing, each bitstream's sha256, and the testbenches' results |
Summarise a meter log with `wai-quantum-landscape-fpga analyze <file>` or
`python3 scripts/power_tables.py <files>`.

View file

@ -0,0 +1,31 @@
certified by the host on the conformance set: settle bit-exact, sample bit-exact
parity earned on this board: settle bit-exact, sample bit-exact
the worked examples on the board and on the CPU reference, 200 seeds each
[PASS] talks at its own temperature: 200/200 answers identical
[PASS] talks at temperature 0: 200/200 answers identical
[PASS] clusters at its own temperature: 200/200 answers identical
[PASS] clusters at temperature 0: 200/200 answers identical
[PASS] selection at its own temperature: 200/200 answers identical
[PASS] selection at temperature 0: 200/200 answers identical
[PASS] chain at its own temperature: 200/200 answers identical
[PASS] chain at temperature 0: 200/200 answers identical
[PASS] assign-2x2 at its own temperature: 200/200 answers identical
[PASS] assign-2x2 at temperature 0: 200/200 answers identical
[PASS] blocks at its own temperature: 200/200 answers identical
[PASS] blocks at temperature 0: 200/200 answers identical
[PASS] assign-3x3 at its own temperature: 200/200 answers identical
[PASS] assign-3x3 at temperature 0: 200/200 answers identical
[PASS] colour-six at its own temperature: 200/200 answers identical
[PASS] colour-six at temperature 0: 200/200 answers identical
[PASS] colour-four at its own temperature: 200/200 answers identical
[PASS] colour-four at temperature 0: 200/200 answers identical
[PASS] piles at its own temperature: 200/200 answers identical
[PASS] piles at temperature 0: 200/200 answers identical
[PASS] cold-constraint at its own temperature: 200/200 answers identical
[PASS] cold-constraint at temperature 0: 200/200 answers identical
[PASS] cold-start at its own temperature: 200/200 answers identical
[PASS] cold-start at temperature 0: 200/200 answers identical
[PASS] too-costly at its own temperature: 200/200 answers identical
[PASS] too-costly at temperature 0: 200/200 answers identical
[PASS] typo: refused by the reader before any member runs: Line 6: `oppos` is not a statement epuML knows. Did you mean `oppose`?
27 checks, 0 failed

View file

@ -0,0 +1,48 @@
wai.quantum.landscape/1 member tests (quantum-conformance/landscape/vectors.json)
settle cases 13/13 bit for bit; chains 42/42 bit for bit
[PASS] settle kernel: the settle cases, bit for bit (state, sweeps, ending, accumulates), each held to the job
[PASS] sample kernel: the chain vectors, bit for bit (state, sweeps, ending, accumulates, draws), each held to the job
[PASS] cells: 650/650 cut points the host computes for the engine equal the set's, bit for bit
[PASS] cell h=-40 c=0 T=0.25: 100000 draws, counts [100000, 0, 0], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-40 c=2 T=1: 100000 draws, counts [100000, 0, 0], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-40 c=8 T=2.9: 100000 draws, counts [100000, 0, 0], z +1.27 -1.27 -0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-17 c=1 T=25: 100000 draws, counts [55805, 29689, 14506], z -1.59 +0.91 +1.07 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-17 c=8 T=0.7: 100000 draws, counts [99999, 1, 0], z -1.45 +1.45 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-5 c=1 T=1.8708286933869707: 100000 draws, counts [89058, 10527, 415], z -0.17 +0.28 -0.48 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-5 c=3 T=13.7: 100000 draws, counts [42641, 36802, 20557], z +0.11 -0.21 +0.12 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-3 c=1 T=0.3: 100000 draws, counts [99874, 126, 0], z +0.10 -0.10 -0.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-3 c=3 T=1.3: 100000 draws, counts [50015, 49537, 448], z +1.65 -1.37 -2.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-1 c=0 T=7.1: 100000 draws, counts [38122, 33245, 28633], z -0.00 +0.88 -0.91 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-1 c=3 T=0.25: 100000 draws, counts [32, 99968, 0], z -0.27 +0.27 -0.11 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=0 T=1: 100000 draws, counts [33342, 33254, 33404], z +0.06 -0.53 +0.47 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=2 T=2.9: 100000 draws, counts [24786, 50120, 25094], z -1.88 +1.31 +0.37 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=8 T=25: 100000 draws, counts [29353, 41113, 29534], z -1.79 +2.16 -0.53 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=1 c=2 T=0.7: 100000 draws, counts [1130, 79767, 19103], z +0.97 -0.12 -0.14 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=1 c=8 T=1.8708286933869707: 100000 draws, counts [801, 96844, 2355], z +0.43 -1.26 +1.20 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=2 c=1 T=13.7: 100000 draws, counts [27835, 34700, 37465], z -0.48 -0.22 +0.66 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=2 c=8 T=0.3: 100000 draws, counts [0, 100000, 0], z -0.00 +0.01 -0.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=3 c=1 T=1.3: 100000 draws, counts [835, 17462, 81703], z +0.94 -0.59 +0.36 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=3 c=3 T=7.1: 100000 draws, counts [17600, 41216, 41184], z -0.66 +0.36 +0.15 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=5 c=1 T=0.25: 100000 draws, counts [0, 0, 100000], z +0.00 -0.11 +0.11 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=5 c=3 T=1: 100000 draws, counts [9, 12006, 87985], z +2.50 +0.84 -0.89 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=9 c=0 T=2.9: 100000 draws, counts [204, 4140, 95656], z +0.83 -2.31 +2.09 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=9 c=2 T=25: 100000 draws, counts [21552, 33829, 44619], z -1.18 +0.85 +0.17 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=0 T=0.7: 100000 draws, counts [0, 0, 100000], z +0.00 -0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=2 T=1.8708286933869707: 100000 draws, counts [0, 36, 99964], z -0.04 +0.53 -0.53 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=8 T=13.7: 100000 draws, counts [5263, 32393, 62344], z +0.64 +0.21 -0.50 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=40 c=2 T=0.3: 100000 draws, counts [0, 0, 100000], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=40 c=8 T=1.3: 100000 draws, counts [0, 0, 100000], z +0.00 -0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] battery one-pair: chi-square 10.7 on 8 degrees of freedom (critical 28.0)
[PASS] battery a-pair-apart: chi-square 45.3 on 26 degrees of freedom (critical 62.1)
[PASS] battery a-pair-far-apart: chi-square 75.3 on 79 degrees of freedom (critical 141.8)
[PASS] battery a-triangle: chi-square 31.0 on 26 degrees of freedom (critical 62.1)
[PASS] battery wide-fields-and-cost: chi-square 18.3 on 22 degrees of freedom (critical 55.2)
[PASS] battery a-small-field-cold: chi-square 6.9 on 4 degrees of freedom (critical 18.1)
[PASS] battery ending-at-zero: chi-square 17.1 on 11 degrees of freedom (critical 34.5)
[PASS] battery cooling: chi-square 16.8 on 23 degrees of freedom (critical 56.9)
[PASS] the all-at-once control (every cell moved together) fails the battery on ["one-pair", "a-pair-apart", "a-pair-far-apart", "a-triangle", "a-small-field-cold", "ending-at-zero", "cooling"]
[PASS] jobs: 31/31 requests answer as the set says (state, energy, verdict, mode, restarts, sweeps, accumulates, draws, replay digest, or the refusal)
[PASS] 2000/2000 random workloads (2-64 cells, fields -5..5, cost 0-3) settle exactly as the reference does
[PASS] 4096/4096 fields read back equal W s + b (dense W, fields across the engine's ±524287)
[PASS] the engine still answers with its ID at the end: no other session replaced it
44 checks, 0 failed

View file

@ -0,0 +1,23 @@
joint statistics against exact enumeration (chains scaled by 1, seed 2027)
ring 8, fixed T = 1.5, 4 sweeps: 200000 chains, 2930 bins; exact law: chi2 3030.0 (p 0.095), G 3066.8 (p 0.037), TV 0.0436, cell |z| 1.91 (p 1.000), pair |z| 2.61 (p 1.000); same as the reference 200000/200000; 3.3 s
control, the same marginals with independent cells: chi2 244079.4 on 3247 bins (p 0.00e0), cell |z| 1.91 (p 1.000), pair |z| 86.9 (p 0.00e0)
[PASS] ring 8, fixed T = 1.5, 4 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] ring 8, fixed T = 1.5, 4 sweeps: every chain's final state is the reference's
[PASS] ring 8, fixed T = 1.5, 4 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
ring 8, the job's anneal from T = 2.5, 10 sweeps: 200000 chains, 45 bins; exact law: chi2 38.5 (p 0.708), G 38.6 (p 0.702), TV 0.0022, cell |z| 1.20 (p 1.000), pair |z| 2.35 (p 1.000); same as the reference 200000/200000; 5.7 s
control, the same marginals with independent cells: chi2 1933336.3 on 181 bins (p 0.00e0), cell |z| 1.20 (p 1.000), pair |z| 390.3 (p 0.00e0)
[PASS] ring 8, the job's anneal from T = 2.5, 10 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] ring 8, the job's anneal from T = 2.5, 10 sweeps: every chain's final state is the reference's
[PASS] ring 8, the job's anneal from T = 2.5, 10 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
dense 6, fixed T = 4, 2 sweeps: 200000 chains, 729 bins; exact law: chi2 682.6 (p 0.884), G 685.3 (p 0.870), TV 0.0219, cell |z| 1.73 (p 1.000), pair |z| 3.58 (p 0.047); same as the reference 200000/200000; 1.7 s
control, the same marginals with independent cells: chi2 91995.4 on 729 bins (p 0.00e0), cell |z| 1.73 (p 1.000), pair |z| 54.5 (p 0.00e0)
[PASS] dense 6, fixed T = 4, 2 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] dense 6, fixed T = 4, 2 sweeps: every chain's final state is the reference's
[PASS] dense 6, fixed T = 4, 2 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
sparse 10, the job's anneal from T = 1.5, 8 sweeps: 400000 chains, 190 bins; exact law: chi2 196.1 (p 0.346), G 207.7 (p 0.167), TV 0.0048, cell |z| 1.45 (p 1.000), pair |z| 3.04 (p 0.773); same as the reference 400000/400000; 11.4 s
control, the same marginals with independent cells: chi2 14139391.3 on 1531 bins (p 0.00e0), cell |z| 1.45 (p 1.000), pair |z| 373.6 (p 0.00e0)
[PASS] sparse 10, the job's anneal from T = 1.5, 8 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] sparse 10, the job's anneal from T = 1.5, 8 sweeps: every chain's final state is the reference's
[PASS] sparse 10, the job's anneal from T = 1.5, 8 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
[PASS] the engine still answers with its ID at the end: no other session replaced it
13 checks, 0 failed

View file

@ -0,0 +1,22 @@
processor-to-logic latency, AXI link: one board, the engine at its measured clock
timer: CNTVCT_EL0 at 99.999 MHz, one tick 10.0 ns
the timer alone (two reads, an ISB each) median 10 ns p10 10 p90 20 p99 20 max 20 mean 11 (n 20000)
EMIO signature read (a processor GPIO register) median 230 ns p10 230 p90 240 p99 240 max 89851 mean 237 (n 100000)
AXI-Lite read, a link register median 230 ns p10 230 p90 230 p99 230 max 19920 mean 233 (n 100000)
AXI-Lite read, an engine register median 240 ns p10 240 p90 240 p99 240 max 25680 mean 242 (n 100000)
a DSB alone median 10 ns p10 10 p90 110 p99 150 max 10470 mean 28 (n 100000)
AXI-Lite write and its response (DSB), a link register median 250 ns p10 250 p90 350 p99 380 max 22460 mean 269 (n 100000)
AXI-Lite write and its response (DSB), an engine register median 250 ns p10 250 p90 350 p99 380 max 56791 mean 269 (n 100000)
AXI-Lite writes back to back, no DSB between: 51 ns each over 100000
AXI-Lite write then read (select a cell, read its field) median 460 ns p10 460 p90 580 p99 600 max 53021 mean 481 (n 100000)
guarded read, as every job access goes (signature, then read) median 470 ns p10 460 p90 470 p99 470 max 21530 mean 470 (n 100000)
a 1-entry write list by DMA, kick to done (4 writes, polls) median 7640 ns p10 7570 p90 7790 p99 9340 max 25820 mean 7775 (n 5000)
a 512-entry write list by DMA (one page), kick to done median 20310 ns p10 20010 p90 20480 p99 23510 max 54711 mean 20433 (n 2000)
= 39.7 ns an entry, 25.2 M register writes/s
128 chains' results written back (one page), kick to read median 37550 ns p10 37240 p90 37990 p99 47880 max 49670 mean 37825 (n 2000)
= 293 ns a chain
AXI, DMA and sequencer: a full table (64 levels x 128 fields, 32835 register writes): median 3.81 ms of 6, 116 ns a write
AXI, DMA and sequencer: 65536 chains of 8 cells x 4 sweeps: 0.285 s, 4.34 us a chain, 230153 chains/s
AXI, plain register accesses: a full table (64 levels x 128 fields, 32835 register writes): median 17.46 ms of 6, 532 ns a write
AXI, plain register accesses: 16384 chains of 8 cells x 4 sweeps: 0.154 s, 9.42 us a chain, 106113 chains/s
AXI accesses 1174977, DMA lists 8862, sequencer batches 257, write-backs 2714

View file

@ -0,0 +1,7 @@
2026-11-06T18:43:55Z start: engine answers: ID and version 0x00404002; measured clock 99.997 MHz
2026-11-06T18:44:09Z conform exit=0
2026-11-06T18:44:20Z agree exit=0
2026-11-06T18:44:42Z joint exit=0
2026-11-06T18:44:44Z latency exit=0
2026-11-06T18:44:48Z throughput exit=0
2026-11-06T18:44:48Z end: engine answers: ID and version 0x00404002; measured clock 99.998 MHz

View file

@ -0,0 +1,3 @@
2026-11-06T18:45:00Z start: engine answers: ID and version 0x00404002; measured clock 99.998 MHz
2026-11-06T18:54:01Z t0-recall done exit=0
2026-11-06T18:54:01Z end

View file

@ -0,0 +1,195 @@
# class t0-recall cores 1 link AXI
# wai-quantum-landscape-fpga meter passes=3 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793990700, tv_nsec: 679432036 }
# pass state sample power_uW
1 halt 1 3150000
1 halt 2 3140000
1 halt 3 3140000
1 halt 4 3150000
1 halt 5 3140000
1 halt 6 3140000
1 halt 7 3150000
1 halt 8 3140000
1 halt 9 3140000
1 halt 10 3150000
1 halt 11 3150000
1 halt 12 3140000
1 halt 13 3140000
1 halt 14 3140000
1 halt 15 3150000
1 halt 16 3150000
1 halt 17 3150000
1 halt 18 3140000
1 halt 19 3140000
1 halt 20 3140000
1 halt 21 3150000
1 halt 22 3150000
1 halt 23 3140000
1 halt 24 3140000
1 halt 25 3140000
1 halt 26 3140000
1 halt 27 3150000
1 halt 28 3180000
1 halt 29 3140000
1 halt 30 3140000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997965
1 run 1 3310000
1 run 2 3320000
1 run 3 3310000
1 run 4 3300000
1 run 5 3300000
1 run 6 3300000
1 run 7 3320000
1 run 8 3300000
1 run 9 3300000
1 run 10 3330000
1 run 11 3300000
1 run 12 3310000
1 run 13 3300000
1 run 14 3300000
1 run 15 3300000
1 run 16 3300000
1 run 17 3300000
1 run 18 3300000
1 run 19 3300000
1 run 20 3310000
1 run 21 3310000
1 run 22 3300000
1 run 23 3300000
1 run 24 3300000
1 run 25 3330000
1 run 26 3300000
1 run 27 3300000
1 run 28 3300000
1 run 29 3300000
1 run 30 3300000
# point pass=1 state=run jobs_per_s=12499746.7 updates_per_s=0.0 clock_hz=99997964
2 run 1 3330000
2 run 2 3310000
2 run 3 3310000
2 run 4 3320000
2 run 5 3310000
2 run 6 3320000
2 run 7 3310000
2 run 8 3320000
2 run 9 3330000
2 run 10 3330000
2 run 11 3310000
2 run 12 3320000
2 run 13 3350000
2 run 14 3310000
2 run 15 3310000
2 run 16 3300000
2 run 17 3310000
2 run 18 3330000
2 run 19 3300000
2 run 20 3320000
2 run 21 3320000
2 run 22 3310000
2 run 23 3310000
2 run 24 3300000
2 run 25 3320000
2 run 26 3320000
2 run 27 3320000
2 run 28 3590000
2 run 29 3610000
2 run 30 3310000
# point pass=2 state=run jobs_per_s=12499746.9 updates_per_s=0.0 clock_hz=99997966
2 halt 1 3420000
2 halt 2 3420000
2 halt 3 3130000
2 halt 4 3140000
2 halt 5 3130000
2 halt 6 3160000
2 halt 7 3150000
2 halt 8 3140000
2 halt 9 3130000
2 halt 10 3140000
2 halt 11 3140000
2 halt 12 3140000
2 halt 13 3130000
2 halt 14 3130000
2 halt 15 3150000
2 halt 16 3140000
2 halt 17 3150000
2 halt 18 3140000
2 halt 19 3130000
2 halt 20 3150000
2 halt 21 3140000
2 halt 22 3140000
2 halt 23 3460000
2 halt 24 3140000
2 halt 25 3140000
2 halt 26 3130000
2 halt 27 3140000
2 halt 28 3140000
2 halt 29 3130000
2 halt 30 3140000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997939
3 halt 1 3150000
3 halt 2 3140000
3 halt 3 3140000
3 halt 4 3160000
3 halt 5 3160000
3 halt 6 3140000
3 halt 7 3160000
3 halt 8 3130000
3 halt 9 3150000
3 halt 10 3140000
3 halt 11 3140000
3 halt 12 3140000
3 halt 13 3150000
3 halt 14 3140000
3 halt 15 3140000
3 halt 16 3140000
3 halt 17 3140000
3 halt 18 3140000
3 halt 19 3140000
3 halt 20 3140000
3 halt 21 3140000
3 halt 22 3140000
3 halt 23 3150000
3 halt 24 3140000
3 halt 25 3140000
3 halt 26 3140000
3 halt 27 3150000
3 halt 28 3140000
3 halt 29 3140000
3 halt 30 3140000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997771
3 run 1 3320000
3 run 2 3300000
3 run 3 3290000
3 run 4 3290000
3 run 5 3300000
3 run 6 3290000
3 run 7 3290000
3 run 8 3310000
3 run 9 3310000
3 run 10 3620000
3 run 11 3560000
3 run 12 3300000
3 run 13 3310000
3 run 14 3300000
3 run 15 3300000
3 run 16 3310000
3 run 17 3320000
3 run 18 3300000
3 run 19 3300000
3 run 20 3310000
3 run 21 3300000
3 run 22 3310000
3 run 23 3300000
3 run 24 3300000
3 run 25 3300000
3 run 26 3330000
3 run 27 3320000
3 run 28 3290000
3 run 29 3300000
3 run 30 3310000
# point pass=3 state=run jobs_per_s=12499726.1 updates_per_s=0.0 clock_hz=99997799
# done SystemTime { tv_sec: 1793991241, tv_nsec: 34183769 }
# passes used 3 of 3 (dropped: [])
# run - halt = 166.7 +/- 6.7 mW (per-pass sd 11.5); 3/3 passes positive; 8 sample excursions beyond 6 MAD
# per-pass differences (mW): 160.0 180.0 160.0
# board power while running 3307 mW; jobs/s 12499739.9; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 162.0 mW: measured/predicted = 1.03, 0.7 sigma from it

View file

@ -0,0 +1,2 @@
settle, dense 64 (recall cue): clock 99.998 MHz, 8.0 clocks a job, 12499745 jobs/s, 0.000e0 cell updates/s, 1.831e11 accumulates/s
sample, sequential, 64 cells, 8 sweeps at T = 0.25: clock 99.998 MHz, 2059.0 clocks a job, 48566 jobs/s, 2.487e7 cell updates/s, 1.138e9 accumulates/s

View file

@ -0,0 +1,31 @@
certified by the host on the conformance set: settle bit-exact, sample bit-exact
parity earned on this board: settle bit-exact, sample bit-exact
the worked examples on the board and on the CPU reference, 200 seeds each
[PASS] talks at its own temperature: 200/200 answers identical
[PASS] talks at temperature 0: 200/200 answers identical
[PASS] clusters at its own temperature: 200/200 answers identical
[PASS] clusters at temperature 0: 200/200 answers identical
[PASS] selection at its own temperature: 200/200 answers identical
[PASS] selection at temperature 0: 200/200 answers identical
[PASS] chain at its own temperature: 200/200 answers identical
[PASS] chain at temperature 0: 200/200 answers identical
[PASS] assign-2x2 at its own temperature: 200/200 answers identical
[PASS] assign-2x2 at temperature 0: 200/200 answers identical
[PASS] blocks at its own temperature: 200/200 answers identical
[PASS] blocks at temperature 0: 200/200 answers identical
[PASS] assign-3x3 at its own temperature: 200/200 answers identical
[PASS] assign-3x3 at temperature 0: 200/200 answers identical
[PASS] colour-six at its own temperature: 200/200 answers identical
[PASS] colour-six at temperature 0: 200/200 answers identical
[PASS] colour-four at its own temperature: 200/200 answers identical
[PASS] colour-four at temperature 0: 200/200 answers identical
[PASS] piles at its own temperature: 200/200 answers identical
[PASS] piles at temperature 0: 200/200 answers identical
[PASS] cold-constraint at its own temperature: 200/200 answers identical
[PASS] cold-constraint at temperature 0: 200/200 answers identical
[PASS] cold-start at its own temperature: 200/200 answers identical
[PASS] cold-start at temperature 0: 200/200 answers identical
[PASS] too-costly at its own temperature: 200/200 answers identical
[PASS] too-costly at temperature 0: 200/200 answers identical
[PASS] typo: refused by the reader before any member runs: Line 6: `oppos` is not a statement epuML knows. Did you mean `oppose`?
27 checks, 0 failed

View file

@ -0,0 +1,48 @@
wai.quantum.landscape/1 member tests (quantum-conformance/landscape/vectors.json)
settle cases 13/13 bit for bit; chains 42/42 bit for bit
[PASS] settle kernel: the settle cases, bit for bit (state, sweeps, ending, accumulates), each held to the job
[PASS] sample kernel: the chain vectors, bit for bit (state, sweeps, ending, accumulates, draws), each held to the job
[PASS] cells: 650/650 cut points the host computes for the engine equal the set's, bit for bit
[PASS] cell h=-40 c=0 T=0.25: 100000 draws, counts [100000, 0, 0], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-40 c=2 T=1: 100000 draws, counts [100000, 0, 0], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-40 c=8 T=2.9: 100000 draws, counts [100000, 0, 0], z +1.27 -1.27 -0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-17 c=1 T=25: 100000 draws, counts [55805, 29689, 14506], z -1.59 +0.91 +1.07 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-17 c=8 T=0.7: 100000 draws, counts [99999, 1, 0], z -1.45 +1.45 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-5 c=1 T=1.8708286933869707: 100000 draws, counts [89058, 10527, 415], z -0.17 +0.28 -0.48 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-5 c=3 T=13.7: 100000 draws, counts [42641, 36802, 20557], z +0.11 -0.21 +0.12 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-3 c=1 T=0.3: 100000 draws, counts [99874, 126, 0], z +0.10 -0.10 -0.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-3 c=3 T=1.3: 100000 draws, counts [50015, 49537, 448], z +1.65 -1.37 -2.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-1 c=0 T=7.1: 100000 draws, counts [38122, 33245, 28633], z -0.00 +0.88 -0.91 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-1 c=3 T=0.25: 100000 draws, counts [32, 99968, 0], z -0.27 +0.27 -0.11 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=0 T=1: 100000 draws, counts [33342, 33254, 33404], z +0.06 -0.53 +0.47 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=2 T=2.9: 100000 draws, counts [24786, 50120, 25094], z -1.88 +1.31 +0.37 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=8 T=25: 100000 draws, counts [29353, 41113, 29534], z -1.79 +2.16 -0.53 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=1 c=2 T=0.7: 100000 draws, counts [1130, 79767, 19103], z +0.97 -0.12 -0.14 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=1 c=8 T=1.8708286933869707: 100000 draws, counts [801, 96844, 2355], z +0.43 -1.26 +1.20 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=2 c=1 T=13.7: 100000 draws, counts [27835, 34700, 37465], z -0.48 -0.22 +0.66 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=2 c=8 T=0.3: 100000 draws, counts [0, 100000, 0], z -0.00 +0.01 -0.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=3 c=1 T=1.3: 100000 draws, counts [835, 17462, 81703], z +0.94 -0.59 +0.36 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=3 c=3 T=7.1: 100000 draws, counts [17600, 41216, 41184], z -0.66 +0.36 +0.15 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=5 c=1 T=0.25: 100000 draws, counts [0, 0, 100000], z +0.00 -0.11 +0.11 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=5 c=3 T=1: 100000 draws, counts [9, 12006, 87985], z +2.50 +0.84 -0.89 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=9 c=0 T=2.9: 100000 draws, counts [204, 4140, 95656], z +0.83 -2.31 +2.09 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=9 c=2 T=25: 100000 draws, counts [21552, 33829, 44619], z -1.18 +0.85 +0.17 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=0 T=0.7: 100000 draws, counts [0, 0, 100000], z +0.00 -0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=2 T=1.8708286933869707: 100000 draws, counts [0, 36, 99964], z -0.04 +0.53 -0.53 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=8 T=13.7: 100000 draws, counts [5263, 32393, 62344], z +0.64 +0.21 -0.50 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=40 c=2 T=0.3: 100000 draws, counts [0, 0, 100000], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=40 c=8 T=1.3: 100000 draws, counts [0, 0, 100000], z +0.00 -0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] battery one-pair: chi-square 10.7 on 8 degrees of freedom (critical 28.0)
[PASS] battery a-pair-apart: chi-square 45.3 on 26 degrees of freedom (critical 62.1)
[PASS] battery a-pair-far-apart: chi-square 75.3 on 79 degrees of freedom (critical 141.8)
[PASS] battery a-triangle: chi-square 31.0 on 26 degrees of freedom (critical 62.1)
[PASS] battery wide-fields-and-cost: chi-square 18.3 on 22 degrees of freedom (critical 55.2)
[PASS] battery a-small-field-cold: chi-square 6.9 on 4 degrees of freedom (critical 18.1)
[PASS] battery ending-at-zero: chi-square 17.1 on 11 degrees of freedom (critical 34.5)
[PASS] battery cooling: chi-square 16.8 on 23 degrees of freedom (critical 56.9)
[PASS] the all-at-once control (every cell moved together) fails the battery on ["one-pair", "a-pair-apart", "a-pair-far-apart", "a-triangle", "a-small-field-cold", "ending-at-zero", "cooling"]
[PASS] jobs: 31/31 requests answer as the set says (state, energy, verdict, mode, restarts, sweeps, accumulates, draws, replay digest, or the refusal)
[PASS] 2000/2000 random workloads (2-64 cells, fields -5..5, cost 0-3) settle exactly as the reference does
[PASS] 4096/4096 fields read back equal W s + b (dense W, fields across the engine's ±524287)
[PASS] the engine still answers with its ID at the end: no other session replaced it
44 checks, 0 failed

View file

@ -0,0 +1,23 @@
joint statistics against exact enumeration (chains scaled by 1, seed 2027)
ring 8, fixed T = 1.5, 4 sweeps: 200000 chains, 2930 bins; exact law: chi2 3030.0 (p 0.095), G 3066.8 (p 0.037), TV 0.0436, cell |z| 1.91 (p 1.000), pair |z| 2.61 (p 1.000); same as the reference 200000/200000; 3.7 s
control, the same marginals with independent cells: chi2 244079.4 on 3247 bins (p 0.00e0), cell |z| 1.91 (p 1.000), pair |z| 86.9 (p 0.00e0)
[PASS] ring 8, fixed T = 1.5, 4 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] ring 8, fixed T = 1.5, 4 sweeps: every chain's final state is the reference's
[PASS] ring 8, fixed T = 1.5, 4 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
ring 8, the job's anneal from T = 2.5, 10 sweeps: 200000 chains, 45 bins; exact law: chi2 38.5 (p 0.708), G 38.6 (p 0.702), TV 0.0022, cell |z| 1.20 (p 1.000), pair |z| 2.35 (p 1.000); same as the reference 200000/200000; 6.2 s
control, the same marginals with independent cells: chi2 1933336.3 on 181 bins (p 0.00e0), cell |z| 1.20 (p 1.000), pair |z| 390.3 (p 0.00e0)
[PASS] ring 8, the job's anneal from T = 2.5, 10 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] ring 8, the job's anneal from T = 2.5, 10 sweeps: every chain's final state is the reference's
[PASS] ring 8, the job's anneal from T = 2.5, 10 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
dense 6, fixed T = 4, 2 sweeps: 200000 chains, 729 bins; exact law: chi2 682.6 (p 0.884), G 685.3 (p 0.870), TV 0.0219, cell |z| 1.73 (p 1.000), pair |z| 3.58 (p 0.047); same as the reference 200000/200000; 2.1 s
control, the same marginals with independent cells: chi2 91995.4 on 729 bins (p 0.00e0), cell |z| 1.73 (p 1.000), pair |z| 54.5 (p 0.00e0)
[PASS] dense 6, fixed T = 4, 2 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] dense 6, fixed T = 4, 2 sweeps: every chain's final state is the reference's
[PASS] dense 6, fixed T = 4, 2 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
sparse 10, the job's anneal from T = 1.5, 8 sweeps: 400000 chains, 190 bins; exact law: chi2 196.1 (p 0.346), G 207.7 (p 0.167), TV 0.0048, cell |z| 1.45 (p 1.000), pair |z| 3.04 (p 0.773); same as the reference 400000/400000; 12.4 s
control, the same marginals with independent cells: chi2 14139391.3 on 1531 bins (p 0.00e0), cell |z| 1.45 (p 1.000), pair |z| 373.6 (p 0.00e0)
[PASS] sparse 10, the job's anneal from T = 1.5, 8 sweeps: the joint law is the exact law (every p at least 0.001)
[PASS] sparse 10, the job's anneal from T = 1.5, 8 sweeps: every chain's final state is the reference's
[PASS] sparse 10, the job's anneal from T = 1.5, 8 sweeps: the independent-cells control is rejected (chi-square p below 1e-6)
[PASS] the engine still answers with its ID at the end: no other session replaced it
13 checks, 0 failed

View file

@ -0,0 +1,18 @@
processor-to-logic latency, AXI link: one board, the engine at its measured clock
timer: CNTVCT_EL0 at 99.999 MHz, one tick 10.0 ns
the timer alone (two reads, an ISB each) median 10 ns p10 10 p90 20 p99 20 max 18810 mean 12 (n 20000)
EMIO signature read (a processor GPIO register) median 230 ns p10 230 p90 240 p99 240 max 28140 mean 236 (n 100000)
AXI-Lite read, a link register median 230 ns p10 230 p90 230 p99 230 max 22360 mean 233 (n 100000)
AXI-Lite read, an engine register median 240 ns p10 240 p90 240 p99 240 max 21600 mean 242 (n 100000)
AXI-Lite write, a link register median 10 ns p10 10 p90 20 p99 20 max 10370 mean 14 (n 100000)
AXI-Lite write, an engine register median 10 ns p10 10 p90 20 p99 20 max 38470 mean 14 (n 100000)
AXI-Lite write then read (select a cell, read its field) median 460 ns p10 460 p90 580 p99 600 max 47820 mean 481 (n 100000)
guarded read, as every job access goes (signature, then read) median 470 ns p10 460 p90 470 p99 470 max 19920 mean 469 (n 100000)
a 1-entry write list by DMA, kick to done (4 writes, polls) median 7460 ns p10 7340 p90 7790 p99 9400 max 28330 mean 7645 (n 5000)
a 512-entry write list by DMA (one page), kick to done median 20020 ns p10 19740 p90 20260 p99 29510 max 53911 mean 20197 (n 2000)
= 39.1 ns an entry, 25.6 M register writes/s
AXI, DMA and sequencer: a full table (64 levels x 128 fields, 32835 register writes): median 3.82 ms of 6, 116 ns a write
AXI, DMA and sequencer: 65536 chains of 8 cells x 4 sweeps: 0.436 s, 6.65 us a chain, 150472 chains/s
AXI, plain register accesses: a full table (64 levels x 128 fields, 32835 register writes): median 17.48 ms of 6, 532 ns a write
AXI, plain register accesses: 16384 chains of 8 cells x 4 sweeps: 0.154 s, 9.43 us a chain, 106053 chains/s
AXI accesses 1387388, DMA lists 8862, sequencer batches 257

View file

@ -0,0 +1,7 @@
2026-11-06T15:24:07Z start: engine answers: ID and version 0x00404002; measured clock 99.998 MHz
2026-11-06T15:24:28Z conform exit=0
2026-11-06T15:24:39Z agree exit=0
2026-11-06T15:25:03Z joint exit=0
2026-11-06T15:25:05Z latency exit=0
2026-11-06T15:25:09Z throughput exit=0
2026-11-06T15:25:10Z end: engine answers: ID and version 0x00404002; measured clock 99.998 MHz

View file

@ -0,0 +1,4 @@
2026-11-06T15:25:28Z start: engine answers: ID and version 0x00404002; measured clock 99.998 MHz
2026-11-06T15:34:28Z t0-recall done exit=0
2026-11-06T15:58:29Z null done exit=0
2026-11-06T15:58:29Z end

View file

@ -0,0 +1,505 @@
# class t0-recall cores 1 link AXI
# wai-quantum-landscape-fpga meter passes=8 settle=60s samples=30 null=true sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793979268, tv_nsec: 870353142 }
# pass state sample power_uW
1 run 1 3140000
1 run 2 3130000
1 run 3 3130000
1 run 4 3130000
1 run 5 3130000
1 run 6 3140000
1 run 7 3120000
1 run 8 3140000
1 run 9 3130000
1 run 10 3120000
1 run 11 3130000
1 run 12 3130000
1 run 13 3140000
1 run 14 3140000
1 run 15 3150000
1 run 16 3130000
1 run 17 3140000
1 run 18 3140000
1 run 19 3130000
1 run 20 3140000
1 run 21 3130000
1 run 22 3130000
1 run 23 3150000
1 run 24 3130000
1 run 25 3140000
1 run 26 3120000
1 run 27 3130000
1 run 28 3140000
1 run 29 3130000
1 run 30 3110000
# point pass=1 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997489
1 halt 1 3130000
1 halt 2 3130000
1 halt 3 3150000
1 halt 4 3130000
1 halt 5 3130000
1 halt 6 3120000
1 halt 7 3170000
1 halt 8 3140000
1 halt 9 3140000
1 halt 10 3140000
1 halt 11 3160000
1 halt 12 3160000
1 halt 13 3140000
1 halt 14 3140000
1 halt 15 3140000
1 halt 16 3140000
1 halt 17 3150000
1 halt 18 3140000
1 halt 19 3150000
1 halt 20 3150000
1 halt 21 3140000
1 halt 22 3140000
1 halt 23 3140000
1 halt 24 3140000
1 halt 25 3140000
1 halt 26 3140000
1 halt 27 3270000
1 halt 28 3140000
1 halt 29 3130000
1 halt 30 3130000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997556
2 halt 1 3140000
2 halt 2 3140000
2 halt 3 3140000
2 halt 4 3130000
2 halt 5 3150000
2 halt 6 3140000
2 halt 7 3140000
2 halt 8 3160000
2 halt 9 3150000
2 halt 10 3140000
2 halt 11 3140000
2 halt 12 3140000
2 halt 13 3150000
2 halt 14 3140000
2 halt 15 3140000
2 halt 16 3140000
2 halt 17 3150000
2 halt 18 3140000
2 halt 19 3140000
2 halt 20 3150000
2 halt 21 3150000
2 halt 22 3150000
2 halt 23 3140000
2 halt 24 3150000
2 halt 25 3140000
2 halt 26 3140000
2 halt 27 3140000
2 halt 28 3140000
2 halt 29 3140000
2 halt 30 3140000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997613
2 run 1 3130000
2 run 2 3140000
2 run 3 3140000
2 run 4 3130000
2 run 5 3150000
2 run 6 3140000
2 run 7 3130000
2 run 8 3140000
2 run 9 3130000
2 run 10 3140000
2 run 11 3140000
2 run 12 3140000
2 run 13 3140000
2 run 14 3130000
2 run 15 3140000
2 run 16 3140000
2 run 17 3130000
2 run 18 3140000
2 run 19 3140000
2 run 20 3140000
2 run 21 3130000
2 run 22 3130000
2 run 23 3130000
2 run 24 3140000
2 run 25 3140000
2 run 26 3140000
2 run 27 3120000
2 run 28 3150000
2 run 29 3160000
2 run 30 3140000
# point pass=2 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997660
3 halt 1 3140000
3 halt 2 3150000
3 halt 3 3130000
3 halt 4 3140000
3 halt 5 3140000
3 halt 6 3130000
3 halt 7 3140000
3 halt 8 3150000
3 halt 9 3140000
3 halt 10 3140000
3 halt 11 3140000
3 halt 12 3140000
3 halt 13 3140000
3 halt 14 3140000
3 halt 15 3130000
3 halt 16 3140000
3 halt 17 3150000
3 halt 18 3140000
3 halt 19 3140000
3 halt 20 3140000
3 halt 21 3140000
3 halt 22 3130000
3 halt 23 3140000
3 halt 24 3140000
3 halt 25 3150000
3 halt 26 3150000
3 halt 27 3140000
3 halt 28 3140000
3 halt 29 3130000
3 halt 30 3130000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997700
3 run 1 3140000
3 run 2 3140000
3 run 3 3140000
3 run 4 3160000
3 run 5 3140000
3 run 6 3130000
3 run 7 3140000
3 run 8 3130000
3 run 9 3140000
3 run 10 3140000
3 run 11 3140000
3 run 12 3150000
3 run 13 3170000
3 run 14 3140000
3 run 15 3130000
3 run 16 3130000
3 run 17 3150000
3 run 18 3130000
3 run 19 3140000
3 run 20 3140000
3 run 21 3150000
3 run 22 3130000
3 run 23 3140000
3 run 24 3130000
3 run 25 3140000
3 run 26 3140000
3 run 27 3140000
3 run 28 3140000
3 run 29 3130000
3 run 30 3140000
# point pass=3 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997734
4 halt 1 3150000
4 halt 2 3140000
4 halt 3 3140000
4 halt 4 3140000
4 halt 5 3140000
4 halt 6 3140000
4 halt 7 3140000
4 halt 8 3140000
4 halt 9 3160000
4 halt 10 3140000
4 halt 11 3140000
4 halt 12 3140000
4 halt 13 3140000
4 halt 14 3140000
4 halt 15 3140000
4 halt 16 3130000
4 halt 17 3130000
4 halt 18 3140000
4 halt 19 3140000
4 halt 20 3130000
4 halt 21 3160000
4 halt 22 3140000
4 halt 23 3140000
4 halt 24 3150000
4 halt 25 3140000
4 halt 26 3140000
4 halt 27 3140000
4 halt 28 3140000
4 halt 29 3140000
4 halt 30 3130000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997763
4 run 1 3140000
4 run 2 3140000
4 run 3 3140000
4 run 4 3140000
4 run 5 3150000
4 run 6 3150000
4 run 7 3140000
4 run 8 3140000
4 run 9 3140000
4 run 10 3140000
4 run 11 3140000
4 run 12 3140000
4 run 13 3140000
4 run 14 3140000
4 run 15 3140000
4 run 16 3130000
4 run 17 3140000
4 run 18 3150000
4 run 19 3140000
4 run 20 3140000
4 run 21 3170000
4 run 22 3120000
4 run 23 3140000
4 run 24 3140000
4 run 25 3140000
4 run 26 3150000
4 run 27 3140000
4 run 28 3140000
4 run 29 3150000
4 run 30 3140000
# point pass=4 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997789
5 halt 1 3150000
5 halt 2 3150000
5 halt 3 3140000
5 halt 4 3140000
5 halt 5 3150000
5 halt 6 3150000
5 halt 7 3150000
5 halt 8 3140000
5 halt 9 3150000
5 halt 10 3150000
5 halt 11 3140000
5 halt 12 3140000
5 halt 13 3140000
5 halt 14 3140000
5 halt 15 3140000
5 halt 16 3150000
5 halt 17 3150000
5 halt 18 3160000
5 halt 19 3140000
5 halt 20 3140000
5 halt 21 3150000
5 halt 22 3150000
5 halt 23 3150000
5 halt 24 3150000
5 halt 25 3150000
5 halt 26 3160000
5 halt 27 3140000
5 halt 28 3150000
5 halt 29 3150000
5 halt 30 3150000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997804
5 run 1 3140000
5 run 2 3160000
5 run 3 3140000
5 run 4 3140000
5 run 5 3140000
5 run 6 3160000
5 run 7 3140000
5 run 8 3140000
5 run 9 3150000
5 run 10 3140000
5 run 11 3140000
5 run 12 3210000
5 run 13 3140000
5 run 14 3160000
5 run 15 3160000
5 run 16 3150000
5 run 17 3140000
5 run 18 3140000
5 run 19 3150000
5 run 20 3150000
5 run 21 3150000
5 run 22 3150000
5 run 23 3180000
5 run 24 3140000
5 run 25 3150000
5 run 26 3140000
5 run 27 3150000
5 run 28 3140000
5 run 29 3130000
5 run 30 3140000
# point pass=5 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997821
6 halt 1 3150000
6 halt 2 3160000
6 halt 3 3150000
6 halt 4 3140000
6 halt 5 3150000
6 halt 6 3140000
6 halt 7 3140000
6 halt 8 3140000
6 halt 9 3140000
6 halt 10 3140000
6 halt 11 3150000
6 halt 12 3140000
6 halt 13 3140000
6 halt 14 3140000
6 halt 15 3140000
6 halt 16 3140000
6 halt 17 3140000
6 halt 18 3130000
6 halt 19 3140000
6 halt 20 3140000
6 halt 21 3140000
6 halt 22 3140000
6 halt 23 3160000
6 halt 24 3150000
6 halt 25 3150000
6 halt 26 3150000
6 halt 27 3140000
6 halt 28 3140000
6 halt 29 3150000
6 halt 30 3140000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997835
6 run 1 3150000
6 run 2 3170000
6 run 3 3140000
6 run 4 3150000
6 run 5 3150000
6 run 6 3150000
6 run 7 3410000
6 run 8 3160000
6 run 9 3150000
6 run 10 3150000
6 run 11 3150000
6 run 12 3280000
6 run 13 3140000
6 run 14 3140000
6 run 15 3150000
6 run 16 3150000
6 run 17 3150000
6 run 18 3140000
6 run 19 3150000
6 run 20 3150000
6 run 21 3140000
6 run 22 3150000
6 run 23 3150000
6 run 24 3140000
6 run 25 3150000
6 run 26 3140000
6 run 27 3140000
6 run 28 3150000
6 run 29 3140000
6 run 30 3140000
# point pass=6 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997847
7 run 1 3140000
7 run 2 3140000
7 run 3 3150000
7 run 4 3140000
7 run 5 3140000
7 run 6 3140000
7 run 7 3140000
7 run 8 3140000
7 run 9 3140000
7 run 10 3140000
7 run 11 3140000
7 run 12 3140000
7 run 13 3130000
7 run 14 3140000
7 run 15 3140000
7 run 16 3140000
7 run 17 3140000
7 run 18 3140000
7 run 19 3140000
7 run 20 3160000
7 run 21 3140000
7 run 22 3130000
7 run 23 3140000
7 run 24 3140000
7 run 25 3140000
7 run 26 3140000
7 run 27 3140000
7 run 28 3150000
7 run 29 3130000
7 run 30 3140000
# point pass=7 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997856
7 halt 1 3140000
7 halt 2 3140000
7 halt 3 3230000
7 halt 4 3140000
7 halt 5 3130000
7 halt 6 3130000
7 halt 7 3140000
7 halt 8 3150000
7 halt 9 3140000
7 halt 10 3140000
7 halt 11 3130000
7 halt 12 3130000
7 halt 13 3140000
7 halt 14 3130000
7 halt 15 3140000
7 halt 16 3150000
7 halt 17 3150000
7 halt 18 3130000
7 halt 19 3140000
7 halt 20 3140000
7 halt 21 3130000
7 halt 22 3130000
7 halt 23 3120000
7 halt 24 3130000
7 halt 25 3160000
7 halt 26 3130000
7 halt 27 3130000
7 halt 28 3130000
7 halt 29 3130000
7 halt 30 3130000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997864
8 halt 1 3140000
8 halt 2 3130000
8 halt 3 3140000
8 halt 4 3140000
8 halt 5 3150000
8 halt 6 3140000
8 halt 7 3140000
8 halt 8 3140000
8 halt 9 3140000
8 halt 10 3130000
8 halt 11 3140000
8 halt 12 3140000
8 halt 13 3160000
8 halt 14 3150000
8 halt 15 3140000
8 halt 16 3150000
8 halt 17 3140000
8 halt 18 3150000
8 halt 19 3140000
8 halt 20 3150000
8 halt 21 3140000
8 halt 22 3150000
8 halt 23 3140000
8 halt 24 3140000
8 halt 25 3130000
8 halt 26 3130000
8 halt 27 3140000
8 halt 28 3140000
8 halt 29 3140000
8 halt 30 3140000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997871
8 run 1 3140000
8 run 2 3140000
8 run 3 3130000
8 run 4 3130000
8 run 5 3140000
8 run 6 3140000
8 run 7 3130000
8 run 8 3120000
8 run 9 3120000
8 run 10 3150000
8 run 11 3120000
8 run 12 3140000
8 run 13 3120000
8 run 14 3140000
8 run 15 3130000
8 run 16 3130000
8 run 17 3130000
8 run 18 3130000
8 run 19 3130000
8 run 20 3130000
8 run 21 3140000
8 run 22 3130000
8 run 23 3130000
8 run 24 3120000
8 run 25 3140000
8 run 26 3140000
8 run 27 3140000
8 run 28 3130000
8 run 29 3120000
8 run 30 3150000
# point pass=8 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997878
# done SystemTime { tv_sec: 1793980709, tv_nsec: 798912421 }
# passes used 8 of 8 (dropped: [])
# run - halt = -1.9 +/- 2.7 mW (per-pass sd 7.5); 2/8 passes positive; 6 sample excursions beyond 6 MAD
# per-pass differences (mW): -10.0 0.0 0.0 0.0 -10.0 10.0 5.0 -10.0
# board power while running 3139 mW; jobs/s 0.0; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 0.0 mW: measured/predicted = -inf, -0.7 sigma from it

View file

@ -0,0 +1,195 @@
# class t0-recall cores 1 link AXI
# wai-quantum-landscape-fpga meter passes=3 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793978728, tv_nsec: 458975042 }
# pass state sample power_uW
1 halt 1 3150000
1 halt 2 3160000
1 halt 3 3140000
1 halt 4 3150000
1 halt 5 3150000
1 halt 6 3150000
1 halt 7 3140000
1 halt 8 3140000
1 halt 9 3150000
1 halt 10 3150000
1 halt 11 3140000
1 halt 12 3150000
1 halt 13 3170000
1 halt 14 3160000
1 halt 15 3150000
1 halt 16 3140000
1 halt 17 3150000
1 halt 18 3150000
1 halt 19 3150000
1 halt 20 3170000
1 halt 21 3150000
1 halt 22 3150000
1 halt 23 3140000
1 halt 24 3140000
1 halt 25 3140000
1 halt 26 3140000
1 halt 27 3140000
1 halt 28 3150000
1 halt 29 3140000
1 halt 30 3150000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99996704
1 run 1 3330000
1 run 2 3320000
1 run 3 3300000
1 run 4 3300000
1 run 5 3300000
1 run 6 3310000
1 run 7 3300000
1 run 8 3290000
1 run 9 3290000
1 run 10 3330000
1 run 11 3300000
1 run 12 3300000
1 run 13 3300000
1 run 14 3300000
1 run 15 3310000
1 run 16 3300000
1 run 17 3300000
1 run 18 3300000
1 run 19 3290000
1 run 20 3300000
1 run 21 3320000
1 run 22 3290000
1 run 23 3300000
1 run 24 3300000
1 run 25 3320000
1 run 26 3300000
1 run 27 3310000
1 run 28 3300000
1 run 29 3300000
1 run 30 3300000
# point pass=1 state=run jobs_per_s=12499613.3 updates_per_s=0.0 clock_hz=99996898
2 run 1 3300000
2 run 2 3300000
2 run 3 3420000
2 run 4 3310000
2 run 5 3310000
2 run 6 3300000
2 run 7 3330000
2 run 8 3300000
2 run 9 3300000
2 run 10 3300000
2 run 11 3310000
2 run 12 3300000
2 run 13 3320000
2 run 14 3310000
2 run 15 3310000
2 run 16 3300000
2 run 17 3300000
2 run 18 3290000
2 run 19 3300000
2 run 20 3300000
2 run 21 3310000
2 run 22 3320000
2 run 23 3300000
2 run 24 3300000
2 run 25 3310000
2 run 26 3300000
2 run 27 3320000
2 run 28 3310000
2 run 29 3310000
2 run 30 3330000
# point pass=2 state=run jobs_per_s=12499633.8 updates_per_s=0.0 clock_hz=99997061
2 halt 1 3140000
2 halt 2 3130000
2 halt 3 3140000
2 halt 4 3150000
2 halt 5 3140000
2 halt 6 3140000
2 halt 7 3140000
2 halt 8 3140000
2 halt 9 3140000
2 halt 10 3130000
2 halt 11 3140000
2 halt 12 3130000
2 halt 13 3140000
2 halt 14 3140000
2 halt 15 3130000
2 halt 16 3140000
2 halt 17 3140000
2 halt 18 3150000
2 halt 19 3150000
2 halt 20 3130000
2 halt 21 3140000
2 halt 22 3140000
2 halt 23 3130000
2 halt 24 3140000
2 halt 25 3140000
2 halt 26 3140000
2 halt 27 3150000
2 halt 28 3140000
2 halt 29 3140000
2 halt 30 3130000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997197
3 run 1 3290000
3 run 2 3300000
3 run 3 3300000
3 run 4 3290000
3 run 5 3300000
3 run 6 3300000
3 run 7 3310000
3 run 8 3300000
3 run 9 3300000
3 run 10 3300000
3 run 11 3300000
3 run 12 3300000
3 run 13 3300000
3 run 14 3300000
3 run 15 3310000
3 run 16 3320000
3 run 17 3290000
3 run 18 3300000
3 run 19 3300000
3 run 20 3290000
3 run 21 3310000
3 run 22 3290000
3 run 23 3300000
3 run 24 3310000
3 run 25 3300000
3 run 26 3300000
3 run 27 3290000
3 run 28 3290000
3 run 29 3310000
3 run 30 3300000
# point pass=3 state=run jobs_per_s=12499665.2 updates_per_s=0.0 clock_hz=99997312
3 halt 1 3130000
3 halt 2 3170000
3 halt 3 3140000
3 halt 4 3160000
3 halt 5 3140000
3 halt 6 3150000
3 halt 7 3140000
3 halt 8 3140000
3 halt 9 3140000
3 halt 10 3150000
3 halt 11 3150000
3 halt 12 3150000
3 halt 13 3140000
3 halt 14 3140000
3 halt 15 3140000
3 halt 16 3140000
3 halt 17 3140000
3 halt 18 3140000
3 halt 19 3140000
3 halt 20 3140000
3 halt 21 3150000
3 halt 22 3140000
3 halt 23 3150000
3 halt 24 3140000
3 halt 25 3140000
3 halt 26 3150000
3 halt 27 3150000
3 halt 28 3140000
3 halt 29 3160000
3 halt 30 3150000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997408
# done SystemTime { tv_sec: 1793979268, tv_nsec: 808725935 }
# passes used 3 of 3 (dropped: [])
# run - halt = 158.3 +/- 4.4 mW (per-pass sd 7.6); 3/3 passes positive; 1 sample excursions beyond 6 MAD
# per-pass differences (mW): 150.0 165.0 160.0
# board power while running 3302 mW; jobs/s 12499637.5; updates/s 0.0; measured PL clock 99.997 MHz
# pre-registered prediction 162.0 mW: measured/predicted = 0.98, -0.8 sigma from it

View file

@ -0,0 +1,2 @@
settle, dense 64 (recall cue): clock 99.998 MHz, 8.0 clocks a job, 12499763 jobs/s, 0.000e0 cell updates/s, 1.831e11 accumulates/s
sample, sequential, 64 cells, 8 sweeps at T = 0.25: clock 99.998 MHz, 2059.0 clocks a job, 48567 jobs/s, 2.487e7 cell updates/s, 1.138e9 accumulates/s

View file

@ -0,0 +1,33 @@
The builds measured on 2026-11-06, and the testbenches' results. Copied from the tool's reports and
logs (its utilisation and timing reports, its build log, sha256sum), the lines that bear on the
README, without the reports' headers.
TOOL
Vivado v2026.1 (lin64), build 6511674. Blocks: zynq_ultra_ps_e 3.5; in the AXI builds also
axi_dwidth_converter 2.1 (twice) and axi_protocol_converter 2.1.
The AXI builds ran with JOBS 1, general.maxThreads 2, under a 10 GB memory cap; the tool's own
peak, as its log reports it, was 5,140 MB. An earlier attempt with JOBS 2 and no cap, on a 15 GB
machine shared with other builds, ran the machine out of memory.
BUILDS (part xck26-sfvc784-2LV-c, 100 MHz)
build sha256 of the .bit.bin LUTs registers BRAM tiles URAM DSP WNS WHS
AXI, final (write-back) bb82505526e33403073924373a64d3519394344e9bf6d0193634c157e42b6dfb 28145 (24.03%) 13773 4 4 20 0.993 ns 0.012 ns
AXI, first (no write-back) 3d84dbd355c9552c7593d3d5aa823284a4941acd25e5f3b7c92d5c0bc36ee441 27666 (23.62%) 13220 4 4 20 0.698 ns 0.012 ns
EMIO, one engine feea5015b2f8d85049fd48bcc66493a411d551326365f67b1db2ae65ec00074c 25539 (21.81%) 11121 0 4 20 0.294 ns 0.069 ns
EMIO, four engines 2a73f02b5a6097bd251962b7d91f5ca368180db162941465aa32129130b06b09 80927 (69.10%) 44355 0 16 80 0.441 ns 0.010 ns
(four engines: 13,925 of 14,640 slices, 95.12%; not loaded on the board this day)
All four: 0 failing endpoints, setup and hold. The AXI builds report one critical warning, a
false-path constraint for the EMIO port's synchroniser that finds no cell in them.
TESTBENCHES, on the scripts gen-tb records from this commit's model (sha256 of each script)
EMIO landscape_tb.script 4fb49e25f794849db750d688755ef452f05fb9146fd0cb016ad0b533dee5205e
Icarus Verilog 13.0: TB DONE: 174345 lines, 2668 checked reads, 0 mismatches, sim time 26571197000
xsim v2026.1: TB DONE: 174345 lines, 2668 checked reads, 0 mismatches, sim time 26571197000
AXI landscape_axi_tb.script 927b463dd40b9a3535fe77a770656415b58ce2dae33a93bf3299b3ab5252fc5c
171 write lists, 50 batches, 50 write-backs
Icarus Verilog 13.0: TB DONE: 175830 lines, 4464 checked reads, 0 mismatches, sim time 6878426000
xsim v2026.1: TB DONE: 175830 lines, 4464 checked reads, 0 mismatches, sim time 6878426000
A flipped expected word in a write-back's results (xsim, the earlier script):
MISMATCH line 1231: written back chain 0 word 2 got 00000000 want f0000000
The RTL the scripts ran against is the final AXI build's: no file under rtl/ that it is built
from has changed since that build but for comments.

View file

@ -0,0 +1,31 @@
certified by the host on the conformance set: settle bit-exact, sample bit-exact
parity earned on this board: settle bit-exact, sample bit-exact
the worked examples on the board and on the CPU reference, 200 seeds each
[PASS] talks at its own temperature: 200/200 answers identical
[PASS] talks at temperature 0: 200/200 answers identical
[PASS] clusters at its own temperature: 200/200 answers identical
[PASS] clusters at temperature 0: 200/200 answers identical
[PASS] selection at its own temperature: 200/200 answers identical
[PASS] selection at temperature 0: 200/200 answers identical
[PASS] chain at its own temperature: 200/200 answers identical
[PASS] chain at temperature 0: 200/200 answers identical
[PASS] assign-2x2 at its own temperature: 200/200 answers identical
[PASS] assign-2x2 at temperature 0: 200/200 answers identical
[PASS] blocks at its own temperature: 200/200 answers identical
[PASS] blocks at temperature 0: 200/200 answers identical
[PASS] assign-3x3 at its own temperature: 200/200 answers identical
[PASS] assign-3x3 at temperature 0: 200/200 answers identical
[PASS] colour-six at its own temperature: 200/200 answers identical
[PASS] colour-six at temperature 0: 200/200 answers identical
[PASS] colour-four at its own temperature: 200/200 answers identical
[PASS] colour-four at temperature 0: 200/200 answers identical
[PASS] piles at its own temperature: 200/200 answers identical
[PASS] piles at temperature 0: 200/200 answers identical
[PASS] cold-constraint at its own temperature: 200/200 answers identical
[PASS] cold-constraint at temperature 0: 200/200 answers identical
[PASS] cold-start at its own temperature: 200/200 answers identical
[PASS] cold-start at temperature 0: 200/200 answers identical
[PASS] too-costly at its own temperature: 200/200 answers identical
[PASS] too-costly at temperature 0: 200/200 answers identical
[PASS] typo: refused by the reader before any member runs: Line 6: `oppos` is not a statement epuML knows. Did you mean `oppose`?
27 checks, 0 failed

View file

@ -0,0 +1,48 @@
wai.quantum.landscape/1 member tests (quantum-conformance/landscape/vectors.json)
settle cases 13/13 bit for bit; chains 42/42 bit for bit
[PASS] settle kernel: the settle cases, bit for bit (state, sweeps, ending, accumulates), each held to the job
[PASS] sample kernel: the chain vectors, bit for bit (state, sweeps, ending, accumulates, draws), each held to the job
[PASS] cells: 650/650 cut points the host computes for the engine equal the set's, bit for bit
[PASS] cell h=-40 c=0 T=0.25: 100000 draws, counts [100000, 0, 0], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-40 c=2 T=1: 100000 draws, counts [100000, 0, 0], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-40 c=8 T=2.9: 100000 draws, counts [100000, 0, 0], z +1.27 -1.27 -0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-17 c=1 T=25: 100000 draws, counts [55805, 29689, 14506], z -1.59 +0.91 +1.07 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-17 c=8 T=0.7: 100000 draws, counts [99999, 1, 0], z -1.45 +1.45 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-5 c=1 T=1.8708286933869707: 100000 draws, counts [89058, 10527, 415], z -0.17 +0.28 -0.48 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-5 c=3 T=13.7: 100000 draws, counts [42641, 36802, 20557], z +0.11 -0.21 +0.12 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-3 c=1 T=0.3: 100000 draws, counts [99874, 126, 0], z +0.10 -0.10 -0.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-3 c=3 T=1.3: 100000 draws, counts [50015, 49537, 448], z +1.65 -1.37 -2.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-1 c=0 T=7.1: 100000 draws, counts [38122, 33245, 28633], z -0.00 +0.88 -0.91 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=-1 c=3 T=0.25: 100000 draws, counts [32, 99968, 0], z -0.27 +0.27 -0.11 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=0 T=1: 100000 draws, counts [33342, 33254, 33404], z +0.06 -0.53 +0.47 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=2 T=2.9: 100000 draws, counts [24786, 50120, 25094], z -1.88 +1.31 +0.37 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=0 c=8 T=25: 100000 draws, counts [29353, 41113, 29534], z -1.79 +2.16 -0.53 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=1 c=2 T=0.7: 100000 draws, counts [1130, 79767, 19103], z +0.97 -0.12 -0.14 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=1 c=8 T=1.8708286933869707: 100000 draws, counts [801, 96844, 2355], z +0.43 -1.26 +1.20 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=2 c=1 T=13.7: 100000 draws, counts [27835, 34700, 37465], z -0.48 -0.22 +0.66 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=2 c=8 T=0.3: 100000 draws, counts [0, 100000, 0], z -0.00 +0.01 -0.01 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=3 c=1 T=1.3: 100000 draws, counts [835, 17462, 81703], z +0.94 -0.59 +0.36 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=3 c=3 T=7.1: 100000 draws, counts [17600, 41216, 41184], z -0.66 +0.36 +0.15 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=5 c=1 T=0.25: 100000 draws, counts [0, 0, 100000], z +0.00 -0.11 +0.11 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=5 c=3 T=1: 100000 draws, counts [9, 12006, 87985], z +2.50 +0.84 -0.89 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=9 c=0 T=2.9: 100000 draws, counts [204, 4140, 95656], z +0.83 -2.31 +2.09 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=9 c=2 T=25: 100000 draws, counts [21552, 33829, 44619], z -1.18 +0.85 +0.17 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=0 T=0.7: 100000 draws, counts [0, 0, 100000], z +0.00 -0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=2 T=1.8708286933869707: 100000 draws, counts [0, 36, 99964], z -0.04 +0.53 -0.53 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=17 c=8 T=13.7: 100000 draws, counts [5263, 32393, 62344], z +0.64 +0.21 -0.50 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=40 c=2 T=0.3: 100000 draws, counts [0, 0, 100000], z +0.00 +0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] cell h=40 c=8 T=1.3: 100000 draws, counts [0, 0, 100000], z +0.00 -0.00 +0.00 (limit 4); 100000/100000 equal to the reference's draw
[PASS] battery one-pair: chi-square 10.7 on 8 degrees of freedom (critical 28.0)
[PASS] battery a-pair-apart: chi-square 45.3 on 26 degrees of freedom (critical 62.1)
[PASS] battery a-pair-far-apart: chi-square 75.3 on 79 degrees of freedom (critical 141.8)
[PASS] battery a-triangle: chi-square 31.0 on 26 degrees of freedom (critical 62.1)
[PASS] battery wide-fields-and-cost: chi-square 18.3 on 22 degrees of freedom (critical 55.2)
[PASS] battery a-small-field-cold: chi-square 6.9 on 4 degrees of freedom (critical 18.1)
[PASS] battery ending-at-zero: chi-square 17.1 on 11 degrees of freedom (critical 34.5)
[PASS] battery cooling: chi-square 16.8 on 23 degrees of freedom (critical 56.9)
[PASS] the all-at-once control (every cell moved together) fails the battery on ["one-pair", "a-pair-apart", "a-pair-far-apart", "a-triangle", "a-small-field-cold", "ending-at-zero", "cooling"]
[PASS] jobs: 31/31 requests answer as the set says (state, energy, verdict, mode, restarts, sweeps, accumulates, draws, replay digest, or the refusal)
[PASS] 2000/2000 random workloads (2-64 cells, fields -5..5, cost 0-3) settle exactly as the reference does
[PASS] 4096/4096 fields read back equal W s + b (dense W, fields across the engine's ±524287)
[PASS] the engine still answers with its ID at the end: no other session replaced it
44 checks, 0 failed

View file

@ -0,0 +1,7 @@
processor-to-logic latency, EMIO link: one board, the engine at its measured clock
timer: CNTVCT_EL0 at 99.999 MHz, one tick 10.0 ns
the timer alone (two reads, an ISB each) median 10 ns p10 10 p90 20 p99 20 max 12120 mean 12 (n 20000)
EMIO register read (handshake over GPIO) median 930 ns p10 920 p90 930 p99 940 max 104301 mean 954 (n 5000)
EMIO register write (handshake over GPIO) median 790 ns p10 780 p90 800 p99 800 max 30540 mean 808 (n 5000)
EMIO, plain register accesses: a full table (64 levels x 128 fields, 32835 register writes): median 29.00 ms of 6, 883 ns a write
EMIO, plain register accesses: 4096 chains of 8 cells x 4 sweeps: 0.060 s, 14.61 us a chain, 68443 chains/s

View file

@ -0,0 +1 @@
FPD_SLCR AFI_FS 0x00000a00: M_AXI_HPM0_FPD 128 bits; AFIFM2 RDCTRL 0x000003b0 WRCTRL 0x000003b0: S_AXI_HP0_FPD reads 128 bits, writes 128 bits (the AXI build is made for 128 on the master and for reads): they match

View file

@ -0,0 +1,2 @@
settle, dense 64 (recall cue): clock 99.998 MHz, 8.0 clocks a job, 12499778 jobs/s, 0.000e0 cell updates/s, 1.831e11 accumulates/s
sample, sequential, 64 cells, 8 sweeps at T = 0.25: clock 99.998 MHz, 2059.0 clocks a job, 48566 jobs/s, 2.487e7 cell updates/s, 1.138e9 accumulates/s

View file

@ -0,0 +1,11 @@
2026-11-06T12:38:56Z start; engine answers: ID and version 0x00404002; measured clock 99.997 MHz
2026-11-06T12:48:56Z step done
2026-11-06T12:48:56Z known answer skipped: KNOWN_ANSWER and KNOWN_BITSTREAM not set
2026-11-06T13:12:57Z null done exit=0
2026-11-06T13:42:58Z t0-recall done exit=0
2026-11-06T14:13:00Z gibbs-recall done exit=0
2026-11-06T14:37:01Z t0-idle done exit=0
PL0_REF_CTRL was 0x1010f00; nominal 50.00 MHz; measured 49.9989 MHz
2026-11-06T14:55:03Z 50 MHz done exit=0
PL0_REF_CTRL was 0x1011e00; nominal 100.00 MHz; measured 99.9981 MHz
2026-11-06T14:55:04Z end

View file

@ -0,0 +1,629 @@
# class gibbs-recall cores 1
# wai-quantum-fpga meter passes=10 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793972579, tv_nsec: 29912947 }
# pass state sample power_uW
1 run 1 3150000
1 run 2 3150000
1 run 3 3140000
1 run 4 3150000
1 run 5 3150000
1 run 6 3150000
1 run 7 3150000
1 run 8 3150000
1 run 9 3160000
1 run 10 3150000
1 run 11 3150000
1 run 12 3160000
1 run 13 3150000
1 run 14 3150000
1 run 15 3150000
1 run 16 3150000
1 run 17 3160000
1 run 18 3170000
1 run 19 3150000
1 run 20 3160000
1 run 21 3150000
1 run 22 3150000
1 run 23 3150000
1 run 24 3150000
1 run 25 3140000
1 run 26 3150000
1 run 27 3150000
1 run 28 3150000
1 run 29 3150000
1 run 30 3150000
# point pass=1 state=run jobs_per_s=48542.7 updates_per_s=24853863.3 clock_hz=99997947
1 halt 1 3140000
1 halt 2 3140000
1 halt 3 3130000
1 halt 4 3130000
1 halt 5 3140000
1 halt 6 3160000
1 halt 7 3130000
1 halt 8 3120000
1 halt 9 3120000
1 halt 10 3130000
1 halt 11 3140000
1 halt 12 3140000
1 halt 13 3140000
1 halt 14 3140000
1 halt 15 3150000
1 halt 16 3140000
1 halt 17 3130000
1 halt 18 3140000
1 halt 19 3130000
1 halt 20 3130000
1 halt 21 3140000
1 halt 22 3140000
1 halt 23 3150000
1 halt 24 3130000
1 halt 25 3140000
1 halt 26 3140000
1 halt 27 3120000
1 halt 28 3140000
1 halt 29 3130000
1 halt 30 3130000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997947
2 run 1 3130000
2 run 2 3140000
2 run 3 3160000
2 run 4 3140000
2 run 5 3140000
2 run 6 3140000
2 run 7 3140000
2 run 8 3140000
2 run 9 3140000
2 run 10 3140000
2 run 11 3150000
2 run 12 3150000
2 run 13 3140000
2 run 14 3160000
2 run 15 3140000
2 run 16 3140000
2 run 17 3140000
2 run 18 3190000
2 run 19 3150000
2 run 20 3140000
2 run 21 3140000
2 run 22 3150000
2 run 23 3140000
2 run 24 3140000
2 run 25 3140000
2 run 26 3160000
2 run 27 3140000
2 run 28 3140000
2 run 29 3140000
2 run 30 3140000
# point pass=2 state=run jobs_per_s=48542.7 updates_per_s=24853863.2 clock_hz=99997947
2 halt 1 3120000
2 halt 2 3120000
2 halt 3 3130000
2 halt 4 3130000
2 halt 5 3130000
2 halt 6 3140000
2 halt 7 3130000
2 halt 8 3140000
2 halt 9 3130000
2 halt 10 3130000
2 halt 11 3140000
2 halt 12 3130000
2 halt 13 3130000
2 halt 14 3120000
2 halt 15 3140000
2 halt 16 3140000
2 halt 17 3130000
2 halt 18 3140000
2 halt 19 3140000
2 halt 20 3140000
2 halt 21 3130000
2 halt 22 3130000
2 halt 23 3140000
2 halt 24 3140000
2 halt 25 3120000
2 halt 26 3140000
2 halt 27 3130000
2 halt 28 3120000
2 halt 29 3120000
2 halt 30 3130000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997947
3 run 1 3140000
3 run 2 3130000
3 run 3 3140000
3 run 4 3140000
3 run 5 3150000
3 run 6 3140000
3 run 7 3140000
3 run 8 3140000
3 run 9 3140000
3 run 10 3140000
3 run 11 3140000
3 run 12 3150000
3 run 13 3140000
3 run 14 3140000
3 run 15 3140000
3 run 16 3140000
3 run 17 3140000
3 run 18 3140000
3 run 19 3150000
3 run 20 3160000
3 run 21 3140000
3 run 22 3140000
3 run 23 3130000
3 run 24 3140000
3 run 25 3130000
3 run 26 3140000
3 run 27 3140000
3 run 28 3150000
3 run 29 3140000
3 run 30 3130000
# point pass=3 state=run jobs_per_s=48542.7 updates_per_s=24853863.4 clock_hz=99997947
3 halt 1 3120000
3 halt 2 3130000
3 halt 3 3130000
3 halt 4 3120000
3 halt 5 3120000
3 halt 6 3120000
3 halt 7 3130000
3 halt 8 3140000
3 halt 9 3120000
3 halt 10 3130000
3 halt 11 3120000
3 halt 12 3110000
3 halt 13 3120000
3 halt 14 3130000
3 halt 15 3120000
3 halt 16 3140000
3 halt 17 3140000
3 halt 18 3120000
3 halt 19 3120000
3 halt 20 3120000
3 halt 21 3130000
3 halt 22 3120000
3 halt 23 3120000
3 halt 24 3110000
3 halt 25 3140000
3 halt 26 3120000
3 halt 27 3110000
3 halt 28 3120000
3 halt 29 3110000
3 halt 30 3120000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997947
4 run 1 3140000
4 run 2 3150000
4 run 3 3140000
4 run 4 3150000
4 run 5 3150000
4 run 6 3130000
4 run 7 3130000
4 run 8 3140000
4 run 9 3140000
4 run 10 3140000
4 run 11 3150000
4 run 12 3140000
4 run 13 3160000
4 run 14 3150000
4 run 15 3140000
4 run 16 3140000
4 run 17 3140000
4 run 18 3150000
4 run 19 3150000
4 run 20 3140000
4 run 21 3150000
4 run 22 3140000
4 run 23 3140000
4 run 24 3140000
4 run 25 3140000
4 run 26 3150000
4 run 27 3140000
4 run 28 3150000
4 run 29 3150000
4 run 30 3140000
# point pass=4 state=run jobs_per_s=48542.7 updates_per_s=24853863.2 clock_hz=99997946
4 halt 1 3120000
4 halt 2 3160000
4 halt 3 3140000
4 halt 4 3120000
4 halt 5 3110000
4 halt 6 3130000
4 halt 7 3140000
4 halt 8 3130000
4 halt 9 3110000
4 halt 10 3150000
4 halt 11 3120000
4 halt 12 3140000
4 halt 13 3130000
4 halt 14 3130000
4 halt 15 3130000
4 halt 16 3130000
4 halt 17 3140000
4 halt 18 3130000
4 halt 19 3130000
4 halt 20 3130000
4 halt 21 3140000
4 halt 22 3130000
4 halt 23 3130000
4 halt 24 3130000
4 halt 25 3140000
4 halt 26 3130000
4 halt 27 3130000
4 halt 28 3130000
4 halt 29 3120000
4 halt 30 3130000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997946
5 run 1 3140000
5 run 2 3140000
5 run 3 3140000
5 run 4 3140000
5 run 5 3140000
5 run 6 3140000
5 run 7 3170000
5 run 8 3130000
5 run 9 3150000
5 run 10 3150000
5 run 11 3140000
5 run 12 3150000
5 run 13 3140000
5 run 14 3140000
5 run 15 3150000
5 run 16 3140000
5 run 17 3140000
5 run 18 3140000
5 run 19 3150000
5 run 20 3150000
5 run 21 3150000
5 run 22 3160000
5 run 23 3140000
5 run 24 3160000
5 run 25 3150000
5 run 26 3140000
5 run 27 3140000
5 run 28 3150000
5 run 29 3140000
5 run 30 3160000
# point pass=5 state=run jobs_per_s=48542.7 updates_per_s=24853863.1 clock_hz=99997946
5 halt 1 3140000
5 halt 2 3120000
5 halt 3 3130000
5 halt 4 3120000
5 halt 5 3120000
5 halt 6 3120000
5 halt 7 3150000
5 halt 8 3120000
5 halt 9 3130000
5 halt 10 3140000
5 halt 11 3120000
5 halt 12 3510000
5 halt 13 3130000
5 halt 14 3130000
5 halt 15 3130000
5 halt 16 3130000
5 halt 17 3140000
5 halt 18 3150000
5 halt 19 3140000
5 halt 20 3140000
5 halt 21 3130000
5 halt 22 3140000
5 halt 23 3130000
5 halt 24 3130000
5 halt 25 3190000
5 halt 26 3140000
5 halt 27 3150000
5 halt 28 3130000
5 halt 29 3140000
5 halt 30 3150000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997946
6 halt 1 3130000
6 halt 2 3140000
6 halt 3 3140000
6 halt 4 3150000
6 halt 5 3140000
6 halt 6 3140000
6 halt 7 3150000
6 halt 8 3140000
6 halt 9 3140000
6 halt 10 3140000
6 halt 11 3130000
6 halt 12 3140000
6 halt 13 3130000
6 halt 14 3140000
6 halt 15 3160000
6 halt 16 3150000
6 halt 17 3140000
6 halt 18 3140000
6 halt 19 3140000
6 halt 20 3140000
6 halt 21 3140000
6 halt 22 3140000
6 halt 23 3140000
6 halt 24 3150000
6 halt 25 3140000
6 halt 26 3120000
6 halt 27 3130000
6 halt 28 3140000
6 halt 29 3130000
6 halt 30 3130000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997946
6 run 1 3140000
6 run 2 3140000
6 run 3 3150000
6 run 4 3150000
6 run 5 3140000
6 run 6 3140000
6 run 7 3150000
6 run 8 3140000
6 run 9 3140000
6 run 10 3140000
6 run 11 3140000
6 run 12 3150000
6 run 13 3140000
6 run 14 3150000
6 run 15 3140000
6 run 16 3140000
6 run 17 3150000
6 run 18 3140000
6 run 19 3140000
6 run 20 3140000
6 run 21 3140000
6 run 22 3140000
6 run 23 3150000
6 run 24 3140000
6 run 25 3140000
6 run 26 3140000
6 run 27 3150000
6 run 28 3140000
6 run 29 3130000
6 run 30 3140000
# point pass=6 state=run jobs_per_s=48542.7 updates_per_s=24853862.9 clock_hz=99997946
7 halt 1 3150000
7 halt 2 3120000
7 halt 3 3130000
7 halt 4 3130000
7 halt 5 3140000
7 halt 6 3130000
7 halt 7 3130000
7 halt 8 3140000
7 halt 9 3150000
7 halt 10 3130000
7 halt 11 3130000
7 halt 12 3140000
7 halt 13 3120000
7 halt 14 3130000
7 halt 15 3130000
7 halt 16 3130000
7 halt 17 3130000
7 halt 18 3140000
7 halt 19 3130000
7 halt 20 3120000
7 halt 21 3140000
7 halt 22 3120000
7 halt 23 3130000
7 halt 24 3140000
7 halt 25 3140000
7 halt 26 3130000
7 halt 27 3130000
7 halt 28 3140000
7 halt 29 3130000
7 halt 30 3120000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997946
7 run 1 3150000
7 run 2 3150000
7 run 3 3150000
7 run 4 3140000
7 run 5 3150000
7 run 6 3140000
7 run 7 3140000
7 run 8 3140000
7 run 9 3150000
7 run 10 3150000
7 run 11 3150000
7 run 12 3160000
7 run 13 3160000
7 run 14 3150000
7 run 15 3150000
7 run 16 3150000
7 run 17 3150000
7 run 18 3150000
7 run 19 3150000
7 run 20 3160000
7 run 21 3160000
7 run 22 3150000
7 run 23 3140000
7 run 24 3150000
7 run 25 3150000
7 run 26 3140000
7 run 27 3150000
7 run 28 3150000
7 run 29 3160000
7 run 30 3140000
# point pass=7 state=run jobs_per_s=48542.7 updates_per_s=24853862.9 clock_hz=99997946
8 run 1 3140000
8 run 2 3140000
8 run 3 3140000
8 run 4 3140000
8 run 5 3140000
8 run 6 3140000
8 run 7 3150000
8 run 8 3140000
8 run 9 3130000
8 run 10 3160000
8 run 11 3140000
8 run 12 3140000
8 run 13 3140000
8 run 14 3140000
8 run 15 3140000
8 run 16 3140000
8 run 17 3140000
8 run 18 3140000
8 run 19 3140000
8 run 20 3160000
8 run 21 3150000
8 run 22 3140000
8 run 23 3140000
8 run 24 3150000
8 run 25 3140000
8 run 26 3140000
8 run 27 3150000
8 run 28 3140000
8 run 29 3140000
8 run 30 3140000
# point pass=8 state=run jobs_per_s=48542.7 updates_per_s=24853862.9 clock_hz=99997946
8 halt 1 3130000
8 halt 2 3140000
8 halt 3 3140000
8 halt 4 3140000
8 halt 5 3130000
8 halt 6 3130000
8 halt 7 3130000
8 halt 8 3140000
8 halt 9 3150000
8 halt 10 3130000
8 halt 11 3120000
8 halt 12 3130000
8 halt 13 3130000
8 halt 14 3130000
8 halt 15 3140000
8 halt 16 3140000
8 halt 17 3140000
8 halt 18 3150000
8 halt 19 3140000
8 halt 20 3120000
8 halt 21 3120000
8 halt 22 3130000
8 halt 23 3130000
8 halt 24 3140000
8 halt 25 3120000
8 halt 26 3130000
8 halt 27 3120000
8 halt 28 3130000
8 halt 29 3140000
8 halt 30 3140000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997946
9 run 1 3150000
9 run 2 3150000
9 run 3 3150000
9 run 4 3150000
9 run 5 3150000
9 run 6 3160000
9 run 7 3150000
9 run 8 3150000
9 run 9 3150000
9 run 10 3150000
9 run 11 3150000
9 run 12 3150000
9 run 13 3150000
9 run 14 3160000
9 run 15 3140000
9 run 16 3150000
9 run 17 3150000
9 run 18 3140000
9 run 19 3150000
9 run 20 3150000
9 run 21 3150000
9 run 22 3150000
9 run 23 3150000
9 run 24 3140000
9 run 25 3150000
9 run 26 3150000
9 run 27 3150000
9 run 28 3150000
9 run 29 3160000
9 run 30 3150000
# point pass=9 state=run jobs_per_s=48542.7 updates_per_s=24853862.8 clock_hz=99997945
9 halt 1 3140000
9 halt 2 3150000
9 halt 3 3150000
9 halt 4 3140000
9 halt 5 3140000
9 halt 6 3140000
9 halt 7 3150000
9 halt 8 3140000
9 halt 9 3150000
9 halt 10 3140000
9 halt 11 3160000
9 halt 12 3130000
9 halt 13 3140000
9 halt 14 3140000
9 halt 15 3130000
9 halt 16 3140000
9 halt 17 3140000
9 halt 18 3140000
9 halt 19 3140000
9 halt 20 3140000
9 halt 21 3140000
9 halt 22 3140000
9 halt 23 3140000
9 halt 24 3140000
9 halt 25 3140000
9 halt 26 3150000
9 halt 27 3140000
9 halt 28 3150000
9 halt 29 3140000
9 halt 30 3150000
# point pass=9 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997946
10 halt 1 3140000
10 halt 2 3140000
10 halt 3 3140000
10 halt 4 3140000
10 halt 5 3140000
10 halt 6 3130000
10 halt 7 3140000
10 halt 8 3140000
10 halt 9 3140000
10 halt 10 3140000
10 halt 11 3140000
10 halt 12 3140000
10 halt 13 3140000
10 halt 14 3150000
10 halt 15 3140000
10 halt 16 3140000
10 halt 17 3130000
10 halt 18 3140000
10 halt 19 3140000
10 halt 20 3140000
10 halt 21 3140000
10 halt 22 3140000
10 halt 23 3150000
10 halt 24 3130000
10 halt 25 3140000
10 halt 26 3130000
10 halt 27 3140000
10 halt 28 3140000
10 halt 29 3130000
10 halt 30 3140000
# point pass=10 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997947
10 run 1 3150000
10 run 2 3150000
10 run 3 3150000
10 run 4 3150000
10 run 5 3150000
10 run 6 3160000
10 run 7 3140000
10 run 8 3150000
10 run 9 3150000
10 run 10 3150000
10 run 11 3160000
10 run 12 3150000
10 run 13 3150000
10 run 14 3150000
10 run 15 3180000
10 run 16 3150000
10 run 17 3140000
10 run 18 3140000
10 run 19 3170000
10 run 20 3160000
10 run 21 3150000
10 run 22 3150000
10 run 23 3150000
10 run 24 3160000
10 run 25 3150000
10 run 26 3140000
10 run 27 3150000
10 run 28 3160000
10 run 29 3150000
10 run 30 3150000
# point pass=10 state=run jobs_per_s=48542.7 updates_per_s=24853862.9 clock_hz=99997945
# done SystemTime { tv_sec: 1793974380, tv_nsec: 187856982 }
# passes used 10 of 10 (dropped: [])
# run - halt = 11.0 +/- 1.8 mW (per-pass sd 5.7); 9/10 passes positive; 2 sample excursions beyond 6 MAD
# per-pass differences (mW): 10.0 10.0 20.0 10.0 10.0 0.0 20.0 10.0 10.0 10.0
# board power while running 3144 mW; jobs/s 48542.7; updates/s 24853863.1; measured PL clock 99.998 MHz
# pre-registered prediction 12.0 mW: measured/predicted = 0.92, -0.6 sigma from it

View file

@ -0,0 +1,505 @@
# class t0-recall cores 1
# wai-quantum-fpga meter passes=8 settle=60s samples=30 null=true sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793969336, tv_nsec: 827384277 }
# pass state sample power_uW
1 halt 1 3130000
1 halt 2 3120000
1 halt 3 3130000
1 halt 4 3140000
1 halt 5 3530000
1 halt 6 3150000
1 halt 7 3130000
1 halt 8 3130000
1 halt 9 3130000
1 halt 10 3130000
1 halt 11 3140000
1 halt 12 3140000
1 halt 13 3130000
1 halt 14 3150000
1 halt 15 3710000
1 halt 16 3130000
1 halt 17 3130000
1 halt 18 3130000
1 halt 19 3130000
1 halt 20 3120000
1 halt 21 3140000
1 halt 22 3150000
1 halt 23 3120000
1 halt 24 3130000
1 halt 25 3130000
1 halt 26 3120000
1 halt 27 3130000
1 halt 28 3120000
1 halt 29 3140000
1 halt 30 3120000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998017
1 run 1 3130000
1 run 2 3140000
1 run 3 3140000
1 run 4 3120000
1 run 5 3140000
1 run 6 3140000
1 run 7 3120000
1 run 8 3120000
1 run 9 3120000
1 run 10 3120000
1 run 11 3150000
1 run 12 3120000
1 run 13 3130000
1 run 14 3120000
1 run 15 3130000
1 run 16 3140000
1 run 17 3120000
1 run 18 3130000
1 run 19 3130000
1 run 20 3130000
1 run 21 3130000
1 run 22 3140000
1 run 23 3120000
1 run 24 3130000
1 run 25 3120000
1 run 26 3140000
1 run 27 3120000
1 run 28 3130000
1 run 29 3130000
1 run 30 3130000
# point pass=1 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998017
2 run 1 3150000
2 run 2 3140000
2 run 3 3140000
2 run 4 3130000
2 run 5 3140000
2 run 6 3140000
2 run 7 3140000
2 run 8 3120000
2 run 9 3150000
2 run 10 3140000
2 run 11 3130000
2 run 12 3120000
2 run 13 3130000
2 run 14 3130000
2 run 15 3140000
2 run 16 3140000
2 run 17 3120000
2 run 18 3140000
2 run 19 3140000
2 run 20 3130000
2 run 21 3140000
2 run 22 3140000
2 run 23 3140000
2 run 24 3150000
2 run 25 3140000
2 run 26 3140000
2 run 27 3140000
2 run 28 3130000
2 run 29 3120000
2 run 30 3140000
# point pass=2 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998017
2 halt 1 3140000
2 halt 2 3140000
2 halt 3 3140000
2 halt 4 3130000
2 halt 5 3130000
2 halt 6 3150000
2 halt 7 3120000
2 halt 8 3130000
2 halt 9 3120000
2 halt 10 3130000
2 halt 11 3140000
2 halt 12 3130000
2 halt 13 3140000
2 halt 14 3140000
2 halt 15 3130000
2 halt 16 3120000
2 halt 17 3120000
2 halt 18 3130000
2 halt 19 3120000
2 halt 20 3140000
2 halt 21 3150000
2 halt 22 3140000
2 halt 23 3130000
2 halt 24 3130000
2 halt 25 3140000
2 halt 26 3130000
2 halt 27 3120000
2 halt 28 3130000
2 halt 29 3450000
2 halt 30 3120000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998017
3 run 1 3130000
3 run 2 3130000
3 run 3 3130000
3 run 4 3120000
3 run 5 3130000
3 run 6 3120000
3 run 7 3130000
3 run 8 3130000
3 run 9 3120000
3 run 10 3140000
3 run 11 3150000
3 run 12 3120000
3 run 13 3140000
3 run 14 3130000
3 run 15 3120000
3 run 16 3130000
3 run 17 3140000
3 run 18 3110000
3 run 19 3140000
3 run 20 3130000
3 run 21 3120000
3 run 22 3140000
3 run 23 3140000
3 run 24 3130000
3 run 25 3130000
3 run 26 3130000
3 run 27 3150000
3 run 28 3120000
3 run 29 3130000
3 run 30 3120000
# point pass=3 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998016
3 halt 1 3120000
3 halt 2 3120000
3 halt 3 3120000
3 halt 4 3120000
3 halt 5 3110000
3 halt 6 3110000
3 halt 7 3140000
3 halt 8 3120000
3 halt 9 3110000
3 halt 10 3130000
3 halt 11 3110000
3 halt 12 3120000
3 halt 13 3120000
3 halt 14 3120000
3 halt 15 3140000
3 halt 16 3140000
3 halt 17 3110000
3 halt 18 3130000
3 halt 19 3120000
3 halt 20 3120000
3 halt 21 3120000
3 halt 22 3120000
3 halt 23 3120000
3 halt 24 3140000
3 halt 25 3110000
3 halt 26 3120000
3 halt 27 3120000
3 halt 28 3120000
3 halt 29 3110000
3 halt 30 3110000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998016
4 halt 1 3120000
4 halt 2 3130000
4 halt 3 3130000
4 halt 4 3140000
4 halt 5 3130000
4 halt 6 3130000
4 halt 7 3110000
4 halt 8 3130000
4 halt 9 3120000
4 halt 10 3120000
4 halt 11 3140000
4 halt 12 3140000
4 halt 13 3140000
4 halt 14 3130000
4 halt 15 3120000
4 halt 16 3120000
4 halt 17 3120000
4 halt 18 3130000
4 halt 19 3120000
4 halt 20 3120000
4 halt 21 3120000
4 halt 22 3120000
4 halt 23 3130000
4 halt 24 3130000
4 halt 25 3120000
4 halt 26 3120000
4 halt 27 3110000
4 halt 28 3120000
4 halt 29 3120000
4 halt 30 3120000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998016
4 run 1 3150000
4 run 2 3130000
4 run 3 3130000
4 run 4 3130000
4 run 5 3140000
4 run 6 3130000
4 run 7 3130000
4 run 8 3140000
4 run 9 3150000
4 run 10 3140000
4 run 11 3140000
4 run 12 3130000
4 run 13 3140000
4 run 14 3140000
4 run 15 3140000
4 run 16 3140000
4 run 17 3130000
4 run 18 3130000
4 run 19 3130000
4 run 20 3140000
4 run 21 3150000
4 run 22 3130000
4 run 23 3130000
4 run 24 3150000
4 run 25 3140000
4 run 26 3130000
4 run 27 3140000
4 run 28 3130000
4 run 29 3130000
4 run 30 3130000
# point pass=4 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998016
5 run 1 3140000
5 run 2 3140000
5 run 3 3140000
5 run 4 3140000
5 run 5 3140000
5 run 6 3150000
5 run 7 3140000
5 run 8 3140000
5 run 9 3140000
5 run 10 3140000
5 run 11 3140000
5 run 12 3130000
5 run 13 3140000
5 run 14 3140000
5 run 15 3130000
5 run 16 3140000
5 run 17 3130000
5 run 18 3130000
5 run 19 3140000
5 run 20 3140000
5 run 21 3150000
5 run 22 3140000
5 run 23 3140000
5 run 24 3140000
5 run 25 3130000
5 run 26 3130000
5 run 27 3150000
5 run 28 3130000
5 run 29 3150000
5 run 30 3140000
# point pass=5 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998015
5 halt 1 3120000
5 halt 2 3130000
5 halt 3 3140000
5 halt 4 3140000
5 halt 5 3130000
5 halt 6 3130000
5 halt 7 3120000
5 halt 8 3130000
5 halt 9 3130000
5 halt 10 3120000
5 halt 11 3130000
5 halt 12 3140000
5 halt 13 3140000
5 halt 14 3140000
5 halt 15 3120000
5 halt 16 3130000
5 halt 17 3140000
5 halt 18 3140000
5 halt 19 3130000
5 halt 20 3130000
5 halt 21 3120000
5 halt 22 3140000
5 halt 23 3120000
5 halt 24 3120000
5 halt 25 3130000
5 halt 26 3140000
5 halt 27 3120000
5 halt 28 3120000
5 halt 29 3120000
5 halt 30 3130000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998015
6 halt 1 3130000
6 halt 2 3140000
6 halt 3 3120000
6 halt 4 3120000
6 halt 5 3130000
6 halt 6 3140000
6 halt 7 3140000
6 halt 8 3130000
6 halt 9 3120000
6 halt 10 3140000
6 halt 11 3120000
6 halt 12 3130000
6 halt 13 3120000
6 halt 14 3120000
6 halt 15 3120000
6 halt 16 3130000
6 halt 17 3120000
6 halt 18 3120000
6 halt 19 3140000
6 halt 20 3140000
6 halt 21 3150000
6 halt 22 3130000
6 halt 23 3140000
6 halt 24 3130000
6 halt 25 3140000
6 halt 26 3120000
6 halt 27 3140000
6 halt 28 3140000
6 halt 29 3120000
6 halt 30 3130000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998016
6 run 1 3110000
6 run 2 3120000
6 run 3 3130000
6 run 4 3140000
6 run 5 3150000
6 run 6 3140000
6 run 7 3140000
6 run 8 3140000
6 run 9 3140000
6 run 10 3130000
6 run 11 3130000
6 run 12 3140000
6 run 13 3180000
6 run 14 3140000
6 run 15 3140000
6 run 16 3140000
6 run 17 3140000
6 run 18 3550000
6 run 19 3130000
6 run 20 3130000
6 run 21 3140000
6 run 22 3140000
6 run 23 3140000
6 run 24 3140000
6 run 25 3140000
6 run 26 3140000
6 run 27 3150000
6 run 28 3150000
6 run 29 3140000
6 run 30 3130000
# point pass=6 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998017
7 run 1 3140000
7 run 2 3140000
7 run 3 3140000
7 run 4 3140000
7 run 5 3130000
7 run 6 3140000
7 run 7 3130000
7 run 8 3140000
7 run 9 3140000
7 run 10 3130000
7 run 11 3140000
7 run 12 3140000
7 run 13 3140000
7 run 14 3140000
7 run 15 3150000
7 run 16 3150000
7 run 17 3140000
7 run 18 3140000
7 run 19 3130000
7 run 20 3140000
7 run 21 3140000
7 run 22 3140000
7 run 23 3160000
7 run 24 3150000
7 run 25 3130000
7 run 26 3140000
7 run 27 3140000
7 run 28 3140000
7 run 29 3140000
7 run 30 3140000
# point pass=7 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998016
7 halt 1 3130000
7 halt 2 3140000
7 halt 3 3140000
7 halt 4 3140000
7 halt 5 3140000
7 halt 6 3140000
7 halt 7 3140000
7 halt 8 3140000
7 halt 9 3140000
7 halt 10 3130000
7 halt 11 3140000
7 halt 12 3150000
7 halt 13 3130000
7 halt 14 3140000
7 halt 15 3140000
7 halt 16 3140000
7 halt 17 3140000
7 halt 18 3130000
7 halt 19 3140000
7 halt 20 3160000
7 halt 21 3140000
7 halt 22 3140000
7 halt 23 3140000
7 halt 24 3140000
7 halt 25 3140000
7 halt 26 3130000
7 halt 27 3140000
7 halt 28 3140000
7 halt 29 3130000
7 halt 30 3140000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997626
8 run 1 3140000
8 run 2 3140000
8 run 3 3140000
8 run 4 3140000
8 run 5 3140000
8 run 6 3140000
8 run 7 3130000
8 run 8 3150000
8 run 9 3150000
8 run 10 3120000
8 run 11 3140000
8 run 12 3140000
8 run 13 3140000
8 run 14 3130000
8 run 15 3140000
8 run 16 3140000
8 run 17 3150000
8 run 18 3130000
8 run 19 3140000
8 run 20 3130000
8 run 21 3140000
8 run 22 3140000
8 run 23 3130000
8 run 24 3140000
8 run 25 3140000
8 run 26 3140000
8 run 27 3140000
8 run 28 3130000
8 run 29 3140000
8 run 30 3140000
# point pass=8 state=run jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997571
8 halt 1 3120000
8 halt 2 3130000
8 halt 3 3140000
8 halt 4 3130000
8 halt 5 3140000
8 halt 6 3140000
8 halt 7 3130000
8 halt 8 3130000
8 halt 9 3120000
8 halt 10 3140000
8 halt 11 3130000
8 halt 12 3120000
8 halt 13 3140000
8 halt 14 3150000
8 halt 15 3140000
8 halt 16 3130000
8 halt 17 3130000
8 halt 18 3130000
8 halt 19 3130000
8 halt 20 3120000
8 halt 21 3120000
8 halt 22 3120000
8 halt 23 3140000
8 halt 24 3130000
8 halt 25 3130000
8 halt 26 3120000
8 halt 27 3140000
8 halt 28 3140000
8 halt 29 3140000
8 halt 30 3140000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997630
# done SystemTime { tv_sec: 1793970777, tv_nsec: 754326175 }
# passes used 8 of 8 (dropped: [])
# run - halt = 8.1 +/- 1.9 mW (per-pass sd 5.3); 6/8 passes positive; 5 sample excursions beyond 6 MAD
# per-pass differences (mW): 0.0 10.0 10.0 15.0 10.0 10.0 0.0 10.0
# board power while running 3137 mW; jobs/s 0.0; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 0.0 mW: measured/predicted = inf, 4.3 sigma from it

View file

@ -0,0 +1,610 @@
# class t0-recall cores 1
# wai-quantum-fpga meter passes=1 settle=0s samples=300 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793968736, tv_nsec: 357321553 }
# pass state sample power_uW
1 run 1 3530000
1 run 2 3300000
1 run 3 3290000
1 run 4 3300000
1 run 5 3310000
1 run 6 3300000
1 run 7 3300000
1 run 8 3300000
1 run 9 3300000
1 run 10 3320000
1 run 11 3300000
1 run 12 3300000
1 run 13 3300000
1 run 14 3310000
1 run 15 3290000
1 run 16 3300000
1 run 17 3320000
1 run 18 3290000
1 run 19 3300000
1 run 20 3320000
1 run 21 3300000
1 run 22 3300000
1 run 23 3300000
1 run 24 3300000
1 run 25 3300000
1 run 26 3310000
1 run 27 3320000
1 run 28 3300000
1 run 29 3300000
1 run 30 3290000
1 run 31 3290000
1 run 32 3310000
1 run 33 3300000
1 run 34 3320000
1 run 35 3300000
1 run 36 3310000
1 run 37 3300000
1 run 38 3320000
1 run 39 3320000
1 run 40 3300000
1 run 41 3300000
1 run 42 3310000
1 run 43 3300000
1 run 44 3300000
1 run 45 3300000
1 run 46 3300000
1 run 47 3320000
1 run 48 3300000
1 run 49 3300000
1 run 50 3300000
1 run 51 3300000
1 run 52 3300000
1 run 53 3300000
1 run 54 3300000
1 run 55 3310000
1 run 56 3300000
1 run 57 3300000
1 run 58 3300000
1 run 59 3310000
1 run 60 3300000
1 run 61 3290000
1 run 62 3360000
1 run 63 3300000
1 run 64 3300000
1 run 65 3290000
1 run 66 3300000
1 run 67 3300000
1 run 68 3290000
1 run 69 3300000
1 run 70 3320000
1 run 71 3330000
1 run 72 3300000
1 run 73 3320000
1 run 74 3310000
1 run 75 3310000
1 run 76 3340000
1 run 77 3300000
1 run 78 3310000
1 run 79 3320000
1 run 80 3300000
1 run 81 3300000
1 run 82 3300000
1 run 83 3310000
1 run 84 3300000
1 run 85 3290000
1 run 86 3290000
1 run 87 3300000
1 run 88 3300000
1 run 89 3300000
1 run 90 3290000
1 run 91 3300000
1 run 92 3320000
1 run 93 3300000
1 run 94 3320000
1 run 95 3300000
1 run 96 3300000
1 run 97 3300000
1 run 98 3300000
1 run 99 3300000
1 run 100 3310000
1 run 101 3300000
1 run 102 3600000
1 run 103 3300000
1 run 104 3300000
1 run 105 3310000
1 run 106 3310000
1 run 107 3300000
1 run 108 3310000
1 run 109 3300000
1 run 110 3290000
1 run 111 3300000
1 run 112 3300000
1 run 113 3310000
1 run 114 3310000
1 run 115 3300000
1 run 116 3300000
1 run 117 3320000
1 run 118 3300000
1 run 119 3300000
1 run 120 3300000
1 run 121 3330000
1 run 122 3290000
1 run 123 3290000
1 run 124 3290000
1 run 125 3320000
1 run 126 3310000
1 run 127 3280000
1 run 128 3300000
1 run 129 3300000
1 run 130 3300000
1 run 131 3290000
1 run 132 3290000
1 run 133 3290000
1 run 134 3300000
1 run 135 3290000
1 run 136 3300000
1 run 137 3290000
1 run 138 3300000
1 run 139 3300000
1 run 140 3290000
1 run 141 3290000
1 run 142 3290000
1 run 143 3290000
1 run 144 3300000
1 run 145 3290000
1 run 146 3290000
1 run 147 3300000
1 run 148 3290000
1 run 149 3320000
1 run 150 3300000
1 run 151 3290000
1 run 152 3300000
1 run 153 3290000
1 run 154 3290000
1 run 155 3310000
1 run 156 3300000
1 run 157 3320000
1 run 158 3300000
1 run 159 3310000
1 run 160 3330000
1 run 161 3290000
1 run 162 3290000
1 run 163 3310000
1 run 164 3300000
1 run 165 3290000
1 run 166 3300000
1 run 167 3300000
1 run 168 3300000
1 run 169 3290000
1 run 170 3300000
1 run 171 3290000
1 run 172 3290000
1 run 173 3290000
1 run 174 3310000
1 run 175 3300000
1 run 176 3290000
1 run 177 3290000
1 run 178 3290000
1 run 179 3300000
1 run 180 3300000
1 run 181 3320000
1 run 182 3300000
1 run 183 3300000
1 run 184 3310000
1 run 185 3300000
1 run 186 3310000
1 run 187 3300000
1 run 188 3320000
1 run 189 3320000
1 run 190 3300000
1 run 191 3310000
1 run 192 3300000
1 run 193 3300000
1 run 194 3300000
1 run 195 3300000
1 run 196 3300000
1 run 197 3300000
1 run 198 3310000
1 run 199 3310000
1 run 200 3290000
1 run 201 3300000
1 run 202 3320000
1 run 203 3300000
1 run 204 3320000
1 run 205 3310000
1 run 206 3310000
1 run 207 3300000
1 run 208 3310000
1 run 209 3300000
1 run 210 3310000
1 run 211 3300000
1 run 212 3320000
1 run 213 3330000
1 run 214 3310000
1 run 215 3310000
1 run 216 3310000
1 run 217 3300000
1 run 218 3310000
1 run 219 3300000
1 run 220 3290000
1 run 221 3320000
1 run 222 3300000
1 run 223 3300000
1 run 224 3290000
1 run 225 3300000
1 run 226 3300000
1 run 227 3300000
1 run 228 3300000
1 run 229 3310000
1 run 230 3310000
1 run 231 3300000
1 run 232 3300000
1 run 233 3300000
1 run 234 3300000
1 run 235 3300000
1 run 236 3320000
1 run 237 3290000
1 run 238 3300000
1 run 239 3310000
1 run 240 3300000
1 run 241 3310000
1 run 242 3320000
1 run 243 3300000
1 run 244 3320000
1 run 245 3310000
1 run 246 3300000
1 run 247 3300000
1 run 248 3300000
1 run 249 3310000
1 run 250 3300000
1 run 251 3310000
1 run 252 3300000
1 run 253 3320000
1 run 254 3310000
1 run 255 3290000
1 run 256 3300000
1 run 257 3310000
1 run 258 3300000
1 run 259 3310000
1 run 260 3310000
1 run 261 3310000
1 run 262 3300000
1 run 263 3300000
1 run 264 3300000
1 run 265 3300000
1 run 266 3300000
1 run 267 3320000
1 run 268 3320000
1 run 269 3300000
1 run 270 3300000
1 run 271 3310000
1 run 272 3300000
1 run 273 3300000
1 run 274 3300000
1 run 275 3300000
1 run 276 3330000
1 run 277 3310000
1 run 278 3310000
1 run 279 3300000
1 run 280 3300000
1 run 281 3300000
1 run 282 3300000
1 run 283 3310000
1 run 284 3310000
1 run 285 3310000
1 run 286 3310000
1 run 287 3290000
1 run 288 3310000
1 run 289 3310000
1 run 290 3300000
1 run 291 3320000
1 run 292 3300000
1 run 293 3300000
1 run 294 3320000
1 run 295 3310000
1 run 296 3300000
1 run 297 3300000
1 run 298 3300000
1 run 299 3660000
1 run 300 3330000
# point pass=1 state=run jobs_per_s=12499751.7 updates_per_s=0.0 clock_hz=99998011
1 halt 1 3370000
1 halt 2 3140000
1 halt 3 3140000
1 halt 4 3140000
1 halt 5 3130000
1 halt 6 3130000
1 halt 7 3140000
1 halt 8 3150000
1 halt 9 3160000
1 halt 10 3140000
1 halt 11 3140000
1 halt 12 3140000
1 halt 13 3140000
1 halt 14 3550000
1 halt 15 3130000
1 halt 16 3140000
1 halt 17 3140000
1 halt 18 3140000
1 halt 19 3130000
1 halt 20 3140000
1 halt 21 3140000
1 halt 22 3160000
1 halt 23 3150000
1 halt 24 3140000
1 halt 25 3140000
1 halt 26 3150000
1 halt 27 3140000
1 halt 28 3140000
1 halt 29 3140000
1 halt 30 3140000
1 halt 31 3140000
1 halt 32 3140000
1 halt 33 3150000
1 halt 34 3140000
1 halt 35 3150000
1 halt 36 3150000
1 halt 37 3140000
1 halt 38 3140000
1 halt 39 3140000
1 halt 40 3140000
1 halt 41 3150000
1 halt 42 3140000
1 halt 43 3140000
1 halt 44 3150000
1 halt 45 3150000
1 halt 46 3160000
1 halt 47 3130000
1 halt 48 3140000
1 halt 49 3140000
1 halt 50 3140000
1 halt 51 3140000
1 halt 52 3140000
1 halt 53 3140000
1 halt 54 3150000
1 halt 55 3130000
1 halt 56 3130000
1 halt 57 3140000
1 halt 58 3130000
1 halt 59 3140000
1 halt 60 3140000
1 halt 61 3140000
1 halt 62 3140000
1 halt 63 3140000
1 halt 64 3140000
1 halt 65 3140000
1 halt 66 3140000
1 halt 67 3140000
1 halt 68 3140000
1 halt 69 3150000
1 halt 70 3140000
1 halt 71 3140000
1 halt 72 3150000
1 halt 73 3140000
1 halt 74 3140000
1 halt 75 3140000
1 halt 76 3140000
1 halt 77 3150000
1 halt 78 3150000
1 halt 79 3140000
1 halt 80 3140000
1 halt 81 3140000
1 halt 82 3130000
1 halt 83 3130000
1 halt 84 3140000
1 halt 85 3140000
1 halt 86 3150000
1 halt 87 3140000
1 halt 88 3150000
1 halt 89 3150000
1 halt 90 3140000
1 halt 91 3130000
1 halt 92 3140000
1 halt 93 3130000
1 halt 94 3140000
1 halt 95 3140000
1 halt 96 3140000
1 halt 97 3140000
1 halt 98 3140000
1 halt 99 3140000
1 halt 100 3140000
1 halt 101 3150000
1 halt 102 3140000
1 halt 103 3140000
1 halt 104 3140000
1 halt 105 3140000
1 halt 106 3140000
1 halt 107 3140000
1 halt 108 3140000
1 halt 109 3150000
1 halt 110 3140000
1 halt 111 3130000
1 halt 112 3140000
1 halt 113 3140000
1 halt 114 3140000
1 halt 115 3140000
1 halt 116 3140000
1 halt 117 3150000
1 halt 118 3140000
1 halt 119 3140000
1 halt 120 3130000
1 halt 121 3140000
1 halt 122 3140000
1 halt 123 3140000
1 halt 124 3150000
1 halt 125 3140000
1 halt 126 3140000
1 halt 127 3140000
1 halt 128 3140000
1 halt 129 3140000
1 halt 130 3150000
1 halt 131 3140000
1 halt 132 3150000
1 halt 133 3150000
1 halt 134 3140000
1 halt 135 3130000
1 halt 136 3140000
1 halt 137 3140000
1 halt 138 3140000
1 halt 139 3140000
1 halt 140 3140000
1 halt 141 3150000
1 halt 142 3140000
1 halt 143 3140000
1 halt 144 3140000
1 halt 145 3130000
1 halt 146 3150000
1 halt 147 3140000
1 halt 148 3140000
1 halt 149 3140000
1 halt 150 3130000
1 halt 151 3140000
1 halt 152 3130000
1 halt 153 3140000
1 halt 154 3140000
1 halt 155 3130000
1 halt 156 3150000
1 halt 157 3140000
1 halt 158 3130000
1 halt 159 3140000
1 halt 160 3140000
1 halt 161 3140000
1 halt 162 3150000
1 halt 163 3140000
1 halt 164 3160000
1 halt 165 3150000
1 halt 166 3130000
1 halt 167 3140000
1 halt 168 3140000
1 halt 169 3130000
1 halt 170 3140000
1 halt 171 3140000
1 halt 172 3140000
1 halt 173 3140000
1 halt 174 3140000
1 halt 175 3160000
1 halt 176 3140000
1 halt 177 3130000
1 halt 178 3130000
1 halt 179 3150000
1 halt 180 3150000
1 halt 181 3140000
1 halt 182 3140000
1 halt 183 3140000
1 halt 184 3140000
1 halt 185 3140000
1 halt 186 3140000
1 halt 187 3150000
1 halt 188 3150000
1 halt 189 3130000
1 halt 190 3130000
1 halt 191 3140000
1 halt 192 3140000
1 halt 193 3130000
1 halt 194 3140000
1 halt 195 3150000
1 halt 196 3150000
1 halt 197 3140000
1 halt 198 3130000
1 halt 199 3140000
1 halt 200 3140000
1 halt 201 3140000
1 halt 202 3140000
1 halt 203 3140000
1 halt 204 3150000
1 halt 205 3140000
1 halt 206 3140000
1 halt 207 3130000
1 halt 208 3140000
1 halt 209 3140000
1 halt 210 3140000
1 halt 211 3160000
1 halt 212 3140000
1 halt 213 3130000
1 halt 214 3140000
1 halt 215 3140000
1 halt 216 3130000
1 halt 217 3140000
1 halt 218 3140000
1 halt 219 3160000
1 halt 220 3150000
1 halt 221 3140000
1 halt 222 3140000
1 halt 223 3140000
1 halt 224 3130000
1 halt 225 3140000
1 halt 226 3140000
1 halt 227 3130000
1 halt 228 3150000
1 halt 229 3140000
1 halt 230 3140000
1 halt 231 3130000
1 halt 232 3130000
1 halt 233 3150000
1 halt 234 3140000
1 halt 235 3140000
1 halt 236 3130000
1 halt 237 3140000
1 halt 238 3130000
1 halt 239 3130000
1 halt 240 3140000
1 halt 241 3140000
1 halt 242 3140000
1 halt 243 3150000
1 halt 244 3130000
1 halt 245 3140000
1 halt 246 3140000
1 halt 247 3140000
1 halt 248 3140000
1 halt 249 3140000
1 halt 250 3140000
1 halt 251 3140000
1 halt 252 3130000
1 halt 253 3120000
1 halt 254 3130000
1 halt 255 3130000
1 halt 256 3120000
1 halt 257 3140000
1 halt 258 3120000
1 halt 259 3140000
1 halt 260 3130000
1 halt 261 3130000
1 halt 262 3120000
1 halt 263 3120000
1 halt 264 3130000
1 halt 265 3130000
1 halt 266 3140000
1 halt 267 3130000
1 halt 268 3140000
1 halt 269 3140000
1 halt 270 3130000
1 halt 271 3130000
1 halt 272 3130000
1 halt 273 3130000
1 halt 274 3150000
1 halt 275 3150000
1 halt 276 3120000
1 halt 277 3130000
1 halt 278 3130000
1 halt 279 3130000
1 halt 280 3130000
1 halt 281 3120000
1 halt 282 3140000
1 halt 283 3150000
1 halt 284 3130000
1 halt 285 3130000
1 halt 286 3120000
1 halt 287 3130000
1 halt 288 3120000
1 halt 289 3130000
1 halt 290 3120000
1 halt 291 3150000
1 halt 292 3120000
1 halt 293 3130000
1 halt 294 3120000
1 halt 295 3130000
1 halt 296 3130000
1 halt 297 3120000
1 halt 298 3140000
1 halt 299 3140000
1 halt 300 3130000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998009
# done SystemTime { tv_sec: 1793969336, tv_nsec: 762934283 }
# passes used 1 of 1 (dropped: [])
# run - halt = 160.0 +/- NaN mW (per-pass sd NaN); 1/1 passes positive; 7 sample excursions beyond 6 MAD
# per-pass differences (mW): 160.0
# board power while running 3300 mW; jobs/s 12499751.7; updates/s 0.0; measured PL clock 99.998 MHz

View file

@ -0,0 +1,505 @@
# class t0-idle cores 1
# wai-quantum-fpga meter passes=8 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793974380, tv_nsec: 248171637 }
# pass state sample power_uW
1 run 1 3130000
1 run 2 3140000
1 run 3 3130000
1 run 4 3140000
1 run 5 3140000
1 run 6 3140000
1 run 7 3150000
1 run 8 3140000
1 run 9 3150000
1 run 10 3140000
1 run 11 3150000
1 run 12 3150000
1 run 13 3140000
1 run 14 3140000
1 run 15 3150000
1 run 16 3140000
1 run 17 3150000
1 run 18 3150000
1 run 19 3140000
1 run 20 3150000
1 run 21 3140000
1 run 22 3160000
1 run 23 3140000
1 run 24 3150000
1 run 25 3150000
1 run 26 3150000
1 run 27 3150000
1 run 28 3140000
1 run 29 3140000
1 run 30 3150000
# point pass=1 state=run jobs_per_s=33332655.7 updates_per_s=0.0 clock_hz=99997945
1 halt 1 3130000
1 halt 2 3140000
1 halt 3 3130000
1 halt 4 3140000
1 halt 5 3130000
1 halt 6 3130000
1 halt 7 3130000
1 halt 8 3130000
1 halt 9 3130000
1 halt 10 3120000
1 halt 11 3120000
1 halt 12 3140000
1 halt 13 3130000
1 halt 14 3130000
1 halt 15 3130000
1 halt 16 3140000
1 halt 17 3120000
1 halt 18 3140000
1 halt 19 3140000
1 halt 20 3140000
1 halt 21 3130000
1 halt 22 3130000
1 halt 23 3130000
1 halt 24 3140000
1 halt 25 3120000
1 halt 26 3130000
1 halt 27 3140000
1 halt 28 3140000
1 halt 29 3130000
1 halt 30 3140000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997945
2 halt 1 3140000
2 halt 2 3360000
2 halt 3 3140000
2 halt 4 3140000
2 halt 5 3140000
2 halt 6 3140000
2 halt 7 3150000
2 halt 8 3130000
2 halt 9 3140000
2 halt 10 3140000
2 halt 11 3130000
2 halt 12 3130000
2 halt 13 3140000
2 halt 14 3130000
2 halt 15 3160000
2 halt 16 3150000
2 halt 17 3140000
2 halt 18 3140000
2 halt 19 3130000
2 halt 20 3150000
2 halt 21 3140000
2 halt 22 3130000
2 halt 23 3140000
2 halt 24 3150000
2 halt 25 3140000
2 halt 26 3130000
2 halt 27 3140000
2 halt 28 3130000
2 halt 29 3130000
2 halt 30 3130000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997945
2 run 1 3120000
2 run 2 3120000
2 run 3 3130000
2 run 4 3130000
2 run 5 3110000
2 run 6 3120000
2 run 7 3150000
2 run 8 3120000
2 run 9 3140000
2 run 10 3130000
2 run 11 3130000
2 run 12 3130000
2 run 13 3120000
2 run 14 3130000
2 run 15 3140000
2 run 16 3140000
2 run 17 3130000
2 run 18 3130000
2 run 19 3130000
2 run 20 3120000
2 run 21 3130000
2 run 22 3140000
2 run 23 3140000
2 run 24 3140000
2 run 25 3130000
2 run 26 3140000
2 run 27 3130000
2 run 28 3140000
2 run 29 3120000
2 run 30 3150000
# point pass=2 state=run jobs_per_s=33332686.8 updates_per_s=0.0 clock_hz=99998037
3 run 1 3140000
3 run 2 3140000
3 run 3 3130000
3 run 4 3150000
3 run 5 3140000
3 run 6 3140000
3 run 7 3130000
3 run 8 3140000
3 run 9 3140000
3 run 10 3130000
3 run 11 3140000
3 run 12 3160000
3 run 13 3140000
3 run 14 3140000
3 run 15 3150000
3 run 16 3150000
3 run 17 3120000
3 run 18 3130000
3 run 19 3130000
3 run 20 3130000
3 run 21 3540000
3 run 22 3130000
3 run 23 3130000
3 run 24 3120000
3 run 25 3120000
3 run 26 3130000
3 run 27 3150000
3 run 28 3130000
3 run 29 3130000
3 run 30 3140000
# point pass=3 state=run jobs_per_s=33332682.8 updates_per_s=0.0 clock_hz=99998024
3 halt 1 3140000
3 halt 2 3130000
3 halt 3 3110000
3 halt 4 3120000
3 halt 5 3120000
3 halt 6 3120000
3 halt 7 3120000
3 halt 8 3120000
3 halt 9 3140000
3 halt 10 3120000
3 halt 11 3120000
3 halt 12 3120000
3 halt 13 3120000
3 halt 14 3130000
3 halt 15 3120000
3 halt 16 3110000
3 halt 17 3130000
3 halt 18 3120000
3 halt 19 3110000
3 halt 20 3110000
3 halt 21 3120000
3 halt 22 3130000
3 halt 23 3120000
3 halt 24 3130000
3 halt 25 3130000
3 halt 26 3120000
3 halt 27 3130000
3 halt 28 3120000
3 halt 29 3130000
3 halt 30 3120000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99998013
4 run 1 3120000
4 run 2 3120000
4 run 3 3120000
4 run 4 3140000
4 run 5 3130000
4 run 6 3140000
4 run 7 3130000
4 run 8 3120000
4 run 9 3130000
4 run 10 3130000
4 run 11 3110000
4 run 12 3130000
4 run 13 3130000
4 run 14 3130000
4 run 15 3140000
4 run 16 3120000
4 run 17 3130000
4 run 18 3120000
4 run 19 3120000
4 run 20 3140000
4 run 21 3140000
4 run 22 3120000
4 run 23 3130000
4 run 24 3130000
4 run 25 3120000
4 run 26 3120000
4 run 27 3120000
4 run 28 3140000
4 run 29 3140000
4 run 30 3130000
# point pass=4 state=run jobs_per_s=33332675.3 updates_per_s=0.0 clock_hz=99998003
4 halt 1 3130000
4 halt 2 3120000
4 halt 3 3140000
4 halt 4 3130000
4 halt 5 3140000
4 halt 6 3140000
4 halt 7 3130000
4 halt 8 3130000
4 halt 9 3150000
4 halt 10 3130000
4 halt 11 3140000
4 halt 12 3140000
4 halt 13 3130000
4 halt 14 3140000
4 halt 15 3140000
4 halt 16 3130000
4 halt 17 3160000
4 halt 18 3150000
4 halt 19 3140000
4 halt 20 3140000
4 halt 21 3130000
4 halt 22 3140000
4 halt 23 3140000
4 halt 24 3130000
4 halt 25 3140000
4 halt 26 3150000
4 halt 27 3140000
4 halt 28 3140000
4 halt 29 3140000
4 halt 30 3140000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997996
5 halt 1 3140000
5 halt 2 3130000
5 halt 3 3130000
5 halt 4 3140000
5 halt 5 3140000
5 halt 6 3150000
5 halt 7 3140000
5 halt 8 3140000
5 halt 9 3130000
5 halt 10 3150000
5 halt 11 3130000
5 halt 12 3140000
5 halt 13 3140000
5 halt 14 3150000
5 halt 15 3140000
5 halt 16 3140000
5 halt 17 3140000
5 halt 18 3140000
5 halt 19 3140000
5 halt 20 3130000
5 halt 21 3140000
5 halt 22 3140000
5 halt 23 3130000
5 halt 24 3140000
5 halt 25 3160000
5 halt 26 3120000
5 halt 27 3120000
5 halt 28 3120000
5 halt 29 3130000
5 halt 30 3130000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997989
5 run 1 3150000
5 run 2 3150000
5 run 3 3150000
5 run 4 3140000
5 run 5 3130000
5 run 6 3150000
5 run 7 3140000
5 run 8 3140000
5 run 9 3140000
5 run 10 3130000
5 run 11 3160000
5 run 12 3140000
5 run 13 3140000
5 run 14 3140000
5 run 15 3140000
5 run 16 3140000
5 run 17 3140000
5 run 18 3140000
5 run 19 3140000
5 run 20 3150000
5 run 21 3140000
5 run 22 3140000
5 run 23 3460000
5 run 24 3140000
5 run 25 3140000
5 run 26 3150000
5 run 27 3140000
5 run 28 3140000
5 run 29 3140000
5 run 30 3140000
# point pass=5 state=run jobs_per_s=33332668.6 updates_per_s=0.0 clock_hz=99997984
6 halt 1 3140000
6 halt 2 3140000
6 halt 3 3140000
6 halt 4 3140000
6 halt 5 3130000
6 halt 6 3140000
6 halt 7 3140000
6 halt 8 3150000
6 halt 9 3130000
6 halt 10 3140000
6 halt 11 3130000
6 halt 12 3130000
6 halt 13 3130000
6 halt 14 3140000
6 halt 15 3130000
6 halt 16 3130000
6 halt 17 3140000
6 halt 18 3140000
6 halt 19 3140000
6 halt 20 3140000
6 halt 21 3130000
6 halt 22 3150000
6 halt 23 3150000
6 halt 24 3130000
6 halt 25 3140000
6 halt 26 3140000
6 halt 27 3140000
6 halt 28 3140000
6 halt 29 3140000
6 halt 30 3130000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997979
6 run 1 3140000
6 run 2 3130000
6 run 3 3140000
6 run 4 3130000
6 run 5 3120000
6 run 6 3130000
6 run 7 3140000
6 run 8 3140000
6 run 9 3140000
6 run 10 3130000
6 run 11 3150000
6 run 12 3130000
6 run 13 3140000
6 run 14 3130000
6 run 15 3130000
6 run 16 3130000
6 run 17 3130000
6 run 18 3130000
6 run 19 3160000
6 run 20 3150000
6 run 21 3130000
6 run 22 3140000
6 run 23 3140000
6 run 24 3140000
6 run 25 3120000
6 run 26 3120000
6 run 27 3140000
6 run 28 3150000
6 run 29 3130000
6 run 30 3120000
# point pass=6 state=run jobs_per_s=33332666.1 updates_per_s=0.0 clock_hz=99997975
7 run 1 3140000
7 run 2 3130000
7 run 3 3150000
7 run 4 3140000
7 run 5 3130000
7 run 6 3150000
7 run 7 3140000
7 run 8 3150000
7 run 9 3130000
7 run 10 3140000
7 run 11 3140000
7 run 12 3130000
7 run 13 3130000
7 run 14 3140000
7 run 15 3130000
7 run 16 3150000
7 run 17 3150000
7 run 18 3140000
7 run 19 3140000
7 run 20 3120000
7 run 21 3120000
7 run 22 3140000
7 run 23 3130000
7 run 24 3130000
7 run 25 3150000
7 run 26 3130000
7 run 27 3130000
7 run 28 3120000
7 run 29 3130000
7 run 30 3130000
# point pass=7 state=run jobs_per_s=33332665.0 updates_per_s=0.0 clock_hz=99997972
7 halt 1 3120000
7 halt 2 3140000
7 halt 3 3130000
7 halt 4 3140000
7 halt 5 3140000
7 halt 6 3130000
7 halt 7 3140000
7 halt 8 3130000
7 halt 9 3130000
7 halt 10 3140000
7 halt 11 3130000
7 halt 12 3130000
7 halt 13 3140000
7 halt 14 3130000
7 halt 15 3140000
7 halt 16 3130000
7 halt 17 3140000
7 halt 18 3120000
7 halt 19 3120000
7 halt 20 3130000
7 halt 21 3120000
7 halt 22 3130000
7 halt 23 3130000
7 halt 24 3120000
7 halt 25 3120000
7 halt 26 3120000
7 halt 27 3140000
7 halt 28 3150000
7 halt 29 3120000
7 halt 30 3140000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997971
8 run 1 3150000
8 run 2 3150000
8 run 3 3140000
8 run 4 3140000
8 run 5 3140000
8 run 6 3130000
8 run 7 3130000
8 run 8 3150000
8 run 9 3140000
8 run 10 3150000
8 run 11 3130000
8 run 12 3130000
8 run 13 3130000
8 run 14 3140000
8 run 15 3140000
8 run 16 3140000
8 run 17 3140000
8 run 18 3120000
8 run 19 3130000
8 run 20 3120000
8 run 21 3130000
8 run 22 3140000
8 run 23 3130000
8 run 24 3130000
8 run 25 3150000
8 run 26 3130000
8 run 27 3140000
8 run 28 3140000
8 run 29 3140000
8 run 30 3130000
# point pass=8 state=run jobs_per_s=33332663.2 updates_per_s=0.0 clock_hz=99997967
8 halt 1 3130000
8 halt 2 3130000
8 halt 3 3130000
8 halt 4 3130000
8 halt 5 3140000
8 halt 6 3120000
8 halt 7 3120000
8 halt 8 3140000
8 halt 9 3150000
8 halt 10 3130000
8 halt 11 3140000
8 halt 12 3120000
8 halt 13 3120000
8 halt 14 3130000
8 halt 15 3140000
8 halt 16 3130000
8 halt 17 3140000
8 halt 18 3130000
8 halt 19 3140000
8 halt 20 3140000
8 halt 21 3140000
8 halt 22 3140000
8 halt 23 3120000
8 halt 24 3140000
8 halt 25 3130000
8 halt 26 3130000
8 halt 27 3120000
8 halt 28 3120000
8 halt 29 3140000
8 halt 30 3140000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997966
# done SystemTime { tv_sec: 1793975821, tv_nsec: 177880976 }
# passes used 8 of 8 (dropped: [])
# run - halt = 1.9 +/- 4.0 mW (per-pass sd 11.3); 4/8 passes positive; 3 sample excursions beyond 6 MAD
# per-pass differences (mW): 10.0 -10.0 20.0 -10.0 0.0 -10.0 5.0 10.0
# board power while running 3136 mW; jobs/s 33332670.4; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 4.0 mW: measured/predicted = 0.47, -0.5 sigma from it

View file

@ -0,0 +1,381 @@
# class t0-recall cores 1
# wai-quantum-fpga meter passes=6 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793975822, tv_nsec: 342834432 }
# pass state sample power_uW
1 run 1 3190000
1 run 2 3190000
1 run 3 3610000
1 run 4 3190000
1 run 5 3200000
1 run 6 3190000
1 run 7 3190000
1 run 8 3190000
1 run 9 3190000
1 run 10 3190000
1 run 11 3180000
1 run 12 3180000
1 run 13 3200000
1 run 14 3200000
1 run 15 3180000
1 run 16 3180000
1 run 17 3180000
1 run 18 3190000
1 run 19 3180000
1 run 20 3190000
1 run 21 3190000
1 run 22 3210000
1 run 23 3180000
1 run 24 3180000
1 run 25 3190000
1 run 26 3190000
1 run 27 3190000
1 run 28 3200000
1 run 29 3190000
1 run 30 3180000
# point pass=1 state=run jobs_per_s=6249874.2 updates_per_s=0.0 clock_hz=49998982
1 halt 1 3110000
1 halt 2 3130000
1 halt 3 3120000
1 halt 4 3110000
1 halt 5 3120000
1 halt 6 3120000
1 halt 7 3110000
1 halt 8 3120000
1 halt 9 3120000
1 halt 10 3130000
1 halt 11 3120000
1 halt 12 3120000
1 halt 13 3110000
1 halt 14 3110000
1 halt 15 3110000
1 halt 16 3110000
1 halt 17 3100000
1 halt 18 3120000
1 halt 19 3110000
1 halt 20 3110000
1 halt 21 3110000
1 halt 22 3110000
1 halt 23 3100000
1 halt 24 3110000
1 halt 25 3120000
1 halt 26 3110000
1 halt 27 3110000
1 halt 28 3110000
1 halt 29 3110000
1 halt 30 3100000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49998982
2 halt 1 3110000
2 halt 2 3110000
2 halt 3 3110000
2 halt 4 3110000
2 halt 5 3110000
2 halt 6 3110000
2 halt 7 3130000
2 halt 8 3110000
2 halt 9 3110000
2 halt 10 3100000
2 halt 11 3110000
2 halt 12 3110000
2 halt 13 3110000
2 halt 14 3110000
2 halt 15 3110000
2 halt 16 3110000
2 halt 17 3100000
2 halt 18 3110000
2 halt 19 3110000
2 halt 20 3100000
2 halt 21 3120000
2 halt 22 3130000
2 halt 23 3110000
2 halt 24 3100000
2 halt 25 3110000
2 halt 26 3120000
2 halt 27 3100000
2 halt 28 3120000
2 halt 29 3120000
2 halt 30 3140000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49998981
2 run 1 3190000
2 run 2 3190000
2 run 3 3180000
2 run 4 3200000
2 run 5 3190000
2 run 6 3180000
2 run 7 3190000
2 run 8 3190000
2 run 9 3180000
2 run 10 3180000
2 run 11 3190000
2 run 12 3190000
2 run 13 3190000
2 run 14 3180000
2 run 15 3190000
2 run 16 3190000
2 run 17 3200000
2 run 18 3190000
2 run 19 3210000
2 run 20 3190000
2 run 21 3200000
2 run 22 3190000
2 run 23 3200000
2 run 24 3200000
2 run 25 3190000
2 run 26 3190000
2 run 27 3200000
2 run 28 3200000
2 run 29 3190000
2 run 30 3190000
# point pass=2 state=run jobs_per_s=6249874.0 updates_per_s=0.0 clock_hz=49998981
3 halt 1 3110000
3 halt 2 3110000
3 halt 3 3110000
3 halt 4 3100000
3 halt 5 3110000
3 halt 6 3110000
3 halt 7 3120000
3 halt 8 3110000
3 halt 9 3120000
3 halt 10 3110000
3 halt 11 3110000
3 halt 12 3110000
3 halt 13 3120000
3 halt 14 3120000
3 halt 15 3130000
3 halt 16 3130000
3 halt 17 3110000
3 halt 18 3120000
3 halt 19 3110000
3 halt 20 3120000
3 halt 21 3120000
3 halt 22 3110000
3 halt 23 3110000
3 halt 24 3130000
3 halt 25 3110000
3 halt 26 3120000
3 halt 27 3100000
3 halt 28 3110000
3 halt 29 3110000
3 halt 30 3110000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49998981
3 run 1 3170000
3 run 2 3170000
3 run 3 3190000
3 run 4 3200000
3 run 5 3170000
3 run 6 3170000
3 run 7 3180000
3 run 8 3170000
3 run 9 3180000
3 run 10 3170000
3 run 11 3180000
3 run 12 3190000
3 run 13 3200000
3 run 14 3170000
3 run 15 3180000
3 run 16 3180000
3 run 17 3180000
3 run 18 3180000
3 run 19 3180000
3 run 20 3180000
3 run 21 3200000
3 run 22 3190000
3 run 23 3200000
3 run 24 3190000
3 run 25 3190000
3 run 26 3190000
3 run 27 3190000
3 run 28 3200000
3 run 29 3190000
3 run 30 3190000
# point pass=3 state=run jobs_per_s=6249873.9 updates_per_s=0.0 clock_hz=49998980
4 halt 1 3120000
4 halt 2 3100000
4 halt 3 3100000
4 halt 4 3100000
4 halt 5 3100000
4 halt 6 3100000
4 halt 7 3110000
4 halt 8 3100000
4 halt 9 3110000
4 halt 10 3100000
4 halt 11 3100000
4 halt 12 3100000
4 halt 13 3100000
4 halt 14 3100000
4 halt 15 3100000
4 halt 16 3100000
4 halt 17 3110000
4 halt 18 3100000
4 halt 19 3100000
4 halt 20 3100000
4 halt 21 3090000
4 halt 22 3110000
4 halt 23 3110000
4 halt 24 3100000
4 halt 25 3100000
4 halt 26 3100000
4 halt 27 3100000
4 halt 28 3090000
4 halt 29 3100000
4 halt 30 3100000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49998980
4 run 1 3180000
4 run 2 3190000
4 run 3 3180000
4 run 4 3190000
4 run 5 3190000
4 run 6 3200000
4 run 7 3190000
4 run 8 3190000
4 run 9 3170000
4 run 10 3190000
4 run 11 3190000
4 run 12 3190000
4 run 13 3190000
4 run 14 3190000
4 run 15 3180000
4 run 16 3190000
4 run 17 3200000
4 run 18 3190000
4 run 19 3190000
4 run 20 3200000
4 run 21 3200000
4 run 22 3190000
4 run 23 3180000
4 run 24 3180000
4 run 25 3170000
4 run 26 3180000
4 run 27 3170000
4 run 28 3180000
4 run 29 3200000
4 run 30 3210000
# point pass=4 state=run jobs_per_s=6249873.9 updates_per_s=0.0 clock_hz=49998979
5 halt 1 3100000
5 halt 2 3100000
5 halt 3 3110000
5 halt 4 3110000
5 halt 5 3100000
5 halt 6 3110000
5 halt 7 3100000
5 halt 8 3100000
5 halt 9 3100000
5 halt 10 3110000
5 halt 11 3110000
5 halt 12 3110000
5 halt 13 3110000
5 halt 14 3100000
5 halt 15 3100000
5 halt 16 3100000
5 halt 17 3100000
5 halt 18 3120000
5 halt 19 3100000
5 halt 20 3090000
5 halt 21 3100000
5 halt 22 3090000
5 halt 23 3100000
5 halt 24 3100000
5 halt 25 3100000
5 halt 26 3110000
5 halt 27 3100000
5 halt 28 3100000
5 halt 29 3110000
5 halt 30 3100000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49998980
5 run 1 3180000
5 run 2 3180000
5 run 3 3200000
5 run 4 3190000
5 run 5 3180000
5 run 6 3190000
5 run 7 3190000
5 run 8 3180000
5 run 9 3190000
5 run 10 3190000
5 run 11 3170000
5 run 12 3190000
5 run 13 3180000
5 run 14 3190000
5 run 15 3180000
5 run 16 3180000
5 run 17 3220000
5 run 18 3190000
5 run 19 3180000
5 run 20 3190000
5 run 21 3180000
5 run 22 3180000
5 run 23 3180000
5 run 24 3180000
5 run 25 3180000
5 run 26 3180000
5 run 27 3200000
5 run 28 3180000
5 run 29 3190000
5 run 30 3190000
# point pass=5 state=run jobs_per_s=6249878.2 updates_per_s=0.0 clock_hz=49999014
6 halt 1 3120000
6 halt 2 3100000
6 halt 3 3100000
6 halt 4 3100000
6 halt 5 3110000
6 halt 6 3110000
6 halt 7 3100000
6 halt 8 3110000
6 halt 9 3100000
6 halt 10 3110000
6 halt 11 3100000
6 halt 12 3110000
6 halt 13 3100000
6 halt 14 3110000
6 halt 15 3110000
6 halt 16 3100000
6 halt 17 3100000
6 halt 18 3100000
6 halt 19 3110000
6 halt 20 3100000
6 halt 21 3110000
6 halt 22 3110000
6 halt 23 3120000
6 halt 24 3120000
6 halt 25 3110000
6 halt 26 3110000
6 halt 27 3100000
6 halt 28 3110000
6 halt 29 3100000
6 halt 30 3100000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=49999283
6 run 1 3190000
6 run 2 3180000
6 run 3 3190000
6 run 4 3190000
6 run 5 3170000
6 run 6 3190000
6 run 7 3200000
6 run 8 3180000
6 run 9 3200000
6 run 10 3190000
6 run 11 3350000
6 run 12 3190000
6 run 13 3190000
6 run 14 3190000
6 run 15 3180000
6 run 16 3550000
6 run 17 3190000
6 run 18 3190000
6 run 19 3190000
6 run 20 3180000
6 run 21 3180000
6 run 22 3210000
6 run 23 3320000
6 run 24 3190000
6 run 25 3180000
6 run 26 3180000
6 run 27 3190000
6 run 28 3190000
6 run 29 3190000
6 run 30 3210000
# point pass=6 state=run jobs_per_s=6249906.7 updates_per_s=0.0 clock_hz=49999241
# done SystemTime { tv_sec: 1793976903, tv_nsec: 28061803 }
# passes used 6 of 6 (dropped: [])
# run - halt = 80.0 +/- 2.6 mW (per-pass sd 6.3); 6/6 passes positive; 5 sample excursions beyond 6 MAD
# per-pass differences (mW): 80.0 80.0 70.0 90.0 80.0 80.0
# board power while running 3187 mW; jobs/s 6249880.1; updates/s 0.0; measured PL clock 49.999 MHz
# pre-registered prediction 80.0 mW: measured/predicted = 1.00, 0.0 sigma from it

View file

@ -0,0 +1,629 @@
# class t0-recall cores 1
# wai-quantum-fpga meter passes=10 settle=60s samples=30 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793970777, tv_nsec: 815511996 }
# pass state sample power_uW
1 halt 1 3140000
1 halt 2 3150000
1 halt 3 3140000
1 halt 4 3140000
1 halt 5 3140000
1 halt 6 3140000
1 halt 7 3140000
1 halt 8 3150000
1 halt 9 3140000
1 halt 10 3130000
1 halt 11 3140000
1 halt 12 3130000
1 halt 13 3140000
1 halt 14 3140000
1 halt 15 3130000
1 halt 16 3190000
1 halt 17 3150000
1 halt 18 3140000
1 halt 19 3150000
1 halt 20 3140000
1 halt 21 3140000
1 halt 22 3140000
1 halt 23 3130000
1 halt 24 3140000
1 halt 25 3150000
1 halt 26 3150000
1 halt 27 3130000
1 halt 28 3150000
1 halt 29 3140000
1 halt 30 3140000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997681
1 run 1 3290000
1 run 2 3300000
1 run 3 3300000
1 run 4 3300000
1 run 5 3320000
1 run 6 3290000
1 run 7 3300000
1 run 8 3300000
1 run 9 3300000
1 run 10 3290000
1 run 11 3290000
1 run 12 3300000
1 run 13 3290000
1 run 14 3290000
1 run 15 3320000
1 run 16 3300000
1 run 17 3300000
1 run 18 3290000
1 run 19 3290000
1 run 20 3290000
1 run 21 3280000
1 run 22 3290000
1 run 23 3300000
1 run 24 3300000
1 run 25 3290000
1 run 26 3290000
1 run 27 3300000
1 run 28 3290000
1 run 29 3290000
1 run 30 3290000
# point pass=1 state=run jobs_per_s=12499718.2 updates_per_s=0.0 clock_hz=99997723
2 halt 1 3130000
2 halt 2 3140000
2 halt 3 3130000
2 halt 4 3130000
2 halt 5 3150000
2 halt 6 3130000
2 halt 7 3120000
2 halt 8 3140000
2 halt 9 3140000
2 halt 10 3140000
2 halt 11 3140000
2 halt 12 3120000
2 halt 13 3150000
2 halt 14 3120000
2 halt 15 3140000
2 halt 16 3140000
2 halt 17 3130000
2 halt 18 3140000
2 halt 19 3120000
2 halt 20 3150000
2 halt 21 3130000
2 halt 22 3130000
2 halt 23 3120000
2 halt 24 3120000
2 halt 25 3140000
2 halt 26 3130000
2 halt 27 3120000
2 halt 28 3150000
2 halt 29 3150000
2 halt 30 3120000
# point pass=2 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997759
2 run 1 3290000
2 run 2 3310000
2 run 3 3300000
2 run 4 3290000
2 run 5 3300000
2 run 6 3290000
2 run 7 3300000
2 run 8 3310000
2 run 9 3290000
2 run 10 3290000
2 run 11 3300000
2 run 12 3290000
2 run 13 3300000
2 run 14 3300000
2 run 15 3300000
2 run 16 3320000
2 run 17 3300000
2 run 18 3290000
2 run 19 3300000
2 run 20 3290000
2 run 21 3290000
2 run 22 3300000
2 run 23 3310000
2 run 24 3300000
2 run 25 3330000
2 run 26 3310000
2 run 27 3300000
2 run 28 3300000
2 run 29 3310000
2 run 30 3300000
# point pass=2 state=run jobs_per_s=12499726.4 updates_per_s=0.0 clock_hz=99997789
3 run 1 3290000
3 run 2 3290000
3 run 3 3290000
3 run 4 3300000
3 run 5 3310000
3 run 6 3290000
3 run 7 3290000
3 run 8 3300000
3 run 9 3290000
3 run 10 3300000
3 run 11 3290000
3 run 12 3300000
3 run 13 3310000
3 run 14 3300000
3 run 15 3300000
3 run 16 3290000
3 run 17 3290000
3 run 18 3300000
3 run 19 3290000
3 run 20 3290000
3 run 21 3290000
3 run 22 3310000
3 run 23 3300000
3 run 24 3290000
3 run 25 3290000
3 run 26 3300000
3 run 27 3300000
3 run 28 3290000
3 run 29 3290000
3 run 30 3300000
# point pass=3 state=run jobs_per_s=12499729.5 updates_per_s=0.0 clock_hz=99997814
3 halt 1 3140000
3 halt 2 3150000
3 halt 3 3140000
3 halt 4 3140000
3 halt 5 3140000
3 halt 6 3130000
3 halt 7 3140000
3 halt 8 3130000
3 halt 9 3140000
3 halt 10 3160000
3 halt 11 3150000
3 halt 12 3140000
3 halt 13 3140000
3 halt 14 3130000
3 halt 15 3140000
3 halt 16 3150000
3 halt 17 3140000
3 halt 18 3130000
3 halt 19 3140000
3 halt 20 3140000
3 halt 21 3130000
3 halt 22 3130000
3 halt 23 3140000
3 halt 24 3140000
3 halt 25 3150000
3 halt 26 3140000
3 halt 27 3130000
3 halt 28 3130000
3 halt 29 3120000
3 halt 30 3130000
# point pass=3 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997837
4 halt 1 3120000
4 halt 2 3130000
4 halt 3 3130000
4 halt 4 3130000
4 halt 5 3130000
4 halt 6 3130000
4 halt 7 3120000
4 halt 8 3120000
4 halt 9 3120000
4 halt 10 3120000
4 halt 11 3120000
4 halt 12 3120000
4 halt 13 3130000
4 halt 14 3120000
4 halt 15 3120000
4 halt 16 3120000
4 halt 17 3120000
4 halt 18 3120000
4 halt 19 3120000
4 halt 20 3140000
4 halt 21 3140000
4 halt 22 3130000
4 halt 23 3120000
4 halt 24 3120000
4 halt 25 3130000
4 halt 26 3130000
4 halt 27 3120000
4 halt 28 3120000
4 halt 29 3140000
4 halt 30 3120000
# point pass=4 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997854
4 run 1 3330000
4 run 2 3290000
4 run 3 3290000
4 run 4 3290000
4 run 5 3290000
4 run 6 3300000
4 run 7 3330000
4 run 8 3300000
4 run 9 3310000
4 run 10 3290000
4 run 11 3290000
4 run 12 3300000
4 run 13 3300000
4 run 14 3300000
4 run 15 3290000
4 run 16 3290000
4 run 17 3320000
4 run 18 3310000
4 run 19 3300000
4 run 20 3290000
4 run 21 3300000
4 run 22 3300000
4 run 23 3300000
4 run 24 3300000
4 run 25 3290000
4 run 26 3310000
4 run 27 3290000
4 run 28 3300000
4 run 29 3290000
4 run 30 3290000
# point pass=4 state=run jobs_per_s=12499736.5 updates_per_s=0.0 clock_hz=99997868
5 halt 1 3120000
5 halt 2 3140000
5 halt 3 3130000
5 halt 4 3130000
5 halt 5 3130000
5 halt 6 3150000
5 halt 7 3130000
5 halt 8 3130000
5 halt 9 3130000
5 halt 10 3120000
5 halt 11 3130000
5 halt 12 3140000
5 halt 13 3130000
5 halt 14 3150000
5 halt 15 3150000
5 halt 16 3130000
5 halt 17 3120000
5 halt 18 3120000
5 halt 19 3120000
5 halt 20 3140000
5 halt 21 3140000
5 halt 22 3130000
5 halt 23 3130000
5 halt 24 3130000
5 halt 25 3120000
5 halt 26 3120000
5 halt 27 3140000
5 halt 28 3130000
5 halt 29 3130000
5 halt 30 3120000
# point pass=5 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997880
5 run 1 3290000
5 run 2 3320000
5 run 3 3300000
5 run 4 3280000
5 run 5 3290000
5 run 6 3290000
5 run 7 3290000
5 run 8 3280000
5 run 9 3280000
5 run 10 3290000
5 run 11 3300000
5 run 12 3290000
5 run 13 3290000
5 run 14 3280000
5 run 15 3300000
5 run 16 3290000
5 run 17 3290000
5 run 18 3290000
5 run 19 3290000
5 run 20 3290000
5 run 21 3290000
5 run 22 3280000
5 run 23 3300000
5 run 24 3290000
5 run 25 3290000
5 run 26 3300000
5 run 27 3290000
5 run 28 3280000
5 run 29 3280000
5 run 30 3280000
# point pass=5 state=run jobs_per_s=12499739.0 updates_per_s=0.0 clock_hz=99997890
6 run 1 3300000
6 run 2 3310000
6 run 3 3290000
6 run 4 3300000
6 run 5 3300000
6 run 6 3300000
6 run 7 3310000
6 run 8 3320000
6 run 9 3290000
6 run 10 3290000
6 run 11 3290000
6 run 12 3300000
6 run 13 3290000
6 run 14 3290000
6 run 15 3310000
6 run 16 3300000
6 run 17 3310000
6 run 18 3300000
6 run 19 3320000
6 run 20 3290000
6 run 21 3290000
6 run 22 3320000
6 run 23 3310000
6 run 24 3290000
6 run 25 3300000
6 run 26 3290000
6 run 27 3710000
6 run 28 3300000
6 run 29 3300000
6 run 30 3300000
# point pass=6 state=run jobs_per_s=12499740.2 updates_per_s=0.0 clock_hz=99997899
6 halt 1 3140000
6 halt 2 3150000
6 halt 3 3140000
6 halt 4 3140000
6 halt 5 3130000
6 halt 6 3140000
6 halt 7 3140000
6 halt 8 3130000
6 halt 9 3130000
6 halt 10 3130000
6 halt 11 3150000
6 halt 12 3130000
6 halt 13 3140000
6 halt 14 3140000
6 halt 15 3140000
6 halt 16 3120000
6 halt 17 3130000
6 halt 18 3130000
6 halt 19 3150000
6 halt 20 3150000
6 halt 21 3130000
6 halt 22 3140000
6 halt 23 3140000
6 halt 24 3130000
6 halt 25 3150000
6 halt 26 3140000
6 halt 27 3130000
6 halt 28 3150000
6 halt 29 3130000
6 halt 30 3160000
# point pass=6 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997906
7 halt 1 3140000
7 halt 2 3130000
7 halt 3 3140000
7 halt 4 3150000
7 halt 5 3140000
7 halt 6 3130000
7 halt 7 3140000
7 halt 8 3140000
7 halt 9 3140000
7 halt 10 3140000
7 halt 11 3120000
7 halt 12 3120000
7 halt 13 3140000
7 halt 14 3120000
7 halt 15 3120000
7 halt 16 3140000
7 halt 17 3150000
7 halt 18 3120000
7 halt 19 3120000
7 halt 20 3130000
7 halt 21 3120000
7 halt 22 3120000
7 halt 23 3130000
7 halt 24 3130000
7 halt 25 3140000
7 halt 26 3130000
7 halt 27 3120000
7 halt 28 3240000
7 halt 29 3130000
7 halt 30 3140000
# point pass=7 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997913
7 run 1 3300000
7 run 2 3300000
7 run 3 3300000
7 run 4 3300000
7 run 5 3310000
7 run 6 3290000
7 run 7 3300000
7 run 8 3320000
7 run 9 3300000
7 run 10 3290000
7 run 11 3300000
7 run 12 3300000
7 run 13 3300000
7 run 14 3310000
7 run 15 3300000
7 run 16 3320000
7 run 17 3310000
7 run 18 3290000
7 run 19 3310000
7 run 20 3300000
7 run 21 3290000
7 run 22 3300000
7 run 23 3300000
7 run 24 3300000
7 run 25 3300000
7 run 26 3300000
7 run 27 3300000
7 run 28 3300000
7 run 29 3300000
7 run 30 3310000
# point pass=7 state=run jobs_per_s=12499742.5 updates_per_s=0.0 clock_hz=99997918
8 run 1 3320000
8 run 2 3310000
8 run 3 3640000
8 run 4 3310000
8 run 5 3330000
8 run 6 3300000
8 run 7 3300000
8 run 8 3300000
8 run 9 3310000
8 run 10 3310000
8 run 11 3300000
8 run 12 3310000
8 run 13 3320000
8 run 14 3310000
8 run 15 3310000
8 run 16 3290000
8 run 17 3300000
8 run 18 3300000
8 run 19 3310000
8 run 20 3290000
8 run 21 3290000
8 run 22 3300000
8 run 23 3290000
8 run 24 3290000
8 run 25 3290000
8 run 26 3290000
8 run 27 3300000
8 run 28 3310000
8 run 29 3300000
8 run 30 3300000
# point pass=8 state=run jobs_per_s=12499743.0 updates_per_s=0.0 clock_hz=99997922
8 halt 1 3150000
8 halt 2 3150000
8 halt 3 3130000
8 halt 4 3130000
8 halt 5 3140000
8 halt 6 3140000
8 halt 7 3140000
8 halt 8 3130000
8 halt 9 3130000
8 halt 10 3140000
8 halt 11 3140000
8 halt 12 3120000
8 halt 13 3130000
8 halt 14 3120000
8 halt 15 3140000
8 halt 16 3120000
8 halt 17 3120000
8 halt 18 3120000
8 halt 19 3120000
8 halt 20 3140000
8 halt 21 3130000
8 halt 22 3140000
8 halt 23 3130000
8 halt 24 3130000
8 halt 25 3150000
8 halt 26 3130000
8 halt 27 3130000
8 halt 28 3120000
8 halt 29 3120000
8 halt 30 3130000
# point pass=8 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997926
9 halt 1 3130000
9 halt 2 3140000
9 halt 3 3140000
9 halt 4 3140000
9 halt 5 3130000
9 halt 6 3140000
9 halt 7 3140000
9 halt 8 3130000
9 halt 9 3140000
9 halt 10 3140000
9 halt 11 3140000
9 halt 12 3140000
9 halt 13 3140000
9 halt 14 3140000
9 halt 15 3120000
9 halt 16 3140000
9 halt 17 3130000
9 halt 18 3140000
9 halt 19 3150000
9 halt 20 3130000
9 halt 21 3130000
9 halt 22 3140000
9 halt 23 3120000
9 halt 24 3140000
9 halt 25 3130000
9 halt 26 3130000
9 halt 27 3130000
9 halt 28 3130000
9 halt 29 3140000
9 halt 30 3150000
# point pass=9 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997929
9 run 1 3290000
9 run 2 3290000
9 run 3 3310000
9 run 4 3300000
9 run 5 3290000
9 run 6 3290000
9 run 7 3290000
9 run 8 3290000
9 run 9 3290000
9 run 10 3310000
9 run 11 3300000
9 run 12 3290000
9 run 13 3290000
9 run 14 3300000
9 run 15 3290000
9 run 16 3290000
9 run 17 3300000
9 run 18 3310000
9 run 19 3320000
9 run 20 3290000
9 run 21 3290000
9 run 22 3290000
9 run 23 3290000
9 run 24 3290000
9 run 25 3290000
9 run 26 3290000
9 run 27 3320000
9 run 28 3290000
9 run 29 3300000
9 run 30 3290000
# point pass=9 state=run jobs_per_s=12499744.1 updates_per_s=0.0 clock_hz=99997931
10 run 1 3300000
10 run 2 3320000
10 run 3 3290000
10 run 4 3300000
10 run 5 3300000
10 run 6 3310000
10 run 7 3300000
10 run 8 3290000
10 run 9 3300000
10 run 10 3290000
10 run 11 3300000
10 run 12 3290000
10 run 13 3290000
10 run 14 3300000
10 run 15 3290000
10 run 16 3300000
10 run 17 3300000
10 run 18 3300000
10 run 19 3320000
10 run 20 3300000
10 run 21 3300000
10 run 22 3300000
10 run 23 3300000
10 run 24 3310000
10 run 25 3310000
10 run 26 3320000
10 run 27 3300000
10 run 28 3300000
10 run 29 3310000
10 run 30 3300000
# point pass=10 state=run jobs_per_s=12499744.5 updates_per_s=0.0 clock_hz=99997934
10 halt 1 3140000
10 halt 2 3140000
10 halt 3 3140000
10 halt 4 3130000
10 halt 5 3140000
10 halt 6 3150000
10 halt 7 3150000
10 halt 8 3140000
10 halt 9 3140000
10 halt 10 3140000
10 halt 11 3140000
10 halt 12 3140000
10 halt 13 3140000
10 halt 14 3140000
10 halt 15 3150000
10 halt 16 3140000
10 halt 17 3140000
10 halt 18 3140000
10 halt 19 3140000
10 halt 20 3140000
10 halt 21 3140000
10 halt 22 3140000
10 halt 23 3140000
10 halt 24 3140000
10 halt 25 3140000
10 halt 26 3150000
10 halt 27 3130000
10 halt 28 3140000
10 halt 29 3150000
10 halt 30 3160000
# point pass=10 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997935
# done SystemTime { tv_sec: 1793972578, tv_nsec: 968782358 }
# passes used 10 of 10 (dropped: [])
# run - halt = 162.0 +/- 3.3 mW (per-pass sd 10.3); 10/10 passes positive; 4 sample excursions beyond 6 MAD
# per-pass differences (mW): 150.0 170.0 150.0 180.0 160.0 160.0 170.0 170.0 150.0 160.0
# board power while running 3296 mW; jobs/s 12499736.4; updates/s 0.0; measured PL clock 99.998 MHz
# pre-registered prediction 160.0 mW: measured/predicted = 1.01, 0.6 sigma from it

View file

@ -0,0 +1,6 @@
2026-11-06T08:01:41Z start; engine answers: ID and version 0x00404002; measured clock 99.997 MHz
2026-11-06T08:11:42Z step done
2026-11-06T08:35:54Z known answer done exit=0
wai-quantum-fpga: another session is active on the board, not loading:
1670130 sudo KNOWN_ANSWER=/home/ubuntu/kv_runhalt.sh KNOWN_BITSTREAM=ev_rdet.bit.bin setsid nohup sh power_campaign.sh campaign-v2
2026-11-06T08:35:55Z reload of the engine failed

View file

@ -0,0 +1,483 @@
# kv_runhalt bitstream=ev_rdet.bit.bin gpio=594 settle=60s passes=8 samples=30
# pass vehicle sample power_uW
1 halt 1 3070000
1 halt 2 3150000
1 halt 3 3080000
1 halt 4 3170000
1 halt 5 3080000
1 halt 6 3170000
1 halt 7 3080000
1 halt 8 3180000
1 halt 9 3100000
1 halt 10 3170000
1 halt 11 3090000
1 halt 12 3170000
1 halt 13 3210000
1 halt 14 3080000
1 halt 15 3220000
1 halt 16 3080000
1 halt 17 3240000
1 halt 18 3080000
1 halt 19 3210000
1 halt 20 3080000
1 halt 21 3240000
1 halt 22 3080000
1 halt 23 3100000
1 halt 24 3090000
1 halt 25 3260000
1 halt 26 3080000
1 halt 27 3250000
1 halt 28 3100000
1 halt 29 3100000
1 halt 30 3070000
1 run 1 3170000
1 run 2 3230000
1 run 3 3150000
1 run 4 3230000
1 run 5 3150000
1 run 6 3220000
1 run 7 3150000
1 run 8 3210000
1 run 9 3150000
1 run 10 3220000
1 run 11 3150000
1 run 12 3240000
1 run 13 3150000
1 run 14 3250000
1 run 15 3150000
1 run 16 3240000
1 run 17 3150000
1 run 18 3250000
1 run 19 3150000
1 run 20 3250000
1 run 21 3150000
1 run 22 3250000
1 run 23 3150000
1 run 24 3230000
1 run 25 3150000
1 run 26 3230000
1 run 27 3150000
1 run 28 3250000
1 run 29 3150000
1 run 30 3270000
2 run 1 3170000
2 run 2 3240000
2 run 3 3160000
2 run 4 3250000
2 run 5 3160000
2 run 6 3240000
2 run 7 3170000
2 run 8 3250000
2 run 9 3160000
2 run 10 3250000
2 run 11 3150000
2 run 12 3250000
2 run 13 3160000
2 run 14 3250000
2 run 15 3160000
2 run 16 3240000
2 run 17 3160000
2 run 18 3250000
2 run 19 3160000
2 run 20 3280000
2 run 21 3180000
2 run 22 3250000
2 run 23 3160000
2 run 24 3260000
2 run 25 3160000
2 run 26 3230000
2 run 27 3160000
2 run 28 3240000
2 run 29 3150000
2 run 30 3260000
2 halt 1 3150000
2 halt 2 3080000
2 halt 3 3140000
2 halt 4 3080000
2 halt 5 3150000
2 halt 6 3070000
2 halt 7 3120000
2 halt 8 3090000
2 halt 9 3140000
2 halt 10 3100000
2 halt 11 3110000
2 halt 12 3080000
2 halt 13 3120000
2 halt 14 3070000
2 halt 15 3130000
2 halt 16 3080000
2 halt 17 3120000
2 halt 18 3080000
2 halt 19 3140000
2 halt 20 3080000
2 halt 21 3150000
2 halt 22 3090000
2 halt 23 3170000
2 halt 24 3080000
2 halt 25 3170000
2 halt 26 3080000
2 halt 27 3180000
2 halt 28 3100000
2 halt 29 3210000
2 halt 30 3090000
3 run 1 3150000
3 run 2 3160000
3 run 3 3150000
3 run 4 3150000
3 run 5 3150000
3 run 6 3150000
3 run 7 3160000
3 run 8 3150000
3 run 9 3150000
3 run 10 3150000
3 run 11 3150000
3 run 12 3160000
3 run 13 3150000
3 run 14 3150000
3 run 15 3160000
3 run 16 3150000
3 run 17 3150000
3 run 18 3160000
3 run 19 3150000
3 run 20 3150000
3 run 21 3150000
3 run 22 3150000
3 run 23 3170000
3 run 24 3150000
3 run 25 3160000
3 run 26 3150000
3 run 27 3150000
3 run 28 3140000
3 run 29 3180000
3 run 30 3150000
3 halt 1 3170000
3 halt 2 3080000
3 halt 3 3170000
3 halt 4 3110000
3 halt 5 3180000
3 halt 6 3080000
3 halt 7 3180000
3 halt 8 3090000
3 halt 9 3180000
3 halt 10 3100000
3 halt 11 3160000
3 halt 12 3080000
3 halt 13 3180000
3 halt 14 3080000
3 halt 15 3180000
3 halt 16 3090000
3 halt 17 3190000
3 halt 18 3100000
3 halt 19 3170000
3 halt 20 3080000
3 halt 21 3190000
3 halt 22 3090000
3 halt 23 3190000
3 halt 24 3080000
3 halt 25 3190000
3 halt 26 3100000
3 halt 27 3190000
3 halt 28 3080000
3 halt 29 3180000
3 halt 30 3080000
4 halt 1 3100000
4 halt 2 3070000
4 halt 3 3080000
4 halt 4 3070000
4 halt 5 3070000
4 halt 6 3070000
4 halt 7 3090000
4 halt 8 3080000
4 halt 9 3090000
4 halt 10 3070000
4 halt 11 3100000
4 halt 12 3070000
4 halt 13 3100000
4 halt 14 3080000
4 halt 15 3130000
4 halt 16 3070000
4 halt 17 3120000
4 halt 18 3090000
4 halt 19 3130000
4 halt 20 3100000
4 halt 21 3160000
4 halt 22 3080000
4 halt 23 3160000
4 halt 24 3070000
4 halt 25 3160000
4 halt 26 3070000
4 halt 27 3160000
4 halt 28 3090000
4 halt 29 3170000
4 halt 30 3070000
4 run 1 3150000
4 run 2 3150000
4 run 3 3160000
4 run 4 3160000
4 run 5 3150000
4 run 6 3160000
4 run 7 3150000
4 run 8 3150000
4 run 9 3150000
4 run 10 3150000
4 run 11 3150000
4 run 12 3150000
4 run 13 3150000
4 run 14 3150000
4 run 15 3150000
4 run 16 3170000
4 run 17 3160000
4 run 18 3170000
4 run 19 3160000
4 run 20 3160000
4 run 21 3140000
4 run 22 3180000
4 run 23 3150000
4 run 24 3200000
4 run 25 3150000
4 run 26 3180000
4 run 27 3150000
4 run 28 3200000
4 run 29 3140000
4 run 30 3180000
5 run 1 3240000
5 run 2 3160000
5 run 3 3240000
5 run 4 3150000
5 run 5 3230000
5 run 6 3160000
5 run 7 3260000
5 run 8 3140000
5 run 9 3220000
5 run 10 3130000
5 run 11 3240000
5 run 12 3150000
5 run 13 3230000
5 run 14 3150000
5 run 15 3250000
5 run 16 3140000
5 run 17 3230000
5 run 18 3140000
5 run 19 3220000
5 run 20 3150000
5 run 21 3220000
5 run 22 3140000
5 run 23 3140000
5 run 24 3230000
5 run 25 3160000
5 run 26 3240000
5 run 27 3140000
5 run 28 3230000
5 run 29 3140000
5 run 30 3250000
5 halt 1 3070000
5 halt 2 3240000
5 halt 3 3080000
5 halt 4 3070000
5 halt 5 3070000
5 halt 6 3090000
5 halt 7 3070000
5 halt 8 3170000
5 halt 9 3070000
5 halt 10 3170000
5 halt 11 3070000
5 halt 12 3160000
5 halt 13 3070000
5 halt 14 3170000
5 halt 15 3070000
5 halt 16 3170000
5 halt 17 3070000
5 halt 18 3160000
5 halt 19 3100000
5 halt 20 3180000
5 halt 21 3080000
5 halt 22 3190000
5 halt 23 3070000
5 halt 24 3170000
5 halt 25 3080000
5 halt 26 3160000
5 halt 27 3070000
5 halt 28 3160000
5 halt 29 3070000
5 halt 30 3180000
6 halt 1 3250000
6 halt 2 3080000
6 halt 3 3240000
6 halt 4 3080000
6 halt 5 3240000
6 halt 6 3090000
6 halt 7 3250000
6 halt 8 3080000
6 halt 9 3250000
6 halt 10 3120000
6 halt 11 3250000
6 halt 12 3100000
6 halt 13 3270000
6 halt 14 3080000
6 halt 15 3080000
6 halt 16 3090000
6 halt 17 3080000
6 halt 18 3090000
6 halt 19 3090000
6 halt 20 3080000
6 halt 21 3090000
6 halt 22 3090000
6 halt 23 3080000
6 halt 24 3090000
6 halt 25 3080000
6 halt 26 3090000
6 halt 27 3080000
6 halt 28 3080000
6 halt 29 3080000
6 halt 30 3090000
6 run 1 3160000
6 run 2 3240000
6 run 3 3150000
6 run 4 3240000
6 run 5 3140000
6 run 6 3240000
6 run 7 3250000
6 run 8 3150000
6 run 9 3250000
6 run 10 3140000
6 run 11 3250000
6 run 12 3150000
6 run 13 3150000
6 run 14 3150000
6 run 15 3160000
6 run 16 3150000
6 run 17 3150000
6 run 18 3150000
6 run 19 3150000
6 run 20 3150000
6 run 21 3170000
6 run 22 3160000
6 run 23 3160000
6 run 24 3150000
6 run 25 3140000
6 run 26 3160000
6 run 27 3140000
6 run 28 3150000
6 run 29 3150000
6 run 30 3150000
7 halt 1 3080000
7 halt 2 3160000
7 halt 3 3080000
7 halt 4 3160000
7 halt 5 3080000
7 halt 6 3190000
7 halt 7 3070000
7 halt 8 3160000
7 halt 9 3100000
7 halt 10 3190000
7 halt 11 3080000
7 halt 12 3190000
7 halt 13 3080000
7 halt 14 3190000
7 halt 15 3080000
7 halt 16 3220000
7 halt 17 3080000
7 halt 18 3210000
7 halt 19 3080000
7 halt 20 3230000
7 halt 21 3090000
7 halt 22 3210000
7 halt 23 3090000
7 halt 24 3220000
7 halt 25 3080000
7 halt 26 3230000
7 halt 27 3080000
7 halt 28 3250000
7 halt 29 3090000
7 halt 30 3110000
7 run 1 3190000
7 run 2 3140000
7 run 3 3170000
7 run 4 3150000
7 run 5 3190000
7 run 6 3150000
7 run 7 3180000
7 run 8 3150000
7 run 9 3180000
7 run 10 3150000
7 run 11 3190000
7 run 12 3410000
7 run 13 3190000
7 run 14 3140000
7 run 15 3190000
7 run 16 3150000
7 run 17 3220000
7 run 18 3150000
7 run 19 3230000
7 run 20 3150000
7 run 21 3210000
7 run 22 3140000
7 run 23 3220000
7 run 24 3240000
7 run 25 3150000
7 run 26 3230000
7 run 27 3140000
7 run 28 3260000
7 run 29 3150000
7 run 30 3250000
8 halt 1 3090000
8 halt 2 3080000
8 halt 3 3080000
8 halt 4 3080000
8 halt 5 3090000
8 halt 6 3080000
8 halt 7 3130000
8 halt 8 3090000
8 halt 9 3130000
8 halt 10 3070000
8 halt 11 3120000
8 halt 12 3070000
8 halt 13 3110000
8 halt 14 3080000
8 halt 15 3100000
8 halt 16 3080000
8 halt 17 3110000
8 halt 18 3080000
8 halt 19 3120000
8 halt 20 3080000
8 halt 21 3140000
8 halt 22 3070000
8 halt 23 3120000
8 halt 24 3080000
8 halt 25 3120000
8 halt 26 3070000
8 halt 27 3140000
8 halt 28 3080000
8 halt 29 3140000
8 halt 30 3080000
8 run 1 3290000
8 run 2 3150000
8 run 3 3270000
8 run 4 3150000
8 run 5 3260000
8 run 6 3150000
8 run 7 3290000
8 run 8 3160000
8 run 9 3270000
8 run 10 3170000
8 run 11 3280000
8 run 12 3150000
8 run 13 3300000
8 run 14 3150000
8 run 15 3290000
8 run 16 3170000
8 run 17 3320000
8 run 18 3160000
8 run 19 3270000
8 run 20 3170000
8 run 21 3270000
8 run 22 3170000
8 run 23 3280000
8 run 24 3160000
8 run 25 3280000
8 run 26 3160000
8 run 27 3410000
8 run 28 3150000
8 run 29 3170000
8 run 30 3160000
# done

View file

@ -0,0 +1,610 @@
# class t0-recall cores 1
# wai-quantum-fpga meter passes=1 settle=0s samples=300 null=false sensor=/sys/class/hwmon/hwmon2/power1_input started=SystemTime { tv_sec: 1793952101, tv_nsec: 820210424 }
# pass state sample power_uW
1 halt 1 3370000
1 halt 2 3140000
1 halt 3 3120000
1 halt 4 3130000
1 halt 5 3120000
1 halt 6 3120000
1 halt 7 3110000
1 halt 8 3120000
1 halt 9 3130000
1 halt 10 3130000
1 halt 11 3120000
1 halt 12 3140000
1 halt 13 3110000
1 halt 14 3120000
1 halt 15 3120000
1 halt 16 3120000
1 halt 17 3130000
1 halt 18 3130000
1 halt 19 3130000
1 halt 20 3110000
1 halt 21 3120000
1 halt 22 3140000
1 halt 23 3130000
1 halt 24 3120000
1 halt 25 3130000
1 halt 26 3140000
1 halt 27 3130000
1 halt 28 3110000
1 halt 29 3120000
1 halt 30 3120000
1 halt 31 3120000
1 halt 32 3120000
1 halt 33 3130000
1 halt 34 3130000
1 halt 35 3150000
1 halt 36 3120000
1 halt 37 3120000
1 halt 38 3130000
1 halt 39 3120000
1 halt 40 3120000
1 halt 41 3120000
1 halt 42 3110000
1 halt 43 3120000
1 halt 44 3130000
1 halt 45 3120000
1 halt 46 3120000
1 halt 47 3120000
1 halt 48 3120000
1 halt 49 3120000
1 halt 50 3140000
1 halt 51 3130000
1 halt 52 3120000
1 halt 53 3120000
1 halt 54 3130000
1 halt 55 3120000
1 halt 56 3130000
1 halt 57 3120000
1 halt 58 3140000
1 halt 59 3140000
1 halt 60 3120000
1 halt 61 3130000
1 halt 62 3130000
1 halt 63 3120000
1 halt 64 3120000
1 halt 65 3120000
1 halt 66 3120000
1 halt 67 3150000
1 halt 68 3120000
1 halt 69 3120000
1 halt 70 3130000
1 halt 71 3120000
1 halt 72 3130000
1 halt 73 3130000
1 halt 74 3120000
1 halt 75 3110000
1 halt 76 3120000
1 halt 77 3130000
1 halt 78 3120000
1 halt 79 3110000
1 halt 80 3120000
1 halt 81 3120000
1 halt 82 3140000
1 halt 83 3120000
1 halt 84 3120000
1 halt 85 3130000
1 halt 86 3130000
1 halt 87 3120000
1 halt 88 3120000
1 halt 89 3120000
1 halt 90 3140000
1 halt 91 3150000
1 halt 92 3130000
1 halt 93 3120000
1 halt 94 3130000
1 halt 95 3120000
1 halt 96 3130000
1 halt 97 3120000
1 halt 98 3120000
1 halt 99 3140000
1 halt 100 3120000
1 halt 101 3120000
1 halt 102 3120000
1 halt 103 3120000
1 halt 104 3140000
1 halt 105 3120000
1 halt 106 3140000
1 halt 107 3130000
1 halt 108 3130000
1 halt 109 3140000
1 halt 110 3130000
1 halt 111 3140000
1 halt 112 3140000
1 halt 113 3150000
1 halt 114 3150000
1 halt 115 3120000
1 halt 116 3130000
1 halt 117 3130000
1 halt 118 3140000
1 halt 119 3120000
1 halt 120 3140000
1 halt 121 3130000
1 halt 122 3150000
1 halt 123 3140000
1 halt 124 3130000
1 halt 125 3140000
1 halt 126 3130000
1 halt 127 3130000
1 halt 128 3140000
1 halt 129 3130000
1 halt 130 3140000
1 halt 131 3140000
1 halt 132 3140000
1 halt 133 3140000
1 halt 134 3130000
1 halt 135 3140000
1 halt 136 3140000
1 halt 137 3150000
1 halt 138 3140000
1 halt 139 3130000
1 halt 140 3130000
1 halt 141 3150000
1 halt 142 3130000
1 halt 143 3140000
1 halt 144 3130000
1 halt 145 3150000
1 halt 146 3150000
1 halt 147 3130000
1 halt 148 3130000
1 halt 149 3160000
1 halt 150 3130000
1 halt 151 3120000
1 halt 152 3130000
1 halt 153 3140000
1 halt 154 3150000
1 halt 155 3130000
1 halt 156 3140000
1 halt 157 3130000
1 halt 158 3130000
1 halt 159 3140000
1 halt 160 3140000
1 halt 161 3140000
1 halt 162 3140000
1 halt 163 3130000
1 halt 164 3140000
1 halt 165 3150000
1 halt 166 3140000
1 halt 167 3130000
1 halt 168 3140000
1 halt 169 3150000
1 halt 170 3140000
1 halt 171 3140000
1 halt 172 3130000
1 halt 173 3150000
1 halt 174 3140000
1 halt 175 3130000
1 halt 176 3130000
1 halt 177 3150000
1 halt 178 3140000
1 halt 179 3140000
1 halt 180 3120000
1 halt 181 3140000
1 halt 182 3140000
1 halt 183 3140000
1 halt 184 3140000
1 halt 185 3140000
1 halt 186 3140000
1 halt 187 3120000
1 halt 188 3130000
1 halt 189 3140000
1 halt 190 3130000
1 halt 191 3150000
1 halt 192 3150000
1 halt 193 3140000
1 halt 194 3140000
1 halt 195 3140000
1 halt 196 3140000
1 halt 197 3140000
1 halt 198 3120000
1 halt 199 3140000
1 halt 200 3150000
1 halt 201 3150000
1 halt 202 3140000
1 halt 203 3150000
1 halt 204 3140000
1 halt 205 3140000
1 halt 206 3140000
1 halt 207 3140000
1 halt 208 3140000
1 halt 209 3150000
1 halt 210 3130000
1 halt 211 3140000
1 halt 212 3130000
1 halt 213 3140000
1 halt 214 3140000
1 halt 215 3140000
1 halt 216 3130000
1 halt 217 3130000
1 halt 218 3120000
1 halt 219 3140000
1 halt 220 3130000
1 halt 221 3140000
1 halt 222 3140000
1 halt 223 3130000
1 halt 224 3140000
1 halt 225 3140000
1 halt 226 3130000
1 halt 227 3130000
1 halt 228 3150000
1 halt 229 3130000
1 halt 230 3130000
1 halt 231 3130000
1 halt 232 3150000
1 halt 233 3140000
1 halt 234 3120000
1 halt 235 3130000
1 halt 236 3130000
1 halt 237 3120000
1 halt 238 3140000
1 halt 239 3120000
1 halt 240 3130000
1 halt 241 3130000
1 halt 242 3120000
1 halt 243 3260000
1 halt 244 3120000
1 halt 245 3130000
1 halt 246 3130000
1 halt 247 3120000
1 halt 248 3500000
1 halt 249 3130000
1 halt 250 3130000
1 halt 251 3120000
1 halt 252 3120000
1 halt 253 3120000
1 halt 254 3140000
1 halt 255 3140000
1 halt 256 3140000
1 halt 257 3120000
1 halt 258 3120000
1 halt 259 3120000
1 halt 260 3120000
1 halt 261 3110000
1 halt 262 3120000
1 halt 263 3120000
1 halt 264 3140000
1 halt 265 3120000
1 halt 266 3120000
1 halt 267 3130000
1 halt 268 3130000
1 halt 269 3130000
1 halt 270 3120000
1 halt 271 3120000
1 halt 272 3120000
1 halt 273 3120000
1 halt 274 3120000
1 halt 275 3120000
1 halt 276 3120000
1 halt 277 3120000
1 halt 278 3140000
1 halt 279 3150000
1 halt 280 3120000
1 halt 281 3120000
1 halt 282 3120000
1 halt 283 3130000
1 halt 284 3130000
1 halt 285 3120000
1 halt 286 3130000
1 halt 287 3140000
1 halt 288 3140000
1 halt 289 3120000
1 halt 290 3130000
1 halt 291 3120000
1 halt 292 3120000
1 halt 293 3120000
1 halt 294 3120000
1 halt 295 3120000
1 halt 296 3140000
1 halt 297 3120000
1 halt 298 3120000
1 halt 299 3130000
1 halt 300 3120000
# point pass=1 state=halt jobs_per_s=0.0 updates_per_s=0.0 clock_hz=99997998
1 run 1 3520000
1 run 2 3310000
1 run 3 3300000
1 run 4 3280000
1 run 5 3290000
1 run 6 3290000
1 run 7 3290000
1 run 8 3290000
1 run 9 3290000
1 run 10 3300000
1 run 11 3290000
1 run 12 3290000
1 run 13 3290000
1 run 14 3290000
1 run 15 3290000
1 run 16 3280000
1 run 17 3290000
1 run 18 3290000
1 run 19 3290000
1 run 20 3280000
1 run 21 3300000
1 run 22 3290000
1 run 23 3280000
1 run 24 3290000
1 run 25 3310000
1 run 26 3290000
1 run 27 3290000
1 run 28 3290000
1 run 29 3290000
1 run 30 3290000
1 run 31 3290000
1 run 32 3290000
1 run 33 3300000
1 run 34 3300000
1 run 35 3290000
1 run 36 3290000
1 run 37 3300000
1 run 38 3290000
1 run 39 3290000
1 run 40 3280000
1 run 41 3280000
1 run 42 3310000
1 run 43 3290000
1 run 44 3280000
1 run 45 3300000
1 run 46 3290000
1 run 47 3290000
1 run 48 3290000
1 run 49 3290000
1 run 50 3290000
1 run 51 3290000
1 run 52 3290000
1 run 53 3290000
1 run 54 3290000
1 run 55 3290000
1 run 56 3290000
1 run 57 3300000
1 run 58 3290000
1 run 59 3290000
1 run 60 3290000
1 run 61 3300000
1 run 62 3290000
1 run 63 3290000
1 run 64 3290000
1 run 65 3320000
1 run 66 3300000
1 run 67 3290000
1 run 68 3290000
1 run 69 3290000
1 run 70 3290000
1 run 71 3280000
1 run 72 3290000
1 run 73 3290000
1 run 74 3310000
1 run 75 3300000
1 run 76 3290000
1 run 77 3290000
1 run 78 3290000
1 run 79 3290000
1 run 80 3290000
1 run 81 3290000
1 run 82 3290000
1 run 83 3290000
1 run 84 3290000
1 run 85 3290000
1 run 86 3290000
1 run 87 3290000
1 run 88 3290000
1 run 89 3300000
1 run 90 3300000
1 run 91 3290000
1 run 92 3290000
1 run 93 3290000
1 run 94 3290000
1 run 95 3290000
1 run 96 3290000
1 run 97 3310000
1 run 98 3290000
1 run 99 3290000
1 run 100 3290000
1 run 101 3290000
1 run 102 3280000
1 run 103 3300000
1 run 104 3290000
1 run 105 3290000
1 run 106 3290000
1 run 107 3290000
1 run 108 3290000
1 run 109 3290000
1 run 110 3290000
1 run 111 3290000
1 run 112 3300000
1 run 113 3290000
1 run 114 3290000
1 run 115 3290000
1 run 116 3290000
1 run 117 3290000
1 run 118 3290000
1 run 119 3290000
1 run 120 3310000
1 run 121 3310000
1 run 122 3300000
1 run 123 3290000
1 run 124 3290000
1 run 125 3290000
1 run 126 3290000
1 run 127 3290000
1 run 128 3290000
1 run 129 3310000
1 run 130 3290000
1 run 131 3290000
1 run 132 3300000
1 run 133 3290000
1 run 134 3290000
1 run 135 3290000
1 run 136 3290000
1 run 137 3290000
1 run 138 3290000
1 run 139 3290000
1 run 140 3300000
1 run 141 3290000
1 run 142 3290000
1 run 143 3290000
1 run 144 3310000
1 run 145 3290000
1 run 146 3300000
1 run 147 3290000
1 run 148 3300000
1 run 149 3290000
1 run 150 3290000
1 run 151 3290000
1 run 152 3310000
1 run 153 3300000
1 run 154 3300000
1 run 155 3300000
1 run 156 3280000
1 run 157 3280000
1 run 158 3290000
1 run 159 3290000
1 run 160 3290000
1 run 161 3290000
1 run 162 3290000
1 run 163 3290000
1 run 164 3290000
1 run 165 3290000
1 run 166 3290000
1 run 167 3290000
1 run 168 3300000
1 run 169 3290000
1 run 170 3290000
1 run 171 3290000
1 run 172 3290000
1 run 173 3290000
1 run 174 3300000
1 run 175 3320000
1 run 176 3320000
1 run 177 3310000
1 run 178 3290000
1 run 179 3300000
1 run 180 3300000
1 run 181 3300000
1 run 182 3300000
1 run 183 3310000
1 run 184 3320000
1 run 185 3290000
1 run 186 3300000
1 run 187 3310000
1 run 188 3300000
1 run 189 3300000
1 run 190 3310000
1 run 191 3300000
1 run 192 3300000
1 run 193 3300000
1 run 194 3300000
1 run 195 3300000
1 run 196 3290000
1 run 197 3300000
1 run 198 3300000
1 run 199 3320000
1 run 200 3300000
1 run 201 3300000
1 run 202 3300000
1 run 203 3290000
1 run 204 3300000
1 run 205 3300000
1 run 206 3300000
1 run 207 3320000
1 run 208 3320000
1 run 209 3300000
1 run 210 3300000
1 run 211 3310000
1 run 212 3300000
1 run 213 3300000
1 run 214 3300000
1 run 215 3310000
1 run 216 3320000
1 run 217 3300000
1 run 218 3300000
1 run 219 3310000
1 run 220 3300000
1 run 221 3300000
1 run 222 3300000
1 run 223 3300000
1 run 224 3300000
1 run 225 3300000
1 run 226 3300000
1 run 227 3300000
1 run 228 3290000
1 run 229 3290000
1 run 230 3300000
1 run 231 3320000
1 run 232 3290000
1 run 233 3300000
1 run 234 3310000
1 run 235 3300000
1 run 236 3290000
1 run 237 3290000
1 run 238 3290000
1 run 239 3300000
1 run 240 3290000
1 run 241 3290000
1 run 242 3290000
1 run 243 3290000
1 run 244 3290000
1 run 245 3290000
1 run 246 3300000
1 run 247 3290000
1 run 248 3300000
1 run 249 3290000
1 run 250 3290000
1 run 251 3290000
1 run 252 3290000
1 run 253 3290000
1 run 254 3320000
1 run 255 3290000
1 run 256 3300000
1 run 257 3290000
1 run 258 3290000
1 run 259 3290000
1 run 260 3290000
1 run 261 3290000
1 run 262 3310000
1 run 263 3300000
1 run 264 3310000
1 run 265 3290000
1 run 266 3290000
1 run 267 3300000
1 run 268 3290000
1 run 269 3290000
1 run 270 3290000
1 run 271 3310000
1 run 272 3280000
1 run 273 3290000
1 run 274 3300000
1 run 275 3290000
1 run 276 3290000
1 run 277 3290000
1 run 278 3280000
1 run 279 3310000
1 run 280 3290000
1 run 281 3300000
1 run 282 3290000
1 run 283 3290000
1 run 284 3720000
1 run 285 3300000
1 run 286 3300000
1 run 287 3290000
1 run 288 3290000
1 run 289 3410000
1 run 290 3290000
1 run 291 3290000
1 run 292 3290000
1 run 293 3290000
1 run 294 3310000
1 run 295 3310000
1 run 296 3280000
1 run 297 3290000
1 run 298 3300000
1 run 299 3290000
1 run 300 3290000
# point pass=1 state=run jobs_per_s=12499745.5 updates_per_s=0.0 clock_hz=99997962
# done SystemTime { tv_sec: 1793952702, tv_nsec: 236216766 }
# passes used 1 of 1 (dropped: [])
# run - halt = 160.0 +/- NaN mW (per-pass sd NaN); 1/1 passes positive; 6 sample excursions beyond 6 MAD
# per-pass differences (mW): 160.0
# board power while running 3290 mW; jobs/s 12499745.5; updates/s 0.0; measured PL clock 99.998 MHz

View file

@ -0,0 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# The EMIO lines cross from the PS's bus clock into pl_clk0. req is synchronised in landscape_port.v; addr,
# we and wdata are stable before req is seen; rdata is stable before ack toggles.
set_false_path -to [get_cells -hierarchical -filter {NAME =~ *req_s1_reg*}]
set_false_path -through [get_pins -hierarchical -filter {NAME =~ *PS8_i/EMIOGPIOO*}]
set_false_path -through [get_pins -hierarchical -filter {NAME =~ *PS8_i/EMIOGPIOI*}]

View file

@ -0,0 +1,364 @@
// SPDX-License-Identifier: Apache-2.0
// The engine's link to the processor: an AXI-Lite register port, a write-list DMA from DDR, and a
// chain sequencer that runs a batch of chains in fabric.
//
// AXI-Lite (from the processor's full-power-domain master, through a width and a protocol
// converter): word address = byte address >> 2. Words 0x0000-0x7FFF are the engine's own map (src/regs.rs);
// 0x8000 and up are this wrapper's (src/axi.rs). Every access is answered within a few clocks,
// whatever the engine, the DMA or the sequencer is doing: an access that never completes would
// hold the processor's bus, and on a shared, remote board nobody is there to power-cycle it. So
// an engine-map access while the DMA or the sequencer holds the engine's bus is not queued: a
// write is dropped, a read returns 0xDEADBEEF, and either counts in DROPS, which the host checks.
//
// Write-list DMA (an AXI4 read master, through a width converter, into the processor's
// high-performance port): the host puts
// a list of 64-bit entries in DDR, {16'b0, addr[15:0], data[31:0]}, and the wrapper applies each
// as a register write, in order, one every two clocks (the engine needs a clock between a
// coupling-memory clear and the next write). An entry for the chain RAM (0x9000-0x97FF) is
// written there. The list must start on a 128-byte boundary and not cross a 4 KiB page: bursts
// are 16 beats of 8 bytes. A read error stops the applying (DMA_ERR) but the list is still
// drained, so DMA_BUSY always falls.
//
// Chain sequencer: for k in 0..SEQ_COUNT (at most 256), it writes chain k's six words from the
// chain RAM (BOUNDARY 0-3, X0_LO, X0_HI) into the engine, starts the job, waits for it, and copies
// STATE 0-3, STEPS and STATUS into the result RAM (0xA000 + 8k). The job's mode, bound, table and
// workload are what the host set before. The anneal loop runs in fabric; the processor only fills
// the chain RAM and collects the results.
//
// Result write-back (an AXI4 write master on the same port): WB_CHAINS chains of the result RAM from
// WB_FIRST, 32 bytes a chain (its eight words), go to DDR at WB_LO/HI in bursts of 16 beats, one
// burst outstanding. The address must be on a 128-byte boundary and the chains must not cross a 4 KiB
// page, 128 chains a page. A write error is held in WB_ERR; every burst is still sent.
//
// EMIO: eight input lines carry a constant signature (0xA5) that the host reads through the
// processor's GPIO controller before it touches an AXI address: a GPIO read cannot hang the bus,
// so a design replaced by another session's is caught before an AXI access could go unanswered.
`timescale 1ns/1ps
`default_nettype none
module landscape_axi (
input wire clk,
input wire rstn,
// AXI4-Lite slave
input wire [31:0] s_awaddr,
input wire s_awvalid,
output reg s_awready = 1'b0,
input wire [31:0] s_wdata,
input wire s_wvalid,
output reg s_wready = 1'b0,
output wire [1:0] s_bresp,
output reg s_bvalid = 1'b0,
input wire s_bready,
input wire [31:0] s_araddr,
input wire s_arvalid,
output reg s_arready = 1'b0,
output reg [31:0] s_rdata = 32'd0,
output wire [1:0] s_rresp,
output reg s_rvalid = 1'b0,
input wire s_rready,
// AXI4 read master (64-bit data)
output reg [48:0] m_araddr = 49'd0,
output wire [7:0] m_arlen,
output wire [2:0] m_arsize,
output wire [1:0] m_arburst,
output wire [3:0] m_arcache,
output wire [2:0] m_arprot,
output reg m_arvalid = 1'b0,
input wire m_arready,
input wire [63:0] m_rdata,
input wire [1:0] m_rresp,
input wire m_rlast,
input wire m_rvalid,
output wire m_rready,
// AXI4 write master (64-bit data)
output reg [48:0] m_awaddr = 49'd0,
output reg [7:0] m_awlen = 8'd0,
output wire [2:0] m_awsize,
output wire [1:0] m_awburst,
output wire [3:0] m_awcache,
output wire [2:0] m_awprot,
output reg m_awvalid = 1'b0,
input wire m_awready,
output reg [63:0] m_wdata = 64'd0,
output wire [7:0] m_wstrb,
output reg m_wlast = 1'b0,
output reg m_wvalid = 1'b0,
input wire m_wready,
input wire [1:0] m_bresp,
input wire m_bvalid,
output wire m_bready,
// EMIO signature
output wire [94:0] emio_i
);
localparam [31:0] LAXI = 32'h4C41_5849; // "LAXI"
localparam [31:0] WRAP_VERSION = 32'h0000_0002;
assign s_bresp = 2'b00;
assign s_rresp = 2'b00;
assign emio_i = {87'd0, 8'hA5};
// ---- the engine's register bus ------------------------------------------------------------
reg stb = 1'b0, we = 1'b0;
reg [15:0] addr = 16'd0;
reg [31:0] wdata = 32'd0;
wire [31:0] rdata;
wire busy, done;
landscape_core core (.clk(clk), .reg_stb(stb), .reg_we(we), .reg_addr(addr), .reg_wdata(wdata),
.reg_rdata(rdata), .busy_o(busy), .done_o(done));
// ---- wrapper state ------------------------------------------------------------------------
reg [31:0] dma_lo = 32'd0, dma_hi = 32'd0, dma_count = 32'd0;
reg [31:0] dma_left = 32'd0, dma_reqs = 32'd0; // entries still to receive, to request
reg dma_busy = 1'b0, dma_err = 1'b0;
reg [31:0] dma_done_count = 32'd0, drops = 32'd0;
reg [8:0] seq_count = 9'd0;
reg [15:0] seq_k = 16'd0, seq_done_count = 16'd0;
reg seq_busy = 1'b0;
reg [3:0] seq_ph = 4'd0;
reg [2:0] seq_i = 3'd0;
reg [3:0] seq_wait = 4'd0;
wire bus_free = !dma_busy && !seq_busy;
reg [31:0] wb_lo = 32'd0, wb_hi = 32'd0, wb_done_count = 32'd0;
reg [7:0] wb_first = 8'd0;
reg [8:0] wb_chains = 9'd0;
reg wb_busy = 1'b0, wb_err = 1'b0;
reg [2:0] wb_ph = 3'd0; // 0 address, 1-2 the two words of a beat, 3 data, 4 response
reg [10:0] wb_ra = 11'd0; // the result RAM word being fetched
reg [10:0] wb_beats = 11'd0; // beats still to send
reg [4:0] wb_left = 5'd0; // beats left in this burst
reg [31:0] wb_word = 32'd0;
// ---- the chain RAM and the result RAM: 256 chains x 8 words each, one write port each -------
reg [31:0] chain_ram [0:2047];
reg [31:0] res_ram [0:2047];
reg cr_we = 1'b0, rr_we = 1'b0;
reg [10:0] cr_wa = 11'd0, rr_wa = 11'd0;
reg [31:0] cr_wd = 32'd0, rr_wd = 32'd0;
reg [31:0] chain_q = 32'd0, res_q = 32'd0;
reg [15:0] ar_word = 16'd0;
always @(posedge clk) begin
if (cr_we) chain_ram[cr_wa] <= cr_wd;
chain_q <= chain_ram[{seq_k[7:0], seq_i}];
end
always @(posedge clk) begin
if (rr_we) res_ram[rr_wa] <= rr_wd;
res_q <= res_ram[wb_busy ? wb_ra : ar_word[10:0]];
end
// ---- AXI-Lite capture ---------------------------------------------------------------------
reg aw_have = 1'b0, w_have = 1'b0, ar_have = 1'b0;
reg [15:0] aw_word = 16'd0;
reg [31:0] w_val = 32'd0;
reg [2:0] host_ph = 3'd0; // 0 idle, 2 engine read issued, 3 engine data next, 4 wrapper data, 5 result RAM data
reg [31:0] wrap_rdata = 32'd0;
// ---- the DMA's read master ----------------------------------------------------------------
assign m_arsize = 3'd3; // 8 bytes a beat
assign m_arburst = 2'b01; // INCR
assign m_arcache = 4'b0011; // normal, bufferable, not coherent: the host cleans the list to DDR
assign m_arprot = 3'b000;
reg [7:0] burst_len = 8'd0;
assign m_arlen = burst_len;
reg ent_have = 1'b0; // an entry waiting to be applied, on the next clock
reg [63:0] ent = 64'd0;
assign m_rready = !ent_have;
// ---- the write-back's write master ---------------------------------------------------------
assign m_awsize = 3'd3;
assign m_awburst = 2'b01;
assign m_awcache = 4'b0011;
assign m_awprot = 3'b000;
assign m_wstrb = 8'hFF;
assign m_bready = 1'b1;
always @(posedge clk) begin
stb <= 1'b0;
we <= 1'b0;
cr_we <= 1'b0;
rr_we <= 1'b0;
s_awready <= 1'b0; s_wready <= 1'b0; s_arready <= 1'b0;
if (!rstn) begin
dma_busy <= 1'b0; seq_busy <= 1'b0; m_arvalid <= 1'b0; ent_have <= 1'b0;
wb_busy <= 1'b0; m_awvalid <= 1'b0; m_wvalid <= 1'b0;
s_bvalid <= 1'b0; s_rvalid <= 1'b0; aw_have <= 1'b0; w_have <= 1'b0; ar_have <= 1'b0; host_ph <= 3'd0;
end else begin
// capture the host's access: an address and its data, or a read address
if (s_awvalid && !aw_have && !s_awready) begin s_awready <= 1'b1; aw_have <= 1'b1; aw_word <= s_awaddr[17:2]; end
if (s_wvalid && !w_have && !s_wready) begin s_wready <= 1'b1; w_have <= 1'b1; w_val <= s_wdata; end
if (s_arvalid && !ar_have && !s_arready && host_ph == 3'd0) begin s_arready <= 1'b1; ar_have <= 1'b1; ar_word <= s_araddr[17:2]; end
if (s_bvalid && s_bready) s_bvalid <= 1'b0;
if (s_rvalid && s_rready) s_rvalid <= 1'b0;
// ---- the DMA: requests, beats, entries ----
if (dma_busy && !m_arvalid && dma_reqs != 32'd0) begin
burst_len <= (dma_reqs > 32'd16) ? 8'd15 : dma_reqs[7:0] - 8'd1;
m_arvalid <= 1'b1;
end else if (m_arvalid && m_arready) begin
m_arvalid <= 1'b0;
m_araddr <= m_araddr + ({41'd0, burst_len} + 49'd1) * 49'd8;
dma_reqs <= dma_reqs - ({24'd0, burst_len} + 32'd1);
end
if (m_rvalid && m_rready) begin
dma_left <= dma_left - 32'd1;
if (m_rresp != 2'b00) dma_err <= 1'b1;
else if (!dma_err) begin ent <= m_rdata; ent_have <= 1'b1; end
end
if (dma_busy && dma_left == 32'd0 && dma_reqs == 32'd0 && !m_arvalid && !ent_have) begin
dma_busy <= 1'b0;
dma_done_count <= dma_done_count + 32'd1;
end
if (ent_have) begin
ent_have <= 1'b0;
if (ent[47:32] < 16'h8000) begin
stb <= 1'b1; we <= 1'b1; addr <= ent[47:32]; wdata <= ent[31:0];
end else if (ent[47:32] >= 16'h9000 && ent[47:32] < 16'h9800) begin
cr_we <= 1'b1; cr_wa <= ent[42:32]; cr_wd <= ent[31:0];
end
end else if (seq_busy) begin
// per chain: six writes, a start, a wait, six reads into the result RAM
case (seq_ph)
4'd0: seq_ph <= 4'd1; // chain_q follows {seq_k, seq_i}
4'd1: begin
stb <= 1'b1; we <= 1'b1; wdata <= chain_q;
addr <= (seq_i < 3'd4) ? (16'h0100 + {13'd0, seq_i}) : (16'h0010 + {13'd0, seq_i - 3'd4});
if (seq_i == 3'd5) begin seq_i <= 3'd0; seq_ph <= 4'd2; end
else begin seq_i <= seq_i + 3'd1; seq_ph <= 4'd0; end
end
4'd2: begin stb <= 1'b1; we <= 1'b1; addr <= 16'h0003; wdata <= 32'd1; seq_wait <= 4'd3; seq_ph <= 4'd3; end
4'd3: begin
if (seq_wait != 4'd0) seq_wait <= seq_wait - 4'd1;
else if (!busy) begin seq_i <= 3'd0; seq_ph <= 4'd4; end
end
4'd4: begin
stb <= 1'b1; we <= 1'b0;
addr <= (seq_i < 3'd4) ? (16'h0110 + {13'd0, seq_i}) : ((seq_i == 3'd4) ? 16'h0028 : 16'h0002);
seq_ph <= 4'd5;
end
4'd5: seq_ph <= 4'd6; // the engine registers its read data on the clock after stb
4'd6: begin
rr_we <= 1'b1; rr_wa <= {seq_k[7:0], seq_i}; rr_wd <= rdata;
if (seq_i == 3'd5) begin
seq_i <= 3'd0;
seq_done_count <= seq_done_count + 16'd1;
if (seq_k + 16'd1 == {7'd0, seq_count}) seq_busy <= 1'b0;
seq_k <= seq_k + 16'd1;
seq_ph <= 4'd0;
end else begin seq_i <= seq_i + 3'd1; seq_ph <= 4'd4; end
end
default: seq_ph <= 4'd0;
endcase
end
// ---- the write-back: per burst an address, then per beat two words, then the response ----
if (wb_busy) begin
case (wb_ph)
3'd0: begin
if (!m_awvalid) begin
m_awvalid <= 1'b1;
m_awlen <= (wb_beats > 11'd16) ? 8'd15 : wb_beats[7:0] - 8'd1;
wb_left <= (wb_beats > 11'd16) ? 5'd16 : wb_beats[4:0];
end else if (m_awready) begin
m_awvalid <= 1'b0;
m_awaddr <= m_awaddr + ({41'd0, m_awlen} + 49'd1) * 49'd8;
wb_ph <= 3'd1;
end
end
3'd1: begin wb_ra <= wb_ra + 11'd1; wb_ph <= 3'd2; end // res_q follows wb_ra
3'd2: begin wb_word <= res_q; wb_ph <= 3'd5; end
3'd5: begin // the high word's turn
m_wdata <= {res_q, wb_word};
m_wvalid <= 1'b1;
m_wlast <= (wb_left == 5'd1);
wb_ra <= wb_ra + 11'd1;
wb_ph <= 3'd3;
end
3'd3: if (m_wready) begin
m_wvalid <= 1'b0;
wb_left <= wb_left - 5'd1;
wb_beats <= wb_beats - 11'd1;
wb_ph <= (wb_left == 5'd1) ? 3'd4 : 3'd1;
end
3'd4: if (m_bvalid) begin
if (m_bresp != 2'b00) wb_err <= 1'b1;
if (wb_beats == 11'd0) begin
wb_busy <= 1'b0;
wb_done_count <= wb_done_count + 32'd1;
end
wb_ph <= 3'd0;
end
default: wb_ph <= 3'd0;
endcase
end
// ---- the host ----
if (aw_have && w_have && host_ph == 3'd0 && !s_bvalid && !ent_have) begin
if (aw_word < 16'h8000) begin
if (bus_free) begin stb <= 1'b1; we <= 1'b1; addr <= aw_word; wdata <= w_val; end
else drops <= drops + 32'd1;
end else begin
case (aw_word)
16'h8002: dma_lo <= w_val;
16'h8003: dma_hi <= w_val;
16'h8004: dma_count <= w_val;
16'h8005: if (bus_free && dma_count != 32'd0) begin
dma_busy <= 1'b1; dma_err <= 1'b0; dma_left <= dma_count; dma_reqs <= dma_count;
m_araddr <= {dma_hi[16:0], dma_lo};
end
16'h8008: drops <= 32'd0;
16'h8010: seq_count <= (w_val > 32'd256) ? 9'd256 : w_val[8:0];
16'h8011: if (bus_free && !wb_busy && seq_count != 9'd0) begin
seq_busy <= 1'b1; seq_k <= 16'd0; seq_ph <= 4'd0; seq_i <= 3'd0;
end
16'h8020: wb_lo <= w_val;
16'h8021: wb_hi <= w_val;
16'h8022: wb_first <= w_val[7:0];
16'h8023: wb_chains <= (w_val > 32'd256) ? 9'd256 : w_val[8:0];
16'h8024: if (bus_free && !wb_busy && wb_chains != 9'd0) begin
wb_busy <= 1'b1; wb_err <= 1'b0; wb_ph <= 3'd0;
wb_ra <= {wb_first, 3'd0};
wb_beats <= {wb_chains, 2'b00};
m_awaddr <= {wb_hi[16:0], wb_lo};
end
16'h8013: if (seq_busy) begin
// abandon the batch and stop the engine's job
seq_busy <= 1'b0;
stb <= 1'b1; we <= 1'b1; addr <= 16'h0003; wdata <= 32'd4;
end
default: if (aw_word >= 16'h9000 && aw_word < 16'h9800) begin
cr_we <= 1'b1; cr_wa <= aw_word[10:0]; cr_wd <= w_val;
end
endcase
end
aw_have <= 1'b0; w_have <= 1'b0; s_bvalid <= 1'b1;
end else if (ar_have && host_ph == 3'd0 && !s_rvalid) begin
if (ar_word < 16'h8000) begin
if (bus_free) begin stb <= 1'b1; we <= 1'b0; addr <= ar_word; host_ph <= 3'd2; end
else begin drops <= drops + 32'd1; wrap_rdata <= 32'hDEAD_BEEF; host_ph <= 3'd4; end
end else begin
case (ar_word)
16'h8000: wrap_rdata <= LAXI;
16'h8001: wrap_rdata <= {27'd0, wb_err, wb_busy, dma_err, dma_busy, seq_busy};
16'h8002: wrap_rdata <= dma_lo;
16'h8003: wrap_rdata <= dma_hi;
16'h8004: wrap_rdata <= dma_count;
16'h8006: wrap_rdata <= dma_done_count;
16'h8007: wrap_rdata <= dma_left;
16'h8008: wrap_rdata <= drops;
16'h8009: wrap_rdata <= WRAP_VERSION;
16'h8010: wrap_rdata <= {23'd0, seq_count};
16'h8012: wrap_rdata <= {16'd0, seq_done_count};
16'h8025: wrap_rdata <= wb_done_count;
default: wrap_rdata <= 32'hDEAD_BEEF;
endcase
host_ph <= (ar_word >= 16'hA000 && ar_word < 16'hA800) ? 3'd5 : 3'd4;
end
end
if (host_ph == 3'd2) host_ph <= 3'd3;
else if (host_ph == 3'd3) begin s_rdata <= rdata; s_rvalid <= 1'b1; ar_have <= 1'b0; host_ph <= 3'd0; end
else if (host_ph == 3'd4) begin s_rdata <= wrap_rdata; s_rvalid <= 1'b1; ar_have <= 1'b0; host_ph <= 3'd0; end
else if (host_ph == 3'd5) begin s_rdata <= res_q; s_rvalid <= 1'b1; ar_have <= 1'b0; host_ph <= 3'd0; end
end
end
endmodule
`default_nettype wire

View file

@ -0,0 +1,457 @@
// SPDX-License-Identifier: Apache-2.0
// Energy-landscape engine: the two kernels of wai.quantum.landscape/1 (extensions/quantum-landscape.md
// section 4) for up to 64 cells, in programmable logic. A classical design: three-state cells, a
// counter-based random stream, no quantum effect.
//
// One field cone per cell computes h_i = b_i + sum_j W_ij s_j from the current state, every clock.
// The couplings W (ternary), the fields b and the cost c are registers the host writes.
//
// MODE 0 T0 every cell at once to threshold(h, c): the settle kernel, section 4.1. One sweep
// per clock; it stops at a fixed point, a two-cycle or the bound.
// MODE 1 GIBBS one cell at a time, in order, each drawn given the cells before it have just
// moved: the sample kernel, section 4.2. Four clocks per cell.
// MODE 2 JACOBI every cell drawn from the same old state and moved together at the end of the
// sweep. NOT the sample kernel: it does not sample the joint distribution. It is
// here only as the negative control the spec's coupled test asks to fail.
//
// 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),
// out(s, j) = mix(s + j * 0x9E3779B97F4A7C15), the SplitMix64 output. Cells go in order and the
// index rises by one per cell, so the engine keeps a counter x, adds the constant once per cell and
// runs mix() in a nine-stage pipeline that advances once per cell; the host writes x's start
// (C + the constant) and the pipeline is primed for nine clocks when a job starts.
//
// The draw is compared with two cut points the host computes for the cell's field at the sweep's
// temperature (a = floor(P(-1) 2^53), b = floor((P(-1)+P(0)) 2^53), 54 bits each): -1 if u < a,
// 0 if u < b, else +1. The table holds 8,192 pairs: 2^(13-WBITS) temperature levels of 2^WBITS
// fields from WIN_LO. A field outside the window takes threshold(h, c); the host puts the window
// where every field whose draw is not certain falls inside it, or refuses the job.
//
// Register map: src/regs.rs. Cycle counts are part of the contract with src/model.rs.
`timescale 1ns/1ps
`default_nettype none
module landscape_field (
input wire [127:0] s, // {neg,pos} per cell
input wire [127:0] w, // {neg,pos} per coupling, row i
input wire signed [19:0] b,
output wire signed [21:0] h
);
wire [63:0] p, n;
genvar j;
generate
for (j = 0; j < 64; j = j + 1) begin : T
assign p[j] = (w[2*j] & s[2*j]) | (w[2*j+1] & s[2*j+1]);
assign n[j] = (w[2*j] & s[2*j+1]) | (w[2*j+1] & s[2*j]);
end
endgenerate
function automatic signed [2:0] c3(input [2:0] pp, input [2:0] nn);
c3 = $signed({2'b0, pp[0]}) + $signed({2'b0, pp[1]}) + $signed({2'b0, pp[2]})
- $signed({2'b0, nn[0]}) - $signed({2'b0, nn[1]}) - $signed({2'b0, nn[2]});
endfunction
wire signed [2:0] v0 [0:21];
wire signed [3:0] v1 [0:10];
wire signed [4:0] v2 [0:5];
wire signed [5:0] v3 [0:2];
wire signed [6:0] v4 [0:1];
wire signed [7:0] v5;
genvar k;
generate
for (k = 0; k < 21; k = k + 1) begin : C
assign v0[k] = c3(p[3*k+2:3*k], n[3*k+2:3*k]);
end
assign v0[21] = c3({2'b0, p[63]}, {2'b0, n[63]});
for (k = 0; k < 11; k = k + 1) begin : L1
assign v1[k] = v0[2*k] + v0[2*k+1];
end
for (k = 0; k < 5; k = k + 1) begin : L2
assign v2[k] = v1[2*k] + v1[2*k+1];
end
assign v2[5] = v1[10];
for (k = 0; k < 3; k = k + 1) begin : L3
assign v3[k] = v2[2*k] + v2[2*k+1];
end
endgenerate
assign v4[0] = v3[0] + v3[1];
assign v4[1] = v3[2];
assign v5 = v4[0] + v4[1];
assign h = v5 + b;
endmodule
// The low 64 bits of x * K for a 64-bit constant K, in three stages, each stage's registers enabled
// by `en`: sixteen-bit limbs, the ten partial products that reach the low word, their sums by
// weight, then the total.
module landscape_mul64 #(parameter [63:0] K = 64'd0) (
input wire clk,
input wire en,
input wire [63:0] x,
output reg [63:0] y = 64'd0
);
wire [15:0] x0 = x[15:0], x1 = x[31:16], x2 = x[47:32], x3 = x[63:48];
localparam [15:0] K0 = K[15:0], K1 = K[31:16], K2 = K[47:32], K3 = K[63:48];
reg [31:0] p00 = 0, p01 = 0, p10 = 0, p02 = 0, p11 = 0, p20 = 0, p03 = 0, p12 = 0, p21 = 0, p30 = 0;
reg [33:0] w0 = 0, w1 = 0, w2 = 0, w3 = 0;
always @(posedge clk) if (en) begin
p00 <= x0 * K0;
p01 <= x0 * K1; p10 <= x1 * K0;
p02 <= x0 * K2; p11 <= x1 * K1; p20 <= x2 * K0;
p03 <= x0 * K3; p12 <= x1 * K2; p21 <= x2 * K1; p30 <= x3 * K0;
w0 <= {2'b0, p00};
w1 <= {2'b0, p01} + {2'b0, p10};
w2 <= {2'b0, p02} + {2'b0, p11} + {2'b0, p20};
w3 <= {2'b0, p03} + {2'b0, p12} + {2'b0, p21} + {2'b0, p30};
y <= {30'd0, w0} + {14'd0, w1, 16'd0} + {w2[31:0], 32'd0} + {w3[15:0], 48'd0};
end
endmodule
// SplitMix64's mix() on a counter: u = top 53 bits of mix(x), nine stages, advancing on `en`.
module landscape_draws (
input wire clk,
input wire load, // x <- x0, the pipeline's contents become stale (prime it)
input wire [63:0] x0,
input wire en,
output wire [52:0] u
);
localparam [63:0] GOLDEN = 64'h9E3779B97F4A7C15;
reg [63:0] x = 64'd0, g1 = 64'd0, g3 = 64'd0, g5 = 64'd0;
wire [63:0] m1, m2;
always @(posedge clk) begin
if (load) x <= x0;
else if (en) x <= x + GOLDEN;
if (en) begin
g1 <= x ^ (x >> 30);
g3 <= m1 ^ (m1 >> 27);
g5 <= m2 ^ (m2 >> 31);
end
end
landscape_mul64 #(.K(64'hBF58476D1CE4E5B9)) M1 (.clk(clk), .en(en), .x(g1), .y(m1));
landscape_mul64 #(.K(64'h94D049BB133111EB)) M2 (.clk(clk), .en(en), .x(g3), .y(m2));
assign u = g5[63:11];
endmodule
module landscape_core (
input wire clk,
input wire reg_stb, // one clock
input wire reg_we,
input wire [15:0] reg_addr,
input wire [31:0] reg_wdata,
output reg [31:0] reg_rdata,
output wire busy_o,
output wire done_o
);
localparam MODE_T0 = 2'd0, MODE_GIBBS = 2'd1, MODE_JACOBI = 2'd2;
localparam [3:0] S_IDLE = 0, S_LOAD = 1, S_T0 = 2, S_REQ = 3, S_TBL = 4, S_COMMIT = 5,
S_JAC = 6, S_FINAL = 7, S_END = 8, S_IDX = 9, S_PRIME = 10;
localparam [1:0] TERM_NONE = 0, TERM_FP = 1, TERM_2C = 2, TERM_BOUND = 3;
localparam integer PRIME = 9; // the draw pipeline's depth
// ---- host-written configuration ------------------------------------------------------
reg [1:0] mode = MODE_T0;
reg rep = 1'b0;
reg final_t0 = 1'b0;
reg [5:0] n_m1 = 6'd0;
reg [19:0] theta = 20'd0;
reg [31:0] bound = 32'd0;
reg [15:0] spl = 16'd1; // sweeps per temperature level
reg [5:0] levels_m1 = 6'd0;
reg [127:0] boundary = 128'd0;
reg [63:0] x0 = 64'd0; // the draw counter's start: chain seed + 0x9E3779B97F4A7C15
reg signed [21:0] win_lo = 22'sd0; // the table window's first field
reg [3:0] wbits = 4'd7; // the window holds 2^wbits fields
wire w_we = reg_stb & reg_we & (reg_addr[15:8] == 8'h10);
wire b_we = reg_stb & reg_we & (reg_addr[15:6] == 10'b0000_0010_00);
reg w_clear = 1'b0;
// ---- state ---------------------------------------------------------------------------
reg [127:0] state = 128'd0;
reg [127:0] prev = 128'd0;
reg [127:0] prev2 = 128'd0;
reg have1 = 1'b0, have2 = 1'b0;
reg [3:0] fsm = S_IDLE;
reg done = 1'b0;
reg [1:0] term = TERM_NONE;
reg [5:0] sel = 6'd0;
reg [63:0] sel_hot = 64'd1;
reg [5:0] level = 6'd0;
reg [15:0] spl_cnt = 16'd0;
reg [31:0] steps = 32'd0;
reg [3:0] prime_cnt = 4'd0;
assign busy_o = (fsm != S_IDLE);
assign done_o = done;
// ---- the cones -----------------------------------------------------------------------
wire signed [21:0] h [0:63];
wire [63:0] gt, lt;
wire signed [21:0] th_s = {2'b00, theta};
wire signed [21:0] nth_s = -th_s;
genvar i;
generate
for (i = 0; i < 64; i = i + 1) begin : CELL
reg [127:0] row = 128'd0;
reg signed [19:0] b = 20'sd0;
always @(posedge clk) begin
if (w_clear) row <= 128'd0;
else if (w_we && reg_addr[7:2] == i) begin
case (reg_addr[1:0])
2'd0: row[31:0] <= reg_wdata;
2'd1: row[63:32] <= reg_wdata;
2'd2: row[95:64] <= reg_wdata;
2'd3: row[127:96] <= reg_wdata;
endcase
end
if (w_clear) b <= 20'sd0;
else if (b_we && reg_addr[5:0] == i) b <= reg_wdata[19:0];
end
landscape_field f (.s(state), .w(row), .b(b), .h(h[i]));
assign gt[i] = (h[i] > th_s);
assign lt[i] = (h[i] < nth_s);
end
endgenerate
wire signed [21:0] h_sel = h[sel];
reg signed [21:0] h_reg = 22'sd0;
reg det_gt = 1'b0, det_lt = 1'b0, in_win = 1'b0;
reg [12:0] tidx = 13'd0;
// ---- the cut-point table: 8,192 x {b[53:0], a[53:0]} -----------------------------------
(* ram_style = "ultra" *) reg [107:0] tbl [0:8191];
reg [12:0] tbl_addr = 13'd0;
reg [53:0] wa = 54'd0; // a, written first
reg [31:0] tbl_b_lo = 32'd0; // b's low word; b's high word commits the pair
reg [107:0] tbl_q = 108'd0;
wire tbl_host_we = reg_stb & reg_we & (reg_addr == 16'h001A);
wire tbl_run_rd = (fsm == S_TBL);
reg tbl_host_rd = 1'b0;
wire [12:0] tbl_a = tbl_run_rd ? tidx : tbl_addr;
always @(posedge clk)
if (tbl_run_rd | tbl_host_rd | tbl_host_we) begin
if (tbl_host_we) tbl[tbl_a] <= {reg_wdata[21:0], tbl_b_lo, wa};
else tbl_q <= tbl[tbl_a];
end
wire [53:0] cut_a = tbl_q[53:0];
wire [53:0] cut_b = tbl_q[107:54];
// ---- the draws -------------------------------------------------------------------------
wire sampling = (mode == MODE_GIBBS) | (mode == MODE_JACOBI);
wire draw_load = (fsm == S_LOAD) & sampling;
wire draw_adv = (fsm == S_PRIME) | (fsm == S_COMMIT);
wire [52:0] u;
landscape_draws D (.clk(clk), .load(draw_load), .x0(x0), .en(draw_adv), .u(u));
wire [54:0] uz = {2'b00, u};
wire u_lt_a = (uz < {1'b0, cut_a});
wire u_lt_b = (uz < {1'b0, cut_b});
wire [1:0] s_cut = u_lt_a ? 2'b10 : (u_lt_b ? 2'b00 : 2'b01);
wire [1:0] s_smp = in_win ? s_cut : {det_lt, det_gt};
// ---- termination, on registers only ----------------------------------------------------
wire eq1 = (state == prev);
wire eq2 = (state == prev2);
wire step_limit = (steps == bound);
wire last_cell = (sel == n_m1);
wire last_sweep = (steps + 32'd1 == bound);
wire t0_terminal = (have1 & eq1) | (have2 & eq2) | step_limit;
wire t0_commit = ((fsm == S_T0) & ~t0_terminal) | (fsm == S_FINAL);
wire g_commit = (fsm == S_COMMIT) & (mode == MODE_GIBBS);
wire j_commit = (fsm == S_COMMIT) & (mode == MODE_JACOBI);
wire jac_all = (fsm == S_JAC);
wire load_state = (fsm == S_LOAD) | (reg_stb & reg_we & (reg_addr == 16'h0003) & reg_wdata[3] & (fsm == S_IDLE));
generate
for (i = 0; i < 64; i = i + 1) begin : UPD
always @(posedge clk) begin
if (load_state) state[2*i +: 2] <= boundary[2*i +: 2];
else if (t0_commit) state[2*i +: 2] <= {lt[i], gt[i]};
else if (jac_all) state[2*i +: 2] <= prev[2*i +: 2];
else if (g_commit & sel_hot[i]) state[2*i +: 2] <= s_smp;
if (load_state) prev[2*i +: 2] <= boundary[2*i +: 2];
else if (t0_commit) prev[2*i +: 2] <= state[2*i +: 2];
else if (j_commit & sel_hot[i]) prev[2*i +: 2] <= s_smp;
if (t0_commit) prev2[2*i +: 2] <= prev[2*i +: 2];
end
end
endgenerate
// ---- counters ------------------------------------------------------------------------
reg [47:0] uptime = 48'd0;
reg [47:0] cycles = 48'd0;
reg [47:0] updates = 48'd0;
reg [47:0] jobs = 48'd0;
reg [20:0] rd_hi = 21'd0;
wire start = reg_stb & reg_we & (reg_addr == 16'h0003) & reg_wdata[0];
wire cont = reg_stb & reg_we & (reg_addr == 16'h0003) & reg_wdata[1];
wire stop = reg_stb & reg_we & (reg_addr == 16'h0003) & reg_wdata[2];
wire clrw = reg_stb & reg_we & (reg_addr == 16'h0003) & reg_wdata[4];
wire clrc = reg_stb & reg_we & (reg_addr == 16'h0003) & reg_wdata[5];
// the window offset and the level's base, for the table index
wire signed [22:0] d_full = {h_reg[21], h_reg} - {win_lo[21], win_lo};
wire [13:0] wsize = 14'd1 << wbits;
wire [12:0] lbase = {7'd0, level} << wbits;
always @(posedge clk) begin
uptime <= uptime + 48'd1;
w_clear <= clrw;
tbl_host_rd <= reg_stb & reg_we & (reg_addr == 16'h0016);
if (clrc) begin
cycles <= 48'd0; updates <= 48'd0; jobs <= 48'd0;
end else begin
if (fsm != S_IDLE) cycles <= cycles + 48'd1;
if (fsm == S_COMMIT) updates <= updates + 48'd1;
if (fsm == S_END) jobs <= jobs + 48'd1;
end
if (fsm == S_REQ || fsm == S_IDLE) h_reg <= h_sel;
if (fsm == S_IDX) begin
in_win <= (d_full >= 23'sd0) && (d_full < $signed({9'd0, wsize}));
tidx <= lbase + d_full[12:0];
det_gt <= (h_reg > th_s);
det_lt <= (h_reg < nth_s);
end
case (fsm)
S_IDLE: begin
if (start) begin
fsm <= S_LOAD; done <= 1'b0; term <= TERM_NONE;
end else if (cont) begin
done <= 1'b0; term <= TERM_NONE; steps <= 32'd0;
level <= 6'd0; spl_cnt <= 16'd0; sel <= 6'd0; sel_hot <= 64'd1;
have1 <= 1'b0; have2 <= 1'b0;
fsm <= (mode == MODE_T0) ? S_T0 : S_REQ;
end
end
S_LOAD: begin
steps <= 32'd0; level <= 6'd0; spl_cnt <= 16'd0; sel <= 6'd0; sel_hot <= 64'd1;
have1 <= 1'b0; have2 <= 1'b0; prime_cnt <= 4'd0;
fsm <= (mode == MODE_T0) ? S_T0 : S_PRIME;
end
S_PRIME: begin
prime_cnt <= prime_cnt + 4'd1;
if (prime_cnt == PRIME - 1)
fsm <= (bound == 32'd0) ? (final_t0 ? S_FINAL : S_END) : S_REQ;
end
S_T0: begin
if (t0_terminal) begin
term <= (have1 & eq1) ? TERM_FP : ((have2 & eq2) ? TERM_2C : TERM_BOUND);
fsm <= S_END;
end else begin
steps <= steps + 32'd1;
have1 <= 1'b1;
have2 <= have1;
end
end
S_REQ: fsm <= S_IDX;
S_IDX: fsm <= S_TBL;
S_TBL: fsm <= S_COMMIT;
S_COMMIT: begin
if (last_cell) begin
sel <= 6'd0; sel_hot <= 64'd1;
if (mode == MODE_JACOBI) fsm <= S_JAC;
else begin
steps <= steps + 32'd1;
if (last_sweep) fsm <= final_t0 ? S_FINAL : S_END;
else begin
fsm <= S_REQ;
if (spl_cnt + 16'd1 == spl) begin
spl_cnt <= 16'd0;
if (level != levels_m1) level <= level + 6'd1;
end else spl_cnt <= spl_cnt + 16'd1;
end
end
end else begin
sel <= sel + 6'd1;
sel_hot <= {sel_hot[62:0], 1'b0};
fsm <= S_REQ;
end
end
S_JAC: begin
steps <= steps + 32'd1;
if (last_sweep) fsm <= final_t0 ? S_FINAL : S_END;
else begin
fsm <= S_REQ;
if (spl_cnt + 16'd1 == spl) begin
spl_cnt <= 16'd0;
if (level != levels_m1) level <= level + 6'd1;
end else spl_cnt <= spl_cnt + 16'd1;
end
end
S_FINAL: fsm <= S_END;
S_END: begin
if (rep) fsm <= S_LOAD;
else begin fsm <= S_IDLE; done <= 1'b1; end
end
default: fsm <= S_IDLE;
endcase
if (stop) begin fsm <= S_IDLE; done <= 1'b0; end
// ---- register writes -------------------------------------------------------------
if (reg_stb && reg_we) begin
case (reg_addr)
16'h0004: begin mode <= reg_wdata[1:0]; rep <= reg_wdata[8]; final_t0 <= reg_wdata[9]; end
16'h0005: n_m1 <= reg_wdata[5:0];
16'h0006: theta <= reg_wdata[19:0];
16'h0007: bound <= reg_wdata;
16'h0008: spl <= reg_wdata[15:0];
16'h0009: levels_m1 <= reg_wdata[5:0];
16'h0010: x0[31:0] <= reg_wdata;
16'h0011: x0[63:32] <= reg_wdata;
16'h0012: win_lo <= reg_wdata[21:0];
16'h0013: wbits <= reg_wdata[3:0];
16'h0014: if (fsm == S_IDLE) sel <= reg_wdata[5:0];
16'h0016: tbl_addr <= reg_wdata[12:0];
16'h0017: wa[31:0] <= reg_wdata;
16'h0018: wa[53:32] <= reg_wdata[21:0];
16'h0019: tbl_b_lo <= reg_wdata;
16'h001A: tbl_addr <= tbl_addr + 13'd1;
16'h0100: boundary[31:0] <= reg_wdata;
16'h0101: boundary[63:32] <= reg_wdata;
16'h0102: boundary[95:64] <= reg_wdata;
16'h0103: boundary[127:96] <= reg_wdata;
default: ;
endcase
end
// ---- register reads --------------------------------------------------------------
if (reg_stb && !reg_we) begin
case (reg_addr)
16'h0000: reg_rdata <= 32'h4C414E44; // "LAND"
16'h0001: reg_rdata <= 32'h0040_4002; // 64 cells, 64 levels, rtl version 2
16'h0002: reg_rdata <= {28'd0, term, done, busy_o};
16'h0004: reg_rdata <= {22'd0, final_t0, rep, 6'd0, mode};
16'h0005: reg_rdata <= {26'd0, n_m1};
16'h0006: reg_rdata <= {12'd0, theta};
16'h0007: reg_rdata <= bound;
16'h0012: reg_rdata <= {{10{win_lo[21]}}, win_lo};
16'h0013: reg_rdata <= {28'd0, wbits};
16'h0015: reg_rdata <= {{10{h_reg[21]}}, h_reg};
16'h0016: reg_rdata <= {19'd0, tbl_addr};
16'h0017: reg_rdata <= tbl_q[31:0];
16'h0018: reg_rdata <= {10'd0, tbl_q[53:32]};
16'h0019: reg_rdata <= tbl_q[85:54];
16'h001A: reg_rdata <= {10'd0, tbl_q[107:86]};
16'h001B: begin reg_rdata <= u[31:0]; rd_hi <= u[52:32]; end
16'h001C: reg_rdata <= {11'd0, rd_hi};
16'h0020: begin reg_rdata <= uptime[31:0]; rd_hi <= {5'd0, uptime[47:32]}; end
16'h0022: begin reg_rdata <= cycles[31:0]; rd_hi <= {5'd0, cycles[47:32]}; end
16'h0024: begin reg_rdata <= updates[31:0]; rd_hi <= {5'd0, updates[47:32]}; end
16'h0026: begin reg_rdata <= jobs[31:0]; rd_hi <= {5'd0, jobs[47:32]}; end
16'h0021, 16'h0023, 16'h0025, 16'h0027: reg_rdata <= {11'd0, rd_hi};
16'h0028: reg_rdata <= steps;
16'h0110: reg_rdata <= state[31:0];
16'h0111: reg_rdata <= state[63:32];
16'h0112: reg_rdata <= state[95:64];
16'h0113: reg_rdata <= state[127:96];
default: reg_rdata <= 32'hDEAD_BEEF;
endcase
end
end
endmodule
`default_nettype wire

View file

@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
// K copies of the engine behind one EMIO port: address bits [15:13] pick the core, [12:0] are the
// core's own map. busy is ORed and done ANDed across cores. The host addresses one core at a time
// (CorePort) or all at once (BroadcastPort).
`timescale 1ns/1ps
`default_nettype none
module landscape_multi #(parameter integer K = 4) (
input wire clk,
input wire [94:0] emio_o,
output wire [94:0] emio_i
);
(* ASYNC_REG = "TRUE" *) reg req_s1 = 1'b0, req_s2 = 1'b0;
reg req_d = 1'b0;
reg [6:0] por = 7'd0;
wire por_done = por[6];
always @(posedge clk) begin
req_s1 <= emio_o[80];
req_s2 <= req_s1;
req_d <= req_s2;
if (!por_done) por <= por + 7'd1;
end
reg stb = 1'b0, we = 1'b0, ack = 1'b0;
reg [15:0] addr = 16'd0;
reg [31:0] wdata = 32'd0;
reg [3:0] wait_cnt = 4'd0;
always @(posedge clk) begin
stb <= 1'b0;
if (por_done && (req_s2 ^ req_d)) begin
addr <= emio_o[79:64]; we <= emio_o[81]; wdata <= emio_o[31:0];
stb <= 1'b1; wait_cnt <= 4'd4;
end else if (wait_cnt != 4'd0) begin
wait_cnt <= wait_cnt - 4'd1;
if (wait_cnt == 4'd1) ack <= ~ack;
end
end
wire [31:0] rd [0:K-1];
wire [K-1:0] busy, done;
genvar c;
generate
for (c = 0; c < K; c = c + 1) begin : CORE
landscape_core core (
.clk(clk), .reg_stb(stb & (addr[15:13] == c)), .reg_we(we),
.reg_addr({3'b000, addr[12:0]}), .reg_wdata(wdata),
.reg_rdata(rd[c]), .busy_o(busy[c]), .done_o(done[c])
);
end
endgenerate
reg [31:0] rdata = 32'd0;
always @(posedge clk) rdata <= rd[addr[15:13] % K];
assign emio_i = {10'd0, &done, |busy, ack, 18'd0, rdata, 32'd0};
endmodule
`default_nettype wire

View file

@ -0,0 +1,67 @@
// SPDX-License-Identifier: Apache-2.0
// The job port: register reads and writes carried over the PS's EMIO GPIO lines.
//
// Why not AXI. A PS load or store to an AXI address whose PL slave is gone never completes, and it
// takes the whole board with it until someone power-cycles it. This board is shared and remote, and
// another session's reload can replace the design between two accesses. An EMIO line is a PS
// register: with the design gone a read returns junk, the ID check fails, and the job gets an
// error. The cost is speed: about 1.6 us a register access, measured.
//
// The PS offers 95 EMIO lines (banks 3, 4 and 31 bits of 5):
// emio_o[31:0] wdata (bank 3) emio_i[63:32] rdata (bank 4)
// emio_o[79:64] addr (bank 5, 15:0) emio_o[80] req (toggle) emio_o[81] we
// emio_i[82] ack (toggle) emio_i[83] busy emio_i[84] done
// busy and done are wired straight through, so polling for the end of a job takes no transaction.
//
// Handshake: the host writes wdata, then addr/we/req in one register write. The req edge crosses a
// two-flop synchroniser, so by the time it is seen addr, we and wdata have long been stable; they are
// latched then, and ack toggles once the access is done.
`timescale 1ns/1ps
`default_nettype none
module landscape_port (
input wire clk,
input wire [94:0] emio_o,
output wire [94:0] emio_i
);
(* ASYNC_REG = "TRUE" *) reg req_s1 = 1'b0, req_s2 = 1'b0;
reg req_d = 1'b0;
reg [6:0] por = 7'd0; // the PS's lines may hold anything when we are configured
wire por_done = por[6];
always @(posedge clk) begin
req_s1 <= emio_o[80];
req_s2 <= req_s1;
req_d <= req_s2;
if (!por_done) por <= por + 7'd1;
end
reg stb = 1'b0, we = 1'b0, ack = 1'b0;
reg [15:0] addr = 16'd0;
reg [31:0] wdata = 32'd0;
reg [3:0] wait_cnt = 4'd0;
wire [31:0] rdata;
wire busy, done;
always @(posedge clk) begin
stb <= 1'b0;
if (por_done && (req_s2 ^ req_d)) begin
addr <= emio_o[79:64];
we <= emio_o[81];
wdata <= emio_o[31:0];
stb <= 1'b1;
wait_cnt <= 4'd4;
end else if (wait_cnt != 4'd0) begin
wait_cnt <= wait_cnt - 4'd1;
if (wait_cnt == 4'd1) ack <= ~ack;
end
end
landscape_core core (
.clk(clk), .reg_stb(stb), .reg_we(we), .reg_addr(addr), .reg_wdata(wdata),
.reg_rdata(rdata), .busy_o(busy), .done_o(done)
);
assign emio_i = {10'd0, done, busy, ack, 18'd0, rdata, 32'd0};
endmodule
`default_nettype wire

View file

@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
// KV260 top: the PS provides pl_clk0 and the EMIO lines; there is no other PL I/O.
`timescale 1ns/1ps
module design_top();
wire pl_clk0;
wire [94:0] emio_o;
wire [94:0] emio_i;
sys_wrapper ps_i (.pl_clk0(pl_clk0), .emio_o(emio_o), .emio_i(emio_i));
landscape_port u (.clk(pl_clk0), .emio_o(emio_o), .emio_i(emio_i));
endmodule

View file

@ -0,0 +1,65 @@
// SPDX-License-Identifier: Apache-2.0
// KV260 top for the AXI link: the processing system gives the clock, an AXI-Lite master (its
// full-power-domain master port, through a width and a protocol converter), a high-performance slave port
// into DDR (through a width converter), and the EMIO lines. See landscape.tcl for why the widths
// are what they are. The reset is the fabric's own: a counter that holds the link's control in
// reset for 128 clocks after configuration, so the design does not depend on the state the
// processor's PL reset line was left in.
`timescale 1ns/1ps
module design_top();
wire pl_clk0;
reg [7:0] por = 8'd0;
always @(posedge pl_clk0) if (!por[7]) por <= por + 8'd1;
wire rstn = por[7];
wire [39:0] l_awaddr, l_araddr;
wire [31:0] l_wdata, l_rdata;
wire l_awvalid, l_awready, l_wvalid, l_wready, l_bvalid, l_bready, l_arvalid, l_arready, l_rvalid, l_rready;
wire [1:0] l_bresp, l_rresp;
wire [3:0] l_wstrb;
wire [2:0] l_awprot, l_arprot;
wire [48:0] d_araddr;
wire [7:0] d_arlen;
wire [2:0] d_arsize, d_arprot;
wire [1:0] d_arburst, d_rresp;
wire [3:0] d_arcache;
wire d_arvalid, d_arready, d_rlast, d_rvalid, d_rready;
wire [48:0] d_awaddr;
wire [7:0] d_awlen, d_wstrb;
wire [2:0] d_awsize, d_awprot;
wire [1:0] d_awburst, d_bresp;
wire [3:0] d_awcache;
wire [63:0] d_wdata;
wire d_awvalid, d_awready, d_wlast, d_wvalid, d_wready, d_bvalid, d_bready;
wire [63:0] d_rdata;
wire [94:0] emio_o, emio_i;
sys_wrapper ps_i (
.pl_clk0(pl_clk0), .aresetn(rstn), .emio_o(emio_o), .emio_i(emio_i),
.LITE_awaddr(l_awaddr), .LITE_awprot(l_awprot), .LITE_awvalid(l_awvalid), .LITE_awready(l_awready),
.LITE_wdata(l_wdata), .LITE_wstrb(l_wstrb), .LITE_wvalid(l_wvalid), .LITE_wready(l_wready),
.LITE_bresp(l_bresp), .LITE_bvalid(l_bvalid), .LITE_bready(l_bready),
.LITE_araddr(l_araddr), .LITE_arprot(l_arprot), .LITE_arvalid(l_arvalid), .LITE_arready(l_arready),
.LITE_rdata(l_rdata), .LITE_rresp(l_rresp), .LITE_rvalid(l_rvalid), .LITE_rready(l_rready),
.DMA_araddr(d_araddr), .DMA_arlen(d_arlen), .DMA_arsize(d_arsize), .DMA_arburst(d_arburst),
.DMA_arcache(d_arcache), .DMA_arprot(d_arprot), .DMA_arvalid(d_arvalid), .DMA_arready(d_arready),
.DMA_arlock(1'b0), .DMA_arqos(4'd0), .DMA_arregion(4'd0),
.DMA_rdata(d_rdata), .DMA_rresp(d_rresp), .DMA_rlast(d_rlast), .DMA_rvalid(d_rvalid), .DMA_rready(d_rready),
.DMA_awaddr(d_awaddr), .DMA_awlen(d_awlen), .DMA_awsize(d_awsize), .DMA_awburst(d_awburst), .DMA_awcache(d_awcache),
.DMA_awprot(d_awprot), .DMA_awvalid(d_awvalid), .DMA_awready(d_awready), .DMA_awlock(1'b0), .DMA_awqos(4'd0), .DMA_awregion(4'd0),
.DMA_wdata(d_wdata), .DMA_wstrb(d_wstrb), .DMA_wlast(d_wlast), .DMA_wvalid(d_wvalid), .DMA_wready(d_wready),
.DMA_bresp(d_bresp), .DMA_bvalid(d_bvalid), .DMA_bready(d_bready)
);
landscape_axi u (
.clk(pl_clk0), .rstn(rstn),
.s_awaddr(l_awaddr[31:0]), .s_awvalid(l_awvalid), .s_awready(l_awready), .s_wdata(l_wdata), .s_wvalid(l_wvalid),
.s_wready(l_wready), .s_bresp(l_bresp), .s_bvalid(l_bvalid), .s_bready(l_bready),
.s_araddr(l_araddr[31:0]), .s_arvalid(l_arvalid), .s_arready(l_arready), .s_rdata(l_rdata), .s_rresp(l_rresp),
.s_rvalid(l_rvalid), .s_rready(l_rready),
.m_araddr(d_araddr), .m_arlen(d_arlen), .m_arsize(d_arsize), .m_arburst(d_arburst), .m_arcache(d_arcache),
.m_arprot(d_arprot), .m_arvalid(d_arvalid), .m_arready(d_arready), .m_rdata(d_rdata), .m_rresp(d_rresp),
.m_rlast(d_rlast), .m_rvalid(d_rvalid), .m_rready(d_rready),
.m_awaddr(d_awaddr), .m_awlen(d_awlen), .m_awsize(d_awsize), .m_awburst(d_awburst), .m_awcache(d_awcache),
.m_awprot(d_awprot), .m_awvalid(d_awvalid), .m_awready(d_awready), .m_wdata(d_wdata), .m_wstrb(d_wstrb),
.m_wlast(d_wlast), .m_wvalid(d_wvalid), .m_wready(d_wready), .m_bresp(d_bresp), .m_bvalid(d_bvalid), .m_bready(d_bready),
.emio_i(emio_i)
);
endmodule

View file

@ -0,0 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
`timescale 1ns/1ps
module design_top();
wire pl_clk0;
wire [94:0] emio_o;
wire [94:0] emio_i;
sys_wrapper ps_i (.pl_clk0(pl_clk0), .emio_o(emio_o), .emio_i(emio_i));
landscape_multi #(.K(`LANDSCAPE_K)) u (.clk(pl_clk0), .emio_o(emio_o), .emio_i(emio_i));
endmodule

View file

@ -0,0 +1,26 @@
#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# Build the engine's bitstream and convert it for the Linux FPGA manager. Needs the vendor's tool on
# PATH, at the version this design was built and measured with: Vivado 2026.1 (landscape.tcl stops on
# any other). Its no-charge licence tier covers this part. The design uses no licensed IP: its vendor
# blocks are the processing system's configuration and, in the AXI build, the tool's AXI width and
# protocol converters. Run from an empty directory:
# sh /path/to/wai-quantum-landscape-fpga/scripts/build_bitstream.sh [MHz] [cores] [axi|emio]
# The AXI link is the default; the multi-core build (cores above 1) has the EMIO link only.
# JOBS (default 4) is how many tool runs go at once. On a small machine, run with JOBS=1 and a memory
# cap: the tool peaked at 5.1 GB alone, and a machine with 15 GB shared with other work ran out.
set -e
FREQ=${1:-100}
CORES=${2:-1}
LINK=${3:-axi}
if [ "$CORES" -gt 1 ]; then LINK=emio; fi
HERE=$(cd "$(dirname "$0")/.." && pwd)
vivado -version | head -1
vivado -mode batch -nojournal -source "$HERE/landscape.tcl" -tclargs FREQ "$FREQ" TAG landscape JOBS "${JOBS:-4}" CORES "$CORES" LINK "$LINK" > build.log 2>&1
grep -E "IMPL_STATUS|BITSTREAM" build.log
BIT=$(ls ev_landscape/ev_landscape.runs/impl_1/*.bit)
printf 'all:\n{\n %s\n}\n' "$BIT" > landscape.bif
bootgen -image landscape.bif -arch zynqmp -process_bitstream bin -w on
cp "$BIT.bin" landscape.bit.bin
grep -A6 "Design Timing Summary" ev_landscape.timing.rpt | tail -1
sha256sum landscape.bit.bin

View file

@ -0,0 +1,10 @@
#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# Cross-compile the host for the board's processor (aarch64 Linux, glibc 2.39 on the board's image).
# Needs rustup's aarch64-unknown-linux-gnu target for the repository's pinned toolchain and the
# aarch64-linux-gnu-gcc linker.
set -e
cd "$(dirname "$0")/.."
rustup target add aarch64-unknown-linux-gnu
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc cargo build --release --target aarch64-unknown-linux-gnu
ls -la "${CARGO_TARGET_DIR:-target}/aarch64-unknown-linux-gnu/release/wai-quantum-landscape-fpga"

View file

@ -0,0 +1,42 @@
#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# The power campaign, in a fixed order: the run/halt step response, a known-answer vehicle where one
# is present, a null, each job class, then the clock-scaling check. Run ON the board as root,
# detached, and touch nothing on the board until DONE appears (the meter reads the whole board, the
# processor included):
# sudo setsid nohup sh power_campaign.sh [OUTDIR] > /dev/null 2>&1 &
# Needs wai-quantum-landscape-fpga and the loaded engine. A known-answer vehicle of your own goes in
# KNOWN_ANSWER (a script taking the bitstream name, settle, passes and samples, printing
# "pass run|halt sample microwatts" lines) and KNOWN_BITSTREAM; without them that step is skipped,
# and the null and the clock-scaling check are the instrument's checks.
cd "$(dirname "$0")"
OUT=${1:-power-$(date -u +%Y%m%dT%H%M%SZ)}/power; mkdir -p "$OUT"
BIT=${ENGINE_BITSTREAM:-landscape.bit.bin}
log() { echo "$(date -u +%FT%TZ) $*" >> "$OUT/campaign.log"; }
guard() {
# the tool leaves out itself and what it descends from: this script, and run_all.sh if that started it
f=$(./wai-quantum-landscape-fpga foreign) || { log "STOP: another session is on the board: $f"; echo STOPPED > "$OUT/DONE"; exit 3; }
}
log "start; $(./wai-quantum-landscape-fpga status 2>&1 | tr '\n' ' ')"
guard
./wai-quantum-landscape-fpga meter t0-recall 1 0 300 > "$OUT/step.dat" 2>&1; log "step done"
if [ -n "${KNOWN_ANSWER:-}" ] && [ -n "${KNOWN_BITSTREAM:-}" ]; then
guard
sh "$KNOWN_ANSWER" "$KNOWN_BITSTREAM" 60 8 30 > "$OUT/known_answer.dat" 2>&1; log "known answer done exit=$?"
[ -d /sys/class/gpio/gpio594 ] && echo 594 > /sys/class/gpio/unexport
./wai-quantum-landscape-fpga load "$BIT" >> "$OUT/campaign.log" 2>&1 || { log "reload of the engine failed"; echo FAILED > "$OUT/DONE"; exit 2; }
else
log "known answer skipped: KNOWN_ANSWER and KNOWN_BITSTREAM not set"
fi
for spec in "null:t0-recall 8 60 30 --null --predict 0" "t0-recall:t0-recall 10 60 30 --predict 160" \
"gibbs-recall:gibbs-recall 10 60 30 --predict 12" "t0-idle:t0-idle 8 60 30 --predict 4"; do
name=${spec%%:*}; args=${spec#*:}
guard
./wai-quantum-landscape-fpga meter $args > "$OUT/$name.dat" 2>&1; log "$name done exit=$?"
done
guard
./wai-quantum-landscape-fpga clock 30 >> "$OUT/campaign.log" 2>&1
./wai-quantum-landscape-fpga meter t0-recall 6 60 30 --predict 80 > "$OUT/t0-recall-50MHz.dat" 2>&1; log "50 MHz done exit=$?"
./wai-quantum-landscape-fpga clock 15 >> "$OUT/campaign.log" 2>&1
log "end"
echo DONE > "$OUT/DONE"

View file

@ -0,0 +1,33 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Per-class table from meter logs, by the pre-registered statistic (mean of per-pass differences
of medians, SE across passes), plus a sensitivity column that was NOT pre-registered: the same
with a 10%-trimmed mean inside each point, which keeps sub-quantum information a median of
10 mW-quantised samples throws away. Passes flagged in the log are dropped.
python3 -I power_tables.py a.dat b.dat ...
"""
import statistics as st, sys, re
def trimmed(xs, f=0.1):
s = sorted(xs); k = int(len(s) * f)
s = s[k:len(s) - k] if k else s
return sum(s) / len(s)
for path in sys.argv[1:]:
pts, flagged = {}, set()
for line in open(path):
m = re.search(r'pass=(\d+)', line)
if line.startswith('#'):
if m and ('FLAGGED' in line or 'FOREIGN-LOAD' in line or 'NOT-OURS' in line):
flagged.add(int(m.group(1)))
continue
f = line.split()
if len(f) >= 4 and f[0].isdigit() and f[3].isdigit():
pts.setdefault((int(f[0]), f[1]), []).append(int(f[3]) / 1000.0)
passes = sorted({p for p, _ in pts} - flagged)
dm = [st.median(pts[(p, 'run')]) - st.median(pts[(p, 'halt')]) for p in passes]
dt = [trimmed(pts[(p, 'run')]) - trimmed(pts[(p, 'halt')]) for p in passes]
n = len(passes)
fmt = lambda d: f"{st.mean(d):7.1f} +/- {st.stdev(d) / n ** 0.5:4.1f}"
pr = st.mean(st.median(pts[(p, 'run')]) for p in passes)
print(f"{path.split('/')[-1]:24s} passes {n:2d} medians {fmt(dm)} mW trimmed {fmt(dt)} mW P_run {pr:6.0f} mW flagged {sorted(flagged)}")

View file

@ -0,0 +1,45 @@
#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# Everything a builder runs on their own board, in order: load the engine, the conformance set, the
# worked examples against the CPU reference, the joint statistics against exact enumeration, the
# processor-to-logic latency, throughput, then the power campaign. On the board, as root, from a
# directory holding wai-quantum-landscape-fpga, landscape.bit.bin, this script and power_campaign.sh:
# sudo setsid nohup sh run_all.sh > /dev/null 2>&1 &
# and leave the board alone until results-*/power/DONE appears (about three hours). It refuses to
# start, and stops between steps, if another session is flashing or measuring, and it stops if a
# check fails: a power figure on a design that failed its checks is not worth the three hours. On a
# shared board, name the other sessions' tools in WAI_QUANTUM_LANDSCAPE_FPGA_FOREIGN.
cd "$(dirname "$0")"
OUT=results-$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$OUT"
B=./wai-quantum-landscape-fpga
BIT=${ENGINE_BITSTREAM:-landscape.bit.bin}
log() { echo "$(date -u +%FT%TZ) $*" >> "$OUT/log.txt"; }
FOREIGN=$($B foreign) || { echo "another session is on the board: $FOREIGN" > "$OUT/STOPPED"; exit 3; }
{
echo "fpga manager flags: $(cat /sys/class/fpga_manager/fpga0/flags)"
echo "last FPGA write: $(dmesg | grep 'writing.*FPGA' | tail -1)"
sha256sum "$BIT" "$B"
} > "$OUT/board_before.txt"
# A non-zero flags word makes the manager treat a plain bitstream as something else; it is put back.
FLAGS=$(cat /sys/class/fpga_manager/fpga0/flags)
echo 0 > /sys/class/fpga_manager/fpga0/flags
# The flock is not held here across the steps: each step takes it itself, and would wait on this
# script for ever. Between steps the board is free, which is why the session check runs at each.
$B load "$BIT" > "$OUT/load.txt" 2>&1 || { log "load failed"; echo "$FLAGS" > /sys/class/fpga_manager/fpga0/flags; exit 2; }
$B status > "$OUT/status.txt" 2>&1
step() {
name=$1; shift
f=$($B foreign) || { log "STOP: another session is on the board: $f"; echo "$FLAGS" > /sys/class/fpga_manager/fpga0/flags; exit 3; }
$B "$@" > "$OUT/$name.txt" 2>&1; rc=$?
log "$name exit=$rc"
[ "$rc" -eq 0 ] || { echo "$FLAGS" > /sys/class/fpga_manager/fpga0/flags; exit 4; }
}
step conform conform
step agree agree --seeds 200
step joint joint --seed 2027
step latency latency
step throughput throughput
sh power_campaign.sh "$OUT"
$B release >> "$OUT/status.txt" 2>&1
echo "$FLAGS" > /sys/class/fpga_manager/fpga0/flags

View file

@ -0,0 +1,25 @@
#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# Replays the model's session against the logic in an open-source simulator (Icarus Verilog), the
# EMIO testbench and the AXI testbench, and fails on any mismatch. Needs iverilog and a Rust
# toolchain. The vendor's simulator takes the same files: the testbenches open landscape_tb.script
# and landscape_axi_tb.script in the directory they run in, so write those there with gen-tb and run
# xvlog rtl/landscape_core.v rtl/landscape_axi.v tb/tb_landscape_axi.v && xelab tb -s tbs && xsim tbs -R
# sh scripts/run_tb.sh [emio|axi|both]
set -e
WHICH=${1:-both}
HERE=$(cd "$(dirname "$0")/.." && pwd)
OUT=$(mktemp -d)
trap 'rm -rf "$OUT"' EXIT
cd "$HERE"
cargo build --locked --release
BIN=${CARGO_TARGET_DIR:-target}/release/wai-quantum-landscape-fpga
replay() { # name script gen-flag sources...
name=$1; script=$2; flag=$3; shift 3
"$BIN" gen-tb "$OUT/$script" $flag
iverilog -g2012 -o "$OUT/$name.vvp" "$@"
(cd "$OUT" && vvp "$name.vvp") | tee "$OUT/$name.log" | tail -3
grep -qE '^TB DONE: [0-9]+ lines, [0-9]+ checked reads, 0 mismatches' "$OUT/$name.log" || { echo "$name: the replay did not finish clean" >&2; exit 1; }
}
case "$WHICH" in emio|both) replay emio landscape_tb.script "" rtl/landscape_core.v rtl/landscape_port.v tb/tb_landscape.v ;; esac
case "$WHICH" in axi|both) replay axi landscape_axi_tb.script --axi rtl/landscape_core.v rtl/landscape_axi.v tb/tb_landscape_axi.v ;; esac

View file

@ -0,0 +1,34 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Settle time of the run/halt switch, from `wai-quantum-landscape-fpga meter <class> 1 0 300` (no
settle, 300 samples).
LAST deviation, not first touch, and of the level, not of one sample: the sensor throws single-sample
spikes all through a segment, so each 15-sample median is held to a band around the segment's final
level, the band being 2 sensor quanta (20 mW). The settle used by the campaign must exceed the last
window outside it, or every point reads the previous state. The last single sample outside
max(20 mW, 3 x the tail's SD) is printed beside it, for the spikes.
python3 -I step_settle.py step.dat
"""
import statistics, sys
WINDOW = 15
seg = {}
for line in open(sys.argv[1]):
if line.startswith('#') or not line.strip():
continue
f = line.split()
if len(f) < 4:
continue
seg.setdefault((int(f[0]), f[1]), []).append(int(f[3]) / 1000.0)
for (p, state), xs in sorted(seg.items()):
tail = xs[-60:]
final = statistics.median(tail)
band = max(20.0, 3 * statistics.pstdev(tail))
last = max((i for i, x in enumerate(xs) if abs(x - final) > band), default=-1)
medians = [statistics.median(xs[i:i + WINDOW]) for i in range(0, len(xs) - WINDOW + 1, WINDOW)]
off = max((k for k, m in enumerate(medians) if abs(m - final) > 20.0), default=-1)
print(f"pass {p} {state:4s}: final {final:.1f} mW; level settled by {(off + 1) * WINDOW} s of {len(xs)} "
f"({WINDOW}-sample medians within 20 mW after that); last single sample outside +-{band:.1f} "
f"at {last + 1} s; first 10 s mean {statistics.mean(xs[:10]):.1f} mW")

View file

@ -0,0 +1,910 @@
//! The board's AXI link (`rtl/landscape_axi.v`): the engine's registers on an AXI-Lite port at
//! 0xA000_0000, a write-list DMA that reads bulk loads from DDR, a sequencer that runs a batch of
//! chains in fabric, and a write-back that puts a batch's results in DDR.
//!
//! An AXI access to a programmable-logic address that nothing answers never completes, and it holds
//! the processor until the board is power-cycled. This board is shared and remote, and another
//! session may replace the design. So every access is preceded by a read of the link's signature on
//! EMIO bank 3 (a processor register, which always answers): if the signature is not there, the
//! access is not made. The link itself answers every access within a few clocks whatever the
//! engine is doing (an engine-map access while the DMA or the sequencer holds the engine is
//! dropped and counted, never queued), and this port never makes one: it checks DROPS after every
//! batch.
//!
//! The port is written against two small traits, [`Regs`] and [`Pages`], so that everything but the
//! `/dev/mem` mapping and the page table runs off the board: the tests here drive it against a
//! software link.
/// The wrapper's registers and RAMs, as word addresses on the link.
pub const WRAP_ID: u16 = 0x8000;
pub const WRAP_ID_VALUE: u32 = 0x4C41_5849; // "LAXI"
pub const WRAP_STATUS: u16 = 0x8001; // [0] sequencer busy [1] DMA busy [2] DMA error [3] write-back busy [4] write-back error
pub const DMA_LO: u16 = 0x8002;
pub const DMA_HI: u16 = 0x8003;
pub const DMA_COUNT: u16 = 0x8004;
pub const DMA_GO: u16 = 0x8005;
pub const DMA_DONE: u16 = 0x8006;
pub const DMA_LEFT: u16 = 0x8007;
pub const WRAP_DROPS: u16 = 0x8008;
pub const WRAP_VERSION: u16 = 0x8009;
pub const WRAP_VERSION_VALUE: u32 = 2;
pub const SEQ_COUNT: u16 = 0x8010;
pub const SEQ_GO: u16 = 0x8011;
pub const SEQ_DONE: u16 = 0x8012;
pub const SEQ_ABORT: u16 = 0x8013;
pub const WB_LO: u16 = 0x8020;
pub const WB_HI: u16 = 0x8021;
pub const WB_FIRST: u16 = 0x8022;
pub const WB_CHAINS: u16 = 0x8023;
pub const WB_GO: u16 = 0x8024;
pub const WB_DONE: u16 = 0x8025;
pub const CHAIN_RAM: u16 = 0x9000;
pub const RESULT_RAM: u16 = 0xA000;
/// Chains the chain RAM holds.
pub const SEQ_MAX: usize = 256;
/// The EMIO signature: bank 3, bits 7:0.
pub const SIGNATURE: u32 = 0xA5;
/// The link's physical address and size.
pub const LITE_BASE: u64 = 0xA000_0000;
pub const LITE_LEN: usize = 0x4_0000;
pub const ST_SEQ: u32 = 1;
pub const ST_DMA: u32 = 2;
pub const ST_DMA_ERR: u32 = 4;
pub const ST_WB: u32 = 8;
pub const ST_WB_ERR: u32 = 16;
/// Bytes a chain takes in the result RAM and in a write-back: eight words.
pub const WB_CHAIN_BYTES: usize = 32;
use crate::port::{ChainWords, Error, Port, ResultWords, batch_plain, bulk_plain, err};
use crate::regs::STATUS;
use std::time::{Duration, Instant};
/// A word every result word is set to before a write-back. All ones is the illegal cell pair, so a
/// result the link did not write cannot be taken for one it did.
pub const POISON: u32 = 0xFFFF_FFFF;
const DATA_3_RO: usize = 0x06C;
/// A window of 32-bit registers.
pub trait Regs {
fn rd(&self, off: usize) -> u32;
fn wr(&self, off: usize, v: u32);
}
/// Pages the link reads lists from and writes results to, by physical address.
pub trait Pages {
/// Entries a page holds.
fn per_page(&self) -> usize;
/// The pages for lists; the rest are for write-backs.
fn list_pages(&self) -> usize;
/// Chains a page's write-back holds.
fn chains_per_page(&self) -> usize;
/// The page for write-back number `j` of a batch.
fn result_slot(&self, j: usize) -> usize;
/// The page's physical address, read afresh each time it is used.
fn phys(&self, k: usize) -> Result<u64, Error>;
/// A list into page `k`, in DDR when this returns.
fn fill(&self, k: usize, list: &[(u16, u32)]);
/// `chains` results of page `k` set to [`POISON`] and pushed to DDR, nothing left in the caches.
fn poison(&self, k: usize, chains: usize);
/// Whatever the caches hold of page `k` dropped, so the next read is of DDR.
fn forget(&self, k: usize);
/// The six words of `chain` in page `k`.
fn result(&self, k: usize, chain: usize) -> ResultWords;
}
/// Whether the processor's ports are as the AXI build is made for. See [`port_text`].
pub fn ports_match(fpd: u32, rd: u32, wr: u32) -> bool {
(fpd >> 8) & 3 == 2 && rd & 3 == 0 && wr & 3 == 0
}
/// The processing system's settings for the two ports the link uses, in words: the
/// full-power-domain master's data width (FPD_SLCR AFI_FS, bits 9:8; 2 is 128 bits) and the
/// high-performance port's read and write widths (AFIFM2 RDCTRL and WRCTRL, bits 1:0; 0 is 128
/// bits). The DMA reads through that port and the write-back writes through it, so both widths count.
pub fn port_text(fpd: u32, rd: u32, wr: u32) -> String {
let w = |v: u32, map: [&'static str; 4]| map[(v & 3) as usize];
format!(
"FPD_SLCR AFI_FS {fpd:#010x}: M_AXI_HPM0_FPD {} bits; AFIFM2 RDCTRL {rd:#010x} WRCTRL {wr:#010x}: S_AXI_HP0_FPD reads {} bits, writes {} bits (the AXI build is made for 128 on all three)",
w(fpd >> 8, ["32", "64", "128", "?"]),
w(rd, ["128", "64", "32", "?"]),
w(wr, ["128", "64", "32", "?"])
)
}
pub struct AxiPort<R: Regs, D: Pages> {
lite: R,
gpio: R,
/// Check the signature before every access (on unless a measurement turns it off).
pub guard: bool,
pub dma: Option<D>,
/// Lists shorter than this go as plain writes.
pub dma_min: usize,
/// Batches go to the sequencer; off, they go as plain accesses (for comparison).
pub sequencer: bool,
pub accesses: u64,
pub dma_lists: u64,
pub batches: u64,
pub write_backs: u64,
}
impl<R: Regs, D: Pages> AxiPort<R, D> {
/// The link's ID and version over AXI, behind the signature.
pub fn attach(lite: R, gpio: R, dma: Option<D>) -> Result<Self, Error> {
let sig = gpio.rd(DATA_3_RO);
if sig & 0xFF != SIGNATURE {
return err(format!(
"the programmable logic does not carry the AXI link's signature (EMIO bank 3 reads {sig:#010x}, want {SIGNATURE:#x} in its low byte), \
so no AXI address is touched. Load the AXI build first; if it was loaded, another session has replaced it."
));
}
let mut p = AxiPort { lite, gpio, guard: true, dma: None, dma_min: 32, sequencer: true, accesses: 0, dma_lists: 0, batches: 0, write_backs: 0 };
let id = p.rd(WRAP_ID)?;
if id != WRAP_ID_VALUE {
return err(format!("the AXI link answers {id:#010x} for its ID, want {WRAP_ID_VALUE:#010x}"));
}
let v = p.rd(WRAP_VERSION)?;
if v != WRAP_VERSION_VALUE {
return err(format!("AXI link version {v}, this host speaks {WRAP_VERSION_VALUE}"));
}
let st = p.rd(WRAP_STATUS)?;
if st & (ST_SEQ | ST_DMA | ST_WB) != 0 {
return err(format!("the AXI link is busy at open (status {st:#x}): another process is using it"));
}
p.wr(WRAP_DROPS, 0)?;
p.dma = dma;
Ok(p)
}
pub fn name(&self) -> &'static str {
"AXI"
}
#[inline]
pub fn signature(&self) -> u32 {
self.gpio.rd(DATA_3_RO)
}
#[inline]
fn check(&self) -> Result<(), Error> {
if self.guard && self.gpio.rd(DATA_3_RO) & 0xFF != SIGNATURE {
return err("the AXI link's signature is gone: another session has replaced the design. Stopped before the next AXI access.");
}
Ok(())
}
/// A read of any word on the link, guarded.
#[inline]
pub fn rd(&mut self, word: u16) -> Result<u32, Error> {
self.check()?;
self.accesses += 1;
Ok(self.lite.rd(word as usize * 4))
}
/// A write of any word on the link, guarded.
#[inline]
pub fn wr(&mut self, word: u16, v: u32) -> Result<(), Error> {
self.check()?;
self.accesses += 1;
self.lite.wr(word as usize * 4, v);
Ok(())
}
/// Unguarded, for the latency measurement only.
#[inline]
pub fn rd_raw(&self, word: u16) -> u32 {
self.lite.rd(word as usize * 4)
}
#[inline]
pub fn wr_raw(&self, word: u16, v: u32) {
self.lite.wr(word as usize * 4, v)
}
fn wait_wrap(&mut self, bits: u32, timeout: Duration, what: &str) -> Result<u32, Error> {
let t0 = Instant::now();
let mut k = 0u32;
loop {
let st = self.rd(WRAP_STATUS)?;
if st & bits == 0 {
return Ok(st);
}
k += 1;
if k % 256 == 0 && t0.elapsed() > timeout {
return err(format!("{what} still busy after {timeout:?} (status {st:#x})"));
}
}
}
fn pages(&self) -> Result<&D, Error> {
self.dma.as_ref().ok_or_else(|| Error("no DMA pages".into()))
}
/// A page that moved while the link was using it held nothing the link saw. The kernel may move
/// a locked page; this catches it after the fact (README, what is not done).
fn still_at(&self, slot: usize, pa: u64) -> Result<(), Error> {
let now = self.pages()?.phys(slot)?;
if now != pa {
return err(format!("a DMA page moved from {pa:#x} to {now:#x} during a transfer: what it carried is not kept"));
}
Ok(())
}
/// One list of at most a page, by DMA, waited for.
fn dma_page(&mut self, slot: usize, list: &[(u16, u32)], timeout: Duration) -> Result<(), Error> {
let pa = {
let d = self.pages()?;
d.fill(slot, list);
d.phys(slot)?
};
let before = self.rd(DMA_DONE)?;
self.wr(DMA_LO, pa as u32)?;
self.wr(DMA_HI, (pa >> 32) as u32)?;
self.wr(DMA_COUNT, list.len() as u32)?;
self.wr(DMA_GO, 1)?;
let st = self.wait_wrap(ST_DMA, timeout, "the DMA")?;
if st & ST_DMA_ERR != 0 {
return err(format!("the DMA's read of the list at {pa:#x} came back with an error"));
}
let after = self.rd(DMA_DONE)?;
if after != before.wrapping_add(1) {
return err(format!("the DMA did not run (done count {before} -> {after})"));
}
self.still_at(slot, pa)?;
self.dma_lists += 1;
Ok(())
}
/// A write list of any length, a page at a time.
pub fn dma_list(&mut self, list: &[(u16, u32)], timeout: Duration) -> Result<(), Error> {
let (per, pages) = match &self.dma {
Some(d) => (d.per_page(), d.list_pages()),
None => return bulk_plain(self, list),
};
for (k, chunk) in list.chunks(per).enumerate() {
self.dma_page(k % pages, chunk, timeout)?;
}
Ok(())
}
/// The first `chains` results of the result RAM, written back to DDR a page at a time.
pub fn write_back(&mut self, chains: usize, timeout: Duration) -> Result<Vec<ResultWords>, Error> {
let per = self.pages()?.chains_per_page();
let mut out = Vec::with_capacity(chains);
for (j, first) in (0..chains).step_by(per).enumerate() {
let n = per.min(chains - first);
let (slot, pa) = {
let d = self.pages()?;
let slot = d.result_slot(j);
d.poison(slot, n);
(slot, d.phys(slot)?)
};
let before = self.rd(WB_DONE)?;
self.wr(WB_LO, pa as u32)?;
self.wr(WB_HI, (pa >> 32) as u32)?;
self.wr(WB_FIRST, first as u32)?;
self.wr(WB_CHAINS, n as u32)?;
self.wr(WB_GO, 1)?;
let st = self.wait_wrap(ST_WB, timeout, "the write-back")?;
if st & ST_WB_ERR != 0 {
return err(format!("the write-back to {pa:#x} came back with an error"));
}
let after = self.rd(WB_DONE)?;
if after != before.wrapping_add(1) {
return err(format!("the write-back did not run (done count {before} -> {after})"));
}
self.still_at(slot, pa)?;
let d = self.pages()?;
d.forget(slot);
for k in 0..n {
let r = d.result(slot, k);
if r[..4].contains(&POISON) {
return err(format!("the write-back reported done, and chain {} of it still holds what was there before it", first + k));
}
out.push(r);
}
self.write_backs += 1;
}
Ok(out)
}
fn drops_zero(&mut self) -> Result<(), Error> {
let d = self.rd(WRAP_DROPS)?;
if d != 0 {
return err(format!("{d} engine accesses were dropped while the link held the engine"));
}
Ok(())
}
}
impl<R: Regs, D: Pages> Port for AxiPort<R, D> {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error> {
if addr >= 0x8000 {
return err(format!("{addr:#06x} is not an engine register"));
}
self.wr(addr, val)
}
fn read(&mut self, addr: u16) -> Result<u32, Error> {
if addr >= 0x8000 {
return err(format!("{addr:#06x} is not an engine register"));
}
self.rd(addr)
}
fn wait_idle(&mut self, timeout: Duration) -> Result<(), Error> {
let t0 = Instant::now();
let mut k = 0u32;
loop {
let st = self.rd(STATUS)?;
if st & 1 == 0 {
return Ok(());
}
k += 1;
if k % 256 == 0 && t0.elapsed() > timeout {
return err(format!("the engine still busy after {timeout:?} (status {st:#x})"));
}
}
}
fn bulk(&mut self, list: &[(u16, u32)]) -> Result<(), Error> {
if list.len() < self.dma_min || self.dma.is_none() {
return bulk_plain(self, list);
}
self.dma_list(list, Duration::from_secs(5))
}
fn batch(&mut self, chains: &[ChainWords], timeout: Duration) -> Result<Vec<ResultWords>, Error> {
if !self.sequencer {
return batch_plain(self, chains, timeout);
}
let mut out = Vec::with_capacity(chains.len());
for group in chains.chunks(SEQ_MAX) {
let mut list = Vec::with_capacity(group.len() * 6);
for (k, c) in group.iter().enumerate() {
for (i, &w) in c.iter().enumerate() {
list.push((CHAIN_RAM + (8 * k + i) as u16, w));
}
}
if self.dma.is_some() {
self.dma_list(&list, timeout)?;
} else {
for &(a, v) in &list {
self.wr(a, v)?;
}
}
let before = self.rd(SEQ_DONE)?;
self.wr(SEQ_COUNT, group.len() as u32)?;
self.wr(SEQ_GO, 1)?;
if let Err(e) = self.wait_wrap(ST_SEQ, timeout, "the sequencer") {
let _ = self.wr(SEQ_ABORT, 1);
return Err(e);
}
let after = self.rd(SEQ_DONE)?;
if after.wrapping_sub(before) & 0xFFFF != group.len() as u32 {
return err(format!("the sequencer ran {} chains of {}", after.wrapping_sub(before) & 0xFFFF, group.len()));
}
if self.dma.is_some() {
out.extend(self.write_back(group.len(), timeout)?);
} else {
for k in 0..group.len() {
let mut r = [0u32; 6];
for (i, w) in r.iter_mut().enumerate() {
*w = self.rd(RESULT_RAM + (8 * k + i) as u16)?;
}
out.push(r);
}
}
self.drops_zero()?;
self.batches += 1;
}
Ok(out)
}
}
#[cfg(target_os = "linux")]
pub use board_side::*;
/// What only the board has: `/dev/mem` and the page table.
#[cfg(target_os = "linux")]
mod board_side {
use super::*;
use crate::emio::Mem;
use std::fs::File;
use std::os::unix::fs::FileExt;
const GPIO_BASE: u64 = 0xFF0A_0000;
unsafe extern "C" {
fn mmap(addr: *mut u8, len: usize, prot: i32, flags: i32, fd: i32, off: i64) -> *mut u8;
fn munmap(addr: *mut u8, len: usize) -> i32;
fn sysconf(name: i32) -> i64;
}
const PROT_RW: i32 = 1 | 2;
const MAP_PRIVATE: i32 = 0x02;
const MAP_ANONYMOUS: i32 = 0x20;
const MAP_LOCKED: i32 = 0x2000;
const MAP_POPULATE: i32 = 0x8000;
const SC_PAGESIZE: i32 = 30;
impl Regs for Mem {
#[inline]
fn rd(&self, off: usize) -> u32 {
Mem::rd(self, off)
}
#[inline]
fn wr(&self, off: usize, v: u32) {
Mem::wr(self, off, v)
}
}
/// The data cache's line size, from CTR_EL0.
#[cfg(target_arch = "aarch64")]
fn dline() -> usize {
let ctr: u64;
unsafe { core::arch::asm!("mrs {0}, ctr_el0", out(reg) ctr) };
4 << ((ctr >> 16) & 0xF)
}
/// Cleans `len` bytes from `p` to the point of coherency: the DMA port is not coherent with the
/// processor's caches, so a list the processor wrote must be in DDR before the link reads it.
#[cfg(target_arch = "aarch64")]
fn clean(p: *const u8, len: usize) {
let line = dline();
let mut a = p as usize & !(line - 1);
let end = p as usize + len;
while a < end {
unsafe { core::arch::asm!("dc cvac, {0}", in(reg) a) };
a += line;
}
unsafe { core::arch::asm!("dsb sy") };
}
#[cfg(not(target_arch = "aarch64"))]
fn clean(_p: *const u8, _len: usize) {}
/// Cleans and invalidates `len` bytes from `p`: before a write-back, so nothing in the caches
/// lands on what the link writes, and after it, so the processor reads DDR and not a line it
/// fetched meanwhile.
#[cfg(target_arch = "aarch64")]
fn flush(p: *const u8, len: usize) {
let line = dline();
let mut a = p as usize & !(line - 1);
let end = p as usize + len;
while a < end {
unsafe { core::arch::asm!("dc civac, {0}", in(reg) a) };
a += line;
}
unsafe { core::arch::asm!("dsb sy") };
}
#[cfg(not(target_arch = "aarch64"))]
fn flush(_p: *const u8, _len: usize) {}
/// Locked pages for write lists and results, and their physical addresses from
/// /proc/self/pagemap (root). Locked is not pinned: the kernel may still move such a page, and
/// a kernel-owned buffer is what would rule that out (README, what is not done).
pub struct Dma {
buf: *mut u8,
pages: usize,
page: usize,
pagemap: File,
}
impl Dma {
pub fn new(pages: usize) -> Result<Dma, Error> {
let page = unsafe { sysconf(SC_PAGESIZE) } as usize;
if !(4096..=65536).contains(&page) {
return err(format!("page size {page}"));
}
let len = pages * page;
let buf = unsafe { mmap(std::ptr::null_mut(), len, PROT_RW, MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED | MAP_POPULATE, -1, 0) };
if buf as isize == -1 || buf.is_null() {
return err("could not lock pages for the DMA's lists");
}
let pagemap = File::open("/proc/self/pagemap").map_err(|e| Error(format!("/proc/self/pagemap: {e}")))?;
let d = Dma { buf, pages, page, pagemap };
for k in 0..pages {
d.phys(k)?;
}
Ok(d)
}
}
impl Pages for Dma {
fn per_page(&self) -> usize {
self.page / 8
}
fn list_pages(&self) -> usize {
self.pages - 2
}
fn chains_per_page(&self) -> usize {
self.page / WB_CHAIN_BYTES
}
fn result_slot(&self, j: usize) -> usize {
self.pages - 1 - j % 2
}
fn phys(&self, k: usize) -> Result<u64, Error> {
let va = self.buf as usize + k * self.page;
let mut e = [0u8; 8];
self.pagemap.read_exact_at(&mut e, (va / self.page * 8) as u64).map_err(|x| Error(format!("pagemap: {x}")))?;
let e = u64::from_le_bytes(e);
let pfn = e & ((1 << 55) - 1);
if e >> 63 == 0 || pfn == 0 {
return err("no physical address for a DMA page (run as root)");
}
Ok(pfn * self.page as u64)
}
fn fill(&self, k: usize, list: &[(u16, u32)]) {
let p = unsafe { self.buf.add(k * self.page) } as *mut u64;
for (i, &(a, v)) in list.iter().enumerate() {
unsafe { std::ptr::write_volatile(p.add(i), ((a as u64) << 32) | v as u64) };
}
clean(p as *const u8, list.len() * 8);
}
fn poison(&self, k: usize, chains: usize) {
let p = unsafe { self.buf.add(k * self.page) } as *mut u32;
for i in 0..chains * WB_CHAIN_BYTES / 4 {
unsafe { std::ptr::write_volatile(p.add(i), POISON) };
}
flush(p as *const u8, self.page);
}
fn forget(&self, k: usize) {
flush(unsafe { self.buf.add(k * self.page) }, self.page);
}
fn result(&self, k: usize, chain: usize) -> ResultWords {
let p = unsafe { self.buf.add(k * self.page + chain * WB_CHAIN_BYTES) } as *const u32;
let mut r = [0u32; 6];
for (i, w) in r.iter_mut().enumerate() {
*w = unsafe { std::ptr::read_volatile(p.add(i)) };
}
r
}
}
impl Drop for Dma {
fn drop(&mut self) {
unsafe {
munmap(self.buf, self.pages * self.page);
}
}
}
pub type BoardAxi = AxiPort<Mem, Dma>;
/// Whether the loaded design carries the AXI link's signature: a GPIO read, safe whatever is loaded.
pub fn signature_present() -> bool {
Mem::map(GPIO_BASE, 0x1000).map(|g| g.rd(DATA_3_RO) & 0xFF == SIGNATURE).unwrap_or(false)
}
/// The two ports' widths as the board's boot left them (read only; loading a bitstream through
/// the FPGA manager does not change them): whether they match, and the sentence that says so.
pub fn ps_ports() -> Result<(bool, String), Error> {
let fpd = Mem::map(0xFD61_5000, 0x1000)?.rd(0);
let afi = Mem::map(0xFD38_0000, 0x1000)?;
let (rd, wr) = (afi.rd(0x000), afi.rd(0x014));
Ok((ports_match(fpd, rd, wr), port_text(fpd, rd, wr)))
}
/// Opens the board's link: the signature and the port widths first, both by registers that always
/// answer, then the wrapper's ID and version over AXI.
pub fn open() -> Result<BoardAxi, Error> {
let gpio = Mem::map(GPIO_BASE, 0x1000)?;
let sig = gpio.rd(DATA_3_RO);
if sig & 0xFF != SIGNATURE {
return err(format!(
"the programmable logic does not carry the AXI link's signature (EMIO bank 3 reads {sig:#010x}, want {SIGNATURE:#x} in its low byte), \
so no AXI address is touched. Load the AXI build first; if it was loaded, another session has replaced it."
));
}
let (ok, text) = ps_ports()?;
if !ok {
return err(format!("the processing system's ports are not set as the AXI build needs, so no AXI address is touched: {text}"));
}
let lite = Mem::map(LITE_BASE, LITE_LEN)?;
AxiPort::attach(lite, gpio, Some(Dma::new(16)?))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::conform::bits;
use crate::engine::Engine;
use crate::member::{BoardInfo, BoardMember};
use crate::model::ModelPort;
use crate::port::batch_plain;
use std::cell::RefCell;
use std::rc::Rc;
const PAGE: usize = 4096;
const BASE: u64 = 0x1000_0000;
/// The link in software: the wrapper's registers and RAMs, over the model of the engine, with
/// its DMA and write-back working on [`FakePages`]. What a test wants broken, it sets here.
struct Link {
model: ModelPort,
pages: Rc<RefCell<Vec<Vec<u8>>>>,
signature: u32,
chain_ram: Vec<u32>,
result_ram: Vec<u32>,
regs: std::collections::HashMap<u16, u32>,
dma_done: u32,
seq_done: u32,
wb_done: u32,
drops: u32,
lite_accesses: u64,
// faults
wb_writes_nothing: bool,
seq_loses_one: bool,
wb_swaps_chains: bool,
}
impl Link {
fn reg(&self, a: u16) -> u32 {
self.regs.get(&a).copied().unwrap_or(0)
}
fn word(&self, addr: u64) -> usize {
(addr - BASE) as usize
}
fn write(&mut self, a: u16, v: u32) {
match a {
DMA_GO => {
let at = self.word(((self.reg(DMA_HI) as u64) << 32) | self.reg(DMA_LO) as u64);
for i in 0..self.reg(DMA_COUNT) as usize {
let e = u64::from_le_bytes(self.pages.borrow()[at / PAGE][at % PAGE + 8 * i..at % PAGE + 8 * i + 8].try_into().unwrap());
let (ea, ev) = ((e >> 32) as u16, e as u32);
if ea >= CHAIN_RAM {
self.chain_ram[(ea - CHAIN_RAM) as usize] = ev;
} else {
self.model.write(ea, ev).unwrap();
}
}
self.dma_done += 1;
}
SEQ_GO => {
let n = self.reg(SEQ_COUNT) as usize;
let chains: Vec<ChainWords> = (0..n).map(|k| std::array::from_fn(|i| self.chain_ram[8 * k + i])).collect();
for (k, r) in batch_plain(&mut self.model, &chains, Duration::from_secs(1)).unwrap().iter().enumerate() {
self.result_ram[8 * k..8 * k + 6].copy_from_slice(r);
}
self.seq_done += (n - usize::from(self.seq_loses_one)) as u32;
}
WB_GO => {
let at = self.word(((self.reg(WB_HI) as u64) << 32) | self.reg(WB_LO) as u64);
if !self.wb_writes_nothing {
let first = self.reg(WB_FIRST) as usize;
for k in 0..self.reg(WB_CHAINS) as usize {
let from = if self.wb_swaps_chains { k ^ 1 } else { k };
for i in 0..8 {
let w = self.result_ram[8 * (first + from) + i];
let o = at % PAGE + k * WB_CHAIN_BYTES + 4 * i;
self.pages.borrow_mut()[at / PAGE][o..o + 4].copy_from_slice(&w.to_le_bytes());
}
}
}
self.wb_done += 1;
}
WRAP_DROPS => self.drops = 0,
a if a < 0x8000 => self.model.write(a, v).unwrap(),
a if (CHAIN_RAM..CHAIN_RAM + 0x800).contains(&a) => self.chain_ram[(a - CHAIN_RAM) as usize] = v,
a => {
self.regs.insert(a, v);
}
}
}
fn read(&mut self, a: u16) -> u32 {
match a {
WRAP_ID => WRAP_ID_VALUE,
WRAP_VERSION => WRAP_VERSION_VALUE,
WRAP_STATUS => 0,
DMA_DONE => self.dma_done,
SEQ_DONE => self.seq_done,
WB_DONE => self.wb_done,
WRAP_DROPS => self.drops,
a if a < 0x8000 => self.model.read(a).unwrap(),
a if (RESULT_RAM..RESULT_RAM + 0x800).contains(&a) => self.result_ram[(a - RESULT_RAM) as usize],
a => self.reg(a),
}
}
}
#[derive(Clone)]
struct Bus {
link: Rc<RefCell<Link>>,
gpio: bool,
}
impl Regs for Bus {
fn rd(&self, off: usize) -> u32 {
let mut l = self.link.borrow_mut();
if self.gpio {
return l.signature;
}
l.lite_accesses += 1;
l.read((off / 4) as u16)
}
fn wr(&self, off: usize, v: u32) {
assert!(!self.gpio);
let mut l = self.link.borrow_mut();
l.lite_accesses += 1;
l.write((off / 4) as u16, v);
}
}
struct FakePages {
pages: Rc<RefCell<Vec<Vec<u8>>>>,
/// Whether the kernel moves a page between the address's two reads.
moves: std::cell::Cell<bool>,
reads: std::cell::Cell<u64>,
}
impl Pages for FakePages {
fn per_page(&self) -> usize {
PAGE / 8
}
fn list_pages(&self) -> usize {
self.pages.borrow().len() - 2
}
fn chains_per_page(&self) -> usize {
PAGE / WB_CHAIN_BYTES
}
fn result_slot(&self, j: usize) -> usize {
self.pages.borrow().len() - 1 - j % 2
}
fn phys(&self, k: usize) -> Result<u64, Error> {
self.reads.set(self.reads.get() + 1);
let moved = self.moves.get() && self.reads.get() % 2 == 0;
Ok(BASE + (k * PAGE) as u64 + if moved { 0x100_0000 } else { 0 })
}
fn fill(&self, k: usize, list: &[(u16, u32)]) {
for (i, &(a, v)) in list.iter().enumerate() {
self.pages.borrow_mut()[k][8 * i..8 * i + 8].copy_from_slice(&(((a as u64) << 32) | v as u64).to_le_bytes());
}
}
fn poison(&self, k: usize, chains: usize) {
self.pages.borrow_mut()[k][..chains * WB_CHAIN_BYTES].fill(0xFF);
}
fn forget(&self, _k: usize) {}
fn result(&self, k: usize, chain: usize) -> ResultWords {
let p = self.pages.borrow();
std::array::from_fn(|i| {
let o = chain * WB_CHAIN_BYTES + 4 * i;
u32::from_le_bytes(p[k][o..o + 4].try_into().unwrap())
})
}
}
type FakePort = AxiPort<Bus, FakePages>;
fn fake() -> (FakePort, Rc<RefCell<Link>>) {
let pages = Rc::new(RefCell::new(vec![vec![0u8; PAGE]; 16]));
let link = Rc::new(RefCell::new(Link {
model: ModelPort::new(),
pages: pages.clone(),
signature: SIGNATURE,
chain_ram: vec![0; 2048],
result_ram: vec![0; 2048],
regs: Default::default(),
dma_done: 0,
seq_done: 0,
wb_done: 0,
drops: 0,
lite_accesses: 0,
wb_writes_nothing: false,
seq_loses_one: false,
wb_swaps_chains: false,
}));
let p = AxiPort::attach(Bus { link: link.clone(), gpio: false }, Bus { link: link.clone(), gpio: true }, Some(FakePages { pages, moves: Default::default(), reads: Default::default() })).unwrap();
(p, link)
}
fn chains(n: u32) -> Vec<ChainWords> {
(0..n).map(|k| [k & 1, 0, 0, 0, k, 0]).collect()
}
/// A one-cell job through the port, set up by plain accesses.
fn set_up(p: &mut FakePort) {
p.write(crate::regs::MODE, crate::regs::MODE_T0).unwrap();
p.write(crate::regs::BOUND, 3).unwrap();
}
#[test]
fn the_port_gives_the_model_s_bits_through_dma_sequencer_and_write_back() {
let (p, link) = fake();
let m = BoardMember::new(Engine::open(p).unwrap(), BoardInfo::default());
let b = bits(&m).unwrap();
assert!(b.settle_ok && b.chains_ok, "{}", b.text);
let e = m.engine.borrow();
assert!(e.port.dma_lists > 0 && e.port.batches > 0 && e.port.write_backs > 0, "{} {} {}", e.port.dma_lists, e.port.batches, e.port.write_backs);
assert_eq!(link.borrow().drops, 0);
}
#[test]
fn more_chains_than_a_page_holds_come_back_in_order() {
let (mut p, _) = fake();
set_up(&mut p);
let got = p.batch(&chains(300), Duration::from_secs(1)).unwrap();
assert_eq!(got.len(), 300);
let (mut q, _) = fake();
set_up(&mut q);
q.sequencer = false;
assert_eq!(got, q.batch(&chains(300), Duration::from_secs(1)).unwrap());
}
#[test]
fn no_access_is_made_without_the_signature() {
let (mut p, link) = fake();
let before = link.borrow().lite_accesses;
link.borrow_mut().signature = 0;
assert!(p.rd(WRAP_ID).is_err());
assert!(p.wr(DMA_COUNT, 1).is_err());
assert!(p.read(crate::regs::ID).is_err());
assert_eq!(link.borrow().lite_accesses, before);
}
#[test]
fn a_design_without_the_signature_is_not_touched_at_open() {
let (p, link) = fake();
drop(p);
link.borrow_mut().signature = 0;
let before = link.borrow().lite_accesses;
let lite = Bus { link: link.clone(), gpio: false };
let gpio = Bus { link: link.clone(), gpio: true };
assert!(AxiPort::<Bus, FakePages>::attach(lite, gpio, None).is_err());
assert_eq!(link.borrow().lite_accesses, before);
}
#[test]
fn a_write_back_that_wrote_nothing_is_caught() {
let (mut p, link) = fake();
set_up(&mut p);
p.batch(&chains(4), Duration::from_secs(1)).unwrap();
// the page still holds the last batch's good results; the poison is what shows
link.borrow_mut().wb_writes_nothing = true;
let e = p.batch(&chains(4), Duration::from_secs(1)).expect_err("stale results");
assert!(e.0.contains("still holds"), "{}", e.0);
}
#[test]
fn a_write_back_out_of_place_changes_the_result() {
let (mut p, _) = fake();
set_up(&mut p);
let good = p.batch(&chains(4), Duration::from_secs(1)).unwrap();
let (mut q, link) = fake();
set_up(&mut q);
link.borrow_mut().wb_swaps_chains = true;
assert_ne!(good, q.batch(&chains(4), Duration::from_secs(1)).unwrap());
}
#[test]
fn a_page_that_moved_during_a_transfer_is_an_error() {
let (mut p, _) = fake();
set_up(&mut p);
let d = p.dma.as_ref().unwrap();
d.reads.set(0);
d.moves.set(true);
let e = p.batch(&chains(4), Duration::from_secs(1)).expect_err("a moved page");
assert!(e.0.contains("moved"), "{}", e.0);
}
#[test]
fn a_short_sequencer_run_is_an_error() {
let (mut p, link) = fake();
set_up(&mut p);
link.borrow_mut().seq_loses_one = true;
let e = p.batch(&chains(4), Duration::from_secs(1)).expect_err("a chain short");
assert!(e.0.contains("the sequencer ran"), "{}", e.0);
}
#[test]
fn a_dropped_access_is_an_error() {
let (mut p, link) = fake();
set_up(&mut p);
link.borrow_mut().drops = 1;
let e = p.batch(&chains(2), Duration::from_secs(1)).expect_err("a dropped access");
assert!(e.0.contains("dropped"), "{}", e.0);
}
#[test]
fn the_link_s_port_widths_are_all_three() {
assert!(ports_match(0xa00, 0x3b0, 0x3b0));
assert!(!ports_match(0x000, 0x3b0, 0x3b0), "the master at 32 bits");
assert!(!ports_match(0xa00, 0x3b1, 0x3b0), "reads at 64 bits");
assert!(!ports_match(0xa00, 0x3b0, 0x3b1), "writes at 64 bits");
}
}

View file

@ -0,0 +1,200 @@
//! Board housekeeping: loading the bitstream and proving the load took, the PL clock, the power
//! sensor, the single-tenant lock, and the checks that another session has not touched the PL.
use crate::emio::Mem;
use crate::port::{Error, err};
use std::fs;
use std::process::Command;
pub const FPGA: &str = "/sys/class/fpga_manager/fpga0";
const CRL_BASE: u64 = 0xFF5E_0000;
const PL0_REF_CTRL: usize = 0xC0;
pub fn dmesg_fpga_writes() -> Result<Vec<String>, Error> {
let out = Command::new("dmesg").output().map_err(|e| Error(format!("dmesg: {e}")))?;
Ok(String::from_utf8_lossy(&out.stdout)
.lines()
.filter(|l| l.contains("writing") && l.contains("FPGA"))
.map(|l| l.to_string())
.collect())
}
/// This process and every process it descends from: a campaign script's own command line may match
/// the patterns below (its arguments name the vehicles it runs), and it is not another session.
fn ancestors() -> Vec<u32> {
let mut out = Vec::new();
let mut pid = std::process::id();
while pid > 1 && out.len() < 64 {
out.push(pid);
let stat = fs::read_to_string(format!("/proc/{pid}/stat")).unwrap_or_default();
// the fields after the command's closing parenthesis: state, then the parent's id
pid = stat.rsplit_once(')').and_then(|(_, rest)| rest.split_whitespace().nth(1)).and_then(|v| v.parse().ok()).unwrap_or(0);
}
out
}
/// What counts as another session: this tool, the two scripts that drive it, and the vendor's
/// loader. A site adds its own in `WAI_QUANTUM_LANDSCAPE_FPGA_FOREIGN`, an extended regex: on a
/// shared board, the names the other sessions' tools go by.
pub fn foreign_pattern(site: Option<&str>) -> String {
let own = "wai-quantum-landscape-fpga|run_all\\.sh|power_campaign\\.sh|fpgautil";
match site {
Some(s) if !s.is_empty() => format!("{own}|{s}"),
_ => own.to_string(),
}
}
/// The lines of `pgrep -af` that are another session's: not the search itself, and not this process
/// or anything it descends from. Another run of this tool counts: its lock does not stop a reload
/// by something else, and a queued job is still a job.
pub fn foreign_lines(pgrep: &str, mine: &[u32]) -> String {
pgrep
.lines()
.filter(|l| !l.contains("pgrep"))
.filter(|l| l.split_whitespace().next().and_then(|p| p.parse::<u32>().ok()).is_none_or(|p| !mine.contains(&p)))
.collect::<Vec<_>>()
.join("\n")
}
/// Processes that mean another session is flashing or measuring.
pub fn foreign_sessions() -> String {
let pat = foreign_pattern(std::env::var("WAI_QUANTUM_LANDSCAPE_FPGA_FOREIGN").ok().as_deref());
let out = Command::new("pgrep").arg("-af").arg(&pat).output();
let mine = ancestors();
out.map(|o| foreign_lines(&String::from_utf8_lossy(&o.stdout), &mine)).unwrap_or_default()
}
/// Loads a bitstream through the FPGA manager and proves the load by its dmesg line. The caller
/// holds [`lock`]: a reload must wait for the job that holds the board.
pub fn load_bitstream(path: &str, _held: &Lock) -> Result<String, Error> {
let f = foreign_sessions();
if !f.is_empty() {
return err(format!("another session is active on the board, not loading:\n{f}"));
}
let flags = fs::read_to_string(format!("{FPGA}/flags")).unwrap_or_default();
if flags.trim() != "0" {
return err(format!("fpga manager flags = {} (want 0)", flags.trim()));
}
let name = std::path::Path::new(path).file_name().and_then(|s| s.to_str()).ok_or_else(|| Error("bad path".into()))?.to_string();
let fw = format!("/lib/firmware/{name}");
if std::path::Path::new(path) != std::path::Path::new(&fw) {
fs::copy(path, &fw).map_err(|e| Error(format!("copy to {fw}: {e}")))?;
}
let before = dmesg_fpga_writes()?.iter().filter(|l| l.contains(&format!("writing {name} "))).count();
fs::write(format!("{FPGA}/firmware"), &name).map_err(|e| Error(format!("fpga manager: {e}")))?;
std::thread::sleep(std::time::Duration::from_secs(3));
let w = dmesg_fpga_writes()?;
let after = w.iter().filter(|l| l.contains(&format!("writing {name} "))).count();
if after <= before {
return err(format!("LOAD-NOOP: no new dmesg line for {name}; the old design is still running"));
}
let last = w.last().cloned().unwrap_or_default();
if last.contains("fail") || last.contains("error") {
return err(format!("load reported an error: {last}"));
}
Ok(last)
}
/// pl_clk0 = IOPLL / div0 / div1. Returns (raw register, MHz) assuming the 1500 MHz source this
/// board's IOPLL runs at; the engine's uptime counter checks it by behaviour.
pub fn pl0_ctrl() -> Result<(u32, f64), Error> {
let m = Mem::map(CRL_BASE, 0x1000)?;
let v = m.rd(PL0_REF_CTRL);
let d0 = ((v >> 8) & 0x3F).max(1);
let d1 = ((v >> 16) & 0x3F).max(1);
Ok((v, 1500.0 / (d0 * d1) as f64))
}
/// Sets div0 for pl_clk0 (div1 = 1). 15 -> 100 MHz. Returns the previous register value.
pub fn set_pl0_div(div0: u32) -> Result<u32, Error> {
if !(4..=63).contains(&div0) {
return err("div0 must be 4..63");
}
let m = Mem::map(CRL_BASE, 0x1000)?;
let old = m.rd(PL0_REF_CTRL);
let new = (old & !(0x3F << 8) & !(0x3F << 16)) | (div0 << 8) | (1 << 16) | (1 << 24);
m.wr(PL0_REF_CTRL, new);
Ok(old)
}
pub fn restore_pl0(v: u32) -> Result<(), Error> {
let m = Mem::map(CRL_BASE, 0x1000)?;
m.wr(PL0_REF_CTRL, v);
Ok(())
}
pub fn find_power_sensor() -> Result<String, Error> {
for e in fs::read_dir("/sys/class/hwmon").map_err(|e| Error(e.to_string()))?.flatten() {
let p = e.path();
if fs::read_to_string(p.join("name")).map(|s| s.trim() == "ina260_u14").unwrap_or(false) {
return Ok(p.join("power1_input").to_string_lossy().to_string());
}
}
err("no ina260_u14 power sensor")
}
pub fn read_uw(path: &str) -> Result<u64, Error> {
fs::read_to_string(path).map_err(|e| Error(e.to_string()))?.trim().parse::<u64>().map_err(|e| Error(e.to_string()))
}
unsafe extern "C" {
fn flock(fd: i32, op: i32) -> i32;
}
/// The board is single-tenant: jobs queue on this lock, in the order the kernel grants it.
pub struct Lock(#[allow(dead_code)] fs::File);
pub fn lock() -> Result<Lock, Error> {
use std::os::unix::io::AsRawFd;
let f = fs::OpenOptions::new().create(true).write(true).truncate(false).open("/run/lock/wai-quantum-landscape-fpga.lock")
.or_else(|_| fs::OpenOptions::new().create(true).write(true).truncate(false).open("/tmp/wai-quantum-landscape-fpga.lock"))
.map_err(|e| Error(format!("lock file: {e}")))?;
if unsafe { flock(f.as_raw_fd(), 2) } != 0 {
return err("flock failed");
}
Ok(Lock(f))
}
#[cfg(test)]
mod tests {
use super::*;
const PGREP: &str = "101 sudo ./wai-quantum-landscape-fpga conform\n102 ./wai-quantum-landscape-fpga conform\n200 sh power_campaign.sh out\n300 ./wai-quantum-landscape-fpga load x.bit.bin\n301 pgrep -af wai-quantum-landscape-fpga";
#[test]
fn another_run_of_this_tool_is_seen() {
let f = foreign_lines(PGREP, &[300]);
assert!(f.contains("102 ./wai-quantum-landscape-fpga conform"), "{f}");
assert!(f.contains("200 sh power_campaign.sh"), "{f}");
}
#[test]
fn this_process_its_parents_and_the_search_are_not() {
let f = foreign_lines(PGREP, &[300, 200, 101, 102]);
assert_eq!(f, "");
}
#[test]
fn the_pattern_names_this_tool_whatever_a_site_adds() {
assert!(foreign_pattern(None).contains("wai-quantum-landscape-fpga"));
let p = foreign_pattern(Some("their_tool"));
assert!(p.contains("wai-quantum-landscape-fpga") && p.ends_with("|their_tool"), "{p}");
}
#[test]
fn the_lock_serialises_two_holders() {
// two opens of one lock file in one process: the second must wait for the first to drop
use std::sync::mpsc::channel;
use std::time::Duration;
let first = lock().unwrap();
let (tx, rx) = channel();
let t = std::thread::spawn(move || {
let l = lock().unwrap();
tx.send(()).unwrap();
drop(l);
});
assert!(rx.recv_timeout(Duration::from_millis(200)).is_err(), "the second holder did not wait");
drop(first);
rx.recv_timeout(Duration::from_secs(5)).unwrap();
t.join().unwrap();
}
}

View file

@ -0,0 +1,407 @@
//! The checks, written once against any port: the board's, the model's, or the testbench recording.
//!
//! - [`bits`]: the conformance set's `settle` and `chains` cases, which a bit-exact kernel gives
//! exactly, case by case; the host's certification (`member::certify`) runs the same cases.
//! - [`full`]: those; the `cells` cut points as the host computes them, and draws of single cells
//! on the device against the reference's, draw for draw and by frequency; the statistical
//! battery, and the all-at-once control it must catch; the `jobs` requests; random settles up
//! to 64 cells.
//! - [`agree`]: every worked example over many seeds, at its own temperature and at 0, on the board
//! and on the CPU reference, answer for answer.
use crate::engine::Loaded;
use crate::member::{BoardMember, VECTORS, beyond, lowered, plan, run_schedule};
use crate::port::Port;
use crate::regs::*;
use serde_json::{Value, json};
use std::fmt::Write as _;
use wai_quantum::quantum_landscape::conformance::{MUST_TAKE_FIELD_BOUND, battery, chain_cases, run_battery, settle_cases};
use wai_quantum::quantum_landscape::energy::Landscape;
use wai_quantum::quantum_landscape::member::{Certified, Chain, Member, check_chain, check_settle, named};
use wai_quantum::quantum_landscape::program::read;
use wai_quantum::quantum_landscape::run::{Job, run_on};
use wai_quantum::quantum_landscape::sample::{anneal_with, cell, chain_seed, cuts, schedule};
use wai_quantum::quantum_landscape::settle::Workload;
use wai_quantum::quantum_landscape::{EXAMPLES, MAX_JOB_BYTES, job};
#[derive(Default)]
pub struct Report {
pub text: String,
pub failures: Vec<String>,
pub checks: usize,
}
impl Report {
pub fn line(&mut self, s: impl AsRef<str>) {
self.text.push_str(s.as_ref());
self.text.push('\n');
}
pub fn check(&mut self, ok: bool, what: impl Into<String>) {
let w = what.into();
self.checks += 1;
let _ = writeln!(self.text, " [{}] {}", if ok { "PASS" } else { "FAIL" }, w);
if !ok {
self.failures.push(w);
}
}
}
pub struct Bits {
/// Every settle case the engine can hold gave the reference's bits, and every one it cannot
/// failed with a sentence.
pub settle_ok: bool,
pub chains_ok: bool,
/// Cases beyond the engine's capacity, by name (`member::beyond`): the spec does not let a
/// member decline them, so each costs the kernel its certificate.
pub settle_beyond: Vec<String>,
pub chains_beyond: Vec<String>,
pub text: String,
}
fn vectors() -> Value {
serde_json::from_str(VECTORS).expect("the conformance set is JSON")
}
/// The `settle` and `chains` cases on the member, case by case, each answer also held to the job
/// (`check_settle`, `check_chain`): what the host's certification grades.
pub fn bits<P: Port>(m: &BoardMember<P>) -> Result<Bits, String> {
let mut text = String::new();
let may_decline = |w: &Workload| w.field_bound() > MUST_TAKE_FIELD_BOUND;
let (mut s_ok, mut s_n, mut s_declined, mut s_wrong) = (0, 0, 0, 0);
let mut settle_beyond = Vec::new();
for c in settle_cases() {
let w = c.landscape.lower();
if m.takes(&w, false).is_err() && may_decline(&w) {
s_declined += 1;
continue;
}
let want = w.settle(&c.start, c.bound);
let got = m.settle(&w, &c.start, c.bound);
if let Some(why) = beyond(&w, &[]) {
match got {
Err(_) => settle_beyond.push(format!("{} ({why})", c.name)),
Ok(_) => {
s_wrong += 1;
let _ = writeln!(text, " settle {}: beyond the engine ({why}), and it answered anyway", c.name);
}
}
continue;
}
s_n += 1;
match got {
Ok(o) if o == want && check_settle(&o, &w, c.bound).is_ok() => s_ok += 1,
Ok(o) => {
s_wrong += 1;
let _ = writeln!(text, " settle {}: got {:?}, want {:?}", c.name, o, want);
}
Err(e) => {
s_wrong += 1;
let _ = writeln!(text, " settle {}: {e}", c.name);
}
}
}
let (mut c_ok, mut c_n, mut c_declined, mut c_wrong) = (0, 0, 0, 0);
let mut chains_beyond = Vec::new();
for c in chain_cases() {
let w = c.landscape.lower();
let temps = c.temperatures.each();
let chains = c.chains();
if m.takes(&w, temps.iter().any(|&t| t > 0.0)).is_err() && may_decline(&w) {
c_declined += chains.len();
continue;
}
let got = m.chains(&w, &chains, &temps);
if let Some(why) = beyond(&w, &temps) {
match got {
Err(_) => chains_beyond.push(format!("{} ({why})", c.name)),
Ok(_) => {
c_wrong += 1;
let _ = writeln!(text, " chains {}: beyond the engine ({why}), and it answered anyway", c.name);
}
}
continue;
}
c_n += chains.len();
let want: Vec<_> = chains.iter().map(|ch| anneal_with(&w, &ch.start, &temps, ch.seed)).collect();
match got {
Ok(got) => {
for (g, wnt) in got.iter().zip(&want) {
if g == wnt && check_chain(g, &w, &temps).is_ok() {
c_ok += 1;
} else {
c_wrong += 1;
let _ = writeln!(text, " chain {}: got {:?}, want {:?}", c.name, g.state, wnt.state);
}
}
}
Err(e) => {
c_wrong += 1;
let _ = writeln!(text, " chains {}: {e}", c.name);
}
}
}
let _ = writeln!(
text,
"settle cases the engine holds: {s_ok}/{s_n} bit for bit; {} beyond its capacity; {s_declined} declined, as the spec lets a member",
settle_beyond.len()
);
let _ = writeln!(
text,
"chains the engine holds: {c_ok}/{c_n} bit for bit; {} cases beyond its capacity; {c_declined} chains declined, as the spec lets a member",
chains_beyond.len()
);
for b in settle_beyond.iter().chain(&chains_beyond) {
let _ = writeln!(text, " beyond the engine: {b}");
}
Ok(Bits { settle_ok: s_wrong == 0 && s_ok == s_n, chains_ok: c_wrong == 0 && c_ok == c_n, settle_beyond, chains_beyond, text })
}
/// `count` chains of one cell, one sweep each at temperature `t`, chain `r` with the seed
/// `chain_seed(seed, r)`: each final state is one draw of the device's, the first of its stream.
fn cell_draws<P: Port>(m: &BoardMember<P>, l: &Loaded, t: f64, seed: u64, count: u64) -> Result<Vec<i8>, String> {
let segs = plan(l, &[t])?;
let mut e = m.engine.borrow_mut();
e.load(l).map_err(|e| e.0)?;
let mut out = Vec::with_capacity(count as usize);
let mut r0 = 0u64;
while r0 < count {
let r1 = (r0 + 4096).min(count);
let list: Vec<(Vec<i8>, u64)> = (r0..r1).map(|r| (vec![0], chain_seed(seed, r))).collect();
out.extend(run_schedule(&mut e, l, &segs, false, &list, 0, MODE_GIBBS)?.into_iter().map(|s| s[0]));
r0 = r1;
}
Ok(out)
}
/// Every check, at `scale` times the draw counts.
pub fn full<P: Port>(m: &BoardMember<P>, scale: f64) -> Result<Report, String> {
let f = vectors();
let mut rep = Report::default();
let b = bits(m)?;
rep.line("wai.quantum.landscape/1 member tests (quantum-conformance/landscape/vectors.json)");
rep.text.push_str(&b.text);
rep.check(b.settle_ok, "settle kernel: every settle case the engine holds, bit for bit (state, sweeps, ending, accumulates), each held to the job; every one it cannot hold refused");
rep.check(b.chains_ok, "sample kernel: every chain vector the engine holds, bit for bit (the state from the device; sweeps, ending, accumulates and draws by the schedule, as check_chain fixes them), each held to the job; every one it cannot hold refused");
// the cut points the host gives the engine, against the set's
let cells = f["cells"].as_array().unwrap();
let mut bad = 0;
for c in cells {
let t = f64::from_bits(u64::from_str_radix(c["temperature_bits"].as_str().unwrap(), 16).unwrap());
if cuts(c["field"].as_i64().unwrap(), c["cost"].as_i64().unwrap(), 1.0 / t) != (c["cut_a"].as_u64().unwrap(), c["cut_b"].as_u64().unwrap()) {
bad += 1;
}
}
rep.check(bad == 0, format!("cells: {}/{} cut points the host computes for the engine equal the set's, bit for bit", cells.len() - bad, cells.len()));
// one cell's draws on the device: every draw the reference's, and the frequencies the cut points give
let draws = ((100_000.0 * scale) as u64).max(2_000);
for c in cells.iter().step_by(23) {
let (h, cost, t) = (c["field"].as_i64().unwrap(), c["cost"].as_i64().unwrap(), c["temperature"].as_f64().unwrap());
let (a, bb) = (c["cut_a"].as_u64().unwrap() as f64, c["cut_b"].as_u64().unwrap() as f64);
let p = [a / TWO53 as f64, (bb - a) / TWO53 as f64, (TWO53 as f64 - bb) / TWO53 as f64];
let l = Loaded { n: 1, rows: vec![vec![]], fields: vec![h], cost };
let seed = 0x5EED ^ (h as u64).wrapping_mul(31) ^ (cost as u64).wrapping_mul(97);
let got = cell_draws(m, &l, t, seed, draws)?;
let mut cnt = [0u64; 3];
let mut same = 0u64;
for (r, &s) in got.iter().enumerate() {
cnt[(s + 1) as usize] += 1;
if s == cell(h, cost, 1.0 / t, chain_seed(seed, r as u64), 1) {
same += 1;
}
}
let n = draws as f64;
let z: Vec<f64> = (0..3)
.map(|i| {
let sd = (n * p[i] * (1.0 - p[i])).sqrt();
let d = cnt[i] as f64 - n * p[i];
if sd > 0.0 { d / sd } else if d.abs() < 1.0 { 0.0 } else { f64::INFINITY }
})
.collect();
rep.check(
z.iter().all(|x| x.abs() <= 4.0) && same == draws,
format!("cell h={h} c={cost} T={t}: {draws} draws, counts {cnt:?}, z {:+.2} {:+.2} {:+.2} (limit 4); {same}/{draws} equal to the reference's draw", z[0], z[1], z[2]),
);
}
// the statistical battery, and the all-at-once control it is built to catch
let held: Vec<bool> = battery().iter().map(|c| beyond(&c.landscape.lower(), &c.temperatures).is_none()).collect();
for (r, holds) in run_battery(m).into_iter().zip(&held) {
if !holds && !r.declined {
let c = battery().into_iter().find(|c| c.name == r.name).expect("the battery's own case");
let why = beyond(&c.landscape.lower(), &c.temperatures).unwrap_or_default();
rep.check(!r.passed, format!("battery {}: beyond the engine ({why}), refused", r.name));
} else if r.declined {
rep.check(r.passed, format!("battery {}: declined, as the spec lets a member", r.name));
} else {
rep.check(r.passed, format!("battery {}: chi-square {:.1} on {} degrees of freedom (critical {:.1})", r.name, r.chi_square, r.degrees, r.critical));
}
}
m.jacobi_control.set(true);
let control = run_battery(m);
m.jacobi_control.set(false);
let caught: Vec<&str> = control.iter().zip(&held).filter(|(r, h)| **h && !r.passed).map(|(r, _)| r.name).collect();
rep.check(!caught.is_empty(), format!("the all-at-once control (every cell moved together) fails the battery on {caught:?}"));
let (mut ok, mut n) = (0, 0);
let mut first = String::new();
let c = named(m)?;
for j in f["jobs"].as_array().unwrap() {
n += 1;
let got = match job::call_certified(&c, "run", &j["request"], MAX_JOB_BYTES) {
Ok(a) => json!({ "state": a["state"], "energy": a["energy"], "verdict": a["verdict"], "mode": a["mode"], "restarts": a["restarts"], "sweeps": a["sweeps"], "accumulates": a["work"]["accumulates"], "draws": a["work"]["draws"], "replay": a["replay"] }),
Err(r) => json!({ "refused": r.plain(), "line": r.line }),
};
if got == j["expect"] {
ok += 1;
} else if first.is_empty() {
first = format!("; first difference: {}: got {got}, want {}", j["name"], j["expect"]);
}
}
rep.check(ok == n, format!("jobs: {ok}/{n} requests answer as the set says (state, energy, verdict, mode, restarts, sweeps, accumulates, draws, replay digest, or the refusal){first}"));
rep.text.push_str(&random_settles(m, ((2000.0 * scale) as usize).max(50), 0xA11CE)?.text);
Ok(rep)
}
/// Random workloads up to 64 cells, with fields and a cost, against the reference's settle.
pub fn random_settles<P: Port>(m: &BoardMember<P>, count: usize, seed: u64) -> Result<Report, String> {
let mut rep = Report::default();
let mut z = seed;
let mut next = || {
z = z.wrapping_add(GOLDEN);
let mut r = z;
r = (r ^ (r >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
r = (r ^ (r >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
r ^ (r >> 31)
};
let mut bad = 0;
for _ in 0..count {
let n = 2 + (next() % 63) as usize;
let dens = 5 + next() % 90;
let mut l = Landscape::new(n);
for i in 0..n {
for j in (i + 1)..n {
if next() % 100 < dens {
l.couple(i, j, if next() & 1 == 0 { 1 } else { -1 });
}
}
l.set_field(i, (next() % 11) as i64 - 5);
}
l.set_cost((next() % 4) as i64);
let start: Vec<i8> = (0..n).map(|_| (next() % 3) as i8 - 1).collect();
let bound = 1 + (next() % 80) as u32;
let w = l.lower();
if m.settle(&w, &start, bound)? != w.settle(&start, bound) {
bad += 1;
}
}
rep.check(bad == 0, format!("{}/{count} random workloads (2-64 cells, fields -5..5, cost 0-3) settle exactly as the reference does", count - bad));
Ok(rep)
}
/// Every field read back from the engine at random states, against W s + b.
pub fn fields<P: Port>(m: &BoardMember<P>, states: usize) -> Result<Report, String> {
let mut rep = Report::default();
let mut l = Landscape::new(64);
let mut z = 0xF1E1Du64;
let mut next = || {
z ^= z << 13;
z ^= z >> 7;
z ^= z << 17;
z
};
for i in 0..64 {
for j in (i + 1)..64 {
let r = next() % 3;
if r != 0 {
l.couple(i, j, if r == 1 { 1 } else { -1 });
}
}
l.set_field(i, (next() % (2 * FIELD_MAX as u64 + 1)) as i64 - FIELD_MAX);
}
let w: Workload = l.lower();
let lw = lowered(&w);
let mut e = m.engine.borrow_mut();
e.load(&lw).map_err(|e| e.0)?;
let mut bad = 0;
for _ in 0..states {
let s: Vec<i8> = (0..64).map(|_| (next() % 3) as i8 - 1).collect();
e.load_state(&s).map_err(|e| e.0)?;
for i in 0..64 {
if e.field(i).map_err(|e| e.0)? != w.field(&s, i) {
bad += 1;
}
}
}
rep.check(bad == 0, format!("{}/{} fields read back equal W s + b (dense W, fields across the engine's ±{FIELD_MAX})", states * 64 - bad, states * 64));
Ok(rep)
}
/// Every worked example, `seeds` seeds, at its own temperature and at 0: the board's answer
/// against the CPU reference's, field for field (the member's name and label aside).
pub fn agree<P: Port>(m: &BoardMember<P>, seeds: u64) -> Result<Report, String> {
let mut rep = Report::default();
rep.line(format!("the worked examples on the board and on the CPU reference, {seeds} seeds each"));
let key = |a: &Value| json!([a["state"], a["energy"], a["verdict"], a["work"], a["replay"], a["truth"], a["reached"], a["checks"], a["mode"], a["temperature"]]);
for (name, text) in EXAMPLES {
let p = match read(text) {
Ok(p) => p,
Err(r) => {
rep.check(true, format!("{name}: refused by the reader before any member runs: {}", r.plain()));
continue;
}
};
let job = Job::new(p);
let board = named(m)?;
let reference = Certified::reference();
for temp in [None, Some(0.0)] {
let (mut same, mut n) = (0u64, 0u64);
let mut first = String::new();
for s in 1..=seeds {
n += 1;
let a = run_on(&board, &job, s, temp).map_err(|r| r.plain())?;
let b = run_on(&reference, &job, s, temp).map_err(|r| r.plain())?;
if key(&a) == key(&b) {
same += 1;
} else if first.is_empty() {
first = format!("; first difference at seed {s}: board {} vs reference {}", a["state"], b["state"]);
}
}
let t = match temp { None => "its own temperature".to_string(), Some(t) => format!("temperature {t}") };
rep.check(same == n, format!("{name} at {t}: {same}/{n} answers identical{first}"));
}
}
Ok(rep)
}
/// A short session for the RTL testbench: both kernels through the member, a schedule longer than
/// the table, the all-at-once mode, a field outside the window, table and draw read-back.
pub fn tb_session<P: Port>(m: &BoardMember<P>) -> Result<(), String> {
bits(m)?;
fields(m, 2)?;
let mut l = Landscape::new(6);
for i in 0..5 {
l.couple(i, i + 1, if i % 2 == 0 { 1 } else { -1 });
}
l.set_field(0, 3).set_field(5, 500).set_cost(1);
let w = l.lower();
let start = [1i8, 0, -1, 0, 1, -1];
m.chains(&w, &[Chain { start: start.to_vec(), seed: 77 }], &schedule(3.0, 140))?;
m.chains(&w, &[Chain { start: start.to_vec(), seed: 78 }, Chain { start: vec![0; 6], seed: 79 }], &schedule(0.0, 9))?;
// a schedule with a sweep at 0 between sweeps above 0
m.chains(&w, &[Chain { start: start.to_vec(), seed: 80 }], &[2.0, 1.0, 0.0, 0.0, 1.5, 0.5, 0.0])?;
let lw = lowered(&w);
let seg = plan(&lw, &[1.5, 0.75])?.remove(0);
let mut e = m.engine.borrow_mut();
e.load_table(seg.win_lo, seg.wbits, seg.first, &seg.levels).map_err(|e| e.0)?;
e.chain(5).map_err(|e| e.0)?;
e.run_sweeps(MODE_JACOBI, &start, 2, 1, false).map_err(|e| e.0)?;
e.next_draw().map_err(|e| e.0)?;
e.table_entry(((1 << seg.wbits) + (seg.first - seg.win_lo)) as usize).map_err(|e| e.0)?;
for c in [UPTIME, CYCLES, UPDATES, JOBS] {
e.counter(c).map_err(|e| e.0)?;
}
Ok(())
}

View file

@ -0,0 +1,168 @@
//! The board's port: the PS GPIO controller's EMIO banks, reached through /dev/mem. See
//! `rtl/landscape_port.v` for the PL side and why this is not AXI.
//!
//! Bank 3 (EMIO 31:0) wdata out; bank 4 (EMIO 63:32) rdata in; bank 5 (EMIO 94:64): addr 15:0,
//! req 16, we 17 out, ack 18, busy 19, done 20 in. The directions this sets are restored on drop.
use crate::port::{Error, Port, err};
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::time::{Duration, Instant};
unsafe extern "C" {
fn mmap(addr: *mut u8, len: usize, prot: i32, flags: i32, fd: i32, off: i64) -> *mut u8;
fn munmap(addr: *mut u8, len: usize) -> i32;
}
const PROT_RW: i32 = 1 | 2;
const MAP_SHARED: i32 = 1;
const O_SYNC: i32 = 0o4010000;
pub struct Mem {
p: *mut u8,
len: usize,
}
impl Mem {
pub fn map(base: u64, len: usize) -> Result<Mem, Error> {
let f = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(O_SYNC)
.open("/dev/mem")
.map_err(|e| Error(format!("/dev/mem: {e} (run as root)")))?;
let p = unsafe { mmap(std::ptr::null_mut(), len, PROT_RW, MAP_SHARED, f.as_raw_fd(), base as i64) };
if p as isize == -1 || p.is_null() {
return err(format!("mmap of {base:#x} failed"));
}
Ok(Mem { p, len })
}
#[inline]
pub fn rd(&self, off: usize) -> u32 {
unsafe { std::ptr::read_volatile(self.p.add(off) as *const u32) }
}
#[inline]
pub fn wr(&self, off: usize, v: u32) {
unsafe { std::ptr::write_volatile(self.p.add(off) as *mut u32, v) }
}
}
impl Drop for Mem {
fn drop(&mut self) {
unsafe {
munmap(self.p, self.len);
}
}
}
const GPIO_BASE: u64 = 0xFF0A_0000;
const DATA_3: usize = 0x04C;
const DATA_5: usize = 0x054;
const DATA_4_RO: usize = 0x070;
const DATA_5_RO: usize = 0x074;
const DIRM_3: usize = 0x2C4;
const OEN_3: usize = 0x2C8;
const DIRM_4: usize = 0x304;
const OEN_4: usize = 0x308;
const DIRM_5: usize = 0x344;
const OEN_5: usize = 0x348;
const OUT5: u32 = 0x0003_FFFF;
const ACK: u32 = 1 << 18;
const BUSY: u32 = 1 << 19;
pub struct EmioPort {
m: Mem,
req: u32,
saved: [u32; 7],
pub txns: u64,
}
impl EmioPort {
pub fn name(&self) -> &'static str {
"EMIO"
}
pub fn open() -> Result<EmioPort, Error> {
let m = Mem::map(GPIO_BASE, 0x1000)?;
let saved = [m.rd(DIRM_3), m.rd(OEN_3), m.rd(DIRM_4), m.rd(OEN_4), m.rd(DIRM_5), m.rd(OEN_5), m.rd(DATA_5)];
m.wr(DIRM_3, 0xFFFF_FFFF);
m.wr(OEN_3, 0xFFFF_FFFF);
m.wr(DIRM_4, 0);
m.wr(OEN_4, 0);
m.wr(DIRM_5, OUT5);
m.wr(OEN_5, OUT5);
let req = (m.rd(DATA_5) >> 16) & 1;
Ok(EmioPort { m, req, saved, txns: 0 })
}
fn txn(&mut self, we: bool, addr: u16, wdata: u32) -> Result<u32, Error> {
let ack0 = self.m.rd(DATA_5_RO) & ACK;
if we {
self.m.wr(DATA_3, wdata);
}
self.req ^= 1;
self.m.wr(DATA_5, ((we as u32) << 17) | (self.req << 16) | addr as u32);
let t = Instant::now();
let mut spins = 0u32;
while self.m.rd(DATA_5_RO) & ACK == ack0 {
spins += 1;
if spins > 64 && t.elapsed() > Duration::from_millis(50) {
return err(format!(
"no ack from the PL for {} {addr:#06x}: the landscape engine bitstream is not loaded, its clock is stopped, or another session replaced it",
if we { "write" } else { "read" }
));
}
}
self.txns += 1;
Ok(if we { 0 } else { self.m.rd(DATA_4_RO) })
}
/// Puts the three EMIO banks back to inputs with outputs disabled, the state this board boots in.
/// For the end of a session, or after a process that held the port was killed before its drop.
pub fn release_to_boot_state() -> Result<(), Error> {
let m = Mem::map(GPIO_BASE, 0x1000)?;
for (d, o) in [(DIRM_3, OEN_3), (DIRM_4, OEN_4), (DIRM_5, OEN_5)] {
m.wr(o, 0);
m.wr(d, 0);
}
m.wr(DATA_3, 0);
m.wr(DATA_5, 0);
Ok(())
}
pub fn busy(&self) -> bool {
self.m.rd(DATA_5_RO) & BUSY != 0
}
}
impl Port for EmioPort {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error> {
self.txn(true, addr, val).map(|_| ())
}
fn read(&mut self, addr: u16) -> Result<u32, Error> {
self.txn(false, addr, 0)
}
fn wait_idle(&mut self, timeout: Duration) -> Result<(), Error> {
let t = Instant::now();
loop {
if !self.busy() {
return Ok(());
}
if t.elapsed() > timeout {
return err("the engine stayed busy past the timeout");
}
std::hint::spin_loop();
}
}
}
impl Drop for EmioPort {
fn drop(&mut self) {
let s = self.saved;
self.m.wr(DATA_5, s[6]);
self.m.wr(DIRM_3, s[0]);
self.m.wr(OEN_3, s[1]);
self.m.wr(DIRM_4, s[2]);
self.m.wr(OEN_4, s[3]);
self.m.wr(DIRM_5, s[4]);
self.m.wr(OEN_5, s[5]);
}
}

View file

@ -0,0 +1,535 @@
//! The driver: put a workload, a cut-point table and a draw counter into the engine, run it, read
//! it back. Generic over the port, so the same code drives the board, the model and the testbench.
use crate::port::{Error, Port, err};
use crate::regs::*;
use std::time::Duration;
pub fn encode(s: &[i8]) -> [u32; 4] {
let mut w = [0u32; 4];
for (j, &t) in s.iter().enumerate() {
let v: u32 = match t {
1 => 0b01,
-1 => 0b10,
_ => 0,
};
let b = 2 * j;
w[b >> 5] |= v << (b & 31);
}
w
}
pub fn decode(w: &[u32; 4], n: usize) -> Result<Vec<i8>, Error> {
(0..n)
.map(|j| {
let b = 2 * j;
match (w[b >> 5] >> (b & 31)) & 3 {
0b00 => Ok(0),
0b01 => Ok(1),
0b10 => Ok(-1),
_ => err(format!("cell {j} reads back as the illegal pair (1,1)")),
}
})
.collect()
}
#[derive(Clone, Debug, PartialEq)]
pub struct T0Run {
pub state: Vec<i8>,
pub steps: u32,
pub term: u32,
pub cycles: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SweepRun {
pub state: Vec<i8>,
pub sweeps: u32,
pub cycles: u64,
pub updates: u64,
}
/// A workload as the engine holds it: rows of (column, ±1), one field a cell, the cost.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Loaded {
pub n: usize,
pub rows: Vec<Vec<(usize, i8)>>,
pub fields: Vec<i64>,
pub cost: i64,
}
impl Loaded {
pub fn nonzeros(&self) -> u64 {
self.rows.iter().map(|r| r.len() as u64).sum()
}
pub fn key(&self) -> [u8; 32] {
let mut h = blake3::Hasher::new();
h.update(&(self.n as u64).to_le_bytes());
for r in &self.rows {
h.update(&(r.len() as u64).to_le_bytes());
for &(j, v) in r {
h.update(&(j as u64).to_le_bytes());
h.update(&[v as u8]);
}
}
for f in &self.fields {
h.update(&f.to_le_bytes());
}
h.update(&self.cost.to_le_bytes());
*h.finalize().as_bytes()
}
}
/// One chain of a batch: where it ended, after how many sweeps (or steps, at temperature 0), and how.
#[derive(Clone, Debug, PartialEq)]
pub struct BatchRun {
pub state: Vec<i8>,
pub steps: u32,
pub term: u32,
}
pub struct Engine<P: Port> {
pub port: P,
pub timeout: Duration,
n: usize,
loaded: Option<[u8; 32]>,
table: Option<[u8; 32]>,
/// The uptime counter when last read, and what BOUND last held: see [`Engine::alive`].
uptime: u64,
bound: u32,
}
/// What BOUND holds from [`Engine::open`] until the first job: any value but the 0 a reload leaves.
const BOUND_AT_OPEN: u32 = 0x4C41_4E44;
/// The uptime counter is 48 bits. It may wrap inside this much of its top, and not be a reload.
const UPTIME_TOP: u64 = 1 << 48;
const UPTIME_WRAP: u64 = 1 << 36;
impl<P: Port> Engine<P> {
/// Opens the engine. Refuses anything whose ID and version are not this design's: on a shared
/// board that is how a foreign bitstream is caught before it is driven.
pub fn open(mut port: P) -> Result<Self, Error> {
let id = port.read(ID)?;
if id != ID_VALUE {
return err(format!(
"the programmable logic is not running the landscape engine (ID {id:#010x}, want {ID_VALUE:#010x}). \
Load the bitstream first; if it was loaded, another session has replaced it."
));
}
let v = port.read(VERSION)?;
if v != VERSION_VALUE {
return err(format!("engine version {v:#010x}, this host speaks {VERSION_VALUE:#010x}"));
}
port.write(BOUND, BOUND_AT_OPEN)?;
let mut e = Engine { port, timeout: Duration::from_secs(60), n: 0, loaded: None, table: None, uptime: 0, bound: BOUND_AT_OPEN };
e.uptime = e.counter(UPTIME)?;
Ok(e)
}
/// Whether the engine is still the one this session loaded. A reload of the same design passes
/// the link's signature and the engine's ID, and leaves W, the fields, the cost and the table
/// empty while the keys here still say they are loaded, so a job would run on a wiped engine and
/// give states that look valid. A reload shows two ways: the uptime counter starts again, and
/// BOUND goes back to 0. Checked before anything is taken as loaded and after every job: a
/// result read across a reload is refused, not kept. After a refusal nothing is taken as loaded.
pub fn alive(&mut self) -> Result<(), Error> {
let u = self.counter(UPTIME)?;
let b = self.port.read(BOUND)?;
let wrapped = self.uptime >= UPTIME_TOP - UPTIME_WRAP && u < UPTIME_WRAP;
if (u < self.uptime && !wrapped) || (self.bound != 0 && b != self.bound) {
let was = (self.uptime, self.bound);
self.loaded = None;
self.table = None;
self.n = 0;
self.uptime = u;
self.bound = b;
return err(format!(
"the engine was reloaded under this session (uptime {} -> {u}, BOUND {:#x} -> {b:#x}): its workload and table are gone, and this job's result is not kept",
was.0, was.1
));
}
self.uptime = u;
Ok(())
}
fn set_bound(&mut self, v: u32) -> Result<(), Error> {
self.port.write(BOUND, v)?;
self.bound = v;
Ok(())
}
pub fn n(&self) -> usize {
self.n
}
pub fn counter(&mut self, lo: u16) -> Result<u64, Error> {
let l = self.port.read(lo)? as u64;
let h = self.port.read(lo + 1)? as u64;
Ok((h << 32) | l)
}
/// Writes W, the fields and the cost, unless the same workload is already there.
pub fn load(&mut self, w: &Loaded) -> Result<(), Error> {
if w.n == 0 || w.n > CELLS {
return err(format!("{} variables; this engine holds 1 to {CELLS}", w.n));
}
if let Some(i) = w.fields.iter().position(|f| f.abs() > FIELD_MAX) {
return err(format!("variable {i} has the field {}, beyond the engine's ±{FIELD_MAX}", w.fields[i]));
}
if !(0..=COST_MAX).contains(&w.cost) {
return err(format!("the cost {} is beyond the engine's 0 to {COST_MAX}", w.cost));
}
self.alive()?;
let key = w.key();
if self.loaded == Some(key) {
self.n = w.n;
return Ok(());
}
self.loaded = None;
let mut list = vec![(CONTROL, CTL_CLEARW)];
for (i, row) in w.rows.iter().enumerate() {
let mut cells = vec![0i8; CELLS];
for &(j, v) in row {
if !(v == 1 || v == -1) || j >= w.n {
return err(format!("row {i}: a coupling must be ±1 to a variable below {}", w.n));
}
cells[j] = v;
}
for (k, &word) in encode(&cells).iter().enumerate() {
if word != 0 {
list.push((W + (i as u16) * 4 + k as u16, word));
}
}
if w.fields[i] != 0 {
list.push((FIELDS + i as u16, (w.fields[i] as u32) & 0xF_FFFF));
}
}
list.push((N_M1, (w.n - 1) as u32));
list.push((COST, w.cost as u32));
self.port.bulk(&list)?;
self.n = w.n;
self.loaded = Some(key);
Ok(())
}
/// Writes the window and its cut points: `levels[l][k]` is the pair for field `first + k` at
/// level `l`. Skipped when the same table is already there.
pub fn load_table(&mut self, win_lo: i64, wbits: u32, first: i64, levels: &[Vec<(u64, u64)>]) -> Result<(), Error> {
if wbits > MAX_WBITS || levels.is_empty() || levels.len() > (TABLE >> wbits).min(LEVELS) {
return err(format!("{} levels of 2^{wbits} fields do not fit the table", levels.len()));
}
let mut h = blake3::Hasher::new();
h.update(&win_lo.to_le_bytes());
h.update(&wbits.to_le_bytes());
h.update(&first.to_le_bytes());
for l in levels {
h.update(&(l.len() as u64).to_le_bytes());
for &(a, b) in l {
h.update(&a.to_le_bytes());
h.update(&b.to_le_bytes());
}
}
let key = *h.finalize().as_bytes();
self.alive()?;
let mut list = vec![(WIN_LO, (win_lo as u32) & 0x3F_FFFF), (WBITS, wbits), (LEVELS_M1, (levels.len() - 1) as u32)];
if self.table == Some(key) {
return self.port.bulk(&list);
}
self.table = None;
for (l, entries) in levels.iter().enumerate() {
if entries.is_empty() {
continue;
}
let off = first - win_lo;
if off < 0 || off as usize + entries.len() > (1usize << wbits) {
return err("table entries outside the window");
}
list.push((TBL_ADDR, ((l << wbits) + off as usize) as u32));
for &(a, b) in entries {
list.extend([(TBL_A_LO, a as u32), (TBL_A_HI, (a >> 32) as u32), (TBL_B_LO, b as u32), (TBL_B_HI, (b >> 32) as u32)]);
}
}
self.port.bulk(&list)?;
self.table = Some(key);
Ok(())
}
/// Reads back one entry of the table.
pub fn table_entry(&mut self, addr: usize) -> Result<(u64, u64), Error> {
self.port.write(TBL_ADDR, addr as u32)?;
let a = self.port.read(TBL_A_LO)? as u64 | ((self.port.read(TBL_A_HI)? as u64) << 32);
let b = self.port.read(TBL_B_LO)? as u64 | ((self.port.read(TBL_B_HI)? as u64) << 32);
Ok((a, b))
}
/// The draw counter's start for a chain: its seed plus GOLDEN, so the first draw is out(chain, 1).
pub fn chain(&mut self, chain_seed: u64) -> Result<(), Error> {
let x0 = chain_seed.wrapping_add(GOLDEN);
self.port.write(X0_LO, x0 as u32)?;
self.port.write(X0_HI, (x0 >> 32) as u32)
}
/// The draw the next cell would take, after a sampling job.
pub fn next_draw(&mut self) -> Result<u64, Error> {
let lo = self.port.read(DRAW_LO)? as u64;
let hi = self.port.read(DRAW_HI)? as u64;
Ok((hi << 32) | lo)
}
pub fn set_boundary(&mut self, s: &[i8]) -> Result<(), Error> {
if s.len() != self.n {
return err(format!("a start of {} cells for {} variables", s.len(), self.n));
}
for (k, &word) in encode(s).iter().enumerate() {
self.port.write(BOUNDARY + k as u16, word)?;
}
Ok(())
}
pub fn read_state(&mut self) -> Result<Vec<i8>, Error> {
let need = (2 * self.n).div_ceil(32);
let mut w = [0u32; 4];
for (k, word) in w.iter_mut().enumerate().take(need) {
*word = self.port.read(STATE + k as u16)?;
}
decode(&w, self.n)
}
/// The field the engine computes for cell `i` at its current state.
pub fn field(&mut self, i: usize) -> Result<i64, Error> {
self.port.write(SEL, i as u32)?;
Ok(self.port.read(FIELD)? as i32 as i64)
}
pub fn load_state(&mut self, s: &[i8]) -> Result<(), Error> {
self.set_boundary(s)?;
self.port.write(CONTROL, CTL_LOADSTATE)
}
fn finish(&mut self) -> Result<(), Error> {
self.port.wait_idle(self.timeout)?;
self.alive()?;
let st = self.port.read(STATUS)?;
if st & 2 == 0 {
return err(format!("the job stopped without finishing (status {st:#x})"));
}
Ok(())
}
/// The settle kernel: every cell at once, to a fixed point, a two-cycle or the bound.
pub fn run_t0(&mut self, start: &[i8], bound: u32) -> Result<T0Run, Error> {
self.port.write(MODE, MODE_T0)?;
self.set_bound(bound)?;
self.set_boundary(start)?;
self.port.write(CONTROL, CTL_CLRCNT)?;
self.port.write(CONTROL, CTL_START)?;
self.finish()?;
let term = (self.port.read(STATUS)? >> 2) & 3;
let steps = self.port.read(STEPS)?;
let cycles = self.counter(CYCLES)?;
Ok(T0Run { state: self.read_state()?, steps, term, cycles })
}
/// `sweeps` sweeps of `mode` from `start` with the draws of the counter set by [`Self::chain`],
/// the level stepping every `spl` sweeps, then one synchronous T=0 sweep if `final_t0`.
pub fn run_sweeps(&mut self, mode: u32, start: &[i8], sweeps: u32, spl: u32, final_t0: bool) -> Result<SweepRun, Error> {
self.prepare(mode, start, sweeps, spl, final_t0)?;
self.port.write(CONTROL, CTL_CLRCNT)?;
self.port.write(CONTROL, CTL_START)?;
self.finish()?;
self.sweep_result()
}
/// More sweeps from the current state, the level back to 0 and the draws continuing: with a
/// fresh table this is how a schedule longer than the table runs.
pub fn cont_sweeps(&mut self, mode: u32, sweeps: u32, spl: u32, final_t0: bool) -> Result<SweepRun, Error> {
if sweeps == 0 {
return err("CONT needs at least one sweep");
}
self.port.write(MODE, mode | if final_t0 { MODE_FINAL_T0 } else { 0 })?;
self.set_bound(sweeps)?;
self.port.write(SPL, spl.max(1))?;
self.port.write(CONTROL, CTL_CLRCNT)?;
self.port.write(CONTROL, CTL_CONT)?;
self.finish()?;
self.sweep_result()
}
fn sweep_result(&mut self) -> Result<SweepRun, Error> {
let sweeps = self.port.read(STEPS)?;
let cycles = self.counter(CYCLES)?;
let updates = self.counter(UPDATES)?;
Ok(SweepRun { state: self.read_state()?, sweeps, cycles, updates })
}
pub fn prepare(&mut self, mode: u32, start: &[i8], sweeps: u32, spl: u32, final_t0: bool) -> Result<(), Error> {
self.port.write(MODE, mode | if final_t0 { MODE_FINAL_T0 } else { 0 })?;
self.set_bound(sweeps)?;
self.port.write(SPL, spl.max(1))?;
self.set_boundary(start)
}
/// Repeat mode, for power: the job restarts from its start every time it ends, until
/// [`Self::halt`]. A sampling job reloads its counter, so every repeat is the same chain.
pub fn run_repeat(&mut self, mode: u32, bound: u32, spl: u32, final_t0: bool) -> Result<(), Error> {
self.port.write(MODE, mode | MODE_REPEAT | if final_t0 { MODE_FINAL_T0 } else { 0 })?;
self.set_bound(bound)?;
self.port.write(SPL, spl.max(1))?;
self.port.write(CONTROL, CTL_CLRCNT)?;
self.port.write(CONTROL, CTL_START)
}
/// A run of chains on the job already set (mode, bound, sweeps per level, table, workload):
/// for each, its start and the draw counter's start, `x0`. On the AXI link the chains run in
/// fabric, one after another, with nothing from the processor between them.
pub fn run_batch(&mut self, mode: u32, bound: u32, spl: u32, final_t0: bool, chains: &[(Vec<i8>, u64)]) -> Result<Vec<BatchRun>, Error> {
self.alive()?;
self.port.bulk(&[(MODE, mode | if final_t0 { MODE_FINAL_T0 } else { 0 }), (BOUND, bound), (SPL, spl.max(1))])?;
self.bound = bound;
let mut words = Vec::with_capacity(chains.len());
for (start, x0) in chains {
if start.len() != self.n {
return err(format!("a start of {} cells for {} variables", start.len(), self.n));
}
let b = encode(start);
words.push([b[0], b[1], b[2], b[3], *x0 as u32, (*x0 >> 32) as u32]);
}
let results = self.port.batch(&words, self.timeout)?;
self.alive()?;
if results.len() != words.len() {
return err(format!("{} results for a batch of {} chains", results.len(), words.len()));
}
let mut out = Vec::with_capacity(chains.len());
for r in results {
if r[5] & 2 == 0 {
return err(format!("a chain of the batch stopped without finishing (status {:#x})", r[5]));
}
out.push(BatchRun { state: decode(&[r[0], r[1], r[2], r[3]], self.n)?, steps: r[4], term: (r[5] >> 2) & 3 });
}
Ok(out)
}
pub fn halt(&mut self) -> Result<(), Error> {
self.port.write(CONTROL, CTL_STOP)?;
let m = self.port.read(MODE)?;
self.port.write(MODE, m & !MODE_REPEAT)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::ModelPort;
use crate::port::{ChainWords, ResultWords};
fn workload() -> Loaded {
Loaded { n: 3, rows: vec![vec![(1, 1)], vec![(0, 1), (2, -1)], vec![(1, -1)]], fields: vec![2, 0, -1], cost: 1 }
}
/// The same design loaded again, as a port sees it: every register back at its reset value.
fn reload(e: &mut Engine<ModelPort>) {
e.port = ModelPort::new();
}
#[test]
fn a_reload_between_jobs_stops_the_next_job() {
let w = workload();
let mut e = Engine::open(ModelPort::new()).unwrap();
e.load(&w).unwrap();
let good = e.run_t0(&[1, 0, -1], 20).unwrap();
reload(&mut e);
let refused = e.load(&w).expect_err("a cached workload on a wiped engine");
assert!(refused.0.contains("reloaded"), "{}", refused.0);
// nothing is taken as loaded now: the workload goes back in, and the job gives the same bits
e.load(&w).unwrap();
assert_eq!(e.run_t0(&[1, 0, -1], 20).unwrap().state, good.state);
}
#[test]
fn a_reload_shows_by_bound_where_uptime_has_passed_its_old_value() {
let w = workload();
let mut e = Engine::open(ModelPort::new()).unwrap();
e.load(&w).unwrap();
e.run_t0(&[1, 0, -1], 20).unwrap();
reload(&mut e);
for _ in 0..1000 {
e.port.read(ID).unwrap(); // the fresh engine's uptime counts past the old one's
}
assert!(e.load(&w).is_err());
}
#[test]
fn a_table_is_not_taken_as_loaded_across_a_reload() {
let levels = vec![vec![(1u64, 2u64); 4]];
let mut e = Engine::open(ModelPort::new()).unwrap();
e.load(&workload()).unwrap();
e.load_table(0, 2, 0, &levels).unwrap();
reload(&mut e);
assert!(e.load_table(0, 2, 0, &levels).is_err());
}
/// A port whose engine is reloaded while a job runs.
struct ReloadedMidJob(ModelPort);
impl Port for ReloadedMidJob {
fn write(&mut self, a: u16, v: u32) -> Result<(), Error> {
self.0.write(a, v)?;
if a == CONTROL && v & CTL_START != 0 {
self.0 = ModelPort::new();
}
Ok(())
}
fn read(&mut self, a: u16) -> Result<u32, Error> {
self.0.read(a)
}
fn wait_idle(&mut self, t: Duration) -> Result<(), Error> {
self.0.wait_idle(t)
}
}
#[test]
fn a_result_read_across_a_reload_is_refused() {
let mut e = Engine::open(ReloadedMidJob(ModelPort::new())).unwrap();
e.load(&workload()).unwrap();
assert!(e.run_t0(&[1, 0, -1], 20).is_err());
let mut e = Engine::open(ReloadedMidJob(ModelPort::new())).unwrap();
e.load(&workload()).unwrap();
assert!(e.run_batch(MODE_T0, 20, 1, false, &[(vec![1, 0, -1], 0)]).is_err());
}
#[test]
fn the_uptime_counter_wrapping_is_not_a_reload() {
let mut e = Engine::open(ModelPort::new()).unwrap();
e.uptime = UPTIME_TOP - 5;
e.alive().unwrap();
e.uptime = UPTIME_TOP / 2;
assert!(e.alive().is_err());
}
/// A port that loses the last result of every batch.
struct ShortBatch(ModelPort);
impl Port for ShortBatch {
fn write(&mut self, a: u16, v: u32) -> Result<(), Error> {
self.0.write(a, v)
}
fn read(&mut self, a: u16) -> Result<u32, Error> {
self.0.read(a)
}
fn wait_idle(&mut self, t: Duration) -> Result<(), Error> {
self.0.wait_idle(t)
}
fn batch(&mut self, chains: &[ChainWords], t: Duration) -> Result<Vec<ResultWords>, Error> {
let mut out = crate::port::batch_plain(self, chains, t)?;
out.pop();
Ok(out)
}
}
#[test]
fn a_short_batch_is_refused() {
let mut e = Engine::open(ShortBatch(ModelPort::new())).unwrap();
e.load(&workload()).unwrap();
let chains = vec![(vec![1, 0, -1], 0), (vec![0, 0, 0], 0)];
let refused = e.run_batch(MODE_T0, 20, 1, false, &chains).expect_err("a batch that came back short");
assert!(refused.0.contains("results for a batch"), "{}", refused.0);
}
}

View file

@ -0,0 +1,492 @@
//! Joint statistics against exact enumeration.
//!
//! Per-cell frequencies can all be right while the joint law is wrong: a sampler that drew each cell
//! from its correct marginal, independently, would pass every per-cell test. So the sample kernel is
//! also held to the exact law of the whole state. For a workload of `n` cells (3^n states, `n` up to
//! about 10) the law of a chain's final state is computed by enumeration: a vector over every state,
//! pushed through each cell update of each sweep with the update's three probabilities taken from
//! the reference's own cut points (a draw `u` of 53 bits gives −1 below `a`, 0 below `b`, +1 above,
//! so the probabilities are `a/2^53`, `(b−a)/2^53` and `1 − b/2^53`), then through the synchronous
//! sweep at temperature 0 where the schedule ends with one. Nothing is sampled and nothing
//! approximated beyond the f64 sums.
//!
//! Many chains, each with its own seed, then give a histogram of final states, which is held to the
//! exact law by Pearson's chi-square and the G-test over every state (states with fewer than five
//! expected chains pooled), and by every cell's marginal and every pair of cells' joint law (z
//! scores, Bonferroni over all of them). The same histogram is also held to the law that has the
//! exact marginals but independent cells: that law must be rejected, which shows the test sees
//! joint structure that per-cell statistics cannot.
use crate::engine::Engine;
use crate::member::{lowered, plan, run_schedule};
use crate::port::Port;
use std::collections::HashMap;
use std::fmt::Write as _;
use wai_quantum::quantum_landscape::energy::Landscape;
use wai_quantum::quantum_landscape::sample::{anneal, chain_seed, cuts, out, pick, temperature};
use wai_quantum::quantum_landscape::settle::Workload;
const TWO53: f64 = 9_007_199_254_740_992.0;
/// The state's index: cell 0 is the least significant base-3 digit, −1, 0, +1 as 0, 1, 2.
pub fn index(s: &[i8]) -> usize {
s.iter().rev().fold(0usize, |a, &v| a * 3 + (v + 1) as usize)
}
pub fn state_of(mut k: usize, n: usize) -> Vec<i8> {
(0..n)
.map(|_| {
let v = (k % 3) as i8 - 1;
k /= 3;
v
})
.collect()
}
/// The exact law of the final state: sequential sweeps at the inverse temperatures `betas` from
/// `start`, then, with `final_t0`, one synchronous sweep at temperature 0.
pub fn exact_law(w: &Workload, start: &[i8], betas: &[f64], final_t0: bool) -> Vec<f64> {
let n = w.n();
let m = 3usize.pow(n as u32);
let pow3: Vec<usize> = (0..n).map(|i| 3usize.pow(i as u32)).collect();
let mut p = vec![0.0f64; m];
p[index(start)] = 1.0;
for &beta in betas {
let mut cache: HashMap<i64, [f64; 3]> = HashMap::new();
for i in 0..n {
let mut q = vec![0.0f64; m];
for (k, &pk) in p.iter().enumerate() {
if pk == 0.0 {
continue;
}
let s = state_of(k, n);
let h = w.field(&s, i);
let pr = *cache.entry(h).or_insert_with(|| {
let (a, b) = cuts(h, w.cost(), beta);
[a as f64 / TWO53, (b - a) as f64 / TWO53, (TWO53 - b as f64) / TWO53]
});
let base = k - (s[i] + 1) as usize * pow3[i];
q[base] += pk * pr[0];
q[base + pow3[i]] += pk * pr[1];
q[base + 2 * pow3[i]] += pk * pr[2];
}
p = q;
}
}
if final_t0 {
let mut q = vec![0.0f64; m];
for (k, &pk) in p.iter().enumerate() {
if pk != 0.0 {
q[index(&w.sweep(&state_of(k, n)))] += pk;
}
}
p = q;
}
p
}
/// The reference's chain through the same schedule, draw for draw: cell `i` of sweep `k` takes
/// `out(chain, k·n + i + 1) >> 11`. For the job's anneal this is `sample::anneal` (a test holds the
/// two equal); at a fixed temperature the schedule is the anneal's inner loop with `beta` fixed.
pub fn reference(w: &Workload, start: &[i8], betas: &[f64], final_t0: bool, chain: u64) -> Vec<i8> {
let n = w.n();
let mut s = start.to_vec();
for (k, &beta) in betas.iter().enumerate() {
let base = (k as u64).wrapping_mul(n as u64);
for i in 0..n {
let (a, b) = cuts(w.field(&s, i), w.cost(), beta);
s[i] = pick(out(chain, base + i as u64 + 1) >> 11, a, b);
}
}
if final_t0 { w.sweep(&s) } else { s }
}
fn ln_gamma(x: f64) -> f64 {
const G: [f64; 9] = [
0.999_999_999_999_809_9,
676.520_368_121_885_1,
-1_259.139_216_722_402_8,
771.323_428_777_653_1,
-176.615_029_162_140_6,
12.507_343_278_686_905,
-0.138_571_095_265_720_12,
9.984_369_578_019_572e-6,
1.505_632_735_149_311_6e-7,
];
if x < 0.5 {
return (std::f64::consts::PI / (std::f64::consts::PI * x).sin()).ln() - ln_gamma(1.0 - x);
}
let x = x - 1.0;
let mut a = G[0];
let t = x + 7.5;
for (i, &g) in G.iter().enumerate().skip(1) {
a += g / (x + i as f64);
}
0.5 * (2.0 * std::f64::consts::PI).ln() + (x + 0.5) * t.ln() - t + a.ln()
}
/// The upper regularised incomplete gamma function Q(a, x): the chi-square tail is Q(df/2, x/2).
pub fn gamma_q(a: f64, x: f64) -> f64 {
if x <= 0.0 {
return 1.0;
}
let lead = -x + a * x.ln() - ln_gamma(a);
if x < a + 1.0 {
let (mut ap, mut sum, mut del) = (a, 1.0 / a, 1.0 / a);
for _ in 0..1_000_000 {
ap += 1.0;
del *= x / ap;
sum += del;
if del.abs() < sum.abs() * 1e-15 {
break;
}
}
(1.0 - sum * lead.exp()).max(0.0)
} else {
let tiny = 1e-300;
let mut b = x + 1.0 - a;
let mut c = 1.0 / tiny;
let mut d = 1.0 / b;
let mut h = d;
for i in 1..1_000_000 {
let an = -(i as f64) * (i as f64 - a);
b += 2.0;
d = an * d + b;
if d.abs() < tiny {
d = tiny;
}
c = b + an / c;
if c.abs() < tiny {
c = tiny;
}
d = 1.0 / d;
let del = d * c;
h *= del;
if (del - 1.0).abs() < 1e-15 {
break;
}
}
(lead.exp() * h).clamp(0.0, 1.0)
}
}
/// erfc, to a relative error under 1.2e-7.
pub fn erfc(x: f64) -> f64 {
let z = x.abs();
let t = 1.0 / (1.0 + 0.5 * z);
let r = t * (-z * z - 1.265_512_23
+ t * (1.000_023_68
+ t * (0.374_091_96 + t * (0.096_784_18 + t * (-0.186_288_06 + t * (0.278_868_07 + t * (-1.135_203_98 + t * (1.488_515_87 + t * (-0.822_152_23 + t * 0.170_872_77)))))))))
.exp();
if x >= 0.0 { r } else { 2.0 - r }
}
/// A histogram held to a law.
#[derive(Clone, Debug)]
pub struct Fit {
pub samples: u64,
pub bins: usize,
pub chi2: f64,
pub p_chi2: f64,
pub g: f64,
pub p_g: f64,
/// Total variation distance between the histogram and the law.
pub tv: f64,
/// The largest |z| over every cell's three marginal frequencies, and its Bonferroni p.
pub z_cell: f64,
pub p_cell: f64,
/// The largest |z| over every pair of cells' nine joint frequencies, and its Bonferroni p.
pub z_pair: f64,
pub p_pair: f64,
}
fn z_max(obs: &[u64], law: &[f64], total: u64, groups: impl Fn(usize) -> Vec<usize>, ngroups: usize) -> (f64, usize) {
let mut o = vec![0u64; ngroups];
let mut e = vec![0.0f64; ngroups];
for (k, (&c, &p)) in obs.iter().zip(law).enumerate() {
for gidx in groups(k) {
o[gidx] += c;
e[gidx] += p;
}
}
let nt = total as f64;
let mut zmax = 0.0f64;
let mut tests = 0usize;
for (oi, &pi) in o.iter().zip(&e) {
if pi <= 1e-12 || pi >= 1.0 - 1e-12 {
if (*oi > 0 && pi <= 1e-12) || (*oi < total && pi >= 1.0 - 1e-12) {
zmax = f64::INFINITY;
}
continue;
}
tests += 1;
let z = (*oi as f64 - nt * pi) / (nt * pi * (1.0 - pi)).sqrt();
zmax = zmax.max(z.abs());
}
(zmax, tests.max(1))
}
pub fn fit(counts: &[u64], law: &[f64], n: usize) -> Fit {
let total: u64 = counts.iter().sum();
let nt = total as f64;
// bins: every state expected at least 5 times on its own, the rest pooled
let (mut chi2, mut g, mut bins) = (0.0, 0.0, 0usize);
let (mut po, mut pe) = (0u64, 0.0f64);
let mut impossible = false;
for (&c, &p) in counts.iter().zip(law) {
let e = nt * p;
if e >= 5.0 {
chi2 += (c as f64 - e).powi(2) / e;
if c > 0 {
g += 2.0 * c as f64 * (c as f64 / e).ln();
}
bins += 1;
} else {
po += c;
pe += e;
if p == 0.0 && c > 0 {
impossible = true;
}
}
}
if pe > 0.0 {
chi2 += (po as f64 - pe).powi(2) / pe;
if po > 0 {
g += 2.0 * po as f64 * (po as f64 / pe).ln();
}
bins += 1;
}
let df = bins.saturating_sub(1).max(1) as f64;
let (mut p_chi2, mut p_g) = (gamma_q(df / 2.0, chi2 / 2.0), gamma_q(df / 2.0, g.max(0.0) / 2.0));
if impossible {
p_chi2 = 0.0;
p_g = 0.0;
}
let tv = 0.5 * counts.iter().zip(law).map(|(&c, &p)| (c as f64 / nt - p).abs()).sum::<f64>();
let pow3: Vec<usize> = (0..n).map(|i| 3usize.pow(i as u32)).collect();
let digit = |k: usize, i: usize| (k / pow3[i]) % 3;
let (z_cell, t_cell) = z_max(counts, law, total, |k| (0..n).map(|i| 3 * i + digit(k, i)).collect(), 3 * n);
let pairs: Vec<(usize, usize)> = (0..n).flat_map(|i| ((i + 1)..n).map(move |j| (i, j))).collect();
let (z_pair, t_pair) = z_max(counts, law, total, |k| pairs.iter().enumerate().map(|(q, &(i, j))| 9 * q + 3 * digit(k, i) + digit(k, j)).collect(), 9 * pairs.len().max(1));
let bonf = |z: f64, t: usize| if z.is_infinite() { 0.0 } else { (t as f64 * erfc(z / std::f64::consts::SQRT_2)).min(1.0) };
Fit { samples: total, bins, chi2, p_chi2, g, p_g, tv, z_cell, p_cell: bonf(z_cell, t_cell), z_pair, p_pair: bonf(z_pair, t_pair) }
}
/// The law with the same marginals as `law` and independent cells.
pub fn independent(law: &[f64], n: usize) -> Vec<f64> {
let mut marg = vec![[0.0f64; 3]; n];
for (k, &p) in law.iter().enumerate() {
for (i, s) in state_of(k, n).iter().enumerate() {
marg[i][(s + 1) as usize] += p;
}
}
(0..law.len()).map(|k| state_of(k, n).iter().enumerate().map(|(i, s)| marg[i][(s + 1) as usize]).product()).collect()
}
/// A pre-registered case: a workload, a start, a schedule, a number of chains.
pub struct Case {
pub name: &'static str,
pub landscape: Landscape,
pub start: Vec<i8>,
/// Temperatures of the sampled sweeps, in order.
pub temps: Vec<f64>,
/// The job's anneal ends with one synchronous sweep at temperature 0.
pub final_t0: bool,
/// For the anneal cases, the job's (t0, sweeps); the reference chain is then `sample::anneal`.
pub anneal: Option<(f64, u32)>,
pub chains: u64,
}
/// The cases, as `measurements/2026-11-06/JOINT-PREREG.txt` registers them. `scale` multiplies the
/// number of chains (1 on the board; small for the model).
pub fn cases(scale: f64) -> Vec<Case> {
let k = |c: u64| ((c as f64 * scale).round() as u64).max(200);
// ring of 8, frustrated by two chords, with fields and a cost
let mut ring = Landscape::new(8);
for i in 0..8 {
ring.couple(i, (i + 1) % 8, if i % 3 == 0 { -1 } else { 1 });
}
ring.couple(0, 4, -1).couple(2, 6, 1);
ring.set_field(0, 1).set_field(2, -2).set_field(4, 3).set_field(7, -1).set_cost(1);
// dense 6, every pair coupled, signs from a fixed stream
let mut dense = Landscape::new(6);
let mut z = 0x0D_E5E6u64;
for i in 0..6 {
for j in (i + 1)..6 {
z = z.wrapping_add(0x9E37_79B9_7F4A_7C15);
dense.couple(i, j, if out(z, 1) & 1 == 0 { 1 } else { -1 });
}
}
// sparse 10: a chain with three long couplings and fields
let mut sparse = Landscape::new(10);
for i in 0..9 {
sparse.couple(i, i + 1, if i % 2 == 0 { 1 } else { -1 });
}
sparse.couple(0, 9, 1).couple(1, 6, -1).couple(3, 8, 1);
sparse.set_field(1, 2).set_field(5, -1).set_field(8, 1).set_cost(1);
let anneal_temps = |t0: f64, sweeps: u32| (0..sweeps - 1).map(|q| temperature(t0, sweeps, q)).collect::<Vec<f64>>();
vec![
Case { name: "ring 8, fixed T = 1.5, 4 sweeps", landscape: ring.clone(), start: vec![0; 8], temps: vec![1.5; 4], final_t0: false, anneal: None, chains: k(200_000) },
Case { name: "ring 8, the job's anneal from T = 2.5, 10 sweeps", landscape: ring, start: vec![1, -1, 0, 0, 1, -1, 0, 1], temps: anneal_temps(2.5, 10), final_t0: true, anneal: Some((2.5, 10)), chains: k(200_000) },
Case { name: "dense 6, fixed T = 4, 2 sweeps", landscape: dense, start: vec![1, 1, 1, -1, -1, -1], temps: vec![4.0; 2], final_t0: false, anneal: None, chains: k(200_000) },
Case { name: "sparse 10, the job's anneal from T = 1.5, 8 sweeps", landscape: sparse, start: vec![0; 10], temps: anneal_temps(1.5, 8), final_t0: true, anneal: Some((1.5, 8)), chains: k(400_000) },
]
}
/// One case's result.
pub struct Outcome {
pub fit: Fit,
pub control: Fit,
/// Chains whose final state equals the reference's, of all.
pub same: u64,
}
/// Runs a case on an engine: every chain on the device and on the reference, the histogram held to
/// the exact law and to the independent-cells control. Chain `r` has the seed `chain_seed(seed, r)`.
pub fn run_case<P: Port>(e: &mut Engine<P>, c: &Case, seed: u64) -> Result<Outcome, String> {
let w = c.landscape.lower();
let n = w.n();
let l = lowered(&w);
e.load(&l).map_err(|x| x.0)?;
let segments = plan(&l, &c.temps)?;
let betas: Vec<f64> = c.temps.iter().map(|t| 1.0 / t).collect();
let law = exact_law(&w, &c.start, &betas, c.final_t0);
let mut counts = vec![0u64; law.len()];
let mut same = 0u64;
let block = 4096u64;
let mut r0 = 0u64;
while r0 < c.chains {
let r1 = (r0 + block).min(c.chains);
let list: Vec<(Vec<i8>, u64)> = (r0..r1).map(|r| (c.start.clone(), chain_seed(seed, r))).collect();
let got = run_schedule(e, &l, &segments, c.final_t0, &list, 0, crate::regs::MODE_GIBBS)?;
for (st, (_, ch)) in got.iter().zip(&list) {
counts[index(st)] += 1;
let want = match c.anneal {
Some((t0, sweeps)) => anneal(&w, &c.start, t0, sweeps, *ch).state,
None => reference(&w, &c.start, &betas, c.final_t0, *ch),
};
if *st == want {
same += 1;
}
}
r0 = r1;
}
let fit_law = fit(&counts, &law, n);
let control = fit(&counts, &independent(&law, n), n);
Ok(Outcome { fit: fit_law, control, same })
}
/// Every case, as a report: the pre-registered rule is that each case's chi-square, G, cell and
/// pair p are all at least 0.001 against the exact law, every chain requested came back and equals
/// the reference's, and, at the registered number of chains (`scale` 1), the independent-cells
/// control is rejected (chi-square p below 1e-6) while its cell family still passes.
pub fn report<P: Port>(e: &mut Engine<P>, scale: f64, seed: u64) -> Result<crate::conform::Report, String> {
let mut rep = crate::conform::Report::default();
rep.line(format!("joint statistics against exact enumeration (chains scaled by {scale}, seed {seed})"));
for c in cases(scale) {
let t = std::time::Instant::now();
let o = run_case(e, &c, seed)?;
let f = &o.fit;
let mut line = String::new();
let _ = write!(
line,
" {}: {} chains, {} bins; exact law: chi2 {:.1} (p {:.3}), G {:.1} (p {:.3}), TV {:.4}, cell |z| {:.2} (p {:.3}), pair |z| {:.2} (p {:.3}); same as the reference {}/{}; {:.1} s",
c.name, f.samples, f.bins, f.chi2, f.p_chi2, f.g, f.p_g, f.tv, f.z_cell, f.p_cell, f.z_pair, f.p_pair, o.same, c.chains, t.elapsed().as_secs_f64()
);
rep.line(line);
rep.line(format!(
" control, the same marginals with independent cells: chi2 {:.1} on {} bins (p {:.2e}), cell |z| {:.2} (p {:.3}), pair |z| {:.1} (p {:.2e})",
o.control.chi2, o.control.bins, o.control.p_chi2, o.control.z_cell, o.control.p_cell, o.control.z_pair, o.control.p_pair
));
let ok = f.p_chi2 >= 1e-3 && f.p_g >= 1e-3 && f.p_cell >= 1e-3 && f.p_pair >= 1e-3;
rep.check(ok, format!("{}: the joint law is the exact law (every p at least 0.001)", c.name));
rep.check(o.same == c.chains && f.samples == c.chains, format!("{}: every chain requested came back, its final state the reference's", c.name));
if scale >= 1.0 {
rep.check(o.control.p_chi2 < 1e-6 && o.control.p_cell >= 1e-3, format!("{}: the independent-cells control is rejected (chi-square p below 1e-6) and its cell family passes", c.name));
}
}
Ok(rep)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn index_round_trips() {
for k in 0..243 {
assert_eq!(index(&state_of(k, 5)), k);
}
}
#[test]
fn the_law_sums_to_one_and_the_schedule_is_the_anneal() {
for c in cases(0.0) {
let w = c.landscape.lower();
let betas: Vec<f64> = c.temps.iter().map(|t| 1.0 / t).collect();
let law = exact_law(&w, &c.start, &betas, c.final_t0);
let s: f64 = law.iter().sum();
assert!((s - 1.0).abs() < 1e-12, "{}: {s}", c.name);
if let Some((t0, sweeps)) = c.anneal {
for r in 0..50 {
let ch = chain_seed(9, r);
assert_eq!(reference(&w, &c.start, &betas, true, ch), anneal(&w, &c.start, t0, sweeps, ch).state, "{}", c.name);
}
}
}
}
#[test]
fn the_reference_meets_the_exact_law_and_the_control_fails() {
// the dense case on the CPU reference: 100,000 chains
let c = cases(0.5).remove(2);
let w = c.landscape.lower();
let betas: Vec<f64> = c.temps.iter().map(|t| 1.0 / t).collect();
let law = exact_law(&w, &c.start, &betas, false);
let mut counts = vec![0u64; law.len()];
for r in 0..c.chains {
counts[index(&reference(&w, &c.start, &betas, false, chain_seed(3, r)))] += 1;
}
let f = fit(&counts, &law, w.n());
assert!(f.p_chi2 > 1e-3 && f.p_g > 1e-3 && f.p_cell > 1e-3 && f.p_pair > 1e-3, "{f:?}");
let ctl = fit(&counts, &independent(&law, w.n()), w.n());
assert!(ctl.p_chi2 < 1e-6, "{ctl:?}");
assert!(ctl.p_cell > 1e-3, "the control has the right marginals: {ctl:?}");
}
/// Rule 3 counts against the chains requested: a run that lost some is not a pass.
#[test]
fn a_run_that_lost_chains_fails_the_report() {
use crate::engine::Engine;
use crate::model::ModelPort;
use crate::port::{ChainWords, Error, Port, ResultWords, batch_plain};
use std::time::Duration;
struct Lossy(ModelPort);
impl Port for Lossy {
fn write(&mut self, a: u16, v: u32) -> Result<(), Error> {
self.0.write(a, v)
}
fn read(&mut self, a: u16) -> Result<u32, Error> {
self.0.read(a)
}
fn wait_idle(&mut self, t: Duration) -> Result<(), Error> {
self.0.wait_idle(t)
}
fn batch(&mut self, chains: &[ChainWords], t: Duration) -> Result<Vec<ResultWords>, Error> {
let mut out = batch_plain(self, chains, t)?;
out.pop();
Ok(out)
}
}
let mut e = Engine::open(Lossy(ModelPort::new())).unwrap();
assert!(report(&mut e, 0.002, 1).is_err());
}
#[test]
fn the_tails_are_right() {
// chi-square with 10 degrees of freedom: P(X > 18.307) = 0.05; with 1000: P(X > 1074.68) = 0.05
assert!((gamma_q(5.0, 18.307 / 2.0) - 0.05).abs() < 1e-4);
assert!((gamma_q(500.0, 1074.68 / 2.0) - 0.05).abs() < 2e-3);
assert!((erfc(1.959_964 / std::f64::consts::SQRT_2) - 0.05).abs() < 1e-6);
}
}

View file

@ -0,0 +1,251 @@
//! The processor-to-logic latency, measured on the board: no primary source gives it for this part,
//! so it is timed here. Each access is timed alone with the processor's generic timer (CNTVCT_EL0,
//! read behind an ISB), many times, and reported as a distribution with the timer's own cost beside
//! it; the bulk paths (a DMA list, a batch of chains) are timed end to end.
use crate::engine::Engine;
use crate::exact::cases;
use crate::member::{lowered, plan, run_schedule};
use crate::port::Port;
use crate::regs::*;
use std::fmt::Write as _;
use std::time::Instant;
use wai_quantum::quantum_landscape::sample::{chain_seed, cuts};
#[cfg(target_arch = "aarch64")]
#[inline(always)]
pub fn ticks() -> u64 {
let t: u64;
unsafe { core::arch::asm!("isb", "mrs {0}, cntvct_el0", out(reg) t) };
t
}
#[cfg(target_arch = "aarch64")]
pub fn tick_hz() -> f64 {
let f: u64;
unsafe { core::arch::asm!("mrs {0}, cntfrq_el0", out(reg) f) };
f as f64
}
#[cfg(not(target_arch = "aarch64"))]
pub fn ticks() -> u64 {
use std::sync::OnceLock;
static T0: OnceLock<Instant> = OnceLock::new();
T0.get_or_init(Instant::now).elapsed().as_nanos() as u64
}
#[cfg(not(target_arch = "aarch64"))]
pub fn tick_hz() -> f64 {
1e9
}
/// Waits for every access before it to complete, the response included: a store alone is timed
/// only to its issue.
#[inline(always)]
pub fn dsb() {
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::asm!("dsb sy")
};
}
/// A distribution of times, in nanoseconds.
pub struct Dist(pub Vec<f64>);
impl Dist {
fn q(&self, f: f64) -> f64 {
let mut v = self.0.clone();
v.sort_by(|a, b| a.partial_cmp(b).unwrap());
v[((v.len() - 1) as f64 * f).round() as usize]
}
pub fn line(&self, what: &str) -> String {
let mean = self.0.iter().sum::<f64>() / self.0.len() as f64;
format!(
" {what:58} median {:7.0} ns p10 {:7.0} p90 {:7.0} p99 {:7.0} max {:8.0} mean {:7.0} (n {})",
self.q(0.5),
self.q(0.1),
self.q(0.9),
self.q(0.99),
self.q(1.0),
mean,
self.0.len()
)
}
}
/// Times `f` alone, `n` times, after `n / 10` unrecorded warm-up calls.
pub fn time_each(n: usize, mut f: impl FnMut()) -> Dist {
let ns = 1e9 / tick_hz();
for _ in 0..n / 10 {
f();
}
let mut v = Vec::with_capacity(n);
for _ in 0..n {
let a = ticks();
f();
let b = ticks();
v.push((b - a) as f64 * ns);
}
Dist(v)
}
/// The header every latency report starts with.
pub fn header(link: &str) -> String {
let mut s = String::new();
let _ = writeln!(s, "processor-to-logic latency, {link} link: one board, the engine at its measured clock");
let _ = writeln!(s, " timer: CNTVCT_EL0 at {:.3} MHz, one tick {:.1} ns", tick_hz() / 1e6, 1e9 / tick_hz());
s.push_str(&time_each(20_000, || {}).line("the timer alone (two reads, an ISB each)"));
s.push('\n');
s
}
/// A 64-level table of 128 fields each, the largest load a job makes: 32,832 register writes.
fn big_table(beta0: f64) -> Vec<Vec<(u64, u64)>> {
(0..64).map(|l| (-64..64).map(|h| cuts(h, 1, beta0 + 0.01 * l as f64)).collect()).collect()
}
/// End-to-end timings that every link has: a full table load, and chains run as a batch.
pub fn bulk_paths<P: Port>(e: &mut Engine<P>, label: &str, chains: u64) -> Result<String, String> {
let mut s = String::new();
let (ta, tb) = (big_table(0.2), big_table(0.3));
let mut times = Vec::new();
for k in 0..6 {
let t = Instant::now();
e.load_table(-64, 7, -64, if k % 2 == 0 { &ta } else { &tb }).map_err(|x| x.0)?;
times.push(t.elapsed().as_secs_f64());
}
times.sort_by(|a, b| a.partial_cmp(b).unwrap());
let writes = 64 * (1 + 4 * 128) + 3;
let _ = writeln!(s, " {label}: a full table (64 levels x 128 fields, {writes} register writes): median {:.2} ms of 6, {:.0} ns a write", times[3] * 1e3, times[3] * 1e9 / writes as f64);
// read one entry back: the load is real
let probe = e.table_entry(5 * 128 + 70).map_err(|x| x.0)?;
if probe != tb[5][70] {
return Err(format!("{label}: the table reads back {probe:?}, want {:?}", tb[5][70]));
}
let c = cases(1.0).remove(0);
let w = c.landscape.lower();
let l = lowered(&w);
e.load(&l).map_err(|x| x.0)?;
let seg = plan(&l, &c.temps)?;
let list: Vec<(Vec<i8>, u64)> = (0..chains).map(|r| (c.start.clone(), chain_seed(11, r))).collect();
run_schedule(e, &l, &seg, false, &list[..list.len().min(256)], 0, MODE_GIBBS)?;
let t = Instant::now();
run_schedule(e, &l, &seg, false, &list, 0, MODE_GIBBS)?;
let dt = t.elapsed().as_secs_f64();
let _ = writeln!(
s,
" {label}: {chains} chains of {} cells x {} sweeps: {:.3} s, {:.2} us a chain, {:.0} chains/s",
l.n,
c.temps.len(),
dt,
dt * 1e6 / chains as f64,
chains as f64 / dt
);
Ok(s)
}
#[cfg(target_os = "linux")]
pub fn axi(p: &mut crate::axi::BoardAxi, n: usize) -> String {
use crate::axi::*;
let mut s = header("AXI");
s.push_str(&time_each(n, || {
std::hint::black_box(p.signature());
})
.line("EMIO signature read (a processor GPIO register)"));
s.push('\n');
s.push_str(&time_each(n, || {
std::hint::black_box(p.rd_raw(WRAP_ID));
})
.line("AXI-Lite read, a link register"));
s.push('\n');
s.push_str(&time_each(n, || {
std::hint::black_box(p.rd_raw(ID));
})
.line("AXI-Lite read, an engine register"));
s.push('\n');
s.push_str(&time_each(n, dsb).line("a DSB alone"));
s.push('\n');
s.push_str(&time_each(n, || {
p.wr_raw(DMA_COUNT, 0);
dsb();
})
.line("AXI-Lite write and its response (DSB), a link register"));
s.push('\n');
s.push_str(&time_each(n, || {
p.wr_raw(SEL, 0);
dsb();
})
.line("AXI-Lite write and its response (DSB), an engine register"));
s.push('\n');
let t = std::time::Instant::now();
for _ in 0..n {
p.wr_raw(SEL, 0);
}
dsb();
let _ = writeln!(s, " AXI-Lite writes back to back, no DSB between: {:.0} ns each over {n}", t.elapsed().as_secs_f64() * 1e9 / n as f64);
s.push_str(&time_each(n, || {
p.wr_raw(SEL, 0);
std::hint::black_box(p.rd_raw(FIELD));
})
.line("AXI-Lite write then read (select a cell, read its field)"));
s.push('\n');
s.push_str(&time_each(n, || {
std::hint::black_box(p.rd(ID).ok());
})
.line("guarded read, as every job access goes (signature, then read)"));
s.push('\n');
let one = [(SEL, 0u32)];
s.push_str(&time_each(n / 20, || {
p.dma_list(&one, std::time::Duration::from_secs(1)).expect("DMA");
})
.line("a 1-entry write list by DMA, kick to done (4 writes, polls)"));
s.push('\n');
let page: Vec<(u16, u32)> = (0..512).map(|k| (SEL, k as u32 & 63)).collect();
let d = time_each(n / 50, || {
p.dma_list(&page, std::time::Duration::from_secs(1)).expect("DMA");
});
let med = d.q(0.5);
s.push_str(&d.line("a 512-entry write list by DMA (one page), kick to done"));
let _ = writeln!(s, "\n = {:.1} ns an entry, {:.1} M register writes/s", med / 512.0, 512.0 / med * 1e3);
let d = time_each(n / 50, || {
p.write_back(128, std::time::Duration::from_secs(1)).expect("write-back");
});
s.push_str(&d.line("128 chains' results written back (one page), kick to read"));
let _ = writeln!(s, "\n = {:.0} ns a chain", d.q(0.5) / 128.0);
s
}
#[cfg(target_os = "linux")]
pub fn emio(p: &mut crate::emio::EmioPort, n: usize) -> String {
let mut s = header("EMIO");
s.push_str(&time_each(n, || {
std::hint::black_box(p.read(ID).ok());
})
.line("EMIO register read (handshake over GPIO)"));
s.push('\n');
s.push_str(&time_each(n, || {
p.write(SEL, 0).ok();
})
.line("EMIO register write (handshake over GPIO)"));
s.push('\n');
s
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::ModelPort;
/// The chain `bulk_paths` times, in the engine's own clocks: 4 a cell update, 8 cells, 4 sweeps,
/// 9 to prime the draws and 2 to start and end. The model counts them as the logic does: the
/// testbenches hold every read of CYCLES to it. The README's figure for the engine's share of a
/// chain is this count.
#[test]
fn the_timed_chain_keeps_the_engine_busy_this_many_clocks() {
let c = cases(1.0).remove(0);
let l = lowered(&c.landscape.lower());
let seg = plan(&l, &c.temps).unwrap();
let mut e = Engine::open(ModelPort::new()).unwrap();
e.load(&l).unwrap();
e.port.write(CONTROL, CTL_CLRCNT).unwrap();
run_schedule(&mut e, &l, &seg, false, &[(c.start.clone(), 1)], 0, MODE_GIBBS).unwrap();
assert_eq!(e.counter(CYCLES).unwrap(), 4 * 8 * 4 + 9 + 2);
}
}

View file

@ -0,0 +1,24 @@
//! An FPGA board as a member of `wai.quantum.landscape/1`: the settle and sample kernels of the
//! energy-landscape job in programmable logic (`rtl/`), the register-level model the logic is
//! tested against, and the host that runs jobs on the board, holds it to the conformance set,
//! and meters its power. Classical throughout: three-state cells and a counter-based random
//! stream, no quantum effect.
pub mod axi;
pub mod conform;
pub mod engine;
pub mod exact;
pub mod latency;
pub mod member;
pub mod model;
pub mod port;
pub mod regs;
pub mod script;
pub mod power;
// The board's own side: Linux's /dev/mem, its constants, and the kernel's FPGA manager.
#[cfg(target_os = "linux")]
pub mod board;
#[cfg(target_os = "linux")]
pub mod emio;

View file

@ -0,0 +1,437 @@
//! wai-quantum-landscape-fpga: the command line. Board commands run on the board's own Linux, as root (for
//! /dev/mem). `selftest`, `gen-tb`, `analyze`, `joint --model` and `landscape --model` run anywhere.
use serde_json::Value;
#[cfg(target_os = "linux")]
use std::fmt::Write as _;
use std::process::exit;
use wai_quantum::quantum_landscape::energy::Landscape;
use wai_quantum::quantum_landscape::job;
use wai_quantum_landscape_fpga::conform::{self, Report};
use wai_quantum_landscape_fpga::engine::Engine;
#[cfg(target_os = "linux")]
use wai_quantum_landscape_fpga::engine::Loaded;
use wai_quantum_landscape_fpga::member::{BoardInfo, BoardMember, lowered, plan};
use wai_quantum_landscape_fpga::model::ModelPort;
use wai_quantum_landscape_fpga::port::Port;
#[cfg(target_os = "linux")]
use wai_quantum_landscape_fpga::port::Error;
use wai_quantum_landscape_fpga::regs::*;
use wai_quantum_landscape_fpga::script::ScriptPort;
fn usage() -> ! {
eprintln!(
"usage: wai-quantum-landscape-fpga <command>\n\
\x20 selftest [--scale X] [--seeds N] every check against the register-level model (no board)\n\
\x20 gen-tb <file> the RTL testbench's script, recorded from the model\n\
\x20 analyze <meter.log> [--predict mW] a power log, summarised\n\
on the board, as root:\n\
\x20 load <bit.bin> load the bitstream, prove the load, check the engine answers\n\
\x20 status engine, clock and power\n\
\x20 conform [--scale X] the conformance set, random settles, field read-back\n\
\x20 agree [--seeds N] every worked example, board against the CPU reference\n\
\x20 landscape <op> <request.json|-> [--key FILE | --demo-key | --unsealed]\n\
\x20 a job of wai.quantum.landscape/1 on the board, sealed\n\
\x20 throughput clocks, jobs and operations a second, from the counters\n\
\x20 meter <class> <passes> <settle_s> <samples> [--predict mW] [--null] [--cores K]\n\
\x20 run/halt board power; classes t0-recall gibbs-recall t0-idle\n\
\x20 clock <div0> pl_clk0 = 1500/div0 MHz, measured back by the engine\n\
\x20 release the EMIO banks back as the board boots\n\
board commands take --core C on the multi-core bitstream"
);
exit(2)
}
fn flag<T: std::str::FromStr>(args: &[String], name: &str) -> Option<T> {
let i = args.iter().position(|a| a == name)?;
let v = args.get(i + 1).unwrap_or_else(|| die(format!("{name} needs a value")));
Some(v.parse().unwrap_or_else(|_| die(format!("{name}: `{v}` is not a value it takes"))))
}
fn die(e: impl std::fmt::Display) -> ! {
eprintln!("wai-quantum-landscape-fpga: {e}");
exit(1)
}
fn finish(rep: &Report) -> ! {
print!("{}", rep.text);
println!("{} checks, {} failed", rep.checks, rep.failures.len());
exit(if rep.failures.is_empty() { 0 } else { 1 })
}
/// The dense recall landscape the power classes run: 8 stored patterns on 64 cells, and a cue 30%
/// corrupted.
fn recall() -> (Landscape, Vec<i8>) {
let mut z = 0x7EC4u64;
let mut next = || {
z = z.wrapping_add(GOLDEN);
let mut r = z;
r = (r ^ (r >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
r = (r ^ (r >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
r ^ (r >> 31)
};
let pats: Vec<Vec<i64>> = (0..8).map(|_| (0..64).map(|_| if next() & 1 == 0 { 1 } else { -1 }).collect()).collect();
let mut l = Landscape::new(64);
for i in 0..64 {
for j in (i + 1)..64 {
let s: i64 = pats.iter().map(|p| p[i] * p[j]).sum();
if s != 0 {
l.couple(i, j, s.signum() as i8);
}
}
}
let mut cue: Vec<i8> = pats[0].iter().map(|&x| x as i8).collect();
for _ in 0..19 {
let i = (next() % 64) as usize;
cue[i] = -cue[i];
}
(l, cue)
}
fn model_member() -> BoardMember<ModelPort> {
let mut m = BoardMember::new(Engine::open(ModelPort::new()).unwrap_or_else(|e| die(e)), model_info());
let t = m.earn().unwrap_or_else(|e| die(e));
print!("{t}");
m
}
fn key(args: &[String]) -> Option<wai_quantum::ed25519_dalek::SigningKey> {
use wai_quantum::ed25519_dalek::SigningKey;
if args.iter().any(|a| a == "--unsealed") {
return None;
}
if args.iter().any(|a| a == "--demo-key") {
return Some(SigningKey::from_bytes(&[42; 32]));
}
let path = flag::<String>(args, "--key")
.or_else(|| std::env::var("WAI_QUANTUM_KEY").ok())
.unwrap_or_else(|| format!("{}/.config/wai/quantum-key", std::env::var("HOME").unwrap_or_else(|_| "/root".into())));
let text = match std::fs::read_to_string(&path) {
Ok(t) => t,
Err(_) => {
let mut seed = [0u8; 32];
use std::io::Read;
std::fs::File::open("/dev/urandom").and_then(|mut f| f.read_exact(&mut seed)).unwrap_or_else(|e| die(format!("no random source for a key: {e}")));
let t: String = seed.iter().map(|b| format!("{b:02x}")).collect();
if let Some(d) = std::path::Path::new(&path).parent() {
let _ = std::fs::create_dir_all(d);
}
#[cfg(unix)]
{
use std::io::Write;
use std::os::unix::fs::OpenOptionsExt;
std::fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(&path).and_then(|mut f| f.write_all(format!("{t}\n").as_bytes())).unwrap_or_else(|e| die(format!("{path}: {e}")));
}
eprintln!("made a signing key at {path}");
t
}
};
let t = text.trim();
let mut seed = [0u8; 32];
if t.len() != 64 {
die(format!("{path}: a key file holds 64 hex digits"));
}
for (i, b) in seed.iter_mut().enumerate() {
*b = u8::from_str_radix(&t[2 * i..2 * i + 2], 16).unwrap_or_else(|_| die(format!("{path}: not hex")));
}
Some(SigningKey::from_bytes(&seed))
}
/// The seed the joint-statistics test is registered at (`measurements/2026-11-06/JOINT-PREREG.txt`).
const JOINT_SEED: u64 = 2027;
fn model_info() -> BoardInfo {
BoardInfo { name: "model".into(), description: "the register-level model of the engine, in software".into() }
}
/// The board commands, on any port: the whole board, or one core of the multi-core bitstream.
fn on_board<P: Port>(cmd: &str, args: &[String], port: P, info: BoardInfo) {
let engine = Engine::open(port).unwrap_or_else(|e| die(e));
let mut m = BoardMember::new(engine, info);
match cmd {
"conform" => {
let mut rep = conform::full(&m, flag(args, "--scale").unwrap_or(1.0)).unwrap_or_else(|e| die(e));
let f = conform::fields(&m, 64).unwrap_or_else(|e| die(e));
rep.text += &f.text;
rep.checks += f.checks;
rep.failures.extend(f.failures);
let ok = m.engine.borrow_mut().alive().is_ok();
rep.check(ok, "the engine was not reloaded during the run: no other session replaced it");
finish(&rep)
}
"agree" => {
let t = m.earn().unwrap_or_else(|e| die(e));
print!("{t}");
println!("parity earned on this board: settle {}, sample {}", m.settle_parity.label(), m.sample_parity.label());
let rep = conform::agree(&m, flag(args, "--seeds").unwrap_or(200)).unwrap_or_else(|e| die(e));
finish(&rep)
}
"landscape" => {
let op = args.get(1).cloned().unwrap_or_else(|| usage());
let file = args.get(2).cloned().unwrap_or_else(|| usage());
let text = if file == "-" {
let mut s = String::new();
std::io::Read::read_to_string(&mut std::io::stdin(), &mut s).unwrap_or_else(|e| die(e));
s
} else {
std::fs::read_to_string(&file).unwrap_or_else(|e| die(e))
};
let request: Value = serde_json::from_str(&text).unwrap_or_else(|e| die(format!("the request is not JSON: {e}")));
let c = wai_quantum::quantum_landscape::member::certify(&m).unwrap_or_else(|e| die(e));
let answer = match job::call_certified(&c, &op, &request, wai_quantum::quantum_landscape::MAX_JOB_BYTES) {
Ok(a) => a,
Err(r) => die(r.plain()),
};
let out = match key(args) {
Some(k) if op != "examples" => job::seal(&request, answer, &k),
_ => answer,
};
println!("{}", serde_json::to_string_pretty(&out).unwrap());
}
"joint" => {
let mut e = m.engine.borrow_mut();
let rep = wai_quantum_landscape_fpga::exact::report(&mut e, flag(args, "--scale").unwrap_or(1.0), flag(args, "--seed").unwrap_or(JOINT_SEED)).unwrap_or_else(|x| die(x));
drop(e);
let ok = m.engine.borrow_mut().alive().is_ok();
let mut rep = rep;
rep.check(ok, "the engine was not reloaded during the run: no other session replaced it");
finish(&rep)
}
"status" => {
let mut e = m.engine.borrow_mut();
let t0 = std::time::Instant::now();
let u0 = e.counter(UPTIME).unwrap_or_else(|x| die(x));
std::thread::sleep(std::time::Duration::from_millis(500));
let u1 = e.counter(UPTIME).unwrap_or_else(|x| die(x));
println!("engine answers: ID and version {VERSION_VALUE:#010x}; measured clock {:.3} MHz", (u1 - u0) as f64 / t0.elapsed().as_secs_f64() / 1e6);
}
"throughput" => {
let (l, cue) = recall();
let w = l.lower();
let lw = lowered(&w);
let seg = plan(&lw, &[0.25]).unwrap_or_else(|x| die(x)).remove(0);
let mut e = m.engine.borrow_mut();
e.load(&lw).unwrap_or_else(|x| die(x));
e.load_table(seg.win_lo, seg.wbits, seg.first, &seg.levels).unwrap_or_else(|x| die(x));
e.chain(1).unwrap_or_else(|x| die(x));
for (name, mode, bound) in [("settle, dense 64 (recall cue)", MODE_T0, 60u32), ("sample, sequential, 64 cells, 8 sweeps at T = 0.25", MODE_GIBBS, 8)] {
e.set_boundary(&cue).unwrap_or_else(|x| die(x));
e.run_repeat(mode, bound, 8, false).unwrap_or_else(|x| die(x));
std::thread::sleep(std::time::Duration::from_millis(200));
let (t0, j0, u0, c0, y0) = (std::time::Instant::now(), e.counter(JOBS).unwrap(), e.counter(UPDATES).unwrap(), e.counter(UPTIME).unwrap(), e.counter(CYCLES).unwrap());
std::thread::sleep(std::time::Duration::from_secs(2));
let (dt, j1, u1, c1, y1) = (t0.elapsed().as_secs_f64(), e.counter(JOBS).unwrap(), e.counter(UPDATES).unwrap(), e.counter(UPTIME).unwrap(), e.counter(CYCLES).unwrap());
e.halt().unwrap();
let jobs = (j1 - j0) as f64 / dt;
let acc = if mode == MODE_T0 { w.settle(&cue, bound).accumulates as f64 * jobs } else { jobs * f64::from(bound) * lw.nonzeros() as f64 };
println!("{name}: clock {:.3} MHz, {:.1} clocks a job, {jobs:.0} jobs/s, {:.3e} cell updates/s, {acc:.3e} accumulates/s", (c1 - c0) as f64 / dt / 1e6, (y1 - y0) as f64 / (j1 - j0).max(1) as f64, (u1 - u0) as f64 / dt);
}
}
_ => usage(),
}
}
#[cfg(target_os = "linux")]
fn meter_class<P: Port>(e: &mut Engine<P>, class: &str, plan_m: &wai_quantum_landscape_fpga::power::Plan, log: &mut String) -> Vec<wai_quantum_landscape_fpga::power::Point> {
let (l, cue) = recall();
let lw = lowered(&l.lower());
let seg = plan(&lw, &[0.25]).unwrap_or_else(|x| die(x)).remove(0);
let class = class.to_string();
let mut start = move |e: &mut Engine<P>| -> Result<(), Error> {
match class.as_str() {
"t0-recall" => {
e.load(&lw)?;
e.set_boundary(&cue)?;
e.run_repeat(MODE_T0, 60, 1, false)
}
"gibbs-recall" => {
e.load(&lw)?;
e.load_table(seg.win_lo, seg.wbits, seg.first, &seg.levels)?;
e.chain(1)?;
e.set_boundary(&cue)?;
e.run_repeat(MODE_GIBBS, 8, 8, true)
}
"t0-idle" => {
e.load(&Loaded { n: 64, rows: vec![vec![]; 64], fields: vec![0; 64], cost: 0 })?;
e.set_boundary(&[0; 64])?;
e.run_repeat(MODE_T0, 0, 1, false)
}
other => Err(Error(format!("unknown class {other}"))),
}
};
wai_quantum_landscape_fpga::power::meter(e, plan_m, &mut start, log).unwrap_or_else(|x| die(x))
}
#[cfg(target_os = "linux")]
enum Link {
Axi(wai_quantum_landscape_fpga::axi::BoardAxi),
Emio(wai_quantum_landscape_fpga::emio::EmioPort),
}
/// The loaded design's link: AXI if its signature is on EMIO bank 3 (a GPIO read, safe whatever is
/// loaded), else EMIO; `--link` insists on one.
#[cfg(target_os = "linux")]
fn open_link(args: &[String]) -> Link {
let axi = match flag::<String>(args, "--link").as_deref() {
Some("axi") => true,
Some("emio") => false,
Some(o) => die(format!("--link {o}: axi or emio")),
None => wai_quantum_landscape_fpga::axi::signature_present(),
};
if axi {
Link::Axi(wai_quantum_landscape_fpga::axi::open().unwrap_or_else(|e| die(e)))
} else {
Link::Emio(wai_quantum_landscape_fpga::emio::EmioPort::open().unwrap_or_else(|e| die(e)))
}
}
#[cfg(target_os = "linux")]
macro_rules! with_link {
($args:expr, $p:ident => $body:expr) => {
match open_link($args) {
Link::Axi($p) => $body,
Link::Emio($p) => $body,
}
};
}
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let cmd = args.first().map(|s| s.as_str()).unwrap_or_else(|| usage());
match cmd {
"selftest" => {
let scale = flag(&args, "--scale").unwrap_or(0.02);
let m = model_member();
let mut rep = conform::full(&m, scale).unwrap_or_else(|e| die(e));
let joint = wai_quantum_landscape_fpga::exact::report(&mut m.engine.borrow_mut(), 0.002, JOINT_SEED).unwrap_or_else(|e| die(e));
for r in [conform::fields(&m, 4).unwrap_or_else(|e| die(e)), conform::agree(&m, flag(&args, "--seeds").unwrap_or(3)).unwrap_or_else(|e| die(e)), joint] {
rep.text += &r.text;
rep.checks += r.checks;
rep.failures.extend(r.failures);
}
finish(&rep)
}
"gen-tb" => {
let out = args.get(1).unwrap_or_else(|| usage());
let port = if args.iter().any(|a| a == "--axi") { ScriptPort::axi() } else { ScriptPort::new() };
let m = BoardMember::new(Engine::open(port).unwrap_or_else(|e| die(e)), BoardInfo::default());
conform::tb_session(&m).unwrap_or_else(|e| die(e));
let e = m.engine.borrow();
std::fs::write(out, &e.port.text).unwrap_or_else(|e| die(e));
println!("{} lines, {} checked reads -> {out}", e.port.text.lines().count(), e.port.reads);
}
"landscape" if args.iter().any(|a| a == "--model") => on_board(cmd, &args, ModelPort::new(), model_info()),
"joint" if args.iter().any(|a| a == "--model") => {
let mut e = Engine::open(ModelPort::new()).unwrap_or_else(|e| die(e));
let rep = wai_quantum_landscape_fpga::exact::report(&mut e, flag(&args, "--scale").unwrap_or(1.0), flag(&args, "--seed").unwrap_or(JOINT_SEED)).unwrap_or_else(|e| die(e));
finish(&rep)
}
"analyze" => {
let f = args.get(1).unwrap_or_else(|| usage());
let text = std::fs::read_to_string(f).unwrap_or_else(|e| die(e));
let pts = wai_quantum_landscape_fpga::power::parse_log(&text);
print!("{}", wai_quantum_landscape_fpga::power::summarise(&pts, flag(&args, "--predict")).text);
}
#[cfg(target_os = "linux")]
"load" => {
let f = args.get(1).unwrap_or_else(|| usage());
// the lock first: a reload waits for the job that holds the board
let l = wai_quantum_landscape_fpga::board::lock().unwrap_or_else(|e| die(e));
println!("{}", wai_quantum_landscape_fpga::board::load_bitstream(f, &l).unwrap_or_else(|e| die(e)));
let link = with_link!(&args, p => { let name = p.name(); Engine::open(p).unwrap_or_else(|e| die(e)); name });
println!("the engine answers with its ID and version, over its {link} link");
}
#[cfg(target_os = "linux")]
"foreign" => {
// for the scripts: this tool and its parents are left out, so a script need not know its own
let f = wai_quantum_landscape_fpga::board::foreign_sessions();
if !f.is_empty() {
println!("{f}");
exit(3);
}
}
#[cfg(target_os = "linux")]
"ps-ports" => {
let (ok, text) = wai_quantum_landscape_fpga::axi::ps_ports().unwrap_or_else(|e| die(e));
println!("{text}: {}", if ok { "they match" } else { "they do not match" });
}
#[cfg(target_os = "linux")]
"release" => {
wai_quantum_landscape_fpga::emio::EmioPort::release_to_boot_state().unwrap_or_else(|e| die(e));
println!("EMIO banks 3-5: inputs, outputs disabled, data 0");
}
#[cfg(target_os = "linux")]
"clock" => {
let d: u32 = args.get(1).and_then(|v| v.parse().ok()).unwrap_or_else(|| usage());
let _l = wai_quantum_landscape_fpga::board::lock().unwrap_or_else(|e| die(e));
with_link!(&args, p => {
let mut e = Engine::open(p).unwrap_or_else(|e| die(e));
let old = wai_quantum_landscape_fpga::board::set_pl0_div(d).unwrap_or_else(|e| die(e));
std::thread::sleep(std::time::Duration::from_millis(100));
let t0 = std::time::Instant::now();
let u0 = e.counter(UPTIME).unwrap_or_else(|e| die(e));
std::thread::sleep(std::time::Duration::from_secs(1));
let u1 = e.counter(UPTIME).unwrap_or_else(|e| die(e));
println!("PL0_REF_CTRL was {old:#x}; nominal {:.2} MHz; measured {:.4} MHz", 1500.0 / f64::from(d), (u1 - u0) as f64 / t0.elapsed().as_secs_f64() / 1e6);
});
}
#[cfg(target_os = "linux")]
"meter" => {
let class = args.get(1).cloned().unwrap_or_else(|| usage());
let passes: u32 = args.get(2).and_then(|v| v.parse().ok()).unwrap_or_else(|| usage());
let settle_s: u64 = args.get(3).and_then(|v| v.parse().ok()).unwrap_or_else(|| usage());
let samples: u32 = args.get(4).and_then(|v| v.parse().ok()).unwrap_or_else(|| usage());
let plan_m = wai_quantum_landscape_fpga::power::Plan { passes, settle_s, samples, null: args.iter().any(|a| a == "--null") };
let cores: u16 = flag(&args, "--cores").unwrap_or(1);
let _l = wai_quantum_landscape_fpga::board::lock().unwrap_or_else(|e| die(e));
let mut log = String::new();
let pts = with_link!(&args, port => {
let _ = writeln!(log, "# class {class} cores {cores} link {}", port.name());
if cores > 1 {
let mut e = Engine::open(wai_quantum_landscape_fpga::port::BroadcastPort { inner: port, k: cores }).unwrap_or_else(|x| die(x));
meter_class(&mut e, &class, &plan_m, &mut log)
} else {
let mut e = Engine::open(port).unwrap_or_else(|x| die(x));
meter_class(&mut e, &class, &plan_m, &mut log)
}
});
print!("{log}");
for l in wai_quantum_landscape_fpga::power::summarise(&pts, flag(&args, "--predict")).text.lines() {
println!("# {l}");
}
}
#[cfg(target_os = "linux")]
"latency" => {
let n: usize = args.get(1).and_then(|v| v.parse().ok()).unwrap_or(100_000);
let _l = wai_quantum_landscape_fpga::board::lock().unwrap_or_else(|e| die(e));
use wai_quantum_landscape_fpga::latency;
match open_link(&args) {
Link::Axi(mut p) => {
print!("{}", latency::axi(&mut p, n));
let mut e = Engine::open(p).unwrap_or_else(|x| die(x));
print!("{}", latency::bulk_paths(&mut e, "AXI, DMA and sequencer", 65_536).unwrap_or_else(|x| die(x)));
e.port.sequencer = false;
e.port.dma_min = usize::MAX;
print!("{}", latency::bulk_paths(&mut e, "AXI, plain register accesses", 16_384).unwrap_or_else(|x| die(x)));
println!(" AXI accesses {}, DMA lists {}, sequencer batches {}, write-backs {}", e.port.accesses, e.port.dma_lists, e.port.batches, e.port.write_backs);
}
Link::Emio(mut p) => {
print!("{}", latency::emio(&mut p, n / 4));
let mut e = Engine::open(p).unwrap_or_else(|x| die(x));
print!("{}", latency::bulk_paths(&mut e, "EMIO, plain register accesses", 4_096).unwrap_or_else(|x| die(x)));
}
}
}
#[cfg(target_os = "linux")]
"conform" | "agree" | "landscape" | "status" | "throughput" | "joint" => {
let _l = wai_quantum_landscape_fpga::board::lock().unwrap_or_else(|e| die(e));
match (open_link(&args), flag::<u16>(&args, "--core")) {
(Link::Emio(mut port), Some(core)) => on_board(cmd, &args, wai_quantum_landscape_fpga::port::CorePort { inner: &mut port, core }, BoardInfo::default()),
(Link::Axi(_), Some(_)) => die("--core is for the multi-core EMIO build"),
(Link::Axi(p), None) => on_board(cmd, &args, p, BoardInfo::default()),
(Link::Emio(p), None) => on_board(cmd, &args, p, BoardInfo::default()),
}
}
_ => usage(),
}
}

View file

@ -0,0 +1,493 @@
//! The board as a member of `wai.quantum.landscape/1` (`extensions/quantum-landscape.md` §5): it
//! runs the two kernels and nothing else. The reader, the truth, the energy of what it returns, the
//! verdict, the replay digest and the seal stay on the host (`wai_quantum::quantum_landscape`).
//!
//! **The settle kernel** is the engine's T0 mode, whole-number arithmetic, so it gives the
//! reference's bits by construction; the conformance set's `settle` vectors hold it to them.
//!
//! **The sample kernel** takes the reference's draws: the engine counts `chain + j·GOLDEN` and mixes
//! it in logic, so the draw of cell `i` in sweep `k` is the top 53 bits of `out(chain, k·n + i + 1)`,
//! the reference's. The host gives it the cut points, computed with the reference's `cuts`, so the
//! fabric needs no exponential: per temperature level, a window of fields from `WIN_LO`. A field
//! outside the window takes the threshold, which equals the reference's draw only where that draw is
//! certain, so the host puts the window over every field a cell can reach whose draw is not certain
//! at any of the segment's temperatures, and refuses a job for which no window fits. The `chains`
//! vectors hold the result to the reference's bits.
//!
//! **A schedule** is one temperature a sweep, as the job gives it. The chains of a job run as one
//! batch ([`Engine::run_batch`]): on the AXI link the engine's sequencer runs them one after another
//! in fabric. A run of sweeps above 0 longer than the table holds goes in segments: the table is
//! rewritten, and every chain starts the next segment from where it ended with its draw counter at
//! the draw the reference takes next (sweep `k`'s draws are `k·n + i + 1`, whatever the sweeps
//! before it were). A sweep at 0 directly after sweeps above 0 is the engine's final synchronous
//! sweep; other sweeps at 0 run in its T0 mode, every sweep, as the reference's do: the engine stops
//! at a fixed point or a two-cycle, and past a two-cycle the state alternates, so one more sweep is
//! taken when the count left is odd.
//!
//! The ending, sweeps, accumulates and draws of a chain's `Outcome` come from the schedule and the
//! state, as the reference's do (`check_chain` fixes them for every member); the engine's own count
//! of sweeps is held to its segment here.
//!
//! What it has earned is not assumed, and not said by the member: the host certifies it
//! (`wai_quantum::quantum_landscape::member::certify`) by running the conformance set's `settle`
//! and `chains` vectors on the device, and, where those miss, the statistical battery.
use crate::engine::{Engine, Loaded};
use crate::port::Port;
use crate::regs::*;
use std::cell::RefCell;
use wai_quantum::quantum_landscape::conformance::MUST_TAKE_FIELD_BOUND;
use wai_quantum::quantum_landscape::member::{Chain, KERNEL_SAMPLE, KERNEL_SETTLE, Member, Parity, certify};
use wai_quantum::quantum_landscape::sample::cuts;
use wai_quantum::quantum_landscape::settle::{Outcome, Term, Workload, threshold};
/// The conformance set a member is held to, as the repository holds it.
pub const VECTORS: &str = include_str!("../../quantum-conformance/landscape/vectors.json");
/// What the board is, as data: the member's name and its answers' label carry it.
#[derive(Clone, Debug)]
pub struct BoardInfo {
/// A short board name for the member id, `fpga:<name>-landscape-64`.
pub name: String,
/// The board and part, in words, for the label.
pub description: String,
}
impl Default for BoardInfo {
fn default() -> Self {
BoardInfo { name: "kv260".into(), description: "a KV260 board (xck26 programmable logic, four Cortex-A53 cores)".into() }
}
}
pub struct BoardMember<P: Port> {
pub engine: RefCell<Engine<P>>,
pub info: BoardInfo,
/// What the host's certification found, kept for display; the trait says nothing of parity.
pub settle_parity: Parity,
pub sample_parity: Parity,
/// Sample with every cell moved at once (JACOBI): not a kernel of the spec, only the negative
/// control the statistical battery must catch.
pub jacobi_control: std::cell::Cell<bool>,
}
/// One segment of a sampling chain: up to `levels.len()` sweeps on one table.
#[derive(Clone, Debug)]
pub struct Segment {
pub sweeps: usize,
pub win_lo: i64,
pub wbits: u32,
pub first: i64,
pub levels: Vec<Vec<(u64, u64)>>,
}
fn certain(h: i64, cost: i64) -> (u64, u64) {
match threshold(h, cost) {
1 => (0, 0),
-1 => (TWO53, TWO53),
_ => (0, TWO53),
}
}
/// The workload as the engine holds it.
pub fn lowered(w: &Workload) -> Loaded {
let n = w.n();
let zero = vec![0i8; n];
Loaded {
n,
rows: (0..n).map(|i| w.row(i).map(|(j, v)| (j, v as i8)).collect()).collect(),
fields: (0..n).map(|i| w.field(&zero, i)).collect(),
cost: w.cost(),
}
}
/// The workload as the engine holds it for sweeps at temperature 0 only, where fields and the cost
/// may exceed the engine's registers. A cell at temperature 0 compares its coupling sum `S`, which
/// lies in `[-d, d]` (`d` its couplings, at most 63), with two thresholds, `c - b` and `-c - b`: it
/// takes +1 above the first, -1 below the second, 0 between. Only where a threshold falls in
/// `[-d - 1, d + 1]` does it matter where; elsewhere only which side. So a field or a cost too large
/// for the engine is replaced by one that puts every threshold on the same side of every `S` the
/// cell can see, and the settle is the same, bit for bit. With a cost above `d + 1`, at most one of
/// a cell's thresholds can fall in that range, and the cost becomes `d + 1` with each field moved
/// to keep that threshold where it was.
pub fn lowered_settle(w: &Workload) -> Loaded {
let mut l = lowered(w);
if l.cost <= COST_MAX && l.fields.iter().all(|f| f.abs() <= FIELD_MAX) {
return l;
}
let d: i128 = 63;
let c = i128::from(l.cost);
let inside = |x: i128| (-d - 1..=d + 1).contains(&x);
if c <= d + 1 {
for b in l.fields.iter_mut() {
*b = (i128::from(*b)).clamp(-(c + d + 1), c + d + 1) as i64;
}
} else {
let t = d + 1;
l.cost = t as i64;
for b in l.fields.iter_mut() {
let (up, down) = (c - i128::from(*b), -c - i128::from(*b));
*b = if inside(up) {
t - up
} else if inside(down) {
-t - down
} else if up < -d - 1 {
t + d + 1
} else if down > d + 1 {
-(t + d + 1)
} else {
0
} as i64;
}
}
l
}
/// Why the engine cannot hold a workload under a schedule, if it cannot: more variables than its 64
/// cells, or, for sweeps above 0, a field or cost beyond its registers or uncertain fields wider
/// than its table's window. Sweeps at 0 it holds at any field ([`lowered_settle`]). The member
/// fails such a workload with a sentence, never with wrong bits; the checks hold it to that.
pub fn beyond(w: &Workload, temperatures: &[f64]) -> Option<String> {
if w.n() > CELLS {
return Some(format!("{} variables, and the engine has {CELLS} cells", w.n()));
}
if temperatures.iter().all(|&t| t <= 0.0) {
return None;
}
let l = lowered(w);
if let Some(f) = l.fields.iter().find(|f| f.abs() > FIELD_MAX) {
return Some(format!("a field of {f} to sample, and the engine's field registers hold ±{FIELD_MAX}"));
}
if l.cost > COST_MAX {
return Some(format!("a cost of {} to sample, and the engine's cost register holds {COST_MAX}", l.cost));
}
let mut k = 0;
while k < temperatures.len() {
let hot = temperatures[k] > 0.0;
let m = temperatures[k..].iter().take_while(|&&t| (t > 0.0) == hot).count();
if hot {
if let Err(e) = plan(&l, &temperatures[k..k + m]) {
return Some(e);
}
}
k += m;
}
None
}
/// The fields cell `i` can reach: its own field plus or minus its number of couplings.
fn reach(l: &Loaded) -> Vec<i64> {
let mut hs: Vec<i64> = Vec::new();
for i in 0..l.n {
let d = l.rows[i].len() as i64;
hs.extend((l.fields[i] - d)..=(l.fields[i] + d));
}
hs.sort_unstable();
hs.dedup();
hs
}
/// The tables for a run at temperatures `temps` (each above 0), one segment per table.
pub fn plan(l: &Loaded, temps: &[f64]) -> Result<Vec<Segment>, String> {
let hs = reach(l);
let (lo_r, hi_r) = (*hs.first().unwrap_or(&0), *hs.last().unwrap_or(&0));
let mut out = Vec::new();
let mut k = 0;
while k < temps.len() {
let mut take = (temps.len() - k).min(LEVELS);
loop {
let betas: Vec<f64> = temps[k..k + take].iter().map(|t| 1.0 / t).collect();
// the fields whose draw is not certain at some temperature of the segment
let open: Vec<i64> = hs.iter().copied().filter(|&h| betas.iter().any(|&b| cuts(h, l.cost, b) != certain(h, l.cost))).collect();
let (lo, hi) = match (open.first(), open.last()) {
(Some(&a), Some(&b)) => (a, b),
_ => (lo_r, lo_r),
};
let span = (hi - lo + 1) as u64;
let wbits = 64 - (span - 1).leading_zeros().min(64);
let wbits = if span <= 1 { 0 } else { wbits };
if wbits > MAX_WBITS {
return Err(format!(
"the fields whose draw is uncertain run from {lo} to {hi}, {span} of them, and the engine's table holds {TABLE} a temperature"
));
}
let fit = (TABLE >> wbits).min(LEVELS);
if take > fit {
take = fit;
continue;
}
// every reachable field inside the window gets its exact pair
let (first, last) = (lo.max(lo_r), (lo + (1i64 << wbits) - 1).min(hi_r));
let levels = betas.iter().map(|&b| if first <= last { (first..=last).map(|h| cuts(h, l.cost, b)).collect() } else { Vec::new() }).collect();
out.push(Segment { sweeps: take, win_lo: lo, wbits, first, levels });
break;
}
k += take;
}
Ok(out)
}
/// Sampling chains through sweeps planned by [`plan`], the first of them sweep `first_sweep` of the
/// chain's schedule: segment by segment, every chain through each, a segment starting from where the
/// chain left the last one with its draw counter at the draw the reference takes next
/// (`out(seed, k·n + i + 1)` for cell `i` of sweep `k`). With `final_t0`, one synchronous sweep at
/// temperature 0 ends the last segment. `chains` are (start, seed); the result is each chain's
/// final state. `mode` is MODE_GIBBS, or MODE_JACOBI for the negative control.
pub fn run_schedule<P: Port>(e: &mut Engine<P>, l: &Loaded, segments: &[Segment], final_t0: bool, chains: &[(Vec<i8>, u64)], first_sweep: u64, mode: u32) -> Result<Vec<Vec<i8>>, String> {
let mut states: Vec<Vec<i8>> = chains.iter().map(|c| c.0.clone()).collect();
let mut done = first_sweep;
for (s, seg) in segments.iter().enumerate() {
let last = s + 1 == segments.len();
e.load_table(seg.win_lo, seg.wbits, seg.first, &seg.levels).map_err(|e| e.0)?;
let first_draw = done * l.n as u64 + 1;
let list: Vec<(Vec<i8>, u64)> = chains.iter().zip(&states).map(|(c, st)| (st.clone(), c.1.wrapping_add(GOLDEN.wrapping_mul(first_draw)))).collect();
let runs = e.run_batch(mode, seg.sweeps as u32, 1, last && final_t0, &list).map_err(|e| e.0)?;
// a sampling job has no early end: the engine reports every sweep it was asked for
if let Some(r) = runs.iter().find(|r| r.steps != seg.sweeps as u32) {
return Err(format!("a chain ran {} sweeps of a segment of {}", r.steps, seg.sweeps));
}
states = runs.into_iter().map(|r| r.state).collect();
done += seg.sweeps as u64;
}
Ok(states)
}
/// `sweeps` synchronous sweeps at temperature 0 from each state, every sweep run.
fn quench<P: Port>(e: &mut Engine<P>, states: &[Vec<i8>], sweeps: u32) -> Result<Vec<Vec<i8>>, String> {
let mut out = states.to_vec();
if sweeps == 0 {
return Ok(out);
}
let runs = e.run_batch(MODE_T0, sweeps, 1, false, &states.iter().map(|s| (s.clone(), 0)).collect::<Vec<_>>()).map_err(|e| e.0)?;
let mut odd = Vec::new();
for (k, r) in runs.into_iter().enumerate() {
if r.term == TERM_2C && (sweeps - r.steps) % 2 == 1 {
odd.push(k);
}
out[k] = r.state;
}
if !odd.is_empty() {
let more = e.run_batch(MODE_T0, 1, 1, false, &odd.iter().map(|&k| (out[k].clone(), 0)).collect::<Vec<_>>()).map_err(|e| e.0)?;
for (&k, r) in odd.iter().zip(more) {
out[k] = r.state;
}
}
Ok(out)
}
impl<P: Port> BoardMember<P> {
pub fn new(engine: Engine<P>, info: BoardInfo) -> Self {
BoardMember { engine: RefCell::new(engine), info, settle_parity: Parity::Uncertified, sample_parity: Parity::Uncertified, jacobi_control: std::cell::Cell::new(false) }
}
/// Has the host certify the member, and keeps what it found for display.
pub fn earn(&mut self) -> Result<String, String> {
let (settle, sample) = {
let c = certify(&*self)?;
(c.parity(KERNEL_SETTLE), c.parity(KERNEL_SAMPLE))
};
self.settle_parity = settle;
self.sample_parity = sample;
Ok(format!("certified by the host on the conformance set: settle {}, sample {}\n", settle.label(), sample.label()))
}
}
impl<P: Port> Member for BoardMember<P> {
fn id(&self) -> String {
format!("fpga:{}-landscape-64", self.info.name)
}
fn class(&self) -> &'static str {
"fpga"
}
fn label(&self) -> String {
format!(
"A classical FPGA board running the settling model's two kernels: {}, up to 64 three-state cells in programmable logic, driven by the board's own processor. Energies are computed exactly on the host. The joule line is priced, as on every member; a reading of the board's power, where one is taken, stands beside it. No photonic hardware, custom chip or quantum effect is involved in this run.",
self.info.description
)
}
/// Declines only what `extensions/quantum-landscape.md` §5 lets a member decline: sampling a
/// workload whose field bound is above 2^31 − 1. (Sweeps at 0 it takes at any field: the host
/// maps them exactly, `lowered_settle`.) Everything else it takes, and what the engine cannot
/// hold, more than 64 variables or sampling fields beyond its 20-bit registers, it then fails
/// with a sentence: the spec does not let a member decline those.
fn takes(&self, w: &Workload, sampling: bool) -> Result<(), String> {
if sampling && w.field_bound() > MUST_TAKE_FIELD_BOUND {
return Err(format!(
"its fields reach {}, past 2^31 - 1, and the engine samples fields of at most ±{FIELD_MAX}",
w.field_bound()
));
}
Ok(())
}
fn settle(&self, w: &Workload, start: &[i8], bound: u32) -> Result<Outcome, String> {
let l = lowered_settle(w);
self.engine.borrow_mut().load(&l).map_err(|e| e.0)?;
let r = self.engine.borrow_mut().run_t0(start, bound).map_err(|e| e.0)?;
let term = match r.term {
TERM_FP => Term::FixedPoint,
TERM_2C => Term::TwoCycle,
_ => Term::Bound,
};
Ok(Outcome { state: r.state, sweeps: r.steps, term, accumulates: u64::from(r.steps) * l.nonzeros(), draws: 0 })
}
fn chains(&self, w: &Workload, chains: &[Chain], temperatures: &[f64]) -> Result<Vec<Outcome>, String> {
let l = if temperatures.iter().all(|&t| t <= 0.0) { lowered_settle(w) } else { lowered(w) };
self.engine.borrow_mut().load(&l).map_err(|e| e.0)?;
let n = l.n;
if let Some(c) = chains.iter().find(|c| c.start.len() != n) {
return Err(format!("a start of {} cells for {n} variables", c.start.len()));
}
if temperatures.iter().any(|t| !t.is_finite()) {
return Err("a temperature that is not a number".into());
}
let len = temperatures.len();
let mut states: Vec<Vec<i8>> = chains.iter().map(|c| c.start.clone()).collect();
let mut e = self.engine.borrow_mut();
let mode = if self.jacobi_control.get() { MODE_JACOBI } else { MODE_GIBBS };
let mut k = 0usize;
while k < len {
let cold = temperatures[k] <= 0.0;
let m = temperatures[k..].iter().take_while(|&&t| (t <= 0.0) == cold).count();
if cold {
states = quench(&mut e, &states, m as u32)?;
k += m;
} else {
// the sweep at 0 that follows, if one does, is the engine's final sweep
let then_cold = k + m < len;
let segments = plan(&l, &temperatures[k..k + m])?;
let list: Vec<(Vec<i8>, u64)> = chains.iter().zip(&states).map(|(c, st)| (st.clone(), c.seed)).collect();
states = run_schedule(&mut e, &l, &segments, then_cold, &list, k as u64, mode)?;
k += m + usize::from(then_cold);
}
}
let hot = temperatures.iter().filter(|&&t| t > 0.0).count() as u64;
Ok(states
.into_iter()
.map(|state| {
let term = if w.rests(&state) { Term::FixedPoint } else { Term::Bound };
Outcome { state, sweeps: len as u32, term, accumulates: len as u64 * l.nonzeros(), draws: n as u64 * hot }
})
.collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::ModelPort;
use wai_quantum::quantum_landscape::energy::Landscape;
fn model() -> BoardMember<ModelPort> {
BoardMember::new(Engine::open(ModelPort::new()).unwrap(), BoardInfo { name: "model".into(), description: "the model".into() })
}
/// Held to the reference on the model, and every field and the cost inside the registers.
fn same_as_reference(m: &BoardMember<ModelPort>, l: &Landscape, start: &[i8]) {
let w = l.lower();
let e = lowered_settle(&w);
assert!(e.cost <= COST_MAX && e.fields.iter().all(|f| f.abs() <= FIELD_MAX), "{e:?}");
assert_eq!(m.settle(&w, start, 40).unwrap(), w.settle(start, 40));
}
fn pair(b0: i64, b1: i64, c: i64) -> Landscape {
let mut l = Landscape::new(4);
l.couple(0, 1, 1).couple(1, 2, -1).couple(2, 3, 1);
l.set_field(0, b0).set_field(1, b1).set_cost(c);
l
}
#[test]
fn a_cost_too_large_keeps_each_threshold_where_it_matters() {
let m = model();
let c = 1 << 40;
same_as_reference(&m, &pair(c, 0, c), &[1, 0, -1, 1]); // the upper threshold in reach
same_as_reference(&m, &pair(-c + 1, 0, c), &[1, 0, -1, 1]); // the lower one in reach
same_as_reference(&m, &pair(0, 3, c), &[1, 0, -1, 1]); // neither: always 0
same_as_reference(&m, &pair(3 * c, 0, c), &[0, 0, 0, 0]); // both below: always +1
same_as_reference(&m, &pair(-3 * c, 0, c), &[0, 0, 0, 0]); // both above: always -1
same_as_reference(&m, &pair(2097152, -2097152, 1048576), &[0, 0, 0, 0]);
}
#[test]
fn a_small_cost_with_a_field_too_large_is_clamped() {
let m = model();
for c in [0, 1, 7, 64] {
same_as_reference(&m, &pair(1_000_000, -1_000_000, c), &[1, 0, -1, 1]);
same_as_reference(&m, &pair(c + 64 + 1, -1_000_000, c), &[0, 0, 0, 0]);
}
}
#[test]
fn every_threshold_case_settles_as_the_reference_does() {
let m = model();
let mut z = 0x001A_46E5u64;
let mut next = move || {
z = z.wrapping_add(GOLDEN);
crate::model::mix(z)
};
for _ in 0..300 {
let n = 2 + (next() % 63) as usize;
let scale = 1i64 << (next() % 41);
let cost = if next() & 1 == 0 { (next() % 70) as i64 } else { (next() as i64 & i64::MAX) % scale };
let mut l = Landscape::new(n);
for i in 0..n {
for j in (i + 1)..n {
if next() % 4 == 0 {
l.couple(i, j, if next() & 1 == 0 { 1 } else { -1 });
}
}
// near a threshold, or anywhere
let b = match next() % 3 {
0 => cost + (next() % 130) as i64 - 65,
1 => -cost + (next() % 130) as i64 - 65,
_ => (next() as i64) % scale,
};
l.set_field(i, b);
}
l.set_cost(cost);
let start: Vec<i8> = (0..n).map(|_| (next() % 3) as i8 - 1).collect();
same_as_reference(&m, &l, &start);
}
}
#[test]
fn a_workload_inside_the_registers_is_left_alone() {
let w = pair(5, -3, 2).lower();
assert_eq!(lowered_settle(&w), lowered(&w));
}
#[test]
fn sampling_beyond_the_registers_is_refused() {
let m = model();
let w = pair(1_000_000, 0, 1).lower();
assert!(m.chains(&w, &[Chain { start: vec![0; 4], seed: 1 }], &[1.0, 0.0]).is_err());
}
/// A port that runs every sampled job one sweep short, by what it reports.
struct ShortSweeps(ModelPort);
impl Port for ShortSweeps {
fn write(&mut self, a: u16, v: u32) -> Result<(), crate::port::Error> {
self.0.write(a, v)
}
fn read(&mut self, a: u16) -> Result<u32, crate::port::Error> {
let v = self.0.read(a)?;
Ok(if a == STEPS { v.saturating_sub(1) } else { v })
}
fn wait_idle(&mut self, t: std::time::Duration) -> Result<(), crate::port::Error> {
self.0.wait_idle(t)
}
}
#[test]
fn a_chain_that_ran_the_wrong_number_of_sweeps_is_refused() {
let m = BoardMember::new(Engine::open(ShortSweeps(ModelPort::new())).unwrap(), BoardInfo::default());
let w = pair(1, 0, 1).lower();
let refused = m.chains(&w, &[Chain { start: vec![0; 4], seed: 1 }], &[1.5, 1.0, 0.5]).expect_err("the count of sweeps");
assert!(refused.contains("sweeps of a segment"), "{refused}");
}
}

View file

@ -0,0 +1,379 @@
//! A software model of `rtl/landscape_core.v` at the register port: the same bits, the same draw
//! order, the same cycle counts. The testbench replays a recording of this model against the RTL
//! and compares every read, so where the two disagree it is the RTL or this file, and the test says
//! which register. Fields are computed from the raw bits exactly as the RTL's cone does.
use crate::port::{Error, Port};
use crate::regs::*;
use std::time::Duration;
/// SplitMix64's mix(), as the draw pipeline computes it.
pub fn mix(mut x: u64) -> u64 {
x ^= x >> 30;
x = x.wrapping_mul(0xBF58_476D_1CE4_E5B9);
x ^= x >> 27;
x = x.wrapping_mul(0x94D0_49BB_1331_11EB);
x ^ (x >> 31)
}
#[derive(Clone)]
pub struct ModelPort {
mode: u32,
rep: bool,
final_t0: bool,
n_m1: u32,
cost: i64,
bound: u32,
spl: u32,
levels_m1: u32,
boundary: [u32; 4],
rows: Vec<[u32; 4]>,
fields: [i64; 64],
state: [u32; 4],
prev: [u32; 4],
tbl: Vec<(u64, u64)>,
tbl_addr: u32,
wa: u64,
wb_lo: u32,
tbl_q: (u64, u64),
x0: u64,
x_next: u64,
primed: bool,
win_lo: i64,
wbits: u32,
sel: u32,
done: bool,
term: u32,
steps: u32,
uptime: u64,
cycles: u64,
updates: u64,
jobs: u64,
rd_hi: u32,
}
impl Default for ModelPort {
fn default() -> Self {
ModelPort {
mode: 0,
rep: false,
final_t0: false,
n_m1: 0,
cost: 0,
bound: 0,
spl: 1,
levels_m1: 0,
boundary: [0; 4],
rows: vec![[0; 4]; 64],
fields: [0; 64],
state: [0; 4],
prev: [0; 4],
tbl: vec![(0, 0); TABLE],
tbl_addr: 0,
wa: 0,
wb_lo: 0,
tbl_q: (0, 0),
x0: 0,
x_next: 0,
primed: false,
win_lo: 0,
wbits: 7,
sel: 0,
done: false,
term: 0,
steps: 0,
uptime: 0,
cycles: 0,
updates: 0,
jobs: 0,
rd_hi: 0,
}
}
}
#[inline]
fn bit(words: &[u32; 4], b: usize) -> u32 {
(words[b >> 5] >> (b & 31)) & 1
}
#[inline]
fn set2(words: &mut [u32; 4], cell: usize, v: u32) {
let b = 2 * cell;
words[b >> 5] = (words[b >> 5] & !(3 << (b & 31))) | ((v & 3) << (b & 31));
}
fn sext(v: u64, bits: u32) -> i64 {
let s = 64 - bits;
((v << s) as i64) >> s
}
impl ModelPort {
pub fn new() -> Self {
Self::default()
}
/// The cone: h_i = sum_j (p_j - n_j) + b_i on the raw bits.
fn field(&self, st: &[u32; 4], i: usize) -> i64 {
let w = &self.rows[i];
let mut h = 0i64;
for j in 0..64 {
let (wp, wn) = (bit(w, 2 * j), bit(w, 2 * j + 1));
let (sp, sn) = (bit(st, 2 * j), bit(st, 2 * j + 1));
h += ((wp & sp) | (wn & sn)) as i64 - ((wp & sn) | (wn & sp)) as i64;
}
h + self.fields[i]
}
fn t0_next(&self, st: &[u32; 4]) -> [u32; 4] {
let mut ns = [0u32; 4];
for i in 0..64 {
let h = self.field(st, i);
set2(&mut ns, i, (((h < -self.cost) as u32) << 1) | (h > self.cost) as u32);
}
ns
}
fn draw(&self) -> u64 {
mix(self.x_next) >> 11
}
/// One cell's update: the S_IDX, S_TBL and S_COMMIT clocks.
fn sample(&mut self, h: i64, level: u32) -> u32 {
let d = h - self.win_lo;
let w = 1i64 << self.wbits;
let in_win = d >= 0 && d < w;
let lbase = ((level as u64) << self.wbits) & 0x1FFF;
let idx = (lbase + (d as u64 & 0x1FFF)) & 0x1FFF;
self.tbl_q = self.tbl[idx as usize];
let u = self.draw();
let s = if in_win {
let (a, b) = self.tbl_q;
if u < a { 0b10 } else if u < b { 0b00 } else { 0b01 }
} else {
(((h < -self.cost) as u32) << 1) | (h > self.cost) as u32
};
self.x_next = self.x_next.wrapping_add(GOLDEN);
s
}
fn run_job(&mut self, load: bool) {
self.done = false;
self.term = TERM_NONE;
let mut cyc = 0u64;
let sampling = self.mode == MODE_GIBBS || self.mode == MODE_JACOBI;
if load {
cyc += 1; // S_LOAD
self.state = self.boundary;
self.prev = self.boundary;
if sampling {
cyc += PRIME; // S_PRIME
self.x_next = self.x0;
self.primed = true;
}
}
let mut prev = self.prev;
let mut prev2 = [0u32; 4];
self.steps = 0;
self.sel = 0;
if self.mode == MODE_T0 {
let (mut have1, mut have2) = (false, false);
loop {
cyc += 1;
if have1 && self.state == prev {
self.term = TERM_FP;
break;
}
if have2 && self.state == prev2 {
self.term = TERM_2C;
break;
}
if self.steps == self.bound {
self.term = TERM_BOUND;
break;
}
let ns = self.t0_next(&self.state);
prev2 = prev;
prev = self.state;
self.state = ns;
self.prev = prev;
self.steps += 1;
have2 = have1;
have1 = true;
}
} else {
let jac = self.mode == MODE_JACOBI;
let (mut level, mut spl_cnt) = (0u32, 0u32);
if self.bound != 0 {
loop {
for i in 0..=(self.n_m1 as usize) {
cyc += 4;
let h = self.field(&self.state, i);
let s = self.sample(h, level);
if jac { set2(&mut prev, i, s) } else { set2(&mut self.state, i, s) }
self.updates += 1;
}
if jac {
cyc += 1;
self.state = prev;
}
self.steps += 1;
if self.steps == self.bound {
break;
}
if spl_cnt + 1 == self.spl {
spl_cnt = 0;
if level != self.levels_m1 {
level += 1;
}
} else {
spl_cnt += 1;
}
}
}
if self.final_t0 {
cyc += 1;
self.prev = self.state;
self.state = self.t0_next(&self.state);
} else {
self.prev = prev;
}
}
cyc += 1; // S_END
self.cycles += cyc;
self.uptime += cyc + 16;
self.jobs += 1;
self.done = true;
}
}
impl Port for ModelPort {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error> {
self.uptime += 8;
match addr {
CONTROL => {
if val & CTL_CLRCNT != 0 {
self.cycles = 0;
self.updates = 0;
self.jobs = 0;
}
if val & CTL_CLEARW != 0 {
self.rows = vec![[0; 4]; 64];
self.fields = [0; 64];
}
if val & CTL_LOADSTATE != 0 {
self.state = self.boundary;
self.prev = self.boundary;
}
if val & CTL_START != 0 {
if self.rep {
return Err(Error("the model does not run repeat mode".into()));
}
self.run_job(true);
} else if val & CTL_CONT != 0 {
if self.rep || self.bound == 0 {
return Err(Error("CONT needs repeat off and a bound above 0".into()));
}
self.run_job(false);
}
if val & CTL_STOP != 0 {
self.done = false;
}
}
MODE => {
self.mode = val & 3;
self.rep = val & MODE_REPEAT != 0;
self.final_t0 = val & MODE_FINAL_T0 != 0;
}
N_M1 => self.n_m1 = val & 63,
COST => self.cost = (val & 0xF_FFFF) as i64,
BOUND => self.bound = val,
SPL => self.spl = val & 0xFFFF,
LEVELS_M1 => self.levels_m1 = val & 63,
X0_LO => self.x0 = (self.x0 & !0xFFFF_FFFF) | val as u64,
X0_HI => self.x0 = (self.x0 & 0xFFFF_FFFF) | ((val as u64) << 32),
WIN_LO => self.win_lo = sext(val as u64 & 0x3F_FFFF, 22),
WBITS => self.wbits = val & 15,
SEL => self.sel = val & 63,
TBL_ADDR => {
self.tbl_addr = val & 0x1FFF;
self.tbl_q = self.tbl[self.tbl_addr as usize];
}
TBL_A_LO => self.wa = (self.wa & !0xFFFF_FFFF) | val as u64,
TBL_A_HI => self.wa = (self.wa & 0xFFFF_FFFF) | (((val & 0x3F_FFFF) as u64) << 32),
TBL_B_LO => self.wb_lo = val,
TBL_B_HI => {
let b = (((val & 0x3F_FFFF) as u64) << 32) | self.wb_lo as u64;
self.tbl[self.tbl_addr as usize] = (self.wa, b);
self.tbl_addr = (self.tbl_addr + 1) & 0x1FFF;
}
a if (BOUNDARY..BOUNDARY + 4).contains(&a) => self.boundary[(a - BOUNDARY) as usize] = val,
a if (FIELDS..FIELDS + 64).contains(&a) => self.fields[(a - FIELDS) as usize] = sext(val as u64 & 0xF_FFFF, 20),
a if (W..W + 256).contains(&a) => {
let k = (a - W) as usize;
self.rows[k >> 2][k & 3] = val;
}
_ => {}
}
Ok(())
}
fn read(&mut self, addr: u16) -> Result<u32, Error> {
self.uptime += 8;
let lo_hi = |v: u64, s: &mut u32| {
*s = (v >> 32) as u32 & 0xFFFF;
v as u32
};
Ok(match addr {
ID => ID_VALUE,
VERSION => VERSION_VALUE,
STATUS => (self.term << 2) | ((self.done as u32) << 1),
MODE => ((self.final_t0 as u32) << 9) | ((self.rep as u32) << 8) | self.mode,
N_M1 => self.n_m1,
COST => self.cost as u32,
BOUND => self.bound,
WIN_LO => self.win_lo as i32 as u32,
WBITS => self.wbits,
FIELD => (self.field(&self.state, self.sel as usize) as i32) as u32,
TBL_ADDR => self.tbl_addr,
TBL_A_LO => self.tbl_q.0 as u32,
TBL_A_HI => (self.tbl_q.0 >> 32) as u32 & 0x3F_FFFF,
TBL_B_LO => self.tbl_q.1 as u32,
TBL_B_HI => (self.tbl_q.1 >> 32) as u32 & 0x3F_FFFF,
DRAW_LO => {
let u = self.draw();
self.rd_hi = (u >> 32) as u32;
u as u32
}
DRAW_HI => self.rd_hi,
UPTIME => { let u = self.uptime; lo_hi(u, &mut self.rd_hi) }
CYCLES => { let u = self.cycles; lo_hi(u, &mut self.rd_hi) }
UPDATES => { let u = self.updates; lo_hi(u, &mut self.rd_hi) }
JOBS => { let u = self.jobs; lo_hi(u, &mut self.rd_hi) }
0x0021 | 0x0023 | 0x0025 | 0x0027 => self.rd_hi,
STEPS => self.steps,
a if (STATE..STATE + 4).contains(&a) => self.state[(a - STATE) as usize],
_ => 0xDEAD_BEEF,
})
}
fn wait_idle(&mut self, _timeout: Duration) -> Result<(), Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
/// The model's draws are the reference's: the engine's counter starts at chain + GOLDEN and
/// the j-th draw of a chain is out(chain, j) >> 11.
#[test]
fn the_draw_is_the_reference_s() {
use wai_quantum::quantum_landscape::sample::out;
let chain = 0x1234_5678_9ABC_DEF0u64;
let mut x = chain.wrapping_add(GOLDEN);
for j in 1..=1000u64 {
assert_eq!(mix(x), out(chain, j));
x = x.wrapping_add(GOLDEN);
}
}
}

View file

@ -0,0 +1,135 @@
//! A register port. The board's is the AXI link (`axi.rs`) or the EMIO lines (`emio.rs`); the
//! model's is software (`model.rs`); the testbench script records one (`script.rs`). Everything
//! above this trait is shared by all of them.
//!
//! Two operations have a plain meaning in register accesses and a faster one on the AXI link:
//! [`Port::bulk`], a list of writes in order (the AXI link reads it from DDR by DMA), and
//! [`Port::batch`], a run of chains (the AXI link's sequencer runs them in fabric). The defaults
//! here are the plain meaning, and the fast forms must give the same bits.
use crate::regs::{BOUNDARY, CONTROL, CTL_START, STATE, STATUS, STEPS, X0_HI, X0_LO};
use std::fmt;
use std::time::Duration;
/// One chain for [`Port::batch`]: BOUNDARY 0-3, X0_LO, X0_HI.
pub type ChainWords = [u32; 6];
/// What a chain leaves: STATE 0-3, STEPS, STATUS.
pub type ResultWords = [u32; 6];
#[derive(Debug, Clone, PartialEq)]
pub struct Error(pub String);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Error {}
pub fn err<T>(s: impl Into<String>) -> Result<T, Error> {
Err(Error(s.into()))
}
pub trait Port {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error>;
fn read(&mut self, addr: u16) -> Result<u32, Error>;
/// Poll until the engine is idle. Errs on timeout rather than waiting forever.
fn wait_idle(&mut self, timeout: Duration) -> Result<(), Error>;
/// Writes, in order.
fn bulk(&mut self, list: &[(u16, u32)]) -> Result<(), Error> {
bulk_plain(self, list)
}
/// For each chain: its start and draw counter written, the job started with the mode, bound and
/// table already set, waited for, and its state, sweeps and status read back.
fn batch(&mut self, chains: &[ChainWords], timeout: Duration) -> Result<Vec<ResultWords>, Error> {
batch_plain(self, chains, timeout)
}
}
/// [`Port::bulk`] as plain register writes.
pub fn bulk_plain<P: Port + ?Sized>(p: &mut P, list: &[(u16, u32)]) -> Result<(), Error> {
for &(a, v) in list {
p.write(a, v)?;
}
Ok(())
}
/// [`Port::batch`] as plain register accesses: what the AXI link's sequencer does, from the host.
pub fn batch_plain<P: Port + ?Sized>(p: &mut P, chains: &[ChainWords], timeout: Duration) -> Result<Vec<ResultWords>, Error> {
let mut out = Vec::with_capacity(chains.len());
for c in chains {
for (k, &w) in c[..4].iter().enumerate() {
p.write(BOUNDARY + k as u16, w)?;
}
p.write(X0_LO, c[4])?;
p.write(X0_HI, c[5])?;
p.write(CONTROL, CTL_START)?;
p.wait_idle(timeout)?;
let mut r = [0u32; 6];
for (k, w) in r[..4].iter_mut().enumerate() {
*w = p.read(STATE + k as u16)?;
}
r[4] = p.read(STEPS)?;
r[5] = p.read(STATUS)?;
out.push(r);
}
Ok(out)
}
/// One core of the multi-core bitstream (`rtl/landscape_multi.v`): address bits 15:13 pick it.
pub struct CorePort<'a, P: Port> {
pub inner: &'a mut P,
pub core: u16,
}
impl<P: Port> Port for CorePort<'_, P> {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error> {
self.inner.write((self.core << 13) | (addr & 0x1FFF), val)
}
fn read(&mut self, addr: u16) -> Result<u32, Error> {
self.inner.read((self.core << 13) | (addr & 0x1FFF))
}
fn wait_idle(&mut self, timeout: Duration) -> Result<(), Error> {
self.inner.wait_idle(timeout)
}
}
/// Every write goes to all `k` cores, reads come from core 0: the same job on every core at once.
/// For power, where K identical copies amplify a signal the one-core meter cannot resolve.
pub struct BroadcastPort<P: Port> {
pub inner: P,
pub k: u16,
}
impl<P: Port> Port for BroadcastPort<P> {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error> {
for c in 0..self.k {
self.inner.write((c << 13) | (addr & 0x1FFF), val)?;
}
Ok(())
}
fn read(&mut self, addr: u16) -> Result<u32, Error> {
self.inner.read(addr & 0x1FFF)
}
fn wait_idle(&mut self, timeout: Duration) -> Result<(), Error> {
self.inner.wait_idle(timeout)
}
}
impl<P: Port + ?Sized> Port for &mut P {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error> {
(**self).write(addr, val)
}
fn read(&mut self, addr: u16) -> Result<u32, Error> {
(**self).read(addr)
}
fn wait_idle(&mut self, timeout: Duration) -> Result<(), Error> {
(**self).wait_idle(timeout)
}
fn bulk(&mut self, list: &[(u16, u32)]) -> Result<(), Error> {
(**self).bulk(list)
}
fn batch(&mut self, chains: &[ChainWords], timeout: Duration) -> Result<Vec<ResultWords>, Error> {
(**self).batch(chains, timeout)
}
}

View file

@ -0,0 +1,250 @@
//! Run/halt power on one bitstream, by the board's written rules:
//!
//! * no reload between the two states, so placement, routing and clock tree are identical and the
//! difference is the job's dynamic power;
//! * the order of run and halt is randomised in every pass, so a monotonic drift cannot alias onto it;
//! * sampling happens on the board, in this process, with nothing else running: a bare read of the
//! sensor and a sleep (the PS is inside the measurement);
//! * every point checks for a reload by another session two ways: the engine's uptime counter must
//! have kept counting (a reload resets it, even of the same file), and the dmesg count of FPGA
//! writes must not have moved. A flagged pass is dropped, never averaged in;
//! * the statistic is the mean of per-pass differences of medians, its error the standard error
//! across passes (n = passes, not samples);
//! * `--predict` prints a figure beside the result. It is only what the command line said: what
//! makes a prediction pre-registered is the file committed before the run, `measurements/*/`.
#[cfg(target_os = "linux")]
use crate::board;
#[cfg(target_os = "linux")]
use crate::engine::Engine;
#[cfg(target_os = "linux")]
use crate::port::{Error, Port};
#[cfg(target_os = "linux")]
use crate::regs::*;
use std::fmt::Write as _;
#[cfg(target_os = "linux")]
use std::time::{Duration, Instant, SystemTime};
pub struct Plan {
pub passes: u32,
pub settle_s: u64,
pub samples: u32,
/// "halt" for an A/A null control: both slots halted, one of them labelled run.
pub null: bool,
}
pub struct Point {
pub pass: u32,
pub run: bool,
pub samples: Vec<f64>, // mW
pub jobs_per_s: f64,
pub updates_per_s: f64,
pub clock_hz: f64,
pub flagged: Option<String>,
}
#[cfg(target_os = "linux")]
fn urandom_bit() -> bool {
let mut b = [0u8; 1];
use std::io::Read;
if let Ok(mut f) = std::fs::File::open("/dev/urandom") {
let _ = f.read_exact(&mut b);
}
b[0] & 1 == 1
}
/// `start` puts the engine into repeat mode on the prepared job; the meter halts it for the halt slot.
#[cfg(target_os = "linux")]
pub fn meter<P: Port>(edge: &mut Engine<P>, plan: &Plan, start: &mut dyn FnMut(&mut Engine<P>) -> Result<(), Error>, log: &mut String) -> Result<Vec<Point>, Error> {
let sensor = board::find_power_sensor()?;
let mut pts = vec![];
let _ = writeln!(log, "# wai-quantum-landscape-fpga meter passes={} settle={}s samples={} null={} sensor={sensor} started={:?}", plan.passes, plan.settle_s, plan.samples, plan.null, SystemTime::now());
let _ = writeln!(log, "# pass state sample power_uW");
for pass in 1..=plan.passes {
let order = if urandom_bit() { [true, false] } else { [false, true] };
for run in order {
if run && !plan.null {
start(edge)?;
} else {
edge.halt()?;
}
let w0 = board::dmesg_fpga_writes()?.len();
let id0 = edge.port.read(ID)?;
let up0 = edge.counter(UPTIME)?;
std::thread::sleep(Duration::from_secs(plan.settle_s));
// the window: counters read just before and just after, sensor only in between
let j0 = edge.counter(JOBS)?;
let u0 = edge.counter(UPDATES)?;
let c0 = edge.counter(UPTIME)?;
let t0 = Instant::now();
let mut s = Vec::with_capacity(plan.samples as usize);
for _ in 0..plan.samples {
s.push(board::read_uw(&sensor)? as f64 / 1000.0);
std::thread::sleep(Duration::from_secs(1));
}
let dt = t0.elapsed().as_secs_f64();
let c1 = edge.counter(UPTIME)?;
let j1 = edge.counter(JOBS)?;
let u1 = edge.counter(UPDATES)?;
let id1 = edge.port.read(ID)?;
let w1 = board::dmesg_fpga_writes()?.len();
let mut flagged = None;
if id0 != ID_VALUE || id1 != ID_VALUE {
flagged = Some(format!("ID {id0:#x}/{id1:#x}"));
} else if !(up0 <= c0 && c0 < c1) {
flagged = Some(format!("uptime went {up0} -> {c0} -> {c1}: the PL was reconfigured"));
} else if w1 != w0 {
flagged = Some(format!("dmesg FPGA writes {w0} -> {w1}"));
}
let label = if run { "run" } else { "halt" };
for (k, v) in s.iter().enumerate() {
let _ = writeln!(log, "{pass} {label} {} {}", k + 1, (v * 1000.0).round() as u64);
}
let clock = (c1 - c0) as f64 / dt;
let _ = writeln!(log, "# point pass={pass} state={label} jobs_per_s={:.1} updates_per_s={:.1} clock_hz={clock:.0}{}", (j1 - j0) as f64 / dt, (u1 - u0) as f64 / dt,
flagged.as_ref().map(|f| format!(" FLAGGED {f}")).unwrap_or_default());
pts.push(Point { pass, run, samples: s, jobs_per_s: (j1 - j0) as f64 / dt, updates_per_s: (u1 - u0) as f64 / dt, clock_hz: clock, flagged });
}
}
edge.halt()?;
let _ = writeln!(log, "# done {:?}", SystemTime::now());
Ok(pts)
}
pub struct Summary {
pub text: String,
pub delta_mw: f64,
pub se_mw: f64,
pub passes_used: usize,
pub jobs_per_s: f64,
pub updates_per_s: f64,
pub clock_hz: f64,
pub p_run_mw: f64,
}
fn excursions(s: &[f64]) -> usize {
let m = median(s);
let mad = median(&s.iter().map(|x| (x - m).abs()).collect::<Vec<_>>()).max(5.0);
s.iter().filter(|x| (*x - m).abs() > 6.0 * mad).count()
}
pub fn summarise(pts: &[Point], predicted_mw: Option<f64>) -> Summary {
let mut t = String::new();
let passes: Vec<u32> = {
let mut v: Vec<u32> = pts.iter().map(|p| p.pass).collect();
v.dedup();
v
};
let mut diffs = vec![];
let mut dropped = vec![];
let (mut jps, mut ups, mut clk, mut prun) = (vec![], vec![], vec![], vec![]);
let mut exc = 0;
for &p in &passes {
let r = pts.iter().find(|x| x.pass == p && x.run);
let h = pts.iter().find(|x| x.pass == p && !x.run);
if let (Some(r), Some(h)) = (r, h) {
if let Some(f) = r.flagged.as_ref().or(h.flagged.as_ref()) {
dropped.push(format!("pass {p}: {f}"));
continue;
}
exc += excursions(&r.samples) + excursions(&h.samples);
let d = median(&r.samples) - median(&h.samples);
diffs.push(d);
jps.push(r.jobs_per_s);
ups.push(r.updates_per_s);
clk.push(r.clock_hz);
prun.push(median(&r.samples));
}
}
let (m, sd) = mean_sd(&diffs);
let se = sd / (diffs.len() as f64).sqrt();
let pos = diffs.iter().filter(|&&d| d > 0.0).count();
let _ = writeln!(t, "passes used {} of {} (dropped: {:?})", diffs.len(), passes.len(), dropped);
let _ = writeln!(t, "run - halt = {m:.1} +/- {se:.1} mW (per-pass sd {sd:.1}); {pos}/{} passes positive; {exc} sample excursions beyond 6 MAD", diffs.len());
let _ = writeln!(t, "per-pass differences (mW): {}", diffs.iter().map(|d| format!("{d:.1}")).collect::<Vec<_>>().join(" "));
let (j, _) = mean_sd(&jps);
let (u, _) = mean_sd(&ups);
let (c, _) = mean_sd(&clk);
let (pr, _) = mean_sd(&prun);
let _ = writeln!(t, "board power while running {pr:.0} mW; jobs/s {j:.1}; updates/s {u:.1}; measured PL clock {:.3} MHz", c / 1e6);
if let Some(p) = predicted_mw {
let _ = writeln!(t, "prediction given {p:.1} mW: measured/predicted = {:.2}, {:.1} sigma from it", m / p, (m - p) / se);
}
Summary { text: t, delta_mw: m, se_mw: se, passes_used: diffs.len(), jobs_per_s: j, updates_per_s: u, clock_hz: c, p_run_mw: pr }
}
/// Parses a meter log back into points (so a run can be re-analysed anywhere).
pub fn parse_log(text: &str) -> Vec<Point> {
let mut pts: Vec<Point> = vec![];
// kv_runhalt.sh (the September known-answer vehicle's script) flags interference this way
let mut foreign: Vec<u32> = vec![];
for line in text.lines() {
for tag in ["# FOREIGN-LOAD pass=", "# NOT-OURS pass="] {
if let Some(r) = line.strip_prefix(tag) {
if let Some(p) = r.split_whitespace().next().and_then(|t| t.parse().ok()) {
foreign.push(p);
}
}
}
}
for line in text.lines() {
if let Some(rest) = line.strip_prefix("# point ") {
let get = |k: &str| rest.split_whitespace().find_map(|t| t.strip_prefix(&format!("{k}="))).unwrap_or("").to_string();
let pass: u32 = get("pass").parse().unwrap_or(0);
let run = get("state") == "run";
if let Some(p) = pts.iter_mut().rev().find(|p| p.pass == pass && p.run == run) {
p.jobs_per_s = get("jobs_per_s").parse().unwrap_or(0.0);
p.updates_per_s = get("updates_per_s").parse().unwrap_or(0.0);
p.clock_hz = get("clock_hz").parse().unwrap_or(0.0);
if rest.contains("FLAGGED") {
p.flagged = Some(rest.split("FLAGGED").nth(1).unwrap_or("").trim().to_string());
}
}
continue;
}
if line.starts_with('#') || line.trim().is_empty() {
continue;
}
let f: Vec<&str> = line.split_whitespace().collect();
if f.len() < 4 {
continue;
}
let (Ok(pass), Ok(uw)) = (f[0].parse::<u32>(), f[3].parse::<f64>()) else { continue };
let run = f[1] == "run";
match pts.iter_mut().find(|p| p.pass == pass && p.run == run) {
Some(p) => p.samples.push(uw / 1000.0),
None => pts.push(Point { pass, run, samples: vec![uw / 1000.0], jobs_per_s: 0.0, updates_per_s: 0.0, clock_hz: 0.0, flagged: None }),
}
}
for p in pts.iter_mut() {
if foreign.contains(&p.pass) && p.flagged.is_none() {
p.flagged = Some("foreign FPGA load in this pass (kv_runhalt.sh)".into());
}
}
pts
}
pub fn mean_sd(v: &[f64]) -> (f64, f64) {
let n = v.len() as f64;
if v.is_empty() {
return (f64::NAN, f64::NAN);
}
let m = v.iter().sum::<f64>() / n;
if v.len() < 2 {
return (m, f64::NAN);
}
(m, (v.iter().map(|x| (x - m) * (x - m)).sum::<f64>() / (n - 1.0)).sqrt())
}
pub fn median(v: &[f64]) -> f64 {
let mut s = v.to_vec();
s.sort_by(|a, b| a.partial_cmp(b).unwrap());
let n = s.len();
if n == 0 {
f64::NAN
} else if n % 2 == 1 {
s[n / 2]
} else {
0.5 * (s[n / 2 - 1] + s[n / 2])
}
}

View file

@ -0,0 +1,69 @@
//! The engine's register map. Word addresses; `rtl/landscape_core.v` is the other half of this file.
pub const ID: u16 = 0x0000;
pub const ID_VALUE: u32 = 0x4C41_4E44; // "LAND"
pub const VERSION: u16 = 0x0001;
pub const VERSION_VALUE: u32 = 0x0040_4002; // 64 cells, 64 levels, rtl 2
pub const STATUS: u16 = 0x0002; // [0] busy [1] done [3:2] term
pub const CONTROL: u16 = 0x0003;
pub const MODE: u16 = 0x0004; // [1:0] mode [8] repeat [9] final T0 sweep
pub const N_M1: u16 = 0x0005;
pub const COST: u16 = 0x0006; // 20 bits
pub const BOUND: u16 = 0x0007; // T0 sweep bound, or sweeps
pub const SPL: u16 = 0x0008; // sweeps per temperature level
pub const LEVELS_M1: u16 = 0x0009;
pub const X0_LO: u16 = 0x0010; // the draw counter's start, chain seed + GOLDEN
pub const X0_HI: u16 = 0x0011;
pub const WIN_LO: u16 = 0x0012; // the table window's first field, signed
pub const WBITS: u16 = 0x0013; // the window holds 2^WBITS fields
pub const SEL: u16 = 0x0014;
pub const FIELD: u16 = 0x0015;
pub const TBL_ADDR: u16 = 0x0016;
pub const TBL_A_LO: u16 = 0x0017;
pub const TBL_A_HI: u16 = 0x0018;
pub const TBL_B_LO: u16 = 0x0019;
pub const TBL_B_HI: u16 = 0x001A; // writing it commits the pair and steps the address
pub const DRAW_LO: u16 = 0x001B; // the draw the next cell would take (53 bits)
pub const DRAW_HI: u16 = 0x001C;
pub const UPTIME: u16 = 0x0020;
pub const CYCLES: u16 = 0x0022;
pub const UPDATES: u16 = 0x0024;
pub const JOBS: u16 = 0x0026;
pub const STEPS: u16 = 0x0028;
pub const BOUNDARY: u16 = 0x0100;
pub const STATE: u16 = 0x0110;
pub const FIELDS: u16 = 0x0200; // one per cell, 20 bits signed
pub const W: u16 = 0x1000;
pub const CTL_START: u32 = 1;
pub const CTL_CONT: u32 = 2;
pub const CTL_STOP: u32 = 4;
pub const CTL_LOADSTATE: u32 = 8;
pub const CTL_CLEARW: u32 = 16;
pub const CTL_CLRCNT: u32 = 32;
pub const MODE_T0: u32 = 0;
pub const MODE_GIBBS: u32 = 1;
pub const MODE_JACOBI: u32 = 2;
pub const MODE_REPEAT: u32 = 1 << 8;
pub const MODE_FINAL_T0: u32 = 1 << 9;
pub const CELLS: usize = 64;
pub const LEVELS: usize = 64;
pub const TABLE: usize = 8192;
pub const MAX_WBITS: u32 = 13;
/// The draw pipeline's depth: a job that samples spends this many clocks priming it.
pub const PRIME: u64 = 9;
/// The largest field a cell's register holds, either sign.
pub const FIELD_MAX: i64 = (1 << 19) - 1;
/// The largest cost the register holds.
pub const COST_MAX: i64 = (1 << 20) - 1;
/// SplitMix64's increment.
pub const GOLDEN: u64 = 0x9E37_79B9_7F4A_7C15;
/// The edge of the 53-bit scale: a cut point of this size means "always below".
pub const TWO53: u64 = 1 << 53;
pub const TERM_NONE: u32 = 0;
pub const TERM_FP: u32 = 1;
pub const TERM_2C: u32 = 2;
pub const TERM_BOUND: u32 = 3;

View file

@ -0,0 +1,114 @@
//! Records a session against the model as a script the RTL testbench replays: every write, every read
//! with the value the model returned (the RTL must return the same), and every wait.
//!
//! W aaaa dddddddd write
//! R aaaa dddddddd mmmmmmmm read, expect (value & mask) == (d & mask)
//! I wait until not busy
//!
//! In AXI form (`tb/tb_landscape_axi.v`) a bulk list and a batch of chains go as the AXI link
//! carries them, so the testbench drives the DMA and the sequencer:
//!
//! D n the next n lines, `aaaa dddddddd`, are a write list: put it in memory
//! and run the DMA over it
//! Q n run the sequencer over n chains (their words already in the chain RAM)
//! and wait for it
//! K n write the batch's n results back to memory; the next 6n lines are the
//! words each must hold
//!
//! and a batch's results are also checked as reads of the result RAM.
use crate::axi::{CHAIN_RAM, RESULT_RAM, WRAP_DROPS};
use crate::model::ModelPort;
use crate::port::{ChainWords, Error, Port, ResultWords};
use crate::regs;
use std::fmt::Write as _;
use std::time::Duration;
pub struct ScriptPort {
pub model: ModelPort,
pub text: String,
pub reads: usize,
/// Record bulk lists and batches in the AXI form.
pub axi: bool,
}
impl ScriptPort {
pub fn new() -> Self {
ScriptPort { model: ModelPort::new(), text: String::new(), reads: 0, axi: false }
}
pub fn axi() -> Self {
ScriptPort { axi: true, ..Self::new() }
}
fn expect(&mut self, addr: u16, v: u32, mask: u32) {
let _ = writeln!(self.text, "R {addr:04x} {v:08x} {mask:08x}");
self.reads += 1;
}
}
impl Default for ScriptPort {
fn default() -> Self {
Self::new()
}
}
impl Port for ScriptPort {
fn write(&mut self, addr: u16, val: u32) -> Result<(), Error> {
let _ = writeln!(self.text, "W {addr:04x} {val:08x}");
self.model.write(addr & 0x1FFF, val)
}
fn read(&mut self, addr: u16) -> Result<u32, Error> {
let v = self.model.read(addr & 0x1FFF)?;
let a = addr & 0x1FFF;
// uptime counts real clocks, which the model cannot know
let mask = if a == regs::UPTIME || a == regs::UPTIME + 1 { 0u32 } else { 0xFFFF_FFFFu32 };
self.expect(addr, v, mask);
Ok(v)
}
fn wait_idle(&mut self, t: Duration) -> Result<(), Error> {
self.text.push_str("I\n");
self.model.wait_idle(t)
}
fn bulk(&mut self, list: &[(u16, u32)]) -> Result<(), Error> {
if !self.axi {
return crate::port::bulk_plain(self, list);
}
let _ = writeln!(self.text, "D {}", list.len());
for &(a, v) in list {
let _ = writeln!(self.text, "{a:04x} {v:08x}");
self.model.write(a & 0x1FFF, v)?;
}
Ok(())
}
fn batch(&mut self, chains: &[ChainWords], t: Duration) -> Result<Vec<ResultWords>, Error> {
if !self.axi {
return crate::port::batch_plain(self, chains, t);
}
let mut out = Vec::with_capacity(chains.len());
for group in chains.chunks(crate::axi::SEQ_MAX) {
// the chain words by DMA, the sequencer, then every result word checked
let _ = writeln!(self.text, "D {}", group.len() * 6);
for (k, c) in group.iter().enumerate() {
for (i, &w) in c.iter().enumerate() {
let _ = writeln!(self.text, "{:04x} {w:08x}", CHAIN_RAM + (8 * k + i) as u16);
}
}
let _ = writeln!(self.text, "Q {}", group.len());
let res = self.model.batch(group, t)?;
for (k, r) in res.iter().enumerate() {
for (i, &w) in r.iter().enumerate() {
self.expect(RESULT_RAM + (8 * k + i) as u16, w, 0xFFFF_FFFF);
}
}
self.expect(WRAP_DROPS, 0, 0xFFFF_FFFF);
let _ = writeln!(self.text, "K {}", group.len());
for r in &res {
for &w in r {
let _ = writeln!(self.text, "{w:08x}");
}
self.reads += r.len();
}
out.extend(res);
}
Ok(out)
}
}

View file

@ -0,0 +1,81 @@
// SPDX-License-Identifier: Apache-2.0
// Replays a session recorded from the host's software model (`wai-quantum-landscape-fpga gen-tb landscape_tb.script`) against the RTL,
// through the EMIO port and its synchroniser, with the PS side deliberately off the PL clock.
// Every read must return what the model returned. A mismatch names the script line.
`timescale 1ns/1ps
module tb;
reg clk = 0;
always #5 clk = ~clk; // pl_clk0, 100 MHz
reg [94:0] emio_o = 95'd0;
wire [94:0] emio_i;
`ifdef MULTI
landscape_multi #(.K(4)) dut (.clk(clk), .emio_o(emio_o), .emio_i(emio_i));
`else
landscape_port dut (.clk(clk), .emio_o(emio_o), .emio_i(emio_i));
`endif
integer fd, r, errors, reads, line, t;
reg [31:0] op, a, d, m, got;
reg req;
task txn(input we, input [15:0] addr, input [31:0] wdata, output [31:0] rdata);
reg ack0;
begin
ack0 = emio_i[82];
emio_o[31:0] = wdata;
#37; // a PS bus write, some time before the next
req = ~req;
emio_o[81] = we;
emio_o[79:64] = addr;
emio_o[80] = req;
t = 0;
while (emio_i[82] == ack0) begin
#13;
t = t + 1;
if (t > 20000) begin
$display("TIMEOUT waiting for ack at script line %0d", line);
$finish;
end
end
rdata = emio_i[63:32];
#29;
end
endtask
initial begin
errors = 0; reads = 0; line = 0; req = 0;
#3000; // past the port's power-on hold-off
fd = $fopen("landscape_tb.script", "r");
if (fd == 0) begin $display("no script"); $finish; end
r = $fscanf(fd, "%s", op);
while (r == 1) begin
line = line + 1;
if (op == "W") begin
r = $fscanf(fd, " %h %h", a, d);
txn(1'b1, a[15:0], d, got);
end else if (op == "R") begin
r = $fscanf(fd, " %h %h %h", a, d, m);
txn(1'b0, a[15:0], 32'd0, got);
reads = reads + 1;
if ((got & m) !== (d & m)) begin
errors = errors + 1;
if (errors <= 25) $display("MISMATCH line %0d: R %h got %h want %h", line, a[15:0], got, d);
end
end else if (op == "I") begin
t = 0;
while (emio_i[83]) begin
#50;
t = t + 1;
if (t > 2000000) begin $display("TIMEOUT busy at line %0d", line); $finish; end
end
#20;
end else begin
$display("bad op at line %0d", line);
$finish;
end
r = $fscanf(fd, "%s", op);
end
$display("TB DONE: %0d lines, %0d checked reads, %0d mismatches, sim time %0t", line, reads, errors, $time);
$finish;
end
endmodule

View file

@ -0,0 +1,286 @@
// SPDX-License-Identifier: Apache-2.0
// Replays a session recorded from the host's software model in its AXI form (`gen-tb --axi`)
// against the AXI link (rtl/landscape_axi.v) and the engine behind it: register accesses over
// AXI-Lite, write lists through the DMA from a memory model that answers with irregular delays and
// several bursts outstanding, and batches of chains through the sequencer, whose results are read
// from the result RAM and written back to the memory model. Every read and every word written back
// must be what the model returned. A mismatch names the line.
`timescale 1ns/1ps
module tb;
reg clk = 0;
always #5 clk = ~clk; // pl_clk0, 100 MHz
reg rstn = 0;
// AXI-Lite master
reg [31:0] awaddr = 0, wdata = 0, araddr = 0;
reg awvalid = 0, wvalid = 0, bready = 0, arvalid = 0, rready = 0;
wire awready, wready, bvalid, arready, rvalid;
wire [1:0] bresp, rresp;
wire [31:0] rdata;
// DMA read port
wire [48:0] m_araddr;
wire [7:0] m_arlen;
wire [2:0] m_arsize, m_arprot;
wire [1:0] m_arburst;
wire [3:0] m_arcache;
wire m_arvalid, m_rready;
reg m_arready = 0, m_rvalid = 0, m_rlast = 0;
reg [63:0] m_rdata = 0;
reg [1:0] m_rresp = 0;
// DMA write port
wire [48:0] m_awaddr;
wire [7:0] m_awlen, m_wstrb;
wire [2:0] m_awsize, m_awprot;
wire [1:0] m_awburst;
wire [3:0] m_awcache;
wire [63:0] m_wdata;
wire m_awvalid, m_wlast, m_wvalid, m_bready;
reg m_awready = 0, m_wready = 0, m_bvalid = 0;
reg [1:0] m_bresp = 0;
wire [94:0] emio_i;
landscape_axi dut (
.clk(clk), .rstn(rstn),
.s_awaddr(awaddr), .s_awvalid(awvalid), .s_awready(awready), .s_wdata(wdata), .s_wvalid(wvalid),
.s_wready(wready), .s_bresp(bresp), .s_bvalid(bvalid), .s_bready(bready),
.s_araddr(araddr), .s_arvalid(arvalid), .s_arready(arready), .s_rdata(rdata), .s_rresp(rresp),
.s_rvalid(rvalid), .s_rready(rready),
.m_araddr(m_araddr), .m_arlen(m_arlen), .m_arsize(m_arsize), .m_arburst(m_arburst), .m_arcache(m_arcache),
.m_arprot(m_arprot), .m_arvalid(m_arvalid), .m_arready(m_arready), .m_rdata(m_rdata), .m_rresp(m_rresp),
.m_rlast(m_rlast), .m_rvalid(m_rvalid), .m_rready(m_rready),
.m_awaddr(m_awaddr), .m_awlen(m_awlen), .m_awsize(m_awsize), .m_awburst(m_awburst), .m_awcache(m_awcache),
.m_awprot(m_awprot), .m_awvalid(m_awvalid), .m_awready(m_awready), .m_wdata(m_wdata), .m_wstrb(m_wstrb),
.m_wlast(m_wlast), .m_wvalid(m_wvalid), .m_wready(m_wready), .m_bresp(m_bresp), .m_bvalid(m_bvalid), .m_bready(m_bready),
.emio_i(emio_i)
);
// ---- DDR: a memory model behind the high-performance port --------------------------------------
localparam [48:0] BASE = 49'h0_1000_0000;
reg [63:0] mem [0:65535];
reg [48:0] q_addr [0:15];
reg [7:0] q_len [0:15];
reg [4:0] q_head = 0, q_tail = 0;
reg [8:0] beat = 0;
integer seed = 7;
wire [4:0] q_used = q_tail - q_head;
always @(posedge clk) begin
// address channel: accept when there is room, sometimes late
m_arready <= 1'b0;
if (m_arvalid && !m_arready && q_used < 5'd8 && ($random(seed) & 3) != 0) begin
m_arready <= 1'b1;
q_addr[q_tail[3:0]] <= m_araddr;
q_len[q_tail[3:0]] <= m_arlen;
q_tail <= q_tail + 5'd1;
if (m_arsize != 3'd3 || m_arburst != 2'b01) begin $display("DMA: bad size or burst"); $finish; end
if ((m_araddr[11:0] + ({4'd0, m_arlen} + 12'd1) * 12'd8) > 13'h1000 && m_araddr[11:0] != 12'd0) begin
$display("DMA: a burst crosses a 4 KiB page at %h", m_araddr); $finish;
end
end
// data channel: one beat at a time, with gaps
if (m_rvalid && m_rready) begin
m_rvalid <= 1'b0;
if (m_rlast) begin q_head <= q_head + 5'd1; beat <= 0; end
else beat <= beat + 9'd1;
end else if (!m_rvalid && q_used != 5'd0 && ($random(seed) % 5) != 0) begin
m_rvalid <= 1'b1;
m_rdata <= mem[(q_addr[q_head[3:0]] - BASE) / 8 + beat];
m_rlast <= (beat == {1'b0, q_len[q_head[3:0]]});
m_rresp <= 2'b00;
end
end
// ---- the write side of the memory model: one burst at a time, with gaps ------------------------
localparam [48:0] WB_BASE = 49'h0_2000_0000;
reg [63:0] wmem [0:4095];
reg [48:0] w_addr = 0;
reg w_open = 0;
always @(posedge clk) begin
m_awready <= 1'b0;
m_wready <= 1'b0;
if (m_bvalid && m_bready) m_bvalid <= 1'b0;
if (m_awvalid && !m_awready && !w_open && ($random(seed) & 1) != 0) begin
m_awready <= 1'b1;
w_addr <= m_awaddr;
w_open <= 1'b1;
if (m_awsize != 3'd3 || m_awburst != 2'b01) begin $display("write-back: bad size or burst"); $finish; end
if ({1'b0, m_awaddr[11:0]} + ({5'd0, m_awlen} + 13'd1) * 13'd8 > 13'h1000) begin
$display("write-back: a burst crosses a 4 KiB page at %h", m_awaddr); $finish;
end
end
if (m_wvalid && !m_wready && w_open && ($random(seed) & 3) != 0) m_wready <= 1'b1;
if (m_wvalid && m_wready) begin
if (m_wstrb != 8'hFF) begin $display("write-back: bad strobes"); $finish; end
wmem[(w_addr - WB_BASE) / 8] <= m_wdata;
w_addr <= w_addr + 49'd8;
if (m_wlast) begin w_open <= 1'b0; m_bvalid <= 1'b1; m_bresp <= 2'b00; end
end
end
// ---- AXI-Lite master tasks --------------------------------------------------------------------
// The master drives just after a clock edge and samples the slave's outputs at the edge, as
// they were before it: a handshake completes on the edge where valid and ready were both high.
integer line, t;
reg a_ok, w_ok, done_hs;
task axi_write(input [15:0] word, input [31:0] d);
begin
@(posedge clk); #1;
awaddr = 32'hA000_0000 | ({16'd0, word} << 2); wdata = d; awvalid = 1; wvalid = 1;
t = 0;
while (awvalid || wvalid) begin
@(posedge clk);
a_ok = awready; w_ok = wready;
#1;
if (a_ok) awvalid = 0;
if (w_ok) wvalid = 0;
t = t + 1;
if (t > 1000) begin $display("TIMEOUT write %h at line %0d", word, line); $finish; end
end
bready = 1;
done_hs = 0; t = 0;
while (!done_hs) begin
@(posedge clk);
done_hs = bvalid;
#1;
t = t + 1;
if (t > 1000) begin $display("TIMEOUT b at line %0d", line); $finish; end
end
bready = 0;
end
endtask
task axi_read(input [15:0] word, output [31:0] d);
begin
@(posedge clk); #1;
araddr = 32'hA000_0000 | ({16'd0, word} << 2); arvalid = 1;
t = 0;
while (arvalid) begin
@(posedge clk);
a_ok = arready;
#1;
if (a_ok) arvalid = 0;
t = t + 1;
if (t > 1000) begin $display("TIMEOUT ar %h at line %0d", word, line); $finish; end
end
rready = 1;
done_hs = 0; t = 0;
while (!done_hs) begin
@(posedge clk);
done_hs = rvalid;
d = rdata;
#1;
t = t + 1;
if (t > 1000) begin $display("TIMEOUT r at line %0d", line); $finish; end
end
rready = 0;
end
endtask
integer fd, r, errors, reads, k, n, i, polls, longest;
reg [31:0] op, a, d, m, got, word;
reg [63:0] ent_a, ent_d;
initial begin
errors = 0; reads = 0; line = 0; longest = 0;
repeat (20) @(posedge clk);
rstn = 1;
repeat (5) @(posedge clk);
if (emio_i[7:0] !== 8'hA5) begin $display("no signature"); $finish; end
axi_read(16'h8000, got);
if (got !== 32'h4C415849) begin $display("wrapper ID %h", got); $finish; end
fd = $fopen("landscape_axi_tb.script", "r");
if (fd == 0) begin $display("no script"); $finish; end
r = $fscanf(fd, "%s", op);
while (r == 1) begin
line = line + 1;
if (op == "W") begin
r = $fscanf(fd, " %h %h", a, d);
axi_write(a[15:0], d);
end else if (op == "R") begin
r = $fscanf(fd, " %h %h %h", a, d, m);
axi_read(a[15:0], got);
reads = reads + 1;
if ((got & m) !== (d & m)) begin
errors = errors + 1;
if (errors <= 25) $display("MISMATCH line %0d: R %h got %h want %h", line, a[15:0], got, d);
end
end else if (op == "I") begin
got = 1; polls = 0;
while (got[0]) begin
axi_read(16'h0002, got);
polls = polls + 1;
if (polls > 200000) begin $display("TIMEOUT busy at line %0d", line); $finish; end
end
end else if (op == "D") begin
r = $fscanf(fd, " %d", n);
for (k = 0; k < n; k = k + 1) begin
r = $fscanf(fd, " %h %h", ent_a, ent_d);
mem[k] = {16'd0, ent_a[15:0], ent_d[31:0]};
line = line + 1;
end
axi_write(16'h8002, BASE[31:0]);
axi_write(16'h8003, {15'd0, BASE[48:32]});
axi_write(16'h8004, n);
axi_write(16'h8005, 1);
got = 2; polls = 0;
while (got[1]) begin
axi_read(16'h8001, got);
polls = polls + 1;
if (polls > 200000) begin $display("TIMEOUT DMA at line %0d", line); $finish; end
end
if (got[2]) begin $display("DMA error at line %0d", line); errors = errors + 1; end
end else if (op == "Q") begin
r = $fscanf(fd, " %d", n);
axi_write(16'h8010, n);
axi_write(16'h8011, 1);
// an engine access while the sequencer holds the engine: answered at once, dropped
axi_read(16'h0000, got);
if (got !== 32'hDEADBEEF) begin $display("an engine read during a batch returned %h at line %0d", got, line); errors = errors + 1; end
axi_read(16'h8008, got);
if (got !== 32'd1) begin $display("DROPS %0d after one dropped read, line %0d", got, line); errors = errors + 1; end
axi_write(16'h8008, 0);
got = 1; polls = 0;
while (got[0]) begin
axi_read(16'h8001, got);
polls = polls + 1;
if (polls > 2000000) begin $display("TIMEOUT sequencer at line %0d", line); $finish; end
end
if (polls > longest) longest = polls;
end else if (op == "K") begin
// the results of the last batch, written back: n chains, six words each, to check
r = $fscanf(fd, " %d", n);
axi_read(16'h8025, d);
axi_write(16'h8020, WB_BASE[31:0]);
axi_write(16'h8021, {15'd0, WB_BASE[48:32]});
axi_write(16'h8022, 0);
axi_write(16'h8023, n);
axi_write(16'h8024, 1);
got = 8; polls = 0;
while (got[3]) begin
axi_read(16'h8001, got);
polls = polls + 1;
if (polls > 200000) begin $display("TIMEOUT write-back at line %0d", line); $finish; end
end
if (got[4]) begin $display("write-back error at line %0d", line); errors = errors + 1; end
axi_read(16'h8025, got);
if (got !== d + 1) begin $display("write-back did not run at line %0d", line); errors = errors + 1; end
for (k = 0; k < n; k = k + 1) begin
for (i = 0; i < 6; i = i + 1) begin
r = $fscanf(fd, " %h", d);
line = line + 1;
word = (i % 2) ? wmem[k * 4 + i / 2][63:32] : wmem[k * 4 + i / 2][31:0];
reads = reads + 1;
if (word !== d) begin
errors = errors + 1;
if (errors <= 25) $display("MISMATCH line %0d: written back chain %0d word %0d got %h want %h", line, k, i, word, d);
end
end
end
end else begin
$display("bad op at line %0d", line);
$finish;
end
r = $fscanf(fd, "%s", op);
end
$display("TB DONE: %0d lines, %0d checked reads, %0d mismatches, sim time %0t", line, reads, errors, $time);
$finish;
end
endmodule