diff mbox

[5/6,RFC] Emulation of Leon3.

Message ID 7c24465a442966eba3ae4e2bb03da4463e0f8644.1291397462.git.chouteau@adacore.com
State New
Headers show

Commit Message

Fabien Chouteau Dec. 6, 2010, 9:26 a.m. UTC
Signed-off-by: Fabien Chouteau <chouteau@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(-)

Comments

Blue Swirl Dec. 6, 2010, 5:53 p.m. UTC | #1
On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>
> Signed-off-by: Fabien Chouteau <chouteau@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.

> +
> +/* 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.

> +        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.

> +
> +    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.

> +
> +    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.

> +
> +    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?

> +#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.
Fabien Chouteau Dec. 7, 2010, 11:40 a.m. UTC | #2
On 12/06/2010 06:53 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@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?

>> +
>> +/* 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.

>> +#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...
Blue Swirl Dec. 11, 2010, 9:56 a.m. UTC | #3
On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>
>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> Signed-off-by: Fabien Chouteau<chouteau@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.

>>> +#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).
Andreas Färber Dec. 12, 2010, 2:41 p.m. UTC | #4
Am 06.12.2010 um 10:26 schrieb Fabien Chouteau:

> 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

Aren't these three candidates for Makefile.hw if, as I understood it,  
they are from some non-sparc-specific component library?

Andreas
Fabien Chouteau Dec. 13, 2010, 3:51 p.m. UTC | #5
On 12/11/2010 10:56 AM, Blue Swirl wrote:
> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>
>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> Signed-off-by: Fabien Chouteau<chouteau@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.

>>>> +#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.
Fabien Chouteau Dec. 13, 2010, 5 p.m. UTC | #6
On 12/12/2010 03:41 PM, Andreas Färber wrote:
> Am 06.12.2010 um 10:26 schrieb Fabien Chouteau:
>
>> 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
>
> Aren't these three candidates for Makefile.hw if, as I understood it, 
> they are from some non-sparc-specific component library?

They are sparc specific, but non-leon3-specific.
Blue Swirl Dec. 13, 2010, 6:18 p.m. UTC | #7
On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>
>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>
>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>  wrote:
>>>>>
>>>>> Signed-off-by: Fabien Chouteau<chouteau@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.
Fabien Chouteau Dec. 15, 2010, 5:47 p.m. UTC | #8
On 12/13/2010 07:18 PM, Blue Swirl wrote:
> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>
>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>
>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>   wrote:
>>>>>>
>>>>>> Signed-off-by: Fabien Chouteau<chouteau@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.

I think I understand what you mean, but I don't see why this would be 
faster than my solution.

>>>>>> +#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.

I don't see what you expect here... How can I get the acknowledgment 
information without changing the do_interrupt function?
Blue Swirl Dec. 17, 2010, 7:14 p.m. UTC | #9
On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>
>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>
>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>  wrote:
>>>>>
>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>
>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>  wrote:
>>>>>>>
>>>>>>> Signed-off-by: Fabien Chouteau<chouteau@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.
>
> I think I understand what you mean, but I don't see why this would be faster
> than my solution.

Shutdown is not performance critical, but interrupt handling is.

>>>>>>> +#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.
>
> I don't see what you expect here... How can I get the acknowledgment
> information without changing the do_interrupt function?

Board can acknowledge the interrupt just before calling cpu_interrupt().
Edgar E. Iglesias Dec. 20, 2010, 6:46 a.m. UTC | #10
On Fri, Dec 17, 2010 at 07:14:20PM +0000, Blue Swirl wrote:
> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> > On 12/13/2010 07:18 PM, Blue Swirl wrote:
> >>
> >> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
> >>  wrote:
> >>>
> >>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
> >>>>
> >>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
> >>>>  wrote:
> >>>>>
> >>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
> >>>>>>
> >>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
> >>>>>>  wrote:
> >>>>>>>
> >>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
> >>>>>>> ---

...

> >>>>>>>  #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.
> >
> > I don't see what you expect here... How can I get the acknowledgment
> > information without changing the do_interrupt function?
> 
> Board can acknowledge the interrupt just before calling cpu_interrupt().

Hi,

I don't think that will work properly. IIUC, the leon acks the irq when it
is actually taken by the CPU. Due to CPU internal masking, that may be at
a later point than when the irq is signalled to the CPU.

IMO, what is needed is something along the lines of what Fabien coded but
with some level of indirection so that the CPU doesn't call directly into
the irqmp model. Maybe through ack function pointers or through a
qemu_irq ack line or some other way. The board should then setup the
connection between the ack mechanism and the irqmp model.

Cheers
Fabien Chouteau Dec. 20, 2010, 9:25 a.m. UTC | #11
On 12/17/2010 08:14 PM, Blue Swirl wrote:
> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>
>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>
>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>   wrote:
>>>>>>
>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>
>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>>   wrote:
>>>>>>>
>>>>>>>> +#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.
>>
>> I think I understand what you mean, but I don't see why this would be faster
>> than my solution.
>
> Shutdown is not performance critical, but interrupt handling is.
>

I understand that, but why is it faster to do it during translation?

I don't see how one "if" statement in "do_interrupt" will even slightly 
impact
the performances, and why it will be slower than the same "if' statement 
in the translation.

BTW, I didn't use the "unlikely" function, but this will even more 
reduce the impact...
Fabien Chouteau Dec. 20, 2010, 9:40 a.m. UTC | #12
On 12/20/2010 07:46 AM, Edgar E. Iglesias wrote:
> On Fri, Dec 17, 2010 at 07:14:20PM +0000, Blue Swirl wrote:
>> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>>
>>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>>   wrote:
>>>>>
>>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>>
>>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>   wrote:
>>>>>>>
>>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>>
>>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>>>   wrote:
>>>>>>>>>
>>>>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>>>>>> ---
>
> ...
>
>>>>>>>>>   #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.
>>>
>>> I don't see what you expect here... How can I get the acknowledgment
>>> information without changing the do_interrupt function?
>>
>> Board can acknowledge the interrupt just before calling cpu_interrupt().
>
> Hi,
>
> I don't think that will work properly. IIUC, the leon acks the irq when it
> is actually taken by the CPU. Due to CPU internal masking, that may be at
> a later point than when the irq is signalled to the CPU.

Exactly I've forget to mention that. Raising the interrupt do not mean 
that the
CPU will handle it directly, for example if traps are disabled or if the CPU
handles an higher priority interrupt at the moment.

>
> IMO, what is needed is something along the lines of what Fabien coded but
> with some level of indirection so that the CPU doesn't call directly into
> the irqmp model. Maybe through ack function pointers or through a
> qemu_irq ack line or some other way. The board should then setup the
> connection between the ack mechanism and the irqmp model.
>

The function pointer is a good idea, something like:

if (env->qemu_irq_ack != NULL) {
     env->qemu_irq_ack(intno);
}

And actually this will help me to implement others machines (erc32 and 
leon2).

Are you OK with that?
Blue Swirl Dec. 20, 2010, 7:27 p.m. UTC | #13
On Mon, Dec 20, 2010 at 9:25 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/17/2010 08:14 PM, Blue Swirl wrote:
>>
>> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>>
>>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>>  wrote:
>>>>>
>>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>>
>>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>  wrote:
>>>>>>>
>>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>>
>>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien
>>>>>>>> Chouteau<chouteau@adacore.com>
>>>>>>>>  wrote:
>>>>>>>>
>>>>>>>>> +#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.
>>>
>>> I think I understand what you mean, but I don't see why this would be
>>> faster
>>> than my solution.
>>
>> Shutdown is not performance critical, but interrupt handling is.
>>
>
> I understand that, but why is it faster to do it during translation?
>
> I don't see how one "if" statement in "do_interrupt" will even slightly
> impact
> the performances, and why it will be slower than the same "if' statement in
> the translation.

Because the whole point of translation is that the translated block
will be executed many times but the translation only once. Therefore
computing should be performed during translation instead of TB
execution time, if possible.
Blue Swirl Dec. 20, 2010, 8:09 p.m. UTC | #14
On Mon, Dec 20, 2010 at 9:40 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/20/2010 07:46 AM, Edgar E. Iglesias wrote:
>>
>> On Fri, Dec 17, 2010 at 07:14:20PM +0000, Blue Swirl wrote:
>>>
>>> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>
>>>  wrote:
>>>>
>>>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>>>
>>>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>>>  wrote:
>>>>>>
>>>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>>>
>>>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien
>>>>>>> Chouteau<chouteau@adacore.com>
>>>>>>>  wrote:
>>>>>>>>
>>>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>>>
>>>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien
>>>>>>>>> Chouteau<chouteau@adacore.com>
>>>>>>>>>  wrote:
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>>>>>>> ---
>>
>> ...
>>
>>>>>>>>>>  #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.
>>>>
>>>> I don't see what you expect here... How can I get the acknowledgment
>>>> information without changing the do_interrupt function?
>>>
>>> Board can acknowledge the interrupt just before calling cpu_interrupt().
>>
>> Hi,
>>
>> I don't think that will work properly. IIUC, the leon acks the irq when it
>> is actually taken by the CPU. Due to CPU internal masking, that may be at
>> a later point than when the irq is signalled to the CPU.
>
> Exactly I've forget to mention that. Raising the interrupt do not mean that
> the
> CPU will handle it directly, for example if traps are disabled or if the CPU
> handles an higher priority interrupt at the moment.

I think there is no way to avoid the performance penalty then.

>> IMO, what is needed is something along the lines of what Fabien coded but
>> with some level of indirection so that the CPU doesn't call directly into
>> the irqmp model. Maybe through ack function pointers or through a
>> qemu_irq ack line or some other way. The board should then setup the
>> connection between the ack mechanism and the irqmp model.
>>
>
> The function pointer is a good idea, something like:
>
> if (env->qemu_irq_ack != NULL) {
>    env->qemu_irq_ack(intno);
> }
>
> And actually this will help me to implement others machines (erc32 and
> leon2).

OK. Alternatively there could be a stub function which is used in
place of NULL, so that the if () can be avoided, but maybe this is
better.

If direct interrupt acks would be interesting also for x86 LAPIC, then
a more generic solution could be to introduce a new
env->interrupt_request flag,  CPU_INTERRUPT_ACK, which could be tested
in common code of cpu-exec.c.
diff mbox

Patch

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;
+
+/* 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;
+        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);
+
+    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;
+
+    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";
+
+    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();
+    }
+#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();
+    }
+#endif
 }
 #endif