Expand description
Write a slice’s frames into HBM, one 26-beat burst per frame.
§The problem
Each slice produces finished signatures as a stream of
64-byte frame beats (FrameBeat): 26 beats make one frame, the
1,642-byte signature plus the message index, padded. Those frames must end
up in HBM where the host can read them back and verify them. Something has
to turn “here is a beat” into legal AXI4 write bursts (see
crate::axi4), and it has to say honestly how many frames actually
landed.
§The reasoning
- Where does frame
fgo? Framefof a slice lands atbase + f · 2048: a 2 KiB stride keeps every burst inside one 4 KiB page as AXI requires, and needs no multiplier. The host reads the first 1,664 bytes of each 2 KiB slot. (26 beats × 64 bytes = 1,664 bytes; the rest of the slot is unused.) Multiplying by 2,048 is just a left shift byFRAME_STRIDE_LOG2= 11 bits, which costs no logic. - One HBM port per slice. Every slice owns one writer and one HBM
port, so frame writes scale with the slice count and slices never
compete for a port (the planner’s “frame writes” stage in
crate::config). - Count responses, not beats. A beat that left the kernel may still
fail in memory. Only the B (write response) channel says the data is in
HBM, so that is what
WriterOutput::frames_writtencounts and what the engine waits for before it declares the run done.
§How it works
signer beats ──▶ [W] ──wvalid/wready──▶ HBM
▲ only after its AW
frame counter ──▶ [AW] base + f << 11, awlen = 25
[B] count responses; any non-OKAY response sets `error`The address for frame f is issued when the frame’s first beat is waiting,
and data beats of frame f flow only after that address was accepted, so
the master never promises data it does not have. At most
MAX_OUTSTANDING bursts await responses. frames_written counts frames
whose write response came back OKAY; that, not beats sent, is what the run
is measured on. (Precisely: it counts every write response, and any
response that was not OKAY also sets the sticky error flag, which the
engine reports in its FAULT register. A run with no fault therefore has
every counted frame acknowledged OKAY.)
§Three counters, one invariant
The whole writer is three 32-bit frame counters and an error bit
(WriterState). Because every frame is exactly one burst, each AXI
channel’s progress is just “how many frames has it finished”:
b_frames ≤ w_frames ≤ aw_frames ≤ b_frames + MAX_OUTSTANDING
(answered) (all beats (address
sent) accepted)
aw_frames == w_frames → the next frame still needs its address
aw_frames > w_frames → the current frame's address is out; send beats
aw_frames − b_frames → bursts awaiting a response (capped at 16)Because an address is only requested once a beat is waiting, and at most
one address runs ahead of the data, aw_frames is at most one ahead of
w_frames.
§Timing
Everything is combinational from the current state and inputs to the
outputs, with the counters updated at the clock edge. So a beat that is
valid with an address already accepted goes out the same cycle wready is
high, and a whole frame can stream at one beat per clock (26 clocks) once
its address is accepted.
§Neighbours
The beats come from crate::slice_signer (through crate::slice);
the beat_ready answer goes back to it. crate::engine instantiates
one writer per slice, gives each its base address from the host
registers, sums frames_written to decide when the run is done, and ORs
the error bits into FAULT bit 0.
Structs§
- HbmWriter
- The per-slice HBM write master.
- Writer
Input - Writer inputs, sampled every clock.
- Writer
Output - Writer outputs.
- Writer
State - Writer state (all resettable; small).
Constants§
- FRAME_
STRIDE_ LOG2 - log2 of the frame stride in bytes (2 KiB).
- MAX_
OUTSTANDING - Bursts in flight before the master waits for responses.
Functions§
- hbm_
writer_ kernel - The writer’s logic.