IC验证01--UVM核心机制的简化理解(附源码)


以下是 UVM(Universal Verification Methodology)的简化实现机制,帮助你理解其核心原理并快速上手:

一、UVM 核心机制的简化理解

1. 组件层次与继承

基础类:uvm_component(所有组件的基类)、uvm_object(事务类的基类)。
关键组件(从简单到复杂):
在这里插入图片描述

2. 事务级通信(TLM)

  • 简化实现:使用 uvm_analysis_port 和 uvm_analysis_export 实现单向通信。

  • 发送方(如 Monitor):

uvm_analysis_port#(my_transaction) ap; // 声明端口
ap.write(tr); // 发送事务

  • 接收方(如 Scoreboard):

uvm_analysis_export#(my_transaction) exp; // 声明导出
function void write(my_transaction tr); // 实现回调
// 处理接收到的事务
endfunction

3. Phase 机制

  • 核心 Phases(按执行顺序):
  • build_phase:创建组件实例(new + uvm_config_db 配置)。
  • connect_phase:连接 TLM 端口。
  • run_phase:执行激励(如 Sequence 发送事务)。
  • 简化使用:在 run_phase 中启动线程或任务。

二、简化 UVM 实现示例,并附有详细注释

1. 事务类(Transaction)

`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV

class my_transaction extends uvm_sequence_item;

   // 定义一个48位宽的随机位字段dmac,用于存储目的MAC地址
   rand bit[47:0] dmac;
   // 定义一个48位宽的随机位字段smac,用于存储源MAC地址
   rand bit[47:0] smac;
   // 定义一个16位宽的随机位字段ether_type,用于存储以太网类型
   rand bit[15:0] ether_type;
   // 定义一个字节类型的数组pload,用于存储负载数据
   rand byte      pload[];
   // 定义一个32位宽的随机位字段crc,用于存储CRC校验码
   rand bit[31:0] crc;

   // 约束条件
   // 约束pload数组的大小,使其在46到1500之间
   constraint pload_cons{
      pload.size >= 46;
      pload.size <= 1500;
   }

   // 计算CRC校验码的函数
   function bit[31:0] calc_crc();
      return 32'h0;
   endfunction

   // 在随机化之后调用的函数,用于计算CRC校验码
   function void post_randomize();
      crc = calc_crc;
   endfunction

   // 使用宏定义注册类,并指定字段在UVM的打印和比较操作中的行为
   `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

2. 驱动类(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

3. 监控类(Monitor)

`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV

class my_monitor extends uvm_monitor;

   // 定义一个虚拟接口vif,用于与DUT进行通信
   virtual my_if vif;

   // 定义一个分析端口ap,用于将事务发送到分析器
   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);
      // 从配置数据库中获取虚拟接口vif
      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);
   // 定义一个事务tr
   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);
   // 定义一个字节类型的队列data_q,用于存储事务数据
   byte unsigned data_q[$];
   // 定义一个字节类型的数组data_array,用于存储事务数据
   byte unsigned data_array[];
   // 定义一个8位宽的逻辑类型data,用于存储单个字节的数据
   logic [7:0] data;
   // 定义一个逻辑类型valid,用于指示数据有效
   logic valid = 0;
   // 定义一个整数data_size,用于存储事务数据的大小
   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

4. 序列类(Sequence)

`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

5. 记分板(Scoreboard)

`ifndef MY_SCOREBOARD__SV
`define MY_SCOREBOARD__SV

class my_scoreboard extends uvm_scoreboard;
   // 定义一个事务队列expect_queue,用于存储期望的事务
   my_transaction  expect_queue[$];
   // 定义一个阻塞获取端口exp_port,用于从期望的事务队列中获取事务
   uvm_blocking_get_port #(my_transaction)  exp_port;
   // 定义一个阻塞获取端口act_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
   exp_port = new("exp_port", this);
   // 创建阻塞获取端口act_port
   act_port = new("act_port", this);
endfunction 

// 主阶段任务
task my_scoreboard::main_phase(uvm_phase phase);
   // 定义两个事务get_expect和get_actual,用于存储从端口获取的事务
   my_transaction  get_expect,  get_actual, tmp_tran;
   // 定义一个结果位result,用于存储比较结果
   bit result;
 
   super.main_phase(phase);
   // 创建两个无限循环,分别从exp_port和act_port获取事务
   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

6. 环境类(Environment)

`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

7. 测试类(Test)

`ifndef BASE_TEST__SV
`define BASE_TEST__SV

class base_test extends uvm_test;

   // 定义一个环境env
   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
   env  =  my_env::type_id::create("env", this); 
endfunction

// 报告阶段函数
function void base_test::report_phase(uvm_phase phase);
   // 定义一个报告服务器server
   uvm_report_server server;
   // 定义一个整数err_num,用于存储错误数量
   int err_num;
   super.report_phase(phase);

   // 获取报告服务器
   server = get_report_server();
   // 获取错误数量
   err_num = server.get_severity_count(UVM_ERROR);

   // 如果错误数量不为0,打印测试失败信息
   if (err_num != 0) begin
      $display("TEST CASE FAILED");
   end
   // 否则,打印测试成功信息
   else begin
      $display("TEST CASE PASSED");
   end
endfunction

`endif

三、简化 UVM 框架的关键技巧

1. 避免复杂特性

  • 不使用:TLM 2.0、工厂机制(Factory)、寄存器模型(Register Model)。
  • 简化替代:
    直接实例化组件(跳过工厂注册)。
    使用 uvm_config_db 传递配置参数。
    手动连接 TLM 端口(不依赖 uvm_component_utils)。

2. 最小化 UVM 依赖

  • 使用 uvm_pkg:😗 代替完整 UVM 库,减少编译时间。
    对于简单验证,可自定义基础类(如 my_component)替代 uvm_component。

3. 手动实现关键功能

  • Phase 控制:手动调用 build_phase、run_phase 等方法。
  • 消息机制:自定义 info、error 函数替代 UVM 宏。

4. 简化测试启动

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

import uvm_pkg::*;
`include "my_if.sv"
`include "my_transaction.sv"
`include "ip_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"

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

四、验证流程优化

1. 快速调试技巧

在 Monitor 中添加 $display 打印关键信号。
在 Scoreboard 中添加简单比对逻辑(如检查数据范围)。

2. 覆盖率收集

// 在Monitor中添加覆盖率组
covergroup cg_trans @(posedge vif.clk);
  cp_addr : coverpoint vif.addr { bins addr_low = {[0:31]}; bins addr_high = {[32:100]}; }
  cp_wr   : coverpoint vif.wr;
  cr_addr_wr : cross cp_addr, cp_wr;
endgroup

// 在Monitor的run_phase中采样
task run_phase(uvm_phase phase);
  cg_trans = new();
  forever begin
    @(posedge vif.clk);
    // 采样事务...
    cg_trans.sample();  // 采样覆盖率
  end
endtask
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yang_20250429

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值