diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2017-08-07 12:49:41 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2017-08-08 00:27:28 -0400 |
commit | f886212b441da245946242513b764d29c07dc167 (patch) | |
tree | 2c67cfcb854666b1d0a7d786ed0e2aaf6665be51 /src/avr | |
parent | 2c272f99a3fac49d8acd4b49a1aa3302225f17b8 (diff) | |
download | kutter-f886212b441da245946242513b764d29c07dc167.tar.gz kutter-f886212b441da245946242513b764d29c07dc167.tar.xz kutter-f886212b441da245946242513b764d29c07dc167.zip |
avr: Rework timer irq handler to check for tasks pending
Allow the timer dispatch irq handler to run for extended periods if
there are no tasks pending. This reduces the amount of lost cpu time
spent entering and exiting the irq handler.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/avr')
-rw-r--r-- | src/avr/timer.c | 72 |
1 files changed, 33 insertions, 39 deletions
diff --git a/src/avr/timer.c b/src/avr/timer.c index a49d9e6d..ae6c36cd 100644 --- a/src/avr/timer.c +++ b/src/avr/timer.c @@ -67,7 +67,10 @@ timer_repeat_set(uint16_t next) { // Timer1B is used to limit the number of timers run from a timer1A irq OCR1B = next; - TIFR1 = 1<<OCF1B; + // This is "TIFR1 = 1<<OCF1B" - gcc handles that poorly, so it's hand coded + uint8_t dummy; + asm volatile("ldi %0, %2\n out %1, %0" + : "=d"(dummy) : "i"(&TIFR1 - 0x20), "i"(1<<OCF1B)); } // Activate timer dispatch as soon as possible @@ -162,52 +165,43 @@ ISR(TIMER1_COMPA_vect) // Run the next software timer next = sched_timer_dispatch(); - int16_t diff = timer_get() - next; - if (likely(diff >= 0)) { - // Another timer is pending - briefly allow irqs to fire + for (;;) { + int16_t diff = timer_get() - next; + if (likely(diff >= 0)) { + // Another timer is pending - briefly allow irqs and then run it + irq_enable(); + if (unlikely(TIFR1 & (1<<OCF1B))) + goto check_defer; + irq_disable(); + break; + } + + if (likely(diff <= -TIMER_MIN_TRY_TICKS)) + // Schedule next timer normally + goto done; + irq_enable(); if (unlikely(TIFR1 & (1<<OCF1B))) - // Too many repeat timers - must exit irq handler - goto force_defer; + goto check_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; + check_defer: + // Check if there are too many repeat timers irq_disable(); - diff = timer_get() - next; - } while (diff < 0); + uint16_t now = timer_get(); + if ((int16_t)(next - now) < (int16_t)(-timer_from_us(1000))) + shutdown("Rescheduled timer in the past"); + if (sched_tasks_busy()) { + timer_repeat_set(now + TIMER_REPEAT_TICKS); + next = now + TIMER_DEFER_REPEAT_TICKS; + goto done; + } + timer_repeat_set(now + TIMER_IDLE_REPEAT_TICKS); + timer_set(now); + } } -force_defer: - // Too many repeat timers - force a pause so tasks aren't starved - irq_disable(); - uint16_t now = timer_get(); - if ((int16_t)(next - now) < (int16_t)(-timer_from_us(1000))) - shutdown("Rescheduled timer in the past"); - timer_repeat_set(now + TIMER_REPEAT_TICKS); - next = now + TIMER_DEFER_REPEAT_TICKS; - done: timer_set(next); - return; -} - -// Periodic background task that temporarily boosts priority of -// timers. This helps prioritize timers when tasks are idling. -void -timer_task(void) -{ - irq_disable(); - timer_repeat_set(timer_get() + TIMER_IDLE_REPEAT_TICKS); - irq_enable(); } -DECL_TASK(timer_task); |