以下の例では、SEデータフラッシュ用に設計されたバンク切替のアーキテクチャを備えた限定キュー手法を、PEデータフラッシュで動作するよう簡単に変更することができることを示しています。この例では、256 x 16のSEデータセクタサイズを使用しています。各セクタは、最大、32ワードの8エントリを保持します。表9は、各セクタのメモリマップを示します。図3は、エントリがSEデータフラッシュに書き込まれる仕組みを示しています。
表9. 有界キューのメモリマップの例
FQueueBank0[ ]
Queue Index
Data Flash
7
0x40E0-0x40FF
6
0x40C0-0x4FDF
5
0x40A0-0x40BF
....
....
2
0x4040-0x405F
1
0x4020-0x403F
0
0x4000-0x401F
FQueueBank1[ ]
Queue Index
Data Flash
15
0x50E0-0x50FF
14
0x50C0-0x5FDF
13
0x50A0-0x50BF
....
....
10
0x5040-0x505F
9
0x5020-0x503F
8
0x5000-0x501F
図3. SEフラッシュ用のバンクスイッチを備えた有界キューのフロー
図3の例の定数とデータの構造は、以下のように定義されています。
変数と定数
#define C_Q_BANKSIZE ( 8 ) // The number of entries in each bank
#define C_Q_BANKS ( 2 ) // The number of banks
#define C_Q_ERASE_THRESH ( C_Q_BANKSIZE - 2 )
#define C_Q_MAX ( C_Q_BANKSIZE * C_Q_BANKS )
#define C_Q_MAX_ID ( C_Q_MAX * 2 )
#define C_Q_DATASIZE ( 31 ) // Data size in words
#define C_Q_FULL ( -1 )
#define C_Q_XSUM_ERROR ( -2 )
#define C_FLASH_EMPTY ( 0xFF )
// For this example each entry is 31 words of arbitrary data.
typedef struct {
u16 iData[C_Q_DATASIZE];
u8 iID;
u8 iChecksum;
} QENTRY;
extern bool ChecksumValid(QENTRY *pEntry);
extern u16 flashEraseSector(void *pAddress);
extern u16 flashWrite(void *pAddress, u16 iData);
extern QENTRY FQueueBank0[C_Q_MAX]; // Mapped to SE data flash sector 0
extern QENTRY FQueueBank1[C_Q_MAX]; // Mapped to SE data flash sector 1
extern u8 iQID; // ID number of the last entry
extern u8 iQIndex; // Index of current entry
extern u8 iBank; // Index of current flash bank
QENTRY *FQueue[C_Q_BANKS] = {FQueueBank0, FQueueBank1};
初期化ルーチンは、以下のとおりです。
初期化コード
/*
// queueInitialize()
// This routine returns the current valid entry in the queue and
// sets the global variable iQID to the ID of the current valid entry.
*/
short queueInitialize(void)
{
s16 iIndex;
iQID = 0;
// Find the active sector
for (iBank=0;iBank < C_Q_BANKS; ++iBank)
{
// Only the current sector will have the first entry used
// and the last entry empty.
if (FQueue[iBank][0].iID != C_FLASH_EMPTY &&
FQueue[iBank][C_Q_BANKSIZE-1].iID == C_FLASH_EMPTY) {
// Find the last valid entry.
for (iIndex=1;iIndex < C_Q_BANKSIZE-1; ++iIndex)
{
if (FQueue[iBank][iIndex].iID == C_FLASH_EMPTY)
{
iQID = FQueue[iBank][iIndex-1].iID;
return iIndex - 1 + C_Q_BANKSIZE*iBank;
}
}
}
}
// Should never get here. The queue is full, return error.
return C_Q_FULL;
}
書込みルーチンコードを次に示します。
書込みデータコード
/*
// queueWriteEntry()
// Writes new entry into the flash queue.
*/
short queueWriteEntry(QENTRY *pEntry)
{
void *pDest;
u16 i;
if (pEntry != NULL)
{
// Compute the new packet ID.
iQID = (iQID + 1) % C_Q_MAX_ID;
// Compute the new Index.
iQIndex = (iQIndex + 1) % C_Q_MAX;
// Set the packet ID.
pEntry->iID = iQID;
// Calculate the checksum.
pEntry->iChecksum = iQID;
for (i = 0; i < C_Q_DATASIZE; ++i)
pEntry->iChecksum += pEntry->iData[i];
// Write packet to flash.
pDest = &FQueue[iQIndex / C_Q_BANKSIZE][iQIndex % C_Q_BANKSIZE];
for (i = 0; i < sizeof(QENTRY)/2; ++i)
flashWrite(pDest, ((u16 *)pEntry)[i]);
return true;
}
return false;
}
/*
// queueEraseSectorMaintenance()
// This routine monitors the flash sectors and, when the current sector is
// almost full, it erases the next sector to free up more room.
// Returns true if a sector erase occurred during the call.
//
// This routine must be called at least once per queueWriteEntry() call.
*/
bool queueEraseSectorMaintenance(void)
{
// If the sector is almost full then we want to try and erase the next
// sector.
if ((iQIndex % C_Q_BANKSIZE) > C_Q_ERASE_THRESH)
{
short iBank = (iQIndex / C_Q_BANKSIZE + 1) % C_Q_BANKS;
// If there are restrictions on when flash can be erased
// then additional conditional can be added here.
// Just make sure that the next sector is erased before the current
// sector is full.
if (FQueue[iBank][0].iID != C_FLASH_EMPTY)
{
flashEraseSector(FQueue[iBank]);
return true;
}
}
return false;
}
これで作業は、この極めて簡単な例を256 x 16のPEデータフラッシュで使用することができるよう変換するだけになります。エントリを個別に消去することができるため、PEデータフラッシュではバンク切替は必要ありません。これによってコードが簡略化されます。表10は、更新されたメモリマップを示し、図4は、エントリがPEデータフラッシュに書き込まれる仕組みを示しています。
#define C_Q_MAX ( 8 )
#define C_Q_MAX_ID ( C_Q_MAX * 2 )
#define C_Q_DATASIZE ( 31 ) // Data size in words
#define C_Q_FULL ( -1 )
#define C_Q_XSUM_ERROR ( -2 )
#define C_FLASH_EMPTY ( 0xFF )
// For this example each entry is 31 words of arbitrary data.
typedef struct {
u16 iData[C_Q_DATASIZE];
u8 iID;
u8 iChecksum;
} QENTRY;
extern bool ChecksumValid(QENTRY *pEntry);
extern u16 flashEraseSector(void *pAddress);
extern u16 flashWrite(void *pAddress, u16 iData);
extern QENTRY FlashQueue[C_Q_MAX]; // Mapped to PE data flash
extern u8 iQID; // ID number of the last entry
extern u8 iQIndex; // Index of current entry
/*
// queueInitialize()
// This routine returns the current valid entry in the queue and
// sets the global variable iQID to the ID of the current valid entry.
*/
short queueInitialize(void)
{
s16 iIndex;
iQID = 0;
// Find the last valid entry.
for (iIndex=0;iIndex < C_Q_MAX-1; ++iIndex)
{
if (FlashQueue[iIndex].iID == C_FLASH_EMPTY)
{
iIndex = (iIndex + C_Q_MAX - 1) % C_Q_MAX;
iQID = FlashQueue[iIndex].iID % C_Q_MAX_ID;
return iIndex;
}
}
// Should never get here.The queue is full,return error.
return C_Q_FULL;
}
/*
// queueWriteEntry()
// Writes new entry into the flash queue.
*/
short queueWriteEntry(QENTRY *pEntry)
{
void *pDest;
u16 i;
if (pEntry != NULL)
{
// Compute the new packet ID.
iQID = (iQID + 1) % C_Q_MAX_ID;
// Compute the new Index.
iQIndex = (iQIndex + 1) % C_Q_MAX;
// Set the packet ID.
pEntry->iID = iQID;
// Calculate the checksum.
pEntry->iChecksum = iQID;
for (i = 0; i < C_Q_DATASIZE; ++i)
pEntry->iChecksum += pEntry->iData[i];
// If next entry is full erase it. There must always be at least one
// empty entry so the latest entry can be located.
if (FlashQueue[(iQIndex + 1) % C_Q_MAX].iID != C_FLASH_EMPTY)
{
// dataFlashErasePage erases two pages (2 words) at a time
// so we need to call it for every other word address in the
// entry.
for (i = 0; i < sizeof(QENTRY)/4; ++i)
dataFlashErasePage (((u16 *)&FlashQueue[(iQIndex + 1) % C_Q_MAX])
+ i*2);
}
// Write packet to flash.
// Assumption is that entry will already be erased due to previous
// two lines of code.
pDest = &FlashQueue[iQIndex];
for (i = 0; i < sizeof(QENTRY)/2; ++i)
dataFlashWrite(pDest, ((u16 *)pEntry)[i]);
return true;
}
return false;
}
/*
// VerySimpleReFlash()
// Sector Erasable Flash
// Application code resides in Sector 1 only.
// Step 1. Wait for erase command, then erase flash.
// Step 2. Wait for program command, then program flash one word
// at a time.
*/
void VerySimpleReFlash()
{
u16 iStatus; // The status returned from flash utility ROM
// calls
u16 iSize; // The size of the main code to program
u16 *pAddress = 0x2000; // The starting address of the main application
InitializeCOMM(); // Can be CAN or UART.
WaitForEraseCommand();
SlowDownWatchdogUpdate(); // If watchdog enabled set timeout > 15s.
iStatus = flashEraseSector(C_ADDRESS_SECTOR_1);
SendFlashErasedResponse(iStatus);
UpdateWatchdog(); // Prevent timeout
if (iStatus)
ResetMicro();
iSize = WaitForProgramCommand();
while (iSize--)
{
u16 iData = GetWordFromCOMM();
iStatus = flashWrite(pAddress, iData);
if (iStatus)
break;
++pAddress;
UpdateWatchdog(); // Prevent timeout
}
SendFlashWriteResponse(iStatus);
ResetMicro();
}
/*
// VerySimpleReFlash()
// Page Erasable Flash
// Application code resides in Sector 1 only.
// Step 1. Wait for erase command, then erase flash.
// Step 2. Wait for program command, then program flash one word
// at a time.
*/
void VerySimpleReFlash()
{
u16 iStatus; // The status returned from flash utility ROM
// calls
u16 iSize; // The size of the main code to program
u16 *pAddress = 0x2000; // The starting address of the main application
u16 i;
InitializeCOMM(); // Can be CAN or UART
WaitForEraseCommand();
SlowDownWatchdogUpdate(); // If watchdog enabled set timeout > 15s
for (i=C_APP_SECTOR_START;i < C_APP_SECTOR_END; ++i)
{
iStatus = flashEraseSector(i * C_SECTOR_SIZE);
// Stop on error
if (iStatus)
break;
}
SendFlashErasedResponse(iStatus);
UpdateWatchdog(); // Prevent timeout
if (iStatus)
ResetMicro();
iSize = WaitForProgramCommand();
flashWriteEmulateInit(); // Just in case we aborted before
while (iSize--)
{
u16 iData = GetWordFromCOMM();
iStatus = flashWriteEmulate(pAddress, iData);
if (iStatus)
break;
++pAddress;
UpdateWatchdog(); // Prevent timeout
}
SendFlashWriteResponse(iStatus);
ResetMicro();
}
static u8 iCount = 0; // Keeps track of how many words in the buffer
static u16 iBuffer[32]; // Buffer that temporarily holds data to write
static u16 *pAddr; // Holds the starting address of the page to
// write.
/*
// flashWriteEmulate()
// Emulates single word write by collecting 32 words in a buffer
// and calling flashWrite() when necessary.
//
*/
u16 flashWriteEmulate(u16 *pAddress, u16 iData)
{
u16 i;
iBuffer[iCount++] = iData;
if (iCount == 0)
{
pAddr = pAddress; // Store the starting address of the page.
}
else if (iCount == 32) {
iCount = 0;
return flashWrite(pAddr,iBuffer);
}
return 0;
}
/*
// flashWriteEmulateInit()
// Resets the counter in case of any error conditions abort the write
// process in the middle of a page.
*/
void flashWriteEmulateInit()
{
iCount = 0;
}