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.
Table of Contents
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
| Syntax | What it does | Example |
|---|---|---|
module name #(params) (ports); … endmodule | ANSI-style module header | See above |
input, output, inout | Port directions | output reg [7:0] q |
parameter / localparam | Overridable / fixed constant | localparam DEPTH = 16; |
| Named instance | Connect ports by name | counter #(.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 names | tx_fsm dut (.*); |
`timescale 1ns/1ps | Time unit / precision for # delays | Put it in every file, or none |
Data types and literals
| Type or literal | Meaning | Example |
|---|---|---|
wire | Net driven by assign or a port | wire [3:0] nibble; |
reg | Variable assigned in always or initial (not necessarily a register) | reg [7:0] count; |
logic (SV) | 4-state variable usable almost everywhere; replaces most wire/reg | logic [7:0] data; |
bit, int, byte (SV) | 2-state types (no X or Z); int is 32-bit signed | int cycles = 0; |
integer | 32-bit signed, 4-state (Verilog) | Loop counters in testbenches |
8'hA5, 4'b1010, 8'd200 | Sized literals: width, base, value | 8'b0000_1111 (underscores allowed) |
-8'sd4 | Signed literal | Use with reg signed [7:0] |
'0, '1 (SV) | Fill every bit with 0 or 1, any width | bit_cnt <= '0; |
x, z | Unknown and high impedance | 4'b1x00 |
reg [7:0] mem [0:3]; | Memory: 4 words of 8 bits | mem[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
| Operator | What it does | Result with a = 8’hA5, b = 8’h0F |
|---|---|---|
&, |, ^, ~ | Bitwise | a & b = 00000101 |
&a, |a, ^a | Reduction 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} | Concatenation | 16'ha50f |
{4{2'b10}} | Replication | 10101010 |
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 literally | Use ===/!== in testbenches |
c ? x : y | Conditional, often a mux | (a > b) ? a : b |
$clog2(n) | Ceiling log2, for address and counter widths | $clog2(16) = 4 |
Combinational logic
| Construct | Use for | Example |
|---|---|---|
assign y = …; | Simple continuous logic | assign busy = (state != IDLE); |
always @* begin … end | Verilog combinational block (blocking =) | always @* case (sel) … |
always_comb (SV) | Same, but tools check it really is combinational | always_comb next = state; |
case … default | Multiplexers and decoders | 2'd0: y = d[0]; |
unique case (SV) | Asserts exactly one branch matches; enables parallel logic | State decoders |
casez | ? as don’t-care bits | Priority 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
| Rule | Why |
|---|---|
Use <= (non-blocking) in clocked blocks | All flip-flops update together, so order of statements does not matter |
Use = (blocking) in combinational blocks | Values flow through in order, like software |
Never assign one signal from two always blocks | Multiple 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 SystemVerilog | Tools 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
| Construct | Example |
|---|---|
| Generate loop | genvar g; generate for (g = 0; g < 4; g = g + 1) begin : gen_bits assign w[g] = a[g] ^ b[g]; end endgenerate |
| Generate if | if (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 override | fifo #(.DEPTH(32), .WIDTH(16)) u_fifo (…); |
| Memory initialisation | initial $readmemh("rom.hex", mem); (supported by most FPGA flows) |
SystemVerilog types, packages and interfaces
| Feature | Example | Note |
|---|---|---|
| Enum | typedef enum logic [1:0] {RED, GREEN, BLUE} color_t; | c.name() prints GREEN; c.next() steps |
| Packed struct | typedef struct packed { logic [7:0] data; logic parity; } frame_t; | A bit vector with named fields |
| Package | package uart_pkg; … endpackage then import uart_pkg::*; | Shared types and constants |
| Interface | interface bus_if; logic valid, ready; logic [7:0] data; modport src (…); endinterface | Bundles a bus; modports set directions |
| Queue (testbench) | logic [3:0] q[$]; q.push_back(4'd3); | q.size(), q.pop_front() |
foreach | foreach (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
| Item | What it does |
|---|---|
initial, #10, @(posedge clk), repeat (n), wait (cond) | Stimulus and timing (not synthesisable) |
$display, $monitor, $write, $time | Print once, print on every change, print without newline, current time |
$finish, $stop | End 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
| Command | What it does |
|---|---|
iverilog -g2005 -Wall -o sim counter.v tb_counter.v | Compile Verilog-2005 with Icarus |
iverilog -g2012 -o sim tx_fsm.sv tb_tx.sv | Compile with SystemVerilog support enabled |
vvp sim | Run the compiled simulation |
verilator --lint-only -Wall tx_fsm.sv | Strict lint: widths, latches, unused signals, file names |
verilator --binary -j 0 tb.sv dut.sv | Build 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.vcd | View 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


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.