路科验证的个人空间 https://blog.eetop.cn/1561828 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

UVM序列篇之五:sequencer和sequence(上)

已有 5390 次阅读| 2018-2-4 14:18 |个人分类:验证系统思想|系统分类:芯片设计

在之前的文章中,读者了解了sequencer与driver之间的传递sequence item的握手过程,同时也掌握了sequence与item之间的关系。接下来,我们需要就sequence挂载到sequencer上的常用方法做出总结,读者可以通过对这些常用方法和宏的介绍,了解到它们的不同的使用场景。另外,面对多个sequence如果需要同时挂载到sequencer时,那么就面临着仲裁的需要,uvm_sequencer自带有仲裁特性,结合着sequence的优先级设定,最终可以实现想要的效果。


常见sequence/item发送的方法和宏

对于UVM的初学者,往往我们给出的小白建议是,区别好方法start和宏`uvm_do就拿下了sequence发送和嵌套的半壁江山。然而,考虑到路粉们对自身技艺的高要求,我们这里就系统性地来阐述一些各种方法和宏之间的关系,以及讨论什么时候可以使用方法、什么时候可以使用宏。


对于已经习惯于sequence宏使用的用户而言,当他们再切回到sequence方法、或者调试这些方法时,会有一种不适感,路桑曾经也如此。但是想要对sequence发送做出更准确的控制,我们还应当正本清源,先熟悉sequence的方法。下面给出一段代码,这段代码中的top_seq嵌套了其它sequence和item,我们就使用到的几种方法做出说明。



输出结果:



在这段例码中,主要使用了两种方法:

uvm_sequence::start(uvm_sequencer_base sequencer, uvm_sequence_base parent_sequence = null,int this_priority = -1, bit call_pre_post = 1)

这个方法是针对将sequence挂载到sequencer上的应用。在使用该方法的过程中,用户首先应该指明sequencer的句柄。如果该sequence是顶部的sequence,即没有更上层的sequence嵌套它,则它可以省略对第二个参数parent_sequence的指定。第三个参数的默认值-1会使得该sequence如果有parent_sequence,即继承下来,如果是root sequence,则将其优先级设定为100。 第四个参数建议使用默认值,这样的话,uvm_sequence::pre_doby()和uvm_sequence::post_body()两个方法会在uvm_sequence::body()的前后执行。


在上面的例子中,child_seq被嵌套到top_seq中,继而在挂载时需要指定parent_sequence;而在test一层调用top_seq时,由于它是root sequence,则不需要再指定parent sequence,这一点用户需要注意。另外,在调用挂载sequence时,需要对这些sequence进行例化。


uvm_sequence::start_item (uvm_sequence_item item, int set_priority = -1, uvm_sequencer_base sequencer=null);

uvm_sequence::finish_item (uvm_sequence_item item, int set_priority = -1);

这一对方法是将item挂载到sequencer上的应用。对于start_item()的使用,第三个参数用户需要注意是否要将item挂载到非当前parent sequence挂载到的sequencer上面,有点绕口是吗?简单来说,如果你想将item和parent sequence挂载到不同的sequencer上面,你就需要指定这个参数。默认情况下,“父”与“子”都是走的一条道路(virtual sequence除外,我们会在后面《层次化》中讨论)。在使用这一对方法时,用户除了需要记得创建item,例如通过uvm_object::create()或者uvm_sequence::create_item(),还需要在它们之间完成item的随机化处理。从这一点建议来看,也需要读者了解到,对于一个item的完整传送,sequence要才sequencer一侧获得什么权限,也可以顺利将其发送至driver。我们可以通过拆解这三个步骤得到更多的细节:

  • 创建item

  • 通过start_item(),等待获得sequence的授权许可,其后执行parent sequence的方法pre_do()。

  • 对item进行随机化处理

  • 通过finish_item(),在对item进行了随机化处理之后,执行parent sequence的mid_do(),以及调用uvm_sequencer::send_request()和uvm_sequencer::wait_for_item_done()来将item发送至sequencer再完成与driver之间的握手。最后,执行了parent_sequence的post_do()。


这些完整的细节有两个部分需要注意。第一,sequence和item自身的优先级,可以决定什么时刻可以获得sequencer的授权;第二,读者需要意识到,parent sequence的虚方法pre_do()、mid_do()和post_do()会发生在发送item的过程中间。


如果对比start()方法和start_item()/finish_item(),读者首先要分清它们面向的挂载对象是不同的。此外,还需要清楚,在执行start()过程中,默认情况下,会执行sequence的pre_body()和post_body(),但是如果start()的参数call_pre_post = 0,那么就不会这样执行,所以在一些场景中的,UVM用户会奇怪为什pre_body()和post_body()没有被执行。在这里,用户需要注意,pre_body()和post_body()并不是一定会被执行的,这一点同UVM的phase顺序执行时有区别的。因此,一个建议是,用户可以在base sequence中自定义一些方法,确保它们会按照顺序执行,譬如下面这段例码。用户可以分别在user_pre_body()、user_post_body()和user_body()中填充代码,确保这些方法会被顺序执行,或者也可以考虑使用pre_start()和post_start()这两个预定义的方法。



下面是一段对这些执行过程的自然代码描述,读者可以看到它们执行的顺序关系和条件:



而对于pre_do()、mid_do()、post_do()而言,这是子一级的item/sequence在调用过程中,会间接调用parent sequence的pre_do()等方法。只要在参数传递过程中,确保child item/sequence与parent sequence的联系,那么这些执行过程是会按照上面的描述依次执行的。下面的也给出一段自然代码描述,来表示执行item时的相关方法执行顺序:



在熟悉了sequence/item的传送方法之后,我们就可以进一步来看看常见的宏有哪些,它们的主要作用是什么。下面给出了一张列表对这些宏做出总结:


对上面的这张表,我们还需给出几点说明:

  • 正是通过几个sequence/item宏打天下的方式,用户们可以通过`uvm_do/`uvm_do_with来发送无论是sequence还是item。这种不区分对象是sequence还是item的方式,带来了不少便捷,但也容易引起verifier们的惰性。所以在使用它们之前,需要先了解它们背后的sequence和item各自发送的方法。

  • 不同的宏,可能会包含创建对象也可能不创建对象。例如`uvm_do/`uvm_do_with会创建对象,而`uvm_send则不会创建对象,也不会对对象做随机处理。因此要了解它们各自包含的执行顺序。

  • 此外,其实还有其它的宏,读者们可以在UVM关于sequence的宏部分做深入了解。例如,将优先级作为参数传递的`uvm_do_pri/`uvm_do_on_prio等,还有专门针对sequence的`uvm_create_seq/`uvm_do_seq/`uvm_do_seq_with等宏。不过,我们在列表中给出的宏已经可以满足大多数的场景应用,而且整齐统一,便于用户的记忆和使用,所以,我们在这里不再对其它的一些宏做额外的说明。


下面的这段例码,我们将之前的例码中的方法对应到一些简单的宏上面,便于读者参照。




最后是路桑给出的关于发送sequence/item的几点建议:

  • 无论sequence处于什么层次,都应当让sequence最终在test结束前执行完毕。

  • 尽量避免使用fork-join_any或者fork-join_none来控制sequence的发送顺序。因为这背后隐藏的风险是,如果用户想终止在背后运行的sequence线程而简单使用disable的方式,那么就可能在不恰当的时间点上锁住sequencer。一旦sequencer被锁住,而又无法释放,接下来也就无法无法其它的sequence。所以,如果用户想实现类似fork-join_any或者fork-join_none的发送顺序,还应当在使用disable前,对各个sequence线程的后台保持关注,尽量在发送完item完成握手之后,再终止sequence, 这样才能避免一些问题。

  • 如果用户要使用fork-join的方式,那么应当确保有方法可以让sequence线程在满足一些条件后停止发送item。否则,只要有一个sequence线程无法停止,则整个fork-join无法退出。面对这种情况,仍然需要用户考虑监测合适的时间点,才能够使用disable来关闭线程。


在上面谈到的建议中,用户需要认识到,disable的手段对于这种需要严格完成握手的传送方式,是需要额外处理的。否则,轻易的停止sequence可能导致整个sequence与sequencer之间的传送瘫痪。


谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。



点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 253

    粉丝
  • 25

    好友
  • 33

    获赞
  • 45

    评论
  • 访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-26 15:26 , Processed in 0.018210 second(s), 12 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部