为何MIG生成的Verilog测试代码用always块而非直接连续赋值?
在Xilinx Vivado的MIG内存控制器示例设计自动生成的顶层仿真测试文件sim_tb_top.v中,看到如下Verilog代码:
localparam real TPROP_PCB_CTRL = 0.00; // Delay for Address and Ctrl signals wire [0:0] ddr3_cs_n_fpga; reg [0:0] ddr3_cs_n_sdram_tmp; wire [0:0] ddr3_cs_n_sdram; always @( * ) ddr3_cs_n_sdram_tmp <= #(TPROP_PCB_CTRL) ddr3_cs_n_fpga; assign ddr3_cs_n_sdram = ddr3_cs_n_sdram_tmp;
不理解为何生成的代码不直接使用更简洁的连续赋值:
assign ddr3_cs_n_sdram = #(TPROP_PCB_CTRL) ddr3_cs_n_fpga;
想了解这种使用组合always块的写法相比直接连续赋值是否有特殊的深层用意?
核心原因解析
这种写法并非冗余,而是出于仿真准确性、扩展性和工具兼容性的考虑:
仿真行为的准确性:
Verilog中,连续赋值的延迟属于惯性延迟,会过滤掉持续时间短于延迟值的脉冲;而always块里的非阻塞赋值(<=)带延迟属于传输延迟,会完整传递所有信号变化,哪怕脉冲宽度极短。实际PCB走线延迟只会延迟信号传递,不会过滤短脉冲,因此用传输延迟的写法更贴近真实硬件行为——哪怕当前TPROP_PCB_CTRL设为0,后续修改为非零值时,仿真结果会更准确。代码扩展性:
这种结构方便后续扩展逻辑,比如如果需要给信号添加毛刺过滤、电平转换或其他处理,直接在always块中修改即可,无需重构整个赋值逻辑。而直接使用assign的写法,若后续要加额外逻辑,要么改成复杂的连续赋值表达式,要么还是得引入中间变量,反而增加了修改成本。工具兼容性:
部分老版本的仿真器对连续赋值带实数延迟的支持不够稳定,MIG作为成熟IP,会优先选择兼容性更强的写法,确保在不同仿真工具(如VCS、ModelSim)中都能正确运行,避免出现仿真异常。统一代码风格:
MIG生成的代码采用统一模板,所有PCB延迟相关的信号都用这种always块+临时寄存器的方式实现,不管是控制信号、地址信号还是数据信号,保持一致的代码结构,便于维护和排查问题,避免不同信号用不同写法导致的混乱。
内容的提问来源于stack exchange,提问作者zzzhhh

