NOR FLASH 与ucLinux

2019-07-13 07:30发布

 uClinux是近几年兴起的一个嵌入式Linux的变种。它主要用于微控制领域的一些没有MMU(Memory  Management Unit)的系列CPU。近来在消费类电子产品中也得到了广泛的应用。
        目前uClinux可以稳定的运行在ARM,MIPS,Powrepc等系列CPU上。因为没有MMU单元的CPU相较而言更为便宜,因此国内也有大批的爱好者自己制作开发板来进行uClinux的开发。作为此类的嵌入式系统一般采用Flash作为存储设备。本文主要讨论在uClinux系统中启动,运行时使用Flash的理论和实践方法。
        本文首先要介绍一下常用的Flash以及uClinux系统在Flash中如何进行存储。接着会阐述一下Flash在内核中被如何驱动,同时介绍一下常用根文件系统的选择。
         本文在理论上分析,阐述了怎样使用,选择根文件系统以及它们在Flash上的实现。并且给出了一个例子。但是真正的嵌入式系统中如何使用Flash将和使用的硬件相关。欢迎大家和我讨论这个问题。                  
         1,Flash的简介
          在过去的20年里,嵌入式系统一直使用ROM(EPROM) 作为它们的存储设备。然而近年来Flash 全面代替了ROM(EPROM)在嵌入式系统中的地位。因为相较ROM而言,Flash有成本低,可*,容易改写等优点。
       目前Flash主要有两种 NOR Flash 和 NADN Flash 它们在应用上有所不同因此也用于不同的场合。
       读取NOR Flash和读取我们常见的SDRAM是一样的。它的所有地址都是可见的,你可以读取它任意随机地址的值。同时它和SDRAM一样你可以直接运行装载在NOR FLASH里面的代码,这就是作谓的XIP(Execute-In-Place)技术。因为NOR 
Flash有这种特性,所以它非常适用于小型嵌入式系统。你可以把你的代码装载到Flash中,在系统启动的时候直接运行它,而减少SRAM的容量从而节约了成本。
       从这种意义上来说,NOR FLASH已经可以代替原先我们一直使用的标准的ROM。并且还具有ROM所没有的特性。
       目前市面上的FLASH 主要来自Intel,AMD,Fujitsu,和Toshiba。常用的容量一般在128K到64M之间。
        NAND Flash 没有采取内存的随机读取技术。它的读取是以一次读取一快的形式来进行的,通常是一次读取512个字节。采用这种技术的Flash比较廉价。但是和所有块设备一样,NAND 
       Flash 比较容易出现怀位。这需要我们采用软件来避免使用这些位。这样以来就增加了软件的复杂度。你不能直接运行NAND Flash上的代码。因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。这样作会增加系统的复杂度。不过最近这种现象有所改观。三星最近生产的一批采用ARM Core的CPU,采用了一个内部的缓冲来存放NAND Flash里读取的东西。以此来直接运行NAND FLASH里面启动代码。比如基于Arm920T和新的S3c2410芯片。
         另外,我们最常见的NAND FLASH的应用是嵌入式系统采用的DOC(Disk On Chip)和我们通常用的“闪盘”。
      目前生产NAND Flash的主要厂家有Samsung 和Toshiba。最大容量已经突破了1G位。写Flash和写SRAM截然不同。它是通过一系列指令才能完成一个写操作的。而我们同用的RAM直接写入即可。无论是NOR Flash 还是NAND 
        Flash都有一个“扇区”的概念。这个“扇区”从8K到256K不等。在写操作中它将作为一个整体来操作。 要向某个地址里面写如一个值得先看一下这个地址原先的值是不是全为“1“。如果全为“1”,那么通过一系列指令可以将这个值写如。反之,则先要进行擦除使其全部变为“1”。擦除操作是不能用一个地址来操作的。擦除必须一次擦除一个“扇区“。把这个“扇区”所有的值都变为“1”,然后才能进行写操作。
        不同型号的Flash的操作指令不同。具体操作的时候需要自习阅读你所使用产品的产品说明书。
        关于Flash的话题还有很多,但是本文着重谈论Flash在uClinux系统中的应用。对Flash本身感兴趣的读者可以自行阅读其他相关资料         2,基于Flash的uClinux系统
        传统意义上嵌入式系统的存储系统通常由Flash和RAM组成。Flash的成本通常又高于RAM的成本。因此系统的存储系统由多大容量的RAM多大容量的FLASH组成,通常根据产品的需求和成本的考虑来决定。
        在基于uClinux的嵌入式系统中,Flash是整个系统代码和数据的储存器件。通常的做法是将uClinux核心的起始代码放在处理器加电所运行的地址处。这个地址必定是在FLASH中的。因为NOR Flash里面的代码可以直接执行。在很多的应用中采用了不使用RAM,直接让uClinux在Flash里面运行。而出于速度和成本的考虑更多的做法是将uClinux系统存放在Flash中根据系统运行的情况把要执行的部分拷贝到RAM中执行。
        为了实现上述思想我们通常把Flash分区。当然这里分区的概念和我们给硬盘分区的含义大不一样。这里的分区十分简单,就是把Flash按照地址分成不同的区间在特定的区间中放入不同的代码或者数据。使得整个系统比较有序和容易调试开发。
                  一个典型的例子,比如:                   块号 地址 用途
                  0 0x0—0x? 启动代码
                  1 
                  2 0x….. uClinux 核心
                  3 
                  4 0x… 根文件系统
                  5  
          在这个例子中第0个分区,也就是Flash的起始位置我们放置了系统启动代码。第1个分区我们可以放置一些配置数据之类的数据。接下来的一个分区我们存放着uClinux的核心。并且我们用另外的一些分区来放置系统的根文件系统。
         以此可见,我们完全可以根据自己的需要把Flash分成不同的区来使用。在以后的讨论中我们可以直到uClinux的块设备驱动支持我们这样的操作。因此我们的焦点将集中在如何划分不同的区域来达到我们的应用。
        通过上面的介绍我们知道Flash的擦写需要根据Flash的扇区来进行。一次擦写至少要擦除一个扇区内所有的内容。因此我们给Flash分区的时候最先要确定的是每个分区需要多少个Flash的扇区。从第几个到第几个,这样以后的操作才可*简单。
        从上面的例子来看,uClinux的内核和根文件系统并不在一个分区。内核放在一个特定的地址。要访问内核必须从某个地址开始整个的访问。而不同于我们同用的桌面版或者服务器版的Linux可以把内核作为一个文件来存放在文件系统中。
         那么这两种方法有什么不同呢?
         在我们的例子,把内核单独放在一个分区中。这需要启动的时候启动程序把整个内核拷贝到RAM里面然后运行它。或者采用我们前面提到的XIP 
         技术在Flash里面运行。而传统的Linux则需要启动程序来确定内核的位置和需要把内核的那部分加载到RAM里面来运行(类似于台式机Linux系统上的LILO或者GRIUB)。
         那么如何选择Flash的分区,以及各个分区存放的内容呢?我想这个要根据各自产品的特点和开发的周期来考虑。接下来我们列出在uClinux系统中常用的几种分区方法并且讨论它们各自的优劣。
                  a) 内核和根文件系统都在固定的分区固定的地址
                  b) 内核在根文件系统之后或者之前
                  c) 压缩的内核作为根文件系统下的一个文件
         a的优势在与系统主要的组成部分都有各自固定的地址。启动程序可以直到内核所在的地址,而内核直到根文件系统的地址。这样启动程序加载内核或者内核挂装根文件系统的时候所进行的操作比较简单。并且我们可以很方便的升级这些组成部分。缺点就是将不可避免的造成内核和根文件系统之间Flash的浪费。
        b的做法节约了一部分Flash的空间。但是它把内核同根文件系统一快编译成了一个二进制文件。这样你必须同时升级内核和根文件系统。不过这样作的好处是编译选项比较简单。容易维护。初次开发建议使用这种方法。
       c的做法由于使用了压缩的核心所有节约了大量的空间。但是必须需要一个启动程序来把内核解压缩到RAM中。这样就需要一个比较充裕的RAM空间。启动程序增加了复杂度。不过一旦你写好这个驱动程序,你就可以不再修改它而将所有精力放在uClinux核心和根文件系统的开发上。
         当然,根据产品需要我们不排除使用多个文件系统的选择。原因很简单,比如你需要对你的某个分区进行读/写操作而对其他一个区只要进行只读操作。由于Flash的读/写特性您就要作比较复杂的设计。对于这种情况,下文有所提及。           3,uClinux系统的bootloader
         作为系统的启动程序,最先要考虑的是CPU在加电的时候运行那个地址的代码。有些CPU比如X86,ARM在加电的时候运行固定地址的代码;也有些CPU比如m68k, ColdFire,在加电的时候读取一个固定的地址,然后用这个地址的值作为最先执行代码的的地址。在现有的系统中这个地址是在Falsh里面的。
        那么我们要考虑的就是在这个地址里面放入我们的代码,以便CPU加电后就执行这行代码。
         我们允许CPU加电后直接运行uClinux的内核代码。这时候uClinux的代码需要作一系列的事情。比如初始化硬件,比如初始化RAM;把uClinux内核中的数据段拷贝到RAM中去;清空BSS段等。不过最重要的还是将它的首行代码放到合适的地方。
           使用BootLoader我们就可以做根多的事情。比如我们可以初始化硬件,比如RAM和系统的I/O设备等。同时它还可以装载写在Flash上的不同内核,或者通过外部设备传输过一个内核并且装载运行它。
           在现阶段的开发板上,很多都采用了这种方式通过串口或者网口加载Pc机上编译好的uClinux内核或者根文件系统。
           除此以外,一个好的bootloader还能够保证内核影像的正确执行,防止新传输来的内核影像不完整等等。一般情况下bootloader 都是固定的烧写在Flash上面并且一般采用锁定的方法防止被擦除。 目前有很多成数的bootloader  可以在互联网上自由下载。它们支持各种开发板上的uClinux的开发。比较著名的有CoLilo, My Right Boot (MRB), PPCboot and Motorola dBUG。它们功能强大并且可以很方面的移植到你自己的开发板上。       4,uClinux内核的块设备驱动
        对于uClinux 的根文件系统,目前有三种块设备的驱动可以选择它们分别是:
                  a) Blkmem 驱动
                  b) MTD 驱动
                  c)  RAM disk 驱动 
         Blkmem 驱动是专门为uClinux  开发的一种块设备驱动。是uClinux系统中最为古老和通用块设备驱动。它原理相对简单但是配置比较复杂,需要根据你即的Flash的分区使用情况来修改代码。当然修改的结果是它可以对一些NOR型的Flash进行读写操作。不过目前支持的Flash类型不够多。如果新加入对一种Flash的支持需要作的工作量比较大。
        Linux的MTD驱动是标准Linux的Flash驱动。它支持大量的设备,有足够的功能来定义Flash的分区,进行地址映射等等。使用MTD你可以在一个系统中使用不同类型的Flash。它可以将不同的Flash组合成一个线性的地址让你来使用。在标准的Linux 
         2.4内核中MTD有一系列的选相,你可以根据个人系统的需要来选择,定制。
         另外一种选择就是RAM disk  驱动。在PC上它经常用于没有硬盘的Linux的启动过程。它和Flash没有直接的关系。不过当Flash上启动的是经过压缩的内核时。RAM disk 可以作为根文件系统。MTD 驱动提供了对Flash强大的支持,你通过它甚至可以在Flash上运行一个可以读写的真正的文件系统,比如JFFS2。而Blkmem驱动则望尘莫及。         5,uClinux的文件系统 
        在uClinux下根文件系统有集中选择。 ROMfs是最常用的一种。它的特点是紧凑,只读。它把所有的文件按照一个文件的次序组合成一定的次序。并且它可以让它的应用程序直接在FLAH里面运行(XIP)。这样以来就减少了运行时对RAM尺寸的要求。在目前基于AMR和ColdFire等CPU的uClinux开发中绝大多数使用这种文件系统作为根文件系统。
        Cramfs是在Linux内核2.4版本以后出现的新的文件系统,它的特点是把只读的文件系统进行压缩。从而可以在Flash上存储更多的应用程序。不过因为压缩它不能本地执行应用程序,而必须解压到RAM中运行。要求比较多的RAM。
         在一些系统中需要可以读写的根文件系统。在uClinux系统中利用MTD驱动,我们可以实现一些基于Flash的日志文件系统比如JFFS 或者 JFFS2。这个文件系统的优点在于它可以避免突然断电对系统存储的影响(在标准Linux中ETX2文件系统掉电会造成数据丢失)。同时因为它们是和为Flash设计它可以保证在Flash中实现读写。如果你使用RAM disk 那么ETX2 就成为首选的文件系统。因为它是标准Linux的文件系统。所以很多操作非常方面。不过缺点是ETX2文件系统不是为嵌入式操作系统所作,所以并没有考虑存储空间的问题。由于RAM 
        disk的特性你在该文件系统上所作的改动下次启动后将不会再有。
       当然Linux支持很多文件系统,你可以根据你的喜好随意选择。不过以上说说的是uClinux系统中最常用的。
        那么我们怎么在Flash上建立一个根文件系统呢? 
        通常的做法是先在开发机上(通常是PC)作好这个文件系统的镜像。然后通过烧写Flash的工具直接烧写到Flash中去。也可以通过阅读一些文件系统自带的工具来进行构建。这里不一一赘述。       6,uClinux 的Flash工具
      在uClinux的源码包中带有一些对Flash操作的应用程序。当你采用MTD的时候这些工具变得非常有用,它们分别是:
                  erase      -- 擦除Flash的某些扇区
                     eraseall   -- 擦除整个Flash
                     lock       -- 锁住Flash(写不进去)
                     unlock     -- 解锁
                     mkfs.jffs  -- 建立一个目录结构的JFFS2文件系统
                     mkfs.jffs2 – 建立一个目录结构的JFFS2文件系统的镜像                   除此之外,还有一些更为复杂的程序是和其他应用相关的。比如和JTAG相关,Net相关等等。可以根据这个工具的说明来加以应用。                   7,应用实例
                  现在我们看一个实例。首先说一下我们的硬件平台。这个平台是基于ARM940T核心的CPU+2M的AMD Flash + 4M  SDRAM。在这个平台上我们运行uClinux 2.4 的内核,采用MTD驱动。因为我们不需要对Flash进行读写操作,所以选择了ROMfs。这样使得开发和维护想对比较简单。AMD的Flash,我所选用的是可启动型的。因为ARM的CPU在加电的时候从零地址开始运行。因此Flash的地址分配为从0X0  到0X1FFFFF。根据这个Flash的扇区大小,我们把整个Flash的分区如下:
                           2M Flash  ---MTD 6                   启动代码 (16K)MTD1
                  启动所需参数 (8K)MTD2
                  设备配置信息(8K)MTD3
                  备用空间(32K)MTD4
                  系统运行参数表(64K)MTD5
                  内核和根文件系统(1984K)MTD6           在内核的MTD驱动的选项中,我们根据所需要的分区信息对代码进行配置。运行的时候,MTD驱动会找到这个设备,并且按照以上我们的分区方案对Flash的地址进行分配。一些详细的信息和细节,在MTD驱动的代码部分有详细的描述。
          完成了这个分区,我们就可以利用我们手头的工具,把我们事先做好的系统启动程序和内核,根文件系统写入Flash。
            常用的方法有:
                  1)建立整个Flash的影像,使用烧录工具烧到Flash中
                  2)书写一个Bootloader,使用系统所带的外部接口将内核还有根文件系统传送到系统RAM中,然后写入Flash。常用的有通过串口和网口的。此类的程序有很多比如netflash等等,具体的用法请参照各命令的手册。
       运行完这些,uClinux系统已经在我们的开发板上安装好了。