UVM(九)之sequence机制续2
1. 用事件做sequence之间的同步
到目前为止,sequence机制就是一个sequence启动之后对应一个sequencer,这个sequence发出transaction,sequencer把这个transaction转交给driver。
但是考虑这样一种情况,验证平台中有两个driver,这个连哥哥driver分别做不同的事情,第一个driver相当于一个CPU,它要在DUT刚启动的时候,配置DUT的寄存器,当它配置完成后,另外一个driver才能发激励。这种情况下在现实中是很常见的。一块DUT在上电复位后虽然其默认的参数就可以工作,但是大部分情况下,CPU还要对DUT做一些配置,这样才能DUT工作在我们期望的方式下。这个问题中,主要的就是一个同步的过程,一种很自然的想法就是把这个同步的过程使用一个event来完成:
之后,通过uvm_config_db的方式分别把两个sequence作为cpu_sequence和mac_sequence的default_sequence:
uvm_config_db#(uvm_objection_wrapper)::set(this,”env_sqr.main_phase”,”default_sequence”,cpu_seq::type_id::get());
uvm_config_db#(uvm_objection_wrapper)::set(this,”env_sqr.main_phase”,”default_sequence”,mac_seq::type_id::get());
当进入到main_phase时,这两个sequence会同步的启动,但是由于mac_seq要config_over事件的到来,所以它并不会马上产生transaction。而cpu_seq则会直接产生transaction,交给cpu_driver。当所有的配置工作完成后,config_over事件被触发,于是mac_seq开始产生transaction。
2. 复杂的同步:virtual sequence
上面的解决同步方法看起来非常的简单实用,不过这里有两个问题,第一个问题就是使用了一个全局的事件config_over。全局变量对于初写代码的人来说是非常受欢迎的,但是应该尽量避免使用全局变量的使用。
第二个问题是上面只是实现了一次同步,如果是有多次同步怎么办?如sequence A要先执行,之后是B,B完了才能是C,C完了才是D,这样显得很笨拙。
实现sequence之间同步的最好的方式就是使用virtual sequence。从字面上理解,虚拟的sequence。虚拟的意思就是它根本不发送transaction,它只是控制其他的sequence,起统一调度的作用。
如图所示,为了使用virtual sequence,一般的需要一个virtual sequencer。virtual sequencer里面包含指向其他实际sequencer的指针。
在test中,可以例化vsqr,并把相应的sequencer赋值给vsqr中的sequencer的指针。
3. 在sequence中慎用fork....join_none
假设DUT中有四个完全相同的MAC,那么相应的验证平台也要有4个完全相同的driver,sequencer。那么vsequencer就要这样定义:
当DUT上电复位,CPU把寄存器配置完成后,需要四个mac_driver同时发送数据,在vseq中可以使用fork来使四个driver同时发送数据:
这里使用了join_none,由于join_none的特性,系统并不等fork起来的进程结束就进入了下一次的fork循环,因此上面的for循环展开后如下:
现在我们要写一个新的sequence,他可以替代上面的两种包,可以这样写:
当sequence启动起来的时候,会自动执行这个sequence的body任务。当body任务完成的时候,那么这个sequence就相当于完成了其使命,已经结束了。如果使用fork...join_none,当使用uvm_do_on宏把四个mseq分别放在四个mac_sqr上执行时,系统会新启动4个进程,但是系统并不等待这4个mseq执行完毕就直接返回了。返回之后就到了endtask,此时系统认为这个sequence已经执行完成了。执行完成之后,系统会清理这个sequence之前占据的内存空间,杀死由其启动起来的进程,由于这4个启动起来的mseq还没有完成就直接被系统杀死掉了,也就是说,看似分别往4个mac_sqr分别丢了一个sequence,但是事实上这个sequence根本没有执行,这就是关键所在!
要避免这个问题可以使用wait fork或者fork....join
4. 在virtual sequence中控制验证平台的关闭
在sequence中使用starting_phase来控制验证平台的关闭。只有把此sequence作为sequencer的某动态运行phase的default_sequence时,其starting_phase才不为NULL。所以如果把某sequence作为uvm_do宏的参数,那么此sequence中的starting_phase是为null的。在此sequence中使用starting_phase.raise_objection是会产生问题。这个问题是比较容易解决的,只要把父sequence的starting_phase赋值给子sequence的starting_phase就可以了,这样只要最顶层的sequence的starting_phase不为null,那么下面所有由其启动的sequence的starting_phase也不为null。