Skip to main content

Module planner

Module planner 

Source
Expand description

DSig as a component of the shared chip planner and TUI ([rhdl_fpga_planner]), next to Ed25519 Sign/Verify and the shared SHA-512.

§The problem

DSig is not built alone. The comparison paper puts a DSig signer and verifier, an Ed25519 signer and verifier and a shared SHA-512 line on one chip, and asks “with this much area, how many operations per second does each get?” The shared chip planner (rhdl_fpga_planner, a sibling crate with a terminal UI, or TUI) answers that by giving each design a share of the chip (LUTs, DSPs, block RAM, UltraRAM) and asking it for a plan. So DSig must speak the planner’s language: the [Component] trait.

§The reasoning

crate::config already knows what DSig costs and how fast it is. This module does not repeat those measurements; it imports them and adds the knobs the comparison wants to explore. Some of those choices have never been synthesized, so their costs are estimates, and every [Plan] says whether it rests on measurement (measured).

Two lessons shaped the plan search:

  • A share is not the device. Moving S-boxes into spare RAM must use the RAM in DSig’s share, not the whole chip’s, or every partial share fails the RAM check (config::Build::fill_within).
  • Small shares still deserve an answer. The smallest built engine has two slices (~207k LUT with S-boxes in RAM). A one-slice engine is offered as a model so a small share still plans something.

§The decision

Two components:

  • Dsig, the signer we build. Its plan is the largest slice count that fits the share.
  • DsigVerify, a model of a verifier that is not built yet, so the paper can compare sign and verify on the same footing.

What the verifier model counts, step by step (nothing here is built):

Verifying a DSig signature (one verify unit) MODEL Not built yet: the planner's model. A verify unit has 3 chain lanes and 1 BLAKE3 lane, like a slice. signature+ message message digestBLAKE3-XOF → 68 digits chain walks on 3 chain lanesrevealed node + (3 − d) steps; ~105 in all 68 endpoints18 B each commitment21 BLAKE3 → pk_hash Merkle path (7 BLAKE3)pk_hash + 7 siblings → batch root root cache64 verified roots (CAM) hit (fraction h)accept on hash work only: no Ed25519 miss (fraction 1 − h): Ed25519 verifyof the batch certificate, then remember its root hit miss done by the chip's Ed25519 verify component Rate of one verify unit (planner model) chain side: ~105 steps ÷ 3 lanes = 35 clocks · BLAKE3 side: 1 digest + 21 commitment + 7 Merkle = 29 on 1 lane cycles per verification = max(35, 29) = 35 → 205 MHz ÷ 35 = 5.86M verifications/s per unit Ed25519 verifies per DSig verify = 1 − h, with h = b · (K − 1)/K = b · 127/128 (b: best-case share of known batches) unit LUT (estimate): 3 × 23,902 Haraka lanes + 16,422 BLAKE3 lane + 20,000 glue (CAM, scheduler, parser) ≈ 108k ~105 expected steps: 64 uniform digits × 1.5 = 96, plus ~9 for the 4 checksum digits (estimate). Dashed borders = modeled, not built. Chain lanes follow the signer's chain hash (Haraka-512, BLAKE3 or SHA-512); a signature only verifies under its own profile.
  rhdl_fpga_planner (TUI)
      │ Share {lut, dsp, bram36, uram}, Context {mhz, ...}
      ▼
  Dsig::plan ── for slices in 1..=16 ─────▶ Dsig::cost(slices, share) ──▶ fits?
      │                                         │ yes: keep, try next size
      │                                         │ no : stop, record the limit
      ▼                                         ▼
  Plan {cost, rate = slices·f/period (own core: min with K·f/895), demands, ...}
      │
      ▼
  Dsig::build_flags ──▶ DSIG_SLICES, DSIG_CHAIN, DSIG_SBOX_PLACEMENT

The cost model of one slice is taken apart into lanes so a knob can swap a lane:

  slice = slice_rest + factory BLAKE3 lane + 3 × chain lane
  slice_rest = SLICE_LUT − 3 × HARAKA_LANE_LUT − BLAKE3_LANE_LUT
  kernel = slices × slice + glue (+ Ed25519 core when DSig has its own)

§Knobs that the signer and verifier share

A verifier can only check what the signer made, so the scheme knobs (HBSS, chain hash, SHA lanes, w, K) are shared: the TUI lists them once, under DSig sign, and sets both modules.

§Where the certificates come from

Every batch of K keys is certified by one Ed25519 signature. Two ways:

  • the chip’s Ed25519 sign module (default): DSig sends its batch roots to the paper’s Ed25519 signer, which the planner sizes for 1/K of DSig’s rate. DSig’s own cost then has no Ed25519 core.
  • inside DSig (as built): the hardware built today carries its own small Ed25519 signer (ED25519_LUT), capping DSig at K × f / 895. The plan covers Ed25519 signing, so the chip’s Ed25519 sign module is not needed; counting both would count the signer twice.

Knobs of Dsig (and, where they apply, DsigVerify):

KnobChoicesWhat it changesStatus
HBSSW-OTS+, HORSthe hash-based one-time schemeW-OTS+ built; HORS not implemented (no plan)
chain hashHaraka-512, BLAKE3, SHA-512the lane in every slice (chain-* feature)built, simulated
SHA lanesown, shared lineSHA-512 chains on DSig’s own pipelines, or as clients of the shared lineown: built; shared: model
Winternitz w4, 16the HBSS: chain length and chain count4 built; 16 model
batch K128, 64, 256keys per Ed25519 certificate128 built; others model
S-box RAMfill, LUT onlyHaraka S-boxes in the share’s spare UltraRAM / block RAMbuilt
BLAKE3 addersLUT, DSP32-bit adders of every BLAKE3 lane in DSP48E2splanned, estimated
Ed25519 certificateschip’s Ed25519 sign module, inside DSigwho signs batch roots (above)inside: built
message16 B (profile 1), 64 B (profile 2)signed message length, DSIG_MESSAGE_BYTES: profile 2 signs uBFT’s whole 48-byte signed context; one more digest compression per signature (an idle timetable residue, so the same rate)16: built, measured; 64: simulated, cost estimated

Slice counts: any of 1 to 16. 2, 4 and 8 are built (DSIG_SLICES=N), and 2 is measured on the card (build v3: 786,432 signatures at 6,019,465/s, 34.06 cycles per signature); the others are a model with the same per-slice cost and slices × f / key period rate.

Costs: measured (routed slices-2 link and Vivado OOC) for the Haraka lane, the BLAKE3 lane, the slice and the Ed25519 core; estimates for the SHA-512 lane, the DSP-adder BLAKE3 lane, the one-slice build and S-box savings (see config).

Structs§

Dsig
The DSig component.
DsigUbftSign
DSig signing as a uBFT service: the batch signer’s slices and certificate unit behind a request stream (crate::ubft::UbftSigner), with its own Ed25519 certificate core and the T-table rounds of the measured builds.
DsigUbftVerify
DSig verification as a uBFT service: one verify core behind a request stream (crate::ubft::UbftVerifier) and the Ed25519 known-key verify core with one key slot per peer replica.
DsigVerify
DSig verification (built: crate::verify).
Hbss
The hash-based one-time signature scheme (HBSS) and its batching.
UbftMessage
The uBFT services’ message settings, shared by signer and verifier. Profile 2 is the default: it signs uBFT’s whole context.

Enums§

Scheme
Which hash-based one-time signature scheme DSig uses.

Constants§

AUTO_TABLE_ROUNDS
The “table rounds” value meaning auto: [Component::plan] tries 0..=6 inside its share and keeps the fastest.
BLAKE3_DSP_LANE_LUT
BLAKE3 lane with its ~224 32-bit adders in DSPs: estimate (each adder ~32 LUTs → one DSP48E2).
BLAKE3_LANE_DSP
DSPs of a BLAKE3 lane with DSP adders: estimate.
BLAKE3_LANE_LUT
BLAKE3 compression lane, Vivado OOC routed.
CHAIN_LATENCY
Latency of one chain step, in cycles, for Haraka-512, BLAKE3 and SHA-512 (indexed by the chain-hash knob; measured in simulation).
HARAKA_LANE_LUT
Haraka chain-step lane, Vivado OOC routed.
HARAKA_TABLE_LANE_BRAM36
BRAM36 tiles of one T-table lane: 176 dual-port BRAM18 ROMs (32 per table round in rounds 3–6, 24 in rounds 7–8 where 16 bytes are never observed). Build v5 synthesized 2,135 RAMB18 for its 12 lanes and caches.
HARAKA_TABLE_LANE_LUT
One Haraka chain-step lane with T-tables in rounds 3–8 (from synthesis): what is left of a v5 slice after the parts a v3 slice also has.
LOOKUPS_PER_URAM
Table lookups one UltraRAM serves: one per port of a dual-port 4,096 × 72 URAM (a 256 × 32 T-table fills 1/16 of it). Block RAM serves four per BRAM36 (two per BRAM18), so URAM is the poorer home per tile, but a slices-4 kernel leaves ~670 of the device’s 960 unused while block RAM is the wall.
MESSAGES
Display names of the message-length choices (index 1 = profile 2).
SHA512_CLIENT_LUT
A chain lane that sends its blocks to the shared SHA-512 line instead of hashing them itself: block assembly, a tag FIFO and the truncation (estimate).
SHA512_LANE_LUT
SHA-512 lane (rhdl_sha512_pipelined, 80 unrolled rounds): estimate until synthesized (80 × ~420 + 64 × ~150 LUTs).
SIGN_LATENCY_CYCLES
Idle signing latency: native simulation (cycle-accurate RHDL, not the card) of the slice signer behind the uBFT service’s request stream (tests/ubft_signer.rs, 2 slices, 8 isolated requests after the first certificate: 171–188 cycles, mean 173.1, every frame the reference signature). A key is cached and its certificate made, so this is the message digest, the 68-cycle schedule slot it waits for, the node reads from the key cache, the 26-beat frame and the relay registers. It replaces an unmeasured 64-cycle estimate (28 BLAKE3 + 26 beats + ~10 registers) that left out the schedule wait and the cache reads. The batch kernel’s HBM reader and writer add their own cycles (not measured).
TABLE_LOADER_LUT
LUTs of the loader one lane needs when any of its T-tables are in URAM: estimate. UltraRAM cannot be preloaded by the bitstream, so after every reset an 8-bit counter walks the 256 entries and writes each table (as crate::aes_table::SboxLoader does for URAM S-boxes); the lane reports not-ready until it finishes. Not built for T-tables.
UBFT_SIGN_LATENCY_CYCLES
Idle signing latency of the whole uBFT service kernel (crate::ubft::UbftEngine): native simulation, tests/ubft_engine.rs, request beat to the frame’s last beat, 6 isolated requests after the first certificate. Profile 1: 207–217 cycles, mean 208.7 (1.01–1.06 µs at 205 MHz); profile 2 (64-byte messages): 207–257, mean 215.3. It is SIGN_LATENCY_CYCLES (the signer alone) plus the engine’s stream registers and arbitration, about 36 cycles. The uBFT signing component uses this one, since a replica sees the whole kernel.
UBFT_SLICES
Slice counts the uBFT signer can be built with (DSIG_SLICES).
UBFT_VERIFY_LATENCY_CYCLES
Checks that the planner component agrees with measurement and behaves sensibly as the budget grows. Isolated verify latency of the uBFT service with its certificate already checked: native simulation (tests/ubft_blocks.rs, instant Ed25519 model, header handshake to verdict: 732–736 cycles; the verify core alone is VERIFY_LATENCY_MEASURED).
V5_KERNEL_LUT_SYNTH
Kernel CLB LUTs of build v5 after synthesis (not routed): slices-4, T-tables in AES rounds 3–8 (DSIG_TABLE_MASK=0x1f8), Ed25519 inside.
VERIFY_CARD_RATE
The verifier kernel’s card rate: measured (build v3, three units, 1,572,864 valid signatures in 19,012,289 cycles at 205.00 MHz, finite burst vrun-clean-1): 12.09 cycles per signature for the kernel, 36.3 per unit.
VERIFY_CARD_UNITS
Units in the card-measured verifier kernel.
VERIFY_CHAIN_STEPS
Expected chain steps to finish one DSig verification: 68 W-OTS+ chains (w = 4) each need 3 − digit steps; the 64 message digits are uniform (mean 1.5 steps) and the 4 checksum digits skew low.
VERIFY_CORE_BRAM36
Block-RAM tiles of that core in the kernel (88 RAMB36 + 388 RAMB18): measured, build v1.
VERIFY_CORE_LUT
CLB LUTs of one routed verify core (Haraka lanes with VERIFY_CORE_TABLE_ROUNDS table rounds) inside the kernel: measured, mean of the three cores of verifier build v1 (Vivado 2023.2 routed, 89,495–89,716). Routed alone (OOC) it was 80,211.
VERIFY_CORE_TABLE_ROUNDS
Table rounds of the measured core’s lanes (DSIG_TABLE_MASK=0x078).
VERIFY_CYCLES_MEASURED
Cycles per verification of one core for the built scheme: native simulation (two whole batches, steady state).
VERIFY_ED25519_BRAM36
Its block-RAM tiles (41 RAMB36 + 4 RAMB18; measured, 007zzi).
VERIFY_ED25519_DSP
Its DSP slices (measured, 007zzi).
VERIFY_ED25519_ISOLATED
Its latency for an isolated check: native simulation (Ed25519 project, one request at a time, 4 samples: 52,642–52,672, median 52,670).
VERIFY_ED25519_LATENCY
Its latency per check, saturated median: native simulation (Ed25519 project: min 54,144, median 63,026, p90 81,738 cycles; isolated ~52k). The verifier’s verdict queue hides it after a run’s first batch.
VERIFY_ED25519_LUT
The Ed25519 known-key verify core a verifier embeds (fix14, constant banks as ConstRom): CLB LUTs inside the verifier kernel, measured (build v3, routed, engine/c1: 79,532). Alone it routes at 84,435 with WNS +0.072 ns (Ed25519 project, 007zzr; fix12 in build v1: 79,786 in context, 82,184 alone).
VERIFY_ED25519_RATE
Its certificate checks per second at 205 MHz: native simulation (Ed25519 project, fix12 dcb2e6e: 600 verifications, 735.25 cycles each).
VERIFY_ED25519_URAM
Its UltraRAMs (measured, 007zzi).
VERIFY_LATENCY_MEASURED
Idle latency of the hash path for the built scheme (first beat to verdict, certificate already checked): native simulation.
VERIFY_UNIT_IO_LUT
LUTs of a unit beyond its core: HBM reader and writer and its share of the cluster (registered links, registers, arbiter). Measured, build v1: (366,916 engine − 79,786 Ed25519) / 3 units − 89,627 per core.

Functions§

table_lane
LUTs and BRAM36 of one Haraka lane with its first rounds table rounds (0..=6, from round 3).
table_lookups
Table lookups (S-boxes replaced by T-table reads) in one lane’s first rounds table rounds, counting up from round 3.