Rework Clock Driver chapter

Update #2737.
This commit is contained in:
Sebastian Huber 2016-12-21 10:55:46 +01:00
parent f5170100f1
commit e7f40f04d3

View File

@ -17,75 +17,126 @@ system.
clock tick work properly. See the *Clock Manager* chapter of the *RTEMS clock tick work properly. See the *Clock Manager* chapter of the *RTEMS
Application C User's Guide* for more details. Application C User's Guide* for more details.
- An optional time counter to generate timestamps of the uptime and wall clock - An optional `timecounter <http://www.freebsd.dk/pubs/timecounter.pdf>`_ to
time. 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. The clock driver is usually located in the :file:`clock` directory of the BSP.
Clock drivers should use the :dfn:`Clock Driver Shell` available via the Clock drivers must use the :dfn:`Clock Driver Shell` available via the
:file:`clockdrv_shell.h` include file. `clockdrv_shell.h <https://git.rtems.org/rtems/tree/c/src/lib/libbsp/shared/clockdrv_shell.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()``.
Clock Driver Shell 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.
The :dfn:`Clock Driver Shell` include file 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. 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 .. code-block:: c
/* /*
* A section with functions, defines and macros to provide hardware specific * A section with functions, defines and macros to provide hardware-specific
* functions for the Clock Driver Shell. * functions for the Clock Driver Shell.
*/ */
#include "../../../shared/clockdrv_shell.h"
Initialization #include "../../../shared/clockdrv_shell.h"
--------------
Depending on the hardware capabilities one out of three clock driver variants Depending on the hardware capabilities one out of three clock driver variants
must be selected. must be selected.
- The most basic clock driver provides only a periodic interrupt service Timecounter
routine which calls ``rtems_clock_tick()``. The interval is determined by The variant which provides all features needs a free running hardware
the application configuration via ``#define CONFIGURE_MICROSECONDS_PER_TICK`` counter and a periodic clock tick interrupt. This variant is mandatory in
and can be obtained via ``rtems_configuration_get_microseconds_per_tick()``. 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. The timestamp resolution is limited to the clock tick interval.
- In case the hardware lacks support for a free running counter, then the Initialization
module used for the clock tick may provide support for timestamps with a ==============
resolution below the clock tick interval. For this so called simple
timecounters can be used.
- The desired variant uses a free running counter to provide accurate Timecounter Variant
timestamps. This variant is mandatory on SMP configurations. ~~~~~~~~~~~~~~~~~~~
Clock Tick Only 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/c/src/lib/libbsp/powerpc/qoriq/clock/clock-config.c>`_.
.. code-block:: 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 ) static void some_support_initialize_hardware( void )
{ {
/* Initialize hardware */ 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() \ #define Clock_driver_support_initialize_hardware() \
some_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/clockdrv_shell.h" #include "../../../shared/clockdrv_shell.h"
Simple Timecounter Variant Simple Timecounter Variant
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
For an example see the `ERC32 clock driver
<https://git.rtems.org/rtems/tree/c/src/lib/libbsp/sparc/erc32/clock/ckinit.c>`_.
.. code-block:: c .. code-block:: c
#include <rtems/timecounter.h> #include <rtems/timecounter.h>
@ -118,16 +169,21 @@ Simple Timecounter Variant
static void some_support_initialize_hardware( void ) static void some_support_initialize_hardware( void )
{ {
uint32_t frequency = 123456; uint64_t us_per_tick;
uint64_t us_per_tick = rtems_configuration_get_microseconds_per_tick(); uint32_t counter_frequency_in_hz;
uint32_t timecounter_ticks_per_clock_tick = uint32_t counter_ticks_per_clock_tick;
( frequency * us_per_tick ) / 1000000;
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 */ /* Initialize hardware */
rtems_timecounter_simple_install( rtems_timecounter_simple_install(
&some_tc, &some_tc,
frequency, counter_frequency_in_hz,
timecounter_ticks_per_clock_tick, counter_ticks_per_clock_tick,
some_tc_get_timecount some_tc_get_timecount
); );
} }
@ -139,63 +195,30 @@ Simple Timecounter Variant
#include "../../../shared/clockdrv_shell.h" #include "../../../shared/clockdrv_shell.h"
Timecounter Variant Clock Tick Only Variant
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
This variant is preferred since it is the most efficient and yields the most For an example see the `Motrola 68360 clock driver
accurate timestamps. It is also mandatory on SMP configurations to obtain <https://git.rtems.org/rtems/tree/c/src/lib/libbsp/m68k/gen68360/clock/clock.c>`_.
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 32-bit
counter this is 0xffffffff. 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()``.
.. code-block:: 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 ) static void some_support_initialize_hardware( void )
{ {
uint64_t us_per_tick = rtems_configuration_get_microseconds_per_tick(); /* Initialize hardware */
uint32_t frequency = 123456;
/*
* The multiplication must be done in 64-bit arithmetic to avoid an integer
* overflow on targets with a high enough counter frequency.
*/
uint32_t interval = (uint32_t) ( ( frequency * us_per_tick ) / 1000000 );
/*
* Initialize hardware and set up a periodic interrupt for the configuration
* based interval.
*/
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() \ #define Clock_driver_support_initialize_hardware() \
some_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/clockdrv_shell.h" #include "../../../shared/clockdrv_shell.h"
Install Clock Tick Interrupt Service Routine Install Clock Tick Interrupt Service Routine
-------------------------------------------- ============================================
The clock driver must provide a function to install the clock tick interrupt The clock driver must provide a function to install the clock tick interrupt
service routine via ``Clock_driver_support_install_isr()``. service routine via ``Clock_driver_support_install_isr()``.
@ -226,9 +249,9 @@ service routine via ``Clock_driver_support_install_isr()``.
#include "../../../shared/clockdrv_shell.h" #include "../../../shared/clockdrv_shell.h"
Support At Tick Support At Tick
--------------- ===============
The hardware specific support at tick is specified by The hardware-specific support at tick is specified by
``Clock_driver_support_at_tick()``. ``Clock_driver_support_at_tick()``.
.. code-block:: c .. code-block:: c
@ -244,11 +267,11 @@ The hardware specific support at tick is specified by
#include "../../../shared/clockdrv_shell.h" #include "../../../shared/clockdrv_shell.h"
System Shutdown Support System Shutdown Support
----------------------- =======================
The :dfn:`Clock Driver Shell` provides the routine ``Clock_exit()`` that is The :dfn:`Clock Driver Shell` provides the routine ``Clock_exit()`` that is
scheduled to be run during system shutdown via the ``atexit()`` routine. The scheduled to be run during system shutdown via the ``atexit()`` routine. The
hardware specific shutdown support is specified by hardware-specific shutdown support is specified by
``Clock_driver_support_shutdown_hardware()`` which is used by ``Clock_exit()``. ``Clock_driver_support_shutdown_hardware()`` which is used by ``Clock_exit()``.
It should disable the clock tick source if it was enabled. This can be used to It should disable the clock tick source if it was enabled. This can be used to
prevent clock ticks after the system is shutdown. prevent clock ticks after the system is shutdown.
@ -265,8 +288,22 @@ prevent clock ticks after the system is shutdown.
#include "../../../shared/clockdrv_shell.h" #include "../../../shared/clockdrv_shell.h"
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 Multiple Clock Driver Ticks Per Clock Tick
------------------------------------------ ==========================================
In case the hardware needs more than one clock driver tick per clock tick (e.g. 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 due to a limited range of the hardware timer), then this can be specified with
@ -285,7 +322,7 @@ x86 and it hopefully remains that way.
#include "../../../shared/clockdrv_shell.h" #include "../../../shared/clockdrv_shell.h"
Clock Driver Ticks Counter Clock Driver Ticks Counter
-------------------------- ==========================
The :dfn:`Clock Driver Shell` provide a global variable that is simply a count 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. of the number of clock driver interrupt service routines that have occurred.