如何编写ARM7的启动代码
- 格式:docx
- 大小:456.67 KB
- 文档页数:5
arm启动代码详细分析所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初始化处理器模式,设置堆栈,初始化变量等等.由于以上的操作均与处理器体系结构和系统配置密切相关,所以一般由汇编来编写.具体到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 HEREUDFHANDLERB .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 stackSysStkSz EQU 128 ; size of SYS stackSvcStkSz EQU 8 ; size of SVC stackUdfStkSz EQU 8 ; size of UDF stackAbtStkSz EQU 8 ; size of ABT stackIrqStkSz EQU 128 ; size of IRQ stackFiqStkSz 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运行时,有明显的速度优势,而且变量可以动态配置,因此可以通过re map将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,因为默认情况下,W DT是打开的,然后是各设备的时钟分配,最后应该remapiar下.s79的分析BSP目录中的是和硬件平台代码相关的函数和定义。
实验ARM汇编程序的编写以及启动代码的分析一、实验目的:练习ARM汇编程序的编写,对提供的程序的启动代码进行分析,了解S3C2410初始化过程, 初始化代码主要是包含在start.s中。
二、实验原理:启动程序要完成的任务包括:硬件初始化,系统存储系统的配置,复制二级中断向量表。
启动程序过程●系统硬件初始化系统上电或复位后,程序从位于地址0x0的Reset Exception Vector处开始执行,因此需要在这里放置Bootloader的第一条指令:b ResetHandler,跳转到标号为ResetHandler处进行第一阶段的硬件初始化,执行完,系统进行堆栈和存储器的初始化。
使用了外设,则需要设置相关的寄存器,确定其刷新频率、总线宽度等信息。
●代码段复制到RAM中运行需要把系统的代码复制到RAM中运行。
映像文件内部共有三种输出段:RO段、RW段和ZI段。
ARMLink同时还产生了这三种输出段的起始和终止定位信息:Image$$RO$$Base、Image$$RO$$Limit、Image$$RW$$Base、Image$RW$Limit、Image$ZI$Base和Image$$ZI$$Limit。
可以在程序中使用这些定位信息。
将ROM中的代码和数据搬移到RAM中。
●建立二级中断向量表在ARM系统中,中断向量表位于0X0开始的地址处,意味着无论运行什么样的上层软件,一旦发生中断,程序就得到Flash存储器中的中断向量表里去,降低系统的运行效率。
因此在RAM中建立自己的二级中断向量表,当中断发生后,程序直接从RAM中取中断向量进入中断子程序。
尤其是在中断频繁发生的系统里,这种方法可以大大提高系统的运行效率。
三、实验内容:1.运行一个简单的串口程序,单步执行初始化代码,观察寄存器变化。
2.分析系统上电后的初始化工作包括哪些内容。
3.分析中断的处理过程,包括中断向量表的建立、中断源的识别及中断IRQ 服务程序是如何进入的。
ARM7启动程序班级:电子0801班姓名:****学号:200846751:PRESERVE8:Reguire8和Preserve8C和汇编有8位对齐的要求,这两个伪指令可以满足此要求,存在REQUIRE8<——> 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启动流程总结一、ARM7TDMI-S处理器1、ARM7TDMI-S处理器概述ARM7TDMI-S处理器是ARM通用32位微处理器家族成员之一。
ARM结构是基于精简指令集计算机(RISC)原理而设计的。
该处理器使用了冯.诺依曼(V onNeumann)结构,指令和数据共用一条32位总线。
只有装载、存储和交换指令可以对存储器中的数据进行访问。
该处理器有两个指令集:●32位ARM指令集●16位Thumb指令集2、指令流水线流水线使用3个阶段,因此指令分3个阶段执行:取指、译码、执行3阶段流水线如下图所示:注:程序计数器(PC)指向被取指的指令,而不是指向正在执行的指令。
二、指定程序段变量段在内存中位置的方法1、两种定位变量到指定位置方法(1)、使用定义在头文件absacc.h中的__at宏如:int x __at (0x40003000); // variable at address 0x40003000 (2)、采用分散加载技术表面定义就是把不同的程序段或数据段加载到不同的ROM或RAM地址。
一般为mem_a.scf文件。
同时,需设定编译连接器的路径:Options for Target->Linker->Scatter File中指定分散加载文件的路径。
三、ARM异常1、异常向量及异常以及各异常状态下的处理器模式(1)、异常优先级由高到低依次为:复位,数据中止,FIQ ,IRQ,预取指中止,未定义指令,SWI (2)、在异常向量中填充的是ARM跳转指令如:LDR PC, ResetAddrResetAddr DCD ResetInitResetInit: (复位入口)…(3)、ARM内核处理异常流程A、进入异常1、在对应处理器模式下的LR中保存下一条指令。
2、将CPSR复制到适当的SPSR中。
3、根据表2-4种异常进入时的模式将CPSR模式强制设为某一值。
4、强制PC从相关的异常向量处对下一条指令取指。
linux arm架构重启指令
Linux ARM架构重启指令。
在Linux操作系统中,ARM架构是一种常见的处理器架构,被
广泛应用于嵌入式系统和移动设备中。
在ARM架构的Linux系统中,重启指令是一项非常重要的功能,它可以帮助用户在需要时快速地
重启设备,以解决一些系统问题或者应用程序崩溃的情况。
在Linux中,用户可以使用不同的方法来执行重启指令,其中
最常用的是使用命令行工具。
在ARM架构的Linux系统中,可以使
用以下命令来执行重启操作:
sudo reboot.
在这个命令中,“sudo”表示以超级用户权限执行命令,“reboot”表示重启操作。
当用户输入这个命令并确认后,系统将
会开始执行重启操作,关闭所有正在运行的程序并重新启动系统。
除了使用命令行工具外,用户还可以通过图形界面来执行重启
操作。
在大多数ARM架构的Linux发行版中,用户可以通过单击菜
单中的“重启”选项来执行重启操作。
这种方法更加直观和用户友好,适合那些不太熟悉命令行操作的用户使用。
无论是使用命令行工具还是图形界面,重启操作都是非常简单
和常用的功能。
在日常使用中,用户可能会遇到一些系统问题或者
应用程序崩溃的情况,这时候执行重启操作往往可以解决这些问题,恢复系统的正常运行。
总的来说,重启指令在Linux ARM架构系统中是一项非常重要
的功能,它为用户提供了一种快速解决系统问题的方法。
无论是通
过命令行工具还是图形界面,用户都可以方便地执行重启操作,确
保系统的稳定和正常运行。
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异常是由内部或外部源产生的以引起处理器处理的一个事件。
ARM 启动代码详解(Vectors.c、Init.s、Target.c、 Target.h)2010-05-15 16:03启动代码是芯片复位后进入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异常是由部或外部源产生的以引起处理器处理的一个事件。
如何编写ARM7的启动代码(LPC2119为例)
随着生活水平的提高和IT技术的进步,8位处理器的处理能力已经不能满足嵌入式系统的需要了;而16位处理器在性能和成本上都没有很大的突破。
并且在8位机的开发中,大多使用汇编语言来编写用户程序。
这使得程序的可维护性、易移植性等都受到了极大的挑战。
正是基于此,ARM 公司适时的推出了一系列的32位嵌入式微控制器。
目前广泛使用的是ARM7和ARM9系列,ARM7TDMI内核的ARM7处理器广泛应用于工业控制、仪器仪表、汽车电子、通讯、消费电子等嵌入式设备。
本文主要以philips 公司ARM7TDMI核的LPC2119为例来分析如何编写ARM7的启动代码。
1、启动代码
在嵌入式系统软件的开发中,应用程序通常是在嵌入式操作系统的开发平台上采用C语言编写的。
然而,在ARM系统上电复位后,需要设置中断向量表、初始化各模式堆栈、设置系统时钟频率等,而这些过程都是针对
ARM内部寄存器结构的操作,用C语言编程是很难实现的。
因此在转到应用程序的c/c++编写之前,需要用ARM的汇编语言编写启动代码,由启动代码完成系统初始化以及跳转到用户C程序。
在ARM设计开发中,启动代码的编写是一个极重要的过程。
然而启动代码随具体的目标系统和开发系统有所区别,但通常包含以下部分:
·向量表定义
·地址重映射及中断向量表的转移
·堆栈初始化
·设置系统时钟频率
·中断寄存器的初始化
·进入C应用程序
下面就结合PHILIPS的LPC2119的启动代码来分析与说明ARM7处理器的启动代码的编写。
1.1向量表定义
ARM芯片上电或复位后,系统进入管理模式、ARM状态、PC(R15)指向0x00000000地址处。
中断向量表为每一个中断设置1个字的存储空间,存放一条跳转指令,通过这条指令使PC指针指向相应的中断服务程序入口,继而执行相应的中断处理程序。
LPC2219的中断向量表和其它基于ARM核的芯片中断向量表较类似,只要注意LPC2219要使向量表所有数据32位累加和为零(0x00000000-0x0000001C的8个字的机器码累加), 才能使用户的程序脱机运行。
1.2 地址重映射及中断向量表的转移
ARM7处理器在复位后从地址0读取第一条指令并执行,因此系统上电后地址0必须是非易失的ROM/FLASH,这样才能保证处理器有正确可用的指令。
为了加快对中断的处理以及实现在不同操作系统模式下对中断的处
理,这就需要重新映射中断向量表、BootbLOCk和SRAM空间的一小部分。
ARM具有非常灵活的存储器地址分配特性。
ARM处理器的地址重映射机制有两种情况:
①由专门的寄存器完成重映射(Remap),只需对相应的Remap寄存器相应位设置即可。
②没有专门的Remap控制寄存器需要重新改写用于控制存储器起始地址的块(Bank)寄存器来实现Remap。
在LPC2119上的重映射,可以通过存储器映射控制器来实现。
实现REMAP操作的程序实现如下:
MOV R8,#0x40000000; /设置新向量表起始地址/
LDR R9,=Interrupt_Vector_Table; /读原向量表源地址/
LDMIA R9!,(R0-R7); /复制中断向量表及中断处理程序的入口地址到RAM中(64字节)/
STMIA R8!,(R0-R7)
LDMIA R9!,(R0-R7)
STMIA R8!,(R0-R7)
LDR R8,=MEMMAP ; /REMMAP操作/
MOV R9,#0x02
STR R9, [R8]
1.3 堆栈初始化
启动代码中各模式堆栈空间的设置是为中断处理和程序跳转时服务的。
当系统响应中断或程序跳转时,需要将当前处理器的状态和部分重要参数保存在一段存储空间中,所以对每个模式都要进行堆栈初始化工作,给每个模式的SP定义一个堆栈基地址和堆栈的容量。
堆栈的初始化有两种方法:第一种方法是结合ADS开发套件中的分散加载文件来定义堆栈。
第二种方
法是最简单也是最常用的一种就是直接进入对应的处理器模式,为SP寄存器指定相应的值。
下面给出了用第二种方法初始化管理模式和中断模式堆栈的程序:
MSR CPSR_c, #0xD3 ; /切换到管理模式,并初始化管理模式的堆栈/ LDR SP, Stack_Svc
MSR CPSR_c, #0xD2 ; /切换到IRQ模式,并初始化IRQ模式的堆栈/ LDR SP, Stack_Irq
…
1.4 系统部分时钟初始化
时钟是芯片各部分正常工作的基础,应该在进入main()函数前设置。
部分ARM7片子内部集成有PLL(锁相环)电路,用户可以用低频率的晶振通过PLL电路获得一个较高频率的时钟。
LPC2119内部的PLL电路接受的输入时钟频率范围为10~25MHz,输入频率通过一个电流控制振荡器(CCO)倍增到范围10~60MHz。
同时为了使高速的ARM处理器与低速的外设正常通讯和降低功耗(降低外设运行速度使功耗降低),LPC2119又集成了一个额外的分频器。
PLL的激活是由PLLCON寄存器控制。
PLL倍频器和分频器的值由PLLCFG寄存器控制。
对PLLCON或PLLCFG寄存器的更改必须遵循严格的顺序,否则所作更改是无法生效的(在连续的VPB周期内向PLLFEED 寄存器写入0xAA、0x55,在此期间中断必须是被禁止的。
)
1.5 中断初始化
ARM7的向量中断控制器(Vectored Interrupt Controller)可以将中断编程为3类:FIQ、向量IRQ、非向量IRQ。
FIQ中断请求的优先级最高,其次是IRQ中断请求,非向量IRQ的优先级最低。
VIC具有32个中断请求输入,但在LPC2219中只占用了17个中断输入。
对于这17个中断源的IRQ/FIQ选择,由VICIntSelect寄存器控制,当对应位设置位1时,则此
中断为FIQ中断,否则为IRQ中断。
若再将IRQ中断设置到向量控制寄存器(VICVectCn TI n)中,则此中断为向量IRQ中断,否则为非向量IRQ中断。
FIQ中断是专门用来处理那些需要及时响应的特殊事件,尽可能地只给FIQ 分配一个中断源。
1.6 进入C应用程序
至此,系统各部分的初始化基本完成,可以直接从启动代码转入到应用程序的main()函数入口。
从启动代码转入到应用程序的实例代码如下:IMPORT main
LDR R0,=main
BX R0
2、总结
一个优秀的启动代码将给应用程序的开发提供一个良好的开发平台。
本文中较详细的讨论了启动代码的编写及难点。
其中在堆栈初始化过程中要特别的注意两点:
①要尽量给堆栈分配快速和高带宽的存储器。
②尽量避免过早将处理器切换到用户模式,一般在系统初始化的最后阶段才切换到用户模式(用户模式没有权限通过修改CPSR来进行模式切换)。