赞
踩
时序逻辑电路的延一拍
“同步”是和工作时钟同步的意思,当时钟的上升沿(也可以是下降沿)来到时检测到复位操作才有效,否则无效。
sys_rst_n 被拉低后 led_out 没有立刻变为 0,而是当 syc_clk 的上升沿到来的时候 led_out 才复位成功,在复位释放的时候也是相同原因。
“异步”是和工作时钟不同步的意思,寄存器的复位不关心时钟的上升沿来不来,只要有检测到按键被按下,就立刻执行复位操作。
sys_rst_n 被拉低后 led_out 立刻变为0,而不是等待 syc_clk 的上升沿到来的时候 led_out 才复位,而在复位释放的时候 led_out不会立刻变为 key_in 的值,因为还要等待时钟上升沿到来到时才能检测到 key_in 的值,此时才将 key_in 的值赋值给 led_out。
区别主要就是复位有效的条件是“立刻”执行还是等待“沿”再执行的区别。
而复位释放之后,都是等待时钟上升沿才有效。这与D触发器的特性一致。
同步:always@(posedge sys_clk)
异步:always@(posedge sys_clk or negedge sys_rst_n)
使用 Intel(Altera)芯片时最好使用异步复位(如果是Xilinx 的芯片则推荐使用同步复位,Xilinx 的 UltraFas 方法学则推荐使用局部复位或最好不使用复位),这样子就可以节约更多的逻辑资源,更利于时序的收敛,之所以会有这样的差异是由于 FPGA 内部结构决定的。
上面两个图最左边的一组红色竖线。key_in 在复位后的第一个时钟的上升沿来到时拉高,我们可以发现此时 led_out 并没有在同一时刻也跟着拉高,而在之前的组合逻辑中输出是在输入变化的同一时刻立刻变化的。
当表达时序逻辑时如果时钟和数据是对齐的,则默认当前时钟沿采集到的数据为在该时钟上升沿前一时刻的值;当表达组合逻辑时如果时钟和数据是对齐的,则默认当前时钟沿采集到的数据为在该时钟上升沿同一时刻的值。
理解延一拍:
是当时钟上升沿来临时,左边的值更新是以上升沿之前右边的值更新的。不管同步还是异步D触发器,时序逻辑电路都有延一拍的特点。
只有一个步骤,即计算赋值号右边的语句并更新赋值号左边的语句,此时不允许有来自任何其他 Verilog 语句的干扰,直到现行的赋值完成时刻,即把当前赋值号右边的值赋值给左边的时刻完成后,它才允许下一条的赋值语句的执行
//阻塞赋值 module blocking ( input wire sys_clk , input wire sys_rst_n , input wire [1:0] in , output reg [1:0] out ); reg [1:0] reg_in; //in_reg:给输入信号打一拍 //out:输出控制一个 LED 灯 always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) begin reg_in = 2'b0; out = 2'b0; end else begin reg_in = in ;//延一拍,只有复位结束并且下一个时钟上升沿到来才会赋值 out = reg_in ;//没有延一拍,阻塞赋值,只要右侧表达式有变化,左侧跟着变化 end end endmodule
`timescale 1ns/1ns module blocking_tb(); reg sys_clk ; reg sys_rst_n ; reg [1:0] in ; wire [1:0] out ; //初始化系统时钟、全局复位和输入信号 initial begin sys_clk = 1'b1; sys_rst_n <= 1'b0; in <= 2'b0; #20 sys_rst_n <= 1'b1; end //模拟系统时钟 always #10 sys_clk = ~sys_clk; //in:产生输入随机数,模拟按键的输入情况 //取模求余数,产生非负随机数 0、1、2、3,每隔 20ns 产生一次随机数 always #20 in <= {$random} % 4; //------------------------blocking_inst------------------------ blocking blocking_inst ( .sys_clk ( sys_clk ), .sys_rst_n ( sys_rst_n ), .in ( in ), .out ( out ) ); endmodule
输入信号 in 和中间变量 in_reg、输出信号out 的关系就是延迟一拍的关系。
首先中间变量 in_reg 一定要等待复位被释放后且第一个时钟上升沿来到时才会被赋值为输入信号 in 的值,所以会比输入信号 in 延迟一拍,而中间变量 in_reg 和输出信号 out 却没有延迟一拍的关系了,而是在同一时刻同时变化的,因为我们使用的是阻塞赋值,也就是说只要赋值号右边的表达式的值有变化,赋值号左边的表达式的值也将立刻变化。
非阻塞操作开始时计算非阻塞赋值符的赋值号右边的语句,赋值操作结束时刻才更新赋值号左边的语句,可以认为是两个步骤(赋值开始时刻和结束时刻)来完成非阻塞赋值。
//非阻塞赋值 module nonblocking ( input wire sys_clk , input wire sys_rst_n , input wire [1:0] in , output reg [1:0] out ); reg [1:0] reg_in; //in_reg:给输入信号打一拍 //out:输出控制一个 LED 灯 always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) begin reg_in <= 2'b0; out <= 2'b0; end else begin //非阻塞赋值不是顺序执行,阻塞赋值才是 reg_in <= in ;//延一拍 out <= reg_in ;//也有延一拍,虽然右边的值变化了,但要等到下一次时钟上升沿变化才会赋值给左边 end end endmodule
`timescale 1ns/1ns module nonblocking_tb(); reg sys_clk ; reg sys_rst_n ; reg [1:0] in ; wire [1:0] out ; //初始化系统时钟、全局复位和输入信号 initial begin sys_clk = 1'b1; sys_rst_n <= 1'b0; in <= 2'b0; #20 sys_rst_n <= 1'b1; end //模拟系统时钟 always #10 sys_clk = ~sys_clk; //in:产生输入随机数,模拟按键的输入情况 //取模求余数,产生非负随机数 0、1、2、3,每隔 20ns 产生一次随机数 always #20 in <= {$random} % 4; //------------------------blocking_inst------------------------ nonblocking nonblocking_inst ( .sys_clk ( sys_clk ), .sys_rst_n ( sys_rst_n ), .in ( in ), .out ( out ) ); endmodule
输入信号 in 和中间变量 in_reg 是延迟一拍的关系,而中间变量 in_reg 和输出信号 out 也是延迟一拍的关系,也就是输入信号 in 和输出信号 out 一共是延迟两拍的关系。
in_reg 一定要等待复位被释放后且第一个时钟上升沿来到时才会被
赋值为输入信号 in 的值,所以会比输入信号 in 延迟一拍,这是和阻塞赋值过程相同的,但是接下来,因为我们使用的是非阻塞赋值,也就是说只要赋值号右边的表达式的值有变化,赋值号左边的表达式的值也不会立刻变化,需要等待下一次时钟沿到来时一起变化,所以最终结果是输出信号 out 相对于输入信号是打了两拍的关系。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。