1 重映射的理论模型
要理解重映射,必须首先理解映射。在此给出映射的基本理论模型,如图1所示。
用公式表示为其中,A表示输入域,B表示输出域,F(X)表示规则。A在规则F(X)下能够与B对应,这种对应关系就是“映射”。
在嵌入式系统中,这个模型应用比较普遍,可以完全用硬件实现,也可以硬件和软件协同实现。根据作用时间的不同,第1次称为“映射”,后面的就称之为“重映射”。基本理论相同,实现方式也类似。在不影响理解的情况下,以下不区分“映射”和“重映射”这两个术语。嵌入式系统中,重映射对应的输入、输出都是地址数据。下面举一个完全硬件实现重映射的简单实例:
令A={0x00000000-0x000fffff},实现到B={0x00100000-0x001fffff)的重映射。可以看出,它们的不同在于A20-B20这对地址线,所以硬件实现只需对此进行处理就可以了。如图2所示,无论A20为高电平还是低电平,对应的B20都是高电平。CPU访问(0x00000000-0x000fffff),或者{0x00100000-0x001fffff),在映射关系下,实际都是访问到{0x00100000-0x001fffff)。通过改变映射关系,可以把{0x00000000-0x000fffff)这个1 MB的地址空间映射到任意位置。这样就可以实现CPU从固定位置取值,但是实际对应的物理存储介质可以不同了。
根据重映射的不同需求,F(X)对应的复杂度也不同。为了分析方便,在AT91RM9200的重映射机制分析中,F(X)就作为一个黑匣子处理。其内部实现的细节这里不作探讨。
2 AT91RM9200的实现方案及启动流程
AT91RM9200上电或复位之后的内存映射关系可以参考其数据手册的Figure 8-1,这里不作重点分析。内存映射完成后,memory controller控制硬件实现重映射(要注意,这是不提供给用户的,也就是说用户无法改变这种映射规则)。下面用伪代码描述:
if BMS为高电平
F(X):boot memory→ROM else
F(X):boot memory→NOR Flash
其中,BMS是启动模式选择引脚。它决定了两种不同的映射关系,因而也决定了U-boot至少可以有与之对应的两种启动模式。
AT91RM9200通过寄存器MC_RCR为用户提供了接口,可以控制重映射。不过这种控制规则仍然是有限的。内存映射完成后,映射规则按照上述伪代码执行。如果执行重映射,不管BMS状态如何,规则变为“F(X):boot memory→SRAM”。再次执行重映射,F(X)将恢复到伪代码描述状态。这个重映射的具体执行手段,就是往MC_RCR写入1。
明确了AT91RM9200的映射机制,就可以对启动流程进行深入分析了。
AT91RM9200数据手册在“13 boot program”一节中对内部启动流程讲解得比较清晰,所以对这部分简略描述。对复杂机制,如果采用情景分析的方法,则会清晰许多。根据AT91RM9200的特点,提出了3种情景。这3种情景分别对应U-boot 启动的3种不同模式。
情景1:令BMS为高电平,系统上电或复位之后从片内启动。通过Xmodem协议上传loader.bin,其执行成功后,再次通过Xmodem协议上传U-boot.bin,实现U-boot的正常启动。
情景2:令BMS为低电平,系统上电或复位之后从片外NOR Flash启动。此处在0x10000000固化boot.bin,在0x10010000处固化U-boot.bin.gz,实现U-boot压缩方式的正常启动。
情景3:令BMS为低电平,系统上电或复位之后从片外NOR Flash启动。此处在0x10000000固化U-boot.bin,实现U-boot的非压缩方式的正常启动。
这3种情景各具特点。其中情景1适用于测试阶段,U-boot还没有固化到Flash中;如果U-boot比较大,考虑代码空间,则可以采用情景2;如果对启动时间要求非常短,则可以考虑精简u-boot的代码,采用情景3实现快速引导启动。
下面通过U-boot的生命周期和boot memory映射规则变化来对3种情景进行对比分析。
情景1:片内启动,loader.bin+U-boot.bin
硬件上电,检测到BMS为高电平,F(X):boot memo-ry→SROM。那么CPU从0x0处取指执行,实际上执行的就是SROM中的代码。当无法发现有效序列的代码后,自动执行uploader程序,将loader.bin放到SRAM中,完成后执行重映射,将PC置为0,这样实际上就开始执行上载到SRAM中的loader.bin程序。此时,F(X):bootmemory→SRAM。
loader.bin将用户上传的U-boot.bin下载到SDRAM中,然后跳转到U-boot的起始位置开始执行。在这种情景中,U-boot.bin的生命之初就在SDRAM中,而且此时F(X):boot memory→SRAM。
情景2:片外启动,boot.bin+U-boot.bin.gz
上电或复位后,F(X):boot memory→Nor Flash。首先执行boot.bin,它的作用就是初始化SDRAM,然后解压U-boot.bin.gz,放到SDRAM中。然后调转到相应位置,执行U-boot。在这种情景中,Uboot.bin的生命之初也是在SDRAM中,不过此时F(X):boot memory→NORFlash。
情景3:片外启动,U-boot.bin
上电或复位后,F(X):boot memory→NOR Flash。开始执行U-boot,这样U-boot将自身下载到SDRAM中,然后跳转至相应位置执行。在这种情景中,U-boot.bin的生命之初在NOR Flash中,然后到SDRAM。不过在这整个过程中,F(X):boot memory→NOR Flash。
通过上述分析和表1,就可以对3种情景U-boot的执行初始状态非常清晰了。
3 U-boot的不合理性分析
从ftp://ftp.denx.de下载最新的U-boot-1.3.O。对AT91RM9200而言,入口位于cpu/arm920t/start.S。此段代码为:
代码的作用就是把u-boot开始的0x40个字节复制到0x0开始的位置,也就是实现了中断向量表的搬移。结合表1,对情景是没有问题的,这样实际就复制到了SRAM中;但是,对于情景2和3,就是不合理的了。因为此时0x0位置是NOR Flash,它们都是不可以直接以字节写入的。也就是说,对情景2和情景3,这段代码不足以完成将中断向量表复制到SRAM中,并将SRAM映射到0x0开始的1 MB地址空间内这个任务。
为了验证此结论,提出一个检测算法1:
向0x0位置写入0x55,然后读取0x0的数值,看看是否为0x55。如果是,说明此处为SRAM;如果不是,说明此处为非易失性存储介质。
利用这个算法,可以在lib_arm/board.c中插入测试代码,验证表1结论的正确性。经实验分析,表1是正确的。这样也就间接证明了上述代码的不合理性。
如果想要直接测试,也可以提供一个简单算法2:
在lib_arm/board.c中插入测试代码,读取从0X0开始的0x40个字节,然后与U-boot.bin起始位置的0x40个字节对比,看看是否一致。
4 解决方案
为了对三种情景都支持,就需要根据3种情景的特点来进行区分,如表2所列,这样才可以实现3种启动方式的无关性。这里需要解决的问题是,通过检测算法1可以判断出情景1;然后判断此时U-boot是在SDRAM还是在NOR Flash,可以区分情景2和情景3。
对情景1和情景2,因为此时U-boot已经在SDRAM中,所以是否执行重映射对U-boot本身的执行并无影响。但是对情景3,此时U-boot仍在NOR Flash中,boot memory仍然指向NOR Flash。一旦执行重映射,boot memory会立即指向SRAM,那么PC下一条指令就无法正常获取了。为了保证其正常获取,必须把跳转到SDRAM之前的代码复制到SRAM中,这样重映射前后就会实现无缝转换。(当然,这种实现方式对于start.S代码比较大的情况不合适。如果是那样,可以采取另外的解决办法,就是在lib_arm/board.c中通过算法1来决定是否执行重映射。这样复制的长度就可以统一为0x40个字节了。)
制作patch,主要修改的代码部分如下:
5 实验结论
编写代码实现上述修正之后,经过测试,在3种情景下,U-boot都可以正常运行。说明上述分析是正确的。实现3种情景的启动无关性,需要充分把握重映射机制,对每一步情景都要清晰,这样才可以很好地设计出启动方式无关性的代码。情景分析的研究方法,对很多的开发工作都有很大的借鉴作用。