UVM Sequence基础、uvm_do宏
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) 转载:全栈芯片工程师
- 分享
- 举报
-
浏览量:4254次2022-02-11 09:00:12
-
浏览量:851次2022-09-03 09:00:55
-
2019-06-19 16:48:17
-
浏览量:2447次2019-07-05 14:57:32
-
浏览量:2942次2020-07-24 10:45:35
-
浏览量:1520次2024-01-24 17:07:21
-
浏览量:5485次2020-11-30 10:01:37
-
浏览量:4449次2021-04-19 14:55:00
-
浏览量:1910次2019-01-21 23:34:54
-
浏览量:1458次2023-12-06 12:30:38
-
浏览量:518次2024-01-25 14:14:02
-
浏览量:1824次2021-01-27 16:48:37
-
浏览量:2601次2018-01-22 15:32:07
-
浏览量:2258次2018-02-01 18:42:08
-
浏览量:1590次2018-08-11 12:20:11
-
浏览量:4723次2022-03-29 09:00:11
-
浏览量:1351次2019-12-09 10:34:06
-
浏览量:582次2024-01-26 08:39:23
-
浏览量:6344次2020-08-30 12:34:17
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
david
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明