CSR — nsm (AXI Network-Security Monitor)
nsm is the AXI transaction-integrity watchdog that sits inline on a single
PCIe master's AXI path through the PEB IO fabric, between the PCIe ingress
("pcie to NSM boundary") and the on-die IO fabric ("NSM to iofabric boundary"). It is the hardware that runs the chain transaction → fault →
isolation → IRQ: it watches the AXI handshake and response protocol for nine
malformed/illegal transaction shapes, and on any of them it (a) latches a
sticky error, (b) fabricates an AXI error response back to the offending
master — SLVERR/DECERR plus a 0xDEADBEEF-poisoned 256-bit read payload — so
the hung master unblocks, and (c) optionally drives the shared PCIe isolation
state machine to quarantine the interface and raise a critical interrupt.
Everything on this page is read byte-exact out of the shipped, RTL-generated
register-description schema csrs/sprot/nsm.json (1,109 lines, Cayman /
NC-v3, the authoritative artifact) and cross-checked against the four
independently-generated renderings of the same block
(sprot_nsm.{h,hpp,go,yaml} + _consts.hpp, banner "Generated by reg_map tool
on 2023/08/08 10:27 / arch revision
cayman_golden_tapeout_candidate_2_2023_07_21-3-g4723889"), the Cayman flat
address map, and the intc/ trigger YAMLs. All four generated views and the JSON
agree field-for-field; any disagreement is flagged as a CORRECTION. Confidence
is tagged [conf · prov] where prov ∈ {OBSERVED, INFERRED, CARRIED}.
The fault→isolation→IRQ flow is unified in
../interrupt/nsm-flow-unified.md; the fabric
perimeter context is ../security/soc-fabric-perimeter.md
(SEC-02); the sprot access-control siblings are
remapper.md and qos-prot.md; the PCIe
isolation triggers are in
../interrupt/pcie-hbm-tpb-d2d-triggers.md;
and the fleet/trust-boundary census is
../address/pkl-intc-sprot-security.md.
DISAMBIGUATION — two unrelated "NSM"s.
sprot/nsm(this page) is the AXI Network-Security Monitor hardware CSR block. It is not the Cadence Xtensa Secure-Monitor library's "Non-Secure Mode" (-D_BUILD_NSM,secmon/*-nsm.S,libsec.a), which is a firmware-side privilege mode. They share only an acronym.[HIGH · OBSERVED]
WHAT NSM IS NOT. NSM is not an
AxPROT/ master-ID / VMID allow-list checker — that role belongs to itssprotneighboursamzn_remapper/user_remapperandqos_prot. No field in the entire register set referencesAxPROT, a master-ID, a VMID, or an address-region window (verified by absence across all 74 bitfields). NSM watches timing and protocol shape only — a denial-of-service / fabric- integrity guard against a misbehaving or hung PCIe endpoint corrupting or stalling the on-die AXI fabric, not an access-control gate.[HIGH · OBSERVED by absence]
1. Block-level metadata [HIGH · OBSERVED]
Read from .RegFile scalar fields of the Cayman nsm.json:
| field | value | note |
|---|---|---|
UnitName | nsm | |
Type | REGFILE | |
RegfileFlavor | POSEDGE | |
InterfaceType | APB | config bus is APB; the monitored bus is AXI |
AddrWidth | 12 (decimal) | → 4 KiB window |
DataWidth | 32 (decimal) | |
SizeInBytes | 0x1000 (hex) | = 4096 B |
Description | "" | empty at RegFile level |
HalName / HalFilenameUnitName | "" | empty |
Includes / Parameters / Memories | [] | all empty |
Totals grounded directly to the JSON (jq … | length, never a decompile
grep): 4 bundles, 50 registers, 74 bitfields. Per-bundle register counts
3 / 17 / 26 / 4.
| bundle | base | BundleSizeInBytes | #regs | HalExists | Description |
|---|---|---|---|---|---|
control | +0x000 | 0x100 | 3 | NONE | NSM control |
wr | +0x100 | 0x100 | 17 | NON_EX_ONLY | AXI write interface handling |
rd | +0x200 | 0x100 | 26 | NON_EX_ONLY | AXI read interface handling |
spare | +0x900 | 0x100 | 4 | NON_EX_ONLY | Spare register |
Each bundle ArraySize=1, GenFlavor=NORMAL. Note the 0x300..0x8FF gap before
spare. The bundle bases are independently confirmed by the absolute offsets in
the generated .h (CONTROL_REPORT_OFFSET 0x0, WR_STATUS_OFFSET 0x100,
RD_STATUS_OFFSET 0x200, SPARE_ALL_ZEROS_0_OFFSET 0x900).
SCHEMA GOTCHAS (verified). (1)
RegFile.SizeInBytesand everyBundleSizeInBytesare hex (0x1000/0x100). (2) RegisterAddressOffsetis mixed-radix: bare-decimal ("4","8") forcontrol.bypass/control.reset_staging_fifo, hex ("0x14","0x1c", …) thereafter — parse defensively. (3) Bitfield geometry is thePositionkey, single bit ("0","4") or range ("31:0","11:0","2:0","1:0") — notBitOffset/BitWidth. (4) Field access isAccessType ∈ {RO, RW};SpecialAccess ∈ {None, PulseOnW}. (5) No0xb1placeholder anywhere.[HIGH · OBSERVED]
In the per-field tables below, register AddressOffset is bundle-relative
(each bundle restarts at 0x0); the absolute offset is bundle base + reg offset (e.g. rd.error_data_0 = 0x200 + 0x1c = 0x21c, matching
RD_ERROR_DATA_0_OFFSET 0x21c in the generated .h).
2. The nine protocol-shape causes [HIGH · OBSERVED]
The malformed AXI transaction shapes NSM detects are exactly the nine sticky
error bits across wr.status (4 causes) and rd.status (5 causes) — jq-counted
to 9 from the JSON, and corroborated verbatim in sprot_nsm.h
(*_MASK 0x10/0x100/0x1000/0x10000 at shifts 4/8/12/16). They are spaced
on nibble boundaries (bits 0, 4, 8, 12, 16 — one cause per nibble, the rest of
each register reserved), which makes the status word trivially human-readable in
hex.
Write side — wr.status @+0x000 (abs 0x100), RO, reset 0x0, desc "Error report"
| bit | field | meaning (the malformed write shape) |
|---|---|---|
4 | error_1_b_no_match_aw_ro | spurious B: "A write response cannot be matched to an outstanding write" |
8 | error_2_b_to_aw_timeout | B timeout: "A write re[s]ponse took too long" |
12 | error_3_awvalid_to_awready_timeout | AW stall: "A write request is stuck trying to get to the fabric" |
16 | error_4_wvalid_to_wready_timeout | W stall: "The data part of a write request is stuck trying to get to the fabric" |
Read side — rd.status @+0x000 (abs 0x200), RO, reset 0x0
| bit | field | meaning (the malformed read shape) |
|---|---|---|
0 | error_0_rlast_before_last_rdata | short read: "A read response is shorter than the requested data" (RLAST early) |
4 | error_1_rlast_not_set | long read: "A read response is longer than the requested data" (RLAST never set) |
8 | error_2_r_no_match_ar | spurious R: "A read response cannot be matched to any outstanding read" |
12 | error_3_r_to_ar_timeout | R timeout: "A read re[s]ponse took too long" |
16 | error_4_arvalid_to_arready_timeout | AR stall: "A read request is stuck trying to get to the fabric" |
The asymmetry is structural and correct for AXI: beat-count / RLAST integrity
(short/long) only exists on the read side — a write data burst has no slave-
returned length to mismatch, so wr.status has no short/long cause and the write
side carries 4 causes versus the read side's 5. Mapping the nine to the four
classes of check:
| class | write causes | read causes |
|---|---|---|
| outstanding-match (spurious response) | error_1_b_no_match_aw_ro | error_2_r_no_match_ar |
| response-latency timeout | error_2_b_to_aw_timeout | error_3_r_to_ar_timeout |
request handshake stall (valid→ready) | error_3_awvalid…, error_4_wvalid… | error_4_arvalid… |
beat-count / RLAST integrity | — (n/a) | error_0_rlast_before…, error_1_rlast_not_set |
All nine roll up into the 2-bit top-level summary control.report
(error_wr = OR of wr.status[*], error_rd = OR of rd.status[*]).
3. Outstanding-transaction tracking and the timeout model [HIGH · OBSERVED]
NSM tracks every in-flight transaction per AXI ID in linked-list FIFOs whose
empty status is exposed in the *_pillm (per-ID linked-list-memory) registers:
- Write side: 32 IDs — a single
wr.sta_pillm_0(empty, IDs 0..31). - Read side: 128 IDs —
rd.sta_pillm_0..3(IDs 0-31, 32-63, 64-95, 96-127).
The deeper read ID space reflects the larger read-reordering window. When a B
or R response arrives that maps to no tracked outstanding AW/AR, it is the
spurious/no_match cause. The fill/drain of these FIFOs is observable through the
boundary counters (sta_up_cnt_* at the pcie→NSM boundary, sta_dn_cnt_* at the
NSM→iofabric boundary) and the linked-list sta_pop_cnt.
RESET-POLARITY QUIRK (cross-gen, the only divergence). The read-side empty- status registers reset to all-FIFOs-empty =
0xffffffffon Cayman (and Maverick/Mariana/Mariana+), but to0x0on Sunda. The write-sidewr.sta_pillm_0resets to0x0on all gens. Both polarities are independently confirmed in the generated.h(RD_STA_PILLM_0_RESET_VALUE 0xffffffff,WR_STA_PILLM_0_RESET_VALUE 0x0). This is cosmetic (RO status), not a functional/security change.[HIGH · OBSERVED]
Timeout model. Each direction has two independent timers — a request
stall timer and a response latency timer — and each timer is
tick × threshold:
cfg_*_tick.timeout— a 32-bit AXI-clock-cycle prescaler (cycles per tick).cfg_*_threshold.timeout— a 12-bit tick count to declare timeout;0disables that timer.
Max programmable window ≈ 2^32 × 2^12 AXI cycles. The four timer pairs are
{wr,rd}.cfg_req_{threshold,tick} and {wr,rd}.cfg_rsp_{threshold,tick}.
4. Violation response — fault latch, error injection, isolation [HIGH · OBSERVED]
4a. Sticky latch and clear
Every violation sets a sticky bit in wr.status/rd.status, OR-rolled into
control.report. The latch is cleared by writing the PulseOnW
{wr,rd}.cfg_clear.errors bit.
4b. The synthetic error-response injection — SLVERR/DECERR + 0xDEADBEEF
When NSM aborts a transaction it manufactures an AXI error response to the offending master so the master receives a completion and releases its outstanding slot rather than hanging the fabric:
- Write abort → returns
BRESP = wr.cfg_1.axi_bresp— a 2-bit code,2'b10 = SLVERR,2'b11 = DECERR. Reset value0x2(SLVERR by default). - Read abort → returns
RRESP = rd.cfg_1.axi_rresp(same encoding, reset0x2 = SLVERR) and a fabricated 256-bit read payload assembled from the eightrd.error_data_0..7registers (@+0x01c,+0x020,+0x024,+0x028,+0x02c,+0x030,+0x034,+0x038; abs0x21c..0x238). Each is a full 32-bitvalfield,RW, reset0xdeadbeef.
The 0xDEADBEEF magic constant is the read-data poison pattern: eight
contiguous 0xDEADBEEF words = 256 poison bits, a recognisable sentinel
returned as RDATA so the hung master gets data + an error RRESP and unblocks,
while any consumer of that data sees an obviously-poisoned value rather than
stale/garbage fabric contents. The constant is grounded eight times in the
JSON (jq count = 8, equal to the raw rg -ci deadbeef = 8) and appears in all
four generated views: 0xdeadbeef ×8 in sprot_nsm.hpp / _consts.hpp /
sprot_nsm.yaml (_default: 0xdeadbeef) and as decimal 3735928559 in
sprot_nsm.go (uint32(3735928559) / LoadValue(3735928559)). Because the
words are RW, secure firmware may reprogram the poison pattern after reset.
NOTE — constant is endianness-unambiguous.
3735928559 = 0xDEADBEEFexactly; the.godecimal and the.h/.hpp/.yamlhex are the same value, and the generated.hpinsRD_ERROR_DATA_0_RESET_VALUE 0xdeadbeef. There is no representation ambiguity in the poison word.[HIGH · OBSERVED]
4c. Isolation entry — two complementary trigger paths
Isolation entry is armed by two independent mechanisms, both per-direction:
-
Per-cause
enter_isolation_mode_on_*bits in{wr,rd}.cfg_1(6 bits total,jq-counted: 2 on write, 4 on read):reg field bit reset enters isolation on… wr.cfg_1enter_isolation_mode_on_rsp_error20an AXI error response wr.cfg_1enter_isolation_mode_on_spurious_rsp31an unexpected (spurious) response rd.cfg_1enter_isolation_mode_on_rsp_error20an AXI error response rd.cfg_1enter_isolation_mode_on_long_rsp31a response longer than requested rd.cfg_1enter_isolation_mode_on_short_rsp41a response shorter than requested rd.cfg_1enter_isolation_mode_on_spurious_rsp51an unexpected (spurious) response These bits sit above the 2-bit
axi_[b/r]respcode in the samecfg_1word. The composite reset values prove the default arming posture and are independently confirmed in the generated.h:WR_CFG_1_RESET_VALUE = 0x0000000a(= axi_bresp 0x2 | spurious bit3) andRD_CFG_1_RESET_VALUE = 0x0000003a(= axi_rresp 0x2 | long bit3 | short bit4 | spurious bit5). So out of reset, spurious/long/short responses auto-isolate but error-response isolation is disabled (*_on_rsp_error = 0). -
The richer per-cause auto-isolation mask
{wr,rd}.cfg_auto_iso(5 bits each, 10 total, all reset0):err_resp,long_resp,short_resp,spurious,timeout.SCHEMA-SYMMETRY QUIRK.
wr.cfg_auto_isocarrieslong_respandshort_respbits even though the write side has no beat-count/RLASTcause (writes have no slave-returned length to mismatch). The mask is laid out symmetrically for both directions; the writelong_resp/short_respbits have no correspondingwr.statuscause and are effectively inert.[HIGH · OBSERVED · INFERRED inertness]
Entry debounce. {wr,rd}.cfg_isolation_enter.count is a 16-bit cycle counter
("number of cycles to count before entering isolation mode; a value of zero
disables the counter") — a grace/debounce window before the interface is
isolated. Reset 0x0 ⇒ immediate entry.
Live FSM state is read back in {wr,rd}.sta_state (3-bit empty field):
bit 2 = short_resp_state, bits 1:0 = iso_state (confirmed verbatim in the
generated .h comment).
4d. Quiesce / recovery — the documented teardown order
After isolation, control.reset_staging_fifo drains the AW/W/AR staging FIFOs.
The field descriptions spell out the exact recovery sequence: clear the
staging FIFOs "after entering isolation mode, before resetting the iofabric".
CORRECTION vs naïve reading —
PulseOnWasymmetry. Onlyreset_staging_fifo.aw(bit 0) carriesSpecialAccess = PulseOnWin the JSON;w(bit 1) andar(bit 2) showSpecialAccess = None. All three are semantically pulse-clear ("clear … bus staging fifo"), so theNoneonw/aris most likely a schema inconsistency rather than a real RW-latch — but a faithful reimplementation should modelawas a self-clearing pulse and treatw/arper the literalNoneunless silicon proves otherwise.[MED · OBSERVED · INFERRED intent]
Default arming posture (from reset values). control.bypass.enable = 0x1 ⇒
the monitor ships bypassed out of reset (independently confirmed
CONTROL_BYPASS_RESET_VALUE 0x00000001); secure firmware must clear bypass.enable
to arm the monitor, then program the timers and isolation masks. This matches
the sprot family posture that NSM, like its remapper/qos neighbours, is brought
up and locked by secure boot.
GOTCHA —
bypass.enablesemantics are name-inferred. Reset =1is read here as "monitoring bypassed at reset"; the name alone is ambiguous (could mean "the bypass feature is enabled"). The secure-boot intent (NSM disarmed until firmware arms it) is[MED · INFERRED], not stated in the field description.
5. Full per-field register table [HIGH · OBSERVED]
Offsets are bundle-relative; absolute = bundle base + offset. Resets are the
field ResetValue.
Bundle control (base +0x000)
reg @off | acc | field [pos] | acc · special · reset | description |
|---|---|---|---|---|
report @0x0 | RO | error_wr [0] | RO · None · 0 | Any write error happens (OR of wr.status) |
error_rd [4] | RO · None · 0 | Any read error happens (OR of rd.status) | ||
bypass @4 | RW | enable [0] | RW · None · 0x1 | (bypass enable; armed by clearing) |
reset_staging_fifo @8 | RW | aw [0] | RW · PulseOnW · 0 | clear aw staging fifo (after isolation, before iofabric reset) |
w [1] | RW · None · 0 | clear w staging fifo (after isolation, before iofabric reset) | ||
ar [2] | RW · None · 0 | clear ar staging fifo (after isolation, before iofabric reset) |
Bundle wr (base +0x100, "AXI write interface handling")
reg @off (abs) | field [pos] | acc · special · reset | description |
|---|---|---|---|
status @0x0 (0x100) | error_1_b_no_match_aw_ro [4] | RO · None · 0 | spurious B (no outstanding write) |
error_2_b_to_aw_timeout [8] | RO · None · 0 | B took too long | |
error_3_awvalid_to_awready_timeout [12] | RO · None · 0 | AW stuck reaching fabric | |
error_4_wvalid_to_wready_timeout [16] | RO · None · 0 | W data stuck reaching fabric | |
cfg_rsp_threshold @0x14 (0x114) | timeout [11:0] | RW · None · 0 | ticks → write response timeout; 0 disables |
cfg_1 @0x18 (0x118) | axi_bresp [1:0] | RW · None · 0x2 | injected BRESP — 2'b10 SLVERR, 2'b11 DECERR |
enter_isolation_mode_on_rsp_error [2] | RW · None · 0 | isolate on AXI error response | |
enter_isolation_mode_on_spurious_rsp [3] | RW · None · 1 | isolate on unexpected response | |
cfg_rsp_tick @0x1c (0x11c) | timeout [31:0] | RW · None · 0 | AXI cycles per cfg_rsp_threshold tick |
cfg_req_threshold @0x20 (0x120) | timeout [11:0] | RW · None · 0 | ticks → write request timeout; 0 disables |
cfg_req_tick @0x24 (0x124) | timeout [31:0] | RW · None · 0 | AXI cycles per cfg_req_threshold tick |
cfg_clear @0x28 (0x128) | errors [0] | RW · PulseOnW · 0 | write clears sticky error flops |
cfg_isolation_enter @0x2c (0x12c) | count [15:0] | RW · None · 0 | cycles before entering isolation; 0 disables |
cfg_auto_iso @0x30 (0x130) | err_resp [0] | RW · None · 0 | auto-isolate on error-code response |
long_resp [1] | RW · None · 0 | auto-isolate on over-length response (inert on write — see §4c) | |
short_resp [2] | RW · None · 0 | auto-isolate on under-length response (inert on write) | |
spurious [3] | RW · None · 0 | auto-isolate on spurious response | |
timeout [4] | RW · None · 0 | auto-isolate on a timeout event | |
sta_pillm_0 @0x34 (0x134) | empty [31:0] | RO · None · 0 | FIFOs for IDs 0..31 empty status |
sta_dn_cnt_aw @0x38 (0x138) | empty [31:0] | RO · None · 0 | #AW valid&ready at NSM→iofabric boundary |
sta_dn_cnt_w @0x3c (0x13c) | empty [31:0] | RO · None · 0 | #W valid&ready at NSM→iofabric boundary |
sta_up_cnt_aw @0x40 (0x140) | empty [31:0] | RO · None · 0 | #AW valid&ready at pcie→NSM boundary |
sta_up_cnt_w @0x44 (0x144) | empty [31:0] | RO · None · 0 | #W valid&ready at pcie→NSM boundary |
sta_up_cnt_b @0x48 (0x148) | empty [31:0] | RO · None · 0 | #B valid&ready at pcie→NSM boundary |
sta_pop_cnt @0x4c (0x14c) | empty [31:0] | RO · None · 0 | number of linked-list pops |
sta_state @0x50 (0x150) | empty [2:0] | RO · None · 0 | bit 2: short_resp_state, bits 1:0: iso_state |
Bundle rd (base +0x200, "AXI read interface handling")
reg @off (abs) | field [pos] | acc · special · reset | description |
|---|---|---|---|
status @0x0 (0x200) | error_0_rlast_before_last_rdata [0] | RO · None · 0 | short read (RLAST early) |
error_1_rlast_not_set [4] | RO · None · 0 | long read (RLAST never set) | |
error_2_r_no_match_ar [8] | RO · None · 0 | spurious R (no outstanding read) | |
error_3_r_to_ar_timeout [12] | RO · None · 0 | R took too long | |
error_4_arvalid_to_arready_timeout [16] | RO · None · 0 | AR stuck reaching fabric | |
cfg_rsp_threshold @0x14 (0x214) | timeout [11:0] | RW · None · 0 | ticks → read response timeout |
cfg_1 @0x18 (0x218) | axi_rresp [1:0] | RW · None · 0x2 | injected RRESP — 2'b10 SLVERR, 2'b11 DECERR |
enter_isolation_mode_on_rsp_error [2] | RW · None · 0 | isolate on AXI error response | |
enter_isolation_mode_on_long_rsp [3] | RW · None · 1 | isolate on over-length response | |
enter_isolation_mode_on_short_rsp [4] | RW · None · 1 | isolate on under-length response | |
enter_isolation_mode_on_spurious_rsp [5] | RW · None · 1 | isolate on unexpected response | |
error_data_0 @0x1c (0x21c) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
error_data_1 @0x20 (0x220) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
error_data_2 @0x24 (0x224) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
error_data_3 @0x28 (0x228) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
error_data_4 @0x2c (0x22c) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
error_data_5 @0x30 (0x230) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
error_data_6 @0x34 (0x234) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
error_data_7 @0x38 (0x238) | val [31:0] | RW · None · 0xdeadbeef | dummy return AXI_RDATA after error |
cfg_rsp_tick @0x3c (0x23c) | timeout [31:0] | RW · None · 0 | AXI cycles per response-timeout tick |
cfg_req_threshold @0x40 (0x240) | timeout [11:0] | RW · None · 0 | ticks → read request timeout |
cfg_req_tick @0x44 (0x244) | timeout [31:0] | RW · None · 0 | AXI cycles per req-timeout tick |
cfg_clear @0x48 (0x248) | errors [0] | RW · PulseOnW · 0 | write clears sticky error flops |
cfg_isolation_enter @0x4c (0x24c) | count [15:0] | RW · None · 0 | cycles before isolation; 0 disables |
cfg_auto_iso @0x50 (0x250) | err_resp [0] | RW · None · 0 | auto-isolate on error-code response |
long_resp [1] | RW · None · 0 | auto-isolate on over-length response | |
short_resp [2] | RW · None · 0 | auto-isolate on under-length response | |
spurious [3] | RW · None · 0 | auto-isolate on spurious response | |
timeout [4] | RW · None · 0 | auto-isolate on a timeout event | |
sta_pillm_0 @0x54 (0x254) | empty [31:0] | RO · None · 0xffffffff | FIFOs for IDs 0..31 empty status |
sta_pillm_1 @0x58 (0x258) | empty [31:0] | RO · None · 0xffffffff | FIFOs for IDs 32..63 empty status |
sta_pillm_2 @0x5c (0x25c) | empty [31:0] | RO · None · 0xffffffff | FIFOs for IDs 64..95 empty status |
sta_pillm_3 @0x60 (0x260) | empty [31:0] | RO · None · 0xffffffff | FIFOs for IDs 96..127 empty status |
sta_dn_cnt_ar @0x64 (0x264) | empty [31:0] | RO · None · 0 | #AR valid&ready at NSM→iofabric boundary |
sta_up_cnt_ar @0x68 (0x268) | empty [31:0] | RO · None · 0 | #AR valid&ready at pcie→NSM boundary |
sta_up_cnt_r @0x6c (0x26c) | empty [31:0] | RO · None · 0 | #R valid&ready at pcie→NSM boundary |
sta_pop_cnt @0x70 (0x270) | empty [31:0] | RO · None · 0 | number of linked-list pops |
sta_state @0x74 (0x274) | empty [2:0] | RO · None · 0 | bit 2: short_resp_state, bits 1:0: iso_state |
Bundle spare (base +0x900)
reg @off (abs) | field [pos] | acc · reset | role |
|---|---|---|---|
all_zeros_0 @0x0 (0x900) | val [31:0] | RW · 0x00000000 | scan / bus-integrity read-back constant |
all_zeros_1 @0x4 (0x904) | val [31:0] | RW · 0x00000000 | scan / bus-integrity read-back constant |
all_ones_0 @0x8 (0x908) | val [31:0] | RW · 0xffffffff | scan / bus-integrity read-back constant |
all_ones_1 @0xc (0x90c) | val [31:0] | RW · 0xffffffff | scan / bus-integrity read-back constant |
The spare constants have no functional NSM role; they are read-back sanity
patterns. [MED · INFERRED]
6. The PCIe isolation state-machine feed and the IRQ [HIGH · OBSERVED]
A latched NSM violation that triggers isolation feeds two interrupt fabrics,
both confirmed verbatim in the Cayman intc/ trigger YAMLs:
6a. PEB apex INTC — level, critical
intc/peb_intc_triggers.yaml carries exactly one nsm trigger:
- trigger: intr_peb_nsm_axi_timeout
name: intr_peb_nsm_axi_timeout
description: PEB SPROT NSM timeout or error interrupt
edge_triggered: false # LEVEL
nmi_mask: 0
nmi_msix_mask: 0
critical: 1 # CRITICAL
It is a direct, level-sensitive, critical input to the peb_intc 128-input
apex summary tree (the L2 apex), sitting adjacent to the
apb_outstding_flushed_* outstanding-flush handshake. [HIGH · OBSERVED]
6b. PCIe isolation state machine — edge-triggered handshake
intc/pcie_triggers.yaml reset_handshake_intr[8..15] is the shared PCIe
isolation-SM event vector. NSM's AXI timeout is source [11], neighbouring
the other isolation causes:
| idx | name | meaning |
|---|---|---|
[8] | isolation_sm_linkdown_detected | link-down |
[9] | isolation_sm_flr_detected | Function-Level Reset |
[10] | isolation_sm_sbr_detected | Secondary Bus Reset |
[11] | isolatio_sm_nsm_axi_timeout_detected | NSM AXI Timeout |
[12] | isolation_sm_nts_axi_timeout_detected | NTS AXI Timeout (the qos_prot NTS sibling) |
[13] | isolation_sm_pir_detected | Pacific-Initiated Reset |
[14] | isolation_sm_isolation_mode_enter | isolation entered |
[15] | isolation_sm_isolation_mode_exit | isolation exited |
All eight are edge_triggered: true. The isolation SM consumes source [11]
(among the others), drives the interface into quarantine, and emits
isolation_mode_enter [14] (and later …_exit [15]).
QUIRK — verbatim typo in the source name. Index
[11]is spelledisolatio_sm_nsm_axi_timeout_detectedin the shipped YAML — missing the trailingnin "isolatio". It is the only index in[8..15]using the misspelledisolatio_smprefix;[8..10]and[12..15]all use the correctisolation_sm. A reimplementation matching the binary's symbol table must reproduce the typo on this one source.[HIGH · OBSERVED]
6c. End-to-end flow
PCIe master AXI txn
│ (pcie→NSM boundary; sta_up_cnt_* count it)
▼
[NSM inline monitor] ── tracks outstanding IDs in *_pillm FIFOs ─┐
│ handshake stall (AW/W/AR valid→ready) > timeout │
│ response latency (B/R) > timeout │
│ spurious response (no matching outstanding) │
│ beat-count mismatch (RLAST early/late, read only) │
▼ ▼
forward to iofabric (violation detected)
(NSM→iofabric boundary; (a) set sticky wr/rd.status
sta_dn_cnt_* count it) (b) roll up control.report
(c) inject SLVERR/DECERR + 8×error_data
│
if cfg_1.enter_iso_* OR cfg_auto_iso cause-bit set:
▼
cfg_isolation_enter.count cycle debounce (0 = immediate)
▼
ENTER ISOLATION (sta_state.iso_state)
│
┌─────────────────────────────────┼──────────────────────────┐
▼ ▼ ▼
pcie_triggers[11] peb_intc apex SW recovery:
isolatio_sm_nsm_axi_ intr_peb_nsm_axi_timeout reset_staging_fifo
timeout_detected (edge) (level, critical) {aw,w,ar} drain,
│ │ THEN reset iofabric,
▼ ▼ clear via cfg_clear,
isolation_sm_isolation_ → core (Q7/GIC; apex→core re-arm bypass.
mode_enter [14] vector map not in schema)
(… later …mode_exit [15])
INFERRED steps. The relative ordering of inject-response vs enter-isolation, and whether
bypass.enableauto-clears on isolation-exit, are reconstructed from field names plus the documented recovery note ("after entering isolation mode, before resetting the iofabric"), not stated explicitly. Thepeb_intc → Q7/GICvector forintr_peb_nsm_axi_timeoutis not in the shipped schema.[MED/LOW · INFERRED]
7. Instances — which masters/slaves NSM guards [HIGH · OBSERVED]
NSM is instantiated per PCIe interface, inline on that interface's AXI master
path into the PEB IO fabric. From output/address_map/address_map_flat.yaml
(rg -c NSM = 220: 28 real + 192 broadcast aliases — independently
counted by two readers):
- 28 real instances = 2
PEB_APB_IOblocks (_0,_1) × 14 interfaces. The 14 interfaces per block:AMZN_IO_PCIE_A_NSM(admin/physical-function path),AMZN_IO_PCIE_U_NSM(user/host path),AMZN_SE_{0,1}_PCIE_S0..S4_NSM(the two sub-engines' SR-IOV slave funcs S0..S4, 10 total),AMZN_PEB_PCIE_M_NSM(PEB PCIe "M" master),AMZN_PEB_NSM(PEB-internal). - 192 broadcast aliases =
PEB_APB_IO_BCAST_*write-broadcast shadows for fan-out config — not extra hardware.
Each window is size 0x1000; adjacent PCIE_S instances are 0x80000 apart.
Example absolute bases (PEB_APB_IO_0; the _1 block adds 0x800000000000):
| interface | base |
|---|---|
IO_PCIE_A_NSM | 0x020008016017000 |
SE_0_PCIE_S0_NSM | 0x020008016097000 (S1 …117000, stride 0x80000) |
IO_PCIE_U_NSM | 0x020008016D97000 |
PEB_PCIE_M_NSM | 0x02000801E017000 |
PEB_NSM | 0x02000801E097000 |
The APB config bus is the secure CSR path; the guarded bus is the PCIe-
side AXI of the named interface. So NSM guards the on-die IO fabric (the slave)
against each PCIe master's malformed AXI traffic. [HIGH bind · MED roles]
NOTE —
sprotdirectory ≠ instantiation container. Althoughnsm.jsonlives undercsrs/sprot/, NSM is not inside theFIS_0_SPROTcontainer that holdsamzn_remapper+qos_prot; it is a standalonePEB_APB_IOleaf (thepkl-intc-sprot-security.mdcensus places it atAMZN_PEB_NSMoffset0x11c000insideamzn_peb, secure-only). "sprot" here is the schema directory, not the same hardware container.[HIGH · OBSERVED]
CORRECTION / RECONCILIATION — 28 vs 24 instance counts. This page's 28 comes from the Cayman flat address-map YAML (2
PEB_APB_IOblocks × 14 interfaces). The siblingpkl-intc-sprot-security.mdreports 24 for NSM (4 SENG × 3 dies × 2 apertures), counted from the Maverickal_address_map_db.pklunder a different instance model. The two figures are not in conflict — they count different generations through different schema databases (Cayman flat-YAML interface leaves vs Maverick pkl die/aperture product). Both are[HIGH · OBSERVED]within their own source; do not cross-substitute them. The NSM register block itself is byte-identical across both gens (§8).
8. Cross-generation divergence [HIGH · OBSERVED]
nsm.json was located for cayman (authoritative, nested
cayman-arch-regs_tgz), mariana, mariana_plus, sunda, and
maverick (maverick/vpc-mirror/arch-regs/src/…). A normalized field-by-field
signature (bundle/reg@off/field[pos]/reset/access/special, md5-folded) yields:
- cayman ≡ mariana ≡ mariana_plus ≡ maverick — byte-identical
(
md5 ca82c156…). All shareSizeInBytes 0x1000,AddrWidth 12,DataWidth 32,InterfaceType APB, bundles{control, wr, rd, spare}, reg counts3/17/26/4, 74 bitfields, identical field names/positions/access/ descriptions. - cayman vs sunda — exactly one divergence:
rd.sta_pillm_0..3ResetValue(Cayman0xffffffff, Sunda0x0);wr.sta_pillm_0reset =0x0in all gens. All descriptions identical. (Cosmetic RO empty-FIFO indicator polarity — see §3.)
NOTE — Maverick (v5) NSM is OBSERVED on disk, not header-only. Unlike some v5/MAVERICK blocks where only a generated header is shipped, the Maverick
nsm.jsonitself is present (maverick/vpc-mirror/arch-regs/src/csrs/sprot/) and is byte-identical to Cayman. So the NSM register map specifically is[HIGH · OBSERVED]on v5 — the v5-interior behaviour (AXI dataflow, apex wiring) remains[* · INFERRED]per thepkl-intc-sprot-security.mdWALL, but the layout is not inferred. Cayman is authoritative and representative for all gens.[HIGH · OBSERVED]
9. Open items / uncertainty
reset_staging_fifo.{aw=PulseOnW, w=None, ar=None}—awis the only pulse- clear in the JSON;w/arare most likely intended pulse-clear too (§4d).[MED]bypass.enablereset0x1read as "monitor bypassed at reset"; the name alone is ambiguous and the secure-boot arming intent is inferred.[MED]- Ordering of error-injection vs isolation-entry, and
bypassauto-clear on isolation-exit, are inferred from field names + the recovery note.[MED] peb_intc → Q7/GICvector forintr_peb_nsm_axi_timeoutis not in the shipped schema.[LOW]