From 31be41653a659a52460734cb8fe1da27e6d5629e Mon Sep 17 00:00:00 2001 From: Alexander Krutwig Date: Mon, 20 Apr 2015 11:08:22 +0200 Subject: timecounter: Port to RTEMS New test sptests/timecounter01. Update #2271. --- doc/bsp_howto/clock.t | 302 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 250 insertions(+), 52 deletions(-) (limited to 'doc/bsp_howto') diff --git a/doc/bsp_howto/clock.t b/doc/bsp_howto/clock.t index 396634b067..f58b89850f 100644 --- a/doc/bsp_howto/clock.t +++ b/doc/bsp_howto/clock.t @@ -7,84 +7,282 @@ @section Introduction -The purpose of the clock driver is to provide a steady time -basis to the kernel, so that the RTEMS primitives that need -a clock tick work properly. See the @code{Clock Manager} chapter -of the @b{RTEMS Application C User's Guide} for more details. +The purpose of the clock driver is to provide two services for the operating +system. +@itemize @bullet +@item A steady time basis to the kernel, so that the RTEMS primitives that need +a clock tick work properly. See the @cite{Clock Manager} chapter of the +@cite{RTEMS Application C User's Guide} for more details. +@item An optional time counter to generate timestamps of the uptime and wall +clock time. +@end itemize -The clock driver is located in the @code{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 +@file{clockdrv_shell.h} include file. -@section Clock Driver Global Variables +@section Clock Driver Shell -This section describes the global variables expected to be provided by -this driver. +The @dfn{Clock Driver Shell} include file defines the clock driver functions +declared in @code{#include } which are used by RTEMS +configuration file @code{#include }. In case the application +configuration defines @code{#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. -@subsection Ticks Counter +@example +/* + * A section with functions, defines and macros to provide hardware specific + * functions for the Clock Driver Shell. + */ + +#include "../../../shared/clockdrv_shell.h" +@end example + +@subsection Initialization + +Depending on the hardware capabilities one out of three clock driver variants +must be selected. +@itemize @bullet +@item The most basic clock driver provides only a periodic interrupt service +routine which calls @code{rtems_clock_tick()}. The interval is determined by +the application configuration via @code{#define +CONFIGURE_MICROSECONDS_PER_TICK} and can be obtained via +@code{rtems_configuration_get_microseconds_per_tick()}. The timestamp +resolution is limited to the clock tick interval. +@item 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. +@item The desired variant uses a free running counter to provide accurate +timestamps. This variant is mandatory on SMP configurations. +@end itemize -Most of the clock device drivers provide a global variable -that is simply a count of the number of clock driver interrupt service -routines that have occured. This information is valuable when debugging -a system. This variable is declared as follows: +@subsubsection Clock Tick Only Variant @example -volatile uint32_t Clock_driver_ticks; +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/clockdrv_shell.h" @end example -@section Initialization +@subsubsection Simple Timecounter Variant + +@example +#include + +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 ) +@{ + 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; + + /* Initialize hardware */ + + rtems_timecounter_simple_install( + &some_tc, + frequency, + timecounter_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/clockdrv_shell.h" +@end example + +@subsubsection Timecounter 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 @code{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 @code{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 +@code{RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER} for the @code{tc_quality}. Set +@code{tc_frequency} to the frequency of the free running counter in Hz. All +other fields of the @code{struct timecounter} must be zero initialized. +Install the initialized timecounter via @code{rtems_timecounter_install()}. -The initialization routine is responsible for -programming the hardware that will periodically -generate an interrupt. A programmable interval timer is commonly -used as the source of the clock tick. +@example +#include + +static struct timecounter some_tc; + +static uint32_t some_tc_get_timecount( struct timecounter *tc ) +@{ + some.free_running_counter; +@} -The device should be programmed such that an interrupt is generated -every @i{m} microseconds, where @i{m} is equal to -@code{rtems_configuration_get_microseconds_per_tick()}. Sometimes -the periodic interval timer can use a prescaler so you have to look -carefully at your user's manual to determine the correct value. +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 ); +@} -You must use the RTEMS primitive @code{rtems_interrupt_catch} to install -your clock interrupt service routine: +#define Clock_driver_support_initialize_hardware() \ + some_support_initialize_hardware() + +#include "../../../shared/clockdrv_shell.h" +@end example + +@subsection Install Clock Tick Interrupt Service Routine + +The clock driver must provide a function to install the clock tick interrupt +service routine via @code{Clock_driver_support_install_isr()}. @example -rtems_interrupt_catch (Clock_ISR, CLOCK_VECTOR, &old_handler); +#include +#include + +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, old ) \ + some_support_install_isr( isr ) + +#include "../../../shared/clockdrv_shell.h" @end example -Since there is currently not a driver entry point invoked at system -shutdown, many clock device drivers use the @code{atexit} routine -to schedule their @code{Clock_exit} routine to execute when the -system is shutdown. +@subsection Support At Tick -By convention, many of the clock drivers do not install the clock -tick if the @code{ticks_per_timeslice} field of the Configuration -Table is 0. +The hardware specific support at tick is specified by +@code{Clock_driver_support_at_tick()}. -@section System shutdown +@example +static void some_support_at_tick( void ) +@{ + /* Clear interrupt */ +@} -Many drivers provide the routine @code{Clock_exit} that is scheduled -to be run during system shutdown via the @code{atexit} routine. -The @code{Clock_exit} routine will disable the clock tick source -if it was enabled. This can be used to prevent clock ticks after the -system is shutdown. +#define Clock_driver_support_at_tick() \ + some_support_at_tick() -@section Clock Interrupt Subroutine +#include "../../../shared/clockdrv_shell.h" +@end example -It only has to inform the kernel that a ticker has elapsed, so call : +@subsection System Shutdown Support + +The @dfn{Clock Driver Shell} provides the routine @code{Clock_exit()} that is +scheduled to be run during system shutdown via the @code{atexit()} routine. +The hardware specific shutdown support is specified by +@code{Clock_driver_support_shutdown_hardware()} which is used by +@code{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. @example -@group -rtems_isr Clock_isr( rtems_vector_number vector ) +static void some_support_shutdown_hardware( void ) @{ - invoke the rtems_clock_tick() directive to announce the tick - if necessary for this hardware - reload the programmable timer + /* Shutdown hardware */ @} -@end group + +#define Clock_driver_support_shutdown_hardware() \ + some_support_shutdown_hardware() + +#include "../../../shared/clockdrv_shell.h" +@end example + +@subsection 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 @code{#define CLOCK_DRIVER_ISRS_PER_TICK} and @code{#define +CLOCK_DRIVER_ISRS_PER_TICK_VALUE} defines. This is currently used only for x86 +and it hopefully remains that way. + +@example +/* 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/clockdrv_shell.h" @end example -@section IO Control +@subsection Clock Driver Ticks Counter -Prior to RTEMS 4.9, the Shared Memory MPCI Driver required a special -IOCTL in the Clock Driver. This is no longer required and the Clock -Driver does not have to provide an IOCTL method at all. +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: +@example +volatile uint32_t Clock_driver_ticks; +@end example -- cgit v1.2.3