Multiple events at the same time

Hi SV forum.
I wrote a state machine in my scoreboard and I encountered a problem that I need help to solve.
My scb receives various items from various monitors.
these items are independent and can arrive at any time.
below is a snippet of the code.

The problem is that If I get 2 items at exactly the same time, for my case let us assume that
voltage_event and current_event triggered at the same time.
after we get out of disable fork;
do_something_a(); is activated
but also do_something_b(); is activated

my intention was that if both of them triggered at the same time
only do_something_a(); should be activated

In the waves I indeed see that both if them are arrived at the same time (print result) but if I expend time sequence I see ticks
and inside them events are changing.
How can I prevent this behavior?


typedef enum int {STATE_A, STATE_B STATE_C} state_t;
state_t state, next_state;

typedef enum int (V_EV_TYPE,C_EV_TYPE,P_EV_TYPE) event_type_t;
event_type_t event_type;

function void write_a (item_a item);
...
-> voltage_event;
endfunction

function void write_b (item_b item);
...
-> current_event;
endfunction

...

task run_phase (uvm_phase phase);
    do_something();
endtask

// Events are coming from various tlm write implementation
task do_something();
    state=STATE_A;
    next_state=STATE_A;
    forever begin
        fork
            begin
                @ voltage_event;
                event_type=V_EV_TYPE;
            end
            begin
                @ current_event;
                event_type=C_EV_TYPE;
            end
            begin
                @ protection_event;
                event_type=P_EV_TYPE;
            end
        join_any
        disable fork;

        state=next_state;

        case(event_type)
            V_EV_TYPE,C_EV_TYPE: begin
                do_something_a();
                next_state=STATE_B;
            end
            C_EV_TYPE: begin
                do_something_b();
                next_state=STATE_C;
            end
            V_EV_TYPE: begin
                do_something_c();
                next_state=STATE_A;
            end
        endcase
    end

endtask

In reply to shimonc:

Events never happen at the same time in SystemVerilog—one always occurs before the other.

Use a non-blocking assignment to avoid race conditions.

typedef enum int {STATE_A, STATE_B STATE_C} state_t;
state_t state, next_state;
 
bit V_EV, C_EV;

function void write_a (item_a item);
  ...
  V_EV <= 1;
endfunction
 
function void write_b (item_b item);
  ...
  C_EV <=1;
endfunction
 
...
 
task run_phase (uvm_phase phase);
    do_something();
endtask
 
// Events are coming from various tlm write implementation
task do_something();
    state=STATE_A;
    next_state=STATE_A;
    forever @(V_EV or C_EV) begin
        state=next_state;
        case(1)
            V_EV && C_EV: begin
                do_something_a();
                next_state=STATE_B;
            end
            C_EV: begin
                do_something_b();
                next_state=STATE_C;
            end
            V_EV: begin
                do_something_c();
                next_state=STATE_A;
            end
        endcase
      {V_EV,C_EV} = 0;
    end
 
endtask

In reply to dave_59:

Hi Dave thanks for your response,
I tried to implement your solution but I still see race condition problems.
obviously I am doing something wrong.
I will write a small component with 2 tlms and check it again.
I will put the code here once I am done so others can also learn from this.

In reply to shimonc:

Hi Dave,
I wrote the next code (sorry for the length)
It works, I triggered 2 sequences at the same time and I checked the results inside and out side the case and it work fine, I have 3 questions.

  1. if indeed 2 items arrive at the same time does it means that the first case condition will be executed always?
  2. in the waves I see that tlma_ev and tlmb_ev dont change at all, not even the usual glitch that I see in events variables, Is it ok?
  3. when I exit the case conditional does it matter if I write nonblocking {tlma_ev,tlmb_ev}=0; or blocking {tlma_ev,tlmb_ev}<=0;

interface prot_if ();
    logic [7:0] data;
endinterface
package rc;
    import uvm_pkg::*;
    `include "uvm_macros.svh"

    `uvm_analysis_imp_decl(_tlma)
    `uvm_analysis_imp_decl(_tlmb)

    class prot_item extends uvm_sequence_item;
        rand bit [7:0] data;
        `uvm_object_utils_begin(prot_item);
        `uvm_field_int(data,UVM_ALL_ON)
        `uvm_object_utils_end
        function new (string name="");
            super.new(name);
        endfunction
        function string convert2string();
            string s="";
            $sformat(s,"%s data=%0h",s,data);
            return s;
        endfunction
    endclass

    class prot_driver extends uvm_driver #(prot_item);
        virtual prot_if m_prot_if;
        `uvm_component_utils(prot_driver)
        function new (string name , uvm_component parent);
            super.new(name,parent);
        endfunction
        task run_phase (uvm_phase phase);
            super.run_phase(phase);
            forever begin 
                seq_item_port.get_next_item(req);
                `uvm_info("DRV",req.convert2string(),UVM_HIGH)
                m_prot_if.data <= req.data;
                seq_item_port.item_done();
            end
        endtask
    endclass

    class prot_monitor extends uvm_monitor;
        virtual prot_if m_prot_if;
        prot_item collected_item;
        uvm_analysis_port #(prot_item) ap;
        `uvm_component_utils(prot_monitor)
        function new (string name , uvm_component parent);
            super.new(name,parent);
            ap=new("ap",this);
        endfunction
        task run_phase (uvm_phase phase);
            super.run_phase(phase);
            forever begin 
                @(m_prot_if.data);
                collected_item=prot_item::type_id::create("item");
                collected_item.data=m_prot_if.data;
                `uvm_info("MNTR",collected_item.convert2string(),UVM_HIGH)
                ap.write(collected_item);
            end
        endtask
    endclass
    
    typedef uvm_sequencer #(prot_item) prot_sequencer;

    class prot_agent extends uvm_agent;
        prot_driver drv;
        prot_monitor mntr;
        prot_sequencer sqr;
        `uvm_component_utils(prot_agent)
        function new (string name , uvm_component parent);
            super.new(name,parent);
        endfunction
        function void build_phase (uvm_phase phase);
            super.build_phase(phase);
            drv=prot_driver::type_id::create("drv",this);
            mntr=prot_monitor::type_id::create("mntr",this);
            sqr=prot_sequencer::type_id::create("sqr",this);
        endfunction
        function void connect_phase (uvm_phase phase);
            super.connect_phase(phase);
            drv.seq_item_port.connect(sqr.seq_item_export);
        endfunction
    endclass

    class prot_scb extends uvm_scoreboard;
        uvm_analysis_imp_tlma#(prot_item,prot_scb) imp_tlma;
        uvm_analysis_imp_tlmb#(prot_item,prot_scb) imp_tlmb;
        bit tlma_ev;
        bit tlmb_ev;
        `uvm_component_utils(prot_scb)
        function new (string name, uvm_component parent);
            super.new(name,parent);
            imp_tlma=new("imp_tlma",this);
            imp_tlmb=new("imp_tlmb",this);
        endfunction
        function void write_tlma (prot_item item);
            `uvm_info("TLMA",item.convert2string(),UVM_HIGH)
            tlma_ev<=1;
        endfunction
        function void write_tlmb (prot_item item);
            `uvm_info("TLMB",item.convert2string(),UVM_HIGH)
            tlmb_ev<=1;
        endfunction
        task run_phase (uvm_phase phase);
            super.run_phase(phase);
            change_state();
        endtask
        task change_state();
            string event_type="";
            forever @(tlma_ev or tlmb_ev) begin
                case (1)
                    tlma_ev: begin
                        print_time("tlma_ev");
                        event_type="tlma_ev";
                    end
                    tlmb_ev: begin
                        print_time("tlmb_ev");
                        event_type="tlmb_ev";
                    end
                endcase
                {tlma_ev,tlmb_ev}=0;
                `uvm_info("",$sformatf("event_type=%s",event_type),UVM_MEDIUM)
            end
        endtask
        function void print_time (string str);
            `uvm_info("",$sformatf("%s was activated sim time is %0t",str,$time()),UVM_HIGH)
        endfunction
    endclass

    class prot_env extends uvm_env;
        prot_agent m_prot_agent_a;
        prot_agent m_prot_agent_b;
        prot_scb m_prot_scb;
        `uvm_component_utils(prot_env)
        function new (string name , uvm_component parent);
            super.new(name,parent);
        endfunction
        function void build_phase (uvm_phase phase);
            super.build_phase(phase);
            m_prot_agent_a=prot_agent::type_id::create("m_prot_agent_a",this);
            m_prot_agent_b=prot_agent::type_id::create("m_prot_agent_b",this);
            m_prot_scb=prot_scb::type_id::create("m_prot_scb",this);
        endfunction
        function void connect_phase (uvm_phase phase);
            super.connect_phase(phase);
            m_prot_agent_a.mntr.ap.connect(m_prot_scb.imp_tlma);
            m_prot_agent_b.mntr.ap.connect(m_prot_scb.imp_tlmb);
        endfunction
    endclass

    class prot_seq extends uvm_sequence #(prot_item);
        `uvm_object_utils(prot_seq)
        function new (string name="");
            super.new(name);
        endfunction
        task body();
            `uvm_do(req)
        endtask

    endclass

    class prot_test extends uvm_test;
        virtual prot_if m_prot_if_a;
        virtual prot_if m_prot_if_b;
        prot_env m_prot_env;
        prot_seq m_prot_seq_a;
        prot_seq m_prot_seq_b;
        `uvm_component_utils(prot_test)
        function new (string name , uvm_component parent);
            super.new(name,parent);
        endfunction
        function void build_phase (uvm_phase phase);
            super.build_phase(phase);
            if (!uvm_config_db#(virtual prot_if)::get(this,"","m_prot_if_a",m_prot_if_a)) `uvm_fatal("PROT_IF_A","Failed")
            if (!uvm_config_db#(virtual prot_if)::get(this,"","m_prot_if_b",m_prot_if_b)) `uvm_fatal("PROT_IF_B","Failed")
            m_prot_env=prot_env::type_id::create("m_prot_env",this);
        endfunction
        function void connect_phase (uvm_phase phase);
            m_prot_env.m_prot_agent_a.drv.m_prot_if=m_prot_if_a;
            m_prot_env.m_prot_agent_a.mntr.m_prot_if=m_prot_if_a;
            m_prot_env.m_prot_agent_b.drv.m_prot_if=m_prot_if_b;
            m_prot_env.m_prot_agent_b.mntr.m_prot_if=m_prot_if_b;
        endfunction
        task run_phase (uvm_phase phase);
            phase.raise_objection(this);
            super.run_phase(phase);
            m_prot_seq_a=prot_seq::type_id::create("m_prot_seq_a",this);
            m_prot_seq_b=prot_seq::type_id::create("m_prot_seq_b",this);
            fork
                begin
                    #1ns;
                    m_prot_seq_a.start(m_prot_env.m_prot_agent_a.sqr);
                end
                begin
                    #1ns;
                    m_prot_seq_b.start(m_prot_env.m_prot_agent_b.sqr);
                end
            join
            #1ns;
            m_prot_seq_a.start(m_prot_env.m_prot_agent_a.sqr);
            #1ns;
            m_prot_seq_b.start(m_prot_env.m_prot_agent_b.sqr);
            #5ns;
            phase.drop_objection(this);
        endtask
    endclass
endpackage
module top;
    import uvm_pkg::*;
    `include "uvm_macros.svh"
    import rc::*;
    prot_if m_prot_if_a();
    prot_if m_prot_if_b();
    initial begin
        uvm_config_db #(virtual prot_if)::set(null,"uvm_test_top","m_prot_if_a",m_prot_if_a);
        uvm_config_db #(virtual prot_if)::set(null,"uvm_test_top","m_prot_if_b",m_prot_if_b);
        run_test("prot_test");
    end
endmodule

In reply to shimonc:

  1. Yes, the case statement evaluates items in the order they appear. If two items are true, only the first one executes
  2. Using $dumpvars you will not see any any changes in tlma_ev and tlmb_ev because they are 1 for less than one time unit.
  3. If you use a non-blocking assignment after existing the case statement, it won’t matter functionally, but you will go through the forever loop an extra time with tlma_ev and tlmb_ev being 0.