[UVM源代码研究] 谈谈寄存器模型中predict

[UVM源代码研究] 谈谈寄存器模型中predict

我们在前面的文章 聊聊寄存器模型中的期望值(desired value)、镜像值(mirrored value)以及DUT中的实际值(actual value)的相关概念及方法 已经简单介绍了predict()函数以及auto_predict相关的概念,本文我们来详细介绍一下寄存器模型中如何正确引入predictor。

首先我们看下当我们环境中使用自动预测时,UVM源代码是如何工作的,我们需要在寄存器模型创建的组件的connect_phase()执行如下代码,将我们寄存器模型中的uvm_reg_map设置为自动预测。
在这里插入图片描述

我们来看看源代码中该函数的定义
在这里插入图片描述

这里的set_auto_predict()参数是可以为1和0的,参数值会直接赋给m_auto_predict,m_auto_predict为1会打开自动预测的功能。

如果打开自动预测的功能,那么通过uvm_reg_map来进行总线的的读写(包括UVM_FRONTDOOR和UVM_BACKDOOR)时,在读写task调用结束前,该寄存器的predict()函数会被自动调用以更新镜像值和期望值。

如果关闭自动预测功能,那么我们需要将uvm_reg_predictor实例化(或者继承一个我们自己的predictor),将predictor实例与我们monitor相连接(这里还需要我们monitor能够正确抓去总线数据),这样predictor就会实时的获取monitor所监测到的transaction信息,通过transaction中提供的address信息与uvm_reg_map中的寄存器address进行匹配进而发起相应的uvm_reg的predict()行为。这样一来uvm_reg的predict行为就跟寄存器模型中的读写行为脱钩了,只是跟monitor上的行为想关联,即使不是寄存器模型发起的读写(例如通过一般的sequence发起的总线读写),也是可以实时的更新寄存器模型中的镜像值和期望值,这样做更能保证寄存器模型中的值与DUT中的寄存器值最大程度的保持一致。

我们在创建uvm_reg_map的时候(如果只有一个uvm_reg_map我们一般会用uvm_reg_block自带的default_map)默认会将m_auto_predict设为0。
在这里插入图片描述

由于m_auto_predict是被local修饰的本地变量,因而不能在uvm_reg_map类外部访问,可以使用get_predict_map获取当前predict类型
在这里插入图片描述

m_auto_predict = 1

首先我们看下当我们执行寄存器模型的读写时,predict是如何被调用的,我们先看下do_read()
在这里插入图片描述

可以看到UVM_BACKDOOR是不管auto_predict有没有打开都自动调用do_predict(),这是因为UVM_BACKDOOR并不会产生实际的总线行为,只能默认auto_predict功能打开了,否则就会漏掉寄存器的更新行为。而对于UVM_FRONTDOOR,do_read()就会进行判断是否打开了auto_predict,如果打开则会在调用do_read()的时候就调用do_predict(),如果没有则不会,那就是我们后面会讲到的关闭auto_predict时do_predict()是如何被调用的。

do_write()调用do_predict的行为与do_read()类似,这里就不展开讲了
在这里插入图片描述

除了上面的write/read所调用的do_write()/do_read()中会调用do_predict(),uvm_reg中的poke()和peek()也会自动调用do_predict()函数,类似于do_write()/do_read()的UVM_BACKDOOR行为,区别我们在[UVM源代码研究] 聊聊寄存器模型中的期望值(desired value)、镜像值(mirrored value)以及DUT中的实际值(actual value)的相关概念及方法 已经讲的很清楚了。

这样我们关于打开auto_predict的原理已经讲清楚了,下面看看如果我们要关闭auto_predict功能,使用自己定义的predictor需要那些步骤。

m_auto_predict = 0

我们先看看uvm_reg_predictor这个类
在这里插入图片描述
在这里插入图片描述

这里的uvm_reg_predictor是个参数化的类,其中的参数对应着不同bus的transaction类型,所以我们在例化uvm_reg_predictor的时候就需要指定具体的transaction类型。

uvm_reg_predictor继承自uvm_component,这个组件也存在于UVM的树形结构中,其实也可以理解,他的作用也无异于uvm_monitor、uvm_driver这类组件,他并不会做太多定制行为,它只是一个无情的监测预测机器,它将monitor发过来的transaction分析后做出predict,所以当时具备了一个uvm_component该有的一切特性。

两个uvm_component之间的通信我们当然优先选择TLM机制,这里uvm_reg_predictor是被动的接收uvm_monitor发过来的数据包,而uvm_monior中发包我们用的是uvm_analysis_port(uvm_monitor需要给很多组件发送收集的数据包,包括ref_model、coverage_collector、scb等等),所以相应的我们在uvm_reg_predictor中需要用一个uvm_analysis_imp来与之连接,并且要实现相应的通信方法write()函数。这里我们用的uvm_analysis_imp是bus_in,我们需要在例化uvm_reg_predictor的地方将uvm_monitor中的uvm_analysis_port与bus_in进行connect,实现uvm_reg_predictor对总线transaction的实时监测。

这里我们看到uvm_reg_predictor中还有三个变量,分别是

  1. uvm_reg_map map
  2. uvm_reg_adapter adapter
  3. uvm_nanalysis_port #(uvm_reg_item)

关于map使用我们可以理解为我们需要比对uvm_monitor发送过来的transaction中address与实际寄存器模型uvm_reg_map中的地址列表作比对,以选择需要做predict的那个uvm_reg,所以我们在创建uvm_reg_predictor实例的时候需要将uvm_reg_map关联到我们寄存器模型中的uvm_reg_map上。

adapter的作用仅仅是通过其内部的bus2reg()函数实现将收集到的bus transaction转化为寄存器模型能够识别的uvm_reg_bus_op供后续使用,包括调用uvm_reg_map中的get_reg_by_offset()函数获取相应的uvm_reg值等。

而reg_ap这么个变量就只是将收到的bus transaction所转化生成的uvm_reg_item以广播包的形式发送出去,这是uvm_reg_predictor中预留了一个hook,源代码中并没有使用这个reg_ap,用户可以根据自己的需要选择是否使用相应的uvm_analysis_imp来连接到这个reg_ap上获取相应的uvm_reg_item数据,并实现write()通信函数。

uvm_reg_predictor中的write()函数截图如下,我们上面提到的相关应用全部在其中实现了,具体实现细节我们这里就不仔细去分析了,绝大多数重要功能我们在上文都已经提及了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最后这个check_phase()就是检查一下是不是所有经过uvm_reg_predictor的transaction都已经完成了predict工作,确保没有遗漏的情况。

最后我们引用cluelogic上的两幅图来回回顾下寄存器模型中的读写过程中数据是如何流通的

  1. uvm_reg的write()
    在这里插入图片描述
  2. uvm_reg的read()
    在这里插入图片描述

总结

至此,通过以上的学习我们得到如下结论:

  1. 我们需要在寄存器模型创建的组件的connect_phase()调用寄存器模型中的uvm_reg_map的set_auto_predict(on)来选择打开(on=1)或关闭(on=0)自动预测功能,默认关闭。
  2. 自动预测打开的优点在于我们UVM环境实现起来会比较简单,工作量大大降低,缺点是无法predict寄存器模型之外的总线行为(例如sequence),因而不能保证寄存器模型与DUT的实时匹配。
  3. 关闭自动预测我们就需要实例化uvm_reg_predictor,并将uvm_reg_predictor实例中的bus_in与我们的uvm_monitor中的uvm_analysis_port相连接,这就需要我们保证uvm_monitor实现必须完善,这样就可以保证我们寄存器模型与DUT中的寄存器值最大可能的保持一致性。

参考文献

UVM Tutorial for Candy Lovers - 9. Register Abstraction

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UVM(Universal Verification Methodology)寄存器模型,可以通过以下步骤编写更新 mirror(寄存器镜像)值的代码: 1. 创建一个寄存器模型类,该类应继承自 `uvm_reg` 或 `uvm_reg_block`。 2. 在寄存器模型定义一个 mirror 变量,用于存储寄存器的镜像值。可以使用 `uvm_reg_data_t` 类型或与寄存器宽度相匹配的其他类型。 3. 在寄存器模型重写 `uvm_reg_block::build()` 方法。在此方法,为寄存器模型创建相应的 mirror 变量,并将其初始化为适当的值。 4. 在寄存器模型重写 `uvm_reg::write()` 方法。此方法将被调用以更新寄存器值时。在此方法,你可以将写入的值赋给 mirror 变量。 5. 在寄存器模型重写 `uvm_reg::read()` 方法。此方法将被调用以读取寄存器值时。在此方法,你可以返回 mirror 变量的值。 下面是一个简单的示例代码,演示了如何在 UVM 寄存器模型编写更新 mirror 值的代码: ```systemverilog class my_reg_model extends uvm_reg; uvm_reg_data_t mirror; // 构造函数 function new(string name = "my_reg_model"); super.new(name, 32, UVM_NO_COVERAGE); endfunction // 构建函数 virtual function void build(); mirror = 0; // 初始化 mirror 变量 endfunction // 写入函数 virtual function void write(input uvm_status_e status, input uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null); mirror = value; // 更新 mirror 变量 super.write(status, value, path, map); endfunction // 读取函数 virtual function uvm_reg_data_t read(input uvm_status_e status, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null); return mirror; // 返回 mirror 变量的值 endfunction endclass ``` 注意:以上代码仅为示例,你可能需要根据你的具体需求进行修改和扩展。此外,还可以根据需要在寄存器模型添加其他的方法和功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值