Chinaunix首页 | 论坛 | 博客
  • 博客访问: 444605
  • 博文数量: 72
  • 博客积分: 3186
  • 博客等级: 中校
  • 技术积分: 1039
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-07 16:53
文章分类

全部博文(72)

文章存档

2012年(1)

2011年(5)

2010年(10)

2009年(56)

我的朋友

分类: 嵌入式

2009-10-15 15:47:29

vivi中对nandflash的操作还是比较多的,比如vivi启动时,首先要将自身的代码从nandflash里复制到sdram里,这一步就需要对nandflash的读取。此时可以通过nandflash控制器来操作,所以实现起来会简单一些。
到了vivi后面的阶段会引入mtd,用以增强对nandflash的操作(读、写、ecc较验等)。mtd封闭了一些对nandflash操作的细节,对上层操作提供统一的接口,如读、写等等。
最后,vivi引导内核时还需要正确的分区,比如bon分区和mtd分区,如果分区上出了一点错误,最终将导致内核不能正常的引导启动。而这些都与nandflash是密切相关的。可见nandflash的重要性。

单独拿nandflash出来讲,还是有点偏大。于是我决定从几个层次来认识与nandflash有关的操作。

这里我的芯片是三星的K9F1208,很通用的一款芯。
容量为:64M(67,108,864)x8bit。nandflash)(注意宽度为8位)
另外还有 2,048K(2,097,152)x8bit 即2M的额外空间。这个在下面会分析到。
大体上了解了这么多的参数就够了。

先来画一个nandflash的草图吧。下面是nandflash的一个block。
                                                                  --> obb(out of bank)
                                                                 /
                                                                |
                       |----> 256B <-----|-----> 256B <----|-> 16B <-|
                       +-----------------+-----------------+---------+
                      /                 /                 /         /|
                     /                 /                 /         / |
                    /                 /                 /         /  |
                   /                 /                 /         /   |
                  /                 /                 /         /    +
                 /                 /                 /         /    /|
                /                 /                 /         /    / +
               /                 /                 /         /    / /|
              /                 /                 /         /    / / |
             /                 /                 /         /    / /  |
------      +-----------------+-----------------+---------+    / /   |
 |          |                 |                 |         |   / /    |
 |          | 1st half page   | 2st half page   |         |  / /     |
 |          |   =256Bytes     |   =256Bytes     |         | / /      |
 |          +-----------------+-----------------+---------+  /       +
\|/  1page->|                 |                 |         | /       /  -> bit0
            +-----------------+-----------------+---------+        /  -> bit1
32pages     |                 |                 |         |       /  -> bit2
=1Block     |                 |                 |         |      /  -> bit3
/|\         |                 |                 |         |     /  -> bit4
 |          |                 |                 |         |    /  -> bit5
 |          |                 |                 |         |   /  -> bit6
 |          |                 |                 |         |  /  -> bit7    
------      +-----------------+-----------------+---------+        |__ 1 Bytes = bit[7:0]
            |----------->  512 Bytes  <---------|


这里我们需要了解到:
这款芯片是8位的,即通过8个io口来与cpu通信(包括地址、数据、命令的传送)。
如上图所示,8个bit组成1个Byte。
有多个Byte组成Page。1Page = 528 Bytes = 512 Bytes + 16 Bytes。注意这里每一页由两部分组成。前一部分512B是我们存储数据用的,后16B是用来存储ECC校验码等额外数据的。所以我们能使用到的就只有前512B。
1Block = 32Pages 。所以1Block = 32 * (512 + 12) Bytes = 2^5 * (2^9 + 2^4) = 2^14 +2^9 Bytes
我们这款芯片总共有4096个Block。所以总容量为:
4096 * (2^14 + 2^9) = 2^12 * (2^14 + 2^9) = 2^26 + 2^11 = 64MB + 2MB
在nandflash里寻址时,我们要定位到某一Byte,必须有三个地址需要确定:
1. 确定是在哪一个Block中。即Block Address
2. 在Block中的哪一页。 即Page Address
3. 一页中,具体是哪一个Byte。看上图Byte如同一列一列的排布,所以称为 Column Address。
定位64M中某一Byte,需要26位地址。再画个图比较直观一些:

   26              14 13              9  8  7               0
   +-----------------+-----------------+---+-----------------+
   |  Block Address  | Page Address    |   Column Address    |
   +-----------------+-----------------+---+-----------------+
注意到bit8了吧。由于bit[7:0]只能表示256个数,而一页的数据区有512个,所以在这块芯片里将一页分为了两个半页。当bit8=0时为第一半页,bit8=1时为第二半页。bit8是由硬件来控制的,当读完一个半页之后,bit8会自动置位。

如此一来,当我们要取nandflash的某一Byte的数据时,就需要分4步来传递地址,然后才能读取到那一字节的数据。
1. 传递bit[7:0] 。相当于传递了Column Address
2. 传递bit[16:9] 。
3. 传递bit[24:17] 。
4. 传递bit[25] 。
可以看到这四个步就是为了传递整个26位地址(bit8特殊)。这就是我们通常所说的“四步寻址”。


由于s3c2410已经集成了nandflash控制器,所以对于nandflash的操作,地址、命令、数据的传递都是通过写nandflash控制器的寄存器来完成的。对于nandflash控制器的详细说明可以看2410的数据手册。
数据手册里有以下几句话,是关于nandflash控制器的配置的。在从nandflash里读出数据时,我们也是按照这个顺序来的。

NAND FLASH MODE CONFIGURATION
1. Set NAND flash configuration by NFCONF register.
2. Write NAND flash command onto NFCMD register.
3. Write NAND flash address onto NFADDR register.
4. Read/Write data while checking NAND flash status by NFSTAT register. R/nB signal should be checked
   before read operation or after program operation.

vivi最开始使用nandflash是要把vivi自身从nandflash复制到sdram里。来看一下它是怎么完成的。


buf 为目标地址。 start_addr是代码位于nandflash中的起始地址(一般为0)。size为代码的大小。
在调用这段代码之前,已经将NFCONF等都配置好了(详见head.S)

#define NAND_SECTOR_SIZE 512
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
int
nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
        unsigned int i, j;

        if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
                return -1;

        /* chip Enable */
        NFCONF &= ~0x800; //使能芯片

        for (i = 0; i < 10; i++);

        for (i = start_addr; i < (start_addr + size);) {
                /* */
                NFCMD = 0;

                /* Write Address ,这里分四步写地址,上面已经分析过了*/
                NFADDR = i & 0xff;
                NFADDR = (i >> 9) & 0xff;
                NFADDR = (i >> 17) & 0xff;
                NFADDR = (i >> 25) & 0xff;

                wait_idle();//写完地址后等待,直到nandflash返回一个标志信号,表明已处理完,然后再进入下面的工作。


/* 完成了之前的工作之后,便可以从nandflash里读出数据了。读数据是直接从寄存器NFDATA中取出,然后写入目标地址 */
                for (j = 0; j < NAND_SECTOR_SIZE; j++, i++) {
                        *buf = NFDATA & 0xff;
                        buf++;
                }
        }

        /* chip Disable */
        NFCONF |= 0x800; //每次在完成了对nandflash的操作之后,便将其禁止掉。


        return 0;
}

上面这段程序很清楚。但需要注意一个地方,一次读取要读512(NAND_SECTOR_SIZE)个字节。读取的时候默认是从第一个半页开始读取的。如果要从第二个半页开始读取,我们则要先写入一个命令,来使bit8置位。读完1st half page后,bit8自动置1,读完2st half page后,bit8也会自动清0 。
同样,读取时,我们也可以不从一页的开始读取。比如,可以从一页的中间读取,这种情况我还没有分析太清楚,但上面的程序显然是必须从某一页的开始读起的。因为在for循环里会连续向buf写512个数据。推荐从页始开始读比较好。

nandflash好些个command。写入不同的command时,也就可以对其进行不同的操作。

----------------------------+--------------+----------+-----------
               Function     |  1st. Cycle  |2nd. Cycle| 3rd. Cycle
----------------------------+--------------+----------+-----------
Read 1                      |   00h/01h(1) |     -    |     -      00:读1st half page  01:读2st half page
----------------------------+--------------+----------+-----------
Read 2                      |      50h     |     -    |     -
----------------------------+--------------+----------+-----------
Read ID                     |      90h     |     -    |     -
----------------------------+--------------+----------+-----------
Reset                       |      FFh     |     -    |     -   
----------------------------+--------------+----------+-----------
Page Program (True)         |      80h     |   10h    |     -
----------------------------+--------------+----------+-----------
Page Program (Dummy)        |      80h     |   11h    |     -
----------------------------+--------------+----------+-----------
Copy-Back Program(True)     |      00h     |   8Ah    |    10h
----------------------------+--------------+----------+-----------
Copy-Back Program(Dummy)    |    03h       | 8Ah      |    11h
----------------------------+--------------+----------+-----------
Block Erase                 |    60h       | D0h      |    -
----------------------------+--------------+----------+-----------
Multi-Plane Block Erase     | 60h----60h   | D0h      |    -
----------------------------+--------------+----------+-----------
Read Status                 |    70h       |   -      |    -     
----------------------------+--------------+----------+-----------
Read Multi-Plane Status     |    71h       |   -      |    -     
----------------------------+--------------+----------+-----------



阅读(1474) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~