Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

cas/fiss Load/Store (LSU) Semantics

This page documents the vector & scalar load/store family — the LSU — as the cycle-accurate ISS models it across its two cooperating plugins. It is one node of Part 14, the ISS as executable oracle: the place you go when the ISA reference tells you a lsnx16.i mnemonic exists and you need to know, to the bit, what address it forms, what bytes it touches, how it extracts an element, and what the base register holds afterward — answers the ISS computes mechanically and a reimplementation must match.

The LSU is where the universal cas/fiss split is at its sharpest. The cycle-accurate timing core in libcas-core.so (45,878,080 B, ELF64 x86-64, not stripped) models the Cayman / Vision-Q7 IVP VLIW pipeline: it does decode + timing — it resolves which architectural registers an op reads and writes, posts each at its latency, and threads the op through the LSU's structural hazards — but it computes no datum. The value math lives in libfiss-base.so (12,330,016 B, self-contained): 864 module__xdref_* value functions, reached from cas through the 119 nx_*_interface TIE-port callbacks. For a load, the "value" is the loaded vector itself, produced by the fiss memload stage (the host memory read) and the wideldshift element-extract funnel; for a store, it is the inverse widestshift funnel that splices a lane back into a 64-byte line. This page traces exactly where cas hands the memory access off to the host model and where fiss provides the funnel transform.

The surface-level anatomy of that split — the plugin ABI, the _issue/_stall naming, the 119 ports — is libcas-core Surface + ISS Plugin ABI. The ISA-reference roster and opcodes this page realizes are B06 — Loads and B07 — Stores. The microarchitectural LSU model (pipes, ports, regions) is LSU + Memory Model, and the formal load/store semantics are Group I Semantics. The unaligned wrapper built on top of this aligned primitive — VALIGN — is in the shuffle/valign page cas valign / shuffle / reduce; the indexed (per-lane scatter/gather) counterpart is cas SuperGather.

Confidence tags. [OBSERVED] byte-exact from a shipped symbol / address / instruction read this pass; [INFERRED] reasoned over observed control/data flow; [CARRIED] carried from a cited sibling page, re-grounded here. HIGH/MED/LOW = confidence. Every cas/fiss symbol and address below is from nm/objdump -d of the two shipped .so files by absolute path. .text/.rodata have VMA == file offset (confirmed readelf -SW: .text @ 0x572fa0); .data carries a 0x200000 delta (.data VMA 0x2280ed8, file offset 0x2080ed8) — irrelevant here, every routine cited is in .text.

GUARD. The modeled core is Cayman / Vision-Q7 (CoreV3-class IVP). The LSU slots, latencies and ports below are that one ISS configuration; cross-gen deltas are not in scope. "GPSIMD" = the POOL-engine Vision-Q7 cores. Provenance is pure static RE of the two shipped binaries (lawful interoperability, DMCA 1201(f)); no external source tree is consulted or referenced.


1. The family — what counts as a load/store, and where it issues

1.1 Two naming tiers, one operand grammar [HIGH/OBSERVED]

The plain (non-indexed) memory-access family splits into two IVP / Tensilica naming tiers, recovered by an nm sweep of libcas-core.so (35 load/store roots):

tierroots (each × {_I,_IP,_X,_XP})nature
LV / SVLV2NX8, LVNX8S, LVNX8U, LVN_2X16S, LVN_2X16U (+ SV*, + _T pred. stores)variable / element-addressed vector ld/st (8-bit roots)
LS / SSLSNX16, LSNX8S, LS2NX8, LSN_2X16S, LSN_2X32, LSN_4X64, LSN_8X128, LSN_16X256aligned-stride vector ld/st (16-bit + wide patterned)
LSRLSR2NX8, LSRNX16, LSRN_2X32, LSRN_4X64stream-rotate aligned load (rotating-buffer streaming)

GOTCHA. The 16-bit plain vector load is LSNX16 / SSNX16 — it lives in the aligned LS/SS tier. There is no LVNX16 / SVNX16; the 16-bit width is not a variable-element form. Reach for LS* when you want the contiguous 16-bit vector load, LV* only for the 8-bit / n_2x16 element forms. [HIGH/OBSERVED — roster sweep]

Two prefixed families look like loads but are not plain data loads: LB* (bit-load) and the compare-loads LE*/LT*/LTU*/LTR* compute a vbool boundary predicate from a vector (the gather vbMask producers ivp_ltun/leun_2x32, consumed by SuperGather) — they carry an L prefix but produce a predicate, not a memory datum. [HIGH/OBSERVED roster; MED for the semantic binding via the gather page]

1.2 Slot placement — S0_LdSt and S1_Ld, every FLIX format [HIGH/OBSERVED]

Every load issues in the two LSU slots of every wide FLIX format F0..F7,F11 and the narrow N0/N1/N2. The LSNX16_I issue symbol is present in (sampled from nm):

F0_S1_Ld   F0_S0_LdSt   F1_S0_LdStALU   F1_S1_Ld   F2_S1_Ld
F3_S0_LdSt F3_S1_Ld     F4_S0_Ld        F4_S1_Ld   F6_S0_LdSt
F6_S1_Ld   F7_S0_LdSt   F7_S1_Ld        N2_S0_LdSt N2_S1_Ld

So the LSU exposes two slots: S0_LdSt = address-generation + the store path, S1_Ld = the load drain (F1 folds them into the combined S0_LdStALU). A bundle can co-issue one S0 op and one S1 op — two memory ops per cycle, modeled by the two host pipes nx_Load_0 / nx_Load_1. These are exactly the two slots valign uses for lalign(S0+S1)/salign(S0) and SuperGather uses for gather(S0)/drain(S1). The plain aligned load is the LSU primitive those two build on. [HIGH/OBSERVED]

1.3 The scalar base-ISA load/store [HIGH/OBSERVED]

Distinct from the vector LSU is the Xtensa scalar core ld/st, present in both binaries: L32I/L32I_N/L32R/L32AI/L32E (32-bit word), L16UI/L16SI/L16U/L16S (16-bit zero/sign-extending), L8UI/L8 (8-bit); S32I/S32I_N/S32RI/S32E/S32NB/S32STK, S16I, S8I. These have hard natural alignment (§3.4) — a different rule from the element-tolerant vector LSU.


2. The four addressing modes — _i / _x / _ip / _xp (cas DECODE)

The universal {_i,_x,_ip,_xp} suffix grid is decoded byte-exact from the four F0_F0_S1_Ld_16_inst_IVP_LSNX16_*_issue routines. Their addresses are the canonical anchors:

suffixsymboladdrmeaning
_i..._LSNX16_I_issue0x71ddc0addr = base_AR + IMM (IMM is an instruction-word field)
_x..._LSNX16_X_issue0x71deb0addr = base_AR + index_AR
_ip..._LSNX16_IP_issue0x71de20addr = base_AR; then base_AR := base_AR + IMM
_xp..._LSNX16_XP_issue0x71df40addr = base_AR; then base_AR := base_AR + index_AR

The operand fields are identical across the grid — what changes is how many AR operands are decoded and whether the base AR is defined a second time.

2.1 _i immediate-offset (0x71ddc0) [HIGH/OBSERVED]

The issue routine decodes exactly two operands:

// F0_F0_S1_Ld_16_inst_IVP_LSNX16_I_issue @ 0x71ddc0
// rsi -> instruction word; rdi -> ISS op context
uint32_t iw = *(uint32_t*)instr_word;          // mov (%rsi),%esi

// OPERAND 1 — base address register: low 4 bits of the word
opnd_sem_AR_addr(ctx, iw & 0xf);               // and $0xf,%esi ; call AR_addr@plt
ctx->AR_def_hook(/*esi=*/ 1);                  // hook 0x14eb0, LAT = 1  (address ready immediately)

// OPERAND 2 — destination vector: bits [8:4] (a 5-bit vec field)
uint32_t vec = (iw << 0x13) >> 0x1b;           // shl $0x13 ; shr $0x1b  => bits[8:4]
opnd_sem_vec_addr(ctx, vec);                   // call vec_addr@plt
ctx->vec_host_hook(/*esi=*/ 0xa);              // hook 0x15200, LAT = 10 (vector load-use)

The immediate offset is not a register operand — it is a field in the instruction word, consumed later in the fiss execute stage (§5). cas posts the base AR at LAT 1 and the destination vector at LAT 10. [HIGH/OBSERVED]

2.2 _x register-indexed (0x71deb0) [HIGH/OBSERVED]

_x decodes a third operand — a second AR holding the index — between the base and the vector:

// ..._LSNX16_X_issue @ 0x71deb0
opnd_sem_AR_addr(ctx, iw & 0xf);                       // OPERAND 1: base AR, LAT 1
ctx->AR_def_hook(1);

uint32_t index = (eax << 0x18) >> 0x1c;                // OPERAND 2: index AR = bits[11:8] (4-bit)
opnd_sem_AR_addr(ctx, index);                          //   shl $0x18 ; shr $0x1c
ctx->AR_def_hook(1);                                   //   LAT 1

uint32_t vec = (iw << 0x13) >> 0x1b;                   // OPERAND 3: dst vec = bits[8:4]
opnd_sem_vec_addr(ctx, vec);
ctx->vec_host_hook(0xa);                               // LAT 10

addr = base + index_AR. The index is a full architectural AR, read at LAT 1. [HIGH/OBSERVED]

2.3 _ip immediate post-increment (0x71de20) — the post-update rule [HIGH/OBSERVED]

_ip is the keystone of the post-increment family. The cas issue decodes the base AR twice — once as the read base, once as the write-back def of the same register number:

// ..._LSNX16_IP_issue @ 0x71de20
opnd_sem_AR_addr(ctx, iw & 0xf);     // OPERAND 1: base AR (the READ base)   -- and $0xf
ctx->AR_def_hook(1);                 //   LAT 1

opnd_sem_AR_addr(ctx, iw & 0xf);     // OPERAND 2: SAME AR (the post-UPDATE) -- and $0xf  (again!)
ctx->AR_def_hook(1);                 //   LAT 1 -- the base-register auto-update def

uint32_t vec = (iw << 0x13) >> 0x1b; // OPERAND 3: dst vec = bits[8:4]
opnd_sem_vec_addr(ctx, vec);
ctx->vec_host_hook(0xa);             // LAT 10

The decisive evidence is the doubled and $0xf: _i masks the base register number once (one AR operand); _ip masks the identical field a second time and posts it as a fresh AR def. That second AR def is the post-increment write-back. The address used by the access is the current base; the base register is then re-defined to the incremented value. [HIGH/OBSERVED]

QUIRK. Post-increment costs an extra architectural register write but updates the same register it read — so a chain of _ip loads walking a buffer never frees the base AR for another use; the scoreboard sees a fresh def of that AR at LAT 1 every iteration. A reimplementation that models the base as read-only on _ip will diverge: the ISS posts a second AR_def.

2.4 _xp register post-increment (0x71df40) [HIGH/OBSERVED]

_xp is _ip's shape with the increment sourced from the index register instead of an immediate: base AR (read) + index AR + base-AR write-back (base := base + index)

  • dst vec. The per-format encoding template that repacks the _x → _xp delta — a fixed positional bit-shift per FLIX format — and the roster-wide post-update marker (opcodes[].flags bit 11, set on all ivp_*_ip/_xp plus lddr32.p/sddr32.p) are carried from the ISA-reference encoding page. [HIGH for the 2-AR cas decode OBSERVED here; CARRIED for the flags/template]

2.5 LV (variable) vs LS (aligned) — identical cas decode [HIGH/OBSERVED]

N1_N1_S0_LdSt_4_inst_IVP_LV2NX8_I_issue decodes base AR (& 0xf, LAT 1) + dst vec (bits[8:4], LAT 10) — byte-for-byte the same operand grammar as LSNX16_I. LV and LS differ only in the fiss value (element width and alignment-fault behavior, §3–4), never in the cas operand decode or timing. [HIGH/OBSERVED]

2.6 The store decode — one shared LSU semantic [HIGH/OBSERVED]

Stores carry no separate _issue symbol. The admit/decode lives in a thin per-mnemonic wrapper <fmt>_<slot>_IVP_<m>_inst_stage1 that sets a per-mnemonic select bit, calls the one shared LSU decode for that (format, slot), then clears the bit:

F0_F0_S0_LdSt_4_IVP_SSNX16_I_inst_stage1 @ 0x119db40:
  orb  $0x10,0x6f4(%rdi)                                    ; set the per-mnemonic select bit
  call F0_F0_S0_LdSt_4_ivp_sem_ld_st_semantic_stage1        ; @ 0x1198ff0 (the ONE shared decode)
  andb $0xef,0x6f4(%rbx)                                    ; clear the select bit
  ...                                                        ; (repeated as a select-bit ladder)

ivp_sem_ld_st_semantic_stage1 (@0x1198ff0 for F0_S0_LdSt) is a large flag-bit dispatcher: a ladder of testb/testl over the per-mnemonic select region (0x6dc..0xbab of the op context) choosing which load/store variant decode runs. The execute handoff is the shared ivp_sem_ld_st_semantic_stage10.isra (@0x11b54a0) → the host VALUE callback (vec hook 0x15200) + the per-instance port pointers. [HIGH/OBSERVED for the wrapper + shared-semantic structure; the full per-mnemonic flag map is a select-bit ladder, not enumerated field-by-field this pass — LOW for the exhaustive map]


3. Alignment — the natural-element model (why VALIGN exists)

3.1 The vector alignment mask scales with element width [HIGH/OBSERVED]

Each fiss memload__ivp_<m>_i fetches a 64-byte line at addr & MASK_W, where the mask clears the in-line bits below the element granularity. Read byte-exact from the and $0xffffff??,%esi immediately before the host read (all reads mov $0x40,%edx):

widthelementmemload maskbits clearedextract funnel
nx8 (8b)64 × 8-bit0xffffffdfbit 5wideldshift_256_512_6
nx16 (16b)32 × 16-bit0xffffffc1bits 1..5wideldshift_16_512_6
2x32 (32b)16 × 32-bit0xffffffc3bits 2..5wideldshift_32_512_6
4x64 (64b)8 × 64-bit0xffffffc7bits 3..5wideldshift_64_512_6
8x128 (128b)4 × 128-bit0xffffffcfbits 4..5wideldshift_128_512_6
16x256 (256b)2 × 256-bit0xffffffdfbit 5wideldshift_256_512_6

This was verified across all six memload__ivp_lsn_*_i / lvnx8u_i bodies. The mask forces a natural-element-aligned 64-byte line read: a vector load whose address is element-aligned (e.g. a 16-bit load at any even byte) reads cleanly without a fault, and the residual low bits feed the element-extract funnel. [HIGH/OBSERVED — byte-exact, all six widths]

NOTE. The mask "scales" because the funnel only needs to extract one element from within the fetched line; the line is always 64 bytes regardless of width. The wider the element, the fewer low address bits the funnel needs, so the mask clears fewer of them. The nx8/16x256 masks coincide (0xdf, bit 5) — both use the _256 funnel, which is why the table has two 0xdf rows.

3.2 The within-line extract — the funnel shift [HIGH/OBSERVED]

opcode__ivp_lsnx16_i__stage_14 (@0x390fb0) computes edx = (base+offset) & 0x3f (the 6-bit byte offset within the 64-byte line) and calls module__xdref_wideldshift_16_512_6 (@0x857410). Its body, read in full:

// module__xdref_wideldshift_16_512_6 @ 0x857410  (line: 16 dwords; addr_lo = addr & 0x3f)
uint32_t shamt = addr_lo << 3;          // shl $0x3       -> bit shift amount
uint32_t widx  = (shamt & 0x1f0) >> 5;  // and $0x1f0 ; shr $0x5  -> 32-bit word index
if (shamt & 0x10) {                     // and $0x10,%edx -> cross-word case?
    // cross-word funnel: join two adjacent 32-bit words
    lo = line[widx]   >> cl;            // shr %cl,%esi   (cl = shamt within word)
    hi = line[widx+1] << cl;            // shl %cl,%edi
    elem = (lo | hi);
} else {
    elem = line[widx] >> cl;
}
return elem & 0xffff;                   // and $0xffff,%eax -> mask to 16 bits

The _6 token is the 6-bit addr_lo. This same wideldshift_W_512_6 family is what valign uses behind the unaligned access — the plain load applies it to the in-line pick over one 64-byte line; valign applies it across two lines for full byte misalignment. [HIGH/OBSERVED]

3.3 The byte-phase bit [HIGH/OBSERVED]

opcode__ivp_lsnx16_i__stage_5 (@0x390f70) derives a phase/parity bit from the instruction word (shr $1 ; xor $1 ; and $1) and the memload gates the host read on addr & 0x1 ORed with a disable mask (sete → a je that skips the host read on disable / odd phase). This is the same byte-phase tracking valign's lalign carries, so the plain aligned word composes cleanly into the align-register two-phase streaming. [HIGH/OBSERVED]

3.4 The scalar load faults on misalignment [HIGH/OBSERVED]

The scalar core is hard-natural-aligned. memload__l32i (@0x88a3a0):

// memload__l32i @ 0x88a3a0
flag_misaligned = ((addr & 0x3) != 0);   // test $0x3,%sil ; setne %al
if (flag_misaligned) fault |= 0x4;       // or $0x4,%ecx   -> LoadStoreAlignmentException
host_read(addr, /*len=*/ 4);             // mov $0x4,%edx ; call *0x18(%r10)

memload__l16ui tests $0x1 (2-byte align); memload__l8ui does no alignment test. The fault surfaces through cas LoadStoreAlignmentException (§6.4). So: scalar ld/st = hard natural alignment; vector LSU = element-alignment-tolerant via the funnel; full byte misalignment of a vector access is the VALIGN regime. [HIGH/OBSERVED]


4. Access width — uniform 64-byte line, per-op element granularity

Every vector LSU access is a 64-byte (512-bit) host transfer (mov $0x40,%edx before every memload host read). The 512-bit vector register is the natural unit (32 × int16, 64 × int8, 16 × int32). The per-op element granularity is selected purely by the funnel suffix W: [HIGH/OBSERVED]

LSNX16     -> wideldshift_16_512_6     (16-bit elements, 32 lanes)
LSN_2X32   -> wideldshift_32_512_6     (32-bit,          16 lanes)
LSN_4X64   -> wideldshift_64_512_6     (64-bit,           8 lanes)
LSN_8X128  -> wideldshift_128_512_6    (128-bit,          4 lanes)
LSN_16X256 -> wideldshift_256_512_6    (256-bit,          2 lanes)
LVNX8U     -> wideldshift_256_512_6    (the 2nx8 64×8-bit access)

The host width ports confirm the supported set: nx_MemDataIs{8,16,32,64,128,256,512}Bits — eight access widths, each _0/_1 dual-pipe. nx_MemDataIs512Bits is the full-vector access; the narrower ports serve the sub-vector / scalar accesses. [HIGH/OBSERVED]

NOTE. There is no 1536-bit memory transfer. The wide MAC accumulator (wvec, 1536-bit, 4-entry) is filled from vector registers by the register op ivp_sem_wvec_pack (in S1_Ld), not by a memory load — the memory side stays 64-byte-wide. (See cas MAC / FMAC.) [CARRIED]


5. The fiss VALUE — the pipeline, address calc, byte layout

A vector load is a 6-stage fiss pipeline. The lsnx16_i chain, byte-exact:

stagesymboladdrwhat it computes
stateloadstateload__ivp_lsnx16_i0x3910f0instr word + SRs → per-op context (0x74..0x88, 0xe0)
regloadregload__ivp_lsnx16_i0x391160AR regfile [idx 0x6c] → base value → ctx 0x70
stage_5opcode__ivp_lsnx16_i__stage_50x390f70addr = base(0x70) + IMM(0x90)0xd8; phase bit → 0xdc/0xf8
memloadmemload__ivp_lsnx16_i0x391180read addr & MASK_W for $0x40 B via host port call *0x18(%r10) → staging 0x98..0xd4
stage_14opcode__ivp_lsnx16_i__stage_140x390fb0(addr & 0x3f)wideldshift_16_512_6 → extracted element
writebackwriteback__ivp_lsnx16_i0x391290result lanes → vec regfile (reg# 0x28 << 4, 16 lanes), guarded by fault flag

The memload stage is where cas's decode hands the access to the host memory model: mov 0x40(%r10),%rdi ; mov $0x40,%edx ; call *0x18(%r10) is the indirect call through the load port into the host's 64-byte line fetch. fiss does not own the bytes — it asks the host for them, then applies the funnel. [HIGH/OBSERVED]

5.1 The _x / _ip address-compute deltas [HIGH/OBSERVED]

The four addressing modes differ only in which context slot supplies the offset and whether an AR write-back fires:

// _i  : opcode__ivp_lsnx16_i__stage_5  @ 0x390f70
addr = ctx->base /*0x70*/ + ctx->imm    /*0x90*/;   // add 0x90

// _x  : opcode__ivp_lsnx16_x__stage_5  @ 0x391770
addr = ctx->base /*0x70*/ + ctx->index  /*0x94*/;   // add 0x94 (register, not immediate)

// _ip : opcode__ivp_lsnx16_ip__stage_14 @ 0x3913a0  (the post-increment update)
ebp        = ctx->base  /*0x70*/;        // access uses the CURRENT base
r12d       = ctx->incr  /*0x94*/;
new_base   = ebp + r12d;                 // add %r12d,%ebp  -> base := base + increment
shamt      = ctx->addr & 0x3f;           // and $0x3f -- extract offset UNCHANGED

The funnel/extract is identical across modes — only the offset slot (0x90 immediate vs 0x94 register) and the presence of the base-update differ. [HIGH/OBSERVED]

5.2 The post-increment write-back — vec and AR [HIGH/OBSERVED]

This is where the second AR def of §2.3 becomes a real regfile write. The _ip write-back emits two writes; the _i write-back emits one:

// writeback__ivp_lsnx16_i  @ 0x391290   -- VECTOR ONLY
write_vec_regfile(ctx->vec_regnum /*0x28*/ << 4, result_lanes);   // 16 lanes, no AR write

// writeback__ivp_lsnx16_ip @ 0x391690   -- VECTOR + AR
write_vec_regfile(ctx->vec_regnum /*0x28*/ << 4, result_lanes);   // 16 lanes
write_ar_regfile (ctx->ar_regnum  /*0x6c*/,      ctx->new_base /*0x74*/);  // the post-update AR

The _i write-back contains no 0x6c/0x74 reference — the extra AR write is the sole structural difference. This matches the cas issue's doubled and $0xf (§2.3). [HIGH/OBSERVED]

5.3 The store — the inverse funnel [HIGH/OBSERVED]

Stores execute at stage 11 (the write-back stage). opcode__ivp_ssnx16_i__stage_11 (@0x395330): addr = base(0x70) + offset(0x90); then (addr & 0x3f) + the source datum feed module__xdref_widestshift_512_16_6 (@0x857a00) — the inverse of the load funnel: it splices the 16-bit element into the 512-bit line at bit position (addr & 0x3f) << 3. The same phase bit is carried. The host store fires through the LSU store port. (Store stage 11 matches valign's salign drain at stage 11.) [HIGH/OBSERVED]

The predicated store SVNX8UT packs each lane via module__xdref_trunc_8_16 (byte truncate, 32×) and guards each lane by the vbool predicate — the same per-lane masking as the gather/select _t forms. [HIGH/OBSERVED]

5.4 Byte layout / extension [HIGH/OBSERVED layout; MED endianness]

The 64-byte line is staged as 16 consecutive 32-bit words (0x98..0xd4, ascending); the funnel indexes word = (shamt & 0x1f0) >> 5 then a cross-word join. Lane k of an int16 vector sits at byte 2k of the line — little-endian, word-ordered, element-k-at-byte-(k·elemW). The word-ordered staging is OBSERVED; that the silicon fabric is little-endian is INFERRED (the ISS mirrors host x86 LE).

Sign/zero extension on narrow scalar loads is a mnemonic split: the unsigned forms (LVNX8U/L8UI/L16UI) zero-extend, the signed forms (LVNX8S/L16SI) sign-extend. The opcode__*__stage_14 bodies (L16UI/L16SI @ 0x889df0/0x88a080) just move the host-fetched value into the AR result slot 0x28; the width/sign decision is carried by the upstream host-width fetch (nx_MemDataIs16Bits + the U/S decode bit). [HIGH for the U/S mnemonic split; MED for the exact extension locus being the host fetch]


6. The cas TIMING — latency, occupancy, faults

6.1 Per-operand latency [HIGH/OBSERVED]

Read from the mov $LAT,%esi immediately before each def/use hook:

operandLATmeaning
base AR (address)1address ready immediately (def-post)
index AR (_x/_xp)1
AR write-back (_ip/_xp)1the post-update def
dst/src vector10the vector load-use latency
scalar L32I dst4the AR load-use latency [CARRIED from the timing page]

A load posts its destination vector at LAT 10 — a dependent vector op stalls on the latency-aware bypass scoreboard until the loaded vector is forwardable. This is the vector load-use interlock; the scalar AR load-use is 4. [HIGH/OBSERVED for the vec/AR/index LATs; CARRIED for the scalar-4 cross-check]

6.2 The stall chain [HIGH/OBSERVED]

F0_F0_S0_LdSt_4_inst_IVP_LSNX16_I_stall (@0x11b21f0) calls, in order:

nx_Load_0_interface     ; the host LOAD PORT — pipe-0 occupancy
opnd_sem_AR_addr        ; base-AR RAW hazard
my_CPENABLE_stall       ; coprocessor / vector-unit enable
my_REV8AR_stall
my_WB_P_stall  /  my_WB_N_stall  /  my_WB_S_stall   ; writeback-port hazards
my_InOCDMode_stall      ; debug-halt interlock
my_MS_DISPST_stall      ; multi-slot dispatch

This is the same structural-hazard set the gather post and the generic LSU model use. The two LSU pipes are nx_Load_0 / nx_Load_1. A store has no architectural result; it occupies the writeback buffer (WB_{S,P,N}) and retires at fiss stage 11, so a following store stalls on the WB port. [HIGH/OBSERVED]

6.3 Address-space reach — the host ports [HIGH/OBSERVED]

The LSU's host-facing surface (from nm -D libcas-core.so):

  • Pipes/ports: nx_Load_{0,1}, nx_Store_0, nx_StoreAcc (store-accumulate); the addressing-mode decomposition nx_VAddr{Base,Index,Offset,Filter,Res,BaseAlternate,BaseUseAlternate} (Base+Index+Offset = the _i/_x modes; BaseAlternate = a second base; Filter = predicate); the width signal nx_MemDataIs{8..512}Bits.
  • Fault ports: nx_LoadStore{CrossMemAcc, InvalidTCMAcc, WrongIramAcc, UnsupportedAtomicOp}.

6.4 The exception set [HIGH/OBSERVED]

The cas exception table carries the full LSU exception roster:

LoadStoreAlignmentException   LoadStoreProhibitedException     LoadStoreFlixMemoryException
LoadStoreAXIDecErrorException LoadStoreAXISlvErrorException    LoadStoreBusErrorException
LoadStoreTLBMultiHitException LoadStoreSlaveAtomicErrorException
LoadStoreUndefinedAttrException  LoadStoreSGAccErrorException

The AXI / TLB / bus exceptions show the access is fabric + TLB-translated: the LSU touches the SoC memory fabric. Reconciled with the ABI memory view — a 64-bit SoC/HBM address is window-mapped to a 32-bit NX-local address; dataram (TCM, fast on-core local) and IRAM are the other regions. The InvalidTCMAcc / WrongIramAcc / CrossMemAcc faults gate the three regions (HBM-fabric / dataram-TCM / IRAM). cas models a uniform load-use LAT + WB-port occupancy — not a per-region HBM-vs-TCM latency; the region-dependent latency is host-resolved (the nx_*MemAccess ports return when ready). [HIGH for the port/fault evidence; MED for the per-region binding — INFERRED from the fault names + the ABI map, not a per-region latency constant]


7. The aligned primitive vs the unaligned wrapper

This page is the aligned LSU primitive; valign is the unaligned wrapper layered on top. They share the unit:

  • Same funnel — both use module__xdref_wideldshift_W_512_6. The plain load picks one element within one line (addr & 0x3f); valign joins two lines for full byte misalignment.
  • Same slots — both issue in S0_LdSt / S1_Ld.
  • Same phase bit — the plain load carries the byte-phase bit (§3.3) so it feeds the align register's two-phase streaming.
  • Same timing — the plain load's dst vector posts at LAT 10; valign's align-register result posts one stage earlier (LAT 9).

The layering is clean: aligned LS/SS = a 64-byte line access tolerant of natural element alignment; VALIGN = the front-end that assembles a fully byte-misaligned vector from two such aligned loads + the funnel + the align register. [HIGH/OBSERVED]

The indexed counterpart is orthogonal: this page is the non-indexed form (one base AR ± a scalar offset/index → one contiguous 64-byte line); SuperGather is the indexed form (16 per-lane 32-bit offsets in a vector register, scattering to per-lane addresses). The LSR* stream-rotate forms are the rotating-buffer streaming variant of the aligned load. [HIGH/OBSERVED roster; MED for the rotate-buffer model — inferred from name + slot]


8. The validation hook (VAL, Part 15)

Because the LSU is a clean stimulus/response oracle — feed it (base, offset, line bytes), read back (loaded vector, updated AR) — it is the ideal target for the differential VAL harness that Part 15 will define: drive the same lsnx16.{i,x,ip,xp} encoding through the fiss pipeline and a candidate reimplementation, and assert byte-equality of the extracted element, the alignment-mask line address, and (for _ip/_xp) the post-update AR. The funnel masks of §3.1 and the LAT table of §6.1 are the two highest-value invariants to pin first.


9. Honesty / uncertainty ledger

HIGH / OBSERVED (disassembled this pass):

  • The 35-root LV/SV/LS/SS/LSR roster + the LSU slot map (S0_LdSt/S1_Ld); the scalar L32I/L16/L8/S32/S16/S8 base-ISA ld/st.
  • The four addressing modes _i/_x/_ip/_xp byte-exact from LSNX16_*_issue (0x71ddc0/de20/deb0/df40): base AR (& 0xf, LAT 1) + dst vec (bits[8:4], LAT 10); _x index AR (bits[11:8]); _ip/_xp the doubled-and $0xf second AR def.
  • LV-vs-LS identical cas decode.
  • The 64-byte ($0x40) line; the per-width funnel wideldshift_W_512_6 {16,32,64,128,256}; the per-width mask {c1,c3,c7,cf,df} (all six verified).
  • The fiss pipeline stateload/regload/stage5/memload/stage14/writeback; addr = base + offset(0x90/0x94); the wideldshift extract; the widestshift_512_16_6 store funnel; store @ stage 11; the _ip add increment + dual (vec + AR) write-back.
  • The scalar L32I test $0x3 fault vs the vector funnel tolerance.
  • cas timing: AR LAT 1, vec LAT 10; the stall chain (nx_Load_0 + CPENABLE + REV8AR + WB_P/N/S + InOCDMode + MS_DISPST). The 119 nx_*_interface callbacks and 864 module__xdref_* value functions (both counts verified exact).
  • The host ports + fault ports + the 10-member LoadStore*Exception set.

MED / INFERRED:

  • The per-region (HBM/dataram-TCM/IRAM) binding of the fault ports — the names are OBSERVED, the region→fault binding is INFERRED, the per-region latency is host-resolved.
  • Device endianness (LE) — the word-ordered staging is OBSERVED, the silicon LE inferred.
  • The narrow-load sign/zero extension locus (host fetch vs opcode body) — the U/S split is HIGH, the exact locus MED.
  • The LSR* rotate-buffer semantics; the LE/LT/LTU compare-load → gather vbMask binding.

LOW:

  • The full per-mnemonic flag-bit map decoded by the shared ld_st_semantic_stage1 dispatcher — structurally a select-bit ladder, not enumerated field-by-field this pass.

CORRECTION. An early cross-reference put the count of LSU instruction-stage functions at "30,789". That figure is a filtered LSU subset and is not directly reproducible from the binary as stated — the total _inst_stage[0-9]+ symbol count in libcas-core.so is 157,775 (all units, every format × slot × stage), and the LSU portion is a fraction of that obtained by an additional mnemonic filter. This page therefore anchors on the two counts that are exactly reproducible — 119 nx_*_interface callbacks and 864 module__xdref_* value functions — and quotes the 35-root LSU roster rather than a single derived stage-function total. [HIGH/OBSERVED — both re-counted from nm this pass]