Timing Validation Methodology

Note: The ±5% tolerance convention described here will be superseded by timing-correctness.md once 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):

MetricGEMCVCNotes
Q arrivalFull combo path delay (CLK→dff_in.Q + chain + interconnect)Final gate CLK→Q onlyGEM is conservative: includes downstream logic
Setup slackTime from data arrival to DFF setup deadlineSimilarUsually aligned
Hold slackTime from data change to DFF hold deadlineSimilarCan 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:

  1. 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
  2. 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.rssky130_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:

  1. 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
  2. mcu-soc-cvc job: Generates CVC reference output

    • Uses stripped SDF (6_final_nocheck.sdf)
    • Produces cvc_output.vcd for comparison
  3. 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:

MetricToleranceNotes
Functional output0 (exact match)Logic correctness required
Arrival time±10%Library delays only; no routing variance
Setup/hold slack±10psRounding 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:

MetricToleranceNotes
Functional output0 (exact match)Required; SDF adds no logic changes
Arrival time±5% (SDF tool dependent)GEM accumulates conservatively
Setup/hold slack±20psDetailed 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

  1. Check testbench stimulus: Does both simulators receive same input?

    • Generate stimulus VCD: --stimulus-vcd stimulus_out.vcd
    • Compare against reference testbench
  2. Check SDF parsing: Did parser successfully load all timing data?

    • Enable debug logging: --sdf-debug
    • Look for "unmatched SDF instances" warnings
  3. Check initialization: Are both simulators starting from same state?

    • Compare first 5-10 cycles after reset
    • Verify reset logic is synchronized
  4. 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

  1. Verify SDF was loaded: Check logs for "Failed to load SDF" warnings
  2. Check corner selection: Confirm --sdf-corner typ|min|max matches reality
  3. Compare against CVC RESULT lines: If available, extract CVC timing measurements
  4. Check for pipeline stage effects: Arrival time = sum of delays across stages

Acceptance Criteria

Test CaseCriterionStatus
inv_chain_pnrFunctional output exact match✅ Passing
inv_chain_pnrArrival time matches CVC±5%✅ Passing
MCU SoCFunctional output exact match✅ Passing (CI)
MCU SoCSDF parsing completes without panic✅ Passing (fixed with strip_sdf_checks)
MCU SoCTiming VCD generates successfully✅ Passing (fixed)
Pre-layout inv_chainLibrary-only SDF generated correctly✅ Passing
Pre-layout logic_coneLibrary-only SDF generated correctly✅ Passing
Pre-layout timing comparisonFunctional output matches CVC⏳ In progress (CVC testbenches added)
CUDA/HIP timing support--timed flag on GPU backends⏳ Not yet implemented
Cosim timing modeArrival time readback in cosim⏳ Not yet implemented

References