1. 前言
UVM在sequence里提供了很多的callback方法给用户,从而更灵活地完成各种复杂场景的交互和控制执行顺序。我们可能在很多情况下只使用了body()方法,本文将介绍sequence里常见的callback方法,以及在不同场景下,它们的是否被调用和调用顺序。
2. start()方法参数
UVM中,sequence的执行是通过调用它的start()方法,可以直接调用start()或者间接调用
virtual task start (uvm_sequencer_base sequencer,
uvm_sequence_base parent_sequence = null,
int this_priority = -1,
bit call_pre_post = 1);
start()有4个参数:
- 第一个参数sequencer指定哪一个sequencer去运行这个sequence,sequencer的类型必须要和sequence兼容,另外,如果调用start()方法没有传递sequencer的话,start()会默认使用第二个参数parent_sequence的sequencer。
- 第二个参数parent_sequence用于传递当前调用start()方法sequence的父sequence。如果parent_sequence传递的是null,那么当前sequence将是最顶层sequence(root sequence)。否则,在sequence层次结构上它将是parent_sequence的子sequence。Parent_sequence的pre_do(), mid_do()和post_do()方法将在子sequence执行期间被调用。
- 第三个参数this_priority用于指定仲裁优先级,数字越高优先级越高。默认情况下,也就是this_priority等于-1,那么sequence的priority等于父sequence的priority。如果sequence是root sequence,那么默认它的priority是100。当然,如果this_priority传递的值大于等于0,那么sequence的priority将直接使用该值。
- 第四个参数call_pre_post的参数值会影响到sequence的pre_body()和post_body()在否会在body()前后被调用。如果为1,那么在执行sequence的body()前会调用pre_body(),执行后会调用post_body()。反之则不会。
3. start()方法执行
Sequence start()方法会调用的方法可能有:pre_start(), pre_body(), pre_do(), mid_do(), body(), post_do(), post_body(), post_start()。为什么说是可能呢?我们在介绍start()参数时说过,parent_sequence参数和call_pre_post参数的取值会影响pre_do(), mid_do(), post_do(), pre_body()和post_body()的调用只有pre_start(), post_start()和body()不受这些参数的影响,一定会被调用,因此用户如果有些代码在任何情况下都必须被执行,最好放在这三个方法里。因此,根据用户的使用需求,sequence的start()方法被直接调用时可以通过控制参数来完成不同的功能。下面举例子来更清楚地说明。
例子1:
假设有两个sequence,分别叫parent_seq和sub_seq,sub_seq例化在parent_seq内,并且在parent_seq的body()内调用sub_seq.start(seqr, parent_seq, priority, call_pre_post),那么将按顺序调用以下方法:
sub_seq.pre_start() (task)
sub_seq.pre_body() (task) if call_pre_post==1
parent_seq.pre_do(0) (task) if parent_sequence!=null
parent_seq.mid_do(this) (func) if parent_sequence!=null
sub_seq.body (task) YOUR STIMULUS CODE
parent_seq.post_do(this) (func) if parent_sequence!=null
sub_seq.post_body() (task) if call_pre_post==1
sub_seq.post_start() (task)
根据上述伪代码,pre_do(), mid_do(), post_do(), pre_body()和post_body()是否执行是受call_pre_post和parent_sequence参数控制的。
例子2:
假设有两个sequence,分别叫parent_seq和sub_seq,sub_seq例化在parent_seq内,并且在parent_seq的body()内调用
uvm_do_with_prior(parent_seq, { constraints }, priority),那么将按顺序调用以下方法:
sub_seq.pre_start() (task)
parent_seq.pre_do(0) (task)
parent_req.mid_do(sub_seq) (func)
sub_seq.body() (task)
parent_seq.post_do(sub_seq) (func)
sub_seq.post_start() (task)
根据上述伪代码,uvm_do_with_prior宏在使用start()方法时,传递的call_pre_post参数默认设置为0,所以sub_seq的pre_body()和post_body()是不会被执行的。
例子3:
假设有1个sequence和1个sequence_item, 分别叫parent_seq和item,item例化在parent_seq内,并且在parent_seq的body()内调用parent_seq.start_item(item, priority)和parent_seq.finish_item(item),或者调用uvm_do_with_prior(item, constraints, priority) ,那么将按顺序调用以下方法:
sequencer.wait_for_grant(prior) (task) \ start_item \
parent_seq.pre_do(1) (task) / \
`uvm_do* macros
parent_seq.mid_do(item) (func) \ /
sequencer.send_request(item) (func) \finish_item /
sequencer.wait_for_item_done() (task) /
parent_seq.post_do(item) (func) /
根据上述伪代码,parent_seq在调用start_item()/finish_item()或uvm_do宏去启动item的过程中,parent_seq的pre_do(), mid_do()和post_do()方法也会被按顺序调用。
另外,pre_do(is_item)里的is_item的值会指示是启动sequence还是sequence_item导致它被调用,mid_do()和post_do()会把启动它们的sequence或sequence_item的指针传递给parent_sequence,这样可以更灵活完成更多功能,可以发挥自己的想象力。比如说,在mid_do()里根据子sequence的不同,初始化子sequence的内部变量;或者统计在pre_do()里启动sequence_item和sequence的个数等等。
作者:谷公子
文章来源:CSDN
推荐阅读
更多IC设计干货请关注IC设计专栏。
迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。