Search code examples
system-veriloguvm

Domain separation in UVM


In order to reset the individual agents of the test environment, I try to transfer them to separate domains. However, I have encountered difficulty: when I set separate domain for an agent, sequence items cease to flow to the driver of this agent.

Below is the simplest example I could write. If you comment out the lines

    ag1.set_domain (d1);
    ag2.set_domain (d2);

then the drivers of the agents will receive the data; if you uncomment it, they will stop. However, if you place the jump inside the fork block, it will occur.

If you move the domain setting to the main phase of test class, the data will go, but the jump to the pre_reset_phase will not occur.

`include "uvm_macros.svh"
package t;
    import uvm_pkg::*;
    class seq_item extends uvm_sequence_item;
        `uvm_object_utils(seq_item)

        rand bit [31:0]            data;
        function new(string name = "apb_seq_item");
            super.new(name);
        endfunction: new
    endclass: seq_item

    class m_sequence extends uvm_sequence#(seq_item);
        `uvm_object_utils(m_sequence)

        function new(string name = "");
            super.new(name);
        endfunction: new

        task body();
            repeat(5) begin
                req = seq_item::type_id::create("ap_it");
                start_item(req);
                req.randomize();
                finish_item(req);
            end
        endtask: body
    endclass: m_sequence

    class driver extends uvm_driver#(seq_item);
        `uvm_component_utils(driver)
        function new (string name, uvm_component parent);
            super.new(name, parent);
        endfunction: new

        task main_phase(uvm_phase phase);
            fork
                super.main_phase(phase);
            join_none
            forever begin
                seq_item_port.get_next_item(req);
                `uvm_info(get_type_name(),$psprintf("Got item with data: %h",req.data),UVM_NONE);
                seq_item_port.item_done();
            end
        endtask: main_phase
    endclass: driver
    class agent extends uvm_agent;
        `uvm_component_utils(agent)
        uvm_analysis_port#(seq_item) ap;

        uvm_sequencer#(seq_item)     seqr;
        driver                       drv;

        function new(string name, uvm_component parent);
            super.new(name,parent);
        endfunction: new
        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            ap  = new(.name("apb_ap"), .parent(this));
            seqr= uvm_sequencer#(seq_item) ::type_id::create(.name("uvm_sequenver"), .parent(this) );
            drv = driver        ::type_id::create(.name("driver"), .parent(this) );
        endfunction: build_phase

        function void connect_phase(uvm_phase phase);
            super.connect_phase(phase);
            drv.seq_item_port.connect(seqr.seq_item_export);
        endfunction: connect_phase

        task pre_reset_phase(uvm_phase phase);
            fork
                super.pre_reset_phase(phase);
            join_none
            `uvm_info(get_type_name(),"jumped into pre_reset_phase",UVM_NONE);
        endtask
    endclass: agent
    class env extends uvm_env;
        `uvm_component_utils(env)

        agent ag1;
        agent ag2;
        uvm_domain d1;
        uvm_domain d2;
        function new(string name, uvm_component parent);
            super.new(name,parent);
        endfunction: new

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            ag1 = agent::type_id::create("ag1", this);
            ag2 = agent::type_id::create("ag2", this);
            d1 = new("d1");
            d2 = new("d2");

            ag1.set_domain(d1);
            ag2.set_domain(d2);
        endfunction: build_phase
    endclass: env

    class test extends uvm_test;
        `uvm_component_utils(test)
        env e;
        m_sequence seq1;
        m_sequence seq2;

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

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            e = env::type_id::create("env",this);
        endfunction
        task main_phase(uvm_phase phase);
            fork
                super.main_phase(phase);
            join_none
            phase.raise_objection(this);
            seq1  = m_sequence::type_id::create("se1");
            seq2  = m_sequence::type_id::create("se2");
            fork
                seq1.start(e.ag1.seqr);
                seq2.start(e.ag2.seqr);
            join
            e.d1.jump(uvm_pre_reset_phase::get());
            phase.drop_objection(this);
        endtask
    endclass: test
endpackage

module top();
    import uvm_pkg::*;
    import t::*;
    initial begin
        run_test();
    end
endmodule

Solution

  • UVM run-time phases are used to control the order of things happening. If things are done in different phases, then you can guarantee that something done in a later phase will happen before something done in an earlier phase.

    Your original code creates two new phase domains and puts the two agents into these new domains. The rest of the test bench is in the original domain. If you do not synchronise domains, then you can no longer guarantee which order things will happen in.

    So, I have made a few changes to your code:

    i) I have added a reference to the sequence in the agent:

       m_sequence seq;
    

    ii) I have added code into the main phase of the agent to (a) raise an objection and (b) start the sequence. Now (a) each sequence is started at the right time in its domain and (b) crucially the phase will not end until the sequence is complete. (You raise an objection to a phase ending, so now each main_phase needs its own objection.)

        task main_phase(uvm_phase phase);
          phase.raise_objection(this);
          `uvm_info(get_type_name(),"jumped into main_phase",UVM_NONE);
          seq.start(seqr);
          phase.drop_objection(this);
        endtask
    

    iii) I have added an extra parameter to your set_domain method call, so that children of the agents are also put into the new domain (by default they are not):

          ag1.set_domain(d1,1);
          ag2.set_domain(d2,1);
    

    iv) I have put the code to create the sequences into the configure_phase of the test. I doubt this guarantees that the sequences are created before the main phases start, so it's working more by luck than judgement. (You need to use the fact that phases guarantee the order of execution to fine-tune this.)

        task configure_phase(uvm_phase phase);
          e.ag1.seq  = m_sequence::type_id::create("se1");
          e.ag2.seq  = m_sequence::type_id::create("se2");
        endtask
    

    v) Finally, to ensure that the phase jump occurs after the sequences have finished, I had added a delay before executing it:

            #1 e.d1.jump(uvm_pre_reset_phase::get());
    

    This is a bit of a hack. Again, given you've jumped into the world of phase domains, you'd probably want to use phases to guarantee this.

    But finally... phase jumping is a bit of a hack. I would recommend only using it as a last resort. Is there not an easier, more conventional way of repeating the sequence for domain 1?


    `include "uvm_macros.svh"
    package t;
        import uvm_pkg::*;
        class seq_item extends uvm_sequence_item;
            `uvm_object_utils(seq_item)
    
            rand bit [31:0]            data;
            function new(string name = "apb_seq_item");
                super.new(name);
            endfunction: new
        endclass: seq_item
    
        class m_sequence extends uvm_sequence#(seq_item);
            `uvm_object_utils(m_sequence)
    
            function new(string name = "");
                super.new(name);
            endfunction: new
    
            task body();
                repeat(5) begin
                    req = seq_item::type_id::create("ap_it");
                    start_item(req);
                    req.randomize();
                    finish_item(req);
                end
            endtask: body
        endclass: m_sequence
    
        class driver extends uvm_driver#(seq_item);
            `uvm_component_utils(driver)
            function new (string name, uvm_component parent);
                super.new(name, parent);
            endfunction: new
    
            task main_phase(uvm_phase phase);
                fork
                    super.main_phase(phase);
                join_none
                forever begin
                    seq_item_port.get_next_item(req);
                    `uvm_info(get_type_name(),$psprintf("Got item with data: %h",req.data),UVM_NONE);
                    seq_item_port.item_done();
                end
            endtask: main_phase
        endclass: driver
        class agent extends uvm_agent;
            `uvm_component_utils(agent)
             uvm_analysis_port#(seq_item) ap;
           m_sequence seq;
    
            uvm_sequencer#(seq_item)     seqr;
            driver                       drv;
    
            function new(string name, uvm_component parent);
                super.new(name,parent);
            endfunction: new
            function void build_phase(uvm_phase phase);
                super.build_phase(phase);
                ap  = new(.name("apb_ap"), .parent(this));
                seqr= uvm_sequencer#(seq_item) ::type_id::create(.name("uvm_sequenver"), .parent(this) );
                drv = driver        ::type_id::create(.name("driver"), .parent(this) );
            endfunction: build_phase
    
            function void connect_phase(uvm_phase phase);
                super.connect_phase(phase);
                drv.seq_item_port.connect(seqr.seq_item_export);
            endfunction: connect_phase
    
            task pre_reset_phase(uvm_phase phase);
                fork
                    super.pre_reset_phase(phase);
                join_none
                `uvm_info(get_type_name(),"jumped into pre_reset_phase",UVM_NONE);
            endtask
    
            task main_phase(uvm_phase phase);
              phase.raise_objection(this);
              `uvm_info(get_type_name(),"jumped into main_phase",UVM_NONE);
              seq.start(seqr);
              phase.drop_objection(this);
            endtask
        endclass: agent
        class env extends uvm_env;
            `uvm_component_utils(env)
    
            agent ag1;
            agent ag2;
            uvm_domain d1;
            uvm_domain d2;
            function new(string name, uvm_component parent);
                super.new(name,parent);
            endfunction: new
    
            function void build_phase(uvm_phase phase);
                super.build_phase(phase);
                ag1 = agent::type_id::create("ag1", this);
                ag2 = agent::type_id::create("ag2", this);
                d1 = new("d1");
                d2 = new("d2");
    
                ag1.set_domain(d1,1);
                ag2.set_domain(d2,1);
            endfunction: build_phase
        endclass: env
    
        class test extends uvm_test;
            `uvm_component_utils(test)
            env e;      
    
            function new(string name, uvm_component parent);
                super.new(name, parent);
            endfunction
    
            function void build_phase(uvm_phase phase);
                super.build_phase(phase);
                e = env::type_id::create("env",this);
            endfunction
    
            task configure_phase(uvm_phase phase);
              e.ag1.seq  = m_sequence::type_id::create("se1");
              e.ag2.seq  = m_sequence::type_id::create("se2");
            endtask
    
            task main_phase(uvm_phase phase);
                fork
                    super.main_phase(phase);
                join_none
                phase.raise_objection(this);          
                #1 e.d1.jump(uvm_pre_reset_phase::get());
                phase.drop_objection(this);
            endtask
        endclass: test
    endpackage
    
    module top();
        import uvm_pkg::*;
        import t::*;
        initial begin
            run_test();
        end
    endmodule
    

    https://www.edaplayground.com/x/imV