1 /*
2  * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/devicetree.h>
10 #include <zephyr/sys/printk.h>
11 #include <zephyr/drivers/flash.h>
12 #include <zephyr/random/random.h>
13 #include <soc/soc_memory_layout.h>
14 #include <zephyr/multi_heap/shared_multi_heap.h>
15 
16 /* definitions used in Flash & RAM operations */
17 #define SPIRAM_ALLOC_SIZE               (24 * 1024)
18 #define FLASH_PAGE_TESTED               (1023)
19 #define FLASH_PAGE_OFFSET               (0)
20 #define FLASH_BYTE_PATTERN              (0x38)
21 #define FLASH_READBACK_LEN              (1024)
22 #define FLASH_ITERATIONS                (10)
23 #define PSRAM_ITERATIONS                (10)
24 
25 /* common thread definitions */
26 #define STACKSIZE 1024
27 #define PRIORITY 7
28 
29 static const struct device *const flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));
30 static struct flash_pages_info page_info;
31 static int *mem;
32 uint8_t flash_fill_buff[FLASH_READBACK_LEN];
33 uint8_t flash_read_buff[FLASH_READBACK_LEN];
34 
35 static uint8_t flash_val = FLASH_BYTE_PATTERN;
36 static bool buffer_ready;
37 static bool needs_fill = true;
38 static bool unfinished_tasks = true;
39 static struct k_spinlock lock;
40 
41 struct coex_test_results {
42 	bool using_ext_ram;
43 	int flash_cnt;
44 	bool psram_ok;
45 };
46 static struct coex_test_results coex_result;
47 
buffer_fill(void)48 static void buffer_fill(void)
49 {
50 	while (needs_fill) {
51 		if (!buffer_ready) {
52 			k_spinlock_key_t key = k_spin_lock(&lock);
53 
54 			memset(flash_fill_buff, ++flash_val, sizeof(flash_fill_buff));
55 			buffer_ready = true;
56 			k_spin_unlock(&lock, key);
57 		}
58 		k_usleep(10);
59 	}
60 	while (true) {
61 		k_usleep(1);
62 	}
63 }
64 
check_flash(void)65 static void check_flash(void)
66 {
67 	int cnt = 0;
68 
69 	for (size_t i = 0; i < sizeof(flash_read_buff); ++i) {
70 		if (flash_read_buff[i] == (FLASH_BYTE_PATTERN + FLASH_ITERATIONS)) {
71 			++cnt;
72 		}
73 	}
74 	coex_result.flash_cnt = cnt;
75 	unfinished_tasks = false;
76 }
77 
do_erase(off_t offset,size_t size)78 static int do_erase(off_t offset, size_t size)
79 {
80 	int rc;
81 
82 	rc = flash_erase(flash_dev, offset, size);
83 	if (rc) {
84 		TC_ERROR("flash erase has failed\n");
85 		return rc;
86 	}
87 
88 	return rc;
89 }
90 
page_erase(void)91 static int page_erase(void)
92 {
93 	int rc;
94 
95 	rc = flash_get_page_info_by_idx(flash_dev, FLASH_PAGE_TESTED, &page_info);
96 	if (rc) {
97 		return rc;
98 	}
99 
100 	rc = do_erase(page_info.start_offset, page_info.size);
101 	if (rc) {
102 		return rc;
103 	}
104 
105 	return rc;
106 }
107 
page_read(void)108 static int page_read(void)
109 {
110 	int rc = flash_get_page_info_by_idx(flash_dev, FLASH_PAGE_TESTED, &page_info);
111 
112 	if (rc) {
113 		TC_ERROR("could not read flash info\n");
114 		return rc;
115 	}
116 	rc = flash_read(flash_dev,
117 			page_info.start_offset + FLASH_PAGE_OFFSET,
118 			flash_read_buff,
119 			sizeof(flash_read_buff));
120 	if (rc) {
121 		TC_ERROR("flash read back has failed\n");
122 		return rc;
123 	}
124 
125 	return 0;
126 }
127 
page_write(void)128 static int page_write(void)
129 {
130 	int rc = flash_get_page_info_by_idx(flash_dev, FLASH_PAGE_TESTED, &page_info);
131 
132 	if (rc) {
133 		TC_ERROR("could not retrieve flash info\n");
134 		return rc;
135 	}
136 
137 	rc = flash_write(flash_dev,
138 			page_info.start_offset + FLASH_PAGE_OFFSET,
139 			flash_fill_buff,
140 			sizeof(flash_fill_buff));
141 	if (rc) {
142 		TC_ERROR("could not write to flash\n");
143 		return rc;
144 	}
145 
146 	return 0;
147 }
148 
do_flashop(void)149 static bool do_flashop(void)
150 {
151 	int rc = page_write();
152 
153 	if (rc) {
154 		return false;
155 	}
156 
157 	return true;
158 }
159 
fill_value(int value)160 static void fill_value(int value)
161 {
162 	int i;
163 
164 	/* fills external RAM with given value */
165 	for (i = 0; i < (SPIRAM_ALLOC_SIZE / sizeof(int)); ++i) {
166 		mem[i] = value;
167 	}
168 }
169 
check_psram(int value)170 static bool check_psram(int value)
171 {
172 	int *ptr = mem;
173 
174 	/* checks if external RAM is filled with the expected value */
175 	for (size_t i = 0; i < (SPIRAM_ALLOC_SIZE / sizeof(int)); ++i) {
176 		if (*ptr++ != value) {
177 			return false;
178 		}
179 	}
180 	return true;
181 }
182 
psram_test(void)183 static void psram_test(void)
184 {
185 	uint32_t sleep_ms = 10;
186 	int rand_val;
187 
188 	for (size_t i = 0; i < PSRAM_ITERATIONS; ++i) {
189 		rand_val = (int)sys_rand32_get();
190 		fill_value(rand_val);
191 		k_msleep(sleep_ms);
192 	}
193 	if (check_psram(rand_val)) {
194 		coex_result.psram_ok = true;
195 	} else {
196 		coex_result.psram_ok = false;
197 	}
198 	while (true) {
199 		k_usleep(1);
200 	}
201 }
202 
psram_init(void)203 static void psram_init(void)
204 {
205 	mem = shared_multi_heap_aligned_alloc(SMH_REG_ATTR_EXTERNAL, 32, SPIRAM_ALLOC_SIZE);
206 	if (!mem) {
207 		TC_ERROR("SPIRAM allocation has failed\n");
208 	}
209 
210 	if (!esp_ptr_external_ram(mem)) {
211 		coex_result.using_ext_ram = false;
212 		TC_ERROR("allocation is not within specified bounds\n");
213 		return;
214 	}
215 
216 	coex_result.using_ext_ram = true;
217 	psram_test();
218 }
219 
flash_test(void)220 static void flash_test(void)
221 {
222 	uint32_t sleep_ms = 15;
223 
224 	for (size_t i = 0; i < FLASH_ITERATIONS; ++i) {
225 		page_erase();
226 		if (buffer_ready) {
227 			k_spinlock_key_t key = k_spin_lock(&lock);
228 
229 			do_flashop();
230 			buffer_ready = false;
231 			k_spin_unlock(&lock, key);
232 		}
233 		k_msleep(sleep_ms);
234 	}
235 	needs_fill = false;
236 	page_read();
237 	check_flash();
238 	while (true) {
239 		k_usleep(1);
240 	}
241 }
242 
flash_init(void)243 static void flash_init(void)
244 {
245 	if (!device_is_ready(flash_dev)) {
246 		TC_ERROR("flash controller not ready\n");
247 	}
248 	flash_test();
249 }
250 
ZTEST(cache_coex,test_using_spiram)251 ZTEST(cache_coex, test_using_spiram)
252 {
253 	zassert_equal(true, coex_result.using_ext_ram, "external RAM is not being used");
254 }
255 
ZTEST(cache_coex,test_flash_integrity)256 ZTEST(cache_coex, test_flash_integrity)
257 {
258 	zassert_equal(FLASH_READBACK_LEN, coex_result.flash_cnt, "flash integrity test failed");
259 }
260 
ZTEST(cache_coex,test_ram_integrity)261 ZTEST(cache_coex, test_ram_integrity)
262 {
263 	zassert_equal(true, coex_result.psram_ok, "SPIRAM integrity test failed");
264 }
265 
cache_coex_setup(void)266 void *cache_coex_setup(void)
267 {
268 	while (unfinished_tasks) {
269 		k_usleep(1);
270 	}
271 
272 	return NULL;
273 }
274 
275 K_THREAD_DEFINE(psram_id, STACKSIZE, psram_init, NULL, NULL, NULL, PRIORITY, 0, 0);
276 K_THREAD_DEFINE(flash_id, STACKSIZE, flash_init, NULL, NULL, NULL, PRIORITY, 0, 0);
277 K_THREAD_DEFINE(buffer_id, STACKSIZE, buffer_fill, NULL, NULL, NULL, PRIORITY, 0, 0);
278 
279 ZTEST_SUITE(cache_coex, NULL, cache_coex_setup, NULL, NULL, NULL);
280