阻塞赋值(Blocking)
格式 b = a
特点:
- 赋值语句执行完后,块才结束;
- b的值在赋值语句执行完后立刻就改变;
- 可能产生意想不到的结果。
举例:
如果a=1,b=2,c=3;
在一个always模块里面
always@(posedge i_clk)
begin
b=a;
c=b;
end
上例中的 "always"块用了阻塞赋值方式。clk信号的上升沿到来时,将发生如下的变化:b马上取a的值,c马上取b的值(即等于a),生成的电路图如下所示只用了一个触发器来寄存器a的值,又输出给b和c。
得到的答案是,a=1,b=1,c=1
非阻塞赋值(Non Blocking)
格式: b<=a
特点:
- 块结束后才完成赋值操作;
- b的值并不是立刻就改变;
- 这是一种比较常用的赋值方法。(特别在编写可综合模块时)
举例:
如果a=1,b=2,c=3;
always@(posedge i_clk)
begin
b<=a;
c<=b;
end
上例中的"always"块中用了非阻塞赋值方式,定义了两个reg型信号b和c,clk信号的上升沿到来时,b就等于a,c就等于b,这里应该用到了两个触发器。请注意:赋值是在"always"块结束后执行的,c应为原来b的值,b为原来的a值。这个"always"块实际描述的电路功能如下图所示:
第二个块,得到的答案是,a=1,b=1,c=2
注意: 对于非阻塞赋值,c获得a的值,需要2个clk完成。第1个clk上升沿a的值赋给b,此时b的值还没有更新;第2个clk上升沿,b的值赋给c,此时c才能获得b更新后的值。
timescale
格式: `timescale time_unit / time_precision
`timescale 1ns / 1ps,
含义为:时延单位为1ns,时延精度为1ps。
假如我们延时x个时间单位,那延时的总时间time = x*time_unit,但最后真正延时的时间是根据time_precision对time进行四舍五入后的结果,如下面的代码所示。
举例:
`timescale 100ns / 10ns // 时间单位100ns,时间精度10ns
module tb;
reg [4:0] set;
parameter d1 = 20,
d2 = 1.5,
d3 = 1.54,
d4 = 1.55;
initial begin
#d1 set = 0; // real_delay = round(20*100) = 2000ns, 以10ns为精度做四舍五入
#d2 set = 1; // real_delay = round(1.5*100) = 150ns
#d3 set = 2; // real_delay = round(1.54*100) = 150ns
#d4 set = 3; // real_delay = round(1.55*100) = 160ns
end
endmodule
注意事项:
- 时间单位和时间精度只能是1、10和100这三种整数,单位有s、ms、us、ns、ps和fs;
- 时间精度必须小于等于时间单位
- 时间精度会影响仿真时间,时间精度设置得更大,占用更多的内存,仿真时间更久
电路延迟
电路的延迟特性分为惯性延迟和传输延迟,
内定延迟和正规延迟
延迟的添加方法有两种:
- 内定延迟或者正规延迟
内定延迟:C = #2 A+B;(#号在里面)
正规延迟:#2 C = A+B;(#号在外面)
内定延迟可以理解为排队取票
正规延迟理解为排队买票
赋值的方法有三种:
- 连续赋值、阻塞赋值和非阻塞赋值
理论的组合方式就有6种,阻塞赋值和非阻塞赋值均为过程赋值。
1. 连续赋值+正规延迟
正规延迟(B):任何小于延迟值的输入变化都会被滤除而不会体现在输出上(和后面的不计算有区别)。
内定延迟+连续赋值因为存在记忆效应,所以是非法的。
**结论:**用来描述电路的惯性延迟。
2. 阻塞赋值 不建议加延迟
内定延迟(B):always触发-计算-延迟-输出-等待再次触发。
正规延迟(C):always触发-延迟-计算-输出-等待再次触发。
缺点:忽略延迟时间段内的数据变化。
结论:既不能模拟惯性延迟、也不能模拟传输延迟。不建议在阻塞赋值中插入延迟。
3. 非阻塞赋值+内定延迟
内定延迟(B):输入的变化延迟后会全部反应在输出上。
正规延迟(C):效果与阻塞赋值相同,会忽略延迟时间段内的数据变化。
**结论:**非阻塞赋值+内定延迟的方式可以描述传输延迟,这是过程赋值中唯一推荐的延迟描述。
赋值使用建议
阻塞赋值适用于组合逻辑
对于全加器
阻塞赋值能很好的反映了组合逻辑中的数据流动顺序。
非阻塞赋值
非阻塞赋值对于左边赋值变量的更新操作的优先级要低于阻塞赋值,也要低于非阻塞赋值本身等号右边的表达式计算,需要等到当前仿真周期结束时才被执行。
X异或Y首先计算,但是所得的结果并不会立刻赋值给temp1,所以第二句中引用的是temp1的原值,不是第一句中X与上Y的结果。
不能反映组合逻辑中的数据流,除非将所有的中间变量都列入敏感事件表中。
结论: 在对组合逻辑建模时采用阻塞赋值。
非阻塞赋值适用于时序逻辑
当对变量的赋值与读取不在同一个always进程时,使用非阻塞赋值对变量赋值.
如果都使用阻塞赋值,则拥有相同的优先级,由于仿真器的执行顺序是随机的,有的仿真器会按照代码的先后顺序执行,有的则会按照随机顺序执行。
这种仿真语义的不确定性会带来综合结果的不确定性,
但是设计者撰写这段代码所希望得到的硬件结构确是一定的:希望以temp作为中间变量,用CLK时钟将X异或Y的结果同步后再打一拍赋值给XxorY,硬件电路相当于两个D触发器的串联,可以看作是一个2位的移位寄存器.
当CLK同步变化时,虽然两个D触发器被同时启动,但是由于输入到输出的信号建立需要一定的时间,所以触发器XxorY得到的是触发器temp输出的原值,从而使XxorY在两个周期后得到X异或Y的结果。
单个仿真语句之间的时延
initial 的时延
ref
https://blog.csdn.net/reborn_lee/article/details/82222665
https://blog.csdn.net/fzr_en/article/details/89552323
https://blog.csdn.net/kevindas/article/details/86777477