1. 为什么你需要动态部分重配置如果你正在数据中心里玩FPGA加速卡或者在做一些需要7x24小时不间断运行的智能硬件那你肯定遇到过这个头疼的问题想改一下FPGA里的业务逻辑就得把整个系统停下来重新烧录甚至重启服务器。这就像你想给一辆高速行驶的汽车换个轮胎却不得不先把车完全熄火拆掉轮子换好再重新点火上路。对于追求高可用性和灵活性的现代计算架构来说这种停机成本是难以接受的。这时候Xilinx Ultrascale FPGA的基于PCIE的动态部分重配置Partial Reconfiguration over PCIe 通常也叫PR over PCIe或MCAP技术就成了你的“救星”。简单来说它允许你把FPGA的逻辑划分成两部分一部分是静态区域比如PCIE控制器、DDR内存控制器、时钟管理这些基础设施它们一旦烧录进去就雷打不动始终保持工作另一部分是动态可重配置区域这里放着你的核心业务逻辑比如一个图像处理的AI引擎或者一个加密解密算法模块。你可以在系统运行中通过PCIE这个高速通道像更新手机APP一样动态地、在线地更换这个区域里的逻辑功能而整个FPGA的其他部分包括与主机服务器的PCIE连接都完全不受影响。我最早在数据中心加速卡项目里用上这个功能是因为客户提出了一个“零停机升级”的需求。他们的服务器集群全年无休但算法模型却需要频繁迭代。如果每次更新模型都要拔卡、烧录、重启运维团队怕是要疯掉。实测下来基于PCIE的动态重配置完美解决了这个问题业务切换平滑得几乎无感。这项技术的核心价值就在于它把FPGA从一块“固化的硬件”变成了“可动态编程的硬件资源池”极大地提升了硬件的利用率和系统的灵活性。2. 动手之前搞懂MCAP与你的硬件在撸起袖子开干之前我们得先弄清楚手里的“兵器”是否称手。不是所有FPGA都支持这个酷炫的功能。MCAP全称是Master Configuration Access Port你可以把它理解成FPGA内部一个专门用来接收配置数据的“后门”。这个后门被集成在了PCIE的硬核Hard IP里。所以实现基于PCIE的重配置有两个硬性前提你的FPGA芯片必须是UltraScale或UltraScale架构。这是Xilinx现在是AMD了从20nm工艺节点开始引入的系列比如常见的Kintex UltraScale KU系列或者像VCU1525加速卡上用的Virtex UltraScale VU9P。更早的7系列如Kintex-7 Virtex-7是不支持的。你的设计必须使用了PCIE硬核。因为MCAP通道是绑定在PCIE硬核内部的你不用PCIE这个“后门”也就不存在。这里有个生活化的类比FPGA就像一个大型办公楼芯片。JTAG配置好比是物业管理员拿着总钥匙JTAG线从正门配置管脚进去给整栋楼断电后重新布置所有房间逻辑单元。而MCAP配置呢就像是楼里已经常驻了一个快递站PCIE硬核。你需要更新某个特定楼层可重配置区域的家具业务逻辑只需要把新的家具打包生成部分比特流通过快递站内部的专用电梯MCAP通道直接送上去更换即可其他楼层静态区域的办公完全不受干扰整栋楼也从来不停电。所以在你开始新建工程前请务必确认你的板卡和芯片型号。我用的测试平台是VCU1525上面的FPGA是Virtex UltraScale XCVU9P这是一块非常经典的用于验证高性能计算和网络功能的加速卡。3. 从零开始搭建一个PR工程框架好了理论铺垫完毕我们进入实战环节。我会用一个超级简单的例子带你走完全程我们要实现一个可以通过PCIE动态切换的LED控制模块。一个重配置模块RM让LED常灭另一个RM让LED常亮。虽然功能简单但流程和复杂应用一模一样。3.1 工程创建与IP核配置首先打开Vivado我用的版本是2022.1建议使用2019.1及以后版本对PR的支持更完善创建一个新工程。工程类型选择RTL Project在添加源文件的步骤可以先跳过。关键的一步来了因为我们依赖PCIE硬核的MCAP功能所以需要例化一个PCIE相关的IP。这里有两种常见选择使用裸的PCIE硬核AXI Bridge for PCI Express Gen3更底层控制更灵活但需要自己处理很多配置细节。使用XDMA IP这是Xilinx提供的一个“一站式”DMA解决方案IP它内部封装了PCIE硬核、DMA引擎、用户接口等。对于快速原型验证和大多数应用来说用XDMA会更方便因为它帮我们处理了很多繁琐的配置并且直接提供了MCAP的使能选项。我们选择用XDMA。在IP Catalog中搜索并打开XDMA Subsystem for PCIe。配置XDMA时有几个选项需要特别注意它们直接关系到MCAP功能能否启用Basic选项卡 - Mode不要用默认的Basic把它改成Advanced。这样才能看到更多高级设置。Basic选项卡 - Tandem Configuration or Partial Reconfiguration这是核心设置在下拉菜单中选择PR over PCIe。这个选项一旦选中Vivado就会为我们启用并配置MCAP通路。PCIe ID选项卡 - Device ID这里需要设置一个特定的ID以便Xilinx提供的官方MCAP驱动能够识别你的设备。根据文档可以设为0x8011、0x8038或0x506F。我们这里填8011。Board选项卡如果你用的是Xilinx官方板卡比如VCU1525可以直接在这里选择你的板卡型号Vivado会自动帮你匹配参考时钟、复位等引脚非常省事。如果非官方板卡就需要自己在XDC约束文件中手动约束PCIE的参考时钟、复位等引脚。其他参数比如PCIE的链路速度Gen3 x8、DMA通道数、时钟频率等可以根据你的实际板卡和需求调整。对于这个演示大部分保持默认即可。配置完成后点击“OK”生成IP。Vivado会提示“Out-of-context (OOC) Per IP”建议选择Global这样综合速度会快一些。3.2 利用Example Design快速搭建测试平台手动编写顶层文件连接XDMA的所有接口是比较繁琐的。Xilinx提供了一个非常好的起点——Example Design。在Sources窗口中找到你刚生成的XDMA IP例如xdma_0右键点击选择Open IP Example Design。Vivado会为你新建一个工程这个工程已经包含了XDMA IP、时钟生成、复位处理以及与PCIE引脚连接的所有逻辑是一个可以直接烧录并运行的基础框架。我们就在这个Example Design工程上进行修改这样能省去大量搭建基础框架的时间。打开后的设计顶层模块通常包含XDMA IP、时钟模块clk_wiz、复位模块以及一些用于测试的AXI4-Stream接口逻辑。我们的目标是在这个稳定的“底盘”上添加我们的可重配置模块。4. 定义你的动态可重配置区域现在我们要创建本次演示的核心——两个可重配置模块Reconfigurable Module, RM。4.1 创建RM模块在Sources窗口中右键点击设计源文件选择Add Sources-Create File创建两个新的Verilog模块文件led_rm_off.v这个模块的功能是让LED熄灭。led_rm_on.v这个模块的功能是让LED点亮。为了让Vivado能识别它们是可互换的RM这两个模块必须有完全相同的接口端口列表。也就是说它们对“外界”静态逻辑的“插座”必须一模一样。// led_rm_off.v 的内容 module led_rm_off ( input wire clk, input wire rst_n, output reg led ); // 这个模块让LED保持为0灭 always (posedge clk or negedge rst_n) begin if (!rst_n) begin led 1b0; end else begin led 1b0; // 始终输出0 end end endmodule// led_rm_on.v 的内容 module led_rm_on ( input wire clk, input wire rst_n, output reg led ); // 这个模块让LED保持为1亮 always (posedge clk or negedge rst_n) begin if (!rst_n) begin led 1b0; end else begin led 1b1; // 始终输出1 end end endmodule你看除了核心的业务逻辑led赋值为0或1不同模块名、输入输出端口clkrst_nled完全一致。这就是RM设计的关键原则接口一致性。4.2 在顶层实例化并启用PR接下来我们需要在顶层模块通常是Example Design的顶层文件比如叫design_1_wrapper或xdma_example_top中实例化其中一个RM。我们先实例化led_rm_off。找到顶层文件的合适位置通常在时钟和复位逻辑之后用户逻辑部分添加如下代码// 实例化可重配置模块初始使用灭灯模块 led_rm_off u_led_rm ( .clk (user_clk), // 连接到一个用户时钟比如XDMA输出的user_clk .rst_n (user_resetn), // 连接到用户复位注意是低有效复位 .led (fpga_led) // 连接到FPGA板上的一个LED引脚 );同时你需要在顶层模块的端口声明中增加output wire fpga_led并且最重要的一步是在工程的XDC约束文件中找到对应的LED网络比如fpga_led为其添加管脚位置和电平标准的约束。做完这些一个包含基础PCIE功能和初始RM灭灯的完整设计就有了。现在我们要告诉Vivadou_led_rm这个实例是一个可重配置分区。在Vivado菜单栏点击Tools-Enable Partial Reconfiguration...。这时会弹出一个对话框告诉你工程将被转换。点击Convert。转换完成后你会发现在左侧的Flow Navigator窗口中PROJECT MANAGER下方多出了一个Partial Reconfiguration Wizard的选项。这说明你的工程已经成功开启了PR模式。5. 使用PR向导完成关键配置Partial Reconfiguration Wizard是我们配置PR流程的“导航仪”跟着它一步步走不容易出错。定义分区在Sources窗口的Hierarchy标签页下找到你刚刚实例化的led_rm_off模块u_led_rm。右键点击它选择Create Partition Definition...。在弹出的窗口中为这个分区起个名字比如pr_led点击OK。完成后你会看到该实例的图标变成了一个黄色的菱形这表示它已被标记为一个可重配置分区。启动向导点击Flow Navigator中的Partial Reconfiguration Wizard启动向导。编辑重配置模块在Edit Reconfiguration Modules界面点击“”号。这里我们要把另一个RM模块led_rm_on添加进来。在弹窗中找到led_rm_on模块将其添加到pr_led分区下。现在pr_led这个分区下就有了两个可互换的RMled_rm_off和led_rm_on。这意味着这个物理区域未来可以动态加载这两个模块中的任何一个。创建配置在Edit Configurations界面选择Automatically create configurations。Vivado会自动创建两个配置Configuration通常命名为config_1和config_2。你可以把它们重命名为更有意义的名字比如config_led_off和config_led_on分别关联led_rm_off和led_rm_on模块。完成向导后续的Edit Configuration Runs界面继续选择自动创建运行配置Automatically create configurations。一路点击Next最后点击Finish。向导会为这两个配置创建独立的综合与实现运行Run。6. 物理规划与实现给RM画个“地盘”配置信息有了我们还得告诉VivadoFPGA芯片上的哪一块物理区域是划给这个RM使用的。这个过程叫做Floorplanning物理规划。首先点击Run Synthesis对整个设计进行综合。综合完成后选择Open Synthesized Design。在打开的界面中找到并点击Floorplanning视图。你可能会看到一堆密密麻麻的逻辑单元。在Netlist窗口中找到你的RM实例u_led_rm。右键点击该实例选择Floorplanning - Draw Pblock。这时你的鼠标会变成十字形回到Floorplanning视图的芯片版图上拖动鼠标画出一个矩形区域将这个实例包含的所有逻辑目前只是几个寄存器框选进去。这个矩形区域就是你为pr_led分区分配的物理资源Pblock。划区域是个技术活有几点经验分享资源要充足区域不能画得太小要预留足够的SLICE查找表和寄存器、DSP、BRAM等资源以备RM逻辑未来增长。可以适当画大一点。考虑布线区域形状尽量规整长宽比不要太夸张有利于工具布线。避开关键资源注意避开PCIE硬核、时钟管理单元MMCM/PLL、高速收发器GT等固定位置的关键资源所在的列。画好Pblock后Vivado会自动生成对应的约束语句并保存到你的XDC文件中。你可以打开看看大概长这样create_pblock pr_led resize_pblock pr_led -add {SLICE_X10Y100:SLICE_X20Y150} add_cells_to_pblock pr_led [get_cells u_led_rm]运行DRC检查在Tools菜单下选择Validate Pblocks或运行Report DRCC检查你划分的Pblock是否合理有没有违反任何设计规则。确保没有严重错误Critical Warning可以适当关注。7. 生成比特流与关键文件物理规划搞定后就可以生成最终的比特流文件了。点击Generate Bitstream。Vivado会依次为config_led_off初始配置和config_led_off另一个配置运行布局布线并生成比特流。这个过程会比普通设计长一些因为工具需要分别处理静态逻辑和两个不同的RM逻辑。生成完成后在工程目录下的project.runs文件夹里你会看到类似这样的结构impl_1这里面存放的是静态逻辑的比特流文件design_1_wrapper.bit。这个文件包含了PCIE、时钟等所有静态部分以及初始RMled_rm_off的逻辑。这个文件需要先烧录到FPGA中。child_0_impl_1和child_1_impl_1这两个文件夹分别对应两个配置运行。在每个文件夹的debug子目录下你能找到对应的部分比特流文件Partial Bitstream文件名可能类似pr_led_led_rm_on_partial.bit和pr_led_led_rm_off_partial.bit。这两个.bit文件才是我们后面要通过PCIE动态加载的“差量包”。这里千万要分清静态比特流是“全量包”负责搭建整个FPGA的固定框架和初始功能部分比特流是“增量包”只包含动态区域里特定RM的逻辑信息。动态重配置时我们只传送和加载这个“增量包”。8. 上位机驱动与加载实战比特流生成了怎么通过PCIE把它“灌”进FPGA呢这就需要上位机软件和驱动了。Xilinx提供了一套基于Linux内核驱动和用户空间工具的方案。核心文档是Xilinx Answer 64761标题是“Partial Reconfiguration of UltraScale/UltraScale FPGA via PCIe (MCAP)”这份文档是必读的圣经。简单来说流程分为以下几步加载静态比特流首先你需要通过传统方式比如JTAG或者对于已编程了启动镜像的板卡上电加载将那个静态比特流design_1_wrapper.bit烧录到FPGA中。完成后你的FPGA应该已经作为一个PCIE设备被系统识别使用lspci命令可以看到并且LED处于熄灭状态因为我们初始RM是灭灯模块。编译与安装驱动在Linux主机上根据Xilinx Answer 64761的指引编译并安装MCAP驱动模块通常是xilinx_mcap.ko。这个驱动会创建一个字符设备例如/dev/mcap0用于与FPGA的MCAP硬件进行通信。使用工具加载部分比特流Xilinx提供了一个用户空间的C程序示例在文档或驱动包中或者你也可以使用一些脚本工具。其核心操作就是打开/dev/mcap0设备文件然后将部分比特流文件比如pr_led_led_rm_on_partial.bit的数据通过write系统调用写入这个设备。驱动会将这些数据通过PCIE总线经由MCAP通道准确地写入到FPGA中你之前划分好的Pblock区域。一个非常简单的命令行操作示意具体命令需参考官方示例# 假设有工具叫 pr_loader sudo ./pr_loader -d /dev/mcap0 -f pr_led_led_rm_on_partial.bit命令执行成功后你应该会立刻看到板卡上的LED被点亮了整个过程系统无需重启PCIE链路也从未中断。踩过的一个坑如果你像原始文章作者一样使用的是XDMA IP那么系统中可能同时存在XDMA的驱动和MCAP驱动。需要确保它们不冲突有时需要整合或者正确设置设备ID让两个驱动能协同工作。这可能需要一些内核驱动开发的调试对于不熟悉驱动开发的朋友来说确实是初期的一个小门槛。9. 进阶技巧与避坑指南走通基本流程只是第一步想把PR技术真正用在产品里还得注意下面这些实战细节时钟与复位处理这是RM设计中最容易出错的地方。供给RM的时钟必须来自静态区域并且要使用BUFGCE这类带使能端的全局时钟缓冲器。当通过PCIE加载新的部分比特流时需要先“冻结”RM的时钟Clock Domain Isolation加载完成并确认逻辑稳定后再释放复位。静态逻辑需要负责产生这个精细的复位序列通常是一个“复位桥”Reset Bridge逻辑确保RM在加载前后处于可控状态。接口信号与协议RM与静态区域之间的所有信号接口都必须被正确地“隔离”Isolation。当RM被重配置时这些接口信号必须被置为安全值比如拉低防止未知信号干扰静态逻辑。Vivado的PR工具链可以帮助插入隔离器Isolation Cell但你需要确保在代码中使用了(* DONT_TOUCH “true” *)等属性来保留这些关键网线防止被优化掉。时序收敛PR设计对时序的要求更为严格。静态区域的时序必须独立收敛且要为RM的接口预留足够的时序裕量Slack。因为不同的RM实现其内部逻辑和到达接口的路径延迟可能不同。在布局布线时要使用opt_design -preserve等命令来保持静态逻辑的布局不变确保每次重配置后的时序都是可预测的。调试与验证PR的调试比普通设计复杂。除了传统的ILA集成逻辑分析仪还可以利用Vivado的PR Verify功能在布局布线前检查RM接口的一致性。在硬件调试时可以设计一个简单的状态机通过PCIE BAR空间访问实时读取RM的内部状态或版本号确认加载是否成功。最后我想说基于PCIE的动态部分重配置是一项强大但稍显复杂的技术。我第一次做的时候光是在划分Pblock和调试驱动上就花了好几周。但一旦跑通那种无需停机就能让硬件“焕然一新”的体验绝对是传统FPGA开发无法比拟的。它真正打开了硬件灵活化的大门特别适合算法快速迭代、多任务时分复用硬件资源的场景。建议你先用这个LED的例子把整个工具链和流程跑熟理解每一个步骤背后的意义然后再逐步应用到更复杂的真实项目中。遇到问题多查Xilinx官方文档Answer Records、UG909和社区论坛很多坑前面的人都踩过了。