Timing Validation Methodology
Note: The ±5% tolerance convention described here will be superseded by
timing-correctness.mdonce phase 0 ships (OpenSTA oracle + timing IR + diff harness). Content below remains accurate for current behaviour.
This document describes how GEM validates timing simulation accuracy against reference simulators (CVC, Icarus Verilog). It covers test cases, comparison metrics, known simulator differences, and acceptance criteria.
Overview
GEM's timing simulation (--timed and --enable-timing flags) must be validated against independent reference simulators to ensure correctness. We use two reference tools:
- CVC (Cadence Verilog Compiler): For post-layout SDF-annotated designs (commercial tool, high confidence)
- Icarus Verilog: For structural Verilog and Liberty-based timing (open source, good for pre-layout)
What We Validate
1. Functional Correctness (Primary Validation)
Definition: Both GEM and reference simulators produce identical output values at each clock cycle.
Signals compared: Primary design outputs (e.g., gpio_out[43:0] for MCU SoC), NOT internal timing signals.
Tolerance: Exact match required. If output values differ, timing simulation has a correctness bug.
Tool: compare_outputs.py script compares VCD outputs cycle-by-cycle.
# Example: MCU SoC functional comparison
uv run tests/mcu_soc/cvc/compare_outputs.py \
jacquard_output.vcd cvc_output.vcd \
--skip-cycles 5 # Skip first 5 cycles (reset/initialization)
2. Timing Accuracy (Secondary Validation)
Definition: Gate-level delays and arrival times computed by GEM match reference simulator within acceptable margins.
Known differences (simulator-specific semantics):
| Metric | GEM | CVC | Notes |
|---|---|---|---|
| Q arrival | Full combo path delay (CLK→dff_in.Q + chain + interconnect) | Final gate CLK→Q only | GEM is conservative: includes downstream logic |
| Setup slack | Time from data arrival to DFF setup deadline | Similar | Usually aligned |
| Hold slack | Time from data change to DFF hold deadline | Similar | Can differ due to arrival rounding |
Why the difference? GEM's boomerang architecture computes cumulative delays through pipeline stages, resulting in conservative estimates that over-predict delays by including full combinational paths. CVC only reports final DFF gate delays.
Tolerance:
- Functional output: Exact match required
- Arrival times: ±5% acceptable (due to architectural differences)
- Setup/hold margins: ±10ps acceptable (platform-dependent rounding)
Test Cases
inv_chain_pnr (Simple Reference Case)
Location: tests/timing_test/inv_chain_pnr/
Description: Single inverted AND gate followed by a chain of inverters. Provides ground truth for:
- Basic gate delay accuracy
- Multi-stage combinational path accumulation
- Clock-to-Q propagation
Size: 9 cells, ~40ps full path delay
Validation:
# Generate CVC reference
cd tests/timing_test/inv_chain_pnr
cvc64 +typdelays tb_cvc.v inv_chain.v 2>&1 | tee cvc.log
./cvcsim 2>&1 | grep "RESULT:" # Extract: clk_to_q, chain_delay, total_delay
# Generate GEM output
cargo run -r --features metal --bin jacquard -- sim \
6_final.v stimulus.vcd output.vcd 1 \
--sdf 6_final.sdf \
--timed
Expected results:
- Jacquard Q arrival ≈ CVC total_delay (within ±5%)
- Both simulators show monotonic delay increase with each inverter stage
MCU SoC post-layout (Complex Case)
Location: tests/mcu_soc/data/6_final.v + 6_final.sdf
Description: Full MCU SoC design with SKY130 cells, post-P&R netlist with SDF timing.
Size: 2.7k cells, ~19MB netlist, ~18MB SDF
Validation:
# In CI: .github/workflows/ci.yml mcu-soc-metal job
# 1. Strip SDF timing checks (remove malformed TIMINGCHECK directives)
uv run tests/mcu_soc/cvc/strip_sdf_checks.py \
tests/mcu_soc/data/6_final.sdf \
tests/mcu_soc/data/6_final_stripped.sdf
# 2. Generate Jacquard timing VCD
cargo run -r --features metal --bin jacquard -- sim \
tests/mcu_soc/data/6_final.v \
tests/mcu_soc/stimulus.vcd \
tests/mcu_soc/jacquard_timed_mcu.vcd 1 \
--sdf tests/mcu_soc/data/6_final_stripped.sdf \
--sdf-corner typ \
--timed \
--max-clock-edges 10000
# 3. Generate CVC reference
cvc64 +typdelays tests/mcu_soc/cvc/tb_cvc.v \
tests/mcu_soc/data/6_final.v \
tests/mcu_soc/cvc/sky130_cells.v \
2>&1 | tee cvc_compile.log
./cvcsim > cvc_output.vcd 2>&1
# 4. Compare functional outputs
uv run tests/mcu_soc/cvc/compare_outputs.py \
tests/mcu_soc/jacquard_timed_mcu.vcd \
tests/mcu_soc/cvc/cvc_output.vcd \
--skip-cycles 5
Expected results:
- Functional output: Exact match (or documented difference with explanation)
- Arrival times: Jacquard values ≥ CVC due to conservative path accumulation
- CI: Comparison completes without errors
Pre-layout Library Timing
Location: tests/timing_test/sky130_timing/
Description: Synthesized designs with Library-only timing (no SDF from P&R). Pre-layout tests validate timing accuracy early, before place-and-route.
Test circuits:
-
inv_chain: DFF → 16 inverters → DFF
- Expected combo delay: 16 × 28ps = 448ps (from Liberty inv_1 tpd)
- Full path: CLK→Q (310ps) + chain (448ps) = 758ps
-
logic_cone: 4 input DFFs → nand2/nor2/and2 tree → DFF
- Critical path: 4 gates × 35ps avg = 140ps
- Full path: CLK→Q (310ps) + logic (140ps) = 450ps
Validation:
# Generate Liberty-only SDF
cd tests/timing_test/sky130_timing
python3 gen_liberty_sdf.py inv_chain.v
python3 gen_liberty_sdf.py logic_cone.v
# Run CVC reference
cvc64 +typdelays tb_inv_chain.v inv_chain.v 2>&1 | tee cvc.log
./cvcsim
# Run Jacquard GPU simulation
cargo run -r --features metal --bin jacquard -- sim \
inv_chain.v stimulus.vcd jacquard_output.vcd 1 \
--sdf inv_chain.sdf \
--timed
# Compare outputs
uv run ../../mcu_soc/cvc/compare_outputs.py jacquard_output.vcd cvc_output.vcd
Expected results:
- Functional output: Exact match (logic correctness)
- Arrival times: Jacquard ≥ CVC (conservative accumulation expected)
- No SDF parsing errors or malformed delays
Purpose: Pre-layout tests catch timing issues early without P&R turnaround (30+ min). Library-only delays provide floor (lower bound); post-layout adds routing parasitics for final validation.
Multi-corner workflow (local Mac dev)
Location: crates/opensta-to-ir/tests/opensta_integration.rs —
sky130_multi_corner_emits_per_corner_values.
The test drives opensta-to-ir --liberty NAME=PATH (WS2.4) three times
with the genuine sky130 typ/slow/fast Liberty files and asserts the
emitted timing IR carries three corners with different setup values
(slow > typ > fast). This is the load-bearing check that
--timing-corner actually means something — the companion
aigpdk_dff_emits_per_corner_timing_values only validates IR shape.
One-time setup on a fresh Mac:
uv sync --group dev # installs volare
uv run volare enable c6d73a35f524070e85faff4a6a9eef49553ebc2b
# Optional override if the PDK lives elsewhere:
export SKY130_LIBERTY_DIR=/path/to/sky130_fd_sc_hd/lib
The pinned volare hash lives in pyproject.toml::[tool.jacquard.pdks.sky130].
The test skips cleanly with an install hint when the PDK isn't found,
so it's safe to run on machines without sky130 installed.
Running:
cd crates/opensta-to-ir
cargo test --test opensta_integration sky130_multi_corner -- --nocapture
GF180MCU PDK (in progress — phased per docs/plans/gf180mcu-enablement.md):
uv sync --group dev
uv run volare enable --pdk gf180mcu 559a117b163cef2f920f33f30f6f690aa0b47e4c
Liberty lands under ~/.volare/volare/gf180mcu/versions/<hash>/gf180mcuC/libs.ref/gf180mcu_fd_sc_mcu{7,9}t5v0/liberty/.
Both 7-track and 9-track standard-cell libraries are pulled together by
volare. Cell models are vendored as submodules at
vendor/gf180mcu_fd_sc_mcu7t5v0/ and vendor/gf180mcu_fd_sc_mcu9t5v0/.
CI Integration
MCU SoC Timing Comparison Workflow
The main CI pipeline (.github/workflows/ci.yml) includes:
-
mcu-soc-metal job: Generates Jacquard timing VCD
- Includes SDF stripping step (strip_sdf_checks.py)
- Produces jacquard_timed_mcu.vcd with arrival time annotations
-
mcu-soc-cvc job: Generates CVC reference output
- Uses stripped SDF (6_final_nocheck.sdf)
- Produces cvc_output.vcd for comparison
-
mcu-soc-comparison job: Validates both produce same functional output
- Runs compare_outputs.py
- Reports pass/fail in CI summary
- Skips gracefully if either simulator fails
SDF Stripping
SDF files from post-P&R tools may contain:
- Malformed TIMINGCHECK directives (setup/hold specs with syntax errors)
- INTERCONNECT entries with escaped port names that parsers reject
Solution: tests/mcu_soc/cvc/strip_sdf_checks.py removes:
- TIMINGCHECK blocks (parser errors, not needed for gate-level functional sim)
- INTERCONNECT lines (wire delays, optional)
- Empty DELAY blocks (after INTERCONNECT removal)
- CELL blocks with escaped $ in instance names
Result: Stripped SDF retains ~402k lines of useful IOPATH (gate delay) data, removing ~131k problematic lines.
Comparison Tolerances
Pre-Layout (Liberty-Only) Tests
Pre-layout tests compare designs without P&R parasitics, using only library cell timing:
| Metric | Tolerance | Notes |
|---|---|---|
| Functional output | 0 (exact match) | Logic correctness required |
| Arrival time | ±10% | Library delays only; no routing variance |
| Setup/hold slack | ±10ps | Rounding differences expected |
Why loose tolerances? Pre-layout designs have no P&R uncertainty. Functional correctness is critical (exact match); timing metrics can vary due to simulator implementation differences.
Post-Layout (SDF-Annotated) Tests
Post-layout designs include routing delays and P&R context:
| Metric | Tolerance | Notes |
|---|---|---|
| Functional output | 0 (exact match) | Required; SDF adds no logic changes |
| Arrival time | ±5% (SDF tool dependent) | GEM accumulates conservatively |
| Setup/hold slack | ±20ps | Detailed routing adds variance |
Known Issues & Limitations
1. Boomerang Path Accumulation
Issue: GEM sums gate delays across all pipeline stages leading to a node, producing cumulative arrival times. CVC only reports final DFF gate delay.
Example: inv_chain test:
- CVC reports: CLK→Q = 350ps (final gate only)
- GEM reports: Q arrival = 1323ps (CLK→dff_in.Q + inverter chain + interconnect)
Why: Boomerang architecture evaluates in hierarchical stages. To know Q arrival, you sum delays from all stages.
Mitigation: Compare Jacquard Q arrival against CVC RESULT: total_delay (if available), or accept that arrival times differ but functional output matches.
2. SDF Parser Robustness
Issue: Post-P&R SDF files from various tools contain edge-case syntax that doesn't parse correctly.
Examples:
- Empty delay specs:
(IOPATH A B () (0:0:0))— treated as 0ps - COND-qualified pins:
(SETUP (COND x==1 D) (posedge CLK) (180:200:220))— not yet supported - Malformed TIMINGCHECK: cause parser errors in byte-range 18M+
Current approach: Strip SDF timing checks before use, preserving IOPATH data.
Future: Improve SDF parser to handle more edge cases without stripping.
3. Timing Model Accuracy
Conservative model: GEM accumulates delays pessimistically. This is intentional—designs validated under the GEM model are guaranteed to meet timing in actual P&R.
Trade-off: Over-predicting delays means GEM may flag timing violations that won't occur in silicon. This is preferable to under-predicting (which would give false confidence).
Debugging Timing Failures
When Functional Output Doesn't Match
-
Check testbench stimulus: Does both simulators receive same input?
- Generate stimulus VCD:
--stimulus-vcd stimulus_out.vcd - Compare against reference testbench
- Generate stimulus VCD:
-
Check SDF parsing: Did parser successfully load all timing data?
- Enable debug logging:
--sdf-debug - Look for "unmatched SDF instances" warnings
- Enable debug logging:
-
Check initialization: Are both simulators starting from same state?
- Compare first 5-10 cycles after reset
- Verify reset logic is synchronized
-
Compare against Jacquard non-timed version:
# Run without timing VCD cargo run -r --features metal --bin jacquard -- sim \ design.v stimulus.vcd functional_output.vcd 1 # Does functional output match CVC? compare_outputs.py functional_output.vcd cvc_output.vcd
When Arrival Times Seem Wrong
- Verify SDF was loaded: Check logs for "Failed to load SDF" warnings
- Check corner selection: Confirm
--sdf-corner typ|min|maxmatches reality - Compare against CVC RESULT lines: If available, extract CVC timing measurements
- Check for pipeline stage effects: Arrival time = sum of delays across stages
Acceptance Criteria
| Test Case | Criterion | Status |
|---|---|---|
| inv_chain_pnr | Functional output exact match | ✅ Passing |
| inv_chain_pnr | Arrival time matches CVC±5% | ✅ Passing |
| MCU SoC | Functional output exact match | ✅ Passing (CI) |
| MCU SoC | SDF parsing completes without panic | ✅ Passing (fixed with strip_sdf_checks) |
| MCU SoC | Timing VCD generates successfully | ✅ Passing (fixed) |
| Pre-layout inv_chain | Library-only SDF generated correctly | ✅ Passing |
| Pre-layout logic_cone | Library-only SDF generated correctly | ✅ Passing |
| Pre-layout timing comparison | Functional output matches CVC | ⏳ In progress (CVC testbenches added) |
| CUDA/HIP timing support | --timed flag on GPU backends | ⏳ Not yet implemented |
| Cosim timing mode | Arrival time readback in cosim | ⏳ Not yet implemented |
References
- Timing Simulation in GEM — Architecture details
- Timing Violation Detection — Setup/hold checks
- SDF Parser — Parsing implementation
- CVC Integration — Reference simulator setup