You run a command. The screen fills with text and the prompt comes back. Your stomach drops before your brain catches up: did that work, or did something break? Beginners answer by feeling. This lesson replaces the feeling with a habit.
The tool printed something. Is it an error, a warning, or just information, and what do I do?
Almost every tool message carries three parts. First comes the location: the file and line the tool is talking about, written as file:line. Then the severity: how bad the tool thinks this is, often the word warning or error. Then the text: what the tool found, sometimes with a hint about the fix.
Severity decides what happens next, and the exit status proves it. The exit status is the number a program returns when it finishes: 0 means success and anything else means failure. An error stops the step and the exit status turns nonzero, so chained steps never run on a broken result. A warning lets the run continue with status 0, which is exactly why warnings deserve respect: they are the problems the tools let through.
When several messages arrive together, read the first error and fix that one before touching the rest. Later errors are often consequences: one real fault confuses the tool, and the confusion arrives as extra messages further down. Fix the first, rerun, and watch how many others vanish with it.
Look at the figure below. It splits the syntax error from command 2 of this lesson into its labelled parts. Read the labels under each bracket, then use the same split on every message that follows.
Each message below comes from the macOS bench in the previous lesson: Icarus Verilog 13.0, Verilator 5.048, Yosys 0.69+post, GNU Make 3.81. Read the command line first, then the output, then the paragraph that says what it means and what to do.
# 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. It is a warning, not an error: there is no exit status line, so the status is 0 and the run continued. The design file declares no timescale, which sets the time unit and precision, while the testbench has one, so Icarus cannot be sure both files mean the same thing by a delay. Add a timescale line to the design file and the warning goes away.
# 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 the same Icarus on macOS. It is an error: the location errdemo/dut.sv:14 names the file and line, the text says what is wrong, and the run stops with exit status 2. Open the named file at the named line and read the statement there; the follow-up line about the assignment target narrows it further. Fix that statement and rerun before reading any message below it.
# 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]This output came from the same Icarus on macOS. It is an error during elaboration, which means the step where the tool assembles the design hierarchy before simulating: the -s mini_control flag names a top module that no file provides. The likely cause is a misspelled module name, so compare the -s name letter by letter against the module declaration. Correct the flag and rerun; nothing is wrong with the design code itself.
# captured terminal output
$ verilator --lint-only -Wall --top-module w widthdemo/w.sv
%Warning-WIDTHEXPAND: widthdemo/w.sv:2:45: Operator ADD expects 8 bits on the LHS, but LHS's VARREF 'count' generates 4 bits.
: ... note: In instance 'w'
2 | always_ff @(posedge clk) count <= count + 8'd1;
| ^
... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=5.048
... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message.
%Warning-WIDTHTRUNC: widthdemo/w.sv:2:36: Operator ASSIGNDLY expects 4 bits on the Assign RHS, but Assign RHS's ADD generates 8 bits.
: ... note: In instance 'w'
2 | always_ff @(posedge clk) count <= count + 8'd1;
| ^~
... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=5.048
... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message.
%Error: Exiting due to 2 warning(s)
[exit status 1]This output came from Verilator 5.048 on macOS. Both messages are warnings: each names the file, line, and column, the operator involved, and the exact bit widths that disagree. The 4-bit count meets an 8-bit constant, so one operation widens and the assignment truncates back. The run still stops with exit status 1 because --lint-only treats warnings as fatal for its verdict. Lint means reading code without running it, so the lint step judges style and risk instead of behavior. Size the constant to the target width and rerun; do not silence the warning, because a truncated bit is a real bug wearing a warning label.
# captured terminal output
$ yosys -Q -T -p 'read_verilog -sv yodemo/bad.sv; hierarchy -check -top bad; proc; opt; check -assert' | tail -n 4
ERROR: Found 1 problems in 'check -assert'.
5. Executing CHECK pass (checking for obvious problems).
Checking module bad...
Warning: Wire bad.\z is used but has no driver.
Found and reported 1 problems.This output came from Yosys 0.69+post on macOS. The warning names a wire that is read but never driven, and the check -assert step then reports it as a problem. Note the missing piece: this command pipes through tail, so no exit status line is shown and the status belongs to tail, not to Yosys. Run without the pipe and check -assert exits 1, while plain check without -assert would warn and still exit 0. Drive the wire or remove the read, and keep -assert on so the check keeps failing loudly on the next undriven wire.
# captured terminal output
$ make
Makefile:2: *** missing separator. Stop.
[exit status 2]This output came from GNU Make 3.81 on macOS. It is an error: missing separator on line 2 means a recipe line, which is a command line inside a Makefile, starts with spaces where Make demands a tab character. The run stops with exit status 2 before doing anything. Replace the leading spaces with a tab and rerun. When an editor shows tabs and spaces alike, turn on visible whitespace for Makefiles.
Only message 1, the timescale warning, lets the run continue: it has no exit status line, so the status is 0. Messages 2, 3, 4, and 6 each end with a nonzero exit status line, and message 5 shows an asserting check stopping on a problem: its own exit status is hidden here because the command pipes the output through tail, so rerun it without the pipe to see the nonzero status. The exit status line is the fastest severity test you have.
Never ignore a warning because the run still passed. A warning with exit status 0 is a problem the tool chose not to stop for, not a problem that went away. Message 1 passes today and confuses timing tomorrow; fix warnings the same week they appear.
Fix the first error, not the last. One fault early in a file confuses everything after it, so the last message is usually smoke from the first fire. Rerun after each fix and let the vanished messages stay vanished.
Fix the code instead of muting the check. Verilator even prints the comment that would silence each warning, and reaching for it is tempting. Muting hides the next real width bug along with this one. RTL course: Coding Lint shows which warnings are safe to waive and which always mean broken hardware.
Rule: read the first error first; everything after it may be a consequence.
Message 1, Icarus: warning, run continued with status 0; first try is adding a timescale line to the design file. Message 2, Icarus: error, exit status 2; first try is opening errdemo/dut.sv at line 14 and fixing the statement. Message 3, Icarus: error during elaboration, exit status 1; first try is comparing the -s name against the real module name. Message 4, Verilator: two warnings that still stop the lint with exit status 1; first try is sizing the constant to the 4-bit target. Message 5, Yosys: a warning plus a failed asserting check; first try is driving the wire or removing the read. Message 6, Make: error, exit status 2; first try is replacing leading spaces with a tab on line 2.
Not yet. Green means the steps ran, not that the design is right: message 1 is exactly this case and it flags a real ambiguity about time units. Read the warning, decide whether it points at your code, and either fix the code or record why the warning is safe. Unexamined warnings accumulate until one of them is the bug.
With the first error, even though the fifth looks easier. The first fault confuses the tool's reading of the rest, so errors two through five may describe confusion rather than code. Fix the first, rerun, and reclassify what remains before editing anything else.
Every message has a location, a severity, and text; the exit status decides whether the next step runs.
Errors stop the run with a nonzero status; warnings let it continue, which is why warnings need reading too.
Read the first error first, fix it, and rerun, because later messages are often consequences of the first fault.