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

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 its sprot neighbours amzn_remapper/user_remapper and qos_prot. No field in the entire register set references AxPROT, 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:

fieldvaluenote
UnitNamensm
TypeREGFILE
RegfileFlavorPOSEDGE
InterfaceTypeAPBconfig bus is APB; the monitored bus is AXI
AddrWidth12 (decimal)→ 4 KiB window
DataWidth32 (decimal)
SizeInBytes0x1000 (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.

bundlebaseBundleSizeInBytes#regsHalExistsDescription
control+0x0000x1003NONENSM control
wr+0x1000x10017NON_EX_ONLYAXI write interface handling
rd+0x2000x10026NON_EX_ONLYAXI read interface handling
spare+0x9000x1004NON_EX_ONLYSpare 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.SizeInBytes and every BundleSizeInBytes are hex (0x1000/0x100). (2) Register AddressOffset is mixed-radix: bare-decimal ("4", "8") for control.bypass/control.reset_staging_fifo, hex ("0x14", "0x1c", …) thereafter — parse defensively. (3) Bitfield geometry is the Position key, single bit ("0", "4") or range ("31:0", "11:0", "2:0", "1:0") — not BitOffset/BitWidth. (4) Field access is AccessType ∈ {RO, RW}; SpecialAccess ∈ {None, PulseOnW}. (5) No 0xb1 placeholder 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"

bitfieldmeaning (the malformed write shape)
4error_1_b_no_match_aw_rospurious B: "A write response cannot be matched to an outstanding write"
8error_2_b_to_aw_timeoutB timeout: "A write re[s]ponse took too long"
12error_3_awvalid_to_awready_timeoutAW stall: "A write request is stuck trying to get to the fabric"
16error_4_wvalid_to_wready_timeoutW 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

bitfieldmeaning (the malformed read shape)
0error_0_rlast_before_last_rdatashort read: "A read response is shorter than the requested data" (RLAST early)
4error_1_rlast_not_setlong read: "A read response is longer than the requested data" (RLAST never set)
8error_2_r_no_match_arspurious R: "A read response cannot be matched to any outstanding read"
12error_3_r_to_ar_timeoutR timeout: "A read re[s]ponse took too long"
16error_4_arvalid_to_arready_timeoutAR 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:

classwrite causesread causes
outstanding-match (spurious response)error_1_b_no_match_aw_roerror_2_r_no_match_ar
response-latency timeouterror_2_b_to_aw_timeouterror_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 IDsrd.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 = 0xffffffff on Cayman (and Maverick/Mariana/Mariana+), but to 0x0 on Sunda. The write-side wr.sta_pillm_0 resets to 0x0 on 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; 0 disables 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 value 0x2 (SLVERR by default).
  • Read abort → returns RRESP = rd.cfg_1.axi_rresp (same encoding, reset 0x2 = SLVERR) and a fabricated 256-bit read payload assembled from the eight rd.error_data_0..7 registers (@+0x01c, +0x020, +0x024, +0x028, +0x02c, +0x030, +0x034, +0x038; abs 0x21c..0x238). Each is a full 32-bit val field, RW, reset 0xdeadbeef.

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 = 0xDEADBEEF exactly; the .go decimal and the .h/.hpp/.yaml hex are the same value, and the generated .h pins RD_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:

  1. Per-cause enter_isolation_mode_on_* bits in {wr,rd}.cfg_1 (6 bits total, jq-counted: 2 on write, 4 on read):

    regfieldbitresetenters 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]resp code in the same cfg_1 word. 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) and RD_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).

  2. The richer per-cause auto-isolation mask {wr,rd}.cfg_auto_iso (5 bits each, 10 total, all reset 0): err_resp, long_resp, short_resp, spurious, timeout.

    SCHEMA-SYMMETRY QUIRK. wr.cfg_auto_iso carries long_resp and short_resp bits even though the write side has no beat-count/RLAST cause (writes have no slave-returned length to mismatch). The mask is laid out symmetrically for both directions; the write long_resp/short_resp bits have no corresponding wr.status cause 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 — PulseOnW asymmetry. Only reset_staging_fifo.aw (bit 0) carries SpecialAccess = PulseOnW in the JSON; w (bit 1) and ar (bit 2) show SpecialAccess = None. All three are semantically pulse-clear ("clear … bus staging fifo"), so the None on w/ar is most likely a schema inconsistency rather than a real RW-latch — but a faithful reimplementation should model aw as a self-clearing pulse and treat w/ar per the literal None unless 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.enable semantics are name-inferred. Reset = 1 is 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 @offaccfield [pos]acc · special · resetdescription
report @0x0ROerror_wr [0]RO · None · 0Any write error happens (OR of wr.status)
error_rd [4]RO · None · 0Any read error happens (OR of rd.status)
bypass @4RWenable [0]RW · None · 0x1(bypass enable; armed by clearing)
reset_staging_fifo @8RWaw [0]RW · PulseOnW · 0clear aw staging fifo (after isolation, before iofabric reset)
w [1]RW · None · 0clear w staging fifo (after isolation, before iofabric reset)
ar [2]RW · None · 0clear ar staging fifo (after isolation, before iofabric reset)

Bundle wr (base +0x100, "AXI write interface handling")

reg @off (abs)field [pos]acc · special · resetdescription
status @0x0 (0x100)error_1_b_no_match_aw_ro [4]RO · None · 0spurious B (no outstanding write)
error_2_b_to_aw_timeout [8]RO · None · 0B took too long
error_3_awvalid_to_awready_timeout [12]RO · None · 0AW stuck reaching fabric
error_4_wvalid_to_wready_timeout [16]RO · None · 0W data stuck reaching fabric
cfg_rsp_threshold @0x14 (0x114)timeout [11:0]RW · None · 0ticks → write response timeout; 0 disables
cfg_1 @0x18 (0x118)axi_bresp [1:0]RW · None · 0x2injected BRESP — 2'b10 SLVERR, 2'b11 DECERR
enter_isolation_mode_on_rsp_error [2]RW · None · 0isolate on AXI error response
enter_isolation_mode_on_spurious_rsp [3]RW · None · 1isolate on unexpected response
cfg_rsp_tick @0x1c (0x11c)timeout [31:0]RW · None · 0AXI cycles per cfg_rsp_threshold tick
cfg_req_threshold @0x20 (0x120)timeout [11:0]RW · None · 0ticks → write request timeout; 0 disables
cfg_req_tick @0x24 (0x124)timeout [31:0]RW · None · 0AXI cycles per cfg_req_threshold tick
cfg_clear @0x28 (0x128)errors [0]RW · PulseOnW · 0write clears sticky error flops
cfg_isolation_enter @0x2c (0x12c)count [15:0]RW · None · 0cycles before entering isolation; 0 disables
cfg_auto_iso @0x30 (0x130)err_resp [0]RW · None · 0auto-isolate on error-code response
long_resp [1]RW · None · 0auto-isolate on over-length response (inert on write — see §4c)
short_resp [2]RW · None · 0auto-isolate on under-length response (inert on write)
spurious [3]RW · None · 0auto-isolate on spurious response
timeout [4]RW · None · 0auto-isolate on a timeout event
sta_pillm_0 @0x34 (0x134)empty [31:0]RO · None · 0FIFOs 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 · 0number of linked-list pops
sta_state @0x50 (0x150)empty [2:0]RO · None · 0bit 2: short_resp_state, bits 1:0: iso_state

Bundle rd (base +0x200, "AXI read interface handling")

reg @off (abs)field [pos]acc · special · resetdescription
status @0x0 (0x200)error_0_rlast_before_last_rdata [0]RO · None · 0short read (RLAST early)
error_1_rlast_not_set [4]RO · None · 0long read (RLAST never set)
error_2_r_no_match_ar [8]RO · None · 0spurious R (no outstanding read)
error_3_r_to_ar_timeout [12]RO · None · 0R took too long
error_4_arvalid_to_arready_timeout [16]RO · None · 0AR stuck reaching fabric
cfg_rsp_threshold @0x14 (0x214)timeout [11:0]RW · None · 0ticks → read response timeout
cfg_1 @0x18 (0x218)axi_rresp [1:0]RW · None · 0x2injected RRESP — 2'b10 SLVERR, 2'b11 DECERR
enter_isolation_mode_on_rsp_error [2]RW · None · 0isolate on AXI error response
enter_isolation_mode_on_long_rsp [3]RW · None · 1isolate on over-length response
enter_isolation_mode_on_short_rsp [4]RW · None · 1isolate on under-length response
enter_isolation_mode_on_spurious_rsp [5]RW · None · 1isolate on unexpected response
error_data_0 @0x1c (0x21c)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
error_data_1 @0x20 (0x220)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
error_data_2 @0x24 (0x224)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
error_data_3 @0x28 (0x228)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
error_data_4 @0x2c (0x22c)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
error_data_5 @0x30 (0x230)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
error_data_6 @0x34 (0x234)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
error_data_7 @0x38 (0x238)val [31:0]RW · None · 0xdeadbeefdummy return AXI_RDATA after error
cfg_rsp_tick @0x3c (0x23c)timeout [31:0]RW · None · 0AXI cycles per response-timeout tick
cfg_req_threshold @0x40 (0x240)timeout [11:0]RW · None · 0ticks → read request timeout
cfg_req_tick @0x44 (0x244)timeout [31:0]RW · None · 0AXI cycles per req-timeout tick
cfg_clear @0x48 (0x248)errors [0]RW · PulseOnW · 0write clears sticky error flops
cfg_isolation_enter @0x4c (0x24c)count [15:0]RW · None · 0cycles before isolation; 0 disables
cfg_auto_iso @0x50 (0x250)err_resp [0]RW · None · 0auto-isolate on error-code response
long_resp [1]RW · None · 0auto-isolate on over-length response
short_resp [2]RW · None · 0auto-isolate on under-length response
spurious [3]RW · None · 0auto-isolate on spurious response
timeout [4]RW · None · 0auto-isolate on a timeout event
sta_pillm_0 @0x54 (0x254)empty [31:0]RO · None · 0xffffffffFIFOs for IDs 0..31 empty status
sta_pillm_1 @0x58 (0x258)empty [31:0]RO · None · 0xffffffffFIFOs for IDs 32..63 empty status
sta_pillm_2 @0x5c (0x25c)empty [31:0]RO · None · 0xffffffffFIFOs for IDs 64..95 empty status
sta_pillm_3 @0x60 (0x260)empty [31:0]RO · None · 0xffffffffFIFOs 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 · 0number of linked-list pops
sta_state @0x74 (0x274)empty [2:0]RO · None · 0bit 2: short_resp_state, bits 1:0: iso_state

Bundle spare (base +0x900)

reg @off (abs)field [pos]acc · resetrole
all_zeros_0 @0x0 (0x900)val [31:0]RW · 0x00000000scan / bus-integrity read-back constant
all_zeros_1 @0x4 (0x904)val [31:0]RW · 0x00000000scan / bus-integrity read-back constant
all_ones_0 @0x8 (0x908)val [31:0]RW · 0xffffffffscan / bus-integrity read-back constant
all_ones_1 @0xc (0x90c)val [31:0]RW · 0xffffffffscan / 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:

idxnamemeaning
[8]isolation_sm_linkdown_detectedlink-down
[9]isolation_sm_flr_detectedFunction-Level Reset
[10]isolation_sm_sbr_detectedSecondary Bus Reset
[11]isolatio_sm_nsm_axi_timeout_detectedNSM AXI Timeout
[12]isolation_sm_nts_axi_timeout_detectedNTS AXI Timeout (the qos_prot NTS sibling)
[13]isolation_sm_pir_detectedPacific-Initiated Reset
[14]isolation_sm_isolation_mode_enterisolation entered
[15]isolation_sm_isolation_mode_exitisolation 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 spelled isolatio_sm_nsm_axi_timeout_detected in the shipped YAML — missing the trailing n in "isolatio". It is the only index in [8..15] using the misspelled isolatio_sm prefix; [8..10] and [12..15] all use the correct isolation_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.enable auto-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. The peb_intc → Q7/GIC vector for intr_peb_nsm_axi_timeout is 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_IO blocks (_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):

interfacebase
IO_PCIE_A_NSM0x020008016017000
SE_0_PCIE_S0_NSM0x020008016097000 (S1 …117000, stride 0x80000)
IO_PCIE_U_NSM0x020008016D97000
PEB_PCIE_M_NSM0x02000801E017000
PEB_NSM0x02000801E097000

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 — sprot directory ≠ instantiation container. Although nsm.json lives under csrs/sprot/, NSM is not inside the FIS_0_SPROT container that holds amzn_remapper + qos_prot; it is a standalone PEB_APB_IO leaf (the pkl-intc-sprot-security.md census places it at AMZN_PEB_NSM offset 0x11c000 inside amzn_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_IO blocks × 14 interfaces). The sibling pkl-intc-sprot-security.md reports 24 for NSM (4 SENG × 3 dies × 2 apertures), counted from the Maverick al_address_map_db.pkl under 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 ≡ maverickbyte-identical (md5 ca82c156…). All share SizeInBytes 0x1000, AddrWidth 12, DataWidth 32, InterfaceType APB, bundles {control, wr, rd, spare}, reg counts 3/17/26/4, 74 bitfields, identical field names/positions/access/ descriptions.
  • cayman vs sunda — exactly one divergence: rd.sta_pillm_0..3 ResetValue (Cayman 0xffffffff, Sunda 0x0); wr.sta_pillm_0 reset = 0x0 in 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.json itself 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 the pkl-intc-sprot-security.md WALL, 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}aw is the only pulse- clear in the JSON; w/ar are most likely intended pulse-clear too (§4d). [MED]
  • bypass.enable reset 0x1 read 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 bypass auto-clear on isolation-exit, are inferred from field names + the recovery note. [MED]
  • peb_intc → Q7/GIC vector for intr_peb_nsm_axi_timeout is not in the shipped schema. [LOW]