Commit 38ac00f21dc17f175ddde4266ca2a21cb506b1c5
authorga <ga@oldell.fish>
Mon, 11 Apr 2022 10:06:35 +0000 (11:06 +0100)
committerga <ga@oldell.fish>
Mon, 11 Apr 2022 10:06:35 +0000 (11:06 +0100)
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.

3 files changed:
simavr/sim/sim_interrupts.c
tests/attiny44_interrupt_irq_test.c [new file with mode: 0644]
tests/test_attiny44_interrupt_irq_test.c [new file with mode: 0644]

index f96466deb659443bf220fa35d973ca4f53e016fc..9e3ed9c064a56e0a4b9e277ddce2462a6843710b 100644 (file)
@@ -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 (file)
index 0000000..cda0195
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <avr/io.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+#include <util/delay.h>
+#include <avr/cpufunc.h>
+#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 (file)
index 0000000..c6e6489
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}