简介OVLOpen Verification Library开放验证库std_ovl_v2p7版压缩包为VHDL/Verilog硬件设计及验证工程师提供大量可直接调用的ASSERTION断言模块可有效提升数字逻辑设计的可验证性尤其适合基于AXI总线协议的系统级验证场景。包内共439个文件131个Verilog源文件.v与99个VHDL源文件.vhd可用于两种主流HDL工程直接集成66个PSL断言文件与65个SystemVerilog文件.sv辅助高级断言与验证环境搭建33个版本库文件.vlib便于版本管理另附14个PDF说明文档、makefile构建脚本及少量辅助列表整体打包为4.79MB的ZIP压缩包目录结构清晰便于定位所需模块。读者可按设计需求挑选适配的断言模块集成到FPGA工程中对信号时序与协议交互进行自动化监测从而在设计阶段提前发现隐患、减少调试耗时特别对于采用AXI协议通信的FPGA设计可借助这些断言自动检查读写通道时序与握手信号是否符合规范显著降低人工核对成本加快验证收敛进程。已有239人学习下载适合需要提升验证效率的FPGA/ASIC设计开发者。1. 一个断言库能省掉多少FPGA调试时间当仿真跑完几十毫秒波形里翻出那一小段异常时往往要花很久才能定位。实际上在 RTL 里提前埋好断言很多问题可以在仿真阶段就爆出来。OVLOpen Verification Library是 Accellera 维护的一套参数化断言模块std_ovl_v2p7_Feb2013.zip 就是 2013 年 2 月放出的 v2.7 版本同时提供 VHDL 和 Verilog 实现。这类库不是新语言而是一组可以直接例化的模块比如 assert_always、assert_frame、assert_never插进 FPGA 设计里就能监视信号时序。它特别适合做图像处理、异步 FIFO、TDC 直方图这类需要严格时序控制的模块。下面默认你已经在用 ModelSim 或 Vivado 做 FPGA 仿真并准备把 OVL 用起来。2. 解包与库编译std_ovl_v2p7_Feb2013.zip 的 VHDL 和 Verilog 两条路先讲库的获取。压缩包解压后切成两部分VHDL 库和 Verilog 库。OVL v2.7 的顶层目录一般包括 ovl_vhdl、ovl_verilog、docs 和 examples。如果要同时支持 VHDL 和 Verilog 混合仿真两边都要编译不能偷懒只编一份否则在跨语言例化时会报 failed to find module 一类的错误。2.1 压缩包目录里你能找到什么下面是常见的目录结构不同平台解包略有差异但主体相同。可以先 unzip 解压然后 ls 确认。std_ovl_v2p7_Feb2013/ ├── ovl_vhdl/ │ ├── std_ovl.vhd │ ├── std_ovl_comp.vhd │ └── std_ovl_procs.vhd ├── ovl_verilog/ │ ├── std_ovl.v │ ├── std_ovl_macros.vh │ └── std_ovl_tasks.vh ├── docs/ │ ├── OVL_Checkers_VHDL.pdf │ └── OVL_Checkers_Verilog.pdf └── examples/ └── vhdl_verilog_mixed/这些文件的职责很清楚std_ovl.vhd是断言模块的实体和结构体std_ovl_comp.vhd是组件声明包std_ovl_procs.vhd是底层过程实现Verilog 侧的.v和.vh对应模块与宏。你真正需要编译进工程的是前三个 VHDL 文件或者对应的 Verilog 文件组。提示VHDL 和 Verilog 版本不要混用。同一个设计里VHDL 顶层例化 VHDL 版 OVLVerilog 顶层例化 Verilog 版混合仿真时再通过库映射让两边互相可见。2.2 用 ModelSim 编译 VHDL 版 OVL 库ModelSim 下的做法是用 vlib 建一个专门存放 OVL 的库然后按依赖顺序编译。常见命令如下vlib ovl_vhdl_lib vmap work ovl_vhdl_lib vcom -work ovl_vhdl_lib ovl_vhdl/std_ovl.vhd vcom -work ovl_vhdl_lib ovl_vhdl/std_ovl_comp.vhd vcom -work ovl_vhdl_lib ovl_vhdl/std_ovl_procs.vhd这里 vlib 是建立物理库目录vmap 把逻辑库名 work 或者你自己起的名字映射到该目录vcom 是 VHDL 编译器。编译顺序上std_ovl.vhd 里通常包含若干实现定义的数据类型和常量必须在 std_ovl_comp.vhd 之前编译如果反过来会出现 component not declared 的错误。std_ovl_procs.vhd 是底层实现依赖前两者放最后。参数上ModelSim 默认按 VHDL-93 处理OVL v2.7 的源码已经兼容 VHDL-93/2002/2008如果你的工程标准是 VHDL-2008可以在 vcom 后加-2008选项强制编译器按 2008 标准解析避免个别event属性歧义。我建议从一开始就在编译脚本里把-2008写死因为后面的设计代码很可能会用到 VHDL-2008 的端口特性。2.3 用 Vivado 的 xvhdl 和 xelab 编译混合库Vivado 的 XSim 环境则不同它用 xvhdl 和 xelab。先编译 VHDL 库再编译 Verilog 库最后 xelab 时才把两个逻辑库映射进仿真视角。示例xvhdl -work ovl_vhdl ovl_vhdl/std_ovl.vhd ovl_vhdl/std_ovl_comp.vhd ovl_vhdl/std_ovl_procs.vhd xvhdl -work work fpga_top.vhd xelab -debug typical fpga_top -L ovl_vhdl -L work第一条命令把所有 OVL VHDL 文件一起编译到 ovl_vhdl 逻辑库因为 xvhdl 会对多个文件按顺序编译-work指定目标库。第二条编译你的 RTL 到 work 库。第三条 xelab 进行全局解析-L ovl_vhdl告诉连接器若要找的模块不在 work 里就去 ovl_vhdl 找。参数-debug typical会保留信号波形信息否则后面仿真可能无法跟踪 OVL 内部信号。这里我习惯把 OVL 单独编到一个预编译库之后每个工程只做-L引用能省掉重复编译时间。编译期最常见的问题是 work 库和 ovl_vhdl 库都映射到同一物理目录导致文件锁冲突。解决办法是分别建目录不要把两个逻辑库指向同一个路径。如果你在编译时看到红色 error 里有library work is not mapped先检查 vmap 的输出确认映射关系是否被后面命令覆盖了。3. VHDL 里接入 OVL 断言从 vunit 到 assert_always这一章重点讲 VHDL 语言侧的例化。OVL 在 VHDL 里都是以实体形式提供的每个断言一个实体端口基本固定。你需要先 use work.std_ovl.all或者直接全名例化。具体语法在不同版本略有差异下面以 v2.7 常见接口为准。3.1 OVL 断言模块的 VHDL 接口以 assert_always 为例它的端口如下entity std_ovl_assert_always is generic ( severity_level : severity_level : error; msg : string : OVL ERROR; num_cks : integer : 1 ); port ( clock : in std_logic; reset : in std_logic; enable : in std_logic : 1; test_expr : in std_logic; fire : out std_logic ); end entity;这里的 clock 和 reset 是全局时钟和复位test_expr 是需要被持续验证的条件。enable 高电平有效为低时断言被强制关闭。num_cks 指定条件必须满足多少个时钟周期才通过默认 1 就是每个周期都要成立。severity_level 决定失败时仿真器报的是 note、warning、error 还是 failure。FPGA 功能验证阶段我一般用 error让仿真器继续跑以便收集更多问题到了系统集成阶段改成 failure让仿真在关键断言失败时立刻停下。注意如果你把 reset 接到了高电平断言在整个仿真中会被抑制这是新手最容易踩的坑。OVL 的复位默认高有效接到低有效 rst_n 时需要取反或者增加复位极性参数。3.2 VHDL 中 OVL 常用参数表下面列出几个最常用断言和它们的关键参数也是我在 FPGA 项目里最先用的一批。断言模块用途关键参数典型场景assert_always条件每个周期必须成立test_expr, num_cks状态机不会进入非法态assert_never指定条件下事件不能发生test_expr, start_event握手信号不能出现毛刺assert_frame事件之间的周期数必须在区间内start_event, test_expr, min_cks, max_cksFIFO 读写间隔限制assert_change信号电平只能在指定周期内变化一次test_expr, num_cks数据总线在采样窗口内必须稳定表中的 min_cks、max_cks 都是整数单位为时钟周期。在 VHDL 实现里它们都是generic在 Verilog 里则对应parameter含义一一对应。这个表可以直接当速查手册用后面第 5 章会结合场景展开。3.3 在 FPGA RTL 中插入断言的完整实现下面是一个完整实例在一个图像处理模块里检测行有效信号 line_valid 与数据有效信号 data_valid 之间的固定延迟。假设要求 data_valid 必须在 line_valid 拉高后的 2 到 4 个时钟周期内出现否则报错。-- 常量定义符合 VHDL 语言中常量的定义方式 constant MAX_DELAY : integer : 4; u_frame : entity work.std_ovl_assert_frame generic map ( severity_level error, msg data_valid timing violation, min_cks 2, max_cks MAX_DELAY ) port map ( clock clk, reset rst_n, enable 1, start_event line_valid, test_expr data_valid );逻辑说明start_event 是起始事件检测到 line_valid 上升沿后开始计数要求在 [min_cks, max_cks] 区间内 test_expr 为高。如果超出窗口还未拉高断言触发 error。这里 rst_n 是低有效复位OVL 内部会统一处理复位极性。使用entity work.std_ovl_assert_frame的全名例化方式可以不依赖组件包。MAX_DELAY 用常量定义方便全局修改这比直接写数字更利于后期时序收敛时的参数调整。从 RTL 风格上讲OVL 断言模块可以放在单独的 generate 区域里用 if 条件控制是否编译。比如if SIMULATION true generate包裹断言例化。这样综合时如果 SIMULATION 为 false这些逻辑自然被裁掉不会影响 FPGA 的资源占用。4. Verilog 和 VHDL 混用 OVLFPGA 仿真中的库映射与编译顺序FPGA 项目发展到一定阶段会出现 VHDL 块和 Verilog 块并存的情况。很多 IP 核是 Verilog 的而自研的算法模块用 VHDL这时候在 Verilog 里例化 VHDL 版本的 OVL 断言就是一个实际需求。4.1 为什么 FPGA 项目会出现混合语言验证常见原因有三个第一公司积累的 VHDL 模块不想重写新来的团队用 Verilog 写测试第二Xilinx 的某些接口 IP 只输出 Verilog 或加密的 VHDL第三验证环境往往用 SystemVerilog而 DUT 是 VHDL。OVL 官方提供了两种语言版本就是为了在这种情况下让断言模块能以统一的方式插入不同语言的 RTL。不需要把整个设计改成单一语言只要能通过库映射互相引用就能在任意语言里例化断言。我遇到过不少团队在混合仿真初期卡住最后发现只是编译顺序或库映射的问题。4.2 Verilog 顶层实例化 OVL VHDL 模块在 Verilog 的顶层模块里使用库名前缀直接例化 VHDL 的 OVL 实体module fpga_top ( input wire clk, input wire rst_n, input wire req, input wire ack ); // VHDL OVL assert_never 的 Verilog 实例化 ovl_vhdl_lib.std_ovl_assert_never #( .severity_level(ERROR), .msg(ack glitch detected) ) u_assert_ack ( .clock (clk), .reset (rst_n), .enable(1b1), .test_expr(ack), .start_event(req) ); endmodule注意在 Verilog 里模块名写成ovl_vhdl_lib.std_ovl_assert_never其中 ovl_vhdl_lib 是前面用 vmap 或 -L 定义的逻辑库名字。参数映射用#(...)字符串参数写双引号VHDL 的 severity_level 类型是枚举在 Verilog 侧用字符串 ERROR 对应。reset 的极性要留意OVL 内部默认高有效复位如果你把 rst_n 直接传进去需要取反或者配置 reset_polarity 参数。很多 OVL 版本支持这个参数不写就默认高电平复位。这里为了减少歧义我会在 VHDL 侧包一层取反逻辑或者干脆在顶层把 reset 连接写成~rst_n。4.3 编译顺序、库映射和 vmap 参数说明混合仿真时编译顺序比单语言要严格。下表列出我常用的顺序步骤操作说明1vlib ovl_vhdl_lib建 VHDL 库2vcom -work ovl_vhdl_lib 编译全部 OVL VHDL必须先编译底层实现3vlib ovl_verilog_lib建 Verilog 库4vlog -work ovl_verilog_lib 编译 OVL Verilog如果后续要 Verilog 例化5vcom -work work VHDL 设计文件编译 RTL6vlog -work work Verilog 设计文件编译顶层7vsim work.fpga_top -L ovl_vhdl_lib -L ovl_verilog_lib仿真并映射两个库第 7 步至关重要vsim 命令必须用 -L 把两个 OVL 库都加到搜索路径中否则解析到 ovl_vhdl_lib.std_ovl_assert_never 时找不到模块。很多同事漏了这一步结果仿真启动时直接报 failed to find module。在使用 Vivado XSim 时对应命令是xelab -L ovl_vhdl_lib -L ovl_verilog_lib参数顺序不要反-L 必须出现在待仿真模块名之前。混合语言的 OVL 断言在编译期一般不会报错问题几乎都集中在链接阶段。可以先跑一个小例子验证库映射把 assert_always 的 test_expr 接成常数 1如果断言不触发说明库没生效。5. 用 OVL 检查 FPGA 常见 Bug计数器、FIFO、握手信号前面讲的是接入方式这一章用三类实际场景说明 OVL 的具体价值。每类场景对应不同的断言模块也都是快速验证里最常用的写法。5.1 用 assert_frame 检查异步 FIFO 的读写指针间隔异步 FIFO 的读写指针属于跨时钟域信号格雷码转换后不能直接比较。常见要求是写指针推进后读指针至少延迟 N 拍才能看到变化否则说明同步链没打够。可以用 assert_frame 来限定两个事件的间隔u_fifo_ptr : entity work.std_ovl_assert_frame generic map ( msg FIFO pointer read side too early, min_cks 3, max_cks 100 ) port map ( clock rd_clk, reset rd_rst_n, enable write_en, start_event wptr_gray_changed, test_expr rptr_gray_changed );这里的 rd_clk 是读时钟域wptr_gray_changed 是写指针格雷码变化的脉冲rptr_gray_changed 是读指针格雷码变化的脉冲。因为两级同步器至少需要 2 拍所以 min_cks 设 3上限给 100 是为了避免仿真结束前断言一直不满足实际中应根据 FIFO 深度算一个保守上限。参数 enable 接 write_en只在写入活跃时检查减少误报。如果断言经常触发优先检查同步器的级数而不是怀疑 OVL 配置。5.2 用 assert_never 检查握手信号的毛刺Verilog 开发里握手信号最常见的 bug 是组合逻辑毛刺。assert_never 可以在指定事件后断言目标信号不能再次跳变。如下ovl_lib.std_ovl_assert_never #( .severity_level(ERROR), .msg(handshake req glitch), .num_cks(2) ) u_handshake_check ( .clock(clk), .reset(rst), .enable(1b1), .start_event(valid), .test_expr(req) );这里 start_event 是 valid 的上升沿test_expr 是 req。当 valid 有效后req 在 num_cks2 个周期内不能变低再变高否则断言触发。这个检查对跨模块的组合路径非常有效但注意 OVL 的 start_event 默认是边沿敏感如果你的设计中 valid 是高电平需要先取一个脉冲信号否则会一直触发。也可以看 OVL 手册部分版本支持 check_type 参数切换边沿或电平模式。表OVL 常用断言与 FPGA 场景对照断言用于检查FPGA 常见场景assert_always恒成立条件状态机非法态、读写使能不重叠assert_never不应发生的事件握手毛刺、时钟域亚稳态传递assert_frame两个事件间隔TDC 直方图采集窗口、FIFO 指针同步assert_change信号稳定性数据总线建立保持时间5.3 把断言放进 testbench 的层次引用有时候不想改 RTL直接在 testbench 里用层次路径加断言对灵活验证很有用。在 VHDL testbench 中可以这样-- 在testbench中引用DUT内部信号 u_dut_assert : entity work.std_ovl_assert_always generic map ( severity_level error, msg state machine illegal state, num_cks 1 ) port map ( clock tb_clk, reset tb_rst, enable tb_enable, test_expr dut_instance.fsm_state / IDLE );dut_instance.fsm_state 是层次化路径VHDL-2008 可以直接写早期版本需要用\转义或使用别名。这样不用改动设计文件只在仿真环境里生效。综合时 testbench 不会被综合所以不存在资源问题。层次引用要注意名字必须与编译后网表中的信号名完全一致否则仿真器报 signal not found。用 ModelSim 的 find signals 功能检查路径是节省时间的做法。断言加进 testbench 后整套回归用例的观测点就清晰很多不用每次在波形里从头找。6. 综合时保留 OVL在 FPGA 里跑硬件断言监视器OVL 不仅限于仿真常见做法是综合时把部分断言保留成硬件逻辑用 LED 或 JTAG 读出故障信号。这对在线调试很有用。6.1 哪些断言能综合以及综合属性不是所有 OVL 模块都能综合。像 assert_time 这种涉及仿真时间抽象的断言就不能综合。可综合的主要是 assert_always、assert_never、assert_change 等基于时钟计数和状态检测的检查。综合时需要加(* keep true *)属性防止优化器把断言逻辑裁掉。在 VHDL 中对应 attribute keep 声明例化前加上attribute keep of u_always : label is true;。没有这个属性综合工具很可能把只有内部连接而没有落到输出的逻辑优化为空。6.2 一个可以被综合的在线监视实例举一个简单例子用 assert_always 监视 FPGA 内部的 fifo_overflow 信号一旦出现拉高就长置位 error_flagu_hw_check : entity work.std_ovl_assert_always generic map ( severity_level error, msg FIFO overflow detected, num_cks 1 ) port map ( clock clk, reset rst_n, enable 1, test_expr not fifo_overflow );综合后OVL 实体里的比较器和计数器会保留在网表中。这里的 fire 输出没有引到顶层所以需要 attribute keep 把它保留。在 Vivado 中综合后检查是否保留的方法是打开 synthesized design在网表里搜索u_hw_check/fire是否存在或者用原理图界面查看。仿真里可以强制拉高 fifo_overflow 看 fire 是否置位从而验证断言逻辑没有在综合中被优化掉。6.3 验证断言是否真的进入网表最简单的方法是把 fire 引到顶层输出然后加一个计数器扩展 LED。综合后的逻辑占用通常只有几个 LUT对于 FPGA 来说可以忽略。如果你用的是 Vivado还可以用 xsim 做行为仿真和综合后仿真对比看断言触发时间是否一致。这一步能同时验证 OVL 在综合阶段没有被优化成恒 0。注意OVL 的 VHDL 实现里可能使用 assert 语句这本身不可综合但不会影响综合结果只是仿真时用来报告。所以不用刻意删掉保留这些语句反而能在综合后仿真里继续看到错误消息。本文还有配套的精品资源点击获取
