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"
2 "simavr.h"
F
"./simavr/sim/simavr.h"
+T
+2 "fifo_declare.h"
+F
+"./simavr/sim/fifo_declare.h"
F
T
1 "tests"
.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,
.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,
.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,
};
#endif /* SIM_CORENAME */
-#endif /* __SIM_MEGAX8_H__ */
\ No newline at end of file
+#endif /* __SIM_MEGAX8_H__ */
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);
}
}
}
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);
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);
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);
// 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);
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;
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);
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);
// 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)
{
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];
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);
}
#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;
}
// 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;
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
}
}
+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 = {
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);
}
#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;
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);
--- /dev/null
+/*
+ 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
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);
--- /dev/null
+/*
+ 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...
+ }
+}
+
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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;
+
+}
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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__ */
#include "sim_core.h"
#include "avr_eeprom.h"
+#include "avr_uart.h"
void hdump(const char *w, uint8_t *b, size_t l)
{
}
-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)
{
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)
{
//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;
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);
}
// 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++)
}
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);
// 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;
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);
// 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
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__*/
--- /dev/null
+/*
+ * 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)
+ ;
+}
--- /dev/null
+/*
+ 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();
+}