在智能手机或智能手表等SoC上通常有一块专门的audio DSP(简称ADSP)来做音频处理。要做音频处理,ADSP首先要被boot起来。本文以CEVA BX2为例来讲讲ADSP的boot过程。
在上篇文章(Audio DSP 链接脚本文件解析)里讲过链接脚本里用关键字ENTRY来设定程序的入口,且这个入口在crt0.c里会用到。ADSP boot过程在crt0.c里也有涉及。先看看crt0.c里有什么,然后再讲ADSP的boot。图1给出了crt0.c开始的几行示例代码。

图 1
上图中crt0.c是用简单汇编写的。最上面是在链接脚本里用关键字ENTRY来设定的程序入口,即 __cxd_inttbl_start。下面是几种场景下的入口地址,主要包括boot时的入口地址(图中定义为0x10,即boot时ADSP从地址0x10处开始运行,然后调函数_boot)、发生中断时的入口地址(图中定义为0x60,即发生中断时ADSP从地址0x60处开始运行,然后调函数_critical_handler)、产生异常(exception)时的入口地址(图中定义为0x70,即产生异常时ADSP从地址0x70处开始运行,然后调函数_common_handler)。
回到boot过程。整个SoC中AP最先起来,ADSP是被AP boot起来的。AP boot ADSP前要做一些准备工作,比如把ITCM的内容拷进ITCM里(ASIC设计时AP可以读写ADSP的ITCM,从ADSP看ITCM的起始地址是0x0,从AP看ADSP的ITCM起始地址被映射成另外一个值)。AP做完准备工作后,写寄存器,让ADSP从boot的起始地址0x10处运行,这样ADSP就从0x10处运行,开始boot过程了。前文写的,从0x10处运行时开始调_boot函数。_boot函数里首先把DTCM的内容拷进DTCM里等,最后调用大家熟悉的main()函数。Main函数里有一系列操作,包括cache相关的、中断控制单元(interrupt control unit, ICU)的相关的、IPC相关的、RTOS相关的等。可以用图2的流程图表示,需要说明的是cache、ICU和IPC没有先后顺序的,谁先谁后都可以,RTOS是一定要放在最后的。下面一一来介绍。

图 2
首先看cache。Cache就是内存缓冲区,速度快于外部memory,接近处理器。用cache来拉近外部memory和处理器之间的性能差异,提高整个系统的性能。Cache分为icache和dcache,icache用于code,dcache用于data。ADSP内部的ITCM和DTCM都很小,audio软件开发需要用到外部的memory(如DDR)来放一些非典型场景下的code和data,如果典型场景下的code和data ADSP内部memory放不下也要放外部memory上,因此ADSP需要cache来提高audio子系统的性能。Boot时需要对外部用到的memory配置一下cache属性。放code的memory配置一下icache属性,一般配成cacheable。放data的memory配置一下dcache属性。如果放的是常量等不需要核间交互的数据就需要配成cacheable,如果放的是需要核间交互的数据就需要配成uncachable。举例来说,某一块外部memory用于放AP发给ADSP的音频数据(AP先把音频数据放到指定的memory上,然后发IPC告诉ADSP起始地址和大小,ADSP收到IPC后就从指定地址上取音频数据),这块memory就要配成uncacheable。如果配成cacheable,当ADSP去取音频数据时,数据有可能还在cache里而不是在这块memory上,造成取到的数据是错误的。
中断相关的配置在ICU(interrupt control unit,中断控制单元)里。中断包括内部中断和外部中断。CEVA BX2内部中断共有9个(中断号是从0到8),比如定时器中断中断号为6,软中断中断号为8。外部中断支持分成几组,每组32个。通常外部中断不会超过32个,用一组就够了。外部中断号从9开始往上加。常见的外部中断包括IPC中断等。中断有中断向量表,表里放的是每个中断的中断服务程序(ISR)。中断服务程序通常很简洁,只有几行语句,包括清中断以及后续处理等。对于每个中断来说,会有些硬件配置,包括中断是电平触发还是沿触发。沿触发也包括是上升沿触发还是下降沿触发。在boot阶段要disable所有的中断,等boot完成再把需要使能的中断放开。前文crt0.c里讲到有几个不同场景下的入口地址,来中断就是其中之一。当有中断时,会从0x70处开始运行,调用_common_handler函数,在函数里会执行注册的中断服务程序。中断服务程序运行完后去做任务调度,执行优先级最高的那个任务。
IPC用于各个core之间的通信。IPC的本质是中断加ring buffer。一个core在跟另一个core通信前,先把要通信的数据以规定好的格式写进指定的ring buffer里,然后给对方发IPC中断。对方收到IPC中断后,从ring buffer里取出数据按照规定的格式做解析,完成一次完整的通信。对于IPC来说,boot时将各个IPC的中断服务程序注册到相应的中断向量表里,并enable这些中断。需要注意的是这些IPC中断在系统运行过程中是不能disable的。
最后看RTOS相关的。先是做RTOS的初始化,然后创建各种需要的task,如IPC task、idle task等。创建完各种task后,系统处于等待WFI(wait for interrupt)的idle task。
经过以上几步后ADSP 就算boot起来了。