Expand description
A reading guide for the SystemVerilog constructs used by the backend.
§Reading the SystemVerilog
This page explains every language pattern needed to read the fast backend. It is intentionally specific to this codebase rather than a general language tutorial.
§Modules and elaboration
A module is a hardware component. Its parameter list is evaluated when the
design is elaborated, before synthesis. Its port list is the physical signal
interface. Instantiating a module creates hardware; it is not a software
function call.
module example #(
parameter TAG_BITS = 16
) (
input wire clk,
input wire [TAG_BITS-1:0] tag_in,
output reg [TAG_BITS-1:0] tag_out
);TAG_BITS changes signal and storage widths. input and output describe
direction at the module boundary. A packed range such as [254:0] is one
255-bit vector. An unpacked range after a name, such as state [0:15], is an
array of sixteen elements.
The backend has no SystemVerilog package imports and no preprocessor
`include dependencies. Build scripts pass all module files to the HDL tool,
which resolves instantiated module names during elaboration.
§Wires, registers, and assignments
wire represents a continuously driven connection. assign creates
combinational logic that is always active:
assign ready_in = !busy;reg means a variable assigned by an always block. In this code it may
synthesize either to a flip-flop or to combinational logic depending on the
block that drives it. The keyword does not by itself guarantee a register.
always @* is combinational. Every output assigned in such a block must receive
a default before conditional overrides, or simulation can infer a latch. The
arbiters use the pattern “default to not found, scan candidates, retain the
first match.”
always @(posedge clk) is sequential. Assignments with <= are nonblocking:
all right-hand sides observe the pre-edge state and all left-hand sides update
together after the edge. This is why a valid bit and its data must travel
through matching registers.
Reset is synchronous and active high inside the cryptographic modules. The AXI
shell receives active-low ap_rst_n and creates areset = ~ap_rst_n.
§Handshakes
Input streams use ready/valid:
accept on a rising edge when valid_in && ready_inThe producer must hold data stable while valid is high and ready is low. Most
arithmetic outputs have valid_out but no ready_out; consumers must provide
enough buffering for every result pulse. Tags are metadata delayed beside the
data so out-of-order internal completion can be associated with the request.
start/ready is the equivalent single-transaction convention used by the
iterative SHA and legacy scalar modules. A request starts on start && ready.
§Generate loops and inferred resources
generate and genvar replicate hardware at elaboration. Four loop iterations
around radix51_field_mul_pipe create four physical multipliers. An ordinary
procedural for inside a clocked block can also unroll into parallel hardware
when its bounds are constants.
Attributes communicate synthesis intent:
| Attribute | Meaning here |
|---|---|
(* use_dsp = "yes" *) | Ask Vivado to map fixed products or MACs into DSP48E2 cells |
(* ram_style = "distributed" *) | Prefer LUTRAM for a small queue or banked context store |
(* ram_style = "block" *) | Prefer block RAM |
(* rom_style = "block" *) | Prefer block RAM for a read-only table |
They are requests, not proofs. The Vivado utilization report is the proof of what was actually inferred.
§Functions
A SystemVerilog function automatic is combinational logic inlined at each use
site. “Automatic” gives each invocation private temporary storage during
simulation. It does not create a shared callable hardware unit. The signer uses
functions for byte reversal, block-word construction, digest repacking, and key
clamping. SHA uses functions for rotations and sigma transforms. The active
field multiplier uses weighted to multiply wrapped coefficients by 19.
§Memories and initialization
$readmemh(file, memory) initializes a simulation memory from hexadecimal text.
For synthesis, multicomb_scan_bank uses AMD xpm_memory_sprom with
MEMORY_INIT_FILE; the .mem files therefore remain required build inputs.
The packaging script adds them to the IP rather than relying on an incidental
working directory.
The SYNTHESIS conditional selects XPM block ROMs in Vivado and ordinary arrays
in simulation. This is a vendor-specific leaf boundary inside otherwise plain
SystemVerilog.
§Bit and byte order
Packed Ed25519 values use the low bits for the first little-endian bytes. SHA-512
words are big endian. reverse_bytes64 converts each 64-bit lane when building
a SHA block. SHA state words are packed from the most significant end of the
512-bit state vector. A packed signature is {S, R}: R occupies bits 255:0
and S occupies bits 511:256, yielding the byte sequence R || S when written
as a little-endian 512-bit record.
§Simulation-only checks
Code under `ifndef SYNTHESIS is omitted from hardware. The point codec uses
this region to terminate simulation if a result queue overflows. $error,
$display, and $fatal are diagnostic operations, not cryptographic logic.