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