summaryrefslogtreecommitdiffstats
path: root/cpukit/sapi/include/rtems/timecounter.h
blob: 04bc534d55135825fdfd948c34e36a3dab7791f2 (plain) (blame)
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/**
 * @file
 *
 * @ingroup SAPITimecounter
 *
 * @brief Timecounter API
 */

/*
 * Copyright (c) 2015 embedded brains GmbH.  All rights reserved.
 *
 *  embedded brains GmbH
 *  Dornierstr. 4
 *  82178 Puchheim
 *  Germany
 *  <rtems@embedded-brains.de>
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#ifndef _RTEMS_TIMECOUNTER_H
#define _RTEMS_TIMECOUNTER_H

#include <rtems/score/timecounter.h>
#include <rtems/score/basedefs.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/**
 * @defgroup SAPITimecounter Timecounter Support
 *
 * @{
 */

/**
 * @brief Timecounter quality for the clock drivers.
 *
 * Timecounter with higher quality value are used in favour of those with lower
 * quality value.
 */
#define RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER 100

/**
 * @copydoc _Timecounter_Install()
 *
 * Below is an exemplary code snippet that shows the adjustable parameters and
 * the following call of the install routine.
 *
 * @code
 * struct timecounter tc;
 *
 * uint32_t get_timecount( struct timecounter *tc )
 * {
 *   return some_free_running_counter;
 * }
 *
 * void install( void )
 * {
 *   tc.tc_get_timecount = get_timecount;
 *   tc.tc_counter_mask = 0xffffffff;
 *   tc.tc_frequency = 123456;
 *   tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
 *   rtems_timecounter_install( &tc );
 * }
 * @endcode
 */
RTEMS_INLINE_ROUTINE void rtems_timecounter_install(
  struct timecounter *tc
)
{
  _Timecounter_Install( tc );
}

/**
 * @copydoc _Timecounter_Tick()
 */
RTEMS_INLINE_ROUTINE void rtems_timecounter_tick(void)
{
  _Timecounter_Tick();
}

/**
 * @brief Simple timecounter to support legacy clock drivers.
 */
typedef struct {
  struct timecounter tc;
  uint64_t scaler;
  uint32_t real_interval;
  uint32_t binary_interval;
} rtems_timecounter_simple;

/**
 * @brief Returns the current value of a simple timecounter.
 */
typedef uint32_t rtems_timecounter_simple_get(
  rtems_timecounter_simple *tc
);

/**
 * @brief Returns true if the interrupt of a simple timecounter is pending, and
 * false otherwise.
 */
typedef bool rtems_timecounter_simple_is_pending(
  rtems_timecounter_simple *tc
);

/**
 * @brief Initializes and installs a simple timecounter.
 *
 * A simple timecounter can be used if the hardware provides no free running
 * counter or only the module used for the clock tick is available.  The period
 * of the simple timecounter equals the clock tick interval.  The interval is
 * scaled up to the next power of two.
 *
 * @param[in] tc Zero initialized simple timecounter.
 * @param[in] frequency_in_hz The timecounter frequency in Hz.
 * @param[in] timecounter_ticks_per_clock_tick The timecounter ticks per clock tick.
 * @param[in] get_timecount The method to get the current time count.
 *
 * @code
 * #include <rtems/timecounter.h>
 *
 * static rtems_timecounter_simple some_tc;
 *
 * static uint32_t some_tc_get( rtems_timecounter_simple *tc )
 * {
 *   return some.value;
 * }
 *
 * static bool some_tc_is_pending( rtems_timecounter_simple *tc )
 * {
 *   return some.is_pending;
 * }
 *
 * static uint32_t some_tc_get_timecount( struct timecounter *tc )
 * {
 *   return rtems_timecounter_simple_downcounter_get(
 *     tc,
 *     some_tc_get,
 *     some_tc_is_pending
 *   );
 * }
 *
 * static void some_tc_tick( void )
 * {
 *   rtems_timecounter_simple_downcounter_tick( &some_tc, some_tc_get );
 * }
 *
 * void install( void )
 * {
 *   uint32_t frequency = 123456;
 *   uint64_t us_per_tick = rtems_configuration_get_microseconds_per_tick();
 *   uint32_t timecounter_ticks_per_clock_tick =
 *     ( frequency * us_per_tick ) / 1000000;
 *
 *   rtems_timecounter_simple_install(
 *     &some_tc,
 *     frequency,
 *     timecounter_ticks_per_clock_tick,
 *     some_tc_get_timecount
 *   );
 * }
 * @endcode
 *
 * @see rtems_timecounter_simple_downcounter_get(),
 * rtems_timecounter_simple_downcounter_tick(),
 * rtems_timecounter_simple_upcounter_get() and
 * rtems_timecounter_simple_upcounter_tick().
 */
void rtems_timecounter_simple_install(
  rtems_timecounter_simple *tc,
  uint32_t                  frequency_in_hz,
  uint32_t                  timecounter_ticks_per_clock_tick,
  timecounter_get_t        *get_timecount
);

/**
 * @brief Maps a simple timecounter value into its binary frequency domain.
 *
 * @param[in] tc The simple timecounter.
 * @param[in] value The value of the simple timecounter.
 *
 * @return The scaled value.
 */
RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_scale(
  const rtems_timecounter_simple *tc,
  uint32_t value
)
{
  return (uint32_t) ( ( value * tc->scaler ) >> 32 );
}

/**
 * @brief Performs a simple timecounter tick for downcounters.
 *
 * @param[in] tc The simple timecounter.
 * @param[in] get The method to get the value of the simple timecounter.
 */
RTEMS_INLINE_ROUTINE void rtems_timecounter_simple_downcounter_tick(
  rtems_timecounter_simple     *tc,
  rtems_timecounter_simple_get  get
)
{
  uint32_t current;

  current = rtems_timecounter_simple_scale(
    tc,
    tc->real_interval - ( *get )( tc )
  );

  _Timecounter_Tick_simple( tc->binary_interval, current );
}

/**
 * @brief Performs a simple timecounter tick for upcounters.
 *
 * @param[in] tc The simple timecounter.
 * @param[in] get The method to get the value of the simple timecounter.
 */
RTEMS_INLINE_ROUTINE void rtems_timecounter_simple_upcounter_tick(
  rtems_timecounter_simple     *tc,
  rtems_timecounter_simple_get  get
)
{
  uint32_t current;

  current = rtems_timecounter_simple_scale( tc, ( *get )( tc ) );

  _Timecounter_Tick_simple( tc->binary_interval, current );
}

/**
 * @brief Gets the simple timecounter value mapped to its binary frequency
 * domain for downcounters.
 *
 * @param[in] tc The simple timecounter.
 * @param[in] get The method to get the value of the simple timecounter.
 * @param[in] is_pending The method which indicates if the interrupt of the
 * simple timecounter is pending.
 */
RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_downcounter_get(
  struct timecounter                  *tc_base,
  rtems_timecounter_simple_get         get,
  rtems_timecounter_simple_is_pending  is_pending
)
{
  rtems_timecounter_simple *tc;
  uint32_t counter;
  uint32_t interval;

  tc = (rtems_timecounter_simple *) tc_base;
  counter = ( *get )( tc );
  interval = tc->real_interval;

  if ( ( *is_pending )( tc ) ) {
    counter = ( *get )( tc );
    interval *= 2;
  }

  return rtems_timecounter_simple_scale( tc, interval - counter );
}

/**
 * @brief Gets the simple timecounter value mapped to its binary frequency
 * domain for upcounters.
 *
 * @param[in] tc The simple timecounter.
 * @param[in] get The method to get the value of the simple timecounter.
 * @param[in] is_pending The method which indicates if the interrupt of the
 * simple timecounter is pending.
 */
RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_upcounter_get(
  struct timecounter                  *tc_base,
  rtems_timecounter_simple_get         get,
  rtems_timecounter_simple_is_pending  is_pending
)
{
  rtems_timecounter_simple *tc;
  uint32_t counter;
  uint32_t interval;

  tc = (rtems_timecounter_simple *) tc_base;
  counter = ( *get )( tc );
  interval = 0;

  if ( ( *is_pending )( tc ) ) {
    counter = ( *get )( tc );
    interval = tc->real_interval;
  }

  return rtems_timecounter_simple_scale( tc, interval + counter );
}

/** @} */

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* _RTEMS_TIMECOUNTER_H */