A test passes only if its own checkers say so. Waveform inspection is for debugging a failure, never for declaring a pass.
The scoreboard and checkers count problems in one shared counter. At end of test the environment prints one verdict banner and calls $finish, or $fatal for a non-zero exit.
Banner | Exit code | Regression verdict |
|---|---|---|
| 0 | Pass |
| Non-zero | Fail, with the first problem line as its signature |
No banner, or banner and code disagree | Any | Fail as infrastructure trouble, never as a pass |
The exit code is the safety net. A script that greps for failure text alone would call a crashed simulation with an empty log a pass.
A run passes only if the PASS banner is present AND the exit code is zero. Everything else is a failure or an infrastructure error.
One subtlety: $fatal(1, msg) stops with finish number 1, not with exit code 1. The finish number is defined by IEEE 1800; the process exit status on $fatal is tool-defined. Icarus 13 and Verilator 5.048 exit with status 1, and commercial simulators also exit non-zero, which the regression scrapes alongside the banner.
The listing below is complete and runs under Icarus 13 and Verilator. An 8-bit adder is checked against a one-line reference model on 20 vectors.
The pattern is the whole discipline: count problems in errors, print exactly one verdict line, then end with $finish on PASS or $fatal(1, "FAIL: ...") on FAIL, so the exit code follows the verdict.
module adder8 (
input logic [7:0] a, b,
output logic [7:0] sum
);
timeunit 1ns; timeprecision 1ps;
assign sum = a + b;
endmodule
Four directed vectors cover zero, wrap-around, and the carry into bit 7. Sixteen loop vectors cover the rest, so the PASS line quotes 20 checks.
Every checker reports through one counter, so logging a problem and failing the test are the same act. A checker that prints a mismatch but never increments the counter lets the banner lie.
A max-error threshold aborts the run early. After the first few real problems the remaining transactions produce cascade noise, not information, and burn license hours.
The class listing below is a class with no top module, so this course's checks do not run it. It shows the production shape of the same counter. Reporting could be a function: $display and $fatal take no time, so IEEE 1800 allows them in functions, and UVM's own die() is a function. A task is used here so the report can later wait, for example on a drain event, before the verdict.
// not-run: class only, no top module
class tb_status;
static int error_count = 0;
static int max_errors = 10;
static task report_error(string who, string msg);
error_count++;
$display("[%0t] LOG_ERROR (%s): %s", $time, who, msg);
if (error_count >= max_errors) begin
$display("[%0t] aborting after %0d recorded problems", $time, max_errors);
print_verdict();
$fatal(1, "max problems reached");
end
endtask
static function void print_verdict();
$display("==================================================");
if (error_count == 0)
$display("PASS: test matched its reference on every check");
else
$display("FAIL: test recorded problems; see the log above");
$display("==================================================");
endfunction
endclassParse runtime plusargs once into a config object and let every component read that object. $value$plusargs then appears in exactly one place, which the seeds lesson shows.
The checker never incremented the shared counter. The banner follows the counter, so the verdict and the log disagree.
report_error in the class listing be a function instead of a task?Yes. $display and $fatal take no simulation time, and IEEE 1800 bars only time-consuming statements from functions. A task is a choice, not a rule: it leaves room to wait, for example on a drain event, before the verdict.
$fatal(1, msg). What does the 1 mean, and where does the exit code come from?The 1 is the finish number defined by IEEE 1800, not an exit code. The non-zero process exit is tool-defined; Icarus 13, Verilator 5.048 and commercial simulators all provide it, and the regression scrapes it.
The regression script must now grep several patterns and can mis-classify runs. Print the verdict exactly once, at the very end, with the problem count in the FAIL line.