
1. 为什么要在 Versal 上折腾 AXI NoCVersal 这颗芯片跟传统 FPGA 最大的区别就是它内部塞了一张AXI NoCNetwork on Chip。你可以把它理解成芯片内部的一张高速公路网PL 端的逻辑、PS 端的处理器、DDR 控制器、各种硬核 IP全都挂在这张网上。以前在 Zynq 上做互联靠的是 AXI Interconnect 一层层搭带宽和延迟全看你怎么连到了 VersalNoC 直接把这件事硬件化了你只需要配置从哪个口进、从哪个口出、走哪条路径、给多少带宽剩下的交给 NoC 自己调度。这个项目标题里的三个关键词——AXI NoC 配置、仿真、QoS 调优——其实是一条完整的链路。配置解决的是通不通仿真解决的是对不对QoS 调优解决的是快不快、稳不稳。很多人第一次用 Versal配置完 NoC 就急着上板结果发现带宽上不去、延迟抖动大回头再查发现是 QoS 的仲裁策略没配对。所以我把这三步串起来讲尽量把每一步背后的逻辑说清楚。这篇文章适合谁看如果你已经用过 Vivado 做 Zynq 或者纯 FPGA 的 AXI 互联现在要迁移到 Versal 平台那这篇基本就是你要的。如果你是完全的新手建议先把 AXI4 协议的基本握手搞清楚再来看 NoC不然会有点懵。我下面会尽量用生活化的类比来解释但该有的参数和步骤一个都不会少。先说清楚一个概念Versal 里的 NoC 不是一个 NoC而是分成了NoC MasterNMU和NoC SlaveNSU两端中间通过 NoC 的交换网络连接。NMU 是发起方比如 PL 里的一个 AXI Master、PS 里的一个端口NSU 是接收方比如 DDR 控制器、PL 里的 AXI Slave。你在 Vivado 里配置的本质上就是给每个 NMU 到 NSU 的路径分配资源。2. 内容整体设计与思路拆解2.1 先想清楚数据流再动手配 NoC我见过太多人一上来就打开 Vivado 的 NoC 配置界面对着那一堆 NMU/NSU 端口发呆。正确的顺序应该是先在纸上画出数据流。谁产生数据、谁消费数据、数据量多大、实时性要求多高这四个问题回答清楚了NoC 的配置方案基本就出来了。举个具体的例子。假设你要做一个视频处理链路PL 里的图像采集 IP 往 DDR 写原始帧PS 里的处理器读出来做算法处理处理完再写回 DDR最后 PL 里的显示 IP 从 DDR 读出来送显示器。这条链路里有四个数据流采集 IP → DDR写大带宽可以容忍一定延迟PS → DDR读中等带宽延迟敏感PS → DDR写中等带宽显示 IP ← DDR读大带宽延迟极其敏感晚了就撕裂你看同样是访问 DDR不同数据流的诉求完全不一样。显示这条如果延迟抖动大画面就会出问题采集这条只要平均带宽够偶尔慢一点无所谓。这就是为什么 QoS 调优必须建立在想清楚数据流的基础上——你连谁重要都没搞清楚怎么分配优先级2.2 方案选型NoC 直连还是走 AXI InterconnectVersal 里有个容易踩的坑不是所有互联都必须走 NoC。PL 内部两个 IP 之间如果只是小数据量通信直接用 AXI Interconnect 或者 AXI SmartConnect 反而更简单、延迟更低。NoC 的价值在于跨 die 区域、跨时钟域、连接硬核资源这些场景。我的经验判断标准是这样的场景推荐方案理由PL 内部 IP 间小数据量AXI SmartConnect延迟低配置简单PL 访问 DDRAXI NoC必须走 NoC 才能到 DDR 控制器PS 访问 PL 里的 IPAXI NoC 或 PL 内部互联看数据量和延迟要求多 Master 共享一个 SlaveAXI NoC QoSNoC 的仲裁比 Interconnect 更精细跨 SLR 的大带宽传输AXI NoCNoC 有专门的跨 SLR 路径优化这个表不是绝对的但大方向不会错。我个人的习惯是只要涉及 DDR 或者硬核一律走 NoC纯 PL 内部能不走就不走。因为 NoC 虽然强大但配置复杂度摆在那里能省则省。2.3 带宽预算别等上板了才发现不够NoC 配置里最核心的参数就是带宽分配。Versal 的 NoC 总带宽是有限的所有 NMU 到 NSU 的路径共享这个总带宽。如果你给每条路径都标一个很大的带宽值最后 Vivado 会告诉你资源不够或者自动帮你压缩压缩完的结果可能就不是你想要的。我的做法是先算总账。假设 DDR 控制器的理论带宽是 19.2 GB/s这个数字取决于你的 DDR 配置具体看 DDR4 的位宽和频率那么所有访问 DDR 的路径加起来不能超过这个数实际还要留 20% 左右的余量给协议开销和刷新操作。然后按优先级分配显示读需要 4 GB/s给 5 GB/s 预算采集写需要 3 GB/s给 4 GB/s 预算PS 读写各需要 1 GB/s各给 1.5 GB/s 预算加起来 12 GB/s离 19.2 GB/s 还有余量这样配置起来就比较从容。如果你算下来发现超了那就得回头优化数据流比如降低分辨率、减少拷贝次数而不是硬着头皮配。提示Vivado 的 NoC 配置界面里有个 Bandwidth 列单位是 MB/s填的时候注意单位换算。我见过有人把 4000 MB/s 填成 4000 GB/s然后纳闷为什么工具不报错——其实工具会默默帮你截断但结果肯定不对。3. 核心细节解析与实操要点3.1 NoC 配置界面的关键参数逐个拆解打开 Vivado 的 NoC 配置你会看到一个类似矩阵的界面行是 NMU列是 NSU交叉点就是一条可能的路径。每条路径上你能配的参数主要有这几个Bandwidth带宽这条路径期望的带宽单位 MB/s。这个值不是保证值而是期望值NoC 会根据所有路径的期望值和实际可用带宽做仲裁。填的时候要基于你的数据流计算不要拍脑袋。QoS Class服务质量等级这个参数决定了当多条路径竞争同一个 NSU 时谁优先。Versal 的 NoC 支持多个 QoS 等级数值越小优先级越高。我一般把实时性要求最高的路径比如显示设成最高优先级把批量传输比如大块数据搬运设成低优先级。Traffic Class流量类别这个跟 QoS Class 配合使用主要影响 NoC 内部的缓冲和调度策略。对于延迟敏感的流量选低延迟类别对于带宽敏感但延迟不敏感的选高吞吐类别。Read/Write 方向每条路径可以单独配读和写的带宽。如果你的数据流是单向的比如采集只写、显示只读那就只配一个方向另一个方向填 0这样能省出带宽给别的路径。AxPROT / AxCACHE这些是 AXI 协议本身的属性NoC 会根据这些属性做一些优化。比如 AxCACHE 标记为 bufferable 的写操作NoC 可以做写合并提高效率。这部分一般保持默认就行除非你有特殊需求。3.2 时钟域与复位NoC 最容易翻车的地方NoC 的时钟域配置是个大坑。Versal 的 NoC 有自己的时钟输入通常是noc_clk这个时钟的频率决定了 NoC 的工作频率。同时每个 NMU 和 NSU 又各自挂在自己的时钟域上NoC 内部要做时钟域转换。我踩过的坑是这样的PL 端的 AXI Master 跑在 250 MHzNoC 跑在 500 MHzDDR 控制器跑在 400 MHz。配置的时候没注意结果上板后发现数据偶尔出错。查了半天才发现是 NoC 的时钟约束没写对导致时序不满足偶尔采样错位。正确的做法是在 Vivado 的 NoC 配置里明确指定 NoC 的参考时钟频率。在 XDC 约束文件里给 NoC 相关的时钟加上正确的create_clock和set_clock_groups。如果 NoC 时钟和 PL 时钟是异步的一定要加set_clock_groups -asynchronous否则工具会按同步时钟去分析时序报告会很难看。复位方面NoC 的复位信号noc_rst_n必须和 NoC 时钟同步释放。我一般用一个复位同步器把外部复位同步到 NoC 时钟域再送进去。这个细节看起来小但不做的话上板后 NoC 可能起不来或者起来后行为异常。3.3 仿真环境搭建别跳过这一步很多人觉得 NoC 是硬件自动调度的仿真没必要。这个想法很危险。NoC 的仿真能帮你验证三件事地址映射对不对、数据通不通、QoS 策略有没有生效。尤其是 QoS你在配置界面里设了优先级但实际仲裁行为是不是符合预期只有仿真能告诉你。Versal 的 NoC 仿真需要用到Vivado Simulator或者VCS之类的工具加上 Versal 的仿真模型。具体步骤我后面会详细讲。这里先说一个关键点NoC 的仿真模型是加密的你需要正确的 license 才能跑。如果你用的是 Vivado Lab Edition可能跑不了完整仿真得用完整版。另外仿真的时候建议把 NoC 的performance monitor打开这样你能看到每条路径的实际带宽和延迟。这个数据比你在配置界面里填的期望值有价值得多因为它反映的是真实仲裁后的结果。4. 实操过程与核心环节实现4.1 从零搭建一个带 NoC 的 Versal 工程我以一个具体的例子来走一遍完整流程。假设我们要做一个简单的系统PL 里有一个 AXI Master模拟数据源通过 NoC 往 DDR 写数据PS 通过 NoC 从 DDR 读数据。目标是验证 NoC 通路和 QoS 配置。第一步创建工程并选对器件在 Vivado 里新建工程器件选 Versal 系列具体型号根据你手头的板子来。我用的是一块 VCK190 的评估板器件是xcvc1902-vsva2197-2MP-e-S。选器件的时候注意不同型号的 NoC 资源不一样NMU/NSU 的数量和 DDR 控制器的数量都不同。第二步添加 NoC IP 并配置在 Block Design 里添加AXI NoCIP。Vivado 会自动检测你的器件支持多少个 NMU 和 NSU。然后打开 NoC 的配置界面开始连线。这里有个技巧先用 Auto Assign 让工具帮你连一版然后手动改。工具的自动分配通常能保证连通性但 QoS 和带宽肯定不是你想要的。在自动分配的基础上改比从零开始连快得多。第三步配置 NMU 和 NSU对于 PL 端的 AXI Master你需要把它映射到一个 NMU 上。在 NoC 配置界面里找到对应的 NMU设置它的 AXI 属性数据位宽、ID 位宽等。对于 DDR找到对应的 NSU设置它的地址范围。地址映射是个容易出错的地方。Versal 的地址空间是统一的NoC 的 NSU 会占用一段地址。你需要在 Address Editor 里确认每个 Master 看到的地址范围是正确的。我一般会画一张地址映射表把每个 Master、每个 Slave 的起始地址和大小都列出来配置的时候对着表填。第四步连接时钟和复位NoC 的时钟输入通常来自一个 MMCM 或者 PLL。我一般用一个单独的 MMCM 给 NoC 供时钟频率根据你的带宽需求来定。比如你要跑 19.2 GB/s 的 DDR 带宽NoC 的工作频率至少要能支撑这个数据率。复位方面用一个proc_sys_reset或者自己写的复位同步器把外部复位同步到 NoC 时钟域。第五步生成输出产物配置完成后Validate Design然后 Generate Output Products。这一步会生成 NoC 的网表文件和约束文件。注意看有没有 critical warning尤其是关于时钟和地址的。4.2 仿真验证写一个能跑的 testbenchNoC 的仿真 testbench 跟普通 AXI 仿真差不多但有几个特殊的地方。首先你需要例化 NoC 的仿真模型。Vivado 在生成输出产物的时候会同时生成仿真用的模型文件。你需要在 testbench 里把这个模型例化进去然后把 AXI 接口连上。其次NoC 的仿真需要一些初始化时间。NoC 内部有配置寄存器上电后需要一段时间来初始化。我一般会在 testbench 里等 1000 个时钟周期再开始发激励避免在 NoC 还没准备好的时候就发数据。下面是一个简化的 testbench 结构module tb_noc; reg noc_clk; reg noc_rst_n; // 时钟生成 initial begin noc_clk 0; forever #2.5 noc_clk ~noc_clk; // 200MHz end // 复位 initial begin noc_rst_n 0; #1000; noc_rst_n 1; end // 例化 NoC 模型 noc_wrapper u_noc ( .noc_clk(noc_clk), .noc_rst_n(noc_rst_n), // ... AXI 接口 ); // 激励 initial begin // 等待 NoC 初始化 #5000; // 发起 AXI 写事务 axi_write(32h0000_0000, 32hDEADBEEF); // 发起 AXI 读事务 axi_read(32h0000_0000); #10000; $finish; end endmodule这个结构是最基础的。实际项目中你可能需要写更复杂的激励比如多个 Master 同时访问、不同 QoS 等级的流量混合等。仿真跑起来后重点看几个东西波形看 AXI 握手信号是否正常有没有卡死。NoC performance monitor 的输出看实际带宽和延迟。有没有协议违例Vivado 的仿真模型会报 AXI 协议错误如果有一定要查。4.3 QoS 调优从能跑到跑得好QoS 调优是 NoC 配置里最考验经验的部分。我的调优流程一般是这样的第一步建立基线先按默认配置跑一遍仿真记录每条路径的带宽和延迟。这个基线数据是你后续调优的参照。第二步识别瓶颈看哪条路径的实际带宽远低于期望值或者延迟远高于预期。这通常意味着这条路径在跟别的路径竞争时吃亏了。第三步调整 QoS 等级把瓶颈路径的 QoS 等级调高把不重要的路径调低。然后重新仿真看瓶颈有没有缓解。第四步迭代QoS 调优是个迭代过程调一次不一定到位。我一般会调 3 到 5 轮直到所有路径的指标都满足要求。这里有个经验不要把所有路径都设成最高优先级。如果所有路径都是最高优先级那等于没有优先级仲裁器会退化成轮询反而失去了 QoS 的意义。正确的做法是分层实时性最高的设成最高次高的设成中等批量传输设成最低。另外Versal 的 NoC 支持Bandwidth Limiting你可以给某条路径设一个带宽上限防止它抢占过多资源。这个功能在调试的时候特别有用比如你怀疑某条路径在抢带宽可以给它设个上限看其他路径的表现有没有改善。5. 常见问题与排查技巧实录5.1 NoC 配置报错速查表报错信息可能原因解决方法NoC bandwidth oversubscribed总带宽超了降低某些路径的带宽期望值NMU not connected有 NMU 没连到 NSU检查连线确保每个用到的 NMU 都有路径Address overlap地址范围重叠检查 Address Editor调整地址映射Clock not specifiedNoC 时钟没约束在 XDC 里加 create_clockQoS class conflictQoS 等级冲突检查是否有多个路径设了相同的最高优先级Simulation model not found仿真模型没生成重新 Generate Output Products确保选了仿真选项这个表是我自己踩坑总结的不一定全但覆盖了大部分常见问题。5.2 上板后 NoC 不工作的排查思路仿真过了不代表上板就能跑。上板后如果 NoC 不工作我一般按这个顺序排查先看时钟。用 ILA 或者示波器看 NoC 的时钟有没有起来频率对不对。NoC 的时钟如果没起来整个 NoC 就是死的。再看复位。NoC 的复位释放时机对不对是不是在时钟稳定之后才释放的。如果复位释放太早NoC 可能初始化失败。然后看 AXI 握手。用 ILA 抓 AXI 接口的ARVALID、ARREADY、RVALID、RREADY这些信号看有没有握手成功。如果ARVALID一直高但ARREADY一直低说明 NSU 没准备好可能是地址映射错了。最后看 NoC 的配置寄存器。Versal 的 NoC 有一些状态寄存器可以通过 PS 读出来看 NoC 当前的状态。具体寄存器地址参考 Versal 的 TRMTechnical Reference Manual。5.3 几个我踩过的坑坑一NoC 的带宽单位是 MB/s不是 Mb/s。我一开始按 Mb/s 算结果配出来的带宽只有预期的八分之一。后来才发现单位是字节不是比特。这个坑很隐蔽因为 Vivado 的界面里没写清楚单位。坑二NoC 的 QoS 等级是数值越小优先级越高。我一开始以为数值越大优先级越高结果把最重要的路径设成了最低优先级调了半天没效果。后来查文档才发现搞反了。坑三NoC 的仿真模型需要 license。我在一台只装了 Vivado Lab Edition 的机器上跑仿真一直报错说找不到模型。后来换了完整版就好了。如果你也遇到类似问题先确认 license 是否支持 Versal 仿真。坑四NoC 的时钟约束不能少。我一开始没写 NoC 的时钟约束综合能过但实现的时候时序报告一塌糊涂。后来补上create_clock和set_clock_groups才正常。坑五NoC 的地址映射要跟 PS 的地址空间对齐。如果 PS 要访问 PL 里的 IPPL 的 IP 地址必须在 PS 的地址空间里可见。这个需要在 Address Editor 里配置不然 PS 访问会报错。5.4 性能调优的独家技巧除了前面说的 QoS 调优还有几个技巧能帮你把 NoC 的性能榨干技巧一用 AXI ID 做乱序传输。AXI4 支持乱序传输不同的 ID 可以乱序返回。NoC 内部会根据 ID 做重排序。如果你给不同的数据流分配不同的 IDNoC 可以并行处理提高吞吐量。技巧二合理设置 Outstanding 事务数。NoC 支持多个 outstanding 事务但数量不是越多越好。太多会占用 NoC 内部的缓冲资源反而影响其他路径。我一般根据路径的延迟和带宽来算outstanding bandwidth × latency / transaction_size。技巧三利用 NoC 的 Performance Monitor。Versal 的 NoC 有内置的性能监控器可以实时统计每条路径的带宽、延迟、事务数。这个数据在上板调试的时候特别有用能帮你快速定位瓶颈。技巧四写合并。对于写操作如果地址连续NoC 可以做写合并把多个小写合并成一个大写提高效率。要启用这个功能需要在 AXI 属性里设置AxCACHE的 bufferable 位。技巧五读预取。对于读操作如果 NoC 检测到连续的读地址可以预取后面的数据。这个功能默认是开的但如果你的读模式是随机的预取反而会浪费带宽这时候可以关掉。6. 从配置到调优的完整经验总结6.1 一个可复用的 NoC 配置模板经过几个项目的积累我总结了一个 NoC 配置的模板基本上新项目直接套用改改参数就行。第一步列出所有 Master 和 Slave。把 PL 和 PS 里所有需要走 NoC 的 AXI 接口都列出来标注每个接口的数据位宽、时钟频率、ID 位宽。第二步画数据流图。用箭头标出每个 Master 到每个 Slave 的数据流标注方向读/写和期望带宽。第三步分配 NMU 和 NSU。根据数据流图把 Master 映射到 NMUSlave 映射到 NSU。注意有些硬核比如 DDR的 NSU 是固定的不能随便改。第四步配置带宽和 QoS。按优先级分配带宽实时性高的给高 QoS批量传输给低 QoS。第五步约束时钟和复位。给 NoC 加时钟约束确保复位同步。第六步仿真验证。跑仿真看带宽和延迟是否满足要求。第七步上板调试。用 ILA 和 Performance Monitor 看实际表现必要时微调 QoS。这个模板看起来简单但每一步都有细节。我建议你第一次做的时候把每一步的输出都记录下来形成文档。下次再做类似项目直接翻文档就行。6.2 不同应用场景的 NoC 配置差异不同的应用场景NoC 的配置重点不一样。我举几个例子视频处理重点是带宽和延迟抖动。显示路径的 QoS 要设到最高带宽要留足余量。采集路径可以设中等 QoS因为偶尔丢一帧影响不大。建议开启写合并和读预取。神经网络推理重点是带宽利用率。权重和特征图的数据量很大NoC 的带宽要尽量给足。QoS 方面权重加载可以设低优先级因为是一次性的特征图的读写要设高优先级因为影响推理速度。工业控制重点是延迟确定性。控制环路的延迟必须稳定不能有大的抖动。QoS 要设到最高带宽不用很大但要保证。建议关闭写合并和读预取因为这些功能会增加延迟的不确定性。数据采集重点是持续带宽。采集数据是连续的NoC 要能持续提供带宽。QoS 设中等即可但带宽要留足。建议开启写合并提高写效率。6.3 工具版本和 license 的注意事项Versal 的 NoC 功能在不同版本的 Vivado 里有些差异。我用过 2021.2、2022.2 和 2023.1感觉 2022.2 之后的版本对 NoC 的支持比较成熟。如果你用的是比较老的版本可能会遇到一些奇怪的 bug。License 方面Versal 的 NoC 配置需要Vivado Enterprise或者Vivado System Edition的 license。Lab Edition 只能看不能配。仿真方面NoC 的仿真模型需要额外的 license具体看你的 license 文件里有没有包含。另外Versal 的 NoC 配置工具在 Vivado 里叫Versal ACAP NoC跟普通的 AXI Interconnect 是分开的。别找错了。6.4 后续可以扩展的方向这个项目做完了其实还有很多可以深挖的地方。比如多 NoC 域Versal 支持多个 NoC 域不同域之间可以隔离。如果你的系统有安全隔离需求可以用这个功能。动态 QoSNoC 的 QoS 等级可以在运行时通过 PS 修改。如果你的系统有不同的工作模式可以动态调整 QoS。NoC 与 AI Engine 的协同Versal 的 AI Engine 也可以通过 NoC 访问 DDR。如果你在做 AI 推理可以研究一下 AI Engine 和 PL 如何共享 NoC 带宽。NoC 的性能建模如果你要在系统设计早期评估 NoC 的性能可以用 Vivado 的性能建模工具不用等到 RTL 写完。这些方向我后续会陆续展开有兴趣的可以关注。最后分享一个我个人的习惯每次配完 NoC我都会把配置导出成 Tcl 脚本存到版本控制里。这样下次做类似项目直接跑脚本就行不用从头配。而且出了问题也能快速回滚到之前的版本。这个习惯帮我省了很多时间推荐你也试试。