uart, ioports, etc. Many more changes
authorMichel Pollet <buserror@gmail.com>
Tue, 1 Dec 2009 21:47:48 +0000 (21:47 +0000)
committerMichel Pollet <buserror@gmail.com>
Tue, 1 Dec 2009 21:47:48 +0000 (21:47 +0000)
+ Reorganized source, split simavr.c
+ IRQ support added to IO modules
+ timer8: fixed a bug related to disabling clock
+ uart:
  - Added support for txen/rxen flags
  - Added a receive fifo, and the rx interupt
  - added a "atmega88_uart_echo" test case
+ simavr: hook rx & tx irqs on first uart, for tests

Signed-off-by: Michel Pollet <buserror@gmail.com>
23 files changed:
.simavr.jcc
simavr/cores/sim_mega644.c
simavr/cores/sim_megax8.h
simavr/sim/avr_eeprom.c
simavr/sim/avr_ioport.c
simavr/sim/avr_ioport.h
simavr/sim/avr_spi.h
simavr/sim/avr_timer8.c
simavr/sim/avr_uart.c
simavr/sim/avr_uart.h
simavr/sim/fifo_declare.h [new file with mode: 0644]
simavr/sim/sim_core.h
simavr/sim/sim_interrupts.c [new file with mode: 0644]
simavr/sim/sim_interrupts.h [new file with mode: 0644]
simavr/sim/sim_io.c [new file with mode: 0644]
simavr/sim/sim_io.h [new file with mode: 0644]
simavr/sim/sim_irq.c [new file with mode: 0644]
simavr/sim/sim_irq.h [new file with mode: 0644]
simavr/sim/sim_regbit.h [new file with mode: 0644]
simavr/sim/simavr.c
simavr/sim/simavr.h
tests/atmega48_disabled_timer.c [new file with mode: 0644]
tests/atmega88_uart_echo.c [new file with mode: 0644]

index 640d586..b69769e 100644 (file)
@@ -72,6 +72,34 @@ T
 F
 "./simavr/sim/sim_elf.h"
 T
+2 "sim_interrupts.c"
+F
+"./simavr/sim/sim_interrupts.c"
+T
+2 "sim_interrupts.h"
+F
+"./simavr/sim/sim_interrupts.h"
+T
+2 "sim_regbit.h"
+F
+"./simavr/sim/sim_regbit.h"
+T
+2 "sim_irq.c"
+F
+"./simavr/sim/sim_irq.c"
+T
+2 "sim_irq.h"
+F
+"./simavr/sim/sim_irq.h"
+T
+2 "sim_io.c"
+F
+"./simavr/sim/sim_io.c"
+T
+2 "sim_io.h"
+F
+"./simavr/sim/sim_io.h"
+T
 2 "simavr.c"
 F
 "./simavr/sim/simavr.c"
@@ -79,6 +107,10 @@ T
 2 "simavr.h"
 F
 "./simavr/sim/simavr.h"
+T
+2 "fifo_declare.h"
+F
+"./simavr/sim/fifo_declare.h"
 F
 T
 1 "tests"
index a063ca2..22b439d 100644 (file)
@@ -92,6 +92,9 @@ static struct mcu_t {
                .name = '0',
                .r_udr = UDR0,
 
+               .txen = AVR_IO_REGBIT(UCSR0B, TXEN0),
+               .rxen = AVR_IO_REGBIT(UCSR0B, RXEN0),
+
                .r_ucsra = UCSR0A,
                .r_ucsrb = UCSR0B,
                .r_ucsrc = UCSR0C,
@@ -118,6 +121,9 @@ static struct mcu_t {
                .name = '1',
                .r_udr = UDR1,
 
+               .txen = AVR_IO_REGBIT(UCSR1B, TXEN1),
+               .rxen = AVR_IO_REGBIT(UCSR1B, RXEN1),
+
                .r_ucsra = UCSR1A,
                .r_ucsrb = UCSR1B,
                .r_ucsrc = UCSR1C,
index 682b2c4..8ea2bd3 100644 (file)
@@ -96,6 +96,9 @@ struct mcu_t SIM_CORENAME = {
                .name = '0',
                .r_udr = UDR0,
 
+               .txen = AVR_IO_REGBIT(UCSR0B, TXEN0),
+               .rxen = AVR_IO_REGBIT(UCSR0B, RXEN0),
+
                .r_ucsra = UCSR0A,
                .r_ucsrb = UCSR0B,
                .r_ucsrc = UCSR0C,
@@ -194,4 +197,4 @@ struct mcu_t SIM_CORENAME = {
 };
 #endif /* SIM_CORENAME */
 
-#endif /* __SIM_MEGAX8_H__ */
\ No newline at end of file
+#endif /* __SIM_MEGAX8_H__ */
index 4045027..b6ff5a1 100644 (file)
@@ -39,7 +39,7 @@ static void avr_eeprom_run(avr_t * avr, avr_io_t * port)
        if (p->ready_raise_timer) {
                p->ready_raise_timer--;
                if (p->ready_raise_timer == 0) {
-                       avr_raise_interupt(avr, &p->ready);
+                       avr_raise_interrupt(avr, &p->ready);
                }
        }
 }
@@ -59,7 +59,7 @@ static void avr_eeprom_write(struct avr_t * avr, uint8_t addr, uint8_t v, void *
                uint16_t addr = avr->data[p->r_eearl] | (avr->data[p->r_eearh] << 8);
        //      printf("eeprom write %04x <- %02x\n", addr, avr->data[p->r_eedr]);
                p->eeprom[addr] = avr->data[p->r_eedr]; 
-               // automaticaly clears that bit (?)
+               // Automatically clears that bit (?)
                p->eempe_clear_timer = 0;
                avr_regbit_clear(avr, p->eempe);
 
@@ -107,8 +107,8 @@ static      avr_io_t        _io = {
 void avr_eeprom_init(avr_t * avr, avr_eeprom_t * p)
 {
        p->io = _io;
-       printf("%s init (%d bytes) EEL/H:%02x/%02x EED=%02x EEC=%02x\n",
-                       __FUNCTION__, p->size, p->r_eearl, p->r_eearh, p->r_eedr, p->r_eecr);
+//     printf("%s init (%d bytes) EEL/H:%02x/%02x EED=%02x EEC=%02x\n",
+//                     __FUNCTION__, p->size, p->r_eearl, p->r_eearh, p->r_eedr, p->r_eecr);
 
        p->eeprom = malloc(p->size);
        memset(p->eeprom, 0xff, p->size);
index 2bb4483..7394c0c 100644 (file)
@@ -33,7 +33,8 @@ static uint8_t avr_ioport_read(struct avr_t * avr, uint8_t addr, void * param)
        uint8_t v = avr->data[addr];
 
        if (addr == p->r_pin) {
-               uint8_t v = avr->data[p->r_port];
+               uint8_t ddr = avr->data[p->r_ddr];
+               uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
                avr->data[addr] = v;
                // made to trigger potential watchpoints
                v = avr_core_watch_read(avr, addr);
@@ -57,66 +58,58 @@ static void avr_ioport_write(struct avr_t * avr, uint8_t addr, uint8_t v, void *
                        // raise the internal IRQ callbacks
                        for (int i = 0; i < 8; i++)
                                if (mask & (1 << i))
-                                       avr_raise_irq(avr, p->irq + i, (v >> i) & 1);
-                       avr_raise_irq(avr, p->irq + IOPORT_IRQ_PIN_ALL, v);
+                                       avr_raise_irq(avr, p->io.irq + i, (v >> i) & 1);
+                       avr_raise_irq(avr, p->io.irq + IOPORT_IRQ_PIN_ALL, v);
                }
        }
 }
 
 /*
- * this is out "main" pin change callback, it can be triggered by either the
+ * this is our "main" pin change callback, it can be triggered by either the
  * AVR code, or any external piece of code that see fit to do it.
- * Either way, this will raise pin change interupts, if needed
+ * Either way, this will raise pin change interrupts, if needed
  */
 void avr_ioport_irq_notify(avr_t * avr, struct avr_irq_t * irq, uint32_t value, void * param)
 {
        avr_ioport_t * p = (avr_ioport_t *)param;
        if (p->r_pcint) {
-               int raise = avr->data[p->r_pcint] & (1 << irq->irq);
+               uint8_t mask = 1 << irq->irq;
+               // set the real PIN bit. ddr doesn't matter here as it's masked when read.
+               avr->data[p->r_pin] &= ~mask;
+               if (value)
+                       avr->data[p->r_pin] |= mask;
+               // if the pcint bit is on, try to raise it
+               int raise = avr->data[p->r_pcint] & mask;
                if (raise)
-                       avr_raise_interupt(avr, &p->pcint);
+                       avr_raise_interrupt(avr, &p->pcint);
        }
-       
-}
-
-static int avr_ioport_ioctl(avr_t * avr, avr_io_t * port, uint32_t ctl, void * io_param)
-{
-       avr_ioport_t * p = (avr_ioport_t *)port;
-       int res = -1;
-
-       switch(ctl) {
-               case AVR_IOCTL_IOPORT_GETIRQ ... AVR_IOCTL_IOPORT_GETIRQ + IOPORT_IRQ_PIN_ALL: {
-                       printf("%s: AVR_IOCTL_IOPORT_GETIRQ  %d\n", __FUNCTION__, 0);
-               }       break;
-       }
-       
-       return res;
 }
 
 static void avr_ioport_reset(avr_t * avr, avr_io_t * port)
 {
        avr_ioport_t * p = (avr_ioport_t *)port;
        for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) 
-               avr_irq_register_notify(avr, p->irq + i, avr_ioport_irq_notify, p);
+               avr_irq_register_notify(avr, p->io.irq + i, avr_ioport_irq_notify, p);
 }
 
 static avr_io_t        _io = {
        .kind = "io",
        .run = avr_ioport_run,
        .reset = avr_ioport_reset,
-       .ioctl = avr_ioport_ioctl,
 };
 
 void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
 {
        p->io = _io;
-       printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n",
-               __FUNCTION__,
-               p->name, p->r_pin,
-               p->name, p->r_ddr,
-               p->name, p->r_port);
-
-       p->irq = avr_alloc_irq(avr, 0, IOPORT_IRQ_PIN_ALL+1);
+//     printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
+//             p->name, p->r_pin,
+//             p->name, p->r_ddr,
+//             p->name, p->r_port);
+
+       // allocate this module's IRQ
+       p->io.irq_count = IOPORT_IRQ_COUNT;
+       p->io.irq = avr_alloc_irq(avr, 0, p->io.irq_count);
+       p->io.irq_ioctl_get = AVR_IOCTL_IOPORT_GETIRQ(p->name);
        
        avr_register_io(avr, &p->io);
        avr_register_vector(avr, &p->pcint);
index c2daad5..04d0890 100644 (file)
@@ -28,11 +28,12 @@ enum {
        IOPORT_IRQ_PIN0 = 0,
        IOPORT_IRQ_PIN1,IOPORT_IRQ_PIN2,IOPORT_IRQ_PIN3,IOPORT_IRQ_PIN4,
        IOPORT_IRQ_PIN5,IOPORT_IRQ_PIN6,IOPORT_IRQ_PIN7,
-       IOPORT_IRQ_PIN_ALL
+       IOPORT_IRQ_PIN_ALL,
+       IOPORT_IRQ_COUNT
 };
 
-// add IOPORT_IRQ_PIN* to this to get the real IRQ
-#define AVR_IOCTL_IOPORT_GETIRQ AVR_IOCTL_DEF('i','o','g',0)
+// add port name (uppercase) to get the real IRQ
+#define AVR_IOCTL_IOPORT_GETIRQ(_name) AVR_IOCTL_DEF('i','o','g',(_name))
 
 typedef struct avr_ioport_t {
        avr_io_t        io;
@@ -43,8 +44,6 @@ typedef struct avr_ioport_t {
 
        avr_int_vector_t pcint; // PCINT vector
        uint8_t r_pcint;                // pcint 8 pins mask
-
-       avr_irq_t * irq;
 } avr_ioport_t;
 
 void avr_ioport_init(avr_t * avr, avr_ioport_t * port);
index 0cae169..e15176a 100644 (file)
@@ -39,7 +39,7 @@ typedef struct avr_spi_t {
        avr_regbit_t cpha;              // phase
        avr_regbit_t spr[4];    // clock divider
        
-       avr_int_vector_t spi;   // spi interupt
+       avr_int_vector_t spi;   // spi interrupt
 } avr_spi_t;
 
 void avr_spi_init(avr_t * avr, avr_spi_t * port);
index 93bcb9c..6cf20c0 100644 (file)
@@ -38,27 +38,11 @@ static void avr_timer8_run(avr_t * avr, avr_io_t * port)
                //      printf("timer a firea %d\n", p->compa_next);
                        fflush(stdout);
                        p->compa_next += p->compa_cycles;                                               
-                       avr_raise_interupt(avr, &p->compa);
+                       avr_raise_interrupt(avr, &p->compa);
                } 
        }
 }
 
-#if 0
-static uint8_t avr_timer8_read(struct avr_t * avr, uint8_t addr, void * param)
-{
-       avr_timer8_t * p = (avr_timer8_t *)param;
-       uint8_t v = avr->data[addr];
-
-       if (addr == p->r_pin) {
-               uint8_t v = avr->data[p->r_port];
-               avr->data[addr] = v;
-               // made to trigger potential watchpoints
-               v = avr_core_watch_read(avr, addr);
-               printf("** PIN%c(%02x) = %02x\n", p->name, addr, v);
-       }
-       return v;
-}
-#endif
 
 static void avr_timer8_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * param)
 {
@@ -75,6 +59,8 @@ static void avr_timer8_write(struct avr_t * avr, uint8_t addr, uint8_t v, void *
        uint8_t cs = avr_regbit_get_array(avr, p->cs, ARRAY_SIZE(p->cs));
        if (cs == 0) {
                printf("%s-%c clock turned off\n", __FUNCTION__, p->name);              
+               p->compa_cycles = 0;
+               return;
        }
        uint8_t mode = avr_regbit_get_array(avr, p->wgm, ARRAY_SIZE(p->wgm));
        uint8_t cs_div = p->cs_div[cs];
@@ -106,13 +92,12 @@ static     avr_io_t        _io = {
 void avr_timer8_init(avr_t * avr, avr_timer8_t * p)
 {
        p->io = _io;
-       printf("%s timer%c created\n", __FUNCTION__, p->name);
+//     printf("%s timer%c created\n", __FUNCTION__, p->name);
 
        avr_register_io(avr, &p->io);
-//     avr_register_vector(avr, &port->pcint);
+       avr_register_vector(avr, &p->compa);
 
        avr_register_io_write(avr, p->cs[0].reg, avr_timer8_write, p);
        avr_register_io_write(avr, p->r_ocra, avr_timer8_write, p);
        avr_register_io_write(avr, p->r_ocrb, avr_timer8_write, p);
-       //avr_register_io_read(avr, port->r_pin, avr_ioport_read, port);
 }
index f016721..e24609a 100644 (file)
 #include <stdio.h>
 #include "avr_uart.h"
 
+DEFINE_FIFO(uint8_t, uart_fifo, 128);
+
 static void avr_uart_run(avr_t * avr, avr_io_t * port)
 {
-//     printf("%s\n", __FUNCTION__);
+       avr_uart_t * p = (avr_uart_t *)port;
+       if (p->input_cycle_timer) {
+               p->input_cycle_timer--;
+               if (p->input_cycle_timer == 0) {
+                       if (avr_regbit_get(avr, p->rxen))
+                               avr_raise_interrupt(avr, &p->rxc);
+               }
+       }
 }
 
 static uint8_t avr_uart_read(struct avr_t * avr, uint8_t addr, void * param)
 {
-//     avr_uart_t * p = (avr_uart_t *)param;
-       uint8_t v = avr->data[addr];
-//     printf("** PIN%c = %02x\n", p->name, v);
+       avr_uart_t * p = (avr_uart_t *)param;
+
+       if (!avr_regbit_get(avr, p->rxen)) {
+               avr->data[addr] = 0;
+               // made to trigger potential watchpoints
+               avr_core_watch_read(avr, addr);
+               return 0;
+       }
+       uint8_t v = uart_fifo_read(&p->input);
+
+       avr->data[addr] = v;
+       // made to trigger potential watchpoints
+       v = avr_core_watch_read(avr, addr);
+       p->input_cycle_timer = uart_fifo_isempty(&p->input) ? 0 : 10;
        return v;
 }
 
@@ -47,9 +67,9 @@ static void avr_uart_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * p
        //      printf("UDR%c(%02x) = %02x\n", p->name, addr, v);
                avr_core_watch_write(avr, addr, v);
 
-               // if the interupts are not used, still raised the UDRE and TXC flaga
-               avr_raise_interupt(avr, &p->udrc);
-               avr_raise_interupt(avr, &p->txc);
+               // if the interrupts are not used, still raised the UDRE and TXC flaga
+               avr_raise_interrupt(avr, &p->udrc);
+               avr_raise_interrupt(avr, &p->txc);
 
                static char buf[128];
                static int l = 0;
@@ -59,12 +79,14 @@ static void avr_uart_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * p
                        l = 0;
                        printf("\e[32m%s\e[0m\n", buf);
                }
-       }
-       if (addr == p->r_ucsra) {
+               // tell other modules we are "outputing" a byte
+               if (avr_regbit_get(avr, p->txen))
+                       avr_raise_irq(avr, p->io.irq + UART_IRQ_OUTPUT, v);
+       } else {
                // get the bits before the write
                uint8_t udre = avr_regbit_get(avr, p->udrc.raised);
                uint8_t txc = avr_regbit_get(avr, p->txc.raised);
-               
+
                avr_core_watch_write(avr, addr, v);
 
                // if writing one to a one, clear bit
@@ -75,10 +97,28 @@ static void avr_uart_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * p
        }
 }
 
+void avr_uart_irq_input(avr_t * avr, struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_uart_t * p = (avr_uart_t *)param;
+
+       // check to see fi receiver is enabled
+       if (!avr_regbit_get(avr, p->rxen))
+               return;
+
+       uart_fifo_write(&p->input, value); // add to fifo
+       // raise interrupt, if it was not there
+       if (p->input_cycle_timer == 0)
+               p->input_cycle_timer = 10;      // random number, should be proportional to speed
+}
+
+
 void avr_uart_reset(avr_t * avr, struct avr_io_t *io)
 {
        avr_uart_t * p = (avr_uart_t *)io;
        avr_regbit_set(avr, p->udrc.raised);
+       avr_irq_register_notify(avr, p->io.irq + UART_IRQ_INPUT, avr_uart_irq_input, p);
+       p->input_cycle_timer = 0;
+       uart_fifo_reset(&p->input);
 }
 
 static avr_io_t        _io = {
@@ -92,11 +132,16 @@ void avr_uart_init(avr_t * avr, avr_uart_t * p)
        p->io = _io;
        avr_register_io(avr, &p->io);
 
-       printf("%s UART%c UDR=%02x\n", __FUNCTION__, p->name, p->r_udr);
+//     printf("%s UART%c UDR=%02x\n", __FUNCTION__, p->name, p->r_udr);
+
+       // allocate this module's IRQ
+       p->io.irq_count = UART_IRQ_COUNT;
+       p->io.irq = avr_alloc_irq(avr, 0, p->io.irq_count);
+       p->io.irq_ioctl_get = AVR_IOCTL_UART_GETIRQ(p->name);
 
        avr_register_io_write(avr, p->r_udr, avr_uart_write, p);
-       avr_register_io_write(avr, p->r_ucsra, avr_uart_write, p);
        avr_register_io_read(avr, p->r_udr, avr_uart_read, p);
 
+       avr_register_io_write(avr, p->r_ucsra, avr_uart_write, p);
 }
 
index dec4cee..9de90d2 100644 (file)
 
 #include "simavr.h"
 
+#include "fifo_declare.h"
+
+DECLARE_FIFO(uint8_t, uart_fifo, 128);
+
+enum {
+       UART_IRQ_INPUT = 0,
+       UART_IRQ_OUTPUT,
+       UART_IRQ_COUNT
+};
+
+// add port number to get the real IRQ
+#define AVR_IOCTL_UART_GETIRQ(_name) AVR_IOCTL_DEF('u','a','r',(_name))
+
 typedef struct avr_uart_t {
        avr_io_t        io;
        char name;
@@ -34,11 +47,17 @@ typedef struct avr_uart_t {
        uint8_t r_ucsrb;
        uint8_t r_ucsrc;
 
+       avr_regbit_t    rxen;           // receive enabled
+       avr_regbit_t    txen;           // transmit enable
+
        uint8_t r_ubrrl,r_ubrrh;
 
        avr_int_vector_t rxc;
        avr_int_vector_t txc;
        avr_int_vector_t udrc;  
+
+       uart_fifo_t     input;
+       uint16_t        input_cycle_timer;
 } avr_uart_t;
 
 void avr_uart_init(avr_t * avr, avr_uart_t * port);
diff --git a/simavr/sim/fifo_declare.h b/simavr/sim/fifo_declare.h
new file mode 100644 (file)
index 0000000..081fcf8
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+       fifo_declare.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * FIFO helpers, aka circular buffers
+ *
+ * these macros define accessory for fifos of any name and any size (power of two)
+ */
+
+#ifndef __FIFO_DECLARE__
+#define __FIFO_DECLARE__
+
+/*
+       doing a :
+       DEFINE_FIFO(uint8_t, myfifo, 128);
+
+       will declare :
+       enum : myfifo_overflow_f
+       type : myfifo_t
+       functions:
+               // write a byte into the fifo, return 1 if there was room, 0 if there wasn't
+               int myfifo_write(myfifo_t *c, uint8_t b);
+               // reads a byte from the fifo, return 0 if empty. Use myfifo_isempty() to check beforehand
+               uint8_t myfifo_read(myfifo_t *c);
+               int myfifo_isfull(myfifo_t *c);
+               int myfifo_isempty(myfifo_t *c);
+               // returns number of items to read now
+               uint16_t myfifo_get_read_size(myfifo_t *c);
+               // read item at offset o from read cursor, no cursor advance
+               uint8_t myfifo_read_at(myfifo_t *c, uint16_t o);
+               // write b at offset o compared to current write cursor, no cursor advance
+               void myfifo_write_at(myfifo_t *c, uint16_t o, uint8_t b);
+
+
+       To use the fifo, you must declare at least one :
+       myfifo_t fifo = FIFO_NULL;
+
+       while (!myfifo_isfull(&fifo))
+               myfifo_write(&fifo, 0xaa);
+       ....
+       while (!myfifo_isempty(&fifo))
+               b = myfifo_read(&fifo);
+ */
+
+#include <stdint.h>
+
+#if __AVR__
+#define FIFO_CURSOR_TYPE       uint8_t
+#define FIFO_BOOL_TYPE char
+#define FIFO_INLINE
+#endif
+#ifndef        FIFO_CURSOR_TYPE
+#define FIFO_CURSOR_TYPE       uint16_t
+#endif
+#ifndef        FIFO_BOOL_TYPE
+#define FIFO_BOOL_TYPE int
+#endif
+#ifndef        FIFO_INLINE
+#define FIFO_INLINE    inline
+#endif
+
+#define FIFO_NULL { {0}, 0, 0, 0 }
+
+#define DECLARE_FIFO(__type, __name, __size) \
+enum { __name##_overflow_f = (1 << 0) }; \
+typedef struct __name##_t {                    \
+       __type          buffer[__size];         \
+       volatile FIFO_CURSOR_TYPE       read;           \
+       volatile FIFO_CURSOR_TYPE       write;          \
+       volatile uint8_t        flags;          \
+} __name##_t
+
+#define DEFINE_FIFO(__type, __name, __size) \
+static FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\
+{\
+       FIFO_CURSOR_TYPE now = c->write;\
+       FIFO_CURSOR_TYPE next = (now + 1) & (__size-1);\
+       if (c->read != next) {  \
+               c->buffer[now] = b;\
+               c->write = next;\
+               return 1;\
+       }\
+       return 0;\
+}\
+static inline FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\
+{\
+       FIFO_CURSOR_TYPE next = (c->write + 1) & (__size-1);\
+       return c->read == next;\
+}\
+static inline FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\
+{\
+       return c->read == c->write;\
+}\
+static FIFO_INLINE __type __name##_read(__name##_t * c)\
+{\
+       if (c->read == c->write)\
+               return 0;\
+       FIFO_CURSOR_TYPE read = c->read;\
+       __type res = c->buffer[read];\
+       c->read = (read + 1) & (__size-1);\
+       return res;\
+}\
+static inline FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\
+{\
+       return c->write > c->read ? c->write - c->read : __size - 1 - c->read + c->write;\
+}\
+static inline void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       c->read = (c->read + o) & (__size-1);\
+}\
+static inline __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       return c->buffer[(c->read + o) & (__size-1)];\
+}\
+static inline void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\
+{\
+       c->buffer[(c->write + o) & (__size-1)] = b;\
+}\
+static inline void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
+{\
+       c->write = (c->write + o) & (__size-1);\
+}\
+static inline void __name##_reset(__name##_t *c)\
+{\
+       c->read = c->write = c->flags = 0;\
+}\
+struct __name##_t
+
+#endif
index 743979b..bdc1626 100644 (file)
@@ -28,7 +28,7 @@
 uint16_t avr_run_one(avr_t * avr);
 
 /*
- * These are for internal access to the stack (for interupts)
+ * These are for internal access to the stack (for interrupts)
  */
 uint16_t _avr_sp_get(avr_t * avr);
 void _avr_sp_set(avr_t * avr, uint16_t sp);
diff --git a/simavr/sim/sim_interrupts.c b/simavr/sim/sim_interrupts.c
new file mode 100644 (file)
index 0000000..545123e
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+       sim_interrupts.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "sim_interrupts.h"
+#include "sim_core.h"
+
+void avr_register_vector(avr_t *avr, avr_int_vector_t * vector)
+{
+       if (vector->vector)
+               avr->vector[vector->vector] = vector;
+}
+
+int avr_has_pending_interrupts(avr_t * avr)
+{
+       return avr->pending[0] || avr->pending[1];
+}
+
+int avr_is_interrupt_pending(avr_t * avr, avr_int_vector_t * vector)
+{
+       return avr->pending[vector->vector >> 5] & (1 << (vector->vector & 0x1f));
+}
+
+int avr_raise_interrupt(avr_t * avr, avr_int_vector_t * vector)
+{
+       if (!vector || !vector->vector)
+               return 0;
+//     printf("%s raising %d\n", __FUNCTION__, vector->vector);
+       // always mark the 'raised' flag to one, even if the interuot 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"
+       if (vector->raised.reg)
+               avr_regbit_set(avr, vector->raised);
+       if (vector->enable.reg) {
+               if (!avr_regbit_get(avr, vector->enable))
+                       return 0;
+       }
+       if (!avr_is_interrupt_pending(avr, vector)) {
+               if (!avr->pending_wait)
+                       avr->pending_wait = 2;          // latency on interrupts ??
+               avr->pending[vector->vector >> 5] |= (1 << (vector->vector & 0x1f));
+
+               if (avr->state != cpu_Running) {
+               //      printf("Waking CPU due to interrupt\n");
+                       avr->state = cpu_Running;       // in case we were sleeping
+               }
+       }
+       // return 'raised' even if it was already pending
+       return 1;
+}
+
+void avr_clear_interrupt(avr_t * avr, int v)
+{
+       avr_int_vector_t * vector = avr->vector[v];
+       avr->pending[v >> 5] &= ~(1 << (v & 0x1f));
+       if (!vector)
+               return;
+       printf("%s cleared %d\n", __FUNCTION__, vector->vector);
+       if (vector->raised.reg)
+               avr_regbit_clear(avr, vector->raised);
+}
+
+/*
+ * check wether interrupts are pending. I so, check if the interrupt "latency" is reached,
+ * and if so triggers the handlers and jump to the vector.
+ */
+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) {
+                               int done = 0;
+                               for (int bi = 0; bi < 2 && !done; bi++) if (avr->pending[bi]) {
+                                       for (int ii = 0; ii < 32 && !done; ii++)
+                                               if (avr->pending[bi] & (1 << ii)) {
+
+                                                       int v = (bi * 32) + ii; // vector
+
+                                               //      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);
+                                                       done++;
+                                                       break;
+                                               }
+                                       break;
+                               }
+                       }
+               } else
+                       avr->pending_wait = 2;  // for next one...
+       }
+}
+
diff --git a/simavr/sim/sim_interrupts.h b/simavr/sim/sim_interrupts.h
new file mode 100644 (file)
index 0000000..270b39d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+       sim_interrupts.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_INTERUPTS_H__
+#define __SIM_INTERUPTS_H__
+
+#include "simavr.h"
+
+// 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_int_vector_t;
+
+
+/*
+ * Interrupt Helper Functions
+ */
+// register an interrupt vector. It's only needed if you want to use the "r_raised" flags
+void avr_register_vector(avr_t *avr, avr_int_vector_t * vector);
+// raise an interrupt (if enabled). The interrupt is latched and will be called later
+// return non-zero if the interrupt was raised and is now pending
+int avr_raise_interrupt(avr_t * avr, avr_int_vector_t * vector);
+// return non-zero if the AVR core has any pending interrupts
+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);
+// called by the core at each cycle to check whether an interrupt is pending
+void avr_service_interrupts(avr_t * avr);
+
+#endif /* __SIM_INTERUPTS_H__ */
diff --git a/simavr/sim/sim_io.c b/simavr/sim/sim_io.c
new file mode 100644 (file)
index 0000000..d2bf62e
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+       sim_io.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "sim_io.h"
+
+int avr_ioctl(avr_t *avr, uint32_t ctl, void * io_param)
+{
+       avr_io_t * port = avr->io_port;
+       int res = -1;
+       while (port && res == -1) {
+               if (port->ioctl)
+                       res = port->ioctl(avr, port, ctl, io_param);
+               port = port->next;
+       }
+       return res;
+}
+
+void avr_register_io(avr_t *avr, avr_io_t * io)
+{
+       io->next = avr->io_port;
+       avr->io_port = io;
+}
+
+void avr_register_io_read(avr_t *avr, uint8_t addr, avr_io_read_t readp, void * param)
+{
+       avr->ior[AVR_DATA_TO_IO(addr)].param = param;
+       avr->ior[AVR_DATA_TO_IO(addr)].r = readp;
+}
+
+void avr_register_io_write(avr_t *avr, uint8_t addr, avr_io_write_t writep, void * param)
+{
+       avr->iow[AVR_DATA_TO_IO(addr)].param = param;
+       avr->iow[AVR_DATA_TO_IO(addr)].w = writep;
+}
+
+struct avr_irq_t * avr_io_getirq(avr_t * avr, uint32_t ctl, int index)
+{
+       avr_io_t * port = avr->io_port;
+       while (port) {
+               if (port->irq && port->irq_ioctl_get == ctl && port->irq_count > index)
+                       return port->irq + index;
+               port = port->next;
+       }
+       return NULL;
+       
+}
diff --git a/simavr/sim/sim_io.h b/simavr/sim/sim_io.h
new file mode 100644 (file)
index 0000000..770b3a8
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+       sim_io.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_IO_H__
+#define __SIM_IO_H__
+
+#include "simavr.h"
+
+/*
+ * used by the ioports to implement their own features
+ * see avr_eeprom.* for an example, and avr_ioctl().
+ */
+#define AVR_IOCTL_DEF(_a,_b,_c,_d) \
+       (((_a) << 24)|((_b) << 16)|((_c) << 8)|((_d)))
+
+/*
+ * IO module base struct
+ * Modules uses that as their first member in their own struct
+ */
+typedef struct avr_io_t {
+       struct avr_io_t * next;
+       const char *            kind;           // pretty name, for debug
+
+       uint32_t                        irq_ioctl_get;  // used to get irqs from this module
+       int                                     irq_count;      // number of (optional) irqs
+       struct avr_irq_t *      irq;            // optional external IRQs
+       // called at every instruction
+       void (*run)(avr_t * avr, struct avr_io_t *io);
+       // called at reset time
+       void (*reset)(avr_t * avr, struct avr_io_t *io);
+       // called externally. allow access to io modules and so on
+       int (*ioctl)(avr_t * avr, struct avr_io_t *io, uint32_t ctl, void *io_param);
+} avr_io_t;
+
+/*
+ * IO modules helper functions
+ */
+
+// registers an IO module, so it's run(), reset() etc are called
+// this is called by the AVR core init functions, you /could/ register an external
+// one after instanciation, for whatever purpose...
+void avr_register_io(avr_t *avr, avr_io_t * io);
+// register a callback for when IO register "addr" is read
+void avr_register_io_read(avr_t *avr, uint8_t addr, avr_io_read_t read, void * param);
+// register a callback for when the IO register is written. callback has to set the memory itself
+void avr_register_io_write(avr_t *avr, uint8_t addr, avr_io_write_t write, void * param);
+// call every IO modules until one responds to this
+int avr_ioctl(avr_t *avr, uint32_t ctl, void * io_param);
+// get the specific irq for a module, check AVR_IOCTL_IOPORT_GETIRQ for example
+struct avr_irq_t * avr_io_getirq(avr_t * avr, uint32_t ctl, int index);
+
+#endif /* __SIM_IO_H__ */
diff --git a/simavr/sim/sim_irq.c b/simavr/sim/sim_irq.c
new file mode 100644 (file)
index 0000000..b5db20e
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+       sim_irq.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "sim_irq.h"
+
+
+void avr_init_irq(avr_t * avr, avr_irq_t * irq, uint32_t base, uint32_t count)
+{
+       memset(irq, 0, sizeof(avr_irq_t) * count);
+
+       for (int i = 0; i < count; i++)
+               irq[i].irq = base + i;
+}
+
+avr_irq_t * avr_alloc_irq(avr_t * avr, uint32_t base, uint32_t count)
+{
+       avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
+       avr_init_irq(avr, irq, base, count);
+       return irq;
+}
+
+void avr_irq_register_notify(avr_t * avr, avr_irq_t * irq, avr_irq_notify_t notify, void * param)
+{
+       if (!irq || !notify)
+               return;
+       
+       avr_irq_hook_t *hook = irq->hook;
+       while (hook) {
+               if (hook->notify == notify && hook->param == param)
+                       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->notify = notify;
+       hook->param = param;
+       irq->hook = hook;
+}
+
+void avr_raise_irq(avr_t * avr, avr_irq_t * irq, uint32_t value)
+{
+       if (!irq || irq->value == value)
+               return ;
+       avr_irq_hook_t *hook = irq->hook;
+       while (hook) {
+               if (hook->notify) {
+                       if (hook->busy == 0) {
+                               hook->busy++;
+                               hook->notify(avr, irq, value, hook->param);
+                               hook->busy--;
+                       }
+               }
+               hook = hook->next;
+       }
+       irq->value = value;
+}
+
+static void _avr_irq_connect(avr_t * avr, avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_irq_t * dst = (avr_irq_t*)param;
+       avr_raise_irq(avr, dst, value);
+}
+
+void avr_connect_irq(avr_t * avr, avr_irq_t * src, avr_irq_t * dst)
+{
+       avr_irq_register_notify(avr, src, _avr_irq_connect, dst);
+}
diff --git a/simavr/sim/sim_irq.h b/simavr/sim/sim_irq.h
new file mode 100644 (file)
index 0000000..9349d30
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+       sim_irq.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_IRQ_H__
+#define __SIM_IRQ_H__
+
+#include "simavr.h"
+
+/*
+ * Internal IRQ system
+ * 
+ * This subsystem allow any piece of code to "register" a hook to be called when an IRQ is
+ * raised. The IRQ definition is up to the module defining it, for example a IOPORT pin change
+ * might be an IRQ in wich case any oiece of code can be notified when a pin has changed state
+ * 
+ * The notify hooks are chained, and duplicates are filtered out so you can't register a
+ * notify hook twice on one particylar IRQ
+ * 
+ * IRQ calling order is not defined, so don't rely on it.
+ * 
+ * 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)(avr_t * avr, struct avr_irq_t * irq, uint32_t value, void * param);
+
+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;
+} avr_irq_hook_t;
+
+typedef struct avr_irq_t {
+       uint32_t                        irq;
+       uint32_t                        value;
+       avr_irq_hook_t *        hook;
+} avr_irq_t;
+
+avr_irq_t * avr_alloc_irq(avr_t * avr, uint32_t base, uint32_t count);
+void avr_init_irq(avr_t * avr, avr_irq_t * irq, uint32_t base, uint32_t count);
+void avr_raise_irq(avr_t * avr, avr_irq_t * irq, uint32_t value);
+// this connects a "source" IRQ to a "destination" IRQ
+void avr_connect_irq(avr_t * avr, avr_irq_t * src, avr_irq_t * dst);
+void avr_irq_register_notify(avr_t * avr, avr_irq_t * irq, avr_irq_notify_t notify, void * param);
+
+#endif /* __SIM_IRQ_H__ */
diff --git a/simavr/sim/sim_regbit.h b/simavr/sim/sim_regbit.h
new file mode 100644 (file)
index 0000000..719dbba
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+       sim_regbit.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIM_REGBIT_H__
+#define __SIM_REGBIT_H__
+
+#include "simavr.h"
+
+#define ARRAY_SIZE(_aa) (sizeof(_aa) / sizeof((_aa)[0]))
+
+/*
+ * this 'structure' is a packed representation of an IO register 'bit'
+ * (or consecutive bits). This allows a way to set/get/clear them.
+ * gcc is happy passing these as register value, so you don't need to
+ * use a pointer when passing them along to functions.
+ */
+typedef struct avr_regbit_t {
+       unsigned long reg : 8, bit : 3, mask : 8;
+} avr_regbit_t;
+
+/*
+ * These accessors are inlined and are used to perform the operations on
+ * avr_regbit_t definitions. This is the "official" way to access bits into registers
+ * The small footorint costs brings much better versatility for functions/bits that are
+ * not always defined in the same place on real AVR cores
+ */
+/*
+ * set/get/clear io register bits in one operation
+ */
+static inline uint8_t avr_regbit_set(avr_t * avr, avr_regbit_t rb)
+{
+       uint8_t a = rb.reg;
+       if (!a)
+               return 0;
+       uint8_t m = rb.mask << rb.bit;
+       avr_core_watch_write(avr, a, avr->data[a] | m);
+       return (avr->data[a] >> rb.bit) & rb.mask;
+}
+
+static inline uint8_t avr_regbit_setto(avr_t * avr, avr_regbit_t rb, uint8_t v)
+{
+       uint8_t a = rb.reg;
+       if (!a)
+               return 0;
+       uint8_t m = rb.mask << rb.bit;
+       avr_core_watch_write(avr, a, (avr->data[a] & ~(m)) | ((v << rb.bit) & m));
+       return (avr->data[a] >> rb.bit) & rb.mask;
+}
+
+static inline uint8_t avr_regbit_get(avr_t * avr, avr_regbit_t rb)
+{
+       uint8_t a = rb.reg;
+       if (!a)
+               return 0;
+       //uint8_t m = rb.mask << rb.bit;
+       return (avr->data[a] >> rb.bit) & rb.mask;
+}
+
+static inline uint8_t avr_regbit_clear(avr_t * avr, avr_regbit_t rb)
+{
+       uint8_t a = (rb.reg);
+       uint8_t m = rb.mask << rb.bit;
+       avr_core_watch_write(avr, a, avr->data[a] & ~m);
+       return avr->data[a];
+}
+
+
+/*
+ * This reads the bits for an array of avr_regbit_t, make up a "byte" with them.
+ * This allows reading bits like CS0, CS1, CS2 etc even if they are not in the same
+ * physical IO register.
+ */
+static inline uint8_t avr_regbit_get_array(avr_t * avr, avr_regbit_t *rb, int count)
+{
+       uint8_t res = 0;
+
+       for (int i = 0; i < count; i++, rb++) if (rb->reg) {
+               uint8_t a = (rb->reg);
+               res |= ((avr->data[a] >> rb->bit) & rb->mask) << i;
+       }
+       return res;
+}
+
+#define AVR_IO_REGBIT(_io, _bit) { . reg = (_io), .bit = (_bit), .mask = 1 }
+#define AVR_IO_REGBITS(_io, _bit, _mask) { . reg = (_io), .bit = (_bit), .mask = (_mask) }
+
+
+#endif /* __SIM_REGBIT_H__ */
index 5181f27..ff253c9 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "sim_core.h"
 #include "avr_eeprom.h"
+#include "avr_uart.h"
 
 void hdump(const char *w, uint8_t *b, size_t l)
 {
@@ -91,153 +92,6 @@ void avr_reset(avr_t * avr)
 
 }
 
-int avr_ioctl(avr_t *avr, uint32_t ctl, void * io_param)
-{
-       avr_io_t * port = avr->io_port;
-       int res = -1;
-       while (port && res == -1) {
-               if (port->ioctl)
-                       res = port->ioctl(avr, port, ctl, io_param);
-               port = port->next;
-       }
-       return res;
-}
-
-void avr_register_io(avr_t *avr, avr_io_t * io)
-{
-       io->next = avr->io_port;
-       avr->io_port = io;
-}
-
-void avr_register_io_read(avr_t *avr, uint8_t addr, avr_io_read_t readp, void * param)
-{
-       avr->ior[AVR_DATA_TO_IO(addr)].param = param;
-       avr->ior[AVR_DATA_TO_IO(addr)].r = readp;
-}
-
-void avr_register_io_write(avr_t *avr, uint8_t addr, avr_io_write_t writep, void * param)
-{
-       avr->iow[AVR_DATA_TO_IO(addr)].param = param;
-       avr->iow[AVR_DATA_TO_IO(addr)].w = writep;
-}
-
-void avr_register_vector(avr_t *avr, avr_int_vector_t * vector)
-{
-       if (vector->vector)
-               avr->vector[vector->vector] = vector;
-}
-
-int avr_has_pending_interupts(avr_t * avr)
-{
-       return avr->pending[0] || avr->pending[1];
-}
-
-int avr_is_interupt_pending(avr_t * avr, avr_int_vector_t * vector)
-{
-       return avr->pending[vector->vector >> 5] & (1 << (vector->vector & 0x1f));
-}
-
-int avr_raise_interupt(avr_t * avr, avr_int_vector_t * vector)
-{
-       if (!vector || !vector->vector)
-               return 0;
-//     printf("%s raising %d\n", __FUNCTION__, vector->vector);
-       // always mark the 'raised' flag to one, even if the interuot is disabled
-       // this allow "pooling" for the "raised" flag, like for non-interupt
-       // driven UART and so so. These flags are often "write one to clear"
-       if (vector->raised.reg)
-               avr_regbit_set(avr, vector->raised);
-       if (vector->enable.reg) {
-               if (!avr_regbit_get(avr, vector->enable))
-                       return 0;
-       }
-       if (!avr_is_interupt_pending(avr, vector)) {
-               if (!avr->pending_wait)
-                       avr->pending_wait = 2;          // latency on interupts ??
-               avr->pending[vector->vector >> 5] |= (1 << (vector->vector & 0x1f));
-
-               if (avr->state != cpu_Running) {
-               //      printf("Waking CPU due to interrupt\n");
-                       avr->state = cpu_Running;       // in case we were sleeping
-               }
-       }
-       // return 'raised' even if it was already pending
-       return 1;
-}
-
-static void avr_clear_interupt(avr_t * avr, int v)
-{
-       avr_int_vector_t * vector = avr->vector[v];
-       avr->pending[v >> 5] &= ~(1 << (v & 0x1f));
-       if (!vector)
-               return;
-       printf("%s cleared %d\n", __FUNCTION__, vector->vector);
-       if (vector->raised.reg)
-               avr_regbit_clear(avr, vector->raised);
-}
-
-void avr_init_irq(avr_t * avr, avr_irq_t * irq, uint32_t base, uint32_t count)
-{
-       memset(irq, 0, sizeof(avr_irq_t) * count);
-
-       for (int i = 0; i < count; i++)
-               irq[i].irq = base + i;
-}
-
-avr_irq_t * avr_alloc_irq(avr_t * avr, uint32_t base, uint32_t count)
-{
-       avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
-       avr_init_irq(avr, irq, base, count);
-       return irq;
-}
-
-void avr_irq_register_notify(avr_t * avr, avr_irq_t * irq, avr_irq_notify_t notify, void * param)
-{
-       if (!irq || !notify)
-               return;
-       
-       avr_irq_hook_t *hook = irq->hook;
-       while (hook) {
-               if (hook->notify == notify && hook->param == param)
-                       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->notify = notify;
-       hook->param = param;
-       irq->hook = hook;
-}
-
-void avr_raise_irq(avr_t * avr, avr_irq_t * irq, uint32_t value)
-{
-       if (!irq || irq->value == value)
-               return ;
-       avr_irq_hook_t *hook = irq->hook;
-       while (hook) {
-               if (hook->notify) {
-                       if (hook->busy == 0) {
-                               hook->busy++;
-                               hook->notify(avr, irq, value, hook->param);
-                               hook->busy--;
-                       }
-               }
-               hook = hook->next;
-       }
-       irq->value = value;
-}
-
-static void _avr_irq_connect(avr_t * avr, avr_irq_t * irq, uint32_t value, void * param)
-{
-       avr_irq_t * dst = (avr_irq_t*)param;
-       avr_raise_irq(avr, dst, value != 0);
-}
-
-void avr_connect_irq(avr_t * avr, avr_irq_t * src, avr_irq_t * dst)
-{
-       avr_irq_register_notify(avr, src, _avr_irq_connect, dst);
-}
 
 void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address)
 {
@@ -279,43 +133,6 @@ uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr)
        return avr->data[addr];
 }
 
-/*
- * check wether interupts are pending. I so, check if the interupt "latency" is reached,
- * and if so triggers the handlers and jump to the vector.
- */
-static void avr_service_interupts(avr_t * avr)
-{
-       if (!avr->sreg[S_I])
-               return;
-
-       if (avr_has_pending_interupts(avr)) {
-               if (avr->pending_wait) {
-                       avr->pending_wait--;
-                       if (avr->pending_wait == 0) {
-                               int done = 0;
-                               for (int bi = 0; bi < 2 && !done; bi++) if (avr->pending[bi]) {
-                                       for (int ii = 0; ii < 32 && !done; ii++)
-                                               if (avr->pending[bi] & (1 << ii)) {
-
-                                                       int v = (bi * 32) + ii; // vector
-
-                                               //      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_interupt(avr, v);
-                                                       done++;
-                                                       break;
-                                               }
-                                       break;
-                               }
-                       }
-               } else
-                       avr->pending_wait = 2;  // for next one...
-       }
-}
-
 
 int avr_run(avr_t * avr)
 {
@@ -334,7 +151,7 @@ int avr_run(avr_t * avr)
        //SREG();
        // if we just re-enabled the interrupts...
        if (avr->sreg[S_I] && !(avr->data[R_SREG] & (1 << S_I))) {
-       //      printf("*** %s: Renabling interupts\n", __FUNCTION__);
+       //      printf("*** %s: Renabling interrupts\n", __FUNCTION__);
                avr->pending_wait++;
        }
        avr_io_t * port = avr->io_port;
@@ -348,7 +165,7 @@ int avr_run(avr_t * avr)
 
        if (avr->state == cpu_Sleeping) {
                if (!avr->sreg[S_I]) {
-                       printf("simavr: sleeping with interupts off, quitting gracefuly\n");
+                       printf("simavr: sleeping with interrupts off, quitting gracefuly\n");
                        exit(0);
                }
                usleep(500);
@@ -358,7 +175,7 @@ int avr_run(avr_t * avr)
        }
        // Interrupt servicing might change the PC too
        if (avr->state == cpu_Running || avr->state == cpu_Sleeping) {
-               avr_service_interupts(avr);
+               avr_service_interrupts(avr);
 
                avr->data[R_SREG] = 0;
                for (int i = 0; i < 8; i++)
@@ -463,6 +280,15 @@ int main(int argc, char *argv[])
        }
        avr->trace = trace;
 
+       // try to enable "local echo" on the first uart, for testing purposes
+       {
+               avr_irq_t * src = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUTPUT);
+               avr_irq_t * dst = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_INPUT);
+               printf("%s:%s activating uart local echo IRQ src %p dst %p\n", __FILE__, __FUNCTION__, src, dst);
+               if (src && dst)
+                       avr_connect_irq(avr, src, dst);
+       }
+
        for (long long i = 0; i < 8000000*10; i++)
 //     for (long long i = 0; i < 80000; i++)
                avr_run(avr);
index af67f78..61a7d03 100644 (file)
@@ -115,10 +115,10 @@ typedef struct avr_t {
        // queue of io modules
        struct avr_io_t *io_port;
 
-       // interupt vectors, and their enable/clear registers
+       // interrupt vectors, and their enable/clear registers
        struct avr_int_vector_t * vector[64];
        uint8_t         pending_wait;   // number of cycles to wait for pending
-       uint32_t        pending[2];             // pending interupts
+       uint32_t        pending[2];             // pending interrupts
 
        // DEBUG ONLY
        int             trace;
@@ -169,45 +169,6 @@ typedef struct avr_symbol_t {
        uint32_t        addr;
 } avr_symbol_t;
 
-/*
- * this 'structure' is a packed representation of an IO register 'bit'
- * (or consecutive bits). This allows a way to set/get/clear them.
- * gcc is happy passing these as register value, so you don't need to
- * use a pointer when passing them along to functions.
- */
-typedef struct avr_regbit_t {
-       unsigned long reg : 8, bit : 3, mask : 8;
-} avr_regbit_t;
-
-// interupt 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 "interupt enable" flag for this vector
-       avr_regbit_t raised;    // IO register index for the register where the "raised" flag is (optional)
-} avr_int_vector_t;
-
-/*
- * used by the ioports to implement their own features
- * see avr_eeprom.* for an example, and avr_ioctl().
- */
-#define AVR_IOCTL_DEF(_a,_b,_c,_d) \
-       (((_a) << 24)|((_b) << 16)|((_c) << 8)|((_d)))
-
-/*
- * IO module base struct
- * Modules uses that as their first member in their own struct
- */
-typedef struct avr_io_t {
-       struct avr_io_t * next;
-       const char * kind;
-       // called at every instruction
-       void (*run)(avr_t * avr, struct avr_io_t *io);
-       // called at reset time
-       void (*reset)(avr_t * avr, struct avr_io_t *io);
-       // called externally. allow access to io modules and so on
-       int (*ioctl)(avr_t * avr, struct avr_io_t *io, uint32_t ctl, void *io_param);
-} avr_io_t;
 
 // initializes a new AVR instance. Will call the IO registers init(), and then reset()
 int avr_init(avr_t * avr);
@@ -217,33 +178,6 @@ void avr_reset(avr_t * avr);
 // load code in the "flash"
 void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address);
 
-/*
- * IO modules helper functions
- */
-
-// registers an IO module, so it's run(), reset() etc are called
-// this is called by the AVR core init functions, you /could/ register an external
-// one after instanciation, for whatever purpose...
-void avr_register_io(avr_t *avr, avr_io_t * io);
-// register a callback for when IO register "addr" is read
-void avr_register_io_read(avr_t *avr, uint8_t addr, avr_io_read_t read, void * param);
-// register a callback for when the IO register is written. callback has to set the memory itself
-void avr_register_io_write(avr_t *avr, uint8_t addr, avr_io_write_t write, void * param);
-// call every IO modules until one responds to this
-int avr_ioctl(avr_t *avr, uint32_t ctl, void * io_param);
-
-/*
- * Interupt Helper Functions
- */
-// register an interupt vector. It's only needed if you want to use the "r_raised" flags
-void avr_register_vector(avr_t *avr, avr_int_vector_t * vector);
-// raise an interupt (if enabled). The interupt is latched and will be called later
-// return non-zero if the interupt was raised and is now pending
-int avr_raise_interupt(avr_t * avr, avr_int_vector_t * vector);
-// return non-zero if the AVR core has any pending interupts
-int avr_has_pending_interupts(avr_t * avr);
-// return nonzero if a soecific interupt vector is pending
-int avr_is_interupt_pending(avr_t * avr, avr_int_vector_t * vector);
 
 /*
  * these are accessors for avr->data but allows watchpoints to be set for gdb
@@ -254,112 +188,10 @@ void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v);
 uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr);
 
 
-/*
- * These accessors are inlined and are used to perform the operations on
- * avr_regbit_t definitions. This is the "official" way to access bits into registers
- * The small footorint costs brings much better versatility for functions/bits that are
- * not always defined in the same place on real AVR cores
- */
-/*
- * set/get/clear io register bits in one operation
- */
-static inline uint8_t avr_regbit_set(avr_t * avr, avr_regbit_t rb)
-{
-       uint8_t a = rb.reg;
-       if (!a)
-               return 0;
-       uint8_t m = rb.mask << rb.bit;
-       avr_core_watch_write(avr, a, avr->data[a] | m);
-       return (avr->data[a] >> rb.bit) & rb.mask;
-}
-
-static inline uint8_t avr_regbit_setto(avr_t * avr, avr_regbit_t rb, uint8_t v)
-{
-       uint8_t a = rb.reg;
-       if (!a)
-               return 0;
-       uint8_t m = rb.mask << rb.bit;
-       avr_core_watch_write(avr, a, (avr->data[a] & ~(m)) | ((v << rb.bit) & m));
-       return (avr->data[a] >> rb.bit) & rb.mask;
-}
-
-static inline uint8_t avr_regbit_get(avr_t * avr, avr_regbit_t rb)
-{
-       uint8_t a = rb.reg;
-       if (!a)
-               return 0;
-       //uint8_t m = rb.mask << rb.bit;
-       return (avr->data[a] >> rb.bit) & rb.mask;
-}
-
-static inline uint8_t avr_regbit_clear(avr_t * avr, avr_regbit_t rb)
-{
-       uint8_t a = (rb.reg);
-       uint8_t m = rb.mask << rb.bit;
-       avr_core_watch_write(avr, a, avr->data[a] & ~m);
-       return avr->data[a];
-}
-
-#define ARRAY_SIZE(_aa) (sizeof(_aa) / sizeof((_aa)[0]))
-
-/*
- * This reads the bits for an array of avr_regbit_t, make up a "byte" with them.
- * This allows reading bits like CS0, CS1, CS2 etc even if they are not in the same
- * physical IO register.
- */
-static inline uint8_t avr_regbit_get_array(avr_t * avr, avr_regbit_t *rb, int count)
-{
-       uint8_t res = 0;
-
-       for (int i = 0; i < count; i++, rb++) if (rb->reg) {
-               uint8_t a = (rb->reg);
-               res |= ((avr->data[a] >> rb->bit) & rb->mask) << i;
-       }
-       return res;
-}
-
-#define AVR_IO_REGBIT(_io, _bit) { . reg = (_io), .bit = (_bit), .mask = 1 }
-#define AVR_IO_REGBITS(_io, _bit, _mask) { . reg = (_io), .bit = (_bit), .mask = (_mask) }
-
-/*
- * Internal IRQ system
- * 
- * This subsystem allow any piece of code to "register" a hook to be called when an IRQ is
- * raised. The IRQ definition is up to the module defining it, for example a IOPORT pin change
- * might be an IRQ in wich case any oiece of code can be notified when a pin has changed state
- * 
- * The notify hooks are chained, and duplicates are filtered out so you can't register a
- * notify hook twice on one particylar IRQ
- * 
- * IRQ calling order is not defined, so don't rely on it.
- * 
- * 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)(avr_t * avr, struct avr_irq_t * irq, uint32_t value, void * param);
-
-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;
-} avr_irq_hook_t;
-
-typedef struct avr_irq_t {
-       uint32_t                        irq;
-       uint32_t                        value;
-       avr_irq_hook_t *        hook;
-} avr_irq_t;
-
-avr_irq_t * avr_alloc_irq(avr_t * avr, uint32_t base, uint32_t count);
-void avr_init_irq(avr_t * avr, avr_irq_t * irq, uint32_t base, uint32_t count);
-void avr_raise_irq(avr_t * avr, avr_irq_t * irq, uint32_t value);
-// this connects a "source" IRQ to a "destination" IRQ
-void avr_connect_irq(avr_t * avr, avr_irq_t * src, avr_irq_t * dst);
-void avr_irq_register_notify(avr_t * avr, avr_irq_t * irq, avr_irq_notify_t notify, void * param);
+#include "sim_io.h"
+#include "sim_regbit.h"
+#include "sim_interrupts.h"
+#include "sim_irq.h"
 
 #endif /*__SIMAVR_H__*/
 
diff --git a/tests/atmega48_disabled_timer.c b/tests/atmega48_disabled_timer.c
new file mode 100644 (file)
index 0000000..5d0b069
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * avrtest.c
+ *
+ *  Created on: 1 Dec 2009
+ *      Author: jone
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#include "avr_mcu_section.h"
+AVR_MCU(F_CPU, "atmega48");
+
+ISR(TIMER0_COMPA_vect)
+{
+       TCCR0B = 0;
+       TCNT0 = 0;
+}
+
+int main(void)
+{
+       // Set up timer0 - do not start yet
+       TCCR0A |= (1 << WGM01);                     // Configure timer 0 for CTC mode
+       TIMSK0 |= (1 << OCIE0A);                    // Enable CTC interrupt
+       OCR0A   = 0xAA;                             // CTC compare value
+
+       sei();                                      // Enable global interrupts
+
+       // here the interupts are enabled, but the interupt
+       // vector should not be called
+       while(1)
+               ;
+}
diff --git a/tests/atmega88_uart_echo.c b/tests/atmega88_uart_echo.c
new file mode 100644 (file)
index 0000000..4d7cccf
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+       atmega88_uart_echo.c
+
+       This test case enables uart RX interupts, does a "printf" and then receive characters
+       via the interupt handler until it reaches a \r.
+
+       This tests the uart reception fifo system. It relies on the uart "irq" input and output
+       to be wired together (see simavr.c)
+ */
+
+#include <avr/io.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <avr/eeprom.h>
+#include <avr/sleep.h>
+
+/*
+ * This demonstrate how to use the avr_mcu_section.h file
+ * The macro adds a section to the ELF file with useful
+ * information for the simulator
+ */
+#include "avr_mcu_section.h"
+AVR_MCU(F_CPU, "atmega88");
+
+
+static int uart_putchar(char c, FILE *stream) {
+  if (c == '\n')
+    uart_putchar('\r', stream);
+  loop_until_bit_is_set(UCSR0A, UDRE0);
+  UDR0 = c;
+  return 0;
+}
+
+static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
+                                         _FDEV_SETUP_WRITE);
+
+volatile uint8_t bindex = 0;
+uint8_t buffer[80];
+volatile uint8_t done = 0;
+
+ISR(USART_RX_vect)
+{
+       uint8_t b = UDR0;
+       buffer[bindex++] = b;
+       buffer[bindex] = 0;
+       if (b == '\r')
+               done++;
+}
+
+int main()
+{
+       stdout = &mystdout;
+
+       // enable receiver
+       UCSR0B |= (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0);
+
+       sei();
+       printf("Hey there, this should be received back\n");
+
+       while (!done)
+               sleep_cpu();
+
+       cli();
+       printf("Received: %s", buffer);
+
+       // this quits the simulator, since interupts are off
+       // this is a "feature" that allows running tests cases and exit
+       sleep_cpu();
+}