1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/ztest.h>
7 #include <zephyr/kernel.h>
8 #include <zephyr/arch/arch_interface.h>
9 
10 #if defined(CONFIG_SMP) && CONFIG_MP_MAX_NUM_CPUS > 1
11 #define SMP_TEST
12 #endif
13 
14 #ifdef SMP_TEST
15 #define MAX_NUM_THREADS CONFIG_MP_MAX_NUM_CPUS
16 #define STACK_SIZE  1024
17 #define PRIORITY    7
18 
19 static struct k_thread threads[MAX_NUM_THREADS];
20 static K_THREAD_STACK_ARRAY_DEFINE(tstack, MAX_NUM_THREADS, STACK_SIZE);
21 #endif
22 
23 #define WAIT_US 1000
24 #define WAIT_NS (WAIT_US * 1000)
25 #define TOLERANCE 0.1
26 
perform_tests(void)27 static void perform_tests(void)
28 {
29 	timing_t start, middle, end;
30 	uint64_t diff1, diff2, diff_all, freq_hz;
31 	uint64_t diff1_ns, diff2_ns, diff_all_ns, diff_avg_ns;
32 	uint32_t freq_mhz;
33 
34 	arch_timing_start();
35 
36 	start = arch_timing_counter_get();
37 	k_busy_wait(WAIT_US);
38 	middle = arch_timing_counter_get();
39 	k_busy_wait(WAIT_US);
40 	end = arch_timing_counter_get();
41 
42 	/* Time shouldn't stop or go backwards */
43 	diff1 = arch_timing_cycles_get(&start, &middle);
44 	diff2 = arch_timing_cycles_get(&middle, &end);
45 	diff_all = arch_timing_cycles_get(&start, &end);
46 	zassert_true(diff1 > 0, NULL);
47 	zassert_true(diff2 > 0, NULL);
48 	zassert_true(diff_all > 0, NULL);
49 
50 	/* Differences shouldn't be so different, as both are spaced by
51 	 * k_busy_wait(WAIT_US).
52 	 */
53 	zassert_within(diff1, diff2, diff1 * TOLERANCE, NULL);
54 	zassert_within(diff_all, diff1 + diff2, (diff1 + diff2) * TOLERANCE,
55 		       NULL);
56 
57 	freq_hz = arch_timing_freq_get();
58 	freq_mhz = arch_timing_freq_get_mhz();
59 	zassert_equal(freq_mhz, (uint32_t)(freq_hz / 1000000ULL), NULL);
60 
61 	diff1_ns = arch_timing_cycles_to_ns(diff1);
62 	diff2_ns = arch_timing_cycles_to_ns(diff2);
63 	diff_all_ns = arch_timing_cycles_to_ns(diff_all);
64 
65 	/* Ensure the differences are close to 100us */
66 	zassert_within(diff1_ns, WAIT_NS, WAIT_NS * TOLERANCE, NULL);
67 	zassert_within(diff2_ns, WAIT_NS, WAIT_NS * TOLERANCE, NULL);
68 	zassert_within(diff_all_ns, 2 * WAIT_NS, 2 * WAIT_NS * TOLERANCE, NULL);
69 
70 	diff_avg_ns = arch_timing_cycles_to_ns_avg(diff1 + diff2, 2);
71 	zassert_within(diff_avg_ns, WAIT_NS, WAIT_NS * TOLERANCE, NULL);
72 
73 	arch_timing_stop();
74 }
75 
timing_setup(void)76 void *timing_setup(void)
77 {
78 	arch_timing_init();
79 
80 	return NULL;
81 }
82 
ZTEST(arch_timing,test_arch_timing)83 ZTEST(arch_timing, test_arch_timing)
84 {
85 	perform_tests();
86 	/* Run tests again to ensure nothing breaks after arch_timing_stop */
87 	perform_tests();
88 }
89 
90 #ifdef SMP_TEST
thread_entry(void * p1,void * p2,void * p3)91 static void thread_entry(void *p1, void *p2, void *p3)
92 {
93 	ARG_UNUSED(p1);
94 	ARG_UNUSED(p2);
95 	ARG_UNUSED(p3);
96 
97 	perform_tests();
98 	/* Run tests again to ensure nothing breaks after arch_timing_stop */
99 	perform_tests();
100 }
101 
ZTEST(arch_timing,test_arch_timing_smp)102 ZTEST(arch_timing, test_arch_timing_smp)
103 {
104 	int i;
105 	unsigned int num_threads = arch_num_cpus();
106 
107 	for (i = 0; i < num_threads; i++) {
108 		k_thread_create(&threads[i], tstack[i], STACK_SIZE,
109 				thread_entry, NULL, NULL, NULL,
110 				PRIORITY, 0, K_FOREVER);
111 		k_thread_cpu_mask_enable(&threads[i], i);
112 		k_thread_start(&threads[i]);
113 	}
114 
115 	for (i = 0; i < num_threads; i++)
116 		k_thread_join(&threads[i], K_FOREVER);
117 }
118 #else
ZTEST(arch_timing,test_arch_timing_smp)119 ZTEST(arch_timing, test_arch_timing_smp)
120 {
121 	ztest_test_skip();
122 }
123 #endif
124 
125 ZTEST_SUITE(arch_timing, NULL, timing_setup, NULL, NULL, NULL);
126