Skip to main content

Module msg_reader

Module msg_reader 

Source
Expand description

Read 16-byte messages from HBM and deal them to the SLICES slices.

§The problem

Every signature signs one message, and the messages start out in HBM, written there by the host before the run. The slices need them one at a time, each slice at its own pace, and every resulting frame must be traceable back to the exact message it signed. This module is the single AXI4 read master (see crate::axi4) that fetches the messages and hands them out like a card dealer.

§The reasoning

  • Fetch in bursts, deal one by one. HBM is efficient with long bursts and inefficient with tiny reads. A message is only 16 bytes, so the reader fetches 16-beat (1 KiB, 64-message) bursts into a small block-RAM FIFO (first-in, first-out queue) and unpacks four messages from each 64-byte beat.
  • Round-robin, in order. Message n always goes to slice n mod SLICES. The rule is fixed, so the host knows in advance which slice’s frame region will hold each signature, and no tag or arbiter is needed. Because SLICES is a power of two, mod is a bit mask (SLICE_MASK); RHDL kernels avoid / and %.
  • Credits instead of backpressure. A read address is issued only when the FIFO has room for the whole burst. Data that the reader asked for therefore always fits, and rready can be tied high. That is simpler and safer than stalling the R channel.

§How it works

The host writes count messages back to back starting at base (4 KiB aligned). Each 64-byte beat holds four messages. Message n goes to slice n mod SLICES, carrying its index n, which the slice writes into the frame padding so the host can check every frame against the right message.

  HBM ──16-beat bursts──▶ beat FIFO (64 beats, credit-limited) ──▶ unpack 4/beat
                                                                    │ msg n
                                              slice (n mod SLICES) buffer ◀┘

Read addresses are issued only while the FIFO has room for the whole burst, so read data can always be accepted (rready is constant high). A slice buffer holds one message; dealing stalls when the target slice’s buffer is still full, which keeps messages strictly in order.

§Four counters track every beat

All bookkeeping is in beats (64 bytes = 4 messages) except dealt, which counts messages:

  consumed ≤ received ≤ requested ≤ ceil(count / 4)

  requested − consumed   beats the FIFO has promised room for (≤ 64)
  received − consumed    beats sitting in the FIFO, ready to unpack
  lane                   which of the 4 messages in the head beat is next
  dealt                  messages handed to slices = index of the next one

The FIFO is a 64-entry ring of 64-byte words: the low 6 bits of received are the write slot and the low 6 bits of consumed the read slot, so the ring wraps for free.

§Timing

The block-RAM FIFO has one clock of read latency. Moving a beat into the head register therefore takes two steps: a cycle that sets read_pending, then a cycle that captures the RAM output. After that the dealer can hand out at most one message per clock, so one beat (4 messages) is unpacked in 4 clocks when the target slices are free. A slice consumes a message far less often than that (one key per 68 clocks, see crate::slice_schedule), so the reader is not the bottleneck.

§Neighbours

crate::engine supplies base, count and run from its registers, forwards the AXI read channel, and wires each slice’s message_taken back here as taken. The Message type, and the rule that a message is held until taken, come from crate::slice_signer. The dealt count is the host-visible DEALT register and error is FAULT bit 1.

Structs§

MessageReader
The message reader and dealer.
ReaderControl
Resettable control.
ReaderData
Resetless data.
ReaderInput
Reader inputs, sampled every clock.
ReaderOutput
Reader outputs.

Constants§

BURST_BEATS
Beats per read burst (1 KiB).
FIFO_BEATS
Beat FIFO capacity.

Functions§

message_reader_kernel
The reader’s logic.