Verilog was designed by Phil Moorby and Prabhu Goel at Gateway Design Automation in 1984 as a proprietary hardware simulation language.
Gateway was acquired by Cadence Design Systems in 1990, which eventually opened Verilog to the public domain to counter the rising popularity of VHDL.
Standardized by the IEEE in 1995 as IEEE 1364 (Verilog-95), and later updated to Verilog-2001 and Verilog-2005. It subsequently laid the foundation for SystemVerilog, which extends Verilog with verification capabilities.
Who:
Phil Moorby (creator) and Cadence / IEEE committee.
Why:
Developed to provide a concise, readable, C-like hardware description and simulation language for modeling electronic circuits and logic gates.
Introduction
Advantages
C-like Syntax — Easy to learn for programmers already familiar with C/C++ style control flow.
Concise & Less Verbose — Requires much less boilerplate code than VHDL (no separate entities/architectures or component declarations needed).
Fast Simulation — Simple event-driven semantic rules allow rapid simulation of complex circuits.
System Tasks — Standard system functions like $display, $readmemh, and $finish built directly into the language for debugging.
Disadvantages
Weak Type Safety — Weak compiler type checks allow implicit scaling or assignments between incompatible vectors without throwing errors.
Unintended Latch Generation — Forgetting to define an else branch or a default case in combinational code automatically synthesizes transparent latches, causing timing problems.
Race Conditions — Mixing blocking and non-blocking assignments incorrectly can produce simulation/synthesis mismatches.
Remember Points
Blocking (=) vs. Non-blocking (⇐) — Use blocking assignments for combinational logic. Use non-blocking assignments for sequential registers.
Wires vs. Regs — A wire represents physical copper connections (updated via assign). A reg represents a data holding variable (updated inside procedural blocks like always).
Basics
Module Declaration
// Modules are the primary building blocks in Verilog.module and_gate ( input a, // Input ports input b, output y // Output port); // Continuous assignment (concurrent) assign y = a & b;endmodule
Comment Syntax
// This is a single-line comment./* This is a multi-line comment block. */
Numeric Representation
// Format: [size]'[base][value]// bases: b (binary), o (octal), d (decimal), h (hexadecimal)my_reg = 8'b1100_1010; // 8-bit binary (underscores allowed for readability)my_reg = 8'hCA; // 8-bit hexmy_reg = 32'd1000; // 32-bit decimalmy_reg = 4'd5; // 4-bit representation of 5 (0101)
Data Types & Logic States
Four-Value Logic States
State Description
0 Logic low / False / Ground
1 Logic high / True / VCC
x or X Unknown / Uninitialized logic value
z or Z High-impedance / Open circuit / Tri-state
Nets and Variables
// 1. Wires (Net types) - physical connections, do not hold valueswire s1;wire [7:0] bus; // 8-bit vector// 2. Registers (Variables) - hold values assigned in procedural blocksreg clk;reg [15:0] data_reg; // 16-bit register vector// 3. Integers - 32-bit signed helper variables (commonly loop counters)integer i;
Concurrent Statements
Continuous Assignment (assign)
//assign runs concurrently, instantly updating whenever RHS signals changeassign sum = a ^ b;assign carry = a & b;// Conditional assignment (Mux)assign out = (sel == 1'b1) ? in1 : in0;
Built-in Gate Primitives
// Verilog contains built-in primitives: and, or, xor, nand, nor, xnor, not, buf// Syntax: gate_type instance_name (output, input1, input2, ...);and u1 (y, a, b); // Instantiates an AND gatenot u2 (inv_a, a); // Instantiates an inverter
Gate-Level Modeling & Delays
Gate Propagation Delays
// Rise, Fall, and Turn-off delays can be specified for primitive gates// Format: #(rise_delay, fall_delay, turn_off_delay)// Single delay value applies to all transitionsand #5 a1 (y_simple, a, b); -- 5 time units delay// Distinct rise and fall delaysor #(3, 4) o1 (y_diff, a, b); -- rise=3, fall=4// Distinct rise, fall, and decay/turn-off delaysbufif0 #(2, 3, 4) b1 (out_tri, in, control);
Net Delays
// Delays can also be associated with net declarationswire #3 wire_delayed;assign wire_delayed = input_signal; -- Updates after 3 time units
User-Defined Primitives (UDP)
Combinational UDPs
// UDPs are defined outside module blocks, establishing truth tablesprimitive multiplexer_udp (out, control, in0, in1); output out; input control, in0, in1; table // ctrl in0 in1 : out 0 0 ? : 0; 0 1 ? : 1; 1 ? 0 : 0; 1 ? 1 : 1; x 0 0 : 0; x 1 1 : 1; endtableendprimitive
Sequential UDPs
// Sequential UDPs can model flip-flops and latches (require internal state column)primitive d_flip_flop_udp (q, clk, d, rst); output q; reg q; -- Needs to be declared as register input clk, d, rst; initialize q = 0; -- Initial state table // clk d rst : current_state : next_state ? ? 1 : ? : 0; -- Async reset (01) 0 0 : ? : 0; -- Rising edge D=0 (01) 1 0 : ? : 1; -- Rising edge D=1 (1?) ? 0 : ? : -; -- Falling edge / no change ? ? 0 : ? : -; -- Clock stable / no change endtableendprimitive
Generate Blocks (Verilog-2001)
Loop Generate
// Used to instantiate parameterized arrays of hardware elementsmodule vector_adder #(parameter SIZE = 8) ( input [SIZE-1:0] a, b, output [SIZE-1:0] sum); genvar i; -- Generate loop index variable (compile-time only) generate for (i = 0; i < SIZE; i = i + 1) begin : add_loop // Instantiates gates dynamically xor u_xor (sum[i], a[i], b[i]); end endgenerateendmodule
Conditional Generate
// Generates alternative architectures depending on parameter settingsmodule multiplier_selector #(parameter USE_FAST = 1) ( input [7:0] a, b, output [15:0] prod); generate if (USE_FAST == 1) begin : fast_mult assign prod = a * b; -- Hardware multipliers end else begin : slow_mult // Serial / shift-add logic component instantiation serial_multiplier sm1 (.x(a), .y(b), .z(prod)); end endgenerateendmodule
Procedural Blocks
always Blocks
Procedural blocks trigger when variables in their sensitivity list change:
// 1. Combinational Always Block (sensitivity list '*' detects any input change)always @(*) begin y = a & b; // Blocking assignment for combinational logicend// 2. Sequential Always Block (triggered by clock edges)always @(posedge clk or posedge rst) begin if (rst) begin q <= 1'b0; // Non-blocking assignment for register reset end else begin q <= d; // Non-blocking assignment for register state updates endend
initial Blocks
// initial blocks execute once at time 0 (used for simulation setups, non-synthesizable)initial begin clk = 0; rst = 1; #20 rst = 0; // wait 20 time units, then clear resetend
Blocking (=) vs. Non-Blocking (⇐)
Execution Semantics
// 1. Blocking (=) - Executes sequentially, blocking subsequent lines until evaluated// Used exclusively for combinational logicalways @(*) begin temp = a ^ b; sum = temp ^ c; // temp is updated before sum is evaluatedend// 2. Non-Blocking (<=) - Evaluates RHS instantly, schedules LHS updates at end of timestep// Used exclusively for sequential registers (prevents race conditions)always @(posedge clk) begin r1 <= in_data; r2 <= r1; // r2 gets OLD value of r1 before it updates! (proper shift register)end
Conditional Statements
if-else
// Must be placed inside procedural blocks (always/initial)always @(*) begin if (sel == 2'b00) out = in0; else if (sel == 2'b01) out = in1; else out = 1'b0; // Always include final else to prevent latch synthesis!end
Case Statements (case, casez, casex)
// case: exact bit matches (0, 1, x, z)// casez: treats 'z'/'?' bits as don't-care values// casex: treats both 'x' and 'z' bits as don't-care values (use with caution)always @(*) begin casez (decoder_input) 4'b1??? : out_select = 2'b11; -- matches any 4-bit starting with 1 4'b01?? : out_select = 2'b10; 4'b001? : out_select = 2'b01; default : out_select = 2'b00; endcaseend
Functions & Tasks
Functions
Functions are combinational helpers inside modules. They return a single value, execute without simulated time delays (no # or @), and cannot call tasks.
module calc_mod ( input [7:0] data_in, output [7:0] data_out); // Function definition function [7:0] compute_parity; input [7:0] val; begin compute_parity = val ^ 8'hAA; -- Assignments set the return value end endfunction assign data_out = compute_parity(data_in);endmodule
Tasks
Tasks are procedural blocks that can execute with time delays, handle multiple input/output parameters, and do not return values directly.
module bus_driver ( input clk, output reg [7:0] bus, output reg write_enable); // Task definition (can contain delays and wait events) task write_byte; input [7:0] data; begin @(posedge clk); write_enable = 1'b1; bus = data; @(posedge clk); write_enable = 1'b0; end endtask initial begin write_enable = 0; bus = 8'h00; #20; write_byte(8'hFF); -- Call task endendmodule
Procedural Continuous Assignment
assign and deassign
// Overrides standard procedural assignments on register typesinitial begin #10 assign my_register = 8'h00; -- overrides any always-block assignments to my_register #50 deassign my_register; -- releases override controlend
force and release
// Can override net types (wires) and variables (registers) during simulationinitial begin #10 force top_level.u1.wire_connection = 1'b1; -- Forces wire state #50 release top_level.u1.wire_connection; -- Releases simulation forceend
Finite State Machines (FSM)
Two-Always-Block Design
module fsm_controller ( input clk, rst, start, output reg ready); // State parameter definitions parameter IDLE = 2'b00; parameter RUN = 2'b01; parameter DONE = 2'b10; reg [1:0] state, next_state; // 1. Sequential Block: Update state registers always @(posedge clk or posedge rst) begin if (rst) state <= IDLE; else state <= next_state; end // 2. Combinational Block: Next state and output logic always @(*) begin next_state = state; // default state hold ready = 1'b0; case (state) IDLE : begin if (start) next_state = RUN; end RUN : begin next_state = DONE; end DONE : begin ready = 1'b1; next_state = IDLE; end default : next_state = IDLE; endcase endendmodule
// Declaring arrays: reg [data_bits] name [address_range]reg [7:0] ram_memory [0:255]; -- 256 bytes memory depth// Read operations (synchronous or asynchronous)assign read_data = ram_memory[read_address];// Write operations (synchronous)always @(posedge clk) begin if (write_enable) ram_memory[write_address] <= write_data;end
Compiler Directives
Directives & timescale
`timescale 1ns / 1ps // Time unit / precision`define WIDTH 8 // Macro definition`ifdef SIMULATION // Code compiled only for simulation tests`else // Code compiled for synthesis hardware`endif`include "my_defines.v" // File inclusion
Synthesis Guidelines & Common Pitfalls
Unintended Latches
Latches are generated when a combinational path does not define updates for all possible conditions.
// LATCH GENERATED (BAD PRACTICE):always @(*) begin if (enable) out = data; // Missing else branch! out retains value, generating a latchend// CORRECT SYNTAX (NO LATCHES):always @(*) begin out = 1'b0; -- Default assignment if (enable) out = data;end
Simulation vs. Synthesis Mismatch
Avoid writing non-synthesizable commands in production module logic:
SystemVerilog solves Verilog’s weak typing and loose structure restrictions:
Verilog Construct SystemVerilog Extension Description
wire / reg logic Unified type (automates wire/reg decision)
always @(*) always_comb Enforces combinational logic checking
always @(posedge clk) always_ff @(posedge clk) Enforces register execution logic check
parameter localparam / const Strong const declarations
No native structs struct packed { ... } Enables custom C-like packed struct data
No interfaces interface Groups bus lines to simplify wiring
Testbenches & Simulation
Testbench Template
`timescale 1ns/1psmodule tb_counter; // 1. local test signals reg clk; reg rst; wire [7:0] count; // 2. Instantiate Unit Under Test (UUT) counter #(.WIDTH(8)) uut ( .clk(clk), .rst(rst), .count(count) ); // 3. Clock generator (toggles every 5ns -> 100MHz clock) always #5 clk = ~clk; // 4. Stimulus process initial begin clk = 0; rst = 1; #15 rst = 0; // Clear reset #200; // run simulation for 200ns // System Tasks $display("Final Counter Value: %d", count); $finish; // End simulation end // 5. Monitoring values in console initial begin $monitor("Time=%d ns, Reset=%b, Count=%d", $time, rst, count); endendmodule
Useful System Tasks
Debugging functions
System Task Description
$display(...) Prints string output once to console (similar to printf)
$monitor(...) Prints string whenever any parameter value changes
$write(...) Similar to $display but without trailing newline
$time Returns current simulation time
$random Generates a random 32-bit signed integer
$readmemb("f.txt", m) Loads binary data from text file into memory array 'm'
$readmemh("f.txt", m) Loads hex data from text file into memory array 'm'
$finish Stops simulator and exits execution
$stop Suspends simulator, opening interactive prompt