Emergency server help: get in touch

Verilog and SystemVerilog Cheat Sheet for Sim and Synthesis

A compact Verilog and SystemVerilog reference for writing synthesisable RTL and simple testbenches, with the open-source commands to simulate, lint and synthesise.

Published 7 min read

Short answer: Verilog describes hardware: assign and always @* model combinational logic, always @(posedge clk) with non-blocking <= models flip-flops, and initial blocks with # delays belong in testbenches only. SystemVerilog (IEEE 1800-2023) adds logic, always_ff, always_comb, typedef enum for state machines, packed structs, packages, interfaces and assertions. Simulate with Icarus Verilog or Verilator, lint with verilator --lint-only -Wall, and synthesise with Yosys or your FPGA vendor’s tools.

The examples in this Verilog cheat sheet were simulated with Icarus Verilog 12.0, linted with Verilator 5.020 and synthesised with Yosys 0.33 on 9 October 2026. Support for SystemVerilog differs between tools; the notes say where Icarus 12 refused a construct.

Module structure and ports

`timescale 1ns/1ps
module counter #(
    parameter WIDTH = 8
) (
    input  wire             clk,
    input  wire             rst_n,      // active-low reset
    input  wire             en,
    output reg  [WIDTH-1:0] count,
    output wire             wrap
);
    assign wrap = en && (count == {WIDTH{1'b1}});

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)  count <= {WIDTH{1'b0}};
        else if (en) count <= count + 1'b1;
    end
endmodule
SyntaxWhat it doesExample
module name #(params) (ports); … endmoduleANSI-style module headerSee above
input, output, inoutPort directionsoutput reg [7:0] q
parameter / localparamOverridable / fixed constantlocalparam DEPTH = 16;
Named instanceConnect ports by namecounter #(.WIDTH(4)) u_cnt (.clk(clk), .rst_n(rst_n), .en(en), .count(c), .wrap(w));
.* (SV)Connect all ports to signals with the same namestx_fsm dut (.*);
`timescale 1ns/1psTime unit / precision for # delaysPut it in every file, or none

Data types and literals

Type or literalMeaningExample
wireNet driven by assign or a portwire [3:0] nibble;
regVariable assigned in always or initial (not necessarily a register)reg [7:0] count;
logic (SV)4-state variable usable almost everywhere; replaces most wire/reglogic [7:0] data;
bit, int, byte (SV)2-state types (no X or Z); int is 32-bit signedint cycles = 0;
integer32-bit signed, 4-state (Verilog)Loop counters in testbenches
8'hA5, 4'b1010, 8'd200Sized literals: width, base, value8'b0000_1111 (underscores allowed)
-8'sd4Signed literalUse with reg signed [7:0]
'0, '1 (SV)Fill every bit with 0 or 1, any widthbit_cnt <= '0;
x, zUnknown and high impedance4'b1x00
reg [7:0] mem [0:3];Memory: 4 words of 8 bitsmem[3] = 8'hFF;

A sized literal with too many digits is truncated: Icarus warned about 4'hFF and kept F, and Verilator treats it as an error.

Operators

OperatorWhat it doesResult with a = 8’hA5, b = 8’h0F
&, |, ^, ~Bitwisea & b = 00000101
&a, |a, ^aReduction to one bit (^ gives parity)&a = 0, |a = 1, ^a = 0
!, &&, ||Logical (any non-zero is true)a && b = 1
<<, >>, >>>Shifts; >>> keeps the sign on signed values-4 >>> 1 = -2
{a, b}Concatenation16'ha50f
{4{2'b10}}Replication10101010
w[15:8], w[8 +: 8], w[15 -: 8]Part-selects (fixed, indexed up, indexed down)Same byte three ways
== vs ===== gives x if any bit is x; === compares x and z literallyUse ===/!== in testbenches
c ? x : yConditional, often a mux(a > b) ? a : b
$clog2(n)Ceiling log2, for address and counter widths$clog2(16) = 4

Combinational logic

ConstructUse forExample
assign y = …;Simple continuous logicassign busy = (state != IDLE);
always @* begin … endVerilog combinational block (blocking =)always @* case (sel) …
always_comb (SV)Same, but tools check it really is combinationalalways_comb next = state;
case … defaultMultiplexers and decoders2'd0: y = d[0];
unique case (SV)Asserts exactly one branch matches; enables parallel logicState decoders
casez? as don’t-care bitsPriority encoders

Every output of a combinational block must be assigned on every path, or synthesis infers a latch. Assign a default at the top of the block (next = state;) or cover every case with default. Verilator reports the mistake directly:

always_comb begin
    if (en) q = d;          // no else: q must hold its value, so a latch
end
// %Warning-LATCH: Latch inferred for signal 'q' (not all control paths
//   of combinational always assign a value)

Sequential logic and state machines

RuleWhy
Use <= (non-blocking) in clocked blocksAll flip-flops update together, so order of statements does not matter
Use = (blocking) in combinational blocksValues flow through in order, like software
Never assign one signal from two always blocksMultiple drivers: synthesis error or simulation race
Asynchronous reset: @(posedge clk or negedge rst_n)Reset acts immediately
Synchronous reset: @(posedge clk) then if (rst)Reset is sampled on the clock edge
Use always_ff in SystemVerilogTools flag anything that does not infer flip-flops
module tx_fsm #(parameter int N = 8) (
    input  logic clk, rst_n, start,
    output logic busy
);
    typedef enum logic [1:0] {IDLE, START, DATA, STOP} state_t;
    localparam int CW = $clog2(N);

    state_t        state, next;
    logic [CW-1:0] bit_cnt;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state   <= IDLE;
            bit_cnt <= '0;
        end else begin
            state <= next;
            if (state == DATA) bit_cnt <= bit_cnt + 1'b1;
        end
    end

    always_comb begin
        next = state;                         // default: no latch
        unique case (state)
            IDLE:    if (start) next = START;
            START:   next = DATA;
            DATA:    if (bit_cnt == CW'(N - 1)) next = STOP;
            STOP:    next = IDLE;
            default: next = IDLE;
        endcase
    end

    assign busy = (state != IDLE);
endmodule

Yosys 0.33 synthesised this FSM to 18 cells, 5 of them flip-flops (2 state bits and 3 counter bits). CW'(N - 1) is a size cast that keeps Verilator’s width check quiet. Icarus 12 accepts unique case but ignores the uniqueness check.

Parameters, generate, functions and tasks

ConstructExample
Generate loopgenvar g; generate for (g = 0; g < 4; g = g + 1) begin : gen_bits assign w[g] = a[g] ^ b[g]; end endgenerate
Generate ifif (USE_FIFO) begin : g_fifo … end else begin : g_direct … end
Function (combinational, returns a value)function [7:0] reverse(input [7:0] x); … endfunction
Task (may contain delays; simulation helper)task show(input [7:0] v); $display("show %h", v); endtask
Parameter overridefifo #(.DEPTH(32), .WIDTH(16)) u_fifo (…);
Memory initialisationinitial $readmemh("rom.hex", mem); (supported by most FPGA flows)

SystemVerilog types, packages and interfaces

FeatureExampleNote
Enumtypedef enum logic [1:0] {RED, GREEN, BLUE} color_t;c.name() prints GREEN; c.next() steps
Packed structtypedef struct packed { logic [7:0] data; logic parity; } frame_t;A bit vector with named fields
Packagepackage uart_pkg; … endpackage then import uart_pkg::*;Shared types and constants
Interfaceinterface bus_if; logic valid, ready; logic [7:0] data; modport src (…); endinterfaceBundles a bus; modports set directions
Queue (testbench)logic [3:0] q[$]; q.push_back(4'd3);q.size(), q.pop_front()
foreachforeach (arr[i]) arr[i] = i * 2;Loops over array indexes

Tool support varies. In the test runs, Icarus 12 failed on interfaces used as ports, on inside, on whole-array assignment patterns ('{1, 2, 3, 4}) and on %p formatting; Verilator linted the interface design cleanly. Yosys 0.33 rejected a package type in a port list (output uart_pkg::frame_t frame), so keep synthesisable SystemVerilog conservative or check your synthesis tool’s support list.

Testbenches and assertions

`timescale 1ns/1ps
module tb_tx;
    logic clk = 0, rst_n = 0, start = 0, busy;
    int cycles = 0;

    tx_fsm #(.N(8)) dut (.*);

    always #5 clk = ~clk;                  // 10 ns period

    initial begin
        $dumpfile("tb_tx.vcd");
        $dumpvars(0, tb_tx);
        repeat (2) @(posedge clk);
        rst_n = 1;
        @(negedge clk) start = 1;
        @(negedge clk) start = 0;
        while (busy) begin
            @(posedge clk);
            cycles++;
        end
        assert (cycles == 11) else $error("got %0d busy cycles", cycles);
        $display("PASS: busy for %0d cycles", cycles);
        $finish;
    end
endmodule
ItemWhat it does
initial, #10, @(posedge clk), repeat (n), wait (cond)Stimulus and timing (not synthesisable)
$display, $monitor, $write, $timePrint once, print on every change, print without newline, current time
$finish, $stopEnd or pause the simulation
$dumpfile, $dumpvars(0, top)Write a VCD waveform for GTKWave or Surfer
$urandom, $urandom_range(max, min)Random stimulus (SV)
assert (expr) else $error(…);Immediate assertion
assert property (@(posedge clk) disable iff (!rst_n) (state == IDLE && start) |=> busy);Concurrent assertion: |=> checks the next cycle (Verilator linted it; Icarus 12 rejected the syntax)

Simulate, lint and synthesise with open-source tools

CommandWhat it does
iverilog -g2005 -Wall -o sim counter.v tb_counter.vCompile Verilog-2005 with Icarus
iverilog -g2012 -o sim tx_fsm.sv tb_tx.svCompile with SystemVerilog support enabled
vvp simRun the compiled simulation
verilator --lint-only -Wall tx_fsm.svStrict lint: widths, latches, unused signals, file names
verilator --binary -j 0 tb.sv dut.svBuild a fast C++ simulation (needs a C++ compiler; slower to build than Icarus)
yosys -p "read_verilog -sv tx_fsm.sv; synth -top tx_fsm; stat"Generic synthesis and a cell count
gtkwave tb_tx.vcdView waveforms

For server admins: the tools install from the distribution packages (apt install iverilog verilator yosys on Debian and Ubuntu), and a lint plus simulation run fits in a CI job: fail the build if verilator --lint-only -Wall returns non-zero or the testbench prints no PASS line. Silence single Verilator warnings with options such as -Wno-DECLFILENAME rather than dropping -Wall.

Official documentation (checked 9 October 2026): IEEE 1800-2023 SystemVerilog standard · IEEE 1364-2005 Verilog standard · Icarus Verilog documentation · Verilator user guide · Yosys documentation

Verilog Cheat Sheet at a glance

Verilog Cheat Sheet – short answer
In short: A compact Verilog and SystemVerilog reference for writing synthesisable RTL and simple testbenches, with the open-source commands to simulate, lint and synthesise.
Verilog Cheat Sheet – what this guide covers
Verilog Cheat Sheet: what this guide covers.

Related: C Cheat Sheet: C17 and C23 Syntax, Memory and gcc Flags · C++ Cheat Sheet: C++20 and C++23 Syntax, STL and Ranges · Number Base Converter.

Reading a lot of our guides? Go ad-free with srvScripts Pro for $6 a month. It also pays for new guides. Cancel any time.

Share this guide

Free website test

Is your website set up right?

Check SSL, security headers, redirects, robots.txt, sitemap, llms.txt and security.txt in one test. It takes about 30 seconds.