ROM Lib
Background
首先来解决ROM Lib是啥?为什么要使用它?来看AI的解答:
A “ROM library” refers to a collection of functions or code stored directly in a device’s Read-Only Memory (ROM), allowing applications to access these functions without needing to use additional RAM space, which is particularly useful in embedded systems with limited memory availability; essentially, it’s a library of code that is permanently built into the hardware and cannot be easily changed.
上述解释了什么是Rom Lib,总结下就是:
- 一些函数和代码存在ROM里
- 允许CPU直接从ROM上取指执行
- 这部分代码不能修改
ROM的access速度会比RAM(DRAM,SRAM)慢很多,ROM code使用ROM是利用它的不可更改性来保证RoT,那Rom Lib为什么也用ROM呢?前面简单写了PPAC,这里就是考虑到A(area)和C(cost),虽然慢,但便宜啊。所以,它一般用来节省使用sram的cost。
当然使用ROM Lib也有它的局限性。
- Rom Lib会merge进netlist,一旦tape out就不能改变
- 一般只会集成一些common的function
- 由于速度的原因,一般只会用在boot阶段,或者MCU的应用里
Implementation
前文讲了为什么要用Rom Lib,那如何实现呢?ATF里就提供了一套完整的方法。
Principle Of Work
ATF的这套实现的输出最终会包含三个部分:
- static library //包含真正的functions
- jump table //其实就是把所有functions的jump address集中在了一起,可以叫做函数集散地
- function wrapper //包装了一下,直接区jump table相应的offset找对应的函数
其中static library和jump table合在一起输出为真正的ROM Lib(jump table放在头上),同时输出一个libwrapper.a供使用者调用ROM Lib里的functions。
Note: 需要调用ROM Lib的application要把libwrapper.a link进去。
整个调用路径为Application -> Wrapper function -> Jump table entry -> Function in ROM Lib。
How To Add Functions
ATF提供了全套的实现,其中有code template,有build script,还有python工具等。本文通过描述如何添加functions到ROM Lib里来介绍它们的作用。
Generate A Library
首先可以分门别类把需要加的function生成static library。步骤可以看How to Make a Library,另外cmake用起来也特别简单,准备另外写一篇怎么用cmake生成library。
Modify Makefile
修改ATF的Makefile加入上一步生成的library。比如ATF自己的ROM Lib有以下几个lib:
1
2
LIBS = -lmbedtls -lfdt -lc
LIBS += -ltest //增加自己的lib,可以把ATF原有的去掉
Add Function to Jump Table
修改ATF的jmptbl.i文件,增加新增ROM Lib Function的入口。
1
2
3
rom rom_lib_init
.......
test test_function_name
这步里用到jmptbl_entry_function.S,当然根据选项不同还有其他几个类似的文件。它会和jmptbl_header.S一起生成jump table的汇编源文件。
1
2
3
4
5
6
7
8
9
10
11
/*
* Copyright (c) 2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
.text
.globl jmptbl
jmptbl:
b rom_lib_init
......
b test_function_name
这个生成的过程由python脚本romlib_generator.py完成。Makefile中的调用如下:
1
2
3
$(BUILD_DIR)/jmptbl.s: $(BUILD_DIR)/jmptbl.i | $$(@D)/
$(s)echo " TBL $@"
$(q)$(ROMLIB_GEN) gentbl --output $@ --bti=$(ENABLE_BTI) $<
Generate Link File
这步的生成过程在Generate Link Script File From ld.S里已经讲过。ATF全部包好了,只要定义一些变量,不需要特别改动。所需定义变量如下:
- PLATFORM_LINKER_FORMAT and PLATFORM_LINKER_ARCH
- ROMLIB_RO_BASE and ROMLIB_RO_LIMIT
- ROMLIB_RW_BASE and ROMLIB_RW_END
其中为什么会存在有RW属性的region定义?ROM Lib也是可以有自己的data段和bss段的,具体实现可以看AT - Section Attribute of GNU Linker Scripts。
BTW:其实是不建议ROM Lib里有自己的data和bss,会增加额外处理(这个后面说)和需要对sram的layout分布多加考虑(data和bss占用的sram一旦tape out就不可改变,需要预留出来)。怎么避免?不定义本地全局和static变量,使用参数传递进来的变量和buffer。
link file生成后就可以把所有要加的lib link为ROM Lib了。
Output libwrapper.a
这里也有几个步骤:
- 生成jmpvar.s
这里面其实就定义了一个变量jmptbl,是jump table的地址,wrapper function里会更具这个地址加上offset调用至实际函数。模板为jmptbl_glob_var.S。1 2 3 4 5 6 7 8 9
/* * Copyright (c) 2019, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ .data .globl jmptbl .align 4 jmptbl: .quad 0x${jmptbl_address}
Makefile里同样是由romlib_generator.py完成。
1
2
3
$(WRAPPER_DIR)/jmpvar.s: $(BUILD_DIR)/romlib.elf | $$(@D)/
$(s)echo " VAR $@"
$(q)$(ROMLIB_GEN) genvar --output $@ $<
- 生成wrappers.s
1 2 3
$(WRAPPER_SOURCES) $&: $(BUILD_DIR)/jmptbl.i | $$(@D)/ $(s)echo " WRP $<" $(q)$(ROMLIB_GEN) genwrappers --bti=$(ENABLE_BTI) -b $(WRAPPER_DIR) $<
python脚本通过解析之前编辑的jmptbl.i和利用模板wrapper.S生成wrappers.s。模板如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
/* * Copyright (c) 2019, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ .section .text.__wrap_${function_name} .globl __wrap_${function_name} __wrap_${function_name}: ldr x17, =jmptbl mov x16, #${function_offset} ldr x17, [x17] add x16, x16, x17 br x16
romlib_generator.py会为每个列在jmptbl.i中的函数生成上述模板中的汇编函数。其中function_name会被替代为function name of rom lib,function_offset是该函数在jump table中的offset。
最后编译生成libwrapper.a就完成了。
How to Use/Test
做Test的时候,可以先把ROM Lib放在sram里测试,function测试通过,然后再去到FPGA和仿真平台上跑。
- 把ROM Lib按定义的ROMLIB_RO_BASE and ROMLIB_RO_LIMIT地址load好。
- 编译测试程序(link libwrapper.a)。
- 调用rom_lib_init把data段copy到ROMLIB_RW_BASE and ROMLIB_RW_END范围内并把bss段清掉。这就是前面讲的多了一步操作。具体代码在init.s中。
- 现在就可以调用其他的ROM Lib function了。
Application
博主利用ATF提供的这套机制为Cortex-M系列芯片制作了ROM Lib,当然有不少东西要改,比如说init.s和templates下的汇编文件,都需要改为Thumb2指令的汇编。
Other Solution
ATF这套方案已经比较完备了,但有点啰嗦,需要到“函数集散地”去转一圈才能调用到真正的函数,其实可以直接jump至函数入口地址。dump出ROM Lib中所有函数入口地址的命令如下:
1
nm romlib.elf
接下来就需要写个脚本做一层wrapper。完工!