sequence
指的是uvm_sequence
类,而item
指的是uvm_sequence_item
类。sequence
来编织的,而对于激励所需要的具体数据和控制要求,则是从item
的成员数据得到的。item
是基于uvm_object
类,这表明了它具备UVM核心基类所必要的数据操作方法,例如copy()
、clone()
、compare()
、record()
。item
根据数据成员的类型,将划分为:
driver
的驱动行为,例如命令driver
的发送间隔或者有无错误插入。driver
解析的时间始末等。class bus_trans extends uvm_sequence_item; //定义一个transactionrand bit write;rand int data;rand int addr;rand int delay;static int id_num;`uvm_object_utils_begin(bus_trans)`uvm_field_int ...`uvm_object_utils_end...
endclassclass test1 extends uvm_test;`uvm_component_utils(test1)...task run_phase(uvm_phase phase);bus_trans t1, t2;phase.raise_objection(phase);# 100ns;t1 = new("t1");t1.print();#200ns;t2 = new("t2");void'(t2.randomize());t2.print();phase.drop_objection(phase);endtask
endclass
输出结果:
item
使用时的特点:
rand
类型,同时按照驱动协议给出合适的constraint
。item
本身的数据属性,为了充分利用UVM域声明的特性,将必要的数据成员都通过'uvm_field_XXX
宏来声明,方便使用基本数据方法自动实现。t1
没有被随机化而t2
被随机化了,这种差别在item
通往sequencer
之前是很明显的。UVM要求item
的创建和随机化都应该发生在sequence
的body()
任务中,而不是在sequencer
或者driver
中。item
对象的生命周期来区分,它的生命应该开始于sequence
的body()
方法,而后经历了随机化并穿越sequencer
最终到达driver
,直到被driver
消化之后,它的生命一般来讲才会结束。一个sequence可以包含一些有序组织起来的item实例,考虑到item在创建后需要被随机化,sequence在声明时也需要预留一些可供外部随机化的变量,这些随机变量一部分是用来通过层次传递约束来最终控制item对象的随机变量,一部分是用来对item对象之间加以组织和时序控制的。
为了区分几种常见的
sequence
定义方式,将其分类为:
- 扁平类。这一类往往只用来组织更细小的粒度,即
item
实例构成的组织。- 层次类。这一类是由更高层的
sequence
用来组织底层的sequence
,进而让这些sequence
按照顺序方法,或者按照并行方式,挂载到同一个sequencer
上 。- 虚拟类。这一类则是最终控制整个测试场景的方式,鉴于整个环境中往往存在不同种类的sequencer和其对应的sequence,因此需要一个虚拟的sequence来协调顶层的测试场景。之所以称这个方式为虚拟类,是因为该序列本身并不会固定挂载于某一种sequencer类型上,而是将其内部不同类型sequence最终挂载到不同的目标sequencer上。
一个flat sequence
往往是由细小的sequence item
群落构成,在此之上sequence
还有更多的信息来完备它需要实现的激励场景。
一般对于flat sequence
而言,它包含的信息:
sequence item
以及相关的constraint
用来关联生成的item
之间的关系,从而完善出一个flat sequence
的时序形态。sequence item
的内容,各个item
之间的时序信息也需要由flat sequence
给定,例如何时生成下一个item
并且发送至driver
。driver
握手的情况(例如读操作),或者等待monitor
事件从而做出反应(例如slave
的memory response
数据响应操作),都需要sequence
在收到另外一侧组件的状态后,再决定下一步操作,即响应具体事件从而创建对应的item
并且发送出去。flat sequence
示例1 class flat_seq extends uvm_sequence;rand int length;rand int addr;rand int data[];rand bit write;rand int delay;constraint cstr{data.size() == length;foreach(data[i]) soft data[i] == i;soft addr == 'h100;soft write == 1;delay inside {[1:5]};};`uvm_object_utils(flat_seq)... //省略掉了new函数task body(); //sequence主要要做的事情就是body,一旦将sequence挂在到sequencer上面,接下来要做的事情就是bodybus_trans tmp; //声明句柄foreach(data[i]) begintmp = new(); //例化了很多对象,并对每一个对象就行随机tmp.randomize() with {data == local::data[i];addr == local::addr + i << 2;write == local::write;delay == local::delay;};tmp.print();endendtask
endclassclass test1 extends uvm_test;`uvm_component_utils(test1)...task run_phase(uvm_phase phase); flat_seq seq;phase.raise_objection(phase);seq = new();seq.randomize() with {addr == 'h200; length == 100};seq.body();phase.drop_objection(phase);endtask
endclass
输出结果:
flat sequence
示例2class bus_trans extends uvm_sequence_item; //下沉rand bit write;rand int data[]; //颗粒度变大,可以传输更多的数据rand int length;rand int addr;rand int delay;static int id_num;constraint cstr{data.size() == length;foreach(data[i]) soft data[i] == i;soft addr == 'h100;soft write == 1;delay inside {[1:5]};};`uvm_object_utils_begin(bus_trans)`uvm_field_int ...`uvm_object_utils_end...
endclassclass flat_seq extends uvm_sequence;rand int addr;rand int length;`uvm_object_utils(flat_seq)...task body();bus_trans tmp;tmp.new();tmp.randomize() with {length == local::length;addr == local::addr;};tmp.print();endtask
endclassclass test1 extends uvm_test;`uvm_component_utils(test1)...task run_phase(uvm_phase phase); flat_seq seq;phase.raise_objection(phase);seq = new();seq.randomize() with {addr == 'h200; length == 3};seq.body();phase.drop_objection(phase);endtask
endclass
输出结果:
这个示例将一段完整发生在数据传输中的、更长的数据都“收编”在一个bug_trans类中,提高这个item粒度的抽象层次,使得item更成熟、更适合切割。这样flat sequence更倾向于控制,不用去关注数据内容,而只关注这个数据包的长度、地址等信息即可,扩充随机数据的责任一般由item负责。
hierarchical sequence
区别于flat sequence
的地方在于,它可以使用其他sequence
,还有item
,这么做是为了创建更丰富的激励场景。hierarchical sequence
示例class hier_seq extends uvm_sequence;`uvm_object_utils(hier_seq)function new(string name = "hier_seq");super.new(name);endfunctiontask body();bus_trans t1, t2;flat_seq s1, s2;`uvm_do_with(t1, {length == 2;}) //宏的作用:1、创建 2、随机 3、传送到sequencerfork`uvm_do_with(s1, {length == 5;})`uvm_do_with(s2, {length == 8;})join`uvm_do_with(t2, {length == 3;})endtask
endclass