//***************************************************************************** // // Flashstore.c - Data logger module handles storage in flash. // this module is responsible for all memory interfacess in the system // each storage type can be defined here and to be accessed from this interface. // This module manages the storage of data logger of sampeled data and configuration into flash memory. //***************************************************************************** #include #include #include #include #include #include #include "Drivers/On_Chip_Flash/Flashstore.h" //***************************************************************************** // // This module manages the storage of data logger of sampeled data and configuration into flash memory. // //***************************************************************************** //***************************************************************************** // // Define the beginning and end of the flash storage area. You must make sure // that this area is well clear of any space occupied by the application // binary, and that this space is not used for any other purpose. // The start and end addresses must be 1K aligned. The end address is // exclusive - it is 1 value greater than the last valid location used for // storage. // //***************************************************************************** typedef struct { uint32_t m_startAddress; uint32_t m_endAddress; } DataSection_t; static DataSection_t g_ui32Addresses[NumOfDataTypes] = {{FLASH_STORE_START_ADDR,FLASH_STORE_END_ADDR}, {FLASH_CONFIG_START_ADDR,FLASH_CONFIG_END_ADDR}}; //***************************************************************************** // This array holds pointer per memory section/type config each pointer represents // The next address in flash, that will be used for storing a data record. // //***************************************************************************** static uint32_t g_ui32StoreAddr[NumOfDataTypes]; //***************************************************************************** // // A buffer used to assemble a complete record of data prior to storing it // in the flash. // //***************************************************************************** //static uint32_t g_pui32RecordBuf[32]; //***************************************************************************** // // Check for non cyclic section owerflow // //***************************************************************************** inline FlashStoreCheckCyclic(FlashDataType_t _dataType,uint32_t _currAddress) { if ((_dataType == ConfigData) && (_currAddress > g_ui32Addresses[_dataType].m_endAddress)) { //JigStopAllWithError("Internal: flash address owerflow"); } } //***************************************************************************** // // Initializes the flash storage. This is a stub because there is nothing // special to do. // //***************************************************************************** void FlashStoreInit(void) { } void FlashSendEndReq(FlashDataType_t _dataType) { /* if (_dataType == ADCData) { // Write last record ((tLogRecord *)g_pui32RecordBuf)->m_itemMask = 0; WriteLogRecord((tLogRecord *)g_pui32RecordBuf); }*/ } //***************************************************************************** // // Saves data records that are stored in the flash to an externally connected // USB memory storage device (USB stick). // The flash memory is scanned for the presence of store data records. When // records are found they are written in CSV format to the USB stick. This // function assumes a non-corrupted storage area, and that any records, once // found, are contiguous with all stored records. It will find the oldest // record and start with that when storing. // //***************************************************************************** int32_t FlashStoreSave(FlashDataType_t _dataType) { /* uint32_t ui32Addr, ui32OldestRecord, ui32OldestIndex, ui32Count, ui32PartialCount; tLogRecord *p_sRecord; // // Initialize locals. // ui32OldestRecord = g_ui32Addresses[_dataType].m_startAddress; ui32OldestIndex = FORMATED_FLASH_SIGNATURE; // // Show a message to the user. // //SetStatusText("SAVE\r\n", "SCANNING ", "FLASH\r\n", 0); // // Start at beginning of flash storage area // ui32Addr = g_ui32Addresses[_dataType].m_startAddress; // // Search all of flash area checking every stored record. // while(ui32Addr < g_ui32Addresses[_dataType].m_endAddress) { // // If a record signature is found, check for oldest record, then // increment to the next record // if((HWREG(ui32Addr) & CRC_SEED_MASK) == MAGIC_NUMBER_OF_FREE_REC_BEG) { // // Get a pointer to the data record (account for flash header word) // p_sRecord = (tLogRecord *)(ui32Addr + RECORD_HEADER_SIZE); // // If the seconds in this record are older than any found so far // then save the seconds value, and the address of this record // if(p_sRecord->m_recIndex < ui32OldestIndex) { ui32OldestIndex = p_sRecord->m_recIndex; ui32OldestRecord = ui32Addr; } // // Advance the address to the next record. // jump to the begining of the next free page ui32Addr += HWREG(ui32Addr) & RECORD_LENGTH_MASK; } else { // // A record was not found so just advance to the next location in // flash // ui32Addr += RECORD_HEADER_SIZE; } } // // If no "oldest" records was found, then there is no valid data stored // if(ui32OldestIndex == FORMATED_FLASH_SIGNATURE) { FlashSendEndReq(_dataType); //SetStatusText("SAVE\r\n", "NO RECORDS ", "FOUND\r\n", 0); return(1); } // // Open the output file on the USB stick. It will return NULL if there // was any problem. // TODO open external flash // if(!USBStickOpenLogFile(0)) // { // SetStatusText("SAVE", 0, "USB ERROR", "PRESS <"); // return(1); // } // // Notify user we are saving data to USB // //SetStatusText("SAVE\r\n", "SAVING ", "TO USB\r\n", 0); // // Start reading records from flash, start at the address of the oldest // record, as found above. We scan through records, assuming the flash // store is not corrupted. Continue scanning until a blank space is // found which should indicate the end of recorded data, or until we // have read all the records. // ui32Addr = ui32OldestRecord; while(HWREG(ui32Addr) != FORMATED_FLASH_SIGNATURE) { // // If a record signature is found (which it should be), extract the // record data and send it to USB stick. // if((HWREG(ui32Addr) & CRC_SEED_MASK) == MAGIC_NUMBER_OF_FREE_REC_BEG) { // // Get byte count for this record // ui32Count = HWREG(ui32Addr) & RECORD_LENGTH_MASK; // // Adjust the count and the address to remove the flash header // ui32Count -= RECORD_HEADER_SIZE; ui32Addr += RECORD_HEADER_SIZE; // // Adjust for memory wrap // if(ui32Addr >= g_ui32Addresses[_dataType].m_endAddress) { FlashStoreCheckCyclic(_dataType,ui32Addr); ui32Addr = g_ui32Addresses[_dataType].m_startAddress; } // // If the contents of this record go past the end of the memory // storage area, then perform a partial copy first. // ui32PartialCount = 0; if((ui32Addr + ui32Count) >= g_ui32Addresses[_dataType].m_endAddress) { FlashStoreCheckCyclic(_dataType,ui32Addr + ui32Count); // // Find how many bytes are left on this page // ui32PartialCount = g_ui32Addresses[_dataType].m_endAddress - ui32Addr; // // Copy the portion until the end of memory store, adjust // remaining count and address // memcpy(g_pui32RecordBuf, (void *)ui32Addr, ui32PartialCount); ui32Count -= ui32PartialCount; ui32Addr = g_ui32Addresses[_dataType].m_startAddress; } // // Copy entire record (or remaining part of record if memory wrap) // into record buffer // memcpy(&g_pui32RecordBuf[ui32PartialCount / 4], (void *)ui32Addr,ui32Count); // // Update address pointer to next record // ui32Addr += ui32Count; // // Now we have an entire data logger record copied from flash // storage into a local (contiguous) memory buffer. Pass it // to the USB file writing function to write the record to the // USB stick. // switch(_dataType) { case ConfigData: { //ConfigurationReadData((tLogRecord *)g_pui32RecordBuf); TODO fix break; } case ADCData: { //WriteLogRecord((tLogRecord *)g_pui32RecordBuf); TODO fix break; } } } else { // // This should not happen, but it means we ended up in a non-blank // location that is not the start of a record. In this case just // advance through memory until either a blank location or another // record is found. // // Increment to next word in flash, adjust for memory wrap. // ui32Addr += RECORD_HEADER_SIZE; if(ui32Addr >= g_ui32Addresses[_dataType].m_endAddress) { FlashStoreCheckCyclic(_dataType,ui32Addr); ui32Addr = g_ui32Addresses[_dataType].m_startAddress; } } } // // Close the USB stick file so that any buffers will be flushed. // // TODO close extrnal flash USBStickCloseFile(); // // Inform user that save is complete. // //SetStatusText("SAVE\r\n", "USB SAVE ", "COMPLETE\r\n", 0); if (_dataType == ADCData) { FlashSendEndReq(_dataType); } // // Return success // * */ return(0); } //***************************************************************************** // function used to write into internal flash specific buffer //***************************************************************************** int32_t FlashStoreWriteBuffer(FlashDataType_t _dataType,uint32_t* _pui32Record,uint32_t _ui32ItemCount) { // // Check the arguments // if(!_pui32Record) { //JigStopAllWithError("Internal: empty flash record"); return(1); } // // Check to see if the record is going to cross a page boundary. // if(((g_ui32StoreAddr[_dataType] & FLASH_PAGE_BOUNDARY_MASK) + _ui32ItemCount) > FLASH_PAGE_BOUNDARY_MASK) { // // Find number of bytes remaining on this page // uint32_t ui32Idx = FLASH_PAGE_SIZE_IN_BYTES - (g_ui32StoreAddr[_dataType] & FLASH_PAGE_BOUNDARY_MASK); // // Program part of the record on the space remaining on the current // page // FlashProgram(_pui32Record, g_ui32StoreAddr[_dataType], ui32Idx); // // Increment the store address by the amount just written, which // should make the new store address be at the beginning of the next // flash page. // g_ui32StoreAddr[_dataType] += ui32Idx; // // Adjust the remaining bytes to program, and the pointer to the // remainder of the record data. // _ui32ItemCount -= ui32Idx; _pui32Record = &_pui32Record[ui32Idx / NUMBER_OF_BYTES_IN_INT]; // // Check to see if the new page is past the end of store and adjust // if(g_ui32StoreAddr[_dataType] >= g_ui32Addresses[_dataType].m_endAddress) { FlashStoreCheckCyclic(_dataType,g_ui32StoreAddr[_dataType]); g_ui32StoreAddr[_dataType] = g_ui32Addresses[_dataType].m_startAddress; } // // If new page is not blank, then erase it // if(HWREG(g_ui32StoreAddr[_dataType]) != FORMATED_FLASH_SIGNATURE) { FlashErase(g_ui32StoreAddr[_dataType]); } } // // Now program the remaining part of the record (if we crossed a page // boundary above) or the full record to the current location in flash // FlashProgram(_pui32Record, g_ui32StoreAddr[_dataType], _ui32ItemCount); // // Increment the storage address to the next location. // g_ui32StoreAddr[_dataType] += _ui32ItemCount; // // Return success indication to caller. // return(0); } //***************************************************************************** // // This is called at the start of logging to prepare space in flash for // storage of logged data. It searches for the first blank area in the // flash storage to be used for storing records. // // If a starting address is specified then the search is skipped and it goes // directly to the new address. If the starting address is 0, then it performs // the search. // //***************************************************************************** int32_t FlashStoreOpenLogFile(FlashDataType_t _dataType,uint32_t _ui32StartAddr) { uint32_t ui32Addr; // // If a valid starting address is specified, then just use that and skip // the search below. // if((_ui32StartAddr >= g_ui32Addresses[_dataType].m_startAddress) && (_ui32StartAddr < g_ui32Addresses[_dataType].m_endAddress)) { g_ui32StoreAddr[_dataType] = _ui32StartAddr; return(0); } // // Start at beginning of flash storage area // ui32Addr = g_ui32Addresses[_dataType].m_startAddress; // // Search until a blank is found or the end of flash storage area // while((HWREG(ui32Addr) != FORMATED_FLASH_SIGNATURE) && (ui32Addr < g_ui32Addresses[_dataType].m_endAddress)) { // // If a record signature is found, then increment to the next record // if((HWREG(ui32Addr) & CRC_SEED_MASK) == MAGIC_NUMBER_OF_FREE_REC_BEG) { ui32Addr += HWREG(ui32Addr) & RECORD_LENGTH_MASK; } else { // // Just advance to the next location in flash // ui32Addr += RECORD_HEADER_SIZE; } } // // If we are at the end of flash that means no blank area was found. // So reset to the beginning and erase the first page. // if(ui32Addr >= g_ui32Addresses[_dataType].m_endAddress) { ui32Addr = g_ui32Addresses[_dataType].m_startAddress; FlashErase(ui32Addr); } // // When we reach here we either found a blank location, or made a new // blank location by erasing the first page. // To keep things simple we are making an assumption that the flash store // is not corrupted and that the first blank location implies the start // of a blank area suitable for storing data records. // g_ui32StoreAddr[_dataType] = ui32Addr; // // Return success indication to caller // return(0); } //***************************************************************************** // // This is called each time there is a new data record to log to the flash // storage area. A simple algorithm is used which rotates programming // data log records through an area of flash. It is assumed that the current // page is blank. Records are stored on the current page until a page // boundary is crossed. If the page boundary is crossed and the new page // is not blank (testing only the first location), then the new page is // erased. Finally the entire record is programmed into flash and the // storage pointers are updated. // // While storing and when crossing to a new page, if the flash page is not // blank it is erased. So this algorithm overwrites old data. // // The data is stored in flash as a record, with a flash header prepended, // and with the record length padded to be a multiple of 4 bytes. The flash // header is a 3-byte magic number and one byte of record length. // //***************************************************************************** /*int32_t FlashStoreWriteRecord(FlashDataType_t _dataType,tLogRecord *_psRecord) { uint32_t ui32Idx, _ui32ItemCount, *_pui32Record; // // Check the arguments // if(!_psRecord) { JigStopAllWithError("Internal: invalid flash record"); return(1); } // // Determine how many channels are to be logged // ui32Idx = _psRecord->m_itemMask; _ui32ItemCount = 0; while(ui32Idx) { if(ui32Idx & 1) { _ui32ItemCount++; } ui32Idx >>= 1; } // // Add 16-bit count equivalent of record header, time stamp, and // selected items mask. This is the total number of 16 bit words // of the record. // _ui32ItemCount += 6; // // Convert the count to bytes, be sure to pad to 32-bit alignment. // _ui32ItemCount = ((_ui32ItemCount * 2) + 3) & ~3; // // Create the flash record header, which is a 3-byte signature and a // one byte count of bytes in the record. Save it at the beginning // of the write buffer. // ui32Idx = MAGIC_NUMBER_OF_FREE_REC_BEG | (_ui32ItemCount & RECORD_LENGTH_MASK); g_pui32RecordBuf[0] = ui32Idx; // // Copy the rest of the record to the buffer, and get a pointer to // the buffer. // memcpy(&g_pui32RecordBuf[1], _psRecord, _ui32ItemCount - NUMBER_OF_BYTES_IN_INT); _pui32Record = g_pui32RecordBuf; int32_t result_of_flash_writing = FlashStoreWriteBuffer(_dataType,_pui32Record,_ui32ItemCount); return result_of_flash_writing; return 0; } */ //***************************************************************************** // // Erase the data storage area of flash. // //***************************************************************************** void FlashStoreErase(FlashDataType_t _dataType) { uint32_t ui32Addr; // // Inform user we are erasing // // SetStatusText("ERASE\r\n", 0, "ERASING\r\n", 0); // // Loop through entire storage area and erase each page. // for(ui32Addr = g_ui32Addresses[_dataType].m_startAddress; ui32Addr < g_ui32Addresses[_dataType].m_endAddress; ui32Addr += FLASH_PAGE_SIZE_IN_BYTES) { FlashErase(ui32Addr); } // // Inform user the erase is done. // // SetStatusText("SAVE\r\n", "ERASE ", "COMPLETE\r\n", 0); } //***************************************************************************** // // Determine if the flash block that contains the address is blank. // //***************************************************************************** static int32_t IsBlockFree(uint32_t _ui32BaseAddr) { uint32_t ui32Addr; // // Make sure we start at the beginning of a 1K block // _ui32BaseAddr &= ~FLASH_PAGE_BOUNDARY_MASK; // // Loop through every address in this block and test if it is blank. // for(ui32Addr = 0; ui32Addr < FLASH_PAGE_SIZE_IN_BYTES; ui32Addr += RECORD_HEADER_SIZE) { if(HWREG(_ui32BaseAddr + ui32Addr) != FORMATED_FLASH_SIGNATURE) { // // Found a non-blank location, so return indication that block // is not free. // return(0); } } // // If we made it to here then every location in this block is erased, // so return indication that the block is free. // return(1); } //***************************************************************************** // // Report to the user the amount of free space and used space in the data // storage area. // //***************************************************************************** void FlashStoreReport(FlashDataType_t _dataType) { uint32_t ui32Addr, ui32FreeBlocks, ui32UsedBlocks = 0; static char p_cBufFree[16], p_cBufUsed[16]; // // Initialize locals. // ui32FreeBlocks = 0; ui32UsedBlocks = 0; // // Loop through each block of the storage area and count how many blocks // are free and non-free. // for(ui32Addr = g_ui32Addresses[_dataType].m_startAddress; ui32Addr < g_ui32Addresses[_dataType].m_endAddress; ui32Addr += FLASH_PAGE_SIZE_IN_BYTES) { if(IsBlockFree(ui32Addr)) { ui32FreeBlocks++; } else { ui32UsedBlocks++; } } // // Report the result to the user via a status display screen. // usnprintf(p_cBufFree, sizeof(p_cBufFree), "FREE: %3uK", ui32FreeBlocks); usnprintf(p_cBufUsed, sizeof(p_cBufUsed), "USED: %3uK", ui32UsedBlocks); //SetStatusText("FREE FLASH\r\n", p_cBufFree, p_cBufUsed, "\r\n"); }