AHB Matrix 四星级 验证笔记(1.8-1.9)scoreboard的实现

文章目录

  • 前言
  • 一、scoreboard接收数据的方式和比较行为的策略选择
    • 1.接受数据的方式
      • 1. 首先是数据从哪儿来?
    • 2.比较数据的方式
      • 1.方案一
      • 2.方案二
      • 3. 方案的选择
  • 二、scoreboard的实现
    • 1.代码
  • 三、tip
    • 1.编辑断点
    • 2. return
    • 3.有关函数的返回值:函数内部隐式声明的变量


前言

  1. 来源路科验证
  2. 本节记录scoreboard接收数据方式和比较行为的策略选择以及如何实现scoreboard
  3. 三星级是在sequence里面做的数据比较,四星级在scoreboard里面;

一、scoreboard接收数据的方式和比较行为的策略选择

1.接受数据的方式

1. 首先是数据从哪儿来?

 1. **三星级**:将`sing_write`和`single_read`嵌套,对同一地址先写后读,然后比较写入的数据和读回的数据是否相同![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/aaaddd6880ef4e0e98c474c61da98562.png)2.  **四星级**:`master_agent` 和`slave_agent` 都有`monitor`可以检测数据,这样的话可以拿到输入端的数据也可以拿到输出端的数据,做数据的比较就可以了;

在这里插入图片描述

2.比较数据的方式

1.方案一

  1. master monitor检测到的数据全部更新存放在scb中的一个memory里,同理slave monitor也如此(即记录的matrix的输出数据和输入数据),之后在scb中做一个数据比较
  2. 在做数据比较时,不仅仅要考虑数据的个数,也要考虑数据的内容,还要考虑地址是否相同;首先保证个数的相同,其次考虑内容的相同;
  3. 这种方案是将数据通过关联数组记录下来

2.方案二

  1. slave agent里面都有一个 类 svt_mem::ahb_slave_mem:master vip输出的数据经过matrix后都流向了三个mem里面,(每一个slave agent里面都有一个mem,那么就有三个mem)可以通过read函数拿到这mem里面的数据
  2. master写的数据可以记录下来,比如通过一个关联数据;slave有三个mem,也可以记录数据
  3. mem记录三块还是一块?如果把整个系统按照地址寻址的话,往一个固定的地址写数据,不管是哪一个master写数据写到一定的addr_range里,最终都会路由到一个特定的slave mem里面;外部的三个slave agent是独立的,他们映射的地址范围是不一样的没有重叠的;所以可以这样理解,看起来是三块slave agent,三块独立的mem,实际上里面做成一块mem是方便作比较的(不需要三块来回做切换),在这种情况下,master检测到的数据就可以放在一个关联数据里面
  4. 这种方案master monitor将数据写在关联数组里面,slave的数据存放在slave agent里面的mem

3. 方案的选择

  1. 如果matrix这个模块级的验证环境走到子系统级别,都利用monitor的方案是完全可以复用的,因为他不依赖于vip slave agent里面的mem;后者在模块级别以上是完全无法复用的
  2. 什么时候做数据的检查?是在仿真过程中还是结束以后?在仿真的结束以后(check_phase中检查),如果仿真过程中在scb中做检查的话,光依靠master那边的数据做检查时间上不一定来得及,因为master那边的数据要穿过matrix到达slave那边,尤其像burst这种情况;
  3. 最好的方式是两端都做检查:既要检查master 关联数组 里面的地址和数据;同时也检查slave的关联数据里面的地址和数据;即检查数量也检查地址和数据的内容;
  4. 总的来说:既要考虑个数,也要考虑内容,对于第二种方案是没法计算个数的,就要单独搞一个计数器;第一种方案比较会更全面,且便于复用

二、scoreboard的实现

1.代码

  1. 首先建立scb的框架,然后在环境中例化,连接
`ifndef RKV_AHBMTX_SCOREBOARD_SV
`define RKV_AHBMTX_SCOREBOARD_SV 
//代码中的注释和预处理指令(如ifndef和endif)用于条件编译,确保代码块只被编译一次
class rkv_ahbmtx_scoreboard extends rkv_ahbmtx_subscriber;`uvm_component_utils(rkv_ahbmtx_scoreboard)function new(string name="rkv_ahbmtx_scoreboard", uvm_component parent);super.new(name, parent);endfunctionendclass`endif // RKV_AHBMTX_SCOREBOARD_SV

连接:这里连接到了一个analysis port上,那么如何区分传递过来的数据来自master还是slave

-----------------------------env::connect_phase----------------------------
foreach(ahb_mst[i]) beginahb_mst[i].monitor.item_observed_port.connect(cov.ahb_trans_observed_imp);
endforeach(ahb_mst[i]) beginahb_mst[i].monitor.item_observed_port.connect(scb.ahb_trans_observed_imp);
endforeach(ahb_slv[i]) beginahb_slv[i].monitor.item_observed_port.connect(scb.ahb_trans_observed_imp);
end

注意:这里也要将cfgenv中传递到scb中,在subscriber中已经有getcfgscb继承subscriber,所以也会继承get cfg

  1. 代码书写
    创建两个关联数组,用于存放slave monitormaster monitor传递过来的数据
 typedef bit[3:0] addr_t;//typedet 必不可好;少了addr_t就是变量,而非数据类型bit[31:0] exp_mem[addr_t];bit[31:0] act_mem[addr_t]; //关联数组里的索引类型必须是有效的数据类型

在定义关联数组的位宽时,可以将其设置为参数,如果以后的位宽发送变化的话

比较逻辑:

先检查两边的地址是否一样,在一样的基础上检查数据是否一样

function void check_phase(uvm_phase phase); //在check_phase中比较保证数据全部传输完毕super.check_phase(phase);if (cfg.enable_scb) begin   //检查的开关if (!check_mem_addr()) begin   //注意check_mem_addr需要返回值`uvm_error("CHK_MEM_ADDR", "check addr is failure!!!")end else begin`uvm_info("cHK_PHASE", "check addr is success!!!", UVM_LOW)if (!check_mem_data()) begin`uvm_error("cHK_PHASE", "check data is failure!!!")end else begin`uvm_info("cHK_PHASE", "check data is success!!!", UVM_LOW)endendend
endfunction

检查地址是否一样,先检查地址的数量,然后检查地址的内容

function bit check_mem_addr();            //有返回值int exp_mem_size = exp_mem.size();     //sizeint act_mem_size = act_mem.size();if (exp_mem_size != act_mem_size) begin`uvm_error("CHK_MEM_ADDR", $sformatf("exp_addr_size[%0d] != act_addr_size[%0d]", exp_mem_size, act_mem_size))cfg.scb_check_error++;return 0;        // Indicate failureend else begin`uvm_info("CHK_MEM_ADDR", $sformatf("exp_addr_size[%0d] == act_addr_size[%0d]", exp_mem_size, act_mem_size), UVM_LOW)endcfg.scb_check_count++;foreach (exp_mem[addr]) beginif (!act_mem.exists(addr)) begin   //使用 exists() 方法检查某个KEY是否存在`uvm_error("CHK_MEM_ADDR", $sformatf("act_mem addr[%0x] does not exist", addr))cfg.scb_check_error++;return 0;             // Indicate failureend else begin`uvm_info("CHK_MEM_ADDR", $sformatf("act_mem addr[%0x] exists", addr), UVM_LOW)cfg.scb_check_count++;//return 1;  //不能放在这里返回,return语句会导致函数立即结束执行,相当于检查了一次endend// If all checks pass, return 1 to indicate successreturn 1;
endfunction

检查数据

function bit check_mem_data();foreach (exp_mem[addr]) beginif (exp_mem[addr] != act_mem[addr]) begin`uvm_error("CHK_MEM_DATA", $sformatf("Failure!!! act_mem[%0x]::[%0d] != exp_mem[%0x]::[%0d]", addr, act_mem[addr], addr, exp_mem[addr]))// return 0;cfg.scb_check_error++;return 0;end else begin`uvm_info("CHK_MEM_DATA", $sformatf("Success!!! act_mem[%0x]::[%0d] == exp_mem[%0x]::[%0d]", addr, act_mem[addr], addr, exp_mem[addr]), UVM_LOW)cfg.scb_check_count++;// return 1;endend// If all checks pass, return 1 to indicate successreturn 1;
endfunction

如何将数据写入到关联数组里面

  virtual function void write(svt_ahb transaction tr);super.write(tr);if(cfg.enable_scb && cur_acc == MEMACC) begin //打开scb且访问的是外部的mem时,开始记录数据write_mem_from_trans(tr);endendfunctionfunction void write_mem_from_trans(svt_ahb_transaction tr);svt_ahb_master_transaction mst_trans;svt_ahb_slave_transaction slv_trans;case (tr.burst_type)svt_ahb_transaction::SINGLE:     beginif ($cast(mst_trans, tr)) begin  //通过将tr句柄转换来查看接收到的数据是哪一边的exp_mem[tr.addr] = tr.data[0];//如果tr句柄并不指向svt_ahb_master_transaction的对象,$cast 会失败end else if ($cast(slv_trans, tr)) beginact_mem[tr.addr] = tr.data[0];endenddefault:`uvm_fatal("TYPEERR", "burst type not supported")endcase
endfunction

通过tr传过来的数据类型:如果可以转换为slave_transaction; 那么就是slave monitor那边传过来;master同理(write传过来的父类,为啥是父类?driversequencer的参数是子类,monitor的无论如何都是父类

上面的代码另一种结构

virtual function void write(svt_ahb_transaction tr);svt_ahb_master_transaction_mst_tr;svt_ahb_slave_transaction slv_tr;super.write(tr);if (cfg.enable_scb && cur_acc == MEMACC) beginif ($cast(mst_tr, tr)) begin : master_monitor_to_exp_memwrite_mem_from_trans(exp_mem, tr);end else if ($cast(slv_tr, tr)) begin : slave_monitor_to_act_memwrite_mem_from_trans(act_mem, tr);endend
endfunction
//这里将关联数组作为参数传入到函数中了,注意ref的应用
function void write_mem_from_trans(ref bit[31:0] mem[addr_t], svt_ahb_transaction tr);// TODO// NOTE:: SINGLE only supported so farcase(tr.burst_type)svt_ahb_transaction::SINGLE: beginmem[tr.addr] = tr.data[0];enddefault:uvm_fatal("TYPEERR", $sformatf("burst type %s cannot be supported yet", tr.burst_type));endcase
endfunction

全部代码

class rkv_ahbmtx_scoreboard extends rkv_ahbmtx_subscriber;typedef bit [31:0] addr_t;bit [31:0] exp_mem [addr_t];bit [31:0] act_mem [addr_t];`uvm_component_utils(rkv_ahbmtx_scoreboard)function new(string name = "rkv_ahbmtx_scoreboard", uvm_component parent);super.new(name, parent);endfunctionvirtual function void write(svt_ahb_transaction tr);svt_ahb_master_transaction mst_tr;svt_ahb_slave_transaction slv_tr;super.write(tr);if (cfg.enable_scb && cur_acc == MEMACC) beginif ($cast(mst_tr, tr)) begin : master_monitor_to_exp_memwrite_mem_from_trans(exp_mem, tr);endelse if ($cast(slv_tr, tr)) begin : slave_monitor_to_act_memwrite_mem_from_trans(act_mem, tr);endendendfunctionfunction void write_mem_from_trans(ref bit[31:0] mem[addr_t], svt_ahb_transaction tr);// TODO// NOTE:: SINGLE only supported so farcase(tr.burst_type)svt_ahb_transaction::SINGLE: beginmem[tr.addr] = tr.data[0];enddefault: beginuvm_fatal("TYPEERR", $sformatf("burst type %s cannot be supported yet", tr.burst_type));endendcase
endfunctionfunction void check_phase(uvm_phase phase);super.check_phase(phase);if(cfg.enable_scb) beginif(!check_mem_addr()) beginuvm_error("CHK_MEM_ADDR", "check memory address content failed");end else beginuvm_info("CHK_MEM_ADDR", "check memory address content success", UVM_LOW);endif(!check_mem_data()) beginuvm_error("CHK_MEM_DATA", "check memory data content failed");end else beginuvm_info("CHK_MEM_DATA", "check memory data content success", UVM_LOW);endend
endfunctionfunction bit check_mem_addr();int exp_mem_size = exp_mem.size();int act_mem_size = act_mem.size();check_mem_addr = 1; // check address countif (exp_mem.size() != act_mem.size()) beginuvm_error("CHK_ADDR_CNT", $sformatf("check memory address count mismatched! exp_mem[%0d] != act_mem[%0d]", exp_mem_size, act_mem_size));endcfg.scb_check_count++;// check address contentforeach(exp_mem[addr]) beginif (!act_mem.exists(addr)) beginuvm_error("CHECK_ADDR_CTNT", $sformatf("expected memory address['h%x] does not exist in actual memory", addr));check_mem_addr = 0;endcfg.scb_check_count++;end
endfunctionfunction bit check_mem_data();check_mem_data = 1;foreach(exp_mem[addr]) beginif (exp_mem[addr] != act_mem[addr]) beginuvm_error("CHECK_DATA_CTNT", $sformatf("Failed! exp_mem['h%08x]::['h%08x]::['h%08x] != act_mem['h%08x]::['h%08x]", addr, exp_mem[addr], addr, act_mem[addr]));check_mem_data = 0;end else beginuvm_info("CHECK_DATA_CTNT", $sformatf("Success! exp_mem['h%08x]::['h%08x] == act_mem['h%08x]::['h%08x]", addr, exp_mem[addr], addr, act_mem[addr]), UVM_HIGH);endcfg.scb_check_count++;end
endfunction
endclass

三、tip

1.编辑断点

  1. cov和scb都继承subscriber,设置断点时,对于公共的部分如果只是想在scb的部分设置断点应该如何操作?
    在这里插入图片描述
    设置断点时,编辑断点指定class object:将蓝色的拖拽到class object中;
  2. 要有scb的的实例拖拽,首先勾选下图的选项,接着run 0创建实例
    在这里插入图片描述
  3. 可通过下图判断是不是正确的设置了断点
    在这里插入图片描述

2. return

return的位置很重要;当一个函数遇到return语句时;(1)如果函数被声明为返回某种类型的值(即不是void类型),return语句后面通常会跟随一个表达式,这个表达式的值会被返回给函数的调用者。(2)return语句会导致函数立即结束执行,控制流返回到调用该函数的代码处。

3.有关函数的返回值:函数内部隐式声明的变量

函数内部会隐式地声明一个与函数同名的局部变量。这个局部变量用于存储函数的返回值。因此,在函数内部看到check_mem_data这样的赋值语句时,实际上是在给这个隐式声明的局部变量赋值,而不是在修改函数本身的其他属性。

这里的check_mem_data既是函数的名称,也是函数内部隐式声明的局部变量的名称。这种设计允许你通过简单地给这个变量赋值来返回结果,而不需要显式地使用return语句。

function bit check_mem_data();check_mem_data = 1;   //将返回值初始化为1foreach(exp_mem[addr]) beginif (exp_mem[addr] != act_mem[addr]) beginuvm_error("CHECK_DATA_CTNT", $sformatf("Failed! exp_mem['h%08x]::['h%08x]::['h%08x] != act_mem['h%08x]::['h%08x]", addr, exp_mem[addr], addr, act_mem[addr]));check_mem_data = 0;  end else beginuvm_info("CHECK_DATA_CTNT", $sformatf("Success! exp_mem['h%08x]::['h%08x] == act_mem['h%08x]::['h%08x]", addr, exp_mem[addr], addr, act_mem[addr]), UVM_HIGH);endcfg.scb_check_count++;end
endfunction

在写代码的时候,要搞一些防御性的编程:在各种出问题的地方,打印一些关键的信息,方便调试;比如在get cfg句柄时的fatal,判断句柄拿到没有

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

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

相关文章

商业潜规则揭秘:从成交艺术到客户满意度的全方位策略

潜规则一:成交的艺术——七大核心原则 顾客追求的是超值感,而非单纯低价。 与顾客讨论的重点应是价值,而非价格。 客户没有绝对的对错,关键在于服务是否到位。 销售方式比销售产品本身更重要。 没有绝对最好的产品,只有…

在IDEA2024中生成SpringBoot模板

1、创建新项目 根据自己想要创建的工程类型选择,这里创建的时web工程 生成项目: 注意:SpringBoot只会扫描主程序所在的包及其下面的子包

物流行业信息化整体规划方案|117页PPT

文件是关于“物流行业信息化整体规划方案”的详细规划报告,涵盖了物流信息化咨询项目的规划报告,包括业务理解与需求分析、信息化现状分析、信息化蓝图规划以及实施路径与保障措施等多个方面。以下是对文档内容的总结: 1. 引言:信…

opencv优秀文章集合

文章目录 一、 CV领域1.1 图像处理1.2 目标检测与识别1.3 图像分割、目标追踪1.4 姿态估计1.5 3D视觉1.6 图像生成1.7 机器视觉1.8 其它 二、 nlp三、语音四、推荐系统 《OpenCV优秀文章集合》《OpenCV系列课程一:图像处理入门(读写、拆分合并、变换、注…

Windows转Mac过渡指南

最近由于工作原因开始使用mac电脑,说实话刚拿到手的时候,window党表示真的用不惯。坚持用一下午之后,发现真的yyds,这篇文章说说mac电脑的基本入门指南。 1. 不会使用mac的触摸板,接上鼠标发现滚轮和windows是反的。 …

字符串逆序(c语言)

错误代码 #include<stdio.h>//字符串逆序 void reverse(char arr[], int n) {int j 0;//采用中间值法//访问数组中第一个元素和最后一个元素//交换他们的值&#xff0c;从而完成了字符串逆序//所以这个需要临时变量for (j 0; j < n / 2; j){char temp arr[j];arr[…

安全成为大模型的核心;大模型安全的途径:大模型对齐

目录 安全成为大模型的核心 大模型安全的途径:大模型对齐 人类反馈强化学习(RLHF) 直接偏好优化(DPO) 安全成为大模型的核心 大模型安全的途径:大模型对齐 大模型对齐技术(Alignment Techniques for Large Language Models)是确保大规模语言模型(例如GPT-4)的输…

K8s企业应用之容器化迁移

#作者&#xff1a;曹付江 K8s企业应用之容器化迁移 Kubernetes&#xff08;K8s&#xff09;中的企业应用容器化迁移是一个复杂但重要的过程&#xff0c;平滑的迁移应用&#xff0c;可以让开发、运维、测试人员循序渐进的学习和掌握Kubernetes&#xff0c;通常包括以下步骤&am…

redis详细教程(3.hash和set类型)

hash Redis中的Hash是一种数据结构&#xff0c;用于存储键值对集合。在Redis中&#xff0c;Hash非常适合表示对象&#xff0c;其中对象的每个字段都对应一个键值对。以下是关于Redis中Hash的详细讲解&#xff1a; 特点&#xff1a; 1. 键值对集合&#xff1a;Hash是一个包含…

linux 安装php扩展:xlswriter

这里以xlswriter扩展为例 进入官方扩展&#xff1a;https://pecl.php.net查询自己php对应版本的扩展包 下载扩展 wget https://pecl.php.net/get/xlswriter-1.5.5.tgz 解压扩展 tar -zxvf xlswriter-1.5.5.tgz 进入扩展目录 cd xlswriter-1.5.5 查找对应php版本的phpiz…

SSID,即Service Set Identifier(服务设置的表示符号)

一、什么是SSID&#xff1f; SSID&#xff0c;即Service Set Identifier&#xff0c;是无线网络中的一个标识符&#xff0c;用于区分不同的无线网络。它可以理解为无线网络的名称&#xff0c;当我们在手机或电脑上搜索可用的无线网络时&#xff0c;就是通过SSID来识别和连接的…

LabVIEW过程控制实验平台

A3000实验平台通过LabVIEW开发&#xff0c;实现了过程控制的虚拟仿真与实时通信&#xff0c;显著提高了教学与实验的互动性和效率。该平台采用模块化设计&#xff0c;支持多种控制策略的实验教学&#xff0c;克服了传统实验设备的不足。项目背景 目前高校过程控制实验设备普遍…

强大的文本编辑器Notepad++8.4.6 最新版

Notepad最新版是一款多功能的代码编辑工具。Notepad官方版支持27种编程语言&#xff0c;涵盖C、C 、Java 、C#,、XML、 HTML,、PHP、python等等&#xff0c;能够帮助程序员提高编辑效率。Notepad软件支持python与sql代码高亮功能&#xff0c;并且免费开源&#xff0c;能够完美地…

Halcon 2D测量Metrology找线/圆/矩形/椭圆

通过2D测量&#xff0c;可以获取物体的范围、方向、角度、位置、尺寸和个数等特征。其中&#xff0c;Halcon的2D Metrology模块提供了亚像素级别的卡尺测量功能&#xff0c;可以测量的几何形状包括直线、圆、椭圆、矩形等。对于2D度量&#xff0c;必须提供要测量的对象的位置&a…

PostgreSQL的学习心得和知识总结(一百五十七)|新的 COPY 选项 LOG_VERBOSITY

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

分析 std::optional 的使用与常见错误

文章目录 引言常见错误及解决方案1. 错误使用 std::optional 变量进行算术运算2. 错误检查 std::optional 是否有值3. 忽视 std::optional 的默认值 结论 引言 std::optional 是 C17 引入的一个模板类&#xff0c;用于表示可能有也可能没有值的情况。它特别适用于函数返回值&a…

大模型中的token是什么;常见大语言模型的 token 情况

目录 大模型中的token是什么 常见大语言模型的 token 情况 大模型中的token是什么 定义 在大模型中,token 是文本处理的基本单位。它可以是一个字、一个词,或者是其他被模型定义的语言单元。简单来说,模型在理解和生成文本时,不是以完整的句子或段落为单位进行一次性处理…

深度了解flink(七) JobManager(1) 组件启动流程分析

前言 JobManager是Flink的核心进程&#xff0c;主要负责Flink集群的启动和初始化&#xff0c;包含多个重要的组件(JboMaster&#xff0c;Dispatcher&#xff0c;WebEndpoint等)&#xff0c;本篇文章会基于源码分析JobManagr的启动流程&#xff0c;对其各个组件进行介绍&#x…

深度学习模型入门教程指南

在当前的人工智能生成内容&#xff08;AIGC&#xff09;领域中&#xff0c;深度学习模型无疑是支撑其技术核心的关键组件。深度学习模型的广泛应用极大地推动了图像生成、自然语言处理和自动化工作流的发展&#xff0c;本文将从多个角度介绍深度学习模型的概念、构建过程、实际…

C语言指针的介绍

零.导言 在日常生活中&#xff0c;我们常常在外出时居住酒店&#xff0c;细心的你一定能发现酒店不同的房间上有着不同的门牌号&#xff0c;上面写着像308&#xff0c;512之类的数字。当你定了酒店之后&#xff0c;你就会拿到一个写有门牌号的钥匙&#xff0c;凭着钥匙就能进入…