UVM基础-sequence系列宏

本文详细介绍了UVM中的宏,如uvm_do系列、uvm_do_on系列、uvm_create系列和uvm_send系列,以及它们在sequence、transaction和sequencer交互中的用法。重点解析了这些宏的底层逻辑,包括start_item和finish_item方法,以及pre_do、mid_do和post_do的使用。此外,还讨论了如何通过重载这些方法来定制transaction的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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。用的比较少,不过多讨论。

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系列宏配合使用。

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函数中。

 

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实战》

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值