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