Skip to content

Network Model

The physical shift-topology graph and its node types; see also Nested SIB networks and Multi-arm ScanMux for how these compose.

warptap.icl_model

Pure ICL (Instrument Connectivity Language) data model (implementation_plan.md §7 Stage 4, §3.2). Two genuinely separate structures, confirmed independently by real ICL sources and by Honza255/icl_parser's own design:

  1. :class:PhysicalGraph: the physical shift-topology graph. What actually sits on the scan path, in TDI-to-TDO order, and which SIB gates which nested segment.
  2. :class:ModuleInstance: the module-instantiation tree. Pure lexical containment, used only for dotted-address resolution (top.wrapper.instrument), never for shift-order math. A SIB's own instance name normally does NOT appear in the dotted address of the instrument it gates -- confirmed against real ICL examples that SIB and instrument are siblings under the same enclosing module, not parent/child.

No networkx dependency: real ICL's ScanMux ... SelectedBy <expr> grammar permits arbitrary multi-arm, multi-bit combinational selects (a real fixture examined this session had an 11-arm, 4-bit-select mux), which is why icl_parser reaches for a graph library -- but every arm of a :class:ScanMuxNode (this project's own N-arm generalization of the binary self-select :class:SibNode) still nests its own independent, non-shared sub-chain, so the whole physical graph stays a pure tree (no cross-branch sharing, no cycles) regardless of how many arms a mux has. A plain NamedTuple tree remains sufficient, matching this codebase's existing no-dataclasses convention (bsr_plan.BsrCell/BsrPlan).

PhysicalGraph

Bases: NamedTuple

SibNode

Bases: NamedTuple

One SIB plus everything it gates (§3.2 structure 1). Exactly one of instrument/ nested is ever populated: a leaf SIB gates one instrument directly; a hierarchy SIB (instrument=None) gates a nested sub-network instead, recursively -- real IEEE 1687 (a SIB's fromSO binds to either an instrument's SO or another SIB's SO, never both). :func:validate_physical_graph enforces this invariant, plus global sib_name uniqueness across the whole tree, on every tree this codebase constructs.

nested widened to tuple["ChainSlot", ...] for the multi-arm ScanMux feature -- annotation-only, no runtime behavior change (no mypy in this repo, and NamedTuple fields aren't runtime-type-checked): every tree built before ScanMuxNode existed still only ever puts SibNodes here.

ScanMuxNode

Bases: NamedTuple

An N-arm generalization of :class:SibNode's binary self-select splice -- real IEEE 1687 ScanMux ... SelectedBy <expr> { <value> : <arm>; ... }. Exactly one arm is ever physically live at a time, chosen by comparing the mux's own select_width-bit self- select register against each arm's claimed value(s); a value matching no declared arm is implicit bypass, directly generalizing a closed :class:SibNode's own never-modeled "closed" state.

select_width is always caller-specified, independent of len(arms) -- a real select register is often an existing architectural register reused as a selector, not necessarily sized to exactly fit the arm count (the vendored mux_3 fixture itself demonstrates this pattern: an 11-arm mux selected by a 4-bit field, wider than len(arms) strictly needs). arms must have at least 2 entries (validated) -- a 1-arm mux isn't a real mux.

ScanArm

Bases: NamedTuple

One arm of a :class:ScanMuxNode -- the value(s) of the mux's own select field that route to it, plus exactly one of instrument/nested, the same leaf-xor-hierarchy invariant :class:SibNode enforces (and for the same reason: real IEEE 1687 has a ScanMux arm bind to either one instrument or one further SIB/mux, never both).

values is a tuple, not a single int, to stay forward-compatible with real ICL's multi-value catch-all-arm form (e.g. a real vendored fixture's own mux_3 mapping 6 distinct values to one destination arm) without a future breaking change to this field's shape -- but v1 requires len(values) == 1 everywhere an arm is actually consumed (this module's own :func:validate_physical_graph, plus RTL parameter derivation, insertion, retargeting, and ICL emit/import), so the behavior stays single-value-only until a later feature deliberately relaxes that one check.

InstrumentNode

Bases: NamedTuple

InstrumentDirection

Bases: Enum

Which way an instrument's bits move real design data (implementation_plan.md §7 Stage 9). One direction per instrument, not per-bit and not real ICL's bidirectional DataInPort+DataOutPort-on-one-TDR shape -- every real signal this project binds so far is cleanly either read-only status or write-only control, never both.

SignalBinding

Bases: NamedTuple

One instrument bit's real host-net binding: bit bit of top-level port port_name on the module being inserted into. An InstrumentNode with no signal_bits has no real binding -- v1's original stub behavior, unchanged.

Alias

Bases: NamedTuple

A named, contiguous sub-range within an instrument's own width -- real ICL's Alias <name>[hi:lo] = <reg>[hi:lo]; construct (confirmed real, e.g. a real fixture's own Alias okay = DO[0];/Alias mode[3:0] = DI[6:5],DI[3:2];). v1 scopes this to a single contiguous range referencing one instrument's own register -- real ICL's concat-of-multiple-sources Alias form (multiple comma-joined ranges in one alias) is not modeled here; no real caller needs it yet.

ModuleInstance

Bases: NamedTuple

One node in the module-instantiation tree (§3.2 structure 2) -- pure lexical containment for dotted addressing only. v1's tree is flat by construction (every instrument a direct sibling child of the root), matching the confirmed sibling-addressing convention, not a simplification of some deeper structure.

ICLAddressError

Bases: WarptapError

Raised by :func:resolve_dotted_address when a path segment doesn't resolve -- matches BsrInsertError's convention of naming the exact bad input rather than returning None.

resolve_dotted_address

resolve_dotted_address(root: ModuleInstance, dotted: str) -> ModuleInstance

Walk root's children by each dot-separated segment of dotted in turn. dotted does not include root.name itself -- it names the path below root, matching how a real ICL dotted address is relative to the enclosing top module.