arm:启动代码判断是从nand启动还是从norflash启动,拷贝程序到内存的过程
一、nand启动和nor启动:[1]
CPU从0x00000000位置开始运行程序。
1、nand启动:
如果将S3C2440配置成从NANDFLASH启动(将开发板的启动开关拔到nand端,此时OM0管脚拉低)S3C2440的Nand控制器会自动把Nandflash中的前4K代码数据搬到内部SRAM中(地址为0x40000000),同时还把这块SRAM地址映射到了0x00000000地址。CPU从0x00000000位置开始运行程序。
2、如果将S3C2440配置成从Norflash启动(将开发的启动开关拔到nor端,此时OM0管脚拉高),0x00000000就是norflash实际的起始地址,norflash中的程序就从这里开始运行,不涉及到数据拷贝和地址映射
3、总结:
nand启动时,地址0x00000000为内部SRAM映射的地址;
nor启动时,地址0x00000000为norflash的实际起始地址。
向Norflash中写数据需要特定的命令时序,而向内存中写数据可以直接向内存地址赋值。
二、判断是从nand启动还是从norflash启动:
1.根据硬件接线,
引脚OM1、OM0 : 00: Nand-boot 01: 16-bit 10: 32-bit 11: Test mode
总线宽度和等待控制寄存器(BWSCON)0x48000000 BWSCON_bit[2:1] Indicate data bus width for bank 0 (read only).
The states are selected by OM[1:0] pins 。(01 = 16-bit, 10 = 32-bit ...)
what's done before copy?
ResetHandler ;WTCON watch dog disable ;INTMSK ;INTSUBMSK ;To reduce PLL lock time, adjust the LOCKTIME register. ;Setting value Fclk:Hclk:Pclk ;Configure UPLL ;Configure MPLL ;Check if the boot is caused by the wake-up from SLEEP mode. ;In case of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler. ;Set memory control registers ;Initialize stacks bl InitStacks ; Setup IRQ handler ldr r0,=HandleIRQ ;This routine is needed ldr r1,=IsrIRQ ;if there isn t 'subs pc,lr,#4' at 0x18, 0x1c str r1,[r0]
now copy:
;=========================================================== ;// 判断是从nor启动还是从nand启动 ;=========================================================== ldr r0, =BWSCON ldr r0, [r0] ands r0, r0, #6 ;0110 bne NORRoCopy ;BWSCON的[2:1]反映了外部引脚OM[1:0]. ;bne:若OM[1:0] != 00, 是从NOR FLash启动. ;若OM[1:0]==00,则为Nand Flash Mode. NandFlashMode adr r0, ResetEntry ;注意adr得到的是相对地址,非绝对地址. Cpu刚启动时,ResetEntry==0. cmp r0, #0 ;再比较入口是否为0地址处 bne InitRamZero ;如果不是0,直接初始化bss段,进入CEntry. ;nop ;=========================================================== ;如果ResetEntry==0,说明是cpu刚启动(注意此处是nand启动),那就应该将nand中的代码搬运到sdram中。 ;=========================================================== nand_boot_beg bl ClearSdram ;将内存清零。从内存起始地址--->指定的足够大的范围,不超过内存的实际大小。 mov r5, #NFCONF ;nand控制器初始化 ;set clk value ldr r0, =(7<<12):OR:(7<<8):OR:(7<<4) str r0, [r5] ;enable control ldr r0, =(0<<13):OR:(0<<12):OR:(0<<10):OR:(0<<9):OR:(0<<8):OR:(1<<6):OR:(1<<5):OR:(1<<4):OR:(1<<1):OR:(1<<0) str r0, [r5, #4] bl ReadNandID ;读nandId,从而判断nand芯片的种类. r5 = nandId mov r6, #0 ldr r0, =0xecF1 cmp r5, r0 beq %F1 ;if(nandId==0xecf1){r6=0;//addr_cycle=4}else{r6=1;//addr_cycle=5} mov r6, #1 ;Nandaddr(寻址周期 0:4 1:5) 1 bl ReadNandStatus ;r1 = ret_of_ReadNandStatus ,这里似乎没有用。 mov r8, #0 ;page_addr = r8 = 0; ldr r9, =ResetEntry ;r9 = size_copyed = pBuff = 0; mov r10,#64 ;+081010 feiling nPages_need_to_copy=64(128KB); 2 ands r0, r8, #0x3f ;R0 = R8 & 0X3F ; 如果是第一页(mov r8, #0),本式子equal 0; bne %F3 mov r0, r8 ;如果是第一页,则检测坏块 bl CheckBadBlk ;unsigned int CheckBadBlk(unsigned int page_addr) cmp r0, #0 addne r8, r8, #64 ;每块的页数 r8 同时也做计数用。 addne r10,r10,#64 ;+081010 feiling if(is_bad_block){page_addr+=64;nPages_need_to_copy+=64;} bne %F4 3 mov r0, r8 mov r1, r9 ;r1 = r9 = size_copyed bl ReadNandPage add r9, r9, #2048 ;size_copyed+=2048 add r8, r8, #1 ;page_addr+1 4 cmp r8, r10 ;要拷贝的页数 081010 pht . if(page_addr < nPages_need_to_copy ){copy_loop;} bcc %B2 mov r5, #NFCONF ;DsNandFlash ldr r0, [r5, #4] bic r0, r0, #1 str r0, [r5, #4] ldr pc, =InitRamZero ;此处跳转到内存空间 LDR 装载数据,寻址灵活。 但不改变PSR ;要装载一个被存储的‘状态’并正确的恢复它 可以这样写:ldr r0, [base] 换行 moves pc, r0 ;============================================================================================= ;若是从NAND启动,则先清零内存,再从nand[0 ~ user_set_size]拷贝到内存(ro 和.data),再初始化bss段,进入main。 ;若是从NOR启动,同样是先清零,后拷贝,再初始化bss段,进入main。为了考虑jlink等调试的情况,避免二次拷贝,才使代码显得复杂。
;其实可以不管,直接全部拷贝过来,nor拷贝比nand拷贝更简单,逐个字节复制即可。;============================================================================================= NORRoCopy ;copy_proc_from_nor_to_sdram bl ClearSdram ;clear all sdram needed adr r0, ResetEntry ;判断是否在ROM中运行,ROM即RO指定的地址,从NOR启动时ResetEntry为0 ldr r2, BaseOfROM ;如果相等,说明是jlink调试,调试器将程序.ro段直接下载到了内存,就不需再拷贝。 cmp r0, r2 ;pFrom = r0 = ResetEntry(活的) ; pTo = r2 = BaseOfROM(链接文件写死的) ;pEnd = TopOfROM(链接文件写死的) beq NORRwCopy ;if(ResetEntry==BaseOfROM){skip RoCopyLoop ,directly do RwCopy;} RoCopyLoop ldr r3, TopOfROM ; 0 ldmia r0!, {r4-r7} stmia r2!, {r4-r7} ;mem_ResetEntry----> mem_BaseOfROM ; pFrom+=4;pTo+=4; cmp r2, r3 ;if(pTo<pEnd){copy_loop;} bcc %B0 NORRwCopy ;拷贝.data段(已初始化的全局变量、静态变量等),它在固件里占有空间。 ldr r0, TopOfROM ;根据链接文件sct, TopOfROM == BaseOfRW ldr r1, BaseOfROM ; sub r0, r0, r1 ;pFrom = r0 = (TopOfROM - BaseOfROM) ldr r2, BaseOfRW ;pTo = BaseOfRW ,pEnd = BaseOfZero ldr r3, BaseOfZero 0 cmp r2, r3 ;if(r2<r3){ ldrcc r1, [r0], #4 ; r1 = [r0] ; r0+=4; strcc r1, [r2], #4 ; [r2] = r1 ; r2+=4; ;} bcc %B0
InitRamZero ;初始化Bss段,之后立即进入CEntry mov r0, #0 ldr r2, BaseOfZero ldr r3, EndOfBSS 1 cmp r2, r3 ;清零bss段,bss段(未初始化的全局变量、静态变量)在bin文件中不占空间,只有占位符,因此需要在系统初始化时,手动初始化(一般memset为0); strcc r0, [r2], #4 bcc %B1 ldr pc, =CEntry ;goto compiler address CEntry bl Main ;Don t use main() because ...... b .
ClearSdram:在拷贝程序到内存之前,先清内存。
;========================================================= ClearSdram mov r1,#0 mov r2,#0 mov r3,#0 mov r4,#0 mov r5,#0 mov r6,#0 mov r7,#0 mov r8,#0 ldr r9,=0x00700000 ;size of ram to clear ldr r0,=0x30000000 0 stmia r0!,{r1-r8} subs r9,r9,#32 bne %B0 BX lr
2、根据 “向norflash写入数据需要一定的命令序列,而向内存中写数据可以直接向内存地址赋值” 这一点来区别:
//int bBootFrmNORFlash(void) : 判断是否从norflash启动。 //ret==1 :norflash启动 //ret==0 :nandflash启动。(或者是jlink调试的情况。) //说明:该函数应用范围有限,只针对于bootloader最初的stage1。 int bBootFrmNORFlash(void) { volatile unsigned int *pdw = (volatile unsigned int *)0; //volatile unsigned int dwVal; /* * 无论是从NOR Flash还是从NAND Flash启动, * 地址0处为指令"b Reset", 机器码为0xEA00000B, * 对于从NAND Flash启动的情况,其开始4KB的代码会复制到CPU内部4K内存中, * 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0。 * 对于NOR Flash,必须通过一定的命令序列才能写数据, * 所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动: * 向地址0写入一个数据,然后读出来,如果没有改变的话就是NOR Flash * 这仅仅针对于初始阶段,norflash启动时,最终也要将程序加载到SDRAM中运行。因此该函数应用范围是有局限性的。 */ dwVal = *pdw; *pdw = 0x12345678; if (*pdw != 0x12345678) { return 1; } else { *pdw = dwVal;//测试完后要还原数据。 return 0; } }
参考:
1、uboot如何检测XC2440是从Nand或Nor启动 http://blog.chinaunix.net/uid-22030783-id-3347621.html