我们在做数字逻辑仿真时,通常用 X (unknown) 作为寄存器的初始值(仿真器的默认行为),这样可以通过 X 值的传播使未初始化的寄存器暴露出来。然而,这种方法并不总是准确的,其会导致一些情形下,原本应为有效值的信号也被误判为 X。如果设计中使用了同步复位,在综合后的网表级仿真时,很容易发生这种误判。
我们来看一个例子。下图中,电路由一个 cell OA33HD、一个 D 触发器、两个反相器构成。cell 的输入 A1、A3、B1 都连接到外部逻辑,在复位状态下我们可以认为它们始终为逻辑“0”;左侧为复位输入,高电平有效。
我们期望在 reset 信号为高电平时,在时钟上升沿,无论 D 触发器中原有的值为多少,都能够将 D 触发器的值复位为 0。观察可以发现,该电路满足我们的期望:
- 假如 D 触发器中的初始值为 0,那么 A1 到 A3 都为 0, 根据 cell 的内部逻辑,Z 也为 0,所以寄存器的 D 为 0。
- 假如 D 触发器中的初始值为 1,那么 B1 到 B3 都是 0,Z 的值也为 0,所以寄存器的 D 为 0。
以上两种情形,D 触发器的 D 引脚输入都是 0,则在时钟上升沿即可完成复位,Q 也变为 0。
然而,如果 D 触发器的初始值为 X,就会不一样的效果:仿真器会认为 A2 和 B3 的值都为“X”,其余 4 根 cell 引脚的值为 0。在这样的前提下,根据 cell 的真值表,仿真器无法判断 Z 的值,因此 Z 的值也为“X”,D 触发器下一个周期的值仍然为“X”,无法完成复位。
我们可以认为,“X”值对仿真器掩盖了某些信号之间的逻辑关系(例如上图中 A2 和 B3 的值是始终相反的),导致了对部分结果本应为常值的输出误判为“X”,从而无法正确模拟门级电路的实际行为。为了使这些隐含关系体现出来,我们可以使用 logic solver 去找到 wire 之间的隐含关系,进而做出可靠的判断。不过我不太清楚具体该怎么操作。
一个比较简单的解决办法是,在仿真时不要将寄存器的初始值设置为“X”,而是用随机值代替。如果观察 Chisel 语言生成的 Verilog 代码,就可以发现它有这样的处理,会在仿真时利用 initial block 将所有寄存器初始化为随机值。
如果是使用 Verilog 直接编写的代码,或者是 DC 等工具综合出的网表文件,若要手动添加随机值初始化,未免有些麻烦。这时,我们可以使用仿真器提供的功能,将寄存器初始化为随机值。例如,如果使用 Synopsys VCS 仿真器,则可以用编译选项 +vcs+initreg+random
来控制随机初始化,参考 https://blog.csdn.net/kevindas/article/details/102178944 及 https://www.francisz.cn/download/vcsmx_ug.pdf(110 页)。
本作品使用基于以下许可授权:Creative Commons Attribution-ShareAlike 4.0 International License.