欢迎大家加入2023届数字IC交流群,QQ群号 628200294
Virtual Sequence是一个使用多个sequencer控制激励产生。由于sequence、sequencer和driver都专注于接口,几乎所有的测试平台都需要一个Virtual Sequence来协调不同interface之间的激励以及它们之间的交互。
Virtual Sequence可以通过两种方式来实现,推荐的方法是使用独立的Virtual Sequence,而“遗留”的替代方案Virtual Sequence则需要在virtual sequencer上运行。
virtual sequencer是指不连接到driver本身的sequencer,但包含测试平台层次结构中的sequencer的句柄。
“这一章节仅仅代表mentor的cookbook的意见,个人认为还是使用virtual sequencer比较好 ”
使用这种方法,virtual sequence需要通过virtual sequencer获取真实的sequencer句柄。virtual sequencer是UVM组件层次结构的一部分,因此它的子sequencer引用可以在连接阶段进行。
通常,将virtual sequencer插入到env级,并使用env的连接方法来分配子sequencer句柄。virtual_sequence通常run方法中创建,并在virtual sequencer上启动——即virtual_sequence.start(virtual_sequencer);
推荐给virtual_sequencer中的子sequencer提供一个关联接口的名称,这样更具有可读性。例如,主总线接口的sequencer可以被称为“bus_master”而不是“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
在创建可重用的env级环境时,测试平台不同层级的sequencer可以连接在一起,允许Virtual Sequencer或子Sequencer在任何层级上运行。为了实现这种级别的灵活性,子Sequencer句柄和virtual Sequencer句柄都需要在不同层级封装。
// 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
virtual sequence的运行是基于它们可以在代理内的子sequencer上运行的假设。但是,根据测试平台的构建过程,代理可能是passive或者active 的。为了防止测试用例出现空句柄错误,virtual sequence应该检查它们打算使用的所有sequencer是不是空句柄。如果检测出空句柄,那么应该通过`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