本节摘要:D 触发器是时序逻辑的最小记忆单元,时钟进程是它的标准书写形式。本节讲清触发器的采样行为、同步与异步复位的两种句式与时序差异、时钟使能的两种写法,并给出逐段对照的波形验证方法。
组合电路的输出跟着输入即时变化,D 触发器(数据触发器)做的事件截然不同:平时输出纹丝不动,只在时钟上升沿那一瞬,把 D 输入的值抄进输出 Q,然后继续纹丝不动。抄进来的值一直保持到下一个上升沿。这一"抄"就是数字系统的记忆机制:一个位的信息被存住了。
行为写成 VHDL 就三行核心:
library ieee; use ieee.std_logic_1164.all; entity dff is port ( clk : in std_logic; d : in std_logic; q : out std_logic ); end entity dff; architecture rtl of dff is begin process (clk) -- 敏感表只有时钟 begin if rising_edge(clk) then -- 只在上升沿动手 q <= d; -- 把 d 抄进 q end if; end process; end architecture rtl;
敏感表只有 clk——上一章的配平法则在这里有个重要例外说明:进程里读了 d,但 d 只在时钟沿被采样,d 自己变化不该唤醒进程,所以只挂 clk 恰好正确。时序进程的标准形态就是"敏感表只有时钟 + rising_edge 判断",记住这个形态,百分之九十的时序代码都是它的变形。
上电瞬间触发器的值是未知的,复位负责把它拉到已知状态。两种风格,句式与时序都不同:
-- 同步复位:复位要等时钟沿才生效 process (clk) begin if rising_edge(clk) then if rst = '1' then q <= '0'; else q <= d; end if; end if; end process; -- 异步复位:复位一来立刻生效,不等时钟 process (clk, rst) begin if rst = '1' then q <= '0'; elsif rising_edge(clk) then q <= d; end if; end process;
对比两段代码的三个差异点。其一,敏感表:同步版只有 clk(rst 只在时钟沿被采样);异步版必须挂上 rst(rst 变化本身要唤醒进程)。其二,判断顺序:异步版 rst 判断在 rising_edge 外面,这是"电平优先于时钟沿"的语法表达。其三,波形表现:同步复位在 rst 拉高后的下一个时钟沿才清零;异步复位 rst 一拉高输出立刻归零。FPGA 的触发器硬件自带异步复位端,两种风格都能直接映射;工程惯例偏向异步复位、同步释放的折中方案,细节在第 8 章上板排障时结合真实案例再谈。

很多场合需要"这个时钟沿要不要采样由条件决定"——这就是时钟使能。标准写法是在 rising_edge 判断里再套一层条件:
process (clk) begin if rising_edge(clk) then if en = '1' then -- 使能有效才采样 q <= d; end if; -- 使能无效:保持原值(这是想要的保持,不是锁存器) end if; end process;
注意这里的"不赋值就保持"与第 3 章的锁存器雷区并不冲突:锁存器发生在组合进程的不完整赋值;时钟进程里"跳过赋值"意味着触发器保持原值,映射的正是触发器的保持行为,完全正确。区分二者只需一句话:时钟沿下的不完整赋值是保持,无时钟的组合路径不完整赋值才是锁存。
验证环节沿用第 1 章的测试台骨架:时钟进程加激励进程,d 与 rst 按时间表翻转。逐段核对波形:找到每个上升沿,标出此刻 d 的值,确认 q 在沿后等于该值;再人为制造一次"复位与时钟沿同时到达",对比同步与异步两个版本的输出差异。这个实验做一遍,复位时序的争论你就有了亲手测过的答案。
⚠️ 常见坑:在同一进程里对同一信号既写时钟沿赋值又写组合赋值,或者用 d 的下降沿、双沿混合驱动——综合器要么报多时钟域错误要么综合出不可预期的电路。一条铁律:一个进程一个时钟,永远只用一个沿。
💡 关键直觉:触发器 = "抄写员"。平时不动作,时钟沿一到,把眼前(D 端)的内容抄到本子(Q 端)上。复位是"把本子擦干净",同步擦要等抄写节拍,异步擦随时动手。
复位分同步复位和异步复位:
-- 异步复位 process(clock, reset) begin if reset = '1' then q <= '0'; elsif rising_edge(clock) then q <= d; end if; end process; -- 同步复位 process(clock) begin if rising_edge(clock) then if reset = '1' then q <= '0'; else q <= d; end if; end if; end process;
异步复位的优点:复位立即生效,不依赖时钟;缺点:复位释放是异步的,可能导致时序违例(recovery/removal)。
同步复位的优点:复位释放与时钟同步,避免 recovery/removal 问题;缺点:复位必须等下一个时钟沿生效。
现代 FPGA 设计推荐异步复位 + 同步释放(async assert, sync deassert):
process(clock, reset_async) begin if reset_async = '1' then sync_reg <= '0'; elsif rising_edge(clock) then sync_reg <= '0'; -- 同步释放 end if; end process; -- 用 sync_reg 作为下游逻辑的复位信号
这种写法既保留了异步复位的快速响应,又避免了异步释放的时序风险。Xilinx 和 Intel 的官方应用笔记都推荐这种模式。