RTL 静态检查:在仿真和综合之前就把结构性错误拦住(B1)
设计侧 lint/waiver 签核方法见 Lint 与 SpyGlass waiver,FPGA 视角见 RTL lint(FPGA);本篇讲 ASIC signoff 级 lint 工具(Synopsys SpyGlass、开源 Verilator lint)的规则体系与违例清零治理。CDC/RDC 是独立专项(B2),本篇讲通用结构/质量规则。
一、Lint 为什么不等于编译器 warning
仿真器和综合器也会报 warning,但目标不同:仿真器为了「跑起来」会容忍很多写法,综合器为了「出网表」会默默推断(infer)出你可能没想要的硬件。Lint 不生成网表、不跑激励,只读 RTL 结构,专门找「语法合法、仿真可能通过、但在硬件上危险或不可综合」的写法:
- 仿真器眼里的 warning 是顺带提示,lint 是按一整套可综合性 + 设计质量 + 跨工具一致性规则逐项查;
- 很多致命问题(组合环、隐式锁存器、多驱动、复位不一致)在 RTL 仿真里因为 0/1 理想模型根本不发作,到后端/硅片才爆。
正因为不用激励、跑得快,lint 应该是最早、每次合入都跑的一道门(S1 RTL 数据 的 Definition of Done)。

二、规则族:工具到底在查什么
| 规则族 | 典型违例 | 危害 |
|---|---|---|
| 可综合性 | 延迟语句、initial 边沿之外、fork-join_none、动态结构 | 仿真过、综合器拒绝或推断异常 |
| 位宽/符号 | 位宽不匹配截断、有符号/无符号混用 | 静默丢高位、比较结果错 |
| 组合逻辑 | 组合反馈环、always @(*) 敏感表不全 | 振荡、仿真与综合不一致 |
| 锁存器推断 | if 无 else / case 不全且未赋默认 | 推断出隐式 latch,时序/DFT 麻烦 |
| 多驱动 | 同一 reg 多 always 驱动 | 总线冲突、不可综合 |
| 复位一致性 | 部分寄存器有复位、同步/异步混用、复位值缺失 | 复位后状态不定(与 RDC 联动) |
| 电平敏感/三态 | 内部三态、case 优先级缺失 | 内部三态难实现、优先级与意图不符 |
| 命名/可读性 | 残留 tmp、参数硬编码 | 维护性(团队自定义规则) |
SpyGlass 通常按 goal/方法学分包(lint/lint_rtl、CDC、RDC、DFT、power),同一套引擎不同规则集;Verilator 开源、用 -Wall -Wno-... + 内联 waiver,适合本地/CI 早筛,但不能替代 foundry/团队认证的 signoff lint。
三、海量违例怎么治理:不是逐条消音
老代码首次跑 lint 常是几千上万条,硬删不可行,放任则等于没跑。成熟做法是基线 + 分级 + waiver 三结合:
- 冻结基线(baseline):把存量违例快照为基线,门禁只卡「相对基线新增」,让存量不再增长、新代码必须干净;
- 按严重度分级:fatal/error(必须修:组合环、多驱动、隐式 latch)、warning(评审)、info(提示)。先清 fatal/error,warning 按模块排期;
- 结构化 waiver:确实可接受的(如故意的非复位配置寄存器、已知第三方 IP)走 waiver,标注规则名、位置、原因、责任人、失效条件;
- waiver 集中管理:放版本控制的 waiver 文件(SpyGlass
.wai、Verilator/* verilator lint_off ... */或配置),而不是散落在 RTL 里,便于审计和随代码/工具版本重审。
waiver 纪律与 CDC、签核 waiver 完全一致:不能把「没看懂的报错」waive 掉;工具/RTL 版本升级后要回归,确认旧 waiver 仍成立。设计侧的签核口径见 lint waiver 签核。
四、消除 vs 修 RTL:常见修法
| 违例 | 正解(改 RTL) | 错误做法 |
|---|---|---|
| 隐式 latch | 组合逻辑开头给默认赋值 / 补全 else | 直接 waiver |
| 位宽截断 | 显式位宽、对齐符号、必要时显式 cast | +define 全局关位宽规则 |
| 组合环 | 断开反馈、在环里插入寄存器 | 当工具误报 waive |
| 复位不一致 | 统一复位策略(哪些必须复位成文) | 每个文件各写各的 |
| 不可综合 | 用可综合等价写法,测试专用代码包在综合宏里 | 依赖综合器「帮我处理」 |
关键原则:优先改 RTL 让规则通过,waiver 只留给「规则与设计意图确有正当冲突」的少数。一个团队 lint 文化的好坏,看的是 waiver 率和 waiver 质量,不是看报告是否「清零」——清零可以靠全关规则造假。
五、把 lint 接进门禁
- 本地/CI:Verilator lint 或 SpyGlass 快速 goal 在每次提交/PR 跑,秒级反馈「不允许新增 fatal/error」;
- 里程碑:综合前跑完整 signoff goal(含 CDC/RDC/DFT/power),出基线对比报告,fatal/error 为零、warning 只降不升、waiver 全审计;
- 工具/规则版本锁定:lint 规则集和工具版本纳入基线(呼应 F10 版本),升级工具会带来新规则、需重新评审 waiver。
lint、CDC/RDC、LEC 共同构成「不需要激励的静态签核」三件套,都必须在综合前干净——问题越往后拖,修一个的代价越呈数量级上升(X3 变更传播)。
相关与延伸
- 静态族:B2 CDC/RDC、B3 形式属性、B4 LEC;流程:C1 综合。
- 方法/设计侧:lint waiver 签核、RTL 编码风格、FPGA lint。
- 数据:S1 RTL 数据。
总结
Lint 是不跑激励、在综合前专门拦截「语法合法但硬件危险/不可综合」写法的静态门,覆盖可综合性、位宽、组合环、隐式锁存、多驱动、复位一致性等规则族;面对首次跑出的海量违例,正确姿势不是逐条消音而是冻结存量基线、只卡新增、按 fatal/warning/info 分级先清硬伤、把确有正当理由的少数做成带规则名位置原因责任人与失效条件并纳入版本管理的 waiver,优先改 RTL 而非关规则;再用本地 CI 快速 goal 加里程碑完整 signoff goal 两层门禁、锁定工具与规则版本,让 lint 与 CDC/RDC、LEC 一起在综合前干净——waiver 率和 waiver 质量才是 lint 文化的真正指标。