数字电路基础回顾:组合逻辑与时序逻辑、触发器与寄存器、时钟与复位、建立时间与保持时间

各位同学,咱们今天聊点基础的东西。别一听「基础」就想跳过,我做了十几年FPGA,吃过最多的亏,恰恰就是这些基础概念没吃透。你想想看,流水线设计说白了就是跟时序打交道,如果连组合逻辑和时序逻辑都分不清,那后面写出来的代码,大概率是跑不起来的。

组合逻辑 vs 时序逻辑

先说说组合逻辑。组合逻辑的输出,只取决于当前的输入。说白了,就是「输入一变,输出立马跟着变」,中间没有记忆功能。比如一个与门、或门,或者一个加法器,都属于组合逻辑。

时序逻辑就不一样了。它的输出不仅跟当前输入有关,还跟之前的状态有关。嗯,它是有「记忆」的。我在项目中遇到过一位同事,写了一个计数器,结果发现每次上电计数值都是随机的——他忘了给寄存器赋初值。这就是典型的把时序逻辑当组合逻辑用了。

核心区别一句话:组合逻辑没有存储单元,时序逻辑有触发器或寄存器。

在FPGA里,组合逻辑通常用LUT(查找表)实现,时序逻辑用触发器实现。你写Verilog的时候,assign语句一般生成组合逻辑,always @(posedge clk)生成时序逻辑。这个对应关系要记牢。

触发器与寄存器

触发器(Flip-Flop)是时序逻辑的基本单元。最常见的D触发器,它有一个数据输入端D,一个时钟端CLK,一个输出端Q。时钟上升沿来的时候,D的值被锁存到Q上,直到下一个时钟沿才更新。

寄存器呢?说白了就是一组触发器拼在一起。比如一个8位寄存器,就是8个D触发器共用同一个时钟。我刚开始学的时候,总觉得这两个概念差不多,后来做项目才发现:触发器是「细胞」,寄存器是「器官」。你写代码时,reg [7:0] data_reg; 这个声明,综合工具会给你生成8个触发器。

我的一个小习惯:命名寄存器时,我习惯加个 _reg 后缀,比如 cnt_reg、data_reg。这样看代码一眼就知道哪些是时序逻辑,哪些是组合逻辑,调试时省不少事。

时钟与复位

时钟是FPGA的心跳。没有时钟,时序逻辑就没法工作。时钟的频率决定了你的系统能跑多快,但同时也决定了时序约束的难度。

复位呢?复位是为了让系统从一个已知状态开始跑。常用的复位方式有两种:同步复位和异步复位。

复位类型 特点 我踩过的坑
同步复位 只在时钟沿有效,抗毛刺能力强 复位信号必须满足建立时间,否则会出问题
异步复位 复位信号一到就立即复位,不依赖时钟 我曾经在异步复位释放时没做同步处理,导致系统偶尔跑飞

注意:异步复位的释放时刻如果离时钟沿太近,会导致寄存器进入亚稳态。我建议的做法是:异步复位,同步释放。也就是复位信号进来后,先打两拍再送给内部逻辑。

建立时间与保持时间

这两个概念,是时序分析的根基。很多同学觉得抽象,我换个说法:

  • 建立时间(Setup Time):时钟沿到来之前,数据必须稳定不变的最短时间。
  • 保持时间(Hold Time):时钟沿到来之后,数据必须稳定不变的最短时间。

为什么会这样?因为触发器内部有个「判决窗口」,在这个窗口内数据不能跳变,否则触发器不知道该锁存0还是1,就会进入亚稳态。

我在项目中遇到过最典型的问题:一个高速接口,数据速率跑到1.25Gbps,结果时序分析报了一堆建立时间违例。查了半天,发现是布线路径太长,数据从发送端到接收端花了太多时间。解决办法?要么降低频率,要么在中间插一级流水线寄存器。

避坑指南:我曾经在综合后忽略了一个建立时间违例,觉得「就差0.1ns,应该没事吧」。结果板子调了三天,偶尔出现数据错乱。从那以后,我养成了习惯:任何时序违例,哪怕只有1ps,也必须解决。

知识体系总览

下面这张图,是我自己总结的数字电路基础核心逻辑。你看一眼,就能把今天讲的内容串起来。

数字电路基础核心知识体系 组合逻辑 • 输出只取决于当前输入 • 无记忆功能 • 用 LUT 实现 • assign 语句生成 时序逻辑 • 输出依赖当前和过去状态 • 有记忆功能 • 用触发器实现 • always @(posedge clk) 生成 触发器与寄存器 • D触发器:基本存储单元 • 寄存器:多个触发器组合 时钟与复位 • 时钟:系统心跳 • 复位:同步/异步,异步复位同步释放 建立时间 + 保持时间 = 时序分析根基

这张图把今天讲的内容分成了三大块:组合逻辑 vs 时序逻辑、触发器与寄存器、时钟与复位,最后都汇聚到建立时间和保持时间这个核心上。你每次做时序分析时,都可以回来看看这张图,思路会清晰很多。

最后分享一个经验:我每次写代码前,都会先在纸上画一下数据路径,标出哪些是组合逻辑,哪些是时序逻辑,时钟从哪里来,复位怎么处理。画清楚了再写代码,基本不会出大问题。你也试试看。

可可精品资料库