Skip to content

Commit

Permalink
[flash] Add simple STM32 Flash implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
salkinium committed Jun 10, 2020
1 parent 61ced02 commit fc0679c
Show file tree
Hide file tree
Showing 7 changed files with 421 additions and 0 deletions.
77 changes: 77 additions & 0 deletions examples/nucleo_f446re/flash/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <modm/board.hpp>
#include <modm/processing.hpp>

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;
}
12 changes: 12 additions & 0 deletions examples/nucleo_f446re/flash/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<library>
<extends>modm:nucleo-f446re</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_f446re/flash</option>
</options>
<modules>
<module>modm:platform:gpio</module>
<module>modm:platform:flash</module>
<module>modm:processing:timer</module>
<module>modm:build:scons</module>
</modules>
</library>
56 changes: 56 additions & 0 deletions examples/nucleo_g071rb/flash/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <modm/board.hpp>
#include <modm/processing.hpp>

#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;
}
12 changes: 12 additions & 0 deletions examples/nucleo_g071rb/flash/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<library>
<extends>modm:nucleo-g071rb</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_g071rb/flash</option>
</options>
<modules>
<module>modm:platform:gpio</module>
<module>modm:platform:flash</module>
<module>modm:processing:timer</module>
<module>modm:build:scons</module>
</modules>
</library>
112 changes: 112 additions & 0 deletions src/modm/platform/flash/stm32/flash.cpp.in
Original file line number Diff line number Diff line change
@@ -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
102 changes: 102 additions & 0 deletions src/modm/platform/flash/stm32/flash.hpp.in
Original file line number Diff line number Diff line change
@@ -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 <modm/platform/clock/rcc.hpp>
#include <modm/architecture/interface/register.hpp>

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<Peripheral::Flash>();
%% endif
}

inline static void
disable()
{
%% if not has_sectors
Rcc::disable<Peripheral::Flash>();
%% 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
Loading

0 comments on commit fc0679c

Please sign in to comment.