SystemVerilog and Python
Design patterns in programming are when engineers find themselves writing the same code over and over to solve the same problems. Design patterns for statically typed object oriented languages (C++ and Java) were cataloged and enshrined in the famous book, "Design Patterns: Elements of Reusable Object-Oriented Software" by Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm. The authors are lovingly called, The Gang of Four, or the GOF and the book is often referred to as the GOF book.
The subset of SystemVerilog used in writing testbenches is a statically typed object oriented language (it's most similar to Java). As people started using SystemVerilog to write testbenches, frameworks for writing testbenches quickly became popular. These frameworks all provide code that implements design patterns from the GOF book. The various frameworks were similar because they were all essentially implementing the same design patterns. Eventually the various frameworks all coalesced into one, the humbly named, Universal Verification Methodology, or UVM.
Below is a table that matches up GOF design patterns with their UVM implementation. This was adapted from this presentation:
If we switched from SystemVerilog to Python for writing our testbenches, would we need to re-implement each of those parts of the UVM? Python is not a statically typed object oriented language like Java and SystemVerilog. It is a dynamically typed language. Prominent and well respected computer scientist Peter Norvig explored this topic for us already. He did this when Python was still a very young language, so he examined other dynamic languages instead (Dylan and Lisp) and he concluded that of the 23 design patterns from the GOF book, 16 of them are either invisible or greatly simplified due to the nature of dynamic languages and their built-in features. As an example to explain how this could be, he points out that a defining a function and calling it used to be design patterns. Higher-level languages came along and made the pattern of defining and calling a function a part of the language.
This is essentially what has happened with dynamic languages. Many design patterns from GOF are now simply part of the language. According to Dr. Norvig, the patterns that dynamic languages obsolete are:
That reduces the above table to:
Trusting that, if we were to write a pure Python testbench we can see that we would still likely implement a few design patterns. Thinking about this, it makes sense that we'd still probably have classes dedicated to transforming high-level sequence items to pin wiggles, just like the sequencer and driver work together to do in the UVM. It also makes sense that we'd have a class hierarchy to organize and relate components (such as the sequencer and driver equivalents) and sequences (high-level stimulus generation). Thing like that.
The more striking thing is the amount of code we would not need.
The subset of SystemVerilog used in writing testbenches is a statically typed object oriented language (it's most similar to Java). As people started using SystemVerilog to write testbenches, frameworks for writing testbenches quickly became popular. These frameworks all provide code that implements design patterns from the GOF book. The various frameworks were similar because they were all essentially implementing the same design patterns. Eventually the various frameworks all coalesced into one, the humbly named, Universal Verification Methodology, or UVM.
Below is a table that matches up GOF design patterns with their UVM implementation. This was adapted from this presentation:
GOF Pattern Name | UVM name |
---|---|
Factory Method, Abstract Factory | uvm_factory, inheriting from uvm base classes |
Singleton | UVM Pool, UVM Global report server, etc. |
Composite | UVM Component Hierarchy, UVM Sequence Hierarchy |
Facade | TLM Ports, UVM scoreboards |
Adapter | UVM Reg Adapter |
Bridge | UVM sequencer, UVM driver |
Observer | UVM Subscriber, UVM Monitor, UVM Coverage |
Template Method | UVM Transaction (do_copy, do_compare), UVM Phase |
Command | UVM Sequence Item |
Strategy | UVM Sequence, UVM Driver |
Mediator | UVM Virtual Sequencer |
If we switched from SystemVerilog to Python for writing our testbenches, would we need to re-implement each of those parts of the UVM? Python is not a statically typed object oriented language like Java and SystemVerilog. It is a dynamically typed language. Prominent and well respected computer scientist Peter Norvig explored this topic for us already. He did this when Python was still a very young language, so he examined other dynamic languages instead (Dylan and Lisp) and he concluded that of the 23 design patterns from the GOF book, 16 of them are either invisible or greatly simplified due to the nature of dynamic languages and their built-in features. As an example to explain how this could be, he points out that a defining a function and calling it used to be design patterns. Higher-level languages came along and made the pattern of defining and calling a function a part of the language.
This is essentially what has happened with dynamic languages. Many design patterns from GOF are now simply part of the language. According to Dr. Norvig, the patterns that dynamic languages obsolete are:
- Abstract-Factory
- Flyweight
- Factory-Method
- State
- Proxy
- Chain-Of-Responsibility
- Command
- Strategy
- Template-Method
- Visitor
- Interpreter
- Iterator
- Mediator
- Observer
- Builder
- Facade
That reduces the above table to:
GOF Pattern Name | UVM name |
---|---|
Singleton | UVM Pool, UVM Global report server, etc. |
Composite | UVM Component Hierarchy, UVM Sequence Hierarchy |
Adapter | UVM Reg Adapter |
Bridge | UVM sequencer, UVM driver |
Trusting that, if we were to write a pure Python testbench we can see that we would still likely implement a few design patterns. Thinking about this, it makes sense that we'd still probably have classes dedicated to transforming high-level sequence items to pin wiggles, just like the sequencer and driver work together to do in the UVM. It also makes sense that we'd have a class hierarchy to organize and relate components (such as the sequencer and driver equivalents) and sequences (high-level stimulus generation). Thing like that.
The more striking thing is the amount of code we would not need.
Comments
I'd agree with most of your analysis here in terms of what patterns are needed to implement various pieces of the UVM library.
I do maybe disagree with the idea that your table matches patterns to 'their UVM implementation'. I'd rather say that the implementation of many of the pieces of the UVM library uses various patterns. That may be a subtle distinction, but things like the Global Report Server doesn't implement a singleton, but it does use the singleton pattern to create one instance of the report server.
https://bitbucket.org/GordonMcGregor/pluvm
It is indeed subtle, but though provoking. If someone were to spend more time on this they could look at each UVM thing and see if it exist solely to implement a design pattern in ancillary support of solving the the meat of The Verification Problem (e.g., uvm_factory), if it's providing core functionality and would use that design pattern no matter which language you use (e.g., global report server), or if it's core functionality that's making use of a design pattern and probably could be simplified or done a different way (e.g., uvm_sequence/strategy pattern could just be a function in a dynamic language).
I'll definitely take a look at your code, thanks for sharing that!
I remember when people wanted to move to systemc, but it did not have the lab infrastructure that Python does.
Just a thought.
This comes a bit late but I'm curious about your last comment. I've started using cocotb quite extensively for designing IP and I do my own block level verification with python models. You seem to imply that those python models could be called from a more traditional system verilog/uvm testbench environment without too much pain . Do you have any references on how this can be done?
Thanks!