SystemVerilog Hidden Features and Advanced Tricks

Lesser-Known Features, Shortcuts, and Power User Techniques

Advanced SystemVerilog capabilities that most people don’t know about

Table of Contents

  1. Array Initialization Tricks
  2. Assignment Patterns
  3. Streaming Operators
  4. Type Casting Magic
  5. String Manipulation
  6. System Function Tricks
  7. Constraint Tricks
  8. Coverage Secrets
  9. Assertion Hidden Features
  10. Compilation and Elaboration
  11. Debug and Introspection
  12. 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 rootandrootandunit 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

FeatureSyntaxExampleBenefit
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
Insideval 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 castvoid'(func())void'(randomize())Suppress warning
Const refconst ref typetask(const ref data)No copy
Letlet name = exprlet valid(a) = a>0Reusable expr
$past$past(sig, N)$past(addr, 2)History access
rose/rose/fell$rose(sig)if($rose(req))Edge detection
Enum methodsenum.next()color.next(2)Enum iteration
Type operatortype(expr)typedef type(a) tType extraction
Queue methodsq.find()q.find with (item>10)Filtering
Cross binsofbinsof(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__);

Leave a Comment

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

Scroll to Top