什么是UVM的sequence机制
- 控制并产生一系列事务,并将事务发送给driver的一套机制
- 是一个过程,需要消耗仿真时间,需要用在task phase当中
(1)控制何时产生、发送事务
(2)产生事务
(3)发送给driver
UVM sequence机制过程
- 仿真进入某一个task phase,比如run phase,与sequencer相连的sequence就会被启动
- 启动后的sequence会在内部产生一个事务对象并处于等待状态
- 当driver要驱动DUT时候,首先要从sequencer获得一个事务对象,driver向sequencer发送一个事务请求,sequencer收到这个请求会将这个请求转发给sequence
- sequence收到请求后会将已经产生好的事务对象发送出去,先发送给与自己相连的sequencer,再由sequencer转发给driver
- driver收到事务后,对此事务进行一系列处理,比如驱动DUT等
- driver处理好当前事务,会产生一个完成好的标志响应,先发送到sequencer,再转发给sequence
- sequence得知driver已经处理好当前事务,会产生下一个事务,并继续等待
以上过程,循环往复,直到所有的事物都产生完毕
为什么要使用UVM sequence机制
UVM sequence机制的原理
00 参与sequence机制主要有三个部分
01 sequencer中的实现
很复杂的sequencer实现,UVM已经全部给出,不需要细究
- 当进入某一个task phase,sequencer检查其内部的default_sequence(指针)是否被配置
- 如果被配置,就会实例化default_sequence对象
- 然后设置default_sequence的starting_phase(raise和drop objection)
- 随机化sequence
- 调用sequence的start( )方法来启动sequence
02 sequence中的实现:start( )方法
03 driver中的实现
一直进行:forever
- 当driver要驱动DUT时候,首先向sequencer发送事物请求,然后处于等待状态;
- sequence收到事务请求后,查看fifo中是否有已经产生好的事务,如果没有就等待sequence的产生,如果有,就将该事务发送给driver
- 当driver成功获取事务后,结束等待状态,接下来将该事务进行分解并且驱动DUT
- 完成以上操作后,driver向sequencer返回一个完成标志,通知该事务已经处理完毕,这个标志也会最终传入sequence
UVM的sequence机制常用的宏 'uvm_do_*宏系列
UVM sequence机制的启动
1 直接启动
sequence定义-实例化-启用start任务
my_sequence my_seq; //定义
my_seq = my_sequence::type_id::creat("my_seq"); //注册
my_seq.start(sequencer); //调用start函数启动
2 使用default_sequence启动
2.1 直接default_sequence
2.2 先实例化要启动的sequence,再通过default_sequence启动
- 可以在不同的地方通过default_sequence来启动sequence,比如在env中,第一个参数是this,第二个是相对于第一个参数的相对路径,路径需要指定到phase一级,比如main_phase,第三个和第四个参数纯属规定,照抄就行。
function void my_case::build_phase(uvm_phase phase);
case_sequence seq;
super.build_phase(phase);
seq = new("seq");
uvm_config_db(uvm_object_wrapper)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
seq);
3 'uvm_do启动
之后再说
此外,sequence启动后会发生什么呢?
会自动执行sequence的body任务,还会调用sequence的pre_body、post_body
UVM sequence的仲裁机制
引言:sequence可通过sequencer向drive发送你的待测case,如果一个sequencer只发送一个sequence是不需要仲裁的,但在实际使用中,如果一个sequencer接收了两个sequence或者更多的时候,会怎样发送各自sequence的case呢?
- 通过指定优先级决定仲裁
- 通过lock或者grap决定仲裁
- 通过is_relevant和对wait_for_relevant重载进行仲裁
- 通过virtual sequence和virtual sequencer进行复杂同步
01 指定sequence优先级
-
在实际使用中,我们可以通过uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)函数来设置仲裁模式。
-
这里的仲裁模式UVM_SEQ_ARB_TYPE有下面几种值可以选择
1.**UVM_SEQ_ARB_FIFO**: // 默认模式。来自于sequences的发送请求,按照FIFO先进先出的方式被依次授权,和优先级没有关系。
2.**UVM_SEQ_ARB_WEIGHTED**://不同sequence的发送请求,将按照它们的优先级权重随机授权。
3.**UVM_SEQ_ARB_RANDOM**: //不同的请求会被随机授权,而无视它们的抵达顺序和优先级。
//--若想使优先级起作用, 应该strict设置仲裁算法
4.**UVM_SEQ_ARB_STRICT_FIFO**: 不同的请求,会按照它们的优先级以及抵达顺序来依次授权,因此与优先级和抵达时间都有关。
5.**UVM_SEQ_ARB_STRICT_RANDOM**: 不同的请求,会按照它们的最高优先级随机授权,与抵达时间无关。
//---------不会用------------------------------------------------------------------
6.**UVM_SEQ_ARB_USER**: 用户可以自定义仲裁方法user_priority_arbitration()来裁定那个sequence的请求被优先授权。
m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
//设置sqr仲裁算法
transaction优先级设置
在sequence被启动是会自动执行它的body、pre_body和post_bosy三个任务,我们常用的是body任务。body任务中可以通过’uvm_do_pri及‘uvm_do_pri_with设置所产生的transaction的优先级。uvm_do_pri与uvm_do_pri_with的第二个参数是优先级,这个数值必须是大于等于-1的整数,数字越大,优先级越高。
PRIORITY:为item或者sequence指定优先级
'uvm_do_pri(SEQ_OR_ITEM,PRIORITY)
'uvm_do_pri_with(SEQ_OR_ITEM,PRIORITY,CONSTRAINTS)
'uvm_do_on_pri(SEQ_OR_ITEM,SEQR,PRIORITY)
以上结果:乱序sequence0 sequence1因为有很多仲裁算法
sequence优先级设置
start第1个参数:sequencer
start第2个参数:parent sequence
start第3个参数:优先级
task my_case::main_phase(uvm_phase phase);
env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO); //仲裁算法设置
fork //sequence优先级设置
seq0.start(env.i_agt.sqr, null, 200);
seq1.start(env.i_agt.sqr, null, 400);
join
endtask
总代码
如下面的这段代码指定了sequence0的优先级为100,sequence1的优先级为200,sequence1的优先级高于sequence0的优先级,同事设定了sequencer采用优先级仲裁机制,这样则会将sequence1的transcation都发送完成后再发送sequence0的transaction。对sequence设置优先级的本质即设置其内产生的transaction的优先级。
`ifndef MY_CASE0__SV
`define MY_CASE0__SV
class sequence0 extends uvm_sequence #(my_transaction);
my_transaction m_trans;
function new(string name= "sequence0");
super.new(name);
endfunction
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat (5) begin
`uvm_do_pri(m_trans, 100)
`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
`uvm_object_utils(sequence0)
endclass
class sequence1 extends uvm_sequence #(my_transaction);
my_transaction m_trans;
function new(string name= "sequence1");
super.new(name);
endfunction
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat (5) begin
`uvm_do_pri_with(m_trans, 200, {m_trans.pload.size < 500;})
`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
`uvm_object_utils(sequence1)
endclass
class my_case0 extends base_test;
function new(string name = "my_case0", uvm_component parent = null);
super.new(name,parent);
endfunction
`uvm_component_utils(my_case0)
extern virtual task main_phase(uvm_phase phase);
endclass
task my_case0::main_phase(uvm_phase phase);
sequence0 seq0;
sequence1 seq1;
seq0 = new("seq0");
seq0.starting_phase = phase;
seq1 = new("seq1");
seq1.starting_phase = phase;
env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);
fork
seq0.start(env.i_agt.sqr);
seq1.start(env.i_agt.sqr);
join
endtask
`endif