UVM基础-m_sequencer和p_sequencer

2023-11-12

1.1 sequence与sequencer之间的数据交互

在UVM的sequence机制中,sequence其实是独立与验证环境之外的部件,作为uvm_object而存在,那么sequence最终是要在某个sequencer上启动的,那么启动的方式有两种:

  • 在tc中实例化sequence,并在main_phase中调用:

tar_seq.start(env.i_agt.tar_sqr)

  • 在tc中通过config_db set的方式,通过default_sequence启动:

config_db #(uvm_object_wrap)::set(this, “env.i_agt.sqr.main_phase”, “default_sequence”, tar_seq)

       此时sequence会自动调用其pre_body,body,post_body函数,一般会在body中使用`uvm_do系列宏产生tr,这个是最基本的sequence机制的用法,但本文不讨论sequence机制,先记住这两个启动的方法,重点关注通过start函数启动sequence的方式,因为通过“default_sequence”的方式启动seq,本质上也会调用seq的start函数。

       真正工程中,存在一个很常见的场景,就是sequence中想要使用sequencer里面的环境变量,用来控制sequence对tr的产生和调度,比如:

1.	class case0_sequence extends uvm_sequence#(uvm_sequence_item);
2.	      my_transaction tr;
3.	      int    pkt_data_size = 100;
4.	......
5.	      extern function void new(string name="case0_sequence");
6.	      extern virtual task pre_body();
7.	      extern virtual task body();
8.	      extern virtual task post_body();
9.	endclass
10.	
11.	function void case0_sequence::new(string name="case0_sequence");
12.	      super.new(name);
13.	endfunction
14.	
15.	task case0_sequence::pre_body();
16.	      if (starting_phase != null) 
17.	            starting_phase.raise_objection(this);
18.	endtask:pre_body
19.	
20.	task case0_sequence::body();
21.	      `uvm_do_with(tr, {tr.data_size == this.pkt_data_size;})
22.	endtask:body
23.	
24.	task case0_sequence::post_body();
25.	       if(starting_phase != null)
26.	            starting_phase.drop_objection(this);
27.	endtask:post_body

这段代码里,声明了一个变量叫pkt_data_size,在body函数中将其作为约束传递给uvm_do宏,但其实这样的传递本身是没有任何意义的,而更加常见的场景是,pkt_data_size其实是一个变量,这个变量最好从sequencer中获取,这样tc中就可以改变这个值,将这个值作为sequence中uvm_do_with宏的传参,来控制sequence对tr的约束。将21行的uvm_do_with宏改为:

`uvm_do_with(tr, {tr.data_size == tar_sqr.pkt_data_size})

       但是,sequence机制中,其实允许在sequence中再实例化一个sequencer,实现这个功能,这会使得uvm的component和object变得极其混乱,不符合验证方法论极简的思想。工程中的做法,是要在sequence中使用`uvm_declare_p_sequencer的宏,将自己的sequencer指定过去,然后调用p_sequencer的变量pkt_data_size,即可实现对sequence的变量控制,如下图代码块:

1.	class case0_sequence extends uvm_sequence#(uvm_sequence_item);
2.	      my_transaction tr;
3.	      `uvm_declare_p_sequencer(my_sqr);
4.	......
5.	      extern function void new(string name="case0_sequence");
6.	      extern virtual task pre_body();
7.	      extern virtual task body();
8.	      extern virtual task post_body();
9.	endclass
10.	
11.	function void case0_sequence::new(string name="case0_sequence");
12.	      super.new(name);
13.	endfunction
14.	
15.	task case0_sequence::pre_body();
16.	      if (starting_phase != null) 
17.	            starting_phase.raise_objection(this);
18.	endtask:pre_body
19.	
20.	task case0_sequence::body();
21.	      `uvm_do_with(tr, {tr.data_size == p_sequencer.pkt_data_size;})
22.	endtask:body
23.	
24.	task case0_sequence::post_body();
25.	       if(starting_phase != null)
26.	            starting_phase.drop_objection(this);
27.	endtask:post_body
28.	
29.	
30.	......
31.	class my_sqr extends uvm_sequencer(uvm_sequence_item);
32.	      int pkt_data_size = 100;
33.	      `uvm_component_utils_begin(my_sqr)
34.	            `uvm_field_int(pkt_data_size, UVM_ALL_ON)
35.	      `uvm_component_utils_end
36.	
37.	       function void new(string name = "my_sqr", uvm_component parent);
38.	             super.new(name, parent);
39.	       endfunction
40.	......
41.	endclass

这样既可以实现sequence中使用sequencer的变量控制了,但其实这里引入了p_sequncer的概念。

1.2 sequence在 sequencer上启动

       那么sequence是如何在sequencer上启动的?其实在整个sequence代码的编写过程中,是没有任何关于sequencer的信息的,只有在启动sequence的时候,调用start函数,会将自己例化的sequencer传递给start作为参数。其实问题的根因就在这个start函数上,start函数是uvm_sequence的基类中uvm_sequence_base中的函数:

 

这个函数第一个参数是uvm_sequencer_base类型的参数,将tar_sqr作为实参传递进去,其实是一个父类句柄指向子类的对象的过程。注意函数体里直接调用了set_item_context,这个函数其实是uvm_sequence_base的基类uvm_sequence_item的函数:

 

 

       这个函数有两个参数,第二个参数为设置传递的实参tar_sqr,注意这里会调用set_sequencer函数,这个函数同样在uvm_sequence_item中:

 

这里会将sequencer赋值给m_sequencer,m_sequencer其实是uvm_sequence_item中的一个uvm_sequencer_base的类声明的实例,为protected类型:

 

       看到这里,其实在sequence中,会有一个叫uvm_sequencer_base的类:m_sequencer,这个句柄可以在子类中使用,因为声明时被protected保护。然后这个m_sequencer在子类sequence调用start函数的时候,会指向uvm_sequencer_base的子类,也就是tar_sqr。这里不得不提一下uvm sequence机制中类的继承关系:

sequence继承主线:

case0_sequence => uvm_sequence => uvm_sequence_base => uvm_sequence_item

sequencer继承主线:

tar_sqr => uvm_sequencer => uvm_sequencer_param_base => uvm_sequencer_base。

看到这里,总结一下:

  • case0_sequence中有一个uvm_sequencer_base的基类m_sequencer;
  • 在调用case0_sequence的start函数的时候,会将子类tar_sqr的对象实例赋值给m_sequencer基类,也就是m_sequencer最终会指向tar_sqr的对象。
  • 通过调用start函数的其他机制,以及m_sequencer指向tar_sqr的对象,seq最终会在tar_sqr中启动,并且根据上述机制,case0_sequence中能够使用m_sequencer的成员变量。

2.1 m_sequencer和p_sequencer的关系

       从1.2节可以看到,sequence最终是通过start函数,将m_sequencer指向了tar_sqr,并成功在tar_sqr上启动的,那么是不是可以通过在case0_sequence中使用m_sequencer进行变量约束呢?如如下代码:

1.	class case0_sequence extends uvm_sequence#(uvm_sequence_item);
2.	      my_transaction tr;
3.	......
4.	      extern function void new(string name="case0_sequence");
5.	      extern virtual task pre_body();
6.	      extern virtual task body();
7.	      extern virtual task post_body();
8.	endclass
9.	
10.	function void case0_sequence::new(string name="case0_sequence");
11.	      super.new(name);
12.	endfunction
13.	
14.	task case0_sequence::pre_body();
15.	      if (starting_phase != null) 
16.	            starting_phase.raise_objection(this);
17.	endtask:pre_body
18.	
19.	task case0_sequence::body();
20.	      `uvm_do_with(tr, {tr.data_size == m_sequencer.pkt_data_size;})
21.	endtask:body
22.	
23.	task case0_sequence::post_body();
24.	       if(starting_phase != null)
25.	            starting_phase.drop_objection(this);
26.	endtask:post_body
27.	
28.	
29.	......
30.	class my_sqr extends uvm_sequencer(uvm_sequence_item);
31.	      int pkt_data_size = 100;
32.	      `uvm_component_utils_begin(my_sqr)
33.	            `uvm_field_int(pkt_data_size, UVM_ALL_ON)
34.	      `uvm_component_utils_end
35.	
36.	       function void new(string name = "my_sqr", uvm_component parent);
37.	             super.new(name, parent);
38.	       endfunction
39.	......
40.	endclass

       这样其实是不可以的,仿真会直接报错,为什么?其实是一个类的复制的问题,继承类的句柄赋值给父类的句柄,父类句柄只能指向子类继承的父类的对象,是没办法访问到子类对象本身的成员变量的,会包类空间越界访问错误。思考一下怎么做可以实现访问?答案很简单,其实借助$cast就可以,在case0_sequence中声明一个my_sqr类型的对象,通过cast方式,将m_sequencer cast给my_sqr,如下所示代码:

1.	class case0_sequence extends uvm_sequence#(uvm_sequence_item);
2.	      my_transaction tr;
3.	......
4.	      extern function void new(string name="case0_sequence");
5.	      extern virtual task pre_body();
6.	      extern virtual task body();
7.	      extern virtual task post_body();
8.	endclass
9.	
10.	function void case0_sequence::new(string name="case0_sequence");
11.	      super.new(name);
12.	endfunction
13.	
14.	task case0_sequence::pre_body();
15.	      if (starting_phase != null) 
16.	            starting_phase.raise_objection(this);
17.	endtask:pre_body
18.	
19.	task case0_sequence::body();
20.	      my_sqr tar_sqr;
21.	      $cast(tar_sar, m_sequencer)
22.	      `uvm_do_with(tr, {tr.data_size == tar_sar.pkt_data_size;})
23.	endtask:body
24.	
25.	task case0_sequence::post_body();
26.	       if(starting_phase != null)
27.	            starting_phase.drop_objection(this);
28.	endtask:post_body
29.	
30.	
31.	......
32.	class my_sqr extends uvm_sequencer(uvm_sequence_item);
33.	      int pkt_data_size = 100;
34.	      `uvm_component_utils_begin(my_sqr)
35.	            `uvm_field_int(pkt_data_size, UVM_ALL_ON)
36.	      `uvm_component_utils_end
37.	
38.	       function void new(string name = "my_sqr", uvm_component parent);
39.	             super.new(name, parent);
40.	       endfunction
41.	......
42.	endclass

这里讲下为什么能cast(m_sequencer其实是my_sqr的父类),在start调用时,已经将m_sequencer指向了my_sqr的对象,实际上时子类和子类的cast,因此可以成功,那么就可以通过tar_sqr访问my_sqr的成员变量了。

2.2 `uvm_declare_p_sequencer(my_sqr)的作用

基于2.1节的内容,其实`uvm_declare_p_sequencer(my_sqr)的作用,就是在case0_sequence中声明了一个my_sqr类型的句柄p_sequencer,将m_sequencer通过cast的方式赋值了p_sequencer,看一下这个宏的定义:

值得注意的是,在cast的过程中,其实是改写了m_set_p_sequencer这个虚方法,其实这个方法是见过的,也就是1.2节,在uvm_sequence_item基类中set_sequencer函数中:

这个函数的原型是在uvm_sequence_item基类中:

空函数直接返回。

因此,这个宏改写了uvm_sequence_item的m_set_p_sequencer函数,将m_sequencer通过cast的方式传递给p_sequencer,这样就可以在case0_sequence中调用tar_sqr的成员变量了。

总结

       其实这个问题,底层根源是一个类的赋值的问题,画个图解释下:

       case0_sequence的start函数,会调用uvm_sequence_base的start,这个start会调用uvm_sequence_item中的set_item_context函数,这个函数会调用set_sequencer函数,这个函数会干两件事,第一件是将函数的uvm_sequencer_base类型的形式参数赋值给m_sequencer,m_sequencer是在uvm_sequence_item中的一个uvm_sequencer_base类型的句柄,另一件事是调用m_set_p_sequencer虚函数,这个函数本身在uvm_sequence_item基类中没有声明函数体。在start的过程中,会将m_sequencer句柄指向my_sqr的对象。

       如果在case0_sequence中声明了`uvm_declare_p_sequencer宏,会干两件事,一件是声明一个my_sqr类型的p_sequencer;另一件是这个函数会重载uvm_sequence_item中m_set_p_sequencer函数,函数体中将m_sequencer赋值给p_sequencer,为什么能将父类赋值给子类,因为在赋值的过程中,m_sequencer已经指向了子类my_sqr,同类可以cast。

       通过这套机制,在case0_sequence中就可以调用sequencer中的成员变量,实现对tr的约束控制,以及其他tr或者sequence的调度和控制。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UVM基础-m_sequencer和p_sequencer 的相关文章

  • 各种FIFO硬件设计(FIFO概念、异步、同步、非2次幂深度FIFO)

    文章目录 一 FIFO概述 二 FIFO分类 三 FIFO重要信号与参数 3 1 信号 3 2 参数 3 2 1 data depth的确定 四 FIFO存储原理 五 同步FIFO 5 1 空满信号判断 5 2 同步FIFO源码 5 3 测
  • 平头哥(T-Head )开源RISCV处理器OpenC906 RTL仿真

    在过去的几年里 阿里集团平头哥陆续推出了几款RISCV处理器 有些处理器已经在产业界得到了应用 比如在某志的D1处理器中 就嵌入了平头哥的玄铁C906内核为 芯 RISCV虽然是一个开放标准 并且网络上也不乏一些开源核的RTL实现 但是商用
  • Vivido添加pynq-Z2开发板

    一 下载pynq z2开发板文件 下载地址 https www tulembedded com FPGA ProductsPYNQ Z2 html 二 将下载的文件解压到vivado安装的位置 如果boards目录下面没有boards fi
  • modelsim 关联 notepad++

    modelsim 控制窗口敲入 1 proc external editor filename linenumber exec I notepad notepad exe filename 2 set PrefSource altEdito
  • 紫光同创 FPGA 开发跳坑指南(三)—— 联合 Modelsim 仿真

    Modelsim 是 FPGA 开发中重要的 EDA 设计仿真工具 主要用于验证数字电路设计是否正确 紫光 Pango Design Suite 开发套件支持联合 Modelsim 仿真 这里作简要的介绍 添加仿真库 方法一 打开 Pang
  • FPGA的基本设计流程

    FPGA开发主要包括系统设计 设计输入 功能仿真 综合优化 综合后仿真 实现与布局布线 时序方针与验证 板级方针与验证 芯片编程与调试等9个部分 如下图所示 1 电路设计 在系统设计之前 首先要进行的是方案论证 系统设计和FPGA芯片选择等
  • 最详细的Vivado安装教程

    V i v a d o 安 装
  • 跨时钟域处理方法(一)——打拍

    一 说明 处理跨时钟域的数据可以分为单bit数据和多bit数据 而打拍的方法主要常见于处理单bit数据的跨时钟域问题 打拍的本质是通过添加寄存器 对输入的数据进行延拍 其主要目标是消除亚稳态的影响 常见的是打2拍 也就是添加2级寄存器 二
  • 实验1-FPGA编程入门

    文章目录 一 认识全加器 二 输入原理图实现1位加法器 一 半加器原理图输入 二 全加器原理图输入 三 Verilog语言实现全加器 四 总结 五 资料参考 一 认识全加器 一 半加器 1 逻辑分析 如下图所示 半加器只有两个输入和两个输出
  • TestBench编写_激励产生

    TestBench编写 激励产生 TestBench编写 激励产生 基本背景 读取函数介绍 a fopen函数使用 b fread函数使用 c fclose函数使用 实际使用 TestBench编写 激励产生 基本背景 最近遇到项目中需要对
  • 【FPGA】面试问题及答案整理合集

    面试问题及答案整理合集 1 硬件描述语言和软件编程语言的区别 2 FPGA选型问题 3 建立时间和保持时间问题 3 亚稳态问题 4 竞争和冒险问题 5 乒乓操作问题 6 同步和异步逻辑电路 7 同步复位和异步复位 8 MOORE 与 MEE
  • Verilog HDL——Modelsim仿真

    常用testbench语法 finish 和 stop finish任务用于终止仿真并跳出仿真器 stop任务则用于中止仿真 timescale time unit time precision time unit指定计时和延时的测量单位
  • 【Xilinx Vivado时序分析/约束系列4】FPGA开发时序分析/约束-实验工程上手实操

    目录 建立工程 添加顶层 模块1 模块2 添加约束文件 编辑时钟约束 打开布线设计 代码代表的含义 时序报告 进行时序分析 Summary 包含了汇总的信息量 Source Clock Path 这部分是表示Tclk1的延时细节 Data
  • 【数字IC】从零开始的Verilog SPI设计

    从零开始的Verilog SPI协议设计 一 写在前面 1 1 协议标准 1 2 数字IC组件代码 1 3 设计要求 1 4 其他协议解读 1 4 1 UART协议 1 4 2 SPI协议 1 4 3 I2C协议 1 4 4 AXI协议 二
  • FPG—VGA显示器字符显示(附代码)

    目录 1 实操 1 1 字符取模 1 2 顶层模块 1 3 图像数据生成模块 1 3 1 模块框图 1 3 2 波形图绘制 1 3 3 代码编写 1 3 4 仿真验证 2 总结 本例程大部分与VGA显示驱动内容相同 只是显示部分改变了 故此
  • [HDLBits] Exams/ece241 2014 q7a

    Design a 1 12 counter with the following inputs and outputs Reset Synchronous active high reset that forces the counter
  • 使用七牛云进行文件上传

    目录 一 七牛云入门测试 1 注册七牛云账号 完成后选择对象存储 2 在里面创建空间 一个空间相当于一个文件夹 就是将对象上传到的地方 3 查看个人秘钥 注册完成账号后 会有一个秘钥 上传文件的时候进行授权和认证 4 文件上传测试 二 封装
  • 【电子技术】什么是LFSR?

    目录 0 前言 1 数学基础 1 1 逻辑异或 1 2 模2乘法 和 模2除法 2 线性反馈移位寄存器LFSR 3 抽头和特征多项式 4 阶线性反馈移位寄存器实例 0 前言 线性反馈移位寄存器 Linear Feedback Shift R
  • Matlab图像处理系列——图像复原之噪声模型仿真

    微信公众号上线 搜索公众号 小灰灰的FPGA 关注可获取相关源码 定期更新有关FPGA的项目以及开源项目源码 包括但不限于各类检测芯片驱动 低速接口驱动 高速接口驱动 数据信号处理 图像处理以及AXI总线等 本节目录 一 图像复原的模型 二
  • MINI-UTDE 10 BASE-T 集成控制器

    MINI UTDE 10 BASE T 集成控制器 MINI UTDE 10 BASE T 拥有多达三个本地I O板和远程I OS总线通信 为用户提供了一系列生产单元功能的单一控制点 包括诸如夹头 反馈器和辅助机器等外围生产设备 支持所有主

随机推荐

  • CentOS通过nvm安装管理node

    今天搭建CentOS node 环境 原本打算源码安装 环境编译一直出错 为节省时间 直接用nvm 来下载和管理node nvm 是一个开源软件 大家可以在github 上面 下载它的源码https github com creationi
  • 【AI with ML】第 14 章 :在 iOS 应用程序中使用 TensorFlow Lite

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Applications(4)

    CONTENTS Other Applications In this section we cover a few other types of applications of deep learning that are differe
  • java使用smb操作win共享文件夹

    package com zky util import jcifs smb SmbException import jcifs smb SmbFile import jcifs smb SmbFileInputStream import j
  • 新手LearnOpenGL纹理不显示的部分解决方法

    项目场景 在LearnOpenGL学习中遇到的一些问题 照着写但是纹理加载不出来或者两张纹理只加载一张 问题描述 lt 纹理加载不出来 gt 1 文件路径是否正确 是否输出texture load fail等提示 设置了成功检查才会有提示
  • RK3568 Camera 使用

    RK3568 Camera 使用 RK3568 Sensor驱动开发移植 1 RK3568 Sensor驱动开发移植 2 RK3568 Sensor驱动开发移植 3 MIPI CSI用法 RK3568平台仅有一个标准物理mipi csi2
  • 修改pip下载源

    pip国内的一些镜像 阿里云 中国科学技术大学 豆瓣 清华大学 中国科学技术大学 修改源方法一 linux 修改 pip pip conf 如无就创建一个新的 修改内容为 global index url https pypi tuna t
  • 三、C语言进阶:二维指针

    3 二维指针 3 1 什么是二维指针 二维指针与一维指针一样都是保存变量的地址 实例 一维指针存放变量地址 二维指针存放一维指针地址 include
  • Java 多线程启动为什么调用 start() 方法而不是 run() 方法?

    多线程在工作中多多少少会用到 我们知道启动多线程调用的是 start 方法 而不是 run 方法 你知道原因吗 在探讨这个问题之前 我们先来了解一些多线程的基础知识 线程的状态 Java 中 定义了 6 种线程状态 在 Thread 类可以
  • 德标螺纹规格对照表_螺栓螺母德标 欧标 国标对照表

    新德标 旧德标 英文名 中文名 国标 DINENISO4014 DIN931 1 Hexagonheadbolts ProductgradesAandB I SO4014 1999 六角头螺栓 GB T5782 2000 DINENISO4
  • muduo网络库定时器的实现

    一 函数介绍 常见的与时间相关的函数有 sleep alarm usleep nanosleep clock nanosleep gettimer settitimer timer create timer settime timer ge
  • Golang(2)——入门语法之基本语法(函数、变量、类型(自动推导、强转)、流程控制 for、 if else、 switch、defer)

    基本语法 包 函数 var变量 const常量 类型 流程控制 更多类型 包管理 go中没有public private protected等访问控制修饰符 它是通过字母大小表示能否被其他方访问或者调用的 大写的方法就表示是可以被调用的 相
  • C ~ 指针

    指针可以简化一些 C 编程任务的执行 且一些任务 如动态内存分配 没有指针无法执行 所以 学习指针是很有必要的 每个变量都有一个内存位置 每一个内存位置都定义了可使用连字号 运算符访问的地址 它表示了在内存中的一个地址 请看下面的实例 它将
  • 应聘Java笔试时可能出现问题及其答案

    Java基础方面 1 作用域public private protected 以及不写时的区别 答 区别如下 作用域 当前类 同一package 子孙类 其他package public protected friendly private
  • Java 小例子:图书馆课程设计(Java 8 版)

    用 Java 模拟一个图书馆 包括创建图书 创建读者 借书 还书 列出所有图书 列出所有读者 列出已借出的图书 列出过期未还的图书等功能 每个读者最多只能借 3 本书 每个书最多只能借 3 个星期 超过就算过期 这个例子跟 http blo
  • 16 bit float 存储_C++:float型数据存储原理及精度丢失溢出深入解析

    电子计算机只能存储0和1 人类世界所能认识的任何数据都需要通过转换为二进制再进行存储 整数 int 型转换为二进制存储很好理解 那么float型数据计算机又是怎么存储的呢 常说的浮点型数据精度丢失和数据溢出又是怎么回事呢 浮点数示意图 位和
  • MATLAB 基础知识 数据类型 分组数组 对分类数据绘图

    本文演示了如何对分类数组中的数据绘图 加载样本数据 加载从 100 位患者收集的样本数据 load patients whos Name Size Bytes Class Attributes Age 100x1 800 double Di
  • 用Javascript实现随机抽奖

    用Javascript实现随机抽奖 思路 首先我们先把一组元素保存到数组arr中 再利用随机生成的整数和数组下标关联起来 这里的重点就是怎样获取随机的整数 下面跟着我一起来看看 HTML部分
  • Linux基础命令

    命令 关闭防火墙1 systemctl stop firewalld service 关闭防火墙程序 systemctl disable firewalld service 关闭开机自启动 关闭防火墙2 firewall cmd set d
  • UVM基础-m_sequencer和p_sequencer

    一 Sequencer问题的由来 1 1 sequence与sequencer之间的数据交互 在UVM的sequence机制中 sequence其实是独立与验证环境之外的部件 作为uvm object而存在 那么sequence最终是要在某