uvm验证环境里一般通过objection机制来控制仿真的结束,不过在机制之外,有时还需要通过看门狗来watchdog避免仿真环境挂死,watchdog配合objection一起来控制仿真的进行与结束。
我一直自诩为对环境watchdog这件事烂熟于心了,不过没想到这天还是被伤害到了。
事故背景
一个中规中矩的watchdog是怎么组织的呢?要明确一下watchdog发挥的作用,就是在objection的基础上进行补充,在环境长时间没有动静的情况下能够使环境报错推出并打印此时阻止仿真结束的罪魁祸首。
基于这个认识,watchdog应该是base_test的run_phase()中进行调用,这样既从时间全程参与又从空间上统揽全局。当然了,因为环境的主要行为集中在main_phase()中,所以把watchdog放在main_phase()中我觉得也是可以的。
watchdog上下的objection还是很有必要的,毕竟你要保证watchdog无论在哪里调用都可以执行起来,别这个phase没有objection就直接略过了。
watchdog内部逻辑就是几个并行的线程,简单来说可以这样写:
代码的主体就是一个大的while(1)循环,循环内以fork - join_any的形式起多个喂狗线程,根据fork - join_any的机制,只要任何一个线程完成了都会触发喂狗机制。
*线程1:正常结束的线程,因为本身watchdog占着一个raise_objection,所以只要等待wait_for_total_count(null, 1)就可以了,为1说明其他的objection都已经drop了,那么就可以正常结束程序,和uvm本身的objection机制完全一样;
*线程2:超时线程,如果很长的时间里都没有喂狗,那么报fatal推出仿真。注意这里必须是fatal使方正立即结束,报error的话环境还是会挂死状态;
*线程3:所有的scoreboard都可以喂狗,因为scb里比对的一方是可以信任的环境预期,如果比对还在进行那么就说明仿真不应该结束;
*线程N:可以喂狗的其他线程,使用rtl线程需要万分谨慎,很有可能rtl里做错了一致重复出数据导致仿真无法结束;
当喂狗一次后,就可以杀掉timeout这个线程了,然后根据情况看看是否重新回到看门狗循环中。
事故现场
看门狗的核心起始就是,确定仿真在“动”,能动就是还活着不能结束仿真,所以在fork-join_any里除了超时线程以外,其他的都是证明系统还活着的“喂狗”线程。这些线程里如果使用rtl的信号作为系统还活着的参照,一定要万分的小心,万分的小心,万分的小心。
第一点小心是该停止但是停不下来,取材自上个月的bug。场景很简单,#100 @harness.dut.hand_en这个线程里hand_en做错了,进入了无限发包无限握手的死循环,带着环境也一直停不下来看门狗直接失效了。
第二点小心是该仿真但是挺下来了,这个事我以前就没想过能出现。事故现场是这样的还是#100 @harness.dut.hand_en这个线程(就是这么头铁,出过错了还继续用),这次确实是RTL正常的发包握手,但是,性能模式下外部没有反压拍拍握手成功,hand_en起来之后就没见到下降沿!这就导致了什么问题呢,导致@harness.dut.hand_en线程根本就触发不了!这就涉及到@和wait的区别了,@捕捉的是event trigger是信号的跳变,harness.dut.hand_en恒1不跳导致看门狗直接超时了。
简直目瞪口呆,只要每天比别人多碰到3个bug,两年能积累别人五年经验。
事故解决
我把@harness.dut.hand_en改成wait harness.dut.hand_en了
全部0条评论
快来发表一下你的评论吧 !