SystemVerilog Cheatsheet - Quick Reference Guide
Quick-scan reference for SystemVerilog. Gotchas and tips are always visible; click "Show code" to expand examples.
Data Types
SystemVerilog has 2-state (0,1) and 4-state (0,1,x,z) types. Choose based on simulation needs vs. X-propagation requirements.
2-State vs 4-State
| Type | States | Default |
|---|---|---|
bit | 0, 1 | 0 |
logic | 0,1,x,z | x |
reg | 0,1,x,z | x |
wire | 0,1,x,z | z |
Integer Types
| Type | Bits | Signed | States |
|---|---|---|---|
byte | 8 | Yes | 2 |
int | 32 | Yes | 2 |
integer | 32 | Yes | 4 |
longint | 64 | Yes | 2 |
2-state types (
bit, int) convert X/Z to 0 silently - bugs hide! Use logic/integer in RTL to catch uninitialized signals.Use
logic everywhere in RTL (replaces both reg and wire). Use bit/int only in testbenches for speed.Other:
real (64-bit float), string (dynamic), void (no return)Arrays
Four array types with different allocation and indexing characteristics.
| Type | Syntax | When to Use |
|---|---|---|
| Fixed | int arr[8]; | Known size at compile time |
| Dynamic | int arr[]; | Size determined at runtime |
| Associative | int arr[string]; | Sparse data, any key type |
| Queue | int arr[$]; | FIFO/LIFO, variable size |
Dynamic arrays must be allocated with
new[n] before use. Accessing unallocated elements is undefined.find(), min(), max() return queues, not scalars! Use arr.min()[0] to get the value.Use
foreach(arr[i]) instead of for loops - cleaner and works with all array types.Show array operations
// Dynamic: allocate, resize, delete
int da[] = new[10];
da = new[20](da); // resize, keep data
da.delete();
// Queue: push, pop, insert
int q[$];
q.push_back(1); q.push_front(0);
int x = q.pop_front();
// Associative: key-value
int aa[string];
aa["key"] = 42;
if (aa.exists("key")) ...
Show array methods (find, sort, sum...)
int arr[] = '{3, 1, 4, 1, 5, 9};
arr.sort(); // In-place sort
arr.reverse(); // In-place reverse
arr.shuffle(); // Randomize order
int q[$] = arr.find(x) with (x > 4); // {5, 9}
int i[$] = arr.find_index(x) with (x==1); // indices
int s = arr.sum(); // Sum all elements
int m = arr.min()[0]; // Min value (note [0]!)
int u[$] = arr.unique(); // Remove duplicates
Operators
Arithmetic
+ - * / % **Logical
! && || ->Bitwise
~ & | ^ ~^Reduction
&a |a ^aEquality
== != === !==Shift
>> << >>> <<<Concat
{a,b} {n{a}}Special
inside ?: $== returns X if either operand has X/Z. Use === for exact 4-state comparison (case equality).>> is logical shift (fills with 0). Use >>> for arithmetic shift (preserves sign bit).inside is cleaner than multiple ||: if (x inside {1, 2, [5:10]})Show operator precedence (high to low)
1. () [] :: . (highest)
2. + - ! ~ (unary)
3. **
4. * / %
5. + -
6. >> << >>> <<<
7. < <= > >=
8. == != === !==
9. &
10. ^ ~^
11. |
12. &&
13. ||
14. ?:
15. = += -= ... (lowest)
Control Flow
case / casez / casexunique case - parallel, fullpriority case - first matchfor (int i=0; i<n; i++)foreach (arr[i])while / do-while / repeat / forevercasex treats X as don't-care in both selector AND case items - dangerous in RTL! Prefer casez.Forgetting
default in case can infer latches in combinational logic.Use
unique/priority keywords - they add runtime checks and help synthesis.Show control flow examples
// casez - use ? for don't care
casez (opcode)
4'b1???: op = JUMP;
4'b01??: op = BRANCH;
default: op = NOP;
endcasez
// foreach - works with any array
foreach (matrix[i,j])
matrix[i][j] = 0;
// repeat - fixed iterations
repeat (5) @(posedge clk);
Always Blocks
SystemVerilog's typed always blocks prevent common RTL bugs.
| Block | Use For | Assignments |
|---|---|---|
always_comb | Combinational | = (blocking) |
always_ff | Sequential (flip-flops) | <= (non-blocking) |
always_latch | Intentional latches | = (blocking) |
always_comb triggers at time 0 and on any RHS change. always @* only triggers on changes.Using
= in always_ff or <= in always_comb causes simulation/synthesis mismatch!Tools will error if
always_comb infers a latch - this catches missing else/default.Show always block examples
// Combinational - use blocking (=)
always_comb begin
y = a & b | c;
end
// Sequential with async reset - use NBA (<=)
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= '0;
else q <= d;
end
Functions & Tasks
| Aspect | Function | Task |
|---|---|---|
| Time | Zero (combinational) | Can consume time |
| Return | Required (or void) | None (use output args) |
| Can call | Functions only | Functions + tasks |
Functions are
static by default - local variables are shared across calls! Use automatic for recursion.ref arguments in functions must be automatic. Static functions can't have ref.Class methods are
automatic by default. Module functions need explicit automatic keyword.Show function/task examples
// Recursive function - needs automatic
function automatic int factorial(int n);
return (n <= 1) ? 1 : n * factorial(n-1);
endfunction
// Task with timing
task automatic drive(logic [7:0] data);
@(posedge clk);
valid <= 1; bus <= data;
@(posedge clk);
valid <= 0;
endtask
// Pass by reference
task automatic swap(ref int a, b);
int t = a; a = b; b = t;
endtask
Structs, Unions & Enums
typedef struct { ... } name_t;typedef struct packed { ... } - bit-addressabletypedef union packed { ... } - overlay viewstypedef enum logic [1:0] { A, B } e;Methods:
.name() .next() .prev().first() .last() .num()Unpacked structs can't be assigned to vectors directly. Use
packed for bit-level operations.Enum assignment from int requires cast:
state = state_e'(val); - won't compile without it.Always specify enum base type for synthesis:
enum logic [1:0] not just enum.Show struct/enum examples
// Packed struct - can cast to vector
typedef struct packed {
logic [3:0] opcode;
logic [7:0] data;
} instr_t; // 12 bits total
instr_t i = 12'hA5B;
$display("op=%h", i.opcode); // A
// Enum with explicit encoding
typedef enum logic [1:0] {
IDLE=0, RUN=1, DONE=2
} state_e;
state_e s = IDLE;
$display("%s", s.name()); // "IDLE"
s = s.next(); // RUN
Interfaces
Bundle signals + protocols. Use modports for direction, clocking blocks for testbench timing.
interface name (input clk); ... modport master(...); modport slave(...); endinterfaceclocking cb @(posedge clk); default input #1step output #0; ... endclockingvirtual interface - handle in classes for testbenchInterfaces can't be used inside classes directly - must use
virtual interface handle.Clocking block drives use
<= not =: vif.cb.valid <= 1;Use
#1step input skew to sample just before clock edge (avoids race conditions).Show interface example
interface axi_if (input clk, rst_n);
logic [31:0] addr, data;
logic valid, ready;
modport master (output addr, data, valid, input ready);
modport slave (input addr, data, valid, output ready);
clocking cb @(posedge clk);
default input #1step output #0;
output addr, data, valid;
input ready;
endclocking
modport tb (clocking cb);
endinterface
// In testbench class
class driver;
virtual axi_if.tb vif;
task drive();
@(vif.cb);
vif.cb.valid <= 1;
endtask
endclass
Packages
package pkg; ... endpackageimport pkg::item; or import pkg::*;pkg::item - explicit reference (no import needed)import pkg::* only imports items when used - not a bulk copy. Name conflicts resolve at use.Package compile order matters! Import before use, and packages must compile before importers.
Prefer explicit
import pkg::item over wildcard - clearer dependencies, fewer conflicts.System Tasks
Display
$display | Print + newline |
$write | Print, no newline |
$sformatf | Format to string |
$monitor | Print on change |
Format Specifiers
%d %0d | Decimal (padded/not) |
%h %b | Hex, Binary |
%s %t | String, Time |
%p | Pattern (structs) |
$display executes immediately. $strobe waits until end of time step - use for NBA results.%0d removes padding. %p prints structs/arrays nicely: '{a:1, b:2}Show file I/O and plusargs
// File I/O
int fd = $fopen("data.txt", "w");
$fdisplay(fd, "value=%0d", val);
$fclose(fd);
// Read memory file
logic [7:0] mem[256];
$readmemh("data.hex", mem);
// Plusargs: +SEED=42 +DEBUG
int seed;
if ($value$plusargs("SEED=%d", seed))
$display("Seed=%0d", seed);
if ($test$plusargs("DEBUG"))
$display("Debug on");
Show conversion functions
$signed(x) // Treat as signed
$unsigned(x) // Treat as unsigned
$clog2(n) // Ceiling log2 (for widths)
$itor(i) // Int to real
$rtoi(r) // Real to int
$random // Signed random
$urandom // Unsigned random
$urandom_range(max, min) // Range
String Methods
s.len() - lengths.substr(i,j) - slices.toupper() .tolower()s.atoi() .atohex() - to ints.itoa(n) - int to strings.compare(s2) - 0 if equalStrings are dynamic objects - comparing with
== works but uses value comparison (not reference).Common Patterns
static int id = 0; my_id = id++; - unique IDclass C #(type T=int); - parameterizedif (!$cast(child, parent)) - safe downcastarr.find_index(x) with (x>10) - filtergenerate if/for ... endgenerategenvar i - generate loop variable$cast returns 0 on failure (doesn't throw). Always check return value!Generate blocks need unique names:
begin : label ... endImplicit upcast is safe. Downcast always needs
$cast() - parent handle might not be child type.Show patterns
// Parameterized class
class Stack #(type T=int, int SIZE=10);
T data[SIZE];
int top = 0;
function void push(T item);
data[top++] = item;
endfunction
endclass
// Generate loop
generate
for (genvar i = 0; i < N; i++) begin : gen_blk
assign out[i] = in[i] & enable;
end
endgenerate
// Safe downcast
child_class c;
if (!$cast(c, parent_handle))
$error("Cast failed");
Part of the SystemVerilog Reference. See also: Classes & Objects
Comments (0)
Leave a Comment