Skip to main content

Module architecture

Module architecture 

Source
Expand description

§Architecture And System Guide

This is the canonical explanation of the repository as of 2026-07-14. It is written for someone who needs to understand the cryptography, trace the RTL, reason about throughput and area, and decide whether a result is actually supported by evidence. For commands and contribution policy, also read CONTRIBUTING.md.

The most important distinction is that the workspace contains two backends:

  • a general RHDL compatibility backend with the Dalek-facing feature set;
  • a specialized SystemVerilog backend for cached-key signing of 64-byte messages at high throughput on an AMD Alveo U280.

They use the same Ed25519 semantics and the same pinned Dalek oracle, but they do not currently have the same interface or feature set.

§1. Evidence Vocabulary

Performance discussions use four different evidence levels. Keep them separate.

TermMeaning
RTL cyclesA simulator counted cycles and checked outputs
OOC projectionRTL cycles combined with out-of-context optimized-netlist timing
Placed/routed attemptVivado completed physical implementation; timing may still fail
Routed resultA complete implementation met timing after placement and routing
Hardware resultAn xclbin ran on the U280 and returned validated outputs

An OOC result is useful for architecture work, but it does not include the platform shell, final placement, routing congestion, or runtime behavior. Likewise, a finite completion span is not automatically a steady initiation interval: a pipeline may accumulate work before the first measured output and then drain a burst.

Every result in this guide names its source scope. The latest checked-in fast source has a matching focused RTL benchmark and optimized OOC synthesis. A builder-pipeline predecessor has both a standalone routed-core result and a fully placed/routed Vitis platform attempt. The standalone core closes setup at 199 MHz, but the platform attempt misses timing and emits no xclbin. The latest DSP-heavy source has not yet been placed or routed.

§2. What The Two Backends Do

§Compatibility backend

The compatibility backend is assembled from RHDL synchronous devices. It owns the broad behavioral contract:

  • derive a public key from a 32-byte RFC 8032 seed;
  • sign with a cold key or a cached key;
  • strictly verify signatures;
  • accept arbitrary-length and multipart message streams;
  • hash entirely in hardware;
  • cache up to 4 KiB for signing replay and request later bytes again;
  • report framing, point, scalar, cache, and equation errors;
  • clear cached key material.

Its top is rhdl_ed25519_top::Ed25519Core. The controller is split across several small crates because compiling one very large RHDL transition function consumes too much host memory. The split is a compile-time ownership boundary, not a collection of independent accelerators.

§Fast fixed-64 backend

The fast backend is the current U280 throughput experiment. It supports:

  • hardware expansion and caching of one 32-byte seed;
  • derivation and caching of the corresponding public key;
  • cached-key Ed25519 signing of exactly 64 message bytes;
  • tagged, many-request operation with 64 signature contexts;
  • explicit key clearing while idle.

It does not currently provide arbitrary-length messages, per-request cold key expansion, verification, or the compatibility command protocol. It is a specialized backend, not a drop-in replacement for the RHDL top.

The current top is:

crates/rhdl_ed25519_fast_sign/rtl/fast64_cached_sign_core.sv

§3. Ed25519 Work

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

h      = SHA512(sk)
a      = clamp(h[0..32])
prefix = h[32..64]
A      = encode([a]B)

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

public key = A
signature  = R || S

The compatibility backend performs that graph for each cold signing command or reuses cached a, prefix, and A for a cached-key command.

The fast backend makes the cache boundary explicit. Key loading performs the seed hash and A derivation once. Message requests are accepted only after key_loaded is true, so every timed signature reuses the expanded key.

flowchart LR
    K["32-byte seed"] --> HS["SHA512 seed block"]
    HS --> AP["clamp a and retain prefix"]
    AP --> AB["multi-comb [a]B"]
    AB --> AC["compress A"]
    AC --> KC["cached a, prefix, A"]

    M["64-byte message"] --> NR["SHA512 prefix || M"]
    KC --> NR
    NR --> RR["reduce r mod l"]
    RR --> RB["multi-comb [r]B"]
    RB --> RC["pairwise compress R"]
    RC --> CH["SHA512 R || A || M"]
    KC --> CH
    M --> CH
    CH --> RK["reduce k mod l"]
    RK --> SM["S = r + k*a mod l"]
    RR --> SM
    KC --> SM
    RC --> OUT["R || S"]
    SM --> OUT

§4. Fast Top-Level Interface

The fast top is synchronous and uses ready/valid input handshakes:

Signal groupContract
load_key_valid, load_key_ready, key_seed_inAccept one seed when both handshake signals are high
key_loaded, cached_public_keyExpanded key and public key are available
valid_in, ready_in, message_in, tag_inAccept one 64-byte message and caller tag
valid_out, signature_out, tag_outPresent one tagged signature for one cycle
clear_keyClear the cached key only while idle
internal_errorSticky protocol or internal queue error until reset

There is no output backpressure signal. Any deployment wrapper must consume every valid_out pulse or place a FIFO immediately after the core.

load_key_ready is high only when no key is valid and no key load is active. To replace a key, wait for all signatures to finish, assert clear_key, and then load a new seed. Clearing during key expansion or while a signing context is active sets internal_error and does not perform the idle clear operation.

The clear operation zeros the cached seed, secret scalar, prefix, and public key. It does not scrub every inactive context register or LUTRAM entry. That is the implemented zeroization boundary; a stronger claim needs more RTL and tests.

§AXI deployment shell

bench/rhdl/rtl/fast64_cached_sign_io_kernel.sv wraps the core with AXI-Lite control and four independent 512-bit AXI masters:

Argument/portHBM bankRecord shape
messages / gmem00one 64-byte message line per signature
commands / gmem11one 64-byte seed-command line per key load
signatures / gmem22one 64-byte `R
summary / gmem33one 64-byte summary line per launch

The scalar arguments are message_stride_lines, message_len, batch, and operation. The shell accepts LoadCachedKey, SignWarm, and ClearCachedKey; it rejects unsupported operations and warm signing without a loaded key. The argument order and bank mapping are identical in amd_fast64_cached_sign_io_kernel.cpp.

The shell contains a 64-entry completion FIFO because the core has a pulse output and no backpressure input. It also submits one internal dummy signature for odd batch sizes so the paired compressor cannot strand a final real point; the dummy never causes an external read or signature write. The summary records payload cycles, core window, output span, key-load cycles, exact AXI beats, completion count, error flags, and queue high-water marks. Its packed C++ definition is bench/include/ed25519_bench_abi.hpp.

§5. Tagged Task Graph

The signer is not a serial FSM. It is a scoreboard of 64 per-signature contexts connected to independent workers. Each context retains its message, nonce/challenge scalars, encoded R, challenge chaining state, caller tag, and pending-work bits.

Workers select ready pending bits, perform fixed-latency work, and return the context tag. A completion updates only that context and makes the next dependency eligible. Different signatures can therefore occupy every stage at once and may finish internal stages out of order.

The current worker inventory is:

WorkerInstancesInternal concurrencyPer cached signature
SHA-512 block builders2one block under construction per builder3 blocks
SHA-512 compression workers4one block per worker3 blocks
512-bit scalar reducers2fully pipelined, II 1one r, one k
Fixed-base point engine116 scalar contexts, four field multipliersone [r]B
Point result FIFO132 projective pointsone point
Pair compressors4four contexts per wayhalf a pair on average
Scalar multiply-add1fully pipelined, II 1one S

Key expansion temporarily uses one SHA worker, the point engine, and a compression way. Signing requests are not accepted until expansion is done, so key work does not compete with steady cached signing.

§What “pipeline” means in this design

The 512 in the standard throughput test is a request count, not a pipeline depth. The fast signer is also not one linear chain of 512 register stages. It is a tagged task graph with several independently pipelined or iterative workers:

QuantityMeaning
64 signing contextsMaximum messages whose dependency state can be live concurrently
16 point contextsScalars that can be interleaved through the shared fixed-base engine
17 field-multiplier positionsPhysical valid/data pipeline inside each radix-51 multiplier
46 scalar-reducer positionsThree fixed-product folds plus final canonical reductions, all II 1
69 scalar-multiply-add positions22-position wide multiply, one add register, and one 46-position reducer
3 SHA round phasesRegistered datapath reused for each of 80 SHA-512 rounds
242 SHA worker cyclesOccupancy of one SHA worker for one compression block
512 benchmark requestsSample population used to measure the steady completion span

Latency through a recirculating worker is not the same as physical pipeline depth. For example, a SHA block spends about 242 cycles in a worker, but the worker contains a three-phase round datapath that is reused 80 times. Likewise, the 9,503 cycles between completion of the measured key load and the first cached signature include repeated SHA rounds, multi-comb rounds, and a fixed inversion chain. They are not 9,503 independently occupied register stages.

The throughput metric is the completion interval after all workers overlap:

101,438 completion-span cycles / 511 intervals
  = 198.508806 cycles/signature

§One signing request, stage by stage

The following table is the shortest complete trace through fast64_cached_sign_core.sv. “Eligibility” names the state that makes the next task visible to an arbiter. Every worker request carries the six-bit context index; the completion tag writes the result back to that same context.

StageEligibility/inputFunctionCompletion and next dependency
1. Allocatevalid_in && ready_inSearch an eight-context rotating window, register the 64-byte message and caller tag, then claim one free contextSet active[i] and nonce_hash_pending[i]
2. Build nonce blocknonce_hash_pending[i]A SHA builder emits the 16 words containing the prefix, message, padding, and length; byte reversal and padding are performed by sha_block_wordSubmit HASH_NONCE with SHA IV and context tag
3. Hash nonceFree member of the four-worker SHA poolPerform all 80 SHA-512 rounds using the three-phase round datapath and 16-word circular schedulePut the 512-bit digest and context into nonce_fifo0 or nonce_fifo1
4. Reduce nonceNon-empty nonce digest FIFOConvert the SHA digest to a little-endian integer and reduce it modulo l with the DSP-folded reducerWrite nonce_scalar[i] = r; set point_r_pending[i]
5. Compute nonce pointpoint_r_pending[i] and a free point contextCompute [r]B with the signed multi-comb engine: 32 constant scans/mixed additions and seven doublingsPush projective (X,Y,Z) and the context tag into the 32-entry point FIFO
6. Compress nonce pointTwo queued signing points, or one key pointPair two projective points, share one inversion between them, recover affine coordinates, and encode Edwards-YSerialize the two encoded results through encoded_write_count; store encoded_r[i]; set challenge0_pending[i]
7. Hash challenge block 0challenge0_pending[i]Build the exact 128-byte concatenation of R, A, and M, then run one SHA compression from the IV; this block has no inline padding because it is fullStore the returned chaining state in a bank selected by context parity/high bit; set challenge1_pending[i]
8. Hash challenge block 1challenge1_pending[i]Compress the all-padding second block with bit length 0x400, starting from the saved block-0 chaining statePut the final digest and context into challenge_fifo0 or challenge_fifo1
9. Reduce challengeNon-empty challenge digest FIFOReduce the digest modulo l using the second dedicated DSP reducerWrite challenge_scalar[i] = k; set muladd_pending[i]
10. Compute Smuladd_pending[i]Compute the wide product k*a, add r, and reduce the sum modulo lReturn canonical S with the context tag
11. RetireMultiply-add completionRead the banked encoded R, assemble signature_out = concat(R,S), and restore the caller tagPulse valid_out; clear active[i], making the context reusable

The dependency chain can be read directly from the pending bits and queues:

nonce_hash_pending
  -> nonce_fifo[0|1]
  -> reducer_r
  -> point_r_pending
  -> point FIFO
  -> paired codec / encoded_write_count
  -> challenge0_pending
  -> saved challenge state
  -> challenge1_pending
  -> challenge_fifo[0|1]
  -> reducer_k
  -> muladd_pending
  -> valid_out and active=0

This is a scoreboard, not an FSM that forces all signatures through the same stage together. Context 7 can be in challenge SHA while context 19 is in point multiplication and context 42 is retiring. Internal completion order may differ from input order; tag_in/tag_out gives the caller the association.

§One-time key-load path

Key loading uses separate global pending flags because it owns the shared workers before signing begins:

  1. key_hash_pending builds and hashes the 32-byte seed with hardware padding.
  2. digest_secret_scalar clamps the low digest half into a, while digest_prefix retains the high half as the deterministic nonce prefix.
  3. key_point_pending sends a through the same multi-comb engine used for nonce points.
  4. The point FIFO marks this result as a key point. The paired codec supplies the identity as its unused partner and returns A = encode([a]B).
  5. The completion stores key_secret_scalar, key_prefix, and key_public_key, clears key_load_busy, and asserts key_valid_q.

ready_in remains low until step 5. Consequently, the measured warm-signing service interval does not hide key expansion inside message work.

§Arbitration, queues, and backpressure

The top deliberately uses bounded rotating searches instead of 64-way global priority muxes:

  • allocation, point, and multiply-add arbiters inspect eight candidates;
  • the two SHA request arbiters inspect eight candidates of one parity and then advance their bases by 16;
  • the point and multiply-add bases advance by eight;
  • the four digest FIFOs separate nonce/challenge work and the two SHA return ports before the dedicated scalar reducers;
  • the 32-entry point FIFO decouples multi-comb completion from pair compression;
  • encoded_write_count serializes a two-result codec completion into the banked encoded-R store.

Input backpressure is explicit through ready_in. Worker boundaries use pending bits, ready/valid handshakes, or FIFOs. The core output is a one-cycle pulse and has no ready_out; the AXI shell therefore provides a 64-entry completion FIFO. Overflow or an illegal clear operation sets the sticky internal_error flag.

The active point engine has two modular add/subtract lanes. During mixed addition its fourth field multiplier, otherwise unused in that phase, calculates 2Z; point doubling still schedules a separate fixed-pattern twice operation. Builder choice and operands are registered before entering the add/subtract lanes.

§Context banking

Wide message, encoded-point, and challenge-state arrays are divided by context parity and high context bit into four banks. SHA request logic reads from the appropriate small bank instead of synthesizing a single 64-way, 512-bit mux.

Several arbiters inspect only a small rotating candidate window. That choice is intentional. A complete 64-way priority tree can save occasional idle cycles but often creates a worse clock path and high-fanout routing.

§Digest FIFOs

SHA completions can arrive while a downstream reducer is busy or selecting the other SHA return lane. Four FIFOs decouple those boundaries:

  • nonce digest from SHA return port 0;
  • nonce digest from SHA return port 1;
  • challenge digest from SHA return port 0;
  • challenge digest from SHA return port 1.

Each FIFO has 32 entries. An entry contains a 512-bit digest and six-bit context tag, so the four FIFOs hold 66,304 logical bits in total. The two reducers choose between the corresponding return-port FIFOs without mixing nonce and challenge work.

§6. SHA-512 Hardware

sha512_compress_3phase.sv performs every SHA-512 round in three registered phases:

  1. Read the round word/constant and calculate sigma plus choice/majority terms.
  2. finish schedule expansion and register T1/T2 partials;
  3. update the eight working words.

The schedule is a 16-word circular window. It does not store all 80 expanded words. One compression occupies a worker for 242 cycles. The four-worker pool has two request ports and two result ports. At most two workers are launched in one cycle, so at most two deterministic-latency completions coincide.

The two top-level builders assemble one 1,024-bit block over 16 cycles and send it to any free compression worker. Padding and length fields are generated in the top; the host never prehashes.

For a cached 64-byte signature:

  • prefix || M is 96 bytes and fits in one padded block;
  • R || A || M is exactly 128 bytes, so it uses one full data block and one padding block;
  • total steady SHA demand is three compression blocks.

The ideal aggregate SHA floor is therefore:

3 blocks/signature * 242 cycles/block / 4 workers
  = 181.5 cycles/signature

The current matching-source integrated interval is 198.509 cycles/signature. SHA remains the largest ideal service demand, with 17.009 cycles/signature, or about 9.37%, of scheduling, builder, queue, and downstream backpressure above its 181.5-cycle floor. The current source marks the 64-bit state and adder registers for DSP implementation. Matching OOC synthesis maps 12 DSP48E2s to each worker, or 48 DSPs across the four-worker pool; the boolean functions, rotates, state, and circular schedule still use LUTs and registers.

§7. Field Arithmetic

§Representation

The fast datapath exposes a packed canonical 255-bit field element. The current multiplier internally uses five radix-2^51 limbs. This is different from the compatibility backend’s ten alternating 26/25-bit limbs and from the fast backend’s predecessor 15-limb radix-2^17 multiplier.

§Radix-51 multiplier

radix51_field_mul_pipe.sv decomposes every 51x51 limb product into nine 17x17 products. Across the 5x5 limb matrix that is still 225 small products, which map naturally to DSP48E2 multipliers. Wrapped coefficients use 2^255 = 19 mod p, followed by two radix-51 carry passes and a final canonical subtraction.

Properties of one current field multiplier:

  • 17-position valid pipeline;
  • initiation interval one;
  • fixed operation schedule;
  • 523 DSP48E2 blocks in the current matching OOC report;
  • canonical packed result below p.

The point engine owns four such multipliers, and the four compression ways own one each. Those eight multipliers account for 4,184 DSPs. The increased DSP count comes from mapping product reconstruction, coefficient accumulation, and carry-stage additions into DSPs as well as the 225 explicit 17x17 products.

§Addition and subtraction

radix17_field_addsub_pipe.sv retains its historical filename but operates on packed canonical field elements. Its registers separate input capture, raw add/subtract, high-bit folding by 19, and final canonical subtraction. It accepts one operation per cycle.

The two active add/subtract instances each map to 12 DSP48E2s in the current matching OOC report. Field and point-formula arithmetic therefore account for 4,208 DSPs: 4,184 in eight multipliers plus 24 in the two add/subtract lanes. The current worst OOC path is no longer the old point-builder input mux. It is inside the scalar reducer’s registered conditional-subtraction chain.

§8. Scalar Arithmetic

The fast scalar pipeline uses the identity:

l = 2^252 + q
q = 0x14def9dea2f79cd65812631a5cf5d3ed

Instead of a 512-step serial divider, scalar_reduce_wide_dsp_pipe folds the high portion around bit 252 using fixed-size radix-2^16 constant products. Three fold stages and final conditional reductions produce a canonical scalar. Every product and normalization stage has a public fixed schedule.

There are two reduction pipelines, one for r and one for k. Each accepts a new digest every cycle. The final scalar_muladd_dsp_pipe computes the wide product k*a, adds r, and passes the sum through the same reducer. It also has initiation interval one.

Current integrated DSP attribution is:

Scalar blockDSP48E2
Nonce reducer626
Challenge reducer626
Multiply-add1,237
Scalar total2,489

This is the main deliberate shift from LUT-heavy serial scalar arithmetic to DSP-heavy throughput arithmetic. Inside the multiply-add hierarchy, the wide multiplier accounts for 338 DSPs and its nested reducer accounts for 626. The remaining 273 DSPs are inferred by the registered accumulation and normalization logic owned directly by the multiply-add hierarchy.

§9. Signed Multi-Comb Fixed-Base Multiplication

multicomb_mul_stream.sv computes [s]B with a signed 4x8x8 multi-comb. It adjusts and recodes the scalar into fixed public rounds, then performs:

  • 32 table lookups and mixed additions;
  • seven point doublings between comb rounds;
  • no secret-dependent loop count or early exit.

The point engine holds 16 independent scalar contexts. Four field multipliers launch all products in a formula phase together. While one context waits for a 17-position field result, lookup and builder logic can advance another context. Only two add/subtract lanes are instantiated. The common mixed-add path uses multiplier lane three as a constant-times-two unit for 2Z, saving a third add/subtract lane; the rarer doubling path retains a separate twice task.

In the current 512-signature integrated profile, 513 point requests are issued and completed: one key point and two nonce points for each paired compression request after batching. Point issue spans 109,789 cycles and completion spans 109,907 cycles. The point FIFO reaches a high-water mark of only two entries. These integrated counters are the relevant current evidence; short isolated point bursts can hide accumulated work before their first output.

§Constant-scan lookup

The logical table has four banks. Each bank contains four public comb blocks by 32 public candidate groups. One entry stores three 255-bit Projective Niels coordinates, for a 765-bit word.

Each lookup reads all 128 candidates in four cycles:

4 physical banks * 8 public groups/cycle * 4 cycles
  = 128 words read per lookup

The secret candidate never controls a BRAM address, enable, cycle count, or exit condition. Its low two bits choose among already-read bank outputs; its high five bits match the public group tag after the reads. The sign is applied to the selected coordinates after scanning.

The logical table is:

4 candidate banks * 4 public blocks * 32 groups * 765 bits
  = 391,680 bits
  = 48,960 bytes

Eight read copies of each logical bank provide 32 parallel words per cycle. Vivado maps the resulting wide/deep arrangement to 352 RAMB36 tiles. This is why the 48,960-byte logical size and physical BRAM count appear so different.

§10. Point Compression

Edwards-Y compression converts projective (X,Y,Z) to affine coordinates and encodes y with the parity of x. Inversion of Z dominates the work.

point_compress_pair_stream.sv amortizes one inversion across two points:

z_product = Za * Zb
inverse   = 1 / z_product
1 / Za    = inverse * Zb
1 / Zb    = inverse * Za

The current wrapper has four physical compression ways. Each way owns four contexts and one radix-51 field multiplier. This creates 16 in-flight point pairs without a global 16-way operand mux. A four-entry result queue per way absorbs bursty completions.

The point engine pushes projective results into a 32-entry FIFO. Normal signing work pops two nonce points and compresses them as one pair. Key loading may present a single point; the second member is the identity, and only the key result is consumed.

The active cached signer instantiates four compression ways. Its integrated 512-signature profile issues and completes 257 pair requests, including the single key point paired with the identity. Pair completion spans 109,908 cycles. A focused six-lane experiment currently reports 223.659 cycles/pair, but that test explicitly overrides LANES=6; it must not be attributed to the active four-lane signer. Until the four-lane isolated test is rerun against the current source, use the integrated counters rather than a standalone codec service claim.

§11. Current Integrated Test

The current cached benchmark loads one deterministic key in hardware, submits 512 deterministic 64-byte messages, and validates the public key and aggregate signature checksum against pinned Dalek. It was rerun against the current DSP-accumulation and two-limb-normalization source on 2026-07-14:

PROFILE input=512 span=98864 stalls=98353 sha=1537 span=119080
        reduce_r=512 span=98958 reduce_k=512 span=101443
PROFILE point_issue=513 span=109789 point=513 span=109907
        codec_issue=257 span=109907 codec=257 span=109908
        muladd_issue=512 span=101438 muladd=512 span=101438
        max_point_fifo=2
PASS cached_benchmark=512 cycles=119459 key_load=8517 first=18020 span=101438

The counts are internally consistent. One seed expansion plus three SHA blocks per signature gives 1 + 3*512 = 1,537 SHA completions. One key point plus one nonce point per signature gives 513 point completions. Pairing those points for compression gives ceil(513/2) = 257 codec requests. Both scalar reducers and the final multiply-add complete exactly 512 signing operations.

Correct throughput interpretation is:

completion intervals = 512 - 1 = 511
steady interval       = 101,438 / 511
                      = 198.508806 cycles/signature

finite batch average  = 119,459 / 512
                      = 233.318359 cycles/signature

The steady interval uses the first-to-last completion span and excludes the distance from benchmark start to the first output. The finite batch average includes key expansion, pipeline fill, and drain. The large stalls value is input backpressure while all 64 signing contexts are occupied; it is not a cryptographic failure or an external-memory wait.

This test proves current-source RTL behavior for its deterministic 512-message workload and Dalek-derived checksum. It is not a full-workspace test, an AXI platform test, or hardware evidence. Existing real-core AXI batch-1 and batch-65 simulations and the latest archived broad workspace log were produced from predecessor source and remain regression evidence only.

§12. Current U280 OOC Results

The latest report whose source matches the current DSP-heavy RTL is an optimized out-of-context run, not placement or routing:

reports/generated/
  u280_fast64_cached_singlepoint_dspacc_norm2_ooc200_20260714/

§Current matching-source resource use

ResourceUsedU280 availableU280 useProject budgetBudget status
CLB LUTs202,3441,303,68015.52%260,00057,656 under
Registers219,8032,607,3608.43%520,000300,197 under
DSP48E26,7459,02474.75%9,0002,255 under
RAMB363562,01617.66%not specifiedfits
RAMB1804,0320%not specifiedfits
URAM09600%not specifiedfits

The direct hierarchy is:

Direct hierarchyLUTsRegistersRAMB36DSP
Point compression, four ways84,29567,70202,092
Fixed-base point engine82,09584,6053522,116
Scalar multiply-add2,25323,34301,237
Nonce reducer2,72211,9820626
Challenge reducer2,84411,9870626
Four-worker SHA pool18,48912,088448
Top-level own logic10,1268,09600

The direct rows report 202,824 LUTs before cross-hierarchy optimization. Vivado combines 480 LUTs across hierarchy boundaries, producing the adjusted top-level total of 202,344. Registers, DSPs, and block RAM sum exactly. Do not add nested field-multiplier or reducer rows to these direct rows.

§Current primitive ownership

Direct hierarchyFunctionCurrent primitive mapping
Top-level own logicOwn 64 contexts, scoreboards, banked messages and points, arbitration, tags, and queuesControl and storage use LUTs, LUTRAM, SRLs, and FFs; no DSP or block RAM is directly owned
SHA poolExpand the key seed and process nonce and challenge blocksFour constant ROMs use four RAMB36s; state and registered 64-bit additions infer 12 DSPs per worker, 48 total
Two reducersProduce canonical nonce r and challenge kEach three-fold radix-16 pipeline uses 626 DSPs and accepts one digest per cycle
Fixed-base point engineCompute [a]B at key load and [r]B for signingFour 523-DSP field multipliers, two 12-DSP add/sub lanes, context state, and the 352-RAMB36 constant-scan table total 2,116 DSPs
Four-way point compressionBatch-invert and encode projective pointsOne 523-DSP field multiplier per way plus independent context/control state totals 2,092 DSPs
Scalar multiply-addCompute S = r + k*a mod l338 DSPs in the wide multiplier, 626 in its reducer, and 273 in owned accumulation/normalization logic total 1,237 DSPs

DSP attribution is independently checkable:

8 field multipliers * 523       4,184
2 point add/sub lanes * 12         24
2 standalone scalar reducers      1,252
scalar multiply-add               1,237
4 SHA workers * 12                   48
                                   -----
core total                         6,745 DSP48E2

Block RAM remains exactly 352 RAMB36s for replicated constant-scan tables plus four RAMB36s for SHA constants. Current OOC synthesis also reports 16,540 LUTRAM primitives and 2,948 SRLs. No external scratch memory or URAM is inferred.

§Current matching-source timing

At the 200 MHz request, period 5.000 ns, optimized OOC timing reports WNS +0.520 ns; setup timing is met. The worst path is:

  • source: muladd/reducer/biased_reg[25];
  • destination: muladd/reducer/reduced1_reg[247];
  • data path: 4.462 ns;
  • 14 logic levels: one DSP input stage, six DSP ALU stages, six DSP output stages, and one LUT3;
  • 89.041% logic delay and 10.959% estimated routing delay.

This proves only optimized OOC setup timing at 200 MHz. The report warns that 788 inputs and 786 outputs have no I/O delays, and it was generated with maximum-delay analysis, so it does not establish hold timing. It also does not prove 250 MHz: a 250 MHz clock has a 4.000 ns period, while the reported worst data path is 4.462 ns. The current source has not been placed or routed.

§Current throughput projection

Combining the current RTL interval with 199 and 200 MHz clock assumptions gives:

Assumed clockSteady, clock / 198.508806Finite 512-item batch, clock / 233.318359
199 MHz1,002,474 signatures/s852,912 signatures/s
200 MHz1,007,512 signatures/s857,198 signatures/s

These are simulation-plus-OOC projections. They are not measurements from an xclbin or a U280 board.

§Predecessor standalone-core route

The immediately preceding builder-pipeline source was fully placed and routed as a standalone core. It is preserved because it provides physical routing evidence, but its 3,376-DSP datapath does not match the current 6,745-DSP RTL. The package checksum manifest now reports eight mismatches against the working tree, including all seven active RTL files and the host source.

At 200 MHz that routed checkpoint missed by only 0.007 ns. Rechecking the same checkpoint at 199 MHz produced WNS +0.018 ns, TNS zero, and no setup failures. All 569,193 routable nets were fully routed with no route errors. Its routed resources were 248,852 LUTs, 237,472 registers, 356 RAMB36s, 3,376 DSPs, and no URAM. It was 11,148 LUTs below the 260,000-LUT project budget.

The report still omitted I/O delays on 788 inputs and 786 outputs. Its DRC contained 748 warnings: 719 unpipelined-DSP-input warnings, 8 DSP PREG warnings, 20 DSP MREG warnings, and one net-with-no-routable-load warning. There were no DRC errors. Those warnings motivated the newer DSP input and accumulator pipelining.

Evidence directories are:

reports/generated/u280_fast64_cached_quadmul_ctx16_codec4x4_addsub2_mul2z_builderpipe_route200_20260713/
reports/generated/u280_fast64_builderpipe_route199_timing_20260713/

§Predecessor full U280 platform placement

Vitis also synthesized, placed, and fully routed that predecessor inside the U280 platform. Placement took 2 h 18 min 53 s and routing took 3 h 21 min 50 s; the complete link ran for 7 h 01 min 49 s. The result is a routed checkpoint, but timing failed and Vitis stopped before bitstream and xclbin generation.

At the 199 MHz kernel clock, period 5.025 ns, the routed platform reported WNS -1.836 ns, TNS -91,954.969 ns, and 137,269 failing setup endpoints out of 1,014,742. Hold timing passed with WHS +0.006 ns and zero hold failures. The 6.404 ns worst path was 89% route delay and ran from point-context state to a point field multiplier DSP input. There is therefore no valid predecessor platform throughput result.

The predecessor kernel’s post-synthesis estimate was 256,641 LUTs, 238,210 registers, 363 RAMB36 plus one RAMB18, 3,376 DSPs, and no URAM. The full routed device, including the fixed U280 platform, used 381,802 LUTs, 419,571 registers, 558 RAMB36s, six RAMB18s, and 3,380 DSPs. Subtracting the report’s fixed column leaves 274,173 LUTs, 283,485 registers, 365 RAMB36 plus one RAMB18, and 3,376 DSPs in the dynamic region; that delta includes AXI/interconnect and is not a kernel-only number. SLR1 reached 73.98% CLB, 52.12% LUT, 64.43% BRAM, and 31.18% DSP occupancy, with 16,623 SLR crossings and no SLL pipeline registers.

The archived platform evidence is:

reports/generated/u280_fast64_io_reference_link199_timing_failed_20260714/

The correct conclusion is deliberately narrow: placement and full routing have occurred for the predecessor, and standalone-core timing closed at 199 MHz. The full predecessor platform did not close timing, and the current source has not yet reached placement. No xclbin or current hardware benchmark exists.

§13. Memory And Access Analysis

§External HBM/DDR

The fast cryptographic core performs no external scratch reads or writes. It retains the expanded key, messages in flight, scalar state, point state, SHA state, and lookup table on chip.

At the core interface, one cached signature requires:

  • 64 input bytes for the message;
  • one caller tag;
  • 64 output bytes for R || S;
  • one returned tag.

The AXI shell makes the external traffic exact. For batch B:

LoadCachedKey: 1 key read + 1 summary write = 128 HBM bytes
SignWarm:      B message reads + B signature writes + 1 summary write
             = 128*B + 64 HBM bytes

Thus warm signing has 128 payload bytes per signature plus 64/B bytes of summary overhead. The batch-65 real-core simulation observed exactly 65 message reads and 65 signature writes; it also exercised the odd-batch dummy without adding external traffic. Reads and writes are currently single-beat transactions rather than coalesced bursts. PCIe traffic in the host runner is 64*B bytes host-to-device and 64*B + 64 bytes device-to-host per measured run, excluding one-time command setup.

The compute-only benchmark still generates messages and reduces signatures to checksums in logic. Use it for core service analysis, and use the AXI shell for memory and host-visible comparison.

§Local BRAM table traffic

One fixed-base scalar multiplication performs 32 constant scans. Each scan reads 32 words per cycle for four cycles:

32 lookups * 4 cycles * 32 words * 765 bits
  = 3,133,440 local bits/signature
  = 391,680 byte-equivalent/signature

This is distributed across 352 local RAMB36 tiles and is not a DRAM/HBM transaction count. At the current projected sustained rates it represents about 392.649 GB/s at 199 MHz and 394.622 GB/s at 200 MHz of aggregate local read-equivalent bandwidth. Both numbers are achieved through replicated parallel block-RAM ports, not one memory bus.

§Other local storage

StorageLogical organizationSynthesized resource
Multi-comb table48,960 logical bytes, eight read copies per bank352 RAMB36
SHA constants80 x 64 bits per worker, four workers4 RAMB36
Digest FIFOs4 x 32 x 518 bitsLUTRAM
Point FIFO32 x (3 x 255-bit coordinates + tag)distributed storage
Context state64 messages, scalars, flags, tags, encoded pointsregisters and LUTRAM

The initialized table copies contain 391,680 logical bytes. Wide-port BRAM fragmentation reserves 12,976,128 physical bits, or 1,622,016 bytes of RAMB36 capacity. This is capacity, not data read from HBM. The current core OOC report contains 16,540 LUTRAMs and 2,948 SRLs. The previously reported seven-RAMB36 plus one-RAMB18 shell delta belongs to the predecessor source; a matching-source shell has not yet been synthesized or placed. No current-source URAM is used.

§14. Security And Constant-Pattern Properties

The design aims for fixed secret-dependent work, not for a formal side-channel proof.

Current structural properties include:

  • SHA round counts depend only on the fixed public message shape;
  • scalar reduction and multiply-add pipelines have fixed stage counts;
  • multi-comb performs the same 32 additions and seven doublings for every scalar;
  • every table lookup reads all 128 candidates in the same public sequence;
  • secret candidates do not control BRAM addresses or enables;
  • point compression uses fixed inversion chains;
  • scheduler tags and completion timing depend on worker occupancy and public request order, not scalar digits.

Things this evidence does not prove:

  • resistance to power, electromagnetic, or fault attacks;
  • physical leakage introduced by place-and-route;
  • complete zeroization of all stale context storage;
  • security of a surrounding AXI, DMA, or host stack;
  • formal equivalence to Dalek for every possible input.

Any change to lookup addressing, valid timing, early exits, context allocation, or key clearing deserves a security review in addition to a functional test.

§15. Compatibility Backend Map

The compatibility path is intentionally more modular:

CrateResponsibility
rhdl_ed25519_apiDalek Signer, Verifier, MultipartSigner, and MultipartVerifier facade
rhdl_ed25519_modelPinned-Dalek oracle and vector behavior
rhdl_ed25519_typesCommands, message words, pass requests, results, errors
rhdl_ed25519_sha512General streaming SHA-512
rhdl_ed25519_hash_feederPrefix insertion, padding, framing, 4 KiB replay cache
rhdl_ed25519_fieldTen-limb radix-2^25.5 field arithmetic
rhdl_ed25519_scalarScalar reduction, canonicality, recoding, multiply-add
rhdl_ed25519_pointExtended Edwards point operations
rhdl_ed25519_scalar_mulFixed-pattern scalar multiplication controller
rhdl_ed25519_point_codecEdwards-Y compression and decompression
rhdl_ed25519_controller_typesController state and child handshakes
rhdl_ed25519_transitionHigh-level next-state logic
rhdl_ed25519_commandsChild-engine command generation
rhdl_ed25519_registersCache, work, and point register updates
rhdl_ed25519_resultResult and error assembly
rhdl_ed25519_topGeneral core composition and end-to-end tests
rhdl_ed25519_simCycle simulation, traces, waveforms, Verilog export

The compatibility message protocol uses 64-bit words with byte keep and last metadata. Signing may need repeated message passes. The hash feeder caches the first 4 KiB and requests replay only beyond that offset, avoiding a large on-chip arbitrary-message buffer while keeping SHA-512 in hardware.

Verification is strict by default: encoded points must decode canonically, S must be canonical, small-order inputs are rejected, and the Edwards verification equation must hold. Error codes separate framing, cache, canonicality, point, and equation failures.

§16. Fast Backend Map

CrateCurrent role
rhdl_ed25519_fast_fieldRadix-51 multiplier and packed field add/sub pipelines
rhdl_ed25519_fast_fixed_baseGenerated multi-comb table, constant scan, point scheduler
rhdl_ed25519_fast_sha512Three-phase compressor and four-worker pool
rhdl_ed25519_fast_scalarDSP-folded reduction and multiply-add
rhdl_ed25519_fast_point_codecFour-way paired projective compression
rhdl_ed25519_fast_signKey cache, 64 contexts, scoreboards, FIFOs, result assembly

Predecessor files remain for regression and design history. In particular, radix17_field_mul_pipe.sv, basepoint_lookup_rom.sv, fixed_base_mul_stream.sv, radix16_recode_stream.sv, and fast64_sign_core.sv are not the datapaths used by the latest cached OOC run. Follow instantiations from fast64_cached_sign_core.sv instead of assuming the newest-looking filename is active.

§17. Test Strategy

Correctness is layered:

  1. Rust model tests check RFC and Dalek behavior.
  2. Big-integer tests check field and scalar arithmetic.
  3. Icarus/Verilator tests check individual RTL blocks with back-to-back input.
  4. Point tests verify projective invariants and compressed basepoint multiples.
  5. End-to-end tests compare cached signatures or checksums with Dalek.
  6. Compatibility simulation checks command framing, message replay, errors, derive/sign/verify, and selected malformed inputs.
  7. Vivado reports check the synthesized structure, area, and timing.
  8. A final claim requires routed hardware with every output validated outside the timed region.

The current fast-path tests to understand first are:

crates/rhdl_ed25519_fast_field/tests/rtl_radix51_pipeline.rs
crates/rhdl_ed25519_fast_field/tests/rtl_addsub_pipeline.rs
crates/rhdl_ed25519_fast_fixed_base/tests/rtl_multicomb_lookup.rs
crates/rhdl_ed25519_fast_fixed_base/tests/rtl_multicomb_mul.rs
crates/rhdl_ed25519_fast_sha512/tests/rtl_compress.rs
crates/rhdl_ed25519_fast_scalar/tests/rtl_scalar_dsp_pipeline.rs
crates/rhdl_ed25519_fast_point_codec/tests/rtl_pair_throughput.rs
crates/rhdl_ed25519_fast_sign/tests/rtl_fast64_cached_sign.rs
crates/rhdl_ed25519_fast_sign/tests/rtl_fast64_cached_benchmark.rs
bench/rhdl/tb/fast64_cached_sign_io_kernel_tb.sv

The current short scheduler-model tests for multi-comb and compression retain constants from predecessor field pipelines. They remain useful for historical algorithm experiments, but their printed service numbers are not evidence for the active radix-51, four-multiplier, four-way codec RTL.

§18. Benchmarking And AMD Comparison

There are now three benchmark layers:

  • fast64_benchmark_kernel generates deterministic messages and checksum reductions in logic. It isolates algorithm service and key-load cost.
  • fast64_cached_sign_io_kernel and amd_fast64_cached_sign_io_kernel expose the same fixed-64 cached-key operation through four 512-bit AXI ports. The shared fast64_io_bench host measures both implementations.
  • ed25519_bench uses the common XRT/AXI ABI for general RHDL and AMD kernels. It covers the broader compatibility operations and message lengths.

These are not interchangeable timing numbers. A fair RHDL-versus-AMD result needs the same operation mode, message length, batch size, data movers, memory banks, warmups, run count, validation, and clock evidence.

The fair-comparison AMD fixed-64 wrapper has this HLS estimate:

ResourceAMD estimate
LUTs101,959
Registers88,510
BRAM18120
DSP14
URAM0
Estimated path4.680 ns against a 5.00 ns request

There is no completed equivalent AMD U280 benchmark JSON or xclbin in the repository. The project therefore has no measured current RHDL-versus-AMD speedup claim. The operation and external ports now match, but the resource numbers remain different evidence classes: the RHDL row is Vivado optimized OOC utilization and the AMD row is an HLS estimate. The older general AMD wrapper’s 261,168-LUT report remains relevant only to the general benchmark.

fast64_io_bench performs five warmups and 30 measured runs by default, reports p50/p95 kernel and end-to-end latency/rate, checks exact beat counters, poisons and verifies the output buffer outside timing, and writes the raw signatures for pinned-Dalek validation. On this local machine its build is blocked by missing XRT headers under /opt/xilinx/xrt/include; use the configured U280 host before claiming comparison results.

§19. Historical U280 Baseline

reports/generated/u280_baseline/ contains a routed 220 MHz xclbin and U280 run for an older single-scan, digest-FIFO, cold-signing architecture. It closed with +0.001 ns WNS and measured approximately 201,933 steady signatures/s at p50 over a 4,160-signature workload.

That result proves an earlier architecture family ran on the board. It does not validate the current cached 198.509-cycle pipeline, current resources, or current OOC clock. Keep the historical revision label attached to it.

§20. How To Think About Optimizations

Start with service demand, then timing, then area.

§Service demand

For each shared worker, calculate operations per signature divided by physical instances. The largest known ideal demand is the first throughput candidate:

SHA:        3 * 242 / 4 = 181.5 cycles/signature
point scan: 32 * 4      = 128 active scan cycles/signature
scalar:     II 1 pipelines, below these demands

The active four-lane codec’s isolated current-source interval has not been rerun, so it is intentionally omitted from this service table. The measured 198.509 cycles/signature is consistent with SHA being the largest known demand, with 17.009 cycles/signature of integrated overhead above the ideal floor. Adding arithmetic lanes without changing SHA and scheduler pressure is unlikely to improve top-level throughput materially. A fifth SHA worker could reduce the floor only if builders, request/return ports, context state, and timing can feed it.

§Timing

After pipelining arithmetic, wide context muxes, cascaded DSP arithmetic, and high fanout can dominate. Read the critical path before adding a register. A register improves clock only if the tag/control pipeline and all dependent state transitions move with it. The current matching-source OOC path is a 4.462 ns scalar-reducer subtraction path and passes at 200 MHz. The predecessor standalone core routes at 199 MHz, while its full platform route fails because point-state-to-DSP routing stretches the worst path to 6.404 ns. The current source has no placed timing result.

§Area

Use hierarchical reports. A logical optimization can move cost rather than remove it:

  • extra BRAM ports replicate memories;
  • fewer contexts reduce state but may expose field latency;
  • more DSPs can reduce LUT arithmetic but add routing pressure;
  • wider constant scans reduce scan cycles but multiply BRAM copies;
  • duplicated codecs remove global muxes but duplicate inversion state.

The right target is the complete kernel under its clock and throughput goals, not the smallest isolated primitive.

§Evidence

For every optimization, retain a before/after table with:

  • exact source identity;
  • validated vector count;
  • latency, II, and measured service window;
  • LUT, FF, LUTRAM, SRL, BRAM18/36, URAM, and DSP;
  • target clock, WNS, critical path, and implementation stage;
  • whether the result is simulation, OOC, routed, or hardware.

§21. Evidence Map

EvidencePath
Current cached benchmark source and rerun commandcrates/rhdl_ed25519_fast_sign/tests/rtl_fast64_cached_benchmark.rs; cargo test -p rhdl_ed25519_fast_sign --test rtl_fast64_cached_benchmark -j 1 -- --nocapture
Current core OOC evidencereports/generated/u280_fast64_cached_singlepoint_dspacc_norm2_ooc200_20260714/
Predecessor standalone route at 200 MHzreports/generated/u280_fast64_cached_quadmul_ctx16_codec4x4_addsub2_mul2z_builderpipe_route200_20260713/
Predecessor standalone route recheck at 199 MHzreports/generated/u280_fast64_builderpipe_route199_timing_20260713/
Predecessor full U280 platform route and timing failurereports/generated/u280_fast64_io_reference_link199_timing_failed_20260714/
Predecessor package manifestreports/generated/FAST64_PACKAGE_SHA256SUMS; mismatches current working source and must not identify it
Predecessor real-core AXI batch-65 simulationreports/generated/fast64_io_real_core_batch65_verilator_20260713.log
AMD equal-I/O HLS estimatereports/generated/amd_fast64_cached_sign_io_kernel_csynth_20260713.rpt
Older source checksum setreports/generated/SOURCE_SHA256SUMS; verify before assigning it to a revision
Predecessor full workspace testsreports/generated/full_workspace_tests_addsub3_20260713.log
Historical routed U280 baselinereports/generated/u280_baseline/
General AMD HLS estimatereports/generated/amd_ed25519_kernel_csynth.rpt
Compatibility simulation summaryreports/generated/simulation_results.json
External traffic analysisreports/generated/traffic_analytics.json
Generated RTL checksumsreports/generated/checksums.sha256

§22. Current Gaps

  • Generate a checksum manifest for the exact current source revision.
  • Complete and archive a full-workspace test run for the current source.
  • Synthesize the current AXI shell and verify shell-inclusive resource use.
  • Place and route the exact current source in the full U280 platform.
  • Close platform timing and produce a loadable xclbin before making a hardware throughput claim.
  • Run and archive an equivalent AMD U280 benchmark.
  • Check every returned signature in final hardware validation, not only an XOR checksum.
  • Either update the old scheduler models to current radix-51/four-way timing or label/remove them more explicitly in code.
  • Decide whether the fast backend should gain arbitrary message lengths and verification or remain a deliberately separate signing accelerator.
  • Strengthen zeroization if the deployment threat model requires all context storage to be scrubbed.

§23. Glossary

  • Cached signing: seed expansion and public-key derivation happen once; subsequent messages reuse a, prefix, and A.
  • Context: per-signature state retained while other signatures use shared workers.
  • Latency: cycles from accepting one operation to its result.
  • Initiation interval (II): cycles between accepting independent operations into one worker.
  • Service interval: long-run cycles between completed signatures after all shared-resource demands are included.
  • Multi-comb: fixed-base scalar multiplication using a structured precomputed table and fixed sequence of additions/doublings.
  • Constant scan: read every public candidate address in the same pattern, then select the secret candidate from returned data.
  • Batch inversion: obtain inverses for two projective points using one field inversion plus multiplications.
  • OOC: out-of-context synthesis/optimization without the final platform shell and placement/routing.
  • WNS: worst negative setup slack; negative means the requested clock does not meet timing.

The compact mental model is: a 64-context tagged task graph feeds a four-worker hardware SHA pool, DSP-folded scalar pipelines, one four-multiplier signed multi-comb point engine, and four paired-compression ways. The current RTL steady interval is 198.509 cycles/signature. The matching-source core fits the requested area limits and meets 200 MHz only in optimized OOC analysis, projecting about 1.008 million cached 64-byte signatures/s at that clock. A predecessor standalone core closes routed timing at 199 MHz, but its full U280 platform route fails timing and emits no xclbin. Current-source placement and hardware throughput therefore remain open, while the architecture still uses one 64-byte HBM read and one 64-byte write per warm signature and no external scratch memory.