On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau <chout...@adacore.com> wrote: > On 12/11/2010 10:56 AM, Blue Swirl wrote: >> >> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chout...@adacore.com> >> wrote: >>> >>> On 12/06/2010 06:53 PM, Blue Swirl wrote: >>>> >>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chout...@adacore.com> >>>> wrote: >>>>> >>>>> Signed-off-by: Fabien Chouteau<chout...@adacore.com> >>>>> --- >>>>> Makefile.target | 5 +- >>>>> hw/leon3.c | 310 >>>>> ++++++++++++++++++++++++++++++++++++++++++++++ >>>>> target-sparc/cpu.h | 10 ++ >>>>> target-sparc/helper.c | 2 +- >>>>> target-sparc/op_helper.c | 30 ++++- >>>>> 5 files changed, 353 insertions(+), 4 deletions(-) >>>>> >>>>> diff --git a/Makefile.target b/Makefile.target >>>>> index 2800f47..f40e04f 100644 >>>>> --- a/Makefile.target >>>>> +++ b/Makefile.target >>>>> @@ -290,7 +290,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 0000000..ba61081 >>>>> --- /dev/null >>>>> +++ b/hw/leon3.c >>>>> @@ -0,0 +1,310 @@ >>>>> +/* >>>>> + * QEMU Leon3 System Emulator >>>>> + * >>>>> + * Copyright (c) 2010 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 "grlib.h" >>>>> + >>>>> +/* #define DEBUG_LEON3 */ >>>>> + >>>>> +#ifdef DEBUG_LEON3 >>>>> +#define DPRINTF(fmt, ...) \ >>>>> + do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0) >>>>> +#else >>>>> +#define DPRINTF(fmt, ...) >>>>> +#endif >>>>> + >>>>> +/* Default system clock. */ >>>>> +#define CPU_CLK (40 * 1000 * 1000) >>>>> + >>>>> +#define PROM_FILENAME "u-boot.bin" >>>>> + >>>>> +#define MAX_PILS 16 >>>>> + >>>>> +typedef struct Leon3State >>>>> +{ >>>>> + uint32_t cache_control; >>>>> + uint32_t inst_cache_conf; >>>>> + uint32_t data_cache_conf; >>>>> + >>>>> + uint64_t entry; /* save kernel entry in case of reset >>>>> */ >>>>> +} Leon3State; >>>>> + >>>>> +Leon3State leon3_state; >>>> >>>> Again global state, please refactor. Perhaps most of the cache >>>> handling code belong to target-sparc/op_helper.c and this structure to >>>> CPUSPARCState. >>> >>> I will try to find a solution for that. >>> Is it OK to add some Leon3 specific stuff in the CPUSPARCState? >> >> Yes, no problem. You can also drop the intermediate Leon3State >> structure if there is no benefit. >> >>>>> + >>>>> +/* Cache control: emulate the behavior of cache control registers but >>>>> without >>>>> + any effect on the emulated CPU */ >>>>> + >>>>> +#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 */ >>>>> + >>>>> +void leon3_cache_control_int(void) >>>>> +{ >>>>> + uint32_t state = 0; >>>>> + >>>>> + if (leon3_state.cache_control& CACHE_CTRL_IF) { >>>>> + /* Instruction cache state */ >>>>> + state = leon3_state.cache_control& 0x3; >>>> >>>> Please add a new define CACHE_CTRL_xxx to replace 0x3. >>>> >>> >>> Done. >>> >>>>> + if (state == CACHE_ENABLED) { >>>>> + state = CACHE_FROZEN; >>>>> + DPRINTF("Instruction cache: freeze\n"); >>>>> + } >>>>> + >>>>> + leon3_state.cache_control&= ~0x3; >>>>> + leon3_state.cache_control |= state; >>>>> + } >>>>> + >>>>> + if (leon3_state.cache_control& CACHE_CTRL_DF) { >>>>> + /* Data cache state */ >>>>> + state = (leon3_state.cache_control>> 2)& 0x3; >>>>> + if (state == CACHE_ENABLED) { >>>>> + state = CACHE_FROZEN; >>>>> + DPRINTF("Data cache: freeze\n"); >>>>> + } >>>>> + >>>>> + leon3_state.cache_control&= ~(0x3<< 2); >>>>> + leon3_state.cache_control |= (state<< 2); >>>>> + } >>>>> +} >>>>> + >>>>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size) >>>>> +{ >>>>> + DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned >>>>> int)addr, >>>>> + (unsigned int)val, size); >>>> >>>> There's PRIx64 to print uint64_t portably, then the casts can be >>>> removed. >>>> >>> >>> Fixed. >>> >>>>> + >>>>> + if (size != 4) { >>>>> + DPRINTF(" CC 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; >>>>> + >>>>> + leon3_state.cache_control = val; >>>>> + break; >>>>> + case 0x04: /* Instruction cache configuration */ >>>>> + case 0x08: /* Data cache configuration */ >>>>> + /* Read Only */ >>>>> + break; >>>>> + default: >>>>> + DPRINTF(" CC write unknown register 0x%04x\n", (int)addr); >>>>> + break; >>>>> + }; >>>>> +} >>>>> + >>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size) >>>>> +{ >>>>> + uint64_t ret = 0; >>>>> + >>>>> + if (size != 4) { >>>>> + DPRINTF(" CC 32bits only\n"); >>>>> + return 0; >>>>> + } >>>>> + >>>>> + switch (addr) { >>>>> + case 0x00: /* Cache control */ >>>>> + ret = leon3_state.cache_control; >>>>> + break; >>>>> + case 0x04: /* Instruction cache configuration */ >>>>> + ret = leon3_state.inst_cache_conf; >>>>> + break; >>>>> + case 0x08: /* Data cache configuration */ >>>>> + ret = leon3_state.data_cache_conf; >>>>> + break; >>>>> + default: >>>>> + DPRINTF(" CC read unknown register 0x%04x\n", (int)addr); >>>>> + break; >>>>> + }; >>>>> + DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned >>>>> int)addr, >>>>> + size, (long unsigned int)ret ); >>>>> + return ret; >>>>> +} >>>>> + >>>>> +void leon3_shutdown(void) >>>>> +{ >>>>> + qemu_system_shutdown_request(); >>>>> +} >>>>> + >>>>> +static void main_cpu_reset(void *opaque) >>>>> +{ >>>>> + CPUState *env = opaque; >>>> >>>> Here you can introduce a helper structure to pass PC and NPC, like >>>> sun4u.c ResetData. Then the global state should not be needed anymore. >>>> >>> >>> OK, I've used the sun4u.c reset scheme. >>> >>>>> + >>>>> + cpu_reset(env); >>>>> + >>>>> + env->halted = 0; >>>>> + env->pc = leon3_state.entry; >>>>> + env->npc = leon3_state.entry + 4; >>>>> + >>>>> + /* Initialize cache control */ >>>>> + leon3_state.cache_control = 0x0; >>>>> + >>>>> + /* Configuration registers are read and only always keep those >>>>> predefined >>>>> + values */ >>>>> + leon3_state.inst_cache_conf = 0x10220000; >>>>> + leon3_state.data_cache_conf = 0x18220000; >>>>> +} >>>>> + >>>>> +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; >>>>> + int aligned_bios_size; >>>>> + >>>>> + /* Init CPU */ >>>>> + if (!cpu_model) >>>>> + cpu_model = "LEON3"; >>>> >>>> Missing braces. >>> >>> Fixed. >>> >>>> >>>>> + >>>>> + 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); >>>>> + >>>>> + qemu_register_reset(main_cpu_reset, env); >>>>> + >>>>> + /* Allocate IRQ manager */ >>>>> + grlib_irqmp_create(0x80000200, env,&cpu_irqs, MAX_PILS); >>>>> + >>>>> + /* 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) { >>>>> + aligned_bios_size = >>>>> + (bios_size + TARGET_PAGE_SIZE - 1)& TARGET_PAGE_MASK; >>>>> + >>>>> + 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; >>>>> + leon3_state.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 7e0d17c..6020ffd 100644 >>>>> --- a/target-sparc/cpu.h >>>>> +++ b/target-sparc/cpu.h >>>>> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp); >>>>> /* sun4m.c, sun4u.c */ >>>>> void cpu_check_irqs(CPUSPARCState *env); >>>>> >>>>> +/* grlib_irqmp.c */ >>>>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno); >>>>> + >>>>> +/* leon3.c */ >>>>> +void leon3_shutdown(void); >>>>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int >>>>> size); >>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size); >>>>> +void leon3_cache_control_int(void); >>>>> + >>>>> + >>>>> #if defined (TARGET_SPARC64) >>>>> >>>>> static inline int compare_masked(uint64_t x, uint64_t y, uint64_t >>>>> mask) >>>>> diff --git a/target-sparc/helper.c b/target-sparc/helper.c >>>>> index e84c312..3bf990f 100644 >>>>> --- a/target-sparc/helper.c >>>>> +++ b/target-sparc/helper.c >>>>> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = { >>>>> .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, >>>>> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c >>>>> index be3c1e0..85df077 100644 >>>>> --- a/target-sparc/op_helper.c >>>>> +++ b/target-sparc/op_helper.c >>>>> @@ -1609,8 +1609,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]; >>>>> @@ -1838,8 +1843,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; >>>>> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env) >>>>> { >>>>> int cwp, intno = env->exception_index; >>>>> >>>>> +#if !defined(CONFIG_USER_ONLY) >>>>> + /* Leon3 shutdown */ >>>>> + if (intno == 0x80&& env->version == 0xf3000000) { >>>>> + leon3_shutdown(); >>>>> + } >>>> >>>> This looks like a hack. Should a trap instruction initiate a shutdown? >>> >>> Yes, on Leon3 "ta 0x0" initiates a shutdown. >> >> Then this should be handled during translation. A Leon3 specific CPU >> feature should be added and used much like CHECK_IU_FEATURE (in >> translate.c). Then execution speed would not be affected for non-Leon3 >> CPUs. >> > > OK, but I don't see how to request a shutdown during translation.
Just create a helper which calls shutdown, translator should insert a call to that. >>>>> +#endif >>>>> + >>>>> #ifdef DEBUG_PCALL >>>>> if (qemu_loglevel_mask(CPU_LOG_INT)) { >>>>> static int count; >>>>> @@ -4135,6 +4153,14 @@ 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 for Leon3 */ >>>>> + if (env->version == 0xf3000000&& (intno& ~15) == TT_EXTINT) >>>>> { >>>>> + grlib_irqmp_ack (env, intno); >>>>> + leon3_cache_control_int(); >>>>> + } >>>> >>>> Like this. I don't think a CPU should immediately ack any incoming >>>> interrupts. >>> >>> Leon3 does... >> >> Strange. Then this should be handled at board level (leon3.c). > > Well, it's a CPU feature not a board feature. Maybe, but we don't want to clutter interrupt handling with this.