UVM Sequence基础、uvm_do宏

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

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>
  • 1
  • 2

5 my_transaction m_trans;

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

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>
  • 1

9 endfunction

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

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>
  • 1

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>
  • 1

15     #1000;

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

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>
  • 1

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>
    • 1
    • 2

    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>
    • 1

    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>
    • 1

    36 ap = mon.ap;

    <span class="code-snippet_outer"><span class="code-snippet__attr">37</span> <span class="code-snippet__string">endfunction</span></span>
    • 1
    
    当把二者连接好之后,就可以在driver中通过get\_next\_item任务向sequencer申请新的transaction:
    
    - 
    - 
    - 
    - 
    - 
    - 
    - 
    - 
    - 
    - 
    - 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    ```
    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元 红包规则

      易百纳技术社区