UVM Hierarchical Sequence
VIP中通常给颗粒度最小的task,需要自己继承于based_sequence,写颗粒度最小的sequence,然后,复杂的激励场景top_seq中body中进行颗粒度最小的sequence的声明及调用。
处理复杂度高的事务的方法是把其模块化并将其添加到对象管理进程中。有以下好处:
- 代码复用
- 易于维护
- 容易debug
- 易于控制复杂场景生成
结合编码规则理解:Top-level sequence 被分解为子sequence,子的sequence又可以被分解为更基础的sequence。
由一简单协议引起的思考:
例子: amba ahb协议中,有两个基础操作write和read。AHB_BASE_WRITE, AHB_BASE_READ是颗粒度最小的sequence。基于这两个最小颗粒度的sequence,更高一层级的sequence中可能含有n次 AHB_BASE_WRITE或n次AHB_BASE_READ,分别称为:AHB_LOOP_WRITE和AHB_LOOP_READ。Top_level sequence WRITE_FOLLOWED_BY_READ中含有x个AHB_LOOP_READ及m个AHB_LOOP_WRITE,且有一定时序。
root_sequence & sub_sequence
- 大多数时候,top_seq充当root_seq。某些情况下,一个sequence可能启动另一个sequence作为root_seq。所以,按此划分:有且只有一个root_seq和一些sub_seq。两者区别是:sub_seq有父seq,而root_seq没有父seq。
- 含有父seq的sequence往往会影响测试seq处理正在运行的seq的方式。例如,如果当前运行的序列是含有sub_seq的直接父seq或远父seq,则sequencer需要接受sub_Seq中的item。
- sequencer 默认总是先获取root_seq。
- root_seq启动自己的执行线程,而sub_seq通常在父seq的执行线程中启动。但这并非充分条件。比如:
a. 假设序列S1以root_seq的形式在其执行线程中启动另一个序列S2,则序列S2将不会授权予序列S1(例如,与seqr的交互目前被S1捕获),必须等待S2完成后才能继续。
b. 另一方面,如果序列S1在其执行线程中启动了两个并行子序列S2 & S3(通过使用fork…join语句),那么子序列S2 & S3将获得父序列S1的授权。(整个过程的完成采用raise & drop objections)。
example
// Transaction Item /
class packet extends uvm_sequence_item;
`uvm_object_utils(packet)
// Constructor
function new (string name);
super.new(name);
endfunction: new
// Packet properties
rand byte payload;
endclass: packet
/// Sequencer Declaration /
typedef uvm_sequencer #(packet) my_sequencer;
// Flat Sub-Sequence Declaration ///
class flat_sub_seq extends uvm_sequence #(packet);
`uvm_object_utils(packet)
// Sub-Sequence Properties
rand byte payload;
rand int count;
constraint c {payload < 10;}
// Constructor
function new (string name);
super.new(name);
endfunction: new
// Body Task
virtual task body();
packet pkt;
`uvm_do_with(pkt, {payload == default_payload;})
repeat (count)
`uvm_do(pkt)
endtask: body
endclass: flat_sub_seq
// Hierarchical Top-level Sequence //
class hier_top_seq extends uvm_sequence #(packet);
`uvm_object_utils(hier_top_seq)
// Constructor
function new (string name);
super.new(name);
endfunction: new
// Body Task
virtual task body();
packet pkt;
flat_sub_seq fss;
`uvm_do(pkt)
`uvm_do(fss)
`uvm_do_with(fss, {payload == 6 && count == 5;})
endtask: body
endclass: hier_top_seq
Test To Start Hierarchical Top-level Sequence
class test_hier_seq extends uvm_test;
`uvm_component_utils(test_hier_seq)
// Constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction: new
// Build Function (Build Lower Level Components)
function void build_phase (uvm_phase phase);
super.build_phase(phase);
..
..
endfunction: build_phase
// Run Task
virtual task run_phase(uvm_phase phase);
// Create Top-level Sequence
hier_top_seq hts;
hts = hier_top_seq::type_id::create("hts", this);
phase.raise_objection(this, "Starting hier_top_seq as Root Sequence");
hts.start(env.agnt.sqnr);
phase.drop_objection(this, "Droping hier_top_seq as Root Sequence");
endtask: run_phase
endclass: test_hier_seq