Commit 7d859a4b1eb18a3ef13636587ba051a54d7a69b3
authorMichel Pollet <buserror@gmail.com>
Mon, 14 Dec 2009 20:57:55 +0000 (20:57 +0000)
committerMichel Pollet <buserror@gmail.com>
Mon, 14 Dec 2009 20:57:55 +0000 (20:57 +0000)
Made the IRQ subsytem even more generic, allows
IRQs to be directly connected to other IRQ without
the glue callback.
Also added flags to allow changing IRQ polarity, and
to enable/disable a "filter" that won't trigger the
callbacks when the IRQ value is the same as before.

Signed-off-by: Michel Pollet <buserror@gmail.com>
2 files changed:
simavr/sim/sim_irq.c
simavr/sim/sim_irq.h

index f3377fd640d2eefd0fae9d62b0468c7381349316..8faff3f3ff534dd9bf818dc84ca4af84e55fb74a 100644 (file)
@@ -37,9 +37,37 @@ avr_irq_t * avr_alloc_irq(uint32_t base, uint32_t count)
 {
        avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
        avr_init_irq(irq, base, count);
+       for (int i = 0; i < count; i++)
+               irq[i].flags |= IRQ_FLAG_ALLOC; 
        return irq;
 }
 
+static avr_irq_hook_t * _avr_alloc_irq_hook(avr_irq_t * irq)
+{
+       avr_irq_hook_t *hook = malloc(sizeof(avr_irq_hook_t));
+       memset(hook, 0, sizeof(avr_irq_hook_t));
+       hook->next = irq->hook;
+       irq->hook = hook;
+       return hook;
+}
+
+void avr_free_irq(avr_irq_t * irq, uint32_t count)
+{
+       for (int i = 0; i < count; i++) {
+               // purge hooks
+               avr_irq_hook_t *hook = irq->hook;
+               while (hook) {
+                       avr_irq_hook_t * next = hook->next;
+                       free(hook);
+                       hook = next;
+               }
+               irq->hook = NULL;
+       }
+       // if that irq list was allocated by us, free it
+       if (irq->flags & IRQ_FLAG_ALLOC)
+               free(irq);
+}
+
 void avr_irq_register_notify(avr_irq_t * irq, avr_irq_notify_t notify, void * param)
 {
        if (!irq || !notify)
@@ -51,44 +79,50 @@ void avr_irq_register_notify(avr_irq_t * irq, avr_irq_notify_t notify, void * pa
                        return; // already there
                hook = hook->next;
        }
-       hook = malloc(sizeof(avr_irq_hook_t));
-       memset(hook, 0, sizeof(avr_irq_hook_t));
-       hook->next = irq->hook;
+       hook = _avr_alloc_irq_hook(irq);
        hook->notify = notify;
        hook->param = param;
-       irq->hook = hook;
 }
 
 void avr_raise_irq(avr_irq_t * irq, uint32_t value)
 {
-       if (!irq || irq->value == value)
+       if (!irq)
                return ;
+       uint32_t output = (irq->flags & IRQ_FLAG_NOT) ? !value : value;
+       if (irq->value == output && (irq->flags & IRQ_FLAG_FILTERED))
+               return;
        avr_irq_hook_t *hook = irq->hook;
        while (hook) {
-               if (hook->notify) {     
+               avr_irq_hook_t * next = hook->next;
                        // prevents reentrance / endless calling loops
-                       if (hook->busy == 0) {
-                               hook->busy++;
-                               hook->notify(irq, value, hook->param);
-                               hook->busy--;
-                       }
-               }
-               hook = hook->next;
+               if (hook->busy == 0) {
+                       hook->busy++;
+                       if (hook->notify)
+                               hook->notify(irq, output,  hook->param);
+                       if (hook->chain)
+                               avr_raise_irq(hook->chain, output);
+                       hook->busy--;
+               }                       
+               hook = next;
        }
-       irq->value = value;
-}
-
-static void _avr_irq_connect(avr_irq_t * irq, uint32_t value, void * param)
-{
-       avr_irq_t * dst = (avr_irq_t*)param;
-       avr_raise_irq(dst, value);
+       // the value is set after the callbacks are called, so the callbacks
+       // can themselves compare for old/new values between their parameter
+       // they are passed (new value) and the previous irq->value
+       irq->value = output;
 }
 
 void avr_connect_irq(avr_irq_t * src, avr_irq_t * dst)
 {
-       if (!src || !dst) {
+       if (!src || !dst || src == dst) {
                printf("avr_connect_irq invalid irq %p/%p", src, dst);
                return;
        }
-       avr_irq_register_notify(src, _avr_irq_connect, dst);
+       avr_irq_hook_t *hook = src->hook;
+       while (hook) {
+               if (hook->chain == dst)
+                       return; // already there
+               hook = hook->next;
+       }
+       hook = _avr_alloc_irq_hook(src);
+       hook->chain = dst;
 }
index c6782149766fefca21ecf462a3995d0c771507de..6c5e94578e3ffca4519a2045871bd714491e6a3b 100644 (file)
  * IRQ hook needs to be registered in reset() handlers, ie after all modules init() bits
  * have been called, to prevent race condition of the initialization order.
  */
-// internal structure for a hook, never seen by the notify procs
 struct avr_irq_t;
 
 typedef void (*avr_irq_notify_t)(struct avr_irq_t * irq, uint32_t value, void * param);
 
+// internal structure for a hook, never seen by the notify procs
 typedef struct avr_irq_hook_t {
        struct avr_irq_hook_t * next;
-       void * param;
        int busy;       // prevent reentrance of callbacks
-       avr_irq_notify_t notify;
+       
+       struct avr_irq_t * chain;       // raise the IRQ on this too - optional if "notify" is on
+       avr_irq_notify_t notify;        // called when IRQ is raised - optional if "chain" is on
+       void * param;                           // "notify" parameter
 } avr_irq_hook_t;
 
+enum {
+       IRQ_FLAG_NOT            = (1 << 0),     // change polarity of the IRQ
+       IRQ_FLAG_FILTERED       = (1 << 1),     // do not "notify" if "value" is the same as previous raise
+       IRQ_FLAG_ALLOC          = (1 << 2), // this irq structure was malloced via avr_alloc_irq
+};
+
+/*
+ * Public IRQ structure
+ */
 typedef struct avr_irq_t {
        uint32_t                        irq;
        uint32_t                        value;
+       uint8_t                         flags;  // IRQ_*
        avr_irq_hook_t *        hook;
 } avr_irq_t;
 
 avr_irq_t * avr_alloc_irq(uint32_t base, uint32_t count);
+void avr_free_irq(avr_irq_t * irq, uint32_t count);
+
 void avr_init_irq(avr_irq_t * irq, uint32_t base, uint32_t count);
 void avr_raise_irq(avr_irq_t * irq, uint32_t value);
 // this connects a "source" IRQ to a "destination" IRQ