Verilog中同一端口的双if分支是否冲突?按键控制功能异常排查
首先明确:同一端口sw_i[0]的两个if分支不存在冲突,问题出在你的时钟生成逻辑上,导致sw_i[0]关闭时,第二个always块根本没机会执行清零代码。
具体原因分析
你的clk_29由
count[div_para]生成,而count的更新完全受sw_i[0]控制:always@(posedge clk or negedge rstn) begin if(!rstn) count <= 0; else if(!sw_i[0]) count <= 0; // sw_i[0]关闭时,count被持续置0 else if(sw_i[0]) count <= count + 1; end当sw_i[0]关闭时,count始终为0,clk_29也就一直是0,没有上升沿。
第二个always块的触发条件是
@(posedge clk_29 or negedge rstn),只有clk_29出现上升沿或rstn下降时才会执行。sw_i[0]关闭后,clk_29停在0,没有上升沿,rstn又未变化的话,这个always块就完全停止运行,自然不会执行if(!sw_i[0])的清零分支,cnt和tmp就停留在最后一次的状态。换成sw_i[1]时,sw_i[0]仍处于开启状态,count持续自增,clk_29有正常的上升沿,第二个always块能正常触发,所以能检测到sw_i[1]的状态变化并执行对应分支,功能恢复正常。
修正方案
方案1:改用原始clk触发第二个always块(推荐)
把第二个always块的触发时钟换成原始输入clk,同时在内部检测clk_29的上升沿来保持原来的计数速度,这样不管sw_i[0]状态如何,always块都能持续运行,及时响应sw_i[0]的变化。同时注意时序always块必须用非阻塞赋值<=(你原来用的阻塞赋值=会导致仿真与硬件行为不一致):
always@(posedge clk or negedge rstn) begin reg clk_29_dly; if(!rstn) begin cnt <= 4'd0; tmp <= 7'b0000000; clk_29_dly <= 1'b0; end else if(!sw_i[0]) begin tmp <= 7'b0000000; cnt <= 4'd0; clk_29_dly <= 1'b0; end else begin clk_29_dly <= clk_29; // 检测clk_29的上升沿,保持原有的计数频率 if(clk_29 && !clk_29_dly) begin case(cnt) 4'b0000:tmp<=7'b0111111; 4'b0001:tmp<=7'b0000110; 4'b0010:tmp<=7'b1011011; 4'b0011:tmp<=7'b1001111; 4'b0100:tmp<=7'b1100110; 4'b0101:tmp<=7'b1101101; 4'b0110:tmp<=7'b1111101; 4'b0111:tmp<=7'b0000111; endcase cnt <= cnt + 1; if(cnt >= 4'b0111) cnt <= 4'b0000; end end end
方案2:修改时钟生成逻辑(不推荐)
如果一定要用clk_29触发,需要保证sw_i[0]关闭时clk_29仍有上升沿,但这种方式会让时钟受输入信号控制,容易产生毛刺和时序问题,不建议在FPGA/ASIC设计中使用。
额外注意点
- 时序always块(由时钟沿触发的always块)中,必须使用非阻塞赋值
<=,阻塞赋值=仅用于组合逻辑always块(@(*)),否则会出现仿真结果和实际硬件行为不符的情况。 - 你的两个if分支
else if(!sw_i[0])和else if(sw_i[0])是互斥的,语法和逻辑上完全合法,不会出现冲突,这次的问题和分支本身无关。
内容的提问来源于stack exchange,提问作者DamXosp4j

