From 67688243000a1928b03f470f11fa3c353d229bd6 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Sat, 25 Feb 2012 10:05:23 +0000 Subject: [PATCH] interrupts: Rewrote interrupt delivery Now uses a fifo, removed the bitfield. This allows multiple interrupt "sources" per vector, and should make delivery a bit quicker too. Signed-off-by: Michel Pollet --- simavr/sim/avr_twi.c | 2 +- simavr/sim/sim_avr.h | 4 +- simavr/sim/sim_interrupts.c | 108 +++++++++++++++++++++++------------- simavr/sim/sim_interrupts.h | 5 +- 4 files changed, 74 insertions(+), 45 deletions(-) diff --git a/simavr/sim/avr_twi.c b/simavr/sim/avr_twi.c index 972d2a7..41d2bde 100644 --- a/simavr/sim/avr_twi.c +++ b/simavr/sim/avr_twi.c @@ -152,7 +152,7 @@ avr_twi_write( avr_regbit_clear(avr, p->twea); avr_regbit_clear(avr, p->twsta); avr_regbit_clear(avr, p->twsto); - avr_clear_interrupt(avr, p->twi.vector); + avr_clear_interrupt(avr, &p->twi); avr_core_watch_write(avr, p->r_twdr, 0xff); _avr_twi_status_set(p, TWI_NO_STATE, 0); p->state = 0; diff --git a/simavr/sim/sim_avr.h b/simavr/sim/sim_avr.h index bc895aa..8e7f8c7 100644 --- a/simavr/sim/sim_avr.h +++ b/simavr/sim/sim_avr.h @@ -245,8 +245,10 @@ typedef struct avr_t { // interrupt vectors, and their enable/clear registers struct avr_int_vector_t * vector[64]; + uint8_t vector_count; uint8_t pending_wait; // number of cycles to wait for pending - uint32_t pending[2]; // pending interrupts + struct avr_int_vector_t * pending[64]; // needs to be >= vectors and a power of two + uint8_t pending_w, pending_r; // fifo cursors // DEBUG ONLY -- value ignored if CONFIG_SIMAVR_TRACE = 0 int trace : 1, diff --git a/simavr/sim/sim_interrupts.c b/simavr/sim/sim_interrupts.c index d1d2dca..e5d1609 100644 --- a/simavr/sim/sim_interrupts.c +++ b/simavr/sim/sim_interrupts.c @@ -27,11 +27,15 @@ #include "sim_interrupts.h" #include "sim_core.h" +// modulo a cursor value on the pending interrupt fifo +#define INT_FIFO_SIZE (sizeof(avr->pending) / sizeof(avr_int_vector_t *)) +#define INT_FIFO_MOD(_v) ((_v) & (INT_FIFO_SIZE - 1)) + void avr_register_vector(avr_t *avr, avr_int_vector_t * vector) { if (vector->vector) { vector->irq.irq = vector->vector; - avr->vector[vector->vector] = vector; + avr->vector[avr->vector_count++] = vector; if (vector->trace) printf("%s register vector %d (enabled %04x:%d)\n", __FUNCTION__, vector->vector, vector->enable.reg, vector->enable.bit); @@ -42,12 +46,12 @@ void avr_register_vector(avr_t *avr, avr_int_vector_t * vector) int avr_has_pending_interrupts(avr_t * avr) { - return avr->pending[0] || avr->pending[1]; + return avr->pending_r != avr->pending_w; } int avr_is_interrupt_pending(avr_t * avr, avr_int_vector_t * vector) { - return avr->pending[vector->vector >> 5] & (1 << (vector->vector & 0x1f)); + return vector->pending; } int avr_is_interrupt_enabled(avr_t * avr, avr_int_vector_t * vector) @@ -61,6 +65,11 @@ int avr_raise_interrupt(avr_t * avr, avr_int_vector_t * vector) return 0; if (vector->trace) printf("%s raising %d (enabled %d)\n", __FUNCTION__, vector->vector, avr_regbit_get(avr, vector->enable)); + if (vector->pending) { + if (vector->trace) + printf("%s trying to double raise %d (enabled %d)\n", __FUNCTION__, vector->vector, avr_regbit_get(avr, vector->enable)); + return 0; + } // always mark the 'raised' flag to one, even if the interrupt is disabled // this allow "pooling" for the "raised" flag, like for non-interrupt // driven UART and so so. These flags are often "write one to clear" @@ -68,7 +77,10 @@ int avr_raise_interrupt(avr_t * avr, avr_int_vector_t * vector) avr_regbit_set(avr, vector->raised); // Mark the interrupt as pending - avr->pending[vector->vector >> 5] |= (1 << (vector->vector & 0x1f)); + vector->pending = 1; + avr->pending[avr->pending_w++] = vector; + avr->pending_w = INT_FIFO_MOD(avr->pending_w); + avr_raise_irq(&vector->irq, 1); // If the interrupt is enabled, attempt to wake the core @@ -85,14 +97,13 @@ int avr_raise_interrupt(avr_t * avr, avr_int_vector_t * vector) return 1; } -void avr_clear_interrupt(avr_t * avr, int v) +void avr_clear_interrupt(avr_t * avr, avr_int_vector_t * vector) { - avr_int_vector_t * vector = avr->vector[v]; - avr->pending[v >> 5] &= ~(1 << (v & 0x1f)); if (!vector) return; if (vector->trace) printf("%s cleared %d\n", __FUNCTION__, vector->vector); + vector->pending = 0; avr_raise_irq(&vector->irq, 0); if (vector->raised.reg) avr_regbit_clear(avr, vector->raised); @@ -101,7 +112,7 @@ void avr_clear_interrupt(avr_t * avr, int v) int avr_clear_interrupt_if(avr_t * avr, avr_int_vector_t * vector, uint8_t old) { if (avr_regbit_get(avr, vector->raised)) { - avr_clear_interrupt(avr, vector->vector); + avr_clear_interrupt(avr, vector); avr_regbit_clear(avr, vector->raised); return 1; } @@ -111,8 +122,10 @@ int avr_clear_interrupt_if(avr_t * avr, avr_int_vector_t * vector, uint8_t old) avr_irq_t * avr_get_interrupt_irq(avr_t * avr, uint8_t v) { - avr_int_vector_t * vector = avr->vector[v]; - return vector ? &vector->irq : NULL; + for (int i = 0; i < avr->vector_count; i++) + if (avr->vector[i]->vector == v) + return &avr->vector[i]->irq; + return NULL; } /* @@ -124,36 +137,51 @@ void avr_service_interrupts(avr_t * avr) if (!avr->sreg[S_I]) return; - if (avr_has_pending_interrupts(avr)) { - if (avr->pending_wait) { - avr->pending_wait--; - if (avr->pending_wait == 0) { - for (int bi = 0; bi < 2; bi++) - if (avr->pending[bi]) { - uint32_t map = avr->pending[bi]; - while (map) { - int bit = ffs(map)-1; - int v = (bi * 32) + bit; // vector - avr_int_vector_t * vector = avr->vector[v]; - // if that single interrupt is masked, ignore it and continue - if (vector && !avr_regbit_get(avr, vector->enable)) { - map &= ~(1 << bit); - continue; - } - if (vector && vector->trace) - printf("%s calling %d\n", __FUNCTION__, v); - _avr_push16(avr, avr->pc >> 1); - avr->sreg[S_I] = 0; - avr->pc = v * avr->vector_size; - - avr_clear_interrupt(avr, v); - break; - } - break; - } - } - } else - avr->pending_wait = 2; // for next one... + if (!avr_has_pending_interrupts(avr)) + return; + + if (!avr->pending_wait) { + avr->pending_wait = 2; // for next one... + return; + } + avr->pending_wait--; + if (avr->pending_wait) + return; + + // how many are pending... + int cnt = avr->pending_w > avr->pending_r ? + avr->pending_w - avr->pending_r : + (avr->pending_w+INT_FIFO_SIZE) - avr->pending_r; + // locate the highest priority one + int min = 0xff; + int mini = 0; + for (int ii = 0; ii < cnt; ii++) { + int vi = INT_FIFO_MOD(avr->pending_r + ii); + avr_int_vector_t * v = avr->pending[vi]; + if (v->vector < min) { + min = v->vector; + mini = vi; + } + } + avr_int_vector_t * vector = avr->pending[mini]; + + // now move the one at the front of the fifo in the slot of + // the one we service + avr->pending[mini] = avr->pending[avr->pending_r++]; + avr->pending_r = INT_FIFO_MOD(avr->pending_r); + + // if that single interrupt is masked, ignore it and continue + // could also have been disabled, or cleared + if (!avr_regbit_get(avr, vector->enable) || !vector->pending) { + vector->pending = 0; + } else { + if (vector && vector->trace) + printf("%s calling %d\n", __FUNCTION__, (int)vector->vector); + _avr_push16(avr, avr->pc >> 1); + avr->sreg[S_I] = 0; + avr->pc = vector->vector * avr->vector_size; + + avr_clear_interrupt(avr, vector); } } diff --git a/simavr/sim/sim_interrupts.h b/simavr/sim/sim_interrupts.h index 92d9c8c..b7c1a3b 100644 --- a/simavr/sim/sim_interrupts.h +++ b/simavr/sim/sim_interrupts.h @@ -31,7 +31,7 @@ extern "C" { // interrupt vector for the IO modules typedef struct avr_int_vector_t { - uint8_t vector; // vector number, zero (reset) is reserved + uint8_t pending : 1, 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) @@ -39,7 +39,6 @@ typedef struct avr_int_vector_t { uint8_t trace; // only for debug of a vector } avr_int_vector_t; - /* * Interrupt Helper Functions */ @@ -53,7 +52,7 @@ int avr_has_pending_interrupts(avr_t * avr); // return nonzero if a specific interrupt vector is pending int avr_is_interrupt_pending(avr_t * avr, avr_int_vector_t * vector); // clear the "pending" status of an interrupt -void avr_clear_interrupt(avr_t * avr, int v); +void avr_clear_interrupt(avr_t * avr, avr_int_vector_t * vector); // called by the core at each cycle to check whether an interrupt is pending void avr_service_interrupts(avr_t * avr); -- 2.39.5