1. 寄存器模型怎么用
1.1 概览
1.1.1 rgm 有什么用?
- 硬件、软件之间 交谈的窗口
- 读取寄存器,获取硬件的状态
- 配置寄存器,使硬件处于某个工作模式下
寄存器验证,一般放在验证工作的前面部分:先保证 寄存器配置,与 预期的硬件状态 是一致的
1.1.2 怎么学 rgm 部分
- mcdf 总线 ( lab 4 ) + rgm ,两者之间怎么搭建、配合使用?
- 设计的过程 是怎样的?
- 验证过程中
- 怎么使用 uvm rgm 提供的 一些 class
- rgm 常用的一些方法
- 用 rgm 发送激励
- 怎么用 rgm 测试硬件
- 完成寄存器读、写的覆盖;寄存器覆盖率 是什么概念
1.1.3 基本术语、概念
- 寄存器一般是 32-bit (也有更短 bit 或更长 bit 的)
- 多个寄存器 按地址排列,形成一个 寄存器块 register block
- 每个寄存器 可以包括不同的 域 field
- 每个 field :有固定的 位数
- 每个 field :有不同的 读、写 属性
- WO
- RO
- RW
- RC (clear-on-read) ,读后擦除
- W1S (write-one-to-set), 只写 1 次
- map (address map) 里面放:所有寄存器的 地址信息
- hw 中所有的 寄存器,在 rgm 中都需要有
- rgm 是软件环境中的
- 层次化的寄存器,对应着 层次化的寄存器列表
1.1.4 寄存器 与 总线
- 除 寄存器,另一个离不开的概念是 总线
- 总线用于 访问 (读、写) 寄存器
- 为什么用 总线
- 为了 复用 测试序列
1.1.5 什么是 中心化管理寄存器(国内暂时没有??)
- 手写 寄存器模型 不现实(上万个寄存器)
- rgm 是 高度格式化的,一般包括的内容是:多少位、功能、address 等
- 可以放到 xml, xls
- 既然是 高度格式化的,可以 通过脚本去实现自动化寄存器模型
- 什么是 “中心化”?
- 单一源管理
- 核心思想是什么:用结构化的文档、数据,去自动生成 寄存器模型
- 实现“中心化”,一般是怎么个流程?
- 先有 寄存器描述文件,如 xml, xls
- 然后有 自动化的工具、脚本,自动生成 寄存器模型(尽量避免手动去生成)
- 寄存器描述文件,也可以用来生成文档
1.1.6 uvm_reg 相关的概念(学习过程中,手写此模型;暂时不 自动生成)
- 关于 rgm 构建,需要用到的 几个类(extends uvm_object)
- uvm_reg_field
- uvm_reg
- 内部包含多个 uvm_reg_field
- uvm_reg_block
- 内含多个 uvm_reg (也可以包含 uvm_mem)
- uvm_reg_map: 各个 register address,读写属性
a) 读写属性 匹配,才允许访问此 reg_map
- 看代码,理解怎么用上面几个类创建寄存器?各自 在哪里例化?等
- uvm_reg 中
- 有 rand uvm_reg_field pkt_len; 前面有 rand 修饰,为什么?
- 有 virtual function build( ), 做什么?
- 对各个 field 创建 (type_id::create),配置 (configure) 域的属性
- 配置什么:多少位?从哪一个起始位开始?读写属性?reset 值是什么?等
- 对各个 field 创建 (type_id::create),配置 (configure) 域的属性
- uvm_reg 的属性在哪里配置?
- 在 address map 中配置
- uvm_reg 中
1.1.7 创建一个 rgm 的 代码实例
- mcdf 的硬件情况
- 两个寄存器:控制寄存器,状态寄存器
- 一共三个 channel,每个 channel 分别有自己的 一个 控制寄存器、状态寄存器
- 创建过程如下:
- 先创建每个 内部的 具体的 寄存器(新加)
- class ctrl_reg extends uvm_reg
- 内部包括各个 uvm_reg_field,并且用 rand 修饰
- 有 virtual function build
i) 先 create
ii) 再 configure
- class state_reg extends uvm_reg
- 内部包括状态寄存器的 各个域
- class ctrl_reg extends uvm_reg
- 然后,使用上面这些寄存器,创建 reg_block (新加)
- class mcdf_rgm extends uvm_reg_block
- 每个 channel 分别有自己的 一个 控制寄存器、状态寄存器
- 内部 reg 也是 rand 修饰
- 内部有 map
- 内部也有 build 函数, build 中内容如下
i) 先 reg 例化
ii) 再 reg configure : reg 与 当前的 reg_block 关联起来(此 reg 属于 此 reg_block)
iii) 再 reg build: reg 内部的 field build (必须手动写这些代码)
One. 因为, uvm_object 没有自动的层级调用;必须手动
iv) map 信息
One. 创建 map:map 名,map 的基地址 等
Two. 通过 add_reg:添加每个 reg 相对于 map 的基地址的偏移地址、访问属性 等
v) 最后,lock_model( );
- class mcdf_rgm extends uvm_reg_block
- 先创建每个 内部的 具体的 寄存器(新加)
- 注意点
- 创建 uvm_block 时,在其 build 函数中:需要把内部 uvm_reg_field 或 uvm_reg 的 rand 修饰提前写好
- build 函数调用,在 block 中需要手动写上 内部 uvm_reg 的 build
1.1.8 有了 以上 rgm 后,怎么使用?
- 要有 总线的适配器 adapter:因为有 总线,需要适配
- 要完成 抽象级的转化:寄存器 level 到 总线level
- 做集成
- 集成后,之前的总线激励依然可以使用;但是,为了做得更好,可以使用 rgm 写一些寄存器级别的激励
- 可以更方便地发激励、测试硬件
- 写了寄存器级别的激励后,有两个方面
- 寄存器模块有无做过检查
- 对硬件做了完备检测后,是否有寄存器的覆盖率
为了完成以上第 8 点,需要熟悉 rgm 的一些常规使用方法、典型应用场景(后续内容)
1.2 集成
拿到 sw 寄存器模型、并且有 hw 总线(即 bus-agent/hw-register)后,怎么做集成?
1.2.1 以 rgm 集成进 mcdf 总线 为例
-
mcdf :有哪些 class
- mcdf_bus_transaction (包括可随机化的数据成员)
- mcdf_bus_sequencer (seq_item_export)
- mcdf_bus_monitor
- 有 analysis_port (可以 一对多,可以也连接 predictor 等)
- monitor 从 virtual interface 上监测数据
- port 在 build_phase 创建(create)
- port 在 run_phase 阶段,监测
- 监测时,用 fork join_none
- 有 analysis_port (可以 一对多,可以也连接 predictor 等)
- mcdf_bus_driver , 完成驱动和复位
- 解析三种命令模式: IDLE, WRITE, READ
1) 在 READ 模式下,将读回的数据通过 item_done(rsp) 写回到 sequencer 和 sequence 一侧。
2) 返回前,clone rsp - 在 run_phase , 监听复位 调用 task reset_listener
- forever 语句
a) seq_item_port.get_next_item
b) 必要的操作
c) 然后,驱动 driver_bus(rsp)
d) 最后,item_done (会完成与 seq 的 finish item 完成握手)
- forever 语句
- 其他的 驱动相关 task
1) drive_read,drive_write
2) drivre_idle, drive_bus
3) task reset_listener
- 解析三种命令模式: IDLE, WRITE, READ
- mcdf_bus_agent
connect phase 连接 driver 的 seq_item_port 与 sequencer 的 seq_item_export
-
mcdf 寄存器 RTL 设计代码 (HW reg 模块)
- class ctrl_regs ,设计代码有 3 部分
i. 信号声明
ii. alwaya 语句块
复位、WRITE、READ、DEFAULT 的行为/值;
非阻塞赋值
iii. assign 端口信号驱动
- class ctrl_regs ,设计代码有 3 部分
-
软件 rgm 已经 ready
-
mcdf ( 的寄存器 RTL,hw ) 与 rgm (sw) 之间,需要有:adapter
- rgm 怎么访问、操作:通过 map 获得地址,有 uvm_reg 等一系列方法
- rgm 得到的数据类型是什么样的: uvm_reg_item
- rgm 得到的数据类型,怎么映射到 dut bus 一侧;反向怎么操作: adapter
- 必须实现的两个虚函数: reg2bus, bus2reg
- adapter 中 rsp 数据存在根据 bus 确定
有些总线有 rsp,有些总线没有 rsp
如果有 rsp,需要把 provides_responses 配置为 1
-
rgm 中的 adapter (extends object)
class reg2mcdf extends uvm_reg_adapter- uvm_sequence_item 代表 bus 一侧; uvm_reg_op 代表 rgm 一侧
- 读、写 都需要经历以下两个方法
- virtual function reg2bus
a) 预定义的,必须实现
b) 映射到 bus 一侧
c) 把 reg 一侧的 数据,转换成 bus_transaction 类型(转化为 bus 操作)
d) function 返回 uvm_sequence_item (不是 bus_transaction):父类、子类句柄自动转换 - virtual function bus2reg
a) 预定义的,必须实现
b) 转换成 uvm_reg 可以识别的 uvm_reg_op
- virtual function reg2bus
-
顶层集成: adapter, uvm rgm, mcdf (dut) agent
- 思考
i. rgm 与 adapter 在哪里集成?彼此什么关系?
ii. adapter 完成传输,需要什么(需要什么句柄)? - 步骤
- class mcdf_bus_env extends env
- build_phase
a) config_db # :尝试得到 rgm
b) 例化 rgm (type_id::create); rgm build ( ) ;
set_auto_predict( ): 没集成 predictor 之前,用这个 auto
c) 例化 adapter (create adapter) - connect phase
a) adapter 与 rgm 中 map 是关联的;adapter 与 agent 的 sequencer 关联;最终 三者关联
rgm.map.set_sequencer(agent.sequencer, adapter_name)
- build_phase
- class test1 extends uvm_test
例化 rgm (create), 例化 env
- class mcdf_bus_env extends env
- 思考
-
小结
- rgm 集成倾向于用 config_db 进行顶层传递
1) 在 test 层例化 rgm
2) 在 env 得到 顶层的例化,并进行 build - predictor 要 预测什么?
hw 寄存器变化时,rgm 中的也需要同步更新- monitor 监测到 bus 上的值,然后用这些监测到的值 去更新
i) 通过函数更新(之前的方法)
ii) 通过 predictor 更新(predictor 可以拿到 adapter 的句柄,可以用 adapter 的 bus2reg 等,更新 rgm) - 如果没有 monitor 和 predictor,也可以通过 adapter 预测: 称为 auto_predict
i) 不完善,如 读状态寄存器时,rgm 一侧怎么预测?
- monitor 监测到 bus 上的值,然后用这些监测到的值 去更新
- rgm 集成倾向于用 config_db 进行顶层传递
-
接下来,怎么利用 rgm?怎么去访问 rgm?
- 前门、后门访问什么区别?
i. 前门:通过总线,真实的物理操作
ii. 后门:通过 DPI,不通过物理总线,不是真实的(真实硬件不支持)
通过 仿真器提供的,可以访问信号的一种手段。
不耗时、速度快
- 前门、后门访问什么区别?
1.3 集成2
(续上部分:寄存器前门访问、后门访问)
1.3.1. 前门访问
1. 示例中的 uvm_reg_sequence , 继承自 uvm_sequence
a. 可以使用 `uvm_do, `uvm_do_with 等与 seq 相关的宏;也包括:
i. uvm_reg_sequence::read_reg( )/write_reg( )
ii. peek_reg( )/uvm_reg( )
b. 可以使用 与 uvm_reg 寄存器操作相关的方法
i. uvm_reg::read( )/write( ) , 参数包括:
1) path: FRONTDOOR
2. 示例代码
a. class mcdf_example_seq extends uvm_reg_sequence
i. 声明 rgm
ii. task body( ) 中
1) 可以直接通过 调用寄存器方法,进行读写
a) rgm.chnl0_ctrl_reg.read(status, data, UVM_FRONTDOOR, .parent(this) )
2) 也可以调用 uvm_reg_sequence 的方法进行读写
a) read_reg(rgm.chnl0_ctrl_reg, status, data, UVM_FRONTDOOR)
1.3.2. 后门访问 (0 时刻,立即返回;不耗时)
1. 通过 DPI 的形式(直接 get 到某些路径下的 信号值)
a. 首先,必须有 信号的 hdl 路径:路径映射
2. 示例代码
a. class mcdf_rgm extends uvm_reg_block
i. 寄存器成员 和 map 声明
ii. virtual function build
1) 路径关联:
a) add_hdl_path("hw 的绝对路径")
b) chnl0_ctrl_reg.add_hdl_path_slice(" 相对路径", 0, 32)
2) PATH 改为 BACKDOOR
iii. 最后,lock_model( )
3. 有时不需要指定 BACKDOOR
a. 当有些方法只能 backdoor 用时:peek( ), poke( )
i. 以上两者,是 read( )/write( ) 的 backdoor 形式
1.3.3. 前门和后门的对比
1. 前门
a. 反应真实的硬件状态、包括时序问题
b. 需要总线、耗时
c. 反应 地址映射关系
d. 每次总线结束后,获取一个 完整的信号包;不能是单独的域
i. 前门,怎么改寄存器的域
1) 先 read 拿到完整寄存器的值
2) 修改 某个域,write 操作
2. 后门
a. 不耗时,访问更快(0 时刻完成)
b. 可以直接 读取值、修改值
c. 可以直接 对 寄存器的域 进行修改
d. 是自动预测的
i. bus_monitor 监测不到任何值
e. 不受时序的影响(对于一些,对时序有要求的,这也可能会带来一些问题)
i. 因为,后门的 0 时刻直接读、写;不能等时序
1.3.4. 通常,前门、后门结合使用,一些典型场景
1. 场景1
a. 首先,先用 前门访问 测试所有寄存器,确认寄存器的硬件路径已调通
b. 然后,可以通过后门直接访问寄存器
2. 场景2, 有些寄存器如果只能 写一次
a. 用前门物理访问,测真实情况
3. 场景3:rgm 生成 随机值,
a. 寄存器模型中 uvm_reg, uvm_reg_field 随机值可以进行设置
i. 随机化之后的 rgm 中的 值,称为: 期望值 desired value
1) 期望,后续 hw 中的 registers 配置成这些值
b. 随机化之后,用这些 desired value 去后门访问 实际 hw 中的 寄存器
4. 与 场景3 相关的问题:怎么对 hw 寄存器做更全面的 随机配置?
a. 可以通过 寄存器模型_adapter 做配置(不够随机)
b. 能否通过 随机值,直接去配置?
i. 没有 constraint,不合理
ii. 更合理的方法
1) rgm 中的 rand 随机一遍
2) 然后,把这些随机后的 value,作为期望值
a) 用这些 期望值去配置 硬件中的寄存器
5. 场景 4:先写后读,测试一个寄存器
a. 如果在 dut 中,寄存器地址是错误的;先读后写 也不会有报错
b. 怎么测?
i. 通过前门访问,对寄存器进行配置
ii. 通过后门访问,去监测是否配置成功了
iii. 比如:前门写 后门读,后门写 前门读,等
6. 场景 5:测试状态寄存器,比如 测试反应 FIFO 余量的寄存器
a. 从总线读出来(前门访问),在时序上会晚一拍;晚一拍,不能准确反映 FIFO 余量
b. 为什么 前门访问,会有时序 晚一拍?(后门读,也会有延时)
i. hw 的 register design 中,always 语句块
1) <= 赋值,意味着一拍的延时
2) 多个 <= ,意味着 多拍延时
c. 应该怎么测试 状态寄存器?
i. 直接映射、监测一些内部重要信号,反映即时的信息
1.3.5. register model 通过直接调用以上的函数,可以快速产生 sequence;后续,rgm 怎么做预测的?怎么做检查?
1.4 常规方法
(续上部分:register model 已经可以通过 uvm_reg, uvm_reg_sequence 的快速产生 sequence;
后续,rgm 怎么做监测的?怎么做检查?)
1.4.1. 寄存器的 域,每个域 都有两个值:镜像值 mirror value、期望值
1. sw, rgm 一侧
a. 镜像值, mirror value:根据监测到的硬件 value,去更新 mirror value
i. 只有做 prediction 的时候,才能去更新 mirror value
ii. mirror value 可能监测不到,不一定 与 硬件 actual value 一样
b. 期望值, desired value
i. 随机化之后的 rgm 中的 值,称为: 期望值 desired value
1) 期望,后续 hw 中的 registers 配置成这些值
2) 首先,更新 rgm model desired 值;然后,更新 hw 值;然后,采集 hw 的值,更新 rgm value 值
2. 硬件的
a. 实际值 actual value
3. 镜像值、期望值 这两者,可能与 硬件实际值 不一致
a. rgm sw 一侧,与 hw 实际一侧,可能会存在不一致的情况
b. 状态寄存器 无法 与 硬件的实际值,保持实时一致
c. 其他路径修改了实际值,但是,没有监测,可能导致 镜像值、期望值没更新
1.4.2. 预测
1. 如何更新 镜像值、期望值?什么时候更新?
2. 前门访问是 显式预测,需要 bus_monitor,predictor
a. 更准确,是硬件实际值
3. 后门,只能使用 自动预测:缺少 bus_monitor, predictor
4. 只要通过 bus 访问,希望也要有 predictor (是 component,不是 object)。
a. predictor 在顶层例化
b. adapter 与 reg map 的句柄,都要给到 predictor
i. 需要用 adapter 的函数
ii. reg map 中的地址信息
1.4.3. 代码示例:如何使用 显示预测,更新值
1. bus_agent 中的 monitor - predictor - adapter 和 reg_map
2. class mcdf_bus_env extends uvm_env
a. 声明 agent, rgm, adapter (reg2mcdf), predictor
b. build_phase
i. create: agent
ii. create: rgm. rgm.build( )
iii. create: predictor
1) (component,需要 parent)
2) 需要指定 predictor 传输什么数据类型(与bus_agent 中的 monitor 中 analysis port 数据类型一致)
iv. adapter 与 reg map 的句柄,都要给到 predictor
1) predictor.map = rgm.map;
2) predictor.adapter = reg2mcdf_adapter;
c. connect_phase
i. adapter 与 rgm 中 map 关联;adapter 与 agent 的 sequencer 关联;最终 三者关联
1) rgm.map.set_sequencer(agent.sequencer, reg2mcdf_adapter)
ii. monitor 与 predictor 的 TLM 连接
1) agent.monitor.ap connenct(predictor.bus_in)
1.4.4. adapter 必须要有,predictor 建议要有
1. adapter 需要实现虚函数
2. predictor 只需要指定其类型
a. 声明、创建时 指定类型
1.4.5. uvm_reg 类,有一些访问方法;这些方法可以用在 前门、后门;uvm_reg_block, uvm_reg_field 也有类似的方法
1. uvm_reg_block
a. 无 read, write, peek, poke 方法
b. mirror 方法(前门、后门)
2. uvm_reg
a. read, write 方法可以用于 前门、后门
i. 以上方法,获得的是 hw bus actual_value
1) (只能得到完整的,如 32-bit;不能操作具体的位)
ii. 然后,更新 mirror value
b. peek, poke 只用于 后门
i. 也是获得值,然后,更新 mirror value
c. mirror 方法(前门、后门)
3. uvm_field
a. read, write 只可用于 后门(与 uvm_reg 不同)
b. peek, poke 也依然只可以用于 后门
4. mirror 方法
a. (在 uvm_reg_block, uvm_reg 支持 mirror 方法)
b. 读 actual value 回来值,更新 mirror value (使命:保持 mirror value 与 actual value 一致)
i. 对 block 调用 mirror, 会对其内的 所有 regs 都更新:在总线上读取所有 regs 的状态
c. (然后,比较 mirror value 与 hw actual value)
d. 使用场景举例(不一定都需要这样):
i. 在 mirror 使用之前,也可以先将读回来的值,与 原 mirror 值进行比较
5. update 方法:更新 hw 的 actual value
a. (在 uvm_reg_block, uvm_reg 支持 mirror 方法)
b. 使用的场景
i. 先修改 rgm 的 desired value,然后,update hw actual value
ii. mirror value 与 actual value 不一致, update hw actual value
6. reset 方法: 软件方法
a. 复位 sw rgm 一侧的值
b. 在 uvm_reg_block, uvm_reg, uvm_reg_field 支持 mirror 方法
c. reset 使用场景:
i. hw 复位时,将 sw 也进行复位
ii. 复位后,读取 rgm 复位值,与 前门访问获得的寄存器值进行比较
7. get_reset 方法
a. 获得reset value
b. 在 uvm_reg, uvm_reg_field 支持 mirror 方法
8. get/set 方法: 与 hw actual value 无关
a. get/set 期望值 desired value
9. set 与 update 使用的典型场景
a. 使用场景
i. 先将整个 reg_block 中的 reg 进行随机化
ii. 然后,对具体的某个 reg 进行 set 值
iii. 然后,做一次 update: 把 hw 一侧的 reg ,update 成上一步中的 set 值
1) 使 rgm 中的值,与 hw 的 值一致
1.4.6. uvm_reg 提供了一些列方法,uvm_reg_sequence 也可以提供一系列方法 ( 只能用于 uvm_reg)
1. uvm_reg_sequence 方法,都是针对于 uvm_reg 的 (不能用于 uvm_reg_block, uvm_reg_field)
a. 后门 (所有方法都适用)
b. read_reg, write_reg
c. peek_reg, poke_reg
mirror_reg, update_reg
1.5 常规方法2
(前面,介绍 uvm_reg_block, uvm_reg, uvm_reg_field; 后面,介绍 uvm_mem; 重点介绍差别)
1. mem 与 reg 的联系和差别
a. 一般用 uvm_reg, uvm_reg_block
b. uvm_mem 针对内部存储建模
i. 考虑到物理存储,消耗资源比较多;所以,mem 不再支持 预测、影子存储等
ii. 可以利用自带的方法去访问存储
iii. 支持前门、后门;通常用法
1) 先通过后门,加载存储内容
2) 再通过前门访问,读取存储内容
iv. 方法
1) read( ), write( ), peek( ), poke( )
2) burst_read( ), burst_write( )
a) 参数 是数组形式
2. 用 uvm_rem 时,需要考虑什么
a. 未看 (TODO)
3. rgm ready 后,怎么用了做测试?
a. uvm_reg 的 built-in sequences
i. 预定义的 sequences,可以测试 所有的 registers
ii. uvm_reg_hw_reset_seq
1) 检查 rgm reset 值 与 hw hdl regsiter reset 值是否一样
iii. uvm_reg_single_bit_bash_seq
1) 对每个 域,根据 读写属性,写、读 测试
iv. uvm_reg_bit_bash_seq
1) 对所有 reg 执行 uvm_reg_single_bit_bash_seq
v. uvm_reg_single_access_seq
1) 前门写、后门读;后门读,前门写测试
2) 用于测试 hdl 路径是否正确;rgm 与 hw register model 是都映射正确
vi. uvm_reg_access_seq (一个 reg_block 有多个 map 的场景)
1) 场景举例:
a) hw/dut 有一个 register block,rgm 中的 uvm_map 有多个,用于 不同的处理器 来访问 dut 中的 register block
b) 先从一个 map 写入寄存器,然后,通过另外的map读回数据;用来检查,所有可能访问寄存器路径的有效性
b. uvm_mem 的 built-in sequences
i. 未看
c. 用 uvm_reg built-in sequence 的代码举例
i. class mcdf_example_seq extends uvm_reg_sequence
1) rgm 声明
2) seq 用 object_utils
3) seqr 进行 declare
4) task body
a) 声明需要用到的 built-in sequences
b) 先 reset 复位
c) (start 之前,需要先告知 seq ,rgm 是哪个?)
i) _seq.model = rgm;
d) 然后,start
i) _seq.start(m_sequencer);
ii. 有一些寄存器,不需要用 内建测试序列测试。怎么排除
因为 uvm_reg_block 和 uvm_reg 都是 uvm_object 类,可以使用 uvm_resource_db 在 build 阶段,排除掉一些寄存器
2. 常用应用场景举例
(除了可以用来测试,还可以用来做什么:测试;功能覆盖率)
1. 影子寄存器 shadow register 是什么?有什么用?
2. 对硬件数据通路,做数据比对
a. 直接用 rgm 中的值,与 硬件中的数据 进行对比
b. 配合 scoreboard,reference model 进行
3. 状态寄存器、非状态寄存器通常怎么测?(内检测试序列支持)
4. 配合 scoreboard 实施检查,注意
a. scoreboard 要可以 access 到 rgm : 句柄传递
b. 怎么去 access 到 rgm uvm_reg, uvm_reg_field 中的值:用什么函数?访问 谁
i. 本质是访问 uvm_reg_field 中的值
ii. uvm_reg, uvm_reg_field 支持的函数略有不同
iii. 建议使用 uvm_reg, uvm_reg_field 都有的函数是 get_mirrored_value( )
5. rgm 的功能覆盖率
a. 自动收集 coverage
i. 首先,定义 coverage bins
ii. 其次,决策:是否生成覆盖率代码、是否例化这部分代码、是否启动采样
1) 有条件的例化
a) has_coverage()
2) sample 自动调用:可以理解为 read(), write() 的回调函数
a) sample_value( ) , 有条件的例化
i) get_coverage
b. 自定义收集 coverage,基于外部事件触发收集:可以更加贴合实际场景,进行收集
i. 自定义覆盖率类 (coverage extends subscriber, subscriber extends component)
1) component:
a) 参数类
2) _subscriber
a) subcribe info from monitor's analysis_port, send to coverage's analysis's export
b) 谁用这些信息,谁就可以 extends from subscriber; 如 scoreboard
3) _coverage
a) covergroup reg_value_cg
i) 指定 uvm_reg_field 感兴趣的 域和值的范围
ii) 将相关的 coverpoint 进行 cross
b) function void write(T t);
i) 手动监测
One. 当 write 捕捉到 transaction 时,调用:reg_value_cg.sample( )
c. 总结:监测总线,更新 rgm;通过 rgm 收 coverage;可以自动、可以手动;可以放在 rgm 外面
3. 寄存器属性的特别说明
1. register model 中,有 25 种访问方式: RC, WC
中断,都是 读了以后,会清除掉:RC (read clear)
4. 寄存器 coverage 如何收集
TODO
参考链接:
NA