FPGA学习之路-阻塞赋值与非阻塞赋值
例子,二者有什么区别呢?
1
2
3
4
5
6
7
// 非阻塞赋值
always @ ( posedge Clk )
counter <= counter + 1 ;
// 阻塞赋值
always @ ( posedge Clk )
counter = counter + 1 ;
非阻塞赋值的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module block_nonblock (
input Clk ,
input Reset_n ,
input a , b , c ,
output reg [ 1 : 0 ] out ;
);
reg [ 1 : 0 ] d ;
always @( posedge Clk or negedge Reset_n )
if ( ! Reset_n ) begin
d <= 2 'b0 ;
out <= 2 'b0 ;
end
else begin
d <= a + b ;
out <= d + c ;
// out <= a + b + c; 这个又是一种可能的写法
end
endmodule
还有一种等价的写法(分成两个always),参数部分省略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
always @( posedge Clk or negedge Reset_n )
if ( ! Reset_n ) begin
d <= 2 'b0 ;
end
else begin
d <= a + b ;
end
always @( posedge Clk or negedge Reset_n )
if ( ! Reset_n ) begin
out <= 2 'b0 ;
end
else begin
out <= d + c ;
end
其中d<=a+b的电路展示:
而out<=d+c的电路展示:
而out<=a+b+c的电路展示:
阻塞赋值的情况
1
2
3
4
5
6
7
8
9
always @( posedge Clk or negedge Reset_n )
if ( ! Reset_n ) begin
d = 2 'b0 ;
out = 2 'b0 ;
end
else begin
d = a + b ;
out = d + c ;
end
在这种情况下,如果使用vivado的综合分析工具,查看schematic view,会发现d寄存器没了,只有一个out寄存器。(可以看成是组合逻辑顺序执行了)
也就是说,阻塞赋值会让d的值在同一个时钟周期内立刻更新,所以out的计算会使用新的d值,相当于out=a+b+c。
所以和out<=a+b+c的电路原理是一样的。
但是如果我这样写:
1
2
3
4
5
6
7
8
9
always @( posedge Clk or negedge Reset_n )
if ( ! Reset_n ) begin
d = 2 'b0 ;
out = 2 'b0 ;
end
else begin
out = d + c ;
d = a + b ;
end
不存在依赖问题,因为out的计算使用的是上一个时钟周期的d值,然后d才被更新为新的值。所以在综合分析后的原理图中,d寄存器又回来了(用于存储供out使用的旧值)。
D触发器
这里顺便说一下D触发器的工作原理
D触发器是一种最简单的存储单元,用于在数字电路中存储单个位的信息。而寄存器则是有多个D触发器组成的,用于存储多位信息。比如Reg [7:0] output 就是有8个D触发器并联组成的8位寄存器(共享同一个时钟信号)。
D触发器在时钟上升沿时,将D输入的值传递到Q输出端。
也就是说,在时钟上升沿之前,D输入的值会被锁存,并在时钟上升沿时更新Q输出。
这意味着在时钟周期内,D输入的值可以变化,但Q输出只会在时钟上升沿时更新为D的值。
例如,如果D在时钟上升沿之前为1,那么在时钟上升沿时,Q将更新为1。
如果D在下一个时钟周期内变为0,那么Q仍然保持为1,直到下一个时钟上升沿时,Q才会更新为0。
总结
在时序逻辑中,一律使用非阻塞赋值(<=),以确保所有寄存器在时钟边沿同时更新,避免竞态条件和不确定行为。
阻塞赋值(=)主要用于组合逻辑电路的描述,在这种情况下,赋值顺序很重要,因为它们会立即生效,影响后续的计算。
Licensed under CC BY-NC-SA 4.0