UVM世界观之七:phase机制(上)

本文转自:http://www.eetop.cn/blog/html/28/1561828-2331501.html

在之前SV的篇章中,读者可以看到,传统的硬件设计模型在仿真开始前,已经完成例化和连接了;而SV的软件部分,类的例化则需要在仿真开始后完成。虽然类的例化通过调用构建函数new()来实现,但是单单通过new()函数无法解决一个重要的问题,那就是验证环境层次化时,需要保证例化的先后关系,以及在确立了各个组件均完成例化后的连接。此外,如果需要实现高级功能,例如顶层到底层的配置时,也无法在底层组件例化之前,提前完成配置逻辑。因此,UVM的验证环境构建中,引入了phase机制,通过该机制,可以很清晰地将UVM仿真的阶段层次化。这里的层次化,不单单是对于各个phase的先后顺序,而且处于同一phase中的层次化组件之间的phase也有先后关系。本文将从机制和应用方面介绍phase概念,最后就UVM仿真的开始和结束方式进行阐述。

phase执行机制

如果暂时抛开phase的机制剖析,对于UVM组件的开发者而言,他们主要关心各个phase之间执行的先后顺序。在完成各个phase虚方法的实现之后,UVM环境会按照phase的顺序分别调用这些方法。

首先来看一看,UVM的phase有哪些?

phase 函数/任务 执行顺序 功能 典型应用
build 函数 自顶向下 创建和配置测试平台的结构 创建组件和寄存器模型,设置或者获取配置
connect 函数 自底向上 建立组件之间的连接 连接TLM/TLM2的端口,连接寄存器模型和adapter
end_of_elaboration 函数 自底向上 测试环境的微调 显示环境结构,打开文件,为组件添加额外配置
start_of_simulation 函数 自底向上 准备测试环境的仿真 显示环境结构,设置断点,设置初始运行的配置值
run 任务 自底向上 激励设计 提供激励、采集数据和数据比较,与OVM兼容
extract 函数 自底向上 从测试环境中收集数据 从测试平台提取剩余数据,从设计观察最终状态
check 函数 自底向上 检查任何不期望的行为 检查不期望的数据
report 函数 自底向上 报告测试结果 报告测试结果,将结果写入到文件中
final 函数 自顶向下 完成测试活动结束仿真 关闭文件,结束联合仿真引擎

关于执行的顺序,可以从下面这段简单的例码中得到佐证:

module common_phase_order;
import uvm_pkg::*;
`include "uvm_macros.svh"

class subcomp extends uvm_component;
`uvm_component_utils(subcomp)

function new(string name, uvm_component parent);
    super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
    `uvm_info("build_phase", "", UVM_LOW)
endfunction

function void connect_phase(uvm_phase phase);
    `uvm_info("connect_phase", "", UVM_LOW)
endfunction

function void end_of_elaboration_phase(uvm_phase phase);
    `uvm_info("end_of_elaboration_phase", "", UVM_LOW)
endfunction

function void start_of_simulation_phase(uvm_phase phase);
    `uvm_info("start_of_simulation_phase", "", UVM_LOW)
endfunction

task run_phase(uvm_phase phase);
    `uvm_info("run_phase", "", UVM_LOW)
endtask

function void extract_phase(uvm_phase phase);
    `uvm_info("extract_phase", "", UVM_LOW)
endfunction

function void check_phase(uvm_phase phase);
    `uvm_info("check_phase", "", UVM_LOW)
endfunction

function void report_phase(uvm_phase phase);
    `uvm_info("report_phase", "", UVM_LOW)
endfunction

function void final_phase(uvm_phase phase);
    `uvm_info("final_phase", "", UVM_LOW)
endfunction
endclass

class topcomp extends subcomp;
subcomp c1, c2;
`uvm_component_utils(topcomp)
function new(string name, uvm_component parent);
    super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
    `uvm_info("build_phase", "", UVM_LOW)
    c1 = subcomp::type_id::create("c1", this);
    c2 = subcomp::type_id::create("c2", this);
endfunction
endclass

class test1 extends uvm_test;
topcomp t1;
`uvm_component_utils(test1)

function new(string name, uvm_component parent);
    super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
    t1 = topcomp::type_id::create("t1", this);
endfunction
endclass

initial begin
//t1 = new("t1", null);
run_test("test1");
end

endmodule

输出结果:

UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top.t1 [build_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [build_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [build_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [connect_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [connect_phase]
UVM_INFO @ 0: uvm_test_top.t1 [connect_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [end_of_elaboration_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [end_of_elaboration_phase]
UVM_INFO @ 0: uvm_test_top.t1 [end_of_elaboration_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [start_of_simulation_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [start_of_simulation_phase]
UVM_INFO @ 0: uvm_test_top.t1 [start_of_simulation_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [run_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [run_phase]
UVM_INFO @ 0: uvm_test_top.t1 [run_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [extract_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [extract_phase]
UVM_INFO @ 0: uvm_test_top.t1 [extract_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [check_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [check_phase]
UVM_INFO @ 0: uvm_test_top.t1 [check_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [report_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [report_phase]
UVM_INFO @ 0: uvm_test_top.t1 [report_phase]
UVM_INFO @ 0: uvm_test_top.t1 [final_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [final_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [final_phase]

从这个例子可以看出,上面的九个phase,对于一个测试环境的声明周期而言,是有固定的执行先后顺序的;同时,对于处于同一个phase的组件之间,执行也会按照层次的顺序或者自顶向下、或者自底向上来执行。这个简单的环境中,顶层测试组件test1中,例化了一个t1组件,而t1组件内又进一步例化了c1和c2组件。从执行的打印结果来看,需要注意的地方有:

  1. 对于build phase,执行顺序按照自顶向下,这符合验证结构建设的逻辑。因为只有先创建高层的组件,才会创建空间来容纳低层的组件。
  2. 只有uvm_component及其继承与uvm_component的子类,才会按照phase机制将上面九个phase先后执行完毕。

上面介绍的九个phase中,常用的phase包括build、connect、run和report,它们分别完成了组件的建立、连接、运行和报告。这些phase在uvm_component中通过_phase的后缀完成了虚方法的定义,比如build_phase()中可以定义一些例化组件和配置的任务。在这九个phase中,只有run_phase方法是一个可以耗时的任务,这意味着该方法中可以完成一些等待、激励、采样的任务。对于其它phase对应的方法,都是函数,必须即时返回(0耗时)。

在run_phase中,用户如果要完成测试,则通常需要经历下面的激励序列:

  1. 上电
  2. 复位
  3. 寄存器配置
  4. 主要测试内容
  5. 等待DUT完成测试

一种简单的方式是,用户在run_phase中完成上面所有的激励;另外一种方式,如果可以将上面的几种典型的序列分到不同的区间,让对应的激励按区搁置的话,也能让测试更有层次。因此,run_phase又可以分为下面的12个phase:

  1. pre_reset_phase
  2. reset_phase
  3. post_reset_phase
  4. pre_configure_phase
  5. configure_phase
  6. post_configure_phase
  7. pre_main_phase
  8. main_phase
  9. post_main_phase
  10. pre_shutdown_phase
  11. shutdown_phase
  12. post_shutdown_phase

上面的12个phase的执行顺序也是前后排列的。那么这12个phase与run_phase是什么关系呢?我们通过这段例码来看看:

module uvm_phase_order;
import uvm_pkg::*;
`include "uvm_macros.svh"

class test1 extends uvm_test;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
    super.new(name, parent);
endfunction

function void start_of_simulation_phase(uvm_phase phase);
    `uvm_info("start_of_simulation", "", UVM_LOW)
endfunction

task run_phase(uvm_phase phase);
    phase.raise_objection(this);
    `uvm_info("run_phase", "entered ..", UVM_LOW)
    #1us;
    `uvm_info("run_phase", "exited ..", UVM_LOW)
    phase.drop_objection(this);
endtask

task reset_phase(uvm_phase phase);
    `uvm_info("reset_phase", "", UVM_LOW)
endtask

task configure_phase(uvm_phase phase);
    `uvm_info("configure_phase", "", UVM_LOW)
endtask

task main_phase(uvm_phase phase);
    `uvm_info("main_phase", "", UVM_LOW)
endtask

task shutdown_phase(uvm_phase phase);
    `uvm_info("shutdown_phase", "", UVM_LOW)
endtask

function void extract_phase(uvm_phase phase);
    `uvm_info("extract_phase", "", UVM_LOW)
endfunction
endclass

initial begin
    run_test("test1");
end

endmodule

输出结果:

UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top [start_of_simulation]
UVM_INFO @ 0: uvm_test_top [run_phase] entered ..
UVM_INFO @ 0: uvm_test_top [reset_phase]
UVM_INFO @ 0: uvm_test_top [configure_phase]
UVM_INFO @ 0: uvm_test_top [main_phase]
UVM_INFO @ 0: uvm_test_top [shutdown_phase]
UVM_INFO @ 1000000: uvm_test_top [run_phase] exited ..
UVM_INFO @ 1000000: uvm_test_top [extract_phase]

从这个例子可以看到,实际上,run_phase任务和上面细分的12个phase是并行进行的。在start_of_simulation_phase任务执行以后,run_phase和reset_phase开始执行,而在shutdown_phase执行完之后,需要等待run_phase执行完以后,才能进入extract_phase。关于执行的关系,可以从下面这张图中得出:

 

这里需要提醒用户的是,虽然run_phase与细分的12个phase是并行执行的,而12个phase也是按照先后顺序执行的。为了避免不必要的干扰,用户可以选择run_phase,或者12个phase中的若干来完成激励,但是请不要将它们混合起来使用,因为这样容易导致执行关系的不明确。

如果要进一步深入phase机制的话,我们首先需要清晰下面的概念:phase、schedule和domain。

  • phase即上面介绍的部分,特定的phase会完成特定的功能。
  • schedule包含phase的关联数组,即若干个phase会由schedule按照安排的顺序先后执行。
  • domain则内置一个schedule。
  • schedule类uvm_schedule和domain类uvm_domain均继承于uvm_phase。

上面首先介绍的9个phase,共同构成了common domain;而另外12个phase,则共同构成了uvm domain。无论是domain、还是phase,它们在UVM环境中都只生成一个唯一的对象。关于common domain和uvm domain的联系和区别是:

  • common domain无法被扩展或者取代,这是UVM phase机制的核心。也就是说,构成它的9个phase的顺序不能更改,也无法添加新的phase。同时,这一核心的domain也是为了与OVM的phase机制保持兼容,方便从OVM代码转换到UVM代码。
  • uvm domain则包含了上面的12个phase,其调度也是按照先后顺序执行的。对于这一部分,与common domain不同的是,它们的执行是与run_phase同时开始,并且最后共同结束的。同时,用户还可以自定义一些phase,添加到uvm domain中,设置好与其他phase执行的先后关系。

上面的common domain和uvm domain中包含的phase在uvm_pkg中例化的唯一phase实例群如下:

在详细介绍完UVM的各个phase,以及它们之间执行的顺序之后,读者可以结合之前硬件和软件的编译和例化部分,来统一理解UVM世界中的编译和运行顺序:

  1. 首先在加载硬件模型,调用仿真器之前,需要完成编译和建模阶段。
  2. 接下来,在开始仿真之前,会分别执行硬件的always/initial语句,以及UVM的调用测试方法run_test和几个phase,分别是build、connect、end_of_elaboration和start_of_simulation。
  3. 在开始仿真后,将会执行run_phase或者对应的12个细化phase。
  4. 在仿真结束后,将会执行剩余的phase,分别是extract、check、report和final。

对于使用phase机制,这里有一些建议:

  • 避免使用reset_phase()、configure_phase()、main_phase()、shutdown_phase()和其它pre_/post_ phase。这12个phase尽管细化了run_phase(),但是也使得phase的跳转过为冗余,在将来的UVM版本中,这些phase将考虑被废除。为了控制reset、configure、main和shutdown各个阶段的任务调度和同步,用户可以考虑fork-join语句块,或者高级的同步方式,例如uvm_barrier和uvm_event。
  • 避免phase的跳跃。实际上,用户可以指定个别组件的phase执行中,从phaseA跳跃到phaseC,而忽略了phaseB。但是这种方式不容易理解和调试,所以不建议使用这一特性。
  • 避免自定义phase的使用。尽管uvm domain中允许用户自定义phase,并且规定新添加phase的执行顺序,但是目前的这一机制还不方便调试。用户应该尽量将置于新phase中的任务,剥离到新的任务中,并且在已有的phase中调用它们,完成任务调用的模块化。

从之前的例子和上面的图中,读者可以看到,UVM的环境建立和各个phase的先后调用的入口,都是从run_test()进入的。默认情况下,如果run_test()方法执行完毕,那么系统函数$finish则会被调用,来终止仿真。然而,有更多的方法来分别控制UVM仿真的开始和结束,我们接下来则分别介绍这些方法的应用。

版权声明:本文为qq_41394155原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41394155/article/details/81914826

智能推荐

我的开源世界观

我是一个Python程序员,在Web开发中我会用到一大堆的开源项目,如Linux、Python、Emacs、Spacemacs、Httpie、Flask、Requests、Sentry、IPython、Pyramid、Mako、Oh My Zsh等等。无法想象没有它们,我该如何工作。 相信你看到上述列表中出现的项目名字,有些也很熟悉,甚至是经常和它们在打交道。 我们先看一个有趣的事情(2016-0...

spring IOC核心类DefaultListableBeanFactory的世界观

IOC作为Spring的核心功能其核心思想: 帮用户管理对象,对象的创建不需要再由应用实现,而是交给了spring来管理。也就是对象的控制交给了第三方,也就是控制反转的由来。 下面是Spring IOC核心工厂类的结构图 从这个类结构图我们可以看出DefaultListableBeanFactory实现了哪些功能 1、 是一个BeanDefinition注册器 2、 最重要的它是一个bean工厂 ...

UVM_sequence机制

Sequence机制 1、什么是UVM的sequence机制 控制和产生一系列的事物,并通过某种方法将事物发送给driver的机制。具有控制何时产生事物,产生事物并将事物发送(通过sequencer)给driver的功能。Sequence是一个产生和发送数据的过程,会消耗仿真时间。只有在task_phase中才会启动。 事物产生和发送流程: (1)当进入某个task_phase之后,sequenc...

linux上安装Qt4.8.6+QtCreator4.0.3

一、Qt简介 Qt是1991年奇趣科技开发的一个跨平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所有功能。Qt很容易扩展,并且允许真正地组件编程。 准备工作 操作系统:centos6.5 位数:64位 二、安装 1、获取源码Qt4.8.6 2、获取源码QtCreator4.0.3 2、安装QtCreator4.0.3 进入QtCreator安装界面,指定...

react-native metro 分析

文章目录 前言 概念 Resolution Transformation Serialization 打包方式 Moudles Plain bundle Indexed RAM bundle File RAM bundle 流程 前置流程 resolve流程 Transformer流程 序列化流程 缓存 为什么要缓存 缓存的请求与缓存 Metro配置 结构 前言 metro是一种支持ReactNa...

猜你喜欢

嵌入式Linux——应用调试:用户态打印段错误信息

简介:     很多时候我们会遇到段错误:segmentation fault,而段错误有时是由内核引起的,有时是由应用程序引起的。在内核态时,发生段错误时会打印oops信息,但是在用户态时,发生段错误却只会打印segmentation fault而并不会打印其他的信息。所以本文主要介绍在用户态时,通过修改内核设置和添加启动参数来打印引发segmentati...

springboot1.4.1整合logback 遇到的问题

springboot1.4.1整合logback 遇到的问题 项目使用了springboot1.4.1整合logback,然而设置的过期时间15 并没有生效, 2GB达到2G自动删除也没有生效,仅仅实现了按大小分割。 经过查看pom 父工程内的源码发现是默认的logback版本是1.1.7,而过期时间配置是在logback 1.1.8以后才支持的。 不得不说这是springboot1.4.1 的b...

记一次C/S架构的渗透测试

概述 目标站点是http://www.example.com,官网提供了api使用文档,但是对其测试后没有发现漏洞,目录、端口扫描等都未发现可利用的点。后发现官网提供了客户端下载,遂对其进行一番测试。 信息收集 先抓了下客户端的包,使用Fiddler和BurpSuite都抓不到,怀疑走的不是HTTP协议,用WireShark查看其确实用的是HTTP协议,但是数据包不好重放,这里最后使用了WSExp...

Linux:结合Securecrt进行文件上传(lrzsz)P2

1、安装rzsz软件   2、点击Scurecrt的option——X/Y/Z配置上传和下载目录   3、首先在Linux里切换到一个目录,然后用rz命令,文件就会上传到钙Linux的目录下   只要敲rz即可,然后在弹出的对话框里选择需要上传的文件即可 4、下载文件用sz   下载单个文件:在当前目录下有该文件     sz filename   下载...

SQL 提示作为 布局 生存工具指南

下面是一些展示AdventureWorks中表现最好的销售人员并列出他们的经理的结构化查询语言代码。 它产生以下结果。 所以,代码是有效的,但它是丑陋的。 如果我需要理解和改进代码,我首先需要把它变成可读的形式。 我有结构化查询语言提示,所以我可以按下计算机的ctrl按键键 踢你自己),它会应用默认的内置代码样式,并对此进行修复。 不,不是,因为我相信你仍然不喜欢它的格式。 没有两个开发人员能够就...