• 《UVM实战》代码示例


    首先是top_tb:

      

    `timescale 1ns/1ps
    `include "uvm_macros.svh"

    import uvm_pkg::*;
    `include "my_if.sv"
    `include "my_transaction.sv"
    `include "my_sequencer.sv"
    `include "my_driver.sv"
    `include "my_monitor.sv"
    `include "my_agent.sv"
    `include "my_model.sv"
    `include "my_scoreboard.sv"
    `include "my_env.sv"
    `include "base_test.sv"
    `include "my_case0.sv"
    `include "my_case1.sv"

    module top_tb;

    reg clk;
    reg rst_n;
    reg[7:0] rxd;
    reg rx_dv;
    wire[7:0] txd;
    wire tx_en;

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

    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));

    initial begin
    clk = 0;
    forever begin
    #100 clk = ~clk;
    end
    end

    initial begin
    rst_n = 1'b0;
    #1000;
    rst_n = 1'b1;
    end

    initial begin
    run_test();
    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

    endmodule

    主要实例化了DUT,和输入输出inf,

    定义了时钟频率,传递了接口以链接TB,

    和一个run_test()。

    然后是base_test:

    `ifndef BASE_TEST__SV
    `define BASE_TEST__SV

    class base_test extends uvm_test;

    my_env env;

    function new(string name = "base_test", uvm_component parent = null);
    super.new(name,parent);
    endfunction

    extern virtual function void build_phase(uvm_phase phase);
    extern virtual function void report_phase(uvm_phase phase);
    `uvm_component_utils(base_test)
    endclass


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

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

    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

    `endif

    就算是build_phase中例化一个env,以及report_phase中进行get_severity_count(UVM_ERROR),

    汇报测试结果

    然后是env:

    `ifndef MY_ENV__SV
    `define MY_ENV__SV

    class my_env extends uvm_env;

    my_agent i_agt;
    my_agent o_agt;
    my_model mdl;
    my_scoreboard scb;

    uvm_tlm_analysis_fifo #(my_transaction) agt_scb_fifo;
    uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
    uvm_tlm_analysis_fifo #(my_transaction) mdl_scb_fifo;

    function new(string name = "my_env", uvm_component parent);
    super.new(name, parent);
    endfunction

    virtual 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_scb_fifo = new("agt_scb_fifo", this);
    agt_mdl_fifo = new("agt_mdl_fifo", this);
    mdl_scb_fifo = new("mdl_scb_fifo", this);

    endfunction

    extern virtual function void connect_phase(uvm_phase phase);

    `uvm_component_utils(my_env)
    endclass

    function void my_env::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

    `endif

    主要是哥哥模块,和三个tlm_analysis_fifo,

    然后build_phase中type::create模块,配置i/o_agent,new三个fifo,

    之后connect_phase中.connect analysis_export和blocking_get_export。

    要传送的transcation:

    `ifndef MY_TRANSACTION__SV
    `define MY_TRANSACTION__SV

    class my_transaction extends uvm_sequence_item;

    rand bit[47:0] dmac;
    rand bit[47:0] smac;
    rand bit[15:0] ether_type;
    rand byte pload[];
    rand bit[31:0] crc;

    constraint pload_cons{
    pload.size >= 46;
    pload.size <= 1500;
    }

    function bit[31:0] calc_crc();
    return 32'h0;
    endfunction

    function void post_randomize();
    crc = calc_crc;
    endfunction

    `uvm_object_utils_begin(my_transaction)
    `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

    function new(string name = "my_transaction");
    super.new();
    endfunction

    endclass
    `endif

    主要各种内容及uvm_object_utils,uvm_field_int注册,

    约数参数,和在post_randomize()中处理内容。

    发送transaction的sequencer:

    `ifndef MY_SEQUENCER__SV
    `define 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

    `endif

    很简单,就是一个注册和new()

    比较复杂的Driver:

    `ifndef MY_DRIVER__SV
    `define MY_DRIVER__SV
    class my_driver extends uvm_driver#(my_transaction);

    virtual my_if vif;

    `uvm_component_utils(my_driver)
    function new(string name = "my_driver", uvm_component parent = null);
    super.new(name, parent);
    endfunction

    virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
    `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
    endfunction

    extern task main_phase(uvm_phase phase);
    extern task drive_one_pkt(my_transaction tr);
    endclass

    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

    task my_driver::drive_one_pkt(my_transaction tr);
    byte unsigned data_q[];
    int data_size;

    data_size = tr.pack_bytes(data_q) / 8;
    `uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
    repeat(3) @(posedge vif.clk);
    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("my_driver", "end drive one pkt", UVM_LOW);
    endtask


    `endif

    首先是virtual inf和build_phase中接受接口,否则fatal,

    然后main_phase中while(1)循环的get_next_item(req),调用发送函数发送req,返回item_done()。

    而在monitor中:

    `ifndef MY_MONITOR__SV
    `define MY_MONITOR__SV
    class my_monitor extends uvm_monitor;

    virtual my_if vif;

    uvm_analysis_port #(my_transaction) ap;

    `uvm_component_utils(my_monitor)
    function new(string name = "my_monitor", uvm_component parent = null);
    super.new(name, parent);
    endfunction

    virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
    `uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")
    ap = new("ap", this);
    endfunction

    extern task main_phase(uvm_phase phase);
    extern task collect_one_pkt(my_transaction tr);
    endclass

    task my_monitor::main_phase(uvm_phase phase);
    my_transaction tr;
    while(1) begin
    tr = new("tr");
    collect_one_pkt(tr);
    ap.write(tr);
    end
    endtask

    task my_monitor::collect_one_pkt(my_transaction tr);
    byte unsigned data_q[$];
    byte unsigned data_array[];
    logic [7:0] data;
    logic valid = 0;
    int data_size;

    while(1) begin
    @(posedge vif.clk);
    if(vif.valid) break;
    end

    `uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);
    while(vif.valid) begin
    data_q.push_back(vif.data);
    @(posedge vif.clk);
    end
    data_size = data_q.size();
    data_array = new[data_size];
    for ( int i = 0; i < data_size; i++ ) begin
    data_array[i] = data_q[i];
    end
    tr.pload = new[data_size - 18]; //da sa, e_type, crc
    data_size = tr.unpack_bytes(data_array) / 8;
    `uvm_info("my_monitor", "end collect one pkt", UVM_LOW);
    endtask


    `endif

    主要是virtual inf和analysis_port,在build_phase中接受接口,

    mian_phase中while(1)的调用接受函数和和ap.write(tr)。

    在agent中:

    `ifndef MY_AGENT__SV
    `define MY_AGENT__SV

    class my_agent extends uvm_agent ;
    my_sequencer sqr;
    my_driver drv;
    my_monitor mon;

    uvm_analysis_port #(my_transaction) ap;

    function new(string name, uvm_component parent);
    super.new(name, parent);
    endfunction

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

    `uvm_component_utils(my_agent)
    endclass


    function void my_agent::build_phase(uvm_phase phase);
    super.build_phase(phase);
    if (is_active == UVM_ACTIVE) begin
    sqr = my_sequencer::type_id::create("sqr", this);
    drv = my_driver::type_id::create("drv", 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

    `endif

    含有driver和monitor,在build_phase中如果UVM_ACTIVE,就例化drv和sqr,但都例化mont,

    在build_phase中如果UVM_ACTIVE就链接drv和sqr的接口。

    而interface比较简单,就是接口就好:

    `ifndef MY_IF__SV
    `define MY_IF__SV

    interface my_if(input clk, input rst_n);

    logic [7:0] data;
    logic valid;
    endinterface

    `endif

    而reference_model中:

    `ifndef MY_MODEL__SV
    `define MY_MODEL__SV

    class my_model extends uvm_component;

    uvm_blocking_get_port #(my_transaction) port;
    uvm_analysis_port #(my_transaction) ap;

    extern function new(string name, uvm_component parent);
    extern function void build_phase(uvm_phase phase);
    extern virtual task main_phase(uvm_phase phase);

    `uvm_component_utils(my_model)
    endclass

    function my_model::new(string name, uvm_component parent);
    super.new(name, parent);
    endfunction

    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_transaction tr;
    my_transaction new_tr;
    super.main_phase(phase);
    while(1) begin
    port.get(tr);
    new_tr = new("new_tr");
    new_tr.copy(tr);
    `uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
    new_tr.print();
    ap.write(new_tr);
    end
    endtask
    `endif

    需要进的blocking_get_port和出的analysis_port两个,在build_phase中连接他们,

    在main_phase中则while1的,如藕藕get_port.get()收到,就ana_port.write()这个tr,

    之后的scroreboard中:

    `ifndef MY_SCOREBOARD__SV
    `define MY_SCOREBOARD__SV
    class my_scoreboard extends uvm_scoreboard;
    my_transaction expect_queue[$];
    uvm_blocking_get_port #(my_transaction) exp_port;
    uvm_blocking_get_port #(my_transaction) act_port;
    `uvm_component_utils(my_scoreboard)

    extern function new(string name, uvm_component parent = null);
    extern virtual function void build_phase(uvm_phase phase);
    extern virtual task main_phase(uvm_phase phase);
    endclass

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

    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_transaction get_expect, get_actual, tmp_tran;
    bit result;

    super.main_phase(phase);
    fork
    while (1) begin
    exp_port.get(get_expect);
    expect_queue.push_back(get_expect);
    end
    while (1) begin
    act_port.get(get_actual);
    if(expect_queue.size() > 0) begin
    tmp_tran = expect_queue.pop_front();
    result = get_actual.compare(tmp_tran);
    if(result) begin
    `uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);
    end
    else begin
    `uvm_error("my_scoreboard", "Compare FAILED");
    $display("the expect pkt is");
    tmp_tran.print();
    $display("the actual pkt is");
    get_actual.print();
    end
    end
    else begin
    `uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty");
    $display("the unexpected pkt is");
    get_actual.print();
    end
    end
    join
    endtask
    `endif

    是两个get_port,在build_phase中new它们,

    首先对于exp端,while(1)的,只要get一个tr,就要在tr_queue中push进去一个,

    同时对于act端,while(1)的。只要get一个tr,就要从tr_queue中pop一个出来,进行比较。

    这个两个是fork join的,互补影响。

    case0中:

    `ifndef MY_CASE0__SV
    `define MY_CASE0__SV
    class case0_sequence extends uvm_sequence #(my_transaction);
    my_transaction m_trans;

    function new(string name= "case0_sequence");
    super.new(name);
    endfunction

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

    `uvm_object_utils(case0_sequence)
    endclass


    class my_case0 extends base_test;

    function new(string name = "my_case0", uvm_component parent = null);
    super.new(name,parent);
    endfunction
    extern virtual function void build_phase(uvm_phase phase);
    `uvm_component_utils(my_case0)
    endclass


    function void my_case0::build_phase(uvm_phase phase);
    super.build_phase(phase);

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

    `endif

    设定了sequece,task_body中uvm_do(my_trans),在之前和之后要starting_phase.raise_objection(this)和starting_phase.drop_objection(this);

    在case0的build_phase中传递sequence。

    case1也类似的:

    `ifndef MY_CASE1__SV
    `define MY_CASE1__SV
    class case1_sequence extends uvm_sequence #(my_transaction);
    my_transaction m_trans;

    function new(string name= "case1_sequence");
    super.new(name);
    endfunction

    virtual task body();
    if(starting_phase != null)
    starting_phase.raise_objection(this);
    repeat (10) begin
    `uvm_do_with(m_trans, { m_trans.pload.size() == 60;})
    end
    #100;
    if(starting_phase != null)
    starting_phase.drop_objection(this);
    endtask

    `uvm_object_utils(case1_sequence)
    endclass

    class my_case1 extends base_test;

    function new(string name = "my_case1", uvm_component parent = null);
    super.new(name,parent);
    endfunction

    extern virtual function void build_phase(uvm_phase phase);
    `uvm_component_utils(my_case1)
    endclass


    function void my_case1::build_phase(uvm_phase phase);
    super.build_phase(phase);

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

    `endif

  • 相关阅读:
    云端开发,云端部署
    Chrome下的Page Speed使用
    Linux的到来
    NoSQL 之 Morphia 操作 MongoDB
    qTip2
    在.NET下使用Task Parallel Library提高程序性能
    WCF REST系列文章汇总
    Google工具pagespeed使用教程
    从零开始系统深入学习android
    如何优化一个网站使之提高访问速度
  • 原文地址:https://www.cnblogs.com/liheng369/p/8536861.html
Copyright © 2020-2023  润新知