Skip to content

IEEE 1500 wrapper

An IEEE 1500 wrapper and its wrapper serial port (WSP), as a model, as ICL, as PDL and as an integrity program. See IEEE 1500 wrappers for how the pieces fit.

from warptap import (
    Wrapper1500, WbrCell, build_wsp_integrity_program, check_wsp_integrity,
    replay_wsp_program, to_icl_1500, import_icl_1500,
)

wrapper = Wrapper1500(
    name="blk",
    chains=((WbrCell("a", "input"), WbrCell("b", "input")), (WbrCell("y", "output"),)),
)
icl = to_icl_1500(wrapper)                    # ICL the vendored parser accepts
assert import_icl_1500(icl) == wrapper        # warptap's own shape reads back exactly

program = build_wsp_integrity_program(wrapper)
observed = replay_wsp_program(program, wrapper)         # or the values your harness measured
assert check_wsp_integrity(program, observed) == []     # (step, point) pairs that differ

program_json = program.to_json()              # a warptap-wsp-program file, for a tool without warptap

Model

warptap.wsp1500_model

An IEEE 1500 wrapper and its wrapper serial port (WSP), as a model: the registers and the cycle-by-cycle behaviour every other 1500 piece of warptap (the ICL it writes, the PDL it retargets, the integrity program) is checked against.

The wrapper's registers:

  • the instruction register (WIR): a shift stage, WSI into bit 0 and the last bit out to WSO, which shifts on the rising edge of WRCK while SelectWIR and ShiftWR are 1; and an update stage, the instruction in force, which takes the shift stage on the falling edge of WRCK while SelectWIR and UpdateWR are 1. WRSTN at 0 clears the update stage at once: WS_BYPASS, the functional mode. A code no instruction has acts as WS_BYPASS.
  • the bypass register (WBY): one bit, WSI to WSO, which shifts while ShiftWR is 1 and captures 0 while CaptureWR is 1, the WIR not selected; cleared by WRSTN.
  • the boundary register (WBR): the boundary cells, in a serial instruction WSI through them to WSO -- the wrapper chains one after another, each from its head.

An instruction is serial (WS_: the WBR is the data register WSI and WSO reach) or parallel (WP_: the WBR works on the wrapper's own scan ports), and puts the boundary cells in a mode, (INTEST, EXTEST) -- (0, 0) is functional. An input cell holds its flop in INTEST and drives the core 0 in EXTEST; an output cell holds in EXTEST and drives its port 0 in INTEST. In a serial instruction every cell also holds, its flop keeping its value, unless the WIR is deselected and CaptureWR is 1 (the cells that observe in the mode capture) or ShiftWR is 1 (the WBR shifts from WSI): a cell changes only on an event of its own (the 1500 WBR rule), not through the WIR's own shifts and updates.

A step is one tester cycle: the inputs applied, WSO strobed, then WRCK pulses (rises, then falls) if the cycle says so. Values are 0, 1 or None, unknown: every register starts unknown but what WRSTN clears. :class:Wsp1500Chain runs wrappers whose WSI and WSO are chained and whose other controls are shared.

Wrapper1500 dataclass

A wrapper: its boundary cells in chains (each a wrapper scan chain, head first), the WIR's width, its instructions and its port names.

cells property

cells: tuple[WbrCell, ...]

The boundary register in serial order: the chains one after another.

instruction_of

instruction_of(code: int) -> str

The instruction an update stage of code puts in force.

WbrCell dataclass

A boundary cell: the label of the port bit it serves and its side.

WspChainDesign dataclass

Wrappers daisy-chained on a SoC: the SoC's top module, the wrappers from its WSI to its WSO, and pins -- each WSP role's SoC port and whether an inverter sits between it and the wrappers (:data:warptap.icl_emit_1500.ROLES).

WspCycle dataclass

One tester cycle on the WSP: each signal's value (WRCK aside), whether WRCK pulses, and each boundary cell's functional input (its port bit, or the core's value), in serial order.

Wsp1500Sim dataclass

A wrapper's state, stepped a cycle at a time.

instruction property

instruction: Optional[str]

The instruction in force, or None while the update stage is unknown.

outputs

outputs(cycle: WspCycle) -> list[Value]

Each boundary cell's functional output in cycle (to the core for an input cell, to the port for an output cell): 0 where the mode makes it safe, its flop where the mode holds it, else its functional input. A serial instruction's extra holds don't show: they hold only cells the mode keeps safe.

scan_outs

scan_outs() -> list[Value]

Each wrapper chain's scan output: its tail cell's flop.

step

step(cycle: WspCycle, wsi: object = _OWN) -> Value

Apply cycle: WSO at its strobe, returned; then its WRCK pulse. wsi (0, 1 or None) overrides the cycle's WSI -- a chain feeds the wrapper before's WSO.

wso

wso(cycle: WspCycle) -> Value

WSO at the strobe of cycle (before its edge): the WIR's last bit when SelectWIR is 1, else the WBR's last cell in a serial instruction, else WBY.

Wsp1500Chain

Wrappers whose WSI and WSO are chained -- WSI into the first, each WSO into the next WSI, the last one's WSO the chain's -- and whose other WSP controls are the same signals.

outputs

outputs(cycle: WspCycle, cfis: Optional[Sequence[tuple[Value, ...]]] = None) -> list[list[Value]]

Each wrapper's cells' functional outputs at the strobe of cycle.

step

step(cycle: WspCycle, cfis: Optional[Sequence[tuple[Value, ...]]] = None) -> Value

Apply cycle to every wrapper; the chain's WSO at its strobe. Every WSO is read before any edge, so each wrapper shifts in what its neighbour showed. cfis: each wrapper's own functional inputs.

wso

wso(cycle: WspCycle, cfis: Optional[Sequence[tuple[Value, ...]]] = None) -> Value

The chain's WSO at the strobe of cycle, without applying it.

program_cycles

program_cycles(wrapper: Wrapper1500, instruction: str | int) -> list[WspCycle]

The cycles that put an instruction in force from any state -- its name, or any code: WRSTN pulsed, the code shifted into the WIR (last bit first), then updated, then the WSP idle (the WIR deselected).

ICL

warptap.icl_emit_1500

ICL (IEEE 1687) for an IEEE 1500 wrapper and its wrapper serial port, from a :class:~warptap.wsp1500_model.Wrapper1500; and for a chain of wrappers on a SoC.

A wrapper is one ICL module, <name>, beside a stub of its core (<name>_core, whose outputs the wrapper's output cells capture):

  • ScanInterface WSP -- WSI, WSO, ShiftWR, CaptureWR, UpdateWR, SelectWIR (a SelectPort), WRCK and WRSTN (ResetPort, active low) -- and ScanInterface WPP, the wrapper's own scan access: one Chain per wrapper chain, with the wrapper scan enable.
  • The WIR: ScanRegister WIR_SR (WSI in, the shift stage) and DataRegister WIR (the update stage: WriteDataSource WIR_SR, written while SelectWIR is selected, reset to WS_BYPASS), whose values Enum <name>_ops names.
  • WBY, a one-bit ScanRegister that captures 0; one ScanRegister WBR<k> per wrapper chain, the head cell the MSB and the tail the LSB, whose capture a DataMux on the WIR chooses -- a serial instruction's cells capture their port (an input cell) or the core's output (an output cell) unless the mode holds them -- and whose scan input serial selects: WSI (chain 0) or the chain before, in a serial instruction, else the chain's own scan port.
  • WSO: WsoMux, the WIR's last bit while SelectWIR is 1, else WdrMux: the last chain's tail in a serial instruction, else WBY.
  • The core's inputs and the wrapper's outputs through a DataMux on the WIR: 0 where the instruction's mode makes the cell safe, the cell's flop where it holds, else the functional signal.

The scan path of a ScanRegister enters its MSB and leaves its LSB (IEEE 1687, and the vendored parser). A wrapper's WIR shifts its code in last bit first, the first bit shifted in ending nearest WSO, so the value ICL gives an instruction is its code with its bits reversed -- iWrite WIR WS_EXTEST shifts in the same bits the code's last bit first does.

Attributes carry the model (warptap_*), so :mod:warptap.icl_import_1500 rebuilds it exactly; the rest is standard ICL, which the vendored icl_parser checks.

Icl1500Error

Bases: ValueError

A wrapper that can't be described as ICL.

to_icl_1500

to_icl_1500(wrapper: Wrapper1500) -> str

The ICL of one wrapper (see the module docstring), top module wrapper.name.

to_icl_1500_chain

to_icl_1500_chain(wrappers: Sequence[Wrapper1500], *, top: str, pins: Optional[Mapping[str, tuple[str, bool]]] = None) -> str

The ICL of wrappers daisy-chained on a SoC: each wrapper's module, and top with the SoC's own WSP -- pins maps a role (ROLES) to the SoC port that drives it and whether an inverter sits between (default: the wrapper's own port name, direct) -- the wrappers' WSI and WSO chained, WSI at the first and WSO from the last, under the shared controls.

warptap.icl_import_1500

The model back from the ICL :mod:warptap.icl_emit_1500 writes: a :class:~warptap.wsp1500_model.Wrapper1500, or -- for a SoC top that chains wrappers -- a :class:~warptap.wsp1500_model.WspChainDesign.

Like :mod:warptap.icl_import for the SIB network, it takes only warptap's own shape, and says so rather than guess: the model is read from the warptap_* attributes, then written as ICL again, and the text must be that ICL byte for byte. Anything else -- a SIB network, a third tool's wrapper, this ICL with its WIR or WBY edited out or an Enum value changed -- is refused, so a program built from an imported wrapper is built from a wrapper whose ICL is the standard description the vendored icl_parser checks.

Icl1500ImportError

Bases: ValueError

ICL that isn't what warptap writes for an IEEE 1500 wrapper.

import_icl_1500

import_icl_1500(text: str) -> Union[Wrapper1500, WspChainDesign]

The wrapper text describes, or the chain of them: the model read from its attributes, which must write exactly text again.

import_icl_1500_file

import_icl_1500_file(path: Path) -> Union[Wrapper1500, WspChainDesign]

PDL and scan operations

warptap.pdl_1500

PDL (IEEE 1687) on an IEEE 1500 wrapper, or a chain of wrappers on a SoC, retargeted onto the wrapper serial port.

iReset clears the wrappers (WRSTN): every WIR is WS_BYPASS. iTarget sets the wrapper iWrite/iRead address -- u0, u1 of a chain (the instances of :func:warptap.icl_emit_1500.to_icl_1500_chain), or the wrapper's own name -- and a register may be written u0.WIR to name its wrapper too. The registers:

  • WIR, written by an instruction name (WS_EXTEST) or its ICL value (the code with its bits reversed). One iApply scans every WIR of the chain at once, the far wrapper's bits first; a wrapper not written keeps its committed instruction, which must be known (an iReset, or a write). A WIR is not read: its shift stage has no capture.
  • WBY, in an instruction whose data register is the bypass bit; and WBR0, WBR1... in a serial instruction, one per wrapper chain (the ICL registers), each by its value: the head cell the MSB, the tail the LSB. One data scan runs through whatever each wrapper's instruction selects, the far wrapper's first; a register not written shifts in 0, one not read isn't checked.

iApply turns the pending writes and reads into tap_ir scan operations -- ShiftIR for the WIRs, then ShiftDR -- appended to program (:mod:warptap.wsp_play lowers them to cycles); iRunLoop appends idle cycles where it is called. history keeps the statements for :func:to_pdl_1500, and :func:import_pdl_1500 reads them back.

Pdl1500Error

Bases: ValueError

PDL a wrapper can't take, or can't print.

Wsp1500Interpreter

See the module docstring. design: one wrapper, or a chain of them.

iApply

iApply() -> list[Op]

Scan the pending writes and reads; the operations added to program.

iReset

iReset() -> None

Clear the wrappers: WRSTN for a cycle, every WIR WS_BYPASS.

iRunLoop

iRunLoop(count: int) -> None

count idle cycles, where this is called.

to_pdl_1500

to_pdl_1500(history: Sequence[Statement], design: Union[Wrapper1500, WspChainDesign]) -> str

history as PDL text, one statement a line, numbers as sized binary literals.

import_pdl_1500

import_pdl_1500(text: str, interp: Wsp1500Interpreter) -> None

Replay PDL text (:func:to_pdl_1500's subset) on interp.

warptap.wsp_ir

Operations on an IEEE 1500 wrapper serial port that have no TAP counterpart.

The scan operations are :mod:warptap.tap_ir's -- ShiftIR is a WIR scan, ShiftDR a data-register scan, Runtest idle WRCK cycles -- so the PDL layer above produces the same operations whichever access it retargets onto. The one operation a WSP adds is WRSTN: a cycle with the wrapper reset asserted, the only way to a known WIR.

WspReset

Bases: NamedTuple

One cycle with WRSTN low and WRCK still: the wrapper's WIR update stage and WBY are cleared, WS_BYPASS in force.

warptap.wsp_play

Scan operations lowered to cycles on an IEEE 1500 wrapper serial port, and played against the model (:mod:warptap.wsp1500_model).

One row is one tester cycle: the WSP's inputs applied, WSO strobed -- against the value the operation expects there, where it expects one -- then WRCK pulsed, rising and falling (the falling edge is what the WIR's update stage takes).

  • :class:~warptap.wsp_ir.WspReset is a cycle with WRSTN low and no pulse.
  • ShiftIR(bits, tdi) is a WIR scan: SelectWIR and ShiftWR high for each bit -- bit 0 of tdi first -- then one cycle of SelectWIR and UpdateWR. The WIR has no capture.
  • ShiftDR(bits, tdi) is a data-register scan, the WIR deselected: a cycle of CaptureWR, bits of ShiftWR, a cycle of UpdateWR.
  • Runtest(count) is count idle cycles with WRCK pulsing; GotoState has no meaning here and is skipped.

tdo and mask of a scan are the TAP's: bit i of tdo is the WSO the i-th shift cycle should strobe, where bit i of mask (all, without one) says it is checked. cfi gives the boundary cells' functional inputs (each port bit, or the core's output, in serial order) for every cycle.

WspPlayError

Bases: ValueError

An operation a WSP can't play.

WspRow

Bases: NamedTuple

A cycle, and the WSO its strobe should show (None: not checked).

to_wsp_rows

to_wsp_rows(ops: Sequence[object], *, cfi: tuple[Value, ...] = ()) -> list[WspRow]

ops as WSP cycles (see the module docstring).

play

play(chain: Wsp1500Chain, rows: Sequence[WspRow]) -> list[Value]

Run rows against chain; the WSO at each row's strobe.

mismatches

mismatches(rows: Sequence[WspRow], observed: Sequence[Value]) -> list[int]

The rows whose strobe isn't the WSO they expect: an unknown (None) where a value is expected counts.

Integrity program

warptap.wsp_integrity

The integrity program of an IEEE 1500 wrapper serial port: WSP cycles that exercise the WIR, the bypass register and the boundary register, each with the value the model (:mod:warptap.wsp1500_model) expects at every observation point -- the test a production flow runs on the wrapper's own infrastructure, in place of scan ATPG, which holds the instruction still and so can't.

Tests, in program order:

  • reset: WRSTN, then a sentinel pattern through the bypass registers, which must come out N shifts late, N the wrappers of the chain (WBY is one bit, and WRSTN and CaptureWR clear it).
  • wir_length: a sentinel through the WIRs, which must come out as many shifts late as the WIRs are long; the pattern ends WS_BYPASS, so the next test starts from it.
  • code_<bits> (u<k>_code_<bits> in a chain): every code of the WIR, one wrapper at a time with the others in bypass -- an unused code included, which acts as WS_BYPASS -- loaded, then the data register it selects scanned with a sentinel: the bypass bit, or in a serial instruction the boundary register, scanned three times so every cell and the register's length show. Each scan starts with a capture from the ports the program drives (~P, P, P: P alternates, so neighbouring cells differ) and leaves a tail in the register (0, 1, 0), so the second capture meets cells holding 0 against ports P and the third cells holding 1 against ports P: a cell that follows its port when its mode says hold, or holds when it should capture, shows in one of them. Then idle cycles, in which the mode's parallel outputs are observed.
  • wby_hold: WBY loaded with 1 and a WIR scan of zeros, then with 0 and a WIR scan carrying ones -- WBY selected again after each update -- and WSO observed at idle: the bypass bit holds its value through a WIR scan and update.
  • held_update: an update while the WIR is deselected, after the complement of the code in force is shifted into the WIR's shift stage (every bit differs), leaves the instruction in force.
  • capture_update: CaptureWR with the WIR selected doesn't capture the boundary cells.

Every cycle carries what the model expects at WSO, at each wrapped output and at each wrapper chain's scan output (:class:ObservePoint). What the program can't know -- the core's outputs, which an output cell captures -- is no care: the model runs twice, with those at 0 and at 1, and an observation is checked only where the runs agree. A program is JSON, a warptap-wsp-program, which a tool without warptap can read and play.

WspIntegrityProgram dataclass

The steps, their tests and what they drive and observe. design is the model the program was built from (None for one read from JSON).

ObservePoint dataclass

Where the program checks the wrapper: WSO, a wrapped output port (output: the cell index of wrapper wrapper), or a wrapper chain's scan output (scan_out: chain index). name: wso, the port's label, the scan-out port's name -- prefixed u<k>. in a chain.

Step

Bases: NamedTuple

One tester cycle: the cycle, its test, the data input ports driven (in drive order), and the expected value at each observation point (None: no care).

WspIntegrityError

Bases: WarptapError

A program that can't be built or read.

build_wsp_integrity_program

build_wsp_integrity_program(design: Design, *, margin: int = 8, exhaustive_opcodes: bool = True, outputs: Optional[bool] = None, scan_outs: bool = True, drive: Optional[Sequence[str]] = None) -> WspIntegrityProgram

The integrity program of design (see the module docstring). margin: how many sentinel bits each scan carries past the register's length; exhaustive_opcodes: test every code of the WIR, not only the instructions' own; drive: the data input ports the tester drives (default: all; () for wrappers whose ports are a SoC's own nets) -- an input cell on any other captures a live value, no care.

check_wsp_integrity

check_wsp_integrity(program: WspIntegrityProgram, observed: Sequence[Sequence[Value]]) -> list[tuple[int, str]]

The (step, observation point) pairs where observed differs from what the program expects, at a care bit. An unknown (None) where a value is expected counts.

replay_wsp_program

replay_wsp_program(program: WspIntegrityProgram, design: Optional[Design] = None, *, live: int = 0) -> list[list[Value]]

The values at each observation point of a program played on design (default: the program's own), the core's outputs at live: one list per step.

observation_points

observation_points(design: Design, *, outputs: Optional[bool] = None, scan_outs: bool = True) -> tuple[ObservePoint, ...]

WSO, then (outputs; default: for one wrapper, not for a chain, whose block outputs aren't pins) each wrapped output port, then (scan_outs) each wrapper chain's scan output.

drive_ports

drive_ports(design: Design, drive: Optional[Sequence[str]] = None) -> tuple[str, ...]

The data input ports the program drives: each input cell's label, u<k>.- prefixed in a chain, in serial order -- all of them, or (drive) only those named; an input cell the tester doesn't drive captures a live value.