[UVM源代码研究] sequence、 sequencer与driver的通信(uvm-1.2版)
driver与sequencer间的基本连接与通信
driver与sequencer之间的通信,我们最常用的最基本的方法就是
- driver中的seq_item_port在agent中connect到了sequencer中的seq_item_export
- 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通信中存在以下几个概念:
- initiator(调用通信方法)和target(实现通信方法)
- producer(产生数据)和consumer(使用数据)
由此我们得出如下结论:
- driver这里充当的是一个initiator和consumer的作用,发起get_next_item()的通信方法并获取数据,因而我们知道get_next_item()一定是在充当target的sequencer中实现的,并且数据也是在sequencer中产生。
- 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的传递会有更深的认识。