一、数字电路基础回顾:组合逻辑与时序逻辑的区别、同步与异步概念、时钟与复位信号

各位同学,咱们今天聊点基础的东西。别小看这些概念,我见过不少工作两三年的工程师,在这上面栽跟头。说白了,数字电路就两大阵营:组合逻辑和时序逻辑。搞不清楚它们的区别,你写出来的 Verilog 代码,大概率是跑不起来的。

1.1 组合逻辑 vs 时序逻辑:到底差在哪?

组合逻辑,顾名思义,输出只取决于当前的输入。没有记忆能力,输入一变,输出立马跟着变。就像一扇门,你推它就开,你拉它就关,没有中间状态。

时序逻辑就不一样了。它是有记忆的。输出不仅看当前输入,还要看之前的状态。说白了,它记得自己之前干了什么。这个「记忆」靠的就是触发器(Flip-Flop)。

我刚开始学的时候,总觉得组合逻辑简单,时序逻辑复杂。后来做项目才发现,真正容易出问题的反而是组合逻辑。为什么?因为组合逻辑容易产生毛刺和竞争冒险。嗯,这里要注意。

核心区别一句话总结:

  • 组合逻辑:输出 = f(当前输入)
  • 时序逻辑:输出 = f(当前输入 + 历史状态)

咱们看个简单的例子。一个与门就是组合逻辑,一个 D 触发器就是时序逻辑。你想想看,如果让你用 Verilog 描述一个 D 触发器,你会怎么写?

// 组合逻辑:与门
assign y = a & b;

// 时序逻辑:D触发器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;
end

看到区别了吗?时序逻辑的 always 块里,敏感列表是时钟沿和复位信号。组合逻辑的 assign 语句,没有时钟概念。我在项目中遇到过有人把组合逻辑写在 always @(posedge clk) 里,结果综合出来一堆奇怪的锁存器。嗯,这是个坑。

1.2 同步与异步:别让信号「乱跑」

同步和异步,这两个词在 FPGA 设计里天天见。但很多人其实没搞明白。

同步设计,就是所有信号的变化都跟同一个时钟沿对齐。就像阅兵方阵,所有人听同一个口令,步调一致。这样做的好处是时序容易分析,不容易出问题。

异步设计,就是信号的变化跟时钟没关系,想什么时候变就什么时候变。这就像一群人在广场上自由活动,你很难预测下一秒谁往哪走。

我个人习惯,能用同步绝不用异步。为什么?因为异步信号容易产生亚稳态。什么叫亚稳态?说白了就是信号在高低电平之间「晃悠」,既不是 0 也不是 1。这个状态如果被后面的电路采到,整个系统可能就乱了。

避坑指南:

我曾经在一个项目中,直接把外部按键信号接到触发器的 D 端。结果按键按下时,系统偶尔会误触发。后来加了二级同步器,问题就解决了。记住:跨时钟域的信号,一定要做同步处理。

同步器的标准写法是这样的:

// 两级同步器,用于异步信号同步化
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        sync1 <= 1'b0;
        sync2 <= 1'b0;
    end
    else begin
        sync1 <= async_in;
        sync2 <= sync1;
    end
end

assign sync_out = sync2;

你看,就是两个触发器串起来。第一级可能产生亚稳态,但第二级采到稳定值的概率就高多了。当然,这不是 100% 可靠,但对于大多数应用场景已经足够了。

1.3 时钟与复位信号:FPGA 的「心跳」和「重启键」

时钟信号,是 FPGA 设计里最重要的信号,没有之一。所有时序逻辑都靠它来驱动。你可以把时钟想象成心脏的跳动,每个上升沿(或下降沿)就是一次心跳,触发器就在这个时刻采样数据。

时钟有几个关键参数:频率、占空比、抖动、偏斜。频率决定了系统能跑多快,占空比一般要求 50%,抖动和偏斜越小越好。我建议初学者先别管那么多,用开发板上的晶振时钟就行,一般 50MHz 或 100MHz。

复位信号呢?就是让系统回到初始状态。上电后,所有寄存器都应该被复位到一个已知值,否则系统可能跑飞。

复位有两种:同步复位和异步复位。看名字就知道,同步复位跟时钟沿对齐,异步复位随时有效。

我的建议:

用异步复位、同步释放的方式。既保证了复位及时响应,又避免了复位信号撤除时的亚稳态问题。这是业界比较推荐的做法。

代码实现如下:

// 异步复位,同步释放
reg rst_sync1, rst_sync2;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rst_sync1 <= 1'b0;
        rst_sync2 <= 1'b0;
    end
    else begin
        rst_sync1 <= 1'b1;
        rst_sync2 <= rst_sync1;
    end
end

wire rst_sync = rst_sync2;

// 使用同步后的复位
always @(posedge clk) begin
    if (!rst_sync)
        q <= 1'b0;
    else
        q <= d;
end

你想想看,如果直接用异步复位,复位信号撤除时刚好不在时钟沿附近,那触发器可能采到不确定的状态。加上同步释放,就避免了这个问题。

1.4 知识体系总览

为了让大家更直观地理解本章的知识结构,我画了一张图。这张图把组合逻辑、时序逻辑、同步异步、时钟复位的关系串起来了。

数字电路基础:核心概念关系图 数字电路 组合逻辑 时序逻辑 无记忆 输出= f(输入) 易产生毛刺 有记忆 输出= f(输入+状态) 需要时钟驱动 同步 vs 异步 同步:所有信号对齐时钟沿 异步:信号随时变化 时钟 · 复位

这张图里,数字电路先分成组合逻辑和时序逻辑两大块。组合逻辑没有记忆,输出只跟当前输入有关;时序逻辑有记忆,需要时钟驱动。同步和异步是信号交互的方式,时钟和复位是时序逻辑的「燃料」和「保险」。搞懂这些,后面的内容就好理解了。

本章要点回顾:

  1. 组合逻辑无记忆,时序逻辑有记忆——这是最根本的区别。
  2. 同步设计更可靠,异步信号必须做同步化处理。
  3. 时钟是时序逻辑的驱动源,复位是系统的初始状态保证。
  4. 推荐使用「异步复位、同步释放」的复位策略。

好了,这一章就到这里。内容不多,但都是基本功。你想想看,如果连组合逻辑和时序逻辑都分不清,后面的状态机、FIFO、接口时序还怎么学?嗯,先把基础打牢。

一个小练习:

试着用 Verilog 写一个 4 位计数器,要求:异步复位,同步使能,计数到 15 后自动归零。写完后对比一下,你的代码里哪些部分是组合逻辑,哪些是时序逻辑?


公众号:蓝海资料掘金营,微信deep3321