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