Skip to content

Commit ec9a43c

Browse files
committed
tests/flash_simulator: Test callbacks for erase and write operations
This test checks the functionality of the callbacks for erase and write operations. The callbacks allows the modification of the simulated flash behaviour during this operations. Signed-off-by: Radosław Koppel <[email protected]>
1 parent e084bd4 commit ec9a43c

File tree

2 files changed

+219
-0
lines changed

2 files changed

+219
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
CONFIG_ZTEST=y
22
CONFIG_FLASH=y
33
CONFIG_FLASH_SIMULATOR=y
4+
CONFIG_FLASH_SIMULATOR_CALLBACKS=y
45
CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=n
56
CONFIG_FLASH_SIMULATOR_UNALIGNED_READ=n
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* Copyright (c) 2025 Koppel Electronic
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include <stdlib.h>
7+
#include <zephyr/ztest.h>
8+
#include <zephyr/drivers/flash.h>
9+
#include <zephyr/drivers/flash/flash_simulator.h>
10+
#include <zephyr/device.h>
11+
12+
/* Test the flash simulator callbacks to modify the behaviour of the
13+
* flash simulator on the fly.
14+
*/
15+
16+
/* configuration derived from DT */
17+
#ifdef CONFIG_ARCH_POSIX
18+
#define SOC_NV_FLASH_NODE DT_CHILD(DT_INST(0, zephyr_sim_flash), flash_0)
19+
#else
20+
#define SOC_NV_FLASH_NODE DT_CHILD(DT_INST(0, zephyr_sim_flash), flash_sim_0)
21+
#endif /* CONFIG_ARCH_POSIX */
22+
#define FLASH_SIMULATOR_BASE_OFFSET DT_REG_ADDR(SOC_NV_FLASH_NODE)
23+
#define FLASH_SIMULATOR_ERASE_UNIT DT_PROP(SOC_NV_FLASH_NODE, erase_block_size)
24+
#define FLASH_SIMULATOR_PROG_UNIT DT_PROP(SOC_NV_FLASH_NODE, write_block_size)
25+
#define FLASH_SIMULATOR_FLASH_SIZE DT_REG_SIZE(SOC_NV_FLASH_NODE)
26+
27+
#define FLASH_SIMULATOR_ERASE_VALUE \
28+
DT_PROP(DT_PARENT(SOC_NV_FLASH_NODE), erase_value)
29+
30+
#if (defined(CONFIG_ARCH_POSIX) || defined(CONFIG_BOARD_QEMU_X86))
31+
static const struct device *const flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));
32+
#else
33+
static const struct device *const flash_dev = DEVICE_DT_GET(DT_NODELABEL(sim_flash_controller));
34+
#endif
35+
36+
37+
/* We are simulating the broken FLASH memory.
38+
* Simulate different types of errors depending on the erase page.
39+
* Page 0: behaves normal
40+
* Page 1: erase works as expected, all writes fails (no write, -EIO returned)
41+
* Page 2: erase works as expected, all writes silently corrupt data (bits 0, 1, 20 in sticks to 1)
42+
* Page 3: erase fails (no erase, -EIO returned), all writes fails (-EIO returned)
43+
* Page 4: erase fails silently (erases writes 0x55 to all bytes), all writes fails (-EIO returned)
44+
*/
45+
enum test_page_type {
46+
TEST_PAGE_TYPE_NORMAL,
47+
TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL,
48+
TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT,
49+
TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL,
50+
TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL,
51+
};
52+
53+
/* Corruption pattern for write operations: bits 0, 1, and 20 in 32-bit word */
54+
static const uint32_t test_write_corruption_pattern = 0x00100003;
55+
56+
static int test_write_byte_callback(const struct device *dev,
57+
const struct flash_simulator_params *params,
58+
off_t offset,
59+
const uint8_t data)
60+
{
61+
enum test_page_type page = (offset - params->base_offset) /
62+
params->erase_unit;
63+
64+
switch (page) {
65+
case TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL:
66+
case TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL:
67+
case TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL:
68+
return -EIO;
69+
case TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT: {
70+
/* Apply corruption pattern based on byte position within word */
71+
uint8_t byte_in_word = offset & 0x3;
72+
uint8_t corruption_byte =
73+
(test_write_corruption_pattern >> (byte_in_word * 8)) & 0xFF;
74+
75+
return data | corruption_byte;
76+
}
77+
case TEST_PAGE_TYPE_NORMAL:
78+
default:
79+
return data;
80+
}
81+
}
82+
83+
static int test_erase_unit_callback(const struct device *dev,
84+
const struct flash_simulator_params *params,
85+
off_t offset)
86+
{
87+
enum test_page_type page = (offset - params->base_offset) /
88+
params->erase_unit;
89+
90+
switch (page) {
91+
case TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL:
92+
return -EIO;
93+
case TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL:
94+
/* Corrupt erase by writing 0x55 to all bytes */
95+
memset(flash_simulator_mock_flash(params, offset),
96+
0x55, params->erase_unit);
97+
return 0;
98+
case TEST_PAGE_TYPE_NORMAL:
99+
case TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL:
100+
case TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT:
101+
default:
102+
/* Normal erase behavior - callback must perform the erase */
103+
memset(flash_simulator_mock_flash(params, offset),
104+
params->erase_value, params->erase_unit);
105+
return 0;
106+
}
107+
}
108+
109+
static struct flash_simulator_cb test_flash_sim_cbs = {
110+
.write_byte = test_write_byte_callback,
111+
.erase_unit = test_erase_unit_callback,
112+
};
113+
114+
void *flash_sim_cbs_setup(void)
115+
{
116+
zassert_true(device_is_ready(flash_dev),
117+
"Simulated flash device not ready");
118+
119+
flash_simulator_set_callbacks(flash_dev, &test_flash_sim_cbs);
120+
121+
return NULL;
122+
}
123+
124+
ZTEST(flash_sim_cbs, test_page_behaviors)
125+
{
126+
int ret;
127+
uint32_t write_buf[FLASH_SIMULATOR_ERASE_UNIT / sizeof(uint32_t)];
128+
uint32_t read_buf[FLASH_SIMULATOR_ERASE_UNIT / sizeof(uint32_t)];
129+
off_t page_offset;
130+
size_t buf_size = sizeof(write_buf);
131+
132+
/* Initialize write buffer with pseudo-random pattern */
133+
srand(0x12345678);
134+
for (size_t i = 0; i < ARRAY_SIZE(write_buf); i++) {
135+
write_buf[i] = ((uint32_t)rand() << 16) | (uint32_t)rand();
136+
}
137+
138+
/* Test Page 0: TEST_PAGE_TYPE_NORMAL - behaves normal */
139+
page_offset = TEST_PAGE_TYPE_NORMAL * FLASH_SIMULATOR_ERASE_UNIT;
140+
141+
ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
142+
zassert_equal(ret, 0, "Page 0: Erase should succeed");
143+
144+
ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
145+
zassert_equal(ret, 0, "Page 0: Write should succeed");
146+
147+
ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
148+
zassert_equal(ret, 0, "Page 0: Read should succeed");
149+
zassert_mem_equal(read_buf, write_buf, buf_size,
150+
"Page 0: Data should match written data");
151+
152+
/* Test Page 1: TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL - erase OK, write fails */
153+
page_offset = TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL * FLASH_SIMULATOR_ERASE_UNIT;
154+
155+
ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
156+
zassert_equal(ret, 0, "Page 1: Erase should succeed");
157+
158+
ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
159+
zassert_equal(ret, 0, "Page 1: Read after erase should succeed");
160+
for (size_t i = 0; i < ARRAY_SIZE(read_buf); i++) {
161+
uint32_t expected = FLASH_SIMULATOR_ERASE_VALUE;
162+
163+
expected |= (expected << 8) | (expected << 16) | (expected << 24);
164+
zassert_equal(read_buf[i], expected,
165+
"Page 1: After erase, data should be erase value");
166+
}
167+
168+
ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
169+
zassert_equal(ret, -EIO, "Page 1: Write should fail with -EIO");
170+
171+
/* Test Page 2: TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT - erase OK, write corrupts */
172+
page_offset = TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT * FLASH_SIMULATOR_ERASE_UNIT;
173+
174+
ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
175+
zassert_equal(ret, 0, "Page 2: Erase should succeed");
176+
177+
ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
178+
zassert_equal(ret, 0, "Page 2: Write should succeed (but corrupt data)");
179+
180+
ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
181+
zassert_equal(ret, 0, "Page 2: Read should succeed");
182+
183+
/* Verify corruption pattern: bits 0, 1, and 20 should be set */
184+
for (size_t i = 0; i < ARRAY_SIZE(read_buf); i++) {
185+
uint32_t expected = write_buf[i] | test_write_corruption_pattern;
186+
187+
zassert_equal(read_buf[i], expected,
188+
"Page 2: Data should be corrupted with pattern 0x%08x",
189+
test_write_corruption_pattern);
190+
}
191+
192+
/* Test Page 3: TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL - both fail */
193+
page_offset = TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL * FLASH_SIMULATOR_ERASE_UNIT;
194+
195+
ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
196+
zassert_equal(ret, -EIO, "Page 3: Erase should fail with -EIO");
197+
198+
ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
199+
zassert_equal(ret, -EIO, "Page 3: Write should fail with -EIO");
200+
201+
/* Test Page 4: TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL - erase corrupts, write fails */
202+
page_offset = TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL * FLASH_SIMULATOR_ERASE_UNIT;
203+
204+
ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
205+
zassert_equal(ret, 0, "Page 4: Erase should succeed (but corrupt)");
206+
207+
ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
208+
zassert_equal(ret, 0, "Page 4: Read after erase should succeed");
209+
for (size_t i = 0; i < ARRAY_SIZE(read_buf); i++) {
210+
zassert_equal(read_buf[i], 0x55555555,
211+
"Page 4: After erase, data should be 0x55555555 (corrupted erase)");
212+
}
213+
214+
ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
215+
zassert_equal(ret, -EIO, "Page 4: Write should fail with -EIO");
216+
}
217+
218+
ZTEST_SUITE(flash_sim_cbs, NULL, flash_sim_cbs_setup, NULL, NULL, NULL);

0 commit comments

Comments
 (0)