锁存器 触发器 寄存器的关系以及modelsim仿真不延时打拍的问题
一、锁存器(D锁存器)
1、锁存器的结构
可以看出由四个与非门组成,第一个部分是由 G1、 G2 两个与非门组成的 RS 锁存器,第二个部分是由 G3、 G4 两个与非门组成的控制电路
D(data)为数据输入,C(control)为控制信号,Qn 是指当前逻辑状态也即触发前的状态, Qn+1 是指触发后的状态。
由1,0高低电平触发
2、锁存器是什么
锁存器(Latch)是一种对脉冲和电平敏感的存储单元电路,它们可以在特定输入脉冲或电平作用下改变
状态。锁存,就是把信号暂存以维持某种电平状态。锁存器在不锁存数据时,输出端的信号随输入信号变化, 一旦锁存信号起锁存作用,则数据被锁住, 输出信号不随输入信号的变化而变化。因此锁存器也称为透明锁存器,指的是不锁存时输出对输入是透明的。
3、什么情况下会产生锁存器
锁存器的分类包括 RS 锁存器、门控 RS 锁存器和 D 锁存器, 一般我们经常使用的是 D 锁存器, 此处我们详细介绍下 D 锁存器。
代码里面出现 latch 的两个原因是在组合逻辑中, if 或者 case 语句不完整的描述, 比如 if 缺少 else 分
支, case 缺少 default 分支,导致代码在综合过程中出现了 latch。解决办法就是 if 必须带 else 分支, case 必
须带 default 分支。
注意!只有不带时钟的 always 语句 if 或者 case 语句不完整才会产生 latch, 带时钟的语句if 或者 case 语句不完整描述不会产生 latch。
4、锁存器需要注意的点
- 代码结构要尽量减少锁存器latch的产生,锁存器对毛刺敏感,enable毛刺的变化会反映到输出端上
- 由之前结构分析我们可以看到,锁存器没有时钟信号,只有数据输入和使能以及输出 q 端,没有时钟信号也就说明我们没有办法对这种器件进行时序分析,这个在时序电路里面是非常危险的行为,因为可能引起时序不满足从而导致电路功能实现有问题。
- 锁存器数据采样条件为电位信号控制(电平)
二、触发器
1、触发器的结构
F1,F2分别是两个锁存器,下方是两个非门
假设CLK是一个上升沿,并且D为1
前半个周期CLK为0:F1中经过非门C1输入为1,1D输入为1,Q0输出1,F2中经过两个非门C1为0,Q1输出不受1D影响,保持。
后半个周期CLK为1:F1中经过非门C1输入为0,Q0保持输出1,F2中经过两个非门C1为1,1D等于Q0为1,Q输出1。
总结:
- 在 CLK 信号由 0 变为 1 这样的一个变化周期内,触发器的输出状态只可能改变一次。
- 触发器是同步时钟上升沿控制
三、寄存器
1、寄存器的结构
一个触发器可以组成一个一位的寄存器(一个触发器其实可以看作一个寄存器),多个触发器可以组成一个多位的寄存器,多位的寄存器可以存储多 bit 的二进制数据。
结构图
这是一个2bit寄存器,由两个1bit寄存器(即两个触发器)组成
通过上图,我们可以看到,第一个触发器的输入是 D0, F1 触发器的 D0 经过时钟上升沿采样后输出Q0, 输出的 Q0 为 F2 的输入。 由于 F2 的输入来自于上一个触发器的输出 Q0,因此 F2 的输出比 F1 的输出要晚一个时钟周期, 这个地方也就是通常所说的延迟一拍的概念, 在逻辑电路设计里面, 俗称“打一拍”,或者寄存一拍。
2、通过四个串联的寄存器理解打拍的概念
RTL电路
从绘制的波形图我们可以看出,因为是时序逻辑电路,所以输入 a 的值会延迟一个时钟周期赋值给
a_reg1。以此类推,每个寄存器的值都会延迟一个时钟周期赋值给下一个寄存器。
3、modelsim仿真中打拍失效的解决方法
有时在仿真中不会延迟一拍,因为现实中D端到Q端有延时,但是仿真中没有,所以在赋值时延迟一个单位时间
module shift_reg
(
input wire clk ,
input wire rst_n ,
input wire a ,
output wire y
);
reg a_reg1;
reg a_reg2;
reg a_reg3;
reg a_reg4;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
a_reg1 <= 0;
a_reg2 <= 0;
a_reg3 <= 0;
a_reg4 <= 0;
end
else begin
a_reg1 <= a ;
a_reg2 <= a_reg1 ;
a_reg3 <= a_reg2 ;
a_reg4 <= a_reg3 ;
end
end
endmodule
仿真文件
module tb_shift_reg();
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;
#200
sys_rst_n <= 1'b1;
#100
a <= 1'b1;
#100
a <= 1'b0;
end
always #10 sys_clk <= ~sys_clk;
shift_reg shift_reg_inst(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.a (a ),
.y (y )
);
endmodule
没有延迟打拍
module tb_shift_reg();
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//这里延迟一个时间单位在301ns会错过上升沿所以可以延迟一拍
sys_rst_n <= 1'b1;
#100
a <= 1'b1;
#100
a <= 1'b0;
end
always #10 sys_clk <= ~sys_clk;
shift_reg shift_reg_inst(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.a (a ),
.y (y )
);
endmodule
延迟了一拍
总结
- 寄存器是同步时钟信号控制上升沿采样
- 实际电路中会产生因为D端到Q端的时延产生延迟打拍现象,modelsim仿真中不会体现,需要手动延迟一个时间单位。
https://www.zhihu.com/question/279664328
https://www.zhihu.com/question/279664328