From 9136ba8378b2fe0238670181787a9e9898be8e93 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Wed, 10 Jun 2015 21:05:50 +0100 Subject: [PATCH] interrupts: Now track pending and running states Introduced two new IRQs per vector; one to track pending, and one to track running state. So you can see any latency issues. Also introduced a global pending/running IRQ, so you can track issues if your interrupts overrun etc. It is reasonably transparent, and seems to have had little impact on the rest of the code... Hooray for nice clean APIs! Signed-off-by: Michel Pollet --- simavr/sim/avr_watchdog.c | 5 ++-- simavr/sim/sim_core.c | 4 +-- simavr/sim/sim_interrupts.c | 56 +++++++++++++++++++++++++++++++++---- simavr/sim/sim_interrupts.h | 18 ++++++++++-- 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/simavr/sim/avr_watchdog.c b/simavr/sim/avr_watchdog.c index f8a5938..64d98d9 100644 --- a/simavr/sim/avr_watchdog.c +++ b/simavr/sim/avr_watchdog.c @@ -196,8 +196,9 @@ static void avr_watchdog_reset(avr_io_t * port) avr_watchdog_set_cycle_count_and_timer(avr, p, 0, 0); } - - avr_irq_register_notify(&p->watchdog.irq, avr_watchdog_irq_notify, p); + /* TODO could now use the two pending/running IRQs to do the same + * as before */ + avr_irq_register_notify(p->watchdog.irq, avr_watchdog_irq_notify, p); } static avr_io_t _io = { diff --git a/simavr/sim/sim_core.c b/simavr/sim/sim_core.c index f00a74d..d3dca45 100644 --- a/simavr/sim/sim_core.c +++ b/simavr/sim/sim_core.c @@ -938,11 +938,11 @@ run_one_again: TRACE_JUMP(); } break; case 0x9518: // RETI -- Return from Interrupt -- 1001 0101 0001 1000 + avr_sreg_set(avr, S_I, 1); + avr_interrupt_reti(avr); case 0x9508: { // RET -- Return -- 1001 0101 0000 1000 new_pc = _avr_pop_addr(avr); cycle += 1 + avr->address_size; - if (opcode & 0x10) // reti - avr_sreg_set(avr, S_I, 1); STATE("ret%s\n", opcode & 0x10 ? "i" : ""); TRACE_JUMP(); STACK_FRAME_POP(); diff --git a/simavr/sim/sim_interrupts.c b/simavr/sim/sim_interrupts.c index e08826e..a5e96ff 100644 --- a/simavr/sim/sim_interrupts.c +++ b/simavr/sim/sim_interrupts.c @@ -38,14 +38,21 @@ avr_interrupt_init( { avr_int_table_p table = &avr->interrupts; memset(table, 0, sizeof(*table)); + + printf("%s\n", __func__); + static const char *names[] = { ">global_int_pending", ">global_int_running" }; + avr_init_irq(&avr->irq_pool, table->irq, + 0, // base number + AVR_INT_IRQ_COUNT, names); } void avr_interrupt_reset( avr_t * avr ) { - printf("%s\n", __func__); avr_int_table_p table = &avr->interrupts; + + table->running_ptr = 0; table->pending_r = table->pending_w = 0; avr->interrupt_state = 0; for (int i = 0; i < table->vector_count; i++) @@ -62,7 +69,11 @@ avr_register_vector( avr_int_table_p table = &avr->interrupts; - vector->irq.irq = vector->vector; + static const char *names[] = { ">int_pending", ">int_running" }; + avr_init_irq(&avr->irq_pool, vector->irq, + vector->vector * 256, // base number + AVR_INT_IRQ_COUNT, names); +// vector->irq.irq = vector->vector; table->vector[table->vector_count++] = vector; if (vector->trace) printf("%s register vector %d (enabled %04x:%d)\n", __FUNCTION__, vector->vector, vector->enable.reg, vector->enable.bit); @@ -115,7 +126,8 @@ avr_raise_interrupt( if (vector->raised.reg) avr_regbit_set(avr, vector->raised); - avr_raise_irq(&vector->irq, 1); + avr_raise_irq(vector->irq + AVR_INT_IRQ_PENDING, 1); + avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, 1); // If the interrupt is enabled, attempt to wake the core if (avr_regbit_get(avr, vector->enable)) { @@ -149,7 +161,11 @@ avr_clear_interrupt( if (vector->trace) printf("%s cleared %d\n", __FUNCTION__, vector->vector); vector->pending = 0; - avr_raise_irq(&vector->irq, 0); + + avr_raise_irq(vector->irq + AVR_INT_IRQ_PENDING, 0); + avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, + avr_has_pending_interrupts(avr)); + if (vector->raised.reg && !vector->raise_sticky) avr_regbit_clear(avr, vector->raised); } @@ -160,6 +176,8 @@ avr_clear_interrupt_if( avr_int_vector_t * vector, uint8_t old) { + avr_raise_irq(avr->interrupts.irq + AVR_INT_IRQ_PENDING, + avr_has_pending_interrupts(avr)); if (avr_regbit_get(avr, vector->raised)) { avr_clear_interrupt(avr, vector); return 1; @@ -174,12 +192,31 @@ avr_get_interrupt_irq( uint8_t v) { avr_int_table_p table = &avr->interrupts; + if (v == AVR_INT_ANY) + return table->irq; for (int i = 0; i < table->vector_count; i++) if (table->vector[i]->vector == v) - return &table->vector[i]->irq; + return table->vector[i]->irq; return NULL; } +/* this is called uppon RETI. */ +void +avr_interrupt_reti( + struct avr_t * avr) +{ + avr_int_table_p table = &avr->interrupts; + if (table->running_ptr) { + avr_int_vector_t * vector = table->running[--table->running_ptr]; + avr_raise_irq(vector->irq + AVR_INT_IRQ_RUNNING, 0); + } + 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)); +} + /* * check whether interrupts are pending. If so, check if the interrupt "latency" is reached, * and if so triggers the handlers and jump to the vector. @@ -224,6 +261,8 @@ avr_service_interrupts( // the one we service table->pending[mini] = table->pending[table->pending_r++]; table->pending_r = INT_FIFO_MOD(table->pending_r); + 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 @@ -237,6 +276,13 @@ avr_service_interrupts( avr_sreg_set(avr, S_I, 0); avr->pc = vector->vector * avr->vector_size; + avr_raise_irq(vector->irq + AVR_INT_IRQ_RUNNING, 1); + avr_raise_irq(table->irq + AVR_INT_IRQ_RUNNING, vector->vector); + if (table->running_ptr == ARRAY_SIZE(table->running)) { + AVR_LOG(avr, LOG_ERROR, "%s run out of nested stack!", __func__); + } else { + table->running[table->running_ptr++] = vector; + } avr_clear_interrupt(avr, vector); } } diff --git a/simavr/sim/sim_interrupts.h b/simavr/sim/sim_interrupts.h index c8c9bed..0ee9294 100644 --- a/simavr/sim/sim_interrupts.h +++ b/simavr/sim/sim_interrupts.h @@ -29,13 +29,20 @@ extern "C" { #endif +enum { + AVR_INT_IRQ_PENDING = 0, + AVR_INT_IRQ_RUNNING, + AVR_INT_IRQ_COUNT, + AVR_INT_ANY = 0xff, // for avr_get_interrupt_irq() +}; // interrupt vector for the IO modules typedef struct avr_int_vector_t { uint8_t vector; // vector number, zero (reset) is reserved avr_regbit_t enable; // IO register index for the "interrupt enable" flag for this vector avr_regbit_t raised; // IO register index for the register where the "raised" flag is (optional) - avr_irq_t irq; // raised to 1 when queued, to zero when called + // 'pending' IRQ, and 'running' status as signaled here + avr_irq_t irq[AVR_INT_IRQ_COUNT]; uint8_t pending : 1, // 1 while scheduled in the fifo trace : 1, // only for debug of a vector raise_sticky : 1; // 1 if the interrupt flag (= the raised regbit) is not cleared @@ -49,6 +56,10 @@ typedef struct avr_int_table_t { avr_int_vector_t * pending[64]; // needs to be >= vectors and a power of two uint8_t pending_w, pending_r; // fifo cursors + uint8_t running_ptr; + avr_int_vector_t *running[64]; // stack of nested interrupts + // global status for pending + running in interrupt context + avr_irq_t irq[AVR_INT_IRQ_COUNT]; } avr_int_table_t, *avr_int_table_p; /* @@ -83,7 +94,10 @@ avr_clear_interrupt( void avr_service_interrupts( struct avr_t * avr); - +// called by the core when RETI opcode is ran +void +avr_interrupt_reti( + struct avr_t * avr); // clear the interrupt (inc pending) if "raised" flag is 1 int avr_clear_interrupt_if( -- 2.39.5