第5章sequence机制

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

5、sequence机制如果发现错误,或有建议,请联系zhangqiang1227@gmail.com735.sequence机制如果说transaction是子弹的话,那么sequence无疑就是弹夹了。在整个的UVM验证平台中,sequence负责transaction的产生,并通过sequencer发送给driver,driver则根据transaction里存储的信息产生激励。要产生不同的transaction,那么就要在sequence中下功夫。验证中要对DUT施加以不同的激励,也就是施加不同的case,在UVM中,不同的case的差异主要就是体现在sequence的不同上。通过控制sequence,可以完美的产生各种不同的激励。sequence的另外一个重要的作用就是控制整个验证平台的关闭。在第三章的时候已经说过了控制验证平台关闭的方法。读完本章,想必读者会对验证平台的关闭有了更深的了解。本章第一节介绍sequence的概念,第二节说明使用uvm_do宏来构建sequence,第三节介绍virtualsequence的使用。5.1.UVM中的sequence机制sequence是什么?经过第一章之后,相信读者已经大体上有了一个印象,本节将会详细的介绍一下sequence机制。5.1、UVM中的sequence机制74如果发现错误,或有建议,请联系zhangqiang1227@gmail.com5.1.1.激励信息的产生与驱动的分离在第三章中,UVM把整个验证平台的运行过程分成了不同的phase,其目的就是为了保证在特定时间做特定事情,形成规范化,制度化,从而提高效率。这种从乱中理出思路,通过分类,来保证易用性的理念是UVM一直推崇的。UVM中,sequence的设计也几乎遵循相同的设计哲学,它把数据流(激励信息)的产生从其它部分完全独立出来,从而提高了其它部分的可重用性,增加了数据流产生的灵活性。使用1.3.2节中DUT的定义,在最原始的验证平台中,使用如下的方式产生激励:moduletop_tb;regclk;reg[7:0]rx_data;regrx_dv;wire[7:0]tx_data;wiretx_dv;dutmy_dut(.clk(clk),.rxd(rx_data),.rx_dv(rx_dv),.txd(tx_data),.tx_en(tx_dv))initialbeginclk=0;foreverbegin#10;clk=~clk;endendinitialbegin#100;@posedgeclk;rx_data=‘HF6;rx_dv=1;@posedgeclk;rx_dv=0;#100;$finish();endendmodule激励的产生与驱动都是在一个initial模块中产生的。这种方法易用性差,要产生复杂的激励比较难。读完了前四章后,大家对uvm_component的概念比较熟悉了,并且也了解了一些常用的uvm_component。driver是用于驱动接口以给DUT施加激励的。所以一种自然的想法就是在driver的main_phase中来写各种各样的代码,从5、sequence机制如果发现错误,或有建议,请联系zhangqiang1227@gmail.com75而可以产生各种各样的激励:taskmac_driver::main_phase(uvm_phasephase);mac_transactiontr;super.main_phase(phase);phase.raise_objection(this);for(inti=0;i10;i++)begintr=new;assert(tr.randomize);drive_one_pkt(tr);endphase.drop_objection(this);endtask这种情况下,其实就相当于driver的main_phase扮演了上面tob_tb中的initial块的作用。不过比initial块好的地方就在于driver中实现了transaction级别的激励产生。这种想法看起来非常美,似乎也够用了,但是仔细想一下,当我们要对DUT施加不同的激励时,那应该怎么办呢?上面的代码中是施加了正确的包,而在下一次测试中要加入crc错误的包,那么可以这么写:taskmac_driver::main_phase(uvm_phasephase);mac_transactiontr;bitsend_crc_err=0;super.main_phase(phase);phase.raise_objection(this);for(inti=0;i10;i++)begintr=new;if(send_crc_err)assert(tr.randomizewith{tr.crc_err==1;});elseassert(tr.randomize);drive_one_pkt(tr);endphase.drop_objection(this);endtask这就相当于是把整个main_phase给重新写了一遍,并且要发送CRC错误包时,还要通过别的方法把send_crc_err设置为1。如果现在有了新的需求,要再测一个超长包呢?那还要再改写main_phase,也就是说,要多测一种情况,就要多改写一次main_phase。如果经常的改写某个任务或者函数,那么就很容易把以前对的地方给改错。所以说,这种方法是不可取的,因为它的可扩展性太差,经常会带来错误。仔细观察main_phase,其实只有从tr=new语句一直到drive_one_pkt之间的语句在变。有没有什么方法把这些语句从main_phase中独立出来呢?最好的方法就是在不同的case中决定这几行语句的内容。这种想法中已经包含了激励的产生与驱动的分离这个朴素的观点。drive_one_pkt是驱动,这是driver应该做的事情,但是像产生什么样的包,如何产生等这些事情应该从driver中独立出去。5.1、UVM中的sequence机制76如果发现错误,或有建议,请联系zhangqiang1227@gmail.com5.1.2.数据流的独立那么如何实现上面的想法呢?最原始的想法应该就是使用一个函数来实现。functiongen_pkt(refmac_transactiontr);tr=new;assert(tr.randomize);endfunctiontaskmac_driver::main_phase(uvm_phasephase);mac_transactiontr;bitsend_crc_err=0;super.main_phase(phase);phase.raise_objection(this);for(inti=0;i10;i++)begingen_pkt(tr);drive_one_pkt(tr);endphase.drop_objection(this);endtask如上所示,可以定义一个产生正常包的gen_pkt函数,但是如何产生一个crc错误包的函数呢?难道像下面这样定义吗?functiongen_pkt(refmac_transactiontr);tr=new;assert(tr.randomizewith{crc_err==1;});endfunction这样带来的一个最大的问题就是gen_pkt函数的重复定义,很显然,这样是不允许的。为了避免重复定义,我们希望能够定义的函数的名字是不一样的,但是在driver的main_phase中又能执行这种具有不同名字的函数。这个问题是一个相当难的问题。用单纯的systemverilog提供的一些接口是根本无法实现的。UVM为了解决这个问题,引入了sequence机制,在解决的过程中还使用了factory机制,config机制。使用sequence机制之后,在不同的case中,把不同的sequence设置成sequencer的main_phase的default_sequence。当sequencer执行到main_phase时,发现有default_sequence,那么它就会把这个sequence启动起来。图5-1中示出了defaultsequence的设置与启动。在env.agt.sqr中,每当进入到一个新的run_timephase时,就会查看是否为此phase设置了default_sequence。图中的例子为main_phase设置了,但是没有为其它phase,所以只会在main_phase启动my_sequence。5、sequence机制如果发现错误,或有建议,请联系zhangqiang1227@gmail.com77build_phase等functionphasecheck_phase等functionphasereset_phase检查是否为reset_phase设置了defaultsequence,如果设置了启动此sequence,执行其body检查是否为main_phase设置了defaultsequence,如果设置了启动此sequence,执行其bodymain_phase............在case的build_phase中:uvm_config_db#(uvm_object_wrapper)::set(this,“env.agt.sqr.main_phase”,“default_sequence”,my_sequence::type_id::get在env.agt.sqr中图5-1defaultsequence的设置与启动仔细的想一下上面的过程,sequencer把sequence启动起来的过程就相当于是之前的gen_pkt,只是执行的位置从driver变到了sequencer。sequencer把执行后产生的transaction交给driver。这个其实跟在driver里面执行没有本质的区别。5.1.3.sequence的启动与执行上一小节提到了把sequence启动起来这样一个概念,那么sequence是如何启动的呢?假设有如下的sequence定义:classmy_sequenceextendsuvm_sequence#(mac_transaction);mac_transactionm_trans;externfunctionnew(stringname=my_sequence);virtualtaskbody();repeat(10)begin`uvm_do(m_trans)end#100;endtask5.1、UVM中的sequence机制78如果发现错误,或有建议,请联系zhangqiang1227@gmail.com`uvm_object_utils(my_sequence)endclassfunctionmy_sequence::new(stringname=my_sequence);super.new(name);endfunction//new那么启动一个sequence就应该这么做:my_sequencemy_seq;my_seq=my_sequence::type_id::create(“my_seq”);my_seq.start(sequencer);可见,要启动一个sequence非常的简单,第一步就是把这个sequence给实例化,第二步就是调用sequence的start任务,调用时要传入一个sequencer参数。当这个sequence启动起来的时候,这个sequence的body就会自动执行,我们可以想像,在my_seq.start()的实现中肯定会有这么一句:my_seq.body();事实也确是如此。读者如果对此有兴趣,可以看本书第15章sequence机制源代码分析。现在,我们可以推测sequencer的main_phase中启动sequence的过程了:它先把传递过来的default_sequence给实例化,然后调用

1 / 17
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功