[UVM源代码研究] 当我们driver中使用put_response却最终导致Reponse queue overflow的UVM源代码解决思路(uvm-1.2版)

[UVM源代码研究] 当我们driver中使用put_response却最终导致Reponse queue overflow的UVM源代码解决思路(uvm-1.2版)

引言

开始本文讨论之前我们先来以一个具体的例子来引出我们今天将要讨论的问题。

put_response/get_response的常规用法实例

图1是apb_master_driver中的一段代码,106-109行是关于driver中将transaction信息通过response的方式返回给发起这次req的sequence中。

图1 apb_master_driver中put_response代码示例

在这里插入图片描述

而我们通过在sequence中调用get_response的方式就能获取到该返回的rsp,如图2所示。

图2 sequence中调用get_response示例

在这里插入图片描述

如此一来通过put_response() —— get_response()的组合就可以实现将driver中接口上返回的signal信息以transaction的形式返回给发起此次transaction行为的sequence中,实现一次完整总线操作的闭环行为。

出错情况

但是我们在使用UVM寄存器模型内建的sequence跑寄存器模型仿真时却报出来如图3所示的UVM_ERROR,我们直接从log的字面意思简单分析一下不难发现这里报的是response queue出现了overflow的情况,原因在于我们在用寄存器模型的内建sequence跑仿真时FRONTDOOR的操作最终还是会通过driver来drive到interface上并最终通过put_response返回结果,而我们寄存器模型内建的sequence并不会显式的调用get_response(后面经过分析我们会知道内建sequence默认直接将driver中的req作为返回结果使用了),那必然response会越积越多最终造成了overflow的情况,虽然我们现在还不清楚这个response信息在UVM源代码中是以怎么一种形式存在的,后面我们会抽丝剥茧一点点寻求解决此问题的方法。

图3 UVM内建sequence报出的UVM_ERROR信息

在这里插入图片描述

在寻求这个问题的答案之前,我们首先想到的是为什么以前我们写driver调用寄存器模型内建sequence的时候为什么没有报类似的UVM_ERROR,回顾一下发现原来是我们并没有使用put_response,而是直接将返回的结果通过req直接带出给sequence了,毕竟sequence中发起的req跟driver中get到的req本质是同一个handler。既然都不存在put_response的调用了,那自然也就不会出现response overflow的情况,那么这个put_response存在的意义为何呢?

put_response意义

寄存器操作通常我们使用的都是低速、简单的接口,例如I2C、SPI、APB,而在对寄存器操作并不会涉及到复杂的总线行为,比如说outstanding、out-of-order乃至interleaving,这样我们写对应接口协议的driver的时候是可以直接把返回信息通过req返回的,像本例中的apb总线本身就不支持outstanding,所以完全是可以把response相关的内容删掉,直接简化为将返回信息通过req带出,而sequence中也可以直接从req提取返回信息。

但对于功能复杂的总线协议,例如AHB、AXI或者其他支持outstanding等更复杂的总线协议,对应接口的driver则必须要使用put_response了。原因很简单,就拿最简单的outstanding(DEPTH>1)功能来说,有可能DUT端在收到若干个driver发过来的req之后才会返回一个response,那么为了仿真真实的总线行为,driver在收到response前不能block住,需要通过fork…join_none基础通过get_next_item获取新的req,那么对于sequence而言就不能无法获取的req对应的返回信息在发了多少个req之后返回了,更无法确定返回的req对应的信息跟之前哪个发送的req是一一对应的,对于out-of-order或者interleaving的情况那就更复杂了,单纯的req更是无法实现这类功能,这时候就必须使用到put_response和get_response功能,通过返回的id信息来匹配req与rsp的关系,达到支持更复杂总线行为的目的。

通过以上分析我们可以知道,解决我们上面报出的问题可以删除掉rsp对应的106-109行代码,并且在sequence中不要使用get_response而是直接用req作为返回的transaction结果。如果我们一定要使用put_response的话那就需要查看UVM源代码中对于这个rsp信息是如何处理的,能不能通过相关配置来规避掉这个rsp无法使用的问题,解决思路有两种:1、直接让UVM源代码把返回的rsp丢弃掉;2、让寄存器模型调用get_response与真实的driver行为保持一致。

UVM源代码分析

追溯源代码的行为,我们通常的思路是从报出的出错log信息开始一点点排查,跟我们仿真验证的debug思路是一致的,我们在uvm的source code目录执行以下代码看看出错代码来自哪个文件的哪一行。

 grep -rn "Response queue overflow, response was dropped" ./*

查询到如图4所示的信息,在文件/seq/uvm_sequence_base.sv的1196行。

图4 代码查询结果

在这里插入图片描述

涉及到的function如图5所示。

图5 报错代码所在function

在这里插入图片描述

解决方案一:关闭打印

这里我们首先看下UVM源代码中什么样的场景会触发报错机制。1190-1191行的代码我们知道response_queue_depth变量的意义是设置response_queue这个队列允许的最大深度(这里我们就回答了上面put_response返回的rsp在UVM源代码中究竟以什么样一种形式存在的问题),一旦我们put_response的rsp数目达到我们预设的response_queue_depth值时就会触发1196行的报错机制,response_queue_depth的default值为8,如图6所示。

图6 response_queue_depth定义

在这里插入图片描述

1190行的代码给出了我们第一种解决报错问题的思路,那就是把response_queue_depth的值设为-1, 对于这个protected类型的变量,UVM源代码提供了读写该变量的两个函数如图7所示,UVM寄存器模型的内建sequence都是从uvm_sequence_base继承而来,对于父类中定义的protected类型的变量子类中是可以继承并读写访问的,那么我们是不是可以通过修改我们调用的内建sequence中的response_queue_depth变量值,来实现关闭UVM_ERROR信息的打印呢?于是我们尝试在testcase中修改该变量的值以期达到不报错的目的(也就是我们在引子中提到的第一种解决方案,直接丢掉rsp而不报错)。

图7 response_queue_depth变量的读写函数

图7 response_queue_depth变量的读写函数

在testcase增加图8红色框中所示的代码,仿真结果如图8所示。

图8 设置运行sequence的response_queue_depth为-1

在这里插入图片描述

仿真结果如图9所示,可以看到rst_seq能够顺利跑完,但是bitbash_seq依然报错,原因是因为uvm_reg_hw_reset_seq里并没有定义子sequence,设置response_queue_depth的值对当前sequence的运行生效。而uvm_reg_bit_bash_seq中会嵌套的调用子sequence uvm_reg_single_bit_bash_seq,这会导致uvm_reg_single_bit_bash_seq中的response_queue出现overflow的情况,也就是log中报出来的层次[bitbash_seq.reg_single_bit_bash_seq]。

图9 仿真结果

在这里插入图片描述

进一步分析我们发现uvm_reg_bit_bash_seq中定义的uvm_reg_single_bit_bash_seq我们是无法直接通过hierarchy的形式引用的,因为它被定义为了protected类型,这就让我们犯难了,没法通过hierarchy引用的方式来设置uvm_reg_bit_bash_seq中定义的uvm_reg_single_bit_bash_seq的response_queue_depth值,只能重新定义一个从uvm_reg_bit_bash_seq继承而来的新的bit_bash子类,才能引用其中的uvm_reg_single_bit_bash_seq变量,于是我们新定义了一个名为bit_bash的sequence从uvm_reg_bit_bash_seq继承而来,并重写了其中的body()来设置其中uvm_reg_single_bit_bash_seq变量的response_queue_depth值为-1,testcase也改为例化bit_bash,相关代码如图10所示。

图10 修改代码解决UVM_ERROR报错

在这里插入图片描述

在这里插入图片描述

最终打印的log显示没有UVM_ERROR报出。

第二种解决办法的思路更简单直接,就是将1195行的response_queue_error_report_disabled设为1,那么按理说就不会执行1196行的UVM_ERROR打印行为我们先看下这是个什么变量以及如何进行赋值。response_queue_error_report_disabled定义在uvm_sequence_base.sv中行如图11所示,uvm_sequence_base中仍然定义了两个访问该变量的function如图12所示

图11 response_queue_error_report_disabled的定义

在这里插入图片描述

图12 response_queue_error_report_disabled的访问function

在这里插入图片描述

与前面修改内建sequence中的response_queue_depth值思路完全一样,我们可以仿照修改response_queue_depth值一样如图13所示设置response_queue_error_report_disabled的值为1,修改对应的代码达到一样的目的,仿真也可以顺利不报UVM_ERROR。

图13 修改response_queue_error_report_disabled涉及的代码

在这里插入图片描述
在这里插入图片描述

以上无论是修改response_queue_depth还是修改response_queue_error_report_disabled,本质上都是丢弃driver返回的response同时不报错,并且修改还需要对代码做较大的改动,操作起来难度较大且治标不治本,那又没有其他方法,在尽可能减少代码量的情况下完成同样的行为甚至做的更好呢?当然是有的。

解决方案二:adapter中设置使用response

我们上文已经讨论了response overflow产生的本质就是寄存器模型内建sequence没有调用get_response进而导致了sequence中的response_queue出现了overflow的情况,那么我们"治本"的方案是不是应该高速寄存器模型的内建sequence我们driver返回的数据是以response的形式返回给你的,你不要用req了!这里我们必须得弄清楚UVM内部寄存器模型与uvm_agent相关组件的通信机制,这一点我们在前面介绍寄存器模型相关概念的时候已经有所提及,例如寄存器模型的write行为数据流向如图14所示。

图14 寄存器模型write对应数据流向

在这里插入图片描述

这里是标准的uvm组件,返回的数据是从uvm_monitor收集并返回的,而从uvm_monitor出去的数据会通过uvm_reg_adapter的bus2reg函数返回给寄存器模型,那么我们猜想是否可以通过控制uvm_reg_adapter里的相关代码实现我们寄存器模型获取response的目的。

查看uvm_reg_adapter源代码,我们一眼就看到了如图15所示的下面这段描述,这不正是我们想要达到的目的吗?

图15 uvm_reg_adapter源代码中关于response信息的设置

在这里插入图片描述

于是我们在例化我们自定义的adapter的地方增加了如图16所示的红色框中的代码再次运行运来出错的case内容,仿真果然PASS了。

图16 重新修改代码

在这里插入图片描述

那么这个provides_responses究竟怎么实现的呢?uvm_reg_adapter中除了以上截图的代码并未有其他代码涉及到provides_responses的使用,我们再次故技重施grep一下,发现uvm_reg_map.sv的2191和2337行有所使用,相关代码如图17所示,这里果然调用了get_base_response()(rw.parent调用的,rw为uvm_reg_item,而parent就是指的是其中定义的uvm_sequence_base类型的变量)将driver返回的response消耗掉了。并且我们还看到调用adapter中的bus2reg()函数时传递变量也根据provides_responses是否为1选择了bus_rsp和bus_req,也进一步印证我们之前所说的寄存器模型使用的时候默认把driver中的req通过bus2reg返回给了寄存器模型,这里不仅仅局限于使用寄存器模型内建的sequence,任何使用寄存器模型访问DUT总线的行为最终都会将driver的req返回给寄存器模型,如果不能正确设置provides_responses的值与实际driver中返回信息的关系,都会导致response overflow(provides_reponses=0但是driver中有put_response())的发生或者返回response出错(driver中没有put_response()但是provides_reponses=1)。

图17 provides_responses使用的相关代码

在这里插入图片描述

通过以上设置,我们实现了从本质上解决了response overflow的问题。

总结

本文通过在实际使用时寄存器模型内建sequence与driver配合出错的一个例子,引出了driver与sequence在put_response/get_response的实现原理,分析UVM源代码最终提出了两套解决UVM报错的方案,一套方案通过重置内部变量达到控制UVM不打印错误,但是治标不治本;另一套方案从本质上将driver产生的response返回给了寄存器模型,达到了从根本解决UVM报错问题。

希望通过本次分析,能给大家在以后UVM源代码的分析中提供一种分析问题解决问题的方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值