About
This post is a cheat sheet for Verilog/Sytem Verilog. I wrote this for myself, so if I ever have trouble remembering something(as I don’t write Verilog on a daily basis), I can just look it up on my own site. And maybe someone out there on the internet will find this useful for themselves as well.
But let’s be real, you and I will probably just ask AI for it, but I had this post in draft for a long time, so I thought I’d still post it.
Modules and Ports
//module, name, then the port list with directions and widths module adder //module name #( parameter WIDTH = 8 //parameters are compile-time constants )( input wire clk, //input port input wire [WIDTH-1:0] a, b, //[MSB:LSB] sets the bus width output reg [WIDTH-1:0] sum //output driven from an always block -> reg ); //... module body goes here ... endmodule //Old (non-ANSI) style you'll still see in legacy code: module adder(a, b, sum); input [7:0] a, b; output [7:0] sum; //... endmodule
Data Types (Nets vs Variables)
wire w; //net: driven continuously (assign) or by a port wire [7:0] bus; //8-bit net reg r; //variable: holds its value, assigned in always/initial reg [7:0] count; //8-bit variable integer i; //32-bit signed, handy for loops in testbenches real rl; //floating point (simulation only, not synthesizable) time t; //64-bit unsigned, for simulation time //Every bit is really 4-state: 0, 1, x (unknown), z (high impedance) reg [3:0] a = 4'b01xz; //x = unknown, z = tri-state / floating
Number Literals
//Format: <size>'<base><value> (size is in bits) 8'd255 //decimal, 8 bits 8'h0F //hexadecimal 4'b1010 //binary 8'o17 //octal -8'd5 //signed 8'hA_5 //underscores are ignored, use them for readability '0, '1 //fill all bits with 0 or 1 (SystemVerilog) 16'hZ //all Z, useful for tri-state buses
Operators
& | ^ ~ //bitwise AND, OR, XOR, NOT
&& || ! //logical AND, OR, NOT (return a single bit)
== != < > <= >= //comparison
=== !== //case equality: also compares x and z bits
<< >> //logical shift
<<< >>> //arithmetic shift (keeps the sign bit)
+ - * / % //arithmetic (/ and % often not synthesizable)
//Reduction operators: collapse a whole vector to one bit
&bus //AND of all bits
|bus //OR of all bits
^bus //XOR of all bits (parity)
//Concatenation and replication
{a, b} //glue vectors together
{4{1'b1}} //replicate: gives 4'b1111
//Ternary (the only "mux" you need)
assign out = sel ? a : b; Continuous Assignment
assign sum = a + b; //an adder
assign y = sel ? d1 : d0; //a 2:1 mux
assign {carry, result} = a + b; //capture the carry-out too
assign #5 z = a & b; //with a 5-time-unit delay (simulation) Procedural Blocks: initial and always
//initial: runs ONCE at time 0. Mostly for testbenches (not synthesizable). initial begin count = 0; #10 reset = 1; //#10 waits 10 time units end //always: runs repeatedly whenever its sensitivity list triggers. always @(a or b or sel) //triggers on any listed signal change y = sel ? a : b; always @(*) //(*) auto-builds the list -> use this for comb. logic y = a & b; always @(posedge clk) //triggers on the rising clock edge -> sequential logic q <= d;
Blocking (=) vs Non-Blocking (<=)
//Blocking: executes immediately, in order, like normal code. always @(*) begin x = a + b; //x updates now... y = x + 1; //...so y uses the NEW x end //Non-blocking: all right-hand sides are sampled first, then assigned together. always @(posedge clk) begin b <= a; //both use the OLD values -> this is a proper c <= b; //2-stage shift register, not c = a end
Combinational Logic (if / case)
always @(*) begin
if (sel == 2'b00) y = a;
else if (sel == 2'b01) y = b;
else y = c; //ALWAYS include else to avoid latches
end
always @(*) begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = 0; //default covers the rest -> no latch
endcase
end
//casex / casez treat x / z bits as don't-cares (use casez for priority logic) Sequential Logic (Flip-Flops & Reset)
//D flip-flop with asynchronous active-low reset always @(posedge clk or negedge rst_n) begin if (!rst_n) q <= 0; //async: reset appears in sensitivity list else q <= d; end //Counter with synchronous reset (reset NOT in the sensitivity list) always @(posedge clk) begin if (rst) count <= 0; else if (en) count <= count + 1; end
Loops
//for: unrolled into hardware if synthesizable, or used in testbenches
integer i;
always @(*)
for (i = 0; i < 8; i = i + 1)
out[i] = in[7-i]; //bit-reverse
repeat (4) @(posedge clk); //wait 4 clocks (testbench)
while (busy) #1; //loop until condition clears
forever #5 clk = ~clk; //run forever -> classic clock generator Parameters and Macros
module reg_file #(parameter WIDTH = 8, DEPTH = 16) ( /*...*/ ); localparam IDLE = 2'b00, RUN = 2'b01; //local constant, can't be overridden //Override a parameter at instantiation time: reg_file #(.WIDTH(16), .DEPTH(32)) u_rf ( /*...*/ ); //`define is a text macro (like C's #define) `define MAX_ADDR 1023 //used as `MAX_ADDR . Prefer parameter/localparam where you can.
Module Instantiation
//Named port connections (recommended -- order-independent, readable) adder #(.WIDTH(8)) u_add ( .clk (clk), //.port_name (signal_in_parent) .a (x), .b (y), .sum (result) ); //Positional connections (fragile -- must match the port order exactly) adder u_add2 (clk, x, y, result);
Generate Blocks
genvar g;
generate
for (g = 0; g < WIDTH; g = g + 1) begin : bit_slice //named block
full_adder fa (
.a (a[g]),
.b (b[g]),
.cin (carry[g]),
.sum (sum[g]),
.cout(carry[g+1])
);
end
endgenerate Tasks and Functions
//function: returns one value, no timing/delays, purely combinational
function [7:0] add;
input [7:0] x, y;
add = x + y; //assign to the function name to return
endfunction
//task: can have delays and multiple outputs, used mostly in testbenches
task apply_reset;
begin
rst = 1;
#20 rst = 0;
end
endtask
//call them:
assign s = add(a, b);
initial apply_reset; Testbench Basics
module tb;
reg clk = 0, rst;
wire [7:0] out;
//Instantiate the design under test
counter dut (.clk(clk), .rst(rst), .out(out));
always #5 clk = ~clk; //100 MHz clock (period = 10 time units)
initial begin
$dumpfile("wave.vcd"); //waveform file for GTKWave
$dumpvars(0, tb); //dump everything in this module
rst = 1; #12 rst = 0; //pulse reset
$display("t=%0t out=%0d", $time, out); //print once
$monitor("t=%0t out=%0d", $time, out); //print on any change
#100 $finish; //end the simulation
end
endmodule System Verilog
New Data Types
logic x; //4-state; use instead of reg/wire almost everywhere logic [7:0] bus; //can be driven by assign OR by always -- no more wire/reg debate bit b; //2-state (0/1 only), faster simulation //Sized 2-state integer types: byte a; // 8-bit signed shortint s; //16-bit signed int n; //32-bit signed longint l; //64-bit signed //logic can't be driven by two sources; use a real net type for buses: wire [7:0] shared_bus; //keep 'wire' where multiple drivers exist
always_comb / always_ff / always_latch
always_comb begin //combinational; auto sensitivity, warns on latches y = sel ? a : b; end always_ff @(posedge clk or negedge rst_n) begin //flip-flops only if (!rst_n) q <= '0; else q <= d; end always_latch begin //use this only when you truly want a latch if (en) q <= d; end
typedef, enum, struct, union
//typedef gives a name to a type
typedef logic [7:0] byte_t;
byte_t data;
//enum: named states -- perfect for FSMs
typedef enum logic [1:0] {IDLE, LOAD, RUN, DONE} state_t;
state_t state, next;
//struct: bundle related fields together
typedef struct packed { //packed = maps to a contiguous bit vector
logic [3:0] opcode;
logic [7:0] addr;
} instr_t;
instr_t ins;
ins.opcode = 4'h2; //access fields with a dot
typedef union packed { /*...*/ } u_t; //same bits, viewed different ways Arrays, Queues & Associative Arrays
logic [7:0] mem [0:255]; //unpacked array: 256 bytes of memory logic [7:0] mem2 [256]; //same thing, shorthand int dyn []; //dynamic array (sized at run time) dyn = new[10]; //allocate 10 elements int q [$]; //queue (grows/shrinks) q.push_back(5); //add to the end q.pop_front(); //remove from the front int aa [string]; //associative array (sparse, keyed by string) aa["count"] = 42;
Interfaces
interface bus_if (input logic clk); logic [7:0] addr, data; logic valid, ready; //modport defines a direction "view" for each side modport master (output addr, data, valid, input ready); modport slave (input addr, data, valid, output ready); endinterface //Use it as a single port: module cpu (bus_if.master b); //access signals as b.addr, b.valid, ... endmodule
Packages
//Share typedefs, params and functions across files
package my_pkg;
typedef enum {RED, GREEN, BLUE} color_t;
localparam WIDTH = 32;
endpackage
import my_pkg::*; //import everything
import my_pkg::color_t; //or just one item Assertions
//Immediate assertion: checked at the moment it's reached
always_comb
assert (onehot(sel)) else $error("sel not one-hot!");
//Concurrent assertion: checked over time against the clock
//"req must be followed by ack within 1..3 cycles"
property p_req_ack;
@(posedge clk) req |-> ##[1:3] ack;
endproperty
assert_req_ack: assert property (p_req_ack); Classes (Verification / OOP)
//Classes are for testbenches, not synthesizable hardware.
class Packet;
rand bit [7:0] addr; //rand = randomize this field
rand bit [7:0] data;
//constraint keeps random values legal
constraint c_addr { addr < 8'h80; }
function new(); //constructor
endfunction
function void print();
$display("addr=%h data=%h", addr, data);
endfunction
endclass
//Use it:
Packet p = new(); //create an object (handle)
if (!p.randomize()) //randomize() honours the constraints
$error("randomization failed");
p.print(); Concurrency: fork/join, mailbox, semaphore
//Run threads in parallel fork drive_stimulus(); monitor_outputs(); join //wait for ALL threads (join_any / join_none also exist) //mailbox: thread-safe queue to pass data between threads mailbox #(Packet) mbx = new(); mbx.put(p); //producer mbx.get(p); //consumer (blocks until something is available) //semaphore: guard a shared resource semaphore sem = new(1); sem.get(1); //take a key //... critical section ... sem.put(1); //give it back
Handy System Functions
$clog2(N) //ceiling log2 -- bits needed to count to N
//e.g. localparam AW = $clog2(DEPTH);
$size(arr) //number of elements
$bits(my_struct) //total width in bits
$signed(x) //treat as signed / unsigned
$unsigned(x)
$random //32-bit pseudo-random value
$urandom_range(1,10) //random in an inclusive range
$cast(dest, src) //safe type cast, returns 0 on failure
$fatal / $error / $warning / $info //severity messages





