| |
关于ATMEL的SAM3S中程序启动分析
作者:卢老师,华清远见嵌入式学院讲师。
在分析启动代码之前,需要了解C和汇编代码通过那些步骤转化为可执行代码,从而最终在硬件上运行。通常的流程分4步。
1 用编译器把源代码编译成ELF(executable and linking format )目标文件。
2 用链接器把目标文件链接成ELF格式的可执行映像文件。
3 用FROMELF工具把可执行的映像文件转化为二进制映像文件。
4 烧写二进制映像文件到目标板。
以上步骤是有IAR软件完成。
Flash.icf文件
在链接步骤中IAR软件会调用配置文件Flash.icf,这个文件在启动的时候会进入对应的Flash段进行操作,与之对应的有ram.icf,ram.icf主要应用模式仿真,并不对实际外设操作。Flash,icf描述了SAM3S4B的RAM和FLASH的段分配,以及映射位置,在各存储段的操作方式。
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Vector table start*/
define symbol __ICFEDIT_vector_start__ = 0x00400000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x2000BFFF;//48kbytes ram
define symbol __ICFEDIT_region_ROM_start__ = 0x00400000;
define symbol __ICFEDIT_region_ROM_end__ = 0x0043FFFF;//256Kbytes Flash
/*-Sizes-*/
if (!isdefinedsymbol(__ICFEDIT_size_cstack__)) {
define symbol __ICFEDIT_size_cstack__ = 0x2000;
}
if (!isdefinedsymbol(__ICFEDIT_size_heap__)) {
define symbol __ICFEDIT_size_heap__ = 0x200;
}
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G; //统一寻址最大容量
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };//对齐方式和容量
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { readwrite };
do not initialize { section .noinit };
place at address mem:__ICFEDIT_vector_start__ { readonly section .intvec };//放在ROM中的内容为只读内容,即const型等
place in ROM_region { readonly };
place in RAM_region { readwrite, block CSTACK, block HEAP };//放在RAM中的内容为可读可写的内容和CSTACK等段
startup_sam3.c功能描述
/* Exception Table */
#pragma language=extended
#pragma segment="CSTACK"
/* The name "__vector_table" has special meaning for C-SPY: */
/* it is where the SP start value is found, and the NVIC vector */
/* table register (VTOR) is initialized to this address if != 0 */
#pragma section = ".intvec"
#pragma location = ".intvec"
constintvec_elem __vector_table[] = {
{.__ptr = __sfe("CSTACK")}, /*__sfe是IAR的“段操作符”segment operator。表示取某个段的后一个字节的地址*/
Reset_Handler,
NMI_Handler,
HardFault_Handler,
MemManage_Handler,
BusFault_Handler,
UsageFault_Handler,
(0UL), /* Reserved */
(0UL), /* Reserved */
(0UL), /* Reserved */
(0UL), /* Reserved */
SVC_Handler,
DebugMon_Handler,
(0UL), /* Reserved */
//PendSV_Handler,
//SysTick_Handler,
//IMPORT OS_CPU_PendSVHandler
OS_CPU_PendSVHandler,
//IMPORT OS_CPU_SysTickHandler
OS_CPU_SysTickHandler,
/* Configurable interrupts */
SUPC_Handler, /* 0 Supply Controller */
RSTC_Handler, /* 1 Reset Controller */
RTC_Handler, /* 2 Real Time Clock */
RTT_Handler, /* 3 Real Time Timer */
WDT_Handler, /* 4 Watchdog Timer */
PMC_Handler, /* 5 PMC */
EFC0_Handler, /* 6 EFC 0 */
EFC1_Handler, /* 7 EFC 1 */
UART_Handler, /* 8 UART */
#ifdef _SAM3XA_SMC_INSTANCE_
SMC_Handler, /* 9 SMC */
#else
(0UL), /* 9 Reserved */
#endif /* _SAM3XA_SMC_INSTANCE_ */
#ifdef _SAM3XA_SDRAMC_INSTANCE_
SDRAMC_Handler, /* 10 SDRAMC */
#else
(0UL), /* 10 Reserved */
#endif /* _SAM3XA_SDRAMC_INSTANCE_ */
PIOA_Handler, /* 11 Parallel IO Controller A */
PIOB_Handler, /* 12 Parallel IO Controller B */
#ifdef _SAM3XA_PIOC_INSTANCE_
PIOC_Handler, /* 13 Parallel IO Controller C */
#else
(0UL), /* 13 Reserved */
#endif /* _SAM3XA_PIOC_INSTANCE_ */
#ifdef _SAM3XA_PIOD_INSTANCE_
PIOD_Handler, /* 14 Parallel IO Controller D */
#else
(0UL), /* 14 Reserved */
#endif /* _SAM3XA_PIOD_INSTANCE_ */
#ifdef _SAM3XA_PIOE_INSTANCE_
PIOE_Handler, /* 15 Parallel IO Controller E */
#else
(0UL), /* 15 Reserved */
#endif /* _SAM3XA_PIOE_INSTANCE_ */
#ifdef _SAM3XA_PIOF_INSTANCE_
PIOF_Handler, /* 16 Parallel IO Controller F */
#else
(0UL), /* 16 Reserved */
#endif /* _SAM3XA_PIOF_INSTANCE_ */
USART0_Handler, /* 17 USART 0 */
USART1_Handler, /* 18 USART 1 */
USART2_Handler, /* 19 USART 2 */
#ifdef _SAM3XA_USART3_INSTANCE_
USART3_Handler, /* 20 USART 3 */
#else
(0UL), /* 20 Reserved */
#endif /* _SAM3XA_USART3_INSTANCE_ */
HSMCI_Handler, /* 21 MCI */
TWI0_Handler, /* 22 TWI 0 */
TWI1_Handler, /* 23 TWI 1 */
SPI0_Handler, /* 24 SPI 0 */
#ifdef _SAM3XA_SPI1_INSTANCE_
SPI1_Handler, /* 25 SPI 1 */
#else
(0UL), /* 25 Reserved */
#endif /* _SAM3XA_SPI1_INSTANCE_ */
SSC_Handler, /* 26 SSC */
TC0_Handler, /* 27 Timer Counter 0 */
TC1_Handler, /* 28 Timer Counter 1 */
TC2_Handler, /* 29 Timer Counter 2 */
TC3_Handler, /* 30 Timer Counter 3 */
TC4_Handler, /* 31 Timer Counter 4 */
TC5_Handler, /* 32 Timer Counter 5 */
#ifdef _SAM3XA_TC2_INSTANCE_
TC6_Handler, /* 33 Timer Counter 6 */
TC7_Handler, /* 34 Timer Counter 7 */
TC8_Handler, /* 35 Timer Counter 8 */
#else
(0UL), /* 33 Reserved */
(0UL), /* 34 Reserved */
(0UL), /* 35 Reserved */
#endif /* _SAM3XA_TC2_INSTANCE_ */
PWM_Handler, /* 36 PWM */
ADC_Handler, /* 37 ADC controller */
DACC_Handler, /* 38 DAC controller */
DMAC_Handler, /* 39 DMA Controller */
UOTGHS_Handler, /* 40 USB OTG High Speed */
TRNG_Handler, /* 41 True Random Number Generator */
#ifdef _SAM3XA_EMAC_INSTANCE_
EMAC_Handler, /* 42 Ethernet MAC */
#else
(0UL), /* 42 Reserved */
#endif /* _SAM3XA_EMAC_INSTANCE_ */
CAN0_Handler, /* 43 CAN Controller 0 */
CAN1_Handler /* 44 CAN Controller 1 */
};
上面一段函数是嵌套向量中断控制器NVIC向量表所对应的中断函数的名字,而中断函数的体,需要程序员自行写出。在Flash.icf中place at address mem:__ICFEDIT_vector_start__ { readonly section .intvec };将中断向量表,放入对应的地址。
启动代码与应用程序接口
在完成映射后,程序首先执行的是Reset_Handler(),通常我们所说的上电进入复位状态,启动函数。
voidReset_Handler(void)
{
__iar_program_start();
}
__iar_program_start();函数是用汇编实现的,在IAR环境中无法直接显示,因为在编译时,IAR软件直接将__iar_program_start;嵌入代码中,不需要程序员编写。
__iar_program_start:
#if AT91_REMAP
; The memory controller is initialized immediately before the remap
ldr r10, =EBI_init_table ; EBI register initialization table
; If pc > 0x100000
movs r0, pc, LSR #20
; Mask the 12 highest bits of the address
moveq r10, r10, LSL #12
moveq r10, r10, LSR #12
; Load the address where to jump
ldr r12, =after_remap ; get the real jump address ( after remap )
; Copy chip select register image to memory controller and command remap
ldmia r10!, {r0-r9,r11} ; load the complete image and the EBI base
stmia r11!, {r0-r9} ; store the complete image with the remap command
; jump to ROM at its new address
; this instruction was loaded into the pipeline before the remap was done
mov pc, r12 ; jump and break the pipeline
; Put constant table here.
LTORG
; EBI initialization table
; 32,768 MHz master clock assumed for timing
EBI_init_table:
dc32 0x0100252d ; Flash at 0x01000000, 16MB, 2 hold, 16 bits, 4 WS
dc32 0x02002121 ; RAM at 0x02000000, 1MB, 0 hold, 16 bits, 1 WS
dc32 0x20000000 ; unused
dc32 0x30000000 ; unused
dc32 0x40000000 ; unused
dc32 0x50000000 ; unused
dc32 0x60000000 ; unused
dc32 0x70000000 ; unused
dc32 0x00000001 ; REMAP command
dc32 0x00000006 ; standard read
dc32 __EBI_CSR0 ; EBI Base address
after_remap:
#endif
; Execute C startup code.
b ?cstartup
;---------------------------------------------------------------
; ?CSTARTUP
;---------------------------------------------------------------
SECTION FIQ_STACK:DATA:NOROOT(3)
SECTION IRQ_STACK:DATA:NOROOT(3)
SECTION SVC_STACK:DATA:NOROOT(3)
SECTION ABT_STACK:DATA:NOROOT(3)
SECTION UND_STACK:DATA:NOROOT(3)
SECTION CSTACK:DATA:NOROOT(3)
SECTION text:CODE:NOROOT(2)
EXTERN ?main
ARM
?cstartup
; Execution starts here.
; After a reset, the mode is ARM, Supervisor, interrupts disabled.
; Add initialization nedded before setup of stackpointers here
; Initialize the stack pointers.
; The pattern below can be used for any of the exception stacks:
; FIQ, IRQ, SVC, ABT, UND, SYS.
; The USR mode uses the same stack as SYS.
; The stack segments must be defined in the linker command file,
; and be declared above.
mrs r0,cpsr ; Original PSR value
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
ldrsp,=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
ldrsp,=SFE(UND_STACK) ; End of UND_STACK
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
ldrsp,=SFE(ABT_STACK) ; End of ABT_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
ldrsp,=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
ldrsp,=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
ldrsp,=SFE(CSTACK) ; End of CSTACK
#ifdef __ARMVFP__
; Enable the VFP coprocessor.
mov r0, #0x40000000 ; Set EN bit in VFP
fmxrfpexc, r0 ; FPEXC, clear others.
; Disable underflow exceptions by setting flush to zero mode.
; For full IEEE 754 underflow compliance this code should be removed
; and the appropriate exception handler installed.
mov r0, #0x01000000 ; Set FZ bit in VFP
fmxrfpscr, r0 ; FPSCR, clear others.
#endif
; Add more initialization here
; Continue to ?main for more IAR specific system startup
ldr r0,=?main//进入main主程序
bx r0//带状态切换指令的跳转
END
以上部分给出了程序在目标板上电后,进入main()函数前的具体工作。