SystemVerilog Series · SV-23

SystemVerilog Series — SV-23: System Tasks and Functions — VLSI Trainers
SystemVerilog Series · SV-23

System Tasks and System Functions

All new SV system tasks and functions: elaboration-time $typeof and $typename, bit-stream sizing with $bits, $isunbounded, shortreal conversions, seven array query functions, assertion severity and control tasks, assertion boolean functions, random number generators, program control, coverage functions, and enhancements to $readmemb/$readmemh plus the new $writememb/$writememh.

📋 $typeof — Elaboration-Time Type

$typeof(expr_or_type) returns a type derived from its argument, evaluated at elaboration time. The result can be used anywhere an elaboration constant is required — most importantly to assign or override a type parameter, or in a type equality comparison.

// Capture the type of a signal — useful with type parameters
bit [12:0] A_bus, B_bus;
parameter type bus_t = $typeof(A_bus);   // bus_t = bit[12:0]

// Use $typeof in a generate-case to select the right adder
generate
  case ($typeof(bus_t))
    $typeof(bit[12:0]): addfixed_int  #(bus_t) (A_bus, B_bus);
    $typeof(real):      add_float     #($typeof(A_bus)) (A_bus, B_bus);
  endcase
endgenerate
  • When called with an expression, returns the self-determined type of the expression.
  • The expression is never actually evaluated — only its type is resolved.
  • The expression must not contain hierarchical identifiers or references to dynamic object members.
  • Equality (==) and case equality (===) between two $typeof results is true if the types are equivalent.
  • The actual value returned is opaque — not visible or usable as a string.

📋 $typename — Type as String

$typename(expr_or_type) returns a string representing the resolved type of its argument. It is useful for debugging type mismatches and for stricter array type-checking than $typeof alone provides (because array ranges are preserved in the string rather than being normalised).

The string is constructed in this order:

  1. Typedefs resolved back to built-in or user-defined types.
  2. Default signing removed (even if present explicitly in source).
  3. System-generated names for anonymous structs, unions, and enums.
  4. $ used as placeholder for anonymous unpacked array names.
  5. Enum constants appended with their encoded values.
  6. User-defined type names prefixed with their package or scope namespace.
  7. Array ranges as unsized decimal numbers.
  8. Whitespace removed; single space between identifiers and keywords.
// source code                    // $typename returns
typedef bit node;               // "bit"
node signed [2:0] X;             // "bit signed[2:0]"
int signed Y;                   // "int"  (default signing removed)

package A;
  enum {A,B,C=99} X;           // "enum{A=32'd0,B=32'd1,C='32bX}A::e$1"
  typedef bit [9:1'b1] word;   // "A::bit[9:1]"
endpackage : A

import A::*;
module top;
  typedef struct {node A,B;} AB_t;
  AB_t AB[10];                  // "struct{bit A;bit B;}top.AB_t$[0:9]"
endmodule
$typename vs $typeof: $typeof returns an opaque type value — useful for type comparisons and parameter overrides but not human-readable. $typename returns a human-readable string — useful for debug and for stricter array range checking, since it preserves exact range syntax rather than normalising it.

📋 $bits — Bit-Stream Size

$bits(expr_or_type) returns the number of bits required to represent the argument as a bit stream. A 4-state value counts as one bit. This is independent of how the simulator physically stores it.

// Fixed-width scalar
logic [31:0] foo;
$bits(foo)   // → 32  (even if stored internally with more bits)

// Struct: sum of all member widths
typedef struct {
  logic      valid;   // 1 bit
  bit [8:1]  data;    // 8 bits
} MyType;
$bits(MyType)  // → 9

// Used as an elaboration-time constant — legal in type declarations
typedef bit[$bits(MyType):1] MyBits;  // same as typedef bit [9:1] MyBits
MyBits b;                              // can hold any MyType bit-for-bit

// On dynamically sized types:
// — dynamic array or queue that is currently empty → returns 0
// — applying $bits directly to a dynamic-sized type identifier → error
$bits as an elaboration constant. Because $bits on a fixed-size type is evaluated at elaboration (before simulation starts), it can appear in type declarations, array dimensions, and parameter defaults — anywhere a constant is required. This makes it extremely useful for writing generic code that sizes bit vectors to exactly match a struct or enum.

📋 $isunbounded

$isunbounded(const_expr) returns true if the constant expression evaluates to $ (the unbounded token). Used in generate conditions to choose between property variants based on whether a range parameter is bounded or not.

parameter int foo = $;
$isunbounded(foo)   // → true
$isunbounded(42)    // → false

// Pattern: different logic for bounded vs unbounded max
generate
  if ($isunbounded(MAX)) begin
    // expr [* MIN] — only minimum width required
  end else begin
    // expr [* MIN:MAX] ##1 !expr — exact window
  end
endgenerate

📋 shortreal Conversions

Verilog already provided $realtobits and $bitstoreal for exact 64-bit transfer of real values. SV adds a matching pair for the new 32-bit shortreal type.

// Convert shortreal to its 32-bit IEEE 754 bit pattern
bit [31:0] bits32 = $shortrealtobits(3.14);

// Reconstruct shortreal from a 32-bit pattern
shortreal f = $bitstoshortreal(bits32);
FunctionDirectionVector width
$realtobits(real_val)real → bit[63:0]64 bits
$bitstoreal(bit_val)bit[63:0] → real64 bits
$shortrealtobits(shortreal_val)shortreal → bit[31:0]32 bits — new in SV
$bitstoshortreal(bit_val)bit[31:0] → shortreal32 bits — new in SV

📋 Array Query Functions

Seven functions return structural information about any array dimension. All accept an array variable or type identifier, plus an optional dimension number (default = 1 if omitted). All return integer.

$left(arr [,dim])
integer
Left bound (MSB) of the dimension — the value as written in the declaration.
$right(arr [,dim])
integer
Right bound (LSB) of the dimension — the value as written in the declaration.
$low(arr [,dim])
integer
Minimum of $left and $right — the numerically smaller of the two bounds.
$high(arr [,dim])
integer
Maximum of $left and $right — the numerically larger of the two bounds.
$increment(arr [,dim])
integer
+1 if $left >= $right (big-endian), −1 if $left < $right (little-endian).
$size(arr [,dim])
integer
Number of elements: $high − $low + 1. Always a positive count regardless of direction.
$dimensions(arr)
integer
Total number of dimensions (packed + unpacked). Returns 0 for a scalar.
// Dimension numbering:
// Dimension 1 = slowest-varying (outermost unpacked)
// Dimension 2 = next slower, etc.
// Packed dimensions follow after all unpacked dimensions
//         dim: 3    4    1    2
reg [3:0][2:1] n [1:5][2:8];

// Examples
$left(n, 1)       // → 1  (left bound of dim 1: [1:5])
$right(n, 1)      // → 5
$low(n, 1)        // → 1  (min of 1,5)
$high(n, 1)       // → 5  (max of 1,5)
$increment(n, 1)  // → 1  ($left >= $right: big-endian)
$size(n, 1)       // → 5  (5 - 1 + 1)
$dimensions(n)    // → 4  (2 unpacked + 2 packed)

// Type-based query — fixed size types: returns elaboration-time constant
typedef logic [16:1] Word;
Word Ram[0:9];
$size(Word)         // → 16   (type query: fixed → elaboration constant)
$size(Ram, 2)       // → 16   (dim 2 of Ram: the packed Word dimension)

// Fixed-size integer types: dimension 1 pre-defined as [$bits(N)-1:0]
// int:      $left=31, $right=0, $size=32
// byte:     $left=7,  $right=0, $size=8
// shortint: $left=15, $right=0, $size=16
// longint:  $left=63, $right=0, $size=64

// Out-of-range dimension → returns 'x
// Dynamic/queue dimension currently empty → returns 'x

📋 Dimension Numbering Rules

  • Dimension 1 is the slowest-varying (outermost) dimension — the leftmost unpacked dimension in the declaration.
  • Successively higher dimension numbers correspond to faster-varying (inner) dimensions.
  • All unpacked dimensions are numbered first (slowest to fastest), then packed dimensions continue the numbering.
  • Intermediate typedefs are fully expanded before counting dimensions.
  • $dimensions returns the total count of all packed + unpacked dimensions. A scalar (no dimensions) returns 0.
// typedef fully expanded: packed_reg = reg[3:0][2:1]
// packed_reg n[1:5][2:8] is identical in dimensions to reg[3:0][2:1] n[1:5][2:8]
//   dim 1: unpacked [1:5]
//   dim 2: unpacked [2:8]
//   dim 3: packed   [3:0]
//   dim 4: packed   [2:1]
typedef reg [3:0][2:1] packed_reg;
packed_reg n[1:5][2:8];
$dimensions(n)  // → 4

📋 Query Functions on Associative Arrays

Array query functions on associative arrays (integral index type only) return information about currently allocated entries, not about the theoretical index range.

FunctionAssociative array returns
$left0 (always)
$rightHighest possible index value for the index type
$lowLowest currently allocated index value
$highLargest currently allocated index value
$increment−1 (always)
$sizeNumber of elements currently allocated

Assertion Severity Tasks

These four tasks set the severity of an assertion failure. By default an assertion uses $error. Each task automatically prints the file name, line number, hierarchical name (or scope), and for simulation, the current time. An optional user message follows the same syntax as $display.

$fatal(N)
Run-time fatal. Terminates simulation via $finish(N). N = 0/1/2 controls diagnostic level.
$error
Run-time error — default severity. Simulation continues.
$warning
Run-time warning. Can be suppressed tool-specifically. Simulation continues.
$info
Informational only — no severity implied. Simulation continues.
// $fatal: stop simulation immediately — for unrecoverable errors
fatal_chk: assert(!impossible_state) else
  $fatal(1, "impossible state reached at %0t", $time);

// $error: protocol violation — record and continue
ack_chk: assert(ack) else
  $error("ACK missing: addr=0x%0h", addr);

// $warning: marginal condition
timing_chk: assert(delay < TMAX) else
  $warning("marginal timing: delay=%0d", delay);

// $info: milestone tracking (pass action — not in else)
pkt_ok: assert(pkt_valid) pkt_count++;
else $info("invalid packet observed");
All four tasks print automatically. Even if you supply no message string, the tool prints the file, line, hierarchical name, and simulation time on its own. Your optional format string is appended to that automatic output — you never need to duplicate the location information.

Assertion Control Tasks

Three tasks control assertion checking at runtime. All accept an optional levels count (hierarchy depth to apply) and an optional list of specific assertions or modules.

// $assertoff: stop checking all specified assertions
// Assertions already executing (including pass/fail statements) are NOT stopped
$assertoff;                      // disable ALL assertions everywhere
$assertoff(0, top.dut);          // disable all assertions in top.dut (level 0 = all depths)
$assertoff(1, top.dut);          // disable at top.dut and 1 level below
$assertoff(0, my_assert);       // disable one named assertion

// $assertkill: abort currently executing assertions AND stop checking
$assertkill;                     // kill and disable ALL assertions
$assertkill(0, top.dut);         // kill and disable in top.dut subtree

// $asserton: re-enable assertion checking after $assertoff or $assertkill
$asserton;                       // re-enable ALL assertions
$asserton(0, top.dut);            // re-enable in top.dut subtree

// Typical use: disable assertions during reset, re-enable after
initial begin
  $assertoff;
  #100 rst_n = 1;
  @(posedge clk);
  $asserton;
end
TaskEffect on currently-executing assertionsStops future checks?
$assertoffLets them complete normallyYes
$assertkillAborts them immediatelyYes
$assertonNo current assertions to affectReverses off/kill

📋 Assertion Boolean Functions

Five functions cover the most common assertion checks on bit vectors and sampled signal values. All return bit (0 or 1).

// $onehot: exactly one bit is 1
assert property (@(posedge clk) $onehot(grant));

// $onehot0: zero or one bit is 1 (allows all-zero)
assert property (@(posedge clk) $onehot0(req));

// $isunknown: any bit is X or Z  (equivalent to ^expr === 'bx)
assert property (@(posedge clk) !$isunknown(data));

// $sampled: sampled value of an expression at the clocking event
// Identical to just using the expression inside a concurrent assertion,
// but usable in procedural code with an explicit clocking event argument
@(posedge clk);
if($sampled(data) != 0) ...

// $rose / $fell / $stable — detect LSB changes between clock ticks
// (same functions described in assertions; listed here for completeness)
// $rose(expr)   : LSB changed from non-1 to 1 since last clock tick
// $fell(expr)   : LSB changed from non-0 to 0 since last clock tick
// $stable(expr) : sampled value did not change since last clock tick

// $past(expr [,N] [,gating] [,clk]) — sampled value N ticks ago (default N=1)
// $countones(expr) — number of 1-bits in a bit vector
FunctionReturns true whenType
$onehot(expr)Exactly one bit of expr is 1bit
$onehot0(expr)At most one bit of expr is 1bit
$isunknown(expr)Any bit of expr is X or Zbit
$sampled(expr [,clk])Returns sampled valuesame as expr
$rose(expr [,clk])LSB just changed to 1bit
$fell(expr [,clk])LSB just changed to 0bit
$stable(expr [,clk])Sampled value unchangedbit
$past(expr[,N][,gate][,clk])Returns value N ticks agosame as expr
$countones(expr)Count of 1-bitsint

🎲 Random Number Functions

Three functions supplement the Verilog $random system function with unsigned and range-limited variants and an explicit seed setter.

// $urandom: unsigned 32-bit pseudorandom number
bit [31:0] r = $urandom();

// $urandom_range(max [, min]): unsigned random in [min:max], default min=0
int delay = $urandom_range(20, 5);  // random integer in [5:20]
int idx   = $urandom_range(15);      // random in [0:15]

// $srandom(seed): set the random seed for the calling process
// Each process has its own seed; $srandom(0) resets to the tool's seed
$srandom(42);   // deterministic replay: same seed → same sequence
$urandom vs $random: $random returns a signed 32-bit value and uses a global seed shared across the entire simulation. $urandom returns an unsigned 32-bit value and uses a per-process seed — different processes generate independent random sequences even without explicit seeding. $urandom_range is the preferred way to get a random value within a specific range without the bias issues of modulo arithmetic on $random.

Program Control

In addition to the standard $stop and $finish, programs can use $exit to terminate gracefully.

// $exit: terminate the calling program block
// — all threads spawned by this program are also killed
// — when ALL program instances have exited, simulation ends with $finish
program tb(...);
  initial begin
    run_tests();
    $exit();   // clean teardown — kills fork threads too
  end
endprogram
TaskWho it affectsWhen simulation ends
$stopSimulation pauses; tool returns to interactive modeOnly after $finish
$finishTerminates entire simulation immediatelyImmediately
$exitTerminates the calling program and its threadsWhen all programs have exited

📈 Coverage System Functions

Five built-in system functions manage coverage data at the simulation level, complementing the per-instance covergroup methods.

FunctionPurpose
$coverage_control(cmd, type, scope, name)Enable, disable, or reset coverage collection for specified items
$coverage_get_max(type, scope, name)Return the maximum possible coverage count for an item
$coverage_get(type, scope, name)Return the current coverage count for an item
$coverage_merge(type, name)Merge coverage data from another database into the current one
$coverage_save(type, name)Save current coverage data to a file

📋 Verilog-2001 Task Enhancements

Two existing Verilog-2001 tasks are extended to handle SV aggregate types.

%u and %z format specifiers

  • For packed data: operate as if applied to the equivalent vector — no change in behaviour.
  • For unpacked structs: applied to each member in declaration order.
  • For unpacked unions: applied to the first member in declaration order.
  • Unpacked arrays: %u and %z are not defined on unpacked arrays.
  • The count of data items for an aggregate type is always 1 or 0 — individual members are not counted separately.

$fread

$fread extended to handle SV aggregate types in the same way as %u/%z: packed data treated as a vector; unpacked struct reads each member in declaration order; unpacked union reads only the first member; unpacked array elements that are structs or unions follow the same rules.

📄 $readmemb / $readmemh Enhancements

Both tasks are extended beyond plain Verilog memories to support the full range of SV array types.

Extended target types

  • Unpacked arrays of packed data — each packed element treated as a vector.
  • Associative arrays of packed data — index type must be integral.
  • Dynamic arrays of packed data — each element treated as a vector.

2-state types

  • For 2-state integer types (e.g. int): reading proceeds as for Verilog reg, except X or Z data in the file is converted to 0.
  • For enumerated types: the file data represents ordinal values. If an ordinal is out of range for the enum, an error is issued and reading stops.

📄 $writememb and $writememh — New Tasks

SV adds two new system tasks for dumping array contents to files in a format that can be read back by $readmemb/$readmemh.

// Signature (identical for both binary and hex variants)
$writememb("filename", memory_name [, start_addr [, finish_addr]]);
$writememh("filename", memory_name [, start_addr [, finish_addr]]);

// Example
int mem[0:255];
$writememh("dump.hex", mem);          // dump entire array
$writememh("partial.hex", mem, 64, 127); // dump only [64:127]

Key rules

  • If the file already exists, it is overwritten — there is no append mode.
  • For packed data: treated identically to the corresponding $readmem rules.
  • For 2-state types: enum values are written as their ordinal integers.
  • For unpacked or dynamic arrays: address specifiers (@address) are not written to the file.
  • For associative arrays: address specifiers are written to the file.

📄 Multi-Dimensional Array File Format

When reading or writing multi-dimensional unpacked arrays, the file is organised in row-major order — the rightmost (lowest) dimension varies most rapidly. This is backward compatible with plain Verilog 1-D memories.

// 3D array:  reg[31:0] mem[0:2][0:4][5:8]
// 3 × 5 × 4 = 60 words in the file
// Row layout — inner dimension (x) fastest, outer dimension (z) slowest:
// z=0, y=0: mem[0][0][5] mem[0][0][6] mem[0][0][7] mem[0][0][8]
// z=0, y=1: mem[0][1][5] mem[0][1][6] ...
// ...
// z=2, y=4: mem[2][4][5] mem[2][4][6] mem[2][4][7] mem[2][4][8]

// With @address entries: address covers the HIGHEST dimension only
// @0 → start of z=0 block (20 words: 5 rows × 4 words each)
// @1 → start of z=1 block
// @2 → start of z=2 block

// Without @address entries: all 60 words read in row-major order

// System task argument notes:
// — memory_name can be a partially-indexed sub-array
// — Higher dimensions must use indexing, not ranges
// — Lowest (rightmost) dimension can use slice syntax
// — start_addr/finish_addr apply to the highest dimension
// — Direction of file entries follows relative order of start_addr and finish_addr
Reversing dimension declarations does not change the file layout. mem[0:2][0:4][5:8] and mem[2:0][0:4][8:5] produce identical file layouts because row-major ordering is determined by which dimension is outermost, not by whether that dimension counts up or down.

📋 Quick Reference

Type and size functions

FunctionReturnsWhen evaluated
$typeof(expr)Opaque type valueElaboration time
$typename(expr)stringElaboration time
$bits(expr_or_type)int (bit count)Elaboration (fixed types); simulation (dynamic types)
$isunbounded(const)bit (true if $)Elaboration time

Array query functions — what they return on a dynamic/queue dimension

  • If the dimension is currently empty → returns 'x.
  • On fixed-size arrays: usable as elaboration-time constants in parameters and type declarations.
  • $dimensions counts all packed + unpacked dimensions; 0 for scalars.

Key rules

  • Assertion severity tasks always print file/line/scope/time automatically — user message is appended.
  • $assertoff lets in-flight assertions finish; $assertkill aborts them immediately.
  • $urandom is per-process (not global); $urandom_range(max,min) for range-bounded values.
  • $exit ends the calling program and all its threads; simulation ends when all programs exit.
  • $writememb/$writememh overwrite the output file — no append mode.
  • Multi-dimensional file layout is row-major; @address entries address only the outermost dimension.
Coming next: SV-24 covers VCD Data — how SV types map to Verilog types for waveform dumping, and the rules for packed arrays, structures, and enumerated types in the VCD format.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top