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 -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAl8JgE8ACgkQ4+MsLN6t wN5KMhAA1IivMK9uD9x8vOK3H1fSIju6ufJz7mlynDXG/LV0dhms1t8n/AlPE/Gk TLhBUBwwdsejVDrTu6GGukoybsKLyULdt8MIK5z6bd++TwY3MlUkdOJeteviBUuP 0hcagNR/Dyg1WLAq/VH4KGHfzisprfDM7sXTvjE3raKBSpqIwO5tfUn4kVm/LWB9 sQNsVbtyKqnM3UW+QYGAN3eGAOM2SWx8pUZqV+UrDDEDoFJIiip7jxoN1t7PJaQp O6t2/omLzbOMrpwqmNAIfrsMjovRylrd8nDGlX/OF5SrEbwXi3qvdJBtEOdBPasp owXbu2Uwo4VUu5x7kzAiTlflBBSOmDpILbYVn5jGLKMZmOjLTPVbrzMKGZqx5GXD gWmmX2aD8ejl2XGmKM+gC1smQJ6/aMTILoYXq97hIKi8pMH7AB2a8Tmzseiqx/E/ Lz1DrrnIW5vwFQAPnhdJCU1GF3B9VUcHG3w0sjvgGKDfpe8tLEgkmISi7CUbbA9/ rJs2P24haqfdFXWLQU2sO1ygTR1vLNy5/ZbU1nyrAPpjWnGeX6GXVaWGQo83BaDd rIfnx0upNYFdaO5Vi4cbHUFGe1fVuR3C/l1xiDbmnzx3yMgys3036Equ2h3fSPqO 4k79bee9ByAUl1YE0X8T4gasCRS6RaSWXZYz7lfdc9h5hGny8LM= =gFE5 -----END PGP SIGNATURE----- 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@amsat.org>" [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@linaro.org>
This commit is contained in:
commit
d34498309c
30
MAINTAINERS
30
MAINTAINERS
@ -167,6 +167,14 @@ S: Maintained
|
|||||||
F: hw/arm/smmu*
|
F: hw/arm/smmu*
|
||||||
F: include/hw/arm/smmu*
|
F: include/hw/arm/smmu*
|
||||||
|
|
||||||
|
AVR TCG CPUs
|
||||||
|
M: Michael Rolnik <mrolnik@gmail.com>
|
||||||
|
R: Sarah Harris <S.E.Harris@kent.ac.uk>
|
||||||
|
S: Maintained
|
||||||
|
F: gdb-xml/avr-cpu.xml
|
||||||
|
F: target/avr/
|
||||||
|
F: tests/acceptance/machine_avr6.py
|
||||||
|
|
||||||
CRIS TCG CPUs
|
CRIS TCG CPUs
|
||||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
@ -982,6 +990,28 @@ F: include/hw/*/nrf51*.h
|
|||||||
F: include/hw/*/microbit*.h
|
F: include/hw/*/microbit*.h
|
||||||
F: tests/qtest/microbit-test.c
|
F: tests/qtest/microbit-test.c
|
||||||
|
|
||||||
|
AVR Machines
|
||||||
|
-------------
|
||||||
|
|
||||||
|
AVR MCUs
|
||||||
|
M: Michael Rolnik <mrolnik@gmail.com>
|
||||||
|
R: Sarah Harris <S.E.Harris@kent.ac.uk>
|
||||||
|
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@amsat.org>
|
||||||
|
R: Sarah Harris <S.E.Harris@kent.ac.uk>
|
||||||
|
S: Maintained
|
||||||
|
F: hw/avr/arduino.c
|
||||||
|
|
||||||
CRIS Machines
|
CRIS Machines
|
||||||
-------------
|
-------------
|
||||||
Axis Dev88
|
Axis Dev88
|
||||||
|
7
configure
vendored
7
configure
vendored
@ -8143,6 +8143,10 @@ case "$target_name" in
|
|||||||
mttcg="yes"
|
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"
|
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)
|
cris)
|
||||||
;;
|
;;
|
||||||
hppa)
|
hppa)
|
||||||
@ -8387,6 +8391,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
|
|||||||
disas_config "ARM_A64"
|
disas_config "ARM_A64"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
avr)
|
||||||
|
disas_config "AVR"
|
||||||
|
;;
|
||||||
cris)
|
cris)
|
||||||
disas_config "CRIS"
|
disas_config "CRIS"
|
||||||
;;
|
;;
|
||||||
|
5
default-configs/avr-softmmu.mak
Normal file
5
default-configs/avr-softmmu.mak
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Default configuration for avr-softmmu
|
||||||
|
|
||||||
|
# Boards:
|
||||||
|
#
|
||||||
|
CONFIG_ARDUINO=y
|
37
docs/system/target-avr.rst
Normal file
37
docs/system/target-avr.rst
Normal file
@ -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``
|
@ -19,3 +19,4 @@ Contents:
|
|||||||
target-xtensa
|
target-xtensa
|
||||||
target-s390x
|
target-s390x
|
||||||
target-rx
|
target-rx
|
||||||
|
target-avr
|
||||||
|
49
gdb-xml/avr-cpu.xml
Normal file
49
gdb-xml/avr-cpu.xml
Normal file
@ -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>
|
@ -43,6 +43,7 @@ source watchdog/Kconfig
|
|||||||
# arch Kconfig
|
# arch Kconfig
|
||||||
source arm/Kconfig
|
source arm/Kconfig
|
||||||
source alpha/Kconfig
|
source alpha/Kconfig
|
||||||
|
source avr/Kconfig
|
||||||
source cris/Kconfig
|
source cris/Kconfig
|
||||||
source hppa/Kconfig
|
source hppa/Kconfig
|
||||||
source i386/Kconfig
|
source i386/Kconfig
|
||||||
|
9
hw/avr/Kconfig
Normal file
9
hw/avr/Kconfig
Normal file
@ -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
|
3
hw/avr/Makefile.objs
Normal file
3
hw/avr/Makefile.objs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
obj-y += boot.o
|
||||||
|
obj-$(CONFIG_AVR_ATMEGA_MCU) += atmega.o
|
||||||
|
obj-$(CONFIG_ARDUINO) += arduino.o
|
149
hw/avr/arduino.c
Normal file
149
hw/avr/arduino.c
Normal file
@ -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)
|
458
hw/avr/atmega.c
Normal file
458
hw/avr/atmega.c
Normal file
@ -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)
|
48
hw/avr/atmega.h
Normal file
48
hw/avr/atmega.h
Normal file
@ -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 */
|
115
hw/avr/boot.c
Normal file
115
hw/avr/boot.c
Normal file
@ -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;
|
||||||
|
}
|
33
hw/avr/boot.h
Normal file
33
hw/avr/boot.h
Normal file
@ -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
|
@ -49,3 +49,6 @@ config TERMINAL3270
|
|||||||
|
|
||||||
config RENESAS_SCI
|
config RENESAS_SCI
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config AVR_USART
|
||||||
|
bool
|
||||||
|
@ -22,6 +22,7 @@ common-obj-$(CONFIG_DIGIC) += digic-uart.o
|
|||||||
common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
|
common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
|
||||||
common-obj-$(CONFIG_RASPI) += bcm2835_aux.o
|
common-obj-$(CONFIG_RASPI) += bcm2835_aux.o
|
||||||
common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.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_CMSDK_APB_UART) += cmsdk-apb-uart.o
|
||||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
||||||
|
320
hw/char/avr_usart.c
Normal file
320
hw/char/avr_usart.c
Normal file
@ -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)
|
@ -131,4 +131,7 @@ config MAC_VIA
|
|||||||
select MOS6522
|
select MOS6522
|
||||||
select ADB
|
select ADB
|
||||||
|
|
||||||
|
config AVR_POWER
|
||||||
|
bool
|
||||||
|
|
||||||
source macio/Kconfig
|
source macio/Kconfig
|
||||||
|
@ -91,3 +91,5 @@ common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o
|
|||||||
obj-$(CONFIG_MAC_VIA) += mac_via.o
|
obj-$(CONFIG_MAC_VIA) += mac_via.o
|
||||||
|
|
||||||
common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o
|
common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_AVR_POWER) += avr_power.o
|
||||||
|
113
hw/misc/avr_power.c
Normal file
113
hw/misc/avr_power.c
Normal file
@ -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)
|
@ -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_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
|
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
|
# eccmemctl.c
|
||||||
ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x"
|
ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x"
|
||||||
ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x"
|
ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x"
|
||||||
|
@ -41,3 +41,6 @@ config RENESAS_TMR
|
|||||||
|
|
||||||
config RENESAS_CMT
|
config RENESAS_CMT
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config AVR_TIMER16
|
||||||
|
bool
|
||||||
|
@ -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_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o
|
||||||
common-obj-$(CONFIG_MSF2) += mss-timer.o
|
common-obj-$(CONFIG_MSF2) += mss-timer.o
|
||||||
common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
|
common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_AVR_TIMER16) += avr_timer16.o
|
||||||
|
621
hw/timer/avr_timer16.c
Normal file
621
hw/timer/avr_timer16.c
Normal file
@ -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)
|
@ -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_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_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
|
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)"
|
||||||
|
@ -211,6 +211,25 @@ enum bfd_architecture
|
|||||||
#define bfd_mach_m32r 0 /* backwards compatibility */
|
#define bfd_mach_m32r 0 /* backwards compatibility */
|
||||||
bfd_arch_mn10200, /* Matsushita MN10200 */
|
bfd_arch_mn10200, /* Matsushita MN10200 */
|
||||||
bfd_arch_mn10300, /* Matsushita MN10300 */
|
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 */
|
bfd_arch_cris, /* Axis CRIS */
|
||||||
#define bfd_mach_cris_v0_v10 255
|
#define bfd_mach_cris_v0_v10 255
|
||||||
#define bfd_mach_cris_v32 32
|
#define bfd_mach_cris_v32 32
|
||||||
|
@ -160,6 +160,8 @@ typedef struct mips_elf_abiflags_v0 {
|
|||||||
|
|
||||||
#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
|
#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_V850 87 /* NEC v850 */
|
||||||
|
|
||||||
#define EM_H8_300H 47 /* Hitachi H8/300H */
|
#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 223 /* Moxie processor family */
|
||||||
#define EM_MOXIE_OLD 0xFEED
|
#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 */
|
/* This is the info that is needed to parse the dynamic section of the file */
|
||||||
#define DT_NULL 0
|
#define DT_NULL 0
|
||||||
#define DT_NEEDED 1
|
#define DT_NEEDED 1
|
||||||
|
93
include/hw/char/avr_usart.h
Normal file
93
include/hw/char/avr_usart.h
Normal file
@ -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 */
|
46
include/hw/misc/avr_power.h
Normal file
46
include/hw/misc/avr_power.h
Normal file
@ -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 */
|
94
include/hw/timer/avr_timer16.h
Normal file
94
include/hw/timer/avr_timer16.h
Normal file
@ -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 */
|
@ -25,6 +25,7 @@ enum {
|
|||||||
QEMU_ARCH_HPPA = (1 << 18),
|
QEMU_ARCH_HPPA = (1 << 18),
|
||||||
QEMU_ARCH_RISCV = (1 << 19),
|
QEMU_ARCH_RISCV = (1 << 19),
|
||||||
QEMU_ARCH_RX = (1 << 20),
|
QEMU_ARCH_RX = (1 << 20),
|
||||||
|
QEMU_ARCH_AVR = (1 << 21),
|
||||||
|
|
||||||
QEMU_ARCH_NONE = (1 << 31),
|
QEMU_ARCH_NONE = (1 << 31),
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
# being.
|
# being.
|
||||||
#
|
#
|
||||||
# @rx: since 5.0
|
# @rx: since 5.0
|
||||||
|
# @avr: since 5.1
|
||||||
#
|
#
|
||||||
# Notes: The resulting QMP strings can be appended to the "qemu-system-"
|
# Notes: The resulting QMP strings can be appended to the "qemu-system-"
|
||||||
# prefix to produce the corresponding QEMU executable name. This
|
# prefix to produce the corresponding QEMU executable name. This
|
||||||
@ -25,7 +26,7 @@
|
|||||||
# Since: 3.0
|
# Since: 3.0
|
||||||
##
|
##
|
||||||
{ 'enum' : 'SysEmuTarget',
|
{ 'enum' : 'SysEmuTarget',
|
||||||
'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32',
|
'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', 'lm32',
|
||||||
'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
|
'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
|
||||||
'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc',
|
'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc',
|
||||||
'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
|
'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
|
||||||
|
@ -90,6 +90,8 @@ int graphic_depth = 32;
|
|||||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||||
#elif defined(TARGET_XTENSA)
|
#elif defined(TARGET_XTENSA)
|
||||||
#define QEMU_ARCH QEMU_ARCH_XTENSA
|
#define QEMU_ARCH QEMU_ARCH_XTENSA
|
||||||
|
#elif defined(TARGET_AVR)
|
||||||
|
#define QEMU_ARCH QEMU_ARCH_AVR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const uint32_t arch_type = QEMU_ARCH;
|
const uint32_t arch_type = QEMU_ARCH;
|
||||||
|
34
target/avr/Makefile.objs
Normal file
34
target/avr/Makefile.objs
Normal file
@ -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
|
36
target/avr/cpu-param.h
Normal file
36
target/avr/cpu-param.h
Normal file
@ -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
|
53
target/avr/cpu-qom.h
Normal file
53
target/avr/cpu-qom.h
Normal file
@ -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) */
|
366
target/avr/cpu.c
Normal file
366
target/avr/cpu.c
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ObjectClass *avr_cpu_class_by_name(const char *cpu_model)
|
||||||
|
{
|
||||||
|
ObjectClass *oc;
|
||||||
|
|
||||||
|
oc = object_class_by_name(cpu_model);
|
||||||
|
if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL ||
|
||||||
|
object_class_is_abstract(oc)) {
|
||||||
|
oc = NULL;
|
||||||
|
}
|
||||||
|
return oc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||||
|
{
|
||||||
|
AVRCPU *cpu = AVR_CPU(cs);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
qemu_fprintf(f, "\n");
|
||||||
|
qemu_fprintf(f, "PC: %06x\n", env->pc_w * 2); /* PC points to words */
|
||||||
|
qemu_fprintf(f, "SP: %04x\n", env->sp);
|
||||||
|
qemu_fprintf(f, "rampD: %02x\n", env->rampD >> 16);
|
||||||
|
qemu_fprintf(f, "rampX: %02x\n", env->rampX >> 16);
|
||||||
|
qemu_fprintf(f, "rampY: %02x\n", env->rampY >> 16);
|
||||||
|
qemu_fprintf(f, "rampZ: %02x\n", env->rampZ >> 16);
|
||||||
|
qemu_fprintf(f, "EIND: %02x\n", env->eind >> 16);
|
||||||
|
qemu_fprintf(f, "X: %02x%02x\n", env->r[27], env->r[26]);
|
||||||
|
qemu_fprintf(f, "Y: %02x%02x\n", env->r[29], env->r[28]);
|
||||||
|
qemu_fprintf(f, "Z: %02x%02x\n", env->r[31], env->r[30]);
|
||||||
|
qemu_fprintf(f, "SREG: [ %c %c %c %c %c %c %c %c ]\n",
|
||||||
|
env->sregI ? 'I' : '-',
|
||||||
|
env->sregT ? 'T' : '-',
|
||||||
|
env->sregH ? 'H' : '-',
|
||||||
|
env->sregS ? 'S' : '-',
|
||||||
|
env->sregV ? 'V' : '-',
|
||||||
|
env->sregN ? '-' : 'N', /* Zf has negative logic */
|
||||||
|
env->sregZ ? 'Z' : '-',
|
||||||
|
env->sregC ? 'I' : '-');
|
||||||
|
qemu_fprintf(f, "SKIP: %02x\n", env->skip);
|
||||||
|
|
||||||
|
qemu_fprintf(f, "\n");
|
||||||
|
for (i = 0; i < ARRAY_SIZE(env->r); i++) {
|
||||||
|
qemu_fprintf(f, "R[%02d]: %02x ", i, env->r[i]);
|
||||||
|
|
||||||
|
if ((i % 8) == 7) {
|
||||||
|
qemu_fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qemu_fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avr_cpu_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
CPUClass *cc = CPU_CLASS(oc);
|
||||||
|
AVRCPUClass *mcc = AVR_CPU_CLASS(oc);
|
||||||
|
|
||||||
|
mcc->parent_realize = dc->realize;
|
||||||
|
dc->realize = avr_cpu_realizefn;
|
||||||
|
|
||||||
|
device_class_set_parent_reset(dc, avr_cpu_reset, &mcc->parent_reset);
|
||||||
|
|
||||||
|
cc->class_by_name = avr_cpu_class_by_name;
|
||||||
|
|
||||||
|
cc->has_work = avr_cpu_has_work;
|
||||||
|
cc->do_interrupt = avr_cpu_do_interrupt;
|
||||||
|
cc->cpu_exec_interrupt = avr_cpu_exec_interrupt;
|
||||||
|
cc->dump_state = avr_cpu_dump_state;
|
||||||
|
cc->set_pc = avr_cpu_set_pc;
|
||||||
|
cc->memory_rw_debug = avr_cpu_memory_rw_debug;
|
||||||
|
cc->get_phys_page_debug = avr_cpu_get_phys_page_debug;
|
||||||
|
cc->tlb_fill = avr_cpu_tlb_fill;
|
||||||
|
cc->vmsd = &vms_avr_cpu;
|
||||||
|
cc->disas_set_info = avr_cpu_disas_set_info;
|
||||||
|
cc->tcg_initialize = avr_cpu_tcg_init;
|
||||||
|
cc->synchronize_from_tb = avr_cpu_synchronize_from_tb;
|
||||||
|
cc->gdb_read_register = avr_cpu_gdb_read_register;
|
||||||
|
cc->gdb_write_register = avr_cpu_gdb_write_register;
|
||||||
|
cc->gdb_num_core_regs = 35;
|
||||||
|
cc->gdb_core_xml_file = "avr-cpu.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting features of AVR core type avr5
|
||||||
|
* --------------------------------------
|
||||||
|
*
|
||||||
|
* This type of AVR core is present in the following AVR MCUs:
|
||||||
|
*
|
||||||
|
* ata5702m322, ata5782, ata5790, ata5790n, ata5791, ata5795, ata5831, ata6613c,
|
||||||
|
* ata6614q, ata8210, ata8510, atmega16, atmega16a, atmega161, atmega162,
|
||||||
|
* atmega163, atmega164a, atmega164p, atmega164pa, atmega165, atmega165a,
|
||||||
|
* atmega165p, atmega165pa, atmega168, atmega168a, atmega168p, atmega168pa,
|
||||||
|
* atmega168pb, atmega169, atmega169a, atmega169p, atmega169pa, atmega16hvb,
|
||||||
|
* atmega16hvbrevb, atmega16m1, atmega16u4, atmega32a, atmega32, atmega323,
|
||||||
|
* atmega324a, atmega324p, atmega324pa, atmega325, atmega325a, atmega325p,
|
||||||
|
* atmega325pa, atmega3250, atmega3250a, atmega3250p, atmega3250pa, atmega328,
|
||||||
|
* atmega328p, atmega328pb, atmega329, atmega329a, atmega329p, atmega329pa,
|
||||||
|
* atmega3290, atmega3290a, atmega3290p, atmega3290pa, atmega32c1, atmega32m1,
|
||||||
|
* atmega32u4, atmega32u6, atmega406, atmega64, atmega64a, atmega640, atmega644,
|
||||||
|
* atmega644a, atmega644p, atmega644pa, atmega645, atmega645a, atmega645p,
|
||||||
|
* atmega6450, atmega6450a, atmega6450p, atmega649, atmega649a, atmega649p,
|
||||||
|
* atmega6490, atmega16hva, atmega16hva2, atmega32hvb, atmega6490a, atmega6490p,
|
||||||
|
* atmega64c1, atmega64m1, atmega64hve, atmega64hve2, atmega64rfr2,
|
||||||
|
* atmega644rfr2, atmega32hvbrevb, at90can32, at90can64, at90pwm161, at90pwm216,
|
||||||
|
* at90pwm316, at90scr100, at90usb646, at90usb647, at94k, m3000
|
||||||
|
*/
|
||||||
|
static void avr_avr5_initfn(Object *obj)
|
||||||
|
{
|
||||||
|
AVRCPU *cpu = AVR_CPU(obj);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
|
||||||
|
set_avr_feature(env, AVR_FEATURE_LPM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_SRAM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_BREAK);
|
||||||
|
|
||||||
|
set_avr_feature(env, AVR_FEATURE_2_BYTE_PC);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_JMP_CALL);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_LPMX);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_MOVW);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_MUL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting features of AVR core type avr51
|
||||||
|
* --------------------------------------
|
||||||
|
*
|
||||||
|
* This type of AVR core is present in the following AVR MCUs:
|
||||||
|
*
|
||||||
|
* atmega128, atmega128a, atmega1280, atmega1281, atmega1284, atmega1284p,
|
||||||
|
* atmega128rfa1, atmega128rfr2, atmega1284rfr2, at90can128, at90usb1286,
|
||||||
|
* at90usb1287
|
||||||
|
*/
|
||||||
|
static void avr_avr51_initfn(Object *obj)
|
||||||
|
{
|
||||||
|
AVRCPU *cpu = AVR_CPU(obj);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
|
||||||
|
set_avr_feature(env, AVR_FEATURE_LPM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_SRAM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_BREAK);
|
||||||
|
|
||||||
|
set_avr_feature(env, AVR_FEATURE_2_BYTE_PC);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_RAMPZ);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_ELPMX);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_ELPM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_JMP_CALL);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_LPMX);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_MOVW);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_MUL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting features of AVR core type avr6
|
||||||
|
* --------------------------------------
|
||||||
|
*
|
||||||
|
* This type of AVR core is present in the following AVR MCUs:
|
||||||
|
*
|
||||||
|
* atmega2560, atmega2561, atmega256rfr2, atmega2564rfr2
|
||||||
|
*/
|
||||||
|
static void avr_avr6_initfn(Object *obj)
|
||||||
|
{
|
||||||
|
AVRCPU *cpu = AVR_CPU(obj);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
|
||||||
|
set_avr_feature(env, AVR_FEATURE_LPM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_SRAM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_BREAK);
|
||||||
|
|
||||||
|
set_avr_feature(env, AVR_FEATURE_3_BYTE_PC);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_RAMPZ);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_EIJMP_EICALL);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_ELPMX);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_ELPM);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_JMP_CALL);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_LPMX);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_MOVW);
|
||||||
|
set_avr_feature(env, AVR_FEATURE_MUL);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct AVRCPUInfo {
|
||||||
|
const char *name;
|
||||||
|
void (*initfn)(Object *obj);
|
||||||
|
} AVRCPUInfo;
|
||||||
|
|
||||||
|
|
||||||
|
static void avr_cpu_list_entry(gpointer data, gpointer user_data)
|
||||||
|
{
|
||||||
|
const char *typename = object_class_get_name(OBJECT_CLASS(data));
|
||||||
|
|
||||||
|
qemu_printf("%s\n", typename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void avr_cpu_list(void)
|
||||||
|
{
|
||||||
|
GSList *list;
|
||||||
|
list = object_class_get_list_sorted(TYPE_AVR_CPU, false);
|
||||||
|
g_slist_foreach(list, avr_cpu_list_entry, NULL);
|
||||||
|
g_slist_free(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_AVR_CPU_TYPE(model, initfn) \
|
||||||
|
{ \
|
||||||
|
.parent = TYPE_AVR_CPU, \
|
||||||
|
.instance_init = initfn, \
|
||||||
|
.name = AVR_CPU_TYPE_NAME(model), \
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo avr_cpu_type_info[] = {
|
||||||
|
{
|
||||||
|
.name = TYPE_AVR_CPU,
|
||||||
|
.parent = TYPE_CPU,
|
||||||
|
.instance_size = sizeof(AVRCPU),
|
||||||
|
.instance_init = avr_cpu_initfn,
|
||||||
|
.class_size = sizeof(AVRCPUClass),
|
||||||
|
.class_init = avr_cpu_class_init,
|
||||||
|
.abstract = true,
|
||||||
|
},
|
||||||
|
DEFINE_AVR_CPU_TYPE("avr5", avr_avr5_initfn),
|
||||||
|
DEFINE_AVR_CPU_TYPE("avr51", avr_avr51_initfn),
|
||||||
|
DEFINE_AVR_CPU_TYPE("avr6", avr_avr6_initfn),
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_TYPES(avr_cpu_type_info)
|
256
target/avr/cpu.h
Normal file
256
target/avr/cpu.h
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* 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_CPU_H
|
||||||
|
#define QEMU_AVR_CPU_H
|
||||||
|
|
||||||
|
#include "cpu-qom.h"
|
||||||
|
#include "exec/cpu-defs.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
#error "AVR 8-bit does not support user mode"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU
|
||||||
|
#define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX)
|
||||||
|
#define CPU_RESOLVING_TYPE TYPE_AVR_CPU
|
||||||
|
|
||||||
|
#define TCG_GUEST_DEFAULT_MO 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVR has two memory spaces, data & code.
|
||||||
|
* e.g. both have 0 address
|
||||||
|
* ST/LD instructions access data space
|
||||||
|
* LPM/SPM and instruction fetching access code memory space
|
||||||
|
*/
|
||||||
|
#define MMU_CODE_IDX 0
|
||||||
|
#define MMU_DATA_IDX 1
|
||||||
|
|
||||||
|
#define EXCP_RESET 1
|
||||||
|
#define EXCP_INT(n) (EXCP_RESET + (n) + 1)
|
||||||
|
|
||||||
|
/* Number of CPU registers */
|
||||||
|
#define NUMBER_OF_CPU_REGISTERS 32
|
||||||
|
/* Number of IO registers accessible by ld/st/in/out */
|
||||||
|
#define NUMBER_OF_IO_REGISTERS 64
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Offsets of AVR memory regions in host memory space.
|
||||||
|
*
|
||||||
|
* This is needed because the AVR has separate code and data address
|
||||||
|
* spaces that both have start from zero but have to go somewhere in
|
||||||
|
* host memory.
|
||||||
|
*
|
||||||
|
* It's also useful to know where some things are, like the IO registers.
|
||||||
|
*/
|
||||||
|
/* Flash program memory */
|
||||||
|
#define OFFSET_CODE 0x00000000
|
||||||
|
/* CPU registers, IO registers, and SRAM */
|
||||||
|
#define OFFSET_DATA 0x00800000
|
||||||
|
/* CPU registers specifically, these are mapped at the start of data */
|
||||||
|
#define OFFSET_CPU_REGISTERS OFFSET_DATA
|
||||||
|
/*
|
||||||
|
* IO registers, including status register, stack pointer, and memory
|
||||||
|
* mapped peripherals, mapped just after CPU registers
|
||||||
|
*/
|
||||||
|
#define OFFSET_IO_REGISTERS (OFFSET_DATA + NUMBER_OF_CPU_REGISTERS)
|
||||||
|
|
||||||
|
typedef enum AVRFeature {
|
||||||
|
AVR_FEATURE_SRAM,
|
||||||
|
|
||||||
|
AVR_FEATURE_1_BYTE_PC,
|
||||||
|
AVR_FEATURE_2_BYTE_PC,
|
||||||
|
AVR_FEATURE_3_BYTE_PC,
|
||||||
|
|
||||||
|
AVR_FEATURE_1_BYTE_SP,
|
||||||
|
AVR_FEATURE_2_BYTE_SP,
|
||||||
|
|
||||||
|
AVR_FEATURE_BREAK,
|
||||||
|
AVR_FEATURE_DES,
|
||||||
|
AVR_FEATURE_RMW, /* Read Modify Write - XCH LAC LAS LAT */
|
||||||
|
|
||||||
|
AVR_FEATURE_EIJMP_EICALL,
|
||||||
|
AVR_FEATURE_IJMP_ICALL,
|
||||||
|
AVR_FEATURE_JMP_CALL,
|
||||||
|
|
||||||
|
AVR_FEATURE_ADIW_SBIW,
|
||||||
|
|
||||||
|
AVR_FEATURE_SPM,
|
||||||
|
AVR_FEATURE_SPMX,
|
||||||
|
|
||||||
|
AVR_FEATURE_ELPMX,
|
||||||
|
AVR_FEATURE_ELPM,
|
||||||
|
AVR_FEATURE_LPMX,
|
||||||
|
AVR_FEATURE_LPM,
|
||||||
|
|
||||||
|
AVR_FEATURE_MOVW,
|
||||||
|
AVR_FEATURE_MUL,
|
||||||
|
AVR_FEATURE_RAMPD,
|
||||||
|
AVR_FEATURE_RAMPX,
|
||||||
|
AVR_FEATURE_RAMPY,
|
||||||
|
AVR_FEATURE_RAMPZ,
|
||||||
|
} AVRFeature;
|
||||||
|
|
||||||
|
typedef struct CPUAVRState CPUAVRState;
|
||||||
|
|
||||||
|
struct CPUAVRState {
|
||||||
|
uint32_t pc_w; /* 0x003fffff up to 22 bits */
|
||||||
|
|
||||||
|
uint32_t sregC; /* 0x00000001 1 bit */
|
||||||
|
uint32_t sregZ; /* 0x00000001 1 bit */
|
||||||
|
uint32_t sregN; /* 0x00000001 1 bit */
|
||||||
|
uint32_t sregV; /* 0x00000001 1 bit */
|
||||||
|
uint32_t sregS; /* 0x00000001 1 bit */
|
||||||
|
uint32_t sregH; /* 0x00000001 1 bit */
|
||||||
|
uint32_t sregT; /* 0x00000001 1 bit */
|
||||||
|
uint32_t sregI; /* 0x00000001 1 bit */
|
||||||
|
|
||||||
|
uint32_t rampD; /* 0x00ff0000 8 bits */
|
||||||
|
uint32_t rampX; /* 0x00ff0000 8 bits */
|
||||||
|
uint32_t rampY; /* 0x00ff0000 8 bits */
|
||||||
|
uint32_t rampZ; /* 0x00ff0000 8 bits */
|
||||||
|
uint32_t eind; /* 0x00ff0000 8 bits */
|
||||||
|
|
||||||
|
uint32_t r[NUMBER_OF_CPU_REGISTERS]; /* 8 bits each */
|
||||||
|
uint32_t sp; /* 16 bits */
|
||||||
|
|
||||||
|
uint32_t skip; /* if set skip instruction */
|
||||||
|
|
||||||
|
uint64_t intsrc; /* interrupt sources */
|
||||||
|
bool fullacc; /* CPU/MEM if true MEM only otherwise */
|
||||||
|
|
||||||
|
uint64_t features;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AVRCPU:
|
||||||
|
* @env: #CPUAVRState
|
||||||
|
*
|
||||||
|
* A AVR CPU.
|
||||||
|
*/
|
||||||
|
typedef struct AVRCPU {
|
||||||
|
/*< private >*/
|
||||||
|
CPUState parent_obj;
|
||||||
|
/*< public >*/
|
||||||
|
|
||||||
|
CPUNegativeOffsetState neg;
|
||||||
|
CPUAVRState env;
|
||||||
|
} AVRCPU;
|
||||||
|
|
||||||
|
extern const struct VMStateDescription vms_avr_cpu;
|
||||||
|
|
||||||
|
void avr_cpu_do_interrupt(CPUState *cpu);
|
||||||
|
bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
||||||
|
hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
||||||
|
int avr_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
|
||||||
|
int avr_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
|
int avr_print_insn(bfd_vma addr, disassemble_info *info);
|
||||||
|
|
||||||
|
static inline int avr_feature(CPUAVRState *env, AVRFeature feature)
|
||||||
|
{
|
||||||
|
return (env->features & (1U << feature)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_avr_feature(CPUAVRState *env, int feature)
|
||||||
|
{
|
||||||
|
env->features |= (1U << feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cpu_list avr_cpu_list
|
||||||
|
#define cpu_signal_handler cpu_avr_signal_handler
|
||||||
|
#define cpu_mmu_index avr_cpu_mmu_index
|
||||||
|
|
||||||
|
static inline int avr_cpu_mmu_index(CPUAVRState *env, bool ifetch)
|
||||||
|
{
|
||||||
|
return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void avr_cpu_tcg_init(void);
|
||||||
|
|
||||||
|
void avr_cpu_list(void);
|
||||||
|
int cpu_avr_exec(CPUState *cpu);
|
||||||
|
int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc);
|
||||||
|
int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf,
|
||||||
|
int len, bool is_write);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TB_FLAGS_FULL_ACCESS = 1,
|
||||||
|
TB_FLAGS_SKIP = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc,
|
||||||
|
target_ulong *cs_base, uint32_t *pflags)
|
||||||
|
{
|
||||||
|
uint32_t flags = 0;
|
||||||
|
|
||||||
|
*pc = env->pc_w * 2;
|
||||||
|
*cs_base = 0;
|
||||||
|
|
||||||
|
if (env->fullacc) {
|
||||||
|
flags |= TB_FLAGS_FULL_ACCESS;
|
||||||
|
}
|
||||||
|
if (env->skip) {
|
||||||
|
flags |= TB_FLAGS_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pflags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int cpu_interrupts_enabled(CPUAVRState *env)
|
||||||
|
{
|
||||||
|
return env->sregI != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t cpu_get_sreg(CPUAVRState *env)
|
||||||
|
{
|
||||||
|
uint8_t sreg;
|
||||||
|
sreg = (env->sregC) << 0
|
||||||
|
| (env->sregZ) << 1
|
||||||
|
| (env->sregN) << 2
|
||||||
|
| (env->sregV) << 3
|
||||||
|
| (env->sregS) << 4
|
||||||
|
| (env->sregH) << 5
|
||||||
|
| (env->sregT) << 6
|
||||||
|
| (env->sregI) << 7;
|
||||||
|
return sreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg)
|
||||||
|
{
|
||||||
|
env->sregC = (sreg >> 0) & 0x01;
|
||||||
|
env->sregZ = (sreg >> 1) & 0x01;
|
||||||
|
env->sregN = (sreg >> 2) & 0x01;
|
||||||
|
env->sregV = (sreg >> 3) & 0x01;
|
||||||
|
env->sregS = (sreg >> 4) & 0x01;
|
||||||
|
env->sregH = (sreg >> 5) & 0x01;
|
||||||
|
env->sregT = (sreg >> 6) & 0x01;
|
||||||
|
env->sregI = (sreg >> 7) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||||
|
MMUAccessType access_type, int mmu_idx,
|
||||||
|
bool probe, uintptr_t retaddr);
|
||||||
|
|
||||||
|
typedef CPUAVRState CPUArchState;
|
||||||
|
typedef AVRCPU ArchCPU;
|
||||||
|
|
||||||
|
#include "exec/cpu-all.h"
|
||||||
|
|
||||||
|
#endif /* !defined (QEMU_AVR_CPU_H) */
|
245
target/avr/disas.c
Normal file
245
target/avr/disas.c
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
* AVR disassembler
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019-2020 Richard Henderson <rth@twiddle.net>
|
||||||
|
* Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
disassemble_info *info;
|
||||||
|
uint16_t next_word;
|
||||||
|
bool next_word_used;
|
||||||
|
} DisasContext;
|
||||||
|
|
||||||
|
static int to_regs_16_31_by_one(DisasContext *ctx, int indx)
|
||||||
|
{
|
||||||
|
return 16 + (indx % 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int to_regs_16_23_by_one(DisasContext *ctx, int indx)
|
||||||
|
{
|
||||||
|
return 16 + (indx % 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int to_regs_24_30_by_two(DisasContext *ctx, int indx)
|
||||||
|
{
|
||||||
|
return 24 + (indx % 4) * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int to_regs_00_30_by_two(DisasContext *ctx, int indx)
|
||||||
|
{
|
||||||
|
return (indx % 16) * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t next_word(DisasContext *ctx)
|
||||||
|
{
|
||||||
|
ctx->next_word_used = true;
|
||||||
|
return ctx->next_word;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_16(DisasContext *ctx, int x)
|
||||||
|
{
|
||||||
|
return x << 16 | next_word(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Include the auto-generated decoder. */
|
||||||
|
static bool decode_insn(DisasContext *ctx, uint16_t insn);
|
||||||
|
#include "decode_insn.inc.c"
|
||||||
|
|
||||||
|
#define output(mnemonic, format, ...) \
|
||||||
|
(pctx->info->fprintf_func(pctx->info->stream, "%-9s " format, \
|
||||||
|
mnemonic, ##__VA_ARGS__))
|
||||||
|
|
||||||
|
int avr_print_insn(bfd_vma addr, disassemble_info *info)
|
||||||
|
{
|
||||||
|
DisasContext ctx;
|
||||||
|
DisasContext *pctx = &ctx;
|
||||||
|
bfd_byte buffer[4];
|
||||||
|
uint16_t insn;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
ctx.info = info;
|
||||||
|
|
||||||
|
status = info->read_memory_func(addr, buffer, 4, info);
|
||||||
|
if (status != 0) {
|
||||||
|
info->memory_error_func(status, addr, info);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
insn = bfd_getl16(buffer);
|
||||||
|
ctx.next_word = bfd_getl16(buffer + 2);
|
||||||
|
ctx.next_word_used = false;
|
||||||
|
|
||||||
|
if (!decode_insn(&ctx, insn)) {
|
||||||
|
output(".db", "0x%02x, 0x%02x", buffer[0], buffer[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.next_word_used ? 4 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define INSN(opcode, format, ...) \
|
||||||
|
static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
|
||||||
|
{ \
|
||||||
|
output(#opcode, format, ##__VA_ARGS__); \
|
||||||
|
return true; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INSN_MNEMONIC(opcode, mnemonic, format, ...) \
|
||||||
|
static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
|
||||||
|
{ \
|
||||||
|
output(mnemonic, format, ##__VA_ARGS__); \
|
||||||
|
return true; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C Z N V S H T I
|
||||||
|
* 0 1 2 3 4 5 6 7
|
||||||
|
*/
|
||||||
|
static const char brbc[][5] = {
|
||||||
|
"BRCC", "BRNE", "BRPL", "BRVC", "BRGE", "BRHC", "BRTC", "BRID"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char brbs[][5] = {
|
||||||
|
"BRCS", "BREQ", "BRMI", "BRVS", "BRLT", "BRHS", "BRTS", "BRIE"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char bset[][4] = {
|
||||||
|
"SEC", "SEZ", "SEN", "SEZ", "SES", "SEH", "SET", "SEI"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char bclr[][4] = {
|
||||||
|
"CLC", "CLZ", "CLN", "CLZ", "CLS", "CLH", "CLT", "CLI"
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Arithmetic Instructions
|
||||||
|
*/
|
||||||
|
INSN(ADD, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(ADC, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(ADIW, "r%d:r%d, %d", a->rd + 1, a->rd, a->imm)
|
||||||
|
INSN(SUB, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(SUBI, "r%d, %d", a->rd, a->imm)
|
||||||
|
INSN(SBC, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(SBCI, "r%d, %d", a->rd, a->imm)
|
||||||
|
INSN(SBIW, "r%d:r%d, %d", a->rd + 1, a->rd, a->imm)
|
||||||
|
INSN(AND, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(ANDI, "r%d, %d", a->rd, a->imm)
|
||||||
|
INSN(OR, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(ORI, "r%d, %d", a->rd, a->imm)
|
||||||
|
INSN(EOR, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(COM, "r%d", a->rd)
|
||||||
|
INSN(NEG, "r%d", a->rd)
|
||||||
|
INSN(INC, "r%d", a->rd)
|
||||||
|
INSN(DEC, "r%d", a->rd)
|
||||||
|
INSN(MUL, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(MULS, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(MULSU, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(FMUL, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(FMULS, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(FMULSU, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(DES, "%d", a->imm)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Branch Instructions
|
||||||
|
*/
|
||||||
|
INSN(RJMP, ".%+d", a->imm * 2)
|
||||||
|
INSN(IJMP, "")
|
||||||
|
INSN(EIJMP, "")
|
||||||
|
INSN(JMP, "0x%x", a->imm * 2)
|
||||||
|
INSN(RCALL, ".%+d", a->imm * 2)
|
||||||
|
INSN(ICALL, "")
|
||||||
|
INSN(EICALL, "")
|
||||||
|
INSN(CALL, "0x%x", a->imm * 2)
|
||||||
|
INSN(RET, "")
|
||||||
|
INSN(RETI, "")
|
||||||
|
INSN(CPSE, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(CP, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(CPC, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(CPI, "r%d, %d", a->rd, a->imm)
|
||||||
|
INSN(SBRC, "r%d, %d", a->rr, a->bit)
|
||||||
|
INSN(SBRS, "r%d, %d", a->rr, a->bit)
|
||||||
|
INSN(SBIC, "$%d, %d", a->reg, a->bit)
|
||||||
|
INSN(SBIS, "$%d, %d", a->reg, a->bit)
|
||||||
|
INSN_MNEMONIC(BRBS, brbs[a->bit], ".%+d", a->imm * 2)
|
||||||
|
INSN_MNEMONIC(BRBC, brbc[a->bit], ".%+d", a->imm * 2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data Transfer Instructions
|
||||||
|
*/
|
||||||
|
INSN(MOV, "r%d, r%d", a->rd, a->rr)
|
||||||
|
INSN(MOVW, "r%d:r%d, r%d:r%d", a->rd + 1, a->rd, a->rr + 1, a->rr)
|
||||||
|
INSN(LDI, "r%d, %d", a->rd, a->imm)
|
||||||
|
INSN(LDS, "r%d, %d", a->rd, a->imm)
|
||||||
|
INSN(LDX1, "r%d, X", a->rd)
|
||||||
|
INSN(LDX2, "r%d, X+", a->rd)
|
||||||
|
INSN(LDX3, "r%d, -X", a->rd)
|
||||||
|
INSN(LDY2, "r%d, Y+", a->rd)
|
||||||
|
INSN(LDY3, "r%d, -Y", a->rd)
|
||||||
|
INSN(LDZ2, "r%d, Z+", a->rd)
|
||||||
|
INSN(LDZ3, "r%d, -Z", a->rd)
|
||||||
|
INSN(LDDY, "r%d, Y+%d", a->rd, a->imm)
|
||||||
|
INSN(LDDZ, "r%d, Z+%d", a->rd, a->imm)
|
||||||
|
INSN(STS, "%d, r%d", a->imm, a->rd)
|
||||||
|
INSN(STX1, "X, r%d", a->rr)
|
||||||
|
INSN(STX2, "X+, r%d", a->rr)
|
||||||
|
INSN(STX3, "-X, r%d", a->rr)
|
||||||
|
INSN(STY2, "Y+, r%d", a->rd)
|
||||||
|
INSN(STY3, "-Y, r%d", a->rd)
|
||||||
|
INSN(STZ2, "Z+, r%d", a->rd)
|
||||||
|
INSN(STZ3, "-Z, r%d", a->rd)
|
||||||
|
INSN(STDY, "Y+%d, r%d", a->imm, a->rd)
|
||||||
|
INSN(STDZ, "Z+%d, r%d", a->imm, a->rd)
|
||||||
|
INSN(LPM1, "")
|
||||||
|
INSN(LPM2, "r%d, Z", a->rd)
|
||||||
|
INSN(LPMX, "r%d, Z+", a->rd)
|
||||||
|
INSN(ELPM1, "")
|
||||||
|
INSN(ELPM2, "r%d, Z", a->rd)
|
||||||
|
INSN(ELPMX, "r%d, Z+", a->rd)
|
||||||
|
INSN(SPM, "")
|
||||||
|
INSN(SPMX, "Z+")
|
||||||
|
INSN(IN, "r%d, $%d", a->rd, a->imm)
|
||||||
|
INSN(OUT, "$%d, r%d", a->imm, a->rd)
|
||||||
|
INSN(PUSH, "r%d", a->rd)
|
||||||
|
INSN(POP, "r%d", a->rd)
|
||||||
|
INSN(XCH, "Z, r%d", a->rd)
|
||||||
|
INSN(LAC, "Z, r%d", a->rd)
|
||||||
|
INSN(LAS, "Z, r%d", a->rd)
|
||||||
|
INSN(LAT, "Z, r%d", a->rd)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bit and Bit-test Instructions
|
||||||
|
*/
|
||||||
|
INSN(LSR, "r%d", a->rd)
|
||||||
|
INSN(ROR, "r%d", a->rd)
|
||||||
|
INSN(ASR, "r%d", a->rd)
|
||||||
|
INSN(SWAP, "r%d", a->rd)
|
||||||
|
INSN(SBI, "$%d, %d", a->reg, a->bit)
|
||||||
|
INSN(CBI, "%d, %d", a->reg, a->bit)
|
||||||
|
INSN(BST, "r%d, %d", a->rd, a->bit)
|
||||||
|
INSN(BLD, "r%d, %d", a->rd, a->bit)
|
||||||
|
INSN_MNEMONIC(BSET, bset[a->bit], "")
|
||||||
|
INSN_MNEMONIC(BCLR, bclr[a->bit], "")
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCU Control Instructions
|
||||||
|
*/
|
||||||
|
INSN(BREAK, "")
|
||||||
|
INSN(NOP, "")
|
||||||
|
INSN(SLEEP, "")
|
||||||
|
INSN(WDR, "")
|
84
target/avr/gdbstub.c
Normal file
84
target/avr/gdbstub.c
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* QEMU AVR gdbstub
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "exec/gdbstub.h"
|
||||||
|
|
||||||
|
int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
|
||||||
|
{
|
||||||
|
AVRCPU *cpu = AVR_CPU(cs);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
|
||||||
|
/* R */
|
||||||
|
if (n < 32) {
|
||||||
|
return gdb_get_reg8(mem_buf, env->r[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SREG */
|
||||||
|
if (n == 32) {
|
||||||
|
uint8_t sreg = cpu_get_sreg(env);
|
||||||
|
|
||||||
|
return gdb_get_reg8(mem_buf, sreg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SP */
|
||||||
|
if (n == 33) {
|
||||||
|
return gdb_get_reg16(mem_buf, env->sp & 0x0000ffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PC */
|
||||||
|
if (n == 34) {
|
||||||
|
return gdb_get_reg32(mem_buf, env->pc_w * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int avr_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||||
|
{
|
||||||
|
AVRCPU *cpu = AVR_CPU(cs);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
|
||||||
|
/* R */
|
||||||
|
if (n < 32) {
|
||||||
|
env->r[n] = *mem_buf;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SREG */
|
||||||
|
if (n == 32) {
|
||||||
|
cpu_set_sreg(env, *mem_buf);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SP */
|
||||||
|
if (n == 33) {
|
||||||
|
env->sp = lduw_p(mem_buf);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PC */
|
||||||
|
if (n == 34) {
|
||||||
|
env->pc_w = ldl_p(mem_buf) / 2;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
348
target/avr/helper.c
Normal file
348
target/avr/helper.c
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
/*
|
||||||
|
* QEMU AVR CPU helpers
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "exec/exec-all.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "exec/helper-proto.h"
|
||||||
|
|
||||||
|
bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
CPUClass *cc = CPU_GET_CLASS(cs);
|
||||||
|
AVRCPU *cpu = AVR_CPU(cs);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
|
||||||
|
if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||||
|
if (cpu_interrupts_enabled(env)) {
|
||||||
|
cs->exception_index = EXCP_RESET;
|
||||||
|
cc->do_interrupt(cs);
|
||||||
|
|
||||||
|
cs->interrupt_request &= ~CPU_INTERRUPT_RESET;
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||||
|
if (cpu_interrupts_enabled(env) && env->intsrc != 0) {
|
||||||
|
int index = ctz32(env->intsrc);
|
||||||
|
cs->exception_index = EXCP_INT(index);
|
||||||
|
cc->do_interrupt(cs);
|
||||||
|
|
||||||
|
env->intsrc &= env->intsrc - 1; /* clear the interrupt */
|
||||||
|
cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void avr_cpu_do_interrupt(CPUState *cs)
|
||||||
|
{
|
||||||
|
AVRCPU *cpu = AVR_CPU(cs);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
|
||||||
|
uint32_t ret = env->pc_w;
|
||||||
|
int vector = 0;
|
||||||
|
int size = avr_feature(env, AVR_FEATURE_JMP_CALL) ? 2 : 1;
|
||||||
|
int base = 0;
|
||||||
|
|
||||||
|
if (cs->exception_index == EXCP_RESET) {
|
||||||
|
vector = 0;
|
||||||
|
} else if (env->intsrc != 0) {
|
||||||
|
vector = ctz32(env->intsrc) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) {
|
||||||
|
cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
|
||||||
|
cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8);
|
||||||
|
cpu_stb_data(env, env->sp--, (ret & 0xff0000) >> 16);
|
||||||
|
} else if (avr_feature(env, AVR_FEATURE_2_BYTE_PC)) {
|
||||||
|
cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
|
||||||
|
cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8);
|
||||||
|
} else {
|
||||||
|
cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
|
||||||
|
}
|
||||||
|
|
||||||
|
env->pc_w = base + vector * size;
|
||||||
|
env->sregI = 0; /* clear Global Interrupt Flag */
|
||||||
|
|
||||||
|
cs->exception_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf,
|
||||||
|
int len, bool is_write)
|
||||||
|
{
|
||||||
|
return cpu_memory_rw_debug(cs, addr, buf, len, is_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
||||||
|
{
|
||||||
|
return addr; /* I assume 1:1 address correspondance */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||||
|
MMUAccessType access_type, int mmu_idx,
|
||||||
|
bool probe, uintptr_t retaddr)
|
||||||
|
{
|
||||||
|
int prot = 0;
|
||||||
|
MemTxAttrs attrs = {};
|
||||||
|
uint32_t paddr;
|
||||||
|
|
||||||
|
address &= TARGET_PAGE_MASK;
|
||||||
|
|
||||||
|
if (mmu_idx == MMU_CODE_IDX) {
|
||||||
|
/* access to code in flash */
|
||||||
|
paddr = OFFSET_CODE + address;
|
||||||
|
prot = PAGE_READ | PAGE_EXEC;
|
||||||
|
if (paddr + TARGET_PAGE_SIZE > OFFSET_DATA) {
|
||||||
|
error_report("execution left flash memory");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
|
||||||
|
/*
|
||||||
|
* access to CPU registers, exit and rebuilt this TB to use full access
|
||||||
|
* incase it touches specially handled registers like SREG or SP
|
||||||
|
*/
|
||||||
|
AVRCPU *cpu = AVR_CPU(cs);
|
||||||
|
CPUAVRState *env = &cpu->env;
|
||||||
|
env->fullacc = 1;
|
||||||
|
cpu_loop_exit_restore(cs, retaddr);
|
||||||
|
} else {
|
||||||
|
/* access to memory. nothing special */
|
||||||
|
paddr = OFFSET_DATA + address;
|
||||||
|
prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tlb_set_page_with_attrs(cs, address, paddr, attrs, prot,
|
||||||
|
mmu_idx, TARGET_PAGE_SIZE);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
void helper_sleep(CPUAVRState *env)
|
||||||
|
{
|
||||||
|
CPUState *cs = env_cpu(env);
|
||||||
|
|
||||||
|
cs->exception_index = EXCP_HLT;
|
||||||
|
cpu_loop_exit(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void helper_unsupported(CPUAVRState *env)
|
||||||
|
{
|
||||||
|
CPUState *cs = env_cpu(env);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I count not find what happens on the real platform, so
|
||||||
|
* it's EXCP_DEBUG for meanwhile
|
||||||
|
*/
|
||||||
|
cs->exception_index = EXCP_DEBUG;
|
||||||
|
if (qemu_loglevel_mask(LOG_UNIMP)) {
|
||||||
|
qemu_log("UNSUPPORTED\n");
|
||||||
|
cpu_dump_state(cs, stderr, 0);
|
||||||
|
}
|
||||||
|
cpu_loop_exit(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void helper_debug(CPUAVRState *env)
|
||||||
|
{
|
||||||
|
CPUState *cs = env_cpu(env);
|
||||||
|
|
||||||
|
cs->exception_index = EXCP_DEBUG;
|
||||||
|
cpu_loop_exit(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void helper_break(CPUAVRState *env)
|
||||||
|
{
|
||||||
|
CPUState *cs = env_cpu(env);
|
||||||
|
|
||||||
|
cs->exception_index = EXCP_DEBUG;
|
||||||
|
cpu_loop_exit(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void helper_wdr(CPUAVRState *env)
|
||||||
|
{
|
||||||
|
CPUState *cs = env_cpu(env);
|
||||||
|
|
||||||
|
/* WD is not implemented yet, placeholder */
|
||||||
|
cs->exception_index = EXCP_DEBUG;
|
||||||
|
cpu_loop_exit(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function implements IN instruction
|
||||||
|
*
|
||||||
|
* It does the following
|
||||||
|
* a. if an IO register belongs to CPU, its value is read and returned
|
||||||
|
* b. otherwise io address is translated to mem address and physical memory
|
||||||
|
* is read.
|
||||||
|
* c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
target_ulong helper_inb(CPUAVRState *env, uint32_t port)
|
||||||
|
{
|
||||||
|
target_ulong data = 0;
|
||||||
|
|
||||||
|
switch (port) {
|
||||||
|
case 0x38: /* RAMPD */
|
||||||
|
data = 0xff & (env->rampD >> 16);
|
||||||
|
break;
|
||||||
|
case 0x39: /* RAMPX */
|
||||||
|
data = 0xff & (env->rampX >> 16);
|
||||||
|
break;
|
||||||
|
case 0x3a: /* RAMPY */
|
||||||
|
data = 0xff & (env->rampY >> 16);
|
||||||
|
break;
|
||||||
|
case 0x3b: /* RAMPZ */
|
||||||
|
data = 0xff & (env->rampZ >> 16);
|
||||||
|
break;
|
||||||
|
case 0x3c: /* EIND */
|
||||||
|
data = 0xff & (env->eind >> 16);
|
||||||
|
break;
|
||||||
|
case 0x3d: /* SPL */
|
||||||
|
data = env->sp & 0x00ff;
|
||||||
|
break;
|
||||||
|
case 0x3e: /* SPH */
|
||||||
|
data = env->sp >> 8;
|
||||||
|
break;
|
||||||
|
case 0x3f: /* SREG */
|
||||||
|
data = cpu_get_sreg(env);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* not a special register, pass to normal memory access */
|
||||||
|
data = address_space_ldub(&address_space_memory,
|
||||||
|
OFFSET_IO_REGISTERS + port,
|
||||||
|
MEMTXATTRS_UNSPECIFIED, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function implements OUT instruction
|
||||||
|
*
|
||||||
|
* It does the following
|
||||||
|
* a. if an IO register belongs to CPU, its value is written into the register
|
||||||
|
* b. otherwise io address is translated to mem address and physical memory
|
||||||
|
* is written.
|
||||||
|
* c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data)
|
||||||
|
{
|
||||||
|
data &= 0x000000ff;
|
||||||
|
|
||||||
|
switch (port) {
|
||||||
|
case 0x38: /* RAMPD */
|
||||||
|
if (avr_feature(env, AVR_FEATURE_RAMPD)) {
|
||||||
|
env->rampD = (data & 0xff) << 16;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x39: /* RAMPX */
|
||||||
|
if (avr_feature(env, AVR_FEATURE_RAMPX)) {
|
||||||
|
env->rampX = (data & 0xff) << 16;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x3a: /* RAMPY */
|
||||||
|
if (avr_feature(env, AVR_FEATURE_RAMPY)) {
|
||||||
|
env->rampY = (data & 0xff) << 16;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x3b: /* RAMPZ */
|
||||||
|
if (avr_feature(env, AVR_FEATURE_RAMPZ)) {
|
||||||
|
env->rampZ = (data & 0xff) << 16;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x3c: /* EIDN */
|
||||||
|
env->eind = (data & 0xff) << 16;
|
||||||
|
break;
|
||||||
|
case 0x3d: /* SPL */
|
||||||
|
env->sp = (env->sp & 0xff00) | (data);
|
||||||
|
break;
|
||||||
|
case 0x3e: /* SPH */
|
||||||
|
if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) {
|
||||||
|
env->sp = (env->sp & 0x00ff) | (data << 8);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x3f: /* SREG */
|
||||||
|
cpu_set_sreg(env, data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* not a special register, pass to normal memory access */
|
||||||
|
address_space_stb(&address_space_memory, OFFSET_IO_REGISTERS + port,
|
||||||
|
data, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this function implements LD instruction when there is a posibility to read
|
||||||
|
* from a CPU register
|
||||||
|
*/
|
||||||
|
target_ulong helper_fullrd(CPUAVRState *env, uint32_t addr)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
env->fullacc = false;
|
||||||
|
|
||||||
|
if (addr < NUMBER_OF_CPU_REGISTERS) {
|
||||||
|
/* CPU registers */
|
||||||
|
data = env->r[addr];
|
||||||
|
} else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
|
||||||
|
/* IO registers */
|
||||||
|
data = helper_inb(env, addr - NUMBER_OF_CPU_REGISTERS);
|
||||||
|
} else {
|
||||||
|
/* memory */
|
||||||
|
data = address_space_ldub(&address_space_memory, OFFSET_DATA + addr,
|
||||||
|
MEMTXATTRS_UNSPECIFIED, NULL);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this function implements ST instruction when there is a posibility to write
|
||||||
|
* into a CPU register
|
||||||
|
*/
|
||||||
|
void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr)
|
||||||
|
{
|
||||||
|
env->fullacc = false;
|
||||||
|
|
||||||
|
/* Following logic assumes this: */
|
||||||
|
assert(OFFSET_CPU_REGISTERS == OFFSET_DATA);
|
||||||
|
assert(OFFSET_IO_REGISTERS == OFFSET_CPU_REGISTERS +
|
||||||
|
NUMBER_OF_CPU_REGISTERS);
|
||||||
|
|
||||||
|
if (addr < NUMBER_OF_CPU_REGISTERS) {
|
||||||
|
/* CPU registers */
|
||||||
|
env->r[addr] = data;
|
||||||
|
} else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
|
||||||
|
/* IO registers */
|
||||||
|
helper_outb(env, addr - NUMBER_OF_CPU_REGISTERS, data);
|
||||||
|
} else {
|
||||||
|
/* memory */
|
||||||
|
address_space_stb(&address_space_memory, OFFSET_DATA + addr, data,
|
||||||
|
MEMTXATTRS_UNSPECIFIED, NULL);
|
||||||
|
}
|
||||||
|
}
|
29
target/avr/helper.h
Normal file
29
target/avr/helper.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* QEMU AVR CPU helpers
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
DEF_HELPER_1(wdr, void, env)
|
||||||
|
DEF_HELPER_1(debug, void, env)
|
||||||
|
DEF_HELPER_1(break, void, env)
|
||||||
|
DEF_HELPER_1(sleep, void, env)
|
||||||
|
DEF_HELPER_1(unsupported, void, env)
|
||||||
|
DEF_HELPER_3(outb, void, env, i32, i32)
|
||||||
|
DEF_HELPER_2(inb, tl, env, i32)
|
||||||
|
DEF_HELPER_3(fullwr, void, env, i32, i32)
|
||||||
|
DEF_HELPER_2(fullrd, tl, env, i32)
|
187
target/avr/insn.decode
Normal file
187
target/avr/insn.decode
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
#
|
||||||
|
# AVR instruction decode definitions.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# regs_16_31_by_one = [16 .. 31]
|
||||||
|
# regs_16_23_by_one = [16 .. 23]
|
||||||
|
# regs_24_30_by_two = [24, 26, 28, 30]
|
||||||
|
# regs_00_30_by_two = [0, 2, 4, 6, 8, .. 30]
|
||||||
|
|
||||||
|
%rd 4:5
|
||||||
|
%rr 9:1 0:4
|
||||||
|
|
||||||
|
%rd_a 4:4 !function=to_regs_16_31_by_one
|
||||||
|
%rd_b 4:3 !function=to_regs_16_23_by_one
|
||||||
|
%rd_c 4:2 !function=to_regs_24_30_by_two
|
||||||
|
%rr_a 0:4 !function=to_regs_16_31_by_one
|
||||||
|
%rr_b 0:3 !function=to_regs_16_23_by_one
|
||||||
|
|
||||||
|
%imm6 6:2 0:4
|
||||||
|
%imm8 8:4 0:4
|
||||||
|
|
||||||
|
%io_imm 9:2 0:4
|
||||||
|
%ldst_d_imm 13:1 10:2 0:3
|
||||||
|
|
||||||
|
|
||||||
|
&rd_rr rd rr
|
||||||
|
&rd_imm rd imm
|
||||||
|
|
||||||
|
@op_rd_rr .... .. . ..... .... &rd_rr rd=%rd rr=%rr
|
||||||
|
@op_rd_imm6 .... .... .. .. .... &rd_imm rd=%rd_c imm=%imm6
|
||||||
|
@op_rd_imm8 .... .... .... .... &rd_imm rd=%rd_a imm=%imm8
|
||||||
|
@fmul .... .... . ... . ... &rd_rr rd=%rd_b rr=%rr_b
|
||||||
|
|
||||||
|
#
|
||||||
|
# Arithmetic Instructions
|
||||||
|
#
|
||||||
|
ADD 0000 11 . ..... .... @op_rd_rr
|
||||||
|
ADC 0001 11 . ..... .... @op_rd_rr
|
||||||
|
ADIW 1001 0110 .. .. .... @op_rd_imm6
|
||||||
|
SUB 0001 10 . ..... .... @op_rd_rr
|
||||||
|
SUBI 0101 .... .... .... @op_rd_imm8
|
||||||
|
SBC 0000 10 . ..... .... @op_rd_rr
|
||||||
|
SBCI 0100 .... .... .... @op_rd_imm8
|
||||||
|
SBIW 1001 0111 .. .. .... @op_rd_imm6
|
||||||
|
AND 0010 00 . ..... .... @op_rd_rr
|
||||||
|
ANDI 0111 .... .... .... @op_rd_imm8
|
||||||
|
OR 0010 10 . ..... .... @op_rd_rr
|
||||||
|
ORI 0110 .... .... .... @op_rd_imm8
|
||||||
|
EOR 0010 01 . ..... .... @op_rd_rr
|
||||||
|
COM 1001 010 rd:5 0000
|
||||||
|
NEG 1001 010 rd:5 0001
|
||||||
|
INC 1001 010 rd:5 0011
|
||||||
|
DEC 1001 010 rd:5 1010
|
||||||
|
MUL 1001 11 . ..... .... @op_rd_rr
|
||||||
|
MULS 0000 0010 .... .... &rd_rr rd=%rd_a rr=%rr_a
|
||||||
|
MULSU 0000 0011 0 ... 0 ... @fmul
|
||||||
|
FMUL 0000 0011 0 ... 1 ... @fmul
|
||||||
|
FMULS 0000 0011 1 ... 0 ... @fmul
|
||||||
|
FMULSU 0000 0011 1 ... 1 ... @fmul
|
||||||
|
DES 1001 0100 imm:4 1011
|
||||||
|
|
||||||
|
#
|
||||||
|
# Branch Instructions
|
||||||
|
#
|
||||||
|
|
||||||
|
# The 22-bit immediate is partially in the opcode word,
|
||||||
|
# and partially in the next. Use append_16 to build the
|
||||||
|
# complete 22-bit value.
|
||||||
|
%imm_call 4:5 0:1 !function=append_16
|
||||||
|
|
||||||
|
@op_bit .... .... . bit:3 ....
|
||||||
|
@op_bit_imm .... .. imm:s7 bit:3
|
||||||
|
|
||||||
|
RJMP 1100 imm:s12
|
||||||
|
IJMP 1001 0100 0000 1001
|
||||||
|
EIJMP 1001 0100 0001 1001
|
||||||
|
JMP 1001 010 ..... 110 . imm=%imm_call
|
||||||
|
RCALL 1101 imm:s12
|
||||||
|
ICALL 1001 0101 0000 1001
|
||||||
|
EICALL 1001 0101 0001 1001
|
||||||
|
CALL 1001 010 ..... 111 . imm=%imm_call
|
||||||
|
RET 1001 0101 0000 1000
|
||||||
|
RETI 1001 0101 0001 1000
|
||||||
|
CPSE 0001 00 . ..... .... @op_rd_rr
|
||||||
|
CP 0001 01 . ..... .... @op_rd_rr
|
||||||
|
CPC 0000 01 . ..... .... @op_rd_rr
|
||||||
|
CPI 0011 .... .... .... @op_rd_imm8
|
||||||
|
SBRC 1111 110 rr:5 0 bit:3
|
||||||
|
SBRS 1111 111 rr:5 0 bit:3
|
||||||
|
SBIC 1001 1001 reg:5 bit:3
|
||||||
|
SBIS 1001 1011 reg:5 bit:3
|
||||||
|
BRBS 1111 00 ....... ... @op_bit_imm
|
||||||
|
BRBC 1111 01 ....... ... @op_bit_imm
|
||||||
|
|
||||||
|
#
|
||||||
|
# Data Transfer Instructions
|
||||||
|
#
|
||||||
|
|
||||||
|
%rd_d 4:4 !function=to_regs_00_30_by_two
|
||||||
|
%rr_d 0:4 !function=to_regs_00_30_by_two
|
||||||
|
|
||||||
|
@io_rd_imm .... . .. ..... .... &rd_imm rd=%rd imm=%io_imm
|
||||||
|
@ldst_d .. . . .. . rd:5 . ... &rd_imm imm=%ldst_d_imm
|
||||||
|
|
||||||
|
# The 16-bit immediate is completely in the next word.
|
||||||
|
# Fields cannot be defined with no bits, so we cannot play
|
||||||
|
# the same trick and append to a zero-bit value.
|
||||||
|
# Defer reading the immediate until trans_{LDS,STS}.
|
||||||
|
@ldst_s .... ... rd:5 .... imm=0
|
||||||
|
|
||||||
|
MOV 0010 11 . ..... .... @op_rd_rr
|
||||||
|
MOVW 0000 0001 .... .... &rd_rr rd=%rd_d rr=%rr_d
|
||||||
|
LDI 1110 .... .... .... @op_rd_imm8
|
||||||
|
LDS 1001 000 ..... 0000 @ldst_s
|
||||||
|
LDX1 1001 000 rd:5 1100
|
||||||
|
LDX2 1001 000 rd:5 1101
|
||||||
|
LDX3 1001 000 rd:5 1110
|
||||||
|
LDY2 1001 000 rd:5 1001
|
||||||
|
LDY3 1001 000 rd:5 1010
|
||||||
|
LDZ2 1001 000 rd:5 0001
|
||||||
|
LDZ3 1001 000 rd:5 0010
|
||||||
|
LDDY 10 . 0 .. 0 ..... 1 ... @ldst_d
|
||||||
|
LDDZ 10 . 0 .. 0 ..... 0 ... @ldst_d
|
||||||
|
STS 1001 001 ..... 0000 @ldst_s
|
||||||
|
STX1 1001 001 rr:5 1100
|
||||||
|
STX2 1001 001 rr:5 1101
|
||||||
|
STX3 1001 001 rr:5 1110
|
||||||
|
STY2 1001 001 rd:5 1001
|
||||||
|
STY3 1001 001 rd:5 1010
|
||||||
|
STZ2 1001 001 rd:5 0001
|
||||||
|
STZ3 1001 001 rd:5 0010
|
||||||
|
STDY 10 . 0 .. 1 ..... 1 ... @ldst_d
|
||||||
|
STDZ 10 . 0 .. 1 ..... 0 ... @ldst_d
|
||||||
|
LPM1 1001 0101 1100 1000
|
||||||
|
LPM2 1001 000 rd:5 0100
|
||||||
|
LPMX 1001 000 rd:5 0101
|
||||||
|
ELPM1 1001 0101 1101 1000
|
||||||
|
ELPM2 1001 000 rd:5 0110
|
||||||
|
ELPMX 1001 000 rd:5 0111
|
||||||
|
SPM 1001 0101 1110 1000
|
||||||
|
SPMX 1001 0101 1111 1000
|
||||||
|
IN 1011 0 .. ..... .... @io_rd_imm
|
||||||
|
OUT 1011 1 .. ..... .... @io_rd_imm
|
||||||
|
PUSH 1001 001 rd:5 1111
|
||||||
|
POP 1001 000 rd:5 1111
|
||||||
|
XCH 1001 001 rd:5 0100
|
||||||
|
LAC 1001 001 rd:5 0110
|
||||||
|
LAS 1001 001 rd:5 0101
|
||||||
|
LAT 1001 001 rd:5 0111
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bit and Bit-test Instructions
|
||||||
|
#
|
||||||
|
LSR 1001 010 rd:5 0110
|
||||||
|
ROR 1001 010 rd:5 0111
|
||||||
|
ASR 1001 010 rd:5 0101
|
||||||
|
SWAP 1001 010 rd:5 0010
|
||||||
|
SBI 1001 1010 reg:5 bit:3
|
||||||
|
CBI 1001 1000 reg:5 bit:3
|
||||||
|
BST 1111 101 rd:5 0 bit:3
|
||||||
|
BLD 1111 100 rd:5 0 bit:3
|
||||||
|
BSET 1001 0100 0 bit:3 1000
|
||||||
|
BCLR 1001 0100 1 bit:3 1000
|
||||||
|
|
||||||
|
#
|
||||||
|
# MCU Control Instructions
|
||||||
|
#
|
||||||
|
BREAK 1001 0101 1001 1000
|
||||||
|
NOP 0000 0000 0000 0000
|
||||||
|
SLEEP 1001 0101 1000 1000
|
||||||
|
WDR 1001 0101 1010 1000
|
119
target/avr/machine.c
Normal file
119
target/avr/machine.c
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "migration/cpu.h"
|
||||||
|
|
||||||
|
static int get_sreg(QEMUFile *f, void *opaque, size_t size,
|
||||||
|
const VMStateField *field)
|
||||||
|
{
|
||||||
|
CPUAVRState *env = opaque;
|
||||||
|
uint8_t sreg;
|
||||||
|
|
||||||
|
sreg = qemu_get_byte(f);
|
||||||
|
cpu_set_sreg(env, sreg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int put_sreg(QEMUFile *f, void *opaque, size_t size,
|
||||||
|
const VMStateField *field, QJSON *vmdesc)
|
||||||
|
{
|
||||||
|
CPUAVRState *env = opaque;
|
||||||
|
uint8_t sreg = cpu_get_sreg(env);
|
||||||
|
|
||||||
|
qemu_put_byte(f, sreg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateInfo vms_sreg = {
|
||||||
|
.name = "sreg",
|
||||||
|
.get = get_sreg,
|
||||||
|
.put = put_sreg,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_segment(QEMUFile *f, void *opaque, size_t size,
|
||||||
|
const VMStateField *field)
|
||||||
|
{
|
||||||
|
uint32_t *ramp = opaque;
|
||||||
|
uint8_t temp;
|
||||||
|
|
||||||
|
temp = qemu_get_byte(f);
|
||||||
|
*ramp = ((uint32_t)temp) << 16;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int put_segment(QEMUFile *f, void *opaque, size_t size,
|
||||||
|
const VMStateField *field, QJSON *vmdesc)
|
||||||
|
{
|
||||||
|
uint32_t *ramp = opaque;
|
||||||
|
uint8_t temp = *ramp >> 16;
|
||||||
|
|
||||||
|
qemu_put_byte(f, temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateInfo vms_rampD = {
|
||||||
|
.name = "rampD",
|
||||||
|
.get = get_segment,
|
||||||
|
.put = put_segment,
|
||||||
|
};
|
||||||
|
static const VMStateInfo vms_rampX = {
|
||||||
|
.name = "rampX",
|
||||||
|
.get = get_segment,
|
||||||
|
.put = put_segment,
|
||||||
|
};
|
||||||
|
static const VMStateInfo vms_rampY = {
|
||||||
|
.name = "rampY",
|
||||||
|
.get = get_segment,
|
||||||
|
.put = put_segment,
|
||||||
|
};
|
||||||
|
static const VMStateInfo vms_rampZ = {
|
||||||
|
.name = "rampZ",
|
||||||
|
.get = get_segment,
|
||||||
|
.put = put_segment,
|
||||||
|
};
|
||||||
|
static const VMStateInfo vms_eind = {
|
||||||
|
.name = "eind",
|
||||||
|
.get = get_segment,
|
||||||
|
.put = put_segment,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VMStateDescription vms_avr_cpu = {
|
||||||
|
.name = "cpu",
|
||||||
|
.version_id = 0,
|
||||||
|
.minimum_version_id = 0,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(env.pc_w, AVRCPU),
|
||||||
|
VMSTATE_UINT32(env.sp, AVRCPU),
|
||||||
|
VMSTATE_UINT32(env.skip, AVRCPU),
|
||||||
|
|
||||||
|
VMSTATE_UINT32_ARRAY(env.r, AVRCPU, NUMBER_OF_CPU_REGISTERS),
|
||||||
|
|
||||||
|
VMSTATE_SINGLE(env, AVRCPU, 0, vms_sreg, CPUAVRState),
|
||||||
|
VMSTATE_SINGLE(env.rampD, AVRCPU, 0, vms_rampD, uint32_t),
|
||||||
|
VMSTATE_SINGLE(env.rampX, AVRCPU, 0, vms_rampX, uint32_t),
|
||||||
|
VMSTATE_SINGLE(env.rampY, AVRCPU, 0, vms_rampY, uint32_t),
|
||||||
|
VMSTATE_SINGLE(env.rampZ, AVRCPU, 0, vms_rampZ, uint32_t),
|
||||||
|
VMSTATE_SINGLE(env.eind, AVRCPU, 0, vms_eind, uint32_t),
|
||||||
|
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
3061
target/avr/translate.c
Normal file
3061
target/avr/translate.c
Normal file
File diff suppressed because it is too large
Load Diff
50
tests/acceptance/machine_avr6.py
Normal file
50
tests/acceptance/machine_avr6.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#
|
||||||
|
# QEMU AVR acceptance tests
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from avocado_qemu import Test
|
||||||
|
|
||||||
|
class AVR6Machine(Test):
|
||||||
|
timeout = 5
|
||||||
|
|
||||||
|
def test_freertos(self):
|
||||||
|
"""
|
||||||
|
:avocado: tags=arch:avr
|
||||||
|
:avocado: tags=machine:arduino-mega-2560-v3
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
https://github.com/seharris/qemu-avr-tests/raw/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf
|
||||||
|
constantly prints out 'ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX'
|
||||||
|
"""
|
||||||
|
rom_url = ('https://github.com/seharris/qemu-avr-tests'
|
||||||
|
'/raw/36c3e67b8755dcf/free-rtos/Demo'
|
||||||
|
'/AVR_ATMega2560_GCC/demo.elf')
|
||||||
|
rom_hash = '7eb521f511ca8f2622e0a3c5e8dd686efbb911d4'
|
||||||
|
rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
|
||||||
|
|
||||||
|
self.vm.add_args('-bios', rom_path)
|
||||||
|
self.vm.add_args('-nographic')
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
|
self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX',
|
||||||
|
self.vm.get_log())
|
@ -66,6 +66,8 @@ check-qtest-i386-y += numa-test
|
|||||||
|
|
||||||
check-qtest-x86_64-y += $(check-qtest-i386-y)
|
check-qtest-x86_64-y += $(check-qtest-i386-y)
|
||||||
|
|
||||||
|
check-qtest-avr-y += boot-serial-test
|
||||||
|
|
||||||
check-qtest-alpha-y += boot-serial-test
|
check-qtest-alpha-y += boot-serial-test
|
||||||
check-qtest-alpha-$(CONFIG_VGA) += display-vga-test
|
check-qtest-alpha-$(CONFIG_VGA) += display-vga-test
|
||||||
|
|
||||||
|
@ -17,6 +17,15 @@
|
|||||||
#include "libqtest.h"
|
#include "libqtest.h"
|
||||||
#include "libqos/libqos-spapr.h"
|
#include "libqos/libqos-spapr.h"
|
||||||
|
|
||||||
|
static const uint8_t bios_avr[] = {
|
||||||
|
0x88, 0xe0, /* ldi r24, 0x08 */
|
||||||
|
0x80, 0x93, 0xc1, 0x00, /* sts 0x00C1, r24 ; Enable tx */
|
||||||
|
0x86, 0xe0, /* ldi r24, 0x06 */
|
||||||
|
0x80, 0x93, 0xc2, 0x00, /* sts 0x00C2, r24 ; Set the data bits to 8 */
|
||||||
|
0x84, 0xe5, /* ldi r24, 0x54 */
|
||||||
|
0x80, 0x93, 0xc6, 0x00, /* sts 0x00C6, r24 ; Output 'T' */
|
||||||
|
};
|
||||||
|
|
||||||
static const uint8_t kernel_mcf5208[] = {
|
static const uint8_t kernel_mcf5208[] = {
|
||||||
0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */
|
0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */
|
||||||
0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */
|
0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */
|
||||||
@ -104,6 +113,8 @@ typedef struct testdef {
|
|||||||
|
|
||||||
static testdef_t tests[] = {
|
static testdef_t tests[] = {
|
||||||
{ "alpha", "clipper", "", "PCI:" },
|
{ "alpha", "clipper", "", "PCI:" },
|
||||||
|
{ "avr", "arduino-duemilanove", "", "T", sizeof(bios_avr), NULL, bios_avr },
|
||||||
|
{ "avr", "arduino-mega-2560-v3", "", "T", sizeof(bios_avr), NULL, bios_avr},
|
||||||
{ "ppc", "ppce500", "", "U-Boot" },
|
{ "ppc", "ppce500", "", "U-Boot" },
|
||||||
{ "ppc", "40p", "-vga none -boot d", "Trying cd:," },
|
{ "ppc", "40p", "-vga none -boot d", "Trying cd:," },
|
||||||
{ "ppc", "g3beige", "", "PowerPC,750" },
|
{ "ppc", "g3beige", "", "PowerPC,750" },
|
||||||
|
@ -27,6 +27,7 @@ static struct arch2cpu cpus_map[] = {
|
|||||||
/* tested targets list */
|
/* tested targets list */
|
||||||
{ "arm", "cortex-a15" },
|
{ "arm", "cortex-a15" },
|
||||||
{ "aarch64", "cortex-a57" },
|
{ "aarch64", "cortex-a57" },
|
||||||
|
{ "avr", "avr6-avr-cpu" },
|
||||||
{ "x86_64", "qemu64,apic-id=0" },
|
{ "x86_64", "qemu64,apic-id=0" },
|
||||||
{ "i386", "qemu32,apic-id=0" },
|
{ "i386", "qemu32,apic-id=0" },
|
||||||
{ "alpha", "ev67" },
|
{ "alpha", "ev67" },
|
||||||
|
Loading…
Reference in New Issue
Block a user