Thought Leadership

Dig a Pool of Specialized SystemVerilog Classes

Introduction

SystemVerilog classes are a great way to encapsulate both variables and the routines that operates on them. What if you want to reuse the methods but change the type of properties? Use a parameter and specialize it!

Definition

In Object-Oriented Programming (OOP), encapsulate variables and routines in a class. A class variable is called a property and a class routine is a method. By grouping them together, the result is more reusable than if they were kept separate.

The First Assignment

Your manager asks you to write a class to represent a stack. When you tell her that SystemVerilog already has a queue that can be used as a LIFO (Last In, First Out), she responds, “Yes, but on the last project, someone kept switching between push_front(), push_back(), and pop_front(), pop_back(), causing testbench bugs. Give me a stack class with just push() and pop().” Phew!

Your first attempt might look like the following, with no error checking.

class Stack;
  bit[31:0] items[100];
  int idx = -1;
  string name;

  function new(string name);
    this.name = name;
  endfunction

  function push(input bit[31:0] val);
    items[++idx] = val;
  endfunction

  function bit[31:0] pop();
    return items[idx--];
  endfunction
endclass

Here is a short test.

Stack s;
initial begin
  s = new("s32");                // Construct a Stack object
  s.push('1);                    // Push all 1’s
  $display("pop()=%x", s.pop()); // Expect FFFF_FFFF
end

That was good, now specialize it

Your manager liked that solution but asked why you only used 32-bit numbers. After all, the next project has a 64-bit data path. Can you make a compatible stack class? How about 128-bits?

Rather than make separate classes for each width, create a parameterized class. This one has a default width of 4 bits so it will obvious if you forget to specify the correct width.

class Stack #(parameter int WIDTH=4);
  bit[WIDTH-1:0] items[100];
  int idx = -1;

  function push(input bit[WIDTH-1:0] val); …
  function bit[WIDTH:0] pop(); …

Declare two handles for this class, first with the default width, and another for 64-bit values.

Stack s4;        // Handle to a 4-bit stack object
Stack #(64) s64; // Handle to a 64-bit stack object

Try these two stack types with the test code from the first section.

When you give a value for a parameter when declaring a class handle (or use the default value), it is called “specialization”. Under the hood, it is as if the compiler creates a new type (but you can’t use it directly). You can imagine it creates the following for a specialization of WIDTH=64.

class _Stack__64; // Equivalent class for specialization: Stack #(64) s64
  bit[64-1:0] items[100];
  int idx = -1;

  function push(input bit[64-1:0] val); …
  function bit[64-1:0] pop(); …
endclass

A group of stacks

A verification engineer saw this parameterized class and said that he wanted to make a group of these stacks as part of the reference model. However, when he tried the following code, it wouldn’t compile.

Stack q[$];         // A queue of stack handles
initial begin
  q.push_back(s4);
  q.push_back(s64); // Bad assign to Stack#(4) from Stack#(64)
end

The problem is that the two specializations, Stack#(4) and Stack#(64), are different types, and you can’t assign between them, not even with the $cast() system task. You can’t group dissimilar types.

Twisting by the Pool

You can group multiple objects together if they have the same type. Wrap this group in a class with methods to make it easier to manipulate them. UVM calls this a “pool”.

A pool of specialized classes
A pool of specialized classes

(The formal name for this group is an “aggregate” which means each object can exist on its own. The opposite is “composition”, such as an Animal class that is composed from Head and Body classes.)

Here is the Pool class. The class defines T, a type based on the width. (Note that the keyword “parameter” is optional.)

class Pool #(int WIDTH=4);  // Pool class
  typedef Stack #(WIDTH) T; // Type of objects in pool
  T queue[$];               // Queue of specialized handles

  function void add(input Stack s);
    queue.push_back(s);
  endfunction

  // Print the name of all objects
  function void print();
    foreach (queue[i])
      $display("Pool[%0d] name='%s'", i, queue[i].name);
  endfunction
endclass

Here is code to test the Stack and Pool classes.

Stack #(4) s0, s1;
Pool #(4)  p4;
initial begin
  p4 = new();       // Construct a pool of Stack#(4) handles
  s0 = new("zero"); // Construct a 4-bit Stack object
  p4.add(s0);       // Add it to the pool
  s1 = new("one");  // Construct another 4-bit Stack
  p4.add(s1);       // Add it to the pool
  p4.print();       // Print the names of all Stack objects
end

What happens if you create a stack of 8-bit values and try to add this to the Pool #(4) instance?

More Experiments

The code above showed how to parameterize a class with a value to create vectors of different widths. What if you want to make a stack of real numbers? SystemVerilog also supports type parameters. Here is the start of a stack class where you can change the type. I have deliberately left out the push() and pop() methods so you can write them yourself.

class Stack_T #(type T = int);
  int idx;
  T items[100]; // Store the stack made of items of type T

  // What do the push() and pop() methods look like?

endclass

// Declare handle to various type stacks
Stack_T              stack_int;   // Stack of int’s
Stack_T #(bit[15:0]) stack_bit16; // Stack of 16-bit values
Stack_T #(real)      stack_real;  // Stack of reals

Learn More

You can learn more about these topics including Oriented Programming with the Siemens SystemVerilog for Verification course. It is offered in instructor led format by our industry expert instructors, or in a self-paced on-demand format. It can also be tailored to address your specific design goals and show you how to set up an environment for reuse for additional designs.  Also, you can now earn a digital badge/level 1 certificate by taking our Advanced Topics Badging Exam. This will enable you to showcase your knowledge of this topic by displaying the badge in your social media and email signature.

Chris Spear

Chris brings over forty years of EDA expertise to Siemens customers. Holding a degree in electrical engineering from Cornell University, Chris has developed deep roots in the EDA industry, including as a Principal Application Consultant. Chris is also an industry author, writing the 2012 best-selling “SystemVerilog for Verification” and developing the IEEE standard for random seeding and File I/O PLI package that is part of SystemVerilog. Having taught thousands of engineers around the world, Chris is driven by a passion for learning new techniques and then helping others learn best practices for hardware verification. Outside of work, you may see Chris bicycling over 12,000-foot mountain passes.

More from this author

Leave a Reply

This article first appeared on the Siemens Digital Industries Software blog at https://blogs.sw.siemens.com/verificationhorizons/2022/10/17/dig-a-pool-of-specialized-systemverilog-classes/