RGM寄存器模型

1.1 复杂的寄存器模型

前面提到过uvm_reg_block可以嵌套形成层次化的寄存器模型。通常会用两层寄存器块来组织寄存器模型,在第一级uvm_reg_block中加入寄存器,而第二级的uvm_reg_block通常只添加uvm_reg_block,这样从整体上呈现比较清晰的结构。

在使用这种层次化的寄存器结构时,需要做的是定义好子reg_block之后,在父reg_block中实例化,并调用configure(),build()等进行初始化设置,最后将子reg_block的default_map作为子map加入父reg_block的default_map中。这是可以理解的,因为子block的default_map只能处理偏移地址,而只有加入了父block的reg_map中才能获取基地址,组成完整地址。因此一般将具有同一基地址的寄存器作为整体加入一个uvm_reg_block中,而不同的基地址对应于不同的uvm_reg_block。

除了已经引入的uvm_reg_field,uvm_reg,uvm_reg_block的概念,UVM的寄存器模型中还有一个uvm_reg_file的概念,这个类主要用于区分不同的hdl路径。由于在configure等函数中大量的出现hdl路径以用于后门访问,因此为了防止路径中的reg_block名字出现改动导致的大量修改,使用uvm_reg_file进行统一管理。

对于uvm_reg_file,假设目前存在两个寄存器regA和regB,它们的hdl路径是top_tb.mac_reg.blockA.regA和top_tb.mac_reg.blockB.regB。引入reg_file后,在调用寄存器的configure函数时,就可以将其第二个参数设为reg_file的指针,由于reg_file是个纯虚类,需要派生后使用,且其中不会定义实质性内容,因此其名字一般不会发送变化。这样子通过reg_file连接reg_block和reg,当reg_block名字发生变化时,只需要将reg_file的configure参数值改变一下即可,减少了大量修改名字发生错误的可能性。

对于一个寄存器包含多个域的情况,与单个域寄存器的区别是每个field需要单独调用configure函数并设置其对应的hdl路径,调用完每个域的configure函数后,需要使用add_hdl_path_slice将其hdl路径加入reg中。这个函数第一个参数是要加入的路径,第二个参数是对应的域在寄存器中的起始位置,第三个参数是对应的位宽。

除了一个寄存器包含多个域的情况,还有一种情况是寄存器位宽超出了系统的数据位宽,因此一个寄存器占据了多个地址。当然我们可以将这种寄存器分割成多个小的寄存器进行处理,但这样子每次读/写操作都需要进行拆分合并,非常麻烦。因此UVM提供了另一种方式,可以使一个寄存器占据多个地址,只需要在寄存器的定义中通过configure函数指定位宽即可,在reg_model中调用default_map的add_reg函数时,会要求指定寄存器地址,这时候只要指明最小的那个地址就行。UVM会自动根据寄存器位宽,数据总线位宽计算出这是一个多地址寄存器,从而自动进行多次读/写操作,在外部看来,这个寄存器被当作了一个整体进行处理。

有时验证人员还需要监控DUT内存储器的状况,而这些存储可能是内部的,未在总线上分配地址的,不过通过UVM也可以在寄存器模型中很容易地加入存储器。首先是由uvm_mem派生出一个类,在其new函数中调用super.new(),该函数的三个参数分别为名字,存储器深度和宽度。之后在reg_model的build函数中将存储器实例化,并调用其configure函数进行配置。最后如果存储器是被分配了地址的,可以调用default_map.add_mem函数将其加入default_map,从而可以进行前门访问;否则不需加入default_map,只能对其进行后门访问。

要对此存储器进行读/写,与对寄存器类似,可以通过read/write/peek/poke实现,相比寄存器,这四个任务/函数在调用时需要额外加入一个offset的参数来说明读取的是此寄存器的哪个地址。值得注意的是,如果存储器的位宽大于系统数据总线位宽,上述的offset参数代表存储单元的偏移,即对于512x32的存储器在16bit位宽总线上的情况,其offset最大值是511而非1023.这样当指定一个offset进行前门访问时,寄存器模型会在总线上进行两次读/写操作。

1.2 寄存器模型对DUT的模拟

寄存器模型中存在镜像值和期望值的概念。所谓镜像值,是寄存器模型中每一个寄存器的一个专门变量,用于最大可能地与DUT保持同步;所谓期望值,可以理解为我们希望寄存器更新的值。因此当我们希望改变寄存器模型中每个寄存器的值时,一种方法是通过之前提到的write任务,这样期望值和镜像值都会被更新为新写入的值;另一种方法是通过set函数更新期望值(此时镜像值未改变),之后调用update任务,该任务会检查期望值与镜像值是否一致,如果不一致,那么将会把期望值写入DUT,并更新镜像值。通过get函数可以得到寄存器的期望值,通过get_mirrored_value可以得到镜像值。

对于存储器来说,不存在期望值和期望值,寄存器模型不对存储器进行模拟,若要得到存储器的值,则只能通过之前介绍的四种操作。

在前述的几种操作中,read/write操作完成后,寄存器模型都会根据读写的结果更新期望值和镜像值;peek/poke也是会同时更新期望值和镜像值;get操作会返回期望值,set操作会更新期望值,update操作会检查期望值和镜像值是否一致,如果不一致,就会将期望值写入DUT,并且更新镜像值,使其与期望值一致。UVM还有一个randomize操作,会将期望值变为随机值,镜像值不会改变。不过不是所有寄存器都能支持该操作,可以通过将寄存器的build()函数的第八个参数设置为0来关闭该随机化功能。

1.3寄存器模型中一些内建的Sequence

UVM提供了一系列的sequence来检查寄存器模型及DUT中的寄存器。这些sequence都派生自uvm_reg_sequence类。

  1. uvm_reg_mem_hdl_paths_seq用于检查hdl路径的正确性。这个sequence的运行依赖于在其基类uvm_rsequence中定义的一个变量uvm_reg_block model,因此启动此sequence时必须给model赋值。在调用这个sequence的start任务时,传入的sequencer参数为null,这个sequence会试图读取hdl所指向的寄存器,如果无法读取,则会给出错误提示;如果某个寄存器/存储器加入寄存器模型时没有指明hdl路径,则该sequence会跳过该寄存器/存储器。
  2. uvm_reg_hw_reset_seq用于检查上电复位后寄存器模型与DUT中寄存器的默认值是否相同。寄存器模型如果不调用reset函数,则其寄存器都会是0而非像DUT那样是复位值。该sequence在检查前会调用model的reset函数,然后通过前门访问的方式读取DUT所有寄存器的值,并将其与寄存器模型中的值比较。同样地,这个sequence也需要指定model变量。如果想跳过某个寄存器的检查,可以在启动此sequence前使用resource_db设置不检查此寄存器。值得一提的是,resource_db和config_db的底层实现是一样的,uvm_config_db就是从uvm_resource_db类派生来的。
  3. uvm_reg_access_seq用于检查寄存器的读写。使用此sequence也要指定model变量,这个sequence会使用前门访问向所有寄存器写数据,然后通过后门访问读回并比较结果;接着将上述过程反过来再执行一遍,及以后门访问写,前门访问读并比较结果。这个sequence正常工作的前提是所有的hdl路径都设置正确,否则后门访问无法正确执行。同样地,使用resource_db可以跳过对某个寄存器的检查。
  4. uvm_mem_access_seq用于检查存储器的读写,其功能和使用方法与uvm_reg_access_seq基本相似。

1.4 寄存器模型的高级用法

此前曾介绍过寄存器模型通过driver完成前门访问读操作的方式,当driver将读取值返回后,寄存器模型会更新寄存器的镜像值和期望值,这个功能被称为寄存器模型的auto predict功能,可以在建立寄存器模型时使用如下的语句打开

rm.default_map.set_auto_predict(1);

除了以上方式外,其实还可以通过monitor将从总线上收集到的transaction交给寄存器模型,然后由寄存器模型更新相应的值。要使用这种方式更新,需要实例化一个reg_predictor,并为其实例化一个adapter。之后在connect_phase中将reg_predictor和bus_agt的ap口连接在一起,并设置reg_predictor的adapter和map。两种方式的示意图如下图所示。

当总线上只有一个主设备时,则上图中左右两种方式是等价的;如果有多个主设备,则左图会漏掉某些transaction(此处未理解)。

UVM提供mirror操作用于读取DUT中寄存器的值并将它们更新到寄存器模型中。

task uvm_reg::mirror(output uvm_status_e status,
                     input  uvm_check_e  check = UVM_NO_CHECK,
                     input  umv_path_e   path = UVM_DEFAULT_PATH,
                     ......);

mirror的常用参数是如上三个。其中第二个参数指示DUT寄存器值与寄存器模型寄存器的镜像值是否相同,通常mirror会被应用于两个场景,一是在验证过程中不断调用以确保寄存器模型的寄存器值与DUT的寄存器值保持一致,另一个场景是仿真结束后调用,检查寄存器模型的寄存器值与DUT中的寄存器值是否相同。对于前者,check参数选择UVM_NO_CHECK,而对于后者应选择UVM_CHECK。同update操作类似,mirror操作可以在uvm_reg和uvm_reg_block级别被调用。

在某些情况下,要实现参考模型只更新寄存器模型而不影响DUT的值的功能,这时候九需要使用predict操作

function bit uvm_reg::predict(uvm_reg_data_t value,
                              uvm_reg_byte_en_t be = -1,
                              uvm_predict_e     kind = UVM_PREDICT_DIRECT,
                              uvm_path_e        path = UVM_FRONTDOOR,
                              ......);

其中第一个参数表示要预测的值,第二参数-1代表byte全部有效,第三个参数是预测的类型,要实现上述功能就设为默认值UVM_PREDICT_DIRECT,第四个参数代表是后门访问还是前门访问。

以更新计数器为例,在参考模型中每得到一个新的transaction,就先取得寄存器模型中计数器的期望值,将新的transaction中的值更新到读取的计数器值上,并用predict函数将新的值更新到寄存器模型中,predict操作会同时更新镜像值和期望值。

此前我们在定义寄存器模型时,对于reg_field,reg和reg_block都加了rand修饰词,证明UVM是支持从reg_block,reg甚至reg_field层级调用randomize操作的。不过要使field能够随机化,除了使用rand修饰词外,还需要在每个reg_field加入uvm_reg时,调用其configure函数并将第八个参数设置为1,并且此field类型必须有写操作——即field的类型必须是RW, WRC, WRS, WO, WI, WOI之一。UVM对于寄存器模型的randomize操作会更新寄存器模型中寄存器的预期值,可以在randomize之后调用update,将随机化后的参数更新到DUT中。这种随机化的特性适合在仿真开始时随机化配置参数。

寄存器模型中寄存器的位宽受系统总线位宽的影响,若超过系统总线位宽,则需要拆分成两个寄存器,或使用之前提到的具有多地址的寄存器。UVM中寄存器模型的系统总线位宽默认最大值是64,它是通过一个宏控制的

`ifndef UVM_REG_DATA_WIDTH 
    `define UVM_REG_DATA_WIDTH 64
`endif

与数据位宽类似,地址位宽默认最大值也为64,其受到`UVM_REG_ADDR_WIDTH宏控制。要想获得更大的数据位宽或地址位宽,可以通过改变上述两个宏来实现。

1.5 寄存器模型的其他常用函数

get_root_blocks函数得到验证平台上所有的根块。根块指最顶层的reg_block。通过调用get_root_blocks,我们可以在不使用指针传递的情况下获得寄存器模型的指针,从而能在其他component中使用寄存器模型。

在使用get_root_blocks获得reg_block的指针后,要使用$cast将其转化为目标reg_block形式(即我们已经定义好的reg_model)。以后就可以直接使用在使用处定义的local变量p_rm来进行寄存器操作了。

get_reg_by_offset函数可以通过寄存器的地址得到一个uvm_reg指针,再调用此uvm_reg的read/write函数就可以进行读写操作。如果是使用了多层次reg_block的寄存器模型,从最顶层的reg_block的get_reg_by_offset也可以得到子reg_block的寄存器,即将子reg_block自身的offset加上reg在子reg_block中的offset即可,而不必调用子reg_block的get_reg_by_offset。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值