5、sequence中使用config::db
在sequence中获取参数
能够调用config_db::get的前提是已经进行了set。sequence本身是一个uvm_object,它无法像uvm_component那样出现在 UVM树中,从而很难确定在对其进行设置时的第二个路径参数。所以在sequence中使用config_db::get函数得到参数的最大障碍是路径问题。
在UVM中使用get_full_name()可以得到一个component的完整路径,同样的,此函数也可以在一个sequence中被调用,尝试 着在一个sequence的body中调用此函数,并打印出返回值,其结果大体如下:
这个路径是由两个部分组成:此sequence的sequencer的路径,及实例化此sequence时传递的名字。
因此,可以使用如下的方式 为一个sequence传递参数:
set函数的第二个路径参数里面出现了通配符,这是因为sequence在实例化时名字一般是不固定的,而且有时是未知的(比如 使用default_sequence启动的sequence的名字就是未知的),所以使用通配符。
在sequence中以如下的方式调用config_db::get函数:
在get函数原型中,第一个参数必须是一个component,而sequence不是一个component, 所以这里不能使用this指针,只能使用null或者uvm_root::get()。前文已经提过,当使用null时,UVM会自动将其替换为 uvm_root::get(),再加上第二个参数get_full_name(),就可以完整地得到此sequence的路径,从而得到参数。
在sequence中设置参数
与获取参数相比,在sequence中使用config_db::set设置参数就比较简单。有了在top_tb中设置virtual interface的经验,读者 在这里可以使用类似的方式为UVM树中的任意结点传递参数:
wait_modified的使用
在上一节的例子中,向scoreboard传递了一个cmp_en的参数,scoreboard可以根据此参数决定是否对收到的transaction进行检 查。在做一些异常用例测试的时候,经常用到这种方式。
但是关键是如何在scoreboard中获取这个参数。 在以前的章节中,scoreboard都是在build_phase中调用get函数,并且调用的前提是参数已经被设置过。一个sequence是在task phase中运行的,当其设置一个参数的时候,其时间往往是不固定的。
针对这种不固定的设置参数的方式,UVM中提供了wait_modified任务,它的参数有三个,与config_db::get的前三个参数完全一样。当它检测到第三个参数的值被更新过后,它就返回,否则一直等待在那里。其调用方式如下:
7、response的使用
put_response与get_response
sequence机制提供了一种sequence→sequencer→driver的单向数据传输机制。但是在复杂的验证平台中,sequence需要根据 driver对transaction的反应来决定接下来要发送的transaction,换言之,sequence需要得到driver的一个反馈。sequence机制提供对这 种反馈的支持,它允许driver将一个response返回给sequence。
如果需要使用response,那么在sequence中需要使用get_response任务:
在driver中,则需要使用put_response任务:
这里的关键是设置set_id_info函数,它将req的id等信息复制到rsp中。由于可能存在多个sequence在同一个sequencer上启动的情况,只有设置了rsp的id等信息,sequencer才知道将response返回给哪个sequence。
除了使用put_response外,UVM还支持直接将response作为item_done的参数:
response的数量问题
通常来说,一个transaction对应一个response,但是事实上,UVM也支持一个transaction对应多个response的情况,在这种情况 下,在sequence中需要多次调用get_response,而在driver中,需要多次调用put_response:
当存在多个response时,将response作为item_done参数的方式就不适用了。由于一个transaction只能对应一个item_done,所以 使用多次item_done(rsp)是会出错的。 response机制的原理是driver将rsp推送给sequencer,而sequencer内部维持一个队列,当有新的response进入时,就推入此队列。但是此队列的大小并不是无限制的,在默认情况下,其大小为8。当队列中有8个response时,如果driver再次向此队列推送新的response,UVM就会给出如下错误提示:
UVM_ERROR @ 1753500000: uvm_test_top.env.i_agt.sqr@@case0_sequence [uvm_test_top.env.i_agt.sqr.case0_sequence] Response queue overflow, response was dropped
因此,如果在driver中每个transaction后都发送一个response,而sequence又没能及时get_response,sequencer中的response队列就存在溢出的风险。
rsp与req类型不同
前面所有的例子中,response的类型都与req的类型完全相同。UVM也支持response与req类型不同的情况。 uvm_driver、uvm_sequencer与uvm_sequence的原型分别是:
在前面章节的例子中只向它们传递了一个参数,因此response与req的类型是一样的。如果要使用不同类型的rsp与req,那么 driver、sequencer与sequence在定义时都要传入两个参数:
之后,可以使用put_response来发送response:
使用get_response来接收response:
response handler
前面讲述的get_response和put_response是一一对应的。当在sequence中启动get_response时,进程就会阻塞在那里,一直到 response_queue中被放入新的记录。如果driver能够马上将response通过put_response的方式传回sequence,那么sequence被阻塞的进 程就会得到释放,可以接着发送下一个transaction给driver。但是假如driver需要延时较长的一段时间才能将transaction传回,在此期 间,driver希望能够继续从sequence得到新的transaction并驱动它,但是由于sequence被阻塞在了那里,根本不可能发出新的 transaction。 发生上述情况的主要原因为sequence中发送transaction与get_response是在同一个进程中执行的,假如将二者分离开来,在不同 的进程中运行将会得到不同的结果。在这种情况下需要使用response_handler:
由于response handler功能默认是关闭的,所以要使用response_handler,首先需要调用use_response_handler函数,打开sequence 的response handler功能。 当打开response handler功能后,用户需要重载虚函数response_handler。此函数的参数是一个uvm_sequence_item类型的指针, 需要首先将其通过cast转换变成my_transaction类型,之后就可以根据rsp的值来决定后续sequence的行为。
使用response_handler之后,body任务中就没有和response相关的语句了,从而实现了分开处理的目的。
另类的response
无论是put/get_response或者response_handler,都是新建了一个transaction,并将其返回给sequence。事实上,当一个uvm_do语 句执行完毕后,其第一个参数并不是一个空指针,而是指向刚刚被送给driver的transaction。利用这一点,可以实现一种另类的 response:
driver中向req中的成员变量赋值,而sequence则检测这个值: