转自:http://blog.chinaunix.net/space.php?uid=7214824&do=blog&id=160528

下面继续分析,由于后面的代码和函数量相对的多且复杂,所以不再贴上代码,只说流程,只要把程序源码打开对着看很容易就明白了。

 
      在DEVICE_init()这个大的平台初始化函数结束以后,下面要做的工作就是将NAND的中UBOOT复制到DDR中并且跳转到那里,然后UBL的历史使命结束,UBOOT的执行开始,那么就是调用这个函数,NANDBOOT_copy()。
 
      NANDBOOT_copy()函数主要分三大块,第一大块是调用NAND_open()函数对NAND控制器和NAND芯片进行初始化,它传递的参数是CE的地址(一般是CE0)和总线位宽(一般是8位)。好,我们跳到NAND_open()函数中。
 
      NAND_open()函数在common\driver\src\nand.c中,与平台无关。首先定义一个NAND_InfoHandle结构 hNandInfo,这个结构包含了所有与NAND有关的参数,比如厂商ID,设备ID,基址,位宽,块数,页数每块,页大小及与ECC,BB有关的一些 函数。然后对这个hNandInfo进行赋值,这里最重要的是把 DEVICE_NAND_PAGE_layout,DEVICE_NAND_ECC_info,DEVICE_NAND_BB_info,DEVICE_NAND_CHIP_infoTable 这四大全局结构赋给了hNandInfo的相关字段。这四大全局结构在每个平台目录下都有定义(比如dm36x\common\src \device_nand.c),编译不同平台的UBL,会将不同平台的四大全局结构传递给nand.c中使用,这样就做到了NAND的驱动分离中平台无 关部分和平台有关部分,这种思想也贯穿在整个LINUX的架构中,先不提这个。然后,操作AEMIF寄存器使能NAND的哪一个CE中,这个由传递的基址 决定,一般是CE0,并使能这段片选之上有ECC功能。
 
      接着调用NAND_reset()函数对NAND芯片进行复位,这个函数本身很简单,就是先发复位命令0xFF,再等待操作完成,懂得NAND操作过程的 会很容易这个操作。但是这里多说一点,一般我们遇到的芯片操作NAND,其CPU内部会有一个NAND控制器,对NAND的操作会通过这个寄存器来操作, 发命令发地址等都是操作这些寄存器来完成。但是DM365不是这样,它没有实质上的NAND控制器,它还是将NAND当做是一个类RAM一样的操作,通过 总线上的时序来完成。CLE和ALE连在地址总线A2和A1上,这样的话发命令实质上就是在总线地址0x00000010u上写值(注意DM365的地址 线问题,有个32位问题),发地址实质就是在总线地址0x00000008u上写值,读写数据实质就是在总线地址0x00000000u上读写值。这些地 址的操作隐含着CLE和ALE的高低电平,再加上配好的时序参数就把NAND的操作弄成了类RAM的操作。这种操作以前没有碰到过,TI的工程师很牛。
 
      接着调用LOCAL_flashGetDetails()函数,这个函数很重要,它会得到NAND的很多详细信息。首先它会判断NAND是否是ONFI, 这个没有细究过,跳过不管。然后它会向NAND发读ID命令,从而得到NAND的厂商ID和设备ID值。有了这两个值就好办了,通过一个循环把它们通四全 大局结构中的DEVICE_NAND_CHIP_infoTable中定义的各种NAND的资料进行对比,通过ID来定位是哪一种NAND,然后就得出了 页数,块数,页大小等重要信息。另外,还要区分NAND是大型还是小型(一般看页大小,大于2K为大),因为它们的地址周期不同。这个函数后面还有一些东 西,没有深究。调用完成后再复位一下NAND,整个NAND_open()函数就结束了。
 
      NAND_open()函数完成后,会调用NAND_readPage()函数从第25块开始一直搜索到第50块,只读第0页。因为UBOOT放在第25 块开始的地方,这是手册规定的,而且第0页放一些启动信息等等。通过MAGIC_NUMBER来判断是否有效,这个知识点详见手册。一旦 MAGIC_NUMBER正确,就会将后面的几个字段,比如UBOOT的进入点,页数,UBOOT在哪一个块,哪一个页,UBOOT的装载点等信息就会得 到。而后循环调用NAND_readPage()函数将整个UBOOT从NAND拷贝到DDR里,最后将刚才得到的UBOOT的进入点地址复制到 gEntryPoint变量中。然后主函数跳转到这里,从此UBL结束,UBOOT正式启动。
 
      这里的分析没有涉及到坏块处理,ECC校验的部分,一方面现在太忙没时间细看。另一方面UBL不是重点,要想深究这个东西,还是到Linux内核里的 NAND驱动中深究,这里能用就行了。还有就是一开始担心的是我们项目用到的NAND是512B每页的老式NAND,担心UBL不支持,需要修改UBL源 码。后来读了UBL源码以后,特别是平台相关的device_nand.c文件,才发现TI的工程师已经把该考虑的都考虑的,都能用,老外就是牛X。不像 国内的代码,根本没有普适性和跨平台性。UBL整个源码,架构较清晰,注释也多,代码整体很完整和美观,很有参考和学习价值。读了UBL源码后,其实很容 易就能读懂NANDEraser,NANDWriter等的源码了,说白了就是平台初始化加NAND读写,呵呵。