From 7d859a4b1eb18a3ef13636587ba051a54d7a69b3 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Mon, 14 Dec 2009 20:57:55 +0000 Subject: [PATCH] Streamlined avr_irq subsystem 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 --- simavr/sim/sim_irq.c | 78 +++++++++++++++++++++++++++++++------------- simavr/sim/sim_irq.h | 20 ++++++++++-- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/simavr/sim/sim_irq.c b/simavr/sim/sim_irq.c index f3377fd..8faff3f 100644 --- a/simavr/sim/sim_irq.c +++ b/simavr/sim/sim_irq.c @@ -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; } diff --git a/simavr/sim/sim_irq.h b/simavr/sim/sim_irq.h index c678214..6c5e945 100644 --- a/simavr/sim/sim_irq.h +++ b/simavr/sim/sim_irq.h @@ -39,25 +39,39 @@ * 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 -- 2.39.5