This guide helps debug VCD input problems where GEM simulations produce incorrect results or warn about missing signals.
NEW: GEM now automatically detects the correct VCD scope containing your design's ports. In most cases, you don't need to specify --input-vcd-scope manually.
When you run jacquard sim without specifying --input-vcd-scope, GEM:
- Extracts the list of required input ports from your synthesized design
- Searches the VCD file for scopes containing all required ports
- Tries common DUT scope names first:
dut,uut,DUT,UUT, or your module name - Falls back to any scope that contains all required ports
- Logs which scope was selected for transparency
INFO No VCD scope specified - attempting auto-detection
DEBUG Searching for VCD scope containing 4 input ports
DEBUG Required ports: {"din_valid", "clk", "reset", "din"}
INFO Auto-detected VCD scope: safe_tb/uut (matched common pattern 'uut')
If auto-detection fails or selects the wrong scope, use --input-vcd-scope to specify manually:
# Slash-separated path to the DUT scope
jacquard sim design.gv input.vcd output.vcd 8 \
--input-vcd-scope "testbench/dut"
# For nested hierarchies
jacquard sim design.gv input.vcd output.vcd 8 \
--input-vcd-scope "top_tb/subsystem/my_module"Note: Use slash separators (/), not dots (.).
WARN (GATESIM_VCDI_MISSING_PI) Primary input port (HierName(), "reset", None) not present in the VCD input
WARN (GATESIM_VCDI_MISSING_PI) Primary input port (HierName(), "din", Some(3)) not present in the VCD input
GEM expects VCD signals at absolute top-level with no module hierarchy prefix. The signal names must exactly match the synthesized module's port names.
- Inspect your VCD file:
grep '\$var' your_input.vcd | head -20- Look for module scopes:
grep '\$scope module' your_input.vcd- Check synthesized module ports:
head -20 your_design_synth.gvCorrect - Signals at top level:
$timescale 1ns/1ns
$var reg 1 ! clk $end
$var reg 1 " reset $end
$var reg 4 # din [3:0] $end
$var reg 1 $ din_valid $end
$var wire 1 % unlocked $end
$enddefinitions $end
$dumpvars
0"
0$
0%
1!
#10
1"
#20
b1100 #
1$
Incorrect - Signals scoped under module:
$scope module testbench $end
$scope module dut $end
$var wire 1 ! clk $end
$var wire 1 " reset $end
$var wire 4 # din [3:0] $end
...
$upscope $end
$upscope $end
Create a testbench that dumps signals at absolute top level:
module testbench;
reg clk = 0;
reg reset;
reg [3:0] din;
reg din_valid = 0;
wire unlocked;
// DUT instantiation
your_module dut (
.clk(clk),
.reset(reset),
.din(din),
.din_valid(din_valid),
.unlocked(unlocked)
);
always #10 clk = !clk;
initial begin
// CRITICAL: Dump signals at top level (depth 1)
// NOT inside module hierarchy!
$dumpfile("output.vcd");
$dumpvars(1, clk, reset, din, din_valid, unlocked);
// Test sequence
reset = 1;
#60;
reset = 0;
// ... your test stimulus ...
#200;
$finish;
end
endmoduleKey Point: $dumpvars(1, signal1, signal2, ...) dumps individual signals at the current scope level, not inside child modules.
# For synthesis-compatible testbench
iverilog -DSYNTHESIS -o sim your_design.v testbench.v
./sim
# Check VCD structure
grep '\$scope' output.vcd # Should be minimal or none
grep '\$var' output.vcd | head -10If you can't change the testbench, post-process the VCD to flatten hierarchy:
#!/usr/bin/env python3
"""Flatten VCD hierarchy to top level"""
import sys
def flatten_vcd(input_vcd, output_vcd):
with open(input_vcd) as inf, open(output_vcd, 'w') as outf:
in_scope = False
scope_depth = 0
for line in inf:
# Track scope depth
if line.strip().startswith('$scope'):
scope_depth += 1
if scope_depth == 1:
continue # Keep root scope
in_scope = True
continue
elif line.strip().startswith('$upscope'):
scope_depth -= 1
if in_scope and scope_depth == 0:
in_scope = False
continue
# Skip signals inside nested scopes, keep only top-level
if in_scope and line.strip().startswith('$var'):
continue # Skip nested module signals
outf.write(line)
if __name__ == '__main__':
flatten_vcd(sys.argv[1], sys.argv[2])Usage:
python3 flatten_vcd.py hierarchical.vcd flat.vcdGEM provides --input-vcd-scope to specify which module hierarchy to read:
cargo run -r --features metal --bin jacquard -- sim \
design.gv input.vcd output.vcd 48 \
--input-vcd-scope module_nameKnown Issue: Currently, signal matching still fails even with correct scope specified. This is under investigation.
Synthesized Module:
grep "^module\|input\|output" design_synth.gvOutput:
module safe(clk, reset, din, din_valid, unlocked);
input clk;
input reset;
input [3:0] din;
input din_valid;
output unlocked;VCD Signals:
grep '\$var.*\(clk\|reset\|din\|unlocked\)' input.vcdOutput should match synthesized port names exactly.
Multi-bit signals must have correct indices:
Synthesized: input [3:0] din;
VCD:
$var reg 4 # din [3:0] $end
GEM expects separate indices: din[3], din[2], din[1], din[0]
GEM expects integer timestamps (not real numbers):
Correct:
#0
#10
#20
Incorrect:
#0.0
#10.5
#20.25
Ensure VCD timescale matches simulation expectations:
$timescale 1ns $end
or
$timescale 1ps $end
Clock periods in testbench should use same time unit.
After fixing VCD issues, validate GEM is reading inputs correctly:
cargo run -r --features metal --bin jacquard -- sim \
design.gv input.vcd output.vcd 48 \
--check-with-cpuThis compares GPU results against CPU gate-level simulation. Should print:
[INFO] sanity test passed!
Run same design with iverilog:
iverilog -o reference_sim design.v testbench.v
./reference_sim # Generates reference.vcdCompare outputs:
# Check if unlocked signal toggles the same in both
grep '^[01]!' gem_output.vcd
grep '^[01]!' reference.vcdcargo run -r --features metal --bin jacquard -- sim \
design.gv input.vcd output.vcd 48 \
2>&1 | grep "total number of cycles"Should match your testbench's simulation time / clock period.
If testbench is only compiled when SYNTHESIS is not defined:
`ifndef SYNTHESIS
module testbench;
// ...
endmodule
`endifYou must compile without -DSYNTHESIS for VCD generation:
iverilog -o sim design.v testbench.v # No -DSYNTHESIS!But the DUT must be compiled with -DSYNTHESIS if it has non-synthesizable constructs:
# Separate compilation
iverilog -DSYNTHESIS -c design.v
iverilog -o sim design.v testbench.vGEM may not handle unknown (X) or high-impedance (Z) values correctly:
$dumpvars
x" # reset = X
bxxxx # # din = XXXX
Solution: Initialize all inputs in testbench:
initial begin
reset = 0; // Don't leave uninitialized
din = 4'h0;
din_valid = 0;
endIf VCD doesn't include clock:
WARN (GATESIM_VCDI_MISSING_PI) Primary input port (HierName(), "clk", None) not present
Ensure:
- Clock is generated in testbench
- Clock is included in
$dumpvars - Clock signal name matches synthesized netlist exactly
// testbench_flat.v - Generates GEM-compatible VCD
module testbench_flat;
// Declare all signals at top level
reg clk = 0;
reg reset = 1;
reg [3:0] din = 4'h0;
reg din_valid = 0;
wire unlocked;
// DUT instantiation
safe dut (
.clk(clk),
.reset(reset),
.din(din),
.din_valid(din_valid),
.unlocked(unlocked)
);
// Clock generation
always #10 clk = !clk; // 20ns period = 50MHz
// Test sequence
initial begin
// CRITICAL: Dump at top level (depth 1)
$dumpfile("safe_flat.vcd");
$dumpvars(1, clk, reset, din, din_valid, unlocked);
// Reset phase
reset = 1;
#60; // 3 clock cycles
reset = 0;
#11; // Small offset from clock edge
// Apply test stimulus
din = 4'hc;
din_valid = 1;
#20;
din = 4'h0;
#20;
din = 4'hd;
#20;
din = 4'he;
#20;
din_valid = 0;
#40;
$finish;
end
endmoduleCompile and test:
# Compile (DUT must be SYNTHESIS-compatible)
iverilog -DSYNTHESIS -o sim safe.v testbench_flat.v
# Run simulation
./sim
# Verify VCD structure
echo "=== VCD Scopes ==="
grep '\$scope' safe_flat.vcd
echo -e "\n=== VCD Signals ==="
grep '\$var' safe_flat.vcd
# Should show signals at top level, no nested $scope modules-
Enable debug logging:
RUST_LOG=debug,vcd_ng=trace cargo run -r --features metal --bin jacquard -- sim <args> 2>&1 | tee debug.log
-
Check with minimal test:
- Create simplest possible design (single DFF)
- Generate flat VCD
- Verify GEM can read it correctly
-
Report issue with:
- Synthesized
.gvfile - Input VCD file
- GEM command line
- Error messages or unexpected output
- Synthesized
Document Version: 1.0 Last Updated: 2025-01-08 Related: simulation-architecture.md