UVM Configuration DB Guidelines
Introduction
My previous blog posts were on static and parameterized classes to get you ready for the big game – the UVM Configuration Database or uvm_config_db. When used properly, this is a great way for one component to share a value with another. If the test or environment knows the path to the agent, the DB is efficient. Used improperly and it will bring your simulation to its knees.
Too Many Choices
The DB is that it is based on an associative array with a string index. So each entry is a name-value pair. If you store 100,000 values, the DB has to search these to find the particular value. If the array index values are organized as a tree, searching may require up to 20 string comparisons. Here is the DB with 100,000 entries.
Since this is a parameterized class, each specialization with a different type divides the size of the database. Perhaps half your configuration values are 32-bit integers, and the other half 64-bit values. Each DB access is now looking through half as many values.
The Wild Problem
The DB further organizes the name-value pairs by adding a scope to each name. The “speed” value can take a very different meaning for a memory compared to a processor.
How does a component share values across the entire testbench? I saw a memory component that wanted to share the memory speed to EVERY component, regardless of location. So it made the following call.
uvm_config_db#(int)::set(null, “*”, “mem_speed”, mem_speed);
The problem is that when you call get() , and the DB contains entries with wildcard scopes, the DB has to perform regular expression matching, which is much less efficient than a straight string match. Worse yet, the DB can’t do a tree-search, and instead has to compare every entry. If your DB only contains a few hundred entries, no problem, but if there are hundreds of thousands, all with wildcards, the runs very slowly. How bad? These wildcard scopes for the memory speed caused the UVM build_phase() to take 24 hours, even though the rest of the simulation took less than an hour. If your testbench is getting large, keep an eye out for this problem!
Act Locally
As mentioned in the webinar, one solution is to group configuration variables together into a “config object”. For example, an agent config has its local parameters such as the active/passive enum, various address and data values, and the virtual interface. If each config object holds just 10 values, the DB size drops by 10x. An agent’s build_phase has a single DB call to get the handle to its config object. When the agent wants an individual value, it just uses the handle to get the value in its local object. Caching values in a config object is much faster than another DB access.
Think Globally
Another solution for the wildcard problem is a “global scope”. Remember, the scopes in the DB do NOT have to match your testbench hierarchy. The memory component could write its value in a unique name space at the top of the DB, such as “mem” shown here.
uvm_config_db#(int)::set(null, “mem”, “speed”, memory_speed);
What if you have multiple memory components? In this global scope of “mem”, you could store a separate config object handle for each instance, assuming “speed” is a property in the mem_cfg class.
foreach (mem_cfg[i]) uvm_config_db#(mem_cfg)::set(null, “mem”, $sformatf(“mem[%0d]”, i), mem_cfgs[i]);
More tips
Don’t call super.build_phase() for components directly derived from a UVM component class such as uvm_component, uvm_test, uvm_env, uvm_agent, etc. This avoids the expensive apply_config_settings().
A wildcard at the end of a scope string, such as “agt*” has fewer matches and better performance than wildcards at the front, such as “*”.
Conclusion
A flat uvm_config_db with wildcard scopes and many entries can be a performance hog. Divide the DB into smaller domains by grouping values into config objects. You can use wildcards in the scope strings, but limit them to the end of the string to help performance.
Enjoy your verification journey!
Chris Spear
Keep learning at mentor.com/training
Questions or ideas? verificationacademy.com/ask-chris-spear
View my recent webinar on UVM Coding Guidelines and the Questions and Answers
Comments
Leave a Reply
You must be logged in to post a comment.
Hi, Chris.
I enjoy reading the articles inspired by your verification journey.
Can you comment more on why you recommend not calling super.build_phase()?
It really does not seem to be an obvious thing to do.
Thanks,
Aurelian
Hi Aurelian,
Good question! There are a couple of levels to the answer.
First, as you may have noticed, I am not a big fan of the uvm_config_db. For small or medium size projects, it’s fine, but when your design and testbench are big, the DB can kill performance. So, in my opinion, the less you touch it, the better off you are.
What does the DB have to do with components? If you call super.build_phase(), the uvm_component class calls apply_config_settings() which checks if you used the field macros to define any configuration variables, and automatically fetches their values. This does hide the ugly uvm-config_db::get() calls. However, since I don’t recommend the field macros, there should not be anything to fetch. If your component has configuration variables, put them in a configuration object and either pass them through the DB, or the set_config() methods.
Let’s take a step back. If your component extends a UVM component, don’t call any super.methods. If your component extends your own base classes, such as my_test_base, your methods should call super.method, especially super.build_phase(), as your base class probably does setup and configuration.
Thanks for the question. Enjoy your SystemVerilog journey.
Chris Spear
P.S. Cool name!