author: scruffybear
release time: 19/06/2007
如有转载,请注明出处,并保持文章的完整性,谢谢!
近段时间进行了三星的S9DG 32位ARM芯片的移植工作,总结出来一点小经验,基本上都是浅尝辄止,现在罗列出来,希望对以后的工作有所帮助。
1,制定Memory map(存储器地址分配)
程序经过编译链接后形成的二进制数据默认是按顺序存放在卡片的Flash空间中,卡片上电后到卡片的指定地址去执行这些二进制数据,也就是执行下载到卡片中的程序。在S9DG进行整个320K的flash空间分配设计的时候,在地址0x0处存放芯片初始化程序、mf头和补丁程序,从0x200开始是用户基本区,扩展区。其它所有的代码放在0x40000。现在的问题是如何将代码放到指定的Flash空间地址处,这就是制定Memory map所要做的事。
制定memory map的方法基本上有两种,一是在link时使用命令行选项,并在程序执行前利用linker pre-define symbol使用汇编语言制定section的段初始化,也就是在程序的最开始的地方用汇编语言进行段的定位。二是使用scatter file。以上两种方法依应用程序的复杂度而定,前者针对简单的情况,后者针对复杂的情况。本次S9DG ARM开发使用的是后者Scatter Load方法制定memory map。
图1对Scatter文件内容进行了详细说明,此文件中的RO,RW,ZI(其中RW和ZI未列出)分别表示readonly,read-write,zero-initialized,表示代码的属性,来源于AREA后的attr属性。比如CODE是RO,DATA是RW,NOINT默认为ZI,即用0值初始化,但是可以选择不进行0值初始化。ZI属性仅仅来源于SPACE,DCB,DCD,DCDU,DCQ,DCQU,DCW或者DCWU。由以上定义,ZI属性的包含于RW属性,它是有初始值的RW数据。又例如在C语言中,代码为RO,静态变量和全局变量是RW,ZI的。
图2表明了经过图1中的scatter文件进行空间分配后代码存放情况。
图1 Scatter File文件写法
图2 Flash空间分配
2,ARM芯片的初始化。
ARM芯片进行初始化应该包括以下内容:
中断向量表
初始化存储器系统
初始化堆栈
初始化有特殊要求的端口、设备
初始化应用程序执行环境
改变处理器模式
呼叫主应用程序
其中初始化存储器系统和初始化应用程序执行环境在前面涉及到一部分。这里主要说明中断向量表和初始化堆栈。以下是中断向量表的一个实例:
B Reset_Handler ;
Start Address ( hardwired vector = 0x0 )
B . ; Undefined_Ins_Handler
B . ; SWI_Handler
B . ; Prefetch_Abort_Handler
B . ; Data_Abort_Handler
B . ; Reserved
B IRQ_Handler ; 普通外部中断
B FIQ_Handler ; 外部快速中断
中断向量表中存放的是跳转指令,每当一个中断产生时,ARM处理器就会强制把PC指针置为中断向量表中相应中断类型的地址,执行相应的跳转指令,跳到相应的中断处理程序中。
ARM有7种运行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此程序中要对每一种模式的SP寄存器定义一个堆栈地址。方法是改变状态寄存器CPSR内的状态位,使处理器切换到不同的状态,然后给SP 赋值。以下为示例:
MRS R0 , CPSR
;
CPSR -> R0
BIC R0 , R0 , #MODEMASK ; 安全起见,屏蔽模式位以外的其它位
ORR R1 , R0 , #IRQMODE ; 把设置模式位设置成需要的模式
MSR CPSR_cxsf , R1 ; 转到IRQ 模式
LDR SP , = UndefStack ; 设置 SP_irq
ORR R1 , R0
,
#FIQMODE
MSR CPSR_cxsf , R1
;
FIQMode
LDR SP , = FIQStack
ORR R1 , R0
,
#SVCMODE
MSR CPSR_cxsf , R1
;
SVCMode
LDR SP , = SVCStack
注意上面的程序中使用到的3 个SP 寄存器是不同的物理寄存器:SP_irq,SP_fiq 和SP_svc。
3,补丁程序的设计
由于中国移动OTA3有补丁程序下载的需求,所以在移植的时候考虑了补丁程序下载的设计,具体的试验思路是:单独编译了函数形成二进制数据,然后将此二进制数据通过APDU指令写入到扩展区的6F01文件中,通过新建的指令00CA000000,跳入文件6F01中的二进制代码处执行,执行完后正常跳出.
在二进制代码中进行跳转时,要特别注意保存LR也就是R14寄存器,此寄存器是子程序返回时要调用的寄存器,如果不保存,将会导致子程序无法返回。另外,要注意ARM的运行状态,是ARM指令模式还是Thumb指令模式,如果搞错了,会导致二进制代码无法执行。两种指令模式是由BX指令执行时,Rm的位[0]的取值情况决定,如果为1,则在跳转的时自动将CPSR中的标志位T置位,即把目标地址的代码解释为Thumb代码,反之为ARM代码。
4,ARM汇编的三条伪指令介绍
AREA伪指令:
AREA伪指令用于定义一个代码段或数据段, ARM汇编程序设计采用分段式设计,一个ARM源程序至少需要一个代码段,大的程序可以包含多个代码段及数据段。
ENTRY伪指令:
ENTRY伪指令用于指定程序的入口点。一个程序中至少要有一个ENTRY,也可以有多个ENTRY,但是一个源文件中最多只有一个ENTRY。
示例:
AREA startbank0,CODE,READONLY
ENTRY
LTORG伪指令:
LTORG用于声明一个文字池(literal pool),由于LDR伪指令最大寻址围是4K,在使用LDR伪指令时,需要在适当的地址加入LTORG声明文字池,这样就会把要加载的数据保存在文字池内,再用ARM的加载指令读出数据。
注意:LTORG伪操作通常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器就不会错误的将数据缓冲池中的数据当作指令来执行。另外不要将数据缓冲区放在MF头后,否则执行800E000000指令时,会将缓冲区数据池给擦掉,造成程序无法正常跳转。
参考文献:
1, ARM Developer Suite Version1.2 Linker and Utilities Guide.
2, ARM Developer Suite Version1.2 Developer Guide