TL; DR: Singleton is an oft-maligned Object Oriented Design Pattern. It’s used to ensure only a single object of a class throughout your code. uvm_root is made to be a singleton. We walked through a way to write your own singleton.
- Introduction
- Video: live code walkthrough
- Why?
- Usage?
singleton::get()
- How do we write one?
- Final notes
- References
Introduction:
Singleton is a Object Oriented Programming (OOP) design pattern and is used to ensure that a class has only a single instance / object no matter how many times it’s called or invoked. The design pattern ensures this unique feature.
Video: live code walkthrough
Why?
(Why would we want to use it?)
The Singleton is used in UVM’s (Universal Verification Methodology) base classes: uvm_root
. This is also used as a building block for the Proxy and Factory discussion we’ll have in following blog posts.
usage?
Key advantages of doing it the way described in this post:
- Not declaring the object as static: Avoids the ‘random static initialization order’ fiasco
- Using a ‘
local
‘ keyword before the internal static handle makes the access to the object much stricter and through declared functions only. - ‘
local
‘ declaration in front of thenew()
function to ensure only get can ‘new
‘ the object. So, the only way to ‘new’ this object is to call it’s ‘get
‘ function. - Can’t extend this class and call
super.new()
as the constructor is declared with the qualifierlocal
How Do we write one?
Introduction to Static classes:
Ensures only one object / instance is created. Original inspiration for our Singleton
design pattern. Singleton
extends the static idea by adding stronger APIs around classes. Also, doesn’t allow more than one class object across the whole program. Whereas, you can always declare a regular class instantiated as static, though that is only talking about making that object as having a memory space which is now accessible to several accessors.
Execute this code in an online editor! – EDA Playground
https://www.edaplayground.com/x/5Uwg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | class Component; endclass: Component class Root extends Component; // local vars local static Root me; local static int access_cnt = 0; // new local function new(); super.new(); endfunction: new // get static function Root get(); if (me == null) me = new(); access_cnt += 1; print(); return me; endfunction: get // print static function void print(); $display("access_cnt = ", access_cnt); endfunction: print endclass: Root module my_test; Root m_root, m_root_copy; initial begin m_root = Root::get(); m_root_copy = m_root_copy.get(); void'(m_root_copy.print()); end endmodule // my_test |
// Output:
> run
access_cnt = 1
access_cnt = 2
access_cnt = 2
Final Notes:
It’s valuable to understand the key (think static and it’s usage) ideas behind Singleton to extend this into: Factory and Proxy usage.
Some background on why it’s often maligned. It’s also known as an Anti-Pattern.
References
https://en.wikipedia.org/wiki/Singleton_pattern
https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons
https://www.edn.com/design/systems-design/4461513/Design-patterns-in-SystemVerilog-OOP-for-UVM-verification
My posts in this series:
1. Singleton: https://jigarsavla.wordpress.com/2019/07/30/singleton-design-patterns-made-simple/
2. Proxy: https://jigarsavla.wordpress.com/2019/08/13/proxy-design-patterns-made-simple/
3. Factory: https://jigarsavla.wordpress.com/2019/08/15/factory-design-patterns-made-simple/
I’m a bit confused that a singleton can only hold 1 instance or object how many time it is called or invoked from your post. But your result of output is
access_cnt = 1
access_cnt = 2
access_cnt = 2
access_cnt has increased 2 not just hold 1.
Can you clarify which point I can see the singleton can only hold 1 instance/object?