为什么要重映射
像51等8位机,程序直接在ROM或Flash中跑,8位的ROM或Flash足够匹配51的主频,不用插入等待指令
而ARM作为32位机,需要程序在RAM中跑才行,Flash一般是8位或16位(速度达不到,32位的也有吧,好像价格很高;如果程序跑在Flash中就要插入过多的等待指令,所以会影响ARM的性能)
ARM的中断向量表,既存放各个中断入口地址的地方,一般放在0x0处,即ROM或Flash中,为了加快中断响应速度,也应该将0X0映射到RAM中去。因此,ARM一般从ROM或Flash启动完成初始化,然后将应用程序拷贝到RAM,然后跳到RAM执行。
向量表映射到RAM(加快相应速度)
为了加快启动的速度,也方便可以更改异常向量表,加快中断响应速度,往往把异常向量表映射到更快、更宽(32bit/16bit)的RAM中。但是异常向量表的开始地址是由ARM架构决定的,必须位于0x0处,因此,必须把RAM映射到0x0
就是加快启动的速度和异常处理速度,一定要初始化异常堆栈和建立异常向量表的。
重映射的过程
复位后从初始位置取指令 比如,基于 ARM7TDMI内核的 CPU在复 位时通常都从地址 0x00000000 处取它的第一条指 始的存储空间内,因此 Bootloader 是系统加电后、操作 系统内核或用户应用程序运行之前,首先必须运行的 一段程序代码。对于嵌入式系统来说,有的使用操作 系统,也有的不使用操作系统,比如功能简单仅包括 应用程序的系统,但在系统启动时都必须执行 Boot- loader,为系统运行准备好软硬件运行环境。 一般来说,在嵌入式世界里BootLoader 是严重地依赖于硬件的,因此想建立一个通用的 BootLoader 几乎是不可能 PC机BIOS的作用就象嵌入式系统中的Bootloader,都是最底层的引导软件,初始化主板的基本设置,为接收外部程序做硬件上的准备。但与Bootloader不同的是,BIOS在装载OS系统的同时还传递一些参数设置,而Bootloader只是简单的装载系统 loader S入口 bootloader 依赖于硬件 )嵌入式系统中,Bootloader的意义与作用与PC上的BIOS有点类似,它对开发板上的主要部件如CPU、SDRAM、FLASH、串口等进行了初始化,也可以使用Bootloader下载文件到开发板和启动系统等。因此,一个功能比较强大的Bootloader已经相当于一个微型的操作系统了。 可选择从APROM或者LDROM开始 main.c并不是程序的起点。其实在main之前还要做很多事情。就程序来说,还有需要要做的事情在main前,比如init和begin,但这些都被编译器默认执行了。 startup.s 函数执行之后就会跳到main.c函数继续执行。 在Startup.s文件中包含一个startup的入口函数,该函数为EBOOT的最开始的入口。在系统上电或者冷启动的时候,这是第一个被执行的函数。该函数都是由汇编语言编写的,完成基于硬件平台的最初的初始化,也就是CPU的相关初始化,如果有必要,也可以在这里对外围的设备进行初始化。该函数执行到最后,会跳转到C语言的入口,一般是Main函数,或者叫做EBootMain函数。 前些天写过,我在学ARM的过程中,虽然看了书,对ARM的体系结构有了了解,可是当我实际想操作操作,写个程序的时候,却发现,真正的启动文件里面,还有很多书里面没有讲过的东西,看不懂。 为了弄懂这些看不懂的部分的含义,让自己真正能做到可以自己写程序,我把ADS带的英文帮助文件仔仔细细看了一遍,这才算明白一点了,前两天自己说现在把STARTUP.S里面所有的东西都弄明白了,其实也不是,但是大部分算是明白了。 我自己试着写了个启动程序,然后用C写了个LPC2220的外部中断的程序,两部分结合起来,用ADS编译,在ZLG的开发板上调试通过。 下面就是我写的STARTUP.S程序,贴在这里,说一下我对各部分的理解吧。文件中的注释是我当时加的,现在加的说明我用颜色区别开。程序主要参考了KEIL的启动文件,所以像堆栈设置等部分,方法也和KEIL的一样。 ; A startup file for ARM by myself ; defination for mode (used for change work mode to setup stack) 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 I_BIT EQU 0X80 ; I bit set to 1 to disable IRQ F_BIT EQU 0X40 ; F bit set to 1 to disable FIQ ; defination of stack size UND_StackSize EQU 0X00*4 SVC_StackSize EQU 0X02*4 ABT_StackSize EQU 0X00*4 FIQ_StackSize EQU 0X20*4 IRQ_StackSize EQU 0X20*4 USR_StackSize EQU 0X100*4 ISR_StackSize EQU (UND_StackSize + SVC_StackSize + ABT_StackSize + \ FIQ_StackSize + IRQ_StackSize) 这个ISR_StackSize就是指所有中断程序的堆栈的尺寸之和,用来在后面计算堆栈top地址用的 AREA STACK, NOINIT, READWRITE, ALIGN=4 USR_Stack space USR_StackSize 定义堆栈的空间,usr在下面,isr的在上面 USR_StackTop space ISR_StackSize StackTop 在这里放个标号,以获得堆栈顶部的地址 Heap_Size EQU 0x100 heap好像主要是给c程序动态分配的内存使用的,如果不用 malloc, new, printf一类的,好像也可以不要(我的理解,不一定对阿) AREA HEAP, NOINIT, READWRITE, ALIGN=4 __heap_base Heap_Mem SPACE Heap_Size __heap_limit AREA RESET, CODE, READONLY ENTRY CODE32 Vectors LDR PC, Reset_addr LDR PC, Undef_addr LDR PC, SWI_addr LDR PC, PAbt_addr LDR PC, DAbt_addr NOP ; RESERVED VECTOR LDR PC, [PC, #-0X0FF0] ; FOR VIC, FOR NORMAL USE LDR PC, IRQ_addr LDR PC, FIQ_addr Reset_addr dcd Reset_handler Undef_addr dcd Undef_handler SWI_addr dcd SWI_handler PAbt_addr dcd PAbt_handler DAbt_addr dcd DAbt_handler FIQ_addr dcd FIQ_handler Undef_handler b Undef_handler SWI_handler b SWI_handler PAbt_handler b PAbt_handler DAbt_handler b DAbt_handler FIQ_handler b FIQ_handler EXPORT Reset_handler Reset_handler LDR R0,=StackTop 先把r0设置为stack的top address MSR CPSR_c, #MODE_UND :OR: I_BIT :OR: F_BIT ; ENTER UNDEFINED MODE MOV SP, R0 ; SET SP TO THE TOP OF IT’S STACK SUB R0, R0, #UND_StackSize ; CALCULATE THE NEXT STACK’S TOP MSR CPSR_c, #MODE_SVC :OR: I_BIT :OR: F_BIT MOV SP, R0 SUB R0, R0, #SVC_StackSize 每设置完一种模式的堆栈,就减去,已得到下一种堆栈的top地址 MSR CPSR_c, #MODE_ABT :OR: I_BIT :OR: F_BIT MOV SP, R0 SUB R0, R0, #ABT_StackSize MSR CPSR_c, #MODE_FIQ :OR: I_BIT :OR: F_BIT MOV SP, R0 SUB R0, R0, #FIQ_StackSize MSR CPSR_c, #MODE_IRQ :OR: I_BIT :OR: F_BIT MOV SP, R0 SUB R0, R0, #IRQ_StackSize MSR CPSR_c, #MODE_USR ; NO I/F BIT SETTING MOV SP, R0 SUB SL, R0, #USR_StackSize ; SET SL THE BOTTOM OF USER STACK, I DON’T KNOW WHAT IT MEAN 这里我有点不明白,谁能给说一下啊? IMPORT __main __main是c环境的一个库函数,可以完成一些初始化的工作,然后跳转到用户的main() B __main IMPORT __use_two_region_memory 如果stack和heap在内存里分成了两段地址,就要用这个标志告诉编译器 EXPORT __user_initial_stackheap 如果使用了scatter文件来控制链接,则必须重载__user_initial_stackheap函数,否则编译器找不到heap的起始地址 __user_initial_stackheap LDR R0, = __heap_base 为什么这么写,我也不知道,还请明白人指点一下吧。 BX LR END BSP 纯粹的BSP所包含的内容一般说来是和系统有关的驱动和程序,如网络驱动和系统中网络 协议有关,串口驱动和系统下载调试有关等等。离开这些驱动系统就不能正常工作。 BSP介绍(Board Support Package)是介于底层硬件和上层软件之间的底层软件开发包,它主要功能为屏蔽硬件,提供操作系统及硬件驱动,具体功能包括: 1 单板硬件初始化,主要是CPU的初始化,为整个软件系统提供底层硬件支持; 2 为操作系统提供设备驱动程序和系统中断服务程序; 3 定制操作系统的功能,为软件系统提供一个实时多任务的运行环境; 4 初始化操作系统,为操作系统的正常运行做好准备。 PC机BIOS的作用更象嵌入式系统中的Bootloader(最底层的引导软件,初始化主板的基本 设置,为接收外部程序做硬件上的准备)。与Bootloader不同的是BIOS在装载OS系统的同 时,还传递一些参数设置(中断端口定义,…),而Bootloader只是简单的装载系统。 1.自己编写的程序 主循环 终端 2.移植现有的程序 bootloader 简单地说,bootloader就是在操作系统内核运行前运行地一段小程序。通过这段小程序,可以对硬件设备,如CPU、SDRAM、Flash、串口等进行初始化,也可以下载文件到系统板、对Flash进行擦除和编程,真正起到引导和加载内核镜像的作用,但是随着嵌入式系统的发展,bootloader已经逐渐在基本功能的基础上,进行了扩展,bootloader可以更多地增加对具体系统的板级支持,即增加一些硬件模块功能上的使用支持,以方便开发人员进行开发和调试。从这个层面上看,功能扩展后bootloader可以虚拟地看成是一个微小的系统级的代码包。 bootloader是依赖于硬件而实现的,特别是在嵌入式系统中。不同的体系结构需求的bootloader是不同的;除了体系结构,bootloader还依赖于具体的嵌入式板级设备的配置。也就是说,对于两块不同的嵌入式板而言,即使它们基于相同的CPU构建,运行在其中一块电路板上的bootloader,未必能够运行在另一块电路开发板上。 在专用的嵌入式板子运行 GNU/Linux 系统已经变得越来越流行。一个嵌入式 Linux 系统从软件的角度看通常可以分为四个层次: 1. 引导加载程序。包括固化在固件(firmware)中的 boot 代码(可选),和 Boot Loader 两大部分。 2. Linux 内核。特定于嵌入式板子的定制内核以及内核的启动参数。 3. 文件系统。包括根文件系统和建立于 Flash 内存设备之上文件系统。通常用 ram disk 来作为 root fs。 4. 用户应用程序。特定于用户的应用程序。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:MicroWindows 和 MiniGUI 懂。 引导加载程序是系统加电后运行的第一段软件代码。回忆一下 PC 的体系结构我们可以知道,PC 机中的引导加载程序由 BIOS(其本质就是一段固件程序)和位于硬盘 MBR 中的 OS Boot Loader(比如,LILO 和 GRUB 等)一起组成。BIOS 在完成硬件检测和资源分配后,将硬盘 MBR 中的 Boot Loader 读到系统的 RAM 中,然后将控制权交给 OS Boot Loader。Boot Loader 的主要运行任务就是将内核映象从硬盘上读到 RAM 中,然后跳转到内核的入口点去运行,也即开始启动操作系统。 而在嵌入式系统中,通常并没有像 BIOS 那样的固件程序(注,有的嵌入式 CPU 也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由 Boot Loader 来完成。比如在一个基于 ARM7TDMI core 的嵌入式系统中,系统在上电或复位时通常都从地址 0x00000000 处开始执行,而在这个地址处安排的通常就是系统的 Boot Loader 程序。 本文将从 Boot Loader 的概念、Boot Loader 的主要任务、Boot Loader 的框架结构以及 Boot Loader 的安装等四个方面来讨论嵌入式系统的 Boot Loader。
u-boot
① 开放源码; ② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS; ③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale; ④ 较高的可靠性和稳定性; ④ 较高的可靠性和稳定性; ⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等; ⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等; ⑦ 较为丰富的开发调试文档与强大的网络技术支持; ===== bootloader 修改 重映射PC=7f000000 开始于reset handler __main跳转到main函数 c文件 h文件 startup.s文件The NUC700 Boot Loader must be placed on the offset zero of the FLASH. For the NUC700 target platform, the boot ROM is remapped to 0x7F000000. Therefore, the boot loader’s starting address is 0x7F000000. 关于0x7F000000和重映射问题: 这个引导过程是这样的: 上电最开始都是从0x0地址ROM/Flash 处取指令的,要先从 ROM/Flash 启动完成最初初始化 然后Bootloader实现一个存储器重映射Remap,将SDRAM映射到0x0,将Flash地址挪到高地址去;具体是:将程序拷贝到SDRAM中去,地址是0x7F000000,然后将SDRAM地址重映射为0x0,将Flash地址映射为0x7F000000,SDRAM中程序和Flash中一样,程序接着前面的指令在SDRAM中继续执行 关键问题是没有copy FLASH的内容到SDRAM中去,而是直接映射到高地址,同时pc偏移到高地址继续执行 Boot ROM重映射到0x7F000000,bootloader的起始地址是0x7F000000 Remap的配置 Remap的实现和ARM处理器的实现相关。 1)如果处理器有专门的寄存器可以完成Remap。那么Remap是通过Remap寄存器的相应bit置1完成的。如Atmel AT91xx 2)如果处理器没有专门的寄存器,但是memory的bank控制寄存器可以用来配置bank的起始地址,那么只要把RAM的起始地址编程为0x0,也可以完成remap。如samsung s3c4510 3)如果上面两种机制都没有,那么Remap就不要做了。因为处理器实现决定了SDRAM对应的bank地址是不能改变的。如Samsung as described in NUC700 Bootloader Source Code Modification Guide.pdf The entry point of bootloader is in init.s. This assembly code will do following task sequentially. Disable interrupt, disable cache, set clock skew, remap memory, and configure SDRAM, set stack pointer, and then jump into __main function, which will call main function later. Bootloader的入口为init.s,它禁用中断、缓冲,重映射存储器,配置SDRAM,设置堆栈指针,然后跳转到main Right after boot up, all codes of bootloader resides in ROM, but before entering main function, some of the code and data will be copied to SDRAM by __main(), the location of each code and data section is decided by the scatter load file in the bootloader project. The vector table is also copied to address 0 during this process. 启动后,bootloader的所有代码位于ROM,但是在进入main之前,部分代码和数值需要复制到SDRAM,代码和数据部分的位置由分散加载文件中确定,在此过程中,向量表也被复制到地址0 整理了一下,对您以前的问题做个汇总回答 您前面讲的44B0用的是ARM Linker - Simple, 而这里用的是scattered,存在复杂的地址映射 44b0不支持重映射,这里支持重映射