全部博文(2759)
分类: 嵌入式
2013-08-02 00:27:47
原文地址:vxworks驱动开发原理——TFFS文件系统(3) 作者:哭泣的土地
根据am29lv160d的datasheet的描述,am29lv160D共有16Mbit的容量,可以配置为2M×8bit或者1M×16bit,具体为byte配置还是双字配置要根据其引脚byte的设置,byte=1说明是双字配置1M×16bit,为0则说明是字节配置2M×8bit。我们需要注意的是am29lv160d的地址线只有20根,即A19~A0,2M的寻址空间则需要21条地址线,因此这就需要一条额外的地址线。amd的设计很巧妙的实现这个问题,在双字模式下,输出为D15~D0,寻址空间为1M,A19~A0正好满足要求;如果是字节模式下则D15~D7位则是无效的,amd的设计就是将其中的D15作为一条附加的地址线A-1。当A-1为0时,表明地址是一个字的低字节,让A-1为1时表明地址是一个字的高字节。也就是说在字节模式下其地址线为A19~A-1共21条满足要求,分别连接外部地址线的A20~A0。
am29lv160d共有35个sector。其中包含31个大小32kword/64kbyte的普通sector,一个大小为16kword/32kbyte的sector,两个4kword/8kbyte的sector,一个8kword/16kbyte的sector。每个sector都可以单独erase。之所以把一个普通sector分成4个大小不一的sector是为了便于存放boot block。
am29lv160d还可以细分为am29lv160dt和am29lv160db两种,am29lv160dt表明其boot block是在内存的高端地址空间;am29lv160db表明其boot block存放在低端地址空间。
我们以高端地址空间为例分析一下各个sector的地址空间地址:
对普通sector(sectorNum=0~30)来说,其大小为32kword/64kbyte,地址空间为sectorNum*64KB~(sectorNum+1)*64KB-1;
第31secotor,其大小16kword/32kbyte,地址空间为F8000~FBFFF(word)/1F0000~1F7FFF(byte);
第32sector,其大小4kword/8kbyte,地址空间为FC000~FCFFF(word)/1F8000~1F9FFF(byte);
第33sector,其大小4kword/8kbyte,地址空间为FD000~FDFFF(word)/1FA000~1FBFFF(byte);
第34sector,其大小8kword/16kbyte,地址空间为FE000~FFFFF(word)/1FC000~1FFFFF(byte);
am29lv160d在使用过程中可以利用多个芯片并联的方式来实现。文件amd92lvMtd.c中针对的flash卡则是是由4个am29lv160组成的,如图7.4。外部地址线A22~A3连接各芯片的A19~A0,外部地址线A2连接各芯片的片选信号,选中单个芯片,这样就实现了64位内存存储空间。
图7.4 一种64位flash卡的连接方法
但是由于CPU是32bit的,因此在访问内存的时候就分为高4字节和低4字节分别访问,我们在后面的读写空间中也可以看到在访问函数的参数中都有一个upper参数,该函数是一个bool变量,1表示访问高4字节,也就是2#3#芯片,0表示访问低4字节,也就是0#1#芯片。
这样一来,其存储容量扩充为8MB,共有35个scoter。每个sector在vxworks中双字模式下的地址分别为(以4个am29lv160dt为例):
对普通sector(sectorNum=0~30)来说,其大小为sectorSize=0x40000(256KB),地址空间为sectorNum*sectorSize~(sectorNum+1)*sectorSize-1;
第31secotor,其大小0x20000(128KB),地址空间为7C0000~7DFFFF;
第32sector,其大小0x8000(32KB),地址空间为7E0000~7E7FFF;
第33sector,其大小0x8000(32KB),地址空间为7E8000~7EFFFF;
第34sector,其大小0x10000(64KB),地址空间为7F0000~7FFFFF;
分析函数库amd29LvMtd之前,我们需要明白三个地址的概念:计算机系统地址、存储设备地址(存储卡)和flash芯片地址。
l 一个计算机有多个存储设备,如内存、CF存储卡等等,对于一个flash卡来说,它的地址和计算机系统地址是不相同的,而是存在一个偏移的关系,这个偏移与socket的设置有关系;
l 一个存储设备如U盘,通常包括多个flash芯片,各个flash芯片都有自己的编址,显然这个flash芯片地址和flash卡的的地址也不相同,如图7.4描述的结构图;
l 计算机系统对flash卡的读写最终要作用在相应在相应的flash芯片上,因此在进行存储设备的读写时需要完成三个转换:计算机系统地址→存储设备→flash芯片地址。
函数库amd29LvMtd的作用,就是对Flflash.h文件中的FLFlash结构变量进行填充,包括MTD层的一些属性以及一些MTD层的操作函数。对FLFlash结构的填充过程是这样的,系统首先指定了一些MTD层的驱动库,包括amd29LvMtd,存放在一个数组mtdTable[]中,在系统需要时会利用这个数组中的各个识别函数分别进行识别,对amd29LvMtd库来说就是通过其识别函数amd29lvMTDIdentify判断flash卡是否和amd29LvMtd库相一致,如果一致则用amd29LvMtd库中的函数和变量来填充FLFlash结构,以供系统在需要的时候直接调用amd29LvMtd库。下面将对amd29LvMtd库中的函数进行逐个分析。
1. FLStatus amd29lvMTDIdentify
(
FLFlash* pVol
)
这个函数的作用是读取卡内记录的制造商ID和设备ID识别flash卡的型号,并进行FLflash结构变量vol的初始化。这个结构提供了一个标准的mtd接口,它包含了基本的三个函数read、write和map。
函数amd29lvMTDIdentify首先调用函数flashIdGet(pVol, &manCode, &devCode)获取设备信息,如果与驱动相匹配,则将驱动信息及接口填入到FLFlash结构变量中:
pVol->type = 0x01C4;
pVol->erasableBlockSize = AMD29LV_MTD_SECTOR_SIZE;
pVol->chipSize = AMD29LV_160_CHIP_SIZE;
pVol->noOfChips = AMD29LV_MTD_CHIP_CNT;
pVol->interleaving = AMD29LV_MTD_INTERLEAVE;
pVol->write = amd29lvProgram;
pVol->erase = amd29lvSectorRangeErase;
pVol->map = amd29lvMap;
2. LOCAL void FAR0* amd29lvMap
(
FLFlash* pVol,
CardAddress address,
int length
)
当访问flash卡上的一个地址时,首先需要将flash卡的地址A转化为计算机系统的地址B,这样对计算机系统来说,只要访问地址B,也就实现了flash卡内地址A的访问。函数amd29lvMap的作用是对根据输入的flash卡的地址计算其在计算机系统中的地址。该地址=flash卡的地址+ (FLFlash.ocket->window.baseAddress << 12)。
3. LOCAL void flashRegWrite32Bits
(
FLFlash* pVol,
UINT32 addr,
UINT32 data,
BOOL upper
)
参数说明:
FLFlash* pVol, FLFlash结构变量
UINT32 addr, flash卡内的地址,从0开始
UINT32 data, 要写入的数据
BOOL upper 表明是高4字节或低4字节。
该函数的并不是直接向flash卡中的地址写入一个数据,真正写入一个数据需要比较复杂的时序,它要求在写特定的flash卡地址前首先向flash的寄存器中写入一个特定的命令,而该函数的作用就是要向flash卡的寄存器中写入一个特定的命令。
对flash卡内地址的写入最终要体现在对flash芯片的写入,因此写入命令最终体现在向相应的flash芯片的写入。由前面的电路结构图图7.4可以看出,对于单个flash芯片来说,如果要在flash芯片上0x555的地址处写入一个数据,那么该芯片对应的地址线A19~A0的数据应为0x555,由于其flash芯片地址线A19~A0连接的是计算机系统A22~A3地址线,这就要求计算机系统A22~A3地址线上的地址为0x555,因此对应的A22~A0则为0x555*8。
另一方面,由于flash卡为64位,因此要求将高32位和低32位的访问分开。
4. LOCAL UINT16 flashRegRead16Bits
(
FLFlash* pVol,
UINT32 addr,
BOOL upper
)
读取16bit数据。类似于flashRegWrite32Bits。
LFlash* pVol, FLFlash结构
UINT32 addr, 地址
BOOL upper 高4字节或者低4字节。
返回值,读出的数据。
注意函数flashRegRead16Bits和函数flashRegWrite32Bits,之所以函数名中有个reg,表明只是一个简单的读写周期,而事实上如果向内存中写入一个数据则需要多个flashRegWrite32Bits周期的组合。
该函数的实际上读出的是32bit数据,但是只保留了低16bit数据。
5. LOCAL FLStatus amd29lvProgram
(
FLFlash* pVol,
CardAddress address,
const void FAR1* buffer,
int length,
FLBoolean overwrite
)
CardAddress address,flash卡的地址
const void FAR1* buffer,数据缓存的地址
int length,写入数据的长度
FLBoolean overwrite,是否进行覆盖写入,如果要求覆盖写入则要求先进行擦出再写入。
向一个flash地址写入数据,正常的理解就是循环调用flashProgram32Bits函数完成写入过程,不过还是要注意一些问题:
l 首先要注意flashProgram32Bits函数每次写入要求必须是4字节对齐的,因此要求flash地址address以及地址buffer都必须是4字节对齐的,否则将返回错误。
l 另一方面是overwrite参数,它的作用在于告诉函数amd29lvProgram flash卡的address地址已经写上了数据,因此在写之前需要进行擦除操作,擦除flash卡上以address开始的length字节数据所在的sector,然后再写入。这就要求在擦除这些sector前需要保留这些sector的数据。
不过这个函数的处理并没有这么复杂,它首先要求address和buffer的开始位置都是4字节对齐的,至于length,这个函数有个莫名其妙的约束,就是overwrite为TRUE且length==2时执行先擦除后写入,否则只有在length是4的倍数时直接写入(不是4的倍数错误退出)。
其实这也正好是这个函数调用中的一个受限制的条件,要么overwrite==TRUE且length==2,要么就是length是4的倍数。不过从后面分析中Ftllite.c中观察,则发现overwrite==TRUE时length==4,那么调用的时候则是直接写入的。此外还函数还有一个明显的bug,就是在overwrite==TRUE时它只能满足flash卡上写入的数据不能跨越sector的情况,如果出现跨越sector则会出现错误。
总的来说,这个函数的实现存在这诸多bug,而且调用条件限制苛刻,建议重新写。
6. LOCAL FLStatus amd29lvSectorRangeErase
(
FLFlash* pVol,
int sectorNum,
int sectorCount
)
int sectorNum, 擦除的sector的开始编号。
int sectorCount,要擦除的sector的数目。
这里仅仅以amd29LV160BT为例进行说明。注意这里的参数并不区分普通sector与boot block sector,而都按照普通sector的大小来对待,因此这里的sectorNum的数值范围是0~31,那么在这种情况下如果第31个sector需要擦除,这个sector就分成了4个,那么则需要擦除的sectorCount要增加3。
还要注意:一个sector包括了4个flash芯片上的sector,4个都要擦除,因此要分为upper分别为TRUE和FALSE分别擦除。
7. LOCAL STATUS flashProgram32Bits
(
FLFlash* pVol,
volatile UINT32* pData,
UINT32 data,
BOOL upper
)
向flash卡内地址为pdata的位置写入32bit数据data。并进行读出检查,看是否正确写入。
向flash芯片中写入数据要遵照如下时序:1)在地址0x555地方写入0xaa;2)在地址0x2aa的地方写入0x55;3)在地址0x555的地方写入0xa0,4)在相应的地址写入数据。
由于步骤1)2)是由函数flashUnlock完成的,所以这里直接调用了flashUnlock。
写完数据之后要进行检查,即读出写入的数据和要写入的数据进行比较,如果数据一致,则说明写入成功。
8. LOCAL STATUS flashHalfSectorErase
(
FLFlash* pVol,
int sectorNum,
BOOL upper
)
erease一个sector的低32位或者高32位。因为一个sector是64位的,这里仅仅erase了一个sector的32位,因此就是半个sector。
pVol->type == 0x1C4表明设备是一个am29lv160b类型的设备,该数值在函数amd29lvMTDIdentify中进行赋值。我们这里只研究pVol->type == 0x1C4即am29lv160b的设备。
对于这个flash卡来说,不同过的sector,其容量可能是不同的,因此首先要根据sectorNum来判断sector的地址和容量,之后才开始进入擦除操作。
擦除数据前要经过如下步骤:1)在地址0x555地方写入0xaa;2)在地址0x2aa的地方写入0x55;3)在地址0x555的地方写入0x80;4)在地址0x555地方写入0xaa;5)在地址0x2aa的地方写入0x55;6)在地址0x555的地方写入0x30。
由于步骤1)2)和步骤4)5)是由函数flashUnlock完成的,所以这里直接调用了flashUnlock。
擦除完毕后要进行检查,即在检查half sector开头的双字是不是全为1,如果全为1则说明写入成功,超过一定时间后发现half sector开头的双字不全1说明写入失败。
需要注意的是这里的参数upper,如果upper为1,则说明是2#3#芯片。
9. LOCAL void flashIdGet
(
FLFlash* pVol,
UINT16* manCode,
UINT16* devCode
)
该函数的作用是获取设备制造商和设备ID。
获取这些信息首先要进入autoselect模式。16bit方式下进入autoselect模式的步骤是:
1)在地址0x555地方写入0xaa;2)在地址0x2aa的地方写入0x55;3)在地址0x555的地方写入0x90。进入autoselect模式后,读出dw地址0和dw地址1寄存器中的数据(对am29lv160来说读取到的数值分别为0x1和0x22c4(am29lv160T类型)/0x2249(am29lv160B类型)。
由于步骤1)、2)是由函数flashUnlock完成的,所以这里直接调用了flashUnlock。
完了之后要调用flashReset函数退出autoSelect模式。
10. LOCAL void flashReset
(
FLFlash* pVol,
BOOL upper
)
根据am29lv160 datasheet的描述,对一个16bit模式芯片来说,向任意地址写入F0即可完成reset,但是这里是要求reset 32bit两个芯片,因此这里是向任意地址写入0x00f000f0。reset之后进入read array data模式。
11. LOCAL void flashUnlock
(
FLFlash* pVol,
BOOL upper
)
根据am29lv160 datasheet的描述,系统有一个Unlock bypass的命令,它是为了更快的完成写入操作,不过这里的flashUnlock的函数的作用并不是这个作用,而是为了其他操作时序的一个组成部分,因此这里只要简单的理解就行了,就是先向0x555写入0xaa,再向0x2AA写入0x55。