always @(*)
在仿真开始时必然执行一次,无论信号是否变化。 always语句在0时刻初始运行前,vivado编译器先遍历一下,读出其中的各信号的值(若testbench没有在0时刻输入激励,那么reg采用默认的X值)。
而时序的always @(posedge clk)则不会,需要有满足的上升沿才会进入。
module Latch1(
input data,
input en,
output reg q
);
always @(*) begin
if (en) begin
q = data;
end
end
endmodule
`timescale 1ns/1ns
module sim_test();
reg data, en;
wire q;
initial begin
#10 //先延时,再赋值
en <= 1;
data <= 1;
end
Latch1 u_test(
.data (data),
.en (en),
.q (q)
);
endmodule
通过上面的波形图可以观察到:在10ns之前,由于en和data没有输入有效激励,导致采用了reg型的默认值x进行输入,相对应的输出q也是x;而10ns之后,由于en从X跳变到1,从而进入了always和if里,data把值传递给q。
下面是取消延时的波形图:
不延时输入激励信号,可以看到在0时刻后立马直接是q为1。
当时我会深究这个现象问题,是因为我认为,既然always的敏感变量在0时刻的初始值就是1,没有电平变化(甚至持续数ns),那么按理来说就不会进入always内,更不会执行q的赋值操作,那么reg型q就没有存储任何值,那就应该是默认的X。
但真实情况是q的值和data的值一样,那就表明我的认为是错的,在0时刻之前或许就已经进入了always内。
于是我才用打印输出的方式进行更直观的验证。
为了更加充分的验证,我们在代码内添加打印,进行更加直观的查看。
在延时后,如果按照我错误的想法,q的值应该为X,但实际上可以很明显的看出,在0时刻的时候q的取值是en为X的情况,也就是进入了else内。也就说明:在0时刻的节拍到来之前程序就已经被执行了一次,寄存器内就已经有值了。
下面我们把初始延时取消。
延时取消,观察0时刻的情况。可以看到0时刻打印了两次,其中第一次en为X进入else,第二次是en为0进入else。
通过上面的对比,反映出一个现象:那就是在0时刻之前,vivado会先遍历一下always里面各信号的值,值由testbench提供,没有就是默认值。