Skip to content

Commit

Permalink
Cherry-pick 8fa3b20 onto upstream.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ga committed Apr 11, 2022
1 parent 7c4afd1 commit 38ac00f
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 7 deletions.
10 changes: 3 additions & 7 deletions simavr/sim/sim_interrupts.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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));
}

/*
Expand Down Expand Up @@ -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
Expand Down
105 changes: 105 additions & 0 deletions tests/attiny44_interrupt_irq_test.c
Original file line number Diff line number Diff line change
@@ -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();
}
132 changes: 132 additions & 0 deletions tests/test_attiny44_interrupt_irq_test.c
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 38ac00f

Please sign in to comment.