System Tasks

File I/O ($fopen), printing ($display, $monitor).

Darshan N
Updated: 19 March 2026
11 min read

System tasks in Verilog are built-in tasks provided by the simulator to interact with the simulation environment. They handle essential simulation utilities such as displaying output, monitoring signal changes, reading and writing files, and controlling simulation flow. For any engineer writing testbenches or debugging RTL designs, system tasks are indispensable tools that bridge the gap between hardware description and software-like simulation control.

Verilog System Tasks OverviewDisplay$display$write$strobe$monitorFile I/O$fopen$fclose$fdisplay$fwrite$fmonitorSim Control$finish$stop$time$realtimeData Loading$readmemb$readmemh$randomAll system tasks are prefixed with $ and are simulator built-ins
Figure 1: Categories of Verilog system tasks used in simulation and verification

Core Concept Explanation

System tasks in Verilog always begin with the dollar sign $ prefix. This distinguishes them from user-defined tasks. The simulator intercepts calls to these tasks and executes corresponding internal routines. They do not synthesize into hardware and are meant exclusively for simulation purposes. Using them inside synthesizable RTL code has no effect on logic but can cause synthesis tool warnings.

$display and $write

The $display task prints formatted text to the simulation console followed by an automatic newline. It evaluates all its arguments at the moment it is called, which is at the end of the current simulation time step. The $write task works identically but does not append a newline. Both accept format specifiers similar to C's printf: %b for binary, %h for hex, %d for decimal, %s for string. These are the most commonly used tasks during RTL debugging.

$monitor

The $monitor task continuously watches a list of signals and automatically prints the format string whenever any listed signal changes value. Only one $monitor can be active at a time in a simulation. If $monitor is called again, the previous one is replaced. This makes it very useful for passively logging bus transitions without manually placing $display at every assignment.

$strobe

The $strobe task prints at the end of the current simulation time step, after all events at that timestep have settled. This guarantees that non-blocking assignments have completed before the value is printed. This is critically different from $display which may print intermediate glitch values if triggered by a blocking procedural statement in the middle of a time step.

File I/O: $fopen and Related Tasks

The $fopen function opens a file and returns an integer file descriptor called a multi-channel descriptor (MCD). The MCD is then passed to file-targeting versions of the display tasks such as $fdisplay, $fwrite, $fmonitor, and $fstrobe. The file must be closed using $fclose at the end of simulation to flush buffers and release the file handle. File I/O is essential for testbenches that log large amounts of simulation data to external log files for post-processing.

The syntax for opening a file is: integer fd = $fopen("filename.txt", "w");. The second argument specifies the mode: "w" for write, "r" for read, "a" for append. When mode is omitted in older Verilog-2001 style, the file is opened as a write-only MCD. SystemVerilog extended this with ANSI-C style modes for full compatibility.

Mathematical Expression

System tasks do not carry a mathematical formula per se, but the format specifier width control follows a defined pattern. The specifier %0d suppresses leading zeros for decimal output. The specifier %<width><type> where width is a positive integer controls the field width for aligned columnar output. For instance, %8h prints an 8-character wide hexadecimal value padded with spaces. This is useful when logging aligned simulation tables to files.

Practical Understanding

In industry-style testbench development, $display and $monitor are typically replaced by more structured logging macros built on top of $fdisplay to direct all output to log files rather than the console. This is important because large designs run simulations for millions of cycles and console output becomes unmanageable. File-based logging with $fopen allows automated scripts to parse the log and check for expected values.

A common mistake is using $display inside an always block triggered by any signal change to trace values. This can flood the console because the always block fires on every transition. Using $monitor instead is cleaner since it deduplicates prints to only when a listed signal changes. Another practical pattern is writing the final simulation summary to a file using $fdisplay and then calling $fclose before $finish to ensure the file is properly saved.

Example
Given:
A testbench monitors a 4-bit counter output 'cnt' and logs its value to a file.
fd = file descriptor returned by $fopen
cnt = 4-bit output signal of DUT
Simulation runs for 200ns

Why this formula applies:
$fopen returns a valid MCD if file opens successfully (non-zero integer).
$fdisplay writes formatted data to the file using the MCD.
$fclose flushes and closes the file at simulation end.

Formula:
  fd = $fopen("counter_log.txt", "w");
  if (fd == 0) $display("File open failed");
  $monitor(fd, "%0t ns : cnt = %0d", $time, cnt);
  #200 $fclose(fd); $finish;

Substitution:
At time 10ns: cnt changes from 0 to 1
At time 20ns: cnt changes from 1 to 2

Calculation:
$monitor fires at each change of cnt.
Line written to file: "10 ns : cnt = 1"
Line written to file: "20 ns : cnt = 2"

Final Answer:
File counter_log.txt contains timestamped log of all cnt transitions across 200ns simulation.
Exam Tip: $strobe always prints after non-blocking assignments settle at end of timestep, while $display may print before NBA updates. For questions asking which task gives the correct final value after NBA, always choose $strobe.

Mechanism: How $monitor Works Internally

$monitor Internal Event MechanismSignal ChangeAny listed signalMonitor Flag SetSimulator marks changeEnd of TimestepAll events settlePrint OutputTo console/fileKey Difference from $display$display fires immediately when called$monitor fires only at end-of-timestep once$displayCalled explicitlyPrints at call pointMay show glitch values$strobeCalled explicitlyPrints after NBA settleAlways final value$monitorAuto-triggeredOnce per timestepOnly on signal change
Figure 2: Internal event mechanism of $monitor vs $display and $strobe in Verilog simulation
  • $monitor registers a callback with the simulator event queue. It does not poll signals continuously but waits for value-change events on its argument list.
  • Only one $monitor statement can be active at a time. A second $monitor call deactivates the first automatically without any error.
  • $fopen must be called before any $fdisplay or $fmonitor. If the returned MCD is 0, the file did not open and further writes will silently fail.
  • $display uses format specifiers that are evaluated left to right. Expressions passed as arguments are evaluated at the time the task is called, not at end of timestep.
  • $fclose should always be the last operation before $finish. Skipping $fclose can leave file buffers unflushed, resulting in incomplete output files.

Quick Revision

  • $display prints immediately with a newline. $write prints immediately without a newline. Both evaluate arguments at call time.
  • $strobe prints at the end of the current timestep after all non-blocking assignments have completed. Gives the settled, final value.
  • $monitor auto-triggers once per timestep whenever any listed signal changes. Only one $monitor is active at any time.
  • $fopen returns a multi-channel descriptor (MCD). MCD of 0 indicates file open failure. Pass MCD to $fdisplay to write to file.
  • File I/O task sequence: $fopen -> $fdisplay/$fmonitor (during simulation) -> $fclose (before $finish).
  • Format specifiers: %b = binary, %h = hex, %d = decimal, %0d = decimal without leading zeros, %s = string.
  • Exam trap: $display inside an always block sensitive to all signals prints on every signal change including intermediate glitches. Use $strobe or $monitor for final stable values.

System Tasks Quiz

Test understanding of Verilog system tasks for I/O and display.

Question 1 of 3

Q1.How does the $monitor system task differ from the $display task?