[UVM源代码研究] sequence、 sequencer与driver的通信(uvm-1.2版)

[UVM源代码研究] sequence、 sequencer与driver的通信(uvm-1.2版)

driver与sequencer间的基本连接与通信

driver与sequencer之间的通信,我们最常用的最基本的方法就是

  1. driver中的seq_item_port在agent中connect到了sequencer中的seq_item_export
  2. driver中的seq_item_port通过调用get_next_item任务来从sequencer上获取transaction

于是我们产生如下几个问题:

  • sequencer上的transaction从何处而来?
  • get_next_item这个任务如何实现数据的传递?
  • seq_item_port和seq_item_export分别是什么样的端口类型?

带着以上几个疑问我们看看uvm源代码中都是如何实现的。

get_next_item

src/seq/uvm_sequencer.svh中的源代码

在这里插入图片描述

src/comps/uvm_driver.svh中的源代码

在这里插入图片描述

经过TLM的学习我们知道在TLM通信中存在以下几个概念:

  1. initiator(调用通信方法)和target(实现通信方法)
  2. producer(产生数据)和consumer(使用数据)

由此我们得出如下结论:

  1. driver这里充当的是一个initiator和consumer的作用,发起get_next_item()的通信方法并获取数据,因而我们知道get_next_item()一定是在充当target的sequencer中实现的,并且数据也是在sequencer中产生。
  2. driver中seq_item_port是一个port类型,而sequencer中的seq_item_export是一个imp类型
    自定义的driver和sequencer中构造函数new中调用super.new()可以分别完成seq_item_port好seq_item_export的创建。

get_next_item_called

src/seq/uvm_sequencer.svh中的源代码

在这里插入图片描述

get_next_item这个的task里使用了get_next_item_called这个变量,212-214行显示调用get_next_item的时候get_next_item_called一定为0,否则会报错。调用完get_next_item后需要将get_next_item_called赋值为1。

我们再看看uvm_sequencer里还有哪些地方对get_next_item_called进行了赋值。

分别在item_done和stop_sequences这两个函数中对get_next_item_called做了赋0的操作,由此我们不难得出如下结论:driver执行get_next_item获取数据的时候,如果前一次get_next_item获取数据后driver中没有执行item_done或者sequencer没有调用stop_sequences来强制kill掉上面运行的所有transaction/sequence,那么sequencer中会报错。即通过get_next_item_called这样一个变量实现了driver一次只能从sequencer获取一个数据进行处理。

sequence_item_requeated

src/seq/uvm_sequencer_base.svh中的源代码

在这里插入图片描述

与get_next_item_called同时出现的另一个变量sequence_item_requeated起到一个等待sequencer授权sequence的作用。

sequence_item_requeated清零的位置与get_next_item_called一样,也是在item_done和stop_sequences中完成的,也就是正常情况下执行到get_next_item里的216行判断语句的时候sequence_item_requeated通常为0,会调用m_select_sequence,这个task是在uvm_sequencer的父类uvm_sequencer_base中实现的。

716-722行代码实现了等待被仲裁授权的sequence。

724-728授权上面等到的sequence(通过sequence对应的id标识arb_completed数组来实现授权)。
等待sequence授权的过程是get_next_item阻塞的原因所在。

get_next_item第221-222行实现了对get_next_item_called、sequence_item_requeated赋值为1,表明此时sequencer中有一个数据正在发送给driver处理,如果在item_done或者stop_sequences之前driver又通过get_next_item发起一个获取数据的请求,212和216行的判断会执行报错和不进行sequence的仲裁选择行为。

get_next_item第223行从m_req_fifo中用peek获取一个数据输出给driver。(注意这里用到的是peek而不是pop,也就是fifo中的数据仍然保留并没有彻底取出,后面会介绍原因。)

item_done

src/seq/uvm_sequencer.svh中的源代码

在这里插入图片描述

src/seq/uvm_sequencer_base.svh中的源代码
在这里插入图片描述

关于item_done,首先需要说明的就是这是一个函数,并不会消耗时间,driver中调用item_done表明通过get_next_item获取的transaction已经drive到interface完成了协议转换,可以接收下一笔数据了。

所以这里281-282行将前面介绍的这两个变量清零了。

284行就解答了我们前面关于get_next_item里为什么调用try_peek来从fifo中获取数据的问题:get_next_item只是表示driver获取了数据,但是数据什么时候被消耗完成是由item_done决定的,所以fifo中的数据只有在item_done的时候才能pop出来。

使用try_get函数同时增加了判断,如果此时取不出数据则报错。

288-289行这两个变量作用就是当sequence调用sequencer中的wait_for_item_done的时候,能够将item_done匹配到对应的sequene的某个特定transaction,正如我们在driver中如果使用rsp,需要调用rsp.set_id_info来将req和rsp通过id关联起来一样。

292-294行作用就是当item_done参数不为空时,就执行put_response()将item_done的参数作为rsp发出。

297行的作用就是授权给那些在等待授权队列里靠前位置调用了lock或者grab的sequence。

sequence与sequencer之间的通信

src/seq/uvm_sequencer_param_base.svh中的源代码

在这里插入图片描述

src/seq/uvm_sequence_base.svh中的源代码

在这里插入图片描述

sequencer中获取的数据存放在m_req_fifo这样一个uvm_tlm_fifo中。

关于这个fifo定义以及添加数据是在uvm_sequencer_param_base类中实现的,它派生自uvm_sequencer_base,而uvm_sequencer又是从它派生而来的。该类中还定义了另一个我们常用到的函数put_response(),后面我们会讲。

这里的send_request函数是在uvm_sequence_base(uvm_sequence的父类)调用的,这样就实现了在uvm_sequence的派生类中调finish_item(通常用uvm_do宏包裹)将数据发送到sequencer并存放在m_req_fifo中。

使用try_put()函数是为了进行判断fifo是否溢出,溢出则报错。

如此我们便清楚了sequence与sequencer之间数据传输的通路实现。

start_item

src/seq/uvm_sequence_base.svh中的源代码

在这里插入图片描述

finish_item实现将sequence中的数据发送到sequencer,而与之通常配对的start_item里都做了什么呢?它又是与sequencer做了哪些交互?

默认是用的是m_sequencer,即sequence启动时指定的sequencer。

这里主要执行操作就是994行等待sequencer的授权。

put_response

src/seq/uvm_sequencer_param_base.svh中的源代码

在这里插入图片描述

src/seq/uvm_sequence_base.svh中的源代码

在这里插入图片描述

从uvm_sequencer_param_base中put_response的实现我们发现,328行获取了通过rsp调用get_sequence_id获取了在该sequencer运行的对应的sequence的句柄,337行调用了对应sequence中的put_response函数并将该rsp压入了该sequence实例中的队列response_queue中,队列的默认允许深度是8。
进而在sequence中调用get_response才能获取到之前被压入的rsp数据。

这就是rsp从driver到sequencer再到sequence的数据通路,其实不难看出sequencer在这里只是起到了个调用函数的中介作用,甚至没有将rsp临时存放起来。

sequence、sequencer与driver数据通信图示

实现通过以上分析,我们对sequence、sequencer与driver数据的传递相关函数调用等总结为如下图示:
在这里插入图片描述

总结

本文通过分析UVM源代码中uvm_sequence、uvm_sequencer、uvm_driver中的相关代码实现,讲述了sequence中发包经sequencer、driver再到发包完整整个包的传递流程,相应经过本文的分析解释读者对UVM中transaction的传递会有更深的认识。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值