git:20261020.334f4d7 to git:20261020.fd49c31

16 added, 0 removed. Audit A to A.

---
name: testbench-gen
description: "Generate a PROFESSIONAL, high-coverage testbench for a design directly from its Phase-1 L-docs — the Phase-2 'Simulation' step (canonical flow Step 4). Use whenever a design needs functional verification: derive the interface (L1/L9), clock/reset (L8/L9), a reference model, a scoreboard, functional coverage, and assertions, then run cocotb. Program-first: the deterministic generator `professional_tb_gen.py` (MCP `eda_professional_tb`) does the derivation; this skill is the judgment layer for filling a non-closed-form reference model and reviewing coverage. Triggers on: 'write a testbench', 'generate testbench', 'verify this design', 'functional verification', 'coverage', '幫我寫 testbench', '產生 testbench', after Phase-2 RTL is authored and before Phase-3."
---
# testbench-gen — deterministic professional testbench generation
The canonical flow's **Step 4 (Simulation)** derives a professional, high-coverage
testbench from the Phase-1 layers — never a hand-waved skeleton. The load-bearing
work is a PROGRAM: `programs/professional_tb_gen.py` (exposed as MCP tool
`eda_professional_tb`). This skill is the judgment layer around it.
## What the program does (program-first)
`professional_tb_gen.py <project>` reads the project's `phase1/generated_docs/L*.json`
and emits, under `phase2/stage1/sim_professional/<top>/`:
- `tb_<top>.py` — a **cocotb** testbench: clock/reset from L8/L9, a reference
model, a SCOREBOARD, functional coverage (cocotb-coverage covergroups), and a
directed-corner + constrained-random stimulus loop.
- `<top>_coverage_model.json` (an **L28** coverage model), `<top>_assertions.sva`
(an **L29** assertion set), a cocotb `Makefile`, and `verification_plan.json`.
It **combines open-source** frameworks rather than re-inventing them: **cocotb**
(TB base) + **cocotb-coverage** (functional coverage + constrained-random) +
**Verilator/Icarus** (sim) + an **SVA bind** for Verilator/SymbiYosys. Add
`cocotb-coverage` to the runtime (see mcp-eda/INSTALL_GUIDE.md).
## The reference-model strategy — 3 tiers (honest, never a vacuous pass)
1. **Closed-form** (arithmetic primitives `c = a OP b`): reuses
`arith_oracle_tb_gen.compute_golden` — the single source of truth.
2. **Bounded-latency streaming scoreboard** (serial datapaths — one parallel
operand + a 1-bit serial operand + a 1-bit serial result, e.g. a bit-serial
multiplier): the scoreboard AUTO-DERIVES the output `(latency, bit_order)`
from a calibration vector, then checks every vector against the closed-form
reference. **This closes the serial-datapath DEFER** that `arith_oracle_tb_gen`
punts on entirely (proven: spm bit-serial multiplier, 208/208 vs
`(x*y) mod 2^N`, `order=lsb latency=1` auto-derived).
3. **Reference hook** (classes with no closed form — CPU/SoC/DSP): the generated
TB emits a clearly-marked `reference_model` hook that **`TestSkip`s until
filled** — NEVER a silent vacuous pass. The judgment layer (this skill /
spec-to-refmodel) fills it from L10 vectors or a spec-derived model, with the
program-vs-AI cross-check (dual-track convergence).
## When YOU (the judgment layer) act
- **Fill a Tier-3 reference model**: read the L-docs (L2 function, L3 protocol,
L10 vectors) and author the Python reference the hook needs — then re-run so
the scoreboard is real. This is the only place an LLM is needed; the interface,
clock/reset, coverage bins, and (for Tier-1/2) the reference are all derived by
the program.
**WRITE IT TO `expert_reference_tb.py`, NOT TO `tb_<top>.py`.**
```
phase2/stage1/sim_professional/<top>/expert_reference_tb.py
```
`tb_<top>.py` is REGENERATED by `professional_tb_gen` on every Step-4 run, so
an oracle written there is deleted by the very re-run the gate asks for —
`cpu_functional_oracle_waiver_check` says "fill … with the testbench-gen
expert fallback and re-run Step 4", and that loop has no fixed point if you
edit the generated file. The sibling `expert_reference_tb.py` is deliberately
stored apart so regeneration cannot erase your work;
`professional_tb_gen._load_expert_reference_tb` reads it and promotes the run
to `dut_kind=expert_reference`, which is what makes the Phase-2 runner
actually launch cocotb for an otherwise-generic class.
For the loader to ACCEPT it (it rejects placeholder shapes before a simulator
is launched), the file must:
| requirement | why |
|---|---|
| a `@cocotb.test()` coroutine that OBSERVES `dut` and carries a real `assert` | a testbench that never reads the DUT is not an oracle |
| print `PROFESSIONAL_TB PASS` from a passing check | the marker the Phase-2 runner greps for |
| contain no `TestSkip` | a skipped hook is the unfilled state |
The JUnit is still the authority: the run is credited only on
`tests>0, passed>0, failures==0, errors==0`. The marker never credits a case
on its own.
*Measured (2026-09-02, a subservient × gf180mcuD acceptance run): the whole
convention was named in exactly two files — `professional_tb_gen.py` and its
own test. No gate message, no skill and no doc mentioned it, so an agent
following the gate's own instruction wrote a full 7-oracle testbench into
`tb_<top>.py` and watched the next Step-4 run delete it.*
- **Review coverage**: after a run, read `coverage_<top>.xml` + the L28 model;
if functional bins are unhit, add directed vectors or tighten the
constrained-random constraints. Loop until the closure policy (functional bins
100% + code coverage targets) is met.
- **Formalise assertions**: the L29 SVA carries reset→known-state + protocol
handshake properties deterministically; L16 must/shall prose is emitted as
`TODO(spec-to-assertion)` stubs for you to formalise (never fabricated as a
passing SVA).
### Skill: Software-visible field profiles for reference models
**Pattern**: An instruction extension or interface selection is treated as proof of every software-readable or writable field, so a reference model confuses internal saved state with the selected software interface.
**When to apply**: Authoring a Tier-3 model around an implementation that exposes a documented subset of architectural or interface state.
**What to do**: First record the declared scope, selected IP revision and pinned public capability sources, then distinguish software read masks, write permissions and side effects from internal state. Compare only source-supported fields while preserving transaction widths, timing, full byte commits and unknown-value checks. Check an internal saved state through its specified observable effect when software readback is not promised. An unsupported or ambiguous requirement remains `NOT_MEASURED` and cannot be silently removed from required acceptance. If a newly reviewed profile narrows an earlier oracle, preserve the old oracle and counterexample and label the new scope explicitly; never choose masks or goldens from DUT traces. Retain positive, fault-specific negative and byte-identical reverse controls.
**Worked pattern** (anonymized): An interrupt implementation may retain a saved enable internally for return behavior while exposing only the current enable through software reads. Test the guaranteed read field and the specified return effect independently; do not invent read or write access to the saved field. A second eventual interrupt alone does not prove that pending state survived masking: use a discriminating countercontrol or keep that property unmeasured.
**Why this is GENERAL**: The same distinction applies to processor status fields, peripheral register views and protocol capability subsets. Instruction or interface membership, internal state and software access are separate claims; actual native/JUnit evidence remains scoped to the tested fields and effects.
_Captured by benchmark-enhancement-capture 2026-10-15._
## Optional reset-retention byte stimulus
When a retention test needs a designer-chosen memory stimulus, declare
`verification_stimuli.reset_retention.memory_preload` in the project's
`plugin_output/declaration.json` through `spec_declaration_emit --from-json`
or `--set`. This verification choice is separate from the SRAM timing contract.
It is a non-empty list of `{"address": 0, "byte": 165}` entries: exactly those
two keys, unique addresses within the selected memory depth, and integer byte
values 0..255. Boolean, string and floating-point values are refused.
`testbench_gen.emit_unit_tbs` applies the bytes after initializing every memory
location and before observing either reset arm. It preserves every rising-edge
write, the pending first-edge check, all-byte comparisons and the 64-cycle
activity budget. Absent preload keeps the stock seed; malformed preload cannot
credit actual-byte retention. A chosen stimulus alone is never execution
evidence: absent non-idempotent pending activity remains NOT_MEASURED.
## Declared serial arithmetic scenarios
The existing unit producer can generate operand-boundary, consecutive-transaction
and synchronous-reset scenarios from the case's own L10 scope. It requires the
selected serial function, bit order and settled observation offset, plus
`transaction_rearm`, `rearm_reset_cycles` and
`parallel_hold=through_final_product_bit`. Supported rearm is `clocked_reset`,
or `continuous` when the settled offset is zero. Unmodeled input roles, clock
domains, reset styles and ambiguous scopes remain unmeasured.
The scenario oracle uses independent modulo goldens at one declared window; it
does not search DUT alignment to choose an expected answer. It exercises all
selected corner pairs, the stated transaction population, early/middle/late
interruptions with single/multiple reset holds, and between-edge reset pulses.
A row listing multiple supported families executes each family in the same
native case, with separate actual populations and an aggregate comparison total.
Generated scenario credit requires the current native comparison population
and unchanged input, DUT, declaration, TB and transcript identities. Generic
generated arithmetic remains insufficient for scenario credit. Authored and
delivered testbenches retain their original priority.
`latency_cycles_origin=settled_edge_offset` is the normal CR-5 convention.
Explicit legacy `pre_nba_sample_offset` is normalized once. Native manifests
carry their origin; a bare measured integer is not origin evidence. Neither
convention relaxes an incorrect declared window.
## Capture
Any recovery here (a new reference-model pattern, a new coverage-derivation rule,
a streaming-alignment case) is captured per `benchmark-enhancement-capture`.
Route full-stack professional-testbench recovery to `professional_tb_gen.py`.
Route an exact-name L10 `.v` / `.sv` unit oracle from
`testbench_gen.run_unit_tbs` through the canonical
`phase2.l10_unit_tb.testbench_gen.run_unit_tbs` entry: Bucket A belongs to
`testbench_gen.py` with `_l10_execution.py` as its receipt owner, and Bucket B
belongs in this skill. Do not borrow the full-stack route for a unit-TB record.
For L10 unit-oracle recovery, keep the authored TB bytes and bind the complete
source closure needed to reproduce its expected result. A wrong expected value
or wrong stimulus must still compile and then fail the native comparison; the
restored original bytes must pass through the normal producer and write a fresh
execution receipt. A broad reference-suite PASS cannot credit a named vector it
did not execute. If the gap is in a forked simulator (Verilator/Icarus/cocotb),
also emit a **Bucket-T** forked-tool item.
## Gaps to grow into (from the 2026-07-11 verification research)
- Fill the currently-empty **L22 verification plan** fields (coverage_goals /
formal_properties / regression_matrix) and wire them to L28/L29.
- Add **L30 stimulus/constraints** (constrained-random weights, sequences).
- Widen code coverage to FSM-transition / expression / condition (Verilator
`--coverage-user`) and gate sign-off on it.
- pyuvm structural env + riscv-dv for CPU-class constrained-random.
## Compliance gate (mandatory)
After producing your output, save it to a file and run:
```bash
python3 plugins/vibe-ic/_shared/skill_compliance_check.py \
--requirements plugins/vibe-ic/skills/testbench-gen/compliance.yaml \
<your_output_file>
```
Exit 0 = PASS, exit 1 = FAIL with specific missing elements listed.
`compliance.yaml` in the corresponding skill directory enumerates
every required element of your output: section headers, metadata fields,
handoff lines, tool invocations.
**Your task is not complete until the audit returns PASS.** Missing
elements are the single largest source of skill-execution non-determinism
across different agents.
+ ### Skill: Legal delayed acknowledgement withdrawal with public observation
+
+ **Pattern**: Nominal service can complete while leaving a source-permitted request-low acknowledgement tail untested.
+
+ **When to apply**: Native coverage identifies that branch and the original interface and reset prose permit bounded delayed withdrawal with stable payload.
+
+ **What to do**: Read the complete handshake and reset obligations before choosing a finite transaction schedule; do not transfer a reset-only timing requirement to every transaction. Admit the normal design class and expert-reference contract first. Give one owner to the public service coroutine, use a separate public-pin monitor to check request remains low and acknowledgement, validity and payload remain stable throughout the selected tail, then withdraw acknowledgement and restore the existing service even on failure. Check subsequent progress against an independently specified result from the input, never an expected value inferred from the DUT. Keep the original vector and control populations, their order and goldens intact; append a named case in the persistent expert source with its complete source closure. A wrong independent expected result must compile and reach a real native comparison failure; exact restoration must pass. A zero-tail control may isolate coverage causality but does not qualify delayed-tail behavior. Hand the complete source back to the normal professional generator and native/JUnit consumers for fresh full-population measurement.
+
+ **Why not Bucket A**: The interface prose permits several finite service schedules and does not uniquely select an observable progress witness. Those choices require source-bound engineering judgment; existing programs retain source, population, execution and coverage validation.
+
+ **Worked pattern** (anonymized): A delayed-tail case passed with a separate public monitor and an input-derived functional golden. Changing one expected result caused a native comparison failure and restoring the exact source passed. An identified native branch counted three, zero and three in delayed-tail, zero-tail and restored arms without changing the coverage denominator. A driver delivery count alone did not prove internal one-time consumption.
+
+ **Why this is GENERAL**: Applies to request/acknowledgement interfaces with source-supported payload stability, legal bounded timing and independently observable progress. It establishes the measured public behavior and selected branch, not complete internal consumption, clock-domain signoff or whole-design qualification.
+
+ _Captured by benchmark-enhancement-capture 2026-10-21._
+