You add dma_scoreboard_eot to the bench, compile, and get an error deep inside uvm_registry.svh about a call to new with the wrong arguments. Your own file looks fine. The cause is one word: you typed ` uvm_object_utils `` on a component. To read that error in seconds instead of an hour, you need to know what the macro actually expands to.
A registration macro is a business card the class hands to the factory. The card says three things: my name (a string, for by-name lookups and printing), my type (a unique handle, for by-type lookups), and how to build me (a constructor call with the right arguments). The factory never sees your class directly; it only ever holds the card, which UVM calls a proxy or registry object.
Because the card includes how to build me, the macro must match the class kind. A component is built with a name and a parent; an object is built with a name only. Pick the wrong card and the generated constructor call does not compile.
Here is roughly what ` uvm_component_utils(dma_scoreboard_eot) `` puts into the class. The real expansion has more plumbing, but these are the members you call:
// snippet: approximately what the component macro generates
typedef uvm_component_registry #(dma_scoreboard_eot, "dma_scoreboard_eot") type_id;
static function type_id get_type();
return type_id::get(); // the single proxy object for this class
endfunction
virtual function uvm_object_wrapper get_object_type();
return type_id::get();
endfunction
const static string type_name = "dma_scoreboard_eot";
virtual function string get_type_name();
return type_name;
endfunctiontype_id is a specialization of uvm_component_registry. Its create(name, parent) asks the factory which class to build, and the registry for the winning class finally calls that class's new(name, parent). The object macro generates the same members with uvm_object_registry, whose create calls new(name) or new(), plus a create() method that clone() relies on.
The macro assumes a constructor of a fixed shape. The bench follows it everywhere; two examples:
// snippet: the two constructor shapes from the bench
class dma_scoreboard_eot extends dma_scoreboard; // a component
`uvm_component_utils(dma_scoreboard_eot)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
// ...
endclass
class dma_job_cfg extends uvm_object; // an object
`uvm_object_utils(dma_job_cfg)
function new(string name = "dma_job_cfg");
super.new(name);
endfunction
// ...
endclassThe default value on the object's name matters: the generated create() calls new() with no argument when no name is given, so an object constructor without a default fails to compile as soon as the macro is added.
Macro | Use it on | Notes |
|---|---|---|
| Drivers, monitors, agents, scoreboards, envs, tests | Constructor |
| Sequence items, sequences, config objects | Constructor |
| Components with field macros | Field macros on a component drive auto-config; see Auto-config and field macros. |
| Objects with field macros | Generates copy, compare, print and pack; see Field macros. |
| Parameterized classes | Registers by type only, with no name string. See Factory advanced. |
| Virtual (abstract) classes | IEEE 1800.2 only. See Factory advanced. |
Sequences are objects too, so they use the object macro and the object constructor, even though they run for a long time and talk to a sequencer. Only classes that live in the component tree (they have a parent and take part in phases) use the component macro. When in doubt, ask whether the class is created in a build_phase with a parent: if yes, component; if it can be created many times at run time, object.
The second argument of the registry, the name string, is what every string-based lookup uses: +UVM_TESTNAME, set_type_override_by_name, the command-line overrides and the factory's print. Names are global across the whole compile, not scoped by package. If two packages each define a class called driver (two VIPs from different vendors, say), the second registration produces a TPRGED warning: the type name is already registered, and string-based lookup cannot tell the two apart. By-type overrides still work, because each class has its own proxy; by-name overrides and command-line overrides become ambiguous.
That is why VIP classes carry a protocol prefix (apb_driver, axis_driver) even inside their own package. A TPRGED warning in a log is worth fixing on sight: it means some by-name lookup in the bench is a coin toss.
You can see registration at work in two places in any bench run. The topology table printed by print_topology lists each component with the string from get_type_name():
uvm_test_top dma_smoke_test - @172
env dma_env - @242
apb apb_agent - @256
drv apb_driver - @458And +UVM_TESTNAME=dma_drain_test works only because dma_drain_test registered its name string: run_test hands that string to the factory, which finds the proxy by name and builds the test. A test class without a registration macro cannot be selected by name at all.
Back to the opening. With ` uvm_object_utils(dma_scoreboard_eot) ` on a component, the object registry tries to compile new(name) against a constructor that requires a parent. The error points into uvm_registry.svh or the macro expansion, not at your line, which is why it is confusing. The opposite mistake, uvm_component_utils ` on an item, generates new(name, parent)` against an item constructor that takes only a name, and fails the same way.
Either way it is a compile error, not a run-time misbehaviour. When you see a registry error mentioning new, look at the macro and the constructor of the class you just touched. A second classic: a class with no macro at all that extends a registered class. That one compiles and silently builds the parent class; it is the inherited type_id trap in Factory advanced.
axis_err_item extends axis_item, add ` uvm_object_utils(axis_err_item) `, and give it function new(string name); super.new(name); endfunction`. Why does it fail to compile?The macro's generated create() calls new() with no argument when no name is passed. Without a default value on name, that call has a missing argument. Add a default: function new(string name = "axis_err_item");.
+UVM_TESTNAME=dma_rand_test select a test that is never mentioned in the top module?dma_rand_test registered its name string through uvm_component_utils. run_test reads the plusarg and asks the factory to create a component by that name, so any registered test in the compiled code can be selected. The string passed to run_test("dma_smoke_test") in the top module is only the default.
drv apb_driver. Where does that string come from, and could it lie?From get_type_name(), generated by the macro from the class name. It lies in one case: a derived class with no macro of its own inherits the parent's get_type_name() and reports the parent's name. That is one more reason every class that may be created or printed carries its own macro.
Next: with registration understood, The type_id::create pattern covers the call side: create for components and objects, and the factory methods underneath.