资讯动态

AI芯片软硬件协同设计实战:从NPU架构到Bring-up

发布时间:2026/10/26 20:31:38 来源:尧图企业网站定制
做AI芯片这几年我最深的一个体会是芯片流片只是起点软硬件能一起稳定跑起来才算阶段性成功。很多人以为“AI芯片”就是堆算力、做脉动阵列、跑个浮点峰值就完事实际项目里真正磨人的往往是算力之外的细节——从寄存器定义到驱动时序从电源上电到编译器算子融合任何一个环节掉链子整颗芯片都只能躺在Demo板上当摆件。这篇文章就以我实际参与的一个边缘AI推理芯片项目为例把软硬件协同设计的整体思路、关键模块的落地细节、验证测试的现场实录一一拆开讲清楚。这不是一篇教科书式的综述更像是我在你的工位旁边倒了两杯茶把项目复盘从头到尾口述一遍。内容面向正在做或者准备做AI芯片的开发者、验证工程师、嵌入式软件工程师也覆盖那些只是好奇“一颗AI芯片到底是怎么从PPT变成真芯片”的朋友。你会看到真实的决策过程、参数计算、踩坑记录也能直接拿走一些可复用的检查清单。1. 项目概述与整体设计思路1.1 这个项目到底要解决什么问题我们当时接到的需求很朴素做一颗面向边缘场景的AI推理芯片放在摄像头、小型机器人和工业设备里把图像分类、目标检测、分割这些常见模型跑起来。约束条件也很直接——功耗不能高成本要可控开发周期还被压缩得比较死。原型规格大致是INT8精度下目标算力在几十TOPS量级支持FP16低精度推理功耗控制在几瓦以内必须能跑CNN也要兼容Transformer类模型。注意这里说的是“兼容”而不是“完整支持”因为Transformer的注意力算子和传统卷积差异很大一开始我们根本不敢承诺大模型全场景覆盖。为什么非要自研芯片而不是用通用CPU或者GPU这个问题立项时被反复问过。核心原因有两个一是通用CPU跑神经网络的能效比太低同样的算力目标CPU功耗会完全失控二是GPU能效比好一些但生态太重、成本太贵边缘场景根本背不动。专用AI加速器能在能效比、成本和软件可控性之间找到最合适的平衡点这也是这类芯片在边缘市场存在的基础逻辑。1.2 软硬件协同设计是怎么介入的传统芯片设计流程一般是“需求→架构→RTL→验证→后端→流片”软件往往被扔到最后一步。但AI芯片特殊在它的价值只有通过软件栈才能发挥出来如果硬件做完了才发现编译器不支持某种算子、驱动跑不通某个中断那返工成本是灾难性的。所以我们在架构定义阶段就让编译器团队和驱动团队的人坐进了同一个会议室。第一步是做“工作负荷分析”把目标模型的算子清单拉出来统计每种算子的占比、访存量、计算量。这一步看起来简单做起来很琐碎因为模型更新很快今天以大卷积为主明天可能就变成注意力模块为主。当时我们拿几个经典模型一个图像分类网络、一个检测网络、一个小型Transformer跑了一遍分析和性能建模才发现访存带宽比MAC阵列规模更紧张。性能模型在这里帮了大忙。我们先用Python搭了一个粗略的周期级模拟器把每层算子的计算时间、DDR带宽占用、SRAM命中率都估出来然后对照需求看瓶颈。最后团队达成共识把MAC阵列做得太大不如把片上SRAM和总线带宽做足因为边缘模型的权重和中间特征图经常能在片上放下减少一次DDR访问比多塞十个计算单元都有效。这一点是整颗芯片最能影响成败的宏观决策后面所有硬件和软件设计都围绕这个结论展开。1.3 为什么会把“软硬件设计”作为一个系列来写这个项目其实是从一个更早的探索项目延续下来的。第一版芯片只做硬件原型验证算力不高软件栈也没有认真做结果连内部同事都不想用那颗片子。到了第二版我们才痛定思痛意识到AI芯片的“设计”根本不是单指RTL设计而是从体系结构到工具链的整个链条。所以这篇内容的“第二版”定位并不是简单的续写而是把重点从“怎么把芯片做出来”转向“怎么把软硬件作为一个整体设计出来”。很多坑是第一版踩过之后我们才总结出规律比如寄存器接口必须和驱动开发并行冻结、电源管理必须给软件留出控制接口、量化工具链必须在流片前就跑通目标模型。2. 核心硬件架构设计与关键模块解析2.1 NPU计算阵列为什么选这个尺寸计算阵列是AI芯片的心脏常见方案是脉动阵列Systolic Array和SIMD向量单元。脉动阵列的优势是数据复用率高输入特征图和权重在阵列里像流水线一样传递每个乘法器只需要从相邻单元拿数据大幅降低寄存器堆和总线压力。缺点是对数据排布敏感如果算子形状跟阵列尺寸不匹配利用率会掉得很难看。我们最终选了16×16的INT8脉动阵列外加一组FP16向量单元做归一化、池化和激活这类“非矩阵”操作。为什么是16×16不是因为16这个数字吉利而是当时主要目标模型的3×3卷积里输出通道数大多在64或128。16×16的阵列可以把一个输出通道的一个空间位置完整计算掉边界情况少调度简单。配套的片上SRAM容量也做了取舍。权重缓冲留了几百KB激活缓冲留了几百KB总共加起来差不多1MB级别。对这个量级的边缘模型来说像YOLO类检测网络的大部分层都能把权重和中间特征图塞进片上真正需要DDR搬运的只有前后几层。这个决定直接影响了后面总线和DMA的设计也避免了大量数据搬运造成的功耗浪费。2.2 SoC启动流程与Boot设计要点热词里“SoC芯片启动”是很多人关心的话题。AI芯片一般是一个完整的SoC内部除了NPU还集成了一堆应用处理器核心、DSP、内存控制器和各种外设。启动流程基本是上电后处理器核心从复位向量取第一条指令这条指令放在芯片内部的BootROM里BootROM代码初始化时钟和DDR然后根据启动引脚的状态选择一个启动源把FSBL第一阶段引导程序加载进内存再跳转过去执行。启动源的选择很重要。常见Boot模式有从SPI Flash启动、从eMMC启动、从SD卡启动、从UART或USB下载启动。设计PCB时这些Boot模式引脚一定不能悬空必须用电阻固定到确定的电平不然芯片上电时可能随机选错启动源现象就是串口完全没输出或者板子反复重启。我们在第一版芯片上踩过一个很典型的坑DDR初始化参数直接写死在BootROM里后来兼容了一颗不同型号的DDR颗粒结果BootROM的时序参数没有跟着改回片后启动成功率只有两三成排查了好几天才发现DDR初始化那段代码的时序完全不对。所以后面改成了把DDR时序参数放在一个可配置的区域软件可以在启动早期覆盖默认值这极大提升了后续做硬件适配的效率。2.3 电源、时钟与复位设计中的关键参数硬件设计中电源、时钟和复位是最不起眼但也最容易致命的部分。AI芯片一般有多个电压域核心逻辑、IO、DDR、PLL、SRAM各自可能需要不同的电压。上电时序必须严格按照芯片手册来设计比如先给IO域供电、再给核心域供电顺序反了轻则芯片不工作重则损坏器件。很多“芯片没反应”的故障最后查出来都是电源时序问题。电源芯片和LED驱动芯片里的FB反馈脚和CS电流采样脚也是高频踩坑点。FB脚的原理是通过电阻分压把输出电压反馈给电源芯片内部参考电压从而稳定输出电压。假设某颗驱动芯片内部参考电压Vref是1.2V你希望输出12V那就可以按Vout Vref × (1 R1/R2)来算分压电阻。比如取R210kΩ解得R1 (12/1.2 - 1) × 10k 90kΩ这就是FB上电阻的值。CS脚则是电流采样端通过采样电阻上的压降来设置输出电流限制Rcs越大限流越小这个阻值必须留足裕量否则重载瞬间就触发过流保护。看门狗芯片也一样看起来简单但细节很多。溢出时间由内部振荡器频率、预分频系数和计数初值共同决定典型的计算是溢出时间 看门狗时钟周期 × 预分频值 × 计数值。喂狗脉冲必须满足最小宽度太窄了芯片不认同时喂狗间隔不能太短也不能太长短了容易在高优先级任务里占资源长了就真的触发复位了。我们在固件里把喂狗任务优先级提得很高但又在Bringup阶段默认关闭看门狗免得调试时不明不白被复位打断。2.4 接口与引脚设计里的那些小细节一个AI SoC往往有几十上百个引脚引脚复用PinMux是硬件和软件交接最容易出问题的地方。同一个引脚既可能是UART发送脚也可能是GPIO输出、PWM输出或者调试接口的信号配置错了最直接的后果就是“处理器在跑代码在跑但串口就是没日志”。做PCB之前一定要把芯片手册里的默认引脚状态表过一遍重点看几个关键信号Boot模式引脚、复位引脚、时钟引脚、调试引脚。如果某些引脚默认是带上拉的而你的外围电路刚好把它拉低了芯片启动状态就可能跟预期完全相反。接口协议上现在很多边缘AI设备用USB Type-C供电Type-C链路里有个容易被忽略的E-Marker芯片。它的作用是在线缆/设备端通过CC引脚向供电端报告自己的能力比如支持多大电流、是不是被动线缆。如果Type-C端口设计有问题或者E-Marker芯片没有正确配置最常见的现象是充电功率上不去甚至完全无法握手。听起来跟AI芯片没关系但整机联调时这种配套芯片的问题经常把硬件团队折腾到半夜。还有一类周边芯片也很常见就是射频接收模块或者无线唤醒模块。拿低频无线接收模块来说它通常有一个数据输出引脚直接连到主控的中断输入脚这个引脚要处理好默认电平和唤醒脉冲的关系。如果接收模块输出常态是低电平那么主控中断就要配成上升沿触发配反了会出现“偶发唤醒正常、常态完全没反应”的诡异现象。3. 软件栈设计与协同开发流程3.1 从寄存器到驱动软硬件接口怎么冻结AI芯片的软件栈大概分几层固件/驱动、运行时、编译器/算子工具链、模型转换与量化工具、上层推理引擎。任何一层的设计都必须建立在清晰的软硬件接口规范上。我们内部有一份寄存器规范这也是硬件和软件之间的“合同”。每个功能模块的寄存器名、地址偏移、位字段定义、读写属性、默认值都要写清楚。在RTL冻结之前这份规范必须先冻结否则硬件改了寄存器地址驱动代码也得跟着改两边追着改的滋味非常难受。拿NPU来说它对外至少需要这些寄存器寄存器组功能说明关键字段控制寄存器启动/停止NPU、软复位、时钟门控RUN、RESET、CLK_EN中断状态寄存器任务完成、错误、超时DONE、ERR、TIMEOUT命令队列寄存器写入任务描述符地址、触发执行CMD_ADDR、CMD_TRIG状态寄存器当前任务状态、队列深度、利用率BUSY、QUEUE_DEPTH内存映射寄存器设置权重/输入/输出的物理地址SRC_ADDR、DST_ADDR这些寄存器名称看着简单但地址对齐、读清还是写清、中断触发边沿等细节稍有不慎就把驱动工程师逼疯。我们在设计规范里专门加了一条所有中断状态寄存器必须支持“写1清0”这样驱动处理中断时直接写对应位就能清除省掉一堆读改写操作。3.2 编译器与算子库让模型能跑起来的魔法层编译器是AI芯片能否被真正用起来的决定性因素。主流程是接收框架导出的模型结构→转成中间表示IR→做算子融合和内存规划→生成NPU机器码→交给运行时加载执行。算子融合是收益最明显的优化之一。卷积批归一化ReLU这三个算子如果分开执行中间结果都要写回DDR不但慢而且费电。把它们融合成一个统一算子之后中间张量完全可以在片上SRAM里传递带宽压力骤降。我们在编译器里实现了一层启发式规则专门检测这种连续的模式并自动替换为融合算子。内存规划也很关键。AI芯片的片上SRAM非常贵要像做拼图一样给每一层的输入、输出、权重分配地址还要考虑生命周期避免两块大缓冲区互相覆盖导致数据被踩。我们在这个环节踩过的坑是编译器为了省内存把两个中间张量放到同一块地址上但没有考虑一个张量还在异步执行另一个就开始被写入结果推理结果随机性出错最后靠反复运行加上数据比对才定位到是内存别名问题。3.3 驱动与固件把硬件能力暴露给软件世界驱动程序是连接上层运行时和底层硬件的中转站核心任务是初始化NPU、分配和管理内存、提交任务、处理中断、处理错误恢复。一个成熟的NPU驱动会维护一个命令描述符队列上层要跑一个模型时驱动把任务描述符写入内存然后通过写寄存器的方式“踢”一下NPUNPU异步执行完成后触发中断驱动在中断回调里唤醒等待的上层任务。这中间有个细节特别值得说上下文切换。边缘设备上很可能同时有多个模型在跑比如摄像头检测模型和语音唤醒模型此时如果两个任务都抢NPU必须做一个硬件级别的上下文切换。我们在NPU里加了一组“任务状态保存寄存器”切换时把当前任务的关键状态比如当前指令指针、状态寄存器、部分执行进度暂存起来再加载新任务切换延迟控制在微秒级保证多任务并发不互相踩。从带宽和功耗角度DMA是NPU搬运数据的默认路径。CPU只需要准备好源地址、目的地址、长度和方向剩下的大块搬运全部交给DMACPU可以干别的事等到传输完成中断再回来收尾。这种设计对AI推理尤其重要因为模型推理的数据量动辄几十MB让CPU一块块搬能把性能拖垮。3.4 面向大模型和AI Agent时代的软件架构演进现在谈AI芯片软件栈绕不开大模型和AI Agent。即便是边缘侧的AI芯片也要支持Transformer类模型这意味着编译器可能需要支持动态形状——输入长度不再是固定值而是每次都可能变化。传统CNN推理可以提前把内存规划好Transformer必须处理变长输入内存分配和算子调度都会复杂很多。从大模型基础理论的角度看Transformer的核心计算是矩阵乘法和注意力机制。注意力需要为每个Token生成Query、Key、Value向量再计算Query和Key之间的相似度最后用相似度加权Value。这个过程中间会产生一个完整的注意力矩阵尺寸是“序列长度 × 序列长度”占据大量内存带宽。硬件上为此需要为QKV矩阵乘法提供足够算力同时给KV Cache留出高速存储空间为了避免每次推理都重复计算Key和Value。再往前看AI Agent以及多AI协作场景要求一颗芯片上同时承载多个模型的推理服务模型之间有调度和优先级关系。我们在软件栈里引入了模型级QoS隔离给每个模型配置算力上限、内存上限、优先级运行时按照这些配置做任务抢占和排队避免一个重模型把整个芯片资源占满。实测下来这种“多模型并发”的调度机制比单纯把一个模型跑到饱和更有价值因为真实场景就是多个智能功能同时在跑。3.5 开发者工具链与体验软件栈如果不考虑开发者体验迟早被弃用。我们团队在交付SDK时会刻意把“示例工程”写得极其完整不只是给一段API调用示例还要给一个能编译、能烧录、能跑出正确结果的完整工程。因为很多嵌入式工程师的习惯是“先跑通示例再改自己的代码”如果示例工程本身都有问题他们对这颗芯片的信任度会瞬间归零。近几年AI编程助手在IDE里普及之后我们写SDK文档也顺手多了很多重复性的寄存器结构体代码可以直接用提示词让助手生成初稿再由工程师审核修改。这本身也是AI赋能软硬件开发的一个缩影。不过要提醒一句AI生成的代码一定要人工核对寄存器地址和时序要求自动生成的代码通常不具备“硬件背景知识”。4. 验证与测试环节实录4.1 前端验证仿真和覆盖率来兜底AI芯片的验证工作量占据整个项目的大头。我们用通用验证方法学搭建验证环境围绕NPU生成大量随机指令序列重点验证中断、异常、地址越界这些边界场景。验证环境里最核心的东西是“功能覆盖点覆盖率”我们为每条指令、每个中断来源、每种异常类型都定义了覆盖点只有覆盖率达到预定目标才允许流片。跨时钟域CDC是NPU和CPU交互时最容易出bug的地方。CPU跑在几百MHzNPU内部有些逻辑跑在更高频率两者之间还有异步FIFO。我们用专门的CDC检查工具把所有跨时钟信号都过了一遍还是漏掉了一个异步FIFO的空满状态信号直到硅前模拟开始跑全系统时才发现经常死锁。这个教训告诉我们验证不是只跑功能还要专门做异步交互和复位同步的验证。4.2 后端物理实现从RTL到版图的最后一段路流片之前的后端物理设计听起来离软件很远但最终性能都由这里决定。综合阶段会把RTL映射成标准单元布局布线决定这些单元的物理位置和连线关系。后端要解决的核心问题是时序收敛每一条信号路径的传播延迟都不能超过时钟周期。NPU里最容易成为关键路径的就是MAC阵列的累加链。16×16阵列的每一列都要把16个乘法结果逐级相加如果全部用同一周期的组合逻辑路径延迟会爆表。我们的处理方式是插入流水寄存器把累加拆成多个周期完成牺牲一点延迟换取更高主频整体吞吐反而提升了。功耗分析同样重要。动态功耗近似等于开关电容乘电压平方乘频率所以降电压是收益最大的手段。我们给NPU做了DVFS在低负载时自动降频降压实测空闲功耗下降很多。后端还要关注IR Drop和信号完整性电源网络上某一点电压跌落可能直接导致附近逻辑在极端工况下出错这种问题只能在物理设计阶段通过加宽电源走线、加去耦电容来缓解。4.3 芯片测试把坏片和好片区分开流片回来的晶圆要先经过测试再切割封装。量产测试最常用的结构是扫描链和BIST。扫描链把触发器串联成移位寄存器测试时把测试向量从输入端灌进去再从输出端扫出来比对BIST则是让SRAM自动跑一整套读写模式用于检测存储单元故障。测试向量来源是个很实际的问题。我们在仿真环境里维护一套回归用例设定为“跑一遍所有指令并校验结果”然后把这些用例的激励和期望响应导出成ATE测试向量格式。这样能保证量产测试覆盖到的功能和我们在仿真里验证过的功能保持一致。第一次做量产测试时我们还遇到过一种“假失效”芯片在ATE上显示失败拿下来手焊到测试板上又一切正常。排查了半天发现是ATE测试频率设得太高加上探针接触电阻偏大导致时序裕量不足。最后把测试频率下调一档所有片子的功能测试全部通过。4.4 回片Bring-up实录拿到封装好第一颗工程样品芯片的那天团队定的纪律是“先别急着跑模型一步一步来”。Bring-up顺序是一条铁律先测电源短路用万用表打各电源域和地之间有没有低阻再上电用示波器看各路电源的上电时序是否满足手册接着测时钟确认晶振起振了没有PLL锁定没有最后测复位确认释放时机和毛刺情况。这些都正常后才会通过JTAG连上芯片读取芯片ID寄存器确认内部逻辑已经运行。接下来是初始化DDR然后跑一个特别简单的小循环程序比如让CPU翻转一个GPIO用示波器看波形。这种测试看着低端但能快速确认CPU取指、执行、写外设的全链路是通的。一切正常之后我们才会烧写完整固件、加载驱动跑第一个真正意义上的AI模型。第一次跑通检测模型的时候团队都很兴奋。但兴奋完马上进入痛苦的性能调优阶段模型是能跑但延迟比性能模型预测的慢了将近一半于是紧接着就是连续好几天的性能分析。4.5 性能摸底与调优性能不达标其实90%不是算力不够而是数据没喂饱计算单元。我们用硬件计数器看NPU的实际忙闲比例发现计算阵列经常在等数据。问题出在编译器生成的内存访问序列上相邻的权重块没有放到同一行缓存导致DMA每次只能搬一小块搬运效率极低。调整办法有两个一是改编译器的内存排布策略尽可能让连续访问的权重落到同一个连续地址段二是调大DMA搬运的块粒度让每次搬运都能跑满总线位宽。优化之后MAC阵列利用率从不到40%提升到70%以上。这个过程特别能说明软硬件协同设计的意义——硬件能力就在那里但只有软件懂得怎么把数据喂给它芯片才能真正发挥性能。5. 常见问题与排查技巧实录5.1 开发环境找不到目标芯片器件很多嵌入式项目在开发环境IDE里装芯片支持包时会遇到“搜不到器件”的问题。常见原因是支持包源版本太旧或者本地缓存损坏。解决时不妨先更新支持包源再清理IDE缓存必要时手动下载离线支持包并指定安装路径。另外检查环境变量里的路径配置有的IDE会把包路径指向错误目录。这类问题虽然小但在项目启动阶段很耽误时间最好提前把支持包下载好离线安装。5.2 电源芯片调压不准导致系统反复复位现象是上电后系统偶尔能启动但几秒后复位或者负载稍大就掉电。用示波器抓电源波形多半能看到电压爬升缓慢或者跌落。这时候要检查的就是FB引脚分压电阻。按公式Vout Vref × (1 R1/R2)反推当前输出电压看看是不是和预期差太多。还有可能是输出电容容量不够瞬态响应跟不上。CS脚采样电阻阻值如果偏大限流点就会偏低系统负载一大就触发保护表现为“一运行就被断电”非常迷惑人。5.3 SoC启动卡死串口完全没有日志遇到这种问题按顺序排查先用示波器确认复位引脚有没有正常释放再看时钟芯片输出频率对不对然后测量Boot模式引脚电平确认它确实停留在当前设计想用的启动方式最后检查串口引脚复用和波特率。这里提供一个实用技巧很多芯片的BootROM会提供“运行状态指示”功能比如点亮某个LED或者循环闪烁几次表示代码走到了哪里。Bringup阶段哪怕占几个GPIO也比串口没有输出时盲猜强一百倍。5.4 引脚配置对不上调试口变成普通GPIO调试串口没有输出的另一个常见原因是引脚复用没配对。很多芯片引脚默认处于GPIO状态要切换到UART功能必须设置对应的复用寄存器同时还要求该引脚所在电源域已经供电。排查时一定要对着芯片手册的引脚复用表逐项核对尤其要注意默认上下拉状态跟你的外围电路是否冲突。很多从旧平台移植过来的硬件沿用原来的外围电路却忽略了新芯片引脚的默认状态不同就很容易出这种问题。5.5 NPU算子精度掉点推理结果不稳定精度问题几乎永远是量化环节先背锅。用校准集做量化时如果校准集代表性不足算出来的缩放系数可能偏向某一类数据导致其他数据精度崩掉。我们的经验是校准集至少要覆盖真实场景中的所有典型亮度、对比度、噪声分布不做筛选直接拿一张图去定scale效果基本没法看。另一个隐蔽的精度问题来自算子融合顺序。如果一个融合算子在寄存器配置上占用了比预期更大的位宽后续算子拿到的中间结果可能被截断。保留一个“调试模式”把NPU每层输出结果与CPU浮点参考结果逐层对比可以很快定位到具体是哪个算子出的问题。5.6 测试中出现的“假失效”和“偶发失败”量产测试中最怕的不是坏片而是时好时坏的“假失效”。ATE测试失败但手工验证通过首先怀疑测试频率过高其次检查IO驱动强度是否不足、探针接触电阻是否稳定。还有一种情况是测试向量本身有问题比如某个寄存器的初始值没有正确复位导致期望值算错。经验是准备一套“离线验证程序”把ATE里报错的向量在低速下重放一遍如果通过几乎可以断定是时序或者接触问题而不是芯片本身有问题。最后分享一个让我印象深刻的排查案例在项目收尾前我们整个团队被一个“偶发启动失败”折磨了将近两周。现象很随机有时候一天出现一两次有时候几天不出现而且看起来跟环境温度、电源电压都无关。后来把所有日志打开才发现真正的元凶是看门狗上层应用里某个线程偶尔会被一个慢速外设中断拖住喂狗任务来不及在超时时间内执行看门狗芯片就干脆利落地把SoC复位了。定位思路其实不难难的是要有意识把“启动失败”和“复位”联系在一起。从那以后我在所有固件项目里都做两件事一是在Bringup阶段默认关闭看门狗等系统逻辑稳定后再打开二是把喂狗任务独立成一个最高优先级的小任务不跟任何业务逻辑绑在一起。这个习惯帮我避掉了很多后来项目里可能出现的“灵异问题”。做AI芯片的软硬件设计越往后越觉得这是一门“配合的艺术”。硬件给不了的能力软件再折腾也榨不出来软件不愿意配合的硬件也必然会被市场冷落。希望这篇复盘里的经验能帮你少踩几个坑也欢迎在评论区聊聊你在这类项目里遇到过最奇怪的Bug。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑