Hello World
The smallest useful Verilog example is not hardware yet. It is a simulation example that proves your tools can compile and run a Verilog file. This is exactly like printing from a tiny software program, except the command flow uses a Verilog compiler and simulator.
module hello_world;
initial begin
$display("Hello, Verilog world");
$finish;
end
endmodule
Open the full file here: examples/verilog_beginner/hello_world.v. Run it with:
cd examples/verilog_beginner
make hello
Or manually:
iverilog -g2012 -Wall -o hello_world.vvp hello_world.v
vvp hello_world.vvp
$display, $finish,
and this style of initial block are not a circuit you would synthesize.
They are a learning and testing tool.
Mental Model
Verilog is a hardware description language. You are not writing a program that runs line by line on a CPU. You are describing circuits: wires, gates, registers, clocks, resets, multiplexers, counters, memories, and state machines.
The most useful beginner sentence is this: Verilog code becomes hardware when it is synthesizable, and it becomes a lab experiment when it is used in a testbench. Those two uses share syntax, but they have different rules.
Software Languages vs Hardware/Firmware Languages
If you come from Python, C, JavaScript, or Java, the biggest trap is assuming Verilog statements are instructions executed one after another. In software, a CPU fetches an instruction, executes it, moves to the next instruction, and changes memory over time. In Verilog, you are describing pieces of hardware that all exist at the same time.
Hardware description languages such as Verilog and VHDL are often used to create firmware for FPGAs. In this context, "firmware" does not mean a C program stored in flash. It means a hardware configuration loaded into a chip: lookup tables, flip-flops, routing, memories, DSP blocks, and I/O pins wired together to behave like your design.
| Software thinking | Verilog/RTL thinking |
|---|---|
| A function runs when it is called. | A module is hardware that exists continuously once instantiated. |
| Statements usually execute in order. | Independent hardware blocks operate concurrently. |
| A variable is storage in memory or a register chosen by the compiler. | A signal is a wire, combinational result, latch, or flip-flop depending on how it is assigned. |
| A loop repeats work over time. | A synthesizable loop often describes repeated hardware structure. |
| Waiting is done by sleeping or blocking a thread. | Real hardware waits by counting clock cycles or changing state on clock edges. |
| Debugging often means printing values as code runs. | Debugging often means inspecting waveforms and checking signal timing. |
Here is the same idea in miniature. In C, an assignment changes a variable when that line executes:
y = a & b;
In Verilog, this continuous assignment describes a gate-like connection.
Whenever a or b changes, y follows:
assign y = a & b;
Clocked logic is different again. This describes flip-flops that update only
on the rising edge of clk:
always @(posedge clk) begin
count <= count + 1'b1;
end
Example Files
These files live under examples/verilog_beginner/. Each link opens
in this site's source viewer, with a raw source button available from there.
Modules
A module is the basic unit of Verilog design. Think of it as a
reusable hardware block with named pins. The pins are the module's interface;
the internals describe how outputs are produced from inputs and stored state.
module and_gate (
input wire a,
input wire b,
output wire y
);
assign y = a & b;
endmodule
A larger design is built by instantiating modules inside other modules. This is hierarchy. A top module might instantiate a counter, a UART, a FIFO, and a controller, just as a circuit board contains connected chips.
Signals: wire, reg, vector, parameter
wire means a continuously driven connection. It is commonly used
for module inputs, simple outputs, and signals driven by assign.
reg means a signal assigned inside an always block.
Despite the name, reg does not always mean a flip-flop; context matters.
| Thing | Use | Example |
|---|---|---|
wire | Continuous connection | assign y = a & b; |
reg | Assigned in procedural blocks | always @(*) y = a; |
| Vector | Multi-bit signal | reg [7:0] count; |
parameter | Compile-time setting | parameter WIDTH = 8 |
Bit ordering matters. [7:0] is an 8-bit vector. Bit 7 is usually
the most significant bit and bit 0 is usually the least significant bit.
Combinational Logic
Combinational logic has no memory. If inputs change, outputs change after propagation delay. Gates, adders, comparators, decoders, and multiplexers are common examples.
assign y = sel ? b : a;
You can also describe combinational logic with always @(*). In
that case, assign every output in every path, or you may accidentally infer
a latch.
always @(*) begin
y = 1'b0;
if (enable)
y = a & b;
end
Sequential Logic
Sequential logic has memory. It changes on a clock edge and is how you build
counters, registers, shift registers, and finite-state machines. Use
nonblocking assignment, <=, for clocked logic.
always @(posedge clk) begin
if (rst)
count <= 8'd0;
else if (en)
count <= count + 1'b1;
end
The reset in the example is synchronous because it is checked inside
always @(posedge clk). An asynchronous reset would appear in
the sensitivity list, such as always @(posedge clk or posedge rst).
Testbenches
A testbench is a simulation-only module that instantiates your design under test, drives inputs, waits for time to pass, and checks or prints outputs. It is not meant to become hardware.
reg clk = 1'b0;
always #5 clk = ~clk;
initial begin
$dumpfile("counter_tb.vcd");
$dumpvars(0, counter_tb);
#12 rst = 1'b0;
#10 en = 1'b1;
#80 $finish;
end
The #5 delay and $display/$monitor style
system tasks are for simulation. They help you inspect behavior, but they
are not hardware structures to synthesize into an FPGA or ASIC.
Simulation Workflow
With Icarus Verilog, a typical flow is compile with iverilog,
then run with vvp. The official Icarus guide describes
iverilog as the compiler and vvp as the runtime
engine.
cd examples/verilog_beginner
make sim
Or manually:
iverilog -g2012 -Wall -o counter_tb.vvp counter_tb.v counter.v
vvp counter_tb.vvp
The testbench writes counter_tb.vcd. Open it with a waveform
viewer such as GTKWave to see signals over time.
gtkwave counter_tb.vcd
Synthesis
Synthesis converts synthesizable Verilog into a gate-level or technology mapped representation. Simulation asks "does the behavior look right?" Synthesis asks "what circuit implements this?"
Yosys is a common open-source synthesis tool. Its documentation describes it as an open-source framework for RTL synthesis and includes a getting started flow with loading, elaboration, mapping, and scripting.
cd examples/verilog_beginner
make synth
counter.v, mux2.v, and traffic_light_fsm.v,
not counter_tb.v.
Beginner Mistakes
- Thinking Verilog runs line by line like C. Hardware is concurrent.
- Using blocking assignment
=for clocked registers instead of<=. - Forgetting default assignments in combinational
always @(*)blocks. - Writing testbench delays inside design code and expecting them to synthesize.
- Mixing clock domains without synchronization.
- Letting signal widths be guessed instead of writing them explicitly.
- Ignoring warnings from simulators, linters, and synthesis tools.
Verilator is often useful as a fast simulator and lint tool for larger projects. For beginners, its warning output can be especially educational once the basic Icarus flow feels comfortable.
Reserved Words You Will See Early
Reserved words are words Verilog keeps for the language itself. Do not use
them as signal names or module names. For example, name a signal
enable, not assign.
| Word | Meaning | Example |
|---|---|---|
module |
Starts a hardware block definition. | module counter (...); |
endmodule |
Ends a module definition. | endmodule |
input |
Declares a port driven from outside the module. | input wire clk |
output |
Declares a port driven by this module. | output reg [7:0] count |
inout |
Declares a bidirectional port, common for tri-state buses and pins. | inout wire sda |
wire |
A continuously driven connection. | wire y; |
reg |
A signal assigned inside an always or initial block. |
reg [3:0] state; |
assign |
Creates a continuous assignment, usually combinational logic. | assign y = a & b; |
always |
Defines procedural behavior that runs when its sensitivity event happens. | always @(posedge clk) |
initial |
Runs once at time zero in simulation. Usually for testbenches. | initial rst = 1'b1; |
begin |
Starts a group of procedural statements. | begin count <= 0; |
end |
Ends a group of procedural statements. | end |
if |
Selects behavior when a condition is true. | if (rst) count <= 0; |
else |
Fallback behavior when the if condition is false. |
else count <= count + 1; |
case |
Chooses one branch from several options. | case (state) |
endcase |
Ends a case statement. |
endcase |
default |
Fallback branch in a case statement. |
default: y = 1'b0; |
parameter |
Compile-time setting for reusable modules. | parameter WIDTH = 8 |
localparam |
Internal compile-time constant that callers should not override. | localparam S_IDLE = 2'd0; |
for |
Loop. In synthesizable RTL, often expands repeated hardware. | for (i = 0; i < 8; i = i + 1) |
while |
Loop while a condition is true. More common in testbenches than RTL. | while (!done) #10; |
repeat |
Repeats a block a fixed number of times, often in testbenches. | repeat (5) @(posedge clk); |
forever |
Infinite loop, commonly used to create a testbench clock. | forever #5 clk = ~clk; |
function |
Defines reusable combinational calculation that returns a value. | function [3:0] add1; |
endfunction |
Ends a function definition. | endfunction |
task |
Defines reusable procedural code. Tasks may consume simulation time. | task pulse_reset; |
endtask |
Ends a task definition. | endtask |
posedge |
Rising edge event. | always @(posedge clk) |
negedge |
Falling edge event. | always @(negedge rst_n) |
or |
Used in event sensitivity lists and expressions. | always @(posedge clk or posedge rst) |
generate |
Starts compile-time hardware generation. | generate |
endgenerate |
Ends a generate region. | endgenerate |
genvar |
Integer-like variable used by generate loops. | genvar i; |
There are more Verilog reserved words, especially for primitives, timing, strengths, UDPs, and specify blocks. The list above focuses on the words a beginner will meet in ordinary RTL and testbenches first.
Relevant Web Pages
- Getting Started With Icarus Verilog: compile with
iverilog, run withvvp, and handle multi-file designs. - Yosys documentation: RTL synthesis concepts, command reference, and getting started material.
- Verilator simulation documentation: fast compiled simulation, runtime behavior, and coverage-related flows.
- HDLBits: interactive Verilog exercises with immediate feedback.