From 4963a90268fb4b0fa8953cc60042784be51b7ea5 Mon Sep 17 00:00:00 2001 From: ga Date: Mon, 22 Mar 2021 18:27:24 +0000 Subject: [PATCH] Merge branch 'fix-irq-handling' of https://github.com/vintagepc/simavr 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 | 30 ++++++++++++++++-------------- simavr/sim/sim_core.h | 2 +- tests/atmega48_enabled_timer.c | 30 +++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/simavr/sim/avr_timer.c b/simavr/sim/avr_timer.c index f85b884..5d52c5e 100644 --- a/simavr/sim/avr_timer.c +++ b/simavr/sim/avr_timer.c @@ -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 diff --git a/simavr/sim/sim_core.h b/simavr/sim/sim_core.h index 403c119..992c068 100644 --- a/simavr/sim/sim_core.h +++ b/simavr/sim/sim_core.h @@ -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; } diff --git a/tests/atmega48_enabled_timer.c b/tests/atmega48_enabled_timer.c index 04b8db4..527e44a 100644 --- a/tests/atmega48_enabled_timer.c +++ b/tests/atmega48_enabled_timer.c @@ -14,8 +14,11 @@ #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(); } -- 2.39.5