我们队伍今年备赛FPGA大赛,选了高云FPGA做实时语音识别,算法流程是MFCC特征提取加GMM分类。现在MFCC的梅尔滤波器组和FFT中间结果占用了大量BRAM,资源表上BRAM已经用了90%以上,还有DNN分类器没部署。试过把部分查找表改成分布式RAM,但时序变差。想问问有没有做过类似项目的同学,BRAM不够用的时候一般怎么取舍?是压缩FFT点数还是减少滤波器组数量?或者有没有办法用DSP48复用做部分计算来省BRAM?
2026年,FPGA大赛用国产高云FPGA做实时语音识别,MFCC特征提取在PL侧加速,BRAM不够用怎么优化?
提问
回答 4

高云的BRAM确实紧,90%占用再塞DNN基本没戏。我建议你先砍FFT点数:如果采样率是16kHz、帧长512点,试试降到256点,MFCC的频带分辨率损失对语音识别影响通常不大,因为梅尔滤波器本来就是平滑的。省下来的BRAM留给DNN。滤波器组数量别轻易减,少于20个Mel带识别率会掉。另外检查一下FFT核有没有用高云的FFT IP核,那个IP核自带双口RAM存储旋转因子,可以改成自己写流水线FFT,用寄存器阵列代替旋转因子ROM,能省一块BRAM。追问:你们采样率和帧长是多少?

BRAM不够,我当年做图像处理也踩过坑。说几个实际能用的路子:第一,FFT中间结果不用全存,MFCC是逐帧处理的,每帧FFT做完直接算能量,中间数据扔掉,只保留梅尔滤波器系数和最终特征向量,这样BRAM只存系数和累加器,能省一大块。第二,DSP48复用做乘法累加,把梅尔滤波器的乘加操作挪到DSP48上,原本用于滤波器系数的BRAM可以换成分布式RAM存少量系数,但时序问题你得用流水线寄存器打几拍来修。第三,如果还是不够,考虑把GMM分类器放在软核里跑,PL只做MFCC特征提取,这样BRAM压力全在PL端,DNN那部分不占硬件资源。不过软核跑分类有实时性风险,得看你们帧间隔是多少。个人感觉最稳妥的是先砍FFT点数到256,同时把滤波器系数存到外部SPI Flash,每帧加载一部分,换时间换资源。你们目前用的是多少阶的FFT?

这个优化问题本质上是存储带宽和计算资源之间的trade-off,我按工程实现的角度给你拆三层。
第一层,先确认BRAM的具体消耗分布。高云的BRAM一般是18K或9K块,你90%占用要搞清楚是哪几块吃得多。常见大头是FFT的旋转因子表和梅尔滤波器系数表。旋转因子可以用CORDIC算法实时计算,代价是多用几个DSP48和LUT,但能省掉整块旋转因子ROM。梅尔滤波器系数可以量化成8位定点数,BRAM位宽减半能省一半深度。如果你们现在用的是32位浮点,改成16位定点,BRAM占用直接打对折。时序变差的问题,大概率是因为分布式RAM的读取延迟比BRAM大,你可以在路径上插一级寄存器做retiming,高云的布局工具对流水线寄存器比较友好。
第二层,如果量化后还差一点,考虑把梅尔滤波器组拆成两段流水:第一段存部分系数,第二段实时计算剩余系数。比如原本40个滤波器,只存前20个的系数在BRAM里,后20个用LUT加少量DSP48即时生成近似值,语音识别对滤波器形状的精度容忍度很高,近似系数影响很小。这样BRAM占用能降到50%左右。
第三层,最后一条路是改变算法流程。MFCC提取里最吃BRAM的是短时FFT的中间缓存,你可以把一帧的语音数据先存到外部SDRAM,然后分块读进PL做FFT,每次只处理一小块,中间结果存回SDRAM。这样BRAM只做缓存窗口,不用存全帧。代价是控制逻辑复杂一点,但高云的低端系列有硬核SDRAM控制器,可以试试。
另外提醒一句:DNN分类器如果层数不多,可以考虑用HLS或者手写一个轻量网络,把权重存在外挂Flash里,每层推理时逐次加载,BRAM只存当前层的激活值。这样DNN部分几乎不占BRAM。
综上,我建议的顺序是:先定点量化,再拆滤波器组,最后才考虑外存方案。你们目前用的是高云哪款芯片?不同系列的BRAM大小和DSP数量差异挺大的。

你们 BRAM 吃到 90% 还差 DNN 没放,这个余量确实太紧了。我建议别急着砍 FFT 点数或 Mel 带数,先做一件事:把整个数据流的存储生命周期画出来。MFCC 里最占 BRAM 的通常不是 FFT 的旋转因子(那玩意一般用 ROM 实现,高云 18K 块能塞下 512 点双精度),而是梅尔滤波器系数表和帧缓存。如果你用的是滑动窗每帧 50% 重叠,那前一个帧的 FFT 结果和当前帧的 FFT 结果其实可以流水处理——只保留当前帧的幅度谱,做完 Mel 滤波就送进累加器,上一帧的幅度谱直接覆盖。很多教材上的 MFCC 实现为了写代码方便,会把整段音频的 FFT 结果全存下来再算滤波器,这在 FPGA 上就是浪费。你检查一下代码里是不是每帧做完 FFT 后把复数结果写进了 BRAM 然后等所有帧做完再读出来算能量?如果是,改成边做边算,BRAM 只存当前帧的幅度谱(比如 256 个 16 位定点数),这样能省掉 90% 以上的帧缓存。滤波器系数表如果用的是 32 位浮点,转成 12 位定点,把 Mel 带的 40 个系数按地址顺序排成一张表,用后立即丢弃,这样 BRAM 占用能从几万比特降到几千。DSP48 复用做 Mel 滤波的乘累加是可行的,高云的 DSP48 支持级联,你只要把系数从 BRAM 里按周期搬出来,DSP48 做流水线乘法,一个周期出一个结果,这样滤波器组本身就不需要额外的 BRAM 做中间累加缓存。时序变差通常是因为分布式 RAM 的读写路径没做寄存器打拍,你在分布式 RAM 的输出端插一级触发器,高云的布局工具对寄存器密集的路径优化力度比纯组合逻辑好很多。最后如果还差一点,可以考虑把 GMM 分类器的高斯分量权重存到外部 SPI Flash 里,每帧只加载当前分类器需要的几个权重,用换时间换面积。你们目前帧长和帧移设的是多少?这直接决定 FFT 点数能不能砍到 256 以下。
发表回答
登录后可在本页底部提交回答
