推理SoC设计实战:NPU算力、存储带宽与互联架构的关键决策
2026/9/7 3:20:00 网站建设 项目流程

做推理 SoC 这几年,项目立项时最容易收到的 PPT 都是一句话:我们要做一颗 NPU 很强的芯片。但真正走到回片调试,你才会发现,一颗 SoC 到底行不行,往往不取决于它的 TOPS 标了多少,而是 NPU、存储和互联这三件事有没有在架构阶段就算清楚。我前后经手过几颗从 NPU 规格定义到回片点亮全流程的芯片,踩过的坑不少,也沉淀了一些可以复用的设计思路。这篇文章不打算复述教材里的体系结构通识,而是按一颗高性能推理 AI SoC 的真实设计推进顺序,拆解 NPU、存储层级、片内和片间互联这三个核心子系统:每一步为什么这么定,关键参数怎么算出来,以及哪些地方是流片后才发现、几乎回不了头的环节。

如果你正在规划芯片项目、做 NPU 相关的 IP 评估,或者单纯想搞明白“推理芯片的算力瓶颈到底在哪”,这篇文章应该能给你一条比 spec 文档清晰得多的主线。

1. 需求拆解与整体架构思路

设计一颗推理 SoC 的第一步,不是选核、不是堆算力,而是把目标场景的模型需求翻译成硬件可执行的指标。很多团队上来就定“我要 100 TOPS”,但 100 TOPS 的 NPU 配一个 30GB/s 的 DDR 控制器,跑大模型照样喘不上气。这就是需求和架构没对齐的典型表现。

1.1 推理场景为什么需要专用芯片

通用 CPU 和 GPU 做推理不是不行,只是越往后越难受。模型的算子组合越来越固定,访存行为却越来越沉重,通用计算单元大量时间在等数据,算力峰值连一成都用不满。CPU 侧的问题是并行度不够,GPU 侧的问题是能效比和系统复杂度不划算。推理 SoC 对 NPU 的核心要求,本质上是把一组特定模型的执行效率压到极致,把功耗和时延压进可用区间。

从产品角度说,端侧需要低功耗、小面积、确定的时延;边缘侧和数据中心边缘侧需要更高吞吐和更可控的能效比。一颗好的推理 SoC,不会去和训练 GPU 拼浮点峰值,而是在“特定算子集、特定 batch 下的单位功耗推理次数”这个维度上做文章。这也决定了架构选型时会比较激进——比如为了跑 Transformer 系模型,直接把 attention 的 fused 算子做成硬件原语,而不是依赖软件去拼。

推理负载还有个特点:算子和执行流相对固定,不需要通用可编程性做得特别强。所以 NPU 架构设计的自由度比 CPU/GPU 大得多,硬件可以针对固定数据流模式深度定制。前几年各家推理芯片架构百花齐放,根源也在这里——没有历史包袱,没有通用生态要兼容,怎么高效怎么来。

1.2 从目标模型反推硬件指标

架构设计真正的起点,是把“延时 20ms”“功耗 5W”这类目标换算成算力、带宽、容量三个硬指标。这里用一个 7B 参数的大模型端侧推理场景做个示例推演。

先看算力。7B 模型生成一个 token,大约需要 2 × 7B = 14 GFLOPs 的计算量。如果目标是一个 token 20ms,那需要的算力就是 14 GFLOPs / 0.02s ≈ 700 GFLOPs = 0.7 TOPS。这个数字放在今天看很低,随便一颗 NPU 都能超过。

再看带宽。假设权重用 INT4 量化,7B 参数就是 3.5GB。每生成一个 token,理论上要把全部权重从 DRAM 过一遍。20ms 内搬完 3.5GB,带宽需要 175GB/s。这个数字就比较吓人了,普通 LPDDR5 也就 50~70GB/s 水平。算力只要 0.7 TOPS,带宽却要求 175GB/s,这中间差了至少 3 倍——这就是为什么存储设计在推理 SoC 里比算力更关键。

所以真正落地时,不可能每个 token 都把全部权重从外部 DRAM 读一遍。必须引入权重驻留、KV cache 复用、多 batch 共享权重等优化手段。这也直接决定了片内 SRAM 容量、NPU 数据流和外部存储带宽的分配方向。架构师在这个阶段要反复做模型花销分析:跑一遍目标模型的完整访存轨迹,统计权重读取量、激活读写量、中间张量溢出量,才能倒推出合理的存储层级配置。

这套方法论我建议每个项目都认真做。芯片不是软件,流片后发现带宽不够,不是改一行配置能解决的,那是几百万美金的沉没成本。

2. NPU 计算单元:算力从哪来

NPU 是最容易让人兴奋的部分,也是设计里看得最清楚的一部分。它本质是一个超大规模的乘累加阵列,外加精心设计的片上缓存和数据调度机制。这里把最关键的设计决策逐一拆开。

2.1 MAC 阵列规模怎么定

NPU 的顶峰算力公式很直接:TOPS = MAC 数量 × 时钟频率 × 2。乘一次加一次算两次计算,所以要乘 2。反过来,从目标算力可以推出 MAC 总量。

假设目标 100 TOPS @ INT8,频率目标 1GHz,那MAC 数 = 100 × 10^12 / (2 × 10^9) = 50,000。也就是需要 5 万个乘累加器。听起来不多,但每个 MAC 背后要配寄存器、数据选择逻辑和布线资源,实际面积放大一个数量级不止。如果按每组 16×16 的 MAC 子阵列来组织,大概需要 196 组左右。

频率的选择比 MAC 数量更敏感。频率往上走,时序收敛难度指数上升,电压也得跟着抬,功耗密度暴涨。所以在先进工艺下,宁可把阵列做大一点、跑低频,也不要追高频。低频大阵列的功耗效率和面积效率通常都更好,这也是 NPU 和 CPU 设计理念差异比较大的地方。CPU 追单核频率是因为串行逻辑绕不开,NPU 的计算天然并行,堆并行度成本更低。

2.2 数据流策略与片上 Buffer 分配

MAC 阵列本身不产生算力,只有数据喂得进去,算力才兑现。这就牵扯到数据流策略。学术界把数据流分成三类:权重驻留(weight stationary)、输出驻留(output stationary)、输入驻留(input stationary)。字面意思就是尽量把哪类数据留在片上反复使用。

推理场景里,权重是固定的,复用次数最高,所以权重驻留通常优先级最高。具体做法是把一个 tile 的权重加载到片上 SRAM,然后把这个 tile 对应的所有输入和输出都算完,再换下一组权重。这样权重从外部 DRAM 读取的频率大大降低。但 Transformer 模型又不太一样——attention 计算里激活复用的价值也很大,QK^T 的结果要被 softmax 之后继续用,中间张量很占空间。所以成熟设计不会只支持一种数据流,而是让编译器根据算子在 weight stationary 和 output stationary 之间切换。

片上 buffer 的容量分配也很有讲究。假设总片上存储 8MB,我习惯的分法是:权重 4MB、激活 2MB、输出/临时 2MB。权重给最多,因为它承担的是省 DRAM 带宽的主要任务;输出累积尽量留在片上,避免频繁写回外部存储,因为写回同样消耗带宽。这个比例不是固定的,如果你的模型里激活特别大,可能要把激活比例调上去,但“权重优先”这个原则基本不变。

2.3 精度支持与量化取舍

推理芯片默认支持 INT8,这是过去几年的事实标准。但最近越来越多的模型开始用 INT4 做 weight-only 量化,FP8 在推理里也开始出现。设计上要早做决定,因为这直接影响 MAC 阵列的硬件结构。

INT8 MAC 和 FP16 MAC 的面积差很多。一个 FP16 乘法器的面积大概是 INT8 的 2~3 倍,而且功耗也高不少。如果目标场景根本不需要 FP16 精度,硬塞进去就是浪费。我在实际项目里的取舍是:主力用 INT8 MAC 阵列,配少量 FP16 向量单元处理特殊算子,INT4 通过软件量化工具链转成两个 INT8 运算的组合或专门的小位宽单元。FP8 的话,E4M3 和 E5M2 两种格式各有优劣,主要看模型动态范围需求,硬件上需要额外的指数处理逻辑,设计复杂度明显上升,不是刚需就别轻易上。

低精度带来的收益不仅是算力翻倍,更重要的是带宽减半。同样一个 7B 模型,INT4 比 INT8 少读一半权重,存储带宽压力降一个级别。所以现在很多推理 SoC 走 INT4 路线,本质是在缓解存储墙,而不是单纯拼 TOPS。

2.4 指令调度与多级并行

有了阵列和 buffer,还得解决“怎么让它们一直忙”的问题。NPU 通常用粗粒度指令控制:一条指令完成一个 tile 的计算,DMA 负责数据搬运,两者通过硬件同步机制协作。最核心的技巧是乒乓缓冲(ping-pong buffer)——MAC 阵列计算当前 tile 的时候,DMA 已经在搬运下一个 tile 的数据了。这样计算和搬运完全重叠,外部存储延迟被掩盖掉。

多核 NPU 里还要考虑多核调度。如果 16 个 NPU 核同时跑,数据搬运的协调就复杂了。常见做法是主核负责任务切分,把不同的 tile 分配给不同从核,每个核有自己的指令队列和本地 buffer。核间通信靠共享存储和事件同步,尽量减少锁和原子操作,因为锁竞争在高并行度下很容易成为新的瓶颈。我见过不少项目,单核效率很高,一上多核就在同步机制上翻车,利用率掉到 50% 以下。

3. 存储层级:带宽和容量怎么匹配

存储设计是推理 SoC 里最容易被低估的部分。很多团队把精力全花在 NPU 算力上,存储随便挂个 LPDDR 控制器就完事,结果流片后跑模型才发现,算力有 80% 在等数据。存储墙这东西,教科书上讲了几十年,落到自己芯片上感受才真切。

3.1 存储墙到底卡在哪

算力提升的速度远快于 DRAM 带宽提升的速度,这是存储墙的本质。一颗 DNN 芯片的单位成本里,DRAM 访存消耗的能量比片内计算高一两个数量级。推理模型越大,权重读取量越大,这个差距越明显。

举一个模型示例:一个 7B 模型,INT8 权重 7GB,如果完全驻留在外部 DRAM,就算带宽 100GB/s,把所有权重过一遍也要 70ms,光这个就远超通常的推理时延预算。所以存储墙不是说换个快一点的 DRAM 就能解决,而是要在架构层面做精细的层级设计:热点数据尽量留片上,不可留片上的数据结构化地搬到外部,让外部 DRAM 只承担它最该承担的部分。

3.2 多级存储的容量与带宽设计原则

推理 SoC 的存储层级一般是这样:寄存器/累加器 → L1 scratchpad(局部共享 SRAM) → L2 共享 SRAM → 外部 DRAM。每一层容量递增、带宽递减,接口带宽近似满足“上一层是下一层的 1/N”的经验法则。

具体到一个中等规模推理芯片,L1 可以配 512KB × 4 组,L2 配 8MB,外部接 LPDDR5 或 DDR5。L1 带宽是 MAC 阵列直接消费的,必须做到每周期能喂满计算单元;L2 负责多核共享和中间结果暂存;外部 DRAM 只承担冷数据的读取和最终结果写回。层与层之间的数据搬运由 DMA 引擎负责,搬运粒度对齐 cache line 或 DRAM page,这样可以最大化 burst 效率。

容量到底够不够,判断标准是“权重驻留率”。假设模型某一层权重 2MB,L2 有 4MB 可用,那至少能装两层,驻留率高,外部带宽压力就小。如果模型一层权重就 8MB,L2 只有 4MB,那只能拆 tile 跑,外部带宽马上变成瓶颈。所以 L2 容量和模型权重尺寸的匹配关系,要在架构定义阶段就用真实模型跑一遍。

3.3 外部存储方案选型:HBM、LPDDR、DDR 怎么选

外部存储选型决定了芯片的成本、功耗和可达到的带宽上限。常见的几个选择各有适用场景,我整理了一个对比:

存储方案单颗带宽典型容量成本/功耗适合场景
LPDDR5/5X50~100GB/s8~32GB中等端侧、边缘盒、低功耗设备
DDR550~100GB/s16~128GB中低通用边缘计算、服务器边缘
GDDR6200~500GB/s8~32GB中高高吞吐推理、边缘加速卡
HBM2E/HBM3500~1000GB/s+8~64GB很高数据中心推理、大模型加速

带宽和成本成正比,这是硬道理。HBM 贵在 interposer 和 TSV 工艺,但确实能解决很多痛苦——模型权重驻留在 HBM 里,带宽不再是瓶颈,设计重心可以从“省带宽”转向“提高计算利用率”。但手端设备根本放不下 HBM,LPDDR 是唯一合理选择。

我的建议是:不要把选择留给后端。架构初期就用目标模型的访存轨迹做带宽预演,如果外部带宽需求超过单颗 LPDDR 的 70%,赶紧考虑双通道或多个 LPDDR 颗粒,不要指望优化能省下来。带宽这东西,省一点容易,省一多半基本不可能,除非你的模型结构特别规整。

3.4 数据布局与内存分配策略

硬件确定了,软件侧的内存分配同样关键。模型权重加载到 DRAM 时,布局要对齐、通道交织、Tile 化,这些听起来偏软件,但直接决定 DMA 搬运效率。

DMA 搬运的最小单元通常是 64B 或 128B,如果你把数据排布成跨 page 的小块,每次搬运可能触发页冲突和 bank 冲突,实际带宽掉 30% 以上。正确的做法是把模型按层按通道切成大块连续内存,确保每次 DMA burst 能拿到足够长的连续数据。权重、激活、输出分别划在独立内存区域,避免互相干扰。编译器在生成指令时就要完成内存规划,这个规划的质量会直接影响最终的端到端推理时延,而且差得不是一星半点。

4. 互联设计:让数据和指令流动起来

互联设计在推理 SoC 里经常被一笔带过,但其实是回片后最容易出鬼问题的部分。一个 NPU 核的内部可以做到很干净,一堆核连起来、再连到 CPU、连到外部接口,问题就开始冒出来了。

4.1 片内互联拓扑:总线、Crossbar、Mesh

小规模 SoC 用 AXI 总线就行,简单可靠,工具链成熟。但到了 16 核 NPU 以上,总线带宽不够,仲裁也麻烦,必须上 NoC(片上网络)。

拓扑选择上有几条路。Crossbar 延迟低,任意两个节点直达,但面积和布线随端口数平方增长,频率也做不高。Mesh 是面积效率最高的方案,多跳延迟可以通过优化路由来缓解,但 QoS 设计复杂。还有 Ring 拓扑,带宽和面积介于两者之间,适合中小规模。

以一个 16 核 NPU 为例,我做过 4×4 mesh 的方案。每跳的数据位宽是 128bit @ 1GHz,单跳理论带宽 16GB/s,但这只是理想值。实际要考虑路由计算、仲裁冲突、流控反压,有效带宽能到 60%~70% 就算不错。多核同时访问共享 L2 时,mesh 的拥塞点往往在 L2 控制器的输入端口,这里要配足够深的缓冲和合理的仲裁策略。

4.2 一致性是个陷阱

这里想认真提醒一下:推理 SoC 的存储一致性设计,不比计算单元简单,而且很容易被低估。CPU 和 NPU 之间到底要不要做 cache 一致,是整个互联架构最容易走弯路的地方。

推理链路里,CPU 负责调度和任务管理,真正跑数据流的是 NPU 的 DMA 和计算阵列。NPU 的数据流动是从 DRAM → DMA → 片上 SRAM,算完再 DMA 写回,完全没有软件 cache 参与。这种情况下,强行做完整的硬件 cache 一致性方案,比如用 CCIX 或 AMBA CHI,代价非常大——大量的 snoop 开销、目录开销、验证复杂度,换来的收益却很小,因为 NPU 根本不需要在 CPU cache 里找数据。

更合适的选择是:让 NPU 走非一致路径,直接物理地址访问 DRAM,CPU 只维护描述符和同步信号。同步机制用门铃中断和事件信号,比如 CPU 写好一组描述符后,发起一次 Doorbell,NPU 收到后开始搬数据,算完再发一个中断通知 CPU 读结果。这个方案省掉了大量一致性协议的开销,设计和验证都轻松很多。前提是描述符和结果缓冲所在的 cache 区域要做好显式 flush,不然真会出现 CPU 改了描述符内存,NPU 拿到的还是旧值的问题。这个坑我后面还会细说。

4.3 外部互联:PCIe、CXL 和多卡组网

推理芯片很少孤立存在,要么挂到服务器主机上,要么组多卡集群。外部互联的带宽和时延特性,直接决定系统能跑多大的模型、多快的同步。

PCIe Gen5 x16 理论带宽 64GB/s,但这是双向总和。实际做 DMA 读时,有效带宽取决于包大小和延迟。如果数据包只有几百字节,带宽利用率会掉得很惨;大块传输才能逼近理论值。所以和主机通信时,DMA 描述符的聚合设计很重要,尽量攒成大块再发。

CXL 是在 PCIe 物理层之上扩展出的缓存和内存语义协议,用来做内存池化、设备间共享内存很合适。对推理 SoC 来说,多颗芯片通过 CXL 共享一个内存池,是缓解单颗芯片容量不足的一种方案。但要注意,CXL 的带宽上限受 PCIe 物理层约束,别指望它是免费午餐。多卡推理集群里,更常见的还是 PCIe Switch 加 RDMA over Ethernet 的组合:先通过 PCIe 做主机内部通信,跨节点走以太网和 RDMA。同步梯度或分布式推理中间结果时,RDMA 的单边读写能显著降低 CPU 开销,这个对扩展性非常关键。

4.4 多 Die 互联与 Chiplet 方案

单颗 Die 做到一定程度,良率和成本会非常难看:NPU 计算密度太高,一块 die 塞不下足够多的 SRAM 和 IO。越来越多的推理 SoC 走 chiplet 路线:计算 die、存储 die、IO die 分开做,再用 Die-to-Die 接口互联。

Die-to-Die 接口有两条路线:UCIe 和自定义 SerDes。UCIe 是现在最被看好的标准化方案,优势是成熟的物理层定义、低功耗、跨工艺可复用,适合做可扩展的 chiplet 生态。自定义 SerDes 可以做到更高带宽或更低的片内功耗,但验证成本高,而且一旦以后想换供应商的 die,接口规格不统一就麻烦。我的个人倾向是:只要不是极端性能需求,优先选 UCIe,把精力留给真正出差异化的 NPU 架构和存储方案。

Chiplet 方案里还有一个容易忽略的问题:Die 间互联的功耗和热。Die-to-Die SerDes 每比特的能耗虽然比片内互连高,但比走 PCB 的外部接口低一两个数量级,这本身就是收益。不过多个 die 堆叠或并排放置时,热点分布会变化,封装设计和散热方案要在早期就介入,不然后端会说“你这个 floorplan 我压不了功耗”。

4.5 启动流程与固件初始化

顺带说一下 SoC 启动,这虽然不是互联直接相关,但和整个系统的“可用性”强相关。推理 SoC 的启动流程一般是:BootROM 最先跑,完成最基础的时钟、PLL 和复位配置,然后从外部存储加载引导程序,引导程序再初始化 DRAM 控制器、NPU 电源域和 NPU 固件。NPU 固件跑起来之后,才把模型权重从外部存储搬进 NPU 的 SRAM 或后续外部内存。

这里有个实际易踩的坑:NPU 固件本身要加载到 scratchpad 或专用的 SRAM 区域,而且这个加载过程也走 DMA。如果启动时 NPU 对应的电源域还没就绪,DMA 可能访问到未上电区域,产生总线错误。所以固件加载顺序和电源域上电顺序必须严格对齐。这个需要 RTL 和固件团队联合确认,不能各写各的,回片前就要把启动时序理一遍。

5. 回片之后:常见问题与排查经验

硬件回片只是开始,真正打磨是在调试阶段。这一节整理了我在多颗推理 SoC 上实际遇到并解决过的问题,按出现的频率排序,希望能帮你少走弯路。

5.1 带宽地板效应:利用率上不去的头号原因

现象很典型:NPU 峰值算力很漂亮,跑 benchmark 单算子效率也不错,一跑完整模型利用率就只有三四成。排查时先用性能计数器看 stall cycle 分布,如果wait_dma占比超过一半,基本可以断定是带宽问题,而不是计算单元的问题。

对策要从几个方向同时做:一是调 tile 大小,让每次 DMA 搬运的块更大;二是把权重预取窗口从 1 个 tile 扩到 2~3 个 tile,让 DMA 提前跑;三是检查数据在 DRAM 里的排布是否连续,有没有跨页导致读放大。我遇到过最夸张的一次,只是把 L2 预取窗口扩大,Transformer 模型吞吐提升了 40%。这种优化不用动硬件,收益却非常可观,建议回片后第一件事就做这个。

5.2 功耗瞬态冲击:回片后最容易翻车

NPU 从 idle 跳到全速运行,瞬间电流变化极大,这个冲击如果没处理好,最直接的后果是电压跌落,然后时序违规,逻辑出错,看起来就是偶发性的计算错误,特别难查。

排查和预防的经验:设计阶段在 NPU 电源域预留足够的去耦电容面积,别为了省面积把 decap 砍掉。动态调频调压的爬坡策略要设计成逐级渐变,不要直接跳变。回片后的测试要专门做瞬态功耗冲击测试,用真实模型跑 idle→full load 的切换,观察电压噪声波形,确认留有足够裕量。这个测试建议放在最初一轮,越早发现问题越好改。

5.3 DMA 死锁与描述符 Cache 一致性问题

多路 DMA 同时搬运时,如果资源分配顺序设计得不好,会出现死锁:DMA A 占着 buffer 1 等 buffer 2,DMA B 占着 buffer 2 等 buffer 1,两边都在等,谁也动不了。防护办法是规定所有 DMA 通道对共享 buffer 的分配顺序一致,要么低编号优先,要么用一个硬件优先级表,并且预留超时中断方便现场抓状态。

另一个容易踩的是描述符被 CPU 篡改的假象。我们曾遇到 DMA 行为完全不可预测,搞了很半天才发现是 CPU 改描述符时,命令写到了 cache 里,但 DMA 读的是 DRAM,两边数据不一致。解决办法就是在 CPU 写完描述符后显式执行 cache line 刷新。这个需要在软件驱动里加两行代码,但很多人第一次做 NPU 集成时容易漏掉。

5.4 工具链和编译器:硬件没问题,问题在算子映射

回片后另一个高频问题是“同一颗芯片,换个模型性能差好几倍”。硬件本身没 bug,是编译器没把算子映射好。最常见的是卷积用矩阵乘实现时没做 im2col 优化,稀疏的算子硬生生跑成了稠密计算,浪费大量 MAC 周期;或者 attention 里的 QK^T、softmax、PV 被拆成多段,中间张量来回来回搬,带宽瞬间爆炸。

我强烈建议在架构阶段就把算子融合的接口定义清楚,至少让编译器有能力把 attention 的整体计算、LayerNorm+QKV 融合成一个硬件调度单元。我自己经手的一个项目里,把 QK^T 和 softmax 做算子融合后,端到端推理时延降了 30% 以上,而且没有改一行 RTL。

5.5 别忽略系统级验证的覆盖场景

系统级验证环节经常拖到最后才做,结果也是最容易让项目翻车的。光验证 NPU 单点和存储读写还不够,一定要把系统级的数据流场景跑通:CPU 启动固件 → NPU 加载模型 → DMA 开始搬运 → NPU 计算 → 结果写回 → CPU 读结果,这个全链路要尽可能早地在仿真环境里跑起来。

还有一个场景是异常注入。DRAM 访问延迟在真实系统里是波动的,遇到刷新周期、bank 冲突,一次访问可能比平均延迟多好几倍。但如果总线协议里没有超时处理,可能直接挂死。这个在 FPGA 原型验证时很容易测出来。建议在架构定义时就把超时、重试、错误上报这些机制写清楚,不要等到后端流片之后再补,那就真的晚了。

我做了几颗推理芯片之后,最深的体会是:算力是“买得到”的——堆 MAC、堆面积,总能把峰值做上去;但带宽是“攒出来”的,互联是“求出来”的,它们需要全链路一起参与设计,而不是最后再补。很多团队把重心放在 NPU 的 TOPS 数字上,流片后才发现,真正决定产品体验的其实是存储带宽和互连调度。如果你也在规划一颗新的推理 SoC,我建议立项初期就认真做一次全链路模型推演,把目标模型的访存轨迹完整跑一遍,再用这个轨迹去反复压测存储和互连架构。这一步做扎实了,后面回片后的麻烦会少很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询