Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
EEPROMStoredBitSet.hxx
Go to the documentation of this file.
1
35#ifndef _UTILS_EEPROMSTOREDBITSET_HXX_
36#define _UTILS_EEPROMSTOREDBITSET_HXX_
37
39#include "utils/logging.h"
40
43{
44protected:
45 typedef uint32_t eeprom_t;
46
48 static unsigned bits_per_cell()
49 {
50 return 27;
51 }
52
57 static unsigned virtual_cell_count()
58 {
59 return 8;
60 }
61
65 static unsigned physical_cell_count()
66 {
67 return 64;
68 }
69
74 static void write_cell(unsigned cell_offset, eeprom_t value);
75
79 static eeprom_t read_cell(unsigned cell_offset);
80};
81
82template <class HW>
83class EEPROMStoredBitSet : private HW, public ShadowedStoredBitSet
84{
85public:
86 template <typename... Args>
87 EEPROMStoredBitSet(Args... args)
88 : HW(args...)
89 , ShadowedStoredBitSet(HW::bits_per_cell() * HW::virtual_cell_count(),
90 HW::bits_per_cell())
91 {
92 mount();
93 }
94
95 void flush() override
96 {
97 do
98 {
100 if (c == NO_CELL)
101 break;
102 if (writeOffset_ >= HW::physical_cell_count())
103 {
104 // Wrap around.
105 currentMarker_ = (~currentMarker_) & MARKER_MASK;
106 write_header();
107 clear_all_dirty();
108 return;
109 }
110 if (writeOffset_ + 1 < HW::physical_cell_count())
111 {
112 auto v = HW::read_cell(writeOffset_ + 1);
113 if ((v & MARKER_MASK) == currentMarker_)
114 {
115 // This is a problem. If we write the second magic now, the
116 // additional entry will become a valid journal entry.
117 HW::write_cell(
118 writeOffset_ + 1, (~currentMarker_) & MARKER_MASK);
119 }
120 }
121 HW::write_cell(writeOffset_++, get_vcell(c));
122 } while (true);
123 }
124
125private:
126 using eeprom_t = typename HW::eeprom_t;
127
128 void mount()
129 {
130 unsigned vcell_ofs_bits =
131 (sizeof(eeprom_t) * 8) - HW::bits_per_cell() - 1;
132 HASSERT(vcell_ofs_bits < 32);
133 HASSERT((1U << vcell_ofs_bits) - 2 >= HW::virtual_cell_count());
134 HASSERT(
135 HW::virtual_cell_count() + 2 <= (HW::physical_cell_count() / 2));
136 eeprom_t magic = HW::read_cell(FIRST_MAGIC_OFS);
137 if ((magic & ~MARKER_MASK) != MAGIC)
138 {
139 format();
140 return;
141 }
142 for (unsigned i = 0; i < HW::virtual_cell_count(); ++i)
143 {
144 eeprom_t v = HW::read_cell(i + HEADER_OFS);
145 read_entry(v);
146 }
147 eeprom_t magic2 = HW::read_cell(SECOND_MAGIC_OFS);
148 if (magic2 == magic)
149 {
150 // Markers are consistent. Read further entries.
151 currentMarker_ = magic2 & MARKER_MASK;
152 writeOffset_ = JOURNAL_OFS;
153 eeprom_t v;
154 while (writeOffset_ < HW::physical_cell_count() &&
155 ((v = HW::read_cell(writeOffset_)) & MARKER_MASK) ==
157 {
158 read_entry(v);
159 ++writeOffset_;
160 }
161 }
162 else
163 {
164 // There was a problem rewriting the header. Read the entire sector.
165 for (unsigned i = JOURNAL_OFS; i < HW::physical_cell_count(); ++i)
166 {
167 eeprom_t v = HW::read_cell(i);
168 if (v == ERASED)
169 {
170 writeOffset_ = i;
171 break;
172 }
173 read_entry(v);
174 }
175 currentMarker_ = magic & MARKER_MASK;
176 write_header();
177 }
178 clear_all_dirty();
179 }
180
181 void clear_all_dirty()
182 {
183 cell_offs_t c;
184 while ((c = next_dirty()) != NO_CELL)
185 {
186 clear_dirty(c);
187 }
188 }
189
190 void format()
191 {
192 for (unsigned j = JOURNAL_OFS; j < HW::physical_cell_count(); ++j)
193 {
194 eeprom_t v = HW::read_cell(j);
195 if (v != 0)
196 {
197 HW::write_cell(j, 0);
198 }
199 }
201 write_header();
202 }
203
204 void write_header()
205 {
206 HW::write_cell(FIRST_MAGIC_OFS, MAGIC | currentMarker_);
207 for (unsigned i = 0; i < HW::virtual_cell_count(); ++i)
208 {
209 eeprom_t ov = HW::read_cell(i + HEADER_OFS);
210 eeprom_t nv = get_vcell(i);
211 if ((ov & ~MARKER_MASK) == (nv & ~MARKER_MASK))
212 {
213 // Didn't change, skip write.
214 continue;
215 }
216 HW::write_cell(i + HEADER_OFS, nv);
217 }
218 eeprom_t v = HW::read_cell(JOURNAL_OFS);
219 if ((v & MARKER_MASK) == currentMarker_)
220 {
221 // This is a problem. If we write the second magic now, the
222 // additional entry will become a valid journal entry.
223 HW::write_cell(JOURNAL_OFS, (~currentMarker_) & MARKER_MASK);
224 }
225 HW::write_cell(SECOND_MAGIC_OFS, MAGIC | currentMarker_);
226 writeOffset_ = JOURNAL_OFS;
227 }
228
229 void read_entry(eeprom_t v)
230 {
231 auto vcell = value_to_vcell_ofs(v);
232 ShadowedStoredBitSet::set_multi(vcell * HW::bits_per_cell(),
233 HW::bits_per_cell(), value_to_vcell_value(v));
234 }
235
236 unsigned value_to_vcell_ofs(eeprom_t value)
237 {
238 return (value & ~MARKER_MASK) >> HW::bits_per_cell();
239 }
240
241 unsigned value_to_vcell_value(eeprom_t value)
242 {
243 return (value & ((1U << HW::bits_per_cell()) - 1));
244 }
245
246 eeprom_t get_vcell(unsigned vcell)
247 {
248 eeprom_t v =
249 ShadowedStoredBitSet::get_multi(vcell * HW::bits_per_cell(), HW::bits_per_cell());
250 v |= (vcell << HW::bits_per_cell());
251 v |= currentMarker_;
252 return v;
253 }
254
257 static constexpr eeprom_t MARKER_MASK = eeprom_t(1)
258 << ((sizeof(eeprom_t) * 8) - 1);
259
260 static constexpr eeprom_t EEPROM_MASK = eeprom_t(-1);
261
264 static constexpr eeprom_t MAGIC =
265 0x02b26758U & EEPROM_MASK & (~MARKER_MASK);
266
267 static constexpr eeprom_t ERASED = 0xFFFFFFFFU & EEPROM_MASK;
268
269 static constexpr unsigned FIRST_MAGIC_OFS = 0;
270 static constexpr unsigned SECOND_MAGIC_OFS = 1;
271 static constexpr unsigned HEADER_OFS = 2;
272 static constexpr unsigned JOURNAL_OFS =
273 HW::virtual_cell_count() + HEADER_OFS;
274
277 unsigned writeOffset_;
278};
279
280#endif // _UTILS_EEPROMSTOREDBITSET_HXX_
Template of how the HW class should look like.
static unsigned virtual_cell_count()
Defines how many virtual cells we store.
static unsigned physical_cell_count()
Defines.
static void write_cell(unsigned cell_offset, eeprom_t value)
Writes to the physical storage.
static eeprom_t read_cell(unsigned cell_offset)
Reads from the physical storage.
static constexpr eeprom_t MAGIC
Magic value in the zero offset certifies that we are correctly formatted.
eeprom_t currentMarker_
What is the current desired value of the marker bit.
static constexpr eeprom_t MARKER_MASK
The MSB of each eeprom value is the marker.
void flush() override
Writes the current values to persistent storage.
StoredBitSet & set_multi(unsigned offset, unsigned size, unsigned value) override
Sets a block of consecutive bits.
cell_offs_t next_dirty()
void clear_dirty(cell_offs_t cell)
Clears the dirty bit for a given cell.
unsigned get_multi(unsigned offset, unsigned size) override
Returns a block of consecutive bits as an integer value.
uint8_t cell_offs_t
Type indexing cells. Must be an unsigned type.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138