diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 07:50:24 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 07:50:24 -0800 |
commit | 060629c6ef0b7e5c267d84c91600113264d33120 (patch) | |
tree | 18fcb144ac71b9c4d08ee5d1dc58e2b16c109a5a /target-lm32 | |
download | qemu-060629c6ef0b7e5c267d84c91600113264d33120.tar.gz qemu-060629c6ef0b7e5c267d84c91600113264d33120.tar.bz2 qemu-060629c6ef0b7e5c267d84c91600113264d33120.zip |
Imported Upstream version 1.2.0upstream/1.2.0
Diffstat (limited to 'target-lm32')
-rw-r--r-- | target-lm32/Makefile.objs | 4 | ||||
-rw-r--r-- | target-lm32/README | 46 | ||||
-rw-r--r-- | target-lm32/TODO | 3 | ||||
-rw-r--r-- | target-lm32/cpu-qom.h | 71 | ||||
-rw-r--r-- | target-lm32/cpu.c | 81 | ||||
-rw-r--r-- | target-lm32/cpu.h | 268 | ||||
-rw-r--r-- | target-lm32/helper.c | 235 | ||||
-rw-r--r-- | target-lm32/helper.h | 14 | ||||
-rw-r--r-- | target-lm32/machine.c | 33 | ||||
-rw-r--r-- | target-lm32/op_helper.c | 104 | ||||
-rw-r--r-- | target-lm32/translate.c | 1231 |
11 files changed, 2090 insertions, 0 deletions
diff --git a/target-lm32/Makefile.objs b/target-lm32/Makefile.objs new file mode 100644 index 000000000..2e0e093e1 --- /dev/null +++ b/target-lm32/Makefile.objs @@ -0,0 +1,4 @@ +obj-y += translate.o op_helper.o helper.o cpu.o +obj-$(CONFIG_SOFTMMU) += machine.o + +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) diff --git a/target-lm32/README b/target-lm32/README new file mode 100644 index 000000000..a1c2c7eb1 --- /dev/null +++ b/target-lm32/README @@ -0,0 +1,46 @@ +LatticeMico32 target +-------------------- + +General +------- +All opcodes including the JUART CSRs are supported. + + +JTAG UART +--------- +JTAG UART is routed to a serial console device. For the current boards it +is the second one. Ie to enable it in the qemu virtual console window use +the following command line parameters: + -serial vc -serial vc +This will make serial0 (the lm32_uart) and serial1 (the JTAG UART) +available as virtual consoles. + + +Programmatically terminate the emulator +---------------------------------------- +Originally neither the LatticeMico32 nor its peripherals support a +mechanism to shut down the machine. Emulation aware programs can write to a +to a special register within the system control block to shut down the +virtual machine. For more details see hw/lm32_sys.c. The lm32-evr is the +first BSP which instantiate this model. A (32 bit) write to 0xfff0000 +causes a vm shutdown. + + +Special instructions +-------------------- +The translation recognizes one special instruction to halt the cpu: + and r0, r0, r0 +On real hardware this instruction is a nop. It is not used by GCC and +should (hopefully) not be used within hand-crafted assembly. +Insert this instruction in your idle loop to reduce the cpu load on the +host. + + +Ignoring the MSB of the address bus +----------------------------------- +Some SoC ignores the MSB on the address bus. Thus creating a shadow memory +area. As a general rule, 0x00000000-0x7fffffff is cached, whereas +0x80000000-0xffffffff is not cached and used to access IO devices. This +behaviour can be enabled with: + cpu_lm32_set_phys_msb_ignore(env, 1); + diff --git a/target-lm32/TODO b/target-lm32/TODO new file mode 100644 index 000000000..b9ea0c8db --- /dev/null +++ b/target-lm32/TODO @@ -0,0 +1,3 @@ +* disassembler (lm32-dis.c) +* linux-user emulation +* native bp/wp emulation (?) diff --git a/target-lm32/cpu-qom.h b/target-lm32/cpu-qom.h new file mode 100644 index 000000000..4ae2eddaf --- /dev/null +++ b/target-lm32/cpu-qom.h @@ -0,0 +1,71 @@ +/* + * QEMU LatticeMico32 CPU + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ +#ifndef QEMU_LM32_CPU_QOM_H +#define QEMU_LM32_CPU_QOM_H + +#include "qemu/cpu.h" +#include "cpu.h" + +#define TYPE_LM32_CPU "lm32-cpu" + +#define LM32_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(LM32CPUClass, (klass), TYPE_LM32_CPU) +#define LM32_CPU(obj) \ + OBJECT_CHECK(LM32CPU, (obj), TYPE_LM32_CPU) +#define LM32_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(LM32CPUClass, (obj), TYPE_LM32_CPU) + +/** + * LM32CPUClass: + * @parent_reset: The parent class' reset handler. + * + * A LatticeMico32 CPU model. + */ +typedef struct LM32CPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + void (*parent_reset)(CPUState *cpu); +} LM32CPUClass; + +/** + * LM32CPU: + * @env: #CPULM32State + * + * A LatticeMico32 CPU. + */ +typedef struct LM32CPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPULM32State env; +} LM32CPU; + +static inline LM32CPU *lm32_env_get_cpu(CPULM32State *env) +{ + return LM32_CPU(container_of(env, LM32CPU, env)); +} + +#define ENV_GET_CPU(e) CPU(lm32_env_get_cpu(e)) + + +#endif diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c new file mode 100644 index 000000000..caa483407 --- /dev/null +++ b/target-lm32/cpu.c @@ -0,0 +1,81 @@ +/* + * QEMU LatticeMico32 CPU + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "cpu.h" +#include "qemu-common.h" + + +/* CPUClass::reset() */ +static void lm32_cpu_reset(CPUState *s) +{ + LM32CPU *cpu = LM32_CPU(s); + LM32CPUClass *lcc = LM32_CPU_GET_CLASS(cpu); + CPULM32State *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); + } + + lcc->parent_reset(s); + + tlb_flush(env, 1); + + /* reset cpu state */ + memset(env, 0, offsetof(CPULM32State, breakpoints)); +} + +static void lm32_cpu_initfn(Object *obj) +{ + LM32CPU *cpu = LM32_CPU(obj); + CPULM32State *env = &cpu->env; + + cpu_exec_init(env); + + env->flags = 0; + + cpu_reset(CPU(cpu)); +} + +static void lm32_cpu_class_init(ObjectClass *oc, void *data) +{ + LM32CPUClass *lcc = LM32_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + + lcc->parent_reset = cc->reset; + cc->reset = lm32_cpu_reset; +} + +static const TypeInfo lm32_cpu_type_info = { + .name = TYPE_LM32_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(LM32CPU), + .instance_init = lm32_cpu_initfn, + .abstract = false, + .class_size = sizeof(LM32CPUClass), + .class_init = lm32_cpu_class_init, +}; + +static void lm32_cpu_register_types(void) +{ + type_register_static(&lm32_cpu_type_info); +} + +type_init(lm32_cpu_register_types) diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h new file mode 100644 index 000000000..da80469f5 --- /dev/null +++ b/target-lm32/cpu.h @@ -0,0 +1,268 @@ +/* + * LatticeMico32 virtual CPU header. + * + * Copyright (c) 2010 Michael Walle <michael@walle.cc> + * + * 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 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef CPU_LM32_H +#define CPU_LM32_H + +#define TARGET_LONG_BITS 32 + +#define CPUArchState struct CPULM32State + +#include "config.h" +#include "qemu-common.h" +#include "cpu-defs.h" +struct CPULM32State; +typedef struct CPULM32State CPULM32State; + +#define TARGET_HAS_ICE 1 + +#define ELF_MACHINE EM_LATTICEMICO32 + +#define NB_MMU_MODES 1 +#define TARGET_PAGE_BITS 12 +static inline int cpu_mmu_index(CPULM32State *env) +{ + return 0; +} + +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +/* Exceptions indices */ +enum { + EXCP_RESET = 0, + EXCP_BREAKPOINT, + EXCP_INSN_BUS_ERROR, + EXCP_WATCHPOINT, + EXCP_DATA_BUS_ERROR, + EXCP_DIVIDE_BY_ZERO, + EXCP_IRQ, + EXCP_SYSTEMCALL +}; + +/* Registers */ +enum { + R_R0 = 0, R_R1, R_R2, R_R3, R_R4, R_R5, R_R6, R_R7, R_R8, R_R9, R_R10, + R_R11, R_R12, R_R13, R_R14, R_R15, R_R16, R_R17, R_R18, R_R19, R_R20, + R_R21, R_R22, R_R23, R_R24, R_R25, R_R26, R_R27, R_R28, R_R29, R_R30, + R_R31 +}; + +/* Register aliases */ +enum { + R_GP = R_R26, + R_FP = R_R27, + R_SP = R_R28, + R_RA = R_R29, + R_EA = R_R30, + R_BA = R_R31 +}; + +/* IE flags */ +enum { + IE_IE = (1<<0), + IE_EIE = (1<<1), + IE_BIE = (1<<2), +}; + +/* DC flags */ +enum { + DC_SS = (1<<0), + DC_RE = (1<<1), + DC_C0 = (1<<2), + DC_C1 = (1<<3), + DC_C2 = (1<<4), + DC_C3 = (1<<5), +}; + +/* CFG mask */ +enum { + CFG_M = (1<<0), + CFG_D = (1<<1), + CFG_S = (1<<2), + CFG_U = (1<<3), + CFG_X = (1<<4), + CFG_CC = (1<<5), + CFG_IC = (1<<6), + CFG_DC = (1<<7), + CFG_G = (1<<8), + CFG_H = (1<<9), + CFG_R = (1<<10), + CFG_J = (1<<11), + CFG_INT_SHIFT = 12, + CFG_BP_SHIFT = 18, + CFG_WP_SHIFT = 22, + CFG_REV_SHIFT = 26, +}; + +/* CSRs */ +enum { + CSR_IE = 0x00, + CSR_IM = 0x01, + CSR_IP = 0x02, + CSR_ICC = 0x03, + CSR_DCC = 0x04, + CSR_CC = 0x05, + CSR_CFG = 0x06, + CSR_EBA = 0x07, + CSR_DC = 0x08, + CSR_DEBA = 0x09, + CSR_JTX = 0x0e, + CSR_JRX = 0x0f, + CSR_BP0 = 0x10, + CSR_BP1 = 0x11, + CSR_BP2 = 0x12, + CSR_BP3 = 0x13, + CSR_WP0 = 0x18, + CSR_WP1 = 0x19, + CSR_WP2 = 0x1a, + CSR_WP3 = 0x1b, +}; + +enum { + LM32_FEATURE_MULTIPLY = 1, + LM32_FEATURE_DIVIDE = 2, + LM32_FEATURE_SHIFT = 4, + LM32_FEATURE_SIGN_EXTEND = 8, + LM32_FEATURE_I_CACHE = 16, + LM32_FEATURE_D_CACHE = 32, + LM32_FEATURE_CYCLE_COUNT = 64, +}; + +enum { + LM32_FLAG_IGNORE_MSB = 1, +}; + +struct CPULM32State { + /* general registers */ + uint32_t regs[32]; + + /* special registers */ + uint32_t pc; /* program counter */ + uint32_t ie; /* interrupt enable */ + uint32_t icc; /* instruction cache control */ + uint32_t dcc; /* data cache control */ + uint32_t cc; /* cycle counter */ + uint32_t cfg; /* configuration */ + + /* debug registers */ + uint32_t dc; /* debug control */ + uint32_t bp[4]; /* breakpoint addresses */ + uint32_t wp[4]; /* watchpoint addresses */ + + CPU_COMMON + + uint32_t eba; /* exception base address */ + uint32_t deba; /* debug exception base address */ + + /* interrupt controller handle for callbacks */ + DeviceState *pic_state; + /* JTAG UART handle for callbacks */ + DeviceState *juart_state; + + /* processor core features */ + uint32_t features; + uint32_t flags; + uint8_t num_bps; + uint8_t num_wps; + +}; + +#include "cpu-qom.h" + +LM32CPU *cpu_lm32_init(const char *cpu_model); +void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf); +int cpu_lm32_exec(CPULM32State *s); +void cpu_lm32_close(CPULM32State *s); +void do_interrupt(CPULM32State *env); +/* you can call this signal handler from your SIGBUS and SIGSEGV + signal handlers to inform the virtual CPU of exceptions. non zero + is returned if the signal was handled by the virtual CPU. */ +int cpu_lm32_signal_handler(int host_signum, void *pinfo, + void *puc); +void lm32_translate_init(void); +void cpu_lm32_set_phys_msb_ignore(CPULM32State *env, int value); + +static inline CPULM32State *cpu_init(const char *cpu_model) +{ + LM32CPU *cpu = cpu_lm32_init(cpu_model); + if (cpu == NULL) { + return NULL; + } + return &cpu->env; +} + +#define cpu_list cpu_lm32_list +#define cpu_exec cpu_lm32_exec +#define cpu_gen_code cpu_lm32_gen_code +#define cpu_signal_handler cpu_lm32_signal_handler + +#define CPU_SAVE_VERSION 1 + +int cpu_lm32_handle_mmu_fault(CPULM32State *env, target_ulong address, int rw, + int mmu_idx); +#define cpu_handle_mmu_fault cpu_lm32_handle_mmu_fault + +#if defined(CONFIG_USER_ONLY) +static inline void cpu_clone_regs(CPULM32State *env, target_ulong newsp) +{ + if (newsp) { + env->regs[R_SP] = newsp; + } + env->regs[R_R1] = 0; +} +#endif + +static inline void cpu_set_tls(CPULM32State *env, target_ulong newtls) +{ +} + +static inline int cpu_interrupts_enabled(CPULM32State *env) +{ + return env->ie & IE_IE; +} + +#include "cpu-all.h" + +static inline target_ulong cpu_get_pc(CPULM32State *env) +{ + return env->pc; +} + +static inline void cpu_get_tb_cpu_state(CPULM32State *env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = 0; +} + +static inline bool cpu_has_work(CPULM32State *env) +{ + return env->interrupt_request & CPU_INTERRUPT_HARD; +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPULM32State *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + +#endif diff --git a/target-lm32/helper.c b/target-lm32/helper.c new file mode 100644 index 000000000..1ea477fea --- /dev/null +++ b/target-lm32/helper.c @@ -0,0 +1,235 @@ +/* + * LatticeMico32 helper routines. + * + * Copyright (c) 2010 Michael Walle <michael@walle.cc> + * + * 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 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "host-utils.h" + +int cpu_lm32_handle_mmu_fault(CPULM32State *env, target_ulong address, int rw, + int mmu_idx) +{ + int prot; + + address &= TARGET_PAGE_MASK; + prot = PAGE_BITS; + if (env->flags & LM32_FLAG_IGNORE_MSB) { + tlb_set_page(env, address, address & 0x7fffffff, prot, mmu_idx, + TARGET_PAGE_SIZE); + } else { + tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); + } + + return 0; +} + +target_phys_addr_t cpu_get_phys_page_debug(CPULM32State *env, target_ulong addr) +{ + return addr & TARGET_PAGE_MASK; +} + +void do_interrupt(CPULM32State *env) +{ + qemu_log_mask(CPU_LOG_INT, + "exception at pc=%x type=%x\n", env->pc, env->exception_index); + + switch (env->exception_index) { + case EXCP_INSN_BUS_ERROR: + case EXCP_DATA_BUS_ERROR: + case EXCP_DIVIDE_BY_ZERO: + case EXCP_IRQ: + case EXCP_SYSTEMCALL: + /* non-debug exceptions */ + env->regs[R_EA] = env->pc; + env->ie |= (env->ie & IE_IE) ? IE_EIE : 0; + env->ie &= ~IE_IE; + if (env->dc & DC_RE) { + env->pc = env->deba + (env->exception_index * 32); + } else { + env->pc = env->eba + (env->exception_index * 32); + } + log_cpu_state_mask(CPU_LOG_INT, env, 0); + break; + case EXCP_BREAKPOINT: + case EXCP_WATCHPOINT: + /* debug exceptions */ + env->regs[R_BA] = env->pc; + env->ie |= (env->ie & IE_IE) ? IE_BIE : 0; + env->ie &= ~IE_IE; + env->pc = env->deba + (env->exception_index * 32); + log_cpu_state_mask(CPU_LOG_INT, env, 0); + break; + default: + cpu_abort(env, "unhandled exception type=%d\n", + env->exception_index); + break; + } +} + +typedef struct { + const char *name; + uint32_t revision; + uint8_t num_interrupts; + uint8_t num_breakpoints; + uint8_t num_watchpoints; + uint32_t features; +} LM32Def; + +static const LM32Def lm32_defs[] = { + { + .name = "lm32-basic", + .revision = 3, + .num_interrupts = 32, + .num_breakpoints = 4, + .num_watchpoints = 4, + .features = (LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_CYCLE_COUNT), + }, + { + .name = "lm32-standard", + .revision = 3, + .num_interrupts = 32, + .num_breakpoints = 4, + .num_watchpoints = 4, + .features = (LM32_FEATURE_MULTIPLY + | LM32_FEATURE_DIVIDE + | LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_I_CACHE + | LM32_FEATURE_CYCLE_COUNT), + }, + { + .name = "lm32-full", + .revision = 3, + .num_interrupts = 32, + .num_breakpoints = 4, + .num_watchpoints = 4, + .features = (LM32_FEATURE_MULTIPLY + | LM32_FEATURE_DIVIDE + | LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_I_CACHE + | LM32_FEATURE_D_CACHE + | LM32_FEATURE_CYCLE_COUNT), + } +}; + +void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf) +{ + int i; + + cpu_fprintf(f, "Available CPUs:\n"); + for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) { + cpu_fprintf(f, " %s\n", lm32_defs[i].name); + } +} + +static const LM32Def *cpu_lm32_find_by_name(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) { + if (strcasecmp(name, lm32_defs[i].name) == 0) { + return &lm32_defs[i]; + } + } + + return NULL; +} + +static uint32_t cfg_by_def(const LM32Def *def) +{ + uint32_t cfg = 0; + + if (def->features & LM32_FEATURE_MULTIPLY) { + cfg |= CFG_M; + } + + if (def->features & LM32_FEATURE_DIVIDE) { + cfg |= CFG_D; + } + + if (def->features & LM32_FEATURE_SHIFT) { + cfg |= CFG_S; + } + + if (def->features & LM32_FEATURE_SIGN_EXTEND) { + cfg |= CFG_X; + } + + if (def->features & LM32_FEATURE_I_CACHE) { + cfg |= CFG_IC; + } + + if (def->features & LM32_FEATURE_D_CACHE) { + cfg |= CFG_DC; + } + + if (def->features & LM32_FEATURE_CYCLE_COUNT) { + cfg |= CFG_CC; + } + + cfg |= (def->num_interrupts << CFG_INT_SHIFT); + cfg |= (def->num_breakpoints << CFG_BP_SHIFT); + cfg |= (def->num_watchpoints << CFG_WP_SHIFT); + cfg |= (def->revision << CFG_REV_SHIFT); + + return cfg; +} + +LM32CPU *cpu_lm32_init(const char *cpu_model) +{ + LM32CPU *cpu; + CPULM32State *env; + const LM32Def *def; + static int tcg_initialized; + + def = cpu_lm32_find_by_name(cpu_model); + if (!def) { + return NULL; + } + + cpu = LM32_CPU(object_new(TYPE_LM32_CPU)); + env = &cpu->env; + + env->features = def->features; + env->num_bps = def->num_breakpoints; + env->num_wps = def->num_watchpoints; + env->cfg = cfg_by_def(def); + + qemu_init_vcpu(env); + + if (tcg_enabled() && !tcg_initialized) { + tcg_initialized = 1; + lm32_translate_init(); + } + + return cpu; +} + +/* Some soc ignores the MSB on the address bus. Thus creating a shadow memory + * area. As a general rule, 0x00000000-0x7fffffff is cached, whereas + * 0x80000000-0xffffffff is not cached and used to access IO devices. */ +void cpu_lm32_set_phys_msb_ignore(CPULM32State *env, int value) +{ + if (value) { + env->flags |= LM32_FLAG_IGNORE_MSB; + } else { + env->flags &= ~LM32_FLAG_IGNORE_MSB; + } +} diff --git a/target-lm32/helper.h b/target-lm32/helper.h new file mode 100644 index 000000000..9d335efc4 --- /dev/null +++ b/target-lm32/helper.h @@ -0,0 +1,14 @@ +#include "def-helper.h" + +DEF_HELPER_1(raise_exception, void, i32) +DEF_HELPER_0(hlt, void) +DEF_HELPER_1(wcsr_im, void, i32) +DEF_HELPER_1(wcsr_ip, void, i32) +DEF_HELPER_1(wcsr_jtx, void, i32) +DEF_HELPER_1(wcsr_jrx, void, i32) +DEF_HELPER_0(rcsr_im, i32) +DEF_HELPER_0(rcsr_ip, i32) +DEF_HELPER_0(rcsr_jtx, i32) +DEF_HELPER_0(rcsr_jrx, i32) + +#include "def-helper.h" diff --git a/target-lm32/machine.c b/target-lm32/machine.c new file mode 100644 index 000000000..6802e818f --- /dev/null +++ b/target-lm32/machine.c @@ -0,0 +1,33 @@ +#include "hw/hw.h" +#include "hw/boards.h" + +static const VMStateDescription vmstate_cpu = { + .name = "cpu", + .version_id = CPU_SAVE_VERSION, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, CPULM32State, 32), + VMSTATE_UINT32(pc, CPULM32State), + VMSTATE_UINT32(ie, CPULM32State), + VMSTATE_UINT32(icc, CPULM32State), + VMSTATE_UINT32(dcc, CPULM32State), + VMSTATE_UINT32(cc, CPULM32State), + VMSTATE_UINT32(eba, CPULM32State), + VMSTATE_UINT32(dc, CPULM32State), + VMSTATE_UINT32(deba, CPULM32State), + VMSTATE_UINT32_ARRAY(bp, CPULM32State, 4), + VMSTATE_UINT32_ARRAY(wp, CPULM32State, 4), + VMSTATE_END_OF_LIST() + } +}; + +void cpu_save(QEMUFile *f, void *opaque) +{ + vmstate_save_state(f, &vmstate_cpu, opaque); +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return vmstate_load_state(f, &vmstate_cpu, opaque, version_id); +} diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c new file mode 100644 index 000000000..51edc1a0e --- /dev/null +++ b/target-lm32/op_helper.c @@ -0,0 +1,104 @@ +#include <assert.h> +#include "cpu.h" +#include "dyngen-exec.h" +#include "helper.h" +#include "host-utils.h" + +#include "hw/lm32_pic.h" +#include "hw/lm32_juart.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 helper_raise_exception(uint32_t index) +{ + env->exception_index = index; + cpu_loop_exit(env); +} + +void helper_hlt(void) +{ + env->halted = 1; + env->exception_index = EXCP_HLT; + cpu_loop_exit(env); +} + +void helper_wcsr_im(uint32_t im) +{ + lm32_pic_set_im(env->pic_state, im); +} + +void helper_wcsr_ip(uint32_t im) +{ + lm32_pic_set_ip(env->pic_state, im); +} + +void helper_wcsr_jtx(uint32_t jtx) +{ + lm32_juart_set_jtx(env->juart_state, jtx); +} + +void helper_wcsr_jrx(uint32_t jrx) +{ + lm32_juart_set_jrx(env->juart_state, jrx); +} + +uint32_t helper_rcsr_im(void) +{ + return lm32_pic_get_im(env->pic_state); +} + +uint32_t helper_rcsr_ip(void) +{ + return lm32_pic_get_ip(env->pic_state); +} + +uint32_t helper_rcsr_jtx(void) +{ + return lm32_juart_get_jtx(env->juart_state); +} + +uint32_t helper_rcsr_jrx(void) +{ + return lm32_juart_get_jrx(env->juart_state); +} + +/* Try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill(CPULM32State *env1, target_ulong addr, int is_write, int mmu_idx, + uintptr_t retaddr) +{ + TranslationBlock *tb; + CPULM32State *saved_env; + int ret; + + saved_env = env; + env = env1; + + ret = cpu_lm32_handle_mmu_fault(env, addr, is_write, mmu_idx); + 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; +} +#endif + diff --git a/target-lm32/translate.c b/target-lm32/translate.c new file mode 100644 index 000000000..872a2ba65 --- /dev/null +++ b/target-lm32/translate.c @@ -0,0 +1,1231 @@ +/* + * LatticeMico32 main translation routines. + * + * Copyright (c) 2010 Michael Walle <michael@walle.cc> + * + * 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 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "disas.h" +#include "helper.h" +#include "tcg-op.h" + +#include "hw/lm32_pic.h" + +#define GEN_HELPER 1 +#include "helper.h" + +#define DISAS_LM32 1 +#if DISAS_LM32 +# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) +#else +# define LOG_DIS(...) do { } while (0) +#endif + +#define EXTRACT_FIELD(src, start, end) \ + (((src) >> start) & ((1 << (end - start + 1)) - 1)) + +#define MEM_INDEX 0 + +static TCGv_ptr cpu_env; +static TCGv cpu_R[32]; +static TCGv cpu_pc; +static TCGv cpu_ie; +static TCGv cpu_icc; +static TCGv cpu_dcc; +static TCGv cpu_cc; +static TCGv cpu_cfg; +static TCGv cpu_eba; +static TCGv cpu_dc; +static TCGv cpu_deba; +static TCGv cpu_bp[4]; +static TCGv cpu_wp[4]; + +#include "gen-icount.h" + +enum { + OP_FMT_RI, + OP_FMT_RR, + OP_FMT_CR, + OP_FMT_I +}; + +/* This is the state at translation time. */ +typedef struct DisasContext { + CPULM32State *env; + target_ulong pc; + + /* Decoder. */ + int format; + uint32_t ir; + uint8_t opcode; + uint8_t r0, r1, r2, csr; + uint16_t imm5; + uint16_t imm16; + uint32_t imm26; + + unsigned int delayed_branch; + unsigned int tb_flags, synced_flags; /* tb dependent flags. */ + int is_jmp; + + int nr_nops; + struct TranslationBlock *tb; + int singlestep_enabled; +} DisasContext; + +static const char *regnames[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26/gp", "r27/fp", "r28/sp", "r29/ra", + "r30/ea", "r31/ba", "bp0", "bp1", "bp2", "bp3", "wp0", + "wp1", "wp2", "wp3" +}; + +static inline int zero_extend(unsigned int val, int width) +{ + return val & ((1 << width) - 1); +} + +static inline int sign_extend(unsigned int val, int width) +{ + int sval; + + /* LSL. */ + val <<= 32 - width; + sval = val; + /* ASR. */ + sval >>= 32 - width; + + return sval; +} + +static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) +{ + TCGv_i32 tmp = tcg_const_i32(index); + + gen_helper_raise_exception(tmp); + tcg_temp_free_i32(tmp); +} + +static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +{ + TranslationBlock *tb; + + tb = dc->tb; + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) && + likely(!dc->singlestep_enabled)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_tl(cpu_pc, dest); + tcg_gen_exit_tb((tcg_target_long)tb + n); + } else { + tcg_gen_movi_tl(cpu_pc, dest); + if (dc->singlestep_enabled) { + t_gen_raise_exception(dc, EXCP_DEBUG); + } + tcg_gen_exit_tb(0); + } +} + +static void dec_add(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + if (dc->r0 == R_R0) { + if (dc->r1 == R_R0 && dc->imm16 == 0) { + LOG_DIS("nop\n"); + } else { + LOG_DIS("mvi r%d, %d\n", dc->r1, sign_extend(dc->imm16, 16)); + } + } else { + LOG_DIS("addi r%d, r%d, %d\n", dc->r1, dc->r0, + sign_extend(dc->imm16, 16)); + } + } else { + LOG_DIS("add r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_addi_tl(cpu_R[dc->r1], cpu_R[dc->r0], + sign_extend(dc->imm16, 16)); + } else { + tcg_gen_add_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_and(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("andi r%d, r%d, %d\n", dc->r1, dc->r0, + zero_extend(dc->imm16, 16)); + } else { + LOG_DIS("and r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_andi_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + } else { + if (dc->r0 == 0 && dc->r1 == 0 && dc->r2 == 0) { + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + gen_helper_hlt(); + } else { + tcg_gen_and_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } + } +} + +static void dec_andhi(DisasContext *dc) +{ + LOG_DIS("andhi r%d, r%d, %d\n", dc->r2, dc->r0, dc->imm16); + + tcg_gen_andi_tl(cpu_R[dc->r1], cpu_R[dc->r0], (dc->imm16 << 16)); +} + +static void dec_b(DisasContext *dc) +{ + if (dc->r0 == R_RA) { + LOG_DIS("ret\n"); + } else if (dc->r0 == R_EA) { + LOG_DIS("eret\n"); + } else if (dc->r0 == R_BA) { + LOG_DIS("bret\n"); + } else { + LOG_DIS("b r%d\n", dc->r0); + } + + /* restore IE.IE in case of an eret */ + if (dc->r0 == R_EA) { + TCGv t0 = tcg_temp_new(); + int l1 = gen_new_label(); + tcg_gen_andi_tl(t0, cpu_ie, IE_EIE); + tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_IE); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_EIE, l1); + tcg_gen_andi_tl(cpu_ie, cpu_ie, ~IE_IE); + gen_set_label(l1); + tcg_temp_free(t0); + } else if (dc->r0 == R_BA) { + TCGv t0 = tcg_temp_new(); + int l1 = gen_new_label(); + tcg_gen_andi_tl(t0, cpu_ie, IE_BIE); + tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_IE); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_BIE, l1); + tcg_gen_andi_tl(cpu_ie, cpu_ie, ~IE_IE); + gen_set_label(l1); + tcg_temp_free(t0); + } + tcg_gen_mov_tl(cpu_pc, cpu_R[dc->r0]); + + dc->is_jmp = DISAS_JUMP; +} + +static void dec_bi(DisasContext *dc) +{ + LOG_DIS("bi %d\n", sign_extend(dc->imm26 << 2, 26)); + + gen_goto_tb(dc, 0, dc->pc + (sign_extend(dc->imm26 << 2, 26))); + + dc->is_jmp = DISAS_TB_JUMP; +} + +static inline void gen_cond_branch(DisasContext *dc, int cond) +{ + int l1; + + l1 = gen_new_label(); + tcg_gen_brcond_tl(cond, cpu_R[dc->r0], cpu_R[dc->r1], l1); + gen_goto_tb(dc, 0, dc->pc + 4); + gen_set_label(l1); + gen_goto_tb(dc, 1, dc->pc + (sign_extend(dc->imm16 << 2, 16))); + dc->is_jmp = DISAS_TB_JUMP; +} + +static void dec_be(DisasContext *dc) +{ + LOG_DIS("be r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_EQ); +} + +static void dec_bg(DisasContext *dc) +{ + LOG_DIS("bg r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16 * 4)); + + gen_cond_branch(dc, TCG_COND_GT); +} + +static void dec_bge(DisasContext *dc) +{ + LOG_DIS("bge r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_GE); +} + +static void dec_bgeu(DisasContext *dc) +{ + LOG_DIS("bgeu r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_GEU); +} + +static void dec_bgu(DisasContext *dc) +{ + LOG_DIS("bgu r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_GTU); +} + +static void dec_bne(DisasContext *dc) +{ + LOG_DIS("bne r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_NE); +} + +static void dec_call(DisasContext *dc) +{ + LOG_DIS("call r%d\n", dc->r0); + + tcg_gen_movi_tl(cpu_R[R_RA], dc->pc + 4); + tcg_gen_mov_tl(cpu_pc, cpu_R[dc->r0]); + + dc->is_jmp = DISAS_JUMP; +} + +static void dec_calli(DisasContext *dc) +{ + LOG_DIS("calli %d\n", sign_extend(dc->imm26, 26) * 4); + + tcg_gen_movi_tl(cpu_R[R_RA], dc->pc + 4); + gen_goto_tb(dc, 0, dc->pc + (sign_extend(dc->imm26 << 2, 26))); + + dc->is_jmp = DISAS_TB_JUMP; +} + +static inline void gen_compare(DisasContext *dc, int cond) +{ + int rX = (dc->format == OP_FMT_RR) ? dc->r2 : dc->r1; + int rY = (dc->format == OP_FMT_RR) ? dc->r0 : dc->r0; + int rZ = (dc->format == OP_FMT_RR) ? dc->r1 : -1; + + if (dc->format == OP_FMT_RI) { + tcg_gen_setcondi_tl(cond, cpu_R[rX], cpu_R[rY], + sign_extend(dc->imm16, 16)); + } else { + tcg_gen_setcond_tl(cond, cpu_R[rX], cpu_R[rY], cpu_R[rZ]); + } +} + +static void dec_cmpe(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpei r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpe r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_EQ); +} + +static void dec_cmpg(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgi r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpg r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GT); +} + +static void dec_cmpge(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgei r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpge r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GE); +} + +static void dec_cmpgeu(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgeui r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpgeu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GEU); +} + +static void dec_cmpgu(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgui r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpgu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GTU); +} + +static void dec_cmpne(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpnei r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpne r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_NE); +} + +static void dec_divu(DisasContext *dc) +{ + int l1; + + LOG_DIS("divu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + + if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { + cpu_abort(dc->env, "hardware divider is not available\n"); + } + + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_R[dc->r1], 0, l1); + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_DIVIDE_BY_ZERO); + gen_set_label(l1); + tcg_gen_divu_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); +} + +static void dec_lb(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lb r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld8s(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lbu(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lbu r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld8u(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lh(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lh r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld16s(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lhu(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lhu r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld16u(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lw(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lw r%d, (r%d+%d)\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16)); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld32s(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_modu(DisasContext *dc) +{ + int l1; + + LOG_DIS("modu r%d, r%d, %d\n", dc->r2, dc->r0, dc->r1); + + if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { + cpu_abort(dc->env, "hardware divider is not available\n"); + } + + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_R[dc->r1], 0, l1); + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_DIVIDE_BY_ZERO); + gen_set_label(l1); + tcg_gen_remu_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); +} + +static void dec_mul(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("muli r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("mul r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_MULTIPLY)) { + cpu_abort(dc->env, "hardware multiplier is not available\n"); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_muli_tl(cpu_R[dc->r1], cpu_R[dc->r0], + sign_extend(dc->imm16, 16)); + } else { + tcg_gen_mul_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_nor(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("nori r%d, r%d, %d\n", dc->r0, dc->r1, + zero_extend(dc->imm16, 16)); + } else { + LOG_DIS("nor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + TCGv t0 = tcg_temp_new(); + tcg_gen_movi_tl(t0, zero_extend(dc->imm16, 16)); + tcg_gen_nor_tl(cpu_R[dc->r1], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } else { + tcg_gen_nor_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_or(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("ori r%d, r%d, %d\n", dc->r1, dc->r0, + zero_extend(dc->imm16, 16)); + } else { + if (dc->r1 == R_R0) { + LOG_DIS("mv r%d, r%d\n", dc->r2, dc->r0); + } else { + LOG_DIS("or r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_ori_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + } else { + tcg_gen_or_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_orhi(DisasContext *dc) +{ + if (dc->r0 == R_R0) { + LOG_DIS("mvhi r%d, %d\n", dc->r1, dc->imm16); + } else { + LOG_DIS("orhi r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm16); + } + + tcg_gen_ori_tl(cpu_R[dc->r1], cpu_R[dc->r0], (dc->imm16 << 16)); +} + +static void dec_scall(DisasContext *dc) +{ + if (dc->imm5 == 7) { + LOG_DIS("scall\n"); + } else if (dc->imm5 == 2) { + LOG_DIS("break\n"); + } else { + cpu_abort(dc->env, "invalid opcode\n"); + } + + if (dc->imm5 == 7) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_SYSTEMCALL); + } else { + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_BREAKPOINT); + } +} + +static void dec_rcsr(DisasContext *dc) +{ + LOG_DIS("rcsr r%d, %d\n", dc->r2, dc->csr); + + switch (dc->csr) { + case CSR_IE: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_ie); + break; + case CSR_IM: + gen_helper_rcsr_im(cpu_R[dc->r2]); + break; + case CSR_IP: + gen_helper_rcsr_ip(cpu_R[dc->r2]); + break; + case CSR_CC: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_cc); + break; + case CSR_CFG: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_cfg); + break; + case CSR_EBA: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_eba); + break; + case CSR_DC: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_dc); + break; + case CSR_DEBA: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_deba); + break; + case CSR_JTX: + gen_helper_rcsr_jtx(cpu_R[dc->r2]); + break; + case CSR_JRX: + gen_helper_rcsr_jrx(cpu_R[dc->r2]); + break; + case CSR_ICC: + case CSR_DCC: + case CSR_BP0: + case CSR_BP1: + case CSR_BP2: + case CSR_BP3: + case CSR_WP0: + case CSR_WP1: + case CSR_WP2: + case CSR_WP3: + cpu_abort(dc->env, "invalid read access csr=%x\n", dc->csr); + break; + default: + cpu_abort(dc->env, "read_csr: unknown csr=%x\n", dc->csr); + break; + } +} + +static void dec_sb(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("sb (r%d+%d), r%d\n", dc->r0, dc->imm16, dc->r1); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_st8(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_sextb(DisasContext *dc) +{ + LOG_DIS("sextb r%d, r%d\n", dc->r2, dc->r0); + + if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { + cpu_abort(dc->env, "hardware sign extender is not available\n"); + } + + tcg_gen_ext8s_tl(cpu_R[dc->r2], cpu_R[dc->r0]); +} + +static void dec_sexth(DisasContext *dc) +{ + LOG_DIS("sexth r%d, r%d\n", dc->r2, dc->r0); + + if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { + cpu_abort(dc->env, "hardware sign extender is not available\n"); + } + + tcg_gen_ext16s_tl(cpu_R[dc->r2], cpu_R[dc->r0]); +} + +static void dec_sh(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("sh (r%d+%d), r%d\n", dc->r0, dc->imm16, dc->r1); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_st16(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_sl(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("sli r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm5); + } else { + LOG_DIS("sl r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + cpu_abort(dc->env, "hardware shifter is not available\n"); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_shli_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); + } else { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + tcg_gen_shl_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } +} + +static void dec_sr(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("sri r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm5); + } else { + LOG_DIS("sr r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + if (dc->format == OP_FMT_RI) { + /* TODO: check r1 == 1 during runtime */ + } else { + if (dc->imm5 != 1) { + cpu_abort(dc->env, "hardware shifter is not available\n"); + } + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_sari_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); + } else { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + tcg_gen_sar_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } +} + +static void dec_sru(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("srui r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm5); + } else { + LOG_DIS("sru r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + if (dc->format == OP_FMT_RI) { + /* TODO: check r1 == 1 during runtime */ + } else { + if (dc->imm5 != 1) { + cpu_abort(dc->env, "hardware shifter is not available\n"); + } + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_shri_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); + } else { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + tcg_gen_shr_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } +} + +static void dec_sub(DisasContext *dc) +{ + LOG_DIS("sub r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + + tcg_gen_sub_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); +} + +static void dec_sw(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("sw (r%d+%d), r%d\n", dc->r0, sign_extend(dc->imm16, 16), dc->r1); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_st32(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_user(DisasContext *dc) +{ + LOG_DIS("user"); + + cpu_abort(dc->env, "user insn undefined\n"); +} + +static void dec_wcsr(DisasContext *dc) +{ + int no; + + LOG_DIS("wcsr r%d, %d\n", dc->r1, dc->csr); + + switch (dc->csr) { + case CSR_IE: + tcg_gen_mov_tl(cpu_ie, cpu_R[dc->r1]); + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + dc->is_jmp = DISAS_UPDATE; + break; + case CSR_IM: + /* mark as an io operation because it could cause an interrupt */ + if (use_icount) { + gen_io_start(); + } + gen_helper_wcsr_im(cpu_R[dc->r1]); + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + if (use_icount) { + gen_io_end(); + } + dc->is_jmp = DISAS_UPDATE; + break; + case CSR_IP: + /* mark as an io operation because it could cause an interrupt */ + if (use_icount) { + gen_io_start(); + } + gen_helper_wcsr_ip(cpu_R[dc->r1]); + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + if (use_icount) { + gen_io_end(); + } + dc->is_jmp = DISAS_UPDATE; + break; + case CSR_ICC: + /* TODO */ + break; + case CSR_DCC: + /* TODO */ + break; + case CSR_EBA: + tcg_gen_mov_tl(cpu_eba, cpu_R[dc->r1]); + break; + case CSR_DEBA: + tcg_gen_mov_tl(cpu_deba, cpu_R[dc->r1]); + break; + case CSR_JTX: + gen_helper_wcsr_jtx(cpu_R[dc->r1]); + break; + case CSR_JRX: + gen_helper_wcsr_jrx(cpu_R[dc->r1]); + break; + case CSR_DC: + tcg_gen_mov_tl(cpu_dc, cpu_R[dc->r1]); + break; + case CSR_BP0: + case CSR_BP1: + case CSR_BP2: + case CSR_BP3: + no = dc->csr - CSR_BP0; + if (dc->env->num_bps <= no) { + cpu_abort(dc->env, "breakpoint #%i is not available\n", no); + } + tcg_gen_mov_tl(cpu_bp[no], cpu_R[dc->r1]); + break; + case CSR_WP0: + case CSR_WP1: + case CSR_WP2: + case CSR_WP3: + no = dc->csr - CSR_WP0; + if (dc->env->num_wps <= no) { + cpu_abort(dc->env, "watchpoint #%i is not available\n", no); + } + tcg_gen_mov_tl(cpu_wp[no], cpu_R[dc->r1]); + break; + case CSR_CC: + case CSR_CFG: + cpu_abort(dc->env, "invalid write access csr=%x\n", dc->csr); + break; + default: + cpu_abort(dc->env, "write_csr unknown csr=%x\n", dc->csr); + break; + } +} + +static void dec_xnor(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("xnori r%d, r%d, %d\n", dc->r0, dc->r1, + zero_extend(dc->imm16, 16)); + } else { + if (dc->r1 == R_R0) { + LOG_DIS("not r%d, r%d\n", dc->r2, dc->r0); + } else { + LOG_DIS("xnor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_xori_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + tcg_gen_not_tl(cpu_R[dc->r1], cpu_R[dc->r1]); + } else { + tcg_gen_eqv_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_xor(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("xori r%d, r%d, %d\n", dc->r0, dc->r1, + zero_extend(dc->imm16, 16)); + } else { + LOG_DIS("xor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_xori_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + } else { + tcg_gen_xor_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_ill(DisasContext *dc) +{ + cpu_abort(dc->env, "unknown opcode 0x%02x\n", dc->opcode); +} + +typedef void (*DecoderInfo)(DisasContext *dc); +static const DecoderInfo decinfo[] = { + dec_sru, dec_nor, dec_mul, dec_sh, dec_lb, dec_sr, dec_xor, dec_lh, + dec_and, dec_xnor, dec_lw, dec_lhu, dec_sb, dec_add, dec_or, dec_sl, + dec_lbu, dec_be, dec_bg, dec_bge, dec_bgeu, dec_bgu, dec_sw, dec_bne, + dec_andhi, dec_cmpe, dec_cmpg, dec_cmpge, dec_cmpgeu, dec_cmpgu, dec_orhi, + dec_cmpne, + dec_sru, dec_nor, dec_mul, dec_divu, dec_rcsr, dec_sr, dec_xor, dec_ill, + dec_and, dec_xnor, dec_ill, dec_scall, dec_sextb, dec_add, dec_or, dec_sl, + dec_b, dec_modu, dec_sub, dec_user, dec_wcsr, dec_ill, dec_call, dec_sexth, + dec_bi, dec_cmpe, dec_cmpg, dec_cmpge, dec_cmpgeu, dec_cmpgu, dec_calli, + dec_cmpne +}; + +static inline void decode(DisasContext *dc) +{ + uint32_t ir; + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(dc->pc); + } + + dc->ir = ir = ldl_code(dc->pc); + LOG_DIS("%8.8x\t", dc->ir); + + /* try guessing 'empty' instruction memory, although it may be a valid + * instruction sequence (eg. srui r0, r0, 0) */ + if (dc->ir) { + dc->nr_nops = 0; + } else { + LOG_DIS("nr_nops=%d\t", dc->nr_nops); + dc->nr_nops++; + if (dc->nr_nops > 4) { + cpu_abort(dc->env, "fetching nop sequence\n"); + } + } + + dc->opcode = EXTRACT_FIELD(ir, 26, 31); + + dc->imm5 = EXTRACT_FIELD(ir, 0, 4); + dc->imm16 = EXTRACT_FIELD(ir, 0, 15); + dc->imm26 = EXTRACT_FIELD(ir, 0, 25); + + dc->csr = EXTRACT_FIELD(ir, 21, 25); + dc->r0 = EXTRACT_FIELD(ir, 21, 25); + dc->r1 = EXTRACT_FIELD(ir, 16, 20); + dc->r2 = EXTRACT_FIELD(ir, 11, 15); + + /* bit 31 seems to indicate insn type. */ + if (ir & (1 << 31)) { + dc->format = OP_FMT_RR; + } else { + dc->format = OP_FMT_RI; + } + + assert(ARRAY_SIZE(decinfo) == 64); + assert(dc->opcode < 64); + + decinfo[dc->opcode](dc); +} + +static void check_breakpoint(CPULM32State *env, DisasContext *dc) +{ + CPUBreakpoint *bp; + + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc->pc) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + } + } + } +} + +/* generate intermediate code for basic block 'tb'. */ +static void gen_intermediate_code_internal(CPULM32State *env, + TranslationBlock *tb, int search_pc) +{ + struct DisasContext ctx, *dc = &ctx; + uint16_t *gen_opc_end; + uint32_t pc_start; + int j, lj; + uint32_t next_page_start; + int num_insns; + int max_insns; + + qemu_log_try_set_file(stderr); + + pc_start = tb->pc; + dc->env = env; + dc->tb = tb; + + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + dc->is_jmp = DISAS_NEXT; + dc->pc = pc_start; + dc->singlestep_enabled = env->singlestep_enabled; + dc->nr_nops = 0; + + if (pc_start & 3) { + cpu_abort(env, "LM32: unaligned PC=%x\n", pc_start); + } + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("-----------------------------------------\n"); + log_cpu_state(env, 0); + } + + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + lj = -1; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + + gen_icount_start(); + do { + check_breakpoint(env, dc); + + 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; + } + + /* Pretty disas. */ + LOG_DIS("%8.8x:\t", dc->pc); + + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + decode(dc); + dc->pc += 4; + num_insns++; + + } 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(); + } + + if (unlikely(env->singlestep_enabled)) { + if (dc->is_jmp == DISAS_NEXT) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + } + t_gen_raise_exception(dc, EXCP_DEBUG); + } else { + switch (dc->is_jmp) { + case DISAS_NEXT: + gen_goto_tb(dc, 1, dc->pc); + break; + default: + case DISAS_JUMP: + case DISAS_UPDATE: + /* indicate that the hash table must be used + to find the next TB */ + tcg_gen_exit_tb(0); + break; + case DISAS_TB_JUMP: + /* nothing more to generate */ + break; + } + } + + gen_icount_end(tb, num_insns); + *gen_opc_ptr = INDEX_op_end; + 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 - pc_start; + tb->icount = num_insns; + } + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("\n"); + log_target_disas(pc_start, dc->pc - pc_start, 0); + qemu_log("\nisize=%d osize=%td\n", + dc->pc - pc_start, gen_opc_ptr - gen_opc_buf); + } +#endif +} + +void gen_intermediate_code(CPULM32State *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 0); +} + +void gen_intermediate_code_pc(CPULM32State *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 1); +} + +void cpu_dump_state(CPULM32State *env, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + int i; + + if (!env || !f) { + return; + } + + cpu_fprintf(f, "IN: PC=%x %s\n", + env->pc, lookup_symbol(env->pc)); + + cpu_fprintf(f, "ie=%8.8x (IE=%x EIE=%x BIE=%x) im=%8.8x ip=%8.8x\n", + env->ie, + (env->ie & IE_IE) ? 1 : 0, + (env->ie & IE_EIE) ? 1 : 0, + (env->ie & IE_BIE) ? 1 : 0, + lm32_pic_get_im(env->pic_state), + lm32_pic_get_ip(env->pic_state)); + cpu_fprintf(f, "eba=%8.8x deba=%8.8x\n", + env->eba, + env->deba); + + for (i = 0; i < 32; i++) { + cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); + if ((i + 1) % 4 == 0) { + cpu_fprintf(f, "\n"); + } + } + cpu_fprintf(f, "\n\n"); +} + +void restore_state_to_opc(CPULM32State *env, TranslationBlock *tb, int pc_pos) +{ + env->pc = gen_opc_pc[pc_pos]; +} + +void lm32_translate_init(void) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { + cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, regs[i]), + regnames[i]); + } + + for (i = 0; i < ARRAY_SIZE(cpu_bp); i++) { + cpu_bp[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, bp[i]), + regnames[32+i]); + } + + for (i = 0; i < ARRAY_SIZE(cpu_wp); i++) { + cpu_wp[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, wp[i]), + regnames[36+i]); + } + + cpu_pc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, pc), + "pc"); + cpu_ie = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, ie), + "ie"); + cpu_icc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, icc), + "icc"); + cpu_dcc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, dcc), + "dcc"); + cpu_cc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, cc), + "cc"); + cpu_cfg = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, cfg), + "cfg"); + cpu_eba = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, eba), + "eba"); + cpu_dc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, dc), + "dc"); + cpu_deba = tcg_global_mem_new(TCG_AREG0, + offsetof(CPULM32State, deba), + "deba"); +} + |