[UVM COOKBOOK]Sequences||Virtual Sequencers

Welcome to the 2023 Digital IC exchange group, QQ group No. 628200294

Virtual sequencers (not recommended)

A Virtual Sequence is one that uses multiple sequencers to control excitation generation. Because sequence, sequencer and driver focus on interfaces, almost all test platforms need a Virtual Sequence to coordinate the incentives and interactions between different interfaces.

Virtual Sequence can be implemented in two ways. The recommended method is to use an independent Virtual Sequence, while the "legacy" alternative Virtual Sequence needs to be run on the Virtual Sequence.

virtual sequencer refers to a sequencer that is not connected to the driver itself, but contains a handle to the sequencer in the test platform hierarchy.

"This chapter only represents the opinions of mentor's cookbook. Personally, I think it's better to use virtual sequencer "

virtual sequence runs on the virtual sequencer

Using this method, the virtual sequence needs to obtain the real sequencer handle through the virtual sequencer. The virtual sequencer is part of the UVM component hierarchy, so its sub sequencer reference can be made in the connection phase.

Usually, the virtual sequencer is inserted into the env level and the connection method of Env is used to allocate the sub sequencer handle. The virtual_sequence is usually created in the run method and started on the virtual sequencer -- that is, virtual_sequence.start(virtual_sequencer);

It is recommended to provide a name of the associated interface to the sub sequencer in the virtual_sequencer, which is more readable. For example, the sequencer of the main bus interface can be called "bus_master" instead of "master_axi_sequencer".

// Virtual sequencer class:
class virtual_sequencer extends uvm_sequencer #(uvm_sequence_item);
 `uvm_component_utils(virtual_sequencer)
// Note that the handles are in terms that the test writer understands
 bus_master_sequencer bus; 
 gpio_sequencer gpio;
    function new(string name = "virtual_sequencer", uvm_component parent = null);
  super.new(name, parent);
    endfunction
endclass: virtual_sequencer
class env extends uvm_env;
// Relevant parts of the env which combines the
// virtual_sequencer and the bus and gpio agents
//
// Build:
 function void build_phase( uvm_phase phase );
  m_bus_agent = bus_master_agent::type_id::create("m_bus_agent", this);
        m_gpio_agent = gpio_agent::type_id::create("m_gpio_agent", this); 
        m_v_sqr = virtual_sequencer::type_id::create("m_v_sqr", this);
 endfunction: build_phase
// Connect - where the virtual_sequencer is hooked up:
// Note that these references are constant in the context of this env
 function void connect_phase( uvm_phase phase ); 
       m_v_sqr.bus = m_bus_agent.m_sequencer; 
        m_v_sqr.gpio = m_gpio_agent.m_sequencer;
 endfunction: connect_phase
endclass:env
// Virtual sequence base class:
//
class virtual_sequence_base extends uvm_sequence #(uvm_sequence_item);
 `uvm_object_utils(virtual_sequence_base)
// This is needed to get to the sub-sequencers in the
// m_sequencer
 virtual_sequencer v_sqr;
// Local sub-sequencer handles 
 bus_master_sequencer bus; 
    gpio_sequencer gpio;
 function new(string name = "virtual_sequence_base");
  super.new(name); 
 endfunction
// Assign pointers to the sub-sequences in the base body method:
 task body();
  if(!$cast(v_sqr, m_sequencer)) begin
            `uvm_error(get_full_name(), "Virtual sequencer pointer cast failed");
  end
  bus = v_sqr.bus; 
        gpio = v_sqr.gpio;
 endtask: body
endclass: virtual_sequence_base
// Virtual sequence class:
//
class example_virtual_seq extends virtual_sequence_base;
 random_bus_seq bus_seq; 
 random_gpio_chunk_seq gpio_seq;
 `uvm_object_utils(example_virtual_seq)
 function new(string name = "example_virtual_seq");
  super.new(name); 
 endfunction
 task body();
  super.body; // Sets up the sub-sequencer pointers
  gpio_seq = random_gpio_chunk_seq::type_id::create("gpio_seq"); 
        bus_seq = random_bus_seq::type_id::create("bus_seq");
  repeat(20) begin 
   bus_seq.start(bus); 
   gpio_seq.start(gpio);
  end 
    endtask: body
endclass: example_virtual_seq
// Inside the test class:
//
task run;
 example_virtual_sequence test_seq = 
 example_virtual_sequencer::type_id::create("test_seq");
//...
 test_seq.start(m_env.m_v_sqr);
//...
endtask: run

Modify the Virtual Sequencer for vertical multiplexing

When creating a reusable env level environment, sequencers at different levels of the test platform can be connected together, allowing virtual sequencer or sub sequencer to run at any level. In order to achieve this level of flexibility, sub sequencer handles and virtual sequencer handles need to be encapsulated at different levels.

// Virtual sequencer from the UART env
class uart_env_virtual_sqr extends uvm_sequencer #(uvm_sequence_item);
// ..
 uart_sequencer uart; 
 bus_sequencer bus;
// ..
endclass: uart_env_virtual_sqr
// Virtual sequencer from the GPIO env
class gpio_env_virtual_sqr extends uvm_sequencer #(uvm_sequence_item);
// ..
 gpio_sequencer gpio; 
 bus_sequencer bus;
// ..
endclass: gpio_env_virtual_sqr
// Virtual sequencer from the SoC env
class soc_env_virtual_sqr extends uvm_sequencer #(uvm_sequence_item);
//..
// Low level sequencers to support virtual sequences running across
// TB hierarchical boundaries 
 uart_sequencer uart; 
 bus_sequencer uart_bus; 
 gpio_sequencer gpio; 
 bus_sequencer gpio_bus;
// Virtual sequencers to support existing virtual sequences
//
 uart_env_virtual_sqr uart_v_sqr; 
 gpio_env_virtual_sqr gpio_v_sqr;
// Low level sequencer pointer assignment:
// This has to be after connect because these connections are
// one down in the hierarchy
 function void end_of_elaboration(); 
  uart = uart_v_sqr.uart;
  uart_bus = uart_v_sqr.bus; 
  gpio = gpio_v_sqr.gpio; 
  gpio_bus = gpio_v_sqr.bus;
 endfunction: end_of_elaboration
endclass: soc_env_virtual_sqr

Coding guide: the virtual sequencer needs to check whether it is an empty handle before use

The running of virtual sequences is based on the assumption that they can run on sub sequencers within the agent. However, according to the construction process of the test platform, the agent may be passive or active. In order to prevent empty handle errors in test cases, virtual sequences should check whether all sequencers they intend to use are empty handles. If empty handles are detected, they should The test case ends with ` uvm_fatal.

// Either inside the virtual sequence base class or in
// an extension of it that will use specific sequencers:
task body();
 if(!$cast(v_sqr, m_sequencer)) begin
        `uvm_error(get_full_name(), "Virtual sequencer pointer cast failed")
 end
 if(v_sqr.gpio == null) begin
  `uvm_fatal(get_full_name(), "GPIO sub-sequencer null pointer: this test case will fail, check config or virtual sequence")
 end
 else begin
  gpio = v_sqr.gpio;
 end
 if(v_sqr.gpio_bus == null) begin
  `uvm_fatal(get_full_name(), "BUS sub-sequencer null pointer: this test case will fail, check config or virtual sequence")
    end
 else begin
  gpio_bus = v_sqr.gpio_bus;
 end 
endtask: body

END

Posted by jimmyt1988 on Thu, 25 Nov 2021 19:23:06 -0800