二、数字逻辑基础:二进制、布尔代数、逻辑门、真值表、组合逻辑与时序逻辑的基本概念

数字逻辑是FPGA设计的基石。FPGA内部的所有功能——无论是简单的接口协议、复杂的信号处理算法,还是嵌入式处理器软核——最终都是通过海量的基本逻辑单元(查找表、触发器、布线资源)组合与级联来实现的。本章将系统梳理数字逻辑的核心概念:从二进制数制出发,建立布尔代数的数学框架,理解逻辑门与真值表的对应关系,并严格区分组合逻辑与时序逻辑这两大设计范式。这些概念不仅是理论铺垫,更直接决定了你编写Verilog/VHDL代码时的思维方式与硬件实现结果。

数字逻辑基础概念关系与设计路径 二进制与数制 基数2,逢二进一 位权:2^n 十六进制/BCD转换 布尔代数 公理:0/1,与/或/非 定律:交换/结合/分配 德摩根定律 逻辑门与真值表 AND/OR/NOT/NAND/NOR XOR/XNOR 真值表→逻辑表达式 组合逻辑 输出仅取决于当前输入 无记忆元件,无反馈回路 典型电路:加法器/多路选择器/译码器 Verilog:assign / always@(*) 竞争冒险:毛刺产生与消除 时序逻辑 输出取决于当前输入 + 历史状态 核心元件:D触发器/锁存器 同步设计:时钟沿驱动 Verilog:always@(posedge clk) 状态机/计数器/移位寄存器 FPGA 硬件映射 查找表(LUT)实现组合逻辑 → 触发器(FF)实现状态存储 布线资源连接 → 时序收敛 → 比特流配置 核心设计路径

2.1 二进制数制与编码基础

数字系统只认识两个物理状态:高电平和低电平,分别抽象为逻辑“1”和逻辑“0”。二进制(Base-2)正是基于这一物理现实构建的计数体系。每一位的权值为2的幂次方,从最低位(LSB)的2^0开始递增。例如,8位二进制数1011_0101转换为十进制为:1×2^7 + 0×2^6 + 1×2^5 + 1×2^4 + 0×2^3 + 1×2^2 + 0×2^1 + 1×2^0 = 128 + 32 + 16 + 4 + 1 = 181。

在FPGA开发中,二进制书写冗长且易错,因此常用十六进制(Base-16)作为缩写形式,每4位二进制对应1位十六进制。例如1011_0101可写作0xB5。此外,有符号数通常采用二进制补码(Two's Complement)表示,最高位为符号位,其优势在于加减法运算统一,无需单独处理符号。补码的转换规则为:正数原码即补码;负数则对其值取反加1。例如,-5在8位补码中表示为1111_1011。

数制基数有效数字示例(十进制181)FPGA应用场景
二进制20,11011_0101位操作、状态编码、硬件底层
八进制80-7265较少使用,Unix权限等
十进制100-9181人机交互、参数配置
十六进制160-9,A-F0xB5地址、数据总线、调试显示
BCD码100000-10010001_1000_0001数码管显示、十进制运算
经验提醒:在编写FPGA代码时,建议统一使用二进制或十六进制常量,并明确标注位宽。例如 8'd181 表示8位十进制181,8'hB5 表示8位十六进制0xB5。避免在代码中混用不同进制导致阅读混乱或综合错误。

2.2 布尔代数:数字逻辑的数学语言

布尔代数由乔治·布尔于19世纪提出,是描述和处理二进制变量关系的数学体系。其基本运算有三种:与(AND,符号·)、或(OR,符号+)、非(NOT,符号上划线或!)。布尔代数的公理体系定义了0和1的运算规则:0·0=0,0·1=0,1·1=1;0+0=0,0+1=1,1+1=1;!0=1,!1=0。

基于这些公理,可以推导出若干重要定律,这些定律是化简逻辑表达式、优化FPGA资源消耗的理论工具:

  • 交换律:A·B = B·A;A+B = B+A
  • 结合律:(A·B)·C = A·(B·C);(A+B)+C = A+(B+C)
  • 分配律:A·(B+C) = A·B + A·C;A + B·C = (A+B)·(A+C)
  • 吸收律:A + A·B = A;A·(A+B) = A
  • 德摩根定律:!(A·B) = !A + !B;!(A+B) = !A · !B

德摩根定律在FPGA设计中尤为常用,它允许我们在与非门(NAND)或或非门(NOR)之间进行等效转换,从而匹配FPGA底层查找表(LUT)的物理实现结构。例如,一个复杂的逻辑表达式F = !(A·B + C·D),通过德摩根定律可改写为F = (!A + !B) · (!C + !D),便于用LUT直接实现。

核心原理:布尔代数与普通代数的关键区别在于变量取值仅为0或1,且“或”运算并非算术加法。例如,1+1=1(逻辑或),而非算术的2。理解这一差异是避免设计错误的前提。

2.3 逻辑门与真值表

逻辑门是布尔代数在硬件中的物理实现。FPGA内部并不直接使用分立逻辑门,而是通过查找表(LUT)来模拟任意组合逻辑功能。但理解基本逻辑门的真值表,是分析复杂电路和调试问题的基本功。常见逻辑门包括:与门(AND)、或门(OR)、非门(NOT)、与非门(NAND)、或非门(NOR)、异或门(XOR)、同或门(XNOR)。

真值表是列出所有输入组合及其对应输出的表格,它是逻辑功能的完整描述。以2输入与非门为例,其真值表如下:

ABY = !(A·B)
001
011
101
110

从真值表可以推导出逻辑表达式。方法是将输出为1的每一行写成最小项(输入变量相与),再将所有最小项相或。例如,对于异或门(XOR),输出为1的行是A=0,B=1和A=1,B=0,因此表达式为Y = (!A)·B + A·(!B),简写为Y = A ⊕ B。

风险提示:在FPGA综合时,不要试图用离散逻辑门搭建电路。综合工具会将你的RTL代码自动映射到LUT和触发器上。手工实例化逻辑门(如调用AND2、OR2原语)不仅效率低下,而且会阻碍综合器的优化能力。应使用行为级描述(如assign Y = A && B;)让工具自动优化。

2.4 组合逻辑:无记忆的决策网络

组合逻辑电路的特点是:任意时刻的输出仅取决于该时刻的输入,与电路的历史状态无关。它没有存储元件,也没有反馈回路。典型的组合逻辑模块包括:多路选择器(MUX)、译码器(Decoder)、编码器(Encoder)、加法器(Adder)、比较器(Comparator)等。

在Verilog中,组合逻辑有两种常用描述方式:

// 方式一:assign 连续赋值
assign y = (sel == 1'b1) ? a : b;

// 方式二:always 块 + 阻塞赋值
always @(*) begin
    if (sel == 1'b1)
        y = a;
    else
        y = b;
end

组合逻辑设计的关键问题之一是竞争冒险。当输入信号经过不同路径到达逻辑门的时间不一致时,输出端可能出现短暂的错误脉冲(毛刺)。例如,一个2输入与门,当A从1变为0、B从0变为1时,理论上输出应保持0,但由于A和B的翻转时间存在微小差异,可能瞬间出现A=1且B=1的状态,导致输出产生一个正向毛刺。消除毛刺的常用方法包括:在输出端增加D触发器采样(同步设计)、使用格雷码编码状态、或增加冗余项消除逻辑险象。

实战场景:设计一个4选1多路选择器,输入为4个8位数据总线d0~d3,选择信号sel[1:0],输出y。使用case语句实现时,务必列出所有sel组合(包括default分支),否则综合器可能推断出锁存器(Latch),导致时序问题。正确写法应包含default: y = 8'h00;。

2.5 时序逻辑:携带记忆的状态机

时序逻辑电路的输出不仅取决于当前输入,还取决于电路的历史状态。其核心存储元件是触发器(Flip-Flop,FF),在FPGA中通常为D触发器。D触发器在时钟上升沿(或下降沿)到来时,将输入D的值锁存到输出Q,其余时间Q保持不变。这种边沿触发特性是同步时序设计的基础。

时序逻辑的基本结构由存储元件和组合逻辑两部分组成。组合逻辑负责根据当前状态和输入计算下一状态及输出,触发器负责在时钟沿更新状态。典型的时序逻辑模块包括:计数器、移位寄存器、有限状态机(FSM)、FIFO、RAM控制器等。

在Verilog中,时序逻辑使用always @(posedge clk)块和非阻塞赋值(<=)描述:

// 8位同步计数器
reg [7:0] cnt;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        cnt <= 8'h00;
    else if (en)
        cnt <= cnt + 1'b1;
end

同步设计的核心原则是:所有触发器共享同一个时钟域,避免使用门控时钟或异步复位(除非必要)。异步电路虽然在某些低功耗场景有优势,但会带来复杂的时序约束和亚稳态风险,初学者应优先掌握同步设计方法。

常见误区:在always块中混用阻塞赋值(=)和非阻塞赋值(<=)是初学者最常见的错误。组合逻辑应使用阻塞赋值,时序逻辑应使用非阻塞赋值。混用会导致仿真与综合结果不一致,产生难以调试的竞争问题。务必遵循这一编码规范。

2.6 组合逻辑与时序逻辑的对比与工程选择

理解两类逻辑的本质区别,是FPGA设计分水岭。下表从多个维度进行对比:

对比维度组合逻辑时序逻辑
输出决定因素仅当前输入当前输入 + 历史状态
存储元件无触发器/锁存器
时钟依赖不依赖时钟依赖时钟沿
Verilog描述assign / always@(*)always@(posedge clk)
赋值方式阻塞赋值 =非阻塞赋值 <=
典型电路加法器、MUX、译码器计数器、FSM、移位寄存器
时序分析关注传播延迟、毛刺关注建立/保持时间、时钟偏斜
FPGA实现LUT + 布线LUT + FF + 布线

在实际工程中,一个完整的数据通路通常由组合逻辑与时序逻辑交替构成。例如,一个UART接收模块:接收移位寄存器(时序逻辑)将串行数据逐位移入,起始位检测逻辑(组合逻辑)判断数据有效,波特率计数器(时序逻辑)产生采样时钟,最终并行数据输出(组合逻辑)送到数据总线。这种“寄存器-组合逻辑-寄存器”的流水线结构是FPGA高性能设计的核心范式。

案例拆解:设计一个4位同步加法计数器,要求具有异步复位和使能功能。初始现象:直接使用always @(posedge clk)块,但忘记处理复位信号,导致上电后计数器状态不确定。分析:FPGA上电后触发器初始值可能是0或1,若不复位,计数起点不可控。解决:添加异步复位端口rst_n,在复位有效时将计数器清零。操作步骤:1) 定义reg [3:0] cnt;2) 在always块敏感列表中加入negedge rst_n;3) 复位分支使用阻塞赋值cnt <= 4'h0;4) 使能分支cnt <= cnt + 1'b1。结果:计数器从0开始稳定递增。风险:异步复位信号本身可能存在亚稳态,需通过同步器处理后再接入系统。复盘:复位策略是时序设计的首要决策点,必须明确异步复位、同步释放的原则。

2.7 本章小结与设计复盘

数字逻辑基础是FPGA设计的“语法”和“词汇”。二进制与编码决定了数据如何表示,布尔代数提供了逻辑化简的数学工具,逻辑门与真值表是硬件行为的抽象描述,而组合逻辑与时序逻辑的区分则定义了电路的结构范式。在实际编码前,建议先画出数据通路草图,明确哪些模块是组合的、哪些是时序的,再动手写RTL。这样能显著减少综合后的功能错误和时序违例。

学习建议:动手练习时,尝试用Verilog分别实现一个8位加法器(组合)和一个8位累加器(时序),对比两者的RTL视图和仿真波形。观察加法器输出随输入即时变化,而累加器输出仅在时钟沿更新。这一对比能帮助你直观建立“组合=即时决策,时序=状态记忆”的工程直觉。