-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathFlashInternalHighCycleAreaStm.cpp
More file actions
162 lines (131 loc) · 5.6 KB
/
FlashInternalHighCycleAreaStm.cpp
File metadata and controls
162 lines (131 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#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>() };
bool IsBankSwapActive()
{
return (FLASH->OPTCR & FLASH_OPTCR_SWAP_BANK) != 0;
}
ConstHalfWordRange GetBankRange(uint32_t bank)
{
switch (bank)
{
case FLASH_BANK_1:
return IsBankSwapActive() ? bank2Range : bank1Range;
case FLASH_BANK_2:
return IsBankSwapActive() ? bank1Range : 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, §orError);
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);
}
}
{}
}