1 #include "pico/test.h"
2 #include "pico/test/xrand.h"
3
4 #include "hardware/sync.h"
5 #include "hardware/sync/spin_lock.h"
6 #include "pico/multicore.h"
7 #include "pico/stdio.h"
8
9 #include <stdio.h>
10
11 uint counter_local[NUM_CORES][NUM_SPIN_LOCKS];
12 uint counter_shared[NUM_SPIN_LOCKS];
13
14 typedef struct test {
15 const char *name;
16 void (*prepare)();
17 void (*run_per_core)();
18 // Return true for ok:
19 bool (*check)();
20 } test_t;
21
22 // Increase until fear turns to boredom:
23 static const uint ITERATIONS = 0x40000;
24
prepare_clear_counters(void)25 void prepare_clear_counters(void) {
26 __mem_fence_acquire();
27 for (int i = 0; i < NUM_SPIN_LOCKS; ++i) {
28 for (int j = 0; j < NUM_CORES; ++j) {
29 counter_local[j][i] = 0;
30 }
31 counter_shared[i] = 0;
32 }
33 __mem_fence_release();
34 }
35
check_counter_sums(void)36 bool check_counter_sums(void) {
37 __mem_fence_acquire();
38 bool all_ok = true;
39 uint full_sum = 0;
40 for (int i = 0; i < NUM_SPIN_LOCKS; ++i) {
41 uint per_lock_sum = 0;
42 for (int j = 0; j < NUM_CORES; ++j) {
43 per_lock_sum += counter_local[j][i];
44 if (counter_local[j][i] > ITERATIONS) {
45 printf("Impossible local counter value %d on core %d: %08x (max %08x)\n",
46 i, j, counter_local[j][i], ITERATIONS);
47 all_ok = false;
48 }
49 }
50 if (per_lock_sum != counter_shared[i]) {
51 printf("Failed sum check for lock %d: expected %08x, actual %08x\n",
52 i, per_lock_sum, counter_shared[i]
53 );
54 all_ok = false;
55 }
56 if (counter_shared[i] > ITERATIONS * NUM_CORES) {
57 printf("Impossible shared counter value %d: %08x (max %08x)\n",
58 i, counter_shared[i], ITERATIONS * NUM_CORES);
59 all_ok = false;
60 }
61 full_sum += per_lock_sum;
62 }
63 if (full_sum != ITERATIONS * NUM_CORES) {
64 printf("Incorrect counter total: expected %08x, got %08x\n",
65 ITERATIONS, full_sum);
66 all_ok = false;
67 }
68 return all_ok;
69 }
70
counter_test_per_core(uint lock_index_mask)71 void counter_test_per_core(uint lock_index_mask) {
72 // Each lock has a global counter. Repeatedly, randomly select a lock and
73 // write to its counter while holding the lock. Also increment a per-core
74 // counter for that lock, so we can check at the end that the per-core
75 // values add up.
76 xrand_state_t state = XRAND_DEFAULT_INIT;
77 uint core_num = get_core_num();
78 for (uint i = 0; i < core_num; ++i) {
79 xrand_jump(&state);
80 }
81 for (uint i = 0; i < ITERATIONS; ++i) {
82 uint lock_index = xrand_next(&state) & lock_index_mask;
83 spin_lock_t *lock = spin_lock_instance(lock_index);
84 uint32_t flags = spin_lock_blocking(lock);
85 counter_shared[lock_index]++;
86 spin_unlock(lock, flags);
87 counter_local[core_num][lock_index]++;
88 busy_wait_at_least_cycles(xrand_next(&state) & 0xffu);
89 }
90 }
91
counter_try_test_per_core(uint lock_index_mask)92 void counter_try_test_per_core(uint lock_index_mask) {
93 // Same as counter_test but use the try_lock variant -- worth testing as
94 // it may be a different asm implementation altogether.
95 xrand_state_t state = XRAND_DEFAULT_INIT;
96 uint core_num = get_core_num();
97 for (uint i = 0; i < core_num; ++i) {
98 xrand_jump(&state);
99 }
100 for (uint i = 0; i < ITERATIONS; ++i) {
101 uint lock_index = xrand_next(&state) & lock_index_mask;
102 spin_lock_t *lock = spin_lock_instance(lock_index);
103 // Assume this test runs without IRQs active
104 while (!spin_try_lock_unsafe(lock))
105 ;
106 counter_shared[lock_index]++;
107 spin_unlock_unsafe(lock);
108 counter_local[core_num][lock_index]++;
109 busy_wait_at_least_cycles(xrand_next(&state) & 0xffu);
110 }
111 }
112
113
114 // Test with successively fewer locks to increase contention
counter_test1(void)115 void counter_test1(void) {
116 counter_test_per_core(NUM_SPIN_LOCKS - 1);
117 }
118
counter_test2(void)119 void counter_test2(void) {
120 counter_test_per_core((NUM_SPIN_LOCKS - 1) >> 1);
121 }
122
counter_test3(void)123 void counter_test3(void) {
124 counter_test_per_core((NUM_SPIN_LOCKS - 1) >> 2);
125 }
126
counter_test4(void)127 void counter_test4(void) {
128 counter_test_per_core((NUM_SPIN_LOCKS - 1) >> 3);
129 }
130
counter_test5(void)131 void counter_test5(void) {
132 counter_test_per_core((NUM_SPIN_LOCKS - 1) >> 4);
133 }
134
counter_try_test1(void)135 void counter_try_test1(void) {
136 counter_try_test_per_core(NUM_SPIN_LOCKS - 1);
137 }
138
counter_try_test2(void)139 void counter_try_test2(void) {
140 counter_try_test_per_core((NUM_SPIN_LOCKS - 1) >> 4);
141 }
142
counter_test_with_irqs(void)143 void counter_test_with_irqs(void) {
144
145 }
146
147
148 static const test_t tests[] = {
149 {
150 "counter test, all locks\n",
151 prepare_clear_counters,
152 counter_test1,
153 check_counter_sums
154 },
155 {
156 "counter test, half of locks\n",
157 prepare_clear_counters,
158 counter_test2,
159 check_counter_sums
160 },
161 {
162 "counter test, 1/4 of locks\n",
163 prepare_clear_counters,
164 counter_test3,
165 check_counter_sums
166 },
167 {
168 "counter test, 1/8 of locks\n",
169 prepare_clear_counters,
170 counter_test4,
171 check_counter_sums
172 },
173 {
174 "counter test, 1/16 of locks\n",
175 prepare_clear_counters,
176 counter_test5,
177 check_counter_sums
178 },
179 {
180 "counter test with try_lock, all locks\n",
181 prepare_clear_counters,
182 counter_try_test1,
183 check_counter_sums
184 },
185 {
186 "counter test with try_lock, 1/16 of locks\n",
187 prepare_clear_counters,
188 counter_try_test2,
189 check_counter_sums
190 },
191 };
192
core1_main(void)193 void core1_main(void) {
194 while (true) {
195 void (*f)() = (void(*)())multicore_fifo_pop_blocking();
196 f();
197 multicore_fifo_push_blocking(0);
198 }
199 }
200
main()201 int main() {
202 stdio_init_all();
203 printf("Hello world\n");
204 multicore_launch_core1(core1_main);
205 uint failed = 0;
206 for (int i = 0; i < count_of(tests); ++i) {
207 const test_t *t = &tests[i];
208 printf(">>> Starting test: %s\n", t->name);
209 spin_locks_reset();
210 t->prepare();
211 multicore_fifo_push_blocking((uintptr_t)t->run_per_core);
212 t->run_per_core();
213 (void)multicore_fifo_pop_blocking();
214 printf(">>> Finished test: %s\n", t->name);
215 if (t->check()) {
216 printf("OK.\n");
217 } else {
218 printf("Failed.\n");
219 ++failed;
220 }
221 }
222 if (failed == 0u) {
223 printf("All tests passed.\n");
224 return 0;
225 } else {
226 printf("%u tests failed. Review log for details.\n", failed);
227 return -1;
228 }
229 }
230