收藏 分享(赏)

ARM的启动过程.doc

上传人:scg750829 文档编号:9623651 上传时间:2019-08-18 格式:DOC 页数:31 大小:115.50KB
下载 相关 举报
ARM的启动过程.doc_第1页
第1页 / 共31页
ARM的启动过程.doc_第2页
第2页 / 共31页
ARM的启动过程.doc_第3页
第3页 / 共31页
ARM的启动过程.doc_第4页
第4页 / 共31页
ARM的启动过程.doc_第5页
第5页 / 共31页
点击查看更多>>
资源描述

1、ARM 芯片的启动程序的分析和总结(2009-02-04 14:35:26) 转载标签: 杂谈分类:ARM1、综述:目前大多基于 ARM 芯片的系统都是一个比较复杂的片上系统,多数硬件模块都是可配置的,可以通过软件来设置其需要的工作状态。因此在运行用户的应用程序之前,需要由专门的一段代码来完成对系统的初始化。这一段代码就称为启动程序。由于这类代码直接面对处理器内核和硬件控制器进行编程,一般都是用汇编语言。在ARM 系统上电复位后,需要设置中断向量表、初始化各模式堆栈、设置系统时钟频率等,需要用 ARM 的汇编语言编写启动代码,由启动代码完成系统初始化以及跳转到用户 C 程序。在 ARM 设计开

2、发中,启动代码的编写是一个极重要的过程。然而启动代码随具体的目标系统和开发系统有所区别,但通常包含以下部分:向量表定义地址重映射及中断向量表的转移堆栈初始化设置系统时钟频率中断寄存器的初始化进入 C 应用程序下面就结合 PHILIPS 的 LPC2119 的启动代码来分析与说明 ARM7 处理器的启动代码的编写。1.1 向量表定义ARM 芯片上电或复位后,系统进入管理模式、ARM 状态、PC(R15 寄存器)指向0x00000000 地址处。中断向量表为每一个中断设置 1 个字的存储空间,存放一条跳转指令,通过这条指令使 PC 指针指向相应的中断服务程序入口,继而执行相应的中断处理程序。LPC

3、2119 的中断向量表和其它基于 ARM 核的芯片中断向量表较类似,只要注意 LPC2119 要使向量表所有数据 32 位累加和为零(0x000000000x0000001C 的 8 个字的机器码累加), 才能使用户的程序脱机运行。LPC2119 的中断向量表如图 1 所示。1.2 地址重映射及中断向量表的转移ARM7 处理器在复位后从地址 0 读取第一条指令并执行,因此系统上电后地址 0 必须是非易失的 ROM/FLASH,这样才能保证处理器有正确可用的指令。为了加快对中断的处理以及实现在不同操作系统模式下对中断的处理,这就需要重新映射中断向量表、Bootblock 和 SRAM 空间的一小

4、部分。ARM 具有非常灵活的存储器地址分配特性。ARM 处理器的地址重映射机制有两种情况:由专门的寄存器完成重映射(Remap),只需对相应的 Remap 寄存器相应位设置即可。没有专门的 Remap 控制寄存器需要重新改写用于控制存储器起始地址的块(Bank)寄存器来实现 Remap。在 LPC2119 上的重映射,可以通过存储器映射控制器来实现。实现REMAP 操作的程序实现如下:MOV R8,#0x40000000; /设置新向量表起始地址/LDR R9,=Interrupt_Vector_Table; /读原向量表源地址/LDMIA R9!,(R0-R7); /复制中断向量表及中断处理

5、程序的入口地址到 RAM中(64 字节)/STMIA R8!,(R0-R7)LDMIA R9!,(R0-R7)STMIA R8!,(R0-R7)LDR R8,=MEMMAP ; /REMMAP 操作/MOV R9,#0x02STR R9, R81.3 堆栈初始化启动代码中各模式堆栈空间的设置是为中断处理和程序跳转时服务的。当系统响应中断或程序跳转时,需要将当前处理器的状态和部分重要参数保存在一段存储空间中,所以对每个模式都要进行堆栈初始化工作,给每个模式的 SP 定义一个堆栈基地址和堆栈的容量。堆栈的初始化有两种方法:第一种方法是结合 ADS 开发套件中的分散加载文件来定义堆栈。第二种方法是最

6、简单也是最常用的一种就是直接进入对应的处理器模式,为 SP 寄存器指定相应的值。下面给出了用第二种方法初始化管理模式和中断模式堆栈的程序:MSR CPSR_c, #0xD3 ; /切换到管理模式,并初始化管理模式的堆栈/LDR SP, Stack_SvcMSR CPSR_c, #0xD2 ; /切换到 IRQ 模式,并初始化 IRQ 模式的堆栈/LDR SP, Stack_Irq1.4 系统部分时钟初始化时钟是芯片各部分正常工作的基础,应该在进入 main()函数前设置。部分 ARM7片子内部集成有 PLL(锁相环)电路,用户可以用低频率的晶振通过 PLL 电路获得一个较高频率的时钟。LPC2

7、119 内部的 PLL 电路接受的输入时钟频率范围为 1025MHz,输入频率通过一个电流控制振荡器(CCO)倍增到范围 1060MHz。同时为了使高速的 ARM 处理器与低速的外设正常通讯和降低功耗(降低外设运行速度使功耗降低),LPC2119 又集成了一个额外的分频器。PLL 的激活是由 PLLCON 寄存器控制。PLL 倍频器和分频器的值由 PLLCFG 寄存器控制。对 PLLCON 或 PLLCFG 寄存器的更改必须遵循严格的顺序,否则所作更改是无法生效的(在连续的 VPB 周期内向 PLLFEED 寄存器写入 0xAA、0x55,在此期间中断必须是被禁止的。)1.5 中断初始化ARM

8、7 的向量中断控制器(Vectored Interrupt Controller)可以将中断编程为 3类:FIQ、向量 IRQ、非向量 IRQ。FIQ 中断请求的优先级最高,其次是 IRQ 中断请求,非向量 IRQ 的优先级最低。VIC 具有 32 个中断请求输入,但在 LPC2219 中只占用了 17 个中断输入。对于这 17 个中断源的 IRQ/FIQ 选择,由 VICIntSelect 寄存器控制,当对应位设置位 1 时,则此中断为 FIQ 中断,否则为 IRQ 中断。若再将 IRQ 中断设置到向量控制寄存器(VICVectCntIn)中,则此中断为向量 IRQ 中断,否则为非向量 IR

9、Q 中断。FIQ 中断是专门用来处理那些需要及时响应的特殊事件,尽可能地只给 FIQ 分配一个中断源。1.6 进入 C 应用程序至此,系统各部分的初始化基本完成,可以直接从启动代码转入到应用程序的main()函数入口。从启动代码转入到应用程序的实例代码如下:IMPORT mainLDR R0,=mainBX R02、总结一个优秀的启动代码将给应用程序的开发提供一个良好的开发平台。本文中较详细的讨论了启动代码的编写及难点。其中在堆栈初始化过程中要特别的注意两点:要尽量给堆栈分配快速和高带宽的存储器。尽量避免过早将处理器切换到用户模式,一般在系统初始化的最后阶段才切换到用户模式(用户模式没有权限通

10、过修改 CPSR 来进行模式切换)。嵌入式系统的迅猛发展,使启动代码的编写成为嵌入式系统开发人员应该具备的能力。本文有助于正在从事嵌入式 ARM 开发的读者理解启动代码的内涵与编写出适合自己的启动代码。启动文件分析(试验总结) (以下所有完全是我自己在今晚对 arm 启动代码的所有理解,可能有很大的问题,但目前为止,我还是能自圆其说,还是另自己信服的,如果高手看出什么低级错误,欢迎指出,共同学习!) 玩 arm 底层编程也有一个星期,感觉机会成熟,晚上认真把周立功的启动底层代码认真看了一遍,感觉豁然开朗,呵呵,赶快把学到的写在这里,怕以后忘记。 完全理解这些基础的代码是学习移至 uc/os 或

11、者 uclinux 的第一步,所有 arm 的核,甚至可以说 x86 的架构都是很相似的。这里所说的启动文件是没有任何操作系统,没有任何软件的基础上,去实现 arm 的编程,让程序运行于 arm 之上。简单的说就是 arm 上电复位,首先执行的代码,相当与 x86 的 BIOS。 如果想透彻了解,下面知识是必备的:c 语言,汇编基础,arm 的架构(对于 arm 底层开发,以及中断处理,假中断避免,调度算法,实时性分析都有很重要的帮助) ,arm 常用指令。这些东西分别可以从谭浩强、杜春雷的书上找到解决方法。最好还要知道一些可执行文件的知识(hex 和 bin,ads 产生的 axf 文件的差

12、别,用 utla 还是很容易看出 ro,rw,zi 上的区别,二进制文件头还是有些不同的) 。因为有操作系统和计算机组成原理的知识,所以进入下面一段,直入主题: 拿周立功 lpc2100 开发板带的启动文件说事,其全部引导相关文件如下: config.h*配置文件 lpc2XXX.h*此款芯片的头文件 target.h *目标头文件 heap.s*堆配置 stack.s*栈配置 IRQ.s*中断配置 startup.s*启动文件配置(VIC 等) mem_b.scf*分散和加载文件(mem_a.scf 类似,以 b 开刀) 下面分别分析每个文件: 打开文件 config.h,其中关于数据类型的

13、定义不用多说,是为了完成数据类型与平台无关,另一个重要的配置是系统晶振频率,VOC,VPB 的配置,这个根据手册要求配置(比如说CCO 频率,必须为 Fcclk 的 2、4、8、16 倍,范围为 156MHz320MHz) ,并且一定要和实际硬件相一致!这个文件,重要的就这一点点 接着是 lpc2XXX.h,里面是本处理器控制寄存器的地址宏定义, (*(volatile unsigned char *) 0xE01FC140)第一个*表示把后面的数字强制转换成地址,前面的*表示把宏定义的名称标为指向这个地址的指针。 Target.h,启动定义了一些 “空”函数给用户填写,用于处理特殊情况 he

14、ap.s 堆配置,仅仅分配了一个空间 stack.s 栈配置,与 heap.s 类似,作为异常向量入口及异常向量与 c 语言代码的接口 irp.s 这个文件值得研究,我把代码贴在下面,我把我懂的地方都注释了: NoInt EQU 0x80 /实际存储的地方 USR32Mode EQU 0x10 /定义 arm 各个模式 SVC32Mode EQU 0x13 SYS32Mode EQU 0x1f IRQ32Mode EQU 0x12 FIQ32Mode EQU 0x11 CODE32 AREA IRQ,CODE,READONLY MACRO $IRQ_Label HANDLER $IRQ_Exc

15、eption_Function EXPORT $IRQ_Label ; 输出的标号 IMPORT $IRQ_Exception_Function ; 引用的外部标号 $IRQ_Label SUB LR, LR, #4 ; 计算返回地址 STMFD SP!, R0-R3, R12, LR ;把寄存器压栈 保存任务环境 MRS R3, SPSR ; 把 spsR 保存到 R3 中 STMFD SP, R3,LR ; 保存 SPSR 和用户状态的 SP,注意不能回写 ; 如果回写的是用户的 SP,所以后面要调整 SP 表示在压栈同时,保存 SPSR NOP SUB SP, SP, #4*2 / 此句

16、不理解 MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式 BL $IRQ_Exception_Function ; 调用 c 语言的中断处理程序 (函数返回) MSR CPSR_c, #(NoInt | IRQ32Mode) ; 切换回 irq 模式 LDMFD SP, R3,LR ; 恢复 SPSR 和用户状态的 SP,注意不能回写 ; 如果回写的是用户的 SP,所以后面要调整 SP,此处功能相似(参考 uc/os arm 移至要点详解) MSR SPSR_cxsf, R3 ;装载之前的 SPSR ADD SP, SP, #4*2 ; 此句不理解 LDMF

17、D SP!, R0-R3, R12, PC ;恢复所有的调用之前寄存器状态 MEND startup.s 文件: 首先是定义每个栈的大小,然后是定义了一个 PINSEL2 根据芯片的 pdf 个人理解是设置pin 的调试功能。接着引入一系列外部声明的函数,然后是非常重要的中断向量表,是要放在逻辑 0x0 的地址(所有向量相加应该为 0!) ,然后是初始化各个堆栈,接着进入了复位中断函数,ResetInit BL InitStack ;初始化堆栈 BL TargetResetInit ;目标板基本初始化 B _main ;跳转到 c 语言入口 下面还有些加密函数等等,重要的是堆栈的地址定义,因为

18、有:MSR CPSR_c, #0xd2 LDR SP, StackIrq,这里会调用下面的地址定义,这里注意,使用的是 ldr 而不是 B,因为ldr 支持全范围的跳转,可以在 flash 内,而 b 不可以 StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4 StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4 StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4 StackAbt DCD AbtStackSpace + (ABT

19、_STACK_LEGTH - 1)* 4 StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH - 1)* 4 堆栈的空间分配根本上是在这儿: AREA MyStacks, DATA, NOINIT, ALIGN=2 SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆栈空间 IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;中断模式堆栈空间 FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ;快速中断模式堆栈空间 AbtStackSpace SPACE

20、 ABT_STACK_LEGTH * 4 ;中止义模式堆栈空间 UndtStackSpace SPACE UND_STACK_LEGTH * 4 ;未定义模式堆栈 请注意这里的 NOINT,他指的是 0x80 因为有 NoInt EQU 0x80 在先 最后一个很重要的文件是 mem_b.scf,他是用来分散加载文件的,为什么要这样,这在最后来讲,先看他具体内容: ROM_LOAD 0x40000000 /定义加载时域的名称为 ROM_LOAD 起始地址为 0x400000,什么是时域,上网查吧 ! ROM_EXEC 0x40000000 第一个运行时域的名称和加载地址 Startup.o (

21、vectors, +First) 将 startup.o 的文件的 vectors 加载到本域的起始地址 ,以下顺序加载其他只读代码 * (+RO) HEAP +0 UNINIT 这段的分析和上面分析类似,就不重复了 heap.o (+ZI) 当我们改变加载地址时候,当编译完成后,通过 cmd 的 fromelf -d 命令我们可以很清楚的看到,每段时域的基地址根据我们的设置是变化的,说明我的猜想是基本正确的。 下面分析具体的启动过程,首先 arm 加电后,pc 指的是 0x0 的地址,但是这个 0x0 的地址是逻辑地址,他在这个系统里对应的是 0x4000000000 这个地址,然后设置中断

22、向量表,初始化堆栈,初始化硬件包括锁相环的设置和连接(上面忘记把这段代码拿出来分析了,其实是很简单的注意些 0xaa,0x55(好像是这两个值)是不允许被打断的,否则无法连接锁相环),最后完成配置后跳转到 c 语言的 main 函数(此话在 startup.s 中,内容如下:) 。 ResetInit BL InitStack ;初始化堆栈调用相关语句 BL TargetResetInit ;目标板基本初始化,调用相关函数 B _main ;跳转到 c 语言入口,进入 c 环境 注:因为二进制代码的 RO,RW,zi 是按顺序存放的,但是在 arm 的地址空间分配中,并不是按照这种格式,有时要

23、把其他文件的 ro 放在本文件的后面,且地址也有严格的规定,这时候,就是分散文件加载和存放上场的时候了,就看到上面 ROM_EXEC 0x40000000 等语句了s3c2410(ARM9)启动代码分析 转贴发布:2009-12-31 09:57 | 作者: bing8687 | 来源:本站 | 查看:53 次 | 字号: 小 中 大from:http:/ 下 C 语言的入口方式和 ROM 镜像文件的生成 这部分介绍下 ADS 下如何生成可以运行的 ROM 镜像文件,我们知道当程序下载到 flash中运行的时候,对于 RW、ZI 数据就存在着两个环境,一个 load 环境,一个是 exec 环

24、境,有时候由于速度的需要 RO 数据也要重新加载,那么对 RO 数据也是有两个环境。编译器产生 ROM 镜像文件时候,这三块数据的存放依次为 RO、RW 、ZI,并且地址空间时连续的。但是到了运行的时候,RW 数据必须被拷贝到 SDRAM(SRAM)中以支持读写,这就是我们所谓的运行环境。那么就要有一段代码去完成这个任务,在本章中我们介绍如何生成这段代码。玩过 2410 的朋友都知道 2410 初始化代码中有一段搬运 RW 和 ZI 初始化的代码,没错,它确实能够在一定程度上完成上面所说的任务,只要我们在生成二进制可执行代码的时候在编译器链接项的地方填写正确的 RORW 地址,(比如 RO =

25、 0, RW = 0x30000000), 那么将程序下到 NOR flash 的零地址并从 nor flash 启动,启动代码会将 RWZI 数据弄到 0x30000000,程序就能跑起来了。但是各位有没有想过,怎么把 RO 代码弄到 SDRAM 中(有时候这是必须的,比方后面我将提到用 nor flash 的 bootloader 烧写 nor flash)?如果直接设 RO0x30000000,那么这段代码下载到 0 地址肯定跑不起来,除非是 ROPI,这个要求就高了。这里我们有必要从介绍 ADS 中规定的 C 语言入口开始,ADS 中从初始化汇编代码跳到 main 函数有两种方式,ma

26、in 和_main:1,在_main 入口的模式下,汇编代码的指令为 b _main, 编译器在跳转到 main 之前还要作一系列的工作,这其中就包括对运行环境的初始化,在中提到: copies nonroot(RO copy code * (Region$Table) ; RO/RW addresses to copy* (ZISection$Table) ; ZI addresses to zeroEXEC2 0x30000000 0x00800000*(+RO)SDRAM 0x30800000 0x00800000*(+RW,+ZI);Sections named Region$Tabl

27、e and ZISection$Table which contain the addresses of the code/data to be copied. 当然,在这种模式下,有些入口函数必须自己重定义,比如_user_initial_stackheap,具体参见 ADS 文档。2, main 入口模式即简单的跳转,这里起始不用“main”这个名字也无所谓。那么编译器不会作任何的初始化,所有运行环境的建立都要靠我们自己,这就是大家看到的那段搬运代码存在的理由。但是它实现一些简单的运行环境是可取的,如果用 scf 定义的复杂环境,虽然我相信是可以做到的,但是可能会比较麻烦。我还没深究。另外

28、,这里提一下 semihost,因为我们在看 ADS 的东西的时候经常出现这个词,我也一直受其困扰。这里我简单说一下自己的见解,semihost 仅仅是一种调试手段,它的机理就是利用 MULTI_IDE 等工具捕捉目标环境运行过程中产生的值为 0x123456 的 SWI 中断,然后向上位机的 ADS 软件发送对应的调试信息。对于我们最后的应用代码来说,都是 nonsemihost 类型的。如果我们在调试中使用 semihost,那么只要在最后重定义 ADS 中的一些使用到的库函数(比如 fputc),代码就可以从 semihost 向nonsemihost 的类型转变。不过到目前为止,我还没

29、体会到 semihost 的威力。2410 启动代码分析这一章主要对目前广泛流行的 2410 启动代码进行分析:S3C2410 的初始化代码主要涉及到对系统主要模块的配置、运行环境的建立、系统时钟、MMU 等模块的配置,下面按执行顺序依次都各个部分进行分析:程序入口:(ResetHandler)在程序一开始,首先进行的一些操作主要保证初始化程序能够顺利的运行, 因此主要包括关闭 WDT、中断,配置锁相环等。配置 memory 接口memory 接口是确保数据访问正确的基本保障,此处主要配置 SFR 寄存器中0x48000000 开始的 memory 接口寄存器组, 确保每个 bank 的位宽、

30、访问类型(waitable)以及时序参数正确。如果没有特别的要求,一般来说时序参数使用默认值即可。初始化堆栈ARM 有 6 种运行模式,必须为每一种模式提供独立的堆栈空间,在堆栈设置之前是不能进行 C 函数的调用的。ARM 的堆栈模式是从高地址递减的,我的所有代码统一将堆栈的首地址设在 0x33ff8000 处,往低依次为 FIQ、IRQ、Abort、Undef、SVC ,其中SVC 和 User 模式不予区分。堆栈大小一般可在头文件或者当前文件中修改。运行空间的初始化这段代码主要完成两个功能,一是将 RW 数据搬运到 RW 空间(我们生成 ROM 镜像时,RW 数据是跟在 RO 数据之后的)

31、,二是 初始化 ZI 数据段。当然,这段代码存在的前提是代码的运行环境只是标准的两段式:一段 RO 空间和一段 RW 空间;并且在 C 程序入口时没有调用编译器的链接库(_main)。后者已经提供相应的功能,并且支持更加复杂的运行环境定义(使用 SCF 文件),(关于这一点,我在介绍 ADS 中 C 代码的启动模式时已经详细介绍)。_rt_lib_init在 ADS1.2 的环境中,如果在 C 入口没有调用编译器的链接库(_main),那么在C 程序一开始要调用该函数以初始化运行时的函数库,以保证对 ADS 提供的某些库函数能够正常调用。从这个函数开始,我们已经在 C 语言环境下了。MMU 初

32、始化2410 的 MMU 支持 1 级2 级地址映射,在我们目前大部分应用中均采用 1 级section 模式的地址映射,一个 section 的大小为 1M,也就是说从逻辑地址到物理地址的转变是这样的一个过程:一个 32 位的地址,高 12 位决定了该地址在页表中的 index,这个 index 的内容决定了该逻辑 section 对应的物理 section; 低 20 位决定了该地址在 section 中的偏移(index )。因此从 0x00xffffffff 的地址空间总共可以分成 0x1000(4K )个 section,页表中每项的大小为 32 个 bit,因此页表的大小为 0x4

33、000(16K)。在我的代码中所有程序的页表统一存放在地址 0x33ff8000。每个页表项的内容如下:bit: 31 20 19 12 11 10 9 8 5 4 3 2 1 0content: Section 对应的物理地址 NULL AP 0 Domain 1 C B 1 0最低两位( 10)是 section 分页的标识。AP:Access Permission,区分只读、读写、SVC其它模式。Domain:每个 section 都属于某个 Domain,一个有 16 个 Domain,每个 Domain的属性由 CP15 的 R3 寄存器控制。在我得所有程序中,都只包含两个 Doma

34、in,一个是SFR 地址以下(包括 SFR)的空间,可访问; 另一个是 SFR 以上的空间,不可访问。C、B:这两位决定了该 section 的 cachewrite buffer 属性,这与该段的用途(RO or RW)有密切关系。不同的用途要做不同的设置。C B 具体含义0 0 无 cache,无写缓冲,任何对 memory 的读写都反映到 ASB 总线上。对 memory 的操作过程中 CPU 需要等待。0 1 无 cache,有写缓冲,读操作直接反映到 ASB 总线上。写操作 CPU 将数据写入 到写缓冲后继续运行,由写缓冲进行 ASB 操作。1 0 有 cache,写通模式,读操作首

35、先考虑 cache hit;写操作时直接将数据写入写缓冲,如果同时出现 cache hit,那么也更新 cache。1 1 有 cache,写回模式,读操作首先考虑 cache hit;写操作也首先考虑cache,如果 hit,则只修改 cache,并将 cache 对应半行的 dirty 比特置位;如果 miss,则写入写缓冲,触发 ASB 总线操作。在我的程序中内存空间的分配统一采用了文末的 MEMORY 图。虽然 MMU 只是使用了逻辑地址到物理地址的 linear transfer(值不改变),但是由于 MMU 能够引入cachewrite buffer,因此系统性能有很大的提高!配置

36、时钟比、重新设置 PLL2410 内部有三个时钟:FCLK 、HCLK、PCLK,分别供 CPU、AHB 总线和 APB 总线使用,为了降低功耗,一般都选择周期比为 1:2:4 的合理配置。 同时将 PLL 配置为运行环境时钟,一般都达到最高 202M。IO 初始化将 IO 口配置为对应的功能选项,同时一般会点亮相应的 LED 灯。中断初始化2410 的内存空间没有 remap 的机制,应该中断入口时钟位于零地址。因此中断服务机制可以描述如下:首先,不管使用那种启动方式,必须确保一下代码段位于内存的 0x0 地址:b ResetHandler b HandlerUndef ;handler f

37、or Undefined modeb HandlerSWI ;handler for SWI interruptb HandlerPabort ;handler for PAbortb HandlerDabort ;handler for DAbortb . ;reservedb HandlerIRQ ;handler for IRQ interrupt b HandlerFIQ ;handler for FIQ interrupt除 ResetHandler 外,其余各项都是由如下的宏定义的一段代码:HandlerFIQ HANDLER HandleFIQMACRO$HandlerLabel

38、 HANDLER $HandleLabel$HandlerLabelsub sp,sp,#4 ;decrement sp(to store jump address)stmfd sp!,r0 ;PUSH the work register to stackldr r0,=$HandleLabel ;load the address of HandleXXX to r0ldr r0,r0 ;load the contentsstr r0,sp,#4 ;store the contents(ISR) of HandleXXX to stackldmfd sp!,r0,pc ;POP the wor

39、k register and pc(jump to ISR)MEND这段代码的含义是通过堆栈将中断向量表中的内容赋给 PC 指针(如 HandleFIQ 是存放着 FIQ 服务程序入口地址的地址),自然程序就跳到相应的入口地址。可见,中断向量表存放的是各个中断服务程序的入口地址,它是用来被加载的,而并不是可执行代码。为了统一,所有示例程序都将中断向量表放在 0x33ffff00 开始的地址,并根据入口地址依次排列。需要注意的是如果各种模式的服务程序用 C 语言定义,那么类型必须用_irq 定义,以保证能够正确返回。初始化串口串口统一选用 UART0,模式采用 115200、1bit STOP、

40、No Parity。最后跳转到我们自己的应用程序!附:我得程序所使用的地址空间结构以及 MMU 中 C、B 的设置:Blank Area: RW_FAULT 0x5b000000 0xffffffffSram Req而 DRAM 因为有动态刷新和地址线复用等特性,通常配有专用的存储器端口。存储器端口的接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要考虑到由此带来的稳定性问题。存储器地址分布一种典型的情况是启动 ROM 的地址重映射。初始化堆栈因为 ARM 有 7 种执行状态,每一种状态的堆栈指针寄存器(

41、SP)都是独立的。因此,对程序中需要用到的每一种模式都要给 SP 定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,让后给 SP 赋值。注意:不要切换到 User 模式进行 User 模式的堆栈设置,因为进入 User 模式后就不能再操作 CPSR 回到别的模式了,可能会对接下去的程序执行造成影响。这是一段堆栈初始化的代码示例,其中只定义了三种模式的 SP 指针:MRS R0,CPSRBIC R0,R0,#MODEMASK 安全起见,屏蔽模式位以外的其他位ORR R1,R0,#IRQMODEMSR CPSR_cxfs,R1LDR SP,=UndefStackORR R

42、1,R0,#FIQMODEMSR CPSR_cxsf,R1LDR SP,=FIQStackORR R1,R0,#SVCMODEMSR CPSR_cxsf,R1LDR SP,=SVCStack初始化有特殊要求的端口,设备初始化应用程序执行环境映像一开始总是存储在 ROMFlash 里面的,其 RO 部分即可以在 ROMFlash 里面执行,也可以转移到速度更快的 RAM 中执行;而 RW 和 ZI 这两部分是必须转移到可写的 RAM 里去。所谓应用程序执行环境的初始化,就是完成必要的从 ROM 到 RAM 的数据传输和内容清零。下面是在 ADS 下,一种常用存储器模型的直接实现:LDR r0,=

43、|Image$RO$Limit| 得到 RW 数据源的起始地址LDR r1,=|Image$RW$Base| RW 区在 RAM 里的执行区起始地址LDR r2,=|Image$ZI$Base| ZI 区在 RAM 里面的起始地址CMP r0,r1 比较它们是否相等BEQ %F10 CMP r1,r3LDRCC r2,r0,#4STRCC r2,r1,#4BCC %B01 LDR r1,=|Image$ZI$Limit|MOV r2,#02 CMP r3,r1STRCC r2,r3,#4BCC %B2程序实现了 RW 数据的拷贝和 ZI 区域的清零功能。其中引用到的 4 个符号是由链接器第一输

44、出的。|Image$RO$Limit|:表示 RO 区末地址后面的地址,即 RW 数据源的起始地址|Image$RW$Base|:RW 区在 RAM 里的执行区起始地址,也就是编译器选项 RW_Base 指定的地址|Image$ZI$Base|:ZI 区在 RAM 里面的起始地址|Image$ZI$Limit|:ZI 区在 RAM 里面的结束地址后面的一个地址程序先把 ROM 里|Image$RO$Limt|开始的 RW 初始数据拷贝到 RAM 里面|Image$RW$Base|开始的地址,当 RAM 这边的目标地址到达|Image$ZI$Base|后就表示 RW 区的结束和 ZI 区的开始,

45、接下去就对这片 ZI 区进行清零操作,直到遇到结束地址|Image$ZI$Limit|改变处理器模式因为在初始化过程中,许多操作需要在特权模式下才能进行(比如对 CPSR 的修改),所以要特别注意不能过早的进入用户模式。内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做总是安全的。呼叫主应用程序当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:IMPORT mainB main直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。在 ARM ADS 环境中,还另外提供了一套系统级的呼叫机制。IMPORT

46、 _mainB _main_main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到 main()函数。嵌入式系统资源有限,程序通常都固化在 ROM 总运行。ROM 中程序执行前,需要对系统硬件和软件运行环境进行初始化,这些工作是用汇编语言编写的启动程序完成。启动程序是嵌入式程序的开头部分,应与应用程序一起固化在 ROM 中,应首先在系统上运行的启动程序应包含各模块中可能出现的所有段类,并合理安排他们的次序。 启动程序一般流程如下: (1) 设置入口指针 (2) 设置中断向量 ARM7 要求中断向量必须设置从 0 地址开始,连续 8*4 字节的空间,

47、分别是复位、未定义指令错误、软件中断、预取指令中断、数据存取错误、IRQ、FIQ 和一个保留的中断向量 (如果 ROM 位于 0 地址,向量表包含一系列指令跳转到中断服务程序,否则向量必须被动态初始化。可以在启动程序中添加一段代码,使其在运行时将向量表拷贝到 0 地址开始的存储空间) 对于各未用的中断,用一个只包含返回指令的哑函数,以防止错误引起系统的混乱。 (3) 初始化堆栈和寄存器 取决于使用了哪些中断,一般系统需要处理哪些错误类型。一般来说管理者堆栈必须设置,如果使用了IRQ 中断,则 IRQ 堆栈必须设置。如果系统使用了 DRAM 或者其他的外设,则需要设置相关的寄存器,以确定其刷新频

48、率,数据总线宽度等信息 (4) 初始化存储器系统 有些芯片可通过寄存器编程初始化存储器系统,而对于复杂系统通常集成了 MMU 来管理内存 (5) 如果有必要改变处理器模式、状态 如果系统应用程序是运行在用户模式下,可在此处将系统改为用户模式并初始化用户的堆栈指针。 (6) 初始化 C 语言所需要的存储器空间 为正确运行应用程序,在初始化期间应将系统需要读写的数据和变量从 ROM 拷贝到 RAM 里;一些要求快速响应的程序,如中断处理程序,也需要在 RAM 中运行; 如果使用 FLASH,对 FALSH 的檫除和写入也一定要在 RAM 里运行。 ARM 公司软件开发工具包中的链接器提供了分布装载的功能,可以实现这一目的。 (7) 呼叫 C 语言 ARM 有两种指令集

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报