Commit 163dd8199d1c63b6321ea54bb382339a3b35a5e3
authorMichel Pollet <buserror@gmail.com>
Wed, 6 Jan 2010 22:35:51 +0000 (22:35 +0000)
committerMichel Pollet <buserror@gmail.com>
Wed, 6 Jan 2010 22:43:10 +0000 (22:43 +0000)
Working base support for the watchdog timer,
and the WDT instruction that resets it.

Signed-off-by: Michel Pollet <buserror@gmail.com>
3 files changed:
simavr/sim/avr_watchdog.c [new file with mode: 0644]
simavr/sim/avr_watchdog.h [new file with mode: 0644]
simavr/sim/sim_core.c

diff --git a/simavr/sim/avr_watchdog.c b/simavr/sim/avr_watchdog.c
new file mode 100644 (file)
index 0000000..99ab395
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+       avr_watchdog.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#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 (file)
index 0000000..48a1da7
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+       avr_watchdog.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __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___ */
index ee95552831eb2b21d9476e35769ff926a32f19db..a8df91d2dca9ed62dc799c3ed94ab8ef77fa6a71 100644 (file)
@@ -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");