DSP

6713_EMIF操作外部flash

2019-07-13 12:10发布

6713EMIF操作FLASH

1. 6713EMIF的特点

Ø       数据总线宽度:32位,有32条数据线D[31:0] Ø       存储空间:4个,#CE0-#CE3,对应地址为0x80000000、0x90000000、0xA0000000、0xB0000000 Ø       每个存储空间的寻址范围:256M Ø       时钟:外部ECLKIN引脚输入或内部SYSCLK3提供,最高时钟频率为100MHZ Ø       可访问的数据宽度:8/16/32位 Ø       支持的存储器类型:SDRAM/SBSRAM/异步存储器(SRAM、Flash等) Ø       各类存储器控制信号:复用引脚,自动切换

2. 6713EMIF接口信号

图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总线请求标志信号,高电平有效;

3. 存储器宽度和字节对齐

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 位宽对其方式

4.6713EMIF外扩FLASH分析

4.1 地址定义

一般地,我们会用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));

4.2 操作Flash

对于Flash的操作主要是flash擦除,flash读和flash写。DSP通过EMIF的CE1存储空间外扩flash,根据Flash的数据手册可以得到Flash的相应操作的命令定义表: 表4.1 Flash 命令定义表

4.2.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擦除函数流程

4.2.2 flash单个读取函数

函数原型: Uint32Flash_Reads(Uint32 addr) 返回读取到的当个地址中的数据,参数addr表示读取当个数据的地址。 直接返回地址中的数据(return (*(Uint32 *)addr);)。

4.2.3 读取flash函数

函数原型: voidFlah_Readm(Uint32 addr,Uint16 *ptr,Uint32 length) 参数addr表示读取flash数据的首地址,ptr表示目标buff的地址,length表示读取长度。 流程:根据length的大小以for循环的形式从首地址中逐次读取flash中数据到目标地址中,由于目标地址中数据类型是Uint16,而源地址中数据是Uint32,所以在读取时,数据长度应该×2。 图4.3 flash读取流程

4.2.4 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.2.5 flash写入函数

图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写入流程

5 FLASH寄存器设置和时序控制

5.1 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#设置为逻辑低电平,所有的输出被禁止,输出口全部被设置为高阻态。  

5.2 FLASH读写时序

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)。

6 附录,程序

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);        } }