第二章 FPGA内部结构:六大核心模块详解
各位同学,今天我们来聊聊FPGA的“五脏六腑”。
很多人刚接触FPGA时,总觉得它是个黑盒子——写代码、综合、烧录,然后就亮了。但说实话,如果你不了解芯片内部到底长什么样,很多性能瓶颈和资源浪费你是看不出来的。我当年第一次做高速接口设计时,就因为不懂IOB的结构,折腾了整整两周才搞定信号完整性。
好,我们直接切入正题。FPGA内部主要由六大模块构成:可编程逻辑块(CLB)、输入输出块(IOB)、互连资源、块RAM(BRAM)、DSP切片、时钟管理单元(PLL/DLL)。下面我一个一个讲。
核心观点:FPGA的本质就是“可编程的硬件积木”。CLB是积木块,互连资源是积木之间的卡扣,IOB是积木与外界的接口,BRAM和DSP是专用功能块,PLL/DLL是时钟管家。
2.1 可编程逻辑块(CLB)—— FPGA 的“基本细胞”
CLB 是 FPGA 里最核心、数量最多的单元。说白了,你写的 Verilog 代码最终就是映射到这些 CLB 上。
每个 CLB 内部包含什么呢?
- 查找表(LUT):通常 4~6 输入,实现任意组合逻辑。你想想看,一个 4 输入 LUT 本质上就是一个 16×1 的 SRAM,输入地址,输出结果。
- 触发器(FF):每个 LUT 后面通常跟着一个 D 触发器,用于实现时序逻辑。
- 进位链(Carry Chain):用于快速实现加法器、计数器等算术运算。
- 多路选择器(MUX):用于 LUT 之间的灵活组合。
个人经验:我习惯在设计初期先估算一下 LUT 和 FF 的消耗比例。如果 LUT 用得多而 FF 用得少,说明你的设计偏组合逻辑;反过来则偏流水线。这个比例能帮你快速判断代码风格是否合理。
举个例子,一个简单的 2 选 1 多路选择器,用 LUT 实现只需要 1 个 LUT+1 个 FF(如果加寄存器)。但如果你用 case 语句写了一个 128 选 1 的大 MUX,那 LUT 消耗会急剧上升——因为综合工具需要级联多个 LUT。
// 一个简单的 2 选 1 示例
assign out = sel ? a : b;
// 综合后:1 个 LUT6(或 2 个 LUT4,取决于器件)
2.2 输入输出块(IOB)—— FPGA 与外界对话的“嘴巴”
IOB 是 FPGA 和外部电路之间的接口。每个 IOB 包含:
- 输入路径:可选的延迟单元(IDELAY)、输入寄存器
- 输出路径:输出寄存器、输出驱动(ODDR)
- 双向控制:三态控制逻辑
- I/O 标准支持:LVCMOS、LVDS、SSTL、HSTL 等
嗯,这里要注意:不同 bank 的 IOB 支持的电压标准可能不同。我曾经在项目中把 3.3V 的信号接到 1.8V bank 上,结果芯片直接冒烟了……所以设计原理图时一定要仔细核对 bank 电压。
避坑指南:Lattice 的 FPGA 中,某些高速接口(如 DDR3)必须使用特定的 IOB bank,并且需要参考电压 VREF。如果你把普通 GPIO 接到这些专用 bank 上,可能会造成信号质量下降甚至功能异常。
2.3 互连资源—— FPGA 的“高速公路网”
互连资源是连接 CLB、IOB、BRAM、DSP 等所有模块的布线网络。它分为几个层级:
- 局部互连:CLB 内部的 LUT 与 FF 之间的连接,延迟最小
- 全局互连:跨越整个芯片的长线,用于时钟、复位等全局信号
- 行/列互连:同一行或同一列模块之间的连接
- 通用互连:任意两个模块之间的可编程连接
说白了,互连资源就是 FPGA 的“血管”。如果设计中的信号路径太长,时序就容易出问题。我一般建议:关键路径上的逻辑尽量放在相邻的 CLB 中,这样可以减少互连延迟。
2.4 块 RAM(BRAM)—— 片上存储的“主力军”
BRAM 是 FPGA 内部的专用存储单元。Lattice 的 BRAM 通常为 18Kb 或 36Kb 大小,支持:
- 单端口模式:一个时钟域读写
- 双端口模式
- 简单双端口:一个端口只读,一个端口只写
- 真双端口:两个端口都可以读写
- FIFO 模式:内置空/满标志逻辑
| 模式 | 端口 A | 端口 B | 典型应用 |
|---|---|---|---|
| 单端口 | 读/写 | — | 寄存器文件 |
| 简单双端口 | 只写 | 只读 | FIFO、数据缓冲 |
| 真双端口 | 读/写 | 读/写 | 双核共享内存 |
我的习惯:当需要存储深度超过 64 个数据时,我基本不用分布式 RAM(LUT 实现),而是直接用 BRAM。因为 BRAM 的功耗更低、速度更快,而且不占用 LUT 资源。你想想看,一个 36Kb 的 BRAM 可以替代几百个 LUT,省下来的 LUT 可以去做更复杂的逻辑。
2.5 DSP 切片—— 数字信号处理的“加速器”
DSP 切片是专门为乘法、加法、累加等运算设计的硬件模块。Lattice 的 DSP 切片通常包含:
- 18×18 乘法器:支持有符号/无符号乘法
- 累加器:48 位宽,支持加减法
- 级联路径:多个 DSP 切片可以级联实现更高位宽的运算
举个例子,实现一个 FIR 滤波器,如果用 LUT 做乘法,一个 18×18 乘法需要大约 300 个 LUT。而用 DSP 切片,一个切片就够了,而且速度更快。我做过一个 128 阶的 FIR,用了 128 个 DSP 切片,跑到了 250MHz,如果用 LUT 实现,估计连 100MHz 都上不去。
// DSP 切片实现乘加运算示例(Lattice 原语风格)
// MULT18X18D 是 Lattice 的 DSP 原语
MULT18X18D u_mult (
.A(a), // 18-bit 输入
.B(b), // 18-bit 输入
.P(p) // 36-bit 输出
);
2.6 时钟管理单元(PLL/DLL)—— FPGA 的“心跳调节器”
时钟是 FPGA 设计的命脉。PLL(锁相环)和 DLL(延迟锁相环)负责:
- 频率合成:将输入时钟倍频或分频
- 相位调整:精确控制时钟相位
- 抖动滤除:降低时钟抖动
- 时钟去歪斜:消除时钟树延迟
PLL 和 DLL 的区别在于:PLL 通过压控振荡器(VCO)产生新时钟,而 DLL 通过延迟线调整相位。PLL 可以输出不同频率,DLL 则主要用于相位对齐。
我曾经踩过的坑:在设计多时钟域系统时,我习惯用 PLL 生成所有时钟,但有一次发现两个 PLL 输出的时钟之间存在固定的相位偏移,导致跨时钟域同步失败。后来我改用同一个 PLL 的不同输出,或者用全局时钟网络来保证同源性。记住:同源时钟是跨时钟域设计的基础。
小结
好了,FPGA 的六大核心模块就讲到这里。总结一下:
- CLB:实现逻辑功能,LUT+FF 是基本单元
- IOB:连接外部世界,注意 bank 电压和 I/O 标准
- 互连资源:模块之间的“血管”,影响时序
- BRAM:片上存储,比分布式 RAM 更高效
- DSP:数学运算加速,乘法器是核心
- PLL/DLL:时钟管理,保证时序收敛
理解这些模块,你就能看懂综合报告里的资源利用率了。下次我们聊聊怎么用这些模块搭一个实际的项目。