数字电路中,信号传输与状态变换时都会有一定的延时。
例如,对于给定逻辑 F = A & A',电路如左下图所示。
由于反相器电路的存在,信号 A' 传递到与门输入端的时间相对于信号 A 会滞后,这就可能导致与门最后的输出结果 F 会出现干扰脉冲。如右下图所示。

其实实际硬件电路中,只要门电路各个输入端延时不同,就有可能产生竞争与冒险。
例如一个简单的与门,输入信号源不一定是同一个信号变换所来,由于硬件工艺、其他延迟电路的存在,也可能产生竞争与冒险,如下图所示。

代数法
在逻辑表达式,保持一个变量固定不动,将剩余其他变量用 0 或 1 代替,如果最后逻辑表达式能化简成
Y = A + A'
或
Y = A · A'
的形式,则可判定此逻辑存在竞争与冒险。
例如逻辑表达式 Y = AB + A'C,在 B=C=1 的情况下,可化简为 Y = A + 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 ; //combination logic
always @(posedge clk or negedge !rstn) begin
if (!rstn) begin
flag <= 1'b0 ;
end
else begin
flag <= condition ;
end
end
endmodule
testbench 描述如下:
`timescale 1ns/1ns
module test ;
reg clk, rstn ;
reg en ;
reg din_rvs ;
wire flag_safe, flag_dgs ;
//clock and rstn generating
initial begin
rstn = 1'b0 ;
clk = 1'b0 ;
#5 rstn = 1'b1 ;
forever begin
#5 clk = ~clk ;
end
end
initial begin
en = 1'b0 ;
din_rvs = 1'b1 ;
#19 ; en = 1'b1 ;
#1 ; din_rvs = 1'b0 ;
end
competition_hazard u_dgs
(
.clk (clk ),
.rstn (rstn ),
.en (en ),
.din_rvs (din_rvs ),
.flag (flag_dgs ));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
仿真结果如下:
由图可知,信号 condition 出现了一个尖峰脉冲,这是由于信号 din_rvs 与信号 en 相对于模块内部时钟都是异步的,所以到达内部门电路时的延时是不同的,就有可能造成竞争冒险。
虽然最后的仿真结果 flag 一直为 0,似乎是我们想要的结果。但是实际电路中,这个尖峰脉冲在时间上非常靠近时钟边沿,就有可能被时钟采集到而产生错误结果。

下面我们对模型进行改进,增加打拍延时的逻辑,如下:
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) begin
if (!rstn) begin
din_rvs_r <= 1'b0 ;
en_r <= 1'b0 ;
end
else begin
din_rvs_r <= din_rvs ;
en_r <= en ;
end
end
wire condition = din_rvs_r & en_r ;
always @(posedge clk or negedge !rstn) begin
if (!rstn) begin
flag <= 1'b0 ;
end
else begin
flag <= condition ;
end
end // always @ (posedge clk or negedge !rstn)
endmodule
将此模块例化到上述 testbench 中,得到如下仿真结果。
由图可知,信号 condition 没有尖峰脉冲的干扰了,仿真结果中 flag 为 0 也如预期。
其实,输入信号与时钟边沿非常接近的情况下,时钟对输入信号的采样也存在不确定性,但是不会出现尖峰脉冲的现象。对输入信号多打 2 拍,是更好的处理方式,对竞争与冒险有更好的抑制作用。

递加的多 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% 的竞争与冒险。
小结
一般来说,为消除竞争冒险,增加滤波电容和逻辑冗余,都不是 Verilog 设计所考虑的。
计数采用格雷码计数器,大多数也是应用在高速时钟下减少信号翻转率来降低功耗的场合。
利用触发器在时钟同步电路下对异步信号进行打拍延时,是 Verilog 设计中经常用到的方法。
除此之外,为消除竞争冒险,Verilog 编码时还需要注意一些问题,详见下一小节。
在编程时多注意以下几点,也可以避免大多数的竞争与冒险问题。
下面,对以上注意事项逐条分析。
前面讲述非阻塞赋值时就陈述过,时序电路中非阻塞赋值可以消除竞争冒险。
例如下面代码描述,由于无法确定 a 与 b 阻塞赋值的操作顺序,就有可能带来竞争冒险。
always @(posedge clk) begin
a = b ;
b = a ;
end
而使用非阻塞赋值时,赋值操作是同时进行的,所以就不会带来竞争冒险,如以下代码描述。
always @(posedge clk) begin
a <= b ;
b <= a ;
end
两条赋值语句同时赋值,F <= c="" &="" d="" 中使用的是信号="" c="" 的旧值,所以导致此时的逻辑是错误的,f="" 的逻辑值不等于="">=>
而且,此时要求信号 C 具有存储功能,但不是时钟驱动,所以 C 可能会被综合成锁存器(latch),导致竞争冒险。
always @(*) begin
C <= A & B ;
F <= C & D ;
end
对代码进行如下修改,F = C & D 的操作一定是在 C = A & B 之后,此时 F 的逻辑值等于 A&B&D,符合设计。
always @(*) begin
C = A & B ;
F = C & D ;
end
虽然时序电路中可能涉及组合逻辑,但是如果赋值操作使用非阻塞赋值,仍然会导致如规范 1 中所涉及的类似问题。
例如在时钟驱动下完成一个与门的逻辑功能,代码参考如下。
always @(posedge clk or negedge rst_n)
if (!rst_n) begin
q <= 1'b0;
end
else begin
q <= a & b; //即便有组合逻辑,也不要写成:q = a & b
end
end
always 涉及的组合逻辑中,既有阻塞赋值又有非阻塞赋值时,会导致意外的结果,例如下面代码描述。
此时信号 C 阻塞赋值完毕以后,信号 F 才会被非阻塞赋值,仿真结果可能正确。
但如果 F 信号有其他的负载,F 的最新值并不能马上传递出去,数据有效时间还是在下一个触发时刻。此时要求 F 具有存储功能,可能会被综合成 latch,导致竞争冒险。
always @(*) begin
C = A & B ;
F <= C & D ;
end
如下代码描述,仿真角度看,信号 C 被非阻塞赋值,下一个触发时刻才会有效。而 F = C & D 虽然是阻塞赋值,但是信号 C 不是阻塞赋值,所以 F 逻辑中使用的还是 C 的旧值。
always @(*) begin
C <= A & B ;
F = C & D ;
end下面分析假如在时序电路里既有阻塞赋值,又有非阻塞赋值会怎样,代码如下。
假如复位端与时钟同步,那么由于复位导致的信号 q 为 0,是在下一个时钟周期才有效。
而如果是信号 a 或 b 导致的 q 为 0,则在当期时钟周期内有效。
如果 q 还有其他负载,就会导致 q 的时序特别混乱,显然不符合设计需求。
always @(posedge clk or negedge rst_n)
if (!rst_n) begin //假设复位与时钟同步
q <= 1'b0;
end
else begin
q = a & b;
end
end
需要说明的是,很多编译器都支持这么写,上述的分析也都是建立在仿真角度上。实际中如果阻塞赋值和非阻塞赋值混合编写,综合后的电路时序将是错乱的,不利于分析调试。
与 C 语言有所不同,Verilog 中不允许在多个 always 块中为同一个变量赋值。此时信号拥有多驱动端(Multiple Driver),是禁止的。当然,也不允许 assign 语句为同一个变量进行多次连线赋值。 从信号角度来讲,多驱动时,同一个信号变量在很短的时间内进行多次不同的赋值结果,就有可能产生竞争冒险。
从语法来讲,很多编译器检测到多驱动时,也会报 Error。
具体分析见下一章:《避免 Latch》。
目录一、inout在设计文件中的使用方法1.1、inout的第一种使用方法1.2、inout实现的第二种使用方法1.3、inout使用总结 二、inout在仿真测试中的使用方法一、inout在设计文件中的使用方法在FPGA的设计过程中,有时候会遇到双向信号(既能作为输出,也能作为输入的信号叫双向信号)。比如,IIC总线中的SDA信号就是一个双向信号,QSPIFlash的四线操作的时候四根信号线均为双向信号。在Verilog中用关键字inout定义双向信号,这里总结一下双向信号的处理方法。1.1、inout的第一种使用方法 实际上,双向信号的本质是由一个三态门组成的,三态门可以输出高电平,低电
我想知道使用MRIruby(2.0.0)和一些全局变量来创建竞争条件是否容易,但事实证明这并不容易。看起来它应该在某个时候失败,但它没有,我已经运行了10分钟。这是我一直在努力实现的代码:definc(*)a=$xa+=1a*=3000a/=3000$x=aendTHREADS=10COUNT=5000loopdo$x=1THREADS.times.mapdoThread.new{COUNT.times(&method(:inc))}end.each(&:join)breakputs"woohoo!"if$x!=THREADS*COUNT+1endputs$x为什么我无法生成(或检
我正在试验多线程示例。我正在尝试使用以下代码产生竞争条件。但我总是得到相同(正确)的输出。classCounterattr_reader:countdefinitialize@count=0enddefincrement@count+=1enddefdecrement@count-=1endendc=Counter.newt1=Thread.start{100_0000.times{c.increment}}t2=Thread.start{100_0000.times{c.increment}}t1.joint2.joinpc.count#200_0000我能够在每个线程中使用更少的迭
简单的例子,它在我的平台上不起作用(Ruby2.2,Cygwin):#!/usr/bin/rubybacktt=fork{exec('mintty','/usr/bin/zsh','-i')}Process.detach(backtt)exit这个小程序(当从shell启动时)应该跨越一个终端窗口(mintty)然后让我回到shell提示符。但是,虽然它确实创建了mintty窗口,但之后我没有shell提示符,而且我无法在调用shell中键入任何内容。但是当我在分离之前引入一个小的延迟时,无论是使用“sleep”,还是通过在标准输出上打印一些东西,它都会按预期工作:#!/usr/bin
我们有一个异步任务,它为一个对象执行一个可能长时间运行的计算。然后将结果缓存在对象上。为了防止多个任务重复相同的工作,我们添加了带有原子SQL更新的锁定:UPDATEobjectsSETlocked=1WHEREid=1234ANDlocked=0加锁只针对异步任务。对象本身仍可由用户更新。如果发生这种情况,旧版本对象的任何未完成任务都应丢弃其结果,因为它们可能已过时。使用原子SQL更新也很容易做到这一点:UPDATEobjectsSETresults='...'WHEREid=1234ANDversion=1如果对象已更新,其版本将不匹配,因此结果将被丢弃。这两个原子更新应该处理任何
是否可以在promise解析和promise等待返回之间执行用户代码?functiona(){returnnewPromise(resolve=>{setTimeout(()=>{//Betweenhere...resolve()},1000))}}asyncfunctionb(){awaita()//...andhere?}规范是否强制立即调用Promise回调?我想知道2点之间的虚拟机是否可以处理一个事件,这可能会导致副作用。 最佳答案 不,它不强制立即调用。Thespec在解决一个promise时会经历许多步骤,其中之一是:P
之前跟着老师的视频安装了一个es6.4.3,视频里说“6->7类型逐渐被废弃,版本变化较大,与springboot不兼容,建议安装6.4.3与内部一致”,我就很天真的跟着一块装了,结果可想而知……我的springboot版本是2.6.4啊,怎么可能不报错呢??!还是要多看官方文件!整合最重要的就是这张图,版本对了什么都好说es对应的springboot版本SpringDataReleaseTrainSpringDataElasticsearchElasticsearchSpringFrameworkSpringBoot2021.2(Raj)4.4.x7.17.45.3.x2.7.x2021.1
我对Foundation6.4中的javascript失去了理智。我不知道这个Webpack是怎么回事。似乎有些库/插件可以工作,有些则不能。我的最新一期是plyr(https://plyr.io/)。我不明白为什么TweenMax可以100%正常工作而plyr.js却不行。我究竟做错了什么?这是我得到的错误..app.js:23026UncaughtReferenceError:plyr未定义这就是我的app.js的样子..import$from'jquery';importwhatInputfrom'what-input';window.$=$;window.jQuery=$;re
工欲善其事,必先利其器。应该没有多少人会使用Quartus和vivado这些软件自带的编辑器吧,原因在于这些编辑器效率很低,VerilogHDL代码格式比较固定,通常可以利用代码片段补全加快书写。基本上代码写完之后才会打开Quartus或者vivado建立工程,这其实要求编辑器需要有代码检错的功能,否则可能编译时一直报错,什么信号没定义,信号定义错误之类的。Vscode利用插件可以实现此功能,可以达到一次设计就通过编译和仿真。1、vscode安装及解决下载速度慢 首先通过VisualStudioCode-CodeEditing.Redefined安装vscode软件,如图1下载64位vs
前言:验证结构与实验3是相同的,但需要验证的对象是完整的mcdf。对比之前新添加了reg寄存器模块(选择数据),formatter模块(数据打包)。种一棵树最好的时间是十年前,其次是现在。不是吗?实验3结构包含moinitor、checker、generator、initiator、test,这已经是一个完整的仿真结构,实验4可以说是实验3结构的复制粘贴。实验4将设计变得更复杂,添加了reg寄存器模块,formatter模块。验证过程完全相同,需要像实验3的验证过程一样对这两个模块也做仿真验证。设计中reg的功能是可以选择从哪个fifo接收数据,并且可以判断fifo余量(之前是margin),