数电基础
时序逻辑电路会复杂很多,强烈推荐mooc上华中科技大学的数字电路与逻辑设计,是我看过讲得最清楚的数电课。
前几节都是组合逻辑电路,即输出只与当前输入有关,而与电路原来的状态无关。 组合逻辑最大的缺点就是会存在 竞争冒险 ,使用时序逻辑就可以极大地避免这种问题,从而使系统更加稳定。 时序逻辑电路有记忆的功能,含有存储电路。 其输出是输入及输出前一个时刻的状态的函数。 这里引入了现态和次态的概念,现态是当前时刻的状态,表示为Qn,而次态表示输入发生变化后其输出的状态 ,表示为Qn+1。 时序逻辑电路可以分为同步时序和异步时序,同步时序有统一的时钟,而异步时序的触发器状态的变化不是同一时间发生的。
时序逻辑最基本的单元就是寄存器,寄存器具有存储功能,一般是由D触发器构成,由时钟脉冲控制,每个D触发器(DFF)能够存储一位二进制码。
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按下去时为低电平灯亮。
全部0条评论
快来发表一下你的评论吧 !