一、数字电路基础回顾:组合逻辑与时序逻辑的区别、同步与异步概念、时钟与复位信号
各位同学,咱们今天聊点基础的东西。别小看这些概念,我见过不少工作两三年的工程师,在这上面栽跟头。说白了,数字电路就两大阵营:组合逻辑和时序逻辑。搞不清楚它们的区别,你写出来的 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 知识体系总览
为了让大家更直观地理解本章的知识结构,我画了一张图。这张图把组合逻辑、时序逻辑、同步异步、时钟复位的关系串起来了。
这张图里,数字电路先分成组合逻辑和时序逻辑两大块。组合逻辑没有记忆,输出只跟当前输入有关;时序逻辑有记忆,需要时钟驱动。同步和异步是信号交互的方式,时钟和复位是时序逻辑的「燃料」和「保险」。搞懂这些,后面的内容就好理解了。
本章要点回顾:
- 组合逻辑无记忆,时序逻辑有记忆——这是最根本的区别。
- 同步设计更可靠,异步信号必须做同步化处理。
- 时钟是时序逻辑的驱动源,复位是系统的初始状态保证。
- 推荐使用「异步复位、同步释放」的复位策略。
好了,这一章就到这里。内容不多,但都是基本功。你想想看,如果连组合逻辑和时序逻辑都分不清,后面的状态机、FIFO、接口时序还怎么学?嗯,先把基础打牢。
一个小练习:
试着用 Verilog 写一个 4 位计数器,要求:异步复位,同步使能,计数到 15 后自动归零。写完后对比一下,你的代码里哪些部分是组合逻辑,哪些是时序逻辑?
公众号:蓝海资料掘金营,微信deep3321