summaryrefslogtreecommitdiffstats
path: root/doc/bsp_howto/clock.t
diff options
context:
space:
mode:
Diffstat (limited to 'doc/bsp_howto/clock.t')
-rw-r--r--doc/bsp_howto/clock.t302
1 files changed, 250 insertions, 52 deletions
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 <rtems/clockdrv.h>} which are used by RTEMS
+configuration file @code{#include <rtems/confdefs.h>}. 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 <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 )
+@{
+ 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 <rtems/timecounter.h>
+
+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 <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, 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