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`

简单的说:

  1. 定义 class
  2. 把 section 放在 class 中
  3. 确定 section 在相应 class 中的位置
    • lo

      从 class 的低地址开始, 向上寻找一个可用的地址

    • hi

      从 class 的高地址开始, 向下寻找一个可用的地址

    • at
    • align
    • next

      使用上一个 section 的结尾地址, 当没有指定时默认即是 next

    • smallest

      相当于把 first-fit 变成 best-fit

    • size
  4. 一些特殊的属性
    • clone

      相当于把 section 在两个不同的 class 分别放了一次

    • noload

      只分配空间, 不加载数据, 例如 bss section.

    • symbol

      定义一个新的 symbol, 应用代码可以使用这些 symbol

  5. 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 来隔离的.

  1. 初始时 M4F 并不会加载 xxx_oninit 或 xxx_cram 的内容.
  2. 当 code_oninit 中的代码被调用时, 会触发 page fault, 导致 code_oninit 和 data_oninit 的数据被加载进来.
  3. 当 code_crame 中的代码被调用时, 会再次触发 page fault, 导致 code_cram 和 data_cram 的数据被加载进来.
  4. 每次加载都会覆盖掉之前的内容 (除非新加载的 section 是 noload)

这种设计主要是为了程序能复用那些只在 `初始化` 时用到的空间. 但也会引入一些问题:

  1. 由于 xxx_oninit 和 xxx_cram 是通过 code_xxx 触发 page fault 来加载, 所以如果程序所有的代码都在 PTCM, 则无法加载 ext mem
  2. code_init 和 code_cram 反复互相调用会导致 ext mem 反复被加载和覆盖
  3. 有些功能无法实现

    例如, 希望初始化代码在 data_cram 中初始化一个 heap, 以便后续初始化代码可以利用这个 heap, 但此时代码在 code_init 中执行, data_cram 并没有加载进来. 如果把这段初始化 heap 的代码放在 code_cram, 又会导致所有初始化代码都需要被放在 code_cram, 因为它们依赖于 heap 初始化…

Author: [email protected]
Date: 2022-09-16 Fri 16:16
Last updated: 2022-11-30 Wed 18:24

知识共享许可协议