1 /*
2  * Copyright (c) 2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/ztest.h>
7 #include <zephyr/types.h>
8 #include <zephyr/sys/time_units.h>
9 #include <zephyr/random/random.h>
10 
11 #define NUM_RANDOM 100
12 
13 enum units { UNIT_ticks, UNIT_cyc, UNIT_ms, UNIT_us, UNIT_ns };
14 
15 enum round { ROUND_floor, ROUND_ceil, ROUND_near };
16 
17 static const char *const round_s[] = {
18 	[ROUND_floor] = "floor",
19 	[ROUND_ceil] = "ceil",
20 	[ROUND_near] = "near",
21 };
22 
23 struct test_rec {
24 	enum units src;
25 	enum units dst;
26 	int precision; /* 32 or 64 */
27 	enum round round;
28 	void *func;
29 };
30 
31 #define TESTREC(src, dst, round, prec) { \
32 		UNIT_##src, UNIT_##dst, prec, ROUND_##round,		\
33 			(void *)test_##src##_to_##dst##_##round##prec	\
34 	}								\
35 
36 #ifdef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME
37 #define TESTVAR(src, dst, round, prec)
38 #else
39 #define TESTVAR(src, dst, round, prec)					\
40 	uint##prec##_t test_##src##_to_##dst##_##round##prec##_val =	\
41 		k_##src##_to_##dst##_##round##prec(42);
42 #endif
43 
44 #define TESTFUNC(src, dst, round, prec)					\
45 	TESTVAR(src, dst, round, prec)					\
46 	static uint##prec##_t test_##src##_to_##dst##_##round##prec(uint##prec##_t t) { \
47 		return k_##src##_to_##dst##_##round##prec(t);		\
48 	}
49 
50 TESTFUNC(ms, cyc, floor, 32)
51 TESTFUNC(ms, cyc, floor, 64)
52 TESTFUNC(ms, cyc, near, 32)
53 TESTFUNC(ms, cyc, near, 64)
54 TESTFUNC(ms, cyc, ceil, 32)
55 TESTFUNC(ms, cyc, ceil, 64)
56 TESTFUNC(ms, ticks, floor, 32)
57 TESTFUNC(ms, ticks, floor, 64)
58 TESTFUNC(ms, ticks, near, 32)
59 TESTFUNC(ms, ticks, near, 64)
60 TESTFUNC(ms, ticks, ceil, 32)
61 TESTFUNC(ms, ticks, ceil, 64)
62 TESTFUNC(us, cyc, floor, 32)
63 TESTFUNC(us, cyc, floor, 64)
64 TESTFUNC(us, cyc, near, 32)
65 TESTFUNC(us, cyc, near, 64)
66 TESTFUNC(us, cyc, ceil, 32)
67 TESTFUNC(us, cyc, ceil, 64)
68 TESTFUNC(us, ticks, floor, 32)
69 TESTFUNC(us, ticks, floor, 64)
70 TESTFUNC(us, ticks, near, 32)
71 TESTFUNC(us, ticks, near, 64)
72 TESTFUNC(us, ticks, ceil, 32)
73 TESTFUNC(us, ticks, ceil, 64)
74 TESTFUNC(cyc, ms, floor, 32)
75 TESTFUNC(cyc, ms, floor, 64)
76 TESTFUNC(cyc, ms, near, 32)
77 TESTFUNC(cyc, ms, near, 64)
78 TESTFUNC(cyc, ms, ceil, 32)
79 TESTFUNC(cyc, ms, ceil, 64)
80 TESTFUNC(cyc, us, floor, 32)
81 TESTFUNC(cyc, us, floor, 64)
82 TESTFUNC(cyc, us, near, 32)
83 TESTFUNC(cyc, us, near, 64)
84 TESTFUNC(cyc, us, ceil, 32)
85 TESTFUNC(cyc, us, ceil, 64)
86 TESTFUNC(cyc, ticks, floor, 32)
87 TESTFUNC(cyc, ticks, floor, 64)
88 TESTFUNC(cyc, ticks, near, 32)
89 TESTFUNC(cyc, ticks, near, 64)
90 TESTFUNC(cyc, ticks, ceil, 32)
91 TESTFUNC(cyc, ticks, ceil, 64)
92 TESTFUNC(ticks, ms, floor, 32)
93 TESTFUNC(ticks, ms, floor, 64)
94 TESTFUNC(ticks, ms, near, 32)
95 TESTFUNC(ticks, ms, near, 64)
96 TESTFUNC(ticks, ms, ceil, 32)
97 TESTFUNC(ticks, ms, ceil, 64)
98 TESTFUNC(ticks, us, floor, 32)
99 TESTFUNC(ticks, us, floor, 64)
100 TESTFUNC(ticks, us, near, 32)
101 TESTFUNC(ticks, us, near, 64)
102 TESTFUNC(ticks, us, ceil, 32)
103 TESTFUNC(ticks, us, ceil, 64)
104 TESTFUNC(ticks, cyc, floor, 32)
105 TESTFUNC(ticks, cyc, floor, 64)
106 TESTFUNC(ticks, cyc, near, 32)
107 TESTFUNC(ticks, cyc, near, 64)
108 TESTFUNC(ticks, cyc, ceil, 32)
109 TESTFUNC(ticks, cyc, ceil, 64)
110 TESTFUNC(ns, cyc, floor, 32)
111 TESTFUNC(ns, cyc, floor, 64)
112 TESTFUNC(ns, cyc, near, 32)
113 TESTFUNC(ns, cyc, near, 64)
114 TESTFUNC(ns, cyc, ceil, 32)
115 TESTFUNC(ns, cyc, ceil, 64)
116 TESTFUNC(ns, ticks, floor, 32)
117 TESTFUNC(ns, ticks, floor, 64)
118 TESTFUNC(ns, ticks, near, 32)
119 TESTFUNC(ns, ticks, near, 64)
120 TESTFUNC(ns, ticks, ceil, 32)
121 TESTFUNC(ns, ticks, ceil, 64)
122 TESTFUNC(cyc, ns, floor, 32)
123 TESTFUNC(cyc, ns, floor, 64)
124 TESTFUNC(cyc, ns, near, 32)
125 TESTFUNC(cyc, ns, near, 64)
126 TESTFUNC(cyc, ns, ceil, 32)
127 TESTFUNC(cyc, ns, ceil, 64)
128 TESTFUNC(ticks, ns, floor, 32)
129 TESTFUNC(ticks, ns, floor, 64)
130 TESTFUNC(ticks, ns, near, 32)
131 TESTFUNC(ticks, ns, near, 64)
132 TESTFUNC(ticks, ns, ceil, 32)
133 TESTFUNC(ticks, ns, ceil, 64)
134 
135 static struct test_rec tests[] = {
136 	 TESTREC(ms, cyc, floor, 32),
137 	 TESTREC(ms, cyc, floor, 64),
138 	 TESTREC(ms, cyc, near, 32),
139 	 TESTREC(ms, cyc, near, 64),
140 	 TESTREC(ms, cyc, ceil, 32),
141 	 TESTREC(ms, cyc, ceil, 64),
142 	 TESTREC(ms, ticks, floor, 32),
143 	 TESTREC(ms, ticks, floor, 64),
144 	 TESTREC(ms, ticks, near, 32),
145 	 TESTREC(ms, ticks, near, 64),
146 	 TESTREC(ms, ticks, ceil, 32),
147 	 TESTREC(ms, ticks, ceil, 64),
148 	 TESTREC(us, cyc, floor, 32),
149 	 TESTREC(us, cyc, floor, 64),
150 	 TESTREC(us, cyc, near, 32),
151 	 TESTREC(us, cyc, near, 64),
152 	 TESTREC(us, cyc, ceil, 32),
153 	 TESTREC(us, cyc, ceil, 64),
154 	 TESTREC(us, ticks, floor, 32),
155 	 TESTREC(us, ticks, floor, 64),
156 	 TESTREC(us, ticks, near, 32),
157 	 TESTREC(us, ticks, near, 64),
158 	 TESTREC(us, ticks, ceil, 32),
159 	 TESTREC(us, ticks, ceil, 64),
160 	 TESTREC(cyc, ms, floor, 32),
161 	 TESTREC(cyc, ms, floor, 64),
162 	 TESTREC(cyc, ms, near, 32),
163 	 TESTREC(cyc, ms, near, 64),
164 	 TESTREC(cyc, ms, ceil, 32),
165 	 TESTREC(cyc, ms, ceil, 64),
166 	 TESTREC(cyc, us, floor, 32),
167 	 TESTREC(cyc, us, floor, 64),
168 	 TESTREC(cyc, us, near, 32),
169 	 TESTREC(cyc, us, near, 64),
170 	 TESTREC(cyc, us, ceil, 32),
171 	 TESTREC(cyc, us, ceil, 64),
172 	 TESTREC(cyc, ticks, floor, 32),
173 	 TESTREC(cyc, ticks, floor, 64),
174 	 TESTREC(cyc, ticks, near, 32),
175 	 TESTREC(cyc, ticks, near, 64),
176 	 TESTREC(cyc, ticks, ceil, 32),
177 	 TESTREC(cyc, ticks, ceil, 64),
178 	 TESTREC(ticks, ms, floor, 32),
179 	 TESTREC(ticks, ms, floor, 64),
180 	 TESTREC(ticks, ms, near, 32),
181 	 TESTREC(ticks, ms, near, 64),
182 	 TESTREC(ticks, ms, ceil, 32),
183 	 TESTREC(ticks, ms, ceil, 64),
184 	 TESTREC(ticks, us, floor, 32),
185 	 TESTREC(ticks, us, floor, 64),
186 	 TESTREC(ticks, us, near, 32),
187 	 TESTREC(ticks, us, near, 64),
188 	 TESTREC(ticks, us, ceil, 32),
189 	 TESTREC(ticks, us, ceil, 64),
190 	 TESTREC(ticks, cyc, floor, 32),
191 	 TESTREC(ticks, cyc, floor, 64),
192 	 TESTREC(ticks, cyc, near, 32),
193 	 TESTREC(ticks, cyc, near, 64),
194 	 TESTREC(ticks, cyc, ceil, 32),
195 	 TESTREC(ticks, cyc, ceil, 64),
196 	 TESTREC(ns, cyc, floor, 32),
197 	 TESTREC(ns, cyc, floor, 64),
198 	 TESTREC(ns, cyc, near, 32),
199 	 TESTREC(ns, cyc, near, 64),
200 	 TESTREC(ns, cyc, ceil, 32),
201 	 TESTREC(ns, cyc, ceil, 64),
202 	 TESTREC(ns, ticks, floor, 32),
203 	 TESTREC(ns, ticks, floor, 64),
204 	 TESTREC(ns, ticks, near, 32),
205 	 TESTREC(ns, ticks, near, 64),
206 	 TESTREC(ns, ticks, ceil, 32),
207 	 TESTREC(ns, ticks, ceil, 64),
208 	 TESTREC(cyc, ns, floor, 32),
209 	 TESTREC(cyc, ns, floor, 64),
210 	 TESTREC(cyc, ns, near, 32),
211 	 TESTREC(cyc, ns, near, 64),
212 	 TESTREC(cyc, ns, ceil, 32),
213 	 TESTREC(cyc, ns, ceil, 64),
214 	 TESTREC(ticks, ns, floor, 32),
215 	 TESTREC(ticks, ns, floor, 64),
216 	 TESTREC(ticks, ns, near, 32),
217 	 TESTREC(ticks, ns, near, 64),
218 	 TESTREC(ticks, ns, ceil, 32),
219 	 TESTREC(ticks, ns, ceil, 64),
220 	};
221 
get_hz(enum units u)222 uint32_t get_hz(enum units u)
223 {
224 	if (u == UNIT_ticks) {
225 		return CONFIG_SYS_CLOCK_TICKS_PER_SEC;
226 	} else if (u == UNIT_cyc) {
227 		return sys_clock_hw_cycles_per_sec();
228 	} else if (u == UNIT_ms) {
229 		return 1000;
230 	} else if (u == UNIT_us) {
231 		return 1000000;
232 	} else if (u == UNIT_ns) {
233 		return 1000000000;
234 	}
235 	__ASSERT(0, "");
236 	return 0;
237 }
238 
test_conversion(struct test_rec * t,uint64_t val)239 static void test_conversion(struct test_rec *t, uint64_t val)
240 {
241 	uint32_t from_hz = get_hz(t->src), to_hz = get_hz(t->dst);
242 	uint64_t result;
243 
244 	if (t->precision == 32) {
245 		uint32_t (*convert)(uint32_t) = (uint32_t (*)(uint32_t)) t->func;
246 
247 		result = convert((uint32_t) val);
248 
249 		/* If the input value legitimately overflows, then
250 		 * there is nothing to test
251 		 */
252 		if ((val * to_hz) >= ((((uint64_t)from_hz) << 32))) {
253 			return;
254 		}
255 	} else {
256 		uint64_t (*convert)(uint64_t) = (uint64_t (*)(uint64_t)) t->func;
257 
258 		result = convert(val);
259 	}
260 
261 	/* We expect the ideal result to be equal to "val * to_hz /
262 	 * from_hz", but that division is the source of precision
263 	 * issues.  So reexpress our equation as:
264 	 *
265 	 *    val * to_hz ==? result * from_hz
266 	 *              0 ==? val * to_hz - result * from_hz
267 	 *
268 	 * The difference is allowed to be in the range [0:from_hz) if
269 	 * we are rounding down, from (-from_hz:0] if we are rounding
270 	 * up, or [-from_hz/2:from_hz/2] if we are rounding to the
271 	 * nearest.
272 	 */
273 	int64_t diff = (int64_t)(val * to_hz - result * from_hz);
274 	int64_t maxdiff, mindiff;
275 
276 	if (t->round == ROUND_floor) {
277 		maxdiff = from_hz - 1;
278 		mindiff = 0;
279 	} else if (t->round == ROUND_ceil) {
280 		maxdiff = 0;
281 		mindiff = -(int64_t)(from_hz-1);
282 	} else {
283 		maxdiff = from_hz/2;
284 		mindiff = -(int64_t)(from_hz/2);
285 	}
286 
287 	zassert_true(diff <= maxdiff && diff >= mindiff,
288 		     "Convert %llu (%llx) from %u Hz to %u Hz %u-bit %s\n"
289 		     "result %llu (%llx) diff %lld (%llx) should be in [%lld:%lld]",
290 		     val, val, from_hz, to_hz, t->precision, round_s[t->round],
291 		     result, result, diff, diff, mindiff, maxdiff);
292 }
293 
ZTEST(timer_api,test_time_conversions)294 ZTEST(timer_api, test_time_conversions)
295 {
296 	for (int i = 0; i < ARRAY_SIZE(tests); i++) {
297 		test_conversion(&tests[i], 0);
298 		test_conversion(&tests[i], 1);
299 		test_conversion(&tests[i], 0x7fffffff);
300 		test_conversion(&tests[i], 0x80000000);
301 		test_conversion(&tests[i], 0xfffffff0);
302 		if (tests[i].precision == 64) {
303 			test_conversion(&tests[i], 0xffffffff);
304 			test_conversion(&tests[i], 0x100000000ULL);
305 		}
306 
307 		for (int j = 0; j < NUM_RANDOM; j++) {
308 			test_conversion(&tests[i], sys_rand32_get());
309 		}
310 	}
311 }
312