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

TypeStatesDefault
bit0, 10
logic0,1,x,zx
reg0,1,x,zx
wire0,1,x,zz

Integer Types

TypeBitsSignedStates
byte8Yes2
int32Yes2
integer32Yes4
longint64Yes2
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.

TypeSyntaxWhen to Use
Fixedint arr[8];Known size at compile time
Dynamicint arr[];Size determined at runtime
Associativeint arr[string];Sparse data, any key type
Queueint 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 ^a
Equality
== != === !==
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 / casex
unique case - parallel, full
priority case - first match
for (int i=0; i<n; i++)
foreach (arr[i])
while / do-while / repeat / forever
casex 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.

BlockUse ForAssignments
always_combCombinational= (blocking)
always_ffSequential (flip-flops)<= (non-blocking)
always_latchIntentional 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

AspectFunctionTask
TimeZero (combinational)Can consume time
ReturnRequired (or void)None (use output args)
Can callFunctions onlyFunctions + 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-addressable
typedef union packed { ... } - overlay views
typedef 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(...); endinterface
clocking cb @(posedge clk); default input #1step output #0; ... endclocking
virtual interface - handle in classes for testbench
Interfaces 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; ... endpackage
import 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

$displayPrint + newline
$writePrint, no newline
$sformatfFormat to string
$monitorPrint on change

Format Specifiers

%d %0dDecimal (padded/not)
%h %bHex, Binary
%s %tString, Time
%pPattern (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() - length
s.substr(i,j) - slice
s.toupper() .tolower()
s.atoi() .atohex() - to int
s.itoa(n) - int to string
s.compare(s2) - 0 if equal
Strings are dynamic objects - comparing with == works but uses value comparison (not reference).

Common Patterns

static int id = 0; my_id = id++; - unique ID
class C #(type T=int); - parameterized
if (!$cast(child, parent)) - safe downcast
arr.find_index(x) with (x>10) - filter
generate if/for ... endgenerate
genvar i - generate loop variable
$cast returns 0 on failure (doesn't throw). Always check return value!
Generate blocks need unique names: begin : label ... end
Implicit 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

Author
Mayur Kubavat
VLSI Design and Verification Engineer sharing knowledge about SystemVerilog, UVM, and hardware verification methodologies.

Comments (0)

Leave a Comment