欢迎关注个人公众号摸鱼范式



本期将讲解UVM环境构成和启动方式。主要参考资料为

http://bbs.eetop.cn/thread-320165-1-1.html

http://rockeric.com/

环境构成

进行仿真验证的基本流程是

  1. 例化DUT
  2. 产生并发送激励
  3. 检测响应
  4. 检查响应是否正确

在验证环境中,产生并发送激励将会交给两个不同的类完成,即uvm_driver和uvm_sequence,检测响应通过uvm_monitor完成,而检查响应是否正确通过uvm_scoreboard。除了保证某项功能正确,我们还需要能够确保spec中的每一项功能都通过测试,而衡量验证完备性的指标之一就是功能覆盖率,在我们的验证环境中收集功能覆盖率的任务则交给了conv_coverage实现。

接下来将以数据从uvm_driver驱动到DUT,再从DUT到uvm_monitor,再到uvm_scoreboard的顺序讲解验证环境的构成。

接口定义

当我们需要进行仿真验证时,与DUT的交互是一个必要的内容,所我们首先分析DUT的接口,较为简单,一共有四组接口,一组寄存器配置接口,三组数据接口用于输入特征图、权重和偏置数据的读取,一组数据接口用于输出特征图的存储接口。

interface的定义在顶层的tb.sv中,三组输入数据几口可以使用同一类型的接口实现,下列代码中的具体内容省略了,详情请自行查看。最后一组接口用于检测寄存器的内容,当前版本没有使用寄存器模型,所以这个接口是必要的。

interface cfg_intf (input clk , input rst_n);
clocking drv_ck @(posedge clk);
default input #1ns output #1ns;
endclocking
clocking mon_ck @(posedge clk);
default input #1ns output #1ns;
endclocking
endinterface interface mem_in_intf (input clk , input rst_n);
clocking drv_ck @(posedge clk);
default input #1ns output #1ns;
endclocking
clocking mon_ck @(posedge clk);
default input #1ns output #1ns;
endclocking
endinterface interface mem_out_intf (input clk , input rst_n);
clocking mon_ck @(posedge clk);
default input #1ns output #1ns;
endclocking
endinterface interface conv_intf (input clk , input rst_n);
clocking mon_ck @(posedge clk);
default input #1ns output #1ns;
endclocking
endinterface

注意在interface定义中,分别定义了两个时钟块,一组驱动用的时钟块,一组检测用的时钟块,目的就是为了模拟真实的建立保持时间,时钟块的具体用法可以参考绿皮书的第四章内容。

环境组件

有了接口定义以后,通过接口定义,我们便能够与DUT交互,那么进行交互我们需要做什么呢?

首先看整体结构,如果看不清,后台回复UVM结构图获取VISIO文件。按照接口进行分类,可以分成两大类,一类通过接口与DUT实现交互,另外一类构成了其他的组件,例如checker,通过其他组件收集到的数据进行数据比对,保证DUT的功能正确性。

验证环境一共由四个PKG组成,通过在顶层import导入:

import cfg_pkg::*;
import mem_in_pkg::*;
import mem_out_pkg::*;
import conv_pkg::*;

与DUT直接联系的组件

我们从DUT与验证环境的接口处开始说起,cfg_pkg包含了对于寄存器进行验证的组件。构建UVM环境基本的几个组件包括uvm_driver,uvm_sequencer,uvm_monitor,uvm_agent。而uvm_sequence_item和uvm_sequence则不属于环境的组件,他们是环境组件之间传递信息的载体。

package cfg_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh" typedef enum {WR,RD,IDLE}cmd_t; class cfg_item extends uvm_sequence_item;
endclass : cfg_item class cfg_driver extends uvm_driver #(cfg_item);
endclass :cfg_driver class cfg_sequencer extends uvm_sequencer #(cfg_item);
endclass: cfg_sequencer class cfg_base_sequence extends uvm_sequence #(cfg_item);
endclass: cfg_conv_sequence class cfg_monitor extends uvm_monitor;
endclass: cfg_monitor class cfg_agent extends uvm_agent;
cfg_driver driver;
cfg_monitor monitor;
cfg_sequencer sequencer;
local virtual cfg_intf vif;
endclass:cfg_agent endpackage

UVM的思想之一就是要降低组件之间的耦合度,让组件的功能更加单纯。

uvm_driver和uvm_monitor是距离DUT最近的两个组件,可以直接与DUT的接口进行互动。

uvm_monitor通过检测接口上的信号,转化为数据包,如实地发送给checker,只实现这一单纯的功能,而对于驱动DUT这一功能则交给uvm_driver。

uvm_driver通过从uvm_sequencer获取到的uvm_sequence_item解析出驱动数据,如实的将uvm_sequence_item的内容驱动到DUT的接口上,也只实现这一单纯的功能,至于具体的激励内容,则通过uvm_sequencer暴露接口给顶层环境,让验证人员通过uvm_sequencer发送激励。

而uvm_sequencer的功能就更加简单了,只需要实现传递uvm_sequence_item即可,不需要关注其他的工作。

而uvm_agent中则通常会例化四个组件,uvm_driver,uvm_sequencer,uvm_monitor和对应的interface。uvm_agent的功能也非常单一,仅仅只是将对于一组接口的相关组件进行一个打包,把他们整合起来,这样在顶层就只需要例化这一个组件即可。

mem_in_pkg和mem_out_pkg的内容整体上和cfg_pkg基本一致,不再赘述。

与DUT没有直接联系的组件

通过cfg_pkg的内容,我们实现了对DUT的驱动与检测,那么驱动的内容从何而来,而检测的数据包又要发送到哪里去呢?从前面图中我们可以看到,除了五个agent以外,我们还有其他的组件,包括conv_checker,conv_coverage和conv_virtual_sequencer。

  class conv_checker extends uvm_scoreboard;
endclass:conv_checker class conv_coverage extends uvm_component;
endclass:conv_coverage class conv_virtual_sequencer extends uvm_sequencer;
cfg_sequencer cfg_sqr;
mem_in_sequencer fmi_sqr;
mem_in_sequencer wt_sqr;
mem_in_sequencer bias_sqr;
endclass:conv_virtual_sequencer

conv_checker继承自uvm_scoreboard,他通过前面所述的五个agent中的monitor,获取DUT的信息,进行数据对比检查。在现在的DUT中,他所实现的功能是,在一次卷积运算结束后,使用软件算法直接进行卷积运算,然后与DUT的计算结果进行对比,确保DUT功能正确。

UVM并没有预置的类用于覆盖率收集,所以conv_coverage继承自uvm_component,成为最简单的UVM组件。由于除了寄存器的接口以外,其他都是简单得sram接口,所以只对寄存器进行覆盖率收集。覆盖率组件通过覆盖率的收集,量化功能验证的完备性,根据对应的功能点,设定对应的覆盖率,而功能验证的目的就是为了达到100%的功能覆盖率。

conv_virtual_sequencer继承自uvm_sequencer,其本身并没有什么功能,所以他的名字中带有virtual,他只是一个虚拟的sequencer。其内部包含了cfg_sqr,fmi_sqr,wt_sqr和bias_sqr,作用就是将他们整合在一起,sequencer就是一根数据线,uvm_sequence_item就是传输的数据,而virtual_sequencer就一个集线器或者说一个拓展坞,把很多条数据线绑在一起。

conv_env

数据的驱动由uvm_driver实现,检测由uvm_monitor实现,激励由uvm_sequencer传递,数据对比由conv_checker实现,覆盖率收集由conv_coverage实现。那么接下来就需要把这些组件全部整合在一起,成为一个验证环境,这便是conv_env。在这里,我们只需要完成各个组件的例化和他们之间的连接,不要关心其他工作。

  class conv_env extends uvm_env;
cfg_agent cfg_agt;
mem_in_agent fmi_agt;
mem_in_agent wt_agt;
mem_in_agent bias_agt;
mem_out_agent fmo_agt;
conv_checker chker;
conv_coverage cvrg;
conv_virtual_sequencer virt_sqr;
endclass: conv_env

uvm_test

现在我们已经获得了一个针对卷积模块的DUT,那么如何开始仿真测试?回忆前面所提到的virtual_sequencer,我们只需要通过virtual_sequencer对每个DUT的接口进行驱动,就能让DUT运转起来。针对每一个测试,我们需要创建对应的uvm_test类,然后再uvm_test内通过virtual_sequencer进行激励发送即可。

  class conv_base_test extends uvm_test;
conv_env env; task run_phase(uvm_phase phase);
phase.raise_objection(this);
this.run_top_virtual_sequence();
phase.drop_objection(this);
endtask virtual task run_top_virtual_sequence();
endtask endclass: conv_base_test

上述代码中的run_phase的内容就是在构建环境后,整个仿真真正需要进行的测试内容。可以看到我们定义了一个run_top_virtual_sequence方法,用于运行virtual_sequence。

与virtual_sequencer对应的,virtual_sequence就是virtual_sequencer所需要传输的内容,它的内部会包括各式各样的sequence,针对每一个agent发送不同的激励。通过修改virtual_sequence的内容,我们就能够完成不同的测试用例。

启动方式

这里先不讨论整个环境的树状结构、连接方式和运行机制,这些将在后续的推送中实现。

在构建完整个环境和测试用例以后,我们就需要在顶层启动测试。

module tb ();
logic clk;
logic rst_n;
conv i_conv ();//这里省略了端口连接,具体请参考实验代码
// clock generation
initial begin
clk <= 0;
forever begin
#5 clk <= !clk;
end
end
// reset trigger
initial begin
#10 rst_n <= 0;
repeat(10) @(posedge clk);
rst_n <= 1;
end
import uvm_pkg::*;
`include "uvm_macros.svh"
import cfg_pkg::*;
import mem_in_pkg::*;
import mem_out_pkg::*;
import conv_pkg::*; cfg_intf cfg_if(.*);
mem_in_intf fmi_if(.*);
mem_in_intf wt_if(.*);
mem_in_intf bias_if(.*);
mem_out_intf fmo_if(.*);
conv_intf conv_if(.*); assign conv_if.start =i_conv.i_regfile.start ;
assign conv_if.done =i_conv.i_regfile.done ;
assign conv_if.fmap_in_w =i_conv.i_regfile.fmap_in_w ;
assign conv_if.fmap_in_h =i_conv.i_regfile.fmap_in_h ;
assign conv_if.fmap_in_ch_div_32 =i_conv.i_regfile.fmap_in_ch_div_32 ;
assign conv_if.k_w =i_conv.i_regfile.k_w ;
assign conv_if.k_h =i_conv.i_regfile.k_h ;
assign conv_if.fmap_out_w =i_conv.i_regfile.fmap_out_w ;
assign conv_if.fmap_out_h =i_conv.i_regfile.fmap_out_h ;
assign conv_if.fmap_out_w_div_32 =i_conv.i_regfile.fmap_out_w_div_32 ;
assign conv_if.fmap_out_ch_div_32 =i_conv.i_regfile.fmap_out_ch_div_32 ;
assign conv_if.pooling_bypass =i_conv.i_regfile.pooling_bypass ;
assign conv_if.act_bypass =i_conv.i_regfile.act_bypass ;
assign conv_if.padding_cnt =i_conv.i_regfile.padding_cnt ;
assign conv_if.stripe =i_conv.i_regfile.stripe ;
assign conv_if.last_pixel =i_conv.i_regfile.last_pixel ;
assign conv_if.last_pixel_div_32 =i_conv.i_regfile.last_pixel_div_32 ;
assign conv_if.fmap_out_ch =i_conv.i_regfile.fmap_out_ch ;
assign bias_if.addr[15:8]='0; initial begin
uvm_config_db#(virtual mem_in_intf)::set(uvm_root::get(), "uvm_test_top", "fmi_in_vif", fmi_if);
uvm_config_db#(virtual mem_in_intf)::set(uvm_root::get(), "uvm_test_top", "wt_vif", wt_if);
uvm_config_db#(virtual mem_in_intf)::set(uvm_root::get(), "uvm_test_top", "bias_vif", bias_if);
uvm_config_db#(virtual cfg_intf)::set(uvm_root::get(), "uvm_test_top", "cfg_vif", cfg_if);
uvm_config_db#(virtual mem_out_intf)::set(uvm_root::get(), "uvm_test_top", "fmo_vif", fmo_if);
uvm_config_db#(virtual conv_intf)::set(uvm_root::get(), "uvm_test_top", "conv_vif", conv_if);
// If no external configured via +UVM_TESTNAME=my_test, the default test is
// std_test
run_test("std_test");
end endmodule : tb

在tb的顶层模块中,我们要做5件事:

  1. 定义时钟与复位
  2. 例化dut
  3. 例化与连接各个interface
  4. 将每个interface句柄通过uvm_config_db传递到环境中去
  5. 通过run_test()方法启动测试

uvm_config_db是UVM所提供用于传递数据的静态方法,在后续的推送中将会展开讲解。这里值得注意的是,一定要在run_test()之前实现uvm_config_db传递,否则在run_test()开始后,环境内部将无法获取句柄,导致报错。

run_test()是UVM提供的测试启动方法,传递参数是一个字符串变量,该字符串将用于指定默认的testcase。如果在命令选项中,没有通过+UVM_TESTNAME指定具体的TESTNAME,将会运行默认的testcase。

总结

本次讲解了验证环境的基本组件和构成,以及在顶层启动的注意事项。

  1. 与DUT直接交互的组件为uvm_driver和uvm_monitor,传递激励信息的组件为uvm_sequencer,uvm_agent将三者组合起来。
  2. uvm_scoreboard获取各个uvm_monitor传递过来的数据,进行比对,保证DUT功能的正确性。
  3. conv_coverage用于收集覆盖率
  4. conv_virtual_sequencer将每个uvm_agent中的uvm_sequencer集中起来进行管理,起到集线器或者说路由器的效果
  5. conv_env将上述所有组件容纳起来,并且进行连接
  6. uvm_test通过编写conv_virtual_sequence,经由conv_virtual_sequencer发送激励实现不同的testcase
  7. 在顶层实现各项例化,并且在run_test()之前传递接口句柄
  8. 通过run_test()启动测试,并且指定默认testcase

更多内容请期待后续推送,实验代码在后台回复“UVM实验”即可获得资源。

UVM实战[二]的更多相关文章

  1. UVM基础总结——基于《UVM实战》示例

    一.前言 工作一直在做SoC验证,更关注模块间的连接性和匹配性,所以相比于擅长随机约束激励的UVM来说,定向测试的概念更容易debug.当然前提是IP已经被充分验证.因此觉得接触UVM的机会较少.到现 ...

  2. coreseek实战(二):windows下mysql数据源部分配置说明

    coreseek实战(二):windows下mysql数据源部分配置说明 关于coreseek在windows使用mysql数据源的配置,以及中文分词的详细说明,请参考官方文档: mysql数据源配置 ...

  3. 【NFS项目实战二】NFS共享数据的时时同步推送备份

    [NFS项目实战二]NFS共享数据的时时同步推送备份 标签(空格分隔): Linux服务搭建-陈思齐 ---本教学笔记是本人学习和工作生涯中的摘记整理而成,此为初稿(尚有诸多不完善之处),为原创作品, ...

  4. chrome调试工具高级不完整使用指南(实战二)

    3.3 给页面添加测试脚本 在现实的工作中,我们往往会遇到一些问题在线上就会触发然后本地就触发不了的问题.或者是,要给某个元素写一个测试脚本.这个时候如果是浏览器有提供一个添加脚本的功能的话,那么我们 ...

  5. Python爬虫实战二之爬取百度贴吧帖子

    大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 前言 亲爱的们,教程比较旧了,百度贴吧页面可能改版,可能代码不 ...

  6. 转 Python爬虫实战二之爬取百度贴吧帖子

    静觅 » Python爬虫实战二之爬取百度贴吧帖子 大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 ...

  7. Netty 仿QQ聊天室 (实战二)

    Netty 聊天器(百万级流量实战二):仿QQ客户端 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之15 [博客园 总入口 ] 源码IDEA工程获取链接:Java 聊天室 实战 源码 写在 ...

  8. kubernetes实战(二十八):Kubernetes一键式资源管理平台Ratel安装及使用

    1. Ratel是什么? Ratel是一个Kubernetes资源平台,基于管理Kubernetes的资源开发,可以管理Kubernetes的Deployment.DaemonSet.Stateful ...

  9. SpringSecurity权限管理系统实战—二、日志、接口文档等实现

    系列目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战 ...

随机推荐

  1. 003.Delphi插件之QPlugins,菜单插件加强

    相比上一篇的菜单插件,这个在创建和销毁时候,做了增强,同时做了2个菜单对应的窗口 unit MenuSvc; interface uses windows, classes, SysUtils, Gr ...

  2. Most simple basic of internet programming based on two different machines sharing the same local net

    This blog is just shown for the most simple basic of internet programming based on two different mac ...

  3. POJ 1852:Ants

    Ants Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 11754   Accepted: 5167 Description ...

  4. 更换虚拟机VMware Workstation14和centos7版本及工具

    本博主之前一直沿用的centos是6.7比较老,工作站是10,想要更换一批新版本的工具,并将之前的一些开发环境整理出来移动到该版本上,将其记录在下 一.下载准备 1.下载centos7镜像,选择最小的 ...

  5. 七十八、SAP中数据库操作之查询条数限制

    一.UP TO <数量> ROWS,表示查询出多少条数据 二.效果如下

  6. 二十、SAP中定义内表

    一.内表相当于传统语言的多维数组的东西,定义一个内表有以下2个方式

  7. Java基础(接口,list和ArrayLIst)

    1,使用接口的目的:不是10类都有getName()方法,则10个类都继承一个接口来实现.接口本身是一种规范,在具体业务有需求的时候才用接口. 具体例子: I充电设备 x = new 手机(" ...

  8. Python 比较 相等性 真值

    1 == 操作符测试 值 的相等性: is 测试对象的一致性.注意短字符串的is相等性测试,PVM会缓存短字符串,s1 is s2 将返回true. 2 false:"", [], ...

  9. 公告上下滚动基于Jquery

    前提  需要引入jquery  如果你用的单位不是px  修改的同时红色部分需保持一致 <!DOCTYPE html> <html> <head> <meta ...

  10. gcc/g++以c++11的方式编译

    方法一: 在程序头加上预定义编译器命令 #pragma GCC diagnostic error "-std=c++11" 通过#pragma 指示 GCC编译器处理错误的方式以c ...