From chipdev-method
Guides behavior model ↔ RTL equivalence checking — sampling point selection (commit/retire vs transaction-edge vs cycle), drive direction (DUT pushes ref), DPI-C probe interface, async event alignment, snapshot-based debugging, and DSL-driven probe generation. Activate when the user explicitly invokes /chipdev-method:align-and-difftest, or asks "怎么 diff cmodel 和 RTL", "behavior 对不上 RTL", "difftest 怎么搭", "what should I sample", "RVVI vs DPI", or "snapshot for difftest", or sets up a difftest infrastructure.
npx claudepluginhub curryfromuestc/dev-guide --plugin chipdev-methodThis skill uses the workspace's default tool permissions.
Use this skill when the user is establishing — or debugging — equivalence
Monitors deployed URLs for regressions after deploys, merges, or upgrades by checking HTTP status, console errors, network failures, performance (LCP/CLS/INP), content, and API health.
Share bugs, ideas, or general feedback.
Use this skill when the user is establishing — or debugging — equivalence checking between a behavior model and an RTL DUT. Do not autoload.
The goal is to give the user a small set of strong defaults that match how the most successful open-source difftest projects work, and to help them avoid the failure modes that absorb teams' Phase 4–5 calendars.
If the user has not yet built a behavior model, redirect to
build-behavior-model. If they don't yet have an alignment contract,
redirect to define-contracts.
When triggered:
Difftest is about closing degrees of freedom. Every "we'll figure that out later" leaves a degree of freedom that becomes a Phase 4 outage. Be specific.
[abstract]Every difftest setup answers four questions. Get them all decided up front.
| Decision | Default for ISA-style | Default for accelerator-style |
|---|---|---|
| Sampling point | Commit / retire boundary | Transaction edge (input + output) |
| Drive direction | DUT pushes ref | DUT pushes ref |
| Cross-language | DPI-C with packed structs | DPI-C, or shared-memory ring if ref is a separate process |
| State alignment | DUT-spec (ref accepts the DUT's view of memory) | DUT-spec |
The rest of this skill explains why these defaults dominate, and what "DUT-pushes-ref" actually entails operationally.
[industry-pattern]Three options. They are not interchangeable.
Compare ref and DUT at the architectural commit boundary — when the DUT's RTL retires an instruction (or transaction-equivalent). Skip transient pipeline state.
[case: OpenXiangShan/difftest, lowRISC Ibex cosim, ImperasDV / RVVI].build-behavior-model).Compare ref and DUT at input transaction edges and output transaction edges. The ref is invoked once per input; output is compared on the next output edge.
Compare every cycle.
build-behavior-model contract. Usually a sign you're using the
wrong artifact as ref.Recommendation: don't pick cycle-driven unless you've already exhausted commit/retire and transaction approaches.
[industry-pattern]Three patterns. The first is the strong default.
The DUT retires an instruction (or completes a transaction). At that moment, it calls a DPI-C function that:
Used by [case: OpenXiangShan/difftest, lowRISC Ibex cosim, ImperasDV/RVVI].
Pros: the DUT controls timing; the ref is purely passive; performance is bound by the DUT, not by ref↔DUT IPC.
Cons: the ref must support being driven externally — most modern behavior models do.
The ref runs first, dumps a trace, the DUT runs second, and trace is diffed offline.
[case: Chipyard / Rocket Chip Spike commit-log diff].Both sides advance together; each side confirms the other after every step.
[industry-pattern: RVVI].[industry-pattern]| Mechanism | Use when | Notes |
|---|---|---|
| DPI-C with packed struct | Default. Same process. | Zero-copy; native debug; SVA-compatible. |
| Shared memory ring buffer | Ref must be a separate process (e.g., it's QEMU, or another tool). | Higher latency; needs careful synchronization. |
| Unix / TCP socket | Distributed setup or debug instrumentation. | Slowest; only for tooling. |
| VPI / PLI | DPI-C unavailable. | Slow; legacy. |
Default: DPI-C, with the probe header generated from the same DSL that
generates the interface (see define-contracts).
// Generated by the DSL toolchain — both sides include this header.
extern void diff_commit_register(uint32_t reg_id, uint64_t value);
extern void diff_commit_pc(uint64_t pc);
extern void diff_step_until_commit(void);
extern int diff_compare(diff_state_t* dut_state);
The signatures are not invented at integration time — they are derived
from the architectural state checklist established by
build-behavior-model.
[abstract]Two questions of policy.
Don't compare everything from day one. Stage in:
Tooling note: [industry-pattern: RVVI] standardizes this masking. Roll
your own with a config flag if you don't use RVVI.
Don't let the ref have an independent memory model. Either:
[case: NEMU DiffMem]mem_query() callback
(pull).Either avoids the false-positive class where ref and DUT disagree on implementation-defined regions (UART, timers, weakly-ordered shared memory).
[industry-pattern]Pattern: RTL detects, latches at architectural boundary, forwards to ref.
async IRQ ──→ DUT detects ──→ DUT latches at next commit boundary
│
▼
DUT calls diff_inject_irq(irq_id)
│
▼
ref accepts at the same architectural
boundary; both step together.
[case: lowRISC Ibex cosim, ImperasDV / RVVI]
Never let the ref generate its own async events. The ref's clock and the DUT's clock have no shared definition.
[industry-pattern]Long simulation runs (multi-day) cannot afford to "catch the mismatch with the wave open from the start". The pattern that solves this:
fork() the simulator process — every N cycles or
every M instructions.Public reference: LightSSS in OpenXiangShan/difftest is the canonical
implementation [case: OpenXiangShan/difftest LightSSS].
This is non-optional for projects with regression cycles measured in hours.
[abstract]A pattern only available if the project uses an interface DSL
(see define-contracts):
The DSL emits, alongside the C++ struct and SystemVerilog interface, a third artifact: the difftest probe header. Both ref and DUT pull from the same generated definitions, so signal sets cannot drift.
This is the operational form of "Generation over hand-writing" (invariant 1) applied to difftest. The cost of doing this from day one is ~one parser back-end. The cost of not doing this is reconciling probe mismatches across the project's lifetime.
[abstract]Triage in this order:
diagnose. Codex is well-suited to a
structured triage of a difftest mismatch.[abstract].so; batch commits.choose-artifact — does this project actually need difftest?define-contracts — the alignment contract and probe DSL feeds this
skill.build-behavior-model — the ref half of the difftest pair.build-rtl — the DUT half, with \ifdef DIFFTEST` instrumentation.diagnose — when difftest is failing and you want Codex to triage.references/case-xiangshan-difftest.md — the most complete
open-source reference.references/case-ibex-cosim.md — lowRISC Ibex retirement-level cosim.references/case-rvvi.md — the RVVI standard.references/case-libsystemctlm-soc.md — TLM ↔ RTL bridge for
SystemC-based refs.