UVM Testbench Top called uvm_config_db::set() for reg_if0, inside tb_top. This article traces that single call all the way down to reg_driver and reg_monitor's own get() calls, explains exactly why they find what they're looking for, and runs the whole thing for the first time.

8 min read | Intermediate Level

What You'll Learn

  • How a set() call's path string has to relate to a get() call's location to actually match
  • Why a single set() in tb_top reaches two different components nested several levels deep
  • How a component's full name appears in every log line
  • Where to look first when a get() call doesn't find what it's looking for

The Two Calls This Article Connects

Two calls have existed in this chapter for a while, each written without knowing exactly how the other one would resolve:


// In tb_top
uvm_config_db #(virtual reg_if)::set(null, "uvm_test_top.*", "vif", reg_if0);

// In reg_driver's build_phase, from its own article
if (!uvm_config_db #(virtual reg_if)::get(this, "", "vif", vif)) begin
  `uvm_fatal(get_type_name(), "Didn't get handle to virtual interface reg_if")
end

reg_monitor has the same get() call, with uvm_error instead of uvm_fatal. Both of these are waiting on that one set() call.

How the Two Sides Actually Match

Both set() and get() reduce down to one string each, called a scope, and a connection only happens when those two strings match. Here they are side by side:


// set()'s scope, built directly from its own arguments
"uvm_test_top.*"

// get()'s scope, built from reg_driver's own position in the hierarchy
"uvm_test_top.m_top_env.m_agent.m_driver"

These look like different strings, but "uvm_test_top.*" isn't meant to be compared letter for letter - the * is a wildcard, meaning "anything can go here." Stripped of the wildcard, the pattern is really just a requirement: start with "uvm_test_top.", then allow anything after it. reg_driver's scope starts with exactly that, so it satisfies the pattern, and the two sides match.

That just leaves one question: where does reg_driver's scope string, "uvm_test_top.m_top_env.m_agent.m_driver", actually come from? It's nothing more than reg_driver's own full name in the component hierarchy - get(this, "", "vif", vif) passes this and an empty string, which together mean "use exactly my own position, nothing more." That position is built up piece by piece, one level per component:

  • run_test("reg_test") instantiates reg_test, and UVM always names the top-level test instance uvm_test_top, regardless of the class's actual name
  • reg_test owns m_top_env, a reg_env
  • reg_env owns m_agent, a reg_agent
  • reg_agent owns m_driver, the actual reg_driver instance asking for vif

Chaining those four names together, separated by dots, is exactly how "uvm_test_top.m_top_env.m_agent.m_driver" is built - it's simply reg_driver's address in the hierarchy, spelled out in full. Once both scopes are written down side by side, the match is just the wildcard rule applied to that address: it starts with "uvm_test_top.", so set()'s pattern covers it, vif gets filled in, and the get() call succeeds.

Quick Check

Q: reg_monitor's full name is "uvm_test_top.m_top_env.m_agent.m_monitor" - a completely different string from reg_driver's. Does its own get() call need a separate set() just for it?

Show Answer

No. "uvm_test_top.*" matches both full names, since both start with "uvm_test_top." followed by something - the wildcard doesn't care what comes after the dot. One set() call, written once in tb_top, is visible to every component anywhere underneath uvm_test_top, which is exactly why the pattern includes a trailing wildcard rather than spelling out a specific component's path.

This is only enough of uvm_config_db to explain why this specific vif connection works. Wildcard placement, matching performance, precedence between multiple set() calls, and configuration objects bundling more than one value together are all covered in UVM Config Database.

Running the Test

With every get() call now able to find what it's looking for, calling run_test("reg_test") actually runs the entire chain this chapter built, end to end.

Every full name printed in a UVM log line - uvm_test_top.m_top_env.m_agent.m_driver among them - is the exact same kind of string this article just traced by hand. None of it is special simulator output; it's simply get_full_name(), the same method uvm_component's own article introduced, printed automatically as part of every log line's component tag.

Common Mistakes

Mistake #1: Leaving off the trailing wildcard in set()

Wrong: uvm_config_db #(virtual reg_if)::set(null, "uvm_test_top", "vif", reg_if0);
Right: uvm_config_db #(virtual reg_if)::set(null, "uvm_test_top.*", "vif", reg_if0);

Why? Without the wildcard, the stored scope is the literal string "uvm_test_top", which only matches a get() call from a component whose full name is exactly that - the test itself, and nothing nested inside it. reg_driver and reg_monitor are both several levels below uvm_test_top, so neither one's get() call would find anything without the wildcard, and both would fail with no virtual interface set at all.

Mistake #2: Assuming a failed get() always produces an obvious error

reg_driver's uvm_fatal stops the simulation immediately if its get() fails, which is hard to miss. But a get() call without that kind of guard around it just leaves its output variable unchanged and silently returns 0 - if nothing checks the return value, a scope mismatch can pass by completely unnoticed until something downstream behaves strangely for no apparent reason.

Practice Exercise

Challenge: Trace a Narrower Scope

Suppose tb_top used uvm_config_db #(virtual reg_if)::set(null, "uvm_test_top.m_top_env.*", "vif", reg_if0); instead. Would reg_driver's get() call still succeed?

Hint

Compare the new scope string against reg_driver's actual full name, traced earlier in this article, the same way the original wildcard was checked against it.

Solution

Yes - reg_driver's full name, "uvm_test_top.m_top_env.m_agent.m_driver", still starts with "uvm_test_top.m_top_env." followed by something, which is exactly what this narrower pattern requires. It would still match reg_monitor too, since both sit underneath m_top_env. The only thing this narrower scope would exclude is a component built entirely outside m_top_env, if this tutorial ever had one.

Summary

In this article, you learned:

  • A set() call's path and a get() call's full hierarchical name have to match as a pattern, not necessarily as identical strings
  • A trailing wildcard is what lets one set() call in tb_top reach every component nested underneath it
  • The exact full names printed in simulation logs are the same kind of path this article traced by hand from each component's known position in the hierarchy
  • A get() call with no failure check can fail silently, which is why reg_driver's and reg_monitor's own articles wrapped theirs in an explicit check from the start

Going Deeper

Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5