timer: Avoid infinite cycle timer on TCNT write
authorJakob Gruber <jakob.gruber@gmail.com>
Thu, 23 Aug 2012 08:57:21 +0000 (10:57 +0200)
committerJakob Gruber <jakob.gruber@gmail.com>
Wed, 29 Aug 2012 12:53:18 +0000 (14:53 +0200)
In some situations, it was possible to enter an infinite cycle timer
loop. In avr_timer_tcnt_write, the tov cycle timer registration is not
protected and can register a timer with p->tov_cycles == 0.

The following atmega1280 program demonstrates this issue:

int main() {

TCCR1A = 0;
TCCR1B = (1<<WGM12) | (1<<ICES1);
OCR1B = OCR1A = 960;

/* Start */
TCNT1 = 0;
TCCR1B |= (1<<CS11);

/* Stop */
TCCR1B &= ~(1<<CS11);
TIFR1 |= (1<<OCF1A) | (1<<OCF1B);

/* Start */
TCNT1 = 0; /**< Registers timer with tov_cycles == 0. */
TCCR1B |= (1<<CS11);

while (1) ;
return 0;
}

simavr/sim/avr_timer.c

index aeda219..6e1d8cc 100644 (file)
@@ -173,9 +173,11 @@ static void avr_timer_tcnt_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t
 //     printf("%s-%c %d/%d -- cycles %d/%d\n", __FUNCTION__, p->name, tcnt, p->tov_top, (uint32_t)cycles, (uint32_t)p->tov_cycles);
 
        // this reset the timers bases to the new base
-       p->tov_base = 0;
-       avr_cycle_timer_register(avr, p->tov_cycles - cycles, avr_timer_tov, p);
-       avr_timer_tov(avr, avr->cycle - cycles, p);
+       if (p->tov_cycles > 1) {
+               avr_cycle_timer_register(avr, p->tov_cycles - cycles, avr_timer_tov, p);
+               p->tov_base = 0;
+               avr_timer_tov(avr, avr->cycle - cycles, p);
+       }
 
 //     tcnt = ((avr->cycle - p->tov_base) * p->tov_top) / p->tov_cycles;
 //     printf("%s-%c new tnt derive to %d\n", __FUNCTION__, p->name, tcnt);