* @author Cory Sharp <cssharp@eecs.berkeley.edu>
*/
-generic module VirtualizeTimerC(typedef precision_tag, int max_timers)
+generic module VirtualizeTimerC(typedef precision_tag, int max_timers) @safe()
{
provides interface Timer<precision_tag> as Timer[uint8_t num];
uses interface Timer<precision_tag> as TimerFrom;
implementation
{
enum
- {
- NUM_TIMERS = max_timers,
- END_OF_LIST = 255,
- };
+ {
+ NUM_TIMERS = max_timers,
+ END_OF_LIST = 255,
+ };
typedef struct
{
} Timer_t;
Timer_t m_timers[NUM_TIMERS];
+ bool m_timers_changed;
- enum {
- /* bitmask */
- S_TIMER_RUNNING = 1,
- S_TIMER_CHANGED = 2
- };
- bool state;
+ task void updateFromTimer();
- task void executeTimersNow();
-
- void executeTimers(uint32_t then)
+ void fireTimers(uint32_t now)
{
- int32_t min_remaining = (1UL<<31)-1; //max signed int32_t
- bool min_remaining_isset = FALSE;
- int num;
-
- /* We always come here with the timer stopped */
- state = 0;
-
- for(num=0; num<NUM_TIMERS; num++)
- {
- Timer_t* timer = &m_timers[num];
+ uint8_t num;
- if (timer->isrunning)
+ for (num=0; num<NUM_TIMERS; num++)
{
- // Calculate "remaining" before the timer is fired. If a timer
- // restarts itself in a fired event, then we 1) need a consistent
- // "remaining" value to work with, and no worries because 2) all
- // start commands post executeTimersNow, so the timer will be
- // recomputed later, anyway.
-
- uint32_t elapsed = then - timer->t0;
- int32_t remaining = timer->dt - elapsed;
-
- if (remaining <= 0)
- {
- if (timer->isoneshot)
- {
- timer->isrunning = FALSE;
- }
- else
+ Timer_t* timer = &m_timers[num];
+
+ if (timer->isrunning)
{
- // The remaining time is non-positive (the timer had fired).
- // So add dt to convert it to remaining for the next event.
- timer->t0 += timer->dt;
- remaining += timer->dt;
+ uint32_t elapsed = now - timer->t0;
+
+ if (elapsed >= timer->dt)
+ {
+ if (timer->isoneshot)
+ timer->isrunning = FALSE;
+ else // Update timer for next event
+ timer->t0 += timer->dt;
+
+ signal Timer.fired[num]();
+ break;
+ }
}
+ }
+ post updateFromTimer();
+ }
+
+ task void updateFromTimer()
+ {
+ /* This code supports a maximum dt of MAXINT. If min_remaining and
+ remaining were switched to uint32_t, and the logic changed a
+ little, dt's up to 2^32-1 should work (but at a slightly higher
+ runtime cost). */
+ uint32_t now = call TimerFrom.getNow();
+ int32_t min_remaining = (1UL << 31) - 1; /* max int32_t */
+ bool min_remaining_isset = FALSE;
+ uint8_t num;
+
+ call TimerFrom.stop();
- signal Timer.fired[num]();
- }
+ for (num=0; num<NUM_TIMERS; num++)
+ {
+ Timer_t* timer = &m_timers[num];
- // check isrunning in case the timer was stopped in the fired
- // event or this was a one shot timer; note that a one shot
- // timer that was restarted in its fired event will push us
- // through here with remaining <= 0, but we're already scheduled
- // an executeTimersTask in that case (and suppressed setting
- // TimerFrom)
if (timer->isrunning)
- {
- if (remaining < min_remaining)
- min_remaining = remaining;
- min_remaining_isset = TRUE;
- }
+ {
+ uint32_t elapsed = now - timer->t0;
+ int32_t remaining = timer->dt - elapsed;
+
+ if (remaining < min_remaining)
+ {
+ min_remaining = remaining;
+ min_remaining_isset = TRUE;
+ }
+ }
}
- }
- if (!(state & S_TIMER_CHANGED) && min_remaining_isset)
- {
- if (min_remaining <= 0)
- post executeTimersNow();
- else
- {
- call TimerFrom.startOneShotAt(then, min_remaining);
- state |= S_TIMER_RUNNING;
- }
- }
+ if (min_remaining_isset)
+ {
+ if (min_remaining <= 0)
+ fireTimers(now);
+ else
+ call TimerFrom.startOneShotAt(now, min_remaining);
+ }
}
-
event void TimerFrom.fired()
{
- executeTimers(call TimerFrom.gett0() + call TimerFrom.getdt());
- }
-
- task void executeTimersNow()
- {
- /* If we set a timer, try and stop it. But if it believes it's
- not running, that means that the execution of its fired event
- is pending, so we:
- - don't need to call executeTimers (it will happen soon)
- - we must not call executeTimers, because that would change the
- timer's setting, confusing the pending call to TimerFrom.fired
- (it would call executeTimers with an incorrect "then"
- parameter)
- We need to use an atomic section even though it's a "timer",
- because it's based on an underlying alarm, whose transitions
- are asynchronous (yuck?)
- */
- if (state & S_TIMER_RUNNING)
- atomic
- {
- if (!call TimerFrom.isRunning())
- return;
- call TimerFrom.stop();
- }
- executeTimers(call TimerFrom.getNow());
+ fireTimers(call TimerFrom.getNow());
}
void startTimer(uint8_t num, uint32_t t0, uint32_t dt, bool isoneshot)
timer->dt = dt;
timer->isoneshot = isoneshot;
timer->isrunning = TRUE;
- state |= S_TIMER_CHANGED;
- post executeTimersNow();
+ post updateFromTimer();
}
command void Timer.startPeriodic[uint8_t num](uint32_t dt)