(四)UVM_RAL 寄存器模型之 mirror 和 update 操作

一、  UVM_PREDICT_DIRECT功能与mirror操作

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

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

它有多个参数,但是常用的只有前三个。其中第二个参数指的是如果发现DUT中寄存器的值与寄存器模型中的镜像值不一致,那么在更新寄存器模型之前是否给出错误提示。其可选的值为UVM_CHECK和UVM_NO_CHECK。它有两种应用场景,一是在仿真中不断地调用它,使得到整个寄存器模型的值与DUT中寄存器的值保持一致,此时check选项是关闭的。二是在仿真即将结束时,检查DUT中寄存器的值与寄存器模型中寄存器的镜像值是否一致,这种情况下,check选项是
打开的。

mirror操作会更新期望值和镜像值。同update操作类似,mirror操作既可以在uvm_reg级别被调用,也可以在uvm_reg_block级别被调用。当调用一个uvm_reg_block的mirror时,其实质是调用加入其中的所有寄存器的mirror。

前文已经说过,在通信系统中存在大量的计数器。当网络出现异常时,借助这些计数器能够快速地找出问题所在,所以必须要保证这些计数器的正确性。一般的,会在仿真即将结束时使用mirror操作检查这些计数器的值是否与预期值一致。

在DUT中的计数器是不断累加的,但是寄存器模型中的计数器则保持静止。参考模型会不断统计收到了多少包,那么怎么将这些统计数据传递给寄存器模型呢?

前文中介绍的所有的操作都无法完成这个事情,无论是set,还是write,或是poke;无论是后门访问还是前门访问。这个问题的实质是想人为地更新镜像值,但是同时又不要对DUT进行任何操作。

UVM提供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,
…);

其中第一个参数表示要预测的值,第二个参数是byte_en,默认-1的意思是全部有效,第三个参数是预测的类型,第四个参数是后门访问或者是前门访问。第三个参数预测类型有如下几种可以选择:

 typedef enum {
      UVM_PREDICT_DIRECT,
      UVM_PREDICT_READ,
      UVM_PREDICT_WRITE
   } uvm_predict_e;

read/peek和write/poke操作在对DUT完成读写后,也会调用此函数,只是它们给出的参数是UVM_PREDICT_READ和UVM_PREDICT_WRITE。要实现在参考模型中更新寄存器模型而又不影响DUT的值,需要使用UVM_PREDICT_DIRECT,即默认值:

 在测试用例中,仿真完成后可以检查DUT中counter的值是否与寄存器模型中的counter值一致:

 二、寄存器模型的随机化与update操作

前文中在向uvm_reg中加入uvm_reg_field时,是将加入的uvm_reg_field定义为rand类型:

class reg_invert extends uvm_reg;
    rand uvm_reg_field reg_data;
…
endclass

在将uvm_reg加入uvm_reg_block中时,同样定义为rand类型:

class reg_model extends uvm_reg_block;
   rand reg_invert invert;
…
endclass

由此可以判断,对register_model来说,支持randomize操作。可以在uvm_reg_block级别调用randomize函数,也可以在uvm_reg级别,甚至可以在uvm_reg_field级别调用:

assert(rm.randomize());
assert(rm.invert.randomize());
assert(rm.invert.reg_data.randomize());

但是,要使某个field能够随机化,只是将其定义为rand类型是不够的。在每个reg_field加入uvm_reg时,要调用其configure函数:

// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset,
is_rand, individually accessible
reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);

这个函数的第八个参数即决定此field是否会在randomize时被随机化。但是即使此参数为1,也不一定能够保证此field被随机化。当一个field的类型中没有写操作时,此参数设置是无效的。换言之,此参数只在此field类型为RW、WRC、WRS、WO、W1、WO1时才有效。

因此,要避免一个field被随机化,可以在以下三种方式中任选其一:

1)当在uvm_reg中定义此field时,不要设置为rand类型。
2)在调用此field的configure函数时,第八个参数设置为0。

3)设置此field的类型为RO、RC、RS、WC、WS、W1C、W1S、W1T、W0C、W0S、W0T、W1SRC、W1CRS、W0SRC、W0CRS、WSRC、WCRS、WOC、WOS中的一种。

其中第一种方式也适用于关闭某个uvm_reg或者某个uvm_reg_block的randomize功能。

既然存在randomize,那么也可以为它们定义constraint:

class reg_invert extends uvm_reg;
   rand uvm_reg_field reg_data;
   constraint cons{
      reg_data.value == 0;
   }
…
endclass

在施加约束时,要深入reg_field的value变量。

randomize会更新寄存器模型中的预期值:

function void uvm_reg_field::post_randomize();
   m_desired = value;
endfunction: post_randomize

这与set函数类似。因此,可以在randomize完成后调用update任务,将随机化后的参数更新到DUT中。这特别适用于在仿真开始时随机化并配置参数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那么菜

你的鼓励和批评是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值