timecounter: Port to RTEMS

New test sptests/timecounter01.

Update #2271.
This commit is contained in:
Alexander Krutwig
2015-05-20 08:40:29 +02:00
committed by Sebastian Huber
parent cc6938455e
commit 31be41653a
19 changed files with 1431 additions and 72 deletions
+3
View File
@@ -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
+141
View File
@@ -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
* <rtems@embedded-brains.de>
*
* 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 <rtems/score/timecounter.h>
#include <rtems/score/basedefs.h>
#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 */
+304
View File
@@ -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
* <rtems@embedded-brains.de>
*
* 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 <rtems/score/timecounter.h>
#include <rtems/score/basedefs.h>
#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 <rtems/timecounter.h>
*
* 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 */
+8
View File
@@ -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
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2015 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* 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 <rtems/timecounter.h>
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 );
}
+12
View File
@@ -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
@@ -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
* <rtems@embedded-brains.de>
*
* 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 <sys/time.h>
#include <sys/timetc.h>
#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 */
@@ -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
* <rtems@embedded-brains.de>
*
* 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 <rtems/score/timecounter.h>
#include <sys/timetc.h>
#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 */
+2
View File
@@ -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
+2
View File
@@ -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
+33
View File
@@ -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
+171
View File
@@ -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 <rtems/score/timecounterimpl.h>
#endif /* __rtems__ */
#include <sys/cdefs.h>
__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 <sys/param.h>
#ifndef __rtems__
#include <sys/kernel.h>
#include <sys/limits.h>
#else /* __rtems__ */
#include <limits.h>
#endif /* __rtems__ */
#ifdef FFCLOCK
#include <sys/lock.h>
#include <sys/mutex.h>
#endif
#ifndef __rtems__
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#endif /* __rtems__ */
#include <sys/timeffc.h>
#include <sys/timepps.h>
#include <sys/timetc.h>
#include <sys/timex.h>
#ifndef __rtems__
#include <sys/vdso.h>
#endif /* __rtems__ */
#ifdef __rtems__
#include <rtems.h>
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
+270 -72
View File
@@ -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.
*/
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:
#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
@subsubsection Clock Tick Only Variant
@example
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
@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()}.
@example
#include <rtems/timecounter.h>
static struct timecounter some_tc;
static uint32_t some_tc_get_timecount( struct timecounter *tc )
@{
some.free_running_counter;
@}
static void some_support_initialize_hardware( void )
@{
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 );
@}
#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
#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
@subsection Support At Tick
The hardware specific support at tick is specified by
@code{Clock_driver_support_at_tick()}.
@example
static void some_support_at_tick( void )
@{
/* Clear interrupt */
@}
#define Clock_driver_support_at_tick() \
some_support_at_tick()
#include "../../../shared/clockdrv_shell.h"
@end example
@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
static void some_support_shutdown_hardware( void )
@{
/* Shutdown hardware */
@}
#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
@subsection 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.
This information is valuable when debugging a system. This variable is
declared as follows:
@example
volatile uint32_t Clock_driver_ticks;
@end example
@section Initialization
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.
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.
You must use the RTEMS primitive @code{rtems_interrupt_catch} to install
your clock interrupt service routine:
@example
rtems_interrupt_catch (Clock_ISR, CLOCK_VECTOR, &old_handler);
@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.
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.
@section System shutdown
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.
@section Clock Interrupt Subroutine
It only has to inform the kernel that a ticker has elapsed, so call :
@example
@group
rtems_isr Clock_isr( rtems_vector_number vector )
@{
invoke the rtems_clock_tick() directive to announce the tick
if necessary for this hardware
reload the programmable timer
@}
@end group
@end example
@section IO Control
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.
+1
View File
@@ -38,6 +38,7 @@ else
_SUBDIRS += sp29
endif
_SUBDIRS += spintrcritical23
_SUBDIRS += sptimecounter01
_SUBDIRS += spatomic01
_SUBDIRS += spintrcritical22
_SUBDIRS += spsem03
+1
View File
@@ -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
@@ -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
+133
View File
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2015 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* 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 <assert.h>
#include <bsp/bootcard.h>
#include <rtems/test.h>
#include <rtems/score/timecounterimpl.h>
#include <rtems/score/watchdogimpl.h>
#include <rtems/timecounter.h>
#include <rtems/bsd.h>
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 <rtems/confdefs.h>
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2015 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* 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
@@ -0,0 +1,2 @@
*** BEGIN OF TEST SPTIMECOUNTER_1 ***
*** END OF TEST SPTIMECOUNTER_1 ***