The UVM Factory
In the previous post in the Python for Verification Series, we discussed how pyuvm implemented the configuration database as a singleton class named
ConfigDB(). In this post, we’ll examine the UVM factory.
The pyuvm implements the UVM factory as it is described in the specification, removing elements that complicated the factory because of SystemVerilog typing.
Instantiating objects using the factory
SystemVerilog UVM developers have a choice of whether they instantiate objects using the
new() function, or whether they use the long factory incantation that delivers an object that can be overridden. pyuvm provides the same options.
We have the following component that logs a tiny message:
class TinyComponent(uvm_component): async def run_phase(self): self.raise_objection() self.logger.info("I'm so tiny!") self.drop_objection()
Notice that we don’t have a
uvm_component_utils() macro that registers the class with the factory. In pyuvm, all classes that extend
uvm_void are all registered with the factory.
You can instantiate this component directly like this:
class TinyTest(uvm_test): def build_phase(self): self.tc = TinyComponent("tc", self)
Or, you can instantiate the component using the
create() class method:
class TinyFactoryTest(uvm_test): def build_phase(self): self.tc = TinyComponent.create("tc", self) -- 1000.00ns INFO testbench.py(15)[uvm_test_top.tc]: I'm so tiny!
Notice that you do not put parenthesis after the class name when you call a class method. This would create an instance of the object and then call the class method using the instance.
Thanks to the lack of typing in Python, the
create() call in Python is much easier to use than the baroque incantation used to call
create() in SystemVerilog.
Now we can override our component.
We override types in pyuvm by using the
uvm_factory() singleton object. The
uvm_factory() has four overriding methods. Note that what SystemVerilog calls a type, we Python programmers call a class. Still, the specification uses the word type in these methods:
set_type_override_by_type(<original class>, <overriding class>)
set_type_override_by_name(<"original class name">, <“overriding class name”>)
set_inst_override_by_type(<original class>, <overriding class>, <"UVM hierarchy path">)
set_inst_override_by_name(<"original class name">, <“overriding class name”>, <"UVM hierarchy path)
Here is an example of creating a component and using it in an override:
class MediumComponent(uvm_component): async def run_phase(self): self.raise_objection() self.logger.info("I'm medium size.") self.drop_objection()
Now we create a test that overrides
class MediumFactoryTest(TinyFactoryTest): def build_phase(self): uvm_factory().set_type_override_by_type( TinyComponent, MediumComponent) super().build_phase() -- 2000.00ns INFO testbench.py(44)[uvm_test_top.tc]: I'm medium size.
Notice that Python classes are objects just like everything else, so we can pass them to
set_type_override_by_type using the simple class names. There is no need for the
get_type() method that we see in SystemVerilog.
We can use an environment to demonstrate instance overrides:
class TwoCompEnv(uvm_env): def build_phase(self): self.tc1 = TinyComponent.create("tc1", self) self.tc2 = TinyComponent.create("tc2", self)
Now we have two components, but only want to override
tc1. Notice that in this example we use
set_inst_override_by_name so we pass strings containing the names of the classes:
class TwoCompTest(uvm_test): def build_phase(self): uvm_factory().set_inst_override_by_name( "TinyComponent", "MediumComponent", "uvm_test_top.env.tc1") self.env = TwoCompEnv("env", self) -- 4000.00ns INFO testbench.py(44)[uvm_test_top.env.tc1]: I'm medium size. 4000.00ns INFO testbench.py(15)[uvm_test_top.env.tc2]: I'm so tiny!
We’ve now overridden only one instance of the
Clearing overrides from the factory
Python and pyuvm allow you to run many UVM tests in a single Python file. When you do this, the overrides from one test will persist unless you explicitly clear them in the next test. You do this with the
uvm_factory().clear_overrides() method, as we do below:
@cocotb.test() async def two_comp_test(_): uvm_factory().clear_overrides() await uvm_root().run_test("TwoCompTest")
Printing the state of the factory
The UVM specification calls for a
uvm_factory().print() method that prints the state of the factory. The
print() method takes an argument that controls what gets printed. The argument, named
all_types, can be set to
2 with the following results:
0—Prints overrides only
1— (the default) Prints user-defined types and overrides. User-defined types are types whose names don’t start with the string
2—Prints all types registered with the factory and overrides.
Here is an example of printing only overrides:
@cocotb.test() async def two_comp_test(_): uvm_factory().clear_overrides() await uvm_root().run_test("TwoCompTest") # from above uvm_factory().print(0) -- # --- overrides --- # # Overrides: # TinyComponent : Type Override: None || Instance Overrides: uvm_test_top.env.tc1 => MediumComponent
print() method uses the Python
print() function to print out the factory status. But you can incorporate the factory status into logging by creating a string from the factory.
Creating a string from the factory
uvm_factory().debug_level data member can take the same three values as we saw above
2 with the same behavior. We create a string from the
uvm_factory() by passing it to the
str() class. For example, we might log the state of the factory in the report phase:
class MediumNameTest(TinyFactoryTest): def build_phase(self): uvm_factory().set_type_override_by_name( "TinyComponent", "MediumComponent") super().build_phase() def report_phase(self): uvm_factory().debug_level = 0 uvm_factory_str = str(uvm_factory()) self.logger.info(uvm_factory_str) -- 3000.00ns INFO testbench.py(69)[uvm_test_top]: --- overrides --- Overrides: TinyComponent : Type Override: MediumComponent || Instance Overrides:
cocotb formats the string to fit in the logging system.
This blog post introduced the UVM factory as implemented in pyuvm. We saw that the lack of typing make the pyuvm factory easier to use than the SystemVerilog one. pyuvm implements the complete UVM factory specification and adds the ability to create a string from the