达芬奇(Davinci)系列嵌入式处理器是TI公司的具有高速处理能力的新一代嵌入式设备[1],它同时具备了DSP和精简指令级计算机技术的优点。它集成了一个高性能的DSP核心与一个ARM9内核,被广泛应用于嵌入式图片、视频处理中[2]。在Davinci平台下,通常以ARM核为基础构建嵌入式操作系统,但是目前经常采用一片NOR Flash加上一片NAND Flash作为外部存储设备,并且通常都是256 B/页或者512 B/页的小页面NAND Flash,本文旨在只以一片2 KB/页的大页面NAND Flash(Samsung
K9K8G08U0A)作为外部存储设备、Davinci
TMS320DM6446作为处理器的硬件结构下,阐述构建稳定可靠的系统需要解决的问题。
1 问题概述
EMIF是用来连接Flash、SRAM等多种存储设备的外设端口。TMS320DM6446的EMIF端口支持每路32 MB总共4路可寻址的片选空间,支持8 bit以及16 bit的数据总线宽度,具有可编程的建立、选通以及保持时间,还具备NAND Flash ECC校验数据生成功能等[5],因此可以方便灵活地与外部NAND Flash芯片通信。在本文的硬件系统中,即采用TMS320DM6446的EMIF的CS2空间与Samsung K9K8G08U0A NAND Flash相连。
由于本系统只有一片NAND Flash作为外部的存储设备,因此所有的引导程序、操作系统内核以及根文件系统均需要存储在这上面,系统也就需要从NAND Flash启动。TMS320DM6446具有多种启动方式,具体由哪种方式启动,由系统复位时引脚BTSEL[1:0]电平决定,当BTSEL[1:0]被置为“01”时,TMS320DM6446的ARM核从EMIFA的EM_CS2存储空间开始执行(地址为0x0200 0000),这种情况下EMIF连接的是具有线性地址的非易失存储器,通常是NOR Flash。当BTSEL[1:0]不为“01”时,TMS320DM6446内部的ROM
BOOT LOADER(简称RBL)开始运行,RBL再根据BTSEL[1:0]的不同值决定从何处加载用户的引导程序UBL(USER BOOT LOADER)。当BTSEL[1:0]为“00”时,RBL将从连接到EMIF的CS2空间的NAND Flash中加载UBL。由于RBL的加载过程是将UBL拷贝到ARM的内部RAM中,因此对于UBL的大小限制在14 KB以内,但是在嵌入式环境常用的U-BOOT、ViVi等的大小都远超过这个限制,因此需要多级加载,一级引导程序主要做系统的初始化,然后将二级引导程序(在本系统中采用U-BOOT,本文后面提到的UBL均指一级引导程序)从NAND
Flash中读取到RAM中,然后启动它,由U-BOOT负责操作系统的引导[4]。于是整个NAND Flash上系统构建的关键问题包括如何移植UBL,以使其能够正常初始化系统,正常加载二级引导程序U-BOOT到RAM中,U-BOOT的移植使其满足大页面NAND Flash的读写要求以及裸机时引导程序的烧写。
2 UBL移植
UBL为TI公司提供的对于Davinci系列处理器通过内部的ROM BOOT LOADER启动时的一级引导程序。其工作流程如图1所示。
UBL的移植主要针对本系统中硬件板的结构修改系统初始化过程以及增加对Samsung K9K8G08U0A NAND Flash的支持,以下分别阐述。
2.1 系统初始化
2.1.1 设置CPU、DDR工作频率
TMS320DM6446具有两路PLL,其中PLL1通过分频供系统的主时钟及大部分外设的时钟,PLL2供DDR2使用。DSP时钟频率为SYSCLK1=27 MHz×(PLL1_PLLM+1),使用固定一分频,本系统中DSP工作在正常频率594 MHz,因此需设置PLL1_PLLM=21,即设置寄存器0x1C4 0910为21。
本系统使用两片K4T1G164QQ-HCE6 DDR2 SDRAM作为系统内存,该芯片为DDR2 667芯片,时钟频率为333 MHz。TMS320DM6446中DDR2使用PLL2的PLLDIV2分频作为时钟频率,计算公式为(27 MHz×(PLL2_PLLM+1))/(PLL2_PLLDIV2→RATIO+1)。因此设置PLL2_PLLM=23,PLL2_PLLDIV2→RATIO=1,PLL2_PLLDIV2的第15位为分频允许位,应置为1,所以PLL2_PLLDIV2为0x8001,即寄存器0x1C4
0D10=23,0x1C4 0D1C=0x8001。
2.1.2 配置EMIF接口
根据Samsung K9K8G08U0A NAND Flash的读、写时序要求,TMS320DM6446的EMIF用于与NAND Flash连接时,配置寄存器各字段值需满足如下要求:
RSETUP≥tCLR(m)/tCYC-1=0
RSTORBE≥max((tREA(m)+tSU)/tCYC,tRP(m)/tcyc)-1=1.5
R_SETUP+R_STROBE≥(tCEA(m)+tSU)/tcyc-1=2
R_HOLD≥(tH-tCHZ(m))/tcyc-1=-4
R_SETUP+R_STROBE+R_HOLD≥tRC(m)/tcyc-3=-0.5
TA≥max((tCHZ(m))/tcyc,(tRHZ(m)-(R_HOLD+1)tcyc)/(tcyc))-1≥2
W_SETUP≥max(tCLS(m)/tcyc,tALS(m)/tcyc,(tCS(m)/tcyc)-1=1
W_STROBE≥tWP(m)/tcyc-1=0.2
W_SETUP+W_STROBE≥tDS(m)/tcyc-1=0.2
W_HOLD≥max((tCLH(m))/(tcyc),(tALH(m))/tcyc,(tCH(m))/(tcyc),(tDH(m))/(tcyc))-1=-0.5
W_SETUP+W_STROBE+W_HOLD≥tWC(m)/(tcyc)-3=-0.5
其中tSU是EMIF数据建立时间,取值5 ns,tH数据保持时间取0,EMFI时钟为系统6分频,所以tcyc=1/(27×(21+1)/6)≈10 ns,根据EMIF连接NAND的取值要求,设置EMIF CS2的配置寄存器值为0x842429c。
2.2 支持Samsung K9K8G08U0A NAND Flash
UBL通过数据结构struct _NAND_DEV_STRUCT_来表示一个型号的NAND Flash,具有devID、numBlocks、pagesPerBlock、bytesPerPage几个字段。通过struct _NAND_
DEV_STRUCT_类型的数组gNandDevInfo[]来记录所有支持的NAND Flash。UBL在从NAND Flash读取数据之前,首先通过读取设备号命令0x90得到NAND Flash的设备号,然后从数组gNandDevInfo[]中查找具有相同设备号的记录,从而得到NAND Flash的详细信息,以确定NAND Flash的读方式。
因此,需要UBL支持特定的NAND Flash,只需要将其信息添加到数组gNandDevInfo[]中即可。本系统中用到的Samsung K9K8G08U0A NAND Flash设备号为0xD3,具有8 192个存储块,每个块具有64个页面,每页具有2 048 B数据存储区域以及64 B的Spare区域,在数组gNandDevInfo[]中添加{0xD3,8192,64,2048+64}即可。
3 U-BOOT移植
本系统中使用的U-BOOT引导程序由TI公司提供的支持Davinci平台以及NAND Flash启动的U-BOOT1.1.3移植而来。
3.1 NAND Flash读写时序
U-BOOT1.1.3不支持2 KB/page的大页面Flash,因此移植过程主要是增加NAND Flash的读写、擦除。2 KB页面NAND Flash与普通读写擦除最主要的区别在于地址构成不同,本系统中用到的Samsung K9K8G08U0A NAND Flash总共存储空间1 GB=230,每页大小为2 KB=211,因此总的地址长度30 bit,从A0~A29,页地址长度为11 bit,从A0~A10,本系统采用8 bit的地址数据宽度连接NAND Flash,页地址和块地址需要分不同的地址周期,因此NAND的地址需要5个周期送出,前两个周期为页地址,依次为地址的A0~A7、A8~A10,后三个周期为块地址,依次为地址的A11~A18、A19~A26、A27~A29,页地址和块地址的最后一个周期不足8位,不足的高位均为0。
Samsung K9K8G08U0A的读过程如下:写0x00命令、分5个周期写地址、写0x30命令、读数据、根据读出的数据生成ECC校验数据、生成的ECC数据与读出的ECC数据比对以确定数据是否有误以及能否校正。
写过程如下:写0x80命令、分5个地址周期写地址、送出数据(包括ECC校验数据)、写0x10命令、读取状态直到busy信号无效、检查是否出现写错误。
擦除过程如下:写0x60命令、分三个地址周期写块地址、写0xD0命令、读取状态直到busy信号无效、检查是否出现擦除错误。
3.2 YAFFS2文件系统烧写
YAFFS2镜像烧写与U-BOOT下普通写NAND Flash区别在于spare区域的数据不需要程序根据数据存储区的数据生成,spare区域的数据在制作YAFFS2镜像时,已经由镜像制作工具生成并写入了镜像文件。因此在nand命令的write中增加.yaffs2选项,当使用nand write.yaffs2命令时,直接从指定地址中读出2 048 B/页数据以及数据后紧跟的64 B的spare区域数据,并将其写入NAND Flash中。
U-BOOT在Flash的读写过程中需要检查坏块情况,在开始读写每个块的时候首先检查该块第一页以及第二页的spare区域的第一个数据是否为0xFF,如果不为0xFF则当前块为坏块,需要跳过它。
4 烧写程序
在UBL以及U-BOOT被固化进NAND Flash之前,系统处于裸机状态,无法正常引导操作系统,烧写程序的作用是在裸机状态下借助仿真器的作用,将UBL以及U-BOOT烧写到NAND Flash正确位置的。
前面已经提到,本系统采用的Samsung K9K8G08U0A NAND Flash具有8 192个存储块(block)。这8 192个块按照如下分配其使用方式:第0块在出厂时确保不是坏块,用作整个NAND Flash的坏块信息存储;第1~3块存储UBL;第4~7块存储U-BOOT;第8块存储U-BOOT环境变量;第9~40块存储Linux操作系统内核;第41~8 191块存储YAFFS2文件系统。
实际中,UBL和U-BOOT都只需要占用一个存储块的存储空间,由于考虑到NAND Flash可能有坏块的存在,于是在设计烧写程序时,为UBL增加了2个冗余块,为U-BOOT增加了3个冗余块,以确保系统稳定可靠地从NAND Flash上启动。
烧写程序是系统在没有任何程序的裸机情况下执行的,烧写程序需要通过仿真器加载到系统目标板的DDR2中运行。由于加载程序时系统未执行任何程序,也就没有做任何初始化,DDR2也处于不可用状态,系统将无法加载程序。TI的CCS集成环境提供了GEL文件来解决这一问题,在仿真器连接目标板时会自动执行GEL文件中的OnTargetConnect()函数,在该函数中,需要对系统做初始化。
烧写程序在被加载到内存后,即可被执行来完成UBL以及U-BOOT的烧写,程序执行过程如图2所示。
数据写入时需要注意UBL是由
TMS320DM6446内部的ROM
BOOT LOADER读入到内部RAM中然后执行的,因此,烧写程序对于ECC校验数据的生成以及ECC数据在spare区域的存储位置必须要与ROM BOOT LOADER读取数据时的校验方式一致。ROM BOOT LOADER采用EMIF的硬件ECC校验,每512 B的数据产生4 B的校验数据,并按照如下方式存储:spare区域地址从0x00到0x3F,其中0x08-0x0B存储第1个512 B数据的第3-0位ECC数据,0x18-0x1B存储第2个512 B数据的第3-0位ECC数据,0x28-0x2B存储第3个512
B数据的第3-0位ECC数据,0x38-0x3B存储第4个512 B数据的第3-0位ECC数据。因此,在烧写程序中也使用EMIF硬件ECC校验来生成校验数据,在每次写入数据达到512 B时,通过读寄存器NANDF1ECC(地址为0x0200 0070)来获得ECC值,最后在一页数据写入完毕后写入到spare区域的对应位置。
在UBL以及U_BOOT成功烧写到NAND Flash后,系统上电,U_BOOT成功执行,通过U_BOOT将Linux操作系统内核以及YAFFS2文件系统镜像烧写到NAND Flash,设置U_BOOT环境变量,再次引导系统,Linux系统正常启动。本系统中成功实现了从裸机到整个系统的构建,解决了对大页面NAND Flash的不支持,同时考虑了NAND Flash存在的坏块情况,系统在实际使用中运行稳定可靠。
参考文献
[1] TI Corporation.TMS320DM6446 Digital Media System-on-Chip[EB/OL].[2008-03-31].http://www.ti.com/lit/gpn/tms320dm6446.
[2] TI Corporation.TMS320DM644x DMSoC ARM Subsystem Reference Guide[EB/OL].[2009-03-31].http://www.ti.com/litv/pdf/sprue14b.
[3] TI Corporation.TMS320DM644x DMSoC Asynchronous External Memory Interface(EMIF) Reference Guide[EB/OL]. [2009-02-24].http://www.ti.com/litv/pdf/sprue20c.
[4] 王化福,孙同景.从NAND Flash启动嵌入式操作系统[J].可编程控制器与工厂自动化,2009(5):79-80.
[5] Samsung Corporation. K9XXG08UXA Flash Memory[S],2006.