1 /*
2 * Copyright (c) 2022, Synopsys.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * @file
9 * @brief load/store portion of DSP sharing test
10 *
11 * @ingroup all_tests
12 *
13 * This module implements the load/store portion of the DSP sharing test. This
14 * version of this test utilizes a pair of tasks.
15 *
16 * The load/store test validates the dsp unit context
17 * save/restore mechanism. This test utilizes a pair of threads of different
18 * priorities that each use the dsp registers. The context
19 * switching that occurs exercises the kernel's ability to properly preserve
20 * the dsp registers. The test also exercises the kernel's ability
21 * to automatically enable dsp support for a task, if supported.
22 *
23 */
24
25 #include <zephyr/ztest.h>
26 #include <zephyr/debug/gcov.h>
27 #include "dsp_regs_arc.h"
28 #include "dsp_context.h"
29 #include "test_common.h"
30
31 /* space for dsp register load/store area used by low priority task */
32 static struct dsp_register_set dsp_reg_set_load;
33 static struct dsp_register_set dsp_reg_set_store;
34
35 /* space for dsp register load/store area used by high priority thread */
36 static struct dsp_register_set dsp_reg_set;
37
38 static volatile unsigned int load_store_low_count;
39 static volatile unsigned int load_store_high_count;
40
41 /* Indicates that the load/store test exited */
42 static volatile bool test_exited;
43
44 /* Semaphore for signaling end of test */
45 static K_SEM_DEFINE(test_exit_sem, 0, 1);
46
47 /**
48 * @brief Low priority DSP load/store thread
49 *
50 * @ingroup kernel_dspsharing_tests
51 *
52 * @see k_sched_time_slice_set(), memset(),
53 * _load_all_dsp_registers(), _store_all_dsp_registers()
54 */
load_store_low(void)55 static void load_store_low(void)
56 {
57 volatile unsigned int i;
58 bool error = false;
59 unsigned char init_byte;
60 unsigned char *store_ptr = (unsigned char *)&dsp_reg_set_store;
61 unsigned char *load_ptr = (unsigned char *)&dsp_reg_set_load;
62 /*
63 * Initialize dsp load buffer to known values;
64 * these values must be different than the value used in other threads.
65 */
66 init_byte = MAIN_DSP_REG_CHECK_BYTE;
67 for (i = 0; i < SIZEOF_DSP_REGISTER_SET; i++) {
68 load_ptr[i] = init_byte++;
69 }
70
71 /* Loop until the test finishes, or an error is detected. */
72 for (load_store_low_count = 0; !test_exited; load_store_low_count++) {
73
74 /*
75 * Clear store buffer to erase all traces of any previous
76 * dsp values that have been saved.
77 */
78 (void)memset(&dsp_reg_set_store, 0, SIZEOF_DSP_REGISTER_SET);
79
80 /*
81 * Utilize an architecture specific function to load all the
82 * dsp registers with known values.
83 */
84 _load_all_dsp_registers(&dsp_reg_set_load);
85
86 /*
87 * Waste some cycles to give the high priority load/store
88 * thread an opportunity to run when the low priority thread is
89 * using the dsp registers.
90 *
91 * IMPORTANT: This logic requires that sys_clock_tick_get_32() not
92 * perform any dsp operations!
93 */
94 k_busy_wait(100);
95
96 /*
97 * Utilize an architecture specific function to dump the
98 * contents of all dsp registers to memory.
99 */
100 _store_all_dsp_registers(&dsp_reg_set_store);
101 /*
102 * Compare each byte of buffer to ensure the expected value is
103 * present, indicating that the dsp registers weren't
104 * impacted by the operation of the high priority thread(s).
105 *
106 * Display error message and terminate if discrepancies are
107 * detected.
108 */
109 init_byte = MAIN_DSP_REG_CHECK_BYTE;
110
111 for (i = 0; i < SIZEOF_DSP_REGISTER_SET; i++) {
112 if (store_ptr[i] != init_byte) {
113 TC_ERROR("Found 0x%x instead of 0x%x @offset 0x%x\n",
114 store_ptr[i], init_byte, i);
115 TC_ERROR("Discrepancy found during iteration %d\n",
116 load_store_low_count);
117 error = true;
118 }
119 init_byte++;
120 }
121
122 /* Terminate if a test error has been reported */
123 zassert_false(error, NULL);
124 }
125 }
126
127 /**
128 * @brief High priority DSP load/store thread
129 *
130 * @ingroup kernel_dspsharing_tests
131 *
132 * @see _load_then_store_all_dsp_registers()
133 */
load_store_high(void)134 static void load_store_high(void)
135 {
136 volatile unsigned int i;
137 unsigned char init_byte;
138 unsigned char *reg_set_ptr = (unsigned char *)&dsp_reg_set;
139
140 /* Run the test until the specified maximum test count is reached */
141 for (load_store_high_count = 0;
142 load_store_high_count <= MAX_TESTS;
143 load_store_high_count++) {
144
145 /*
146 * Initialize the dsp_reg_set structure by treating it as
147 * a simple array of bytes (the arrangement and actual number
148 * of registers is not important for this generic C code). The
149 * structure is initialized by using the byte value specified
150 * by the constant FIBER_DSP_REG_CHECK_BYTE, and then
151 * incrementing the value for each successive location in the
152 * dsp_reg_set structure.
153 *
154 * The initial byte value, and thus the contents of the entire
155 * dsp_reg_set structure, must be different for each
156 * thread to effectively test the kernel's ability to
157 * properly save/restore the dsp-processed values during a
158 * context switch.
159 */
160 init_byte = FIBER_DSP_REG_CHECK_BYTE;
161
162 for (i = 0; i < SIZEOF_DSP_REGISTER_SET; i++) {
163 reg_set_ptr[i] = init_byte++;
164 }
165
166 /*
167 * Utilize an architecture specific function to load all the
168 * dsp registers with the contents of the
169 * dsp_reg_set structure.
170 *
171 * The goal of the loading all dsp registers with
172 * values that differ from the values used in other threads is
173 * to help determine whether the dsp register
174 * save/restore mechanism in the kernel's context switcher
175 * is operating correctly.
176 *
177 * When a subsequent k_timer_test() invocation is
178 * performed, a (cooperative) context switch back to the
179 * preempted task will occur. This context switch should result
180 * in restoring the state of the task's dsp
181 * registers when the task was swapped out due to the
182 * occurrence of the timer tick.
183 */
184 _load_then_store_all_dsp_registers(&dsp_reg_set);
185
186 /*
187 * Relinquish the processor for the remainder of the current
188 * system clock tick, so that lower priority threads get a
189 * chance to run.
190 *
191 * This exercises the ability of the kernel to restore the
192 * DSP state of a low priority thread _and_ the ability of the
193 * kernel to provide a "clean" DSP state to this thread
194 * once the sleep ends.
195 */
196 k_sleep(K_MSEC(1));
197
198 /* Periodically issue progress report */
199 if ((load_store_high_count % 100) == 0) {
200 PRINT_DATA("Load and store OK after %u (high) "
201 "+ %u (low) tests\n",
202 load_store_high_count,
203 load_store_low_count);
204 }
205 }
206
207 /* Signal end of test */
208 test_exited = true;
209 k_sem_give(&test_exit_sem);
210 }
211
212 K_THREAD_DEFINE(load_low, THREAD_STACK_SIZE, load_store_low, NULL, NULL, NULL,
213 THREAD_LOW_PRIORITY, THREAD_DSP_FLAGS, K_TICKS_FOREVER);
214
215 K_THREAD_DEFINE(load_high, THREAD_STACK_SIZE, load_store_high, NULL, NULL, NULL,
216 THREAD_HIGH_PRIORITY, THREAD_DSP_FLAGS, K_TICKS_FOREVER);
217
ZTEST(dsp_sharing,test_load_store)218 ZTEST(dsp_sharing, test_load_store)
219 {
220 /* Initialise test states */
221 test_exited = false;
222 k_sem_reset(&test_exit_sem);
223
224 /* Start test threads */
225 k_thread_start(load_low);
226 k_thread_start(load_high);
227
228 /* Wait for test threads to exit */
229 k_sem_take(&test_exit_sem, K_FOREVER);
230 }
231