How UVM knows "my_test" class? why/where do we need to import "my_pkg::*" that holds the class "my_test"?

I’m looking at the “uvm hello world” example at UVM Hello World - EDA Playground ,
and I’m wondering why do we need line 15 “import my_testbench_pkg::*;”?


/*******************************************
This is a basic UVM "Hello World" testbench.

Explanation of this testbench on YouTube:
https://www.youtube.com/watch?v=Qn6SvG-Kya0
*******************************************/

`include "uvm_macros.svh"
`include "my_testbench_pkg.svh"

// The top module that contains the DUT and interface.
// This module starts the test.
module top;
  import uvm_pkg::*;
  import my_testbench_pkg::*;
  
  // Instantiate the interface
  dut_if dut_if1();
  
  // Instantiate the DUT and connect it to the interface
  dut dut1(.dif(dut_if1));
  
  // Clock generator
  initial begin
    dut_if1.clock = 0;
    forever #5 dut_if1.clock = ~dut_if1.clock;
  end
  
  initial begin
    // Place the interface into the UVM configuration database
    uvm_config_db#(virtual dut_if)::set(null, "*", "dut_vif", dut_if1);
    // Start the test
    run_test("my_test");
  end
  
  // Dump waves
  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, top);
  end
  
endmodule

I understand that the compiler looks inside the imported pkg only if it doesn’t find the wanted name in the local scope.
so I understand that we need to import “uvm_pkg::*” in order to use “run_test()”,
but nothing in “testbench.sv” is using any class within “my_testbench_pkg.svh”,
so why do we need to import it?

what is the actual flow in uvm that makes it knows “my_test” class?
I understand it’s related to a factory and the line “`uvm_component_utils(my_test)”,
but why isn’t compilation enough?
why do I have to use “import my_testbench_pkg::*;”?
why the test fails when I delete this import?
(UVM_FATAL @ 0: reporter [INVTST] Requested test from call to run_test(my_test) not found.)

In reply to uriyam:

You have to import my_testbench_pkg because it contains your testbench topology including the definition of the class “my_test”.

In reply to chr_sue:

so i’m including it, it compiles…
why should I also import it in “testbench.sv” if no one is using it there?

I imagine that it needs to be imported inside the uvm_pkg, inside run_test(), maybe even in the factory, where uvm instantiate “my_test” class. but obviously we can’t inject an import into the uvm files. so what is the mechanism?

In reply to uriyam:

Of course you can include this file. In your simple environment it does not make any difference.
Packaging files belonging together like agent data in a package allows you to import these data in any place you need it like a different project.

In reply to chr_sue:

I don’t understand how your answer relates to my questions…?
the pkg is already included:
`include “my_testbench_pkg.svh”
isn’t this enough? why should I also import it inside the testbench module if no one is using it there?

(I have a thought maybe it relates to some compiler optimization? maybe without the import the compiler thinks no one is using those classes so he through them away…?)

In reply to uriyam:

Yes, that is correct, the compiler needs to know that someone is using the package.

Many compilation scripts compile much more than is needed for a particular simulation. Some tools require the user specify a top-level module, and the elaboration process figures out just the relevant units needed from that point downwards.

The UVM factory relies on initializing a static member of specialized parameterized class at time 0. That initialization calls a function that “registers” the class by adding an element to an array inside the UVM package. (See this article for more about that)

But that initialization code only gets executed if it’s determined that the package is needed for the simulation. There are a number of ways that can happen, which may involve tool specific options:

  • That package gets imported directly or indirectly into the design
  • You tell the compiler the package is a top-level unit.
  • You tell the compiler you want everything compiled to be part of the design.