电子说
当我开始使用UVM RAL时,我无法理解UVM基类库对更新Desired和Mirror 寄存器的解释。觉得使用的术语并不能准确地反映里面的真实的意思。花了一些时间后,我想出了一个表格,它可以帮助我理解寄存器模型API 的行为,以及如何最好地调用它们。
在介绍表格之前,我们先来看看寄存器模型的创建过程:
创建寄存器格式规范:有许多寄存器格式可用于描述DUT的寄存器规范。您可能熟悉广泛使用的 Synopsys RALF 格式。下图说明了使用 Synopsys Ralgen 工具将 RALF 格式转换为寄存器模型的流程。虚线表示您可以为不同的方法生成寄存器模型:
使用寄存器模型:寄存器模型具有一组用于Desired和Mirror寄存器值的变量。UVM文档使用了术语Desired 和Mirror, 但我在下面将它们称为Main和Mirror以避免混淆。Mirror变量的目的是始终保持或表示RTL的值,以便它可以用作Scoreboard。有很多 API 可对这些变量进行操作。此处的目的是阐明在仿真期间调用这些API时Main变量和Mirror变量会发生什么。
让我们看一下可用的 API。我将它们分为三组:Active、Passive和Indirect。
Active: 在总线上通过物理事务做读写操作。Read()、write()、update() 和 mirror() 是Active API,它们使用物理接口在 DUT 上运行。您可以选择使用后门机制,在这种情况下它不会消耗仿真周期。与使用前门访问相同的RTL寄存器行为一致。
Passive:仅使用寄存器模型操作。set()、get() 和 predict() 是直接在模型上操作的Passive API。调用Passive peek() 不会在读取过程中改变寄存器值。例如,读取以清除寄存器——执行 peek() 时寄存器不会被清除。
Indirect: 有一组 API 可以间接在 DUT 上运行,它们是 peek() 和 poke()。请注意 peek() 和 poke() API 只是后门访问。尽管 poke 可以更新 RTL 寄存器,但它不能模拟物理读取期间可能发生的实际寄存器行为。例如,写1来清除寄存器。
让我们简要介绍一下广泛使用的 API 定义。您可以在 UVM 类参考指南中找到更多详细信息。
Read():使用前门或后门访问从 DUT 寄存器中读取值。
Write():使用前门或后门访问更新 DUT 寄存器。
Update(): 如果您使用 set() 更改了主寄存器变量中的任何值,则可以使用这种方法(批量更新)将所有这些寄存器写入 DUT。您可以调用单独的 write() 方法来实现相同的结果。
Mirror(): Mirror 维护DUT寄存器值的副本。Mirror() 方法读取寄存器,如果启用检查,则可选择将回读值与当前镜像值进行比较。 可以使用物理接口(前门)或 peek()(后门)机制执行镜像。
Peek(): 使用后门访问机制从DUT寄存器中读取值。
Poke():使用后门访问机制将指定值写入DUT寄存器。
Predict():您可以使用此方法将镜像变量值更改为期望值。
我进行了一些实验,下表显示了当从Testbench执行任何这些 API 时,寄存器模型和 DUT 中发生的情况。
缩写
UMV – 更新主变量,UMrV – 更新镜像变量,AP – Auto Predict
RDR – 读取DUT寄存器,UDR – 更新DUT寄存器,RMV – 读取Main变量
FD – 前门,BD – 后门,* – 检查是否使用了 UVM_CHEK, NA – 不适用
要记住的几点
没想到 peek() 和 poke() 方法会无条件更新镜像值。查看UVM源码后,发现在peek()和poke()方法内部无条件调用do_preedit()方法。我还注意到使用后门机制的 write() 和 read() 方法会在调用 do_predict() 时更新镜像寄存器,而无需检查此 get_auto_predict() 方法的输出。我看到这个有条件调用的唯一地方是具有前门访问的 write() 和 read() 方法。
在与专家讨论后,了解到这样设计是有意的,是为了确保镜像变量中具有最新的寄存器值。类似地,使用后门访问的 read()/write() 也会更新镜像寄存器——这也是有意的。因为使用了后门,所以不会在物理接口上观察到(当auto predict关闭时)更新寄存器模型的事务。因此,它必须在所有情况下进行更新。
作者:Vidyashankar Ramaswamy
来源:
https://blogs.synopsys.com/vip-central/2015/01/06/using-uvm-register-model/
全部0条评论
快来发表一下你的评论吧 !