Linux系统自诞生以来,不断发展壮大,支持越来越多的硬件体系,获得了日益广泛的应用,从服务器、桌面计算,到机顶盒、手机、路由器等,可以说无处不在。虽然都是Linux系统,但是嵌入式环境和通用计算环境中的软件/硬件配置大不相同。这是因为嵌入式系统大多都是为某一专门应用而特别设计的,有可能需要耐受各种恶劣环境(比如意外断电、极端温度、强冲击/振动/辐射等),还受到体积、功耗、成本等诸多因素的限制,功能针对性强,需要酌情增加一些专用的硬件(如各种传感器和专用接口),而许多通用计算机上常用的外设在嵌入式系统中不那么常见,典型的例子是硬盘、CD/DVD-ROM等大容量的非易失存储设备,在嵌入式系统中,它们通常被各种形式的闪存所取代。闪存的存储特性与硬盘等存储设备的巨大差异,导致它必须使用专用存储控制器、驱动程序及文件系统。对不同类型闪存及相应文件系统的选用,会影响最终形成系统的性能和稳定性,必须综合各种系统构件的特点及目标系统的需求做出慎重的抉择。
1 闪存类型及特性 嵌入式系统中常用的闪存有两类:NORFLASH和NANDFLASH。它们因内部结构与“或非”及“与非”门相似而得名。它们不仅在内部结构上不同,外部特性和应用也不一样。NORFLASH的容量通常不大,常见的只有几MB,可以重复擦写10万次到100万次。NORFLASH遵循CFI标准,可以通过CFI命令查询其制造商、器件型号、容量、内部扇区布局等参数,实现软件自动配置。NORFLASH的优势还在于它在出厂时能保证每个数据位都是有效的,不需要做坏块处理。NORFLASH的线性寻址特性使之可以作为启动存储器使用。与NORFLASH相比,NANDFLASH的容量可以做得很大,常见的有几十MB到几GB,可以重复擦写10万次。NANDFLASH芯片上没有地址与数据线之分,只有复用的I/O线和命令锁存(CLE)、地址 锁存(ALE)、读/写使能(RE,WE)和片选(CE)等控制线,必须通过特定的逻辑来操作。NANDFLASH不支持线性寻址,一般不能用作启动ROM。但这也不是绝对的。有些微控制器(如AT91SAM926x)提供出厂前固化在芯片内部的BOOT-ROM,并在BOOT-ROM中提供对NANDFLASH启动的支持。不过这样一来,首先启动的是BOOT-ROM中的程序,会产生启动逻辑和延时方面的种种问题,设计时需要全面考虑。另外,生产厂商不保证NAND-FLASH中每一个数据位都是有效的,除芯片中的第一块之外,允许有“初始坏块”,并约定在坏块的第一页或第二页带外区(OOB)的特定位置标记坏块。NANDFLASH还允许在使用过程中出现新的坏块,以及非坏块在读出过程中出错。基于这些特点,使用这种闪存时要做额外的坏块管理和校验/纠错工作。在写入密集型系统中,必须提供ECC及坏块换出算法,才能达到10万次的写入指标。 除了以上提到的两种闪存之外,还有一种由NORFLASH衍生的串行闪存,通常是SPI接口。这种闪存继承了NORFLASH没有坏块的优点,但不支持CFI标准,并且由于是串行接口,线性寻址没有意义,为了方便操作,有些产品中加入了类似NANDFLASH的块/页结构及基于片内SRAM的页缓存,其优势在于硬件接口简单,提供小尺寸的封装,可以显著减小PCB面积和布线复杂程度。 另外,基于NANDFLASH技术的串行闪存已经量产,使用的也是SPI接口,容量可以做到1Gb。
2 应用设计 目标应用系统是一台专用的户外显示设备,要求其具有低功耗、抗振、宽温操作及高可靠性等特点。为此,选择了AT91SAM9261/AT91-SAM9G10,它是以ARM9为核心的集成片上液晶控制器的工业级微控制器,以DATA FLASH/NORFLASH和NANDFLASH存储固件代码和数据。在设计过程中,根据不同的闪存使用需求,采取了具有针对性的方案。
2.1 启动设计 在该系统中,结合微控制器提供的功能和各种闪存的特点,可以综合使用不同类型的闪存,选择不同的启动方式。AT91SAM9261内部集成了启动ROM,其中固化了支持启动和操作闪存的程序。流程图如图1所示。当AT91SAM9261的启动模式选择(BMS)引脚在复位期间为高电平时,会运行内部固化的启动程序;否则运行外部NORFLASH中的程序。从流程图中可以看出,启动程序支持从串行闪存中启动。这是通过启动程序将串行闪存中的代码加载到内部SRAM中实现的。由于内部SRAM容量有限(依芯片型号不同,有16 KB和160 KB两种),像U-BOOt(编译后有170KB 左右,与配置有关)这样的功能,若较全面地启动加载程序(Bootloader)是不能直接从串行闪存中启动的,而只能选择两级启动程序,先从串行闪存中加载一段尽可能小的一级启动程序(通常只有4~5 KB).用于初始化关键的硬件(如SDRAM控制器。由于时序、数据线宽等参数是可变的,不可能在AT91SAM9261内部固化的启动程序中提供通用的SDRAM控制器初始化代码),然后再由一级启动程序把功能较全面的二级启动程序载入到容量足够大的SDRAM中运行,以启动系统。从NORFLASH启动时会跳过AT91SAM9261内部固化的启动程序,系统复位后执行的第一条指行就是NORFLASH中的。此时,启动程序可以只有1级,当然,为了使软件和串行闪存启动方式有较好的兼容性,也仍然可以采用两级启动程序,这样只需简单修改第一级启动程序即可适用于两种不同的硬件启动配置,为硬件设计留下更多的选择空间。由于AT91SAM9261本身的原因,从NORFLASH启动是实现宽温工作的惟一选择(AT91SAM9G10无此问题)。图2显示了不同的启动配置。
在这个AT91SAM9261系统中,分别采用了2 MB的DATAFLASH或2 MB的NORFLASH作为启动存储器,由BMS引脚选择具体使用何种启动方式。闪 存中的地址划分如图3所示,其中的bootstrap是第一级启动程序;U-Boot是第二级启动程序。
2.2 系统内核及应用程序文件系统映像 系统内核映像和各MTD分区的文件系统映像大小在几MB到几十MB不等,需要存储在容量较大的NANDFLASH中。对于系统内核,由于做了适当的裁减,其长度不大,和初始根文件系统加在一起不过几MB,如果不在乎稍长的启动时间,还可以对它使用gzip压缩,大幅度减小其尺寸。在使用U-Boot作为启动程序的系统中,由于U-Boot具有直接读取NANDFLASH块/页的能力,不需要使用文件系统,将内核映像直接写到闪存块里。
应用程序及其所需的库文件、资源文件等,作为独立的文件系统映像挂载,在此选择了带有压缩及去除重复文件功能的只读文件系统,即SqLrashFS(SquashFs文件系统已经被广泛用于各种Linux Live CD形式的发行版中,被充分证明是可靠的,并且从Linux 2.6.29版开始,它已加入到系统核心源码)。在嵌入式系统中,使用只读文件系统有许多好处,比如挂载时间短,不受掉电影响,不必在系统运行过程中处理坏块及平衡损耗等。在使用过程中由于不涉及写入,其可靠性优于可写的文件系统。 在此目标系统中,内核和初始根文件系统的U-Boot映像约为2.8 MB,应用程序、GUI子系统,以及应用程序运行过程中所需的图形和字体文件的SquashFS映像约为12 MB。系统中使用的NANDFLASH是一片总容量为64 MB的8位数据线宽的芯片,块容量是16 KB+512 B,页容量是512 B+16 B,其属于块尺寸较小的那种,与大块NANDFLASH相比,操作命令稍有区别,在驱动程序中需要区别对待。该系统中的MTD分区结构如表1所示。
2.3 应用程序对闪存的使用 大多数情况下,仅提供对闪存的只读操作是不够的。比如,U-Boot至少在更新其自身以及保存环境变量时需要写闪存;操作系统在记录日志时要写闪存;应用程序在保存用户配置及工作数据时也要写闪存。对于启动加载程序来说,问题不是很严重。因为系统处于更新及配置状态时,大多是脱离正常工作状态的,且由专人操作,操作中途发生异常情况(如掉电)的可能性不大,即使发生了,也会被及时发现和处理。对于系统日志,在嵌入式系统中可以将其关闭,以减少对闪存的写操作。应用程序对闪存的写操作是不可避免的,而且处于设备自动工作期间,需要应对各种偶然发生的异常状况,特别是意外掉电。 在Linux系统中,通过文件系统访问闪存是顺理成章的做法。目前支持NANDFLAsH的常用文件系统有YAFFS/YAFFS2,JFFS2和UBIFS等。它们都是记帐式的文件系统,各有特点,也有不足。 YAFFS/YAFFS2是专为NANDFLASH写的文件系统。在YAFFS的代码里包括管理闪存带外区(OOB)的部分,而这部分代码一般认为属于设备驱动的范畴,其他文件系统里是不含这部分代码的。YAFFS是一种稳键的记帐结构的文件系统。高效率是它追求的另一个目标。它可以用在各种操作系统中(已用于Linux,WinCE,pSOS,eCos,ThreadX及各种专用操作系统中),甚至可以在没有操作系统的环境下工作。YAFFS2支持“检查点(checkpoints)”,以避免挂载过程中耗时的扫描操作,实现快速挂载。 相对于JFFS,JFFS2有了一些改进,可以支持硬连接(hard Links),垃圾回收更有效,平衡损耗更均匀。但它在挂载时仍需要扫描寻找最新版本的闪存块,并建立RAM中的数据结构,文件系统越大,挂载时间越长,RAM开销也越大。虽然JFFS2已经通过小结节点技术减少了挂载时间,但结果仍不理想,挂载时间是s级的。 UBIFS是JFFS2的后继(原来称作JFFS3),第1个稳定版本于2008年10月加入到Linux 2.6.27版核心中,它有一个竞争者叫LogFS。UBIFS与JFFS2的最大不同在于它的文件索引信息是写在闪存中的,而JFFS2是暂存在RAM中的。因此,UBIFS在挂载时不需要扫描全部闪存空间,挂载耗时很短(ms级);UBIFS对RAM的消耗不会随着文件系统的尺寸变大而线性增长,适用于大容量的文件系统。 除这这些不同之外,各种闪存文件系统也存在一些共性。由于闪存的写入次数有限,为了避免局部因频繁写入而过早失效,必须使写入操作尽量均匀分布到所有位置上,即平衡损耗(wear leveling)。这导致了更新文件时必须做异位更新,而不能像在磁盘或RAM中那样简单地原位更新,从而引起一系列复杂的问题。首先,异位更新会导致闪存块中出现越来越多的过期页面,它们与有效页面混杂在一起,形成所谓的脏块(dirty blocks)。当所有的闪存块都成为脏块后,就没有闪存块可供擦除再分配了。因此,基于闪存的文件系统都有垃圾回收器,用于将分散的过期页面集中在一起,形成空闪存块(free blocks)。由此引起的另一个问题是文件系统在使用时不能用到接近填满,否则也会导致类似的问题。其次,树状结构的文件索引中存在大量的互相引用,某个节点的改变会引起该节点本身及直接和间接引用它的一系列节点的异位更新。 从以上的分析可以看出,NANDFLASH上的文件系统是一把双刃剑。它确实可以提供清晰的软件层次和使用上的方便,但同时也会降低操作效率,并具有潜在的可靠性问题。关键是如何合理使用,扬长避短。其实,对于写入量小(一个擦除块之内),并且不频繁的数据,可以跳过文件系统,通过ioctrl()函数直接操作闪存。这样做的缺点是破坏了软件层次,要求应用软件开发人员了解一部分硬件的细节,在应用程序中完成一些本应属于驱动程序的底层功能(如块擦除,发现和标记坏块等);优点是可以抛开复杂的文件系统,不需要挂载及卸载,更不存在文件系统的完整性问题,平衡损耗等措施可以视需要取舍,尤其在意外掉电时,该方法可将所有的读/写错误都限制在相对较小的局部,具有较好的应对掉电的能力。
2.4 闪存烧写支持 系统中的各种闪存可以用不同的方法写入数据,各种方法都有优缺点。可供选择的方法主要有两种:在芯片焊接前用通用的编程器烧写;在芯片焊接后进行在系统烧写(ISP)。使用通用的编程器可以快速大量地烧写芯片,适合大批量生产,但芯片一旦焊到印制板上,这 种方法就不能用了。在系统编程(ISP)适合烧写已经焊在印制板上的芯片,通常1次只能写1块板子,但是不需要专用的设备,只要有1台计算机和相应的连接电缆(如USB电缆)及配套软件就可以工作,非常适合小批量生产和软件升级。另外,通过ISP软件提供的硬件寄存器读/写和目标存储器读/写功能,还可以实现一定程度上的电路板测试和调试功能。 具体到AT91系列芯片可以借助芯片内固化的SAM-BA BOOT(见图1)提供的支持,通过USB或调试串口烧写系统中的闪存,尤其是通过USB烧写十分方便快捷,烧写Linux核心映像到NANDFLASH只需要几秒。对于AT91SAM9261,使用这种编程方式需要满足几个条件: (1)AT91SAM9261的BMS引脚在复位期间保持高电平; (2)SPl0的CS0对应的芯片不存在,或者对应的芯片中不存在有效的启动代码; (3)PC机与目标板之间通过串口或USB口连接; (4)PC机上安装SAM-BA工具软件。 PC机上的SAM-BA工具软件可以支持对目标系统多种存储器的读/写,默认情况下可以支持DATAFLASH,NANDFLASH、内部SRAM和外部SDRA-M。尤其是对DATAFLASH的支持非常不错,可以自动识别各种不同容量的芯片,写入速度也比较快。但是它对NANDFLASH的支持并不理想,对于某些NANDFLASH芯片,操作会失败。对于NORFLASH,则根本不提供现成的支持。不过它提供了基于COM技术的动态库,并且公开了编程接口,可以使用C/C++程序或TCL脚本控制烧写过程,对于不提供官方支持的芯片,可以自行编写代码扩展的SAMBA的功能。以扩展NORFLASH编程功能为例,需要自行编写的有以下部分: (1)下载到目标板上运行的ARM代码。这部分程序在SAM-BA v2.4中称为monitor,在SAM-BA v2.8中称为applet,其实就是供芯片内固化的SAMBA B00T调用的功能扩展部分; (2)PC机上的TCL脚本或C/C++程序。用于初始化硬件(如SDRAM控制器),向目标板下载monitor/applet,以及传送目标程序代码和控制烧写过程。 如果使用TCL脚本,在相关脚本更改完成后,运行SAMBA工具软件,会出现新添加的NORFLASH标签页,如图4所示。在扩展的各部分功能基础之上,还可以编写一个综合性的自动化脚本,将所有的程序代码及数据(如Bootstrap,U-Boot,U-Boot的环境变量、内核映像、各分区的文件系统映像等)一次性写入目标板上不同芯片内的各个指定地址,以简化编程操作,提高生产效率。
3 结语 闪存是目前嵌入式系统中广泛应用的非易失存储介质,具有可以重复写入和存储容量大等优点,但是也存在写入次数有限,操作稍显复杂和速度慢等缺陷。若使用不当,会引起性能和可靠性方面的问题。通过深入分析现有闪存相关的硬件、软件特点,在系统中按需要采取具有针对性的应用方式,设计的系统在闪存应用方面获得了较好的效果:系统启动时间较短,工作稳定,顺利通过了高温老化试验及长达数月的现场应用考验。另外,采用自动化脚本与监控程序结合的闪存烧写设计,不仅简化了生产过程,还提供了一定程度上硬件调试的支持。
|