Improved and fixed tickless mode.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7842 35acf78f-673a-0410-8e92-d51de3d6d3f4
master
Giovanni Di Sirio 2015-04-02 10:43:11 +00:00
parent 9366ed77a6
commit 56d0fdc011
2 changed files with 89 additions and 55 deletions

View File

@ -339,6 +339,26 @@ static inline bool chVTIsArmedI(virtual_timer_t *vtp) {
return (bool)(vtp->vt_func != NULL); return (bool)(vtp->vt_func != NULL);
} }
/**
* @brief Returns @p true if the specified timer is armed.
* @pre The timer must have been initialized using @p chVTObjectInit()
* or @p chVTDoSetI().
*
* @param[in] vtp the @p virtual_timer_t structure pointer
* @return true if the timer is armed.
*
* @api
*/
static inline bool chVTIsArmed(virtual_timer_t *vtp) {
bool b;
chSysLock();
b = chVTIsArmedI(vtp);
chSysUnlock();
return b;
}
/** /**
* @brief Disables a Virtual Timer. * @brief Disables a Virtual Timer.
* @note The timer is first checked and disabled only if armed. * @note The timer is first checked and disabled only if armed.
@ -466,35 +486,43 @@ static inline void chVTDoTickI(void) {
} }
#else /* CH_CFG_ST_TIMEDELTA > 0 */ #else /* CH_CFG_ST_TIMEDELTA > 0 */
virtual_timer_t *vtp; virtual_timer_t *vtp;
systime_t now, delta;
/* First timer to be processed.*/
vtp = ch.vtlist.vt_next;
now = chVTGetSystemTimeX();
/* The list is assumed to be non-empty because an tick interrupt just /* The list is assumed to be non-empty because an tick interrupt just
occurred.*/ occurred.*/
chDbgAssert(&ch.vtlist != (virtual_timers_list_t *)ch.vtlist.vt_next, chDbgAssert(&ch.vtlist != (virtual_timers_list_t *)vtp,
"timers list empty"); "timers list empty");
/* First timer to be processed, its time is assumed to be between /* The timer time is assumed to be between "vt_lasttime" and "now".*/
"vt_lasttime" and "now".*/
vtp = ch.vtlist.vt_next;
chDbgAssert(chVTIsTimeWithinX(ch.vtlist.vt_lasttime + vtp->vt_delta, chDbgAssert(chVTIsTimeWithinX(ch.vtlist.vt_lasttime + vtp->vt_delta,
ch.vtlist.vt_lasttime, ch.vtlist.vt_lasttime,
chVTGetSystemTimeX() + 1), now + 1),
"out of time window"); "out of time window");
/* Timers processing loop.*/ /* All timers within the time window are triggered and removed,
while (true) { note that the loop is stopped by the timers header having
systime_t now; "ch.vtlist.vt_delta == (systime_t)-1" which is greater than
all deltas.*/
while (vtp->vt_delta <= now - ch.vtlist.vt_lasttime) {
vtfunc_t fn; vtfunc_t fn;
/* The "last time" becomes this timer's expiration time.*/ /* The "last time" becomes this timer's expiration time.*/
ch.vtlist.vt_lasttime += vtp->vt_delta; ch.vtlist.vt_lasttime += vtp->vt_delta;
/* The timer is removed from the list and marked as non-armed.*/
vtp->vt_next->vt_prev = (virtual_timer_t *)&ch.vtlist; vtp->vt_next->vt_prev = (virtual_timer_t *)&ch.vtlist;
ch.vtlist.vt_next = vtp->vt_next; ch.vtlist.vt_next = vtp->vt_next;
fn = vtp->vt_func; fn = vtp->vt_func;
vtp->vt_func = NULL; vtp->vt_func = NULL;
/* if the list becomes empty then the timer is stopped.*/
if (ch.vtlist.vt_next == (virtual_timer_t *)&ch.vtlist) {
port_timer_stop_alarm();
}
/* Leaving the system critical zone in order to execute the callback /* Leaving the system critical zone in order to execute the callback
and in order to give a preemption chance to higher priority and in order to give a preemption chance to higher priority
interrupts.*/ interrupts.*/
@ -507,37 +535,27 @@ static inline void chVTDoTickI(void) {
of the list.*/ of the list.*/
chSysLockFromISR(); chSysLockFromISR();
/* If the list is empty then ending the loop, the list has to be /* Next element in the list, the current time could have advanced so
re-checked because new timers could have been added/removed from recalculating the time window.*/
within the callback or other ISRs.*/
vtp = ch.vtlist.vt_next; vtp = ch.vtlist.vt_next;
if (&ch.vtlist == (virtual_timers_list_t *)vtp) { now = chVTGetSystemTimeX();
/* Timers list empty, stopping alarms.*/ }
port_timer_stop_alarm();
/* if the list is empty, nothing else to do.*/
if (ch.vtlist.vt_next == (virtual_timer_t *)&ch.vtlist) {
return; return;
} }
/* Getting the current system time and calculating the time window since /* Recalculating the next alarm time.*/
the last time has expired.*/ delta = ch.vtlist.vt_lasttime + vtp->vt_delta - now;
now = chVTGetSystemTimeX();
/* The next element is outside the current time window, the loop
is stopped here.*/
if (vtp->vt_delta > now - ch.vtlist.vt_lasttime) {
/* Updating the alarm to the next deadline, deadline that must not be
closer in time than the minimum time delta.*/
systime_t delta = ch.vtlist.vt_lasttime + vtp->vt_delta - now;
if (delta < (systime_t)CH_CFG_ST_TIMEDELTA) { if (delta < (systime_t)CH_CFG_ST_TIMEDELTA) {
delta = (systime_t)CH_CFG_ST_TIMEDELTA; delta = (systime_t)CH_CFG_ST_TIMEDELTA;
} }
port_timer_set_alarm(now + delta); port_timer_set_alarm(now + delta);
chDbgAssert((chVTGetSystemTimeX() - ch.vtlist.vt_lasttime) < delta, chDbgAssert((chVTGetSystemTimeX() - ch.vtlist.vt_lasttime) <=
(now + delta - ch.vtlist.vt_lasttime),
"exceeding delta"); "exceeding delta");
return;
}
}
#endif /* CH_CFG_ST_TIMEDELTA > 0 */ #endif /* CH_CFG_ST_TIMEDELTA > 0 */
} }

View File

@ -101,40 +101,56 @@ void chVTDoSetI(virtual_timer_t *vtp, systime_t delay,
vtp->vt_par = par; vtp->vt_par = par;
vtp->vt_func = vtfunc; vtp->vt_func = vtfunc;
p = ch.vtlist.vt_next;
#if CH_CFG_ST_TIMEDELTA > 0 #if CH_CFG_ST_TIMEDELTA > 0
{ {
systime_t now = chVTGetSystemTimeX(); systime_t now = chVTGetSystemTimeX();
/* Special case where the timers list is empty.*/
if (&ch.vtlist == (virtual_timers_list_t *)ch.vtlist.vt_next) {
/* If the requested delay is lower than the minimum safe delta then it /* If the requested delay is lower than the minimum safe delta then it
is raised to the minimum safe value.*/ is raised to the minimum safe value.*/
if (delay < (systime_t)CH_CFG_ST_TIMEDELTA) { if (delay < (systime_t)CH_CFG_ST_TIMEDELTA) {
delay = (systime_t)CH_CFG_ST_TIMEDELTA; delay = (systime_t)CH_CFG_ST_TIMEDELTA;
} }
if (&ch.vtlist == (virtual_timers_list_t *)p) {
/* The delta list is empty, the current time becomes the new /* The delta list is empty, the current time becomes the new
delta list base time.*/ delta list base time, the timer is inserted.*/
ch.vtlist.vt_lasttime = now; ch.vtlist.vt_lasttime = now;
ch.vtlist.vt_next = vtp;
ch.vtlist.vt_prev = vtp;
vtp->vt_next = (virtual_timer_t *)&ch.vtlist;
vtp->vt_prev = (virtual_timer_t *)&ch.vtlist;
vtp->vt_delta = delay;
/* Being the first element in the list the alarm timer is started.*/
port_timer_start_alarm(ch.vtlist.vt_lasttime + delay); port_timer_start_alarm(ch.vtlist.vt_lasttime + delay);
return;
} }
else {
/* Special case where the timer will be placed as first element in a
non-empty list, the alarm needs to be recalculated.*/
if ((now + delay) < (ch.vtlist.vt_lasttime + ch.vtlist.vt_next->vt_delta)) {
/* If the requested delay is lower than the minimum safe delta then it
is raised to the minimum safe value.*/
if (delay < (systime_t)CH_CFG_ST_TIMEDELTA) {
delay = (systime_t)CH_CFG_ST_TIMEDELTA;
}
/* Now the delay is calculated as delta from the last tick interrupt /* Now the delay is calculated as delta from the last tick interrupt
time.*/ time.*/
delay += now - ch.vtlist.vt_lasttime; delay += now - ch.vtlist.vt_lasttime;
/* If the specified delay is closer in time than the first element /* New alarm deadline.*/
in the delta list then it becomes the next alarm event in time.*/
if (delay < p->vt_delta) {
port_timer_set_alarm(ch.vtlist.vt_lasttime + delay); port_timer_set_alarm(ch.vtlist.vt_lasttime + delay);
} }
} }
}
#endif /* CH_CFG_ST_TIMEDELTA > 0 */ #endif /* CH_CFG_ST_TIMEDELTA > 0 */
/* The delta list is scanned in order to find the correct position for /* The delta list is scanned in order to find the correct position for
this timer. */ this timer. */
p = ch.vtlist.vt_next;
while (p->vt_delta < delay) { while (p->vt_delta < delay) {
delay -= p->vt_delta; delay -= p->vt_delta;
p = p->vt_next; p = p->vt_next;