第二章:组合逻辑与时序逻辑
各位同学,今天我们来聊聊数字电路设计的两个基本概念——组合逻辑和时序逻辑。说实话,这两个概念就像硬币的两面,你搞懂了它们,FPGA设计的大门就算真正推开了。
我个人习惯把组合逻辑想象成「纯函数」——输入一变,输出立马跟着变,没有任何记忆能力。而时序逻辑呢,它是有「记忆」的,输出不仅看当前输入,还得看之前的状态。嗯,这个区别很关键。
2.1 基本门电路
先从最基础的开始。与门、或门、非门,这些是数字世界的「原子操作」。我在项目中遇到过一位新手,上来就用一堆门电路搭复杂逻辑,结果时序一塌糊涂。其实现在FPGA里都有现成的查找表(LUT),你写个 assign y = a & b; 就够了,综合工具会自动映射到LUT里。
但理解门电路还是有用的。比如你要做位宽转换、数据掩码,脑子里能快速反应出「这里应该用与门还是或门」,写代码就快很多。
核心要点:
- 与门:全1出1,有0出0
- 或门:有1出1,全0出0
- 非门:取反
- 异或门:相同出0,不同出1
2.2 多路选择器
多路选择器(MUX)在FPGA里太常见了。说白了就是「根据选择信号,从多个输入里挑一个输出」。我建议你写代码时尽量用 case 语句或者三元运算符 ? :,综合工具会帮你生成最优的MUX结构。
曾经有一次,我在做视频数据流切换时,用了嵌套的 if-else,结果综合出来一个巨大的优先级编码器,延迟超标。后来改成 case 语句,立马变成了并行MUX,时序就过了。嗯,这里要注意:case 是并行的,if-else 是串行的,别搞混了。
// 4选1多路选择器
always @(*) begin
case(sel)
2'b00: out = a;
2'b01: out = b;
2'b10: out = c;
2'b11: out = d;
default: out = a;
endcase
end
2.3 加法器
加法器是算术运算的基础。最简单的就是全加器,一位一位地加,带进位传递。但如果你直接用 assign sum = a + b;,综合工具会自动帮你选最优的加法器结构——可能是超前进位加法器,也可能是进位选择加法器,取决于你的时序要求。
我个人经验是:对于位宽小于16位的加法,直接写 + 就行,工具处理得很好。但如果你在做32位甚至64位加法,尤其是高频设计,我建议你考虑流水线加法器——把加法拆成几级,每级插一个寄存器,这样能跑更高的频率。
小技巧: 加法器的进位链是FPGA里最长的路径之一。如果你发现时序不满足,试试把加法拆成两半,中间加一级流水线。我曾经用这招把一个200MHz的设计救活了。
2.4 触发器与寄存器
触发器(Flip-Flop)是时序逻辑的基本单元。FPGA里的触发器通常是D触发器,时钟上升沿采样数据。寄存器呢,就是一组触发器拼在一起,用来存多位数据。
你想想看,为什么我们需要触发器?因为组合逻辑的输出会随着输入变化而抖动,我们需要一个「快照」机制,在时钟边沿把稳定的值抓下来。这就是时序逻辑的核心思想。
我曾经调试过一个bug,数据总是错位。查了半天,发现是复位信号没处理好——异步复位和同步复位的用法搞混了。这里我给大家一个建议:
- 异步复位:复位信号不依赖时钟,立即生效。适合上电初始化。
- 同步复位:复位信号只在时钟边沿生效。适合避免毛刺。
// 带异步复位的D触发器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
2.5 有限状态机设计
有限状态机(FSM)是数字设计里的「大脑」。你想想看,任何需要「记住当前状态、根据输入决定下一步」的逻辑,都可以用FSM来实现。比如图像处理里的像素流水线控制、协议解析、数据调度……
我习惯把FSM分成三段式来写:
- 第一段:状态转移(时序逻辑,用
always @(posedge clk)) - 第二段:次态逻辑(组合逻辑,根据当前状态和输入决定下一个状态)
- 第三段:输出逻辑(可以是组合逻辑,也可以是时序逻辑)
为什么要分三段?因为这样代码清晰,综合出来的电路也规整。我曾经接手过一个项目,前人把FSM写在一个always块里,状态和输出混在一起,调试起来简直想哭。后来我重构成了三段式,问题一目了然。
避坑指南: 写FSM时,一定要考虑「非法状态」。如果状态编码是二进制,万一受到干扰跳到了未定义状态,你的FSM可能就卡死了。我建议用 default 分支回到初始状态,或者用独热码编码——虽然浪费一点触发器,但容错性好很多。
// 三段式FSM示例
// 第一段:状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
// 第二段:次态逻辑
always @(*) begin
case(state)
IDLE: next_state = (start) ? RUN : IDLE;
RUN: next_state = (done) ? IDLE : RUN;
default: next_state = IDLE;
endcase
end
// 第三段:输出逻辑
assign busy = (state == RUN);
知识体系总览
下面这张图是我自己画的,把本章的知识结构串起来了。你可以看到,组合逻辑和时序逻辑是两条主线,最终在FSM这里汇合。
好了,这一章的内容就到这里。组合逻辑和时序逻辑是FPGA设计的根基,你花再多时间理解都不为过。下一章我们会深入时序约束和静态时序分析,到时候这些基础概念还会反复用到。
本章小结:
- 组合逻辑:输出只取决于当前输入,无记忆
- 时序逻辑:输出取决于当前输入和过去状态,有记忆
- 触发器是时序逻辑的基本单元,寄存器是触发器的集合
- FSM是复杂时序控制的核心,推荐三段式写法
- 写代码时注意综合工具的行为,
case和if-else有本质区别
可可精品资料库