← 返回文章列表
Note

从两个反相器到 D 触发器:一位数据是怎样被记住的?

从反馈闭环开始,逐级理解 SR 锁存器、D 锁存器和主从触发器,以及透明窗口、时钟边沿与亚稳态。

数字电路不只需要计算,还需要记住计算的结果。加法器可以算出下一步的值,但如果没有存储状态的地方,计数器就不知道自己数到了几,状态机也不知道接下来该走哪一步。

那么,一个只能输出 0 或 1 的电路,是怎样拥有“记忆”的?既然锁存器已经能保存数据,为什么同步设计中又常常使用触发器?

沿着 反相器 → SR 锁存器 → D 锁存器 → D 触发器 走一遍,这两个问题其实是同一个故事:先让电路记住状态,再一步步控制它何时改变状态。

本文采用常见数字设计术语:Latch 指电平敏感锁存器,Flip-Flop 指边沿敏感触发器。讨论对象默认没有异步置位、复位等额外控制。

1. 两个反相器,为什么能记住 1 bit?

反相器的功能很简单:输入为 0,输出为 1;输入为 1,输出为 0。

把两个反相器首尾相连,让后一个的输出反馈给前一个,就得到一个闭环。

两个反相器构成的反馈闭环

将两个节点记为 Q 和 Q̅,它们满足:

Q = ¬Q̅
Q̅ = ¬Q

这个结构有两个稳定状态:Q = 0、Q̅ = 1,或者 Q = 1、Q̅ = 0。只要供电正常、没有外部扰动使其翻转,反馈就会维持当前状态,于是电路能够保存 1 bit。

但“能保持”还不等于“能可靠写入”。上电后究竟落在哪个状态,会受到器件差异、噪声和电源上升过程的影响。电路也可能在中间电平附近短暂停留,表现出亚稳态。

因此,我们还需要一个入口,能够主动告诉它:现在请记住 0,或者现在请记住 1。

2. 换成 NOR 门,让状态可以被写入

NOR 门,也叫或非门,其输出是两个输入“或”运算结果的取反:

Y = ¬(A ∨ B)

当 B = 0 时,它相当于反相器;当 B = 1 时,无论 A 是什么,输出都会被强制拉到 0。这个额外输入,恰好能用来打破反馈原有的状态。

用两个 NOR 门交叉连接,就得到高有效的 SR 锁存器。

高有效 NOR 型 SR 锁存器的交叉反馈结构

这里的 S 是 Set,表示置位;R 是 Reset,表示复位。注意,图中 R 直接控制输出 Q 的 NOR 门,S 直接控制输出 Q̅ 的 NOR 门。

SRQ 的结果操作
00保持原值记忆
101置位
010复位
11Q = Q̅ = 0禁止作为正常存储操作

例如,要写入 1,可以先令 S = 1、R = 0。下方 NOR 门的 Q̅ 被强制为 0,上方 NOR 门随之输出 Q = 1。随后让 S 回到 0,两个控制输入都不再施加强制作用,反馈仍能维持 Q = 1。

S、R 同为 1 时,两个输出都被压为 0,失去互补关系。若随后同时释放,最终状态可能取决于延迟差异,也可能经历亚稳态,因此不能把它当作正常的数据写入方式。

从反相器闭环到 SR 锁存器,我们完成了第一步:让记忆可以被外部控制。

3. D 锁存器:把两个控制输入变成一个数据输入

SR 锁存器需要分别管理 S 和 R,还要避开禁止组合。更自然的接口是:一个输入 D 表示想存的数据,另一个输入 EN 表示现在是否允许写入。

在 SR 锁存器之前增加组合逻辑:

S = EN ∧ D
R = EN ∧ ¬D

D 锁存器的数据与使能逻辑

当 EN = 0 时,S、R 都是 0,电路保持原值。EN = 1 时,D = 1 就置位,D = 0 就复位。在理想布尔模型下,S 和 R 不会同时为 1;真实门电路仍有路径延迟,不能仅凭这个式子就认为所有瞬态问题都已消失。

D 锁存器的行为可以概括为:

EN = 1:Q 跟随 D
EN = 0:Q 保持原值

关键词是 透明(Transparent)。EN 为高的整个时间窗口内,只要 D 改变,Q 就会在传播延迟之后跟着改变;EN 变低后,Q 才保存关闭前的数据。TI 的 SN74HC373 说明给出了这一透明锁存行为的器件实例。

所以,锁存器并不是“使能刚变高时采样一次”,而是“使能有效期间持续允许数据通过”。

4. 两个锁存器,怎样得到边沿触发?

如果希望数据只在时钟上升沿附近被接收,就需要把透明窗口进一步收束为一个明确的更新边界。

一种经典实现是主从结构:把两个 D 锁存器串联,让它们在相反的时钟电平下透明。以下讨论正沿触发的情况。

正沿主从触发器:主锁存器接反相时钟,从锁存器接原始时钟

当 CLK = 0 时,主锁存器打开,从锁存器关闭。 主锁存器的内部节点 M 跟随 D,但输出 Q 被从锁存器隔离,保持原值。

当 CLK 从 0 变为 1 时,主锁存器关闭,从锁存器打开。 主锁存器保存边沿附近满足时序要求的数据,从锁存器再把这个状态传到 Q。真实输出会经过时钟到输出延迟,不会在数学意义上的同一瞬间完成跳变。

当 CLK = 1 时,主锁存器保持关闭。 尽管从锁存器仍然透明,它接收到的 M 已经固定,因此 D 后续的变化无法一路传到 Q。

等到下降沿,从锁存器关闭、主锁存器重新打开,为下一次采样做准备。整体表现出来的,就是正沿触发的 D 触发器。SN74HC74是这类正沿触发行为的器件实例,其额外异步控制不在本文讨论范围内。

逻辑互补,还不够

在纸面上,CLK 与 ¬CLK 总是相反;在物理电路中,反相器、布线和晶体管都有延迟。如果从锁存器已经打开,主锁存器却还没有完全关闭,两者就可能短暂同时透明,让数据穿透。

主从使能重叠风险与非重叠控制示意

非重叠控制的基本思路,是先让一个锁存器关闭,再允许另一个打开,避免不恰当的共同透明区间。具体实现需要由电路拓扑和器件时序保证,不能只靠理想逻辑图判断。

主从结构适合理解工作原理,但并非所有触发器都由这两个功能块直接实现;实际还有其他拓扑,不能把这张图当作所有标准单元内部电路的统一模板。

5. 波形上的区别:一段窗口,还是一个边沿

把相同的 D 和时钟分别送给高电平透明锁存器、正沿触发器,两者的区别就很直观了。

相同输入下,D 锁存器与 D 触发器的输出波形比较

在第一个高电平窗口中,D 多次变化,锁存器输出也随之变化;触发器只接收该窗口起点上升沿附近的数据,之后保持到下一个有效沿。

这也回答了一个常见问题:“锁存器的问题,是不是采样时间太长?”更准确地说,透明窗口会改变数据能够穿过电路的方式,因此需要不同的时序组织。

考虑两级存储单元,中间夹着组合逻辑:

Q1 → 组合逻辑 → 第二级存储单元 → Q2

若两级都是同一时钟驱动的正沿触发器,在满足建立时间、保持时间并考虑时钟偏斜的前提下,第二级在当前沿接收的是边沿前的数据;第一级在该沿之后产生的新结果,通常到下一周期才被第二级接收。这样,状态更新就有了清晰的周期边界。

若两级都是同一高电平使能的锁存器,在使能有效时,它们会同时透明。只要路径延迟允许,数据就可能从第一级穿过组合逻辑,再进入第二级。这种 竞态穿透(Race-through) 可能破坏原本期望的分级保存行为。

触发器让这种阶段划分更直接,但它同样需要满足保持时间;不能因为用了触发器,就忽略过短的数据路径。

6. 锁存器仍有自己的用武之地

电平敏感不等于设计缺陷。采用正确的时钟相位安排与时序约束,锁存器也能构成流水线。

其中一个特点是 时间借用(Time Borrowing):在接收锁存器仍透明时,某一级数据可以晚于名义阶段边界到达,使用下一阶段的一部分时间。但借来的时间会减少后续阶段的余量,不能凭空增加整个流水线的时间预算。

对比项D 锁存器D 触发器
敏感方式电平敏感边沿敏感
数据何时能影响输出使能有效期间有效时钟沿附近采样,延迟后更新
时序组织需管理透明窗口、相位与穿透通常按时钟周期划分
时间借用可利用透明窗口实现传统固定边沿流水线不具备相同机制
面积与功耗部分实现中有优势,取决于库与设计取决于单元、时钟网络与架构
常规 FPGA 同步 RTL应避免无意推断通常作为基本状态单元

工程选择取决于目标器件、工具和时序方法。对多数常规 FPGA 同步逻辑来说,边沿触发器更容易配合器件资源与工具流程;对于专门设计的锁存器流水线,则需要主动管理它的透明特性。

7. “边沿采样”也不是一个无限窄的瞬间

数字模型把采样画成一个边沿,真实触发器却需要输入在边沿前后保持稳定:边沿前的一段要求叫建立时间(Setup Time),边沿后的一段要求叫保持时间(Hold Time)。具体数值应以器件或标准单元的时序模型为准。

建立时间与保持时间限定的输入稳定区间

如果输入在不满足要求的时刻变化,采样结果就不再有正常的时序保证,触发器可能进入亚稳态,无法在预期时间内输出确定的逻辑电平。

这也是跨时钟域(CDC)需要专门处理的原因之一。对于符合条件的单比特电平信号,常见同步器会用多级触发器为潜在亚稳态提供更多恢复时间,降低它影响后续逻辑的概率;它不能彻底消除风险,也不能直接解决任意多比特数据或窄脉冲的跨域传输。Intel 的亚稳态分析文档说明了同步寄存器链的作用。

因此,边沿为同步系统提供了共同的更新边界,而器件时序约束则决定这个边界能否在真实电路中可靠工作。

8. 回到 RTL:你描述的是哪一种记忆?

在 Verilog 中,下面的代码描述正沿触发的状态单元:

always @(posedge clk) begin
    q <= d;
end

而下面的代码在 en = 0 时没有给 q 赋新值,要求它保留之前的值,综合工具通常会据此推断锁存器:

always @(*) begin
    if (en)
        q = d;
end

关键在于赋值行为:什么时候允许改变,什么时候必须保留。在预期为组合逻辑的过程块中,如果某些分支没有赋值,就可能无意中引入存储状态。

从两个反相器开始,我们依次为反馈加上写入接口、数据输入和时钟控制。锁存器让状态在一个窗口内更新,触发器则把更新组织到明确的时钟边沿附近。理解这条路径,再看电路图和 RTL,就不只是辨认元件名称,而是在判断:这份数据,究竟能在什么时候改变?