diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index 4dd9a6b..2969e01 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -84,15 +84,36 @@ int Esp32Platform::readBytes(uint8_t * buffer, uint16_t maxLen) return len; } -uint8_t * Esp32Platform::getEepromBuffer(uint16_t size) +bool Esp32Platform::writeNVMemory(uint32_t addr,uint8_t data) { - EEPROM.begin(size); - return EEPROM.getDataPtr(); + *((uint8_t*)addr) = data; + return true; } -void Esp32Platform::commitToEeprom() +uint8_t Esp32Platform::readNVMemory(uint32_t addr) +{ + return *((uint8_t*)addr); +} + +uint32_t Esp32Platform::allocNVMemory(uint32_t size,uint32_t ID) +{ + if(size > EEPROM_EMULATION_SIZE) + fatalError(); + return (uint32_t)EEPROM.getDataPtr(); +} + +uint32_t Esp32Platform::reloadNVMemory(uint32_t ID) +{ + EEPROM.begin(1024); + return (uint32_t)EEPROM.getDataPtr(); +} + +void Esp32Platform::finishNVMemory() { EEPROM.commit(); } +void Esp32Platform::freeNVMemory(uint32_t ID) +{ +} #endif diff --git a/src/esp32_platform.h b/src/esp32_platform.h index 9394577..b73de3a 100644 --- a/src/esp32_platform.h +++ b/src/esp32_platform.h @@ -28,8 +28,12 @@ public: int readBytes(uint8_t* buffer, uint16_t maxLen) override; //memory - uint8_t* getEepromBuffer(uint16_t size); - void commitToEeprom(); + bool writeNVMemory(uint32_t addr,uint8_t data); + uint8_t readNVMemory(uint32_t addr); + uint32_t allocNVMemory(uint32_t size,uint32_t ID); + uint32_t reloadNVMemory(uint32_t ID); + void finishNVMemory(); + void freeNVMemory(uint32_t ID); private: WiFiUDP _udp; }; diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index 738df8c..76b4bfe 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -85,14 +85,36 @@ int EspPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) return len; } -uint8_t * EspPlatform::getEepromBuffer(uint16_t size) +bool EspPlatform::writeNVMemory(uint32_t addr,uint8_t data) { - EEPROM.begin(size); - return EEPROM.getDataPtr(); + *((uint8_t*)addr) = data; + return true; } -void EspPlatform::commitToEeprom() +uint8_t EspPlatform::readNVMemory(uint32_t addr) +{ + return *((uint8_t*)addr); +} + +uint32_t EspPlatform::allocNVMemory(uint32_t size,uint32_t ID) +{ + if(size > EEPROM_EMULATION_SIZE) + fatalError(); + return (uint32_t)EEPROM.getDataPtr(); +} + +uint32_t EspPlatform::reloadNVMemory(uint32_t ID) +{ + EEPROM.begin(1024); + return (uint32_t)EEPROM.getDataPtr(); +} + +void EspPlatform::finishNVMemory() { EEPROM.commit(); } + +void EspPlatform::freeNVMemory(uint32_t ID) +{ +} #endif diff --git a/src/esp_platform.h b/src/esp_platform.h index 372ff32..db28c93 100644 --- a/src/esp_platform.h +++ b/src/esp_platform.h @@ -29,8 +29,12 @@ class EspPlatform : public ArduinoPlatform int readBytes(uint8_t* buffer, uint16_t maxLen) override; //memory - uint8_t* getEepromBuffer(uint16_t size); - void commitToEeprom(); + bool writeNVMemory(uint32_t addr,uint8_t data); + uint8_t readNVMemory(uint32_t addr); + uint32_t allocNVMemory(uint32_t size,uint32_t ID); + uint32_t reloadNVMemory(uint32_t ID); + void finishNVMemory(); + void freeNVMemory(uint32_t ID); private: WiFiUDP _udp; }; diff --git a/src/knx/application_program_object.cpp b/src/knx/application_program_object.cpp index 7269ff4..1858fbe 100644 --- a/src/knx/application_program_object.cpp +++ b/src/knx/application_program_object.cpp @@ -74,6 +74,35 @@ uint32_t ApplicationProgramObject::getInt(uint32_t addr) return ::getInt(TableObject::data() + addr); } +uint32_t ApplicationProgramObject::size(){ + return sizeof(_programVersion)+TableObject::size(); +} + + +uint8_t* ApplicationProgramObject::save() +{ + if(TableObject::data() == NULL) + return NULL; + + uint8_t* buffer; + uint32_t addr =(uint32_t)(TableObject::data() - sizeof(_programVersion) - TableObject::sizeMetadata()); + if(TableObject::_platform.NVMemoryType() == internalFlash) + buffer = new uint8_t[sizeof(_programVersion)]; + else + buffer = (uint8_t*)addr; + + pushByteArray(_programVersion, sizeof(_programVersion), buffer); + + if(TableObject::_platform.NVMemoryType() == internalFlash){ + for(uint32_t i=0;i> 8)); @@ -1839,4 +1839,4 @@ void bcdToPayload(uint8_t* payload, size_t payload_length, int index, uint8_t va payload[index / 2] = (payload[index / 2] & 0xF0) | (value & 0x0F); else payload[index / 2] = (payload[index / 2] & 0x0F) | ((value << 4) & 0xF0); -} \ No newline at end of file +} diff --git a/src/knx/ip_parameter_object.cpp b/src/knx/ip_parameter_object.cpp index d35883e..b107f30 100644 --- a/src/knx/ip_parameter_object.cpp +++ b/src/knx/ip_parameter_object.cpp @@ -5,6 +5,15 @@ //224.0.23.12 #define DEFAULT_MULTICAST_ADDR 0xE000170C +#define METADATA_SIZE ( sizeof(_projectInstallationId) \ + +sizeof(_ipAssignmentMethod) \ + +sizeof(_ipCapabilities) \ + +sizeof(_ipAddress) \ + +sizeof(_subnetMask) \ + +sizeof(_defaultGateway) \ + +sizeof(_multicastAddress) \ + +sizeof(_ttl) \ + +sizeof(_friendlyName)) IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platform): _deviceObject(deviceObject), _platform(platform) @@ -146,6 +155,38 @@ uint8_t IpParameterObject::propertySize(PropertyID id) } return 0; } +uint32_t IpParameterObject::size(){ + return METADATA_SIZE; +} + +uint8_t* IpParameterObject::save() +{ + uint8_t* buffer = new uint8_t[METADATA_SIZE]; + + buffer = pushWord(_projectInstallationId, buffer); + buffer = pushByte(_ipAssignmentMethod, buffer); + buffer = pushByte(_ipCapabilities, buffer); + buffer = pushInt(_ipAddress, buffer); + buffer = pushInt(_subnetMask, buffer); + buffer = pushInt(_defaultGateway, buffer); + buffer = pushInt(_multicastAddress, buffer); + buffer = pushByte(_ttl, buffer); + buffer = pushByteArray((uint8_t*)_friendlyName, 30, buffer); + buffer -= METADATA_SIZE; + + if(_platform.NVMemoryType() == internalFlash){ + _platform.freeNVMemory(_ID); + uint32_t addr = _platform.allocNVMemory(METADATA_SIZE, _ID); + + for(uint32_t i=0;irestore(data); + + } +} + +void Memory::writeFlashMemory() +{ + for (int i = 0; i < _saveCount; i++){ + _saveRestores[i]->save(); + } + + _platform.finishNVMemory(); + _modified = false; +} + +void Memory::readRamMemory() +{ + _data = (uint8_t*)_platform.reloadNVMemory(1); if (_data[0] != 0x00 || _data[1] != 0xAD || _data[2] != 0xAF || _data[3] != 0xFE) return; @@ -26,11 +85,20 @@ void Memory::readMemory() for (int i = 0; i < size; i++) { buffer = _saveRestores[i]->restore(buffer); + buffer = (uint8_t*)(((uint32_t)buffer + 3) / 4 * 4); //allign to 32bit } } -void Memory::writeMemory() +void Memory::writeRamMemory() { + uint32_t bytesToSave = 10; + + for (int i = 0; i < _saveCount; i++){ + bytesToSave += _saveRestores[i]->size(); + } + + _data = (uint8_t*)_platform.allocNVMemory(bytesToSave,BASE_ID); + _data[0] = 0x00; _data[1] = 0xAD; _data[2] = 0xAF; @@ -41,16 +109,103 @@ void Memory::writeMemory() for (int i = 0; i < size; i++) { buffer = _saveRestores[i]->save(buffer); + buffer = (uint8_t*)(((uint32_t)buffer + 3) / 4 * 4); //allign to 32bit } - _platform.commitToEeprom(); + _platform.finishNVMemory(); _modified = false; } + + + +void Memory::readExternalMemory() +{ + + int size = _saveCount; + volatile uint32_t addr = _platform.reloadNVMemory(BASE_ID); + volatile uint32_t bytesToRestore; + + if(addr == 0) + return; + + + if (_platform.readNVMemory(addr++) != 0x00 || _platform.readNVMemory(addr++) != 0xAD || _platform.readNVMemory(addr++) != 0xAF || _platform.readNVMemory(addr++) != 0xFE) + return; + + + for (int i = 0; i < size; i++) + { + ((uint8_t*)&bytesToRestore)[0] = _platform.readNVMemory(addr++); + ((uint8_t*)&bytesToRestore)[1] = _platform.readNVMemory(addr++); + ((uint8_t*)&bytesToRestore)[2] = _platform.readNVMemory(addr++); + ((uint8_t*)&bytesToRestore)[3] = _platform.readNVMemory(addr++); + + if(bytesToRestore == 0) + continue; + _data = _platform.allocMemory(bytesToRestore); + if(_data == NULL) + _platform.fatalError(); + + for (uint32_t e=0;erestore(_data); + } +} + +void Memory::writeExternalMemory() +{ + uint32_t bytesToSave = 4; + int size = _saveCount; + + _platform.freeNVMemory(BASE_ID); + + for (int i = 0; i < size; i++){ + bytesToSave += _saveRestores[i]->size() + 4; + } + + uint32_t addr = _platform.allocNVMemory(bytesToSave,BASE_ID); + + //write valid mask + _platform.writeNVMemory(addr++,0x00); + _platform.writeNVMemory(addr++,0xAD); + _platform.writeNVMemory(addr++,0xAF); + _platform.writeNVMemory(addr++,0xFE); + + for (int i = 0; i < size; i++) + { + _data = _saveRestores[i]->save(); + if(_data == NULL) + bytesToSave = 0; + else + bytesToSave = _saveRestores[i]->size(); + + //write size + _platform.writeNVMemory(addr++,((uint8_t*)&bytesToSave)[0]); + _platform.writeNVMemory(addr++,((uint8_t*)&bytesToSave)[1]); + _platform.writeNVMemory(addr++,((uint8_t*)&bytesToSave)[2]); + _platform.writeNVMemory(addr++,((uint8_t*)&bytesToSave)[3]); + + if(bytesToSave == 0) + continue; + + + for (uint32_t e=0;e= MAXSAVE - 1) return; + obj->memoryID(BASE_ID + _saveCount); _saveRestores[_saveCount] = obj; _saveCount += 1; } diff --git a/src/knx/memory.h b/src/knx/memory.h index b9831ec..7661c1e 100644 --- a/src/knx/memory.h +++ b/src/knx/memory.h @@ -16,6 +16,12 @@ public: void writeMemory(); void addSaveRestore(SaveRestore* obj); private: + void readFlashMemory(); + void writeFlashMemory(); + void readRamMemory(); + void writeRamMemory(); + void readExternalMemory(); + void writeExternalMemory(); Platform& _platform; bool _modified = false; SaveRestore* _saveRestores[MAXSAVE] = {0}; diff --git a/src/knx/platform.cpp b/src/knx/platform.cpp index eaf18bb..fef1c8f 100644 --- a/src/knx/platform.cpp +++ b/src/knx/platform.cpp @@ -12,6 +12,8 @@ uint8_t* Platform::memoryReference() uint8_t* Platform::allocMemory(size_t size) { uint8_t* address = (uint8_t*)malloc(size); + if(address == NULL) + fatalError(); // if (_memoryReference == 0 || address < _memoryReference) // _memoryReference = address; print("MemRef: "); diff --git a/src/knx/platform.h b/src/knx/platform.h index 497d98e..351b27c 100644 --- a/src/knx/platform.h +++ b/src/knx/platform.h @@ -4,6 +4,13 @@ #include #include "save_restore.h" +typedef enum{ + notDefined, + internalRam, + internalFlash, + external +}NVMemory_t; + class Platform { public: @@ -29,13 +36,18 @@ class Platform virtual int readUart() = 0; virtual size_t readBytesUart(uint8_t* buffer, size_t length) = 0; - virtual uint8_t* getEepromBuffer(uint16_t size) = 0; - virtual void commitToEeprom() = 0; + virtual bool writeNVMemory(uint32_t addr,uint8_t data) = 0; + virtual uint8_t readNVMemory(uint32_t addr) = 0; + virtual uint32_t allocNVMemory(uint32_t size,uint32_t ID) = 0; + virtual uint32_t reloadNVMemory(uint32_t ID) = 0; + virtual void finishNVMemory() = 0; + virtual void freeNVMemory(uint32_t ID) = 0; virtual uint8_t* memoryReference(); virtual uint8_t* allocMemory(size_t size); virtual void freeMemory(uint8_t* ptr); - + NVMemory_t NVMemoryType(){return _NVMemoryType;} protected: uint8_t* _memoryReference = 0; -}; \ No newline at end of file + NVMemory_t _NVMemoryType = notDefined; +}; diff --git a/src/knx/save_restore.h b/src/knx/save_restore.h index f87091d..74d28e5 100644 --- a/src/knx/save_restore.h +++ b/src/knx/save_restore.h @@ -16,6 +16,7 @@ class SaveRestore * the start of its buffer. */ virtual uint8_t* save(uint8_t* buffer) = 0; + virtual uint8_t* save() = 0; /** * This method is called when the object should restore its state from the buffer. * @@ -25,4 +26,8 @@ class SaveRestore * the start of its buffer. */ virtual uint8_t* restore(uint8_t* buffer) = 0; + virtual uint32_t size() = 0; + void memoryID (uint32_t ID){_ID = ID;} + protected: + uint32_t _ID; }; \ No newline at end of file diff --git a/src/knx/table_object.cpp b/src/knx/table_object.cpp index c9aaa1b..9c09bfb 100644 --- a/src/knx/table_object.cpp +++ b/src/knx/table_object.cpp @@ -3,6 +3,8 @@ #include "table_object.h" #include "bits.h" +#define METADATA_SIZE (sizeof(_state)+sizeof(_errorCode)+sizeof(_size)) + TableObject::TableObject(Platform& platform): _platform(platform) { @@ -92,11 +94,40 @@ uint8_t* TableObject::save(uint8_t* buffer) return buffer; } +uint8_t* TableObject::save() +{ + if(_data == NULL) + return NULL; + + uint32_t addr; + uint8_t* buffer; + addr =(uint32_t)(_data - METADATA_SIZE); + if(_platform.NVMemoryType() == internalFlash) + buffer = new uint8_t[METADATA_SIZE]; + else + buffer = (uint8_t*)addr; + + buffer = pushByte(_state, buffer); + buffer = pushByte(_errorCode, buffer); + buffer = pushInt(_size, buffer); + buffer -= METADATA_SIZE; + + if(_platform.NVMemoryType() == internalFlash){ + for(uint32_t i=0;i 0) - _data = _platform.allocMemory(_size); + _data = buffer; + else _data = 0; - buffer = popByteArray(_data, _size, buffer); + buffer += _size; return buffer; } @@ -124,9 +153,13 @@ uint32_t TableObject::tableReference() bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte) { - if (_data) + if (_dataComplete) { - _platform.freeMemory(_data); + if(_platform.NVMemoryType() == internalFlash) + _platform.freeNVMemory(_ID); + else if(_platform.NVMemoryType() == external) + _platform.freeMemory(_dataComplete); + _dataComplete = 0; _data = 0; _size = 0; } @@ -134,15 +167,24 @@ bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte) if (size == 0) return true; - _data = _platform.allocMemory(size); - if (!_data) - return false; - + if(_platform.NVMemoryType() == internalFlash){ + _dataComplete = (uint8_t*)_platform.allocNVMemory(size+this->size(), _ID); + } + else{ + _dataComplete = _platform.allocMemory(size+this->size()); + } + _data = _dataComplete + this->size(); //skip metadata _size = size; - - if (doFill) - memset(_data, fillByte, size); - + if (doFill){ + if(_platform.NVMemoryType() == internalFlash){ + uint32_t addr = (uint32_t)_data; + for(uint32_t i=0; i<_size;i++) + _platform.writeNVMemory(addr++, fillByte); + } + else{ + memset(_data, fillByte, _size); + } + } return true; } @@ -279,7 +321,12 @@ uint8_t* TableObject::data() uint32_t TableObject::size() { - return _size; + return _size + METADATA_SIZE; +} + +uint32_t TableObject::sizeMetadata() +{ + return METADATA_SIZE; } void TableObject::errorCode(ErrorCode errorCode) diff --git a/src/knx/table_object.h b/src/knx/table_object.h index d30fb70..f256451 100644 --- a/src/knx/table_object.h +++ b/src/knx/table_object.h @@ -26,7 +26,9 @@ public: */ LoadState loadState(); virtual uint8_t* save(uint8_t* buffer); + virtual uint8_t* save(); virtual uint8_t* restore(uint8_t* buffer); + virtual uint32_t size(); protected: /** * This method is called before the interface object enters a new ::LoadState. @@ -40,15 +42,18 @@ protected: * must not be freed. */ uint8_t* data(); + uint32_t sizeMetadata(); /** * returns the size of the internal data of the interface object int byte. */ - uint32_t size(); + // uint32_t size(); /** * Set the reason for a state change failure. */ void errorCode(ErrorCode errorCode); + Platform& _platform; + uint8_t *_dataComplete = 0; private: uint32_t tableReference(); bool allocTable(uint32_t size, bool doFill, uint8_t fillByte); @@ -67,7 +72,6 @@ protected: */ void loadState(LoadState newState); LoadState _state = LS_UNLOADED; - Platform& _platform; uint8_t *_data = 0; uint32_t _size = 0; ErrorCode _errorCode = E_NO_FAULT; diff --git a/src/knx_facade.h b/src/knx_facade.h index d059a17..7b98e19 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -257,9 +257,16 @@ template class KnxFacade : private SaveRestore uint32_t _buttonPin = 0; saveRestoreCallback _saveCallback = 0; saveRestoreCallback _restoreCallback = 0; + uint32_t (*_sizeCallback)() = 0; bool _toogleProgMode = false; bool _progLedState = false; + uint32_t size(){ + if (_sizeCallback != 0) + return _sizeCallback(); + + return 0; + } uint8_t* save(uint8_t* buffer) { if (_saveCallback != 0) @@ -268,6 +275,7 @@ template class KnxFacade : private SaveRestore return buffer; } + uint8_t* save(){}; uint8_t* restore(uint8_t* buffer) { if (_restoreCallback != 0) diff --git a/src/linux_platform.cpp b/src/linux_platform.cpp index 8445381..16814b6 100644 --- a/src/linux_platform.cpp +++ b/src/linux_platform.cpp @@ -200,22 +200,44 @@ int LinuxPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) return len; } -uint8_t * LinuxPlatform::getEepromBuffer(uint16_t size) +bool LinuxPlatform::writeNVMemory(uint32_t addr,uint8_t data) { - if (_fd < 0) - doMemoryMapping(); - - return _mappedFile + 2; + *((uint8_t*)addr) = data; + return true; } -void LinuxPlatform::commitToEeprom() +uint8_t LinuxPlatform::readNVMemory(uint32_t addr) +{ + return *((uint8_t*)addr); +} + +uint32_t LinuxPlatform::allocNVMemory(uint32_t size,uint32_t ID) { if (_fd < 0) doMemoryMapping(); + return (uint32_t)_mappedFile + 2; +} + +uint32_t LinuxPlatform::reloadNVMemory(uint32_t ID) +{ + if (_fd < 0) + doMemoryMapping(); + + return (uint32_t)_mappedFile + 2; +} + +void LinuxPlatform::finishNVMemory() +{ + if (_fd < 0) + doMemoryMapping(); + fsync(_fd); } +void LinuxPlatform::freeNVMemory(uint32_t ID) +{ +} #define FLASHSIZE 0x10000 void LinuxPlatform::doMemoryMapping() { diff --git a/src/linux_platform.h b/src/linux_platform.h index 722c537..b3bc09a 100644 --- a/src/linux_platform.h +++ b/src/linux_platform.h @@ -44,8 +44,12 @@ public: size_t readBytesUart(uint8_t *buffer, size_t length) override; //memory - uint8_t* getEepromBuffer(uint16_t size) override; - void commitToEeprom() override; + bool writeNVMemory(uint32_t addr,uint8_t data); + uint8_t readNVMemory(uint32_t addr); + uint32_t allocNVMemory(uint32_t size,uint32_t ID); + uint32_t reloadNVMemory(uint32_t ID); + void finishNVMemory(); + void freeNVMemory(uint32_t ID); uint8_t* allocMemory(size_t size) override; void freeMemory(uint8_t* ptr) override; void cmdlineArgs(int argc, char** argv); diff --git a/src/samd_flash.cpp b/src/samd_flash.cpp new file mode 100644 index 0000000..afd59be --- /dev/null +++ b/src/samd_flash.cpp @@ -0,0 +1,416 @@ +#include +#include "samd_flash.h" + +#define VALID (0xDEADC0DE) + +extern uint32_t __etext; +extern uint32_t __data_start__; +extern uint32_t __data_end__; + +static const uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 }; + +SamdFlash::SamdFlash(){ + _pageSize = pageSizes[NVMCTRL->PARAM.bit.PSZ]; + _pageCnt = NVMCTRL->PARAM.bit.NVMP; + _rowSize = PAGES_PER_ROW * _pageSize; + _rowBuffer = new uint8_t[_rowSize]; + + //find end of program flash and set limit to next row + uint32_t endEddr = (uint32_t)(&__etext + (&__data_end__ - &__data_start__)); //text + data MemoryBlock + _MemoryEnd = getRowAddr(endEddr) + _rowSize-1; //23295 + + //map info structure to last row in flash + _info = (info_data_t*)getRowAddr(_pageSize*_pageCnt-1); //261888 (rowAddr of last row) +} + +uint32_t SamdFlash::getStartAddress(){ + return _info->freeMemoryStart; +} + +uint8_t SamdFlash::read(uint8_t* addr){ + if(_rowBufferModified == false) + return *addr; + + if(getRowAddr((uint32_t)addr) != _rowBufferAddr) + return *addr; + + //return data from buffer because flash data is not up to date + return _rowBuffer[(uint32_t)addr-_rowBufferAddr]; +} + +bool SamdFlash::write(uint8_t* addr, uint8_t data){ + //Check if the destination address is valid + if ((uint32_t)addr >= (_pageSize * _pageCnt) || (uint32_t)addr <= _MemoryEnd) + return false; + + uint32_t newRowAddr = getRowAddr((uint32_t)addr); + //if the row changed, write old Buffer and init new + if(newRowAddr != _rowBufferAddr){ + uploadRowBuffer(_rowBufferAddr); + downloadRowBuffer(newRowAddr); + _rowBufferAddr = newRowAddr; + } + + _rowBuffer[(uint32_t)addr-_rowBufferAddr] = data; + _rowBufferModified = true; + return true; +} + +void SamdFlash::uploadRowBuffer(uint32_t rowAddr){ + if(!_rowBufferModified) return; + eraseRow(rowAddr); + //Clear Page Buffer + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC; + while (NVMCTRL->INTFLAG.bit.READY == 0) {} + // Disable automatic page write + NVMCTRL->CTRLB.bit.MANW = 1; + + volatile uint32_t* src_addr = (volatile uint32_t*)_rowBuffer; + volatile uint32_t* dst_addr = (volatile uint32_t *)rowAddr; + for(uint32_t p=0;pCTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP; + while (NVMCTRL->INTFLAG.bit.READY == 0) { } + } +} +void SamdFlash::downloadRowBuffer(uint32_t rowAddr){ + volatile uint32_t* src_addr = (volatile uint32_t*)rowAddr; + volatile uint32_t* dst_addr = (volatile uint32_t *)_rowBuffer; + for(uint32_t p=0;pADDR.reg = rowAddr/2; + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; + while (!NVMCTRL->INTFLAG.bit.READY) { } +} + +void SamdFlash::erase(){ + uint32_t rowAddr = getRowAddr((uint32_t)_info); + uint32_t nextrowAddr = getRowAddr(_MemoryEnd); + + while(rowAddr != nextrowAddr){ + eraseRow(rowAddr); + rowAddr = rowAddr - _rowSize; + } +} + +void SamdFlash::erase(uint32_t ID){ + block_meta_data_t ramCopy; + block_meta_data_t* block = getBlock(ID); + if(block == NULL) + return; + + ramCopy = *block; + uint32_t rowAddr = (uint32_t)block; + uint32_t nextrowAddr = getRowAddr(rowAddr + block->size - 1 +sizeof(block_meta_data_t)); + + while(rowAddr != nextrowAddr){ + eraseRow(nextrowAddr); + nextrowAddr = nextrowAddr - _rowSize; + } + uploadRowBuffer(_rowBufferAddr); + + _rowBufferAddr = rowAddr; + downloadRowBuffer(_rowBufferAddr); + //map block meta structure into ram copy + block = (block_meta_data_t*)_rowBuffer; + //copy into row buffer + *block = ramCopy; + _rowBufferModified = true; + uploadRowBuffer(_rowBufferAddr); +} +void SamdFlash::copyAndFree(block_meta_data_t* src, block_meta_data_t* dst){ + block_meta_data_t metaDataRamCopy; + block_meta_data_t* tempBlock; + if(src == NULL || dst == NULL) + return; + + metaDataRamCopy = *dst; + uint32_t lastRow = getRowAddr((uint32_t)src + src->size -1 + sizeof(block_meta_data_t)); + uint32_t srcRow = (uint32_t)src; + uint32_t dstRow = (uint32_t)dst; + + while(srcRow <= lastRow){ + downloadRowBuffer(srcRow); + //if first row copy meta data too + if(srcRow == (uint32_t)src){ + //map block meta structure into ram copy + tempBlock = (block_meta_data_t*)_rowBuffer; + //copy into row buffer + *tempBlock = metaDataRamCopy; + } + _rowBufferModified = true; + uploadRowBuffer(dstRow); + + srcRow = srcRow + _rowSize; + dstRow = dstRow + _rowSize; + } + + //free src + downloadRowBuffer((uint32_t)src); + //map block meta structure into ram copy + tempBlock = (block_meta_data_t*)_rowBuffer; + tempBlock->ID = 0; + _rowBufferModified = true; + uploadRowBuffer((uint32_t)src); +} + +block_meta_data_t* SamdFlash::findEmptyBlock(uint32_t size,uint32_t* compSize, uint32_t* previousBlockAddr){ + block_meta_data_t* block = _info->firstBlock; + block_meta_data_t* preblock = NULL; + block_meta_data_t* conblock = NULL; + if(block != NULL && block->validMask == VALID){ + do{ + if(block->ID == 0){ + if(block->size >= size){ + *compSize = 0; + return block; + } + //ceck if we can merge next empty blocks + else if(block->next != NULL){ + conblock = block->next; + *compSize = block->size; + while(conblock != NULL && conblock->ID == 0){ + *compSize += conblock->size + sizeof(block_meta_data_t); + if(*compSize >= size){ + *previousBlockAddr = (uint32_t)preblock; + return conblock; + } + conblock = conblock->next; + } + } + //block is last one + else{ + *compSize = 0; + *previousBlockAddr = (uint32_t)preblock; + return block; + } + } + preblock = block; + block = block->next; + }while(block != NULL && block->validMask == VALID); + } + return NULL; +} + +uint8_t* SamdFlash::loadBlock(uint32_t ID){ + block_meta_data_t* block = _info->firstBlock; + if(block == (block_meta_data_t*)0xFFFFFFFF) + return NULL; + + if(block != NULL && block->validMask == VALID){ + do{ + if(block->ID == ID){ + return ((uint8_t*)block)+sizeof(block_meta_data_t); + } + block = block->next; + }while(block != NULL && block->validMask == VALID); + } + return NULL; +} + +block_meta_data_t* SamdFlash::getBlock(uint32_t ID){ + uint8_t* block = loadBlock(ID); + if(block == NULL) + return NULL; + return (block_meta_data_t*)(block-sizeof(block_meta_data_t)); +} + +block_meta_data_t* SamdFlash::findLastBlock(){ + block_meta_data_t* block = _info->firstBlock; + if(block == (block_meta_data_t*)0xFFFFFFFF || block == NULL) + return NULL; + while(block->next != NULL && block->next->validMask == VALID){ + block = block->next; + } + return block; +} + +void SamdFlash::free(uint32_t ID){ + block_meta_data_t* block = getBlock(ID); + if(block != NULL){ + //upload actual rowBuffer if necessary + uploadRowBuffer(_rowBufferAddr); + //download block into ram buffer + _rowBufferAddr = (uint32_t)block; + downloadRowBuffer(_rowBufferAddr); + //map block structure into ram copy + block = (block_meta_data_t*)_rowBuffer; + //mark the block as empty and write to flash + block->ID = 0; + _rowBufferModified = true; + uploadRowBuffer(_rowBufferAddr); + } +} + +uint8_t* SamdFlash::malloc(uint32_t size, uint32_t ID){ + //check if ID is already present + if(loadBlock(ID) != NULL) + return NULL; + + return forcemalloc(size, ID); + +} + +uint8_t* SamdFlash::forcemalloc(uint32_t size, uint32_t ID){ + uploadRowBuffer(_rowBufferAddr); + //download actual info row into ram buffer + _rowBufferAddr = (uint32_t)_info; //261888 + downloadRowBuffer(_rowBufferAddr); + //map info structure into ram copy + _info = (info_data_t*)_rowBuffer; + + //create new info if actual is not valid (all data got lost) + if(_info->validMask != VALID){ + _info->validMask = VALID; + _info->freeMemoryStart = getRowAddr(_pageSize*_pageCnt-1)-1; //161887 + _info->firstBlock = NULL; + } + + //create ram copies for further usage + block_meta_data_t newBlock; + block_meta_data_t previousBlock; + uint32_t newBlockFlashAddr; + uint32_t previousBlockFlashAddr = 0; + block_meta_data_t* tempBlock; + + //check if we can use an existing empty block or allocate a new one + uint32_t newSize; + tempBlock = findEmptyBlock(size,&newSize,&previousBlockFlashAddr); + if(tempBlock != NULL){ + //copy found block into ram + newBlock = *tempBlock; + newBlockFlashAddr = (uint32_t)tempBlock; + //two ore more empty blocks were connected + if(newSize != 0){ + newBlock.size = newSize; + if(previousBlockFlashAddr == 0){ + _info->firstBlock = (block_meta_data_t*)newBlockFlashAddr; + } + else{ + //copy found block into ram + previousBlock = *((block_meta_data_t*)previousBlockFlashAddr); + previousBlock.next = (block_meta_data_t*)newBlockFlashAddr; + } + } + //newBlock is last one in list an can be extended + if(newBlock.size < size){ + if(previousBlockFlashAddr == 0){ + newBlockFlashAddr = getRowAddr(getRowAddr(_pageSize*_pageCnt-1) - (size+sizeof(block_meta_data_t))); + newBlock.size = getRowAddr(_pageSize*_pageCnt-1)-newBlockFlashAddr-sizeof(block_meta_data_t); //1008 + //update newBlockAddr in list + _info->firstBlock = (block_meta_data_t*)newBlockFlashAddr; + } + else{ + newBlockFlashAddr = getRowAddr(previousBlockFlashAddr - (size+sizeof(block_meta_data_t))); + newBlock.size = previousBlockFlashAddr-newBlockFlashAddr-sizeof(block_meta_data_t); //1008 + //copy found block into ram and update newBlockAddr in list + previousBlock = *((block_meta_data_t*)previousBlockFlashAddr); + previousBlock.next = (block_meta_data_t*)newBlockFlashAddr; + } + //set MemoryStart to end of new block + _info->freeMemoryStart = newBlockFlashAddr-1; //260863 + } + //fill meta data of new block + newBlock.validMask = VALID; + newBlock.ID = ID; + } + else{ + //check if size fits into free area + if(_info->freeMemoryStart - (size+sizeof(block_meta_data_t)) <= _MemoryEnd) + return NULL; + + //get start address of new block + newBlockFlashAddr = getRowAddr(_info->freeMemoryStart +1 - (size+sizeof(block_meta_data_t))); //260864 (size= 1024-16=1008) + newBlock.size = _info->freeMemoryStart-newBlockFlashAddr+1-sizeof(block_meta_data_t); //1008 + newBlock.next = NULL; + //set MemoryStart to end of new block + _info->freeMemoryStart = newBlockFlashAddr-1; //260863 + //fill meta data of new block + newBlock.validMask = VALID; + newBlock.ID = ID; + + //add block to end of list + tempBlock = findLastBlock();//(block_meta_data_t*)previousBlockFlashAddr; + if(tempBlock == NULL){ + _info->firstBlock = (block_meta_data_t*)newBlockFlashAddr; + } + else{ + //copy found block into ram + previousBlock = *tempBlock; + previousBlock.next = (block_meta_data_t*)newBlockFlashAddr; + previousBlockFlashAddr = (uint32_t)tempBlock; + } + } + + //write modified ram info structure into last flash row + _rowBufferModified = true; + uploadRowBuffer(_rowBufferAddr); + + //write modified ram copy of last block meta into flash + if(previousBlockFlashAddr != 0){ + _rowBufferAddr = previousBlockFlashAddr; + downloadRowBuffer(_rowBufferAddr); + //map block meta structure into ram copy + tempBlock = (block_meta_data_t*)_rowBuffer; + //copy into row buffer + *tempBlock = previousBlock; + + _rowBufferModified = true; + uploadRowBuffer(_rowBufferAddr); + } + + //write ram copy of new block meta into flash + _rowBufferAddr = newBlockFlashAddr; + downloadRowBuffer(_rowBufferAddr); + //map block meta structure into ram copy + tempBlock = (block_meta_data_t*)_rowBuffer; + //copy into row buffer + *tempBlock = newBlock; + _rowBufferModified = true; + uploadRowBuffer(_rowBufferAddr); + + //config structure now points into updated flash again + _info = (info_data_t*)getRowAddr(_pageSize*_pageCnt-1); + return (uint8_t*)(newBlockFlashAddr+sizeof(block_meta_data_t)); +} + +uint8_t* SamdFlash::realloc(uint32_t size, uint32_t ID){ + uint32_t newBlockFlashAddr; + block_meta_data_t* actualBlock; + + actualBlock = getBlock(ID); + + //check if ID is presenty + if(actualBlock == NULL) + return malloc(size, ID); + + //if size already fits into block, nothing to do otherwise alloc block with new size + if(actualBlock->size >= size) + return loadBlock(ID); + + newBlockFlashAddr = (uint32_t)(forcemalloc(size, ID)-sizeof(block_meta_data_t)); + copyAndFree(actualBlock, (block_meta_data_t*)newBlockFlashAddr); + return (uint8_t*)(newBlockFlashAddr+sizeof(block_meta_data_t)); +} diff --git a/src/samd_flash.h b/src/samd_flash.h new file mode 100644 index 0000000..6b1585c --- /dev/null +++ b/src/samd_flash.h @@ -0,0 +1,59 @@ +#ifndef SAMD_FLASH_H +#define SAMD_FLASH_H + +#include + +#define PAGES_PER_ROW 4 + +typedef struct block_meta_data{ + uint32_t validMask; + uint32_t ID; + uint32_t size; + struct block_meta_data *next; +}__attribute__((__packed__))block_meta_data_t; + +typedef struct { + uint32_t validMask; + uint32_t freeMemoryStart; + block_meta_data_t* firstBlock; +}__attribute__((__packed__))info_data_t; + +class SamdFlash{ +public: + SamdFlash(); + void erase(); + void erase(uint32_t ID); + void free(uint32_t ID); + uint8_t* malloc(uint32_t size, uint32_t ID); + uint8_t* realloc(uint32_t size, uint32_t ID); + uint8_t* loadBlock(uint32_t ID); + bool write(uint8_t* addr, uint8_t data); + uint8_t read(uint8_t* addr); + void finalise(); + uint32_t getStartAddress(); + +private: + uint8_t* forcemalloc(uint32_t size, uint32_t ID); + void uploadRowBuffer(uint32_t rowAddr); + void downloadRowBuffer(uint32_t rowAddr); + uint32_t getRowAddr(uint32_t flasAddr); + void eraseRow(uint32_t rowAddr); + void copyAndFree(block_meta_data_t* src, block_meta_data_t* dst); + + + block_meta_data_t* findEmptyBlock(uint32_t size,uint32_t* compSize, uint32_t* nextBlockAddr); + block_meta_data_t* findLastBlock(); + block_meta_data_t* getBlock(uint32_t ID); + + uint32_t _MemoryEnd=0; + uint32_t _pageSize; + uint32_t _rowSize; + uint32_t _pageCnt; + + + uint8_t* _rowBuffer; + bool _rowBufferModified = false; + uint32_t _rowBufferAddr; + volatile info_data_t* _info; +}; +#endif /* SAMD_FLASH_H */ diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp index db94c61..9105354 100644 --- a/src/samd_platform.cpp +++ b/src/samd_platform.cpp @@ -4,14 +4,18 @@ #include #include -#include +#include "samd_flash.h" + +SamdFlash Flash; SamdPlatform::SamdPlatform() : ArduinoPlatform(&Serial1) { + Platform::_NVMemoryType = internalFlash; } SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s) { + Platform::_NVMemoryType = internalFlash; } void SamdPlatform::restart() @@ -20,19 +24,121 @@ void SamdPlatform::restart() NVIC_SystemReset(); } -uint8_t * SamdPlatform::getEepromBuffer(uint16_t size) + +bool SamdPlatform::writeNVMemory(uint32_t addr,uint8_t data) { - //EEPROM.begin(size); - if(size > EEPROM_EMULATION_SIZE) + if(Flash.write((uint8_t*)addr, data)==false) fatalError(); - - return EEPROM.getDataPtr(); + return true; } -void SamdPlatform::commitToEeprom() +uint8_t SamdPlatform::readNVMemory(uint32_t addr) +{ + return Flash.read((uint8_t*)addr); +} + +uint32_t SamdPlatform::allocNVMemory(uint32_t size,uint32_t ID) +{ + uint32_t addr = (uint32_t)Flash.malloc(size, ID); + if(addr == 0) + fatalError(); + return addr; +} + +uint32_t SamdPlatform::reloadNVMemory(uint32_t ID) +{ + // Flash.erase(); + return (uint32_t)Flash.loadBlock(ID); +} + +void SamdPlatform::finishNVMemory() +{ + Flash.finalise(); +} + +void SamdPlatform::freeNVMemory(uint32_t ID) +{ + Flash.free(ID); + // Flash.erase(); +} + +uint8_t* SamdPlatform::memoryReference() +{ + return (uint8_t*)Flash.getStartAddress(); +} + + + +/*************_NVMemoryType = internalRam************************* + +bool SamdPlatform::writeNVMemory(uint32_t addr,uint8_t data) +{ + *((uint8_t*)addr) = data; + return true; +} + +uint8_t SamdPlatform::readNVMemory(uint32_t addr) +{ + return *((uint8_t*)addr); +} + +uint32_t SamdPlatform::allocNVMemory(uint32_t size,uint32_t ID) +{ + if(size > EEPROM_EMULATION_SIZE) + fatalError(); + return (uint32_t)EEPROM.getDataPtr(); +} + +uint32_t SamdPlatform::reloadNVMemory(uint32_t ID) +{ + return (uint32_t)EEPROM.getDataPtr(); +} + +void SamdPlatform::finishNVMemory() { EEPROM.commit(); } + +void SamdPlatform::freeNVMemory(uint32_t ID) +{ +} +/* + +/*************_NVMemoryType = external************************* +bool SamdPlatform::writeNVMemory(uint32_t addr,uint8_t data) +{ + EEPROM.write(addr-1, data); + return true; +} + +uint8_t SamdPlatform::readNVMemory(uint32_t addr) +{ + return EEPROM.read(addr-1); +} + +uint32_t SamdPlatform::allocNVMemory(uint32_t size,uint32_t ID) +{ + if(size > EEPROM_EMULATION_SIZE) + fatalError(); + return 1; +} + +uint32_t SamdPlatform::reloadNVMemory(uint32_t ID) +{ + return 1; +} + +void SamdPlatform::finishNVMemory() +{ + EEPROM.commit(); +} + +void SamdPlatform::freeNVMemory(uint32_t ID) +{ +} + +*/ + #endif diff --git a/src/samd_platform.h b/src/samd_platform.h index 6b45c83..eaf2ee1 100644 --- a/src/samd_platform.h +++ b/src/samd_platform.h @@ -11,8 +11,13 @@ public: SamdPlatform( HardwareSerial* s); void restart(); - uint8_t* getEepromBuffer(uint16_t size); - void commitToEeprom(); + bool writeNVMemory(uint32_t addr,uint8_t data); + uint8_t readNVMemory(uint32_t addr); + uint32_t allocNVMemory(uint32_t size,uint32_t ID); + uint32_t reloadNVMemory(uint32_t ID); + void finishNVMemory(); + void freeNVMemory(uint32_t ID); + uint8_t* memoryReference(); }; #endif