diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp index f370a33..e5633dc 100644 --- a/src/samd_platform.cpp +++ b/src/samd_platform.cpp @@ -4,17 +4,29 @@ #include #include +#ifdef USE_SAMD_EEPROM_EMULATION #include +#endif + +#if KNX_FLASH_SIZE % 1024 +#error "KNX_FLASH_SIZE must be multiple of 1024" +#endif SamdPlatform::SamdPlatform() #ifndef KNX_NO_DEFAULT_UART : ArduinoPlatform(&Serial1) #endif { +#ifndef USE_SAMD_EEPROM_EMULATION + init(); +#endif } SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s) { +#ifndef USE_SAMD_EEPROM_EMULATION + init(); +#endif } uint32_t SamdPlatform::uniqueSerialNumber() @@ -43,7 +55,9 @@ void SamdPlatform::restart() NVIC_SystemReset(); } -uint8_t * SamdPlatform::getEepromBuffer(uint16_t size) +#ifdef USE_SAMD_EEPROM_EMULATION +#pragma warning "Using EEPROM Simulation" +uint8_t* SamdPlatform::getEepromBuffer(uint16_t size) { //EEPROM.begin(size); if(size > EEPROM_EMULATION_SIZE) @@ -56,6 +70,156 @@ void SamdPlatform::commitToEeprom() { EEPROM.commit(); } +#else + +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}; + +void SamdPlatform::init() +{ + _memoryType = Flash; + _pageSize = pageSizes[NVMCTRL->PARAM.bit.PSZ]; + _pageCnt = NVMCTRL->PARAM.bit.NVMP; + _rowSize = PAGES_PER_ROW * _pageSize; + + // find end of program flash and set limit to next row + uint32_t endEddr = (uint32_t)(&__etext + (&__data_end__ - &__data_start__)); // text + data MemoryBlock + _MemoryStart = getRowAddr(_pageSize * _pageCnt - KNX_FLASH_SIZE - 1); // 23295 + _MemoryEnd = getRowAddr(_pageSize * _pageCnt - 1); + // chosen flash size is not available anymore + if (_MemoryStart < endEddr) { + println("KNX_FLASH_SIZE is not available (possible too much flash use by firmware)"); + fatalError(); + } +} + +size_t SamdPlatform::flashEraseBlockSize() +{ + return PAGES_PER_ROW; +} + +size_t SamdPlatform::flashPageSize() +{ + return _pageSize; +} + +uint8_t* SamdPlatform::userFlashStart() +{ + return (uint8_t*)_MemoryStart; +} + +size_t SamdPlatform::userFlashSizeEraseBlocks() +{ + if (KNX_FLASH_SIZE <= 0) + return 0; + else + return ((KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1; +} + +void SamdPlatform::flashErase(uint16_t eraseBlockNum) +{ + noInterrupts(); + + eraseRow((void *)(_MemoryStart + eraseBlockNum * _rowSize)); + // flash_range_erase(KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + + interrupts(); +} + +void SamdPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data) +{ + noInterrupts(); + + write((void *)(_MemoryStart + pageNumber * _pageSize), data, _pageSize); + // flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize()); + + interrupts(); +} + +void SamdPlatform::writeBufferedEraseBlock() +{ + if (_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) + { + noInterrupts(); + + eraseRow((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize)); + write((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize), _eraseblockBuffer, _rowSize); + // flash_range_erase(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + // flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize()); + + interrupts(); + + _bufferedEraseblockDirty = false; + } +} + +uint32_t SamdPlatform::getRowAddr(uint32_t flasAddr) +{ + return flasAddr & ~(_rowSize - 1); +} + +void SamdPlatform::write(const volatile void *flash_ptr, const void *data, uint32_t size) +{ + // Calculate data boundaries + size = (size + 3) / 4; + volatile uint32_t *src_addr = (volatile uint32_t *)data; + volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr; + // volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr; + // const uint8_t *src_addr = (uint8_t *)data; + + // Disable automatic page write + NVMCTRL->CTRLB.bit.MANW = 1; + + // Do writes in pages + while (size) + { + // Execute "PBC" Page Buffer Clear + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC; + while (NVMCTRL->INTFLAG.bit.READY == 0) + { + } + + // Fill page buffer + uint32_t i; + for (i = 0; i < (_pageSize / 4) && size; i++) + { + *dst_addr = *src_addr; + src_addr++; + dst_addr++; + size--; + } + + // Execute "WP" Write Page + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP; + while (NVMCTRL->INTFLAG.bit.READY == 0) + { + } + } +} + +void SamdPlatform::erase(const volatile void *flash_ptr, uint32_t size) +{ + const uint8_t *ptr = (const uint8_t *)flash_ptr; + while (size > _rowSize) + { + eraseRow(ptr); + ptr += _rowSize; + size -= _rowSize; + } + eraseRow(ptr); +} + +void SamdPlatform::eraseRow(const volatile void *flash_ptr) +{ + NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr) / 2; + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; + while (!NVMCTRL->INTFLAG.bit.READY) + { + } +} + +#endif #endif - - diff --git a/src/samd_platform.h b/src/samd_platform.h index dd1765d..fb22dc6 100644 --- a/src/samd_platform.h +++ b/src/samd_platform.h @@ -4,6 +4,8 @@ #ifdef ARDUINO_ARCH_SAMD +#define PAGES_PER_ROW 4 + class SamdPlatform : public ArduinoPlatform { public: @@ -14,8 +16,40 @@ public: uint32_t uniqueSerialNumber() override; void restart(); +#ifdef USE_SAMD_EEPROM_EMULATION uint8_t* getEepromBuffer(uint16_t size); void commitToEeprom(); +#else + // size of one EraseBlock in pages + virtual size_t flashEraseBlockSize(); + // size of one flash page in bytes + virtual size_t flashPageSize(); + // start of user flash aligned to start of an erase block + virtual uint8_t* userFlashStart(); + // size of the user flash in EraseBlocks + virtual size_t userFlashSizeEraseBlocks(); + // relativ to userFlashStart + virtual void flashErase(uint16_t eraseBlockNum); + // write a single page to flash (pageNumber relative to userFashStart + virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); + + // writes _eraseblockBuffer to flash - overrides Plattform::writeBufferedEraseBlock() for performance optimization only + void writeBufferedEraseBlock(); + +private: + void init(); + uint32_t _MemoryEnd = 0; + uint32_t _MemoryStart = 0; + uint32_t _pageSize; + uint32_t _rowSize; + uint32_t _pageCnt; + + uint32_t getRowAddr(uint32_t flasAddr); + void write(const volatile void* flash_ptr, const void* data, uint32_t size); + void erase(const volatile void* flash_ptr, uint32_t size); + void eraseRow(const volatile void* flash_ptr); + +#endif }; #endif