我可爱的多多还没满三个月时,公司就把我招回来干活了。为了能准时下班陪多多,我在上班的前两天拼命的debug呀,终于把M2MTC那个串口的问题解决了。接下来就很闲,闲的发慌的时候就胡思乱想,有一分钟想到了以前phoenix EFI下读写SPI flash的问题,于是决定也来研究一下AMI的flash模块。
u 第一只程序—FlashInit
BOOLEAN found = FALSE;
for(i=0; !found && FlashList[i]; i++)
{
found=FlashList[i](pBlockAddress, &FlashAPI);
}
FlashList的定义为:IDENTIFY*FlashList[] = {FLASH_LIST NULL};
FlashList是一个指针数组,它在flash.mak里有定义/D\"FLASH_LIST=$(FlashList)\"\,这个数组的每一个指针都指向一个函数。由于不同厂家的flash part都可能有不同的opcode,所以每一家的flash part都有一个identify函数,在我们这个案子里,一共支持了4种flash part,我们可以在build生成的token.mak里找到FlashList的实际值。
FlashList = IdentifySst_25VF,\
IdentifyStm_25PExx,\
IdentifyAtmel_26DF,\
IdentifySst_25LF,
所以FlashList[0]= IdentifySst_25VF
FlashList[1]= IdentifyStm_25Pexx
FlashList[2]= IdentifyAtmel_26DF
FlashList[3]= IdentifySst_25LF
FlashList[4]=NULL
如果FlashList[0]返回值为true,就表明flash part型号已经找到,否则继续调用FlashList[1],依此类推,直到FlashList[i]全部执行完都没有找到flash part,就可以向全世界公告本机台不支持你这颗flash芯片了。
u 第二只程序—CommonSpiReadId
其实所有的Identify函数都差不多,只是opcode不同而已,这里我们以IdentifySst_25VF为例来研究。这只程序调用到的第一个函数为
CommonSpiReadId( &Sst_25VF, &MfgDevId );
注意Sst_25VF的定义为:
// Flash Part Data Structures
FLASH_INFOSst_25VF =
{ // Write Byte
{SPI_SST25LF_COMMAND_WRITE, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS},
// Read Data
{SPI_SST25LF_COMMAND_READ, SPI_OPCODE_TYPE_READ_WITH_ADDRESS},
// Erase 4k Sector
{SPI_SST25LF_COMMAND_ERASE, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS},
// Read Device Status Reg
{SPI_SST25LF_COMMAND_READ_STATUS,SPI_OPCODE_TYPE_READ_NO_ADDRESS},
// Read device ID
{SPI_SST25VF_COMMAND_READ_ID, SPI_OPCODE_TYPE_READ_WITH_ADDRESS},
// Write Status Register
{SPI_SST25LF_COMMAND_WRITE_S, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS},
// Write Status Enable
{SPI_SST25LF_COMMAND_WRITE_S_EN, SPI_OPCODE_TYPE_READ_NO_ADDRESS},
// Write Enable
{SPI_SST25LF_COMMAND_WRITE_ENABLE,SPI_OPCODE_TYPE_WRITE_NO_ADDRESS},
1, // Page Size
SECTOR_SIZE_4KB
};
CommonSpiReadId这只程序所做的事情如下:
(1) InitializeSpiEnvironment(FlashInfo);
//FlashInfo就是Sst_25VF, SPI BAR=RCBA+0x3020
把Sst_25VF里相应的值填到SPI BAR 那边的opcode menu和opcode type里去。
注意:write status enable和write enable这两个opcode是填到opcode prefix那边,和其他的read,read ID等命令区别开,以防止flash芯片被恶意擦写。
(2) SPI Address填0,然后往SPIcontrol寄存器下命令,opcode index要按照(1)里面SPI_SST25VF_COMMAND_READ_ID所在位置来设置。
(3) ID读回来后,根据ID就能判断flash芯片的容量了。使用一个结构体mExFlashPart来记录下容量,ID,还有opcode等信息,
u 第三个重点—FLASH_PART FlashAPI
(1)第一只程序FlashInit跑完后,会返回一个类型为FLASH_PART的FlashAPI。
typedef struct _FLASH_PART {
FLASH_READ_COMMAND FlashReadCommand;
FLASH_ERASE_COMMAND FlashEraseCommand;
FLASH_PROGRAM_COMMAND FlashProgramCommand;
FLASH_IS_ERASE_COMPLETED FlashIsEraseCompleted;
FLASH_IS_PROGRAM_COMPLETED FlashIsProgramCompleted;
FLASH_BLOCK_WRITE_ENABLE FlashBlockWriteEnable;
FLASH_BLOCK_WRITE_DISABLE FlashBlockWriteDisable;
FLASH_DEVICE_WRITE_ENABLE FlashDeviceWriteEnable;
FLASH_DEVICE_WRITE_DISABLE FlashDeviceWriteDisable;
FLASH_VIRTUAL_FIXUP FlashVirtualFixup;
UINT32 FlashProgramSize;
UINT32 FlashSectorSize;
UINT8 *FlashPartNumber;
} FLASH_PART;
(2)上面那个FlashAPI是啥时候被初始化的呢,原来第二只程序IdentifySst_25VF读完ID后,会给FlashAPI赋值,
*FlashStruct = &mCommonSpiFlash;
FLASH_PART mCommonSpiFlash ={
CommonSpiReadCommand,
CommonSpiEraseCommand,
CommonSpiProgramCommand,
CommonSpiIsEraseCompleted,
CommonSpiIsProgramCompleted,
CommonSpiBlockWriteEnable,
CommonSpiBlockWriteDisable,
CommonSpiDeviceWriteEnable,
CommonSpiDeviceWriteDisable,
CommonSpiDeviceVirtualFixup,
1, // defaultvalue, should be changed in Init function
SECTOR_SIZE_4KB
};
所以实际上FlashAPI -> FlashReadCommand = CommonSpiReadCommand; 等等。。
各位,请注意这里的Common,所谓Common就是通用了,我之所以能通用,是因为CommonSpiReadId里的InitializeSpiEnvironment已经把opcode填好了。不同的flash芯片最大的差异就在于opcode的不同,真希望有一天,这个世界可以和谐到所有的opcode都统一。
所以以后的读写擦除程序,你想单干的都必须先做FlashInit。
(1) CommonSpiReadCommand
很简单,使用SPI_SST25LF_COMMAND_READ命令,一次读一个byte回来,想要多长的数据就读多少次了。
(2) CommonSpiEraseCommand
FlashPart擦除是以一个sector为单位的,大多数sector都是4KB的,也有些是8KB或者64KB。
说到sector,我一直在想,它和block到底谁是老大,他们的实际关系如下:
1 Flash = n x FV (Flash Volume)
1 FV = n x Block
1 Block = n x Sector
1 page = n x Sector
擦之前先读一下,如果值已经是全FF,就不必再擦了。
(3) CommonSpiProgramCommand
有些flash芯片只支持一次写一个byte,有些可以一次写256个byte。
下一次命令写一点,写完为止。