Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
EEPROMEmuTest.hxx
1#include "utils/test_main.hxx"
2
3// We have to avoid pulling in freertos stuff. We redefine the base class to
4// avoid dependency on hand-written fileio stuff.
5#define _FREERTOS_DRIVERS_COMMON_EEPROM_HXX_
6
7class EEPROM
8{
9public:
16 EEPROM(const char *name, size_t file_size)
18 {
19 }
20
29 virtual void write(unsigned int index, const void *buf, size_t len) = 0;
30
39 virtual void read(unsigned int index, void *buf, size_t len) = 0;
40
42 size_t file_size()
43 {
44 return fileSize;
45 }
46
47private:
48 size_t fileSize;
49};
50
51// Terrible hack to test internals of the eeprom emulation.
52#define private public
53#define protected public
54
57
58static const char FILENAME[] = "/tmp/eeprom";
59
60#define EELEN 32768
61
63{
64}
65
66// We need to jump through some hoops to define a linker symbol
67// "__eeprom_start" in a place that is not actually constant.
68namespace foo {
69extern "C" {
70uint8_t __eeprom_start[EELEN];
71uint8_t __eeprom_end;
72}
73}
74
75#define EEBLOCKSIZE 4
76
77const size_t EEPROMEmulation::SECTOR_SIZE = (4 * 1024);
78const size_t EEPROMEmulation::BLOCK_SIZE = (EEBLOCKSIZE);
79const size_t EEPROMEmulation::BYTES_PER_BLOCK = (EEBLOCKSIZE / 2);
80static constexpr unsigned blocks_per_sector = EEPROMEmulation::SECTOR_SIZE / EEPROMEmulation::BLOCK_SIZE;
81
85{
86public:
89 MyEEPROM(size_t file_size, bool clear = true)
90 : EEPROMEmulation(FILENAME, file_size)
91 {
93 if (clear) {
94 memset(foo::__eeprom_start, 0xFF, EELEN);
95 }
96 mount();
97
98 LOG(INFO, "sector count %d, active index %d, slot count %d, available count %d", sector_count(), activeSector_, slot_count(), avail());
99 }
100
102 unsigned avail() {
103 return availableSlots_;
104 }
105
106private:
107 void flash_erase(unsigned sector) override {
108 ASSERT_LE(0u, sector);
109 ASSERT_GT(EELEN / SECTOR_SIZE, sector);
110 void* address = &foo::__eeprom_start[sector * SECTOR_SIZE];
111 memset(address, 0xff, SECTOR_SIZE);
112 }
113
114 void flash_program(unsigned sector, unsigned block, uint32_t *data, uint32_t byte_count) override {
115 ASSERT_LE(0u, sector);
116 ASSERT_GT(EELEN / SECTOR_SIZE, sector);
117 ASSERT_LE(0u, block);
118 ASSERT_GT(SECTOR_SIZE/BLOCK_SIZE, block);
119 ASSERT_EQ(0u, byte_count % BLOCK_SIZE);
120 uint8_t* address = &foo::__eeprom_start[sector * SECTOR_SIZE + block * BLOCK_SIZE];
121 memcpy(address, data, byte_count);
122 }
123
124 const uint32_t* block(unsigned sector, unsigned index) override {
125 EXPECT_GT(EELEN / SECTOR_SIZE, sector);
126 EXPECT_GT(SECTOR_SIZE / BLOCK_SIZE, index);
127 void* address = &foo::__eeprom_start[sector * SECTOR_SIZE + index * BLOCK_SIZE];
128 return (uint32_t*) address;
129 }
130};
131
132TEST(EepromStaticTest, assertions) {
133 volatile size_t p1 = (volatile size_t)&__eeprom_start;
134 volatile size_t p2 = (volatile size_t)&foo::__eeprom_start[0];
135
136 volatile size_t e1 = (volatile size_t)&__eeprom_end;
137
138 ASSERT_EQ(p1, p2);
139 ASSERT_EQ(p1 + EELEN, e1);
140 ASSERT_EQ(0u, p1 % 4); // alignment
141}
142
144class EepromTest : public ::testing::Test {
145protected:
148 void create(bool clear = true) {
149 e.reset(new MyEEPROM(eeprom_size, clear));
150 }
151
157 void write_to(unsigned ofs, const string &payload)
158 {
159 ee()->write(ofs, payload.data(), payload.size());
160 }
161
164 return static_cast<EEPROM*>(e.operator->());
165 }
166
169 string block_data(unsigned block_number) {
170 uint32_t* address = (uint32_t*)&foo::__eeprom_start[block_number * EEBLOCKSIZE];
171 uint8_t data[EEBLOCKSIZE / 2];
172 for (int i = 0; i < EEBLOCKSIZE / 4; ++i) {
173 data[(i * 2) + 0] = (address[i] >> 0) & 0xFF;
174 data[(i * 2) + 1] = (address[i] >> 8) & 0xFF;
175 }
176 return string((char*)data, EEBLOCKSIZE / 2);
177 }
178
182 unsigned avail = e->avail();
183 for (int i = 0; i < 27000; ++i) {
184 char d[1] = {static_cast<char>(i & 0xff)};
185 write_to(27, string(d, 1));
186 if (e->avail() > avail) return;
187 avail = e->avail();
188 }
189 }
190
194 uint32_t block_address(unsigned block_number) {
195 uint32_t* address = (uint32_t*)&foo::__eeprom_start[block_number * EEBLOCKSIZE];
196 return ((*address) >> 16) * (EEBLOCKSIZE / 2);
197 }
198
199#define EXPECT_AT(ofs, PAYLOAD) { string p(PAYLOAD); string ret(p.size(), 0); ee()->read(ofs, &ret[0], p.size()); EXPECT_EQ(p, ret); }
200
201#define EXPECT_SLOT(block_number, address, payload) { EXPECT_EQ((unsigned)address, block_address(block_number)); EXPECT_EQ(string(payload), block_data(block_number)); }
202
203 static constexpr unsigned eeprom_size = 1000;
204 std::unique_ptr<MyEEPROM> e;
205};
206
207
208TEST_F(EepromTest, create) {
209 create();
210 EXPECT_EQ(0, e->activeSector_);
211 EXPECT_EQ(8u, e->sector_count());
212 EXPECT_EQ((uint32_t*)&__eeprom_start, e->block(0, 0));
213 EXPECT_EQ((uint32_t*)&foo::__eeprom_start[4*1024], e->block(1, 0));
214}
215
216TEST_F(EepromTest, readwrite) {
217 create();
218
219 write_to(13, "abcd");
220 EXPECT_SLOT(3, 12, "\xFF""a");
221 EXPECT_SLOT(4, 14, "bc");
222 EXPECT_SLOT(5, 16, "d\xFF");
223 EXPECT_SLOT(6, 2*0xFFFF, "\xFF\xFF");
224
225 EXPECT_AT(13, "abcd");
226 EXPECT_AT(14, "bc");
227 EXPECT_AT(15, "cd");
228
229 write_to(12, "up");
230 EXPECT_AT(12, "upb");
231 EXPECT_AT(12, "upbcd");
232 write_to(12, "kq");
233 EXPECT_AT(12, "kqbcd");
234 EXPECT_AT(12, "kqbcd\xFF");
235
236 // Reboot MCU
237 create(false);
238 EXPECT_AT(12, "kqbcd\xFF");
239}
240
241TEST_F(EepromTest, readwrite_recreate) {
242 create();
243 // Reboot MCU after creating empty.
244 create(false);
245
246 write_to(13, "abcd");
247 EXPECT_AT(13, "abcd");
248
249 // Reboot MCU
250 create(false);
251 EXPECT_AT(13, "abcd");
252}
253
254TEST_F(EepromTest, smalloverflow) {
255 create();
256 write_to(13, "abcd");
257 EXPECT_AT(13, "abcd");
258 EXPECT_SLOT(3, 12, "\xFF""a");
259 EXPECT_SLOT(4, 14, "bc");
260 EXPECT_SLOT(5, 16, "d\xFF");
261 overflow_block();
262 EXPECT_EQ(1, e->activeSector_);
263 EXPECT_SLOT(3, 12, "\xFF""a");
264 EXPECT_SLOT(4, 14, "bc");
265 EXPECT_SLOT(5, 16, "d\xFF");
266 EXPECT_SLOT(blocks_per_sector + 3, 12, "\xFF""a");
267 EXPECT_SLOT(blocks_per_sector + 4, 14, "bc");
268 EXPECT_SLOT(blocks_per_sector + 5, 16, "d\xFF");
269
270 EXPECT_AT(13, "abcd");
271 overflow_block();
272 EXPECT_AT(13, "abcd");
273 overflow_block();
274 EXPECT_AT(13, "abcd");
275 EXPECT_EQ(3, e->activeSector_);
276 create(false);
277 EXPECT_AT(13, "abcd");
278 EXPECT_EQ(3, e->activeSector_);
279}
280
281TEST_F(EepromTest, many_overflow) {
282 create();
283 write_to(13, "abcd");
284 EXPECT_AT(13, "abcd");
285 // A lot of writes will surely cause the data to be overflowed to a new
286 // sector
287 for (int i = 0; i < 20; ++i) {
288 overflow_block();
289 }
290 EXPECT_AT(13, "abcd");
291 unsigned s = e->activeSector_;
292 create(false);
293 EXPECT_AT(13, "abcd");
294 EXPECT_EQ(s, e->activeSector_);
295}
const char __eeprom_end
Linker-defined symbol where in the memory space (flash) the eeprom emulation data ends.
const char __eeprom_start
Linker-defined symbol where in the memory space (flash) the eeprom emulation data starts.
Emulates EEPROM in FLASH for the Tiva, LPC17xx and LPC40xx platforms.
uint8_t activeSector_
Index of the active sector.
static const size_t BLOCK_SIZE
block size in bytes
static const size_t SECTOR_SIZE
Sector size in bytes.
size_t availableSlots_
Number of available (writable) slots for new data in the active sector.
void mount()
Mount the EEPROM file.
static const size_t BYTES_PER_BLOCK
useful data bytes size in bytes
unsigned slot_count()
Total number of EEPROM slots in a FLASH sector.
unsigned sector_count()
Total number of FLASH sectors being used for emulation.
void updated_notification()
This function will be called after every write.
Common base class for all EEPROM access.
Definition EEPROM.hxx:44
size_t fileSize
Maximum file size we can grow to.
Definition EEPROM.hxx:87
virtual void read(unsigned int index, void *buf, size_t len)=0
Override this function to read data from the eeprom.
virtual void write(unsigned int index, const void *buf, size_t len)=0
Override this function to write data to the eeprom.
EEPROM(const char *name, size_t file_size)
Constructor.
size_t file_size()
Get the maximum file size of the EEPROM file.
Definition EEPROM.hxx:81
Test fixture class for testing the EEPROM emulation.
EEPROM * ee()
string block_data(unsigned block_number)
void overflow_block()
Write enough much data to the eepromemu under test to overflow the current block.
void write_to(unsigned ofs, const string &payload)
Helper function to write to the test eeprom.
void create(bool clear=true)
Creates the eeprom under test.
std::unique_ptr< MyEEPROM > e
EEPROM under test.
static constexpr unsigned eeprom_size
test eeprom size
uint32_t block_address(unsigned block_number)
const char * name
device name
Definition Devtab.hxx:266
Test EEPROM emulation HAL implementation that writes to a block of (RAM) memory.
void flash_program(unsigned sector, unsigned block, uint32_t *data, uint32_t byte_count) override
Simple hardware abstraction for FLASH program API.
unsigned avail()
MyEEPROM(size_t file_size, bool clear=true)
Contructor.
const uint32_t * block(unsigned sector, unsigned index) override
Computes the pointer to load the data stored in a specific block from.
void flash_erase(unsigned sector) override
Simple hardware abstraction for FLASH erase API.
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138