From e7f40f04d39d3d87d30b50a02bab90a72a9dae61 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 21 Dec 2016 10:55:46 +0100 Subject: Rework Clock Driver chapter Update #2737. --- bsp-howto/clock.rst | 237 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 137 insertions(+), 100 deletions(-) diff --git a/bsp-howto/clock.rst b/bsp-howto/clock.rst index f1d4c3b..55c94ac 100644 --- a/bsp-howto/clock.rst +++ b/bsp-howto/clock.rst @@ -17,75 +17,126 @@ system. clock tick work properly. See the *Clock Manager* chapter of the *RTEMS Application C User's Guide* for more details. -- An optional time counter to generate timestamps of the uptime and wall clock - time. +- An optional `timecounter `_ 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 should use the :dfn:`Clock Driver Shell` available via the -:file:`clockdrv_shell.h` include file. - -Clock Driver Shell -================== - -The :dfn:`Clock Driver Shell` include file defines the clock driver functions -declared in ``#include `` which are used by RTEMS -configuration file ``#include ``. 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. +Clock drivers must use the :dfn:`Clock Driver Shell` available via the +`clockdrv_shell.h `_ +include file. This include file is not a normal header file and instead +defines the clock driver functions declared in ``#include `` +which are used by RTEMS configuration file ``#include ``. 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 + * A section with functions, defines and macros to provide hardware-specific * 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 must be selected. -- The most basic clock driver provides only a periodic interrupt service - routine which calls ``rtems_clock_tick()``. The interval is determined by - the application configuration via ``#define CONFIGURE_MICROSECONDS_PER_TICK`` - and can be obtained via ``rtems_configuration_get_microseconds_per_tick()``. - The timestamp resolution is limited to the clock tick interval. +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. -- In case the hardware lacks support for a free running counter, then the - 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. +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. -- The desired variant uses a free running counter to provide accurate - timestamps. This variant is mandatory on SMP configurations. +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. -Clock Tick Only Variant -~~~~~~~~~~~~~~~~~~~~~~~ +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 +`_. .. code-block:: c - static void some_support_initialize_hardware( void ) + #include + + static struct timecounter some_tc; + + static uint32_t some_tc_get_timecount( struct timecounter *tc ) { - /* Initialize hardware */ + some.free_running_counter; } - #define Clock_driver_support_initialize_hardware() \ - some_support_initialize_hardware() + 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; - /* Indicate that this clock driver lacks a proper timecounter in hardware */ + us_per_tick = rtems_configuration_get_microseconds_per_tick(); + counter_frequency_in_hz = some_tc_get_frequency(); - #define CLOCK_DRIVER_USE_DUMMY_TIMECOUNTER + /* + * 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/clockdrv_shell.h" Simple Timecounter Variant ~~~~~~~~~~~~~~~~~~~~~~~~~~ +For an example see the `ERC32 clock driver +`_. + .. code-block:: c #include @@ -118,84 +169,56 @@ Simple Timecounter Variant static void some_support_initialize_hardware( 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; + 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, - frequency, - timecounter_ticks_per_clock_tick, + counter_frequency_in_hz, + counter_ticks_per_clock_tick, some_tc_get_timecount ); } #define Clock_driver_support_initialize_hardware() \ - some_support_initialize_hardware() + some_support_initialize_hardware() #define Clock_driver_timecounter_tick() \ - some_tc_tick() + some_tc_tick() #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 -accurate timestamps. It is also mandatory on 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 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()``. +For an example see the `Motrola 68360 clock driver +`_. .. code-block:: c - #include - - 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 = rtems_configuration_get_microseconds_per_tick(); - 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 ); + /* 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" Install Clock Tick Interrupt Service Routine --------------------------------------------- +============================================ The clock driver must provide a function to install the clock tick interrupt service routine via ``Clock_driver_support_install_isr()``. @@ -221,14 +244,14 @@ service routine via ``Clock_driver_support_install_isr()``. } #define Clock_driver_support_install_isr( isr, old ) \ - some_support_install_isr( isr ) + some_support_install_isr( isr ) #include "../../../shared/clockdrv_shell.h" 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()``. .. code-block:: c @@ -239,16 +262,16 @@ The hardware specific support at tick is specified by } #define Clock_driver_support_at_tick() \ - some_support_at_tick() + some_support_at_tick() #include "../../../shared/clockdrv_shell.h" System Shutdown Support ------------------------ +======================= The :dfn:`Clock Driver Shell` provides the routine ``Clock_exit()`` that is 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()``. It should disable the clock tick source if it was enabled. This can be used to prevent clock ticks after the system is shutdown. @@ -261,12 +284,26 @@ prevent clock ticks after the system is shutdown. } #define Clock_driver_support_shutdown_hardware() \ - some_support_shutdown_hardware() + some_support_shutdown_hardware() #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 ------------------------------------------- +========================================== 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 @@ -285,7 +322,7 @@ x86 and it hopefully remains that way. #include "../../../shared/clockdrv_shell.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. -- cgit v1.2.3