日常记录(40)UVM框架构建要点

https://gitee.com/bai-mengwei/uvm_practive_first

顶层连接与通信

顶层,是top_tb.sv,包括了interface、dut、和testbatch,驱动时钟、复位。

并包含uvm的宏文件svh、导入uvm的包。以及包含其它各个模块的文件。

引入uvm

`include "uvm_macros.svh"
import uvm_pkg::*;

interface

my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);

连接DUT

dut my_dut(.clk(clk),
    .rst_n(rst_n),
    .rxd(input_if.data),
    .rx_dv(input_if.valid),
    .txd(output_if.data),
    .tx_en(output_if.valid));

启动testbench与连接testbench

initial begin
    /* run_test("my_env"); */
    /* run_test("base_test"); */
    /* run_test("my_case0"); */
    run_test();
    /* my_driver drv; */
    /* drv=new("drv", null); */
    /* drv.main_phase(null); */
    /* $finish; */
end

initial begin
    uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.drv", "vif", input_if);
    uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.mon", "vif", input_if);
    uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.o_agt.mon", "vif", output_if);
end

  

启动一个test后

以base_test为例,可以使用default_sequence,自动完成sequence的启动过程。

report_phase为后续的处理过程

function void base_test::build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = my_env::type_id::create("env", this);
    uvm_config_db#(uvm_object_wrapper)::set( 
       this, "env.i_agt.sqr.main_phase","default_sequence", my_sequence::type_id::get()); 
endfunction: build_phase

function void base_test::report_phase(uvm_phase phase);
    uvm_report_server server;
    int err_num;

    server = get_report_server();
    err_num = server.get_severity_count(UVM_ERROR);
    if(err_num!=0) begin
        $display("TEST CASE FAILED");
    end else begin
        $display("TEST CASE PASSED");
    end
endfunction: report_phase

  

不使用而手动的启动(在env中)

手动指明了seq给一个i_agt.sqr发送trans

    task main_phase(uvm_phase phase);
         my_sequence seq; 
         phase.raise_objection(this); 
         seq = my_sequence::type_id::create("seq"); 
         seq.start(i_agt.sqr); 
         phase.drop_objection(this); 
    endtask

  

在env中,

执行了config_db中的树形访问对象创建、包括agt、mdl、scb。

其中的fifo,为agt->mdl->scb与agt->scb,进行了通信的搭建。涉及的ap与port,需要在agt、mdl、scb中定义。

其中agt的ap设置为mon的ap(引用)。

default_sequence

定义过程中指明了sqr。

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        i_agt=my_agent::type_id::create("i_agt", this);
        o_agt=my_agent::type_id::create("o_agt", this);
        i_agt.is_active = UVM_ACTIVE;
        o_agt.is_active = UVM_PASSIVE;
        mdl=my_model::type_id::create("mdl", this);
        scb=my_scoreboard::type_id::create("scb", this);
        agt_mdl_fifo = new("agt_mdl_fifo", this);
        agt_scb_fifo = new("agt_scb_fifo", this);
        mdl_scb_fifo = new("mdl_scb_fifo", this);

        uvm_config_db#(uvm_object_wrapper)::set(
            this, "i_agt.sqr.main_phase", "default_sequence", my_sequence::type_id::get());
    endfunction: build_phase

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        i_agt.ap.connect(agt_mdl_fifo.analysis_export);
        mdl.port.connect(agt_mdl_fifo.blocking_get_export);
        mdl.ap.connect(mdl_scb_fifo.analysis_export);
        scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);
        o_agt.ap.connect(agt_scb_fifo.analysis_export);
        scb.act_port.connect(agt_scb_fifo.blocking_get_export);
    endfunction: connect_phase

  

Agent中的内容

有ap,用于发送信息,然后是drv、mon、sqr。

drv用于驱动dut,mon用于实现数据发送,sqr用于装载seq,seq用于生成trans。

drv和sqr只在前向送dut数据才有。

connect过程中,seq_item_port和seq_item_export数据内置变量。drv向sqr发起申请数据请求,sqr从seq给予数据。

class my_agent extends uvm_agent;
    // data or class properties
    `uvm_component_utils(my_agent)

    my_driver drv;
    my_monitor mon;
    my_sequencer sqr;

    uvm_analysis_port#(my_transation) ap;
    function new(string name="my_agent", uvm_component parent);
        super.new(name, parent);
    endfunction : new

    extern virtual function void build_phase(uvm_phase phase);
    extern virtual function void connect_phase(uvm_phase phase);

endclass : my_agent

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

function void my_agent::connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    if(is_active==UVM_ACTIVE) begin
        drv.seq_item_port.connect(sqr.seq_item_export);
    end
    ap = mon.ap;
endfunction

  

Driver的内容

driver构建链接关系

https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_driver.sv#L12

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        `uvm_info("my_driver", "build_phase is called", UVM_LOW);
        if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif)) begin
            `uvm_fatal("my_driver", "interface set error!");
        end
    endfunction: build_phase

driver从sqr获取数据

https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_driver.sv#L52

seq_item_port.try_next_item

    task main_phase(uvm_phase phase);
        vif.data <= 8'b0;
        vif.valid <= 1'b0;
        while(!vif.rst_n) begin
            @(posedge vif.clk);
        end
        while (1) begin
            /* seq_item_port.get_next_item(req); */
            seq_item_port.try_next_item(req);
            if (req==null) begin
                @(posedge vif.clk);
            end else begin
                drive_one_pkt(req);
                seq_item_port.item_done();
            end
        end

    endtask

driver驱动一个数据包

https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_driver.sv#L71

    task drive_one_pkt(my_transation tr); 
        byte unsigned data_q[];
        int data_size;

        data_size = tr.pack_bytes(data_q) / 8;
        `uvm_info("drive_one_pkt", "start drive one packet", UVM_LOW);
        repeat (3) begin
            @(posedge vif.clk);
        end
        for (int i = 0; i < data_size; i++) begin
            @(posedge vif.clk);
            vif.valid<=1'b1;
            vif.data<= data_q[i];
        end

        @(posedge vif.clk);
        vif.valid<=1'b0;
        `uvm_info("drive_one_pkt", "one packet send over", UVM_LOW);

    endtask

  

Sequence

uvm_do:创建实例my_tr,将其随机化,送给sqr。然后drv从sqr获取数据,进行驱动。

class my_sequence extends uvm_sequence#(my_transation); 
    // data or class properties
    `uvm_object_utils(my_sequence)
    my_transation my_tr;
    

    // initialization
    function new(string name="my_sequence");
        super.new(name);
    endfunction : new

    virtual task body();
        if(starting_phase!=null) begin
            starting_phase.raise_objection(this);
        end
        repeat (10) begin
            `uvm_do(my_tr)
        end
        #1000;
        if (starting_phase!=null) begin
            starting_phase.drop_objection(this);
        end
    endtask

endclass : my_sequence

  

model

参考模型。给予输入数据,得到作为参考的正确输出数据,将其发送给scb

https://gitee.com/bai-mengwei/uvm_practive_first/blob/master/my_model.sv#L25

class my_model extends uvm_component;
    // data or class properties
    `uvm_component_utils(my_model)

    uvm_blocking_get_port #(my_transation) port;
    uvm_analysis_port #(my_transation) ap;
    // initialization
    function new(string name="my_model", uvm_component parent);
        super.new(name, parent);
    endfunction : new

    extern function void build_phase(uvm_phase phase);
    extern task main_phase(uvm_phase phase);

endclass : my_model

function void my_model::build_phase(uvm_phase phase);
    super.build_phase(phase);
    port = new("port", this);
    ap = new("ap", this);
endfunction

task my_model::main_phase(uvm_phase phase);
    my_transation tr;
    my_transation new_tr;
    super.main_phase(phase);
    while (1) begin
        port.get(tr);
        new_tr = new("new_tr");
        new_tr.copy(tr);
        `uvm_info("main_phase", "tr copy finished.", UVM_LOW)
        new_tr.print();
        ap.write(new_tr);
    end
endtask

  

Scoreboard

获取实际输出与期望输出,进行对比。

由于实际输出消耗仿真时间,期望输出不需要,因此常,if成立。

class my_scoreboard extends uvm_scoreboard;
    // data or class properties
    `uvm_component_utils(my_scoreboard)

    my_transation expect_queue[$];
    uvm_blocking_get_port #(my_transation) exp_port;
    uvm_blocking_get_port #(my_transation) act_port;

    // initialization
    function new(string name="my_scoreboard", uvm_component parent);
        super.new(name, parent);
    endfunction : new

    extern function void build_phase(uvm_phase phase);
    extern task main_phase(uvm_phase phase);

endclass : my_scoreboard

function void my_scoreboard::build_phase(uvm_phase phase);
    super.build_phase(phase);
    exp_port = new("exp_port", this);
    act_port = new("act_port", this);
endfunction

task my_scoreboard::main_phase(uvm_phase phase);
    my_transation get_exp, get_act, tmp_trans;
    bit result;

    super.main_phase(phase);
    fork
        while (1) begin
            exp_port.get(get_exp);
            expect_queue.push_back(get_exp);
        end

        while (1) begin
            act_port.get(get_act);
            
            if (expect_queue.size>0) begin
                tmp_trans=expect_queue.pop_front;
                result=get_act.compare(tmp_trans);
                if(result) begin
                    `uvm_info("main_phase", "compare success", UVM_LOW)
                end else begin
                    `uvm_info("main_phase", "compare failed!", UVM_LOW)
                    $display("actual is:");
                    get_act.my_print();
                    $display("expect is:");
                    tmp_trans.my_print();
                end
            end else begin
                `uvm_error("main_phase", "expect queue is empty!")
                $display("the unexpect pkt is");
                get_act.my_print();
            end
        end
    join
endtask

  

Transaction

注册factory时,采用begin。。。end,其中内容用field,可以更好的使用print、compare、pack_bytes、unpack_bytes函数。

    `uvm_object_utils_begin(my_transation)
        `uvm_field_int(dmac, UVM_ALL_ON)
        `uvm_field_int(smac, UVM_ALL_ON)
        `uvm_field_int(ether_type, UVM_ALL_ON)
        `uvm_field_array_int(pload, UVM_ALL_ON)
        `uvm_field_int(crc, UVM_ALL_ON)
    `uvm_object_utils_end

  

 

posted @ 2022-01-16 21:40  大浪淘沙、  阅读(408)  评论(0)    收藏  举报