Verilog与VHDL如何处理(n+delta)周期后的冲突赋值以正确解决电路毛刺?
Verilog与VHDL中OR门毛刺场景的行为机制解析
咱们先把问题背景理清楚:假设有一个实现((a AND b) OR c)的组合逻辑电路,输入a、b、c是同步变化的(比如都来自时钟寄存器输出)。这里AND门延迟可忽略,OR门有5ns的显著延迟,我们用两个独立进程来建模,代码如下:
Verilog 实现
// 进程1:计算AND门输出 always @(a, b) begin temp <= a & b; end // 进程2:带延迟的OR门输出 always @(temp, c) begin out <= #5 temp | c; end
VHDL 实现
-- 进程1:计算AND门输出 process(a, b) begin temp <= a and b; end process; -- 进程2:带延迟的OR门输出 process(temp, c) begin out <= temp or c after 5ns; end process;
当输入从(a=1, b=1, c=0)跳变到(a=0, b=1, c=1)时,会出现一个容易混淆的调度场景:
- temp(AND门输出)会在0ns的后续delta周期内从1变为0,触发OR门的赋值调度;
- c同时从0变为1,也会触发OR门的赋值调度。
如果按照简单模型,可能会误以为两个调度都在5ns执行,且执行顺序错误会导致out错误停留在0,但实际上Verilog和VHDL都有明确的机制保证结果正确,核心逻辑和你之前设想的三种机制都不同:
核心机制:延迟赋值的右侧表达式触发时立即计算
无论是Verilog的<= #5 ...还是VHDL的after 5ns延迟赋值,它们的执行流程都是两步走:
- 触发时刻计算值:当敏感信号(temp或c)发生变化时,立即计算延迟赋值右侧表达式的当前值(而非等到延迟时间点再计算);
- 调度未来赋值:把“在指定延迟后(这里是5ns)将该计算值赋给目标信号”的事件,放入对应时间点的事件队列。
放到咱们的场景里具体看:
- 当c在0ns从0变为1时,触发进程2,此时temp还没更新(还是1),计算
temp | c得到1 | 1 = 1,于是调度“5ns后给out赋值1”; - 当temp在0ns的后续delta周期从1变为0时,再次触发进程2,此时c已经是1了,计算
temp | c得到0 | 1 = 1,于是又调度“5ns后给out赋值1”。
到5ns时,两个调度事件执行的都是给out赋值1,无论执行顺序如何,最终out的结果都是正确的1,不会出现错误的0。
两种语言的细节差异
虽然核心逻辑一致,但两者在事件队列的管理上有细节区别:
- Verilog:依赖分层事件队列(Stratified Event Queue),延迟赋值的事件会被放入「未来时间队列」的对应时间点,同一时间点的事件会按调度顺序执行,但由于每个事件的赋值是预先计算好的固定值,最终结果只取决于所有赋值的内容,而非顺序;
- VHDL:将延迟赋值视为信号的事务(Transaction),同一信号的事务会被放入事件队列,到达指定时间后按调度顺序处理,同样,每个事务的赋值是预先计算的固定值,最终信号值取最后一次赋值的结果(但在这个场景里两次赋值都是1,结果一致)。
简单来说,两种语言都通过「预先计算赋值结果,再调度未来执行」的方式,从根源上避免了因延迟时间点输入变化导致的错误,完美解决了你担心的调度顺序问题。
内容的提问来源于stack exchange,提问作者Martin Geisse
相关产品推荐
相关产品推荐

