Wait_for_state() in a static module doesn't work as expected

I tried using wait_for_state() of uvm_phase class to wait for a specific UVM phase in a module. I referred to the following UVM 1.2 document site.
wait_for_state()

Here is the TB code.

`include "uvm_macros.svh"
module tb;
    import uvm_pkg::*;
        
    class my_test extends uvm_test;

        function new(string name, uvm_component parent);
            super.new(name, parent);
        endfunction

        `uvm_component_utils(my_test)

        virtual function void build_phase(uvm_phase phase);
            `uvm_info(this.get_type_name(), "Build Phase", UVM_MEDIUM)
        endfunction

        virtual task run_phase(uvm_phase phase);
            phase.raise_objection(this, "Starting Run Phase");
            `uvm_info(this.get_type_name(), "Run Phase", UVM_MEDIUM)
            #100ns;
            phase.drop_objection (this, "Ending Run Phase");
        endtask

    endclass

    initial
      run_test("my_test");

    initial begin
      uvm_pkg::uvm_run_phase::get().wait_for_state(uvm_pkg::UVM_PHASE_STARTED);
      `uvm_info("TB", "Waited for Run Phase Start", UVM_MEDIUM)
    end

endmodule;

But this doesn’t work. “Waited for Run Phase Start” is not printed in the log.

# UVM_INFO @ 0: reporter [RNTST] Running test my_test...
# UVM_INFO testbench.sv(17) @ 0: uvm_test_top [my_test] Build Phase# UVM_INFO testbench.sv(22) @ 0: uvm_test_top [my_test] Run Phase
# UVM_INFO /usr/share/questa/questasim/verilog_src/uvm-1.2/src/base/uvm_objection.svh(1270) @ 100: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
# UVM_INFO /usr/share/questa/questasim/verilog_src/uvm-1.2/src/base/uvm_report_server.svh(847) @ 100: reporter [UVM/REPORT/SERVER] 

Why doesn’t this work? Any issues?
Here is EDA Playground link.

1 Like

I have been unable to get the wait_for_state() function to work outside of the UVM hierarchy. I can see it work inside a UVM class where the phase is passed in from the UVM scheduler, but I am not sure how to successfully get the correct phase handle outside of the UVM environment.

@cgales,
Thank you for your reply.

uvm_<phase name>_phase::get() is a static function, which returns the singleton phase handle.
uvm_run_phase::get()

I thought it is the same scheme as uvm_root::get(), uvm_report_server::get_server(), uvm_factory::get() and it is possible to use it in a static module outside UVM class in the same way.

But what are you trying to achieve by using it inside “initial begin..end”?

Is there a real purpose for it?
Or just for experimental cause?

Hi @MichaelP,
I want to wait for test configuration done by UVM test class in a static module to sync the test configuration between UVM and static module TB components.
In the actual usage, I am using uvm_pkg::uvm_wait_for_nba_region() for that purpose now.
For example,

module TB;
  
  initial uvm_pkg::run_test();
  
  bit clk;
  initial begin
    int      clock_speed_mhz;
    realtime clk_half_peridod;
    uvm_pkg::umv_wait_for_nba_region();
    uvm_pkg::uvm_config_db#(int)::get(null,"","clk_speed",clk_speed_mhz);
    clk_half_period = 1000ns/clk_speed_mhz/2;
    forever #(clk_half_period) clk = !clk;
  end

end

I am searching for more decent UVM phase wait way in a static module. So, I tried wait_for_state() as an experiment. But it didn’t work as I expected.

The undisputed king of SV and UVM dave_59 once wrote:
The UVM has a lot of feature-creep..

The more time you spend working with the UVM base code - or generally writing and building UVM-based testbenches - the more you realize it includes many capabilities and methods you’ll probably never use.

A common practice in UVM-based testbenches is to pass a virtual interface from the static module to the dynamic, class-based verification environment.
You can find examples of this approach in the UVM Cookbook, which demonstrates the use of the uvm_config_db construct for set and get operations.

Usually we will get the virtual interface in the build_phase of the test.

Frankly, I’ve never come across code that tries to synchronize between a static module and UVM dynamic class phases using: uvm_pkg::uvm_run_phase::get().wait_for_state(uvm_pkg::UVM_PHASE_STARTED);

Only saw it used in sequences we try make phase-aware (and this should only be done as a last resort because it is likely to make the code harder to maintain).
We’ll probably need someone with hands-on experience in this area.

When you execute: uvm_pkg::run_test();
It phases all components through all registered phases.

My question is - do you really need to implement what you’re attempting here?
Or might you be overcomplicating it?

I really like the image below:

source: Stop oversimplifying simplicity

Writing simple, functional, and maintainable code is the hallmark of good engineering.

Additional reads on umv_wait_for_nba_region in the next posts:

https://forums.accellera.org/topic/1611-what-is-the-intend-of-uvm_wait_for_nba_region/

@MichaelP
Thanks. Yes. I know there are a lot of other ways to sync static module world and UVM class dynamic world. For example, interface usage as you suggested. Besides, via uvm_event in config DB, or simply putting SV event in static places like global area or packages. uvm_event_pool may be another way. uvm_wait_for_naba_region() is also one way.

My question is - do you really need to implement what you’re attempting here?
Or might you be overcomplicating it?

Yes. It is really necessary. In reality, mixture of static TB components and UVM class base TB is quite common. Typical example is conventional module based BFM usage with UVM. We usually don’t intend to make a lot of efforts to convert existing assets to UVM class based components. In such case, syncing module based components and UVM phases is the easiest way. Moreover, uvm_wait_for_nba_region() usage is the best practice so far.
But I also know it is originally not for that purpose. That’s why I am searching for better and more decent way.

Anyhow, I did the following experiment.

  • uvm_run_phase::get() usage in TB module, run_phase() task, main_phase() tasks and their comparison with phase in each tasks’ argument.
  • run_phase() done wait in main_phase() in UVM test class.

`include "uvm_macros.svh"
module tb;
    import uvm_pkg::*;
        
    class my_test extends uvm_test;

        function new(string name, uvm_component parent);
            super.new(name, parent);
        endfunction

        `uvm_component_utils(my_test)

        virtual task run_phase(uvm_phase phase);
            `uvm_info(this.get_type_name(), "Run Phase", UVM_MEDIUM)
            `uvm_info(this.get_type_name(), $sformatf("    phase(%s) : %h %p\n%s",phase.get_full_name(),phase,phase.get_state(),phase.sprint()), UVM_MEDIUM)
            begin
                uvm_run_phase run_phase = uvm_run_phase::get();
                `uvm_info(this.get_type_name(), $sformatf("run_phase(%s) : %h %p\n%s",run_phase.get_full_name(),run_phase,run_phase.get_state(),run_phase.sprint()), UVM_MEDIUM)
            end
            phase.raise_objection(this, "Starting Run Phase");
            #100ns;
            phase.drop_objection (this, "Ending Run Phase");
        endtask

        virtual task main_phase(uvm_phase phase);
            `uvm_info(this.get_type_name(), "Main Phase", UVM_MEDIUM)
            `uvm_info(this.get_type_name(), $sformatf("    phase(%s) : %h %p\n%s",phase.get_full_name(),phase,phase.get_state(),phase.sprint()), UVM_MEDIUM)
            phase.raise_objection(this, "Starting Main Phase");
            begin
                uvm_run_phase run_phase = run_phase = uvm_run_phase::get();
                `uvm_info(this.get_type_name(), $sformatf("run_phase(%s) : %h %p\n%s",run_phase.get_full_name(),run_phase,run_phase.get_state(),run_phase.sprint()), UVM_MEDIUM)
                run_phase.wait_for_state(UVM_PHASE_DONE);
            end
            #100ns;
            phase.drop_objection (this, "Ending Main Phase");
        endtask

    endclass

    initial run_test("my_test");

    initial begin
        uvm_pkg::uvm_run_phase run_phase;
        run_phase = uvm_pkg::uvm_run_phase::get();  
        `uvm_info("TB", "module initial begin", UVM_MEDIUM)
        `uvm_info("TB", $sformatf("run_phase(%s) : %h %p\n%s",run_phase.get_full_name(),run_phase,run_phase.get_state(),run_phase.sprint()), UVM_MEDIUM)
        run_phase.wait_for_state(UVM_PHASE_STARTED);
        `uvm_info("TB", "Waited for Run Phase Start", UVM_MEDIUM)
    end

endmodule;

The result is quite interesting.

UVM_INFO tb.sv(46) @ 0: reporter [TB] module initial begin
UVM_INFO tb.sv(47) @ 0: reporter [TB] run_phase(run) : 00007f72856dce00 UVM_PHASE_UNINITIALIZED
--------------------------------
Name  Type           Size  Value
--------------------------------
run   uvm_run_phase  -     @170 
--------------------------------

UVM_INFO tb.sv(15) @ 0: uvm_test_top [my_test] Run Phase
UVM_INFO tb.sv(16) @ 0: uvm_test_top [my_test]     phase(common.run) : 00007f72856dcc00 UVM_PHASE_EXECUTING
----------------------------
Name  Type       Size  Value
----------------------------
run   <unknown>  -     @172 
----------------------------

UVM_INFO tb.sv(19) @ 0: uvm_test_top [my_test] run_phase(run) : 00007f72856dce00 UVM_PHASE_UNINITIALIZED
--------------------------------
Name  Type           Size  Value
--------------------------------
run   uvm_run_phase  -     @170 
--------------------------------

UVM_INFO tb.sv(27) @ 0: uvm_test_top [my_test] Main Phase
UVM_INFO tb.sv(28) @ 0: uvm_test_top [my_test]     phase(uvm.uvm_sched.main) : 00007f72855fb600 UVM_PHASE_EXECUTING
----------------------------
Name  Type       Size  Value
----------------------------
main  <unknown>  -     @113 
----------------------------

UVM_INFO tb.sv(32) @ 0: uvm_test_top [my_test] run_phase(run) : 00007f72856dce00 UVM_PHASE_UNINITIALIZED
--------------------------------
Name  Type           Size  Value
--------------------------------
run   uvm_run_phase  -     @170 
--------------------------------

UVM_FATAL uvm_phase_hopper.svh(587) @ 9200000000000: reporter [PH_TIMEOUT] Default timeout of 9200000000000 hit, indicating a probable testbench issue
  • The instance of uvm_run_phase::get() and phase argument of run_phase() task are different.
  • The instance of uvm_run_phase::get() is same in TB module and UVM class.
  • uvm_run_phase::get().wait_for_state() doesn’t work even in UVM class (timeout in main_phase()).

I wonder what wait_for_state() is for… :confused: I looks it is useless even in UVM class. I originally thought it is for UVM phase sync in static modules since it is static. It doesn’t need to be static if it is not used in modules. I really wonder why it is static…

This might be kind of a bug of UVM.

@cgales, @MichaelP ,
After asking to ChatGPT, I got the following way. I confirmed this way works. How smart it is!

initial begin
  uvm_phase  run_phase;
  run_phase = uvm_domain::get_common_domain().find(uvm_run_phase::get());
  run_phase.wait_for_state(UVM_PHASE_STARTED);
  ...
end

uvm_domain manages UVM phases. So, we need to get its instance. uvm_domain::get_common_domain() is for it. find() can be used to get specified uvm_<phae name>_phase instance under uvm_domain. uvm_run_phase::get() is to give the argument of uvm_phase::find().

uvm_domain::get_common_domain()
uvm_phase::find()

@Orimitsu
You are right, did not think about this usage.
In this case it is really necessary.

Would be happy to see more code snippets, if it is possible for you to share, on usage of: uvm_wait_for_nba_region()
If you prefer to move to private mailing.

I would suggest as well the next paper:

Specifically chapter VI.
You might find it useful.

Would be happy to hear what approach you chose eventually to handle this sync.

1 Like

@MichaelP
Thank you for sharing the DVCon paper. Yes. Chapter VI “Phase Aware Configuration” is the case that static area needs to know UVM phases. In this case, an UVM component class is instantiated in an interface. I also found such a proposal in the past post of this forum.

I have updated the EDA playground.
UVM Phase State Wait In A Module
It includes three UVM phase ways now.

  • WAIT_FOR_NBA_REGION
  • UVM_RUN_PHASE // Doesn’t Work!!!
  • COMMON_DOMAIN_RUN_PHASE

Although I cannot share codes which use uvm_wait_for_nba_region() in the actual projects, you can find in in my published GitHub repo.

uvm_wait_for_nba_region() is used in TB2/test_bench.sv of it.

I would choose COMMON_DOMAIN_RUN_PHASE way from now on. I might put it in my own custom uvm package library for more generic usage.
I.e)

package my_custom_uvm_pkg;
  import uvm_pkg::*;
  ...
  task automatic wait_run_phase(wait_state=UVM_PHASE_STARTED);
    uvm_phase  run_phase;
    run_phase = uvm_domain::get_common_domain().find(uvm_run_phase::get());
    run_phase.wait_for_state(wait_state);
  endtask
  ...
endpackage
1 Like