UVM Sequencer的仲裁特性介绍
uvm_sequencer类自建了仲裁机制用来保证多个sequence在同时挂载到sequencer时,可以按照仲裁规则允许特定sequence中的item优先通过。
介绍
在实际使用中,可以通过uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)函数来设置仲裁模式,这里的仲裁模式UVM_SEQARB_TYPE有下面几种值可以选择:
- UVM_SEQ_ARB_FIFO:默认模式。来自于sequences的发送请求,按照FIFO先进先出的方式被依次授权,和优先级没有关系。
- UVM_SEQ_ARB_WEIGHTED:不同sequence的发送请求,将按照它们的优先级权重随机授权。
- UVM_SEQ_ARB_RANDOM:不同的请求会被随机授权,而无视它们的抵达顺序和优先级。
- UVM_SEQ_ARB_STRICT_FIFO:不同的请求,会按照它们的优先级以及抵达顺序来依次授权,因此与优先级和抵达时间都有关。
- UVM_SEQ_ARB_STRICT_RANDOM:不同的请求,会按照它们的最高优先级随机授权,与抵达时间无关。
- UVM_SEQ_ARB_USER:用户可以自定义仲裁方法user_priority_arbitration()来裁定哪个sequence的请求被优先授权。
Sequencer的仲裁示例
class bus_trans extends uvm_sequence_item;
rand int data;
...
endclass
class child_seq extends uvm_sequence;
rand int base;
...
task body();
bus_trans req;
repeat(2)`uvm_do_with(req,{data inside {[base:base+9]};})
endtask
endclass
class top_seq extends uvm_sequence;
...
task body();
child_seq seq1, seq2, seq3;
m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
fork
`uvm_do_pri_with(seq1,500,{base==10;})
`uvm_do_pri_with(seq2,500,{base==20;})
`uvm_do_pri_with(seq3,300,{base==30;})
join
endtask
endclass
class sequencer extends uvm_sequencer;
...
endclass
uvm_sequencer提供了两种锁定机制,分别通过lock()和grab()方法实现,这两种方法的区别在于:
1、lock()与unlock()这一对方法可以为sequence提供排外的访问权限,但前提条件是,该sequence首先需要按照sequencer的仲裁机制获得授权。而一旦sequence获得授权,则无需担心权限被收回,只有该sequence主动解锁(unlock)它的sequencer,才可以释放这一锁定的权限。lock()是一种阻塞任务,只有获得了权限,它才会返回。
2、grab()与ungrab()也可以为sequence提供排外的访问权限,而且它只需要在sequencer下一次授权周期时就可以无条件地获得授权。与lock方法相比,grab方法无视同一时刻内发起传送请求的其它sequence,而唯一可以阻止它的只有已经预先获得授权的其它lock或者grab的sequence。
3、需要注意的是,由于“解铃还须系铃人”,如果sequence使用了lock()或者grab()方法,必须在sequence结束前调用unlock()或者ungrab()方法来释放权限,否则sequencer会进入死锁状态而无法继续为其余sequence授权。
class bus_trans extends uvm_sequence_item;
...
endclass
class child_seq extends uvm_sequence;
...
endclass
class lock_seq extends uvm_sequence;
...
task body();
bus_trans req;
#10ns;
m_sequencer.lock(this);
`uvm_info("LOCK","get execlusive access by lock()", UVM_LOW)
repeat(3)#10ns `uvm_do_with(req,{data inside {[100:110]};})
m_sequencer.unlock(this);
endtask
endclass
class grab_seq extends uvm_sequence;
...
task body();
bus_trans req;
#20ns;
m_sequencer.grab(this);
`uvm info("GRAB","get exclusive access by grab()", UVM_LOW)
repeat(3)#10ns `uvm_do_with(req,{ data inside {[200:210]};})
m_sequencer.ungrab(this);
endtask
endclass
class top_seq extends uvm_secquence;
...
task body();
child_seq seql, seq2, seq3;
lock_seq locks;
grab_seq grabs;
m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
fork
`uvm_do_pri_with(seq1,500,{ base==10;})
`uvm_do_pri_with(seq2,500,{ base==20;})
`uvm_do_pri_with(seq3,300,{ base==30;})
`uvm_do_pri(locks,300)
`uvm_do(grabs)
join
endtask
endclass
输出结果为:
结果总结
对于sequence locks,在10ns时它跟其它几个sequence一同向sequencer发起请求,按照仲裁模式,sequencer先后授权给seq1、seq2、seq3,最后才授权给locks。
而locks在获得授权之后,就可以一查享有权限而无需担心权限被sequencer收回,locks结束前,用户需要通过unlock()方法返还权限。
对于sequence grabs,尽管他在20ns时就发起了请求权限(实际上seq1、seq2、seq3也在同一时刻发起了权限请求),而由于权限已经被locks占用,所以它也无权收回权限。
因此只有当locks在40ns结束时,grabs才可以在sequencer没有被锁定的状态下获得权限,而grabs在此条件下获取权限是无视同一时刻发起请求的其它sequence的。
同样地,grabs结束前,也应当通过ungrab()方法释放权限,防止sequencer的死锁行为。
grab在大家都在等待的时候可以无条件拿到条件;
这篇笔记参考《UVM实战》、《芯片验证漫游指南》和某验证视频整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。