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$typeofresults 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:
- Typedefs resolved back to built-in or user-defined types.
- Default signing removed (even if present explicitly in source).
- System-generated names for anonymous structs, unions, and enums.
$used as placeholder for anonymous unpacked array names.- Enum constants appended with their encoded values.
- User-defined type names prefixed with their package or scope namespace.
- Array ranges as unsized decimal numbers.
- 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
$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 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);
| Function | Direction | Vector width |
|---|---|---|
| $realtobits(real_val) | real → bit[63:0] | 64 bits |
| $bitstoreal(bit_val) | bit[63:0] → real | 64 bits |
| $shortrealtobits(shortreal_val) | shortreal → bit[31:0] | 32 bits — new in SV |
| $bitstoshortreal(bit_val) | bit[31:0] → shortreal | 32 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.
// 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. $dimensionsreturns 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.
| Function | Associative array returns |
|---|---|
| $left | 0 (always) |
| $right | Highest possible index value for the index type |
| $low | Lowest currently allocated index value |
| $high | Largest currently allocated index value |
| $increment | −1 (always) |
| $size | Number 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: 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");
⛔ 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
| Task | Effect on currently-executing assertions | Stops future checks? |
|---|---|---|
| $assertoff | Lets them complete normally | Yes |
| $assertkill | Aborts them immediately | Yes |
| $asserton | No current assertions to affect | Reverses 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
| Function | Returns true when | Type |
|---|---|---|
| $onehot(expr) | Exactly one bit of expr is 1 | bit |
| $onehot0(expr) | At most one bit of expr is 1 | bit |
| $isunknown(expr) | Any bit of expr is X or Z | bit |
| $sampled(expr [,clk]) | Returns sampled value | same as expr |
| $rose(expr [,clk]) | LSB just changed to 1 | bit |
| $fell(expr [,clk]) | LSB just changed to 0 | bit |
| $stable(expr [,clk]) | Sampled value unchanged | bit |
| $past(expr[,N][,gate][,clk]) | Returns value N ticks ago | same as expr |
| $countones(expr) | Count of 1-bits | int |
🎲 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
$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
| Task | Who it affects | When simulation ends |
|---|---|---|
| $stop | Simulation pauses; tool returns to interactive mode | Only after $finish |
| $finish | Terminates entire simulation immediately | Immediately |
| $exit | Terminates the calling program and its threads | When 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.
| Function | Purpose |
|---|---|
| $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:
%uand%zare 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 Verilogreg, 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
$readmemrules. - 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
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
| Function | Returns | When evaluated |
|---|---|---|
| $typeof(expr) | Opaque type value | Elaboration time |
| $typename(expr) | string | Elaboration 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.
$dimensionscounts all packed + unpacked dimensions; 0 for scalars.
Key rules
- Assertion severity tasks always print file/line/scope/time automatically — user message is appended.
$assertofflets in-flight assertions finish;$assertkillaborts them immediately.$urandomis per-process (not global);$urandom_range(max,min)for range-bounded values.$exitends the calling program and all its threads; simulation ends when all programs exit.$writememb/$writememhoverwrite the output file — no append mode.- Multi-dimensional file layout is row-major;
@addressentries address only the outermost dimension.
