avr: Integrate timer_try_set_next() into the irq handler

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-03-28 10:53:19 -04:00
parent 6d05dd07f5
commit 65be6d5146
1 changed files with 31 additions and 44 deletions

View File

@ -135,40 +135,40 @@ timer_periodic(void)
#define TIMER_MIN_TRY_TICKS 60 // 40 ticks to exit irq; 20 ticks of progress #define TIMER_MIN_TRY_TICKS 60 // 40 ticks to exit irq; 20 ticks of progress
#define TIMER_DEFER_REPEAT_TICKS 200 #define TIMER_DEFER_REPEAT_TICKS 200
// Set the next timer wake time (in absolute clock ticks) or return 1 // Hardware timer IRQ handler - dispatch software timers
// if the next timer is too close to schedule. Caller must disable ISR(TIMER1_COMPA_vect)
// irqs.
static uint8_t
timer_try_set_next(unsigned int target)
{ {
uint16_t next = target; uint16_t next;
int16_t diff = next - timer_get();
if (likely(diff < 0)) {
// Another timer is pending - briefly allow irqs to fire
irq_enable();
if (unlikely(TIFR1 & (1<<OCF1B)))
// Too many repeat timers - must exit irq handler
goto force_pause;
irq_disable();
return 0;
}
if (likely(diff > TIMER_MIN_TRY_TICKS))
// Schedule next timer normally
goto done;
// Next timer in very near future - wait for it to be ready
for (;;) { for (;;) {
irq_enable(); // Run the next software timer
if (unlikely(TIFR1 & (1<<OCF1B))) next = sched_timer_dispatch();
break;
irq_disable(); int16_t diff = next - timer_get();
diff = next - timer_get(); if (likely(diff < 0)) {
if (diff < 0) // Another timer is pending - briefly allow irqs to fire
return 0; irq_enable();
if (unlikely(TIFR1 & (1<<OCF1B)))
// Too many repeat timers - must exit irq handler
goto force_defer;
irq_disable();
continue;
}
if (likely(diff > TIMER_MIN_TRY_TICKS))
// Schedule next timer normally
goto done;
// Next timer in very near future - wait for it to be ready
do {
irq_enable();
if (unlikely(TIFR1 & (1<<OCF1B)))
goto force_defer;
irq_disable();
diff = next - timer_get();
} while (diff >= 0);
} }
force_pause: force_defer:
// Too many repeat timers - force a pause so tasks aren't starved // Too many repeat timers - force a pause so tasks aren't starved
irq_disable(); irq_disable();
uint16_t now = timer_get(); uint16_t now = timer_get();
@ -179,24 +179,11 @@ force_pause:
done: done:
timer_set(next); timer_set(next);
return 1; return;
fail: fail:
shutdown("Rescheduled timer in the past"); shutdown("Rescheduled timer in the past");
} }
// Harware OCR1A interrupt handler
ISR(TIMER1_COMPA_vect)
{
for (;;) {
uint16_t next_waketime = sched_timer_dispatch();
// Schedule next timer event (or run next timer if it's ready)
uint8_t res = timer_try_set_next(next_waketime);
if (res)
break;
}
}
// Periodic background task that temporarily boosts priority of // Periodic background task that temporarily boosts priority of
// timers. This helps prioritize timers when tasks are idling. // timers. This helps prioritize timers when tasks are idling.
static void static void