Files
SyncHome/trunk/Arduino/libraries/ArduinoOTA/src/InternalStorage.cpp

223 lines
5.9 KiB
C++
Raw Normal View History

2023-03-17 11:59:21 +00:00
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
WiFi101OTA version Feb 2017
by Sandeep Mistry (Arduino)
modified for ArduinoOTA Dec 2018
by Juraj Andrassy
*/
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_NRF5)
#include <Arduino.h>
#include "InternalStorage.h"
InternalStorageClass::InternalStorageClass() :
MAX_PARTIONED_SKETCH_SIZE((MAX_FLASH - SKETCH_START_ADDRESS) / 2),
STORAGE_START_ADDRESS(SKETCH_START_ADDRESS + MAX_PARTIONED_SKETCH_SIZE)
{
pageAlignedLength = 0;
_writeIndex = 0;
_writeAddress = nullptr;
}
void InternalStorageClass::debugPrint() {
Serial.print("SKETCH_START_ADDRESS ");
Serial.println(SKETCH_START_ADDRESS);
Serial.print("PAGE_SIZE ");
Serial.println(PAGE_SIZE);
Serial.print("MAX_FLASH ");
Serial.println(MAX_FLASH);
Serial.print("MAX_PARTIONED_SKETCH_SIZE ");
Serial.println(MAX_PARTIONED_SKETCH_SIZE);
Serial.print("STORAGE_START_ADDRESS ");
Serial.println(STORAGE_START_ADDRESS);
}
extern "C" {
// these functions must be in RAM (.data) and NOT inlined
// as they erase and copy the sketch data in flash
__attribute__ ((long_call, noinline, section (".data#"))) //
void waitForReady() {
#if defined(__SAMD51__)
while (!NVMCTRL->STATUS.bit.READY);
#elif defined(ARDUINO_ARCH_SAMD)
while (!NVMCTRL->INTFLAG.bit.READY);
#elif defined(ARDUINO_ARCH_NRF5)
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
#endif
}
2024-09-24 16:54:39 +00:00
2023-03-17 11:59:21 +00:00
#if defined(__SAMD51__)
// Invalidate all CMCC cache entries if CMCC cache is enabled.
__attribute__ ((long_call, noinline, section (".data#")))
static void invalidate_CMCC_cache()
{
if (CMCC->SR.bit.CSTS) {
CMCC->CTRL.bit.CEN = 0;
while (CMCC->SR.bit.CSTS) {}
CMCC->MAINT0.bit.INVALL = 1;
CMCC->CTRL.bit.CEN = 1;
}
}
#endif
__attribute__ ((long_call, noinline, section (".data#")))
static void eraseFlash(int address, int length, int pageSize)
{
#if defined(__SAMD51__)
int rowSize = (pageSize * NVMCTRL->PARAM.bit.NVMP) / 64;
for (int i = 0; i < length; i += rowSize) {
NVMCTRL->ADDR.reg = ((uint32_t)(address + i));
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EB;
waitForReady();
invalidate_CMCC_cache();
}
#elif defined(ARDUINO_ARCH_SAMD)
int rowSize = pageSize * 4;
for (int i = 0; i < length; i += rowSize) {
NVMCTRL->ADDR.reg = ((uint32_t)(address + i)) / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
2024-09-24 16:54:39 +00:00
2023-03-17 11:59:21 +00:00
waitForReady();
}
#elif defined(ARDUINO_ARCH_NRF5)
// Enable erasing flash
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
// Erase page(s)
int end_address = address + length;
while (address < end_address) {
waitForReady();
// Erase one 1k/4k page
NRF_NVMC->ERASEPAGE = address;
address = address + pageSize;
}
// Disable erasing, enable write
waitForReady();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
waitForReady();
2024-09-24 16:54:39 +00:00
#endif
2023-03-17 11:59:21 +00:00
}
__attribute__ ((long_call, noinline, section (".data#")))
static void copyFlashAndReset(int dest, int src, int length, int pageSize)
{
volatile uint32_t* d = (volatile uint32_t*)dest;
uint32_t* s = (uint32_t*)src;
eraseFlash(dest, length, pageSize);
for (int i = 0; i < length; i += 4) {
*d++ = *s++;
waitForReady();
}
NVIC_SystemReset();
}
}
int InternalStorageClass::open(int length)
{
if (length > MAX_PARTIONED_SKETCH_SIZE)
return 0;
pageAlignedLength = ((length / PAGE_SIZE) + 1) * PAGE_SIZE; // align to page up
_writeIndex = 0;
_writeAddress = (uint32_t*)STORAGE_START_ADDRESS;
#if defined(__SAMD51__)
// Enable auto dword writes
NVMCTRL->CTRLA.bit.WMODE = NVMCTRL_CTRLA_WMODE_ADW_Val;
waitForReady();
// Disable NVMCTRL cache while writing, per SAMD51 errata
NVMCTRL->CTRLA.bit.CACHEDIS0 = 1;
NVMCTRL->CTRLA.bit.CACHEDIS1 = 1;
#elif defined(ARDUINO_ARCH_SAMD)
// Enable auto page writes
NVMCTRL->CTRLB.bit.MANW = 0;
#endif
2024-09-24 16:54:39 +00:00
#if !defined(ARDUINO_ARCH_NRF5)
// Erase all pages
2023-03-17 11:59:21 +00:00
eraseFlash(STORAGE_START_ADDRESS, pageAlignedLength, PAGE_SIZE);
2024-09-24 16:54:39 +00:00
#endif
2023-03-17 11:59:21 +00:00
return 1;
}
size_t InternalStorageClass::write(uint8_t b)
{
_addressData.u8[_writeIndex] = b;
_writeIndex++;
if (_writeIndex == 4) {
_writeIndex = 0;
2024-09-24 16:54:39 +00:00
#if defined(ARDUINO_ARCH_NRF5)
// Erase a single page if needed
if ((int)(_writeAddress) % PAGE_SIZE == 0) {
eraseFlash((int)_writeAddress, PAGE_SIZE, PAGE_SIZE);
}
#endif
2023-03-17 11:59:21 +00:00
*_writeAddress = _addressData.u32;
_writeAddress++;
waitForReady();
}
return 1;
}
void InternalStorageClass::close()
{
while ((int)_writeAddress % PAGE_SIZE) {
write(0xff);
}
2024-09-24 16:54:39 +00:00
// Re-calculate pageAlignedLength in case the actually written binary
// is smaller then the size provided in open()
pageAlignedLength = (_writeAddress - (uint32_t*)STORAGE_START_ADDRESS) * sizeof(uint32_t);
2023-03-17 11:59:21 +00:00
}
void InternalStorageClass::clear()
{
}
void InternalStorageClass::apply()
{
// disable interrupts, as vector table will be erase during flash sequence
noInterrupts();
copyFlashAndReset(SKETCH_START_ADDRESS, STORAGE_START_ADDRESS, pageAlignedLength, PAGE_SIZE);
}
long InternalStorageClass::maxSize()
{
return MAX_PARTIONED_SKETCH_SIZE;
}
InternalStorageClass InternalStorage;
#endif