Chinaunix首页 | 论坛 | 博客
  • 博客访问: 477299
  • 博文数量: 56
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1957
  • 用 户 组: 普通用户
  • 注册时间: 2013-06-07 23:02
文章分类

全部博文(56)

文章存档

2016年(1)

2014年(7)

2013年(48)

分类: 嵌入式

2013-07-31 22:28:53

本博文为原创,转帖请注明出处。谢谢。

7.2.3 函数库amd9LvMtdLib

根据am29lv160ddatasheet的描述,am29lv160D共有16Mbit的容量,可以配置为2M×8bit或者1M×16bit,具体为byte配置还是双字配置要根据其引脚byte的设置,byte=1说明是双字配置1M×16bit,为0则说明是字节配置2M×8bit。我们需要注意的是am29lv160d的地址线只有20根,即A19~A02M的寻址空间则需要21条地址线,因此这就需要一条额外的地址线。amd的设计很巧妙的实现这个问题,在双字模式下,输出为D15~D0,寻址空间为1MA19~A0正好满足要求;如果是字节模式下则D15~D7位则是无效的,amd的设计就是将其中的D15作为一条附加的地址线A-1。当A-10时,表明地址是一个字的低字节,让A-11时表明地址是一个字的高字节。也就是说在字节模式下其地址线为A19~A-121条满足要求,分别连接外部地址线的A20~A0

am29lv160d共有35sector。其中包含31个大小32kword/64kbyte的普通sector,一个大小为16kword/32kbytesector,两个4kword/8kbytesector,一个8kword/16kbytesector。每个sector都可以单独erase。之所以把一个普通sector分成4个大小不一的sector是为了便于存放boot block

am29lv160d还可以细分为am29lv160dtam29lv160db两种,am29lv160dt表明其boot block是在内存的高端地址空间;am29lv160db表明其boot block存放在低端地址空间。

我们以高端地址空间为例分析一下各个sector的地址空间地址:

对普通sectorsectorNum=0~30)来说,其大小为32kword/64kbyte,地址空间为sectorNum*64KB~(sectorNum+1*64KB-1

31secotor,其大小16kword/32kbyte,地址空间为F8000~FBFFF(word)/1F0000~1F7FFFbyte);

32sector,其大小4kword/8kbyte,地址空间为FC000~FCFFF(word)/1F8000~1F9FFFbyte);

33sector,其大小4kword/8kbyte,地址空间为FD000~FDFFF(word)/1FA000~1FBFFFbyte);

34sector,其大小8kword/16kbyte,地址空间为FE000~FFFFF(word)/1FC000~1FFFFFbyte);

am29lv160d在使用过程中可以利用多个芯片并联的方式来实现。文件amd92lvMtd.c中针对的flash卡则是是由4am29lv160组成的,如图7.4。外部地址线A22~A3连接各芯片的A19~A0外部地址线A2连接各芯片的片选信号,选中单个芯片,这样就实现了64位内存存储空间。


图7.4 一种64flash卡的连接方法

但是由于CPU32bit的,因此在访问内存的时候就分为高4字节和低4字节分别访问,我们在后面的读写空间中也可以看到在访问函数的参数中都有一个upper参数,该函数是一个bool变量,1表示访问高4字节,也就是2#3#芯片,0表示访问低4字节,也就是0#1#芯片。

这样一来,其存储容量扩充为8MB,共有35scoter。每个sectorvxworks中双字模式下的地址分别为(以4am29lv160dt为例):

对普通sectorsectorNum=0~30)来说,其大小为sectorSize=0x40000256KB),地址空间为sectorNum*sectorSize~(sectorNum+1)*sectorSize-1

31secotor,其大小0x20000128KB),地址空间为7C0000~7DFFFF

32sector,其大小0x800032KB),地址空间为7E0000~7E7FFF

33sector,其大小0x800032KB),地址空间为7E8000~7EFFFF

34sector,其大小0x1000064KB),地址空间为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接口,它包含了基本的三个函数readwritemap

函数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的数据。

不过这个函数的处理并没有这么复杂,它首先要求addressbuffer的开始位置都是4字节对齐的,至于length,这个函数有个莫名其妙的约束,就是overwriteTRUElength==2时执行先擦除后写入,否则只有在length4的倍数时直接写入(不是4的倍数错误退出)。

其实这也正好是这个函数调用中的一个受限制的条件,要么overwrite==TRUElength==2,要么就是length4的倍数。不过从后面分析中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为例进行说明。注意这里的参数并不区分普通sectorboot block sector,而都按照普通sector的大小来对待,因此这里的sectorNum的数值范围是0~31,那么在这种情况下如果第31sector需要擦除,这个sector就分成了4个,那么则需要擦除的sectorCount要增加3。

还要注意:一个sector包括了4flash芯片上的sector4个都要擦除,因此要分为upper分别为TRUEFALSE分别擦除。

7. LOCAL STATUS flashProgram32Bits

    (

    FLFlash* pVol,

    volatile UINT32* pData,

    UINT32 data,

    BOOL upper

)

flash卡内地址为pdata的位置写入32bit数据data。并进行读出检查,看是否正确写入。

向flash芯片中写入数据要遵照如下时序1)在地址0x555地方写入0xaa2)在地址0x2aa的地方写入0x553)在地址0x555的地方写入0xa04)在相应的地址写入数据。

由于步骤12)是由函数flashUnlock完成的,所以这里直接调用了flashUnlock

写完数据之后要进行检查,即读出写入的数据和要写入的数据进行比较,如果数据一致,则说明写入成功。

8. LOCAL STATUS flashHalfSectorErase

    (

    FLFlash* pVol,

    int sectorNum,

    BOOL upper

    )

erease一个sector的低32位或者高32位。因为一个sector64位的,这里仅仅erase了一个sector32位,因此就是半个sector

pVol->type == 0x1C4表明设备是一个am29lv160b类型的设备,该数值在函数amd29lvMTDIdentify中进行赋值。我们这里只研究pVol->type == 0x1C4am29lv160b的设备。

对于这个flash卡来说,不同过的sector,其容量可能是不同的,因此首先要根据sectorNum来判断sector的地址和容量,之后才开始进入擦除操作。

擦除数据前要经过如下步骤:1)在地址0x555地方写入0xaa2)在地址0x2aa的地方写入0x553)在地址0x555的地方写入0x804)在地址0x555地方写入0xaa5)在地址0x2aa的地方写入0x556)在地址0x555的地方写入0x30

由于步骤12)和步骤45)是由函数flashUnlock完成的,所以这里直接调用了flashUnlock

擦除完毕后要进行检查,即在检查half sector开头的双字是不是全为1,如果全为1则说明写入成功,超过一定时间后发现half sector开头的双字不全1说明写入失败。

需要注意的是这里的参数upper,如果upper1,则说明是2#3#芯片。

9. LOCAL void flashIdGet

    (

    FLFlash* pVol,

    UINT16* manCode,

    UINT16* devCode

    )

该函数的作用是获取设备制造商和设备ID

获取这些信息首先要进入autoselect模式。16bit方式下进入autoselect模式的步骤是:

1)在地址0x555地方写入0xaa2)在地址0x2aa的地方写入0x553)在地址0x555的地方写入0x90。进入autoselect模式后,读出dw地址0dw地址1寄存器中的数据(对am29lv160来说读取到的数值分别为0x10x22c4am29lv160T类型)/0x2249(am29lv160B类型)。

由于步骤1)、2)是由函数flashUnlock完成的,所以这里直接调用了flashUnlock

完了之后要调用flashReset函数退出autoSelect模式。

10. LOCAL void flashReset

    (

    FLFlash* pVol,

    BOOL upper

    )

根据am29lv160 datasheet的描述,对一个16bit模式芯片来说,向任意地址写入F0即可完成reset,但是这里是要求reset 32bit两个芯片,因此这里是向任意地址写入0x00f000f0reset之后进入read array data模式。

11. LOCAL void flashUnlock

    (

    FLFlash* pVol,

    BOOL upper

    )

根据am29lv160 datasheet的描述,系统有一个Unlock bypass的命令,它是为了更快的完成写入操作,不过这里的flashUnlock的函数的作用并不是这个作用,而是为了其他操作时序的一个组成部分,因此这里只要简单的理解就行了,就是先向0x555写入0xaa,再向0x2AA写入0x55

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