Why did my code compile but fail to simulate?
Your file compiles with no errors, so you start the simulation. It stops at once: the top module is missing. Nothing you wrote changed between the two commands, yet the first succeeded and the second failed. The compile told the truth, but it answered a smaller question than the one you asked.
A design passes through five stages, and each stage asks its own question of the same source. Compile, also called parsing, asks whether the text is legal SystemVerilog, the hardware description language this course uses: matching semicolons, known keywords, complete statements. Elaboration asks which modules, instances, and parameters exist and which one sits on top: it builds the design hierarchy from your files and binds every instance to its definition.
Simulation asks what values change over simulated time: it runs the design together with its testbench, the code that drives the inputs and checks the outputs, edge by edge. Synthesis asks what hardware can be built from the source: it translates behavior into gates and wires. Lint asks what looks suspicious without running anything: it reads the text and flags patterns that often mean bugs.
The same file gives five different answers because each stage reads it with different eyes. A missing semicolon stops the compile. A misspelled top name passes the compile and stops elaboration, because the files are legal but the requested hierarchy does not exist. A wrong increment passes both and fails in simulation, because only running the design reveals what it computes. A design that simulates correctly can still fail synthesis when it uses behavior that maps to no hardware, and lint can flag a width mismatch that every other stage accepts.
The table below names each stage, the question it asks, the tool that answers it in this course, and the failure you meet when that stage is the one complaining.
Step | Question | Tool in this course | Example failure |
|---|---|---|---|
Compile (parse) | Is it legal SystemVerilog? | Icarus ( |
|
Elaborate | Which modules exist, and which is the top? | Icarus ( |
|
Simulate | What values change over time? | Icarus ( |
|
Synthesize | What hardware can be built? | Yosys | Code that simulates but describes no hardware (lesson 4.5) |
Lint | What looks suspicious without running? | Verilator ( |
|
The figure below draws the five stages as two lanes plus lint. Simulation needs the testbench; synthesis and lint read the source alone. Look at which boxes share a lane, and at the dashed arrow into lint, which only reads and never runs.
The listing below is the design every stage in this lesson reads: mini_controller, the 4-bit event counter that runs through this whole course. Its rule is a priority list: reset first, then clear, then count, then hold.
`timescale 1ns/1ps
// Counts accepted events, 4 bits, wraps 15 -> 0.
// Priority: reset > clear > count > hold. Reset is synchronous, active low.
module mini_controller (
input logic clk,
input logic rst_n,
input logic event_valid,
input logic clear_count,
output logic [3:0] count
);
always_ff @(posedge clk) begin
if (!rst_n)
count <= 4'd0;
else if (clear_count)
count <= 4'd0;
else if (event_valid)
count <= count + 4'd1;
end
endmoduleThis listing is the complete design: clock and control inputs in the port list, one 4-bit count output, and a register that updates once per rising clock edge.
Compile it with Icarus and a correct file answers with silence. A file with a small defect still builds, and the tool tells you what it tolerated. The project keeps a copy of this design with its first line, the timescale, removed; compiling that copy says:
# captured terminal output
$ iverilog -g2012 -Wall -s tb_mini_controller -o build/ts.vvp tsdemo/dut.sv tsdemo/tb.sv
warning: Some design elements have no explicit time unit and/or
: time precision. This may cause confusing timing results.
: Affected design elements are:
: -- module mini_controller declared here: tsdemo/dut.sv:3This output came from Icarus Verilog 13.0 on macOS. The warning names the module and the missing time unit, then the build continues: a warning is advice, while an error stops the run. When a later lesson shows a build with no output at all, that silence means every stage passed without even advice to offer.
errdemo/dut.sv:14: syntax error. (b) error: Unable to find the root module "mini_control" in the Verilog source. (c) ERROR edge 1: expected count=1 got 2. Which stage produced each message, and why?(a) Compile: the text breaks a language rule, so parsing stops at line 14. (b) Elaboration: every file parsed, but no module named mini_control exists, so there is no hierarchy to run. (c) Simulation: the design built and ran to edge 1, where the check comparing the count against its expected value failed. The later the stage, the more of your intent was understood before something broke.
Now cause two of the failures yourself and read the full output of each. First, a design copy with a missing semicolon:
# captured terminal output
$ iverilog -g2012 -s mini_controller -o build/err.vvp errdemo/dut.sv
errdemo/dut.sv:14: syntax error
errdemo/dut.sv:15: Syntax in assignment statement l-value.
[exit status 2]This output came from Icarus Verilog 13.0 on macOS. One missing semicolon produces two error lines: the parser trips at line 14 and misreads line 15 as a consequence. The fix is a single character at line 14, so always start with the first error: later lines often describe the same mistake.
Second, a correct design with the wrong top name on the command line:
# captured terminal output
$ iverilog -g2012 -s mini_control -o build/top.vvp rtl/mini_controller.sv
error: Unable to find the root module "mini_control" in the Verilog source.
: Perhaps ``-s mini_control'' is incorrect?
1 error(s) during elaboration.
[exit status 1]Same tool and platform. Every file parsed, so compile has nothing to report; then elaboration finds no module called mini_control and stops before simulating anything. The fix names the module exactly as written, and when the file holds a testbench you name the testbench, as the next lesson shows.
A third design passes both stages and still fails: change the increment so each event adds two, and the run reaches edge 1 before reporting expected count 1 but got 2. Lesson 4.2, First Simulation runs that exact experiment and reads every line of its output, so this lesson does not repeat it. Notice what the journey tells you: reaching an edge error proves compile and elaboration already passed.
Reading a clean compile as a passing test. A compile with no output says the text is legal; it says nothing about behavior. Only the testbench verdict speaks about behavior, which is why the next lesson ends at a PASS line instead of a quiet prompt.
Fixing the last error first. The first error often causes the rest, as the two error lines from one missing semicolon show. Start at the top, fix one thing, and rerun before touching anything below.
Rule: a clean compile says the text is legal; only a passing check says the design behaves.
The top name on the command line does not match any module in the files. The text parsed, so the code is legal and the name is wrong: check the -s flag against the module names, letter by letter.
Simulation failed. The design built and ran, so compile and elaboration are innocent; the bug is in what the design computes or what the testbench drives, and the edge number tells you exactly which cycle to read first.
No. A green simulation covers only the cases it drives, while lint read the text itself. Fix the width first, then rerun the loop; lesson 4.4 shows this exact repair.
Compile checks the text, elaboration builds the hierarchy, simulation runs it over time, synthesis maps it to gates, and lint reads it without running.
One command often runs several stages, so read the message to learn which stage complained.
A green compile starts testing; it never delivers the verdict.