UVM
Scope Rules
How a config_db get resolves against multiple sets — glob scope matching, walking the hierarchy, and the precedence rule (higher-in-hierarchy setter wins, then last set) that lets a test override an env's defaults.
Configuration Database · Module 7 · Page 7.3
The Engineering Problem
The previous chapter (Module 7.2) assumed a set's scope simply "reaches" the getter, and left the precise rules for later. This chapter is that precision. In a real testbench, many set calls exist — an env sets defaults for its agents, a base test sets some values, a derived test overrides others — and a single get may match several of them. Two questions then decide everything: which sets match a given get (the matching rules — globs and hierarchy), and when several match, which one wins (the precedence rule).
The problem is that the precedence rule is not what most engineers assume. Coming from CSS or routing tables, people expect the most specific pattern to win — a precise path should beat a broad wildcard. UVM does not work that way. Its precedence is by the position of the setting component in the hierarchy: a set from a component higher in the tree (closer to the root — the test) beats a set from a component lower down (the env), regardless of how specific each pattern is, and only among sets of equal hierarchical precedence does the last set win. This single rule is the entire reason the config DB works as a top-down control mechanism — it's why a test can override an env's defaults — but it surprises anyone expecting specificity to decide. This chapter is the matching and precedence rules: how a get's path is matched against set globs, how the hierarchy is walked, and the precedence order that resolves conflicts.
How does a config_db
getresolve when multiplesetcalls match it — how do glob scopes match a getter's path, and what is the precedence rule (higher-in-hierarchy setter wins, then last set) that decides which set takes effect?
Motivation — why precedence is by hierarchy, not specificity
The precedence rule looks arbitrary until you see what it's for. The config DB exists so that a higher-level component can configure a lower-level one it doesn't directly control (Module 7.1). That goal dictates the precedence rule:
- Override must flow top-down, so the higher setter must win. The whole point is that a test can override what an env configures by default. The test is higher than the env. So a
setfrom the test must beat asetfrom the env — precedence has to favor the component higher in the hierarchy, or override would be impossible. - Specificity can't decide, because the env's settings are often more specific. An env typically sets precise, per-instance configuration (
agent0,agent1); a test often sets a broad default (*). If specificity won, the env's precise settings would beat the test's broad override — the exact opposite of what's needed. So precedence must ignore specificity. - Timing can't be the primary rule, because top-down build sets the test first.
build_phaseis top-down: the test'ssetruns before the env's. If last-wins were primary, the env (running later) would override the test — again backwards. So order can only be a tiebreaker among equal-hierarchy sets, never the primary rule. - Hence: hierarchy first, order second. The only rule consistent with top-down override is "the higher-in-hierarchy setter wins; among equals, the last set wins." Everything about config DB precedence follows from making top-down override work.
- Matching is separate from precedence. Before precedence even applies, a set must match the get — its scope glob must cover the getter's path, and field and type must agree. Precedence only adjudicates among the sets that matched.
The motivation, in one line: config DB precedence is by hierarchy (not specificity, not primarily order) because its job is to let higher components override lower ones — the rule is the mechanism of top-down control, not an arbitrary tie-break.
Mental Model
Hold scope rules as a chain of command delivering orders by area:
Sets are orders addressed to an area; a get obeys all orders addressed to its location but follows the one from the highest-ranking officer — not the most detailed order. Each
setis an order pinned to a notice board, addressed to an area (a glob like "everyone under env" or "the whole base"). A component reading configuration (get) looks at its own location and collects every order whose area covers it. If only one covers it, it obeys that. If several do, it does not pick the most detailed or most specific order — it obeys the order from the officer highest in the chain of command. The general (the test, at the top) outranks the captain (the env, lower down), so the general's order stands even if the captain's order was more specific to this exact spot. Only when two orders come from officers of the same rank does the most recent one win. This is why a general can override a captain's standing orders across the whole unit with one broad command — rank beats detail — and it's why a soldier who assumes "the most specific order wins" will follow the wrong one.
So resolving a get is two steps: collect every set whose area covers you (matching), then obey the highest-ranking among them (precedence by hierarchy), breaking ties by recency. The misconception to unlearn is "specific beats broad" — in UVM, high beats low, full stop.
Visual Explanation — resolving a get against multiple sets
A get resolves in two stages: first match (which sets' globs cover this getter's path), then rank (among matches, which setter is highest in the hierarchy). The diagram shows both stages for a getter that several sets match.
The two stages are independent and must not be conflated. Matching (step 2) is purely about coverage: does this set's scope glob, as a pattern, cover the getter's full hierarchical path? Every set whose glob matches — whether it's a precise path like env.agent0.drv or a broad wildcard like * — is collected into the candidate set. Specificity plays no role here; a glob either covers the path or it doesn't. Ranking (step 3) then decides among the matched candidates by precedence, and precedence is the position of the setting component in the hierarchy: the test sits above the env, so any set issued from the test outranks any set issued from the env — even though the env's set named the exact driver and the test's set was a blanket *. Between the two test sets (equal precedence, both from test-level), the last one registered wins. The outcome that surprises people is right there in the figure: the env's precise env.agent0.drv set loses to the test's broad * set, because high beats specific. Internalize the two-stage shape — match by coverage, rank by hierarchy — and config DB resolution stops being mysterious.
RTL / Simulation Perspective — globs and the precedence outcome in code
The matching and precedence rules are visible directly in code: the scope strings (with their wildcards) determine matching, and which component calls set determines precedence.
// ── ENV sets a precise, per-instance default (LOWER in the hierarchy) ──
class my_env extends uvm_env;
function void build_phase(uvm_phase phase);
// scope glob = "uvm_test_top.env.agent0.drv" — precise, matches exactly one driver
uvm_config_db#(my_cfg)::set(this, "agent0.drv", "cfg", env_default_cfg);
...
endfunction
endclass
// ── TEST sets a broad override (HIGHER in the hierarchy) ──
class my_test extends uvm_base_test;
function void build_phase(uvm_phase phase);
// scope glob = "uvm_test_top.*" — broad wildcard, matches every component
uvm_config_db#(my_cfg)::set(this, "*", "cfg", test_cfg); // wins everywhere — test is higher
super.build_phase(phase); // builds env → env's set runs AFTER, but is LOWER precedence
endfunction
endclass
// ── DRIVER gets — resolves to test_cfg, NOT env_default_cfg ──
// lookup path "uvm_test_top.env.agent0.drv" is matched by BOTH sets above:
// env's "uvm_test_top.env.agent0.drv" (precise) — matches, but LOWER precedence
// test's "uvm_test_top.*" (broad) — matches, and HIGHER precedence → WINS
uvm_config_db#(my_cfg)::get(this, "", "cfg", cfg); // cfg == test_cfgThe code makes both rules concrete. Globs: the env's set(this, "agent0.drv", ...) builds the scope uvm_test_top.env.agent0.drv — a literal that matches exactly one driver; the test's set(this, "*", ...) builds uvm_test_top.* — a wildcard that matches every component below the test. (UVM scope strings use glob-style wildcards: * matches any sequence of characters, ? matches a single character.) Both globs match the driver's lookup path uvm_test_top.env.agent0.drv, so both are candidates. Precedence: the winner is the test's set — not because it's broader, but because the test is higher in the hierarchy than the env. Notice the timing too: because build is top-down, the test's set actually runs before the env's set (which runs inside super.build_phase()), yet the test still wins — proving precedence is by hierarchy, not by who set last. The env's precise, later set loses to the test's broad, earlier set. This is the top-down override working exactly as designed: the test reshapes configuration the env established defaults for, without the env knowing.
Verification Perspective — the precedence rule, precisely
The precedence rule has two levels — hierarchy first, order second — and getting the order of these right is what separates correct mental models from the specificity misconception.
The rule is a strict two-level comparison. Level one — hierarchy: among all the sets that matched, compare the positions of the components that issued them. If one setting component is higher in the tree (closer to the root) than the rest, its set wins outright, and nothing else matters — not how specific its pattern was, not when it ran. This is the level that delivers top-down override. Level two — order (tiebreaker only): the order comparison applies only when the leading candidates were set from the same hierarchy level (for example, a base test and a derived test both setting from test scope, or two sets from the same component). In that case, the last set registered wins. The thing the rule never does is compare specificity: a precise path has no inherent advantage over a wildcard. The dashed "misconception" branch — "the most specific pattern wins" — is the model to actively unlearn, because it's intuitive (CSS, route tables, firewall rules all work that way) and it's wrong for UVM. The discipline is to reason about precedence by asking, in order: who is higher? then, only if tied, who set last? — and never who is more specific?
Runtime / Execution Flow — walking the hierarchy on a get
When a component calls get, the database resolves it by walking the matching sets and applying precedence — a process that happens at the moment of the get, against whatever sets exist at that time.
The resolution is a scan-and-select that runs at the moment of the get. First the getter forms its lookup path — its own full hierarchical name combined with the field it's asking for. The database then scans all registered sets and collects the candidates: every set whose scope glob covers that path and whose field name and type T agree (matching, from Module 7.2, gates entry — a set that doesn't match on field or type isn't even a candidate). Among the collected candidates it selects the winner by precedence — highest-in-hierarchy setter, then last-set tiebreaker — and returns that value. The crucial runtime consequence is that resolution sees only the sets that exist when the get runs: a set registered after this get won't be considered. Top-down build is what makes this safe for the normal pattern — a parent or test set in build_phase is registered before the child's build_phase get runs, so it's visible. But it also means a set registered later (say in run_phase) can't retroactively affect a get that already resolved in build_phase. Precedence decides among visible sets; visibility is decided by timing. Both must line up for a set to win a get.
Waveform Perspective — the higher setter wins regardless of order
The defining behavior — the higher-in-hierarchy set wins even though it ran first — is a timing/precedence interplay, and a timeline makes it concrete.
Test set (higher, earlier) beats env set (lower, later) — precedence is by hierarchy
10 cyclesThe waveform isolates the counter-intuitive part: the winner ran first. The test_set pulse comes before the env_set pulse — because top-down build runs the test's build_phase before the env's. Both sets match the driver's lookup path (the test's * and the env's precise agent0.drv both cover it), so both are candidates when the driver's get resolves. Yet resolved_cfg settles on TEST, not ENV, even though the env's set was registered later. If precedence were last-wins, the env would have won — but it doesn't, because precedence is hierarchy-first, and the test is higher. This is the visual proof that order is not the deciding rule: a later set from a lower component loses to an earlier set from a higher one. Only when two sets come from the same level would the later pulse win. Carry this timing picture: in the normal top-down flow, the test sets first and still wins, which is precisely the behavior that makes "configure from the top, override defaults below" work.
DebugLab — the specific set that lost to a broad one
An env's precise per-agent config that a test's blanket wildcard silently overrode
An env configured each agent precisely: set(this, "agent0", "cfg", agent0_cfg) and set(this, "agent1", "cfg", agent1_cfg), giving each agent its own tuned configuration. A test, meanwhile, set a blanket default for convenience: set(this, "*", "cfg", default_cfg). The engineer expected the env's specific per-agent sets to win for agent0 and agent1 (they named the exact agents), with the test's * only filling in anything the env didn't configure. Instead, both agents came up with default_cfg — the env's carefully tuned per-agent configuration was completely ignored, with no error.
UVM precedence is by the setter's hierarchy position, not pattern specificity — so the test's broad * (higher) beat the env's precise agent0/agent1 (lower):
env (lower): set(this, "agent0", "cfg", agent0_cfg) // scope uvm_test_top.env.agent0 (precise)
set(this, "agent1", "cfg", agent1_cfg) // scope uvm_test_top.env.agent1 (precise)
test (higher): set(this, "*", "cfg", default_cfg) // scope uvm_test_top.* (broad)
agent0 get → both "uvm_test_top.env.agent0" (env) and "uvm_test_top.*" (test) match
precedence → TEST is higher in the hierarchy than ENV → test's "*" WINS (specificity ignored)
result → agent0.cfg = default_cfg, agent1.cfg = default_cfg (env's tuned sets lost)
assumption that caused it: "the more specific scope wins" — UVM does NOT work this way
fix options:
(a) don't use a blanket "*" in the test for a field the env configures per-instance; or
(b) if the test must override, make it intentional and per-instance:
set(this, "env.agent0", "cfg", t0); set(this, "env.agent1", "cfg", t1); // explicit
(c) verify with the resource/config dump which set won (see Diagnosis)The engineer applied a specificity mental model — "a pattern naming the exact agent beats a blanket wildcard" — which is how CSS, routing tables, and firewall rules behave, but not how UVM config DB precedence works. In UVM, the only thing that decides among matching sets is the hierarchy position of the setting component: the test is above the env, so the test's set wins, and the fact that the env's pattern was far more specific is completely irrelevant. The blanket set(this, "*", ...) from the test therefore overrode every per-agent set the env made. This is the same family as a too-broad factory override (Module 6.7), but driven by the precedence rule rather than scope alone: even a correctly scoped specific set loses if a higher component also matches. The fixes are to not blanket-set a field that lower components configure per-instance, or — if the override is intentional — to make it explicit and per-instance from the test, and always to verify which set actually won.
The tell is "my specific set was ignored in favor of a broader one from higher up." Diagnose precedence problems:
- Identify all sets that match the get. List every
setfor that field whose scope glob covers the getter's path — not just the one you expected to win. The surprise winner is usually a broad set from a higher component. - Compare setter hierarchy, not pattern specificity. Rank the matching sets by where the setting component sits (test above env above agent). The highest one wins — if a blanket set from the test exists, suspect it immediately.
- Remember order is only a tiebreaker. If the top candidates are at the same level, the last set wins; otherwise order is irrelevant. Don't be misled by which set ran first.
- Dump the configuration/resource database. Use the config/resource print (e.g., the resource dump or
+UVM_CONFIG_DB_TRACE) to see all matching sets and which one was selected — this shows the actual precedence outcome rather than your assumed one.
Reason about precedence by hierarchy, and make overrides intentional:
- Never assume specificity wins. Internalize "higher-in-hierarchy setter wins, then last set" — specificity is never a factor. When a higher component sets a field, expect it to win over lower components regardless of pattern.
- Don't blanket-set fields configured per-instance below. A test's
set(this, "*", field, ...)overrides every lower set for that field. Use a blanket set only for fields you genuinely want uniform; otherwise set per-instance. - Make intentional overrides explicit and scoped. If a test must override a specific lower set, set it at the specific path from the test, so the intent is visible and the blast radius is bounded.
- Verify which set won. Use the config/resource trace to confirm the intended set actually won for each getter — precedence surprises are silent, so checking is the only way to be sure.
The one-sentence lesson: UVM config DB precedence is decided by the setting component's hierarchy position — higher always beats lower, with order only as a same-level tiebreaker and specificity never a factor — so a test's blanket set(this, "*", ...) silently overrides an env's precise per-instance sets, and the fix is to stop assuming "specific wins," scope intentional overrides explicitly, and verify which set won.
Common Mistakes
- Assuming the most specific scope wins. UVM precedence is by setter hierarchy, never specificity; a precise env set loses to a broad test set. Reason by who is higher, not whose pattern is tighter.
- Blanket-setting a field configured per-instance below. A test's
set(this, "*", field, ...)overrides every lower set for that field. Blanket-set only fields meant to be uniform. - Thinking last-set always wins. Order is only a tiebreaker among equal-hierarchy sets; a later set from a lower component still loses to an earlier set from a higher one.
- Forgetting the field and type must match before precedence applies. Precedence only adjudicates among sets that already matched on glob, field, and type — a field typo means the set isn't even a candidate.
- Expecting a late set to affect an already-resolved get. Resolution sees only sets registered before the get runs; a
run_phaseset can't change abuild_phaseget that already resolved. - Using an over-broad wildcard unintentionally.
*matches everything below; if you meant one subtree, scope it (env.agent0.*) so it doesn't catch unrelated components.
Senior Design Review Notes
Interview Insights
The one set by the component highest in the hierarchy wins, and among sets from the same level, the last one registered wins — specificity is never a factor. Resolving a get is two stages. First, matching: the database collects every set whose scope glob covers the getter's full hierarchical path and whose field name and type agree. Specificity plays no role in matching — a glob either covers the path or it doesn't, whether it's a precise path or a broad wildcard. Second, precedence: among the matched sets, the winner is decided by the position of the setting component in the hierarchy. A set from a component closer to the root — the test — beats a set from a lower component — the env — regardless of how specific each pattern is. Only when the top candidates were set from the same hierarchy level does the tiebreaker apply, and then the last set registered wins. The rule the config DB never uses is specificity: a precise env set does not beat a broad test set. This is by design — the whole purpose of the config DB is top-down override, so the higher component must win, which means an env's precise per-instance set can be overridden by a test's blanket wildcard. The mental shortcut is: high beats low, then last beats first, and specific never beats broad.
Exercises
- Rank the winners. A get at
uvm_test_top.env.agent0.drvfor fieldcfgis matched by: env'sset(this,"agent0.drv",...), base test'sset(this,"*",...), derived test'sset(this,"*",...). List which match, then name the winner and the rule that decides. - Refute the misconception. A colleague says "the env's
agent0.drvset is more specific, so it beats the test's*." Explain why they're wrong and what actually wins. - Scope an intentional override. A test must override only
agent1's config while leaving the env'sagent0config intact. Write the test-sidesetthat does this without a blanket*. - Timing vs precedence. Explain why a test's set wins even though, under top-down build, it runs before the env's set — and give the one situation where running last would decide.
Summary
- A
getresolves in two stages: match (collect every set whose scope glob covers the getter's full path and whose field + type agree) then rank (select among the matches by precedence). Matching is by coverage, not specificity. - Precedence is by the setting component's hierarchy position: a set from a component higher in the tree (closer to the root — the test) beats one from lower down (the env), regardless of pattern specificity. This is why a test overrides an env's defaults.
- Order is only a tiebreaker: among sets of equal hierarchical precedence, the last set wins. Because top-down build runs the test's set first, a hierarchy-first rule is the only one that makes override work — and a later set from a lower component still loses.
- Specificity is never a factor — the dangerous misconception (from CSS/routing) is that a precise scope beats a broad one; in UVM a blanket
set(this, "*", ...)from a test silently overrides an env's precise per-instance sets. - The durable rule of thumb: resolve precedence by asking "who is higher?" then, only if tied, "who set last?" — never "who is more specific?"; scope test-level sets intentionally (a
*overrides everything below for that field), and verify which set won with the config/resource trace, because precedence surprises are silent.
Next — Hierarchical Configuration: with matching and precedence understood, the next chapter builds the patterns on top of them — configuring nested structures, passing configuration objects down through layers, and assembling a coherent configuration tree for a multi-agent environment.