前言
写Verilog语言的 always 语句块时,总会觉得在敏感信号触发的瞬间,各个电平到底是高是低弄不清楚,这里做了几个实验来总结一下。
测试
首先这是一个always_test 模块,输入只有异步复位信号,时钟信号。输出是一个三位的LED信号
module always_test(
input sys_rst_n ,
input sys_clk ,
output reg [2:0] led
);
//led_0 时钟上升沿触发时反转
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led[0] <= 1'b0;
else
led[0] <= !led[0];
end
//led_1 时钟上升沿触发时等于时钟电平
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led[1] <= 1'b0;
else
led[1] <= sys_clk;
end
//led_2 时钟上升沿触发时等于led_0
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led[2] <= 1'b0;
else
led[2] <= led[0];
end
endmodule
使用如下TD代码进行仿真测试:
开始输入信号都为0,然后10ns后复位信号赋值为1(无效),然后每隔20ns一个周期时钟信号进行翻转。
`timescale 1ns/1ns
module tb_always_test();
parameter CLK_PERIOD = 20;
reg sys_rst_n ;
reg sys_clk ;
wire [2:0] led ;
initial begin
sys_rst_n <= 1'b0;
sys_clk <= 1'b0;
#10
sys_rst_n <= 1'b1;
end
always #(CLK_PERIOD/2)
sys_clk <= !sys_clk;
always_test u_always_test(
.sys_rst_n (sys_rst_n),
.sys_clk (sys_clk ),
.led (led )
);
endmodule
得到的仿真波形如下:
可以看出在黄线处有很多细节:
1.rst复位信号和时钟信号同时升高,这时候触发的是 posedge sys_clk 这个敏感事件
2.触发敏感事件后需要判断sys_rst_n的电平,如果是低,三个信号都不会变化,显然,这里信号变化了,所以判断为高,所以这告诉我们,当敏感事件到来时,判断非always赋值的跳变中信号,信号的值是跳变后的
3.led_1的逻辑是时钟信号触发敏感事件时,led_1的值变为时钟信号的电平,led_1后续一直是高电平,证明了时钟信号上升沿作为敏感事件被触发的时候,在语句块内,时钟信号都是高电平
4.led_2的逻辑是每次时钟上升沿触发时,都把自己的值变成led_0的值,我们观察到led_2的值一直“滞后”led_0一个周期,这是因为时序逻辑的特点,也就是说当敏感事件触发,执行always块的时候,除了该信号本身,所有此时此刻正在跳变的、被always语句赋值的值 都取跳变之前的值。
总结
1.当敏感事件被触发的时候,可能有很多信号处于跳变状态,这时候,判断他们的值需要分析
2.当敏感信号上升或下降沿被触发的时候,在语句块内,该跳变信号的值是跳变后的值(led_1)
3.当敏感信号上升或下降沿被触发的时候,在语句块内,其他所有跳变中输入信号的值是跳变后的值(黄线处rst是跳变中的输入信号,取了跳变后的值)
4.当敏感信号上升或下降沿被触发的时候,在语句块内,其他被always赋值的输出信号,是跳变前的值(每次时钟到来时,led_2都取了跳变中led_1的跳变前的值),这是因为always语句并行执行,先把所有always的右值算出来然后再放到左边,不同always之间没有优先级。