include/nuttx/clock.h, sched/sched: Support the CPU load measurement using timer_lowerhalf_s interface

This commit is contained in:
Xiang Xiao 2018-08-24 10:10:57 -06:00 committed by Gregory Nutt
parent 0074afa0ac
commit 3af35699b9
5 changed files with 311 additions and 14 deletions

View File

@ -426,6 +426,28 @@ struct oneshot_lowerhalf_s;
void sched_oneshot_extclk(FAR struct oneshot_lowerhalf_s *lower); void sched_oneshot_extclk(FAR struct oneshot_lowerhalf_s *lower);
#endif #endif
/****************************************************************************
* Name: sched_period_extclk
*
* Description:
* Configure to use a period timer as described in
* include/nuttx/timers/timer.h to provide external clocking to assess
* the CPU load.
*
* Input Parameters:
* lower - An instance of the period timer interface as defined in
* include/nuttx/timers/timer.h
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_CPULOAD_PERIOD
struct timer_lowerhalf_s;
void sched_period_extclk(FAR struct timer_lowerhalf_s *lower);
#endif
#undef EXTERN #undef EXTERN
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -729,6 +729,10 @@ config SCHED_CPULOAD_TICKSPERSEC
that 100Hz is the default frequency of the system time and, hence, that 100Hz is the default frequency of the system time and, hence,
the worst possible choice in most cases. the worst possible choice in most cases.
choice
prompt "Select CPU load timer"
default CPULOAD_ONESHOT
config CPULOAD_ONESHOT config CPULOAD_ONESHOT
bool "Use Oneshot timer" bool "Use Oneshot timer"
default n default n
@ -748,7 +752,25 @@ config CPULOAD_ONESHOT
significantly faster than CONFIG_SCHED_CPULOAD_TICKSPERSE to permit significantly faster than CONFIG_SCHED_CPULOAD_TICKSPERSE to permit
precise modulation the sample periods. precise modulation the sample periods.
config CPULOAD_ONESHOT_ENTROPY config CPULOAD_PERIOD
bool "Use Period timer"
default n
---help---
Use an MCU-specific period timer as the external clock. The
period timer must be configured by board specific logic which must
then call:
void sched_period_extclk(FAR struct timer_lowerhalf_s *lower);
To start the CPU load measurement. See include/nuttx/clock.h
NOTE that in this configuration, CONFIG_SCHED_CPULOAD_TICKSPERSEC is
the sample rate that will be accomplished by programming the period
time.
endchoice
config CPULOAD_ENTROPY
int "Bits of entropy" int "Bits of entropy"
default 6 default 6
range 0 30 range 0 30
@ -764,7 +786,7 @@ config CPULOAD_ONESHOT_ENTROPY
CPULOAD_ONESHOT_NOMINAL is the nominal sample interval implied CPULOAD_ONESHOT_NOMINAL is the nominal sample interval implied
by CONFIG_SCHED_CPULOAD_TICKSPERSEC in units of microseconds. by CONFIG_SCHED_CPULOAD_TICKSPERSEC in units of microseconds.
CPULOAD_ONESHOT_ENTROPY is (1 << CONFIG_CPULOAD_ONESHOT_ENTROPY), CPULOAD_ONESHOT_ENTROPY is (1 << CONFIG_CPULOAD_ENTROPY),
and 'error' is an error value that is retained from interval to and 'error' is an error value that is retained from interval to
interval so that although individual intervals are randomized, interval so that although individual intervals are randomized,
the average will still be CONFIG_SCHED_CPULOAD_TICKSPERSEC. the average will still be CONFIG_SCHED_CPULOAD_TICKSPERSEC.

View File

@ -86,6 +86,9 @@ CSRCS += sched_cpuload.c
ifeq ($(CONFIG_CPULOAD_ONESHOT),y) ifeq ($(CONFIG_CPULOAD_ONESHOT),y)
CSRCS += sched_cpuload_oneshot.c CSRCS += sched_cpuload_oneshot.c
endif endif
ifeq ($(CONFIG_CPULOAD_PERIOD),y)
CSRCS += sched_cpuload_period.c
endif
endif endif
ifeq ($(CONFIG_SCHED_TICKLESS),y) ifeq ($(CONFIG_SCHED_TICKLESS),y)

View File

@ -70,14 +70,14 @@
# error CONFIG_SCHED_CPULOAD_TICKSPERSEC not defined # error CONFIG_SCHED_CPULOAD_TICKSPERSEC not defined
#endif #endif
/* CONFIG_CPULOAD_ONESHOT_ENTROPY determines that amount of random "jitter" /* CONFIG_CPULOAD_ENTROPY determines that amount of random "jitter"
* that will be added to the nominal sample interval. Specified as a number * that will be added to the nominal sample interval. Specified as a number
* bits. * bits.
*/ */
#ifndef CONFIG_CPULOAD_ONESHOT_ENTROPY #ifndef CONFIG_CPULOAD_ENTROPY
# warning CONFIG_CPULOAD_ONESHOT_ENTROPY not defined # warning CONFIG_CPULOAD_ENTROPY not defined
# define CONFIG_CPULOAD_ONESHOT_ENTROPY 0 # define CONFIG_CPULOAD_ENTROPY 0
#endif #endif
/* Calculate the nomimal sample interval in microseconds: /* Calculate the nomimal sample interval in microseconds:
@ -93,10 +93,10 @@
/* Convert the entropy from number of bits to a numeric value */ /* Convert the entropy from number of bits to a numeric value */
#define CPULOAD_ONESHOT_ENTROPY (1 << CONFIG_CPULOAD_ONESHOT_ENTROPY) #define CPULOAD_ONESHOT_ENTROPY (1 << CONFIG_CPULOAD_ENTROPY)
#if CPULOAD_ONESHOT_NOMINAL < CPULOAD_ONESHOT_ENTROPY #if CPULOAD_ONESHOT_NOMINAL < CPULOAD_ONESHOT_ENTROPY
# error CPULOAD_ONESHOT_NOMINAL too small for CONFIG_CPULOAD_ONESHOT_ENTROPY # error CPULOAD_ONESHOT_NOMINAL too small for CONFIG_CPULOAD_ENTROPY
#endif #endif
#define CPULOAD_ONESHOT_ENTROPY_MASK (CPULOAD_ONESHOT_ENTROPY - 1) #define CPULOAD_ONESHOT_ENTROPY_MASK (CPULOAD_ONESHOT_ENTROPY - 1)
@ -108,7 +108,7 @@
struct sched_oneshot_s struct sched_oneshot_s
{ {
FAR struct oneshot_lowerhalf_s *oneshot; FAR struct oneshot_lowerhalf_s *oneshot;
#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 #if CONFIG_CPULOAD_ENTROPY > 0
struct xorshift128_state_s prng; struct xorshift128_state_s prng;
int32_t maxdelay; int32_t maxdelay;
int32_t error; int32_t error;
@ -152,7 +152,7 @@ static struct sched_oneshot_s g_sched_oneshot;
static void sched_oneshot_start(void) static void sched_oneshot_start(void)
{ {
struct timespec ts; struct timespec ts;
#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 #if CONFIG_CPULOAD_ENTROPY > 0
uint32_t entropy; uint32_t entropy;
#endif #endif
int32_t secs; int32_t secs;
@ -160,7 +160,7 @@ static void sched_oneshot_start(void)
/* Get the next delay */ /* Get the next delay */
#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 #if CONFIG_CPULOAD_ENTROPY > 0
/* The one shot will be set to this interval: /* The one shot will be set to this interval:
* *
* CPULOAD_ONESHOT_NOMINAL - (CPULOAD_ONESHOT_ENTROPY / 2) + error * CPULOAD_ONESHOT_NOMINAL - (CPULOAD_ONESHOT_ENTROPY / 2) + error
@ -234,7 +234,6 @@ static void sched_oneshot_callback(FAR struct oneshot_lowerhalf_s *lower,
{ {
sched_process_cpuload(); sched_process_cpuload();
} }
#endif
/* Then restart the oneshot */ /* Then restart the oneshot */
@ -264,14 +263,14 @@ static void sched_oneshot_callback(FAR struct oneshot_lowerhalf_s *lower,
void sched_oneshot_extclk(FAR struct oneshot_lowerhalf_s *lower) void sched_oneshot_extclk(FAR struct oneshot_lowerhalf_s *lower)
{ {
#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 #if CONFIG_CPULOAD_ENTROPY > 0
struct timespec ts; struct timespec ts;
#endif #endif
DEBUGASSERT(lower != NULL && lower->ops != NULL); DEBUGASSERT(lower != NULL && lower->ops != NULL);
DEBUGASSERT(lower->ops->start != NULL); DEBUGASSERT(lower->ops->start != NULL);
#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 #if CONFIG_CPULOAD_ENTROPY > 0
DEBUGASSERT(lower->ops->max_delay != NULL); DEBUGASSERT(lower->ops->max_delay != NULL);
/* Get the maximum delay */ /* Get the maximum delay */
@ -302,3 +301,4 @@ void sched_oneshot_extclk(FAR struct oneshot_lowerhalf_s *lower)
g_sched_oneshot.oneshot = lower; g_sched_oneshot.oneshot = lower;
sched_oneshot_start(); sched_oneshot_start();
} }
#endif

View File

@ -0,0 +1,250 @@
/****************************************************************************
* sched/sched/sched_cpuload_period.c
*
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
* Author: Xiang Xiao <xiaoxiang@pinecone.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/clock.h>
#include <nuttx/lib/xorshift128.h>
#include <nuttx/timers/timer.h>
#ifdef CONFIG_CPULOAD_PERIOD
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#if !defined(CONFIG_SCHED_CPULOAD) || !defined(CONFIG_SCHED_CPULOAD_EXTCLK)
# error CONFIG_SCHED_CPULOAD and CONFIG_SCHED_CPULOAD_EXTCLK must be defined
#endif
/* CONFIG_SCHED_CPULOAD_TICKSPERSEC is the frequency of the external clock
* source.
*/
#ifndef CONFIG_SCHED_CPULOAD_TICKSPERSEC
# error CONFIG_SCHED_CPULOAD_TICKSPERSEC not defined
#endif
/* CONFIG_CPULOAD_ENTROPY determines that amount of random "jitter"
* that will be added to the nominal sample interval. Specified as a number
* bits.
*/
#ifndef CONFIG_CPULOAD_ENTROPY
# warning CONFIG_CPULOAD_ENTROPY not defined
# define CONFIG_CPULOAD_ENTROPY 0
#endif
/* Calculate the nomimal sample interval in microseconds:
*
* nominal = (1,000,000 usec/sec) / Frequency cycles/sec) = Period usec/cycle
*/
#define CPULOAD_PERIOD_NOMINAL (1000000 / CONFIG_SCHED_CPULOAD_TICKSPERSEC)
#if CPULOAD_PERIOD_NOMINAL < 1 || CPULOAD_PERIOD_NOMINAL > 0x7fffffff
# error CPULOAD_PERIOD_NOMINAL is out of range
#endif
/* Convert the entropy from number of bits to a numeric value */
#define CPULOAD_PERIOD_ENTROPY (1 << CONFIG_CPULOAD_ENTROPY)
#if CPULOAD_PERIOD_NOMINAL < CPULOAD_PERIOD_ENTROPY
# error CPULOAD_PERIOD_NOMINAL too small for CONFIG_CPULOAD_ENTROPY
#endif
#define CPULOAD_PERIOD_ENTROPY_MASK (CPULOAD_PERIOD_ENTROPY - 1)
/****************************************************************************
* Private Types
****************************************************************************/
#if CONFIG_CPULOAD_ENTROPY > 0
struct sched_period_s
{
struct xorshift128_state_s prng;
uint32_t maxtimeout;
int32_t error;
};
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static bool sched_period_callback(FAR uint32_t *next_interval_us,
FAR void *arg);
/****************************************************************************
* Private Data
****************************************************************************/
#if CONFIG_CPULOAD_ENTROPY > 0
static struct sched_period_s g_sched_period;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sched_period_callback
*
* Description:
* This is the callback function that will be invoked when the period
* timer expires.
*
* Input Parameters:
* next_interval_us - The timeout value for next interval
* arg - The opaque argument provided when the callback was registered
*
* Returned Value:
* return false to stop the timer, true to continue the timing
*
****************************************************************************/
static bool sched_period_callback(FAR uint32_t *next_interval_us,
FAR void *arg)
{
/* Get the next delay */
#if CONFIG_CPULOAD_ENTROPY > 0
/* The period timer will be set to this interval:
*
* CPULOAD_PERIOD_NOMINAL - (CPULOAD_PERIOD_ENTROPY / 2) + error
* + nrand(CPULOAD_PERIOD_ENTROPY)
*/
*next_interval_us = CPULOAD_PERIOD_NOMINAL - CPULOAD_PERIOD_ENTROPY / 2 +
g_sched_period.error;
/* Add the random value in the range 0..(CPULOAD_PERIOD_ENTROPY - 1) */
*next_interval_us += xorshift128(&g_sched_period.prng) &
CPULOAD_PERIOD_ENTROPY_MASK;
DEBUGASSERT(*next_interval_us > 0); /* Check for overflow to negative or zero */
/* Make sure that the accumulated value does not exceed the maximum timeout */
if (*next_interval_us > g_sched_period.maxtimeout)
{
tmrwarn("WARNING: Truncating\n");
*next_interval_us = g_sched_period.maxtimeout;
}
/* Save the new error value */
g_sched_period.error = CPULOAD_PERIOD_NOMINAL +
g_sched_period.error - *next_interval_us;
#endif
/* Perform CPU load measurements */
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (sched_process_cpuload != NULL)
#endif
{
sched_process_cpuload();
}
/* Then continue the timing */
return true;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: sched_period_extclk
*
* Description:
* Configure to use a period timer as described in
* include/nuttx/timers/timer.h to provide external clocking to assess
* the CPU load.
*
* Input Parameters:
* lower - An instance of the period timer interface as defined in
* include/nuttx/timers/timer.h
*
* Returned Value:
* None
*
****************************************************************************/
void sched_period_extclk(FAR struct timer_lowerhalf_s *lower)
{
DEBUGASSERT(lower != NULL && lower->ops != NULL);
DEBUGASSERT(lower->ops->setcallback != NULL);
DEBUGASSERT(lower->ops->settimeout != NULL);
DEBUGASSERT(lower->ops->start != NULL);
#if CONFIG_CPULOAD_ENTROPY > 0
DEBUGASSERT(lower->ops->maxtimeout != NULL);
/* Get the maximum timeout */
DEBUGVERIFY(lower->ops->maxtimeout(lower, &g_sched_period.maxtimeout));
tmrinfo("maxtimeout = %lu usec\n", (long)g_sched_period.maxtimeout);
DEBUGASSERT(CPULOAD_PERIOD_NOMINAL < g_sched_period.maxtimeout);
/* Seed the PRNG */
g_sched_period.prng.w = 97;
g_sched_period.prng.x = 101;
g_sched_period.prng.y = g_sched_period.prng.w << 17;
g_sched_period.prng.z = g_sched_period.prng.x << 25;
#endif
/* Then start the period timer */
lower->ops->setcallback(lower, sched_period_callback, NULL);
lower->ops->settimeout(lower, CPULOAD_PERIOD_NOMINAL);
lower->ops->start(lower);
}
#endif