Virtual timers tolerance to insufficient delta, experimental.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@14727 27425a3e-05d8-49a3-a47f-9c15f0e5edd8
This commit is contained in:
Giovanni Di Sirio 2021-09-02 08:12:16 +00:00
parent 0980d3af53
commit 3407139cf6
3 changed files with 108 additions and 71 deletions

View File

@ -49,6 +49,91 @@
/*===========================================================================*/ /*===========================================================================*/
#if (CH_CFG_ST_TIMEDELTA > 0) || defined(__DOXYGEN__) #if (CH_CFG_ST_TIMEDELTA > 0) || defined(__DOXYGEN__)
#if 1
static systime_t vt_set_alarm(systime_t basetime, sysinterval_t delta) {
sysinterval_t mindelta;
systime_t next_alarm;
/* Initial delta is what is configured statically.*/
mindelta = (sysinterval_t)CH_CFG_ST_TIMEDELTA;
if (delta < mindelta) {
/* We need to avoid that the system time goes past the alarm we are
going to set before the alarm is actually set.*/
delta = mindelta;
}
#if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
else if (delta > (sysinterval_t)TIME_MAX_SYSTIME) {
/* The delta could be too large for the physical timer to handle
this can happen when: sizeof (systime_t) < sizeof (sysinterval_t).*/
delta = (sysinterval_t)TIME_MAX_SYSTIME;
}
#endif
while (true) {
sysinterval_t nowdelta;
systime_t now;
/* Absolute time for next alarm.*/
next_alarm = chTimeAddX(basetime, delta);
/* Setting up the alarm on the next deadline.*/
port_timer_set_alarm(next_alarm);
/* Check on current time, we need to detect the error condition where
current time skipped past the calculated deadline.*/
now = chVTGetSystemTimeX();
nowdelta = chTimeDiffX(basetime, now);
if (nowdelta < delta) {
break;
}
/* Trying again with a more relaxed minimum delta.*/
mindelta += (sysinterval_t)1;
/* Current time becomes the new "base" time.*/
basetime = now;
delta = mindelta;
#if !defined(CH_VT_RFCU_DISABLED)
chRFCUCollectFaultsI(CH_RFCU_VT_INSUFFICIENT_DELTA);
#endif
}
return next_alarm;
}
#else
static systime_t vt_set_alarm(systime_t basetime, sysinterval_t delta) {
sysinterval_t mindelta;
systime_t next_alarm;
/* Initial delta is what is configured statically.*/
mindelta = (sysinterval_t)CH_CFG_ST_TIMEDELTA;
if (delta < mindelta) {
/* We need to avoid that the system time goes past the alarm we are
going to set before the alarm is actually set.*/
delta = mindelta;
}
#if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
else if (delta > (sysinterval_t)TIME_MAX_SYSTIME) {
/* The delta could be too large for the physical timer to handle
this can happen when: sizeof (systime_t) < sizeof (sysinterval_t).*/
delta = (sysinterval_t)TIME_MAX_SYSTIME;
}
#endif
/* Absolute time for next alarm.*/
next_alarm = chTimeAddX(basetime, delta);
/* Setting up the alarm on the next deadline.*/
port_timer_set_alarm(next_alarm);
return next_alarm;
}
#endif
/** /**
* @brief Inserts a timer as first element in a delta list. * @brief Inserts a timer as first element in a delta list.
* @note This is the special case when the delta list is initially empty. * @note This is the special case when the delta list is initially empty.
@ -120,24 +205,8 @@ static void vt_enqueue(virtual_timers_list_t *vtlp,
/* Checking if this timer would become the first in the delta list, this /* Checking if this timer would become the first in the delta list, this
requires changing the current alarm setting.*/ requires changing the current alarm setting.*/
if (delta < vtlp->dlist.next->delta) { if (delta < vtlp->dlist.next->delta) {
sysinterval_t deadline_delta;
/* A small delay that will become the first element in the delta list (void) vt_set_alarm(vtlp->lasttime, delta);
and next deadline.*/
deadline_delta = delta;
/* Limit delta to CH_CFG_ST_TIMEDELTA.*/
if (deadline_delta < (sysinterval_t)CH_CFG_ST_TIMEDELTA) {
deadline_delta = (sysinterval_t)CH_CFG_ST_TIMEDELTA;
}
#if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
/* The delta could be too large for the physical timer to handle
this can happen when: sizeof (systime_t) < sizeof (sysinterval_t).*/
else if (deadline_delta > (sysinterval_t)TIME_MAX_SYSTIME) {
deadline_delta = (sysinterval_t)TIME_MAX_SYSTIME;
}
#endif
port_timer_set_alarm(chTimeAddX(vtlp->lasttime, deadline_delta));
} }
} }
#else /* CH_CFG_ST_TIMEDELTA == 0 */ #else /* CH_CFG_ST_TIMEDELTA == 0 */
@ -258,6 +327,7 @@ void chVTDoResetI(virtual_timer_t *vtp) {
is the last of the list, restoring it.*/ is the last of the list, restoring it.*/
vtlp->dlist.delta = (sysinterval_t)-1; vtlp->dlist.delta = (sysinterval_t)-1;
#else /* CH_CFG_ST_TIMEDELTA > 0 */ #else /* CH_CFG_ST_TIMEDELTA > 0 */
systime_t now;
sysinterval_t nowdelta, delta; sysinterval_t nowdelta, delta;
/* If the timer is not the first of the list then it is simply unlinked /* If the timer is not the first of the list then it is simply unlinked
@ -296,7 +366,8 @@ void chVTDoResetI(virtual_timer_t *vtp) {
vtlp->dlist.next->delta += vtp->dlist.delta; vtlp->dlist.next->delta += vtp->dlist.delta;
/* Distance in ticks between the last alarm event and current time.*/ /* Distance in ticks between the last alarm event and current time.*/
nowdelta = chTimeDiffX(vtlp->lasttime, chVTGetSystemTimeX()); now = chVTGetSystemTimeX();
nowdelta = chTimeDiffX(vtlp->lasttime, now);
/* If the current time surpassed the time of the next element in list /* If the current time surpassed the time of the next element in list
then the event interrupt is already pending, just return.*/ then the event interrupt is already pending, just return.*/
@ -307,21 +378,8 @@ void chVTDoResetI(virtual_timer_t *vtp) {
/* Distance from the next scheduled event and now.*/ /* Distance from the next scheduled event and now.*/
delta = vtlp->dlist.next->delta - nowdelta; delta = vtlp->dlist.next->delta - nowdelta;
/* Making sure to not schedule an event closer than CH_CFG_ST_TIMEDELTA /* Setting up the alarm.*/
ticks from now.*/ (void) vt_set_alarm(now, delta);
if (delta < (sysinterval_t)CH_CFG_ST_TIMEDELTA) {
delta = nowdelta + (sysinterval_t)CH_CFG_ST_TIMEDELTA;
}
else {
delta = nowdelta + delta;
#if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
/* The delta could be too large for the physical timer to handle.*/
if (delta > (sysinterval_t)TIME_MAX_SYSTIME) {
delta = (sysinterval_t)TIME_MAX_SYSTIME;
}
#endif
}
port_timer_set_alarm(chTimeAddX(vtlp->lasttime, delta));
#endif /* CH_CFG_ST_TIMEDELTA > 0 */ #endif /* CH_CFG_ST_TIMEDELTA > 0 */
} }
@ -407,7 +465,7 @@ void chVTDoTickI(void) {
#else /* CH_CFG_ST_TIMEDELTA > 0 */ #else /* CH_CFG_ST_TIMEDELTA > 0 */
virtual_timer_t *vtp; virtual_timer_t *vtp;
sysinterval_t delta, nowdelta; sysinterval_t delta, nowdelta;
systime_t now; systime_t now, next_alarm;
/* Looping through timers consuming all timers with deltas lower or equal /* Looping through timers consuming all timers with deltas lower or equal
than the interval between "now" and "lasttime".*/ than the interval between "now" and "lasttime".*/
@ -510,36 +568,12 @@ void chVTDoTickI(void) {
/* Calculating the delta to the next alarm time.*/ /* Calculating the delta to the next alarm time.*/
delta = vtp->dlist.delta - nowdelta; delta = vtp->dlist.delta - nowdelta;
/* Limit delta to CH_CFG_ST_TIMEDELTA.*/
if (delta < (sysinterval_t)CH_CFG_ST_TIMEDELTA) {
delta = (sysinterval_t)CH_CFG_ST_TIMEDELTA;
}
#if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
/* The delta could be too large for the physical timer to handle.*/
else if (delta > (sysinterval_t)TIME_MAX_SYSTIME) {
delta = (sysinterval_t)TIME_MAX_SYSTIME;
}
#endif
/* Update alarm time to next timer.*/ /* Update alarm time to next timer.*/
{ next_alarm = vt_set_alarm(now, delta);
sysinterval_t next_alarm = chTimeAddX(now, delta);
port_timer_set_alarm(next_alarm); chDbgAssert(chTimeDiffX(vtlp->lasttime, chVTGetSystemTimeX()) <=
chTimeDiffX(vtlp->lasttime, next_alarm),
#if !defined(CH_VT_RFCU_DISABLED) "insufficient delta");
if (chTimeDiffX(vtlp->lasttime, chVTGetSystemTimeX()) >
chTimeDiffX(vtlp->lasttime, next_alarm)) {
chDbgAssert(false, "insufficient delta");
chRFCUCollectFaultsI(CH_RFCU_VT_INSUFFICIENT_DELTA);
}
#else
chDbgAssert(chTimeDiffX(vtlp->lasttime, chVTGetSystemTimeX()) <=
chTimeDiffX(vtlp->lasttime, next_alarm),
"insufficient delta");
#endif
}
#endif /* CH_CFG_ST_TIMEDELTA > 0 */ #endif /* CH_CFG_ST_TIMEDELTA > 0 */
} }

View File

@ -5,7 +5,7 @@
# Compiler options here. # Compiler options here.
ifeq ($(USE_OPT),) ifeq ($(USE_OPT),)
USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 USE_OPT = -Og -ggdb -fomit-frame-pointer -falign-functions=16
endif endif
# C specific options here (added to USE_OPT). # C specific options here (added to USE_OPT).

View File

@ -277,6 +277,7 @@ void vt_storm_execute(const vt_storm_config_t *cfg) {
#endif #endif
for (i = 1; i <= VT_STORM_CFG_ITERATIONS; i++) { for (i = 1; i <= VT_STORM_CFG_ITERATIONS; i++) {
bool dw;
chprintf(cfg->out, "Iteration %d\r\n", i); chprintf(cfg->out, "Iteration %d\r\n", i);
chThdSleepS(TIME_MS2I(10)); chThdSleepS(TIME_MS2I(10));
@ -305,7 +306,7 @@ void vt_storm_execute(const vt_storm_config_t *cfg) {
chVTSetI(&guard2, TIME_MS2I(250) + (CH_CFG_TIME_QUANTUM + 1), guard_cb, NULL); chVTSetI(&guard2, TIME_MS2I(250) + (CH_CFG_TIME_QUANTUM + 1), guard_cb, NULL);
chVTSetI(&guard3, TIME_MS2I(250) + (CH_CFG_TIME_QUANTUM * 2), guard_cb, NULL); chVTSetI(&guard3, TIME_MS2I(250) + (CH_CFG_TIME_QUANTUM * 2), guard_cb, NULL);
/* Letting them run for half second.*/ /* Letting them run for a while.*/
chThdSleepS(TIME_MS2I(100)); chThdSleepS(TIME_MS2I(100));
/* Stopping everything.*/ /* Stopping everything.*/
@ -323,21 +324,23 @@ void vt_storm_execute(const vt_storm_config_t *cfg) {
chVTResetI(&guard3); chVTResetI(&guard3);
/* Check for relevant RFCU events.*/ /* Check for relevant RFCU events.*/
delta_warning = chRFCUGetAndClearFaultsI(CH_RFCU_VT_INSUFFICIENT_DELTA | dw = chRFCUGetAndClearFaultsI(CH_RFCU_VT_INSUFFICIENT_DELTA |
CH_RFCU_VT_SKIPPED_DEADLINE) != (rfcu_mask_t)0; CH_RFCU_VT_SKIPPED_DEADLINE) != (rfcu_mask_t)0;
chSysUnlock(); chSysUnlock();
if (saturated) { if (saturated) {
chprintf(cfg->out, "#"); chprintf(cfg->out, "#");
break; break;
} }
else if (delta_warning) { else if (dw) {
palToggleLine(config->line);
chprintf(cfg->out, "+"); chprintf(cfg->out, "+");
break; delta_warning = true;
}
else {
palToggleLine(config->line);
chprintf(cfg->out, ".");
} }
palToggleLine(config->line);
chprintf(cfg->out, ".");
// if (delay >= TIME_US2I(1)) { // if (delay >= TIME_US2I(1)) {
// delay -= TIME_US2I(1); // delay -= TIME_US2I(1);
// } // }