最近在研究HP 39gs的裸跑问题,之前一直用的Keil RealView MDK,然而由于个人喜好问题,想把工程移植到IAR Embedded Workbench上。网上2410/2440+IAR裸跑的先例不多,质量参差不齐,于是我就参考MDK的官方2410启动文件以及IAR的官方某ARM7启动文件重新写了一份,拿出来和大家分享以下,也算是加深一下对ARM启动过程的认识。
在解决启动代码问题之前,首先要写一份icf文件,这个是IAR独有的一种配置文件,MDK下是直接在工程选项里面设置的。这个ICF一般不用关注太多内容,按照以下模板修改即可。
/*###ICF### Section handled by ICF editor, don't touch! ****/ /*-Editor annotation file-*/ /* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */ /*-Specials-*/ define symbol __ICFEDIT_intvec_start__ = 0x00000000; /*-Memory Regions-*/ define symbol __ICFEDIT_region_ROM_start__ = 0x00000000; define symbol __ICFEDIT_region_ROM_end__ = 0x0007FFFF; define symbol __ICFEDIT_region_RAM_start__ = 0x08000000; define symbol __ICFEDIT_region_RAM_end__ = 0x08037FFF; /*-Sizes-*/ define symbol __ICFEDIT_size_cstack__ = 0x2000; define symbol __ICFEDIT_size_svcstack__ = 0x100; define symbol __ICFEDIT_size_irqstack__ = 0x100; define symbol __ICFEDIT_size_fiqstack__ = 0x100; define symbol __ICFEDIT_size_undstack__ = 0x100; define symbol __ICFEDIT_size_abtstack__ = 0x100; define symbol __ICFEDIT_size_heap__ = 0x8000; /**** End of ICF editor section. ###ICF###*/ define memory mem with size = 4G; define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { }; define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { }; define block FIQ_STACK with alignment = 8, size = __ICFEDIT_size_fiqstack__ { }; define block UND_STACK with alignment = 8, size = __ICFEDIT_size_undstack__ { }; define block ABT_STACK with alignment = 8, size = __ICFEDIT_size_abtstack__ { }; define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; initialize by copy { readwrite }; do not initialize { section .noinit }; place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; place in ROM_region { readonly }; place in RAM_region { readwrite, block CSTACK, block SVC_STACK, block IRQ_STACK, block FIQ_STACK, block UND_STACK, block ABT_STACK, block HEAP };
需要修改的就是RAM、ROM的起始地址,注意比如这里ROM的容量为0x80000,那么ROM的终止地址应该为0x7FFFF而不是0x80000!(网上下载到的某开发板例程里面的ICF文件就犯了这个错误)另外如果自己有单独安排RAM的使用,比如LCD的Framebuffer、MMU的TranslationTable之类的,那么已经分配掉的部分不应该被写入这个范围。(某ICF又犯了错误)。然后上面的stack只要自己设置尺寸即可,不需要设定地址,linker会自动分配。
随后就可以开始写启动文件。过程就不赘述,直接放出完成的代码
;****************************************************************************** ; S3C2410A 启动文件 by ZephRay 2016/1/25 ;****************************************************************************** MODULE ?cstartup #include "config.inc" ;定义之后会用到的SECTION,注意名字是和ICF对应的 SECTION IRQ_STACK:DATA:NOROOT(3) SECTION ABT_STACK:DATA:NOROOT(3) SECTION SVC_STACK:DATA:NOROOT(3) SECTION UND_STACK:DATA:NOROOT(3) SECTION FIQ_STACK:DATA:NOROOT(3) SECTION CSTACK:DATA:NOROOT(3) ;定义section .intvec,参考ICF会把.intvec的Section放在0地址 SECTION .intvec:CODE:NOROOT(2) PUBLIC __vector PUBLIC __iar_program_start ARM ;中断向量表,必须使用绝对地址,每个向量占用4个字节 ;没有用到的向量留了空,可以补上实现 __vector: B Reset_Handler ;复位向量,开机首先跳转执行复位向量 B . ;未定义指令(Undefined instruction) B . ;软件中断(SWI) B . ;预读取失败(Prefetch abort) B . ;数据失败(Data abort) B . ;保留(Reserved) B IRQ_Handler ;IRQ中断 B . ;FIQ中断 ;定义section .text SECTION .text:CODE:NOROOT(2) EXTERN ?main REQUIRE __vector ARM ;IRQ中断处理,读取当前中断源,然后查位于HandleEINT0开始的中断向量表然后执行ISR IRQ_Handler SUB sp,sp,#4 ;reserved for PC STMFD sp!,{r8-r9} LDR r9,=INTOFFSET LDR r9,[r9] LDR r8,=HandleEINT0 ADD r8,r8,r9,lsl #2 LDR r8,[r8] STR r8,[sp,#8] LDMFD sp!,{r8-r9,pc} ;按照设置初始化内存控制器 IF MC_SETUP <> 0 MC_CFG DCD BWSCON_Val DCD BANKCON0_Val DCD BANKCON1_Val DCD BANKCON2_Val DCD BANKCON3_Val DCD BANKCON4_Val DCD BANKCON5_Val DCD BANKCON6_Val DCD BANKCON7_Val DCD REFRESH_Val DCD BANKSIZE_Val DCD MRSRB6_Val DCD MRSRB7_Val ENDIF ;初始化时钟控制器 IF CLK_SETUP <> 0 CLK_CFG DCD LOCKTIME_Val DCD CLKDIVN_Val DCD MPLLCON_Val DCD UPLLCON_Val DCD CLKSLOW_Val DCD CLKCON_Val ENDIF ;复位向量,真正的程序从这里开始执行 __iar_program_start: ?cstartup: EXPORT Reset_Handler Reset_Handler IF WT_SETUP <> 0 LDR R0, =WT_BASE LDR R1, =WTCON_Val LDR R2, =WTDAT_Val STR R2, [R0, #WTCNT_OFS] STR R2, [R0, #WTDAT_OFS] STR R1, [R0, #WTCON_OFS] ENDIF IF CLK_SETUP <> 0 LDR R0, =CLK_BASE ADR R8, CLK_CFG LDMIA R8, {R1-R6} STR R1, [R0, #LOCKTIME_OFS] STR R2, [R0, #CLKDIVN_OFS] STR R3, [R0, #MPLLCON_OFS] STR R4, [R0, #UPLLCON_OFS] STR R5, [R0, #CLKSLOW_OFS] STR R6, [R0, #CLKCON_OFS] ENDIF IF MC_SETUP <> 0 ADR R14, MC_CFG LDMIA R14, {R0-R12} LDR R14, =MC_BASE STMIA R14, {R0-R12} ENDIF IF PIO_SETUP <> 0 LDR R14, =PIO_BASE IF PIOA_SETUP <> 0 LDR R0, =PCONA_Val STR R0, [R14, #PCONA_OFS] ENDIF IF PIOB_SETUP <> 0 LDR R0, =PCONB_Val LDR R1, =PUPB_Val STR R0, [R14, #PCONB_OFS] STR R1, [R14, #PUPB_OFS] ENDIF IF PIOC_SETUP <> 0 LDR R0, =PCONC_Val LDR R1, =PUPC_Val STR R0, [R14, #PCONC_OFS] STR R1, [R14, #PUPC_OFS] ENDIF IF PIOD_SETUP <> 0 LDR R0, =PCOND_Val LDR R1, =PUPD_Val STR R0, [R14, #PCOND_OFS] STR R1, [R14, #PUPD_OFS] ENDIF IF PIOE_SETUP <> 0 LDR R0, =PCONE_Val LDR R1, =PUPE_Val STR R0, [R14, #PCONE_OFS] STR R1, [R14, #PUPE_OFS] ENDIF IF PIOF_SETUP <> 0 LDR R0, =PCONF_Val LDR R1, =PUPF_Val STR R0, [R14, #PCONF_OFS] STR R1, [R14, #PUPF_OFS] ENDIF IF PIOG_SETUP <> 0 LDR R0, =PCONG_Val LDR R1, =PUPG_Val STR R0, [R14, #PCONG_OFS] STR R1, [R14, #PUPG_OFS] ENDIF IF PIOH_SETUP <> 0 LDR R0, =PCONH_Val LDR R1, =PUPH_Val STR R0, [R14, #PCONH_OFS] STR R1, [R14, #PUPH_OFS] ENDIF ENDIF ;为每个模式设置栈空间 MRS r0, cpsr ; Original PSR value BIC r0, r0, #MODE_BITS ; Clear the mode bits ORR r0, r0, #ABT_MODE ; Set ABT mode bits MSR cpsr_c, r0 ; Change the mode LDR sp, =SFE(ABT_STACK) ; End of ABT_STACK BIC r0, r0, #MODE_BITS ; Clear the mode bits ORR r0, r0, #SVC_MODE ; Set SVC mode bits MSR cpsr_c, r0 ; Change the mode LDR sp, =SFE(SVC_STACK) ; End of SVC_STACK BIC r0, r0, #MODE_BITS ; Clear the mode bits ORR r0, r0, #UND_MODE ; Set UND mode bits MSR cpsr_c, r0 ; Change the mode LDR sp, =SFE(UND_STACK) ; End of UND_STACK BIC r0, r0, #MODE_BITS ; Clear the mode bits ORR r0, r0, #FIQ_MODE ; Set FIQ mode bits MSR cpsr_c, r0 ; Change the mode LDR sp, =SFE(FIQ_STACK) ; End of FIQ_STACK BIC r0, r0, #MODE_BITS ; Clear the mode bits ORR r0, r0, #IRQ_MODE ; Set IRQ mode bits MSR cpsr_c, r0 ; Change the mode LDR sp, =SFE(IRQ_STACK) ; End of IRQ_STACK BIC r0 ,r0, #MODE_BITS ; Clear the mode bits ORR r0 ,r0, #SYS_MODE ; Set System mode bits MSR cpsr_c, r0 ; Change the mode LDR sp, =SFE(CSTACK) ; End of CSTACK ;执行IAR的数据初始化流程 LDR r0,=?main BX r0 B . END
关于最上的config.inc的内容,使用Keil MDK的启动文件配置文件自动生成的,这里就不贴了,个人认为这点设计还是不错的。这个启动文件中关于各个外设的初始化流程也是使用了参考了Keil的做法。最后STACK的做法是直接让IAR自己来按照空间要求分配空间,而不是像某例程里面自己分配(实际上某例程里面,ICF里面定义了让IAR分配,但是启动文件里面放着分配的空间不用,用自己直接定义的地址,其实这种做法是有隐患的),另外也指出Keil自带的启动代码中的一个错误,设置堆栈的最后一步应该是进入系统(SYS)模式设置栈而不是进入用户(User)模式设置堆栈,系统模式和用户模式是共享栈空间的,设置任意一个都可以达成目标,而一旦进入用户模式后讲失去对MMU这类的控制权限,而且只能使用触发SWI经由SWI中断服务程序退出而不能直接设置CPSR退出。由于此时还未完成所有初始化工作,应该先进入系统模式,等全部完成再按需进入用户模式。