Software Time vs Hardware Time
In a program, time advances one instruction at a time. The CPU finishes line 1, then runs line 2. If you want two things to happen at once, you reach for threads, async, or a second machine.
In hardware, everything happens at once. A circuit doesn't take turns. The adder is always adding, the multiplexer is always selecting, the flip-flop is always watching the clock. There's no program counter. There's no "current line."
Verilog has to describe that world in text. The way it does it is the source of every confusion in this chapter.
Two Layers of Concurrency
When you read a Verilog module, you're looking at two different things at the same time:
- The static description of the circuit. Wires, registers, gate instances, sub-module instances, continuous assignments. These all exist simultaneously. Their order in the file doesn't matter.
- Procedural blocks -
initialandalways- which look like little programs that the simulator steps through. Inside one of these blocks, statements do run in some kind of order. But severalalwaysblocks can be active at once, each in its own tiny thread of simulated time.
module example(input wire a, input wire b, output wire y, output wire z);
assign y = a & b; // exists at all times
assign z = a | b; // also exists at all times, in parallel
always @(posedge a) begin
// a separate "always-on" reactor that wakes up on each
// rising edge of `a`
end
endmodule
The two assign lines aren't a sequence. They describe two pieces of combinational logic that the synthesis tool can lay out side by side. The always block is a third thing happening in parallel.
What "Signal" Means
In software, a variable holds a value until you change it. In Verilog, a signal holds a value continuously - and at every moment it depends on whatever drives it.
A wire is driven from outside (an assign, a sub-module's output port, an inout connection). A reg is driven from inside a procedural block. Both have a value at all times. There's no such thing as "uninitialized" in the way C means it - signals are either driven to a defined value, the special unknown x, or high-impedance z. We'll cover the last two in X and Z Values.
Clocks Change Everything
Once you bring a clock in, time starts mattering. A flip-flop is a tiny piece of hardware that captures its input value on the rising (or falling) edge of a clock and holds it until the next edge. The thing that lets you build counters, state machines, pipelines - anything that has memory - is the clock.
Notice q <= d rather than q = d. That's a non-blocking assignment - the workhorse of clocked logic. It says "at the next clock edge, schedule q to become whatever d is now." We'll dig into the rules in Blocking vs Non-blocking; for now, recognize that the assignment isn't pretending to be a software statement.
RTL: The Register Transfer Mental Model
Most synthesizable Verilog is written in a style called Register Transfer Level, or RTL. The idea is simple:
- Decide what state your circuit needs (the registers).
- For each register, describe two things: what resets it, and what combinational logic computes its next value.
- Wire combinational logic outputs to register inputs and you have a working circuit.
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
always @(*) begin
case (state)
IDLE: next_state = start ? RUNNING : IDLE;
RUNNING: next_state = done ? IDLE : RUNNING;
default: next_state = IDLE;
endcase
end
That's a two-state machine. The first always block is clocked - it's a flip-flop. The second is purely combinational - it's just an equation. Almost every state machine, counter, and pipeline you'll write follows that shape.
The Habits That Get You Stuck
If you're coming from software, here's a short list of habits to set down:
- "Variables update when I assign to them." Not on a clock edge they don't - a non-blocking assignment schedules the update for the end of the time step.
- "Statements execute top to bottom." Outside of procedural blocks, no. Inside a clocked block, sort of - but blocking vs non-blocking changes what "in order" actually means.
- "I'll allocate this when I need it." Hardware doesn't allocate. Every register and gate has to exist at synthesis time. The size of every vector is fixed.
- "This loop is fast - it's just one operation." A
forloop in synthesizable Verilog is unrolled into parallel hardware. A 64-iteration loop becomes 64 copies of the body, not a CPU instruction that runs 64 times.
You're not learning to write a program. You're learning to describe a circuit. The instinct to read top-to-bottom is exactly the wrong one and it takes a while to retrain.
What Comes Next
The next docs walk through getting a local toolchain (optional - the browser editor is fine), then writing your first module from scratch. We'll come back to the hardware-vs-software contrast every time something looks weird, because most of the "weird" parts have the same root cause: this isn't software.
Frequently Asked Questions
What is the difference between hardware and software in Verilog terms?
Software is a sequence of instructions executed by a CPU, one after another. Hardware - what Verilog describes - is a network of gates and wires that all carry signals at the same time. A Verilog file describes that network. The simulator imitates the parallel behavior; a synthesis tool turns it into actual silicon.
Does Verilog code run top-to-bottom like C?
No - and treating it that way is the most common beginner mistake. Top-level statements (assigns, module instances, always blocks) all 'exist' at once. Only inside procedural blocks - initial and always - does anything resembling sequential execution happen, and even there, non-blocking assignments break the illusion.
What does 'concurrent' mean in Verilog?
It means multiple statements describe parts of a circuit that all operate at the same time. Two assign statements in the same module aren't 'line 1 then line 2' - they're two pieces of hardware running in parallel, both reacting to their inputs continuously.
What is RTL design?
RTL stands for Register Transfer Level. It's a style of writing Verilog where you describe the circuit as registers (flip-flops) and the combinational logic that computes their next values. Most synthesizable Verilog is RTL. The level above is behavioral; the level below is gate-level.