CAYMAN × SP image (+ SP-vs-TOP_SP)
The CAYMAN × SP firmware image is the program for the Sync-Processor (TPB_SP,
engine_idx = 4) — the fifth and last NX engine inside one NC-v3 TPB, alongside
PE (0), ACT (1), POOL (2) and
DVE (3). It is one more compiled flavor of the same shared cayman/seq/
NX-class SEQ-dispatch chassis the other four engines run — byte-identical reset vector, boot
trampoline, .globstruct dispatcher-state magic and SEQ dispatch-table architecture — recompiled
with the leanest possible handler subset. This page carves all 12 image variants byte-exact
from libnrtucode_internal.so, proves SP's 18-handler roster is the exact 5-way intersection
of all five engines (zero engine-specific handlers), resolves the SP-vs-TOP_SP naming collision
via the shipped ISA enum, characterizes the SP DRAM (SEQ dispatch table, no PROF, no baked
tables) and the SP↔EVT_SEM/barrier interaction, and closes the 5-engine CAYMAN set.
The decisive result is a negative: SP adds nothing. Where PE layers five matmul micro-ops, ACT
seven activation ops and POOL twenty-three compute ops onto the shared control core, SP's entire
handler set is that shared control core — {AluOp, BRANCH, BranchPrefetchHint, Event_Semaphore, EXT_BREAK, Halt, INS_BREAK, INS_FL, MOVE, NOP, NOTIFY, POLL_SEM, Redirect, SET_OM, STRONG_ORDER, TensorLoad, TensorStore, WRITE}, 18 names, the precise lower bound of the SEQ engine model. SP is
the pure control/sync substrate the other four engines are built on top of. The leanness is the
story.
Confidence/evidence tags follow the project
Confidence & Walls Model: HIGH/MED/LOW ×
OBSERVED/INFERRED/CARRIED. Every device fact is byte-pinned to a carve from
libnrtucode_internal.so (sha256 b7c67e89…) and decoded with the shipped ncore2gp
xtensa-elf-objdump; the EVT_SEM aperture geometry, the TOP_SP block facts and the barrier
pre-lowering are CARRIED from the engine/collective pages cited inline. The page default is
[HIGH/OBSERVED]; claims that depart from it carry an explicit tag.
NOTE — the objects used. Container:
…/custom_op/c10/lib/libnrtucode_internal.so(sha256b7c67e898a116454a8e0ce257b1d6523a23ffa237a6ec21021ecb70632fc329b, ELF64 x86-64 DYN, not stripped). First RLOADis the identity map (off 0x0 == vaddr 0x0,filesz 0x9af194), so each<NAME>.dataaccessor address is simultaneously the.rodataVA and the file offset of its blob — carve =so[ptr : ptr+size]. The SP image blobs are the device-side.rodatapayloads of theimg_CAYMAN_NX_SP_*members. IRAM file-offset == device IRAM VA (reset vector at byte 0); DRAM string-file-offset == device DRAM VA −0x80000. Disassembler:extracted/nested/gpsimd_tools_tgz/tools/XtensaTools/bin/xtensa-elf-objdump(GNU Binutils 2.34.20200201,XTENSA_CORE=ncore2gp, ConfigNameXm_ncore2gp, uarch Cairo, Xtensa24, RI-2022.9,TargetHWVersion=NX1.1.4, FLIX/VLIW). The clean C ISA headeraws_neuron_isa_tpb_common.h(shipped redistributable, in the samecustomoppackage) is cited for the engine enum and the opcode values.
1. The headline
- CAYMAN × SP is the same
cayman/seq/SEQ-dispatch engine as PE/ACT/POOL/DVE, compiled with the minimal handler subset. Byte-decisive: the carved IRAM reset vector is the byte-identical06 76 00 00(j 0x1dc) shared by all five engines; the boot trampoline at0x1dc(const16 a0,0 ; const16 a0,0x90 ; jx a0 → enter_run @0x90) decodes exactly as POOL's; the DRAM header word is the shared.globstructmagic0x6099cb34; the DEBUG dispatch table base is the same DRAM0x814; the assertion paths are the same/opt/workspace/NeuronUcode/cayman/seq/ src/…. - SP = the SYNC/CONTROL sequencer (
TPB_SP,engine_idx = 4, the 5th execution engine inside one TPB). Its 18 distinct handlers are exactly the shared SEQ control core —AluOp,BRANCH,BranchPrefetchHint,Event_Semaphore,EXT_BREAK,Halt,INS_BREAK,INS_FL,MOVE,NOP,NOTIFY,POLL_SEM,Redirect,SET_OM,STRONG_ORDER,TensorLoad,TensorStore,WRITE— with zero engine-specific compute handlers. - SP is the LEANEST of the five, and its set is the EXACT 5-way intersection. Handler tally
across the wave: DVE 53 | POOL 41 | ACT 26 | PE 24 | SP 18. SP's 18 names equal the
intersection
SP ∩ ACT ∩ DVE ∩ PE ∩ POOLbyte-for-name (diff EMPTY), and SP is a strict subset of every one of the other four individually (SP-handlers-not-in-{ACT,DVE,PE,POOL} =0/0/0/0). SP has zero SP-only handlers. - 12 image getters; 6 carry real bytes, 6 are zero-size boundary cursors — DEBUG/PERF/TEST ×
{IRAM, DRAM} = 6 flat firmware segments; DEBUG/PERF/TEST × {SRAM, EXTRAM} = 6 empty cursors (SP
runs entirely out of IRAM+DRAM on CAYMAN). All 6 real carves are byte-identical (sha256) to the
matching
libnrtucode.amember.rodata. - SP ships NO PROF and has NO paired Q7. Where ACT/DVE/PE/POOL each ship 14 NX getters
(12 base + 2 PROF
{CAM, TABLE}, byte-identical across the four), SP ships 12 = base only:nm | rg -c CAYMAN_NX_SP_PROF= 0,rg -c CAYMAN.*Q7_SP= 0. SP is the only CAYMAN NX engine without PROF, and (unlike POOL) carries no paired Q7 compute core or EXTISA ELF. It is a pure NX-only sequencer.
CORRECTION — "SP hosts the barrier/semaphore handlers" is the wrong framing. SP hosts no SP-exclusive sync/barrier/collective handler. The sync primitives —
Event_Semaphore(0xa0),POLL_SEM(0xb3),NOTIFY(0xa6),Event_Semaphore Rng Clr(0xb0) — are part of the shared 18-handler core present on all five engines (each carries exactly one of each). What distinguishes SP is the negative: it is the engine whose entire handler set is this sync/control core and nothing else. The collective pseudo-ops (CORE_BARRIER 0xd8) are pre-lowered by the compiler into these same sharedEVENT_SEMAPHORE/POLL_SEMHW ops — there is no dedicated0xd8handler on SP or any engine (§7).
2. The 12 image getters (instruction-exact)
Each getter is the 4-instruction (img-ptr, size) stub
(lea <blob>(%rip),%rax ; mov %rax,(%rdi) ; movq $<size>,(%rsi) ; ret) disassembled
from .text 0x9b3220..0x9b3520 (PERF/TEST) and 0x9b3720..0x9b37a0 (DEBUG). CLS=NX, ENG=SP. All
12 .data addresses (use plain nm, not nm -D — they are local t symbols) and sizes agree with
the catalog (image-catalog-index.md, CAYMAN NX_SP rows).
| VARIANT | REGION | ACCESSOR (.text VA) | IMG-PTR (.rodata VA = file off) | SIZE | STATUS |
|---|---|---|---|---|---|
| PERF | IRAM | 0x9b3220 | 0x0ba8a0 | 0x182c0 | REAL (SEQ code) |
| PERF | DRAM | 0x9b3240 | 0x0d2b60 | 0x02d40 | REAL (SEQ data, no S:) |
| PERF | SRAM | 0x9b3260 | 0x0d58a0 (=ACT_TEST_IRAM cursor) | 0 | EMPTY (boundary) |
| PERF | EXTRAM | 0x9b3280 | 0x0d58a0 (=ACT_TEST_IRAM cursor) | 0 | EMPTY (boundary) |
| TEST | IRAM | 0x9b34a0 | 0x136640 | 0x16ba0 | REAL (SEQ code) |
| TEST | DRAM | 0x9b34c0 | 0x14d1e0 | 0x03040 | REAL (SEQ data) |
| TEST | SRAM | 0x9b34e0 | 0x150220 (=ACT_DEBUG_IRAM cursor) | 0 | EMPTY (boundary) |
| TEST | EXTRAM | 0x9b3500 | 0x150220 (=ACT_DEBUG_IRAM cursor) | 0 | EMPTY (boundary) |
| DEBUG | IRAM | 0x9b3720 | 0x1d4b60 | 0x199a0 | REAL (SEQ code) |
| DEBUG | DRAM | 0x9b3740 | 0x1ee500 | 0x06360 | REAL (SEQ data + S: log) |
| DEBUG | SRAM | 0x9b3760 | 0x1f4860 (=Q7_POOL_PERF_IRAM cursor) | 0 | EMPTY (boundary) |
| DEBUG | EXTRAM | 0x9b3780 | 0x1f4860 (=Q7_POOL_PERF_IRAM cursor) | 0 | EMPTY (boundary) |
The six zero-size SRAM/EXTRAM getters all execute movq $0x0,(%rsi) and return their pointer at the
contiguous-layout cursor = the start of the next blob in the .rodata layout. objdump
therefore aliases the SP DEBUG SRAM/EXTRAM symbols to CAYMAN_Q7_POOL_PERF_IRAM_get.data (0x1f4860)
and the PERF/TEST SRAM/EXTRAM symbols to the next ACT blob — confirming SP is followed in the layout
by the POOL Q7 image. SP uses no SRAM/EXTRAM on CAYMAN.
NOTE — why 12, not 14. The four sequencer engines ACT/DVE/PE/POOL each ship 14 NX getters = 12 base + 2 PROF
{CAM, TABLE}. SP ships 12 = base only. It has no HW-decode profiling CAM/table (nm | rg -c CAYMAN_NX_SP_PROF= 0). SP is the only CAYMAN NX engine without PROF.
2.1 Carve provenance + byte-identity
Carve rule (identity map): blob = so[IMG-PTR : IMG-PTR+SIZE] via dd bs=1. The 6 real carves and
their sha256 (spot-reconciled 3/3 identical to the libnrtucode.a
member .rodata via ar p + objcopy -O binary --only-section=.rodata + cmp):
| IMAGE | FILE-OFF | SIZE | sha256 (full) |
|---|---|---|---|
SP_PERF_IRAM | 0xba8a0 | 0x182c0 | 5a6f6eaa7f6654a089b199c4c0ef2c48d6c7c79f28dc1c405161314b18ac1858 |
SP_PERF_DRAM | 0xd2b60 | 0x2d40 | 9fe5e19db3d5e865052c5e2886ecd5a3c8c898531f0beb6bd61df42db0f67d04 |
SP_TEST_IRAM | 0x136640 | 0x16ba0 | b24ef299778ce0655545f6786b21997e6ac14e3de8292f499a8e9096c601b949 |
SP_TEST_DRAM | 0x14d1e0 | 0x3040 | deed216d810cf32fb094b38f54c3862ed808f5f1f89c92d56157f28960a7bf6b |
SP_DEBUG_IRAM | 0x1d4b60 | 0x199a0 | 6c3a6f79373cbe69038f794f6154e08bed312cb7ab8dd5ef1973dd5adaf9962d |
SP_DEBUG_DRAM | 0x1ee500 | 0x6360 | 5340ad8c6aa6a38868f76caf4d6dfbe6fd544b004bdccdbcf4a99ea35f778b76 |
The SP PERF_IRAM 5a6f6eaa is the engine-distinguishing fingerprint in the cross-engine matrix
(§9); it reproduces the value the PE/POOL pages cited for SP
exactly. The archive ships the SP img_*_contents.c.o members (PERF/TEST/DEBUG × {IRAM,DRAM})
and no hwdecode_*_PROF_* member; the internal.so getter blob equals the .a member .rodata
for the 3 spot-checked (SP_PERF_IRAM, SP_DEBUG_IRAM, SP_DEBUG_DRAM), byte-for-byte.
3. Flat-image geometry + boot
None of the 6 carves is an ELF (head ≠ \x7fELF) — they are flat device-memory segments, the
device-side .rodata payload of the img_* members (contrast POOL's Q7 EXTISA blobs, which are
EM_XTENSA ELFs; SP has none). Heads:
IRAM (all 3 variants byte-identical): 06 76 00 00 00 00 86 77 00 00 00 00 ; j 0x1dc / j 0x1e8
DRAM (all 3 variants): 34 cb 99 60 ; header word 0x6099cb34
Reset vector (byte-identical across all 3 SP IRAM variants AND across all 5 engines):
0x000: 06 76 00 j 0x1dc ; primary reset vector -> boot path
0x006: 86 77 00 j 0x1e8 ; secondary vector -> halt trap
0x1dc: 04 00 00 const16 a0,0
0x1df: 04 90 00 const16 a0,144 ; (= 0x90)
... jx a0 ; jump to C enter_run @ 0x90
0x1e8: 00 52 00 halt 0 ; 2nd vector = HALT trap
The boot jx a0 lands on enter_run @ 0x90; the trampoline at 0x1dc decodes exactly as
POOL's NX core. The same flat binary can be loaded on any engine slot: the DRAM carries
S: engine_base_addr=%llx tpb_base_addr=%llx -> is_tpb=%u is_die_0=%u engine_idx=%u, so the SP
image's engine_idx (= 4) is derived at boot from engine_base_addr vs tpb_base_addr — the
binary is not hard-wired to SP (§4c). [string HIGH/OBSERVED; runtime-compute INFERRED-HIGH]
Disassembled with the shipped ncore2gp objdump, SP_DEBUG_IRAM decodes a
full Q7/NX windowed-ABI code body — not a stub. Census:
| metric | SP DEBUG IRAM |
|---|---|
entry (window-frame open) | 515 |
retw (window return) | 728 |
call8 | 1550 |
callx8 | 71 |
const16 | 2044 |
jx | 53 |
So SP carries the full windowed-ABI control spine + the generic SEQ fetch/DMA/cache infrastructure
— it is a genuine, separately-compiled cayman/seq/ sequencer, not a thin trampoline. (The
FLIX-vector datapath is partly bundle-interleaved by the linear sweep — the documented FW-00
limitation — but the control spine decodes cleanly.)
4. The SP-vs-TOP_SP resolution
The brief's central question: is the IMG "SP" engine the per-NeuronCore SP (engine_idx 4), or
the standalone TOP_SP SoC-level sync block? The shipped ISA enum settles it decisively: they
are two distinct enumerated engines, and the carved image is the per-core TPB_SP.
4a. The ISA enum is authoritative — TPB_SP(4) != TOP_SP(5)
aws_neuron_isa_tpb_common.h:139-146 (verbatim):
typedef enum NEURON_ISA_TPB_NEURON_ENGINE {
NEURON_ISA_TPB_NEURON_ENGINE_PE = 0,
NEURON_ISA_TPB_NEURON_ENGINE_ACT = 1,
NEURON_ISA_TPB_NEURON_ENGINE_POOL = 2,
NEURON_ISA_TPB_NEURON_ENGINE_DVE = 3,
NEURON_ISA_TPB_NEURON_ENGINE_TPB_SP = 4, // <== the per-NeuronCore SP engine (this image)
NEURON_ISA_TPB_NEURON_ENGINE_TOP_SP = 5, // <== the standalone top-level SP block
};
There are six engines, and TPB_SP and TOP_SP are separate enumerators. The image-catalog
ENG = SP row is the TPB_SP = engine_idx 4 — the 5th execution engine inside one
TPB/NeuronCore (PE/ACT/POOL/DVE + SP). This is exactly the project's
PE=0 / ACT=1 / POOL=2 / DVE=3 / TPB_SP=4 model
(per-engine-depth.md §1). The standalone TOP_SP (engine 5) is the
separate SoC-level sequencer that hosts the global EVT_SEM array + the SoC time-sync tick and
walks the host-built collective program (the
collective end-to-end engine_idx 5 target). They are
not the same block; the naming collision (both abbreviate to "SP") is the entire source of the
question. [HIGH/OBSERVED — the enum is the authoritative shipped artifact]
4b. Both have an NX core — but this library ships exactly ONE SP image [HIGH on the one-image fact]
Both TPB_SP and TOP_SP embed an Xtensa NX core. The gpsimd-customop firmware library surfaces
exactly one NX SP image family, CAYMAN_NX_SP (nm | rg -c CAYMAN.*TOP_SP = 0 — there is
no TOP_SP-named getter). The carved CAYMAN_NX_SP blobs comfortably fit the TPB_SP NX core
geometry (NX_IRAM 0x20000 = 128 KiB holds SP_DEBUG_IRAM 0x199a0 ≈ 105 KiB / SP_PERF_IRAM 0x182c0 ≈ 99 KiB). The binding "this image runs on the TPB_SP (engine 4) NX core" is
INFERRED-HIGH from the getter name + the engine enum + the geometry fit. How (or whether) the
standalone TOP_SP is provisioned is out of this library's scope — that is NCFW/runtime territory;
the data here neither confirms nor denies that the same cayman/seq/ build serves both slots, and
this page does not fabricate an answer. [HIGH on the one-image fact + the zero TOP_SP getter; LOW / NOT-CLAIMED on the TOP_SP provisioning path.]
4c. engine_idx is runtime-computed, not baked
The SP DEBUG DRAM carries the same boot-identity string as every NX engine —
S: engine_base_addr=%llx tpb_base_addr=%llx -> is_tpb=%u is_die_0=%u engine_idx=%u — a runtime
format string, not a constant. The engine identity (engine_idx, is_tpb, is_die_0) is derived
at boot from the engine_base_addr the core reads against tpb_base_addr. So the SP image
self-identifies as engine_idx=4 because it is placed at the TPB_SP IRAM base, not because of
its bytes. This is the architectural reason all five NX engines can share the identical reset vector
and the identical boot trampoline: one boot path, late-bound identity. [HIGH/OBSERVED — the string + the shared boot; the late-binding mechanism INFERRED-HIGH from the runtime-formatted identity string]
4d. Conclusion
The carved image is unambiguously the per-NeuronCore TPB_SP (engine 4) — the 5th execution
engine inside the TPB — distinct from the standalone TOP_SP (engine 5). The two blocks share an
architecture (each an NX-core sequencer with a semaphore/notification surface) and a role family
(the TPB-side sequencer-processor IP), but they are two enumerated, separately-addressed engines.
The POLL_SEM op the per-core SP carries is what lets it participate in global sync orchestrated
by the standalone TOP_SP; it is not evidence that the image is TOP_SP.
5. The SP handler set = the EXACT 5-way intersection
Method (identical to the sibling pages): extract every single-token S: <OpName> from each engine's
CAYMAN DEBUG DRAM (regex ^S: [A-Za-z][\w/-]*$), sort -u, set-diff. The SP DEBUG DRAM yields
exactly 18 distinct handler names. Per-engine counts (derived independently from
each engine's DEBUG DRAM carve): DVE 53 | POOL 41 | ACT 26 | PE 24 | SP 18 — SP
is the leanest of the five.
5a. SP's 18 handlers (the complete roster)
Read directly from the SP DEBUG DRAM's own S: logs (the 142 S: strings in the DEBUG build):
AluOp BRANCH BranchPrefetchHint Event_Semaphore EXT_BREAK Halt INS_BREAK INS_FL
MOVE NOP NOTIFY POLL_SEM Redirect SET_OM STRONG_ORDER TensorLoad TensorStore WRITE
By function: [HIGH for the names; the grouping is the analyst's, MED]
| group | handlers |
|---|---|
| control flow / fetch | BRANCH, BranchPrefetchHint, Redirect, Halt |
| debug / break | EXT_BREAK, INS_BREAK, INS_FL |
| data move | MOVE, TensorLoad, TensorStore, WRITE |
| scalar ALU | AluOp |
| ordering | SET_OM (ordering mode), STRONG_ORDER |
| sync / EVT_SEM | Event_Semaphore (0xa0), POLL_SEM (0xb3), NOTIFY (0xa6) |
| no-op | NOP |
5b. SP == the intersection; a strict subset of every engine
comm -12 of all five engines' handler sets (SP ∩ ACT ∩ DVE ∩ PE ∩ POOL) yields exactly these
18 names — SP's set is byte-for-name identical to the 5-way intersection (diff EMPTY).
Pairwise-verified: SP-handlers not-in-ACT = 0, not-in-DVE = 0, not-in-PE = 0, not-in-POOL = 0.
SP is a strict subset of every other engine; it has zero engine-exclusive handlers. SP is the
shared control core that ACT/DVE/PE/POOL are each built on top of by adding their compute handlers.
| engine | total | = shared 18 core | + engine-specific (examples) |
|---|---|---|---|
| SP | 18 | the 18 (= the whole set) | none |
| PE | 24 | the 18 | EngineNop + 5 matmul (Ldweights/Matmul/MatmulSparse/LdTags/PeRegWrite) |
| ACT | 26 | the 18 | activation set (Activate/Cast/Copy/ActivationTableLoad/…) |
| POOL | 41 | the 18 | pool/reduce/gather/sort/dequant + ExtendedInst (the Q7 bridge) + RNG |
| DVE | 53 | the 18 | the batch-norm/predicated/scan/dropout data-vector set |
So the engines are the same cayman/seq/ firmware with disjoint compute subsets layered on the
shared 18-handler control core; SP's extension is the empty one — the precise lower bound. "Same
SEQ engine, different handler subset" is CONFIRMED for SP, in its limiting/degenerate form.
QUIRK —
EngineNopis the cleanest discriminator between "control core" and "lean compute engine". PE — the next-leanest at 24 — carriesEngineNop(a barrier/serialization helper shared with POOL/DVE), but SP does not. SP's 18 names containNOP(the scalar no-op) but notEngineNop. The 18-name intersection is therefore strictly smaller than "PE minus its 5 matmul ops": SP is the only engine with no member outside the all-five intersection.
6. The SP DRAM — SEQ dispatch table, no PROF, no baked tables
The SP DRAM is the generic SEQ data segment: the .globstruct header + dispatcher-state init block
- the SEQ dispatch table + the
S:string pool. No weight/LUT/kernel table is baked in — SP is a pure control sequencer.
.globstruct header + dispatcher-state init (byte-identical to POOL). DRAM head: header word
0x6099cb34 @ 0x0; the dispatcher-state init block — 4 × 0x00001000 @ 0x18 and 4 × 0x00ffffff
@ 0x28 — is byte-identical between SP and POOL. The shared init block is
the common dispatcher-state initialization every flat NX DRAM carries.
The SEQ dispatch table @ DRAM 0x814 (file 0x814). SP carries the same indexed-jump
trampoline-pointer table POOL/PE/ACT use at the same base (DEBUG, 4-byte-LE
trampoline pointers): [HIGH location / MED per-opcode row decode]
0x814: 90 2a 00 00 c9 2a 00 00 c9 2a 00 00 c9 2a 00 00 ; 0x2a90, then 0x2ac9 (Bad-Opcode band)
0x824: 99 2a 00 00 88 2a 00 00 70 2a 00 00 78 2a 00 00 ; 0x2a99 0x2a88 0x2a70 0x2a78
Trampoline targets cluster in 0x2a70..0x4400 (the handler-trampoline region) with 0x2ac9 as the
repeated Bad-Opcode slot and 0x2fe4-band defaults. The table is followed by the string pool at
0xa98, with the dispatch log S: Dispatch opcode=0x%x @ 0xaa8. SP's table simply binds fewer
real handlers (most slots → default), matching its 18-handler set. The ErrorHandler arms
(Bad Opcode(0x%x) @ 0x2f81, Illegal Instruction(0x%x), FP Error(%d), Int Div Zero Error)
are all present. In PERF the table relocates to file 0x218 (head 0x62d0 0x62d6 0x62e2 0x62eb,
then a 0x98c3 repeating default band) — the same DEBUG-segmented-vs-PERF-clean split the sibling
engines show. [HIGH/OBSERVED for the table location + default/trampoline bands; the exhaustive per-opcode→handler decode is the FLIX-desync-limited frontier (FW-00), not fully decoded here.]
NOTE — no resident table in the SP firmware. SP carries no weight/coefficient/kernel table. Its only "tables" are the dispatch table and the shared dispatcher-state block. (Contrast POOL, which additionally hosts a
kernel_info_tablein its Q7 EXTISA ELFs; SP has no Q7 and no EXTISA.)
The SP DRAM otherwise carries the generic SEQ runtime infra — S: IRAM cache init,
S: start_fill_siram, S: DramRingDMA::allocate, sunda_fast_fetch, S: Sunda seq Loop, the
DGE (descriptor-generation-engine) family (13 DGE strings, incl. DGE: Select backend Pool/RTL),
and S: dge_shape[].step/.num — confirming the same cache/PC-bounds/DMA/shape machinery as the
other NX engines.
7. The SP ↔ EVT_SEM / barrier interaction
SP's sync handlers map to the shipped ISA opcodes (from the OPCODE enum in
aws_neuron_isa_tpb_common.h):
| HW op | ISA opcode | enum | S: roster handler |
|---|---|---|---|
| Event-semaphore arrive/wait | 0xa0 | …_OPCODE_EVENT_SEMAPHORE | Event_Semaphore |
| Notify | 0xa6 | …_OPCODE_NOTIFY | NOTIFY |
| Event-semaphore range-clear | 0xb0 | …_OPCODE_EVENT_SEMAPHORE_RANGE_CLEAR | (folded into Event_Semaphore) |
| Poll / min-fold semaphore | 0xb3 | …_OPCODE_POLL_SEM | POLL_SEM |
GOTCHA — four EVT_SEM HW opcodes, three roster handlers. The OPCODE enum has four EVT_SEM ops (
0xa0/0xa6/0xb0/0xb3), but the SPS:-handler roster has only three sync names (Event_Semaphore,NOTIFY,POLL_SEM). The0xb0EVENT_SEMAPHORE_RANGE_CLEARhas no separate roster entry on any engine — it is covered by the singleEvent_Semaphorehandler (range-clear is a mode of that handler, not its own dispatch arm). Verified across all five DEBUG DRAMs.
These are the EVT_SEM HW-semaphore operations. They are shared by all five engines (each carries
exactly one of each S: name — verified) — not SP-exclusive. SP is simply the engine whose
entire handler set is this sync/control core.
The barrier path (CARRIED from the collective + per-engine-depth pages). The collective/barrier
pseudo-ops — CORE_BARRIER 0xd8, SYNC_BARRIER 0xd5, DMA_BARRIER 0xc3 (all present in the ISA
OPCODE enum as PSEUDO_* opcodes) — are pre-lowered by the compiler / NRT into concrete
EVENT_SEMAPHORE(0xa0) arrive/wait + POLL_SEM(0xb3) HW instructions before the stream reaches
the engine sequencer. There is no dedicated 0xd8 handler on SP or any engine, and zero
Barrier S:-strings appear in any of the five DEBUG DRAMs — which is exactly why SP's 18-handler
set has Event_Semaphore/POLL_SEM but no "Barrier" handler. [HIGH/OBSERVED — the absence of a 0xd8handler / anyBarrier string on all 5 engines; the pre-lowering is the [collective end-to-end](../orientation/collective-end-to-end.md) finding.]
The HW substrate these ops operate on is the EVT_SEM aperture: 256 hardware EVENTS (1-bit
set/clr) + 256 hardware SEMAPHORES (32-bit counters), with distinct APB sub-windows aliasing the
array per operation (read/set/inc/dec). A CORE_BARRIER rendezvous keys on a semaphore handle; SP's
Event_Semaphore/POLL_SEM ops are the arrive/wait against that semaphore. The EVT_SEM geometry
is HIGH (per-engine-depth.md §7); the exact SP-op→APB-window binding
is MED — the addressing lives in the lowered instruction operands, not in the firmware image.
[HIGH EVT_SEM geometry / MED op-to-window binding]
NOTE — "the TOP_SP poll accelerator" vs the per-core
POLL_SEM. The collective docs callPOLL_SEM"theTOP_SPMin-fold poll accelerator" and labelTOP_SP = engine 5.POLL_SEM(0xb3) is nonetheless present on the per-coreTPB_SPimage (engine 4) AND on all five engines — it is a generic SEQ sync op, not aTOP_SP-only accelerator. The "TOP_SPpoll accelerator" phrasing reflects that the standaloneTOP_SPblock (engine 5) is the global-sync host; the per-core engines all carry the op to participate.[HIGH thatPOLL_SEMis on all 5 per-core engines; the global-sync role of the standaloneTOP_SPis the collective subsystem's territory.]
7.1 Annotated SP dispatch + barrier/event-wait path
The SP sequencer reproduced as C pseudocode. Symbols are byte-pinned (CAYMAN opcodes / DRAM offsets OBSERVED; the EVT_SEM aperture + barrier pre-lowering CARRIED from the cited pages):
// ---------------------------------------------------------------------------
// CAYMAN SP (TPB_SP, engine_idx 4) — boot -> main fetch/dispatch -> handler.
// SP is PURE CONTROL/SYNC: its 18 handlers ARE the shared SEQ control core;
// it issues no compute. engine_idx is computed at boot, not baked.
// Sync opcodes (shared by ALL 5 engines, observed):
// EVENT_SEMAPHORE=0xa0 NOTIFY=0xa6 EVENT_SEM_RANGE_CLEAR=0xb0 POLL_SEM=0xb3
// NO dedicated barrier handler: CORE_BARRIER(0xd8)/SYNC_BARRIER(0xd5)/
// DMA_BARRIER(0xc3) are pre-lowered by the compiler into 0xa0/0xb3 ops.
// ---------------------------------------------------------------------------
void sp_enter_run(void) { // boot jx a0 lands @ IRAM 0x90
sp_compute_engine_idx(); // engine_base_addr vs tpb_base_addr -> 4 (TPB_SP)
// "S: engine_base_addr=%llx ... engine_idx=%u"
bool hw_decode = nx_mode_select(); // dual-mode SEQ feature (not SP-specific)
for (;;) { // "S: Sunda seq Loop" main loop
instr_t *ins = seq_fetch(hw_decode); // I$-backed fetch (cache + PC-bounds + DGE/DMA infra)
uint8_t op = ins->opcode;
log("S: Dispatch opcode=0x%x", op); // DRAM 0xaa8 (DEBUG only)
// DEBUG: 4-byte-LE trampoline table @ DRAM 0x814 (most slots -> Bad-Opcode 0x2ac9).
// PERF: relocated addx4 table @ DRAM 0x218. SP binds ONLY the 18 control-core opcodes.
switch (op) {
// ---- the SYNC / EVT_SEM ops (shared by all 5 engines; SP's whole reason to exist) -----
case 0xa0: h_event_semaphore(ins); break; // "S: Event_Semaphore" arrive/set/inc/dec; also
// covers 0xb0 range-clear (a mode, not its own arm)
case 0xb3: h_poll_sem(ins); break; // "S: POLL_SEM" wait / min-fold (<=16 reads)
case 0xa6: h_notify(ins); break; // "S: NOTIFY" "S: sending notification"
// ---- the rest of the shared 18-handler control core --------------------------------
case 0x..: h_alu_op / h_branch / h_branch_prefetch_hint / h_redirect / h_move /
h_tensor_load / h_tensor_store / h_write / h_set_om / h_strong_order /
h_halt / h_nop / h_ext_break / h_ins_break / h_ins_fl; break;
// ---- there is NO compute case on SP, and NO 0xd8 barrier case on ANY engine --------
default: error_handler(op); // "S: ErrorHandler : Bad Opcode(0x%x)" @0x2f81
}
}
}
// ---- the barrier / event-wait path the sync handlers drive ----
// A CORE_BARRIER(0xd8) the compiler emitted was ALREADY lowered into these two ops
// against a chosen semaphore handle in the 256-semaphore EVT_SEM array.
void h_event_semaphore(instr_t *ins) { // 0xa0 the ARRIVE side
uint32_t sema = ins->sema_handle; // index into the 256-entry HW semaphore array
// SET / INC / DEC / RANGE_CLEAR via the per-op APB sub-window (aliases of the array).
evt_sem_apply(sema, ins->op_mode, ins->delta); // arrive: this core signals it reached the point
}
void h_poll_sem(instr_t *ins) { // 0xb3 the WAIT side (min-fold poll accelerator)
uint32_t sema = ins->sema_handle;
// poll up to n_read (<=16) semaphores, min-fold, block until the target count is met;
// this is the rendezvous wait for the lowered CORE_BARRIER.
while (evt_sem_min(sema, ins->n_read) < ins->target)
; // HW-assisted poll; no busy CPU spin in the seq
// proceed: all participating engines/cores have arrived.
}
void h_notify(instr_t *ins) { // 0xa6 notification emit
// "S: sending notification" / "S: sending interrupt" — the per-core SEQ notify path.
// (The standalone TOP_SP's 4-way SW-notification fabric is a SEPARATE block-level path.)
seq_send_notification(ins->notify_target, ins->payload);
}
GOTCHA — the engine-numbering subtlety in the notify/interrupt path. SP's
S: sending notification/S: sending interruptstrings are the per-coreTPB_SP(engine 4) notification path. The standaloneTOP_SP(engine 5) block has its own 4-way SW-notification fabric (sp_nx_nt/sp_explicit_nt/events_semaphores_nt/errors_nt) — a different, block-level fabric. Do not conflate the per-core SP notify with the SoCTOP_SPfabric.[HIGH that SP carries notify/interrupt dispatch; theTOP_SPfabric is a distinct block.]
8. DEBUG vs PERF(release) vs TEST
| variant | IRAM size | DRAM size | S: strings | total DRAM strings |
|---|---|---|---|---|
| DEBUG | 0x199a0 | 0x6360 | 142 | 242 |
| PERF | 0x182c0 | 0x2d40 | 0 | 15 |
| TEST | 0x16ba0 | 0x3040 | 0 | 59 |
- DEBUG is the only build carrying the 142
S:runtime log strings — the RE substrate (every handler self-names, including the 18-handler roster). It is the largest variant. - PERF (the production/release flavor) strips all
S:logs: DRAM shrinks to0x2d40, only 15 strings survive — assertion source-paths only (…/exception_handler.hpp,…/decode/*.cpp,Assertion failure!). - TEST sits between: 0
S:logs but 59 strings — keeps function-name/file symbols for assert context. - The dispatch mechanism is invariant across all three: same reset vector (
06 76 00 00), same boot →enter_run @0x90, same SEQ table (DEBUG @0x814/ PERF @0x218), sameErrorHandler/ Bad-Opcode arm, same.globstructmagic. A DEBUG→RELEASE(PERF) swap is a pure observability change, not a functional/dispatch change.
9. Cross-engine code-sharing
Cross-engine PERF_IRAM matrix (all 5 distinct, all reproduce the prior engine pages exactly):
| engine | idx | PERF_IRAM sha256 | PERF_IRAM size | PROF |
|---|---|---|---|---|
| PE | 0 | 13ba3969… | 0x159e0 | 8fd7e422 / ce761f81 |
| ACT | 1 | 5ef2a351… | 0x13dc0 | 8fd7e422 / ce761f81 |
| POOL | 2 | 9049bf8c… | 0x17280 | 8fd7e422 / ce761f81 |
| DVE | 3 | 9fa066f4… | 0x15c20 | 8fd7e422 / ce761f81 |
| SP | 4 | 5a6f6eaa… | 0x182c0 | (no PROF) |
- CODE/DATA (IRAM/DRAM): SP shares NO bytes with PE/ACT/POOL/DVE. Each engine's PERF_IRAM has a
distinct sha256 — each is a separately-compiled
cayman/seq/build with its own handler subset. The sharing is at the source/structure level (identical reset vector06 76 00 00, identical boot trampoline →enter_run @0x90, identical 18-handler control core, identical.globstructmagic0x6099cb34+ init block, identical SEQ dispatch-table architecture, identical ErrorHandler arms, identical EVT_SEM op set) — not at the linked-byte level. - PROF: SP ships none. The other four NX engines share byte-identical PROF_CAM (
8fd7e422) / PROF_TABLE (ce761f81); SP has neither (nm | rg -c CAYMAN_NX_SP_PROF= 0). SP is the only CAYMAN NX engine without PROF. - Q7: SP has none. POOL is the only CAYMAN engine with a paired Q7 (and EXTISA ELFs). SP is a pure NX-only sequencer.
QUIRK — SP's PERF_IRAM is the LARGEST of the five despite the FEWEST handlers. SP_PERF_IRAM (
0x182c0≈ 99 KiB) is bigger than POOL's (0x17280), DVE's, PE's and ACT's PERF_IRAM — even though SP has the fewest handlers (18 vs POOL's 41). The reason: the build size is infrastructure-dominated, not handler-count-dominated. SP carries the full SEQ fetch / DMA / cache / sunda-fetch infrastructure (DGE × 13,DramRingDMA,start_fill_siram, IRAM cache,sunda_fast_fetch, the notification/interrupt dispatch) without any compute handlers to amortize that infra against — so the common spine is a larger fraction of a smaller total. Handler count is a poor proxy for image size.
10. Engine-model classification — the 5-engine CAYMAN set is complete
SP is a SEQ-style ASCII-opcode dispatch engine (the NX-class sequencer model), the same
cayman/seq/ firmware as PE/ACT/POOL/DVE, compiled with the minimal/intersection handler subset.
"Same SEQ engine, different handler subset" is CONFIRMED for SP, in its limiting form.
| property | SP (this image) | the other 4 NX engines |
|---|---|---|
| packaging | flat IRAM/DRAM segments | flat IRAM/DRAM |
| reset vector | j 0x1dc (06 76 00 00) | j 0x1dc (identical) |
| boot | const16 a0,0x90 ; jx → enter_run @0x90 | identical |
| dispatch base | DRAM 0x814 (DEBUG) / 0x218 (PERF) | identical bases |
.globstruct magic | 0x6099cb34 + shared init block | identical |
| miss policy | ErrorHandler : Bad Opcode(0x%x) | identical |
| source tree | cayman/seq/src/… | identical |
| distinct handlers | 18 (= the 5-way intersection) | 24 / 26 / 41 / 53 |
| compute subset | none | matmul / activation / pool / data-vector |
| PROF | none | byte-identical CAM+TABLE |
| paired Q7 | none | POOL only |
With SP carved + handler-diffed, the five-engine CAYMAN set is complete:
| ENGINE | idx | HANDLERS | ROLE | SPECIAL |
|---|---|---|---|---|
| PE | 0 | 24 | matmul / weights / tags | PE-only matmul set |
| ACT | 1 | 26 | activation / cast / copy | ACT-only activation set |
| POOL | 2 | 41 | general compute (RICHEST) | dual-dispatch + Q7 + EXTISA |
| DVE | 3 | 53 | data/vector (most COUNT) | DVE-only bn/scan/dropout |
| SP (this page) | 4 | 18 | SYNC / CONTROL (LEANEST) | == the 5-way intersection |
All five: identical reset 06 76 00 00, boot → enter_run @0x90, .globstruct 0x6099cb34, SEQ
dispatch table @ 0x814, shared 18-handler control core. The four NX sequencers (PE/ACT/POOL/DVE)
share byte-identical PROF; SP ships none. POOL alone pairs a Q7. SP alone is the pure control
core — the differentiation is entirely the layered compute-handler subset on a common SEQ
chassis.
11. Honesty ledger
HIGH / OBSERVED:
- 12
CAYMAN_NX_SPgetters indexed instruction-exact (6 real + 6 zero-size boundary cursors → next-blob ACT/Q7-POOL IRAM); 6 real carves byte-identical (sha256) to thelibnrtucode.amember.rodata(3/3 spot-reconciled). SP PERF_IRAM =5a6f6eaa. NO PROF getter, NO Q7_SP, NO TOP_SP getter (nm | rg -c= 0/0/0). - All carves FLAT (no ELF magic); reset vector
06 76 00 00(j 0x1dc) identical across DEBUG/PERF/TEST and across all 5 engines; bootconst16 a0,0x90 ; jx → enter_run @0x90; 2nd vectorj 0x1e8 → halt 0.SP_DEBUG_IRAMcensus 515 entry / 728 retw / 1550 call8 / 71 callx8 / 2044 const16 / 53 jx (nativencore2gpobjdump). - DRAM head
0x6099cb34; dispatcher-state init block (4×0x1000@0x18,4×0xffffff@0x28) byte-identical to POOL. SEQ dispatch table @0x814(DEBUG, default-band0x2ac9/0x2fe4); PERF table @0x218;S: Dispatch opcode=0x%x@0xaa8; ErrorHandler arms @0x2f81. - SP handler set = 18 = the EXACT 5-way intersection (diff EMPTY); strict subset of every engine
(pairwise not-in = 0/0/0/0). Counts reproduce the wave (DVE 53 | POOL 41 | ACT 26 | PE 24 |
SP 18).
EngineNoppresent on PE/POOL/DVE but not SP. - The ISA enum
TPB_SP = 4/TOP_SP = 5(aws_neuron_isa_tpb_common.h:139-146) — the authoritative SP-vs-TOP_SP disambiguation. - EVT_SEM opcodes
EVENT_SEMAPHORE 0xa0/NOTIFY 0xa6/RANGE_CLEAR 0xb0/POLL_SEM 0xb3read from the ISA OPCODE enum; the three roster handlers (Event_Semaphore/NOTIFY/POLL_SEM) present on SP AND all 5 engines;0xb0folded intoEvent_Semaphore.CORE_BARRIER 0xd8/SYNC_BARRIER 0xd5exist asPSEUDO_*opcodes but have no engine handler (zeroBarrierstrings in any of the 5 DEBUG DRAMs). - Cross-engine PERF_IRAM sha matrix (5 distinct, all reproduce prior pages). SP_PERF_IRAM is the
largest despite the fewest handlers (infra-dominated). DEBUG 142
S:logs; PERF/TEST 0. - SP-unique infra string
sunda_fast_fetch;DGE × 13;S: sending notification/interrupt.
MED / INFERRED:
- "The
CAYMAN_NX_SPimage runs on theTPB_SP(engine 4) NX core" — INFERRED-HIGH from the getter name + ISA enum + theNX_IRAM 0x20000geometry fit; the image carries no self-bakedengine_idx(it is runtime-computed from base addr). - The
engine_idxlate-binding mechanism (one boot path, identity derived fromengine_base_addr) — INFERRED-HIGH from the runtime-formatted identity string + the shared reset/boot across all 5. - The SP-op → specific EVT_SEM APB-window binding — MED (the addressing is in the lowered instruction operands, not the firmware image; the EVT_SEM aperture geometry itself is HIGH/CARRIED).
- The exhaustive per-opcode SEQ dispatch-table row decode (FLIX/literal desync, FW-00).
LOW / NOT CLAIMED:
- Whether the standalone
TOP_SP(engine 5) runs the samecayman/seq/SP build or a different firmware path: this library ships only one NX SP image (namedSP = TPB_SP); how/whetherTOP_SPis provisioned is out of scope (NCFW/runtime territory). Not fabricated. - Which silicon part / runtime selects DEBUG vs PERF vs TEST.
- SUNDA/MARIANA/MAVERICK SP variants (out of CAYMAN scope; see mariana-sp.md and the forward Part-6 pages).
12. Cross-references
- Image Catalog Index — the full getter map (CAYMAN NX_SP rows).
- Per-Engine Firmware Depth (PE / SP / TOP_SP / ACT) — §7 is the
companion deep-dive on the
TPB_SP/TOP_SPsync ops, the no-dedicated-barrier-handler result, the EVT_SEM aperture, and the per-gen SP evolution. - A Collective, End to End — the
CORE_BARRIER 0xd8pre-lowering and theTOP_SP(engine 5) collective-program execution this page references. - SEQ Decode / Dispatch Hub — the shared SEQ table / trampoline / thunk dispatch mechanism SP runs (the 178-entry POOL table is the worked example; SP binds fewer).
- The sibling CAYMAN engine images: × PE, × ACT, × POOL, × DVE.
- MARIANA × SP image — the v4 SP diff baseline against this page.
- PROF_CAM / PROF_TABLE formats — the generic HW-decode profiling CAM/table shared by the 4 NX engines that have PROF; SP has none.
- Confidence & Walls Model — the tag taxonomy.