Skip to content

Commit

Permalink
feat: support for internal high cycle area flash (#449)
Browse files Browse the repository at this point in the history
* Allow enable NonMaskableInt_IRQn interrupt

* Fix Flash Write and cleanup

Before this change, writing data to flash for STM32WBA/STM32H5 only considered only the first 16 bytes.
The remaining data was not processed. Fix this by iterating over an fitting MemoryRange like it is done
for the other MCU families.
This change also cleans up writing flash method calls for the different families.

* Add FlashInternalHighCycleAreaStm

* Increase flash latency by 2

Needed for the use of OTP, read-only and flash high-cycle data.
From datasheet:
"During a read access, two wait states are added in addition to the memory
wait states. These wait states are necessary to parse the data buffer."
  • Loading branch information
fabiangottstein authored Nov 12, 2024
1 parent b505ee2 commit 2705f02
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 61 deletions.
2 changes: 2 additions & 0 deletions hal_st/cortex/InterruptCortex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace hal
}
else if (irq == -13 /*HardFault_IRQn*/)
;
else if (irq == NonMaskableInt_IRQn)
;
else if (irq == PendSV_IRQn)
;
else if (irq == SysTick_IRQn)
Expand Down
2 changes: 2 additions & 0 deletions hal_st/stm32fxxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ target_sources(hal_st.stm32fxxx PRIVATE
EthernetMacStm.hpp
EthernetSmiStm.cpp
EthernetSmiStm.hpp
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:FlashInternalHighCycleAreaStm.cpp>
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:FlashInternalHighCycleAreaStm.hpp>
FlashInternalStm.cpp
FlashInternalStm.hpp
GpioStm.cpp
Expand Down
2 changes: 1 addition & 1 deletion hal_st/stm32fxxx/DefaultClockDiscoveryH573I.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ void ConfigureDefaultClockDiscoveryH573I()
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
}
2 changes: 1 addition & 1 deletion hal_st/stm32fxxx/DefaultClockNucleoH563ZI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ void ConfigureDefaultClockNucleoH563ZI()
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
}
157 changes: 157 additions & 0 deletions hal_st/stm32fxxx/FlashInternalHighCycleAreaStm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include DEVICE_HEADER
#include "hal_st/stm32fxxx/FlashInternalHighCycleAreaStm.hpp"
#include "hal_st/stm32fxxx/FlashInternalStmDetail.hpp"
#include "infra/event/EventDispatcher.hpp"
#include "infra/util/ByteRange.hpp"
#include "infra/util/MemoryRange.hpp"
#include <cstdint>

namespace
{
using ConstHalfWordRange = hal::FlashInternalHighCycleAreaStm::ConstHalfWordRange;

constexpr uint32_t fullSize{ FLASH_EDATA_SIZE };
constexpr uint32_t bankSize{ fullSize / 2 };
constexpr uint32_t bankSectorNumber{ FLASH_EDATA_SECTOR_NB };
constexpr uint32_t sectorSize{ bankSize / bankSectorNumber };
constexpr uint32_t bankSectorOffset{ FLASH_SECTOR_NB - bankSectorNumber };

template<uint32_t address>
constexpr auto ptr()
{
return reinterpret_cast<const uint16_t*>(address);
}

const ConstHalfWordRange bank1Range{ ptr<FLASH_EDATA_BASE>(), ptr<FLASH_EDATA_BASE + bankSize>() };
const ConstHalfWordRange bank2Range{ ptr<FLASH_EDATA_BASE + bankSize>(), ptr<FLASH_EDATA_BASE + fullSize>() };

ConstHalfWordRange GetBankRange(uint32_t bank)
{
switch (bank)
{
case FLASH_BANK_1:
return bank1Range;
case FLASH_BANK_2:
return bank2Range;
default:
std::abort();
}
}

void Copy(const uint16_t* first, const uint16_t* last, uint16_t* result)
{
for (; first != last; ++result, ++first)
*result = *first;
}
}

namespace hal
{
FlashInternalHighCycleAreaStm::FlashInternalHighCycleAreaStm(uint32_t bank)
: flashMemory(GetBankRange(bank))
, bank(bank)
{
FLASH_OBProgramInitTypeDef obCurrent{};
obCurrent.Banks = bank;
HAL_FLASHEx_OBGetConfig(&obCurrent);

bool updateNeeded = obCurrent.EDATASize != bankSectorNumber;
if (updateNeeded)
{
HAL_FLASH_OB_Unlock();

FLASH_OBProgramInitTypeDef obInit;
obInit.Banks = bank;
obInit.OptionType = OPTIONBYTE_EDATA;
obInit.EDATASize = bankSectorNumber;

auto result = HAL_FLASHEx_OBProgram(&obInit);
really_assert(result == HAL_OK);

HAL_FLASH_OB_Launch();

HAL_FLASH_OB_Lock();
}
}

void FlashInternalHighCycleAreaStm::ReadBuffer(infra::ByteRange buffer, uint32_t address, infra::Function<void()> onDone)
{
really_assert(buffer.size() % sizeof(uint16_t) == 0);
// address is byte-based, addressAdjusted is half-word-based
auto addressAdjusted = address / 2;
auto destination = infra::ReinterpretCastMemoryRange<uint16_t>(buffer);
// explicitely copy data uint16_t aligned and prevent gcc from (falsely) optimizing to memmove which will reinterpret as arrays of uint8_t
Copy(flashMemory.begin() + addressAdjusted, flashMemory.begin() + addressAdjusted + destination.size(), destination.begin());

infra::EventDispatcher::Instance().Schedule(onDone);
}

void FlashInternalHighCycleAreaStm::WriteBuffer(infra::ConstByteRange buffer, uint32_t address, infra::Function<void()> onDone)
{
HAL_FLASH_Unlock();

detail::AlignedWriteBuffer<uint16_t, FLASH_TYPEPROGRAM_HALFWORD_EDATA, true>(buffer, address, reinterpret_cast<uint32_t>(flashMemory.begin()));

HAL_FLASH_Lock();

infra::EventDispatcher::Instance().Schedule(onDone);
}

void FlashInternalHighCycleAreaStm::EraseSectors(uint32_t beginIndex, uint32_t endIndex, infra::Function<void()> onDone)
{
HAL_FLASH_Unlock();

uint32_t sectorError = 0;

FLASH_EraseInitTypeDef eraseInitStruct{};
eraseInitStruct.Banks = bank;
eraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
eraseInitStruct.Sector = beginIndex + bankSectorOffset;
eraseInitStruct.NbSectors = endIndex - beginIndex;

auto result = HAL_FLASHEx_Erase(&eraseInitStruct, &sectorError);
really_assert(result == HAL_OK);

HAL_FLASH_Lock();

infra::EventDispatcher::Instance().Schedule(onDone);
}

uint32_t FlashInternalHighCycleAreaStm::NumberOfSectors() const
{
return bankSectorNumber;
}

uint32_t FlashInternalHighCycleAreaStm::SizeOfSector(uint32_t sectorIndex) const
{
return sectorSize;
}

uint32_t FlashInternalHighCycleAreaStm::SectorOfAddress(uint32_t address) const
{
return address / sectorSize;
}

uint32_t FlashInternalHighCycleAreaStm::AddressOfSector(uint32_t sectorIndex) const
{
return sectorIndex * sectorSize;
}

FlashInternalHighCycleAreaStm::WithIrqHandler::WithIrqHandler(uint32_t bank)
: FlashInternalHighCycleAreaStm(bank)
, nmi{
IRQn_Type::NonMaskableInt_IRQn, []
{
// From RM0481, Section 7.3.4 FLASH read operations:
// If the application reads an OTP data or flash high-cycle data not previously written, a
// double ECC error is reported and only a word full of set bits is returned (see
// Section 7.3.9 for details). The read data (in 16 bits) is stored in FLASH_ECCDR
// register, so that the user can identify if the double ECC error is due to a virgin data or a
// real ECC error.
really_assert(__HAL_FLASH_GET_FLAG(FLASH_FLAG_ECCD) && READ_REG(FLASH->ECCDR) == 0xFFFF);

__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ECCD);
}
}
{}
}
49 changes: 49 additions & 0 deletions hal_st/stm32fxxx/FlashInternalHighCycleAreaStm.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2024 Koninklijke Philips N.V.
#ifndef HAL_FLASH_INTERNAL_HIGH_CYCLE_AREA_STM_HPP
#define HAL_FLASH_INTERNAL_HIGH_CYCLE_AREA_STM_HPP

#include DEVICE_HEADER
#include "hal/interfaces/Flash.hpp"
#include "hal_st/cortex/InterruptCortex.hpp"
#include "infra/util/MemoryRange.hpp"
#include <cstdint>

namespace hal
{
class FlashInternalHighCycleAreaStm
: public Flash
{
public:
using HalfWordRange = infra::MemoryRange<uint16_t>;
using ConstHalfWordRange = infra::MemoryRange<const uint16_t>;

class WithIrqHandler;

FlashInternalHighCycleAreaStm(uint32_t bank = FLASH_BANK_2);

void WriteBuffer(infra::ConstByteRange buffer, uint32_t address, infra::Function<void()> onDone) override;
void ReadBuffer(infra::ByteRange buffer, uint32_t address, infra::Function<void()> onDone) override;
void EraseSectors(uint32_t beginIndex, uint32_t endIndex, infra::Function<void()> onDone) override;

uint32_t NumberOfSectors() const override;
uint32_t SizeOfSector(uint32_t sectorIndex) const override;
uint32_t SectorOfAddress(uint32_t address) const override;
uint32_t AddressOfSector(uint32_t sectorIndex) const override;

private:
ConstHalfWordRange flashMemory;
uint32_t bank;
};

class FlashInternalHighCycleAreaStm::WithIrqHandler
: public FlashInternalHighCycleAreaStm
{
public:
WithIrqHandler(uint32_t bank = FLASH_BANK_2);

private:
hal::ImmediateInterruptHandler nmi;
};
}

#endif // HAL_FLASH_INTERNAL_HIGH_CYCLE_AREA_STM_HPP
71 changes: 19 additions & 52 deletions hal_st/stm32fxxx/FlashInternalStm.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "hal_st/stm32fxxx/FlashInternalStm.hpp"
#include "hal_st/stm32fxxx/FlashInternalStmDetail.hpp"
#include "infra/event/EventDispatcher.hpp"
#include "services/util/FlashAlign.hpp"
#include <cstdint>

namespace
Expand All @@ -21,6 +21,17 @@ namespace
}
}
#endif

struct uint128_t
{
#ifdef __ARM_BIG_ENDIAN
uint64_t high;
uint64_t low;
#else
uint64_t low;
uint64_t high;
#endif
};
}

namespace hal
Expand All @@ -34,28 +45,30 @@ namespace hal
{
HAL_FLASH_Unlock();

const auto flashBegin = reinterpret_cast<uint32_t>(flashMemory.begin());

#if defined(STM32WBA) || defined(STM32H5)
AlignedWriteBuffer(buffer, address);
detail::AlignedWriteBuffer<uint128_t, FLASH_TYPEPROGRAM_QUADWORD, true>(buffer, address, flashBegin);
#elif defined(STM32WB) || defined(STM32G4) || defined(STM32G0)
AlignedWriteBuffer<uint64_t, FLASH_TYPEPROGRAM_DOUBLEWORD>(buffer, address);
detail::AlignedWriteBuffer<uint64_t, FLASH_TYPEPROGRAM_DOUBLEWORD, false>(buffer, address, flashBegin);
#else
uint32_t word;
while (buffer.size() >= sizeof(word) && ((address & (sizeof(word) - 1)) == 0))
{
std::copy(buffer.begin(), buffer.begin() + sizeof(word), reinterpret_cast<uint8_t*>(&word));
auto result = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, reinterpret_cast<uint32_t>(flashMemory.begin() + address), word);
auto result = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, flashBegin + address, word);
really_assert(result == HAL_OK);
address += sizeof(word);
buffer.pop_front(sizeof(word));
}
#endif

#if defined(STM32F0) || defined(STM32F3)
AlignedWriteBuffer<uint16_t, FLASH_TYPEPROGRAM_HALFWORD>(buffer, address);
detail::AlignedWriteBuffer<uint16_t, FLASH_TYPEPROGRAM_HALFWORD, false>(buffer, address);
#elif !defined(STM32WB) && !defined(STM32G4) && !defined(STM32G0) && !defined(STM32WBA) && !defined(STM32H5)
for (uint8_t byte : buffer)
{
auto result = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, reinterpret_cast<uint32_t>(flashMemory.begin() + address), byte);
auto result = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, flashBegin + address, byte);
really_assert(result == HAL_OK);
++address;
}
Expand Down Expand Up @@ -118,52 +131,6 @@ namespace hal
infra::EventDispatcher::Instance().Schedule(onDone);
}

template<typename alignment, uint32_t flashType>
void FlashInternalStmBase::AlignedWriteBuffer(infra::ConstByteRange buffer, uint32_t address)
{
services::FlashAlign::WithAlignment<sizeof(alignment)> flashAlign;
flashAlign.Align(address, buffer);

services::FlashAlign::Chunk* chunk = flashAlign.First();
while (chunk != nullptr)
{
really_assert(chunk->data.size() % sizeof(alignment) == 0);
auto fullAddress = reinterpret_cast<uint32_t>(flashMemory.begin() + chunk->alignedAddress);

for (alignment data : infra::ReinterpretCastMemoryRange<const alignment>(chunk->data))
{
auto result = HAL_FLASH_Program(flashType, fullAddress, data);
really_assert(result == HAL_OK);
fullAddress += sizeof(alignment);
}
chunk = flashAlign.Next();
}
}

#if defined(STM32WBA) || defined(STM32H5)
const uint8_t alignment = sizeof(uint64_t) * 2;

void FlashInternalStmBase::AlignedWriteBuffer(infra::ConstByteRange buffer, uint32_t address)
{
services::FlashAlign::WithAlignment<alignment> flashAlign;
flashAlign.Align(address, buffer);

services::FlashAlign::Chunk* chunk = flashAlign.First();
auto dataSize = chunk->data.size();
while (chunk != nullptr)
{
really_assert(chunk->data.size() % sizeof(alignment) == 0);
auto fullAddress = reinterpret_cast<uint32_t>(flashMemory.begin() + chunk->alignedAddress);

uint32_t addr = reinterpret_cast<uint32_t>(chunk->data.begin());
auto result = HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, fullAddress, addr);
really_assert(result == HAL_OK);
fullAddress += alignment;
chunk = flashAlign.Next();
}
}
#endif

FlashInternalStm::FlashInternalStm(infra::MemoryRange<uint32_t> sectorSizes, infra::ConstByteRange flashMemory)
: FlashInternalStmBase(flashMemory)
, sectorSizes(sectorSizes)
Expand Down
7 changes: 0 additions & 7 deletions hal_st/stm32fxxx/FlashInternalStm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ namespace hal
void ReadBuffer(infra::ByteRange buffer, uint32_t address, infra::Function<void()> onDone) override;
void EraseSectors(uint32_t beginIndex, uint32_t endIndex, infra::Function<void()> onDone) override;

private:
template<typename alignment, uint32_t flashType>
void AlignedWriteBuffer(infra::ConstByteRange buffer, uint32_t address);
#if defined(STM32WBA) || defined(STM32H5)
void AlignedWriteBuffer(infra::ConstByteRange buffer, uint32_t address);
#endif

private:
infra::ConstByteRange flashMemory;
};
Expand Down
Loading

0 comments on commit 2705f02

Please sign in to comment.