NPU的RISC-V扩展:自定义NPU指令
去年做AIoT芯片项目,调试一个卷积加速器的时候,发现RISC-V核和NPU之间通信延迟占了总推理时间的40%。当时用的还是传统做法——通过MMIO写寄存器来触发NPU操作,每次启动卷积要写十几个寄存器,光地址译码和总线握手就吃掉几十个周期。后来实在忍不了,直接在RISC-V的指令集里塞了一条自定义指令,把NPU启动变成了单周期操作。今天聊聊这个坑怎么填的。
为什么非要动指令集
很多人觉得RISC-V的扩展接口就是给学术界玩的,工业界用标准指令就够了。但当你真正做NPU的时候会发现,NPU和CPU之间的交互频率远高于你的想象。不仅仅是启动计算,还有数据搬运、同步屏障、状态查询——这些操作如果都走内存映射IO,每次都要经历load/store、地址译码、总线仲裁、外设响应这一整套流程。
我那个项目里,NPU每处理完一个tile就要通知CPU取结果,CPU再下发下一个tile的参数。这个握手过程如果用MMIO,一次就要15-20个周期。而自定义指令直接在流水线里完成,只需要1-2个周期。对于需要频繁交互的tinyML场景,这个差距直接决定了能不能跑实时。
RISC-V的扩展机制
RISC-V的指令编码里预留了四个自定义空间:custom-0、custom-1、custom-2、custom-3。每个空间对应一组opcode,你可以自由定义里面的指令格式。我一般用custom-0做NPU控制指令,custom-1做数据搬运指令,这样分类清晰。
指令格式上,R-type和I-type最常用