Merge branch 'fix-irq-handling' of https://github.com/vintagepc/simavr
authorga <ga@oldell.fish>
Mon, 22 Mar 2021 18:27:24 +0000 (18:27 +0000)
committerga <ga@oldell.fish>
Fri, 2 Apr 2021 18:20:09 +0000 (19:20 +0100)
Upstream pull request #415: Fix TOV/ICR handling, SEI delay
with corrections to timer code and addition of minimal test code.

simavr/sim/avr_timer.c
simavr/sim/sim_core.h
tests/atmega48_enabled_timer.c

index f85b884..5d52c5e 100644 (file)
@@ -716,23 +716,25 @@ avr_timer_write_pending(
                void * param)
 {
        avr_timer_t * p = (avr_timer_t *)param;
-       // save old bits values
-       uint8_t ov = avr_regbit_get(avr, p->overflow.raised);
-       uint8_t ic = avr_regbit_get(avr, p->icr.raised);
-       uint8_t cp[AVR_TIMER_COMP_COUNT];
 
-       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++)
-               cp[compi] = avr_regbit_get(avr, p->comp[compi].interrupt.raised);
+       // All bits in this register are assumed to be write-1-to-clear.
 
-       // write the value
-       // avr_core_watch_write(avr, addr, v); // This raises flags instead of clearing it.
-
-       // clear any interrupts & flags
-       avr_clear_interrupt_if(avr, &p->overflow, ov);
-       avr_clear_interrupt_if(avr, &p->icr, ic);
+       if (addr == p->overflow.raised.reg &&
+           avr_regbit_from_value(avr, p->overflow.raised, v)) {
+               avr_clear_interrupt(avr, &p->overflow);
+       }
+       if (addr == p->icr.raised.reg &&
+           avr_regbit_from_value(avr, p->icr.raised, v)) {
+               avr_clear_interrupt(avr, &p->icr);
+       }
 
-       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++)
-               avr_clear_interrupt_if(avr, &p->comp[compi].interrupt, cp[compi]);
+       for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) {
+               if (addr == p->comp[compi].interrupt.raised.reg &&
+                   avr_regbit_from_value(avr, p->comp[compi].interrupt.raised,
+                                         v)) {
+                       avr_clear_interrupt(avr, &p->comp[compi].interrupt);
+               }
+       }
 }
 
 static void
index 403c119..992c068 100644 (file)
@@ -113,7 +113,7 @@ static inline void avr_sreg_set(avr_t * avr, uint8_t flag, uint8_t ival)
        if (flag == S_I) {
                if (ival) {
                        if (!avr->sreg[S_I])
-                               avr->interrupt_state = -2;
+                               avr->interrupt_state = -1;
                } else
                        avr->interrupt_state = 0;
        }
index 04b8db4..527e44a 100644 (file)
 #include "avr_mcu_section.h"
 AVR_MCU(F_CPU, "atmega48");
 
+volatile uint8_t count;
+
 ISR(TIMER0_COMPA_vect)
 {
+    ++count;
 }
 
 int main(void)
@@ -27,13 +30,30 @@ int main(void)
 
        TCCR0B |= (1 << CS00) | (1 << CS01);        // Start timer: clk/64
 
+       while ((TIFR0 & (1 << OCF0A)) == 0)
+               ;
+
+       // Now interrupt is pending.  Try and clear it.
+
+       TIFR0 = 0;
+       if (TIFR0 & (1 << OCF0A))
+               ++count;                            // Should not clear
+       TIFR0 = (1 << OCF0A);
+       if ((TIFR0 & (1 << OCF0A)) == 0)
+               ++count;                            // Should clear!
+
        sei();                                      // Enable global interrupts
 
-       // here the interupts are enabled, but the interupt
-       // vector should not be called
-       sleep_mode();
+       // Let it run to next interrupt.
 
-       // this should not be reached
-       cli();
        sleep_mode();
+       TIMSK0 = 0;                                 // Disable CTC interrupt
+
+       if (count == 3)                             // Expected
+               cli();
+
+       // Time out if interrupting or count wrong.
+
+       for (;;)
+               sleep_mode();
 }