/* * 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 <stdio.h> #include <string.h> #include <assert.h> #include "config.h" #include "cpu.h" #include "exec-all.h" #include "host-utils.h" int cpu_lm32_handle_mmu_fault(CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu) { 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(CPUState *env, target_ulong addr) { return addr & TARGET_PAGE_MASK; } void do_interrupt(CPUState *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; } CPUState *cpu_lm32_init(const char *cpu_model) { CPUState *env; const LM32Def *def; static int tcg_initialized; def = cpu_lm32_find_by_name(cpu_model); if (!def) { return NULL; } env = qemu_mallocz(sizeof(CPUState)); env->features = def->features; env->num_bps = def->num_breakpoints; env->num_wps = def->num_watchpoints; env->cfg = cfg_by_def(def); env->flags = 0; cpu_exec_init(env); cpu_reset(env); if (!tcg_initialized) { tcg_initialized = 1; lm32_translate_init(); } return env; } /* 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(CPUState *env, int value) { if (value) { env->flags |= LM32_FLAG_IGNORE_MSB; } else { env->flags &= ~LM32_FLAG_IGNORE_MSB; } } void cpu_reset(CPUState *env) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); log_cpu_state(env, 0); } tlb_flush(env, 1); /* reset cpu state */ memset(env, 0, offsetof(CPULM32State, breakpoints)); }