The host sends rows to the card in frames of 256 words, and each row goes inside a frame as a slot tagged raw or LIL. A sparse row may be sent compressed as LIL. A row too dense for LIL is always sent raw, so no row is ever refused or cut short. Every row the card sends back is raw, because the card never compresses.
You do not build frames yourself. The host library does it when your program
calls the functions in sparsr.h. This page is for readers who want to know what
crosses the PCIe link, and why.
What runs today. The Sparsr VM speaks this format from the nightly SDK build of 16 September 2026 onward, as version 3 of its wire protocol. The host library's compressed write,
sparsr_write_data_wmem(), still refuses a row with more than 48 non-zero lanes;sparsr_write_data_wmem_raw()stores a row of any density. The receiver for this format on the FPGA hardware is still being built.
| Part | Words | Holds |
|---|---|---|
| Header | 3 | CMD, LEN, ADDR |
| Payload | 253 | the slots, back to back |
| Total | 256 |
A frame is 256 words in both directions. CMD says what the frame does.
For a frame of rows, LEN is how many payload words the slots use and ADDR is
how many slots the frame holds, from 1 to 4.
A frame contains up to four slots. A sparse row compressed as LIL uses a small part of the payload, so packing several rows in one frame keeps the link from carrying mostly padding.
A slot starts with two words:
| Word | Holds |
|---|---|
| 0 | the tag |
| 1 | the row number |
| 2 onwards | the payload, a whole number of words |
The tag is one 32-bit word:
| Bits | Field | Values |
|---|---|---|
| 31 to 30 | kind | 00 end of the slots, 01 raw, 10 LIL, 11 reserved |
| 29 to 28 | lane width | 2 means 32-bit lanes, the only width used |
| 27 to 16 | count | LIL: entries, 0 to 102. Raw: words in this slot |
| 15 to 0 | offset | Raw: the slot's first word within the row. LIL: 0 |
00 ends the list, so the zero padding at the end of a frame is never
read as a slot.11 and any other lane width are refused.A raw slot's payload is count words of the row, starting at word offset.
A LIL slot's payload is count entries, then zero bytes up to a whole word. Each
entry is:
| Bytes | Holds |
|---|---|
| 1 | the lane number, from 0 |
| 4 | the lane's 32 bits |
A row goes as LIL when it has at most 102 non-zero lanes and its LIL payload is shorter than the raw row. Every other row goes raw.
| Raw row | LIL slot at 102 entries | |
|---|---|---|
| Bytes | 1,024 | 510 |
| Words | 256 | 128 |
Why 102. A LIL slot at its limit is 2 tag words and at most 128 payload words, which always fits the 253 payload words of one frame. So a LIL slot is never split across frames.
The encoder counts the non-zero lanes before it writes anything. The limit decides only which form a row is sent in. It never stops a row from being sent.
A raw row is 256 words, and a frame's payload is 253, so a raw row is split
into pieces. Each piece is a raw slot, and its offset says where in the row
it starts.
The frame does not grow to fit a whole row, because the card's receive buffers are 256 words.
| Destination | What the card does |
|---|---|
| XMEM | Stores the slot as it arrived, raw or LIL |
| WMEM | Expands a LIL slot to a raw row, or stores a raw slot as it is |
The card checks a whole frame before it stores any of it. A slot that does not parse, a LIL entry that breaks the rules above, a piece that does not continue the row in progress, or a header that disagrees with the slots refuses the whole frame. A row number past the end of the memory refuses only that slot.
Rows come back raw. When the host reads rows, each answer frame contains raw slots only, split into pieces in the same way.
The card has one LIL decoder, on the path into WMEM. The DMA uses the same decoder when it copies a LIL row from XMEM into WMEM, so no row is re-encoded on the card. See WMEM, XMEM and the DMA.