前言
在使用always语句的时候会用到敏感信号列表,这种列表可以很清晰地表示该always块在什么时候被触发。然而当敏感列表有多个条件的时候,如果条件同时满足,always肯定会触发,但是具体会触发哪一个呢,触发两次还是一次呢?这需要通过一些实验来证明。
测试
编写RTL代码,输入是系统时钟和复位,输出是一个LED灯,模块中还包含了一个计数器,用来测试always块被进入了几次。
module sensitive_test(
input sys_clk ,
input sys_rst_n ,
output reg led
);
reg [3:0] count = 4'b0;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_clk)
led <= 1'b1;
else if(!sys_rst_n)
led <= 1'b0;
else
led <= 1'bz;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
count <= count + 4'b10;
else if(sys_clk)
count <= count + 4'b1;
else
count <= 4'bz;
end
endmodule
对应的tb代码如下:
`timescale 1ns/1ns
module tb_sensitive_test();
parameter CLK_PERIOD = 20;
reg sys_clk;
reg sys_rst_n;
wire led;
initial begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
#(CLK_PERIOD/2)
sys_rst_n <= 1'b1;
#(CLK_PERIOD*4)
sys_rst_n <= 1'b0;
end
always #(CLK_PERIOD/2)
sys_clk <= !sys_clk;
sensitive_test u_sensitive_test(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.led (led )
);
endmodule
经过仿真的波形图如图所示:
下面来仔细分析一下:
1.在0ns处,计数器直接从初始值0变成了2,这说明在tb文件中给sys_rst_n赋值为0也相当于一个下降沿,成功触发了两个always块
2.而后每次时钟上升沿都会触发两个always块,所以灯一直亮,计数器一直加1
3.关键的90ns处,时钟处于上升沿,rst处于下降沿,同时满足了触发always块的条件,所以进入了always块,这里需要注意,没有先后顺序,同时满足就进入,并不是满足了哪一个所以进了,就是都满足所以进来了,无需纠结到底是谁触发的这个always。
问题1:90ns处,这里led并没有清零,这是为什么呢?
答:因为我们的代码是这样写的:
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_clk) //先判断时钟信号,这是一种不妙的写法
led <= 1'b1;
else if(!sys_rst_n)
led <= 1'b0;
else
led <= 1'bz;
end
所以,逻辑是这样的:因为“两个信号同时触发了” ==> 进入了always块 ==> 进入后第一行要求判断clk的电平 ==> 因为输入信号取变化后的值 ==> 所以clk为高 ==> 执行led变高电平 ==> 退出
(对输入信号为什么取变化后的值不理解,可以看上一篇文章)
问题2:90ns处,计数器直接加了2,这说明什么呢?
计数器直接加二,说明进入always块后执行了如下计数器代码:
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
count <= count + 4'b10;
else if(sys_clk)
count <= count + 4'b1;
else
count <= 4'bz;
end
判断rst为低,所以直接执行count+2的代码,然后退出结束。
这说明:两个敏感电平同时变化,只会进入always块一次,否则这里就会加4了
总结
1.对于多个敏感信号触发的always模块,如果条件同时满足,将只会执行一次块内代码。
2.不论如何进入的always模块,都会严格按照always模块内部逻辑按顺序进行执行。