From 0b698ce0c4148375fba91ab0295e5624067f3cac Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Tue, 1 Dec 2009 21:47:48 +0000 Subject: [PATCH] uart, ioports, etc. Many more changes + 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 --- .simavr.jcc | 32 +++++ simavr/cores/sim_mega644.c | 6 + simavr/cores/sim_megax8.h | 5 +- simavr/sim/avr_eeprom.c | 8 +- simavr/sim/avr_ioport.c | 55 ++++----- simavr/sim/avr_ioport.h | 9 +- simavr/sim/avr_spi.h | 2 +- simavr/sim/avr_timer8.c | 25 +--- simavr/sim/avr_uart.c | 69 +++++++++-- simavr/sim/avr_uart.h | 19 +++ simavr/sim/fifo_declare.h | 147 +++++++++++++++++++++++ simavr/sim/sim_core.h | 2 +- simavr/sim/sim_interrupts.c | 120 +++++++++++++++++++ simavr/sim/sim_interrupts.h | 53 +++++++++ simavr/sim/sim_io.c | 68 +++++++++++ simavr/sim/sim_io.h | 70 +++++++++++ simavr/sim/sim_irq.c | 89 ++++++++++++++ simavr/sim/sim_irq.h | 67 +++++++++++ simavr/sim/sim_regbit.h | 106 +++++++++++++++++ simavr/sim/simavr.c | 200 +++----------------------------- simavr/sim/simavr.h | 180 +--------------------------- tests/atmega48_disabled_timer.c | 33 ++++++ tests/atmega88_uart_echo.c | 69 +++++++++++ 23 files changed, 998 insertions(+), 436 deletions(-) create mode 100644 simavr/sim/fifo_declare.h create mode 100644 simavr/sim/sim_interrupts.c create mode 100644 simavr/sim/sim_interrupts.h create mode 100644 simavr/sim/sim_io.c create mode 100644 simavr/sim/sim_io.h create mode 100644 simavr/sim/sim_irq.c create mode 100644 simavr/sim/sim_irq.h create mode 100644 simavr/sim/sim_regbit.h create mode 100644 tests/atmega48_disabled_timer.c create mode 100644 tests/atmega88_uart_echo.c diff --git a/.simavr.jcc b/.simavr.jcc index 640d586..b69769e 100644 --- a/.simavr.jcc +++ b/.simavr.jcc @@ -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" diff --git a/simavr/cores/sim_mega644.c b/simavr/cores/sim_mega644.c index a063ca2..22b439d 100644 --- a/simavr/cores/sim_mega644.c +++ b/simavr/cores/sim_mega644.c @@ -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, diff --git a/simavr/cores/sim_megax8.h b/simavr/cores/sim_megax8.h index 682b2c4..8ea2bd3 100644 --- a/simavr/cores/sim_megax8.h +++ b/simavr/cores/sim_megax8.h @@ -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__ */ diff --git a/simavr/sim/avr_eeprom.c b/simavr/sim/avr_eeprom.c index 4045027..b6ff5a1 100644 --- a/simavr/sim/avr_eeprom.c +++ b/simavr/sim/avr_eeprom.c @@ -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); diff --git a/simavr/sim/avr_ioport.c b/simavr/sim/avr_ioport.c index 2bb4483..7394c0c 100644 --- a/simavr/sim/avr_ioport.c +++ b/simavr/sim/avr_ioport.c @@ -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); diff --git a/simavr/sim/avr_ioport.h b/simavr/sim/avr_ioport.h index c2daad5..04d0890 100644 --- a/simavr/sim/avr_ioport.h +++ b/simavr/sim/avr_ioport.h @@ -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); diff --git a/simavr/sim/avr_spi.h b/simavr/sim/avr_spi.h index 0cae169..e15176a 100644 --- a/simavr/sim/avr_spi.h +++ b/simavr/sim/avr_spi.h @@ -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); diff --git a/simavr/sim/avr_timer8.c b/simavr/sim/avr_timer8.c index 93bcb9c..6cf20c0 100644 --- a/simavr/sim/avr_timer8.c +++ b/simavr/sim/avr_timer8.c @@ -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); } diff --git a/simavr/sim/avr_uart.c b/simavr/sim/avr_uart.c index f016721..e24609a 100644 --- a/simavr/sim/avr_uart.c +++ b/simavr/sim/avr_uart.c @@ -26,16 +26,36 @@ #include #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); } diff --git a/simavr/sim/avr_uart.h b/simavr/sim/avr_uart.h index dec4cee..9de90d2 100644 --- a/simavr/sim/avr_uart.h +++ b/simavr/sim/avr_uart.h @@ -24,6 +24,19 @@ #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 index 0000000..081fcf8 --- /dev/null +++ b/simavr/sim/fifo_declare.h @@ -0,0 +1,147 @@ +/* + fifo_declare.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +/* + * 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 + +#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 diff --git a/simavr/sim/sim_core.h b/simavr/sim/sim_core.h index 743979b..bdc1626 100644 --- a/simavr/sim/sim_core.h +++ b/simavr/sim/sim_core.h @@ -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 index 0000000..545123e --- /dev/null +++ b/simavr/sim/sim_interrupts.c @@ -0,0 +1,120 @@ +/* + sim_interrupts.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#include +#include +#include +#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 index 0000000..270b39d --- /dev/null +++ b/simavr/sim/sim_interrupts.h @@ -0,0 +1,53 @@ +/* + sim_interrupts.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#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 index 0000000..d2bf62e --- /dev/null +++ b/simavr/sim/sim_io.c @@ -0,0 +1,68 @@ +/* + sim_io.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + + +#include +#include +#include +#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 index 0000000..770b3a8 --- /dev/null +++ b/simavr/sim/sim_io.h @@ -0,0 +1,70 @@ +/* + sim_io.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#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 index 0000000..b5db20e --- /dev/null +++ b/simavr/sim/sim_irq.c @@ -0,0 +1,89 @@ +/* + sim_irq.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#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 index 0000000..9349d30 --- /dev/null +++ b/simavr/sim/sim_irq.h @@ -0,0 +1,67 @@ +/* + sim_irq.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#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 index 0000000..719dbba --- /dev/null +++ b/simavr/sim/sim_regbit.h @@ -0,0 +1,106 @@ +/* + sim_regbit.h + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#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__ */ diff --git a/simavr/sim/simavr.c b/simavr/sim/simavr.c index 5181f27..ff253c9 100644 --- a/simavr/sim/simavr.c +++ b/simavr/sim/simavr.c @@ -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); diff --git a/simavr/sim/simavr.h b/simavr/sim/simavr.h index af67f78..61a7d03 100644 --- a/simavr/sim/simavr.h +++ b/simavr/sim/simavr.h @@ -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 index 0000000..5d0b069 --- /dev/null +++ b/tests/atmega48_disabled_timer.c @@ -0,0 +1,33 @@ +/* + * avrtest.c + * + * Created on: 1 Dec 2009 + * Author: jone + */ + +#include +#include + +#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 index 0000000..4d7cccf --- /dev/null +++ b/tests/atmega88_uart_echo.c @@ -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 +#include +#include +#include +#include + +/* + * 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(); +} -- 2.39.5