This page gives a quick developer reference for the SCOAP API surface in OpenTestability.
Purpose
SCOAP computes structural testability metrics:
CC0: controllability to logic 0CC1: controllability to logic 1CO: observability to outputs
These metrics are used directly in DFT analysis and test-point selection workflows.
Primary package
opentestability.core.scoap
Common imports
from opentestability.core.scoap import run
from opentestability.core.scoap.accelerated import build_scoap_accelerated
Main entry point
run(input_filename, output_filename, json_flag=False, reconvergence_data=None, reconvergent_roots=None)
Runs SCOAP end to end. Internally delegates to build_scoap_accelerated().
Key arguments:
| Argument | Meaning |
|---|---|
input_filename |
parsed netlist input |
output_filename |
output report/metrics file |
json_flag |
output JSON when True |
reconvergence_data |
optional reconvergence metadata |
reconvergent_roots |
fanout roots for reconvergence-aware processing |
Accelerated engine
The runtime uses Cython-compiled kernels (core/scoap/_kernels.pyx) via build_scoap_accelerated().
The pure-Python build_controllability_sequential and build_observability functions are kept as
reference oracles for testing only — they are not the runtime path.
from opentestability.core.scoap.accelerated import build_scoap_accelerated
ctrl, obs = build_scoap_accelerated(nets, inputs, outputs, gates, method="topo")
# ctrl: {"CC0_<sig>": int, "CC1_<sig>": int, ...}
# obs: {"CO_<sig>": int, ...}
# math.inf marks a signal that is uncontrollable (CC0/CC1) or unobservable (CO)
# — e.g. a primary input with no fan-out into the combinational core after the
# full-scan cut. It is a real, unbounded metric, not a "not computed" marker.
Kernel methods (all produce identical results — same least fixpoint):
| Method | Description | When to use |
|---|---|---|
"topo" (default) |
controllability in one topological pass; observability as reverse-topo fixpoint | always — fewest iterations |
"fixpoint" |
gate-index-order fixpoint loop | equivalence testing against reference |
"parallel" |
Cython OpenMP prange chaotic relaxation |
SoC-scale circuits on multi-core hosts |
Minimal examples
Basic run:
from opentestability.core.scoap import run
run(
input_filename="circuit.txt",
output_filename="scoap_results.json",
json_flag=True,
)
Direct accelerated-engine call:
from opentestability.core.scoap.accelerated import build_scoap_accelerated
ctrl, obs = build_scoap_accelerated(nets, inputs, outputs, gates)
print(ctrl["CC0_N22"], ctrl["CC1_N22"]) # c17: (5, 4)
JSON output (json_flag=True)
run(..., json_flag=True) writes a metrics array — one entry per primary
input and per gate — next to the requested output file. Each cc0, cc1, and
co field is encoded as follows (JSON has no native infinity):
| Value | Meaning |
|---|---|
| a number | the computed SCOAP cost |
"inf" (string) |
infinite metric: uncontrollable (cc0/cc1) or unobservable (co). A real, unbounded value — parse it back with float("inf") |
null |
metric genuinely absent from the result — a gap that should not occur; distinct from "inf" so it is not silently read as “unobservable” |
For a sequential design such as s27, the constants and the clock/reset control
inputs drive nothing after the full-scan cut, so their co is "inf". (Earlier
versions emitted null for these, conflating “unobservable” with “not
computed”.)
Notes for developers
- Input must be a Yosys JSON netlist.
- When benchmarking, keep the
methodfixed for fair comparisons. - The Cython kernel must be compiled (
pip install -e .) before import. Iffrom opentestability.core.scoap import _kernelsfails, runpip install -e .in your Linux/macOS shell (or usenix develop, which compiles it for you).