- 一、uvm_do系列宏
1.1 `uvm_do宏及其用法
sequence机制提供了丰富的宏用于产生和发送tr,调动sequence,sequencer和driver三者交互,`uvm_do系列宏最为常用。`uvm_do系列宏主要包括下表:
宏定义 |
参数说明 |
`uvm_do(tr_or_seq) |
只有一个参数,可以是transaction或者sequence |
`uvm_do_with(tr_or_seq, {cons}) |
有两个参数,一个是传输的tr或者seq,第二个参数为tr或者seq的约束 |
`uvm_do_pri(tr_or_seq, priority) |
两个参数,一个是传输的tr或者seq,第二个参数为tr或者seq的优先级 |
`uvm_do_pri_with(tr_or_seq, pri, {cons}) |
三个参数,一个是传输的tr或者seq,第二个参数是tr/seq的优先级,第三个参数是约束 |
这个系列宏实际上集成度较为高,因为宏定义并没有给出seq/tr在哪个sequencer上启动,实际上,该系列宏都是从默认的m_sequencer上启动的,也就是通过seq.start函数传递的sqr指针。
这些宏可以写在seqeunce中的body函数里,用于发送tr或者做sequence的嵌套使用。
1.2 `uvm_do_on宏及其用法
`uvm_do_on系列宏可以指定sequence或者transaction在哪个sqr上启动,特别是如果系统中包含多个sequencer,顶层通过virtual sequencer控制,可以通过在sequence中使用`uvm_declare_p_sequencer(my_sqr)来指定sequence的启动位置。`uvm_do_on系列宏包括:
宏定义 |
参数说明 |
`uvm_do_on(tr_or_seq, sqr) |
有两个参数,第二个参数为sqr的指针 |
`uvm_do_on_with(tr_or_seq, sqr, {cons}) |
有三个参数,第二个为sqr,第三个为约束 |
`uvm_do_on_pri(tr_or_seq, sqr, pri) |
三个参数,第三个指定了优先级 |
`uvm_do_on_pri_with(tr_or_seq, sqr, pri, {cons}) |
四个参数,第三个为优先级,第四个为约束 |
事实上,无论是`uvm_do系列宏还是`uvm_do_on系列宏,底层都是`uvm_do_on_pri_with这个参数最全的宏定义,不一样的宏分别对这个宏做了参数固化,比如`uvm_do:
`uvm_do_on_pri_with(tr_or_seq, m_sequencer, -1, {})
此时,sequencer默认传递为m_sequencer,优先级为-1,即不具备优先级,约束为空,不具备约束。同理,其他宏也在此宏定义上做了参数的固化:
最后,我们来分析一下`uvm_do_on_pri_with的源码:
这段源码先申明了一个uvm_sequence_base类型的_seq,然后通过`uvm_create_on宏将其实例化出来,并指定在参数SEQR上实例化(指定m_sequencer为SEQR),然后有一个if条件,思考一下,如果SEQ_OR_ITEM参数此时如果是transcation,会发生什么?
这个问题的答案很简单,如果是transaction,cast会返回0,条件成立,执行start_item,此时start_item参数为指定的transaction,并设定优先级。为什么cast会为0?还记得transaction继承自哪个类吗?实际上,transaction继承自uvm_sequence_item,也就是uvm_sequence_base的父类,因此父类的继承类cast给子类的继承类,当然无法cast,返回0。
中间判断了如果transaction执行不了randomize,会报warning,可以不用管它,然后又是一段判断,条件和上一个if一致,如果是transaction,执行finish_item,指定transaction和优先级。如果参数是seq,那么else分支会调用seq的start函数。
分析过后,实际上可以得到一个总结:
- `uvm_do系列宏的底层是`uvm_do_on_pri_with宏,只不过将参数进行了固化;
- `uvm_do_on_pri_with宏会实际上是调用start_item和finish_item方法,并且只有在参数是transaction的时候会调用。
- 如果参数是sequence,会调用sequence的start任务,start任务会接着调用sequence的pre_body,body,post_body,如果子sequence的body中的uvm_do系列宏传递的是transaction,那么会调用start_item和finish_item。
- 如果参数是sequence,最终会调用transaction为参数的uvm_do系列宏,并落在start_item和finish_item任务上。
- start_item和finish_item任务的参数只能是transaction,不能是sequence。
事实上,除了uvm_do系列宏之外,sequence还提供了专门用于发送sequence系列宏,主要包括:uvm_do_seq,uvm_do_seq_with,这两个宏底层是uvm_do和uvm_do_with宏,对其参数进行了固化,并且只能传参seq,并且需要指定sqr。用的比较少,不过多讨论。
- 二、uvm_create和uvm_send
2.1 `uvm_create和`uvm_create_on
除了使用uvm_do系列宏可以发送激励之外,uvm还提供了`uvm_create或者`uvm_create_on,用于生成transaction的实例,然后结合`uvm_send宏将transaction发送出去。`uvm_create(seq_or_item),参数是sequence或者transaction,`uvm_create_on(seq_of_item, SEQR),参数有两个,除了要传输的sequence或者transaction之外,还有一个sequencer的指针。实际上,`uvm_create宏的底层就是`uvm_create_on宏:
然后来分析一下`uvm_create_on宏的源码:
这个宏先申明了一个uvm_object_wrapper的变量w_,这个类和factory机制有关,然后通过调用SEQ_OR_ITEM的get_type函数返回uvm_object_wrapper的指针,赋值给w_,然后通过cast方法,调用create_item函数,将实例化的类赋值给SEQ_OR_ITEM,那么关键在于create_item:
这个函数位于基类uvm_sequence_base中,会先声明uvm_factory,然后通过create_object_by_type的方式实例化类,实际上是通过类名字符串进行创建实例,并cast给create_item返回值。因此`uvm_create的创建方式,实际上是调用了factory机制进行实例创建,因此是支持factory机制的重载的。
那么也可以给出总结:
- `uvm_create的底层是`uvm_create_on,对其参数进行了封装
- `uvm_create的逻辑是通过factory机制创建sequence或者item的实例,支持sequence的重载和item的重载
- 用这个宏的方式创建实例,等价于seq_or_item::type_id::create(“name”)创建实例的方式。
- 如果使用new函数创建seq或者item的实例,那么不支持重载。
另外,还有一个专门用于生成seq的宏:uvm_create_seq,底层是uvm_create_on宏,对参数做了固化,用的比较少,不做过多讨论。
2.2 `uvm_send和`uvm_send_pri
这个宏定义用于发送sequence或者item,用法是直接将seq或者item作为宏的参数传递给`uvm_send,`uvm_send_pri宏除了要传递seq或者item之外,还要指明优先级,实际上,`uvm_send的底层是`uvm_send_pri,只不过优先级传参为-1:
分析一下`uvm_send_pri的源码:
这个宏和uvm_do系列宏的逻辑一样,如果是item会调用start_item和finish_item,如果是sequence,会调用sequence的start函数。
值得注意的是uvm_send宏不会对transaction进行随机化,因此如果有随机需求,需要在宏外调用randomize对tr或者seq进行随机,然后再使用uvm_send。
2.2 `uvm_rand_send系列宏
这个系列宏可以对transaction进行随机化,并发送,也就是在uvm_send的基础上集成了seq或者tr的随机。这个系列宏有四个:
宏定义 |
参数说明 |
`uvm_rand_send(tr_or_seq) |
一个参数,tr或者seq |
`uvm_rand_send_pri(tr_or_seq, pri) |
两个参数,第二个参数是优先级 |
`uvm_rand_send_with(tr_or_seq, cons) |
两个参数,第二个参数是约束 |
`uvm_rand_send_pri_with(tr_or_seq, pri, cons) |
三个参数,第二个为优先级,第三个为约束 |
事实上,这几个宏的底层都是`uvm_rand_send_pri_with,其他宏在其基础上做了参数固化,对于`uvm_rand_send_pri_with的源码分析,逻辑和其他的宏一样,只不过这个宏除了调用start_item和finish_item之外,还需要调用get_sequencer来获取sequencer,因此,这一系列宏需要和uvm_create系列宏配合使用。
- start_item和finish_item
3.1 start_item和finish_item及用法
如果不想用sequence的宏来声明产生transaction的调度,用户也可以直接调用start_item和finish_item方法,启动transaction的调度,需要注意的是,这个方法只能用来传输transaction,不能用来传输sequence,而且在传输transaction之前,需要先实例化transaction,无论是直接调用new函数的方式,还是调用factory机制实例化。
transaction可以进行随机,需要单独写出来,因为start_item和finish_item不具备随机功能。可以写在两个方法之前,也可以写在两个方法中间。
start_item和finish_item可以指定优先级,作为第二参数传递,如果不指定优先级,那么优先级参数默认为-1,即不具备优先级。通过一段代码说明这个的用法,假设有sequence:
1. class my_sequence extends uvm_sequence(uvm_sequence_item);
2. `uvm_object_utils(my_sequence)
3. `uvm_declare_p_sequencer(my_sequencer)
4. ......
5. virutal task body();
6. my_transaction my_tr;
7. my_tr = new("my_tr");
8. //my_tr = my_transaction::type_id::create("my_tr");
9. //`uvm_create(my_tr) or `uvm_create_on(my_tr, p_sequencer);
10. assert(my_tr.randomize() with {my_tr.data_size==200;});
11. start_item(my_tr);
12. //start_item(my_tr, 200); //指定优先级
13. //assert(my_tr.randomize() with {my_tr.data_size==200;});//随机化可以写在中间
14. finish_item(my_tr);
15. //finish_item(my_tr, 200);//指定优先级
16. endtask:body()
17. ......
18. endclass
3.2 start_item和finish_item源码展开和底层逻辑
还是在理解底层逻辑的情况下体会uvm_sequence机制的使用,在uvm源码中,有这样一段注释:
事实上,start_item的源码展开,包括了两个任务:sequencer的wait_for_grant任务和parent_seq的pre_do,参数传1:
这里涉及到sequence和sequencer以及driver三者交互,在之前有篇帖子介绍过《UVM-Seq-Sqr-Driver交互逻辑》,这里不做多介绍。那么实际上核心任务是调用了pre_do,原型为:
任务直接返回,有一个参数表示是否是item,start_item必须参数为item,因此参数固化为1。
对于finish_item:
这个任务中,调用了mid_do,参数为item,以及sqr的send_request,wait_for_item_done,都和三者交互有关,不做多讨论。最后会调用post_do,参数传递为item,对于mid_do:
同样直接返回了,然后post_do:
直接返回。
因此,在调用uvm_do系列宏,或者是uvm_send的时候,可以把对transaction的处理放在重载的pre_do,mid_do或者post_do函数中,一般写在mid_do函数中。
- 四、pre_do、mid_do和post_do
4.1 pre_do、mid_do和post_do的用法
这几个方法,在实际工程中用的比较少,用法就是在sequence中重载pre_do,mid_do和post_do,然后在任务或者函数体内完成对tr的初始化,随机等动作,然后在body函数中调用uvm_do系列宏,或者uvm_send等。需要注意的是,mid_do的参数是uvm_sequence_item,重载mid_do,需要将参数的uvm_sequence_item通过cast的方式指针传递给申明的transaction,然后对transaction进行一些初始化动作。
实际上uvm_do系列宏,包含了对transaction的初始化动作,而且就算uvm_do系列宏达不到对transaction的处理目标,也可以通过uvm_create和uvm_send/uvm_rand_send的组合实现,比如需要关掉transaction中的某些约束,并对其重定义。使用重载pre_do,mid_do,和post_do的方法只会把简单的方式复杂化。本文参考《UVM实战》中的例子,不重新自己写代码:
这段代码核心功能重载了mid_do,在mid_do中对tr进行了定义,将pload赋值为num等,然后调用tr的calc_crc,对crc域进行计算,最后在body中使用uvm_do启动,实际上是为transaction打了id(id就是num),赋值给pload。
参考资料《UVM实战》