RNG — LFSR + rand_algo Dispatch Tree
This page documents the second random generator the GPSIMD POOL engine carries — a
32-bit linear-feedback shift register (LFSR) — and the rand_algo dispatch tree
that forks between it and Xorwow. The LFSR is not a separate generator function and not
a dedicated opcode: it shares the per-draw advance driver with the Xorwow path and differs
only in seed initialization. The fork is a single bbci instruction in the shared
SetSeeds body, driven by a saltu(rand_algo, 1) comparison that resolves the four-value
ISA enum down to the two algorithms POOL actually wires. This page decodes that fork
instruction-exact, reconciles it against the shipped host ISA headers, proves which silicon
generations carry the LFSR, and closes the RNG subsystem with a five-way map tying the
software-Xorwow, TIE-Xorwow, LFSR, Dropout, and Rand2 pieces together.
This is the Cadence Vision-Q7 GPSIMD compute core's own firmware — windowed-ABI Xtensa
code on the ncore2gp (Cairo) core — and the companion NX/SEQ sequencer firmware that
validates the operand before it reaches the compute core. Every device fact below is
byte-pinned to a carve re-derived this session from libnrtucode_internal.so; every
host-ISA fact is read out of the public aws_neuron_isa_tpb_*.h headers shipped in the same
customop-lib package. Confidence and evidence tags follow the project
Confidence & Walls Model: HIGH/MED/LOW ×
OBSERVED/INFERRED/CARRIED.
NOTE — what was carved this session, and the exact objects used. The firmware container is
…/custom_op/c10/lib/libnrtucode_internal.so(10,276,288 B, sha256b7c67e898a116454…— the FW-26/FW-27/FW-41 anchor, re-verified in-task). The per-generation POOL images are.rodata-resident_get.datasymbols (so file offset == symbol VA,.rodataAddress==Off==0x46b0; no.datadelta applies). Carved withdd, disassembled with the nativextensa-elf-objdump(XTENSA_CORE=ncore2gp, GNU Binutils 2.34.20200201 / Xtensa Tools 14.09) shipped inside the gpsimd-tools package.
carved object file off / size sha256 (first 8) role MARIANA_Q7_POOL_DEBUG_IRAM0x4e2440/0x1ed4047f76629the fork + bodies MARIANA_Q7_POOL_DEBUG_DRAM0x501180/0x15d8002cacff0the log strings MARIANA_PLUS_Q7_POOL_DEBUG_IRAM0x7adf40/0x1ef001c9c15bcbyte-match proof MARIANA_NX_POOL_DEBUG_IRAM0x44b540/0x1c08041b6c798SEQ enum gate MARIANA_NX_POOL_DEBUG_DRAM0x4675c0/0x7000— SEQ error strings CAYMAN_Q7_POOL_DEBUG_IRAM0x249020/0x1ea40513a8a22per-gen anchor CAYMAN_NX_POOL_DEBUG_IRAM0x1b1420/0x1c820— per-gen anchor SUNDA_Q7_POOL_RELEASE_DRAM0x4cec0/0xa540— per-gen negative The MARIANA IRAM sha
47f76629and DRAM sha02cacff0reproduce FW-27 exactly; the CAYMAN IRAM sha513a8a22reproduces the FW-00 anchor.[HIGH/OBSERVED]
1. The headline
- The full
rand_algoenum is recovered verbatim from the shipped public ISA headers and is airtight-consistent with the disassembled fork:NEURON_ISA_TPB_RAND_ALGORITHM { LFSR=0, PCG32=1, PHILOX=2, XORWOW=3 }. On the POOL/Q7 software RNG, exactly two of the four are wired:LFSR(0)andXORWOW(3).PCG32(1)andPHILOX(2)are ISA-defined but hit the SEQ "rand_algorithm(0x%x) not currently supported on POOL" arm.[HIGH/OBSERVED] - The LFSR is a single-
u32-word-per-lane generator (vs Xorwow's 5–6 words). Thed4_rand.hheader states it verbatim, and the Q7LfsrSetSeedsbody's structural minimalism — frame 192, one FLIX bundle, nomemset, no multi-word seed chain, no Weyl const — confirms the one-word state.[HIGH/OBSERVED header + structure] - There is no separate
LfsrRngfunction and no dedicated LFSR opcode. The per-draw advance is shared with the Xorwow(TIE) driver0xbc78; only the seed init differs (LfsrSetSeeds 0xb700vsXorwowSetSeeds(TIE) 0xb744). The fork lives only in the sharedSet/GetSeedsbodies.[HIGH/OBSERVED] - The LFSR feedback polynomial / tap form is not byte-recoverable. No classical tap-mask
constant appears anywhere in the Q7 POOL IRAM/DRAM, and the state-write lives in a
FLIX-desync'd bundle. The recurrence is a 32-bit single-word xorshift-style LFSR over
GF(2); the exact taps and Galois-vs-Fibonacci direction are unrecovered.
[width=32 HIGH; taps LOW/UNRECOVERED] - The output contract is the
RAND_POST_PROCenum:RAW_U32(0)/UNIFORM_IN_RANGE(1)/NORMAL(2, unsupported).RAW_U32is theuint32Dropout/Rand2 consume;UNIFORM_IN_RANGEis the0x3F800000float seam.[HIGH/OBSERVED] - Dropout's inline RNG is the LFSR:
s3d3_dropout.hsays "generates a u32 LFSR for each lane for each element; converts it to an f32 in range (0.0 to 1.0); compares to threshold". This pins the Dropout consumer to the LFSR arm.[HIGH/OBSERVED]
2. The rand_algo enum — recovered from the shipped ISA headers
The authoritative source is aws_neuron_isa_tpb_common.h, present per-arch under
…/c10/include/neuron_<arch>_arch_isa/tpb/. The NEURON_ISA_TPB_RAND_ALGORITHM enum is
byte-identical across cayman/mariana/maverick; SUNDA's is truncated (no XORWOW):
/* aws_neuron_isa_tpb_common.h (mariana: line 987; cayman: 894; maverick: 1142) */
typedef enum NEURON_ISA_TPB_RAND_ALGORITHM {
NEURON_ISA_TPB_RAND_ALGORITHM_LFSR = 0,
NEURON_ISA_TPB_RAND_ALGORITHM_PCG32 = 1, // PCG-XSH-RR, 64-bit state, 32-bit output
NEURON_ISA_TPB_RAND_ALGORITHM_PHILOX = 2, // Philox4x32, 10 round
NEURON_ISA_TPB_RAND_ALGORITHM_XORWOW = 3, // XORWOW implementation <-- ABSENT on SUNDA
} NEURON_ISA_PACKED NEURON_ISA_TPB_RAND_ALGORITHM;
[HIGH/OBSERVED — all four arch headers re-grepped: SUNDA stops at PHILOX=2; the other three carry XORWOW=3.]
Two companion enums fix the output and the state source:
typedef enum NEURON_ISA_TPB_RAND_POST_PROC { /* mariana common.h:994 */
NEURON_ISA_TPB_RAND_POST_PROC_RAW_U32 = 0, // raw u32 in [0, 2^32-1]
NEURON_ISA_TPB_RAND_POST_PROC_UNIFORM_IN_RANGE = 1, // fp32 in (min_fp32, max_fp32)
NEURON_ISA_TPB_RAND_POST_PROC_NORMAL = 2, // Not supported yet
} NEURON_ISA_PACKED NEURON_ISA_TPB_RAND_POST_PROC;
typedef enum NEURON_ISA_TPB_RAND_SRC { /* mariana common.h:1001 */
NEURON_ISA_TPB_RAND_SRC_RNG_LFSR = 0,
NEURON_ISA_TPB_RAND_SRC_RNG_XORWOW = 1,
NEURON_ISA_TPB_RAND_SRC_RNG_PHILOX = 2,
NEURON_ISA_TPB_RAND_SRC_OUTPUT_CVT_LFSR = 3,
} NEURON_ISA_PACKED NEURON_ISA_TPB_RAND_SRC;
[HIGH/OBSERVED] Note the asymmetry between the two enums: RAND_ALGORITHM and RAND_SRC
are different namespaces with different integer encodings — XORWOW is 3 as an
algorithm but 1 as a source (RNG_XORWOW), and LFSR is 0 as both an algorithm and a
source (RNG_LFSR), with a fourth source OUTPUT_CVT_LFSR(3) being a conversion variant that
also binds to RandAlgorithm::LFSR. So the LFSR has two source modes (RNG_LFSR and
OUTPUT_CVT_LFSR); the device dispatcher only ever reads RAND_ALGORITHM, never RAND_SRC,
when it forks (§4).
GOTCHA — do not conflate
rand_algowithrand_generator. A reimplementer wiring the operand must keep the two fields independent:rand_algorithm(offset 12) selects the recurrence and is what the device branches on;rand_generator/RandSrc(offset 15 inD4_RAND, offset 24 inS1_RAND) selects the state-storage mode and is validated for mutual consistency but never used to pick the code path. Thehas_valid_*constraint blocks ind4_rand.henumerate the legal pairings — e.g.(RandSrc::RNG_XORWOW, RandAlgorithm:: XORWOW),(RandSrc::RNG_LFSR, RandAlgorithm::LFSR),(RandSrc::OUTPUT_CVT_LFSR, RandAlgorithm::LFSR)— and reject mismatches at decode.[HIGH/OBSERVED]
2.1 The "general Rand" vs the "POOL Rng" distinction
d4_rand.h documents the general TPB hardware Rand instruction (// note, different from RNG instruction), whose own comment block names "Three allowed values: LFSR (disabled in ISA
as of Oct 7, 2022 due to ucode issues), PCG32 (disabled …), PHILOX" — i.e. the general HW
Rand op effectively does only PHILOX. That is a different instruction from the POOL
GPSIMD software RNG documented here. The POOL Rng handler (the 'S: Rng …' SEQ front-end,
opcode RNG, see Rand2) is a software generator running on the POOL compute
engine, and it implements LFSR + XORWOW. The XORWOW=3 enum value was added at cayman
(absent on SUNDA) specifically for this POOL software RNG. [HIGH/OBSERVED — header text + the per-gen enum diff in §6]
3. The operand structs — field offsets cross-checked against the device
The dispatcher's field reads (§4) are a decisive cross-check: every byte offset the device
loads matches a named struct field. The three relevant structs (all 64 B, ISA_STATIC_ASSERT-pinned):
/* NEURON_ISA_TPB_D4_RAND_STRUCT (the Rand op + RandGetState) — d4_rand.h:95 */
struct {
HEADER header; /* +0 ( 0- 3) */
EVENTS events; /* +4 ( 4-11) */
RAND_ALGORITHM rand_algorithm; /* +12 <-- the fork field */
uint8_t rand_num_steps; /* +13 <-- LFSR multi-step count */
RAND_POST_PROC post_process; /* +14 <-- RAW_U32/UNIFORM/NORMAL */
RAND_SRC rand_generator; /* +15 */
RAND_PARAM params[4]; /* +16 (16-31) min_fp32/max_fp32 for UNIFORM */
DTYPE dtype; /* +32 */
uint8_t num_active_channels; /* +34 1..128 */
TENSOR4D dst_mem_pattern; /* +44 (44-63) */
};
/* NEURON_ISA_TPB_S1_RAND_STRUCT (RandSetState) — s1_rand.h:26 */
struct {
/* header+events at +0..+11 */
RAND_ALGORITHM rand_algorithm; /* +12 <-- SEQ validates this {0,3} */
uint8_t num_active_channels; /* +13 */
RAND_STATE_UPDATE update_state; /* +14 */
DTYPE in_dtype; /* +15 */
TENSOR1D src_mem_pattern; /* +16 (16-23) */
RAND_SRC rand_generator; /* +24 */
uint32_t imm_state[8]; /* +32 (32-63) the 8x u32 seed/state operand */
};
/* NEURON_ISA_TPB_D3_RAND_STRUCT (backs RAND2 *and* RNG) — d3_rand.h:19 */
struct {
/* header+events at +0..+11 */
RAND_ALGORITHM rand_algorithm; /* +12 <-- Rand2/RNG fork on the same enum */
/* +13 */
RAND_POST_PROC post_process; /* +14 */
/* … 64 B total … */
};
The match is byte-exact: rand_algorithm@12 is precisely the [a1+12] the shared SetSeeds
body stores/loads at 0xb6df/0xb6e2, and dst_mem_pattern@44 is the [a4+45] the driver
reads. The struct↔opcode binding is fixed by the shipped instruction_mapping.json
(struct2opcode): D4_RAND_STRUCT → {RAND, RAND_GET_STATE}, S1_RAND_STRUCT → {RAND_SET_STATE}, D3_RAND_STRUCT → {RAND2, RNG}, PSEUDO_SET_RNG_SEED_STRUCT → {PSEUDO_SET_RNG_SEED}. [HIGH/OBSERVED — header offsets + JSON mapping re-read in-task]
NOTE — Rand2 and the POOL
Rngop shareD3_RAND_STRUCT, hence share therand_algofield. TheD3_RANDvalidation block references bothRandAlgorithm::XORWOWandRandAlgorithm::LFSR, so a Rand2/Rng instruction selects its generator through the same{LFSR, XORWOW}mechanism documented here. The device routing of Rand2 specifically is Rand2's scope and is not decoded on this page; the struct-level evidence that it reuses this enum is[HIGH/OBSERVED].
4. The dispatch tree — instruction-exact
Two cores cooperate. The SEQ/NX sequencer validates the rand_algo enum before the
operand reaches the compute core (the {0,3} gate, §4.1). The Q7 POOL compute core then
decodes the operand, computes the algorithm-select bit, and forks at the shared SetSeeds
body (§4.2). The per-draw generator dispatcher does not fork on rand_algo (§4.3).
4.1 The SEQ enum gate — POOL accepts exactly {0, 3}
In MARIANA_NX_POOL_DEBUG_IRAM, the RandSetState validation (0x301b…0x304a) builds two
equality flags and admits the operand iff rand_algo ∈ {0, 3}:
301b: l32i a2, a1, 64 ; a2 = rand_algorithm
301e: addi a3, a2, -3
3021: movi.n a2, 1
3023: saltu a3, a3, a2 ; flag1 = ((rand_algo-3) < 1) = (rand_algo == 3)
3026: s8i a3, a1, 60
3029: l32i a3, a1, 64
302c: saltu a3, a3, a2 ; flag0 = (rand_algo < 1) = (rand_algo == 0)
302f: s8i a3, a1, 56
3032: l8ui a3, a1, 56
3035: bbsi a3, 0, 0x3041 ; if rand_algo == 0 -> SUPPORTED
303b: l8ui a2, a1, 60 ; (else) a2 = flag1 = (rand_algo == 3)
3041: extui a2, a2, 0, 1
3044: s8i a2, a1, 52
3047: l8ui a2, a1, 52
304a: bbsi a2, 0, 0x3065 ; if (rand_algo == 0 OR == 3) -> SUPPORTED (0x3065)
3056: const16 a10, 0xe89 ; else: ERROR string @DRAM 0xe89
3059: call8 0x183b0 ; log
DRAM 0xe89 is, byte-exact:
"S: RandSetState : rand_algorithm(0x%x) not currently supported on POOL\n". The symmetric
RandGetState gate (same {0,3} structure) logs DRAM 0xe41:
"S: RandGetState : rand_algorithm(0x%x) not currently supported on POOL\n". So POOL
admits exactly rand_algo ∈ {0=LFSR, 3=XORWOW}; {1=PCG32, 2=PHILOX} fall to the
"not currently supported on POOL" arm — exactly the ISA enum's POOL subset.
[HIGH/OBSERVED — disasm + both error strings read directly from NX DRAM]
CORRECTION (vs an earlier draft). The error strings read "not currently supported on POOL" (the word
currentlyis present in both0xe89and0xe41). Any prose droppingcurrentlyis wrong; the bytes are decisive.
4.2 The Q7 dispatch + the algo-select bit
RandSetState enters at 0xb5f4, logs the entry, decodes the operand, and tail-calls the
dispatcher 0xb640:
b5f4: entry a1, 48
b617: l32i.n a12, a1, 12 ; num_active_channels
b619: l32i.n a13, a1, 8 ; rand_algo (operand-local copy)
b61e: const16 a10, 0x1e80 ; log @DRAM 0x1e80
b621: call8 0x18d0c
b62b: call8 0x7fd0 ; operand decode
b631: call8 0x44f0 ; setup
b634: l32i.n a11, a1, 8 ; rand_algo -> arg
b636: call8 0xb640 ; -> dispatcher
DRAM 0x1e80 (byte-exact):
"P%i: RandSetState : num_chans = %0d : rand_algo = 0x%x\n".
The dispatcher 0xb640 computes the algorithm-select bit with one saltu and stores it
at [a1+36]:
b640: entry a1, 80
b646: s32i.n a3, a1, 24 ; [a1+24] = rand_algo
b64c: l32i a0, a0, 0
b64f: extui a4, a0, 0, 2 ; 2-bit config read
b659: extui a0, a0, 1, 2
b676: l32i.n a2, a1, 24 ; a2 = rand_algo
b678: movi.n a3, 1
b67a: saltu a2, a2, a3 ; a2 = (rand_algo < 1) = (rand_algo == 0)
b67d: s8i a2, a1, 36 ; ALGO-SELECT bit = (rand_algo == 0)
b680: l8ui a2, a1, 36
b683: bbci a2, 0, 0xb6c0 ; algo-select==0 -> skip the ==0 extra-setup block
; (algo-select==1, i.e. rand_algo==0/LFSR -> b68d..b6b0 extra state writes)
b6c0: l8ui a2, a1, 40
b6c3: bbci a2, 0, 0xb6d8 ; ENABLE gate: skip seeding if [a1+40] bit0 clear
b6c9: l8ui a2, a1, 36 ; reload algo-select bit
b6ce: extui a10, a2, 0, 1 ; arg = algo-select bit (0/1)
b6d1: call8 0xb6dc ; -> SHARED SetSeeds, passing the select bit
GOTCHA —
0xb683is not a second algorithm fork. It branches on the same algo-select bit[a1+36], but both arms converge at the enable gate0xb6c0; the==0(LFSR) arm merely performs a few extra state-field writes before re-joining. The only function-level fork is at the sharedSetSeeds 0xb6dc. Treat0xb683as conditional in-line setup, not a dispatch.[HIGH/OBSERVED — both arms re-join at 0xb6c0]
4.3 The fork instruction — shared SetSeeds 0xb6dc
This is the decisive instruction. The shared body stores the passed select bit, reloads it,
and forks with a single bbci:
b6dc: entry a1, 48
b6df: s8i a2, a1, 12 ; store the algo-select bit
b6e2: l8ui a2, a1, 12
b6e5: bbci a2, 0, 0xb6f5 ; *** THE FORK *** bytes: 07 62 0c
; bit SET (1) -> fall through -> b6e8 j 0xb6ed -> call8 0xb700 (LfsrSetSeeds, frame 192)
; bit CLEAR (0) -> branch 0xb6f5 -> call8 0xb744 (XorwowSetSeeds(TIE), frame 0x400)
b700: entry a1, 192 ; LfsrSetSeeds (SMALL frame)
b716: const16 a10, 0x1eb8 ; log "P%i: LfsrSetSeeds\n"
b719: call8 0x18d0c
b744: entry a1, 0x400 ; XorwowSetSeeds(TIE) (LARGE frame)
b75a: const16 a10, 0x1ecb ; log "P%i: XorwowSetSeeds(TIE)\n"
DRAM 0x1eb8 = "P%i: LfsrSetSeeds\n"; DRAM 0x1ecb = "P%i: XorwowSetSeeds(TIE)\n" — both read
directly. [HIGH/OBSERVED — fork bytes 07 62 0c at 0xb6e5; both body entries + both log strings.]
RandGetState's shared GetSeeds 0xb9ec mirrors it exactly — same bbci a2, 0, 0xba05
(bytes 07 62 0c), LFSR arm log 0x1f1d = "P%i: LfsrGetSeeds", Xorwow arm log
0x1f30 = "P%i: XorwowGetSeeds(TIE)". [HIGH/OBSERVED]
4.4 The net polarity
Composing §4.2 and §4.3 resolves the bit polarity end-to-end:
algo_select_bit = saltu(rand_algo, 1) = (rand_algo == 0)
rand_algo == 0 (LFSR) -> bit = 1 -> SET -> fall through -> LfsrSetSeeds 0xb700
rand_algo == 3 (XORWOW) -> bit = 0 -> CLEAR -> branch -> XorwowSetSeeds 0xb744
This matches the ISA enum exactly (LFSR=0, XORWOW=3) and is cross-validated three ways:
the saltu chain, the SEQ {0,3} gate (§4.1), and the host enum (§2). [HIGH/OBSERVED for the bbci fork + the two body entries; the integer→algo mapping HIGH by triple cross-validation.]
4.5 The generator dispatcher does not fork on rand_algo
The per-draw Rng generator dispatcher 0xbf74 logs "P%i: Rng : num_chans = %0d"
(DRAM 0x1f4a), decodes, sets up, and unconditionally tail-calls 0xbed8 — no algo
branch:
bf74: entry a1, 48
bf90: const16 a10, 0x1f4a ; "P%i: Rng : num_chans = %0d"
bf9c: call8 0x7fd0 ; decode
bfa2: call8 0x44f0 ; setup
bfa5: call8 0xbed8 ; -> post-proc + driver (NO rand_algo read)
0xbed8 is a dtype / post-process switch (beqi a2, 10; bnei a2, 6) that builds the
0x3F80 (= 0x3F800000 = 1.0f high half) and 127/-1 mantissa-fill constants at
0xbf2d…0xbf3f, then unconditionally call8 0xbc78 at 0xbf69 — again no algo branch.
The driver 0xbc78 (frame 0x100) self-names "P%i: XorwowRng(TIE)" (DRAM 0x1f66, log
@0xbd57) and carries the Xorwow Weyl constant:
be08: movi.n a2, 5
be0a: const16 a2, 0x87c5 ; const16-pair builds 0x587c5 = 362437 (Weyl increment)
CORRECTION — the Weyl constant is
0x587c5 = 362437, not0x87c5. Xtensaconst16assembles a 32-bit immediate from a pair ofconst16ops (high half via the precedingmovi.n a2, 5, then the low halfconst16 a2, 0x87c5), yielding(5 << 16) | 0x87c5 = 0x587c5 = 362437— the classic XORWOW Weyl-sequence additive constant (the same362437seen in the software-Xorwow path). The bare low half0x87c5alone is34757and is meaningless in isolation.[HIGH/OBSERVED — arithmetic identity on the const16 pair at 0xbe08/0xbe0a.]
So the seed init is the only thing that forks; the advance is shared. Whether the LFSR arm
applies the Weyl step or the shared recurrence degenerates to a pure single-word shift under
the LFSR's differently-initialized state is not byte-decidable — there is no clean
algo-conditional at the driver entry; any per-algo behaviour is either state-driven or buried
in a FLIX-desync'd bundle. [HIGH that there is no clean algo fork in the driver; MED/INFERRED that the driver is fully algo-agnostic.]
5. The LFSR state, seed, and recurrence
5.1 The state — one u32 word per lane
d4_rand.h states the LFSR state model verbatim:
"LFSR treats each of the 128 lanes as an independent random number generator. The lowest u32 word of the rand_state (rand_state[0]) for each lane is used as the initial state. The LFSR algorithm is applied one or more times (number of steps parameter) and the resulting u32 is issued as the random number for that lane. (On Neuron this takes about 30 cycles to generate one element for each lane.)"
So the LFSR is 128 independent single-register generators, each seeded from rand_state[0].
Contrast the other algorithms (also from d4_rand.h): PCG32 uses two words
(rand_state[0..1], ~110 cycles), Xorwow uses 5–6 words (x,y,z,w,v
- Weyl
d), and Philox is engine-wide (a single 16 B counter + 8 B key + 4 B offset stream distributed across all lanes — not per-lane). TheS1_RANDoperand carriesimm_state[8](eightu32, the maximum state envelope); for LFSR onlyimm_state[0]is the seed (the rest constrained to0for the LFSR pairings per thehas_valid_*blocks). Thepseudo_set_rng_seedoperand likewise carries a single 4-byteseed(offset 16), consistent with the one-word LFSR seed.[HIGH/OBSERVED — header verbatim + struct offsets]
5.2 The structural smoking gun — LfsrSetSeeds vs XorwowSetSeeds
The two seed-set bodies, decoded side by side, confirm the 1-word-vs-6-word asymmetry:
LfsrSetSeeds 0xb700 | XorwowSetSeeds(TIE) 0xb744 | |
|---|---|---|
entry frame | 192 (small) | 0x400 (large) |
memset of state | none | present (the 0x180-stride zero) |
| seed-word writes | one FLIX bundle (desync'd) | 6-fold unrolled (b769/b779/b789/…) |
| Weyl const | none | 0x587c5 chain |
| big vector helpers | none | call8 0x14a70/0x15538/0x1d440/0x25348/… |
| body size | ~0x44 B | large |
GetSeeds counterpart | single call8 0xcca8 | entry a1, 0x580 |
The frame-size and body-size gap directly mirrors the 1-word LFSR state vs the 6-word
Xorwow state. [HIGH/OBSERVED — both entryframes byte-read; body structure observed past the FLIX desync via explicit--start-address.]
5.3 rand_num_steps — the LFSR-only multi-step advance
d4_rand.h: rand_num_steps (offset 13) is the number of times HW updates the RNG state
before emitting one output u32. "Only used for LFSR to mitigate back-to-back random number
correlations. If LFSR, rand_num_steps >= 1. If PCG32 or PHILOX, rand_num_steps == 1." The ISA
constraint is explicit:
fn has_valid_rand_num_steps(i: Inst) -> bool {
(i.d4_rand.rand_algorithm == RandAlgorithm::LFSR)
|| (i.d4_rand.rand_num_steps == 1)
}
So the LFSR is iterated rand_num_steps times per draw; the other algorithms exactly once.
The Q7 multi-step loop is not cleanly observable (it lives in the FLIX-desync'd inner core):
the step-count semantics are [HIGH/OBSERVED header + constraint]; the exact Q7 loop
construct is [MED].
5.4 The recurrence / polynomial — what is and is not recoverable
- Width = 32 bits (one
u32word per lane; output is oneu32).[HIGH/OBSERVED] - Galois vs Fibonacci / exact taps / feedback polynomial: UNRECOVERED. An exhaustive
little-endian scan of the MARIANA Q7 POOL IRAM and DRAM for the eight classical 32-bit
LFSR tap masks
{0x80000057, 0x800000C2, 0xA3000000, 0x80200003, 0x04C11DB7, 0xEDB88320, 0xD0000001, 0x8000000B}returns zero little-endian hits — there is no embedded feedback-mask constant.[HIGH/OBSERVED negative]Thed4_rand.hheader names the algorithm "LFSR" but, unlike PCG32/Philox, does not state its polynomial or tap layout. The state-advance lives in a FLIX-desync'd bundle (the flat DEBUG Q7 IRAM has no.xt.propproperty table — the corpus-wide FLIX-desync ceiling), so the per-step shift/xor micro-ops are not byte-pinnable. - Characterization. The "LFSR" is therefore a 32-bit single-word xorshift-style
linear-feedback generator (a shift+xor recurrence over GF(2) on the full word), composed
from the generic integer vector ALU primitives (the xor/sll/srl grid driven through the
shared TensorTensorArith engine) — not a tap-bit Galois/Fibonacci LFSR with an embedded
feedback-mask constant, and not a dedicated LFSR opcode (the GPSIMD opcode roster carries
no
rng/lfsrmnemonic; the advance is the shared driver0xbc78). The 32-bit-width + xorshift-family + GF(2)-linear + no-dedicated-opcode characterization is[HIGH]; the exact taps and Galois-vs-Fibonacci direction are[LOW/UNRECOVERED]and honestly flagged.
This is a named wall —
closable-with-corpus(a FLIX-aware disassembler config) orclosable-with-hardware(a device run that exposes the recurrence). Do not fabricate a tap polynomial. A reimplementer who needs bit-identical output must recover the recurrence by a captured device round-trip; the interface (1-wordu32per lane, seeded fromrand_state[0], iteratedrand_num_steps>=1, outputu32) is fully specified above.
6. Per-generation presence — closing the subsystem
Two independent evidence streams agree on which generation carries what: (A) the host ISA enum per arch header, and (B) the per-generation Q7 POOL RNG log strings.
| GEN | ISA enum has XORWOW? | Q7 POOL RNG strings present | wired POOL algos |
|---|---|---|---|
| SUNDA (v2) | NO ({LFSR,PCG32,PHILOX}; no XORWOW) | none — no Lfsr/Xorwow/Rng/RandSet/RandGet strings in the SUNDA Q7 POOL image | none (RNG not implemented on POOL) |
| CAYMAN (v3) | YES (XORWOW=3 added) | Xorwow(SW) Init/Set/Get/Rng only; no Lfsr, no (TIE) | XORWOW only (software; see rng-xorwow-sw) |
| MARIANA (v4) | YES | Xorwow*(TIE) + LfsrSetSeeds/LfsrGetSeeds (decoded here) | LFSR + XORWOW (TIE) |
| MARIANA_PLUS (v4+) | YES | same (TIE) + Lfsr set; fork bytes byte-match MARIANA | LFSR + XORWOW (TIE) |
| MAVERICK (v5) | YES | same (TIE) + Lfsr set (string-confirmed; IRAM body nm-aliased onto DRAM, not separately carved) | LFSR + XORWOW (header-OBSERVED → interior INFERRED) |
[HIGH/OBSERVED — enum per-arch header grep + per-gen string sweep + MARIANA_PLUS fork-address byte-match.]
The proofs:
- SUNDA negative. The SUNDA Q7 POOL image (only a RELEASE build ships; no DEBUG) contains
the
…/sunda/pool/src/…source-path strings (proving the carve hit SUNDA) but zeroLfsr/Xorwow/Rng/RandSetState/RandGetStatestrings. SUNDA defined theLFSRenum value (=0) before POOL implemented any RNG handler.[HIGH/OBSERVED] - CAYMAN is Xorwow(SW)-only. Its Q7 POOL DRAM carries
XorwowSetSeeds(SW),XorwowRng(SW),XorwowGetSeeds(SW),Xorwow(SW) : Initializing XORWOW state…— and noLfsr*strings and no(TIE)suffix. CAYMAN added theXORWOW=3enum value and shipped the software Xorwow only.[HIGH/OBSERVED] - MARIANA / MARIANA_PLUS / MAVERICK carry LFSR + Xorwow(TIE). All three Q7 POOL DRAMs
carry
LfsrSetSeeds,LfsrGetSeeds, and theXorwow*(TIE)family. The MARIANA_PLUS fork is byte-identical to MARIANA: bytes07 62 39@0xb683,07 62 11@0xb6c3,07 62 0c@0xb6e5.[HIGH/OBSERVED byte-match]
NOTE — LFSR was added at MARIANA, not CAYMAN. The progression is: SUNDA = no POOL RNG; CAYMAN =
XORWOW(SW)only (the enum value's debut); MARIANA+ = the(TIE)-build Xorwow + the LFSR. MAVERICK adds no new algorithm (still{LFSR, XORWOW}on POOL; PCG32/PHILOX remain ISA-defined-but-"not currently supported on POOL").[HIGH/OBSERVED]
MAVERICK (v5) interior — header-OBSERVED only → INFERRED. Per the generation-grounding policy, the v5 LFSR+Xorwow presence is OBSERVED (its Q7 POOL DRAM carries the
Lfsr*/Xorwow*(TIE)strings), but the v5 IRAM image is nm-aliased onto its DRAM symbol and not separately carvable, so the fork-body bytes are INFERRED identical-family from MARIANA/MARIANA_PLUS, not re-read. Do not cite a Maverick fork address as byte-observed.[presence HIGH/OBSERVED; body MED/INFERRED]
7. The uint32 output contract — reconciled with the consumers
- Raw product. Both LFSR and Xorwow emit a
uint32per lane (LFSR:rand_state[0]advancedrand_num_stepstimes →u32; Xorwow:v + d→u32). TheRAW_U32/uint32ABI is generation-invariant and algorithm-invariant — onlypost_processoptionally floats it.[HIGH/OBSERVED — header + the shared driver] - The
post_processfield is the contract (§2):RAW_U32(0)→ the rawuint32(what Dropout / Rand2 consume).UNIFORM_IN_RANGE(1)→ fp32 uniform in(min_fp32=params[0], max_fp32=params[1]); this is the0x3F800000(1.0f) +0x7FFFFFmantissa-fill float seam built at0xbf2d…0xbf3finside0xbed8.[the0x3F80/127/-1constants HIGH; the exact mantissa-fill cast MED through the FLIX desync — same condition as the Xorwow pages.]NORMAL(2)→ "Not supported yet" (d4_rand.h: "Normal is not supported by HW yet").
- Dropout reconciliation.
s3d3_dropout.hstates the Dropout op "generates a u32 LFSR for each lane for each element; converts it to an f32 in range (0.0 to 1.0); compares to threshold (ptr or immediate)" — withif threshold_type == DropRate: if (rand > threshold) output 0.0andif threshold_type == KeepRate: if (rand < threshold) output 0.0. So the DVE Dropout's inline random source is the LFSR (RAW_U32produced, then castuint32→fp32, then compared). This pins the Dropout consumer to the LFSR arm.[HIGH/OBSERVED header verbatim]
8. The RNG subsystem — the five-way closing map
This page closes the GPSIMD RNG subsystem. The five pieces and their relationship:
| piece | what it is | algo | per-draw advance | output | page |
|---|---|---|---|---|---|
| Xorwow-SW | software XORWOW on the POOL compute engine (CAYMAN debut) | XORWOW | XorwowRng(SW) | u32 | rng-xorwow-sw |
| Xorwow-TIE | the (TIE)-build XORWOW (MARIANA+) | XORWOW | XorwowRng(TIE) driver 0xbc78, Weyl 0x587c5 | u32 | rng-xorwow-tie |
| LFSR | second algo (MARIANA+); 1-word/lane, rand_num_steps≥1 | LFSR | shares driver 0xbc78; only the seed init 0xb700 differs | u32 | this page |
| Dropout | DVE consumer; inline LFSR → f32(0,1) → threshold compare | LFSR (inline) | inline per element | mask / passthrough | dropout |
| Rand2 | user random-tensor op (D3_RAND_STRUCT, opcodes RAND2/RNG) | {LFSR, XORWOW} via same enum | (FW-48 scope) | u32 / fp32 | rand2 |
The unifying structure:
- One enum, one fork.
rand_algorithm(offset 12) selects the recurrence; the device reduces the four-value ISA enum to{LFSR(0), XORWOW(3)}viasaltu(rand_algo, 1)and forks with a singlebbciin the sharedSet/GetSeedsbody. PCG32/PHILOX are ISA-defined but POOL-unsupported. - One advance, two seeds. There is no separate
LfsrRngand no LFSR opcode; LFSR and Xorwow share the per-draw driver and differ only in seed initialization (LfsrSetSeeds 0xb700frame 192 vsXorwowSetSeeds 0xb744frame0x400). - One output ABI. Every generator emits a
uint32per lane (RAND_POST_PROC::RAW_U32), optionally cast to fp32(min, max)(UNIFORM_IN_RANGE). Dropout consumes the rawuint32from the LFSR arm; the producer decode (Rng + RandGet/SetState on POOL) is complete, with the Rand2 device routing flagged as the one remaining boundary (Rand2 /d3_rand). - Seed/state plumbing (the
RandSetState/RandGetStateoperand transport, thepseudo_set_rng_seedsingle-word seed) is documented at RNG Seed-State Opcodes.
9. Honesty ledger
HIGH / OBSERVED — direct disasm, byte read, symtab/header read, or arithmetic identity:
- The
rand_algoenum{LFSR=0, PCG32=1, PHILOX=2, XORWOW=3}from the shipped ISA headers, byte-identical across cayman/mariana/maverick; SUNDA truncated (noXORWOW). - The
RAND_POST_PROC {RAW_U32=0, UNIFORM_IN_RANGE=1, NORMAL=2}andRAND_SRC {RNG_LFSR=0, RNG_XORWOW=1, RNG_PHILOX=2, OUTPUT_CVT_LFSR=3}enums. - The
D4_RAND/S1_RAND/D3_RANDfield offsets (rand_algorithm@12etc.) and their match to the Q7 dispatcher field reads; thestruct2opcodeJSON binding. - The SEQ
{0,3}validation gate (saltu/addi -3/bbsi@0x301b); the two error strings (0xe89/0xe41, "not currently supported on POOL"). - The Q7 fork:
RandSetState 0xb5f4 → dispatcher 0xb640(saltu rand_algo,1) → sharedSetSeeds 0xb6dcbbci a2,0,0xb6f5(bytes07 62 0c) →0xb700(Lfsr, frame 192) vs0xb744(Xorwow, frame0x400); the symmetricGetSeeds 0xb9ecfork; every log xref (0x1eb8/0x1ecb/0x1f1d/0x1f30) read directly. - The net polarity
rand_algo==0→LFSR, ==3→XORWOW, triple-cross-validated. - The
Rngdispatcher0xbf74+0xbed8do not fork onrand_algo; unconditionalcall8 0xbc78(sharedXorwowRng(TIE)driver); the Weyl0x587c5 = 362437const16 pair. - The LFSR = 1
u32word/lane (header verbatim) + the structural confirmation (frame 192 / no memset / no seed chain / no Weyl / single bundle). rand_num_steps= LFSR-only multi-step advance (header + ISA constraint).- No LFSR tap-mask constant anywhere in Q7 POOL IRAM/DRAM (negative).
- Dropout's inline RNG = LFSR (
s3d3_dropout.hverbatim). - The per-gen matrix (SUNDA none / CAYMAN XORWOW-SW only / MARIANA+ LFSR+XORWOW-TIE), enum + string streams agreeing; MARIANA_PLUS fork-address byte-match.
MED / INFERRED:
- The exact Q7 realization of the LFSR per-draw step (the
rand_num_stepsloop; whether the shared driver applies the Weyl step on the LFSR arm or behaves as a pure single-word shift) — no clean algo-conditional at the driver entry; either state-driven or FLIX-desync'd. - The
uint32→fp32UNIFORM_IN_RANGEmantissa-fill cast (the0x3F80/127/-1constants are HIGH; the exact cast steps MED through the desync). - The
0xb6c3"enable gate" precise field ([a1+40]bit0 —num_chans>0vsupdate_state) is inferred, not byte-pinned.
LOW / UNRECOVERED:
- The LFSR feedback polynomial / exact tap positions / Galois-vs-Fibonacci form (no embedded mask constant; FLIX-desync'd body; header silent on the poly). 32-bit width + xorshift-family + GF(2)-linear + no-dedicated-opcode is solid; the precise recurrence is not recovered. Named wall.
- The MAVERICK Q7 POOL IRAM body (nm-aliased onto DRAM; not separately carved; presence HIGH by string, body INFERRED identical-family).
- The Rand2 (
d3_rand) device operand path — struct-level reuse of this enum is OBSERVED; the device routing is Rand2's scope, flagged.
Cross-references
- RNG — Xorwow Software Path — the CAYMAN software XORWOW sibling
(the
(SW)strings; the362437Weyl debut). - RNG — Xorwow TIE Hardware Path — the MARIANA+
(TIE)XORWOW; the shared driver0xbc78this page's LFSR reuses for its advance. - RNG Seed-State Opcodes — the
RandSetState/RandGetStateoperand transport and thepseudo_set_rng_seedsingle-word seed. - Rand2 (user random-tensor op) — the
D3_RANDop that reuses thisrand_algoenum; the one remaining device-routing boundary. - Dropout — the DVE consumer whose inline RNG is the LFSR (
RAW_U32→uint32→fp32→ threshold compare). - The Confidence & Walls Model — the HIGH/MED/LOW × OBSERVED/INFERRED tagging, the FLIX-desync ceiling, and the v5/Maverick header-OBSERVED-only policy this page applies.