arch_timer: adjust timer/arch_timer to support tick

Enable CONFIG_SCHED_TICKLESS_TICK_ARGUMENT in tickless mode
to improve the performance.
This commit is contained in:
zhangyuan21 2022-09-09 15:17:18 +08:00 committed by Xiang Xiao
parent 189aa0292f
commit b118083c35
5 changed files with 172 additions and 109 deletions

View File

@ -72,7 +72,7 @@
* wrap around. Timer's base clock is dynamically changed with cpu clock.
*/
#define TIMER_MAXTIMEOUT (ULONG_MAX / 160 / TIMER_DIVIDER)
#define CXD56_MAXTIMEOUT (ULONG_MAX / 160 / TIMER_DIVIDER)
/****************************************************************************
* Private Types
@ -377,10 +377,10 @@ static int cxd56_settimeout(struct timer_lowerhalf_s *lower,
/* Can this timeout be represented? */
if (timeout < 1 || timeout > TIMER_MAXTIMEOUT)
if (timeout < 1 || timeout > CXD56_MAXTIMEOUT)
{
tmrerr("ERROR: Cannot represent timeout=%" PRIu32 " > %lu\n",
timeout, TIMER_MAXTIMEOUT);
timeout, CXD56_MAXTIMEOUT);
return -ERANGE;
}

View File

@ -75,6 +75,7 @@ config TIMER_ARCH
select ARCH_HAVE_TICKLESS
select ARCH_HAVE_TIMEKEEPING
select SCHED_TICKLESS_LIMIT_MAX_SLEEP if SCHED_TICKLESS
select SCHED_TICKLESS_TICK_ARGUMENT if SCHED_TICKLESS
---help---
Implement timer arch API on top of timer driver interface.

View File

@ -40,12 +40,6 @@
#define CONFIG_BOARD_LOOPSPER10USEC ((CONFIG_BOARD_LOOPSPERMSEC+50)/100)
#define CONFIG_BOARD_LOOPSPERUSEC ((CONFIG_BOARD_LOOPSPERMSEC+500)/1000)
#define TIMER_START(l) ((l)->ops->start(l))
#define TIMER_GETSTATUS(l,s) ((l)->ops->getstatus(l,s))
#define TIMER_SETTIMEOUT(l,t) ((l)->ops->settimeout(l,t))
#define TIMER_SETCALLBACK(l,c,a) ((l)->ops->setcallback(l,c,a))
#define TIMER_MAXTIMEOUT(l,t) ((l)->ops->maxtimeout(l,t))
/****************************************************************************
* Private Types
****************************************************************************/
@ -54,8 +48,7 @@ struct arch_timer_s
{
FAR struct timer_lowerhalf_s *lower;
uint32_t *next_interval;
uint32_t maxtimeout;
uint64_t timebase;
clock_t timebase;
};
/****************************************************************************
@ -77,15 +70,6 @@ static inline void timespec_from_usec(FAR struct timespec *ts,
}
#ifdef CONFIG_SCHED_TICKLESS
static inline uint64_t timespec_to_usec(FAR const struct timespec *ts)
{
return (uint64_t)ts->tv_sec * USEC_PER_SEC + ts->tv_nsec / NSEC_PER_USEC;
}
static inline bool timeout_diff(uint32_t new, uint32_t old)
{
return new < old ? old - new >= USEC_PER_TICK : new - old >= USEC_PER_TICK;
}
static uint32_t update_timeout(uint32_t timeout)
{
@ -95,7 +79,7 @@ static uint32_t update_timeout(uint32_t timeout)
* since caller already do it for us
*/
TIMER_GETSTATUS(g_timer.lower, &status);
TIMER_TICK_GETSTATUS(g_timer.lower, &status);
if (g_timer.next_interval)
{
/* If the timer interrupt is in the process,
@ -104,11 +88,11 @@ static uint32_t update_timeout(uint32_t timeout)
*g_timer.next_interval = timeout;
}
else if (timeout_diff(timeout, status.timeleft))
else if (timeout != status.timeleft)
{
/* Otherwise, update the timeout directly. */
TIMER_SETTIMEOUT(g_timer.lower, timeout);
TIMER_TICK_SETTIMEOUT(g_timer.lower, timeout);
g_timer.timebase += status.timeout - status.timeleft;
}
@ -119,7 +103,7 @@ static uint32_t update_timeout(uint32_t timeout)
static uint64_t current_usec(void)
{
struct timer_status_s status;
uint64_t timebase;
clock_t timebase;
do
{
@ -128,7 +112,7 @@ static uint64_t current_usec(void)
}
while (timebase != g_timer.timebase);
return timebase + (status.timeout - status.timeleft);
return TICK2USEC(timebase) + (status.timeout - status.timeleft);
}
static void udelay_accurate(useconds_t microseconds)
@ -187,27 +171,26 @@ static void udelay_coarse(useconds_t microseconds)
}
}
static bool timer_callback(FAR uint32_t *next_interval_us, FAR void *arg)
static bool timer_callback(FAR uint32_t *next_interval, FAR void *arg)
{
#ifdef CONFIG_SCHED_TICKLESS
struct timer_status_s status;
uint32_t next_interval;
uint32_t temp_interval;
g_timer.timebase += *next_interval_us;
next_interval = g_timer.maxtimeout;
g_timer.next_interval = &next_interval;
g_timer.timebase += *next_interval;
temp_interval = g_oneshot_maxticks;
g_timer.next_interval = &temp_interval;
nxsched_timer_expiration();
g_timer.next_interval = NULL;
TIMER_GETSTATUS(g_timer.lower, &status);
if (timeout_diff(next_interval, status.timeleft))
TIMER_TICK_GETSTATUS(g_timer.lower, &status);
if (temp_interval != status.timeleft)
{
g_timer.timebase += status.timeout - status.timeleft;
*next_interval_us = next_interval;
*next_interval = temp_interval;
}
#else
g_timer.timebase += USEC_PER_TICK;
g_timer.timebase++;
nxsched_process_timer();
#endif
@ -222,13 +205,11 @@ void up_timer_set_lowerhalf(FAR struct timer_lowerhalf_s *lower)
{
g_timer.lower = lower;
TIMER_MAXTIMEOUT(g_timer.lower, &g_timer.maxtimeout);
#ifdef CONFIG_SCHED_TICKLESS
g_oneshot_maxticks = g_timer.maxtimeout / USEC_PER_TICK;
TIMER_SETTIMEOUT(g_timer.lower, g_timer.maxtimeout);
g_oneshot_maxticks = TIMER_TICK_MAXTIMEOUT(lower);
TIMER_TICK_SETTIMEOUT(g_timer.lower, g_oneshot_maxticks);
#else
TIMER_SETTIMEOUT(g_timer.lower, USEC_PER_TICK);
TIMER_TICK_SETTIMEOUT(g_timer.lower, 1);
#endif
TIMER_SETCALLBACK(g_timer.lower, timer_callback, NULL);
@ -271,7 +252,7 @@ void up_timer_set_lowerhalf(FAR struct timer_lowerhalf_s *lower)
#ifdef CONFIG_CLOCK_TIMEKEEPING
void weak_function up_timer_getmask(FAR clock_t *mask)
{
uint32_t maxticks = g_timer.maxtimeout / USEC_PER_TICK;
uint32_t maxticks = TIMER_TICK_MAXTIMEOUT(g_timer.lower);
*mask = 0;
while (1)
@ -287,22 +268,7 @@ void weak_function up_timer_getmask(FAR clock_t *mask)
}
#endif
#if defined(CONFIG_SCHED_TICKLESS) && !defined(CONFIG_SCHED_TICKLESS_TICK_ARGUMENT)
int weak_function up_timer_gettime(FAR struct timespec *ts)
{
int ret = -EAGAIN;
if (g_timer.lower != NULL)
{
timespec_from_usec(ts, current_usec());
ret = OK;
}
return ret;
}
#endif
#if defined(CONFIG_SCHED_TICKLESS_TICK_ARGUMENT) || defined(CONFIG_CLOCK_TIMEKEEPING)
#if defined(CONFIG_SCHED_TICKLESS) || defined(CONFIG_CLOCK_TIMEKEEPING)
int weak_function up_timer_gettick(FAR clock_t *ticks)
{
int ret = -EAGAIN;
@ -354,13 +320,13 @@ int weak_function up_timer_gettick(FAR clock_t *ticks)
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int weak_function up_timer_cancel(FAR struct timespec *ts)
int weak_function up_timer_tick_cancel(FAR clock_t *ticks)
{
int ret = -EAGAIN;
if (g_timer.lower != NULL)
{
timespec_from_usec(ts, update_timeout(g_timer.maxtimeout));
*ticks = update_timeout(g_oneshot_maxticks);
ret = OK;
}
@ -394,13 +360,13 @@ int weak_function up_timer_cancel(FAR struct timespec *ts)
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int weak_function up_timer_start(FAR const struct timespec *ts)
int weak_function up_timer_tick_start(clock_t ticks)
{
int ret = -EAGAIN;
if (g_timer.lower != NULL)
{
update_timeout(timespec_to_usec(ts));
update_timeout(ticks);
ret = OK;
}

View File

@ -285,14 +285,7 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
/* Start the timer, resetting the time to the current timeout */
if (lower->ops->start)
{
ret = lower->ops->start(lower);
}
else
{
ret = -ENOSYS;
}
ret = TIMER_START(lower);
}
break;
@ -305,8 +298,7 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
/* Stop the timer */
DEBUGASSERT(lower->ops->stop != NULL); /* Required */
ret = lower->ops->stop(lower);
ret = TIMER_STOP(lower);
nxsig_cancel_notification(&upper->work);
}
break;
@ -322,21 +314,14 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* Get the current timer status */
if (lower->ops->getstatus) /* Optional */
status = (FAR struct timer_status_s *)((uintptr_t)arg);
if (status)
{
status = (FAR struct timer_status_s *)((uintptr_t)arg);
if (status)
{
ret = lower->ops->getstatus(lower, status);
}
else
{
ret = -EINVAL;
}
ret = TIMER_GETSTATUS(lower, status);
}
else
{
ret = -ENOSYS;
ret = -EINVAL;
}
}
break;
@ -353,14 +338,7 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
/* Set a new timeout value (and reset the timer) */
if (lower->ops->settimeout) /* Optional */
{
ret = lower->ops->settimeout(lower, (uint32_t)arg);
}
else
{
ret = -ENOSYS;
}
ret = TIMER_SETTIMEOUT(lower, (uint32_t)arg);
}
break;
@ -396,14 +374,7 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
/* Get the maximum supported timeout value */
if (lower->ops->maxtimeout) /* Optional */
{
ret = lower->ops->maxtimeout(lower, (FAR uint32_t *)arg);
}
else
{
ret = -ENOSYS;
}
ret = TIMER_MAXTIMEOUT(lower, (FAR uint32_t *)arg);
}
break;
@ -420,14 +391,7 @@ static int timer_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
* method.
*/
if (lower->ops->ioctl) /* Optional */
{
ret = lower->ops->ioctl(lower, cmd, arg);
}
else
{
ret = -ENOTTY;
}
ret = TIMER_IOCTL(lower, cmd, arg);
}
break;
}
@ -553,8 +517,7 @@ void timer_unregister(FAR void *handle)
/* Disable the timer */
DEBUGASSERT(lower->ops->stop); /* Required */
lower->ops->stop(lower);
TIMER_STOP(lower);
nxsig_cancel_notification(&upper->work);
/* Unregister the timer device */

View File

@ -25,6 +25,7 @@
* Included Files
****************************************************************************/
#include <nuttx/clock.h>
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <nuttx/irq.h>
@ -88,6 +89,37 @@
#define TCFLAGS_HANDLER (1 << 1) /* 1=Call the user function when the
* timer expires */
/* Method access helper macros **********************************************/
#define TIMER_START(l) \
((l)->ops->start ? (l)->ops->start(l) : -ENOSYS)
#define TIMER_STOP(l) \
((l)->ops->stop ? (l)->ops->stop(l) : -ENOSYS)
#define TIMER_GETSTATUS(l,s) \
((l)->ops->getstatus ? (l)->ops->getstatus(l,s) : timer_getstatus(l,s))
#define TIMER_TICK_GETSTATUS(l,s) \
((l)->ops->tick_getstatus ? (l)->ops->tick_getstatus(l,s) : timer_tick_getstatus(l,s))
#define TIMER_SETTIMEOUT(l,t) \
((l)->ops->settimeout ? (l)->ops->settimeout(l,t) : timer_settimeout(l,t))
#define TIMER_TICK_SETTIMEOUT(l,t) \
((l)->ops->tick_setttimeout ? (l)->ops->tick_setttimeout(l,t) : timer_tick_settimeout(l,t))
#define TIMER_MAXTIMEOUT(l,t) \
((l)->ops->maxtimeout ? (l)->ops->maxtimeout(l,t) : timer_maxtimeout(l,t))
#define TIMER_TICK_MAXTIMEOUT(l,t) \
((l)->ops->tick_maxtimeout ? (l)->ops->tick_maxtimeout(l,t) : timer_tick_maxtimeout(l,t))
#define TIMER_SETCALLBACK(l,c,a) ((l)->ops->setcallback(l,c,a))
#define TIMER_IOCTL(l,c,a) \
((l)->ops->ioctl ? (l)->ops->ioctl(l,c,a) : -ENOTTY)
/****************************************************************************
* Public Types
****************************************************************************/
@ -96,7 +128,7 @@
* function can modify the next interval if desired.
*/
typedef CODE bool (*tccb_t)(FAR uint32_t *next_interval_us, FAR void *arg);
typedef CODE bool (*tccb_t)(FAR uint32_t *next_interval, FAR void *arg);
/* This is the type of the argument passed to the TCIOC_GETSTATUS ioctl and
* and returned by the "lower half" getstatus() method.
@ -165,6 +197,21 @@ struct timer_ops_s
CODE int (*maxtimeout)(FAR struct timer_lowerhalf_s *lower,
FAR uint32_t *maxtimeout);
/* Get the current tick timer status */
CODE int (*tick_getstatus)(FAR struct timer_lowerhalf_s *lower,
FAR struct timer_status_s *status);
/* Set a new tick timeout value of (and reset the timer) */
CODE int (*tick_setttimeout)(FAR struct timer_lowerhalf_s *lower,
uint32_t timeout);
/* Get the maximum supported tick timeout value */
CODE int (*tick_maxtimeout)(FAR struct timer_lowerhalf_s *lower,
FAR uint32_t *maxtimeout);
};
/* This structure provides the publicly visible representation of the
@ -200,6 +247,92 @@ extern "C"
#define EXTERN extern
#endif
static inline
int timer_getstatus(FAR struct timer_lowerhalf_s *lower,
FAR struct timer_status_s *status)
{
int ret;
DEBUGASSERT(lower->ops->tick_getstatus);
ret = lower->ops->tick_getstatus(lower, status);
if (ret >= 0)
{
status->timeout = TICK2USEC(status->timeout);
status->timeleft = TICK2USEC(status->timeleft);
}
return ret;
}
static inline
int timer_settimeout(FAR struct timer_lowerhalf_s *lower,
uint32_t timeout)
{
DEBUGASSERT(lower->ops->tick_setttimeout);
return lower->ops->tick_setttimeout(lower, USEC2TICK(timeout));
}
static inline
int timer_maxtimeout(FAR struct timer_lowerhalf_s *lower,
FAR uint32_t *maxtimeout)
{
int ret;
DEBUGASSERT(lower->ops->tick_maxtimeout);
ret = lower->ops->tick_maxtimeout(lower, maxtimeout);
if (ret >= 0)
{
*maxtimeout = TICK2USEC(*maxtimeout);
}
return ret;
}
static inline
int timer_tick_getstatus(FAR struct timer_lowerhalf_s *lower,
FAR struct timer_status_s *status)
{
int ret;
DEBUGASSERT(lower->ops->getstatus);
ret = lower->ops->getstatus(lower, status);
if (ret >= 0)
{
status->timeout = USEC2TICK(status->timeout);
status->timeleft = USEC2TICK(status->timeleft);
}
return ret;
}
static inline
int timer_tick_settimeout(FAR struct timer_lowerhalf_s *lower,
uint32_t timeout)
{
DEBUGASSERT(lower->ops->settimeout);
return lower->ops->settimeout(lower, TICK2USEC(timeout));
}
static inline
int timer_tick_maxtimeout(FAR struct timer_lowerhalf_s *lower,
FAR uint32_t *maxtimeout)
{
int ret;
DEBUGASSERT(lower->ops->maxtimeout);
ret = lower->ops->maxtimeout(lower, maxtimeout);
if (ret >= 0)
{
*maxtimeout = USEC2TICK(*maxtimeout);
}
return ret;
}
/****************************************************************************
* "Upper-Half" Timer Driver Interfaces
****************************************************************************/