上文试图运行了第一个UVM平台,但实际上和真正的验证平台相差甚远,本文试图补充一些组件内容,同时解读一下书中的代码。

https://github.com/shirainbown/UVM-Step-by-Step/tree/master

我们从第一个实例可以发现,一个最基础的UVM 平台至少是需要有driver的,包含的内容有 top_tb + driver + DUT,结构层次为:

├── top_tb
   ├── drv
   ├── my_dut

修改代码得到:

// my_driver.sv
`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
class my_driver extends uvm_driver;

   `uvm_component_utils(my_driver)  // 改动1
   function new(string name = "my_driver", uvm_component parent = null);
      super.new(name, parent);
      `uvm_info("my_driver", "new is called", UVM_LOW);// 打印信息
   endfunction
   extern virtual task main_phase(uvm_phase phase);
endclass

task my_driver::main_phase(uvm_phase phase);
   phase.raise_objection(this);  // 改动2
   `uvm_info("my_driver", "main_phase is called", UVM_LOW);
   top_tb.rxd <= 8'b0; 
   top_tb.rx_dv <= 1'b0;
   while(!top_tb.rst_n)
      @(posedge top_tb.clk);
   for(int i = 0; i < 256; i++)begin
      @(posedge top_tb.clk);
      top_tb.rxd <= $urandom_range(0, 255);
      top_tb.rx_dv <= 1'b1;
      `uvm_info("my_driver", "data is drived", UVM_LOW);
   end
   @(posedge top_tb.clk);
   top_tb.rx_dv <= 1'b0;
   phase.drop_objection(this);  //改动3 
endtask
`endif

有几个比较重要的改动,`uvm_component_utils 表示将my_driver注册到UVM 内部,这样my_driver可以使用一些内置的函数,例如在top_tb中的run_test,不用自己定义,较为方便。

改动2-3 是为了配合top_tb中的run_test,通过run_test执行driver的这个task必须要先raise再drop_objection。

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

import uvm_pkg::*;
`include "my_driver.sv"

module top_tb;

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

dut my_dut(.clk(clk),
           .rst_n(rst_n),
           .rxd(rxd),
           .rx_dv(rx_dv),
           .txd(txd),
           .tx_en(tx_en));

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("my_driver");  // 替代了原来的显示调用
end
/*
   my_driver drv;
   drv = new("drv", null);
   drv.main_phase(null);  // main_phase 类似于主函数
   $finish();
*/
endmodule

运行同样的脚本得到输出:

`uvm_info("my_driver", "main_phase is called", UVM_LOW);
// my_driver作为一个分类的名字,会统计这个标签出现的次数; "main_phase is called" 是需要输出的信息;UVM_LOW 冗余程度

下一篇文章将添加一个接口Interface,用于连接dut和driver,替代driver中直接访问top_tb的方式。