avr: Introduce optimized timer_is_before()

Provide hand-coded assembler for timer_is_before() on AVR as that code
is used frequently in the time-critical timer dispatch loop and gcc
doesn't do a good job at compiling that comparison code.  Remove the
no longer needed waketime+1 hack from reschedule_timer().

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-03-24 23:17:23 -04:00
parent 60e488eb17
commit 4dfa6c6ee4
2 changed files with 22 additions and 9 deletions

View File

@ -25,13 +25,29 @@ timer_from_us(uint32_t us)
return us * (F_CPU / 1000000); return us * (F_CPU / 1000000);
} }
union u32_u {
struct { uint8_t b0, b1, b2, b3; };
struct { uint16_t lo, hi; };
uint32_t val;
};
// Return true if time1 is before time2. Always use this function to // Return true if time1 is before time2. Always use this function to
// compare times as regular C comparisons can fail if the counter // compare times as regular C comparisons can fail if the counter
// rolls over. // rolls over.
uint8_t uint8_t __always_inline
timer_is_before(uint32_t time1, uint32_t time2) timer_is_before(uint32_t time1, uint32_t time2)
{ {
return (int32_t)(time1 - time2) < 0; // This asm is equivalent to:
// return (int32_t)(time1 - time2) < 0;
// But gcc doesn't do a good job with the above, so it's hand coded.
union u32_u utime1 = { .val = time1 };
uint8_t f = utime1.b3;
asm(" cp %A1, %A2\n"
" cpc %B1, %B2\n"
" cpc %C1, %C2\n"
" sbc %0, %D2"
: "+r"(f) : "r"(time1), "r"(time2));
return (int8_t)f < 0;
} }
static inline uint16_t static inline uint16_t
@ -99,11 +115,8 @@ static uint16_t timer_high;
uint32_t uint32_t
timer_read_time(void) timer_read_time(void)
{ {
union u32_u16_u {
struct { uint16_t lo, hi; };
uint32_t val;
} calc;
irqstatus_t flag = irq_save(); irqstatus_t flag = irq_save();
union u32_u calc;
calc.val = timer_get(); calc.val = timer_get();
calc.hi = timer_high; calc.hi = timer_high;
if (!(TIFR1 & (1<<TOV1))) { if (!(TIFR1 & (1<<TOV1))) {

View File

@ -127,9 +127,9 @@ sched_del_timer(struct timer *del)
static struct timer * static struct timer *
reschedule_timer(struct timer *t) reschedule_timer(struct timer *t)
{ {
uint32_t waketime = t->waketime;
struct timer *pos = t->next; struct timer *pos = t->next;
uint32_t minwaketime = t->waketime + 1; if (timer_is_before(waketime, pos->waketime))
if (!timer_is_before(pos->waketime, minwaketime))
// Timer is still the first - no insertion needed // Timer is still the first - no insertion needed
return t; return t;
@ -142,7 +142,7 @@ reschedule_timer(struct timer *t)
// micro optimization for AVR - reduces register pressure // micro optimization for AVR - reduces register pressure
asm("" : "+r"(prev) : : "memory"); asm("" : "+r"(prev) : : "memory");
pos = pos->next; pos = pos->next;
if (!timer_is_before(pos->waketime, minwaketime)) if (timer_is_before(waketime, pos->waketime))
break; break;
} }
t->next = pos; t->next = pos;