How to keeping a neat repository for flexible testbench structure.
When I want to wear a certain clothing item, I take out it of the closet. When I go shopping, I add those clothes it to my closet and there are now new items for me to pick out in the future. A database works much the same way, a collection of information that is stored and accessed on demand.
Take the UVM configuration database for example. It basically acts as a repository so that when the time comes, certain portions of the UVM testbench can be obtained from the database and used to build the structure.
When items are placed in the database with a set() method (uvm_config_db::set()), components in lower levels will call the get() method in order to obtain the necessary parts to build the verification framework.
Sharing an interface
If I were to ‘set’ an interface from my top level into the database while simultaneously giving it an identifying name, officially referred to as the ‘field name’, I could later use the field name to retrieve that interface in my driver to connect to the DUT by calling the get() method (uvm_config_db::get()).
Fig. 1: Setting the interface in the configuration database using an identifier ‘my_identifier’.
Fig. 2: In order to connect a monitor or driver to the dut, the get() function will need to be called to access the interface in the respective build phase.
Setting up configurations
If I wanted to change or modify my testbench structure, I could create a ‘configuration’. In my configuration, I could specify some rules as to what components I want my testbench to have. If I am designing a processor where I’ve already loaded up the memory with instructions, there’s no need to generate stimulus, therefore I could eliminate the driver and sequencer.
This is what UVM refers to as passive and active modes. Passive mode is where only a monitor exists to observe data and active mode is where a driver and sequencer are needed to generate stimulus. Placing certain variables in the configuration database can help to determine whether the testbench is setup as passive or active.
In order to declare the testbench as passive or active, a configuration object is created. The built in uvm_active_passive_enum data type is used to indicate whether the testbench is UVM_ACTIVE or UVM_PASSIVE.
Fig. 3: An example configuration.
The environment would then go ahead and set() this configuration class, similar to how the interface was set, so that the agent class could get() it. Once obtained, the variable would be evaluated to determine if drivers and sequencers need to be built and connected.
Fig. 4: An agent class calling the get() method to obtain variables for modifying the testbench structure.
This provides flexibility to the verification testbench such that parts of the testbench can be interchanged very easily. Another great benefit of using the configuration database is precisely in the name. It allows the testbench to be heavily configurable. By creating various configurations with various desired components, things become highly customizable.
UVM Resource Database
Fig. 5: A graphical representation of database relationships taken from the UVM user guide.
Until now, I’ve spoken about only the configuration database, but there is also the resource database. The resource database and configuration database both store their items in the same place. That means that a resource database get() could access items set() by the configuration database and vice versa. The big question then becomes, what is the difference? And why should I choose one over the other?
The answer is scope. If I wanted to restrict access to the database to only certain components within my testbench, the configuration database would be the way to go. By restricting access and providing strict hierarchical limitations for access to certain database items through the ‘inst_name’ field, I’m making sure that any items I store in the database are only accessed by the components that I intend. This makes my verification methodology and my testbench very structured and organized.
In fig. 1, I showed the setting of the interface into the database, but I allowed everything under the sun to access it by providing the inst_name with uvm_test_top.*. If I were to provide a more strict path, for instance uvm_test_top.env.agent, I could limit any access to the interface to an agent.
If I wanted a free-for-all where components could grab items out of the database for convenient access, I could also do that, but I might run into issues where my sequencer accesses the interface for no reason and it just creates clutter. The freedom from choice provided by the configuration database allows me to make fewer mistakes and keeps my testbench neat.
Fig. 6: Riviera-PRO UVM Configuration Window.
When placing a large number of items into the database, it’s easy to forget exactly what is inside. Riviera-PRO has convenient windows designed specifically to make verification easy, including the configuration window. The Riviera-PRO UVM configuration window can help organize the database and provide information on the resources inside. From the name, scope, and value of a particular resource to statistics and information on what components have accessed it at different times, plenty of information is available to understand exactly what is going on.
Leave a Reply