时钟门控Verilog代码功能异常问题排查求助
嘿,我帮你拆解下这个问题的核心原因,其实是两个关键的理解偏差导致了你的仿真结果不符合预期:
1. 错误把组合逻辑信号当时钟用
你用always @(*) write_clk_en = write_clk & write_clk_mod;生成的write_clk_en是个组合逻辑信号,却试图用@(posedge write_clk_en)来触发赋值操作——这是问题的根源。
为什么会出问题?看你的write_clk_mod更新逻辑:它是在write_clk上升沿通过非阻塞赋值更新的。举个具体的时间点例子:当第三个write_clk上升沿(50ns左右)到来时,你把write_clk_mod设为0,这个赋值会在时钟沿的非阻塞赋值阶段生效,此时write_clk已经是高电平了,所以write_clk_en会立刻从1掉到0;而到第四个write_clk上升沿(70ns左右)时,你把write_clk_mod设回1,同样在非阻塞赋值阶段生效,此时write_clk还是高电平,write_clk_en会从0跳到1——这个跳变是在时钟高电平期间发生的,并不是你预期的“门控时钟上升沿”,但仿真器会把任何0→1的跳变都当成上升沿来触发,这就导致你误以为在write_clk_en关断的周期执行了data_in[3]的赋值。
2. 混淆了时钟使能和门控时钟的正确用法
你想要实现的是“仅当write_clk_en有效时,在时钟上升沿传输数据”,本质上是时钟使能逻辑,而非真正的门控时钟。正确的做法应该是把原始时钟write_clk作为触发源,用write_clk_mod作为使能条件,在时钟上升沿判断是否执行赋值,而不是把组合逻辑生成的write_clk_en当作时钟来用。
给你修正后的代码示例
把你最后那个触发赋值的initial块改成下面这样,就能完全符合你的预期:
initial begin data_in = 5'b00000; // 逐个bit赋值,仅在write_clk_mod有效时的时钟上升沿执行 repeat(1) @(posedge write_clk) if(write_clk_mod) data_in[0] <= 1'b1; repeat(1) @(posedge write_clk) if(write_clk_mod) data_in[1] <= 1'b1; repeat(1) @(posedge write_clk) if(write_clk_mod) data_in[2] <= 1'b1; repeat(1) @(posedge write_clk) if(write_clk_mod) data_in[3] <= 1'b1; repeat(1) @(posedge write_clk) if(write_clk_mod) data_in[4] <= 1'b1; end
或者更简洁的写法,避免重复代码:
initial begin data_in = 5'b00000; foreach(data_in[i]) begin wait(write_clk_mod); // 先等使能信号有效 @(posedge write_clk); // 再等时钟上升沿 data_in[i] <= 1'b1; end end
这样修改后,只有当write_clk_mod为1时,才会在write_clk的上升沿执行赋值,完全匹配你想要的“仅在有效门控时钟上升沿传输数据”的逻辑。
内容的提问来源于stack exchange,提问作者Ansuman Mishra

