Create() Vs new()

In short: In UVM you build objects and components with the factory call type_id::create() instead of the plain constructor new(). Both end up calling new(), but create() first asks the factory which class to build, so a single override line in a test can swap one class for another without editing the code that builds it. Use create() everywhere; keep new() only for the constructor definition itself.

When you first read UVM code you see two ways to make an object: the language keyword new(), and the longer my_type::type_id::create("name", this). They look like they do the same thing, and for a single run they often do. The difference shows up the moment you want one test to behave differently from another without touching shared code. This guide explains what each call really does, why the factory route matters, three worked examples, a side-by-side table, and the mistakes that quietly disable overrides.

A simple way to picture it

Think of ordering a taxi through an app instead of walking to one specific car. If you walk straight to a numbered car and get in, that is new(): you always get that exact car. If you open the app and ask for a ride, that is create(): the dispatcher decides which car turns up. Most days you get an ordinary car, but on a special day the dispatcher can send a different one, and you never change how you ask. The factory is that dispatcher, and the override is the note that tells it to send a different car today.

What new() does

new() is the plain SystemVerilog constructor. It builds one object of exactly the class you name, and nothing can change that at run time. It is the right tool inside a class to define how that class is constructed, and you still write a new() in every UVM class. What you avoid is calling it directly to build components and transactions, because a direct new() hard-codes the type.

// new() always builds exactly this class
bus_driver drv;
drv = new("drv", this);   // hard-coded: always a bus_driver, no override possible

What create() does

create() is added to every class by the registration macro. It asks the factory, is there an override for this type or this instance path, and then calls new() on whatever the factory decides. In the common case with no override it returns exactly the class you asked for, so it behaves like new(). The payoff is that a test can register an override once and every create() for that type quietly returns the replacement.

// create() asks the factory first, then constructs
bus_driver drv;
drv = bus_driver::type_id::create("drv", this); // factory may return a subclass

// for an object (no parent), pass only a name
bus_item req;
req = bus_item::type_id::create("req");

The registration macro is what makes create() exist

You do not get type_id or create() for free. The registration macro adds them: components use uvm_component_utils and objects such as sequences and sequence items use uvm_object_utils. Skip the macro and create() will not compile for that class, which is a common early error.

class bus_driver extends uvm_driver #(bus_item);
  `uvm_component_utils(bus_driver)   // gives bus_driver::type_id::create(...)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
endclass

class bus_item extends uvm_sequence_item;
  `uvm_object_utils(bus_item)        // gives bus_item::type_id::create(...)
  function new(string name = "bus_item");
    super.new(name);
  endfunction
endclass

Worked example: the override that create() enables

Here is the whole point in one example. The environment always builds its driver with create(). A normal test changes nothing. An error test registers a type override, and now the same environment builds an error-injecting driver instead, with no edit to the environment.

// environment: never edited, always uses create()
class bus_env extends uvm_env;
  `uvm_component_utils(bus_env)
  bus_driver drv;
  function new(string n, uvm_component p); super.new(n,p); endfunction
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    drv = bus_driver::type_id::create("drv", this); // factory decides the real type
  endfunction
endclass

// error test: one override line changes what create() returns
class error_test extends base_test;
  `uvm_component_utils(error_test)
  function new(string n, uvm_component p); super.new(n,p); endfunction
  function void build_phase(uvm_phase phase);
    bus_driver::type_id::set_type_override(error_driver::get_type());
    super.build_phase(phase); // env now builds an error_driver
  endfunction
endclass

If the environment had used drv = new("drv", this), the override would do nothing, because the factory never sees the request. That single detail is why the habit matters.

A second example: create() inside a sequence

The same rule applies to transactions. A sequence builds its items with create(), so a test can override the item type too, for example swapping a plain packet for a corner-case packet without rewriting the sequence.

task body();
  bus_item req;
  req = bus_item::type_id::create("req"); // an override can return a stress_item here
  start_item(req);
  assert(req.randomize());
  finish_item(req);
endtask

create() versus new(), side by side

Pointcreate()new()
Who decides the typeThe factory (can be overridden)Fixed at compile time
Enables overridesYesNo
Needs the registration macroYesNo
Arguments for a componentname and parentname and parent
Arguments for an objectname onlyname only
Where you use itBuilding components and transactionsOnly the constructor definition itself
Behaviour with no overrideSame result as new()The only result

When to use which

The rule is short. Use create() for every component and every transaction you build, so overrides stay available across the whole testbench. Write a new() in each class because the constructor still has to exist, and call new() directly only for plain data objects that are not part of the UVM factory, such as a simple helper class that never needs overriding. When in doubt, reach for create().

Common mistakes to avoid

  • Building components with new(), which hides the request from the factory so no override can apply.
  • Forgetting the registration macro, so type_id and create() do not exist for that class.
  • Passing a parent to create() for an object, or omitting it for a component; objects take a name only, components take a name and parent.
  • Setting a type override after super.build_phase() has already built the component, so the original class is created first.
  • Assuming create() is slow. The lookup happens once per object and is tiny next to the run phase.

Keep learning

This habit is what makes the UVM factory useful, so read the concept of the UVM factory for type, instance, and name overrides. See where create() is called throughout a real testbench in the UVM testbench architecture guide, and why the base classes are subclassed in inheritance in SystemVerilog. More topics live on the UVM category page, and this comparison is a frequent interview question.

Frequently asked questions

What is the main difference between create() and new()?

create() asks the UVM factory which class to build and then calls new() on it, so an override can substitute a different class. new() builds exactly the class you name with no chance of substitution. For a run with no override the result is the same, but only create() keeps overrides available.

Do I still need to write new() in my classes?

Yes. Every UVM class still defines a new() constructor, and create() calls it internally. What you avoid is calling new() directly to build components and transactions; use create() for that so the factory stays in the loop.

Why does my factory override do nothing?

The usual cause is building the component with new() instead of create(), so the factory never sees the request. Other causes are a missing registration macro or setting the override after super.build_phase() has already built the component.

How do the arguments differ between components and objects?

For a component you pass a name and a parent, such as create(“drv”, this). For an object such as a sequence item you pass only a name, such as create(“req”). Mixing these up is a common compile error.

Does create() work without the registration macro?

No. The uvm_component_utils or uvm_object_utils macro is what adds type_id and create() to the class. Without it the create() call will not compile for that type.

Is create() slower than new()?

Not in any way that matters. The factory lookup happens once when the object is built, which is a tiny cost next to the simulation time spent in the run phase. The flexibility is well worth it.

Similar Posts

2 Comments

  1. Hii , i am a verification trainee and i need some suggestions regarding verification so that i can be a better verification engineer

Comments are closed.