Mastering Format Specifications in Verilog and SystemVerilog: A Comprehensive Guide

In the digital design and coding world, Verilog and SystemVerilog stand as two of the most reliable and widely used hardware description languages. A pivotal feature of these languages, enhancing the debugging process and code comprehension, is the employment of format specifications. Format specifications regulate how data appears when utilizing I/O system tasks.

Here's a brief overview of the format specifications we'll discuss:

FormatDescription
%d, %o, %h, %x, %bDisplay in decimal, octal, hexadecimal (either %h or %x), binary format
%c, %sDisplay ASCII characters and strings
%e, %f, %gDisplay real numbers in various formats
%l, %mDisplay library binding information, hierarchical names
%vDisplay net signal strength
%pDisplay as an assignment pattern
%tDisplay in current time format
%u, %zUnformatted 2 and 4 value data

In this comprehensive guide, we'll explore these specifications to help you decipher the code and output in Verilog and SystemVerilog. Let's embark on this informative journey!

%d, %o, %h, %x, %b - Exploring Decimal, Octal, Hexadecimal, and Binary Formats

In Verilog and SystemVerilog programming, you'll frequently utilize the $display function to present data in your preferred format. Specific format specifiers, like %d for decimal, %o for octal, %h or %x for hexadecimal, and %b for binary, help shape your data's appearance.

Consider an example where we display a 12-bit register value in these various formats:

module TestModule;
  reg [11:0] twelveBitReg;
  initial begin
    twelveBitReg = 12'd1023;  // decimal format
    $display("Decimal Format: %4d", twelveBitReg);
    $display("Octal Format: %o", twelveBitReg);
    $display("Hexadecimal Format: %3h", twelveBitReg);  // three characters for hexadecimal
    $display("Same Hexadecimal Format: %3x", twelveBitReg);  // %x acts the same as %h
    $display("Binary Format: %b", twelveBitReg);
  end
endmodule

The output would present the twelveBitReg value in each of the specified formats. Notably, we've utilized %4d and %3h or %3x to control the output width—a potent feature when handling varied-sized data, enhancing your output's clarity and readability.

%c, %s - Decoding ASCII Character and String Formats

Verilog and SystemVerilog allow us to interpret ASCII codes as characters and to display strings—convenient for printing messages or data in human-readable format:

module StringModule;
  initial begin
    byte asciiVal = 8'h41; // ASCII code for 'A'
    $display("ASCII character: %c", asciiVal);
    
    string strVal = "Hello, SystemVerilog!";
    $display("String: %s", strVal);
  end
endmodule

The %c format specifier displays the ASCII character equivalent of a byte, and %s is used to display strings.

%e, %f, %g - Mastering the Art of Displaying Real Numbers

Real numbers in SystemVerilog can be displayed in exponential, decimal, or a shorter version of both. This feature proves incredibly useful when handling large floating-point numbers or precision decimal numbers:

module RealNumbersModule;
  real pi = 3.1415926535;
  initial begin
    $display("Exponential format: %e", pi);
    $display("Decimal format: %f", pi);
    $display("Shorter format: %g", pi);
  end
endmodule

Here, %e displays in exponential format, %f in decimal format, and %g selects the shorter of the two.

%l, %m - Deciphering Library Binding and Hierarchical Names in SystemVerilog

In SystemVerilog, %l and %m formatting specifiers perform unique roles. The %l specifier outputs the library details of a module instance in a "library.cell" format, illuminating the origin library and the cell name of the current instance. Similarly, the %m specifier displays the hierarchical name of the invoking design element, whether it's a subroutine, a named block, or any other labeled statement. This is particularly handy when dealing with multiple instances of a module and needing to trace a specific system task's source.

Let's witness both specifiers in action within a single module:

module SystemVerilogModule;
  initial begin
    $display("Library Binding Info: %l");
    $display("Hierarchical Name Info: %m");
  end
endmodule

The brilliance of both %l and %m is that they don't require arguments—they directly refer to the library and hierarchical name details of the current instance, respectively. These specifiers are a debugging boon, aiding in more efficient issue identification and resolution.

%v - Unveiling Net Signal Strength in SystemVerilog

Signal strength in SystemVerilog is crucial for evaluating a net's driving capability. To determine this, we use the %v format specifier. It presents the strength of scalar nets in a three-character format: the first two characters represent the strength, while the third signifies the scalar's logic value.

Here's an illustration of how it works:

module SignalStrengthModule;
  wire #(2,4) weakPullUpWire = 1'b1;
  initial begin
    $display("Strength: %v", weakPullUpWire);
  end
endmodule

In this module, %v reveals the strength of weakPullUpWire.

The strength levels are either represented by a mnemonic or a pair of decimal digits, indicating a range of strength levels. Below is a brief overview of strength mnemonics and their corresponding strength levels:

MnemonicStrength NameStrength Level
SuSupply Drive7
StStrong Drive6
PuPull Drive5
LaLarge Capacitor4
WeWeak Drive3
MeMedium Capacitor2
SmSmall Capacitor1
HiHigh Impedance0

The third character, representing the scalar's logic value, can be:

ArgumentDescription
0For a logic 0 value
1For a logic 1 value
XFor an unknown value
ZFor a high-impedance value
LFor a logic 0 or high-impedance value
HFor a logic 1 or high-impedance value

Using the %v format specifier, you can gain deeper insights into your scalar nets' signal strength, making debugging and performance optimizations more streamlined.

%p - Deciphering Assignment Patterns in SystemVerilog

SystemVerilog's %p format specifier offers a versatile tool for displaying complex data structures. The following example demonstrates how to represent an unpacked structure in an assignment pattern:

module PatternModule;
  struct packed {bit [1:0] a; bit [1:0] b;} abStruct;
  initial begin
    abStruct = '{2'b01, 2'b10};
    $display("Pattern: %p", abStruct);
  end
endmodule

The %p specifier neatly presents the abStruct value. Here are some highlights of its usage:

  • Structures are represented with named elements.
  • Enum types display as their name, if valid.
  • String types appear in quotes.
  • Unique types like class handles show in a distinct format, with null values as 'null'.
  • Other types are printed unformatted.

For a condensed view of aggregate expressions, the %0p format specifier is used. It's a more compact, implementation-dependent alternative.

In essence, the %p and %0p format specifiers provide a neat way of interpreting complex data structures in SystemVerilog.

%t - Understanding Current Time Format

Last but not least, %t is used to display the current simulation time. This can be very useful for tracking event timing during simulation:

module TimeModule;
  initial begin
    #5; // wait for 5 time units
    $display("Current Time: %t", $time);
  end
endmodule

This will display the current simulation time (5 time units) when it is called.

%u, %z - Handling Unformatted Binary Data

SystemVerilog has distinct specifications for writing unformatted binary data: %u (or %U) and %z (or %Z). These specifications work best with $fwrite when transmitting data to external programs.

The %u specifier writes binary data to the output, disregarding any unknown or high-impedance bits by treating them as zero. This is useful for transferring data to external applications that only recognize binary data.

Conversely, the %z specifier supports the transfer of binary data while preserving unknown (x) and high-impedance (z) bits. It's designed for communication with programs that understand and accommodate these bits.

Here's a unified example:

module BinaryModule;
  reg [7:0] regByte = 8'b10101010;
  reg [7:0] regByteZ = 8'bz1010010;
  initial begin
    $fwrite(file, "Binary Data: %u", regByte);
    $fwrite(file, "Binary Data with Z: %z", regByteZ);
  end
endmodule

Both specifiers write data in the native endian format of the system, in 32-bit units, with the word containing the LSB written first. Note that for POSIX applications, files should be opened with wb, wb+, or w+b specifiers to prevent the system from altering patterns in the unformatted stream that resemble special characters.


Whether you're a seasoned veteran or a newcomer to Verilog and SystemVerilog, understanding and using format specifications can significantly enhance your coding and debugging process. They allow for clear, concise, and context-specific displays of data, making your output more readable and your code easier to debug.