From: Michel Pollet Date: Wed, 2 Dec 2009 21:50:09 +0000 (+0000) Subject: GDB working, some more source massaging X-Git-Tag: v1.0a1~73 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=24c5c6069017010fd7d27eda7585e38b5fff7a4b;p=sx%2Fsimavr.git GDB working, some more source massaging Big news is gdb support, you can trace, breakpoint, resume, inspect (including eeprom addresses!). You can't modify variables on the fly yet. It's not very fast rignt now, but some very obvious changes will help that a lot. Other changes are more moving, shuffling. "simavr" is gone, replaced by a simple "run_avr" that does the same, but no longer has any emulation specific code. Signed-off-by: Michel Pollet --- diff --git a/.gitignore b/.gitignore index b3de6c0..543ae13 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ obj *.hex *.s simavr/simavr +simavr/run_avr diff --git a/.simavr.jcc b/.simavr.jcc index b69769e..d7a4a41 100644 --- a/.simavr.jcc +++ b/.simavr.jcc @@ -100,17 +100,21 @@ T F "./simavr/sim/sim_io.h" T -2 "simavr.c" +2 "sim_avr.c" F -"./simavr/sim/simavr.c" +"./simavr/sim/sim_avr.c" T -2 "simavr.h" +2 "sim_avr.h" F -"./simavr/sim/simavr.h" +"./simavr/sim/sim_avr.h" T 2 "fifo_declare.h" F "./simavr/sim/fifo_declare.h" +T +2 "run_avr.c" +F +"./simavr/sim/run_avr.c" F T 1 "tests" @@ -118,6 +122,14 @@ T 2 "atmega88_example.c" F "./tests/atmega88_example.c" +T +2 "atmega48_disabled_timer.c" +F +"./tests/atmega48_disabled_timer.c" +T +2 "atmega88_uart_echo.c" +F +"./tests/atmega88_uart_echo.c" F T 1 "cores" diff --git a/simavr/Makefile b/simavr/Makefile index 6138d99..3625912 100644 --- a/simavr/Makefile +++ b/simavr/Makefile @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with simavr. If not, see . -target = simavr +target = run_avr ifeq (${shell uname}, Darwin) AVR_ROOT := "/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/avr-4/" @@ -44,7 +44,7 @@ IPATH += /opt/local/include CFLAGS += ${patsubst %,-I%,${subst :, ,${IPATH}}} LFLAGS = -L/opt/local/lib/ -LDFLAGS += -lelf +LDFLAGS += -lelf -lpthread all: obj ${target} diff --git a/simavr/cores/sim_mega168.c b/simavr/cores/sim_mega168.c index 54d8bcf..8d0f2e4 100644 --- a/simavr/cores/sim_mega168.c +++ b/simavr/cores/sim_mega168.c @@ -19,7 +19,7 @@ along with simavr. If not, see . */ -#include "simavr.h" +#include "sim_avr.h" #define SIM_VECTOR_SIZE 4 #define SIM_MMCU "atmega168" diff --git a/simavr/cores/sim_mega48.c b/simavr/cores/sim_mega48.c index b0b0c95..08771af 100644 --- a/simavr/cores/sim_mega48.c +++ b/simavr/cores/sim_mega48.c @@ -19,7 +19,7 @@ along with simavr. If not, see . */ -#include "simavr.h" +#include "sim_avr.h" #define SIM_VECTOR_SIZE 2 #define SIM_MMCU "atmega48" diff --git a/simavr/cores/sim_mega644.c b/simavr/cores/sim_mega644.c index 22b439d..f240a2f 100644 --- a/simavr/cores/sim_mega644.c +++ b/simavr/cores/sim_mega644.c @@ -20,7 +20,7 @@ */ #include -#include "simavr.h" +#include "sim_avr.h" #include "sim_core_declare.h" #include "avr_eeprom.h" #include "avr_ioport.h" diff --git a/simavr/cores/sim_mega88.c b/simavr/cores/sim_mega88.c index c5e43cb..59bc480 100644 --- a/simavr/cores/sim_mega88.c +++ b/simavr/cores/sim_mega88.c @@ -19,7 +19,7 @@ along with simavr. If not, see . */ -#include "simavr.h" +#include "sim_avr.h" #define SIM_VECTOR_SIZE 2 #define SIM_MMCU "atmega88" diff --git a/simavr/cores/sim_megax8.c b/simavr/cores/sim_megax8.c index 44bf334..7b1f293 100644 --- a/simavr/cores/sim_megax8.c +++ b/simavr/cores/sim_megax8.c @@ -19,7 +19,7 @@ along with simavr. If not, see . */ #include -#include "simavr.h" +#include "sim_avr.h" #include "sim_megax8.h" diff --git a/simavr/cores/sim_megax8.h b/simavr/cores/sim_megax8.h index 8ea2bd3..97eb61e 100644 --- a/simavr/cores/sim_megax8.h +++ b/simavr/cores/sim_megax8.h @@ -181,11 +181,13 @@ struct mcu_t SIM_CORENAME = { .spi = { .disabled = AVR_IO_REGBIT(PRR,PRSPI), + + .r_spdr = SPDR, + .r_spcr = SPCR, + .r_spsr = SPSR, + .spe = AVR_IO_REGBIT(SPCR, SPE), - .dord = AVR_IO_REGBIT(SPCR, DORD), .mstr = AVR_IO_REGBIT(SPCR, MSTR), - .cpol = AVR_IO_REGBIT(SPCR, CPOL), - .cpha = AVR_IO_REGBIT(SPCR, CPHA), .spr = { AVR_IO_REGBIT(SPCR, SPR0), AVR_IO_REGBIT(SPCR, SPR1), AVR_IO_REGBIT(SPSR, SPI2X) }, .spi = { diff --git a/simavr/cores/sim_tiny85.c b/simavr/cores/sim_tiny85.c index e4d7af3..eec13a6 100644 --- a/simavr/cores/sim_tiny85.c +++ b/simavr/cores/sim_tiny85.c @@ -20,7 +20,7 @@ */ #include -#include "simavr.h" +#include "sim_avr.h" #include "sim_core_declare.h" #include "avr_eeprom.h" #include "avr_ioport.h" diff --git a/simavr/sim/avr_eeprom.c b/simavr/sim/avr_eeprom.c index b6ff5a1..53c82f0 100644 --- a/simavr/sim/avr_eeprom.c +++ b/simavr/sim/avr_eeprom.c @@ -93,6 +93,18 @@ static int avr_eeprom_ioctl(avr_t * avr, avr_io_t * port, uint32_t ctl, void * i printf("%s: AVR_IOCTL_EEPROM_SET Loaded %d at offset %d\n", __FUNCTION__, desc->size, desc->offset); } break; + case AVR_IOCTL_EEPROM_GET: { + avr_eeprom_desc_t * desc = (avr_eeprom_desc_t*)io_param; + if (!desc || (desc->offset + desc->size) >= p->size) { + printf("%s: AVR_IOCTL_EEPROM_GET Invalid argument\n", + __FUNCTION__); + return -2; + } + if (desc->ee) + memcpy(desc->ee, p->eeprom + desc->offset, desc->size); + else // allow to get access to the read data, for gdb support + desc->ee = p->eeprom + desc->offset; + } break; } return res; diff --git a/simavr/sim/avr_eeprom.h b/simavr/sim/avr_eeprom.h index c7f1dcd..89678fe 100644 --- a/simavr/sim/avr_eeprom.h +++ b/simavr/sim/avr_eeprom.h @@ -22,7 +22,7 @@ #ifndef __AVR_EEPROM_H__ #define __AVR_EEPROM_H__ -#include "simavr.h" +#include "sim_avr.h" typedef struct avr_eeprom_t { avr_io_t io; diff --git a/simavr/sim/avr_ioport.h b/simavr/sim/avr_ioport.h index 04d0890..0a1ca37 100644 --- a/simavr/sim/avr_ioport.h +++ b/simavr/sim/avr_ioport.h @@ -22,7 +22,7 @@ #ifndef __AVR_IOPORT_H__ #define __AVR_IOPORT_H__ -#include "simavr.h" +#include "sim_avr.h" enum { IOPORT_IRQ_PIN0 = 0, diff --git a/simavr/sim/avr_spi.c b/simavr/sim/avr_spi.c index b373e8e..01b0d7f 100644 --- a/simavr/sim/avr_spi.c +++ b/simavr/sim/avr_spi.c @@ -27,7 +27,6 @@ static void avr_spi_run(avr_t * avr, avr_io_t * port) // printf("%s\n", __FUNCTION__); } -#if 0 static uint8_t avr_spi_read(struct avr_t * avr, uint8_t addr, void * param) { avr_spi_t * p = (avr_spi_t *)param; @@ -40,27 +39,27 @@ static void avr_spi_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * pa { avr_spi_t * p = (avr_spi_t *)param; - if (addr == p->r_udr) { + if (addr == p->r_spdr) { // printf("UDR%c(%02x) = %02x\n", p->name, addr, v); avr_core_watch_write(avr, addr, v); - avr_regbit_set(avr, p->udre); - - static char buf[128]; - static int l = 0; - buf[l++] = v <= ' ' ? '.' : v; - buf[l] = 0; - if (v == '\n' || l == 127) { - l = 0; - printf("\e[32m%s\e[0m\n", buf); - } } } -#endif + +static void avr_spi_irq_input(avr_t * avr, struct avr_irq_t * irq, uint32_t value, void * param) +{ + avr_spi_t * p = (avr_spi_t *)param; + + // check to see fi receiver is enabled + if (!avr_regbit_get(avr, p->spe)) + return; + + // double buffer the input.. ? +} void avr_spi_reset(avr_t * avr, struct avr_io_t *io) { -// avr_spi_t * p = (avr_spi_t *)io; -// avr_regbit_set(avr, p->udre); + avr_spi_t * p = (avr_spi_t *)io; + avr_irq_register_notify(avr, p->io.irq + SPI_IRQ_INPUT, avr_spi_irq_input, p); } static avr_io_t _io = { @@ -76,7 +75,12 @@ void avr_spi_init(avr_t * avr, avr_spi_t * p) printf("%s SPI%c init\n", __FUNCTION__, p->name); -// avr_register_io_write(avr, p->r_udr, avr_spi_write, p); -// avr_register_io_read(avr, p->r_udr, avr_spi_read, p); + // allocate this module's IRQ + p->io.irq_count = SPI_IRQ_COUNT; + p->io.irq = avr_alloc_irq(avr, 0, p->io.irq_count); + p->io.irq_ioctl_get = AVR_IOCTL_SPI_GETIRQ(p->name); + + avr_register_io_write(avr, p->r_spdr, avr_spi_write, p); + avr_register_io_read(avr, p->r_spdr, avr_spi_read, p); } diff --git a/simavr/sim/avr_spi.h b/simavr/sim/avr_spi.h index e15176a..2c24e52 100644 --- a/simavr/sim/avr_spi.h +++ b/simavr/sim/avr_spi.h @@ -22,7 +22,16 @@ #ifndef AVR_SPI_H_ #define AVR_SPI_H_ -#include "simavr.h" +#include "sim_avr.h" + +enum { + SPI_IRQ_INPUT = 0, + SPI_IRQ_OUTPUT, + SPI_IRQ_COUNT +}; + +// add port number to get the real IRQ +#define AVR_IOCTL_SPI_GETIRQ(_name) AVR_IOCTL_DEF('s','p','i',(_name)) typedef struct avr_spi_t { avr_io_t io; @@ -31,12 +40,10 @@ typedef struct avr_spi_t { uint8_t r_spdr; // data register uint8_t r_spcr; // control register + uint8_t r_spsr; // status register avr_regbit_t spe; // spi enable - avr_regbit_t dord; // data order avr_regbit_t mstr; // master/slave - avr_regbit_t cpol; // clock polarity - avr_regbit_t cpha; // phase avr_regbit_t spr[4]; // clock divider avr_int_vector_t spi; // spi interrupt diff --git a/simavr/sim/avr_timer8.h b/simavr/sim/avr_timer8.h index b73a831..1f3ecca 100644 --- a/simavr/sim/avr_timer8.h +++ b/simavr/sim/avr_timer8.h @@ -22,7 +22,7 @@ #ifndef AVR_TIMER8_H_ #define AVR_TIMER8_H_ -#include "simavr.h" +#include "sim_avr.h" typedef struct avr_timer8_t { avr_io_t io; diff --git a/simavr/sim/avr_uart.c b/simavr/sim/avr_uart.c index e24609a..35b8b0d 100644 --- a/simavr/sim/avr_uart.c +++ b/simavr/sim/avr_uart.c @@ -97,7 +97,7 @@ 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) +static 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; diff --git a/simavr/sim/avr_uart.h b/simavr/sim/avr_uart.h index 9de90d2..81b4e13 100644 --- a/simavr/sim/avr_uart.h +++ b/simavr/sim/avr_uart.h @@ -22,7 +22,7 @@ #ifndef AVR_UART_H_ #define AVR_UART_H_ -#include "simavr.h" +#include "sim_avr.h" #include "fifo_declare.h" diff --git a/simavr/sim/run_avr.c b/simavr/sim/run_avr.c new file mode 100644 index 0000000..4560033 --- /dev/null +++ b/simavr/sim/run_avr.c @@ -0,0 +1,148 @@ +/* + run_avr.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 +#include "sim_avr.h" +#include "sim_elf.h" +#include "sim_core.h" +#include "sim_gdb.h" +#include "avr_eeprom.h" +#include "avr_uart.h" + +void hdump(const char *w, uint8_t *b, size_t l) +{ + uint32_t i; + if (l < 16) { + printf("%s: ",w); + for (i = 0; i < l; i++) printf("%02x",b[i]); + } else { + printf("%s:\n",w); + for (i = 0; i < l; i++) { + if (!(i & 0x1f)) printf(" "); + printf("%02x",b[i]); + if ((i & 0x1f) == 0x1f) { + printf(" "); + printf("\n"); + } + } + } + printf("\n"); +} + + +void display_usage() +{ + printf("usage: simavr [-t] [-g] [-m ] [-f ] firmware\n"); + printf(" -t: run full scale decoder trace\n"); + printf(" -g: listen for gdb connection on port 1234\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + elf_firmware_t f; + long f_cpu = 0; + int trace = 0; + int gdb = 0; + char name[16] = ""; + int option_count; + int option_index = 0; + + struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"mcu", required_argument, 0, 'm'}, + {"freq", required_argument, 0, 'f'}, + {"trace", no_argument, 0, 't'}, + {"gdb", no_argument, 0, 'g'}, + {0, 0, 0, 0} + }; + + if (argc == 1) + display_usage(); + + while ((option_count = getopt_long(argc, argv, "tghm:f:", long_options, &option_index)) != -1) { + switch (option_count) { + case 'h': + display_usage(); + break; + case 'm': + strcpy(name, optarg); + break; + case 'f': + f_cpu = atoi(optarg); + break; + case 't': + trace++; + break; + case 'g': + gdb++; + break; + } + } + + elf_read_firmware(argv[argc-1], &f); + + if (strlen(name)) + strcpy(f.mmcu.name, name); + if (f_cpu) + f.mmcu.f_cpu = f_cpu; + + printf("firmware %s f=%d mmcu=%s\n", argv[argc-1], (int)f.mmcu.f_cpu, f.mmcu.name); + + avr_t * avr = avr_make_mcu_by_name(f.mmcu.name); + if (!avr) { + fprintf(stderr, "%s: AVR '%s' now known\n", argv[0], f.mmcu.name); + exit(1); + } + avr_init(avr); + avr->frequency = f.mmcu.f_cpu; + avr->codeline = f.codeline; + avr_loadcode(avr, f.flash, f.flashsize, 0); + avr->codeend = f.flashsize - f.datasize; + if (f.eeprom && f.eesize) { + avr_eeprom_desc_t d = { .ee = f.eeprom, .offset = 0, .size = f.eesize }; + avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &d); + } + 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); + } + + if (gdb) { + avr->state = cpu_Stopped; + avr_gdb_init(avr); + } + +// for (long long i = 0; i < 8000000*10; i++) +// for (long long i = 0; i < 80000; i++) + for (;;) + avr_run(avr); + +} diff --git a/simavr/sim/sim_avr.c b/simavr/sim/sim_avr.c new file mode 100644 index 0000000..16a908a --- /dev/null +++ b/simavr/sim/sim_avr.c @@ -0,0 +1,209 @@ +/* + sim_avr.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 +#include "sim_avr.h" +#include "sim_core.h" +#include "sim_gdb.h" + + +int avr_init(avr_t * avr) +{ + avr->flash = malloc(avr->flashend + 1); + memset(avr->flash, 0xff, avr->flashend + 1); + avr->data = malloc(avr->ramend + 1); + memset(avr->data, 0, avr->ramend + 1); + + // cpu is in limbo before init is finished. + avr->state = cpu_Limbo; + avr->frequency = 1000000; // can be overriden via avr_mcu_section + if (avr->init) + avr->init(avr); + avr->state = cpu_Running; + avr_reset(avr); + return 0; +} + +void avr_reset(avr_t * avr) +{ + memset(avr->data, 0x0, avr->ramend + 1); + _avr_sp_set(avr, avr->ramend); + avr->pc = 0; + for (int i = 0; i < 8; i++) + avr->sreg[i] = 0; + if (avr->reset) + avr->reset(avr); + + avr_io_t * port = avr->io_port; + while (port) { + if (port->reset) + port->reset(avr, port); + port = port->next; + } +} + + +void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address) +{ + memcpy(avr->flash + address, code, size); +} + +void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v) +{ + if (addr > avr->ramend) { + printf("*** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x out of ram\n", + avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, v); + CRASH(); + } + if (addr < 32) { + printf("*** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x low registers\n", + avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, v); + CRASH(); + } +#if AVR_STACK_WATCH + /* + * this checks that the current "function" is not doctoring the stack frame that is located + * higher on the stack than it should be. It's a sign of code that has overrun it's stack + * frame and is munching on it's own return address. + */ + if (avr->stack_frame_index > 1 && addr > avr->stack_frame[avr->stack_frame_index-2].sp) { + printf("\e[31m%04x : munching stack SP %04x, A=%04x <= %02x\e[0m\n", avr->pc, _avr_sp_get(avr), addr, v); + } +#endif + avr->data[addr] = v; +} + +uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr) +{ + if (addr > avr->ramend) { + printf("*** Invalid read address PC=%04x SP=%04x O=%04x Address %04x out of ram (%04x)\n", + avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, avr->ramend); + CRASH(); + } + return avr->data[addr]; +} + + +int avr_run(avr_t * avr) +{ + avr_gdb_processor(avr); + + if (avr->state == cpu_Stopped) { + usleep(500); + return avr->state; + } + + int step = avr->state == cpu_Step; + if (step) { + avr->state = cpu_Running; + } + + uint16_t new_pc = avr->pc; + + if (avr->state == cpu_Running) { + new_pc = avr_run_one(avr); + avr_dump_state(avr); + } else + avr->cycle ++; + + // re-synth the SREG + //SREG(); + // if we just re-enabled the interrupts... + if (avr->sreg[S_I] && !(avr->data[R_SREG] & (1 << S_I))) { + // printf("*** %s: Renabling interrupts\n", __FUNCTION__); + avr->pending_wait++; + } + avr_io_t * port = avr->io_port; + while (port) { + if (port->run) + port->run(avr, port); + port = port->next; + } + + avr->pc = new_pc; + + if (avr->state == cpu_Sleeping) { + if (!avr->sreg[S_I]) { + printf("simavr: sleeping with interrupts off, quitting gracefuly\n"); + exit(0); + } + usleep(500); + long sleep = (float)avr->frequency * (1.0f / 500.0f); + avr->cycle += sleep; + // avr->state = cpu_Running; + } + // Interrupt servicing might change the PC too + if (avr->state == cpu_Running || avr->state == cpu_Sleeping) { + avr_service_interrupts(avr); + + avr->data[R_SREG] = 0; + for (int i = 0; i < 8; i++) + if (avr->sreg[i] > 1) { + printf("** Invalid SREG!!\n"); + CRASH(); + } else if (avr->sreg[i]) + avr->data[R_SREG] |= (1 << i); + } + + if (step) { + avr->state = cpu_StepDone; + } + + return avr->state; +} + + +extern avr_kind_t tiny85; +extern avr_kind_t mega48,mega88,mega168; +extern avr_kind_t mega644; + +avr_kind_t * avr_kind[] = { + &tiny85, + &mega48, + &mega88, + &mega168, + &mega644, + NULL +}; + +avr_t * avr_make_mcu_by_name(const char *name) +{ + avr_kind_t * maker = NULL; + for (int i = 0; avr_kind[i] && !maker; i++) { + for (int j = 0; avr_kind[i]->names[j]; j++) + if (!strcmp(avr_kind[i]->names[j], name)) { + maker = avr_kind[i]; + break; + } + } + if (!maker) { + fprintf(stderr, "%s: AVR '%s' now known\n", __FUNCTION__, name); + return NULL; + } + + avr_t * avr = maker->make(); + printf("Starting %s - flashend %04x ramend %04x e2end %04x\n", avr->mmcu, avr->flashend, avr->ramend, avr->e2end); + return avr; +} + diff --git a/simavr/sim/sim_avr.h b/simavr/sim/sim_avr.h new file mode 100644 index 0000000..fe7f36d --- /dev/null +++ b/simavr/sim/sim_avr.h @@ -0,0 +1,203 @@ +/* + sim_avr.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_AVR_H__ +#define __SIM_AVR_H__ + +#include + +struct avr_t; +typedef uint8_t (*avr_io_read_t)(struct avr_t * avr, uint8_t addr, void * param); +typedef void (*avr_io_write_t)(struct avr_t * avr, uint8_t addr, uint8_t v, void * param); + +enum { + // SREG bit indexes + S_C = 0,S_Z,S_N,S_V,S_S,S_H,S_T,S_I, + + // 16 bits register pairs + R_XL = 0x1a, R_XH,R_YL,R_YH,R_ZL,R_ZH, + // stack pointer + R_SPL = 32+0x3d, R_SPH, + // real SREG + R_SREG = 32+0x3f, + + // maximum number of IO regisrer, on normal AVRs + MAX_IOs = 256 - 32, // minus 32 GP registers +}; + +#define AVR_DATA_TO_IO(v) ((v) - 32) +#define AVR_IO_TO_DATA(v) ((v) + 32) + +/* + * Core states. This will need populating with debug states for gdb + */ +enum { + cpu_Limbo = 0, // before initialization is finished + cpu_Stopped, + cpu_Running, + cpu_Sleeping, + + cpu_Step, + cpu_StepDone, +}; + +/* + * Main AVR instance. Some of these fields are set by the AVR "Core" definition files + * the rest is runtime data (as little as possible) + */ +typedef struct avr_t { + const char * mmcu; // name of the AVR + // these are filled by sim_core_declare from constants in /usr/lib/avr/include/avr/io*.h + uint16_t ramend; + uint32_t flashend; + uint32_t e2end; + uint8_t vector_size; + uint8_t signature[3]; + uint8_t fuse[4]; + + // filled by the ELF data, this allow tracking of invalid jumps + uint32_t codeend; + + int state; // stopped, running, sleeping + uint32_t frequency; // frequency we are running at + uint64_t cycle; // current cycle + + // called at init time + void (*init)(struct avr_t * avr); + // called at reset time + void (*reset)(struct avr_t * avr); + + // Mirror of the SREG register, to facilitate the access to bits + // in the opcode decoder. + // This array is re-synthetized back/forth when SREG changes + uint8_t sreg[8]; + + /* + * ** current PC ** + * Note that the PC is reoresenting /bytes/ while the AVR value is + * assumed to be "words". This is in line with what GDB does... + * this is why you will see >>1 ane <<1 in the decoder to handle jumps + */ + uint32_t pc; + + /* + * callback when specific IO registers are read/written + */ + struct { + void * param; + avr_io_read_t r; + } ior[MAX_IOs]; + struct { + void * param; + avr_io_write_t w; + } iow[MAX_IOs]; + + // flash memory (initialized to 0xff, and code loaded into it) + uint8_t * flash; + // this is the general purpose registers, IO registers, and SRAM + uint8_t * data; + + // queue of io modules + struct avr_io_t *io_port; + + // 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 interrupts + + // DEBUG ONLY + int trace; + struct avr_symbol_t ** codeline; + + /* DEBUG ONLY + * this keeps track of "jumps" ie, call,jmp,ret,reti and so on + * allows dumping of a meaningful data even if the stack is + * munched and so on + */ + #define OLD_PC_SIZE 32 + struct { + uint32_t pc; + uint16_t sp; + } old[OLD_PC_SIZE]; // catches reset.. + int old_pci; + +#if AVR_STACK_WATCH + #define STACK_FRAME_SIZE 32 + // this records the call/ret pairs, to try to catch + // code that munches the stack -under- their own frame + struct { + uint32_t pc; + uint16_t sp; + } stack_frame[STACK_FRAME_SIZE]; + int stack_frame_index; +#endif + + // DEBUG ONLY + // keeps track of wich registers gets touched by instructions + // reset before each new instructions. Allows meaningful traces + uint32_t touched[256 / 32]; // debug + + // placeholder + struct avr_gdb_t * gdb; +} avr_t; + + +// this is a static constructor for each of the AVR devices +typedef struct avr_kind_t { + const char * names[4]; // name aliases + avr_t * (*make)(); +} avr_kind_t; + +// a symbol loaded from the .elf file +typedef struct avr_symbol_t { + const char * symbol; + uint32_t addr; +} avr_symbol_t; + +// locate the maker for mcu "name" and allocates a new avr instance +avr_t * avr_make_mcu_by_name(const char *name); +// initializes a new AVR instance. Will call the IO registers init(), and then reset() +int avr_init(avr_t * avr); +// resets the AVR, and the IO modules +void avr_reset(avr_t * avr); +// run one cycle of the AVR, sleep if necessary +int avr_run(avr_t * avr); + +// load code in the "flash" +void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address); + + +/* + * these are accessors for avr->data but allows watchpoints to be set for gdb + * IO modules use that to set values to registers, and the AVR core decoder uses + * that to register "public" read by instructions. + */ +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); + + +#include "sim_io.h" +#include "sim_regbit.h" +#include "sim_interrupts.h" +#include "sim_irq.h" + +#endif /*__SIM_AVR_H__*/ + diff --git a/simavr/sim/sim_core.c b/simavr/sim/sim_core.c index 0980ab5..b70edd6 100644 --- a/simavr/sim/sim_core.c +++ b/simavr/sim/sim_core.c @@ -23,7 +23,7 @@ #include #include #include -#include "simavr.h" +#include "sim_avr.h" #include "sim_core.h" // SREG bit names @@ -717,6 +717,8 @@ uint16_t avr_run_one(avr_t * avr) } break; case 0x9598: { // BREAK STATE("break\n"); + if (avr->gdb) + avr->state = cpu_StepDone; } break; case 0x95a8: { // WDR STATE("wdr\n"); diff --git a/simavr/sim/sim_elf.c b/simavr/sim/sim_elf.c index 1270d67..02aaa93 100644 --- a/simavr/sim/sim_elf.c +++ b/simavr/sim/sim_elf.c @@ -89,7 +89,7 @@ int elf_read_firmware(const char * file, elf_firmware_t * firmware) } else if (!strcmp(name, ".mmcu")) { Elf_Data *s = elf_getdata(scn, NULL); firmware->mmcu = *((struct avr_mcu_t*)s->d_buf); - // printf("%s: avr_mcu_t size %ld / read %ld\n", __FUNCTION__, sizeof(avr_mcu_t), s->d_size); + printf("%s: avr_mcu_t size %ld / read %ld\n", __FUNCTION__, sizeof(struct avr_mcu_t), s->d_size); // avr->frequency = f_cpu; } #if ELF_SYMBOLS diff --git a/simavr/sim/sim_elf.h b/simavr/sim/sim_elf.h index 7b13e51..aa6bc77 100644 --- a/simavr/sim/sim_elf.h +++ b/simavr/sim/sim_elf.h @@ -29,7 +29,7 @@ #endif #if ELF_SYMBOLS -#include "simavr.h" +#include "sim_avr.h" #endif typedef struct elf_firmware_t { diff --git a/simavr/sim/sim_gdb.c b/simavr/sim/sim_gdb.c index 74714d1..eac0d20 100644 --- a/simavr/sim/sim_gdb.c +++ b/simavr/sim/sim_gdb.c @@ -1,8 +1,6 @@ /* sim_gdb.c - Placeholder! - Copyright 2008, 2009 Michel Pollet This file is part of simavr. @@ -25,19 +23,342 @@ #include #include #include +#include #include #include #include #include #include #include -#include "simavr.h" +#include +#include "sim_avr.h" +#include "avr_eeprom.h" typedef struct avr_gdb_t { avr_t * avr; - int sock; + int listen; // listen socket + int s; // current gdb connection + + pthread_t thread; + + uint32_t query_len; + char query[1024]; + + uint32_t watchmap; + struct { + uint32_t pc; + uint32_t len; + int kind; + } watch[32]; } avr_gdb_t; + // decode line text hex to binary +int read_hex_string(const char * src, uint8_t * buffer, int maxlen) +{ + uint8_t * dst = buffer; + int ls = 0; + uint8_t b = 0; + while (*src && maxlen--) { + char c = *src++; + switch (c) { + case 'a' ... 'f': b = (b << 4) | (c - 'a' + 0xa); break; + case 'A' ... 'F': b = (b << 4) | (c - 'A' + 0xa); break; + case '0' ... '9': b = (b << 4) | (c - '0'); break; + default: + if (c > ' ') { + fprintf(stderr, "%s: huh '%c' (%s)\n", __FUNCTION__, c, src); + return -1; + } + continue; + } + if (ls & 1) { + *dst++ = b; b = 0; + } + ls++; + } + + return dst - buffer; +} + +static void gdb_send_reply(avr_gdb_t * g, char * cmd) +{ + uint8_t reply[1024]; + uint8_t * dst = reply; + uint8_t check = 0; + *dst++ = '$'; + while (*cmd) { + check += *cmd; + *dst++ = *cmd++; + } + sprintf((char*)dst, "#%02x", check); + printf("%s '%s'\n", __FUNCTION__, reply); + send(g->s, reply, dst - reply + 3, 0); +} + +static void gdb_send_quick_status(avr_gdb_t * g, uint8_t signal) +{ + char cmd[64]; + + sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;", + signal, g->avr->data[R_SREG], + g->avr->data[R_SPL], g->avr->data[R_SPH], + g->avr->pc & 0xff, (g->avr->pc>>8)&0xff, (g->avr->pc>>16)&0xff); + gdb_send_reply(g, cmd); +} + +static int gdb_change_breakpoint(avr_gdb_t * g, int set, int kind, uint32_t addr, uint32_t len) +{ + printf("set %d kind %d addr %08x len %d (map %08x)\n", set, kind, addr, len, g->watchmap); + if (set) { + if (g->watchmap == 0xffffffff) + return -1; // map full + + // check to see if it exists + for (int i = 0; i < 32; i++) + if ((g->watchmap & (1 << i)) && g->watch[i].pc == addr) { + g->watch[i].len = len; + return 0; + } + for (int i = 0; i < 32; i++) + if (!(g->watchmap & (1 << i))) { + g->watchmap |= (1 << i); + g->watch[i].len = len; + g->watch[i].pc = addr; + g->watch[i].kind = kind; + return 0; + } + } else { + for (int i = 0; i < 32; i++) + if ((g->watchmap & (1 << i)) && g->watch[i].pc == addr) { + g->watchmap &= ~(1 << i); + g->watch[i].len = 0; + g->watch[i].pc = 0; + g->watch[i].kind = 0; + return 0; + } + } + return -1; +} + +static void gdb_handle_command(avr_gdb_t * g) +{ + avr_t * avr = g->avr; + char * cmd = g->query; + char rep[1024]; + uint8_t command = *cmd++; + switch (command) { + case '?': + gdb_send_reply(g, "S00"); + break; + case 'p': { + unsigned int regi = 0; + sscanf(cmd, "%x", ®i); + switch (regi) { + case 0 ... 31: + sprintf(rep, "%02x", g->avr->data[regi]); + break; + case 32: + sprintf(rep, "%02x", g->avr->data[R_SREG]); + break; + case 33: + sprintf(rep, "%02x%02x", g->avr->data[R_SPL], g->avr->data[R_SPH]); + break; + case 34: + sprintf(rep, "%02x%02x%02x00", + g->avr->pc & 0xff, (g->avr->pc>>8)&0xff, (g->avr->pc>>16)&0xff); + break; + } + gdb_send_reply(g, rep); + } break; + case 'm': { + uint32_t addr, len; + sscanf(cmd, "%x,%x", &addr, &len); + printf("read memory %08x, %08x\n", addr, len); + uint8_t * src = NULL; + if (addr < 0xffff) { + src = avr->flash + addr; + } else if (addr >= 0x800000 && (addr - 0x800000) <= avr->ramend) { + src = avr->data + addr - 0x800000; + } else if (addr >= 0x810000 && (addr - 0x810000) <= (16*1024)) { + avr_eeprom_desc_t ee = {.offset = (addr - 0x810000)}; + avr_ioctl(avr, AVR_IOCTL_EEPROM_GET, &ee); + if (ee.ee) + src = ee.ee; + else + gdb_send_reply(g, "E01"); + } else { + gdb_send_reply(g, "E01"); + break; + } + char * dst = rep; + while (len--) { + sprintf(dst, "%02x", *src++); + dst += 2; + } + *dst = 0; + gdb_send_reply(g, rep); + } break; + case 'M': { + uint32_t addr, len; + sscanf(cmd, "%x,%x", &addr, &len); + printf("write memory %08x, %08x\n", addr, len); + char * start = strchr(cmd, ':'); + if (!start) { + gdb_send_reply(g, "E01"); + break; + } + if (addr < 0xffff) { + read_hex_string(start + 1, avr->flash + addr, strlen(start+1)); + gdb_send_reply(g, "OK"); + } else if (addr >= 0x800000 && (addr - 0x800000) <= avr->ramend) { + read_hex_string(start + 1, avr->data + addr - 0x800000, strlen(start+1)); + gdb_send_reply(g, "OK"); + } else + gdb_send_reply(g, "E01"); + } break; + case 'c': { + avr->state = cpu_Running; + } break; + case 's': { + avr->state = cpu_Step; + } break; + case 'Z': + case 'z': { + uint32_t kind, addr, len; + sscanf(cmd, "%d,%x,%x", &kind, &addr, &len); + printf("breakbpoint %d, %08x, %08x\n", kind, addr, len); + switch (kind) { + case 0: // software breakpoint + case 1: // hardware breakpoint + if (addr <= avr->flashend) { + if (gdb_change_breakpoint(g, command == 'Z', kind, addr, len)) + gdb_send_reply(g, "E01"); + else + gdb_send_reply(g, "OK"); + } else + gdb_send_reply(g, "E01"); // out of flash address + break; + case 2: // write watchpoint + case 3: // read watchpoint + case 4: // access watchpoint + default: + gdb_send_reply(g, ""); + } + } break; + default: + gdb_send_reply(g, ""); + } +} + +void avr_gdb_processor(avr_t * avr) +{ + if (!avr || !avr->gdb) + return; + avr_gdb_t * g = avr->gdb; + + if (g->watchmap && avr->state == cpu_Running) { + for (int i = 0; i < 32; i++) + if ((g->watchmap & (1 << i)) && g->watch[i].pc == avr->pc) { + printf("avr_gdb_processor hit breakpoint at %08x\n", avr->pc); + gdb_send_quick_status(g, 0); + avr->state = cpu_Stopped; + } + } + if (avr->state == cpu_StepDone) { + gdb_send_quick_status(g, 0); + avr->state = cpu_Stopped; + } + if (avr->gdb->query_len) { + g->query_len = 0; + + // printf("avr_gdb_handle_query got a query '%s'\n", g->query); + gdb_handle_command(g); + } +} + + +static void * gdb_network_handler(void * param) +{ + avr_gdb_t * g = (avr_gdb_t*)param; + + do { + if (listen(g->listen, 1)) { + perror("gdb_network_handler listen"); + sleep(5); + continue; + } + + struct sockaddr_in address = { 0 }; + socklen_t ad_len = sizeof(address); + + g->s = accept(g->listen, (struct sockaddr*)&address, &ad_len); + + if (g->s == -1) { + perror("gdb_network_handler accept"); + sleep(5); + continue; + } + // should make that thread safe... + g->avr->state = cpu_Stopped; + + do { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(g->s, &read_set); + + struct timeval timo = { 1, 0000 }; // short, but not too short interval + /*int ret =*/ select(g->s + 1, &read_set, NULL, NULL, &timo); + + if (FD_ISSET(g->s, &read_set)) { + uint8_t buffer[1024]; + + ssize_t r = recv(g->s, buffer, sizeof(buffer)-1, 0); + + if (r == 0) { + printf("%s connection closed\n", __FUNCTION__); + break; + } + if (r == -1) { + perror("gdb_network_handler recv"); + break; + } + buffer[r] = 0; + // printf("%s: received %d bytes\n'%s'\n", __FUNCTION__, r, buffer); + // hdump("gdb", buffer, r); + + uint8_t * src = buffer; + while (*src == '+' || *src == '-') + src++; + if (*src == 3) { + src++; + g->query[0] = 3; + g->query_len = 1; // pass it on ? + } + if (*src == '$') { + // strip checksum + uint8_t * end = buffer + r - 1; + while (end > src && *end != '#') + *end-- = 0; + *end = 0; + src++; + printf("GDB command = '%s'\n", src); + + send(g->s, "+", 1, 0); + + strcpy(g->query, (char*)src); + g->query_len = strlen((char*)src); + } + } + } while(1); + + close(g->s); + + } while(1); + + return NULL; +} + + int avr_gdb_init(avr_t * avr) { avr_gdb_t * g = malloc(sizeof(avr_gdb_t)); @@ -45,22 +366,27 @@ int avr_gdb_init(avr_t * avr) avr->gdb = NULL; - if ((g->sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + if ((g->listen = socket(PF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr, "Can't create socket: %s", strerror(errno)); return -1; } int i = 1; - setsockopt(g->sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); + setsockopt(g->listen, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); struct sockaddr_in address = { 0 }; address.sin_family = AF_INET; address.sin_port = htons (1234); - if (bind(g->sock, (struct sockaddr *) &address, sizeof(address))) { + if (bind(g->listen, (struct sockaddr *) &address, sizeof(address))) { fprintf(stderr, "Can not bind socket: %s", strerror(errno)); return -1; } + printf("avr_gdb_init listening on port %d\n", 1234); + g->avr = avr; avr->gdb = g; + + pthread_create(&g->thread, NULL, gdb_network_handler, g); + return 0; } diff --git a/simavr/sim/sim_gdb.h b/simavr/sim/sim_gdb.h index 67f7a07..cbd4ed1 100644 --- a/simavr/sim/sim_gdb.h +++ b/simavr/sim/sim_gdb.h @@ -22,4 +22,9 @@ #ifndef __SIM_GDB_H__ #define __SIM_GDB_H__ +int avr_gdb_init(avr_t * avr); + +// call from the main AVR decoder thread +void avr_gdb_processor(avr_t * avr); + #endif diff --git a/simavr/sim/sim_interrupts.h b/simavr/sim/sim_interrupts.h index 270b39d..c602e8e 100644 --- a/simavr/sim/sim_interrupts.h +++ b/simavr/sim/sim_interrupts.h @@ -22,7 +22,7 @@ #ifndef __SIM_INTERUPTS_H__ #define __SIM_INTERUPTS_H__ -#include "simavr.h" +#include "sim_avr.h" // interrupt vector for the IO modules typedef struct avr_int_vector_t { diff --git a/simavr/sim/sim_io.h b/simavr/sim/sim_io.h index 770b3a8..87eb5b9 100644 --- a/simavr/sim/sim_io.h +++ b/simavr/sim/sim_io.h @@ -22,7 +22,7 @@ #ifndef __SIM_IO_H__ #define __SIM_IO_H__ -#include "simavr.h" +#include "sim_avr.h" /* * used by the ioports to implement their own features diff --git a/simavr/sim/sim_irq.h b/simavr/sim/sim_irq.h index 9349d30..161c311 100644 --- a/simavr/sim/sim_irq.h +++ b/simavr/sim/sim_irq.h @@ -22,7 +22,7 @@ #ifndef __SIM_IRQ_H__ #define __SIM_IRQ_H__ -#include "simavr.h" +#include "sim_avr.h" /* * Internal IRQ system diff --git a/simavr/sim/sim_regbit.h b/simavr/sim/sim_regbit.h index 719dbba..11700c8 100644 --- a/simavr/sim/sim_regbit.h +++ b/simavr/sim/sim_regbit.h @@ -22,7 +22,7 @@ #ifndef __SIM_REGBIT_H__ #define __SIM_REGBIT_H__ -#include "simavr.h" +#include "sim_avr.h" #define ARRAY_SIZE(_aa) (sizeof(_aa) / sizeof((_aa)[0])) diff --git a/simavr/sim/simavr.c b/simavr/sim/simavr.c deleted file mode 100644 index 7cb73fa..0000000 --- a/simavr/sim/simavr.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - simavr.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 -#include -#include -#include -#include -#include "simavr.h" -#include "sim_elf.h" - -#include "sim_core.h" -#include "avr_eeprom.h" -#include "avr_uart.h" - -void hdump(const char *w, uint8_t *b, size_t l) -{ - uint32_t i; - if (l < 16) { - printf("%s: ",w); - for (i = 0; i < l; i++) printf("%02x",b[i]); - } else { - printf("%s:\n",w); - for (i = 0; i < l; i++) { - if (!(i & 0x1f)) printf(" "); - printf("%02x",b[i]); - if ((i & 0x1f) == 0x1f) { - printf(" "); - printf("\n"); - } - } - } - printf("\n"); -} - - - -int avr_init(avr_t * avr) -{ - avr->flash = malloc(avr->flashend + 1); - memset(avr->flash, 0xff, avr->flashend + 1); - avr->data = malloc(avr->ramend + 1); - memset(avr->data, 0, avr->ramend + 1); - - // cpu is in limbo before init is finished. - avr->state = cpu_Limbo; - avr->frequency = 1000000; // can be overriden via avr_mcu_section - if (avr->init) - avr->init(avr); - avr->state = cpu_Running; - avr_reset(avr); - return 0; -} - -void avr_reset(avr_t * avr) -{ - memset(avr->data, 0x0, avr->ramend + 1); - _avr_sp_set(avr, avr->ramend); - avr->pc = 0; - for (int i = 0; i < 8; i++) - avr->sreg[i] = 0; - if (avr->reset) - avr->reset(avr); - - avr_io_t * port = avr->io_port; - while (port) { - if (port->reset) - port->reset(avr, port); - port = port->next; - } - -} - - -void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address) -{ - memcpy(avr->flash + address, code, size); -} - -void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v) -{ - if (addr > avr->ramend) { - printf("*** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x out of ram\n", - avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, v); - CRASH(); - } - if (addr < 32) { - printf("*** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x low registers\n", - avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, v); - CRASH(); - } -#if AVR_STACK_WATCH - /* - * this checks that the current "function" is not doctoring the stack frame that is located - * higher on the stack than it should be. It's a sign of code that has overrun it's stack - * frame and is munching on it's own return address. - */ - if (avr->stack_frame_index > 1 && addr > avr->stack_frame[avr->stack_frame_index-2].sp) { - printf("\e[31m%04x : munching stack SP %04x, A=%04x <= %02x\e[0m\n", avr->pc, _avr_sp_get(avr), addr, v); - } -#endif - avr->data[addr] = v; -} - -uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr) -{ - if (addr > avr->ramend) { - printf("*** Invalid read address PC=%04x SP=%04x O=%04x Address %04x out of ram (%04x)\n", - avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, avr->ramend); - CRASH(); - } - return avr->data[addr]; -} - - -int avr_run(avr_t * avr) -{ - if (avr->state == cpu_Stopped) - return avr->state; - - uint16_t new_pc = avr->pc; - - if (avr->state == cpu_Running) { - new_pc = avr_run_one(avr); - avr_dump_state(avr); - } else - avr->cycle ++; - - // re-synth the SREG - //SREG(); - // if we just re-enabled the interrupts... - if (avr->sreg[S_I] && !(avr->data[R_SREG] & (1 << S_I))) { - // printf("*** %s: Renabling interrupts\n", __FUNCTION__); - avr->pending_wait++; - } - avr_io_t * port = avr->io_port; - while (port) { - if (port->run) - port->run(avr, port); - port = port->next; - } - - avr->pc = new_pc; - - if (avr->state == cpu_Sleeping) { - if (!avr->sreg[S_I]) { - printf("simavr: sleeping with interrupts off, quitting gracefuly\n"); - exit(0); - } - usleep(500); - long sleep = (float)avr->frequency * (1.0f / 500.0f); - avr->cycle += sleep; - // avr->state = cpu_Running; - } - // Interrupt servicing might change the PC too - if (avr->state == cpu_Running || avr->state == cpu_Sleeping) { - avr_service_interrupts(avr); - - avr->data[R_SREG] = 0; - for (int i = 0; i < 8; i++) - if (avr->sreg[i] > 1) { - printf("** Invalid SREG!!\n"); - CRASH(); - } else if (avr->sreg[i]) - avr->data[R_SREG] |= (1 << i); - } - return avr->state; -} - -extern avr_kind_t tiny85; -extern avr_kind_t mega48,mega88,mega168; -extern avr_kind_t mega644; - -avr_kind_t * avr_kind[] = { - &tiny85, - &mega48, - &mega88, - &mega168, - &mega644, - NULL -}; - -void display_usage() -{ - printf("usage: simavr [-t] [-m ] [-f ] firmware\n"); - printf(" -t: run full scale decoder trace\n"); - exit(1); -} - -int main(int argc, char *argv[]) -{ - elf_firmware_t f; - long f_cpu = 0; - int trace = 0; - char name[16] = ""; - int option_count; - int option_index = 0; - - struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"mcu", required_argument, 0, 'm'}, - {"freq", required_argument, 0, 'f'}, - {"trace", no_argument, 0, 't'}, - {0, 0, 0, 0} - }; - - if (argc == 1) - display_usage(); - - while ((option_count = getopt_long(argc, argv, "thm:f:", long_options, &option_index)) != -1) { - switch (option_count) { - case 'h': - display_usage(); - break; - case 'm': - strcpy(name, optarg); - break; - case 'f': - f_cpu = atoi(optarg); - break; - case 't': - trace++; - break; - } - } - - elf_read_firmware(argv[argc-1], &f); - - if (strlen(name)) - strcpy(f.mmcu.name, name); - if (f_cpu) - f.mmcu.f_cpu = f_cpu; - - printf("firmware %s f=%d mmcu=%s\n", argv[argc-1], f.mmcu.f_cpu, f.mmcu.name); - - avr_kind_t * maker = NULL; - for (int i = 0; avr_kind[i] && !maker; i++) { - for (int j = 0; avr_kind[i]->names[j]; j++) - if (!strcmp(avr_kind[i]->names[j], f.mmcu.name)) { - maker = avr_kind[i]; - break; - } - } - if (!maker) { - fprintf(stderr, "%s: AVR '%s' now known\n", argv[0], f.mmcu.name); - exit(1); - } - - avr_t * avr = maker->make(); - printf("Starting %s - flashend %04x ramend %04x e2end %04x\n", avr->mmcu, avr->flashend, avr->ramend, avr->e2end); - avr_init(avr); - avr->frequency = f.mmcu.f_cpu; - avr->codeline = f.codeline; - avr_loadcode(avr, f.flash, f.flashsize, 0); - avr->codeend = f.flashsize - f.datasize; - if (f.eeprom && f.eesize) { - avr_eeprom_desc_t d = { .ee = f.eeprom, .offset = 0, .size = f.eesize }; - avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &d); - } - 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 deleted file mode 100644 index 61a7d03..0000000 --- a/simavr/sim/simavr.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - simavr.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 __SIMAVR_H__ -#define __SIMAVR_H__ - -#include - -struct avr_t; -typedef uint8_t (*avr_io_read_t)(struct avr_t * avr, uint8_t addr, void * param); -typedef void (*avr_io_write_t)(struct avr_t * avr, uint8_t addr, uint8_t v, void * param); - -enum { - // SREG bit indexes - S_C = 0,S_Z,S_N,S_V,S_S,S_H,S_T,S_I, - - // 16 bits register pairs - R_XL = 0x1a, R_XH,R_YL,R_YH,R_ZL,R_ZH, - // stack pointer - R_SPL = 32+0x3d, R_SPH, - // real SREG - R_SREG = 32+0x3f, - - // maximum number of IO regisrer, on normal AVRs - MAX_IOs = 256 - 32, // minus 32 GP registers -}; - -#define AVR_DATA_TO_IO(v) ((v) - 32) -#define AVR_IO_TO_DATA(v) ((v) + 32) - -/* - * Core states. This will need populating with debug states for gdb - */ -enum { - cpu_Limbo = 0, // before initialization is finished - cpu_Stopped, - cpu_Running, - cpu_Sleeping, -}; - -/* - * Main AVR instance. Some of these fields are set by the AVR "Core" definition files - * the rest is runtime data (as little as possible) - */ -typedef struct avr_t { - const char * mmcu; // name of the AVR - // these are filled by sim_core_declare from constants in /usr/lib/avr/include/avr/io*.h - uint16_t ramend; - uint32_t flashend; - uint32_t e2end; - uint8_t vector_size; - uint8_t signature[3]; - uint8_t fuse[4]; - - // filled by the ELF data, this allow tracking of invalid jumps - uint32_t codeend; - - int state; // stopped, running, sleeping - uint32_t frequency; // frequency we are running at - uint64_t cycle; // current cycle - - // called at init time - void (*init)(struct avr_t * avr); - // called at reset time - void (*reset)(struct avr_t * avr); - - // Mirror of the SREG register, to facilitate the access to bits - // in the opcode decoder. - // This array is re-synthetized back/forth when SREG changes - uint8_t sreg[8]; - - /* - * ** current PC ** - * Note that the PC is reoresenting /bytes/ while the AVR value is - * assumed to be "words". This is in line with what GDB does... - * this is why you will see >>1 ane <<1 in the decoder to handle jumps - */ - uint32_t pc; - - /* - * callback when specific IO registers are read/written - */ - struct { - void * param; - avr_io_read_t r; - } ior[MAX_IOs]; - struct { - void * param; - avr_io_write_t w; - } iow[MAX_IOs]; - - // flash memory (initialized to 0xff, and code loaded into it) - uint8_t * flash; - // this is the general purpose registers, IO registers, and SRAM - uint8_t * data; - - // queue of io modules - struct avr_io_t *io_port; - - // 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 interrupts - - // DEBUG ONLY - int trace; - struct avr_symbol_t ** codeline; - - /* DEBUG ONLY - * this keeps track of "jumps" ie, call,jmp,ret,reti and so on - * allows dumping of a meaningful data even if the stack is - * munched and so on - */ - #define OLD_PC_SIZE 32 - struct { - uint32_t pc; - uint16_t sp; - } old[OLD_PC_SIZE]; // catches reset.. - int old_pci; - -#if AVR_STACK_WATCH - #define STACK_FRAME_SIZE 32 - // this records the call/ret pairs, to try to catch - // code that munches the stack -under- their own frame - struct { - uint32_t pc; - uint16_t sp; - } stack_frame[STACK_FRAME_SIZE]; - int stack_frame_index; -#endif - - // DEBUG ONLY - // keeps track of wich registers gets touched by instructions - // reset before each new instructions. Allows meaningful traces - uint32_t touched[256 / 32]; // debug - - // placeholder - struct avr_gdb_t * gdb; -} avr_t; - - -// this is a static constructor for each of the AVR devices -typedef struct avr_kind_t { - const char * names[4]; // name aliases - avr_t * (*make)(); -} avr_kind_t; - -// a symbol loaded from the .elf file -typedef struct avr_symbol_t { - const char * symbol; - uint32_t addr; -} avr_symbol_t; - - -// initializes a new AVR instance. Will call the IO registers init(), and then reset() -int avr_init(avr_t * avr); -// resets the AVR, and the IO modules -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); - - -/* - * these are accessors for avr->data but allows watchpoints to be set for gdb - * IO modules use that to set values to registers, and the AVR core decoder uses - * that to register "public" read by instructions. - */ -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); - - -#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 index 5d0b069..2ccfaf8 100644 --- a/tests/atmega48_disabled_timer.c +++ b/tests/atmega48_disabled_timer.c @@ -7,6 +7,7 @@ #include #include +#include #include "avr_mcu_section.h" AVR_MCU(F_CPU, "atmega48"); @@ -29,5 +30,5 @@ int main(void) // here the interupts are enabled, but the interupt // vector should not be called while(1) - ; + sleep_mode(); }