Skip to content
Open
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
4 changes: 4 additions & 0 deletions hal_st/stm32fxxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ target_sources(hal_st.stm32fxxx PRIVATE
$<$<STREQUAL:${TARGET_MCU},stm32wb55>:FlashInternalStmBle.hpp>
GpioStm.cpp
GpioStm.hpp
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:HighCycleAreaOrOtpIrqHandler.cpp>
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:HighCycleAreaOrOtpIrqHandler.hpp>
$<$<NOT:$<STREQUAL:${TARGET_MCU_FAMILY},stm32g0xx>>:I2cStm.cpp>
$<$<NOT:$<STREQUAL:${TARGET_MCU_FAMILY},stm32g0xx>>:I2cStm.hpp>
QuadSpiStm.cpp
Expand All @@ -80,6 +82,8 @@ target_sources(hal_st.stm32fxxx PRIVATE
LpTimerStm.hpp
$<$<NOT:$<STREQUAL:${TARGET_MCU},stm32wb55>>:LpTimerPwmStm.cpp>
$<$<NOT:$<STREQUAL:${TARGET_MCU},stm32wb55>>:LpTimerPwmStm.hpp>
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:OneTimeProgrammableFlashStm.cpp>
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:OneTimeProgrammableFlashStm.hpp>
RandomDataGeneratorStm.cpp
RandomDataGeneratorStm.hpp
ResetStm.cpp
Expand Down
23 changes: 5 additions & 18 deletions hal_st/stm32fxxx/FlashInternalHighCycleAreaStm.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#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>
#include DEVICE_HEADER

namespace
{
Expand Down Expand Up @@ -38,10 +38,10 @@ namespace
}
}

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

Expand Down Expand Up @@ -139,19 +139,6 @@ namespace hal

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);
}
}
, HighCycleAreaOrOtpIrqHandler()
{}
}
13 changes: 5 additions & 8 deletions hal_st/stm32fxxx/FlashInternalHighCycleAreaStm.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// 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 "hal_st/stm32fxxx/HighCycleAreaOrOtpIrqHandler.hpp"
#include "infra/util/MemoryRange.hpp"
#include <cstdint>
#include DEVICE_HEADER

namespace hal
{
Expand All @@ -19,7 +18,7 @@ namespace hal

class WithIrqHandler;

FlashInternalHighCycleAreaStm(uint32_t bank = FLASH_BANK_2);
explicit 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;
Expand All @@ -37,12 +36,10 @@ namespace hal

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

private:
hal::ImmediateInterruptHandler nmi;
explicit WithIrqHandler(uint32_t bank = FLASH_BANK_2);
};
}

Expand Down
24 changes: 24 additions & 0 deletions hal_st/stm32fxxx/HighCycleAreaOrOtpIrqHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "hal_st/stm32fxxx/HighCycleAreaOrOtpIrqHandler.hpp"
#include "infra/util/ReallyAssert.hpp"
#include DEVICE_HEADER

namespace hal
{
HighCycleAreaOrOtpIrqHandler::HighCycleAreaOrOtpIrqHandler()
: nmi{
IRQn_Type::NonMaskableInt_IRQn, []
{
// From RM0481 Rev 4:
// 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 (fixed:) 7.9.10 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);
}
}
{}
}
22 changes: 22 additions & 0 deletions hal_st/stm32fxxx/HighCycleAreaOrOtpIrqHandler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef HALST_HIGH_CYCLE_AREA_OR_OTP_IRQ_HANDLER_HPP
#define HALST_HIGH_CYCLE_AREA_OR_OTP_IRQ_HANDLER_HPP

#include "hal_st/cortex/InterruptCortex.hpp"

namespace hal
{
// This class is used as a base class for both FlashInternalHighCycleAreaStm::WithIrqHandler and OneTimeProgrammableFlashStm::WithIrqHandler,
// as they share the same NMI handler which is responsible for clearing the flash end of operation flag.
class HighCycleAreaOrOtpIrqHandler
{
public:
HighCycleAreaOrOtpIrqHandler();
HighCycleAreaOrOtpIrqHandler(const HighCycleAreaOrOtpIrqHandler&) = delete;
HighCycleAreaOrOtpIrqHandler& operator=(const HighCycleAreaOrOtpIrqHandler&) = delete;

private:
hal::ImmediateInterruptHandler nmi;
};
}

#endif
120 changes: 120 additions & 0 deletions hal_st/stm32fxxx/OneTimeProgrammableFlashStm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include "hal_st/stm32fxxx/OneTimeProgrammableFlashStm.hpp"
#include "hal_st/stm32fxxx/FlashInternalStmDetail.hpp"
#include "infra/event/EventDispatcher.hpp"
#include "infra/util/LogAndAbort.hpp"
#include "infra/util/ReallyAssert.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include DEVICE_HEADER

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

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

constexpr size_t halfWordSize = sizeof(uint16_t);
constexpr uint32_t sectorNumber{ 32 };
constexpr uint32_t sectorSize{ FLASH_OTP_SIZE / sectorNumber };
const ConstHalfWordRange OtpRange{ ptr<FLASH_OTP_BASE>(), ptr<FLASH_OTP_BASE + FLASH_OTP_SIZE>() };

void Copy(const uint16_t* begin, const uint16_t* end, uint16_t* destination)
{
for (; begin != end; ++destination, ++begin)
*destination = *begin;
}
}

namespace hal
{
OneTimeProgrammableFlashWorker::OneTimeProgrammableFlashWorker()
: flashMemory{ OtpRange }
{}

void OneTimeProgrammableFlashWorker::ReadBuffer(infra::ByteRange buffer, uint32_t address)
{
really_assert(buffer.size() % sizeof(uint16_t) == 0);
// address is byte-based, addressAdjusted is half-word-based
auto addressAdjusted = address / halfWordSize;
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());
}

void OneTimeProgrammableFlashWorker::WriteBuffer(infra::ConstByteRange buffer, uint32_t address)
{
AssertDestinationIsUntouched(buffer, address);

HAL_FLASH_Unlock();

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

HAL_FLASH_Lock();
}

[[noreturn]] void OneTimeProgrammableFlashWorker::EraseSectors([[maybe_unused]] uint32_t beginIndex, [[maybe_unused]] uint32_t endIndex)
{
// OTP cannot be erased
LOG_AND_ABORT("Not supported");
}

uint32_t OneTimeProgrammableFlashWorker::SectorNumber()
{
return sectorNumber;
}

uint32_t OneTimeProgrammableFlashWorker::SectorSize()
{
return sectorSize;
}

infra::MemoryRange<volatile uint16_t> OneTimeProgrammableFlashWorker::Range()
{
auto* base = reinterpret_cast<volatile uint16_t*>(FLASH_OTP_BASE);
auto* end = reinterpret_cast<volatile uint16_t*>(FLASH_OTP_BASE + FLASH_OTP_SIZE);
return infra::MakeRange(base, end);
}

void OneTimeProgrammableFlashWorker::AssertDestinationIsUntouched(infra::ConstByteRange buffer, uint32_t address)
{
auto writeBuffer = infra::ReinterpretCastMemoryRange<const uint16_t>(buffer);
auto otpDestination = infra::Head(infra::DiscardHead(Range(), address / halfWordSize), writeBuffer.size());
// From RM0481 Rev 4:
// Section 7.3.9 OTP and RO memory access:
// Overwriting an already programmed 16-bit half-word can lead to data and ECC errors,
// and is therefore not supported.
for (size_t offset = 0; const auto& halfWord : otpDestination)
{
really_assert(halfWord == 0xFFFF || halfWord == writeBuffer[offset]);
++offset;
}
}

OneTimeProgrammableFlashStm::OneTimeProgrammableFlashStm()
: FlashHomogeneous{ OneTimeProgrammableFlashWorker::SectorNumber(), OneTimeProgrammableFlashWorker::SectorSize() }
, OneTimeProgrammableFlashWorker{}
{}

void OneTimeProgrammableFlashStm::ReadBuffer(infra::ByteRange buffer, uint32_t address, infra::Function<void()> onDone)
{
OneTimeProgrammableFlashWorker::ReadBuffer(buffer, address);
infra::EventDispatcher::Instance().Schedule(onDone);
}

void OneTimeProgrammableFlashStm::WriteBuffer(infra::ConstByteRange buffer, uint32_t address, infra::Function<void()> onDone)
{
OneTimeProgrammableFlashWorker::WriteBuffer(buffer, address);
infra::EventDispatcher::Instance().Schedule(onDone);
}

void OneTimeProgrammableFlashStm::EraseSectors(uint32_t beginIndex, uint32_t endIndex, infra::Function<void()> onDone)
{
OneTimeProgrammableFlashWorker::EraseSectors(beginIndex, endIndex);
infra::EventDispatcher::Instance().Schedule(onDone);
}
}
57 changes: 57 additions & 0 deletions hal_st/stm32fxxx/OneTimeProgrammableFlashStm.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef HALST_ONE_TIME_PROGRAMMABLE_FLASH_STM_HPP
#define HALST_ONE_TIME_PROGRAMMABLE_FLASH_STM_HPP

#include "hal/interfaces/FlashHomogeneous.hpp"
#include "hal_st/stm32fxxx/HighCycleAreaOrOtpIrqHandler.hpp"
#include <cstdint>

namespace hal
{
// This class is used to implement both the synchronous and asynchronous version of the OTP flash,
// as the underlying implementation is the same except for scheduling the onDone callback.
class OneTimeProgrammableFlashWorker
{
public:
using ConstHalfWordRange = infra::MemoryRange<const uint16_t>;

class WithIrqHandler;

OneTimeProgrammableFlashWorker();

void WriteBuffer(infra::ConstByteRange buffer, uint32_t address);
void ReadBuffer(infra::ByteRange buffer, uint32_t address);
[[noreturn]] void EraseSectors(uint32_t beginIndex, uint32_t endIndex);

static uint32_t SectorNumber();
static uint32_t SectorSize();
static infra::MemoryRange<volatile uint16_t> Range();

private:
ConstHalfWordRange flashMemory;
void AssertDestinationIsUntouched(infra::ConstByteRange buffer, uint32_t address);
};

class OneTimeProgrammableFlashStm
: public FlashHomogeneous
, private OneTimeProgrammableFlashWorker
{
public:
class WithIrqHandler;

OneTimeProgrammableFlashStm();

// implementation of Flash interface
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;

using OneTimeProgrammableFlashWorker::Range;
};

class OneTimeProgrammableFlashStm::WithIrqHandler
: public OneTimeProgrammableFlashStm
, private HighCycleAreaOrOtpIrqHandler
{};
}

#endif
2 changes: 2 additions & 0 deletions hal_st/synchronous_stm32fxxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ target_sources(hal_st.synchronous_stm32fxxx PRIVATE
SynchronousFlashInternalStm.hpp
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32wbxx>:SynchronousHardwareSemaphoreStm.cpp>
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32wbxx>:SynchronousHardwareSemaphoreStm.hpp>
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:SynchronousOneTimeProgrammableFlashStm.cpp>
$<$<STREQUAL:${TARGET_MCU_FAMILY},stm32h5xx>:SynchronousOneTimeProgrammableFlashStm.hpp>
SynchronousRandomDataGeneratorStm.cpp
SynchronousRandomDataGeneratorStm.hpp
SynchronousSpiMasterStm.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "hal_st/synchronous_stm32fxxx/SynchronousOneTimeProgrammableFlashStm.hpp"

namespace hal
{
SynchronousOneTimeProgrammableFlashStm::SynchronousOneTimeProgrammableFlashStm()
: SynchronousFlashHomogeneous{ OneTimeProgrammableFlashWorker::SectorNumber(), OneTimeProgrammableFlashWorker::SectorSize() }
, OneTimeProgrammableFlashWorker{}
{}

void SynchronousOneTimeProgrammableFlashStm::ReadBuffer(infra::ByteRange buffer, uint32_t address)
{
OneTimeProgrammableFlashWorker::ReadBuffer(buffer, address);
}

void SynchronousOneTimeProgrammableFlashStm::WriteBuffer(infra::ConstByteRange buffer, uint32_t address)
{
OneTimeProgrammableFlashWorker::WriteBuffer(buffer, address);
}

void SynchronousOneTimeProgrammableFlashStm::EraseSectors(uint32_t beginIndex, uint32_t endIndex)
{
OneTimeProgrammableFlashWorker::EraseSectors(beginIndex, endIndex);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef HALST_SYNCHRONOUS_ONE_TIME_PROGRAMMABLE_FLASH_STM_HPP
#define HALST_SYNCHRONOUS_ONE_TIME_PROGRAMMABLE_FLASH_STM_HPP

#include "hal/synchronous_interfaces/SynchronousFlashHomogeneous.hpp"
#include "hal_st/stm32fxxx/OneTimeProgrammableFlashStm.hpp"

namespace hal
{
class SynchronousOneTimeProgrammableFlashStm
: public SynchronousFlashHomogeneous
, private OneTimeProgrammableFlashWorker
{
public:
class WithIrqHandler;

SynchronousOneTimeProgrammableFlashStm();

// implementation of SynchronousFlash interface
void WriteBuffer(infra::ConstByteRange buffer, uint32_t address) override;
void ReadBuffer(infra::ByteRange buffer, uint32_t address) override;
void EraseSectors(uint32_t beginIndex, uint32_t endIndex) override;

using OneTimeProgrammableFlashWorker::Range;
};

class SynchronousOneTimeProgrammableFlashStm::WithIrqHandler
: public SynchronousOneTimeProgrammableFlashStm
, private HighCycleAreaOrOtpIrqHandler
{};
}

#endif
Loading