[efr32] fallback to use relative timer if absolute timer fails to start (#4081)

Added a fallback to the efr32mg12 where if an alarm set with an
absolute expiry time (t0 + dt) has failed to start then we fallback to
using a relative timer (dt).

It has been observed in testing that alarms have stopped working on
the efr32mg12. This has caused other issues such as buffers not being
freed or the network splitting as no further alarms are scheduled.

The cause is due to an alarm being set to expire slightly in the
past. In this case we fallback to using a relative timer using only
the dt value. For the cases where it fails it will be set to 1ms into
the future from the time the RAIL_SetTimer is called.

RAIL maintains time in microseconds but is being rounded down for the
time returned in milliseconds by otPlatAlarmMilliGetNow. We used a 1ms
minimum delta to ensure that the absolute time would be in the next
millisecond but this could still fail. We therefore use a relative
timer in this case which on the cases where it fails should be 1ms
ahead of current time.
This commit is contained in:
Marven Gilhespie
2019-08-14 20:32:50 +01:00
committed by Jonathan Hui
parent 0dde90edcb
commit 89dca58915
+40 -23
View File
@@ -137,15 +137,23 @@ void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t t0, uint32_t dt)
expires_microsec = (t0 + dt) * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, expires_microsec, RAIL_TIME_ABSOLUTE, RAILCb_TimerExpired);
if (status == RAIL_STATUS_NO_ERROR)
if (status != RAIL_STATUS_NO_ERROR)
{
sIsRunning = true;
}
else
{
sIsRunning = false;
otLogCritPlat("Alarm set timer, status: %d, dt: %u, t0: %u", status, dt, t0);
// The RAIL timer could not be set due to expiration time being in the past with respect to RAIL's current
// time which is in microseconds. We fallback to using a relative timer from the current time.
expires_microsec = dt * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, expires_microsec, RAIL_TIME_DELAY, RAILCb_TimerExpired);
if (status != RAIL_STATUS_NO_ERROR)
{
otLogCritPlat("Alarm start timer failed, status: %d, dt: %u, t0: %u, now: %u", status, dt, t0,
otPlatAlarmMilliGetNow());
assert(false);
}
}
sIsRunning = true;
}
void otPlatAlarmMilliStop(otInstance *aInstance)
@@ -175,6 +183,9 @@ void efr32AlarmProcess(otInstance *aInstance)
if (sAlarmDt > RAIL_TIMER_MAX_DELTA_MS)
{
// We split longer delays in two due to the maximum allowed timer in RAIL. Here we
// re-arm the RAIL timer with the remaining part of the alarm.
now = otPlatAlarmMilliGetNow();
dt = (sAlarmT0 + sAlarmDt) - now;
@@ -190,27 +201,33 @@ void efr32AlarmProcess(otInstance *aInstance)
new_expires_microsec = (now + dt) * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, new_expires_microsec, RAIL_TIME_ABSOLUTE, RAILCb_TimerExpired);
if (status == RAIL_STATUS_NO_ERROR)
if (status != RAIL_STATUS_NO_ERROR)
{
sIsRunning = true;
}
else
{
sIsRunning = false;
otLogCritPlat("Alarm set timer, status: %d, expires: %u, dt: %u, now: %u", status, sAlarmT0 + sAlarmDt,
dt, now);
}
}
new_expires_microsec = dt * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, new_expires_microsec, RAIL_TIME_DELAY, RAILCb_TimerExpired);
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
otPlatDiagAlarmFired(aInstance);
if (status != RAIL_STATUS_NO_ERROR)
{
otLogCritPlat("Alarm extend timer failed, status: %d, dt: %u, now: %u", status, dt,
otPlatAlarmMilliGetNow());
assert(false);
}
}
sIsRunning = true;
}
else
#endif
{
otPlatAlarmMilliFired(aInstance);
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
otPlatDiagAlarmFired(aInstance);
}
else
#endif
{
otPlatAlarmMilliFired(aInstance);
}
}
}
exit: