简绍:
transaction/sequence_item/sequence/sequencer 这几个东西对刚接触的人来说比较绕,刚开始看这个部分的时候我头都是大的,满屏幕的sequence sequencer 还有base,傻傻分不清楚,后来想了想,只要把sequence_base跟sequence理解成一个整体,把sequencer_base和sequencer理解成一个整体就好了。
虽然这几个东西名字很像,但他们背后其实是有两套逻辑。
对于sequence,它的本质是一个从object拓展出来的transaction,sequence是sequence_item的子类,也就是一个普通payload的加强版。
对于sequencer,它的本质是一个从object扩展出来的component,它本身跟sequence_item没什么关系,要类比起来,它跟driver这种component更像,它跟driver可以理解成是同事。
打个比方,sequence_item是一个子弹,sequence是弹夹,它的作用是装子弹,而sequencer是一把枪,它的作用是把子弹射出去,driver也是一把枪。
接下来就到了第二步,展开来看,sequence这个弹夹是怎么装子弹的。
我们建立一个sequence类后,最主要要做的事情是建立一个函数来装子弹,如下所示:
task body();
jelly_bean_transaction jb_tx;//这是个sequence_item
jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));
start_item(jb_tx);
assert(jb_tx.randomize());
finish_item(jb_tx);
这篇总结的目的只有一个,理解从start_item到finish_item背后的逻辑。
1)基础用法: 在sequence中合成激励数据,然后通过start函数启动body函数,我们在body函数中通过uvm_do(或者start_item,finish_item或者send)将激励发送出去,之后就通过fifo在drive拿到数据,然后波形化。
2)项目升级用法:
在tc中的csim通过start启动tb_mac的sequence的body(sequence的嵌套),
然后设置发送激励参数,再次调用start进入主sequence的body函数,通过pack组装报文,send发出,进入driver中。
3)底层原理分析:
调用start后,就会进入下图内容。
在uvm第二章给我们介绍了最简单的uvm_do相应的宏,方便我们发送激励,在官方源码中,详细解释了这个宏主要下图几件事:
从描述可以看到,start_item到finish_item的过程调用了两个东西,一个是sequencer,一个是parent_seq,parent_seq是眼下这个sequence的父类sequence,那说到底还是个sequence,也就是说儿子把父亲做的事情先做一遍,那不重要,我们不用管它。就看sequencer的调用好了,一看发现这就是一个wait-send-再wait的通信过程。Wait中定义了很多指针和队列,用来存储start函数放入的数据。
我们在通过sequence_item来构造子弹的时候,系统帮我们实现声明了一把可以用的枪,这个枪就是sequencer_base,如果我们只是构造这个子弹,那么这个子弹和枪之间没有关系,但当我们通过sequence(弹夹)调用start_item的时候,系统就会为我们把子弹和枪联系起来。建立了这种联系后,我们后面才能调用枪(sequencer)的那三个通信函数。
最后总结整个流程:
a. 我们在声明sequence_item这个子弹和sequence这个弹夹的时候,系统都给我们预留了一把枪的位置。
b. 当我们实现sequence(弹夹)的时候,具体而言是在sequence类中的start_item函数内,系统帮我们把子弹,弹夹和枪联系了起来。
c. 在sequence类的ramdomize函数中实例化出具体的子弹。
d. 在sequence类的finish_item函数中把实例化的子弹发送出去。
Response的使用:sequence机制提供了一个机制,根据driver对tansaction的反应决定时候就接下来发送transaction(反馈机制),具有阻塞的功能,同时在pre_body函数中使用use_repinse_handler中重载,$cast(rsp,response),防止环境卡死。