From 163dd8199d1c63b6321ea54bb382339a3b35a5e3 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Wed, 6 Jan 2010 22:35:51 +0000 Subject: [PATCH] core: Add watchdog timer support Working base support for the watchdog timer, and the WDT instruction that resets it. Signed-off-by: Michel Pollet --- simavr/sim/avr_watchdog.c | 128 ++++++++++++++++++++++++++++++++++++++ simavr/sim/avr_watchdog.h | 68 ++++++++++++++++++++ simavr/sim/sim_core.c | 2 + 3 files changed, 198 insertions(+) create mode 100644 simavr/sim/avr_watchdog.c create mode 100644 simavr/sim/avr_watchdog.h diff --git a/simavr/sim/avr_watchdog.c b/simavr/sim/avr_watchdog.c new file mode 100644 index 0000000..99ab395 --- /dev/null +++ b/simavr/sim/avr_watchdog.c @@ -0,0 +1,128 @@ +/* + avr_watchdog.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 "avr_watchdog.h" + +static avr_cycle_count_t avr_watchdog_timer(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)param; + + printf("WATCHDOG timer fired.\n"); + avr_raise_interrupt(avr, &p->watchdog); + + if (!avr_regbit_get(avr, p->watchdog.enable)) { + printf("WATCHDOG timer fired and interrupt is not enabled. Quitting\n"); + avr_sadly_crashed(avr, 10); + } + + return 0; +} + +static avr_cycle_count_t avr_wdce_clear(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)param; + avr_regbit_clear(p->io.avr, p->wdce); + return 0; +} + +static void avr_watchdog_write(avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)param; + // backup the registers + uint8_t wd = avr->data[p->wdce.reg]; + uint8_t wdce_o = avr_regbit_get(avr, p->wdce); // old + uint8_t wde_o = avr_regbit_get(avr, p->wde); + uint8_t wdp_o[4]; + +// printf("avr_watchdog_write %02x\n", v); + for (int i = 0; i < 4; i++) + wdp_o[i] = avr_regbit_get(avr, p->wdp[i]); + + avr->data[p->wdce.reg] = v; + uint8_t wdce_n = avr_regbit_get(avr, p->wdce); // new + + if (wdce_o /* || wdce_n */) { + // make sure bit gets reset eventually + if (wdce_n) + avr_cycle_timer_register(avr, 4, avr_wdce_clear, p); + + uint8_t wdp = avr_regbit_get_array(avr, p->wdp, 4); + p->cycle_count = 2048 << wdp; + p->cycle_count = (p->cycle_count * avr->frequency) / 128000; + if (avr_regbit_get(avr, p->wde)) { + printf("Watchdog reset to %d cycles @ 128kz (* %d) = %d CPU cycles)\n", 2048 << wdp, 1 << wdp, (int)p->cycle_count); + avr_cycle_timer_register(avr, p->cycle_count, avr_watchdog_timer, p); + } else { + printf("Watchdog disabled\n"); + avr_cycle_timer_cancel(avr, avr_watchdog_timer, p); + } + } else { + // reset old values + avr_regbit_setto(avr, p->wde, wde_o); + for (int i = 0; i < 4; i++) + avr_regbit_setto(avr, p->wdp[i], wdp_o[i]); + v = avr->data[p->wdce.reg]; + } + avr_core_watch_write(avr, addr, v); +} + +/* + * called by the core when a WTD instruction is found + */ +static int avr_watchdog_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param) +{ + avr_watchdog_t * p = (avr_watchdog_t *)port; + int res = -1; + + if (ctl == AVR_IOCTL_WATCHDOG_RESET) { + if (avr_regbit_get(p->io.avr, p->wde)) + avr_cycle_timer_register(p->io.avr, p->cycle_count, avr_watchdog_timer, p); + res = 0; + } + + return res; +} + +static void avr_watchdog_reset(avr_io_t * port) +{ + avr_watchdog_t * p = (avr_watchdog_t *)port; + +} + +static avr_io_t _io = { + .kind = "watchdog", + .reset = avr_watchdog_reset, + .ioctl = avr_watchdog_ioctl, +}; + +void avr_watchdog_init(avr_t * avr, avr_watchdog_t * p) +{ + p->io = _io; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->watchdog); + + avr_register_io_write(avr, p->wdce.reg, avr_watchdog_write, p); +} + diff --git a/simavr/sim/avr_watchdog.h b/simavr/sim/avr_watchdog.h new file mode 100644 index 0000000..48a1da7 --- /dev/null +++ b/simavr/sim/avr_watchdog.h @@ -0,0 +1,68 @@ +/* + avr_watchdog.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 __AVR_WATCHDOG_H___ +#define __AVR_WATCHDOG_H___ + + +#include "sim_avr.h" + +typedef struct avr_watchdog_t { + avr_io_t io; + + avr_regbit_t wdrf; // watchdog reset flag (in MCU Status Register) + + avr_regbit_t wdce; // watchdog change enable + avr_regbit_t wde; // watchdog enabled + avr_regbit_t wdp[4]; // watchdog Timer Prescaler + + avr_int_vector_t watchdog; // watchdog interrupt + + avr_cycle_count_t cycle_count; +} avr_watchdog_t; + +/* takes no parameter */ +#define AVR_IOCTL_WATCHDOG_RESET AVR_IOCTL_DEF('w','d','t','r') + +void avr_watchdog_init(avr_t * avr, avr_watchdog_t * p); + + +/* + * This helps declare a watchdog block into a core. + * No guarantee it will work with all, but it works + * with the one we have right now + */ +#define AVR_WATCHDOG_DECLARE(_WDSR, _vec) \ + .watchdog = {\ + .wdrf = AVR_IO_REGBIT(MCUSR, WDRF),\ + .wdce = AVR_IO_REGBIT(_WDSR, WDCE),\ + .wde = AVR_IO_REGBIT(_WDSR, WDE),\ + .wdp = { AVR_IO_REGBIT(_WDSR, WDP0),AVR_IO_REGBIT(_WDSR, WDP1),\ + AVR_IO_REGBIT(_WDSR, WDP2),AVR_IO_REGBIT(_WDSR, WDP3) },\ + .watchdog = {\ + .enable = AVR_IO_REGBIT(_WDSR, WDIE),\ + .raised = AVR_IO_REGBIT(_WDSR, WDIF),\ + .vector = _vec,\ + },\ + } + +#endif /* __AVR_WATCHDOG_H___ */ diff --git a/simavr/sim/sim_core.c b/simavr/sim/sim_core.c index ee95552..a8df91d 100644 --- a/simavr/sim/sim_core.c +++ b/simavr/sim/sim_core.c @@ -26,6 +26,7 @@ #include "sim_avr.h" #include "sim_core.h" #include "avr_flash.h" +#include "avr_watchdog.h" // SREG bit names const char * _sreg_bit_name = "cznvshti"; @@ -798,6 +799,7 @@ uint16_t avr_run_one(avr_t * avr) } break; case 0x95a8: { // WDR STATE("wdr\n"); + avr_ioctl(avr, AVR_IOCTL_WATCHDOG_RESET, 0); } break; case 0x95e8: { // SPM STATE("spm\n"); -- 2.20.1