| name | eda-sim |
| description | Offline EDA simulation workflow for ASIC DV projects using Synopsys VCS/Verdi. Use this skill whenever the user asks to run simulation, compile RTL, run regression, debug waveforms, read FSDB/waveform files, check coverage, or any task involving VCS/Verdi/UVM simulation. Also trigger when encountering compile errors, sim failures, needing to analyze sim.log results, or reading signal values from FSDB waveform dumps. This skill ensures correct offline tool invocation and includes an FSDB reader script using Synopsys NPI. |
Offline EDA Simulation Workflow
This skill defines the mandatory procedure for running Synopsys VCS simulations in an offline (no-network) environment. The EDA tools are installed locally but their default wrappers try to phone home for license validation, which fails without network access. The workaround is to invoke binaries directly and point to a local license server.
Why This Matters
Synopsys tool wrappers (e.g., vcs, verdi) use snpslmd license checkout that may route through network namespaces. In offline setups, this fails silently or hangs. By calling the binary directly via $VCS_BIN (set in .envrc) and ensuring SNPSLMD_LICENSE_FILE=27000@localhost.localdomain, we bypass network dependencies entirely.
Step 0: Environment Setup (MANDATORY, every session)
Before ANY EDA operation, source the project environment:
source <project_root>/.envrc
This sets all required variables. Verify with:
echo "VCS_BIN=$VCS_BIN"
echo "VERDI_HOME=$VERDI_HOME"
echo "LM_LICENSE_FILE=$LM_LICENSE_FILE"
If .envrc doesn't exist, create one following this template:
#!/bin/bash
export PROJECT_HOME="<absolute_path>"
export DV_ROOT="$PROJECT_HOME/dv"
export DIG_ROOT="$PROJECT_HOME/rtl"
export VCS_HOME="<path_to_vcs_installation>"
export VERDI_HOME="<path_to_verdi_installation>"
export SNPSLMD_LICENSE_FILE="27000@localhost.localdomain"
export LM_LICENSE_FILE="$SNPSLMD_LICENSE_FILE"
export VCS_BIN="$VCS_HOME/bin/vcs"
export VERDI_PLI_TAB="$VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab"
export VERDI_PLI_A="$VERDI_HOME/share/PLI/VCS/LINUX64/pli.a"
export PATH="$DV_ROOT/sim:$PATH"
Step 1: Running a Single Test
Use the project Makefile when available:
cd $DV_ROOT/sim
make all CASE_NAME=<category/test_name>
If no Makefile or it doesn't work, use direct VCS invocation:
$VCS_BIN -full64 -top test_bench -sverilog -lca -kdb \
-f $DV_ROOT/sim/flist \
-timescale=1ns/1ps \
+notimingcheck +nospecify \
-debug_access+all \
+define+DUMPOFF \
+warn=noIPC +lint=TFIPC-L \
+error+20 \
-P $VERDI_PLI_TAB $VERDI_PLI_A \
-l compile.log
./simv +UVM_TESTNAME=base_test -l sim.log
Key points:
- Always use
$VCS_BIN (the direct binary path), NEVER bare vcs
-timescale=1ns/1ps sets default for modules without explicit timescale
- Do NOT use
-unit_timescale — it overrides ALL modules including vendor IPs with different timescales, causing timing behavior changes
- Do NOT use
+memcbk on VCS 2023.12+ — it's deprecated, use -debug_access+all instead
Step 2: Running Regression
Use the project regression script:
cd $DV_ROOT/sim
./run_regr.sh [regr_list_name]
MAX_PARALLEL=8 ./run_regr.sh regr_list
If writing a custom regression script, key patterns:
- Use
$VCS_BIN not vcs for compilation
- Each test needs its own working directory with
current_case/ symlinks
- Create empty
define.sv if the test case doesn't provide one (flist requires it)
- Check
sim.log for $finish AND filter error/warning lines using exclude.txt
- Use
timeout command to prevent hung simulations
Step 3: Post-Processing Results
Check simulation pass/fail:
grep -q '$finish' sim.log && echo "SIM FINISHED" || echo "SIM INCOMPLETE"
grep -iE 'error|warning' sim.log \
| grep -v 'Warning-\[' \
| grep -v 'UVM_WARNING *:' \
| grep -v 'UVM_ERROR *:' \
| grep -v 'UVM_FATAL *:' \
| grep -v -f exclude.txt
Common false positive patterns to add to exclude.txt:
error_type.*v_error_type_e — UVM transaction field prints
^UVM_INFO — info messages containing "error" substring
error_en 0 rand_num — error-enable field in protocol logs
Command:.*simv — VCS command line echoes containing "error" in paths
illegal seting of role control — analog model transient states
MEM_Error — SRAM model clock glitch in functional sim (no SDF)
Step 4: Waveform Debug (if needed)
make all CASE_NAME=<name> FSDB=1
$VERDI_HOME/bin/verdi -elab simv.daidir/kdb.elab++ -ssf test.fsdb
Step 5: Coverage
urg -full64 -dir $(find . -name "*.vdb") -dbname merge.vdb
$VERDI_HOME/bin/verdi -cov -covdir merge.vdb
Step 6: FSDB Waveform Reading (without Verdi GUI)
FSDB is a Synopsys proprietary binary format. Use the bundled scripts/fsdb_reader.py to read signals programmatically. The script uses Synopsys NPI (primary) or CLI tools (fallback), no GUI needed.
IMPORTANT: NPI requires Verdi's bundled Python 3.6 ($VERDI_HOME/platform/linux64/Python/bin/python3.6). The script auto-detects this. System Python (3.7+) will cause segfault with NPI .so files — the script handles this transparently by invoking Verdi's Python as a subprocess.
Read a signal's value changes
python3 <skill_dir>/scripts/fsdb_reader.py <file.fsdb> --signal <path> [--start <time>] [--end <time>] [--format h]
Signal paths use . as hierarchy separator (NPI native format):
python3 scripts/fsdb_reader.py test.fsdb --signal test_bench.u_chip.clk --end 100ns
Format options: b (binary), o (octal), d (decimal), u (unsigned), h (hex, default).
List signals in an FSDB
python3 scripts/fsdb_reader.py test.fsdb --list-signals --depth 2
Convert FSDB to VCD
python3 scripts/fsdb_reader.py test.fsdb --to-vcd --output out.vcd [--end 1us]
VCD is a text format readable by any tool. Use --end to limit file size for large FSDBs.
Force CLI fallback mode
python3 scripts/fsdb_reader.py test.fsdb --signal test_bench.clk --cli
Direct CLI (alternative)
If the script isn't available, use Verdi tools directly:
$VERDI_HOME/bin/fsdbreport file.fsdb -s "test_bench/signal_name" -of h -et 1us
$VERDI_HOME/bin/fsdb2vcd file.fsdb -o output.vcd -et 1us
Generating FSDB from simulation
To enable FSDB dump, run with FSDB=1:
make all CASE_NAME=<name> FSDB=1
If the testbench $fsdbDumpvars is commented out, use UCLI at runtime:
echo 'call {$fsdbDumpfile("test.fsdb")}
call {$fsdbDumpvars(0, test_bench)}
run
quit' > dump.tcl
./simv +UVM_TESTNAME=base_test -ucli -i dump.tcl -l sim.log
Compilation Troubleshooting
| Error | Cause | Fix |
|---|
Error-[DEBUG_DEP_ERROR] +memcbk deprecated | VCS 2023.12+ removed +memcbk | Use -debug_access+all instead |
Error-[XMRE] Cross-module reference | Test references instances not in current TB | Check if the model/module exists in the testbench |
Error-[UM] Undefined macro | Missing +define+ or define file not compiled before RTL | Ensure define.sv is listed BEFORE RTL in flist |
Error-[SFCOR] Source file cannot be opened | Generated file (e.g., from TCL script) not created | Run the generation script (e.g., tclsh gen_regs.tcl > test_cmd.sv) |
Error-[SE] Syntax error: parameter in begin block | parameter/localparam not allowed in procedural blocks | Inline the constant value or move declarations before assignments |
| License checkout failure / tool hangs | Network wrapper can't reach license server | Use $VCS_BIN directly instead of vcs wrapper |
Regression Analysis Checklist
When analyzing regression failures:
- Classify failures: COMPILE FAIL vs SIM FAIL vs TIMEOUT
- For SIM FAIL, check
check_log_failed.log — if it only contains V_NO_ERROR or error_type lines, it's a false positive
- Group failures by error pattern (ADC value mismatch, CC timing, MEM_Error, etc.)
- Fix the highest-impact issues first (false positive filtering fixes the most tests)
- Re-process existing sim.log files when only the pass/fail checker changed (no need to re-simulate)