Scatter-Load Description File的结构:
".scf"文件中的"+RW"对应".s"源文件中的"READWRITE".
".scf"文件中的"+ZI"对应".s"源文件中的"NOINIT".
".scf"文件中的"+RO"对应".s"源文件中的"READONLY".
在".s"源文件中有:
AREA area_name CODE/DATA,READONLY/NOINIT/READWRITE
END
".scf"的例子
内容 | 注解 | |
ROM_LOAD 0x80000000 { |
Name of Load Region, Start Address for Load Region and Maximum size of Load Region(省略了) | |
ROM_EXEC 0x80000000 0x20000 { |
片外存储区,从0x80000000开始,最多0x20000字节。 | |
Startup.o(Vector,+First) | Startup模块的Vector段放在最前面。注1 | |
*(+RO) | 其他所有模块中的所有代码和只读的数据放在这里。 | |
} | ||
IRAM 0x40000000 0x00004000 { |
片内RAM区,从0x40000000开始,最多0x4000字节 | |
Startup.o(MyStacks,+first) | 指定Startup.o中MyStacks放在最前面。 | |
Startup.o(+RW,+ZI) | Startup.o中的其他+RW/+ZI段。注1 | |
os_cpu_a.o(+RW,+ZI) | ||
} | ||
STACKS 0x40004000 UNINIT { |
片内16K RAM的顶端,存放不需要被"C library"初始化的段。 | |
Stack.o(+ZI) | 注2 | |
} | ||
ERAM 0x80040000 { |
||
*(+RW,+ZI) | ||
} | ||
HEAP +0 UNINIT { |
"+0"表示接着上一段"ERAM"的结尾,继续安排存储区。 | |
Heap.o(+ZI) | 注3 | |
} | ||
} |
下面是在scf文件中引用过的源文件示意:
"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 |
注2: 在"Stack.o"里面会生成名为"Stacks"的段,段的属性为"NOINIT",该属性对应scf文件中的"+ZI".该段不需要初始化或者可以被初始化为"0". |
"Heap.s" area Heap,DATA,NOINIT export bottom_of_heap bottom_of_heap SPACE 1 end |
注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。
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)
}
这样,所有汇编中定义的变量地址就相对集中了。
如果只有一个汇编文件如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"中指定一个输出文件名,该祥单会直接存在制定文件中以供多次研究。
C语言程序通常需要一段用于初始化的汇编代码,通常存储为"Startup.s",它实现的任务通常是:
1、做好中断向量表
2、初始化外部总线控制器/堆栈/目标板基本模块。
3、给库函数使用的"__user_inital_stackheap"。
4、除数为零时的死循环"__rt_div0: B __rt_div0"。
5、支持加密功能的语句。
6、定义堆栈空间:AREA MyStacks, DATA,NOINIT,ALIGN = 2
要定义栈的起始地址:IrqStackSpace SPACE ...
和栈的头部:StackIrq DCD IrqStackSpace + Length*4。因为栈是向下生长的。
与目标板有关的初始化程序可以放在一个名为"Target.c"的文件里。
1、定义中断处理入口:void IRQ_Exception(void), void FIQ_Exception(void), void Timer0_Exception(void)。
2、向量中断控制器初始化。
3、remap,系统时钟,实时时钟,存储器加速。
关于C编译器使用的堆栈设置:
1、在Startup.s中有一句:
MSR CPSR_C,#0x5f //系统模式
LDR SP, =UsrStack //用户栈
2、在Scatter文件中,有
STACKS 0x40004000 UNINT
{
UsrStack.o(+ZI)
}
3、在UsrStack.s中有
AREA Stacks, DATA, NOINT
EXPORT UsrStack
UsrStack SPACE 1
END
定义一个UsrStack,大小都无所谓,把它放在可用物理内存的最顶端。C编译器在编译子程序调用时,会将要保护的寄存器压栈,如:
stmfd r13!,{r3-r7,r14}
其中,r13的别名是SP。
这是一个满递减堆栈。即SP指向的单元内的数据是有效的,入栈时先减SP再存数据。