FPGA成长的小Tips之赋值语句
深刻理解HDL的阻塞赋值和非阻塞赋值,就一定首先需要理解C语言的阻塞和非阻塞。
文章目录
前言背景
在软件层面上,阻塞赋值和非阻塞赋值是一个单纯进程管理的概念。其中阻塞赋值是指当前进程调用函数,函数返回之前,进程被挂起(进程被阻塞);而非阻塞赋值是当前进程调用函数,函数立即返回(返回一个回执),然后进程反复更新所调用函数的返回数据,直到得到有效的返回数据。期间进程不会被挂起(进程非阻塞)。
一、阻塞赋值(Blocking)与非阻塞赋值(Non_Blocking)是什么?及其赋值过程
(1)1.1阻塞赋值使用赋值运算符号为“=”。
阻塞赋值的过程是立刻执行的,即赋值运算符右侧表达式求值完后立刻会更新至运算符左侧,并且这个执行的过程不受其他语句执行的影响,其后的语句只有当前的赋值操作执行完场后才能顺利执行。
1.2所谓阻塞的概念是指在同一个always块中,其后母的赋值语句从概念上是在前一条赋值语句结束后开始赋值的。
(2)2.1非阻塞赋值使用的赋值运算符为“<=”。
2.2非阻塞赋值执行过程:在当前仿真时间槽(time-slot)开始分析计算获得右侧表达式的值,在当前时间槽执行结束时更新左侧表达式的值,在右侧表达式分析计算和左侧表达式被更新之间,任何其他事情都可以执行,同时也可能修改已经计算完成的右侧表达式的值,即非阻塞赋值的过程不影响其他语句的执行。根据非阻塞的特点,其赋值运算符左侧操作数只能为寄存器类型;因此非阻塞赋值只能用于过程性语句中(initial和always),不允许在连续赋值语句中使用非阻塞赋值。
二、代码案例
1.阻塞赋值(Blocking)
代码如下(sysverilog):
module Blocking;
logic clk;
logic [3:0] in, q, q_r, q_rr;
initial
begin
clk = 0;
in = 0;
q = 1;
q_r = 2;
q_rr = 3;
end
always @ (posedge clk)
begin
q = in;
q_r = q;
q_rr = q_r;
end
always #10 clk = ~clk;
endmodule
通过【VivadoSimulator】仿真观察到,一个时钟周期之后:
下面的语句一直等待上面的语句赋值完成后再执行。
q=0;q_r=0;q_rr=0;
2.非阻塞赋值(Non_Blocking)
代码如下(Verilog):
module Non_blocking;
reg clk;
reg [3:0] in;
reg [3:0] q, q_r, q_rr;
initial
begin
clk = 0;
in = 0;
q = 1;
q_r = 2;
q_rr = 3;
end
always @ (posedge clk)
begin
q <= in;
q_r <= q;
q_rr <= q_r;
end
always #10 clk = ~clk;
endmodule
通过观察【ModelSimSimulator】一个时钟周期后:q=0;q_r=1;q_rr=2;
总结
用阻塞赋值感觉就是在一种确定的事情上,如电平敏感的情况下,一旦赋值就“没法回头”。而非阻塞赋值就是为了解决这种“死机”问题,在每次赋值/调用的时候都给一个“反馈”。