Verilog · Chapter 1 · Foundations
Introduction & Overview of Verilog HDL
Verilog is the text that digital hardware is described in. This chapter teaches you to read it as hardware rather than as a program. You will meet a real Verilog module in the first few minutes, then unpack it: what logic the code describes, why hardware behaves concurrently instead of line by line, when a value changes immediately and when it changes only on a clock edge, what a simulator shows you, and what a synthesis tool can build from it. Everything later in this track — data types, operators, modeling styles, timing — assumes the way of reading you build here.
Foundation35 min readRTLHDLVerilogSimulationSynthesis
Chapter 1 · Page 1.1 · Foundations
1. The Engineering Problem
A modern chip contains an enormous number of transistors — billions on a large design. Nobody draws them.
Early digital design worked from schematics: an engineer drew gates and wired them together by hand in a CAD tool. That works for tens or hundreds of gates. It does not work for a million. Drawing by hand does not scale, and neither does reviewing, changing, or sharing a drawing that large.
So engineers stopped drawing hardware and started describing it. They write a text file that says what the hardware should contain and how it should behave. Software tools read that text and produce the actual gate-level implementation.
That text file is written in Verilog.
This is the idea the whole chapter rests on: Verilog is a way of describing hardware in text, so that tools can build it.
2. Why an HDL Exists
A hardware description language (HDL) is a language whose purpose is to describe digital circuits. Verilog is one; VHDL is another.
Text gives you two things a drawing cannot.
First, text scales. A counter is ten lines of Verilog. The same counter as a schematic is dozens of shapes and wires. Text is faster to write, easier to review, and works with ordinary version control — you can diff two revisions of a design the same way you diff two revisions of a program.
Second, you can describe behaviour instead of structure. You do not have to name every gate. You can write what the circuit should compute, and a synthesis tool works out an implementation that does it. That raises how much hardware one engineer can produce, which is what makes large designs practical at all.
There is a trade-off: you give up direct control over exactly which gates appear, in exchange for working at a scale where naming every gate is impossible.
Verilog dates from the mid-1980s, developed at Gateway Design Automation by Phil Moorby and colleagues, and became an IEEE standard in 1995.
2.1 Where the standards sit
Three names come up constantly. Keeping them straight now saves confusion later.
- Verilog — the hardware description language this track teaches. Its last standalone IEEE standard is IEEE 1364-2005.
- SystemVerilog / IEEE 1800 — a much larger language that contains Verilog. In 2009 the Verilog standard was merged into IEEE 1800, so since then there is no separate Verilog document being revised; IEEE 1800 is the maintained standard.
- What that means for you. SystemVerilog adds a great deal on top — especially for verification, where it is the normal choice — and design constructs many teams use for synthesisable code. But its design half is built on the Verilog core: modules, ports, nets, variables, continuous assignments, procedural blocks, clock edges.
This track teaches that core deliberately. It is the part that does not change, it is what you will read in existing code, and it is what the SystemVerilog track builds on.
3. Look at One First
Before any more theory, here is real Verilog. This is a complete, valid module.
module and_gate (
input wire a,
input wire b,
output wire y
);
assign y = a & b;
endmoduleThis code describes one small piece of combinational hardware: a two-input AND function. a and b are inputs, y is the output, and y is always the AND of the two inputs.
Do not worry yet about what each keyword means. The point of showing it now is the shape of the thing: a named block of hardware, with inputs and outputs, and a statement describing what the output is. Almost every Verilog file you ever open has that shape.
The next several sections build the way of reading that this code needs. Then §8 comes back and unpacks it line by line.
4. A Blueprint, Not a Recipe
Treat that as a mental model, not a literal account of what tools do. A blueprint has a one-to-one relationship with the building; Verilog does not have a one-to-one relationship with a specific set of gates. A synthesis tool is free to implement your description in many different ways, and it will choose based on the target technology and the speed, area, and power goals it is given. What stays fixed is the behaviour you described — not the exact gates that end up implementing it.
So the useful habit is not "which gate is this line?" It is:
What hardware behaviour does this code describe, and when does it change?
Two words matter there. Behaviour — because that is what you control. When — because hardware exists in time, and that is the part beginners miss.
5. Hardware Exists Concurrently
This is the single idea that separates reading Verilog from reading a program, so it is worth doing properly rather than just asserting.
Here is a small module with two continuous assignments:
module and_or (
input wire a,
input wire b,
input wire c,
output wire y
);
wire x;
assign x = a & b;
assign y = x | c;
endmoduleNow the same module with the two assignments written in the opposite order:
module and_or (
input wire a,
input wire b,
input wire c,
output wire y
);
wire x;
assign y = x | c;
assign x = a & b;
endmoduleThese two modules describe exactly the same hardware. Both describe:
- an AND function producing
xfromaandb, - an OR function producing
yfromxandc, - a connection carrying
xfrom the first to the second.
In the second version, the y assignment is not "running before x has a value" and it is not "waiting" for the other line. Neither line runs. Both describe connections that exist at the same time. If a changes, the AND logic responds, x changes, and because the OR logic is permanently connected to x, it responds too. The order you typed them in never enters into it.
Swapping two lines of a program usually changes the result. Swapping two continuous assignments changes nothing at all — which is what concurrent means: everything described inside a module is present simultaneously.
6. Where Verilog Fits in the VLSI Flow
Verilog is not the whole design process; it is one artefact near the start of it, and almost everything downstream is derived from it.
Where Verilog sits
data flowA mistake in that source is the cheapest kind to find and the most expensive kind to miss.
One thing from this picture matters for Chapter 1: your source is read by two very different tools, and they want different things from it. A simulator runs your description to show how it behaves; a synthesis tool produces hardware from it. Part of the language exists only to control a simulation and cannot be built at all. §12 is about that split.
The full industrial flow has many more stages than the six above — architecture, lint, timing analysis, physical design, sign-off. That is the next chapter, Typical VLSI Design Flow. This simplified spine is all Chapter 1 needs.
7. The Module — Inputs, Outputs, and Hierarchy
A module is a block of hardware with inputs and outputs. That is the whole idea; the formal wording — Verilog's unit of hardware description and of hierarchy — says the same thing more precisely. Everything you write in Verilog lives inside a module.
Whoever uses your module only needs to know that boundary; what is inside is your problem to get right.
Three terms, defined once:
- Port — a signal on the module's boundary. Ports are how a module connects to the outside world.
- Input / output — the direction of a port. An
inputis driven from outside; anoutputis driven by this module. - Internal signal — a signal declared inside the module that does not appear on the boundary.
wire x;in §5 was one: it carries a value between two pieces of logic and is invisible from outside.
7.1 Modules can contain other modules
A module can use another module inside it. This is called instantiation, and it is how real designs are built: small blocks combined into larger ones.
module and_pair (
input wire a0,
input wire b0,
input wire a1,
input wire b1,
output wire y0,
output wire y1
);
// Two separate instances of and_gate. Each instance is its own hardware.
and_gate u_and0 (.a(a0), .b(b0), .y(y0));
and_gate u_and1 (.a(a1), .b(b1), .y(y1));
endmoduleRead and_gate u_and0 (.a(a0), .b(b0), .y(y0)); as: "place a copy of and_gate here, call this copy u_and0, and connect its port a to my signal a0, its b to b0, its y to y0." The .port(signal) form is named port connection — the style to use, because it says explicitly which port goes where and so keeps working if the port list later changes order.
Real designs nest this many levels deep: a chip is a module containing subsystems, containing blocks, containing gates.
Module instantiation and port mapping get a full treatment in Design and Testbench Creation (Chapter 9). Here you only need the idea: modules nest.
8. Reading the AND Gate Line by Line
Back to the module from §3, now with everything it needs to be a real file:
// Turn off implicit nets: a typo becomes a compile error, not a silent bug.
`default_nettype none
module and_gate (
input wire a,
input wire b,
output wire y
);
// y is permanently the AND of a and b.
assign y = a & b;
endmoduleLine by line:
`default_nettype none— a compiler directive. Without it, Verilog silently invents a one-bit wire for any name it does not recognise, so a misspelled signal compiles cleanly and quietly does nothing. With it, the misspelling is an error. Put it at the top of design files. (Compiler directives are Chapter 7.)module and_gate ( ... );— begins a module and names it. The parentheses hold the port list.input wire adeclares a port nameda, directioninput, typewire;output wire ylikewise.wire— the type for a signal continuously driven by something else. It models a connection: it stores nothing and carries whatever drives it. (Nets are Chapter 5.1.1.)assign y = a & b;— a continuous assignment.yis always equal to the expression on the right, so wheneveraorbchanges,yfollows. (&is bitwise AND; operators are Chapter 10.)endmodule— ends the module. No semicolon.
What hardware does this describe? Logic that continuously produces the AND of two signals. There is no clock and nothing is stored: the output depends only on the inputs as they are right now. That is what combinational means.
8.1 Signals wider than one bit
Real designs move groups of bits — bytes, addresses, counters — not single wires. Verilog writes that with a range in square brackets:
`default_nettype none
module and8 (
input wire [7:0] a,
input wire [7:0] b,
output wire [7:0] y
);
assign y = a & b;
endmodule[7:0] means eight bits, numbered 7 down to 0. Bit 7 is the most significant; bit 0 is the least significant. A signal like this is called a vector.
assign y = a & b; is unchanged, and it now means something bigger: bit 0 of y is the AND of bit 0 of each input, bit 1 of y the AND of bit 1 of each, and so on. One line, eight independent AND functions, all operating at the same time. That is §5's concurrency again.
You will also meet literals written as 8'h00. Read that as "eight bits wide, in hex, value 00" — width, then base, then digits.
That is as much as Chapter 1 needs. Vectors, literals, arrays, and the full type system are Variables & Data Types (Chapter 5).
9. Driving It — Anatomy of a Testbench
You now have a module. How do you find out whether it works?
You write a second module whose job is to feed the first one inputs and report what came out. That second module is a testbench, and the module being tested is the DUT (device under test).
`timescale 1ns/1ps
`default_nettype none
module and_gate_tb; // no ports — a testbench has no boundary
reg a, b; // driven by this testbench
wire y; // driven by the DUT, observed here
and_gate u_dut (.a(a), .b(b), .y(y)); // the design under test
initial begin
$display("time(ns) a b | y");
$display("--------------------");
a = 0; b = 0; #10 $display("%8d %b %b | %b", $time, a, b, y);
a = 0; b = 1; #10 $display("%8d %b %b | %b", $time, a, b, y);
a = 1; b = 0; #10 $display("%8d %b %b | %b", $time, a, b, y);
a = 1; b = 1; #10 $display("%8d %b %b | %b", $time, a, b, y);
$finish;
end
endmoduleTake it apart:
- A testbench is also a module. Same keyword, same structure. Nothing new to learn.
- It has no ports. Nothing outside connects to it — it is the outermost thing in the simulation. That empty boundary is how you recognise a testbench at a glance.
- It declares signals for the DUT's ports and instantiates the DUT with named port connections, exactly as in §7.1.
`timescale 1ns/1pssets the file's time unit to 1 ns, which is what makes#10mean 10 ns. (timescaleis Chapter 7.3.)initial begin ... endis a procedural block: it starts once at the beginning of the simulation and runs its statements in order. Inside a procedural block, order does matter — the one place in Verilog where sequence is meaningful, because writing a test is naturally a sequence of steps. (Procedural blocks are Chapter 14.1.)#10waits 10 ns of simulation time.$displayprints a line;$finishends the run. (System tasks are Chapter 8.)- These are testbench constructs, not design constructs. They exist to script a simulation and describe no hardware. Keep them in test files, out of the modules you intend to build.
9.1 Why a is reg and y is wire
Two shortcuts you will hear are both wrong: "reg means input, wire means output," and "wire is combinational, reg is sequential." Here is the actual rule.
Verilog has two families of signal:
- A net (
wire) models a connection. It holds nothing; it carries whatever is driving it. You drive a net with a continuous assignment or by connecting it to a module output. - A variable (
reg) is assigned by procedural statements — the ones insideinitialandalwaysblocks — and keeps the value last assigned to it until something assigns it again.
In this testbench, a and b are set inside an initial block, so they must be variables: reg. y is driven by the DUT's output port, so it must be a net: wire.
The rule is about how a signal is assigned, not about direction and not about what hardware it becomes. reg is short for "register," which makes it sound like declaring one creates storage. It does not. Whether procedural code describes combinational logic, storage, or simulation-only behaviour depends entirely on how the block is written — §13 shows the same keyword doing two different jobs. Read reg as "procedurally assigned," never as "flip-flop." Nets and variables get their full treatment in Variables & Data Types (Chapter 5).
9.2 What the simulation prints
Running the DUT and the testbench together produces:
time(ns) a b | y
--------------------
10 0 0 | 0
20 0 1 | 0
30 1 0 | 0
40 1 1 | 1$time returns the current simulation time in the file's time unit, which `timescale 1ns/1ps set to 1 ns — so these are nanoseconds. Each line is printed after a #10 wait, so it reports the inputs that were applied 10 ns earlier, along with the output they produced.
Read the right-hand column on its own: 0, 0, 0, 1. That is the AND truth table. The module does what it claims.
10. Watching It — the Waveform
Printed text is fine for four lines. For anything real you want a waveform: every signal's value drawn against time. Here is the same run.
and_gate — four input combinations
8 cyclesTwo things to read off it:
- Inputs change, and the output responds. There is no clock here and none is needed. The output is a function of the inputs as they currently are, and it is never stored — cover the inputs and you cannot say what
yshould be. - Compare against what you expected. That is what a waveform is for. You predicted an AND truth table before opening it; each marker confirms one row.
11. Adding Time — Your First Flip-Flop
Everything so far has been combinational: output determined by inputs, right now. Useful hardware also needs to remember — counters, state machines and pipelines all hold a value from one moment to the next.
A circuit that remembers has state, and in Verilog you describe state by saying when a value should be captured. The smallest possible example:
`default_nettype none
module dff (
input wire clk,
input wire d,
output reg q
);
// On each rising edge of clk, q takes the value d had at that edge.
always @(posedge clk)
q <= d;
endmoduleRead it slowly:
dis the value waiting to be stored. It can change whenever it likes.clkis the clock — a signal that alternates between 0 and 1 and decides when storage happens.posedge clkmeans the rising edge: the instantclkgoes from 0 to 1.always @(posedge clk)means: every time that instant arrives, do what follows. Between those instants, do nothing.q <= d;samplesdat that edge and updatesqfrom the sampled value. Read<=for now as "the value ofdat this edge becomes the newq"; why it is written that way rather than=is Blocking and Non-Blocking Assignments (Chapter 14.3).output reg q—qis assigned inside a procedural block, so it is a variable, per §9.1.
The behaviour in one sentence: q changes only at a rising clock edge, and between edges it holds. In the AND gate the output had no choice — it followed its inputs continuously. Here d can wander around and q ignores it until an edge arrives.
What hardware does this describe? Something that stores a bit and updates it on a clock edge — a flip-flop. Whatever the target technology, the implementation needs a storage element with a clock input.
The triangle at the clock input is the standard symbol for edge-triggered: it marks the one input that decides when, rather than what.
11.1 Driving the flip-flop
The testbench needs one thing the previous one did not: a clock.
`timescale 1ns/1ps
`default_nettype none
module dff_tb;
reg clk;
reg d;
wire q;
dff u_dut (.clk(clk), .d(d), .q(q));
// Free-running clock: 10 ns period, so a rising edge every 10 ns
// starting at 5 ns.
initial begin
clk = 1'b0;
forever #5 clk = ~clk;
end
// Change d between edges, never on one, so what each edge captures
// is unambiguous.
initial begin
d = 1'b0;
#10 d = 1'b1; // at 10 ns
#20 d = 1'b0; // at 30 ns
#10 $finish; // at 40 ns
end
// Print 1 ns after each edge, once q has settled.
always @(posedge clk)
#1 $display("%8d edge: captured d = %b -> q = %b", $time, d, q);
endmodule 6 edge: captured d = 0 -> q = 0
16 edge: captured d = 1 -> q = 1
26 edge: captured d = 1 -> q = 1
36 edge: captured d = 0 -> q = 0One detail worth naming: the stimulus changes d at 10 ns and 30 ns — between rising edges, never on one — so there is no doubt about which value each edge sampled. Driving a signal at the exact instant a flip-flop samples it is genuinely ambiguous, and disciplined testbenches avoid it.
11.2 The sequential waveform
dff — q follows d, but only at rising clock edges
8 cyclesWork through it once, and the mental model is yours:
- At edge 1,
dis 0. Soqbecomes 0. - Between edge 1 and edge 2,
drises to 1.qdoes not move. There is no edge, so nothing is sampled. This is the important bit:dchanged andqdid not. - At edge 2,
dis 1. Soqbecomes 1. - At edge 3,
dis still 1.qstays 1. - At edge 4,
dis back to 0. Soqbecomes 0.
Note what decides the delay between d changing and q changing: not a fixed clock period, but how long it is until the next active edge. Here d rose in the middle of a cycle, so q waited until the following edge. Had d risen shortly before an edge, that same edge would have sampled it. The rule is only ever "at each active edge, sample d; between edges, hold."
You now have the two halves of the model — combinational, which changes whenever its inputs change, and sequential, which changes only at a clock edge. For any line of Verilog you read from here on, ask which one it is. Asking that question is most of what reading RTL consists of.
12. Simulation and Synthesis — Two Questions, One Language
The same Verilog file is read by two kinds of tool, and each is asking a different question.
A simulator asks: "What will this design do over time?" It models the behaviour your code describes and runs it against your testbench's stimulus, recording every signal at every moment. Output: logs and waveforms.
A synthesis tool asks: "What hardware can implement this behaviour?" It maps your description onto the resources of a chosen target — standard cells for an ASIC, lookup tables and flip-flops for an FPGA — optimising as it goes. Output: an implementation that later stages turn into a working device.
Because the questions differ, so does what each tool can use:
| Construct | In simulation | For synthesis |
|---|---|---|
assign y = a & b; | Re-evaluates whenever an operand changes | Combinational logic |
always @(posedge clk) q <= d; | Updates q at each rising edge | A storage element with a clock |
#10 | Advances simulation time by 10 units | No hardware meaning — keep it out of design code |
$display, $finish | Prints, and ends the run | Not hardware — testbench services |
initial | Runs once at the start | Target-dependent — see below |
On initial specifically. You will read that initial is never synthesisable. That is too absolute: it is overwhelmingly a simulation and testbench construct, and that is how this track uses it, but some FPGA flows do support limited initialisation. Whether it means anything depends on your target and tool. So rather than memorising a rule that is not universally true, adopt the portable habit — give state a defined starting value through your design's intended reset methodology, not through an initial block. Reset arrives in RTL Designing (Chapter 3); the flip-flop in §11 was left without one only to keep the first sequential example small.
13. Common Beginner Mistakes
Three mistakes, all of them mental-model failures rather than typos. Each one follows directly from something earlier in this chapter.
Mistake A — expecting source order to be execution order
The bug this produces is the accidental second driver. A beginner wants y to be a & b normally and c in some other situation, and reaches for a second assign.
assign y = a & b;
assign y = c; // a second driver, not a replacementBoth statements describe permanent connections to y. Two sources now drive the same net at once, and where they disagree simulation resolves y to x — unknown.
assign y = use_c ? c : (a & b);One connection to y. The choice between the two values is made by logic inside the single driver, which is what a multiplexer is.
The general rule: a net should have exactly one driver. If you want a signal to take different values in different situations, express the choice as logic, not as two statements.
Mistake B — thinking reg always means a hardware register
reg says how a signal is assigned, not what hardware it becomes. The surrounding block is what decides.
reg y;
always @(*) begin
y = a & b;
endThis block reacts to its inputs. y is re-derived whenever a or b changes, with no clock involved — the same behaviour as assign y = a & b;. No storage. y is reg only because it is procedurally assigned.
reg q;
always @(posedge clk) begin
q <= d;
endThis block runs only at rising clock edges. Between edges q keeps its value, so storage is described. Same keyword, different hardware — because the sensitivity is different.
The details of always @(*) are Behavioural Modeling (Chapter 14). The point here is only that the declaration tells you nothing on its own — look at what triggers the block.
Mistake C — letting testbench habits into a design module
A design module that kept its testbench habits
SIMULATION / HARDWARE MISMATCH// Intended as a synthesisable design block.
module pulse_gen (
input wire clk,
output reg pulse
);
initial pulse = 1'b0; // set the starting value here
always @(posedge clk) begin
#2 pulse <= ~pulse; // "wait 2 ns, then toggle"
end
endmoduleThe RTL simulation looks exactly right — pulse starts at 0 and toggles every clock. After synthesis the tool warns about the delay, and the implemented hardware does not match: the 2 ns offset is gone, and on an ASIC target pulse has no guaranteed starting value.
Two testbench constructs ended up in a design module.
#2 is a simulation time control: it tells a simulator to pause and describes no hardware, so there is nothing for synthesis to build from it. Whether the tool warns, drops it, or rejects the file, simulated and implemented behaviour diverge.
initial pulse = 1'b0; was doing the job that reset should do, and its effect at power-on depends on the target and tool — an FPGA flow may honour it, an ASIC flow will not.
module pulse_gen (
input wire clk,
input wire rst_n, // active-low reset
output reg pulse
);
always @(posedge clk) begin
if (!rst_n) pulse <= 1'b0; // defined starting value
else pulse <= ~pulse; // timing comes from the clock
end
endmoduleTiming now comes from the clock, which is the only thing that sequences hardware, and the starting value comes from an explicit reset that exists in the implemented design. Simulation and hardware agree, on any target.
14. Practical Debugging
When a design does not do what you expected, the productive routine is the same every time. It is a search for the first thing that is wrong, not an inspection of the thing that looks wrong.
- Find the first wrong signal, at the first wrong moment. Open the waveform and move backwards in time until the last point where everything was still correct. Wrong values spread, so where you noticed the problem is almost never where it started.
- Find what drives that signal. Every signal has one source — a continuous assignment, a procedural block, or a module output. Check its inputs, then repeat on those. The bug is where a source's inputs are right but its output is not.
- State what you expected before you look. Say it precisely — "at this edge,
qshould capture 1." Vague expectations produce vague debugging. Then compare, and see which half was wrong: your expectation, or the design. - Check starting values. Much "strange behaviour at the beginning" is state that was never given a defined value. A signal showing
x(unknown) from the start means storage with no reset. - Shrink the failing case. A failure you can reproduce in three clock cycles is a failure you can understand.
Simulators also have precise rules about the order in which events are processed within a single moment of simulation time — the reason <= exists. A real topic, but not a first-chapter one: see Timing Regions (Chapter 19) and Blocking and Non-Blocking Assignments (Chapter 14.3).
15. Where Verilog Is Used
Briefly, so you know what you are learning toward.
RTL design. Writing the synthesisable modules that become the chip — datapaths, control logic, interfaces. This is the work everything in this track leads to.
Verification. Writing the test code that proves a design is correct before it is built, which is at least as much effort as designing it. Verification teams typically work in SystemVerilog, but every testbench begins with what §9 showed: instantiate the design, drive its inputs, check its outputs.
FPGA and ASIC. Two destinations for the same source.
| ASIC | FPGA | |
|---|---|---|
| What it is | Custom silicon manufactured for one design | An off-the-shelf chip of programmable logic, configured to behave like your design |
| Cost shape | Very high one-time cost, very low per unit | Low one-time cost, higher per unit |
| Turnaround | Months from finished design to first chips | Minutes to hours from edit to running hardware |
| Suits | High volume products | Prototyping, lower volumes, designs that change |
The Verilog you write can be largely the same for both; what differs is the synthesis tool and the hardware it targets. FPGAs are also how most people first run their own RTL on real hardware, which makes them an excellent way to learn.
To follow along you need a simulator. Free options such as Icarus Verilog and Verilator are more than enough for everything in these chapters, and the language is the same as in the commercial tools production teams use. Learn one well before trying a second.
16. Knowledge Check
Answer these in your own words before reading the answers. If an answer is only a definition you can recite, it has not landed yet.
17. Learning Roadmap
The Verilog track is nineteen chapters in five groups. Where this chapter sits, and what follows:
- Foundations (Chapters 1–4). This page, then Typical VLSI Design Flow for the full path from specification to silicon, RTL Designing for the register-plus-logic style that real designs are written in, and Lexical Conventions for the rules every line of Verilog obeys.
- Data & Variables (Chapters 5–7). Variables & Data Types develops the nets and variables introduced in §9.1, along with vectors and arrays. Then constants, and the compiler directives that
`default_nettypeand`timescalebelong to. - System Tasks & Design (Chapters 8–9). System Tasks & Functions covers
$displayand its relatives properly; Design and Testbench Creation turns §9 into a full method, including module instantiation and port mapping. - Operators & Modeling (Chapters 10–14). The operator set, then the three modeling styles: gate-level, dataflow (where
assignis developed in depth), and behavioural (procedural blocks, conditionals, loops, and the=versus<=question this chapter deferred). - Advanced Topics (Chapters 15–19). Tasks and functions, user-defined primitives, delay modeling, timing checks, and Timing Regions — the event-ordering rules underneath everything else.
Chapters 2 and 3 are the natural next steps and are best read in order.
18. Exercises
Work these before reading the answers below them. Writing a wrong answer and finding out why is worth more than reading a right one.
Exercise 1 — Code to hardware
For each snippet, say what hardware it describes, and whether the output changes as soon as an input changes or only at a clock edge.
assign y = a | b;always @(posedge clk)
count_q <= count_q + 1'b1;assign y = a & b;
assign z = y | c;Exercise 2 — Write a multiplexer
A 2-to-1 multiplexer passes one of two inputs through to its output, chosen by a select signal. Write a module mux2 with:
- inputs
sel(1 bit),aandb(8 bits each), - output
y(8 bits), - behaviour: when
selis 0,yisa; whenselis 1,yisb.
Use `default_nettype none and a single continuous assignment. The conditional operator cond ? value_if_true : value_if_false is the usual way to write it.
Then say what hardware you have described, and whether y changes immediately or at a clock edge.
Exercise 3 — Predict a flip-flop waveform
Using the dff module from §11, determine q after each rising clock edge. d is listed as the value it holds at the moment of each edge.
| Rising edge | d at the edge | q after the edge |
|---|---|---|
| 1 | 1 | ? |
| 2 | 1 | ? |
| 3 | 0 | ? |
| 4 | 0 | ? |
| 5 | 1 | ? |
Then answer: between edge 2 and edge 3, d falls from 1 to 0. What is q during that interval, before edge 3 arrives?
Answers
Exercise 1a. Combinational logic producing the OR of a and b. y changes whenever a or b changes — no clock is involved and nothing is stored.
Exercise 1b. A counter: storage holding count_q, with combinational logic computing count_q + 1 feeding back into it. count_q changes only at rising clock edges; between edges it holds. Both kinds of logic are present — the adder is combinational, and the storage is what makes it a counter rather than just an adder.
Exercise 1c. Two pieces of combinational logic in series: an AND producing y, and an OR producing z from y and c. Both change whenever a relevant input changes; no clock, no storage. The two statements could be written in either order and describe the same hardware — §5 again.
Exercise 2.
`default_nettype none
module mux2 (
input wire sel,
input wire [7:0] a,
input wire [7:0] b,
output wire [7:0] y
);
// sel = 0 selects a; sel = 1 selects b.
assign y = sel ? b : a;
endmoduleCheck the polarity deliberately: sel of 1 yields b, sel of 0 yields a. That matches. Writing sel ? a : b gives a working multiplexer with the selection reversed — a bug that compiles cleanly and is easy to miss.
The hardware is combinational: eight 2-to-1 selections in parallel, one per bit. y changes as soon as sel, a or b changes; nothing is stored, because a multiplexer chooses rather than remembers. Note that a and b are both connected at all times — sel decides which reaches the output, not which one "runs."
Exercise 3.
| Rising edge | d at the edge | q after the edge |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 0 | 0 |
| 4 | 0 | 0 |
| 5 | 1 | 1 |
q simply takes the value d had at each edge.
The follow-up is the one that matters. Between edge 2 and edge 3, d falls to 0 — and q stays 1 for that whole interval, because q only moves at an edge and the next edge has not arrived. If you answered that q follows d down immediately, re-read §11.2: that is the combinational reflex this chapter exists to replace.
19. Summary
The ideas worth carrying out of this chapter:
- Verilog describes hardware. It is text that says what a circuit contains and how it behaves — not instructions that run in order.
- Modules are blocks of hardware with inputs and outputs, and they nest. Instantiating a module twice creates two pieces of hardware.
- Hardware exists concurrently. Everything described in a module is present at the same time. Reordering continuous assignments changes nothing.
assigndescribes combinational logic — a permanent relationship, with no memory. Its output depends only on its current inputs.- Storage introduces state, and state is what lets a design remember anything.
- Clock edges decide when sequential state updates. Between edges, stored values hold — no matter what their inputs do.
- Testbenches drive and observe a design in simulation. They are modules with no ports, and they use simulation-only constructs that do not belong in design code.
- Simulation predicts behaviour over time; synthesis maps a synthesisable description onto hardware. The same file, two different questions.
And the habit underneath all of it — the question to ask of every line of Verilog you read from here on:
What hardware does this code describe, and when does it change?
If you can answer both halves, you are reading Verilog the way an RTL engineer reads it. Chapter 2 widens the view to the full design flow this description sits inside.
Related Tutorials
- Typical VLSI Design Flow — Chapter 2; the full path from specification to silicon.
- RTL Designing — Chapter 3; registers, combinational logic between them, and reset.
- Design and Testbench Creation — Chapter 9; the full method behind §9's testbench.
- Continuous Assignments — Chapter 13.1;
assignin depth. - Blocking and Non-Blocking Assignments — Chapter 14.3; why the flip-flop used
<=.
Standards & specifications
- Governing standard
- IEEE Std 1364 (Verilog)(opens IEEE in a new tab)
Defines the Verilog language and its simulation semantics, including the event scheduling model. Synthesis support is defined by tools, not by this standard.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the Verilog HDL curriculum.
