Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">○</td>
<td align="center"></td>
<td align="center"></td>
<td align="center">○</td>
<td align="center">○</td>
<td align="center">○</td>
Expand Down
92 changes: 92 additions & 0 deletions examples/nucleo_h723zg/flash/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2026, Henrik Hose
*
* 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 <cstring>
#include <modm/board.hpp>

#undef MODM_LOG_LEVEL
#define MODM_LOG_LEVEL modm::log::INFO

extern "C" const uint32_t __flash_reserved_start[];

static Flash::MaxWordType
makeFlashWord(const uint32_t *data)
{
Flash::MaxWordType word;
for (size_t ii = 0; ii < Flash::FlashWordWords; ++ii) {
word.data[ii] = data[ii];
}
return word;
}

int
main()
{
Board::initialize();

MODM_LOG_INFO << "STM32H7 Flash write/read example" << modm::endl;

constexpr uint32_t dummy_data[] = {
0xDEADBEEF, 0xCAFEBABE, 0x12345678, 0xAABBCCDD,
0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00,
0x0BADCAFE, 0xC001D00D, 0xFEEDFACE, 0x8BADF00D,
0x13579BDF, 0x2468ACE0, 0x10203040, 0x50607080,
};
constexpr size_t num_values = std::size(dummy_data);
static_assert(num_values % Flash::FlashWordWords == 0);
constexpr size_t num_flash_words = num_values / Flash::FlashWordWords;

const uintptr_t base = reinterpret_cast<uintptr_t>(__flash_reserved_start);
const uint8_t sector = Flash::getPage(base - Flash::OriginAddr);

MODM_LOG_INFO << "Reserved sector: " << sector << " base: 0x" << modm::hex << base
<< modm::endl;

if (not Flash::unlock())
{
MODM_LOG_ERROR << "Flash unlock failed!" << modm::endl;
while (1);
}

// Erase the reserved sector before writing
if (const uint32_t err = Flash::erase(sector); err != 0)
{
MODM_LOG_ERROR << "Erase error: 0x" << modm::hex << err << modm::endl;
while (1);
}
MODM_LOG_INFO << "Sector erased." << modm::endl;

// Write dummy data
uint32_t err{0};
for (size_t i = 0; i < num_flash_words; i++)
err |= Flash::program(base + i * Flash::FlashWordSize,
makeFlashWord(&dummy_data[i * Flash::FlashWordWords]));

if (err != 0)
{
MODM_LOG_ERROR << "Program error: 0x" << modm::hex << err << modm::endl;
while (1);
}
MODM_LOG_INFO << "Write complete. Verifying..." << modm::endl;

// Read back and verify
for (size_t i = 0; i < num_values; i++)
{
uint32_t readback;
memcpy(&readback, reinterpret_cast<const void *>(base + i * sizeof(uint32_t)),
sizeof(uint32_t));
const bool ok = (readback == dummy_data[i]);
MODM_LOG_INFO << "[" << i << "] wrote 0x" << modm::hex << dummy_data[i] << " read 0x"
<< readback << (ok ? " OK" : " MISMATCH") << modm::endl;
}

while (1);
return 0;
}
11 changes: 11 additions & 0 deletions examples/nucleo_h723zg/flash/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<library>
<extends>modm:nucleo-h723zg</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_h723zg/flash</option>
<option name="modm:platform:cortex-m:linkerscript.flash_reserved">1024*128</option>
</options>
<modules>
<module>modm:platform:flash</module>
<module>modm:build:scons</module>
</modules>
</library>
92 changes: 92 additions & 0 deletions examples/nucleo_h743zi/flash/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2026, Henrik Hose
*
* 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 <cstring>
#include <modm/board.hpp>

#undef MODM_LOG_LEVEL
#define MODM_LOG_LEVEL modm::log::INFO

extern "C" const uint32_t __flash_reserved_start[];

static Flash::MaxWordType
makeFlashWord(const uint32_t *data)
{
Flash::MaxWordType word;
for (size_t ii = 0; ii < Flash::FlashWordWords; ++ii) {
word.data[ii] = data[ii];
}
return word;
}

int
main()
{
Board::initialize();

MODM_LOG_INFO << "STM32H7 Flash write/read example" << modm::endl;

constexpr uint32_t dummy_data[] = {
0xDEADBEEF, 0xCAFEBABE, 0x12345678, 0xAABBCCDD,
0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00,
0x0BADCAFE, 0xC001D00D, 0xFEEDFACE, 0x8BADF00D,
0x13579BDF, 0x2468ACE0, 0x10203040, 0x50607080,
};
constexpr size_t num_values = std::size(dummy_data);
static_assert(num_values % Flash::FlashWordWords == 0);
constexpr size_t num_flash_words = num_values / Flash::FlashWordWords;

const uintptr_t base = reinterpret_cast<uintptr_t>(__flash_reserved_start);
const uint8_t sector = Flash::getPage(base - Flash::OriginAddr);

MODM_LOG_INFO << "Reserved sector: " << sector << " base: 0x" << modm::hex << base
<< modm::endl;

if (not Flash::unlock())
{
MODM_LOG_ERROR << "Flash unlock failed!" << modm::endl;
while (1);
}

// Erase the reserved sector before writing
if (const uint32_t err = Flash::erase(sector); err != 0)
{
MODM_LOG_ERROR << "Erase error: 0x" << modm::hex << err << modm::endl;
while (1);
}
MODM_LOG_INFO << "Sector erased." << modm::endl;

// Write dummy data
uint32_t err{0};
for (size_t i = 0; i < num_flash_words; i++)
err |= Flash::program(base + i * Flash::FlashWordSize,
makeFlashWord(&dummy_data[i * Flash::FlashWordWords]));

if (err != 0)
{
MODM_LOG_ERROR << "Program error: 0x" << modm::hex << err << modm::endl;
while (1);
}
MODM_LOG_INFO << "Write complete. Verifying..." << modm::endl;

// Read back and verify
for (size_t i = 0; i < num_values; i++)
{
uint32_t readback;
memcpy(&readback, reinterpret_cast<const void *>(base + i * sizeof(uint32_t)),
sizeof(uint32_t));
const bool ok = (readback == dummy_data[i]);
MODM_LOG_INFO << "[" << i << "] wrote 0x" << modm::hex << dummy_data[i] << " read 0x"
<< readback << (ok ? " OK" : " MISMATCH") << modm::endl;
}

while (1);
return 0;
}
11 changes: 11 additions & 0 deletions examples/nucleo_h743zi/flash/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<library>
<extends>modm:nucleo-h743zi</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_h743zi/flash</option>
<option name="modm:platform:cortex-m:linkerscript.flash_reserved">1024*128</option>
</options>
<modules>
<module>modm:platform:flash</module>
<module>modm:build:scons</module>
</modules>
</library>
32 changes: 17 additions & 15 deletions src/modm/platform/core/cortex/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -87,27 +87,28 @@ def common_memories(env):
memories = listify(device.get_driver("core")["memory"])

# Convert from string to int and add offsets
flash_size = env.get(":platform:core:boot2_size", 0)
if flash_size:
if (flash_size := env.get(":platform:core:boot2_size", 0)):
memories.append({
"name": "flash",
"access": "rx",
"start": "0x10000000",
"size": hex(flash_size)})
flash_offset = env.get(":platform:cortex-m:linkerscript.flash_offset", 0)
flash_reserved = env.get(":platform:cortex-m:linkerscript.flash_reserved", 0)
# sort by start address, so that we can find the "first" memory sections with a single pass
for m in memories:
if m["name"] in ["flash", "flash1"]:
m["start"] = int(m["start"], 0) + flash_offset
m["size"] = int(m["size"], 0) - flash_offset
elif m["name"] in ["flash2", "flash"]:
m["start"] = int(m["start"], 0)
m["size"] = int(m["size"], 0) - flash_reserved
else:
m["start"] = int(m["start"], 0)
m["size"] = int(m["size"], 0)

m["start"] = int(m["start"], 0)
m["size"] = int(m["size"], 0)
memories.sort(key=lambda m: m["start"])
physical_memories = list(m for m in memories if "alias" not in m)
# Flash offset is counted from the start of flash, which could be flash or flash1
if (flash_offset := env.get(":platform:cortex-m:linkerscript.flash_offset", 0)):
offset_flash = next(m for name in ["flash", "flash1", "flash2"] for m in memories if m["name"] == name)
offset_flash["start"] = offset_flash["start"] + flash_offset
offset_flash["size"] = offset_flash["size"] - flash_offset
# Reserved flash is counted from the end of flash, which could be flash2, flash1 or flash
if (flash_reserved := env.get(":platform:cortex-m:linkerscript.flash_reserved", 0)):
reserved_flash = next(m for name in ["flash2", "flash1", "flash"] for m in memories if m["name"] == name)
reserved_flash["size"] = reserved_flash["size"] - flash_reserved

# Find all continuous memory sections
cont = []
index = 0
Expand Down Expand Up @@ -138,12 +139,13 @@ def common_memories(env):
memories.append({
"name": "flash_reserved",
"access": "rx",
"start": next(m["start"] + m["size"] for m in memories if m["name"] in ["flash2", "flash1", "flash"]),
"start": reserved_flash["start"] + reserved_flash["size"],
"size": flash_reserved})
memories.sort(key=lambda m: m["start"])

properties = {
"memories": memories,
"physical_memories": physical_memories,
"regions": [m["name"] for m in memories],
"ram_regions": ram_regions,
"cont_flash_regions": cont_flash_regions,
Expand Down
Loading
Loading