* [RFC 0/1] Implement AVR WDT (watchdog timer) @ 2021-05-02 20:10 Michael Rolnik 2021-05-02 20:10 ` Michael Rolnik 2021-05-02 20:10 ` [RFC 1/1] Implement AVR watchdog timer Michael Rolnik 0 siblings, 2 replies; 11+ messages in thread From: Michael Rolnik @ 2021-05-02 20:10 UTC (permalink / raw) To: qemu-devel; +Cc: Michael Rolnik, me, konrad, richard.henderson, f4bug 1. Initial implementation of AVR WDT There are two issues with this implementation so I need your help here a. when I configure the WDT to fire an interrupt every 15ms it actually happens every 6 instructions b. when I specify --icount shift=0 qemu stucks Michael Rolnik (1): Implement AVR watchdog timer hw/avr/Kconfig | 1 + hw/avr/atmega.c | 15 ++- hw/avr/atmega.h | 2 + hw/watchdog/Kconfig | 3 + hw/watchdog/avr_wdt.c | 190 ++++++++++++++++++++++++++++++++++ hw/watchdog/meson.build | 2 + hw/watchdog/trace-events | 5 + include/hw/watchdog/avr_wdt.h | 47 +++++++++ target/avr/cpu.c | 3 + target/avr/cpu.h | 1 + target/avr/helper.c | 7 +- 11 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 hw/watchdog/avr_wdt.c create mode 100644 include/hw/watchdog/avr_wdt.h -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC 0/1] Implement AVR WDT (watchdog timer) 2021-05-02 20:10 [RFC 0/1] Implement AVR WDT (watchdog timer) Michael Rolnik @ 2021-05-02 20:10 ` Michael Rolnik 2021-05-03 13:06 ` Fred Konrad 2021-05-02 20:10 ` [RFC 1/1] Implement AVR watchdog timer Michael Rolnik 1 sibling, 1 reply; 11+ messages in thread From: Michael Rolnik @ 2021-05-02 20:10 UTC (permalink / raw) To: qemu-devel; +Cc: Michael Rolnik, me, konrad, richard.henderson, f4bug 1. Initial implementation of AVR WDT There are two issues with this implementation so I need your help here a. when I configure the WDT to fire an interrupt every 15ms it actually happens every 6 instructions b. when I specify --icount shift=0 qemu stucks Michael Rolnik (1): Implement AVR watchdog timer hw/avr/Kconfig | 1 + hw/avr/atmega.c | 15 ++- hw/avr/atmega.h | 2 + hw/watchdog/Kconfig | 3 + hw/watchdog/avr_wdt.c | 188 ++++++++++++++++++++++++++++++++++ hw/watchdog/meson.build | 2 + hw/watchdog/trace-events | 5 + include/hw/watchdog/avr_wdt.h | 47 +++++++++ target/avr/cpu.c | 3 + target/avr/cpu.h | 1 + target/avr/helper.c | 7 +- 11 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 hw/watchdog/avr_wdt.c create mode 100644 include/hw/watchdog/avr_wdt.h -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/1] Implement AVR WDT (watchdog timer) 2021-05-02 20:10 ` Michael Rolnik @ 2021-05-03 13:06 ` Fred Konrad 0 siblings, 0 replies; 11+ messages in thread From: Fred Konrad @ 2021-05-03 13:06 UTC (permalink / raw) To: Michael Rolnik, qemu-devel; +Cc: me, richard.henderson, f4bug Hi Michael, Le 5/2/21 à 10:10 PM, Michael Rolnik a écrit : > 1. Initial implementation of AVR WDT Nice! > There are two issues with this implementation so I need your help here > a. when I configure the WDT to fire an interrupt every 15ms it actually happens every 6 instructions Tested with the maximal prescaler: I've ~8 seconds which seems expected according to the documentation, I don't know how much time it takes to run through all the timers and go back in the execution loop, maybe 15ms is just too low? > b. when I specify --icount shift=0 qemu stucks For me it just crashes: qemu-system-avr: Bad icount read (gdb) bt #0 icount_get_raw_locked () at ../src/softmmu/icount.c:117 #1 0x00000000004e1801 in icount_get_locked () at ../src/softmmu/icount.c:128 #2 0x00000000004e187a in icount_get () at ../src/softmmu/icount.c:154 #3 0x00000000004bc8ff in cpus_get_virtual_clock () at ../src/softmmu/cpus.c:217 #4 0x00000000006f5bd5 in qemu_clock_get_ns (type=QEMU_CLOCK_VIRTUAL) at ../src/util/qemu-timer.c:637 #5 0x00000000005422d7 in avr_wdt_reset_alarm (wdt=0xe14040) at ../src/hw/watchdog/avr_wdt.c:74 #6 0x0000000000542536 in avr_wdt_write (opaque=0xe14040, offset=0, val64=57, size=1) at ../src/hw/watchdog/avr_wdt.c:134 #7 0x00000000004cdeaa in memory_region_write_accessor (mr=0xe14360, addr=0, value=0x7fff76ae9378, size=1, shift=0, mask=255, attrs=...) at ../src/softmmu/memory.c:491 #8 0x00000000004ce0cd in access_with_adjusted_size (addr=0, value=0x7fff76ae9378, size=1, access_size_min=1, access_size_max=1, access_fn=0x4cddc7 <memory_region_write_accessor>, mr=0xe14360, attrs=...) at ../src/softmmu/memory.c:552 if (cpu && cpu->running) { if (!cpu->can_do_io) { error_report("Bad icount read"); exit(1); HERE. } I'm not sure icount is supported on avr? ie: I don't see any gen_io_start(); in target/avr? > > Michael Rolnik (1): > Implement AVR watchdog timer > > hw/avr/Kconfig | 1 + > hw/avr/atmega.c | 15 ++- > hw/avr/atmega.h | 2 + > hw/watchdog/Kconfig | 3 + > hw/watchdog/avr_wdt.c | 188 ++++++++++++++++++++++++++++++++++ > hw/watchdog/meson.build | 2 + > hw/watchdog/trace-events | 5 + > include/hw/watchdog/avr_wdt.h | 47 +++++++++ > target/avr/cpu.c | 3 + > target/avr/cpu.h | 1 + > target/avr/helper.c | 7 +- > 11 files changed, 269 insertions(+), 5 deletions(-) > create mode 100644 hw/watchdog/avr_wdt.c > create mode 100644 include/hw/watchdog/avr_wdt.h > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC 1/1] Implement AVR watchdog timer 2021-05-02 20:10 [RFC 0/1] Implement AVR WDT (watchdog timer) Michael Rolnik 2021-05-02 20:10 ` Michael Rolnik @ 2021-05-02 20:10 ` Michael Rolnik 2021-05-03 13:15 ` Fred Konrad 1 sibling, 1 reply; 11+ messages in thread From: Michael Rolnik @ 2021-05-02 20:10 UTC (permalink / raw) To: qemu-devel; +Cc: Michael Rolnik, me, konrad, richard.henderson, f4bug Signed-off-by: Michael Rolnik <mrolnik@gmail.com> --- hw/avr/Kconfig | 1 + hw/avr/atmega.c | 15 ++- hw/avr/atmega.h | 2 + hw/watchdog/Kconfig | 3 + hw/watchdog/avr_wdt.c | 190 ++++++++++++++++++++++++++++++++++ hw/watchdog/meson.build | 2 + hw/watchdog/trace-events | 5 + include/hw/watchdog/avr_wdt.h | 47 +++++++++ target/avr/cpu.c | 3 + target/avr/cpu.h | 1 + target/avr/helper.c | 7 +- 11 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 hw/watchdog/avr_wdt.c create mode 100644 include/hw/watchdog/avr_wdt.h diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig index d31298c3cc..9939e4902f 100644 --- a/hw/avr/Kconfig +++ b/hw/avr/Kconfig @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU select AVR_TIMER16 select AVR_USART select AVR_POWER + select AVR_WDT config ARDUINO select AVR_ATMEGA_MCU diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index 44c6afebbb..31ceb1c21c 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -28,6 +28,7 @@ enum AtmegaPeripheral { GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, USART0, USART1, USART2, USART3, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, + WDT, PERIFMAX }; @@ -75,6 +76,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { [GPIOD] = { 0x29 }, [GPIOC] = { 0x26 }, [GPIOB] = { 0x23 }, + [WDT] = { 0x60 }, }, dev1280_2560[PERIFMAX] = { [USART3] = { 0x130, POWER1, 2 }, [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, @@ -99,6 +101,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { [GPIOC] = { 0x26 }, [GPIOB] = { 0x23 }, [GPIOA] = { 0x20 }, + [WDT] = { 0x60 }, }; enum AtmegaIrq { @@ -118,6 +121,7 @@ enum AtmegaIrq { TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, + WATCHDOG_TIMER_IRQ, IRQ_COUNT }; @@ -133,6 +137,7 @@ enum AtmegaIrq { #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) static const uint8_t irq168_328[IRQ_COUNT] = { + [WATCHDOG_TIMER_IRQ] = 7, [TIMER2_COMPA_IRQ] = 8, [TIMER2_COMPB_IRQ] = 9, [TIMER2_OVF_IRQ] = 10, @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { [USART0_DRE_IRQ] = 20, [USART0_TXC_IRQ] = 21, }, irq1280_2560[IRQ_COUNT] = { + [WATCHDOG_TIMER_IRQ] = 13, [TIMER2_COMPA_IRQ] = 14, [TIMER2_COMPB_IRQ] = 15, [TIMER2_OVF_IRQ] = 16, @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState *dev, Error **errp) g_free(devname); } + /* Watchdog Timer */ + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, TYPE_AVR_WDT); + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, + OFFSET_DATA + mc->dev[WDT].addr); + qdev_connect_gpio_out_named(cpudev, "wdr", 0, + qdev_get_gpio_in_named(DEVICE(&s->wdt), "wdr", 0)); + create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6); create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8); create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2); - create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1); create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3); create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3); } diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h index a99ee15c7e..60bbd44bdd 100644 --- a/hw/avr/atmega.h +++ b/hw/avr/atmega.h @@ -13,6 +13,7 @@ #include "hw/char/avr_usart.h" #include "hw/timer/avr_timer16.h" +#include "hw/watchdog/avr_wdt.h" #include "hw/misc/avr_power.h" #include "target/avr/cpu.h" #include "qom/object.h" @@ -45,6 +46,7 @@ struct AtmegaMcuState { AVRMaskState pwr[POWER_MAX]; AVRUsartState usart[USART_MAX]; AVRTimer16State timer[TIMER_MAX]; + AVRWatchdogState wdt; uint64_t xtal_freq_hz; }; diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig index 66e1d029e3..e0f89d2fe0 100644 --- a/hw/watchdog/Kconfig +++ b/hw/watchdog/Kconfig @@ -20,3 +20,6 @@ config WDT_IMX2 config WDT_SBSA bool + +config AVR_WDT + bool diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c new file mode 100644 index 0000000000..4ce1029a64 --- /dev/null +++ b/hw/watchdog/avr_wdt.c @@ -0,0 +1,190 @@ +/* + * AVR watchdog + * + * Copyright (c) 2018 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/watchdog/avr_wdt.h" +#include "trace.h" + +/* Field masks */ +#define WDTCSR_MASK_WDP0 0x01 +#define WDTCSR_MASK_WDP1 0x02 +#define WDTCSR_MASK_WDP2 0x04 +#define WDTCSR_MASK_WDE 0x08 +#define WDTCSR_MASK_WCE 0x10 +#define WDTCSR_MASK_WDP3 0x20 +#define WDTCSR_MASK_WDIE 0x40 +#define WDTCSR_MASK_WDIF 0x80 + +#define WDTCSR_SHFT_WDP0 0x00 +#define WDTCSR_SHFT_WDP1 0x01 +#define WDTCSR_SHFT_WDP2 0x02 +#define WDTCSR_SHFT_WDE 0x03 +#define WDTCSR_SHFT_WCE 0x04 +#define WDTCSR_SHFT_WDP3 0x05 +#define WDTCSR_SHFT_WDIE 0x06 +#define WDTCSR_SHFT_WDIF 0x07 + +/* Helper macros */ +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> WDTCSR_SHFT_WDP0) +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> WDTCSR_SHFT_WDP1) +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> WDTCSR_SHFT_WDP2) +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> WDTCSR_SHFT_WDP3) +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ + (WDP1(csr) << 1) | (WDP0(csr) << 0)) +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> WDTCSR_SHFT_WDIE) +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> WDTCSR_SHFT_WDE) +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> WDTCSR_SHFT_WCE) + +#define DB_PRINT(fmt, args...) /* Nothing */ + +#define MS2NS(n) ((n) * 1000000ull) + +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) +{ + uint32_t csr = wdt->csr; + int wdp = WDP(csr); + assert(wdp <= 9); + + if (WDIE(csr) == 0 && WDE(csr) == 0) { + /* watchdog is stopped */ + return; + } + + timer_mod_ns(wdt->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (MS2NS(15) << wdp)); +} + +static void avr_wdt_interrupt(void *opaque) +{ + AVRWatchdogState *wdt = opaque; + int8_t csr = wdt->csr; + + if (WDE(csr) == 0 && WDIE(csr) == 0) { + /* Stopped */ + + } else if (WDE(csr) == 0 && WDIE(csr) == 1) { + /* Interrupt Mode */ + wdt->csr |= WDTCSR_MASK_WDIF; + qemu_set_irq(wdt->irq, 1); + trace_avr_wdt_interrupt(); + } else if (WDE(csr) == 1 && WDIE(csr) == 0) { + /* System Reset Mode */ + } else if (WDE(csr) == 1 && WDIE(csr) == 1) { + /* Interrupt and System Reset Mode */ + wdt->csr |= WDTCSR_MASK_WDIF; + qemu_set_irq(wdt->irq, 1); + trace_avr_wdt_interrupt(); + } + + avr_wdt_reset_alarm(wdt); +} + +static void avr_wdt_reset(DeviceState *dev) +{ + AVRWatchdogState *wdt = AVR_WDT(dev); + + wdt->csr = 0; + qemu_set_irq(wdt->irq, 0); + avr_wdt_reset_alarm(wdt); +} + +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, unsigned size) +{ + assert(size == 1); + AVRWatchdogState *wdt = opaque; + uint8_t retval = wdt->csr; + + trace_avr_wdt_read(offset, retval); + + return (uint64_t)retval; +} + +static void avr_wdt_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size == 1); + AVRWatchdogState *wdt = opaque; + uint8_t val8 = (uint8_t)val64; + + trace_avr_wdt_write(offset, val8); + + wdt->csr = val8; + avr_wdt_reset_alarm(wdt); +} + +static const MemoryRegionOps avr_wdt_ops = { + .read = avr_wdt_read, + .write = avr_wdt_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = {.max_access_size = 1} +}; + +static void avr_wdt_wdr(void *opaque, int irq, int level) +{ + AVRWatchdogState *wdt = AVR_WDT(opaque); + + avr_wdt_reset_alarm(wdt); +} + +static void avr_wdt_init(Object *obj) +{ + AVRWatchdogState *s = AVR_WDT(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, + s, "avr-wdt", 0xa); + + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); +} + +static void avr_wdt_realize(DeviceState *dev, Error **errp) +{ + AVRWatchdogState *s = AVR_WDT(dev); + + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); +} + +static void avr_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = avr_wdt_reset; + dc->realize = avr_wdt_realize; +} + +static const TypeInfo avr_wdt_info = { + .name = TYPE_AVR_WDT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AVRWatchdogState), + .instance_init = avr_wdt_init, + .class_init = avr_wdt_class_init, +}; + +static void avr_wdt_register_types(void) +{ + type_register_static(&avr_wdt_info); +} + +type_init(avr_wdt_register_types) diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index 054c403dea..8db2be8317 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) + +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: files('avr_wdt.c')) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index 3124ca1f1b..ac14773179 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK AP cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 + +# avr_wdt.c +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u value:%u" +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u value:%u" +avr_wdt_interrupt(void) "" diff --git a/include/hw/watchdog/avr_wdt.h b/include/hw/watchdog/avr_wdt.h new file mode 100644 index 0000000000..2679e8f2a6 --- /dev/null +++ b/include/hw/watchdog/avr_wdt.h @@ -0,0 +1,47 @@ +/* + * AVR 16-bit timer + * + * Copyright (c) 2021 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#ifndef HW_WATCHDOG_AVR_WDT_H +#define HW_WATCHDOG_AVR_WDT_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/hw.h" +#include "qom/object.h" + +#define TYPE_AVR_WDT "avr-wdt" +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) + +struct AVRWatchdogState { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + MemoryRegion imsk_iomem; + MemoryRegion ifr_iomem; + QEMUTimer *timer; + qemu_irq irq; + + /* registers */ + uint8_t csr; +}; + +#endif /* HW_WATCHDOG_AVR_WDT_H */ diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 0f4596932b..d5eb785833 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) /* Set the number of interrupts supported by the CPU. */ qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, sizeof(cpu->env.intsrc) * 8); + + /* register watchdog timer reset interrupt */ + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); } static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) diff --git a/target/avr/cpu.h b/target/avr/cpu.h index d148e8c75a..f8f5641c8b 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -152,6 +152,7 @@ typedef struct AVRCPU { CPUNegativeOffsetState neg; CPUAVRState env; + qemu_irq wdr; /* reset WDT */ } AVRCPU; extern const struct VMStateDescription vms_avr_cpu; diff --git a/target/avr/helper.c b/target/avr/helper.c index 35e1019594..dd88057e5f 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "exec/address-spaces.h" #include "exec/helper-proto.h" +#include "hw/irq.h" bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) void helper_wdr(CPUAVRState *env) { - CPUState *cs = env_cpu(env); + AVRCPU *cpu = env_archcpu(env); - /* WD is not implemented yet, placeholder */ - cs->exception_index = EXCP_DEBUG; - cpu_loop_exit(cs); + qemu_set_irq(cpu->wdr, 1); } /* -- 2.25.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC 1/1] Implement AVR watchdog timer 2021-05-02 20:10 ` [RFC 1/1] Implement AVR watchdog timer Michael Rolnik @ 2021-05-03 13:15 ` Fred Konrad 2021-05-03 13:36 ` Michael Rolnik 0 siblings, 1 reply; 11+ messages in thread From: Fred Konrad @ 2021-05-03 13:15 UTC (permalink / raw) To: Michael Rolnik, qemu-devel; +Cc: me, richard.henderson, f4bug Le 5/2/21 à 10:10 PM, Michael Rolnik a écrit : > Signed-off-by: Michael Rolnik <mrolnik@gmail.com> > --- > hw/avr/Kconfig | 1 + > hw/avr/atmega.c | 15 ++- > hw/avr/atmega.h | 2 + > hw/watchdog/Kconfig | 3 + > hw/watchdog/avr_wdt.c | 190 ++++++++++++++++++++++++++++++++++ > hw/watchdog/meson.build | 2 + > hw/watchdog/trace-events | 5 + > include/hw/watchdog/avr_wdt.h | 47 +++++++++ > target/avr/cpu.c | 3 + > target/avr/cpu.h | 1 + > target/avr/helper.c | 7 +- > 11 files changed, 271 insertions(+), 5 deletions(-) > create mode 100644 hw/watchdog/avr_wdt.c > create mode 100644 include/hw/watchdog/avr_wdt.h > > diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig > index d31298c3cc..9939e4902f 100644 > --- a/hw/avr/Kconfig > +++ b/hw/avr/Kconfig > @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU > select AVR_TIMER16 > select AVR_USART > select AVR_POWER > + select AVR_WDT > > config ARDUINO > select AVR_ATMEGA_MCU > diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c > index 44c6afebbb..31ceb1c21c 100644 > --- a/hw/avr/atmega.c > +++ b/hw/avr/atmega.c > @@ -28,6 +28,7 @@ enum AtmegaPeripheral { > GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, > USART0, USART1, USART2, USART3, > TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, > + WDT, > PERIFMAX > }; > > @@ -75,6 +76,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > [GPIOD] = { 0x29 }, > [GPIOC] = { 0x26 }, > [GPIOB] = { 0x23 }, > + [WDT] = { 0x60 }, > }, dev1280_2560[PERIFMAX] = { > [USART3] = { 0x130, POWER1, 2 }, > [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, > @@ -99,6 +101,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > [GPIOC] = { 0x26 }, > [GPIOB] = { 0x23 }, > [GPIOA] = { 0x20 }, > + [WDT] = { 0x60 }, > }; > > enum AtmegaIrq { > @@ -118,6 +121,7 @@ enum AtmegaIrq { > TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, > TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, > TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, > + WATCHDOG_TIMER_IRQ, > IRQ_COUNT > }; > > @@ -133,6 +137,7 @@ enum AtmegaIrq { > #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) > > static const uint8_t irq168_328[IRQ_COUNT] = { > + [WATCHDOG_TIMER_IRQ] = 7, > [TIMER2_COMPA_IRQ] = 8, > [TIMER2_COMPB_IRQ] = 9, > [TIMER2_OVF_IRQ] = 10, > @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { > [USART0_DRE_IRQ] = 20, > [USART0_TXC_IRQ] = 21, > }, irq1280_2560[IRQ_COUNT] = { > + [WATCHDOG_TIMER_IRQ] = 13, > [TIMER2_COMPA_IRQ] = 14, > [TIMER2_COMPB_IRQ] = 15, > [TIMER2_OVF_IRQ] = 16, > @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState *dev, Error **errp) > g_free(devname); > } > > + /* Watchdog Timer */ > + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, TYPE_AVR_WDT); > + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, > + OFFSET_DATA + mc->dev[WDT].addr); > + qdev_connect_gpio_out_named(cpudev, "wdr", 0, > + qdev_get_gpio_in_named(DEVICE(&s->wdt), "wdr", 0)); > + > create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6); > create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8); > create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2); > - create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1); > create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3); > create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3); > } > diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h > index a99ee15c7e..60bbd44bdd 100644 > --- a/hw/avr/atmega.h > +++ b/hw/avr/atmega.h > @@ -13,6 +13,7 @@ > > #include "hw/char/avr_usart.h" > #include "hw/timer/avr_timer16.h" > +#include "hw/watchdog/avr_wdt.h" > #include "hw/misc/avr_power.h" > #include "target/avr/cpu.h" > #include "qom/object.h" > @@ -45,6 +46,7 @@ struct AtmegaMcuState { > AVRMaskState pwr[POWER_MAX]; > AVRUsartState usart[USART_MAX]; > AVRTimer16State timer[TIMER_MAX]; > + AVRWatchdogState wdt; > uint64_t xtal_freq_hz; > }; > > diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig > index 66e1d029e3..e0f89d2fe0 100644 > --- a/hw/watchdog/Kconfig > +++ b/hw/watchdog/Kconfig > @@ -20,3 +20,6 @@ config WDT_IMX2 > > config WDT_SBSA > bool > + > +config AVR_WDT > + bool > diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c > new file mode 100644 > index 0000000000..4ce1029a64 > --- /dev/null > +++ b/hw/watchdog/avr_wdt.c > @@ -0,0 +1,190 @@ > +/* > + * AVR watchdog > + * > + * Copyright (c) 2018 Michael Rolnik 2021? > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library 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 > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qemu/log.h" > +#include "hw/irq.h" > +#include "hw/watchdog/avr_wdt.h" > +#include "trace.h" > + > +/* Field masks */ > +#define WDTCSR_MASK_WDP0 0x01 > +#define WDTCSR_MASK_WDP1 0x02 > +#define WDTCSR_MASK_WDP2 0x04 > +#define WDTCSR_MASK_WDE 0x08 > +#define WDTCSR_MASK_WCE 0x10 > +#define WDTCSR_MASK_WDP3 0x20 > +#define WDTCSR_MASK_WDIE 0x40 > +#define WDTCSR_MASK_WDIF 0x80 > + > +#define WDTCSR_SHFT_WDP0 0x00 > +#define WDTCSR_SHFT_WDP1 0x01 > +#define WDTCSR_SHFT_WDP2 0x02 > +#define WDTCSR_SHFT_WDE 0x03 > +#define WDTCSR_SHFT_WCE 0x04 > +#define WDTCSR_SHFT_WDP3 0x05 > +#define WDTCSR_SHFT_WDIE 0x06 > +#define WDTCSR_SHFT_WDIF 0x07 > + > +/* Helper macros */ > +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> WDTCSR_SHFT_WDP0) > +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> WDTCSR_SHFT_WDP1) > +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> WDTCSR_SHFT_WDP2) > +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> WDTCSR_SHFT_WDP3) > +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ > + (WDP1(csr) << 1) | (WDP0(csr) << 0)) > +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> WDTCSR_SHFT_WDIE) > +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> WDTCSR_SHFT_WDE) > +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> WDTCSR_SHFT_WCE) > + > +#define DB_PRINT(fmt, args...) /* Nothing */ > + > +#define MS2NS(n) ((n) * 1000000ull) > + > +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) > +{ > + uint32_t csr = wdt->csr; > + int wdp = WDP(csr); > + assert(wdp <= 9); Maybe qemu_log(..) instead and pick a default value? > + > + if (WDIE(csr) == 0 && WDE(csr) == 0) { > + /* watchdog is stopped */ > + return; > + } > + > + timer_mod_ns(wdt->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > + (MS2NS(15) << wdp)); > +} > + > +static void avr_wdt_interrupt(void *opaque) > +{ > + AVRWatchdogState *wdt = opaque; > + int8_t csr = wdt->csr; > + > + if (WDE(csr) == 0 && WDIE(csr) == 0) { > + /* Stopped */ > + > + } else if (WDE(csr) == 0 && WDIE(csr) == 1) { > + /* Interrupt Mode */ > + wdt->csr |= WDTCSR_MASK_WDIF; > + qemu_set_irq(wdt->irq, 1); > + trace_avr_wdt_interrupt(); > + } else if (WDE(csr) == 1 && WDIE(csr) == 0) { > + /* System Reset Mode */ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); +/- setting the MCUSR, WDRF flags would be nice. > + } else if (WDE(csr) == 1 && WDIE(csr) == 1) { > + /* Interrupt and System Reset Mode */ > + wdt->csr |= WDTCSR_MASK_WDIF; > + qemu_set_irq(wdt->irq, 1); > + trace_avr_wdt_interrupt(); > + } > + > + avr_wdt_reset_alarm(wdt); > +} > + > +static void avr_wdt_reset(DeviceState *dev) > +{ > + AVRWatchdogState *wdt = AVR_WDT(dev); > + > + wdt->csr = 0; > + qemu_set_irq(wdt->irq, 0); > + avr_wdt_reset_alarm(wdt); > +} > + > +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, unsigned size) > +{ > + assert(size == 1); > + AVRWatchdogState *wdt = opaque; > + uint8_t retval = wdt->csr; > + > + trace_avr_wdt_read(offset, retval); > + > + return (uint64_t)retval; > +} > + > +static void avr_wdt_write(void *opaque, hwaddr offset, > + uint64_t val64, unsigned size) > +{ > + assert(size == 1); > + AVRWatchdogState *wdt = opaque; > + uint8_t val8 = (uint8_t)val64; > + > + trace_avr_wdt_write(offset, val8); > + > + wdt->csr = val8; > + avr_wdt_reset_alarm(wdt); > +} > + > +static const MemoryRegionOps avr_wdt_ops = { > + .read = avr_wdt_read, > + .write = avr_wdt_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .impl = {.max_access_size = 1} > +}; > + > +static void avr_wdt_wdr(void *opaque, int irq, int level) > +{ > + AVRWatchdogState *wdt = AVR_WDT(opaque); > + > + avr_wdt_reset_alarm(wdt); > +} > + > +static void avr_wdt_init(Object *obj) > +{ > + AVRWatchdogState *s = AVR_WDT(obj); > + > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); > + > + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, > + s, "avr-wdt", 0xa); > + > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); > + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); > +} > + > +static void avr_wdt_realize(DeviceState *dev, Error **errp) > +{ > + AVRWatchdogState *s = AVR_WDT(dev); > + > + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); > +} > + > +static void avr_wdt_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->reset = avr_wdt_reset; > + dc->realize = avr_wdt_realize; > +} > + > +static const TypeInfo avr_wdt_info = { > + .name = TYPE_AVR_WDT, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(AVRWatchdogState), > + .instance_init = avr_wdt_init, > + .class_init = avr_wdt_class_init, > +}; > + > +static void avr_wdt_register_types(void) > +{ > + type_register_static(&avr_wdt_info); > +} > + > +type_init(avr_wdt_register_types) > diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build > index 054c403dea..8db2be8317 100644 > --- a/hw/watchdog/meson.build > +++ b/hw/watchdog/meson.build > @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) > softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) > softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) > softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) > + > +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: files('avr_wdt.c')) > diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events > index 3124ca1f1b..ac14773179 100644 > --- a/hw/watchdog/trace-events > +++ b/hw/watchdog/trace-events > @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK AP > cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" > cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" > cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 > + > +# avr_wdt.c > +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u value:%u" > +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u value:%u" > +avr_wdt_interrupt(void) "" > diff --git a/include/hw/watchdog/avr_wdt.h b/include/hw/watchdog/avr_wdt.h > new file mode 100644 > index 0000000000..2679e8f2a6 > --- /dev/null > +++ b/include/hw/watchdog/avr_wdt.h > @@ -0,0 +1,47 @@ > +/* > + * AVR 16-bit timer AVR Watchdog? > + * > + * Copyright (c) 2021 Michael Rolnik > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library 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 > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > + */ > + > +#ifndef HW_WATCHDOG_AVR_WDT_H > +#define HW_WATCHDOG_AVR_WDT_H > + > +#include "hw/sysbus.h" > +#include "qemu/timer.h" > +#include "hw/hw.h" > +#include "qom/object.h" > + > +#define TYPE_AVR_WDT "avr-wdt" > +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) > + > +struct AVRWatchdogState { > + /* <private> */ > + SysBusDevice parent_obj; > + > + /* <public> */ > + MemoryRegion iomem; > + MemoryRegion imsk_iomem; > + MemoryRegion ifr_iomem; > + QEMUTimer *timer; > + qemu_irq irq; > + > + /* registers */ > + uint8_t csr; > +}; > + > +#endif /* HW_WATCHDOG_AVR_WDT_H */ > diff --git a/target/avr/cpu.c b/target/avr/cpu.c > index 0f4596932b..d5eb785833 100644 > --- a/target/avr/cpu.c > +++ b/target/avr/cpu.c > @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) > /* Set the number of interrupts supported by the CPU. */ > qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, > sizeof(cpu->env.intsrc) * 8); > + > + /* register watchdog timer reset interrupt */ > + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); > } > > static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) > diff --git a/target/avr/cpu.h b/target/avr/cpu.h > index d148e8c75a..f8f5641c8b 100644 > --- a/target/avr/cpu.h > +++ b/target/avr/cpu.h > @@ -152,6 +152,7 @@ typedef struct AVRCPU { > > CPUNegativeOffsetState neg; > CPUAVRState env; > + qemu_irq wdr; /* reset WDT */ > } AVRCPU; > > extern const struct VMStateDescription vms_avr_cpu; > diff --git a/target/avr/helper.c b/target/avr/helper.c > index 35e1019594..dd88057e5f 100644 > --- a/target/avr/helper.c > +++ b/target/avr/helper.c > @@ -24,6 +24,7 @@ > #include "exec/exec-all.h" > #include "exec/address-spaces.h" > #include "exec/helper-proto.h" > +#include "hw/irq.h" > > bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) > { > @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) > > void helper_wdr(CPUAVRState *env) > { > - CPUState *cs = env_cpu(env); > + AVRCPU *cpu = env_archcpu(env); > > - /* WD is not implemented yet, placeholder */ > - cs->exception_index = EXCP_DEBUG; > - cpu_loop_exit(cs); > + qemu_set_irq(cpu->wdr, 1); > } > > /* > Thanks! ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 1/1] Implement AVR watchdog timer 2021-05-03 13:15 ` Fred Konrad @ 2021-05-03 13:36 ` Michael Rolnik 2021-05-03 20:08 ` Michael Rolnik 0 siblings, 1 reply; 11+ messages in thread From: Michael Rolnik @ 2021-05-03 13:36 UTC (permalink / raw) To: Fred Konrad Cc: Joaquin de Andres, Richard Henderson, QEMU Developers, Philippe Mathieu-Daudé [-- Attachment #1: Type: text/plain, Size: 17655 bytes --] Hi Fred. 1. thanks 2. It seems I have forgotten to set those flags. 3. 15ms is easy to test 8s will take 533 times longer, so in my case 3200 instructions which is totally incorrect. I don't understand why as I program the timer in virtual nanoseconds and not host time. 4. I hope Richard could help with icount. best regards, Michael Rolnik On Mon, May 3, 2021 at 4:15 PM Fred Konrad <konrad@adacore.com> wrote: > > > Le 5/2/21 à 10:10 PM, Michael Rolnik a écrit : > > Signed-off-by: Michael Rolnik <mrolnik@gmail.com> > > --- > > hw/avr/Kconfig | 1 + > > hw/avr/atmega.c | 15 ++- > > hw/avr/atmega.h | 2 + > > hw/watchdog/Kconfig | 3 + > > hw/watchdog/avr_wdt.c | 190 ++++++++++++++++++++++++++++++++++ > > hw/watchdog/meson.build | 2 + > > hw/watchdog/trace-events | 5 + > > include/hw/watchdog/avr_wdt.h | 47 +++++++++ > > target/avr/cpu.c | 3 + > > target/avr/cpu.h | 1 + > > target/avr/helper.c | 7 +- > > 11 files changed, 271 insertions(+), 5 deletions(-) > > create mode 100644 hw/watchdog/avr_wdt.c > > create mode 100644 include/hw/watchdog/avr_wdt.h > > > > diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig > > index d31298c3cc..9939e4902f 100644 > > --- a/hw/avr/Kconfig > > +++ b/hw/avr/Kconfig > > @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU > > select AVR_TIMER16 > > select AVR_USART > > select AVR_POWER > > + select AVR_WDT > > > > config ARDUINO > > select AVR_ATMEGA_MCU > > diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c > > index 44c6afebbb..31ceb1c21c 100644 > > --- a/hw/avr/atmega.c > > +++ b/hw/avr/atmega.c > > @@ -28,6 +28,7 @@ enum AtmegaPeripheral { > > GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, > > USART0, USART1, USART2, USART3, > > TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, > > + WDT, > > PERIFMAX > > }; > > > > @@ -75,6 +76,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > > [GPIOD] = { 0x29 }, > > [GPIOC] = { 0x26 }, > > [GPIOB] = { 0x23 }, > > + [WDT] = { 0x60 }, > > }, dev1280_2560[PERIFMAX] = { > > [USART3] = { 0x130, POWER1, 2 }, > > [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, > > @@ -99,6 +101,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > > [GPIOC] = { 0x26 }, > > [GPIOB] = { 0x23 }, > > [GPIOA] = { 0x20 }, > > + [WDT] = { 0x60 }, > > }; > > > > enum AtmegaIrq { > > @@ -118,6 +121,7 @@ enum AtmegaIrq { > > TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, > > TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, > > TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, > > + WATCHDOG_TIMER_IRQ, > > IRQ_COUNT > > }; > > > > @@ -133,6 +137,7 @@ enum AtmegaIrq { > > #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) > > > > static const uint8_t irq168_328[IRQ_COUNT] = { > > + [WATCHDOG_TIMER_IRQ] = 7, > > [TIMER2_COMPA_IRQ] = 8, > > [TIMER2_COMPB_IRQ] = 9, > > [TIMER2_OVF_IRQ] = 10, > > @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { > > [USART0_DRE_IRQ] = 20, > > [USART0_TXC_IRQ] = 21, > > }, irq1280_2560[IRQ_COUNT] = { > > + [WATCHDOG_TIMER_IRQ] = 13, > > [TIMER2_COMPA_IRQ] = 14, > > [TIMER2_COMPB_IRQ] = 15, > > [TIMER2_OVF_IRQ] = 16, > > @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState *dev, Error > **errp) > > g_free(devname); > > } > > > > + /* Watchdog Timer */ > > + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, TYPE_AVR_WDT); > > + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); > > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, > > + OFFSET_DATA + mc->dev[WDT].addr); > > + qdev_connect_gpio_out_named(cpudev, "wdr", 0, > > + qdev_get_gpio_in_named(DEVICE(&s->wdt), "wdr", 0)); > > + > > create_unimplemented_device("avr-twi", OFFSET_DATA + > 0x0b8, 6); > > create_unimplemented_device("avr-adc", OFFSET_DATA + > 0x078, 8); > > create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + > 0x074, 2); > > - create_unimplemented_device("avr-watchdog", OFFSET_DATA + > 0x060, 1); > > create_unimplemented_device("avr-spi", OFFSET_DATA + > 0x04c, 3); > > create_unimplemented_device("avr-eeprom", OFFSET_DATA + > 0x03f, 3); > > } > > diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h > > index a99ee15c7e..60bbd44bdd 100644 > > --- a/hw/avr/atmega.h > > +++ b/hw/avr/atmega.h > > @@ -13,6 +13,7 @@ > > > > #include "hw/char/avr_usart.h" > > #include "hw/timer/avr_timer16.h" > > +#include "hw/watchdog/avr_wdt.h" > > #include "hw/misc/avr_power.h" > > #include "target/avr/cpu.h" > > #include "qom/object.h" > > @@ -45,6 +46,7 @@ struct AtmegaMcuState { > > AVRMaskState pwr[POWER_MAX]; > > AVRUsartState usart[USART_MAX]; > > AVRTimer16State timer[TIMER_MAX]; > > + AVRWatchdogState wdt; > > uint64_t xtal_freq_hz; > > }; > > > > diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig > > index 66e1d029e3..e0f89d2fe0 100644 > > --- a/hw/watchdog/Kconfig > > +++ b/hw/watchdog/Kconfig > > @@ -20,3 +20,6 @@ config WDT_IMX2 > > > > config WDT_SBSA > > bool > > + > > +config AVR_WDT > > + bool > > diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c > > new file mode 100644 > > index 0000000000..4ce1029a64 > > --- /dev/null > > +++ b/hw/watchdog/avr_wdt.c > > @@ -0,0 +1,190 @@ > > +/* > > + * AVR watchdog > > + * > > + * Copyright (c) 2018 Michael Rolnik > > 2021? > > > + * > > + * This library is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU Lesser General Public > > + * License as published by the Free Software Foundation; either > > + * version 2.1 of the License, or (at your option) any later version. > > + * > > + * This library 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 > > + * Lesser General Public License for more details. > > + * > > + * You should have received a copy of the GNU Lesser General Public > > + * License along with this library; if not, see > > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > > + */ > > + > > +#include "qemu/osdep.h" > > +#include "qapi/error.h" > > +#include "qemu/log.h" > > +#include "hw/irq.h" > > +#include "hw/watchdog/avr_wdt.h" > > +#include "trace.h" > > + > > +/* Field masks */ > > +#define WDTCSR_MASK_WDP0 0x01 > > +#define WDTCSR_MASK_WDP1 0x02 > > +#define WDTCSR_MASK_WDP2 0x04 > > +#define WDTCSR_MASK_WDE 0x08 > > +#define WDTCSR_MASK_WCE 0x10 > > +#define WDTCSR_MASK_WDP3 0x20 > > +#define WDTCSR_MASK_WDIE 0x40 > > +#define WDTCSR_MASK_WDIF 0x80 > > + > > +#define WDTCSR_SHFT_WDP0 0x00 > > +#define WDTCSR_SHFT_WDP1 0x01 > > +#define WDTCSR_SHFT_WDP2 0x02 > > +#define WDTCSR_SHFT_WDE 0x03 > > +#define WDTCSR_SHFT_WCE 0x04 > > +#define WDTCSR_SHFT_WDP3 0x05 > > +#define WDTCSR_SHFT_WDIE 0x06 > > +#define WDTCSR_SHFT_WDIF 0x07 > > + > > +/* Helper macros */ > > +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> WDTCSR_SHFT_WDP0) > > +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> WDTCSR_SHFT_WDP1) > > +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> WDTCSR_SHFT_WDP2) > > +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> WDTCSR_SHFT_WDP3) > > +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ > > + (WDP1(csr) << 1) | (WDP0(csr) << 0)) > > +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> WDTCSR_SHFT_WDIE) > > +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> WDTCSR_SHFT_WDE) > > +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> WDTCSR_SHFT_WCE) > > + > > +#define DB_PRINT(fmt, args...) /* Nothing */ > > + > > +#define MS2NS(n) ((n) * 1000000ull) > > + > > +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) > > +{ > > + uint32_t csr = wdt->csr; > > + int wdp = WDP(csr); > > + assert(wdp <= 9); > > Maybe qemu_log(..) instead and pick a default value? > > > + > > + if (WDIE(csr) == 0 && WDE(csr) == 0) { > > + /* watchdog is stopped */ > > + return; > > + } > > + > > + timer_mod_ns(wdt->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > > + (MS2NS(15) << wdp)); > > +} > > + > > +static void avr_wdt_interrupt(void *opaque) > > +{ > > + AVRWatchdogState *wdt = opaque; > > + int8_t csr = wdt->csr; > > + > > + if (WDE(csr) == 0 && WDIE(csr) == 0) { > > + /* Stopped */ > > + > > + } else if (WDE(csr) == 0 && WDIE(csr) == 1) { > > + /* Interrupt Mode */ > > + wdt->csr |= WDTCSR_MASK_WDIF; > > + qemu_set_irq(wdt->irq, 1); > > + trace_avr_wdt_interrupt(); > > + } else if (WDE(csr) == 1 && WDIE(csr) == 0) { > > + /* System Reset Mode */ > > qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); > > +/- setting the MCUSR, WDRF flags would be nice. > > > + } else if (WDE(csr) == 1 && WDIE(csr) == 1) { > > + /* Interrupt and System Reset Mode */ > > + wdt->csr |= WDTCSR_MASK_WDIF; > > + qemu_set_irq(wdt->irq, 1); > > + trace_avr_wdt_interrupt(); > > + } > > + > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static void avr_wdt_reset(DeviceState *dev) > > +{ > > + AVRWatchdogState *wdt = AVR_WDT(dev); > > + > > + wdt->csr = 0; > > + qemu_set_irq(wdt->irq, 0); > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, unsigned size) > > +{ > > + assert(size == 1); > > + AVRWatchdogState *wdt = opaque; > > + uint8_t retval = wdt->csr; > > + > > + trace_avr_wdt_read(offset, retval); > > + > > + return (uint64_t)retval; > > +} > > + > > +static void avr_wdt_write(void *opaque, hwaddr offset, > > + uint64_t val64, unsigned size) > > +{ > > + assert(size == 1); > > + AVRWatchdogState *wdt = opaque; > > + uint8_t val8 = (uint8_t)val64; > > + > > + trace_avr_wdt_write(offset, val8); > > + > > + wdt->csr = val8; > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static const MemoryRegionOps avr_wdt_ops = { > > + .read = avr_wdt_read, > > + .write = avr_wdt_write, > > + .endianness = DEVICE_NATIVE_ENDIAN, > > + .impl = {.max_access_size = 1} > > +}; > > + > > +static void avr_wdt_wdr(void *opaque, int irq, int level) > > +{ > > + AVRWatchdogState *wdt = AVR_WDT(opaque); > > + > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static void avr_wdt_init(Object *obj) > > +{ > > + AVRWatchdogState *s = AVR_WDT(obj); > > + > > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); > > + > > + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, > > + s, "avr-wdt", 0xa); > > + > > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); > > + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); > > +} > > + > > +static void avr_wdt_realize(DeviceState *dev, Error **errp) > > +{ > > + AVRWatchdogState *s = AVR_WDT(dev); > > + > > + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); > > +} > > + > > +static void avr_wdt_class_init(ObjectClass *klass, void *data) > > +{ > > + DeviceClass *dc = DEVICE_CLASS(klass); > > + > > + dc->reset = avr_wdt_reset; > > + dc->realize = avr_wdt_realize; > > +} > > + > > +static const TypeInfo avr_wdt_info = { > > + .name = TYPE_AVR_WDT, > > + .parent = TYPE_SYS_BUS_DEVICE, > > + .instance_size = sizeof(AVRWatchdogState), > > + .instance_init = avr_wdt_init, > > + .class_init = avr_wdt_class_init, > > +}; > > + > > +static void avr_wdt_register_types(void) > > +{ > > + type_register_static(&avr_wdt_info); > > +} > > + > > +type_init(avr_wdt_register_types) > > diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build > > index 054c403dea..8db2be8317 100644 > > --- a/hw/watchdog/meson.build > > +++ b/hw/watchdog/meson.build > > @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: > files('wdt_diag288.c')) > > softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: > files('wdt_aspeed.c')) > > softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) > > softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) > > + > > +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: files('avr_wdt.c')) > > diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events > > index 3124ca1f1b..ac14773179 100644 > > --- a/hw/watchdog/trace-events > > +++ b/hw/watchdog/trace-events > > @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, > unsigned size) "CMSDK AP > > cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned > size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " > size %u" > > cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" > > cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" > PRIu32 > > + > > +# avr_wdt.c > > +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u value:%u" > > +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u value:%u" > > +avr_wdt_interrupt(void) "" > > diff --git a/include/hw/watchdog/avr_wdt.h > b/include/hw/watchdog/avr_wdt.h > > new file mode 100644 > > index 0000000000..2679e8f2a6 > > --- /dev/null > > +++ b/include/hw/watchdog/avr_wdt.h > > @@ -0,0 +1,47 @@ > > +/* > > + * AVR 16-bit timer > > AVR Watchdog? > > > + * > > + * Copyright (c) 2021 Michael Rolnik > > + * > > + * This library is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU Lesser General Public > > + * License as published by the Free Software Foundation; either > > + * version 2.1 of the License, or (at your option) any later version. > > + * > > + * This library 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 > > + * Lesser General Public License for more details. > > + * > > + * You should have received a copy of the GNU Lesser General Public > > + * License along with this library; if not, see > > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > > + */ > > + > > +#ifndef HW_WATCHDOG_AVR_WDT_H > > +#define HW_WATCHDOG_AVR_WDT_H > > + > > +#include "hw/sysbus.h" > > +#include "qemu/timer.h" > > +#include "hw/hw.h" > > +#include "qom/object.h" > > + > > +#define TYPE_AVR_WDT "avr-wdt" > > +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) > > + > > +struct AVRWatchdogState { > > + /* <private> */ > > + SysBusDevice parent_obj; > > + > > + /* <public> */ > > + MemoryRegion iomem; > > + MemoryRegion imsk_iomem; > > + MemoryRegion ifr_iomem; > > + QEMUTimer *timer; > > + qemu_irq irq; > > + > > + /* registers */ > > + uint8_t csr; > > +}; > > + > > +#endif /* HW_WATCHDOG_AVR_WDT_H */ > > diff --git a/target/avr/cpu.c b/target/avr/cpu.c > > index 0f4596932b..d5eb785833 100644 > > --- a/target/avr/cpu.c > > +++ b/target/avr/cpu.c > > @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) > > /* Set the number of interrupts supported by the CPU. */ > > qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, > > sizeof(cpu->env.intsrc) * 8); > > + > > + /* register watchdog timer reset interrupt */ > > + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); > > } > > > > static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) > > diff --git a/target/avr/cpu.h b/target/avr/cpu.h > > index d148e8c75a..f8f5641c8b 100644 > > --- a/target/avr/cpu.h > > +++ b/target/avr/cpu.h > > @@ -152,6 +152,7 @@ typedef struct AVRCPU { > > > > CPUNegativeOffsetState neg; > > CPUAVRState env; > > + qemu_irq wdr; /* reset WDT */ > > } AVRCPU; > > > > extern const struct VMStateDescription vms_avr_cpu; > > diff --git a/target/avr/helper.c b/target/avr/helper.c > > index 35e1019594..dd88057e5f 100644 > > --- a/target/avr/helper.c > > +++ b/target/avr/helper.c > > @@ -24,6 +24,7 @@ > > #include "exec/exec-all.h" > > #include "exec/address-spaces.h" > > #include "exec/helper-proto.h" > > +#include "hw/irq.h" > > > > bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) > > { > > @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) > > > > void helper_wdr(CPUAVRState *env) > > { > > - CPUState *cs = env_cpu(env); > > + AVRCPU *cpu = env_archcpu(env); > > > > - /* WD is not implemented yet, placeholder */ > > - cs->exception_index = EXCP_DEBUG; > > - cpu_loop_exit(cs); > > + qemu_set_irq(cpu->wdr, 1); > > } > > > > /* > > > > Thanks! > -- Best Regards, Michael Rolnik [-- Attachment #2: Type: text/html, Size: 22356 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 1/1] Implement AVR watchdog timer 2021-05-03 13:36 ` Michael Rolnik @ 2021-05-03 20:08 ` Michael Rolnik 2021-05-15 9:45 ` Philippe Mathieu-Daudé 0 siblings, 1 reply; 11+ messages in thread From: Michael Rolnik @ 2021-05-03 20:08 UTC (permalink / raw) To: Fred Konrad Cc: Joaquin de Andres, Richard Henderson, QEMU Developers, Philippe Mathieu-Daudé [-- Attachment #1: Type: text/plain, Size: 18944 bytes --] Hi all, I was about to make icount work. but, there is something I still don't understand. I have this code timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); and then void avr_wdt_interrupt(/* some arguments */) { #define MS2NS(n) ((n) * 1000000ull) timer_mod_ns(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + MS2NS(15)); } when running with --icount shift=0, *avr_wdt_interrupt* is called about every 1K instructions, however it should have been called 15M instructions as shift=0 makes every instruction to be executed in 1 virtual ns. What am I doing wrong? Thank you, Michael Rolnik On Mon, May 3, 2021 at 4:36 PM Michael Rolnik <mrolnik@gmail.com> wrote: > Hi Fred. > > 1. thanks > 2. It seems I have forgotten to set those flags. > 3. 15ms is easy to test 8s will take 533 times longer, so in my case 3200 > instructions which is totally incorrect. I don't understand why as > I program the timer in virtual nanoseconds and not host time. > 4. I hope Richard could help with icount. > > best regards, > Michael Rolnik > > On Mon, May 3, 2021 at 4:15 PM Fred Konrad <konrad@adacore.com> wrote: > >> >> >> Le 5/2/21 à 10:10 PM, Michael Rolnik a écrit : >> > Signed-off-by: Michael Rolnik <mrolnik@gmail.com> >> > --- >> > hw/avr/Kconfig | 1 + >> > hw/avr/atmega.c | 15 ++- >> > hw/avr/atmega.h | 2 + >> > hw/watchdog/Kconfig | 3 + >> > hw/watchdog/avr_wdt.c | 190 ++++++++++++++++++++++++++++++++++ >> > hw/watchdog/meson.build | 2 + >> > hw/watchdog/trace-events | 5 + >> > include/hw/watchdog/avr_wdt.h | 47 +++++++++ >> > target/avr/cpu.c | 3 + >> > target/avr/cpu.h | 1 + >> > target/avr/helper.c | 7 +- >> > 11 files changed, 271 insertions(+), 5 deletions(-) >> > create mode 100644 hw/watchdog/avr_wdt.c >> > create mode 100644 include/hw/watchdog/avr_wdt.h >> > >> > diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig >> > index d31298c3cc..9939e4902f 100644 >> > --- a/hw/avr/Kconfig >> > +++ b/hw/avr/Kconfig >> > @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU >> > select AVR_TIMER16 >> > select AVR_USART >> > select AVR_POWER >> > + select AVR_WDT >> > >> > config ARDUINO >> > select AVR_ATMEGA_MCU >> > diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c >> > index 44c6afebbb..31ceb1c21c 100644 >> > --- a/hw/avr/atmega.c >> > +++ b/hw/avr/atmega.c >> > @@ -28,6 +28,7 @@ enum AtmegaPeripheral { >> > GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, >> > USART0, USART1, USART2, USART3, >> > TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, >> > + WDT, >> > PERIFMAX >> > }; >> > >> > @@ -75,6 +76,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { >> > [GPIOD] = { 0x29 }, >> > [GPIOC] = { 0x26 }, >> > [GPIOB] = { 0x23 }, >> > + [WDT] = { 0x60 }, >> > }, dev1280_2560[PERIFMAX] = { >> > [USART3] = { 0x130, POWER1, 2 }, >> > [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, >> > @@ -99,6 +101,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { >> > [GPIOC] = { 0x26 }, >> > [GPIOB] = { 0x23 }, >> > [GPIOA] = { 0x20 }, >> > + [WDT] = { 0x60 }, >> > }; >> > >> > enum AtmegaIrq { >> > @@ -118,6 +121,7 @@ enum AtmegaIrq { >> > TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, >> > TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, >> > TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, >> > + WATCHDOG_TIMER_IRQ, >> > IRQ_COUNT >> > }; >> > >> > @@ -133,6 +137,7 @@ enum AtmegaIrq { >> > #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) >> > >> > static const uint8_t irq168_328[IRQ_COUNT] = { >> > + [WATCHDOG_TIMER_IRQ] = 7, >> > [TIMER2_COMPA_IRQ] = 8, >> > [TIMER2_COMPB_IRQ] = 9, >> > [TIMER2_OVF_IRQ] = 10, >> > @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { >> > [USART0_DRE_IRQ] = 20, >> > [USART0_TXC_IRQ] = 21, >> > }, irq1280_2560[IRQ_COUNT] = { >> > + [WATCHDOG_TIMER_IRQ] = 13, >> > [TIMER2_COMPA_IRQ] = 14, >> > [TIMER2_COMPB_IRQ] = 15, >> > [TIMER2_OVF_IRQ] = 16, >> > @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState *dev, >> Error **errp) >> > g_free(devname); >> > } >> > >> > + /* Watchdog Timer */ >> > + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, TYPE_AVR_WDT); >> > + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); >> > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, >> > + OFFSET_DATA + mc->dev[WDT].addr); >> > + qdev_connect_gpio_out_named(cpudev, "wdr", 0, >> > + qdev_get_gpio_in_named(DEVICE(&s->wdt), "wdr", 0)); >> > + >> > create_unimplemented_device("avr-twi", OFFSET_DATA + >> 0x0b8, 6); >> > create_unimplemented_device("avr-adc", OFFSET_DATA + >> 0x078, 8); >> > create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + >> 0x074, 2); >> > - create_unimplemented_device("avr-watchdog", OFFSET_DATA + >> 0x060, 1); >> > create_unimplemented_device("avr-spi", OFFSET_DATA + >> 0x04c, 3); >> > create_unimplemented_device("avr-eeprom", OFFSET_DATA + >> 0x03f, 3); >> > } >> > diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h >> > index a99ee15c7e..60bbd44bdd 100644 >> > --- a/hw/avr/atmega.h >> > +++ b/hw/avr/atmega.h >> > @@ -13,6 +13,7 @@ >> > >> > #include "hw/char/avr_usart.h" >> > #include "hw/timer/avr_timer16.h" >> > +#include "hw/watchdog/avr_wdt.h" >> > #include "hw/misc/avr_power.h" >> > #include "target/avr/cpu.h" >> > #include "qom/object.h" >> > @@ -45,6 +46,7 @@ struct AtmegaMcuState { >> > AVRMaskState pwr[POWER_MAX]; >> > AVRUsartState usart[USART_MAX]; >> > AVRTimer16State timer[TIMER_MAX]; >> > + AVRWatchdogState wdt; >> > uint64_t xtal_freq_hz; >> > }; >> > >> > diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig >> > index 66e1d029e3..e0f89d2fe0 100644 >> > --- a/hw/watchdog/Kconfig >> > +++ b/hw/watchdog/Kconfig >> > @@ -20,3 +20,6 @@ config WDT_IMX2 >> > >> > config WDT_SBSA >> > bool >> > + >> > +config AVR_WDT >> > + bool >> > diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c >> > new file mode 100644 >> > index 0000000000..4ce1029a64 >> > --- /dev/null >> > +++ b/hw/watchdog/avr_wdt.c >> > @@ -0,0 +1,190 @@ >> > +/* >> > + * AVR watchdog >> > + * >> > + * Copyright (c) 2018 Michael Rolnik >> >> 2021? >> >> > + * >> > + * This library is free software; you can redistribute it and/or >> > + * modify it under the terms of the GNU Lesser General Public >> > + * License as published by the Free Software Foundation; either >> > + * version 2.1 of the License, or (at your option) any later version. >> > + * >> > + * This library 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 >> > + * Lesser General Public License for more details. >> > + * >> > + * You should have received a copy of the GNU Lesser General Public >> > + * License along with this library; if not, see >> > + * <http://www.gnu.org/licenses/lgpl-2.1.html> >> > + */ >> > + >> > +#include "qemu/osdep.h" >> > +#include "qapi/error.h" >> > +#include "qemu/log.h" >> > +#include "hw/irq.h" >> > +#include "hw/watchdog/avr_wdt.h" >> > +#include "trace.h" >> > + >> > +/* Field masks */ >> > +#define WDTCSR_MASK_WDP0 0x01 >> > +#define WDTCSR_MASK_WDP1 0x02 >> > +#define WDTCSR_MASK_WDP2 0x04 >> > +#define WDTCSR_MASK_WDE 0x08 >> > +#define WDTCSR_MASK_WCE 0x10 >> > +#define WDTCSR_MASK_WDP3 0x20 >> > +#define WDTCSR_MASK_WDIE 0x40 >> > +#define WDTCSR_MASK_WDIF 0x80 >> > + >> > +#define WDTCSR_SHFT_WDP0 0x00 >> > +#define WDTCSR_SHFT_WDP1 0x01 >> > +#define WDTCSR_SHFT_WDP2 0x02 >> > +#define WDTCSR_SHFT_WDE 0x03 >> > +#define WDTCSR_SHFT_WCE 0x04 >> > +#define WDTCSR_SHFT_WDP3 0x05 >> > +#define WDTCSR_SHFT_WDIE 0x06 >> > +#define WDTCSR_SHFT_WDIF 0x07 >> > + >> > +/* Helper macros */ >> > +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> WDTCSR_SHFT_WDP0) >> > +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> WDTCSR_SHFT_WDP1) >> > +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> WDTCSR_SHFT_WDP2) >> > +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> WDTCSR_SHFT_WDP3) >> > +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ >> > + (WDP1(csr) << 1) | (WDP0(csr) << 0)) >> > +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> WDTCSR_SHFT_WDIE) >> > +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> WDTCSR_SHFT_WDE) >> > +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> WDTCSR_SHFT_WCE) >> > + >> > +#define DB_PRINT(fmt, args...) /* Nothing */ >> > + >> > +#define MS2NS(n) ((n) * 1000000ull) >> > + >> > +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) >> > +{ >> > + uint32_t csr = wdt->csr; >> > + int wdp = WDP(csr); >> > + assert(wdp <= 9); >> >> Maybe qemu_log(..) instead and pick a default value? >> >> > + >> > + if (WDIE(csr) == 0 && WDE(csr) == 0) { >> > + /* watchdog is stopped */ >> > + return; >> > + } >> > + >> > + timer_mod_ns(wdt->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + >> > + (MS2NS(15) << wdp)); >> > +} >> > + >> > +static void avr_wdt_interrupt(void *opaque) >> > +{ >> > + AVRWatchdogState *wdt = opaque; >> > + int8_t csr = wdt->csr; >> > + >> > + if (WDE(csr) == 0 && WDIE(csr) == 0) { >> > + /* Stopped */ >> > + >> > + } else if (WDE(csr) == 0 && WDIE(csr) == 1) { >> > + /* Interrupt Mode */ >> > + wdt->csr |= WDTCSR_MASK_WDIF; >> > + qemu_set_irq(wdt->irq, 1); >> > + trace_avr_wdt_interrupt(); >> > + } else if (WDE(csr) == 1 && WDIE(csr) == 0) { >> > + /* System Reset Mode */ >> >> qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); >> >> +/- setting the MCUSR, WDRF flags would be nice. >> >> > + } else if (WDE(csr) == 1 && WDIE(csr) == 1) { >> > + /* Interrupt and System Reset Mode */ >> > + wdt->csr |= WDTCSR_MASK_WDIF; >> > + qemu_set_irq(wdt->irq, 1); >> > + trace_avr_wdt_interrupt(); >> > + } >> > + >> > + avr_wdt_reset_alarm(wdt); >> > +} >> > + >> > +static void avr_wdt_reset(DeviceState *dev) >> > +{ >> > + AVRWatchdogState *wdt = AVR_WDT(dev); >> > + >> > + wdt->csr = 0; >> > + qemu_set_irq(wdt->irq, 0); >> > + avr_wdt_reset_alarm(wdt); >> > +} >> > + >> > +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, unsigned >> size) >> > +{ >> > + assert(size == 1); >> > + AVRWatchdogState *wdt = opaque; >> > + uint8_t retval = wdt->csr; >> > + >> > + trace_avr_wdt_read(offset, retval); >> > + >> > + return (uint64_t)retval; >> > +} >> > + >> > +static void avr_wdt_write(void *opaque, hwaddr offset, >> > + uint64_t val64, unsigned size) >> > +{ >> > + assert(size == 1); >> > + AVRWatchdogState *wdt = opaque; >> > + uint8_t val8 = (uint8_t)val64; >> > + >> > + trace_avr_wdt_write(offset, val8); >> > + >> > + wdt->csr = val8; >> > + avr_wdt_reset_alarm(wdt); >> > +} >> > + >> > +static const MemoryRegionOps avr_wdt_ops = { >> > + .read = avr_wdt_read, >> > + .write = avr_wdt_write, >> > + .endianness = DEVICE_NATIVE_ENDIAN, >> > + .impl = {.max_access_size = 1} >> > +}; >> > + >> > +static void avr_wdt_wdr(void *opaque, int irq, int level) >> > +{ >> > + AVRWatchdogState *wdt = AVR_WDT(opaque); >> > + >> > + avr_wdt_reset_alarm(wdt); >> > +} >> > + >> > +static void avr_wdt_init(Object *obj) >> > +{ >> > + AVRWatchdogState *s = AVR_WDT(obj); >> > + >> > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); >> > + >> > + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, >> > + s, "avr-wdt", 0xa); >> > + >> > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); >> > + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); >> > +} >> > + >> > +static void avr_wdt_realize(DeviceState *dev, Error **errp) >> > +{ >> > + AVRWatchdogState *s = AVR_WDT(dev); >> > + >> > + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); >> > +} >> > + >> > +static void avr_wdt_class_init(ObjectClass *klass, void *data) >> > +{ >> > + DeviceClass *dc = DEVICE_CLASS(klass); >> > + >> > + dc->reset = avr_wdt_reset; >> > + dc->realize = avr_wdt_realize; >> > +} >> > + >> > +static const TypeInfo avr_wdt_info = { >> > + .name = TYPE_AVR_WDT, >> > + .parent = TYPE_SYS_BUS_DEVICE, >> > + .instance_size = sizeof(AVRWatchdogState), >> > + .instance_init = avr_wdt_init, >> > + .class_init = avr_wdt_class_init, >> > +}; >> > + >> > +static void avr_wdt_register_types(void) >> > +{ >> > + type_register_static(&avr_wdt_info); >> > +} >> > + >> > +type_init(avr_wdt_register_types) >> > diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build >> > index 054c403dea..8db2be8317 100644 >> > --- a/hw/watchdog/meson.build >> > +++ b/hw/watchdog/meson.build >> > @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: >> files('wdt_diag288.c')) >> > softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: >> files('wdt_aspeed.c')) >> > softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) >> > softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) >> > + >> > +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: files('avr_wdt.c')) >> > diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events >> > index 3124ca1f1b..ac14773179 100644 >> > --- a/hw/watchdog/trace-events >> > +++ b/hw/watchdog/trace-events >> > @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, >> unsigned size) "CMSDK AP >> > cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned >> size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " >> size %u" >> > cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" >> > cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" >> PRIu32 >> > + >> > +# avr_wdt.c >> > +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u value:%u" >> > +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u value:%u" >> > +avr_wdt_interrupt(void) "" >> > diff --git a/include/hw/watchdog/avr_wdt.h >> b/include/hw/watchdog/avr_wdt.h >> > new file mode 100644 >> > index 0000000000..2679e8f2a6 >> > --- /dev/null >> > +++ b/include/hw/watchdog/avr_wdt.h >> > @@ -0,0 +1,47 @@ >> > +/* >> > + * AVR 16-bit timer >> >> AVR Watchdog? >> >> > + * >> > + * Copyright (c) 2021 Michael Rolnik >> > + * >> > + * This library is free software; you can redistribute it and/or >> > + * modify it under the terms of the GNU Lesser General Public >> > + * License as published by the Free Software Foundation; either >> > + * version 2.1 of the License, or (at your option) any later version. >> > + * >> > + * This library 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 >> > + * Lesser General Public License for more details. >> > + * >> > + * You should have received a copy of the GNU Lesser General Public >> > + * License along with this library; if not, see >> > + * <http://www.gnu.org/licenses/lgpl-2.1.html> >> > + */ >> > + >> > +#ifndef HW_WATCHDOG_AVR_WDT_H >> > +#define HW_WATCHDOG_AVR_WDT_H >> > + >> > +#include "hw/sysbus.h" >> > +#include "qemu/timer.h" >> > +#include "hw/hw.h" >> > +#include "qom/object.h" >> > + >> > +#define TYPE_AVR_WDT "avr-wdt" >> > +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) >> > + >> > +struct AVRWatchdogState { >> > + /* <private> */ >> > + SysBusDevice parent_obj; >> > + >> > + /* <public> */ >> > + MemoryRegion iomem; >> > + MemoryRegion imsk_iomem; >> > + MemoryRegion ifr_iomem; >> > + QEMUTimer *timer; >> > + qemu_irq irq; >> > + >> > + /* registers */ >> > + uint8_t csr; >> > +}; >> > + >> > +#endif /* HW_WATCHDOG_AVR_WDT_H */ >> > diff --git a/target/avr/cpu.c b/target/avr/cpu.c >> > index 0f4596932b..d5eb785833 100644 >> > --- a/target/avr/cpu.c >> > +++ b/target/avr/cpu.c >> > @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) >> > /* Set the number of interrupts supported by the CPU. */ >> > qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, >> > sizeof(cpu->env.intsrc) * 8); >> > + >> > + /* register watchdog timer reset interrupt */ >> > + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); >> > } >> > >> > static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) >> > diff --git a/target/avr/cpu.h b/target/avr/cpu.h >> > index d148e8c75a..f8f5641c8b 100644 >> > --- a/target/avr/cpu.h >> > +++ b/target/avr/cpu.h >> > @@ -152,6 +152,7 @@ typedef struct AVRCPU { >> > >> > CPUNegativeOffsetState neg; >> > CPUAVRState env; >> > + qemu_irq wdr; /* reset WDT */ >> > } AVRCPU; >> > >> > extern const struct VMStateDescription vms_avr_cpu; >> > diff --git a/target/avr/helper.c b/target/avr/helper.c >> > index 35e1019594..dd88057e5f 100644 >> > --- a/target/avr/helper.c >> > +++ b/target/avr/helper.c >> > @@ -24,6 +24,7 @@ >> > #include "exec/exec-all.h" >> > #include "exec/address-spaces.h" >> > #include "exec/helper-proto.h" >> > +#include "hw/irq.h" >> > >> > bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) >> > { >> > @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) >> > >> > void helper_wdr(CPUAVRState *env) >> > { >> > - CPUState *cs = env_cpu(env); >> > + AVRCPU *cpu = env_archcpu(env); >> > >> > - /* WD is not implemented yet, placeholder */ >> > - cs->exception_index = EXCP_DEBUG; >> > - cpu_loop_exit(cs); >> > + qemu_set_irq(cpu->wdr, 1); >> > } >> > >> > /* >> > >> >> Thanks! >> > > > -- > Best Regards, > Michael Rolnik > -- Best Regards, Michael Rolnik [-- Attachment #2: Type: text/html, Size: 24044 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 1/1] Implement AVR watchdog timer 2021-05-03 20:08 ` Michael Rolnik @ 2021-05-15 9:45 ` Philippe Mathieu-Daudé 0 siblings, 0 replies; 11+ messages in thread From: Philippe Mathieu-Daudé @ 2021-05-15 9:45 UTC (permalink / raw) To: Michael Rolnik, Fred Konrad Cc: Alex Bennée, Joaquin de Andres, Richard Henderson, QEMU Developers, Pavel Dovgalyuk +Pavel/Alex On 5/3/21 10:08 PM, Michael Rolnik wrote: > Hi all, > > I was about to make icount work. but, there is something I still don't > understand. I have this code > > timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); > > and then > > void avr_wdt_interrupt(/* some arguments */) { > #define MS2NS(n) ((n) * 1000000ull) > timer_mod_ns(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + MS2NS(15)); > } > > when running with --icount shift=0, *avr_wdt_interrupt* is called about > every 1K instructions, however it should have been called 15M > instructions as shift=0 makes every instruction to be executed in 1 > virtual ns. > > What am I doing wrong? > > Thank you, > Michael Rolnik > > > On Mon, May 3, 2021 at 4:36 PM Michael Rolnik <mrolnik@gmail.com > <mailto:mrolnik@gmail.com>> wrote: > > Hi Fred. > > 1. thanks > 2. It seems I have forgotten to set those flags. > 3. 15ms is easy to test 8s will take 533 times longer, so in my case > 3200 instructions which is totally incorrect. I don't understand why > as I program the timer in virtual nanoseconds and not host time. > 4. I hope Richard could help with icount. > > best regards, > Michael Rolnik > > On Mon, May 3, 2021 at 4:15 PM Fred Konrad <konrad@adacore.com > <mailto:konrad@adacore.com>> wrote: > > > > Le 5/2/21 à 10:10 PM, Michael Rolnik a écrit : > > Signed-off-by: Michael Rolnik <mrolnik@gmail.com > <mailto:mrolnik@gmail.com>> > > --- > > hw/avr/Kconfig | 1 + > > hw/avr/atmega.c | 15 ++- > > hw/avr/atmega.h | 2 + > > hw/watchdog/Kconfig | 3 + > > hw/watchdog/avr_wdt.c | 190 > ++++++++++++++++++++++++++++++++++ > > hw/watchdog/meson.build | 2 + > > hw/watchdog/trace-events | 5 + > > include/hw/watchdog/avr_wdt.h | 47 +++++++++ > > target/avr/cpu.c | 3 + > > target/avr/cpu.h | 1 + > > target/avr/helper.c | 7 +- > > 11 files changed, 271 insertions(+), 5 deletions(-) > > create mode 100644 hw/watchdog/avr_wdt.c > > create mode 100644 include/hw/watchdog/avr_wdt.h > > > > diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig > > index d31298c3cc..9939e4902f 100644 > > --- a/hw/avr/Kconfig > > +++ b/hw/avr/Kconfig > > @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU > > select AVR_TIMER16 > > select AVR_USART > > select AVR_POWER > > + select AVR_WDT > > > > config ARDUINO > > select AVR_ATMEGA_MCU > > diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c > > index 44c6afebbb..31ceb1c21c 100644 > > --- a/hw/avr/atmega.c > > +++ b/hw/avr/atmega.c > > @@ -28,6 +28,7 @@ enum AtmegaPeripheral { > > GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, > > USART0, USART1, USART2, USART3, > > TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, > > + WDT, > > PERIFMAX > > }; > > > > @@ -75,6 +76,7 @@ static const peripheral_cfg > dev168_328[PERIFMAX] = { > > [GPIOD] = { 0x29 }, > > [GPIOC] = { 0x26 }, > > [GPIOB] = { 0x23 }, > > + [WDT] = { 0x60 }, > > }, dev1280_2560[PERIFMAX] = { > > [USART3] = { 0x130, POWER1, 2 }, > > [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, > > @@ -99,6 +101,7 @@ static const peripheral_cfg > dev168_328[PERIFMAX] = { > > [GPIOC] = { 0x26 }, > > [GPIOB] = { 0x23 }, > > [GPIOA] = { 0x20 }, > > + [WDT] = { 0x60 }, > > }; > > > > enum AtmegaIrq { > > @@ -118,6 +121,7 @@ enum AtmegaIrq { > > TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, > > TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, > > TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, > > + WATCHDOG_TIMER_IRQ, > > IRQ_COUNT > > }; > > > > @@ -133,6 +137,7 @@ enum AtmegaIrq { > > #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + > TIMER0_OVF_IRQ) > > > > static const uint8_t irq168_328[IRQ_COUNT] = { > > + [WATCHDOG_TIMER_IRQ] = 7, > > [TIMER2_COMPA_IRQ] = 8, > > [TIMER2_COMPB_IRQ] = 9, > > [TIMER2_OVF_IRQ] = 10, > > @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { > > [USART0_DRE_IRQ] = 20, > > [USART0_TXC_IRQ] = 21, > > }, irq1280_2560[IRQ_COUNT] = { > > + [WATCHDOG_TIMER_IRQ] = 13, > > [TIMER2_COMPA_IRQ] = 14, > > [TIMER2_COMPB_IRQ] = 15, > > [TIMER2_OVF_IRQ] = 16, > > @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState > *dev, Error **errp) > > g_free(devname); > > } > > > > + /* Watchdog Timer */ > > + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, > TYPE_AVR_WDT); > > + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); > > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, > > + OFFSET_DATA + mc->dev[WDT].addr); > > + qdev_connect_gpio_out_named(cpudev, "wdr", 0, > > + qdev_get_gpio_in_named(DEVICE(&s->wdt), > "wdr", 0)); > > + > > create_unimplemented_device("avr-twi", > OFFSET_DATA + 0x0b8, 6); > > create_unimplemented_device("avr-adc", > OFFSET_DATA + 0x078, 8); > > create_unimplemented_device("avr-ext-mem-ctrl", > OFFSET_DATA + 0x074, 2); > > - create_unimplemented_device("avr-watchdog", > OFFSET_DATA + 0x060, 1); > > create_unimplemented_device("avr-spi", > OFFSET_DATA + 0x04c, 3); > > create_unimplemented_device("avr-eeprom", > OFFSET_DATA + 0x03f, 3); > > } > > diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h > > index a99ee15c7e..60bbd44bdd 100644 > > --- a/hw/avr/atmega.h > > +++ b/hw/avr/atmega.h > > @@ -13,6 +13,7 @@ > > > > #include "hw/char/avr_usart.h" > > #include "hw/timer/avr_timer16.h" > > +#include "hw/watchdog/avr_wdt.h" > > #include "hw/misc/avr_power.h" > > #include "target/avr/cpu.h" > > #include "qom/object.h" > > @@ -45,6 +46,7 @@ struct AtmegaMcuState { > > AVRMaskState pwr[POWER_MAX]; > > AVRUsartState usart[USART_MAX]; > > AVRTimer16State timer[TIMER_MAX]; > > + AVRWatchdogState wdt; > > uint64_t xtal_freq_hz; > > }; > > > > diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig > > index 66e1d029e3..e0f89d2fe0 100644 > > --- a/hw/watchdog/Kconfig > > +++ b/hw/watchdog/Kconfig > > @@ -20,3 +20,6 @@ config WDT_IMX2 > > > > config WDT_SBSA > > bool > > + > > +config AVR_WDT > > + bool > > diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c > > new file mode 100644 > > index 0000000000..4ce1029a64 > > --- /dev/null > > +++ b/hw/watchdog/avr_wdt.c > > @@ -0,0 +1,190 @@ > > +/* > > + * AVR watchdog > > + * > > + * Copyright (c) 2018 Michael Rolnik > > 2021? > > > + * > > + * This library is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU Lesser General Public > > + * License as published by the Free Software Foundation; either > > + * version 2.1 of the License, or (at your option) any later > version. > > + * > > + * This library 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 > > + * Lesser General Public License for more details. > > + * > > + * You should have received a copy of the GNU Lesser General > Public > > + * License along with this library; if not, see > > + * <http://www.gnu.org/licenses/lgpl-2.1.html > <http://www.gnu.org/licenses/lgpl-2.1.html>> > > + */ > > + > > +#include "qemu/osdep.h" > > +#include "qapi/error.h" > > +#include "qemu/log.h" > > +#include "hw/irq.h" > > +#include "hw/watchdog/avr_wdt.h" > > +#include "trace.h" > > + > > +/* Field masks */ > > +#define WDTCSR_MASK_WDP0 0x01 > > +#define WDTCSR_MASK_WDP1 0x02 > > +#define WDTCSR_MASK_WDP2 0x04 > > +#define WDTCSR_MASK_WDE 0x08 > > +#define WDTCSR_MASK_WCE 0x10 > > +#define WDTCSR_MASK_WDP3 0x20 > > +#define WDTCSR_MASK_WDIE 0x40 > > +#define WDTCSR_MASK_WDIF 0x80 > > + > > +#define WDTCSR_SHFT_WDP0 0x00 > > +#define WDTCSR_SHFT_WDP1 0x01 > > +#define WDTCSR_SHFT_WDP2 0x02 > > +#define WDTCSR_SHFT_WDE 0x03 > > +#define WDTCSR_SHFT_WCE 0x04 > > +#define WDTCSR_SHFT_WDP3 0x05 > > +#define WDTCSR_SHFT_WDIE 0x06 > > +#define WDTCSR_SHFT_WDIF 0x07 > > + > > +/* Helper macros */ > > +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> > WDTCSR_SHFT_WDP0) > > +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> > WDTCSR_SHFT_WDP1) > > +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> > WDTCSR_SHFT_WDP2) > > +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> > WDTCSR_SHFT_WDP3) > > +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ > > + (WDP1(csr) << 1) | (WDP0(csr) << 0)) > > +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> > WDTCSR_SHFT_WDIE) > > +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> > WDTCSR_SHFT_WDE) > > +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> > WDTCSR_SHFT_WCE) > > + > > +#define DB_PRINT(fmt, args...) /* Nothing */ > > + > > +#define MS2NS(n) ((n) * 1000000ull) > > + > > +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) > > +{ > > + uint32_t csr = wdt->csr; > > + int wdp = WDP(csr); > > + assert(wdp <= 9); > > Maybe qemu_log(..) instead and pick a default value? > > > + > > + if (WDIE(csr) == 0 && WDE(csr) == 0) { > > + /* watchdog is stopped */ > > + return; > > + } > > + > > + timer_mod_ns(wdt->timer, > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > > + (MS2NS(15) << wdp)); > > +} > > + > > +static void avr_wdt_interrupt(void *opaque) > > +{ > > + AVRWatchdogState *wdt = opaque; > > + int8_t csr = wdt->csr; > > + > > + if (WDE(csr) == 0 && WDIE(csr) == 0) { > > + /* Stopped */ > > + > > + } else if (WDE(csr) == 0 && WDIE(csr) == 1) { > > + /* Interrupt Mode */ > > + wdt->csr |= WDTCSR_MASK_WDIF; > > + qemu_set_irq(wdt->irq, 1); > > + trace_avr_wdt_interrupt(); > > + } else if (WDE(csr) == 1 && WDIE(csr) == 0) { > > + /* System Reset Mode */ > > qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); > > +/- setting the MCUSR, WDRF flags would be nice. > > > + } else if (WDE(csr) == 1 && WDIE(csr) == 1) { > > + /* Interrupt and System Reset Mode */ > > + wdt->csr |= WDTCSR_MASK_WDIF; > > + qemu_set_irq(wdt->irq, 1); > > + trace_avr_wdt_interrupt(); > > + } > > + > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static void avr_wdt_reset(DeviceState *dev) > > +{ > > + AVRWatchdogState *wdt = AVR_WDT(dev); > > + > > + wdt->csr = 0; > > + qemu_set_irq(wdt->irq, 0); > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, > unsigned size) > > +{ > > + assert(size == 1); > > + AVRWatchdogState *wdt = opaque; > > + uint8_t retval = wdt->csr; > > + > > + trace_avr_wdt_read(offset, retval); > > + > > + return (uint64_t)retval; > > +} > > + > > +static void avr_wdt_write(void *opaque, hwaddr offset, > > + uint64_t val64, unsigned size) > > +{ > > + assert(size == 1); > > + AVRWatchdogState *wdt = opaque; > > + uint8_t val8 = (uint8_t)val64; > > + > > + trace_avr_wdt_write(offset, val8); > > + > > + wdt->csr = val8; > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static const MemoryRegionOps avr_wdt_ops = { > > + .read = avr_wdt_read, > > + .write = avr_wdt_write, > > + .endianness = DEVICE_NATIVE_ENDIAN, > > + .impl = {.max_access_size = 1} > > +}; > > + > > +static void avr_wdt_wdr(void *opaque, int irq, int level) > > +{ > > + AVRWatchdogState *wdt = AVR_WDT(opaque); > > + > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static void avr_wdt_init(Object *obj) > > +{ > > + AVRWatchdogState *s = AVR_WDT(obj); > > + > > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); > > + > > + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, > > + s, "avr-wdt", 0xa); > > + > > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); > > + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); > > +} > > + > > +static void avr_wdt_realize(DeviceState *dev, Error **errp) > > +{ > > + AVRWatchdogState *s = AVR_WDT(dev); > > + > > + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, > avr_wdt_interrupt, s); > > +} > > + > > +static void avr_wdt_class_init(ObjectClass *klass, void *data) > > +{ > > + DeviceClass *dc = DEVICE_CLASS(klass); > > + > > + dc->reset = avr_wdt_reset; > > + dc->realize = avr_wdt_realize; > > +} > > + > > +static const TypeInfo avr_wdt_info = { > > + .name = TYPE_AVR_WDT, > > + .parent = TYPE_SYS_BUS_DEVICE, > > + .instance_size = sizeof(AVRWatchdogState), > > + .instance_init = avr_wdt_init, > > + .class_init = avr_wdt_class_init, > > +}; > > + > > +static void avr_wdt_register_types(void) > > +{ > > + type_register_static(&avr_wdt_info); > > +} > > + > > +type_init(avr_wdt_register_types) > > diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build > > index 054c403dea..8db2be8317 100644 > > --- a/hw/watchdog/meson.build > > +++ b/hw/watchdog/meson.build > > @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', > if_true: files('wdt_diag288.c')) > > softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: > files('wdt_aspeed.c')) > > softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: > files('wdt_imx2.c')) > > softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: > files('sbsa_gwdt.c')) > > + > > +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: > files('avr_wdt.c')) > > diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events > > index 3124ca1f1b..ac14773179 100644 > > --- a/hw/watchdog/trace-events > > +++ b/hw/watchdog/trace-events > > @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, > uint64_t data, unsigned size) "CMSDK AP > > cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, > unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " > data 0x%" PRIx64 " size %u" > > cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" > > cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: > lock %" PRIu32 > > + > > +# avr_wdt.c > > +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u > value:%u" > > +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u > value:%u" > > +avr_wdt_interrupt(void) "" > > diff --git a/include/hw/watchdog/avr_wdt.h > b/include/hw/watchdog/avr_wdt.h > > new file mode 100644 > > index 0000000000..2679e8f2a6 > > --- /dev/null > > +++ b/include/hw/watchdog/avr_wdt.h > > @@ -0,0 +1,47 @@ > > +/* > > + * AVR 16-bit timer > > AVR Watchdog? > > > + * > > + * Copyright (c) 2021 Michael Rolnik > > + * > > + * This library is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU Lesser General Public > > + * License as published by the Free Software Foundation; either > > + * version 2.1 of the License, or (at your option) any later > version. > > + * > > + * This library 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 > > + * Lesser General Public License for more details. > > + * > > + * You should have received a copy of the GNU Lesser General > Public > > + * License along with this library; if not, see > > + * <http://www.gnu.org/licenses/lgpl-2.1.html > <http://www.gnu.org/licenses/lgpl-2.1.html>> > > + */ > > + > > +#ifndef HW_WATCHDOG_AVR_WDT_H > > +#define HW_WATCHDOG_AVR_WDT_H > > + > > +#include "hw/sysbus.h" > > +#include "qemu/timer.h" > > +#include "hw/hw.h" > > +#include "qom/object.h" > > + > > +#define TYPE_AVR_WDT "avr-wdt" > > +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) > > + > > +struct AVRWatchdogState { > > + /* <private> */ > > + SysBusDevice parent_obj; > > + > > + /* <public> */ > > + MemoryRegion iomem; > > + MemoryRegion imsk_iomem; > > + MemoryRegion ifr_iomem; > > + QEMUTimer *timer; > > + qemu_irq irq; > > + > > + /* registers */ > > + uint8_t csr; > > +}; > > + > > +#endif /* HW_WATCHDOG_AVR_WDT_H */ > > diff --git a/target/avr/cpu.c b/target/avr/cpu.c > > index 0f4596932b..d5eb785833 100644 > > --- a/target/avr/cpu.c > > +++ b/target/avr/cpu.c > > @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) > > /* Set the number of interrupts supported by the CPU. */ > > qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, > > sizeof(cpu->env.intsrc) * 8); > > + > > + /* register watchdog timer reset interrupt */ > > + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); > > } > > > > static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) > > diff --git a/target/avr/cpu.h b/target/avr/cpu.h > > index d148e8c75a..f8f5641c8b 100644 > > --- a/target/avr/cpu.h > > +++ b/target/avr/cpu.h > > @@ -152,6 +152,7 @@ typedef struct AVRCPU { > > > > CPUNegativeOffsetState neg; > > CPUAVRState env; > > + qemu_irq wdr; /* reset WDT */ > > } AVRCPU; > > > > extern const struct VMStateDescription vms_avr_cpu; > > diff --git a/target/avr/helper.c b/target/avr/helper.c > > index 35e1019594..dd88057e5f 100644 > > --- a/target/avr/helper.c > > +++ b/target/avr/helper.c > > @@ -24,6 +24,7 @@ > > #include "exec/exec-all.h" > > #include "exec/address-spaces.h" > > #include "exec/helper-proto.h" > > +#include "hw/irq.h" > > > > bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) > > { > > @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) > > > > void helper_wdr(CPUAVRState *env) > > { > > - CPUState *cs = env_cpu(env); > > + AVRCPU *cpu = env_archcpu(env); > > > > - /* WD is not implemented yet, placeholder */ > > - cs->exception_index = EXCP_DEBUG; > > - cpu_loop_exit(cs); > > + qemu_set_irq(cpu->wdr, 1); > > } > > > > /* > > > > Thanks! > > > > -- > Best Regards, > Michael Rolnik > > > > -- > Best Regards, > Michael Rolnik ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC v1 0/1] Implement AVR WDT (watchdog timer) @ 2021-05-05 21:18 Michael Rolnik 2021-05-05 21:18 ` [RFC 1/1] Implement AVR watchdog timer Michael Rolnik 0 siblings, 1 reply; 11+ messages in thread From: Michael Rolnik @ 2021-05-05 21:18 UTC (permalink / raw) To: qemu-devel; +Cc: Michael Rolnik, me, konrad, richard.henderson, f4bug 1. Initial implementation of AVR WDT There are two issues with this implementation so I need your help here a. when I configure the WDT to fire an interrupt every 15ms it actually happens every 6 instructions b. when I specify --icount shift=0 qemu stucks changes since v1 1. correct RW or RW1C behavior is implemented 2. icount functionality is fixed 3. I still observe something strange, it takes AVR 150 instructions to simulate 15ms *** BLURB HERE *** Michael Rolnik (1): Implement AVR watchdog timer MAINTAINERS | 2 + hw/avr/Kconfig | 1 + hw/avr/atmega.c | 15 +- hw/avr/atmega.h | 2 + hw/watchdog/Kconfig | 3 + hw/watchdog/avr_wdt.c | 274 ++++++++++++++++++++++++++++++++++ hw/watchdog/meson.build | 2 + hw/watchdog/trace-events | 5 + include/hw/watchdog/avr_wdt.h | 47 ++++++ target/avr/cpu.c | 3 + target/avr/cpu.h | 1 + target/avr/helper.c | 7 +- target/avr/translate.c | 38 ++++- 13 files changed, 391 insertions(+), 9 deletions(-) create mode 100644 hw/watchdog/avr_wdt.c create mode 100644 include/hw/watchdog/avr_wdt.h -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC 1/1] Implement AVR watchdog timer 2021-05-05 21:18 [RFC v1 0/1] Implement AVR WDT (watchdog timer) Michael Rolnik @ 2021-05-05 21:18 ` Michael Rolnik 2021-05-13 12:27 ` Pavel Dovgalyuk 0 siblings, 1 reply; 11+ messages in thread From: Michael Rolnik @ 2021-05-05 21:18 UTC (permalink / raw) To: qemu-devel; +Cc: Michael Rolnik, me, konrad, richard.henderson, f4bug Signed-off-by: Michael Rolnik <mrolnik@gmail.com> --- MAINTAINERS | 2 + hw/avr/Kconfig | 1 + hw/avr/atmega.c | 15 +- hw/avr/atmega.h | 2 + hw/watchdog/Kconfig | 3 + hw/watchdog/avr_wdt.c | 274 ++++++++++++++++++++++++++++++++++ hw/watchdog/meson.build | 2 + hw/watchdog/trace-events | 5 + include/hw/watchdog/avr_wdt.h | 47 ++++++ target/avr/cpu.c | 3 + target/avr/cpu.h | 1 + target/avr/helper.c | 7 +- target/avr/translate.c | 38 ++++- 13 files changed, 391 insertions(+), 9 deletions(-) create mode 100644 hw/watchdog/avr_wdt.c create mode 100644 include/hw/watchdog/avr_wdt.h diff --git a/MAINTAINERS b/MAINTAINERS index 4c05ff8bba..e1fce736d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1052,6 +1052,8 @@ F: include/hw/timer/avr_timer16.h F: hw/timer/avr_timer16.c F: include/hw/misc/avr_power.h F: hw/misc/avr_power.c +F: include/hw/watchdog/avr_wdt.h +F: hw/watchdog/avr_wdt.c Arduino M: Philippe Mathieu-Daudé <f4bug@amsat.org> diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig index d31298c3cc..9939e4902f 100644 --- a/hw/avr/Kconfig +++ b/hw/avr/Kconfig @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU select AVR_TIMER16 select AVR_USART select AVR_POWER + select AVR_WDT config ARDUINO select AVR_ATMEGA_MCU diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index 44c6afebbb..31ceb1c21c 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -28,6 +28,7 @@ enum AtmegaPeripheral { GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, USART0, USART1, USART2, USART3, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, + WDT, PERIFMAX }; @@ -75,6 +76,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { [GPIOD] = { 0x29 }, [GPIOC] = { 0x26 }, [GPIOB] = { 0x23 }, + [WDT] = { 0x60 }, }, dev1280_2560[PERIFMAX] = { [USART3] = { 0x130, POWER1, 2 }, [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, @@ -99,6 +101,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { [GPIOC] = { 0x26 }, [GPIOB] = { 0x23 }, [GPIOA] = { 0x20 }, + [WDT] = { 0x60 }, }; enum AtmegaIrq { @@ -118,6 +121,7 @@ enum AtmegaIrq { TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, + WATCHDOG_TIMER_IRQ, IRQ_COUNT }; @@ -133,6 +137,7 @@ enum AtmegaIrq { #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) static const uint8_t irq168_328[IRQ_COUNT] = { + [WATCHDOG_TIMER_IRQ] = 7, [TIMER2_COMPA_IRQ] = 8, [TIMER2_COMPB_IRQ] = 9, [TIMER2_OVF_IRQ] = 10, @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { [USART0_DRE_IRQ] = 20, [USART0_TXC_IRQ] = 21, }, irq1280_2560[IRQ_COUNT] = { + [WATCHDOG_TIMER_IRQ] = 13, [TIMER2_COMPA_IRQ] = 14, [TIMER2_COMPB_IRQ] = 15, [TIMER2_OVF_IRQ] = 16, @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState *dev, Error **errp) g_free(devname); } + /* Watchdog Timer */ + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, TYPE_AVR_WDT); + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, + OFFSET_DATA + mc->dev[WDT].addr); + qdev_connect_gpio_out_named(cpudev, "wdr", 0, + qdev_get_gpio_in_named(DEVICE(&s->wdt), "wdr", 0)); + create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6); create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8); create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2); - create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1); create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3); create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3); } diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h index a99ee15c7e..60bbd44bdd 100644 --- a/hw/avr/atmega.h +++ b/hw/avr/atmega.h @@ -13,6 +13,7 @@ #include "hw/char/avr_usart.h" #include "hw/timer/avr_timer16.h" +#include "hw/watchdog/avr_wdt.h" #include "hw/misc/avr_power.h" #include "target/avr/cpu.h" #include "qom/object.h" @@ -45,6 +46,7 @@ struct AtmegaMcuState { AVRMaskState pwr[POWER_MAX]; AVRUsartState usart[USART_MAX]; AVRTimer16State timer[TIMER_MAX]; + AVRWatchdogState wdt; uint64_t xtal_freq_hz; }; diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig index 66e1d029e3..e0f89d2fe0 100644 --- a/hw/watchdog/Kconfig +++ b/hw/watchdog/Kconfig @@ -20,3 +20,6 @@ config WDT_IMX2 config WDT_SBSA bool + +config AVR_WDT + bool diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c new file mode 100644 index 0000000000..cbd6457c8b --- /dev/null +++ b/hw/watchdog/avr_wdt.c @@ -0,0 +1,274 @@ +/* + * AVR watchdog + * + * Copyright (c) 2021 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/watchdog/avr_wdt.h" +#include "trace.h" +#include "target/avr/cpu.h" +#include "sysemu/runstate.h" + +/* Field masks */ +#define WDTCSR_MASK_WDP0 0x01 +#define WDTCSR_MASK_WDP1 0x02 +#define WDTCSR_MASK_WDP2 0x04 +#define WDTCSR_MASK_WDE 0x08 +#define WDTCSR_MASK_WDCE 0x10 +#define WDTCSR_MASK_WDP3 0x20 +#define WDTCSR_MASK_WDIE 0x40 +#define WDTCSR_MASK_WDIF 0x80 + +#define WDTCSR_SHFT_WDP0 0x00 +#define WDTCSR_SHFT_WDP1 0x01 +#define WDTCSR_SHFT_WDP2 0x02 +#define WDTCSR_SHFT_WDE 0x03 +#define WDTCSR_SHFT_WDCE 0x04 +#define WDTCSR_SHFT_WDP3 0x05 +#define WDTCSR_SHFT_WDIE 0x06 +#define WDTCSR_SHFT_WDIF 0x07 + +#define MCUSR_MASK_WDRF 0x04 +#define MCUSR_ADDR (OFFSET_DATA + 0x55) + +/* Helper macros */ +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> WDTCSR_SHFT_WDP0) +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> WDTCSR_SHFT_WDP1) +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> WDTCSR_SHFT_WDP2) +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> WDTCSR_SHFT_WDP3) +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ + (WDP1(csr) << 1) | (WDP0(csr) << 0)) +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> WDTCSR_SHFT_WDIE) +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> WDTCSR_SHFT_WDE) +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> WDTCSR_SHFT_WCE) + +#define DB_PRINT(fmt, args...) /* Nothing */ + +#define MS2NS(n) ((n) * 1000000ull) + +static void set_bits(uint8_t *addr, uint8_t bits) +{ + *addr |= bits; +} + +static void rst_bits(uint8_t *addr, uint8_t bits) +{ + *addr &= ~bits; +} + +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) +{ + uint32_t csr = wdt->csr; + int wdp = WDP(csr); + + if (WDIE(csr) == 0 && WDE(csr) == 0) { + /* watchdog is stopped */ + return; + } + + timer_mod_ns(wdt->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (MS2NS(15) << wdp)); +} + +static void avr_wdt_interrupt(void *opaque) +{ + AVRWatchdogState *wdt = opaque; + int8_t csr = wdt->csr; + + if (WDIE(csr) == 1) { + /* Interrupt Mode */ + set_bits(&wdt->csr, WDTCSR_MASK_WDIF); + qemu_set_irq(wdt->irq, 1); + trace_avr_wdt_interrupt(); + rst_bits(&wdt->csr, WDTCSR_MASK_WDIF | WDTCSR_MASK_WDIE); + } + + if (WDE(csr) == 1) { + /* System Reset Mode */ + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + + avr_wdt_reset_alarm(wdt); +} + +static void avr_wdt_reset(DeviceState *dev) +{ + AVRWatchdogState *wdt = AVR_WDT(dev); + + wdt->csr = 0; + qemu_set_irq(wdt->irq, 0); + avr_wdt_reset_alarm(wdt); +} + +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, unsigned size) +{ + assert(size == 1); + AVRWatchdogState *wdt = opaque; + uint8_t retval = wdt->csr; + + trace_avr_wdt_read(offset, retval); + + return (uint64_t)retval; +} + +static void avr_wdt_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size == 1); + AVRWatchdogState *wdt = opaque; + uint8_t val = (uint8_t)val64; + uint8_t set1 = val; /* bits that should be set to 1 */ + uint8_t set0 = ~val;/* bits that should be set to 0 */ + uint8_t mcusr = 0; + + /* + * Bit 7 - WDIF: Watchdog Interrupt Flag + * This bit is set when a time-out occurs in the Watchdog Timer and the + * Watchdog Timer is configured for interrupt. WDIF is cleared by hardware + * when executing the corresponding interrupt handling vector. + * Alternatively, WDIF is cleared by writing a logic one to the flag. + * When the I-bit in SREG and WDIE are set, the Watchdog Time-out Interrupt + * is executed. + */ + if (val & WDTCSR_MASK_WDIF) { + rst_bits(&set1, WDTCSR_MASK_WDIF); /* don't set 1 */ + set_bits(&set0, WDTCSR_MASK_WDIF); /* set 0 */ + } else { + rst_bits(&set1, WDTCSR_MASK_WDIF); /* don't set 1 */ + rst_bits(&set0, WDTCSR_MASK_WDIF); /* don't set 0 */ + } + + /* + * Bit 4 - WDCE: Watchdog Change Enable + * This bit is used in timed sequences for changing WDE and prescaler + * bits. To clear the WDE bit, and/or change the prescaler bits, + * WDCE must be set. + * Once written to one, hardware will clear WDCE after four clock cycles. + */ + if (!(val & WDTCSR_MASK_WDCE)) { + uint8_t bits = WDTCSR_MASK_WDE | WDTCSR_MASK_WDP0 | WDTCSR_MASK_WDP1 | + WDTCSR_MASK_WDP2 | WDTCSR_MASK_WDP3; + rst_bits(&set1, bits); + rst_bits(&set0, bits); + } + + /* + * Bit 3 - WDE: Watchdog System Reset Enable + * WDE is overridden by WDRF in MCUSR. This means that WDE is always set + * when WDRF is set. To clear WDE, WDRF must be cleared first. This + * feature ensures multiple resets during conditions causing failure, and + * a safe start-up after the failure. + */ + cpu_physical_memory_read(MCUSR_ADDR, &mcusr, sizeof(mcusr)); + if (mcusr & MCUSR_MASK_WDRF) { + set_bits(&set1, WDTCSR_MASK_WDE); + rst_bits(&set0, WDTCSR_MASK_WDE); + } + + /* update CSR value */ + assert((set0 & set1) == 0); + + val = wdt->csr; + set_bits(&val, set1); + rst_bits(&val, set0); + wdt->csr = val; + trace_avr_wdt_write(offset, val); + avr_wdt_reset_alarm(wdt); + + /* + * Bit 6 - WDIE: Watchdog Interrupt Enable + * When this bit is written to one and the I-bit in the Status Register is + * set, the Watchdog Interrupt is enabled. If WDE is cleared in + * combination with this setting, the Watchdog Timer is in Interrupt Mode, + * and the corresponding interrupt is executed if time-out in the Watchdog + * Timer occurs. + * If WDE is set, the Watchdog Timer is in Interrupt and System Reset Mode. + * The first time-out in the Watchdog Timer will set WDIF. Executing the + * corresponding interrupt vector will clear WDIE and WDIF automatically by + * hardware (the Watchdog goes to System Reset Mode). This is useful for + * keeping the Watchdog Timer security while using the interrupt. To stay + * in Interrupt and System Reset Mode, WDIE must be set after each + * interrupt. This should however not be done within the interrupt service + * routine itself, as this might compromise the safety-function of the + * Watchdog System Reset mode. If the interrupt is not executed before the + * next time-out, a System Reset will be applied. + */ + if ((val & WDTCSR_MASK_WDIE) && (wdt->csr & WDTCSR_MASK_WDIF)) { + avr_wdt_interrupt(opaque); + } +} + +static const MemoryRegionOps avr_wdt_ops = { + .read = avr_wdt_read, + .write = avr_wdt_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = {.max_access_size = 1} +}; + +static void avr_wdt_wdr(void *opaque, int irq, int level) +{ + AVRWatchdogState *wdt = AVR_WDT(opaque); + + avr_wdt_reset_alarm(wdt); +} + +static void avr_wdt_init(Object *obj) +{ + AVRWatchdogState *s = AVR_WDT(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, + s, "avr-wdt", 0xa); + + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); +} + +static void avr_wdt_realize(DeviceState *dev, Error **errp) +{ + AVRWatchdogState *s = AVR_WDT(dev); + + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); +} + +static void avr_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = avr_wdt_reset; + dc->realize = avr_wdt_realize; +} + +static const TypeInfo avr_wdt_info = { + .name = TYPE_AVR_WDT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AVRWatchdogState), + .instance_init = avr_wdt_init, + .class_init = avr_wdt_class_init, +}; + +static void avr_wdt_register_types(void) +{ + type_register_static(&avr_wdt_info); +} + +type_init(avr_wdt_register_types) diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index 054c403dea..8db2be8317 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) + +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: files('avr_wdt.c')) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index 3124ca1f1b..ac14773179 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK AP cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 + +# avr_wdt.c +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u value:%u" +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u value:%u" +avr_wdt_interrupt(void) "" diff --git a/include/hw/watchdog/avr_wdt.h b/include/hw/watchdog/avr_wdt.h new file mode 100644 index 0000000000..8433663013 --- /dev/null +++ b/include/hw/watchdog/avr_wdt.h @@ -0,0 +1,47 @@ +/* + * AVR watchdog + * + * Copyright (c) 2021 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#ifndef HW_WATCHDOG_AVR_WDT_H +#define HW_WATCHDOG_AVR_WDT_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/hw.h" +#include "qom/object.h" + +#define TYPE_AVR_WDT "avr-wdt" +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) + +struct AVRWatchdogState { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + MemoryRegion imsk_iomem; + MemoryRegion ifr_iomem; + QEMUTimer *timer; + qemu_irq irq; + + /* registers */ + uint8_t csr; +}; + +#endif /* HW_WATCHDOG_AVR_WDT_H */ diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 0f4596932b..d5eb785833 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) /* Set the number of interrupts supported by the CPU. */ qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, sizeof(cpu->env.intsrc) * 8); + + /* register watchdog timer reset interrupt */ + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); } static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) diff --git a/target/avr/cpu.h b/target/avr/cpu.h index d148e8c75a..f8f5641c8b 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -152,6 +152,7 @@ typedef struct AVRCPU { CPUNegativeOffsetState neg; CPUAVRState env; + qemu_irq wdr; /* reset WDT */ } AVRCPU; extern const struct VMStateDescription vms_avr_cpu; diff --git a/target/avr/helper.c b/target/avr/helper.c index 35e1019594..dd88057e5f 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "exec/address-spaces.h" #include "exec/helper-proto.h" +#include "hw/irq.h" bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) void helper_wdr(CPUAVRState *env) { - CPUState *cs = env_cpu(env); + AVRCPU *cpu = env_archcpu(env); - /* WD is not implemented yet, placeholder */ - cs->exception_index = EXCP_DEBUG; - cpu_loop_exit(cs); + qemu_set_irq(cpu->wdr, 1); } /* diff --git a/target/avr/translate.c b/target/avr/translate.c index 850c5941d9..b7b716f4a0 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -1411,6 +1411,9 @@ static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) { TCGv temp = tcg_const_i32(a->reg); + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_inb(temp, cpu_env, temp); tcg_gen_andi_tl(temp, temp, 1 << a->bit); ctx->skip_cond = TCG_COND_EQ; @@ -1429,6 +1432,9 @@ static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a) { TCGv temp = tcg_const_i32(a->reg); + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_inb(temp, cpu_env, temp); tcg_gen_andi_tl(temp, temp, 1 << a->bit); ctx->skip_cond = TCG_COND_NE; @@ -1611,6 +1617,9 @@ static TCGv gen_get_zaddr(void) static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) { if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_fullwr(cpu_env, data, addr); } else { tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] = data */ @@ -1620,6 +1629,9 @@ static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr) { if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_fullrd(data, cpu_env, addr); } else { tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data = mem[addr] */ @@ -2325,6 +2337,9 @@ static bool trans_IN(DisasContext *ctx, arg_IN *a) TCGv Rd = cpu_r[a->rd]; TCGv port = tcg_const_i32(a->imm); + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_inb(Rd, cpu_env, port); tcg_temp_free_i32(port); @@ -2341,6 +2356,9 @@ static bool trans_OUT(DisasContext *ctx, arg_OUT *a) TCGv Rd = cpu_r[a->rd]; TCGv port = tcg_const_i32(a->imm); + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_outb(cpu_env, port, Rd); tcg_temp_free_i32(port); @@ -2641,6 +2659,9 @@ static bool trans_SBI(DisasContext *ctx, arg_SBI *a) TCGv data = tcg_temp_new_i32(); TCGv port = tcg_const_i32(a->reg); + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_inb(data, cpu_env, port); tcg_gen_ori_tl(data, data, 1 << a->bit); gen_helper_outb(cpu_env, port, data); @@ -2660,6 +2681,9 @@ static bool trans_CBI(DisasContext *ctx, arg_CBI *a) TCGv data = tcg_temp_new_i32(); TCGv port = tcg_const_i32(a->reg); + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_inb(data, cpu_env, port); tcg_gen_andi_tl(data, data, ~(1 << a->bit)); gen_helper_outb(cpu_env, port, data); @@ -2830,6 +2854,9 @@ static bool trans_SLEEP(DisasContext *ctx, arg_SLEEP *a) */ static bool trans_WDR(DisasContext *ctx, arg_WDR *a) { + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_wdr(cpu_env); return true; @@ -2919,6 +2946,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) * This flag is set by ST/LD instruction we will regenerate it ONLY * with mem/cpu memory access instead of mem access */ + if (tb_cflags(tb) & CF_USE_ICOUNT) { + gen_io_start(); + } max_insns = 1; } if (ctx.singlestep) { @@ -2955,6 +2985,10 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) goto done_generating; } + if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { + gen_io_start(); + } + /* Conditionally skip the next instruction, if indicated. */ if (ctx.skip_cond != TCG_COND_NEVER) { skip_label = gen_new_label(); @@ -2998,10 +3032,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4 && !tcg_op_buf_full()); - if (tb->cflags & CF_LAST_IO) { - gen_io_end(); - } - bool nonconst_skip = canonicalize_skip(&ctx); switch (ctx.bstate) { -- 2.25.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC 1/1] Implement AVR watchdog timer 2021-05-05 21:18 ` [RFC 1/1] Implement AVR watchdog timer Michael Rolnik @ 2021-05-13 12:27 ` Pavel Dovgalyuk 2021-05-14 7:20 ` Michael Rolnik 0 siblings, 1 reply; 11+ messages in thread From: Pavel Dovgalyuk @ 2021-05-13 12:27 UTC (permalink / raw) To: Michael Rolnik, qemu-devel; +Cc: me, konrad, richard.henderson, f4bug On 06.05.2021 00:18, Michael Rolnik wrote: > Signed-off-by: Michael Rolnik <mrolnik@gmail.com> > --- > MAINTAINERS | 2 + > hw/avr/Kconfig | 1 + > hw/avr/atmega.c | 15 +- > hw/avr/atmega.h | 2 + > hw/watchdog/Kconfig | 3 + > hw/watchdog/avr_wdt.c | 274 ++++++++++++++++++++++++++++++++++ > hw/watchdog/meson.build | 2 + > hw/watchdog/trace-events | 5 + > include/hw/watchdog/avr_wdt.h | 47 ++++++ > target/avr/cpu.c | 3 + > target/avr/cpu.h | 1 + > target/avr/helper.c | 7 +- > target/avr/translate.c | 38 ++++- > 13 files changed, 391 insertions(+), 9 deletions(-) > create mode 100644 hw/watchdog/avr_wdt.c > create mode 100644 include/hw/watchdog/avr_wdt.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 4c05ff8bba..e1fce736d2 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1052,6 +1052,8 @@ F: include/hw/timer/avr_timer16.h > F: hw/timer/avr_timer16.c > F: include/hw/misc/avr_power.h > F: hw/misc/avr_power.c > +F: include/hw/watchdog/avr_wdt.h > +F: hw/watchdog/avr_wdt.c > > Arduino > M: Philippe Mathieu-Daudé <f4bug@amsat.org> > diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig > index d31298c3cc..9939e4902f 100644 > --- a/hw/avr/Kconfig > +++ b/hw/avr/Kconfig > @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU > select AVR_TIMER16 > select AVR_USART > select AVR_POWER > + select AVR_WDT > > config ARDUINO > select AVR_ATMEGA_MCU > diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c > index 44c6afebbb..31ceb1c21c 100644 > --- a/hw/avr/atmega.c > +++ b/hw/avr/atmega.c > @@ -28,6 +28,7 @@ enum AtmegaPeripheral { > GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, > USART0, USART1, USART2, USART3, > TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, > + WDT, > PERIFMAX > }; > > @@ -75,6 +76,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > [GPIOD] = { 0x29 }, > [GPIOC] = { 0x26 }, > [GPIOB] = { 0x23 }, > + [WDT] = { 0x60 }, > }, dev1280_2560[PERIFMAX] = { > [USART3] = { 0x130, POWER1, 2 }, > [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, > @@ -99,6 +101,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > [GPIOC] = { 0x26 }, > [GPIOB] = { 0x23 }, > [GPIOA] = { 0x20 }, > + [WDT] = { 0x60 }, > }; > > enum AtmegaIrq { > @@ -118,6 +121,7 @@ enum AtmegaIrq { > TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, > TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, > TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, > + WATCHDOG_TIMER_IRQ, > IRQ_COUNT > }; > > @@ -133,6 +137,7 @@ enum AtmegaIrq { > #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) > > static const uint8_t irq168_328[IRQ_COUNT] = { > + [WATCHDOG_TIMER_IRQ] = 7, > [TIMER2_COMPA_IRQ] = 8, > [TIMER2_COMPB_IRQ] = 9, > [TIMER2_OVF_IRQ] = 10, > @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { > [USART0_DRE_IRQ] = 20, > [USART0_TXC_IRQ] = 21, > }, irq1280_2560[IRQ_COUNT] = { > + [WATCHDOG_TIMER_IRQ] = 13, > [TIMER2_COMPA_IRQ] = 14, > [TIMER2_COMPB_IRQ] = 15, > [TIMER2_OVF_IRQ] = 16, > @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState *dev, Error **errp) > g_free(devname); > } > > + /* Watchdog Timer */ > + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, TYPE_AVR_WDT); > + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, > + OFFSET_DATA + mc->dev[WDT].addr); > + qdev_connect_gpio_out_named(cpudev, "wdr", 0, > + qdev_get_gpio_in_named(DEVICE(&s->wdt), "wdr", 0)); > + > create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6); > create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8); > create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2); > - create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1); > create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3); > create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3); > } > diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h > index a99ee15c7e..60bbd44bdd 100644 > --- a/hw/avr/atmega.h > +++ b/hw/avr/atmega.h > @@ -13,6 +13,7 @@ > > #include "hw/char/avr_usart.h" > #include "hw/timer/avr_timer16.h" > +#include "hw/watchdog/avr_wdt.h" > #include "hw/misc/avr_power.h" > #include "target/avr/cpu.h" > #include "qom/object.h" > @@ -45,6 +46,7 @@ struct AtmegaMcuState { > AVRMaskState pwr[POWER_MAX]; > AVRUsartState usart[USART_MAX]; > AVRTimer16State timer[TIMER_MAX]; > + AVRWatchdogState wdt; > uint64_t xtal_freq_hz; > }; > > diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig > index 66e1d029e3..e0f89d2fe0 100644 > --- a/hw/watchdog/Kconfig > +++ b/hw/watchdog/Kconfig > @@ -20,3 +20,6 @@ config WDT_IMX2 > > config WDT_SBSA > bool > + > +config AVR_WDT > + bool > diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c > new file mode 100644 > index 0000000000..cbd6457c8b > --- /dev/null > +++ b/hw/watchdog/avr_wdt.c > @@ -0,0 +1,274 @@ > +/* > + * AVR watchdog > + * > + * Copyright (c) 2021 Michael Rolnik > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library 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 > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qemu/log.h" > +#include "hw/irq.h" > +#include "hw/watchdog/avr_wdt.h" > +#include "trace.h" > +#include "target/avr/cpu.h" > +#include "sysemu/runstate.h" > + > +/* Field masks */ > +#define WDTCSR_MASK_WDP0 0x01 > +#define WDTCSR_MASK_WDP1 0x02 > +#define WDTCSR_MASK_WDP2 0x04 > +#define WDTCSR_MASK_WDE 0x08 > +#define WDTCSR_MASK_WDCE 0x10 > +#define WDTCSR_MASK_WDP3 0x20 > +#define WDTCSR_MASK_WDIE 0x40 > +#define WDTCSR_MASK_WDIF 0x80 > + > +#define WDTCSR_SHFT_WDP0 0x00 > +#define WDTCSR_SHFT_WDP1 0x01 > +#define WDTCSR_SHFT_WDP2 0x02 > +#define WDTCSR_SHFT_WDE 0x03 > +#define WDTCSR_SHFT_WDCE 0x04 > +#define WDTCSR_SHFT_WDP3 0x05 > +#define WDTCSR_SHFT_WDIE 0x06 > +#define WDTCSR_SHFT_WDIF 0x07 > + > +#define MCUSR_MASK_WDRF 0x04 > +#define MCUSR_ADDR (OFFSET_DATA + 0x55) > + > +/* Helper macros */ > +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> WDTCSR_SHFT_WDP0) > +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> WDTCSR_SHFT_WDP1) > +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> WDTCSR_SHFT_WDP2) > +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> WDTCSR_SHFT_WDP3) > +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ > + (WDP1(csr) << 1) | (WDP0(csr) << 0)) > +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> WDTCSR_SHFT_WDIE) > +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> WDTCSR_SHFT_WDE) > +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> WDTCSR_SHFT_WCE) > + > +#define DB_PRINT(fmt, args...) /* Nothing */ > + > +#define MS2NS(n) ((n) * 1000000ull) I think there is SCALE_MS define for that. > + > +static void set_bits(uint8_t *addr, uint8_t bits) > +{ > + *addr |= bits; > +} > + > +static void rst_bits(uint8_t *addr, uint8_t bits) > +{ > + *addr &= ~bits; > +} > + > +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) > +{ > + uint32_t csr = wdt->csr; > + int wdp = WDP(csr); > + > + if (WDIE(csr) == 0 && WDE(csr) == 0) { > + /* watchdog is stopped */ > + return; > + } > + > + timer_mod_ns(wdt->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > + (MS2NS(15) << wdp)); > +} > + > +static void avr_wdt_interrupt(void *opaque) > +{ > + AVRWatchdogState *wdt = opaque; > + int8_t csr = wdt->csr; > + > + if (WDIE(csr) == 1) { > + /* Interrupt Mode */ > + set_bits(&wdt->csr, WDTCSR_MASK_WDIF); > + qemu_set_irq(wdt->irq, 1); > + trace_avr_wdt_interrupt(); > + rst_bits(&wdt->csr, WDTCSR_MASK_WDIF | WDTCSR_MASK_WDIE); > + } > + > + if (WDE(csr) == 1) { > + /* System Reset Mode */ > + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); > + } > + > + avr_wdt_reset_alarm(wdt); > +} > + > +static void avr_wdt_reset(DeviceState *dev) > +{ > + AVRWatchdogState *wdt = AVR_WDT(dev); > + > + wdt->csr = 0; > + qemu_set_irq(wdt->irq, 0); > + avr_wdt_reset_alarm(wdt); > +} > + > +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, unsigned size) > +{ > + assert(size == 1); > + AVRWatchdogState *wdt = opaque; > + uint8_t retval = wdt->csr; > + > + trace_avr_wdt_read(offset, retval); > + > + return (uint64_t)retval; > +} > + > +static void avr_wdt_write(void *opaque, hwaddr offset, > + uint64_t val64, unsigned size) > +{ > + assert(size == 1); > + AVRWatchdogState *wdt = opaque; > + uint8_t val = (uint8_t)val64; > + uint8_t set1 = val; /* bits that should be set to 1 */ > + uint8_t set0 = ~val;/* bits that should be set to 0 */ > + uint8_t mcusr = 0; > + > + /* > + * Bit 7 - WDIF: Watchdog Interrupt Flag > + * This bit is set when a time-out occurs in the Watchdog Timer and the > + * Watchdog Timer is configured for interrupt. WDIF is cleared by hardware > + * when executing the corresponding interrupt handling vector. > + * Alternatively, WDIF is cleared by writing a logic one to the flag. > + * When the I-bit in SREG and WDIE are set, the Watchdog Time-out Interrupt > + * is executed. > + */ > + if (val & WDTCSR_MASK_WDIF) { > + rst_bits(&set1, WDTCSR_MASK_WDIF); /* don't set 1 */ > + set_bits(&set0, WDTCSR_MASK_WDIF); /* set 0 */ > + } else { > + rst_bits(&set1, WDTCSR_MASK_WDIF); /* don't set 1 */ > + rst_bits(&set0, WDTCSR_MASK_WDIF); /* don't set 0 */ > + } > + > + /* > + * Bit 4 - WDCE: Watchdog Change Enable > + * This bit is used in timed sequences for changing WDE and prescaler > + * bits. To clear the WDE bit, and/or change the prescaler bits, > + * WDCE must be set. > + * Once written to one, hardware will clear WDCE after four clock cycles. > + */ > + if (!(val & WDTCSR_MASK_WDCE)) { > + uint8_t bits = WDTCSR_MASK_WDE | WDTCSR_MASK_WDP0 | WDTCSR_MASK_WDP1 | > + WDTCSR_MASK_WDP2 | WDTCSR_MASK_WDP3; > + rst_bits(&set1, bits); > + rst_bits(&set0, bits); > + } > + > + /* > + * Bit 3 - WDE: Watchdog System Reset Enable > + * WDE is overridden by WDRF in MCUSR. This means that WDE is always set > + * when WDRF is set. To clear WDE, WDRF must be cleared first. This > + * feature ensures multiple resets during conditions causing failure, and > + * a safe start-up after the failure. > + */ > + cpu_physical_memory_read(MCUSR_ADDR, &mcusr, sizeof(mcusr)); > + if (mcusr & MCUSR_MASK_WDRF) { > + set_bits(&set1, WDTCSR_MASK_WDE); > + rst_bits(&set0, WDTCSR_MASK_WDE); > + } > + > + /* update CSR value */ > + assert((set0 & set1) == 0); > + > + val = wdt->csr; > + set_bits(&val, set1); > + rst_bits(&val, set0); > + wdt->csr = val; > + trace_avr_wdt_write(offset, val); > + avr_wdt_reset_alarm(wdt); > + > + /* > + * Bit 6 - WDIE: Watchdog Interrupt Enable > + * When this bit is written to one and the I-bit in the Status Register is > + * set, the Watchdog Interrupt is enabled. If WDE is cleared in > + * combination with this setting, the Watchdog Timer is in Interrupt Mode, > + * and the corresponding interrupt is executed if time-out in the Watchdog > + * Timer occurs. > + * If WDE is set, the Watchdog Timer is in Interrupt and System Reset Mode. > + * The first time-out in the Watchdog Timer will set WDIF. Executing the > + * corresponding interrupt vector will clear WDIE and WDIF automatically by > + * hardware (the Watchdog goes to System Reset Mode). This is useful for > + * keeping the Watchdog Timer security while using the interrupt. To stay > + * in Interrupt and System Reset Mode, WDIE must be set after each > + * interrupt. This should however not be done within the interrupt service > + * routine itself, as this might compromise the safety-function of the > + * Watchdog System Reset mode. If the interrupt is not executed before the > + * next time-out, a System Reset will be applied. > + */ > + if ((val & WDTCSR_MASK_WDIE) && (wdt->csr & WDTCSR_MASK_WDIF)) { > + avr_wdt_interrupt(opaque); > + } > +} > + > +static const MemoryRegionOps avr_wdt_ops = { > + .read = avr_wdt_read, > + .write = avr_wdt_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .impl = {.max_access_size = 1} > +}; > + > +static void avr_wdt_wdr(void *opaque, int irq, int level) > +{ > + AVRWatchdogState *wdt = AVR_WDT(opaque); > + > + avr_wdt_reset_alarm(wdt); > +} > + > +static void avr_wdt_init(Object *obj) > +{ > + AVRWatchdogState *s = AVR_WDT(obj); > + > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); > + > + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, > + s, "avr-wdt", 0xa); > + > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); > + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); > +} > + > +static void avr_wdt_realize(DeviceState *dev, Error **errp) > +{ > + AVRWatchdogState *s = AVR_WDT(dev); > + > + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); > +} > + > +static void avr_wdt_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->reset = avr_wdt_reset; > + dc->realize = avr_wdt_realize; > +} > + > +static const TypeInfo avr_wdt_info = { > + .name = TYPE_AVR_WDT, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(AVRWatchdogState), > + .instance_init = avr_wdt_init, > + .class_init = avr_wdt_class_init, > +}; > + > +static void avr_wdt_register_types(void) > +{ > + type_register_static(&avr_wdt_info); > +} > + > +type_init(avr_wdt_register_types) > diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build > index 054c403dea..8db2be8317 100644 > --- a/hw/watchdog/meson.build > +++ b/hw/watchdog/meson.build > @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) > softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) > softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) > softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) > + > +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: files('avr_wdt.c')) > diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events > index 3124ca1f1b..ac14773179 100644 > --- a/hw/watchdog/trace-events > +++ b/hw/watchdog/trace-events > @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK AP > cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" > cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" > cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 > + > +# avr_wdt.c > +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u value:%u" > +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u value:%u" > +avr_wdt_interrupt(void) "" > diff --git a/include/hw/watchdog/avr_wdt.h b/include/hw/watchdog/avr_wdt.h > new file mode 100644 > index 0000000000..8433663013 > --- /dev/null > +++ b/include/hw/watchdog/avr_wdt.h > @@ -0,0 +1,47 @@ > +/* > + * AVR watchdog > + * > + * Copyright (c) 2021 Michael Rolnik > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library 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 > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > + */ > + > +#ifndef HW_WATCHDOG_AVR_WDT_H > +#define HW_WATCHDOG_AVR_WDT_H > + > +#include "hw/sysbus.h" > +#include "qemu/timer.h" > +#include "hw/hw.h" > +#include "qom/object.h" > + > +#define TYPE_AVR_WDT "avr-wdt" > +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) > + > +struct AVRWatchdogState { > + /* <private> */ > + SysBusDevice parent_obj; > + > + /* <public> */ > + MemoryRegion iomem; > + MemoryRegion imsk_iomem; > + MemoryRegion ifr_iomem; > + QEMUTimer *timer; > + qemu_irq irq; > + > + /* registers */ > + uint8_t csr; > +}; > + You should add vmstate which includes timer and csr. It will allow reverse debugging available with icount enabled. > +#endif /* HW_WATCHDOG_AVR_WDT_H */ > diff --git a/target/avr/cpu.c b/target/avr/cpu.c > index 0f4596932b..d5eb785833 100644 > --- a/target/avr/cpu.c > +++ b/target/avr/cpu.c > @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) > /* Set the number of interrupts supported by the CPU. */ > qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, > sizeof(cpu->env.intsrc) * 8); > + > + /* register watchdog timer reset interrupt */ > + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); > } > > static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) > diff --git a/target/avr/cpu.h b/target/avr/cpu.h > index d148e8c75a..f8f5641c8b 100644 > --- a/target/avr/cpu.h > +++ b/target/avr/cpu.h > @@ -152,6 +152,7 @@ typedef struct AVRCPU { > > CPUNegativeOffsetState neg; > CPUAVRState env; > + qemu_irq wdr; /* reset WDT */ > } AVRCPU; > > extern const struct VMStateDescription vms_avr_cpu; > diff --git a/target/avr/helper.c b/target/avr/helper.c > index 35e1019594..dd88057e5f 100644 > --- a/target/avr/helper.c > +++ b/target/avr/helper.c > @@ -24,6 +24,7 @@ > #include "exec/exec-all.h" > #include "exec/address-spaces.h" > #include "exec/helper-proto.h" > +#include "hw/irq.h" > > bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) > { > @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) > > void helper_wdr(CPUAVRState *env) > { > - CPUState *cs = env_cpu(env); > + AVRCPU *cpu = env_archcpu(env); > > - /* WD is not implemented yet, placeholder */ > - cs->exception_index = EXCP_DEBUG; > - cpu_loop_exit(cs); > + qemu_set_irq(cpu->wdr, 1); > } > > /* > diff --git a/target/avr/translate.c b/target/avr/translate.c > index 850c5941d9..b7b716f4a0 100644 > --- a/target/avr/translate.c > +++ b/target/avr/translate.c > @@ -1411,6 +1411,9 @@ static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) > { > TCGv temp = tcg_const_i32(a->reg); > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_inb(temp, cpu_env, temp); > tcg_gen_andi_tl(temp, temp, 1 << a->bit); > ctx->skip_cond = TCG_COND_EQ; Translation should stop at inb-related instructions in icount mode, because icount is valid only at the end of the block. Reading it in the middle makes execution non-deterministic. > @@ -1429,6 +1432,9 @@ static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a) > { > TCGv temp = tcg_const_i32(a->reg); > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_inb(temp, cpu_env, temp); > tcg_gen_andi_tl(temp, temp, 1 << a->bit); > ctx->skip_cond = TCG_COND_NE; > @@ -1611,6 +1617,9 @@ static TCGv gen_get_zaddr(void) > static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) > { > if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_fullwr(cpu_env, data, addr); > } else { > tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] = data */ > @@ -1620,6 +1629,9 @@ static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) > static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr) > { > if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_fullrd(data, cpu_env, addr); > } else { > tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data = mem[addr] */ > @@ -2325,6 +2337,9 @@ static bool trans_IN(DisasContext *ctx, arg_IN *a) > TCGv Rd = cpu_r[a->rd]; > TCGv port = tcg_const_i32(a->imm); > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_inb(Rd, cpu_env, port); > > tcg_temp_free_i32(port); > @@ -2341,6 +2356,9 @@ static bool trans_OUT(DisasContext *ctx, arg_OUT *a) > TCGv Rd = cpu_r[a->rd]; > TCGv port = tcg_const_i32(a->imm); > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_outb(cpu_env, port, Rd); > > tcg_temp_free_i32(port); > @@ -2641,6 +2659,9 @@ static bool trans_SBI(DisasContext *ctx, arg_SBI *a) > TCGv data = tcg_temp_new_i32(); > TCGv port = tcg_const_i32(a->reg); > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_inb(data, cpu_env, port); > tcg_gen_ori_tl(data, data, 1 << a->bit); > gen_helper_outb(cpu_env, port, data); > @@ -2660,6 +2681,9 @@ static bool trans_CBI(DisasContext *ctx, arg_CBI *a) > TCGv data = tcg_temp_new_i32(); > TCGv port = tcg_const_i32(a->reg); > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_inb(data, cpu_env, port); > tcg_gen_andi_tl(data, data, ~(1 << a->bit)); > gen_helper_outb(cpu_env, port, data); > @@ -2830,6 +2854,9 @@ static bool trans_SLEEP(DisasContext *ctx, arg_SLEEP *a) > */ > static bool trans_WDR(DisasContext *ctx, arg_WDR *a) > { > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > gen_helper_wdr(cpu_env); > > return true; > @@ -2919,6 +2946,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) > * This flag is set by ST/LD instruction we will regenerate it ONLY > * with mem/cpu memory access instead of mem access > */ > + if (tb_cflags(tb) & CF_USE_ICOUNT) { > + gen_io_start(); > + } > max_insns = 1; > } > if (ctx.singlestep) { > @@ -2955,6 +2985,10 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) > goto done_generating; > } > > + if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { > + gen_io_start(); > + } > + > /* Conditionally skip the next instruction, if indicated. */ > if (ctx.skip_cond != TCG_COND_NEVER) { > skip_label = gen_new_label(); > @@ -2998,10 +3032,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) > && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4 > && !tcg_op_buf_full()); > > - if (tb->cflags & CF_LAST_IO) { > - gen_io_end(); > - } > - > bool nonconst_skip = canonicalize_skip(&ctx); > > switch (ctx.bstate) { > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 1/1] Implement AVR watchdog timer 2021-05-13 12:27 ` Pavel Dovgalyuk @ 2021-05-14 7:20 ` Michael Rolnik 0 siblings, 0 replies; 11+ messages in thread From: Michael Rolnik @ 2021-05-14 7:20 UTC (permalink / raw) To: Pavel Dovgalyuk Cc: Joaquin de Andres, Fred Konrad, Richard Henderson, QEMU Developers, Philippe Mathieu-Daudé [-- Attachment #1: Type: text/plain, Size: 27121 bytes --] Ok, thanks. Sent from my cell phone, please ignore typos On Thu, May 13, 2021, 3:27 PM Pavel Dovgalyuk <pavel.dovgalyuk@ispras.ru> wrote: > On 06.05.2021 00:18, Michael Rolnik wrote: > > Signed-off-by: Michael Rolnik <mrolnik@gmail.com> > > --- > > MAINTAINERS | 2 + > > hw/avr/Kconfig | 1 + > > hw/avr/atmega.c | 15 +- > > hw/avr/atmega.h | 2 + > > hw/watchdog/Kconfig | 3 + > > hw/watchdog/avr_wdt.c | 274 ++++++++++++++++++++++++++++++++++ > > hw/watchdog/meson.build | 2 + > > hw/watchdog/trace-events | 5 + > > include/hw/watchdog/avr_wdt.h | 47 ++++++ > > target/avr/cpu.c | 3 + > > target/avr/cpu.h | 1 + > > target/avr/helper.c | 7 +- > > target/avr/translate.c | 38 ++++- > > 13 files changed, 391 insertions(+), 9 deletions(-) > > create mode 100644 hw/watchdog/avr_wdt.c > > create mode 100644 include/hw/watchdog/avr_wdt.h > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 4c05ff8bba..e1fce736d2 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -1052,6 +1052,8 @@ F: include/hw/timer/avr_timer16.h > > F: hw/timer/avr_timer16.c > > F: include/hw/misc/avr_power.h > > F: hw/misc/avr_power.c > > +F: include/hw/watchdog/avr_wdt.h > > +F: hw/watchdog/avr_wdt.c > > > > Arduino > > M: Philippe Mathieu-Daudé <f4bug@amsat.org> > > diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig > > index d31298c3cc..9939e4902f 100644 > > --- a/hw/avr/Kconfig > > +++ b/hw/avr/Kconfig > > @@ -3,6 +3,7 @@ config AVR_ATMEGA_MCU > > select AVR_TIMER16 > > select AVR_USART > > select AVR_POWER > > + select AVR_WDT > > > > config ARDUINO > > select AVR_ATMEGA_MCU > > diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c > > index 44c6afebbb..31ceb1c21c 100644 > > --- a/hw/avr/atmega.c > > +++ b/hw/avr/atmega.c > > @@ -28,6 +28,7 @@ enum AtmegaPeripheral { > > GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, > > USART0, USART1, USART2, USART3, > > TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, > > + WDT, > > PERIFMAX > > }; > > > > @@ -75,6 +76,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > > [GPIOD] = { 0x29 }, > > [GPIOC] = { 0x26 }, > > [GPIOB] = { 0x23 }, > > + [WDT] = { 0x60 }, > > }, dev1280_2560[PERIFMAX] = { > > [USART3] = { 0x130, POWER1, 2 }, > > [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, > > @@ -99,6 +101,7 @@ static const peripheral_cfg dev168_328[PERIFMAX] = { > > [GPIOC] = { 0x26 }, > > [GPIOB] = { 0x23 }, > > [GPIOA] = { 0x20 }, > > + [WDT] = { 0x60 }, > > }; > > > > enum AtmegaIrq { > > @@ -118,6 +121,7 @@ enum AtmegaIrq { > > TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, > > TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, > > TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, > > + WATCHDOG_TIMER_IRQ, > > IRQ_COUNT > > }; > > > > @@ -133,6 +137,7 @@ enum AtmegaIrq { > > #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) > > > > static const uint8_t irq168_328[IRQ_COUNT] = { > > + [WATCHDOG_TIMER_IRQ] = 7, > > [TIMER2_COMPA_IRQ] = 8, > > [TIMER2_COMPB_IRQ] = 9, > > [TIMER2_OVF_IRQ] = 10, > > @@ -147,6 +152,7 @@ static const uint8_t irq168_328[IRQ_COUNT] = { > > [USART0_DRE_IRQ] = 20, > > [USART0_TXC_IRQ] = 21, > > }, irq1280_2560[IRQ_COUNT] = { > > + [WATCHDOG_TIMER_IRQ] = 13, > > [TIMER2_COMPA_IRQ] = 14, > > [TIMER2_COMPB_IRQ] = 15, > > [TIMER2_OVF_IRQ] = 16, > > @@ -344,10 +350,17 @@ static void atmega_realize(DeviceState *dev, Error > **errp) > > g_free(devname); > > } > > > > + /* Watchdog Timer */ > > + object_initialize_child(OBJECT(dev), "wdt", &s->wdt, TYPE_AVR_WDT); > > + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_abort); > > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, > > + OFFSET_DATA + mc->dev[WDT].addr); > > + qdev_connect_gpio_out_named(cpudev, "wdr", 0, > > + qdev_get_gpio_in_named(DEVICE(&s->wdt), "wdr", 0)); > > + > > create_unimplemented_device("avr-twi", OFFSET_DATA + > 0x0b8, 6); > > create_unimplemented_device("avr-adc", OFFSET_DATA + > 0x078, 8); > > create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + > 0x074, 2); > > - create_unimplemented_device("avr-watchdog", OFFSET_DATA + > 0x060, 1); > > create_unimplemented_device("avr-spi", OFFSET_DATA + > 0x04c, 3); > > create_unimplemented_device("avr-eeprom", OFFSET_DATA + > 0x03f, 3); > > } > > diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h > > index a99ee15c7e..60bbd44bdd 100644 > > --- a/hw/avr/atmega.h > > +++ b/hw/avr/atmega.h > > @@ -13,6 +13,7 @@ > > > > #include "hw/char/avr_usart.h" > > #include "hw/timer/avr_timer16.h" > > +#include "hw/watchdog/avr_wdt.h" > > #include "hw/misc/avr_power.h" > > #include "target/avr/cpu.h" > > #include "qom/object.h" > > @@ -45,6 +46,7 @@ struct AtmegaMcuState { > > AVRMaskState pwr[POWER_MAX]; > > AVRUsartState usart[USART_MAX]; > > AVRTimer16State timer[TIMER_MAX]; > > + AVRWatchdogState wdt; > > uint64_t xtal_freq_hz; > > }; > > > > diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig > > index 66e1d029e3..e0f89d2fe0 100644 > > --- a/hw/watchdog/Kconfig > > +++ b/hw/watchdog/Kconfig > > @@ -20,3 +20,6 @@ config WDT_IMX2 > > > > config WDT_SBSA > > bool > > + > > +config AVR_WDT > > + bool > > diff --git a/hw/watchdog/avr_wdt.c b/hw/watchdog/avr_wdt.c > > new file mode 100644 > > index 0000000000..cbd6457c8b > > --- /dev/null > > +++ b/hw/watchdog/avr_wdt.c > > @@ -0,0 +1,274 @@ > > +/* > > + * AVR watchdog > > + * > > + * Copyright (c) 2021 Michael Rolnik > > + * > > + * This library is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU Lesser General Public > > + * License as published by the Free Software Foundation; either > > + * version 2.1 of the License, or (at your option) any later version. > > + * > > + * This library 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 > > + * Lesser General Public License for more details. > > + * > > + * You should have received a copy of the GNU Lesser General Public > > + * License along with this library; if not, see > > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > > + */ > > + > > +#include "qemu/osdep.h" > > +#include "qapi/error.h" > > +#include "qemu/log.h" > > +#include "hw/irq.h" > > +#include "hw/watchdog/avr_wdt.h" > > +#include "trace.h" > > +#include "target/avr/cpu.h" > > +#include "sysemu/runstate.h" > > + > > +/* Field masks */ > > +#define WDTCSR_MASK_WDP0 0x01 > > +#define WDTCSR_MASK_WDP1 0x02 > > +#define WDTCSR_MASK_WDP2 0x04 > > +#define WDTCSR_MASK_WDE 0x08 > > +#define WDTCSR_MASK_WDCE 0x10 > > +#define WDTCSR_MASK_WDP3 0x20 > > +#define WDTCSR_MASK_WDIE 0x40 > > +#define WDTCSR_MASK_WDIF 0x80 > > + > > +#define WDTCSR_SHFT_WDP0 0x00 > > +#define WDTCSR_SHFT_WDP1 0x01 > > +#define WDTCSR_SHFT_WDP2 0x02 > > +#define WDTCSR_SHFT_WDE 0x03 > > +#define WDTCSR_SHFT_WDCE 0x04 > > +#define WDTCSR_SHFT_WDP3 0x05 > > +#define WDTCSR_SHFT_WDIE 0x06 > > +#define WDTCSR_SHFT_WDIF 0x07 > > + > > +#define MCUSR_MASK_WDRF 0x04 > > +#define MCUSR_ADDR (OFFSET_DATA + 0x55) > > + > > +/* Helper macros */ > > +#define WDP0(csr) ((csr & WDTCSR_MASK_WDP0) >> WDTCSR_SHFT_WDP0) > > +#define WDP1(csr) ((csr & WDTCSR_MASK_WDP1) >> WDTCSR_SHFT_WDP1) > > +#define WDP2(csr) ((csr & WDTCSR_MASK_WDP2) >> WDTCSR_SHFT_WDP2) > > +#define WDP3(csr) ((csr & WDTCSR_MASK_WDP3) >> WDTCSR_SHFT_WDP3) > > +#define WDP(csr) ((WDP3(csr) << 3) | (WDP2(csr) << 2) | \ > > + (WDP1(csr) << 1) | (WDP0(csr) << 0)) > > +#define WDIE(csr) ((csr & WDTCSR_MASK_WDIE) >> WDTCSR_SHFT_WDIE) > > +#define WDE(csr) ((csr & WDTCSR_MASK_WDE) >> WDTCSR_SHFT_WDE) > > +#define WCE(csr) ((csr & WDTCSR_MASK_WCE) >> WDTCSR_SHFT_WCE) > > + > > +#define DB_PRINT(fmt, args...) /* Nothing */ > > + > > +#define MS2NS(n) ((n) * 1000000ull) > > I think there is SCALE_MS define for that. > > > + > > +static void set_bits(uint8_t *addr, uint8_t bits) > > +{ > > + *addr |= bits; > > +} > > + > > +static void rst_bits(uint8_t *addr, uint8_t bits) > > +{ > > + *addr &= ~bits; > > +} > > + > > +static void avr_wdt_reset_alarm(AVRWatchdogState *wdt) > > +{ > > + uint32_t csr = wdt->csr; > > + int wdp = WDP(csr); > > + > > + if (WDIE(csr) == 0 && WDE(csr) == 0) { > > + /* watchdog is stopped */ > > + return; > > + } > > + > > + timer_mod_ns(wdt->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > > + (MS2NS(15) << wdp)); > > +} > > + > > +static void avr_wdt_interrupt(void *opaque) > > +{ > > + AVRWatchdogState *wdt = opaque; > > + int8_t csr = wdt->csr; > > + > > + if (WDIE(csr) == 1) { > > + /* Interrupt Mode */ > > + set_bits(&wdt->csr, WDTCSR_MASK_WDIF); > > + qemu_set_irq(wdt->irq, 1); > > + trace_avr_wdt_interrupt(); > > + rst_bits(&wdt->csr, WDTCSR_MASK_WDIF | WDTCSR_MASK_WDIE); > > + } > > + > > + if (WDE(csr) == 1) { > > + /* System Reset Mode */ > > + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); > > + } > > + > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static void avr_wdt_reset(DeviceState *dev) > > +{ > > + AVRWatchdogState *wdt = AVR_WDT(dev); > > + > > + wdt->csr = 0; > > + qemu_set_irq(wdt->irq, 0); > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static uint64_t avr_wdt_read(void *opaque, hwaddr offset, unsigned size) > > +{ > > + assert(size == 1); > > + AVRWatchdogState *wdt = opaque; > > + uint8_t retval = wdt->csr; > > + > > + trace_avr_wdt_read(offset, retval); > > + > > + return (uint64_t)retval; > > +} > > + > > +static void avr_wdt_write(void *opaque, hwaddr offset, > > + uint64_t val64, unsigned size) > > +{ > > + assert(size == 1); > > + AVRWatchdogState *wdt = opaque; > > + uint8_t val = (uint8_t)val64; > > + uint8_t set1 = val; /* bits that should be set to 1 */ > > + uint8_t set0 = ~val;/* bits that should be set to 0 */ > > + uint8_t mcusr = 0; > > + > > + /* > > + * Bit 7 - WDIF: Watchdog Interrupt Flag > > + * This bit is set when a time-out occurs in the Watchdog Timer > and the > > + * Watchdog Timer is configured for interrupt. WDIF is cleared by > hardware > > + * when executing the corresponding interrupt handling vector. > > + * Alternatively, WDIF is cleared by writing a logic one to the > flag. > > + * When the I-bit in SREG and WDIE are set, the Watchdog Time-out > Interrupt > > + * is executed. > > + */ > > + if (val & WDTCSR_MASK_WDIF) { > > + rst_bits(&set1, WDTCSR_MASK_WDIF); /* don't set 1 */ > > + set_bits(&set0, WDTCSR_MASK_WDIF); /* set 0 */ > > + } else { > > + rst_bits(&set1, WDTCSR_MASK_WDIF); /* don't set 1 */ > > + rst_bits(&set0, WDTCSR_MASK_WDIF); /* don't set 0 */ > > + } > > + > > + /* > > + * Bit 4 - WDCE: Watchdog Change Enable > > + * This bit is used in timed sequences for changing WDE and > prescaler > > + * bits. To clear the WDE bit, and/or change the prescaler bits, > > + * WDCE must be set. > > + * Once written to one, hardware will clear WDCE after four clock > cycles. > > + */ > > + if (!(val & WDTCSR_MASK_WDCE)) { > > + uint8_t bits = WDTCSR_MASK_WDE | WDTCSR_MASK_WDP0 | > WDTCSR_MASK_WDP1 | > > + WDTCSR_MASK_WDP2 | WDTCSR_MASK_WDP3; > > + rst_bits(&set1, bits); > > + rst_bits(&set0, bits); > > + } > > + > > + /* > > + * Bit 3 - WDE: Watchdog System Reset Enable > > + * WDE is overridden by WDRF in MCUSR. This means that WDE is > always set > > + * when WDRF is set. To clear WDE, WDRF must be cleared first. This > > + * feature ensures multiple resets during conditions causing > failure, and > > + * a safe start-up after the failure. > > + */ > > + cpu_physical_memory_read(MCUSR_ADDR, &mcusr, sizeof(mcusr)); > > + if (mcusr & MCUSR_MASK_WDRF) { > > + set_bits(&set1, WDTCSR_MASK_WDE); > > + rst_bits(&set0, WDTCSR_MASK_WDE); > > + } > > + > > + /* update CSR value */ > > + assert((set0 & set1) == 0); > > + > > + val = wdt->csr; > > + set_bits(&val, set1); > > + rst_bits(&val, set0); > > + wdt->csr = val; > > + trace_avr_wdt_write(offset, val); > > + avr_wdt_reset_alarm(wdt); > > + > > + /* > > + * Bit 6 - WDIE: Watchdog Interrupt Enable > > + * When this bit is written to one and the I-bit in the Status > Register is > > + * set, the Watchdog Interrupt is enabled. If WDE is cleared in > > + * combination with this setting, the Watchdog Timer is in > Interrupt Mode, > > + * and the corresponding interrupt is executed if time-out in the > Watchdog > > + * Timer occurs. > > + * If WDE is set, the Watchdog Timer is in Interrupt and System > Reset Mode. > > + * The first time-out in the Watchdog Timer will set WDIF. > Executing the > > + * corresponding interrupt vector will clear WDIE and WDIF > automatically by > > + * hardware (the Watchdog goes to System Reset Mode). This is > useful for > > + * keeping the Watchdog Timer security while using the interrupt. > To stay > > + * in Interrupt and System Reset Mode, WDIE must be set after each > > + * interrupt. This should however not be done within the interrupt > service > > + * routine itself, as this might compromise the safety-function of > the > > + * Watchdog System Reset mode. If the interrupt is not executed > before the > > + * next time-out, a System Reset will be applied. > > + */ > > + if ((val & WDTCSR_MASK_WDIE) && (wdt->csr & WDTCSR_MASK_WDIF)) { > > + avr_wdt_interrupt(opaque); > > + } > > +} > > + > > +static const MemoryRegionOps avr_wdt_ops = { > > + .read = avr_wdt_read, > > + .write = avr_wdt_write, > > + .endianness = DEVICE_NATIVE_ENDIAN, > > + .impl = {.max_access_size = 1} > > +}; > > + > > +static void avr_wdt_wdr(void *opaque, int irq, int level) > > +{ > > + AVRWatchdogState *wdt = AVR_WDT(opaque); > > + > > + avr_wdt_reset_alarm(wdt); > > +} > > + > > +static void avr_wdt_init(Object *obj) > > +{ > > + AVRWatchdogState *s = AVR_WDT(obj); > > + > > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); > > + > > + memory_region_init_io(&s->iomem, obj, &avr_wdt_ops, > > + s, "avr-wdt", 0xa); > > + > > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); > > + qdev_init_gpio_in_named(DEVICE(s), avr_wdt_wdr, "wdr", 1); > > +} > > + > > +static void avr_wdt_realize(DeviceState *dev, Error **errp) > > +{ > > + AVRWatchdogState *s = AVR_WDT(dev); > > + > > + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_wdt_interrupt, s); > > +} > > + > > +static void avr_wdt_class_init(ObjectClass *klass, void *data) > > +{ > > + DeviceClass *dc = DEVICE_CLASS(klass); > > + > > + dc->reset = avr_wdt_reset; > > + dc->realize = avr_wdt_realize; > > +} > > + > > +static const TypeInfo avr_wdt_info = { > > + .name = TYPE_AVR_WDT, > > + .parent = TYPE_SYS_BUS_DEVICE, > > + .instance_size = sizeof(AVRWatchdogState), > > + .instance_init = avr_wdt_init, > > + .class_init = avr_wdt_class_init, > > +}; > > + > > +static void avr_wdt_register_types(void) > > +{ > > + type_register_static(&avr_wdt_info); > > +} > > + > > +type_init(avr_wdt_register_types) > > diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build > > index 054c403dea..8db2be8317 100644 > > --- a/hw/watchdog/meson.build > > +++ b/hw/watchdog/meson.build > > @@ -6,3 +6,5 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: > files('wdt_diag288.c')) > > softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: > files('wdt_aspeed.c')) > > softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) > > softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) > > + > > +specific_ss.add(when: 'CONFIG_AVR_WDT', if_true: files('avr_wdt.c')) > > diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events > > index 3124ca1f1b..ac14773179 100644 > > --- a/hw/watchdog/trace-events > > +++ b/hw/watchdog/trace-events > > @@ -5,3 +5,8 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, > unsigned size) "CMSDK AP > > cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned > size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " > size %u" > > cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" > > cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" > PRIu32 > > + > > +# avr_wdt.c > > +avr_wdt_read(uint8_t addr, uint8_t value) "wdt read addr:%u value:%u" > > +avr_wdt_write(uint8_t addr, uint8_t value) "wdt write addr:%u value:%u" > > +avr_wdt_interrupt(void) "" > > diff --git a/include/hw/watchdog/avr_wdt.h > b/include/hw/watchdog/avr_wdt.h > > new file mode 100644 > > index 0000000000..8433663013 > > --- /dev/null > > +++ b/include/hw/watchdog/avr_wdt.h > > @@ -0,0 +1,47 @@ > > +/* > > + * AVR watchdog > > + * > > + * Copyright (c) 2021 Michael Rolnik > > + * > > + * This library is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU Lesser General Public > > + * License as published by the Free Software Foundation; either > > + * version 2.1 of the License, or (at your option) any later version. > > + * > > + * This library 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 > > + * Lesser General Public License for more details. > > + * > > + * You should have received a copy of the GNU Lesser General Public > > + * License along with this library; if not, see > > + * <http://www.gnu.org/licenses/lgpl-2.1.html> > > + */ > > + > > +#ifndef HW_WATCHDOG_AVR_WDT_H > > +#define HW_WATCHDOG_AVR_WDT_H > > + > > +#include "hw/sysbus.h" > > +#include "qemu/timer.h" > > +#include "hw/hw.h" > > +#include "qom/object.h" > > + > > +#define TYPE_AVR_WDT "avr-wdt" > > +OBJECT_DECLARE_SIMPLE_TYPE(AVRWatchdogState, AVR_WDT) > > + > > +struct AVRWatchdogState { > > + /* <private> */ > > + SysBusDevice parent_obj; > > + > > + /* <public> */ > > + MemoryRegion iomem; > > + MemoryRegion imsk_iomem; > > + MemoryRegion ifr_iomem; > > + QEMUTimer *timer; > > + qemu_irq irq; > > + > > + /* registers */ > > + uint8_t csr; > > +}; > > + > > You should add vmstate which includes timer and csr. It will allow > reverse debugging available with icount enabled. > > > +#endif /* HW_WATCHDOG_AVR_WDT_H */ > > diff --git a/target/avr/cpu.c b/target/avr/cpu.c > > index 0f4596932b..d5eb785833 100644 > > --- a/target/avr/cpu.c > > +++ b/target/avr/cpu.c > > @@ -131,6 +131,9 @@ static void avr_cpu_initfn(Object *obj) > > /* Set the number of interrupts supported by the CPU. */ > > qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, > > sizeof(cpu->env.intsrc) * 8); > > + > > + /* register watchdog timer reset interrupt */ > > + qdev_init_gpio_out_named(DEVICE(cpu), &cpu->wdr, "wdr", 1); > > } > > > > static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) > > diff --git a/target/avr/cpu.h b/target/avr/cpu.h > > index d148e8c75a..f8f5641c8b 100644 > > --- a/target/avr/cpu.h > > +++ b/target/avr/cpu.h > > @@ -152,6 +152,7 @@ typedef struct AVRCPU { > > > > CPUNegativeOffsetState neg; > > CPUAVRState env; > > + qemu_irq wdr; /* reset WDT */ > > } AVRCPU; > > > > extern const struct VMStateDescription vms_avr_cpu; > > diff --git a/target/avr/helper.c b/target/avr/helper.c > > index 35e1019594..dd88057e5f 100644 > > --- a/target/avr/helper.c > > +++ b/target/avr/helper.c > > @@ -24,6 +24,7 @@ > > #include "exec/exec-all.h" > > #include "exec/address-spaces.h" > > #include "exec/helper-proto.h" > > +#include "hw/irq.h" > > > > bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) > > { > > @@ -188,11 +189,9 @@ void helper_break(CPUAVRState *env) > > > > void helper_wdr(CPUAVRState *env) > > { > > - CPUState *cs = env_cpu(env); > > + AVRCPU *cpu = env_archcpu(env); > > > > - /* WD is not implemented yet, placeholder */ > > - cs->exception_index = EXCP_DEBUG; > > - cpu_loop_exit(cs); > > + qemu_set_irq(cpu->wdr, 1); > > } > > > > /* > > diff --git a/target/avr/translate.c b/target/avr/translate.c > > index 850c5941d9..b7b716f4a0 100644 > > --- a/target/avr/translate.c > > +++ b/target/avr/translate.c > > @@ -1411,6 +1411,9 @@ static bool trans_SBIC(DisasContext *ctx, arg_SBIC > *a) > > { > > TCGv temp = tcg_const_i32(a->reg); > > > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_inb(temp, cpu_env, temp); > > tcg_gen_andi_tl(temp, temp, 1 << a->bit); > > ctx->skip_cond = TCG_COND_EQ; > > Translation should stop at inb-related instructions in icount mode, > because icount is valid only at the end of the block. Reading it in the > middle makes execution non-deterministic. > > > @@ -1429,6 +1432,9 @@ static bool trans_SBIS(DisasContext *ctx, arg_SBIS > *a) > > { > > TCGv temp = tcg_const_i32(a->reg); > > > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_inb(temp, cpu_env, temp); > > tcg_gen_andi_tl(temp, temp, 1 << a->bit); > > ctx->skip_cond = TCG_COND_NE; > > @@ -1611,6 +1617,9 @@ static TCGv gen_get_zaddr(void) > > static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) > > { > > if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_fullwr(cpu_env, data, addr); > > } else { > > tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] = > data */ > > @@ -1620,6 +1629,9 @@ static void gen_data_store(DisasContext *ctx, TCGv > data, TCGv addr) > > static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr) > > { > > if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_fullrd(data, cpu_env, addr); > > } else { > > tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data = > mem[addr] */ > > @@ -2325,6 +2337,9 @@ static bool trans_IN(DisasContext *ctx, arg_IN *a) > > TCGv Rd = cpu_r[a->rd]; > > TCGv port = tcg_const_i32(a->imm); > > > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_inb(Rd, cpu_env, port); > > > > tcg_temp_free_i32(port); > > @@ -2341,6 +2356,9 @@ static bool trans_OUT(DisasContext *ctx, arg_OUT > *a) > > TCGv Rd = cpu_r[a->rd]; > > TCGv port = tcg_const_i32(a->imm); > > > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_outb(cpu_env, port, Rd); > > > > tcg_temp_free_i32(port); > > @@ -2641,6 +2659,9 @@ static bool trans_SBI(DisasContext *ctx, arg_SBI > *a) > > TCGv data = tcg_temp_new_i32(); > > TCGv port = tcg_const_i32(a->reg); > > > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_inb(data, cpu_env, port); > > tcg_gen_ori_tl(data, data, 1 << a->bit); > > gen_helper_outb(cpu_env, port, data); > > @@ -2660,6 +2681,9 @@ static bool trans_CBI(DisasContext *ctx, arg_CBI > *a) > > TCGv data = tcg_temp_new_i32(); > > TCGv port = tcg_const_i32(a->reg); > > > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_inb(data, cpu_env, port); > > tcg_gen_andi_tl(data, data, ~(1 << a->bit)); > > gen_helper_outb(cpu_env, port, data); > > @@ -2830,6 +2854,9 @@ static bool trans_SLEEP(DisasContext *ctx, > arg_SLEEP *a) > > */ > > static bool trans_WDR(DisasContext *ctx, arg_WDR *a) > > { > > + if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > gen_helper_wdr(cpu_env); > > > > return true; > > @@ -2919,6 +2946,9 @@ void gen_intermediate_code(CPUState *cs, > TranslationBlock *tb, int max_insns) > > * This flag is set by ST/LD instruction we will regenerate it > ONLY > > * with mem/cpu memory access instead of mem access > > */ > > + if (tb_cflags(tb) & CF_USE_ICOUNT) { > > + gen_io_start(); > > + } > > max_insns = 1; > > } > > if (ctx.singlestep) { > > @@ -2955,6 +2985,10 @@ void gen_intermediate_code(CPUState *cs, > TranslationBlock *tb, int max_insns) > > goto done_generating; > > } > > > > + if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { > > + gen_io_start(); > > + } > > + > > /* Conditionally skip the next instruction, if indicated. */ > > if (ctx.skip_cond != TCG_COND_NEVER) { > > skip_label = gen_new_label(); > > @@ -2998,10 +3032,6 @@ void gen_intermediate_code(CPUState *cs, > TranslationBlock *tb, int max_insns) > > && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4 > > && !tcg_op_buf_full()); > > > > - if (tb->cflags & CF_LAST_IO) { > > - gen_io_end(); > > - } > > - > > bool nonconst_skip = canonicalize_skip(&ctx); > > > > switch (ctx.bstate) { > > > > [-- Attachment #2: Type: text/html, Size: 33807 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2021-05-15 9:47 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-05-02 20:10 [RFC 0/1] Implement AVR WDT (watchdog timer) Michael Rolnik 2021-05-02 20:10 ` Michael Rolnik 2021-05-03 13:06 ` Fred Konrad 2021-05-02 20:10 ` [RFC 1/1] Implement AVR watchdog timer Michael Rolnik 2021-05-03 13:15 ` Fred Konrad 2021-05-03 13:36 ` Michael Rolnik 2021-05-03 20:08 ` Michael Rolnik 2021-05-15 9:45 ` Philippe Mathieu-Daudé 2021-05-05 21:18 [RFC v1 0/1] Implement AVR WDT (watchdog timer) Michael Rolnik 2021-05-05 21:18 ` [RFC 1/1] Implement AVR watchdog timer Michael Rolnik 2021-05-13 12:27 ` Pavel Dovgalyuk 2021-05-14 7:20 ` Michael Rolnik
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).