第二章:高通ADSP架构详解
各位同学,今天我们来聊聊ADSP。说实话,ADSP是高通音频系统里最核心的部件,没有之一。我刚开始接触高通平台时,总觉得ADSP像个黑盒子——主CPU发个命令过去,音频就出来了。后来踩了不少坑才明白,不理解ADSP,你根本做不好音频调试。
2.1 ADSP的内核:一个独立的“小系统”
ADSP不是简单的硬件加速器。它有自己的内核,说白了就是一个独立的处理器。高通平台用的通常是Hexagon DSP,这是高通自家的架构。
Hexagon DSP有几个特点:
- VLIW架构:一条指令里可以塞多个操作。我刚开始看汇编代码时,差点被这种并行指令搞晕。
- 硬件多线程:支持同时跑多个线程,每个线程有自己的寄存器组。嗯,这点和主CPU的SMT有点像。
- 低功耗设计:专门为音频这类实时处理优化的。你想想看,手机放音乐时,主CPU可以睡觉,ADSP自己干活就行。
我记得有一次调试一个低功耗场景,主CPU已经休眠了,但音频还在播放。当时我就在想,ADSP到底是怎么独立工作的?后来看了内部文档才明白,它有自己的时钟域和电源域,完全可以脱离主CPU运行。
2.2 内存布局:别搞混了
ADSP的内存布局和主CPU完全不同。这里我给大家画个重点:
| 内存区域 | 用途 | 注意事项 |
|---|---|---|
| L2 SRAM | 代码和数据的高速缓存 | 容量有限,通常几百KB |
| DDR共享内存 | 与主CPU交换数据 | 需要缓存一致性处理 |
| AHB/APB外设空间 | 访问硬件寄存器 | 地址映射要小心 |
这里有个坑,我必须要提醒大家。ADSP访问DDR时,延迟比访问L2 SRAM大得多。我曾经遇到过一个性能问题,音频处理偶尔出现卡顿。查了半天,发现是某个音频算法把数据放在了DDR里,每次处理都要等几十个时钟周期。后来我把关键数据挪到L2 SRAM,问题就解决了。
2.3 任务调度:ADSP怎么安排工作
ADSP的任务调度和主CPU的Linux调度完全不同。它用的是基于优先级的抢占式调度,但有几个特殊之处:
- 硬实时任务:比如音频中断处理,优先级最高,不能被抢占。
- 软实时任务:比如音频算法处理,有截止时间要求,但可以稍微等待。
- 后台任务:比如日志输出,优先级最低,有空才做。
为什么会这样设计?说白了,音频是实时系统。你想想看,如果音频中断被其他任务抢占了,那声音就会断断续续,用户马上就能感觉到。
我个人习惯在调试时,先确认任务的优先级设置是否正确。有一次,我发现某个音频效果处理总是延迟,查了任务优先级才发现,它被设置成了后台任务。改成软实时后,问题就消失了。
2.4 与主CPU的通信机制
ADSP和主CPU怎么说话?主要有两种方式:RPMSG和GLINK。我分别说说。
2.4.1 RPMSG:轻量级消息传递
RPMSG,全称Remote Processor Messaging。说白了,就是两个处理器之间发消息。它的特点是:
- 基于共享内存:在DDR里划出一块区域,两边都能访问。
- 消息队列:发送方把消息放到队列里,接收方从队列里取。
- 简单高效:适合传递控制命令,不适合大数据量传输。
我举个例子,主CPU想告诉ADSP“开始播放音频”,就会通过RPMSG发一个消息。ADSP收到后,开始处理音频数据。这个过程非常快,通常几微秒就能完成。
2.4.2 GLINK:更复杂的通信协议
GLINK,全称Global Link。它比RPMSG复杂得多,但功能也更强大。GLINK支持:
- 多通道通信:可以同时建立多个逻辑通道,每个通道独立工作。
- 流式传输:适合传输音频数据流,比如PCM数据。
- 错误恢复:如果通信中断,GLINK可以自动重连。
我记得在一个项目中,需要同时传输多路音频数据(比如通话和录音同时进行)。用RPMSG的话,需要自己管理多个消息队列,很麻烦。后来改用GLINK,每个音频流用一个独立通道,代码清晰多了。
2.5 实际调试中的经验
说了这么多理论,我分享几个实际调试中的经验:
- 先确认ADSP是否在运行:有时候ADSP没启动,主CPU发消息也没用。可以通过查看ADSP的寄存器状态来确认。
- 检查共享内存地址:主CPU和ADSP对共享内存的地址映射可能不同。我曾经遇到过地址不对齐导致数据错乱的问题。
- 注意缓存一致性:主CPU写数据到共享内存后,ADSP可能读到旧数据。需要手动刷新缓存,或者使用非缓存内存区域。
嗯,这里要特别强调缓存一致性问题。我记得有一次调试,主CPU写了一个音频参数,但ADSP读到的总是旧值。查了半天,发现是主CPU的缓存没有刷新。加上缓存刷新操作后,问题就解决了。
好了,关于ADSP架构,今天就讲到这里。下一章我们会深入音频路径,看看音频数据是怎么从APP流到扬声器的。到时候我会分享更多实战经验。