UVM Sequence机制基本使用介绍

目录

1. UVM Sequence基本概念

2. Sequence/Sequencer定义&例化

2.1 定义

2.2 例化

3. Sequence的仲裁机制

3.1 优先级原则

3.2 锁住原则

4. transaction传输数据流


1. UVM Sequence基本概念

UVM Sequence用来生产激励数据,并将激励数据送至driver。Sequence机制包括Sequencer和Sequence,之间的关系为:Sequencer将Sequence产生的激励数据(transaction的形式)送到driver,经driver将transaction喂进DUT。

2. Sequence/Sequencer定义&例化

2.1 定义

1)Sequencer定义:派生自uvm_sequencer(uvm_component),并使用uvm_component_utils宏来注册到factory中。uvm_sequencer是一个参数化的类,参数为my_transaction,即对应Sequence产生的transaction的类型,定义代码如下:

//my_sequencer.sv
class my_sequencer extends uvm_sequencer #(my_transaction);
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    `uvm_component_utils(my_sequencer)
endclass

2)Sequence定义:派生自uvm_sequence(uvm_object),定义时要指定要产生的transaction类型。每一个sequence都有一个body任务,当一个sequence启动之后,会自动执行body中的代码,定义代码如下:

//my_sequence.sv
class my_sequence extends uvm_sequence #(my_transaction);
    my_transaction m_trans;
    function new(string name= "my_sequence");
        super.new(name);
    endfunction
    virtual task body();
        repeat (10) begin
            `uvm_do(m_trans)
        end
        #1000;
    endtask
    `uvm_object_utils(my_sequence)
endclass

上述代码示例用到了一个uvm_do宏,这个宏的主要作用:

        ①创建一个my_transaction的实例m_trans;

        ②产生随机化的transaction;

        ③将产生的transaction送给sequencer;

uvm_do系列的宏主要有:

`uvm_do(SEQ_OR_ITEM)
//该宏有一个参数,为transaction的指针;
//默认优先级为-1;

`uvm_do_pri(SEQ_OR_ITEM, PRIORITY)
//该宏有两个参数,1为transaction的指针,2为优先级;
//优先级参数为大于等于-1的整数,数字越大,优先级越大
//示例:`uvm_do_pri(tr, 20)

`uvm_do_with(SEQ_OR_ITEM, CONSTRAINTS)
//该宏有两个参数,1为transaction的指针,2为约束;
//示例:`uvm_do_with(tr, {tr.pload.size == 100;})

`uvm_do_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
//该宏有三个参数,1为transaction的指针,2为优先级,3为约束;
//示例:`uvm_do_pri_with(tr, 20, {tr.pload.size < 10;})

`uvm_do_on(SEQ_OR_ITEM, SEQR)
//该宏显式地指定使用哪个sequencer发送产生的transaction;
//有两个参数,1是transaction的指针,2是sequencer的指针;
//uvm_do等价于: `uvm_do_on(tr, this.m_sequencer);

`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_do宏,uvm_create宏与uvm_send宏也可以用来产生transaction,示例如下:

//my_sequence.sv
class my_sequence extends uvm_sequence #(my_transaction);
    my_transaction m_trans;
    ...
    virtual task body();
        repeat (10) begin
            `uvm_create(m_trans)  
            //用来实例化m_trans, 也可直接调用new进行实例化,如:m_trans = new("m_trans");
            assert(m_trans.randomize());
            `uvm_send(m_trans)
            //将m_trans送到对应的sequencer;
        end
    endtask
endclass

uvm_send系列宏有:

`uvm_send_pri(SEQ_OR_ITEM, PRIORITY)
//该宏有两个参数,1为transaction的指针,2为优先级;
//优先级参数类似于uvm_do宏的优先级,为大于等于-1的整数; 数越大,优先级越高;
//示例:`uvm_send_pri(m_trans, 200)

`uvm_rand_send(SEQ_OR_ITEM)
//该宏会自动对transaction进行随机化;

`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_do和uvm_creat宏,start_item与finish_item也可以产生transaction,示例如下:

//my_sequence.sv
class my_sequence extends uvm_sequence #(my_transaction);
    my_transaction m_trans;
    ...
    virtual task body();
        repeat (10) begin
            tr = new("m_trans");
            //start_item在使用前必须对transaction进行实例化
            assert(tr.randomize() with {tr.pload.size == 200;});
            start_item(tr);
            finish_item(tr);
        end
    endtask
endclass

2.2 例化

1)Sequencer和driver同级,直接在agent的build_phase里进行例化,代码如下:

//my_agent.sv
class my_agent extends uvm_agent ;
    my_sequencer sqr;
    my_driver drv;
    my_monitor mon;
    ...
endclass

function void my_agent::build_phase(uvm_phase phase);
    super.build_phase(phase);
    sqr = my_sequencer::type_id::create("sqr", this);
    drv = my_driver::type_id::create("drv", this);
    mon = my_monitor::type_id::create("mon", this);
endfunction

sequencer在例化好以后,需要在agent中对driver做一个连接,只有连接成功后sequencer才知道将transaction传送给哪个driver。在uvm_driver中有成员变量seq_item_port,而在uvm_sequencer中有成员变量seq_item_export,这两者之间可以建立一个“通道”,通道中传递的transaction类型就是定义my_sequencer和my_driver时指定的transaction类型,连接代码如下:

//my_agent.sv
function void my_agent::connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    drv.seq_item_port.connect(sqr.seq_item_export);
endfunction

当把二者连接好之后,就可以在driver中通过get_next_item任务向sequencer申请新的transaction:

//my_driver.sv
task my_driver::main_phase(uvm_phase phase);
    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);
        drive_one_pkt(req);
        seq_item_port.item_done();
    end
endtask

通过get_next_item任务来得到一个新的req,并且驱动它,驱动完成后调用item_done通知sequencer,driver已经得到了这个transaction,接着sequencer就会把这个transaction删除;

换句话说,当sequence受到item_done信号后,uvm_do宏才算是执行完毕,返回后开始执行下一个uvm_do,并产生新的transaction。 

2)sequence在某一个uvm_component(如sequencer,env等)的main_phase例化就可以;示例代码如下:

//my_env.sv
task my_env::main_phase(uvm_phase phase);
    my_sequence seq;
    phase.raise_objection(this);
    seq = my_sequence::type_id::create("seq");
    seq.start(i_agt.sqr);
    //sequence在例化后要进行启动, 并通过start函数指定要传送的sequencer的指针;
    //如果要在sequencer中例化,启动代码应改为:seq.start(this);
    phase.drop_objection(this);
endtask

除了在main_phase中手工启动sequence,还可通过default_sequence的方式进行启动,只需要在某个uvm_component的build_phase设置如下代码:

//my_env.sv
virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    uvm_config_db#(uvm_object_wrapper)::set(this,"i_agt.sqr.main_phase",
                                        "default_sequence",
                                        my_sequence::type_id::get());
endfunction

除了在env的build_phase中设置default_sequence外,还可以在top_tb进行设置:

module top_tb;
    initial begin
        uvm_config_db#(uvm_object_wrapper)::set(null,"uvm_test_top.i_agt.sqr.main_phase",
                                          "default_sequence",my_sequence::type_id::get());
    end
endmodule

3. Sequence的仲裁机制

一个sequencer支持启动多个sequence,这样就会遇到一个问题,如果有多个sequence的transaction过来,sequencer根据什么原则去做选择?UVM采用了仲裁机制来解决,主要有优先级原则和锁住原则。

3.1 优先级原则

Sequencer根据transaction的优先级高低来进行选择,优先级仲裁算法主要有:

SEQ_ARB_FIFO
//默认优先级算法,严格遵循先入先出的顺序,不考虑transaction的优先级;

SEQ_ARB_WEIGHTED
//按照优先级进行随机选择

SEQ_ARB_RANDOM
//无视优先级,完全随机选择

SEQ_ARB_STRICT_FIFO
//严格按照优先级进行选择,如有同一优先级,遵循先入先出的原则

SEQ_ARB_STRICT_RANDOM
//严格按照优先级进行选择,如有同一优先级,随机从最高优先级进行选择

SEQ_ARB_USER
//用户自定义优先级算法

//可以在tc的main_phase设置使用哪个优先级算法,示例如下:
//my_case0.sv
task my_case0::main_phase(uvm_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

3.2 锁住原则

即sequence向sequencer发送一个请求,要求锁住该sequencer的使用权,直到sequencer收到解锁信号,才开始根据仲裁算法处理别的sequence的transaction,主要有lockgrab操作。

1)lock操作:将来自某个sequence的锁住信号放入仲裁队列的最后,当sequencer处理完所有transaction后,锁住sequencer,直到收到解锁信号。如果有多个锁住信号,则根据先入先出原则,先执行最早发出锁住信号的transaction,示例代码如下:

//my_sequence1.sv
class my_sequence1 extends uvm_sequence #(my_transaction);
    ...
    virtual task body();
        repeat (3) begin
            `uvm_do_with(m_trans, {m_trans.pload.size < 500;})
        end
        lock();//锁住信号
        repeat (4) begin
            `uvm_do_with(m_trans, {m_trans.pload.size < 500;})
        end
        unlock();//解锁信号
     endtask
    ...
endclass

2)grab操作:将来自某个sequence的锁住信号放入仲裁队列的最前,一开始就锁住sequencer的使用权,示例代码如下:

//my_sequence1.sv
class my_sequence1 extends uvm_sequence #(my_transaction);
    ...
    virtual task body();
        repeat (3) begin
            `uvm_do_with(m_trans, {m_trans.pload.size < 500;})
        end
        grab();//锁住信号
        repeat (4) begin
            `uvm_do_with(m_trans, {m_trans.pload.size < 500;})
        end
        ungrab();//解锁信号
     endtask
    ...
endclass

如果一个sequence在使用grab操作获取sequencer的使用权前,另外一个sequence已经使用lock操作获得了sequencer的使用权,则grab操作会一直等待lock的释放。

4. transaction传输数据流

一个sequence在向sequencer发送transaction前,要先向sequencer发送一个请求,sequencer把这个请求放在一个仲裁队列中。作为sequencer,它需做两件事情:

1:检测仲裁队列里是否有某个sequence发送transaction的请求;

2:检测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。

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值