- sequence指的是
uvm_sequence
类,而item指的是uvm_sequence_item
类。 - 对于激励生成和场景控制,是由
sequence
来编织的,而对于激励需要的具体数据和控制要求,则是从item
的成员数据得到。
Sequence Item
- item是基于
uvm_object
类,这表明它具备UVM核心基类必要的数据操作方法,例如copy()、clone()、compare()、record()。 - item根据数据成员的类型划分为:
- 控制类。例如总线协议上的读写类型、数据长度、传送模式等。
- 负载类。一般指的是数据总线上的数据包。
- 配置类。用来控制driver的驱动行为,例如命令driver的发送间隔或有无错误插入。
- 调试类。用来标记一些额外信息方便调试,例如该对象的实例序号、创建时间、被driver解析的时间始末等。
Sequence Item示例
class bus_trans extends uvm_sequence_item;
rand 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
...
endclass
class 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直到被消化之后,它的生命一般才会结束。之所以要突出这一点是因为一些用户在使用中会不恰当地直接操作item对象,直接修改其数据,或将其句柄发送给其他组件使用,这无形中修改item的数据基因,或延长一个item对象的寿命,可取代的方式是合理利用copy()
和clone()
等数据方法。
Item和Sequence的关系
- 一个sequence可以包含一些有序组织起来的item实例,考虑到item在创建后需要被随机化,sequence在声明时也需要预留一些可供外部随机化的变量,这些随机变量一部分是用来通过层次传递约束来最终控制item对象的随机变量,一部分是用来对item对象之间加以组织和时序控制。
- 为了区分几种常见的sequence定义方式,将其分类为:
- 扁平类。这一类往往只用来组织更细小的粒度,即item实例构成的组织。
- 层次类。这一类是由更高层的sequence用来组织底层的sequence,进而让这些sequence按照顺序/并行方式,挂载到同一个sequencer上。
- 虚拟类。这一类是最终控制整个测试场景的方式,鉴于整个环境中往往存在不同种类的sequencer和其对应的sequence,因此需要一个虚拟的sequence协调顶层的测试场景。之所以称之为虚拟类是因为该序列并不会固定挂载于某种sequencer类型上,而是将其内部不同类型sequence最终挂载到不同的目标sequencer上(这是不同于hierarchical sequence的最大一点)。
Flat Sequence
- 一个
flat sequence
往往是由细小的sequence item群落构成,在此之上sequence还有更多的信息来完备它需要实现的激励场景。 - 一般对于flat sequence而言,它包含的信息:
- sequence item以及相关的constraint用来关联生成的item之间的关系,从而完善出一个flat sequence的时序形态。
- 除了限制sequence item的内容,各个item之间的时序信息也需要由flat sequence给定,例如何时生成下一个item并且发送至driver。
- 对于需要driver握手的情况(例如读操作),或等待monitor事件从而做出反应,都需要sequence在收到另一侧组件的状态后再决定下一步操作,即响应具体事件从而创建对应的item并发送出去。
Flat Sequence示例1
class 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;
`uvm_object_utils_begin(bus_trans)
`uvm_field_int ...
`uvm_object_utils_end
...
endclass
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)
...
task body(); //类比于组件的run
bus_trans tmp;
foreach(data[i]) begin
tmp = new();
tmp.randomize() with {
data == local::data[i];
addr == local::addr + i << 2;
write == local::write;
delay == local::delay;
};
tmp.print();
end
endtask
endclass
class 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
输出结果:
- 暂时没有使用sequence的宏或其它发送item的宏实现sequence/item与sequencer之间的传送,而是用更直白的方式来描述这种层次关系。
flat_seq
类可以看作是一个更长的数据包,数据包的具体内容、长度、地址等信息都包含在其中。生成item过程中通过将自身随机变量作为constraint内容限定item随机变量,这是flat sequence的大致处理方法。- 例码没有给出如
uvm_do/uvm_do_with/uvm_create
等宏是为了首先认清sequence与item之间的关系。因此该例也只给出在flat_seq::body()
任务中创建和随机化item,而省略了发送item。 - 实际上bus_trans理应容纳更多时序内容,而不应该只作为一次数据传输。作为数据传送的最小粒度,用户们有权将它们扩展到更大的数据和时间范围,从而间接减小数据通信和处理的成本,提高整体运行效率。
Flat Sequence示例2
class 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
...
endclass
class 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
endclass
class 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
输出结果:
- 可以将一段完整发生在数据传输中更长的数据都“收编"在bus_trans类中,提高这个item粒度的抽象层次,让它变得更有“气质”。而一旦拥有了更成熟的、更合适切割的item,上层的flat sequence在使用过程中就会更顺手。
- flat_seq类不再操本不应该的心去考虑数据内容,而只应考虑数据包的长度、地址等信息,因为扩充随机数据的责任一般由item负责就足够了,使用flat_seq的用户无需考虑多余的数据约束。
Hierarchical Sequence
hierarchical sequence
和flat sequence
的区别在于它可以使用其他sequence还有item,这么做是为了创建更丰富的激励场景。- 通过层次嵌套关系,可以让hierarchical sequence使用其它hierarchical sequence、flat sequence和sequence item,这就意味着如果底层的sequence item和flat sequence的粒度得当,就可以充分复用这些flat sequence和sequence构成形式更多样的hierarchical sequence。
Hierarchical Sequence示例
class hier_seq extends uvm_sequence;
`uvm_object_utils(hier_seq)
function new(string name = "hier_seq");
super.new(name);
endfunction
task body();
bus_trans t1, t2;
flat_seq s1, s2;
`uvm_do_with(t1, {length == 2;})
fork
`uvm_do_with(s1, {length == 5;})
`uvm_do_with(s2, {length == 8;})
join
`uvm_do_with(t2, {length == 3;})
endtask
endclass
- 从
hier_seq::body()
来看它包含有bus_trans t1,t2
和flat_seq s1,s2
,而它的层次关系就体现在对各个sequence/item的协调上面。例码使用了uvm_do_with
宏,这个宏完成了三个步骤:- sequence或item的创建;
- sequence或item的随机化;
- sequence或item的传送。
- 区别于之前的例码,这个例码通过uvm_do_with帮助理解,所谓的sequence复用就是通过高层的sequence来嵌套底层的sequence/iten,最后创建期望的场景。
- 在示例中既有串行的激励关系,也有并行的激励关系,而在更复杂的场景中,用户还可以考虑加入事件同步,或者一定的延迟关系来构成sequence/item之间的时序关系。