第四篇 锁存器、触发器、寄存器

2024-06-04 12:12

本文主要是介绍第四篇 锁存器、触发器、寄存器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

实验四 锁存器、触发器、寄存器

4.1 实验目的

  1. 理解时序逻辑电路的概念和基本原理;

  2. 掌握锁存器、触发器和寄存器的原理和架构及代码实现;

  3. 熟悉数字电路的设计、仿真流程,最后在DE1-SOC开发板上验证设计 。

4.2 原理介绍

4.2.1 时序逻辑电路

前三个实验中,我们学习了基本的逻辑门、多路数据选择器、译码器,这些电路都是组合逻辑电路,也就是任一时刻产生的输出信号仅仅取决于该时刻的输入信号的状态,与以前的输入信号无关。接下来我们将介绍时序逻辑电路的基本概念和原理。

在时序逻辑电路中,任一时刻的输出信号不仅取决于该时刻的输入信号,还取决于电路原来的状态,或者说,还与以前的输入信号有关。这样表述可能不好理解,我们先举一个实际的例子来说明一下。

有一个自动饮料售卖机,饮料10元,可投入货币有1元、5元、10元三种。假设你有两个5元,那么你需要投币两次,机器才能弹出饮料。当完成第一次投币后,机器记录你的总投币金额为5元;当完成第二次投币后,机器记录你的总投币金额为10元,此时机器会弹出饮料,并将总投币金额重置为0元。如图4.1所示为自动饮料售卖机的电路结构,它包含了两部分,一部分是加法器,用来计算总投币金额,另一部分是存储电路,用来记录每次投币后的总金额。

image-20210630114008496

图4.1 自动售货机电路

图4.2 时序逻辑电路的结构框图

通过这个例子我们可以看出,时序逻辑电路由组合逻辑电路和存储电路构成,并且存储电路是必不可少的。组合逻辑电路完成输入到输出的逻辑处理,处理的结果同时会进入存储电路中存储下来,等到下一个时刻再和输入信号一起决定组合电路的输出。

时序电路的存储电路一般由锁存器、触发器和寄存器构成。接下来我们将详细介绍这三个器件。

4.2.2 锁存器

锁存器(latch)是数字电路中的一种具有记忆功能的逻辑元件,可以记录二进制数字信号“0”和“1”。锁存器(latch)是电平触发的存储单元,数据存储的动作取决于输入时钟信号(或者使能)的电平值,即当锁存器处于使能状态时,输出才会随着输入数据发生变化。

锁存器分为基本SR锁存器、门控SR锁存器和D锁存器,接下来我们分别详细介绍这三种锁存器,首先介绍基本SR锁存器,它是各种锁存器、触发器电路的基本构成部分。

基本SR锁存器

图4.3(a)是一个基本SR锁存器,我们来分析它为什么会有记忆功能。如果只有一个或非门G_1,那它的输出会随着输入的变化而变化,因此不具备记忆功能。当我们用另外一个或非门G_2将v{O1}反相(同时将G_2的另一个输入端接低电平),则G_2的输出v{O2}将与v{I1}同相。再将v{O2}接回到G_1的另外一个输入端,这时,即使原来加在v{I1}输入端上的信号消失了,v{O1}和$v_{O2}$的状态也能保持下去,因此便可以记录“0”或“1”的状态。

image-20210630121602334

图4.3 用或非门组成的锁存器 (a)、(b)电路结构(c)逻辑符号

我们习惯上会把基本SR锁存器画成图4.3(b)中所示的对称形式,图4.3(c)为其逻辑符号。Q和\bar{Q}称为输出端,并定义Q=1、\bar{Q}=0为基本SR锁存器的1状态,Q=0、\bar{Q}=1为基本SR锁存器的0状态,S端称为置位端或置1输入端,R端称为复位端或置0输入端。

当S=1、R=0时,Q=1、\bar{Q}=0,在S=1信号消失后,由于有Q端的高电平接回到G_2的另一个输入端,因而电路的1状态得以保持,这也被称为基本SR锁存器的置1操作。

当S=0、R=1时,Q=0、\bar{Q}=1,在R=1信号消失后,电路保持0状态不变,也被称为基本SR锁存器的置0操作。

当S=R=0时,电路维持原来的状态不变。

当S=R=1时,Q=\bar{Q}=0,这既不是定义的1状态,也不是定义的0状态。而且,在S和R同时回到0以后无法断定锁存器回到1状态还是0状态。因此,在正常工作时,输入信号应遵守SR=0的约束条件,亦即不允许输入S=R=1的信号。

将上述逻辑关系列成真值表,就得到基本SR锁存器的特性表。Q是基本SR锁存器的当前状态,也叫现态,Q^*是指基本SR锁存器新的状态,也叫次态。

表4.1 基本SR锁存器的特性表

S

R

Q

Q*

基本SR锁存器状态

0

0

0

0

保持

0

1

1

1

1

0

0

1

置1

1

0

1

1

0

1

0

0

置0

0

1

1

0

1

1

0

0注

不确定

1

1

1

0注

注:S、R的1状态同时消失后状态不定。

我们还可以进一步画出基本SR锁存器的波形,如图4.4所示。从图中我们可以看出,S、R是基本SR锁存器的输入信号,Q、\bar{Q}是基本SR锁存器的输出信号,当S为高电平、R为低电平时,基本SR锁存器执行置1操作,即Q被置为高电平;当S为低电平、R为高电平时,基本SR锁存器执行置0操作,即Q被置为低电平。

image-20210701092744956

图4.4 基本SR锁存器的典型工作波形

门控SR锁存器

基本SR锁存器的输出状态是由输入信号S或R直接控制的,而门控SR锁存器在基本SR锁存器前增加了一对与门G_3、G_4,并用CLK信号控制锁存器在某一指定时刻根据S、R输入信号确定输出状态,如图4.5(a)所示。

当CLK=0,G_3、G_4关闭,S、R端的信号无法通过G_3、G_4而影响输出状态,故输出保持原来的状态不变;当CLK=1,G_3、G_4打开,将S、R端的信号传送到基本SR锁存器的输入端,使Q、\Q根据S、R信号而改变状态;

图4.3(b)是门控SR锁存器的逻辑符号,C1表示$CLK$是编号为1的一个控制信号,1S和1R表示受C1控制的两个输入信号,只有在C1为有效电平时(C1=1),1S和1R才能其作用。框图外部的输入端处没有小圆圈表示CLK以高电平为有效信号(如果在CLK输入端有小圆圈,则表示CLK以低电平作为有效信号)。

image-20210630122521726

图4.5 门控SR锁存器

表4.2为门控SR锁存器的特性表,由表可见,只有当CLK=1时,门控SR锁存器的输出端的状态才受输入信号的控制,而且其特性表与基本SR锁存器的特性表是一样的。同样的,门控SR锁存器也应当遵守SR=0的约束条件。

表4.2 门控SR锁存器的特性表

CLK

S

R

Q

Q*

门控SR锁存器的状态

0

x

x

0

0

保持

0

x

x

1

1

1

0

0

0

0

保持

1

0

1

1

1

1

1

0

0

1

置1

1

1

0

1

1

1

0

1

0

0

置0

1

0

1

1

0

1

1

1

0

0注

不确定

1

1

1

1

0注

注:CLK回到低电平后状态不定。

我们也可以进一步画出门控SR锁存器的波形,如图4.6所示。从图中我们可以看出,CLK是门控SR锁存器控制信号,S、R是门控SR锁存器输入信号,Q、\Q是门控SR锁存器的输出信号。当CLK为高电平时,若S为高电平、R为低电平,门控SR锁存器执行置1操作,即Q被置为高电平;当S为低电平、R为高电平时,门控SR锁存器执行置0操作,即Q被置为低电平;也就是说,当CLK为高电平时,门控SR锁存器的特性与基本SR锁存器的特性是一样的。当CLK为低电平时,门控SR锁存器保持原来的状态不变;

image-20210701093130925

图4.6 门控SR锁存器的工作波形

门控D锁存器

门控SR锁存器必须严格遵守SR=0的约束条件,这就为电路设计带来了很多不便。为了解决门控SR锁存器带来的问题(S、R不能同时为1),在图4.5(a)所示的电路的R输入端连接一个非门G_5,从而保证了S和R不同时为1的条件,其电路结构如图4.7(a)所示,它只有两个输入端:数据输入D和控制输入CLK,有两个输出端Q和\bar{Q},图4.7(b)是门控D锁存器的逻辑符号。

注意:这里的基本SR​锁存器是用与非门构成的,即当S_g=R_g=1时,电路保持原来的状态不变;S_g=0,R_g=1时,电路置1;S_g=1,R_g=0时,电路置0;正常工作时,不应出现S_g=R_g=0。

image-20210630135111939

图4.7 逻辑门控D锁存器

我们来分析一下门控D锁存器的工作原理。当CLK=0时,G_3、G_4输出均为1,使G_1、G_2构成的基本SR​锁存器处于保持状态,无论D信号怎样变化,输出Q和\Q均保持不变。当需要更新状态时,将CLK置1,此时,根据送到D端新的二值信息将锁存器置为新的状态:如果D=0,无论基本SR锁存器原来状态如何,都将使Q=0、\Q=1;反之,D=1,Q=1、\Q=0。总结来说就是,如果D信号在CLK=1期间发生变化,Q端信号跟随D信号变化;当CLK由1跳变为0后,锁存器将锁存跳变前瞬间D端的逻辑值。它的特性表如表4.3所示。

表4.3 门控D锁存器特性表

CLK

D

Q

Q*

门控D锁存器的状态

0

x

0

0

保持

0

x

1

1

1

0

0

0

置0

1

0

1

0

1

1

0

1

置1

1

1

1

1

图4.8为其波形图。从波形图中我们可以看出,CLK是门控D锁存器的控制信号,D是门控D锁存器的输入信号,Q是门控D锁存器的输出信号。当CLK为高电平时,Q跟随D的变化而变化,所以Q的波形等于D的波形;当CLK为低电平时,锁存器进入保持状态,Q的波形不会跟随D的变化而变化。

image-20210630141737633

图4.8 门控D锁存器的工作波形

门控D锁存器的Verilog HDL描述

下面我们来从实际的逻辑设计中看如何用Verilog HDL来描述门控D锁存器。

使用Verilog HDL描述门控D锁存器有两种方法,一种是根据图4.7的结构使用基本的逻辑门元件,采用结构描述的方法来实现;另外一种方法是采用功能描述,即不涉及实现电路的具体描述,靠“算法”实现电路。对于不太喜欢低层次硬件逻辑图的人来说,功能描述风格的Verilog HDL是一种最佳选择。在实际的逻辑设计中,大多数情况下,我们也是选用功能描述的方法来实现门控D锁存器。

下面给出两种方法描述逻辑门控D锁存器的Verilog HDL代码。

// 逻辑门控D锁存器的结构描述
module Dlatch_Structural (input   Clk,input   D,output  Q
);wire R_g, S_g, Qa, Qb /* synthesis keep */ ;
​
nand (S_g, D, Clk);
nand (R_g, ~D, Clk);
nand (Qa, S_g, Qb);
nand (Qb, R_g, Qa);
​
assign Q = Qa;
​
endmodule

代码4.1 逻辑门控D锁存器的结构描述

// 逻辑门控D锁存器的功能描述
module Dlatch_bh (input       Clk,input       D,output  reg Q
);
​
// gated D-latch
always @( * )if (Clk == 1'b1)Q = D;
​
endmodule

代码4.2 逻辑门控D锁存器的功能描述

在实际应用中,出现latch的两个原因: 在组合逻辑中,if或者case语句不完整的描述,比如if缺少else分支,case缺少default分支,都会导致代码在综合过程中出现latch。解决办法就是if必须带else分支,case必须带default分支。

需要注意的是,只有不带时钟的always语句的if或者case语句不完整才会产生latch, 带时钟的语句if或者case语句不完整描述不会产生latch,下面我们以if语句举例说明一下。

我们先来写一个不带else的always语句,代码如下:

module dlat(
input       en,
input       d,
output  reg q
);
​
always@(*)
beginif(en == 1)q = d;
end
​
endmodule

代码4.3 不带else的if语句

我们使用Quartus的RTL Viewer来看下综合后的电路结构,如图4.9所示,图中标识出了“LATCH”字眼,可以看出这个电路就是latch。

image-20210622114631243

图4.9 不带else的if语句的RTL Viewer

下面我们把if语句缺少的else补充完整再来看下电路结构,代码如下

module dlat(
input       en,
input       d,
output  reg q
);
​
always@(*)
beginif(en == 1)q = d;elseq = 1'b0;
end
​
endmodule

代码4.4 带else的if语句

我们再来看一下Quartus综合出来的电路结构,如图4.10所示,电路综合后是一个mux选择电路,可以看出,加了else分支后电路就不会综合成latch了。

image-20210622115200430

图4.10 带else的if语句的RTL Viewer

4.2.3 触发器

触发器(Flip-flop)也是数字电路中一种具有记忆功能的逻辑元件,可以记录二进制数字信号“0”和“1”。触发器是边沿敏感的存储单元,数据存储的动作由时钟信号的上升沿或下降沿进行同步。

根据逻辑功能,触发器分为SR触发器、JK触发器、D触发器、T触发器,实际应用中,D触发器使用的最为频繁,下面我们也是着重介绍D触发器。

D触发器

D触发器可以由两个门控D​锁存器级联构成,如图4.11(a)所示。图中左边的锁存器称为主锁存器,右边的称为从锁存器。主锁存器的时钟信号正好与从锁存器反相,利用两个锁存器的交互锁存,则可实现存储数据和输入信号之间的隔离。

当CLK处于低电平时,CLK_1为高电平,因而FF_1的输出Q1跟随输入端D的状态变化,始终保持Q_1=D。与此同时,CLK2为低电平,FF_2的输出Q_2(也就是整个电路最后的输出Q)保持原来的状态不变。

当CLK由低电平跳变至高电平时,CLK_1随之变成了低电平,于是Q_1保持为CLK上升沿到达前瞬间输入端D的状态,此后不再跟随D的状态而改变。与此同时,CLK_2跳变为高电平,使Q_2与它的输入状态相同。由于FF_2的输入就是FF_1的输出Q_1,所以输出端Q便被置成了与CLK上升沿到达前瞬间D端相同状态,而与以后D端的状态无关。

image-20210630142953413

图4.11 主从D触发器

图4.11(b)是D触发器的逻辑符号,用CLK输入端处框内的“->”表示触发器为边沿触发方式。在特性表中,则用CLK一栏中的“ \uparrow”表示边沿触发方式,而且是上升沿触发,如表4.4所示,如果是下降沿触发,则应在CLK输入端加小圆圈,并在特性表中用“\downarrow”表示。

表4.4 D触发器特性表

Clk

D

Q

Q*

x

x

x

Q

0->1

0

0

0

0->1

0

1

0

0->1

1

0

1

0->1

1

1

1

通过D锁存器和D触发器的学习,可能有些同学已经发现了,D锁存器和D触发器的逻辑功能其实是相同的,只不过它们的触发方式有所不同。接下来我们通过将D触发器的波形图与D锁存器的波形图进行比较,来看一看,它们的触发方式不同在哪里。

image-20210630143744972

图4.12 D锁存器和D触发器的波形对比图

我们先看D触发器的波形图(左),D触发器在CLK为0时,主锁存器接收D的值,并将这个值锁存起来;当CLK变为1时,Q才会改变为CLK由0变为1时D端的值,所以,D触发器是在CLK由0变为1的这个边沿触发的。

而D锁存器就比较简单,当CLK为0时,锁存器没有被使能,处于保持的状态,Q的值不会发生变化;当CLK为1时,锁存器被使能,Q端的值随D的变化而变化,所以,D锁存器是CLK为高电平触发的。

D触发器的Verilog HDL描述

与D锁存器类似,D触发器的Verilog HDL描述也是有两种方法,一种是根据图4.11(a所示的结构使用连续复制语句来实现,另外一种是使用功能描述风格,使用always和if-else语句对输出变量赋值,其中posedge代表上升沿,negedge代表下降沿。

下面给出两种方法描述D触发器的Verilog HDL代码。

module DFF_Structural(
input       clk,
input       d,
output      q
);
​
// wires declaration
wire q1, q2;
​
// module Dlatch_Structural (input Clk, D, output Q);
Dlatch_Structural   U1 (.Clk (~clk),.D   (d),.Q   (q1)
);
Dlatch_Structural   U2 (.Clk (clk),.D   (q1),.Q   (q2)
);
assign q = q2;
​
endmodule
​
​
// 逻辑门控D锁存器的结构描述
module Dlatch_Structural (input   Clk,input   D,output  Q
);wire R_g, S_g, Qa, Qb /* synthesis keep */ ;
​
nand (S_g, D, Clk);
nand (R_g, ~D, Clk);
nand (Qa, S_g, Qb);
nand (Qb, R_g, Qa);
​
assign Q = Qa;
​
endmodule

代码4.5 D触发器的结构描述

module DFF_Structural(
input       clk,
input       d,
output      q
);
​
// wires declaration
wire q1, q2;
​
// module Dlatch_Structural (input Clk, D, output Q);
Dlatch_Structural   U1 (.Clk (~clk),.D   (d),.Q   (q1)
);
Dlatch_Structural   U2 (.Clk (clk),.D   (q1),.Q   (q2)
);
assign q = q2;
​
endmodule
​
​
// 逻辑门控D锁存器的结构描述
module Dlatch_Structural (input   Clk,input   D,output  Q
);wire R_g, S_g, Qa, Qb /* synthesis keep */ ;
​
nand (S_g, D, Clk);
nand (R_g, ~D, Clk);
nand (Qa, S_g, Qb);
nand (Qb, R_g, Qa);
​
assign Q = Qa;
​
endmodule

代码4.6 D触发器的功能描述

从上述两段代码也可以看出,相比于结构描述方法,使用功能描述的方法实现D触发器简洁、高效、易懂、不易出错,这也是我们在实际的逻辑设计中采用的方法。

接下来,我们来介绍两种在逻辑设计中常用的D触发器。

带复位信号的D触发器

在设计中,我们常常会使用到复位信号来设定电路的起始值或作清除的动作,强迫电路进入某种特定的状态。D触发器根据复位的不同分为两种,一种是同步复位D触发器,另一种是异步复位D触发器,下面我们来分析两种D触发器的异同。

异步复位D触发器中的“异步”是和工作时钟不同步的意思,也就是说,寄存器的复位不关心时钟的有效沿来不来,只要检测到复位信号有效,就立刻执行复位操作。图4.13所示为有异步复位端的D触发器的逻辑符号,Rst_n代表低电平有效的复位信号,我们来结合波形图分析一下它的工作过程。在Rst_n被拉低后Q立刻变为0,而不是等待CLK的上升沿到来的时候$Q$才复位;而在Rst_n复位释放的时候,Q不会立刻变为D的值,因为还要等待时钟上升沿到来时才能检测到D的值,此时才能将D的值赋值给Q。

image-20210630144319958

图4.13 异步复位D触发器的逻辑符号

image-20210630144629915

图4.14 异步复位D触发器的波形图

代码4.7为Verilog HDL描述的带有异步复位的D触发器,always语句有两个触发条件,一个是时钟信号clk上升沿,一个是复位信号rst_n的下降沿,而从if-else语句可得知复位信号rst_n的优先级比时钟信号clk高。图4.15为代码4.x综合后的RTL Viewer,从中可以看到,复位信号rst_n连接到了D触发器的异步复位CLRN端。

module DFF_Asynchronous_Reset(
input       clk,
input       rst_n,
input       d,
output  reg q
);
​
always@(posedge clk, negedge rst_n)if(~rst_n)q <= 1'b0;elseq <= d;
​
endmodule

代码4.7 异步复位D触发器的Verilog HDL描述

image-20210624105558412

代码4.15 代码4.x的RTL Viewer

然后我们再来看同步复位D触发器,其中的“同步”是和工作时钟同步的意思,也就是说,复位信号只有在时钟的有效沿到来时,才能有效,否则,就无法完成复位工作。我们还是结合波形图来分析其工作过程。复位信号$rst_n$被拉低后$Q$没有立刻变为0,而是当时钟信号$clk$的上升沿到来的时候$Q$才复位成功。

image-20210701093419933

图4.16 同步复位D触发器的逻辑符号

image-20210630145004454

图4.17 同不复位D触发器的波形图

代码4.8为Verilog HDL描述的带有同步复位的D触发器,此时的always语句只有一个触发条件 -- 时钟信号clk的上升沿,也就是说,复位信号rst_n也要在时钟信号clk的上升沿的触发下才起作用。当rst_n=0,并且clk上升沿到来时,触发器的输出Q会被清零,当rst_n=1时,触发器可以正常工作。

module DFF_Synchronous_Reset(
input       clk,
input       rst_n,
input       d,
output  reg q
);
​
always@(posedge clk)if(~rst_n)q <= 1'b0;elseq <= d;
​
endmodule

代码4.8 同步复位D触发器的Verilog HDL描述

image-20210624105405067

代码4.18 代码4.x的RTL Viewer

通过上面异步复位D触发器和同步复位D触发器的RTL Viewer对比,我们可以发现,采用同步复位会多出一个选择器的结构,所以我们在使用Intel芯片时最好使用异步复位,这样就可以节约更多的逻辑资源。

有使能端的D触发器

在实际工作中经常会遇到这样的问题,在时钟有效沿到来时,即使触发器的输入信号发生变化,但仍然希望其中一些触发器能够保持现在的状态不变。解决这一问题的方法之一是使用带使能端的触发器,这种触发器广泛的应用在FPGA或CPLD中,图4.19是其逻辑符号,当EN=0时,触发器的状态不会发生变化;当EN=1时,触发器相当于普通D触发器。

image-20210701093532616

图4.19 异步复位、同步使能D触发器的逻辑符号

我们结合波形图来分析它的工作原理,当EN=1时,Q的数据随D的变化而变化,相当于普通的D触发器;而当EN=1时,无论D怎样变化,都不会影响Q,触发器的状态不发生变化,而只有等EN再次为1时,Q才会继续随着D的变化而变化。

image-20210630145516922

图4.20 异步复位、同步使能D触发器的波形图

以下是异步复位、同步使能D触发器的Verilog HDL描述。

module DFF_Asynchronous_Enable(
input       clk,
input        rst_n,
input       en,
input       d,
output  reg q
);always@(posedge clk, negedge rst_n)if(~rst_n)q <= 1'b0;else if(en)q <= d;endmodule

代码4.9 异步复位、同步使能D触发器的Verilog HDL描述

图4.21为异步复位、同步使能的D触发器的RTL Viewer。从图中我们也可以看出,FPGA中的D触发器是带有同步使能端的。

image-20210622140303309

图4.21 带异步复位、同步使能的D触发器RTL Viewer

4.2.4 寄存器

寄存器(register)是时序逻辑电路中最重要的逻辑单元。在实际的数字系统中,通常把能够用来存储一组二进制代码的同步时序逻辑电路称为寄存器。

图4.22给出了带异步复位、同步使能端D触发器组成的4位寄存器,它由4个带有异步复位、同步使能的D触发器构成。其中,D_3 \sim D_0是4位数据输入,当EN=1时,在时钟上升沿到来时,Q_3=D_3、Q_2=D_2、Q_1=D_1、Q_0=D_0,即输入数据D_3 \sim D_0同时存入相应的触发器;当EN=0时,在时钟上升沿到来时,输出端的状态保持不变。

image-20210630150434590

图4.22 4个D触发器组成的4位寄存器

我们还是结合波形图来分析它的功能。当寄存器复位时,寄存器的输出Q[3:0]=4'b000;当寄存器退出复位后,若EN=1,D[3:0]的数据在时钟信号CLK的上升沿存入Q[3:0],比如D[3:0]=4'b0001,那么在下一个时钟上升沿,Q[3:0]=4'b0001;若EN=0,D[3:0]中的数据就不会存入Q[3:0],此时Q[3:0]保持原来的状态不变,如图中所示,Q[3:0]保持为4'b0110;只有当EN重新回到1后,新的数据才会存入寄存器。

image-20210630150622126

图4.23 4位寄存器的波形图

图4.22所示的寄存器也是本实验所要实现的目标之一,为了避免重复,它的框图和Verilog HDL代码我们将在4.3.2节给出。

4.3 实验目标

  • 设计一个4位的、带有异步复位、同步使能的寄存器;

  • 并将其加入到实验3实现的计算器中,要求将计算器运算的结果先进行寄存,再译码显示到七段数码管。

4.4 设计实现

4.4.1 设计思路
系统框图

首先,我们要对实验有一个整体的认识,我们先来看一下实验工程的系统框图,如图4.24所示。本实验是在实验三的基础上,在数据选择的和七段数码管译码器之间加入一个4位的寄存器,将计算器的运算结果先进行寄存再显示。

image-20210630153315129

图4.24 在计算器中插入寄存器

寄存器的数据输入为多路数据选择的输出F,KEY1作为寄存器的使能控制信号,KEY0作为寄存器的异步复位信号,时钟信号由DE-Cloud开发板上的名为FPGA_CLK1_50的50MHz时钟输入,一个时钟周期后,寄存器的输出数据流入七段数码管译码器,译码器译码输出并将结果显示七段数码管上,同时寄存器的输出也会显示到LED3 \sim LEDR0上。

我们将实验的端口列表和功能总结成表4.5。

表4.5 实验4输入输出信号描述

信号名称

位宽

方向

功能描述

a

4-bit

Input

计算器的第一个操作数

b

4-bit

Input

计算器的第二个操作数

sel

2-bit

Input

计算器的功能选择信号

en

1-bit

Input

寄存器的同步使能信号

clk

1-bit

Input

寄存器的时钟信号

rst_n

1-bit

Input

寄存器的异步复位信号

hex0_out

7-bit

Output

七段数码管译码器的输出信号

ledr_out

4-bit

Output

寄存器的数据输出

子模块框图

根据系统框图我们可以看到,实验一共分为8个模块,每个模块的功能如下:

表4.6 实验4模块功能简介

模块名称

功能描述

c1

与运算模块

c2

或运算模块

c3

异或运算模块

c4

非运算模块

mux4x1

四选一数据选择器

reg4bits

4位寄存器

decod7seg

七段数码管译码器

calculator

计算器系统

c1 ~ c4、mux4x1、decod7seg模块在实验三中已介绍过功能,不再做过多解释,这里主要介绍reg4bits模块,其模块框图如图4.25所示。

image-20210630153459118

图4.25 reg4bits模块框图

表4.7 reg4bits模块信号描述

信号名称

位宽

方向

功能描述

en

1 bit

Input

寄存器的同步使能信号

reset_n

1 bit

Input

寄存器的异步复位信号

clk

1 bit

Input

寄存器的时钟信号

D

4 bit

Input

寄存器的数据输入信号

Q

4 bit

Output

寄存器的数据输出信号

reg4bits模块的功能是当en=1时,d[3:0]中的数据存到该寄存器中,当en=0,寄存器保持原有的数据不变。该寄存器的结构和波形图在4.1.4已经介绍过,这里不再赘述。

4.4.2 代码实现
  • reg4bits模块:这个模块是本实验的重点,本实验的最终要实现的计算器是在实验三的基础上加入了这个模块。

    module reg4bits(
    input               clk,        // 时钟信号50MHz
    input               rst_n,      // 异步复位信号,低电平有效
    input               en,         // 同步使能信号,高电平有效
    input       [3:0]   d,          // 并行输入数据
    output  reg [3:0]   q           // 并行输出数据
    );// 当检测到clk上升沿或rst_n下降沿时执行下面的语句
    always@(posedge clk, negedge rst_n)
    beginif(~rst_n)              // rst_n为低电平复位,且不需要等待clk上升沿到来后再复位q <= 4'b0000;else if(en)q <= d;elseq <= q;endendmodule

    代码4.10 reg4bits.v

    reg4bits子模块综合出来的RTL Viewer如图4.26所示。

    image-20210624122905599

图4.26 reg4bits模块RTL Viewer

  • calculator模块:完成reg4bits模块后,我们需要将其添加到实验三实现的计算器中,所以我们需要修改calculator.v文件,其中第72行 ~ 79行为主要的修改部分,主要是添加了reg4bits模块。

    module calculator(input           clk,        // 时钟信号,50MHzinput           rst_n,      // 异步复位信号input           en,         // 寄存器使能信号input   [3:0]   a,          // 计算器的第一个操作数input   [3:0]   b,          // 计算器的第二个操作数input   [1:0]   sel,        // 计算器的功能选择信号output  [3:0]   ledr_out,   // 寄存器的数据输出output  [6:0]   hex0_out    // 七段数码管译码器的输出
    );// 变量声明
    wire [3:0]  operator_a;
    wire [3:0]  operator_b;
    wire [1:0]  operation_s;
    wire        reg4bits_rst_n;
    wire        reg4bits_en;// 输入信号赋值
    assign  operator_a      =   a;
    assign  operator_b      =   b;
    assign  operation_s     =   sel;
    assign  reg4bits_rst_n  =   rst_n;
    assign  reg4bits_en     =   ~en;wire [3:0]  f1;
    wire [3:0]  f2;
    wire [3:0]  f3;
    wire [3:0]  f4;
    wire [3:0]  f;
    wire [3:0]  m;
    wire [3:0]  g;
    wire [6:0]  hex0dec_output;// 与运算
    c1  c1_inst(.a  (operator_a),.b  (operator_b),.f  (f1)
    );
    // 或运算
    c2  c2_inst(.a  (operator_a),.b  (operator_b),.f  (f2)
    );
    // 异或运算
    c3  c3_inst(.a  (operator_a),.b  (operator_b),.f  (f3)
    );
    // 非运算
    c4  c4_inst(.a  (operator_a),.f  (f4)
    );
    // 在f1,f2,f3,f4四种运算结果中,选择一个运算结果f
    mux4x1 mux4x1_inst(.in1  (f1),.in2  (f2),.in3  (f3),.in4  (f4),.sel  (operation_s),.out  (f)
    );// 将选出的运算结果f存入寄存器中
    reg4bits reg4bits_inst(.clk    (clk),.rst_n  (reg4bits_rst_n),.en     (reg4bits_en),.d      (f),.q      (g)
    );// 寄存器的输出数据g输入至数码管译码器,经译码后显示至七段数码管上
    decod7seg decod7seg_inst(.hex        (g),.display    (hex0dec_output)
    );// 寄存器的输出数据g直接显示在LED上
    assign  ledr_out    = g;
    assign  hex0_out    = hex0dec_output;endmodule

代码4.11 calculator.v

calculator模块综合出来的RTL Viewer如图4.27所示,可以看出这和我们的系统框图是一致的。

1670381415577

图4.27 calculator模块RTL Viewer

4.5 实验步骤

4.5.1 创建工程和代码输入

1. 点击电脑右下角的开始菜单找到Quartus软件,双击Quartus (Quartus Prime 17.1)打开Quartus Prime软件。

2. 点击菜单File-->New Project Wizard弹出工程创建的对话框。在弹出的对话框中点击Next。

​3. 在您的DE1-SOC 工作文件夹下创建一个lab4的文件夹,并将工程路径指向该文件夹,且工程的名称也命名calculator。如图4.28所示。

图4.28 lab4的工程创建

图4.29 创建lab4工程

4. 完成创建工程后,打开后的工程Quartus Prime工程界面如图4.30所示。

图4.30 创建lab4工程

5. 在Quartus工具栏依次点击File-->New,在New窗口中选择Verilog HDL File后点击OK按钮新建1个空白Verilog HDL文件,命名为reg4bits.v,并新建名为v的文件夹,将该Verilog HDL文件保存在v文件夹中。将代码4.10 复制添加到reg4bits.v文件中。

图4.31 新建reg4bits.v文件

6. 将实验3中的c1.v、c2.v、c3.v、c4.v、decod7seg.v、mux4x1.v、calculator.v文件复制到本实验工程目录的v文件夹下,如图4.32所示。

图4.32 复制文件至本实验的工程路径

7. 点击Quartus软件工具栏的Project --> Add/Remove Files in Project ...,将前面拷贝的文件添加至本实验的Quartus工程,如图4.33-1,4.33-2和4.33-3所示。

 图4.33-1

  图4.33-2

图4.33-3 成功添加文件至Quartus工程

8. 修改calculator.v文件,参考代码4.11,相对于实验3,主要添加了时钟、复位、使能信号以及寄存器模块。

图4.34 calculator.v

9. 点击Quartus软件工具栏的Processing --> Start --> Start Analysis & Synthesis或点击

image-20210603145513555

按钮对Verilog HDL代码执行语法检查和综合。如果在该过程中提示有错误,请检查Verilog HDL代码语法,确保与上述代码块完全一致。

​ 图4.35

图4.36 执行语法检查和综合

4.5.2 仿真

1. 点击Quartus工具栏File -->Open打开v文件夹的calculator_tb.v文件,并将里面的代码替换成如下4.13的代码。

`timescale 1ns/1ns
module calculator_tb();// 产生50MHz时钟信号
reg clk;
localparam  PERIOD = 20;                // 20ns = 50MHz
initial 
beginclk     =   1'b0;forever #(PERIOD/2) clk = ~clk;     // 每隔10ns,clk翻转一次
end// 产生复位信号,用作4位寄存器的异步复位
reg rst_n;
initial 
beginrst_n = 1'b0;#(PERIOD)   rst_n = 1'b1;
end// 产生计算器的两个操作数a, b、运算操作选择sel和4位寄存器的使能信号en
// 其中,为方便分析,运算操作选择sel固定为00,即只进行与运算
reg [3:0] a;
reg [3:0] b;
reg [1:0] sel;
reg       en;
initial 
begin#0a   = 4'd0;b   = 4'd0;sel = 2'b00;en  = 1'b1;#(PERIOD)a   = 4'd1;b   = 4'd1;sel = 2'b00;en  = 1'b1;#(PERIOD)a   = 4'd2;b   = 4'd2;sel = 2'b00;en  = 1'b1;#(PERIOD)a   = 4'd3;b   = 4'd3;sel = 2'b00;en  = 1'b0;#(PERIOD)a   = 4'd4;b   = 4'd4;sel = 2'b00;en  = 1'b0;#(PERIOD)a   = 4'd5;b   = 4'd5;sel = 2'b00;en  = 1'b1;#(PERIOD)a   = 4'd6;b   = 4'd6;sel = 2'b00;en  = 1'b1;#(PERIOD)a   = 4'd7;b   = 4'd7;sel = 2'b00;en  = 1'b1;$stop();
end// 例化 calculator
wire    [3:0]   ledr_out;
wire    [6:0]   hex0_out;
calculator calculator_inst(.clk        (clk),.rst_n      (rst_n),.en         (en),.a          (a),.b          (b),.sel        (sel),.ledr_out   (ledr_out),.hex0_out   (hex0_out)
);endmodule

代码4.13 calculator_tb.v

图4.37 修改仿真代码

  1. 点击Quartus工具栏Assignments --> Settings --> EDA Tool Settings --> Simulation,设置Quartus自动调用ModelSim仿真软件,并设置test bench为calculator_tb,设置完成后如图4.38所示。

    图4.38 设置test bech为calculator_tb

    其中,添加Compile test bench的具体步骤如下。

    图4.39 设置test bech的具体步骤

  2. 点击Quartus工具栏Tools --> Run Simulation Tool --> RTL Simulation启动ModelSim仿真,再点击Wave切换到仿真波形窗口,仿真波形如图4.40所示。

    图4.40 calculator仿真波形

仿真波形分析

为了方便分析,在编写仿真文件时,将sel固定为00,即计算器只进行与运算;

  • 0 ~ 20ns,a=b=(0000)~2~,经过与运算后,经数据选择器后输出m=(0000)~2~,此时,由于rst_n=0,所以寄存器处于复位状态,ledr_out=(0000)~2~;

  • 20ns时刻,a=b=(0001)~2~,则m=(0001)~2~,此时,rst_n上的低电平撤销,寄存器恢复正常状态,但此时clk上升沿还没有到来,所以ledr_out=(0000)~2~;

  • 30ns时刻,clk上升沿到来,并且en=1,所以寄存器的输出ledr_out=(0001)~2~,并且七段数码管译码器的输出hex0_out=(1111001)~2~,即数码管显示十进制数“1”;

  • 40ns时刻,a=b=(0010)~2~,m=(0010)~2~;

  • 50ns时刻,clk上升沿到来,en=1,所以ledr_out=(0010)~2~,hex0_out=(10100100)~2~,即数码管显示十进制数“2”;

  • 60ns时刻,a=b=(0011)~2~,m=(0011)~2~;

  • 70ns时刻,clk上升沿到来,而en=0,所以ledr_out和hex0_out保持不变,即数码管仍旧显示十进制数“2”;

  • 110ns时刻,clk上升沿到来,并且en恢复为高电平,所以ledr_out=(0101)~2~,hex0_out=(0010010)~2~,即数码管显示十进制数“5”;

综合上述的仿真数据,我们看到,只有到clk上升沿到来,并且en为高电平时,运算的结果才会显示到数码管上,这与我们预期的功能一致。

4.5.3 引脚分配、全编译与烧录

1. 点击Quartus菜单Assignments——Pin Planner。

图4.41

关于引脚分配信息可以查看DE1-SoC_v.5.1.3_HWrevF.revG_SystemCD\UserManual\DE1-SoC_User_manual.pdf第 22、25、26、28页或者E:\CD_Package\01-DE1-SoC\DE1-SoC_v.5.1.3_HWrevF.revG_SystemCD\Schematic\DE1-SoC.pdf的第3页。 

 图4.42

图4.43

图4.44

 图4.45

 图4.46

这里,a到b以及sel可以通过拨码开关SW0到SW9来控制,out信号分别输出到LEDR0到LEDR3,hex0_out输出到数码管0,en和rst_n连接到按键key1和key0。

图4.47

2. 点击Quartus软件工具栏的Processing --> Start Compilation或点击

image-20210603145755433

按钮编译lab4工程,并检查是否生成calculator.sof。

图4.48 编译工程

  1. 点击Quartus软件工具栏的Tools --> Programmer

    image-20210603150014201

    按钮打开Programmer窗口,点击Hardware Setup选择USB-Blaster[USB-0],点击Auto Detect选择5CSEBA6器件,左键单击选中5CSEBA6器件再点击Change File,选择calculator.sof,勾选program/configure,点击Start下载calculator.sof。

     图4.49

      图4.50

      图4.51

    图4.52 烧录calculator.sof成功

4.5.4 实验现象观察

1. 在lab4中,使用的KEY、SW、LEDR和HEX如下图所示。

图4.53 lab4中KEY、SW、HEX、LEDR对应关系

2. 通过切换滑动开关SW9-0到 up 或 down 位置,点击KEY0,观察LEDR3-0的状态以及HEX0的显示来测试设计的功能。

a. 先按一次KEY0,寄存器异步复位,寄存器中的数据被清0,LEDR3 ~ LEDR0全部熄灭,HEX0显示十六进制数字“0”;

b. 拨动SW9 ~ SW8为“down、down”,SW3 ~ SW0为“down、down、down、up”,SW7 ~ SW4为“down、down、down、up”;按一次KEY1LEDR3 ~ LEDR0显示为“熄灭、熄灭、熄灭、点亮”,HEX0显示十六进制数字“1”;

即:a = (0001)2,b = (0001)2,a & b=(0001)2=(1){16};

c. 拨动SW9 ~ SW8为“down、down”,SW3 ~ SW0为“down、down、up、down”,SW7 ~ SW4为“down、down、up、down”;按一次KEY1LEDR3 ~ LEDR0显示为“熄灭、熄灭、点亮、熄灭”,HEX0显示十六进制数字“2”;

即:a = (0010)2,b = (0010)2,a & b=(0010)2=(2){16};

d. 此时,按一次KEY0,寄存器异步复位,寄存器中的数据被清0,LEDR3 ~ LEDR0全部熄灭,HEX0显示十六进制数字"0";

e. 拨动SW9 ~ SW8为“down、down”,SW3 ~ SW0为“down、down、up、up”,SW7 ~ SW4为“down、down、up、up”;按一次KEY1LEDR3 ~ LEDR0显示为“熄灭、熄灭、点亮、点亮”,HEX0显示十六进制数字“3”;

即:a = (0011)2,b = (0011)2,a & b=(0011)2=(3){16};

4.6 实验小结

本章首次讲解了 FPGA 中的时序逻辑,这也是我们正式学习、认识 FPGA 的一个新开始,因为用 FPGA 设计的大型电路几乎都是以时序逻辑为主的,我们也会在今后更加看到FPGA 大显身手的时刻。

本章中我们还对比了D锁存器和D触发器在触发方式上的博通,同步复位D触发器和异步复位D触发器在波形、代码编写、上的不同,并给出了推荐的用法,希望大家能够在以后的设计中加以规范,熟练使用。在代码的设计上要深刻体会组合逻辑和时序逻辑的差别,特别是在用always块描述时,要理解电平触发和沿触发的区别,注意敏感列表的不同。

这篇关于第四篇 锁存器、触发器、寄存器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1030052

相关文章

神经网络第四篇:推理处理之手写数字识别

到目前为止,我们已经介绍完了神经网络的基本结构,现在用一个图像识别示例对前面的知识作整体的总结。本专题知识点如下: MNIST数据集图像数据转图像神经网络的推理处理批处理  MNIST数据集          mnist数据图像 MNIST数据集由0到9的数字图像构成。像素取值在0到255之间。每个图像数据都相应地标有“7”、“2”、“1”等数字标签。MNIST数据集中,

Modbus初学者教程,第三章:modbus寄存器说明

第三章:modbus寄存器说明 寄存器种类 Modbus协议中一个重要的概念是寄存器,所有的数据均存放于寄存器中。Modbus寄存器是指一块内存区域。Modbus寄存器根据存放的数据类型以及各自读写特性,将寄存器分为4个部分,这4个部分可以连续也可以不连续,由开发者决定。寄存器的意义如下表所示。 Modbus协议定义了设备间的数据传输方式,包括数据格式和通信规则。Modbus寄存器是协议中用

读线圈和离散状态寄存器信息

一.功能码操作类型 二.读线圈状态 需求实例 读取设备地址为 3 的从设备的线圈状态寄存器,线圈地址为 19 到 55(从 0 开始计算)共 37 个状态。 分析:由需求可知读取地址,则功能码是0x01,地址为3即为0x03,线圈地址为19到55则起始地址为19,即0x13,数量为37,即是0x25,查询报表如下所示: 假设从设备的状态值如下: 对应的响应包如下: 使

STM32读写备份寄存器和实时时钟

文章目录 1. 硬件电路 2. RTC操作注意事项 操作步骤 3. 代码实现 3.1 读写备份寄存器 3.1.1 main.c 3.2 实时时钟 3.2.1 MyRTC.c 3.2.2 MyRTC.h 3.2.3 main.c 1. 硬件电路 对于BKP备份寄存器和RTC实时时钟的详细解析可以看下面这篇文章: STM32单片机BKP备份寄存器和RTC实时时钟详解

MySQL【触发器、存储过程、函数、范式】

day53 MySQL 触发器 创建触发器:(before : 前置触发器、after :后置触发器) 语法:delimiter xx 指定分隔符xxcreate trigger 触发器名 [before | after] 触发事件 on 表名 for each row 执行语句begin多条执行语句end 加上 “结束符xx”delimiter ;还原分隔符未; 注意: 由某个

第四篇 CentOs7下安装Zabbix

这篇文章是源码安装方式,yum安装方式请参照以下几篇文章 CentOs7下Zabbix安装教程——准备工作 CentOs7下Zabbix安装教程——zabbix server安装 CentOs7下Zabbix安装教程——zabbix agent安装和前端配置 我这里是使用源码来安装zabbix的,系统是CentOs7,zabbix版本是3.2.1 zabbix的安装需要LAMP环境

Zabbix 值匹配字符串 创建触发器

Zabbix监控脚本返回值是字符串时,也可以使用字符串函数来创建触发器。举个栗子,现在有个需求要监控从服务器上下载数据是否出现异常,当数据下载失败时返回异常并告警。那么就可以在监控脚本中设置当下载成功时返回值为”download complete”,下载失败时返回值为异常信息。 创建监控项时设置返回值为字符型。 创建触发器 选择当存在匹配值时,则返回1否则返回0 设置匹配值V为do

zabbix触发器和监控项设置监控时间范围

触发器配置监控时间范围 比如现在配置在晚上十二点到凌晨一点半的时候触发。 创建一个触发器,选中一个你要是用的监控项比如我这里的监控项X,点击表达式构造器。 打开表达式构造器后,点击编辑,再次选择监控项X,在功能出选择当前时间小于N,然后N值出输入时间013000即可,01300表示凌晨一点半,152700表示下午三点27 选中确定之后,可以选择与或关系,这里我选择‘和’关系,关闭表

【PL/SQL】替换触发器的详解

替换触发器——instead of 替换触发器,它的触发时机是instead of,与其他类型触发器不同的是,替换触发器定义在视图(一种数据库对象)上的,而不是定义在表上。由于视图是由多个基表连接组成的逻辑结构,所有一般不允许用户进行DML操作(insert,update,delete等操作),这样当用户编写替换触发器后,对视图操作实际上就变成了执行触发器中的PL/SQL操作。先创建视图 cr

MySQL基础之触发器,函数,存储过程

目录 1 MySQL触发器 2 存储过程 2.1 创建存储过程 2.2 变量 2.2.1 存储过程内使用变量 2.2.2 赋值变量 2.2.3 变量作用域 2.3 查看并使用存储 2.3.1 查看调用 2.3.2 存储过程控制语句 2.4 修改删除存储 3 函数 3.1 创建函数 3.2 查看并使用 3.3 修改删除函数 3.4 自带基本函数 3.4.1 字符串类