为了使激励的产生,发送和驱动分工更加的明确,分为了driver, sequencer和sequnce。而uvm中的sequence机制使得激励的产生和约束变得更加灵活。sequence机制是一种与sequencer,driver交互的过程,概括为:
1,通过sequence发送层次化、随机化的激励。
2,sequencer对sequence进行仲裁。
3,driver接受sequencer传递来的sequence(req)并按照时序激励发送到DUT,而后driver等待从端口返回信号和状态。
4,dirver给sequencer发送反馈信息(rsp)。
sequence的生命周期到此结束,完成一个完整的握手。
如果把sequence_item比作乘客的话,sequence类比为电梯调度中心的调度员,它决定了哪些乘客(sequence_item)进入电梯(driver),sequencer可以比作大楼的调度中心,它控制着哪些事务(transactions)何时被发送到driver。
UVM squence是一个参数化的类,属于uvm_object,不属于uvm树形结构中,它在验证平台中是有生命周期。
图3.14是sequence机制交互流程:
图3.14 sequence机制交互流程图
1,sequence中的流程为:
(1)产生sequence item(create_item)。
(2)sequence_item的随机化。
(3)指定sequencer发送这个sequence(start_item)。
(4)sequence_item送到REQ_FIFO(finish_item),如果有response,得到response(get_response(rsp)) 。
2,sequencer的流程为:
(1)对sequence进行仲裁。
3,driver的流程为:
(1)得到sequence_item(get_next_item(req))。
(2)发送给DUT。
(3)产生response。
这里会涉及到两个概念:sequence的执行和sequence的启动。
执行:sequence的执行是指sequence在sequencer中启动后,执行pre_start、body、post_body等函数,主要涉及到数据的生成和激励的发送。
启动:sequence的启动是指通过手动启动方式和自动启动的方式来激活和使用sequence,从而实现对硬件功能的准确、高效的测试。
一、 sequence中的执行
sequence的执行必须在task body中执行,task body是在task phase中自动调用的。
sequence的发送可以分为uvm_sequence_item类型的发送和uvm_sequence类型的发送。
1, sequence_item的发送
如果sequence发送的是uvm_sequence_item类型,那么发送的方法有以下几种:
(1)start_item 和finish_item
virtual task start_item(uvm_sequence_item item, int set_priority=-1,uvm_sequence_base sequence=null);
start_item()有三个参数,第一个是传入的sequence_item, 第二个是优先级,第三个是指定该sequence_item发送给哪一个sequencer。
这个task的作用可以理解为让数据包和sequencer建立联系。
virtual task finish_item(uvm_sequence_item item, int set_priority=-1);
finish_item()有二个参数,第一个是传入的sequence_item, 第二个是优先级。
这个task的作用可以理解为把数据包sequence_item发送给driver。
在实际的运用中的流程为:
1)实例化item 。
2)start_item 。
3)对item随机化。
4)结束finish_item。
5)如果需要rsp ,等到rsp返回后结束流程。
part1/sequence是对UVM sequence机制的实验, dadd_rand_test中对data_sequence_item进行随机,在dadd_rand_sequence的task body代码为:
cpp
File:dadd_sequence.sv
Class:dadd_rand_sequence
`ifdef START_ITEM
task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(20)
begin
item = new("item");
start_item(item);
item.randomize();
finish_item(item);
end
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : body
代码3.10 sequence机制中通过start_item和finish_item对sequence item进行发送
执行Makefile脚本:
make send_item_start_item
(2)`uvm_create(item),`uvm_send(item)
uvm还提供了`uvm_create或者`uvm_create_on,用于生成transaction的实例,然后结合`uvm_send宏将transaction发送出去。
`uvm_create为实例化,就是我们常用的new函数,还有`uvm_create_on(item,sqr)宏,这个宏可以指定发送的sequencer。
`uvm_send是把数据包sequence_item发送给driver,这个define实际就是封装了start_item和finish_item。
在实际的运用中的流程为:
1)`uvm_create(item) 。
2)对item随机化。
3)`uvm_send(item)。
4)如果需要rsp ,等到rsp返回后结束流程。
cpp
File:dadd_sequence.sv
Class:dadd_rand_sequence
`elsif UVM_CREATE
task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(20)
begin
`uvm_create(item);
item.randomize();
`uvm_send(item);
end
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : body
代码3.11 sequence机制中通过uvm_create和uvm_send对sequence item进行发送
执行Makefile脚本:
make send_item_uvm_create
(3)`uvm_do系列宏
`uvm_do系列宏是最常用的方法,用于发送sequence_item或者是sequence,
相关的宏为:
`uvm_do(item_or_seq)
只有一个参数,可以是sequence_item或者sequence
`uvm_do_with(item_or_seq, {cons})
有两个参数,一个是传输的item或者seq,第二个参数为item或者seq的约束
`uvm_do_pri(item_or_seq, priority)
两个参数,一个是传输的item或者seq,第二个参数为item或者seq的优先级
`uvm_do_pri_with(item_or_seq, pri, {cons})
三个参数,一个是传输的item或者seq,第二个参数是item/seq的优先级,第三个参数是约束
`uvm_do_on(item_or_seq, sqr)
有两个参数,第二个参数为sqr的指针
`uvm_do_on_with(item_or_seq, sqr, {cons})
有三个参数,第二个为sqr,第三个为约束
`uvm_do_on_pri(item_or_seq, sqr, pri)
三个参数,第三个指定了优先级
`uvm_do_on_pri_with(item_or_seq, sqr, pri, {cons})
四个参数,第三个为优先级,第四个为约束
如果发送的是sequence_item 那么`uvm_do系列宏可以理解为封装了uvm_create,start_item,finish_item 。在实际的运用中的流程为:
1)`uvm_do(item)。
2)如果需要rsp ,等到rsp返回后结束流程。
cpp
File:dadd_sequence.sv
Class:dadd_rand_sequence
`else//UVM_DO
task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(20)
begin
`uvm_do(item)
end
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : body
代码3.12 sequence机制中通过uvm_do对sequence item进行发送
执行Makefile脚本:
make send_item_uvm_do
2, sequence的发送
sequence中可以嵌套sequence,如果sequence发送的是子sequence,那么发送的方法为:
(1)task start函数
调用start函数启动。
virtual task start (uvm_sequencer_base sequencer, uvm_sequence_base parent_sequence = null,int this_priority = -1,bit call_pre_post = 1);
在实际的运用中的流程为:
1)sequence的实例化。
2)sequence随机化约束。
3)seq.start()。
在dadd的测试平台中dadd_en是随机,如果想让dadd_en约束为1,及连续的发送数据,就可以对dadd_rand_sequence进行约束。
在dadd_rand_sequence中声明一个random的变量data_en_rand,然后通过`uvm_do_on_with(item,sqr,{const})对sequence_item中的data_en进行约束。
代码为:
cpp
File:dadd_sequence.sv
Class:dadd_rand_sequence
class dadd_rand_sequence extends uvm_sequence;
`uvm_object_utils(dadd_rand_sequence)
`uvm_declare_p_sequencer(dadd_sequencer)
dadd_item item;
rand bit data_en_rand;
function new(string name = "dadd_rand_sequence");
super.new(name);
endfunction : new
`ifdef SEND_SEQ
task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(20)
begin
`uvm_do_on_with(item,p_sequencer,{
item.data_en== data_en_rand;
})
end
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : body
代码3.13 sequence机制dadd_rand_sequence的代码
在dadd_fixen_sequence中实例化了dadd_rand_sequence并对data_en_rand约束为1。
cpp
File:dadd_sequence.sv
Class:dadd_fixen_sequence
`ifdef SEND_SEQ
`ifdef START
task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
seq = dadd_rand_sequence :: type_id ::create("seq");
seq.randomize() with {data_en_rand == 1;};
seq.start(p_sequencer);
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : body
代码3.14 sequence机制通过start发送sequence的代码
执行Makefile脚本:
make send_seq_start
(2)`uvm_create,`uvm_send
`uvm_create实际就是sequence的实例化,`uvm_send封装了start函数。流程为:
1)`uvm_create(seq) 。
2)sequence随机化约束。
3)`uvm_send(seq)。
在dadd_fixen_sequence中的代码为:
cpp
File:dadd_sequence.sv
Class:dadd_fixen_sequence
`elsif UVM_CREATE
task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
`uvm_create(seq)
seq.randomize() with {data_en_rand == 1;};
`uvm_send(seq)
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : body
代码3.15 sequence机制通过uvm_create和uvm_send发送sequence的代码
执行Makefile脚本:
make send_seq_uvm_create
(3)`uvm_do系列宏
同上`uvm_do宏,如果发送的sequence那么`uvm_do系列宏可以理解为封装了`uvm_create, tast start()。
在实际的运用中的流程为:
1)`uvm_do_with(item,{const})
在dadd_fixen_sequence中的代码为:
cpp
File:dadd_sequence.sv
Class:dadd_fixen_sequence
`else//UVM_DO
task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
`uvm_do_with(seq,{data_en_rand == 1;})
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : body
代码3.16 sequence机制通过uvm_do发送sequence的代码
执行Makefile脚本:
make send_seq_uvm_do
二、 sequence的启动
sequence的启动有两种方式一种是手动启动,另一种是自动启动。
1,手动启动start:通过例化sequence后挂载到sequencer上。
my_seq seq = ny_seq:type_id::create("seq");
seq.start(sequencer);
代码为:
cpp
File:dadd_test.sv
Class:dadd_rand_test
task dadd_rand_test :: main_phase(uvm_phase phase);
super.main_phase(phase);
`ifndef DEFAULT_SEQUENCE
seq.starting_phase = phase;
seq.start(env.iagt.sqr);
`endif//SET_DEFAULT_SEQUENCE
endtask: main_phase
代码3.17 sequence机制通过start手动启动sequence的代码
执行Makefile脚本:
make rand
2,自动启动:通过default_sequence启动,通过调用config_db。
uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",case_sequnce::type_id::get())
用到的是config_db的方法,在set中第一个参数是启动这个sequence的phase在UVM树形结构的路径,第二个为标识符,uvm规定为"default_sequence",第三个为这个sequence的实例化句柄。
cpp
File:dadd_test.sv
Class:dadd_rand_test
function void dadd_rand_test :: build_phase(uvm_phase phase);
seq = dadd_rand_sequence :: type_id :: create("seq");
env = dadd_environment :: type_id :: create("env",this);
`ifdef DEFAULT_SEQUENCE
uvm_config_db #(uvm_object_wrapper) :: set(this,"env.iagt.sqr.main_phase","default_sequence",dadd_rand_sequence::type_id::get());
`endif//SET_DEFAULT_SEQUENCE
endfunction : build_phase
代码3.18 sequence机制通过自动启动sequence的代码
执行Makefile脚本:
make default_seq_rand
本书(《UVM实验教程-从平台、脚本到方法学全代码解析-王建利》)及其实验代码已上传至GitHub 访问网址为: https://github.com/brentwang-lab/uvm_tb_gen