From 6d60bfd4145136b1e9d21083034fd44aa1c09822 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Thu, 10 Oct 2013 15:12:31 +0100 Subject: [PATCH] ioports: Redid the DDR handling logic Now supports an "external" simulation of a pullup/down, this resets the outgoing IRQ for pins to a default that can be specified. Signed-off-by: Michel Pollet --- simavr/sim/avr_ioport.c | 47 +++++++++++++++++++++++++++++------------ simavr/sim/avr_ioport.h | 21 ++++++++++++++++++ 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/simavr/sim/avr_ioport.c b/simavr/sim/avr_ioport.c index b787d2c..d758ecf 100644 --- a/simavr/sim/avr_ioport.c +++ b/simavr/sim/avr_ioport.c @@ -35,6 +35,27 @@ static uint8_t avr_ioport_read(struct avr_t * avr, avr_io_addr_t addr, void * pa return v; } +static void avr_ioport_update_irqs(avr_ioport_t * p) +{ + avr_t * avr = p->io.avr; + uint8_t ddr = avr->data[p->r_ddr]; + // Set the PORT value if the pin is marked as output + // otherwise, if there is an 'external' pullup, set it + // otherwise, if the PORT pin was 1 to indicate an + // internal pullup, set that. + for (int i = 0; i < 8; i++) { + if (ddr & (1 << i)) + avr_raise_irq(p->io.irq + i, (avr->data[p->r_port] >> i) & 1); + else if (p->external.pull_mask & (1 << i)) + avr_raise_irq(p->io.irq + i, (p->external.pull_value >> i) & 1); + else if ((avr->data[p->r_port] >> i) & 1) + avr_raise_irq(p->io.irq + i, 1); + } + uint8_t pin = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr); + pin = (pin & ~p->external.pull_mask) | p->external.pull_value; + avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, pin); +} + static void avr_ioport_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) { avr_ioport_t * p = (avr_ioport_t *)param; @@ -42,10 +63,7 @@ static void avr_ioport_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, avr_core_watch_write(avr, addr, v); // printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv); - // raise the internal IRQ callbacks - for (int i = 0; i < 8; i++) - avr_raise_irq(p->io.irq + i, (v >> i) & 1); - avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, v); + avr_ioport_update_irqs(p); } /* @@ -71,13 +89,7 @@ static void avr_ioport_ddr_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t avr_raise_irq(p->io.irq + IOPORT_IRQ_DIRECTION_ALL, v); avr_core_watch_write(avr, addr, v); - const uint8_t oldpin = avr->data[p->r_pin]; - const uint8_t pin = (oldpin & ~v) | (avr->data[p->r_port] & v); - avr_core_watch_write(avr, p->r_pin, pin); - for (int i = 0; i < 8; i++) - if (((oldpin ^ pin) >> i) & 1) - avr_raise_irq(p->io.irq + i, (pin >> i) & 1); - avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, pin); + avr_ioport_update_irqs(p); } /* @@ -112,7 +124,7 @@ void avr_ioport_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param) static void avr_ioport_reset(avr_io_t * port) { avr_ioport_t * p = (avr_ioport_t *)port; - for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) + for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p); } @@ -157,6 +169,15 @@ static int avr_ioport_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_para *((avr_ioport_state_t*)io_param) = state; res = 0; } + /* + * Set the default IRQ values when pin is set as input + */ + if (ctl == AVR_IOCTL_IOPORT_SET_EXTERNAL(p->name)) { + avr_ioport_external_t * m = (avr_ioport_external_t*)io_param; + p->external.pull_mask = m->mask; + p->external.pull_value = m->value; + res = 0; + } } } @@ -190,7 +211,7 @@ void avr_ioport_init(avr_t * avr, avr_ioport_t * p) // p->name, p->r_pin, // p->name, p->r_ddr, // p->name, p->r_port); - + avr_register_io(avr, &p->io); avr_register_vector(avr, &p->pcint); // allocate this module's IRQ diff --git a/simavr/sim/avr_ioport.h b/simavr/sim/avr_ioport.h index c6f5a4b..32feef8 100644 --- a/simavr/sim/avr_ioport.h +++ b/simavr/sim/avr_ioport.h @@ -70,6 +70,18 @@ typedef struct avr_ioport_state_t { // add port name (uppercase) to get the port state #define AVR_IOCTL_IOPORT_GETSTATE(_name) AVR_IOCTL_DEF('i','o','s',(_name)) +/* + * ioctl used to set default port state when set as input. + * + */ +typedef struct avr_ioport_external_t { + unsigned long name : 7, + mask : 8, value : 8; +} avr_ioport_external_t; + +// add port name (uppercase) to set default input pin IRQ values +#define AVR_IOCTL_IOPORT_SET_EXTERNAL(_name) AVR_IOCTL_DEF('i','o','p',(_name)) + /** * pin structure */ @@ -91,6 +103,15 @@ typedef struct avr_ioport_t { avr_int_vector_t pcint; // PCINT vector avr_io_addr_t r_pcint; // pcint 8 pins mask + + // this represent the default IRQ value when + // the port is set as input. + // If the mask is not set, no output value is sent + // on the output IRQ. If the mask is set, the specified + // value is sent. + struct { + uint8_t pull_mask, pull_value; + } external; } avr_ioport_t; void avr_ioport_init(avr_t * avr, avr_ioport_t * port); -- 2.39.5