Skip to main content

Module architecture

Module architecture 

Source
Expand description

§Architecture and Contributor Guide

This document explains the current workspace as of 2026-07-12. It is the canonical guide to the project structure, fast signing pipeline, parallelism, cycle counts, and contribution workflow. HANDOFF.md is a historical snapshot of the earlier compatibility-core work and should not be used for current fast backend performance claims.

§1. What This Project Is

The project is an Ed25519 hardware backend derived from the behavior of Curve25519 Dalek. Dalek remains the software oracle; it is not instantiated in the synthesizable datapath. Hardware owns SHA-512 padding, key expansion, scalar arithmetic, Edwards arithmetic, point encoding, and signature assembly.

There are two hardware tracks because compatibility and throughput currently need different architectures.

§Compatibility backend

The compatibility backend is the general RHDL implementation. It supports:

  • public-key derivation from a 32-byte RFC 8032 seed;
  • cold signing and cached-key signing;
  • strict signature verification;
  • arbitrary-length and multipart message streams;
  • a 4 KiB on-chip message replay cache;
  • explicit cached-key clearing and error codes.

It is the semantic integration path for the Dalek-facing API. It is also useful for cycle-accurate RHDL simulation and Verilog generation. It is not the current high-throughput U280 datapath.

§Fixed-64 fast backend

The fast backend specializes one important workload: saturated cold signing of exactly 64 message bytes on an AMD Alveo U280. It trades generality for parallelism:

  • the seed and complete 64-byte message arrive in one request;
  • 32 top-level contexts can be in flight;
  • duplicated workers handle independent cryptographic roles;
  • DSP-heavy radix-17 multiplication is fully pipelined;
  • signed multi-comb multiplication reduces sequential point work;
  • A and R are compressed together with one batch inversion.

The fast backend currently signs only. It does not yet implement the compatibility backend’s arbitrary message lengths, warm signing, verification, clear-key operation, or streaming transport.

§2. Ed25519 Dataflow

For seed sk, message M, basepoint B, field modulus p = 2^255 - 19, and group order l, signing computes:

digest = SHA512(sk)
a      = clamp(digest[0..32])
prefix = digest[32..64]

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

public key = A
signature  = R || S

The dependency graph matters more than the textual order. Once the seed hash finishes, [a]B and the nonce hash can run concurrently. Once r is reduced, [r]B can run while [a]B is still in flight. The challenge hash must wait for both compressed points, and the final multiply-add must wait for reduced k.

flowchart LR
    IN["seed + 64-byte message"] --> H0["SHA512(seed)"]
    H0 --> A["clamp a"]
    H0 --> P["prefix"]
    A --> MA["multi-comb [a]B"]
    P --> HR["SHA512(prefix || M)"]
    HR --> RR["reduce r mod l"]
    RR --> MR["multi-comb [r]B"]
    MA --> C["paired compression"]
    MR --> C
    C --> EA["A"]
    C --> ER["R"]
    EA --> HK["SHA512(R || A || M)"]
    ER --> HK
    HK --> RK["reduce k mod l"]
    RK --> S["S = r + k*a mod l"]
    RR --> S
    A --> S
    S --> OUT["A and R || S"]
    ER --> OUT

§3. Fast Backend Pipeline

The fast top is crates/rhdl_ed25519_fast_sign/rtl/fast64_sign_core.sv. It is a tagged task pipeline rather than a single instruction pipeline. Each accepted request is assigned a context. The context stores its seed, message, intermediate digests, scalars, projective points, encoded points, and pending-work bits.

Workers pull ready tasks from those pending bits. A returned result carries a context tag, allowing the top to update the correct context even when another signature completes a different stage in the same cycle.

flowchart TB
    Q["32 signature contexts"]
    Q --> S0["SHA lane 0"]
    Q --> S1["SHA lane 1"]
    Q --> R0["nonce reducer"]
    Q --> R1["challenge reducer"]
    Q --> P0["[a]B multi-comb lane"]
    Q --> P1["[r]B multi-comb lane"]
    P0 --> PC["12-context paired compressor"]
    P1 --> PC
    Q --> SM["scalar multiply-add"]
    S0 --> Q
    S1 --> Q
    R0 --> Q
    R1 --> Q
    PC --> Q
    SM --> O["tagged public key + signature"]

§Latency versus throughput

Three timing terms recur throughout the code:

  • Latency is the delay from one accepted operation to its result.
  • Initiation interval (II) is the number of cycles before that worker can accept another independent operation.
  • Service interval is the long-run number of cycles per completed signature after all dependencies and shared resources are considered.

The field multiplier demonstrates why the distinction matters. Its latency is 45 cycles, but its II is one cycle. One dependent point context cannot use the next result until 45 cycles later. Several independent contexts can fill those otherwise empty issue slots.

§4. How The Pipelining Works

§Radix-17 field multiplication

Field elements are fifteen little-endian 17-bit limbs. Fifteen limbs exactly cover 255 bits, and 17-bit operands map naturally to DSP48E2 multipliers.

radix17_field_mul_pipe.sv computes a 15-by-15 wrapped convolution:

  • 225 limb products are arranged as 15 systolic dot-product rows;
  • products that wrap past bit 254 are multiplied by 19 because 2^255 = 19 mod p;
  • the MAC array is followed by two registered 15-level carry passes;
  • a final conditional subtraction emits a canonical field element;
  • the complete path has 45 cycles of latency and accepts one multiply per cycle.

Vivado maps one integrated field multiplier to 240 DSP48E2 blocks in the latest report. The extra blocks beyond the 225 plain limb products come from wrapped terms and implementation mapping.

Field addition and subtraction use three registered stages: raw arithmetic, high-bit folding, and conditional subtraction. They also accept one operation per cycle.

§Signed multi-comb fixed-base multiplication

Both [a]B and [r]B use multicomb_mul_stream.sv. The current table has four blocks, eight teeth, and spacing eight. A scalar multiplication performs:

  • 32 signed table lookups and mixed additions;
  • seven point doublings between comb rounds;
  • 280 field multiplications in total: 32 * 7 + 7 * 8.

Each point lane owns eight interleaved contexts and one II=1 field multiplier. The scheduler issues work from another context while a dependent result is moving through the 45-cycle multiplier.

The table lookup is constant-pattern. Each lookup scans 32 public groups in four physical BRAM banks. The secret candidate does not control a BRAM address, bank enable, scan count, or loop exit. Secret magnitude and sign are applied after the reads.

One point lane has 48,960 logical table bytes. Two point lanes therefore have 97,920 logical bytes and synthesize to 88 RAMB36 tiles because the 765-bit word width causes physical BRAM fragmentation.

§Three-phase SHA-512

sha512_compress_3phase.sv splits every SHA-512 round into three registered phases:

  1. Compute the choice/majority and sigma terms and read the round constant.
  2. Finish message-schedule expansion and register T1/T2 partials.
  3. Update the eight working words.

The schedule uses a 16-word circular window instead of storing all 80 expanded words. One compression block occupies a lane for 242 cycles. The top contains two independent lanes and constructs all padding blocks in hardware.

§Fixed-trip-count scalar arithmetic

The nonce and challenge reducers each consume a 512-bit digest in two phases per input bit: calculate, then commit. Each reduction therefore takes 1,024 cycles. There are separate reducer instances for r and k.

The final r + k*a mod l lane first performs eight two-cycle normalization steps and then 256 two-cycle multiply-add steps, for 528 cycles total. The loops have fixed public trip counts.

§Paired point compression

Edwards-Y compression needs affine x = X/Z and y = Y/Z, so the expensive operation is inversion. Compressing A and R separately would perform two inversions. point_compress_pair_stream.sv instead computes one batch inverse:

z_product = Za * Zr
inverse   = 1 / z_product
1 / Za    = inverse * Zr
1 / Zr    = inverse * Za

The fixed chain and affine products require 272 field multiplications per point pair. Twelve contexts share one II=1 field multiplier. This is another latency hiding scheduler: one context waits on a dependency while another enters the multiplier.

§5. Levels of Parallelism

The design uses parallelism at four different levels.

§Within one field multiplication

The 15-by-15 convolution is spatially distributed across DSP MAC cells. This is fine-grained arithmetic parallelism.

§Across dependent point operations

Eight multi-comb contexts and twelve paired-compression contexts interleave on shared field multipliers. This is latency hiding, not duplicated arithmetic.

§Across cryptographic roles

The top duplicates workers where every signature needs two independent jobs:

RoleInstancesReason
SHA-512 compression2Four blocks per cold signature would bottleneck one lane
Wide scalar reduction2Nonce and challenge reductions have separate long service times
Fixed-base multiplication2[a]B and [r]B can overlap
Paired point compression1Batch inversion handles both points together
Scalar multiply-add1One 528-cycle operation per signature is below the main service interval

§Across complete signatures

The top keeps 32 signatures in flight. Pending bits act like a hardware scoreboard. Small round-robin candidate scans choose work for each worker, and tags reconnect each completion to its owning context. This level is what lets the cryptographic dependency graph overlap across many messages.

When changing an arbiter, test both throughput and starvation. The current arbiters intentionally inspect a small candidate set per cycle to avoid a large priority tree in the timing path.

§6. Cycle Breakdown

This section records the cycle model and the latest focused test run on 2026-07-12. Cycle counts are clock-independent; rates require an assumed or closed clock frequency.

§Primitive and stage costs

StageWork per cold 64-byte signatureCyclesParallelism or interpretation
Field add/subAs required by point builderslatency 3, II 1Four add/sub lanes in each multi-comb point engine
Field multiplyAs required by point/inversion chainslatency 45, II 1240 DSPs per integrated multiplier
Seed hash1 SHA-512 block242Scheduled on either of two SHA lanes
Nonce hash1 SHA-512 block242Can overlap [a]B for another/current context
Challenge hash2 SHA-512 blocks484Second block continues from first block’s state
Total SHA work4 blocks968 lane-cyclesTwo lanes give 484 cycles/signature of aggregate service demand
Nonce reduction1 wide digest1,024Dedicated r reducer
Challenge reduction1 wide digest1,024Dedicated k reducer
[a]B fixed-base lane1 scalar1,104.583 modeled serviceEight interleaved contexts
[r]B fixed-base lane1 scalar1,104.583 modeled serviceSeparate eight-context lane, so it overlaps [a]B
Paired A,R compression1 point pair1,088.060 modeled serviceTwelve contexts, 272 field products/pair
Final scalar multiply-add1 r + k*a528One lane

The multi-comb and paired-codec numbers come from 20,000-completion scheduler tests:

8 contexts: 1104.583 cycles/scalar, field 0.253, lookup 0.956, builder 0.424
paired codec: 1088.060 cycles/signature, 229767 signatures/s at 250 MHz

Do not add the rows to estimate end-to-end latency. Stages overlap across contexts, and several rows represent duplicated workers running concurrently.

§Latest integrated RTL run

The current rtl_fast64_sign_sustained test compiled the fast top with Verilator, submitted 64 deterministic seed/message pairs, and compared every public key and signature with pinned Dalek.

MeasurementResult
Correct signatures64 / 64
First output observedcycle 24,182
Final output observedcycle 99,144
Warmup outputs excluded32
Measured output span33,051 cycles
Completion intervals in span31
Average interval in measured span1,066.161 cycles/signature
Largest interval after warmup1,544 cycles
Finite-window projection at 250 MHz234,486 signatures/s

The 234,486 signatures/s value is a projection from RTL cycles. It is not a 250 MHz timing-closure result and not an FPGA benchmark. The finite test can also drain work accumulated before the measurement window. For long-run planning, the eight-context fixed-base scheduler’s 1,104.583 cycles/scalar and the conservative 1,110-cycle service budget are safer current estimates. They project to roughly 226,330 and 225,225 signatures/s at 250 MHz, respectively.

§Clock and synthesis status

The latest archived integrated OOC synthesis report is reports/generated/vivado_u280_fast64_sign_ctx32_point8_priority_300/:

ItemResult
Devicexcu280-fsvh2892-2L-e
Requested clock300.03 MHz / 3.333 ns
WNS-1.078 ns
LUTs360,288
Registers154,783
RAMB3690
DSP48E2720
URAM0

This is an optimized out-of-context synthesis result, not post-route timing. The design fits the U280’s absolute capacity but misses 300 MHz and exceeds the earlier 260,000-LUT project budget. There is no current evidence that the integrated design closes 250 MHz. Always report the cycle projection and the clock evidence as separate facts.

§7. Memory and Access Pattern

The fixed-64 core uses no external HBM or DDR as arithmetic scratch storage. Its core-level command payload is 96 bytes (seed + message), and its result payload is 96 bytes (public key + signature). A Vitis shell may add aligned records and data-mover traffic; that traffic is not represented by the core’s internal access count.

The main local memory is:

StorageLogical sizeLatest physical result
[a]B multi-comb table48,960 bytes44 RAMB36
[r]B multi-comb table48,960 bytes44 RAMB36
Two SHA constant ROMs2 x 5,120 bits2 RAMB36 in integrated synthesis
Fast top context stateregisters/LUTRAMincluded in top LUT/FF totals

The two point tables are duplicated so [a]B and [r]B can run concurrently. The table is much smaller than a large host-style precomputed table, but its wide constant-scan layout consumes more physical BRAM than its logical byte count suggests.

§8. Workspace Structure

§Compatibility and API crates

CrateResponsibility
rhdl_ed25519_apiHardware transport trait plus Dalek Signer, Verifier, MultipartSigner, and MultipartVerifier facades
rhdl_ed25519_modelPinned-Dalek oracle, vectors, strict verification, and multipart reference behavior
rhdl_ed25519_typesWire-level command, message, pass-request, result, and error types
rhdl_ed25519_sha512General streaming SHA-512 engine
rhdl_ed25519_hash_feederPrefix insertion, framing, and 4 KiB message replay cache
rhdl_ed25519_fieldCompatibility radix-2^25.5 field arithmetic
rhdl_ed25519_scalarCompatibility scalar reduction, canonicality, recoding, and multiply-add
rhdl_ed25519_pointExtended Edwards point operations
rhdl_ed25519_scalar_mulCompatibility fixed-pattern scalar multiplication controller
rhdl_ed25519_point_codecPoint compression and decompression
rhdl_ed25519_controller_typesTop-level controller state and handshake types
rhdl_ed25519_transitionHigh-level next-state kernel
rhdl_ed25519_commandsCommands sent to child arithmetic engines
rhdl_ed25519_registersCache/work/point register updates
rhdl_ed25519_resultResult and error assembly
rhdl_ed25519_topCompatibility-core composition and end-to-end tests
rhdl_ed25519_simCycle simulation, traces, checksums, and Verilog export

The compatibility controller is partitioned across crates to keep RHDL macro expansion and Rust memory use manageable. It is one logical controller even though its transition, command, register, and result kernels compile separately.

§Fast backend crates

CrateResponsibilityPrimary current RTL
rhdl_ed25519_fast_fieldRadix-17 field types and DSP black-box wrapperradix17_field_mul_pipe.sv, radix17_field_addsub_pipe.sv
rhdl_ed25519_fast_fixed_baseGenerated tables, constant scan, and signed multi-comb schedulermulticomb_lookup_rom.sv, multicomb_mul_stream.sv
rhdl_ed25519_fast_sha512Fixed-64 SHA compression lanesha512_compress_3phase.sv
rhdl_ed25519_fast_point_codecSingle and paired projective-point compressionpoint_compress_pair_stream.sv
rhdl_ed25519_fast_scalarWide reduction and final multiply-addscalar_arithmetic_2phase.sv
rhdl_ed25519_fast_signContext storage, scoreboarding, arbitration, and result assemblyfast64_sign_core.sv

basepoint_lookup_rom.sv, fixed_base_mul_stream.sv, and radix16_recode_stream.sv are predecessor radix-16 experiments. The integrated fast top uses the multicomb_* modules and the assets/multicomb_t8_n4_s8/ table.

§Tooling and evidence

PathResponsibility
tools/basepoint_table_genRegenerates and checksums tables from pinned Dalek constants
tools/vivadoOOC synthesis and implementation Tcl scripts
benchCommon XRT host, AMD Vitis Security wrapper, and RHDL RTL shell
reports/generatedGenerated simulations and synthesis reports; never hand-edit
upstreamPinned source snapshots; treat as read-only

§9. Contributor Workflow

§Choose the backend first

Make compatibility changes in the non-fast crates. Make fixed-64 throughput changes in the fast crates. A fast-path optimization does not automatically become a compatibility-core feature.

§Read in this order

For the fast path:

  1. crates/rhdl_ed25519_fast_sign/rtl/fast64_sign_core.sv
  2. crates/rhdl_ed25519_fast_sha512/rtl/sha512_compress_3phase.sv
  3. crates/rhdl_ed25519_fast_scalar/rtl/scalar_arithmetic_2phase.sv
  4. crates/rhdl_ed25519_fast_fixed_base/rtl/multicomb_lookup_rom.sv
  5. crates/rhdl_ed25519_fast_fixed_base/rtl/multicomb_mul_stream.sv
  6. crates/rhdl_ed25519_fast_point_codec/rtl/point_compress_pair_stream.sv
  7. The corresponding tests/ scheduler and RTL tests

For the compatibility path, begin with rhdl_ed25519_types, then rhdl_ed25519_controller_types, rhdl_ed25519_top, and the arithmetic child crates.

§Test from narrow to broad

# Software oracle and trait facade
cargo test -p rhdl_ed25519_model --release -j 1
cargo test -p rhdl_ed25519_api --release -j 1

# Fast arithmetic and RTL blocks
cargo test -p rhdl_ed25519_fast_field --release -j 1
cargo test -p rhdl_ed25519_fast_fixed_base --release -j 1
cargo test -p rhdl_ed25519_fast_sha512 --release -j 1
cargo test -p rhdl_ed25519_fast_scalar --release -j 1
cargo test -p rhdl_ed25519_fast_point_codec --release -j 1

# Explicit cycle models
cargo test -p rhdl_ed25519_fast_fixed_base --release \
  --test multicomb_scheduler_model -- --nocapture
cargo test -p rhdl_ed25519_fast_point_codec --release \
  --test scheduler_model -- --nocapture

# End-to-end fixed-64 correctness and finite sustained window
cargo test -p rhdl_ed25519_fast_sign --release \
  --test rtl_fast64_sign_sustained -- --nocapture

Keep -j 1; .cargo/config.toml also sets one build job. Verilator tests set their own compile parallelism to one.

§Regenerate tables deliberately

The multi-comb assets are generated from the pinned Dalek constants. Do not edit .mem files by hand.

cargo run -p basepoint_table_gen --release -j 1

Review the generated manifest, logical size, and SHA-256 values. Then rerun lookup, scalar-multiplication, and end-to-end signature tests.

§Preserve constant-pattern behavior

For secret-dependent code, review emitted RTL and tests for all of the following:

  • memory addresses are public and follow the same sequence for every scalar;
  • bank enables do not depend on a secret digit;
  • loop counts and valid timing are fixed;
  • zero and nonzero digits execute the same point-operation schedule;
  • output tags do not expose secret-dependent stalls.

§Treat performance evidence as a stack

A performance claim needs all layers below it:

  1. Arithmetic or Dalek differential correctness.
  2. Scheduler-model service interval.
  3. RTL simulation with every output validated.
  4. OOC synthesis resource and timing report.
  5. Post-route timing closure.
  6. U280 execution and XRT/Vitis profiling.

Do not combine a cycle result from one revision with a clock report from another revision. Do not call a 250 MHz cycle projection a 250 MHz FPGA result.

§10. Terms To Use When Discussing The Design

  • Compatibility core: the general RHDL derive/sign/verify implementation.
  • Fast fixed-64 backend: the specialized cold signer for 64-byte messages.
  • Context: per-signature state retained while workers process other jobs.
  • Lane: a physical worker instance, such as one SHA compressor or one point multiplier.
  • II=1 field multiplier: 45-cycle result latency with a new independent input accepted every cycle.
  • Signed multi-comb: the fixed-base scalar multiplication method using the four-block, eight-tooth precomputed table.
  • Constant scan: every secret lookup reads the same public address sequence and selects after memory access.
  • Batch inversion: compressing A and R with one inversion of Za*Zr.
  • Cycle projection: simulated cycles divided into an assumed clock.
  • Closed throughput: sustained hardware throughput after post-route timing closure and output validation.

The short project description is: the fast backend combines a DSP-pipelined radix-17 field multiplier, constant-scan signed multi-comb point multiplication, context interleaving, duplicated cryptographic workers, and paired point compression to overlap many complete Ed25519 signatures without external scratch memory.