Post

ROM Lib

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,总结下就是:

  1. 一些函数和代码存在ROM里
  2. 允许CPU直接从ROM上取指执行
  3. 这部分代码不能修改

  ROM的access速度会比RAM(DRAM,SRAM)慢很多,ROM code使用ROM是利用它的不可更改性来保证RoT,那Rom Lib为什么也用ROM呢?前面简单写了PPAC,这里就是考虑到A(area)和C(cost),虽然慢,但便宜啊。所以,它一般用来节省使用sram的cost。

  当然使用ROM Lib也有它的局限性。

  1. Rom Lib会merge进netlist,一旦tape out就不能改变
  2. 一般只会集成一些common的function
  3. 由于速度的原因,一般只会用在boot阶段,或者MCU的应用里

Implementation

  前文讲了为什么要用Rom Lib,那如何实现呢?ATF里就提供了一套完整的方法。

Principle Of Work

  ATF的这套实现的输出最终会包含三个部分:

  1. static library //包含真正的functions
  2. jump table //其实就是把所有functions的jump address集中在了一起,可以叫做函数集散地
  3. function wrapper //包装了一下,直接区jump table相应的offset找对应的函数

  其中static library和jump table合在一起输出为真正的ROM Lib(jump table放在头上),同时输出一个libwrapper.a供使用者调用ROM Lib里的functions。

  看官网的图更容易理解。
romlib_design
romlib_wrapper

  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 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

  这里也有几个步骤:

  1. 生成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 $@ $<
  1. 生成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和仿真平台上跑。

  1. 把ROM Lib按定义的ROMLIB_RO_BASE and ROMLIB_RO_LIMIT地址load好。
  2. 编译测试程序(link libwrapper.a)。
  3. 调用rom_lib_init把data段copy到ROMLIB_RW_BASE and ROMLIB_RW_END范围内并把bss段清掉。这就是前面讲的多了一步操作。具体代码在init.s中。
  4. 现在就可以调用其他的ROM Lib function了。

Application

  博主利用ATF提供的这套机制为Cortex-M系列芯片制作了ROM Lib,当然有不少东西要改,比如说init.stemplates下的汇编文件,都需要改为Thumb2指令的汇编。

Other Solution

  ATF这套方案已经比较完备了,但有点啰嗦,需要到“函数集散地”去转一圈才能调用到真正的函数,其实可以直接jump至函数入口地址。dump出ROM Lib中所有函数入口地址的命令如下:

1
nm romlib.elf

  接下来就需要写个脚本做一层wrapper。完工!

Reference

Library at ROM
Scripts and Templates

This post is licensed under CC BY 4.0 by the author.