TPI inserts observation and control logic so hard-to-test internal signals become easier to control and observe.
What gets added
Depending on mode and selected points, the generated design can include:
- control inputs such as
test_modeandtp_ctrl_* - observation outputs such as
tp_obs_* - global enable ports such as
tp_obs_enandtp_ctrl_en
Minimal workflow
python3 opentest analyze_and_add_tp -i <design.json> --scoap -t 50 -m 10 -v
Key parameters
| Option | Meaning | Typical value |
|---|---|---|
-t, --threshold |
Minimum testability threshold for selecting weak signals | 50 |
-m, --max-points |
Maximum number of test points to insert | 10 |
force_type |
Force only observation or only control insertion | Python API only (insert_testpoints_yosys(force_type=...)); not yet CLI-exposed |
--dont-touch, --dont-touch-file |
Nets or cell instances to leave alone | see below |
--area-budget |
Cap on total test point cell area | see below |
--obs-share |
Observation points XORed onto each output port | 1 (off) |
-v, --verbose |
Write detailed analysis logs and reports | recommended |
Constraints
These apply after candidate selection, so every --tpi-algorithm respects them.
Don’t-touch. Each pattern is a shell glob, case-sensitive, and is also
compared literally first, so bus[3] means that exact bit. Plain patterns match
net names, including every alias a net has; cell:<glob> matches cell instance
names, e.g. cell:u_ip.* for a flattened IP block. A net is excluded when one of
its names matches, or when a matching cell drives or reads it. Excluded nets are
dropped before -m is applied, so they never use up the point budget, and each
shows up in the report with reason Don't-touch. Use --dont-touch-file for a
long list: one pattern per line, # comments allowed.
Area budget. --area-budget caps the summed area of every cell TPI inserts,
in the Liberty area units of the chosen --tech. Candidates are taken in rank
order; one that would overrun the budget is skipped (reason Exceeded area
budget) and cheaper points further down can still go in. Costs per point:
observation = one buffer; force-1 control = one OR; force-0 control = one AND,
plus the shared inverter on the first one; legacy mux control = one mux (plus an
inverter on PDKs whose mux inverts). Given --area-budget without -m, the
budget alone limits how many points go in. The report’s area.used is always
filled in, budget or not.
Observation sharing. --obs-share N XORs up to N observation points onto one
output port (tp_obs_grp, tp_obs_grp_1, …) through a balanced tree of 2-input
XOR cells, in place of one buffered port per point. N points then cost N - 1 XORs
and one pin, against N buffers and N pins. A group left with a single point gets a
normal buffered port. The trade-off is parity aliasing: fault effects reaching an
even number of nets in the same group at once cancel at the port. With sharing on,
the _tp_cells.json sidecar also lists the XOR cell (FaultFlow gate type XOR2).
Simulation checklist
Drive all DFT/test inputs in your testbench:
reg test_mode;
reg tp_ctrl_0;
reg tp_ctrl_1;
initial begin
test_mode = 1'b0;
tp_ctrl_0 = 1'b0;
tp_ctrl_1 = 1'b0;
end
Also ensure your circuit reset sequence initializes all state elements.
Expected behavior
- With
test_mode = 0, functional outputs should match the original design. - Observation outputs should mirror internal nodes without destabilizing normal logic.
- Verbose mode should produce per-run logs and reports under
results/.
Troubleshooting
- If inserted ports show
X, check that every new test input is explicitly driven. - If TPI inserts fewer points than expected, reduce threshold or increase max points.
With
-v, the rejected list says why each point was left out (don’t-touch, area budget, and so on). - If the input is rejected, confirm it is a valid Yosys JSON netlist.