ARM启动代码详解.

合集下载

arm启动原理及代码分析

arm启动原理及代码分析

初始化应用程序的执行环境
• Elf文件的类型:可执行文件、可重定位文 件、共享库object(又叫做共享库)文件。 • Elf文件的功能: 1、用作链接器的输入生成可执行的映像 文件 2、可装载到内存里运行,完成特定功能 的文件。
elf文件的物理结构
• • • • Elf文件头 Section: .text .data .bss .symtab Program header 可重定位文件与可执行文件的区别: 区与段的概念 program header
Section Header Table
可执行映像文件的逻辑结构
Output 输出段
Output 输出段 域 Output 输出段
装载域和运行域
• 域又组成什么呢?当然就是组成可执行文 件了,一个映像文件可以包含一到多个域。 与看起来有些多余,实际上更为重要的是 用域来描述输出区运行前和运行时在存储 系统上的位置。所以,域分为装载域和运 行域。装载域描述运行前输出段在rom/ram 中的分布状态,运行域描述运行时输出段 在rom/ram中的分布状态
• Nand flash(不可作为bootrom的存储器)
Boot相关硬件
Boot的主要功能
• • • • • 建立异常向量表 初始化中断 初始化硬件设备 初始化应用程序执行环境 跳转到主应用程序
0x00 0x04 异 常 中 断 向 量 表 的 建 立 0x08 0x0c 0x10 0x14 0x18
Section Header Table Hello.o
可执行映像文件的逻辑结构
由几个可重定位的目标文件的相同属性区组 成了可执行映像的段,那么段有组成什么? 在逻辑结构里,段组成了更大的组织:域
输入区ro 输入区rw

ARM启动代码详细注释

ARM启动代码详细注释

ARM启动代码详细注释syd168 2009-07-232410Init.s包括了板子上电后的初始化过程,具体有以下几个步骤:1.屏蔽所有中断,关看门狗。

2.根据工作频率设置PLL寄存器3.初始化存储控制相关寄存器4.初始化各模式下的栈指针5.设置缺省中断处理函数6.将数据段拷贝到RAM中,将零初始化数据段清零7.跳转到C语言Main入口函数中;=========================================; NAME: 2410INIT.S; DESC: C start up codes; Configure memory, ISR ,stacks; Initialize C-variables; HISTORY:; 2002.02.25:kwtark: ver 0.0; 2002.03.20:purnnamu: Add some functions for testing STOP,POWER_OFF mode; 2002.04.10:SJS:sub interrupt disable 0x3ff -> 0x7ff;=========================================GET option.s ;GET相当INCLUDE 将一个源文件包含到当前源文件,这里表示包含option.s,并在当前位置进行汇编GET memcfg.sGET 2410addr.sBIT_SELFREFRESH EQU (1<<22);SDRAM/DRAM 刷新控制器bit22 REFMD位0:CBR/AUTO REFRESH 1:SHIF REFRESH;下面是对arm处理器模式寄存器对应值的常数定义,arm处理器中有一个CPSR程序状态寄存器,后五位决定目前的处理器模式。

;Pre-defined constants ;PSR 模式位USERMODE EQU 0x10 ;用户模式FIQMODE EQU 0x11 ;FIQ快速中断模式IRQMODE EQU 0x12 ;中断模式SVCMODE EQU 0x13 ;超级用户模式ABORTMODE EQU 0x17 ;中止模式UNDEFMODE EQU 0x1b ;未定义指令模式MODEMASK EQU 0x1f ;系统模式NOINT EQU 0xc0 ;禁止IRQ和FIQ中断在option.s中, _STACK_BASEADDRESS EQU (SDRAM_END-0x8000) ;0x33ff8000;;定义各模式堆栈地址_STACK_BASEADDRESSUserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~ ; 用户模式堆栈SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~0x3ff47ff ; 超级用户模式堆栈4kUndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~0x3ff57ff ; 未定义指令模式堆栈1kAbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~0x3ff5bff ; 中止模式堆栈1kIRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~0x3ff5fff ; 中断模式堆栈4kFIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~0x33ff6fff ; 快速中断模式堆栈4k;arm处理器有两种工作状态;1.arm:32位这种工作状态下执行字对齐的arm指令;2.Thumb:16位这种工作状;态执行半字对齐的Thumb指令;因为处理器分为16位32位两种工作状态程序的编译器也是分16位和32两种编译方式;所以下面的程序用于根据处理器工作状态确定编译器编译方式;code16伪指令指示汇编编译器,后面的指令为16位的thumb指令;code32伪指令指示汇编编译器,后面的指令为32位的arm指令;这段是为了统一目前的处理器工作状态和软件编译方式(16位编译环境使用tasm.exe编译);===========================================================================================;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used. 检查是否是用tasm.exe进行16位编译GBLL THUMBCODE ;声明一个全局变量并初始化为{FALSE}[ {CONFIG} = 16 ;if config=16这里表示用16位编译方式THUMBCODE SETL {TRUE} ;SETL 给全局变量赋值,;设置THUMBCODE 为true CODE32 ;转入32位编译模式| ; | 等同ELSETHUMBCODE SETL {FALSE} ;设置THUMBCODE 为false] ; ] 等同ENDIF;========================================================================================== MACRO ;MACRO 定义宏MOV_PC_LR ;宏名MOV_PC_LR[ THUMBCODE ;if THUMBCODE=truebx lr ;THUMBCODE 模式上返回ARM状态| ;elsemov pc,lr ;ARM状态下返回] ;end ifMEND ;宏结束;================================================================MACRO ;宏定义开始MOVEQ_PC_LR ;宏名为MOVEQ_PC_LR[ THUMBCODE ;if THUMBCODE=truebxeq lr ;EQ 相等则跳转回ARM状态| ;elsemoveq pc,lr ;????] ;end ifMEND ;宏结束;==================================================================;注意下面这段程序是个宏定义;下面包含的HandlerXXX HANDLER HandleXXX将都被下面这段程序展开;这段程序用于把中断服务程序的首地址装载到pc中,有人称之为“加载程序”。

ARM启动代码分析

ARM启动代码分析

ARM启动代码分析
ARM启动代码的第一步是设置处理器的模式和栈指针。

ARM处理器有
多种模式,如用户模式、系统模式、中断模式和监视模式等,而每种模式
都有自己的寄存器集合,因此需要选择一个合适的模式。

一般情况下,一
开始会进入特权模式(如系统模式),然后将栈指针设置到RAM的一些合
适位置。

ARM启动代码的第三步是设置不同的中断服务例程。

ARM处理器支持
多种中断,如IRQ中断和FIQ中断等。

具体中断处理过程包括保存现场、
执行中断服务例程、恢复现场等步骤,这些都需要在启动代码中进行相关
的设置。

ARM启动代码的第四步是初始化内存管理单元(MMU)。

MMU是用来处
理虚拟地址和物理地址之间的映射关系的硬件单元,它可以提供更高的系
统性能和更灵活的内存管理能力。

初始化MMU需要设置页表和相关的控制
寄存器。

除了上述主要的步骤,ARM启动代码还可能包括其他一些完成特定任
务的代码,如初始化外设、配置中断控制器、设置缓存等。

这些任务的完
成都是为了保证系统正常启动和运行。

总之,ARM启动代码是用来初始化ARM处理器和系统环境的一段代码,它完成了处理器模式设置、时钟频率配置、中断服务例程设置、MMU初始
化以及跳转到操作系统内核等主要任务。

通常情况下,ARM启动代码由汇
编语言编写,具有高度灵活性和直接性。

ARM启动代码分析

ARM启动代码分析

ARM启动代码分析-philips的LPC2xxx系列***********************************************************************************************File: startup.s*Author: Embest w.h.xie 2005.02.21*Desc: lpc22xx\lpc212x\lpc211x\lpc210x startup code*History:* note modify:cui jian jie 2006-4-25*comment:**********************************************************************************************/# 处理器的七种工作方式的常量定义.EQU Mode_USR, 0x10 #用户模式.EQU Mode_FIQ, 0x11 #FIQ模式.EQU Mode_IRQ, 0x12 #IRQ模式.EQU Mode_SVC, 0x13 #超级用户模式.EQU Mode_ABT, 0x17 #终止模式.EQU Mode_UND, 0x1B #未定义模式.EQU Mode_SYS, 0x1F #系统模式# 中断屏蔽位.EQU I_Bit, 0x80 //IRQ中断控制位,当被置位时,IRQ中断被禁止.EQU F_Bit, 0x40 //FIQ中断控制位,当被置位时,FIQ中断被禁止# 状态屏蔽位.EQU T_bit, 0x20 //T位,置位时在Thumb模式下运行,清零时在ARM下运行# 定义程序入口点.globl _start.code 32.TEXT_start:# 中断向量表Vectors:LDR PC, Reset_Addr //把Reset_Addr地址处的内容放入PC中LDR PC, Undef_AddrLDR PC, SWI_AddrLDR PC, PAbt_AddrLDR PC, DAbt_Addr.long 0xb9205f80 @ keep interrupt vectors sum is 0LDR PC, [PC, #-0xff0] //当前PC值减去0xFF0等于IRQ中断入口地址LDR PC, FIQ_Addr#地址表Reset_Addr: #该地址标号存放Reset_Handler程序段的入口地址 .long Reset_HandlerUndef_Addr: #该地址标号存放Undef_Handler程序段的入口地址 .long Undef_HandlerSWI_Addr: #该地址标号存放SWI_Handler程序段的入口地址 .long SWI_HandlerPAbt_Addr: #该地址标号存放PAbt_Handler程序段的入口地址 .long PAbt_HandlerDAbt_Addr:.long DAbt_Handler.long 0IRQ_Addr: #地址标号处存放一个无效的数据.long 0FIQ_Addr: #该地址标号存放FIQ_Handler程序段的入口地址 .long FIQ_HandlerUndef_Handler:B Undef_HandlerPAbt_Handler:B PAbt_HandlerDAbt_Handler:B DAbt_Handler#软中断的中断服务子程序入口地址SWI_Handler:STMFD sp!, {r0-r3, r12, lr} //入栈,现场数据保护MOV r1, sp //把堆栈指针SP存入R1中MRS r0, spsr //把SPSR值存入R0,SPSR值为产生软中断时的CPSRTST r0, #T_bit //判断R0(SPSR)的T位是否为0#SPSR的T位不为0,工作在Thumb模式下LDRNEH r0, [lr,#-2] //SPSR的T位不为0,则[lr-2]-〉r0BICNE r0, r0, #0xFF00 // SPSR的T位不为0,清除r0的Bit8~Bit15位# SPSR的T位为0,工作在ARM模式下LDREQ r0, [lr,#-4] // SPSR的T位为0,则[lr-4] -〉r0BICEQ r0, r0, #0xFF000000 // SPSR的T位为0,清除r0的Bit24~Bit131位# R0 is interrupt number //R0是中断号# R1 is stack point //R1是堆栈指针BL SWI_Exception //进入软中断处理程序LDMFD sp!, {r0-r3, r12, pc}^ //出栈,现场数据恢复# 快速响应中断的中断服务自程序的入口地址FIQ_Handler:STMFD SP!, {R0-R3, LR} //入栈的现场保护# BL FIQ_Exception //进入FIQ的中断处理程序LDMFD SP!, {R0-R3, LR} //出栈,恢复现场SUBS PC, LR, #4 //返回到主程序# 复位后程序处理的入口地址Reset_Handler:BL RemapSRAM //进行存储器映射的操作#下面几行代码用来判断当前的工作模式MRS R0, CPSR //读CPSR到寄存器R0AND R0, R0, #0x1F //R0 = R0 AND 0x1FCMP R0, #Mode_USR //比较R0 和#Mode_USR,二者相减//如果相等则说明当前处在用户模式下,需要通过产生11号软中断进入系统模式。

ARM启动代码详解

ARM启动代码详解

PART1系统初始化流程如下:禁止看门狗----》在中断控制器中屏蔽所有中断----》系统时钟设置----》初始化端口----》DMA设置----》cashe和总线设置----》存储器设置,初始化SDRAM----》初始化堆栈----》设置IRQ和FIQ的入口----》地址重映射。

必须由汇编来完成的任务有:异常中断向量表的设置、IRQ向量表(向量模式)或ISR初始化(非向量模式)、二级ISR地址表的定义、Flash和SDRAM的设置(否则系统无法加载代码)、堆栈设置和模式切换、拷贝RW和ZI代码、设置系统时钟等。

对于非向量模式,不使用IRQ中断向量表,但二级ISR地址表的设置是相同的。

PART2理解启动代码(ADS)所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初始化处理器模式,设置堆栈,初始化变量等等.由于以上的操作均与处理器体系结构和系统配置密切相关,所以一般由汇编来编写.具体到S64,启动代码分成两部分,一是与ARM7TDMI内核相关的部分,包括处理器各异常向量的配置,各处理器模式的堆栈设置,如有必要,复制向量到RAM,以便remap之后处理器正确处理异常,初始化数据(包括RW与ZI),最后跳转到Main.二是与处理器外部设备相关的部分,这和厂商的联系比较大.虽然都采用了ARM7TDMI的内核,但是不同的厂家整合了不同的片上外设,需要不同的初始化,其中比较重要的是初始化WDT,初始化各子系统时钟,有必要的话,进行remap.这一部分与一般控制器的初始化类似,因此,本文不作重点描述.在进行分析之前,请确认如下相关概念:S64片上FLASH起始于0x100000,共64kB,片上RAM起始于0x200000,共16kB.S64复位之后,程序会从0开始执行,此时FLASH被映射到0地址,因此,S64可以取得指令并执行.显然,此时还是驻留在0x100000地址.如果使用remap命令,将会把RAM映射到0地址,同样的这时0地址的内容也只是RAM的镜像.S64的FLASH可以保证在最差情况时以30MHz进行单周期访问,而RAM可以保证在最大速度时的单周期访问.OK,以下开始分析启动代码.一,处理器异常S64将异常向量至于0地址开始的几个直接,这些是必需要处理的.由于复位向量位于0,也需要一条跳转指令.具体代码如下:RESETB SYSINIT ; ResetB UDFHANDLER ; UNDEFINEDB SWIHANDLER ; SWIB PABTHANDLER ; PREFETCH ABORTB DABTHANDLER ; DATA ABORTB . ; RESERVEDB VECTORED_IRQ_HANDLERB . ; ADD FIQ CODE HERE UDFHANDLERB . SWIHANDLERB . PABTHANDLERB . DABTHANDLERB .请注意,B指令经汇编后会替换为当前PC值加上一个修正值(+/-),所以这条指令是代码位置无关的,也就是不管这条指令是在0地址还是在0x100000执行,都能跳转到指定的位置,而LDR PC,=???将向PC直接装载一个标号的值,请注意,标号在编译过后将被替换为一个与RO 相对应的值,也就是说,这样的指令无论在哪里执行,都只会跳转到一个指定的位置.下面举一个具体的例子来说明两者的区别: 假定有如下程序:RESETB INIT 或者LDR PC,=INIT…INIT…其中RESET为起始时的代码,也就是这条代码的偏移为0,设INIT的偏移量为offset.如果将这段程序按照RO=0x1000000编译, 那么B INIT可理解为ADD PC, PC, #offset,而LDR PC,=INIT可被理解为MOV PC,#(RO+offset) .显然当系统复位时,程序从0开始运行,而0地址有FLASH的副本,执行B INIT将把PC指向位于0地址处的镜像代码位置,也即INIT;如果执行LDR PC,=INIT将会将PC 直接指向位于FLASH中的原始代码.因此以上两者都能正确运行.下面将RO设置为0x200000,编译后生成代码,还是得烧写到FLASH中,也就是还是0x100000,系统复位后从0地址执行,还是FLASH的副本,此时执行B INIT,将跳到副本中的INIT位置执行,此处有对应的代码;但是如果执行LDR PC,=INIT,将向PC加载0x200000+offset,这将使得PC跳到RAM中,而此时由于代码没有复制,RAM中的指定位置并没有代码,程序无法运行.二,处理器模式ARM的处理器可工作于多种模式,不同模式有不同的堆栈,以下设置各模式及其堆栈.预定义一些参数:MODUSR EQU 0x10MODSYS EQU 0x1FMODSVC EQU 0x13MODABT EQU 0x17MODUDF EQU 0x1BMODIRQ EQU 0x12MODFIQ EQU 0x11IRQBIT EQU 0x80FIQBIT EQU 0x40RAMEND EQU 0x00204000 ; S64 : 16KB RAMVECTSIZE EQU 0x100 ;UsrStkSz EQU 8 ; size of USR stack SysStkSz EQU 128 ; size of SYS stack SvcStkSz EQU 8 ; size of SVC stack UdfStkSz EQU 8 ; size of UDF stack AbtStkSz EQU 8 ; size of ABT stack IrqStkSz EQU 128 ; size of IRQ stack FiqStkSz EQU 16 ; size of FIQ stack修改这些值即可修改相应模式堆栈的尺寸.以下为各模式代码:SYSINIT;MRS R0,CPSRBIC R0,R0,#0x1FMOV R2,#RAMENDORR R1,R0,#(MODSVC :OR: IRQBIT :OR: FIQBIT)MSR cpsr_cxsf,R1 ; ENTER SVC MODEMOV sp,R2SUB R2,R2,#SvcStkSzORR R1,R0,#(MODFIQ :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER FIQ MODEMOV sp,R2SUB R2,R2,#FiqStkSzORR R1,R0,#(MODIRQ :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER IRQ MODEMOV sp,R2SUB R2,R2,#IrqStkSzORR R1,R0,#(MODUDF :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER UDF MODEMOV sp,R2SUB R2,R2,#UdfStkSzORR R1,R0,#(MODABT :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER ABT MODEMOV sp,R2SUB R2,R2,#AbtStkSz;ORR R1,R0,#(MODUSR :OR: IRQBIT :OR: FIQBIT);MSR CPSR_cxsf,R1 ; ENTER USR MODE;MOV sp,R2;SUB R2,R2,#UsrStkSzORR R1,R0,#(MODSYS :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER SYS MODEMOV sp,R2 ;三,初始化变量编译完成之后,连接器会生成三个基本的段,分别是RO,RW,ZI,并会在image中顺序摆放.显然,RW,ZI在运行开始时并不位于指定的RW位置,因此必须初始化LDR R0,=|Image$$RO$$Limit|LDR R1,=|Image$$RW$$Base|LDR R2,=|Image$$ZI$$Base|1CMP R1,R2LDRLO R3,[R0],#4STRLO R3,[R1],#4BLO %B1MOV R3,#0LDR R1,=|Image$$ZI$$Limit|2CMP R2,R1STRLO R3,[R2],#4BLO %B2四,复制异常向量由于代码于RAM运行时,有明显的速度优势,而且变量可以动态配置,因此可以通过remap将RAM映射到0,使得出现异常时ARM从RAM中取得向量.IMPORT |Image$$RO$$Base|IMPORT |Image$$RO$$Limit|IMPORT |Image$$RW$$Base|IMPORT |Image$$RW$$Limit|IMPORT |Image$$ZI$$Base|IMPORT |Image$$ZI$$Limit|COPY_VECT_TO_RAMLDR R0,=|Image$$RO$$Base|LDR R1,=SYSINITLDR R2,=0x200000 ; RAM STARTCMP R0,R1LDRLO R3,[R0],#4STRLO R3,[R2],#4BLO %B0这段程序将SYSINIT之前的代码,也就是异常处理函数,全部复制到RAM中, 这就意味着不能将RW设置为0x200000,这样会使得向量被冲掉.四,在RAM中运行如果有必要,且代码足够小,可以将代码置于RAM中运行,由于RAM中本身没有代码,就需要将代码复制到RAM中:COPY_BEGINLDR R0,=0x200000LDR R1,=RESET ; =|Image$$RO$$Base|CMP R1,R0 ;BLO COPY_END ;ADR R0,RESETADR R2,COPY_ENDSUB R0,R2,R0ADD R1,R1,R0LDR R3,=|Image$$RO$$Limit|3CMP R1,R3LDRLO R4,[R2],#4STRLO R4,[R1],#4BLO %B3LDR PC,=COPY_ENDCOPY_END程序首先取得RESET的连接地址,判断程序是否时是在RAM中运行,方法是与RAM起始地址比较,如果小于,那么就跳过代码复制.在复制代码的时候需要注意,在这段程序结束之前的代码没有必要复制,因为这些代码都已经执行过了,所以,先取得COPY_END,作为复制起始地址,然后计算其相对RESET的偏移,然后以RO的值加上这个偏移,就是复制目的地的起始地址,然后开始复制.五,开始主程序以上步骤完成,就可以跳转到main运行IMPORT MainLDR PC,=MainB .六,器件初始化主程序首先要进行器件的初始化,对S64而言,应该先初始化WDT,因为默认情况下,WDT是打开的,然后是各设备的时钟分配,最后应该remap.以上是必要的启动步骤,实际应用中可以根据具体情况添加一些代码.PART3ARM启动程序分析字体: 小中大 | 打印发布: 2007-7-17 11:05 作者: 网络转载来源: 网络查看: 4次ARM启动程序分析TIMER1用来触发和IRQ中断代码在FLASH中运行这个例子有一下几个文件:1. 中断向量表(ivt.s)2. 汇编启动代码 (init.s)3. C主函数文件中断向量表;代码必须链接在地址0X0。

linuxarm64启动代码:汇编部分

linuxarm64启动代码:汇编部分

linuxarm64启动代码:汇编部分1. 汇编部分主流程1.1 时序图1.2 代码解析1 ENTRY(stext)2/*3 * Kernel startup entry point.4 * ---------------------------5 *6 * The requirements are:7 * MMU = off, D-cache = off, I-cache = on or off,8 * x0 = physical address to the FDT blob.9 *10 * This code is mostly position independent so you call this at11 * __pa(PAGE_OFFSET + TEXT_OFFSET).12 *13 * Note that the callee-saved registers are used for storing variables14 * that are useful before the MMU is enabled. The allocations are described15 * in the entry routines.16*/17 __HEAD1819/*20 * DO NOT MODIFY. Image header expected by Linux boot-loaders.21*/22 b stext // 跳转到内核初始化中23 .long0// reserved24 .quad TEXT_OFFSET // Image load offset from start of RAM25 .quad 0// reserved26 .quad 0// reserved27 .quad 0// reserved28 .quad 0// reserved29 .quad 0// reserved30 .byte0x41// Magic number, "ARM\x64"31 .byte0x5232 .byte0x4d33 .byte0x6434 .word 0// reserved3536 ENTRY(stext)37 mov x21, x0 // x21=FDT,FDT是bootloader传过来的,是DTS的基地址38 bl __calc_phys_offset // 计算物理地址和物理地址和线性地址偏移x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET39 bl el2_setup // 从el2模式退回到el1模式40 mrs x22, midr_el1 // 获得cpuid x22=cpuid41 mov x0, x2242 bl lookup_processor_type // 搜索处理器类型,在arch/arm64/kernel/cputable.c中定义43 mov x23, x0 // x23=current cpu_table44 cbz x23, __error_p // invalid processor (x23=0)?45 bl __vet_fdt // 检测FDT是否是8字节对齐,位于物理地址的前512MB之内46 bl __create_page_tables // 创建内核页表,x25=TTBR0, x26=TTBR147/*48 * The following calls CPU specific code in a position independent49 * manner. See arch/arm64/mm/proc.S for details. x23 = base of50 * cpu_info structure selected by lookup_processor_type above.51 * On return, the CPU will be ready for the MMU to be turned on and52 * the TCR will have been set.53*/54 ldr x27, __switch_data // 需要注意这⾥,处理器初始化完成后跳转到__switch_data55// 这时候MMU已经打开56 adr lr, __enable_mmu // 初始化完成以后ret的时候返回到__enable_mmu所在的地址57 ldr x12, [x23, #CPU_INFO_SETUP]58 add x12, x12, x28 // cpu_info的地址转换成物理地址59 br x12 // 初始化处理器60 ENDPROC(stext)。

第5章 ARM启动代码原理和分析


19
5.2 ARM映像文件
RAM
Uninitialized data U i iti li d d t System data
拷贝
ZI RW
Main()
输出段 RO 输出段 RW 输出段 ZI 加载前输出段
Int num = 10
Sqr() System code System data Int num = 10 Uninitialized data
7
5.1 启动代码主要功能
ⅱ初始化存储器系统:
(3)总线宽度 ( )总线宽度 ARM微处理器架构支持8/16/32位的数据总线宽度访问存储器 和外设,对于特定的存储器来说,需要设定数据总线的宽度。 和外设 对于特定的存储器来说 需要设定数据总线的宽度 (4)存储器地址的配置 ARM微处理器架构理论上可以支持4GB的地址空间,而对于一 个实际的系统来说,配置的物理地址远没有这么多,因此,如何 配置存储器的地址,也是一个重要的问题。 配置存储器的地址 也是 个重要的问题 存储器本身不具备地址信息,它们在芯片中的地址是由芯片 厂家分配的,给存储器分配地址的过程称为存储器映射。 厂家分配的 给存储器分配地址的过程称为存储器映射 各存储 器分配到特定的地址范围后,这时用户所看见的存储器分布如下 图所示:
12
5.1 启动代码主要功能
ⅴ初始化应用程序的执行环境: 所谓应用程序执行环境的初始化,就是完成必要的从ROM 行 境 成 到RAM的数据传输和内容清零。其中的指令至少应该包含如 下功能: 下功能
1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存 在ROM中。 在ROM中 2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中, 所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区 域清零。ZI中也是变量,同理变量不能存在ROM中。在程序运行 的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访 问变量。否则只能运行不含变量的代码。

ARM启动代码研究(附源代码).

ARM启动代码研究(附源代码)1:PRESERVE8:Reguire8和Preserve8C和汇编有8位对齐的要求,这两个伪指令可以满足此要求,存在REQUIRE8<——>PRESERVE8的对应关系,但不是说有一个REQUIRE8就要有一个PRESERVE8,如果是一个c文件和一个汇编文件的调用,也就涉及一个PRESERVE8或者是一个REQUIRE8.另外,REQUIRE8和PRESERVE8并不完成8byte对齐的操作,对齐由ALIGN完成。

将ADS的代码移植到KEILMDK上需要做的修改:当用户拥有ADS遗留工程的所有源代码时,1:PRESERVE8:Reguire8和Preserve8C和汇编有8位对齐的要求,这两个伪指令可以满足此要求,存在REQUIRE8&lt;——> PRESERVE8的对应关系,但不是说有一个REQUIRE8就要有一个 PRESERVE8,如果是一个c文件和一个汇编文件的调用,也就涉及一个PRESERVE8或者是一个REQUIRE8.另外,REQUIRE8和PRESERVE8并不完成8 byte 对齐的操作,对齐由ALIGN完成。

将ADS的代码移植到KEIL MDK上需要做的修改:当用户拥有ADS遗留工程的所有源代码时,使用MDK重新编译链接全部代码是最好的解决方法,MDK中的新版本编译工具会重新生成满足堆栈8BYTE对齐要求的目标文件,避免由于堆栈不对齐引起的链接错误.从ADS到KEIL很重要的一个修改的地方就是这里的8BYTE对齐,想要编译通过,在startup.s里面我们必须加入PRESERVE8指令,使得寄存器8BYTE对齐.代码:CODE32PRESERVE8 ;这个在KEIL里面是必须的,要求8BYTE对齐.在ADS的启动代码中就没有.AREA vectors,CODE,READONLY2: ARM的处理器可工作于多种模式,下面设置个模式的一些参数.Mode_USR EQU 0x10 用户模式Mode_FIQ EQU 0x11快中断模式Mode_IRQ EQU 0x12中断模式Mode_SVC EQU 0x13 管理模式Mode_ABT EQU 0x17 中止模式Mode_UND EQU 0x1B 未定义模式Mode_SYS EQU 0x1F系统模式参数的由来:这里各个模式的参数是由寄存器CPSR的模式位设置M[4:0]得来的,比如这里的用户模式,CPSR的M[4:0]设置为10000就是0x10,同理其他.详见<<ARM嵌入式系统基础教程>>P47页,CPSR设置很关键!3:I_Bit EQU 0x80 ; when I bit is set, IRQ is disabledF_Bit EQU 0x40 ; when F bit is set, FIQ is disabled也和CPSR寄存器的设置有关,这里两位是禁止/开启快速中断和一般中断的设置.4: 各模式下定义的堆栈地址.UND_Stack_Size EQU 0x00000000SVC_Stack_Size EQU 0x00000100ABT_Stack_Size EQU 0x00000000FIQ_Stack_Size EQU 0x00000000IRQ_Stack_Size EQU 0x00000100USR_Stack_Size EQU 0x00000200设置堆栈大小Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size +FIQ_Stack_Size +IRQ_Stack_Size + USR_Stack_Size)AREA STACK, NOINIT, READWRITE, ALIGN=3Stack_Mem SPACE Stack_SizeStack_Top EQU Stack_Mem + Stack_Size堆栈大小的设置,各公司写的启动代码有所不同,但是不影响大局,可以借鉴一些你认为比较简单的启动代码,然后写自己的堆栈地址和大小设置程序.5:堆的设置Heap_Size EQU 0x00000000AREA HEAP, NOINIT, READWRITE, ALIGN=3Heap_Mem SPACE Heap_SizeAREA Init,CODE,READONLY,ALIGN=3 //指定后面的指令为8位对齐(2的3次方) align n它的含义就是使得下面的代码按一定规则对齐,.align n 指令的对齐值有两种方案,n 或 2^n ,各种平台最初的汇编器一般都不是gas,采取方案1或2的都很多,gas的目标是取代原来的汇编器,必然要保持和原来汇编器的兼容,因此在gas中如何解释 .align指令会显得有些混乱,原因在于保持兼容。

ARM的启动分析详解


7
程序入口
下面是大小端的一个判断, 条件编译, 下面是大小端的一个判断 条件编译,在编译成机器码前 就设定好,在 里已经设为FALSE。 就设定好 在Option.inc里已经设为 里已经设为 。
8
分配中断向量表
ARM 要求中断向量表必须放置在从 要求中断向量表必须放置在从0x00000000 地址开 始,连续32 个字节的空间内。 连续 个字节的空间内。 每当一个中断发生后,即使移植了操作系统如 每当一个中断发生后,即使移植了操作系统如linux ,处理 处理 器还是会跳转到从0x0开始 强制把 指针指向对应中断类型 开始,强制把 器还是会跳转到从 开始 强制把PC指针指向对应中断类型 的向量表中的地址。 的向量表中的地址。 因为每个中断只占据向量表中4 个字节的存储空间, 因为每个中断只占据向量表中 个字节的存储空间,只能 放置一条ARM 指令,所以,通常放一条跳转指令让程序跳 指令,所以, 放置一条 转到存储器的其他地方,再执行中断处理。 转到存储器的其他地方,再执行中断处理。
根据工作频率设置pll : 根据工作频率设置 Fpllo=(m*Fin)/(p*2^s) m=MDIV+8,p=PDIV+2,s=SDIV Fpllo必须大于 必须大于20Mhz小于 小于66Mhz,Fpllo*2^s必须小于 必须小于170Mhz 必须大于 小于 , 必须小于
14
4、初始化PLL和时钟 、初始化 和时钟
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;Fin=12MHz,Fout=50MHz str r1,[r0] ;Configure UPLL ldr r0,=UPLLCON ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV) ;Fin=12MHz,UPLLout=48MHz str r1,[r0] ] 16

ARM 启动代码详解(Vectors.c、Init.s、Target.c、 Target.h)

ARM 启动代码详解(Vectors.c、Init.s、Target.c、Target.h)启动代码是芯片复位后进入C语言的main()函数前执行的一段代码,主要是为运行C语言程序提供基本运行环境,如初始化存储器系统等。

ARM公司只设计内核,不自己生产芯片,只是把内核授权给其它厂商,其它厂商购买了授权且加入自己的外设后生产出各具特色的芯片。

这样就促进了基于ARM处理器核的芯片多元化,但也使得每一种芯片的启动代码差别很大,不易编写出统一的启动代码。

ADS(针对ARM 处理器核的C语言编译器)的策略是不提供完整的启动代码,启动代码不足部分或者由厂商提供,或者自己编写。

启动代码划分为4个文件:Vectors.c、Init.s、Target.c、Target.h。

Vectors.c包含异常向量表、堆栈初始化及中断服务程序与C程序的接口。

Init.s包含统初始化代码,并跳转到ADS提供的初始化代码。

Target.c和Target.h包含目标板特殊的代码,包括异常处理程序和目标板初始化程序。

这样做的目的是为了尽量减少汇编代码,同时把不需要修改的代码独立出来以减少错误。

§4.2.1 Vectors.c文件的编写§4.2.1.1 中断向量表VectorsLDR PC, ResetAddrLDR PC, UndefinedAddrLDR PC, SWI_AddrLDR PC, PrefetchAddrLDR PC, DataAbortAddrDCD 0xb9205f80LDR PC, [PC, #-0xff0]LDR PC, FIQ_AddrResetAddr DCD ResetUndefinedAddr DCD UndefinedSWI_Addr DCD SoftwareInterruptPrefetchAddr DCD PrefetchAbortDataAbortAddr DCD DataAbortnouse DCD 0IRQ_Addr DCD IRQ_HandlerFIQ_Addr DCD FIQ_Handler异常是由内部或外部源产生的以引起处理器处理的一个事件。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

PART1系统初始化流程如下:禁止看门狗----》在中断控制器中屏蔽所有中断----》系统时钟设置----》初始化端口----》DMA设置----》cashe和总线设置----》存储器设置,初始化SDRAM----》初始化堆栈----》设置IRQ和FIQ的入口----》地址重映射。

必须由汇编来完成的任务有:异常中断向量表的设置、IRQ向量表(向量模式或ISR 初始化(非向量模式、二级ISR地址表的定义、Flash和SDRAM的设置(否则系统无法加载代码、堆栈设置和模式切换、拷贝RW和ZI代码、设置系统时钟等。

对于非向量模式,不使用IRQ中断向量表,但二级ISR地址表的设置是相同的。

PART2理解启动代码(ADS所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初始化处理器模式,设置堆栈,初始化变量等等.由于以上的操作均与处理器体系结构和系统配置密切相关,所以一般由汇编来编写.具体到S64,启动代码分成两部分,一是与ARM7TDMI内核相关的部分,包括处理器各异常向量的配置,各处理器模式的堆栈设置,如有必要,复制向量到RAM,以便remap之后处理器正确处理异常,初始化数据(包括RW与ZI,最后跳转到Main.二是与处理器外部设备相关的部分,这和厂商的联系比较大.虽然都采用了ARM7TDMI的内核,但是不同的厂家整合了不同的片上外设,需要不同的初始化,其中比较重要的是初始化WDT,初始化各子系统时钟,有必要的话,进行remap.这一部分与一般控制器的初始化类似,因此,本文不作重点描述.在进行分析之前,请确认如下相关概念:S64片上FLASH起始于0x100000,共64kB,片上RAM起始于0x200000,共16kB.S64复位之后,程序会从0开始执行,此时FLASH被映射到0地址,因此,S64可以取得指令并执行.显然,此时还是驻留在0x100000地址.如果使用remap命令,将会把RAM映射到0地址,同样的这时0地址的内容也只是RAM的镜像.S64的FLASH可以保证在最差情况时以30MHz进行单周期访问,而RAM可以保证在最大速度时的单周期访问.OK,以下开始分析启动代码.一,处理器异常S64将异常向量至于0地址开始的几个直接,这些是必需要处理的.由于复位向量位于0,也需要一条跳转指令.具体代码如下:RESETB SYSINIT ; ResetB UDFHANDLER ; UNDEFINEDB SWIHANDLER ; SWIB PABTHANDLER ; PREFETCH ABORTB DABTHANDLER ; DATA ABORTB . ; RESERVEDB VECTORED_IRQ_HANDLERB . ; ADD FIQ CODE HERE UDFHANDLERB . SWIHANDLERB . PABTHANDLERB . DABTHANDLERB .请注意,B指令经汇编后会替换为当前PC值加上一个修正值(+/-,所以这条指令是代码位置无关的,也就是不管这条指令是在0地址还是在0x100000执行,都能跳转到指定的位置,而LDR PC,=???将向PC直接装载一个标号的值,请注意,标号在编译过后将被替换为一个与RO 相对应的值,也就是说,这样的指令无论在哪里执行,都只会跳转到一个指定的位置.下面举一个具体的例子来说明两者的区别: 假定有如下程序:RESETB INIT 或者LDR PC,=INIT…INIT…其中RESET为起始时的代码,也就是这条代码的偏移为0,设INIT的偏移量为offset.如果将这段程序按照RO=0x1000000编译, 那么B INIT可理解为ADD PC, PC, #offset,而LDR PC,=INIT可被理解为MOV PC,#(RO+offset .显然当系统复位时,程序从0开始运行,而0地址有FLASH的副本,执行B INIT将把PC指向位于0地址处的镜像代码位置,也即INIT;如果执行LDR PC,=INIT将会将PC 直接指向位于FLASH中的原始代码.因此以上两者都能正确运行.下面将RO设置为0x200000,编译后生成代码,还是得烧写到FLASH中,也就是还是0x100000,系统复位后从0地址执行,还是FLASH的副本,此时执行B INIT,将跳到副本中的INIT位置执行,此处有对应的代码;但是如果执行LDR PC,=INIT,将向PC加载0x200000+offset,这将使得PC跳到RAM中,而此时由于代码没有复制,RAM中的指定位置并没有代码,程序无法运行.二,处理器模式ARM的处理器可工作于多种模式,不同模式有不同的堆栈,以下设置各模式及其堆栈.预定义一些参数:MODUSR EQU 0x10MODSYS EQU 0x1FMODSVC EQU 0x13MODABT EQU 0x17MODUDF EQU 0x1BMODIRQ EQU 0x12MODFIQ EQU 0x11IRQBIT EQU 0x80FIQBIT EQU 0x40RAMEND EQU 0x00204000 ; S64 : 16KB RAMVECTSIZE EQU 0x100 ;UsrStkSz EQU 8 ; size of USR stack SysStkSz EQU 128 ; size of SYS stack SvcStkSz EQU 8 ; size of SVC stack UdfStkSz EQU 8 ; size of UDF stack AbtStkSz EQU 8 ; size of ABT stack IrqStkSz EQU 128 ; size of IRQ stack FiqStkSz EQU 16 ; size of FIQ stack修改这些值即可修改相应模式堆栈的尺寸.以下为各模式代码:SYSINIT;MRS R0,CPSRBIC R0,R0,#0x1FMOV R2,#RAMENDORR R1,R0,#(MODSVC :OR: IRQBIT :OR: FIQBIT MSR cpsr_cxsf,R1 ; ENTER SVC MODEMOV sp,R2SUB R2,R2,#SvcStkSzORR R1,R0,#(MODFIQ :OR: IRQBIT :OR: FIQBIT MSR CPSR_cxsf,R1 ; ENTER FIQ MODEMOV sp,R2SUB R2,R2,#FiqStkSzORR R1,R0,#(MODIRQ :OR: IRQBIT :OR: FIQBIT MSR CPSR_cxsf,R1 ; ENTER IRQ MODEMOV sp,R2SUB R2,R2,#IrqStkSzORR R1,R0,#(MODUDF :OR: IRQBIT :OR: FIQBITMSR CPSR_cxsf,R1 ; ENTER UDF MODEMOV sp,R2SUB R2,R2,#UdfStkSzORR R1,R0,#(MODABT :OR: IRQBIT :OR: FIQBITMSR CPSR_cxsf,R1 ; ENTER ABT MODEMOV sp,R2SUB R2,R2,#AbtStkSz;ORR R1,R0,#(MODUSR :OR: IRQBIT :OR: FIQBIT;MSR CPSR_cxsf,R1 ; ENTER USR MODE;MOV sp,R2;SUB R2,R2,#UsrStkSzORR R1,R0,#(MODSYS :OR: IRQBIT :OR: FIQBITMSR CPSR_cxsf,R1 ; ENTER SYS MODEMOV sp,R2 ;三,初始化变量编译完成之后,连接器会生成三个基本的段,分别是RO,RW,ZI,并会在image中顺序摆放.显然,RW,ZI在运行开始时并不位于指定的RW位置,因此必须初始化LDR R0,=|Image$$RO$$Limit|LDR R1,=|Image$$RW$$Base|LDR R2,=|Image$$ZI$$Base|1CMP R1,R2LDRLO R3,[R0],#4STRLO R3,[R1],#4BLO %B1MOV R3,#0LDR R1,=|Image$$ZI$$Limit|2CMP R2,R1STRLO R3,[R2],#4BLO %B2四,复制异常向量由于代码于RAM运行时,有明显的速度优势,而且变量可以动态配置,因此可以通过remap将RAM映射到0,使得出现异常时ARM从RAM中取得向量.IMPORT |Image$$RO$$Base|IMPORT |Image$$RO$$Limit|IMPORT |Image$$RW$$Base|IMPORT |Image$$RW$$Limit|IMPORT |Image$$ZI$$Base|IMPORT |Image$$ZI$$Limit|COPY_VECT_TO_RAMLDR R0,=|Image$$RO$$Base|LDR R1,=SYSINITLDR R2,=0x200000 ; RAM STARTCMP R0,R1LDRLO R3,[R0],#4STRLO R3,[R2],#4BLO %B0这段程序将SYSINIT之前的代码,也就是异常处理函数,全部复制到RAM中, 这就意味着不能将RW设置为0x200000,这样会使得向量被冲掉.四,在RAM中运行如果有必要,且代码足够小,可以将代码置于RAM中运行,由于RAM中本身没有代码,就需要将代码复制到RAM中:COPY_BEGINLDR R0,=0x200000LDR R1,=RESET ; =|Image$$RO$$Base|CMP R1,R0 ;BLO COPY_END ;ADR R0,RESETADR R2,COPY_ENDSUB R0,R2,R0ADD R1,R1,R0LDR R3,=|Image$$RO$$Limit|3CMP R1,R3LDRLO R4,[R2],#4STRLO R4,[R1],#4BLO %B3LDR PC,=COPY_ENDCOPY_END程序首先取得RESET的连接地址,判断程序是否时是在RAM中运行,方法是与RAM起始地址比较,如果小于,那么就跳过代码复制.在复制代码的时候需要注意,在这段程序结束之前的代码没有必要复制,因为这些代码都已经执行过了,所以,先取得COPY_END,作为复制起始地址,然后计算其相对RESET的偏移,然后以RO的值加上这个偏移,就是复制目的地的起始地址,然后开始复制.五,开始主程序以上步骤完成,就可以跳转到main运行IMPORT MainLDR PC,=MainB .六,器件初始化主程序首先要进行器件的初始化,对S64而言,应该先初始化WDT,因为默认情况下,WDT是打开的,然后是各设备的时钟分配,最后应该remap.以上是必要的启动步骤,实际应用中可以根据具体情况添加一些代码.PART3ARM启动程序分析字体: 小中大 | 打印发布: 2007-7-17 11:05 作者: 网络转载来源: 网络查看: 4次ARM启动程序分析TIMER1用来触发和IRQ中断代码在FLASH中运行这个例子有一下几个文件:1. 中断向量表(ivt.s2. 汇编启动代码 (init.s3. C主函数文件中断向量表;代码必须链接在地址0X0。

相关文档
最新文档