From b04d98905400aeb7dd62ce938d7eecddf2816817 Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Mon, 24 Jan 2011 12:56:55 +0100 Subject: [PATCH] SPARC: Emulation of Leon3 Leon3 is an open-source VHDL System-On-Chip, well known in space industry (more information on http://www.gaisler.com). Leon3 is made of multiple components available in the GrLib VHDL library. Three devices are implemented: uart, timers and IRQ manager. You can find code for these peripherals in the grlib_* files. Signed-off-by: Fabien Chouteau Signed-off-by: Blue Swirl --- Makefile.target | 5 +- hw/leon3.c | 218 +++++++++++++++++++++++++++++++++++++++ target-sparc/cpu.h | 37 ++++--- target-sparc/helper.c | 7 +- target-sparc/helper.h | 1 + target-sparc/op_helper.c | 156 +++++++++++++++++++++++++++- target-sparc/translate.c | 13 ++- trace-events | 4 + 8 files changed, 419 insertions(+), 22 deletions(-) create mode 100644 hw/leon3.c diff --git a/Makefile.target b/Makefile.target index cd2abde394..b0ba95f76e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -286,7 +286,10 @@ obj-sparc-y += cirrus_vga.o else obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o + +# GRLIB +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o endif obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o diff --git a/hw/leon3.c b/hw/leon3.c new file mode 100644 index 0000000000..69d8f3b655 --- /dev/null +++ b/hw/leon3.c @@ -0,0 +1,218 @@ +/* + * QEMU Leon3 System Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "qemu-timer.h" +#include "qemu-char.h" +#include "sysemu.h" +#include "boards.h" +#include "loader.h" +#include "elf.h" +#include "trace.h" + +#include "grlib.h" + +/* Default system clock. */ +#define CPU_CLK (40 * 1000 * 1000) + +#define PROM_FILENAME "u-boot.bin" + +#define MAX_PILS 16 + +typedef struct ResetData { + CPUState *env; + uint32_t entry; /* save kernel entry in case of reset */ +} ResetData; + +static void main_cpu_reset(void *opaque) +{ + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; + + cpu_reset(env); + + env->halted = 0; + env->pc = s->entry; + env->npc = s->entry + 4; +} + +static void leon3_irq_ack(void *irq_manager, int intno) +{ + grlib_irqmp_ack((DeviceState *)irq_manager, intno); + leon3_cache_control_int(); +} + +static void leon3_set_pil_in(void *opaque, uint32_t pil_in) +{ + CPUState *env = (CPUState *)opaque; + + assert(env != NULL); + + env->pil_in = pil_in; + + if (env->pil_in && (env->interrupt_index == 0 || + (env->interrupt_index & ~15) == TT_EXTINT)) { + unsigned int i; + + for (i = 15; i > 0; i--) { + if (env->pil_in & (1 << i)) { + int old_interrupt = env->interrupt_index; + + env->interrupt_index = TT_EXTINT | i; + if (old_interrupt != env->interrupt_index) { + trace_leon3_set_irq(i); + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + break; + } + } + } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { + trace_leon3_reset_irq(env->interrupt_index & 15); + env->interrupt_index = 0; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void leon3_generic_hw_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + ram_addr_t ram_offset, prom_offset; + int ret; + char *filename; + qemu_irq *cpu_irqs = NULL; + int bios_size; + int prom_size; + ResetData *reset_info; + + /* Init CPU */ + if (!cpu_model) { + cpu_model = "LEON3"; + } + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n"); + exit(1); + } + + cpu_sparc_set_id(env, 0); + + /* Reset data */ + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + qemu_register_reset(main_cpu_reset, reset_info); + + /* Allocate IRQ manager */ + grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS, &leon3_set_pil_in); + + env->qemu_irq_ack = leon3_irq_ack; + + /* Allocate RAM */ + if ((uint64_t)ram_size > (1UL << 30)) { + fprintf(stderr, + "qemu: Too much memory for this machine: %d, maximum 1G\n", + (unsigned int)(ram_size / (1024 * 1024))); + exit(1); + } + + ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size); + cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM); + + /* Allocate BIOS */ + prom_size = 8 * 1024 * 1024; /* 8Mb */ + prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size); + cpu_register_physical_memory(0x00000000, prom_size, + prom_offset | IO_MEM_ROM); + + /* Load boot prom */ + if (bios_name == NULL) { + bios_name = PROM_FILENAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + + bios_size = get_image_size(filename); + + if (bios_size > prom_size) { + fprintf(stderr, "qemu: could not load prom '%s': file too big\n", + filename); + exit(1); + } + + if (bios_size > 0) { + ret = load_image_targphys(filename, 0x00000000, bios_size); + if (ret < 0 || ret > prom_size) { + fprintf(stderr, "qemu: could not load prom '%s'\n", filename); + exit(1); + } + } else if (kernel_filename == NULL) { + fprintf(stderr, "Can't read bios image %s\n", filename); + exit(1); + } + + /* Can directly load an application. */ + if (kernel_filename != NULL) { + long kernel_size; + uint64_t entry; + + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + 1 /* big endian */, ELF_MACHINE, 0); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + if (bios_size <= 0) { + /* If there is no bios/monitor, start the application. */ + env->pc = entry; + env->npc = entry + 4; + reset_info->entry = entry; + } + } + + /* Allocate timers */ + grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6); + + /* Allocate uart */ + if (serial_hds[0]) { + grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]); + } +} + +QEMUMachine leon3_generic_machine = { + .name = "leon3_generic", + .desc = "Leon-3 generic", + .init = leon3_generic_hw_init, + .use_scsi = 0, +}; + +static void leon3_machine_init(void) +{ + qemu_register_machine(&leon3_generic_machine); +} + +machine_init(leon3_machine_init); diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 7225b2ed3c..5c50d9e535 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -252,20 +252,21 @@ typedef struct sparc_def_t { uint32_t maxtl; } sparc_def_t; -#define CPU_FEATURE_FLOAT (1 << 0) -#define CPU_FEATURE_FLOAT128 (1 << 1) -#define CPU_FEATURE_SWAP (1 << 2) -#define CPU_FEATURE_MUL (1 << 3) -#define CPU_FEATURE_DIV (1 << 4) -#define CPU_FEATURE_FLUSH (1 << 5) -#define CPU_FEATURE_FSQRT (1 << 6) -#define CPU_FEATURE_FMUL (1 << 7) -#define CPU_FEATURE_VIS1 (1 << 8) -#define CPU_FEATURE_VIS2 (1 << 9) -#define CPU_FEATURE_FSMULD (1 << 10) -#define CPU_FEATURE_HYPV (1 << 11) -#define CPU_FEATURE_CMT (1 << 12) -#define CPU_FEATURE_GL (1 << 13) +#define CPU_FEATURE_FLOAT (1 << 0) +#define CPU_FEATURE_FLOAT128 (1 << 1) +#define CPU_FEATURE_SWAP (1 << 2) +#define CPU_FEATURE_MUL (1 << 3) +#define CPU_FEATURE_DIV (1 << 4) +#define CPU_FEATURE_FLUSH (1 << 5) +#define CPU_FEATURE_FSQRT (1 << 6) +#define CPU_FEATURE_FMUL (1 << 7) +#define CPU_FEATURE_VIS1 (1 << 8) +#define CPU_FEATURE_VIS2 (1 << 9) +#define CPU_FEATURE_FSMULD (1 << 10) +#define CPU_FEATURE_HYPV (1 << 11) +#define CPU_FEATURE_CMT (1 << 12) +#define CPU_FEATURE_GL (1 << 13) +#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ #ifndef TARGET_SPARC64 #define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ @@ -437,6 +438,12 @@ typedef struct CPUSPARCState { #define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER) #endif sparc_def_t *def; + + void *irq_manager; + void (*qemu_irq_ack) (void *irq_manager, int intno); + + /* Leon3 cache control */ + uint32_t cache_control; } CPUSPARCState; #ifndef NO_CPU_IO_DEFS @@ -469,6 +476,8 @@ int cpu_cwp_inc(CPUState *env1, int cwp); int cpu_cwp_dec(CPUState *env1, int cwp); void cpu_set_cwp(CPUState *env1, int new_cwp); +void leon3_cache_control_int(void); + /* sun4m.c, sun4u.c */ void cpu_check_irqs(CPUSPARCState *env); diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 6b337ca091..ec6ac271f1 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -770,6 +770,7 @@ void cpu_reset(CPUSPARCState *env) env->pc = 0; env->npc = env->pc + 4; #endif + env->cache_control = 0; } static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model) @@ -1274,20 +1275,20 @@ static const sparc_def_t sparc_defs[] = { .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, }, { .name = "LEON3", .iu_version = 0xf3000000, .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ .mmu_version = 0xf3000000, - .mmu_bm = 0x00004000, + .mmu_bm = 0x00000000, .mmu_ctpr_mask = 0x007ffff0, .mmu_cxr_mask = 0x0000003f, .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, }, #endif }; diff --git a/target-sparc/helper.h b/target-sparc/helper.h index e6d82f9eab..12e8557133 100644 --- a/target-sparc/helper.h +++ b/target-sparc/helper.h @@ -85,6 +85,7 @@ DEF_HELPER_0(fcmpeq_fcc2, void) DEF_HELPER_0(fcmpeq_fcc3, void) #endif DEF_HELPER_1(raise_exception, void, int) +DEF_HELPER_0(shutdown, void) #define F_HELPER_0_0(name) DEF_HELPER_0(f ## name, void) #define F_HELPER_DQ_0_0(name) \ F_HELPER_0_0(name ## d); \ diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index b70970ac2a..d3e1b63539 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -1,6 +1,7 @@ #include "exec.h" #include "host-utils.h" #include "helper.h" +#include "sysemu.h" //#define DEBUG_MMU //#define DEBUG_MXCC @@ -9,6 +10,7 @@ //#define DEBUG_ASI //#define DEBUG_PCALL //#define DEBUG_PSTATE +//#define DEBUG_CACHE_CONTROL #ifdef DEBUG_MMU #define DPRINTF_MMU(fmt, ...) \ @@ -36,6 +38,13 @@ #define DPRINTF_PSTATE(fmt, ...) do {} while (0) #endif +#ifdef DEBUG_CACHE_CONTROL +#define DPRINTF_CACHE_CONTROL(fmt, ...) \ + do { printf("CACHE_CONTROL: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_CACHE_CONTROL(fmt, ...) do {} while (0) +#endif + #ifdef TARGET_SPARC64 #ifndef TARGET_ABI32 #define AM_CHECK(env1) ((env1)->pstate & PS_AM) @@ -49,6 +58,27 @@ #define QT0 (env->qt0) #define QT1 (env->qt1) +/* Leon3 cache control */ + +/* Cache control: emulate the behavior of cache control registers but without + any effect on the emulated */ + +#define CACHE_STATE_MASK 0x3 +#define CACHE_DISABLED 0x0 +#define CACHE_FROZEN 0x1 +#define CACHE_ENABLED 0x3 + +/* Cache Control register fields */ + +#define CACHE_CTRL_IF (1 << 4) /* Instruction Cache Freeze on Interrupt */ +#define CACHE_CTRL_DF (1 << 5) /* Data Cache Freeze on Interrupt */ +#define CACHE_CTRL_DP (1 << 14) /* Data cache flush pending */ +#define CACHE_CTRL_IP (1 << 15) /* Instruction cache flush pending */ +#define CACHE_CTRL_IB (1 << 16) /* Instruction burst fetch */ +#define CACHE_CTRL_FI (1 << 21) /* Flush Instruction cache (Write only) */ +#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */ +#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */ + #if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) static void do_unassigned_access(target_ulong addr, int is_write, int is_exec, int is_asi, int size); @@ -294,6 +324,13 @@ void HELPER(raise_exception)(int tt) raise_exception(tt); } +void helper_shutdown(void) +{ +#if !defined(CONFIG_USER_ONLY) + qemu_system_shutdown_request(); +#endif +} + void helper_check_align(target_ulong addr, uint32_t align) { if (addr & align) { @@ -1612,6 +1649,103 @@ static void dump_asi(const char *txt, target_ulong addr, int asi, int size, #ifndef TARGET_SPARC64 #ifndef CONFIG_USER_ONLY + + +/* Leon3 cache control */ + +void leon3_cache_control_int(void) +{ + uint32_t state = 0; + + if (env->cache_control & CACHE_CTRL_IF) { + /* Instruction cache state */ + state = env->cache_control & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF_CACHE_CONTROL("Instruction cache: freeze\n"); + } + + env->cache_control &= ~CACHE_STATE_MASK; + env->cache_control |= state; + } + + if (env->cache_control & CACHE_CTRL_DF) { + /* Data cache state */ + state = (env->cache_control >> 2) & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF_CACHE_CONTROL("Data cache: freeze\n"); + } + + env->cache_control &= ~(CACHE_STATE_MASK << 2); + env->cache_control |= (state << 2); + } +} + +static void leon3_cache_control_st(target_ulong addr, uint64_t val, int size) +{ + DPRINTF_CACHE_CONTROL("st addr:%08x, val:%" PRIx64 ", size:%d\n", + addr, val, size); + + if (size != 4) { + DPRINTF_CACHE_CONTROL("32bits only\n"); + return; + } + + switch (addr) { + case 0x00: /* Cache control */ + + /* These values must always be read as zeros */ + val &= ~CACHE_CTRL_FD; + val &= ~CACHE_CTRL_FI; + val &= ~CACHE_CTRL_IB; + val &= ~CACHE_CTRL_IP; + val &= ~CACHE_CTRL_DP; + + env->cache_control = val; + break; + case 0x04: /* Instruction cache configuration */ + case 0x08: /* Data cache configuration */ + /* Read Only */ + break; + default: + DPRINTF_CACHE_CONTROL("write unknown register %08x\n", addr); + break; + }; +} + +static uint64_t leon3_cache_control_ld(target_ulong addr, int size) +{ + uint64_t ret = 0; + + if (size != 4) { + DPRINTF_CACHE_CONTROL("32bits only\n"); + return 0; + } + + switch (addr) { + case 0x00: /* Cache control */ + ret = env->cache_control; + break; + + /* Configuration registers are read and only always keep those + predefined values */ + + case 0x04: /* Instruction cache configuration */ + ret = 0x10220000; + break; + case 0x08: /* Data cache configuration */ + ret = 0x18220000; + break; + default: + DPRINTF_CACHE_CONTROL("read unknown register %08x\n", addr); + break; + }; + DPRINTF_CACHE_CONTROL("st addr:%08x, ret:%" PRIx64 ", size:%d\n", + addr, ret, size); + return ret; +} + uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) { uint64_t ret = 0; @@ -1621,8 +1755,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) helper_check_align(addr, size - 1); switch (asi) { - case 2: /* SuperSparc MXCC registers */ + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ switch (addr) { + case 0x00: /* Leon3 Cache Control */ + case 0x08: /* Leon3 Instruction Cache config */ + case 0x0C: /* Leon3 Date Cache config */ + ret = leon3_cache_control_ld(addr, size); + break; case 0x01c00a00: /* MXCC control register */ if (size == 8) ret = env->mxccregs[3]; @@ -1850,8 +1989,14 @@ void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size) { helper_check_align(addr, size - 1); switch(asi) { - case 2: /* SuperSparc MXCC registers */ + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ switch (addr) { + case 0x00: /* Leon3 Cache Control */ + case 0x08: /* Leon3 Instruction Cache config */ + case 0x0C: /* Leon3 Date Cache config */ + leon3_cache_control_st(addr, val, size); + break; + case 0x01c00000: /* MXCC stream data register 0 */ if (size == 8) env->mxccdata[0] = val; @@ -4177,6 +4322,13 @@ void do_interrupt(CPUState *env) env->pc = env->tbr; env->npc = env->pc + 4; env->exception_index = -1; + +#if !defined(CONFIG_USER_ONLY) + /* IRQ acknowledgment */ + if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) { + env->qemu_irq_ack(env->irq_manager, intno); + } +#endif } #endif diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 21c567562e..dff0f19f70 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -1997,8 +1997,9 @@ static void disas_sparc_insn(DisasContext * dc) } else tcg_gen_mov_tl(cpu_dst, cpu_src1); } + cond = GET_FIELD(insn, 3, 6); - if (cond == 0x8) { + if (cond == 0x8) { /* Trap Always */ save_state(dc, cpu_cond); if ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc)) @@ -2007,7 +2008,15 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK); tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP); tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_dst); - gen_helper_raise_exception(cpu_tmp32); + + if (rs2 == 0 && + dc->def->features & CPU_FEATURE_TA0_SHUTDOWN) { + + gen_helper_shutdown(); + + } else { + gen_helper_raise_exception(cpu_tmp32); + } } else if (cond != 0) { TCGv r_cond = tcg_temp_new(); int l1; diff --git a/trace-events b/trace-events index 8e8573ed90..c75a7a9824 100644 --- a/trace-events +++ b/trace-events @@ -244,3 +244,7 @@ disable grlib_irqmp_unknown_register(const char *op, uint64_t val) "%s unknown r # hw/grlib_apbuart.c disable grlib_apbuart_event(int event) "event:%d" disable grlib_apbuart_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" + +# hw/leon3.c +disable leon3_set_irq(int intno) "Set CPU IRQ %d" +disable leon3_reset_irq(int intno) "Reset CPU IRQ %d"