在UVM中,将激励生成和测试平台分开,使平台只编译一次,但可以运行多个激励,要实现这一点需要sequence机制、factory机制和config_db机制。
在不同的测试用例中,将不同的sequence设置成sequencer的main_phase中的default_sequence。当sequencer执行到main_phase时,发现有default_sequence,就启动sequence。
1. sequence的启动 1)方法一
my_sequence seq0; seq0 = my_sequence::type_id::create("seq0" ); uvm_config_db#(uvm_sequence_base)::set(this,"env.i_agt.sqr.main_phase","default_sequence",seq0) ;
2)方法二
1 uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_sequence::type_id::get()) ;
3)方法三
1 2 3 my_sequence seq0; seq0 = my_sequence::type_id::create("seq0" ); seq0.start (env.i_agt .sqr );
2. sequence相关的宏 1)`uvm_do系列
在sequence类中的body任务里调用`uvm_do系列的宏,用来指定在那个sequencer中发送transaction。
1 2 3 4 5 6 7 8 `uvm_do (SEQ_OR_ITEM) `uvm_do_pri (SEQ_OR_ITEM, PRIORITY) `uvm_do_with (SEQ_OR_ITEM, CONSTRAINTS) `uvm_do_pri_with (SEQ_OR_ITEM, PRIORITY, CONSTRAINTS) `uvm_do_on (SEQ_OR_ITEM, SEQR) `uvm_do_on_pri (SEQ_OR_ITEM, SEQR, PRIORITY) `uvm_do_on_with (SEQ_OR_ITEM, SEQR, CONSTRAINTS) `uvm_do_on_pri_with (SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS)
在UVM中uvm_do的其他七个宏都是从是从`uvm_do_on_pri_with产生的
1 2 `define uvm_do(SEQ_OR_ITME) \ `uvm_do_on_pri_with(SEQ_OR_ITEM,m_sequencer,-1 ,{})
下面是一个`uvm_do_with的例子:
1 2 3 4 5 6 7 8 9 10 11 12 class crc_seq extends uvm_sequence#(my_transaction) ; `uvm_object_utils(crc_seq) function new (string name= "crc_seq" ); super .new (name); endfunction virtual task body(); my_transaction tr; `uvm_do_with(tr, {tr.crc_err == 1 ; tr.dmac == 48'h980F ;}) endtask endclass
2)`uvm_send系列
1 2 3 4 5 6 `uvm_send (SEQ_OR_ITEM) `uvm_send_pri (SEQ_OR_ITEM, PRIORITY) `uvm_rand_send (SEQ_OR_ITEM) `uvm_rand_send_pri (SEQ_OR_ITEM, PRIORITY) `uvm_rand_send_with (SEQ_OR_ITEM, CONSTRAINTS) `uvm_rand_send_pri_with (SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
在使用`uvm_send之前,transaction必须是已经创建好了的,可以用`uvm_create宏来创建,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 virtual task body(); int num = 0 ; int p_sz; my_transaction m_trans; if (starting_phase != null ) starting_phase.raise_objection (this ); 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 #100 ; if (starting_phase != null ) starting_phase.drop_objection (this ); endtask
3. sequence相关函数 1)start_item和finish_item
用这两个函数来代替`uvm_do系列的宏,来控制transaction的发送。但是要求transaction是已经创建好了的。
1 2 3 4 virtual task start_item ( uvm_sequence_item item,int set_priority = -1 , uvm_sequencer_base sequencer = null ) virtual task finish_item ( uvm_sequence_item item,int set_priority = -1 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 virtual task body(); my_transaction tr; if (starting_phase != null ) starting_phase.raise_objection (this ); repeat (10 ) begin tr = new ("tr" ); assert (tr.randomize () with {tr.pload .size == 200 ;}); start_item(tr); finish_item(tr); end #100 ; if (starting_phase != null ) starting_phase.drop_objection (this ); endtask
2)pre_do、mid_do和post_do
`uvm_do系列的宏封装了很多命令:
如果`uvm_do中的参数是uvm_sequence_item:
1 2 3 4 5 6 7 8 `uvm_create(item) sequencer.wait_for_grant (prior) (task )this .pre_do (1 ) (task ) item.randomize ()this .mid_do (item) (func) sequencer.send_request (item) (func) sequencer.wait_for_item_done () (task )this .post_do (item) (func)
pre_do是start_item返回前执行的最后一行代码;mid_do是finish_item最开始,post_do是finish_item最后一行代码。
如果`uvm_do中的参数是uvm_sequence:
1 2 3 4 5 6 7 8 `uvm_create(sub_seq) sub_seq.randomize () sub_seq.pre_start () (task )this .pre_do (0 ) (task )this .mid_do (sub_seq) (func) sub_seq.body () (task )this .post_do (sub_seq) (func) sub_seq.post_start () (task )
4. sequence的仲裁机制 4.1 在同一个sequencer上同时启动多个sequence 如果在同一个sequencer上同时启动多个sequence,UVM通过优先级来仲裁先发送哪个transaction或者先启动哪个sequence。
1)transaction的优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 task my_case0::main_phase(uvm_phase phase); sequence0 seq0; sequence1 seq1; seq0 = new ("seq0" ); seq0.starting_phase = phase; seq1 = new ("seq1" ); seq1.starting_phase = phase; env.i_agt .sqr .set_arbitration (SEQ_ARB_STRICT_FIFO); fork seq0.start (env.i_agt .sqr ); seq1.start (env.i_agt .sqr ); join endtask
transaction的优先级在`uvm_pri_do宏中指定:
1 `uvm_do_pri(m_trans, 100 )
除此之外还需要设置sequencer的仲裁算法:
1 function void set_arbitration( SEQ_ARB_TYPE val )
仲裁算法
描述
SEQ_ARB_FIFO
Requests are granted in FIFO order (default)
SEQ_ARB_WEIGHTED
Requests are granted randomly by weight
SEQ_ARB_RANDOM
Requests are granted randomly
SEQ_ARB_STRICT_FIFO
Requests at highest priority granted in fifo order
SEQ_ARB_STRICT_RANDOM
Requests at highest priority granted in randomly
SEQ_ARB_USER
Arbitration is delegated to the user-defined function, user_priority_arbitration. That function will specify the next sequence to grant.
1 env.i_agt .sqr .set_arbitration (SEQ_ARB_STRICT_FIFO);
2)sequence的优先级
sequence也有优先级,在start任务中指定:
1 2 3 4 virtual task start ( uvm_sequencer_base sequencer, uvm_sequence_base parent_sequence = null ,int this_priority = -1 ,bit call_pre_post = 1 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class sequence0 extends uvm_sequence #(my_transaction) ; my_transaction m_trans; function new (string name= "sequence0" ); super .new (name); endfunction virtual task body(); if (starting_phase != null ) starting_phase.raise_objection (this ); repeat (5 ) begin `uvm_do(m_trans) `uvm_info("sequence0" , "send one transaction" , UVM_MEDIUM) end #100 ; if (starting_phase != null ) starting_phase.drop_objection (this ); endtask `uvm_object_utils(sequence0)endclass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 task my_case0::main_phase(uvm_phase phase); sequence0 seq0; sequence1 seq1; seq0 = new ("seq0" ); seq0.starting_phase = phase; seq1 = new ("seq1" ); seq1.starting_phase = phase; env.i_agt .sqr .set_arbitration (SEQ_ARB_STRICT_FIFO); fork seq0.start (env.i_agt .sqr , null , 100 ); seq1.start (env.i_agt .sqr , null , 200 ); join endtask
在start中指定了sequence优先级,就不需要在`uvm_do宏中指定transaction的优先级了。上面的设定中,seq1的优先级高,seq1中的transaction都发送完才会发送seq0中的transaction。
4.2 sequencer的lock 4.3 sequencer的grab 4.4 sequence的有效性 sequencer在仲裁sequence时会查看sequence的有效性。查看Is_relevant函数的返回结果,如果为1,有效,为0,无效。所以可以通过重载is_relevant函数来控制sequence,使它无效。
1 2 3 4 5 6 7 8 9 10 class sequence0 extends uvm_sequence #(my_transaction) ; my_transaction m_trans; int num; bit has_delayed; virtual function bit is_relevant(); if ((num >= 3 )&&(!has_delayed)) return 0 ; else return 1 ; endfunction endclass
当sequencer发现其上启动的所有的sequence都无效的时候会调用wait_for_relevant并等待sequence(查看is_relevant的返回结果)有效。可以重载wait_for_relevant使无效的sequence有效。
如果sequence无效了,那么它需要等待其他有效的sequence都执行完,sequencer才会调用wait_for_relevant,所以sequence的无效是自己控制的,重新有效却受其他sequence控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class sequence0 extends uvm_sequence #(my_transaction) ; my_transaction m_trans; int num; bit has_delayed; virtual function bit is_relevant(); if ((num >= 3 )&&(!has_delayed)) return 0 ; else return 1 ; endfunction virtual task wait_for_relevant(); #10000 ; has_delayed = 1 ; endtask endclass
is_relevant、wait_for_relevant一般成对重载。
5. sequence进阶 5.1 嵌套sequence sequence中不但可以发送transaction,也可以启动不同的sequence。可以启动不同sequence的sequence就是嵌套sequence,被嵌套的sequence和嵌套sequence在同一个sequencer中启动。
嵌套sequence不产生transaction,它只是调度sequence。
被嵌套的sequence中产生的transaction可以被同一个sequencer发送。这是嵌套的前提条件。这些transaction需要虚sequencer支持的transaction类型相同或者派生自这个类型。
1 2 3 4 5 6 7 8 9 10 11 12 class crc_seq extends uvm_sequence#(my_transaction) ; `uvm_object_utils(crc_seq) function new (string name= "crc_seq" ); super .new (name); endfunction virtual task body(); my_transaction tr; `uvm_do_with(tr, {tr.crc_err == 1 ; tr.dmac == 48'h980F ;}) endtask endclass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class case0_sequence extends uvm_sequence #(my_transaction) ; my_transaction m_trans; ... virtual task body(); crc_seq cseq; long_seq lseq; if (starting_phase != null ) starting_phase.raise_objection (this ); repeat (10 ) begin `uvm_do(cseq) `uvm_do(lseq) end #100 ; if (starting_phase != null ) starting_phase.drop_objection (this ); endtask `uvm_object_utils(case0_sequence)endclass
也可以用start函数启动sequence:
其中m_sequencer是启动sequence的sequencer。它是uvm_sequencer_base类型。它是uvm_sequence_item中的私有变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 virtual task body(); crc_seq cseq; long_seq lseq; if (starting_phase != null ) starting_phase.raise_objection (this ); repeat (10 ) begin cseq = new ("cseq" ); cseq.start (m_sequencer); lseq = new ("lseq" ); lseq.start (m_sequencer); end #100 ; if (starting_phase != null ) starting_phase.drop_objection (this ); endtask
5.2 sequence中也可以使用rand变量 `uvm_do宏在启动sequence时也对他进行随机化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class long_seq extends uvm_sequence#(my_transaction) ; rand bit [47 :0 ] ldmac; `uvm_object_utils(long_seq) ... virtual task body(); my_transaction tr; `uvm_do_with(tr, {tr.crc_err == 0 ; tr.pload .size () == 1500 ; tr.dmac == ldmac;}) tr.print (); endtask endclass class case0_sequence extends uvm_sequence #(my_transaction) ; my_transaction m_trans; ... virtual task body(); long_seq lseq; if (starting_phase != null ) starting_phase.raise_objection (this ); repeat (10 ) begin `uvm_do_with(lseq, {lseq.ldmac == 48'hFFFF ;}) end #100 ; if (starting_phase != null ) starting_phase.drop_objection (this ); endtask `uvm_object_utils(case0_sequence)endclass
5.3 transaction的类型匹配 一个sequencer只能产生一种transaction,如果sequence在此sequencer上启动,那么sequence中产生的transaction必须是sequencer支持的类型或者派生自这个类型。
如果想要在sequencer产生完全不同的transaction,可以将sequencer的类型参数定义为uvm_sequence_item。
1 2 class my_sequencer extends uvm_sequencer#(uvm_sequence_item) class my_driver extends uvm_driver#(uvm_sequence_item)
需要在my_driver中用$cast函数将uvm_sequence_item类型转换成my_transaction 类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class my_driver extends uvm_driver#(uvm_sequence_item) ; task main_phase(uvm_phase phase); my_transaction m_tr; your_transaction y_tr; vif.data <= 8'b0 ; vif.valid <= 1'b0 ; while (!vif.rst_n ) @(posedge vif.clk ); while (1 ) begin seq_item_port.get_next_item (req); if ($cast (m_tr, req)) begin drive_my_transaction(m_tr); `uvm_info("driver" , "receive a transaction whose type is my_transaction" , UVM_MEDIUM) end else if ($cast (y_tr, req)) begin drive_your_transaction(y_tr); `uvm_info("driver" , "receive a transaction whose type is your_transaction" , UVM_MEDIUM) end else begin `uvm_error("driver" , "receive a transaction whose type is unknown" ) end seq_item_port.item_done (); end endtask endclass
5.4p_sequencer变量 之前的m_sequencer是uvm_sequencer_base类型,p_sequencer是真正发送sequence的sequencer类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class case0_sequence extends uvm_sequence #(my_transaction) ; my_transaction m_trans; `uvm_object_utils(case0_sequence) `uvm_declare_p_sequencer(my_sequencer) ... virtual task body(); if (starting_phase != null ) starting_phase.raise_objection (this ); repeat (10 ) begin `uvm_do_with(m_trans, {m_trans.dmac == p_sequencer.dmac ; m_trans.smac == p_sequencer.smac ;}) end #100 ; if (starting_phase != null ) starting_phase.drop_objection (this ); endtask endclass
p_sequencer主要用来指定virtual sequencer中的真正的sequencer。