Skip to main content

Module hbm_writer

Module hbm_writer 

Source
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 f go? Frame f of a slice lands at base + 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 by FRAME_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_written counts 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.
WriterInput
Writer inputs, sampled every clock.
WriterOutput
Writer outputs.
WriterState
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.