UVM中的sequence | UVM笔记 4

Posted by Kion on January 31, 2020

sequence 基础

sequence机制用于生成激励,提高代码重用性,使得进行新的测试变得更加方便。

sequence的启动主要有以下几种。

/*一 使用start任务*/
my_sequence my_seq;
my_seq = my_sequence::type_id::create("my_seq");
my_seq.start(sequencer);/*指定接收的sequencer*/
/*二 default sequence*/
uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_sequence::type_id::get());/*要指明phase*/
/*三 实例化后再用default sequence*/
my_sequence my_seq;
my_seq = new("my_seq");
uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_seq);

sequence启动后,会自动执行内部的body任务。

sequence的仲裁机制

当sequencer面对多个sequence时,就要运用仲裁机制来决定优先级。

/*
SEQ_ARB_FIFO,先进先出,不考虑优先级 
SEQ_ARB_WIGHTED
SEQ_ARB_RANDOM
SEQ_ARB_STRICT_FIFO,先进先出,考虑优先级
SEQ_ARB_STRICT_RANDOM,随机,考虑优先级
SEQ_ARB_USER
*/
env.i_agt.sqt.set_arbitration(SEQ_ARB_STRICT_FIFO);
/*在sequence中设置优先级别,第二个参数大于-1,越大越高*/
`uvm_do_pri(tr,100)
/*在启动sequence时设置优先级,第二个参数是parent sequence*/
my_seq0.start(env.i_at.sqr,null,200);

可以使用lock以及grab操作控制sequencer的所有权。当一个sequence中出现lock()时,它就会占有sequencer,直到unlock()。同样grab也有这样功能。两者的区别在于,grab的优先级别更高,grab请求会被插入仲裁队列的最前面,而lock则是最后面。

可以使用is_relevant函数控制sequence的有效与否,返回1则有效。当sequencer发现所有sequence都无效时,会启动wait_for_sequence函数,等待某个sequence生效。这两个函数一般成对出现。在is_relevant中设置失效条件,在wait_for_relevant中清除失效条件。

sequence 相关宏及其实现

涉及到sequence的主要宏有:

`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) /*第二个参数指明sequencer指针*/
...
`uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) /*其他宏由此实现*/

/*uvm_do宏其实是进行了下列动作的封装*/
tr = new("tr");
start_item(tr);
assert(tr.randomize());
finish_item(tr);

`uvm_create(tr); /*实例化transaction*/
`uvm_send(tr); /*发送*/
`uvm_rand_send(tr); /*对实例化的transaction进行随机化*/
/*UVM提供三个接口,pre_do,mid_do,post_do,来增加uvm_do的功能*/
/*三者分别是在start_item末尾,finish_item开头与末尾执行*/

sequence 进阶应用

sequence内部可以嵌套sequence:

class case0_sequence extends uvm_sequence # (my_transaction);
    virtual task body();
        crc_seq cseq;
        long_seq lseq;
        repeat (10) begin
            cseq = new("cseq");
            cseq.start(m_sequencer); /*m_sequencer是case0_sequence使用的sequencer对应指针*/
            lseq = new("lseq");
            lseq.start(m_sequencer);
            /*或者*/
            /*`uvm_do(cseq)*/
            /*`uvm_do(lseq)*/
        end
    endtask
endclass

从上可以看出,sequence与transaction之间的界限比较模糊,sequence中也可以有rand修饰的变量,也可以对其随机化,因而uvm_do可以接收sequence作为参数。当uvm_do接收的是transaction时,会自动调用start_item/finish_item,当接收到sequence时,会自动执行其内部的start。

一个sequencer只能接受一种transaction,所以嵌套sequence的前提是,所有的sequence生成的transaction类型相同。可以把sequencer或driver的接收参数改为uvm_sequencer_item以适应多种transaction的接收。不过driver在使用其成员变量时,要先用cast进行转换。

上述代码中的m_sequencer是对应的sequencer指针,但是不能用它来调用成员变量。同样需要cast进行类型转换,或者使用宏uvm_declare_p_sequencer(SEQUENCER)来自动生成真实的sequencer指针(p_sequencer)。

除了嵌套外,sequence也支持派生继承。并且上述的p_sequencer只需在父类定义即可。

virtual sequence 的使用

当面对多个sequencer时,可以用virtual sequence进行统一调度。virtual sequence其实就是用一个包含了多个sequencer的sequencer进行操作。在vritual sequencer内定义指向其他sequencer的指针,然后在test中将已生成的sequencer实例赋值给指针;在virtual sequence中进行调度各个sequencer将会接受的sequence,这一步要用到`uvm_do_on()宏。但是使用了这个宏,就无法使用starting_phase进行提起撤销objection。而使用default_sequence会自动为starting_phase赋值。所以一般在最顶层的virtual sequence进行控制objection。

13si1s.png

/*virtual sequencer*/
class my_vsqr extends uvm_sequencer;
    my_sequencer p_sqr0;
    my_sequencer p_sqr1;
endclass

/*base_test*/
class base_test extends uvm_test;
    my_env env0;
    my_env env1;
    my_vsqr v_sqr;
endclass

function void base_test::build_phase(uvm_phase phase);
    super.build_phase(phase);
    env0 = my_env::type_id::create("env0",this);
    env1 = my_env::type_id::create("env1",this);
    v_sqr = my_vsqr::type_id::create("v_sqr",this);
endfunction

function void base_test::connect_phase(uvm_phase phase);
    v_sqr.p_sqr0 = env0.i_agt.sqr;
    v_sqr.p_sqr1 = env1.i_agt.sqr;
endfunction

/*virtual sequence*/
class case0_vseq extends uvm_sequence;
    `uvm_object_utils(case0_vseq)
    `uvm_declare_p_sequencer(my_vsqr) /*生成指向virtual sequencer的指针*/
    
    virtual task body();
        my_transaction tr;
        drv0_seq seq0;
        drv1_seq seq1;
        /*调配sequencer之前的额外操作,控制sequence之间的差别*/
        `uvm_do_on_with(tr,p_sequencer.p_sqr0,{tr.pload.size == 1500;})
        /*调配sequencer*/
        fork
            `uvm_do_on(seq0,p_sequencer.p_sqr0);/*第二个参数指向对应sequencer*/
            `uvm_do_on(seq1,p_sequencer.p_sqr1);
        join
    endtask
endclass

在sequence中使用fork join_none时要注意,任务结束时,会主动杀死任务内的fork线程,显然会影响到激励的生成。所以在结束前可以用wait fork,等待所有fork线程结束;或者使用fork join。

在sequence中使用config_db

由于config_db::get/set的前两个参数是component的路径,所以为了适配sequence机制,需要用到通配符,或利用get_full_name()。可以用wait_modified检查参数是否被get。

/*在component中使用,通配符一般指sequence的实例名*/
uvm_config_db#(int)::set(this,"env.i_agt.sqr.*","count",9);
/*在sequence中使用*/
uvm_config_db#(int)::get(null,get_full_name(),"count",count);
/*检查是否获取到参数*/
uvm_config_db#(int)::wait_modified(null,get_full_name(),"count");

response 的使用

sequence允许接收response,可以使用get_response任务,而在driver中则需要put_response任务。并且可以多次调用任务。

get_response(rsp);

rsp = new("rsp");
rsp.set_id_info(req);/*需要用该函数告知rsp的去向*/
seq_item_port.put_response(rsp);
seq_item_port.item_done();

由于发送接收response是阻塞的,可以利用reponse handler来分开与transaction的发送工作。

/*在sequence中*/
virtual task pre_body();
    use_response_handler(1);/*启动response handler*/
endtask
virtual function void response_handler(uvm_sequence_item response);
    if (!$cast(rsp,response))/*首先应该转换成my_transaction*/
        `uvm_error("seq","can't cast")
    else begin
        `uvm_info("seq","get one response",UVM_MEDIUM)
        rsp.print();
    end            
endfunction
virtual task body();
    ...

put/get_response以及response handler是新建了一个transaction并返回给sequence。另外,uvm_do执行完毕后,第一个参数指向的是刚刚送给driver的transaction。

sequence library

sequence library 是sequence的集合,本质上也是一个sequence。定义时应该注意:

class simple_sequence_library extends uvm_sequence_library#(my_transaction);/*注明transaction类型*/
    function new(string name="simple_sequence_library");
        super.new(name);
        init_sequence_library(); /*初始化*/
    endfunction
    
    `uvm_object_utils(simple_seq_library)
    `uvm_sequence_library_utils(simple_seq_library);/*注册*/
endclass

`uvm_add_to_seq_lib(seq0,simple_seq_library)/*将seq0注册到sequence library中,可以注册进多个library,也可以多个seq注册到单个library*/

实例化sequence library时,可以用default sequence,就如同实例化普通sequence一样。sequence library执行时,会随机从库中选取sequence,有多种控制选择:

seq_lib.selection_mode = UVM_SEQ_LIB_RANDC;/*随机算法*/
seq_lib.min_random_count = 10;/*执行次数上下限*/
seq_lib.max_random_count = 15;

可以使用类uvm_sequence_library_cfg进行统一配置。