Verilog HDL 学习笔记(十五)| 第15章 高级验证技术

15.1 传统验证流程(Traditional Verification Flow)

一、传统验证的基本流程

在进入高级验证技术之前,先回顾一下我们一直在用的传统验证方法:

复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                        传统验证流程                                         │
│                                                                             │
│  1. 编写RTL设计                                                            │
│           ↓                                                                 │
│  2. 编写Testbench(激励 + 监视 + 检查)                                    │
│           ↓                                                                 │
│  3. 运行仿真                                                               │
│           ↓                                                                 │
│  4. 分析波形和输出                                                         │
│           ↓                                                                 │
│  5. 发现错误 → 修改RTL → 重新仿真(回到第1步)                            │
│           ↓                                                                 │
│  6. 仿真通过 → 覆盖率达标 → 完成                                           │
└─────────────────────────────────────────────────────────────────────────────┘

二、传统验证的核心方法:定向测试

定向测试(Directed Test) 是传统验证的主要手段------设计者根据功能规格,手写测试向量,逐一验证每个功能点。

复制代码
// 典型的定向测试:ALU加法功能验证
initial begin
    // 测试用例1:正数 + 正数
    a = 8'd10; b = 8'd20; op = OP_ADD;
    #20;
    if (out !== 8'd30)
        $display("ERROR: 10 + 20 = %d, expected 30", out);
    
    // 测试用例2:负数 + 正数
    a = 8'd200; b = 8'd10; op = OP_ADD;
    #20;
    if (out !== 8'd210)
        $display("ERROR: 200 + 10 = %d, expected 210", out);
    
    // 测试用例3:溢出
    a = 8'd127; b = 8'd1; op = OP_ADD;
    #20;
    if (overflow !== 1'b1)
        $display("ERROR: 127 + 1 should overflow");
    
    // ... 手写更多测试用例
end

三、传统验证的局限性

局限性 说明
覆盖率有限 只能测试设计者想到的场景,无法覆盖所有可能的输入组合
随机性不足 定向测试的输入模式是固定的,难以发现"意外"的错误
耗时巨大 手工编写大量测试向量,工作量大且容易遗漏
验证不充分 仿真通过≠没有Bug,只是"没测到Bug"
无法证明正确性 仿真只能证明"有错",不能证明"无错"

💡 一个经典问题:如果你的设计有32位输入,那么有 2^32 = 42亿 种输入组合。你不可能全部测完。传统仿真只能验证你测过的那部分。

四、传统验证的改进方向

面对这些局限,工业界发展出多种改进方法:

方法 核心思想
随机测试(Random Test) 用随机数生成测试向量,覆盖更多场景
覆盖率驱动验证(CDV) 用覆盖率指标衡量验证进度,直到达标
断言检查(Assertion) 在设计中嵌入"自动检查器"
形式化验证(Formal) 用数学方法证明设计正确性

15.2 断言检查(Assertion Checking)

一、什么是断言?

断言(Assertion) 是一种嵌入在设计中的检查器,用于验证设计是否满足特定的属性(Property)。

简单说:断言就是你对设计行为的"期望",用Verilog代码写出来,让工具在仿真过程中自动检查是否满足。

复制代码
// 断言示例:握手协议检查
// 期望:如果request为1,那么两个周期内acknowledge必须为1
property handshake_check;
    @(posedge clk) request |-> ##[1:2] acknowledge;
endproperty

assert property (handshake_check);

二、为什么需要断言?

传统Testbench vs 断言:

对比维度 传统Testbench 断言
检查方式 在Testbench中写检查逻辑 在设计中嵌入检查器
检查时机 仿真结束后分析输出 实时检查
定位错误 只知道输出错了 立即定位到错误发生的位置
复用性 检查代码与测试耦合 断言可复用于不同测试
覆盖范围 只检查测试用例覆盖的场景 只要仿真运行就会自动检查

断言的核心优势 :把"检查"从Testbench转移到设计中 ,实现"边仿真边检查"。你不用等仿真结束后再看波形,断言会在错误发生的瞬间立即报警。

三、断言的类型

类型 特点 作用
立即断言(Immediate Assertion) 在过程块中执行,立即检查 检查当前时刻的条件
并发断言(Concurrent Assertion) 基于时钟周期,并行执行 检查时序行为(如"2个周期后")

立即断言示例:

复制代码
always @(posedge clk) begin
    // 检查:如果写使能有效,数据不应为x
    if (write_enable)
        assert (data !== 8'bxxxxxxxx) 
            else $error("Write data is unknown!");
end

并发断言示例(SystemVerilog):

复制代码
// 检查:如果request有效,acknowledge应在1-3个周期内有效
property req_ack;
    @(posedge clk) disable iff (reset)
    request |-> ##[1:3] acknowledge;
endproperty

assert property (req_ack)
    else $error("Handshake timeout!");

四、断言在实际设计中的应用

应用1:协议检查
复制代码
// 检查:SPI从设备片选有效时,时钟不应超过指定频率
property spi_cs_setup;
    @(posedge clk)
    !cs |-> ##[1:$] cs;  // cs先低后高
endproperty

property spi_cs_hold;
    @(posedge clk)
    cs |-> !cs within [1:10];  // cs在1-10周期内释放
endproperty

assert property (spi_cs_setup);
assert property (spi_cs_hold);
应用2:状态机安全检查
复制代码
// 检查:状态机永远不会进入非法状态
property no_illegal_state;
    @(posedge clk) disable iff (reset)
    state inside {IDLE, READ, WRITE, DONE};
endproperty

assert property (no_illegal_state)
    else $fatal("FSM entered illegal state!");
应用3:FIFO安全检查
复制代码
// 检查:FIFO空时不能读,满时不能写
property fifo_no_read_when_empty;
    @(posedge clk) disable iff (reset)
    (!empty) |-> (rd_en |=> !empty);
endproperty

property fifo_no_write_when_full;
    @(posedge clk) disable iff (reset)
    (!full) |-> (wr_en |=> !full);
endproperty

assert property (fifo_no_read_when_empty)
    else $error("FIFO read while empty!");
assert property (fifo_no_write_when_full)
    else $error("FIFO write while full!");

四、断言检查的工作流程

复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                       断言检查工作流程                                      │
│                                                                             │
│  1. 在设计代码中嵌入断言                                                    │
│           ↓                                                                 │
│  2. 运行正常仿真(不需要额外的检查代码)                                    │
│           ↓                                                                 │
│  3. 仿真过程中,断言自动检查                                                │
│           ↓                                                                 │
│  4. 如果断言被违反 → 立即报错,仿真停止或记录                              │
│           ↓                                                                 │
│  5. 查看报错信息 → 定位错误位置 → 修改设计                                 │
└─────────────────────────────────────────────────────────────────────────────┘

五、断言的优缺点

优点 缺点
实时检查,错误立即暴露 需要在设计中嵌入额外代码
可复用性强,一次编写多次使用 断言编写需要专门的语法知识
定位精确,直接指出错误位置 复杂时序属性的断言编写较难
覆盖率高,只要仿真运行就检查 部分断言可能不可综合

15.3 形式化验证(Formal Verification)

一、什么是形式化验证?

形式化验证(Formal Verification) 是使用数学方法 来证明设计是否满足特定属性的技术。

💡 核心区别:

  • 仿真 :给一组输入,看输出对不对------"测试"

  • 形式化验证 :用数学证明,对所有可能的输入,设计都满足属性------"证明"

经典类比:

方法 类比
仿真 抽查考试------只检查你抽到的题目
形式化验证 数学证明------证明"对所有x,P(x)成立"

二、形式化验证的两种主要形式

形式 说明 工具示例
等价性检查(Equivalence Checking) 证明两个设计的功能完全相同(如RTL vs 门级网表) Conformal、Formality
属性检查(Property Checking) 证明设计满足指定的属性(如"永远不会死锁") JasperGold、VC Formal

三、等价性检查

应用场景: 综合后,证明门级网表与RTL的功能完全等价。

复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                        等价性检查                                           │
│                                                                             │
│  ┌─────────────┐                                                           │
│  │   RTL设计   │──────┐                                                    │
│  └─────────────┘      │                                                    │
│                       ▼                                                    │
│              ┌─────────────────┐                                          │
│              │  等价性检查工具 │──────▶ 等价 ✅ / 不等价 ❌                │
│              └─────────────────┘                                          │
│                       ▲                                                    │
│  ┌─────────────┐      │                                                    │
│  │  门级网表   │──────┘                                                    │
│  └─────────────┘                                                           │
│                                                                             │
│  数学证明:对所有输入,RTL输出 ≡ 门级网表输出                             │
└─────────────────────────────────────────────────────────────────────────────┘

等价性检查的优势:

优势 说明
完全性 数学证明,覆盖所有输入组合
速度快 比门级仿真快几个数量级
无需测试向量 不需要编写Testbench

等价性检查的局限:

局限 说明
只验证等价性 如果RTL本身有错,等价性检查无法发现
依赖综合设置 综合优化可能改变结构但不改变功能
状态空间爆炸 对于复杂设计(如含大存储器),可能无法完成

四、属性检查

应用场景: 证明设计满足特定的属性(Property) 。

属性示例:

属性类型 示例
安全性(Safety) "永远不会进入非法状态"
活性(Liveness) "请求最终会被响应"
互斥性(Mutual Exclusion) "两个信号不会同时为1"
无死锁(Deadlock Free) "系统永远不会卡死"

属性检查的工作流程:

复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                        属性检查流程                                         │
│                                                                             │
│  1. 编写RTL设计                                                             │
│           ↓                                                                 │
│  2. 编写属性(用SVA/PSL等属性描述语言)                                     │
│           ↓                                                                 │
│  3. 形式化工具自动搜索反例                                                  │
│           ↓                                                                 │
│  4. 结果:                                                                  │
│     ├── 证明通过 → 属性在所有情况下都成立 ✅                               │
│     └── 找到反例 → 属性在某些情况下不成立 ❌ → 分析反例                    │
└─────────────────────────────────────────────────────────────────────────────┘

形式化验证的核心思想:

形式化工具不需要 你提供测试向量。它会穷举所有可能的输入序列,自动搜索是否有违反属性的情况。如果找到反例,会给出具体的输入序列和波形,帮助你定位问题。

五、形式化验证 vs 仿真验证

对比维度 仿真验证 形式化验证
验证方法 测试(给输入,看输出) 证明(数学推理)
覆盖范围 只覆盖测试到的场景 覆盖所有可能的输入
是否需要Testbench ✅ 需要 ❌ 不需要(只需属性)
能否发现"未测试到"的Bug ❌ 不能 ✅ 能
验证速度 慢(需要跑很多向量) 快(数学求解)
状态空间 不受限 受限于状态空间大小
适用设计规模 大规模 中小规模(模块级)
调试难度 容易(看波形) 较难(需分析反例)
工业应用 全流程 模块级、关键路径

重要认识 :形式化验证不能完全替代仿真 。对于大规模设计(如完整CPU),形式化验证会因为"状态空间爆炸"而无法完成。但在模块级 和关键控制逻辑(如仲裁器、协议控制器、状态机)中,形式化验证非常有效。

15.4 三种验证方法的对比与选择

一、总览对比

验证方法 核心思想 覆盖范围 适用阶段 典型工具
传统仿真 给输入,看输出 测试到的场景 全流程 ModelSim、VCS
断言检查 嵌入检查器,实时报警 仿真运行的所有时刻 RTL仿真、门级仿真 SVA、PSL
形式化验证 数学证明 所有可能的输入 模块级、RTL阶段 JasperGold、VC Formal

二、实际项目中的验证策略

在实际芯片设计中,三种方法不是互斥的,而是互补的:

复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                      现代芯片验证策略                                       │
│                                                                             │
│  模块级验证                                                                 │
│  ├── 形式化验证(属性检查)→ 验证控制逻辑、协议                                │
│  ├── 断言检查 → 嵌入关键路径检查                                              │
│  └── 传统仿真 → 验证数据通路功能                                              │
│           ↓                                                                 │
│  子系统级验证                                                               │
│  ├── 断言检查 → 检查接口协议                                                  │
│  └── 传统仿真 → 系统级功能验证                                                │
│           ↓                                                                 │
│  全芯片验证                                                                 │
│  ├── 等价性检查 → RTL vs 门级网表                                            │
│  └── 传统仿真 → 全芯片功能验证                                                │
│           ↓                                                                 │
│  物理验证                                                                   │
│  └── 时序分析(STA)→ 验证时序约束                                            │
└─────────────────────────────────────────────────────────────────────────────┘

三、验证方法的选择原则

场景 推荐方法 原因
控制逻辑(状态机、仲裁器) 形式化验证 + 断言 状态空间小,适合形式化
数据通路(加法器、乘法器) 传统仿真 + 断言 输入空间大,形式化困难
接口协议(AXI、USB) 断言检查 协议规则明确,适合断言
RTL vs 门级 等价性检查 证明两者功能相同
存储器、大FIFO 传统仿真 状态空间太大,形式化不可行
相关推荐
156082072196 小时前
JFMRFVU3P5G的bit正常,但烧录mcs程序后加载异常
fpga开发
狂奔蜗牛(bradley)6 小时前
把 EtherCAT 初始化从 FPGA 搬到 ARM:命令通道的接口设计与11个坑
arm开发·人工智能·fpga开发·架构
女神下凡1 天前
芯参谋(37):EEPROM_电路设计指南
arm开发·单片机·嵌入式硬件·fpga开发·设计规范
szxinmai主板定制专家2 天前
工业视觉新思路:FPGA图像预处理+RK NPU推理,实现高速缺陷检测
人工智能·嵌入式硬件·fpga开发·rk3576+codesys
FPGA小徐2 天前
【硬件实战】立创 EDA 绘制 TF/SD 卡模块电路|SPI 模式原理图 + 阻抗匹配 + PCB 布局布线全教程
单片机·嵌入式硬件·fpga开发
狂奔蜗牛(bradley)2 天前
EtherCAT DC 分布式时钟同步的 ModelSim 仿真:从帧模板到从站行为建模
人工智能·嵌入式硬件·fpga开发·架构
ALINX技术博客3 天前
8 通道同步采样 ADC!AN706 波形显示实验 【FPGA黑金云课堂】
fpga开发
szxinmai主板定制专家3 天前
RK3568+FPGA+CODESYS异构控制方案:赋能半导体设备与工业自动化升级
人工智能·fpga开发·自动化·rk3576·半导体设备
辰丁电子3 天前
allegro入门教程-菜单栏Logic
fpga开发·pcb设计·pcb工艺·layout·pcb layout
FPGA小徐4 天前
数字IC/FPGA设计基础_ILA原理与使用
fpga开发