From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:37590) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TBY6m-0000XQ-P1 for qemu-devel@nongnu.org; Tue, 11 Sep 2012 17:35:09 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TBY6e-0002dJ-L1 for qemu-devel@nongnu.org; Tue, 11 Sep 2012 17:35:00 -0400 Received: from hall.aurel32.net ([88.191.126.93]:60940) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TBY6d-0002cl-Im for qemu-devel@nongnu.org; Tue, 11 Sep 2012 17:34:52 -0400 Date: Tue, 11 Sep 2012 23:34:43 +0200 From: Aurelien Jarno Message-ID: <20120911213443.GA6791@ohm.aurel32.net> References: <1347236407-10465-1-git-send-email-crwulff@gmail.com> <1347236407-10465-2-git-send-email-crwulff@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <1347236407-10465-2-git-send-email-crwulff@gmail.com> Subject: Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU. List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: crwulff@gmail.com Cc: qemu-devel@nongnu.org On Sun, Sep 09, 2012 at 08:19:59PM -0400, crwulff@gmail.com wrote: > From: Chris Wulff > > Signed-off-by: Chris Wulff > --- > target-nios2/Makefile.objs | 5 + > target-nios2/altera_iic.c | 100 +++ > target-nios2/cpu-qom.h | 69 +++ > target-nios2/cpu.c | 83 +++ > target-nios2/cpu.h | 259 ++++++++ > target-nios2/exec.h | 60 ++ > target-nios2/helper.c | 291 +++++++++ > target-nios2/helper.h | 45 ++ > target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++ > target-nios2/instruction.h | 290 +++++++++ > target-nios2/machine.c | 33 + > target-nios2/mmu.c | 273 +++++++++ > target-nios2/mmu.h | 49 ++ > target-nios2/op_helper.c | 125 ++++ > target-nios2/translate.c | 252 ++++++++ > 15 files changed, 3397 insertions(+) > create mode 100644 target-nios2/Makefile.objs > create mode 100644 target-nios2/altera_iic.c > create mode 100644 target-nios2/cpu-qom.h > create mode 100644 target-nios2/cpu.c > create mode 100644 target-nios2/cpu.h > create mode 100644 target-nios2/exec.h > create mode 100644 target-nios2/helper.c > create mode 100644 target-nios2/helper.h > create mode 100644 target-nios2/instruction.c > create mode 100644 target-nios2/instruction.h > create mode 100644 target-nios2/machine.c > create mode 100644 target-nios2/mmu.c > create mode 100644 target-nios2/mmu.h > create mode 100644 target-nios2/op_helper.c > create mode 100644 target-nios2/translate.c > > diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs > new file mode 100644 > index 0000000..d072795 > --- /dev/null > +++ b/target-nios2/Makefile.objs > @@ -0,0 +1,5 @@ > +obj-y += translate.o op_helper.o helper.o cpu.o instruction.o > +obj-$(CONFIG_SOFTMMU) += mmu.o machine.o > +obj-y += altera_iic.o > + > +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) > diff --git a/target-nios2/altera_iic.c b/target-nios2/altera_iic.c > new file mode 100644 > index 0000000..d45d6fa > --- /dev/null > +++ b/target-nios2/altera_iic.c > @@ -0,0 +1,100 @@ > +/* > + * QEMU Altera Internal Interrupt Controller. > + * > + * Copyright (c) 2012 Chris Wulff > + * > + * 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 "hw/sysbus.h" > +#include "dyngen-exec.h" > +#include "cpu.h" > + > +struct altera_iic { > + SysBusDevice busdev; > + Nios2CPU *cpu; > + qemu_irq parent_irq; > +}; > + > +static void update_irq(struct altera_iic *pv) > +{ > + uint32_t i; > + > + if ((pv->cpu->env.regs[CR_STATUS] & CR_STATUS_PIE) == 0) { > + qemu_irq_lower(pv->parent_irq); > + return; > + } > + > + for (i = 0; i < 32; i++) { > + if (pv->cpu->env.regs[CR_IPENDING] & > + pv->cpu->env.regs[CR_IENABLE] & (1 << i)) { > + break; > + } > + } > + if (i == 32) { > + qemu_irq_lower(pv->parent_irq); > + } else { > + qemu_irq_raise(pv->parent_irq); > + } > +} > + > +static void irq_handler(void *opaque, int irq, int level) > +{ > + struct altera_iic *pv = opaque; > + > + pv->cpu->env.regs[CR_IPENDING] &= ~(1 << irq); > + pv->cpu->env.regs[CR_IPENDING] |= level << irq; > + > + update_irq(pv); > +} > + > +static int altera_iic_init(SysBusDevice *dev) > +{ > + struct altera_iic *pv = FROM_SYSBUS(typeof(*pv), dev); > + pv->cpu = g_cpu; /* TODO: Get attached CPU instead somehow... */ > + > + qdev_init_gpio_in(&dev->qdev, irq_handler, 32); > + sysbus_init_irq(dev, &pv->parent_irq); > + > + return 0; > +} > + > +static Property altera_iic_properties[] = { > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void altera_iic_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); > + > + k->init = altera_iic_init; > + dc->props = altera_iic_properties; > +} > + > +static TypeInfo altera_iic_info = { > + .name = "altera,iic", > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(struct altera_iic), > + .class_init = altera_iic_class_init, > +}; > + > +static void altera_iic_register(void) > +{ > + type_register_static(&altera_iic_info); > +} > + > +type_init(altera_iic_register) > + > diff --git a/target-nios2/cpu-qom.h b/target-nios2/cpu-qom.h > new file mode 100644 > index 0000000..be6fa65 > --- /dev/null > +++ b/target-nios2/cpu-qom.h > @@ -0,0 +1,69 @@ > +/* > + * QEMU Nios II CPU > + * > + * Copyright (c) 2012 Chris Wulff > + * > + * 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 QEMU_NIOS2_CPU_QOM_H > +#define QEMU_NIOS2_CPU_QOM_H > + > +#include "qemu/cpu.h" > + > +#define TYPE_NIOS2_CPU "nios2-cpu" > + > +#define NIOS2_CPU_CLASS(klass) \ > + OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU) > +#define NIOS2_CPU(obj) \ > + OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU) > +#define NIOS2_CPU_GET_CLASS(obj) \ > + OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU) > + > +/** > + * Nios2CPUClass: > + * @parent_reset: The parent class' reset handler. > + * > + * A Nios2 CPU model. > + */ > +typedef struct Nios2CPUClass { > + /*< private >*/ > + CPUClass parent_class; > + /*< public >*/ > + > + void (*parent_reset)(CPUState *cpu); > +} Nios2CPUClass; > + > +/** > + * Nios2CPU: > + * @env: #CPUNios2State > + * > + * A Nios2 CPU. > + */ > +typedef struct Nios2CPU { > + /*< private >*/ > + CPUState parent_obj; > + /*< public >*/ > + > + CPUNios2State env; > +} Nios2CPU; > + > +static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env) > +{ > + return NIOS2_CPU(container_of(env, Nios2CPU, env)); > +} > + > +#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e)) > + > +#endif /* QEMU_NIOS2_CPU_QOM_H */ > diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c > new file mode 100644 > index 0000000..bd819c4 > --- /dev/null > +++ b/target-nios2/cpu.c > @@ -0,0 +1,83 @@ > +/* > + * QEMU Nios II CPU > + * > + * Copyright (c) 2012 Chris Wulff > + * > + * 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 "cpu.h" > +#include "qemu-common.h" > + > + > +/* CPUClass::reset() */ > +static void nios2_cpu_reset(CPUState *s) > +{ > + Nios2CPU *cpu = NIOS2_CPU(s); > + Nios2CPUClass *mcc = NIOS2_CPU_GET_CLASS(cpu); > + CPUNios2State *env = &cpu->env; > + > + if (qemu_loglevel_mask(CPU_LOG_RESET)) { > + qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); > + log_cpu_state(env, 0); > + } > + > + mcc->parent_reset(s); > + > + tlb_flush(env, 1); > + > + memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS); > + env->regs[R_PC] = env->reset_addr; > + > +#if defined(CONFIG_USER_ONLY) > + /* start in user mode with interrupts enabled. */ > + env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE; > +#else > + mmu_init(&env->mmu); > +#endif > +} > + > +static void nios2_cpu_initfn(Object *obj) > +{ > + Nios2CPU *cpu = NIOS2_CPU(obj); > + CPUNios2State *env = &cpu->env; > + > + cpu_exec_init(env); > +} > + > +static void nios2_cpu_class_init(ObjectClass *oc, void *data) > +{ > + CPUClass *cc = CPU_CLASS(oc); > + Nios2CPUClass *mcc = NIOS2_CPU_CLASS(oc); > + > + mcc->parent_reset = cc->reset; > + cc->reset = nios2_cpu_reset; > +} > + > +static const TypeInfo nios2_cpu_type_info = { > + .name = TYPE_NIOS2_CPU, > + .parent = TYPE_CPU, > + .instance_size = sizeof(Nios2CPU), > + .instance_init = nios2_cpu_initfn, > + .class_size = sizeof(Nios2CPUClass), > + .class_init = nios2_cpu_class_init, > +}; > + > +static void nios2_cpu_register_types(void) > +{ > + type_register_static(&nios2_cpu_type_info); > +} > + > +type_init(nios2_cpu_register_types) > diff --git a/target-nios2/cpu.h b/target-nios2/cpu.h > new file mode 100644 > index 0000000..0ee22e6 > --- /dev/null > +++ b/target-nios2/cpu.h > @@ -0,0 +1,259 @@ > +/* > + * Altera Nios II virtual CPU header > + * > + * Copyright (c) 2012 Chris Wulff > + * > + * 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_NIOS2_H > +#define CPU_NIOS2_H > + > +#include "config.h" > +#include "qemu-common.h" > + > +#define TARGET_LONG_BITS 32 > + > +#define CPUArchState struct CPUNios2State > + > +#include "cpu-defs.h" > +#include "softfloat.h" > +struct CPUNios2State; > +typedef struct CPUNios2State CPUNios2State; > +#if !defined(CONFIG_USER_ONLY) > +#include "mmu.h" > +#endif > + > +#define TARGET_HAS_ICE 1 > + > +/* Configuration options for Nios II */ > +#define RESET_ADDRESS 0x00000000 > +#define EXCEPTION_ADDRESS 0x00000004 > +#define FAST_TLB_MISS_ADDRESS 0x00000008 > + > + > +#define ELF_MACHINE EM_ALTERA_NIOS2 > + > +/* GP regs + CR regs + PC */ > +#define NUM_CORE_REGS (32 + 32 + 1) > + > +/* General purpose egister aliases */ > +#define R_ZERO 0 > +#define R_AT 1 > +#define R_RET0 2 > +#define R_RET1 3 > +#define R_ARG0 4 > +#define R_ARG1 5 > +#define R_ARG2 6 > +#define R_ARG3 7 > +#define R_ET 24 > +#define R_BT 25 > +#define R_GP 26 > +#define R_SP 27 > +#define R_FP 28 > +#define R_EA 29 > +#define R_BA 30 > +#define R_RA 31 > + > +/* Control register aliases */ > +#define CR_BASE 32 > +#define CR_STATUS (CR_BASE + 0) > +#define CR_STATUS_PIE (1<<0) > +#define CR_STATUS_U (1<<1) > +#define CR_STATUS_EH (1<<2) > +#define CR_STATUS_IH (1<<3) > +#define CR_STATUS_IL (63<<4) > +#define CR_STATUS_CRS (63<<10) > +#define CR_STATUS_PRS (63<<16) > +#define CR_STATUS_NMI (1<<22) > +#define CR_STATUS_RSIE (1<<23) > +#define CR_ESTATUS (CR_BASE + 1) > +#define CR_BSTATUS (CR_BASE + 2) > +#define CR_IENABLE (CR_BASE + 3) > +#define CR_IPENDING (CR_BASE + 4) > +#define CR_CPUID (CR_BASE + 5) > +#define CR_EXCEPTION (CR_BASE + 7) > +#define CR_PTEADDR (CR_BASE + 8) > +#define CR_PTEADDR_PTBASE_SHIFT 22 > +#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT) > +#define CR_PTEADDR_VPN_SHIFT 2 > +#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT) > +#define CR_TLBACC (CR_BASE + 9) > +#define CR_TLBACC_IGN_SHIFT 25 > +#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT) > +#define CR_TLBACC_C (1<<24) > +#define CR_TLBACC_R (1<<23) > +#define CR_TLBACC_W (1<<22) > +#define CR_TLBACC_X (1<<21) > +#define CR_TLBACC_G (1<<20) > +#define CR_TLBACC_PFN_MASK 0x000FFFFF > +#define CR_TLBMISC (CR_BASE + 10) > +#define CR_TLBMISC_WAY_SHIFT 20 > +#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT) > +#define CR_TLBMISC_RD (1<<19) > +#define CR_TLBMISC_WR (1<<18) > +#define CR_TLBMISC_PID_SHIFT 4 > +#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT) > +#define CR_TLBMISC_DBL (1<<3) > +#define CR_TLBMISC_BAD (1<<2) > +#define CR_TLBMISC_PERM (1<<1) > +#define CR_TLBMISC_D (1<<0) > +#define CR_BADADDR (CR_BASE + 12) > +#define CR_CONFIG (CR_BASE + 13) > +#define CR_MPUBASE (CR_BASE + 14) > +#define CR_MPUACC (CR_BASE + 15) > + > +/* Other registers */ > +#define R_PC 64 > + > +/* Exceptions */ > +#define EXCP_BREAK -1 > +#define EXCP_RESET 0 > +#define EXCP_PRESET 1 > +#define EXCP_IRQ 2 > +#define EXCP_TRAP 3 > +#define EXCP_UNIMPL 4 > +#define EXCP_ILLEGAL 5 > +#define EXCP_UNALIGN 6 > +#define EXCP_UNALIGND 7 > +#define EXCP_DIV 8 > +#define EXCP_SUPERA 9 > +#define EXCP_SUPERI 10 > +#define EXCP_SUPERD 11 > +#define EXCP_TLBD 12 > +#define EXCP_TLBX 13 > +#define EXCP_TLBR 14 > +#define EXCP_TLBW 15 > +#define EXCP_MPUI 16 > +#define EXCP_MPUD 17 > + > +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 > + > +#define NB_MMU_MODES 2 > + > +typedef struct CPUNios2State { > + uint32_t regs[NUM_CORE_REGS]; > + > + /* Addresses that are hard-coded in the FPGA build settings */ > + uint32_t reset_addr; > + uint32_t exception_addr; > + uint32_t fast_tlb_miss_addr; > + > +#if !defined(CONFIG_USER_ONLY) > + struct nios2_mmu mmu; > +#endif > + > + CPU_COMMON > +} CPUNios2State; > + > +#include "cpu-qom.h" > + > +extern Nios2CPU *cpu_nios2_init(const char *cpu_model); > +extern int cpu_nios2_exec(CPUNios2State *s); > +extern void cpu_nios2_close(CPUNios2State *s); > +extern void do_interrupt(CPUNios2State *env); > +extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc); > +extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env); > + > +#define TARGET_PHYS_ADDR_SPACE_BITS 32 > +#define TARGET_VIRT_ADDR_SPACE_BITS 32 > + > +extern Nios2CPU *g_cpu; > + > +static inline CPUNios2State *cpu_init(const char *cpu_model) > +{ > + Nios2CPU *cpu = cpu_nios2_init(cpu_model); > + if (cpu == NULL) { > + return NULL; > + } > + return &cpu->env; > +} > + > +#define cpu_exec cpu_nios2_exec > +#define cpu_gen_code cpu_nios2_gen_code > +#define cpu_signal_handler cpu_nios2_signal_handler > + > +#define CPU_SAVE_VERSION 1 > + > +#define TARGET_PAGE_BITS 12 > + > +/* MMU modes definitions */ > +#define MMU_MODE0_SUFFIX _kernel > +#define MMU_MODE1_SUFFIX _user > +#define MMU_SUPERVISOR_IDX 0 > +#define MMU_USER_IDX 1 > + > +static inline int cpu_mmu_index(CPUNios2State *env) > +{ > + return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX : > + MMU_SUPERVISOR_IDX; > +} > + > +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address, > + int rw, int mmu_idx, int is_softmmu); > +#define cpu_handle_mmu_fault cpu_nios2_handle_mmu_fault > + > +#if defined(CONFIG_USER_ONLY) > +static inline void cpu_clone_regs(CPUNios2State *env, target_ulong newsp) > +{ > + if (newsp) { > + env->regs[R_SP] = newsp; > + } > + env->regs[R_RET0] = 0; > +} > +#endif > + > +static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls) > +{ > +} > + > +static inline int cpu_interrupts_enabled(CPUNios2State *env) > +{ > + return env->regs[CR_STATUS] & CR_STATUS_PIE; > +} > + > +#include "cpu-all.h" > + > +static inline target_ulong cpu_get_pc(CPUNios2State *env) > +{ > + return env->regs[R_PC]; > +} > + > +static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc, > + target_ulong *cs_base, int *flags) > +{ > + *pc = env->regs[R_PC]; > + *cs_base = 0; > + *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U)); > +} > + > +#if !defined(CONFIG_USER_ONLY) > +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr, > + int is_write, int is_exec, int is_asi, int size); > +#endif > + > +static inline bool cpu_has_work(CPUNios2State *env) > +{ > + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); > +} > + > +#include "exec-all.h" > + > +static inline void cpu_pc_from_tb(CPUNios2State *env, TranslationBlock *tb) > +{ > + env->regs[R_PC] = tb->pc; > +} > + > +#endif /* CPU_NIOS2_H */ > + > diff --git a/target-nios2/exec.h b/target-nios2/exec.h > new file mode 100644 > index 0000000..cd4446b > --- /dev/null > +++ b/target-nios2/exec.h > @@ -0,0 +1,60 @@ > +/* > + * Altera Nios II execution defines > + * > + * Copyright (c) 2012 Chris Wulff > + * > + * 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 NIOS2_EXEC_H > +#define NIOS2_EXEC_H > + > +#include "dyngen-exec.h" > + > +register struct CPUNios2State *env asm(AREG0); > + > +#include "cpu.h" > +#include "exec-all.h" > + > +#if !defined(CONFIG_USER_ONLY) > +#include "softmmu_exec.h" > +#endif > + > +static inline int cpu_has_work(CPUState *env) > +{ > + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); > +} > + > +static inline int cpu_halted(CPUState *env) > +{ > + if (!env->halted) { > + return 0; > + } > + > + if (cpu_has_work(env)) { > + env->halted = 0; > + return 0; > + } > + > + return EXCP_HALTED; > +} > + > +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) > +{ > + env->regs[R_PC] = tb->pc; > +} > + > +#endif /* NIOS2_EXEC_H */ > + > diff --git a/target-nios2/helper.c b/target-nios2/helper.c > new file mode 100644 > index 0000000..9b200c9 > --- /dev/null > +++ b/target-nios2/helper.c > @@ -0,0 +1,291 @@ > +/* > + * Altera Nios II helper routines. > + * > + * Copyright (c) 2012 Chris Wulff > + * > + * 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 > +#include > +#include > + > +#include "config.h" > +#include "cpu.h" > +#include "exec-all.h" > +#include "host-utils.h" > + > +#if defined(CONFIG_USER_ONLY) > + > +void do_interrupt(CPUNios2State *env) > +{ > + env->exception_index = -1; > + env->regs[R_EA] = env->regs[R_PC] + 4; > +} > + > +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address, > + int rw, int mmu_idx, int is_softmmu) > +{ > + env->exception_index = 0xaa; > + cpu_dump_state(env, stderr, fprintf, 0); > + return 1; > +} > + > +#else /* !CONFIG_USER_ONLY */ > + > +bool has_mmu = true; > + > +void do_interrupt(CPUNios2State *env) > +{ > + switch (env->exception_index) { > + case EXCP_IRQ: > + assert(env->regs[CR_STATUS] & CR_STATUS_PIE); > + > + qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]); > + > + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; > + env->regs[CR_STATUS] |= CR_STATUS_IH; > + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U); > + > + env->regs[CR_EXCEPTION] &= ~(0x1F << 2); > + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2; > + > + env->regs[R_EA] = env->regs[R_PC] + 4; > + env->regs[R_PC] = env->exception_addr; > + break; > + > + case EXCP_TLBD: > + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { > + qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n", > + env->regs[R_PC]); > + log_cpu_state(env, 0); > + > + /* Fast TLB miss */ > + /* Variation from the spec. Table 3-35 of the cpu reference shows > + * estatus not being changed for TLB miss but this appears to > + * be incorrect. */ > + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; > + env->regs[CR_STATUS] |= CR_STATUS_EH; > + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U); > + > + env->regs[CR_EXCEPTION] &= ~(0x1F << 2); > + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2; > + > + env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL; > + env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; > + > + env->regs[R_EA] = env->regs[R_PC] + 4; > + env->regs[R_PC] = env->fast_tlb_miss_addr; > + } else { > + qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n", > + env->regs[R_PC]); > + > + /* Double TLB miss */ > + env->regs[CR_STATUS] |= CR_STATUS_EH; > + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U); > + > + env->regs[CR_EXCEPTION] &= ~(0x1F << 2); > + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2; > + > + env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL; > + > + env->regs[R_PC] = env->exception_addr; > + } > + break; > + > + case EXCP_TLBR: > + case EXCP_TLBW: > + case EXCP_TLBX: > + qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]); > + log_cpu_state(env, 0); > + > + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; > + env->regs[CR_STATUS] |= CR_STATUS_EH; > + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U); > + > + env->regs[CR_EXCEPTION] &= ~(0x1F << 2); > + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2; > + > + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { > + env->regs[CR_TLBMISC] |= CR_TLBMISC_WR; > + } > + > + env->regs[R_EA] = env->regs[R_PC] + 4; > + env->regs[R_PC] = env->exception_addr; > + break; > + > + case EXCP_SUPERA: > + case EXCP_SUPERI: > + case EXCP_SUPERD: > + qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n", > + env->regs[R_PC]); > + > + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { > + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; > + env->regs[R_EA] = env->regs[R_PC] + 4; > + } > + > + env->regs[CR_STATUS] |= CR_STATUS_EH; > + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U); > + > + env->regs[CR_EXCEPTION] &= ~(0x1F << 2); > + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2; > + > + env->regs[R_PC] = env->exception_addr; > + break; > + > + case EXCP_ILLEGAL: > + case EXCP_TRAP: > + qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n", > + env->regs[R_PC]); > + > + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { > + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; > + env->regs[R_EA] = env->regs[R_PC] + 4; > + } > + > + env->regs[CR_STATUS] |= CR_STATUS_EH; > + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U); > + > + env->regs[CR_EXCEPTION] &= ~(0x1F << 2); > + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2; > + > + env->regs[R_PC] = env->exception_addr; > + break; > + > + case EXCP_BREAK: > + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) { > + env->regs[CR_BSTATUS] = env->regs[CR_STATUS]; > + env->regs[R_BA] = env->regs[R_PC] + 4; > + } > + > + env->regs[CR_STATUS] |= CR_STATUS_EH; > + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U); > + > + env->regs[CR_EXCEPTION] &= ~(0x1F << 2); > + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2; > + > + env->regs[R_PC] = env->exception_addr; > + break; > + > + default: > + cpu_abort(env, "unhandled exception type=%d\n", > + env->exception_index); > + break; > + } > +} > + > +static int cpu_nios2_handle_virtual_page( > + CPUNios2State *env, target_ulong address, int rw, int mmu_idx) > +{ > + target_ulong vaddr, paddr; > + struct nios2_mmu_lookup lu; > + unsigned int hit; > + hit = mmu_translate(env, &lu, address, rw, mmu_idx); > + if (hit) { > + vaddr = address & TARGET_PAGE_MASK; > + paddr = lu.paddr + vaddr - lu.vaddr; > + > + if (((rw == 0) && (lu.prot & PAGE_READ)) || > + ((rw == 1) && (lu.prot & PAGE_WRITE)) || > + ((rw == 2) && (lu.prot & PAGE_EXEC))) { > + > + tlb_set_page(env, vaddr, paddr, lu.prot, > + mmu_idx, TARGET_PAGE_SIZE); > + return 0; > + } else { > + /* Permission violation */ > + env->exception_index = (rw == 0) ? EXCP_TLBR : > + ((rw == 1) ? EXCP_TLBW : > + EXCP_TLBX); > + } > + } else { > + env->exception_index = EXCP_TLBD; > + } > + > + if (rw == 2) { > + env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D; > + } else { > + env->regs[CR_TLBMISC] |= CR_TLBMISC_D; > + } > + env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK; > + env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK; > + env->mmu.pteaddr_wr = env->regs[CR_PTEADDR]; > + env->regs[CR_BADADDR] = address; > + return 1; > +} > + > +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address, > + int rw, int mmu_idx, int is_softmmu) > +{ > + if (has_mmu) { > + if (MMU_SUPERVISOR_IDX == mmu_idx) { > + if (address >= 0xC0000000) { > + /* Kernel physical page - TLB bypassed */ > + address &= TARGET_PAGE_MASK; > + tlb_set_page(env, address, address, PAGE_BITS, > + mmu_idx, TARGET_PAGE_SIZE); > + } else if (address >= 0x80000000) { > + /* Kernel virtual page */ > + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx); > + } else { > + /* User virtual page */ > + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx); > + } > + } else { > + if (address >= 0x80000000) { > + /* Illegal access from user mode */ > + env->exception_index = EXCP_SUPERA; > + env->regs[CR_BADADDR] = address; > + return 1; > + } else { > + /* User virtual page */ > + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx); > + } > + } > + } else { > + /* No MMU */ > + address &= TARGET_PAGE_MASK; > + tlb_set_page(env, address, address, PAGE_BITS, > + mmu_idx, TARGET_PAGE_SIZE); > + } > + > + return 0; > +} > + > +target_phys_addr_t cpu_get_phys_page_debug(CPUNios2State *env, > + target_ulong addr) > +{ > + target_ulong vaddr, paddr = 0; > + struct nios2_mmu_lookup lu; > + unsigned int hit; > + > + if (has_mmu && (addr < 0xC0000000)) { > + hit = mmu_translate(env, &lu, addr, 0, 0); > + if (hit) { > + vaddr = addr & TARGET_PAGE_MASK; > + paddr = lu.paddr + vaddr - lu.vaddr; > + } else { > + cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\n", addr); > + } > + } else { > + paddr = addr & TARGET_PAGE_MASK; > + } > + > + return paddr; > +} > + > +#endif /* !CONFIG_USER_ONLY */ > + > diff --git a/target-nios2/helper.h b/target-nios2/helper.h > new file mode 100644 > index 0000000..69b6684 > --- /dev/null > +++ b/target-nios2/helper.h > @@ -0,0 +1,45 @@ > +/* > + * Altera Nios II helper routines header. > + * > + * Copyright (c) 2012 Chris Wulff > + * > + * 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 "def-helper.h" > + > +/* Define this to enable tracing calls/returns */ > +/* #define CALL_TRACING */ > + > +#ifdef CALL_TRACING > +DEF_HELPER_2(call_status, void, i32, i32) > +DEF_HELPER_1(eret_status, void, i32) > +DEF_HELPER_1(ret_status, void, i32) > +#endif > + > +DEF_HELPER_1(raise_exception, void, i32) > + > +#if !defined(CONFIG_USER_ONLY) > +DEF_HELPER_1(mmu_read, i32, i32) > +DEF_HELPER_2(mmu_write, void, i32, i32) > +#endif > + > +DEF_HELPER_2(divs, i32, i32, i32) > +DEF_HELPER_2(divu, i32, i32, i32) > + > +DEF_HELPER_4(memalign, void, i32, i32, i32, i32) > + > +#include "def-helper.h" > + > diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c > new file mode 100644 > index 0000000..4dc06cd > --- /dev/null > +++ b/target-nios2/instruction.c > @@ -0,0 +1,1463 @@ > +/* > + * Copyright (C) 2010 Tobias Klauser > + * (Portions of this file that were originally from nios2sim-ng.) > + * > + * Copyright (C) 2012 Chris Wulff > + * > + * 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 > + > +#include "instruction.h" > +#include "exec-all.h" > +#include "helper.h" > + > +#define GEN_HELPER 1 > +#include "helper.h" > + > +static inline uint32_t get_opcode(uint32_t code) > +{ > + I_TYPE(instr, code); > + return instr->op; > +} > + > +static inline uint32_t get_opxcode(uint32_t code) > +{ > + R_TYPE(instr, code); > + return instr->opx6; > +} > + > +static inline void t_gen_helper_raise_exception(DisasContext *dc, > + uint32_t index) > +{ > + TCGv_i32 tmp = tcg_const_i32(index); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc); > + gen_helper_raise_exception(tmp); > + tcg_temp_free_i32(tmp); > + dc->is_jmp = DISAS_UPDATE; > +} > + > +/* > + * Instructions not implemented by the simulator. > + */ > +static void unimplemented(DisasContext *dc, uint32_t code) > +{ > + t_gen_helper_raise_exception(dc, EXCP_UNIMPL); > +} > + > +/* > + * Illegal instruction > + */ > +static void illegal_instruction(DisasContext *dc, > + uint32_t code __attribute__((unused))) > +{ > + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); > +} > + > +static void gen_check_supervisor(DisasContext *dc, int label) > +{ > + int l1 = gen_new_label(); > + > + TCGv_i32 tmp = tcg_temp_new(); > + tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U); > + tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1); > + t_gen_helper_raise_exception(dc, EXCP_SUPERI); > + tcg_gen_br(label); > + > + gen_set_label(l1); > + tcg_temp_free_i32(tmp); > + > + /* If we aren't taking the exception, update the PC to the > + * next instruction */ > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc+4); You probably don't want to do that. It's quite expensive to have a branch in every priviledged instruction. Given that CR_STATUS_U is use for the flags in cpu_get_tb_cpu_state(), for a given value of CR_STATUS_U, only TB with a corresponding CR_STATUS_U will be reused. For that you should copy tb->flags in DisasContext and use that instead. > +} > + > +static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr, > + unsigned int size) > +{ > + int mem_index = cpu_mmu_index(dc->env); In general you should not use env in instructions.c, unless it is for accessing a value that doesn't change during the execution (for example CPU features/capabilities), as there is no guarantee that the value will be the same when the TB is actually executed. In this case it should work correctly because cpu_mmu_index() only access CR_STATUS_U which is in the TB flags, but it is technically wrong. Either make cpu_mmu_index check the tb flags (see above to add them in DisasContext), or add a mem_index flag in DisasContext, computed from TB flags at the beginning of gen_intermediate_code_internal. In the latter case, you might even remove these functions and directly call tcg_gen_qemu_ldXXu(dst, addr, ctx->mem_index) when needed. > + > + switch (size) { > + case 1: > + tcg_gen_qemu_ld8u(dst, addr, mem_index); > + break; > + case 2: > + tcg_gen_qemu_ld16u(dst, addr, mem_index); > + break; > + case 4: > + tcg_gen_qemu_ld32u(dst, addr, mem_index); > + break; > + default: > + cpu_abort(dc->env, "Incorrect load size %d\n", size); > + break; > + } > +} > + > +static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr, > + unsigned int size) > +{ > + int mem_index = cpu_mmu_index(dc->env); > + Ditto > + switch (size) { > + case 1: > + tcg_gen_qemu_ld8s(dst, addr, mem_index); > + break; > + case 2: > + tcg_gen_qemu_ld16s(dst, addr, mem_index); > + break; > + case 4: > + tcg_gen_qemu_ld32s(dst, addr, mem_index); > + break; > + default: > + cpu_abort(dc->env, "Incorrect load size %d\n", size); > + break; > + } > +} > + > +static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr, > + unsigned int size) > +{ > + int mem_index = cpu_mmu_index(dc->env); > + Ditto > + switch (size) { > + case 1: > + tcg_gen_qemu_st8(val, addr, mem_index); > + break; > + case 2: > + tcg_gen_qemu_st16(val, addr, mem_index); > + break; > + case 4: > + tcg_gen_qemu_st32(val, addr, mem_index); > + break; > + default: > + cpu_abort(dc->env, "Incorrect load size %d\n", size); > + break; > + } > +} > + > +/* > + * Used as a placeholder for all instructions which do not have an effect on the > + * simulator (e.g. flush, sync) > + */ > +static void nop(DisasContext *dc __attribute__((unused)), > + uint32_t code __attribute__((unused))) > +{ > + /* Nothing to do here */ > +} > + > +/* > + * J-Type instructions > + */ > + > +/* > + * ra <- PC + 4 > + * PC <- (PC(31..28) : IMM26 * 4) > + */ > +static void call(DisasContext *dc, uint32_t code) > +{ > + J_TYPE(instr, code); > + > +#ifdef CALL_TRACING > + TCGv_i32 tmp = tcg_const_i32(dc->pc); > + TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) | (instr->imm26 * 4)); > + gen_helper_call_status(tmp, tmp2); > + tcg_temp_free_i32(tmp); > + tcg_temp_free_i32(tmp2); > +#endif > + > + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + (dc->pc & 0xF0000000) | (instr->imm26 * 4)); > + > + dc->is_jmp = DISAS_JUMP; > +} > + You probably want to add some tcg_gen_goto_tb() for static jumps, so that TB linking is possible. It greatly improves the speed of the emulation. > +/* PC <- (PC(31..28) : IMM26 * 4) */ > +static void jmpi(DisasContext *dc, uint32_t code) > +{ > + J_TYPE(instr, code); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + (dc->pc & 0xF0000000) | (instr->imm26 * 4)); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* > + * I-Type instructions > + */ > + > +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */ > +static void ldbu(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); Minor nitpick: it's better to write: tcg_gen_add_tl(addr, addr, dc->cpu_R[instr->a]); as the code generator on non-RISC hosts usually generate a slightly tiny better code. > + > + gen_load_u(dc, dc->cpu_R[instr->b], addr, 1); > + > + tcg_temp_free(addr); > +} > + > +/* rB <- rA + IMM16 */ > +static void addi(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv imm = tcg_temp_new(); > + tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16)); > + > + tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm); > + You can also call tcg_gen_addi_tl() instead, so that you don't need to load the constant by yourself. As a bonus the immediate = 0 case is optimized earlier in the code generation. > + tcg_temp_free(imm); > +} > + > +/* Mem8[rA + @(IMM16)] <- rB(7..0) */ > +static void stb(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); Ditto > + > + gen_store(dc, dc->cpu_R[instr->b], addr, 1); > + > + tcg_temp_free(addr); > +} > + > +/* PC <- PC + 4 + IMM16 */ > +static void br(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC)); > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rB <- @(Mem8[rA + @(IMM16)]) */ > +static void ldb(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_s(dc, dc->cpu_R[instr->b], addr, 1); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if ((signed) rA >= (signed) @(IMM16)) > + * rB <- 1 > + * else > + * rB <- 0 > + */ > +static void cmpgei(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_setcondi_tl(TCG_COND_GE, dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + (int32_t)((int16_t)instr->imm16)); > +} > + > +/* rB <- 0x0000 : Mem16[rA + @IMM16)] */ > +static void ldhu(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_u(dc, dc->cpu_R[instr->b], addr, 2); > + > + tcg_temp_free(addr); > +} > + > +/* rB <- rA & IMM16 */ > +static void andi(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16); > +} > + > +/* Mem16[rA + @(IMM16)] <- rB(15..0) */ > +static void sth(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_store(dc, dc->cpu_R[instr->b], addr, 2); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if ((signed) rA >= (signed) rB) > + * PC <- PC + 4 + @(IMM16) > + * else > + * PC <- PC + 4 > + */ > +static void bge(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC)); > + tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a], > + dc->cpu_R[instr->b], l1); > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4); > + gen_set_label(l1); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rB <- Mem16[rA + @IMM16)] */ > +static void ldh(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_s(dc, dc->cpu_R[instr->b], addr, 2); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if ((signed) rA < (signed) @(IMM16)) > + * rB <- 1 > + * else > + * rB <- 0 > + */ > +static void cmplti(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_setcondi_tl(TCG_COND_LT, dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + (int32_t)((int16_t)instr->imm16)); > +} > + > +/* Initializes the data cache line currently caching address rA + @(IMM16) */ > +static void initda(DisasContext *dc __attribute__((unused)), > + uint32_t code __attribute__((unused))) > +{ > + /* TODO */ > +} > + > +/* rB <- rA | IMM16 */ > +static void ori(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16); > +} > + > +/* Mem32[rA + @(IMM16)] <- rB */ > +static void stw(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_store(dc, dc->cpu_R[instr->b], addr, 4); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if ((signed) rA < (signed) rB) > + * PC <- PC + 4 + @(IMM16) > + * else > + * PC <- PC + 4 > + */ > +static void blt(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC)); > + tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a], > + dc->cpu_R[instr->b], l1); > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4); > + gen_set_label(l1); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rB <- @(Mem32[rA + @(IMM16)]) */ > +static void ldw(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_u(dc, dc->cpu_R[instr->b], addr, 4); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if ((signed) rA != (signed) @(IMM16)) > + * rB <- 1 > + * else > + * rB <- 0 > + */ > +static void cmpnei(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_setcondi_tl(TCG_COND_NE, dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + (int32_t)((int16_t)instr->imm16)); > +} > + > +/* rB <- rA ^ IMM16 */ > +static void xori(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16); > +} > + > +/* > + * if (rA != rB) > + * PC <- PC + 4 + @(IMM16) > + * else > + * PC <- PC + 4 > + */ > +static void bne(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC)); > + tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a], > + dc->cpu_R[instr->b], l1); > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4); > + gen_set_label(l1); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* > + * if ((signed) rA == (signed) @(IMM16)) > + * rB <- 1 > + * else > + * rB <- 0 > + */ > +static void cmpeqi(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_setcondi_tl(TCG_COND_EQ, dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + (int32_t)((int16_t)instr->imm16)); > +} > + > +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] (bypassing cache) */ > +static void ldbuio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_u(dc, dc->cpu_R[instr->b], addr, 1); > + > + tcg_temp_free(addr); > +} > + > +/* rB <- (rA * @(IMM16))(31..0) */ > +static void muli(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv imm = tcg_temp_new(); > + tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_mul_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm); tcg_gen_muli_tl() also exists. > + tcg_temp_free(imm); > +} > + > +/* Mem8[rA + @(IMM16)] <- rB(7..0) (bypassing cache) */ > +static void stbio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_store(dc, dc->cpu_R[instr->b], addr, 1); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if (rA == rB) > + * PC <- PC + 4 + @(IMM16) > + * else > + * PC <- PC + 4 > + */ > +static void beq(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC)); > + tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a], > + dc->cpu_R[instr->b], l1); > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4); > + gen_set_label(l1); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rB <- @(Mem8[rA + @(IMM16)]) (bypassing cache) */ > +static void ldbio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_s(dc, dc->cpu_R[instr->b], addr, 1); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if (rA >= 0x0000 : @(IMM16)) > + * rB <- 1 > + * else > + * rB <- 0 > + */ > +static void cmpgeui(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_setcondi_tl(TCG_COND_GEU, dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + (int32_t)((int16_t)instr->imm16)); > +} > + > +/* rB <- 0x0000 : Mem16[rA + @IMM16)] (bypassing cache) */ > +static void ldhuio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_u(dc, dc->cpu_R[instr->b], addr, 2); > + > + tcg_temp_free(addr); > +} > + > +/* rB <- rA & (IMM16 : 0x0000) */ > +static void andhi(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + instr->imm16 << 16); > +} > + > +/* Mem16[rA + @(IMM16)] <- rB(15..0) (bypassing cache) */ > +static void sthio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_store(dc, dc->cpu_R[instr->b], addr, 2); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if (rA >= rB) > + * PC <- PC + 4 + @(IMM16) > + * else > + * PC <- PC + 4 > + */ > +static void bgeu(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC)); > + tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a], > + dc->cpu_R[instr->b], l1); > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4); > + gen_set_label(l1); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rB <- Mem16[rA + @IMM16)] (bypassing cache) */ > +static void ldhio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_s(dc, dc->cpu_R[instr->b], addr, 2); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if (rA < 0x0000 : @(IMM16)) > + * rB <- 1 > + * else > + * rB <- 0 > + */ > +static void cmpltui(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_setcondi_tl(TCG_COND_LTU, dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + (int32_t)((int16_t)instr->imm16)); > +} > + > +/* */ > +static void initd(DisasContext *dc __attribute__((unused)), > + uint32_t code __attribute((unused))) > +{ > + /* TODO */ > +} > + > +/* rB <- rA | (IMM16 : 0x0000) */ > +static void orhi(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + instr->imm16 << 16); > +} > + > +/* Mem32[rA + @(IMM16)] <- rB (bypassing cache) */ > +static void stwio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_store(dc, dc->cpu_R[instr->b], addr, 4); > + > + tcg_temp_free(addr); > +} > + > +/* > + * if ((unsigned) rA < (unsigned) rB) > + * PC <- PC + 4 + @(IMM16) > + * else > + * PC <- PC + 4 > + */ > +static void bltu(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + > + tcg_gen_movi_tl(dc->cpu_R[R_PC], > + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC)); > + tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a], > + dc->cpu_R[instr->b], l1); > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4); > + gen_set_label(l1); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rB <- @(Mem32[rA + @(IMM16)]) (bypassing cache) */ > +static void ldwio(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + TCGv addr = tcg_temp_new(); > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16)); > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr); > + > + gen_load_u(dc, dc->cpu_R[instr->b], addr, 4); > + > + tcg_temp_free(addr); > +} > + > +/* Prototype only, defined below */ > +static void handle_r_type_instr(DisasContext *dc, uint32_t code); > + > +/* rB <- rA ^ (IMM16 : 0x0000) */ > +static void xorhi(DisasContext *dc, uint32_t code) > +{ > + I_TYPE(instr, code); > + > + tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], > + instr->imm16 << 16); > +} > + > +static struct instruction i_type_instructions[I_TYPE_COUNT] = { > + [CALL] = INSTRUCTION(call), > + [JMPI] = INSTRUCTION(jmpi), > + [0x02] = INSTRUCTION_ILLEGAL(), > + [LDBU] = INSTRUCTION(ldbu), > + [ADDI] = INSTRUCTION(addi), > + [STB] = INSTRUCTION(stb), > + [BR] = INSTRUCTION(br), > + [LDB] = INSTRUCTION(ldb), > + [CMPGEI] = INSTRUCTION(cmpgei), > + [0x09] = INSTRUCTION_ILLEGAL(), > + [0x0a] = INSTRUCTION_ILLEGAL(), > + [LDHU] = INSTRUCTION(ldhu), > + [ANDI] = INSTRUCTION(andi), > + [STH] = INSTRUCTION(sth), > + [BGE] = INSTRUCTION(bge), > + [LDH] = INSTRUCTION(ldh), > + [CMPLTI] = INSTRUCTION(cmplti), > + [0x11] = INSTRUCTION_ILLEGAL(), > + [0x12] = INSTRUCTION_ILLEGAL(), > + [INITDA] = INSTRUCTION(initda), > + [ORI] = INSTRUCTION(ori), > + [STW] = INSTRUCTION(stw), > + [BLT] = INSTRUCTION(blt), > + [LDW] = INSTRUCTION(ldw), > + [CMPNEI] = INSTRUCTION(cmpnei), > + [0x19] = INSTRUCTION_ILLEGAL(), > + [0x1a] = INSTRUCTION_ILLEGAL(), > + [FLUSHDA] = INSTRUCTION_NOP(flushda), > + [XORI] = INSTRUCTION(xori), > + [0x1d] = INSTRUCTION_ILLEGAL(), > + [BNE] = INSTRUCTION(bne), > + [0x1f] = INSTRUCTION_ILLEGAL(), > + [CMPEQI] = INSTRUCTION(cmpeqi), > + [0x21] = INSTRUCTION_ILLEGAL(), > + [0x22] = INSTRUCTION_ILLEGAL(), > + [LDBUIO] = INSTRUCTION(ldbuio), > + [MULI] = INSTRUCTION(muli), > + [STBIO] = INSTRUCTION(stbio), > + [BEQ] = INSTRUCTION(beq), > + [LDBIO] = INSTRUCTION(ldbio), > + [CMPGEUI] = INSTRUCTION(cmpgeui), > + [0x29] = INSTRUCTION_ILLEGAL(), > + [0x2a] = INSTRUCTION_ILLEGAL(), > + [LDHUIO] = INSTRUCTION(ldhuio), > + [ANDHI] = INSTRUCTION(andhi), > + [STHIO] = INSTRUCTION(sthio), > + [BGEU] = INSTRUCTION(bgeu), > + [LDHIO] = INSTRUCTION(ldhio), > + [CMPLTUI] = INSTRUCTION(cmpltui), > + [0x31] = INSTRUCTION_ILLEGAL(), > + [CUSTOM] = INSTRUCTION_UNIMPLEMENTED(custom), > + [INITD] = INSTRUCTION(initd), > + [ORHI] = INSTRUCTION(orhi), > + [STWIO] = INSTRUCTION(stwio), > + [BLTU] = INSTRUCTION(bltu), > + [LDWIO] = INSTRUCTION(ldwio), > + [RDPRS] = INSTRUCTION_UNIMPLEMENTED(rdprs), > + [0x39] = INSTRUCTION_ILLEGAL(), > + [R_TYPE] = { "", handle_r_type_instr }, > + [FLUSHD] = INSTRUCTION_NOP(flushd), > + [XORHI] = INSTRUCTION(xorhi), > + [0x3d] = INSTRUCTION_ILLEGAL(), > + [0x3e] = INSTRUCTION_ILLEGAL(), > + [0x3f] = INSTRUCTION_ILLEGAL(), > +}; > + > +/* > + * R-Type instructions > + */ > + > +/* > + * status <- estatus > + * PC <- ea > + */ > +static void eret(DisasContext *dc, uint32_t code __attribute__((unused))) > +{ > +#ifdef CALL_TRACING > + TCGv_i32 tmp = tcg_const_i32(dc->pc); > + gen_helper_eret_status(tmp); > + tcg_temp_free_i32(tmp); > +#endif > + > + tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]); > + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rC <- rA rotated left IMM5 bit positions */ > +static void roli(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv t0 = tcg_temp_new(); > + > + tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5); > + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5); > + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0); > + Looks like you want to use tcg_gen_rotli, which is optimized on some hosts (like x86). > + tcg_temp_free(t0); > +} > + > +/* rC <- rA rotated left rB(4..0) bit positions */ > +static void rol(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv t0 = tcg_temp_new(); > + TCGv t1 = tcg_temp_new(); > + > + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31); > + tcg_gen_movi_tl(t1, 32); > + tcg_gen_sub_tl(t1, t1, t0); > + tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1); > + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0); > + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1); > + Same, tcg_gen_rotl exists. > + tcg_temp_free(t0); > + tcg_temp_free(t1); > +} > + > +/* */ > +static void flushp(DisasContext *dc __attribute__((unused)), > + uint32_t code __attribute__((unused))) > +{ > + /* TODO */ > +} > + > +/* PC <- ra */ > +static void ret(DisasContext *dc, uint32_t code __attribute__((unused))) > +{ > +#ifdef CALL_TRACING > + TCGv_i32 tmp = tcg_const_i32(dc->pc); > + gen_helper_ret_status(tmp); > + tcg_temp_free_i32(tmp); > +#endif > + > + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rC <- ~(A | rB) */ > +static void nor(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_nor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- ((unsigned)rA * (unsigned)rB))(31..0) */ > +static void mulxuu(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv_i64 t0, t1; > + > + t0 = tcg_temp_new_i64(); > + t1 = tcg_temp_new_i64(); > + > + tcg_gen_extu_i32_i64(t0, dc->cpu_R[instr->a]); > + tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]); > + tcg_gen_mul_i64(t0, t0, t1); > + > + tcg_gen_shri_i64(t0, t0, 32); > + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0); > + > + tcg_temp_free_i64(t0); > + tcg_temp_free_i64(t1); > +} > + > +/* > + * if (rA >= rB) > + * rC <- 1 > + * else > + * rC <- 0 > + */ > +static void cmpge(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_setcond_tl(TCG_COND_GE, dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* PC <- ba */ > +static void bret(DisasContext *dc, uint32_t code __attribute__((unused))) > +{ > + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rC <- rA rotated right rb(4..0) bit positions */ > +static void ror(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv t0 = tcg_temp_new(); > + TCGv t1 = tcg_temp_new(); > + > + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31); > + tcg_gen_movi_tl(t1, 32); > + tcg_gen_sub_tl(t1, t1, t0); > + tcg_gen_shli_tl(t1, dc->cpu_R[instr->a], t1); > + tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0); > + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1); > + > + tcg_temp_free(t0); > + tcg_temp_free(t1); > +} > + > +/* */ > +static void flushi(DisasContext *dc __attribute__((unused)), > + uint32_t code __attribute__((unused))) > +{ > + /* TODO */ > +} > + > +/* PC <- rA */ > +static void jmp(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rC <- rA & rB */ > +static void and(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_and_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* > + * if ((signed) rA < (signed) rB) > + * rC <- 1 > + * else > + * rC <- 0 > + */ > +static void cmplt(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_setcond_tl(TCG_COND_LT, dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- rA << IMM5 */ > +static void slli(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5); > +} > + > +/* rC <- rA << rB(4..0) */ > +static void sll(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv t0 = tcg_temp_new(); > + > + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31); > + tcg_gen_shl_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0); > + > + tcg_temp_free(t0); > +} > + > +/* rC <- rA | rB */ > +static void or(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- ((signed)rA * (unsigned)rB))(31..0) */ > +static void mulxsu(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv_i64 t0, t1; > + > + t0 = tcg_temp_new_i64(); > + t1 = tcg_temp_new_i64(); > + > + tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]); > + tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]); > + tcg_gen_mul_i64(t0, t0, t1); > + > + tcg_gen_shri_i64(t0, t0, 32); > + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0); > + > + tcg_temp_free_i64(t0); > + tcg_temp_free_i64(t1); > +} > + > +/* > + * if (rA != rB) > + * rC <- 1 > + * else > + * rC <- 0 > + */ > +static void cmpne(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_setcond_tl(TCG_COND_NE, dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- (unsigned) rA >> ((unsigned) IMM5)*/ > +static void srli(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5); > +} > + > +/* rC <- (unsigned) rA >> ((unsigned) rB(4..0))*/ > +static void srl(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv t0 = tcg_temp_new(); > + > + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31); > + tcg_gen_shr_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0); > + > + tcg_temp_free(t0); > +} > + > +/* rC <- PC + 4 */ > +static void nextpc(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_movi_tl(dc->cpu_R[instr->c], dc->pc + 4); > +} > + > +/* > + * ra <- PC + 4 > + * PC <- rA > + */ > +static void callr(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > +#ifdef CALL_TRACING > + TCGv_i32 tmp = tcg_const_i32(dc->pc); > + gen_helper_call_status(tmp, dc->cpu_R[instr->a]); > + tcg_temp_free_i32(tmp); > +#endif > + > + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); > + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]); > + > + dc->is_jmp = DISAS_JUMP; > +} > + > +/* rC <- rA ^ rB */ > +static void xor(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_xor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- ((signed)rA * (signed)rB))(31..0) */ > +static void mulxss(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv_i64 t0, t1; > + > + t0 = tcg_temp_new_i64(); > + t1 = tcg_temp_new_i64(); > + > + tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]); > + tcg_gen_ext_i32_i64(t1, dc->cpu_R[instr->b]); > + tcg_gen_mul_i64(t0, t0, t1); > + > + tcg_gen_shri_i64(t0, t0, 32); > + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0); > + > + tcg_temp_free_i64(t0); > + tcg_temp_free_i64(t1); > +} > + > +/* > + * if (rA == rB) > + * rC <- 1 > + * else > + * rC <- 0 > + */ > +static void cmpeq(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_setcond_tl(TCG_COND_EQ, dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- rA / rB */ > +static void divu(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + gen_helper_divu(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- rA / rB */ > +static void _div(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + gen_helper_divs(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- ctlN */ > +static void rdctl(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + gen_check_supervisor(dc, l1); > + > + switch (instr->imm5 + 32) { > + case CR_PTEADDR: > + case CR_TLBACC: > + case CR_TLBMISC: > + { > + TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32); > + gen_helper_mmu_read(dc->cpu_R[instr->c], tmp); > + tcg_temp_free_i32(tmp); > + break; > + } > + > + default: > + tcg_gen_mov_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->imm5 + 32]); > + break; > + } > + > + gen_set_label(l1); > +} > + > +/* rC <- (rA * rB))(31..0) */ > +static void mul(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_mul_i32(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* > + * if (rA >= rB) > + * rC <- 1 > + * else > + * rC <- 0 > + */ > +static void cmpgeu(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_setcond_tl(TCG_COND_GEU, dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* */ > +static void initi(DisasContext *dc __attribute__((unused)), > + uint32_t code __attribute__((unused))) > +{ > + /* TODO */ > +} > + > +/* > + * estatus <- status > + * PIE <- 0 > + * U <- 0 > + * ea <- PC + 4 > + * PC <- exception handler address > + */ > +static void trap(DisasContext *dc, uint32_t code __attribute__((unused))) > +{ > + t_gen_helper_raise_exception(dc, EXCP_TRAP); > +} > + > +/* ctlN <- rA */ > +static void wrctl(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + int l1 = gen_new_label(); > + gen_check_supervisor(dc, l1); > + > + switch (instr->imm5 + 32) { > + case CR_PTEADDR: > + case CR_TLBACC: > + case CR_TLBMISC: > + { > + TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32); > + gen_helper_mmu_write(tmp, dc->cpu_R[instr->a]); > + tcg_temp_free_i32(tmp); > + break; > + } > + > + default: > + tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]); > + break; > + } > + > + gen_set_label(l1); > +} > + > +/* > + * if (rA < rB) > + * rC <- 1 > + * else > + * rC <- 0 > + */ > +static void cmpltu(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_setcond_tl(TCG_COND_LTU, dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- rA + rB */ > +static void add(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_add_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* > + * bstatus ← status > + * PIE <- 0 > + * U <- 0 > + * ba <- PC + 4 > + * PC <- break handler address > + */ > +static void __break(DisasContext *dc, uint32_t code __attribute__((unused))) > +{ > + t_gen_helper_raise_exception(dc, EXCP_BREAK); > +} > + > +/* rC <- rA - rB */ > +static void sub(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_sub_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], > + dc->cpu_R[instr->b]); > +} > + > +/* rC <- (signed) rA >> ((unsigned) IMM5) */ > +static void srai(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + tcg_gen_sari_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5); > +} > + > +/* rC <- (signed) rA >> ((unsigned) rB(4..0)) */ > +static void sra(DisasContext *dc, uint32_t code) > +{ > + R_TYPE(instr, code); > + > + TCGv t0 = tcg_temp_new(); > + > + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31); > + tcg_gen_sar_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0); > + > + tcg_temp_free(t0); > +} > + > +static struct instruction r_type_instructions[R_TYPE_COUNT] = { > + [0x00] = INSTRUCTION_ILLEGAL(), > + [ERET] = INSTRUCTION(eret), > + [ROLI] = INSTRUCTION(roli), > + [ROL] = INSTRUCTION(rol), > + [FLUSHP] = INSTRUCTION(flushp), > + [RET] = INSTRUCTION(ret), > + [NOR] = INSTRUCTION(nor), > + [MULXUU] = INSTRUCTION(mulxuu), > + [CMPGE] = INSTRUCTION(cmpge), > + [BRET] = INSTRUCTION(bret), > + [0x0a] = INSTRUCTION_ILLEGAL(), > + [ROR] = INSTRUCTION(ror), > + [FLUSHI] = INSTRUCTION(flushi), > + [JMP] = INSTRUCTION(jmp), > + [AND] = INSTRUCTION(and), > + [0x0f] = INSTRUCTION_ILLEGAL(), > + [CMPLT] = INSTRUCTION(cmplt), > + [0x11] = INSTRUCTION_ILLEGAL(), > + [SLLI] = INSTRUCTION(slli), > + [SLL] = INSTRUCTION(sll), > + [WRPRS] = INSTRUCTION_UNIMPLEMENTED(wrprs), > + [0x15] = INSTRUCTION_ILLEGAL(), > + [OR] = INSTRUCTION(or), > + [MULXSU] = INSTRUCTION(mulxsu), > + [CMPNE] = INSTRUCTION(cmpne), > + [0x19] = INSTRUCTION_ILLEGAL(), > + [SRLI] = INSTRUCTION(srli), > + [SRL] = INSTRUCTION(srl), > + [NEXTPC] = INSTRUCTION(nextpc), > + [CALLR] = INSTRUCTION(callr), > + [XOR] = INSTRUCTION(xor), > + [MULXSS] = INSTRUCTION(mulxss), > + [CMPEQ] = INSTRUCTION(cmpeq), > + [0x21] = INSTRUCTION_ILLEGAL(), > + [0x22] = INSTRUCTION_ILLEGAL(), > + [0x23] = INSTRUCTION_ILLEGAL(), > + [DIVU] = INSTRUCTION(divu), > + [DIV] = { "div", _div }, > + [RDCTL] = INSTRUCTION(rdctl), > + [MUL] = INSTRUCTION(mul), > + [CMPGEU] = INSTRUCTION(cmpgeu), > + [INITI] = INSTRUCTION(initi), > + [0x2a] = INSTRUCTION_ILLEGAL(), > + [0x2b] = INSTRUCTION_ILLEGAL(), > + [0x2c] = INSTRUCTION_ILLEGAL(), > + [TRAP] = INSTRUCTION(trap), > + [WRCTL] = INSTRUCTION(wrctl), > + [0x2f] = INSTRUCTION_ILLEGAL(), > + [CMPLTU] = INSTRUCTION(cmpltu), > + [ADD] = INSTRUCTION(add), > + [0x32] = INSTRUCTION_ILLEGAL(), > + [0x33] = INSTRUCTION_ILLEGAL(), > + [BREAK] = { "break", __break }, > + [0x35] = INSTRUCTION_ILLEGAL(), > + [SYNC] = INSTRUCTION(nop), > + [0x37] = INSTRUCTION_ILLEGAL(), > + [0x38] = INSTRUCTION_ILLEGAL(), > + [SUB] = INSTRUCTION(sub), > + [SRAI] = INSTRUCTION(srai), > + [SRA] = INSTRUCTION(sra), > + [0x3c] = INSTRUCTION_ILLEGAL(), > + [0x3d] = INSTRUCTION_ILLEGAL(), > + [0x3e] = INSTRUCTION_ILLEGAL(), > + [0x3f] = INSTRUCTION_ILLEGAL(), > +}; > + > +static void handle_r_type_instr(DisasContext *dc, uint32_t code) > +{ > + uint32_t opx; > + instruction_handler handle_instr; > + > + opx = get_opxcode(code); > + if (unlikely(opx >= R_TYPE_COUNT)) { > + goto illegal_op; > + } > + > + LOG_DIS("R: %s (%08x)\n", r_type_instructions[opx].name, code); > + handle_instr = r_type_instructions[opx].handler; > + > + handle_instr(dc, code); > + > + return; > + > +illegal_op: > + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); > +} > + > +void handle_instruction(DisasContext *dc) > +{ > + uint32_t insn = ldl_code(dc->pc); > + uint32_t op = get_opcode(insn); > + > + LOG_DIS("%8.8x\t", insn); > + > + if (unlikely(op >= I_TYPE_COUNT)) { > + goto illegal_op; > + } > + > + if (op != R_TYPE) { > + LOG_DIS("I: %s (%08x)\n", i_type_instructions[op].name, insn); > + } > + i_type_instructions[op].handler(dc, insn); > + > + return; > + > +illegal_op: > + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); > +} > + > +const char *instruction_get_string(uint32_t code) > +{ > + uint32_t op = get_opcode(code); > + > + if (unlikely(op >= I_TYPE_COUNT)) { > + return ""; > + } else if (op == R_TYPE) { > + uint32_t opx = get_opxcode(code); > + if (unlikely(opx >= R_TYPE_COUNT)) { > + return ""; > + } > + return r_type_instructions[opx].name; > + } else { > + return i_type_instructions[op].name; > + } > +} > + > diff --git a/target-nios2/instruction.h b/target-nios2/instruction.h > new file mode 100644 > index 0000000..fe29933 > --- /dev/null > +++ b/target-nios2/instruction.h > @@ -0,0 +1,290 @@ > +/* > + * Copyright (C) 2010 Tobias Klauser > + * Copyright (C) 2010 chysun2000@gmail.com > + * (Portions of this file that were originally from nios2sim-ng.) > + * > + * Copyright (C) 2012 Chris Wulff > + * > + * 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 _INSTRUCTION_H_ > +#define _INSTRUCTION_H_ > + > +#include > +#include "cpu.h" > +#include "tcg-op.h" > + > +/* > + * Instruction Word Formats > + */ > + > +/* I-Type instruction */ > +struct i_type { > + uint32_t op:6; > + uint32_t imm16:16; > + uint32_t b:5; > + uint32_t a:5; > +} __attribute__((packed)); > + > +union i_type_u { > + uint32_t v; > + struct i_type i; > +}; > + > +#define I_TYPE(instr, op) \ > + union i_type_u instr_u = { .v = op }; \ > + struct i_type *instr = &instr_u.i > + > +/* R-Type instruction */ > +struct r_type { > + uint32_t op:6; > + /* > + * Some R-Type instructions embed a small immediate value in the > + * low-order bits of OPX. > + */ > + uint32_t imm5:5; > + uint32_t opx6:6; > + uint32_t c:5; > + uint32_t b:5; > + uint32_t a:5; > +} __attribute__((packed)); > + > +union r_type_u { > + uint32_t v; > + struct r_type i; > +}; > + > +#define R_TYPE(instr, op) \ > + union r_type_u instr_u = { .v = op }; \ > + struct r_type *instr = &instr_u.i > + > +/* J-Type instruction */ > +struct j_type { > + uint32_t op:6; > + uint32_t imm26:26; > +} __attribute__((packed)); > + > +#define J_TYPE(instr, op) \ > + struct j_type *instr = (struct j_type *) &op > + > +/* > + * Instruction Opcodes > + */ > + > +/* > + * OP Encodings for I-Type instructions (except for CALL and JMPI, which are > + * J-type instructions) > + */ > +enum { > + CALL = 0x00, /* J-type */ > + JMPI = 0x01, /* J-type */ > + /* 0x02 */ > + LDBU = 0x03, > + ADDI = 0x04, > + STB = 0x05, > + BR = 0x06, > + LDB = 0x07, > + CMPGEI = 0x08, > + /* 0x09 */ > + /* 0x0A */ > + LDHU = 0x0B, > + ANDI = 0x0C, > + STH = 0x0D, > + BGE = 0x0E, > + LDH = 0x0F, > + CMPLTI = 0x10, > + /* 0x11 */ > + /* 0x12 */ > + INITDA = 0x13, > + ORI = 0x14, > + STW = 0x15, > + BLT = 0x16, > + LDW = 0x17, > + CMPNEI = 0x18, > + /* 0x19 */ > + /* 0x1A */ > + FLUSHDA = 0x1B, > + XORI = 0x1C, > + /* 0x1D */ > + BNE = 0x1E, > + /* 0x1F */ > + CMPEQI = 0x20, > + /* 0x21 */ > + /* 0x22 */ > + LDBUIO = 0x23, > + MULI = 0x24, > + STBIO = 0x25, > + BEQ = 0x26, > + LDBIO = 0x27, > + CMPGEUI = 0x28, > + /* 0x29 */ > + /* 0x2A */ > + LDHUIO = 0x2B, > + ANDHI = 0x2C, > + STHIO = 0x2D, > + BGEU = 0x2E, > + LDHIO = 0x2F, > + CMPLTUI = 0x30, > + /* 0x31 */ > + CUSTOM = 0x32, > + INITD = 0x33, > + ORHI = 0x34, > + STWIO = 0x35, > + BLTU = 0x36, > + LDWIO = 0x37, > + RDPRS = 0x38, > + /* 0x39 */ > + R_TYPE = 0x3A, > + FLUSHD = 0x3B, > + XORHI = 0x3C, > + /* 0x3D */ > + /* 0x3E */ > + /* 0x3F */ > +}; > +#define I_TYPE_COUNT 0x40 > + > +/* OPX Encodings for R-Type instructions */ > +enum { > + /* 0x00 */ > + ERET = 0x01, > + ROLI = 0x02, > + ROL = 0x03, > + FLUSHP = 0x04, > + RET = 0x05, > + NOR = 0x06, > + MULXUU = 0x07, > + CMPGE = 0x08, > + BRET = 0x09, > + /* 0x0A */ > + ROR = 0x0B, > + FLUSHI = 0x0C, > + JMP = 0x0D, > + AND = 0x0E, > + /* 0x0F */ > + CMPLT = 0x10, > + /* 0x11 */ > + SLLI = 0x12, > + SLL = 0x13, > + WRPRS = 0x14, > + /* 0x15 */ > + OR = 0x16, > + MULXSU = 0x17, > + CMPNE = 0x18, > + /* 0x19 */ > + SRLI = 0x1A, > + SRL = 0x1B, > + NEXTPC = 0x1C, > + CALLR = 0x1D, > + XOR = 0x1E, > + MULXSS = 0x1F, > + CMPEQ = 0x20, > + /* 0x21 */ > + /* 0x22 */ > + /* 0x23 */ > + DIVU = 0x24, > + DIV = 0x25, > + RDCTL = 0x26, > + MUL = 0x27, > + CMPGEU = 0x28, > + INITI = 0x29, > + /* 0x2A */ > + /* 0x2B */ > + /* 0x2C */ > + TRAP = 0x2D, > + WRCTL = 0x2E, > + /* 0x2F */ > + CMPLTU = 0x30, > + ADD = 0x31, > + /* 0x32 */ > + /* 0x33 */ > + BREAK = 0x34, > + /* 0x35 */ > + SYNC = 0x36, > + /* 0x37 */ > + /* 0x38 */ > + SUB = 0x39, > + SRAI = 0x3A, > + SRA = 0x3B, > + /* 0x3C */ > + /* 0x3D */ > + /* 0x3E */ > + /* 0x3F */ > +}; > +#define R_TYPE_COUNT 0x40 > + > +/* > + * Return values for instruction handlers > + */ > +#define INSTR_UNIMPL -2 /* Unimplemented instruction */ > +#define INSTR_ERR -1 /* Error in instruction */ > +#define PC_INC_NORMAL 0 /* Normal PC increment after instruction */ > +#define PC_INC_BY_INSTR 1 /* PC got incremented by instruction */ > +#define INSTR_BREAK 2 /* Break encountered */ > +#define INSTR_EXCEPTION 255 /* Instruction generated an exception > + (the exception cause will be stored > + in struct nios2 */ > + > +#define EXCEPTION(cpu, cause) \ > + ({ \ > + (cpu)->exception_cause = cause; \ > + INSTR_EXCEPTION; \ > + }) > + > +typedef struct DisasContext { > + CPUNios2State *env; > + TCGv *cpu_R; > + int is_jmp; > + target_ulong pc; > + struct TranslationBlock *tb; > +} DisasContext; > + > +typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode); > + > +struct instruction { > + const char *name; > + instruction_handler handler; > +}; > + > +/* > + * Stringification macro (taken from Linux Kernel source code) > + */ > + > +/* Indirect stringification. Doing two levels allows the parameter to be a > + * macro itself. For example, compile with -DFOO=bar, __stringify(FOO) > + * converts to "bar". > + */ > + > +#define __stringify_1(x...) #x > +#define __stringify(x...) __stringify_1(x) > + > +#define INSTRUCTION(name) { __stringify(name), name } > +#define INSTRUCTION_NOP(name) { __stringify(name), nop } > +#define INSTRUCTION_UNIMPLEMENTED(name) { __stringify(name), unimplemented } > +#define INSTRUCTION_ILLEGAL() { "", illegal_instruction } > + > +extern void handle_instruction(DisasContext *dc); > +extern const char *instruction_get_string(uint32_t code); > + > +#define SIM_COMPAT 0 > +#define DISAS_GNU 1 /* Disassembly via GNU gdb derived routines */ > +#define DISAS_NIOS2 0 /* Disassembly via routines in instruction.c */ > +#if DISAS_NIOS2 && !SIM_COMPAT > +# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) > +#else > +# define LOG_DIS(...) do { } while (0) > +#endif > + > +#endif /* _INSTRUCTION_H_ */ > diff --git a/target-nios2/machine.c b/target-nios2/machine.c > new file mode 100644 > index 0000000..09198db > --- /dev/null > +++ b/target-nios2/machine.c > @@ -0,0 +1,33 @@ > +/* > + * Altera Nios II MMU emulation for qemu. > + * > + * Copyright (C) 2012 Chris Wulff > + * > + * 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 "hw/hw.h" > +#include "hw/boards.h" > + > +void cpu_save(QEMUFile *f, void *opaque) > +{ > + /* TODO */ > +} > + > +int cpu_load(QEMUFile *f, void *opaque, int version_id) > +{ > + /* TODO */ > + return 0; > +} > diff --git a/target-nios2/mmu.c b/target-nios2/mmu.c > new file mode 100644 > index 0000000..a52fa1b > --- /dev/null > +++ b/target-nios2/mmu.c > @@ -0,0 +1,273 @@ > +/* > + * Altera Nios II MMU emulation for qemu. > + * > + * Copyright (C) 2012 Chris Wulff > + * > + * 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 > +#include > +#include > + > +#include "config.h" > +#include "cpu.h" > +#include "exec-all.h" > + > +/* Define this to enable MMU debug messages */ > +/* #define DEBUG_MMU */ > + > +#ifdef DEBUG_MMU > +#define MMU_LOG(x) x > +#else > +#define MMU_LOG(x) > +#endif > + > +uint32_t mmu_read(CPUNios2State *env, uint32_t rn) > +{ > + switch (rn) { > + case CR_TLBACC: > + MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn])); > + break; > + > + case CR_TLBMISC: > + MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn])); > + break; > + > + case CR_PTEADDR: > + MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn])); > + break; > + > + default: > + break; > + } > + return env->regs[rn]; > +} > + > +/* rw - 0 = read, 1 = write, 2 = fetch. */ > +unsigned int mmu_translate(CPUNios2State *env, > + struct nios2_mmu_lookup *lu, > + target_ulong vaddr, int rw, int mmu_idx) > +{ > + int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4; > + int vpn = vaddr >> 12; > + > + MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n", > + vaddr, pid, vpn)); > + > + int way; > + for (way = 0; way < env->mmu.tlb_num_ways; way++) { > + > + struct nios2_tlb_entry *entry = > + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) + > + (vpn & env->mmu.tlb_entry_mask)]; > + > + MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n", > + (way * env->mmu.tlb_num_ways) + > + (vpn & env->mmu.tlb_entry_mask), > + entry->tag, (entry->tag >> 12))); > + > + if (((entry->tag >> 12) != vpn) || > + (((entry->tag & (1<<11)) == 0) && > + ((entry->tag & ((1<mmu.pid_bits)-1)) != pid))) { > + continue; > + } > + lu->vaddr = vaddr & TARGET_PAGE_MASK; > + lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS; > + lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) | > + ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) | > + ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0); > + > + MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n", > + (way * env->mmu.tlb_num_ways) + > + (vpn & env->mmu.tlb_entry_mask), > + lu->vaddr, lu->paddr, lu->prot)); > + return 1; > + } > + return 0; > +} > + > +static void mmu_flush_pid(CPUNios2State *env, uint32_t pid) > +{ > + int idx; > + MMU_LOG(qemu_log("TLB Flush PID %d\n", pid)); > + > + for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) { > + struct nios2_tlb_entry *entry = &env->mmu.tlb[idx]; > + > + MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n", > + idx, entry->tag, entry->data)); > + > + if ((entry->tag & (1<<10)) && (!(entry->tag & (1<<11))) && > + ((entry->tag & ((1<mmu.pid_bits)-1)) == pid)) { > + uint32_t vaddr = entry->tag & TARGET_PAGE_MASK; > + > + MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr)); > + > + tlb_flush_page(env, vaddr); > + } > + } > +} > + > +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v) > +{ > + MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v)); > + > + switch (rn) { > + case CR_TLBACC: > + MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n", > + v >> CR_TLBACC_IGN_SHIFT, > + (v & CR_TLBACC_C) ? 'C' : '.', > + (v & CR_TLBACC_R) ? 'R' : '.', > + (v & CR_TLBACC_W) ? 'W' : '.', > + (v & CR_TLBACC_X) ? 'X' : '.', > + (v & CR_TLBACC_G) ? 'G' : '.', > + v & CR_TLBACC_PFN_MASK)); > + > + /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ > + if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) { > + int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT); > + int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2; > + int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4; > + int g = (v & CR_TLBACC_G) ? 1 : 0; > + int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0; > + struct nios2_tlb_entry *entry = > + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) + > + (vpn & env->mmu.tlb_entry_mask)]; > + uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid; > + uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W | > + CR_TLBACC_X | CR_TLBACC_PFN_MASK); > + > + if ((entry->tag != newTag) || (entry->data != newData)) { > + if (entry->tag & (1<<10)) { > + /* Flush existing entry */ > + MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n", > + entry->tag & TARGET_PAGE_MASK)); > + tlb_flush_page(env, entry->tag & TARGET_PAGE_MASK); > + } > + entry->tag = newTag; > + entry->data = newData; > + MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n", > + (way * env->mmu.tlb_num_ways) + > + (vpn & env->mmu.tlb_entry_mask), > + entry->tag, entry->data)); > + } > + /* Auto-increment tlbmisc.WAY */ > + env->regs[CR_TLBMISC] = > + (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) | > + (((way+1) & (env->mmu.tlb_num_ways-1)) << CR_TLBMISC_WAY_SHIFT); > + } > + > + /* Writes to TLBACC don't change the read-back value */ > + env->mmu.tlbacc_wr = v; > + break; > + > + case CR_TLBMISC: > + MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n", > + v >> CR_TLBMISC_WAY_SHIFT, > + (v & CR_TLBMISC_RD) ? 'R' : '.', > + (v & CR_TLBMISC_WR) ? 'W' : '.', > + (v & CR_TLBMISC_DBL) ? '2' : '.', > + (v & CR_TLBMISC_BAD) ? 'B' : '.', > + (v & CR_TLBMISC_PERM) ? 'P' : '.', > + (v & CR_TLBMISC_D) ? 'D' : '.', > + (v & CR_TLBMISC_PID_MASK) >> 4)); > + > + if ((v & CR_TLBMISC_PID_MASK) != > + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) { > + mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> > + CR_TLBMISC_PID_SHIFT); > + } > + /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ > + if (v & CR_TLBMISC_RD) { > + int way = (v >> CR_TLBMISC_WAY_SHIFT); > + int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2; > + struct nios2_tlb_entry *entry = > + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) + > + (vpn & env->mmu.tlb_entry_mask)]; > + > + env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK; > + env->regs[CR_TLBACC] |= entry->data | > + ((entry->tag & (1<<11)) ? CR_TLBACC_G : 0); > + env->regs[CR_TLBMISC] = > + (v & ~CR_TLBMISC_PID_MASK) | > + ((entry->tag & ((1<mmu.pid_bits)-1)) << > + CR_TLBMISC_PID_SHIFT); > + env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK; > + env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT; > + MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, " > + "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n", > + way, vpn, entry->tag, entry->data, > + env->regs[CR_TLBACC], env->regs[CR_TLBMISC], > + env->regs[CR_PTEADDR])); > + } else { > + env->regs[CR_TLBMISC] = v; > + } > + > + env->mmu.tlbmisc_wr = v; > + break; > + > + case CR_PTEADDR: > + MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n", > + v >> CR_PTEADDR_PTBASE_SHIFT, > + (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT)); > + > + /* Writes to PTEADDR don't change the read-back VPN value */ > + env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) | > + (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK); > + env->mmu.pteaddr_wr = v; > + break; > + > + default: > + break; > + } > +} > + > +void mmu_init(struct nios2_mmu *mmu) > +{ > + MMU_LOG(qemu_log("mmu_init\n")); > + > + mmu->pid_bits = 8; /* TODO: get this from ALTR,pid-num-bits */ > + mmu->tlb_num_ways = 16; /* TODO: get this from ALTR,tlb-num-ways */ > + mmu->tlb_num_entries = 256; /* TODO: get this from ALTR,tlb-num-entries */ > + mmu->tlb_entry_mask = (mmu->tlb_num_entries/mmu->tlb_num_ways) - 1; > + > + mmu->tlb = (struct nios2_tlb_entry *)g_malloc0( > + sizeof(struct nios2_tlb_entry) * mmu->tlb_num_entries); > +} > + > +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env) > +{ > + int i; > + cpu_fprintf(f, "MMU: ways %d, entries %d, pid bits %d\n", > + env->mmu.tlb_num_ways, env->mmu.tlb_num_entries, > + env->mmu.pid_bits); > + > + for (i = 0; i < env->mmu.tlb_num_entries; i++) { > + struct nios2_tlb_entry *entry = &env->mmu.tlb[i]; > + cpu_fprintf(f, "TLB[%d] = %08X %08X %c VPN %05X " > + "PID %02X %c PFN %05X %c%c%c%c\n", > + i, entry->tag, entry->data, > + (entry->tag & (1<<10)) ? 'V' : '-', > + entry->tag >> 12, entry->tag & ((1<mmu.pid_bits)-1), > + (entry->tag & (1<<11)) ? 'G' : '-', > + entry->data & CR_TLBACC_PFN_MASK, > + (entry->data & CR_TLBACC_C) ? 'C' : '-', > + (entry->data & CR_TLBACC_R) ? 'R' : '-', > + (entry->data & CR_TLBACC_W) ? 'W' : '-', > + (entry->data & CR_TLBACC_X) ? 'X' : '-'); > + } > +} > + > diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h > new file mode 100644 > index 0000000..01d67cf > --- /dev/null > +++ b/target-nios2/mmu.h > @@ -0,0 +1,49 @@ > +/* > + * Altera Nios II MMU emulation for qemu. > + * > + * Copyright (C) 2012 Chris Wulff > + * > + * 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 > + * > + */ > + > +struct nios2_tlb_entry { > + target_ulong tag; > + target_ulong data; > +}; > + > +struct nios2_mmu { > + int pid_bits; > + int tlb_num_ways; > + int tlb_num_entries; > + int tlb_entry_mask; > + uint32_t pteaddr_wr; > + uint32_t tlbacc_wr; > + uint32_t tlbmisc_wr; > + struct nios2_tlb_entry *tlb; > +}; > + > +struct nios2_mmu_lookup { > + target_ulong vaddr; > + target_ulong paddr; > + int prot; > +}; > + > +void mmu_flip_um(CPUNios2State *env, unsigned int um); > +unsigned int mmu_translate(CPUNios2State *env, > + struct nios2_mmu_lookup *lu, > + target_ulong vaddr, int rw, int mmu_idx); > +uint32_t mmu_read(CPUNios2State *env, uint32_t rn); > +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v); > +void mmu_init(struct nios2_mmu *mmu); > diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c > new file mode 100644 > index 0000000..37bac66 > --- /dev/null > +++ b/target-nios2/op_helper.c > @@ -0,0 +1,125 @@ > +/* > + * Altera Nios II helper routines. > + * > + * Copyright (C) 2012 Chris Wulff > + * > + * 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 "cpu.h" > +#include "dyngen-exec.h" > +#include "helper.h" > +#include "host-utils.h" > + > +#if !defined(CONFIG_USER_ONLY) > +#define MMUSUFFIX _mmu > +#define SHIFT 0 > +#include "softmmu_template.h" > +#define SHIFT 1 > +#include "softmmu_template.h" > +#define SHIFT 2 > +#include "softmmu_template.h" > +#define SHIFT 3 > +#include "softmmu_template.h" > + > +void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx, > + uintptr_t retaddr) > +{ > + TranslationBlock *tb; > + CPUNios2State *saved_env; > + int ret; > + > + saved_env = env; > + env = env1; > + > + ret = cpu_nios2_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); > + if (unlikely(ret)) { > + if (retaddr) { > + /* now we have a real cpu fault */ > + tb = tb_find_pc(retaddr); > + if (tb) { > + /* The PC is inside the translated code. It means that we have > + a virtual CPU fault */ > + cpu_restore_state(tb, env, retaddr); > + } > + } > + cpu_loop_exit(env); > + } > + env = saved_env; > +} > + > +void helper_raise_exception(uint32_t index) > +{ > + env->exception_index = index; > + cpu_loop_exit(env); > +} > + > +uint32_t helper_mmu_read(uint32_t rn) > +{ > + return mmu_read(env, rn); > +} > + > +void helper_mmu_write(uint32_t rn, uint32_t v) > +{ > + mmu_write(env, rn, v); > +} > + > +void helper_memalign(uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask) > +{ > + if (addr & mask) { > + qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n", > + addr, mask, wr, dr); > + env->regs[CR_BADADDR] = addr; > + env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2; > + helper_raise_exception(EXCP_UNALIGN); > + } > +} > + > +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr, > + int is_write, int is_exec, int is_asi, int size) > +{ > + qemu_log("unassigned access to %08X\n", addr); > +} > + > +uint32_t helper_divs(uint32_t a, uint32_t b) > +{ > + return (int32_t)a / (int32_t)b; > +} > + > +uint32_t helper_divu(uint32_t a, uint32_t b) > +{ > + return a / b; > +} > + > +#ifdef CALL_TRACING > +void helper_call_status(uint32_t pc, uint32_t target) > +{ > + qemu_log("%08X: CALL %08X %s\n", pc, target, lookup_symbol(target)); > +} > + > +void helper_eret_status(uint32_t pc) > +{ > + qemu_log("%08X: ERET STATUS %08X, ESTATUS %08X, EA %08X\n", > + pc, env->regs[CR_STATUS], env->regs[CR_ESTATUS], env->regs[R_EA]); > +} > + > +void helper_ret_status(uint32_t pc) > +{ > + qemu_log("%08X: RET RA %08X\n", pc, env->regs[R_RA]); > +} > +#endif > + > +#endif /* !CONFIG_USER_ONLY */ > + > diff --git a/target-nios2/translate.c b/target-nios2/translate.c > new file mode 100644 > index 0000000..5d0979f > --- /dev/null > +++ b/target-nios2/translate.c > @@ -0,0 +1,252 @@ > +/* > + * Altera Nios II emulation for qemu: main translation routines. > + * > + * Copyright (C) 2012 Chris Wulff > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > + > +#include "cpu.h" > +#include "exec-all.h" > +#include "disas.h" > +#include "helper.h" > +#include "qemu-common.h" > + > +#include "instruction.h" > + > +#define GEN_HELPER 1 > +#include "helper.h" > + > +static const char *regnames[] = { > + "zero", "at", "r2", "r3", > + "r4", "r5", "r6", "r7", > + "r8", "r9", "r10", "r11", > + "r12", "r13", "r14", "r15", > + "r16", "r17", "r18", "r19", > + "r20", "r21", "r22", "r23", > + "et", "bt", "gp", "sp", > + "fp", "ea", "ba", "ra", > + "status", "estatus", "bstatus", "ienable", > + "ipending", "cpuid", "reserved", "exception", > + "pteaddr", "tlbacc", "tlbmisc", "reserved", > + "badaddr", "config", "mpubase", "mpuacc", > + "reserved", "reserved", "reserved", "reserved", > + "reserved", "reserved", "reserved", "reserved", > + "reserved", "reserved", "reserved", "reserved", > + "reserved", "reserved", "reserved", "reserved", > + "rpc" > +}; > + > +static TCGv_ptr cpu_env; > +static TCGv cpu_R[NUM_CORE_REGS]; > + > +#include "gen-icount.h" > + > +/* generate intermediate code for basic block 'tb'. */ > +static void gen_intermediate_code_internal( > + CPUNios2State *env, TranslationBlock *tb, int search_pc) > +{ > + DisasContext dc1, *dc = &dc1; > + int num_insns; > + int max_insns; > + uint32_t next_page_start; > + int j, lj = -1; > + uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; > + > + /* Initialize DC */ > + dc->env = env; > + dc->cpu_R = cpu_R; > + dc->is_jmp = DISAS_NEXT; > + dc->pc = tb->pc; > + dc->tb = tb; > + > + /* Dump the CPU state to the log */ > + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { > + qemu_log("--------------\n"); > + log_cpu_state(env, 0); > + } > + > + /* Set up instruction counts */ > + num_insns = 0; > + max_insns = tb->cflags & CF_COUNT_MASK; > + if (max_insns == 0) { > + max_insns = CF_COUNT_MASK; > + } > + next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; > + > + gen_icount_start(); > + do { > + /* Mark instruction start with associated PC */ > + if (search_pc) { > + j = gen_opc_ptr - gen_opc_buf; > + if (lj < j) { > + lj++; > + while (lj < j) { > + gen_opc_instr_start[lj++] = 0; > + } > + } > + gen_opc_pc[lj] = dc->pc; > + gen_opc_instr_start[lj] = 1; > + gen_opc_icount[lj] = num_insns; > + } > + > + LOG_DIS("%8.8x:\t", dc->pc); > + > + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { > + gen_io_start(); > + } > + > + /* Decode an instruction */ > + handle_instruction(dc); > + > + dc->pc += 4; > + num_insns++; > + > + /* Translation stops when a conditional branch is encountered. > + * Otherwise the subsequent code could get translated several times. > + * Also stop translation when a page boundary is reached. This > + * ensures prefetch aborts occur at the right place. */ > + } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end && > + !env->singlestep_enabled && > + !singlestep && > + dc->pc < next_page_start && > + num_insns < max_insns); > + > + if (tb->cflags & CF_LAST_IO) { > + gen_io_end(); > + } > + > + /* Indicate where the next block should start */ > + switch (dc->is_jmp) { > + case DISAS_NEXT: > + /* Save the current PC back into the CPU register */ > + tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); > + tcg_gen_exit_tb(0); > + break; > + > + default: > + case DISAS_JUMP: > + case DISAS_UPDATE: > + /* The jump will already have updated the PC register */ > + tcg_gen_exit_tb(0); > + break; > + > + case DISAS_TB_JUMP: > + /* nothing more to generate */ > + break; > + } > + > + /* End off the block */ > + gen_icount_end(tb, num_insns); > + *gen_opc_ptr = INDEX_op_end; > + > + /* Mark instruction starts for the final generated instruction */ > + if (search_pc) { > + j = gen_opc_ptr - gen_opc_buf; > + lj++; > + while (lj <= j) { > + gen_opc_instr_start[lj++] = 0; > + } > + } else { > + tb->size = dc->pc - tb->pc; > + tb->icount = num_insns; > + } > + > +#ifdef DEBUG_DISAS > + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { > + qemu_log("----------------\n"); > + qemu_log("IN: %s\n", lookup_symbol(tb->pc)); > + log_target_disas(tb->pc, dc->pc - tb->pc, 0); > + qemu_log("\nisize=%d osize=%td\n", > + dc->pc - tb->pc, gen_opc_ptr - gen_opc_buf); > + } > +#endif > +} > + > +void gen_intermediate_code(CPUNios2State *env, struct TranslationBlock *tb) > +{ > + gen_intermediate_code_internal(env, tb, 0); > +} > + > +void gen_intermediate_code_pc(CPUNios2State *env, struct TranslationBlock *tb) > +{ > + gen_intermediate_code_internal(env, tb, 1); > +} > + > +void cpu_dump_state(CPUNios2State *env, FILE *f, fprintf_function cpu_fprintf, > + int flags) > +{ > + int i; > + > + if (!env || !f) { > + return; > + } > + > + cpu_fprintf(f, "IN: PC=%x %s\n", > + env->regs[R_PC], lookup_symbol(env->regs[R_PC])); > + > + for (i = 0; i < NUM_CORE_REGS; i++) { > + cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); > + if ((i + 1) % 4 == 0) { > + cpu_fprintf(f, "\n"); > + } > + } > + cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", > + env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, > + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, > + env->mmu.tlbacc_wr); > + cpu_fprintf(f, "\n\n"); > +} > + > +Nios2CPU *cpu_nios2_init(const char *cpu_model) > +{ > + Nios2CPU *cpu; > + int i; > + > + cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU)); > + > + cpu->env.reset_addr = RESET_ADDRESS; > + cpu->env.exception_addr = EXCEPTION_ADDRESS; > + cpu->env.fast_tlb_miss_addr = FAST_TLB_MISS_ADDRESS; > + > + cpu_reset(CPU(cpu)); > + qemu_init_vcpu(&cpu->env); > + > + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); > + > + for (i = 0; i < NUM_CORE_REGS; i++) { > + cpu_R[i] = tcg_global_mem_new(TCG_AREG0, > + offsetof(CPUNios2State, regs[i]), > + regnames[i]); > + } > + > +#define GEN_HELPER 2 > +#include "helper.h" > + > + return cpu; > +} > + > +void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, int pc_pos) > +{ > + env->regs[R_PC] = gen_opc_pc[pc_pos]; > +} > + > -- > 1.7.9.5 > > > -- Aurelien Jarno GPG: 1024D/F1BCDB73 aurelien@aurel32.net http://www.aurel32.net