最近在搞Uboot时才发现自己的裸机实验中没有相关NorFlash的代码,对NorFlash一无所知,查了一些资料,将自己的一点心得总结一下。
开发板:mini2440
NorFlash:SST39VF1601
NorFlash简单来说与sdram与Nand的中间品,它能像sdram一样直接读,但是又得像nand一样编程擦写。因此程序可以直接在nor里跑,速度要比sdram慢一些,往nor里写数据必须先擦除,因为nor的每一位只能由1变为0。Nor可读不可直接写的特性可以被用来判断是Nor启动还是nand启动,因为nand启动的话前4K是可写的,我们写入数据再读取出来应该是没有问题的,而nor启动的话,读出的数据必然是错误的。
NorFlash的硬件接线:
首先,如果做过sdram实验的朋友应该知道,NorFlash与sdram很相似,只不过sdram位宽为32,NOR为16。在硬件连接上,Nor的地址线与cpu的地址线错开1位,sdram错开2位。简单分析一下:
32位的CPU地址线为32位,每一个地址对应1个byte,地址的步长为1byte
NorFlash的软件设置:
需要像初始化sdram一样,设置Bank寄存器,主要是一些时序图的时间大小。
位宽在BWSCON寄存器中设置,不过它是由硬件决定的,bank0的位宽,我们拨动开关选择nor启动还是nand启动的时候,它就已经确定了。
NorFlash信息:
我这款Nor大小为2M,32个Block,每个block分为16个sector,一个sector为4K,具体信息如下图。
NorFlash写、擦除、读芯片ID:
Nor可以像sdram一样直接读取,对于其它的操作,例如写、擦除、读信息等需要写特定的Date到特定的Addr。
这里尤其需要注意的是,Addr指的是nor地址线上看到的地址,相对于cpu也就是我们写程序的时候,需要将addr<<1,这样cpu错位的地址线发出的地址正好对上nor需求的地址。
对于擦除操作,nor支持按block、sector或者整片的擦除,整个擦除需要6个周期,以按sector擦除为例,前五个周期为往XXX里写XXX,最后一个周期将0X30写入要擦除的sector对应的首地址即可。
对于写操作,需要4个周期,前3个周期往XXX里写XXX,最后一个周期将Data写入addr,这里需要注意的是addr必须是半字对齐,data也要求为16bit。
对于读取信息的操作,主要是读ID的操作,前3个周期往XXX里写XXX,第四个周期去读特定的地址,对于DeviceID来说,A1-A19=0,A0=1,对于CPU来说就是0x1<<1.
NorFlash的等待:
NorFlash有三种方式,1中读硬件引脚,另外两种读地址线,主要用读地址线的两种方式。
1、Toggle
连续读两次相同地址的数据,看DQ6是否相等,相等则擦除或写完成
2、polling
读数据,看DQ7是否正确,正确则擦除完成。如果擦除的话DQ7应该等于1,写操作的话,对比数据的第7位。
还有,很重要的一点:
经过我的测试,sdram的单字节读取没有问题,但是Nor单字节读取的时候,读出的数据并不正确。比如我在0x00地址处写入了0xff11,单字节读取0x0理论上应该得到0x11,而我在实际实验的时候得到的却是0xff。也就是说nor在存储半字数据的时候高8位于低8位互换了。
#include "uart.h" #define MAIN_SECT_SIZE (4*1024) /* 4 KB */ #define CMD_UNLOCK1 0x000000AA #define CMD_UNLOCK2 0x00000055 #define CMD_ERASE_SETUP 0x00000080 #define CMD_ERASE_CONFIRM 0x00000030 #define CMD_PROGRAM 0x000000A0 #define MEM_FLASH_ADDR1 (*(volatile U16 *)(0x000005555 << 1)) #define MEM_FLASH_ADDR2 (*(volatile U16 *)(0x000002AAA << 1)) #define U16 unsigned short #define U32 unsigned long #define U8 unsigned char int write_hword (U32 dest, U16 data); typedef struct { U32 size; /* total bank size in bytes */ U16 sector_count; /* number of erase units */ U32 flash_id; /* combined device & manufacturer code */ U32 start[512]; /* physical sector start addresses */ } flash_info_t; /*-----------------------------------------------------------------------*/ void flash_id(){ MEM_FLASH_ADDR1 = CMD_UNLOCK1; MEM_FLASH_ADDR2 = CMD_UNLOCK2; MEM_FLASH_ADDR1 = 0x00000090; print("Manufacturer ID :%x\r\n",*((U16 *)0x00)); MEM_FLASH_ADDR1 = CMD_UNLOCK1; MEM_FLASH_ADDR2 = CMD_UNLOCK2; MEM_FLASH_ADDR1 = 0x00000090; print("Device ID :%x\r\n",*((U16 *)(0x01<<1))); } void flash_init () //计算总大小,每个sector的起始地址 { flash_id(); //print("flash init OK\r\n"); print("*((U32 *)0x32000000) = 0x00001100\r\n"); *((U32 *)0x32000000) = 0x00001100; print("U8 0x32000001 == %x\r\n",*((U8 *)0x32000001)); *((U8 *)0x32000003) = 0xff; print("*((U8 *)0x32000003) = 0xff\r\n"); print("now the U32 0x32000000 should be 0xff001100\r\n"); print("U32 0x32000000 = %x\r\n",*((U32 *)0x32000000)); print("U8 0x32000000 = %x\r\n",*((U8 *)0x32000000)); print("U8 0x32000001 = %x\r\n",*((U8 *)0x32000001)); print("U8 0x32000002 = %x\r\n",*((U8 *)0x32000002)); print("U8 0x32000003 = %x\r\n",*((U8 *)0x32000003)); if(flash_erase(0,1) == 0) print("erase OK\r\n"); U32 a = 0x00001c15; U16 b = 0x11ff; write_hword(a,b); print("*(U32 *0x00001c15) == 0x11ff\r\n"); print("U8 0x1c15 = %x\r\n",*((U8 *)0x1c15)); print("U8 0x1c16 = %x\r\n",*((U8 *)0x1c16)); print("U16 0x1c15 = %x\r\n",*((U16 *)0x1c15)); if(write_buff((1024*20),0,(7*1024)) == 0) print("write OK\r\n"); } int flash_erase (int s_first, int s_last) { int i,j; U32 size = 0; flash_info_t flash_info; for (i = 0; i < 1; i++) { U32 flashbase = 0; flash_info.size = 2*1024*1024; flash_info.sector_count = 512; flashbase = 0; for (j = 0; j < flash_info.sector_count; j++) { flash_info.start[j] = flashbase + (j) * MAIN_SECT_SIZE; //print("%d \t:%d\r\n",j,flash_info.start[j]); } } int sect; unsigned short temp; /* Start erase on unprotected sectors */ for (sect = s_first; sect <= s_last; sect++) { print ("Erasing sector %d ... \r\n", sect+1); volatile U16 * addr = (volatile U16 *)(flash_info.start[sect]); MEM_FLASH_ADDR1 = CMD_UNLOCK1; MEM_FLASH_ADDR2 = CMD_UNLOCK2; MEM_FLASH_ADDR1 = CMD_ERASE_SETUP; MEM_FLASH_ADDR1 = CMD_UNLOCK1; MEM_FLASH_ADDR2 = CMD_UNLOCK2; *addr = CMD_ERASE_CONFIRM; print("wait\r\n"); while(1){ temp = (*addr) & 0x40; // 0100 0000 DQ6 if(temp != (*addr) & 0x40) //DQ6 continue; if(*addr & 0x80) //dq7 ==1 break; } } return 0; } //写16位数据,地址应该是16位对齐的。 int write_hword (U32 dest, U16 data) { //print("now to write %d\r\n",dest); int rc; volatile U16 *addr = (volatile U16 *)dest; U16 result; //Check if Flash is (sufficiently) erased result = *addr; if ((result & data) != data) return 1; MEM_FLASH_ADDR1 = CMD_UNLOCK1; MEM_FLASH_ADDR2 = CMD_UNLOCK2; MEM_FLASH_ADDR1 = CMD_PROGRAM; *addr = data; while(1){ unsigned short i = *addr & 0x40; if(i != (*addr & 0x40)) //D6 == D6 continue; if((*addr & 0x80) == (data & 0x80)){ rc = 0; break; //D7 == D7 } } //print("write %d OK\r\n",dest); return rc; } int write_buff (U32 src, U32 addr, U32 len) { int rc; U16 data; //16Bit? if(addr % 2){ print("addr is not for 16bit align\n"); return 1; } while (len >= 2) { data = *((volatile U16 *) src); if ((rc = write_hword (addr, data)) != 0) { return (rc); } src += 2; addr += 2; len -= 2; } return 0; }