A user defined phase that spans multiple run-time phases in UVM

A question was raised by verification engineer I work with regarding using phases in uvm.

A monitor is designed to constantly check the outputs of the design. However, at the very beginning, before the completion of reset process, the outputs may be invalid or even X’s, which causes monitor to report false errors.

The ask is to have a way to start the monitor after reset and stops after main phase. The monitor should be turned off during shutdown process.

It seems there’s no built-in way of doing this in UVM framework.

There might be several ways to do this.

a) Put the monitor logic in run_phase and have the codes wait for a reset_complete event and exit after detecting another main_complete event. To do this, we can use one of the two following ways.

a-1) generate event signals from design or somewhere in the test environment (but usually can’t do this from monitor itself as monitor may not have access to such info/signals). This causes dependency between components, which compromises the re-use of the monitor.

a-2) generate the event within the monitor itself, from phase_ready_to_end function in reset/main phases, or simply in post_reset & post_main phases. This seems much simpler but because of the parallel nature of components running in environment there’s no guarantee that the event is generated really as the last event of the phase. E.g. someone can raise objection in phase_ready_to_end function in another component in the same phase and extend the phase after the event generated in this monitor. Thus, the logic in run_phase may have already started even though the reset process hasn’t been fully completed. Same thing to the post_xxx phases.

b) Create a user defined phase that spans from pre_configure all the way to post_main phase.

Therefore I created a couple of class, as experiments, to make option b) easier. See codes below. Basically with the uvm_flex_phase_monitor class you can set the after/before phases easily. A flex phase that spans between the after/before phases will be automatically created and all you need to do is putting your logic code in this new flex_phase task.

We don’t have resources to extensively test. So before I extend this from monitor to other UVM components and put these in real project use I’d really like to hear from experts 1) What’s the recommended way to do this? 2) Are there any issues in the codes that might cause other unexpected problems?


// uvm_flex_phase.svh
typedef class uvm_flex_phase_component;

class uvm_flex_phase #(type AFTER_PHASE = uvm_post_reset_phase,
                       type BEFORE_PHASE = uvm_pre_shutdown_phase) extends uvm_task_phase;

    virtual task exec_task(uvm_component comp, uvm_phase phase);
        uvm_flex_phase_component#(AFTER_PHASE, BEFORE_PHASE) flex_phase_comp;
        if ($cast(flex_phase_comp, comp))
            flex_phase_comp.flex_phase(phase);
    endtask : exec_task

    local static uvm_flex_phase #(AFTER_PHASE, BEFORE_PHASE) m_inst;
    static const string type_name = "uvm_flex_phase";

    protected function new(string name = {"flex", "_after_", AFTER_PHASE::get().get_name(), "_before_", BEFORE_PHASE::get().get_name()});
        super.new(name);
    endfunction : new

    static function uvm_flex_phase #(AFTER_PHASE, BEFORE_PHASE) get();
        if (m_inst == null)
            m_inst = new();
        return m_inst;
    endfunction : get

    virtual function string get_type_name();
        return {type_name, "_after_", AFTER_PHASE::get().get_type_name(), "_before_", BEFORE_PHASE::get().get_type_name()};
    endfunction : get_type_name;

endclass : uvm_flex_phase

// uvm_flex_phase_component.svh
typedef class uvm_flex_phase;

virtual class uvm_flex_phase_component
    #(type AFTER_PHASE = uvm_post_reset_phase,
      type BEFORE_PHASE = uvm_pre_shutdown_phase) extends uvm_component;


    local uvm_phase uvm_schedule;
    local uvm_phase after_phase, before_phase;

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

        after_phase = AFTER_PHASE::get();
        before_phase = BEFORE_PHASE::get();

        if (uvm_flex_phase#(AFTER_PHASE, BEFORE_PHASE)::get().is_after(after_phase) ||
            uvm_flex_phase#(AFTER_PHASE, BEFORE_PHASE)::get().is_before(before_phase))
            return;

        uvm_schedule = uvm_domain::get_uvm_schedule();

        uvm_schedule.add(uvm_flex_phase#(AFTER_PHASE, BEFORE_PHASE)::get(), .after_phase(after_phase), .before_phase(before_phase));

    endfunction : new

    const static string type_name = "uvm_flex_phase_component #(AFTER_PHASE, BEFORE_PHASE)";

    virtual function string get_type_name();
        return type_name;
    endfunction : get_type_name

    virtual task flex_phase(uvm_phase phase);
        return;
    endtask: flex_phase

endclass : uvm_flex_phase_component

// uvm_flex_phase_monitor.svh
typedef class uvm_flex_phase_component;

virtual class uvm_flex_phase_monitor
    #(type AFTER_PHASE = uvm_post_reset_phase,
      type BEFORE_PHASE = uvm_pre_shutdown_phase) extends uvm_flex_phase_component #(AFTER_PHASE, BEFORE_PHASE);

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

    const static string type_name = "uvm_flex_phase_monitor #(AFTER_PHASE, BEFORE_PHASE)";

    virtual function string get_type_name();
        return type_name;
    endfunction : get_type_name;

endclass : uvm_flex_phase_monitor

// Usage -- set the after/before phases parameters when you extends the monitor
//          and put your logic codes in the flex_phase task.

class your_monitor extends uvm_flex_monitor #(uvm_post_reset_phase, uvm_pre_shutdown_phase);

virtual task flex_phase(uvm_phase phase);
... Your code ...;
endtask
....
endclass



Hi, you need some coupling between this component and in your test environment, whichever mechanism you use to start or stop the monitor operation.

I would recommend simpler solution a1 because using user defined phases is a lot of extra work and makes your testbench quite hard to comprehend by other team members.

You would run the monitor continuously but have an enable bit to control whether it actually monitors or not. That enable bit could either be set by an API on the monitor, or exist in a shared config object or similar. Of course the controlling end needs to know how to turn it on and off so some coupling exists. If you want, you could minimize coupling by adding a further stage of decoupling, so that whichever component DOES have access to the reset condition/signal/criteria (another monitor?), can just report that fact, and some other component can act as a broker, listening for that status, and calling the mechanism to enable/disable your monitor. Your scoreboard might be a common place to make that link.

In reply to gordon:

Thank you for your answer.

But I felt that this would be quite common problem – needing to control the start/stop of components to skip certain reset/initialization/shutdown processes.

If this is true, shouldn’t UVM framework provide something to support that instead of letting people to implement this in different ways and thus causes problems in verification component reuse?