Multiple lock() in parallel sequences is broken in uvm-1.2?

Hi All,

We were exploring lock/grab feature of uvm_sequencer on UVM-1.2 using below code:

class instruction extends uvm_sequence_item;
  typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_t;
  rand inst_t inst;

  `uvm_object_utils_begin(instruction)
    `uvm_field_enum(inst_t,inst, UVM_ALL_ON)
  `uvm_object_utils_end

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

class instruction_sequencer extends uvm_sequencer #(instruction);

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

  `uvm_sequencer_utils(instruction_sequencer)
endclass


class seq_a extends uvm_sequence #(instruction);
  instruction req;
  function new(string name="seq_a");
    super.new(name);
  endfunction

  `uvm_sequence_utils(seq_a, instruction_sequencer)
  virtual task body();
      repeat(2) begin
         `uvm_do_with(req, { inst == PUSH_A; });
      end
  endtask
endclass

class seq_b extends uvm_sequence #(instruction);
  instruction req;

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

  `uvm_sequence_utils(seq_b, instruction_sequencer)
  virtual task body();
      lock();
      repeat(2) begin
         `uvm_do_with(req, { inst == PUSH_B; });
      end
      unlock();
  endtask
endclass

class seq_c extends uvm_sequence #(instruction);
  instruction req;

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

  `uvm_sequence_utils(seq_c, instruction_sequencer)
  virtual task body();
    lock();
      repeat(2) begin
         `uvm_do_with(req, { inst == POP_C; });
      end
    unlock();
  endtask
endclass

class parallel_sequence extends uvm_sequence #(instruction);
  seq_a s_a;
  seq_b s_b;
  seq_c s_c;
  instruction_sequencer m_sqr;

  function new(string name="parallel_sequence");
    super.new(name);
    s_a = seq_a :: type_id :: create("s_a");
    s_b = seq_b :: type_id :: create("s_b");
    s_c = seq_c :: type_id :: create("s_c");
  endfunction

  `uvm_sequence_utils(parallel_sequence, instruction_sequencer)
  virtual task body();
      fork
        s_a.start(m_sqr);
        s_b.start(m_sqr);
        s_c.start(m_sqr);
      join
  endtask
endclass

class instruction_driver extends uvm_driver #(instruction);

  // Provide implementations of virtual methods such as get_type_name and create
  `uvm_component_utils(instruction_driver)

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

  task run_phase (uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      $display("0: Driving Instruction  ",$time,req.inst.name());
      #10;
      seq_item_port.item_done();
    end
  endtask
endclass

class test extends uvm_test;
  instruction_sequencer sequencer;
  instruction_driver driver;
  parallel_sequence par_seq;
  
  `uvm_component_utils(test)  
  function new (string name = "", uvm_component parent=null);
    super.new(name, parent);
  endfunction: new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    sequencer = instruction_sequencer::type_id::create("sequencer", this);
    driver   = instruction_driver::type_id::create("driver", this);
  endfunction: build_phase
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    driver.seq_item_port.connect(sequencer.seq_item_export);
  endfunction: connect_phase
  
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    par_seq = parallel_sequence::type_id::create("par_seq");
    par_seq.m_sqr = sequencer;
    phase.raise_objection(this);
    par_seq.start(sequencer);
    phase.drop_objection(this);
  endtask: run_phase  
endclass: test

module tb();
  initial 
    begin
      run_test("test");
    end
endmodule: tb

Here, our expected output was:


 0 PUSH_A
 10 PUSH_B
 20 PUSH_B
 30 POP_C
 40 POP_C
 50 PUSH_A

But we’re getting below output:

 0 PUSH_A

Here it is displaying only first fetch from the driver, rest all requests are getting stuck in sequencer queue and that’s the reason we’re getting timeout error from UVM. So is this behavior expected?

Thanks and Regards,
Mitesh Patel

In reply to mitesh.patel:

Hi Mitesh,

I have encountered a similar problem. Were you able to resolve it?

Thanks,
Prasad

In reply to PrasadHaldule:

Hi,

It’s known and reported to the UVM panel.
Please find the mantis ID/URL for more details.
URL : https://accellera.mantishub.io/my_view_page.php
Mantis ID : 0007052

Regards,
Mitesh Patel

Hi,

This happens because of seq start with normal one, if your sequence order is b->a->c in fork then we don’t get any collision or any other order but start with grep or lock sequence, this happens because of logic in side uvm_sequencer_base.svh, in side uvm_sequencer_base when uvm call m_select_sequence() this function call m_wait_for_available_sequence() this lead to call
m_wait_arb_not_equal() this block all simulation and hit 92s of uvm, this function m_lock_arb_size value need to be update but that’s not updated, this variable update when m_unlock_req, m_lock_req, remove_sequence_from_queues, grant_queued_locks, m_select_sequence, wait_for_grantany one of this call , in this case this value has to be update from grant_queued_locks, grant_queued_locks create lock_list this lock_list use by is_blocked function to block seq from executing when your pattern start with normal seq at that time lock list fill with all other seq, so when ever is_blocked is call from m_choose_next_request and m_wait_for_available_sequence always return -1 and 1, and list not able to update and stuck at m_wait_arb_not_equal, for batter understanding you have to read uvm_sequencer_base code.