目录

  • 1、加入transaction
  • 2、加入env
  • 3、加入monitor
  • 4、封装成agent

开始引入reference model、monitor、scoreboard等验证平台的组件,在这些组件之间信息的传递是基于transaction的

1、加入transaction

  • transaction是抽象的概念。物理协议中的数据交换都是以帧或包为单位的,在一帧或一个包中要定义好各项参数,每个包的大小不一样。很少会有协议是以bit或byte为单位进行数据交换。
  • 以以太网为例,每个包的大小至少是64byte。这个包中要包括源地址、目的地址、包的类型、整个包的CRC校验数据等。
  • transaction就是用于模拟这种实际情况,一笔transaction就是一个包。在不同的验证平台中会有不同的transaction。一个简单的transaction的定义如下:
class my_transaction extends uvm_sequence_item;rand bit[47:0] dmac; //以太网的目的地址rand bit[47:0] smac; //以太网的源地址rand bit[15:0] ether_type; //以太网类型rand byte pload[]; //携带数据的大小rand bit[31:0] crc; //前面所有数据的校验值constraint pload_cons{pload.size >= 46;pload.size <= 1500; //数据大小限制在46~1500byte}function bit[31:0] calc_crc();return 32'h0;endfunctionfunction void post_randomize();//当某个类的实例的randomize函数被调用后执行该函数crc = calc_crc;endfunction`uvm_object_utils(my_transaction)function new(string name = "my_transaction");super.new(name);endfunction
endclass
  • UVM所有transaction都从uvm_sequence_item派生,只有这样才可使用sequence机制。
  • 代码使用了uvm_object_utils实现factory机制。在整个仿真期间my_driver是一直存在的,而my_transaction有生命周期,在仿真的某一时间产生,经过driver驱动再经过reference model处理,最终由scoreboard比较完成后其生命周期就结束了。这种类都是派生自uvm_object或uvm_object的派生类,UVM中具有这种特征的类都要使用uvm_object_utils宏来实现。

当完成transaction的定义后, 就可以在my_driver中实现基于transaction的驱动

task my_driver::main_phase(uvm_phase phase);my_transaction tr;…for(int i = 0; i < 2; i++) begintr = new("tr");assert(tr.randomize() with {pload.size == 200;}); //随机化trdrive_one_pkt(tr);//将tr的内容驱动到DUT的端口上end…
endtask
task my_driver::drive_one_pkt(my_transaction tr);bit [47:0] tmp_data;bit [7:0] data_q[$];//push dmac to data_qtmp_data = tr.dmac;for(int i = 0; i < 6; i++) begindata_q.push_back(tmp_data[7:0]);//将tr的数据压入队列data_q中,相当于打包成一个byte流tmp_data = (tmp_data >> 8); //end//push smac to data_q…//push ether_type to data_q…//push payload to data_q…//push crc to data_qtmp_data = tr.crc;for(int i = 0; i < 4; i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >> 8);end`uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);repeat(3) @(posedge vif.clk);while(data_q.size() > 0) begin@(posedge vif.clk);vif.valid <= 1'b1;vif.data <= data_q.pop_front(); //将data_q中所有数据弹出并驱动end@(posedge vif.clk);vif.valid <= 1'b0;`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask

2、加入env

  • 在验证平台加入组件之前假设这些组件已经定义好,需要考虑在验证平台的什么位置对它们进行实例化,可引入一个容器类解决这个问题——在它里面实例化各个组件。在调用run_test时传递的参数不再是my_driver而是这个容器类,即让UVM自动创建这个容器类的实例
  • 在UVM中这个容器类称为uvm_env
claa my_env extends uvm_env;my_driver drv;function new(string name = "my_env", uvm_component parent);super.new(name, parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);drv = my_driver::type_id::create("drv", this); //factory机制的实例化方式endfunction`uvm_component_utils(my_env)
endclass
  • 所有的env应该派生自uvm_env,且与my_driver一样是component类,应使用uvm_component_utils宏来实现factory的注册。
  • 上例drv的实例化方式是只有使用factory机制注册过的类才能使用这种方式实例化,才能进而使用factory机制中的重载功能。在drv实例化时,传递了两个参数名字drv和this指针,表示my_env。
  • 验证平台中的组件在实例化时都应该使用type_name::type_id::create的方式。
  • 之前通过new函数进行实例化有两个参数:实例名和parent。上例my_driver在uvm_env中实例化,所以它的父结点(parent)就是my_env;UVM通过parent的形式建立起了树形的组织结构,由run_test创建的实例是树根(my_env),且树根名为uvm_test_top(固定名)。树根生长出枝叶(my_driver),这个过程需要在my_env的build_phase中手动实现。无论是树根/树叶,都必须由uvm_component或其派生类继承而来。

整棵UVM树的结构如图所示:

  • 加入my_env后,整个验证平台中存在两个build_phase(my_env和my_driver)。在UVM的树形结构中,build_phase的执行遵照从树根到树叶的顺序,即先执行my_env的build_phase,再执行my_driver的build_phase。当把整棵树的build_phase都执行完毕后,再执行后面的phase。
  • my_driver在验证平台中的层次结构发生了变化——从树根变成了树叶,所以在top_tb中使用config_db机制传递virtual my_if时要改变相应的路径;同时,run_test的参数也从my_driver变为了my_env
initial beginrun_test("my_env");
end
initial beginuvm_config_db#(virtual my_if)::set(null, "uvm_test_top.drv", "vif", input_if);
end

set函数的第二个参数从uvm_test_top变为了uvm_test_top.drv,其中uvm_test_top是UVM自动创建的树根的名字,而drv则是在my_env的build_phase中实例化drv时传递过去的名字。如果在实例化drv时传递的名字是my_drv,那么set函数的第二个参数中也应该是my_drv:

class my_env extends uvm_env
…drv = my_driver::type_id::create("my_drv", this);
…
endclass
module top_tb;
…
initial beginuvm_config_db#(virtual my_if)::set(null, "uvm_test_top.my_drv", "vif", inpu t_if);
end
endmodule

3、加入monitor

  • 验证平台通过monitor组件监测DUT的行为,只有知道DUT的输入输出信号变化之后,才能根据这些信号变化来判定DUT的行为是否正确
  • driver负责把transaction级别的数据转变成DUT的端口级别并驱动给DUTmonitor的行为与其相对,用于收集DUT的端口数据并将其转换成transaction交给后续的组件处理
    monitor的定义:
class my_monitor extends uvm_monitor;virtual my_if vif;`uvm_component_utils(my_monitor)function new(string name = "my_monitor", uvm_component parent = null);super.new(name, parent);if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")endfunctionextern task main_phase(uvm_phase phase);extern task collect_one_pkt(my_transaction tr);
endclass
task my_monitor::main_phase(uvm_phase phase);my_transaction tr;while(1) begintr = new("tr");collect_one_pkt(tr);end
endtask
task my_monitor::collect_one_pkt(my_transaction tr);bit[7:0] data_q[$];int psize;while(1) begin@(posedge vif.clk);if(vif.valid) break;end`uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);while(vif.valid) begindata_q.push_back(vif.data);@(posedge vif.clk);end//pop dmacfor(int i = 0; i < 6; i++) begintr.dmac = {tr.dmac[39:0], data_q.pop_front()};end//pop smac...//pop ether_type...//pop payload...//pop crcfor(int i = 0; i < 4; i++) begintr.crc = {tr.crc[23:0], data_q.pop_front()};end`uvm_info("my_monitor", "end collect one pkt, print it:", UVM_LOW);tr.my_print(); //收集完transaction后打印出来
endtask
  • 所有的monitor类应该派生自uvm_monitor
  • 与driver类似,在my_monitor中也需要有一个virtual my_if;
  • uvm_monitor在仿真中是一直存在的,它是一个component,要使用uvm_component_utils宏注册;
  • 由于monitor需要时刻收集数据,所以在main_phase中使用while(1)循环来实现这一目的。

完成monitor的定义后,可以再env中对其进行实例化:

class my_env extends uvm_env;my_driver drv;my_monitor i_mon; //监测DUT的输入口my_monitor o_mon; //监测DUT的输出口...virtual function void build_phase(uvm_phase phase);super.build_phase(phase);drv = my_driver::type_id::create("drv", this);i_mon = my_monitor::type_id::create("i_mon", this);o_mon = my_monitor::type_id::create("o_mon", this);endfunction...
endclass

使用monitor的原因:

  • 第一,在一个大型的项目中,driver根据某一协议发送数据,而monitor根据这种协议收集数据, 如果driver和monitor由不同人员实现,那么可以大大减少其中任何一方对协议理解的错误;
  • 第二,在实现代码重用时,使用monitor是非常有必要的。

加入monitor的UVM树结构如图所示:

在env中实例化monitor后,要在top_tb中使用config_db将input_if和output_if传递给两个monitor:

initial beginuvm_config_db#(virtual my_if)::set(null, "uvm_test_top.drv", "vif", input_if);uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.i_mon", "vif", input_if);uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.o_mon", "vif", output_if);
end

4、封装成agent

可以发现driver和monitor的代码高度相似,其本质是因为它们处理的是同一种协议,在同样规则下做着不同的事情。由于两者的相似性,UVM中通常将二者封装在一起成为一个agent。因此不同的agent就代表了不同的协议。

class my_agent extends uvm_agent;my_driver drv;my_monitor mon;function new(string name, uvm_component parent);super.new(name, parent);endfunctionextern virtual function void build_phase(uvm_phase phase);extern virtual function void connect_phase(uvm_phase phase);`uvm_component_utils(my_agent)
endclass
function void my_agent::build_phase(uvm_phase phase);super.build_phase(phase);if(is_active == UVM_ACTIVE) begindrv = my_driver::type_id::create("drv", this);endmon = my_monitor::type_id::create("mon", this);
endfunction
function void my_agent::connect_phase(uvm_phase phase);super.connect_phase(phase);
endfunction
  • 所有agent都要派生自uvm_agent类且是component类,应使用uvm_component_utils宏来实现factory注册。
  • build_phase根据is_active的值决定是否创建driver的实例,它是uvm_agent的一个成员变量,从UVM的源代码中可以找到它的原型如下:uvm_active_passive_enum is_active = UVM_ACTIVE;
  • uvm_active_passive_enum是一个枚举类型变量, 其定义为:typedef enum bit { UVM_PASSIVE=0, UVM_ACTIVE=1 } uvm_active_passive_enum;
  • 这个枚举变量仅有两个值:UVM_PASSIVE和UVM_ACTIVE。在uvm_agent中is_active的值默认为UVM_ACTIVE,这种模式下是需要实例化driver的。 对于UVM_PASSIVE模式:如下图所示,在输出端口上不需要驱动任何信号,只需要监测信号。在这种情况下端口上只需要monitor,所以driver可以不用实例化。

在把driver和monitor封装成agent后,在env中需要实例化agent,而不需要直接实例化driver和monitor:

class my_env extends uvm_env;my_agent i_agt; //声明i_agt和o_agtmy_agent o_agt;...virtual function void build_phase(uvm_phase phase);super.build_phase(phase);i_agt = my_agent::type_id::create("i_agt", this); //实例化o_agt = my_agent::type_id::create("o_agt", this);i_agt.is_active = UVM_ACTIVE; //指定工作模式o_agt.is_active = UVM_PASSIVE;endfunction...
endclass

此时UVM树更新为:

由于agent的加入,driver和monitor的层次结构改变了,在top_tb中使用config_db设置virtual my_if时要注意改变路径:

initial beginuvm_config_db#(virtual my_if)::set(null, "uvm_test_top.i_agt.drv", "vif", input_if);uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.i_agt.mon", "vif", input_if);vm_config_db#(virtual my_if)::set(null, "uvm_test_top.o_agt.mon", "vif", output_if);
end

在加入my_agent后,UVM的树形结构越来越清晰:

  • 只有uvm_component才能作为树的结点,像my_transaction这种使用uvm_object_utils宏实现的类不能作为UVM树的结点。
  • 在my_env的build_phase中创建i_agt和o_agt的实例;在agent中创建driver和monitor的实例也是在build_phase中。build_phase从树根到树叶的执行顺序建立一棵完整的UVM树
  • UVM要求UVM树最晚在build_phase时段完成,如果在build_phase后的某个phase实例化一个component,UVM会报错。但不是只能在build_phase中执行实例化的动作,还可以在new函数中执行实例化的动作:
function new(string name, uvm_component parent);super.new(name, parent);if (is_active == UVM_ACTIVE) begindrv = my_driver::type_id::create("drv", this);endmon = my_monitor::type_id::create("mon", this);
endfunction

但这样做无法通过直接赋值的方式向uvm_agent传递is_active的值。在my_env的build_phase(或new函数)中向i_agt和o_agt的is_active赋值不会产生效果。因此i_agt和o_agt都工作在active模式(is_active默认值是UVM_ACTIVE),这与预想相悖。可以在my_agent实例化之前使用config_db语句传递is_active的值解决这个问题:

class my_env extends uvm_env;virtual function void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(uvm_active_passive_enum)::set(this,"i_agt","is_active",UVM_ACTIVE);uvm_config_db#(uvm_active_passive_enum)::set(this,"o_agt","is_active",UVM_PASSIVE);endfunction
endclass
class my_agent extends uvm_agent;function new(string name, uvm_component parent);super.new(name, parent);uvm_config_db#(uvm_active_passive_enum)::get(this, "", "is_active", is_active);if(is_active == UVM_ACTIVE) begindrv = my_driver::type_id::create("drv", this);endmon = my_monitor::type_id::create("mon", this);endfunction
endclass

因此,建议仅在build_phase中完成实例化

UVM实战 卷I学习笔记2——为验证平台加入各个组件(1)相关推荐

  1. UVM实战 卷I学习笔记10——UVM中的寄存器模型(3)

    目录 后门访问与前门访问 *UVM中前门访问的实现 后门访问操作的定义 *使用interface进行后门访问操作 UVM中后门访问操作的实现:DPI+VPI *UVM中后门访问操作接口 后门访问与前门 ...

  2. UVM实战 卷I学习笔记8——UVM验证平台的运行(2)

    目录 *build阶段出现UVM_ERROR停止仿真 *phase的跳转 phase机制的必要性 phase的调试 超时退出 *build阶段出现UVM_ERROR停止仿真 之前的代码中,如果使用co ...

  3. 《UVM实战卷Ⅰ》学习笔记 第七章 UVM中的寄存器模型(2)

    推荐另外一篇相对更为基础的UVM 寄存器相关知识梳理UVM寄存器模型笔记_IC-V的博客-CSDN博客 目录 7.3 后门访问和前门访问 1.后门访问的方式 7.4复杂的寄存器模型 7.5复杂寄存器模 ...

  4. 1.5 Hello, world! 解剖 -JSF实战 -hxzon -jsf学习笔记

    为什么80%的码农都做不了架构师?>>>    1.5 Hello, world! 解剖 -JSF实战 -hxzon -jsf学习笔记 既然已经对JSF能够解决什么问题有了初步理解, ...

  5. python3《机器学习实战系列》学习笔记----3.2 决策树实战

    前言 一.ID3算法构造决策树 1.1 背景 1.2 信息增益计算 1.3 递归生成决策树 二.使用Matplotlib注解绘制树形图 2.1 Matplotlib注解 2.2 构造注解树 三.测试和 ...

  6. 《崔庆才Python3网络爬虫开发实战教程》学习笔记(5):将爬虫爬取到的数据存储到TXT,Word,Excel,Json等文件中

    本篇博文是自己在学习崔庆才的<Python3网络爬虫开发实战教程>的学习笔记系列,此套教程共5章,加起来共有34节课,内容非常详细丰富!如果你也要这套视频教程的话,关注我公众号[小众技术] ...

  7. MySQL实战45讲学习笔记

    文章目录 MySQL实战45讲-学习笔记 01 基础架构:一条SQL查询语句是如何执行的? mysql逻辑架构 连接器 查询缓存 分析器 优化器 执行器 02 日志系统:一条SQL更新语句如何执行 r ...

  8. 《机器学习实战》kNN学习笔记

    <机器学习实战>kNN学习笔记 文章目录 <机器学习实战>kNN学习笔记 概述 优缺点 k-近邻算法的一般流程 简单案例kNN.py 在约会网站上使用k-近邻算法 归一化特征值 ...

  9. 【1】机器学习实战peter Harrington——学习笔记

    机器学习实战peter Harrington--学习笔记 综述 数据挖掘十大算法 本书结构 一.机器学习基础 1.1 机器学习 1.2 关键术语 1.3 机器学习主要任务 1.4 如何选择合适的算法 ...

最新文章

  1. php把单词切割成数组,PHP – 将单词分解为数组
  2. Android Material各种颜色设置
  3. 通信网络设计(最小生成树+图的联通)
  4. Socket阻塞,非阻塞,同步,异步
  5. Android社招最全面试题,妈妈再也不用担心我找工作了!
  6. SpringBoot - 子模块下spring-boot-configuration-processor不生效问题
  7. SpringBoot+jquery实现post提交表单并添加隐藏域属性完成编辑功能
  8. GRPC golang版源码分析之客户端(一)
  9. 一文搞定Qt读写excel以及qt读写xml数据
  10. 实现了某一个接口的匿名类的例子_java中的内部类内部接口详解,一文搞定
  11. 3583. 整数分组
  12. 拓扑一致体参数化的复杂模型的等几何分析计算重用
  13. 《巴伦周刊》:除了芯片,英特尔还是一家顶级风投
  14. 面试题整理 | 45道CSS面试题
  15. Pycharm安装scrapy以及初始化爬虫项目
  16. 51单片机游戏(俄罗斯方块)
  17. matlab导入word数据,如何将Excel数据导入MATLAB中?/excel数据导入word模板
  18. SAP IDES ECC6.0 EHP4 安装后的RZ10参数设置 减小内存 SGEN 编译组件
  19. 图灵奖得主Bengio:深度学习不会被取代,我想让AI会推理、计划和想象
  20. Android的定位策略

热门文章

  1. U盘做为系统盘安装系统,出现start booting from usb device和boot failed解决方案
  2. 华泰单因子测试之换手率类因子
  3. 第五章 大数定律及中心极限定律
  4. 香鸡排三部曲:完结篇
  5. php vox转码,php base64 编码图片,音频,视频
  6. anaconda利用pip安装module
  7. 记录CTF命令执行练习中遇到的几道题(一些PHP命令过滤的绕过方法)
  8. 沧海一声笑(最好版):也论智能的生成
  9. PAT1020 月饼 分数 25
  10. flowable 查询完成的流程_flowable流程引擎初体验,完成一个请假流程