| ||
C. 数据串行组织
对于复杂环境的调试,小伙伴们是怎么做的?路桑指的是,如果有多个UVC在发送、监测不同种类的事务,那么你是如何记录和分析他们的?一些仿真器有较灵活的监测方式,例如Questasim的事务记录功能就不错,还有VCS的SmartLog也能提供一些帮助。不过还是比不上自己定制化的来得贴心好用,而Sokorac分享的一种在原有验证环境基础上可以直接扩展的,只需添加少量代码的解决方案,可以将仿真时间窗口中,任何类型的事务都统一到一个调试方案中,例如统一的TLM事件定义:
而对于项目中新定义的事务,只需要在以往继承于uvm_sequence_item的事务,附带“implements arm_event”,实现其预定义的方法即可完成统一的事件格式,继而在事务创建时(new()函数),可利用中心化的arm_event_recorder完成统一的、串行的仿真数据组织,继而将数据格式高度统一化、串行化,便于后期的数据跟踪调试,也使得数据的来源、目的地、数据之间的联系一目了然。
那么如果是已有的环境,已经定义过的sequence item的旧环境,还有救吗?当然!可以在原有代码(如果允许修改的话),添加如上的代码即可;如果是商业VIP库,那么我们可以考虑采取定义子类,再由顶层覆盖(override)的方式,使得原有环境中所有的sequence item可以最终取得一致的数据类型,并且在创建时可以自动完成数据记录工作。
D. 对象化计时方式
最后这个话题更加有趣,在验证环境中,我们经常会采用@clk的方式来增加延迟,然而这种方式使得代码修改时,非常容易影响随机数生成器(RNG, Random Number Generator)的稳定性。关于随机发生器稳定性的讨论,可以阅读路桑这篇文章:
为了提高随机的稳定性,可以将所有的时钟等待方法都交由中心化的计时器对象处理。这里中心化的计时对象类型为clocking_center,它也采取轮询的方式为每一个需要时钟的对象进行“授时”。而对于以前需要独立等待时钟的各个组件,例如interface_timeout_monitor类,则可以一开始进行授时需求登记(connect阶段),而在接口类clockable的计时方法中tock()实现中,进行计时的逻辑实现。由此完成了中心化的授时工作,而分散的组件则只需要实现每次计时的逻辑,不再需要出现“@clk”的语句。简单来看,更新后的组件则不再需要任何时钟等待,这也促进了随机过程的稳定性。
总结
这篇论文介绍的四种方式都非常有帮助,可谓让人大开眼界。
广播-收听的设计模式可以利用接口类进一步提高灵活性,不过值得探讨的是,UVM的哲学是重视结构稳定性的,即TLM端口都是为了完成组件之间的通信,而在设计时并没有为sequence与uvm_component通信预留方法。如果广泛地引入接口类实现任何对象之间的通信,那需要考虑代码的一致性,而不是各种混杂使用容易给代码维护造成负担。另外,路桑还是认为UVM新手不应该太自由太任性,需要UVM套路的管束。待其理解UVM哲学后,使用SV的接口类,则可以协助其完成平时只有依靠uvm_event或者其它方式才能完成的对象通信。
多继承的模式也能减轻代码的体积,简化类的继承层次。由此,verifier可在类定义时进一步抽象,哪些行为可能是介于多种不同类的共同行为,而考虑将其抽象为接口类。数据的串行结构化可以在已有环境上完成数据格式的统一化,便于后期的数据统计分析和调试跟踪。
这篇文章值得多看多回顾,在将来如果遇到上述的一些困难,希望你脑海里还能闪过一个概念——“接口类(interface class)”而不是“接口(interface)”。
谢谢你对路科验证的关注,你的支持是我们保持前行的动力。