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: the 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).