对sequence的一些理解
sequence其实不属于验证平台的一部分,他是object而不是component,但是sequence跟component的sequencer密切相关。只有在sequencer的帮助下,sequence产生出的transaction才能最终送给driver。这里就有几个问题:如何去理解transaction? sequencer是如何把sequence送给driver的?transaction可以理解为一个具备某种数据结构的一个数据,一个transaction里面有一包或者一帧数据的很多特征信息,sequence就是一个装满了transaction,可以有各种各样的transaction。如果把sequence比作弹夹,transaction比作子弹,那么sequencer就是一把枪,一把枪可以有很多弹夹吧,一把枪只有有弹夹,弹夹有子弹,那么这把枪才有意义吧?借这个比喻,如果一个子弹(transaction)打出去了,那么该子弹的声明周期到期了,但是弹夹(sequence)没有,因为里面还有很多子弹,等子弹打完了,sequence的生命周期也就到期了。
sequence派生自uvm_sequence,transaction派生自uvm_sequence_item,在定义弹夹(sequence)的时候要指定用什么子弹(transaction),当一个sequence驱动之后,会自动执行sequence里面的一个body task,将一发发子弹打出去。有两种方式打子弹:1.使用uvm_do这个宏;2.使用start_item和finish_item任务完成。解释:使用uvm_do这个宏,方便但是灵活性比较差,因为uvm_do宏封装很多操作,封装的越多,灵活性越差。uvm_do做了三件事,第一件,创建一个自定义的transaction的实例,第二,将其随机化,第三将其发送给sequencer。如果不是用uvm_do这个宏,如何完成上述工作?可以使用start_item和finish_item这两个任务。这连个任务做了啥?其实这两个任务分别封装了跟加底层,更加灵活的函数和任务,但是一般使用start_item和finish_item就足够了。
现在明白了sequence这个弹夹要把transaction这些子弹通过手枪sequencer打出去,那么弹夹(sequence)把子弹(transaction)送给手枪(sequencer),让手枪(sequencer)发射有没有一个握手或者请求通知之类的呢?一个sequence在向sequencer发送transaction前,先向sequencer发送一个请求,sequencer把这个请求放在一个仲裁队列中。这样,sequencer就只要做两件事,一是检查有没有sequence要发送transaction的请求,二是driver有没有申请transaction(别人没请求你开枪,就不能乱开枪吧?一旦请求了开枪,总得要看看枪里有没有准备好的子弹吧?)。
那么问题来了,driver是如何向sequencer申请transaction的呢?在uvm_driver中有成员变量seq_item_port,而在uvm_sequencer中有成员变量seq_item_export,这两个port端口在agent的connect_phase连接到一起了(手动去连接),driver是主,sequencer是从。当二者连接好了之后,driver调用get_item_item这个任务,任务的参数就是transaction类型的,这样就向sequencer申请了新的transaction。由此可见,driver只是负责获取并且驱动transaction而不负责去产生transaction。驱动transaction部分自己按照时序要求去写,驱动走了一个transaction后,要不要通知sequencer,你给我的transaction我已经成功发送出去了?答案是要的,这是一种增加可靠性的握手机制。那么driver如何告诉sequencer我的transaction发送OK了?驱动完成后调用item_done通知sequencer,在没有通知之前,sequencer其实自己会保留一份刚刚送给driver的transaction,万一driver没有收到,那sequencer就再发一次。当sequencer收到item_done之后,sequencer就认为driver已经得到了这个transaction,将会把这个transaction删除,此时,sequence中的uvm_do宏才算是执行完毕。
driver向sequencer那里拿transaction,sequencer的transaction又是从sequence中来的,那么问题来了,sequence是如何把transaction给sequencer的呢?只需要在某个component中去启动这个sequence,在启动sequence的时候,要指定是哪个sequencer。其过程是首先创建一个自定义的sequence的实例seq,之后调用start任务,穿三就是启动这个transaction的sequencer指针。这个过程是在run_phase(main_phase)中完成的。注意在调用start任务前后,要调用phase.raise_objection和phase.drop_objection。在很多main_phase中,objection会伴随着sequence,sequence这个弹夹把里面的子弹transaction打完了,仿真就可以结束了,也就drop_objection了。
还有一种default_sequence,用法稍微简单点,在某个component例如env中使用uvm_config_db去指定,哪个sequencer的哪个phase把哪个sequence指定为default的,这种自动的方式启动sequence用的地方还是比较多的。
今天就总结写入门的东西,理清之间的关系,后面sequence的一些高级用法,再总结。