3. 模型检查(Model Checking)核心原理:状态空间搜索、时序属性、Kripke结构、不动点计算与CTL/LTL基础

问题背景:为什么需要模型检查?

在形式化验证的工程实践中,仿真(Simulation)只能证明“测试过的路径存在”,无法证明“未测试的路径不存在”。对于复杂的硬件控制逻辑、总线协议或流水线设计,状态空间可能达到 \(10^{20}\) 以上,依靠手工编写测试用例或随机仿真,覆盖率难以收敛。

模型检查(Model Checking)提供了一种穷尽式自动验证方法:它遍历系统所有可达状态,检查是否满足给定的时序属性。如果属性不成立,则自动生成一条反例路径(Counterexample),帮助工程师快速定位设计缺陷。

核心价值:模型检查不是“找 bug 的仿真器”,而是“证明正确性的验证器”。只要建模正确、属性完整,结果就是数学级别的结论。

核心概念:模型检查的四大支柱

概念说明在Questa Formal中的角色
状态空间系统所有可能的状态及状态转移关系的集合由 RTL 设计自动提取,可做抽象或约简
Kripke 结构形式化模型 \(M=(S, S_0, R, L)\),S 为状态集合,R 为转移关系,L 为原子命题标记函数验证引擎内部表示,用于遍历和推理
时序属性描述系统随时间变化的期望行为,如“请求后必然响应”使用 SVA/PSL 编写,或使用 CTL/LTL 描述
不动点计算通过迭代计算状态集合的最小/最大不动点,判定属性是否成立BDD 或 SAT/SMT 引擎的基础算法

原理机制:Kripke 结构与状态空间搜索

模型检查的第一步是将设计转换为 Kripke 结构。每个状态包含所有寄存器、存储器和关键信号的值;状态转移对应一个时钟周期或输入激励。属性 \(\varphi\) 的验证就转化为:是否存在一条从初始状态出发的路径,使得 \(\varphi\) 在指定时刻成立或不成立。

RTL/Netlist 设计建模 + 属性 Kripke 结构 状态空间 时序属性 CTL/LTL/SVA 表达规格 不动点 迭代遍历 BDD/SAT 通过 / 反例 报告

CTL( Computation Tree Logic)和 LTL(Linear Temporal Logic)是两种常用的属性描述语言。CTL 以状态量词和路径量词组合表达,例如 \(AG( req \rightarrow AF\ ack )\) 表示“在所有可达状态中,请求之后所有路径最终都能收到应答”。LTL 则以线性路径为语义,使用 \(\square\)( always)、\(\Diamond\)( eventually)、\(\mathsf{U}\)( until)等算子描述系统所有执行路径上的时序行为。

流程步骤:一次完整的模型检查任务

  1. 建立模型:将 RTL 或抽象模型转换为 Kripke 结构。
  2. 编写属性:使用 SystemVerilog Assertions(SVA)、PSL 或 CTL/LTL 形式化描述设计意图。
  3. 选择引擎:根据属性类型和设计规模选择 BDD、SAT 或基于 ABC 的引擎。
  4. 执行验证:工具自动遍历状态空间,计算不动点。
  5. 分析结果:若属性失败,查看反例波形定位 bug;若成功,确认完备性和覆盖度。
Questa Formal 实践建议:对于控制密集的场景,优先使用verify_property 和断言覆盖(cover)语句。运行时注意观察“State Explosion”警告,必要时使用cutpoint 或抽象模式。

案例说明:仲裁器验证

假设一个简单总线仲裁器,保证“一旦某个 master 发出请求,最终会获得 grant”。其 SVA 属性可写为:

property req_granted;
  @(posedge clk)
  request |=> eventually grant;
endproperty
assert property (req_granted);

Questa Formal 在验证该属性时,会从初始状态出发,遍历所有可能的状态组合。如果存在某个状态之后 grant 一直为 0,工具会返回一条反例路径,帮助工程师判断是协议漏洞还是约束缺失。

表格总结:CTL 与 LTL 核心算子对比

逻辑核心算子语义特征典型场景
LTL\(\square\)、\(\Diamond\)、\(\mathsf{U}\)、\(\bigcirc\)(next)所有线性路径上的事件顺序协议时序、流水线推进、信号握手
CTL\(A\)、\(E\)、\(G\)、\(F\)、\(X\)、\(U\)状态与路径量词组合,可表达可能性复位释放、互斥访问、活锁检测
CTL*上述全部更广泛地混合路径与状态量化复杂嵌套时序属性

误区提醒

  • 把模型检查当仿真用:模型检查面向穷尽证明,不适合大数据流计算;仿真适合功能调试。
  • 忽略属性正确性:属性写错,结论全错。务必先进行“正反断言”检查,故意注入错误验证属性敏感性。
  • 以为状态空间总能完全遍历:实际工程中常常需要抽象、约简或假设-保证推理,否则会超时或内存溢出。
  • 混淆 LTL 与 CTL 语义:LTL 的“最终”是路径上的,而 CTL 的 \(EF\) 是“存在路径可达”;换用符号时务必明确语义。

实践清单:入门模型检查应完成的 6 件事

  1. 用 Questa Formal 打开一个简单设计(如 Arbiter、FIFO)。
  2. 为设计的关键协议编写 3~5 条 SVA 断言。
  3. 使用 check 命令运行形式化分析。
  4. 故意修改设计制造 bug,观察反例波形。
  5. 识别模型的初始状态与 reset 约束,避免非法状态干扰。
  6. 记录验证时间和状态空间大小,作为后续复杂设计的对比基线。
小结:模型检查的核心是“状态空间搜索 + 时序属性 + 不动点计算”。掌握 Kripke 结构建模、CTL/LTL 属性表达以及反例分析,是学习 Questa Formal 的关键基石。