【Verilog】竞争、冒险
在数字电路中,信号传输和状态变换会有一定的延时
- 在组合逻辑电路中,不同路径的输入信号变化传输到同一点门极电路时,在时间上有先有后,这种先后所形成的时间差称为竞争(Competition)
- 由于竞争的存在,输出信号需要经过一段时间才能达到期望状态,过度时间内可能产生瞬间的错误输出,例如尖峰脉冲,这种现象被称为冒险(Hazard)
竞争不一定有冒险,但冒险一定会有竞争
F=A&A'
由于反相器电路的存在,信号A'传递到与门输入端的时间相对于信号A会滞后,这就可能导致与门最后的输出结果F会出现脉冲干扰
如何判断?
代数法
在逻辑表达式,保持一个变量固定不动,将剩余其他变量用0或1代替,如果最后逻辑表达式能化简成Y=A+A'或Y=A · A'的形式,则可判定此逻辑存在竞争与冒险
卡诺图法
有两个相切的卡诺圈,并且相切处没有其他卡诺圈包围,可能会出现竞争与冒险现象。
例如左下图所存在竞争与冒险,右下图则没有。
其实,卡诺图本质上还是对逻辑表达式的一个分析,只是可以进行直观的判断。
例如,左上图逻辑表达式可以简化为 Y = A'B' + AC,当 B=0 且 C=1 时,此逻辑表达式又可以表示为 Y = A' + A。所以肯定会存在竞争与冒险。
右上图逻辑表达式可以简化为 Y = A'B' + AB,显然 B 无论等于 1 还是 0,此式都不会化简成 Y = A' + A。所以此逻辑不存在竞争与冒险。
需要注意的是,卡诺图是首尾相临的。如下图所示,虽然看起来两个卡诺圈并没有相切,但实际上,m6 与 m4 也是相邻的,所以下面卡诺图所代表的数字逻辑也会产生竞争与冒险。
其他较为复杂的情况,可能需要采用 "计算机辅助分析 + 实验" 的方法
如何消除?
对数字电路来说,常见的避免竞争与冒险的方法主要有 4 种:
- 增加滤波电容,滤除窄脉冲
- 修改逻辑,增加冗余项
- 使用时钟同步电路,利用触发器进行打拍延迟
- 采用格雷码计数器
增加滤波电容,滤除窄脉冲
在输出端并联一个小电容,将尖峰脉冲的幅度削弱值门电路阈值以下
此方法简单,但是会增加输出电压的翻转时间,易破坏波形
修改逻辑,增加冗余项
利用卡诺图,在两个相切的圆之间,增加一个卡诺图,并加在逻辑表达式之中
如下图所示,对数字逻辑 Y = A'B' + AC 增加冗余项 B'C,则此电路逻辑可以表示为 Y = A'B' + AC + B'C。此时电路就不会再存在竞争与冒险
使用时钟同步电路,利用触发器进行打拍延迟
同步电路信号的变化都发生在时钟边沿。对于触发器的D输入端,只要毛刺不出现在时钟的上升沿并且不满足数据的建立和保持时间,就不会对系统造成危害,因此可认为D触发器的D输入端对毛刺不敏感。利用此特性,在时钟边沿驱动下,对一个组合逻辑信号进行延迟打拍,可消除竞争冒险
延迟一拍时钟,会一定概率的减少竞争冒险的出现。最安全的打拍延迟周期是3拍。可有效减少竞争冒险的出现;根据自己的设计需求,对信号进行合理的打拍延迟
module competition_hazard(input clk,input rstn,input en,input din_rvs,output reg flag);wire condition=din_rvs & en;always@(posedge clk or negedge !rstn) beginif(!rstn) beginflag<=1'b0;endelse beginflag<=condition;endend
endmodule
'timescale 1ns/1nsmodule test;reg clk,rstn;reg en;reg din_rvs;wire flag_safe,flag_dgs;//clock and rstn generatinginitial beginrstn=1'b0;clk=1'b0;#5 rstn=1'b1;forever begin#5 clk=~clk;endendinitial beginen=1'b0;din_rvs=1'b1;#19 en=1'b1;#1 din_rvs=1'b0;endcompetition_hazard u_dgs(.clk(clk),.rstn(rstn),.en(en),.din_rvs(din_rvs),.flag(flag_dgs));initial beginforever begin#100 if($time>=1000) $finish;endend
endmodule
增加打拍延时的逻辑
module clap_delay(input clk,input rstn,input en,input din_rvs,output reg flag);reg din_rvs_r;reg en_r;always@(posedge clk or !rstn) beginif(!rstn) begindin_rvs_r<=1'b0;en_r<=1'b0;endelse begindin_rvs_r<=din_rvs;en_r<=en;endendwire condition=din_rvs_r & en_r;always@(posedge clk or negedge !rstn) beginif(!rstn) beginflag<=1'b0;endelse beginflag<=condition;endend
endmodule
采用格雷码计数器
递加的多 bit 位计数器,计数值有时候会发生多个 bit 位的跳变。
例如计数器变量 counter 从 5 计数到 6 时, 对应二进制数字为 4'b101 到 4'b110 的转换。因为各 bit 数据位的延时,counter 的变换过程可能是: 4'b101 -> 4'b111 -> 4'b110。如果有以下逻辑描述,则信号 cout 可能出现短暂的尖峰脉冲,这显然是与设计相悖的
cout = counter[3:0] == 4'd7 ;
而格雷码计数器,计数时相邻的数之间只有一个数据 bit 发生了变化,所以能有效的避免竞争冒险。
好在 Verilog 设计时,计数器大多都是同步设计。即便计数时存在多个 bit 同时翻转的可能性,但在时钟驱动的触发器作用下,只要信号间满足时序要求,就能消除掉 100% 的竞争与冒险。
在编程时多注意以下几点,也可以避免大多数的竞争与冒险问题。
1)时序电路建模时,用非阻塞赋值。
2)组合逻辑建模时,用阻塞赋值。
3)在同一个 always 块中建立时序和组合逻辑模型时,用非阻塞赋值。
4)在同一个 always 块中不要既使用阻塞赋值又使用非阻塞赋值。
5)不要在多个 always 块中为同一个变量赋值。
6)避免 latch 产生。
6.4 Verilog 竞争与冒险 | 菜鸟教程