UVM消息打印机制打印的时间为啥跟波形时间不一样
扫描二维码
随时随地手机看文章
硅芯思见,主理人nano,主要从事芯片验证工作,平时会在公众号分享一些在芯片设计、验证的学习过程中遇到的各种问题,以及一些个人的想法。
前面讨论过$time和$realtime显示时间时,log信息中显示的时间会跟仿真波形不一致,有一些差异(详见《硅心思见:【157】SystemVerilog中的time的一些事儿》)。有些朋友在使用UVM构建测试平台时调用`uvm_info时发现波形中信号变化的时间和`uvm_info显示的时间不一致(本文以`uvm_info为例说明),并且使用UVM-1.1和UVM-1.2居然`uvm_info显示的时间还不一样,这到底是怎么回事儿呢?下面本文将通过追究下到底是什么原因导致的,并且给出自定义消息格式的一些方法。
【示例】
【仿真结果】 使用UVM-1.1进行仿真
【仿真结果】 使用UVM-1.2进行仿真
通过上面两个示例结果我们发现UVM-1.2和UVM-1.1的结果相差还是比较大的,并且UVM-1.2仿真波形结果与log信息差别还是比较大的,这是为什么呢?下面一块挖掘下这到底是为啥?
【使用UVM-1.1进行仿真现象分析】
首先我们知道`uvm_info实际上都来自于下图中这个宏,其中实现打印的实际上是uvm_report_info这个function,所以需要关心下uvm_report_info怎么实现的。
uvm_report_info这个function定义在uvm_globals.svh中,如下图。
在这个方法中,可以看到uvm_report_info的主要实现是通过uvm_root这个类中的uvm_report_info这个方法实现的,下面我们继续查找uvm_root这个类,在uvm_root中并没有找到这个方法的实现,所以继续查找uvm_root的父类uvm_component,很遗憾,uvm_component里头也么有uvm_report_info这个方法的实现,那么这个哥们去哪里呢?uvm_component还有一个父类是uvm_report_object,在uvm_report_object.svh中定义的uvm_report_object类中找到了uvm_report_object这个方法的定义,如下图。
从上面代码中可以看到uvm_report_info中通过m_rh调用了report()这个方法,这个方法来自于句柄m_rh的类型,在uvm_report_object类中m_rh的类型从上述代码中可以看到是uvm_report_handler,所以report()这个方法至少应该来自于uvm_report_handler中,下面是在uvm_report_handler这个类找到的report方法的定义,如下图。
在report()方法中,又通过srvr这个句柄调用了report方法,此时的这个report方法至少应该在srvr这个句柄的类型uvm_report_server这个类中,uvm_report_sever这个类中果然有report这个方法的定义,如下图。
在report这个方法中实际上实现输出消息的是通过process_report这个方法,但是在这个方法中并没有关于时间显示的信息,关于时间信息实际上是来自于compose_message这个方法,如上图。在compose_message这个方法中实现了当前仿真时间的获取(如图中红色阴影),可以看到这个时间是通过$realtime获取的,而$realtime获取的时间是一个实数,但是在显示的时间受$timeformat设置的默认值的影响,即此时显示的时间单位是该语句执行时所在域的时间精度,所以此时我们看到\`uvm_info调用时在log中显示的时间是一个五位数,与波形上期望的小数有一些差异,如果期望对这个数据显示格式进行约束,可以在顶层调用\`uvm_info宏之前通过$timeformat进行设置。
【使用UVM-1.2进行仿真现象分析】
在UVM-1.2中关于`uvm_info的分析在uvm_message_defines.svh、uvm_globals.svh、uvm_root.svh、uvm_component.svh和UVM-1.1基本是一样的,没有太大变化,但是在uvm_report_object.svh中有一些差异。在uvm_report_object中,uvm_report_info方法的定义如下。
在uvm_report_object中,uvm_report_info方法调用了uvm_report这个方法,uvm_report这个方法中实现信息处理的是uvm_process_report_message这个方法,此方法中实现信息处理的实际上是通过其中的m_rh.process_report_message实现的,所以m_rh调用的这个方法应该在m_rh这个句柄的类型定义中,而m_rh的类型为uvm_report_handler,所以关于消息打印的追踪进入到uvm_report_handler中,在uvm_report_handler中process_report_message的定义如下图所示。
在uvm_report_handler中process_report_message的定义中,实际上对于信息处理的来自于srver.process_report_message,而srver这个句柄的类型为uvm_report_server,所以process_report_message至少来自于uvm_report_server,打开uvm_report_server.svh着实被惊喜到了,因为在uvm_report_server中process_report_message是个纯虚方法,其定义如下。
那么process_report_message要实现功能,必须在uvm_report_server的一个子类中实现,在UVM-1.2中这个子类就是uvm_default_report_server,在uvm_default_report_server中process_report_message的定义如下。
这个方法中,处理信息的实际上是compose_report_message,这个方法虽然是通过svr(uvm_report_server)调用的,所以此时的compose_report_message()其实也来自于uvm_report_server的子类中,其定义如下。
在compose_report_message这个方法中实现了当前仿真时间的获取(如图中红色阴影),可以看到这个时间是通过$time(注意:这里与UVM-1.1不同)获取的,而$time获取的时间是一个整型数据,并且此时显示的时间也受$timeformat设置的默认值的影响,即此时显示的时间单位是该语句执行时所在域的时间精度,所以此时我们看到\`uvm_info调用时在log中显示的时间是一个五位数并且只保留了对应时间单位的整数部分数字,显示的时间就与波形上期望的小数存在了较大的差异,此时虽然可以通过$timeformat进行设置,但是也不会获得时间精度对应的具体小数部分的数字了。
那么,既然库里提供的默认显示时间与用户期望的不一致,有没有办法可以解决呢?其实只要注意是上述最终实现消息字符串的实际上都是虚方法,那么就可以通过派生子类重写该方法,然后将子类句柄指向的对象传递给实现消息机制的父类句柄,从而就可以实现对于消息机制的重定义了。下面我们通过一个示例来说明如何自定义消息格式,同样的分别针对UVM-1.1和UVM-1.2进行示例说明如何定制消息格式。
【UVM-1.1消息格式定制】
第一步:定义usr_report_server(类名根据需要自定义)类,因为UVM中消息最终实现都是在uvm_report_server中,所以usr_report_server也拓展自uvm_report_server。
第二步:在用户拓展自uvm_test的测试类的build_phase中,声明一个第一步定义的usr_report_server类的句柄usr_svr,并且在build_phase中将其通过new函数创建对象;
第三步:在build_phase中将指向创建好对象的句柄usr_svr传递给uvm_report_server::set_server(usr_svr),代码如下例。
第四步:运行仿真,下面是仿真实例结果。
【UVM-1.2消息格式定制】
第一步:定义usr_report_server(类名根据需要自定义)类,因为UVM中消息最终实现都是在uvm_default_report_server中,所以usr_report_server也拓展自uvm_default_report_server。
第二步:在用户拓展自uvm_test的测试类的build_phase中,声明一个第一步定义的usr_report_server类的句柄usr_svr,并且在build_phase中将其通过new函数创建对象;
第三步:在build_phase中将指向创建好对象的句柄usr_svr传递给uvm_report_server::set_server(usr_svr),代码同【UVM-1.1消息格式定制】第三步的代码截图;
第四步:运行仿真,下面是仿真实例结果。
通过上述方式,用户就可以自己定义显示消息的方法,并且可以根据需要控制显示时间等信息。