UVM Sequence基础、uvm_do宏

david 2022-02-10 09:00:10 12394

Sequence的uvm_do

当一个sequence启动后会自动执行sequence的body任务。其实,除了body外,还会自动调用sequence的pre_body与post_body:

<pre class="code-snippet__js" data-lang="properties">```
<span class="code-snippet_outer"><span class="code-snippet__attr">4</span> <span class="code-snippet__string">class my_sequence extends uvm_sequence #(my_transaction);</span></span>

5 my_transaction m_trans;

<span class="code-snippet_outer"><span class="code-snippet__attr">6</span></span>

7 function new(string name= "my_sequence");

<span class="code-snippet_outer"><span class="code-snippet__attr">8</span>         <span class="code-snippet__string">super.new(name);</span></span>

9 endfunction

<span class="code-snippet_outer"><span class="code-snippet__attr">10</span></span>

11 virtual task body();

<span class="code-snippet_outer"><span class="code-snippet__attr">12</span>         <span class="code-snippet__string">repeat (10) begin</span></span>

13 `uvm_do(m_trans)

<span class="code-snippet_outer"><span class="code-snippet__attr">14</span>     <span class="code-snippet__string">end</span></span>

15     #1000;

<span class="code-snippet_outer"><span class="code-snippet__attr">16</span> <span class="code-snippet__string">endtask</span></span>

17

<span class="code-snippet_outer"><span class="code-snippet__attr">18</span> <span class="code-snippet__string">`uvm_object_utils(my_sequence)</span></span>

19 endclass

每一个sequence都应该派生自uvm_sequence,并且在定义时指定要产生的transaction的类型,这里是my_transaction。每一个sequence都有一个body任务,当一个sequence启动之后,会自动执行body中的代码。在上面的例子中,用到了一个全新的宏:uvm_do。

这个宏是UVM中最常用的宏之一,它用于:

①创建一个my_transaction的实例m_trans;

②将其随机化;

③最终将其送给sequencer。

如果不使用uvm_do宏,也可以直接使用start_item与finish_item的方式产生transaction,对于初学者来说,使用uvm_do宏即可。

一个sequence在向sequencer发送transaction前,要先向sequencer发送一个请求,sequencer把这个请求放在一个仲裁队列中。

作为sequencer,它需做两件事情:

第一,检测仲裁队列里是否有某个sequence发送transaction的请求;

第二,检测driver是否申请transaction。

1)如果仲裁队列里有发送请求,但是driver没有申请transaction,那么sequencer将会一直处于等待driver的状态,直到driver申请新的transaction。此时,sequencer同意sequence的发送请求,sequence在得到sequencer的批准后,产生出一个transaction并交给sequencer,后者把这个transaction交给driver。

2)如果仲裁队列中没有发送请求,但是driver向sequencer申请新的transaction,那么sequencer将会处于等待sequence的状态,一直到有sequence递交发送请求,sequencer马上同意这个请求,sequence产生transaction并交给sequencer,最终driver获得这个transaction。

3)如果仲裁队列中有发送请求,同时driver也在向sequencer申请新的transaction,那么将会同意发送请求,sequence产生transaction并交给sequencer,最终driver获得这个transaction。

driver如何向sequencer申请transaction

driver如何向sequencer申请transaction呢?在uvm_driver中有成员变量seq_item_port,而在uvm_sequencer中有成员变量seq_item_export,这两者之间可以建立一个“通道”,通道中传递的transaction类型就是定义my_sequencer和my_driver时指定的transaction类型,在这里是my_transaction,当然了,这里并不需要显式地指定“通道”的类型,UVM已经做好了。在my_agent中,使用connect函数把两者联系在一起:

<pre class="code-snippet__js" data-lang="properties">```
<span class="code-snippet_outer"><span class="code-snippet__attr">31</span> <span class="code-snippet__string">function void my_agent::connect_phase(uvm_phase phase);</span></span>

32 super.connect_phase(phase);

<span class="code-snippet_outer"><span class="code-snippet__attr">33</span>     <span class="code-snippet__string">if (is_active == UVM_ACTIVE) begin</span></span>

34 drv.seq_item_port.connect(sqr.seq_item_export);

<span class="code-snippet_outer"><span class="code-snippet__attr">35</span>     <span class="code-snippet__string">end</span></span>

36 ap = mon.ap;

<span class="code-snippet_outer"><span class="code-snippet__attr">37</span> <span class="code-snippet__string">endfunction</span></span>

当把二者连接好之后,就可以在driver中通过get\_next\_item任务向sequencer申请新的transaction:

- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
```
22 task my_driver::main_phase(uvm_phase phase);
``````
23 vif.data 
``````
24 vif.valid 
``````
25 while(!vif.rst_n)
``````
26 @(posedge vif.clk);
``````
27 while(1) begin
``````
28 seq_item_port.get_next_item(req);
``````
29 drive_one_pkt(req);
``````
30 seq_item_port.item_done();
``````
31 end
``````
32 endtask
```
```

在如上的代码中,一个最显著的特征是使用了while(1)循环,因为driver只负责驱动transaction,而不负责产生,只要有transaction就驱动,所以必须做成一个无限循环的形式。这与monitor、reference model和scoreboard的情况非常类似。

通过get\_next\_item任务来得到一个新的req,并且驱动它,驱动完成后调用item\_done通知sequencer。这里为什么会有一个item\_done呢?当driver使用get\_next\_item得到一个transaction时,sequencer自己也保留一份刚刚发送出的transaction。当出现sequencer发出了transaction,而driver并没有得到的情况时,sequencer会把保留的这份transaction再发送出去。那么sequencer如何知道driver是否已经成功得到transaction呢?如果在下次调用get\_next\_item前,item\_done被调用,那么sequencer就认为driver已经得到了这个transaction,将会把这个transaction删除。换言之,这其实是一种为了增yun加可靠性而使用的握手机制。

**那么uvm\_do怎么握手?**在sequence中,向sequencer发送transaction使用的是uvm\_do宏。这个宏什么时候会返回呢?uvm\_do宏产生了一个transaction并交给sequencer,driver取走这个transaction后,uvm\_do并不会立刻返回执行下一次的uvm\_do宏,而是等待在那里,直到driver返回item\_done信号。此时,uvm\_do宏才算是执行完毕,返回后开始执行下一个uvm\_do,并产生新的transaction。

在实现了driver后,接下来的问题是:sequence如何向sequencer中送出transaction呢?前面已经定义了sequence,只需要在某个component(如my\_sequencer、my\_env)的main\_phase中启动这个sequence即可。以在my\_env中启动为例:

- 
- 
- 
- 
- 
- 
- 

```
```
48 task my_env::main_phase(uvm_phase phase);
``````
49 my_sequence seq;
``````
50 phase.raise_objection(this);
``````
51 seq = my_sequence::type_id::create("seq");
``````
52 seq.start(i_agt.sqr);
``````
53 phase.drop_objection(this);
``````
54 endtask
```
```

首先创建一个my\_sequence的实例seq,之后调用start任务。start任务的参数是一个sequencer指针,如果不指明此指针,则sequence不知道将产生的transaction交给哪个sequencer。

这里需要引起关注的是objection,在UVM中,objection一般伴随着sequence,通常只在sequence出现的地方才提起和撤销objection。如前面所说,sequence是弹夹,当弹夹里面的子弹用光之后,可以结束仿真了。

也可以在sequencer中启动sequence:

- 
- 
- 
- 
- 
- 
- 

```
```
task my_sequencer::main_phase(uvm_phase phase);
``````
my_sequence seq;
``````
phase.raise_objection(this);
``````
seq = my_sequence::type_id::create("seq");
``````
seq.start(this);
``````
phase.drop_objection(this);
``````
endtask
```
```

在sequencer中启动与在my\_env中启动相比,唯一区别是seq.start的参数变为了this。

**与uvm\_do等价的“uvm\_create与uvm\_send”**

除了使用uvm\_do宏产生transaction,还可以使用uvm\_create宏与uvm\_send宏来产生。

- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 

```
```
class case0_sequence extends uvm_sequence #(my_transaction);
``````
  virtual task body();
``````
    int num = 0;
``````
    int p_sz;
``````
    repeat (10) begin
``````
      num++;
``````
      `uvm_create(m_trans)
``````
      assert(m_trans.randomize());
``````
      p_sz = m_trans.pload.size();
``````
      { m_trans.pload[p_sz - 4],
``````
        m_trans.pload[p_sz - 3],
``````
        m_trans.pload[p_sz - 2],
``````
        m_trans.pload[p_sz - 1]}
``````
      = num;
``````
      `uvm_send(m_trans)
``````
    end
``````
  endtask
``````
endclass
```
```

uvm\_create宏的作用是实例化transaction。当一个transaction被实例化后,可以对其做更多的处理,处理完毕后使用uvm\_send宏发送出去。这种使用方式比uvm\_do系列宏更加灵活。如在上例中,就将pload的最后4个byte替换为此transaction的序号。

事实上,在上述的代码中,也完全可以不使用uvm\_create宏,而直接调用new进行实例化。

- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 

```
```
virtual task body();
``````

``````
m_trans = new("m_trans");
``````
assert(m_trans.randomize());
``````
p_sz = m_trans.pload.size();
``````
{m_trans.pload[p_sz - 4],
``````
m_trans.pload[p_sz - 3],
``````
m_trans.pload[p_sz - 2],
``````
m_trans.pload[p_sz - 1]}
``````
= num;
``````
`uvm_send(m_trans)
``````

``````
endtask
```
```

除了uvm\_send外,还有uvm\_send\_pri宏,它的作用是在将transaction交给sequencer时设定优先级:

- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 

```
```
virtual task body();
``````

``````
  m_trans = new("m_trans");
``````
  assert(m_trans.randomize());
``````
  p_sz = m_trans.pload.size();
``````
  {m_trans.pload[p_sz - 4],
``````
  m_trans.pload[p_sz - 3],
``````
  m_trans.pload[p_sz - 2],
``````
  m_trans.pload[p_sz - 1]}
``````
  = num;
``````
  `uvm_send_pri(m_trans, 200)
``````

``````
endtask
```
```

****与uvm\_do等价的“**start\_item与finish\_item”**

使用宏来产生transaction。宏隐藏了细节,方便了用户的使用,但是也给用户带来了困扰:宏到底做了什么事情?

不使用宏产生transaction的方式要依赖于两个任务:start\_item和finish\_item。在使用这两个任务前,必须要先实例化transaction后才可以调用这两个任务:

- 
- 
- 

```
```
tr = new("tr");
``````
start_item(tr);
``````
finish_item(tr);
```
```

完整使用如上两个任务构建的一个sequence如下:

- 
- 
- 
- 
- 
- 
- 

```
```
virtual task body();
``````
  repeat(10) begin
``````
  tr = new("tr");
``````
  start_item(tr);
``````
  finish_item(tr);
``````
  end
``````
endtask
```
```

上述代码中并没有对tr进行随机化。可以在transaction实例化后、finish\_item调用前对其进行随机化:

- 
- 
- 
- 
- 
- 
- 
- 
- 
- 

```
```
class case0_sequence extends uvm_sequence #(my_transaction);
``````
  virtual task body();
``````
    repeat (10) begin
``````
      tr = new("tr");
``````
      assert(tr.randomize() with {tr.pload.size == 200;});
``````
      start_item(tr);
``````
      finish_item(tr);
``````
      end
``````
  endtask
``````
endclass
```
```

上述assert语句也可以放在start\_item之后、finish\_item之前。uvm\_do系列宏其实是将下述动作封装在了一个宏中:

- 
- 
- 
- 
- 
- 
- 
- 

```
```
virtual task body();
``````

``````
  tr = new("tr");
``````
  start_item(tr);
``````
  assert(tr.randomize() with {tr.pload.size() == 200;});
``````
  finish_item(tr);
``````

``````
endtask
```
```

文章内容参考自:张强《UVM实战》

感谢阅读,别走!点赞、关注、转发后再走吧

![](https://ebaina.oss-cn-hangzhou.aliyuncs.com/wechat-official-crawl/2022-02/164445481034553.jpg)

转载:全栈芯片工程师
                
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
david
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
david
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区