UVM简单验证平台从0到1

前序

本篇文章主要参考《UVM实战:第二章》,虽说只是搭建了一个简单的验证平台,但从书本的零星知识到makefile成功并且dump到正确的波形,还是费了周折,有一定的参考意义。现将过程与学习IC验证的同学分享,也是防止自己日后遗忘。

验证平台拓扑结构(case调用report_phase中的uvm_top.print_topology()函数来打印整个验证平台的拓扑结构):
在这里插入图片描述
笔者的文件目录:
在这里插入图片描述

DUT

wrapper.v

DUT很简单,就是将接收到的数据在下一拍输出,输入输出都由data和valid构成。

module wrapper(
    input clk,
    input rst_n,
    input [7 : 0] rxd,
    input rx_dv,
    output reg [7 : 0] txd,
    output reg tx_en
);

always@(posedge clk or negedge rst_n)begin
    if(!rst_n) begin
        txd <= 8'd0;
        tx_en <= 1'b0;
    end
    else begin
        txd <= rxd;
        tx_en <= rx_dv;
    end
end
endmodule

UVM验证平台(自上而下的顺序,主要组件实现的功能)

case0.sv:

1. 首先定义了此case对应的sequence,在sequence中产生transaction,如何产生取决于sequence中的body()任务,此case中的sequence用`uvm_do系列宏产生了10个transaction,关于uvm_do系列宏,参考笔者之前的一片博文,应该会有一些了解。
2. 并用starting_phase指针控制sequencer.main_phase平台的raise/drop_objection:参考书中原话,在uvm_sequence这个基类中,有一个变量名为starting_phase,它的类型是uvm_phase,sequencer在启动default_sequence时,将seq.starting_phase指向了sequencer的main_phase。
3. 在build_phase中实例化一个env,并通过uvm_config_db把sequence寄信给sqr.main_phase。
4. 打印此case的拓扑结构以及最终的比较结果。

class case0_sequence extends uvm_sequence#(transaction_dut);
    transaction_dut 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_with(m_trans, {m_trans.pload.size() == 10;})
        end
        #100;
        if(starting_phase != null) begin
            starting_phase.drop_objection(this);
        end
    endtask
    `uvm_object_utils(case0_sequence)
endclass

class case0 extends uvm_test;
    my_env env;

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

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        `uvm_info("case0", "case0 build_phase", UVM_LOW)
        env = my_env::type_id::create("env", this);
        
        //use default_sequence
        uvm_config_db#(uvm_object_wrapper)::set(this, "env.in_agt.sqr.main_phase", "default_sequence", case0_sequence::type_id::get());
    endfunction
    
    extern virtual function void report_phase(uvm_phase phase);
    `uvm_component_utils(case0)
endclass

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

        uvm_top.print_topology();
        server = get_report_server();
        err_num = server.get_severity_count(UVM_ERROR);

        if(err_num != 0) begin
            $display("----------------------");
            $display("-----   TEST CASE FAILED   ----");
            $display("----------------------");
        end
        else begin
            $display("----------------------");
            $display("-----   TEST CASE PASSED   ----");
            $display("----------------------");
        end
endfunction

env.sv

1. build_phase中创建四个组件,以及连接他们的三个fifo,以及agent的参数。
2. connect_phase中将他们连接起来。

class my_env extends uvm_env;
    
    my_agent in_agt;
    my_agent out_agt;
    my_model mdl;
    my_scoreboard scb;

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

    extern function new(string name = "my_env", uvm_component parent = null);

    extern virtual function void build_phase(uvm_phase phase);

    extern virtual function void connect_phase(uvm_phase phase);

    `uvm_component_utils(my_env)
endclass

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

function void my_env::build_phase(uvm_phase phase);
    super.build_phase(phase);
    in_agt = my_agent::type_id::create("in_agt", this);
    out_agt = my_agent::type_id::create("out_agt", this);
    in_agt.is_active = UVM_ACTIVE;
    out_agt.is_active = UVM_PASSIVE;
    mdl = my_model::type_id::create("mld", 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

function void my_env::connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    
    in_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);
    
    out_agt.ap.connect(agt_scb_fifo.analysis_export);
    scb.act_port.connect(agt_scb_fifo.blocking_get_export);
endfunction

agent.sv

1. build_phase中判断参数创建组件,以及连接fifo的port。
2. connect_phase中将指针指向monitor的port,实质上是monitor 向fifo写数据;再将driver和sequencer的port连接

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

    uvm_analysis_port #(transaction_dut) ap;
    
    extern function new (string name, uvm_component parent);

    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 my_agent::new(string name, uvm_component parent);
    super.new(name, parent);
endfunction 

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 = driver_dut::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 

model.sv

1. build中实例化连接fifo的两侧端口
2. main_phase中将接收到的数据复制发送

class my_model extends uvm_component;
    uvm_blocking_get_port #(transaction_dut) port;
    uvm_analysis_port #(transaction_dut) ap;

    extern function new(string name = "my_model", uvm_component parent = null);
    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 = "my_model", uvm_component parent = null);
    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);
    transaction_dut tr;
    transaction_dut 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

scoreboard.sv

1. build_phase中创建连接两个fifo的接口
2. main_phase中通过队列缓存来自in_agent的数据,得到来自DUT的数据后,比较后打印结果(缓存来自in_agent的数据是因为来自DUT的数据有时钟的概念,迟于来自in_agent的数据)

class my_scoreboard extends uvm_scoreboard;
    transaction_dut expect_queue[$];
    uvm_blocking_get_port #(transaction_dut) exp_port;
    uvm_blocking_get_port #(transaction_dut) act_port;
    `uvm_component_utils(my_scoreboard)

    extern function new(string name, uvm_component parent);
    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);
    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);
    transaction_dut 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

driver_dut.sv

1. build_phase中config_db虚接口
2. main_phase中,把请求到的tranaction通过tr.pack_bytes任务打包成DUT需要的数据,最后调用seq_item_port.item_done()任务,告诉sequence这次传输完成,即`uvm_do宏的一次完成。

class driver_dut extends uvm_driver#(transaction_dut);
    virtual interface_dut vif;

    `uvm_component_utils(driver_dut)

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

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

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

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

task driver_dut::main_phase(uvm_phase phase);
    vif.data <= 8'd0;
    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 driver_dut::drive_one_pkt(transaction_dut tr);
    byte unsigned data_q[];
    int data_size;

    data_size = tr.pack_bytes(data_q) / 8;
    `uvm_info("driver_dut", "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("driver_dut", "end drive one pkt", UVM_LOW)
endtask

driver_dut.sv

1. build_phase中实例化port,并config虚接口
2. main_phase中收集数据并转化为transaction通过端口广播出去。

class my_monitor extends uvm_monitor;
    virtual interface_dut vif;

    uvm_analysis_port#(transaction_dut) ap;

    `uvm_component_utils(my_monitor);

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

    extern virtual function void build_phase(uvm_phase phase);
    extern task main_phase(uvm_phase phase);
    extern task collect_one_pkt(transaction_dut tr);
endclass

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

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

task my_monitor::collect_one_pkt(transaction_dut 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];//-18 byte
    data_size = tr.unpack_bytes(data_array) / 8;
    `uvm_info("my_monitor", "end collect one pkt", UVM_LOW)
endtask

sequencer.sv

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

所有代码均放在此仓库中,需要的从这里拿

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值