powerpc nor flash启动
备注:若本文没有说明,cpu一般当powerpc处理。 cpu 访问nor flash来进行启动。 假设cpu上电后,从0xffff0000启动。但是uboot的链接地址为0xefff0000。然后生成uboot.bin文件 这时一定要注意存储地址、运行地址和编译地址的关系。 1.存储地址,又叫加载地址 即代码存放的位置 2.运行地址(有效地址) 代码运行的地址 3.编译地址(链接地址) 代码通过编译器生成的地址 刚开始汇编时mmu关闭,运行地址=存储地址都在0xffff0000范围 随后mmu打开运行c语言,运行地址=编译地址在0xefff0000范围。 即powerpc nor flash启动时,该开始运行地址不等于编译地址,后来运行到c语言运行地址=编译地址。当uboot从nor flash拷贝到ram后,运行地址=加载地址。 而arm nor flash启动就不同,运行地址根本不等于编译地址。当uboot从nor flash拷贝到ram后,运行地址=编译地址。 uboot.bin内容主要就是指令数据,而某些指令中的标号翻译成了编译地址。下图是flash中的uboot.bin主要内容。 存储地址 编译地址 指令 0xffff0000: 0xefff0000 ?? ? ? ? ? ? ? ? ?b reset 0xffff0004: 0xefff0004 ? ? ? ? ? ? ? ? ? ... 0xffff0008: 0xefff0008? ? ? ? ? ? ? ? ? reset: BMS为1时,系统默认从地址0xFF80 0000 -- 0xFFFF FFFF启动,? pc指针指向0xFFF0 0100(即从0xFFF0 0100取第一条指令),从nor flash中读取代码段的第一条代码 b reset。 注意此时的跳转语句,reset是个偏移量,跳转后的地址=pc+reset,跟存储位置没有关系,即位置无关的代码。如果第一句代码换成 ldr pc,= reset。这时跳转后的地址=链接地址(0xefff0000)+reset,肯定是错误的。 uboot分stage1和stage2,其中stage1主要是汇编语言写的,一般与位置无关;而stage2是C语言写的,其中含有很多静态符号表,肯定与位置相关。 ARM使用nor flash启动时一般都是汇编指令,没有用到函数指针和数组等静态符号表。运行c语言的时候已经到sdram。所以在flash中的uboot代码就是位置无关的,在ram中的uboot代码是位置相关的。 但是powerpc是个例外,uboot在nor flash中就已经运行C语言(cpu_init_f中init_laws对law_table初始化),用到了静态符号表,因此在flash中的uboot代码就是位置相关的,这时就要求编译地址和运行c语言时的有效地址相同(在cpu_init_f由于mmu已经开启,所以需要初始化bat表,建立0xefff0000-0xefffffff到0xffff0000-0xffffffff,这时通过运行地址映射成实际地址才能访问flash中的静态符号表)。 顺便说下x86的引导过程 系统启动时CS:IP=0xffff:0x0,cpu再这里执行jump far addr,跳转到BIOS映射的代码段。bios进行主机自检,接下来根据bios设置读取引导驱动器的引导顺序,依次检查直到找到可以引导的驱动器,然后调用驱动器磁盘的引导扇区进行引导。bios将所检查的第一个扇区载入内存,放在内存0x0:0x7c00处。如果扇区最后两个字节是"55 AA",此扇区就是引导扇区。 有了上面的基础,我们讲讲mpc8641是如何启动的 (1) 寻址方式是当前pc值加减偏移量 由于复位时 当BMS = 1,law寄存器默认为0xff800000即cpu core(soc)将EA直接送到local bus的数据总线上。 同时br寄存器给了nor flash片选控制信号,所以cpu可以正常访问nor flash。 b boot_cold ----> bl invalidate_bats----> bl clear_tlbs------>calulate absolute address ?in Flash and jump there in_flash--->bl enable_ext_addr(set bat table,build MMU map)----> jump addr_trans_enabled func(turn on MMU) 在enable_ext_addr中我们将0xefff0000至0xefff0000+1M-1映射到0xffff0000至0xffff0000+1M-1(2)mmu开启后代码分析 存储地址 编译地址 指令 0xffff0xx4: 0xefff0xx4? ? ? ? ? ? ? ... 0xffff0xx8: 0xefff0xx8? ? ? ? ? ? ? ... refi指令的意思是将addr_trans_enabled函数的编译地址赋给了pc计数器。此时我们从0xffff0000的范围变成了0xefff0000,实现了cpu跳转到addr_trans_enabled 函数 开启mmu后,后续的代码所用的地址(cpu使用的地址)均为运行地址范围为0xefff0000至0xefff0000+1M-1,mmu自动将运行地址映射到物理地址。其实外设访问的地址还是0xffff0000至0xffff0000+1M-1,但是代码所用的地址(cpu使用的地址)均为运行地址范围为0xefff0000至0xefff0000+1M-1 按照代码的执行顺序,分析addr_trans_enabled 。 bl l1dcache_enable ------>bl dcache_enable bl cpu_init_f----------->bl board_init_f cpu_init_f主要实现local access window的初始化 CPU core是怎么访问SOC上的各个功能模块的?比如eLBC控制器,DDR控制器,PCI控制器等等。通过LAW(Local Access Window)寄存器来配置。每个LAW寄存器将一段地址空间和相应的功能模块连接起来,从core出来的物理地址和各个LAW的地址进行比较,如果在某个LAW指定的地址空间,那么就将这个发送给该LAW指定的功能模块。 详细请参考我的另一篇文章powerpc memory和io访问原理 1. powerpc |