09年在MPC8536E上做启动开发的工作,当时的目标就是将“从NOR flash/NAND flash/SD卡/SPI flash启动”这几中方式用比较统一的方式来实现,并将patch推到Wolfgang的U-Boot tree上。下面是当时开发时做的一些记录,所以基于的U-Boot code是09年的,但变化不大,重要的是原理。
PowerPC CPU可以从不同的存储介质上启动,最常用的是从NOR flash启动,还可以从NAND flash启动,从SD卡启动,从SPI flash启动。为什么需要这么多种的启动方式?需求驱动,本来NOR flash是启动设备的最佳选择,能擦除且字节访问,但容量不够大,所以在单板设计中还可能需要另外一种存储介质去存放kernel image,根文件系统什么的,比如使用NAND flash,这时问题就来了,系统中既然有个NAND flash,能不能将bootloader也放在NAND flash中,从而可以从NAND flash启动?但人家NAND flash志存高远,当时设计的目的是为了取代硬盘而不是作为非易失行存储介质去和NOR flash抢饭碗,像U盘,SD卡,记忆棒,还有固态硬盘SSD神马的,内部都是NAND flash,这也就注定了NAND flash是以block为单位访问的,但我们CPU只能是字节访问,所以要想从NAND flash启动还得需要eLBC controller的帮忙(NOR/NAND一般都挂在local bus控制器上)。从SD卡上启动就更进一步了,整个系统都在一块卡上,拎着想去哪就去哪,呵呵。下面就开始扯吧,先会扯一点CPU地址空间,LAW,TLB,cache什么的,然后是各个不同启动机制的介绍,U-Boot启动代码的解析还有相关的patch介绍,在Wolfgang 的git tree上,还有开发碰到的问题等等。。。
下面这些问题应该能从本文中找到答案:
1. CPU的基础知识和各种启动方式的原理
2.
先来张MPC8536E的架构框图:
什么是地址空间呢?在手册上也叫local address map,就是core能够访问到的36bit的地址空间。这个地址空间包括各个功能模块能够访问到的地址空间,DDR SDRAM空间以及CCSR地址空间。CPU core是怎么访问SOC上的各个功能模块的?比如eLBC控制器,DDR控制器,PCI控制器等等。通过LAW(Local AccessWindow)寄存器来配置。每个LAW寄存器将一段地址空间和相应的功能模块连接起来,从core出来的物理地址和各个LAW的地址进行比较,如果在某个LAW指定的地址空间,那么就将这个交易发送给该LAW指定的功能模块。
每个LAW可指定的地址空间大小为4K-32G。
特别提醒:LAWBA[BASE_ADDR]域必须和地址空间大小对齐。
在这个地址空间中有三段地址是比较特殊的:
1.CCSR的地址空间
这段地址空间只需要通过CCSBAR寄存器来指定起始地址,地址空间大小是固定的1M空间(e500 core是1M,e500mc是16M)。
CCSRBAR(Configuration,Control, and Status Registers Base Address Register)的缺省值是0xFF700000。
访问该空间的TLB在设置时,需要将I和G位置1.
2.L2 SRAM地址空间
MPC8536E有512K的L2 cache,该cache可以配置为SRAM使用,此时需要通过L2CTL[L2SRAM]域来指定L2SRAM的大小,通过L2SRBAR寄存器指定其实地址。
3.缺省的8M
一般soc会定义8MB的bootRom启动地址空间(0xFF80_0000–0xFFFF_FFFF),在core复位启动访问第一条指令时能够直接访问指定启动设备的地址空间。启动设备可以指定挂在eLBC上的NAND flash,NOR flash,或者指定SDCARD/SPI flash。
上述地址空间配置优先级:
L2SRBAR指定的地址优先于其他方式指定的地址空间;
CCSRBAR指定的地址空间不能和DDR控制器指定的地址空间重复,否则,cpu行为是未定义的;
LAW之间的地址如果重复,则低编号LAW覆盖高编号LAW的地址空间;
TLB简单说就是将程序虚拟地址转化为物理地址的一个查找表。TLB使用WIMGE字段来指定所访问地址空间的属性。以后会单独写下TLB,比如怎么配置TLB,怎么查找TLB,TLB在U-Boot和内核的使用,内核进程切换时TLB的切换操作等。
在启动的时候,所有L1/L2MMU中的TLB Entry都为无效的,除了TLB1的Entry0,该Entry被赋予以下数值来指定地址空间0(AS0)中的4KB启动地址空间(0xFFFF_F000– 0xFFFF_FFFF)。在U-Boot代码中,这段4K代码被称为bootpage,即cpu/mpc85xx/start.S中的bootpg汇编代码。
E500 core在复位的时候会去0xFFFF_FFFC去读取第一条指令,该指令一般是一条跳转指令,跳转到bootpg的头部去执行。
WIMGE字段是e500内核进行虚实地址转化时,用来描述TLB对应的物理地址空间的访问属性字段。这几个字段配置和Cache的MESI协议来保持Cache一致性。
? Write-throughrequired (W)
若此位为1,说明对该物理地址空间访问时采用cache-through策略;
若此位为0,说明对该物理地址空间访问时采用cache-back策略;
? Caching inhibited (I)
若此位为1,说明对该物理地址空间访问时不使用L1 cache;
若此位为0,说明对该物理地址空间访问时使用L1 cache;
? Memory coherency required (M)
若此位为1,说明对该物理地址空间访问时需要进行存储一致性处理;
? Guarded(G)
若此位为1,说明对该物理地址空间访问时需要对相应的地址空间进行保护,阻止PowerPC的猜测访问;
? Endianness(E)
若此位为1,说明对该物理地址空间访问时采用小端模式;
若此位为0,说明对该物理地址空间访问时采用大端模式;
想更多了解write through/write back,cache inhibited和guarded怎么影响Cache一致性,请参阅EREF。
一般NOR flash挂接在CPU的eLBC控制器上,通过CS0来片选。要想从NOR flash启动,首先将编译好的512K U-Boot image烧写到NOR flash的最后512K中。CPU core启动时会去0xFFFFFFFC处取第一条指令执行,一般是一条跳转指令跳转到bootpage的地方,因为NOR flash是可以字节访问的,随后CPU会顺序执行NOR flash中的代码。
eLBC通过GPCM连接NOR flash,通过FCM来连接NAND flash。
BR0/OR0– BR7/OR7 指定eLBC 8个片选信号所对应的地址空间和属性。CS0连接启动设备,比如从NOR flash启动时,NOR flash必须连接到CS0上。从NAND flash启动时,NAND flash必须连接到CS0上。对于既有NOR flash又有NAND flash的单板,并且支持两种启动模式的话,需要CPLD来动态切换CS0片选信号指向启动设备。
当选择从NOR flash启动时,CS0是作为启动片选信号输出,选择的地址和空间由BR0和OR0信息来决定,该寄存器启动值如下,显然访问的是从0– 0xFFFFFFFF的4G地址空间。该片选信号一直按照此种方式运行直到第一次更改BR0或OR0的值。
在往NORflash烧写U-Boot程序时,是将512K的image写到flash的最后512K字节,即eff8_0000开始的地方。当core启动时,第一条指令是FFFF_FFFC,实际上读取的就是就是efff_fffc处的指令(0x4BFF_F004)。
首先一个问题是U-Boot image要烧写到NOR flash的哪个地址?是由CPU和NOR flash的硬件连线决定的。同时该地址也决定了U-Boot的CONFIG_SYS_TEXT_BASE的值,该值在config.mk文件中赋值给-Ttext,也就是U-Boot image中text段的起始地址。
关于 ld script的内容可以参考《Using ld
-
1.
-T指定使用的script文件
2. -e指定image的开始执行的地址
3. -Ttext: 和--section-start .text同义,该字段的意思是给text段指定绝对地址。
#ifndef
#define
#endif
LDFLAGS_u-boot
ifneq
LDFLAGS_u-boot
endif
以MPC8536DS单板来看下CPU 和 NOR flash的硬件连线:
使用的NOR flash芯片是128MB的29GL01GP,通过单板上的一个拨线开关可以将128M的flash划分为4个bank,每个bank 32M。
通过上图可以看到CPU地址线的使用如下:
0 1 2 3
4 5 6
7-30
31
读取flash时,高4位不会用到,所以分配该4位主要看U_Boot的地址空间分配。在8536DS中分配的是e
Localbus地址空间是从e000_0000到efff_efff.
Promjet:e0000000-e7ffffff
NOR flash:
e8000000-efffffff
通过FPGA来设置flash的25/26两位地址线,相当于把NOR flash划分为4个region。
连接NOR flash的低24位数据线(32M)
通过16bit的数据线连接NORflash,所以不care最低位
1110
10 0
10 1
11 0
11 1
0_0…0=> e800_0000 – e9ff_ffff
0_0…0=> ea00_0000 – ebff_ffff
0_0…0=> ec00_0000 – edff_ffff
0_0…0=> ee00_0000 – efff_ffff
x
在往NOR flash烧写U-Boot程序时,是将512K的image写到flash的最后512K字节,即eff8_0000开始的地方。当core启动时,会去地址FFFF_FFFC获取第一条指令,实际上读取的就是efff_fffc处的指令,因为CPU高4位数据线根本就没有连接到NOR flash上。
那么在地址efff_fffc地方存放的是什么指令,需要看U-Boot的ld script,因为ld script决定了U-Boot image每个段存放的内容。
U-Boot image使用的load scriptor 是u-boot.lds。
SECTIONS
{
......
.text :
{
*(.text)
*(.got1)
}:text
_etext= .;
PROVIDE(etext = .);
.= (. + 0x00FF) & 0xFFFFFF00;
_erotext= .;
PROVIDE(erotext = .);
......
.bootpg RESET_VECTOR_ADDRESS - 0xffc :
{
cpu/mpc85xx/start.o(.bootpg)
}:text = 0xffff
.resetvec RESET_VECTOR_ADDRESS :
{
*(.resetvec)
}:text = 0xffff
.= RESET_VECTOR_ADDRESS + 0x4;
.= ALIGN(4);
_end= . ;
PROVIDE(end = .);
}
"board/freescale/mpc8536ds/config.mk"
ifndefTEXT_BASE
TEXT_BASE= 0xeff80000
Endif
RESET_VECTOR_ADDRESS= 0xeffffffc
"cpu/mpc85xx/resetvec.S"
.section.resetvec,"ax"
b_start_e500
从上面的ldscript可以看出,u-boot image最后的4个字节存放的是启动向量,即一条跳转到bootpg的跳转指令(0x4BFF_F004)。可以通过objdump u-boot 找到U-Boot image最后4个字节的内容。
显然core启动时读取的就是这条指令0x4BFF_F004,这条指令是条跳转指令(因为OP = 010010),core的指令译码单元会执行如下的计算来得到跳转到的地址:
010010
-------|-----------------------------------|--|---
OP
--------------------------------------------------
EXTS(LI || 0b00) => 0xFFFFF004
CIA
-------------------------------
通过objdump
u-boot可以看到effff000出存放的代码,对应的是start.S文件中的4k
的启动代码段bootpg。
effff000<_start_e500>:
effff000: 38 00 00 02 li r0,2
effff004: 7c 12 fb a6 mtdbcr0 r0
effff008: 7c 13 fb a6 mtspr 1011,r0
effff00c: 7c 30 4a a6 mfspr r1,304
effff010: 7c 30 4b a6 mtspr 304,r1
effff014: 3c 40 00 01 lis r2,1
effff018: 60 42 00 01 ori r2,r2,1
effff01c: 7c 52 fb a6 mtdbcr0 r2
effff020: 4c 00 01 2c isync
effff024: 7c 53 fb a6 mtspr 1011,r2
effff028: 4c 00 01 2c isync
effff02c: 7c 00 04 ac sync
"cpu/mpc85xx/start.S"
.section.bootpg,"ax"
.globl_start_e500
_start_e500: