在构建复杂的 sequence 序列的时候,我们经常会用到 m_sequencer 和 p_sequencer,并且在很多资料中都提到两者实际指向的是同一个对象,那么为什么要同时存在他们两个,存在一个不就够用了吗?为此,本文通过示例说明下两者之间的关系。
首先,我们先来看看“白皮书”上的一个示例。
【示例】
“白皮书”中讲解在 case0_sequence 中如果企图通过 m_sequencer 引用 my_sequencer 中的变量将会报错,需要通过调用宏声明 p_sequencer 后通过 p_sequencer 才能引用 my_sequencer 中的变量,并且还强调了了 m_sequencer 和 p_sequencer 指向同一个对象,那么为什么 m_sequencer 不能直接访问 my_sequencer 中的变量呢?这里首先需要明白,在 UVM 中 m_sequencer 句柄的类型是 uvm_sequencer_base,并且定义在 uvm_sequence_item 类中,可以理解为其是 sequence 的 member sequencer 的简写,是 uvm_sequence_item 类中的一个成员(如下图),用户编写的 sequence 大都派生自该类。在建立 sequence 和 sequencer 关系(uvm_sequence::start())时,该成员将指向该 sequence 将要运行的 sequencer。
那么,p_sequencer 的类型是什么呢?我们从代码中的宏定义来看一下,这个宏定义展开如下。
在该宏中,SEQUENCER 必须为“类型”,当程序中使用该宏时,SEQUENCER 为用户自定义的 sequencer,即示例中的 my_sequencer,而因为宏中 p_sequencer 指定的类型为 SEQUENCER,即也就是调用该宏时传入的参数 my_sequencer,所以可以知道 p_sequencer 的类型为用户自定义的 sequencer,即示例中的 my_sequencer,而用户自定义的 sequencer 派生自 uvm_sequencer。至此,我们也就知道了 m_sequencer 和 p_sequencer 的句柄类型。我们汇总下上述信息可以得到下图。
在声明 p_sequencer 的宏中,451 行通过$cast 将 p_sequencer 指向了 m_sequencer 指向的对象,而 m_sequencer 此时指向对象的类型就是用户自定义的 sequencer,即经过一番骚操作之后,p_sequencer 和 m_sequencer 指向了同一个 sequencer。既然指向了同一个对象,为什么一个句柄(p_sequencer)可以访问对象的属性而另一个(m_sequencer)不行呢?这里就需要大家理解 SystemVerilog 中的句柄转换后,父类句柄都可以访问子类对象中的那些属性的问题。为了描述问题方便,我们通过一个简化示例来说明 p_sequencer 和 m_sequencer 之间的访问关系。
【示例】
【仿真结果】
示例中,m_sequencer 句柄的类型为 sequencer_base,p_sequencer 句柄类型为 sequencer_user,p_sequencer 指向创建对象之后,可以访问 sequencer_user 中的属性 user_str,通过显示函数显示出来,但是在 18 行将 m_sequencer 指向 p_sequencer 指向的对象之后,通过 m_sequencer 访问 sequencer_user 中的属性 user_str 析构时报错!这是因为在 SystemVerilog 中,如果企图使用父类句柄去访问子类对象时,父类句柄是不能直接访问子类对象中的属性成员,其实这也符合人之常情,时代在进步,子类肯定会具有一些父类所不具有的特性,这些特性就是父类所不具有也不能直接进行访问的。那么,如何实现父类对于子类中属性的访问呢?可以采用曲线救国的策略,我们可以通过父类句柄访问子类中的方法,并且该方法还必须是虚方法,然后在子类中的方法访问子类中的属性实现,只不过这样实现起来有些费劲,所以我们将一些通用的属性都放在父类中,这样可以实现这些资源的共享。
回到UVM 中的 m_sequencer 和 p_sequencer 中,因为 m_sequencer 相当于是父类句柄,而 p_sequencer 是子类句柄,所以不能使用 m_sequencer 去访问子类句柄指向的子类对象。如果要实现对于子类对象的访问,那么应该如何实现呢?在 SystemVerilog 中,我们可以通过将指向子类对象的父类句柄通过$cast 转换成具有子类对象类型的子类句柄,从而实现子类对象中属性的访问,我们还是通过上例进行示例说明。
【示例】
【仿真结果】
示例中,虽然 m_sequencer 句柄具有父类类型,但是其指向的是子类对象,所以此时可以通过$cast 成功将 p1_sequencer 句柄指向 m_sequencer 指向的子类对象,因为 p1_sequencer 具有和子类对象相同的类型,所以 p1_sequencer 可以访问指向对象中所有的属性。
所以我们回到本文开始“白皮书”示例描述的,之所以出现“白皮书”中描述 m_sequencer 不能访问用户自定义的 sequencer 中属性的原因就是因为 m_sequencer 句柄的类型为当前自定义 sequencer 类型的父类,访问用户自定义 sequencer 类的对象中属性的方法只能通过具有该类型的句柄 p_sequencer 才行。
END
文章来源:处芯积律
推荐阅读
- protocol——命令行绘制协议图大杀器
- PCIe 调试:超级好用的 rescan 与 remove 命令
- 使用 scp 命令跨服务器传输文件
- PCIE , USB , SATA , Ethernet 都在用的 SerDes 到底是个啥?
- 学会用代码绘制接口时序图
更多 IC 设计干货请关注IC设计专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。