搭建一个简单的UVM验证平台

news/2025/1/6 6:41:52/文章来源:https://www.cnblogs.com/liushaowe1/p/18646977

转载自  https://zhuanlan.zhihu.com/p/713891980

一. UVM 方法学简介

UVM(Universal Verification Methodology),又称作通用验证方法学。它起源于OVM(Open Verification Methdology),是由Cadence,Mentor和Synopsys联合推出的主流验证方法学;UVM方法学可以帮助我们搭建验证平台、编写测试用例,而它自身提供的基础类库(basic class library)和基本验证结构可以让具有不同程度编程经验的芯片验证工程师们快速构建起一个结构良好可信的验证框架。

验证方法学的发展历程

二. 基本组件编写和平台搭建

1.平台基本结构

首先,我们将一个非常简单的设计模块作为待验证的DUT,其代码如下:

module simple_design input[1:0]   selinput[7:0]   ainput[7:0]   binput[7:0]   coutput[31:0] d
);reg[31:0]  d_reg;always@(*) begincase(sel)0:  d_reg = a;1:  d_reg = b;2:  d_reg = c;3:  d_reg = (a+b+c);endcaseendassign d = d_reg;endmodule

基于该模块,可规划出验证平台如下,Driver负责将数据传输至接口,进而发送给DUT;Monitor负责从接口采集数据;Env作为更高层级的组件,可容纳Driver和Monitor,并控制Driver和Monitor相互配合(在这里使用了mailbox作为媒介,具体控制方式见组件中的描述);test_top则是整个UVM树形结构的顶层,对应我们的仿真用例。

验证平台结构图

2.m_transaction

在验证环境中,一般需要对数据流进行事务级的建模,因此我们可编写m_transaction的类,将需要发送的激励进行封装,并加以约束,代码如下:

//file_name: m_transcation.sv
class m_transcation extends uvm_object;rand bit[1:0]  tr_sel;rand bit[7:0]  tr_a;rand bit[7:0]  tr_b; rand bit[7:0]  tr_c;`uvm_object_utils(m_transcation)constraint sel_c {tr_sel dist{3:/40, [0:2]:/60};}
endclass

3.m_ctrl_if

由于Driver和Monitor中都需要对interface中的信号进行操作,因此我们在实现编写Driver和Monitor之前,需编写interface文件,代码如下:

//file_name: m_ctrl_if.sv
interface m_ctrl_if(input logic clk, rst);logic[1:0]   sel;logic[7:0]   a;logic[7:0]   b;logic[7:0]   c;logic[31:0]  d;
endinterface

4.m_driver

Driver组件中,将transaction进行随机化并驱动至接口,由于DUT所需的数据并不复杂,本平台中的Driver承担了产生激励的功能,后续可适当进行优化;

除了需要发送激励,Driver还应与Monitor进行通信,告知其何时进行采集,因此我们使用了mailbox完成flag的交互(mailbox是一种在进程之间交换消息的机制,数据可以通过一个进程发送到mailbox,然后由另一个进程获取,所传输的数据可以是任何有效的SystemVerilog数据类型以及class),mailbox的主要方法如下,我们在Driver中使用了带阻塞的put方法。

mailbox主要方法

Driver的完整代码如下:

//file_name: m_driver.sv
class m_driver extends uvm_driver;int                flag;int                set_times;m_transcation      m_tr;mailbox#(int)      flag_fifo;virtual m_ctrl_if  m_if;`uvm_component_utils(m_driver)function new(string name, uvm_component parent);super.new(name, parent);m_tr = new();flag_fifo = new();flag = 0;endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(virtual m_ctrl_if)::get(this, "", "m_if", m_if);uvm_config_db#(int)::get(this, "", "set_times", set_times);endfunctionfunction void connect_phase(uvm_phase phase);super.contect_phase(phase);endfunctiontask main_phase(uvm_phase phase);int times;super.main_phase();phase.raise_objection(this);times = set_times;for(int i=0; i<times; i++) begin#60ns;`uvm_info(get_type_name(),$sformatf("The times[%0d] send pkt",i),UVM_NONE)send_one_pkt();flag = flag + 1;endphase.drop_objection(this);endtasktask set_flag(mailbox#(int) flag_fifo);this.flag_fifo = flag_fifo;this.flag_fifo.put(flag);endtasktask send_ont_pkt();m_tr.randomize();`uvm_info(get_type_name(),$sformatf("Send pkt as follow:::   \
                                           \n tr_sel   ---%0d      \
                                           \n tr_a     ---%0d      \
                                           \n tr_b     ---%0d      \
                                           \n tr_c     ---%0d",m_tr.tr_sel,m_tr.tr_a,m_tr.tr_b,m_tr.tr_c),UVM_NONE)m_if.sel = m_tr.tr_sel;m_if.a   = m_tr.tr_a;m_if.b   = m_tr.tr_b;m_if.c   = m_tr.tr_c;endtaskendclass

5.m_monitor

Monitor组件中,主要的task包括:从接口获取获取数据、checker数据是否正确、从mailbox信箱中获取flag(使用get方法),其代码如下:

//file_name: m_monitor.sv
class m_monitor extends uvm_monitor;int                flag;int                set_times;mailbox#(int)      flag_fifo;virtual m_ctrl_if  m_if;`uvm_component_utils(m_monitor)function new(string name, uvm_component parent);super.new(name, parent);flag_fifo = new();flag = 0;endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(virtual m_ctrl_if)::get(this, "", "m_if", m_if);uvm_config_db#(int)::get(this, "", "set_times", set_times);endfunctionfunction void connect_phase(uvm_phase phase);super.contect_phase(phase);endfunctiontask main_phase(uvm_phase phase);int times;super.main_phase();phase.raise_objection(this);times = set_times;for(int i=0; i<times; i++) beginwhile(flag == i) begin#5ns;end`uvm_info(get_type_name(),$sformatf("The times[%0d] get pkt",i),UVM_NONE)get_one_pkt();endphase.drop_objection(this);endtasktask get_flag(mailbox#(int) flag_fifo);this.flag_fifo = flag_fifo;this.flag_fifo.get(flag);endtasktask data_checker(bit[1:0] mode, bit[7:0] data_i0, bit[7:0] data_i1, bit[7:0] data_i2, bit[31:0] data_o);case(mode)0: beginif(data_o == data_i0)`uvm_info(get_type_name(),$sformatf("The data check pass!!!"),UVM_NONE)else`uvm_error(get_type_name(),$sformatf("The data check fail!!!"))end1: beginif(data_o == data_i1)`uvm_info(get_type_name(),$sformatf("The data check pass!!!"),UVM_NONE)else`uvm_error(get_type_name(),$sformatf("The data check fail!!!"))end2: beginif(data_o == data_i2)`uvm_info(get_type_name(),$sformatf("The data check pass!!!"),UVM_NONE)else`uvm_error(get_type_name(),$sformatf("The data check fail!!!"))end3: beginif(data_o == (data_i0+data_i1+data_i2))`uvm_info(get_type_name(),$sformatf("The data check pass!!!"),UVM_NONE)else`uvm_error(get_type_name(),$sformatf("The data check fail!!!"))endendcaseendtasktask get_one_pkt();bit[1:0]   sel;bit[7:0]   a;bit[7:0]   b;bit[7:0]   c;bit[31:0]  d;sel = m_if.sel;a   = m_if.a;b   = m_if.b;c   = m_if.c;d   = m_if.d;`uvm_info(get_type_name(),$sformatf("Get pkt as follow:::   \
                                           \n tr_sel   ---%0d     \
                                           \n tr_a     ---%0d     \
                                           \n tr_b     ---%0d     \
                                           \n tr_c     ---%0d     \
                                           \n tr_d     ---%0d",sel,a,b,c,d),UVM_NONE)data_checker(sel, a, b, c, d);$display();endtaskendclass

6.m_env

Env组件中,创建了top_flag_fifo,即Driver和Monitor之间的mailbox,将其传入两个底层组件中,调用Driver的set_flag和Monitor的get_flag后可实现正常通信,Env代码如下:

//file_name: m_env.sv
class m_env extends uvm_env;m_driver           m_drv;m_monitor          m_mon;mailbox#(int)      top_flag_fifo;`uvm_component_utils(m_env)function new(string name, uvm_component parent);super.new(name, parent);top_flag_fifo = new();endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);m_drv = m_driver::type_id::create("m_drv",this);m_mon = m_monitor::type_id::create("m_mon",this);endfunctionfunction void connect_phase(uvm_phase phase);super.contect_phase(phase);endfunctiontask main_phase(uvm_phase phase);super.main_phase();phase.raise_objection(this);repeat(3000) beginforkm_drv.set_flag(top_flag_fifo);m_mon.get_flag(top_flag_fifo);join1ns;endphase.drop_objection(this);endtaskendclass

7.m_base_test

在编写base_test之前,可以编写一个test_pkg文件,将编译所需的uvm_pkg以及sv文件进行打包,后续在base_test中include即可,具体如下:

//file_name: m_test_pkg.sv
`include "uvm_macros.svh"
import uvm_pkg::*;`include "m_transcation.sv"
`include "m_driver.sv"
`include "m_monitor.sv"
`include "m_env.sv"

base_test中通过config_db机制向下set了virtual interface以及发送激励的次数,底层组件利用config_db::get后可进行相应操作,具体的代码如下:

//file_name: m_base_test.sv
`include "m_test_pkg.sv"class m_base_test extends uvm_test;int     set_tims;m_env   top_env;`uvm_component_utils(m_base_test)function new(string name, uvm_component parent);super.new(name, parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);set_times = $urandom_range(10,35);uvm_config_db#(int)::set(this, "top_env.m_drv", "set_times", set_times);uvm_config_db#(int)::set(this, "top_env.m_drv", "set_times", set_times);top_env = m_env::type_id::create("top_env",this);endfunctionvirtual function void connect_phase(uvm_phase phase);super.contect_phase(phase);endfunctionvirtual task pre_main_phase(uvm_phase phase);super.pre_main_phase();`uvm_info(get_type_name(),$sformatf("--------------------"),UVM_NONE)`uvm_info(get_type_name(),$sformatf("-----TEST START-----"),UVM_NONE)`uvm_info(get_type_name(),$sformatf("The set_times is %0d", set_times),UVM_NONE)$display();endtaskvirtual task main_phase(uvm_phase phase);super.main_phase();endtaskvirtual task post_main_phase(uvm_phase phase);super.post_main_phase();`uvm_info(get_type_name(),$sformatf("-----TEST END-------"),UVM_NONE)`uvm_info(get_type_name(),$sformatf("--------------------"),UVM_NONE)$display();endtaskendclass

8.tb_top

在上述各文件都编写完成之后,我们在tb_top中实例化接口,完成验证平台与DUT的连接,通过run_test全局函数指定用例,UVM会自动实例化case,名字固定为uvm_test_top,并自动执行case中的build phase,自顶向下,从而形成完整的UVM树形结构,之后依次执行各个phase,完成仿真。这里需注意的是:在tb_top的initial块中对于run_test的调用应在uvm config db传递接口之后,这样才能保证接口信号在build_phase之前已经传递;

此外,还可在其他的initial块中加入dump波形等$fsdb函数,具体代码如下:

//file_name: tb_top.sv
`timescale 1ns/1ns
`include "m_ctrl_if.sv"
`include "m_base_test.sv"module simple_design input[1:0]   selinput[7:0]   ainput[7:0]   binput[7:0]   coutput[31:0] d
);reg[31:0]  d_reg;always@(*) begincase(sel)0:  d_reg = a;1:  d_reg = b;2:  d_reg = c;3:  d_reg = (a+b+c);endcaseendassign d = d_reg;endmodulemodule tb_top;reg clk;reg rst;m_ctrl_if  m_if(clk,rst);simple_design  u_simple_design(.sel   (m_if.sel);.a     (m_if.a);.b     (m_if.b);.c     (m_if.c);.d     (m_if.d));initial beginrst = 1; clk = 0;#10ns;rst = 0;endalways #10 clk = ~clk;initial beginuvm_config_db#(virtual m_ctrl_if)::set(uvm_root::get(), "*", "m_if", m_if);run_test("m_base_test");endinitial begin$fsdbDumpfile("simple_platform0.fsdb");$fsdbDumpvars(0, tb_top);$fsdbDumpSVA;$fsdbDumpMDA;endendmodule

至此,一个简易的UVM验证平台已搭建完成,仿真时通过Makefile脚本控制即可,Makefile写法不是我们本文的重点,为防止文章篇幅过长,在这里暂不给出,若有需要的同学可联系我!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/862812.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react学习之antd

antd为 Web 应用提供了丰富的基础 UI 组件,我们还将持续探索企业级应用的最佳 UI 实践。它最初是基于 React 的组件库,但随着技术的发展,现在也提供了基于 Vue.js 的版本——Antd Vue。无论你是 React 还是 Vue 的开发者,都可以利用 Antd 来丰富你的 Web 应用界面。antd 为…

windos server添加新用户

以Windows Server 2022举例子(该机器本身是一台轻量应用服务器)。 首先添加新用户:以管理员身份登录,选择“更改账户设置”: 选择 “其他用户” --> “将其他人添加到这台电脑”: 选择 “用户”: 单击鼠标右键,选择 “新用户”: 填写要创建的新用户信息: 选…

LLM2Vec: 解锁大语言模型的隐藏能力

LLM2Vec:重新定义大语言模型在自然语言处理中的应用一种名为 ** LLM2Vec ** 的新方法正在改变我们对大语言模型(LLMs)在自然语言处理(NLP)中的使用方式。 研究人员提出了一种创新方法,将通常仅用于生成文本的大型语言模型转化为更强大的文本理解和组织工具。这项技术有可…

将未来帧中的点 pts​ 对齐到当前帧

已知当前的rt矩阵,和未来下一帧的rt矩阵和未来下一帧的5个点pts,求把pts对齐到当前帧。import numpy as npdef align_points(rt_current, rt_future, pts_future):# 计算从未来帧到当前帧的相对变换矩阵rt_relative = np.linalg.inv(rt_current) @ rt_future# 将点转换为齐次…

基于甘特图的任务调度与跟踪工具

在当今复杂的项目管理领域,工具的种类繁多且功能各异。常见的项目管理工具包括禅道、Trello、Jira、Microsoft Project等。这些工具在不同层面上助力项目管理,从任务分配到进度跟踪,从团队协作到资源管理。甘特图作为一种直观且有效的任务调度与跟踪工具,在众多项目管理工具…

2024年项目管理软件的创新突破:数据分析与自动化的结合

项目管理软件在现代企业中的作用愈加重要,尤其是在快速变化和竞争激烈的环境中。随着技术不断发展,项目管理软件也在不断创新,以适应新的工作方式和需求。2024年,项目管理软件将不仅仅是一个任务管理工具,而是一个集成化平台,支持团队协作、数据分析、资源优化等多项功能…

分布匹配蒸馏:扩散模型的单步生成优化方法研究

扩散模型在生成高质量图像领域具有显著优势,但其迭代去噪过程导致计算开销较大。分布匹配蒸馏(Distribution Matching Distillation,DMD)通过将多步扩散过程精简为单步生成器来解决这一问题。该方法结合分布匹配损失函数和对抗生成网络损失,实现从噪声图像到真实图像的高效…

服务器负载过高,硬件升级后仍无法访问网站

当您的服务器因负载过高提示需要升级,且在完成硬件升级后仍然无法访问网站时,可能是由于多个方面的问题导致的。为了确保网站能够正常运行,请按照以下步骤进行排查和优化: 1. 检查Web服务状态 首先,确保Web服务器(如Apache、Nginx)正在运行。通过SSH登录到服务器,使用命…

配置无误的网站无法访问 - 云服务器问题

当遇到配置看似正确却仍无法访问的情况时,可以从以下几个方面进行排查:检查80端口状态:首先确认服务器的80端口是否畅通。使用命令行工具如netstat或ss来查看端口监听情况,确保Nginx确实在监听80端口。如果发现端口未被占用,可能是因为Nginx服务未启动或配置文件中指定了错…

如何解决 Windows 服务器被平台强制修改密码后无法远程登录的问题?

,Windows 服务器被平台强制修改了远程桌面密码,导致无法通过远程桌面或控制台登录。以下是详细的排查和解决方案:重置为初始密码:平台通常会将服务器的远程桌面密码重置为初始密码。您可以登录到服务商的后台管理系统,查找并获取初始密码。 如果您忘记了初始密码,可以通过…

如何解决手机端文章列表中文章无法点击的问题?

网站手机版文章列表中文章无法点击打开,点击后页面显示空白。这种情况可能是由多种因素引起的,以下是详细的排查步骤和解决方案:检查移动端模板配置:确认网站的移动端模板是否正确配置。特别是在使用CMS系统(如WordPress、Discuz)时,确保移动端模板已启用并且配置正确。…

如何解决虚拟主机/数据库访问问题?

您好,当您遇到虚拟主机或数据库无法正常访问的问题时,通常需要从多个角度进行排查。首先,请确保您的服务器和数据库配置正确无误。如果在续费后出现登录跳转异常的情况,建议检查登录验证相关代码,确认是否有缓存或配置文件未更新导致的问题。 对于FTP密码错误的问题,您可…