Blame view

RIOT/tests/xtimer_drift/main.c 6 KB
fb11e647   vrobic   reseau statique a...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  /*
   * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
   * Copyright (C) 2015 Eistec AB
   *               2013 INRIA
   *
   * This file is subject to the terms and conditions of the GNU Lesser
   * General Public License v2.1. See the file LICENSE in the top level
   * directory for more details.
   */
  
  /**
   * @ingroup tests
   * @{
   *
   * @file
   * @brief    xtimer_drift test application
   *
   * @author   Kaspar Schleiser <kaspar@schleiser.de>
   * @author   Oliver Hahm <oliver.hahm@inria.fr>
   * @author   Christian Mehlis <mehlis@inf.fu-berlin.de>
   * @author   Joakim Nohlgård <joakim.nohlgard@eistec.se>
   *
   * @}
   */
  
  #include <stdio.h>
  #include <time.h>
  
  #include "xtimer.h"
  #include "thread.h"
  #include "msg.h"
  
  /* We generate some context switching and IPC traffic by using multiple threads
   * and generate some xtimer load by scheduling several messages to be called at
   * different times. TEST_HZ is the frequency of messages being sent from the
   * main thread to the worker, all other message frequencies are derived from
   * TEST_HZ.
   * TEST_MSG_RX_USLEEP is a tiny sleep inside the message reception thread to
   * cause extra context switches.
   */
  #define TEST_HZ (64LU)
  #define TEST_INTERVAL (1000000LU / TEST_HZ)
  #define TEST_MSG_RX_USLEEP (200LU)
  
  char slacker_stack1[THREAD_STACKSIZE_DEFAULT];
  char slacker_stack2[THREAD_STACKSIZE_DEFAULT];
  char worker_stack[THREAD_STACKSIZE_MAIN];
  
  struct timer_msg {
      xtimer_t timer;
      uint32_t interval;
      msg_t msg;
  };
  
  struct timer_msg msg_a = { .interval = (TEST_INTERVAL / 2) };
  struct timer_msg msg_b = { .interval = (TEST_INTERVAL / 3) };
  struct timer_msg msg_c = { .interval = (TEST_INTERVAL * 5) };
  struct timer_msg msg_d = { .interval = (TEST_INTERVAL * 2) };
  
  /* This thread is only here to give the kernel some extra load */
  void *slacker_thread(void *arg)
  {
      (void) arg;
      timex_t now;
  
      printf("Starting thread %" PRIkernel_pid "\n", thread_getpid());
  
      /* we need a queue if the second message arrives while the first is still processed */
      /* without a queue, the message would get lost */
      msg_t msgq[4];
      msg_init_queue(msgq, 4);
  
      while (1) {
          msg_t m;
          msg_receive(&m);
          struct timer_msg *tmsg = m.content.ptr;
          xtimer_now_timex(&now);
          xtimer_usleep(TEST_MSG_RX_USLEEP);
  
          tmsg->msg.type = 12345;
          tmsg->msg.content.ptr = tmsg;
          xtimer_set_msg(&tmsg->timer, tmsg->interval, &tmsg->msg, thread_getpid());
      }
  }
  
  /* This thread will print the drift to stdout once per second */
  void *worker_thread(void *arg)
  {
      (void) arg;
      uint32_t loop_counter = 0;
      uint32_t start = 0;
      uint32_t last = 0;
  
      printf("Starting thread %" PRIkernel_pid "\n", thread_getpid());
  
      while (1) {
          msg_t m;
          msg_receive(&m);
          xtimer_ticks32_t ticks = xtimer_now();
          uint32_t now = xtimer_usec_from_ticks(ticks);
          if (start == 0) {
              start = now;
              last = start;
              ++loop_counter;
              continue;
          }
  
          uint32_t us, sec;
          us = now % SEC_IN_USEC;
          sec = now / SEC_IN_USEC;
          if ((loop_counter % TEST_HZ) == 0) {
              uint32_t expected = start + loop_counter * TEST_INTERVAL;
              int32_t drift = now - expected;
              expected = last + TEST_HZ * TEST_INTERVAL;
              int32_t jitter = now - expected;
              printf("now=%" PRIu32 ".%06" PRIu32 " (0x%08" PRIx32 " ticks), ",
                  sec, us, ticks.ticks32);
              printf("drift=%" PRId32 " us, jitter=%" PRId32 " us\n", drift, jitter);
              last = now;
          }
          ++loop_counter;
      }
  }
  
  int main(void)
  {
      msg_t m;
  
      puts("xtimer_drift test application");
      puts("Make note of the PC clock when starting this test, let run for a while, "
          "compare the printed time against the expected time from the PC clock.");
      puts("The difference is the RIOT timer drift, this is likely caused by either: "
          "an inaccurate hardware timer, or bugs in the software (xtimer or periph/timer).");
      printf("This test will run a periodic timer every %lu microseconds (%lu Hz), ",
          (unsigned long)TEST_INTERVAL, (unsigned long)TEST_HZ);
      puts("The current time will be printed once per second, along with the "
           "difference between the actual and expected xtimer_now value.");
      puts("The first output variable, 'drift', represents the total offset since "
           "start between xtimer_now and the expected time.");
      puts("The second output variable, 'jitter', represents the difference in drift from the last printout.");
      puts("Two other threads are also running only to cause extra interrupts and context switches.");
      puts(" <====== PC clock if running in pyterm.");
      puts("");
      puts(" =======================");
      puts(" ===== Test begins =====");
      puts(" =======================");
  
      kernel_pid_t pid1 = thread_create(
          slacker_stack1,
          sizeof(slacker_stack1),
          THREAD_PRIORITY_MAIN - 1,
          THREAD_CREATE_STACKTEST,
          slacker_thread,
          NULL,
          "slacker1");
  
      puts("sending 1st msg");
      m.content.ptr = &msg_a;
      msg_try_send(&m, pid1);
  
      puts("sending 2nd msg");
      m.content.ptr = &msg_b;
      msg_try_send(&m, pid1);
  
      kernel_pid_t pid2 = thread_create(
          slacker_stack2,
          sizeof(slacker_stack2),
          THREAD_PRIORITY_MAIN - 1,
          THREAD_CREATE_STACKTEST,
          slacker_thread,
          NULL,
          "slacker2");
  
      puts("sending 3rd msg");
      m.content.ptr = &msg_c;
      msg_try_send(&m, pid2);
  
      puts("sending 4th msg");
      m.content.ptr = &msg_d;
      msg_try_send(&m, pid2);
  
      kernel_pid_t pid3 = thread_create(
                     worker_stack,
                     sizeof(worker_stack),
                     THREAD_PRIORITY_MAIN - 1,
                     THREAD_CREATE_STACKTEST,
                     worker_thread,
                     NULL,
                     "worker");
  
      xtimer_ticks32_t last_wakeup = xtimer_now();
      while (1) {
          xtimer_periodic_wakeup(&last_wakeup, TEST_INTERVAL);
          msg_try_send(&m, pid3);
      }
  }