在前几节的例子中,driver中等待时钟事件(@posedge top.clk)、给DUT中输入端口赋值(top.rx_dv <= 1' b1)都是使用绝对路径,绝对路径的使用大大减弱了验证平台的可移植性。一个最简单的例子就是假如clk信号的层次从top.clk变成了top.clk_inst.clk,那么就需要对driver中的相关代码做大量修改。因此,从根本上来说,应该尽量杜绝在验证平台中使用绝对路径

避免绝对路径的一个方法是使用宏:

代码清单 2-11
`define TOP top_tb
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
`TOP.rxd <= 8'b0;
`TOP.rx_dv <= 1'b0;
while(!`TOP.rst_n)
@(posedge `TOP.clk);
for(int i = 0; i < 256; i++)begin
@(posedge `TOP.clk);
`TOP.rxd <= $urandom_range(0, 255);
`TOP.rx_dv <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW);
end
@(posedge `TOP.clk);
`TOP.rx_dv <= 1'b0;
phase.drop_objection(this);
endtask

这样,当路径修改时,只需要修改宏的定义即可。但是假如clk的路径变为了top_tb.clk_inst.clk,而rst_n的路径变为了top_tb.rst_inst.rst_n,那么单纯地修改宏定义是无法起到作用的。

避免绝对路径的另外一种方式是使用interface。在SystemVerilog中使用interface来连接验证平台与DUT的端口。

interface的定义比较简单:

代码清单 2-12
文件:src/ch2/section2.2/2.2.4/my_if.sv
4 interface my_if(input clk, input rst_n);
5
6 logic [7:0] data;
7 logic valid;
8 endinterface

定义了interface后,在top_tb中实例化DUT时,就可以直接使用:

代码清单 2-13
文件:src/ch2/section2.2/2.2.4/top_tb.sv
17 my_if input_if(clk, rst_n);
18 my_if output_if(clk, rst_n);
19
20 dut my_dut(.clk(clk),
21 .rst_n(rst_n),
22 .rxd(input_if.data),
23 .rx_dv(input_if.valid),
24 .txd(output_if.data),
25 .tx_en(output_if.valid));

那么如何在driver中使用interface呢?一种想法是在driver中声明如下语句,然后再通过赋值的形式将top_tb中的input_if传递给它:

代码清单 2-14
class my_driver extends uvm_driver;
my_if drv_if;

endclass

读者可以试一下,这样的使用方式是会报语法错误的,因为my_driver是一个类,在类中不能使用上述方式声明一个interface,只有在类似top_tb这样的模块(module)中才可以。在类中使用的是virtual interface:

代码清单 2-15
文件:src/ch2/section2.2/2.2.4/my_driver.sv
3 class my_driver extends uvm_driver;
4
5 virtual my_if vif;

在声明了vif后,就可以在main_phase中使用如下方式驱动其中的信号:

代码清单 2-16
文件:src/ch2/section2.2/2.2.4/my_driver.sv
23 task my_driver::main_phase(uvm_phase phase);
24 phase.raise_objection(this);
25 `uvm_info("my_driver", "main_phase is called", UVM_LOW);
26 vif.data <= 8'b0;
27 vif.valid <= 1'b0;
28 while(!vif.rst_n)
29 @(posedge vif.clk);
30 for(int i = 0; i < 256; i++)begin
31 @(posedge vif.clk);
32 vif.data <= $urandom_range(0, 255);
33 vif.valid <= 1'b1;
34 `uvm_info("my_driver", "data is drived", UVM_LOW);
35 end
36 @(posedge vif.clk);
37 vif.valid <= 1'b0;
38 phase.drop_objection(this);
39 endtask

可以清楚看到,代码中的绝对路径已经消除了,大大提高了代码的可移植性和可重用性。

剩下的最后一个问题就是,如何把top_tb中的input_if和my_driver中的vif对应起来呢?

最简单的方法莫过于直接赋值。此时一个新的问题又摆在了面前:在top_tb中,通过run_test语句建立了一个my_driver的实例,但是应该如何引用这个实例呢?不可能像引用my_dut那样直接引用my_driver中的变量:top_tb.my_dut.xxx是可以的,但是top_tb.my_driver.xxx是不可以的。这个问题的终极原因在于UVM通过run_test语句实例化了一个脱离了top_tb层次结构的实例,建立了一个新的层次结构。

对于这种脱离了top_tb层次结构,同时又期望在top_tb中对其进行某些操作的实例,UVM引进了config_db机制。在config_db机制中,分为set和get两步操作。所谓set操作,可以简单地理解成是“寄信”,而get则相当于是“收信”。

在top_tb中执行set操作:

代码清单 2-17
文件:src/ch2/section2.2/2.2.4/top_tb.sv
44 initial begin
45 uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
46 end

在my_driver中,执行get操作:

代码清单 2-18
文件:src/ch2/section2.2/2.2.4/my_driver.sv
13 virtual function void build_phase(uvm_phase phase);
14 super.build_phase(phase);
15 `uvm_info("my_driver", "build_phase is called", UVM_LOW);
16 if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
17 `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
18 endfunction

这里引入了build_phase。与main_phase一样,build_phase也是UVM中内建的一个phase。当UVM启动后,会自动执行build_phase。build_phase在new函数之后main_phase之前执行。在build_phase中主要通过config_db的set和get操作来传递一些数据,以及实例化成员变量等。需要注意的是,这里需要加入super.build_phase语句,因为在其父类的build_phase中执行了一些必要的操作,这里必须显式地调用并执行它。build_phase与main_phase不同的一点在于,build_phase是函数phase,而main_phase是任务phase,build_phase是不消耗仿真时间的。build_phase总是在仿真时间($time函数打印出的时间)为0时执行。

在build_phase中出现了uvm_fatal宏,uvm_fatal宏是一个类似于uvm_info的宏,但是它只有两个参数,这两个参数与uvm_info宏的前两个参数的意义完全一样。与uvm_info宏不同的是,当它打印第二个参数所示的信息后,会直接调用Verilog的finish函数来结束仿真。uvm_fatal的出现表示验证平台出现了重大问题而无法继续下去,必须停止仿真并做相应的检查。所以对于uvm_fatal来说,uvm_info中出现的第三个参数的冗余度级别是完全没有意义的,只要是uvm_fatal打印的信息,就一定是非常关键的,所以无需设置第三个参数。

config_db的set和get函数都有四个参数,这两个函数的第三个参数必须完全一致。set函数的第四个参数表示要将哪个interface通过config_db传递给my_driver,get函数的第四个参数表示把得到的interface传递给哪个my_driver的成员变量。set函数的第二个参数表示的是路径索引,即在2.2.1节介绍uvm_info宏时提及的路径索引。

在top_tb中通过run_test创建了一个my_driver的实例,那么这个实例的名字是什么呢?

答案是uvm_test_top:UVM通过run_test语句创建一个名字为uvm_test_top的实例。读者可以通过把代码清单2-3中的语句插入my_driver(build_phase或者main_phase)中来验证。

无论传递给run_test的参数是什么,创建的实例的名字都为uvm_test_top。由于set操作的目标是my_driver,所以set函数的第二个参数就是uvm_test_top。set函数的第一个参数null以及get函数的第一和第二个参数可以暂时放在一边,后文会详细说明。

set函数与get函数让人疑惑的另外一点是其古怪的写法。使用双冒号是因为这两个函数都是静态函数,而uvm_config_db#(virtual my_if)则是一个参数化的类,其参数就是要寄信的类型,这里是virtual my_if。假如要向my_driver的var变量传递一个int类型的数据,那么可以使用如下方式:

代码清单 2-19
initial begin
uvm_config_db#(int)::set(null, "uvm_test_top", "var", 100);
end

而在my_driver中应该使用如下方式:

代码清单 2-20
class my_driver extends uvm_driver;
int var;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
if(!uvm_config_db#(int)::get(this, "", "var", var))
`uvm_fatal("my_driver", "var must be set!!!")
endfunction

从这里可以看出,可以向my_driver中“寄”许多信。上文列举的两个例子是top_tb向my_driver传递了两个不同类型的数据,其实也可以传递相同类型的不同数据。假如my_driver中需要两个my_if,那么可以在top_tb中这么做:

代码清单 2-21
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif2", output_if);
end

在my_driver中这么做:

代码清单 2-22
virtual my_if vif;
virtual my_if vif2;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif2", vif2))
`uvm_fatal("my_driver", "virtual interface must be set for vif2!!!")
endfunction

*2.2.4 加入virtual interface的更多相关文章

  1. Neutron 理解 (6): Neutron 是怎么实现虚拟三层网络的 [How Neutron implements virtual L3 network]

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  2. Standard C++ Programming: Virtual Functions and Inlining

    原文链接:http://www.drdobbs.com/cpp/standard-c-programming-virtual-functions/184403747 By Josée Lajoie a ...

  3. Tun/Tap interface tutorial

    Foreword: please note that the code available here is only for demonstration purposes. If you want t ...

  4. systemverilog interface

    普通的模块使用法:注意我们这里只实现了部分功能....不是完全的读写模块....     module mem_core(   input logic wen,  input logic ren,   ...

  5. c++virtual inline 是否冲突

    关于inline关键字:effective c++ item33:明智运用inlining.说到:inline指令就像register指令一样,只是对编译器的一种提示,而不是一个强制命令,意思是编译器 ...

  6. openwrt network interface(openwrt中的网络接口)

    这篇算是对openwrt网络接口的一个翻译吧,源地址:http://wiki.openwrt.org/doc/networking/network.interfaces network的接口类型:物理 ...

  7. openwrt interface

    orige : http://www.cnblogs.com/preorder69/p/3959187.html 这篇算是对openwrt网络接口的一个翻译吧,源地址:http://wiki.open ...

  8. NetScaler SNIPs Bound To An Interface Without A VLAN

    NetScaler SNIPs Bound To An Interface Without A VLAN https://www.citrix.com/blogs/2014/04/09/work-yo ...

  9. systemverilog interface杂记

    随着IC设计复杂度的提高,模块间互联变得复杂,SV引入接口,代表一捆连线的结构. Systemverilog语法标准,新引入一个重要的数据类型:interface. interface主要作用有两个: ...

随机推荐

  1. 【转】不用软件,解压Win8/Win8.1的install.wim文件

    今天用好压解压Windows 8.1的install.wim文件,居然提示文件损坏,换了7Z仍然如此:其实文件是好的.只不过这些软件暂时不支持罢了,还好可以用dism命令来手动完成. 一.检查镜像版本 ...

  2. Docker 入门笔记

    Docker 可以理解为一个轻量化的虚拟机, 启动速度快,本身占的资源小 [重要], 容器里是不能保存数据的,容器只要一停止, 所有的数据都会丢失,所以如果重要的数据, 都需要通过配制,把数据保存在 ...

  3. Windows7 64位中出现的KERNELBASE.dll错误的解决方法

    最近在服程序时遇到个问题,电脑是win764位,编译完的exe测试,偶尔总报错,报错是偶尔的,有时候报错很频繁,但是有一次测试,测试了半天都没有报错,我以为好,发布输出没一会儿又报错了,真是崩溃了,所 ...

  4. epoll好文章

    https://www.cnblogs.com/apprentice89/p/3234677.html https://www.jianshu.com/p/aa486512e989 https://c ...

  5. mysql 判断表字段或索引是否存在 - 举一反三

    判断字段是否存在: DROP PROCEDURE IF EXISTS schema_change; DELIMITER // CREATE PROCEDURE schema_change() BEGI ...

  6. Qt5学习笔记(消息基础)

    #include "MyWidget.h" #include <QApplication> #include <QEvent> #include <Q ...

  7. SQL注入不简单?那是你没有懂它的原理~

    我们真的了解SQL注入吗? 不管用什么语言编写的Web应用,它们都用一个共同点,具有交互性并且多数是数据库驱动.在网络中,数据库驱动的Web应用随处可见,由此而存在的SQL注入是影响企业运营且最具破坏 ...

  8. FTP枢轴攻击

    简单来说,这是攻击者可以利用属于不同网络的那些系统的攻击. 本文作者:jishuzhain 对于这种攻击,攻击者需要利用主服务器来帮助攻击者将自己添加到本地网络中,然后攻击者就可以将客户端系统进行定位 ...

  9. 前后端分离——token超时刷新策略

    前言 记录一下前后端分离下————token超时刷新策略! 需求场景 昨天发了一篇记录 前后端分离应用——用户信息传递 中介绍了token认证机制,跟几位群友讨论了下,有些同学有这么一个疑惑:toke ...

  10. log 模块使用 (直接用的方法)

    前情提要: 生活中经常用到log 模块. 但是原生的log 模块复杂或者有许多不好用得地方, 在此记录一个经常用的log 的基本操作方法 一:首先导入模块 import logging.config ...