310 ;===========================================================
311
312 ldr r0, =BWSCON
313 ldr r0, [r0]
314 ands r0, r0, #6 ;OM[1:0] != 0, NOR FLash boot
315 bne copy_proc_beg ;do not read nand flash
316 adr r0, ResetEntry ;OM[1:0] == 0, NAND FLash boot
317 cmp r0, #0 ;if use Multi-ice,
318 bne copy_proc_beg ;do not read nand flash for boot
319 ;nop
320 ;===========================================================
第312-318行判断代码启动的方式,是通过BWSCON寄存器的[2:1]两位,这两位是OM[1:0]的显示,如果OM[1:0]为00,是nandflash启动方式,如果为11就是test mode,,再者就是NORFLASH方式。分别介绍一下这三种启动方式。
第一种是nandboot,由于nandflash的物理结构,代码是不能在nandflash中执行的,所以硬件会自动把代码的前4K数据放到SRAM中执行,然后再把整个代码拷贝到sdram中。由于代码启动时为在0x0地址处运行,所以这个4K的地址被映射到0x0处。
第二种是norflash启动,代码端可以在norflash中执行,由于RW段在norflash中操作起来相当耗时,所以代码会搬到SDRAM中执行。由于norflash片在0x0的地址处,所以不需要映射地址。
第三种是使用调试工具,使代码直接在SDRAM中执行。
第312-314行把BWSCON寄存器的地址放到R0中,再把R0中的值取出,放到R0中,与#6相与的结果放到R0中
第315行bne指令不相等是跳转,B是跳转,NE是不相等(或不为0)。也就BWSCON的[2:1]两位不为0是跳转到copy_proc_beg.
第316-318行把入RsetEntry的值放到R0中,这个值可能是0,也可能是0x30000000,主要看启动的方式,如果使用调试工具的话,这个值为0x30000000.如果R0中的值不为0,再跳copy_proc_beg函数处,否则的话,则执行下面的代码,也就是nandflash启动。
/*在介绍nand boot之前,先说一下,nandflash的基本知识,
第一部分,nandflash的概述。
nandflash是由plane、block、page组成,page是读写的最小单位,block是擦除的最小单位。每一个页分成两部分,一部分是用来存放数据,另一部分是用存放OOB(可以存放纠错的信息)。Flash中的地址分成两部分,一部分为column address(页内地址),例如512Byte大小的页,页内地址为9位。另一部分为页地址,例如64M大小的DIVICE,Block大小16K,PAGE大小512,则PAGE count = 64M/16K*16K/512=1024*64=2^17。需要17位。
目前使用的K9F1208的情况
Memory SIZE = (64M+2K)*8bit
Block SIZE = (16K+512)Byte
Page SIZE = (512+16)byte
Block Count =64M/16K = 4096
Page Count = 16K/512 = 32
第二部分,nandflash的读写
读写的过程,先发送命令和地址,后面会跟着读写的数据,数据由flash control到flash内部的数据缓冲区,然后再写到指定的flash地址上或者先到FLASH内部的缓冲区,再到用户的缓冲区。例如K9F1208读数据过程 write命令(00H)-----write(address)---write(30H)---数据到内部缓冲区(busy)-----Data out0….Data OutN.
对于下面这段代码,感觉用C语言写比较直观一下。
*
*/
321 nand_boot_beg
322 [ {TRUE}
323 bl CopyProgramFromNand
324 |
325 mov r5, #NFCONF
326 ;set timing value
327 ldr r0, =(7<<12)|(7<<8)|(7<<4)
328 str r0, [r5]
329 ;enable control
330 ldr r0, =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)
331 str r0, [r5, #4]
332
第325-331行主要是配置nand flash的控制时序,和对nand flash控制寄存器的设置
第327行需要设置 tacls、twrph0,twrph1.它们三个之和只要大于50NS即可,使用的HCLK的时间为100M,也就是一个时间周期为10NS。
Tacls的值Tacls * Hclk = 0*10NS
Twrph0的值Twrph0 *5 =50NS
Twrph1的值Twrph1*1 =10NS
第328行把配置的值写到NFCONF寄存器中
第330-331行查看datasheet page226
333 bl ReadNandID
334 mov r6, #0
335 ldr r0, =0xec73
336 cmp r5, r0
337 beq %F1
338 ldr r0, =0xec75
339 cmp r5, r0
340 beq %F1
341 mov r6, #1
342 1
第333-342行读出nandflash的ID,用来判断是什么型号的ID,如果这个有parameter命令的话会更好,因为可以知道几个device、plane、block、page、的大小。代码很简单,读出ID和这个两个型号比较。
343 bl ReadNandStatus
344
第343行read status函数,用来返回flash的状态。(read or busy or fail)
345 mov r8, #0
346 ldr r9, =ResetEntry
347 2
348 ands r0, r8, #0x1f
349 bne %F3
350 mov r0, r8
351 bl CheckBadBlk
352 cmp r0, #0
353 addne r8, r8, #32
354 bne %F4
355 3
356 mov r0, r8
357 mov r1, r9
358 bl ReadNandPage
359 add r9, r9, #512
360 add r8, r8, #1
361 4
362 cmp r8, #5120
363 bcc %B2
364
365 mov r5, #NFCONF ;DsNandFlash
366 ldr r0, [r5, #4]
367 bic r0, r0, #1
368 str r0, [r5, #4]
369 ]
第345-369行这几行要连起来行,要不然不容易理解,其实本意就是把数据拷到SDRAM中,代码首先给出了拷贝的目的地址(ResetEntry)。然后会check一下,这个块是否为坏块,如果是坏块则R8=R8+32,这个32就是一个块包含的页数,意思就是跳过这个块,然后开始读出flash page中的数据,由于一个页的大小为512Byte。所以R9第次加512,而R8做为计数count,第次递增1,与5120比较。所以拷贝数据的大小为5120*512Byte=2.5M的数据,好像不需要这么多的数据,一个boot的代码也就几百K。不知道为什么.
第345-346行R8清0,R9拷贝的目的地址。
第348-354行如果是第一次读这个块的话,会CHECK块是否为坏块,所以第348行R0的值为,不执行跳转,然后把R8的值赋给R0,进行CHECK,如果R0的值不为0,则R8+32,跳转到标号4的地方,说明这是一个bad block。
第356-360行给R0,R1赋值,进行读数据,读出数据后,R9递增512,R8递增1.
第362-363行判断是否读完数据
第365-368行R0写回到NFCONF,R0的最后一位清0,写回到NFCONT中,就是disable flash control.
370 ldr pc, =copy_proc_beg
371 ;===========================================================
372 copy_proc_beg
373 adr r0, ResetEntry
374 ldr r2, BaseOfROM
375 cmp r0, r2
376 ldreq r0, TopOfROM
377 beq InitRam
378 ldr r3, TopOfROM
379 0
380 ldmia r0!, {r4-r7}
381 stmia r2!, {r4-r7}
382 cmp r2, r3
383 bcc %B0
384
385 sub r2, r2, r3
386 sub r0, r0, r2
387
388 InitRam
389 ldr r2, BaseOfBSS
390 ldr r3, BaseOfZero
391 0
392 cmp r2, r3
393 ldrcc r1, [r0], #4
394 strcc r1, [r2], #4
395 bcc %B0
396
397 mov r0, #0
398 ldr r3, EndOfBSS
399 1
400 cmp r2, r3
401 strcc r0, [r2], #4
402 bcc %B1
403
404 ldr pc, =%F2 ;goto compiler address
405 2
第372-405主要实现代码的拷贝和RAM的初始化,先介绍一下简单的流程,代码先判断ResetEntry和代码运行的地址是否一致,如果相同,说明现在的代码在0x30000000处运行,也就需要初始化RAM并且拷贝RW,ZI段。否则的话就是在NOR FLASH启动。
第372-377行把ResetEntry的值放到R0,把RO段的运行地址放到R2,并比较它们是否相等,如果相等,则把运行地址放到R0中,同时跳到INITRAM处。
第378行如果不相等,则代码在nor flash中,把RO段的长度赋给R3.
第379-386行拷贝NOR FLASH中的RO段,LDMIA和STMIA是一组寄存器和内存单元传输数据,IA是为事后增加(类似于i++)。把ResetEntry所在的内存中的数据,先放到寄存器中,然后再把寄存器中的值放到R2所指向的内存地址中。直到R2和R3的值相等,也就是拷贝整个RO段的大小。
第385-386行这两行代码,此时R2==R3,所以R2的值是0,而R0-R2应该是RO段的长度,因为经过拷贝之后,R0的值应该等于R0+R3.
第308-405行这段代码就比较简单了,主要实现把RW段,同时进行初始化ZI段。
第389-396行此时R0的值为RO的末尾,其实也是RW段的基地址。R3为RW段的长度,步长为4,进行搬动,和前面的代码拷贝类似.
第307-405行是进行ZI段的初始化,初始化的值为0,R3为ZI的大小,执行的步长为4。
415 ;===========================================================
416 ; Setup IRQ handler
417 ldr r0,=HandleIRQ ;This routine is needed
418 ldr r1,=IsrIRQ ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c
419 str r1,[r0]
420
第417-419行这几行代码是什么情况,把HandleIRQ的地址给R0,把IsrIRQ的函数地址给R1,然后把R1放到R0寄存器值,其实应该是这样的,中断响应的过程,是会先在一级向量表中查找,如果是IRQ中断,就会在IRQ的二级向量中查找。
421 ; ;Copy and paste RW data/zero initialized data
422 ; ldr r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
423 ; ldr r1, =|Image$$RW$$Base| ; and RAM copy
424 ; ldr r3, =|Image$$ZI$$Base|
425 ;
426 ; ;Zero init base => top of initialised data
427 ; cmp r0, r1 ; Check that they are different
428 ; beq %F2
429 ;1
430 ; cmp r1, r3 ; Copy init data
431 ; ldrcc r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4
432 ; strcc r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4
433 ; bcc %B1
434 ;2
435 ; ldr r1, =|Image$$ZI$$Limit| ; Top of zero init segment
436 ; mov r2, #0
437 ;3
438 ; cmp r3, r1 ; Zero init
439 ; strcc r2, [r3], #4
440 ; bcc %B3
441
442
443 [ :LNOT:THUMBCODE
444 bl Main ;Do not use main() because ......
445 ;ldr pc, =Main ;
446 b .
447 ]
448
449 [ THUMBCODE ;for start-up code for Thumb mode
450 orr lr,pc,#1
451 bx lr
452 CODE16
453 bl Main ;Do not use main() because ......
454 b .
455 CODE32
456 ]
457
第443-456行两种跳转到Main函数的方式,一种是非thumb代码,一种是thumb代码。
458
459 ;function initializing stacks
460 InitStacks
461 ;Do not use DRAM,such as stmfd,ldmfd......
462 ;SVCstack is initialized before
463 ;Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'
464 mrs r0,cpsr
465 bic r0,r0,#MODEMASK
466 orr r1,r0,#UNDEFMODE|NOINT
467 msr cpsr_cxsf,r1 ;UndefMode
468 ldr sp,=UndefStack ; UndefStack=0x33FF_5C00
469
470 orr r1,r0,#ABORTMODE|NOINT
471 msr cpsr_cxsf,r1 ;AbortMode
472 ldr sp,=AbortStack ; AbortStack=0x33FF_6000
473
474 orr r1,r0,#IRQMODE|NOINT
475 msr cpsr_cxsf,r1 ;IRQMode
476 ldr sp,=IRQStack ; IRQStack=0x33FF_7000
477
478 orr r1,r0,#FIQMODE|NOINT
479 msr cpsr_cxsf,r1 ;FIQMode
480 ldr sp,=FIQStack ; FIQStack=0x33FF_8000
481
482 bic r0,r0,#MODEMASK|NOINT
483 orr r1,r0,#SVCMODE
484 msr cpsr_cxsf,r1 ;SVCMode
485 ldr sp,=SVCStack ; SVCStack=0x33FF_5800
486
487 ;USER mode has not be initialized.
488
489 mov pc,lr
490 ;The LR register will not be valid if the current mode is not SVC mode.
491
第464-485行分别设置处理器几种工作模式的堆栈,MSR指令用于将通用寄存器的内容或一个立即数传送到状态寄存器(CPSR或SPSR)中,MRS指令用于将状态寄存器的内容传送到通用寄存器中。代码大到火类同,只介绍其中的一个。
第464行把CPSR的值放到R0中,由于不能直接修改状态寄存器中的值,所以是先读出,修改,再写回。
第465行BIC指令清除相应的位,在本行代码中MODEMASK的值为0x1f,意思就是清除掉后5位,再放回R0中。
第466行ORR指令将某些位设成1,在这里面把R0的后5位设成0x1b|0xc0.
第467行把R0的值写到CPSR寄存器中,其实只是修改了它的后5位,后5位为0x1b,查看CPSR寄存器的后5位可以得知,处理器的模式为Undefined.
492 ;===========================================================
493 [ {TRUE}
494 |
495 ReadNandID
496 mov r7,#NFCONF
497 ldr r0,[r7,#4] ;NFChipEn();
498 bic r0,r0,#2
499 str r0,[r7,#4]
第496-499行把R0值写入到NFCONF+4的寄存器中,也就NFCONT,使能nandflash controller,然后把第一位取反(bic r0,r0,#2)就是选中nandflash 使能。
500 mov r0,#0x90 ;WrNFCmd(RdIDCMD);
501 strb r0,[r7,#8]
502 mov r4,#0 ;WrNFAddr(0);
503 strb r4,[r7,#0xc]
第500-503行发送命令0x90和地址0写入到NFCMMD(NFCONF+8),NFADDR(NFCONF+0xc)中
504 1 ;while(NFIsBusy());
505 ldr r0,[r7,#0x20]
506 tst r0,#1
507 beq %B1
第505-507行测试NFSTAT(NFCONF+0x20)的最低位,如果为0,则busy,否则ready.
508 ldrb r0,[r7,#0x10] ;id = RdNFDat()<<8;
509 mov r0,r0,lsl #8
510 ldrb r1,[r7,#0x10] ;id |= RdNFDat();
511 orr r5,r1,r0
512 ldr r0,[r7,#4] ;NFChipDs();
513 orr r0,r0,#2
514 str r0,[r7,#4]
515 mov pc,lr
516
第508-515行 LDRB字节加载指令,取出NFDATA(NFCONF+0x10)中的1个字节,放到R0第二个8位上,然后再取出1个字节放到低8位上,nandflash片选disable,这个不是nandflash controller.请注意。
517 ReadNandStatus
518 mov r7,#NFCONF
519 ldr r0,[r7,#4] ;NFChipEn();
520 bic r0,r0,#2
521 str r0,[r7,#4]
522 mov r0,#0x70 ;WrNFCmd(QUERYCMD);
523 strb r0,[r7,#8]
524 ldrb r1,[r7,#0x10] ;r1 = RdNFDat();
525 ldr r0,[r7,#4] ;NFChipDs();
526 orr r0,r0,#2
527 str r0,[r7,#4]
528 mov pc,lr
529
第518-528行READ STATUS比较简单,和上面的READID,是类似的,先使能NANDFLASH,然后发送命令0x70,把结果放到R1中,然后再取消全能NANDFLASH,返回。
530 WaitNandBusy
531 mov r0,#0x70 ;WrNFCmd(QUERYCMD);
532 mov r1,#NFCONF
533 strb r0,[r1,#8]
534 1 ;while(!(RdNFDat()&0x40));
535 ldrb r0,[r1,#0x10]
536 tst r0,#0x40
537 beq %B1
538 mov r0,#0 ;WrNFCmd(READCMD0);
539 strb r0,[r1,#8]
540 mov pc,lr
541
第531-540行因为FLASH在操作过程中,会和生一个busy信号,如READ数据时,如果busy信号产生,说明现在数据正在放FLASH内部的缓冲区放,等放完数据后,这个信号就会变成READY,我们就可以取出数据。
代码很简单,发送命令0x70,然后把返回的数据放到R0中,如果R0的最高位为1,说明现在是FAIL的,不管是读,写都是操作不能完成的。
542 CheckBadBlk
543 mov r7, lr
544 mov r5, #NFCONF
545
546 bic r0,r0,#0x1f ;addr &= ~0x1f;
547 ldr r1,[r5,#4] ;NFChipEn()
548 bic r1,r1,#2
549 str r1,[r5,#4]
550
551 mov r1,#0x50 ;WrNFCmd(READCMD2)
552 strb r1,[r5,#8]
553 mov r1, #5;6 ;6->5
554 strb r1,[r5,#0xc] ;WrNFAddr(5);(6) 6->5
555 strb r0,[r5,#0xc] ;WrNFAddr(addr)
556 mov r1,r0,lsr #8 ;WrNFAddr(addr>>8)
557 strb r1,[r5,#0xc]
558 cmp r6,#0 ;if(NandAddr)
559 movne r0,r0,lsr #16 ;WrNFAddr(addr>>16)
560 strneb r0,[r5,#0xc]
561
562 ; bl WaitNandBusy ;WaitNFBusy()
563 ;do not use WaitNandBusy, after WaitNandBusy will read part A!
564 mov r0, #100
565 1
566 subs r0, r0, #1
567 bne %B1
568 2
569 ldr r0, [r5, #0x20]
570 tst r0, #1
571 beq %B2
572
573 ldrb r0, [r5,#0x10] ;RdNFDat()
574 sub r0, r0, #0xff
575
576 mov r1,#0 ;WrNFCmd(READCMD0)
577 strb r1,[r5,#8]
578
579 ldr r1,[r5,#4] ;NFChipDs()
580 orr r1,r1,#2
581 str r1,[r5,#4]
582
583 mov pc, r7
584
第543-549行保存LR,用于返回函数。然后是使能nandflash,和上面几个函数是一样的。第551-560行发送命令0x50,是READ2命令,其实是想读OOB的信息,碰到的micro,sandisk,Toshiba的NANDFLASH,查找坏块的方法就是读出OOB中的一个字节,然后与0xff相比,这里也是一样,发送完命令后,下面是地址。
第565-571是BUSY信号的等待。然后测试现在是BUSY,还是READY信号。
第573-583行是取出读出的数据与0xff比较,返回函数。
585 ReadNandPage
586 mov r7,lr
587 mov r4,r1
588 mov r5,#NFCONF
589
590 ldr r1,[r5,#4] ;NFChipEn()
591 bic r1,r1,#2
592 str r1,[r5,#4]
593
594 mov r1,#0 ;WrNFCmd(READCMD0)
595 strb r1,[r5,#8]
596 strb r1,[r5,#0xc] ;WrNFAddr(0)
597 strb r0,[r5,#0xc] ;WrNFAddr(addr)
598 mov r1,r0,lsr #8 ;WrNFAddr(addr>>8)
599 strb r1,[r5,#0xc]
600 cmp r6,#0 ;if(NandAddr)
601 movne r0,r0,lsr #16 ;WrNFAddr(addr>>16)
602 strneb r0,[r5,#0xc]
603
604 ldr r0,[r5,#4] ;InitEcc()
605 orr r0,r0,#0x10
606 str r0,[r5,#4]
607
608 bl WaitNandBusy ;WaitNFBusy()
609
610 mov r0,#0 ;for(i=0; i<512; i++)
611 1
612 ldrb r1,[r5,#0x10] ;buf[i] = RdNFDat()
613 strb r1,[r4,r0]
614 add r0,r0,#1
615 bic r0,r0,#0x10000
616 cmp r0,#0x200
617 bcc %B1
618
619 ldr r0,[r5,#4] ;NFChipDs()
620 orr r0,r0,#2
621 str r0,[r5,#4]
622
623 mov pc,r7
624 ]
625
第586-592行是NANDFLASH的片选
第594-608行发送命令0,然后发送地址0,就是从页内地址0,也就是块的第一个页开始读出。紧跟着下面几个地址命令。(参考K9F1208 PAGE8)看地址应该如何赋值。
第608行等待数据到FLASH内部的缓冲区。
第610-617行把取出的数据放到要用的BUFFER中,因为页的大小为512个,所以会有512个字节的数据。
第619-623行disable nandflash,然后返回.