From nobody Thu Apr 25 14:09:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1562909899; cv=none; d=zoho.com; s=zohoarc; b=WLQRK6b3+vjab9139AXWX+sKg3bdh4VxTlKtFHz94kCdTQAdmsLbrcY+ISZ/+uonb3wdsNKLeVvJplqeCfcNI5loIkLgbUGKmJ/BIRhH0z6jTEzl3rTZzycDhBszX8ifPSTupRKRtc5g4yABQ18M5kjWv9W3DMLGvUN7lg2aM+I= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1562909899; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=8zghmxBHAWHWTdocBPUgz8TL7PONGm85AWyc1TEm4jU=; b=CPXhW8ZAIk9J31b1GlQJVZ1/Bb/BqsF0jSIDESXSKmToqfNa+BQueRhHDBnRrqeWcUMlCd5dofQ7tZ8xpzg/H2bqOIo2plfhKb4MG/2xjDw++l2b+mOgOrOlcos5Aeq9HLVW3TVPexeC6iv//GddH+AYgakuH1MimequbTDGFAg= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 156290989983829.61159393346736; Thu, 11 Jul 2019 22:38:19 -0700 (PDT) Received: from localhost ([::1]:46708 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloGA-0000Xf-Hv for importer@patchew.org; Fri, 12 Jul 2019 01:38:18 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35021) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFA-0004vd-Vh for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hloF7-0007cK-1Z for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:16 -0400 Received: from mail-wr1-x444.google.com ([2a00:1450:4864:20::444]:42098) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hloF6-0007bu-Ml for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:12 -0400 Received: by mail-wr1-x444.google.com with SMTP id j8so4458964wrj.9 for ; Thu, 11 Jul 2019 22:37:12 -0700 (PDT) Received: from 8c859074c0ff.ant.amazon.com.com (bzq-109-66-61-47.red.bezeqint.net. [109.66.61.47]) by smtp.gmail.com with ESMTPSA id y6sm7824009wmd.16.2019.07.11.22.37.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Jul 2019 22:37:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8zghmxBHAWHWTdocBPUgz8TL7PONGm85AWyc1TEm4jU=; b=MOuOnfTXS/tBaQKmddhF+1+1BRY/XjuT+1P9aC6KYbTNJqykuVMQpCHFy8RUodxSi1 HqGhKWNrViqvt3oAwQqpsWJH6JfGVawjmDxaJ2uJZNYTCMR160I2hcHVm91MkjN8fL9M 21khxybAntpi7pKA3n6mQU8zWL9Tis7VIuP8O4yfRz6wGW4RxwUgsEyQ4wbZOFZMiwit 0IhU+ciGJ5a6Dt1wdKwvZE27+VYMK6tKjOvhdpJzEmhdRmJwqDEh1L8R/wmpPbYOk8vC /1rtY7+hEcILI1SVewRwVuBne4nNPUugWucyTUwzxrXYF5G4iWzE7Uu623S1C0hLRe4K /fnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=8zghmxBHAWHWTdocBPUgz8TL7PONGm85AWyc1TEm4jU=; b=ZNQX2UZD++NqKRbaPnnGrB1oikFlZGs9E+cL/P3AqVGXB0XSwgk8wUtlBEODgwkzTM 8B387QGfPHul1FZuUvDd0SY4GZXyOmqJPOrV3SYRNX2nlUaPbPVUuSubeIEunIOwesHs IwfEYQ41ZbQJtTHDj3NyMZeuwgwv7nHjhlTwMRZH4z1lh8PJDJAYYPvJ7rU+QOQBXjof kItO8/6ptIoN0Cjx9GKYyY34nRMBwhEn5lxhN6+PXXzQ5Qu+DWB+i5s9BaZaNnrK4poZ 0njFESw0mz1yZplPUGwg3X9VryCbj2lV0JXJYkSHIheiws2jmpnQ7rhOj7ZugB7S0mJI awZw== X-Gm-Message-State: APjAAAVEI5Ad9Wg8NcdHCGlfC2/vQKAmFkIWBD9txXH0DrhJ8RB5zLW9 M44p9Y8A8k0GgkfNpHI+TovaoRLVVVgZ1g== X-Google-Smtp-Source: APXvYqxpUdi/h/T04aRPI70ThaIBpZ2bLHgC8alNsdGTffhsPa7++bfksrqaI4Yo0JH68IydILyn0Q== X-Received: by 2002:a5d:5647:: with SMTP id j7mr8966236wrw.191.1562909830958; Thu, 11 Jul 2019 22:37:10 -0700 (PDT) From: Michael Rolnik To: qemu-devel@nongnu.org Date: Fri, 12 Jul 2019 08:36:58 +0300 Message-Id: <20190712053704.21228-2-mrolnik@gmail.com> X-Mailer: git-send-email 2.17.2 (Apple Git-113) In-Reply-To: <20190712053704.21228-1-mrolnik@gmail.com> References: <20190712053704.21228-1-mrolnik@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::444 Subject: [Qemu-devel] [PATCH v26 1/7] target/avr: Add outward facing interfaces and core CPU logic X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: imammedo@redhat.com, Sarah Harris , Michael Rolnik , richard.henderson@linaro.org, philmd@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Sarah Harris This includes: - CPU data structures - object model classes and functions - migration functions - GDB hooks Signed-off-by: Michael Rolnik Acked-by: Igor Mammedov --- gdb-xml/avr-cpu.xml | 49 ++++ target/avr/cpu-param.h | 37 +++ target/avr/cpu.c | 579 +++++++++++++++++++++++++++++++++++++++++ target/avr/cpu.h | 280 ++++++++++++++++++++ target/avr/gdbstub.c | 85 ++++++ target/avr/machine.c | 123 +++++++++ 6 files changed, 1153 insertions(+) create mode 100644 gdb-xml/avr-cpu.xml create mode 100644 target/avr/cpu-param.h create mode 100644 target/avr/cpu.c create mode 100644 target/avr/cpu.h create mode 100644 target/avr/gdbstub.c create mode 100644 target/avr/machine.c diff --git a/gdb-xml/avr-cpu.xml b/gdb-xml/avr-cpu.xml new file mode 100644 index 0000000000..c4747f5b40 --- /dev/null +++ b/gdb-xml/avr-cpu.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h new file mode 100644 index 0000000000..ccd1ea3429 --- /dev/null +++ b/target/avr/cpu-param.h @@ -0,0 +1,37 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +#ifndef AVR_CPU_PARAM_H +#define AVR_CPU_PARAM_H 1 + +#define TARGET_LONG_BITS 32 +/* + * TARGET_PAGE_BITS cannot be more than 8 bits because + * 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they + * should be implemented as a device and not memory + * 2. SRAM starts at the address 0x0100 + */ +#define TARGET_PAGE_BITS 8 +#define TARGET_PHYS_ADDR_SPACE_BITS 24 +#define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define NB_MMU_MODES 2 + + +#endif diff --git a/target/avr/cpu.c b/target/avr/cpu.c new file mode 100644 index 0000000000..c474526925 --- /dev/null +++ b/target/avr/cpu.c @@ -0,0 +1,579 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +#include "qemu/osdep.h" +#include "qemu/qemu-print.h" +#include "qemu/log.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +static void avr_cpu_set_pc(CPUState *cs, vaddr value) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + + cpu->env.pc_w =3D value / 2; /* internally PC points to words */ +} + +static bool avr_cpu_has_work(CPUState *cs) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RE= SET)) + && cpu_interrupts_enabled(env); +} + +static void avr_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + env->pc_w =3D tb->pc / 2; /* internally PC points to words */ +} + +static void avr_cpu_reset(CPUState *cs) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + AVRCPUClass *mcc =3D AVR_CPU_GET_CLASS(cpu); + CPUAVRState *env =3D &cpu->env; + + mcc->parent_reset(cs); + + env->pc_w =3D 0; + env->sregI =3D 1; + env->sregC =3D 0; + env->sregZ =3D 0; + env->sregN =3D 0; + env->sregV =3D 0; + env->sregS =3D 0; + env->sregH =3D 0; + env->sregT =3D 0; + + env->rampD =3D 0; + env->rampX =3D 0; + env->rampY =3D 0; + env->rampZ =3D 0; + env->eind =3D 0; + env->sp =3D 0; + + env->skip =3D 0; + + memset(env->r, 0, sizeof(env->r)); + + tlb_flush(cs); +} + +static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach =3D bfd_arch_avr; + info->print_insn =3D NULL; +} + +static void avr_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs =3D CPU(dev); + AVRCPUClass *mcc =3D AVR_CPU_GET_CLASS(dev); + Error *local_err =3D NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err !=3D 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 =3D opaque; + CPUAVRState *env =3D &cpu->env; + CPUState *cs =3D CPU(cpu); + + uint64_t mask =3D (1ull << irq); + if (level) { + env->intsrc |=3D mask; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + env->intsrc &=3D ~mask; + if (env->intsrc =3D=3D 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +static void avr_cpu_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + + cpu_set_cpustate_pointers(cpu); + +#ifndef CONFIG_USER_ONLY + /* Set the number of interrupts supported by the CPU. */ + qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, 57); +#endif +} + +static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + + oc =3D object_class_by_name(cpu_model); + if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) =3D=3D NULL || + object_class_is_abstract(oc)) { + oc =3D NULL; + } + return oc; +} + +static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + int i; + + qemu_fprintf(f, "\n"); + qemu_fprintf(f, "PC: %06x\n", env->pc_w); + 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 =3D 0; i < ARRAY_SIZE(env->r); i++) { + qemu_fprintf(f, "R[%02d]: %02x ", i, env->r[i]); + + if ((i % 8) =3D=3D 7) { + qemu_fprintf(f, "\n"); + } + } + qemu_fprintf(f, "\n"); +} + +static void avr_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(oc); + CPUClass *cc =3D CPU_CLASS(oc); + AVRCPUClass *mcc =3D AVR_CPU_CLASS(oc); + + mcc->parent_realize =3D dc->realize; + dc->realize =3D avr_cpu_realizefn; + + mcc->parent_reset =3D cc->reset; + cc->reset =3D avr_cpu_reset; + + cc->class_by_name =3D avr_cpu_class_by_name; + + cc->has_work =3D avr_cpu_has_work; + cc->do_interrupt =3D avr_cpu_do_interrupt; + cc->cpu_exec_interrupt =3D avr_cpu_exec_interrupt; + cc->dump_state =3D avr_cpu_dump_state; + cc->set_pc =3D avr_cpu_set_pc; +#if !defined(CONFIG_USER_ONLY) + cc->memory_rw_debug =3D avr_cpu_memory_rw_debug; +#endif +#ifdef CONFIG_USER_ONLY + cc->handle_mmu_fault =3D avr_cpu_handle_mmu_fault; +#else + cc->get_phys_page_debug =3D avr_cpu_get_phys_page_debug; + cc->vmsd =3D &vms_avr_cpu; +#endif + cc->disas_set_info =3D avr_cpu_disas_set_info; + cc->tlb_fill =3D avr_cpu_tlb_fill; + cc->tcg_initialize =3D avr_cpu_tcg_init; + cc->synchronize_from_tb =3D avr_cpu_synchronize_from_tb; + cc->gdb_read_register =3D avr_cpu_gdb_read_register; + cc->gdb_write_register =3D avr_cpu_gdb_write_register; + cc->gdb_num_core_regs =3D 35; + cc->gdb_core_xml_file =3D "avr-cpu.xml"; +} + +static void avr_avr1_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); +} + +static void avr_avr2_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); +} + +static void avr_avr25_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); +} + +static void avr_avr3_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); +} + +static void avr_avr31_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); +} + +static void avr_avr35_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); +} + +static void avr_avr4_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_avr5_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_avr51_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_avr6_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_3_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_EIJMP_EICALL); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); +} + +static void avr_xmega2_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega4_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega5_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_2_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPD); + avr_set_feature(env, AVR_FEATURE_RAMPX); + avr_set_feature(env, AVR_FEATURE_RAMPY); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega6_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_3_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_EIJMP_EICALL); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +static void avr_xmega7_initfn(Object *obj) +{ + AVRCPU *cpu =3D AVR_CPU(obj); + CPUAVRState *env =3D &cpu->env; + + avr_set_feature(env, AVR_FEATURE_LPM); + avr_set_feature(env, AVR_FEATURE_IJMP_ICALL); + avr_set_feature(env, AVR_FEATURE_ADIW_SBIW); + avr_set_feature(env, AVR_FEATURE_SRAM); + avr_set_feature(env, AVR_FEATURE_BREAK); + + avr_set_feature(env, AVR_FEATURE_3_BYTE_PC); + avr_set_feature(env, AVR_FEATURE_2_BYTE_SP); + avr_set_feature(env, AVR_FEATURE_RAMPD); + avr_set_feature(env, AVR_FEATURE_RAMPX); + avr_set_feature(env, AVR_FEATURE_RAMPY); + avr_set_feature(env, AVR_FEATURE_RAMPZ); + avr_set_feature(env, AVR_FEATURE_EIJMP_EICALL); + avr_set_feature(env, AVR_FEATURE_ELPMX); + avr_set_feature(env, AVR_FEATURE_ELPM); + avr_set_feature(env, AVR_FEATURE_JMP_CALL); + avr_set_feature(env, AVR_FEATURE_LPMX); + avr_set_feature(env, AVR_FEATURE_MOVW); + avr_set_feature(env, AVR_FEATURE_MUL); + avr_set_feature(env, AVR_FEATURE_RMW); +} + +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 =3D object_class_get_name(OBJECT_CLASS(data)); + + qemu_printf("%s\n", typename); +} + +void avr_cpu_list(void) +{ + GSList *list; + list =3D 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 =3D TYPE_AVR_CPU, \ + .instance_init =3D initfn, \ + .name =3D model "-avr-cpu", \ + } + +static const TypeInfo avr_cpu_type_info[] =3D { + { + .name =3D TYPE_AVR_CPU, + .parent =3D TYPE_CPU, + .instance_size =3D sizeof(AVRCPU), + .instance_init =3D avr_cpu_initfn, + .class_size =3D sizeof(AVRCPUClass), + .class_init =3D avr_cpu_class_init, + .abstract =3D true, + }, + DEFINE_AVR_CPU_TYPE("avr1", avr_avr1_initfn), + DEFINE_AVR_CPU_TYPE("avr2", avr_avr2_initfn), + DEFINE_AVR_CPU_TYPE("avr25", avr_avr25_initfn), + DEFINE_AVR_CPU_TYPE("avr3", avr_avr3_initfn), + DEFINE_AVR_CPU_TYPE("avr31", avr_avr31_initfn), + DEFINE_AVR_CPU_TYPE("avr35", avr_avr35_initfn), + DEFINE_AVR_CPU_TYPE("avr4", avr_avr4_initfn), + 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_AVR_CPU_TYPE("xmega2", avr_xmega2_initfn), + DEFINE_AVR_CPU_TYPE("xmega4", avr_xmega4_initfn), + DEFINE_AVR_CPU_TYPE("xmega5", avr_xmega5_initfn), + DEFINE_AVR_CPU_TYPE("xmega6", avr_xmega6_initfn), + DEFINE_AVR_CPU_TYPE("xmega7", avr_xmega7_initfn), +}; + +DEFINE_TYPES(avr_cpu_type_info) diff --git a/target/avr/cpu.h b/target/avr/cpu.h new file mode 100644 index 0000000000..2659785cf8 --- /dev/null +++ b/target/avr/cpu.h @@ -0,0 +1,280 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +#ifndef CPU_AVR_H +#define CPU_AVR_H + +#include "qemu-common.h" +#include "qom/cpu.h" +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +#define TCG_GUEST_DEFAULT_MO 0 + +#define TYPE_AVR_CPU "avr-cpu" +#define CPU_RESOLVING_TYPE TYPE_AVR_CPU + +/* + * 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 NO_CPU_REGISTERS 32 +/* Number of IO registers accessible by ld/st/in/out */ +#define NO_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 + NO_CPU_REGISTERS) + +enum avr_features { + 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, +}; + +typedef struct CPUAVRState CPUAVRState; + +struct CPUAVRState { + uint32_t pc_w; /* 0x003fffff up to 22 bits */ + + uint32_t sregC; /* 0x00000001 1 bits */ + uint32_t sregZ; /* 0x0000ffff 16 bits, negative logic; */ + /* 0=3Dflag set, >0=3Dflag cleared */ + uint32_t sregN; /* 0x00000001 1 bits */ + uint32_t sregV; /* 0x00000001 1 bits */ + uint32_t sregS; /* 0x00000001 1 bits */ + uint32_t sregH; /* 0x00000001 1 bits */ + uint32_t sregT; /* 0x00000001 1 bits */ + uint32_t sregI; /* 0x00000001 1 bits */ + + 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[NO_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 */ + + uint32_t features; +}; + +#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; + void (*parent_reset)(CPUState *cpu); +} AVRCPUClass; + +/** + * AVRCPU: + * @env: #CPUAVRState + * + * A AVR CPU. + */ +typedef struct AVRCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUNegativeOffsetState neg; + CPUAVRState env; +} AVRCPU; + +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vms_avr_cpu; +#endif + +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, uint8_t *buf, int reg); +int avr_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +static inline int avr_feature(CPUAVRState *env, int feature) +{ + return (env->features & (1U << feature)) !=3D 0; +} + +static inline void avr_set_feature(CPUAVRState *env, int feature) +{ + env->features |=3D (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_handle_mmu_fault(CPUState *cpu, vaddr address, int size, + int rw, int mmu_idx); +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf, + int len, bool is_write); + +enum { + TB_FLAGS_FULL_ACCESS =3D 1, + TB_FLAGS_SKIP =3D 2, +}; + +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *pflags) +{ + uint32_t flags =3D 0; + + *pc =3D env->pc_w * 2; + *cs_base =3D 0; + + if (env->fullacc) { + flags |=3D TB_FLAGS_FULL_ACCESS; + } + if (env->skip) { + flags |=3D TB_FLAGS_SKIP; + } + + *pflags =3D flags; +} + +static inline int cpu_interrupts_enabled(CPUAVRState *env) +{ + return env->sregI !=3D 0; +} + +static inline uint8_t cpu_get_sreg(CPUAVRState *env) +{ + uint8_t sreg; + sreg =3D (env->sregC & 0x01) << 0 + | (env->sregZ =3D=3D 0 ? 1 : 0) << 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 =3D (sreg >> 0) & 0x01; + env->sregZ =3D (sreg >> 1) & 0x01 ? 0 : 1; + env->sregN =3D (sreg >> 2) & 0x01; + env->sregV =3D (sreg >> 3) & 0x01; + env->sregS =3D (sreg >> 4) & 0x01; + env->sregH =3D (sreg >> 5) & 0x01; + env->sregT =3D (sreg >> 6) & 0x01; + env->sregI =3D (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 (CPU_AVR_H) */ diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c new file mode 100644 index 0000000000..20a5252482 --- /dev/null +++ b/target/avr/gdbstub.c @@ -0,0 +1,85 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int avr_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + /* R */ + if (n < 32) { + return gdb_get_reg8(mem_buf, env->r[n]); + } + + /* SREG */ + if (n =3D=3D 32) { + uint8_t sreg =3D cpu_get_sreg(env); + + return gdb_get_reg8(mem_buf, sreg); + } + + /* SP */ + if (n =3D=3D 33) { + return gdb_get_reg16(mem_buf, env->sp & 0x0000ffff); + } + + /* PC */ + if (n =3D=3D 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 =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + /* R */ + if (n < 32) { + env->r[n] =3D *mem_buf; + return 1; + } + + /* SREG */ + if (n =3D=3D 32) { + cpu_set_sreg(env, *mem_buf); + return 1; + } + + /* SP */ + if (n =3D=3D 33) { + env->sp =3D lduw_p(mem_buf); + return 2; + } + + /* PC */ + if (n =3D=3D 34) { + env->pc_w =3D ldl_p(mem_buf) / 2; + return 4; + } + + return 0; +} diff --git a/target/avr/machine.c b/target/avr/machine.c new file mode 100644 index 0000000000..2dc3fb654d --- /dev/null +++ b/target/avr/machine.c @@ -0,0 +1,123 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "cpu.h" +#include "hw/boards.h" +#include "migration/qemu-file.h" + +static int get_sreg(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + CPUAVRState *env =3D opaque; + uint8_t sreg; + + sreg =3D 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 =3D opaque; + uint8_t sreg =3D cpu_get_sreg(env); + + qemu_put_byte(f, sreg); + return 0; +} + +static const VMStateInfo vms_sreg =3D { + .name =3D "sreg", + .get =3D get_sreg, + .put =3D put_sreg, +}; + +static int get_segment( + QEMUFile *f, void *opaque, size_t size, const VMStateField *field) +{ + uint32_t *ramp =3D opaque; + uint8_t temp; + + temp =3D qemu_get_byte(f); + *ramp =3D ((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 =3D opaque; + uint8_t temp =3D *ramp >> 16; + + qemu_put_byte(f, temp); + return 0; +} + +static const VMStateInfo vms_rampD =3D { + .name =3D "rampD", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_rampX =3D { + .name =3D "rampX", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_rampY =3D { + .name =3D "rampY", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_rampZ =3D { + .name =3D "rampZ", + .get =3D get_segment, + .put =3D put_segment, +}; +static const VMStateInfo vms_eind =3D { + .name =3D "eind", + .get =3D get_segment, + .put =3D put_segment, +}; + +const VMStateDescription vms_avr_cpu =3D { + .name =3D "cpu", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(env.pc_w, AVRCPU), + VMSTATE_UINT32(env.sp, AVRCPU), + VMSTATE_UINT32(env.skip, AVRCPU), + + VMSTATE_UINT32_ARRAY(env.r, AVRCPU, NO_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() + } +}; --=20 2.17.2 (Apple Git-113) From nobody Thu Apr 25 14:09:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1562909862; cv=none; d=zoho.com; s=zohoarc; b=gFQbMHbKA782/TrjX9Pl7MEJ9f0ovcS35xEJk3vxwL1BYwJs3oClUXS8V+7kQVvYflfJsEJUuqpYaWcJjUQ6gWXlqgpQ0rwJ4AcDisBbqkUgKdPwe+saacpilGmlZlXW5eH09YlT9v0BMKy0QHpFHVLZ4nW15SIkBxszVjOMWvE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1562909862; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=DfUPX4d+oYL58rU5/XwmCjG2LU+U0B2z9crX+xLdr20=; b=XRqHEuaJy/j3LbwRJig/Uad4vpXyh0AWdT9+pfwfW74RZ91XOrAlqSiRnuw/LNF/i+NkJY9EJqkYENF/i2n9WyEMqo4IHBdo/OWbpnuA2O2JXdcArTefcLkwkGnHd07M6j71hZdKgCirvIKcz9oGc1b9/yR5uH/g6KqbRT/sefY= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1562909862126443.59955943963166; Thu, 11 Jul 2019 22:37:42 -0700 (PDT) Received: from localhost ([::1]:46682 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFU-0005v6-F8 for importer@patchew.org; Fri, 12 Jul 2019 01:37:36 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35011) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFA-0004vb-8a for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hloF8-0007eJ-7T for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:16 -0400 Received: from mail-wr1-x441.google.com ([2a00:1450:4864:20::441]:34376) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hloF8-0007d2-05 for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:14 -0400 Received: by mail-wr1-x441.google.com with SMTP id 31so8581040wrm.1 for ; Thu, 11 Jul 2019 22:37:13 -0700 (PDT) Received: from 8c859074c0ff.ant.amazon.com.com (bzq-109-66-61-47.red.bezeqint.net. [109.66.61.47]) by smtp.gmail.com with ESMTPSA id y6sm7824009wmd.16.2019.07.11.22.37.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Jul 2019 22:37:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=DfUPX4d+oYL58rU5/XwmCjG2LU+U0B2z9crX+xLdr20=; b=Q9i5d47/jJrjsDO5Vr7IR80TjfvsCJ4TN1EmIai/h5+7cptbgwXHhe0LX4XM3aFbai 7mFXugVccm/NHRrT9Nmt/TG46SltnP6OpBxWoXLUA0ytDWSLRkSoYfqoscy38mktaN0c VyiPQxwEelzDLlM7jYsPqNIoISKM35oHacI7Qr0jHkqeWSfp4IfRVo5+cBqFulSL9YjR sqnyj1mmRh5uzdlsBhAggSAbn7HS1ZDbKN5Z33GUfqhMuOjuiPf7qdoglKi/AnfXqPZ0 awQxvAXuqTAokNnvl1o6UyE1tgfMNKphaCpYXa95qOSo0m/f9/GLQb4PLdE0LR6sqbRR hHLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=DfUPX4d+oYL58rU5/XwmCjG2LU+U0B2z9crX+xLdr20=; b=CafLWo0rXMm4s1fL4OljVmEuxqE4My4sWUQA8lqBC/9OAdiVPHerPxVjeqaa1zcBpm 7048dDKzWwbPh0dSaxEKMBG2lMf4Y1ZWHXHzmab1MlMwzDaClt3W4RLBjx55cWbq4L8h NGyLnmPwoeLaxruyxZ+qpCdm5Mg0ulm61hC6b3Xp57LxHz2sMbiNPBdp7y0qr6y5K2S0 KfIwXMxQ+f7P+nbWtoVP8C+TzqcLCo+9xwdx27TED/ltmQUy4sLtavUmw714AqQZPAhr IxrMcIgqWwXSFiHy09jyIBl1NwOccCF6Df433EiAjIyCHrZRDE7NKZd7Nn8LIgrVs7Qv CkFw== X-Gm-Message-State: APjAAAXUQ88bHoqX/5dM0JfgNuAIHTzBLAhhSZlDX21D/1nexQRWoSFf bo3T/aLM0f8ZWqpuUzoCAG72oFmoK8aGOw== X-Google-Smtp-Source: APXvYqwNrhoco9Y1OYu3z7ym4Qao0m99XTgNSDt/9Mzy0B+KZdMyUkHCtvNclcsTCUCIV5qlHUgflQ== X-Received: by 2002:adf:fe4f:: with SMTP id m15mr9048284wrs.36.1562909832482; Thu, 11 Jul 2019 22:37:12 -0700 (PDT) From: Michael Rolnik To: qemu-devel@nongnu.org Date: Fri, 12 Jul 2019 08:36:59 +0300 Message-Id: <20190712053704.21228-3-mrolnik@gmail.com> X-Mailer: git-send-email 2.17.2 (Apple Git-113) In-Reply-To: <20190712053704.21228-1-mrolnik@gmail.com> References: <20190712053704.21228-1-mrolnik@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::441 Subject: [Qemu-devel] [PATCH v26 2/7] target/avr: Add instruction helpers X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: imammedo@redhat.com, Sarah Harris , Michael Rolnik , richard.henderson@linaro.org, philmd@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Sarah Harris Stubs for unimplemented instructions and helpers for instructions that need= to interact with QEMU. SPM and WDR are unimplemented because they require emulation of complex per= ipherals. The implementation of SLEEP is very limited due to the lack of peripherals = to generate wake interrupts. Memory access instructions are implemented here because some address ranges= actually refer to CPU registers. Signed-off-by: Michael Rolnik --- target/avr/helper.c | 354 ++++++++++++++++++++++++++++++++++++++++++++ target/avr/helper.h | 29 ++++ 2 files changed, 383 insertions(+) create mode 100644 target/avr/helper.c create mode 100644 target/avr/helper.h diff --git a/target/avr/helper.c b/target/avr/helper.c new file mode 100644 index 0000000000..f0f0d4f15a --- /dev/null +++ b/target/avr/helper.c @@ -0,0 +1,354 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "exec/ioport.h" +#include "qemu/host-utils.h" +#include "qemu/error-report.h" + +bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + bool ret =3D false; + CPUClass *cc =3D CPU_GET_CLASS(cs); + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + if (interrupt_request & CPU_INTERRUPT_RESET) { + if (cpu_interrupts_enabled(env)) { + cs->exception_index =3D EXCP_RESET; + cc->do_interrupt(cs); + + cs->interrupt_request &=3D ~CPU_INTERRUPT_RESET; + + ret =3D true; + } + } + if (interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_interrupts_enabled(env) && env->intsrc !=3D 0) { + int index =3D ctz32(env->intsrc); + cs->exception_index =3D EXCP_INT(index); + cc->do_interrupt(cs); + + env->intsrc &=3D env->intsrc - 1; /* clear the interrupt */ + cs->interrupt_request &=3D ~CPU_INTERRUPT_HARD; + + ret =3D true; + } + } + return ret; +} + +void avr_cpu_do_interrupt(CPUState *cs) +{ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + + uint32_t ret =3D env->pc_w; + int vector =3D 0; + int size =3D avr_feature(env, AVR_FEATURE_JMP_CALL) ? 2 : 1; + int base =3D 0; + + if (cs->exception_index =3D=3D EXCP_RESET) { + vector =3D 0; + } else if (env->intsrc !=3D 0) { + vector =3D 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 =3D base + vector * size; + env->sregI =3D 0; /* clear Global Interrupt Flag */ + + cs->exception_index =3D -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 */ +} + +int avr_cpu_handle_mmu_fault( + CPUState *cs, vaddr address, int size, int rw, int mmu_idx) +{ + /* currently it's assumed that this will never happen */ + cs->exception_index =3D EXCP_DEBUG; + cpu_dump_state(cs, stderr, 0); + return 1; +} + +bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr) +{ + int prot =3D 0; + MemTxAttrs attrs =3D {}; + uint32_t paddr; + + address &=3D TARGET_PAGE_MASK; + + if (mmu_idx =3D=3D MMU_CODE_IDX) { + /* access to code in flash */ + paddr =3D OFFSET_CODE + address; + prot =3D PAGE_READ | PAGE_EXEC; + if (paddr + TARGET_PAGE_SIZE > OFFSET_DATA) { + error_report("execution left flash memory"); + exit(1); + } + } else if (address < NO_CPU_REGISTERS + NO_IO_REGISTERS) { + /* + * access to CPU registers, exit and rebuilt this TB to use full a= ccess + * incase it touches specially handled registers like SREG or SP + */ + AVRCPU *cpu =3D AVR_CPU(cs); + CPUAVRState *env =3D &cpu->env; + env->fullacc =3D 1; + cpu_loop_exit_restore(cs, retaddr); + } else { + /* access to memory. nothing special */ + paddr =3D OFFSET_DATA + address; + prot =3D PAGE_READ | PAGE_WRITE; + } + + tlb_set_page_with_attrs( + cs, address, paddr, attrs, prot, mmu_idx, TARGET_PAGE_SIZE); + + return true; +} + +void helper_sleep(CPUAVRState *env) +{ + CPUState *cs =3D env_cpu(env); + + cs->exception_index =3D EXCP_HLT; + cpu_loop_exit(cs); +} + +void helper_unsupported(CPUAVRState *env) +{ + CPUState *cs =3D env_cpu(env); + + /* + * I count not find what happens on the real platform, so + * it's EXCP_DEBUG for meanwhile + */ + cs->exception_index =3D EXCP_DEBUG; + if (qemu_loglevel_mask(LOG_UNIMP)) { + qemu_log("UNSUPPORTED\n"); + cpu_dump_state(cs, qemu_logfile, 0); + } + cpu_loop_exit(cs); +} + +void helper_debug(CPUAVRState *env) +{ + CPUState *cs =3D env_cpu(env); + + cs->exception_index =3D EXCP_DEBUG; + cpu_loop_exit(cs); +} + +void helper_break(CPUAVRState *env) +{ + CPUState *cs =3D env_cpu(env); + + cs->exception_index =3D EXCP_DEBUG; + cpu_loop_exit(cs); +} + +void helper_wdr(CPUAVRState *env) +{ + CPUState *cs =3D env_cpu(env); + + /* WD is not implemented yet, placeholder */ + cs->exception_index =3D 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 memo= ry + * 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 =3D 0; + + switch (port) { + case 0x38: /* RAMPD */ + data =3D 0xff & (env->rampD >> 16); + break; + case 0x39: /* RAMPX */ + data =3D 0xff & (env->rampX >> 16); + break; + case 0x3a: /* RAMPY */ + data =3D 0xff & (env->rampY >> 16); + break; + case 0x3b: /* RAMPZ */ + data =3D 0xff & (env->rampZ >> 16); + break; + case 0x3c: /* EIND */ + data =3D 0xff & (env->eind >> 16); + break; + case 0x3d: /* SPL */ + data =3D env->sp & 0x00ff; + break; + case 0x3e: /* SPH */ + data =3D env->sp >> 8; + break; + case 0x3f: /* SREG */ + data =3D cpu_get_sreg(env); + break; + default: + /* not a special register, pass to normal memory access */ + cpu_physical_memory_read(OFFSET_IO_REGISTERS + port, &data, 1); + } + + 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 re= gister + * b. otherwise io address is translated to mem address and physical mem= ory + * is written. + * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementati= on + * + */ +void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data) +{ + data &=3D 0x000000ff; + + switch (port) { + case 0x38: /* RAMPD */ + if (avr_feature(env, AVR_FEATURE_RAMPD)) { + env->rampD =3D (data & 0xff) << 16; + } + break; + case 0x39: /* RAMPX */ + if (avr_feature(env, AVR_FEATURE_RAMPX)) { + env->rampX =3D (data & 0xff) << 16; + } + break; + case 0x3a: /* RAMPY */ + if (avr_feature(env, AVR_FEATURE_RAMPY)) { + env->rampY =3D (data & 0xff) << 16; + } + break; + case 0x3b: /* RAMPZ */ + if (avr_feature(env, AVR_FEATURE_RAMPZ)) { + env->rampZ =3D (data & 0xff) << 16; + } + break; + case 0x3c: /* EIDN */ + env->eind =3D (data & 0xff) << 16; + break; + case 0x3d: /* SPL */ + env->sp =3D (env->sp & 0xff00) | (data); + break; + case 0x3e: /* SPH */ + if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) { + env->sp =3D (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 */ + cpu_physical_memory_write(OFFSET_IO_REGISTERS + port, &data, 1); + } +} + +/* + * 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 =3D false; + + if (addr < NO_CPU_REGISTERS) { + /* CPU registers */ + data =3D env->r[addr]; + } else if (addr < NO_CPU_REGISTERS + NO_IO_REGISTERS) { + /* IO registers */ + data =3D helper_inb(env, addr - NO_CPU_REGISTERS); + } else { + /* memory */ + cpu_physical_memory_read(OFFSET_DATA + addr, &data, 1); + } + 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 =3D false; + + /* Following logic assumes this: */ + assert(OFFSET_CPU_REGISTERS =3D=3D OFFSET_DATA); + assert(OFFSET_IO_REGISTERS =3D=3D OFFSET_CPU_REGISTERS + NO_CPU_REGIST= ERS); + + if (addr < NO_CPU_REGISTERS) { + /* CPU registers */ + env->r[addr] =3D data; + } else if (addr < NO_CPU_REGISTERS + NO_IO_REGISTERS) { + /* IO registers */ + helper_outb(env, addr - NO_CPU_REGISTERS, data); + } else { + /* memory */ + cpu_physical_memory_write(OFFSET_DATA + addr, &data, 1); + } +} diff --git a/target/avr/helper.h b/target/avr/helper.h new file mode 100644 index 0000000000..bf087504a8 --- /dev/null +++ b/target/avr/helper.h @@ -0,0 +1,29 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +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) --=20 2.17.2 (Apple Git-113) From nobody Thu Apr 25 14:09:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1562909880; cv=none; d=zoho.com; s=zohoarc; b=oXmo2yn7dt9WpBnD/zoKX5F4CrZiMGkdwcghRvxQ++F1GTqiBpgkgcvUfXnCVXryWrXM6jhdeBU37LW3XjzEKvJ2M8AOK/XvK4m4nTohvpMr/Q53fk3AH27smOoVN0uHjHDJaAG+x5hmKXYG5+n3GDIfyMMIZU3XUaXIkKBMCRg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1562909880; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=oElsIV9jGIkcznG1hjJ8A0RR90Xk82sVn+s7o2gFZVw=; b=QEDi8L18ufBLl4yPBIkhp4kcTgjqxHj1zOFyps1NIbogIEoe7MO10xFTR6ttVKDIqZ+fPhu450TYyrRKQo69VdpM3DtzSk0R6dBSCuRXkQX3dzo3DCpbyvnmUuybpvPxvpoi/AF+AILot54qh45HHubADZ3sZXrS/wRryzgxDY8= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1562909880954818.6453791555184; Thu, 11 Jul 2019 22:38:00 -0700 (PDT) Received: from localhost ([::1]:46696 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFr-0007ez-Mz for importer@patchew.org; Fri, 12 Jul 2019 01:37:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35019) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFA-0004vc-Ux for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hloF9-0007fM-J1 for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:16 -0400 Received: from mail-wr1-x441.google.com ([2a00:1450:4864:20::441]:44610) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hloF9-0007ea-CT for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:15 -0400 Received: by mail-wr1-x441.google.com with SMTP id p17so8523889wrf.11 for ; Thu, 11 Jul 2019 22:37:15 -0700 (PDT) Received: from 8c859074c0ff.ant.amazon.com.com (bzq-109-66-61-47.red.bezeqint.net. [109.66.61.47]) by smtp.gmail.com with ESMTPSA id y6sm7824009wmd.16.2019.07.11.22.37.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Jul 2019 22:37:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=oElsIV9jGIkcznG1hjJ8A0RR90Xk82sVn+s7o2gFZVw=; b=T+Kg6jjQgNoAsjpznTiqzNLZnhX2sh8am2e5PZNRo1UErN/2zOA+aKsE8VZ4oWmZiS 8q2QBTnzqa2BGab1CzMp8ffdzbiWrlwQR9kyXA16/0Eyeh37WPxJzCvZnqCuAADu71Y9 kM02+IjZBScW5aR+cFI6I/VCrTIgN2Qhw5oVe2Dxkshe2e6SG9m0DnH89xoeOLVCfNUD 2svovkVrlq07/pxR013NNTgPl7jVFv0ByEqzhbGHMgWWnYXR8XApnrclGoW4BpxxYblt qm2HXMdOTwGUfDoiKZQ0y8b8VDeE26OSxVo9c0/rziC4gsa/P/nGPBMGWSlWXhxiu87v x8xA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=oElsIV9jGIkcznG1hjJ8A0RR90Xk82sVn+s7o2gFZVw=; b=JCJgRwY01hc77XBDdn7lZrKHusMmGbVvfv+CDBxsnO0maWF0z5dn5AM0htmmgVKyQ5 TooNffSCNsWW/2aXJeHrnRQcdLn6o1bwPg2bLplu04oAzNTIaUy1yW3HkaOzn9b7StY/ N8fbpA5oyqXENZNPHuXDL5NARzuTsErGSd1WMsut1TRBnP5+M2TaD+Do9PIw3Pscxqs6 v5iQKD458GknOTQtBe39neSTfJO14MHjZGiTyW5KsgMQy1iiyt9Eo7Zt4itud2vpoVVf anFMy/ougPK3AYzLnP1AW7UFHxtFg7Hw7LNUJDvumjilz6WVhDT+ihIYB8Q2Q1QG0O+J NQHA== X-Gm-Message-State: APjAAAUQ8T2poF6Wzd3UFn8hPd/HzR3PMQF1ol0dJ2sqM/nZOV7I9poo UiYz46hKLx5OmAjOHJyE/ZjbOf+K/j0= X-Google-Smtp-Source: APXvYqykxm0pzAbSduPjRrdwiFRJRNpd3Yw3Rk8rXtd7gZms/eqmiLiHV3tFqVeK+T8WxM+/amCjNQ== X-Received: by 2002:adf:ea8b:: with SMTP id s11mr8945983wrm.100.1562909833975; Thu, 11 Jul 2019 22:37:13 -0700 (PDT) From: Michael Rolnik To: qemu-devel@nongnu.org Date: Fri, 12 Jul 2019 08:37:00 +0300 Message-Id: <20190712053704.21228-4-mrolnik@gmail.com> X-Mailer: git-send-email 2.17.2 (Apple Git-113) In-Reply-To: <20190712053704.21228-1-mrolnik@gmail.com> References: <20190712053704.21228-1-mrolnik@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::441 Subject: [Qemu-devel] [PATCH v26 3/7] target/avr: Add instruction decoding X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: imammedo@redhat.com, Michael Rolnik , richard.henderson@linaro.org, philmd@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This includes: - encoding of all 16 bit instructions - encoding of all 32 bit instructions Signed-off-by: Michael Rolnik --- target/avr/insn.decode | 175 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 target/avr/insn.decode diff --git a/target/avr/insn.decode b/target/avr/insn.decode new file mode 100644 index 0000000000..6b387762c6 --- /dev/null +++ b/target/avr/insn.decode @@ -0,0 +1,175 @@ +# +# A =3D [16 .. 31] +# B =3D [16 .. 23] +# C =3D [24, 26, 28, 30] +# D =3D [0, 2, 4, 6, 8, .. 30] + +%rd 4:5 +%rr 9:1 0:4 + +&rd_rr rd rr +&rd_imm rd imm + +@op_rd_rr .... .. . ..... .... &rd_rr rd=3D%rd rr=3D%rr +ADD 0000 11 . ..... .... @op_rd_rr +ADC 0001 11 . ..... .... @op_rd_rr +AND 0010 00 . ..... .... @op_rd_rr +CP 0001 01 . ..... .... @op_rd_rr +CPC 0000 01 . ..... .... @op_rd_rr +CPSE 0001 00 . ..... .... @op_rd_rr +EOR 0010 01 . ..... .... @op_rd_rr +MOV 0010 11 . ..... .... @op_rd_rr +MUL 1001 11 . ..... .... @op_rd_rr +OR 0010 10 . ..... .... @op_rd_rr +SBC 0000 10 . ..... .... @op_rd_rr +SUB 0001 10 . ..... .... @op_rd_rr + + +%rd_c 4:2 !function=3Dto_C +%imm6 6:2 0:4 + +@op_rd_imm6 .... .... .. .. .... &rd_imm rd=3D%rd_c imm=3D%= imm6 +ADIW 1001 0110 .. .. .... @op_rd_imm6 +SBIW 1001 0111 .. .. .... @op_rd_imm6 + + +%rd_a 4:4 !function=3Dto_A +%rr_a 0:4 !function=3Dto_A +%rd_d 4:4 !function=3Dto_D +%rr_d 0:4 !function=3Dto_D +%imm8 8:4 0:4 + +@op_rd_imm8 .... .... .... .... &rd_imm rd=3D%rd_a imm=3D%= imm8 +ANDI 0111 .... .... .... @op_rd_imm8 +CPI 0011 .... .... .... @op_rd_imm8 +LDI 1110 .... .... .... @op_rd_imm8 +ORI 0110 .... .... .... @op_rd_imm8 +SBCI 0100 .... .... .... @op_rd_imm8 +SUBI 0101 .... .... .... @op_rd_imm8 + + +@op_rd .... ... rd:5 .... +ASR 1001 010 ..... 0101 @op_rd +COM 1001 010 ..... 0000 @op_rd +DEC 1001 010 ..... 1010 @op_rd +ELPM2 1001 000 ..... 0110 @op_rd +ELPMX 1001 000 ..... 0111 @op_rd +INC 1001 010 ..... 0011 @op_rd +LDX1 1001 000 ..... 1100 @op_rd +LDX2 1001 000 ..... 1101 @op_rd +LDX3 1001 000 ..... 1110 @op_rd +LDY2 1001 000 ..... 1001 @op_rd +LDY3 1001 000 ..... 1010 @op_rd +LDZ2 1001 000 ..... 0001 @op_rd +LDZ3 1001 000 ..... 0010 @op_rd +LPM2 1001 000 ..... 0100 @op_rd +LPMX 1001 000 ..... 0101 @op_rd +LSR 1001 010 ..... 0110 @op_rd +NEG 1001 010 ..... 0001 @op_rd +POP 1001 000 ..... 1111 @op_rd +PUSH 1001 001 ..... 1111 @op_rd +ROR 1001 010 ..... 0111 @op_rd +STY2 1001 001 ..... 1001 @op_rd +STY3 1001 001 ..... 1010 @op_rd +STZ2 1001 001 ..... 0001 @op_rd +STZ3 1001 001 ..... 0010 @op_rd +SWAP 1001 010 ..... 0010 @op_rd + + +@op_bit .... .... . bit:3 .... +BCLR 1001 0100 1 ... 1000 @op_bit +BSET 1001 0100 0 ... 1000 @op_bit + + +@op_rd_bit .... ... rd:5 . bit:3 +BLD 1111 100 ..... 0 ... @op_rd_bit +BST 1111 101 ..... 0 ... @op_rd_bit + + +@op_bit_imm .... .. imm:s7 bit:3 +BRBC 1111 01 ....... ... @op_bit_imm +BRBS 1111 00 ....... ... @op_bit_imm + + +BREAK 1001 0101 1001 1000 +EICALL 1001 0101 0001 1001 +EIJMP 1001 0100 0001 1001 +ELPM1 1001 0101 1101 1000 +ICALL 1001 0101 0000 1001 +IJMP 1001 0100 0000 1001 +LPM1 1001 0101 1100 1000 +NOP 0000 0000 0000 0000 +RET 1001 0101 0000 1000 +RETI 1001 0101 0001 1000 +SLEEP 1001 0101 1000 1000 +SPM 1001 0101 1110 1000 +SPMX 1001 0101 1111 1000 +WDR 1001 0101 1010 1000 + + +@op_reg_bit .... .... reg:5 bit:3 +CBI 1001 1000 ..... ... @op_reg_bit +SBI 1001 1010 ..... ... @op_reg_bit +SBIC 1001 1001 ..... ... @op_reg_bit +SBIS 1001 1011 ..... ... @op_reg_bit + + +DES 1001 0100 imm:4 1011 + + +%rd_b 4:3 !function=3Dto_B +%rr_b 0:3 !function=3Dto_B +@fmul .... .... . ... . ... &rd_rr rd=3D%rd_b rr=3D%r= r_b +FMUL 0000 0011 0 ... 1 ... @fmul +FMULS 0000 0011 1 ... 0 ... @fmul +FMULSU 0000 0011 1 ... 1 ... @fmul +MULSU 0000 0011 0 ... 0 ... @fmul + + +%io_imm 9:2 0:4 +@io_rd_imm .... . .. ..... .... &rd_imm rd=3D%rd imm=3D%io= _imm +IN 1011 0 .. ..... .... @io_rd_imm +OUT 1011 1 .. ..... .... @io_rd_imm + + +XCH 1001 001 rd:5 0100 +LAC 1001 001 rd:5 0110 +LAS 1001 001 rd:5 0101 +LAT 1001 001 rd:5 0111 +STX1 1001 001 rr:5 1100 +STX2 1001 001 rr:5 1101 +STX3 1001 001 rr:5 1110 + + +%ldst_d_imm 13:1 10:2 0:3 +@ldst_d .. . . .. . rd:5 . ... &rd_imm imm=3D%ldst_d_imm +LDDY 10 . 0 .. 0 ..... 1 ... @ldst_d +LDDZ 10 . 0 .. 0 ..... 0 ... @ldst_d +STDY 10 . 0 .. 1 ..... 1 ... @ldst_d +STDZ 10 . 0 .. 1 ..... 0 ... @ldst_d + + +MOVW 0000 0001 .... .... &rd_rr rd=3D%rd_d rr=3D%r= r_d +MULS 0000 0010 .... .... &rd_rr rd=3D%rd_a rr=3D%r= r_a + +RCALL 1101 imm:s12 +RJMP 1100 imm:s12 + +SBRC 1111 110 rr:5 0 bit:3 +SBRS 1111 111 rr:5 0 bit:3 + +# 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=3Dappend_16 +CALL 1001 010 ..... 111 . imm=3D%imm_call +JMP 1001 010 ..... 110 . imm=3D%imm_call + + +# 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=3D0 +LDS 1001 000 ..... 0000 @ldst_s +STS 1001 001 ..... 0000 @ldst_s --=20 2.17.2 (Apple Git-113) From nobody Thu Apr 25 14:09:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1562909917; cv=none; d=zoho.com; s=zohoarc; b=DCSnxYs6ogIfSv8VVGNsDqPnwnHw+tGv5zH4AXVM11450kVMrsmX6D7Xy9DQdHtsdvw9nqRfBMW/ofYy/6XsNW44FjbwYxOuLpT/f2h4uGmh0WofVXFzGVyUUYQK4mShF4ksS4ik1z+jwO/05YHPafDhC+/HFhoyySCfbUwDw54= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1562909917; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=QRVv/yFPxv+HPUb40iKmISBavNJC7O7DoPnBJh6kT5g=; b=iuzvOnOm05oD9b9OOfC1VwsVGZrMTM4rPXhbi4i1fqi8T00pA+n09+ptL2xvR9DaRHnR4kWYMWpWCL20mqqVwJAAaqXik4k83zP8YseEh254TTxlKULa3kNVnS8vUrdJG86CZSyqwaDwTsDqM6zyVi0kxyGbxxYwFvHFscvhpoo= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1562909917489990.8234852585059; Thu, 11 Jul 2019 22:38:37 -0700 (PDT) Received: from localhost ([::1]:46714 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloGS-0001Zc-Ap for importer@patchew.org; Fri, 12 Jul 2019 01:38:36 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35114) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFM-0005jE-Ov for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:36 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hloFE-0007hy-9l for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:28 -0400 Received: from mail-wr1-x441.google.com ([2a00:1450:4864:20::441]:37507) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hloFD-0007hC-QE for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:20 -0400 Received: by mail-wr1-x441.google.com with SMTP id n9so8565056wrr.4 for ; Thu, 11 Jul 2019 22:37:19 -0700 (PDT) Received: from 8c859074c0ff.ant.amazon.com.com (bzq-109-66-61-47.red.bezeqint.net. [109.66.61.47]) by smtp.gmail.com with ESMTPSA id y6sm7824009wmd.16.2019.07.11.22.37.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Jul 2019 22:37:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=QRVv/yFPxv+HPUb40iKmISBavNJC7O7DoPnBJh6kT5g=; b=t3l4fwTGUPC/ZaH4oiB2qO/e7ZL7dhWi07EWm4fZMw8chtHGl1IqFZUTcVGETczxj+ 5FucrcFOXRmMsHC1kNHO5SgDDCzSUgkw+4OmofeIQ4JlmjaRF2oiIaCVf7AmUFqqNVTv XHgXjcBtGinf17MrNKYM68nInOZ5+CpARhTziLYv/7d23jY+BKBMdw2x166ETLmRN5eb PFcpisWOxgbx5bKcHx6Npvk8e53lFhKs2P1EZwfRTb1fhTBB8ZuqGRMqQ6EB9lDoRKGN K9Mu+q/cdlQMGrBq4x1Yl+Ji2Q5lTodNCbGqhhffHQGC6gdz7mNAwAC7EvRFENfJYIl3 ivTg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=QRVv/yFPxv+HPUb40iKmISBavNJC7O7DoPnBJh6kT5g=; b=CbnQ1YX1AMxSSVpvpeUJ8LbiRG9Do1zoz8S5XpUB3+KNmTzvIVe93epvgJUu5Ekjmb E8xA5R00gf5LUgBSE0cJvQJacUgqSDoy8nKiAeQcj24yFyEEH1o/+8p3JJqyx+tzBKz2 /JWX1mVML+RpWB4v7ORHrmXwXr+FoPyIQOZr6zI2RLXDr6Qi5SGHWyTi+CvDmhRMg1s3 24vpxSrbtJQfiOJBQcHzXOKYbb7Q1Xd4Etaln3v1PG5rdw223tfffG4asc4EULlVVPm0 yfYMbh5l/QF4sbxtRq+szZQrThQs5G1SRmWShMzCFlWROhHIXIEPFvjl/iWSRV43R+jK vJ0A== X-Gm-Message-State: APjAAAUNAkbNJf1l69pZsnJVMLKeZD5Df5iV5rp/Bs9blWbARJ7pOHVt jQreNIH/v3GmQZOx5vHNRuKdokAOlIw= X-Google-Smtp-Source: APXvYqxluJDXUpCNYnOI5jnztNXGB/0soaT8SOIrh+Gco0M15Xg5qXLpbm2VxbPIUUcomvEMgpPpCw== X-Received: by 2002:a5d:4cc5:: with SMTP id c5mr8636020wrt.278.1562909837523; Thu, 11 Jul 2019 22:37:17 -0700 (PDT) From: Michael Rolnik To: qemu-devel@nongnu.org Date: Fri, 12 Jul 2019 08:37:01 +0300 Message-Id: <20190712053704.21228-5-mrolnik@gmail.com> X-Mailer: git-send-email 2.17.2 (Apple Git-113) In-Reply-To: <20190712053704.21228-1-mrolnik@gmail.com> References: <20190712053704.21228-1-mrolnik@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::441 Subject: [Qemu-devel] [PATCH v26 4/7] target/avr: Add instruction translation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: imammedo@redhat.com, Michael Rolnik , richard.henderson@linaro.org, philmd@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This includes: - TCG translations for each instruction Signed-off-by: Michael Rolnik --- target/avr/translate.c | 2888 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2888 insertions(+) create mode 100644 target/avr/translate.c diff --git a/target/avr/translate.c b/target/avr/translate.c new file mode 100644 index 0000000000..42cb4a690c --- /dev/null +++ b/target/avr/translate.c @@ -0,0 +1,2888 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +#include "qemu/osdep.h" +#include "qemu/qemu-print.h" +#include "tcg/tcg.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" +#include "exec/gdbstub.h" +#include "exec/translator.h" + +/* + * Define if you want a BREAK instruction translated to a breakpoint + * Active debugging connection is assumed + * This is for + * https://github.com/seharris/qemu-avr-tests/tree/master/instruction-tes= ts + * tests + */ +#undef BREAKPOINT_ON_BREAK + +static TCGv cpu_pc; + +static TCGv cpu_Cf; +static TCGv cpu_Zf; +static TCGv cpu_Nf; +static TCGv cpu_Vf; +static TCGv cpu_Sf; +static TCGv cpu_Hf; +static TCGv cpu_Tf; +static TCGv cpu_If; + +static TCGv cpu_rampD; +static TCGv cpu_rampX; +static TCGv cpu_rampY; +static TCGv cpu_rampZ; + +static TCGv cpu_r[NO_CPU_REGISTERS]; +static TCGv cpu_eind; +static TCGv cpu_sp; + +static TCGv cpu_skip; + +static const char reg_names[NO_CPU_REGISTERS][8] =3D { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", +}; +#define REG(x) (cpu_r[x]) + +enum { + DISAS_EXIT =3D DISAS_TARGET_0, /* We want return to the cpu main lo= op. */ + DISAS_LOOKUP =3D DISAS_TARGET_1, /* We have a variable condition exit= . */ + DISAS_CHAIN =3D DISAS_TARGET_2, /* We have a single condition exit. = */ +}; + +typedef struct DisasContext DisasContext; + +/* This is the state at translation time. */ +struct DisasContext { + TranslationBlock *tb; + + CPUAVRState *env; + CPUState *cs; + + target_long npc; + uint32_t opcode; + + /* Routine used to access memory */ + int memidx; + int bstate; + int singlestep; + + TCGv skip_var0; + TCGv skip_var1; + TCGCond skip_cond; + bool free_skip_var0; +}; + +static int to_A(DisasContext *ctx, int indx) { return 16 + (indx % 16); } +static int to_B(DisasContext *ctx, int indx) { return 16 + (indx % 8); } +static int to_C(DisasContext *ctx, int indx) { return 24 + (indx % 4) * 2;= } +static int to_D(DisasContext *ctx, int indx) { return (indx % 16) * 2; } + +static uint16_t next_word(DisasContext *ctx) +{ + return cpu_lduw_code(ctx->env, ctx->npc++ * 2); +} + +static int append_16(DisasContext *ctx, int x) +{ + return x << 16 | next_word(ctx); +} + +static bool decode_insn(DisasContext *ctx, uint16_t insn); +#include "decode_insn.inc.c" + +static bool avr_have_feature(DisasContext *ctx, int feature) +{ + if (!avr_feature(ctx->env, feature)) { + gen_helper_unsupported(cpu_env); + ctx->bstate =3D DISAS_NORETURN; + return false; + } + return true; +} + +static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +{ + TranslationBlock *tb =3D ctx->tb; + + if (ctx->singlestep =3D=3D 0) { + tcg_gen_goto_tb(n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb(tb, n); + } else { + tcg_gen_movi_i32(cpu_pc, dest); + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(NULL, 0); + } + ctx->bstate =3D DISAS_NORETURN; +} + +#include "exec/gen-icount.h" + +static void gen_add_CHf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + TCGv t3 =3D tcg_temp_new_i32(); + + tcg_gen_and_tl(t1, Rd, Rr); /* t1 =3D Rd & Rr */ + tcg_gen_andc_tl(t2, Rd, R); /* t2 =3D Rd & ~R */ + tcg_gen_andc_tl(t3, Rr, R); /* t3 =3D Rr & ~R */ + tcg_gen_or_tl(t1, t1, t2); /* t1 =3D t1 | t2 | t3 */ + tcg_gen_or_tl(t1, t1, t3); + + tcg_gen_shri_tl(cpu_Cf, t1, 7); /* Cf =3D t1(7) */ + tcg_gen_shri_tl(cpu_Hf, t1, 3); /* Hf =3D t1(3) */ + tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_add_Vf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + + /* t1 =3D Rd & Rr & ~R | ~Rd & ~Rr & R =3D (Rd ^ R) & ~(Rd ^ Rr) */ + tcg_gen_xor_tl(t1, Rd, R); + tcg_gen_xor_tl(t2, Rd, Rr); + tcg_gen_andc_tl(t1, t1, t2); + + tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf =3D t1(7) */ + + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_sub_CHf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + TCGv t3 =3D tcg_temp_new_i32(); + + /* Cf & Hf */ + tcg_gen_not_tl(t1, Rd); /* t1 =3D ~Rd */ + tcg_gen_and_tl(t2, t1, Rr); /* t2 =3D ~Rd & Rr */ + tcg_gen_or_tl(t3, t1, Rr); /* t3 =3D (~Rd | Rr) & R */ + tcg_gen_and_tl(t3, t3, R); + tcg_gen_or_tl(t2, t2, t3); /* t2 =3D ~Rd & Rr | ~Rd & R | R & Rr */ + tcg_gen_shri_tl(cpu_Cf, t2, 7); /* Cf =3D t2(7) */ + tcg_gen_shri_tl(cpu_Hf, t2, 3); /* Hf =3D t2(3) */ + tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_sub_Vf(TCGv R, TCGv Rd, TCGv Rr) +{ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv t2 =3D tcg_temp_new_i32(); + + /* Vf */ + /* t1 =3D Rd & ~Rr & ~R | ~Rd & Rr & R =3D (Rd ^ R) & (Rd ^ R) */ + tcg_gen_xor_tl(t1, Rd, R); + tcg_gen_xor_tl(t2, Rd, Rr); + tcg_gen_and_tl(t1, t1, t2); + tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf =3D t1(7) */ + + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_rshift_ZNVSf(TCGv R) +{ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf =3D R(7) */ + tcg_gen_xor_tl(cpu_Vf, cpu_Nf, cpu_Cf); + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ +} + +static void gen_NSf(TCGv R) +{ + tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf =3D R(7) */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ +} + +static void gen_ZNSf(TCGv R) +{ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf =3D R(7) */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ +} + +static void gen_push_ret(DisasContext *ctx, int ret) +{ + if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) { + + TCGv t0 =3D tcg_const_i32((ret & 0x0000ff)); + + tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_UB); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + tcg_temp_free_i32(t0); + } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) { + + TCGv t0 =3D tcg_const_i32((ret & 0x00ffff)); + + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_BEUW); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + tcg_temp_free_i32(t0); + + } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) { + + TCGv lo =3D tcg_const_i32((ret & 0x0000ff)); + TCGv hi =3D tcg_const_i32((ret & 0xffff00) >> 8); + + tcg_gen_qemu_st_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 2); + tcg_gen_qemu_st_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + tcg_temp_free_i32(lo); + tcg_temp_free_i32(hi); + } +} + +static void gen_pop_ret(DisasContext *ctx, TCGv ret) +{ + if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) { + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_UB); + } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) { + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_BEUW); + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) { + TCGv lo =3D tcg_temp_new_i32(); + TCGv hi =3D tcg_temp_new_i32(); + + tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + tcg_gen_qemu_ld_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW); + + tcg_gen_addi_tl(cpu_sp, cpu_sp, 2); + tcg_gen_qemu_ld_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB); + + tcg_gen_deposit_tl(ret, lo, hi, 8, 16); + + tcg_temp_free_i32(lo); + tcg_temp_free_i32(hi); + } +} + +static void gen_jmp_ez(DisasContext *ctx) +{ + tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8); + tcg_gen_or_tl(cpu_pc, cpu_pc, cpu_eind); + ctx->bstate =3D DISAS_LOOKUP; +} + +static void gen_jmp_z(DisasContext *ctx) +{ + tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8); + ctx->bstate =3D DISAS_LOOKUP; +} + +/* + * in the gen_set_addr & gen_get_addr functions + * H assumed to be in 0x00ff0000 format + * M assumed to be in 0x000000ff format + * L assumed to be in 0x000000ff format + */ +static void gen_set_addr(TCGv addr, TCGv H, TCGv M, TCGv L) +{ + + tcg_gen_andi_tl(L, addr, 0x000000ff); + + tcg_gen_andi_tl(M, addr, 0x0000ff00); + tcg_gen_shri_tl(M, M, 8); + + tcg_gen_andi_tl(H, addr, 0x00ff0000); +} + +static void gen_set_xaddr(TCGv addr) +{ + gen_set_addr(addr, cpu_rampX, cpu_r[27], cpu_r[26]); +} + +static void gen_set_yaddr(TCGv addr) +{ + gen_set_addr(addr, cpu_rampY, cpu_r[29], cpu_r[28]); +} + +static void gen_set_zaddr(TCGv addr) +{ + gen_set_addr(addr, cpu_rampZ, cpu_r[31], cpu_r[30]); +} + +static TCGv gen_get_addr(TCGv H, TCGv M, TCGv L) +{ + TCGv addr =3D tcg_temp_new_i32(); + + tcg_gen_deposit_tl(addr, M, H, 8, 8); + tcg_gen_deposit_tl(addr, L, addr, 8, 16); + + return addr; +} + +static TCGv gen_get_xaddr(void) +{ + return gen_get_addr(cpu_rampX, cpu_r[27], cpu_r[26]); +} + +static TCGv gen_get_yaddr(void) +{ + return gen_get_addr(cpu_rampY, cpu_r[29], cpu_r[28]); +} + +static TCGv gen_get_zaddr(void) +{ + return gen_get_addr(cpu_rampZ, cpu_r[31], cpu_r[30]); +} + +/* + * Adds two registers and the contents of the C Flag and places the resul= t in + * the destination register Rd. + */ +static bool trans_ADC(DisasContext *ctx, arg_ADC *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_add_tl(R, Rd, Rr); /* R =3D Rd + Rr + Cf */ + tcg_gen_add_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_add_CHf(R, Rd, Rr); + gen_add_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * Adds two registers without the C Flag and places the result in the + * destination register Rd. + */ +static bool trans_ADD(DisasContext *ctx, arg_ADD *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_add_tl(R, Rd, Rr); /* Rd =3D Rd + Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_add_CHf(R, Rd, Rr); + gen_add_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * Adds an immediate value (0 - 63) to a register pair and places the res= ult + * in the register pair. This instruction operates on the upper four regi= ster + * pairs, and is well suited for operations on the pointer registers. Th= is + * instruction is not available in all devices. Refer to the device speci= fic + * instruction set summary. + */ +static bool trans_ADIW(DisasContext *ctx, arg_ADIW *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_ADIW_SBIW)) { + return true; + } + + TCGv RdL =3D cpu_r[a->rd]; + TCGv RdH =3D cpu_r[a->rd + 1]; + int Imm =3D (a->imm); + TCGv R =3D tcg_temp_new_i32(); + TCGv Rd =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd =3D RdH:RdL */ + tcg_gen_addi_tl(R, Rd, Imm); /* R =3D Rd + Imm */ + tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */ + + /* Cf */ + tcg_gen_andc_tl(cpu_Cf, Rd, R); /* Cf =3D Rd & ~R */ + tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15); + + /* Vf */ + tcg_gen_andc_tl(cpu_Vf, R, Rd); /* Vf =3D R & ~Rd */ + tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15); + + /* Zf */ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + + /* Nf */ + tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf =3D R(15) */ + + /* Sf */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf);/* Sf =3D Nf ^ Vf */ + + /* R */ + tcg_gen_andi_tl(RdL, R, 0xff); + tcg_gen_shri_tl(RdH, R, 8); + + tcg_temp_free_i32(Rd); + tcg_temp_free_i32(R); + + return true; +} + +/* + * Performs the logical AND between the contents of register Rd and regis= ter + * Rr and places the result in the destination register Rd. + */ +static bool trans_AND(DisasContext *ctx, arg_AND *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_and_tl(R, Rd, Rr); /* Rd =3D Rd and Rr */ + + /* Vf */ + tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf =3D 0 */ + + /* Zf */ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * Performs the logical AND between the contents of register Rd and a con= stant + * and places the result in the destination register Rd. + */ +static bool trans_ANDI(DisasContext *ctx, arg_ANDI *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + int Imm =3D (a->imm); + + /* op */ + tcg_gen_andi_tl(Rd, Rd, Imm); /* Rd =3D Rd & Imm */ + + tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf =3D 0 */ + gen_ZNSf(Rd); + + return true; +} + +/* + * Shifts all bits in Rd one place to the right. Bit 7 is held constant. = Bit 0 + * is loaded into the C Flag of the SREG. This operation effectively divi= des a + * signed value by two without changing its sign. The Carry Flag can be u= sed to + * round the result. + */ +static bool trans_ASR(DisasContext *ctx, arg_ASR *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv t0 =3D tcg_temp_new_i32(); + + /* Cf */ + tcg_gen_andi_tl(cpu_Cf, Rd, 1); /* Cf =3D Rd(0) */ + + /* op */ + tcg_gen_andi_tl(t0, Rd, 0x80); /* Rd =3D (Rd & 0x80) | (Rd >> 1) */ + tcg_gen_shri_tl(Rd, Rd, 1); + tcg_gen_or_tl(Rd, Rd, t0); + + gen_rshift_ZNVSf(Rd); + + tcg_temp_free_i32(t0); + + return true; +} + +/* + * Clears a single Flag in SREG. + */ +static bool trans_BCLR(DisasContext *ctx, arg_BCLR *a) +{ + switch (a->bit) { + case 0x00: + tcg_gen_movi_tl(cpu_Cf, 0x00); + break; + case 0x01: + tcg_gen_movi_tl(cpu_Zf, 0x01); + break; + case 0x02: + tcg_gen_movi_tl(cpu_Nf, 0x00); + break; + case 0x03: + tcg_gen_movi_tl(cpu_Vf, 0x00); + break; + case 0x04: + tcg_gen_movi_tl(cpu_Sf, 0x00); + break; + case 0x05: + tcg_gen_movi_tl(cpu_Hf, 0x00); + break; + case 0x06: + tcg_gen_movi_tl(cpu_Tf, 0x00); + break; + case 0x07: + tcg_gen_movi_tl(cpu_If, 0x00); + break; + } + + return true; +} + +/* + * Copies the T Flag in the SREG (Status Register) to bit b in register R= d. + */ +static bool trans_BLD(DisasContext *ctx, arg_BLD *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_andi_tl(Rd, Rd, ~(1u << a->bit)); /* clear bit */ + tcg_gen_shli_tl(t1, cpu_Tf, a->bit); /* create mask */ + tcg_gen_or_tl(Rd, Rd, t1); + + tcg_temp_free_i32(t1); + + return true; +} + +/* + * Conditional relative branch. Tests a single bit in SREG and branches + * relatively to PC if the bit is cleared. This instruction branches rela= tively + * to PC in either direction (PC - 63 < =3D destination <=3D PC + 64). The + * parameter k is the offset from PC and is represented in two's compleme= nt + * form. + */ +static bool trans_BRBC(DisasContext *ctx, arg_BRBC *a) +{ + TCGLabel *not_taken =3D gen_new_label(); + TCGCond cond =3D TCG_COND_EQ; + TCGv var; + + switch (a->bit) { + case 0x00: + var =3D cpu_Cf; + break; + case 0x01: + cond =3D TCG_COND_NE; + var =3D cpu_Zf; + break; + case 0x02: + var =3D cpu_Nf; + break; + case 0x03: + var =3D cpu_Vf; + break; + case 0x04: + var =3D cpu_Sf; + break; + case 0x05: + var =3D cpu_Hf; + break; + case 0x06: + var =3D cpu_Tf; + break; + case 0x07: + var =3D cpu_If; + break; + default: + g_assert_not_reached(); + } + + tcg_gen_brcondi_i32(tcg_invert_cond(cond), var, 0, not_taken); + gen_goto_tb(ctx, 0, ctx->npc + a->imm); + gen_set_label(not_taken); + + ctx->bstate =3D DISAS_CHAIN; + return true; +} + +/* + * Conditional relative branch. Tests a single bit in SREG and branches + * relatively to PC if the bit is set. This instruction branches relative= ly to + * PC in either direction (PC - 63 < =3D destination <=3D PC + 64). The p= arameter k + * is the offset from PC and is represented in two's complement form. + */ +static bool trans_BRBS(DisasContext *ctx, arg_BRBS *a) +{ + TCGLabel *not_taken =3D gen_new_label(); + TCGCond cond =3D TCG_COND_NE; + TCGv var; + + switch (a->bit) { + case 0x00: + var =3D cpu_Cf; + break; + case 0x01: + cond =3D TCG_COND_EQ; + var =3D cpu_Zf; + break; + case 0x02: + var =3D cpu_Nf; + break; + case 0x03: + var =3D cpu_Vf; + break; + case 0x04: + var =3D cpu_Sf; + break; + case 0x05: + var =3D cpu_Hf; + break; + case 0x06: + var =3D cpu_Tf; + break; + case 0x07: + var =3D cpu_If; + break; + default: + g_assert_not_reached(); + } + + tcg_gen_brcondi_i32(tcg_invert_cond(cond), var, 0, not_taken); + gen_goto_tb(ctx, 0, ctx->npc + a->imm); + gen_set_label(not_taken); + + ctx->bstate =3D DISAS_CHAIN; + return true; +} + +/* + * Sets a single Flag or bit in SREG. + */ +static bool trans_BSET(DisasContext *ctx, arg_BSET *a) +{ + switch (a->bit) { + case 0x00: + tcg_gen_movi_tl(cpu_Cf, 0x01); + break; + case 0x01: + tcg_gen_movi_tl(cpu_Zf, 0x00); + break; + case 0x02: + tcg_gen_movi_tl(cpu_Nf, 0x01); + break; + case 0x03: + tcg_gen_movi_tl(cpu_Vf, 0x01); + break; + case 0x04: + tcg_gen_movi_tl(cpu_Sf, 0x01); + break; + case 0x05: + tcg_gen_movi_tl(cpu_Hf, 0x01); + break; + case 0x06: + tcg_gen_movi_tl(cpu_Tf, 0x01); + break; + case 0x07: + tcg_gen_movi_tl(cpu_If, 0x01); + break; + } + + return true; +} + +/* + * The BREAK instruction is used by the On-chip Debug system, and is + * normally not used in the application software. When the BREAK instruct= ion is + * executed, the AVR CPU is set in the Stopped Mode. This gives the On-ch= ip + * Debugger access to internal resources. If any Lock bits are set, or e= ither + * the JTAGEN or OCDEN Fuses are unprogrammed, the CPU will treat the BRE= AK + * instruction as a NOP and will not enter the Stopped mode. This instru= ction + * is not available in all devices. Refer to the device specific instruct= ion + * set summary. + */ +static bool trans_BREAK(DisasContext *ctx, arg_BREAK *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_BREAK)) { + return true; + } + +#ifdef BREAKPOINT_ON_BREAK + tcg_gen_movi_tl(cpu_pc, ctx->npc - 1); + gen_helper_debug(cpu_env); + ctx->bstate =3D DISAS_EXIT; +#else + /* NOP */ +#endif + + return true; +} + +/* + * Stores bit b from Rd to the T Flag in SREG (Status Register). + */ +static bool trans_BST(DisasContext *ctx, arg_BST *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + + tcg_gen_andi_tl(cpu_Tf, Rd, 1 << a->bit); + tcg_gen_shri_tl(cpu_Tf, cpu_Tf, a->bit); + + return true; +} + +/* + * Calls to a subroutine within the entire Program memory. The return + * address (to the instruction after the CALL) will be stored onto the St= ack. + * (See also RCALL). The Stack Pointer uses a post-decrement scheme during + * CALL. This instruction is not available in all devices. Refer to the = device + * specific instruction set summary. + */ +static bool trans_CALL(DisasContext *ctx, arg_CALL *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_JMP_CALL)) { + return true; + } + + int Imm =3D a->imm; + int ret =3D ctx->npc; + + gen_push_ret(ctx, ret); + gen_goto_tb(ctx, 0, Imm); + + return true; +} + +/* + * Clears a specified bit in an I/O Register. This instruction operates on + * the lower 32 I/O Registers -- addresses 0-31. + */ +static bool trans_CBI(DisasContext *ctx, arg_CBI *a) +{ + TCGv data =3D tcg_temp_new_i32(); + TCGv port =3D tcg_const_i32(a->reg); + + gen_helper_inb(data, cpu_env, port); + tcg_gen_andi_tl(data, data, ~(1 << a->bit)); + gen_helper_outb(cpu_env, port, data); + + tcg_temp_free_i32(data); + tcg_temp_free_i32(port); + + return true; +} + +/* + * Clears the specified bits in register Rd. Performs the logical AND + * between the contents of register Rd and the complement of the constant= mask + * K. The result will be placed in register Rd. + */ +static bool trans_COM(DisasContext *ctx, arg_COM *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_xori_tl(Rd, Rd, 0xff); + + tcg_gen_movi_tl(cpu_Cf, 1); /* Cf =3D 1 */ + tcg_gen_movi_tl(cpu_Vf, 0); /* Vf =3D 0 */ + gen_ZNSf(Rd); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs a compare between two registers Rd and Rr. + * None of the registers are changed. All conditional branches can be used + * after this instruction. + */ +static bool trans_CP(DisasContext *ctx, arg_CP *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs a compare between two registers Rd and Rr and + * also takes into account the previous carry. None of the registers are + * changed. All conditional branches can be used after this instruction. + */ +static bool trans_CPC(DisasContext *ctx, arg_CPC *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr - Cf */ + tcg_gen_sub_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_NSf(R); + + /* + * Previous value remains unchanged when the result is zero; + * cleared otherwise. + */ + tcg_gen_or_tl(cpu_Zf, cpu_Zf, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs a compare between register Rd and a constant. + * The register is not changed. All conditional branches can be used afte= r this + * instruction. + */ +static bool trans_CPI(DisasContext *ctx, arg_CPI *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + int Imm =3D a->imm; + TCGv Rr =3D tcg_const_i32(Imm); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + tcg_temp_free_i32(R); + tcg_temp_free_i32(Rr); + + return true; +} + +/* + * This instruction performs a compare between two registers Rd and Rr, a= nd + * skips the next instruction if Rd =3D Rr. + */ +static bool trans_CPSE(DisasContext *ctx, arg_CPSE *a) +{ + ctx->skip_cond =3D TCG_COND_EQ; + ctx->skip_var0 =3D cpu_r[a->rd]; + ctx->skip_var1 =3D cpu_r[a->rr]; + return true; +} + +/* + * Subtracts one -1- from the contents of register Rd and places the resu= lt + * in the destination register Rd. The C Flag in SREG is not affected by= the + * operation, thus allowing the DEC instruction to be used on a loop coun= ter in + * multiple-precision computations. When operating on unsigned values, o= nly + * BREQ and BRNE branches can be expected to perform consistently. When + * operating on two's complement values, all signed branches are availabl= e. + */ +static bool trans_DEC(DisasContext *ctx, arg_DEC *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + + tcg_gen_subi_tl(Rd, Rd, 1); /* Rd =3D Rd - 1 */ + tcg_gen_andi_tl(Rd, Rd, 0xff); /* make it 8 bits */ + + /* cpu_Vf =3D Rd =3D=3D 0x7f */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x7f); + gen_ZNSf(Rd); + + return true; +} + +/* + * The module is an instruction set extension to the AVR CPU, performing + * DES iterations. The 64-bit data block (plaintext or ciphertext) is pla= ced in + * the CPU register file, registers R0-R7, where LSB of data is placed in= LSB + * of R0 and MSB of data is placed in MSB of R7. The full 64-bit key (inc= luding + * parity bits) is placed in registers R8- R15, organized in the register= file + * with LSB of key in LSB of R8 and MSB of key in MSB of R15. Executing o= ne DES + * instruction performs one round in the DES algorithm. Sixteen rounds mu= st be + * executed in increasing order to form the correct DES ciphertext or + * plaintext. Intermediate results are stored in the register file (R0-R1= 5) + * after each DES instruction. The instruction's operand (K) determines w= hich + * round is executed, and the half carry flag (H) determines whether encr= yption + * or decryption is performed. The DES algorithm is described in + * "Specifications for the Data Encryption Standard" (Federal Information + * Processing Standards Publication 46). Intermediate results in this + * implementation differ from the standard because the initial permutatio= n and + * the inverse initial permutation are performed each iteration. This doe= s not + * affect the result in the final ciphertext or plaintext, but reduces + * execution time. + */ +static bool trans_DES(DisasContext *ctx, arg_DES *a) +{ + /* TODO */ + if (!avr_have_feature(ctx, AVR_FEATURE_DES)) { + return true; + } + + return true; +} + +/* + * Indirect call of a subroutine pointed to by the Z (16 bits) Pointer + * Register in the Register File and the EIND Register in the I/O space. = This + * instruction allows for indirect calls to the entire 4M (words) Program + * memory space. See also ICALL. The Stack Pointer uses a post-decrement = scheme + * during EICALL. This instruction is not available in all devices. Refe= r to + * the device specific instruction set summary. + */ +static bool trans_EICALL(DisasContext *ctx, arg_EICALL *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_EIJMP_EICALL)) { + return true; + } + + int ret =3D ctx->npc; + + gen_push_ret(ctx, ret); + gen_jmp_ez(ctx); + return true; +} + +/* + * Indirect jump to the address pointed to by the Z (16 bits) Pointer + * Register in the Register File and the EIND Register in the I/O space. = This + * instruction allows for indirect jumps to the entire 4M (words) Program + * memory space. See also IJMP. This instruction is not available in all + * devices. Refer to the device specific instruction set summary. + */ +static bool trans_EIJMP(DisasContext *ctx, arg_EIJMP *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_EIJMP_EICALL)) { + return true; + } + + gen_jmp_ez(ctx); + return true; +} + +/* + * Loads one byte pointed to by the Z-register and the RAMPZ Register in + * the I/O space, and places this byte in the destination register Rd. Th= is + * instruction features a 100% space effective constant initialization or + * constant data fetch. The Program memory is organized in 16-bit words w= hile + * the Z-pointer is a byte address. Thus, the least significant bit of the + * Z-pointer selects either low byte (ZLSB =3D 0) or high byte (ZLSB =3D = 1). This + * instruction can address the entire Program memory space. The Z-pointer + * Register can either be left unchanged by the operation, or it can be + * incremented. The incrementation applies to the entire 24-bit concatena= tion + * of the RAMPZ and Z-pointer Registers. Devices with Self-Programming + * capability can use the ELPM instruction to read the Fuse and Lock bit = value. + * Refer to the device documentation for a detailed description. This + * instruction is not available in all devices. Refer to the device speci= fic + * instruction set summary. + */ +static bool trans_ELPM1(DisasContext *ctx, arg_ELPM1 *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_ELPM)) { + return true; + } + + TCGv Rd =3D cpu_r[0]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_ELPM2(DisasContext *ctx, arg_ELPM2 *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_ELPM)) { + return true; + } + + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_ELPMX(DisasContext *ctx, arg_ELPMX *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_ELPMX)) { + return true; + } + + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Performs the logical EOR between the contents of register Rd and + * register Rr and places the result in the destination register Rd. + */ +static bool trans_EOR(DisasContext *ctx, arg_EOR *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + + tcg_gen_xor_tl(Rd, Rd, Rr); + + tcg_gen_movi_tl(cpu_Vf, 0); + gen_ZNSf(Rd); + + return true; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit unsigned + * multiplication and shifts the result one bit left. + */ +static bool trans_FMUL(DisasContext *ctx, arg_FMUL *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) { + return true; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_mul_tl(R, Rd, Rr); /* R =3D Rd *Rr */ + tcg_gen_shli_tl(R, R, 1); + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + tcg_gen_andi_tl(R1, R1, 0xff); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_andi_tl(cpu_Zf, R, 0x0000ffff); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication + * and shifts the result one bit left. + */ +static bool trans_FMULS(DisasContext *ctx, arg_FMULS *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) { + return true; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */ + tcg_gen_mul_tl(R, t0, t1); /* R =3D Rd *Rr */ + tcg_gen_shli_tl(R, R, 1); + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + tcg_gen_andi_tl(R1, R1, 0xff); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_andi_tl(cpu_Zf, R, 0x0000ffff); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication + * and shifts the result one bit left. + */ +static bool trans_FMULSU(DisasContext *ctx, arg_FMULSU *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) { + return true; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_mul_tl(R, t0, Rr); /* R =3D Rd *Rr */ + tcg_gen_shli_tl(R, R, 1); + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + tcg_gen_andi_tl(R1, R1, 0xff); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_andi_tl(cpu_Zf, R, 0x0000ffff); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return true; +} + +/* + * Calls to a subroutine within the entire 4M (words) Program memory. The + * return address (to the instruction after the CALL) will be stored onto= the + * Stack. See also RCALL. The Stack Pointer uses a post-decrement scheme = during + * CALL. This instruction is not available in all devices. Refer to the = device + * specific instruction set summary. + */ +static bool trans_ICALL(DisasContext *ctx, arg_ICALL *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_IJMP_ICALL)) { + return true; + } + + int ret =3D ctx->npc; + + gen_push_ret(ctx, ret); + gen_jmp_z(ctx); + + return true; +} + +/* + * Indirect jump to the address pointed to by the Z (16 bits) Pointer + * Register in the Register File. The Z-pointer Register is 16 bits wide = and + * allows jump within the lowest 64K words (128KB) section of Program mem= ory. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static bool trans_IJMP(DisasContext *ctx, arg_IJMP *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_IJMP_ICALL)) { + return true; + } + + gen_jmp_z(ctx); + + return true; +} + +/* + * Loads data from the I/O Space (Ports, Timers, Configuration Registers, + * etc.) into register Rd in the Register File. + */ +static bool trans_IN(DisasContext *ctx, arg_IN *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv port =3D tcg_const_i32(a->imm); + + gen_helper_inb(Rd, cpu_env, port); + + tcg_temp_free_i32(port); + + return true; +} + +/* + * Adds one -1- to the contents of register Rd and places the result in t= he + * destination register Rd. The C Flag in SREG is not affected by the + * operation, thus allowing the INC instruction to be used on a loop coun= ter in + * multiple-precision computations. When operating on unsigned numbers, = only + * BREQ and BRNE branches can be expected to perform consistently. When + * operating on two's complement values, all signed branches are availabl= e. + */ +static bool trans_INC(DisasContext *ctx, arg_INC *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + + tcg_gen_addi_tl(Rd, Rd, 1); + tcg_gen_andi_tl(Rd, Rd, 0xff); + + /* cpu_Vf =3D Rd =3D=3D 0x80 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x80); + gen_ZNSf(Rd); + + return true; +} + +/* + * Jump to an address within the entire 4M (words) Program memory. See al= so + * RJMP. This instruction is not available in all devices. Refer to the = device + * specific instruction set summary.0 + */ +static bool trans_JMP(DisasContext *ctx, arg_JMP *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_JMP_CALL)) { + return true; + } + + gen_goto_tb(ctx, 0, a->imm); + + return true; +} + +/* + * Load one byte indirect from data space to register and stores an clear + * the bits in data space specified by the register. The instruction can = only + * be used towards internal SRAM. The data location is pointed to by the= Z (16 + * bits) Pointer Register in the Register File. Memory access is limited = to the + * current data segment of 64KB. To access another data segment in device= s with + * more than 64KB data space, the RAMPZ in register in the I/O area has t= o be + * changed. The Z-pointer Register is left unchanged by the operation. T= his + * instruction is especially suited for clearing status bits stored in SR= AM. + */ +static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) +{ + if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { + gen_helper_fullwr(cpu_env, data, addr); + } else { + tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] =3D data = */ + } +} + +static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr) +{ + if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) { + gen_helper_fullrd(data, cpu_env, addr); + } else { + tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data =3D mem[addr]= */ + } +} + +static bool trans_LAC(DisasContext *ctx, arg_LAC *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) { + return true; + } + + TCGv Rr =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + gen_data_load(ctx, t0, addr); /* t0 =3D mem[addr] */ + /* t1 =3D t0 & (0xff - Rr) =3D t0 and ~Rr */ + tcg_gen_andc_tl(t1, t0, Rr); + + tcg_gen_mov_tl(Rr, t0); /* Rr =3D t0 */ + gen_data_store(ctx, t1, addr); /* mem[addr] =3D t1 */ + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Load one byte indirect from data space to register and set bits in data + * space specified by the register. The instruction can only be used towa= rds + * internal SRAM. The data location is pointed to by the Z (16 bits) Poi= nter + * Register in the Register File. Memory access is limited to the current= data + * segment of 64KB. To access another data segment in devices with more t= han + * 64KB data space, the RAMPZ in register in the I/O area has to be chang= ed. + * The Z-pointer Register is left unchanged by the operation. This instru= ction + * is especially suited for setting status bits stored in SRAM. + */ +static bool trans_LAS(DisasContext *ctx, arg_LAS *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) { + return true; + } + + TCGv Rr =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + gen_data_load(ctx, t0, addr); /* t0 =3D mem[addr] */ + tcg_gen_or_tl(t1, t0, Rr); + + tcg_gen_mov_tl(Rr, t0); /* Rr =3D t0 */ + gen_data_store(ctx, t1, addr); /* mem[addr] =3D t1 */ + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Load one byte indirect from data space to register and toggles bits in + * the data space specified by the register. The instruction can only be= used + * towards SRAM. The data location is pointed to by the Z (16 bits) Poin= ter + * Register in the Register File. Memory access is limited to the current= data + * segment of 64KB. To access another data segment in devices with more t= han + * 64KB data space, the RAMPZ in register in the I/O area has to be chang= ed. + * The Z-pointer Register is left unchanged by the operation. This instru= ction + * is especially suited for changing status bits stored in SRAM. + */ +static bool trans_LAT(DisasContext *ctx, arg_LAT *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) { + return true; + } + + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + gen_data_load(ctx, t0, addr); /* t0 =3D mem[addr] */ + tcg_gen_xor_tl(t1, t0, Rd); + + tcg_gen_mov_tl(Rd, t0); /* Rd =3D t0 */ + gen_data_store(ctx, t1, addr); /* mem[addr] =3D t1 */ + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Loads one byte indirect from the data space to a register. For parts + * with SRAM, the data space consists of the Register File, I/O memory and + * internal SRAM (and external SRAM if applicable). For parts without SRA= M, the + * data space consists of the Register File only. In some parts the Flash + * Memory has been mapped to the data space and can be read using this co= mmand. + * The EEPROM has a separate address space. The data location is pointed= to by + * the X (16 bits) Pointer Register in the Register File. Memory access is + * limited to the current data segment of 64KB. To access another data se= gment + * in devices with more than 64KB data space, the RAMPX in register in th= e I/O + * area has to be changed. The X-pointer Register can either be left unc= hanged + * by the operation, or it can be post-incremented or predecremented. Th= ese + * features are especially suited for accessing arrays, tables, and Stack + * Pointer usage of the X-pointer Register. Note that only the low byte o= f the + * X-pointer is updated in devices with no more than 256 bytes data space= . For + * such devices, the high byte of the pointer is not used by this instruc= tion + * and can be used for other purposes. The RAMPX Register in the I/O area= is + * updated in parts with more than 64KB data space or more than 64KB Prog= ram + * memory, and the increment/decrement is added to the entire 24-bit addr= ess on + * such devices. Not all variants of this instruction is available in all + * devices. Refer to the device specific instruction set summary. In the + * Reduced Core tinyAVR the LD instruction can be used to achieve the same + * operation as LPM since the program memory is mapped to the data memory + * space. + */ +static bool trans_LDX1(DisasContext *ctx, arg_LDX1 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LDX2(DisasContext *ctx, arg_LDX2 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_load(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LDX3(DisasContext *ctx, arg_LDX3 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_xaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_load(ctx, Rd, addr); + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Loads one byte indirect with or without displacement from the data spa= ce + * to a register. For parts with SRAM, the data space consists of the Reg= ister + * File, I/O memory and internal SRAM (and external SRAM if applicable). = For + * parts without SRAM, the data space consists of the Register File only.= In + * some parts the Flash Memory has been mapped to the data space and can = be + * read using this command. The EEPROM has a separate address space. The= data + * location is pointed to by the Y (16 bits) Pointer Register in the Regi= ster + * File. Memory access is limited to the current data segment of 64KB. To + * access another data segment in devices with more than 64KB data space,= the + * RAMPY in register in the I/O area has to be changed. The Y-pointer Re= gister + * can either be left unchanged by the operation, or it can be post-incre= mented + * or predecremented. These features are especially suited for accessing + * arrays, tables, and Stack Pointer usage of the Y-pointer Register. Not= e that + * only the low byte of the Y-pointer is updated in devices with no more = than + * 256 bytes data space. For such devices, the high byte of the pointer i= s not + * used by this instruction and can be used for other purposes. The RAMPY + * Register in the I/O area is updated in parts with more than 64KB data = space + * or more than 64KB Program memory, and the increment/decrement/displace= ment + * is added to the entire 24-bit address on such devices. Not all varian= ts of + * this instruction is available in all devices. Refer to the device spec= ific + * instruction set summary. In the Reduced Core tinyAVR the LD instructi= on can + * be used to achieve the same operation as LPM since the program memory = is + * mapped to the data memory space. + */ +static bool trans_LDY2(DisasContext *ctx, arg_LDY2 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_yaddr(); + + gen_data_load(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LDY3(DisasContext *ctx, arg_LDY3 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_load(ctx, Rd, addr); + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LDDY(DisasContext *ctx, arg_LDDY *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_addi_tl(addr, addr, a->imm); /* addr =3D addr + q */ + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Loads one byte indirect with or without displacement from the data spa= ce + * to a register. For parts with SRAM, the data space consists of the Reg= ister + * File, I/O memory and internal SRAM (and external SRAM if applicable). = For + * parts without SRAM, the data space consists of the Register File only.= In + * some parts the Flash Memory has been mapped to the data space and can = be + * read using this command. The EEPROM has a separate address space. The= data + * location is pointed to by the Z (16 bits) Pointer Register in the Regi= ster + * File. Memory access is limited to the current data segment of 64KB. To + * access another data segment in devices with more than 64KB data space,= the + * RAMPZ in register in the I/O area has to be changed. The Z-pointer Re= gister + * can either be left unchanged by the operation, or it can be post-incre= mented + * or predecremented. These features are especially suited for Stack Poi= nter + * usage of the Z-pointer Register, however because the Z-pointer Registe= r can + * be used for indirect subroutine calls, indirect jumps and table lookup= , it + * is often more convenient to use the X or Y-pointer as a dedicated Stack + * Pointer. Note that only the low byte of the Z-pointer is updated in de= vices + * with no more than 256 bytes data space. For such devices, the high byt= e of + * the pointer is not used by this instruction and can be used for other + * purposes. The RAMPZ Register in the I/O area is updated in parts with = more + * than 64KB data space or more than 64KB Program memory, and the + * increment/decrement/displacement is added to the entire 24-bit address= on + * such devices. Not all variants of this instruction is available in all + * devices. Refer to the device specific instruction set summary. In the + * Reduced Core tinyAVR the LD instruction can be used to achieve the same + * operation as LPM since the program memory is mapped to the data memory + * space. For using the Z-pointer for table lookup in Program memory see= the + * LPM and ELPM instructions. + */ +static bool trans_LDZ2(DisasContext *ctx, arg_LDZ2 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + gen_data_load(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LDZ3(DisasContext *ctx, arg_LDZ3 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_load(ctx, Rd, addr); + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LDDZ(DisasContext *ctx, arg_LDDZ *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_addi_tl(addr, addr, a->imm); /* addr =3D addr + q */ + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Loads an 8 bit constant directly to register 16 to 31. + */ +static bool trans_LDI(DisasContext *ctx, arg_LDI *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + int imm =3D a->imm; + + tcg_gen_movi_tl(Rd, imm); + + return true; +} + +/* + * Loads one byte from the data space to a register. For parts with SRAM, + * the data space consists of the Register File, I/O memory and internal = SRAM + * (and external SRAM if applicable). For parts without SRAM, the data sp= ace + * consists of the register file only. The EEPROM has a separate address = space. + * A 16-bit address must be supplied. Memory access is limited to the cur= rent + * data segment of 64KB. The LDS instruction uses the RAMPD Register to a= ccess + * memory above 64KB. To access another data segment in devices with more= than + * 64KB data space, the RAMPD in register in the I/O area has to be chang= ed. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static bool trans_LDS(DisasContext *ctx, arg_LDS *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_rampD; + a->imm =3D next_word(ctx); + + tcg_gen_mov_tl(addr, H); /* addr =3D H:M:L */ + tcg_gen_shli_tl(addr, addr, 16); + tcg_gen_ori_tl(addr, addr, a->imm); + + gen_data_load(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Loads one byte pointed to by the Z-register into the destination + * register Rd. This instruction features a 100% space effective constant + * initialization or constant data fetch. The Program memory is organized= in + * 16-bit words while the Z-pointer is a byte address. Thus, the least + * significant bit of the Z-pointer selects either low byte (ZLSB =3D 0) = or high + * byte (ZLSB =3D 1). This instruction can address the first 64KB (32K wo= rds) of + * Program memory. The Zpointer Register can either be left unchanged by = the + * operation, or it can be incremented. The incrementation does not apply= to + * the RAMPZ Register. Devices with Self-Programming capability can use = the + * LPM instruction to read the Fuse and Lock bit values. Refer to the de= vice + * documentation for a detailed description. The LPM instruction is not + * available in all devices. Refer to the device specific instruction set + * summary + */ +static bool trans_LPM1(DisasContext *ctx, arg_LPM1 *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_LPM)) { + return true; + } + + TCGv Rd =3D cpu_r[0]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_r[31]; + TCGv L =3D cpu_r[30]; + + tcg_gen_shli_tl(addr, H, 8); /* addr =3D H:L */ + tcg_gen_or_tl(addr, addr, L); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LPM2(DisasContext *ctx, arg_LPM2 *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_LPM)) { + return true; + } + + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_r[31]; + TCGv L =3D cpu_r[30]; + + tcg_gen_shli_tl(addr, H, 8); /* addr =3D H:L */ + tcg_gen_or_tl(addr, addr, L); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_LPMX(DisasContext *ctx, arg_LPMX *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_LPMX)) { + return true; + } + + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_r[31]; + TCGv L =3D cpu_r[30]; + + tcg_gen_shli_tl(addr, H, 8); /* addr =3D H:L */ + tcg_gen_or_tl(addr, addr, L); + + tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd =3D mem[addr] */ + + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + tcg_gen_andi_tl(L, addr, 0xff); + + tcg_gen_shri_tl(addr, addr, 8); + tcg_gen_andi_tl(H, addr, 0xff); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Shifts all bits in Rd one place to the right. Bit 7 is cleared. Bit 0 = is + * loaded into the C Flag of the SREG. This operation effectively divides= an + * unsigned value by two. The C Flag can be used to round the result. + */ +static bool trans_LSR(DisasContext *ctx, arg_LSR *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + + tcg_gen_andi_tl(cpu_Cf, Rd, 1); + + tcg_gen_shri_tl(Rd, Rd, 1); + + tcg_gen_mov_tl(cpu_Zf, Rd); + tcg_gen_movi_tl(cpu_Nf, 0); + tcg_gen_mov_tl(cpu_Vf, cpu_Cf); + tcg_gen_mov_tl(cpu_Sf, cpu_Vf); + + return true; +} + +/* + * This instruction makes a copy of one register into another. The source + * register Rr is left unchanged, while the destination register Rd is lo= aded + * with a copy of Rr. + */ +static bool trans_MOV(DisasContext *ctx, arg_MOV *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + + tcg_gen_mov_tl(Rd, Rr); + + return true; +} + +/* + * This instruction makes a copy of one register pair into another regist= er + * pair. The source register pair Rr+1:Rr is left unchanged, while the + * destination register pair Rd+1:Rd is loaded with a copy of Rr + 1:Rr. = This + * instruction is not available in all devices. Refer to the device speci= fic + * instruction set summary. + */ +static bool trans_MOVW(DisasContext *ctx, arg_MOVW *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_MOVW)) { + return true; + } + + TCGv RdL =3D cpu_r[a->rd]; + TCGv RdH =3D cpu_r[a->rd + 1]; + TCGv RrL =3D cpu_r[a->rr]; + TCGv RrH =3D cpu_r[a->rr + 1]; + + tcg_gen_mov_tl(RdH, RrH); + tcg_gen_mov_tl(RdL, RrL); + + return true; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit unsigned multiplicat= ion. + */ +static bool trans_MUL(DisasContext *ctx, arg_MUL *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) { + return true; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_mul_tl(R, Rd, Rr); /* R =3D Rd *Rr */ + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + + tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf =3D R(16) */ + tcg_gen_mov_tl(cpu_Zf, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplicatio= n. + */ +static bool trans_MULS(DisasContext *ctx, arg_MULS *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) { + return true; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */ + tcg_gen_mul_tl(R, t0, t1); /* R =3D Rd * Rr */ + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + + tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf =3D R(16) */ + tcg_gen_mov_tl(cpu_Zf, R); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs 8-bit x 8-bit -> 16-bit multiplication of a + * signed and an unsigned number. + */ +static bool trans_MULSU(DisasContext *ctx, arg_MULSU *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) { + return true; + } + + TCGv R0 =3D cpu_r[0]; + TCGv R1 =3D cpu_r[1]; + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + TCGv t0 =3D tcg_temp_new_i32(); + + tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */ + tcg_gen_mul_tl(R, t0, Rr); /* R =3D Rd *Rr */ + + tcg_gen_andi_tl(R0, R, 0xff); + tcg_gen_shri_tl(R1, R, 8); + + tcg_gen_shri_tl(cpu_Cf, R, 16); /* Cf =3D R(16) */ + tcg_gen_mov_tl(cpu_Zf, R); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return true; +} + +/* + * Replaces the contents of register Rd with its two's complement; the + * value $80 is left unchanged. + */ +static bool trans_NEG(DisasContext *ctx, arg_NEG *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv t0 =3D tcg_const_i32(0); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, t0, Rd); /* R =3D 0 - Rd */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, t0, Rd); + gen_sub_Vf(R, t0, Rd); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction performs a single cycle No Operation. + */ +static bool trans_NOP(DisasContext *ctx, arg_NOP *a) +{ + + /* NOP */ + + return true; +} + +/* + * Performs the logical OR between the contents of register Rd and regist= er + * Rr and places the result in the destination register Rd. + */ +static bool trans_OR(DisasContext *ctx, arg_OR *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + tcg_gen_or_tl(R, Rd, Rr); + + tcg_gen_movi_tl(cpu_Vf, 0); + gen_ZNSf(R); + + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * Performs the logical OR between the contents of register Rd and a + * constant and places the result in the destination register Rd. + */ +static bool trans_ORI(DisasContext *ctx, arg_ORI *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + int Imm =3D (a->imm); + + tcg_gen_ori_tl(Rd, Rd, Imm); /* Rd =3D Rd | Imm */ + + tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf =3D 0 */ + gen_ZNSf(Rd); + + return true; +} + +/* + * Stores data from register Rr in the Register File to I/O Space (Ports, + * Timers, Configuration Registers, etc.). + */ +static bool trans_OUT(DisasContext *ctx, arg_OUT *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv port =3D tcg_const_i32(a->imm); + + gen_helper_outb(cpu_env, port, Rd); + + tcg_temp_free_i32(port); + + return true; +} + +/* + * This instruction loads register Rd with a byte from the STACK. The Sta= ck + * Pointer is pre-incremented by 1 before the POP. This instruction is n= ot + * available in all devices. Refer to the device specific instruction set + * summary. + */ +static bool trans_POP(DisasContext *ctx, arg_POP *a) +{ + /* + * Using a temp to work around some strange behaviour: + * tcg_gen_addi_tl(cpu_sp, cpu_sp, 1); + * gen_data_load(ctx, Rd, cpu_sp); + * seems to cause the add to happen twice. + * This doesn't happen if either the add or the load is removed. + */ + TCGv t1 =3D tcg_temp_new_i32(); + TCGv Rd =3D cpu_r[a->rd]; + + tcg_gen_addi_tl(t1, cpu_sp, 1); + gen_data_load(ctx, Rd, t1); + tcg_gen_mov_tl(cpu_sp, t1); + + return true; +} + +/* + * This instruction stores the contents of register Rr on the STACK. The + * Stack Pointer is post-decremented by 1 after the PUSH. This instructi= on is + * not available in all devices. Refer to the device specific instruction= set + * summary. + */ +static bool trans_PUSH(DisasContext *ctx, arg_PUSH *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + + gen_data_store(ctx, Rd, cpu_sp); + tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); + + return true; +} + +/* + * Relative call to an address within PC - 2K + 1 and PC + 2K (words). The + * return address (the instruction after the RCALL) is stored onto the St= ack. + * See also CALL. For AVR microcontrollers with Program memory not exceed= ing 4K + * words (8KB) this instruction can address the entire memory from every + * address location. The Stack Pointer uses a post-decrement scheme during + * RCALL. + */ +static bool trans_RCALL(DisasContext *ctx, arg_RCALL *a) +{ + int ret =3D ctx->npc; + int dst =3D ctx->npc + a->imm; + + gen_push_ret(ctx, ret); + gen_goto_tb(ctx, 0, dst); + + return true; +} + +/* + * Returns from subroutine. The return address is loaded from the STACK. + * The Stack Pointer uses a preincrement scheme during RET. + */ +static bool trans_RET(DisasContext *ctx, arg_RET *a) +{ + gen_pop_ret(ctx, cpu_pc); + + ctx->bstate =3D DISAS_LOOKUP; + return true; +} + +/* + * Returns from interrupt. The return address is loaded from the STACK and + * the Global Interrupt Flag is set. Note that the Status Register is not + * automatically stored when entering an interrupt routine, and it is not + * restored when returning from an interrupt routine. This must be handle= d by + * the application program. The Stack Pointer uses a pre-increment scheme + * during RETI. + */ +static bool trans_RETI(DisasContext *ctx, arg_RETI *a) +{ + gen_pop_ret(ctx, cpu_pc); + tcg_gen_movi_tl(cpu_If, 1); + + /* Need to return to main loop to re-evaluate interrupts. */ + ctx->bstate =3D DISAS_EXIT; + return true; +} + +/* + * Relative jump to an address within PC - 2K +1 and PC + 2K (words). For + * AVR microcontrollers with Program memory not exceeding 4K words (8KB) = this + * instruction can address the entire memory from every address location.= See + * also JMP. + */ +static bool trans_RJMP(DisasContext *ctx, arg_RJMP *a) +{ + int dst =3D ctx->npc + a->imm; + + gen_goto_tb(ctx, 0, dst); + + return true; +} + +/* + * Shifts all bits in Rd one place to the right. The C Flag is shifted in= to + * bit 7 of Rd. Bit 0 is shifted into the C Flag. This operation, combin= ed + * with ASR, effectively divides multi-byte signed values by two. Combine= d with + * LSR it effectively divides multi-byte unsigned values by two. The Carr= y Flag + * can be used to round the result. + */ +static bool trans_ROR(DisasContext *ctx, arg_ROR *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv t0 =3D tcg_temp_new_i32(); + + tcg_gen_shli_tl(t0, cpu_Cf, 7); + tcg_gen_andi_tl(cpu_Cf, Rd, 1); + tcg_gen_shri_tl(Rd, Rd, 1); + tcg_gen_or_tl(Rd, Rd, t0); + + gen_rshift_ZNVSf(Rd); + + tcg_temp_free_i32(t0); + + return true; +} + +/* + * Subtracts two registers and subtracts with the C Flag and places the + * result in the destination register Rd. + */ +static bool trans_SBC(DisasContext *ctx, arg_SBC *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr - Cf */ + tcg_gen_sub_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_NSf(R); + + /* + * Previous value remains unchanged when the result is zero; + * cleared otherwise. + */ + tcg_gen_or_tl(cpu_Zf, cpu_Zf, R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * SBCI -- Subtract Immediate with Carry + */ +static bool trans_SBCI(DisasContext *ctx, arg_SBCI *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D tcg_const_i32(a->imm); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr - Cf */ + tcg_gen_sub_tl(R, R, cpu_Cf); + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_NSf(R); + + /* + * Previous value remains unchanged when the result is zero; + * cleared otherwise. + */ + tcg_gen_or_tl(cpu_Zf, cpu_Zf, R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + tcg_temp_free_i32(Rr); + + return true; +} + +/* + * Sets a specified bit in an I/O Register. This instruction operates on + * the lower 32 I/O Registers -- addresses 0-31. + */ +static bool trans_SBI(DisasContext *ctx, arg_SBI *a) +{ + TCGv data =3D tcg_temp_new_i32(); + TCGv port =3D tcg_const_i32(a->reg); + + gen_helper_inb(data, cpu_env, port); + tcg_gen_ori_tl(data, data, 1 << a->bit); + gen_helper_outb(cpu_env, port, data); + + tcg_temp_free_i32(port); + tcg_temp_free_i32(data); + + return true; +} + +/* + * This instruction tests a single bit in an I/O Register and skips the + * next instruction if the bit is cleared. This instruction operates on t= he + * lower 32 I/O Registers -- addresses 0-31. + */ +static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) +{ + TCGv temp =3D tcg_const_i32(a->reg); + + gen_helper_inb(temp, cpu_env, temp); + tcg_gen_andi_tl(temp, temp, 1 << a->bit); + ctx->skip_cond =3D TCG_COND_EQ; + ctx->skip_var0 =3D temp; + ctx->free_skip_var0 =3D true; + + return true; +} + +/* + * This instruction tests a single bit in an I/O Register and skips the + * next instruction if the bit is set. This instruction operates on the l= ower + * 32 I/O Registers -- addresses 0-31. + */ +static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a) +{ + TCGv temp =3D tcg_const_i32(a->reg); + + gen_helper_inb(temp, cpu_env, temp); + tcg_gen_andi_tl(temp, temp, 1 << a->bit); + ctx->skip_cond =3D TCG_COND_NE; + ctx->skip_var0 =3D temp; + ctx->free_skip_var0 =3D true; + + return true; +} + +/* + * Subtracts an immediate value (0-63) from a register pair and places the + * result in the register pair. This instruction operates on the upper fo= ur + * register pairs, and is well suited for operations on the Pointer Regis= ters. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static bool trans_SBIW(DisasContext *ctx, arg_SBIW *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_ADIW_SBIW)) { + return true; + } + + TCGv RdL =3D cpu_r[a->rd]; + TCGv RdH =3D cpu_r[a->rd + 1]; + int Imm =3D (a->imm); + TCGv R =3D tcg_temp_new_i32(); + TCGv Rd =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd =3D RdH:RdL */ + tcg_gen_subi_tl(R, Rd, Imm); /* R =3D Rd - Imm */ + tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */ + + /* Cf */ + tcg_gen_andc_tl(cpu_Cf, R, Rd); + tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15); /* Cf =3D R & ~Rd */ + + /* Vf */ + tcg_gen_andc_tl(cpu_Vf, Rd, R); + tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15); /* Vf =3D Rd & ~R */ + + /* Zf */ + tcg_gen_mov_tl(cpu_Zf, R); /* Zf =3D R */ + + /* Nf */ + tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf =3D R(15) */ + + /* Sf */ + tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf =3D Nf ^ Vf */ + + /* R */ + tcg_gen_andi_tl(RdL, R, 0xff); + tcg_gen_shri_tl(RdH, R, 8); + + tcg_temp_free_i32(Rd); + tcg_temp_free_i32(R); + + return true; +} + +/* + * This instruction tests a single bit in a register and skips the next + * instruction if the bit is cleared. + */ +static bool trans_SBRC(DisasContext *ctx, arg_SBRC *a) +{ + TCGv Rr =3D cpu_r[a->rr]; + + ctx->skip_cond =3D TCG_COND_EQ; + ctx->skip_var0 =3D tcg_temp_new(); + ctx->free_skip_var0 =3D true; + + tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit); + return true; +} + +/* + * This instruction tests a single bit in a register and skips the next + * instruction if the bit is set. + */ +static bool trans_SBRS(DisasContext *ctx, arg_SBRS *a) +{ + TCGv Rr =3D cpu_r[a->rr]; + + ctx->skip_cond =3D TCG_COND_NE; + ctx->skip_var0 =3D tcg_temp_new(); + ctx->free_skip_var0 =3D true; + + tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit); + return true; +} + +/* + * This instruction sets the circuit in sleep mode defined by the MCU + * Control Register. + */ +static bool trans_SLEEP(DisasContext *ctx, arg_SLEEP *a) +{ + gen_helper_sleep(cpu_env); + ctx->bstate =3D DISAS_NORETURN; + return true; +} + +/* + * SPM can be used to erase a page in the Program memory, to write a page + * in the Program memory (that is already erased), and to set Boot Loader= Lock + * bits. In some devices, the Program memory can be written one word at a= time, + * in other devices an entire page can be programmed simultaneously after= first + * filling a temporary page buffer. In all cases, the Program memory must= be + * erased one page at a time. When erasing the Program memory, the RAMPZ = and + * Z-register are used as page address. When writing the Program memory, = the + * RAMPZ and Z-register are used as page or word address, and the R1:R0 + * register pair is used as data(1). When setting the Boot Loader Lock bi= ts, + * the R1:R0 register pair is used as data. Refer to the device documenta= tion + * for detailed description of SPM usage. This instruction can address the + * entire Program memory. The SPM instruction is not available in all de= vices. + * Refer to the device specific instruction set summary. Note: 1. R1 + * determines the instruction high byte, and R0 determines the instructio= n low + * byte. + */ +static bool trans_SPM(DisasContext *ctx, arg_SPM *a) +{ + /* TODO */ + if (!avr_have_feature(ctx, AVR_FEATURE_SPM)) { + return true; + } + + return true; +} + +static bool trans_SPMX(DisasContext *ctx, arg_SPMX *a) +{ + /* TODO */ + if (!avr_have_feature(ctx, AVR_FEATURE_SPMX)) { + return true; + } + + return true; +} + +static bool trans_STX1(DisasContext *ctx, arg_STX1 *a) +{ + TCGv Rd =3D cpu_r[a->rr]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STX2(DisasContext *ctx, arg_STX2 *a) +{ + TCGv Rd =3D cpu_r[a->rr]; + TCGv addr =3D gen_get_xaddr(); + + gen_data_store(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STX3(DisasContext *ctx, arg_STX3 *a) +{ + TCGv Rd =3D cpu_r[a->rr]; + TCGv addr =3D gen_get_xaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_store(ctx, Rd, addr); + gen_set_xaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STY2(DisasContext *ctx, arg_STY2 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_yaddr(); + + gen_data_store(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STY3(DisasContext *ctx, arg_STY3 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_store(ctx, Rd, addr); + gen_set_yaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STDY(DisasContext *ctx, arg_STDY *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_yaddr(); + + tcg_gen_addi_tl(addr, addr, a->imm); /* addr =3D addr + q */ + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STZ2(DisasContext *ctx, arg_STZ2 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + gen_data_store(ctx, Rd, addr); + tcg_gen_addi_tl(addr, addr, 1); /* addr =3D addr + 1 */ + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STZ3(DisasContext *ctx, arg_STZ3 *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_subi_tl(addr, addr, 1); /* addr =3D addr - 1 */ + gen_data_store(ctx, Rd, addr); + + gen_set_zaddr(addr); + + tcg_temp_free_i32(addr); + + return true; +} + +static bool trans_STDZ(DisasContext *ctx, arg_STDZ *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D gen_get_zaddr(); + + tcg_gen_addi_tl(addr, addr, a->imm); /* addr =3D addr + q */ + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Stores one byte from a Register to the data space. For parts with SRAM, + * the data space consists of the Register File, I/O memory and internal = SRAM + * (and external SRAM if applicable). For parts without SRAM, the data sp= ace + * consists of the Register File only. The EEPROM has a separate address = space. + * A 16-bit address must be supplied. Memory access is limited to the cur= rent + * data segment of 64KB. The STS instruction uses the RAMPD Register to a= ccess + * memory above 64KB. To access another data segment in devices with more= than + * 64KB data space, the RAMPD in register in the I/O area has to be chang= ed. + * This instruction is not available in all devices. Refer to the device + * specific instruction set summary. + */ +static bool trans_STS(DisasContext *ctx, arg_STS *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv addr =3D tcg_temp_new_i32(); + TCGv H =3D cpu_rampD; + a->imm =3D next_word(ctx); + + tcg_gen_mov_tl(addr, H); /* addr =3D H:M:L */ + tcg_gen_shli_tl(addr, addr, 16); + tcg_gen_ori_tl(addr, addr, a->imm); + + gen_data_store(ctx, Rd, addr); + + tcg_temp_free_i32(addr); + + return true; +} + +/* + * Subtracts two registers and places the result in the destination + * register Rd. + */ +static bool trans_SUB(DisasContext *ctx, arg_SUB *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D cpu_r[a->rr]; + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Rr */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + + return true; +} + +/* + * Subtracts a register and a constant and places the result in the + * destination register Rd. This instruction is working on Register R16 t= o R31 + * and is very well suited for operations on the X, Y, and Z-pointers. + */ +static bool trans_SUBI(DisasContext *ctx, arg_SUBI *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv Rr =3D tcg_const_i32(a->imm); + TCGv R =3D tcg_temp_new_i32(); + + /* op */ + tcg_gen_sub_tl(R, Rd, Rr); /* R =3D Rd - Imm */ + tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */ + + gen_sub_CHf(R, Rd, Rr); + gen_sub_Vf(R, Rd, Rr); + gen_ZNSf(R); + + /* R */ + tcg_gen_mov_tl(Rd, R); + + tcg_temp_free_i32(R); + tcg_temp_free_i32(Rr); + + return true; +} + +/* + * Swaps high and low nibbles in a register. + */ +static bool trans_SWAP(DisasContext *ctx, arg_SWAP *a) +{ + TCGv Rd =3D cpu_r[a->rd]; + TCGv t0 =3D tcg_temp_new_i32(); + TCGv t1 =3D tcg_temp_new_i32(); + + tcg_gen_andi_tl(t0, Rd, 0x0f); + tcg_gen_shli_tl(t0, t0, 4); + tcg_gen_andi_tl(t1, Rd, 0xf0); + tcg_gen_shri_tl(t1, t1, 4); + tcg_gen_or_tl(Rd, t0, t1); + + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); + + return true; +} + +/* + * This instruction resets the Watchdog Timer. This instruction must be + * executed within a limited time given by the WD prescaler. See the Watc= hdog + * Timer hardware specification. + */ +static bool trans_WDR(DisasContext *ctx, arg_WDR *a) +{ + gen_helper_wdr(cpu_env); + + return true; +} + +/* + * Exchanges one byte indirect between register and data space. The data + * location is pointed to by the Z (16 bits) Pointer Register in the Regi= ster + * File. Memory access is limited to the current data segment of 64KB. To + * access another data segment in devices with more than 64KB data space,= the + * RAMPZ in register in the I/O area has to be changed. The Z-pointer Re= gister + * is left unchanged by the operation. This instruction is especially sui= ted + * for writing/reading status bits stored in SRAM. + */ +static bool trans_XCH(DisasContext *ctx, arg_XCH *a) +{ + if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) { + return true; + } + + TCGv Rd =3D cpu_r[a->rd]; + TCGv t0 =3D tcg_temp_new_i32(); + TCGv addr =3D gen_get_zaddr(); + + gen_data_load(ctx, t0, addr); + gen_data_store(ctx, Rd, addr); + tcg_gen_mov_tl(Rd, t0); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(addr); + + return true; +} + +void avr_cpu_tcg_init(void) +{ + int i; + +#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x) + cpu_pc =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc"); + cpu_Cf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf"); + cpu_Zf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf"); + cpu_Nf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf"); + cpu_Vf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf"); + cpu_Sf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf"); + cpu_Hf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf"); + cpu_Tf =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf"); + cpu_If =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If"); + cpu_rampD =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "ra= mpD"); + cpu_rampX =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "ra= mpX"); + cpu_rampY =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "ra= mpY"); + cpu_rampZ =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "ra= mpZ"); + cpu_eind =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind= "); + cpu_sp =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp"); + cpu_skip =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(skip), "skip= "); + + for (i =3D 0; i < NO_CPU_REGISTERS; i++) { + cpu_r[i] =3D tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]), + reg_names[i]); + } +#undef AVR_REG_OFFS +} + +static void translate(DisasContext *ctx) +{ + uint32_t opcode =3D next_word(ctx); + + if (!decode_insn(ctx, opcode)) { + gen_helper_unsupported(cpu_env); + ctx->bstate =3D DISAS_NORETURN; + } +} + +/* Standardize the cpu_skip condition to NE. */ +static bool canonicalize_skip(DisasContext *ctx) +{ + switch (ctx->skip_cond) { + case TCG_COND_NEVER: + /* Normal case: cpu_skip is known to be false. */ + return false; + + case TCG_COND_ALWAYS: + /* + * Breakpoint case: cpu_skip is known to be true, via TB_FLAGS_SKI= P. + * The breakpoint is on the instruction being skipped, at the start + * of the TranslationBlock. No need to update. + */ + return false; + + case TCG_COND_NE: + if (ctx->skip_var1 =3D=3D NULL) { + tcg_gen_mov_tl(cpu_skip, ctx->skip_var0); + } else { + tcg_gen_xor_tl(cpu_skip, ctx->skip_var0, ctx->skip_var1); + ctx->skip_var1 =3D NULL; + } + break; + + default: + /* Convert to a NE condition vs 0. */ + if (ctx->skip_var1 =3D=3D NULL) { + tcg_gen_setcondi_tl(ctx->skip_cond, cpu_skip, ctx->skip_var0, = 0); + } else { + tcg_gen_setcond_tl(ctx->skip_cond, cpu_skip, + ctx->skip_var0, ctx->skip_var1); + ctx->skip_var1 =3D NULL; + } + ctx->skip_cond =3D TCG_COND_NE; + break; + } + if (ctx->free_skip_var0) { + tcg_temp_free(ctx->skip_var0); + ctx->free_skip_var0 =3D false; + } + ctx->skip_var0 =3D cpu_skip; + return true; +} + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_ins= ns) +{ + CPUAVRState *env =3D cs->env_ptr; + DisasContext ctx =3D { + .tb =3D tb, + .cs =3D cs, + .env =3D env, + .memidx =3D 0, + .bstate =3D DISAS_NEXT, + .skip_cond =3D TCG_COND_NEVER, + .singlestep =3D cs->singlestep_enabled, + }; + target_ulong pc_start =3D tb->pc / 2; + int num_insns =3D 0; + + if (tb->flags & TB_FLAGS_FULL_ACCESS) { + /* + * This flag is set by ST/LD instruction we will regenerate it ONLY + * with mem/cpu memory access instead of mem access + */ + max_insns =3D 1; + } + if (ctx.singlestep) { + max_insns =3D 1; + } + + gen_tb_start(tb); + + ctx.npc =3D pc_start; + if (tb->flags & TB_FLAGS_SKIP) { + ctx.skip_cond =3D TCG_COND_ALWAYS; + ctx.skip_var0 =3D cpu_skip; + } + + do { + TCGLabel *skip_label =3D NULL; + + /* translate current instruction */ + tcg_gen_insn_start(ctx.npc); + num_insns++; + + /* + * this is due to some strange GDB behavior + * let's assume main has address 0x100 + * b main - sets breakpoint at address 0x00000100 (code) + * b *0x100 - sets breakpoint at address 0x00800100 (data) + */ + if (unlikely(!ctx.singlestep && + (cpu_breakpoint_test(cs, OFFSET_CODE + ctx.npc * 2, BP_ANY= ) || + cpu_breakpoint_test(cs, OFFSET_DATA + ctx.npc * 2, BP_ANY= )))) { + canonicalize_skip(&ctx); + tcg_gen_movi_tl(cpu_pc, ctx.npc); + gen_helper_debug(cpu_env); + goto done_generating; + } + + /* Conditionally skip the next instruction, if indicated. */ + if (ctx.skip_cond !=3D TCG_COND_NEVER) { + skip_label =3D gen_new_label(); + if (ctx.skip_var0 =3D=3D cpu_skip) { + /* + * Copy cpu_skip so that we may zero it before the branch. + * This ensures that cpu_skip is non-zero after the label + * if and only if the skipped insn itself sets a skip. + */ + ctx.free_skip_var0 =3D true; + ctx.skip_var0 =3D tcg_temp_new(); + tcg_gen_mov_tl(ctx.skip_var0, cpu_skip); + tcg_gen_movi_tl(cpu_skip, 0); + } + if (ctx.skip_var1 =3D=3D NULL) { + tcg_gen_brcondi_tl(ctx.skip_cond, ctx.skip_var0, 0, skip_l= abel); + } else { + tcg_gen_brcond_tl(ctx.skip_cond, ctx.skip_var0, + ctx.skip_var1, skip_label); + ctx.skip_var1 =3D NULL; + } + if (ctx.free_skip_var0) { + tcg_temp_free(ctx.skip_var0); + ctx.free_skip_var0 =3D false; + } + ctx.skip_cond =3D TCG_COND_NEVER; + ctx.skip_var0 =3D NULL; + } + + translate(&ctx); + + if (skip_label) { + canonicalize_skip(&ctx); + gen_set_label(skip_label); + if (ctx.bstate =3D=3D DISAS_NORETURN) { + ctx.bstate =3D DISAS_CHAIN; + } + } + } while (ctx.bstate =3D=3D DISAS_NEXT + && num_insns < max_insns + && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4 + && !tcg_op_buf_full()); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + bool nonconst_skip =3D canonicalize_skip(&ctx); + + switch (ctx.bstate) { + case DISAS_NORETURN: + assert(!nonconst_skip); + break; + case DISAS_NEXT: + case DISAS_TOO_MANY: + case DISAS_CHAIN: + if (!nonconst_skip) { + /* Note gen_goto_tb checks singlestep. */ + gen_goto_tb(&ctx, 1, ctx.npc); + break; + } + tcg_gen_movi_tl(cpu_pc, ctx.npc); + /* fall through */ + case DISAS_LOOKUP: + if (!ctx.singlestep) { + tcg_gen_lookup_and_goto_ptr(); + break; + } + /* fall through */ + case DISAS_EXIT: + if (ctx.singlestep) { + gen_helper_debug(cpu_env); + } else { + tcg_gen_exit_tb(NULL, 0); + } + break; + default: + g_assert_not_reached(); + } + +done_generating: + gen_tb_end(tb, num_insns); + + tb->size =3D (ctx.npc - pc_start) * 2; + tb->icount =3D num_insns; +} + +void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc_w =3D data[0]; +} --=20 2.17.2 (Apple Git-113) From nobody Thu Apr 25 14:09:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1562909935; cv=none; d=zoho.com; s=zohoarc; b=afk0SQqHhFaIMhwlaRFm8f2PxVLI8sJXGTaEeTPP6TzpYVIe/Eu4/Nr7daO05dJoweGdkWyWuHHuKiOCR9/f5MtTu+UC828ZP9FQiT8/dufgDi5V5jGPTY4ghN21qj+sTQ2SlSDDF0cNQKbrFWlpknqvy8GViHNWHDhyXhbmqcY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1562909935; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=4HJz4QwSyDrITqaCCSPLn0aGSTeN9nz5MIk6rqg1k+A=; b=Vm357B7ZZmvShMPuEuzTZw+f48xxancrrBm5d2tZyNWOqkWEvqXGtzIZ5rjfjgCC+9qkWvyD7fHLPA9QpTrcl+4gyCR/CgHB2kjGCYExvi7KcHvEDcqjVmfKFW9bbJ2I2+E0JCEk0rvUCqPMUP603IqtrREY+NYEijkZ+XdRYuc= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1562909935804965.1804017925531; Thu, 11 Jul 2019 22:38:55 -0700 (PDT) Received: from localhost ([::1]:46724 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloGk-00031h-PM for importer@patchew.org; Fri, 12 Jul 2019 01:38:54 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35109) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFM-0005iB-Hm for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hloFG-0007j6-4t for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:26 -0400 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]:56031) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hloFF-0007iT-Oj for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:22 -0400 Received: by mail-wm1-x32a.google.com with SMTP id a15so7631206wmj.5 for ; Thu, 11 Jul 2019 22:37:21 -0700 (PDT) Received: from 8c859074c0ff.ant.amazon.com.com (bzq-109-66-61-47.red.bezeqint.net. [109.66.61.47]) by smtp.gmail.com with ESMTPSA id y6sm7824009wmd.16.2019.07.11.22.37.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Jul 2019 22:37:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=4HJz4QwSyDrITqaCCSPLn0aGSTeN9nz5MIk6rqg1k+A=; b=amUxNZJ3akEkkqRIQO8cUyLFM6G22HqAG5QUALZenr5DUE6B360Z0O/2oCsMxWsSgu micTpbDvHNACDmZo3V2SLHQPgKczZ7DTtQRyoeNTLaAzrMSK1F186SudMYNoz0LwCsqp O//hzo6KeK97ZmhNgWhJAhIfWT6kgqsl0wc1NadbD5ZtZU2DKMnzPA5E6g5fPwFoYdsr MyeIWhndXY0GEPRqlzXWXJfggNDE7O1i9CTOQH8Ys92UyX0fVpPfNdKC6ddg5431cEF5 kW2toZdqVF9J7GvUWPXab41ANfY8VBfOGvW0SXuBIbtsx+Uy4kYIzwd/v9HyauVQhy/Z F+Ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=4HJz4QwSyDrITqaCCSPLn0aGSTeN9nz5MIk6rqg1k+A=; b=bpCvC72ZQ4wOWPonkUXThj+T1yTL2U3k9fsJmwFkt+okrZCTTLMHdY7eEUF/EYyQqt 5iwluy9ovOAhJiIPkJ0elHrJe/HSRco4ErJLoKCLuE1aUCvSMc5wr4FVm3ynhm/4g6LZ EeH9peXCS4apmNebdjvxRpWQqBv7If6omSlsBxkK0ag0hkbOxrfIkHCtn3VmMfJyqjzk nJ/lnErXkfMXdytxzquQJoqdpjbUO1fFctizTDDjbgbOvJ0F5KNUOgW/8zH6r7O96LS9 lL7R+Saj7fyRGP1PMEhC7zfTzDLI0Pt8ABRKvA8On1agF7Yz/G69mMO8xKj1cf69THxS 7Xxw== X-Gm-Message-State: APjAAAV0E1f4GJrMdkOMOwpvSdG3dWZPQmAnw80xF6rlJsVKR/x0crlb 8GCTZoOlpD3M0kykRhbEOp7BIJeKljYT5Q== X-Google-Smtp-Source: APXvYqzjiUKBaxlcdP30pWw/dtNW9YHgFXSGmUKuvwViTmoU49M9JGsebmG3iXDhW5dG0707MV0EJw== X-Received: by 2002:a1c:4041:: with SMTP id n62mr7660749wma.100.1562909839848; Thu, 11 Jul 2019 22:37:19 -0700 (PDT) From: Michael Rolnik To: qemu-devel@nongnu.org Date: Fri, 12 Jul 2019 08:37:02 +0300 Message-Id: <20190712053704.21228-6-mrolnik@gmail.com> X-Mailer: git-send-email 2.17.2 (Apple Git-113) In-Reply-To: <20190712053704.21228-1-mrolnik@gmail.com> References: <20190712053704.21228-1-mrolnik@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::32a Subject: [Qemu-devel] [PATCH v26 5/7] target/avr: Add limited support for USART and 16 bit timer peripherals X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: imammedo@redhat.com, Sarah Harris , Michael Rolnik , richard.henderson@linaro.org, philmd@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Sarah Harris These were designed to facilitate testing but should provide enough functio= n to be useful in other contexts. Only a subset of the functions of each peripheral is implemented, mainly du= e to the lack of a standard way to handle electrical connections (like GPIO= pins). Signed-off-by: Michael Rolnik --- hw/char/Kconfig | 3 + hw/char/Makefile.objs | 1 + hw/char/avr_usart.c | 322 ++++++++++++++++++ hw/misc/Kconfig | 3 + hw/misc/Makefile.objs | 2 + hw/misc/avr_mask.c | 110 ++++++ hw/timer/Kconfig | 3 + hw/timer/Makefile.objs | 1 + hw/timer/avr_timer16.c | 603 +++++++++++++++++++++++++++++++++ include/hw/char/avr_usart.h | 97 ++++++ include/hw/misc/avr_mask.h | 47 +++ include/hw/timer/avr_timer16.h | 97 ++++++ 12 files changed, 1289 insertions(+) create mode 100644 hw/char/avr_usart.c create mode 100644 hw/misc/avr_mask.c create mode 100644 hw/timer/avr_timer16.c create mode 100644 include/hw/char/avr_usart.h create mode 100644 include/hw/misc/avr_mask.h create mode 100644 include/hw/timer/avr_timer16.h diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 40e7a8b8bb..331b20983f 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -46,3 +46,6 @@ config SCLPCONSOLE =20 config TERMINAL3270 bool + +config AVR_USART + bool diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 02d8a66925..09ed50f1d0 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -21,6 +21,7 @@ obj-$(CONFIG_PSERIES) +=3D spapr_vty.o obj-$(CONFIG_DIGIC) +=3D digic-uart.o obj-$(CONFIG_STM32F2XX_USART) +=3D stm32f2xx_usart.o obj-$(CONFIG_RASPI) +=3D bcm2835_aux.o +obj-$(CONFIG_AVR_USART) +=3D avr_usart.o =20 common-obj-$(CONFIG_CMSDK_APB_UART) +=3D cmsdk-apb-uart.o common-obj-$(CONFIG_ETRAXFS) +=3D etraxfs_ser.o diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c new file mode 100644 index 0000000000..21f533f6e6 --- /dev/null +++ b/hw/char/avr_usart.c @@ -0,0 +1,322 @@ +/* + * AVR USART + * + * Copyright (c) 2018 University of Kent + * Author: Sarah Harris + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * 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/char/avr_usart.h" +#include "qemu/log.h" + +static int avr_usart_can_receive(void *opaque) +{ + AVRUsartState *usart =3D 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 siz= e) +{ + AVRUsartState *usart =3D opaque; + assert(size =3D=3D 1); + assert(!usart->data_valid); + usart->data =3D buffer[0]; + usart->data_valid =3D true; + usart->csra |=3D 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 =3D ((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 =3D 0b11111; + break; + case 1: + usart->char_mask =3D 0b111111; + break; + case 2: + usart->char_mask =3D 0b1111111; + break; + case 3: + usart->char_mask =3D 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 =3D 0b11111111; + break; + default: + assert(0); + } +} + +static void avr_usart_reset(DeviceState *dev) +{ + AVRUsartState *usart =3D AVR_USART(dev); + usart->data_valid =3D false; + usart->csra =3D 0b00100000; + usart->csrb =3D 0b00000000; + usart->csrc =3D 0b00000110; + usart->brrl =3D 0; + usart->brrh =3D 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 siz= e) +{ + AVRUsartState *usart =3D opaque; + uint8_t data; + assert(size =3D=3D 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 =3D usart->data & usart->char_mask; + usart->data_valid =3D false; + } else { + data =3D 0; + } + usart->csra &=3D 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 =3D opaque; + uint8_t mask; + uint8_t data; + assert((value & 0xff) =3D=3D value); + assert(size =3D=3D 1); + + if (!usart->enabled) { + return; + } + + switch (addr) { + case USART_DR: + if (!(usart->csrb & USART_CSRB_TXEN)) { + /* Transmitter disabled, ignore. */ + return; + } + usart->csra |=3D USART_CSRA_TXC; + usart->csra |=3D USART_CSRA_DRE; + if (usart->csrb & USART_CSRB_TXCIE) { + qemu_set_irq(usart->txc_irq, 1); + usart->csra &=3D 0xff ^ USART_CSRA_TXC; + } + if (usart->csrb & USART_CSRB_DREIE) { + qemu_set_irq(usart->dre_irq, 1); + } + data =3D value; + qemu_chr_fe_write_all(&usart->chr, &data, 1); + break; + case USART_CSRA: + mask =3D 0b01000011; + /* Mask read-only bits. */ + value =3D (value & mask) | (usart->csra & (0xff ^ mask)); + usart->csra =3D value; + if (value & USART_CSRA_TXC) { + usart->csra ^=3D 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 =3D 0b11111101; + /* Mask read-only bits. */ + value =3D (value & mask) | (usart->csrb & (0xff ^ mask)); + usart->csrb =3D value; + if (!(value & USART_CSRB_RXEN)) { + /* Receiver disabled, flush input buffer. */ + usart->data_valid =3D 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 =3D 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 =3D value; + break; + case USART_BRRH: + usart->brrh =3D 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 =3D { + .read =3D avr_usart_read, + .write =3D avr_usart_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .impl =3D {.min_access_size =3D 1, .max_access_size =3D 1} +}; + +static Property avr_usart_properties[] =3D { + DEFINE_PROP_CHR("chardev", AVRUsartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void avr_usart_pr(void *opaque, int irq, int level) +{ + AVRUsartState *s =3D AVR_USART(opaque); + + s->enabled =3D !level; + + if (!s->enabled) { + avr_usart_reset(DEVICE(s)); + } +} + +static void avr_usart_init(Object *obj) +{ + AVRUsartState *s =3D 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= , 8); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + qdev_init_gpio_in(DEVICE(s), avr_usart_pr, 1); + s->enabled =3D true; +} + +static void avr_usart_realize(DeviceState *dev, Error **errp) +{ + AVRUsartState *s =3D 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 =3D DEVICE_CLASS(klass); + + dc->reset =3D avr_usart_reset; + dc->props =3D avr_usart_properties; + dc->realize =3D avr_usart_realize; +} + +static const TypeInfo avr_usart_info =3D { + .name =3D TYPE_AVR_USART, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AVRUsartState), + .instance_init =3D avr_usart_init, + .class_init =3D avr_usart_class_init, +}; + +static void avr_usart_register_types(void) +{ + type_register_static(&avr_usart_info); +} + +type_init(avr_usart_register_types) diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 385e1b0cec..ebdcf319f6 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -117,4 +117,7 @@ config AUX bool select I2C =20 +config AVR_MASK + bool + source macio/Kconfig diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index e9aab519a1..0806e9df9c 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -80,3 +80,5 @@ obj-$(CONFIG_MSF2) +=3D msf2-sysreg.o obj-$(CONFIG_NRF51_SOC) +=3D nrf51_rng.o =20 obj-$(CONFIG_GRLIB) +=3D grlib_ahb_apb_pnp.o + +obj-$(CONFIG_AVR_MASK) +=3D avr_mask.o diff --git a/hw/misc/avr_mask.c b/hw/misc/avr_mask.c new file mode 100644 index 0000000000..1d1f8e10a8 --- /dev/null +++ b/hw/misc/avr_mask.c @@ -0,0 +1,110 @@ +/* + * AVR Power Reduction + * + * Copyright (c) 2019 Michael Rolnik + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * 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_mask.h" +#include "qemu/log.h" + +#define DB_PRINT(fmt, args...) /* Nothing */ +/*#define DB_PRINT(fmt, args...) printf("%s: " fmt "\n", __func__, ## args= )*/ + +static void avr_mask_reset(DeviceState *dev) +{ + AVRMaskState *s =3D AVR_MASK(dev); + + s->val =3D 0x00; + + for (int i =3D 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 =3D=3D 1); + assert(offset =3D=3D 0); + AVRMaskState *s =3D opaque; + + return (uint64_t)s->val; +} + +static void avr_mask_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size =3D=3D 1); + assert(offset =3D=3D 0); + AVRMaskState *s =3D opaque; + uint8_t val8 =3D val64; + + DB_PRINT("write %d to offset %d", val8, (uint8_t)offset); + + s->val =3D val8; + for (int i =3D 0; i < 8; i++) { + qemu_set_irq(s->irq[i], (val8 & (1 << i)) !=3D 0); + } +} + +static const MemoryRegionOps avr_mask_ops =3D { + .read =3D avr_mask_read, + .write =3D avr_mask_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .impl =3D {.max_access_size =3D 1} +}; + +static void avr_mask_init(Object *dev) +{ + AVRMaskState *s =3D AVR_MASK(dev); + SysBusDevice *busdev =3D 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 =3D 0; i < 8; i++) { + sysbus_init_irq(busdev, &s->irq[i]); + } + s->val =3D 0x00; +} + +static void avr_mask_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->reset =3D avr_mask_reset; +} + +static const TypeInfo avr_mask_info =3D { + .name =3D TYPE_AVR_MASK, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AVRMaskState), + .class_init =3D avr_mask_class_init, + .instance_init =3D avr_mask_init, +}; + +static void avr_mask_register_types(void) +{ + type_register_static(&avr_mask_info); +} + +type_init(avr_mask_register_types) diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 51921eb63f..ad754df750 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -61,3 +61,6 @@ config CMSDK_APB_TIMER config CMSDK_APB_DUALTIMER bool select PTIMER + +config AVR_TIMER16 + bool diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 123d92c969..4f853e265d 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -35,6 +35,7 @@ obj-$(CONFIG_PXA2XX) +=3D pxa2xx_timer.o obj-$(CONFIG_SH4) +=3D sh_timer.o obj-$(CONFIG_DIGIC) +=3D digic-timer.o obj-$(CONFIG_MIPS_CPS) +=3D mips_gictimer.o +obj-$(CONFIG_AVR_TIMER16) +=3D avr_timer16.o =20 obj-$(CONFIG_MC146818RTC) +=3D mc146818rtc.o =20 diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c new file mode 100644 index 0000000000..bd13a92765 --- /dev/null +++ b/hw/timer/avr_timer16.c @@ -0,0 +1,603 @@ +/* + * AVR 16 bit timer + * + * Copyright (c) 2018 University of Kent + * Author: Ed Robbins + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * 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. + */ + +/* + * 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 "hw/timer/avr_timer16.h" +#include "qemu/log.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 ERROR(fmt, args...) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## args) +#define DB_PRINT(fmt, args...) /* Nothing */ +/*#define DB_PRINT(fmt, args...) printf("%s: " fmt "\n", __func__, ## args= )*/ + +static inline int64_t avr_timer16_ns_to_ticks(AVRTimer16State *t16, int64_= t t) +{ + if (t16->period_ns =3D=3D 0) { + return 0; + } + return t / t16->period_ns; +} + +static void avr_timer16_update_cnt(AVRTimer16State *t16) +{ + uint16_t cnt; + cnt =3D avr_timer16_ns_to_ticks(t16, qemu_clock_get_ns(QEMU_CLOCK_VIRT= UAL) - + t16->reset_time_ns); + t16->cntl =3D (uint8_t)(cnt & 0xff); + t16->cnth =3D (uint8_t)((cnt & 0xff00) >> 8); +} + +static inline void avr_timer16_recalc_reset_time(AVRTimer16State *t16) +{ + t16->reset_time_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + CNT(t16) * t16->period_ns; +} + +static void avr_timer16_clock_reset(AVRTimer16State *t16) +{ + t16->cntl =3D 0; + t16->cnth =3D 0; + t16->reset_time_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void avr_timer16_clksrc_update(AVRTimer16State *t16) +{ + uint16_t divider =3D 0; + switch (CLKSRC(t16)) { + case T16_CLKSRC_EXT_FALLING: + case T16_CLKSRC_EXT_RISING: + ERROR("external clock source unsupported"); + goto end; + case T16_CLKSRC_STOPPED: + goto end; + case T16_CLKSRC_DIV1: + divider =3D 1; + break; + case T16_CLKSRC_DIV8: + divider =3D 8; + break; + case T16_CLKSRC_DIV64: + divider =3D 64; + break; + case T16_CLKSRC_DIV256: + divider =3D 256; + break; + case T16_CLKSRC_DIV1024: + divider =3D 1024; + break; + default: + goto end; + } + t16->freq_hz =3D t16->cpu_freq_hz / divider; + t16->period_ns =3D 1000000000ULL / t16->freq_hz; + DB_PRINT("Timer frequency %" PRIu64 " hz, period %" PRIu64 " ns (%f s)= ", + t16->freq_hz, t16->period_ns, 1 / (double)t16->freq_hz); +end: + return; +} + +static void avr_timer16_set_alarm(AVRTimer16State *t16) +{ + if (CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_FALLING || + CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_RISING || + CLKSRC(t16) =3D=3D T16_CLKSRC_STOPPED) { + /* Timer is disabled or set to external clock source (unsupported)= */ + goto end; + } + + uint64_t alarm_offset =3D 0xffff; + enum NextInterrupt next_interrupt =3D 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 =3D OCRA(t16); + next_interrupt =3D COMPA; + } + break; + case T16_MODE_CTC_OCRA: + /* CTC mode, top =3D ocra */ + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16)) { + alarm_offset =3D OCRA(t16); + next_interrupt =3D COMPA; + } + break; + case T16_MODE_CTC_ICR: + /* CTC mode, top =3D icr */ + if (ICR(t16) < alarm_offset && ICR(t16) > CNT(t16)) { + alarm_offset =3D ICR(t16); + next_interrupt =3D CAPT; + } + if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCA)) { + alarm_offset =3D OCRA(t16); + next_interrupt =3D COMPA; + } + break; + default: + ERROR("pwm modes are unsupported"); + goto end; + } + if (OCRB(t16) < alarm_offset && OCRB(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCB)) { + alarm_offset =3D OCRB(t16); + next_interrupt =3D COMPB; + } + if (OCRC(t16) < alarm_offset && OCRB(t16) > CNT(t16) && + (t16->imsk & T16_INT_OCC)) { + alarm_offset =3D OCRB(t16); + next_interrupt =3D COMPC; + } + alarm_offset -=3D CNT(t16); + + t16->next_interrupt =3D next_interrupt; + uint64_t alarm_ns =3D + t16->reset_time_ns + ((CNT(t16) + alarm_offset) * t16->period_ns); + timer_mod(t16->timer, alarm_ns); + + DB_PRINT("next alarm %" PRIu64 " ns from now", + alarm_offset * t16->period_ns); + +end: + return; +} + +static void avr_timer16_interrupt(void *opaque) +{ + AVRTimer16State *t16 =3D opaque; + uint8_t mode =3D MODE(t16); + + avr_timer16_update_cnt(t16); + + if (CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_FALLING || + CLKSRC(t16) =3D=3D T16_CLKSRC_EXT_RISING || + CLKSRC(t16) =3D=3D T16_CLKSRC_STOPPED) { + /* Timer is disabled or set to external clock source (unsupported)= */ + return; + } + + DB_PRINT("interrupt, cnt =3D %d", CNT(t16)); + + /* Counter overflow */ + if (t16->next_interrupt =3D=3D OVERFLOW) { + DB_PRINT("0xffff overflow"); + avr_timer16_clock_reset(t16); + if (t16->imsk & T16_INT_TOV) { + t16->ifr |=3D T16_INT_TOV; + qemu_set_irq(t16->ovf_irq, 1); + } + } + /* Check for ocra overflow in CTC mode */ + if (mode =3D=3D T16_MODE_CTC_OCRA && t16->next_interrupt =3D=3D COMPA)= { + DB_PRINT("CTC OCRA overflow"); + avr_timer16_clock_reset(t16); + } + /* Check for icr overflow in CTC mode */ + if (mode =3D=3D T16_MODE_CTC_ICR && t16->next_interrupt =3D=3D CAPT) { + DB_PRINT("CTC ICR overflow"); + avr_timer16_clock_reset(t16); + if (t16->imsk & T16_INT_IC) { + t16->ifr |=3D T16_INT_IC; + qemu_set_irq(t16->capt_irq, 1); + } + } + /* Check for output compare interrupts */ + if (t16->imsk & T16_INT_OCA && t16->next_interrupt =3D=3D COMPA) { + t16->ifr |=3D T16_INT_OCA; + qemu_set_irq(t16->compa_irq, 1); + } + if (t16->imsk & T16_INT_OCB && t16->next_interrupt =3D=3D COMPB) { + t16->ifr |=3D T16_INT_OCB; + qemu_set_irq(t16->compb_irq, 1); + } + if (t16->imsk & T16_INT_OCC && t16->next_interrupt =3D=3D COMPC) { + t16->ifr |=3D T16_INT_OCC; + qemu_set_irq(t16->compc_irq, 1); + } + avr_timer16_set_alarm(t16); +} + +static void avr_timer16_reset(DeviceState *dev) +{ + AVRTimer16State *t16 =3D 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 siz= e) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + uint8_t retval =3D 0; + + switch (offset) { + case T16_CRA: + retval =3D t16->cra; + break; + case T16_CRB: + retval =3D t16->crb; + break; + case T16_CRC: + retval =3D t16->crc; + break; + case T16_CNTL: + avr_timer16_update_cnt(t16); + t16->rtmp =3D t16->cnth; + retval =3D t16->cntl; + break; + case T16_CNTH: + retval =3D 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 =3D t16->icrh; + retval =3D t16->icrl; + break; + case T16_ICRH: + retval =3D t16->rtmp; + break; + case T16_OCRAL: + retval =3D t16->ocral; + break; + case T16_OCRAH: + retval =3D t16->ocrah; + break; + case T16_OCRBL: + retval =3D t16->ocrbl; + break; + case T16_OCRBH: + retval =3D t16->ocrbh; + break; + case T16_OCRCL: + retval =3D t16->ocrcl; + break; + case T16_OCRCH: + retval =3D t16->ocrch; + break; + default: + break; + } + return (uint64_t)retval; +} + +static void avr_timer16_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + uint8_t val8 =3D (uint8_t)val64; + uint8_t prev_clk_src =3D CLKSRC(t16); + + DB_PRINT("write %d to offset %d", val8, (uint8_t)offset); + + switch (offset) { + case T16_CRA: + t16->cra =3D val8; + if (t16->cra & T16_CRA_OC_CONF) { + ERROR("output compare pins unsupported"); + } + break; + case T16_CRB: + t16->crb =3D val8; + if (t16->crb & T16_CRB_ICNC) { + ERROR("input capture noise canceller unsupported"); + } + if (t16->crb & T16_CRB_ICES) { + ERROR("input capture unsupported"); + } + if (CLKSRC(t16) !=3D prev_clk_src) { + avr_timer16_clksrc_update(t16); + if (prev_clk_src =3D=3D T16_CLKSRC_STOPPED) { + t16->reset_time_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUA= L); + } + } + break; + case T16_CRC: + t16->crc =3D val8; + ERROR("output compare pins unsupported"); + 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 =3D val8; + t16->cnth =3D t16->rtmp; + avr_timer16_recalc_reset_time(t16); + break; + case T16_CNTH: + t16->rtmp =3D val8; + break; + case T16_ICRL: + /* ICR can only be written in mode T16_MODE_CTC_ICR */ + if (MODE(t16) =3D=3D T16_MODE_CTC_ICR) { + t16->icrl =3D val8; + t16->icrh =3D t16->rtmp; + } + break; + case T16_ICRH: + if (MODE(t16) =3D=3D T16_MODE_CTC_ICR) { + t16->rtmp =3D 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 =3D val8; + break; + case T16_OCRAH: + t16->ocrah =3D val8; + break; + case T16_OCRBL: + t16->ocrbl =3D val8; + break; + case T16_OCRBH: + t16->ocrbh =3D val8; + break; + case T16_OCRCL: + t16->ocrcl =3D val8; + break; + case T16_OCRCH: + t16->ocrch =3D val8; + break; + default: + break; + } + avr_timer16_set_alarm(t16); +} + +static uint64_t avr_timer16_imsk_read(void *opaque, + hwaddr offset, + unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return 0; + } + return t16->imsk; +} + +static void avr_timer16_imsk_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return; + } + t16->imsk =3D (uint8_t)val64; +} + +static uint64_t avr_timer16_ifr_read(void *opaque, + hwaddr offset, + unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return 0; + } + return t16->ifr; +} + +static void avr_timer16_ifr_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + assert(size =3D=3D 1); + AVRTimer16State *t16 =3D opaque; + if (offset !=3D 0) { + return; + } + t16->ifr =3D (uint8_t)val64; +} + +static const MemoryRegionOps avr_timer16_ops =3D { + .read =3D avr_timer16_read, + .write =3D avr_timer16_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .impl =3D {.max_access_size =3D 1} +}; + +static const MemoryRegionOps avr_timer16_imsk_ops =3D { + .read =3D avr_timer16_imsk_read, + .write =3D avr_timer16_imsk_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .impl =3D {.max_access_size =3D 1} +}; + +static const MemoryRegionOps avr_timer16_ifr_ops =3D { + .read =3D avr_timer16_ifr_read, + .write =3D avr_timer16_ifr_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .impl =3D {.max_access_size =3D 1} +}; + +static Property avr_timer16_properties[] =3D { + DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State, + cpu_freq_hz, 20000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void avr_timer16_pr(void *opaque, int irq, int level) +{ + AVRTimer16State *s =3D AVR_TIMER16(opaque); + + s->enabled =3D !level; + + if (!s->enabled) { + avr_timer16_reset(DEVICE(s)); + } +} + +static void avr_timer16_init(Object *obj) +{ + AVRTimer16State *s =3D 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, TYPE_AVR_TIMER16, 0xe); + memory_region_init_io(&s->imsk_iomem, obj, &avr_timer16_imsk_ops, + s, TYPE_AVR_TIMER16, 0x1); + memory_region_init_io(&s->ifr_iomem, obj, &avr_timer16_ifr_ops, + s, TYPE_AVR_TIMER16, 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); + + s->timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_timer16_interrupt, s= ); + s->enabled =3D true; +} + +static void avr_timer16_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->reset =3D avr_timer16_reset; + dc->props =3D avr_timer16_properties; +} + +static const TypeInfo avr_timer16_info =3D { + .name =3D TYPE_AVR_TIMER16, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AVRTimer16State), + .instance_init =3D avr_timer16_init, + .class_init =3D avr_timer16_class_init, +}; + +static void avr_timer16_register_types(void) +{ + type_register_static(&avr_timer16_info); +} + +type_init(avr_timer16_register_types) diff --git a/include/hw/char/avr_usart.h b/include/hw/char/avr_usart.h new file mode 100644 index 0000000000..8e9ee88bbd --- /dev/null +++ b/include/hw/char/avr_usart.h @@ -0,0 +1,97 @@ +/* + * AVR USART + * + * Copyright (c) 2018 University of Kent + * Author: Sarah Harris + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * 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_AVR_USART_H +#define HW_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 { + /* */ + SysBusDevice parent_obj; + + /* */ + 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_AVR_USART_H */ diff --git a/include/hw/misc/avr_mask.h b/include/hw/misc/avr_mask.h new file mode 100644 index 0000000000..d3e21972d8 --- /dev/null +++ b/include/hw/misc/avr_mask.h @@ -0,0 +1,47 @@ +/* + * AVR Power Reduction + * + * Copyright (c) 2019 Michael Rolnik + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * 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_avr_mask_H +#define HW_avr_mask_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "hw/hw.h" + + +#define TYPE_AVR_MASK "avr-mask" +#define AVR_MASK(obj) OBJECT_CHECK(AVRMaskState, (obj), TYPE_AVR_MASK) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + + uint8_t val; + qemu_irq irq[8]; +} AVRMaskState; + +#endif /* HW_avr_mask_H */ diff --git a/include/hw/timer/avr_timer16.h b/include/hw/timer/avr_timer16.h new file mode 100644 index 0000000000..5639074ce5 --- /dev/null +++ b/include/hw/timer/avr_timer16.h @@ -0,0 +1,97 @@ +/* + * AVR 16 bit timer + * + * Copyright (c) 2018 University of Kent + * Author: Ed Robbins + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * 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 OT= HER + * 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. + */ + +/* + * 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 AVR_TIMER16_H +#define 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 { + /* */ + SysBusDevice parent_obj; + + /* */ + 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; + + uint64_t cpu_freq_hz; + uint64_t freq_hz; + uint64_t period_ns; + uint64_t reset_time_ns; + enum NextInterrupt next_interrupt; +} AVRTimer16State; + +#endif /* AVR_TIMER16_H */ --=20 2.17.2 (Apple Git-113) From nobody Thu Apr 25 14:09:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1562909874; cv=none; d=zoho.com; s=zohoarc; b=ctQRaR6ygtUV48hwIQfIEypI1sf6c2tlyybGH2rVv3v81Dn076CAhj+aIvtVADGnDlnoAAbG5llWY2Xw+vDUb+a6B2icDN8xCWhhPKleDBM0qw50BiaeZCzEEXsRUh3U3RGLJk4Hs5JA0BlK3tEnTcwitIw8ADMzHtp0CriKwuE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1562909874; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=Ey13EClBuaHWKYYTeDob7odWNTfhNM4Ye8gssDzgmuY=; b=TM6Vn2xE7ydgkFeM8IjqWGYT6VaI5gD4GIV6YtovG/jKOmf9JuPITT+Fff50kv2f8AlJWqNl2ZqtvIHzwbVxqDhpr8qS1daNUqN5IdSaeYoK16LVDq+s3GbwVVqR+bMbPrigGaut7y7+PeCMMs+DJxpQ9SGtSyaWWs9fRDgNW8s= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1562909874866919.859172477974; Thu, 11 Jul 2019 22:37:54 -0700 (PDT) Received: from localhost ([::1]:46694 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFl-0007CY-Kg for importer@patchew.org; Fri, 12 Jul 2019 01:37:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35106) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFM-0005i4-H0 for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hloFH-0007jb-60 for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:26 -0400 Received: from mail-wr1-x444.google.com ([2a00:1450:4864:20::444]:46468) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hloFG-0007iq-II for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:22 -0400 Received: by mail-wr1-x444.google.com with SMTP id z1so8507281wru.13 for ; Thu, 11 Jul 2019 22:37:22 -0700 (PDT) Received: from 8c859074c0ff.ant.amazon.com.com (bzq-109-66-61-47.red.bezeqint.net. [109.66.61.47]) by smtp.gmail.com with ESMTPSA id y6sm7824009wmd.16.2019.07.11.22.37.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Jul 2019 22:37:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Ey13EClBuaHWKYYTeDob7odWNTfhNM4Ye8gssDzgmuY=; b=q7lz/K0Qw0lYdhtsXg9e+daLjCKyW896lF7aKsWatCn93eLArFI71hEYt5mLQws5so VY3ScX3BNXG0QSUrhSDpyp8U0gQgq4Qjt8VP+c2bkA2WMjOtEVCnIjWEl/SafFITjY+z s7K96UfafKcjYL2U7IU0qwypiY7UGrLLduHoDURS0x0qlf2lj+EXhJoArLqDThTt3h7M QS4o94+Dtm/FHvPHQRjz3bSsiDhOW/ZWyBpnY1sSTdhuz/23KW+zDe8VCz2u69I2MLSu 7Xn0BnkL3mdh08SuRnSkQEHhKtNsVzRH15YYRRGV1nxsf9OOyeChzOoawlZdnjZ3f6Lo 4RAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Ey13EClBuaHWKYYTeDob7odWNTfhNM4Ye8gssDzgmuY=; b=XuJHULP/lCf9l6MrQla7JUE7XMQYpfDKsKrzomltYMCWkNvMhKquXOdXvX0J+Rx1Yw M9YuiObn8vZS9FUsCzJo+tXMQkK+dpzLuv2jyIYPNPzdWhJs48wMZHQ7en2fKTmHgJWT 8or3YGIuTfbDc2kjd7VjvUwOTmoNIYhyJ6+GWPPff5RtAvm6X/5KegE0W3aAjtl1ZjrC mNNM2TjMYPIEaZtkcto/sfJ/SGzV8Q+qH8bCnPCuDpv5Q/2DWJV6GfpLmwvOeFNJCfDX xk044DDUxCeo8XSCwnFKLS3zOlPlFhKnPdI75UMyX6QJVBoH5H1XkuNA9QoJuwqkn2gz ILww== X-Gm-Message-State: APjAAAUW8qQPwX1tadVxWfTABe87951g4/fJUlCoMvk/FEmR4T6FQOoZ 6J0Dy6l7XdlFK3HFxmNnRG2MRfVamgoqrQ== X-Google-Smtp-Source: APXvYqx284zkMFVTqJgaGkyAkgLb+QhdpE4pUpZ0pc777580LKc8lqAkEKavbgLZ8gpQHgQ+WTdDCw== X-Received: by 2002:a5d:4e50:: with SMTP id r16mr3056541wrt.227.1562909841160; Thu, 11 Jul 2019 22:37:21 -0700 (PDT) From: Michael Rolnik To: qemu-devel@nongnu.org Date: Fri, 12 Jul 2019 08:37:03 +0300 Message-Id: <20190712053704.21228-7-mrolnik@gmail.com> X-Mailer: git-send-email 2.17.2 (Apple Git-113) In-Reply-To: <20190712053704.21228-1-mrolnik@gmail.com> References: <20190712053704.21228-1-mrolnik@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::444 Subject: [Qemu-devel] [PATCH v26 6/7] target/avr: Add example board configuration X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: imammedo@redhat.com, Sarah Harris , Michael Rolnik , richard.henderson@linaro.org, philmd@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Sarah Harris A simple board setup that configures an AVR CPU to run a given firmware ima= ge. This is all that's useful to implement without peripheral emulation as AVR = CPUs include a lot of on-board peripherals. Signed-off-by: Michael Rolnik --- hw/Kconfig | 1 + hw/avr/Kconfig | 5 + hw/avr/Makefile.objs | 1 + hw/avr/sample.c | 237 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 hw/avr/Kconfig create mode 100644 hw/avr/Makefile.objs create mode 100644 hw/avr/sample.c diff --git a/hw/Kconfig b/hw/Kconfig index 195f541e50..1f25636855 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -42,6 +42,7 @@ source watchdog/Kconfig # arch Kconfig source arm/Kconfig source alpha/Kconfig +source avr/Kconfig source cris/Kconfig source hppa/Kconfig source i386/Kconfig diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig new file mode 100644 index 0000000000..dd02a4c37a --- /dev/null +++ b/hw/avr/Kconfig @@ -0,0 +1,5 @@ +config AVR_SAMPLE + bool + select AVR_TIMER16 + select AVR_USART + select AVR_MASK diff --git a/hw/avr/Makefile.objs b/hw/avr/Makefile.objs new file mode 100644 index 0000000000..626b7064b3 --- /dev/null +++ b/hw/avr/Makefile.objs @@ -0,0 +1 @@ +obj-y +=3D sample.o diff --git a/hw/avr/sample.c b/hw/avr/sample.c new file mode 100644 index 0000000000..563edbd417 --- /dev/null +++ b/hw/avr/sample.c @@ -0,0 +1,237 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2019 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 + * + */ + +/* + * NOTE: + * This is not a real AVR board, this is an example! + * The CPU is an approximation of an ATmega2560, but is missing vario= us + * built-in peripherals. + * + * This example board loads provided binary file into flash memory and + * executes it from 0x00000000 address in the code memory space. + * + * Currently used for AVR CPU validation + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "ui/console.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "qemu/error-report.h" +#include "exec/address-spaces.h" +#include "include/hw/sysbus.h" +#include "include/hw/char/avr_usart.h" +#include "include/hw/timer/avr_timer16.h" +#include "include/hw/misc/avr_mask.h" +#include "elf.h" + +#define SIZE_FLASH 0x00040000 +#define SIZE_SRAM 0x00002200 +/* + * Size of additional "external" memory, as if the AVR were configured to = use + * an external RAM chip. + * Note that the configuration registers that normally enable this feature= are + * unimplemented. + */ +#define SIZE_EXMEM 0x00000000 + +/* Offsets of periphals in emulated memory space (i.e. not host addresses)= */ +#define PRR0_BASE 0x64 +#define PRR1_BASE 0x65 +#define USART_BASE 0xc0 +#define TIMER1_BASE 0x80 +#define TIMER1_IMSK_BASE 0x6f +#define TIMER1_IFR_BASE 0x36 + +/* Interrupt numbers used by peripherals */ +#define USART_RXC_IRQ 24 +#define USART_DRE_IRQ 25 +#define USART_TXC_IRQ 26 + +#define TIMER1_CAPT_IRQ 15 +#define TIMER1_COMPA_IRQ 16 +#define TIMER1_COMPB_IRQ 17 +#define TIMER1_COMPC_IRQ 18 +#define TIMER1_OVF_IRQ 19 + +/* Power reduction */ +#define PRR1_BIT_PRTIM5 0x05 /* Timer/Counter5 */ +#define PRR1_BIT_PRTIM4 0x04 /* Timer/Counter4 */ +#define PRR1_BIT_PRTIM3 0x03 /* Timer/Counter3 */ +#define PRR1_BIT_PRUSART3 0x02 /* USART3 */ +#define PRR1_BIT_PRUSART2 0x01 /* USART2 */ +#define PRR1_BIT_PRUSART1 0x00 /* USART1 */ + +#define PRR0_BIT_PRTWI 0x06 /* TWI */ +#define PRR0_BIT_PRTIM2 0x05 /* Timer/Counter2 */ +#define PRR0_BIT_PRTIM0 0x04 /* Timer/Counter0 */ +#define PRR0_BIT_PRTIM1 0x03 /* Timer/Counter1 */ +#define PRR0_BIT_PRSPI 0x02 /* Serial Peripheral Interface */ +#define PRR0_BIT_PRUSART0 0x01 /* USART0 */ +#define PRR0_BIT_PRADC 0x00 /* ADC */ + +typedef struct { + MachineClass parent; +} SampleMachineClass; + +typedef struct { + MachineState parent; + MemoryRegion *ram; + MemoryRegion *flash; + AVRUsartState *usart0; + AVRTimer16State *timer1; + AVRMaskState *prr[2]; +} SampleMachineState; + +#define TYPE_SAMPLE_MACHINE MACHINE_TYPE_NAME("sample") + +#define SAMPLE_MACHINE(obj) \ + OBJECT_CHECK(SampleMachineState, obj, TYPE_SAMPLE_MACHINE) +#define SAMPLE_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SampleMachineClass, obj, TYPE_SAMPLE_MACHINE) +#define SAMPLE_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SampleMachineClass, klass, TYPE_SAMPLE_MACHINE) + +static void sample_init(MachineState *machine) +{ + SampleMachineState *sms =3D SAMPLE_MACHINE(machine); + MemoryRegion *system_memory =3D get_system_memory(); + AVRCPU *cpu; + const char *firmware =3D NULL; + const char *filename; + int bytes_loaded; + SysBusDevice *busdev; + DeviceState *cpudev; + + system_memory =3D get_system_memory(); + sms->ram =3D g_new(MemoryRegion, 1); + sms->flash =3D g_new(MemoryRegion, 1); + + cpu =3D AVR_CPU(cpu_create(machine->cpu_type)); + cpudev =3D DEVICE(cpu); + + memory_region_allocate_system_memory( + sms->ram, NULL, "avr.ram", SIZE_SRAM + SIZE_EXMEM); + memory_region_add_subregion(system_memory, OFFSET_DATA, sms->ram); + + memory_region_init_rom(sms->flash, NULL, "avr.flash", SIZE_FLASH, + &error_fatal); + memory_region_add_subregion(system_memory, OFFSET_CODE, sms->flash); + + /* Power Reduction built-in peripheral */ + sms->prr[0] =3D AVR_MASK(sysbus_create_simple(TYPE_AVR_MASK, + OFFSET_DATA + PRR0_BASE, NULL)); + sms->prr[1] =3D AVR_MASK(sysbus_create_simple(TYPE_AVR_MASK, + OFFSET_DATA + PRR1_BASE, NULL)); + + /* USART 0 built-in peripheral */ + sms->usart0 =3D AVR_USART(object_new(TYPE_AVR_USART)); + busdev =3D SYS_BUS_DEVICE(sms->usart0); + qdev_prop_set_chr(DEVICE(sms->usart0), "chardev", serial_hd(0)); + object_property_set_bool(OBJECT(sms->usart0), true, "realized", + &error_fatal); + sysbus_mmio_map(busdev, 0, OFFSET_DATA + USART_BASE); + /* + * These IRQ numbers don't match the datasheet because we're counting = from + * zero and not including reset. + */ + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(cpudev, USART_RXC_IRQ)); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(cpudev, USART_DRE_IRQ)); + sysbus_connect_irq(busdev, 2, qdev_get_gpio_in(cpudev, USART_TXC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(sms->prr[1]), PRR1_BIT_PRUSART1, + qdev_get_gpio_in(DEVICE(sms->usart0), 0)); + + /* Timer 1 built-in periphal */ + sms->timer1 =3D AVR_TIMER16(object_new(TYPE_AVR_TIMER16)); + object_property_set_bool(OBJECT(sms->timer1), true, "realized", + &error_fatal); + busdev =3D SYS_BUS_DEVICE(sms->timer1); + sysbus_mmio_map(busdev, 0, OFFSET_DATA + TIMER1_BASE); + sysbus_mmio_map(busdev, 1, OFFSET_DATA + TIMER1_IMSK_BASE); + sysbus_mmio_map(busdev, 2, OFFSET_DATA + TIMER1_IFR_BASE); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(cpudev, TIMER1_CAPT_IRQ= )); + sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(cpudev, TIMER1_COMPA_IR= Q)); + sysbus_connect_irq(busdev, 2, qdev_get_gpio_in(cpudev, TIMER1_COMPB_IR= Q)); + sysbus_connect_irq(busdev, 3, qdev_get_gpio_in(cpudev, TIMER1_COMPC_IR= Q)); + sysbus_connect_irq(busdev, 4, qdev_get_gpio_in(cpudev, TIMER1_OVF_IRQ)= ); + sysbus_connect_irq(SYS_BUS_DEVICE(sms->prr[0]), PRR0_BIT_PRTIM1, + qdev_get_gpio_in(DEVICE(sms->timer1), 0)); + + /* Load firmware (contents of flash) trying to auto-detect format */ + firmware =3D machine->firmware; + if (firmware !=3D NULL) { + filename =3D qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware); + if (filename =3D=3D NULL) { + error_report("Unable to find %s", firmware); + exit(1); + } + + bytes_loaded =3D load_elf( + filename, NULL, NULL, NULL, NULL, NULL, NULL, 0, EM_NONE, 0, 0= ); + if (bytes_loaded < 0) { + error_report( + "Unable to load %s as ELF, trying again as raw binary", + firmware); + bytes_loaded =3D load_image_targphys( + filename, OFFSET_CODE, SIZE_FLASH); + } + if (bytes_loaded < 0) { + error_report( + "Unable to load firmware image %s as ELF or raw binary", + firmware); + exit(1); + } + } +} + +static void sample_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc =3D MACHINE_CLASS(oc); + + mc->desc =3D "AVR sample/example board (ATmega2560)"; + mc->init =3D sample_init; + mc->default_cpus =3D 1; + mc->min_cpus =3D mc->default_cpus; + mc->max_cpus =3D mc->default_cpus; + mc->default_cpu_type =3D "avr6-avr-cpu"; /* ATmega2560. */ + mc->is_default =3D 1; +} + +static const TypeInfo sample_info =3D { + .name =3D TYPE_SAMPLE_MACHINE, + .parent =3D TYPE_MACHINE, + .instance_size =3D sizeof(SampleMachineState), + .class_size =3D sizeof(SampleMachineClass), + .class_init =3D sample_class_init, +}; + +static void sample_machine_init(void) +{ + type_register_static(&sample_info); +} + +type_init(sample_machine_init); --=20 2.17.2 (Apple Git-113) From nobody Thu Apr 25 14:09:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1562909921; cv=none; d=zoho.com; s=zohoarc; b=CP+AUnlBZMW0fjCIlRzc23qyLmsW+DaHfeB7HoT44FLBZY3UzL2OA0DV5nnLirKe7Ufji5KloNlCr/iLIxsgaGmUKW9LREc7QUVb7EJTBx5F7XDLS1ymhw1leTbkreRM9TApzHduCsGjVxEDwpCBqT4vaPx/OyUP8PhoSiXgcAk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1562909921; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=UIhsGy04dsjgJibOFfGaAvr1JXdhmdGtxDKJUOCNxfs=; b=M2CKwy0XIFrx7cJbRqVct0eyJe+RCnmbW6RiyqwMtBO5QiMQ1n8v33EXe5aaZae65mhc6+LCbBQYm6AwqrmMGhBJKbnLvNA4xixip2GKZBo+oLXaifaJj3D13FAwegRlr1HLLnzO5rjVkeivS+IogVZp/I0kgVUdNzBlflZCRXs= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1562909921701768.5653390367471; Thu, 11 Jul 2019 22:38:41 -0700 (PDT) Received: from localhost ([::1]:46716 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloGT-0001ql-74 for importer@patchew.org; Fri, 12 Jul 2019 01:38:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35107) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hloFM-0005i9-Gt for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hloFI-0007kh-PP for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:26 -0400 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:52045) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hloFI-0007jr-AP for qemu-devel@nongnu.org; Fri, 12 Jul 2019 01:37:24 -0400 Received: by mail-wm1-x341.google.com with SMTP id 207so7652737wma.1 for ; Thu, 11 Jul 2019 22:37:24 -0700 (PDT) Received: from 8c859074c0ff.ant.amazon.com.com (bzq-109-66-61-47.red.bezeqint.net. [109.66.61.47]) by smtp.gmail.com with ESMTPSA id y6sm7824009wmd.16.2019.07.11.22.37.21 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Jul 2019 22:37:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=UIhsGy04dsjgJibOFfGaAvr1JXdhmdGtxDKJUOCNxfs=; b=NTcKNFZAaWy/7q/z4Iq6KhDxH0It94g0Q2Q82/l2VF+0S7pAAFGb0spyWdj1KNWU/e WcaLqoxr674JDsvIuxAcpHSOLhnvzkRtQBHn2AwNjm8K6LiZTDVx+8ytewFRUJ10cloR OpJxGwkDZm0b1Sssatm5ZpVeP3B9aQ5t3xv8fIL3seskza3YqdC83W5fZ2xxKzw0w6Q5 tl0qmldEUiqI2hRzjXFxEz/Jn17heZP7zkLyFJRwuqJaQoNZbJAbR3UyY/txQ1j134Fr ooYQBblxR0mmj95nT8sn+dgmCqnrTJEhtlcqvpOFiNbAXh8F5rt/RS+GZgaMJvn1CPVL U5qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=UIhsGy04dsjgJibOFfGaAvr1JXdhmdGtxDKJUOCNxfs=; b=TO+ytwsifdc/fcgV66zp8Yo28aZHB1vF0HFGe4Jy4mWG5HDEhxxZ7z2yqwG+Trl6yL VEVvIb7mx/buDht0eo35GjSNKISUbo0ZmMOEzUwbPakJ3Ncd2OO8UGzCS2BuY0xUyDG9 0uG0IfrB7G/KSRb0qi3WLq64Vs211B0+SY0vHOFYgNWiLkvJsTYRmxjFc0cCMPWmq1sK KS9EAj3vetvXcisyUs0DjVqfsOwFu8dG6peto2yIR+vblY457mFwiaTCVX4MO+W+9lAz 9R0BWWW1MmmttY/wGmHP+/s6ChUXtGzW4m6j2kte57LwTlJij5uBg4ZkgMUeLs5Zghqj hkCA== X-Gm-Message-State: APjAAAVe3CUhAJiKf8OWpqKs9LNEdc1li/8dc23ccQw9z4wUtfaVKNgU YDMPgvY+YxsvVV10/qqn4tGraPI6HYA= X-Google-Smtp-Source: APXvYqwRy98hNmuDVRpASlV9lrKSM5JeoOttCgbYjrfCHXYvCs47ct+dn5KGS8aQUfnPYXy1Ws4AGw== X-Received: by 2002:a1c:6c14:: with SMTP id h20mr2036459wmc.168.1562909842658; Thu, 11 Jul 2019 22:37:22 -0700 (PDT) From: Michael Rolnik To: qemu-devel@nongnu.org Date: Fri, 12 Jul 2019 08:37:04 +0300 Message-Id: <20190712053704.21228-8-mrolnik@gmail.com> X-Mailer: git-send-email 2.17.2 (Apple Git-113) In-Reply-To: <20190712053704.21228-1-mrolnik@gmail.com> References: <20190712053704.21228-1-mrolnik@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::341 Subject: [Qemu-devel] [PATCH v26 7/7] target/avr: Register AVR support with the rest of QEMU, the build system, and the MAINTAINERS file X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: imammedo@redhat.com, Michael Rolnik , richard.henderson@linaro.org, philmd@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Michael Rolnik --- MAINTAINERS | 6 ++++++ arch_init.c | 2 ++ configure | 7 +++++++ default-configs/avr-softmmu.mak | 5 +++++ include/disas/dis-asm.h | 6 ++++++ include/sysemu/arch_init.h | 1 + qapi/common.json | 3 ++- target/avr/Makefile.objs | 33 +++++++++++++++++++++++++++++++++ tests/machine-none-test.c | 1 + 9 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 default-configs/avr-softmmu.mak create mode 100644 target/avr/Makefile.objs diff --git a/MAINTAINERS b/MAINTAINERS index cc9636b43a..934ad5739b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -163,6 +163,12 @@ S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* =20 +AVR TCG CPUs +M: Michael Rolnik +S: Maintained +F: target/avr/ +F: hw/avr/ + CRIS TCG CPUs M: Edgar E. Iglesias S: Maintained diff --git a/arch_init.c b/arch_init.c index 74b0708634..413ad7acfd 100644 --- a/arch_init.c +++ b/arch_init.c @@ -85,6 +85,8 @@ int graphic_depth =3D 32; #define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_XTENSA) #define QEMU_ARCH QEMU_ARCH_XTENSA +#elif defined(TARGET_AVR) +#define QEMU_ARCH QEMU_ARCH_AVR #endif =20 const uint32_t arch_type =3D QEMU_ARCH; diff --git a/configure b/configure index 4983c8b533..ab8ebba100 100755 --- a/configure +++ b/configure @@ -7503,6 +7503,10 @@ case "$target_name" in target_compiler=3D$cross_cc_aarch64 eval "target_compiler_cflags=3D\$cross_cc_cflags_${target_name}" ;; + avr) + gdb_xml_files=3D"avr-cpu.xml" + target_compiler=3D$cross_cc_avr + ;; cris) target_compiler=3D$cross_cc_cris ;; @@ -7780,6 +7784,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do disas_config "ARM_A64" fi ;; + avr) + disas_config "AVR" + ;; cris) disas_config "CRIS" ;; diff --git a/default-configs/avr-softmmu.mak b/default-configs/avr-softmmu.= mak new file mode 100644 index 0000000000..d1e1c28118 --- /dev/null +++ b/default-configs/avr-softmmu.mak @@ -0,0 +1,5 @@ +# Default configuration for avr-softmmu + +# Boards: +# +CONFIG_AVR_SAMPLE=3Dy diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index e9c7dd8eb4..8bedce17ac 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -211,6 +211,12 @@ enum bfd_architecture #define bfd_mach_m32r 0 /* backwards compatibility */ bfd_arch_mn10200, /* Matsushita MN10200 */ bfd_arch_mn10300, /* Matsushita MN10300 */ + bfd_arch_avr, /* Atmel AVR microcontrollers. */ +#define bfd_mach_avr1 1 +#define bfd_mach_avr2 2 +#define bfd_mach_avr3 3 +#define bfd_mach_avr4 4 +#define bfd_mach_avr5 5 bfd_arch_cris, /* Axis CRIS */ #define bfd_mach_cris_v0_v10 255 #define bfd_mach_cris_v32 32 diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 10cbafe970..aff57bfe61 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -25,6 +25,7 @@ enum { QEMU_ARCH_NIOS2 =3D (1 << 17), QEMU_ARCH_HPPA =3D (1 << 18), QEMU_ARCH_RISCV =3D (1 << 19), + QEMU_ARCH_AVR =3D (1 << 20), }; =20 extern const uint32_t arch_type; diff --git a/qapi/common.json b/qapi/common.json index 99d313ef3b..6866c3e81d 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -183,11 +183,12 @@ # is true even for "qemu-system-x86_64". # # ppcemb: dropped in 3.1 +# avr: added in 4.1 # # Since: 3.0 ## { 'enum' : 'SysEmuTarget', - 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', + 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', 'lm= 32', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', 'ppc64', 'riscv32', 'riscv64', 's390x', 'sh4', diff --git a/target/avr/Makefile.objs b/target/avr/Makefile.objs new file mode 100644 index 0000000000..2976affd95 --- /dev/null +++ b/target/avr/Makefile.objs @@ -0,0 +1,33 @@ +# +# QEMU AVR CPU +# +# Copyright (c) 2019 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 +# +# + +DECODETREE =3D $(SRC_PATH)/scripts/decodetree.py +decode-y =3D $(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 +=3D translate.o cpu.o helper.o +obj-y +=3D gdbstub.o +obj-$(CONFIG_SOFTMMU) +=3D machine.o diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c index 5953d31755..3e5c74e73e 100644 --- a/tests/machine-none-test.c +++ b/tests/machine-none-test.c @@ -27,6 +27,7 @@ static struct arch2cpu cpus_map[] =3D { /* tested targets list */ { "arm", "cortex-a15" }, { "aarch64", "cortex-a57" }, + { "avr", "avr6-avr-cpu" }, { "x86_64", "qemu64,apic-id=3D0" }, { "i386", "qemu32,apic-id=3D0" }, { "alpha", "ev67" }, --=20 2.17.2 (Apple Git-113)