简介SpyGlass® LintRules Reference Guide 是 Synopsys 官方发布的静态分析工具参考手册版本 Q-2020.03-SP1面向从事 IC 设计验证的工程师、Verilog/VHDL 开发者及数字前端设计人员。它系统梳理了 SpyGlass lint 产品的全部规则集覆盖设计风格、时序分析、功耗优化、设计约束与接口检查等维度每条规则均给出规则 ID、目的描述、示例代码、修改方案及严重性等级帮助读者在设计早期定位语法错误、编码规范偏离与潜在缺陷。资源包为单一 PDF 文件约 2.04MB结构清晰、便于按规则编号检索查阅。目前已有 4457 人学习下载适合需要深入理解 lint 规则含义、自定义规则集或排查违规告警的工程师作为案头工具书使用也可为团队制定编码规范与验证流程提供权威依据。1. SpyGlass LintRules Reference 到底在查什么从一次 RTL 提交被打回说起凌晨两点提交的 RTL早上九点被前端负责人原样退回附言只有一句「SpyGlass 的 Lint 报告自己看」。打开报告几百条违例按规则名分组W164a、STARC05-2.1.4.1、NoAssignX-ML这些名字像天书一样堆在屏幕上。很多工程师第一次接触 SpyGlass LintRules Reference就是在这种被规则名淹没的场景里。它本质上是一份规则字典把 SpyGlass 在 RTL 静态检查阶段能报出的每一条 Lint 规则按类别、严重级别、触发条件、修复建议逐条列清楚。你不需要背下全部规则但必须知道怎么查、怎么筛、怎么把一条规则从「看不懂的编号」翻译成「我这段代码哪里写错了」。这份 Reference 适合三类人刚接手 SpyGlass 流程的验证/前端新人、需要给团队定制规则集的流程搭建者、以及被某条规则反复卡住想搞清楚边界的老手。下面按「规则怎么读 → 环境怎么搭 → 规则集怎么裁 → 违例怎么修 → 坑在哪」的顺序拆开讲。2. 读懂 LintRules Reference 的规则命名与分级体系2.1 规则编号背后的三套命名来源SpyGlass 的 Lint 规则不是一套统一编号而是混了三类来源这也是新手最容易懵的地方。第一类是 SpyGlass 原生规则形如W164a、W123W开头多为 WarningE开头为 Error数字是内部编号后缀字母表示同一规则的不同变体。第二类是 STARC 规则形如STARC05-2.1.4.1这是 Synopsys 主导的可综合设计规范编号对应规范文档的章节号可读性最好看到2.1.4.1基本能猜到是时钟或复位相关。第三类是方法论规则形如NoAssignX-ML、Latch-ML-ML后缀通常表示 Methodology Lint针对的是可综合性、可测性这类工程约束。读 Reference 时不要按编号顺序翻先按类别定位。常见类别有 Clock/Reset、Synthesis、Simulation、DFT、CDC 前置检查几大类。每一条规则在 Reference 里通常包含规则名、严重级别Fatal/Error/Warning/Info、触发描述、正例、反例、修复建议。真正有用的是反例和修复建议正例往往一笔带过。提示不同 SpyGlass 版本的规则集会有增删Reference 的版本号必须和工具版本对齐否则会出现「文档里有、工具不报」或反过来「工具报了、文档查不到」的情况。2.2 严重级别与 waiver 的优先级关系SpyGlass 的严重级别直接决定流程能不能往下走。Fatal 会中断当前 goalError 会让 goal 失败Warning 默认不阻断但会被计入报告Info 基本是提示。很多团队的做法是把 Warning 也设成阻断结果新人第一天就被几百条 Warning 淹没反而忽略了真正的 Error。这里有个优先级关系必须搞清楚waiver豁免的优先级高于规则本身的级别设置。也就是说一条规则即使被设成 Error只要命中了 waiver就不会报。waiver 的写法在 Reference 里通常不展开但它是规则集裁剪的核心手段。常见 waiver 形式有三种按规则名全局豁免、按模块路径豁免、按信号名豁免。全局豁免最危险容易把真问题一起放过按模块路径豁免最常用比如第三方 IP 不检查按信号名豁免适合处理跨时钟域这类已知例外。# SpyGlass waiver 文件示例按模块路径豁免 waive -rule W164a -module u_ddr_ctrl/* waive -rule STARC05-2.1.4.1 -module u_pll_wrapper # 按信号名豁免慎用容易漏掉同名的其他信号 waive -rule NoAssignX-ML -signal test_mode_sync上面三段 waiver 分别对应三种粒度。第一段用通配符豁免了整个 DDR 控制器子层次适合外购 IP第二段精确到某个模块适合已知的时钟例外第三段按信号名风险最高因为同名信号在不同层次可能语义不同。参数上-rule必须和 Reference 里的规则名完全一致大小写敏感-module支持*通配但不要滥用通配范围越大越容易掩盖真问题。2.3 从 Reference 反查违例的最小操作路径拿到一份违例报告最快的定位方式不是逐条读而是用规则名反查 Reference。假设报告里出现W164a操作路径是先在 Reference 的规则索引里搜W164a读到它的触发条件是「组合逻辑输出直接驱动时序单元时钟端」再看反例代码基本就能对上自己 RTL 里那行assign clk_gate en clk;。这一步的关键是不要跳过反例反例往往比描述更直观。如果报告里出现的是 STARC 编号比如STARC05-2.1.4.1直接按编号去 STARC 规范章节找通常比在 SpyGlass Reference 里翻更快因为 STARC 规范对每条规则的背景解释更完整。方法论规则如NoAssignX-ML则要看-ML后缀对应的检查意图X表示未知态传播NoAssignX就是禁止把含 X 的表达式直接赋值给输出。3. 搭一套能跑通 LintRules 检查的最小 SpyGlass 环境3.1 工程目录与必需输入文件SpyGlass 跑 Lint 不需要 testbench但需要一份完整的文件列表和顶层模块名。最小工程目录通常长这样rtl/放设计源码filelist.f列文件顺序spyglass.prj是工程配置waiver.tcl放豁免run/是输出目录。文件顺序很重要SpyGlass 按 filelist 顺序解析include文件必须排在引用它的模块之前否则会报模块未定义。# 最小运行命令-project 指定工程文件-goal 指定检查目标 spyglass -project spyglass.prj -goal lint/lint_rtl -batch # 查看可用 goal 列表 spyglass -project spyglass.prj -showgoals-goal lint/lint_rtl是最常用的 RTL Lint 目标-batch表示无图形界面批处理模式适合集成到 CI。-showgoals会列出当前工程所有可用 goal不同 license 和版本下 goal 名称可能不同跑之前先确认。如果命令直接报 license 错误先检查SPYGLASS_HOME和 license 环境变量这一步翻车的人不少。3.2 工程配置里必须显式设置的几个选项spyglass.prj里有一批选项如果不设跑出来的结果会和你预期差很远。下面这段是常见的最小配置。# spyglass.prj 关键配置 set_option top top_module set_option language_mode mixed set_option designread_enable_synthesis yes set_option enableSV yes read_file -type sourcelist filelist.f read_file -type waiver waiver.tcl set_option lint_rtl_severity_error {W164a STARC05-2.1.4.1}逐行说明top指定顶层模块必须和 RTL 里的模块名完全一致language_mode mixed允许 Verilog 和 SystemVerilog 混用designread_enable_synthesis yes让 SpyGlass 按可综合语义解析这对 Lint 很关键否则会把一些可综合写法误判enableSV yes打开 SystemVerilog 支持read_file两行分别读入文件列表和 waiver最后一行把两条规则提升为 Error 级别覆盖默认的 Warning。参数上lint_rtl_severity_error接受规则名列表空格分隔不要加引号包整串。3.3 第一次跑通后先看哪几个报告文件跑完后run/目录下会生成一堆文件新手容易迷路。真正要先看的是三个spyglass.log记录解析和运行日志模块未定义、文件顺序错误都在这里lint_rtl/spyglass_reports/lint/lint_rtl.rpt是违例汇总按规则分组lint_rtl/spyglass_reports/lint/lint_rtl_summary.rpt是统计摘要看每类规则报了多少条。先看 summary 判断量级再看 rpt 定位具体行号最后回 log 排查解析问题。顺序反了会在几百条违例里浪费时间。4. 规则集裁剪把几千条违例压到能修的数量4.1 按项目阶段选择 goal 而不是全开SpyGlass 的 Lint goal 有很多细分全开必然爆炸。常见做法是按阶段选RTL 刚写完只跑lint/lint_rtl基础集重点看语法和可综合性功能稳定后加lint/lint_functional_rtl看仿真相关接近综合时加lint/lint_synthesis看综合约束。每加一个 goal违例量会上升一个量级所以不要一次性全开。# 分阶段 goal 配置按需启用 current_goal lint/lint_rtl -top top_module # 功能稳定后再启用下面这行 # current_goal lint/lint_functional_rtl -top top_modulecurrent_goal指定当前激活的 goal注释掉的行表示暂不启用。这样做的价值是让团队先消化基础违例再逐步加严避免一上来就被几千条吓退。参数上-top可以覆盖工程级的 top 设置适合多顶层工程。4.2 用 severity 覆盖把噪音规则降级Reference 里每条规则都有默认级别但默认级别不一定适合你的项目。比如W123默认 Warning如果你的设计里大量使用某种合法但会触发它的写法可以把它降成 Info让它出现在报告里但不阻断。反过来某些默认 Warning 的规则在你项目里是硬约束就提升成 Error。# 降级噪音规则升级关键规则 set_option lint_rtl_severity_info {W123 W280} set_option lint_rtl_severity_error {NoLatch-ML NoAssignX-ML}第一行把W123、W280降为 Info第二行把两条方法论规则升为 Error。注意 severity 覆盖是全局的如果只想对某个模块生效得配合 waiver 而不是 severity。参数上规则名必须和 Reference 完全一致写错了不会报错但也不生效这是隐蔽的坑。4.3 waiver 的三种粒度和维护策略waiver 是规则集裁剪的主力但也是最容易失控的地方。三种粒度前面提过全局、模块路径、信号名。维护策略上我一般要求每条 waiver 必须带注释说明原因和负责人否则半年后没人知道为什么豁免。waiver 文件建议按模块拆成多个用read_file依次读入而不是堆在一个大文件里。# waiver 按模块拆分每条带注释 # owner: zhangsan, date: 2024-05, reason: 外购 DDR IP 不检查 waive -rule W164a -module u_ddr_ctrl/* # owner: lisi, date: 2024-06, reason: PLL 输出时钟已知例外 waive -rule STARC05-2.1.4.1 -module u_pll_wrapper注释里的 owner 和 date 不是形式主义是出问题时能找到人。参数上-module的路径要写全层次从 top 往下通配符只放在末尾中间用通配符容易误伤。5. 常见违例的修复套路与避坑记录5.1 时钟复位类违例W164a 与 STARC05 系列W164a的典型触发是组合逻辑直接驱动时钟比如门控时钟没走标准单元。修复方式是用工艺库提供的集成时钟门控单元ICG而不是自己用与门搭。STARC05-2.1.4.1通常和复位同步有关要求异步复位同步释放修复是加两级同步器。// 反例组合逻辑直接驱动时钟 assign gated_clk en clk; // 正例使用 ICG 单元 CLKICG u_icg (.CP(clk), .E(en), .Q(gated_clk));反例里en如果有毛刺会直接传到时钟上造成时序单元误触发。正例用库单元内部已经做了毛刺过滤。参数上 ICG 单元的端口名因工艺库而异用之前查库手册。5.2 可综合性违例Latch 推断与 X 传播Latch-ML报的是组合逻辑里推断出锁存器常见原因是if或case分支不完整。修复是补全else或default或者把信号在分支前赋默认值。NoAssignX-ML报的是含 X 的表达式直接赋值修复是加case的default分支或初始化。// 反例分支不完整推断 Latch always (*) begin if (sel) q a; // 缺少 elseq 在 sel0 时保持推断 Latch end // 正例补默认值 always (*) begin q 1b0; if (sel) q a; end正例在 always 块开头给q赋默认值这样即使if不成立q也有确定值不会推断 Latch。参数上默认值要和信号位宽一致否则会报位宽不匹配。5.3 避坑记录五条血泪经验现象一报告里规则名查不到。原因工具版本和 Reference 版本不一致或者规则被 waiver 掉了但报告没刷新。解决先确认版本对齐再检查 waiver 是否误命中最后清空run/重跑。现象二模块未定义错误刷屏。原因filelist 顺序错误include文件排在引用之后或者漏了某个子模块文件。解决按依赖顺序重排 filelistinclude文件置顶用-showgoals前的解析日志确认。现象三waiver 写了不生效。原因规则名大小写不一致或-module路径层次写错。解决从报告里直接复制规则名模块路径从 top 逐层核对通配符只放末尾。现象四severity 覆盖后违例还在。原因severity 覆盖和 waiver 冲突waiver 优先级更高或者覆盖写在了 goal 之后。解决把 severity 设置放在current_goal之前检查是否有 waiver 命中。现象五CI 里跑结果和本地不一致。原因CI 环境的 SpyGlass 版本、license、环境变量和本地不同。解决在 CI 脚本里显式打印版本和SPYGLASS_HOME用同一份工程文件和 waiver避免本地缓存干扰。6. 把 LintRules 检查嵌进 CI 并做趋势管理规则集稳定后下一步是让它自动跑。CI 里跑 SpyGlass 的关键是批处理模式和退出码判断。spyglass -batch跑完后如果 goal 失败会返回非零退出码CI 据此判断是否阻断合并。但要注意Warning 默认不阻断所以要么把关键规则升成 Error要么在 CI 脚本里解析报告统计 Error 数量。#!/bin/bash # CI 脚本片段跑 Lint 并判断结果 spyglass -project spyglass.prj -goal lint/lint_rtl -batch if [ $? -ne 0 ]; then echo Lint goal failed, check reports exit 1 fi # 统计 Error 数量超过阈值阻断 error_count$(grep -c Error run/lint_rtl/spyglass_reports/lint/lint_rtl_summary.rpt) if [ $error_count -gt 0 ]; then echo Found $error_count errors exit 1 fi脚本里$?判断 goal 是否失败grep -c统计 summary 里的 Error 行数。参数上阈值可以根据项目阶段调整初期可以放宽后期收紧。趋势管理上我习惯每次 CI 跑完把 summary 归档按周对比违例数量如果某类规则突然上升说明最近提交引入了新问题能提前发现。一个具体技巧是给每条规则建一个「修复成本」标签高成本低风险的规则先 waiver低成本高风险的优先修。这样在有限时间里把精力花在刀刃上。我自己踩过的最大坑是一开始追求零违例结果在低风险规则上耗了两周真正的高风险问题反而没时间看。后来改成按风险排序先修时钟复位和可综合性噪音规则统一 waiver效率高了很多。希望帮到你。本文还有配套的精品资源点击获取