【UVM学习笔记】更加灵活的UVM—通信

news2025/4/5 19:24:18

系列文章目录

【UVM学习笔记】UVM基础—一文告诉你UVM的组成部分
【UVM学习笔记】UVM中的“类”


文章目录

  • 系列文章目录
  • 前言
  • 一、TLM是什么?
  • 二、put操作
    • 2.1、建立PORT和EXPORT的连接
    • 2.2 IMP组件
  • 三、get操作
  • 四、transport端口
  • 五、nonblocking端口
  • 六、analysis端口
  • 七、monitor与scoreboard之间的通信
  • 八、使用FIFO通信
  • 总结


前言

该专题用于记录学习UVM芯片验证的过程,主要学习书籍为经典的《UVM实战》,同时也会去进行一些UVM的项目联系。


一、TLM是什么?

TLM是Transaction Level Modeling(事务级建模)的缩写。所谓transaction level是相对DUT中各个模块之间信号线级别的通信来说的。
TLM通常有三种模式:

  1. put操作,通信的发起者A把一个transaction发送给B。在这个过程中,A称为“发起者”,而B称为“目标”。A具有的端口(用方框表示)称为PORT,而B的端口(用圆圈表示)称为EXPORT。这个过程中,数据流是从A流向B的。
  2. get操作。在这个过程中,A依然是“发起者”,B依然是“目标”,A上的端口依然是PORT,而B上的端口依然是EXPORT。这个过程中,数据流是从B流向A的。PORT和EXPORT体现的是控制流而不是数据流。

在这里插入图片描述

  1. transport操作,transport操作相当于一次put操作加一次get操作,这两次操作的“发起者”都是A,目标都是B。在这个过程中,数据流先从A流向B,再从B流向A。在现实世界中, 相当于是A向B提交了一个请求(request),而B返回给A一个应答(response)。

在这里插入图片描述

二、put操作

2.1、建立PORT和EXPORT的连接

UVM中使用connect函数来建立连接关系。如A要和B通信(A是发起者),那么可以这么写:A.port.connect(B.export)。下面是A的代码部分:

class A extends uvm_component;
   `uvm_component_utils(A)

   uvm_blocking_put_port#(my_transaction) A_port;
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction

   extern function void build_phase(uvm_phase phase);
   extern virtual  task main_phase(uvm_phase phase);
endclass

function void A::build_phase(uvm_phase phase);
   super.build_phase(phase);
   A_port = new("A_port", this);
endfunction

task A::main_phase(uvm_phase phase);
endtask

然后得到B的代码:

class B extends uvm_component;
   `uvm_component_utils(B)

   uvm_blocking_put_export#(my_transaction) B_export;
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction

   extern function void build_phase(uvm_phase phase);
   extern virtual  task main_phase(uvm_phase phase);
endclass

function void B::build_phase(uvm_phase phase);
   super.build_phase(phase);
   B_export = new("B_export", this);
endfunction

task B::main_phase(uvm_phase phase);
endtask

然后在env将两者进行链接

class my_env extends uvm_env;

   A   A_inst;
   B   B_inst;
   
   
   function new(string name = "my_env", uvm_component parent);
      super.new(name, parent);
   endfunction

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);

      A_inst = A::type_id::create("A_inst", this);
      B_inst = B::type_id::create("B_inst", this);

   endfunction

   extern virtual function void connect_phase(uvm_phase phase);
   
   `uvm_component_utils(my_env)
endclass

function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   A_inst.A_port.connect(B_inst.B_export);
endfunction

2.2 IMP组件

除了TLM中定义的PORT与EXPORT外,UVM中加入了第三种端口:IMP,起作用相当于在EXPORT后进行接受操作。
添加IMP后,A的代码变为:

task A::main_phase(uvm_phase phase);
   my_transaction tr;
   repeat(10) begin
      #10;
      tr = new("tr");
      assert(tr.randomize());
      A_port.put(tr);
   end
endtask

在B中需要改动的要多一点:

class B extends uvm_component;
   `uvm_component_utils(B)

   uvm_blocking_put_export#(my_transaction) B_export;
   uvm_blocking_put_imp#(my_transaction, B) B_imp;
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction

   extern function void build_phase(uvm_phase phase);
   extern function void connect_phase(uvm_phase phase);
   extern function void put(my_transaction tr);
   extern virtual  task main_phase(uvm_phase phase);
endclass

function void B::build_phase(uvm_phase phase);
   super.build_phase(phase);
   B_export = new("B_export", this);
   B_imp = new("B_imp", this);
endfunction

function void B::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   B_export.connect(B_imp);
endfunction

function void B::put(my_transaction tr);
   `uvm_info("B", "receive a transaction", UVM_LOW) 
   tr.print();
endfunction

在上述连接关系中,IMP是作为连接的终点。在UVM中,只有IMP才能作为连接关系的终点。如果是PORT或者EXPORT作为终点,则会报错。

三、get操作

get系列端口与put系列端口在某些方面完全相反。在这种连接关系中,数据流依然是从A到B,但是A由动作发起者变成了动作接收者,而B由动作接收者变成了动作发起者。
在这里插入图片描述
B_port的类型为uvm_blocking_get_port,A_export的类型为uvm_blocking_get_export,A_imp的类型为uvm_blocking_get_imp。A的代码为:

class A extends uvm_component;
   `uvm_component_utils(A)

   uvm_blocking_get_export#(my_transaction) A_export;
   uvm_blocking_get_imp#(my_transaction, A) A_imp;
   my_transaction tr_q[$];
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction

   extern function void build_phase(uvm_phase phase);
   extern function void connect_phase(uvm_phase phase);
   extern virtual  task get(output my_transaction tr);
   extern virtual  task main_phase(uvm_phase phase);
endclass

function void A::build_phase(uvm_phase phase);
   super.build_phase(phase);
   A_export = new("A_export", this);
   A_imp = new("A_imp", this);
endfunction

function void A::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   A_export.connect(A_imp); 
endfunction

task A::get(output my_transaction tr);
   while(tr_q.size() == 0) #2;
   tr = tr_q.pop_front();
endtask

task A::main_phase(uvm_phase phase);
   my_transaction tr;
   repeat(10) begin
      #10;
      tr = new("tr");
      tr_q.push_back(tr); 
   end
endtask

在A的get任务中,每隔2个时间单位检查tr_q中是否有数据,如果有则发送出去。当B在其main_phase调用get任务时,会最终执行A的get任务。在A的connect_phase,需要把A_export和A_imp连接起来。下面是B的部分:

class B extends uvm_component;
   `uvm_component_utils(B)

   uvm_blocking_get_port#(my_transaction) B_port;
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction

   extern function void build_phase(uvm_phase phase);
   extern virtual  task main_phase(uvm_phase phase);
endclass

function void B::build_phase(uvm_phase phase);
   super.build_phase(phase);
   B_port = new("B_port", this);
endfunction

task B::main_phase(uvm_phase phase);
   my_transaction tr;
   while(1) begin
      B_port.get(tr);
      `uvm_info("B", "get a transaction", UVM_LOW) 
      tr.print();
   end
endtask

在这些连接关系中,需要谨记的是连接的终点必须是一个IMP。

四、transport端口

在这里插入图片描述
A代码如下所示:

task A::main_phase(uvm_phase phase);
   my_transaction tr;
   my_transaction rsp;
   repeat(10) begin
      #10;
      tr = new("tr");
      assert(tr.randomize());
      A_transport.transport(tr, rsp);
      `uvm_info("A", "received rsp", UVM_MEDIUM)
      rsp.print();
   end
endtask

B中需要定义一个类型为uvm_blocking_transport_imp的IMP:

class B extends uvm_component;
   `uvm_component_utils(B)

   uvm_blocking_transport_imp#(my_transaction, my_transaction, B) B_imp;
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction

   extern function void build_phase(uvm_phase phase);
   extern task transport(my_transaction req, output my_transaction rsp);
endclass

function void B::build_phase(uvm_phase phase);
   super.build_phase(phase);
   B_imp = new("B_imp", this);
endfunction

task B::transport(my_transaction req, output my_transaction rsp);
   `uvm_info("B", "receive a transaction", UVM_LOW) 
   req.print();
   //do something according to req
   #5;
   rsp = new("rsp");
endtask

env中的代码是:

function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   A_inst.A_transport.connect(B_inst.B_imp);
endfunction

在A中调用transport任务,并把生成的transaction作为第一个参数。B中的transaport任务接收到这笔transaction,根据这笔transaction做某些操作,并把操作的结果作为transport的第二个参数发送出去。A根据接收到的rsp来决定后面的行为。

五、nonblocking端口

task A::main_phase(uvm_phase phase);
   my_transaction tr;
   repeat(10) begin
      tr = new("tr");
      assert(tr.randomize());
      while(!A_port.can_put()) #10;
      void'(A_port.try_put(tr));
   end
endtask

由于端口变为了非阻塞的,所以在送出transaction之前需要调用can_put函数来确认是否能够执行put操作。can_put最终会调用B中的can_put:

六、analysis端口

UVM中还有两种特殊的端口:analysis_port和analysis_export。该端口有两点需要注意的地方:

  • 一个analysis_port(analysis_export)可以连接多个IMP,analysis_port(analysis_export)与IMP 之间的通信是一对多的通信。analysis_port(analysis_export)更像是一个广播。
  • put与get系列端口都有阻塞和非阻塞的区分。但是对于analysis_port和analysis_export来说,没有阻塞和非阻塞的概念。

一个analysis_port可以和多个IMP相连接进行通信,但是IMP的类型必须是uvm_analysis_imp,否则会报错。
在这里插入图片描述
下面是A的代码:

class A extends uvm_component;
   `uvm_component_utils(A)

   uvm_analysis_port#(my_transaction) A_ap;
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction

   extern function void build_phase(uvm_phase phase);
   extern virtual  task main_phase(uvm_phase phase);
endclass

function void A::build_phase(uvm_phase phase);
   super.build_phase(phase);
   A_ap = new("A_ap", this);
endfunction

task A::main_phase(uvm_phase phase);
   my_transaction tr;
   repeat(10) begin
      #10;
      tr = new("tr");
      assert(tr.randomize());
      A_ap.write(tr);
   end
endtask

A的代码很简单,只是简单地定义一个analysis_port,并在main_phase中每隔10个时间单位写入一个transaction。
B的代码为:

function void B::write(my_transaction tr);
   `uvm_info("B", "receive a transaction", UVM_LOW) 
   tr.print();
endfunction

在env中通过下面方式进行连接:

function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   A_inst.A_ap.connect(B_inst.B_imp);
   A_inst.A_ap.connect(C_inst.C_imp);
endfunction

上面只是一个analysis_port与IMP相连的例子。analysis_export和IMP也可以这样相连接,只需将上面例子中的uvm_analysis_port改为uvm_analysis_export就可以。

七、monitor与scoreboard之间的通信

和上一个一样,在两段分别进行定义,monitor的代码为:

task my_monitor::main_phase(uvm_phase phase);
   my_transaction tr;
   while(1) begin
      tr = new("tr");
      collect_one_pkt(tr);
      ap.write(tr);
   end
endtask

scoreboard的代码为:

function void my_scoreboard::write_monitor(my_transaction tr);
   my_transaction  tmp_tran;
   bit result;
   if(expect_queue.size() > 0) begin
      tmp_tran = expect_queue.pop_front();
      result = tr.compare(tmp_tran);
      if(result) begin 
         `uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);
      end
      else begin
         `uvm_error("my_scoreboard", "Compare FAILED");
         $display("the expect pkt is");
         tmp_tran.print();
         $display("the actual pkt is");
         tr.print();
      end
   end
   else begin
      `uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty");
      $display("the unexpected pkt is");
      tr.print();
   end
endfunction

之后在env中可以使用connect连接。
由于monitor与scoreboard在UVM树中并不是平等的兄妹关系,这里选择下面的连接方式:
在agent中声明一个ap,但是不实例化它,让其指向monitor中的ap。在env中可以直接连接agent的ap到scoreboard的imp:

agent:
class my_agent extends uvm_agent ; 
	uvm_analysis_port #(my_transaction) ap; 
	...  
	function void my_agent::connect_phase(uvm_phase phase); 
		ap = mon.ap; 
		...  
	endfunction 
endclass 
env:
function void my_env::connect_phase(uvm_phase phase); 
	o_agt.ap.connect(scb.scb_imp); 
		...  
endfunction

在上面的例子中,scoreboard只接收一路数据。但在现实情况中,scoreboard除了接收monitor的数据之外,还要接收reference model的数据。相应的scoreboard就要再添加一个 uvm_analysis_imp的IMP。此时问题就出现了,由于接收到的两路数据应该做不同的处理,所以这个新的IMP也要有一个write任务与其对应。但是write只有一个,怎么办?
可以使用宏定义的方法:

`uvm_analysis_imp_decl(_monitor)
`uvm_analysis_imp_decl(_model)
class my_scoreboard extends uvm_scoreboard;
   	my_transaction  expect_queue[$];
	uvm_analysis_imp_monitor#(my_transaction, my_scoreboard) monitor_imp; 
	uvm_analysis_imp_model#(my_transaction, my_scoreboard) model_imp;
   `uvm_component_utils(my_scoreboard)

   extern function new(string name, uvm_component parent = null);
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual task main_phase(uvm_phase phase);
endclass 

上述代码通过宏uvm_analysis_imp_decl声明了两个后缀_monitor和_model。
当与monitor_imp相连接的analysis_port执行write函数时,会自动调用write_monitor函数,而与model_imp相连接的analysis_port执行write 函数时,会自动调用write_model函数。

八、使用FIFO通信

使用fifo的方法能够让两个端口都能实现主动的接收,因此下面的例子便是利用FIFO来实现monitor和scoreboard的通信。
FIFO的本质是一块缓存加两个IMP。在monitor与FIFO的连接关系中,monitor中依然是analysis_port,FIFO中是uvm_analysis_imp,数据流和控制流的方向相同。在scoreboard与FIFO的连接关系中,scoreboard中使用blocking_get_port端口:

class my_scoreboard extends uvm_scoreboard;
   my_transaction  expect_queue[$];
   uvm_blocking_get_port #(my_transaction)  exp_port[16];
   uvm_blocking_get_port #(my_transaction)  act_port;
   `uvm_component_utils(my_scoreboard)

   extern function new(string name, uvm_component parent = null);
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual task main_phase(uvm_phase phase);
endclass 

而FIFO中使用的是一个get端口的IMP。在这种连接关系中,控制流是从scoreboard到FIFO,而数据流是从FIFO到scoreboard。

在env中连接方式如下:

function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   i_agt.ap.connect(agt_mdl_fifo.analysis_export);
   mdl.port.connect(agt_mdl_fifo.blocking_get_export);
   for(int i = 0; i < 16; i++) begin
      mdl.ap[i].connect(mdl_scb_fifo[i].analysis_export);
      scb.exp_port[i].connect(mdl_scb_fifo[i].blocking_get_export);
   end
   o_agt.ap.connect(agt_scb_fifo.analysis_export);
   scb.act_port.connect(agt_scb_fifo.blocking_get_export); 
endfunction

FIFO中有两个IMP,但是在上面的连接关系中,FIFO中却是EXPORT,这是为什么呢?实际上,FIFO中的analysis_export和blocking_get_export虽然名字中有关键字export,但是其类型却是IMP。UVM为了掩饰IMP的存在,在它们的命名中加入了export关键字。
但事实上,FIFO上的端口并不局限于上述两个,一个FIFO中有众多的端口。端口列表如下:

总结

总结来说,这一章主要讲了数据在UVM中的传递方式,学习这一章可以更好的编写灵活性更高的UVM代码。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NSSCTF [HGAME 2023 week1]simple_shellcode

3488.[HGAME 2023 week1]simple_shellcode 手写read函数shellcode和orw [HGAME 2023 week1]simple_shellcode (1) motalymotaly-VMware-Virtual-Platform:~/桌面$ file vuln vuln: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpret…

数据集(Dataset)和数据加载器(DataLoader)-pytroch学习3

pytorch网站学习 处理数据样本的代码往往会变得很乱、难以维护&#xff1b;理想情况下&#xff0c;我们希望把数据部分的代码和模型训练部分分开写&#xff0c;这样更容易阅读、也更好维护。 简单说&#xff1a;数据和模型最好“分工明确”&#xff0c;不要写在一起。 PyTor…

数据结构|排序算法(一)快速排序

一、排序概念 排序是数据结构中的一个重要概念&#xff0c;它是指将一组数据元素按照特定的顺序进行排列的过程&#xff0c;默认是从小到大排序。 常见的八大排序算法&#xff1a; 插入排序、希尔排序、冒泡排序、快速排序、选择排序、堆排序、归并排序、基数排序 二、快速…

文件或目录损坏且无法读取:数据恢复的实战指南

在数字化时代&#xff0c;数据的重要性不言而喻。然而&#xff0c;在日常使用电脑、移动硬盘、U盘等存储设备时&#xff0c;我们难免会遇到“文件或目录损坏且无法读取”的提示。这一提示如同晴天霹雳&#xff0c;让无数用户心急如焚&#xff0c;尤其是当这些文件中存储着重要的…

leetcode数组-螺旋矩阵Ⅱ

题目 题目链接&#xff1a;https://leetcode.cn/problems/spiral-matrix-ii/ 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7…

小刚说C语言刷题——第14讲 逻辑运算符

当我们需要将一个表达式取反&#xff0c;或者要判断两个表达式组成的大的表达式的结果时&#xff0c;要用到逻辑运算符。 1.逻辑运算符的分类 (1)逻辑非(!) &#xff01;a&#xff0c;当a为真时&#xff0c;&#xff01;a为假。当a为假时&#xff0c;&#xff01;a为真。 例…

WPS宏开发手册——Excel实战

目录 系列文章5、Excel实战使用for循环给10*10的表格填充行列之和使用for循环将10*10表格中的偶数值提取到另一个sheet页使用for循环给写一个99乘法表按市场成员名称分类&#xff08;即市场成员A、B、C...&#xff09;&#xff0c;统计月内不同时间段表1和表2的乘积之和&#x…

【Cursor】切换主题

右键顶部&#xff0c;把菜单栏勾上 首选项-主题-颜色主题 选择和喜欢的颜色主题即可&#xff0c;一般是“现代深色”

spring druid项目中监控sql执行情况

场景 在 Spring Boot 结合 MyBatis 的服务中&#xff0c;实现 SQL 执行覆盖情况的监控&#xff0c;可以基于Druid提供的内置的 SQL 监控统计功能。 开启监控 在 application.yml 中启用 Druid 的 stat 和 wall 过滤器&#xff0c;并配置监控页面的访问权限 …

Obsidian按下三个横线不能出现文档属性

解决方案: 需要在标题下方的一行, 按下 键盘数字0后面那个横线(英文横线), 然后回车就可以了 然后点击横线即可

pyqt SQL Server 数据库查询-优化2

1、增加导出数据功能 2、增加删除表里数据功能 import sys import pyodbc from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QListWidget, QLineEdit, QPushButton, \QTableWidget, QTableWidgetItem, QLabel, QMessageBox from PyQt6.QtGui i…

Hyperlane:高性能 Rust HTTP 服务器框架评测

Hyperlane&#xff1a;高性能 Rust HTTP 服务器框架评测 在当今快速发展的互联网时代&#xff0c;选择一个高效、可靠的 HTTP 服务器框架对于开发者来说至关重要。最近&#xff0c;我在评估各种服务器框架性能时&#xff0c;发现了一个名为 Hyperlane 的 Rust HTTP 服务器库&a…

Laravel 中使用 JWT 作用户登录,身份认证

什么是JWT&#xff1a; JWT 全名 JSON Web Token&#xff0c;是一种开放标准 (RFC 7519)。 用于在网络应用环境间安全地传输信息作为 JSON 对象。 它是一种轻量级的认证和授权机制&#xff0c;特别适合分布式系统的身份验证。 核心特点 紧凑格式&#xff1a;体积小&#x…

VBA中类的解读及应用第二十二讲:利用类判断任意单元格的类型-5

《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…

STM32F103_LL库+寄存器学习笔记13 - 梳理外设CAN与如何发送CAN报文(串行发送)

导言 CAN总线因其高速稳定的数据传输与卓越抗干扰性能&#xff0c;在汽车、机器人及工业自动化中被广泛应用。它采用分布式网络结构&#xff0c;实现多节点间实时通信&#xff0c;确保各控制模块精准协同。在汽车领域&#xff0c;CAN总线连接发动机、制动、车身系统&#xff0c…

Linux系统调用编程

文章目录 一、进程和线程二、Linux的虚拟内存管理和stm32的真实物理内存**Linux虚拟内存管理**STM32物理内存映射2. 主要区别 三、Linux系统调用函数 fork()、wait()、exec()1. fork()&#xff1a;创建子进程2. wait()&#xff1a;等待子进程状态改变3. exec()&#xff1a;替换…

游戏引擎学习第203天

回顾当前情况 在这里我将直播完成整个游戏的制作。我们现在面临一些技术上的困难&#xff0c;确实如此。我的笔记本电脑的电源接口坏了&#xff0c;所以我不得不准备了这台备用笔记本&#xff0c;希望它能够正常工作。我所以希望一切都还好&#xff0c;尽管我不完全确定是否一…

深度学习数据集划分比例多少合适

在机器学习和深度学习中&#xff0c;测试集的划分比例需要根据数据量、任务类型和领域需求灵活调整。 1. 常规划分比例 通用场景 训练集 : 验证集 : 测试集 60% : 20% : 20% 适用于大多数中等规模数据集&#xff08;如数万到数十万样本&#xff09;&#xff0c;平衡了训练数…