A loopback test wants the DMA's output agent active for once. In the test's build_phase, the author sets is_active to UVM_ACTIVE on env.out_agt through uvm_config_db, then worries: the env sets UVM_PASSIVE for the same agent, later, in its own build_phase. Surely the later set wins? It does not. The test wins, every time, wherever the line goes. This lesson is the only place in the course where the precedence rules are taught; other lessons link here.
Think of an office where instructions are pinned on a board during the morning planning session. Each note carries the writer's rank. When two notes conflict, the higher-ranked writer wins, no matter who pinned last: the director's note beats the team lead's, even if the team lead pinned theirs an hour later. Between two notes of the same rank, the newer one wins. After planning ends, rank stops mattering: everything pinned during the working day has the same rank, so the newest note always wins.
In UVM the rank is precedence, and it is computed from the setter's position in the hierarchy. During build_phase, a set gets precedence 1000 - depth, where depth is the setter context's distance from the top: uvm_root (or a null context) has depth 0, uvm_test_top depth 1, the env depth 2, an agent depth 3. Outside build_phase every set gets the default precedence 1000. A get collects every setting that matches its key, picks the highest precedence, and breaks ties by taking the most recent set.
Notice what is missing: how specific the path is. A wildcard env.* from the test beats an exact out_agt from the env. Path form only decides whether a set matches at all (reach).
// snippet: in a loopback test's build_phase (position relative to super does not matter)
uvm_config_db#(uvm_active_passive_enum)::set(this, "env.out_agt", "is_active", UVM_ACTIVE);
// test depth 1 -> precedence 999
// later, dma_env::build_phase: set(this, "out_agt", "is_active", UVM_PASSIVE)
// env depth 2 -> precedence 998. The agent reads UVM_ACTIVE.The env's set happens after the test's (the env builds after the test), yet the test wins: 999 beats 998. This is the design intent. The env states a default for its own children; any test above it can override that default without editing the env.
The top module's interface sets run in an initial block before run_test, outside any phase, so they get 1000. A test that tries to swap an interface during build gets 999 and loses. To let tests choose interfaces, add one hop: the top publishes each interface under a source name, and the env passes the chosen one on under the name the drivers read.
// snippet: letting a test choose the interface
// top, before run_test (precedence 1000, but nobody else sets these names)
uvm_config_db#(virtual axis_if)::set(null, "uvm_test_top.env", "s_vif_a", s_ax_a);
uvm_config_db#(virtual axis_if)::set(null, "uvm_test_top.env", "s_vif_b", s_ax_b);
// test build_phase: pick one by name (a plain string knob, no clash with the top)
uvm_config_db#(string)::set(this, "env", "in_vif_name", "s_vif_b");
// env build_phase: read the choice, fetch that interface, re-set it for the agent
if (!uvm_config_db#(string)::get(this, "", "in_vif_name", name)) name = "s_vif_a";
if (!uvm_config_db#(virtual axis_if)::get(this, "", name, vif)) `uvm_fatal("NOVIF", name)
uvm_config_db#(virtual axis_if)::set(this, "in_agt.*", "vif", vif);Two sets from the same component have the same depth, so the later one wins, whatever their paths. And every set made outside build_phase gets 1000, so a run-time set from anywhere beats a build-time set and then follows last-set-wins. That is how a test changes a knob in the middle of run_phase for a sequence that reads config later (Configuring sequences).
Settings that match the getter | Precedences | Winner |
|---|---|---|
env sets | 998 vs 999 | Test: ACTIVE |
test sets | 999 vs 998 | Test: 5 (wildcard beats exact) |
test sets | 999 vs 999 | Later set: 5 |
top sets vif before run_test; test sets vif in build | 1000 vs 999 | Top |
test sets knob=1 in build; agent sets knob=4 in run_phase | 999 vs 1000 | Agent: 4 |
test sets knob=1 in run_phase, later knob=2 in run_phase | 1000 vs 1000 | Later set: 2 |
In the shipped bench no two settings collide, and the env's PASSIVE set is the only is_active setting for out_agt. The evidence is the topology at the start of every run, where out_agt has a monitor and no driver, and the passing log:
+UVM_TESTNAME=dma_smoke_test
UVM_INFO dma_tb.sv(411) @ 655000: uvm_test_top.env.sb [SB] 8 words matched, 0 mismatched
UVM_ERROR : 0When collisions do happen, three tools show the decision. +UVM_CONFIG_DB_TRACE prints a CFGDB/SET line for every set and a CFGDB/GET line for every get, naming the scope, the field, the type, the accessor and the value. print_config(1, 1) on a component lists every setting that reaches it and its children, and with the audit argument, who read them. uvm_config_db#(T)::dump() prints the whole resource pool, whatever T is, with each resource's precedence.
// snippet: precedence debugging from the test
function void end_of_elaboration_phase(uvm_phase phase);
env.out_agt.print_config(1, 1); // settings visible to out_agt and below, with readers
uvm_config_db#(int)::dump(); // the entire resource pool, not just int
endfunctionA team's agents read a coverage_enable knob at the start of run_phase. Tests set it to 0 in build_phase for a fast regression, and it was ignored on half the agents. Someone "fixed" it by moving the test's set later in the test's build_phase. It changed nothing, because order was never the issue.
The rule explains both halves. Some agents made sure the knob had a value by setting their own default in build_phase (set(this, "", "coverage_enable", 1)): the test's set (999) beat that self-set (997), so those agents obeyed the test. In the other agents, the default was set by the parent env with a broad pattern in its connect_phase, which is outside build, so it carried precedence 1000 and beat the test. The fix was the pattern this course recommends: a component never sets config to create a default. It initializes the variable, calls a non-fatal get, and keeps its default on a miss. Only components above set values for those below, and they do it during build.
uvm_config_db#(int)::set(this, "*", "rate", 500) in build. The test sets set(this, "env.*", "rate", 2000) in build. What does the agent's monitor read?2000. Both sets match the monitor's full name. The test's precedence is 999 and the agent's is 997, so the test wins, even though the agent's set is closer to the monitor and happens later.
gap for its driver in build_phase (998). In run_phase, a virtual sequence calls uvm_config_db#(int)::set(null, "uvm_test_top.env.in_agt.drv", "gap", 0). The driver re-reads gap before each packet. What does it see after the virtual sequence's set?0. The run-time set is made outside build, so it gets precedence 1000 and beats the env's 998. If anything sets gap again later at run time, the newest of those sets wins.
The test sets set(this, "env.*", "gap", 5) and the env sets set(this, "in_agt.drv", "gap", 2), both in build. The driver reads 5: the test's wildcard set (999) beats the env's exact set (998). Specificity is not part of the lookup.
Next: loose knobs multiply collisions. Config objects bundles them into one typed handle, as dma_rand_test does with its job description.