跳到主要内容
UVM验证

UVM 基础入门

UVM验证方法学入门,基于实例讲解基础概念

UVM 验证

UVM 基础入门

本册基于 design.svtestbench.sv 示例讲解 UVM 基础概念,包括架构、 Component 关系、Transaction、Sequence、Sequencer、数据流和约束。 掌握这些概念后,可继续阅读《UVM 工程化实践》,从验证计划、平台装配、连接机制、 检查闭环和覆盖收敛的角度学习可维护验证平台的搭建方法。

1. UVM 是什么

UVM (Universal Verification Methodology) = 通用验证方法学

UVM = 一套标准 + 一套工具库 + 一套最佳实践

目的:让不同公司的验证环境可以复用

为什么需要 UVM

没有 UVM:
├── 每家公司自己写验证环境
├── 代码不通用
└── 复用性差

有 UVM:
├── 统一标准
├── 代码可复用
└── 大量现成组件

2. UVM 架构总览

┌──────────────────────────────────────────────────┐
│                  Test (测试用例)                  │
│  ┌────────────────────────────────────────────┐  │
│  │           Environment (环境)               │  │
│  │  ┌──────────────────────────────────────┐  │  │
│  │  │         Agent (代理)                 │  │  │
│  │  │                                      │  │  │
│  │  │  Sequence ──→ Sequencer ──→ Driver ──│──│──│──→ Interface ──→ DUT
│  │  │                                      │  │  │
│  │  │              Monitor ←───────────────│──│──│←── Interface ←── DUT
│  │  └──────────────────────────────────────┘  │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘

本例的架构

┌─────────────────────────────────────┐
│           adpcm_test                │
│                                     │
│   adpcm_tx_seq                      │
│        │                            │
│        ▼                            │
│   adpcm_env                         │
│      └── adpcm_agent                │
│          ├── adpcm_sequencer        │
│          ├── adpcm_driver ──────────│──→ adpcm_if ──→ DUT
│          └── adpcm_monitor ←────────│─── adpcm_if
└─────────────────────────────────────┘

3. UVM Component 关系详解

Component 是什么?

Component = 有生命周期的"活的"组件

特点:
├── 有 build_phase(创建阶段)
├── 有 run_phase(运行阶段)
├── 有 parent(父组件)
├── 可以创建子组件
└── 仿真结束才销毁

Component vs Object

特性ComponentObject
生命周期隶属 UVM 组件树,贯穿各 phase由句柄引用决定,可短暂也可贯穿仿真
有 parent✅ 有层次❌ 独立
有 Phase✅ 完整❌ 无
基类uvm_componentuvm_object
例子Driver, Monitor, TestSequence Item
类比:
  Component = 员工(长期在职,有上级)
  Object = 快递包裹(用完就扔)

Component 层次结构

uvm_component                    ← 根(所有 component 的祖先)

    ├── uvm_test                ← 测试用例
    │       │
    │       └── uvm_env         ← 环境
    │               │
    │               └── uvm_agent    ← 代理
    │                       │
    │                       ├── uvm_driver     ← 驱动器
    │                       ├── uvm_monitor    ← 监控器
    │                       └── uvm_sequencer  ← 序列器

    └── uvm_scoreboard         ← 记分板

Parent-Child 关系

// 创建组件时,指定 parent
class my_test extends uvm_test;
    my_env env;

    function void build_phase(uvm_phase phase);
        // this = my_test(自己是 parent)
        env = my_env::type_id::create("env", this);  // env 的 parent 是 test
    endfunction
endclass

class my_env extends uvm_env;
    my_agent agent;

    function void build_phase(uvm_phase phase);
        // this = my_env(自己是 parent)
        agent = my_agent::type_id::create("agent", this);  // agent 的 parent 是 env
    endfunction
endclass

层次结构图

my_test (parent = null)

    └── my_env (parent = my_test)

            └── my_agent (parent = my_env)

                    ├── my_driver (parent = my_agent)
                    ├── my_monitor (parent = my_agent)
                    └── my_sequencer (parent = my_agent)

层级路径:
  my_driver 的全名 = "uvm_test_top.env.agent.driver"

为什么需要层次?

1. 仿真结束判断
   └── run phase 中的 objection 全部撤销后,UVM 才可结束运行阶段

2. Phase 传播
   └── test.build_phase → env.build_phase → agent.build_phase → ...

3. 配置传递
   └── test 设置参数 → 子组件可以读取

4. 打印层次
   └── 便于调试,知道组件在哪个位置

完成 Agent 和 Environment 扩展后的层次

top_tb                          ← 顶层模块(不是 component)

└── adpcm_test              ← component(parent = null)

        └── env             ← adpcm_env(parent = test)

            └── agent       ← adpcm_agent(parent = env)

                ├── driver      ← Active 模式创建
                ├── monitor     ← Active/Passive 都创建
                └── sequencer   ← Active 模式创建

// 各层只创建自己的直接子组件:
// Test:
env = adpcm_env::type_id::create("env", this);

// Environment:
agent = adpcm_agent::type_id::create("agent", this);

// Active Agent:
driver    = adpcm_driver::type_id::create("driver", this);
monitor   = adpcm_monitor::type_id::create("monitor", this);
sequencer = adpcm_sequencer::type_id::create("sequencer", this);

Component 的 Phase 顺序

uvm_test (顶层)

    ├── build_phase          从 test 开始,向下传播
    │   └── test.build_phase
    │       └── env.build_phase
    │           └── agent.build_phase
    │               └── driver.build_phase

    ├── connect_phase        从底层开始,向上传播
    │   └── driver.connect_phase
    │       └── agent.connect_phase
    │           └── env.connect_phase
    │               └── test.connect_phase

    └── run_phase            所有 component 并行运行
        ├── driver.run_phase      ← 并行
        ├── monitor.run_phase     ← 并行
        └── sequencer.run_phase   ← 并行

4. 事务层 (Sequence Item)

testbench.sv 第 17-72 行

class adpcm_seq_item extends uvm_sequence_item;

    rand logic[31:0] data;    // 32位数据(随机生成)
    rand int delay;           // 发送前延迟(随机生成)

    constraint c_delay { delay > 0; delay <= 20; }  // 约束:1-20

    // 以下都是 UVM 需要的固定写法
    `uvm_object_utils(adpcm_seq_item)   // 注册到工厂

    function new(string name = "adpcm_seq_item");
        super.new(name);                // 调用父类构造函数
    endfunction

    function void do_copy(uvm_object rhs);     // 拷贝功能
        adpcm_seq_item rhs_;
        if(!$cast(rhs_, rhs))                  // 类型转换
            `uvm_fatal("COPY", "adpcm_seq_item 类型转换失败")
        data = rhs_.data;
        delay = rhs_.delay;
    endfunction

    function bit do_compare(uvm_object rhs, uvm_comparer comparer);  // 比较功能
        adpcm_seq_item rhs_;
        do_compare = $cast(rhs_, rhs) &&
                     data == rhs_.data &&
                     delay == rhs_.delay;
    endfunction

    function string convert2string();           // 转字符串(打印用)
        return $sformatf(" data:\t%0h\n delay:\t%0d", data, delay);
    endfunction

endclass

这里的 $cast(目标变量, 源句柄) 不是普通赋值,而是运行时类型检查do_copy()do_compare() 的参数类型都是通用的 uvm_object;只有当 rhs 实际指向 adpcm_seq_item(或其子类)对象时,转换才成功并返回 1,此后才能安全访问 datadelay。转换只让两个句柄指向同一个对象,不会创建或复制对象。

do_compare() 中,&& 具有短路求值特性:若 $cast 返回 0,后面的 rhs_.datarhs_.delay 不会被访问。若转换失败属于不应继续的错误,则像 do_copy() 一样检查返回值并用 `uvm_fatal 报告。更完整的向上转换、向下转换和失败行为见《SystemVerilog 验证专用语法》的“安全的向下转换:$cast”。

类比理解

Sequence Item = 快递包裹

┌─────────────────┐
│   data = 32'hAA │  ← 包裹里的东西
│   delay = 5     │  ← 发送前等待的时间
└─────────────────┘

rand     = 随机生成(每次仿真值不同)
constraint = 约束(delay 必须在 1-20 之间)

关键方法

方法作用什么时候调用
new()构造函数创建对象时
do_copy()复制数据需要拷贝事务时
do_compare()比较数据scoreboard 比对时
convert2string()返回便于日志显示的字符串显式调用或由 sprint() 使用时

5. 序列层 (Sequence)

testbench.sv 第 136-165 行

class adpcm_tx_seq extends uvm_sequence #(adpcm_seq_item);

    `uvm_object_utils(adpcm_tx_seq)

    adpcm_seq_item req;

    int unsigned no_reqs = 10;    // 默认发送 10 个事务

    task body();
        for(int i = 0; i < no_reqs; i++) begin
            req = adpcm_seq_item::type_id::create($sformatf("req_%0d", i));
            start_item(req);                              // 1. 开始
            if (!req.randomize())
                `uvm_fatal("RAND", "adpcm_seq_item 随机化失败")
            finish_item(req);                             // 4. 发送
            $display("发送第 %0d 帧", i);
        end
    endtask

endclass

Sequence 的作用

Sequence = 测试计划

"我要发 10 个随机包"
"每个包的数据随机"
"每个包的延迟随机"

它不关心"怎么发",只关心"发什么"

Sequence 的 API

API作用
type_id::create()创建对象(工厂模式)
start_item(req)通知 Sequencer 准备发送
finish_item(req)等待 Driver 处理完
$urandom_range(1,20)生成随机数

6. 序列器 (Sequencer)

testbench.sv 第 121-129 行

class adpcm_sequencer extends uvm_sequencer #(adpcm_seq_item);
    `uvm_component_utils(adpcm_sequencer)

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

7. 完整数据流

时间线:

T0:  adpcm_test.build_phase
     └── 创建 adpcm_env
         └── env 创建 adpcm_agent
             └── agent 创建 Driver、Monitor、Sequencer

T1:  connect_phase
     ├── agent 内部连接 Driver ←→ Sequencer
     └── env 连接 Monitor → 检查/覆盖组件(加入这些组件后)

T2:  adpcm_test.run_phase
     └── 在 env.agent.sequencer 上启动 adpcm_tx_seq

T3:  adpcm_tx_seq.body
     └── 创建 req,随机化 data/delay

T4:  start_item(req)
     └── 通知 Sequencer 准备

T5:  finish_item(req)
     └── Sequencer 把 req 传给 Driver

T6:  Driver 与 Monitor 并发工作
     ├── adpcm_driver
     │   ├── get_next_item(req)  拿到 req
     │   ├── repeat(delay)       等待
     │   ├── frame <= 1          帧开始
     │   └── 发送 8 个 nibble    数据传输

     └── adpcm_monitor
         ├── 等待 frame == 1
         ├── 采样 8 个 nibble
         └── 重组 tr.data

T7:  一帧完成
     ├── Driver: frame <= 0,item_done()
     └── Input Monitor: analysis_port.write(tr)
         └── Reference Model: 生成 expected transaction

T8-T12: 重复 T3-T7 共 10 次


---

## 8. 总结对照表

| UVM 概念              | 本例对应                     | 作用                  |
|-----------------------|-----------------------------|----------------------|
| **Interface**         | `adpcm_if`                  | 信号打包              |
| **Virtual Interface** | `virtual adpcm_if.drv_mp`   | 软件访问硬件的桥梁     |
| **Sequence Item**     | `adpcm_seq_item`            | 数据包定义            |
| **Driver**            | `adpcm_driver`              | 信号时序驱动          |
| **Monitor**           | `adpcm_monitor`             | 采样信号并重组事务     |
| **Agent**             | `adpcm_agent`               | 封装一个协议接口的组件 |
| **Environment**       | `adpcm_env`                 | 组装和连接验证组件     |
| **Reference Model**   | `adpcm_reference_model`     | 根据真实输入预测 expected |
| **Sequencer**         | `adpcm_sequencer`           | 中转调度              |
| **Sequence**          | `adpcm_tx_seq`              | 测试激励              |
| **Test**              | `adpcm_test`                | 测试用例              |
| **Factory**           | `type_id::create()`         | 对象创建              |
| **Factory Override**  | `set_type_override_by_type` | 不改代码替换组件       |
| **Phase**             | `build/connect/run`         | 执行顺序              |
| **objection**         | `raise/drop`                | 防止仿真提前结束       |
| **Config_db**         | `set/get`                   | 跨层次传参             |
| **Scoreboard**        | `check_phase`               | 比对 DUT 输出 vs 预期  |
| **TLM**               | `analysis_port/imp`         | 组件间事务级通信        |
| **Constraint**        | `constraint c_xxx`          | 随机约束               |
| **Coverage**          | `covergroup`                | 功能覆盖率             |
| **Field Macro**       | `uvm_field_int`             | 自动注册字段           |
| **Virtual Sequence**  | `fork/join`                 | 跨 Agent 协调         |
| **Register Model**    | `reg.write/read`            | 寄存器抽象层           |

### UVM 验证流程口诀

  1. 定义接口 (Interface)
  2. 定义虚拟接口 (Virtual Interface)
  3. 定义数据包 (Sequence Item)
  4. 写驱动器 (Driver)
  5. 写监控器 (Monitor)
  6. 写参考模型 (Reference Model)
  7. 写记分板 (Scoreboard)
  8. 写序列器 (Sequencer)
  9. 写测试序列 (Sequence)
  10. 组装代理 (Agent)
  11. 组装测试环境 (Environment)
  12. 组装测试用例 (Test)
  13. 配置参数 (Config_db)
  14. 设置覆盖率 (Coverage)
  15. 跑仿真看结果

---

## 9. Constraint(约束)

### 什么是 Constraint?

Constraint = 随机约束(控制随机变量的取值范围)

rand data; // 随机变量 constraint c_data { // 约束条件 data inside {[0:255]}; }


### 代码示例

```verilog
class my_seq_item extends uvm_sequence_item;
    rand logic [7:0] data;
    rand int delay;
    rand bit [2:0] burst_len;

    // 约束:delay 在 1-20 之间
    constraint c_delay {
        delay > 0;
        delay <= 20;
    }

    // 约束:burst_len 只能是 1, 2, 4, 8
    constraint c_burst {
        burst_len inside {1, 2, 4, 8};
    }

    // 约束:data 不能是 0
    constraint c_data {
        data != 0;
    }
endclass

常用约束类型

约束类型语法作用
范围约束inside {[min:max]}指定范围
列表约束inside {a, b, c}指定列表
条件约束if (条件) {约束}条件生效
唯一约束unique {变量}变量唯一
权重约束dist {1:=50, 2:=50}指定权重

Inline Constraint

// 在调用时添加额外约束
my_seq_item item = my_seq_item::type_id::create("item");
item.randomize() with {
    data == 8'hFF;      // 临时约束:data 必须是 FF
    delay inside {[1:5]}; // 临时约束:delay 在 1-5
};

简单类比

Constraint = 抽奖规则

rand:抽奖(随机)
constraint:规则(限制范围)

"从 1-100 中随机抽一个数"
  → rand int num;
  → constraint c { num inside {[1:100]}; }

最后更新: 2026-08-20