Keil的分散加载机制,让我们可以用一个文本描述文件,来指定内存分配(Memory map)方式,链接器(Linker)根据它来生成程序映像(image)。
1.1 分散加载概述
分散加载(Scatter-loading)让我们能够完整的控制image,按照指定的分组和位置生成。
你可以使用分散加载来创建一个简单的image,但是一般建议用来创建复杂的Memory map,这样在Memory Map中的多个存储区域在加载和运行时将会按照指定位置分布。
创建image的Memory map 由区域和输出段组成,每个区域可以拥有不同的加载和执行地址。
链接器(Linker)为了构建生成特定Image的Memory map,需要知道:
1,描述输入段如何组成输出段和代码区域的分组信息;
2,描述在memory map中特定代码区域位置的分配信息。
当链接器使用分散加载描述文件(scatter file)创建Image时,它会创建一些代码区域相关的符号表,并且只有你的代码引用了这些符号表相关区域的代码时,它才会创建这些符号表。
1.2 什么时候使用分散加载
分散加载通常是实现嵌入式系统所必需的,因为这些系统使用ROM、RAM和内存映射外围设备。
需要或非常有用的分散负载情况:
1 需要复杂的内存映射Memory Maps
必须放在许多不同内存区域中的代码和数据的时候,需要详细的说明清楚在内存空间的什么地方放置什么分段。
2 使用不同类型的物理存储器
许多系统包含各种物理内存设备,如闪存、ROM、SDRAM和fast SRAM。分散加载描述可以用最合适的内存类型匹配代码和数据,例如,可以将中断代码放入快速SRAM中以提高中断响应时间,而将不常用的配置信息放入较慢的闪存中。
3 需要将内存映射到外设
分散加载描述可以将数据段放在内存映射中的精确地址,以便可以访问内存映射的外围设备。
4 需要将函数放置在固定位置
函数可以放在内存中的相同位置,即使周围的应用程序已经被修改和重新编译。这对于实现跳转表非常有用。
5 需要使用符号来识别堆(heap)和栈(stack)
在链接应用程序时,可以为堆和栈位置定义符号.
1.3 链接器(Linker)预定义的符号
链接器(Linker)预定义的符号不能够在分散加载文件中重新定义使用。如下为接器(Linker)预定义的符号:
Image$$RO$$Base.
Image$$RO$$Limit.
Image$$RW$$Base.
Image$$RW$$Limit.
Image$$XO$$Base.
Image$$XO$$Limit.
Image$$ZI$$Base.
Image$$ZI$$Limit.
如果您使用了一个分散文件,但是没有为堆栈和堆使用特殊的区域名,或者没有重新实现_user_setup_stackheap(),则会生成一条错误消息。
1.4 为栈(stack)和堆(heap)空间指定分散加载文件
ARM的c库为函数__user_setup_stackheap()提供了多种实现,能够根据分散加载文件自动选择正确的实现。
为了指定这两个区域的内存分配,你需要在分散加载文件中定义这两个执行区域:ARM_LIB_HEAP 和 ARM_LIB_STACK,给它们都指定 EMPTY 的属性,这样C库就不会使用默认的实现,而使用如下的符号来实现__user_setup_stackheap()功能:
Image$$ARM_LIB_STACK$$ZI$$Base.
Image$$ARM_LIB_STACK$$ZI$$Limit.
Image$$ARM_LIB_HEAP$$ZI$$Base.
Image$$ARM_LIB_HEAP$$ZI$$Limit
若只有ARM_LIB_HEAP 或 ARM_LIB_STACK 中的一个被指定,那么你必须指定大小,例如:
你也可以使用带有 EMPTY 属性的符号 ARM_LIB_STACKHEAP 来合并指定栈和堆的分配区域,这会导致 __user_setup_stackheap() 使用如下符号的值来定义:Image$$ARM_LIB_STACKHEAP$$ZI$$Base 和 Image$$ARM_LIB_STACKHEAP$$ZI$$Limit 。
注意:如果你重新实现了库函数 _user_setup_stackheap(), 这将会覆盖所有的其他的自动实现方式。
1.5 分散加载的可选命令参数
命令参数可以全局的控制链接器(Linker)分配数据和代码区域的行为,但是对区域位置完全控制需要比命令参数更多更详细的指令(使用分散加载文件)。
1 复杂的memory map
复杂的memory map必须使用分散加载文件实现,你可以使用这个参数来指定分散加载文件:
--scatter=scatter_file
链接器将按照scatter_file的描述来生成Image。
2 简单的memory map
对于简单的memory map实现,你可以使用如下相关的命令参数:
--partial.创建可在后续链接步骤中使用的部分链接对象。
--ro_base.
--rw_base.
--ropi.
--rwpi.
--rosplit.
--split.
--reloc.创建具有连续执行区域的单个可重定位加载区域。
--xo_base.
--zi_base.
1.6 简单memory map的分散加载image示例
对于具有简单memory map的Image,您可以仅使用链接器命令行选项或使用分散加载文件指定内存映射。下面的图片展示了一个简答的 Memory map:
下面的示例展示了相应的分散加载描述文件,来指引object文件中的代码段分配到memory中:
请注意不要超越芯片各种类型内存映射的最大可用区域。
1.7 复杂memory map的分散加载image示例
对于复杂memory map的Image,您可以不可能仅使用链接器命令行选项指定内存映射,这种情况下你必须使用分散加载文件。下面的图片展示了一个复杂Memory map的示例:
下面的示例展示了相应的分散加载描述文件,来指引object文件 program1.o 和 program2.o 中的代码段分配到memory中:
注意:本例中的分散加载描述仅为 program1.o 和 program2.o 指定了代码和数据的位置。如果您链接一个额外的模块,例如 program3.o 。但使用这个分散加载文件时,并没有指定 program3.o 的代码和数据的具体位置。除非您希望对代码和数据的放置非常严格,否则ARM建议您使用 * 或 .any 的说明符来放置剩余的代码和数据。