1. 问题现象与背景分析
最近在Vivado仿真环境中调试FPGA设计时,遇到了一个典型的信号同步问题:原本设计好的两级寄存器打拍(pipeline)结构,在仿真时出现了第二级寄存器未能正确采样第一级寄存器输出的情况。具体表现为时钟上升沿时刻,第一级寄存器的输出值直接穿透到了第二级寄存器,仿佛两级寄存器合并成了一级。
这种情况在Xilinx Vivado自带的仿真器(通常为XSIM)中尤为常见,尤其当设计者从其他仿真工具(如ModelSim)迁移过来时更容易遇到。本质上,这是Verilog仿真中的"阻塞赋值"与"非阻塞赋值"语义差异导致的典型问题。
关键提示:Vivado仿真器对Verilog语义的处理与其他商业仿真器存在细微差别,特别是在处理混合使用阻塞(=)和非阻塞(<=)赋值时表现更为敏感。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 信号打拍的基本原理与实现
2.1 正确的寄存器打拍结构
一个标准的两级寄存器同步链应该如下所示:
verilog复制always @(posedge clk) begin
reg_stage1 <= data_in; // 第一级寄存器
reg_stage2 <= reg_stage1; // 第二级寄存器
end
在理想的时序仿真中,每个时钟上升沿应该:
- 将data_in采样到reg_stage1
- 将上一个时钟周期的reg_stage1值采样到reg_stage2
2.2 Vivado仿真器的特殊行为
实际仿真中可能出现异常波形:
- 时钟上升沿时刻,reg_stage1和reg_stage2同时变化
- reg_stage2直接获得了data_in的值,而非reg_stage1的上一个周期值
- 波形显示两级寄存器似乎被合并
这种情况往往源于以下两种错误写法之一:
错误写法示例1:混合赋值类型
verilog复制always @(posedge clk) begin
reg_stage1 = data_in; // 阻塞赋值
reg_stage2 <= reg_stage1; // 非阻塞赋值
end
错误写法示例2:不完整的敏感列表
verilog复制always @(posedge clk or data_i
