FPGA时序逻辑电路寄存器讲解

描述

数电基础

时序逻辑电路会复杂很多,强烈推荐mooc上华中科技大学的数字电路与逻辑设计,是我看过讲得最清楚的数电课。

前几节都是组合逻辑电路,即输出只与当前输入有关,而与电路原来的状态无关。 组合逻辑最大的缺点就是会存在 竞争冒险 ,使用时序逻辑就可以极大地避免这种问题,从而使系统更加稳定。 时序逻辑电路有记忆的功能,含有存储电路。 其输出是输入及输出前一个时刻的状态的函数。 这里引入了现态和次态的概念,现态是当前时刻的状态,表示为Qn,而次态表示输入发生变化后其输出的状态 ,表示为Qn+1。 时序逻辑电路可以分为同步时序和异步时序,同步时序有统一的时钟,而异步时序的触发器状态的变化不是同一时间发生的。

时序逻辑最基本的单元就是寄存器,寄存器具有存储功能,一般是由D触发器构成,由时钟脉冲控制,每个D触发器(DFF)能够存储一位二进制码。

D触发器

D触发器是一种最简单的触发器。 D触发器的特点是:在时钟上升沿时,次态=输入D,在时钟处于高电平或低电平时,次态保持不变。 用表格表示:

时序

D 时钟 Qn Qn+1
0 上升沿触发 0/1 0
1 上升沿触发 0/1 1
X 0/1 Qn Qn

同步复位的D触发器

时序

当时钟的上升沿到来时,检测到按键的复位操作才有效,否则无效。 clk是时钟,rst_n是复位键(低电平有效),在前两条虚线中,key_in在时钟上升沿变为1,但是led_out不能立刻改变,而是在下一个时钟上升沿时变为key_in(Qn+=D)。 key_in的抖动也不会影响到led_out。 注意 :第五条虚线,sys_rst_n被拉低后led_out没有立刻复位变为0,而是当syc_clk的上升沿到来的时候(第六条虚线)led_out才复位成功,在复位释放的时候也是相同原因(第七条虚线)。

同步复位的D触发器

时序

异步的意思是和工作时钟不同步,只要有检测到按键被按下,就立刻执行复位操作。 第五条虚线,sys_rst_n被拉低后led_out立刻变为0,没有等时钟上升沿,复位释放时需要等待时钟上升沿才会为key_in。

同步和异步复位的D触发器区别只在于复位时需不需要等时钟上升沿。 他们的共同点是对于电路中产生的毛刺有着极好的屏蔽作用。

设计规划

本例中我们的目标和(一)中一样,点亮一个LED灯。 但是这里使用的D触发器,当按键被按下,key_in=0作为输入给触发器的D端口,然后在时钟上升沿时会被传送给输出led_out=0使LED灯被点亮。

编写代码

同步复位的代码

module flip_flop
(
input wire sys_clk , //系统时钟50Mh


input wire sys_rst_n, //全局复位


 input wire key_in , 


 output reg led_out 
 );
 always@(posedge sys_clk) 
 if(sys_rst_n == 1'b0) 
 led_out <= 1'b0; 
 else
 led_out <= key_in;
 endmodule

同步复位的特点是,复位时要等待上升沿,因此需要在上升沿时检测复位状态,这样就能保证复位信号在上升沿时才有效。 使用always语句,时钟上升沿时执行块中的判断语句,当复位信号为低电平时,LED灯点亮,否则,将key_in赋给led_out。 (四)中提到**always 时序逻辑块中多用非阻塞赋值<=。 **

异步复位的代码

module flip_flop
(
input wire sys_clk , //系统时钟50Mh
input wire sys_rst_n , //全局复位
input wire key_in , 
output reg led_out 
);


 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)//sys_rst_n为低电平时复位,且是检测到sys_rst_n的下
 //降沿时立刻复位,不需等待sys_clk的上升沿来到后再复位
 led_out <= 1'b0;
 else
 led_out <= key_in;


 endmodule

异步复位的特点是,复位时不需要等待上升沿。 当电路发生always语句()中的变化时,执行always块,由于复位不需要等待上升沿,这里发生变化的条件就包含时钟上升和复位有效。 当时钟上升或复位有效时,执行判断语句,如果复位键为低电平则LED输出低电平点亮,否则将key_in的值赋给led_out。

我们采用同步复位来演示。 将代码综合看RTL视图

时序

如果复位键为低电平,那么复位有效,0被传给触发器并输出,如果复位键为高电平,那么key_in的值被传给触发器并输出,与我们的设计含义一致。

编写testbench

`timescale 1ns/1ns
module tb_flip_flop();
reg sys_clk ;
reg sys_rst_n ;
reg key_in ;
wire led_out ;


 //初始化系统时钟、全局复位和输入信号
 initial begin
 sys_clk = 1'b1; 
 sys_rst_n <= 1'b0; 
 key_in <= 1'b0; 
 #20
 sys_rst_n <= 1'b1; //初始化20ns后,复位释放
 #210
 sys_rst_n <= 1'b0; //为了观察同步复位和异步复位的区别
 sys_rst_n <= 1'b1; //复位40ns后再次让复位释放掉
 end


 //sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
 always #10 sys_clk = ~sys_clk; 
 always #20 key_in <= {$random} % 2; 


 //------------------------------------------------------------
 initial begin
 $timeformat(-9, 0, "ns", 6);
 $monitor("@time %t: key_in=%b led_out=%b", $time, key_in, led_out);
 end
 //------------------------------------------------------------


 //------------------flip_flop_inst-------------------
 flip_flop flip_flop_inst
 (
 .sys_clk (sys_clk ), //input sys_clk
 .sys_rst_n (sys_rst_n ), //input sys_rst_n
 .key_in (key_in ), //input key_in
 .led_out (led_out ) //output led_out
 );


 endmodule

初始化:initial 块中时钟信号用阻塞赋值=,其他信号用非阻塞赋值。 初始时时钟为高电平,复位为低电平,key_in无所谓,此时输出不能确定。 延时20ns后复位释放,再延时210ns后再次复位, 这么做的目的是:由于时钟周期是20ns(后面的代码可以看出来),在时钟下降沿复位可以观察同步复位和异步复位的变化。 同步复位要等时钟上升沿才变化,异步复位是即刻复位。 再延时40ns后再次复位释放。

模拟时钟:每隔10ns翻转一次,周期为20ns。 模拟按键输入:每隔20ns产生一个0或1的随机数。 时间间隔应该小于等于时钟周期,否则会产生毛刺。

打印和实例化与之前的没有区别。

对比波形

时序

同步复位电路:最开始的输出不确定,20ns-210ns间,Qn+1=D,发现输出与上一时刻的输入相同,这是D触发器的特征,210ns时复位有效,但是同步复位要等下一个时钟上升沿,输出才会为0。 250ns复位释放,等到时钟上升沿260ns之后,输出才变为上一时刻的输入。

观察一下异步复位

时序

时序

分配管脚

不同开发板的管脚设置不同,需要看用户手册的介绍。 这里时钟周期是20ns,也就是50MHz的时钟晶振,选择E1管脚。

时序

时序

全编译后上板验证

用的异步复位,S0为key_in,S1为复位,LED0是输出。 当key_in不按时为高电平,灯也为高电平熄灭,当复位键按下时,即刻复位灯亮。 当key_in按下去时为低电平灯亮。

时序

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分