LPC32XX内置有16K的ROM(称之为IROM)。里面包含一个叫做Bootstrap的程序。在CPU复位后,会自动将IROM映射到地址为0x0的地址范围,从而上电后自动运行Bootstrap。这个过程是无须用户干涉的,也无法改变的。正是通过这个机制,引申除了LPC32XX系列芯片灵活的启动功能。

既然复位后会自动运行Bootstrap,那么我们就来看看Bootstrap到底做了些什么。虽然没有Bootstrap的源代码,但是LPC32XX用户手册(第35章 图129)上详细列出了Bootstrap的执行流程。简单的归纳如下:

  • 先检测Service_N这个引脚是否有效,如果有效则进入Service Boot模式(亦即串口启动模式)。否则往下进入正常启动模式;Service Boot模式会通过和串口交互,以获取启动参数和启动代码。如果交互失败,则仍然进入正常启动模式。

  • 正常启动模式先通过SPI接口,读取SPI启动校验码(0x13579BD1)和SPI启动镜像大小。然后校验这个校验码和启动大小。如果任何一个校验不通过,则往下进入EMC接口启动方式。如果校验都通过了,则通过SPI接口将启动代码复制到IRAM。接着跳转到IRAM中(0x80000000),将控制权交给IRAM中从SPI读取到的启动代码。

  • 接着通过EMC(EMC:静态存储控制器,比如NorFlash)接口,读取EMC启动校验码,如果校验通过,则直接跳转到EMC接口对应的地址(0xE0000000)处执行,控制权交给EMC代码。否则继续往下进入NAND Flash接口启动。

  • 最后,通过NAND Flash接口启动。这种方式会作一些NAND Flash接口的初始化操作。然后读取NAND Flash的第0页数据,并通过一定的规则校验。如果校验成功,则把跳转到IRAM中,把控制权交给IRAM中从NAND Flash读取到的数据。如果这种方式启动也失败了。那么CPU就会重新复位重新启动。

流程大致如此,具体的细节本文不去深究。本文的主题是启动地址。从上面的流程可以看出,除了EMC方式是直接跳转外,其他方式都是(在完成一定规则的校验后),将对应接口的代码读取到内部的IRAM中,然后跳转到IRAM中执行。

在说启动地址前。我们先说说启动地址是指什么。在程序(比如uboot)编译的时候,可能会指定很多地址,比如下载地址、运行地址、链接地址等等。这里我们不去纠结这些名字。我们就记住这里对【启动地址】的定义。简单的说就是代码的运行时候的起始地址。也就是说你的代码编译成机器码并下载到板子上运行的时候,第一条指令所处的地址。取得这个地址的方法很简单,在编写代码时,给代码的入口指令设置一个(汇编)标号,然后通过adr r0,_start即可将启动地址写入到r0寄存器了。

由前面的分析可知:当LPC32XX通过EMC方式启动的时候。代码的下载地址和启动地址就是一样,0xE0000000。不相信可以自己验证。这个不存在什么问题。关键是其他几种方式。

当通过SPI启动(串口启动类似)的时候,代码会被复制到IRAM中。LPC32XX手册上说的是地址为0x0处。但通过实际分析我们知道,这个时候0x0是映射给ROM的,显然是无法写入的。实际情况(通过代码实测证明),这里是复制到0x80000000处。然后跳转到这里执行的。

需要特别注意的,如果你和我一样,直接把EMC启动的校验码0x13579BDm(根据EMC位宽不同,m取0、1、2、3)嵌入到程序(uboot)镜像中。并且在制作用于SPI启动的EEPROM/Flash的时候为了省事儿,直接将用于EMC启动的镜像前面加上一个SPI启动校验码0x13579BDF,然后将EMC启动的校验码直接修改为镜像的大小。那么你可能会遇到程序中读出来的启动地址是0x08FF_FFFC。这中情况出现的原因很简单。因为SPI启动的时候Bootstrap是从SPI存储器上的0x8地址开始复制数据的,也就是说刚好跳过了我们程序的第一个字(就是EMC的校验码)。而adr指令是参考的是EMC校验码的位置,因此就会出现这种情况了。