Mistake on this page?
Report an issue in GitHub or email us
timeout_tests.h
1 /* mbed Microcontroller Library
2  * Copyright (c) 2017 ARM Limited
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #ifndef MBED_TIMEOUT_TESTS_H
18 #define MBED_TIMEOUT_TESTS_H
19 
20 #include "mbed.h"
21 #include "unity/unity.h"
22 
23 using namespace std::chrono;
24 
25 #define NUM_TIMEOUTS 16
26 const microseconds DRIFT_TEST_PERIOD = 10ms;
27 
28 const milliseconds TEST_DELAY = 10ms;
29 
30 /* Timeouts are quite arbitrary due to large number of boards with varying level of accuracy */
31 #define LONG_DELTA_US (100000)
32 #define SHORT_DELTA_US (2000)
33 
34 void sem_callback(Semaphore *sem)
35 {
36  sem->release();
37 }
38 
39 void cnt_callback(volatile uint32_t *cnt)
40 {
41  (*cnt)++;
42 }
43 
44 /** Template for tests: callback called once
45  *
46  * Test callback called once
47  * Given a Timeout object with a callback attached with @a attach()
48  * When given time elapses
49  * Then the callback is called exactly one time
50  *
51  * Test callback called once
52  * Given a Timeout object with a callback attached with @a attach_us()
53  * When given time elapses
54  * Then the callback is called exactly one time
55  */
56 template<typename T>
57 void test_single_call(void)
58 {
59  Semaphore sem(0, 1);
60  T timeout;
61 
62  timeout.attach(mbed::callback(sem_callback, &sem), TEST_DELAY);
63 
64  bool acquired = sem.try_acquire();
65  TEST_ASSERT_FALSE(acquired);
66 
67  acquired = sem.try_acquire_for(TEST_DELAY + 2ms);
68  TEST_ASSERT_TRUE(acquired);
69 
70  acquired = sem.try_acquire_for(TEST_DELAY + 2ms);
71  TEST_ASSERT_FALSE(acquired);
72 
73  timeout.detach();
74 }
75 
76 /** Template for tests: callback not called when cancelled
77  *
78  * Test callback not called when cancelled
79  * Given a Timeout object with a callback attached with @a attach()
80  * When the callback is detached before being called
81  * Then the callback is never called
82  *
83  * Test callback not called when cancelled
84  * Given a Timeout object with a callback attached with @a attach_us()
85  * When the callback is detached before being called
86  * Then the callback is never called
87  */
88 template<typename T>
89 void test_cancel(void)
90 {
91  Semaphore sem(0, 1);
92  T timeout;
93 
94  timeout.attach(mbed::callback(sem_callback, &sem), 2 * TEST_DELAY);
95 
96  bool acquired = sem.try_acquire_for(TEST_DELAY);
97  TEST_ASSERT_FALSE(acquired);
98  timeout.detach();
99 
100  acquired = sem.try_acquire_for(TEST_DELAY + 2ms);
101  TEST_ASSERT_FALSE(acquired);
102 }
103 
104 /** Template for tests: callback override
105  *
106  * Test callback override
107  * Given a Timeout object with a callback attached with @a attach()
108  * When another callback is attached before first one is called
109  * and second callback's delay elapses
110  * Then the second callback is called
111  * and the first callback is never called
112  *
113  * Test callback override
114  * Given a Timeout object with a callback attached with @a attach_us()
115  * When another callback is attached before first one is called
116  * and second callback's delay elapses
117  * Then the second callback is called
118  * and the first callback is never called
119  */
120 template<typename T>
121 void test_override(void)
122 {
123  Semaphore sem1(0, 1);
124  Semaphore sem2(0, 1);
125  T timeout;
126 
127  timeout.attach(mbed::callback(sem_callback, &sem1), 2 * TEST_DELAY);
128 
129  bool acquired = sem1.try_acquire_for(TEST_DELAY);
130  TEST_ASSERT_FALSE(acquired);
131  timeout.attach(mbed::callback(sem_callback, &sem2), 2 * TEST_DELAY);
132 
133  acquired = sem2.try_acquire_for(2 * TEST_DELAY + 2ms);
134  TEST_ASSERT_TRUE(acquired);
135  acquired = sem1.try_acquire();
136  TEST_ASSERT_FALSE(acquired);
137 
138  timeout.detach();
139 }
140 
141 /** Template for tests: multiple Timeouts
142  *
143  * Test multiple Timeouts
144  * Given multiple separate Timeout objects
145  * When a callback is attached to all of these Timeout objects with @a attach()
146  * and delay for every Timeout elapses
147  * Then all callbacks are called
148  *
149  * Test multiple Timeouts
150  * Given multiple separate Timeout objects
151  * When a callback is attached to all of these Timeout objects with @a attach_us()
152  * and delay for every Timeout elapses
153  * Then all callbacks are called
154  */
155 template<typename T>
156 void test_multiple(void)
157 {
158  volatile uint32_t callback_count = 0;
159  T timeouts[NUM_TIMEOUTS];
160  for (size_t i = 0; i < NUM_TIMEOUTS; i++) {
161  timeouts[i].attach(mbed::callback(cnt_callback, &callback_count), TEST_DELAY);
162  }
163  ThisThread::sleep_for(TEST_DELAY + 2ms);
164  TEST_ASSERT_EQUAL(NUM_TIMEOUTS, callback_count);
165 }
166 
167 /** Template for tests: zero delay
168  *
169  * Test zero delay
170  * Given a Timeout object
171  * When a callback is attached with 0.0 s delay, with @a attach()
172  * Then the callback is called instantly
173  *
174  * Test zero delay
175  * Given a Timeout object
176  * When a callback is attached with 0.0 s delay, with @a attach_us()
177  * Then the callback is called instantly
178  */
179 template<typename T>
180 void test_no_wait(void)
181 {
182  for (int i = 0; i < 100; i++) {
183  Semaphore sem(0, 1);
184  T timeout;
185  timeout.attach(mbed::callback(sem_callback, &sem), 0s);
186  bool sem_acquired = sem.try_acquire();
187  TEST_ASSERT_EQUAL(true, sem_acquired);
188  timeout.detach();
189  }
190 }
191 
192 /** Template for tests: accuracy of timeout delay
193  *
194  * Test delay accuracy
195  * Given a Timeout object with a callback attached with @a attach()
196  * When the callback is called
197  * Then elapsed time matches given delay
198  *
199  * Test delay accuracy
200  * Given a Timeout object with a callback attached with @a attach_us()
201  * When the callback is called
202  * Then elapsed time matches given delay
203  */
204 template<typename T, us_timestamp_t delay_us, us_timestamp_t delta_us>
205 void test_delay_accuracy(void)
206 {
207  Semaphore sem(0, 1);
208  T timeout;
209  Timer timer;
210 
211  timer.start();
212  timeout.attach(mbed::callback(sem_callback, &sem), microseconds(delay_us));
213 
214  sem.acquire();
215  timer.stop();
216  TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.elapsed_time().count());
217 
218  timeout.detach();
219 }
220 
221 #if DEVICE_SLEEP
222 /** Template for tests: timeout during sleep
223  *
224  * Test timeout during sleep
225  * Given a Timeout object with a callback attached with @a attach()
226  * and the uC in a sleep mode
227  * When given delay elapses
228  * Then the callback is called
229  * and elapsed time matches given delay
230  *
231  * Test timeout during sleep
232  * Given a Timeout object with a callback attached with @a attach_us()
233  * and the uC in a sleep mode
234  * When given delay elapses
235  * Then the callback is called
236  * and elapsed time matches given delay
237  */
238 template<typename T, us_timestamp_t delay_us, us_timestamp_t delta_us>
239 void test_sleep(void)
240 {
241  Semaphore sem(0, 1);
242  T timeout;
243  Timer timer;
244 
245  sleep_manager_lock_deep_sleep();
246  timer.start();
247  timeout.attach(mbed::callback(sem_callback, &sem), microseconds(delay_us));
248 
249  bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
250  TEST_ASSERT_FALSE_MESSAGE(deep_sleep_allowed, "Deep sleep should be disallowed");
251  sem.acquire();
252  timer.stop();
253 
254  sleep_manager_unlock_deep_sleep();
255  TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.elapsed_time().count());
256 
257  timeout.detach();
258 }
259 
260 #if DEVICE_LPTICKER
261 /** Template for tests: timeout during deepsleep
262  *
263  * Test timeout during deepsleep
264  * Given a LowPowerTimeout object with a callback attached with @a attach()
265  * and the uC in a deepsleep mode
266  * When given delay elapses
267  * Then the callback is called
268  * and elapsed time matches given delay
269  *
270  * Test timeout during deepsleep
271  * Given a LowPowerTimeout object with a callback attached with @a attach_us()
272  * and the uC in a deepsleep mode
273  * When given delay elapses
274  * Then the callback is called
275  * and elapsed time matches given delay
276  */
277 template<typename T, us_timestamp_t delay_us, us_timestamp_t delta_us>
278 void test_deepsleep(void)
279 {
280  Semaphore sem(0, 1);
281  T timeout;
282  /*
283  * We use here the low power timer instead of microsecond timer for start and
284  * end because the microseconds timer might be disabled during deepsleep.
285  */
286  LowPowerTimer timer;
287 
288  /*
289  * Since deepsleep() may shut down the UART peripheral, we wait for 20ms
290  * to allow for hardware serial buffers to completely flush.
291  *
292  * Take NUMAKER_PFM_NUC472 as an example:
293  * Its UART peripheral has 16-byte Tx FIFO. With baud rate set to 9600, flush
294  * Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 (ms). So set wait time to
295  * 20ms here for safe.
296 
297  * This should be replaced with a better function that checks if the
298  * hardware buffers are empty. However, such an API does not exist now,
299  * so we'll use the ThisThread::sleep_for() function for now.
300  */
301  ThisThread::sleep_for(20ms);
302 
303  timer.start();
304  timeout.attach(mbed::callback(sem_callback, &sem), microseconds(delay_us));
305 
306  bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
307  TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
308  sem.acquire();
309  timer.stop();
310 
311  TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.elapsed_time().count());
312 
313  timeout.detach();
314 }
315 #endif
316 #endif
317 
318 template<typename TimeoutTesterType>
320 public:
321  TimeoutDriftTester(microseconds period = 1ms) :
322  _callback_count(0), _period(period), _timeout()
323  {
324  }
325 
326  void reschedule_callback(void)
327  {
328  _timeout.attach(mbed::callback(this, &TimeoutDriftTester::reschedule_callback), _period);
329  _callback_count++;
330  }
331 
332  void detach_callback(void)
333  {
334  _timeout.detach();
335  }
336 
337  uint32_t get_callback_count(void)
338  {
339  return _callback_count;
340  }
341 
342 private:
343  volatile uint32_t _callback_count;
344  microseconds _period;
345  TimeoutTesterType _timeout;
346 };
347 
348 /** Template for tests: rescheduling
349  *
350  * Given a Timeout object
351  * When a callback is attached with that reattaches itself, with @a attach()
352  * Then the callback is called repeatedly
353  *
354  * Given a Timeout object
355  * When a callback is attached with that reattaches itself, with @a attach_us()
356  * Then the callback is called repeatedly
357  */
358 template<typename T>
359 void test_reschedule(void)
360 {
361  TimeoutDriftTester<T> timeout(TEST_DELAY);
362 
363  timeout.reschedule_callback();
364  ThisThread::sleep_for(TEST_DELAY * 5);
365  TEST_ASSERT(timeout.get_callback_count() >= 3);
366 }
367 
368 /** Template for tests: accuracy of timeout delay scheduled repeatedly
369  *
370  * Test time drift -- device part
371  * Given a Timeout object with a callback repeatedly attached with @a attach()
372  * When the testing host computes test duration based on values received from uC
373  * Then computed time and actual time measured by host are equal within given tolerance
374  *
375  * Test time drift -- device part
376  * Given a Timeout object with a callback repeatedly attached with @a attach_us()
377  * When the testing host computes test duration based on values received from uC
378  * Then computed time and actual time measured by host are equal within given tolerance
379  *
380  * Original description:
381  * 1) DUT would start to update callback_trigger_count every milli sec
382  * 2) Host would query what is current count base_time, Device responds by the callback_trigger_count
383  * 3) Host after waiting for measurement stretch. It will query for device time again final_time.
384  * 4) Host computes the drift considering base_time, final_time, transport delay and measurement stretch
385  * 5) Finally host send the results back to device pass/fail based on tolerance.
386  * 6) More details on tests can be found in timing_drift_auto.py
387  */
388 template<typename T>
389 void test_drift(void)
390 {
391  char _key[11] = { };
392  char _value[128] = { };
393  int expected_key = 1;
394  TimeoutDriftTester<T> timeout(DRIFT_TEST_PERIOD);
395 
396  greentea_send_kv("timing_drift_check_start", 0);
397  timeout.reschedule_callback();
398 
399  // wait for 1st signal from host
400  do {
401  greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
402  expected_key = strcmp(_key, "base_time");
403  } while (expected_key);
404 
405  greentea_send_kv(_key, timeout.get_callback_count() * DRIFT_TEST_PERIOD.count());
406 
407  // wait for 2nd signal from host
408  greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
409  greentea_send_kv(_key, timeout.get_callback_count() * DRIFT_TEST_PERIOD.count());
410 
411  timeout.detach_callback();
412 
413  //get the results from host
414  greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
415 
416  TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key, "Host script reported a failure");
417 }
418 
419 #endif
Low power timer.
Definition: LowPowerTimer.h:39
Callback< R(ArgTs...)> callback(R(*func)(ArgTs...)=nullptr) noexcept
Create a callback class with type inferred from the arguments.
Definition: Callback.h:678
bool sleep_manager_can_deep_sleep_test_check(void)
Check if the target can deep sleep within a period of time.
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.