summaryrefslogtreecommitdiffstats
path: root/bsp-howto/clock.rst
blob: 326ad6ccd8c407e899beba59b28e66b615e36b36 (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
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
.. comment SPDX-License-Identifier: CC-BY-SA-4.0

.. COMMENT: COPYRIGHT (c) 1988-2002.
.. COMMENT: On-Line Applications Research Corporation (OAR).
.. COMMENT: All rights reserved.

Clock Driver
************

Introduction
============

The purpose of the clock driver is to provide two services for the operating
system.

- A steady time basis to the kernel, so that the RTEMS primitives that need a
  clock tick work properly.  See the *Clock Manager* chapter of the *RTEMS
  Application C User's Guide* for more details.

- An optional `timecounter <http://www.freebsd.dk/pubs/timecounter.pdf>`_ to
  provide timestamps of the uptime and wall clock time with higher resolution
  than the clock tick.

The clock driver is usually located in the :file:`clock` directory of the BSP.
Clock drivers must use the :dfn:`Clock Driver Shell` available via the
`clockimpl.h <https://git.rtems.org/rtems/tree/bsps/shared/dev/clock/clockimpl.h>`_
include file.  This include file is not a normal header file and instead
defines the clock driver functions declared in ``#include <rtems/clockdrv.h>``
which are used by RTEMS configuration file ``#include <rtems/confdefs.h>``.  In
case the application configuration defines
``#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER``, then the clock driver is
registered and should provide its services to the operating system.  The clock
tick interval is determined by the application configuration via
``#define CONFIGURE_MICROSECONDS_PER_TICK`` and can be obtained via
``rtems_configuration_get_microseconds_per_tick()``.

A hardware-specific clock driver must provide some functions, defines and
macros for the :dfn:`Clock Driver Shell` which are explained here step by step.
A clock driver file looks in general like this.

.. code-block:: c

    /*
     * A section with functions, defines and macros to provide hardware-specific
     * functions for the Clock Driver Shell.
     */

    #include "../../../shared/dev/clock/clockimpl.h"

Depending on the hardware capabilities one out of three clock driver variants
must be selected.

Timecounter
    The variant which provides all features needs a free running hardware
    counter and a periodic clock tick interrupt.  This variant is mandatory in
    SMP configurations.

Simple Timecounter
    A simple timecounter can be used if the hardware provides no free running
    hardware counter and only a periodic hardware counter synchronous to the
    clock tick interrupt is available.

Clock Tick Only
    The most basic clock driver provides only a periodic clock tick interrupt.
    The timestamp resolution is limited to the clock tick interval.

Initialization
==============

Timecounter Variant
~~~~~~~~~~~~~~~~~~~

This variant is preferred since it is the most efficient and yields the most
accurate timestamps.  It is also mandatory in SMP configurations to obtain
valid timestamps.  The hardware must provide a periodic interrupt to service
the clock tick and a free running counter for the timecounter.  The free
running counter must have a power of two period.  The ``tc_counter_mask`` must
be initialized to the free running counter period minus one, e.g. for a 17-bit
counter this is ``0x0001ffff``.  The ``tc_get_timecount`` function must return
the current counter value (the counter values must increase, so if the counter
counts down, a conversion is necessary).  Use
``RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER`` for the ``tc_quality``.  Set
``tc_frequency`` to the frequency of the free running counter in Hz.  All other
fields of the ``struct timecounter`` must be zero initialized.  Install the
initialized timecounter via ``rtems_timecounter_install()``.

For an example see the `QorIQ clock driver
<https://git.rtems.org/rtems/tree/bsps/powerpc/qoriq/clock/clock-config.c>`_.

.. code-block:: c

    #include <rtems/timecounter.h>

    static struct timecounter some_tc;

    static uint32_t some_tc_get_timecount( struct timecounter *tc )
    {
      some.free_running_counter;
    }

    static void some_support_initialize_hardware( void )
    {
      uint64_t us_per_tick;
      uint32_t counter_frequency_in_hz;
      uint32_t counter_ticks_per_clock_tick;

      us_per_tick = rtems_configuration_get_microseconds_per_tick();
      counter_frequency_in_hz = some_tc_get_frequency();

      /*
       * The multiplication must be done in 64-bit arithmetic to avoid an integer
       * overflow on targets with a high enough counter frequency.
       */
      counter_ticks_per_clock_tick =
        (uint32_t) ( counter_frequency_in_hz * us_per_tick ) / 1000000;

      /*
       * Initialize hardware and set up a periodic interrupt for the configuration
       * based counter ticks per clock tick.
       */

      some_tc.tc_get_timecount = some_tc_get_timecount;
      some_tc.tc_counter_mask = 0xffffffff;
      some_tc.tc_frequency = frequency;
      some_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
      rtems_timecounter_install( &some_tc );
    }

    #define Clock_driver_support_initialize_hardware() \
      some_support_initialize_hardware()

    #include "../../../shared/dev/clock/clockimpl.h"

Simple Timecounter Variant
~~~~~~~~~~~~~~~~~~~~~~~~~~

For an example see the `ERC32 clock driver
<https://git.rtems.org/rtems/tree/bsps/sparc/erc32/clock/ckinit.c>`_.

.. code-block:: c

    #include <rtems/timecounter.h>

    static rtems_timecounter_simple some_tc;

    static uint32_t some_tc_get( rtems_timecounter_simple *tc )
    {
      return some.counter;
    }

    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 );
    }

    static void some_support_initialize_hardware( void )
    {
      uint64_t us_per_tick;
      uint32_t counter_frequency_in_hz;
      uint32_t counter_ticks_per_clock_tick;

      us_per_tick = rtems_configuration_get_microseconds_per_tick();
      counter_frequency_in_hz = some_tc_get_frequency();
      counter_ticks_per_clock_tick =
        (uint32_t) ( counter_frequency_in_hz * us_per_tick ) / 1000000;

      /* Initialize hardware */

      rtems_timecounter_simple_install(
        &some_tc,
        counter_frequency_in_hz,
        counter_ticks_per_clock_tick,
        some_tc_get_timecount
      );
    }

    #define Clock_driver_support_initialize_hardware() \
      some_support_initialize_hardware()
    #define Clock_driver_timecounter_tick() \
      some_tc_tick()

    #include "../../../shared/dev/clock/clockimpl.h"

Clock Tick Only Variant
~~~~~~~~~~~~~~~~~~~~~~~

For an example see the `Motrola 68360 clock driver
<https://git.rtems.org/rtems/tree/bsps/m68k/gen68360/clock/clock.c>`_.

.. code-block:: c

    static void some_support_initialize_hardware( void )
    {
      /* Initialize hardware */
    }

    #define Clock_driver_support_initialize_hardware() \
      some_support_initialize_hardware()

    /* Indicate that this clock driver lacks a proper timecounter in hardware */

    #define CLOCK_DRIVER_USE_DUMMY_TIMECOUNTER

    #include "../../../shared/dev/clock/clockimpl.h"

Install Clock Tick Interrupt Service Routine
============================================

The clock driver may provide a function to install the clock tick interrupt
service routine via ``Clock_driver_support_install_isr()``.  The clock tick
interrupt service routine is passed as the one and only parameter to this
macro.  The default implementation will do nothing.

.. code-block:: c

    #include <bsp/irq.h>
    #include <bsp/fatal.h>

    static void some_support_install_isr( rtems_interrupt_handler isr )
    {
      rtems_status_code sc;
      sc = rtems_interrupt_handler_install(
        SOME_IRQ,
        "Clock",
        RTEMS_INTERRUPT_UNIQUE,
        isr,
        NULL
      );
      if ( sc != RTEMS_SUCCESSFUL ) {
        bsp_fatal( SOME_FATAL_IRQ_INSTALL );
      }
    }

    #define Clock_driver_support_install_isr( isr ) \
      some_support_install_isr( isr )

    #include "../../../shared/dev/clock/clockimpl.h"

Support At Tick
===============

The hardware-specific support at tick is specified by
``Clock_driver_support_at_tick()``.

.. code-block:: c

    static void some_support_at_tick( void )
    {
      /* Clear interrupt */
    }

    #define Clock_driver_support_at_tick() \
      some_support_at_tick()

    #include "../../../shared/dev/clock/clockimpl.h"

System Shutdown Support
=======================

The clock driver system shutdown support was removed in RTEMS 5.1.

SMP Support
===========

In SMP configurations, the clock tick service must be executed for each
processor used by RTEMS.  By default, the clock tick interrupt must be
distributed to all processors used by RTEMS and each processor invokes the
clock tick service individually.  A clock driver may delegate all the work to
the boot processor.  It must define ``CLOCK_DRIVER_USE_ONLY_BOOT_PROCESSOR`` in
this case.

Clock drivers must define
``Clock_driver_support_set_interrupt_affinity(online_processors)`` to set the
interrupt affinity of the clock tick interrupt.

Multiple Clock Driver Ticks Per Clock Tick
==========================================

In case the hardware needs more than one clock driver tick per clock tick (e.g.
due to a limited range of the hardware timer), then this can be specified with
the optional ``#define CLOCK_DRIVER_ISRS_PER_TICK`` and ``#define
CLOCK_DRIVER_ISRS_PER_TICK_VALUE`` defines.  This is currently used only for
x86 and it hopefully remains that way.

.. code-block:: c

    /* Enable multiple clock driver ticks per clock tick */
    #define CLOCK_DRIVER_ISRS_PER_TICK 1

    /* Specifiy the clock driver ticks per clock tick value */
    #define CLOCK_DRIVER_ISRS_PER_TICK_VALUE 123

    #include "../../../shared/dev/clock/clockimpl.h"

Clock Driver Ticks Counter
==========================

The :dfn:`Clock Driver Shell` provide a global variable that is simply a count
of the number of clock driver interrupt service routines that have occurred.
This information is valuable when debugging a system.  This variable is
declared as follows:

.. code-block:: c

    volatile uint32_t Clock_driver_ticks;