6713EMIF操作FLASH
Ø 数据总线宽度:32位,有32条数据线D[31:0]
Ø 存储空间:4个,#CE0-#CE3,对应地址为0x80000000、0x90000000、0xA0000000、0xB0000000
Ø 每个存储空间的寻址范围:256M
Ø 时钟:外部ECLKIN引脚输入或内部SYSCLK3提供,最高时钟频率为100MHZ
Ø 可访问的数据宽度:8/16/32位
Ø 支持的存储器类型:SDRAM/SBSRAM/异步存储器(SRAM、Flash等)
Ø 各类存储器控制信号:复用引脚,自动切换
图2.1 EMIF接口信号
ECLKIN: EMIF外部时钟输入;
ECLKOUT: EMIF工作时钟(有2 个来源:ECLKIN 和SYSCLK3 由EKSRC(DEVCFG.[4]) 选择 EKSRC = 0 时,选中SYSCLK3(默认) EKSRC = 1 时,选中ECLKIN)
ED[31:0]: 32-位数据总线;
EA[21:2]: 20-位地址总线;
: 4-个存储空间选通信号,低电平有效;
: 4-个字节使能信号,低电平有效;
: 异步存储器读出使能信号/SDRAM 行选通信号/SBSRAM读 出使能信号,低电平有效;
:异步存储器读使能信号/SDRAM 列选通信号/SBSRAM地址 选通信号,低电平有效;
: 异步存储器写使能信号/SDRAM 写使能信号/SBSRAM写使 能信号,低电平有效;
ARDY: 异步存储器数据就绪信号,高电平有效;
: EMIF总线保持请求信号,低电平有效;
: EMIF总线已保持确认信号,低电平有效;
BUSREQ: EMIF总线请求标志信号,高电平有效;
C6713 能直接与8/16/32位存储器无缝接口,内部以字节进行编址(逻辑地址),外部存储器地址(物理地址),由EMIF根据所接口的存储器的宽度,自动对逻辑地址进行移位产生,逻辑地址与物理地址之间的关系如下表所示:
图3.1 逻辑地址和物理地址的关系
对小于32位的外部存储器进行访问时,EMIF 将自动完成数据打包和拆包。例如,对8位存储器进行读操作时,EMIF自动读字节地址 N、N+1、N+2、N+3中的4个8位数据打包成32-位的数据;而对 16-位存储器进行写操作时,EMIF自动将32位数据拆包成2个16位数据分别写入字地址N、N+1中。8位与16位数据与EMIF的32位数据总线之间的对应关系由Endian模式决定,在LittleEndian模式时,对齐EMIF的最低有效位,在 Big Endian模式时,对齐EMIF的最高有效位。如下图所示:
图3.2 位宽对其方式
一般地,我们会用CPLD或者FPGA对这些外设进行译码,分别给各个外设的片选分配一个唯一的地址。这个地址是物理地址,是用CEx和EA[21:2]硬线做译码得到的。而DSP要操作这个地址,必须使用逻辑地址(字节地址),这就要求有一个物理地址和逻辑地址的对应关系。在建立这种关系的时候,我们要明确地指出某个CEx的数据宽度,是8位还是16位。我们所选用的FLASH为S29AL016J(如果BYTE#引脚被设置为1,那么为16位宽度,如果BYTE#被设置为0,那么为8位宽度),并且其与EMIF连接的存储空间为CE1,所以其基地址设置为FLASH_BASE1 = (unsignedint *)0x90000000;由于选择的是16位数据宽度,所以对Flash 而言其物理地址以16位为单位进行编址,而程序中使用的逻辑地址是以字节为单位进行编址的,二者之间的关系如下:
逻辑地址 = 物理地址 << 1
volatile Uint16 *FLASH_555 = (volatile Uint16 *) (0x90000000 +(0x555<<1));
volatile Uint16 *FLASH_2AA = (volatile Uint16 *) (0x90000000 +(0x2AA<<1));
对于Flash的操作主要是flash擦除,flash读和flash写。DSP通过EMIF的CE1存储空间外扩flash,根据Flash的数据手册可以得到Flash的相应操作的命令定义表:
表4.1 Flash 命令定义表
图4.1 擦除规范
函数原型:
Uint32Flash_Erase(Uint32 addr, Uint16 type)
参数addr表示擦除Flash的基地址,参数type表示擦除类型,有擦除扇区和擦除整个芯片两种,0x30表示擦除扇区,0x10表示擦除整个chip。
整片擦除:由于是16位宽度,所以擦除命令为word这一行,也就是向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 +(0x555<<1)))写入AA->((volatile Uint16 *) (0x90000000 +(0x2AA<<1)))写入55->向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入80->向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入AA->向flash的地址FLASH_2AA((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入55->向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入10。擦除过程中Data#会将DQ7的值拉低为0,擦除完成Data#会将DQ7拉高为1(while((*(Uint16 *)addr & 0x80) != 0x80);)。
扇区擦除:由于是16位宽度,所以擦除命令为word这一行,也就是向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 +(0x555<<1)))写入AA->((volatile Uint16 *) (0x90000000 +(0x2AA<<1)))写入55->向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入80->向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入AA->向flash的地址FLASH_2AA((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入55->向给定地址addr中写入30。擦除过程中Data#会将DQ7的值拉低为0,擦除完成Data#会将DQ7拉高为1。( while((*(Uint16 *)addr& 0x80) != 0x80);)
图4.2 flash擦除函数流程
函数原型:
Uint32Flash_Reads(Uint32 addr)
返回读取到的当个地址中的数据,参数addr表示读取当个数据的地址。
直接返回地址中的数据(return (*(Uint32 *)addr);)。
函数原型:
voidFlah_Readm(Uint32 addr,Uint16 *ptr,Uint32 length)
参数addr表示读取flash数据的首地址,ptr表示目标buff的地址,length表示读取长度。
流程:根据length的大小以for循环的形式从首地址中逐次读取flash中数据到目标地址中,由于目标地址中数据类型是Uint16,而源地址中数据是Uint32,所以在读取时,数据长度应该×2。
图4.3 flash读取流程
图4.4 flash写入规范
函数原型:
voidFlash_Writes(Uint32 addr,Uint16 data)
参数addr表示写入flash的写入地址,data表示写入的单个数据。
由于是16位宽度,所以擦除命令为word这一行,也就是向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 +(0x555<<1)))写入AA->((volatile Uint16 *) (0x90000000 +(0x2AA<<1)))写入55->向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入a0。接着向给定地址addr中写入data。
图4.5 flash写入规范
函数原型:
voidFlash_Writem(Uint32 src, Uint32 dst,unsigned short length)
参数src表示源地址,dst表示写入的目标地址,length表示写入的长度。
定义相关变量,判断输入长度是否正确,不正确退出,长度大于0则进行写入操作,先将源地址和目标地址数据转换成Uint16类型,然后根据长度逐个将数据写入到flash对应的地址中:FLASH_555((volatile Uint16 *) (0x90000000 +(0x555<<1)))写入AA->((volatile Uint16 *) (0x90000000 +(0x2AA<<1)))写入55->向flash的地址FLASH_555((volatile Uint16 *) (0x90000000 + (0x555<<1)))写入a0。然后以while循环将数据写入到flash。
图4.6 flash写入流程
Flash通过相应链接线路上的电平时序变化产生相应的操作命令,入下表所示:
图5.1 flash总线操作
BYTE#引脚决定flash选择8位宽度数据配置还是16位宽度。如果设置为逻辑1,那么falsh配置为16位宽度,DQ15-DQ0为数据IO口,由CE#和OE#控制。如果设置为逻辑0,那么flash配置为8位宽度,DQ0-DQ7为数据IO口,由CE#和OE#控制,并且DQ15位设置为数据地址的最低有效位。
读数据的时候系统必须设置CE#和OE#为逻辑低电平,CE#作为片选引脚,OE#是读控制使能引脚。WE#得设置为逻辑高电平。标准的微处理器读数过程是给flash的地址线输入有效地址,falsh通过数据线输出有效数据,在命令寄存器设置没有改变时一直保持读数状态。
写命令或者命令序列(对flssh进行写入操作或者擦除操作)的时候必须设置WE#和CE#为逻辑低电平,OE#为逻辑高电平。开启旁路模式的时候能更快的将数据写入到flash,因为它只要两个写入周期就能写一个数据,正常的需要4个写入周期。擦除操作分一个扇区擦除,多个扇区擦除以及整个片擦除。在擦除或者写入操作的过程中,系统会检查DQ7-DQ0上的状态位确定当期操作状态(擦除完或者写完)。
当系统没有读或者写flash的时候会让flash进入备用模式,在这种模式下功耗非常低,所有的输出都将被设置为高阻态。进入这种模式时CE#和RESET#全部保持在VCC+-0.3v。
当OE#设置为逻辑低电平,所有的输出被禁止,输出口全部被设置为高阻态。
Flash读数过程:
图5.2 读数时序图
Flash芯片读数过程主要由芯片上的片选信号CE#和读使能信号OE#控制,写使能信号WE#为逻辑低电平,当CE#和OE#中后到来的下降沿(此时CE#和OE#都为低电平),开始读取对应地址中的数据,读数的时候会有时序图的一些延时,如下表:
图5.3 读数延时表
Flash写数时序:
图5.4 写数时序图
Flash芯片读数过程主要由芯片上的片选信号CE#和写使能信号WE#控制,地址信息用CE#和WE#中后到来的下降沿(此时CE#和WE#都为低)来获得,数据信息用CE#和WE#中先到来的上升沿(此时CE#和WE#只有一个为高)来获得,读数的时候会有时序图的一些延时,如下表:
图5.5 写数延时表
Flash擦除时序:
图5.6 flash擦除时序
Flash芯片读数过程主要由芯片上的片选信号CE#和写使能信号WE#控制,地址信息用CE#和WE#中后到来的下降沿(此时CE#和WE#都为低)来获得,数据信息用CE#和WE#中先到来的上升沿(此时CE#和WE#只有一个为高)来获得,经过5写入个周期后,由DQ7是否为逻辑高电平来确定擦除有没有完成(completed)。
Flash.h
#define FLASH_UL1 0xAA
#define FLASH_UL2 0x55
#define FLASH_UL3 0x80
#define FLASH_UL4 0xAA
#define FLASH_UL5 0x55
#define FLASH_SECTOR_UL6 0x30
#define FLASH_CHIP_UL6 0x10
#define FLASH_PROGRAM 0xA0
// #define SECTOR_SIZE 0x40000 //256K*16bit
#define SECTOR_SIZE 0x8000 //32K*16bit
#define CHIP_SIZE 0x100000 //1M*16bit
volatile Uint16 *FLASH_555 = (volatileUint16 *) (0x90000000 + (0x555<<1));
volatile Uint16 *FLASH_2AA = (volatileUint16 *) (0x90000000 + (0x2AA<<1));
/********************************************************************************/
Uint32 Flash_Erase(Uint32 addr,Uint16type);
void Flash_Readm(Uint32 addr,Uint16*ptr,Uint32 length);
Uint32 Flash_Reads(Uint32 addr);
void Flash_Writem(Uint32 src,Uint32 dst,unsignedshort length);
void Flash_Writes(Uint32 addr,Uint16 data);
Flash.c
#include
#include
#include
#include
#include
#include
#include
#include
/* Flashfunction difine. */
/********************************************************************************/
/* Flash erase function. */
/********************************************************************************/
Uint32 Flash_Erase(Uint32 addr,Uint16 type)
{
Uint32i,j;
*FLASH_555= FLASH_UL1; //first
*FLASH_2AA= FLASH_UL2; //second
*FLASH_555= FLASH_UL3; //third
*FLASH_555= FLASH_UL4;
*FLASH_2AA= FLASH_UL5;
/* *FLASH_555= type;
while(((*FLASH_555)& 0x80) != 0x80);
for(i= 0; i < CHIP_SIZE; i++)
{
if(*(Uint16 *)(addr + i) != 0xffff)
{
break;
}
}
}*/
switch(type)
{
/* case 0x50: //block erase
*(Uint16*)addr = type;
while((*(Uint16*)addr & 0x80) != 0x80);
for(i= 0; i < BLOCK_SIZE; i++)
{
if(*(Uint16 *)(addr + i) != 0xffff)
{
j= 0;
break;
}
}
j= 1;
break;
*/
case 0x30: //sector erase
*(Uint16*)addr = type;
while((*(Uint16*)addr & 0x80) != 0x80);
for(i= 0; i < SECTOR_SIZE; i++)
{
if(*(Uint16 *)(addr + i) != 0xffff)
{
j= 0;
break;
}
}
j= 1;
break;
case 0x10: //chip erase
*FLASH_555= type;
DSP_wait(1000);
while((*FLASH_555 & 0x80) != 0x80);
for(i= 0; i < CHIP_SIZE; i++)
{
if(*(Uint16 *)(addr + i) != 0xffff)
{
j= 0;
break;
}
}
j= 1;
break;
default:
break;
}
return(j);
}
/********************************************************************************/
/* Write a single data. */
/********************************************************************************/
void Flash_Writes(Uint32 addr,Uint16 data)
{
//Uint16TempData=0;
*FLASH_555= FLASH_UL1;
*FLASH_2AA= FLASH_UL2;
*FLASH_555= FLASH_PROGRAM;
//for(;;)
//{
*(Uint16*)addr = data;
//TempData= *(Uint16 *)(addr);
//}
//TempData= *(Uint16 *)(addr);
while(*(Uint16*)addr != data);
}
/********************************************************************************\
\* Write the certain length data. *\
\********************************************************************************/
void Flash_Writem(Uint32 src,Uint32dst,unsigned short length)
{
Uint16 *psrc, *pdst;
unsigned short i;
if (length<=0)return;
/* Establish source and destination */
psrc = (Uint16 *)src;
pdst = (Uint16 *)dst;
for (i = 0; i < length; i++)
{
// Program one 16-bit word
*FLASH_555 = FLASH_UL1;
*FLASH_2AA = FLASH_UL2;
*FLASH_555 = FLASH_PROGRAM;
*pdst = *psrc;
// Wait for operation to complete
while(1)
{
if(*pdst == *psrc)
{
break;
}
}
pdst++;
psrc++;
}
}
/********************************************************************************\
\* Read a single data. *\
\********************************************************************************/
Uint32 Flash_Reads(Uint32 addr)
{
return(*(Uint32 *)addr);
}
/********************************************************************************\
\* Read the certain length data. *\
\********************************************************************************/
void Flash_Readm(Uint32 addr,Uint16*ptr,Uint32 length)
{
Uint32i;
for(i= 0; i < length; i++)
{
*(ptr + i) = Flash_Reads(addr+2*i);
}
}