Override one instance
Replace the driver inside one agent from the test, write a path pattern that matches that driver and nothing else, and confirm with the factory's own reports before the run starts.
- Lesson
- U11.3
- Time
- 25 min
- Runs on
- Verilator with UVM
- Check
- 5 questions
The chip has twelve lanes, lane0 to lane11, each with its own incrementer agent. An error test should corrupt lane 1 only, so it adds an instance override for the pattern "*lane1*". Three lanes start failing: the pattern also matches lane10 and lane11, and nothing warns about it.
| Driver path | Matches "lane1" | Matches "*.lane1.drv" |
|---|---|---|
uvm_test_top.env.lane0.drv | No | No |
uvm_test_top.env.lane1.drv | Yes | Yes |
uvm_test_top.env.lane10.drv | Yes | No |
uvm_test_top.env.lane11.drv | Yes | No |
A star matches any characters, including digits. Anchoring the name between dots matches only lane 1.
Change one agent without editing it
A reusable agent is shared by many tests and often by other teams, so a test should not edit it to inject errors. The factory lets the test swap a class from outside. U2.2 showed the two kinds of override: a type override replaces a class everywhere, an instance override replaces it at matching paths only.
This lesson uses the instance kind. The test asks for err_driver wherever incr_driver is created at one path. The agent's code still says incr_driver::type_id::create("drv", this). At that call, the factory looks at the requested type and the new component's full path, and builds err_driver instead when an override matches both.
For that to work, the agent must create its driver through the factory. A driver built with new never consults the factory, so no override can reach it. The replacement must also extend incr_driver, so the agent's handle of that type can hold it.
The path names the component you create
An instance override matches the full path of the component being created, not of its parent. The agent uvm_test_top.port_b creates its driver as "drv", so the driver's path is uvm_test_top.port_b.drv. That is the string the pattern must match.
| Pattern given to set_inst_override | Called with | Driver at uvm_test_top.port_b.drv |
|---|---|---|
"port_b.drv" | parent this (the test) | Replaced: the test's path is put in front |
"uvm_test_top.port_b.drv" | no parent | Replaced |
"uvm_test_top.port_b.*" | no parent | Replaced, with every other incr_driver under port_b |
"uvm_test_top.port_b" | no parent | Not replaced: that is the agent's path |
"port_b.drv" | no parent | Not replaced: no path starts with port_b |
Five ways to aim at one driver. Only the requested type, incr_driver, is affected in every row.
The fourth row is a common slip. The author thinks "override in port B" and names the agent. The agent is an incr_agent, not an incr_driver, so nothing is replaced and the call reports nothing. The last row is U2.2's trap again: without a parent, the pattern is read from the root.
Wildcards match more than you think
Patterns use glob wildcards: * matches any run of characters, including dots, and ? matches one character. A star is handy for "every driver under port B", but it does not stop at a name boundary. As the hook showed, "*lane1*" matches lane10 and lane11 as well.
Anchor names with the dots around them. "*.lane1.drv" matches only a drv whose parent is called exactly lane1. "*.lane1.*" matches every descendant of lane1, at any depth, but nothing under lane10. Better still, give the full relative path from the test, such as "env.lane1.drv" with this as the parent.
Instance overrides beat type overrides
create call. Arrows are the order of the checks.- port_b.drv
The path
uvm_test_top.port_b.drvmatches the instance override, so the factory buildserr_driverand looks no further.More context
The example prints
uvm_test_top.port_b.drv is a err_driver, anddebug_create_by_typereportsThe factory will produce an object of type 'err_driver'.Follow the calls, values and waits
- call incr_driver @ port_b.drv: create("drv") → Instance overrides
- data err_driver: Instance overrides → Instance result
- port_a.drv
No instance override matches
uvm_test_top.port_a.drv, and the example has no type override, so the factory builds the requestedincr_driver.More context
The example prints
uvm_test_top.port_a.drv is a incr_driver. Both agents are the same class with the same create call; only the path differs.Follow the calls, values and waits
- call incr_driver @ port_a.drv: create("drv") → Instance overrides
- call incr_driver @ port_a.drv: Instance overrides → Type override
- data incr_driver: Type override → Requested type
Read the diagram as text
- create("drv"): incr_driver at port_b.drv
- Instance overrides: In order added; first match wins
- Type override: Only if no instance match
- Instance result: err_driver for port_b
- Type result: Its override type
- Requested type: incr_driver for port_a
- create("drv") → Instance overrides: type + full path
- Instance overrides → Instance result: path matches
- Instance overrides → Type override: no match
- Type override → Type result: registered
- Type override → Requested type: none
The order of the calls that added the overrides does not change this order of checks.
A test may hold both kinds of override for the same requested type. At each create, the factory checks the instance overrides first, in the order they were added, and uses the first one whose path matches. Only when none matches does it use the type override.
The order of the calls in the test does not change this. A type override to slow_drv and an instance override to err_drv for port 1 give err_drv on port 1 and slow_drv everywhere else, whichever call came first.
When the override must exist
The factory decides at the moment of create. An override added after a component was created does not change it. So the question is when the driver is created, and the answer is in the agent's build_phase.
Build runs top-down. The test's build_phase runs to its end before the agent's build_phase starts. So an override added late in the test's build_phase still reaches the driver, even after the test has created the agent object. An override added in connect_phase arrives after every driver exists, and changes nothing.
Test build_phase
Adds the override, then creates the agents. Their drivers do not exist yet.
Agent build_phase
Each agent calls create for "drv". The factory matches the path and builds err_driver for port_b.
end_of_elaboration_phase
Every component exists. Print each driver's type name to confirm the swap before any time passes.
Check the result at the start of every error test. drv.get_type_name() returns the class actually built, and uvm_top.print_topology() lists every component with its type. A test that silently runs the normal driver passes for the wrong reason.
Ask the factory before the run
Two factory reports show the overrides themselves, before any time passes. The example calls both in the test's end_of_elaboration_phase.
uvm_factory::get().print(0), then debug_create_by_type(incr_driver::get_type(), "uvm_test_top.port_b", "drv"), report headers shortened.[UVM/FACTORY/PRINT]
Instance Overrides:
Requested Type Override Path Override Type
-------------- ----------------------- -------------
incr_driver uvm_test_top.port_b.drv err_driver
No type overrides are registered with this factory
[UVM/FACTORY/DUMP]
Given a request for an object of type 'incr_driver' with an instance
path of 'uvm_test_top.port_b.drv' the factory encountered
no relevant overrides.
Result:
The factory will produce an object of type 'err_driver'print(0) lists every registered override with its full path, so a pattern that aims at the agent instead of its driver shows up here. debug_create_by_type answers one question: what would create build at this path now? Its Result line names err_driver. Neither report says what was built earlier; that is what get_type_name() on the built driver tells you.
Choosing the smallest override
Pick the narrowest change that does the job. Overriding one driver at one path affects one port. Overriding the agent class would replace its monitor and sequencer too, and a type override would reach every port. Each wider choice is one more thing that can change by accident when the bench grows.
The same rules work for objects, with one extra step. A sequence item created inside a sequence with no context gets a path that is only its own name, such as it, as U2.2 showed. A full-path pattern then never matches it, while a pattern that is just the name matches every such item in the bench. Pass a context to create to give it a real path. Components always have one, which makes instance overrides the natural tool for one-port changes.
Injecting the error
In the example, err_driver extends incr_driver and overrides one virtual function, value_to_drive. The normal driver returns x; the error driver flips bit 7. The test overrides the driver at "port_b.drv" relative to itself, and runs the same two items, 10 and 11, on both ports.
Port B carries 10 + 128 = 138 and 11 + 128 = 139. Port A is unchanged.
Only port B changed, and neither the agent nor the sequence was edited. The design still adds 1, so a scoreboard that predicts from the intended items flags both port B results.
Try it
The first two lines confirm the swap before the run starts: port B's driver is an err_driver. The factory reports follow: one instance override at uvm_test_top.port_b.drv, and err_driver as the result for that path. Both monitors then report the same two items, with bit 7 set on port B only.
Change the pattern to "port_b" and run again: both drivers are incr_driver, because the pattern now names the agent. Then move the set_inst_override call to the test's connect_phase and see that it comes too late.
uvm_test_top.port_a.drv is a incr_driver uvm_test_top.port_b.drv is a err_driver incr_driver uvm_test_top.port_b.drv err_driver no relevant overrides. The factory will produce an object of type 'err_driver' 15 ns port_a saw x=10 y=11 15 ns port_b saw x=138 y=139 35 ns port_a saw x=11 y=12 35 ns port_b saw x=139 y=140 UVM_ERROR : 0Check yourself
Remember
- An instance override replaces a class only where the new component's full path matches the pattern.
- Aim the pattern at the component being created, such as
port_b.drv, not at its parent agent. - A
*matches any characters, so anchor names with dots:"*lane1*"also matcheslane10. - Instance overrides win over type overrides, and both must exist before the
createcall they should change. - Confirm the swap in
end_of_elaboration_phasewithget_type_name()before trusting an error test;factory.print(0)lists the overrides anddebug_create_by_typenames whatcreatewould build at a path.