UVM入门与进阶7
flat sequence
body自动调用,之所以没有run,是因为它不是component
hierarchical sequence
hierarchical sequence区别于flat_sequence的地方在于,它可以使用其他sequence,当然还有item,这么做是为了创建更丰富的激励场景
`uvm_do宏做的三件事:
创建seq/item
完成随机化
传送到seqr
class hier_seq extends uvm_sequence; `uvm_object_utils(hier_seq) function new(string name = "hier_seq"); super.new(name); endfunction task body(); bus_trans t1,t2; flat_seq s1,s2; `uvm_do_with(t1, {length == 2;}) fork `uvm_do_with(s1, {length == 5;}) `uvm_do_with(s2, {length == 8;}) join `uvm_do_with(t2, {length == 3;}) endtask endclass
sequencer与driver
sequencer没有req_fifo,但是有rsp_fifo,所以端口声明的时候,analysis_port为export
seq_item_pull_port方法:
task get_next_item()
function void item_done(input RSP rsp_arg=null)
task wait_for_sequences(): 往往与try_get_item联合使用
function bit has_do_available():
function void put_response():
由于继承,原先的这三个方法也都有
1 class bus_trans extends uvm_sequence_item; 2 rand int data; 3 `uvm_object_utils_begin 4 `uvm_field_int(data, UVM_ALL_ON) 5 `uvm_object_utils_end 6 … 7 endclass 8 class flat_seq extneds uvm_sequence; 9 `uvm_object_utils(flat_seq) 10 … 11 task body(); 12 uvm_sequence_item tmp; 13 bus_trans req, rsp; 14 tmp = create_item(bus_trans::get_type(), m_sequencer, "req");//这里不用create_item也可以,使用T::type_id::create()或new()也行 15 void'($cast(req, tmp)); 16 strat_item(req); 17 req.randomize with {data == 10;}; 18 `uvm_info("SEQ", $sformatf("sent a item \n %s",req.sprint()), UVM_LOW) 19 finish_item(req); 20 get_response(tmp); 21 void'($cast(rsp, tmp)); 22 `uvm_info("SEQ", $sformatf("got a item \n %s",rsp.sprint()), UVM_LOW) 23 endtask 24 endclass 25 26 class sequencer extends uvm_sequencer; 27 `uvm_component_utils(sequencer) 28 … 29 enclass 30 class driver extends uvm_driver; 31 `uvm_component_utils(driver) 32 … 33 task run_phase(uvm_phase phase); 34 REQ tmp; 35 bus_trans req, rsp; 36 seq_item_port.get_next_item(tmp); 37 void'($cast(req, tmp)); 38 `uvm_info("DRV", $sformatf("got a item \n %s",rsp.sprint()), UVM_LOW) 39 void'($cast(rsp, req.clone())); 40 rsp.set_sequence_id(req.get_sequence_id()); 41 rsp.data +=10; 42 seq_item_port.item_done(rsp); 43 `uvm_info("DRV", $sformatf("sent a item \n %s",rsp.sprint()), UVM_LOW) 44 endtask 45 endclass 46 47 class env extends uvm_env; 48 sequencer sqr; 49 driver drv; 50 `uvm_component_utils(env) 51 … 52 function void build_phase(uvm_phase phase); 53 sqr = sequencer::type_id::create("sqr", this); 54 drv = driver::type_id::create("drv",this); 55 endfunction 56 function void connect_phase(uvm_phase phase); 57 drv.seq_item_port.connect(sqr.seq_item_export); 58 endfucntion 59 endclass 60 class test1 extends uvm_test; 61 env e; 62 `uvm_component_utils(test1) 63 … 64 function void build_phase(uvm_phase phase) 65 e = env::type_id::create("e", this); 66 endfunction 67 task run_phase(uvm_phase phase); 68 flat_seq seq; 69 phase.raise_objection(phase); 70 seq = new(); 71 seq.start(e.sqr); //将seq挂载到sqr上 72 phase.drop_objection(phase); 73 endtask 74 endclass
create_item: 创建了一个item,完成了两件事:
1.利用factory机制创建item
2.告知这个item挂载到指定的sequencer上,本例即m_sequencer
m_sequencer是uvm_sequence的成员变量
note:如果item没有挂载到sequencer上,使用start_item和finish_item的时候默认会将这个item挂载到这个sequence所挂载的sequencer上
15行的动态转换是为了访问子类中的成员变量进行constraint,但是如果一开始使用tmp.randomize(),使用父类句柄也可以随机化子类变量,因为randomize是虚方法
sqr仲裁的条件:
1.driver做get_next_item 2.要做start_item
然后选择对应的item,选择前做randomize
39行req.clone()返回的是句柄是uvm_object,为此需要转换
finish_item等到item_done的时候再release
当seq挂载到sqr上是,自身的body()将自动执行,挂载并不意味这随机化seq
上例没有指明driver拿到的类型,REQ的类型为默认的uvm_sequence_item
输出结果:
UVM_INFO @ 0: uvm_test_top.e.sqr@@flat_seq [SEQ] sent a item … UVM_INFO @ 0: uvm_test_top.e.drv [DRV] got a item … UVM_INFO @ 0: uvm_test_top.e.drv [DRV] sent a item … UVM_INFO @ 0: uvm_test_top.e.sqr@@flat_seq [SEQ] got a item …
展示了从item定义,到sequence定义,最后到sequencer与driver的连接,即sequencer和driver之间的item传输过程,帮助理解传输过程的起点、各个节点以及终点
一旦理解了这其中的朴素原理,那么这两个组件之间的握手也就不再那么神秘了
对于理解driver从sequencer获取item,经过时序处理再返回给sequence的握手过程很有帮助
事务传输过程分析
在定义sequencer时,默认了REQ类型为uvm_sequence_item类型,这与稍后定义的driver时采取默认REQ类型保持一致
flat_seq作为动态创建的数据载体,它的主任务flat_seq::body()做了如下的几件事情:
通过方法create_item()创建request item对象
调用start_item()准备发送item
在完成发送item之前对item进行随机处理
调用finish_item()完成item发送 //start_item是立即返回的,finish_item才是阻塞的行为
有必要的情况下可以从driver那里获取response item
在定义driver时,它的主任务driver::run_phase()也应通常做出如下处理:
通过seq_item_port.get_next_item(REQ)从sequencer获取有效的request item
从request item中获取数据,进而产生数据激励
对request item进行克隆生成新的对象response item
修改response item中的数据成员,最终通过seq_item_port.item_done(RSP)将response item 对象返回给sequence
对于uvm_sequence::get_response(RSP)和uvm_driver::item_done(RSP)这种成对的操作,是可选的而不是必须的,即用户可以选择uvm_driver不返回response item,同时sequence也无需获取response item
在高层环境中,应该在connect phase中完成driver到sequencer的TLM端口连接,比如例码在env::connect_phase()中通过drv.seq_item_port.connect(sqr.seq_item_export)完成了driver与sequencer之间的连接
在完成了flat_seq、sequencer、driver和env的定义之后,到了test1层,除了需要考虑挂起objection防止提前退出,便可以利用uvm_sequence类的方法uvm_sequence::start(SEQUENCER)来实现sequence到sequencer的挂载
通信时序
无论是sequence还是driver,他们通话的对象都是sequencer。当多个sequence试图挂载到同一个sequencer上时,涉及sequencer的仲裁功能
重点分析sequencer作为sequence和driver之间握手的桥梁,是如何扮演好这一角色的
我们将抽取去这三个类的主要方法,利用时间箭头演示出完整的TLM通信过程
浙公网安备 33010602011771号