UVM寄存器篇之六:寄存器模型的常规方法(上)

时间:2024-04-11 07:38:14

本文转自:http://www.eetop.cn/blog/html/28/1561828-6266223.html

mirrored、desired和actual value

在我们应用寄存器模型的时候,除了利用它的寄存器文件信息,也会利用它来跟踪寄存器的值。跟踪寄存器的值,一方面是建立mirrored value,另外一方面是为建立desired value。读到这里,读者们首先需要确立,任何一个寄存器模型中的寄存器实例,都应该有两个值,一个是镜像值(mirrored value),一个是期望值(desired value)。期望值是先利用寄存器模型修改软件对象值,而后利用该值更新硬件值;镜像值是表示当前硬件的已知状态值。期望值往往由模型预测给出,关于预测,我们上一节给了一些基本概念,即在前门访问时通过观察总线或者在后门访问时通过自动预测等方式来给出镜像值。然而镜像值有可能与硬件实际值(hardware actual value)不一致,例如状态寄存器的镜像值则无法保持与硬件实际值保持同步更新,另外如果其他访问寄存器的通路修改了寄存器,那么可能由于那一路总线没有被监测,因此寄存器的镜像值也无法得到及时更新。

 

接下来我们要讨论的寄存器模型预测方式,与上面的三种值相关,这是因为预测行为会直接影响到如何更新镜像值和期望值。在介绍之前,需要需要分辨与,mirrored value与desired value是寄存器模型的属性,而actual value则对应硬件的真实数值。

 

prediction的分类

UVM提供了两种用来跟踪寄存器值的方式,我们将其分为自动预测(auto prediction)和显式预测(explicit )。自动预测的例码在上一节中已经给出,如果用户想使用自动预测的方式,还需要调用函数uvm_reg_map::set_auto_predict()。这两种预测方式的显著差别在于预测的寄存器数值上,显式预测更为准确,具体原因我们可以通过下面对两种模式的分析来得出。

 

自动预测(auto prediction)

如果用户并没有集成独立的predictor在环境中,而是利用寄存器的操作来自动记录每一次寄存器的读写数值,并在后台自动调用predict()方法的话,这种方式被称之为自动预测。这种方式简单有效,然而需要注意,如果其它一些sequence直接在总线层面上对寄存器进行操作(跳过寄存器级别的write()/read()操作,或者通过其它总线来访问寄存器等等这些为通过寄存器模型做操作的方式来访问寄存器都无法自动得到寄存器的镜像值和预期值。

UVM寄存器篇之六:寄存器模型的常规方法(上)

 

显式预测(explicit prediction)

更为可靠的一种方式是在物理总线上通过监视器来捕捉总线事务,并将捕捉到的事务传递给外部例化的predictor(预测器)。该predictor由UVM参数化类uvm_reg_predictor例化并集成在顶层环境中。在集成的过程中,需要将adapter与map的句柄也一并传递给predictor,同时将monitor采集好的事务通过analysis port接入到predictor一侧。这种集成关系可以使得,monitor一旦捕捉到有效事务,会发送给predicotr,再尤其利用adapter的桥接方法,实现事务信息转换,并将转化后的寄存器模型有关信息更新到map中。默认情况下,系统将采用显式预测的方式,这就要求集成到环境中的总线UVC的monitor需要具备捕捉事务功能和对应的analysis port,以便于predictor连接。

UVM寄存器篇之六:寄存器模型的常规方法(上)

 

关于predictor在顶层环境中的集成,读者可以通过下面的一段例码片段来掌握集成时的几个要素:

 

UVM寄存器篇之六:寄存器模型的常规方法(上)

 

 

uvm_reg的访问方法

在给出寄存器模型的常见应用模式之前,我们还需要更全面了解uvm_reg_block/uvm_reg/uvm_field三个类提供的用于访问寄存器的公共方法:

UVM寄存器篇之六:寄存器模型的常规方法(上)

 

在上一节给出的例码中,已经类比了uvm_reg_block/uvm_reg/uvm_reg_field的方法和uvm_reg_sequence封装的方法。在上面给出寄存器模型相关类的详尽方法列表之后,我们再将uvm_reg_sequence提供的方法(均是针对寄存器对象的,而不是寄存器块或者寄存器域)整理如下,方便读者进行对比使用:

UVM寄存器篇之六:寄存器模型的常规方法(上)

在上一节中,我们对uvm_reg提供的四种方法即read()、write()、peek()和poke()如何进行前门访问和后门访问做了介绍。结合着mirrored value、desired value和actual value,我们需要理解这四种方法在调用时,三个值之间的变化时序关系:

  • 对于前门访问的read()和write(),在总线事务完成时,镜像值和期望值才会更新为与总线上相同的值,这种预测方式是显式预测。

  • 对于peek()和poke(),以及后门访问模式下的read()和write(),由于不通过总线,默认采取自动预测的方式,因此在零时刻方法调用返回后,镜像值和期望值也相应修改。

 

关于reset()和get_reset()的用法,下面也给出部分例码。例如硬件在复位触发时,会将内部寄存器值复位,而寄存器模型在捕捉到复位事件时,为了保持同硬件行为一致,也应当对其复位。这里注意的是,复位的对象是寄存器模型,而不是硬件。

 

UVM寄存器篇之六:寄存器模型的常规方法(上)

 

在复位之后,用户也可以通过读取寄存器模型的复位值(与寄存器描述文件一致),与从前门访问获取的寄存器复位值进行比较,以此来判断硬件各个寄存器的复位值是否按照寄存器描述文件去实现。这里个get_reset()方法指的也是寄存器模型的复位值,而不是硬件。

 

UVM寄存器篇之六:寄存器模型的常规方法(上)

 

 

mirror()方法与read()方法类似,也可以选择前门访问后者后门访问,不同的是,mirror()不会返回读回的数值,但是会将对应的镜像值修改。在修改镜像值之前,用户还可以选择是否将读回的值与模型中的原镜像值进行比较。下面的例码一方面在更新镜像值之前,首先将读回的值与上一次镜像值做了比对,随后再更新镜像值。譬如,对于配置寄存器,可以采用这种方法来检查上一次的配置是否生效,又或者对于状态寄存器,可以选择只更新镜像值不做比较,这是因为状态寄存器随时可能被硬件内部逻辑修改。

 

UVM寄存器篇之六:寄存器模型的常规方法(上)

下面的方法是运用set()和update()对寄存器做批量修改。首先set()方法的对象是寄存器模型自身,通过set()可以修改期望值,而在寄存器配置时不妨先对其模型随机化,再配置个别寄存器或者域,当寄存器的期望值与镜像值不相同时,可以通过update()方法来将不相同的寄存器通过前门访问或者后门访问的方式做全部修改。这种set()和update()的方式较write()和poke()的写寄存器方式更为灵活的是,它可以实现随机化寄存器配置值(先随机化寄存器模型,后将随机值写入到寄存器),继而模拟更多不可预知的寄存器应用场景,另外update()强大的批量操作寄存器功能使得修改寄存器更为便捷。

 

UVM寄存器篇之六:寄存器模型的常规方法(上)