一、sequence的启动与执行
- 启动方法一:sequence定义后,使用start任务启动:
sequence seq;
seq = sequence::type_id::create("seq");
seq.start(sequencer);
- 启动方法二:使用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);
- sequence启动后或自动执行sequence的body任务,除此之外还会自动调用sequence的pre_body和post_body:
class case_sequence extends uvm_sequence #(transaction);
virtual task pre_body();
endtask
virtual task post_body();
endtask
virtual task body();
endtask
`uvm_object_utils(case_sequence);
endclass
三个函数的执行顺序是:pre_body() -> body() -> post_body()
二、sequencer的仲裁机制
2.1 item/sequence的优先级
-
uvm_sequencer类自建了仲裁机制用来保证多个sequence在同时挂载到sequencer时,可以按照仲裁规则允许特定sequence中的item优先通过。
-
在实际使用中,我们可以通过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**: 不同的请求会被随机授权,而无视它们的抵达顺序和优先级。
4.**UVM_SEQ_ARB_STRICT_FIFO**: 不同的请求,会按照它们的优先级以及抵达顺序来依次授权,因此与优先级和抵达时间都有关。
5.**UVM_SEQ_ARB_STRICT_RANDOM**: 不同的请求,会按照它们的最高优先级随机授权,与抵达时间无关。
6.**UVM_SEQ_ARB_USER**: 用户可以自定义仲裁方法user_priority_arbitration()来裁定那个sequence的请求被优先授权。
在上面的仲裁模式中,与priority有关的模式有UVM_SEQ_ARB_WEIGHTED、UVM_SEQ_ARB_STRICT_FIFO和UVM_SEQ_ARB_STRICT_RANDOM。
m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
//设置sqr仲裁算法
-
这三种模式的区别在于,UVM_SEQ_ARB_WEIGHTED的授权可能会落到各个优先级sequence的请求上面,而UVM_SEQ_ARB_STRICT_RANDOM则只会将授权随机安排到最高优先级的请求上面,UVM_SEQ_ARB_STRICT_FIFO则不会随机授权,而是严格按照优先级以及抵达顺序来依次授权。
-
没有特别的要求,用户不需要再额外自定义授权机制,因此使用UVM_SEQ_ARB_USER这一模式的情况不多见,其他模式可以满足绝大多数的仲裁需求。
-
transaction优先级设置:当使用uvm_do或者uvm_do_with宏时,产生的transaction的优先级是默认的优先级,即-1,可以通过uvm_do_pri及uvm_do_pri_with设置所产生的transaction的优先级。uvm_do_pri与uvm_do_pri_with的第二个参数是优先级,这个数值必须是大于等于-1的整数,数字越大,优先级越高。
-
sequence优先级设置:
task my_case::main_phase(uvm_phase phase);
env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);
fork
seq0.start(env.i_agt.sqr, null, 200);
seq1.start(env.i_agt.sqr, null, 400);
join
endtask
2.2 sequencer的锁定机制
有的sequence还会要求一旦它拿到sequencer的控制权 ,就要把它里面的transaction全部传输出去,中间不允许被打断,这时就要使用锁定操作来解决
lock()与unlock()
这一对方法可以为sequence提供排外的访问权限,但前提条件是,该sequence首先需要按照sequencer的仲裁机制获得授权。而一旦sequence获得授权,则无需担心权限被收回,只有该sequence主动解锁(unlock)它的sequencer,才可以释放这一锁定的权限。lock()是一种阻塞任务,只有获得了权限,它才会返回。
class sequence extends uvm_sequence #(transaction);
virtual task body();
lock();
repeat(3) begin
`uvm_do_with(my_trans);
end
unlock();
endtask
endclass
grab与ungrab()
也可以为sequence提供排外的访问权限,而且它只需要在sequencer下一次授权周期时就可以无条件地获得授权。与lock方法相比,grab方法无视同一时刻内发起传送请求的其他sequence,而唯一可以阻值它的只有已经预先获得授权的其他lock或者grab的sequence。- 这里需要注意的是,由于"解铃还须系铃人",如果sequence使用了lock()或者grab()方法,必须再sequence结束前调用unlock()或者ungrab()方法来释放权限,否则sequencer会进入死锁状态而无法继续为其余sequence授权。
class sequence extends uvm_sequence #(transaction);
virtual task body();
grab();
repeat(3) begin
`uvm_do_with(my_trans);
end
ungrab();
endtask
endclass
2.3 sequence的有效性
除了独占sequencer的控制权外,也可以通过重载is_relevant函数,放弃sequencer的控制权,即让特定的sequence不参与sequencer的仲裁。返回值如果为1,说明sequence有效,否则无效
class ssequence extends uvm_sequsence #(transaction);
transaction m_trans;
int num;
virtual function bit is_relevant();
if(delay_en == 1) return 0;
else return 1;
endfunction
virtual task wait_for_relevant();
#1000;
delay_en = 0;
endtask
virtual task body();
fork
repeat(10) begin
`uvm_do(m_trans);
num++;
if(num == 6) delay_en = 1;
end
join
endtask
endclass
三、sequence相关宏
四、virtual sequencer
4.1 何时使用virtual sequencer/sequence
如果只有一个激励驱动的 agent,则不需要virtual sequencer。如果你有多个驱动agent,但不需要激励之间的协调(在设计上是相互独立的),则不需要virtual sequencer。如果您有多个驱动 agent并且需要激励间的协调,则需要一个virtual sequencer。如果测试平台将来需要扩展成有多个agent,或者多个独立激励的测试平台需要设计成相互协调的激励,那么环境就要更新到包括一个或多个virtual sequencer。
- virtual sequence:承载不同目标sequencer的sequence群落,实现sequence同步;
virtual sequence一般只会挂载到virtual sequencer上,且没有自己的sequence_item,只用于控制其他的sequence执行顺序,起统一调度作用。
- virtual sequencer:桥接其它sequencer,即
连接所有底层sequencer的句柄(指针)
,是一个中心化的路由器。virtual sequencer本身并不传送item数据对象,因此不需要与driver进行TLM连接。所以用户需在顶层的connect阶段做好virtual sequencer中各个sequencer句柄与sequencer实体对象的一一连接,避免句柄悬空。
4.2 实现virtual sequencer
- 在virtual sequencer中定义指向其他真实sequencer句柄指针;
- 在virtual sequence中定义sequence句柄,在body( )任务中控制这些序列;
- 通过宏`uvm_declare_p_sequencer( )来绑定virtual sequencer同时声明p_sequencer,并操作virtual sequencer中的内容;
- 通过宏`uvm_do_on( )将虚序列器句柄与虚序列句柄相连接;
- 注意区别:virtual_sequence中嵌套的sequence和普通sequence中嵌套sequence。普通sequence中嵌套的sequence都在同一个sequencer上启动(通过sequencer仲裁决定);而在virtual sequence中嵌套的sequence可以在不同sequencer实体上同时启动。
class virtual_sequencer extends uvm_sequencer;
bfm_sequencer bfm0_sqr; //1.插入虚序列器句柄,指向真实sequencer
bfm_sequencer bfm1_sqr;
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
endclass
class virtual_sequence extends uvm_sequence;
`uvm_object_utils(virtual_sequence)
`uvm_declare_p_sequencer(virtual_sequencer) //1.声明p_sequencer,操作virtual sequencer中的内容
bfm_sequence bfm0_seq; //2.插入序列句柄
bfm_sequence bfm1_seq; //序列seq可由不同的seqr启动
virtual task body();
`uvm_do_on(bfm0_seq,p_sequencer.bfm0_sqr); //3. 将虚序列器句柄与虚序列句柄相连接
`uvm_do_on(bfm1_seq,p_sequencer.bfm1_sqr);
endfunction
endclass
4.2.1 关于p_sequencer的说明:
sequence类里有一个uvm_sequencer_base类型m_sequencer指针
,当sequence和sequencer关联后,m_sequencer会自动指向该sequencer,但通过m_sequencer不能直接使用seqr里的变量,否则会出现编译错误。只能使用cast强制向子类转换后,才能通过m_sequencer.xxx来访问该seqr内的xxx变量。
UVM引入p_sequencer,可以自动的实现上面所述的cast动作
,从而可以在sequence中自由使用关联sequencer内的变量。
p_sequencer并不是UVM自动地在sequence的创建的,需要用户使用uvm_declare_p_sequencer宏声明
,之后UVM会自动实现指向seqr及cast的动作。
4.3 在环境/测试用例中连接sequencer到virtual sequencer
- 在build_phase阶段创建并例化virtual sequencer;将不同agt场景下的sqr发送的default sequence设置为null,关闭相应的sequence;
- 在connect_phase阶段,将被控制的sequencers与virtual sequencer关联一起;
- `在env或test中配置virtual_sequence为default sequence,在需要的phase阶段执行virtual sequence;
class top_env extends uvm_env;
virtual_sequencer v_sqr; //and subenvs
...
virtual funvtion void build_phase(uvm_phase phase);
super.build_phase(phase);
...
v_sqr = virtual_sequencer::type_id::create("v_sqr",this);
uvm_config_db#(uvm_object_wrapper)::set(this,"subenv0.bfm0_agt.sqr.main_phase","default_sequence",null);
uvm_config_db#(uvm_object_wrapper)::set(this,"subenv0.bfm1_agt.sqr.main_phase","default_sequence",null);
uvm_config_db#(uvm_object_wrapper)::set(this,"v_sqr.main_phase","default_sequence",virtual_sequence::get_type());
endfunction
virtual function void connect_phase(uvm_phase phase);
v.sqr.bfm0_sqr = bfm_agt0.bfm0_sqr; //将被控制的真实sequencers赋给virtual sequencer的指针,建立连接
v.sqr.bfm1_sqr = bfm_agt1.bfm1_sqr;
endfunction
endclass
五、sequence library
参考文章:
UVM sequencer的仲裁机制
UVM中的sequence
UVM——虚序列器与虚序列(virtual sequencer与virtual sequence)