always触发的具体时机

该文章为内容未经严格验证,仅基于自身理解和非严谨实验,请读者自身多加甄别。
若有错误,欢迎在评论区指正。

本文主要探究always触发的具体时机。

问题背景

首先查看下面代码,是一段同步时序下使用KEY控制LED的简单代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
module flip_flop(
input clk, //时钟信号
input rst_n, //复位信号
input key_in, //按键信号
output reg led_out, //LED输出信号
);

always @(posedge clk or negedge rst_n)
if (rst_n == 1'b0)
led_out <= 1'b0;
else
led_out <= key_in;
endmodule

这部分代码中,使用到了异步复位,rst_n出现下降沿,在always语句块中判断rst_n电平,如果是低电平则进行复位。
一个猜想就产生了:进入always语句块时的时刻,是触发信号已经稳定下来的时刻,而不是跳变过程中的中间态。
此时观察敏感信号clk,根据猜想推测:在clk的上升沿时触发,clk稳定下来为高电平的时刻开始执行always语句块中的内容。如果key信号和clk完全同步变化,则led_out的输出永远为高电平,否则为低电平。

设计实验

根据猜想设计如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module always_test(
input clk,
input rst_n,
output reg a
);

always @(posedge clk or negedge rst_n) begin
if (!rst_n)begin
a <= 1'b0;
end
else
begin
a <= clk;
end
end
endmodule

对应tb文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module tb_always_test(

);

reg clk;
reg rst_n;

wire a;

always #10 clk = ~clk;

initial begin
clk = 1'b0;
rst_n = 1'b0;

#100
rst_n = 1'b1;
#1000
$finish;
end

always_test uut(
.clk(clk),
.rst_n(rst_n),
.a(a)
);
endmodule

显然,如果猜想正确,a电平输出将保持高电平。

仿真验证

运行仿真,a信号果然是高电平,证明猜想正确。

仿真

拓展

野火FPGA曾说:计算赋值号右手边的信号时,所有的变量值均是触发沿到来前的值,更新的赋值号左手边的信号作为触发沿后的值,并且保持到下一个触发沿到来时候,等待更新。

这句话的前半句如果仔细考虑,会感觉有点反因果:先计算,后触发计算。
真实情况是当clk信号触发下降沿时,此时clk信号已经是高电平,其他寄存器并未发生变化。而后执行被clk信号触发而驱动的语句块,第一步进行右边信号的计算,但此时参与计算的寄存器的值均未被更新;第二步左边信号开始更新赋值,此时寄存器的值被更新。
考虑到从clk拉到高电平稳定到第一个步骤时间极短,画图时这一误差并未画出,视觉显示为重叠在一起,故而看作是下降沿之前的值;同理第二个步骤,看作为下降沿之后的值。

作者

LiXintao

发布于

2025-07-23

更新于

2025-07-23

许可协议

评论