Skip to main content

Crate rhdl_ed25519_core

Crate rhdl_ed25519_core 

Source
Expand description

§Dalek-compatible Ed25519 hardware in RHDL

This crate is the hardware facade for the workspace. Its modules re-export the real SHA-512, field, scalar, point, message-feeder, and wire-type crates used by the synthesizable design. It contains no Dalek implementation code: the rhdl_ed25519_model crate uses the pinned Dalek snapshot only as a differential-test oracle.

The workspace has two hardware tracks:

  • The compatibility core implements public-key derivation, cold and cached-key signing, strict verification, multipart byte streams, and key clearing. rhdl_ed25519_top::Ed25519Core assembles the modules exported here into the end-to-end synchronous design.
  • The fixed-64 fast path is an optimization track for hardware key expansion followed by saturated cached-key signing of 64-byte messages on an AMD Alveo U280. Its fast_field, fast_fixed_base, fast_sha512, fast_point_codec, fast_scalar, and fast_sign crates contain the specialized SystemVerilog datapath, RHDL wrapper types, cycle models, and RTL tests. It is not yet a replacement for the compatibility core.

Implementation boundary: the optimized fixed-64 datapath is handwritten SystemVerilog and is compiled directly by Icarus, Verilator, and Vivado. It is not emitted from RHDL. The SystemVerilog backend manual documents every module, parameter, helper function, port group, state machine, memory image, and build boundary in that rewrite.

§End-to-end signing

For an RFC 8032 seed sk and message M, the hardware executes:

SHA512(sk) -----------------> clamp -> a -------> [a]B -> encode -> A
    |
    +-----------------------> prefix

SHA512(prefix || M) --------> reduce mod l -> r -> [r]B -> encode -> R
SHA512(R || A || M) --------> reduce mod l -> k
S = r + k*a mod l
signature = R || S

The host never supplies a prehash, expanded key, reduced nonce, challenge, or intermediate point. SHA-512 padding, clamping, reduction, scalar multiplication, point compression, and signature assembly all execute in hardware.

§Fixed-64 pipeline anatomy

The cached fixed-64 signer is a tagged task graph, not a 512-stage linear pipeline. 512 is the number of requests in the standard throughput test. The physical concurrency is 64 live signing contexts, 16 interleaved point contexts, a three-phase SHA-512 round datapath reused for 80 rounds, and a 17-position data/valid pipeline in each radix-51 field multiplier. A SHA worker occupies one block for 242 cycles even though it has only three round phases; worker occupancy and physical register depth are different values.

One cached signing request advances through these dependency stages:

StageFunctionMain hardware and completion state
1. AllocateAccept a 64-byte message and caller tag into a free contextAn eight-context rotating search writes banked context registers and raises nonce_hash_pending
2. Build nonce blockForm `prefix
3. Hash nonceExecute all 80 SHA-512 roundsOne of four three-phase compression workers returns a 512-bit digest through a nonce FIFO
4. Reduce nonceCompute canonical r = digest mod lThe dedicated 626-DSP, 46-position nonce reducer writes nonce_scalar and raises point_r_pending
5. Compute nonce pointCompute [r]BThe 16-context signed multi-comb engine performs 32 constant scans/mixed additions and seven doublings, then enqueues projective (X,Y,Z)
6. Compress nonce pointEncode projective [r]B as Edwards-Y ROne of four pair-codec ways shares one inversion across two points, stores R, and raises challenge0_pending
7. Hash challenge block 0Compress the full 128-byte `R
8. Hash challenge block 1Compress the padding-only block for a 1024-bit inputA SHA worker emits the final challenge digest through a challenge FIFO
9. Reduce challengeCompute canonical k = digest mod lThe second 626-DSP, 46-position reducer writes challenge_scalar and raises muladd_pending
10. Compute SCompute canonical S = r + k*a mod lA 69-position, 1,237-DSP multiply-add pipeline returns S with the context tag
11. RetireAssemble `signature = R

Pending bits and bounded FIFOs form the scoreboard between stages. Workers can therefore service different requests simultaneously and complete internal stages out of input order; the six-bit context tag associates every completion with its retained state. The AXI wrapper adds a 64-entry result FIFO because the core result is a one-cycle pulse. See architecture for the exact eligibility rules, queue topology, key-load path, backpressure, banking, and stage-by-stage source map.

§Strict verification

Verification rejects malformed inputs before evaluating the group equation:

canonical(S)?
decode(A) -> reject malformed or small-order A
decode(R) -> reject malformed or small-order R
SHA512(R || A || M) -> reduce mod l -> k
compare R with encode([S]B - [k]A)

Dedicated result codes distinguish framing errors, a missing cached key, malformed points, non-canonical S, small-order points, and equation failure. See types for the wire-level contract.

§Arithmetic hierarchy

The compatibility backend follows Dalek’s serial 32-bit organization:

  • field represents values modulo p = 2^255 - 19 with ten alternating 26/25-bit limbs stored in 32-bit lanes.
  • scalar represents values modulo the Ed25519 group order l and implements wide reduction, 256-bit reduction, canonicality, and r + k*a mod l.
  • point uses extended Edwards coordinates (X:Y:Z:T), Projective Niels points, balanced radix-16 recoding, and fixed-pattern candidate scans.
  • sha512 implements padding, a 16-word circular schedule, and all 80 compression rounds in synchronous logic.
  • hash_feeder inserts hash prefixes and retains the first 4 KiB of a message for the second signing pass.

§Fast-path component and primitive map

The current matching-source U280 optimized out-of-context core report divides into seven direct children. Registers, RAMB36s, and DSPs sum exactly. Vivado combines 480 LUTs across hierarchy boundaries, so the adjusted LUT total is smaller than the raw direct-row sum. A nested field multiplier must not be added to its parent a second time.

ComponentFunctionLUTsRegistersRAMB36DSP48E2
Top-level context/controlOwn 64 contexts, banked state, scoreboards, arbiters, tags, and queues10,1268,09600
Four-worker SHA poolBuild and compress seed, nonce, and challenge blocks18,48912,088448
Nonce reducerReduce the 512-bit nonce digest modulo l2,72211,9820626
Challenge reducerReduce the 512-bit challenge digest modulo l independently2,84411,9870626
Fixed-base point engineCompute [a]B and [r]B with signed multi-comb arithmetic82,09584,6053522,116
Four-way point codecBatch-invert and compress projective nonce points84,29567,70202,092
Scalar multiply-addCompute and canonicalize r + k*a mod l2,25323,34301,237
Raw direct-row sum202,824219,8033566,745
Adjusted core total202,344219,8033566,745

SHA uses LUTs for Boolean/rotate logic, FFs for working words and schedules, one RAMB36 constant ROM per worker, and 12 DSPs per worker for marked 64-bit state/add operations. Each 512-bit scalar reducer uses 626 DSPs. Every radix-51 field multiplier uses 523 DSPs and has a 17-position, initiation-interval-one pipeline: four in the point engine and four in the codec account for 4,184 DSPs. Two field add/sub lanes add 24 DSPs. Scalar arithmetic accounts for 2,489 DSPs: two 626-DSP digest reducers and the 1,237-DSP multiply-add hierarchy. The remaining 48 DSPs are in SHA. The 356 RAMB36 total is exactly 352 multi-comb table memories plus four SHA constant ROMs. architecture records LUTRAM/SRL use, timing paths, memory traffic, placement boundaries, and evidence filenames.

§Memory and traffic

Arithmetic engines use registers and local BRAM/ROM only. They do not use HBM or DDR as scratch storage. For a signing message of N bytes, the 4 KiB cache gives the logical external message traffic

N + max(N - 4096, 0)

before AXI-line padding. Verification reads N message bytes once. The top-level result separately reports logical stream bytes and external read bytes; host/AXI/HBM traffic must also count command records, result records, line padding, and data-mover behavior.

§Secret-dependent work

Scalar loops have fixed trip counts. Point-table lookup reads every candidate bank at the same public address and selects only after the reads. A secret digit must not select a BRAM address, enabled bank, iteration count, or stall pattern. Zero digits execute the same point-operation schedule as nonzero digits. These properties require emitted-RTL address and enable tests in addition to functional Rust tests.

§Source map

The modules below are the stable way to navigate arithmetic and protocol APIs. Their items are re-exported from the owning implementation crates. Higher-level control is intentionally split into separate crates:

CrateResponsibility
rhdl_ed25519_topchild wiring, arbitration, and top-level synchronous design
rhdl_ed25519_controller_typestop-level I/O, states, and status
rhdl_ed25519_transitionpure high-level next-state kernel
rhdl_ed25519_commandschild-engine command generation
rhdl_ed25519_registersretained cache/work/point register updates
rhdl_ed25519_resultresult and error classification
rhdl_ed25519_apiDalek signing traits over a driver/simulator transport
rhdl_ed25519_modelpinned-Dalek reference behavior and vectors
rhdl_ed25519_simcycle simulation, traces, RTL export, and checksums
architecturecurrent backend map, pipeline, cycle breakdown, and contributor guide

§Evidence and performance status

The compatibility core has end-to-end RHDL simulation evidence, including exact RFC 8032 signing and strict verification. The recorded 64-byte signing run takes 90,928 core cycles. A U280 out-of-context synthesis constrained to 300 MHz records 237,294 LUTs, 94,129 registers, 800 DSPs, and 6 block-RAM tiles, but has negative WNS and therefore must not be described as a closed 300 MHz implementation.

The current cached fixed-64 source passed a 512-signature RTL benchmark against a Dalek-derived public key and signature checksum. The first-to-last output span is 101,438 cycles across 511 intervals, or 198.508806 cycles/signature. One hardware key load plus fill/drain gives a 119,459-cycle finite batch, or 233.318359 cycles/signature.

Matching-source U280 optimized OOC core synthesis uses 202,344 LUTs, 219,803 registers, 356 RAMB36 tiles, 6,745 DSPs, and no URAM. It is 57,656 LUTs, 300,197 registers, and 2,255 DSPs below the project budgets. At the 200 MHz request, WNS is +0.520 ns. The 4.462 ns worst path is inside the scalar reducer, so this report does not establish a 4.000 ns/250 MHz clock. Combining 200 MHz with the RTL interval projects 1,007,512 sustained cached signatures/s and 857,198 signatures/s for the finite batch. These are simulation-plus-OOC projections, not routed or measured FPGA throughput.

Physical implementation currently belongs to the preceding 3,376-DSP builder-pipeline source. Its standalone core is fully routed and closes setup at 199 MHz with +0.018 ns WNS. Its full U280 platform is also fully placed and routed, but misses setup by 1.836 ns with 137,269 failing endpoints; Vitis therefore emits no bitstream or xclbin. Seven active RTL files and the host source differ from that predecessor package. The current 6,745-DSP source has not been placed or routed, so predecessor timing must not be combined with current cycles as a routed claim.

The completed U280 result belongs to the preceding single-scan digest-FIFO revision. Its packaged 220 MHz xclbin closed with +0.001 ns WNS and measured 201,933 steady signatures/s (p50) over a 4,160-signature batch, five warm-ups, and 30 runs; output checksums matched the Dalek-derived reference. The packaged kernel synthesis used 210,098 LUTs, 101,849 registers, 90 RAMB36, and 720 DSPs. It is historical evidence and must not be attributed to the current cached pipeline. See architecture for the current hierarchy, evidence matrix, memory analysis, and AMD comparison status. A matching fixed-64 AMD wrapper now has the same AXI ABI and HBM bank map, but neither implementation has a current comparable U280 hardware benchmark.

§Contributor workflow

The repository-level CONTRIBUTING.md is the complete workflow. In short:

  1. Start in the owning module below, not in generated Verilog.
  2. Add an independent arithmetic or Dalek differential test first.
  3. Compile the affected RHDL kernel and simulate emitted RTL when lowering or a SystemVerilog black box is involved.
  4. Inspect secret-dependent address, enable, and valid traces.
  5. Run the end-to-end simulator before changing a compatibility claim.
  6. Regenerate Vivado evidence before changing an area or timing claim.

On memory-constrained hosts, use one Cargo job and iterate on narrow crates:

cargo test -p rhdl_ed25519_model --release -j 1
cargo test -p rhdl_ed25519_core --release -j 1
cargo test --workspace -j 1
cargo doc --workspace --no-deps -j 1

Re-exports§

pub use types::*;

Modules§

architecture
Architecture And System Guide
field
Field arithmetic modulo 2^255 - 19.
hash_feeder
Streaming SHA-512 input assembly and the 4 KiB message replay cache.
point
Edwards-point arithmetic, scalar multiplication, and point encoding.
scalar
Scalar arithmetic modulo the Ed25519 group order.
sha512
End-to-end hardware SHA-512.
types
Public command, message-stream, pass-request, result, flag, and error encodings shared by the core, simulator, and FPGA shell.