From: ga Date: Mon, 11 Apr 2022 10:06:35 +0000 (+0100) Subject: Cherry-pick 8fa3b206d917609ae696e4e9d89cfbec211113ff onto upstream. X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=38ac00f21dc17f175ddde4266ca2a21cb506b1c5;p=sx%2Fsimavr.git Cherry-pick 8fa3b206d917609ae696e4e9d89cfbec211113ff onto upstream. Make the global interrupt pending IRQ behave consistently, so that the value is always a vector number. Make the floating flag more useful; when set it indicates the next interrupt expected. Remove a call that added no information. Add a test. --- diff --git a/simavr/sim/sim_interrupts.c b/simavr/sim/sim_interrupts.c index f96466d..9e3ed9c 100644 --- a/simavr/sim/sim_interrupts.c +++ b/simavr/sim/sim_interrupts.c @@ -135,7 +135,7 @@ avr_raise_interrupt( } avr_raise_irq(vector->irq + AVR_INT_IRQ_PENDING, 1); - avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, 1); + avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, vector->vector); // If the interrupt is enabled, attempt to wake the core if (avr_regbit_get(avr, vector->enable)) { @@ -175,7 +175,7 @@ avr_clear_interrupt( avr_has_pending_interrupts(avr) ? avr_int_pending_read_at( &avr->interrupts.pending, 0)->vector : 0, - !avr_has_pending_interrupts(avr)); + avr_has_pending_interrupts(avr)); if (vector->raised.reg && !vector->raise_sticky) avr_regbit_clear(avr, vector->raised); @@ -211,7 +211,7 @@ avr_get_interrupt_irq( return NULL; } -/* this is called uppon RETI. */ +/* this is called upon RETI. */ void avr_interrupt_reti( struct avr_t * avr) @@ -224,8 +224,6 @@ avr_interrupt_reti( avr_raise_irq(table->irq + AVR_INT_IRQ_RUNNING, table->running_ptr > 0 ? table->running[table->running_ptr-1]->vector : 0); - avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, - avr_has_pending_interrupts(avr)); } /* @@ -272,8 +270,6 @@ avr_service_interrupts( // destination for the swap. table->pending.buffer[(table->pending.read + mini - 1) % avr_int_pending_fifo_size] = fifo_front; } - avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, - avr_has_pending_interrupts(avr)); // if that single interrupt is masked, ignore it and continue // could also have been disabled, or cleared diff --git a/tests/attiny44_interrupt_irq_test.c b/tests/attiny44_interrupt_irq_test.c new file mode 100644 index 0000000..cda0195 --- /dev/null +++ b/tests/attiny44_interrupt_irq_test.c @@ -0,0 +1,105 @@ +/* + attiny44_interrupt_irq_test.c + + Copyright 2022 Giles Atkinson + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include "avr_mcu_section.h" + +AVR_MCU(F_CPU, "attiny44"); +AVR_MCU_VOLTAGES(5000, 5000, 3000) // VCC, AVCC, VREF - millivolts. + +static volatile int nest; + +ISR(INT0_vect) +{ + GPIOR1 = 1; // Signal to test harness + if (nest) + sei(); +} + +ISR(PCINT1_vect) +{ + GPIOR1 = 2; + if (nest) + sei(); +} + +ISR(ADC_vect) +{ + GPIOR1 = 3; + if (nest) + sei(); +} + +void go(void) +{ + + /* Turn on the ADC. */ + + ADCSRA = _BV(ADEN) + _BV(ADSC) + _BV(ADIE); // Enable, start, clk scale = 2 + + /* Wait for ADC. */ + + while ((ADCSRA & (1 << ADIF)) == 0) + ; + + sei(); // Three interrupts. + + /* Wait for ADC interrupt. */ + + while (ADCSRA & _BV(ADIF)) + ; + cli(); +} + +int main(void) +{ + /* Cause "external" and pin-change interrupts. */ + + GIMSK = _BV(INT0) + _BV(PCIE1); // Enable INTO and PORTB pin change. + MCUCR = 1; // Interrupt on either edge of PB2. + PCMSK1 = _BV(PCINT10); // Pin change interrupt for PB2. + PORTB = _BV(2); // Two interrupts if pull-ups configured. + DDRB = _BV(2); // Make sure of it. + + go(); + + /* Do it again with interrupt nesting. */ + + nest = 1; + PORTB = 0; // Two interrupts. + go(); + + /* Final check: writing to GPIOR1 should have had not effect as the + * handler did not writr thr passed value. + */ + + if (GPIOR1) + GPIOR1 = 0xff; + + /* Stop test by sleeping with interrupts off. */ + + sleep_cpu(); +} diff --git a/tests/test_attiny44_interrupt_irq_test.c b/tests/test_attiny44_interrupt_irq_test.c new file mode 100644 index 0000000..c6e6489 --- /dev/null +++ b/tests/test_attiny44_interrupt_irq_test.c @@ -0,0 +1,132 @@ +/* + test_attiny44_interrupt_irq_test.c + + Copyright 2022 Giles Atkinson + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include "sim_irq.h" +#include "sim_interrupts.h" +#include "tests.h" + +/* Accumulate log of events for comparison at the end. */ + +static char log[512]; +static char *fill = log; + +#define LOG(...) \ + (fill += snprintf(fill, (log + sizeof log) - fill, __VA_ARGS__)) + +#define GPIOR1 ((avr_io_addr_t)0x34) // Unused I/O register. + +/* Callbacks for interrupt events. */ + +static void any_pending(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("P-%d%s ", value, irq->flags & IRQ_FLAG_FLOATING ? "f": ""); +} + +static void any_running(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("R-%d ", value); +} + +static void ext_pending(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("PX-%d ", value); +} + +static void ext_running(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("RX-%d ", value); +} + +static void pc1_pending(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("PC-%d ", value); +} + +static void pc1_running(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("RC-%d ", value); +} + +static void adc_pending(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("PA-%d ", value); +} + +static void adc_running(struct avr_irq_t *irq, uint32_t value, void *param) +{ + LOG("RA-%d ", value); +} + +static void gpior1_change(struct avr_t *avr, avr_io_addr_t addr, + uint8_t v, void *param) +{ + LOG("*-%d ", v); +} + +static const char *expected = + "PX-1 P-1 PC-1 P-3 PA-1 P-13 " // Raise three interrupts. + "RX-1 R-1 PX-0 P-3f *-1 RX-0 R-0 " // External (INT0) interrupt runs. + "RC-1 R-3 PC-0 P-13f *-2 RC-0 R-0 " // Pin change interrupt runs. + "RA-1 R-13 PA-0 P-0 *-3 RA-0 R-0 " // ADC interrupt runs. + "PX-1 P-1 PC-1 P-3 PA-1 P-13 " // Second round: raise three. + "RX-1 R-1 PX-0 P-3f *-1 " // External (INT0) interrupt runs. + "RC-1 R-3 PC-0 P-13f *-2 " // Pin change interrupt runs. + "RA-1 R-13 PA-0 P-0 *-3 RA-0 " // ADC interrupt runs. + "R-3 RC-0 R-1 RX-0 R-0 "; // Unwind stack. + +int main(int argc, char **argv) { + avr_t *avr; + avr_irq_t *irq; + + tests_init(argc, argv); + avr = tests_init_avr("attiny44_interrupt_irq_test.axf"); + + /* Request callbacks on events in the interrupt code.. */ + + irq = avr_get_interrupt_irq(avr, AVR_INT_ANY); + avr_irq_register_notify(irq + AVR_INT_IRQ_PENDING, any_pending, NULL); + avr_irq_register_notify(irq + AVR_INT_IRQ_RUNNING, any_running, NULL); + irq = avr_get_interrupt_irq(avr, 1); // INT0 - pin PB2 + avr_irq_register_notify(irq + AVR_INT_IRQ_PENDING, ext_pending, NULL); + avr_irq_register_notify(irq + AVR_INT_IRQ_RUNNING, ext_running, NULL); + irq = avr_get_interrupt_irq(avr, 3); // PCINT1 - Port B + avr_irq_register_notify(irq + AVR_INT_IRQ_PENDING, pc1_pending, NULL); + avr_irq_register_notify(irq + AVR_INT_IRQ_RUNNING, pc1_running, NULL); + irq = avr_get_interrupt_irq(avr, 13); // ADC + avr_irq_register_notify(irq + AVR_INT_IRQ_PENDING, adc_pending, NULL); + avr_irq_register_notify(irq + AVR_INT_IRQ_RUNNING, adc_running, NULL); + + /* Watch GPIOR1 for interrupt handler activity. */ + + avr_register_io_write(avr, GPIOR1, gpior1_change, NULL); + + /* Run program and check results. */ + + if (tests_run_test(avr, 100000) == LJR_CYCLE_TIMER) + fail("Timed out\n"); + if (strcmp(expected, log)) + fail("\nInternal log: %s.\n Expected: %s.\n", log, expected); + tests_success(); + return 0; +}