Skip to content

Quickstart

pip install warptap

warptap also needs a real Yosys: it shells out to it for netlist ingest and re-emission, and never bundles it. If nothing is found on PATH, set WARPTAP_YOSYS_CMD to point at any Yosys-compatible executable, or install the yowasp-yosys WASM build as a fallback.

The core pipeline

Insert a JTAG/IJTAG test-access network into your design, then either drive it with PDLInterpreter and emit a real ATE pattern file, or hand the inserted netlist on to your own normal synthesis flow.

The example below mirrors a real integration shape: a generator (e.g. an MBIST wrapper generator) produces RTL with real control/status ports, then warptap wraps a subset of them with JTAG/IJTAG access before synthesis.

1. Declare the instruments

One InstrumentSpec per real signal you want write/read access to: mix WRITE (control) and READ (status) freely in one network. Each one's signal_bits names the real host port (and, for a multi-bit signal, the specific bit) it binds to.

from warptap import InstrumentSpec, InstrumentDirection, SignalBinding

specs = [
    InstrumentSpec(
        "self_repair_start", width=1, capture_value=0,
        direction=InstrumentDirection.WRITE,
        signal_bits=(SignalBinding("self_repair_start"),),
    ),
    InstrumentSpec(
        "self_repair_busy", width=1, capture_value=0,
        direction=InstrumentDirection.READ,
        signal_bits=(SignalBinding("self_repair_busy"),),
    ),
]

2. Insert the network

insert_test_access ingests your RTL, inserts the TAP plus the IJTAG scan network the specs above describe, and hands back synthesizable Verilog plus everything needed to drive it. From here, feed the returned Verilog into your own normal synthesis flow instead of the original sources. See the Insertion reference for the exact signature.

from warptap import insert_test_access

inserted_verilog, graph, root = insert_test_access(
    ["mem_subsystem_mbist.sv"], "mem_subsystem_mbist", specs,
)

The TAP's IDCODE defaults to a placeholder; pass idcode_value= (32 bits, bit 0 set) to use your own, and give to_bsdl the same value.

3. Drive it

PDLInterpreter is a small state machine over four commands: iTarget selects which instrument the next command addresses, iWrite/iRead queue a value to write or an expected value to check, and iApply actually emits the scan operations that retarget the network to that instrument and shift the value through. iRunLoop advances time: by default in Run-Test/Idle (-tck), or, given a real functional-clock port name, by pulsing that clock directly instead (see System-clock pulsing).

pdl = PDLInterpreter(graph, root)
pdl.iTarget("self_repair_start")
pdl.iWrite(1)
pdl.iApply()
pdl.iRunLoop(10)                # let it settle
pdl.iTarget("self_repair_busy")
pdl.iRead(1)
pdl.iApply()

4. Emit a pattern file

Every op PDLInterpreter recorded is sitting in pdl.program. It holds data-register scans only, so put the instruction load in front: the network moves only while EXTEST is loaded, and every reset loads IDCODE. Then render it as whichever ATE format you need:

from warptap import select_instruction, to_svf
from warptap.tap_model import OPCODE_EXTEST

print(to_svf(select_instruction(OPCODE_EXTEST) + pdl.program))

to_stapl/to_stil render the identical pdl.program as STAPL/STIL instead. to_icl describes the inserted network's own topology as real ICL text. Every exception this library raises subclasses WarptapError. See the Pattern Export and ICL reference pages for details.

Next

  • Guide for nested networks, ScanMux, field addressing, import, clock pulsing, and faultflow retargeting.
  • API Reference for the full public surface.