diff --git a/examples/nucleo_f446re/flash/main.cpp b/examples/nucleo_f446re/flash/main.cpp new file mode 100644 index 0000000000..ba61e5e2b5 --- /dev/null +++ b/examples/nucleo_f446re/flash/main.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +using namespace std::chrono_literals; + +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::INFO + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + + MODM_LOG_INFO << "\n\nReboot\n"; + if (not Flash::unlock()) { + MODM_LOG_INFO << "Flash unlock failed!" << modm::endl; + } + + for (uintptr_t offset{0}, sector{255}; offset < Flash::Size; offset += 1) + { + const uint8_t nsector = Flash::getSector(offset); + if (sector != nsector) { + MODM_LOG_INFO << "Sector " << nsector << " found at boundary " << + (Flash::Origin + offset) << modm::endl; + sector = nsector; + } + } + + { + uint32_t err{0}; + const uint8_t sector_start = Flash::getSector(Flash::Size/2); + const uint8_t sector_end = Flash::getSector(Flash::Size); + MODM_LOG_INFO << "Erasing sectors [" << sector_start << ", " << sector_end << ")" << modm::endl; + MODM_LOG_INFO.flush(); + modm::delay(1s); + + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + + for (uint8_t sector{sector_start}; sector < sector_end; sector++) + err |= Flash::erase(sector); + + const auto diff = (modm::PreciseClock::now() - start); + MODM_LOG_INFO << "Erasing done in " << diff << " with errors: " << err << modm::endl; + MODM_LOG_INFO << "Erasing with " << (Flash::Size/2 / (diff.count() >> 10) ) << "kiB/s" << modm::endl; + } + + { + uint32_t err{0}; + MODM_LOG_INFO.flush(); + modm::delay(1s); + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + for (uint32_t dst_addr = Flash::OriginAddr + Flash::Size/2, src_addr = Flash::OriginAddr; + src_addr < (Flash::OriginAddr + Flash::Size/2); + src_addr += sizeof(uint32_t), dst_addr += sizeof(uint32_t)) + { + err |= Flash::program(dst_addr, *(uint32_t*)src_addr); + } + + const auto diff = (modm::PreciseClock::now() - start); + MODM_LOG_INFO << "Programming done in " << diff << " with errors: " << err << modm::endl; + MODM_LOG_INFO << "Programming with " << (Flash::Size/2 / (diff.count() >> 10) ) << "kiB/s" << modm::endl; + } + + while(1) ; + return 0; +} diff --git a/examples/nucleo_f446re/flash/project.xml b/examples/nucleo_f446re/flash/project.xml new file mode 100644 index 0000000000..69f785966e --- /dev/null +++ b/examples/nucleo_f446re/flash/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-f446re + + + + + modm:platform:gpio + modm:platform:flash + modm:processing:timer + modm:build:scons + + diff --git a/examples/nucleo_g071rb/flash/main.cpp b/examples/nucleo_g071rb/flash/main.cpp new file mode 100644 index 0000000000..b5a316d431 --- /dev/null +++ b/examples/nucleo_g071rb/flash/main.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::INFO + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + + Flash::unlock(); + + { + uint32_t err{0}; + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + + for (uint8_t page{32}; page < 64u; page++) + err |= Flash::erase(page); + + MODM_LOG_INFO << "Erasing done in " << + (modm::PreciseClock::now() - start) << + " with errors: " << err << modm::endl; + } + + + { + uint32_t err{0}; + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + for (uint32_t dst_addr = Flash::OriginAddr + Flash::Size/2, + src_addr = Flash::OriginAddr; + src_addr < (Flash::OriginAddr + Flash::Size/2); + src_addr += sizeof(uint64_t), dst_addr += sizeof(uint64_t)) + { + err |= Flash::program(dst_addr, *(uint64_t*)src_addr); + } + + MODM_LOG_INFO << "Programming done in " << + (modm::PreciseClock::now() - start) << + " with errors: " << err << modm::endl; + } + + while(1) ; + return 0; +} diff --git a/examples/nucleo_g071rb/flash/project.xml b/examples/nucleo_g071rb/flash/project.xml new file mode 100644 index 0000000000..d1d5f56b79 --- /dev/null +++ b/examples/nucleo_g071rb/flash/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-g071rb + + + + + modm:platform:gpio + modm:platform:flash + modm:processing:timer + modm:build:scons + + diff --git a/src/modm/platform/flash/stm32/flash.cpp.in b/src/modm/platform/flash/stm32/flash.cpp.in new file mode 100644 index 0000000000..ff62acaa9e --- /dev/null +++ b/src/modm/platform/flash/stm32/flash.cpp.in @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "flash.hpp" + +static constexpr uint32_t FLASH_SR_ERR = 0xfffe; + +namespace modm::platform +{ + +bool +Flash::unlock() +{ + Flash::enable(); + if (isLocked()) + { + FLASH->KEYR = 0x45670123; + FLASH->KEYR = 0xCDEF89AB; + } + return not isLocked(); +} + +uint8_t +Flash::get{{ type | capitalize }}(uintptr_t offset) +{ + const uint8_t index = (offset >> {{ shift }}); +%% if has_sectors + uint8_t small_index{0}; + // 128kB Block 0 and 8 are subdivided into 4x16kB + 64kB + if (index == 0 or index == 8) + { + if (index == 8) small_index += 4; + // Check upper 64kB first + if (offset & 0x1'0000ul) small_index += 4; + // Otherwise check lower 16kB + else small_index += ((offset & 0xC000) >> 14); + } + else small_index += 4; + // 128kB Blocks + return index + small_index; +%% else + return index; +%% endif +} + +modm_ramcode uint32_t +%% if has_sectors +Flash::erase(uint8_t index, WordSize size) +%% else +Flash::erase(uint8_t index) +%% endif +{ + FLASH->SR = FLASH_SR_ERR; +%% if has_sectors + FLASH->CR = FLASH_CR_STRT | FLASH_CR_SER | uint32_t(size) | + ((index << FLASH_CR_SNB_Pos) & FLASH_CR_SNB_Msk); +%% else + FLASH->CR = FLASH_CR_STRT | FLASH_CR_PER | + ((index << FLASH_CR_PNB_Pos) & FLASH_CR_PNB_Msk); +%% endif + + while(isBusy()) ; + FLASH->CR = 0; + + return FLASH->SR & FLASH_SR_ERR; +} + +modm_ramcode uint32_t +%% if has_sectors +Flash::program(uintptr_t addr, uint32_t data, WordSize size) +%% else +Flash::program(uintptr_t addr, uint64_t data) +%% endif +{ + FLASH->SR = FLASH_SR_ERR; +%% if has_sectors + FLASH->CR = FLASH_CR_PG | uint32_t(size); + switch(size) + { + case WordSize::B8: + *(uint8_t *) addr = data; + break; + case WordSize::B16: + *(uint16_t *) addr = data; + break; + default: + *(uint32_t *) addr = data; + break; + } +%% else + FLASH->CR = FLASH_CR_PG; + + *(uint32_t*) addr = (uint32_t) data; + __ISB(); + *(uint32_t*)(addr+4) = (uint32_t)(data >> 32); +%% endif + + while(isBusy()) ; + FLASH->CR = 0; + + return FLASH->SR & FLASH_SR_ERR; +} + +} // namespace modm::platform diff --git a/src/modm/platform/flash/stm32/flash.hpp.in b/src/modm/platform/flash/stm32/flash.hpp.in new file mode 100644 index 0000000000..96fb323d45 --- /dev/null +++ b/src/modm/platform/flash/stm32/flash.hpp.in @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once +#include "../device.hpp" +#include +#include + +namespace modm::platform +{ + +/// @ingroup modm_platform_flash +class Flash +{ +public: + static constexpr uintptr_t OriginAddr{ 0x{{ "%0x" | format(start) }} }; + static constexpr size_t Size{ 0x{{ "%0x" | format(size) }} }; + static inline uint8_t *const Origin{(uint8_t*)OriginAddr}; + +%% if has_sectors + enum class + WordSize : uint32_t + { + B8 = 0, + B16 = FLASH_CR_PSIZE_0, + B32 = FLASH_CR_PSIZE_1, + }; +%% endif + +public: + inline static void + enable() + { +%% if not has_sectors + Rcc::enable(); +%% endif + } + + inline static void + disable() + { +%% if not has_sectors + Rcc::disable(); +%% endif + + } + + static bool + isLocked() + { return FLASH->CR & FLASH_CR_LOCK; } + + static inline bool + isBusy() +%% if has_sectors + { return FLASH->SR & FLASH_SR_BSY; } +%% else + { return FLASH->SR & FLASH_SR_BSY1; } +%% endif + + static bool + unlock(); + + static inline uint8_t + get{{ type | capitalize }}(uint8_t *ptr) + { return get{{ type | capitalize }}(ptr - Flash::Origin); } + + static uint8_t + get{{ type | capitalize }}(uintptr_t offset); + + static uint32_t +%% if has_sectors + erase(uint8_t {{ type }}, WordSize size = WordSize::B32); +%% else + erase(uint8_t {{ type }}); +%% endif + +%% if has_sectors + static inline uint32_t + program(uintptr_t addr, uint8_t data) + { return program(addr, data, WordSize::B8); } + + static inline uint32_t + program(uintptr_t addr, uint16_t data) + { return program(addr, data, WordSize::B16); } + + static uint32_t + program(uintptr_t addr, uint32_t data, WordSize size = WordSize::B32); +%% else + static uint32_t + program(uintptr_t addr, uint64_t data); +%% endif +}; + +} // namespace modm::platform diff --git a/src/modm/platform/flash/stm32/module.lb b/src/modm/platform/flash/stm32/module.lb new file mode 100644 index 0000000000..f7c266d61c --- /dev/null +++ b/src/modm/platform/flash/stm32/module.lb @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:flash" + module.description = "Flash Memory" + +def prepare(module, options): + device = options[":target"] + if device.identifier.family not in ["g0", "f4"]: + return False + if not device.has_driver("flash:stm32*"): + return False + + module.depends(":cmsis:device", ":platform:rcc", + ":architecture:register") + return True + +def build(env): + target = env[":target"].identifier + memories = listify(env[":target"].get_driver("core")["memory"]) + flash = next(filter(lambda m: m["name"] == "flash", memories)) + + if target.family in ["f4"]: + block_shift = 17 + ftype = "sector" + elif target.family in ["g0"]: + block_shift = 11 + ftype = "page" + + env.substitutions = { + "start": int(flash["start"], 16), + "size": int(flash["size"]), + "type": ftype, + "shift": block_shift, + "has_sectors": ftype == "sector", + } + env.outbasepath = "modm/src/modm/platform/flash" + env.template("flash.hpp.in") + env.template("flash.cpp.in")