[UVM源代码研究] 聊聊UVM源代码中的DPI函数

[UVM源代码研究] 聊聊UVM源代码中的DPI函数

引言

之前的一篇文章 数字验证大头兵:[UVM源代码研究] 聊聊寄存器模型的后门访问 中提到进行寄存器后门访问的时候用到了UVM源代码中自带的DPI函数uvm_hdl_read(),于是乎打算写一篇文章把UVM源代码中涉及到的DPI函数彻底梳理一遍。

SystemVerilog DPI,全称SystemVerilog直接编程接口 (英语:SystemVerilog Direct Programming Interface)是SystemVerilog与其他外来编程语言的接口。能够使用的语言包括C语言、C++、SystemC等。直接编程接口由两个层次构成:SystemVerilog层和外来语言层,两个层次相互分离。对于SystemVerilog方面,另一边使用的编程语言是透明的,它只需要保证函数名和函数参数返回值类型的匹配即可。SystemVerilog和外来语言的编译器各自并不需要分析另一种语言的代码,两者各自独立编译。由于不触及SystemVerilog层,因此支持使用不同的语言。不过,目前SystemVerilog仅为C语言定义了外来语言层。所以,每个函数DPI的文件都有.svh和*.cc或*.c 两个文件。

dpi目录文件介绍

UVM源代码中的dpi目录包含文件如图1所示。

图1 /src/dpi目录文件结构

在这里插入图片描述

uvm_dpi.cc和uvm_dpi.svh

uvm_dpi.cc文件如图2所示,其中include了所有C语言相关的源文件。

图2 /src/dpi/uvm_dpi.cc文件内容

在这里插入图片描述

相应的uvm_dpi.svh则是所有DPI相关的sv代码的文件集合,如图3所示。

图3 /src/dpi/uvm_dpi.svh文件内容

在这里插入图片描述

可以看到uvm_dpi.svh中除了include相关几个sv文件之外,还做了宏的统一管理,一旦打开了UVM_NO_DPI宏,那么其中包含的三个文件中的NO_DPI宏就统一打开了,这样所有的DPI函数就都不能正常使用了,当然也可以分别独立打开每一个NO_DPI宏。

如此一来,我们只需要把上面提到的这两个文件都编译了就可以使用相应的DPI函数功能了,uvm_pkg.sv中包含文件uvm_dpi.svh,而http://uvm_dpi.cc需要在uvm_pkg.sv之外单独编译,所以在旧版本的仿真器编译时通常我们需要分别编译uvm_pkg.sv和http://uvm_dpi.cc两个文件,以VCS为例,早起版本我们通常需要像图4一样添加编译参数。

图4 vcs编译工具添加支持UVM DPI的编译参数

在这里插入图片描述

如今得益于多数仿真工具已经将UVM源代码库集成到了仿真工具的安装路径下了,并且支持相关的参数来简化仿真器的配置项,诸如上面一些列UVM相关的参数可以直接使用下面一句话来替换完成。

vcs -ntb_opts uvm-1.2

VCS仿真器会自动将指定的UVM版本相关的源代码全部编译,大大简化了仿真工具的参数配置过程。

dpi目录下剩余三组c/cc和svh后缀文件分别对应三种DPI的应用,下面我们分别展开介绍。

uvm_hdl.svh和uvm_hdl.c

UVM源代码实现的后门访问HDL路径的函数基本都包含在uvm_hdl文件中,我们可以在uvm中实现HDL的后门访问,具体包括的function有uvm_hdl_check_path,uvm_hdl_deposit, uvm_hdl_force,uvm_hdl_release,uvm_hdl_read, task 有uvm_hdl_force_time。 下面分别介绍下这些函数/任务的作用和使用示例。

uvm_hdl_check_path

  // Function: uvm_hdl_check_path
  //
  // Checks that the given HDL ~path~ exists. Returns 0 if NOT found, 1 otherwise.
  //
  function int uvm_hdl_check_path(string path)

检查字符串表示的HDL路径是否存在,存在返回1,不存在返回0。

uvm_hdl_check_path的使用示例参考图5所示的代码段。

图5 uvm_hdl_check_path使用示例

在这里插入图片描述

执行结果如图6所示。

图6 uvm_hdl_check_path示例执行结果

在这里插入图片描述

uvm_hdl_check_path函数的典型应用场景可以用来判断某些通过宏或者generate选择性例化的instance是否有被例化来执行相应的后续处理代码。

下面几个DPI函数涉及到对HDL路径上的几个信号进行值操作,这里对几个信号的关系进行简单的说明,涉及到如下三个信号。

output wire[4:0]  da_xtal_ib_ctrl;
input wire[4:0]   da_xtal_ib_ctrl_func;
output reg[4:0]   xtal_ib_ctrl;  

他们之间的关系是:

  1. da_xtal_ib_ctrl的值由da_xtal_ib_ctrl_func通过assign连续赋值更新

  2. da_xtal_ib_ctrl_func的值通过接口传递由xtal_ib_ctrl更新

  3. xtal_ib_ctrl的值由always语句通过时序逻辑更新

uvm_hdl_deposit

// Function: uvm_hdl_deposit
  //
  // Sets the given HDL ~path~ to the specified ~value~.
  // Returns 1 if the call succeeded, 0 otherwise.
  //
  import "DPI-C" context function int uvm_hdl_deposit(string path, uvm_hdl_data_t value);

uvm_hdl_deposit函数具有两个参数。第一个参数是字符串表示的HDL路径信号,第二个参数是要发送到信号上的值。

uvm_hdl_deposit的使用示例如图7所示。

图7 uvm_hdl_deposit代码示例

在这里插入图片描述

仿真结果波形如图8所示。

图8 uvm_hdl_deposit代码示例运行结果

在这里插入图片描述

从波形中我们可以看到虽然da_xtal_ib_ctrl信号定义成了wire类型并且通过连续赋值语句由信号da_xtal_ib_ctrl_func进行赋值,但是使用uvm_hdl_deposit赋值了之后连续赋值语句就无效了,该值被保存了下来,执行效果有点类似于force。但是等待5us后又对da_xtal_ib_ctrl_func信号采用了uvm_hdl_deposit赋了一个新值,发现da_xtal_ib_ctrl的值也跟着更新了,可见uvm_hdl_deposit的作用还不同于force功能,他像是给HDL中的某个信号进行赋值,知道该信号在HDL中的赋值逻辑发生变化时才会再次更新值,这样就不会被连续赋值语句覆盖了。最后通过配置寄存器又将xtal_ib_ctrl的值进行更新进而覆盖了之前deposit的值。

uvm_hdl_force/uvm_hdl_release

  // Function: uvm_hdl_force
  //
  // Forces the ~value~ on the given ~path~. Returns 1 if the call succeeded, 0 otherwise.
  //
  import "DPI-C" context function int uvm_hdl_force(string path, uvm_hdl_data_t value);

  // Function: uvm_hdl_release
  //
  // Releases a value previously set with <uvm_hdl_force>.
  // Returns 1 if the call succeeded, 0 otherwise.
  //
  import "DPI-C" context function int uvm_hdl_release(string path);

这个函数的功能类似于我们在systemverilog中的force语法,只不过force的对象是HDL hierarchy,而uvm_hdl_force的对象是一个表示HDL hierarchy的字符串,,它也有与force对应的release功能的函数uvm_hdl_release。uvm_hdl_force跟uvm_hdl_deposit的区别在于后者能够被新的赋值所覆盖,而前者在被uvm_hdl_release释放之前信号值永远不会被覆盖。

我们紧接着uvm_hdl_deposit代码后面补充uvm_hdl_force/uvm_hdl_release代码示例,如图9所示。

图9 uvm_hdl_force/uvm_hdl_release代码示例

在这里插入图片描述

运行结果波形如图10所示。

图10 uvm_hdl_force/uvm_hdl_release代码示例运行结果

在这里插入图片描述

图中结果可以看到uvm_hdl_force会强制改变信号的值,执行uvm_hdl_release释放了又会被连续赋值语句覆盖之前force的值,而在uvm_hdl_force和uvm_hdl_release之间执行的uvm_hdl_deposit则被忽略了。

uvm_hdl_force/uvm_hdl_release相比于force/release的好处是将HDL hierarchy封装成了字符串的形式,不仅有利于将路径作为参数进行传递,同时还规避了package中不允许出现HDL hierarchy的语法问题,这样我们就可以在被package包含的class文件中对HDL hierarchy 进行操作了。

uvm_hdl_release_and_read

 // Function: uvm_hdl_release_and_read
  //
  // Releases a value previously set with <uvm_hdl_force>.
  // Returns 1 if the call succeeded, 0 otherwise. ~value~ is set to
  // the HDL value after the release. For 'reg', the value will still be
  // the forced value until it has been procedurally reassigned. For 'wire',
  // the value will change immediately to the resolved value of its
  // continuous drivers, if any. If none, its value remains as forced until
  // the next direct assignment.
  //
  import "DPI-C" context function int uvm_hdl_release_and_read(string path, inout uvm_hdl_data_t value);

uvm_hdl_release_and_read相比于uvm_hdl_release简单粗暴的释放信号的驱动,对wire和reg类型做了不同的处理,对于wire类型直接释放信号的驱动,连续赋值语句能够将新值立即drive给该信号更新信号值,而对于reg类型需要再次驱动该信号才能更新该值,并且在释放驱动的一瞬间用接口上的inout参数value来更新信号release后的值(实际实验发现这个value并没有被更新上去,不知道是使用的方法不正确,还是代码有漏洞)。

代码示例如图11所示。

图11 uvm_hdl_release_and_read代码示例

在这里插入图片描述

运行结果波形如图12所示。

图12 uvm_hdl_release_and_read代码示例运行结果

在这里插入图片描述

在图12标记的时间点1、2、3我们分别对三个信号da_xtal_ib_ctrl, da_xtal_ib_ctrl_func, xtal_ib_ctrl执行了uvm_hdl_force,时间点4、5、6依次执行了uvm_hdl_release_and_read,并且我们调用uvm_hdl_release_and_read传递的value参数都是1,都是事与愿违,无论是wire还是reg类型,这个value值都没有在release时设置到对应的信号。从执行uvm_hdl_release_and_read的结果我们可以看到对于wire信号da_xtal_ib_ctrl, da_xtal_ib_ctrl_func释放之后立马值就被HDL的连续赋值更新了,不管是通过assign还是接口连线,而reg类型的xtal_ib_ctrl释放后由于没有赋值则没有更新值。

uvm_hdl_force_time

  // Function: uvm_hdl_force_time
  //
  // Forces the ~value~ on the given ~path~ for the specified amount of ~force_time~.
  // If ~force_time~ is 0, <uvm_hdl_deposit> is called.
  // Returns 1 if the call succeeded, 0 otherwise.
  //
  task uvm_hdl_force_time(string path, uvm_hdl_data_t value, time force_time = 0);
    if (force_time == 0) begin
      void'(uvm_hdl_deposit(path, value));
      return;
    end
    if (!uvm_hdl_force(path, value))
      return;
    #force_time;
    void'(uvm_hdl_release_and_read(path, value));
  endtask

这个uvm_hdl_force_time实际上并没有专门的C函数实现,他是对uvm_hdl_deposit, uvm_hdl_force和uvm_hdl_release_and_read的组合实现,表示force某个信号为特定值之后一段时间后再释放。需要注意的是,当force时间为0时相当于执行了uvm_hdl_deposit,当force时间超过0时release用的是uvm_hdl_release_and_read,这样对于wire类型就会产生两种不同的force效果,因为uvm_hdl_deposit对于连续赋值语句之后等号右边的表达式有新值时才会更新赋值,而不会立刻执行连续赋值的结果。

uvm_hdl_read

  // Function: uvm_hdl_read()
  //
  // Gets the value at the given ~path~.
  // Returns 1 if the call succeeded, 0 otherwise.
  //
  import "DPI-C" context function int uvm_hdl_read(string path, output uvm_hdl_data_t value);

这个我们在之前的文章将寄存器后门访问的时候已经有过讲解,表示通过后门读取HDL路径上的信号值,不限于寄存器。

这样我们关于uvm_hdl.svh中涉及到的函数任务就讲解完成了,uvm_hdl.c文件中的具体C代码实现我们这里就不展开讲解了,感兴趣的可以自行研究。

uvm_regex.svh和uvm_regex.cc

这两个文件实现了UVM中的正则表达相关的函数,这个使用场景很有限,这里就不做介绍了。

uvm_svcmd_dpi.svh和uvm_svcmd_dpi.cc

UVM中有需要从cmmand line获取输入参数的需求就可以调用这里的几个DPI函数,但是由于 t e s t test testplusargs和 v a l u e s values valuesplusargs的存在,这里的DPI函数也不太常用,所以这里就不做介绍了。

总结

本文主要介绍了UVM源代码src/dpi/uvm_hdl.svh中的几个通过HDL hierarchy后门访问RTL的方法,提供了一种UVM环境与DUT交互的新思路。

  • 27
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值