[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [qemu-xen staging] Merge remote-tracking branch 'remotes/philmd-gitlab/tags/avr-port-20200711' into staging
=== This changeset includes merge from high-traffic branch === Commits on that branch are not reported individually. commit d34498309cff7560ac90c422c56e3137e6a64b19 Merge: 2033cc6efa98b831d7839e367aa7d5aa74d0750f 19b293472f1514b5424ef4d9b092e02bd9b106c2 Author: Peter Maydell <peter.maydell@xxxxxxxxxx> AuthorDate: Sat Jul 11 19:27:59 2020 +0100 Commit: Peter Maydell <peter.maydell@xxxxxxxxxx> CommitDate: Sat Jul 11 19:27:59 2020 +0100 Merge remote-tracking branch 'remotes/philmd-gitlab/tags/avr-port-20200711' into staging 8bit AVR port from Michael Rolnik. Michael started to work on the AVR port few years ago [*] and kept improving the code over various series. List of people who help him (in chronological order): - Richard Henderson - Sarah Harris and Edward Robbins - Philippe Mathieu-Daudé and Aleksandar Markovic - Pavel Dovgalyuk - Thomas Huth [*] The oldest contribution I could find on the list is from 2016: https://lists.nongnu.org/archive/html/qemu-devel/2016-06/msg02985.html Tests included: $ avocado --show=app run -t arch:avr tests/acceptance/ Fetching asset from tests/acceptance/machine_avr6.py:AVR6Machine.test_freertos (1/1) tests/acceptance/machine_avr6.py:AVR6Machine.test_freertos: PASS (2.13 s) RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0 JOB TIME : 2.35 s $ make check-qtest-avr TEST check-qtest-avr: tests/qtest/boot-serial-test TEST check-qtest-avr: tests/qtest/cdrom-test TEST check-qtest-avr: tests/qtest/device-introspect-test TEST check-qtest-avr: tests/qtest/machine-none-test TEST check-qtest-avr: tests/qtest/qmp-test TEST check-qtest-avr: tests/qtest/qmp-cmd-test TEST check-qtest-avr: tests/qtest/qom-test TEST check-qtest-avr: tests/qtest/test-hmp TEST check-qtest-avr: tests/qtest/qos-test CI results: . https://cirrus-ci.com/build/5697049146425344 . https://gitlab.com/philmd/qemu/-/pipelines/165328058 . https://travis-ci.org/github/philmd/qemu/builds/705817933 . https://app.shippable.com/github/philmd/qemu/runs/822/summary/console # gpg: Signature made Sat 11 Jul 2020 10:03:11 BST # gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@xxxxxxxxx>" [full] # Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE * remotes/philmd-gitlab/tags/avr-port-20200711: (32 commits) target/avr/disas: Fix store instructions display order target/avr/cpu: Fix $PC displayed address target/avr/cpu: Drop tlb_flush() in avr_cpu_reset() target/avr: Add section into QEMU documentation tests/acceptance: Test the Arduino MEGA2560 board tests/boot-serial: Test some Arduino boards (AVR based) hw/avr: Add limited support for some Arduino boards hw/avr: Add some ATmega microcontrollers hw/avr: Add support for loading ELF/raw binaries hw/misc: avr: Add limited support for power reduction device hw/timer: avr: Add limited support for 16-bit timer peripheral hw/char: avr: Add limited support for USART peripheral tests/machine-none: Add AVR support target/avr: Register AVR support with the rest of QEMU target/avr: Add support for disassembling via option '-d in_asm' target/avr: Initialize TCG register variables target/avr: Add instruction translation - CPU main translation function target/avr: Add instruction translation - MCU Control Instructions target/avr: Add instruction translation - Bit and Bit-test Instructions target/avr: Add instruction translation - Data Transfer Instructions ... Signed-off-by: Peter Maydell <peter.maydell@xxxxxxxxxx> MAINTAINERS | 30 + configure | 7 + default-configs/avr-softmmu.mak | 5 + docs/system/target-avr.rst | 37 + docs/system/targets.rst | 1 + gdb-xml/avr-cpu.xml | 49 + hw/Kconfig | 1 + hw/avr/Kconfig | 9 + hw/avr/Makefile.objs | 3 + hw/avr/arduino.c | 149 ++ hw/avr/atmega.c | 458 ++++++ hw/avr/atmega.h | 48 + hw/avr/boot.c | 115 ++ hw/avr/boot.h | 33 + hw/char/Kconfig | 3 + hw/char/Makefile.objs | 1 + hw/char/avr_usart.c | 320 ++++ hw/misc/Kconfig | 3 + hw/misc/Makefile.objs | 2 + hw/misc/avr_power.c | 113 ++ hw/misc/trace-events | 4 + hw/timer/Kconfig | 3 + hw/timer/Makefile.objs | 2 + hw/timer/avr_timer16.c | 621 ++++++++ hw/timer/trace-events | 12 + include/disas/dis-asm.h | 19 + include/elf.h | 4 + include/hw/char/avr_usart.h | 93 ++ include/hw/misc/avr_power.h | 46 + include/hw/timer/avr_timer16.h | 94 ++ include/sysemu/arch_init.h | 1 + qapi/machine.json | 3 +- softmmu/arch_init.c | 2 + target/avr/Makefile.objs | 34 + target/avr/cpu-param.h | 36 + target/avr/cpu-qom.h | 53 + target/avr/cpu.c | 366 +++++ target/avr/cpu.h | 256 ++++ target/avr/disas.c | 245 +++ target/avr/gdbstub.c | 84 ++ target/avr/helper.c | 348 +++++ target/avr/helper.h | 29 + target/avr/insn.decode | 187 +++ target/avr/machine.c | 119 ++ target/avr/translate.c | 3061 ++++++++++++++++++++++++++++++++++++++ tests/acceptance/machine_avr6.py | 50 + tests/qtest/Makefile.include | 2 + tests/qtest/boot-serial-test.c | 11 + tests/qtest/machine-none-test.c | 1 + 49 files changed, 7172 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 361ae5c662..fe8139f367 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -167,6 +167,14 @@ S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* +AVR TCG CPUs +M: Michael Rolnik <mrolnik@xxxxxxxxx> +R: Sarah Harris <S.E.Harris@xxxxxxxxxx> +S: Maintained +F: gdb-xml/avr-cpu.xml +F: target/avr/ +F: tests/acceptance/machine_avr6.py + CRIS TCG CPUs M: Edgar E. Iglesias <edgar.iglesias@xxxxxxxxx> S: Maintained @@ -982,6 +990,28 @@ F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c +AVR Machines +------------- + +AVR MCUs +M: Michael Rolnik <mrolnik@xxxxxxxxx> +R: Sarah Harris <S.E.Harris@xxxxxxxxxx> +S: Maintained +F: default-configs/avr-softmmu.mak +F: hw/avr/ +F: include/hw/char/avr_usart.h +F: hw/char/avr_usart.c +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 + +Arduino +M: Philippe Mathieu-Daudé <f4bug@xxxxxxxxx> +R: Sarah Harris <S.E.Harris@xxxxxxxxxx> +S: Maintained +F: hw/avr/arduino.c + CRIS Machines ------------- Axis Dev88 diff --git a/configure b/configure index ee6c3c6792..31e2ddbf28 100755 --- a/configure +++ b/configure @@ -8143,6 +8143,10 @@ case "$target_name" in mttcg="yes" gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml arm-m-profile.xml" ;; + avr) + gdb_xml_files="avr-cpu.xml" + target_compiler=$cross_cc_avr + ;; cris) ;; hppa) @@ -8387,6 +8391,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do disas_config "ARM_A64" fi ;; + avr) + disas_config "AVR" + ;; cris) disas_config "CRIS" ;; diff --git a/default-configs/avr-softmmu.mak b/default-configs/avr-softmmu.mak new file mode 100644 index 0000000000..80218add98 --- /dev/null +++ b/default-configs/avr-softmmu.mak @@ -0,0 +1,5 @@ +# Default configuration for avr-softmmu + +# Boards: +# +CONFIG_ARDUINO=y diff --git a/docs/system/target-avr.rst b/docs/system/target-avr.rst new file mode 100644 index 0000000000..dc99afc895 --- /dev/null +++ b/docs/system/target-avr.rst @@ -0,0 +1,37 @@ +.. _AVR-System-emulator: + +AVR System emulator +------------------- + +Use the executable ``qemu-system-avr`` to emulate a AVR 8 bit based machine. +These can have one of the following cores: avr1, avr2, avr25, avr3, avr31, +avr35, avr4, avr5, avr51, avr6, avrtiny, xmega2, xmega3, xmega4, xmega5, +xmega6 and xmega7. + +As for now it supports few Arduino boards for educational and testing purposes. +These boards use a ATmega controller, which model is limited to USART & 16-bit +timer devices, enought to run FreeRTOS based applications (like +https://github.com/seharris/qemu-avr-tests/blob/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf +). + +Following are examples of possible usages, assuming demo.elf is compiled for +AVR cpu + + - Continuous non interrupted execution: + ``qemu-system-avr -machine mega2560 -bios demo.elf`` + + - Continuous non interrupted execution with serial output into telnet window: + ``qemu-system-avr -machine mega2560 -bios demo.elf -serial + tcp::5678,server,nowait -nographic`` + and then in another shell + ``telnet localhost 5678`` + + - Debugging wit GDB debugger: + ``qemu-system-avr -machine mega2560 -bios demo.elf -s -S`` + and then in another shell + ``avr-gdb demo.elf`` + and then within GDB shell + ``target remote :1234`` + + - Print out executed instructions: + ``qemu-system-avr -machine mega2560 -bios demo.elf -d in_asm`` diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 99435a3eba..560783644d 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -19,3 +19,4 @@ Contents: target-xtensa target-s390x target-rx + target-avr diff --git a/gdb-xml/avr-cpu.xml b/gdb-xml/avr-cpu.xml new file mode 100644 index 0000000000..c4747f5b40 --- /dev/null +++ b/gdb-xml/avr-cpu.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2018-2019 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- Register numbers are hard-coded in order to maintain backward + compatibility with older versions of tools that didn't use xml + register descriptions. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.riscv.cpu"> + <reg name="r0" bitsize="8" type="int" regnum="0"/> + <reg name="r1" bitsize="8" type="int"/> + <reg name="r2" bitsize="8" type="int"/> + <reg name="r3" bitsize="8" type="int"/> + <reg name="r4" bitsize="8" type="int"/> + <reg name="r5" bitsize="8" type="int"/> + <reg name="r6" bitsize="8" type="int"/> + <reg name="r7" bitsize="8" type="int"/> + <reg name="r8" bitsize="8" type="int"/> + <reg name="r9" bitsize="8" type="int"/> + <reg name="r10" bitsize="8" type="int"/> + <reg name="r11" bitsize="8" type="int"/> + <reg name="r12" bitsize="8" type="int"/> + <reg name="r13" bitsize="8" type="int"/> + <reg name="r14" bitsize="8" type="int"/> + <reg name="r15" bitsize="8" type="int"/> + <reg name="r16" bitsize="8" type="int"/> + <reg name="r17" bitsize="8" type="int"/> + <reg name="r18" bitsize="8" type="int"/> + <reg name="r19" bitsize="8" type="int"/> + <reg name="r20" bitsize="8" type="int"/> + <reg name="r21" bitsize="8" type="int"/> + <reg name="r22" bitsize="8" type="int"/> + <reg name="r23" bitsize="8" type="int"/> + <reg name="r24" bitsize="8" type="int"/> + <reg name="r25" bitsize="8" type="int"/> + <reg name="r26" bitsize="8" type="int"/> + <reg name="r27" bitsize="8" type="int"/> + <reg name="r28" bitsize="8" type="int"/> + <reg name="r29" bitsize="8" type="int"/> + <reg name="r30" bitsize="8" type="int"/> + <reg name="r31" bitsize="8" type="int"/> + <reg name="sreg" bitsize="8" type="int"/> + <reg name="sp" bitsize="8" type="int"/> + <reg name="pc" bitsize="8" type="int"/> +</feature> diff --git a/hw/Kconfig b/hw/Kconfig index 62f9ebdc22..4de1797ffd 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -43,6 +43,7 @@ source watchdog/Kconfig # arch Kconfig source arm/Kconfig source alpha/Kconfig +source avr/Kconfig source cris/Kconfig source hppa/Kconfig source i386/Kconfig diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig new file mode 100644 index 0000000000..d31298c3cc --- /dev/null +++ b/hw/avr/Kconfig @@ -0,0 +1,9 @@ +config AVR_ATMEGA_MCU + bool + select AVR_TIMER16 + select AVR_USART + select AVR_POWER + +config ARDUINO + select AVR_ATMEGA_MCU + select UNIMP diff --git a/hw/avr/Makefile.objs b/hw/avr/Makefile.objs new file mode 100644 index 0000000000..4dca064bfc --- /dev/null +++ b/hw/avr/Makefile.objs @@ -0,0 +1,3 @@ +obj-y += boot.o +obj-$(CONFIG_AVR_ATMEGA_MCU) += atmega.o +obj-$(CONFIG_ARDUINO) += arduino.o diff --git a/hw/avr/arduino.c b/hw/avr/arduino.c new file mode 100644 index 0000000000..65093ab6fd --- /dev/null +++ b/hw/avr/arduino.c @@ -0,0 +1,149 @@ +/* + * QEMU Arduino boards + * + * Copyright (c) 2019-2020 Philippe Mathieu-Daudé + * + * This work is licensed under the terms of the GNU GPLv2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* TODO: Implement the use of EXTRAM */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "atmega.h" +#include "boot.h" + +typedef struct ArduinoMachineState { + /*< private >*/ + MachineState parent_obj; + /*< public >*/ + AtmegaMcuState mcu; +} ArduinoMachineState; + +typedef struct ArduinoMachineClass { + /*< private >*/ + MachineClass parent_class; + /*< public >*/ + const char *mcu_type; + uint64_t xtal_hz; +} ArduinoMachineClass; + +#define TYPE_ARDUINO_MACHINE \ + MACHINE_TYPE_NAME("arduino") +#define ARDUINO_MACHINE(obj) \ + OBJECT_CHECK(ArduinoMachineState, (obj), TYPE_ARDUINO_MACHINE) +#define ARDUINO_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(ArduinoMachineClass, (klass), TYPE_ARDUINO_MACHINE) +#define ARDUINO_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ArduinoMachineClass, (obj), TYPE_ARDUINO_MACHINE) + +static void arduino_machine_init(MachineState *machine) +{ + ArduinoMachineClass *amc = ARDUINO_MACHINE_GET_CLASS(machine); + ArduinoMachineState *ams = ARDUINO_MACHINE(machine); + + object_initialize_child(OBJECT(machine), "mcu", &ams->mcu, amc->mcu_type); + object_property_set_uint(OBJECT(&ams->mcu), "xtal-frequency-hz", + amc->xtal_hz, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&ams->mcu), &error_abort); + + if (machine->firmware) { + if (!avr_load_firmware(&ams->mcu.cpu, machine, + &ams->mcu.flash, machine->firmware)) { + exit(1); + } + } +} + +static void arduino_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = arduino_machine_init; + mc->default_cpus = 1; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_parallel = 1; +} + +static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); + + /* https://www.arduino.cc/en/Main/ArduinoBoardDuemilanove */ + mc->desc = "Arduino Duemilanove (ATmega168)", + mc->alias = "2009"; + amc->mcu_type = TYPE_ATMEGA168_MCU; + amc->xtal_hz = 16 * 1000 * 1000; +}; + +static void arduino_uno_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); + + /* https://store.arduino.cc/arduino-uno-rev3 */ + mc->desc = "Arduino UNO (ATmega328P)"; + mc->alias = "uno"; + amc->mcu_type = TYPE_ATMEGA328_MCU; + amc->xtal_hz = 16 * 1000 * 1000; +}; + +static void arduino_mega_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); + + /* https://www.arduino.cc/en/Main/ArduinoBoardMega */ + mc->desc = "Arduino Mega (ATmega1280)"; + mc->alias = "mega"; + amc->mcu_type = TYPE_ATMEGA1280_MCU; + amc->xtal_hz = 16 * 1000 * 1000; +}; + +static void arduino_mega2560_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); + + /* https://store.arduino.cc/arduino-mega-2560-rev3 */ + mc->desc = "Arduino Mega 2560 (ATmega2560)"; + mc->alias = "mega2560"; + amc->mcu_type = TYPE_ATMEGA2560_MCU; + amc->xtal_hz = 16 * 1000 * 1000; /* CSTCE16M0V53-R0 */ +}; + +static const TypeInfo arduino_machine_types[] = { + { + .name = MACHINE_TYPE_NAME("arduino-duemilanove"), + .parent = TYPE_ARDUINO_MACHINE, + .class_init = arduino_duemilanove_class_init, + }, { + .name = MACHINE_TYPE_NAME("arduino-uno"), + .parent = TYPE_ARDUINO_MACHINE, + .class_init = arduino_uno_class_init, + }, { + .name = MACHINE_TYPE_NAME("arduino-mega"), + .parent = TYPE_ARDUINO_MACHINE, + .class_init = arduino_mega_class_init, + }, { + .name = MACHINE_TYPE_NAME("arduino-mega-2560-v3"), + .parent = TYPE_ARDUINO_MACHINE, + .class_init = arduino_mega2560_class_init, + }, { + .name = TYPE_ARDUINO_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(ArduinoMachineState), + .class_size = sizeof(ArduinoMachineClass), + .class_init = arduino_machine_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(arduino_machine_types) diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c new file mode 100644 index 0000000000..7131224431 --- /dev/null +++ b/hw/avr/atmega.c @@ -0,0 +1,458 @@ +/* + * QEMU ATmega MCU + * + * Copyright (c) 2019-2020 Philippe Mathieu-Daudé + * + * This work is licensed under the terms of the GNU GPLv2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/boards.h" /* FIXME memory_region_allocate_system_memory for sram */ +#include "hw/misc/unimp.h" +#include "atmega.h" + +enum AtmegaPeripheral { + POWER0, POWER1, + GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, + GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, + USART0, USART1, USART2, USART3, + TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, + PERIFMAX +}; + +#define GPIO(n) (n + GPIOA) +#define USART(n) (n + USART0) +#define TIMER(n) (n + TIMER0) +#define POWER(n) (n + POWER0) + +typedef struct { + uint16_t addr; + enum AtmegaPeripheral power_index; + uint8_t power_bit; + /* timer specific */ + uint16_t intmask_addr; + uint16_t intflag_addr; + bool is_timer16; +} peripheral_cfg; + +typedef struct AtmegaMcuClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + const char *uc_name; + const char *cpu_type; + size_t flash_size; + size_t eeprom_size; + size_t sram_size; + size_t io_size; + size_t gpio_count; + size_t adc_count; + const uint8_t *irq; + const peripheral_cfg *dev; +} AtmegaMcuClass; + +#define ATMEGA_MCU_CLASS(klass) \ + OBJECT_CLASS_CHECK(AtmegaMcuClass, (klass), TYPE_ATMEGA_MCU) +#define ATMEGA_MCU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AtmegaMcuClass, (obj), TYPE_ATMEGA_MCU) + +static const peripheral_cfg dev168_328[PERIFMAX] = { + [USART0] = { 0xc0, POWER0, 1 }, + [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false }, + [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true }, + [POWER0] = { 0x64 }, + [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false }, + [GPIOD] = { 0x29 }, + [GPIOC] = { 0x26 }, + [GPIOB] = { 0x23 }, +}, dev1280_2560[PERIFMAX] = { + [USART3] = { 0x130, POWER1, 2 }, + [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, + [GPIOL] = { 0x109 }, + [GPIOK] = { 0x106 }, + [GPIOJ] = { 0x103 }, + [GPIOH] = { 0x100 }, + [USART2] = { 0xd0, POWER1, 1 }, + [USART1] = { 0xc8, POWER1, 0 }, + [USART0] = { 0xc0, POWER0, 1 }, + [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false }, /* TODO async */ + [TIMER4] = { 0xa0, POWER1, 4, 0x72, 0x39, true }, + [TIMER3] = { 0x90, POWER1, 3, 0x71, 0x38, true }, + [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true }, + [POWER1] = { 0x65 }, + [POWER0] = { 0x64 }, + [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false }, + [GPIOG] = { 0x32 }, + [GPIOF] = { 0x2f }, + [GPIOE] = { 0x2c }, + [GPIOD] = { 0x29 }, + [GPIOC] = { 0x26 }, + [GPIOB] = { 0x23 }, + [GPIOA] = { 0x20 }, +}; + +enum AtmegaIrq { + USART0_RXC_IRQ, USART0_DRE_IRQ, USART0_TXC_IRQ, + USART1_RXC_IRQ, USART1_DRE_IRQ, USART1_TXC_IRQ, + USART2_RXC_IRQ, USART2_DRE_IRQ, USART2_TXC_IRQ, + USART3_RXC_IRQ, USART3_DRE_IRQ, USART3_TXC_IRQ, + TIMER0_CAPT_IRQ, TIMER0_COMPA_IRQ, TIMER0_COMPB_IRQ, + TIMER0_COMPC_IRQ, TIMER0_OVF_IRQ, + TIMER1_CAPT_IRQ, TIMER1_COMPA_IRQ, TIMER1_COMPB_IRQ, + TIMER1_COMPC_IRQ, TIMER1_OVF_IRQ, + TIMER2_CAPT_IRQ, TIMER2_COMPA_IRQ, TIMER2_COMPB_IRQ, + TIMER2_COMPC_IRQ, TIMER2_OVF_IRQ, + TIMER3_CAPT_IRQ, TIMER3_COMPA_IRQ, TIMER3_COMPB_IRQ, + TIMER3_COMPC_IRQ, TIMER3_OVF_IRQ, + TIMER4_CAPT_IRQ, TIMER4_COMPA_IRQ, TIMER4_COMPB_IRQ, + TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, + TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, + TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, + IRQ_COUNT +}; + +#define USART_IRQ_COUNT 3 +#define USART_RXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_RXC_IRQ) +#define USART_DRE_IRQ(n) (n * USART_IRQ_COUNT + USART0_DRE_IRQ) +#define USART_TXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_TXC_IRQ) +#define TIMER_IRQ_COUNT 5 +#define TIMER_CAPT_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_CAPT_IRQ) +#define TIMER_COMPA_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPA_IRQ) +#define TIMER_COMPB_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPB_IRQ) +#define TIMER_COMPC_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPC_IRQ) +#define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) + +static const uint8_t irq168_328[IRQ_COUNT] = { + [TIMER2_COMPA_IRQ] = 8, + [TIMER2_COMPB_IRQ] = 9, + [TIMER2_OVF_IRQ] = 10, + [TIMER1_CAPT_IRQ] = 11, + [TIMER1_COMPA_IRQ] = 12, + [TIMER1_COMPB_IRQ] = 13, + [TIMER1_OVF_IRQ] = 14, + [TIMER0_COMPA_IRQ] = 15, + [TIMER0_COMPB_IRQ] = 16, + [TIMER0_OVF_IRQ] = 17, + [USART0_RXC_IRQ] = 19, + [USART0_DRE_IRQ] = 20, + [USART0_TXC_IRQ] = 21, +}, irq1280_2560[IRQ_COUNT] = { + [TIMER2_COMPA_IRQ] = 14, + [TIMER2_COMPB_IRQ] = 15, + [TIMER2_OVF_IRQ] = 16, + [TIMER1_CAPT_IRQ] = 17, + [TIMER1_COMPA_IRQ] = 18, + [TIMER1_COMPB_IRQ] = 19, + [TIMER1_COMPC_IRQ] = 20, + [TIMER1_OVF_IRQ] = 21, + [TIMER0_COMPA_IRQ] = 22, + [TIMER0_COMPB_IRQ] = 23, + [TIMER0_OVF_IRQ] = 24, + [USART0_RXC_IRQ] = 26, + [USART0_DRE_IRQ] = 27, + [USART0_TXC_IRQ] = 28, + [TIMER3_CAPT_IRQ] = 32, + [TIMER3_COMPA_IRQ] = 33, + [TIMER3_COMPB_IRQ] = 34, + [TIMER3_COMPC_IRQ] = 35, + [TIMER3_OVF_IRQ] = 36, + [USART1_RXC_IRQ] = 37, + [USART1_DRE_IRQ] = 38, + [USART1_TXC_IRQ] = 39, + [TIMER4_CAPT_IRQ] = 42, + [TIMER4_COMPA_IRQ] = 43, + [TIMER4_COMPB_IRQ] = 44, + [TIMER4_COMPC_IRQ] = 45, + [TIMER4_OVF_IRQ] = 46, + [TIMER5_CAPT_IRQ] = 47, + [TIMER5_COMPA_IRQ] = 48, + [TIMER5_COMPB_IRQ] = 49, + [TIMER5_COMPC_IRQ] = 50, + [TIMER5_OVF_IRQ] = 51, + [USART2_RXC_IRQ] = 52, + [USART2_DRE_IRQ] = 53, + [USART2_TXC_IRQ] = 54, + [USART3_RXC_IRQ] = 55, + [USART3_DRE_IRQ] = 56, + [USART3_TXC_IRQ] = 57, +}; + +static void connect_peripheral_irq(const AtmegaMcuClass *k, + SysBusDevice *dev, int dev_irqn, + DeviceState *cpu, + unsigned peripheral_index) +{ + int cpu_irq = k->irq[peripheral_index]; + + if (!cpu_irq) { + return; + } + /* FIXME move that to avr_cpu_set_int() once 'sample' board is removed */ + assert(cpu_irq >= 2); + cpu_irq -= 2; + + sysbus_connect_irq(dev, dev_irqn, qdev_get_gpio_in(cpu, cpu_irq)); +} + +static void connect_power_reduction_gpio(AtmegaMcuState *s, + const AtmegaMcuClass *k, + DeviceState *cpu, + unsigned peripheral_index) +{ + unsigned power_index = k->dev[peripheral_index].power_index; + assert(k->dev[power_index].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pwr[power_index - POWER0]), + k->dev[peripheral_index].power_bit, + qdev_get_gpio_in(cpu, 0)); +} + +static void atmega_realize(DeviceState *dev, Error **errp) +{ + AtmegaMcuState *s = ATMEGA_MCU(dev); + const AtmegaMcuClass *mc = ATMEGA_MCU_GET_CLASS(dev); + DeviceState *cpudev; + SysBusDevice *sbd; + char *devname; + size_t i; + + assert(mc->io_size <= 0x200); + + if (!s->xtal_freq_hz) { + error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); + return; + } + + /* CPU */ + object_initialize_child(OBJECT(dev), "cpu", &s->cpu, mc->cpu_type); + object_property_set_bool(OBJECT(&s->cpu), "realized", true, &error_abort); + cpudev = DEVICE(&s->cpu); + + /* SRAM */ + memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size, + &error_abort); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + mc->io_size, &s->sram); + + /* Flash */ + memory_region_init_rom(&s->flash, OBJECT(dev), + "flash", mc->flash_size, &error_fatal); + memory_region_add_subregion(get_system_memory(), OFFSET_CODE, &s->flash); + + /* + * I/O + * + * 0x00 - 0x1f: Registers + * 0x20 - 0x5f: I/O memory + * 0x60 - 0xff: Extended I/O + */ + s->io = qdev_new(TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(s->io, "name", "I/O"); + qdev_prop_set_uint64(s->io, "size", mc->io_size); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s->io), &error_fatal); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->io), 0, OFFSET_DATA, -1234); + + /* Power Reduction */ + for (i = 0; i < POWER_MAX; i++) { + int idx = POWER(i); + if (!mc->dev[idx].addr) { + continue; + } + devname = g_strdup_printf("power%zu", i); + object_initialize_child(OBJECT(dev), devname, &s->pwr[i], + TYPE_AVR_MASK); + sysbus_realize(SYS_BUS_DEVICE(&s->pwr[i]), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pwr[i]), 0, + OFFSET_DATA + mc->dev[idx].addr); + g_free(devname); + } + + /* GPIO */ + for (i = 0; i < GPIO_MAX; i++) { + int idx = GPIO(i); + if (!mc->dev[idx].addr) { + continue; + } + devname = g_strdup_printf("atmega-gpio-%c", 'a' + (char)i); + create_unimplemented_device(devname, + OFFSET_DATA + mc->dev[idx].addr, 3); + g_free(devname); + } + + /* USART */ + for (i = 0; i < USART_MAX; i++) { + int idx = USART(i); + if (!mc->dev[idx].addr) { + continue; + } + devname = g_strdup_printf("usart%zu", i); + object_initialize_child(OBJECT(dev), devname, &s->usart[i], + TYPE_AVR_USART); + qdev_prop_set_chr(DEVICE(&s->usart[i]), "chardev", serial_hd(i)); + sbd = SYS_BUS_DEVICE(&s->usart[i]); + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[USART(i)].addr); + connect_peripheral_irq(mc, sbd, 0, cpudev, USART_RXC_IRQ(i)); + connect_peripheral_irq(mc, sbd, 1, cpudev, USART_DRE_IRQ(i)); + connect_peripheral_irq(mc, sbd, 2, cpudev, USART_TXC_IRQ(i)); + connect_power_reduction_gpio(s, mc, DEVICE(&s->usart[i]), idx); + g_free(devname); + } + + /* Timer */ + for (i = 0; i < TIMER_MAX; i++) { + int idx = TIMER(i); + if (!mc->dev[idx].addr) { + continue; + } + if (!mc->dev[idx].is_timer16) { + create_unimplemented_device("avr-timer8", + OFFSET_DATA + mc->dev[idx].addr, 5); + create_unimplemented_device("avr-timer8-intmask", + OFFSET_DATA + + mc->dev[idx].intmask_addr, 1); + create_unimplemented_device("avr-timer8-intflag", + OFFSET_DATA + + mc->dev[idx].intflag_addr, 1); + continue; + } + devname = g_strdup_printf("timer%zu", i); + object_initialize_child(OBJECT(dev), devname, &s->timer[i], + TYPE_AVR_TIMER16); + object_property_set_uint(OBJECT(&s->timer[i]), "cpu-frequency-hz", + s->xtal_freq_hz, &error_abort); + sbd = SYS_BUS_DEVICE(&s->timer[i]); + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[idx].addr); + sysbus_mmio_map(sbd, 1, OFFSET_DATA + mc->dev[idx].intmask_addr); + sysbus_mmio_map(sbd, 2, OFFSET_DATA + mc->dev[idx].intflag_addr); + connect_peripheral_irq(mc, sbd, 0, cpudev, TIMER_CAPT_IRQ(i)); + connect_peripheral_irq(mc, sbd, 1, cpudev, TIMER_COMPA_IRQ(i)); + connect_peripheral_irq(mc, sbd, 2, cpudev, TIMER_COMPB_IRQ(i)); + connect_peripheral_irq(mc, sbd, 3, cpudev, TIMER_COMPC_IRQ(i)); + connect_peripheral_irq(mc, sbd, 4, cpudev, TIMER_OVF_IRQ(i)); + connect_power_reduction_gpio(s, mc, DEVICE(&s->timer[i]), idx); + g_free(devname); + } + + 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); +} + +static Property atmega_props[] = { + DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState, + xtal_freq_hz, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void atmega_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = atmega_realize; + device_class_set_props(dc, atmega_props); + /* Reason: Mapped at fixed location on the system bus */ + dc->user_creatable = false; +} + +static void atmega168_class_init(ObjectClass *oc, void *data) +{ + AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); + + amc->cpu_type = AVR_CPU_TYPE_NAME("avr5"); + amc->flash_size = 16 * KiB; + amc->eeprom_size = 512; + amc->sram_size = 1 * KiB; + amc->io_size = 256; + amc->gpio_count = 23; + amc->adc_count = 6; + amc->irq = irq168_328; + amc->dev = dev168_328; +}; + +static void atmega328_class_init(ObjectClass *oc, void *data) +{ + AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); + + amc->cpu_type = AVR_CPU_TYPE_NAME("avr5"); + amc->flash_size = 32 * KiB; + amc->eeprom_size = 1 * KiB; + amc->sram_size = 2 * KiB; + amc->io_size = 256; + amc->gpio_count = 23; + amc->adc_count = 6; + amc->irq = irq168_328; + amc->dev = dev168_328; +}; + +static void atmega1280_class_init(ObjectClass *oc, void *data) +{ + AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); + + amc->cpu_type = AVR_CPU_TYPE_NAME("avr6"); + amc->flash_size = 128 * KiB; + amc->eeprom_size = 4 * KiB; + amc->sram_size = 8 * KiB; + amc->io_size = 512; + amc->gpio_count = 86; + amc->adc_count = 16; + amc->irq = irq1280_2560; + amc->dev = dev1280_2560; +}; + +static void atmega2560_class_init(ObjectClass *oc, void *data) +{ + AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); + + amc->cpu_type = AVR_CPU_TYPE_NAME("avr6"); + amc->flash_size = 256 * KiB; + amc->eeprom_size = 4 * KiB; + amc->sram_size = 8 * KiB; + amc->io_size = 512; + amc->gpio_count = 54; + amc->adc_count = 16; + amc->irq = irq1280_2560; + amc->dev = dev1280_2560; +}; + +static const TypeInfo atmega_mcu_types[] = { + { + .name = TYPE_ATMEGA168_MCU, + .parent = TYPE_ATMEGA_MCU, + .class_init = atmega168_class_init, + }, { + .name = TYPE_ATMEGA328_MCU, + .parent = TYPE_ATMEGA_MCU, + .class_init = atmega328_class_init, + }, { + .name = TYPE_ATMEGA1280_MCU, + .parent = TYPE_ATMEGA_MCU, + .class_init = atmega1280_class_init, + }, { + .name = TYPE_ATMEGA2560_MCU, + .parent = TYPE_ATMEGA_MCU, + .class_init = atmega2560_class_init, + }, { + .name = TYPE_ATMEGA_MCU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AtmegaMcuState), + .class_size = sizeof(AtmegaMcuClass), + .class_init = atmega_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(atmega_mcu_types) diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h new file mode 100644 index 0000000000..0928cb0ce6 --- /dev/null +++ b/hw/avr/atmega.h @@ -0,0 +1,48 @@ +/* + * QEMU ATmega MCU + * + * Copyright (c) 2019-2020 Philippe Mathieu-Daudé + * + * This work is licensed under the terms of the GNU GPLv2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_AVR_ATMEGA_H +#define HW_AVR_ATMEGA_H + +#include "hw/char/avr_usart.h" +#include "hw/timer/avr_timer16.h" +#include "hw/misc/avr_power.h" +#include "target/avr/cpu.h" + +#define TYPE_ATMEGA_MCU "ATmega" +#define TYPE_ATMEGA168_MCU "ATmega168" +#define TYPE_ATMEGA328_MCU "ATmega328" +#define TYPE_ATMEGA1280_MCU "ATmega1280" +#define TYPE_ATMEGA2560_MCU "ATmega2560" + +#define ATMEGA_MCU(obj) OBJECT_CHECK(AtmegaMcuState, (obj), TYPE_ATMEGA_MCU) + +#define POWER_MAX 2 +#define USART_MAX 4 +#define TIMER_MAX 6 +#define GPIO_MAX 12 + +typedef struct AtmegaMcuState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + AVRCPU cpu; + MemoryRegion flash; + MemoryRegion eeprom; + MemoryRegion sram; + DeviceState *io; + AVRMaskState pwr[POWER_MAX]; + AVRUsartState usart[USART_MAX]; + AVRTimer16State timer[TIMER_MAX]; + uint64_t xtal_freq_hz; +} AtmegaMcuState; + +#endif /* HW_AVR_ATMEGA_H */ diff --git a/hw/avr/boot.c b/hw/avr/boot.c new file mode 100644 index 0000000000..6fbcde4061 --- /dev/null +++ b/hw/avr/boot.c @@ -0,0 +1,115 @@ +/* + * AVR loader helpers + * + * Copyright (c) 2019-2020 Philippe Mathieu-Daudé + * + * This work is licensed under the terms of the GNU GPLv2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/loader.h" +#include "elf.h" +#include "boot.h" +#include "qemu/error-report.h" + +static const char *avr_elf_e_flags_to_cpu_type(uint32_t flags) +{ + switch (flags & EF_AVR_MACH) { + case bfd_mach_avr1: + return AVR_CPU_TYPE_NAME("avr1"); + case bfd_mach_avr2: + return AVR_CPU_TYPE_NAME("avr2"); + case bfd_mach_avr25: + return AVR_CPU_TYPE_NAME("avr25"); + case bfd_mach_avr3: + return AVR_CPU_TYPE_NAME("avr3"); + case bfd_mach_avr31: + return AVR_CPU_TYPE_NAME("avr31"); + case bfd_mach_avr35: + return AVR_CPU_TYPE_NAME("avr35"); + case bfd_mach_avr4: + return AVR_CPU_TYPE_NAME("avr4"); + case bfd_mach_avr5: + return AVR_CPU_TYPE_NAME("avr5"); + case bfd_mach_avr51: + return AVR_CPU_TYPE_NAME("avr51"); + case bfd_mach_avr6: + return AVR_CPU_TYPE_NAME("avr6"); + case bfd_mach_avrtiny: + return AVR_CPU_TYPE_NAME("avrtiny"); + case bfd_mach_avrxmega2: + return AVR_CPU_TYPE_NAME("xmega2"); + case bfd_mach_avrxmega3: + return AVR_CPU_TYPE_NAME("xmega3"); + case bfd_mach_avrxmega4: + return AVR_CPU_TYPE_NAME("xmega4"); + case bfd_mach_avrxmega5: + return AVR_CPU_TYPE_NAME("xmega5"); + case bfd_mach_avrxmega6: + return AVR_CPU_TYPE_NAME("xmega6"); + case bfd_mach_avrxmega7: + return AVR_CPU_TYPE_NAME("xmega7"); + default: + return NULL; + } +} + +bool avr_load_firmware(AVRCPU *cpu, MachineState *ms, + MemoryRegion *program_mr, const char *firmware) +{ + const char *filename; + int bytes_loaded; + uint64_t entry; + uint32_t e_flags; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware); + if (filename == NULL) { + error_report("Unable to find %s", firmware); + return false; + } + + bytes_loaded = load_elf_ram_sym(filename, + NULL, NULL, NULL, + &entry, NULL, NULL, + &e_flags, 0, EM_AVR, 0, 0, + NULL, true, NULL); + if (bytes_loaded >= 0) { + /* If ELF file is provided, determine CPU type reading ELF e_flags. */ + const char *elf_cpu = avr_elf_e_flags_to_cpu_type(e_flags); + const char *mcu_cpu_type = object_get_typename(OBJECT(cpu)); + int cpu_len = strlen(mcu_cpu_type) - strlen(AVR_CPU_TYPE_SUFFIX); + + if (entry) { + error_report("BIOS entry_point must be 0x0000 " + "(ELF image '%s' has entry_point 0x%04" PRIx64 ")", + firmware, entry); + return false; + } + if (!elf_cpu) { + warn_report("Could not determine CPU type for ELF image '%s', " + "assuming '%.*s' CPU", + firmware, cpu_len, mcu_cpu_type); + return true; + } + if (strcmp(elf_cpu, mcu_cpu_type)) { + error_report("Current machine: %s with '%.*s' CPU", + MACHINE_GET_CLASS(ms)->desc, cpu_len, mcu_cpu_type); + error_report("ELF image '%s' is for '%.*s' CPU", + firmware, + (int)(strlen(elf_cpu) - strlen(AVR_CPU_TYPE_SUFFIX)), + elf_cpu); + return false; + } + } else { + bytes_loaded = load_image_mr(filename, program_mr); + } + if (bytes_loaded < 0) { + error_report("Unable to load firmware image %s as ELF or raw binary", + firmware); + return false; + } + return true; +} diff --git a/hw/avr/boot.h b/hw/avr/boot.h new file mode 100644 index 0000000000..684d553322 --- /dev/null +++ b/hw/avr/boot.h @@ -0,0 +1,33 @@ +/* + * AVR loader helpers + * + * Copyright (c) 2019-2020 Philippe Mathieu-Daudé + * + * This work is licensed under the terms of the GNU GPLv2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_AVR_BOOT_H +#define HW_AVR_BOOT_H + +#include "hw/boards.h" +#include "cpu.h" + +/** + * avr_load_firmware: load an image into a memory region + * + * @cpu: Handle a AVR CPU object + * @ms: A MachineState + * @mr: Memory Region to load into + * @firmware: Path to the firmware file (raw binary or ELF format) + * + * Load a firmware supplied by the machine or by the user with the + * '-bios' command line option, and put it in target memory. + * + * Returns: true on success, false on error. + */ +bool avr_load_firmware(AVRCPU *cpu, MachineState *ms, + MemoryRegion *mr, const char *firmware); + +#endif diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 874627520c..b7e0e4d5fa 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -49,3 +49,6 @@ config TERMINAL3270 config RENESAS_SCI bool + +config AVR_USART + bool diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 8306c4a789..bf177ac41d 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -22,6 +22,7 @@ common-obj-$(CONFIG_DIGIC) += digic-uart.o common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o common-obj-$(CONFIG_RASPI) += bcm2835_aux.o common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o +common-obj-$(CONFIG_AVR_USART) += avr_usart.o common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c new file mode 100644 index 0000000000..fbe2a112b7 --- /dev/null +++ b/hw/char/avr_usart.c @@ -0,0 +1,320 @@ +/* + * AVR USART + * + * Copyright (c) 2018 University of Kent + * Author: Sarah Harris + * + * 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 "hw/char/avr_usart.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" + +static int avr_usart_can_receive(void *opaque) +{ + AVRUsartState *usart = opaque; + + if (usart->data_valid || !(usart->csrb & USART_CSRB_RXEN)) { + return 0; + } + return 1; +} + +static void avr_usart_receive(void *opaque, const uint8_t *buffer, int size) +{ + AVRUsartState *usart = opaque; + assert(size == 1); + assert(!usart->data_valid); + usart->data = buffer[0]; + usart->data_valid = true; + usart->csra |= USART_CSRA_RXC; + if (usart->csrb & USART_CSRB_RXCIE) { + qemu_set_irq(usart->rxc_irq, 1); + } +} + +static void update_char_mask(AVRUsartState *usart) +{ + uint8_t mode = ((usart->csrc & USART_CSRC_CSZ0) ? 1 : 0) | + ((usart->csrc & USART_CSRC_CSZ1) ? 2 : 0) | + ((usart->csrb & USART_CSRB_CSZ2) ? 4 : 0); + switch (mode) { + case 0: + usart->char_mask = 0b11111; + break; + case 1: + usart->char_mask = 0b111111; + break; + case 2: + usart->char_mask = 0b1111111; + break; + case 3: + usart->char_mask = 0b11111111; + break; + case 4: + /* Fallthrough. */ + case 5: + /* Fallthrough. */ + case 6: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Reserved character size 0x%x\n", + __func__, + mode); + break; + case 7: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Nine bit character size not supported (forcing eight)\n", + __func__); + usart->char_mask = 0b11111111; + break; + default: + assert(0); + } +} + +static void avr_usart_reset(DeviceState *dev) +{ + AVRUsartState *usart = AVR_USART(dev); + usart->data_valid = false; + usart->csra = 0b00100000; + usart->csrb = 0b00000000; + usart->csrc = 0b00000110; + usart->brrl = 0; + usart->brrh = 0; + update_char_mask(usart); + qemu_set_irq(usart->rxc_irq, 0); + qemu_set_irq(usart->txc_irq, 0); + qemu_set_irq(usart->dre_irq, 0); +} + +static uint64_t avr_usart_read(void *opaque, hwaddr addr, unsigned int size) +{ + AVRUsartState *usart = opaque; + uint8_t data; + assert(size == 1); + + if (!usart->enabled) { + return 0; + } + + switch (addr) { + case USART_DR: + if (!(usart->csrb & USART_CSRB_RXEN)) { + /* Receiver disabled, ignore. */ + return 0; + } + if (usart->data_valid) { + data = usart->data & usart->char_mask; + usart->data_valid = false; + } else { + data = 0; + } + usart->csra &= 0xff ^ USART_CSRA_RXC; + qemu_set_irq(usart->rxc_irq, 0); + qemu_chr_fe_accept_input(&usart->chr); + return data; + case USART_CSRA: + return usart->csra; + case USART_CSRB: + return usart->csrb; + case USART_CSRC: + return usart->csrc; + case USART_BRRL: + return usart->brrl; + case USART_BRRH: + return usart->brrh; + default: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, + addr); + } + return 0; +} + +static void avr_usart_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + AVRUsartState *usart = opaque; + uint8_t mask; + uint8_t data; + assert((value & 0xff) == value); + assert(size == 1); + + if (!usart->enabled) { + return; + } + + switch (addr) { + case USART_DR: + if (!(usart->csrb & USART_CSRB_TXEN)) { + /* Transmitter disabled, ignore. */ + return; + } + usart->csra |= USART_CSRA_TXC; + usart->csra |= USART_CSRA_DRE; + if (usart->csrb & USART_CSRB_TXCIE) { + qemu_set_irq(usart->txc_irq, 1); + usart->csra &= 0xff ^ USART_CSRA_TXC; + } + if (usart->csrb & USART_CSRB_DREIE) { + qemu_set_irq(usart->dre_irq, 1); + } + data = value; + qemu_chr_fe_write_all(&usart->chr, &data, 1); + break; + case USART_CSRA: + mask = 0b01000011; + /* Mask read-only bits. */ + value = (value & mask) | (usart->csra & (0xff ^ mask)); + usart->csra = value; + if (value & USART_CSRA_TXC) { + usart->csra ^= USART_CSRA_TXC; + qemu_set_irq(usart->txc_irq, 0); + } + if (value & USART_CSRA_MPCM) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: MPCM not supported by USART\n", + __func__); + } + break; + case USART_CSRB: + mask = 0b11111101; + /* Mask read-only bits. */ + value = (value & mask) | (usart->csrb & (0xff ^ mask)); + usart->csrb = value; + if (!(value & USART_CSRB_RXEN)) { + /* Receiver disabled, flush input buffer. */ + usart->data_valid = false; + } + qemu_set_irq(usart->rxc_irq, + ((value & USART_CSRB_RXCIE) && + (usart->csra & USART_CSRA_RXC)) ? 1 : 0); + qemu_set_irq(usart->txc_irq, + ((value & USART_CSRB_TXCIE) && + (usart->csra & USART_CSRA_TXC)) ? 1 : 0); + qemu_set_irq(usart->dre_irq, + ((value & USART_CSRB_DREIE) && + (usart->csra & USART_CSRA_DRE)) ? 1 : 0); + update_char_mask(usart); + break; + case USART_CSRC: + usart->csrc = value; + if ((value & USART_CSRC_MSEL1) && (value & USART_CSRC_MSEL0)) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: SPI mode not supported by USART\n", + __func__); + } + if ((value & USART_CSRC_MSEL1) && !(value & USART_CSRC_MSEL0)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad USART mode\n", __func__); + } + if (!(value & USART_CSRC_PM1) && (value & USART_CSRC_PM0)) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Bad USART parity mode\n", + __func__); + } + update_char_mask(usart); + break; + case USART_BRRL: + usart->brrl = value; + break; + case USART_BRRH: + usart->brrh = value & 0b00001111; + break; + default: + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, + addr); + } +} + +static const MemoryRegionOps avr_usart_ops = { + .read = avr_usart_read, + .write = avr_usart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = {.min_access_size = 1, .max_access_size = 1} +}; + +static Property avr_usart_properties[] = { + DEFINE_PROP_CHR("chardev", AVRUsartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void avr_usart_pr(void *opaque, int irq, int level) +{ + AVRUsartState *s = AVR_USART(opaque); + + s->enabled = !level; + + if (!s->enabled) { + avr_usart_reset(DEVICE(s)); + } +} + +static void avr_usart_init(Object *obj) +{ + AVRUsartState *s = AVR_USART(obj); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rxc_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->dre_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->txc_irq); + memory_region_init_io(&s->mmio, obj, &avr_usart_ops, s, TYPE_AVR_USART, 7); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + qdev_init_gpio_in(DEVICE(s), avr_usart_pr, 1); + s->enabled = true; +} + +static void avr_usart_realize(DeviceState *dev, Error **errp) +{ + AVRUsartState *s = AVR_USART(dev); + qemu_chr_fe_set_handlers(&s->chr, avr_usart_can_receive, + avr_usart_receive, NULL, NULL, + s, NULL, true); + avr_usart_reset(dev); +} + +static void avr_usart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = avr_usart_reset; + device_class_set_props(dc, avr_usart_properties); + dc->realize = avr_usart_realize; +} + +static const TypeInfo avr_usart_info = { + .name = TYPE_AVR_USART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AVRUsartState), + .instance_init = avr_usart_init, + .class_init = avr_usart_class_init, +}; + +static void avr_usart_register_types(void) +{ + type_register_static(&avr_usart_info); +} + +type_init(avr_usart_register_types) diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index bdd77d8020..92c397ca07 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -131,4 +131,7 @@ config MAC_VIA select MOS6522 select ADB +config AVR_POWER + bool + source macio/Kconfig diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 5aaca8a039..6be3d255ab 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -91,3 +91,5 @@ common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o + +obj-$(CONFIG_AVR_POWER) += avr_power.o diff --git a/hw/misc/avr_power.c b/hw/misc/avr_power.c new file mode 100644 index 0000000000..a5412f2cfe --- /dev/null +++ b/hw/misc/avr_power.c @@ -0,0 +1,113 @@ +/* + * AVR Power Reduction Management + * + * Copyright (c) 2019-2020 Michael Rolnik + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/misc/avr_power.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/irq.h" +#include "trace.h" + +static void avr_mask_reset(DeviceState *dev) +{ + AVRMaskState *s = AVR_MASK(dev); + + s->val = 0x00; + + for (int i = 0; i < 8; i++) { + qemu_set_irq(s->irq[i], 0); + } +} + +static uint64_t avr_mask_read(void *opaque, hwaddr offset, unsigned size) +{ + assert(size == 1); + assert(offset == 0); + AVRMaskState *s = opaque; + + trace_avr_power_read(s->val); + + return (uint64_t)s->val; +} + +static void avr_mask_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size == 1); + assert(offset == 0); + AVRMaskState *s = opaque; + uint8_t val8 = val64; + + trace_avr_power_write(val8); + s->val = val8; + for (int i = 0; i < 8; i++) { + qemu_set_irq(s->irq[i], (val8 & (1 << i)) != 0); + } +} + +static const MemoryRegionOps avr_mask_ops = { + .read = avr_mask_read, + .write = avr_mask_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .max_access_size = 1, + }, +}; + +static void avr_mask_init(Object *dev) +{ + AVRMaskState *s = AVR_MASK(dev); + SysBusDevice *busdev = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, dev, &avr_mask_ops, s, TYPE_AVR_MASK, + 0x01); + sysbus_init_mmio(busdev, &s->iomem); + + for (int i = 0; i < 8; i++) { + sysbus_init_irq(busdev, &s->irq[i]); + } + s->val = 0x00; +} + +static void avr_mask_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = avr_mask_reset; +} + +static const TypeInfo avr_mask_info = { + .name = TYPE_AVR_MASK, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AVRMaskState), + .class_init = avr_mask_class_init, + .instance_init = avr_mask_init, +}; + +static void avr_mask_register_types(void) +{ + type_register_static(&avr_mask_info); +} + +type_init(avr_mask_register_types) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index ebea53735c..066752aa90 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -19,6 +19,10 @@ allwinner_h3_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +# avr_power.c +avr_power_read(uint8_t value) "power_reduc read value:%u" +avr_power_write(uint8_t value) "power_reduc write value:%u" + # eccmemctl.c ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x" ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x" diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 59a667c503..8749edfb6a 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -41,3 +41,6 @@ config RENESAS_TMR config RENESAS_CMT bool + +config AVR_TIMER16 + bool diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index a39f6ec0c2..1303b13e0d 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -37,3 +37,5 @@ common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o common-obj-$(CONFIG_MSF2) += mss-timer.o common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o + +obj-$(CONFIG_AVR_TIMER16) += avr_timer16.o diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c new file mode 100644 index 0000000000..c48555da52 --- /dev/null +++ b/hw/timer/avr_timer16.c @@ -0,0 +1,621 @@ +/* + * AVR 16-bit timer + * + * Copyright (c) 2018 University of Kent + * Author: Ed Robbins + * + * 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> + */ + +/* + * Driver for 16 bit timers on 8 bit AVR devices. + * Note: + * ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 bit + */ + +/* + * XXX TODO: Power Reduction Register support + * prescaler pause support + * PWM modes, GPIO, output capture pins, input compare pin + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/timer/avr_timer16.h" +#include "trace.h" + +/* Register offsets */ +#define T16_CRA 0x0 +#define T16_CRB 0x1 +#define T16_CRC 0x2 +#define T16_CNTL 0x4 +#define T16_CNTH 0x5 +#define T16_ICRL 0x6 +#define T16_ICRH 0x7 +#define T16_OCRAL 0x8 +#define T16_OCRAH 0x9 +#define T16_OCRBL 0xa +#define T16_OCRBH 0xb +#define T16_OCRCL 0xc +#define T16_OCRCH 0xd + +/* Field masks */ +#define T16_CRA_WGM01 0x3 +#define T16_CRA_COMC 0xc +#define T16_CRA_COMB 0x30 +#define T16_CRA_COMA 0xc0 +#define T16_CRA_OC_CONF \ + (T16_CRA_COMA | T16_CRA_COMB | T16_CRA_COMC) + +#define T16_CRB_CS 0x7 +#define T16_CRB_WGM23 0x18 +#define T16_CRB_ICES 0x40 +#define T16_CRB_ICNC 0x80 + +#define T16_CRC_FOCC 0x20 +#define T16_CRC_FOCB 0x40 +#define T16_CRC_FOCA 0x80 + +/* Fields masks both TIMSK and TIFR (interrupt mask/flag registers) */ +#define T16_INT_TOV 0x1 /* Timer overflow */ +#define T16_INT_OCA 0x2 /* Output compare A */ +#define T16_INT_OCB 0x4 /* Output compare B */ +#define T16_INT_OCC 0x8 /* Output compare C */ +#define T16_INT_IC 0x20 /* Input capture */ + +/* Clock source values */ +#define T16_CLKSRC_STOPPED 0 +#define T16_CLKSRC_DIV1 1 +#define T16_CLKSRC_DIV8 2 +#define T16_CLKSRC_DIV64 3 +#define T16_CLKSRC_DIV256 4 +#define T16_CLKSRC_DIV1024 5 +#define T16_CLKSRC_EXT_FALLING 6 +#define T16_CLKSRC_EXT_RISING 7 + +/* Timer mode values (not including PWM modes) */ +#define T16_MODE_NORMAL 0 +#define T16_MODE_CTC_OCRA 4 +#define T16_MODE_CTC_ICR 12 + +/* Accessors */ +#define CLKSRC(t16) (t16->crb & T16_CRB_CS) +#define MODE(t16) (((t16->crb & T16_CRB_WGM23) >> 1) | \ + (t16->cra & T16_CRA_WGM01)) +#define CNT(t16) VAL16(t16->cntl, t16->cnth) +#define OCRA(t16) VAL16(t16->ocral, t16->ocrah) +#define OCRB(t16) VAL16(t16->ocrbl, t16->ocrbh) +#define OCRC(t16) VAL16(t16->ocrcl, t16->ocrch) +#define ICR(t16) VAL16(t16->icrl, t16->icrh) + +/* Helper macros */ +#define VAL16(l, h) ((h << 8) | l) +#define DB_PRINT(fmt, args...) /* Nothing */ + +static inline int64_t avr_timer16_ns_to_ticks(AVRTimer16State *t16, int64_t t) +{ + if (t16->period_ns == 0) { + return 0; + } + return t / t16->period_ns; +} + +static void avr_timer16_update_cnt(AVRTimer16State *t16) +{ + uint16_t cnt; + cnt = avr_timer16_ns_to_ticks(t16, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + t16->reset_time_ns); + t16->cntl = (uint8_t)(cnt & 0xff); + t16->cnth = (uint8_t)((cnt & 0xff00) >> 8); +} + +static inline void avr_timer16_recalc_reset_time(AVRTimer16State *t16) +{ + t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + CNT(t16) * t16->period_ns; +} + +static void avr_timer16_clock_reset(AVRTimer16State *t16) +{ + t16->cntl = 0; + t16->cnth = 0; + t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void avr_timer16_clksrc_update(AVRTimer16State *t16) +{ + uint16_t divider = 0; + switch (CLKSRC(t16)) { + case T16_CLKSRC_EXT_FALLING: + case T16_CLKSRC_EXT_RISING: + qemu_log_mask(LOG_UNIMP, "%s: external clock source unsupported\n", + __func__); + break; + case T16_CLKSRC_STOPPED: + break; + case T16_CLKSRC_DIV1: + divider = 1; + break; + case T16_CLKSRC_DIV8: + divider = 8; + break; + case T16_CLKSRC_DIV64: + divider = 64; + break; + case T16_CLKSRC_DIV256: + divider = 256; + break; + case T16_CLKSRC_DIV1024: + divider = 1024; + break; + default: + break; + } + if (divider) { + t16->freq_hz = t16->cpu_freq_hz / divider; + t16->period_ns = NANOSECONDS_PER_SECOND / t16->freq_hz; + trace_avr_timer16_clksrc_update(t16->freq_hz, t16->period_ns, + (uint64_t)(1e6 / t16->freq_hz)); + } +} + +static void avr_timer16_set_alarm(AVRTimer16State *t16) +{ + if (CLKSRC(t16) == T16_CLKSRC_EXT_FALLING || + CLKSRC(t16) == T16_CLKSRC_EXT_RISING || + CLKSRC(t16) == T16_CLKSRC_STOPPED) { + /* Timer is disabled or set to external clock source (unsupported) */ + return; + } + + uint64_t alarm_offset = 0xffff; + enum NextInterrupt next_interrupt = OVERFLOW; + + switch (MODE(t16)) { + case T16_MODE_NORMAL: + /* Normal mode */ + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCA)) { + alarm_offset = OCRA(t16); + next_interrupt = COMPA; + } + break; + case T16_MODE_CTC_OCRA: + /* CTC mode, top = ocra */ + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16)) { + alarm_offset = OCRA(t16); + next_interrupt = COMPA; + } + break; + case T16_MODE_CTC_ICR: + /* CTC mode, top = icr */ + if (ICR(t16) < alarm_offset && ICR(t16) > CNT(t16)) { + alarm_offset = ICR(t16); + next_interrupt = CAPT; + } + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCA)) { + alarm_offset = OCRA(t16); + next_interrupt = COMPA; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: pwm modes are unsupported\n", + __func__); + return; + } + if (OCRB(t16) < alarm_offset && OCRB(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCB)) { + alarm_offset = OCRB(t16); + next_interrupt = COMPB; + } + if (OCRC(t16) < alarm_offset && OCRB(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCC)) { + alarm_offset = OCRB(t16); + next_interrupt = COMPC; + } + alarm_offset -= CNT(t16); + + t16->next_interrupt = next_interrupt; + uint64_t alarm_ns = + t16->reset_time_ns + ((CNT(t16) + alarm_offset) * t16->period_ns); + timer_mod(t16->timer, alarm_ns); + + trace_avr_timer16_next_alarm(alarm_offset * t16->period_ns); +} + +static void avr_timer16_interrupt(void *opaque) +{ + AVRTimer16State *t16 = opaque; + uint8_t mode = MODE(t16); + + avr_timer16_update_cnt(t16); + + if (CLKSRC(t16) == T16_CLKSRC_EXT_FALLING || + CLKSRC(t16) == T16_CLKSRC_EXT_RISING || + CLKSRC(t16) == T16_CLKSRC_STOPPED) { + /* Timer is disabled or set to external clock source (unsupported) */ + return; + } + + trace_avr_timer16_interrupt_count(CNT(t16)); + + /* Counter overflow */ + if (t16->next_interrupt == OVERFLOW) { + trace_avr_timer16_interrupt_overflow("counter 0xffff"); + avr_timer16_clock_reset(t16); + if (t16->imsk & T16_INT_TOV) { + t16->ifr |= T16_INT_TOV; + qemu_set_irq(t16->ovf_irq, 1); + } + } + /* Check for ocra overflow in CTC mode */ + if (mode == T16_MODE_CTC_OCRA && t16->next_interrupt == COMPA) { + trace_avr_timer16_interrupt_overflow("CTC OCRA"); + avr_timer16_clock_reset(t16); + } + /* Check for icr overflow in CTC mode */ + if (mode == T16_MODE_CTC_ICR && t16->next_interrupt == CAPT) { + trace_avr_timer16_interrupt_overflow("CTC ICR"); + avr_timer16_clock_reset(t16); + if (t16->imsk & T16_INT_IC) { + t16->ifr |= T16_INT_IC; + qemu_set_irq(t16->capt_irq, 1); + } + } + /* Check for output compare interrupts */ + if (t16->imsk & T16_INT_OCA && t16->next_interrupt == COMPA) { + t16->ifr |= T16_INT_OCA; + qemu_set_irq(t16->compa_irq, 1); + } + if (t16->imsk & T16_INT_OCB && t16->next_interrupt == COMPB) { + t16->ifr |= T16_INT_OCB; + qemu_set_irq(t16->compb_irq, 1); + } + if (t16->imsk & T16_INT_OCC && t16->next_interrupt == COMPC) { + t16->ifr |= T16_INT_OCC; + qemu_set_irq(t16->compc_irq, 1); + } + avr_timer16_set_alarm(t16); +} + +static void avr_timer16_reset(DeviceState *dev) +{ + AVRTimer16State *t16 = AVR_TIMER16(dev); + + avr_timer16_clock_reset(t16); + avr_timer16_clksrc_update(t16); + avr_timer16_set_alarm(t16); + + qemu_set_irq(t16->capt_irq, 0); + qemu_set_irq(t16->compa_irq, 0); + qemu_set_irq(t16->compb_irq, 0); + qemu_set_irq(t16->compc_irq, 0); + qemu_set_irq(t16->ovf_irq, 0); +} + +static uint64_t avr_timer16_read(void *opaque, hwaddr offset, unsigned size) +{ + assert(size == 1); + AVRTimer16State *t16 = opaque; + uint8_t retval = 0; + + switch (offset) { + case T16_CRA: + retval = t16->cra; + break; + case T16_CRB: + retval = t16->crb; + break; + case T16_CRC: + retval = t16->crc; + break; + case T16_CNTL: + avr_timer16_update_cnt(t16); + t16->rtmp = t16->cnth; + retval = t16->cntl; + break; + case T16_CNTH: + retval = t16->rtmp; + break; + case T16_ICRL: + /* + * The timer copies cnt to icr when the input capture pin changes + * state or when the analog comparator has a match. We don't + * emulate this behaviour. We do support it's use for defining a + * TOP value in T16_MODE_CTC_ICR + */ + t16->rtmp = t16->icrh; + retval = t16->icrl; + break; + case T16_ICRH: + retval = t16->rtmp; + break; + case T16_OCRAL: + retval = t16->ocral; + break; + case T16_OCRAH: + retval = t16->ocrah; + break; + case T16_OCRBL: + retval = t16->ocrbl; + break; + case T16_OCRBH: + retval = t16->ocrbh; + break; + case T16_OCRCL: + retval = t16->ocrcl; + break; + case T16_OCRCH: + retval = t16->ocrch; + break; + default: + break; + } + trace_avr_timer16_read(offset, retval); + + return (uint64_t)retval; +} + +static void avr_timer16_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size == 1); + AVRTimer16State *t16 = opaque; + uint8_t val8 = (uint8_t)val64; + uint8_t prev_clk_src = CLKSRC(t16); + + trace_avr_timer16_write(offset, val8); + + switch (offset) { + case T16_CRA: + t16->cra = val8; + if (t16->cra & T16_CRA_OC_CONF) { + qemu_log_mask(LOG_UNIMP, "%s: output compare pins unsupported\n", + __func__); + } + break; + case T16_CRB: + t16->crb = val8; + if (t16->crb & T16_CRB_ICNC) { + qemu_log_mask(LOG_UNIMP, + "%s: input capture noise canceller unsupported\n", + __func__); + } + if (t16->crb & T16_CRB_ICES) { + qemu_log_mask(LOG_UNIMP, "%s: input capture unsupported\n", + __func__); + } + if (CLKSRC(t16) != prev_clk_src) { + avr_timer16_clksrc_update(t16); + if (prev_clk_src == T16_CLKSRC_STOPPED) { + t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + } + break; + case T16_CRC: + t16->crc = val8; + qemu_log_mask(LOG_UNIMP, "%s: output compare pins unsupported\n", + __func__); + break; + case T16_CNTL: + /* + * CNT is the 16-bit counter value, it must be read/written via + * a temporary register (rtmp) to make the read/write atomic. + */ + /* ICR also has this behaviour, and shares rtmp */ + /* + * Writing CNT blocks compare matches for one clock cycle. + * Writing CNT to TOP or to an OCR value (if in use) will + * skip the relevant interrupt + */ + t16->cntl = val8; + t16->cnth = t16->rtmp; + avr_timer16_recalc_reset_time(t16); + break; + case T16_CNTH: + t16->rtmp = val8; + break; + case T16_ICRL: + /* ICR can only be written in mode T16_MODE_CTC_ICR */ + if (MODE(t16) == T16_MODE_CTC_ICR) { + t16->icrl = val8; + t16->icrh = t16->rtmp; + } + break; + case T16_ICRH: + if (MODE(t16) == T16_MODE_CTC_ICR) { + t16->rtmp = val8; + } + break; + case T16_OCRAL: + /* + * OCRn cause the relevant output compare flag to be raised, and + * trigger an interrupt, when CNT is equal to the value here + */ + t16->ocral = val8; + break; + case T16_OCRAH: + t16->ocrah = val8; + break; + case T16_OCRBL: + t16->ocrbl = val8; + break; + case T16_OCRBH: + t16->ocrbh = val8; + break; + case T16_OCRCL: + t16->ocrcl = val8; + break; + case T16_OCRCH: + t16->ocrch = val8; + break; + default: + break; + } + avr_timer16_set_alarm(t16); +} + +static uint64_t avr_timer16_imsk_read(void *opaque, + hwaddr offset, + unsigned size) +{ + assert(size == 1); + AVRTimer16State *t16 = opaque; + trace_avr_timer16_read_imsk(offset ? 0 : t16->imsk); + if (offset != 0) { + return 0; + } + return t16->imsk; +} + +static void avr_timer16_imsk_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size == 1); + AVRTimer16State *t16 = opaque; + trace_avr_timer16_write_imsk(val64); + if (offset != 0) { + return; + } + t16->imsk = (uint8_t)val64; +} + +static uint64_t avr_timer16_ifr_read(void *opaque, + hwaddr offset, + unsigned size) +{ + assert(size == 1); + AVRTimer16State *t16 = opaque; + trace_avr_timer16_read_ifr(offset ? 0 : t16->ifr); + if (offset != 0) { + return 0; + } + return t16->ifr; +} + +static void avr_timer16_ifr_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size == 1); + AVRTimer16State *t16 = opaque; + trace_avr_timer16_write_imsk(val64); + if (offset != 0) { + return; + } + t16->ifr = (uint8_t)val64; +} + +static const MemoryRegionOps avr_timer16_ops = { + .read = avr_timer16_read, + .write = avr_timer16_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = {.max_access_size = 1} +}; + +static const MemoryRegionOps avr_timer16_imsk_ops = { + .read = avr_timer16_imsk_read, + .write = avr_timer16_imsk_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = {.max_access_size = 1} +}; + +static const MemoryRegionOps avr_timer16_ifr_ops = { + .read = avr_timer16_ifr_read, + .write = avr_timer16_ifr_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = {.max_access_size = 1} +}; + +static Property avr_timer16_properties[] = { + DEFINE_PROP_UINT8("id", struct AVRTimer16State, id, 0), + DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State, + cpu_freq_hz, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void avr_timer16_pr(void *opaque, int irq, int level) +{ + AVRTimer16State *s = AVR_TIMER16(opaque); + + s->enabled = !level; + + if (!s->enabled) { + avr_timer16_reset(DEVICE(s)); + } +} + +static void avr_timer16_init(Object *obj) +{ + AVRTimer16State *s = AVR_TIMER16(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->capt_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compa_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compb_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compc_irq); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->ovf_irq); + + memory_region_init_io(&s->iomem, obj, &avr_timer16_ops, + s, "avr-timer16", 0xe); + memory_region_init_io(&s->imsk_iomem, obj, &avr_timer16_imsk_ops, + s, "avr-timer16-intmask", 0x1); + memory_region_init_io(&s->ifr_iomem, obj, &avr_timer16_ifr_ops, + s, "avr-timer16-intflag", 0x1); + + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->imsk_iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->ifr_iomem); + qdev_init_gpio_in(DEVICE(s), avr_timer16_pr, 1); +} + +static void avr_timer16_realize(DeviceState *dev, Error **errp) +{ + AVRTimer16State *s = AVR_TIMER16(dev); + + if (s->cpu_freq_hz == 0) { + error_setg(errp, "AVR timer16: cpu-frequency-hz property must be set"); + return; + } + + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_timer16_interrupt, s); + s->enabled = true; +} + +static void avr_timer16_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = avr_timer16_reset; + dc->realize = avr_timer16_realize; + device_class_set_props(dc, avr_timer16_properties); +} + +static const TypeInfo avr_timer16_info = { + .name = TYPE_AVR_TIMER16, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AVRTimer16State), + .instance_init = avr_timer16_init, + .class_init = avr_timer16_class_init, +}; + +static void avr_timer16_register_types(void) +{ + type_register_static(&avr_timer16_info); +} + +type_init(avr_timer16_register_types) diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 866c9f546a..447b7c405b 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -75,3 +75,15 @@ nrf51_timer_set_count(uint8_t timer_id, uint8_t counter_id, uint32_t value) "tim bcm2835_systmr_irq(bool enable) "timer irq state %u" bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 bcm2835_systmr_write(uint64_t offset, uint64_t data) "timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 + +# avr_timer16.c +avr_timer16_read(uint8_t addr, uint8_t value) "timer16 read addr:%u value:%u" +avr_timer16_read_ifr(uint8_t value) "timer16 read addr:ifr value:%u" +avr_timer16_read_imsk(uint8_t value) "timer16 read addr:imsk value:%u" +avr_timer16_write(uint8_t addr, uint8_t value) "timer16 write addr:%u value:%u" +avr_timer16_write_ifr(uint8_t value) "timer16 write addr:ifr value:%u" +avr_timer16_write_imsk(uint8_t value) "timer16 write addr:imsk value:%u" +avr_timer16_interrupt_count(uint8_t cnt) "count: %u" +avr_timer16_interrupt_overflow(const char *reason) "overflow: %s" +avr_timer16_next_alarm(uint64_t delay_ns) "next alarm: %" PRIu64 " ns from now" +avr_timer16_clksrc_update(uint64_t freq_hz, uint64_t period_ns, uint64_t delay_s) "timer frequency: %" PRIu64 " Hz, period: %" PRIu64 " ns (%" PRId64 " us)" diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index c5f9fa08ab..9856bf7921 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -211,6 +211,25 @@ enum bfd_architecture #define bfd_mach_m32r 0 /* backwards compatibility */ bfd_arch_mn10200, /* Matsushita MN10200 */ bfd_arch_mn10300, /* Matsushita MN10300 */ + bfd_arch_avr, /* AVR microcontrollers */ +#define bfd_mach_avr1 1 +#define bfd_mach_avr2 2 +#define bfd_mach_avr25 25 +#define bfd_mach_avr3 3 +#define bfd_mach_avr31 31 +#define bfd_mach_avr35 35 +#define bfd_mach_avr4 4 +#define bfd_mach_avr5 5 +#define bfd_mach_avr51 51 +#define bfd_mach_avr6 6 +#define bfd_mach_avrtiny 100 +#define bfd_mach_avrxmega1 101 +#define bfd_mach_avrxmega2 102 +#define bfd_mach_avrxmega3 103 +#define bfd_mach_avrxmega4 104 +#define bfd_mach_avrxmega5 105 +#define bfd_mach_avrxmega6 106 +#define bfd_mach_avrxmega7 107 bfd_arch_cris, /* Axis CRIS */ #define bfd_mach_cris_v0_v10 255 #define bfd_mach_cris_v32 32 diff --git a/include/elf.h b/include/elf.h index 8fbfe60e09..5b06b55f28 100644 --- a/include/elf.h +++ b/include/elf.h @@ -160,6 +160,8 @@ typedef struct mips_elf_abiflags_v0 { #define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_AVR 83 /* AVR 8-bit microcontroller */ + #define EM_V850 87 /* NEC v850 */ #define EM_H8_300H 47 /* Hitachi H8/300H */ @@ -202,6 +204,8 @@ typedef struct mips_elf_abiflags_v0 { #define EM_MOXIE 223 /* Moxie processor family */ #define EM_MOXIE_OLD 0xFEED +#define EF_AVR_MACH 0x7F /* Mask for AVR e_flags to get core type */ + /* This is the info that is needed to parse the dynamic section of the file */ #define DT_NULL 0 #define DT_NEEDED 1 diff --git a/include/hw/char/avr_usart.h b/include/hw/char/avr_usart.h new file mode 100644 index 0000000000..5739aaf26f --- /dev/null +++ b/include/hw/char/avr_usart.h @@ -0,0 +1,93 @@ +/* + * AVR USART + * + * Copyright (c) 2018 University of Kent + * Author: Sarah Harris + * + * 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_CHAR_AVR_USART_H +#define HW_CHAR_AVR_USART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "hw/hw.h" + +/* Offsets of registers. */ +#define USART_DR 0x06 +#define USART_CSRA 0x00 +#define USART_CSRB 0x01 +#define USART_CSRC 0x02 +#define USART_BRRH 0x05 +#define USART_BRRL 0x04 + +/* Relevant bits in regiters. */ +#define USART_CSRA_RXC (1 << 7) +#define USART_CSRA_TXC (1 << 6) +#define USART_CSRA_DRE (1 << 5) +#define USART_CSRA_MPCM (1 << 0) + +#define USART_CSRB_RXCIE (1 << 7) +#define USART_CSRB_TXCIE (1 << 6) +#define USART_CSRB_DREIE (1 << 5) +#define USART_CSRB_RXEN (1 << 4) +#define USART_CSRB_TXEN (1 << 3) +#define USART_CSRB_CSZ2 (1 << 2) +#define USART_CSRB_RXB8 (1 << 1) +#define USART_CSRB_TXB8 (1 << 0) + +#define USART_CSRC_MSEL1 (1 << 7) +#define USART_CSRC_MSEL0 (1 << 6) +#define USART_CSRC_PM1 (1 << 5) +#define USART_CSRC_PM0 (1 << 4) +#define USART_CSRC_CSZ1 (1 << 2) +#define USART_CSRC_CSZ0 (1 << 1) + +#define TYPE_AVR_USART "avr-usart" +#define AVR_USART(obj) \ + OBJECT_CHECK(AVRUsartState, (obj), TYPE_AVR_USART) + +typedef struct { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion mmio; + + CharBackend chr; + + bool enabled; + + uint8_t data; + bool data_valid; + uint8_t char_mask; + /* Control and Status Registers */ + uint8_t csra; + uint8_t csrb; + uint8_t csrc; + /* Baud Rate Registers (low/high byte) */ + uint8_t brrh; + uint8_t brrl; + + /* Receive Complete */ + qemu_irq rxc_irq; + /* Transmit Complete */ + qemu_irq txc_irq; + /* Data Register Empty */ + qemu_irq dre_irq; +} AVRUsartState; + +#endif /* HW_CHAR_AVR_USART_H */ diff --git a/include/hw/misc/avr_power.h b/include/hw/misc/avr_power.h new file mode 100644 index 0000000000..e08e44f629 --- /dev/null +++ b/include/hw/misc/avr_power.h @@ -0,0 +1,46 @@ +/* + * AVR Power Reduction Management + * + * Copyright (c) 2019-2020 Michael Rolnik + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_MISC_AVR_POWER_H +#define HW_MISC_AVR_POWER_H + +#include "hw/sysbus.h" +#include "hw/hw.h" + + +#define TYPE_AVR_MASK "avr-power" +#define AVR_MASK(obj) OBJECT_CHECK(AVRMaskState, (obj), TYPE_AVR_MASK) + +typedef struct { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + + uint8_t val; + qemu_irq irq[8]; +} AVRMaskState; + +#endif /* HW_MISC_AVR_POWER_H */ diff --git a/include/hw/timer/avr_timer16.h b/include/hw/timer/avr_timer16.h new file mode 100644 index 0000000000..982019d242 --- /dev/null +++ b/include/hw/timer/avr_timer16.h @@ -0,0 +1,94 @@ +/* + * AVR 16-bit timer + * + * Copyright (c) 2018 University of Kent + * Author: Ed Robbins + * + * 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> + */ + +/* + * Driver for 16 bit timers on 8 bit AVR devices. + * Note: + * On ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 bit + */ + +#ifndef HW_TIMER_AVR_TIMER16_H +#define HW_TIMER_AVR_TIMER16_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/hw.h" + +enum NextInterrupt { + OVERFLOW, + COMPA, + COMPB, + COMPC, + CAPT +}; + +#define TYPE_AVR_TIMER16 "avr-timer16" +#define AVR_TIMER16(obj) \ + OBJECT_CHECK(AVRTimer16State, (obj), TYPE_AVR_TIMER16) + +typedef struct AVRTimer16State { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + MemoryRegion imsk_iomem; + MemoryRegion ifr_iomem; + QEMUTimer *timer; + qemu_irq capt_irq; + qemu_irq compa_irq; + qemu_irq compb_irq; + qemu_irq compc_irq; + qemu_irq ovf_irq; + + bool enabled; + + /* registers */ + uint8_t cra; + uint8_t crb; + uint8_t crc; + uint8_t cntl; + uint8_t cnth; + uint8_t icrl; + uint8_t icrh; + uint8_t ocral; + uint8_t ocrah; + uint8_t ocrbl; + uint8_t ocrbh; + uint8_t ocrcl; + uint8_t ocrch; + /* + * Reads and writes to CNT and ICR utilise a bizarre temporary + * register, which we emulate + */ + uint8_t rtmp; + uint8_t imsk; + uint8_t ifr; + + uint8_t id; + uint64_t cpu_freq_hz; + uint64_t freq_hz; + uint64_t period_ns; + uint64_t reset_time_ns; + enum NextInterrupt next_interrupt; +} AVRTimer16State; + +#endif /* HW_TIMER_AVR_TIMER16_H */ diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 71a7a285ee..54f069d491 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -25,6 +25,7 @@ enum { QEMU_ARCH_HPPA = (1 << 18), QEMU_ARCH_RISCV = (1 << 19), QEMU_ARCH_RX = (1 << 20), + QEMU_ARCH_AVR = (1 << 21), QEMU_ARCH_NONE = (1 << 31), }; diff --git a/qapi/machine.json b/qapi/machine.json index ff7b5032e3..f59144023c 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -17,6 +17,7 @@ # being. # # @rx: since 5.0 +# @avr: since 5.1 # # Notes: The resulting QMP strings can be appended to the "qemu-system-" # prefix to produce the corresponding QEMU executable name. This @@ -25,7 +26,7 @@ # Since: 3.0 ## { 'enum' : 'SysEmuTarget', - 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', + 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', 'lm32', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', 'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4', diff --git a/softmmu/arch_init.c b/softmmu/arch_init.c index 8afea4748b..7fd5c09b2b 100644 --- a/softmmu/arch_init.c +++ b/softmmu/arch_init.c @@ -90,6 +90,8 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_XTENSA) #define QEMU_ARCH QEMU_ARCH_XTENSA +#elif defined(TARGET_AVR) +#define QEMU_ARCH QEMU_ARCH_AVR #endif const uint32_t arch_type = QEMU_ARCH; diff --git a/target/avr/Makefile.objs b/target/avr/Makefile.objs new file mode 100644 index 0000000000..6e35ba2c5c --- /dev/null +++ b/target/avr/Makefile.objs @@ -0,0 +1,34 @@ +# +# QEMU AVR +# +# Copyright (c) 2016-2020 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> +# + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py +decode-y = $(SRC_PATH)/target/avr/insn.decode + +target/avr/decode_insn.inc.c: $(decode-y) $(DECODETREE) + $(call quiet-command, \ + $(PYTHON) $(DECODETREE) -o $@ --decode decode_insn --insnwidth 16 $<, \ + "GEN", $(TARGET_DIR)$@) + +target/avr/translate.o: target/avr/decode_insn.inc.c + +obj-y += translate.o cpu.o helper.o +obj-y += gdbstub.o +obj-y += disas.o +obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h new file mode 100644 index 0000000000..7ef4e7c679 --- /dev/null +++ b/target/avr/cpu-param.h @@ -0,0 +1,36 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016-2020 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 AVR_CPU_PARAM_H +#define AVR_CPU_PARAM_H + +#define TARGET_LONG_BITS 32 +/* + * TARGET_PAGE_BITS cannot be more than 8 bits because + * 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they + * should be implemented as a device and not memory + * 2. SRAM starts at the address 0x0100 + */ +#define TARGET_PAGE_BITS 8 +#define TARGET_PHYS_ADDR_SPACE_BITS 24 +#define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define NB_MMU_MODES 2 + +#endif diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h new file mode 100644 index 0000000000..d23ad43a99 --- /dev/null +++ b/target/avr/cpu-qom.h @@ -0,0 +1,53 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016-2020 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 QEMU_AVR_QOM_H +#define QEMU_AVR_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_AVR_CPU "avr-cpu" + +#define AVR_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(AVRCPUClass, (klass), TYPE_AVR_CPU) +#define AVR_CPU(obj) \ + OBJECT_CHECK(AVRCPU, (obj), TYPE_AVR_CPU) +#define AVR_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AVRCPUClass, (obj), TYPE_AVR_CPU) + +/** + * AVRCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * @vr: Version Register value. + * + * A AVR CPU model. + */ +typedef struct AVRCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + DeviceRealize parent_realize; + DeviceReset parent_reset; +} AVRCPUClass; + +typedef struct AVRCPU AVRCPU; + +#endif /* !defined (QEMU_AVR_CPU_QOM_H) */ diff --git a/target/avr/cpu.c b/target/avr/cpu.c new file mode 100644 index 0000000000..5d9c4ad5bf --- /dev/null +++ b/target/avr/cpu.c @@ -0,0 +1,366 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019-2020 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/qemu-print.h" +#include "exec/exec-all.h" +#include "cpu.h" +#include "disas/dis-asm.h" + +static void avr_cpu_set_pc(CPUState *cs, vaddr value) +{ + AVRCPU *cpu = AVR_CPU(cs); + + cpu->env.pc_w = value / 2; /* internally PC points to words */ +} + +static bool avr_cpu_has_work(CPUState *cs) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET)) + && cpu_interrupts_enabled(env); +} + +static void avr_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + env->pc_w = tb->pc / 2; /* internally PC points to words */ +} + +static void avr_cpu_reset(DeviceState *ds) +{ + CPUState *cs = CPU(ds); + AVRCPU *cpu = AVR_CPU(cs); + AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu); + CPUAVRState *env = &cpu->env; + + mcc->parent_reset(ds); + + env->pc_w = 0; + env->sregI = 1; + env->sregC = 0; + env->sregZ = 0; + env->sregN = 0; + env->sregV = 0; + env->sregS = 0; + env->sregH = 0; + env->sregT = 0; + + env->rampD = 0; + env->rampX = 0; + env->rampY = 0; + env->rampZ = 0; + env->eind = 0; + env->sp = 0; + + env->skip = 0; + + memset(env->r, 0, sizeof(env->r)); +} + +static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_arch_avr; + info->print_insn = avr_print_insn; +} + +static void avr_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + qemu_init_vcpu(cs); + cpu_reset(cs); + + mcc->parent_realize(dev, errp); +} + +static void avr_cpu_set_int(void *opaque, int irq, int level) +{ + AVRCPU *cpu = opaque; + CPUAVRState *env = &cpu->env; + CPUState *cs = CPU(cpu); + uint64_t mask = (1ull << irq); + + if (level) { + env->intsrc |= mask; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + env->intsrc &= ~mask; + if (env->intsrc == 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +static void avr_cpu_initfn(Object *obj) +{ + AVRCPU *cpu = AVR_CPU(obj); + + cpu_set_cpustate_pointers(cpu); + + /* Set the number of interrupts supported by the CPU. */ + qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, + sizeof(cpu->env.intsrc) * 8); +} +
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |