Chinaunix首页 | 论坛 | 博客
  • 博客访问: 139490
  • 博文数量: 17
  • 博客积分: 1420
  • 博客等级: 上尉
  • 技术积分: 210
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-18 14:29
文章分类
文章存档

2011年(1)

2009年(12)

2008年(4)

我的朋友

分类:

2009-05-25 20:16:45

本文主要结合Jflash的源码讨论如何利用JTAG下载二进制文件到ARM的原理。为了实现这个功能,我们需要用到并口、JTAGNand Flash以及ARM的一些调试架构,下面各章将分述之。我的想法是:每一章讨论上面的一个问题并对Jflash源码中相关的部分进行说明。

JflashLinux平台上用来烧写程序到ARM平台的一个工具,它涉及到很多底层硬件,如欲烧写的开发板的硬件配置,其如下:

CPUS3C2440 Nand FlashK9F120864M Nor Flash       JTAG接口采用Sdt接口。

知识

我们的最终目的是将指定的二进制文件下载到Nand Flash上,所以本章先讨论Nand Flash的操作。

S3C2440处理器集成了8NandFlash控制器。目前市场上常见的8NandFlash有三星公司的K9F1208k9f1g08k9f2g08等。k9f1208k9f1g08k9f2g08的数据页大小分别为512Byte2kByte2kByte。它们在寻址方式上有一定差异,所以程序代码并不通用。本章结合S3C2440K9F1208,讨论NandFlash的读写方法。

NAND Flash 的数据以bit 的方式保存在memory cell。一般来说,一个cell 中只能存储一个bit。这些cell 8 个或者16 个为单位,连成bit line,形成所谓的byte(x8)/word(x16),这就是NAND Device 的位宽。这些Line 会再组成Page,而Page再组成Block。目前Nand FlashPage有几种大小,下面以K9F1208 进行说明。

对于K9F1208每页528Byte,其中 16Byte用于标识该页的一些信息,称为Spare Area。每32 page 形成一个Block,即:

1 Block = 32 page = 32 * 528 Byte = 32 * (512Byte + 16Byte),可存储16K 的内容。

K9F1208 可存储 64M Byte的内容(不包含Spare Area)。

64M Byte = 4K Block = 128K page

Nand flash 以页为单位读写数据,而以块为单位擦除数据。按照这样的组织方式可以形成所谓的三类地址:

-- Block Address -- Page Address -- Column Address

对于K9F1208来讲,地址和命令只能在I/O[7:0]上传递,数据宽度是8 位。

512byte需要9bit来表示,对于528byte系列的NAND,这512byte被分成1st half2nd half各自的访问由地址指针命令来选择,A[7:0]就是所谓的column address32 page 需要5bit 来表示,占用A[13:9],即该page 在块内的相对地址。Block的地址是由A14 以上的bit 来表示,例如512Mb NAND,共4096block,因此,需要12 bit 来表示,即A[25:14],如果是1Gbit 528byte/pageNAND Flash,则block addressA[26:24]表示。而page address就是blcok address | page address in blockNAND Flash 的地址表示为: Block Address | Page Address in block | halfpage pointer | Column Address 地址传送顺序是Column AddressPage AddressBlock Address 由于地址只能在I/O[7:0]上传递,因此,必须采用移位的方式进行。 例如,对于512Mbit x8 NAND flash,地址范围是0~0x3FF_FFFF,只要是这个范围内的数值表示的地址都是有效的。以NAND_ADDR 为例:

1步是传递column address,就是NAND_ADDR[7:0],不需移位即可传递到I/O[7:0]上,而halfpage pointer bit8 是由操作指令决定的,即指令决定在哪个halfpage 上进行读写。而真正的bit8 的值是don't care 的;

2步就是将NAND_ADDR 右移9 位,将NAND_ADDR[16:9]传到I/O[7:0]

3 步将NAND_ADDR[24:17]放到I/O

4 步需要将NAND_ADDR[25]放到I/O

因此,整个地址传递过程需要4 步才能完成,即4-step addressing 如果NAND Flash 的容量是256Mbit 以下,那么,block adress 最高位只到bit24,因此寻址只需要3 步。

下面,就16 NAND flash 器件稍微进行一下说明。 由于一个page main area 的容量为256word,仍相当于512byte。但是,这个时候没有所谓 1st halfpage 2nd halfpage 之分了,所以,bit8就变得没有意义了,也就是这个时候 bit8 完全不用管,地址传递仍然和8 器件相同。除了,这一点之外,x16 NAND使用方法和 x8 的使用方法完全相同。

2.1 K9F1208的管脚定义

管脚

功能

I/O0- I/O7

数据输入/输出口。用于输入命令、地址和数据,并在read操作中输出数据。当芯片被取消片选货输出非能时,这些管脚为高阻状态。

CLE

命令锁存使能,输入口。当为高电平时,命令在/WE信号的上升沿由IO口锁入命令寄存器

ALE

地址锁存使能,输入口。当为高电平时,地址在/WE信号的上升沿锁入地址寄存器

/CE

片选,输入口。当设备处于 状态时,/CE的高电平将被忽略,设备在 编程 擦除 操作时不会翻好standby模式。

/RE

读使能,输入口。当其使能时,数据将被送至I/O总线上。在/RE下降沿过后tREA时间后,输出数据才是有效的。该下降沿也将给内部列地址加一。

/WE

写使能,输入口。命令、地址及数据在该信号的上升沿有效。

/WP

写保护,输入口。当/WP有效时,内部电压产生器将被复位。

RnB

就绪/忙状态,输出口。该信号表明设备操作的状态。当为低电平时,表明 编程、擦除或任意读操作在进行中,当这些操作结束时,该信号将返回高电平。该管脚为开漏输出,且芯片被取消选中或输出非能时不会进入高阻态。

Vccq

输出缓冲电压,以供给给输出缓冲。该管脚内部连接到Vcc

Vcc

设备电源供给。

Vss

不同的Flash支持不同的命令,所以一般这部分代码的可移植性都不够强。K9F1208支持的指令如下:

2.2 K9F1208支持的指令集

注意:

1.       00h 命令表示后续传入地址的起始地址在1st half,也就是从该page00地址开始;

01h命令表示后续传入地址的起始地址在2 nd half,也就是从该page256地址开始;

50h命令表示后续传入地址的起始地址在3 nd half,也就是读取该pageSpare Area区域;

通过01h命令指定访问2nd half后,下一个周期状态指针将自动移动到1st half。也就是说:01h命令的有效期只有当前周期。

2.       90h命令用于获取Nand FlashID号;

3.       FFh命令用于重启Nand Flash

4.       80h命令用于启动 编程,开始根据传入的地址写Flash10h命令提示 编程 命令的结束;

5.       60h命令用于启动 擦除,开始根据传入的Block地址擦除一个Block的内容,D0命令提示 擦除 命令的结束;

6.       70h命令用于获取前一个命令操作的结果;

7.       Page Program(True) and Copy-Back Program(True) are available on 1 plane operation.

Page Program(Dummy) and Copy-Back Program(Dummy) are available on the 2nd,3rd,4th plane of multi plane operation.

8.       The 71h command should be used for read status of Multi Plane operation.

9.       Multi plane operation and Copy-Back Program are not supported with 1.8V device.

Caution : Any undefined command inputs are prohibited except for above command set of Table 1.

下表列出了K9F1208支持的所有最底层操作的电平时序。读者可在后面的读写源码中对比这些时序。

2.3 K9F1208支持的所有最底层操作的电平时序

以发送ReadID命令为例,其时序图如下。

1)拉低/CE以选通芯片;而后拉高CLE,拉低/WE,同时输入ReadID命令(0x90),之后在/WE的上升沿将ReadID写入命令寄存器;

2)拉高ALE,拉低/WE,同时送入地址0x00, 之后在/WE的上升沿该地址将被锁存;

3)拉低/RE,延时tREA后开始通过IO口读取数据;

4)根据读取的ID的大小重复步骤3,直至读取结束。

2.1 ReadID命令的时序图

从上文举的ReadID的例子可知,对Nand Flash的读写其实就是通过对S3C2440的数据总线、数据总线以及控制总线的控制来实现的,以下通过实际源码来讨论读写过程。

K9F1208的寻址分为4cycle。分别是:A[0:7]A[9:16]A[17:24]A[25]

读操作的过程为:1、发送读取指令;2、发送第1cycle地址;3、发送第2cycle地址;4、发送第3cycle地址;5、发送第4cycle地址;6、读取数据至页末。

K9F1208提供了两个读指令,‘0x00’‘0x01’。这两个指令区别在于‘0x00’可以将A[8]置为0,选中上半页;而‘0x01’可以将A[8]置为1,选中下半页。

虽然读写过程可以不从页边界开始,但在正式场合下还是建议从页边界开始读写至页结束。下面通过分析读取页的代码,阐述读过程。

static int NF_ReadPage(U32 block, U32 page, U8 *buffer, U8 *spareBuf)

{

       Int          i;

       unsigned int    blockPage;

       U8          *bufPt=buffer;

 

       page = page & 0x1f;

       blockPage = (block<<5) + page;

 

       NF_nFCE_L();       //使能NandFlash

       NF_CMD(CMD_READ_1_1);      //发送读指令‘0x00’,由于是整页读取,所以选用指令‘0x00’

       NF_ADDR(0);       //写地址的第1cycle,即Column Address,由于是整页读取所以取0

       NF_ADDR(blockPage & 0xff);     //写地址的第2cycle,即A[9:16]

       NF_ADDR((blockPage>>8) & 0xff);   //写地址的第3cycle,即A[17:24]

       NF_ADDR((blockPage>>16) & 0xff);  //写地址的第4cycle,即A[25]

       Delay(1); //wait tWB(100ns)

       NF_WAITRB();      //等待系统不忙(max 12us)

 

       for(i=0; i<(512); i++)     //循环读出1页数据

              (*(bufPt++)) = NF_RDDATA();    // Read one page

 

       if(spareBuf != NULL) { // 读取Spare Area区域

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

                     spareBuf[i] = NF_RDDATA();      // Read spare array

       }

 

       NF_nFCE_H();      //释放NandFlash

 

       return 1;

}

写操作的过程为: 1、发送写开始指令;2、发送第1cycle地址;3、发送第2cycle地址;4、发送第3cycle地址;5、发送第4cycle地址;6、写入数据至页末;7、发送写结束指令

下面通过分析写入页的代码,阐述读写过程。

static int NF_WritePage(U32 block, U32 page, U8 *buffer, U8 *spareBuf)

{

       int    i;

       U32 blockPage=(block<<5)+page;

       U8   *bufPt=buffer;

 

       NF_nFCE_L();       //使能NandFlash

       NF_CMD(CMD_READ_1_1);

       NF_CMD(CMD_PAGE_PROGRAM_START);   //发送写开始指令’0x80’

       NF_ADDR(0);      //写地址的第1cycle

       NF_ADDR(blockPage & 0xff);    //写地址的第2cycle

       NF_ADDR((blockPage>>8) & 0xff);   //写地址的第3cycle

       NF_ADDR((blockPage>>16) & 0xff);        //写地址的第4cycle

 

       for(i=0;i<512;i++)  //循环写入1页数据

              NF_WRDATA(*bufPt++);

 

       if(spareBuf!=NULL) {   // 写入Spare Area区域

              for(i=0; i<16; i++) {

                     NF_WRDATA(spareBuf[i]);   // Write spare array(ECC and Mark)

              }

       }

 

       NF_CMD(CMD_PAGE_PROGRAM_END);       //发送写结束指令’0x10’

       Delay(1);     //tWB = 100ns.

       NF_WAITRB();    //wait tPROG 200~500us;

       NF_CMD(CMD_READ_STATUS);      // 发送命令获取操作结果

       Delay(1);     //twhr=60ns

 

       if (NF_RDDATA() & 0x1) {// 写入出错

              NF_nFCE_H();      //释放NandFlash

              printf("[PROGRAM_ERROR:block#=%d]\n", block);

              NF_MarkBadBlock(block);

              return 0;

       }

       else {

              NF_nFCE_H();      //释放NandFlash

#if (1 == WRITEVERIFY)

              //return NF_VerifyPage(block,page, pPage);

#else

              return 1;

#endif

       }

}

本节以S3C2440处理器和K9F1208为例讨论了nand flash的读写过程,在读写过程中没有考虑到坏块、ECC和坏块处理的问题。

到这里,我们已经了解了如何通过操作Flash的相关管脚去读写Flash。有读者可能要问了:好像不能用PC机去直接控制开发板的管脚吧?其实是可以的,这涉及到JTAG协议。这就是我们下一章要讨论的问题。

本章源码中用到了很多子函数,如 NF_nFCE_H(),正是通过JTAG操作2440的管脚口的结果,具体过程在下一章中讨论。

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