亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> VHDL

精通PSL形式化验证VHDL模块:从理论到实战

为安全关键FPGA应用设计VHDL,仅写测试平台远不够。工程师必须证明模块完全符合规范、无隐藏副作用。形式化验证提供这种数学级保证。

形式化验证通过证明所有可能输入序列均满足设计属性,弥合需求与实现鸿沟。它是医疗器械、航空电子等要求DO-254认证领域的必经之路。

本客座文章由资深形式化验证实践者Michael Finn Jørgensen撰写,分享实战洞见与可落地技巧。示例基于熟悉的AXI FIFO教程如何用就绪/有效握手在块RAM中实现AXI FIFO,演示如何对真实VHDL设计应用PSL。

什么是形式化验证?

形式化验证(FV)是综合前系统性方法,保证设计对所有输入组合正确。不同于依赖人工激励的常规测试平台,FV指定属性,要求所有时钟周期成立。SAT求解器再穷尽证明属性成立——或给出反例精确定位违规周期。

两大核心技术驱动FV:

BMC仅需少量属性即可运行,常作为第一道防线。归纳证明虽更苛刻,却给出绝对确定性。

为何使用形式化验证?

FV擅长自动探索全输入空间揭露隐蔽缺陷。发现故障时,工具给出精简波形精确指示违规属性的准确周期——远易于调试长仿真波形。

虽FV是强大单元测试工具,不替代手工集成测试。模块规模增长时求解器运行时间可能激增。但投入FV时间能减少总调试工作、加速上市。

在PSL中编写属性

PSL(属性规范语言)是VHDL形式化验证通用语。属性通常存放于单独.psl文件,验证阶段由求解器读取。

三个PSL关键字描述属性:

PSL文件也可包含普通VHDL声明与进程,便于为复杂断言创建辅助信号。

每个PSL文件以vunit块起始,绑定特定实体:

vunit INSTANCE_NAME(ENTITY_NAME(ARCHITECTURE_NAME)) { } 

继以默认时钟声明:

default clock is rising_edge(clk); 

属性语法如下:

LABEL : assert always {PRECONDITION} |-> {POSTCONDITION} LABEL : assume always {PRECONDITION} |-> {POSTCONDITION} 

用|=> 表达次周期断言:

LABEL : assert always {PRECONDITION} |=> {POSTCONDITION} 

条件中可用stable约束信号周期间不变,用{C1; C2}链式表达顺序约束。

安装所需工具链

商业工具(如ModelSim)支持形式化验证,但免费Student Edition缺此功能。幸而Linux(及Windows WSL)有健壮开源栈。下文基于Ubuntu 20.04 LTS,适用任意近期Debian系发行版。

前置依赖

sudo apt update && sudo apt upgrade 

安装核心开发库:

sudo apt install build-essential clang bison flex libreadline-dev \ gawk tcl-dev libffi-dev git mercurial graphviz \ xdot pkg-config python python3 libftdi-dev gperf \ libboost-program-options-dev autoconf libgmp-dev \ cmake gnat gtkwave 

Yosys与SymbiYosys

git clone https://github.com/YosysHQ/yosys cd yosys make sudo make install cd .. git clone https://github.com/YosysHQ/SymbiYosys cd SymbiYosys sudo make install cd .. git clone https://github.com/SRI-CSL/yices2 cd yices2 autoconf ./configure make sudo make install cd .. 

GHDL与Yosys插件

git clone https://github.com/ghdl/ghdl cd ghdl ./configure --prefix=/usr/local make sudo make install cd .. git clone https://github.com/ghdl/ghdl-yosys-plugin cd ghdl-yosys-plugin make sudo make install cd .. 

下载示例项目

填写下方表单获取含完整AXI FIFO源码、PSL文件、验证脚本的ZIP包。

实战案例:AXI FIFO形式化验证

下文梳理FIFO关键属性类别并展示PSL编码。

复位处理

f_reset : assume {rst}; 

省略always关键字,此假设仅作用于复位后首周期。标签约定(f_前缀)便于定位失败。

f_after_reset_valid : assert always {rst} |=> {not out_valid}; f_after_reset_ready : assert always {rst} |=> {in_ready}; f_after_reset_head : assert always {rst} |=> {count = 0}; 

注意内部信号count可被引用,因vunit声明包含架构名。

FIFO满/空信号

f_empty : assert always {count = 0} |-> {not out_valid}; f_full : assert always {count = ram_depth-1} |-> {not in_ready}; 

AXI协议合规

f_in_data_stable : assume always {in_valid and not in_ready and not rst} |=> {stable(in_valid) and stable(in_data)}; f_out_data_stable : assert always {out_valid and not out_ready and not rst} |=> {stable(out_valid) and stable(out_data)}; 

就绪信号稳定性:

f_out_ready_stable : assume always {out_ready and not out_valid and not rst} |=> {stable(out_ready)}; f_in_ready_stable : assume always {in_ready and not in_valid and not rst} |=> {stable(in_ready)}; 

FIFO顺序性

顺序性保证先写先出。用辅助信号记忆特定数据值进出时刻建模。

signal f_sampled_in_d1 : std_logic := '0'; signal f_sampled_in_d2 : std_logic := '0'; p_sampled : process (clk) begin if rising_edge(clk) then if in_valid then if in_data = f_value_d1 then f_sampled_in_d1 <= '1'; end if; if in_data = f_value_d2 then f_sampled_in_d2 <= '1'; end if; end if; if out_valid then if out_data = f_value_d1 then f_sampled_out_d1 <= '1'; end if; if out_data = f_value_d2 then f_sampled_out_d2 <= '1'; end if; end if; if rst = '1' then f_sampled_in_d1 <= '0'; f_sampled_in_d2 <= '0'; f_sampled_out_d1 <= '0'; f_sampled_out_d2 <= '0'; end if; end if; end process p_sampled; 

声明数据值为无约束常量,供求解器任意选择位模式:

signal f_value_d1 : std_logic_vector(ram_width - 1 downto 0); signal f_value_d2 : std_logic_vector(ram_width - 1 downto 0); attribute anyconst : boolean; attribute anyconst of f_value_d1 : signal is true; attribute anyconst of f_value_d2 : signal is true; 

最后编码顺序约束:

f_fifo_ordering_in : assume always {f_sampled_in_d2} |-> {f_sampled_in_d1}; f_fifo_ordering_out : assert always {f_sampled_out_d2} |-> {f_sampled_out_d1}; 

运行形式化验证

为sby工具创建axi_fifo.sby脚本:

[tasks] bmc [options] bmc: mode bmc bmc: depth 10 [engines] smtbmc [script] ghdl --std=08 -gram_width=4 -gram_depth=8 axi_fifo.vhd axi_fifo.psl -e axi_fifo prep -top axi_fifo [files] axi_fifo.psl axi_fifo.vhd 

执行验证:

sby --yosys ";yosys -m ghdl" -f axi_fifo.sby 

成功运行输出:

[axi_fifo_bmc] DONE (PASS, rc=0) 

将depth设大于FIFO深度可确保求解器探索满容量场景。

突变测试

为评估属性集完备性,故意注入微小缺陷——如移除驱动out_valid的逻辑——重跑FV。若求解器报告断言失败,证明属性足以捕获该错误。

失败示例:

Assert failed in axi_fifo: i_axi_fifo.f_out_data_stable 

用GTKWave打开生成的trace.vcd定位违规准确周期。

Cover语句

Cover语句用于断言特定场景可达。例:验证FIFO可被填满再清空:

f_full_to_empty : cover { rst = '1'; rst = '0'[*]; rst = '0' and count = ram_depth-1; rst = '0'[*]; rst = '0' and count = 0}; 

调整sby脚本含cover任务:

[tasks] bmc cover [options] bmc: mode bmc bmc: depth 10 cover: mode cover 

成功覆盖输出:

Reached cover statement at i_axi_fifo.f_full_to_empty in step 15. 

将count = ram_depth-1改为count = ram_depth将使cover不可达,展示工具对属性精度的敏感度。

后续进阶

VHDL

  1. 现代燕尾服:历史、设计与生产洞察
  2. 铣床改车床:金属工实操步骤
  3. 掌握 C# using 指令:命名空间导入、别名与静态导入全解析
  4. VHDL入门:构建你的第一个与门
  5. VHDL类型转换全攻略:Numeric_Std与Std_Logic_Arith包详解
  6. VHDL过程示例:标准逻辑向量自增
  7. 善用VHDL记录类型构建整洁可复用FIFO接口
  8. 掌握VHDL有符号与无符号类型:实战洞察
  9. VHDL变量详解:实用示例与可靠设计规则
  10. VHDL 字符串列表创建:最佳实践与示例
  11. 优雅结束 VHDL 测试平台仿真
  12. VHDL 实现 PWM 控制器:设计、仿真与 FPGA 演示