CEVA lsf
Table of Contents
1. CEVA lsf
lsf 即 linker script file
1.1. lsf 语法
参考 CEVA-Toolbox_Binary_Tools_Reference_Guide.pdf, `5. Linker Description`
简单的说:
- 定义 class
- 把 section 放在 class 中
- 确定 section 在相应 class 中的位置
lo
从 class 的低地址开始, 向上寻找一个可用的地址
hi
从 class 的高地址开始, 向下寻找一个可用的地址
- at
- align
next
使用上一个 section 的结尾地址, 当没有指定时默认即是 next
smallest
相当于把 first-fit 变成 best-fit
- size
- 一些特殊的属性
clone
相当于把 section 在两个不同的 class 分别放了一次
noload
只分配空间, 不加载数据, 例如 bss section.
symbol
定义一个新的 symbol, 应用代码可以使用这些 symbol
section 的各种属性没有先后和从属关系, 例如
`section_a lo align 0x2 clone secition_b smallest`
表示:
- lo
- align 0x2
- clone section_b
- smallest
1.2. clone
`ZS302X 方案 DSP 开发指南` 提到:
`在有些实现中,在搬移初始化数据到 DTCM 中时,M4F 没有直接搬移数据到 DTCM 中去而是先搬到 dsp exernal ram 中,dsp 运行起来之后再由 dsp 自己将数据从 exernal ram 搬移到DTCM 中。`
例如 sample 中的 lsf 写的这段:
__DTCM_DATA_BEGIN next symbol .data next noload const_data next noload data_dtcm next noload EXT_aec_table_DATA next noload __DTCM_DATA_END next symbol
.data 等指定了 `noload`, 表示它并没有被直接加载到 DTCM. data_oninit 中的数据是 DTCM 的 clone, 需要用户代码自己把 data_oninit 中的数据复制到 DTCM 中:
data_oninit: __DTCM_DATA_CLONE_BEGIN symbol data_clone lo clone .data const_data_clone lo clone const_data data_dtcm_clone next clone data_dtcm EXT_aec_table_DATA_clone next clone EXT_aec_table_DATA __DTCM_DATA_CLONE_END next symbol
其中负责 copy 的代码是:
_GENERIC_CODE_SECTION(bankswitch_code_dtcm_data) void bank_switch_dtcm_data(void) { extern char _DTCM_BSS_BEGIN[]; extern char _DTCM_BSS_END[]; extern char _DTCM_DATA_BEGIN[]; extern char _DTCM_DATA_END[]; extern char _DTCM_DATA_CLONE_BEGIN[]; memset(_DTCM_BSS_BEGIN, 0, _DTCM_BSS_END - _DTCM_BSS_BEGIN); memcpy( _DTCM_DATA_BEGIN, _DTCM_DATA_CLONE_BEGIN, _DTCM_DATA_END - _DTCM_DATA_BEGIN); }
这个 bankswitch_code_dtcm_data 在每次 aec_init 时都会被调用
为什么 M4F 需要把 data 写到 external ram, 然后再由 dsp 自己复制到 DTCM?
从 M4F 的角度, DTCM 是共用的: 通话, 播放音乐, aec 等都会用到. 当切换到 aec 时,需要 aec_init 自己去加载数据. 而原始数据所在的 data_oninit (如后面描述) 是通过 page fault 按需加载的, 由此可以实现多个应用的按需加载.
另外, PTCM 不是多个程序公用的, 所以 PTCM 不需要通过 ext ram 中转
1.3. xxx_oninit 与 xxx_cram
`ZS302X 方案 DSP 开发指南` 提到:
code_oninit 和 data_oninit ... 与 code_cram/data_cram 指向的是同一片物理内存,因此与 code_cram/data_cram 定义的 函数和内存不能相互引用,否则运行会出错
所谓`指向同一片物理内存` 是指 `25403C80` 与 `25443C80` 对应同样的物理地址
但 code_oninit/data_oninit 和 code_cram/data_cram 在加载时并不会互相覆盖 例如:
code_oninit: EXT_code_init bankswitch_code_dtcm_data code_cram: .text prog_data prom_data CODE code_external
因为 xxx_oninit 和 xxx_cram 是通过 page fault 来隔离的.
- 初始时 M4F 并不会加载 xxx_oninit 或 xxx_cram 的内容.
- 当 code_oninit 中的代码被调用时, 会触发 page fault, 导致 code_oninit 和 data_oninit 的数据被加载进来.
- 当 code_crame 中的代码被调用时, 会再次触发 page fault, 导致 code_cram 和 data_cram 的数据被加载进来.
- 每次加载都会覆盖掉之前的内容 (除非新加载的 section 是 noload)
这种设计主要是为了程序能复用那些只在 `初始化` 时用到的空间. 但也会引入一些问题:
- 由于 xxx_oninit 和 xxx_cram 是通过 code_xxx 触发 page fault 来加载, 所以如果程序所有的代码都在 PTCM, 则无法加载 ext mem
- code_init 和 code_cram 反复互相调用会导致 ext mem 反复被加载和覆盖
有些功能无法实现
例如, 希望初始化代码在 data_cram 中初始化一个 heap, 以便后续初始化代码可以利用这个 heap, 但此时代码在 code_init 中执行, data_cram 并没有加载进来. 如果把这段初始化 heap 的代码放在 code_cram, 又会导致所有初始化代码都需要被放在 code_cram, 因为它们依赖于 heap 初始化…