System Tasks and Compiler Directives
System tasks (prefixed $) and compiler directives (prefixed `) are Verilog's built-in tooling vocabulary — not hardware description at all, but the debugging, file I/O, and preprocessing mechanisms that make writing and building real Verilog projects practical.
Display system tasks
initial begin
$display("Reset complete at time %0t", $time);
$monitor("t=%0t a=%b b=%b sum=%b cout=%b", $time, a, b, sum, cout);
end
| Task | Behavior |
|---|---|
$display | Prints its arguments once, immediately, like a formatted print statement |
$monitor | Prints its arguments automatically, once per simulation time step, whenever any listed variable changes — a "live watch," not a one-shot print |
$strobe | Like $display, but prints at the very end of the current time step, after every other statement scheduled for that step has executed — useful for guaranteeing you're printing the settled value rather than one mid-update |
$time / $realtime | Returns the current simulation time, typically used as an argument to the tasks above |
All of these are simulation-only — they have no synthesis meaning whatsoever, and appear exclusively in initial/always blocks used for debugging or in testbenches, never in RTL meant to become real hardware.
Ending or pausing a simulation
Two more system tasks control the simulation run itself rather than printing anything: $finish ends the simulation entirely, exactly as if the testbench had run off the end of its code — most simulators exit back to the shell (or the GUI's "simulation ended" state) once it executes. $stop instead suspends the simulation, dropping into the simulator's interactive command prompt without actually ending the run — useful for pausing mid-simulation to inspect signals by hand before deciding whether to continue or finish. Without an explicit $finish (or a forever loop that never calls it, timing out on some simulators' default time limit instead), a testbench with nothing left to execute simply idles at whatever time its last statement ran.
Reading memory contents from a file
initial begin
$readmemh("rom_contents.hex", mem); // hex-formatted file
// $readmemb("rom_contents.bin", mem); // binary-formatted file, same idea
end
$readmemh/$readmemb load an array (like the ROM array from Arrays and Memories) from an external text file, one value per line — the practical, scalable alternative to hand-writing dozens of mem[n] = ...; assignment lines for real ROM content.
Compiler directives
Directives are processed by Verilog's preprocessor, before the compiler proper ever sees the code — textual substitution and conditional inclusion, conceptually identical to the C preprocessor:
`define WIDTH 8
`define ADD_OP 2'b00
module alu (
input [`WIDTH-1:0] a,
input [`WIDTH-1:0] b,
input [1:0] op,
output reg [`WIDTH-1:0] result
);
| Directive | Purpose |
|---|---|
`define | Text macro substitution — `WIDTH is replaced with 8 everywhere it appears, textually, before compilation |
`include | Pastes another file's contents in at that point — used for sharing common definitions across multiple modules |
`timescale | Sets the time unit and precision ( `timescale 1ns/1ps — 1ns per time unit, simulated with 1ps resolution) that #delay values and $time are interpreted against |
`ifdef / `ifndef / `else / `endif | Conditional compilation — include or exclude a block of code depending on whether a macro is defined, commonly used to gate simulation-only debug code out of a synthesis run |
`ifdef SIMULATION
initial $display("Testbench starting");
`endif
`ifdef SIMULATION/ `endif here means the $display line only exists in builds where a SIMULATION macro was defined (typically via a simulator command-line flag) — a clean way to keep debug-only code physically present in the source for anyone reading it, while guaranteeing it's compiled out entirely for synthesis, rather than relying on synthesis tools to silently ignore $display (which they do, but making the exclusion explicit is more robust and self-documenting).
What's next
Debugging tools and directives round out the practical vocabulary needed to actually build and simulate a Verilog project. The next page puts it to use directly — writing a minimal, stand-alone testbench in plain Verilog, the bridge into the much deeper verification content covered by Testbench and UVM.