From f0e5a2a41cf4b81071bc69f4845ba8d695a247ad Mon Sep 17 00:00:00 2001 From: Henrik Hose Date: Wed, 20 Mar 2024 14:25:51 +0100 Subject: [PATCH 1/4] [cortex-m] added flash_reserved section Co-authored-by: Niklas Hauser --- src/modm/platform/core/cortex/module.lb | 38 ++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/modm/platform/core/cortex/module.lb b/src/modm/platform/core/cortex/module.lb index f00af09659..a30092a63a 100644 --- a/src/modm/platform/core/cortex/module.lb +++ b/src/modm/platform/core/cortex/module.lb @@ -91,14 +91,23 @@ def common_memories(env): "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) for m in memories: if m["name"] == "flash": m["start"] = int(m["start"], 0) + flash_offset - m["size"] = int(m["size"], 0) - flash_offset + m["size"] = int(m["size"], 0) - flash_offset - flash_reserved else: m["start"] = int(m["start"], 0) m["size"] = int(m["size"], 0) + # Add reserved FLASH section to memories + if flash_reserved: + memories.append({ + "name": "flash_reserved", + "access": "rx", + "start": next(m["start"] + m["size"] for m in memories if m["name"] == "flash"), + "size": flash_reserved}) + # Find all continuous internal SRAM sections cont = [] index = 0 @@ -229,7 +238,7 @@ def prepare(module, options): default=default_location)) # Find the size of the flash memory - flash_size = next((int(x['size']) for x in memories if x['name'] == 'flash'), 16*1024*1024) + flash_size = next((int(x["size"]) for x in memories if x["name"] == "flash"), 16*1024*1024) module.add_option( NumericOption( name="linkerscript.flash_offset", @@ -238,6 +247,14 @@ def prepare(module, options): maximum=hex(flash_size), default=0)) + module.add_option( + NumericOption( + name="linkerscript.flash_reserved", + description="Add a reserved section at the end of the flash.", + minimum=0, + maximum=hex(flash_size), + default=0)) + module.add_option( PathOption( name="linkerscript.override", @@ -290,8 +307,21 @@ def validate(env): raise ValidateException("Invalid flash offset in option '{}' (value=0x{:X}). " "The offset needs to be {} bit aligned." .format(flash_offset_option_name, - flash_offset, - bit_alignment)) + flash_offset, bit_alignment)) + + device = env[":target"] + memories = listify(device.get_driver("core")["memory"]) + flash_size = next((int(x["size"]) for x in memories if x["name"] == "flash"), 16*1024*1024) + flash_reserved_option_name = "modm:platform:cortex-m:linkerscript.flash_reserved" + flash_reserved = env[flash_reserved_option_name] + if flash_reserved != 0: + if flash_reserved + flash_offset > flash_size: + raise ValidateException("Invalid reserved flash size in option '{}'. " + "The 'linkerscript.flash_offset' of {} at the beginning " + "of FLASH is growing into the 'linkerscript.flash_reserved' " + "of {} at the end of FLASH due to flash size {}." + .format(flash_reserved_option_name, flash_offset, + flash_reserved, flash_size)) def build(env): From cde35ed61487d55dccef24661899c6ed1428d66b Mon Sep 17 00:00:00 2001 From: Henrik Hose Date: Mon, 25 Mar 2024 23:18:34 +0100 Subject: [PATCH 2/4] [ext] added nlohmann json submodule --- .gitmodules | 3 +++ ext/nlohmann/json | 1 + ext/nlohmann/json.lb | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 160000 ext/nlohmann/json create mode 100644 ext/nlohmann/json.lb diff --git a/.gitmodules b/.gitmodules index 2e18aa0512..7882a65ec6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,3 +46,6 @@ [submodule "ext/arm/cmsis-dsp"] path = ext/arm/cmsis-dsp url = https://github.com/modm-ext/cmsis-dsp-partial.git +[submodule "ext/nlohmann/json"] + path = ext/nlohmann/json + url = git@github.com:modm-ext/json-partial.git diff --git a/ext/nlohmann/json b/ext/nlohmann/json new file mode 160000 index 0000000000..96557860a5 --- /dev/null +++ b/ext/nlohmann/json @@ -0,0 +1 @@ +Subproject commit 96557860a57b2c4166d6041a693721242fb40e20 diff --git a/ext/nlohmann/json.lb b/ext/nlohmann/json.lb new file mode 100644 index 0000000000..81344286a7 --- /dev/null +++ b/ext/nlohmann/json.lb @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2024, 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/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":nlohmann-json" + module.description = """ +# Header-only JSON library + +The nlohmann JSON C++ library + +- https://json.nlohmann.me/ +- https://github.com/nlohmann/json + +""" + +def prepare(module, options): + if options[":target"].identifier.platform != "hosted": + module.depends(":platform:heap") + + return True + +def build(env): + env.collect(":build:path.include", "modm/ext/nlohmann-json") + env.outbasepath = "modm/ext/nlohmann-json" + + env.copy("json/single_include/nlohmann/json.hpp", dest="json.hpp") + env.copy("json/single_include/nlohmann/json_fwd.hpp", dest="json_fwd.hpp") From 4c79529ec2a3007295c11565305dcbe6b9c36bc9 Mon Sep 17 00:00:00 2001 From: Henrik Hose Date: Tue, 12 Mar 2024 17:42:14 +0100 Subject: [PATCH 3/4] [ext] example for json usage added --- examples/nucleo_g474re/flash_json/main.cpp | 194 ++++++++++++++++++ examples/nucleo_g474re/flash_json/project.xml | 14 ++ 2 files changed, 208 insertions(+) create mode 100644 examples/nucleo_g474re/flash_json/main.cpp create mode 100644 examples/nucleo_g474re/flash_json/project.xml diff --git a/examples/nucleo_g474re/flash_json/main.cpp b/examples/nucleo_g474re/flash_json/main.cpp new file mode 100644 index 0000000000..8c85b90a02 --- /dev/null +++ b/examples/nucleo_g474re/flash_json/main.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2024, 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 +#include +#include + +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::INFO + +// ---------------------------------------------------------------------------- + +extern "C" const uint32_t __flash_reserved_start[]; + +using json = nlohmann::json; + +constexpr auto max_flash_pages{256}; + +namespace data +{ +struct person +{ + std::string name; + float height; + int age; + auto + operator<=>(const person&) const = default; +}; + +void +to_json(json& j, const person& p) +{ + j = json{{"name", p.name}, {"height", p.height}, {"age", p.age}}; +} + +void +from_json(const json& j, person& p) +{ + j.at("name").get_to(p.name); + j.at("height").get_to(p.height); + j.at("age").get_to(p.age); +} +} // namespace data + +int +main() +{ + Board::initialize(); + + MODM_LOG_INFO << "\n\n+++++++++++++++++++++++++++" << modm::endl; + MODM_LOG_INFO << "++ NLOHMANN JSON example ++" << modm::endl; + MODM_LOG_INFO << "+++++++++++++++++++++++++++\n" << modm::endl; + + data::person alice_struct = {"Alice", 1.85, 80}; + json alice_json = alice_struct; + + // basic JSON character string usage + { + auto alice_json_string = alice_json.dump(); + + // imagine that we transmit this string over some interface + MODM_LOG_INFO << "JSON from struct is: " << alice_json_string.c_str() << modm::endl; + + // and now we can retrieve the struct from the json object + json retrieved_alice_json = json::parse(alice_json_string); + + // JSON is fairly self-annotated, so we might not even need the original struct + MODM_LOG_INFO << "\nRetrieved from string JSON has fields ... "; + for (auto& [key, value] : retrieved_alice_json.items()) + { + MODM_LOG_INFO << key.c_str() << ", "; + } + MODM_LOG_INFO << modm::endl; + MODM_LOG_INFO.flush(); + + // check if retrieved struct is same + auto retrieved_alice_struct = retrieved_alice_json.template get(); + if (retrieved_alice_struct == alice_struct) + { + MODM_LOG_INFO << "\nRetrieved from string and original struct are identical" + << modm::endl; + } + MODM_LOG_INFO.flush(); + } + + // basic binary JSON usage + { + // convert to BSON + auto alice_binary = json::to_bson(alice_json); + + MODM_LOG_INFO << "\nBinary JSON (BSON): "; + for (const auto& byte : alice_binary) { MODM_LOG_INFO.printf(" 0x%02x", byte); } + MODM_LOG_INFO << modm::endl; + + // put BSON in reserved flash + uint32_t err{0}; + const size_t page_start = + Flash::getPage(reinterpret_cast(&__flash_reserved_start)); + const size_t num_bytes = alice_binary.size(); + const size_t flash_page_size = Flash::getSize(page_start); + const size_t end_page = page_start + (num_bytes + flash_page_size - 1) / flash_page_size; + if (end_page >= max_flash_pages) + { + MODM_LOG_ERROR << "\nRequested flash end page exceeds flash [" << page_start << ", " + << end_page << ")" << modm::endl; + MODM_LOG_ERROR.flush(); + while (1) + ; + } + + // erase the pages before programming + MODM_LOG_INFO << "\nErasing flash sectors [" << page_start << ", " << end_page << ")" + << modm::endl; + MODM_LOG_INFO.flush(); + if (not Flash::unlock()) + { + MODM_LOG_ERROR << "Flash unlock failed!" << modm::endl; + while (1) + ; + } + for (size_t page{page_start}; page < end_page; page++) err |= Flash::erase(page); + if (err != 0) + { + MODM_LOG_ERROR << "\nThere was an error while erasing flash!" << modm::endl; + MODM_LOG_ERROR.flush(); + while (1) + ; + } + + // pad the data with zeros to fit flash words + size_t unpadded_size{alice_binary.size()}; + size_t word_size{sizeof(Flash::MaxWordType)}; + if (auto padding = (word_size - unpadded_size % word_size) % word_size; padding > 0) + { + alice_binary.resize(alice_binary.size() + padding, 0); + } + + // now, write the padded data + MODM_LOG_INFO << "\nWriting, word size: " << word_size + << ", num of bytes (payload): " << unpadded_size + << ", num of bytes (padded): " << alice_binary.size() << "... "; + + const auto flash_write_base_addr{reinterpret_cast(Flash::getAddr(page_start))}; + for (size_t ii = 0; ii < alice_binary.size(); ii += sizeof(Flash::MaxWordType)) + { + Flash::MaxWordType outdata; + memcpy(&outdata, &alice_binary[ii], sizeof(Flash::MaxWordType)); + err |= Flash::program(flash_write_base_addr + ii, outdata); + } + + if (err != 0) + { + MODM_LOG_ERROR << "\nThere was an error while programming flash!" << modm::endl; + MODM_LOG_ERROR.flush(); + while (1) + ; + } + MODM_LOG_INFO << "Writing complete! " << modm::endl; + MODM_LOG_INFO.flush(); + + // we can now read BSON back from flash, first 4 bytes entry is the size + uint32_t read_len; + memcpy(&read_len, Flash::getAddr(page_start), sizeof(read_len)); + + // read the actual data + MODM_LOG_INFO << "\nReading BSON of size " << read_len << " from flash: "; + auto raw_bytes = + std::vector(Flash::getAddr(page_start), Flash::getAddr(page_start) + read_len); + for (const auto& byte : raw_bytes) { MODM_LOG_INFO.printf(" 0x%02x", byte); } + MODM_LOG_INFO << modm::endl; + MODM_LOG_INFO.flush(); + + // reconstruct the data + auto retrieved_alice_json = json::from_bson(raw_bytes); + auto retrieved_alice_struct = retrieved_alice_json.template get(); + if (retrieved_alice_struct == alice_struct) + { + MODM_LOG_INFO << "\nRetrieved from flash and original struct are identical" + << modm::endl; + MODM_LOG_INFO.flush(); + } + } + + while (1) + ; + return 0; +} \ No newline at end of file diff --git a/examples/nucleo_g474re/flash_json/project.xml b/examples/nucleo_g474re/flash_json/project.xml new file mode 100644 index 0000000000..85b116faec --- /dev/null +++ b/examples/nucleo_g474re/flash_json/project.xml @@ -0,0 +1,14 @@ + + modm:nucleo-g474re + + + + + + modm:debug + modm:platform:gpio + modm:platform:flash + modm:nlohmann-json + modm:build:scons + + \ No newline at end of file From 71becdc4035d01bf07b3d8043884fa8815d55942 Mon Sep 17 00:00:00 2001 From: Henrik Hose Date: Sat, 30 Mar 2024 16:12:40 +0100 Subject: [PATCH 4/4] [flash] STM32G4 flash erase dual bank BKER bit Co-authored-by: Niklas Hauser --- src/modm/platform/flash/stm32/flash.cpp.in | 14 +++++++++++++- src/modm/platform/flash/stm32/module.lb | 20 ++++++++++++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/modm/platform/flash/stm32/flash.cpp.in b/src/modm/platform/flash/stm32/flash.cpp.in index 1cacf2e493..98192cae7f 100644 --- a/src/modm/platform/flash/stm32/flash.cpp.in +++ b/src/modm/platform/flash/stm32/flash.cpp.in @@ -91,9 +91,21 @@ Flash::erase(uint8_t index) FLASH->CR = FLASH_CR_STRT | FLASH_CR_SER | uint32_t(size) | ((index << FLASH_CR_SNB_Pos) & FLASH_CR_SNB_Msk); %% else -%% if family in ["g0","g4"] +%% if family in ["g0", "g4"] +%% if dual_bank + uint32_t cr = FLASH_CR_STRT | FLASH_CR_PER; + uint32_t page = index; + if (FLASH->OPTR & {{dual_bank}} and index >= (Size/2 >> 11)) + { + // second bank index starts back at zero again + page -= (Size/2 >> 11); + cr |= FLASH_CR_BKER; + } + FLASH->CR = cr | ((page << FLASH_CR_PNB_Pos) & FLASH_CR_PNB_Msk); +%% else FLASH->CR = FLASH_CR_STRT | FLASH_CR_PER | ((index << FLASH_CR_PNB_Pos) & FLASH_CR_PNB_Msk); +%% endif %% else FLASH->CR &= ~FLASH_CR_STRT; FLASH->CR |= FLASH_CR_PER; diff --git a/src/modm/platform/flash/stm32/module.lb b/src/modm/platform/flash/stm32/module.lb index 8cc3db884e..400f14b6ca 100644 --- a/src/modm/platform/flash/stm32/module.lb +++ b/src/modm/platform/flash/stm32/module.lb @@ -31,26 +31,29 @@ def build(env): memories = listify(env[":target"].get_driver("core")["memory"]) flash = next(filter(lambda m: m["name"] == "flash", memories)) + dual_bank = False family = target.family + busy_bit = "FLASH_SR_BSY" + ftype = "page" if target.family in ["f4"]: block_shift = 17 ftype = "sector" - busy_bit = "FLASH_SR_BSY" elif target.family in ["f1"]: if target.name in ["05", "07"] or int(flash["size"]) >= 262144: block_shift = 11 else: block_shift = 10 - ftype = "page" - busy_bit = "FLASH_SR_BSY" elif target.family in ["g0"]: - block_shift = 11 - ftype = "page" busy_bit = "FLASH_SR_BSY1" + block_shift = 11 + if target.name[0] in ["b", "c"]: + dual_bank = "FLASH_OPTR_DUAL_BANK" elif target.family in ["g4"]: block_shift = 11 - ftype = "page" - busy_bit = "FLASH_SR_BSY" + # Only Cat3 devices have dual bank, so g47x or g48x + if target.name[0] in ["7", "8"]: + dual_bank = "FLASH_OPTR_DBANK" + block_shift = "(FLASH->OPTR & FLASH_OPTR_DBANK ? 11 : 12)" env.substitutions = { "start": int(flash["start"], 16), @@ -59,7 +62,8 @@ def build(env): "shift": block_shift, "has_sectors": ftype == "sector", "busy_bit": busy_bit, - "family": family + "family": family, + "dual_bank": dual_bank } env.outbasepath = "modm/src/modm/platform/flash" env.template("flash.hpp.in")