Verilog Explained: A Complete Guide to Hardware Description Language for Beginners
If you're stepping into the world of digital electronics, FPGA design, or ASIC development, there's one language you simply can't avoid: Verilog. In this post, we'll break down what Verilog is, why it exists, its core building blocks, and walk through real code examples you can try yourself.
What is Verilog?
Verilog is a Hardware Description Language (HDL) used to model, design, and verify digital electronic circuits. Unlike software programming languages such as C or Python — which describe a sequence of instructions executed by a processor — Verilog describes hardware structure and behavior. It tells a tool how logic gates, flip-flops, and entire processors should be built and how they behave over time.
Verilog was created in 1984 by Phil Moorby and Prabhu Goel at Gateway Design Automation, later becoming an IEEE standard (IEEE 1364) in 1995. Today it remains one of the two dominant HDLs, alongside VHDL, and its successor SystemVerilog extends it with verification-focused features.
Verilog vs. Software Languages: The Key Mindset Shift
The biggest hurdle for beginners is realizing Verilog code doesn't run "top to bottom" like software. Instead, it describes concurrent hardware blocks that all operate simultaneously, just like real circuits do. A block of code doesn't "finish" and move to the next line — it represents a physical piece of hardware that exists and reacts continuously.
The Basic Building Block: The Module
Every piece of hardware in Verilog is described inside a module. Think of a module as a black box with inputs and outputs — similar to a function, but representing a physical circuit block.
module and_gate (
input wire a,
input wire b,
output wire y
);
assign y = a & b; // continuous assignment
endmodule
This describes a simple 2-input AND gate. The assign statement creates a continuous, always-active connection — exactly like a real wire driven by a gate.
Data Types You'll Use Most
- wire – represents a physical connection; driven continuously by
assignor a gate/module output. - reg – holds a value between procedural assignments (used inside
alwaysblocks). Despite the name, it doesn't always mean a physical register. - integer – a 32-bit signed variable, mainly for simulation/loop counters.
- parameter – a compile-time constant, useful for configurable widths or values.
Two Modeling Styles: Structural and Behavioral
1. Structural modeling — you connect predefined gates or modules like wiring a circuit diagram:
module mux2to1_structural (
input wire a, b, sel,
output wire y
);
wire not_sel, and1, and2;
not (not_sel, sel);
and (and1, a, not_sel);
and (and2, b, sel);
or (y, and1, and2);
endmodule
2. Behavioral modeling — you describe what the circuit should do, and let the synthesis tool figure out the gates:
module mux2to1_behavioral (
input wire a, b, sel,
output reg y
);
always @(*) begin
if (sel)
y = b;
else
y = a;
end
endmodule
Sequential Logic: The always Block and Clocking
Most real designs need memory — flip-flops and registers that update on a clock edge. This is where the always @(posedge clk) construct comes in.
module d_flip_flop (
input wire clk,
input wire rst_n,
input wire d,
output reg q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0; // asynchronous reset
else
q <= d; // non-blocking assignment
end
endmodule
Notice the <= operator — this is a non-blocking assignment, used almost exclusively inside clocked always blocks. It models the fact that all flip-flops update simultaneously on the clock edge, not one after another. Combinational logic, on the other hand, typically uses the blocking assignment = inside always @(*) blocks.
<= for sequential (clocked) logic and = for combinational logic. Mixing them incorrectly is one of the most common beginner bugs.
A Practical Example: 4-Bit Counter
module counter_4bit (
input wire clk,
input wire rst_n,
output reg [3:0] count
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
count <= 4'b0000;
else
count <= count + 1'b1;
end
endmodule
Verifying Your Design: The Testbench
A testbench is a separate, non-synthesizable module used purely for simulation. It generates stimulus (like a clock) and checks outputs.
module tb_counter;
reg clk = 0;
reg rst_n = 0;
wire [3:0] count;
counter_4bit uut (.clk(clk), .rst_n(rst_n), .count(count));
always #5 clk = ~clk; // 10ns clock period
initial begin
rst_n = 0;
#12 rst_n = 1;
#100 $finish;
end
initial
$monitor("Time=%0t count=%0d", $time, count);
endmodule
Common Operators Cheat Sheet
| Category | Operators |
|---|---|
| Bitwise | & | ^ ~ |
| Logical | && || ! |
| Relational | == != > < >= <= |
| Arithmetic | + - * / % |
| Shift | << >> |
| Concatenation | {a, b} |
Tools to Get Started
- Icarus Verilog – free, open-source simulator, great for beginners.
- Verilator – fast open-source Verilog-to-C++ simulator, industry-grade speed.
- ModelSim/QuestaSim – widely used in industry for simulation.
- Xilinx Vivado / Intel Quartus – for synthesizing designs onto real FPGAs.
- EDA Playground – browser-based, no installation needed, perfect for quick experiments.
Where Verilog Is Used
Verilog powers the design of ASICs (custom chips), FPGAs (reconfigurable chips), CPUs, GPUs, memory controllers, communication protocols (like UART, SPI, I2C), and even entire open-source processor cores such as RISC-V implementations.
Final Thoughts
Verilog isn't just "another programming language" — it's a way of thinking about hardware as parallel, timed, physical structures rather than sequential instructions. Start small: build a gate, then a mux, then a flip-flop, then a counter, and simulate each one. Once the concurrency mindset clicks, everything else — FSMs, pipelines, even full CPUs — builds naturally on these same fundamentals.
Got questions about a specific Verilog construct or want the next post to cover Finite State Machines? Drop a comment below!

0 Comments