Commit e4b36b58d22d22da62a0bcd95755f7454df411cb
authorMichel Pollet <buserror@gmail.com>
Wed, 10 Jun 2015 20:05:50 +0000 (21:05 +0100)
committerMichel Pollet <buserror@gmail.com>
Thu, 2 Jul 2015 18:02:54 +0000 (19:02 +0100)
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 <buserror@gmail.com>
4 files changed:
simavr/sim/avr_watchdog.c
simavr/sim/sim_core.c
simavr/sim/sim_interrupts.c
simavr/sim/sim_interrupts.h

index f8a5938a8e19a2e4c9f72ec3b522cf2afe9c6982..64d98d9428df657d0663a7c32af0456e11106eec 100644 (file)
@@ -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 = {
index f00a74d003a18c856076394fd3d5e7d4afd4dd8e..d3dca454d299138a716137784b928e6b000a38c5 100644 (file)
@@ -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();
index e08826e036f50cfdb74280c99ce40c957373f474..a5e96ff1d38e9a06c1711cbeacb5127d7f95788e 100644 (file)
@@ -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);
        }
 }
index c8c9bed1f96235ee35befa43894d86d9c9a3afb4..0ee929430eb167ea1864ff000a4d0007484c18b7 100644 (file)
 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(