目录
数字电路基础---寄存器
1、寄存器简介
1.1、寄存器是如何工作的
2、实验任务
3、程序设计
3.1、模块设计
3.2、绘制波形图
4、编写代码
5、仿真验证
5.1、编写 TB 文件
5.2、仿真验证
6、总结
7、拓展训练
寄存器是构成时序逻辑最重要的一个单元,可以说没有寄存器就没有时序逻辑。寄存器是由触发器组成的,一个触发器可以组成一个一位的寄存器,多个触发器可以组成一个多位的寄存器。
寄存器一般是由多个触发器构成的,所以在学习寄存器之前,我们先来了解一下触发器。
从 D 触发器的电路图中我们可以看出,该电路是由两个相同的 D 锁存器以及两个非门连接而成的,图中的 F1 和 F2 就是 D 锁存器的电路符号,F1 为主锁存器,F2 为从锁存器,由于主锁存器的输出信号 Q0 就是从锁存器的输入信号,因而造成了两个锁存器的主从关系,这两个锁存器的控制信号都由外部时钟信号 CLK 提供。
首先当 CLK=0 时,CLK 经过非门后直接作为 F1 的控制信号,那么此时 F1 的控制信号为 1,F1 被选通,F1 处于工作状态。如果现在输入信号 D 为 1 的话,它经过 F1,F1 的输出 Q0 就为 1,这里的 Q0 不仅是 F1 的输出信号,同时也是 F2 的输入信号。不过现在 F2 的控制信号为 0,因此 F2 现在被锁存了,处于保持状态。输入信号 D 没有办法直接改变输出 Q1 的状态,这是前半拍的工作情况,也就是说,输入信号先存入主锁存器中,但不直接影响输出 Q1 的状态。
接下来我们再来看后半拍,当外部的控制信号 CLK 由 0 变为 1 时,经过第一个作为 F1 控制信号的非门后,此时 F1 的控制信号将变为 0。主锁存器 F1 就被封锁了,它的输出 Q0 将保持在当前的状态,即使现在输入信号 D 再发生改变,Q0 的值也不再受影响了。经过两个非门后,F2 的控制信号 CLK 此时为 1,那么 F2 将处于工作状态。Q0 将会作为 F2 这个从锁存器的输入信号,直接影响到输出信号 Q1 的状态。当 Q0 为 1 时,那么根据 D 锁存器的逻辑规律,输出的 Q1 就为 1,F2 的 Q 非为 0,这就是后半拍的工作情况。在后半拍中我们才能实现整个电路状态的改变,因此从上面的分析中我们就可以看出,在 CLK 信号由 0 变为 1 这样的一个变化周期内,触发器的输出状态只可能改变一次。
在了解完触发器之后,我们再来看一下寄存器。一个触发器可以组成一个一位的寄存器(一个触发器其实可以看作一个寄存器),多个触发器可以组成一个多位的寄存器,多位的寄存器可以存储多 bit 的二进制数据。
如果多个触发器组成的寄存器输入都是相同信号,那么寄存器的输出也都是相同的信号,这种属于触发器并联。如果多个触发器组成的寄存器输入是互相传递的(下一个触发器的输入是上一个触发器的输出),那么寄存器的输出也都是不相同的信号,这种属于触发器串联。
下面我们先来看下触发器的并联,我们以 2bit 的寄存器为例子说明,如下图所示:
通过上图,我们可以看到,每个触发器的输入都是 D0,F1 触发器的 D0 经过时钟上升沿采样后输出是 Q0,F2 触发器的 D0 经过时钟上升沿采样后输出是 Q1。由于两个触发器的 D0 是来自同一个信号,所以每个触发器的 Q 端输出也是相同的。
下面我们来看下这并联下的 2bit 寄存器的状态表格。
由上表格我们可以看出,只有时钟上升沿,数据采样变化,其他时候数据是保持锁存状态。看完触发器的并联后,接下来我们再来看下触发器的串联,我们还是以 2bit 寄存器为例子说明, 如下图所示:
通过上图,我们可以看到,第一个触发器的输入是 D0,F1 触发器的 D0 经过时钟上升沿采样后输出是 Q0,输出的 Q0 为 F2 的输入。由于 F2 的输入来自于上一个触发器的输出 Q0,因此 F2 的输出比 F1 的输出要晚一个时钟周期,这个地方也就是通常所说的延迟一拍的概念,在逻辑电路设计里面,俗称“打一拍”,或者寄存一拍。
下面我们来看下这串联下的 2bit 寄存器的状态表格:
由寄存器的状态表可以看出,当 F1 的输入为 1 时,在时钟上升沿采样后,F1 的输出是 1,此时 F2 的输出还是 0,当下一个上升沿过去之后,F2 的输出才为 1。同理,如果更多的触发器依次串联,其实完成的是一个多级打拍的功能,也是逻辑电路里面移位的功能,就是一个信号在时钟上升沿跳变的时候依次传递到下一个寄存器中。
本节的实验任务是设计一个 4 级串联寄存器电路。
根据实验任务可知,我们需要设计一个 4 级串联寄存器的电路模块。它的输入端口为 a,输出端口为 y,在输入到输出之间使用 4 个触发器进行串联。而每个触发器都是需要时钟信号以及复位信号,所以我们还需要一个时钟端口以及一个复位端口。
模块端口与功能描述如下表所示:
根据模块框图以及端口功能的描述列表可以画出该模块的波形,波形如下图所示:
从绘制的波形图我们可以看出,因为是时序逻辑电路,所以输入 a 的值会延迟一个时钟周期赋值给 a_reg1。以此类推,每个寄存器的值都会延迟一个时钟周期赋值给下一个寄存器。
4 级的串联寄存器需要 4 个寄存器,每个寄存器都是需要有时钟,复位信号,输入和输出,根据波形图我们可以写出如下代码:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/06/19 11:56:45
// Design Name:
// Module Name: shfit_reg4
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//实验任务
//本节的实验任务是设计一个 4 级串联寄存器电路。
//4 级的串联寄存器需要 4 个寄存器,每个寄存器都是需要有时钟,复位信号,输入和输出
module shfit_reg4(
input sys_clk, //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
input sys_rst_n, //全局复位
input a, //输入a
output y //输出y
);
reg a_reg1; //寄存器1
reg a_reg2; //寄存器2
reg a_reg3; //寄存器3
reg a_reg4; //寄存器4
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
a_reg1 <= 1'b0;
a_reg2 <= 1'b0;
a_reg3 <= 1'b0;
a_reg4 <= 1'b0;
end
else begin
a_reg1 <= a;
a_reg2 <= a_reg1;
a_reg3 <= a_reg2;
a_reg4 <= a_reg3;
end
end
assign y = a_reg4;
endmodule
程序中第 33 行到第 36 行定义了 4 个寄存器,代码第 38 行到第 51 行将 4 个寄存器串联起来,第 53 行将 a_reg4 的值赋给输出 y。
我们可以使用 VIVADO 的 RTL ANALYSlS 来看一下 RTL 电路。
从上图可以看出,每个触发器都有 sys_clk 输入信号端口、一个异步清零端口、一个输出端口以及一个输出端口。我们代码中的输入 a 通过 a_reg1、a_reg2、a_reg3 以及 a_reg4 的串联输出到 y,从图中可以看出这个电路就是一个 4 级寄存器组成的移位寄存器。
下面我们编写一个 testbech 激励文件,通过仿真来看下移位寄存器的波形。仿真代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/06/19 14:15:30
// Design Name:
// Module Name: tb_shfit_reg4
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_shfit_reg4();
reg sys_clk;
reg sys_rst_n;
reg a;
wire y;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
a <= 1'b0;
#201
sys_rst_n <= 1'b1;
#100
a <= 1'b1;
#100
a <= 1'b0;
end
always #10 sys_clk <= ~sys_clk;
shfit_reg4 u_shfit_reg4(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.a (a ),
.y (y )
);
endmodule
编写完我们的仿真代码,就可以对仿真代码进行仿真的验证了。
我们打开 Modelsim 软件对代码进行仿真,在运行仿真 1us 后,仿真的波形如下图所示:
由上图可知,当输入信号 a 为 1 之后,在下一个时钟上升沿到来时,a_reg1 拉高,经过一个时钟后,a_reg2 拉高,再经过一个时钟后,a_reg3 拉高,在 a 拉高之后的第 4 个时钟,a_reg4 也由低变为了高,然后 a_reg4 的值是通过组合逻辑直接赋值给了 y 信号,因此 y 的波形和 a_reg4 也是完全一样的。可以看出触发器的采样都是在时钟上升沿进行的,其他时候触发器是保持之前采样的信号。另外从波形中,我们也能看到一个移位的效果,就是数据 1 依次移动到下一个触发器中。
本章主要为大家介绍了寄存器的相关概念,并且通过一个时序逻辑电路设计了一个移位寄存器。通过代码的编写以及仿真波形的观察,我们知道了带有时序逻辑的 always 语句块都具有延迟一拍的效果。在后续的学习与设计中我们要养成一个思维,看到 always 语句块下的时序逻辑电路,自然的想到会产生延迟一拍的现象,这个也就是人们常说的“打拍”操作。
我们学习并了解了锁存器和寄存器。可以对比一下锁存器以及寄存器到底有什么区别?锁存器最大的危害是什么?可以画一下寄存器和锁存器的波形图进行对比。