zoukankan      html  css  js  c++  java
  • 《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

  • 相关阅读:
    Treap 树堆 容易实现的平衡树
    (转)Maven实战(二)构建简单Maven项目
    (转)Maven实战(一)安装与配置
    根据请求头跳转判断Android&iOS
    (转)苹果消息推送服务器 php 证书生成
    (转)How to renew your Apple Push Notification Push SSL Certificate
    (转)How to build an Apple Push Notification provider server (tutorial)
    (转)pem, cer, p12 and the pains of iOS Push Notifications encryption
    (转)Apple Push Notification Services in iOS 6 Tutorial: Part 2/2
    (转)Apple Push Notification Services in iOS 6 Tutorial: Part 1/2
  • 原文地址:https://www.cnblogs.com/liheng369/p/8536861.html
Copyright © 2011-2022 走看看