One-always-one-signal coding style: Each signal should be assigned in exactly one always block
Follow synthesizable coding guidelines:
always @(posedge clk) for sequential logicassign or always @(*) for combinational logicInclude header comments with author, date, and revision (see Version Tracking below)
Use descriptive signal names, avoid single-letter variables
Before simulation, run static syntax checking using slang:
bash
# Check Verilog/SystemVerilog syntax
slang <module_name>.v
# Or for SystemVerilog files
slang <module_name>.sv
What slang checks:
If slang reports errors:
Before simulation, verify:
if/ case branches assign in combinational blocks)default case branch=) in sequential always blocks<=) in combinational always blockstimescale directive present in all source filesalways #5 clk = ~clk; for 10ns period)$display() or $monitor() for pass/fail reporting$finish() after all tests complete<module_name>_tb.vThe skill automatically detects and uses available simulators in priority order:
vcs command available)xrun command available)bash
# Priority order: VCS → Xrun → Icarus
which vcs && use_vcs
which xrun && use_xrun
fallback to iverilog
bash
vcs -full64 -debug_acc+all -l sim.log -R <module_name>.v <module_name>_tb.v
bash
vcs -full64 -debug_acc+all -sverilog -l sim.log -R <module_name>.sv <module_name>_tb.sv
bash
xrun -64bit -access rwc -l sim.log <module_name>.v <module_name>_tb.v
bash
xrun -64bit -access rwc -sv -l sim.log -R <module_name>.sv <module_name>_tb.sv
bash
iverilog -o <module_name>.vvp <module_name>.v <module_name>_tb.v
vvp <module_name>.vvp
⚠️ Important: Always use VCD format for waveform dumping to ensure compatibility:
verilog
initial begin
$dumpfile("<module_name>.vcd");
$dumpvars(0, <module_name>_tb);
end
$dumpfile()/ $dumpvars()gtkwave ORpython3 <skill_dir>/scripts/check_vcd.py <module>.vcdverilog
`timescale 1ns/1ps
module <module>_tb;
// Parameters
parameter CLK_PERIOD = 10;
// Signals
reg clk;
reg rst_n;
// ... add inputs/outputs
// Instantiate DUT
<module> dut (
.clk(clk),
.rst_n(rst_n),
// ... ports
);
// Clock generation
initial begin
clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
// VCD dump
initial begin
$dumpfile("<module>.vcd");
$dumpvars(0, <module>_tb);
end
// Test stimulus
initial begin
// Initialize
rst_n = 0;
// ... init inputs
// Release reset
#(CLK_PERIOD * 2);
rst_n = 1;
// Apply test vectors
// ... stimulus code
// Check results
// ... self-checking assertions
#(CLK_PERIOD * 10);
$finish();
end
// Monitor
initial begin
$monitor("Time=%0t: signals=...", $time);
end
endmodule
For automated waveform checking, use Python VCD parsing. Reference: references/vcd-analysis.md
memory/verilog_specs/<module_name>_spec.md<module_name>.v<module_name>_tb.v.vvp (Icarus), sim.log, and .vcd filesUse the provided helper script to automatically select and run the best available simulator:
bash
# The script checks for VCS → Xrun → Icarus in order
bash <skill_dir>/scripts/simulate.sh <module_name>
Example workflow:
bash
# 1. Detect simulator and run
bash scripts/simulate.sh counter
# 2. Check simulation log
cat sim.log
# 3. Analyze VCD waveforms
python3 scripts/check_vcd.py counter.vcd
ToolCommandPurposeSynopsys VCSvcs -full64 -debug_acc+all -l sim.log -R file.vCompile & Simulate VerilogSynopsys VCS (SV)vcs -full64 -debug_acc+all -sverilog -l sim.log -R file.svCompile & Simulate SystemVerilogCadence Xrunxrun -64bit -access rwc -l sim.log file.vCompile & Simulate VerilogCadence Xrun (SV)xrun -64bit -access rwc -sv -l sim.log -R file.svCompile & Simulate SystemVerilogIcarus Verilogiverilog -o out.vvp file.vCompile Verilog (fallback)VVPvvp out.vvpRun simulationGTKWavegtkwave dump.vcdView waveforms (optional)
IssueFixMultiple driversEnsure one-always-one-signal: each signal assigned in exactly one always blockLatch inferenceEnsure all if/ case branches assign in combinational alwaysMissing resetInclude explicit reset in sequential always blocksRace conditionsUse non-blocking <= in sequential logic onlySimulation mismatchCheck timescale and delaysVCD not generatedEnsure $dumpfile() called before $dumpvars()
SymptomCauseSolutionNo output, simulation stuckCombinational loopCheck for circular logic in combinational always blocksInfinite loop warningZero-delay feedbackAdd delay elements or check async feedback pathsDivision by zeroRuntime calculation errorCheck divisor is never zeroArray out of boundsInvalid indexVerify index range before array access
SymptomCauseSolutionSpecific signal is XUninitialized registerAdd explicit reset valueWide bus partially XMixed width assignmentCheck vector width consistencyAfter reset releaseReset deassertion timingEnsure reset held long enoughRandom X propagationX propagation from inputTrace back to source of X
SymptomCauseSolutionOutput one cycle lateBlocking vs non-blockingUse <= in sequential always blocksGlitches on outputCombinational logic hazardAdd register stage or use synchronous outputSetup/hold violations (ASIC)Clock/data skewCheck synthesis timing reports
ErrorCauseSolution"Not synthesizable"Unsupported Verilog constructReplace with synthesizable equivalent"Multiple drivers"Signal assigned in multiple alwaysMerge logic or use intermediate signals"Latch inferred"Incomplete if/case in combinationalAdd default assignment or use else"Undriven signal"Output declared but not assignedConnect to logic or tie to constant
Every Verilog file should include:
verilog
/**
* Module: <module_name>
* Description: <brief description>
* Author: <name>
* Date: <YYYY-MM-DD>
* Version: <major>.<minor>.<patch>
*
* Changelog:
* v1.0.0 - <date> - Initial release
* v1.1.0 - <date> - <description of changes>
* v2.0.0 - <date> - <breaking changes>
*
* Parameters:
* - PARAM1: <description> (default: <value>)
* - PARAM2: <description> (default: <value>)
*
* Ports:
* - clk: <description>
* - rst_n: <description>
* ...
*/
bash
# Before starting new feature
git checkout -b feature/new-functionality
# After completing and testing
git add <files>
git commit -m "feat: add <feature> to <module>"
git checkout main
git merge feature/new-functionality