赞
踩
FPGA初学者可能经常听到一句话:“时序逻辑电路,或者说用 <= 输出的电路会延迟(落后)一个时钟周期。”但在仿真过程中经常会发现不符合这一“定律”的现象–明明是在仿真时序逻辑,怎么输出不会落后一拍?
先来看一个简单的例子:把输入信号用时序逻辑电路寄存两次,即俗称的“打两拍”。Verilog代码如下:
module test( input clk, //系统时钟; input rst, //系统复位,高电平有效; input [1:0] in, output [1:0] out ); reg [1:0] in_r,in_rr; //分别打一拍、打两拍 assign out = in_rr; always@(posedge clk or posedge rst)begin if(rst)begin in_r <= 2'd0; //复位初始值 in_rr <= 2'd0; //复位初始值 end else begin in_r <= in; //输入打一拍 in_rr <= in_r; //输入打两拍 end end endmodule
然后再写个TB文件来仿真一下:
`timescale 1 ns/1 ns module tb_test(); //输入输出端口 reg clk; reg rst; reg [1:0] in; wire [1:0] out; //例化被测试模块 test u_test ( .clk (clk), .rst (rst), .in (in ), .out (out) ); //生成系统时钟,周期10ns; initial begin clk = 1; forever #5 clk = ~clk; end //生成复位信号 initial begin rst = 1; #25; rst = 0; end //生成输入信号(测试激励) initial begin in = 0; #30; repeat(8)begin //循环8次; #10 in = in + 1; //输入递增1 end $stop; //停止仿真 end endmodule
这段测试代码的测试逻辑是:在复位完成后,每10ns依次对输入信号in执行+1操作,观察打一拍信号in_r和打两拍信号in_rr的变化。来看下仿真结果:
那么,是什么导致仿真结果与预期目的不符?
在FPGA设计中所用的底层时序逻辑单元是D触发器(DFF,D Flip-Flop),在理想状况下,可以认为DFF的变化是瞬态的,即输出从0到1或者从1到0,都是在一瞬间完成。但在实际使用中,这种瞬态变化显然不可能存在,所以寄存器的输出必定需要一些时间,而这个时间就是Tco。
上图是一张DFF的非理想状态下的数据传输示意图,为此需要明确3个概念:
上面的概念理解两点即可:
接下来继续分析上面的仿真结果。
这样理解起来可能还是不够直观,没事,我们把代码做一些小小的改变:
in_r <= #1 in; //输入打一拍
in_rr <= #1 in_r; //输入打两拍
只是把输出语句加一个 “#1”,即输出会延迟1ns。聪明的你应该已经看出来了,这就是用来模拟Tco这个概念的。
继续看仿真结果,是不是一目了然?
所以现在我们清楚了,时序逻辑电路的输出根本就不会落后一个时钟周期,而只会落后一个Tco时间。二者之所以看上去会落后一个周期,完全是由于前级输出的Tco时间存在,导致后级电路在当前时钟上升沿无法采集到最新值,而只能采集到前级未变化的值!
上面的仿真还有个问题悬而未决,那就是in_r是in被寄存后的信号,为啥没有落后in一个时钟周期?问题出在仿真机制和TB文件中对in的赋值方式上。
在TB中,我们是这么对in赋值的:
#10 in = in + 1; //输入递增1
注意看,用的是阻塞赋值“ = ”,阻塞赋值“ = ”一般用来描述组合逻辑,而非阻塞赋值“ <= "则一般用来描述时序逻辑。
虽然输入信号in是我们构建的一个虚拟向量,但对于被测试模块来说,这个激励仍然被视作是来自于上级模块的输出,所以需要指明它到底是一个组合逻辑的输出值还是一个时序逻辑的输出值。
如果它是用“ = ”来描述的,那它就是来自组合逻辑,而组合逻辑的输出是不与时钟上升沿有关的,它的输出几乎就是瞬时完成的。如果它是用“ <= ”来描述的,那它就是来自时序逻辑,而时序逻辑的输出则会落后时钟上升沿一个Tco时间。
回到上面的仿真结果,由于信号in使用“ = ”来赋值,所以它的每一次更新都几乎与时钟上升沿同步,并不会有Tco时间的存在,每一个上升沿后级的in_r信号都能采集到最新的in值,所以二者并不会有一个周期的延迟。
假如我们把信号in改成“ <= ”这种赋值方式:
#10 in <= in + 1; //输入递增1
那么仿真结果就是这样了:
这与我们最初料想的一致:打一拍信号in_r落后输入信号in一个时钟周期,打两拍信号in_rr后输入信号两个时钟周期。
如果说还有问题的话,就是输入信号in的变化没有很好的体现Tco时间,所以再修改一下:
#10 in <= #1 in + 1; //输入递增1
嗯,这样就没问题了。由于采用了“#1”这种赋值方式来模拟Tco的存在,所以你应该再也不会搞错信号在哪个时钟沿采样和哪个时钟沿变化了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。