Lesser-Known Features, Shortcuts, and Power User Techniques
Advanced SystemVerilog capabilities that most people don’t know about
Table of Contents
- Array Initialization Tricks
- Assignment Patterns
- Streaming Operators
- Type Casting Magic
- String Manipulation
- System Function Tricks
- Constraint Tricks
- Coverage Secrets
- Assertion Hidden Features
- Compilation and Elaboration
- Debug and Introspection
- Performance Tricks
1. Array Initialization Tricks
1.1 Default Pattern
// Fill all elements with same value
bit [7:0] arr[5] = '{default: 5};
// Result: arr[0]=5, arr[1]=5, arr[2]=5, arr[3]=5, arr[4]=5
int data[10] = '{default: -1};
// All elements = -1
// Works with multi-dimensional
int matrix[3][4] = '{default: 0};
// All 12 elements = 0
1.2 Positional and Named Patterns
// Positional assignment
int arr[5] = '{1, 2, 3, 4, 5};
// Named assignment (by index)
int arr2[5] = '{0:10, 1:20, 2:30, 3:40, 4:50};
// Mixed (named + default)
int arr3[10] = '{0:100, 5:500, default:0};
// arr3[0]=100, arr3[5]=500, others=0
// Replication in pattern
bit [7:0] data[8] = '{4{8'hAA}, 4{8'h55}};
// First 4 elements = 0xAA, last 4 = 0x55
// Nested replication
int matrix[4][3] = '{4{'{1, 2, 3}}};
// Each row = {1, 2, 3}
1.3 Structure Initialization
typedef struct {
bit [7:0] addr;
bit [31:0] data;
bit write;
} packet_t;
// Default initialization
packet_t pkt = '{default: 0};
// Named initialization
packet_t pkt2 = '{addr: 8'h10hDEAD, write: 1};
// Partial initialization
packet_t pkt3 = '{addr: 8'h20, default: 0};
// addr=0x20, data=0, write=0
// Array of structures
packet_t pkt_arr[4] = '{
'{addr: 8'h10h1111, write: 1},
'{addr: 8'h20, data: 32'h2222, write: 0},
'{addr: 8'h30,3333, write: 1},
'{addr: 8'h40h4444, write: 0}
};
// Default + specific
packet_t arr[3] = '{
0: '{addr: 8'hFF, default: 0},
default: '{default: 0}
};
2. Assignment Patterns
2.1 Unpacking with Assignment Pattern
// Unpack concatenation
bit [31:0] word = 32'hDEAD_BEEF;
bit [7:0] b3, b2, b1, b0;
{b3, b2, b1, b0} = word;
// b3=0xDE, b2=0xAD, b1=0xBE, b0=0xEF
// Unpack to structure
typedef struct packed {
bit [7:0] opcode;
bit [7:0] addr;
bit [15:0] data;
} instruction_t;
instruction_t inst;
bit [31:0] raw_data = 32'h12_34_56_78;
inst = raw_data;
// inst.opcode=0x12, inst.addr=0x34, inst.data=0x5678
2.2 Multiple Assignments
// Assign multiple variables at once
{a, b, c} = '{1, 2, 3};
// Assign from function return
function automatic {bit[7:0], bit[7:0]} split(bit[15:0] data);
return '{data[15:8], data[7:0]};
endfunction
{high_byte, low_byte} = split(16'hABCD);
// high_byte=0xAB, low_byte=0xCD
// Swap values
{a, b} = {b, a}; // Swap in one line!
3. Streaming Operators
3.1 Pack and Unpack Data
bit [7:0] bytes[4] = '{8'h12, 8'h34, 8'h56, 8'h78};
bit [31:0] word;
// Pack bytes into word (left to right)
word = {<<8{bytes}};
// Result: word = 32'h12345678
// Pack right to left
word = {>>8{bytes}};
// Result: word = 32'h78563412
// Unpack word into bytes
bytes = {<<8{word}};
// Different slice sizes
bit [3:0] nibbles[8];
word = {<<4{nibbles}}; // Pack nibbles
3.2 Endianness Conversion
// Big-endian to little-endian
function bit[31:0] swap_endian(bit[31:0] data);
bit [7:0] bytes[4];
bytes = {<<8{data}};
bytes.reverse();
return {<<8{bytes}};
endfunction
bit [31:0] big_endian = 32'h12345678;
bit [31:0] little_endian = swap_endian(big_endian);
// little_endian = 32'h78563412
// Quick byte swap
bit [31:0] original = 32'hAABBCCDD;
bit [31:0] swapped = {original[7:0], original[15:8],
original[23:16], original[31:24]};
// swapped = 32'hDDCCBBAA
4. Type Casting Magic
4.1 Static and Dynamic Casting
// Static cast
int a = 10;
shortint b;
b = shortint'(a);
// Bit width cast
bit [7:0] byte_val = 8'hFF;
bit [15:0] word_val = 16'(byte_val); // Zero extend
// Signed cast
bit [7:0] unsigned_val = 8'hFF;
int signed_val = signed'(unsigned_val); // -1
// Dynamic cast (for classes)
class base; endclass
class derived extends base; endclass
base b;
derived d;
d = new();
b = d; // Upcast (always safe)
derived d2;
if ($cast(d2, b)) // Downcast (check at runtime)
$display("Cast successful");
else
$error("Cast failed");
4.2 Type Operator
// type() operator - get type of expression
typedef type(data_in) data_t; // data_t same type as data_in
typedef type(addr[3:0]) nibble_t; // nibble_t is bit[3:0]
// Useful for parameterized code
class generic #(type T = int);
T value;
typedef type(value) value_t; // Export type
endclass
5. String Manipulation
5.1 String Methods
string s = "Hello World";
// Length
int len = s.len(); // 11
// Character access
byte c = s[0]; // 'H'
// Substring
string sub = s.substr(0, 4); // "Hello"
// Case conversion
string upper = s.toupper(); // "HELLO WORLD"
string lower = s.tolower(); // "hello world"
// Comparison
if (s.compare("Hello World") == 0) ...
if (s.icompare("HELLO world") == 0) ... // Case-insensitive
// Search
int pos = s.search("World"); // Returns 6
if (s.search("xyz") == -1) ... // Not found
// Conversion
int val;
s = "12345";
val = s.atoi(); // String to integer: 12345
real r = s.atoreal();
// Format
string msg = $sformatf("Data: %h, Count: %0d", data, count);
5.2 String Tricks
// Concatenation
string first = "Hello";
string last = "World";
string full = {first, " ", last}; // "Hello World"
// String array
string words[3] = '{"one", "two", "three"};
// String queue
string q[$];
q.push_back("apple");
q.push_back("orange");
// Parsing
string csv = "10,20,30,40";
string parts[$];
int values[$];
// Manual parsing
string temp = csv;
while (temp.len() > 0) begin
int pos = temp.search(",");
if (pos >= 0) begin
parts.push_back(temp.substr(0, pos-1));
temp = temp.substr(pos+1, temp.len()-1);
end else begin
parts.push_back(temp);
break;
end
end
// Convert to integers
foreach(parts[i])
values.push_back(parts[i].atoi());
6. System Function Tricks
6.1 Bit Manipulation Functions
bit [7:0] data = 8'b1010_1100;
// Count ones
int ones = $countones(data); // 4
// Count leading zeros
int lz = $countbits(data, '0); // From MSB
// Find first set bit
int pos = $clog2(data); // Log2, useful for position
// One-hot check
bit [3:0] grant = 4'b0100;
if ($onehot(grant)) // Exactly one bit
$display("One-hot");
if ($onehot0(grant)) // Zero or one bit
$display("One-hot or zero");
// Parity
bit parity = ^data; // XOR reduction
// Reverse bits
function bit[7:0] reverse_bits(bit[7:0] d);
bit[7:0] result;
for (int i = 0; i < 8; i++)
result[i] = d[7-i];
return result;
endfunction
bit [7:0] reversed = reverse_bits(8'b1010_0011);
// reversed = 8'b1100_0101
6.2 Math Functions
// Ceiling log2
int addr_bits = $clog2(256); // 8 (2^8 = 256)
int fifo_bits = $clog2(17); // 5 (2^5 = 32 > 17)
// Power
real result = $pow(2.0, 8.0); // 256.0
// Square root
real sqrt_val = $sqrt(16.0); // 4.0
// Trig functions
real sin_val = $sin(angle);
real cos_val = $cos(angle);
real tan_val = $tan(angle);
// Absolute value
int abs_val = $abs(-42); // 42
// Min/Max
int minimum = $min(a, b, c);
int maximum = $max(x, y, z);
6.3 System Query Functions
// Get hierarchical name
string name = $sformatf("%m");
// Value change
bit changed = $changed(signal); // True if changed this cycle
// Test if unknown
if ($isunknown(data))
$error("Data has X or Z!");
// Simulation time
realtime current = $realtime;
time t = $time;
// Random seed control
int seed = 12345;
void'($urandom(seed)); // Set seed
// Bits to represent value
int bits = $bits(data); // Width of data
// Size of array
int size = $size(array);
int left = $left(array); // Left bound
int right = $right(array); // Right bound
int low = $low(array); // Low bound
int high = $high(array); // High bound
int increment = $increment(array); // 1 or -1
7. Constraint Tricks
7.1 Hidden Constraint Features
class advanced_constraints;
rand bit [7:0] addr;
rand bit [31:0] data;
rand bit [7:0] array[10];
//=========================================================================
// TRICK 1: Implication with variables
//=========================================================================
rand bit enable;
constraint enable_implies {
enable -> (addr > 10);
!enable -> (addr == 0);
}
//=========================================================================
// TRICK 2: Foreach with index
//=========================================================================
constraint array_pattern {
foreach(array[i]) {
array[i] == i * 10; // array[0]=0, array[1]=10, array[2]=20...
}
}
//=========================================================================
// TRICK 3: Sum constraint
//=========================================================================
rand bit [3:0] a, b, c;
constraint sum_constraint {
a + b + c == 15;
}
//=========================================================================
// TRICK 4: Unique constraint
//=========================================================================
rand bit [7:0] unique_vals[5];
constraint all_unique {
unique {unique_vals}; // All elements different
}
//=========================================================================
// TRICK 5: Soft constraints (can be overridden)
//=========================================================================
constraint soft_default {
soft addr == 8'h00; // Default to 0, but can override
}
// Override soft constraint
// obj.randomize() with {addr == 8'hFF;}; // Overrides soft
//=========================================================================
// TRICK 6: Bidirectional constraint
//=========================================================================
rand bit [3:0] size;
rand bit [7:0] length;
constraint size_length {
(size == 4'h1) <-> (length == 8'd8);
(size == 4'h2) <-> (length == 8'd16);
(size == 4'h3) <-> (length == 8'd32);
}
//=========================================================================
// TRICK 7: Constraint with function
//=========================================================================
function bit is_power_of_2(bit[7:0] val);
return (val != 0) && ((val & (val - 1)) == 0);
endfunction
constraint power_of_2 {
is_power_of_2(addr); // addr must be power of 2
}
//=========================================================================
// TRICK 8: Array.size() constraint
//=========================================================================
rand bit [7:0] dynamic_arr[];
rand int arr_size;
constraint size_constraint {
arr_size inside {[1:20]};
dynamic_arr.size() == arr_size;
}
//=========================================================================
// TRICK 9: Distribution with := vs :/
//=========================================================================
rand bit [1:0] cmd;
constraint distribution {
// := assigns weight to each value
cmd dist {0 := 10, 1 := 20, 2 := 30, 3 := 40};
// Probability: 0=10%, 1=20%, 2=30%, 3=40%
// :/ assigns weight to each item in range
// cmd dist {[0:1] :/ 50, [2:3] :/ 50};
// Each value in range gets equal share
}
endclass
7.2 Pre/Post Randomize Tricks
class smart_randomization;
rand bit [7:0] addr;
rand bit [31:0] data;
static int transaction_count = 0;
// Pre-randomize: Setup before randomization
function void pre_randomize();
transaction_count++;
$display("Randomization #%0d starting", transaction_count);
// Can modify non-rand variables
// Can read rand variables (see previous values)
endfunction
// Post-randomize: Fixup after randomization
function void post_randomize();
// Force alignment even if constraints didn't
addr[1:0] = 2'b00;
// Add checksum
data[31:24] = ^data[23:0]; // Parity in MSB
// Conditional fixup
if (addr < 10)
data = 0; // Force data=0 for low addresses
$display("Final: addr=%h, data=%h", addr, data);
endfunction
endclass
7.3 Randcase and Randsequence
// Randcase - Random case selection with weights
task generate_transaction();
randcase
10: send_read(); // 10% probability
30: send_write(); // 30% probability
50: send_burst(); // 50% probability
10: send_idle(); // 10% probability
endcase
endtask
// Randsequence - Grammar-based generation
randsequence(main)
main: first second third;
first: read | write;
second: delay short_delay | delay long_delay;
third: done;
read: { generate_read_txn(); };
write: { generate_write_txn(); };
short_delay: { #10ns; };
long_delay: { #100ns; };
done: { $display("Sequence complete"); };
endsequence
// Call it
initial begin
repeat(10) randsequence(main);
end
8. Coverage Secrets
8.1 Hidden Coverage Features
class coverage_tricks;
bit [7:0] addr;
bit [31:0] data;
covergroup cg;
//=======================================================================
// TRICK 1: Cross with binsof filter
//=======================================================================
addr_cp: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
data_cp: coverpoint data {
bins small = {[0:100]};
bins large = {[101:1000]};
}
cross_filtered: cross addr_cp, data_cp {
// Only cover specific combinations
ignore_bins ignore_high_large =
binsof(addr_cp.high) && binsof(data_cp.large);
}
//=======================================================================
// TRICK 2: Bins with intersection
//=======================================================================
coverpoint cmd {
bins read_cmds = {READ, READ_BURST} intersect {[0:10]};
}
//=======================================================================
// TRICK 3: Auto bins with maximum
//=======================================================================
coverpoint addr {
option.auto_bin_max = 8; // Create max 8 bins
bins auto[] = {[0:255]}; // Splits into 8 equal bins
}
//=======================================================================
// TRICK 4: Set coverpoint from function
//=======================================================================
coverpoint get_transaction_type() {
bins read = {0};
bins write = {1};
}
//=======================================================================
// TRICK 5: Conditional bins with iff
//=======================================================================
coverpoint addr iff (enable) {
// Only sample when enable=1
bins values[] = {[0:15]};
}
//=======================================================================
// TRICK 6: Generic cross
//=======================================================================
cross addr, data, write; // 3-way cross
//=======================================================================
// TRICK 7: Cross with automatic bin creation
//=======================================================================
addr_data_cross: cross addr_cp, data_cp {
option.cross_auto_bin_max = 10; // Limit cross bins
}
endgroup
function int get_transaction_type();
return write ? 1 : 0;
endfunction
endclass
8.2 Coverage Sampling Tricks
class sampling_tricks;
bit [7:0] addr;
// Multiple covergroups in one class
covergroup cg1 @(posedge clk);
coverpoint addr;
endgroup
covergroup cg2 with function sample(bit[7:0] a);
coverpoint a;
endgroup
// Conditional covergroup creation
function new(bit enable_cov);
if (enable_cov) begin
cg1 = new();
cg2 = new();
end
endfunction
// Start/Stop coverage
function void start_coverage();
if (cg1 == null) cg1 = new();
endfunction
function void stop_coverage();
if (cg1 != null) cg1.stop();
endfunction
// Get coverage per coverpoint
function void report_coverage();
$display("CG1 Coverage: %0.2f%%", cg1.get_coverage());
$display("Addr Coverpoint: %0.2f%%", cg1.addr.get_coverage());
endfunction
endclass
9. Assertion Hidden Features
9.1 Advanced Assertion Tricks
module assertion_tricks;
logic clk, reset_n;
logic [7:0] data;
logic valid, ready;
//=========================================================================
// TRICK 1: Assertion with sampled value
//=========================================================================
property check_sampled;
@(posedge clk) disable iff (!reset_n)
valid |-> ##1 (ready && ($past(data) == data));
// Uses $past to reference sampled value
endproperty
a1: assert property (check_sampled);
//=========================================================================
// TRICK 2: Expect statement (blocking assertion)
//=========================================================================
initial begin
expect (@(posedge clk) valid |=> ready)
$display("Handshake completed");
else
$error("Handshake failed");
// Blocks until assertion passes or fails
end
//=========================================================================
// TRICK 3: Assume for input constraints
//=========================================================================
// In testbench, use assume to constrain inputs
assume property (@(posedge clk) valid |-> (data != 0))
else $error("Input constraint violated!");
//=========================================================================
// TRICK 4: Assertion with procedural code
//=========================================================================
sequence check_seq;
@(posedge clk) valid ##1 ready;
endsequence
property with_action;
check_seq;
endproperty
a_action: assert property (with_action)
begin
// Pass action block
pass_count++;
if (pass_count % 100 == 0)
$display("100 successful handshakes");
end
else
$error("Handshake failed");
//=========================================================================
// TRICK 5: Restrict (formal verification hint)
//=========================================================================
restrict property (@(posedge clk) data inside {[0:100]});
// Tells formal tools to only consider data in this range
//=========================================================================
// TRICK 6: Sequence method calls
//=========================================================================
sequence req_seq;
@(posedge clk) req ##[1:5] gnt;
endsequence
property check_after_seq;
@(posedge clk) req_seq.triggered |-> done;
endproperty
endmodule
9.2 Assertion Debugging Tricks
// Enable assertion tracing
initial begin
$assertcontrol(1); // Enable all assertions
$assertcontrol(0); // Disable all assertions
// Detailed control
$assertpasson; // Report pass as well as fail
$assertpassoff; // Don't report pass
$assertfailon; // Report failures
$assertfailoff; // Don't report failures
$assertnonvacuouson; // Report vacuous pass
end
// Assertion message with severity override
a_custom: assert property (prop)
else begin
if (error_count < 10)
$warning("Minor violation #%0d", error_count);
else
$fatal("Too many violations!");
end
10. Compilation and Elaboration
10.1 Compilation Directives
// Conditional compilation
`ifdef SIMULATION
initial $display("In simulation mode");
`elsif SYNTHESIS
// Synthesis specific code
`else
// Default
`endif
// Define macros
`define MAX_SIZE 256
`define MIN(a,b) ((a) < (b) ? (a) : (b))
int size = `MAX_SIZE;
int minimum = `MIN(x, y);
// Include files
`include "definitions.svh"
`include "parameters.sv"
// Line directive
`line 100 "myfile.sv" 1
// Resets line number for error reporting
// Pragma
// pragma protect begin
// Encrypted/protected code
// pragma protect end
// Default nettype
`default_nettype none // Force explicit declarations
`default_nettype wire // Back to default
10.2 Generate Blocks
// Generate loop
module decoder #(parameter WIDTH = 4)(
input [WIDTH-1:0] select,
output logic [2**WIDTH-1:0] out
);
genvar i;
generate
for (i = 0; i < 2**WIDTH; i++) begin : gen_decode
assign out[i] = (select == i);
end
endgenerate
endmodule
// Generate if
generate
if (USE_FIFO) begin : fifo_gen
fifo #(.DEPTH(16)) my_fifo (.*);
end else begin : no_fifo
assign fifo_empty = 1;
end
endgenerate
// Generate case
generate
case (BUS_TYPE)
"APB": apb_interface apb_if(clk);
"AXI": axi_interface axi_if(clk);
"AHB": ahb_interface ahb_if(clk);
endcase
endgenerate
11. Debug and Introspection
11.1 Hierarchical Access
module top;
sub_module sub();
initial begin
// Access signals in sub-module
sub.internal_signal = 1;
// Hierarchical path
$display("Value: %h", top.sub.deep.nested.signal);
// Upward reference (from sub to top)
// In sub_module:
// $display("Top level: %h", $root.top.some_signal);
end
endmodule
// Force and release (debugging)
initial begin
#100ns;
force dut.internal_reg = 8'hAA; // Override value
#50ns;
release dut.internal_reg; // Return to normal
end
11.2 Reflection and Introspection
class reflection_example;
// Get type name
function string get_class_name();
return $typename(this);
endfunction
// Get field name
function void print_type_info();
$display("Class type: %s", $typename(this));
$display("Bits: %0d", $bits(this));
endfunction
endclass
// Usage
reflection_example obj = new();
$display("Type: %s", obj.get_class_name());
// Type comparison
class base; endclass
class derived extends base; endclass
derived d = new();
base b = d;
if ($typename(b) == "derived")
$display("Actually derived type");
11.3 Coverage Introspection
covergroup cg;
addr_cp: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
endgroup
cg my_cg = new();
// Get coverage
real total = my_cg.get_coverage();
real addr_cov = my_cg.addr_cp.get_coverage();
// Get instance coverage (if per_instance=1)
real inst_cov = my_cg.get_inst_coverage();
// Coverage per option
$display("Goal: %0d%%", my_cg.option.goal);
$display("At least: %0d", my_cg.option.at_least);
// Set options at runtime
my_cg.option.goal = 95;
my_cg.addr_cp.option.weight = 10;
12. Performance Tricks
12.1 Faster Simulation
// Use 2-state types when X/Z not needed
bit [31:0] addr; // Faster than logic[31:0]
int counter; // Faster than integer (logic[31:0])
// Disable assertions when not needed
initial begin
`ifdef FAST_SIM
$assertoff; // Disable all assertions
`endif
end
// Use automatic tasks (no static storage)
task automatic fast_task();
int local_var; // Stack allocated
endtask
// Avoid delays in synthesis code
always_comb begin
// #0; // DON'T use delays in combinational logic
y = a & b;
end
// Use non-blocking in sequential, blocking in combinational
always_ff @(posedge clk)
q <= d; // Non-blocking for FF
always_comb
y = a & b; // Blocking for combinational
12.2 Memory Optimization
// Sparse memory with associative array
class sparse_mem;
bit [31:0] mem [bit[31:0]]; // Only allocates when written
function void write(bit[31:0] addr, bit[31:0] data);
mem[addr] = data;
endfunction
function bit[31:0] read(bit[31:0] addr);
return mem.exists(addr) ? mem[addr] : 32'h0;
endfunction
function int get_usage();
return mem.num(); // Number of allocated entries
endfunction
endclass
// Queue instead of dynamic array for growing data
int q[$]; // More efficient for push/pop than dynamic array
13. Hidden Language Features
13.1 Typedef Forward Declaration
// Forward declaration
typedef class my_class;
// Use in another class before definition
class container;
my_class obj; // Can use before my_class is defined
endclass
// Now define it
class my_class;
int data;
endclass
13.2 This Keyword Tricks
class my_class;
int data;
function new(int data);
this.data = data; // Disambiguate parameter from member
endfunction
function my_class get_this();
return this; // Return handle to self
endfunction
function void chain_method();
this.data = 10;
this.other_method();
endfunction
endclass
13.3 Extern Methods
class my_class;
// Declare method
extern function void my_function();
extern task my_task();
endclass
// Define outside class
function void my_class::my_function();
$display("External function");
endfunction
task my_class::my_task();
#10ns;
$display("External task");
endtask
13.4 Pure Virtual
virtual class abstract_base;
pure virtual function void must_implement();
pure virtual task must_implement_task();
// No implementation in base class
endclass
class concrete extends abstract_base;
// MUST implement pure virtual methods
virtual function void must_implement();
$display("Implemented!");
endfunction
virtual task must_implement_task();
$display("Task implemented!");
endtask
endclass
// Cannot instantiate abstract class
// abstract_base ab = new(); // ERROR!
concrete c = new(); // OK
13.5 Parameterized Classes
class generic_fifo #(
parameter WIDTH = 32,
parameter DEPTH = 16,
type T = bit[WIDTH-1:0]
);
T queue[$];
function void push(T data);
if (queue.size() < DEPTH)
queue.push_back(data);
endfunction
function T pop();
return queue.pop_front();
endfunction
endclass
// Usage
generic_fifo#(.WIDTH(64), .DEPTH(32)) fifo64 = new();
typedef struct {
bit[7:0] addr;
bit[31:0] data;
} packet_t;
generic_fifo#(.type(packet_t)) packet_fifo = new();
14. Operator Overloading (Copy, Compare, Print)
14.1 UVM Macros for Automation
class transaction extends uvm_sequence_item;
rand bit [7:0] addr;
rand bit [31:0] data;
rand bit write;
// Automation macros
`uvm_object_utils_begin(transaction)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(write, UVM_ALL_ON)
`uvm_object_utils_end
// Provides automatic:
// - copy()
// - compare()
// - print()
// - pack()/unpack()
function new(string name = "transaction");
super.new(name);
endfunction
endclass
// Usage
transaction t1 = transaction::type_id::create("t1");
transaction t2 = transaction::type_id::create("t2");
t1.addr = 8'h10;
t2.copy(t1); // Deep copy
if (t1.compare(t2)) // Deep compare
$display("Match");
t1.print(); // Formatted print
15. Advanced Tricks Collection
15.1 Bit Select Tricks
bit [31:0] data = 32'hDEAD_BEEF;
// Part select
bit [7:0] byte0 = data[7:0]; // 0xEF
bit [7:0] byte3 = data[31:24]; // 0xDE
// Indexed part select (constant width)
bit [7:0] byte_n;
int n = 2;
byte_n = data[n*8 +: 8]; // data[16 +: 8] = data[23:16]
// Decrement part select
byte_n = data[n*8+7 -: 8]; // data[23 -: 8] = data[23:16]
// Variable bit select
int bit_pos = 5;
bit selected_bit = data[bit_pos]; // data[5]
15.2 Wildcard Comparison
bit [7:0] opcode = 8'b1010_0011;
// Wildcard comparison (? = don't care)
if (opcode ==? 8'b1010_????)
$display("Match!"); // Matches
// In case statement
case (opcode) matches
8'b0000_????: $display("Type 0");
8'b0001_????: $display("Type 1");
8'b1010_????: $display("Type A"); // Matches this
endcase
// In constraints
constraint wildcard_constraint {
opcode ==? 8'b10??_????; // Force upper 2 bits = 10
}
15.3 Unique and Priority Case
// Unique case - ensure no overlap, all covered
unique case (state)
2'b00: next = IDLE;
2'b01: next = ACTIVE;
2'b10: next = WAIT;
2'b11: next = DONE;
endcase
// Warnings if: overlap detected, not all cases covered
// Priority case - first match wins
priority case (cmd)
4'b0000: op = NOP;
4'b0001: op = READ;
4'b001?: op = WRITE; // Matches 0010, 0011
4'b01??: op = SPECIAL; // Never reached due to priority
endcase
// Unique if
unique if (a) action_a();
else if (b) action_b();
else if (c) action_c();
// Warns if multiple conditions true
// Priority if
priority if (a) action_a();
else if (b) action_b();
else if (c) action_c();
15.4 Dist Constraint Tricks
class dist_tricks;
rand bit [2:0] cmd;
rand bit [7:0] addr;
//=========================================================================
// := vs :/ in dist
//=========================================================================
constraint cmd_dist {
// := assigns weight to specific value
cmd dist {0 := 40, 1 := 30, 2 := 20, 3 := 10};
// 0: 40%, 1: 30%, 2: 20%, 3: 10%
}
constraint addr_dist {
// :/ divides weight among range values
addr dist {[0:63] :/ 80, [64:255] :/ 20};
// Each value in [0:63] gets 80/64 = 1.25%
// Each value in [64:255] gets 20/192 = 0.104%
}
//=========================================================================
// Multiple ranges in dist
//=========================================================================
rand bit [9:0] value;
constraint multi_range_dist {
value dist {
[0:99] := 30, // 30% total for range
[100:199] := 50, // 50% total
[200:1023] := 20 // 20% total
};
}
endclass
15.5 Inside Operator Tricks
// Inside with ranges
if (data inside {[0:100], [200:300], 500})
$display("Match");
// Inside in constraints
constraint addr_constraint {
addr inside {[0:255], [1024:2047]};
}
// Inside with queue
int valid_vals[$] = '{10, 20, 30, 40};
if (data inside {valid_vals})
$display("Valid");
// Not inside
if (!(addr inside {[100:199]}))
$display("Outside range");
15.6 Cast and Size Extension Tricks
// Automatic sign extension
bit [7:0] small = 8'hFF;
bit [15:0] large = 16'(small); // Zero extend: 0x00FF
// Signed extension
byte signed_byte = -1; // 8'hFF
int signed_int = signed_byte; // Sign extends to 32'hFFFFFFFF
// Force unsigned interpretation
int val = unsigned'(signed_byte); // 255, not -1
// Truncation
bit [31:0] wide = 32'hDEAD_BEEF;
bit [7:0] narrow = wide[7:0]; // 0xEF (truncate)
// Explicit cast
real r = 3.14;
int i = int'(r); // 3
15.7 Class Handle Tricks
class packet;
int data;
endclass
// Null check shorthand
packet pkt;
if (pkt != null) pkt.data = 10;
// Better: Use ternary
int value = (pkt != null) ? pkt.data : 0;
// Copy vs reference
packet p1 = new();
packet p2;
p2 = p1; // Both point to same object (shallow copy)
p2.data = 100; // Also changes p1.data!
packet p3 = new p1; // Deep copy (if copy() not defined)
// OR
p3 = new();
p3.copy(p1); // Proper deep copy
15.8 Mailbox Type Checking
// Typed mailbox
mailbox #(packet) mb = new();
packet pkt = new();
mb.put(pkt);
packet received;
mb.get(received); // Type-safe
// Generic mailbox (any type)
mailbox mb_generic = new();
mb_generic.put(pkt);
mb_generic.put(42);
mb_generic.put("string");
// Not type-safe!
15.9 Interface Parameter Trick
// Interface with parameters
interface generic_if #(parameter WIDTH = 32)(input logic clk);
logic [WIDTH-1:0] data;
logic valid;
endinterface
// Instantiate with different widths
generic_if #(8) if8(clk);
generic_if #(64) if64(clk);
// Module can be generic
module generic_module #(parameter WIDTH = 32)(
generic_if #(WIDTH) bus
);
// Use bus.data which is WIDTH bits
endmodule
15.10 Disable Fork Trick
// Kill all child processes
fork
begin
forever #10ns $display("Process 1");
end
begin
forever #20ns $display("Process 2");
end
join_none
#100ns;
disable fork; // Kills all forked processes
$display("All processes killed");
// Named block disable
begin : my_block
fork
#10ns $display("A");
#20ns $display("B");
join_any
end
disable my_block; // Disable specific block
15.11 Wait Fork Trick
// Wait for all forked processes
fork
#10ns $display("Task 1");
#20ns $display("Task 2");
#15ns $display("Task 3");
join_none
// Continue immediately, but can wait later
$display("Forked");
wait fork; // Wait for all forked processes to complete
$display("All done");
15.12 Parameter Port Lists
// Override parameters during instantiation
module fifo #(
parameter DEPTH = 16,
parameter WIDTH = 32
)(
input clk,
input [WIDTH-1:0] data_in
);
endmodule
// Instantiation methods
fifo #(.DEPTH(32), .WIDTH(64)) f1 (.*); // Named
fifo #(32, 64) f2 (.*); // Positional
fifo #(.DEPTH(32)) f3 (.*); // Partial (WIDTH=default)
// Defparam (legacy, avoid)
defparam top.sub.fifo.DEPTH = 64;
15.13 Bind Tricks
// Bind assertions to all instances of a module
bind fifo fifo_assertions #(.DEPTH(DEPTH))
checker (.*);
// Bind to specific instance
bind top.sub.fifo fifo_assertions
checker (.*);
// Conditional bind
`ifdef ENABLE_ASSERTIONS
bind dut protocol_checker check_inst (.*);
`endif
15.14 Always_comb Gotchas
// always_comb automatically triggers on ANY input change
always_comb begin
y = a & b; // Triggers when a OR b changes
end
// Hidden feature: Can read from memory
logic [7:0] mem[256];
logic [7:0] addr;
logic [7:0] data;
always_comb begin
data = mem[addr]; // Allowed! Triggers when addr changes
end
// But assignments must be to same variable
always_comb begin
if (sel == 0)
out = a;
else
out = b;
// Must assign 'out' in all paths
end
15.15 Enum Tricks
typedef enum {RED, GREEN, BLUE, YELLOW} color_t;
color_t color;
// Enum methods
color = color.first(); // RED
color = color.next(); // GREEN
color = color.next(2); // BLUE (skip 2)
color = color.prev(); // GREEN
color = color.last(); // YELLOW
int num_colors = color.num(); // 4
string name = color.name(); // "GREEN" if color==GREEN
// Convert from integer
color = color_t'(2); // BLUE
// Loop through enum
for (color = color.first(); color != color.last(); color = color.next())
$display("Color: %s", color.name());
15.16 rootandunit Tricks
// $root - Access absolute top
module sub;
initial begin
$display("Top signal: %h", $root.top.signal);
end
endmodule
// $unit - Access compilation-unit scope
// File: definitions.sv
int global_counter = 0; // In $unit scope
// File: testbench.sv
initial begin
$unit::global_counter++; // Access compilation unit variable
end
15.17 Void Cast Trick
// Ignore function return value
function int my_function();
return 42;
endfunction
// Normal call (warning about unused return value)
my_function(); // Warning!
// Void cast to suppress warning
void'(my_function()); // No warning
// Commonly used with randomize
void'(pkt.randomize()); // Don't care about return value
// With std::randomize
void'(std::randomize(addr, data));
15.18 Expression Width Tricks
// Self-determined vs context-determined
bit [7:0] a = 8'hFF;
bit [15:0] result;
result = a + 1; // 8-bit math: 0x00 (wraps), then extended
result = a + 16'd1; // 16-bit math: 0x0100 (correct)
// Force width with concatenation
result = {8'd0, a} + 1; // 16-bit: 0x0100
// Replication for width
bit sign_extend = a[7];
result = {{8{sign_extend}}, a}; // Sign extend 8-bit to 16-bit
15.19 Const Ref Arguments
// Avoid copying large structures
function void process(const ref bit[1000:0] large_data);
// 'ref' passes by reference (no copy)
// 'const' prevents modification
$display("Processing data");
endfunction
// Regular vs ref
task process_normal(bit[1000:0] data);
// Copies 1001 bits on call!
endtask
task process_ref(ref bit[1000:0] data);
// No copy, just reference
data[0] = 1; // Can modify original
endtask
task process_const_ref(const ref bit[1000:0] data);
// No copy, cannot modify
// data[0] = 1; // ERROR!
endtask
15.20 Let Construct (Assertion Helper)
// Define reusable expressions
module assertion_let;
logic clk;
logic [7:0] addr, data;
logic valid;
// Let declaration - like a macro for assertions
let is_valid_addr(a) = a inside {[0:127]};
let is_aligned(a) = (a[1:0] == 2'b00);
property p_check_addr;
@(posedge clk) valid |-> is_valid_addr(addr) && is_aligned(addr);
endproperty
a_check: assert property (p_check_addr)
else $error("Address check failed!");
// Multi-argument let
let data_in_range(d, min, max) = (d >= min) && (d <= max);
property p_data_range;
@(posedge clk) valid |-> data_in_range(data, 0, 200);
endproperty
a_range: assert property (p_data_range);
endmodule
Hidden Feature Summary Table
| Feature | Syntax | Example | Benefit |
|---|---|---|---|
| Default pattern | ‘{default: val} | arr[10] = ‘{default: 0} | Quick initialization |
| Named pattern | ‘{idx: val} | arr = ‘{0:10, 5:50, default:0} | Specific + default |
| Streaming | {<<N{data}} | word = {<<8{bytes}} | Pack/unpack |
| Inside | val inside {ranges} | if (x inside {[0:10], 20}) | Multiple ranges |
| Part select | [base +: width] | data[n*8 +: 8] | Variable slicing |
| Wildcard | ==? | opcode ==? 4’b10?? | Don’t care bits |
| Void cast | void'(func()) | void'(randomize()) | Suppress warning |
| Const ref | const ref type | task(const ref data) | No copy |
| Let | let name = expr | let valid(a) = a>0 | Reusable expr |
| $past | $past(sig, N) | $past(addr, 2) | History access |
| rose/fell | $rose(sig) | if($rose(req)) | Edge detection |
| Enum methods | enum.next() | color.next(2) | Enum iteration |
| Type operator | type(expr) | typedef type(a) t | Type extraction |
| Queue methods | q.find() | q.find with (item>10) | Filtering |
| Cross binsof | binsof(cp.bin) | ignore_bins x = binsof… | Cross filtering |
Tricky Examples Collection
Example 1: One-Liner Tricks
// Swap without temp variable
{a, b} = {b, a};
// Clear all bits except specific ones
data &= ~32'h0000_00FF; // Clear lower byte
// Set specific bits
data |= 32'h0000_00FF; // Set lower byte
// Toggle bits
data ^= 32'h0000_00FF; // Toggle lower byte
// Check if power of 2
bit is_pow2 = (val != 0) && ((val & (val - 1)) == 0);
// Round up to power of 2
function int round_up_pow2(int val);
val--;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
val++;
return val;
endfunction
// Count trailing zeros
function int ctz(bit[31:0] val);
return $clog2(val & -val);
endfunction
// Reverse byte order (endian swap)
function bit[31:0] bswap(bit[31:0] val);
return {val[7:0], val[15:8], val[23:16], val[31:24]};
endfunction
Example 2: Queue Advanced Tricks
int q[$] = '{1, 2, 3, 4, 5};
// Find all matching elements
int result[$];
result = q.find with (item > 2); // {3, 4, 5}
// Find with index
result = q.find_index with (item > 2); // {2, 3, 4} (indices)
// Find first
result = q.find_first with (item > 2); // {3}
// Find last
result = q.find_last with (item < 4); // {3}
// Min/max with expression
int mins[$] = q.min(); // {1}
int maxs[$] = q.max(); // {5}
// Unique values
int dup_q[$] = '{1, 2, 2, 3, 3, 3, 4};
int uniq[$] = dup_q.unique(); // {1, 2, 3, 4}
// Reverse
q.reverse(); // {5, 4, 3, 2, 1}
// Sort with expression
q.sort with (item); // Ascending
q.rsort with (item); // Descending
// Shuffle
q.shuffle(); // Random order
Example 3: Constraint Distribution Secret
class dist_secret;
rand bit [7:0] value;
// Secret: Combine := and :/ for fine control
constraint mixed_dist {
value dist {
0 := 10, // Exactly 0: 10%
[1:9] :/ 40, // 1-9: each gets 40/9 = 4.44%
[10:99] := 30, // 10-99 total: 30%
[100:255] :/ 20 // 100-255: each gets 20/156
};
}
// Secret: Weight can be variable!
rand int weight;
constraint variable_weight {
weight inside {[1:10]};
value dist {0 := weight, [1:255] := (100-weight)};
}
endclass
Example 4: Coverage Wild Tricks
covergroup wild_coverage;
// TRICK: Coverpoint on expression
coverpoint (addr[7:4]) { // Cover only upper nibble
bins values[] = {[0:15]};
}
// TRICK: Coverpoint on function call
coverpoint calc_parity(data) {
bins even = {0};
bins odd = {1};
}
// TRICK: Bins with && condition
coverpoint addr {
bins special_addr[] = {[0:255]} with
(item[1:0] == 2'b00 && item < 128);
// Only multiples of 4 less than 128
}
// TRICK: Transition with range repetition
coverpoint state {
bins long_active = (IDLE => ACTIVE [*5:10] => DONE);
// Stay in ACTIVE for 5-10 cycles
}
// TRICK: Cross with specific bin selection
addr_cp: coverpoint addr {
bins addr_bins[] = {[0:7]};
}
data_cp: coverpoint data {
bins data_bins[] = {[0:3]};
}
selective_cross: cross addr_cp, data_cp {
bins special = binsof(addr_cp) intersect {0, 1, 2} &&
binsof(data_cp) intersect {0, 1};
// Only covers (0,0), (0,1), (1,0), (1,1), (2,0), (2,1)
}
endgroup
Example 5: System Task Tricks
// Current hierarchical name
$display("Module: %m"); // Prints full hierarchical path
// Simulation time control
$printtimescale; // Print timescale
$timeformat(-9, 2, " ns", 10); // Format time display
// Value change dump tricks
initial begin
$dumpfile("waves.vcd");
$dumpvars(0, top); // Dump all in top
$dumpvars(1, top.sub); // Dump 1 level deep
#1000ns $dumpoff; // Stop dumping
#500ns $dumpon; // Resume dumping
$dumpflush; // Flush to file
$dumplimit(1000000); // Limit file size
end
// Random seed control
int seed = $get_initial_random_seed(); // Get current seed
void'($urandom(seed)); // Set new seed
// Process control
initial begin
fork
begin : proc1
forever #10ns $display("Proc1");
end
join_none
#100ns;
process p = process::self();
p.kill(); // Kill this process (dangerous!)
end
Example 6: Faster Simulation Tricks
// Use 2-state when X/Z not needed
bit [31:0] counter; // Faster than logic[31:0]
int address; // Faster than integer (logic[31:0])
// Packed struct instead of unpacked
typedef struct packed { // Single vector, faster
bit [7:0] addr;
bit [31:0] data;
} fast_struct;
typedef struct { // Separate variables, slower
bit [7:0] addr;
bit [31:0] data;
} slow_struct;
// Automatic tasks (no static storage)
task automatic fast_task(int data);
int local; // Allocated on stack
endtask
// Disable assertions for speed
`ifdef FAST_SIM
initial $assertoff;
`endif
// Use queues instead of dynamic arrays for push/pop
int q[$]; // Faster push/pop
q.push_back(data);
q.pop_front();
// Sparse memory with associative array
bit [31:0] sparse_mem [bit[31:0]]; // Only allocates when used
sparse_mem[32'h1000] = 32'hDEAD; // One entry allocated
// Packed arrays for large memories
bit [7:0] packed_mem [0:65535]; // Contiguous memory
Example 7: Special Assignment Operators
// Increment/Decrement
counter++; // counter = counter + 1
counter--; // counter = counter - 1
++counter; // Pre-increment
--counter; // Pre-decrement
// Compound assignment
data += 10; // data = data + 10
data -= 5; // data = data - 5
data *= 2; // data = data * 2
data /= 4; // data = data / 4
data %= 3; // data = data % 3
data &= 8'hF0; // data = data & 8'hF0
data |= 8'h0F; // data = data | 8'h0F
data ^= 8'hFF; // data = data ^ 8'hFF
data <<= 2; // data = data << 2
data >>= 1; // data = data >> 1
Example 8: Macro Magic
// Multi-line macros
`define ASSERT(condition, message) \
assert (condition) \
else $error(message)
`ASSERT(data < 256, "Data overflow!")
// Macro with arguments
`define MAX(a, b) ((a) > (b) ? (a) : (b))
`define MIN(a, b) ((a) < (b) ? (a) : (b))
`define CLAMP(val, min, max) `MAX(`MIN(val, max), min)
result = `MAX(x, y);
clamped = `CLAMP(data, 0, 255);
// Stringify macro argument
`define PRINT_VAR(var) $display(`"var = %0d`", var)
`PRINT_VAR(counter); // Prints: counter = <value>
// Concatenation in macros
`define REG(name) logic name``_reg
`REG(data); // Creates: logic data_reg
// File and line macros
$display("File: %s, Line: %0d", `__FILE__, `__LINE__);