在 audio DSP 中如何做软件固化
在 audio DSP 中, 软件的 code 和 data 主要放在 3 种不同的 memory 上,分别是片内的 ITCM、DTCM 和片外的 memory(比如 DDR)上。ITCM 只能放 code,DTCM 只能放 data,片外的 memory 既能放 code 也能放 data。在写代码时要规划好哪些放片内,哪些放片外。上面说的这三种 memory 都属于 RAM(random access memory, 随机访问存储器),可读可写。与之相对应的是 ROM(read only memory,只读存储器),只能读。ROM 相对 RAM 的优点之一是相同存储容量的情况下 ROM 的面积要比 RAM 小很多,在芯片上面积小就意味着成本低。为了降成本,有必要将部分 audio 的 code 放在 ROM 里,通常称为将软件固化。哪些 code 可以放进 ROM 呢?从 ROM 的只读特性知道放进 ROM 的 code 就不能改动了,因此放进 ROM 的 code 是经过充分验证的不会再改的代码,在音频领域主要是一些非常成熟的算法的代码,比如 MP3 解码算法的代码就适合放进 ROM 里。本文就以把 MP3 解码算法的代码放进 ROM 为例来讲讲是怎么一步步做的。
1,确定好哪些放进 ROM 里,即把哪些做固化。通常是一些非常成熟且不会再改的代码,多数是成熟算法的代码。确定前先要跟 ASIC 定好 ROM 的大小,比如 128K 或者 256K。还要算好要放软件模块的 code size 和 data size。要放进 ROM 的模块最好能把 ROM 塞满,充分利用 ROM。本文就是把 MP3 解码算法的代码放进 ROM 里。
2,ROM 在 memory 里是独立的一块。在芯片 tape out 前软件工程师把用于 ROM 的二进制文件给 ASIC 工程师,ASIC 工程师再将 ROM 文件里面的二进制数据放进 ROM 里。 开发 ROM 文件时,芯片还处于设计阶段,没法直接拿来用,只能在要设计芯片的相似芯片上去开发,用 RAM 中的一块独立的区域来模拟 ROM。通常在片外 memory 上找一块独立的区域,因为内部 memory 的空间相对较小,不适合做。我在固化 MP3 解码 code 时就在 DDR 上找了一块独立的区域来放 MP3 解码算法的 code 和 data。 即以前 MP3 解码的 code 和其他的 code 是放在一起的,以前 MP3 解码的 data 和其他的 data 是放在一起的,现在要把它们拿出来放在一个独立的区域。放在独立区域后开始调试,要确保 MP3 播放功能正常。调试时主要是修改 LD(link descriptor)文件,改后用新生成的 adsp.bin 文件去播放 MP3,正常播放就说明改对了。
3,ROM 里的函数有可能调用其他函数,如 libc 里的库函数 memset 等。这些函数如果不放在 ROM 里,而是放在其他地方,它们的地址随着软件的开发就有可能发生变化(放进 RAM 的函数的地址是动态变化的)。而 ROM 里这些函数的地址还是先前做 ROM 时的地址,这些地址已经对应不上那些函数了,就不会得到正确的执行。因此要把这些函数排查出来,并放进 ROM 里。怎么排查呢?方法是在 LD 文件里定义一个 rom_check_shift 的变量,放在 RAM 上的 code/data section 的头部,好让 code/data 的地址产生偏移。刚开始设 rom_check_shift 为零,会生成 ROM 上 code 的一个反汇编文件。 然后不断增大 rom_check_shift 的值(比如从 0 到 16、32、64、128 等),同样会生成 ROM 上 code 的一个反汇编文件,将其与 rom_check_shift 为 0 时的反汇编文件进行比较,要确保代码的完全一样。如果不一样,看比较后不一样的地方,把找到的函数放进 ROM 里,直至完全一样。修改后也要确保播放 MP3 音乐功能完全正常。下图是比较反汇编文件时一处不一样的地方。
![](https://static001.geekbang.org/infoq/38/38455d3bb48ecd0303564e5d105f3326.jpeg?x-oss-process=image%2Fresize%2Cp_80%2Fauto-orient%2C1)
从上图看出,库函数 memcpy()原先是放在 RAM 上的,由于 rom_check_shift 的改变,函数 memcpy()的地址就不一样了。ROM 里的函数调用函数 memcpy()时,函数地址还是做 ROM 时的,可是后面随着软件的开发,memcpy()在 RAM 上的地址变了。ROM 里的函数再去访问 memcpy()原先的地址已经不能正确调用 memcpy()了。因此要把 memcpy()放进 ROM 里,确保它的地址永远不变。
4,ASIC 的同学会告诉 ROM 的起始地址。先前是把要放在 ROM 上的先放在 RAM 里方便调试。现在调试 OK 了,就要把这部分放到 ROM 上去了。由于这部分放在 ROM 里就不能再包含在 adsp.bin 里,因此要修改生成 adsp.bin 的应用程序的代码,把放到 ROM 的部分生成单独的二进制文件,而不是放到 adsp.bin 里,这样 adsp.bin 就变小了。
5,得到用于 ROM 的二进制文件后,需要做格式转换,转成 ASIC 需要的格式。下图列出了软件生成的二进制格式以及 ASIC 需要的格式。
![](https://static001.geekbang.org/infoq/15/15abe6c92dee2d16eca6a77bc31b2f91.jpeg?x-oss-process=image%2Fresize%2Cp_80%2Fauto-orient%2C1)
从上图看出,ASIC 需要的格式是一行放 8 个字节,同时放在 ROM 里的数据是小端放的。知道怎么转换后,写个小应用程序,把我们生成的二进制文件转成 ASIC 需要的文件格式。
6,ASIC 拿到需要的文件后,将数据放进 ROM 里,生成 bitfile,让我们在 FPGA 上做验证,确保正确无误,如果有错误,这块 ROM 就废掉了。在 FPGA 上验证的通常是跟硬件相关的,如 IPC 通信等。要验证 MP3 解码功能不太方便,因此验证就变成了做数据内容的 check,即 ROM 地址上的数据跟我们做 ROM 时生成的反汇编相同地址上的数据完全一致。做完 FPGA 验证后,ASIC 就可以放心的把要做 ROM 的二进制数据放进 ROM 里了。
上面六步就是把软件模块固化的过程。
文章转载自:davidtym
评论