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