FPGA学习之疑难杂症(四)——阻塞赋值和非阻塞赋值

本文详细比较了Verilog语言中的阻塞赋值和非阻塞赋值在initial和always语句中的使用,强调了非阻塞赋值在时序逻辑中的优势,通过实例展示了它们在电路设计中的不同效果。还讨论了阻塞赋值可能导致的问题以及如何模拟实际电路的延迟。
摘要由CSDN通过智能技术生成

1. 概念:

1)阻塞赋值(用“=”)和非阻塞赋值(用“<=”)都是过程性赋值语句,出现在initial语句和always语句中;区别于出现在模块module中,使用assign关键词的连续赋值语句(用“=”)

2)阻塞赋值(用“=”)可以用于组合逻辑,也可以用于时序逻辑中,只不过用于时序逻辑中时,如果语句顺序没有使用恰当,得到的结果可能不是所期待的,所以建议时序逻辑中使用非阻塞赋值(用“<=”)(后文例子中解释)

3)非阻塞赋值(用“<=”)除了能描述时序逻辑电路外,也可描述组合逻辑电路,只是这样的代码风格不好

4)阻塞赋值是串行的,按照顺序执行,排列在前面的语句完成赋值后,再执行下面的赋值语句;阻塞赋值(如b=a)立即执行,也就是说执行下一条语句时,b已等于a

5)非阻塞赋值是并行的,允许块中的其他语句同时执行(非阻塞赋值语句把想要赋给左式的值安排在未来时刻,然后继续执行,换言之,它并不等到赋值执行完成之后才执行下一句);非阻塞赋值在语句块中,上面语句所赋的变量值不能立即就为下面的语句所用,块结束后才能完成这次赋值操作,而所赋的变量值是上一次赋值得到的(不立即执行)。(如always @(posedge clk or negedge rst_n) begin b<=a;c<=b end)在赋值开始时计算表达式右边的值,在过程块结束(本次clk周期结束)才更新被赋值变量,因为同时进行赋值,后面的语句会比上一条语句慢一个clk周期(也就是时钟clk上升沿来了之后,a的值赋给b,上一clk周期的b的值赋给c)(可能理解有误,欢迎指正~)

阻塞过程性赋值

非阻塞过程性赋值

使用“=”赋值符号

使用“<=”赋值符号

出现在initial语句和always语句中

出现在initial语句和always语句中

驱动变量

驱动变量

可用于组合逻辑(推荐),也可用于时序逻辑(不推荐,语句书写顺序不恰当可能产生意想不到的结果)

可用于组合逻辑(不推荐,代码风格不好),也可用于时序逻辑(推荐)

串行,立即执行,赋值语句执行完后块才结束

并行,不立即执行,块结束后才能完成这次赋值

2.实例:

设计代码:

情况1:非阻塞赋值

module block_nonblock1 (
  input clk,
  input rst_n,
  input a,b,c,
  output reg [1:0] out
  );
  
  reg [1:0] d;
  
  always @(posedge clk or negedge rst_n)
  if (!rst_n)begin
     out<=2'b0;
     d<=2'b0;
     end
  else begin
     d<=a+b;
     out<=d+c;
     end
     
     //等同于上面的写法
//  always @(posedge clk or negedge rst_n)
//  if (!rst_n)begin
//     out<=2'b0;
//     end
//  else begin
//     out<=d+c;
//     end  

  
//  always @(posedge clk or negedge rst_n)
//  if (!rst_n)begin
//     d<=2'b0;
//     end
//  else begin
//     d<=a+b;
//     end  
  endmodule

情况2:将非阻塞赋值功能错误理解成阻塞赋值功能后,的非阻塞赋值

module block_nonblock2 (
  input clk,
  input rst_n,
  input a,b,c,
  output reg [1:0] out
  );
  
  reg [1:0] d;
  
  always @(posedge clk or negedge rst_n)
  if (!rst_n)begin
    out<=2'b0;
    d<=2'b0;
     end
   else begin
     out<=a+b+c;
   end
  endmodule

情况3:能够实现正确目标设计的阻塞赋值

module block_nonblock3 (
  input clk,
  input rst_n,
  input a,b,c,
  output reg [1:0] out
  );
  
  reg [1:0] d;
  
  always @(posedge clk or negedge rst_n)
  if (!rst_n)begin
     out=2'b0;
     d=2'b0;
     end
  else begin
     out=d+c;
     d=a+b;
     end
  endmodule

情况4:因书写顺序不同,导致结果与预设不同的,阻塞赋值

module block_nonblock2 (
  input clk,
  input rst_n,
  input a,b,c,
  output reg [1:0] out
  );
  
  reg [1:0] d;
  
  always @(posedge clk or negedge rst_n)
  if (!rst_n)begin
     out=2'b0;
     d=2'b0;
     end
  else begin
     d=a+b;
     out=d+c;
     end
  endmodule

我们从底层电路角度进行分析:

从图中可看出,情况1和情况3的结果一致;情况2和情况4的结果一致。

下面展示出情况1和情况4(只存在非阻塞赋值和阻塞赋值的区别)的电路结构图:

情况1 非阻塞赋值,两层D触发器

情况4 阻塞赋值,一层D触发器

测试代码:

给出情况1的

//对情况1,非阻塞,d<=a+b;out<=d+c;
`timescale 1ns / 1ns
`define clk_period 20

module block_nonblock2_tb();
  reg clk;
  reg rst_n;
  reg a,b,c;
  wire [1:0] out;
  
  block_nonblock1 block_num(.clk(clk),.rst_n(rst_n),.a(a),.b(b),.c(c),.out(out));
  
  initial clk=1;
  always #(`clk_period/2) clk=~clk;
  
  initial begin
    rst_n=1'b0;
    a=0;
    b=0;
    c=0;
    #(`clk_period*200+1);
    rst_n=1'b1;
    #(`clk_period*200);
    a=0;b=0;c=0;
    #(`clk_period*200);
    a=0;b=0;c=1;    
    #(`clk_period*200);
    a=0;b=1;c=0;
    #(`clk_period*200);
    a=0;b=1;c=1;
    #(`clk_period*200);
    a=1;b=0;c=0;   
    #(`clk_period*200);
    a=1;b=0;c=1;
    #(`clk_period*200);
    a=1;b=1;c=0;  
    #(`clk_period*200);
    a=1;b=1;c=1;
    #(`clk_period*200);
    #(`clk_period*200);
    $stop;
    end

endmodule

仿真波形:

情况一的仿真波形

Tip:

有个疑问,时钟上升沿的时候,d和out的值到底是0还是1?

解决方法:

代码更改

模拟D触发器电路延时的设计代码

仿真结果:

out<#2 d+c

这与实际的电路中是一样的。

理想D触发器中,clk上升沿一到,D的值就赋给Q。但实际电路中,是有延时的。所以"out<#2 d+c"这样的写法可以模拟实际的电路的传输延迟,也可以方便看仿真波形。而#1没有实际的电路与之对应,软件在进行比特流生成布局布线的时候,直接忽视它,所以也不影响板级调试。

参考

夏闻宇《Verilog数字系统设计教程》

阻塞赋值和非阻塞赋值的深度理解-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值