1.引言
在开发基于AT91RM9200处理器的嵌入式系统时,以何种方式启动系统是一个首先要考虑的基本问题。庆幸的是,AT91RM9200处理器提供了各种各样的启动方式,总体上可分为从外部的DATAFLASH、二线EEPROM或8位并行存储器引导启动和从内部的BOOTROM引导启动两种情况。当从外部存储器启动时,存储器中的启动代码又是从那里来的呢?有3种手段,可以直接通过编程器将启动代码写入外部存储器,也可以通过JTAG接口从主机下载到目标系统的闪存芯片,还可以由AT91RM9200处理器的内部BOOTROM启动系统与主机建立通信并下载所需代码再写入闪存芯片。那么当从内部的BOOTROM启动时,所需的启动代码又是如何得到的呢?很简单,芯片厂商在生产芯片时就嵌入了这段代码。内嵌的启动代码被存储在AT91RM9200处理器的片内ROM中,片内ROM的起始物理地址是0x0010_0000,片内SRAM的起始物理地址为0x0020_0000。我们都知道ARM处理器启动时会产生复位异常,程序计数器PC指向复位异常向量地址0x0000_0000,也就是说启动时首先执行的是位于地址0x0000_0000处的指令。因此从0x0000_0000到0x0010_0000的1M的内部存储区域(内部存储区0)在上电启动时的代码将决定系统的启动过程。那么是应该由外部存储器中的启动代码来占据内部存储区0以实现外部启动,还是应该由位于0x0010_0000(内部存储区1)处的ROM中的内嵌启动代码来占据这一空间以实现内部启动呢?这就需要一个仲裁机制来进行选择。这就是AT91RM9200芯片的PA31/BMS引脚(在PQFP封装中为79脚,在BGA封装中为A10脚)。BMS即BootModeSelect(启动模式选择),若BMS=1,则将内部存储区1的数据映射至内部存储区0,即从内部的ROM启动;若BMS=0,则将外部存储器的区域0映射至内部存储区0,即从外部存储器启动。需要注意的是只有在上电启动时,该引脚具有启动模式选择的功能,此后便成为标准的I/O接口PA31。令BMS=1进行启动,则会在内部存储区0和1中具有完全相同的代码,即执行内部启动程序。内部启动程序主要包括两大部分:BootLoader和BootUploader。
2.BootLoader
BootLoader首先被执行,它要做的第一件事就是进行设备初始化,主要包括:通过设定PMC(PowerManagementController)中的相应寄存器得到主振荡器的频率和用于PLLB(PhaseLockLoopB)的合适的分频,从而使PLLB输出一48MHz的时钟信号,该信号是用于USB设备的必需时钟。为各种ARM模式建立堆栈。检测主振荡器频率。设定中断控制器。初始化C变量。跳转到main函数执行。接下来BootLoader的任务就是在片外“非挥发”的存储器中寻找有效的可执行代码,这些有效代码可以是应用程序也可以是第二级引导装入程序。查找有效代码的顺序是先查找与SPI(SerialPeripheralInterface)的NPCS0连接的串行DATAFLASH,然后是与TWI(Two-WireInterface)相连的串行EEPROM和连向EBI(ExternalBusInterface)的NCS0的8位并行存储器。在查找串行DATAFLASH与串行EEPROM时需先发送一个读指令,然后根据是否收到设备准备好的ACK(应答使能)信号决定是否继续在该设备上查找。查找有效的可执行代码的依据是分析相应存储器的开始32字节的代码,这是因为如果某存储器的代码有效,则该存储器的代码将被下载到AT91RM9200处理器的片内SRAM的起始地址(0x0020_0000)处,随后该地址就会被映射到0x0000_0000并执行。也就是说,外部存储器的起始32字节的内容其实就是ARM的异常向量入口处应执行的代码,通常情况下,该处内容应为跳转指令以使当ARM发生异常时转向不同的处理程序进行处理。所以,如果某存储器的起始32字节中是ARM的跳转指令代码或向PC寄存器装入偏移地址的指令则认为该存储器中的代码有效。下面是一个有效异常向量的例子:00ea00000bB0x2c04e59ff014LDRPC,[PC,20]08e59ff014LDRPC,[PC,20]0Ce59ff014LDRPC,[PC,20]10e59ff014LDRPC,[PC,20]1400001234LDRPC,[PC,20]18e51fff20LDRPC,[PC,-0xf20]1Ce51fff20LDRPC,[PC,-0xf20]找到有效代码后接下来就是将代码下载到处理器的片内SRAM中,但是应该下载的代码到哪个位置截止呢?也就是代码的大小有多少呢?位于起始地址0x18处的一个字长(4字节)的代码也就是重新映射后会成为第6个异常向量的存储单元中包含了所需的信息。其结构如下表所示:3117161312870存储器页面大小页面位数Nb保留要下载的块(512字节)数例如,对于型号为AT45DB642的DATAFLASH来说有11776字节代码要下载,则其第6异常向量内容为:0x0841A017(000010000010000011010000000010111b)含义为:要下载的代码大小:0x17×512字节=11776字节;存储器页面数(1101b):13==>213=8192存储器页面大小(0000100000100000)=1056对于串行EEPROM和8位并行FLASH,只需计算其需下载的代码大小即可。对存储器的异常向量分析并得到代码大小的有关信息后,接下来BootLoader要做的就是将确定大小的代码下载到处理器片内SRAM处(0x0020_0000),下载完毕则复位处理器外围寄存器并进行存储器的重新映射,重新映射可通过改写MC_RCR(MemoryController_RemapControlRegister)的RCB位为1完成,通过重新映射使片内SRAM的内容映射到0x0000_0000处,然后通过将PC置0开始执行下载的应用代码。在上述从AT91RM9200处理器内部ROM启动然后在外部存储器中找到有效的应用代码并下载到片内SRAM进行执行的过程中有几点应引起注意:从片外存储器下载的代码大小应小于处理器片内SRAM的大小。串行EEPROM在TWI总线上的物理地址必须为0。有效代码总是从片外存储器的0x0000_0000地址处下载到片内SRAM,在重新映射后下载代码地址为片内0x0000_0000处。下载的代码应与其所处存储位置无关或可链接至地址0x0000_0000处。DATAFLASH必须是与SPI的NPCS0相连。8位并行FLASH必须是与EBI的NCS0相连。
3.BootUploader
当BootLoader不能得到串行DATAFLASH和EEPROM的ACK应答或在外部存储器中找不到有效的可执行代码时,BootUploader便被激活执行。BootUploader的主要任务就是建立外部通信通道并从该通道上传可执行代码到0x0020_0000的片内SRAM起始位置处。AT91RM9200处理器的BootUploader提供了两种通信的方式,即调试串口和USB设备接口。BootUploader将调试串口初始化为115200波特、8位数据、无奇偶校验位、1位停止位并以发送字符“C”(0x43)的方式启动Xmodem协议建立通信。任何运行该协议的终端都可用来向目标系统传送应用代码。在使用USB设备接口进行通信时需要一个48MHz的USB时钟,这一点已经在设备初始化中通过对PLLB配置实现了,另外通信时使用的是DFU(DeviceFirmwareUpgrade)协议,关于这一点请读者参考相关的资料。考虑到内部启动代码及协议运行使用的变量和堆栈会占用一部分SRAM,在通信建立后上传时为避免发生错误,应使上传的应用代码应比处理器片内SRAM的容量小至少3K。和从外部存储器下载有效代码之后相同,上传完成后便开始复位外围寄存器、关中断并进行重新映射,映射后片内SRAM的起始地址成了0x0000_0000,片内ROM的地址为0x0010_0000。接下来PC被置0,于是开始执行刚刚上传的应用代码。当目标系统采用移植操作系统等较大代码时,仅仅由片内的有限的SRAM是不可能满足运行的需要的,因此也不可能由BootUploader直接将这类代码长传并执行。一般的做法是先上传一段由用户自己开发的引导代码,该代码独立于嵌入式操作系统且与目标应用程序无关,其主要作用是初始化硬件系统,特别是片外FLASH和RAM等存储器和串行通信口或以太网接口,从而为将庞大的嵌入式操作系统或应用程序进行可靠快速的上传并能由足够的空间运行或存储提供先决条件。