第二章 FPGA内部结构:可编程逻辑块(CLB)、查找表(LUT)、触发器(FF)、块内存(BRAM)、数字信号处理单元(DSP)、可编程互连资源

各位同学,欢迎来到第二章。上一章我们聊了FPGA是什么,能干什么。这一章,咱们得把FPGA的“肚子”剖开看看。

说白了,FPGA就是一堆可编程的积木。你买到的芯片,里面其实已经预制好了各种功能模块。你的任务,就是用代码把这些模块“连起来”,实现你想要的功能。

我个人习惯,把FPGA的内部结构想象成一个“乐高底板”。底板上固定着几种不同形状的积木块,而“可编程互连资源”就是积木块之间的卡槽。你想想看,是不是这个理?

核心观点:FPGA内部不是“万能门阵列”,而是由几种固定的、可配置的硬件资源组成的。理解这些资源,是写出高效代码的前提。

FPGA 内部核心资源架构图 可编程互连资源 (Routing / Interconnect) 可编程逻辑块 (CLB) 包含: 查找表 (LUT) 触发器 (FF) 进位链 (Carry Chain) 块内存 (BRAM) 18Kb / 36Kb 真双口 RAM FIFO 支持 DSP 单元 乘法器 (25x18) 累加器 (48-bit) 乘加运算 (MAC) 可编程输入输出块 (IOB) 图例: CLB BRAM DSP IOB 互连资源

2.1 可编程逻辑块 (CLB) —— FPGA的“肌肉”

CLB是FPGA里最基础、也是数量最多的单元。你可以把它理解为“万能逻辑单元”。任何组合逻辑(与或非)和时序逻辑(寄存器)都可以用它搭出来。

一个CLB里面通常包含几个Slice(切片)。每个Slice里又包含:

  • 查找表 (LUT):实现组合逻辑的核心。
  • 触发器 (FF):实现时序逻辑的核心。
  • 进位链 (Carry Chain):专门用来做加法、减法运算。
  • 多路选择器 (MUX):用于信号选择。

我在项目中遇到过一个问题:写了一个很复杂的组合逻辑,综合后报告说LUT不够用了。后来我把逻辑拆成两步,用流水线寄存器隔开,LUT占用率瞬间降下来了。嗯,这里要注意,LUT不是万能的,它擅长做“查表”,但不擅长做“长链计算”。

2.2 查找表 (LUT) —— 用“真值表”实现任何逻辑

LUT是CLB的核心。说白了,它就是一个可以配置的“真值表”。

举个例子,一个4输入的LUT(LUT4),它有4个输入引脚和1个输出引脚。你可以通过编程,让这个LUT实现任何4输入的逻辑函数。比如:

  • Y = A & B & C & D
  • Y = A ^ B ^ C ^ D
  • Y = (A | B) & (C | D)

怎么实现的?LUT内部其实是一块小的SRAM(静态随机存取存储器)。你把真值表的结果写进SRAM里,输入信号就相当于地址线,输出就是查到的结果。

个人经验:现代FPGA的LUT通常是6输入的(LUT6)。但如果你需要实现7输入的逻辑,工具会自动把两个LUT拼起来。不过,这会消耗更多资源。所以,写代码时尽量把输入控制在6个以内,能省不少LUT。

2.3 触发器 (FF) —— 记住“上一个状态”

触发器,也叫寄存器。它的作用很简单:在时钟沿到来时,把输入的值“锁存”住,直到下一个时钟沿。

你写的 always @(posedge clk) 或者 if rising_edge(clk),综合后就会变成触发器。

我曾经犯过一个低级错误:在组合逻辑里直接用了 reg 类型变量,以为它能记住值。结果仿真时波形乱跳,查了半天才发现——组合逻辑里的 reg 只是语法上的,综合出来还是连线,不是触发器。

避坑指南:触发器有建立时间(Setup Time)和保持时间(Hold Time)要求。如果时钟频率太高,或者组合逻辑路径太长,数据可能来不及稳定就被采进去了。这就是“时序违例”。我曾经因为一个关键路径没优化,导致芯片在高温下频繁出错。从那以后,我每次上板前必跑时序分析。

2.4 块内存 (BRAM) —— 片上的“大仓库”

当你要存的数据比较多(比如一帧图像、一个数据包),用触发器存就太浪费了。这时候就该BRAM上场了。

BRAM是FPGA内部专用的存储块。以Xilinx 7系列为例,每个BRAM是36Kb(可以配置成两个18Kb)。

特性 BRAM 分布式RAM (LUT实现)
容量 大(18Kb / 36Kb) 小(几十到几百bit)
速度 快(专用布线) 中等(依赖LUT布线)
功耗 较高
适用场景 大容量缓存、FIFO、查找表 小容量寄存器堆、移位寄存器

我建议,只要数据量超过几百bit,优先考虑BRAM。别用LUT硬拼,否则你的设计会又慢又热。

2.5 数字信号处理单元 (DSP) —— 算数的“加速器”

如果你要做乘法、乘加、累加这些运算,用LUT搭出来的电路又慢又费资源。FPGA厂商早就想到了这一点,所以在芯片里集成了专用的DSP单元。

一个典型的DSP单元包含:

  • 一个25x18位的乘法器
  • 一个48位的累加器
  • 支持乘加运算(MAC)
  • 支持流水线寄存器

说白了,DSP就是为数字信号处理(滤波、FFT、卷积)量身定做的。你写 a * b + c,综合工具会自动推断出DSP单元,而不是用LUT拼乘法器。

关键点:DSP单元是“硬核”,不是“软核”。它的速度和功耗都远优于用LUT实现的等效电路。所以,做数学运算时,尽量让工具推断出DSP,而不是自己用LUT搭。

2.6 可编程互连资源 —— FPGA的“血管和神经”

有了CLB、BRAM、DSP这些“器官”,还需要“血管”把它们连起来。这就是可编程互连资源。

FPGA内部有大量的金属线和可编程开关。你可以通过编程,把任意两个逻辑单元的引脚连接起来。这有点像PCB上的走线,只不过是在芯片内部完成的。

互连资源分为几种:

  • 局部互连:同一个CLB内部,或者相邻CLB之间的连接。延迟最小。
  • 全局互连:跨越整个芯片的长线。用于时钟、复位等扇出很大的信号。
  • I/O互连:连接芯片内部逻辑和外部引脚。

嗯,这里要注意,互连资源不是无限的。如果你的设计布线很拥挤,工具可能会绕很远的路,导致时序变差。我遇到过最夸张的一次,一个简单的加法器,因为布局布线不合理,延迟比预期多了3倍。后来我手动加了布局约束,才把问题解决。

个人习惯:写代码时,尽量让模块之间的接口信号少一些。信号越多,布线越复杂,越容易出问题。这叫“接口最小化原则”。

小结

这一章我们拆解了FPGA的六大核心资源。你想想看,CLB负责逻辑,BRAM负责存储,DSP负责运算,互连资源负责连接。理解了这些,你就能明白为什么有些代码综合出来资源占用很高,有些却很低。

下一章,我们会把这些资源用起来,开始写真正的Verilog代码。到时候你会发现,理解了内部结构,写代码会更有底气。


专注资料整理