Tickless: If using an ALARM, then really should report time of expiration

This commit is contained in:
Gregory Nutt 2014-08-12 10:00:32 -06:00
parent 66803d0db6
commit 686e5d7e3d
4 changed files with 259 additions and 16 deletions

View File

@ -120,7 +120,8 @@
<a href="#listmgmt">4.5.2 OS List Management APIs</a><br>
<a href="#schedprocesstimer">4.5.3 <code>sched_process_timer()</code></a><br>
<a href="#schedtimerexpiration">4.5.4 <code>sched_timer_expiration()</code></a></br>
<a href="#irqdispatch">4.5.5 <code>irq_dispatch()</code></a>
<a href="#schedalarmexpiration">4.5.5 <code>sched_alarm_expiration()</code></a></br>
<a href="#irqdispatch">4.5.6 <code>irq_dispatch()</code></a>
</ul>
<a href="#demandpaging">4.6 On-Demand Paging</a><br>
<a href="#ledsupport">4.7 LED Support</a>
@ -2539,14 +2540,17 @@ config ARCH_SIM
</p>
<p>
Since timers are a limited resource, the use of two timers could be an issue on some systems.
The job could be done with a single timer if, for example, the single timer were kept in a free-running at all times. Some timer/counters (such as the Atmel SAMA5's timer), for example, have the capability to generate a compare interrupt when the timer matches a comparison value but also to continue counting without stopping. if your hardware supports such counters, one might be able to simply set the comparison count at the value of the free running timer <i>PLUS</i> the desired delay. Then you could have both with a single timer: An interval timer and a free-running counter with the same timer!
The job could be done with a single timer if, for example, the single timer were kept in a free-running at all times. Some timer/counters have the capability to generate a compare interrupt when the timer matches a comparison value but also to continue counting without stopping. If your hardware supports such counters, one might used the <code>CONFIG_SCHED_TICKLESS_ALARM</code> option and be able to simply set the comparison count at the value of the free running timer <i>PLUS</i> the desired delay. Then you could have both with a single timer: An alarm and a free-running counter with the same timer!
</p>
<p>
In addition to these imported interfaces, the RTOS will export the following interfaces for use by the platform-specific interval timer implementation:
</p>
<ul>
<li>
<a href="#schedtimerexpiration"><code>sched_timer_expiration()</code></a>
<a href="#schedalarmexpiration"><code>sched_alarm_expiration()</code></a>. Called by the platform-specific logic when the alarm expires.
</li>
<li>
<a href="#schedtimerexpiration"><code>sched_timer_expiration()</code></a>. Called by the platform-specific logic when the interval time expires.
</li>
</ul>
@ -2968,7 +2972,30 @@ void sched_timer_expiration(void);
Base code implementation assumes that this function is called from interrupt handling logic with interrupts disabled.
</ul>
<h3><a name="irqdispatch">4.5.5 <code>irq_dispatch()</code></a></h3>
<h3><a name="schedalarmexpiration">4.5.5 <code>sched_alaram_expiration()</code></a></h3>
<p><b>Prototype</b>:<p>
<ul><pre>
#include &lt;nuttx/arch.h&gt;
void sched_timer_expiration(void);
</ul>
<p><b>Description</b>:</p>
Description: if <code>CONFIG_SCHED_TICKLESS</code> is defined, then this function is provided by the RTOS base code and called from platform-specific code when the interval timer used to implemented the tick-less OS expires.
<ul>
</ul>
<p><b>Input Parameters</b>:</p>
<ul>
None
</ul>
<p><b>Returned Value</b>:</p>
<ul>
None
</ul>
<p><b>Assumptions</b>:</p>
<ul>
Base code implementation assumes that this function is called from interrupt handling logic with interrupts disabled.
</ul>
<h3><a name="irqdispatch">4.5.6 <code>irq_dispatch()</code></a></h3>
<p><b>Prototype</b>: <code>void irq_dispatch(int irq, FAR void *context);</code></p>
<p><b>Description</b>.

View File

@ -956,7 +956,6 @@ int up_prioritize_irq(int irq, int priority);
* events to occur after an interval. With the alarm, you can set a time in
* the future and get an event when that alarm goes off.
*
* #ifdef CONFIG_SCHED_TICKLESS_ALARM
* int up_alarm_cancel(void): Cancel the alarm.
* int up_alarm_start(FAR const struct timespec *ts): Enable (or re-anable
* the alarm.
@ -969,8 +968,13 @@ int up_prioritize_irq(int irq, int priority);
* The RTOS will provide the following interfaces for use by the platform-
* specific interval timer implementation:
*
* #ifdef CONFIG_SCHED_TICKLESS_ALARM
* void sched_alarm_expiration(FAR const struct timespec *ts): Called
* by the platform-specific logic when the alarm expires.
* #else
* void sched_timer_expiration(void): Called by the platform-specific
* logic when the interval timer expires.
* #endif
*
****************************************************************************/
@ -1046,7 +1050,7 @@ int up_timer_gettime(FAR struct timespec *ts);
* Description:
* Cancel the alarm and return the time of cancellation of the alarm.
* These two steps need to be as nearly atomic as possible.
* sched_timer_expiration() will not be called unless the alarm is
* sched_alarm_expiration() will not be called unless the alarm is
* restarted with up_alarm_start().
*
* If, as a race condition, the alarm has already expired when this
@ -1082,14 +1086,14 @@ int up_alarm_cancel(FAR struct timespec *ts);
* Name: up_alarm_start
*
* Description:
* Start the alarm. sched_timer_expiration() will be called when the
* Start the alarm. sched_alarm_expiration() will be called when the
* alarm occurs (unless up_alaram_cancel is called to stop it).
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* ts - The time at the alarm is expected to occur. When the alarm occurs
* the timer logic will call sched_timer_expiration().
* ts - The time in the future at the alarm is expected to occur. When
* the alarm occurs the timer logic will call sched_alarm_expiration().
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
@ -1273,7 +1277,7 @@ void sched_process_timer(void);
* Description:
* if CONFIG_SCHED_TICKLESS is defined, then this function is provided by
* the RTOS base code and called from platform-specific code when the
* interval timer used to implemented the tick-less OS expires.
* interval timer used to implement the tick-less OS expires.
*
* Input Parameters:
* None
@ -1287,10 +1291,34 @@ void sched_process_timer(void);
*
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
#if defined(CONFIG_SCHED_TICKLESS) && !defined(CONFIG_SCHED_TICKLESS_ALARM)
void sched_timer_expiration(void);
#endif
/****************************************************************************
* Name: sched_alarm_expiration
*
* Description:
* if CONFIG_SCHED_TICKLESS is defined, then this function is provided by
* the RTOS base code and called from platform-specific code when the
* alarm used to implement the tick-less OS expires.
*
* Input Parameters:
* ts - The time that the alarm expired
*
* Returned Value:
* None
*
* Assumptions/Limitations:
* Base code implementation assumes that this function is called from
* interrupt handling logic with interrupts disabled.
*
****************************************************************************/
#if defined(CONFIG_SCHED_TICKLESS) && defined(CONFIG_SCHED_TICKLESS_ALARM)
void sched_alarm_expiration(FAR const struct *ts);
#endif
/************************************************************************
* Name: sched_process_cpuload
*

View File

@ -66,7 +66,7 @@ config SCHED_TICKLESS
defined include/nuttx/arch.h
if SCHED_TICKLESS
conf SCHED_TICKLESS_ALARM
config SCHED_TICKLESS_ALARM
bool "Tickless alarm"
default n
---help---

View File

@ -85,9 +85,104 @@
static unsigned int g_timer_interval;
#ifdef CONFIG_SCHED_TICKLESS_ALARM
/* This is the time that the timer was stopped. All future times are
* calculated against this time. It must be valid at all times when
* the timer is not running.
*/
static struct timespec g_stop_time;
#endif
/************************************************************************
* Private Functions
************************************************************************/
/************************************************************************
* Name: sched_timespec_add
*
* Description:
* Add timespec ts1 to to2 and return the result in ts3
*
* Inputs:
* ts1 and ts2: The two timespecs to be added
* t23: The location to return the result (may be ts1 or ts2)
*
* Return Value:
* None
*
************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS_ALARM
static void sched_timespec_add(FAR const struct timespec *ts1,
FAR const struct timespec *ts2,
FAR struct timespec ts3)
{
time_t sec = ts1->tv_sec + ts2->tv_sec;
long nsec = ts1->tv_nsec + ts2->tv_nsec;
if (nsec >= NSEC_PER_SEC)
{
nsec -= NSEC_PER_SEC
sec++;
}
ts3->tv_sec = sec;
ts3->tv_nsec = nsec;
}
#endif
/************************************************************************
* Name: sched_timespec_subtract
*
* Description:
* Subtract timespec ts2 from to1 and return the result in ts3.
* Zero is returned if the time difference is negative.
*
* Inputs:
* ts1 and ts2: The two timespecs to be subtracted (ts1 - ts2)
* t23: The location to return the result (may be ts1 or ts2)
*
* Return Value:
* None
*
************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS_ALARM
static void sched_timespec_subtract(FAR const struct timespec *ts1,
FAR const struct timespec *ts2,
FAR struct timespec ts3)
{
time_t sec;
long nsec;
if (ts1->tv_sec < ts2->tv_sec)
{
sec = 0;
nsec = 0;
}
else if (ts1->tv_sec == ts2->tv_sec && ts1->tv_nsec <= ts2->tv_nsec)
{
sec = 0;
nsec = 0;
}
else
{
sec = ts1->tv_sec + ts2->tv_sec;
if (ts1->tv_nsec < ts2->tv_nsec)
{
nsec = (ts1->tv_nsec + NSEC_PER_SEC) - ts2->tv_nsec;
sec--;
}
else
{
nsec = ts1->tv_nsec - ts2->tv_nsec;
}
}
ts3->tv_sec = sec;
ts3->tv_nsec = sec;
}
#endif
/************************************************************************
* Name: sched_process_timeslice
@ -329,6 +424,14 @@ static void sched_timer_start(unsigned int ticks)
ts.tv_sec = (time_t)secs;
ts.tv_nsec = (long)nsecs;
#ifdef CONFIG_SCHED_TICKLESS_ALARM
/* Convert the delay to a time in the future (with respect
* to the time when last stopped the timer).
*/
sched_timespec_add(&g_stop_time, &ts, &ts);
#endif
/* [Re-]start the interval timer */
ret = up_timer_start(&ts);
@ -345,27 +448,39 @@ static void sched_timer_start(unsigned int ticks)
************************************************************************/
/****************************************************************************
* Name: sched_timer_expiration
* Name: sched_alarm_expiration
*
* Description:
* if CONFIG_SCHED_TICKLESS is defined, then this function is provided by
* the RTOS base code and called from platform-specific code when the
* interval timer used to implemented the tick-less OS expires.
* alarm used to implement the tick-less OS expires.
*
* Input Parameters:
* ts - The time that the alarm expired
*
* Returned Value:
* None
*
* Assumptions/Limitations:
* Base code implementation assumes that this function is called from
* interrupt handling logic with interrupts disabled.
*
****************************************************************************/
void sched_timer_expiration(void)
#ifdef CONFIG_SCHED_TICKLESS_ALARM
void sched_alarm_expiration(FAR const struct *ts);
{
unsigned int elapsed;
unsigned int nexttime;
/* Get the interval associated with las expiration */
DEBUGASSERT(ts);
/* Save the time that the alarm occurred */
g_stop_time.tv_sec = ts->tv_sec;
g_stop_time.tv_nsec = ts->tv_nsec;
/* Get the interval associated with last expiration */
elapsed = g_timer_interval;
g_timer_interval = 0;
@ -375,6 +490,41 @@ void sched_timer_expiration(void)
nexttime = sched_timer_process(elapsed, false);
sched_timer_start(nexttime);
}
#endif
/****************************************************************************
* Name: sched_timer_expiration
*
* Description:
* if CONFIG_SCHED_TICKLESS is defined, then this function is provided by
* the RTOS base code and called from platform-specific code when the
* interval timer used to implement the tick-less OS expires.
*
* Input Parameters:
*
* Returned Value:
* Base code implementation assumes that this function is called from
* interrupt handling logic with interrupts disabled.
*
****************************************************************************/
#ifndef CONFIG_SCHED_TICKLESS_ALARM
void sched_timer_expiration(void)
{
unsigned int elapsed;
unsigned int nexttime;
/* Get the interval associated with last expiration */
elapsed = g_timer_interval;
g_timer_interval = 0;
/* Process the timer ticks and set up the next interval (or not) */
nexttime = sched_timer_process(elapsed, false);
sched_timer_start(nexttime);
}
#endif
/****************************************************************************
* Name: sched_timer_cancel
@ -399,6 +549,38 @@ void sched_timer_expiration(void)
*
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS_ALARM
unsigned int sched_timer_cancel(void)
{
struct timespec ts;
unsigned int elapsed;
/* Cancel the alarm and and get the time that the alarm was cancelled.
* If the alarm was not enabled (or, perhaps, just expired since
* interrupts were disabled), up_timer_cancel() will return the
* current time.
*/
ts.tv_sec = g_stop_time.tv_sec;
ts.tv_nsec = g_stop_time.tv_nsec;
g_timer_interval = 0;
(void)up_timer_cancel(&g_stop_time);
/* Convert this to the elapsed time */
sched_timespec_subtract(&g_stop_time, &ts, &ts);
/* Convert to ticks */
elapsed = SEC2TICK(ts.tv_sec);
elapsed += NSEC2TICK(ts.tv_nsec);
/* Process the timer ticks and return the next interval */
return sched_timer_process(elapsed, true);
}
#else
unsigned int sched_timer_cancel(void)
{
struct timespec ts;
@ -428,6 +610,7 @@ unsigned int sched_timer_cancel(void)
return sched_timer_process(elapsed, true);
}
#endif
/****************************************************************************
* Name: sched_timer_resume
@ -443,6 +626,11 @@ unsigned int sched_timer_cancel(void)
* Returned Value:
* None.
*
* Assumptions:
* This function is called right after sched_timer_cancel(). If
* CONFIG_SCHED_TICKLESS_ALARM=y, then g_stop_time must be the value time
* when the timer was cancelled.
*
****************************************************************************/
void sched_timer_resume(void)