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. --- cpukit/sapi/Makefile.am | 3 + cpukit/sapi/include/rtems/bsd.h | 141 ++++++++++ cpukit/sapi/include/rtems/timecounter.h | 304 +++++++++++++++++++++ cpukit/sapi/preinstall.am | 8 + cpukit/sapi/src/tcsimpleinstall.c | 52 ++++ cpukit/score/Makefile.am | 12 + cpukit/score/include/rtems/score/timecounter.h | 199 ++++++++++++++ cpukit/score/include/rtems/score/timecounterimpl.h | 49 ++++ cpukit/score/include/sys/timeffc.h | 2 + cpukit/score/include/sys/timetc.h | 2 + cpukit/score/preinstall.am | 33 +++ cpukit/score/src/kern_tc.c | 171 ++++++++++++ doc/bsp_howto/clock.t | 302 ++++++++++++++++---- testsuites/sptests/Makefile.am | 1 + testsuites/sptests/configure.ac | 1 + testsuites/sptests/sptimecounter01/Makefile.am | 19 ++ testsuites/sptests/sptimecounter01/init.c | 133 +++++++++ .../sptests/sptimecounter01/sptimecounter01.doc | 29 ++ .../sptests/sptimecounter01/sptimecounter01.scn | 2 + 19 files changed, 1411 insertions(+), 52 deletions(-) create mode 100644 cpukit/sapi/include/rtems/bsd.h create mode 100644 cpukit/sapi/include/rtems/timecounter.h create mode 100644 cpukit/sapi/src/tcsimpleinstall.c create mode 100644 cpukit/score/include/rtems/score/timecounter.h create mode 100644 cpukit/score/include/rtems/score/timecounterimpl.h create mode 100644 testsuites/sptests/sptimecounter01/Makefile.am create mode 100644 testsuites/sptests/sptimecounter01/init.c create mode 100644 testsuites/sptests/sptimecounter01/sptimecounter01.doc create mode 100644 testsuites/sptests/sptimecounter01/sptimecounter01.scn diff --git a/cpukit/sapi/Makefile.am b/cpukit/sapi/Makefile.am index 070800e636..d7fd9aac63 100644 --- a/cpukit/sapi/Makefile.am +++ b/cpukit/sapi/Makefile.am @@ -4,6 +4,7 @@ include $(top_srcdir)/automake/compile.am include_rtemsdir = $(includedir)/rtems include_rtems_HEADERS = include/confdefs.h +include_rtems_HEADERS += include/rtems/bsd.h include_rtems_HEADERS += include/rtems/chain.h include_rtems_HEADERS += include/rtems/config.h include_rtems_HEADERS += include/rtems/counter.h @@ -19,6 +20,7 @@ include_rtems_HEADERS += include/rtems/rbheap.h include_rtems_HEADERS += include/rtems/rbtree.h include_rtems_HEADERS += include/rtems/scheduler.h include_rtems_HEADERS += include/rtems/sptables.h +include_rtems_HEADERS += include/rtems/timecounter.h include_rtems_HEADERS += include/rtems/timespec.h EXTRA_DIST = include/rtems/README @@ -39,6 +41,7 @@ libsapi_a_SOURCES += src/delayticks.c libsapi_a_SOURCES += src/delaynano.c libsapi_a_SOURCES += src/profilingiterate.c libsapi_a_SOURCES += src/profilingreportxml.c +libsapi_a_SOURCES += src/tcsimpleinstall.c libsapi_a_CPPFLAGS = $(AM_CPPFLAGS) include $(srcdir)/preinstall.am diff --git a/cpukit/sapi/include/rtems/bsd.h b/cpukit/sapi/include/rtems/bsd.h new file mode 100644 index 0000000000..0c44e3787d --- /dev/null +++ b/cpukit/sapi/include/rtems/bsd.h @@ -0,0 +1,141 @@ +/** + * @file + * + * @ingroup BSD + * + * @brief BSD Compatibility API + */ + +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef _RTEMS_BSD_H +#define _RTEMS_BSD_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup BSD BSD Compatibility Support + * + * @{ + */ + +/** + * @copydoc _Timecounter_Bintime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_bintime( struct bintime *bt ) +{ + _Timecounter_Bintime( bt ); +} + +/** + * @copydoc _Timecounter_Nanotime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_nanotime( struct timespec *ts ) +{ + _Timecounter_Nanotime( ts ); +} + +/** + * @copydoc _Timecounter_Microtime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_microtime( struct timeval *tv ) +{ + _Timecounter_Microtime( tv ); +} + +/** + * @copydoc _Timecounter_Binuptime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_binuptime( struct bintime *bt ) +{ + _Timecounter_Binuptime( bt ); +} + +/** + * @copydoc _Timecounter_Nanouptime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_nanouptime( struct timespec *ts ) +{ + _Timecounter_Nanouptime( ts ); +} + +/** + * @copydoc _Timecounter_Microtime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_microuptime( struct timeval *tv ) +{ + _Timecounter_Microuptime( tv ); +} + +/** + * @copydoc _Timecounter_Getbintime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_getbintime( struct bintime *bt ) +{ + _Timecounter_Getbintime( bt ); +} + +/** + * @copydoc _Timecounter_Getnanotime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_getnanotime( struct timespec *ts ) +{ + _Timecounter_Getnanotime( ts ); +} + +/** + * @copydoc _Timecounter_Getmicrotime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_getmicrotime( struct timeval *tv ) +{ + _Timecounter_Getmicrotime( tv ); +} + +/** + * @copydoc _Timecounter_Getbinuptime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_getbinuptime( struct bintime *bt ) +{ + _Timecounter_Getbinuptime( bt ); +} + +/** + * @copydoc _Timecounter_Getnanouptime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_getnanouptime( struct timespec *ts ) +{ + _Timecounter_Getnanouptime( ts ); +} + +/** + * @copydoc _Timecounter_Getmicrouptime() + */ +RTEMS_INLINE_ROUTINE void rtems_bsd_getmicrouptime( struct timeval *tv ) +{ + _Timecounter_Getmicrouptime( tv ); +} + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_BSD_H */ diff --git a/cpukit/sapi/include/rtems/timecounter.h b/cpukit/sapi/include/rtems/timecounter.h new file mode 100644 index 0000000000..04bc534d55 --- /dev/null +++ b/cpukit/sapi/include/rtems/timecounter.h @@ -0,0 +1,304 @@ +/** + * @file + * + * @ingroup SAPITimecounter + * + * @brief Timecounter API + */ + +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef _RTEMS_TIMECOUNTER_H +#define _RTEMS_TIMECOUNTER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup SAPITimecounter Timecounter Support + * + * @{ + */ + +/** + * @brief Timecounter quality for the clock drivers. + * + * Timecounter with higher quality value are used in favour of those with lower + * quality value. + */ +#define RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER 100 + +/** + * @copydoc _Timecounter_Install() + * + * Below is an exemplary code snippet that shows the adjustable parameters and + * the following call of the install routine. + * + * @code + * struct timecounter tc; + * + * uint32_t get_timecount( struct timecounter *tc ) + * { + * return some_free_running_counter; + * } + * + * void install( void ) + * { + * tc.tc_get_timecount = get_timecount; + * tc.tc_counter_mask = 0xffffffff; + * tc.tc_frequency = 123456; + * tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; + * rtems_timecounter_install( &tc ); + * } + * @endcode + */ +RTEMS_INLINE_ROUTINE void rtems_timecounter_install( + struct timecounter *tc +) +{ + _Timecounter_Install( tc ); +} + +/** + * @copydoc _Timecounter_Tick() + */ +RTEMS_INLINE_ROUTINE void rtems_timecounter_tick(void) +{ + _Timecounter_Tick(); +} + +/** + * @brief Simple timecounter to support legacy clock drivers. + */ +typedef struct { + struct timecounter tc; + uint64_t scaler; + uint32_t real_interval; + uint32_t binary_interval; +} rtems_timecounter_simple; + +/** + * @brief Returns the current value of a simple timecounter. + */ +typedef uint32_t rtems_timecounter_simple_get( + rtems_timecounter_simple *tc +); + +/** + * @brief Returns true if the interrupt of a simple timecounter is pending, and + * false otherwise. + */ +typedef bool rtems_timecounter_simple_is_pending( + rtems_timecounter_simple *tc +); + +/** + * @brief Initializes and installs a simple timecounter. + * + * A simple timecounter can be used if the hardware provides no free running + * counter or only the module used for the clock tick is available. The period + * of the simple timecounter equals the clock tick interval. The interval is + * scaled up to the next power of two. + * + * @param[in] tc Zero initialized simple timecounter. + * @param[in] frequency_in_hz The timecounter frequency in Hz. + * @param[in] timecounter_ticks_per_clock_tick The timecounter ticks per clock tick. + * @param[in] get_timecount The method to get the current time count. + * + * @code + * #include + * + * static rtems_timecounter_simple some_tc; + * + * static uint32_t some_tc_get( rtems_timecounter_simple *tc ) + * { + * return some.value; + * } + * + * 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 ); + * } + * + * void install( 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; + * + * rtems_timecounter_simple_install( + * &some_tc, + * frequency, + * timecounter_ticks_per_clock_tick, + * some_tc_get_timecount + * ); + * } + * @endcode + * + * @see rtems_timecounter_simple_downcounter_get(), + * rtems_timecounter_simple_downcounter_tick(), + * rtems_timecounter_simple_upcounter_get() and + * rtems_timecounter_simple_upcounter_tick(). + */ +void rtems_timecounter_simple_install( + rtems_timecounter_simple *tc, + uint32_t frequency_in_hz, + uint32_t timecounter_ticks_per_clock_tick, + timecounter_get_t *get_timecount +); + +/** + * @brief Maps a simple timecounter value into its binary frequency domain. + * + * @param[in] tc The simple timecounter. + * @param[in] value The value of the simple timecounter. + * + * @return The scaled value. + */ +RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_scale( + const rtems_timecounter_simple *tc, + uint32_t value +) +{ + return (uint32_t) ( ( value * tc->scaler ) >> 32 ); +} + +/** + * @brief Performs a simple timecounter tick for downcounters. + * + * @param[in] tc The simple timecounter. + * @param[in] get The method to get the value of the simple timecounter. + */ +RTEMS_INLINE_ROUTINE void rtems_timecounter_simple_downcounter_tick( + rtems_timecounter_simple *tc, + rtems_timecounter_simple_get get +) +{ + uint32_t current; + + current = rtems_timecounter_simple_scale( + tc, + tc->real_interval - ( *get )( tc ) + ); + + _Timecounter_Tick_simple( tc->binary_interval, current ); +} + +/** + * @brief Performs a simple timecounter tick for upcounters. + * + * @param[in] tc The simple timecounter. + * @param[in] get The method to get the value of the simple timecounter. + */ +RTEMS_INLINE_ROUTINE void rtems_timecounter_simple_upcounter_tick( + rtems_timecounter_simple *tc, + rtems_timecounter_simple_get get +) +{ + uint32_t current; + + current = rtems_timecounter_simple_scale( tc, ( *get )( tc ) ); + + _Timecounter_Tick_simple( tc->binary_interval, current ); +} + +/** + * @brief Gets the simple timecounter value mapped to its binary frequency + * domain for downcounters. + * + * @param[in] tc The simple timecounter. + * @param[in] get The method to get the value of the simple timecounter. + * @param[in] is_pending The method which indicates if the interrupt of the + * simple timecounter is pending. + */ +RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_downcounter_get( + struct timecounter *tc_base, + rtems_timecounter_simple_get get, + rtems_timecounter_simple_is_pending is_pending +) +{ + rtems_timecounter_simple *tc; + uint32_t counter; + uint32_t interval; + + tc = (rtems_timecounter_simple *) tc_base; + counter = ( *get )( tc ); + interval = tc->real_interval; + + if ( ( *is_pending )( tc ) ) { + counter = ( *get )( tc ); + interval *= 2; + } + + return rtems_timecounter_simple_scale( tc, interval - counter ); +} + +/** + * @brief Gets the simple timecounter value mapped to its binary frequency + * domain for upcounters. + * + * @param[in] tc The simple timecounter. + * @param[in] get The method to get the value of the simple timecounter. + * @param[in] is_pending The method which indicates if the interrupt of the + * simple timecounter is pending. + */ +RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_upcounter_get( + struct timecounter *tc_base, + rtems_timecounter_simple_get get, + rtems_timecounter_simple_is_pending is_pending +) +{ + rtems_timecounter_simple *tc; + uint32_t counter; + uint32_t interval; + + tc = (rtems_timecounter_simple *) tc_base; + counter = ( *get )( tc ); + interval = 0; + + if ( ( *is_pending )( tc ) ) { + counter = ( *get )( tc ); + interval = tc->real_interval; + } + + return rtems_timecounter_simple_scale( tc, interval + counter ); +} + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_TIMECOUNTER_H */ diff --git a/cpukit/sapi/preinstall.am b/cpukit/sapi/preinstall.am index 3f864bb61d..8a4e54ffd2 100644 --- a/cpukit/sapi/preinstall.am +++ b/cpukit/sapi/preinstall.am @@ -22,6 +22,10 @@ $(PROJECT_INCLUDE)/rtems/confdefs.h: include/confdefs.h $(PROJECT_INCLUDE)/rtems $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/confdefs.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/confdefs.h +$(PROJECT_INCLUDE)/rtems/bsd.h: include/rtems/bsd.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/bsd.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/bsd.h + $(PROJECT_INCLUDE)/rtems/chain.h: include/rtems/chain.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/chain.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/chain.h @@ -82,6 +86,10 @@ $(PROJECT_INCLUDE)/rtems/sptables.h: include/rtems/sptables.h $(PROJECT_INCLUDE) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/sptables.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/sptables.h +$(PROJECT_INCLUDE)/rtems/timecounter.h: include/rtems/timecounter.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/timecounter.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/timecounter.h + $(PROJECT_INCLUDE)/rtems/timespec.h: include/rtems/timespec.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/timespec.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/timespec.h diff --git a/cpukit/sapi/src/tcsimpleinstall.c b/cpukit/sapi/src/tcsimpleinstall.c new file mode 100644 index 0000000000..563edd7c87 --- /dev/null +++ b/cpukit/sapi/src/tcsimpleinstall.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +void rtems_timecounter_simple_install( + rtems_timecounter_simple *tc, + uint32_t frequency_in_hz, + uint32_t counter_ticks_per_clock_tick, + timecounter_get_t *get_timecount +) +{ + uint32_t power_of_two = 1; + uint32_t mask; + uint64_t scaler; + int i; + + for ( i = 0; i < 32; ++i ) { + if ( power_of_two >= counter_ticks_per_clock_tick ) { + break; + } + + power_of_two *= 2; + } + + mask = ( 2 * power_of_two ) - 1; + scaler = ( (uint64_t) power_of_two << 32 ) / counter_ticks_per_clock_tick; + + tc->scaler = scaler; + tc->real_interval = counter_ticks_per_clock_tick; + tc->binary_interval = ( mask + 1 ) / 2; + tc->tc.tc_get_timecount = get_timecount; + tc->tc.tc_counter_mask = mask; + tc->tc.tc_frequency = (uint32_t) ( ( frequency_in_hz * scaler ) >> 32 ); + tc->tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; + _Timecounter_Install( &tc->tc ); +} diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am index b6b7c9fa74..a16aedac1d 100644 --- a/cpukit/score/Makefile.am +++ b/cpukit/score/Makefile.am @@ -5,6 +5,15 @@ SUBDIRS = cpu ## include +include_sysdir = $(includedir)/sys + +include_sys_HEADERS = +include_sys_HEADERS += include/sys/_ffcounter.h +include_sys_HEADERS += include/sys/timeffc.h +include_sys_HEADERS += include/sys/timepps.h +include_sys_HEADERS += include/sys/timetc.h +include_sys_HEADERS += include/sys/timex.h + include_rtemsdir = $(includedir)/rtems include_rtems_HEADERS = include/rtems/debug.h @@ -85,6 +94,8 @@ include_rtems_score_HEADERS += include/rtems/score/threadqimpl.h include_rtems_score_HEADERS += include/rtems/score/timespec.h include_rtems_score_HEADERS += include/rtems/score/timestamp.h include_rtems_score_HEADERS += include/rtems/score/timestamp64.h +include_rtems_score_HEADERS += include/rtems/score/timecounter.h +include_rtems_score_HEADERS += include/rtems/score/timecounterimpl.h include_rtems_score_HEADERS += include/rtems/score/tls.h include_rtems_score_HEADERS += include/rtems/score/tod.h include_rtems_score_HEADERS += include/rtems/score/todimpl.h @@ -344,6 +355,7 @@ libscore_a_SOURCES += src/profilingisrentryexit.c libscore_a_SOURCES += src/once.c libscore_a_SOURCES += src/resourceiterate.c libscore_a_SOURCES += src/smpbarrierwait.c +libscore_a_SOURCES += src/kern_tc.c EXTRA_DIST = src/Unlimited.txt diff --git a/cpukit/score/include/rtems/score/timecounter.h b/cpukit/score/include/rtems/score/timecounter.h new file mode 100644 index 0000000000..0d17cc7ce3 --- /dev/null +++ b/cpukit/score/include/rtems/score/timecounter.h @@ -0,0 +1,199 @@ +/** + * @file + * + * @ingroup ScoreTimecounter + * + * @brief Timecounter API + */ + +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef _RTEMS_SCORE_TIMECOUNTER_H +#define _RTEMS_SCORE_TIMECOUNTER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup ScoreTimecounter Timecounter Handler + * + * @ingroup Score + * + * @{ + */ + +/** + * @brief Returns the wall clock time in the bintime format. + * + * @param[out] bt Returns the wall clock time. + */ +void _Timecounter_Bintime( struct bintime *bt ); + +/** + * @brief Returns the wall clock time in the timespec format. + * + * @param[out] ts Returns the wall clock time. + */ +void _Timecounter_Nanotime( struct timespec *ts ); + +/** + * @brief Returns the wall clock time in the timeval format. + * + * @param[out] tv Returns the wall clock time. + */ +void _Timecounter_Microtime( struct timeval *tv ); + +/** + * @brief Returns the uptime in the bintime format. + * + * @param[out] bt Returns the uptime. + */ +void _Timecounter_Binuptime( struct bintime *bt ); + +/** + * @brief Returns the uptime in the timespec format. + * + * @param[out] ts Returns the uptime. + */ +void _Timecounter_Nanouptime( struct timespec *ts ); + +/** + * @brief Returns the uptime in the timeval format. + * + * @param[out] tv Returns the uptime. + */ +void _Timecounter_Microuptime( struct timeval *tv ); + +/** + * @brief Returns the wall clock time in the bintime format. + * + * This function obtains the time with a lower overhead and lower accuracy + * compared to the _Timecounter_Bintime() variant. + * + * @param[out] ts Returns the wall clock time. + */ +void _Timecounter_Getbintime( struct bintime *bt ); + +/** + * @brief Returns the wall clock time in the timespec format. + * + * This function obtains the time with a lower overhead and lower accuracy + * compared to the _Timecounter_Nanotime() variant. + * + * @param[out] ts Returns the wall clock time. + * + * @see _Timecounter_Getbintime(). + */ +void _Timecounter_Getnanotime( struct timespec *ts ); + +/** + * @brief Returns the wall clock time in the timeval format. + * + * This function obtains the time with a lower overhead and lower accuracy + * compared to the _Timecounter_Microtime() variant. + * + * @param[out] tv Returns the wall clock time. + * + * @see _Timecounter_Getbintime(). + */ +void _Timecounter_Getmicrotime( struct timeval *tv ); + +/** + * @brief Returns the uptime in the bintime format. + * + * This function obtains the time with a lower overhead and lower accuracy + * compared to the _Timecounter_Binuptime() variant. + * + * @param[out] ts Returns the uptime. + */ +void _Timecounter_Getbinuptime( struct bintime *bt ); + +/** + * @brief Returns the uptime in the timespec format. + * + * This function obtains the time with a lower overhead and lower accuracy + * compared to the _Timecounter_Nanouptime() variant. + * + * @param[out] ts Returns the uptime. + */ +void _Timecounter_Getnanouptime( struct timespec *ts ); + +/** + * @brief Returns the uptime in the timeval format. + * + * This function obtains the time with a lower overhead and lower accuracy + * compared to the _Timecounter_Microuptime() variant. + * + * @param[out] tv Returns the uptime. + */ +void _Timecounter_Getmicrouptime( struct timeval *tv ); + +/** + * @brief Installs the timecounter. + * + * The timecounter structure must contain valid values in the fields + * tc_get_timecount, tc_counter_mask, tc_frequency and tc_quality. All other + * fields must be zero initialized. + * + * @param[in] tc The timecounter. + */ +void _Timecounter_Install( struct timecounter *tc ); + +/** + * @brief Performs a timecounter tick. + */ +void _Timecounter_Tick( void ); + +/** + * @brief Performs a simple timecounter tick. + * + * This is a special purpose tick function for simple timecounter to support + * legacy clock drivers. + * + * @param[in] delta The time in timecounter ticks elapsed since the last call + * to _Timecounter_Tick_simple(). + * @param[in] offset The current value of the timecounter. + */ +void _Timecounter_Tick_simple( uint32_t delta, uint32_t offset ); + +/** + * @brief The wall clock time in seconds. + */ +extern volatile time_t _Timecounter_Time_second; + +/** + * @brief The uptime in seconds. + * + * For compatibility with the FreeBSD network stack the initial value is one + * second. + */ +extern volatile time_t _Timecounter_Time_uptime; + +/** + * @brief The current timecounter. + */ +extern struct timecounter *_Timecounter; + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_SCORE_TIMECOUNTER_H */ diff --git a/cpukit/score/include/rtems/score/timecounterimpl.h b/cpukit/score/include/rtems/score/timecounterimpl.h new file mode 100644 index 0000000000..dd47aacc04 --- /dev/null +++ b/cpukit/score/include/rtems/score/timecounterimpl.h @@ -0,0 +1,49 @@ +/** + * @file + * + * @ingroup ScoreTimecounter + * + * @brief Timecounter Implementation + */ + +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef _RTEMS_SCORE_TIMECOUNTERIMPL_H +#define _RTEMS_SCORE_TIMECOUNTERIMPL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @addtogroup ScoreTimecounter + * + * @{ + */ + +void _Timecounter_Initialize( void ); + +void _Timecounter_Set_clock( const struct timespec *ts ); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_SCORE_TIMECOUNTERIMPL_H */ diff --git a/cpukit/score/include/sys/timeffc.h b/cpukit/score/include/sys/timeffc.h index 893afe8bda..b3a1cd9ce7 100644 --- a/cpukit/score/include/sys/timeffc.h +++ b/cpukit/score/include/sys/timeffc.h @@ -55,11 +55,13 @@ struct ffclock_estimate { #if __BSD_VISIBLE #ifdef _KERNEL +#ifndef __rtems__ /* Define the kern.sysclock sysctl tree. */ SYSCTL_DECL(_kern_sysclock); /* Define the kern.sysclock.ffclock sysctl tree. */ SYSCTL_DECL(_kern_sysclock_ffclock); +#endif /* __rtems__ */ /* * Index into the sysclocks array for obtaining the ASCII name of a particular diff --git a/cpukit/score/include/sys/timetc.h b/cpukit/score/include/sys/timetc.h index 9d269a780b..88e90dedac 100644 --- a/cpukit/score/include/sys/timetc.h +++ b/cpukit/score/include/sys/timetc.h @@ -12,9 +12,11 @@ #ifndef _SYS_TIMETC_H_ #define _SYS_TIMETC_H_ +#ifndef __rtems__ #ifndef _KERNEL #error "no user-serviceable parts inside" #endif +#endif /* __rtems__ */ /*- * `struct timecounter' is the interface between the hardware which implements diff --git a/cpukit/score/preinstall.am b/cpukit/score/preinstall.am index 62e15720f4..13ea020c9e 100644 --- a/cpukit/score/preinstall.am +++ b/cpukit/score/preinstall.am @@ -13,6 +13,31 @@ all-am: $(PREINSTALL_FILES) PREINSTALL_FILES = CLEANFILES = $(PREINSTALL_FILES) +$(PROJECT_INCLUDE)/sys/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/sys + @: > $(PROJECT_INCLUDE)/sys/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/sys/$(dirstamp) + +$(PROJECT_INCLUDE)/sys/_ffcounter.h: include/sys/_ffcounter.h $(PROJECT_INCLUDE)/sys/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/sys/_ffcounter.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/sys/_ffcounter.h + +$(PROJECT_INCLUDE)/sys/timeffc.h: include/sys/timeffc.h $(PROJECT_INCLUDE)/sys/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/sys/timeffc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/sys/timeffc.h + +$(PROJECT_INCLUDE)/sys/timepps.h: include/sys/timepps.h $(PROJECT_INCLUDE)/sys/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/sys/timepps.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/sys/timepps.h + +$(PROJECT_INCLUDE)/sys/timetc.h: include/sys/timetc.h $(PROJECT_INCLUDE)/sys/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/sys/timetc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/sys/timetc.h + +$(PROJECT_INCLUDE)/sys/timex.h: include/sys/timex.h $(PROJECT_INCLUDE)/sys/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/sys/timex.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/sys/timex.h + $(PROJECT_INCLUDE)/rtems/$(dirstamp): @$(MKDIR_P) $(PROJECT_INCLUDE)/rtems @: > $(PROJECT_INCLUDE)/rtems/$(dirstamp) @@ -323,6 +348,14 @@ $(PROJECT_INCLUDE)/rtems/score/timestamp64.h: include/rtems/score/timestamp64.h $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/timestamp64.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/timestamp64.h +$(PROJECT_INCLUDE)/rtems/score/timecounter.h: include/rtems/score/timecounter.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/timecounter.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/timecounter.h + +$(PROJECT_INCLUDE)/rtems/score/timecounterimpl.h: include/rtems/score/timecounterimpl.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/timecounterimpl.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/timecounterimpl.h + $(PROJECT_INCLUDE)/rtems/score/tls.h: include/rtems/score/tls.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/tls.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/tls.h diff --git a/cpukit/score/src/kern_tc.c b/cpukit/score/src/kern_tc.c index 246c98bb90..bca73ecb19 100644 --- a/cpukit/score/src/kern_tc.c +++ b/cpukit/score/src/kern_tc.c @@ -13,6 +13,27 @@ * of Melbourne under sponsorship from the FreeBSD Foundation. */ +#ifdef __rtems__ +#define _KERNEL +#define bintime _Timecounter_Bintime +#define binuptime _Timecounter_Binuptime +#define boottimebin _Timecounter_Boottimebin +#define getbintime _Timecounter_Getbintime +#define getbinuptime _Timecounter_Getbinuptime +#define getmicrotime _Timecounter_Getmicrotime +#define getmicrouptime _Timecounter_Getmicrouptime +#define getnanotime _Timecounter_Getnanotime +#define getnanouptime _Timecounter_Getnanouptime +#define microtime _Timecounter_Microtime +#define microuptime _Timecounter_Microuptime +#define nanotime _Timecounter_Nanotime +#define nanouptime _Timecounter_Nanouptime +#define tc_init _Timecounter_Install +#define timecounter _Timecounter +#define time_second _Timecounter_Time_second +#define time_uptime _Timecounter_Time_uptime +#include +#endif /* __rtems__ */ #include __FBSDID("$FreeBSD r277406 2015-01-20T03:54:30Z$"); @@ -21,20 +42,42 @@ __FBSDID("$FreeBSD r277406 2015-01-20T03:54:30Z$"); #include "opt_ffclock.h" #include +#ifndef __rtems__ #include #include +#else /* __rtems__ */ +#include +#endif /* __rtems__ */ #ifdef FFCLOCK #include #include #endif +#ifndef __rtems__ #include #include #include +#endif /* __rtems__ */ #include #include #include #include +#ifndef __rtems__ #include +#endif /* __rtems__ */ +#ifdef __rtems__ +#include +ISR_LOCK_DEFINE(static, _Timecounter_Lock, "Timecounter"); +#define hz rtems_clock_get_ticks_per_second() +#define printf(...) +#define log(...) +static inline int +fls(int x) +{ + return x ? sizeof(x) * 8 - __builtin_clz(x) : 0; +} +/* FIXME: https://devel.rtems.org/ticket/2348 */ +#define ntp_update_second(a, b) do { (void) a; (void) b; } while (0) +#endif /* __rtems__ */ /* * A large step happens on boot. This constant detects such steps. @@ -53,9 +96,13 @@ __FBSDID("$FreeBSD r277406 2015-01-20T03:54:30Z$"); static uint32_t dummy_get_timecount(struct timecounter *tc) { +#ifndef __rtems__ static uint32_t now; return (++now); +#else /* __rtems__ */ + return 0; +#endif /* __rtems__ */ } static struct timecounter dummy_timecounter = { @@ -76,6 +123,7 @@ struct timehands { struct timehands *th_next; }; +#if defined(RTEMS_SMP) static struct timehands th0; static struct timehands th9 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th0}; static struct timehands th8 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th9}; @@ -86,6 +134,7 @@ static struct timehands th4 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th5}; static struct timehands th3 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th4}; static struct timehands th2 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th3}; static struct timehands th1 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th2}; +#endif static struct timehands th0 = { &dummy_timecounter, 0, @@ -95,19 +144,26 @@ static struct timehands th0 = { {0, 0}, {0, 0}, 1, +#if defined(RTEMS_SMP) &th1 +#else + &th0 +#endif }; static struct timehands *volatile timehands = &th0; struct timecounter *timecounter = &dummy_timecounter; static struct timecounter *timecounters = &dummy_timecounter; +#ifndef __rtems__ int tc_min_ticktock_freq = 1; +#endif /* __rtems__ */ volatile time_t time_second = 1; volatile time_t time_uptime = 1; struct bintime boottimebin; +#ifndef __rtems__ struct timeval boottime; static int sysctl_kern_boottime(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_kern, KERN_BOOTTIME, boottime, CTLTYPE_STRUCT|CTLFLAG_RD, @@ -133,12 +189,16 @@ SYSCTL_PROC(_kern_timecounter, OID_AUTO, alloweddeviation, CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, 0, sysctl_kern_timecounter_adjprecision, "I", "Allowed time interval deviation in percents"); +#endif /* __rtems__ */ static void tc_windup(void); +#ifndef __rtems__ static void cpu_tick_calibrate(int); +#endif /* __rtems__ */ void dtrace_getnanotime(struct timespec *tsp); +#ifndef __rtems__ static int sysctl_kern_boottime(SYSCTL_HANDLER_ARGS) { @@ -175,6 +235,7 @@ sysctl_kern_timecounter_freq(SYSCTL_HANDLER_ARGS) freq = tc->tc_frequency; return sysctl_handle_64(oidp, &freq, 0, req); } +#endif /* __rtems__ */ /* * Return the difference between the timehands' counter value now and what @@ -976,6 +1037,7 @@ getmicrotime(struct timeval *tvp) #endif /* FFCLOCK */ +#ifndef __rtems__ /* * This is a clone of getnanotime and used for walltimestamps. * The dtrace_ prefix prevents fbt from creating probes for @@ -993,6 +1055,7 @@ dtrace_getnanotime(struct timespec *tsp) *tsp = th->th_nanotime; } while (gen == 0 || gen != th->th_generation); } +#endif /* __rtems__ */ #ifdef FFCLOCK /* @@ -1006,6 +1069,7 @@ int sysclock_active = SYSCLOCK_FBCK; extern int time_status; extern long time_esterror; +#ifndef __rtems__ /* * Take a snapshot of sysclock data which can be used to compare system clocks * and generate timestamps after the fact. @@ -1137,6 +1201,7 @@ sysclock_snap2bintime(struct sysclock_snap *cs, struct bintime *bt, return (0); } +#endif /* __rtems__ */ /* * Initialize a new timecounter and possibly use it. @@ -1144,6 +1209,7 @@ sysclock_snap2bintime(struct sysclock_snap *cs, struct bintime *bt, void tc_init(struct timecounter *tc) { +#ifndef __rtems__ uint32_t u; struct sysctl_oid *tc_root; @@ -1163,9 +1229,11 @@ tc_init(struct timecounter *tc) tc->tc_name, (uintmax_t)tc->tc_frequency, tc->tc_quality); } +#endif /* __rtems__ */ tc->tc_next = timecounters; timecounters = tc; +#ifndef __rtems__ /* * Set up sysctl tree for this counter. */ @@ -1196,11 +1264,16 @@ tc_init(struct timecounter *tc) if (tc->tc_quality == timecounter->tc_quality && tc->tc_frequency < timecounter->tc_frequency) return; +#endif /* __rtems__ */ (void)tc->tc_get_timecount(tc); (void)tc->tc_get_timecount(tc); timecounter = tc; +#ifdef __rtems__ + tc_windup(); +#endif /* __rtems__ */ } +#ifndef __rtems__ /* Report the frequency of the current timecounter. */ uint64_t tc_getfrequency(void) @@ -1208,29 +1281,42 @@ tc_getfrequency(void) return (timehands->th_counter->tc_frequency); } +#endif /* __rtems__ */ /* * Step our concept of UTC. This is done by modifying our estimate of * when we booted. * XXX: not locked. */ +#ifndef __rtems__ void tc_setclock(struct timespec *ts) +#else /* __rtems__ */ +void +_Timecounter_Set_clock(const struct timespec *ts) +#endif /* __rtems__ */ { +#ifndef __rtems__ struct timespec tbef, taft; +#endif /* __rtems__ */ struct bintime bt, bt2; +#ifndef __rtems__ cpu_tick_calibrate(1); nanotime(&tbef); +#endif /* __rtems__ */ timespec2bintime(ts, &bt); binuptime(&bt2); bintime_sub(&bt, &bt2); bintime_add(&bt2, &boottimebin); boottimebin = bt; +#ifndef __rtems__ bintime2timeval(&bt, &boottime); +#endif /* __rtems__ */ /* XXX fiddle all the little crinkly bits around the fiords... */ tc_windup(); +#ifndef __rtems__ nanotime(&taft); if (timestepwarnings) { log(LOG_INFO, @@ -1240,6 +1326,7 @@ tc_setclock(struct timespec *ts) (intmax_t)ts->tv_sec, ts->tv_nsec); } cpu_tick_calibrate(1); +#endif /* __rtems__ */ } /* @@ -1256,6 +1343,11 @@ tc_windup(void) uint32_t delta, ncount, ogen; int i; time_t t; +#ifdef __rtems__ + ISR_lock_Context lock_context; + + _ISR_lock_ISR_disable_and_acquire(&_Timecounter_Lock, &lock_context); +#endif /* __rtems__ */ /* * Make the next timehands a copy of the current one, but do not @@ -1333,16 +1425,20 @@ tc_windup(void) /* Now is a good time to change timecounters. */ if (th->th_counter != timecounter) { +#ifndef __rtems__ #ifndef __arm__ if ((timecounter->tc_flags & TC_FLAGS_C2STOP) != 0) cpu_disable_c2_sleep++; if ((th->th_counter->tc_flags & TC_FLAGS_C2STOP) != 0) cpu_disable_c2_sleep--; #endif +#endif /* __rtems__ */ th->th_counter = timecounter; th->th_offset_count = ncount; +#ifndef __rtems__ tc_min_ticktock_freq = max(1, timecounter->tc_frequency / (((uint64_t)timecounter->tc_counter_mask + 1) / 3)); +#endif /* __rtems__ */ #ifdef FFCLOCK ffclock_change_tc(th); #endif @@ -1401,9 +1497,15 @@ tc_windup(void) #endif timehands = th; +#ifndef __rtems__ timekeep_push_vdso(); +#endif /* __rtems__ */ +#ifdef __rtems__ + _ISR_lock_Release_and_ISR_enable(&_Timecounter_Lock, &lock_context); +#endif /* __rtems__ */ } +#ifndef __rtems__ /* Report or change the active timecounter hardware. */ static int sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) @@ -1468,7 +1570,9 @@ sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS) SYSCTL_PROC(_kern_timecounter, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_kern_timecounter_choice, "A", "Timecounter hardware detected"); +#endif /* __rtems__ */ +#ifndef __rtems__ /* * RFC 2783 PPS-API implementation. */ @@ -1749,6 +1853,9 @@ pps_event(struct pps_state *pps, int event) /* Wakeup anyone sleeping in pps_fetch(). */ wakeup(pps); } +#else /* __rtems__ */ +/* FIXME: https://devel.rtems.org/ticket/2349 */ +#endif /* __rtems__ */ /* * Timecounters need to be updated every so often to prevent the hardware @@ -1757,10 +1864,13 @@ pps_event(struct pps_state *pps, int event) * the update frequency. */ +#ifndef __rtems__ static int tc_tick; SYSCTL_INT(_kern_timecounter, OID_AUTO, tick, CTLFLAG_RD, &tc_tick, 0, "Approximate number of hardclock ticks in a millisecond"); +#endif /* __rtems__ */ +#ifndef __rtems__ void tc_ticktock(int cnt) { @@ -1770,9 +1880,54 @@ tc_ticktock(int cnt) if (count < tc_tick) return; count = 0; +#else /* __rtems__ */ +void +_Timecounter_Tick(void) +{ +#endif /* __rtems__ */ tc_windup(); } +#ifdef __rtems__ +void +_Timecounter_Tick_simple(uint32_t delta, uint32_t offset) +{ + struct bintime bt; + struct timehands *th; + uint32_t ogen; + ISR_lock_Context lock_context; + + _ISR_lock_ISR_disable_and_acquire(&_Timecounter_Lock, &lock_context); + + th = timehands; + ogen = th->th_generation; + th->th_offset_count = offset; + bintime_addx(&th->th_offset, th->th_scale * delta); + + bt = th->th_offset; + bintime_add(&bt, &boottimebin); + + /* Update the UTC timestamps used by the get*() functions. */ + /* XXX shouldn't do this here. Should force non-`get' versions. */ + bintime2timeval(&bt, &th->th_microtime); + bintime2timespec(&bt, &th->th_nanotime); + + /* + * Now that the struct timehands is again consistent, set the new + * generation number, making sure to not make it zero. + */ + if (++ogen == 0) + ogen = 1; + th->th_generation = ogen; + + /* Go live with the new struct timehands. */ + time_second = th->th_microtime.tv_sec; + time_uptime = th->th_offset.sec; + + _ISR_lock_Release_and_ISR_enable(&_Timecounter_Lock, &lock_context); +} +#endif /* __rtems__ */ +#ifndef __rtems__ static void __inline tc_adjprecision(void) { @@ -1794,7 +1949,9 @@ tc_adjprecision(void) sbt_timethreshold = bttosbt(bt_timethreshold); sbt_tickthreshold = bttosbt(bt_tickthreshold); } +#endif /* __rtems__ */ +#ifndef __rtems__ static int sysctl_kern_timecounter_adjprecision(SYSCTL_HANDLER_ARGS) { @@ -1811,10 +1968,17 @@ sysctl_kern_timecounter_adjprecision(SYSCTL_HANDLER_ARGS) done: return (0); } +#endif /* __rtems__ */ +#ifndef __rtems__ static void inittimecounter(void *dummy) +#else /* __rtems__ */ +void +_Timecounter_Initialize(void) +#endif /* __rtems__ */ { +#ifndef __rtems__ u_int p; int tick_rate; @@ -1838,6 +2002,7 @@ inittimecounter(void *dummy) tc_tick_sbt = bttosbt(tc_tick_bt); p = (tc_tick * 1000000) / hz; printf("Timecounters tick every %d.%03u msec\n", p / 1000, p % 1000); +#endif /* __rtems__ */ #ifdef FFCLOCK ffclock_init(); @@ -1848,8 +2013,11 @@ inittimecounter(void *dummy) tc_windup(); } +#ifndef __rtems__ SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_SECOND, inittimecounter, NULL); +#endif /* __rtems__ */ +#ifndef __rtems__ /* Cpu tick handling -------------------------------------------------*/ static int cpu_tick_variable; @@ -1982,7 +2150,9 @@ cputick2usec(uint64_t tick) } cpu_tick_f *cpu_ticks = tc_cpu_ticks; +#endif /* __rtems__ */ +#ifndef __rtems__ static int vdso_th_enable = 1; static int sysctl_fast_gettime(SYSCTL_HANDLER_ARGS) @@ -2018,6 +2188,7 @@ tc_fill_vdso_timehands(struct vdso_timehands *vdso_th) enabled = 0; return (enabled); } +#endif /* __rtems__ */ #ifdef COMPAT_FREEBSD32 uint32_t 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 diff --git a/testsuites/sptests/Makefile.am b/testsuites/sptests/Makefile.am index 9025ff3535..8844436876 100644 --- a/testsuites/sptests/Makefile.am +++ b/testsuites/sptests/Makefile.am @@ -38,6 +38,7 @@ else _SUBDIRS += sp29 endif _SUBDIRS += spintrcritical23 +_SUBDIRS += sptimecounter01 _SUBDIRS += spatomic01 _SUBDIRS += spintrcritical22 _SUBDIRS += spsem03 diff --git a/testsuites/sptests/configure.ac b/testsuites/sptests/configure.ac index ae3c763848..d463532124 100644 --- a/testsuites/sptests/configure.ac +++ b/testsuites/sptests/configure.ac @@ -41,6 +41,7 @@ AM_CONDITIONAL(HAS_SMP,test "$rtems_cv_RTEMS_SMP" = "yes") # Explicitly list all Makefiles here AC_CONFIG_FILES([Makefile spintrcritical23/Makefile +sptimecounter01/Makefile spatomic01/Makefile spglobalcon01/Makefile spintrcritical22/Makefile diff --git a/testsuites/sptests/sptimecounter01/Makefile.am b/testsuites/sptests/sptimecounter01/Makefile.am new file mode 100644 index 0000000000..b2310886fc --- /dev/null +++ b/testsuites/sptests/sptimecounter01/Makefile.am @@ -0,0 +1,19 @@ +rtems_tests_PROGRAMS = sptimecounter01 +sptimecounter01_SOURCES = init.c + +dist_rtems_tests_DATA = sptimecounter01.scn sptimecounter01.doc + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../automake/compile.am +include $(top_srcdir)/../automake/leaf.am + +AM_CPPFLAGS += -I$(top_srcdir)/../support/include + +LINK_OBJS = $(sptimecounter01_OBJECTS) +LINK_LIBS = $(sptimecounter01_LDLIBS) + +sptimecounter01$(EXEEXT): $(sptimecounter01_OBJECTS) $(sptimecounter01_DEPENDENCIES) + @rm -f sptimecounter01$(EXEEXT) + $(make-exe) + +include $(top_srcdir)/../automake/local.am diff --git a/testsuites/sptests/sptimecounter01/init.c b/testsuites/sptests/sptimecounter01/init.c new file mode 100644 index 0000000000..47ebb2722c --- /dev/null +++ b/testsuites/sptests/sptimecounter01/init.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +#include + +#include + +#include +#include +#include +#include + +const char rtems_test_name[] = "SPTIMECOUNTER_1"; + +typedef struct { + struct timecounter tc_soft; + u_int tc_soft_counter; +} test_context; + +static test_context test_instance; + +static uint32_t test_get_timecount_soft(struct timecounter *tc) +{ + test_context *ctx = tc->tc_priv; + + ++ctx->tc_soft_counter; + + return ctx->tc_soft_counter; +} + +void boot_card(const char *cmdline) +{ + test_context *ctx = &test_instance; + struct timecounter *tc_soft = &ctx->tc_soft; + uint64_t soft_freq = 1000000; + struct bintime bt; + + rtems_test_begink(); + + _Timecounter_Initialize(); + _Watchdog_Handler_initialization(); + + rtems_bsd_binuptime(&bt); + assert(bt.sec == 1); + assert(bt.frac== 0); + + rtems_bsd_binuptime(&bt); + assert(bt.sec == 1); + assert(bt.frac == 0); + + rtems_timecounter_tick(); + rtems_bsd_binuptime(&bt); + assert(bt.sec == 1); + assert(bt.frac == 0); + + ctx->tc_soft_counter = 0; + tc_soft->tc_get_timecount = test_get_timecount_soft; + tc_soft->tc_counter_mask = 0x0fffffff; + tc_soft->tc_frequency = soft_freq; + tc_soft->tc_quality = 1234; + tc_soft->tc_priv = ctx; + _Timecounter_Install(tc_soft); + assert(ctx->tc_soft_counter == 3); + + rtems_bsd_binuptime(&bt); + assert(ctx->tc_soft_counter == 4); + + assert(bt.sec == 1); + assert(bt.frac == 18446744073708); + + ctx->tc_soft_counter = 0xf0000000 | 3; + rtems_bsd_binuptime(&bt); + assert(ctx->tc_soft_counter == (0xf0000000 | 4)); + + assert(bt.sec == 1); + assert(bt.frac == 18446744073708); + + /* Ensure that the fraction overflows and the second remains constant */ + ctx->tc_soft_counter = (0xf0000000 | 3) + soft_freq; + rtems_bsd_binuptime(&bt); + assert(ctx->tc_soft_counter == (0xf0000000 | 4) + soft_freq); + assert(bt.sec == 1); + assert(bt.frac == 18446742522092); + + rtems_test_endk(); + + _Terminate(RTEMS_FATAL_SOURCE_EXIT, false, 0); +} + +#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER + +#define CONFIGURE_APPLICATION_DISABLE_FILESYSTEM + +#define CONFIGURE_DISABLE_NEWLIB_REENTRANCY + +#define CONFIGURE_SCHEDULER_USER + +#define CONFIGURE_SCHEDULER_CONTEXT + +#define CONFIGURE_SCHEDULER_CONTROLS { } + +#define CONFIGURE_MEMORY_PER_TASK_FOR_SCHEDULER 0 + +#define CONFIGURE_TASK_STACK_ALLOCATOR NULL + +#define CONFIGURE_TASK_STACK_DEALLOCATOR NULL + +#define CONFIGURE_IDLE_TASK_INITIALIZES_APPLICATION + +#define CONFIGURE_IDLE_TASK_BODY NULL + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_INIT + +#include diff --git a/testsuites/sptests/sptimecounter01/sptimecounter01.doc b/testsuites/sptests/sptimecounter01/sptimecounter01.doc new file mode 100644 index 0000000000..4a7442c2e6 --- /dev/null +++ b/testsuites/sptests/sptimecounter01/sptimecounter01.doc @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +This file describes the directives and concepts tested by this test set. + +test set name: sptimecounter01 + +directives: + + _Timecounter_Initialize + rtems_timecounter_tick + _Timecounter_Install + rtems_bsd_bintime + +concepts: + + This test checks the correct functioning of the FreeBSD timecounter startup + process diff --git a/testsuites/sptests/sptimecounter01/sptimecounter01.scn b/testsuites/sptests/sptimecounter01/sptimecounter01.scn new file mode 100644 index 0000000000..5fa9c0f281 --- /dev/null +++ b/testsuites/sptests/sptimecounter01/sptimecounter01.scn @@ -0,0 +1,2 @@ +*** BEGIN OF TEST SPTIMECOUNTER_1 *** +*** END OF TEST SPTIMECOUNTER_1 *** -- cgit v1.2.3