The APB agent needs a timeout knob, so you add uvm_config_db#(int)::get(this, "", "timeout", timeout) to apb_agent and, to keep things together, set it next to the interface in the top: set(null, "uvm_test_top.env.apb.*", "timeout", 100). The driver and the monitor both pick it up. The agent does not. Same path, same name, same type. The pattern itself is the problem.
A scope pattern is an address on an envelope. uvm_test_top.env.apb.* means "everyone whose address starts with uvm_test_top.env.apb.", dot included. The driver (...apb.drv) and the monitor (...apb.mon) live there. The agent's own address is uvm_test_top.env.apb, which has no trailing dot, so it is not a resident of its own building. A wildcard pattern must match the getter's entire full name, character for character.
A pattern decides reach: which components can see a setting. It does not decide precedence: when two settings both reach a component, a narrow exact path does not beat a broad wildcard. That is decided by the setter's depth and the order of the sets, taught in Resolution order.
By default a scope is a glob: * matches any run of characters, including dots, and ? matches exactly one character. A scope written between slashes, such as /^uvm_test_top\.env\.(in|out)_agt\..*$/, is a regular expression; regex scopes need the UVM DPI library, so globs are the portable choice.
Scope (set from null) | Reaches | Watch out for |
|---|---|---|
| Only the APB driver. | Breaks if the driver is renamed. |
| Everything below the APB agent. | Not the agent itself (the opening bug). |
| The agent and everything below it. | Also |
| Everything below the env. | Not the env itself. |
| Every component, and every sequence that reads config. | Costs every lookup of that field name; see performance. |
| Every component named | Silently grows as the bench grows. |
The top's three agent-interface sets each use agent.*, which is exactly right: the agents themselves do not need the interface, their drivers and monitors do. The env sets is_active with the exact relative path out_agt, because that setting is for one agent and nobody else:
// snippet: the bench's two styles of scope
// top module: absolute, wildcard below one agent
uvm_config_db#(virtual apb_if)::set(null, "uvm_test_top.env.apb.*", "vif", apb);
// dma_env::build_phase: relative to the env, exact
uvm_config_db#(uvm_active_passive_enum)::set(this, "out_agt", "is_active", UVM_PASSIVE);Absolute paths from the top module all start with uvm_test_top, the name run_test always gives the test instance. That is safe to rely on. What is not safe is everything after it: env, apb, in_agt are names chosen inside the env, so a top-module path that spells them out depends on code two levels away. Keep absolute paths short (down to the env), and let the env, which owns its children's names, set the rest relative to itself.
The same scope string can be written two ways. From the test, set(this, "env.apb.*", ...) and set(null, "uvm_test_top.env.apb.*", ...) produce the identical scope uvm_test_top.env.apb.*, so they reach exactly the same components. They are not equivalent, though: the context is also the setter, and the setter's depth decides precedence during build. The first is a depth-1 set (precedence 999), the second a depth-0 set (1000). Use this from inside components, so precedence reflects who really made the decision, and null only from the top module, which has no component context. A test that sets from null to "win harder" also outranks every other test-level setting, which is rarely what the next engineer expects.
To reach both the agent and its children, either make two sets, or use the apb* form and accept its reach. Two sets are clearer and never surprise you when someone adds an apb2 agent:
// snippet: reach the agent and everything below it
uvm_config_db#(int)::set(null, "uvm_test_top.env.apb", "timeout", 100);
uvm_config_db#(int)::set(null, "uvm_test_top.env.apb.*", "timeout", 100);Field names can also be globs, but keep them exact: a wildcard field name makes a set match labels nobody intended.
The bench's narrow scopes are visible in its topology and its passing log. Each agent's driver and monitor found an interface, out_agt built only a monitor, and the run checked every word:
+UVM_TESTNAME=dma_smoke_test
UVM_INFO dma_tb.sv(411) @ 655000: uvm_test_top.env.sb [SB] 8 words matched, 0 mismatched
UVM_FATAL : 0To see reach directly, print_config(1) on any component lists every setting visible to it and its children, with the scope each one came from. Calling it on the APB agent before and after the two-set fix shows timeout appear on the agent's own line.
A block bench had one APB agent called apb, and the test set uvm_test_top.env.apb* to configure it. A year later the block went into a subsystem env that also had apb_bridge, an agent for a different bus with a field of the same name, timeout. The bridge picked up the block's value, meant for a different protocol, and started timing out real transfers. Nothing in the log mentioned config; the symptom was a protocol error two levels away.
The fix was to stop using prefix globs on component names. Exact paths, or name.* plus an exact set for the component itself, have a reach you can read. When a setting must reach "all agents of one kind", put it in that agent's config object instead of matching names, so the type, not the spelling, decides who receives it.
uvm_config_db#(int)::set(this, "env.*", "verbose", 1). Which of these read it with get(this, "", ...): the env, the APB driver, the test?Only the APB driver (and every other component below the env). The scope is uvm_test_top.env.*; the env's full name uvm_test_top.env has no trailing dot, and the test's name is shorter still.
build_phase: first set(this, "env.apb.drv", "gap", 1), then set(this, "env.*", "gap", 5). What does the APB driver read?5. Both sets reach the driver and both come from the same setter, so they have the same precedence, and between equal precedence the last set wins. The exact path does not make the first set stronger; path form decides reach only.
*.drv a risky scope in a bench that will be reused in a subsystem?It reaches every component named drv anywhere in the hierarchy, including drivers in agents that do not exist yet. When the block is reused, other teams' drivers start receiving the setting if they read the same field name and type.
Next: when two settings both reach a component, which one does it read? Resolution order is the one place the course answers that.