下面是一款bootloader的scatter文件,scatter的解析可以参照上一个例子。
LOAD_ROM 0x10000000 0x002000
{
EXEC_ROM 0x10000000 0x002000
{
init.o (init, +First) #可以看到,我们在这里没有vectors.o,因为不需要
* (+RO)
}
RAM 0x30080000 0x7000
{
* (+RW,+ZI)
}
STACKS 0x30088000 0x1000
{
stack.o (+ZI)
}
}
这是相应的init.s的内容:
ENTRY
; --- Perform ROM/RAM remapping, if required
IF :DEF: ROM_RAM_REMAP
; On reset, an aliased copy of ROM is at 0x0.
; Continue execution from 'real' ROM rather than aliased copy
LDR pc, =Instruct_2
Instruct_2
LDR r1, =Remap_ctl_reg
LDR r0, =Remap_value
STR r0, [r1]
ENDIF
Reset_Handler
IMPORT top_of_stacks ; defined in stack.s and located by scatter file
LDR r0, =top_of_stacks
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit ; No interrupts
SUB sp, r0, #Offset_SVC_Stack
IMPORT load_firmware
B load_firmware ;
END
我们可以看出来,基本上runtime的启动文件基本相同,只是最后跳转的地方不一样。但是,这只是假象,由于scatter文件的不同,两个工程生成的可执行文件有着根本的不同,runtime那个在0地址储存的是向量表,供cpu在发生异常时取用,而bootloader的在0地址直接就是顺序执行的初始化语句,因为bootloader只关心一件事,把runtime image以某种方式加载进来,如果有需要的话还要解压,当这些做完之后再把控制权交给runtime image中的可执行域就行了,至于以后的异常处理等等问题不在它考虑范围内。
还有一个不同是我们用了B load_firmware,而不是B __main,这样做的一个好处是链接时就不会把ARM的库函数包进来,能节省1K左右的ROM空间,而坏处就是你要仔细对待你的代码,因为缺少了一个初始化函数,所以内存中的值就没有人给你赋值,也就是说你的代码中不允许有带初值的全局变量,这一点切记,很多人在这上面吃亏。 |