MCU B Open ISA — AES-128-CBC over random input
A verify-by-result track with no fixed ISA. You are not handed a binary to run — you are handed a behavior: encrypt random plaintext under a random key delivered at runtime and prove the ciphertext on UART. Any instruction set, any microarchitecture, even a programmable crypto datapath, as long as the bytes come out right and the chip hardens clean into the locked chassis. Ranked by energy per byte, not IPC.
user_project_wrapper with the same locked pinout — Wishbone control, QSPI flash, UART, and the
GPIO map are identical for everyone — so results are directly comparable and the design is manufacturable on
the chipignite shuttle.Overview
MCU A locks the ISA to RV32I so that two submissions retire the same instruction stream and IPC becomes a fair contest. MCU B does the opposite. It removes the ISA from the contract entirely. The deliverable is defined purely by input → output behavior: build a chip that reads random 16-byte plaintext blocks delivered at runtime, encrypts them with AES-128 in CBC mode under a random key and IV, and emits the resulting ciphertext plus a digest on UART. How you get there is your design space. A classic soft core running C. A custom ISA tuned for the inner loop. A tiny sequencer driving a hardwired AES round datapath. A wide programmable crypto engine. All are legal, because the grader never inspects how the answer was produced — only that the right ciphertext appears for inputs the submitter could not have seen at design time.
The chip still has to be real. The same harden flow that grades every PPABench track applies here: yosys synthesis →
OpenROAD place & route + STA → Magic DRC → netgen LVS, hardened into the fixed user_project_wrapper pinout
on sky130 HD. A submission that produces correct ciphertext in a Verilator run but cannot route, fails timing, or
breaks LVS does not score. Correctness and silicon-realizability are both gates, not bonuses.
Why open ISA changes the metric: cycles, not IPC
IPC (instructions per cycle) is a perfectly good figure of merit on MCU A and a meaningless one here. The reason is structural. IPC only compares across designs when the instruction stream is held constant. The moment the ISA is free, "instruction" stops being a fixed unit of work. A CISC-leaning ISA retires fewer, fatter instructions — one op might do a full AES column mix that a RISC core spreads across a dozen. A submission could win on IPC while doing strictly more work, or lose on IPC while being the faster, leaner chip. Counting instructions across different ISAs is counting in different currencies.
So MCU B drops IPC and ranks on quantities that are ISA-invariant for a fixed workload:
- Cycles to complete the fixed AES-128-CBC workload, counted externally at the wrapper boundary.
- Energy per byte — total energy to encrypt the workload divided by plaintext bytes. This is the primary ranking key.
- Cycles per block and EDP (energy-delay product) as secondary efficiency views.
The workload is fixed even though the ISA is not. Both submissions encrypt the same number of bytes through the same cipher, so cycles and joules are directly comparable no matter how the work is sliced into instructions. Contrast this explicitly with MCU A · fixed RV32I, where the ISA lock is what makes IPC legitimate; here the ISA freedom is what makes IPC illegitimate. Same benchmark family, opposite metric, for the same underlying reason: you can only divide by "instructions" when everyone agrees what an instruction is.
The challenge
The functional task is AES-128-CBC encryption over N random plaintext blocks under a random key and IV. Concretely, per run the testbench:
- draws a random 128-bit key, a random 128-bit IV, and N random 16-byte plaintext blocks (the reference workload is N = 4 blocks, 64 bytes);
- injects those bytes into the flash image the design boots from, so the key/IV/plaintext only exist at runtime — they are not present when the design is built;
- starts the design and lets it run to completion, expecting the final ciphertext and a digest emitted on UART.
CBC chaining is the deliberately hard part. In CBC, each plaintext block is XORed with the previous ciphertext block before the AES core runs, so block k+1 cannot begin until block k is finished. The chain is strictly sequential. That kills the obvious shortcut of precomputing all blocks in parallel ahead of time, and it means a submission's throughput is bounded by its real per-block AES latency, not by how many blocks it can fan out. The digest emitted alongside the ciphertext lets the testbench check the whole output cheaply and catch partial or truncated runs.
Anti-cheat — the core idea
The entire benchmark is built so that the only way to score is to actually compute AES. Four mechanisms stack:
- Random input per run. The key, IV, and plaintext are drawn fresh and injected into the flash image at runtime. There is no fixed answer to hardcode, because the correct ciphertext is a function of inputs that did not exist when the design was submitted. A lookup table of canned outputs is useless — it would have to contain a 2128-entry map.
- Software golden re-derivation. The grader runs a reference AES implementation over the same random key/IV/plaintext and re-derives the expected ciphertext independently. The DUT's UART output is compared byte-for-byte against that golden. There is no "self-reported correct" path.
- K fresh random trials, all must pass. Functional credit requires exact-match across K independent random trials, each with new key/IV/plaintext. A design that happens to handle one input but not the general case fails. Passing means the design implements the cipher, not a special case.
- Externally-counted cycles. The graded cycle count is
wb_clk_iedges counted by the testbench fromCTRL.starttoSTATUS.doneat the wrapper boundary — never a counter the DUT reports about itself. The design cannot under-report its own runtime; the clock is counted outside the chip.
This is precisely the structural defense against the classic benchmark fraud. A Dhrystone-style design that detects the benchmark and emits a memorized answer fails here, because the answer changes every run and is checked against an independent golden. A design that fakes a fast cycle count fails, because cycles are counted externally. To score, you must build hardware that takes arbitrary key/IV/plaintext and emits the correct CBC ciphertext within the cycle and PPA budget — there is no path that skips the computation.
Interface & protocol
MCU B uses the same locked chassis as the rest of PPABench — the fixed user_project_wrapper pinout on
sky130 HD. The relevant pins and registers:
- QSPI flash on
io[24:29]— the design boots and executes from this flash, and it is also the channel the testbench uses to inject the per-run key, IV, and plaintext into the flash image (input injection + program). - UART on
io[30:31]— the proof channel; the ciphertext and digest are decoded here. - Wishbone CTRL/STATUS at
0x30000000—CTRL.startkicks the run,STATUS.donereports completion. - done on
io[35](anderroronio[36]) — the wrapper-boundary completion signal that bounds the externally-counted cycle window.
Per-trial flow as the hidden testbench runs it:
Grading
A submission must clear every gate, then it is ranked on efficiency:
- Functional — exact-match ciphertext vs the software AES golden across all K fresh random trials. Any miss fails the track.
- Performance — cycles, cycles/block, energy/byte (pJ/byte), total energy, and EDP. Energy/byte is the primary ranking key.
- PPA caps — area, power, and the other harden KPIs must sit within the track's caps.
- Timing — STA must close with WNS ≥ 0 at the target clock.
- DRC / LVS — Magic DRC-clean and netgen LVS-match, hardened into the locked wrapper.
Everyone who passes is, by construction, a real chip that actually encrypts. Ranking among passers is by efficiency — lower energy/byte ranks higher.
Reference baseline — picosoc + tiny-AES-c
The reference runs AES-128-CBC entirely in software on the stock picosoc (picorv32 RV32IMC core, spimemio QSPI XIP,
simpleuart) using the tiny-AES-c library — no crypto acceleration of any kind. These are real numbers:
functional matched on 3/3 random seeds; cycles and PPA from the live Verilator/cocotb harden + sim flow.
MCU B Reference baseline · picosoc + tiny-AES-c
AES-128-CBC, 4 random blocks (64 B) · energy = routed PPA × sim cycles @ 50 MHz| Metric | Value | Notes |
|---|---|---|
| Functional | ✓ 3/3 | exact-match vs software golden, 3 fresh random seeds |
| Cycles (4 blocks) | 1,088,825 | externally counted, start → done |
| Cycles / block | 272,206 | AES round logic emulated in software |
| pJ / cycle | 3.54 | P/f × 1000 @ 50 MHz |
| Energy | 3,854 nJ | P × cycles / f |
| Energy / byte | 60,226 pJ | primary ranking key |
| Area | 3,810 µm² | routed sky130 HD |
| fmax | 88.5 MHz | 8.7 ns slack at 50 MHz target |
What a strong submission looks like
The headroom over the software baseline is almost entirely about moving the AES round into hardware. Strong directions, and their costs:
- Hardware AES rounds. A combinational or lightly-pipelined round (SubBytes via S-box ROM or composite-field logic, ShiftRows as wiring, MixColumns as fixed GF(28) arithmetic) collapses thousands of instructions per block into a handful of cycles. This is the single biggest lever on both cycles/block and energy/byte.
- Key-expansion pipelining. AES-128 needs 11 round keys; precomputing the key schedule once and overlapping its expansion with the data path removes it from the per-block critical path. With one fixed key across the CBC workload, the schedule is amortized to near-zero.
- Wider datapath. A 128-bit state path processes a full block per round instead of marshaling it 32 bits at a time, slashing cycle count — at the cost of area and a tougher timing closure.
Every one of these is a trade against the PPA caps. A fully-unrolled AES pipeline is fast and energy-cheap per byte but area-hungry and can stress timing; a compact iterative round is small and easy to close but slower. Because CBC is sequential, throughput-via-parallelism is off the table — the win comes from cutting per-block latency and the energy each round burns, not from running blocks side by side. The interesting submissions are the ones that find the best energy/byte at a given area, not the ones that simply throw the most gates at the problem.
Tracked metrics
The full KPI set graded and reported for MCU B (formulas per ppabench/chassis/KPIS.md):
- Functional — K-of-K exact-match ciphertext vs software AES golden over fresh random trials.
- Cycles — externally counted
wb_clk_iedges,start→done. - Cycles / block and wall time = cycles / f.
- pJ / cycle = P[mW] / f[MHz] × 1000.
- Total energy (nJ) = P × cycles / f.
- Energy / byte (pJ/byte) — workload-normalized, primary ranking key.
- Throughput — bytes/s, cycles/byte.
- EDP = energy × wall time; EDAP = EDP × area.
- Area (µm²), area per kgate; total / dynamic / leakage power, leakage fraction.
- fmax (MHz) and timing closure (WNS ≥ 0).
- DRC-clean + LVS-match in the locked wrapper.
Where a figure derives from the reference routed PPA rather than a per-submission harden of its own, it is a reference-route number and is labeled as such — energy figures throughout are the reference routed PPA combined with the real sim cycles at 50 MHz. A submitted design carries its own routed PPA, and its energy/byte follows from its own power and its own externally-counted cycles.
PPABench v0.1 · MCU B reference baseline picosoc + tiny-AES-c on sky130 HD · grading: harden (OpenROAD/Magic/netgen) + hidden cocotb TB, external cycles, random input · github.com/balbekov