ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

FPGA与CPLD结构差异:从LUT、宏单元到资源与时序收敛

FPGA与CPLD结构差异:从LUT、宏单元到资源与时序收敛 简介面向EDA课程学习者这份PPT课件系统梳理可编程逻辑器件中CPLD与FPGA的结构原理覆盖PLD基本概念、PROM/PLA/PAL/GAL四种简单类型以及从PAL/GAL发展而来的CPLD三大部分I/O块、功能块、互连矩阵和FPGA内部逻辑阵列块、宏单元、乘积项选择矩阵、可编程寄存器、可编程连线阵列等核心模块并对比两种器件的结构差异与适用场景。课件还介绍了硬件测试方法、大规模PLD产品概况以及通过JTAG等接口完成编程配置的流程能够帮助电子工程、嵌入式系统方向的学生与刚接触EDA的工程师快速建立清晰的知识框架。资源包内含1个PPT演示文稿大小约1.12MB内容紧凑、图文结合每页要点明确既可用于课堂辅助教学也便于自学时对照章节逐步理解。目前已有150人浏览学习适合配合教材学习或实验环节对照使用。1. 会写 Verilog 不等于懂 FPGA 与 CPLD两种“可编程”背后是两套完全不同的电路骨架很多工程师习惯把 FPGA 和 CPLD 统称为“可编程逻辑器件”等到综合报告出来才发现问题明明只是写了一个assign y a b为什么综合结果占了这么多 LUT查找表为什么同样的逻辑放在 CPLD 上延迟是固定的放到 FPGA 上路径时延却每次都飘这两个问题如果只停留在“代码写法”层面永远解释不通。根源在于FPGA 和 CPLD 虽然都叫“可编程”内部却分别是两套物理实现FPGA 靠 SRAM 查找表加可编程布线CPLD 靠非易失性存储驱动的与或阵列。写代码的人其实是在指挥 EDA 工具搬运这些底层结构不懂结构综合结果就是黑盒时序收敛就是碰运气。这篇文章把第 2 章结构原理部分的课件内容落回到资源报告、引脚约束、复位信号和数码管动态显示这些日常场景适合刚接触可编程逻辑器件EDA设计的学生也适合面试前需要把 FPGA 与 CPLD 结构差异说清楚的开发者。2. 拆开 FPGALUT、Slice 与块资源如何决定综合结果2.1 查找表的本质一个用 SRAM 实现任意功能的小型真理机FPGA 最基本的可编程单元是查找表Look-Up TableLUT。现代主流 FPGA 中常见的 6 输入 LUT内部并不存在“与门”“或门”这样的真实门电路而是一块 64 bit 的 SRAM外加一个 64 选 1 的多路选择器。SRAM 的每一位存储在综合时被预先写入真值表结果运行时输入信号作为选择器的地址线选中对应的 SRAM 位并输出。这意味着无论你写的是a b c、a ^ b还是一个复杂的算术表达式只要输入不超过 LUT 的输入端口数EDA 工具最终都只是把这张真值表“烧”进 SRAM。module and3( input a, b, c, output y ); assign y a b c; endmodule这段代码综合到 FPGA 后不会有一个真实的“三输入与门”器件出现而是被映射到某个 LUT 的存储单元里。整体资源占用量通过综合报告都能看到比如在 Vivado 中打开综合设计后执行open_run synth_1 report_utilization -file utilization.rpt读完utilization.rpt就能看到当前设计用掉了多少 LUT。所谓“可编程逻辑”的推理过程非常直接无论组合逻辑多么复杂只要输入不超过 LUT 的端口数就可以用一个 LUT 完成超出端口数时才会多级级联级联会增加组合路径延迟。这也是为什么综合报告里看到 LUT 数量上升时要先回头检查是否发生了过多的逻辑级联。6 输入 LUT 能显著降低级数但代价是每个 LUT 都要消耗 64 bit SRAM半导体面积增长明显。2.2 Slice 与 CLBLUT 和触发器是搭伙生活的LUT 只能做组合逻辑数字电路还需要寄存器来保存状态。于是 FPGA 将 LUT 和触发器打包成更大的单元典型布局是每个 Slice 包含 4 个 6 输入 LUT、8 个触发器和进位链两个 Slice 合起来组成一个 CLB可配置逻辑块。触发器的 D 端接到 LUT 输出时钟、使能、复位引脚则来自芯片内部的全局时钟网络。这种打包方式意味着写代码时一个寄存器通常要对应一个触发器。always (posedge clk) begin if (rst_n) q 1b0; else if (en) q d; end对综合工具来说always (posedge clk)这样的写法会直接被识别为一个寄存器。重点在于复位类型这里用的是同步复位。同步复位不会给触发器增加额外引脚只是让 LUT 在复位条件下输出 0所以综合后往往不额外增加触发器资源而异步复位才会让工具额外分配专用复位端口。很多入门教程推荐“一律使用异步复位”并不是因为结构上更优而是早期器件定义的惯性。在选型阶段同一份代码如果目标器件换成一个不支持异步复位的架构综合工具可能会用 LUT 模拟复位行为资源即刻翻倍。这也是“代码移植到不同 FPGA 前必须先看结构差异”的原因。进一层CLB 内部还集成了专用进位链CARRY4 一类逻辑。加法器、计数器这类频繁进位传播的结构会被自动映射到进位链上从而绕开普通 LUT 级联的延迟。如果综合报告显示加法器没有使用专用进位链往往是因为代码写出了带条件的加法或者把进位逻辑人为拆分破坏了工具对结构的识别。这时候需要做的是改写代码风格而不是去约束 LUT 的摆放位置。2.3 BRAM 与 DSP搬出 LUT 才能跑得动的专业资源纯靠 LUT 搭 RAM 是可行的但成本太高。一个 32×32 的同步 RAM 如果全部用 LUT 实现要消耗上百个 LUT还要额外占用大量布线资源。FPGA 为此专门设计了块 RAMBRAM。BRAM 是硬化的大容量存储阵列常见的单块容量有 18 Kb 和 36 Kb 两种规格可以通过配置变成不同位宽的单端口、双端口 RAM 或 FIFO。综合工具会自动根据容量和端口模式推断是否使用 BRAM但代码里若写成全异步读写BRAM 无法处理异步读写场景就会被工具降级为分布式 RAM也就是 LUTRAM。reg [7:0] mem [0:1023]; always (posedge clk) begin if (we) mem[addr_w] data_w; data_r mem[addr_r]; end这段代码是经典的单时钟同步读写会被推断成 BRAM。逻辑结构层面控制器看的是写使能、地址、写数据在同一个时钟沿采样读数据在时钟沿后输出。一旦写出了异步读工具只能用 LUT 去搭组合逻辑相当于把存储打散到可配置逻辑块里资源利用率立刻失控。在 FPGA 图像处理场景里一行图像要缓存行长个像素正确使用 BRAM 会让资源利用率从 30% 降到 3%。DSP Slice 是另一类硬核资源内部包含乘法器、加法器和累加器。卷积、FIR 滤波、定点数运算这些操作应当尽量把乘加映射到 DSP 上否则大位宽乘法器用 LUT 拼接会形成很长的组合路径。FPGA 定点数的设计目标不只是找到合适的位宽还包括让工具在综合阶段认出“这是带符号乘法”“这是乘累加”的结构。代码风格显然很关键always (posedge clk) begin acc acc a * b; end这种乘累加写法在很多工具里反而不会直接映射到 DSP因为工具会担忧反馈回路的结构先综合成普通算术再融合。更好的写法是先实例化 DSP IP 核或者在代码里手动拆分乘法和累加再用综合属性指定算术单元。设计评估阶段最理想的做法是第一版先用 IP 生成器搭一个最简卷积器替换掉手写乘累加对比资源报告里 DSP 数量的变化。2.4 打开 FPGA 编辑器从 LUT、FF 到底层视觉验证综合报告里的数字始终不够直观尤其是在做结构原理教学时最好是能看到“哪个 LUT 对应哪行代码”。Vivado 和 Quartus 都提供了逻辑编辑器和芯片规划视图。打开综合后的设计再打开器件视图可以手动追踪某个 LUT 的位置查看其输入的连接关系。这个方法在调试特定路径时也很有用。比如奔跑的数码管动态显示程序如果发现段码译码电路消耗了远超预期的 LUT可以在器件视图里放大对应 CLB检查工具是否生成了两级 LUT 级联进而思考模块拆分是否合理。这一步对学习结构原理的价值在于原本抽象的“6 输入查找表”会成为看得见的资源块。引脚规划也在这里完成FPGA 的 IOB 包含输入输出缓冲器、寄存器、上下拉电阻和差分对。高速差分信号比如 LVDS必须使用专用的差分对引脚和 IOB 内部差分缓冲器普通 GPIO 无法处理这种信号。具体连接方式在下一章通过引脚约束来落地。3. CPLD 的乘积项架构确定延迟与片上 Flash 带来的实际取舍3.1 与或阵列和乘积项CPLD 的逻辑实现是“连线”而不是“查表”CPLD 的核心叫宏单元Macrocell。每个宏单元内部是一个与阵列、或阵列和可配置寄存器的组合。输入信号经过可编程的与阵列产生一组乘积项乘积项再经过或阵列合并成最终输出。一个典型的关系可以写作output P1 P2 P3 ...其中每个Pn都是若干输入信号的“与”组合。例如要输出y ab | cdCPLD 里看到的是一条从输入 a、b 进与门与或阵列再与 c、d 的乘积项合并的固定路径。这个路径上的延迟和逻辑资源关系不大主要取决于信号穿越阵列时经过的开关层级而 CPLD 的布线池是全局的路径长度基本固定所以延迟可预测。FPGA 的互连要通过大量可编程开关点开关的导通状态由 SRAM 决定路径是动态路由的因此同样逻辑的延迟在每次布局布线后都可能不同。这是 FPGA 时序收敛难、CPLD 从容的根本原因。需要补充的是CPLD 的与或阵列是由非易失性存储单元控制的比如 EEPROM 或 Flash。器件掉电后配置依然保留上电瞬间逻辑就开始工作。这种特性非常适合作为主板上的“胶合逻辑”比如地址译码器、总线缓冲器、复位管理逻辑。而 FPGA 的 SRAM 配置在掉电后丢失每次上电都要从外部配置芯片加载比特流启动过程如果存在竞争风险就需要额外设计上电时序。一句话总结硬件特性CPLD 是“焊上去就能跑的”FPGA 是“等它加载完才敢用的”。很多系统设计会把启动配置逻辑放在一片小 CPLD 里等 FPGA 配置完成后再释放复位这两类可编程逻辑器件的配合本身就依赖对结构差异的理解。3.2 FPGA 与 CPLD 结构对比一张表决定芯片选型对比维度FPGACPLD基本逻辑单元LUT 触发器乘积项 宏单元配置方式SRAM掉电丢失EEPROM/Flash掉电保持典型容量数千到数百万逻辑单元数十到数千宏单元布线方式可编程互连开关全局布线池延迟特性随布局变化固定可预测启动速度需要从外部加载配置上电即可工作典型应用高速接口、图像处理、通信基带胶合逻辑、总线接口、初始化控制这张表可以作为选型手册的第一页。FPGA 适合数据通路宽、状态复杂、需要大容量 RAM 的场景CPLD 适合逻辑规模不大但时序要求严格、上电就必须处于确定状态的设计。一个小规模系统里两者常同时出现CPLD 负责系统的时基复位和总线译码FPGA 负责复杂的协议处理和图像算法。选型错误时常表现为用小 CPLD 装一个 FIFO宏单元和乘积项根本不够用只好用“宏单元扩展”方式拼接延迟优势也不复存在或者用大 FPGA 只做简单的三态缓冲空闲资源浪费严重还增加了配置芯片和启动时序的复杂度。3.3 写出能落到乘积项的代码case 语句与宏单元的匹配CPLD 的宏单元数量普遍不多几十到几百个因此用 CPLD 时代码风格要尽可能贴合乘积项结构。多路选择逻辑最典型case语句的每个分支都会对应一个乘积项工具化简时会把共享项合并。假设要实现一个按键扫描译码器casez (key) 3b1??: y 4d1; 3b01?: y 4d2; 3b001: y 4d3; default: y 4d0; endcase每个分支的?在综合时就是与阵列上“不关心”的配置相当于跳过了某些输入信号的与项。逻辑上这段代码会被映射成三条乘积项再通过或门输出。宏单元中可配置寄存器的存在也让时序逻辑能在每个宏单元里完成。需要警惕的是如果代码写出了合成器无法化简的“Case 优先级”结构比如if/else if会引入优先级链乘积项数量成倍增加。通常在 CPLD 设计里用case而不是if/else if表达并行选择逻辑用综合属性parallel_case提示工具这些分支是互斥的可以并行输出到或阵列。Quartus 综合属性写法(* parallel_case *) case (sel)属性本身不会改变功能只会影响综合工具对结构的推断。EDA 工具在 CPLD 综合时还会提供乘积项利用率和宏单元利用率两个独立报告乘积项利用率偏高说明逻辑化简不彻底宏单元利用率偏高说明寄存器或 IO 占多了。两份报告要分开看不能只盯其中一个数字。4. 在 EDA 工具里读资源报告、引脚约束与时序路径4.1 资源报告的正确读法先看比例再看绝对数量拿到一份典型的 FPGA 资源报告重点关注 LUT、触发器、BRAM、DSP 和 IO。表格如下资源类型已用可用利用率CLB LUTs3214634005%CLB Registers27651268002%Block RAM Tile122704%DSPs82403%Bonded IOB4121019%不要只看百分比要看 LUT 和 Register 的数量关系。一个寄存器通常伴随一个 LUT二者比值接近 1:1 时说明设计里“状态与组合逻辑”均衡分布工具布局布线结构也相对健康。如果 LUT 数量是寄存器的 5 倍以上说明组合逻辑链过长或大量逻辑被综合成了纯组合函数如果寄存器数量远超 LUT则说明存在大量未参与运算的“搬运”寄存器考虑是否真正需要每个流水级都打一拍。接着看 BRAM 使用量和 DSP 使用量的匹配关系例如图像处理中一个 3×3 卷积分支通常需要 2 到 3 个 DSP如果 DSP 用了很多但 BRAM 为 0可能是把图像行缓存误写成了分布式逻辑导致 LUT 暴涨。见到这种失衡我的第一反应是回到 RTL 检查实例化而不是继续优化综合选项。grep -E CLB LUTs|CLB Registers|Block RAM|DSPs|Bonded IOB utilization.rpt这段命令用于持久化保存报告里关键行。在无人值守的回归构建里用脚本从资源报告抽取这几个数字就能自动比对每次代码提交前后的资源变化。参数说明utilization.rpt是 Vivado 生成的报告名如果是 Quartus 则需要在编译流程里勾选报告生成选项输出文件路径略有不同。养成“看比例而非看绝对值”的习惯后资源调整才能有的放矢。4.2 复位信号与亚稳态为什么结构上讨厌“到处复位”FPGA 里的寄存器数量是固定的异步复位引脚也不是每个触发器都有。多数现代 FPGA 架构里寄存器可以综合成带异步复位但代价是复位网络要单独布线复位信号的扇出和时序约束就变得特别棘手。复位信号的移除时间与时钟沿之间一旦出现冲突就会进入亚稳态这是“FPGA 复位信号亚稳态”面试题的核心来源。结构上更稳妥的做法是顶层用异步复位释放内部所有寄存器采用同步复位。reg rst_n_sync1, rst_n_sync2; always (posedge clk or negedge rst_n) begin if (!rst_n) begin rst_n_sync1 1b0; rst_n_sync2 1b0; end else begin rst_n_sync1 1b1; rst_n_sync2 rst_n_sync1; end end两拍同步后的rst_n_sync2作为全设计同步复位源。参数说明第一级触发器会进入“亚稳态”但有了一个时钟周期的决断时间第二级输出已经稳定可以安全地驱动后续所有寄存器的同步复位端。这种结构牺牲了复位速度但换来了极高的可靠性。在综合报告里查看复位信号扇出时若发现复位网络连接的寄存器超过设计预期优先检查是否有人在组合逻辑里使用了异步复位。另外复位信号本身也要被加入时序约束否则工具默认它是伪路径根本不分析其边沿与时钟的关系。约束写法set_false_path -from [get_ports {reset_n}] -to [all_registers]这行命令告诉时序分析工具 reset_n 不需要做数据路径时序收敛但要注意如果复位被误设成 false path异步复位的释放时间也完全不受约束这正是亚稳态的高发来源。所以我的习惯是同步复位信号不加 false path异步释放的信号专门约束 recovery/removal 时间。4.3 引脚约束、LVDS 与 IOB 结构的关系FPGA 的引脚不是均匀普通的金属焊盘而是映射到特定 Bank 的 IOB 结构。每个 Bank 有独立的 VCCO 供电Bank 内的 IO 标准必须匹配同一个电平标准。例如 Bank 供电 1.8V 就不能直接接 LVCMOS33 信号强行接入轻则采样出错重则损伤 IOB。差分对信号比如 LVDS必须使用引脚对中间一般不能走普通单端线。set_property PACKAGE_PIN M18 [get_ports {led[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}] set_property PACKAGE_PIN H5 [get_ports {rx_p}] set_property PACKAGE_PIN H6 [get_ports {rx_n}] set_property IOSTANDARD LVDS [get_ports {rx_p}] set_property IOSTANDARD LVDS [get_ports {rx_n}] set_property DIFF_TERM TRUE [get_ports {rx_p}]PACKAGE_PIN是芯片物理引脚编号IOSTANDARD是电气标准DIFF_TERM使能差分输入内部端接电阻。关键动作是差分输入的两个引脚必须由同一对物理管脚提供且标准要分别声明到 P 和 N 端。FPGA 的 LVDS 接收在硬件上把差分信号转为单端后可以直接进入内部逻辑不需要额外的外部电阻网络节省板级面积。在原理图绘制阶段就要把 Bank 电压规划好使用立创 EDA 画 FPGA 电路时同一个 Bank 的网络必须仔细检查电压域否则 PCB 打样回来才发现在同一个 Bank 同时使用了 1.8V 和 3.3V 的器件。这也是 FPGA 器件选型时比 CPLD 要额外关注的因素。4.4 时序路径与互连延迟降低 LUT 级联是唯一出路结构知识最终要落到“为什么关键路径优化不动”这件事上。FPGA 的一条数据路径由四段延迟组成逻辑延迟LUT 内部延迟、布线延迟互连开关、触发器建立时间和时钟偏斜。其中 LUT 内部延迟相对固定布线延迟占了大头。因此当你发现关键路径很长时最可靠的动作是减少路径上经过的 LUT 级数。wire p0 a0 * b0; wire p1 a1 * b1; wire sum p0 p1;以上三段独立运算如果用一条组合链路串起来综合工具为了满足面积约束可能会把乘法器拆成多级小 LUT形成 5 级甚至更长的组合。改动方案是把整个链路打一拍always (posedge clk) begin p0_reg a0 * b0; p1_reg a1 * b1; end always (posedge clk) begin sum_reg p0_reg p1_reg; end结构上就是给关键路径插入了流水寄存器时序报告里路径级数会明显下降。插入流水线后吞吐量不变但响应延迟增加。由架构本身决定的路径往往可以通过多拍流水换取更高的时钟频率这是 FPGA 设计中把组合逻辑打散到寄存器之间的基本手段。时序报告里的最差负裕量可以从负数修到零附近只要回头检查模块划分是否把运算链路切得足够细。5. 用“结构倒推法”校验设计手工预算 LUT 与 FF 数量并对比综合报告这里分享一个我非常喜欢用的技巧在写大规模模块之前先手工估算模块的资源占用再与实际综合报告对比。以常见的 FPGA 实现数码管动态显示为例。四位数码管按 1 kHz 扫描刷新需要一个分频计数器、位选计数器和段码译码逻辑。reg [15:0] cnt; reg [1:0] sel_cnt; always (posedge clk) begin cnt cnt 1b1; end always (posedge clk) begin if (cnt 16d49999) sel_cnt sel_cnt 1b1; end always (*) begin case (sel_cnt) 2d0: seg_data seg_rom[hex_data[3:0]]; 2d1: seg_data seg_rom[hex_data[7:4]]; 2d2: seg_data seg_rom[hex_data[11:8]]; 2d3: seg_data seg_rom[hex_data[15:12]]; endcase end手工推算分频计数器 16 bit 占用 16 个 FF位选计数器 2 bit 占用 2 个 FF段码 ROM 如果是只读逻辑在 LUT 里实现约需要 7 个 LUT一个数码管 7 段。位选信号为了让四个数码管轮流导通需要至少 2 个 LUT。合计预计 20 个 FF 加 9 个 LUT 左右。综合报告出来如果与预算差 2 倍以上说明产生了意外结构比如cnt 16d49999的比较器被综合成多级 LUT 而不是进位链的快速比较或者seg_rom被推断成一块分布式 RAM 而不是组合逻辑。这个偏差本身就是最重要的排错线索。读报告的方法也值得固化下来。使用综合工具生成报告后可以直接用命令行提炼grep -E LUT as Logic|Register as Flip Flop|Block RAM|DSP utilization.rpt如果发现 FF 数量远大于手工预算的寄存器数量就去 RTL 里查循环变量是否被意外综合成了寄存器grep -n reg rtl/*.v这个技巧能捕捉到新手常见的“信号定义成 reg 但从未在 always 中赋值”这一类错误常常让综合工具推断出多余的寄存器资源利用率与实际设计意图严重脱节。在 FPGA 面试中这个检查顺序也常常被用作考察候选人是否理解资源结构的试金石。report_timing_summary -delay_type min_max -max_paths 20 -file timing.rpt最后把时序报告也拉进来。如果手工估算出的逻辑级数是 3 级但时序报告却报了巨大的组合延迟先检查 LUT 级联情况再看约束是否人为制造了多周期路径。用命令report_design_analysis查看每个逻辑浅层延迟分布通常能定位到某一段组合电路。结构是静态的但每次综合工具在布局布线时的选择并不完全相同理解了 LUT、FF、BRAM、DSP 这些资源的工作方式遇到资源异常和路径违例时才不会只是反复调整综合策略而是直接回到 RTL 层去修正设计。本文还有配套的精品资源点击获取
返回列表