Singleton class in Systemverilog
Singleton class is a type of class which only has one object. Once constructed, it will persist and can be accessed everywhere. Singleton pattern is actually one of the simpliest design pattern, and also pretty common in many programming languages.
How to create a singleton class in Systemverilog
Creating a protected constructor
To create a singleton class, firstly we need a protected constructor.
- By make the constructor protected, we have a full control of how this class is constructed, because this constructor
new()
function can only be called inside this class.
Adding static m_self variable and static get_inst() function
Since the constructor new()
is protected, we need to create a function that acts as a contructor and return the handle to the object.
- The
m_self
will have the handle to theexample_singleton_class
object itself. - The
get_inst()
will check if them_self
is null. If it is, we will construct aexample_singleton_class
object then assign the handle of that object tom_self
variable. If them_self
already has the handle of the singleton object, we just need to return that handle. By doing this, we can make sure that thenew()
constructor will only called one time when theget_inst()
is called the first time. Eventually, only oneexample_singleton_class
object will be created. - We need to define the
get_inst()
function as static, so that we can called it by using the class name with scope operatorexample_singleton_class::get_inst()
. This provided the global access to theget_inst()
function. Also since it isstatic
, theget_inst()
cannot be defined asvirtual
. - Because the
get_inst()
is static, them_self
must be static as well (The static function can only access to the static variable of the class). - We also want to defined the
m_self
as protected, so that the only way to get the handle of this object is to call theget_inst()
function.
Constructing a singleton class
Now, when we need the access to the object of example_singleton_class
, we just need to call the get_inst()
function as below. Since this function is static, we can call it everywhere in our environment.
Eager initialization
We can have the singleton class to construct itself by calling the get_inst()
when initialize the m_self
variable
When doing this, the singleton class will be constructed at the very beginning of the simulation. This way to constructing singleton object is called eager initialization.
Is it any good?
Which class can be singleton
From it properties, a class might need below requirements can be a candidate for creating singleton class:
- Need to be access from many unrelated part of our environment.
- Have the access to a shared resource.
- Have a single purpose which many classes needs.
This could be a class that provide the backdoor access to the memory of DUT, a class that handle all the plusargs that passed from the run time commands or a class to dynamic allocate/deallocate index number of data packets.
Some uvm singleton classes
If using the uvm methodology, we can see that several classes are actually singleton:
- uvm_root and uvm_top:
- uvm_root is the the singleton class, and the uvm_top has the handle to that singleton class.
- This class serves as a top for all of the uvm component, and it is contructed by default when we call
run_test()
.
- uvm_core_service_t:
- From uvm1.2, the core services class will contains the method to access to handle of several uvm feature such as Factory, Report Server.
- To get the handle of this singleton class, we just call
uvm_core_service_t cs = uvm_core_service_t::get()
- uvm_factory:
- Factory is actually another type of design pattern, but we’ll talk about it in another post.
- In uvm, uvm_factory allow us to substitute a class with its child class when constructing object.
- To have the handle to uvm_factory, we can call its static class as other singleton:
uvm_factory factory = uvm_factory::get();
- There’s another way to get uvm_factory singleton handle using core services:
uvm_factory factory = uvm_core_service_t::get().get_factory()
- To understand how uvm works, read this post: How uvm_factory acutally works
- uvm_pool:
uvm_pool
is a class which acts as an associative array.- We can decide the type of key and data of this associative array by defining 2 parameters of
uvm_pool
class. - This class is singleton class so it can be used to share data between components, objects in the environment.
- The 2 common
uvm_pool
built-in the uvm areuvm_event_pool
anduvm_barrier_pool
- uvm_queue:
- Similar to
uvm_pool
,uvm_queue
is a class which act as a Systemverilog queue. - Defined as singleton class, this queue can be accessed anywhere to share data.
- Similar to
Alternative solution: static method in package
Finding more information
To have more understanding as well as more examples, check below references:
- Singleton Pattern - Creational Design Patterns
- How uvm_factory acutally works
- uvm_barrier_pool
- uvm_event_pool
[Tags
systemverilog
uvm
]