ARM——分散加载描述文件
scatter file--ARM中的RO、RW和ZI DATA说明

一直以来对于ARM体系中所描述的RO,RW和ZI数据存在似是而非的理解,这段时间对其仔细了解了一番,发现了一些规律,理解了一些以前书本上有的但是不理解的东西,我想应该有不少人也有和我同样的困惑,因此将我的一些关于RO,RW和ZI的理解写出来,希望能对大家有所帮助。
要了解RO,RW和ZI需要首先了解以下知识:ARM程序的组成此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件,这一点清注意区别。
一个ARM程序包含3部分:RO,RW和ZIRO是程序中的指令和常量RW是程序中的已初始化变量ZI是程序中的未初始化的变量由以上3点说明可以理解为:RO就是readonly,RW就是read/write,ZI就是zeroARM映像文件的组成所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。
以下用Image文件来称呼它。
Image文件包含了RO和RW数据。
之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。
包含进去反而浪费存储空间。
Q:为什么Image中必须包含RO和RW?A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。
ARM程序的执行过程从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM 程序之间并不是完全一样的。
因此就有必要了解ARM程序是如何从ROM 中的image到达实际运行状态的。
实际上,RO中的指令至少应该有这样的功能:1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。
ZI中也是变量,同理:变量不能存在ROM中在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。
ARM7的Bootloader和分散加载文件笔记

Boot Loader概述简单地说,在操作系统内核运行之前,通过一小程序,可以初始化硬件设备、建立内存空间的映射图等,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核配置好相应的环境,也可以下载文件到系统板上的SDRAM,对Flash进行擦除与编程,这个小程序一般称为Boot Loader。
可以说,一个功能完善的Boot Loader已经相当于一个微型的操作系统了。
Boot Loader作为系统复位或上电后首先运行的代码,一般应写入Flash存储器并从起始物理地址0x0开始。
Boot Loader是非常依赖于硬件而实现的,而且根据实现的功能不同,其复杂程度也各不相同。
一个简单的Boot Loader可以只完成USB口的初始化,而功能完善的Boot Loader可以支持比较复杂的命令集,对系统的软硬件资源进行合理的配置与管理。
因此,建立一个通用的Boot Loader 几乎是不可能的。
系统初始化代码直接对ARM微处理器内核及硬件控制器编程,多采用汇编语言编程,初始化代码一般应包括如下典型任务:1.定义程序入口点;2.设置异常和中断向量表;3.初始化存储设备;4.初始化堆栈指针寄存器;5.初始化用户执行环境;6.呼叫主应用程序。
1.1 定义程序入口初始化代码必须定义整个程序的入口点。
通过伪指令Entry指定编译器保留该段代码,同时配合链接器的设置,确定整个程序的入口点。
1.2 设置异常和中断向量表1.3初始化存储设备1. 存储器类型和时序的配置2.存储器的地址分配与地址重映射一种典型的存储器地址重映射过程描述如下:当系统上电或复位以后,PC指针指向0x0,程序从0x0地址开始执行,因此,为了能正确读取代码,要求此时Flash (或其它类型的ROM)的起始地址为0x0。
但Flash(或其它类型的ROM)的访问速度大大低于RAM,每次产生异常后,都要从Flash(或其它类型的ROM)的异常向量表调转到相应的处理程序,会影响异常的响应速度,因此,系统便提供一种灵活的地址重映射方法,在系统完成必要地初始化以后,将RAM安排到0x0地址处,而将原来位于0x0处的Flash(或其它类型的ROM)安排到其他的地方上去,加快异常的响应速度。
09327@52RD_使用分散加载文件

使用分散加载描述文件本章介绍如何将 ARM®链接器armlink与分散加载描述文件配合使用以创建复杂映像。
本章分为以下几节:•第5-2页的关于分散加载•第5-9页的指定区和节地址的示例•第5-31页的简单映像的等效分散加载描述使用分散加载描述文件5.1关于分散加载映像由区和输出节组成。
映像中的每个区可以包含不同的加载和执行地址。
有关详细信息,请参阅第3-2页的指定映像结构。
要构建映像的内存映射,链接器必须具有:•描述如何将输入节划分到输出节和区中的分组信息•描述区位于内存映射中的地址的位置信息通过使用分散加载机制,您可以使用文本文件中的描述为链接器指定映像的内存映射。
分散加载为您提供了对映像组件分组和位置的全面控制。
分散加载可以用于简单映像,但它通常仅用于具有复杂内存映射的映像,即多个区在加载和执行时分散在内存映射中。
5.1.1为分散加载定义的符号当链接器使用分散加载描述文件创建映像时,它会创建一些与区相关的符号。
第4-3页的与区相关的符号对这些符号进行了介绍。
仅当代码引用这些特殊符号时,链接器才会创建它们。
未定义的符号请注意,在使用分散加载描述文件时,不会定义以下符号:•Image$$RW$$Base•Image$$RW$$Limit•Image$$RO$$Base•Image$$RO$$Limit•Image$$ZI$$Base•Image$$ZI$$Limit有关详细信息,请参阅第4-3页的访问链接器定义的符号。
如果使用分散加载描述文件,但没有指定任何特殊区名称,也没有重新实现__user_initial_stackheap(),则库会生成错误消息。
有关详细信息,请参阅:•《库和浮点支持指南》中第2-67页的调整运行时内存模型•《开发指南》中第3-13页的放置堆栈和堆5-2Copyright ©2002-2008 ARM Limited. All rights reserved.ARM DUI 0206IC使用分散加载描述文件5.1.2使用分散加载描述文件指定堆栈和堆ARM C 库提供了__user_initial_stackheap()函数的多个实现,可以根据分散加载描述文件中给出的信息自动选择正确的函数实现。
Scatter file详述

一直以来对于 ARM 体系中所描述的 RO,RW 和 ZI 数据存在似是而非的理解,这段时间对其仔 细了解了一番,发现了一些规律,理解了一些以前书本上有的但是不理解的东西。我想应该有不少 人也有和我同样的困惑,因此,将我的一些关于 RO,RW 和 ZI 的理解写出来,希望能对大家有所帮 助。要了解 RO,RW 和 ZI 需要首先了解以下知识:
附: 程序的编译命令(假定 C 程序名为 tst.c): armcc -c -o tst.o tst.c armlink -noremove -elf -nodebug -info totals -info sizes -map -list aa.map -o tst.elf tst.o 编译后的信息就在 aa.map 文件中。 ROM 主要指:NAND Flash,Nor Flash RAM 主要指:PSRAM,SDRAM,SRAM,DDRAM
0 Grand Totals
==============================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
型变量“a”所引起的。 注意:如果一个变量被初始化为 0,则该变量的处理方法与未初始化华变量一样放在 ZI 区域。
即:ARM C 程序中,所有的未初始化变量都会被自动初始化为 0。
7.总结
1.C 中的指令以及常量被编译后是 RO 类型数据。 2.C 中的未被初始化或初始化为 0 的变量编译后是 ZI 类型数据。 3.C 中的被初始化成非 0 值的变量编译后是 RW 类型数据。
ARM处理器系统初始化过程

MEMMAP有两个控制位 MEMMAP[1:0]
00BOOT装载程序模式
01User FLASH模式
10用户RAM模式
11用户外部存储器模式
10模式也就是RAM模式 我们访问地址0X00是跟访问RAM地址0X40000000中的数据是完全一样的 向RAM中写进数据 然后通过数据窗口观察0X0地址的变化 应该是同步变化的
同时有两份以上的文件交错地打印在一张纸上。像不可剥夺的资源,就一定要关闭中断,让
它占有这个资源。在ARM里,没有像x86那样有清除中断指令CLI。那么在ARM里是怎么样实现
关中断和开中断的呢?下面就来看看ARM的关中断和开中断实现。
void Lock(void)
{
stmdb sp!, {r0}
mrs r0, cpsr
(3)总线宽度
ARM微处理器架构支持8/16/32位的数据总线宽度访问存储器和外设,对于特定的存储器来说,需要设定数据总线的宽度。
(4)存储器地址的配置
ARM微处理器架构理论上可以支持4GB的地址空间,而对于一个实际的系统来说,配置的物理地址远没有这么多,因此,如何配置存储器的地址,也是一个重要的问题。
因 为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给SP定义一个堆栈地址。方法是改变状 态寄存器内的状态位,使处理器切换到不同的状态,让后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就不 能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。
IMPORT main
B main
直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
ARM处理器系统初始化过程程序

ARM处理器系统初始化过程1 禁止MMU,关闭中断,禁止cache;2 根据硬件设计配制好处理器时钟、DRAM时钟、定时器时钟;3 根据系统中所用的flash和DRAM芯片容量和电气参数设置它们的起始地址、容量、刷新频率等;4 将固化在flash芯片中的程序搬移到DRAM内存中;5 使能cache,使能MMU,跳转到DRAM内存中运行继续初始化,包括根据具体应用以及系统中的硬件配置初始化各个功能模块、安装好异常中断处理程序、使能中断等;6 进行操作系统相关初始化;禁止MMU,关闭中断,禁止cache通过写系统控制协处理器的寄存器1 的第0 位可以允许和禁止MMU。
在复位后这位是0,MMU 被禁止。
关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个程序输出打印内容,这样在打印机上就会乱得不得了,同时有两份以上的文件交错地打印在一张纸上。
像不可剥夺的资源,就一定要关闭中断,让它占有这个资源。
在ARM里,没有像x86那样有清除中断指令CLI。
那么在ARM里是怎么样实现关中断和开中断的呢?下面就来看看ARM的关中断和开中断实现。
void Lock(void){stmdb sp!, {r0}mrs r0, cpsrorr r0,r0,#0xC0msr cpsr_cxsf,r0ldmia sp!,{r0}}上面这段程序是通过设置CPSR的第6,7位来实现的,因为第6,7位是设置为1时,就不再响应中断。
void UnLock(void){stmdb sp!, {r0}mrs r0, cpsrbic r0,r0,#0xC0msr cpsr_cxsf,r0ldmia sp!,{r0}}上面是重新开中断的命令,同样是设置CPSR的第6,7位,但它的值是0,就可接收中断了。
如果在多个任务之间进行共享数据,一般是需要使用关中断和开中断实现数据同步的,其实中这种关中断和开中断,就是进入临界区和退出临界区。
ARM分散加载文件

ARM分散加载文件(一)原理ARM 的连接器提供了一种分散加载机制,在连接时可以根据分散加载文件(.scf 文件)中指定的存储器分配方案,将可执行镜像文件分成指定的分区并定位于指定的存储器物理地址。
这样,当嵌入式系统在复位或重新上电时,在对CPU 相应寄存器进行初始化后,首先执行ROM 存储器的Bootloader 代码,根据连接时的存储器分配方案,将相应代码和数据由加载地址拷贝到运行地址,这样,定位在RAM 存储器的代码和数据就在RAM 存储器中运行,而不再从ROM 存储器中取数据或取指令,从而大大提高了CPU 的运行速率和效率。
(二)结构Scatlertoading 的存储区块可以分成二种类型:装载区:当系统启动或加载时应用程序的存放区。
执行区:系统启动后,应用程序进行执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行块。
(三)分散加载时连接器生成的预定义符号在编译连接时如果指定了分散加载文件(.scf 文件),在连接后会自动生成如下变量:(四)具体例子说明;ROM_LOAD 为加载区的名称,其后面的0x00000000 表示加载区的起始地址(存放程序代码的起始地址)ROM_LOAD 0x0{;ROM_EXEC 描述了执行区的地址,放在第一块位置定义ROM_EXEC 0x00000000 {;从起始地址开始放置向量表(即Startup.o(vectors, +First),其中Startup.o 为Startup.s 的目标文件) ;+First 表示Vector 段放在最前面;AREAvectors, CODE, READONLY Startup.o (vectors, +First);接着放置其。
分散加载简介

1
什么是分散加载
分散加载是指定ARM连接器在生成映像文 件时如何分配RO,RW,ZI等数据的存放地址。 如果不分散加载,ARM连接器会按照默认的方 式来生成映像,一般情况下不用编写分散加载。 但是某些场合我希望把某些数据存放在指 定的地址处,那么分散加载就发挥了非常大的作 用。
2
如何分散加载
想要实现分散加载就需要编写分散加载文件 在keil中分散加载文件时.sct . 在IAR中是.icf文件 在ads中是.scf Scatter file实际是一个具有简单语法规则的文本文件,可以用来描述ARM连接器生成映像文件时 所需要的信息。 各个加载时域的加载地址、最大尺寸和属性; 从每个加载时域中分割出的运行时域; 各个运行时域的起始地址、最大尺寸和属性; 各个运行时域存储访问特性; 各个运行时域中包含的输入段;
3
分散加载文件语法
• LR_IROM1 0x08000000 0x00002000 第一个加载域,名字为LR_IROM1,起始地址为0x0,大小为0x80000 { ER_IROM1 0x08000000 0x00002000 加载域中的运行时域,名字为ER_IROM1,起始地址为0x08000000 ,大小为0x00002000 { *.o (RESET, +First) 将编译后生成的文件.o中的代码放在第一个起始地址。 *(InRoot$$Sections) .ANY (+RO) 以及所有编译生成的RO属性的代码全部存放在
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一样咯。输入段的排放规律就是:最先排放 RO 属性的输入段,然后是 RW 属性 段,最后是 ZI 或 NOINIT 段。
域:
为什么还要加一层域,我的理解是由于代码的功能不同,那么我们有必要把
不同功能的代码分类放。我们可以把需要高速执行的代码放在一起、把对速度要
求不高的放在一起、把执行频率高的放在一起,把执行频率低的放在一起...那么 按照这种方式放的代码就可以根据其具体需要放在不同的存储器中了。这样可以
程序总有两种状态:运行态和静止态。当系统掉电的时候程序需要被保存在 非易失性的存储器中,且这个时候程序的排放是按照地址依次放的,换句话说: 我才懒得管它怎么放,只要不掉就行。当系统上电后,CPU 就要跑起来了,CPU 属于高速器件,存储器总是不怎么能跟得上,既然跟不上那么我们就尽量缩短它 们之间的差距,那留下一条路,那就是尽量提高存储器的读取速度,存储器类型 决定其速度的水平,那么尽量放在速度高的存储器就成为首选解决方案。那么我 们就把要执行的程序暂时拿到速度较快的 RAM 中。那么拿的过程就牵涉到程序 的加载了。这就是要解决的问题。
AREA RESET, CODE, READONLY AREA DSEG1, DATA, READWRITE AREA HEAP, NOINIT, READWRITE 看出其属性没? 输出段: 为了简化编译过程和更容易取得各种段的地址,那么把多个同属性的输入段 按照一定的规律组合在一起,当然这个输出段的属性就和它包含的输入段的属性
看看这个加深理解:
LOAD_ROM1 0X00000000 ; 从火车上取出来时的地址(如:成都站)
{
EXEC_ROM1 0x40000000
{ PROGRAM.O(+RO) ;把品牌 RO 的货物发给 0x400000
{ PROGRAM.O(+RW,+ZI);把品牌 RW,ZI 的货物依次发给
"Startup.s" code 32 area Vectors,CODE,READONLY entry ... end
注 1:在"Startup.o"里面会生成名为"Vectors"的段,段的 属性为"READONLY"
"Stack.s" area Stacks, DATA, NOINIT export StackUsr StackUsr SPACE 1 end
映像文件就是有 N 节车厢的火车,车厢(域)里装着要送到不同站(不同 类型的存储器)的货物。到相应的站了,那么就把相应的车厢拿下来。指挥拿这
个的就是 scatter 文件。拿下货物车厢后,我们就解开它,把里面的品牌为 RO 的 货物提取出来,按照 scatter 的指示发给某个地址,然后再先后把品牌为 RW 和 ZI 的货物发到 scatter 指定的地址。
在"Option"页里的"Image Entry Point"填入起始地址。 Scatter-Load Description File 的结构:
".scf"文件中的"+RW"对应".s"源文件中的"READWRITE"。 ".scf"文件中的"+ZI"对应".s"源文件中的"NOINIT"。 ".scf"文件中的"+RO"对应".s"源文件中的"READONLY"。 在".s"源文件中有: AREA area_name CODE/DATA,READONLY/NOINIT/READWRITEEND ".scf"的例子
片内 RAM 区,从 0x40000000 开始,最多 0x4000 字节
指定 Startup.o 中 MyStacks 放在最前面。
Startup.o(+RW,+ZI)
Startup.o 中的其他+RW/+ZI 段。注 1
os_cpu_a.o(+RW,+ZI)
}
STACKS 0x40004000\ UNINIT {
内容
注解
ROM_LOAD\ 0x80000000 {
Name of Load Region, Start Address for Load Region and Maximum size of Load Region(省略了)
ROM_EXEC 0x80000000\ 0x20000 {
Startup.o(Vector,+First)
ARM--分散加载描述文件.scf 的设置
简单应用时可以不写.scf 文件。而在"Output"页中选择"Simple",然后填写"RO Base"和"RW Base"的起始地址。在"Lay Out"页中,填写 Object/Symble:Startup.o, Section: Start,编写启动文件:Startup.s。
注 2: 在"Stack.o"里面会生成名为"Stacks"的段,段的属性 为"NOINIT",该属性对应 scf 文件中的"+ZI". 该段不需要 初始化或者可以被初始化为"0".
"Heap.s" area Heap,DATA,NOINIT export bottom_of_heap bottom_of_heap SPACE 1 end
如果只有一个汇编文件如 startup.s,也可以这样: IRAM 0x40002000 0x1000 { startup.o (Mystack,+first) *(+RW,+ZI) } 用一个"+first"强行将 startup.s 中的 Mystack 放在 0x40002000 位置。 在"Edit -> DebugRel Settings...->ARM Linker"中选中"Image map"。编译后在 Error & Warnings 窗口会 显示出详细的内存分配情况。如果在"List file name"中指定一个输出文件名,该祥单会直接存在制定 文件中以供多次研究。
*(+RO)
片外存储区,从 0x80000000 开始,最多 0x20000 字节。
Startup 模块的 Vector 段放 在最前面。注 1 其他所有模块中的所有代 码和只读的数据放在这里。
} IRAM 0x40000000\ 0x00004000 {
Startup.o(MyStacks,+first)
Stack.o(+ZI)
片内 16K RAM 的顶端,存 放不需要被"C library"初始 化的段。
注2
}
ERAM 0x80040000 {
*(+RW,+ZI)
}
HEAP +0 UNINIT {
"+0"表示接着上一段 "ERAM"的结尾,继续安排
存储区。
Heap.o(+ZI)
注3
}
}
下面是在 scf 文件中引用过的源文件示意:
一个映像文件由域(region)、输出段(output sections)和输入段(input sections) 组成。不要想得太复杂,其实他们之间就是包含与被包好的关系。具体关系是这 样的:
映像文件>域>输出段>输入段 输入段: 输入段就是我们写的代码+初始化的数据+应该被初始化为 0 的数据+没有初 始化的数据,用英文表示一下就是:RO(ReadOnly),RW (ReadWrite),ZI (ZeroInitialized),NOINIT(Not Initialized)。ARM 连接器根据各个输入段不同 的属性把相同的拿再一起组合一下就成为了输出段。 请看看平时写的东东:
Region 的"UNINIT"之类的参数要放在"Maximum size"参数之前。 在一个 Region 中,RAM 的分配不是按照罗列的顺序来的。要想让汇编中使用的变量有固定的位置, 可以把所有汇编文件产生的".o"放在同一个 Region 中。如: IRAM1 0x40000000 { startup.o(+RW,+ZI) ASMSourceCode1.o(+RW,+ZI) ASMSourceCode2.o(+RW,+ZI) } IRAM2 +0 { CSourceCode1.o(+RW,+ZI) CSourceCode2.o(+RW,+ZI) } 这样,所有汇编中定义的变量地址就相对集中了。
提高程序执行速度。一个域中包含 1~3 个输出段。 映像文件:
我暂时把映像文件理解成烧到存储器中的文件,由 N 个域组成。这些域其 实可以看做是独立的模块,只是他们按照一定的顺序(这个顺序还是:
RO+RW+ZI)被捆绑在一起,这样才方便烧写到非易失存储器中去。 好了,了解了映像文件的组成,那么来看看映像文件是怎么跑起来的。
注 3: "Heap.o"里面名为"Heap"的段。
在 Scatter 文件中最好每一个 Region 都加一个 Maximum 参数,这样当编译时如果实际使用的空 间大于 Maximum Size,会有 Error:16220E: Excution region xxx size (xxx bytes) exceeds limit (xx bytes)。 如果地址有重复,会有 Error: 16221E: Excution region xxx overlaps with excution region xxx。前一个 Region 的首地址 + Maximum > 后一个 Region 的首地址时不一定有 Error。只有当一分配的内存出 现覆盖时才会有 Error。