《Verilog and SystemVerilog gotchas》笔记
Posted on 2021-04-25 22:13 in IC
Note
- 这本书《Verilog and SystemVerilog gotchas》非常老,第一作者 Sutherland 已经去世了。虽然有对应的中文翻译版本,但是翻译和校对的质量很差,语句不同顺,笔误满天飞,可能看英文版理解起来更通顺一些
- 只记录了 Design 相关的陷阱,Verfication 相关的陷阱没有记录
关于作者
Stuart Sutherland 是 Verilog 和 SV 标准的起草者,也是业界培训大佬,写了很多著名的书和培训教程。
Q:什么是陷阱 Gotcha?
A:符合语法规则,但是结果并不符合预期,比如常见的 C 语言中 if(a=b) 把比较写成了赋值。
Verilog/SV 产生陷阱的原因:
- 继承自 C/C++ 的陷阱
- 松散的类型操作,即操作符可以执行任意类型的数据
- 允许良莠不齐的模型设计(可综合 and 不可综合)
Verilog 的标准:
- Verilog-1995 (IEEE Std 1364-1995)
- Verilog-2001 (IEEE Std 1364-2001)
- Verilog-2005 (IEEE Std 1364-2005)
SV 的标准:
- IEEE Std 1800-2005
- IEEE Std 1800-2009(合并 IEEE 1364-2005 和 IEEE 1800-2005)
- IEEE Std 1800-2012
声明以及字符表述类陷阱
| 陷阱 | 如何避免 | 
|---|---|
| VHDL 大小写不敏感,而 V/SV 大小写敏感 | 良好的 coding style | 
| 笔误和忘记声明会自动推断出隐式 wire,可能导致功能错误 | 高级编辑器 / 使用 .name或.*方式连接端口 | 
| 自动推断出的内部隐式 wire 只有 1bit,可能导致位宽不匹配 | 工具会报端口位宽不匹配 | 
| 多个文件使用 $unit 一起综合可能会导致命名冲突 | 使用 package 代替 $unit | 
| var 可以在任何地方声明,但是必须先声明后使用 | coding style 约束所有声明放在最前面 | 
| 枚举类型 import 并不能导入 label | 明确导入每个 label / 使用 *通配符导入 | 
| 使用通配符 *导入多个 package 可能出现命名冲突 | 使用显式的 import 导入 | 
| case 的数据类型(进制、符号)不匹配时可能产生功能错误 | 某些场景下可以使用 unique case | 
| 带 base 的常数默认是 unsigned,不带 base 的常数默认是 signed | 小心使用 | 
| 定义常数时位宽范围不正确,截断 / 填充规则可能不符合预期 | lint 工具检查 / 写代码时明确匹配位宽和数值 | 
| 位宽不匹配的常数赋值语句,截断 / 扩展规则取决于等号右侧常数的类型,可能和预期不符 | 小心使用 | 
| data = 'b1并不能设置 data 为全 1 | 使用 SV 语法 data = '1 | 
| 笔误可能导致 {}和'{}混淆使用,不会有语法问题但是功能不正确 | 小心使用 / lint 工具 | 
| module 端口位宽不匹配可能导致出错 | 注意工具 warning / 使用 .name或.*方式连接端口 | 
| input/output 端口可以反向使用而且不会报错 | 使用 logic 代替所有的 wire/reg | 
| real 类型无法作为端口传递 | Verilog 必须使用系统函数 / SV 需要特别声明 | 
RTL 建模中的陷阱
| 陷阱 | 如何避免 | 
|---|---|
| always *可能推断不出所调用的 function 用到的所有信号 | 使用 always_comb | 
| array 如何添加到敏感列表中? | 使用 always_comb | 
| vector 和 posedge/negedge 配合时,只会处理 LSB | 避免使用,如果一定要用 vector 必须先转化成 1bit 信号 | 
| posedge/negedge 只被敏感列表中表达式的结果触发,不受操作数的影响 | 使用 always_comb | 
| 不恰当的 begin...end 可能导致时序逻辑功能错误 | always_ff不需要 begin...end,只有内部 if...else 需要 | 
| 有些信号可能在 reset 块中会被不小心漏掉,导致功能错误 | lint、coverage 等工具报错 | 
| 时序逻辑误用阻塞赋值,组合逻辑误用非阻塞赋值 | lint 工具报错 / always_comb、always_ff | 
| 组合逻辑块中语句的顺序错误可能导致仿真和实际硬件行为不一致 | 写代码时小心 | 
| parallel_case使用不当导致仿真和硬件行为不一致 | 增加 default 分支 / unique case、priority case | 
| 粘贴复制导致的重复条件分支,只会执行第一个分支,导致与设计意图不符 | lint 工具 / unique case | 
| 不恰当使用 unique导致功能不正确 | 关注 warning | 
| 使用 2-state 信号建模可能导致仿真出错 | 避免使用 2-state 信号 / 使用 always_comb,always_ff | 
| 内部 X 态可能并不会传播到端口上,导致错误未被发现 | 使用 SVA | 
| Verilog 允许 net 多驱动,可能出现功能错误 | lint 工具 / 使用 SV 的 always_comb | 
运算符陷阱
| 陷阱 | 如何避免 | 
|---|---|
| SV 不允许在条件语句中包含赋值语句 | 小心使用 | 
| 有些操作符是 context-determined,有些操作符是 self-determined | 理解操作符的含义 | 
| 设计者错误理解赋值语句的 bit 扩展规则 | 理解 loosely type,小心使用 | 
| 有符号运算是 context-determined | 使用时确认右边的操作数类型 | 
| 从 signed 部分选择的结果是 unsigned 类型,可能与意图不符 | 使用 $signed()或signed'() | 
| 自增 / 自减是阻塞赋值,错误使用 | 仅在组合逻辑中使用自增 / 自减 | 
| 错误认为前自增 / 后自增相同 | 理解含义,谨慎使用 | 
| 仿真中的求值短路可能导致仿真和硬件行为不一致 | 避免使用对操作数产生副作用的建模方式 | 
| 把 ~和!的作用视为相同(实际不同) | 逻辑判断中永远不要使用 ~ |