Expand description
AES T-table ROM: one BRAM port replaces one S-box and its MixColumns share.
§The story so far
A DSig signature needs 204 Haraka chain steps, and each folded chain step
needs 494 AES S-box evaluations (see crate::chain_step). An S-box
(“substitution box”) is the one non-linear part of AES: a fixed table that
maps every byte value 0..256 to another byte. Everything else in an AES
round is XOR and wiring. So the S-boxes are where the chip area goes, and
where each S-box physically lives is the biggest area decision in the
signer.
This module owns that physical side. It builds every S-box and every
T-table as a read-only memory (ROM) out of RHDL parts, and leaves the
choice of memory type to crate::sbox_placement. Its neighbours:
crate::haraka the math: what an AES round computes (reference + kernels)
│
▼
crate::aes_table this file: the S-box / T-table ROMs, registered
│ (SboxStage, SboxStages, SboxLoader, DualTableRom)
▼
crate::chain_step the lane: 20 stages that use these ROMs every cycle
▲
crate::sbox_placement which of the 494 live boxes are UltraRAM / BRAM / LUT§One AES round, and which part this file builds
An AES round (Intel’s AESENC instruction) runs on a 16-byte state, laid out
as four columns of four bytes. It has four steps:
state ──▶ SubBytes ──▶ ShiftRows ──▶ MixColumns ──▶ AddRoundKey ──▶ state'
(16 S-box (row r moves (each column (XOR a 16-byte
lookups) left r places) times a fixed round constant)
4×4 matrix)
└─── non-linear ───┘└───────────── linear (XOR only) ─────────┘
In this crate: SubBytes+ShiftRows = SboxStage (one register stage)
MixColumns+AddKey = mix_key512_kernel in crate::haraka
(the next register stage)ShiftRows is only a re-ordering of bytes, so it costs no logic: it just
decides which input byte each S-box reads. That is why SboxStage
does SubBytes and ShiftRows together.
§The idea in one picture
An AES round turns each four-byte column (s0, s1, s2, s3) into
out = 2·S(s0) ⊕ 3·S(s1) ⊕ S(s2) ⊕ S(s3) (row 0, and rotations)Because MixColumns is linear, each input byte’s contribution to the whole output column depends on that byte alone. Precompute it once:
T0[x] = bytes (2·S(x), S(x), S(x), 3·S(x)) — a 256 × 32-bit table
out = T0[s0] ⊕ rotl8(T0[s1]) ⊕ rotl16(T0[s2]) ⊕ rotl24(T0[s3]) ⊕ keyIn LUT logic an S-box costs about 32 LUTs. As a table it costs one read
port of a block RAM, and the only LUTs left are one 5-input XOR per output
bit. See sustained for the budget.
§Built only from RHDL parts
Every table here is an [rhdl_fpga] [SyncBRAM] that is filled at
configuration time and never written, i.e. a ROM with a registered read:
| Item | Made of | Vivado maps it to |
|---|---|---|
SboxStage | 32 × SboxPair (two S-box lookups each, SboxStageRoms) | LUT ROMs (≈32 LUTs per lookup), or one BRAM18 / URAM per pair |
DualTableRom | one dual-port 256 × 32 ROM holding T0 | one BRAM18, two reads |
The ROM registers its output like any synchronous BRAM read. In a pipeline
that register is the stage register, so a table round has the same
latency as a LUT round (see chain_step).
This is the textbook “T-table” software AES trick moved to FPGA block RAM. In software it leaks timing through caches. A dedicated ROM port has fixed single-cycle latency regardless of address, so that concern does not apply.
§Three places an S-box can live
The same 256 × 8 ROM can be built three ways on the U280. The numbers are
the ones in crate::sbox_placement and sustained:
| Place | Cost per S-box | Filled how |
|---|---|---|
| LUT ROM (the default) | ~32 CLB LUTs | constant logic, nothing to fill |
| Block RAM | ½ BRAM18 (an SboxPair serves two boxes) | preloaded by the bitstream |
| UltraRAM | ½ URAM (an SboxPair serves two boxes) | cannot be preloaded: SboxLoader writes it after reset |
UltraRAM is the odd one out. Because the bitstream cannot fill it, every
UltraRAM S-box needs a write port, and the lane needs a small loader that
runs after every reset. Until the loader is done the lane reports
“not ready” (ChainStepOutput::ready),
and nothing upstream starts a key. That keeps the one-time-key rule safe:
no key is ever spent on a lane whose tables are still empty.
§Reading order
T0andDualTableRom: the T-table trick (used byTableChainStepLane).SboxPairandSboxStageRoms: 32 dual-port S-box pairs for one round, wired through ShiftRows.SboxStage: the same hardware, plus a fast simulation model.SboxStages: ten of them, one per AES round of a Haraka-512 lane.SboxLoader: fills the UltraRAM boxes after reset.
Structs§
- Dual
Table Rom - Two registered
T0lookups per cycle from one true dual-port ROM. - Loader
Out - What the loader presents each cycle.
- Pair
Bytes S(a)andS(b)for the addresses presented one cycle earlier.- Pair
Read - The two addresses of an
SboxPair, and the loader’s write. - RomLoad
- One write into an UltraRAM S-box: entry
addrbecomesvalue. - Sbox
Loader - Fills a lane’s UltraRAM S-boxes after reset: entry
igetsS(i)fori = 0..256, all boxes in parallel, from one LUT S-box ROM. - Sbox
Pair - Two registered S-box lookups per cycle from one dual-port memory.
- Sbox
Stage - Registered
SubBytes ∘ ShiftRowsfor all four Haraka AES lanes. - Sbox
Stage In - Input of one S-box stage: the 64 state bytes, and the loader’s write.
- Sbox
Stage Roms - The hardware of
SboxStage: 64 S-box ROMs (SyncBRAM<b8, 8>), one per output byte, addressed through ShiftRows. - Sbox
Stages - The ten S-box stages of one chain-step lane, each placed by
sbox_placement. - Sbox
Stages In - Input of
SboxStages: every round’s bytes plus the shared table load. - Table
Read - Two independent byte addresses presented in the same cycle.
- Table
Words T0entries for the addresses presented one cycle earlier.
Enums§
Constants§
- T0
T0[x] = (2·S(x), S(x), S(x), 3·S(x)), row 0 in the low byte.
Functions§
- rom_
read8_ kernel - A read-only
SyncBRAMinput: readaddress, never write. - sbox_
loader_ kernel - Read
S(i)one cycle, write it the next. - sbox_
read_ kernel - Like
rom_read8_kernelfor a byte-wide ROM. - sbox_
stage_ kernel - ShiftRows decides which input byte each ROM reads; only UltraRAM boxes see the loader’s writes.
- sbox_
stages_ kernel - Hand each round its bytes; broadcast the table load.