SPARC: Emulation of Leon3

Leon3 is an open-source VHDL System-On-Chip, well known in space industry (more
information on http://www.gaisler.com).

Leon3 is made of multiple components available in the GrLib VHDL library.
Three devices are implemented: uart, timers and IRQ manager.
You can find code for these peripherals in the grlib_* files.

Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
Fabien Chouteau 2011-01-24 12:56:55 +01:00 committed by Blue Swirl
parent 8b1e132074
commit b04d989054
8 changed files with 419 additions and 22 deletions

View File

@ -286,7 +286,10 @@ obj-sparc-y += cirrus_vga.o
else
obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
# GRLIB
obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
endif
obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o

218
hw/leon3.c Normal file
View File

@ -0,0 +1,218 @@
/*
* QEMU Leon3 System Emulator
*
* Copyright (c) 2010-2011 AdaCore
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw.h"
#include "qemu-timer.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "boards.h"
#include "loader.h"
#include "elf.h"
#include "trace.h"
#include "grlib.h"
/* Default system clock. */
#define CPU_CLK (40 * 1000 * 1000)
#define PROM_FILENAME "u-boot.bin"
#define MAX_PILS 16
typedef struct ResetData {
CPUState *env;
uint32_t entry; /* save kernel entry in case of reset */
} ResetData;
static void main_cpu_reset(void *opaque)
{
ResetData *s = (ResetData *)opaque;
CPUState *env = s->env;
cpu_reset(env);
env->halted = 0;
env->pc = s->entry;
env->npc = s->entry + 4;
}
static void leon3_irq_ack(void *irq_manager, int intno)
{
grlib_irqmp_ack((DeviceState *)irq_manager, intno);
leon3_cache_control_int();
}
static void leon3_set_pil_in(void *opaque, uint32_t pil_in)
{
CPUState *env = (CPUState *)opaque;
assert(env != NULL);
env->pil_in = pil_in;
if (env->pil_in && (env->interrupt_index == 0 ||
(env->interrupt_index & ~15) == TT_EXTINT)) {
unsigned int i;
for (i = 15; i > 0; i--) {
if (env->pil_in & (1 << i)) {
int old_interrupt = env->interrupt_index;
env->interrupt_index = TT_EXTINT | i;
if (old_interrupt != env->interrupt_index) {
trace_leon3_set_irq(i);
cpu_interrupt(env, CPU_INTERRUPT_HARD);
}
break;
}
}
} else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) {
trace_leon3_reset_irq(env->interrupt_index & 15);
env->interrupt_index = 0;
cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
}
}
static void leon3_generic_hw_init(ram_addr_t ram_size,
const char *boot_device,
const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
const char *cpu_model)
{
CPUState *env;
ram_addr_t ram_offset, prom_offset;
int ret;
char *filename;
qemu_irq *cpu_irqs = NULL;
int bios_size;
int prom_size;
ResetData *reset_info;
/* Init CPU */
if (!cpu_model) {
cpu_model = "LEON3";
}
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
exit(1);
}
cpu_sparc_set_id(env, 0);
/* Reset data */
reset_info = qemu_mallocz(sizeof(ResetData));
reset_info->env = env;
qemu_register_reset(main_cpu_reset, reset_info);
/* Allocate IRQ manager */
grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS, &leon3_set_pil_in);
env->qemu_irq_ack = leon3_irq_ack;
/* Allocate RAM */
if ((uint64_t)ram_size > (1UL << 30)) {
fprintf(stderr,
"qemu: Too much memory for this machine: %d, maximum 1G\n",
(unsigned int)(ram_size / (1024 * 1024)));
exit(1);
}
ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM);
/* Allocate BIOS */
prom_size = 8 * 1024 * 1024; /* 8Mb */
prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
cpu_register_physical_memory(0x00000000, prom_size,
prom_offset | IO_MEM_ROM);
/* Load boot prom */
if (bios_name == NULL) {
bios_name = PROM_FILENAME;
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
bios_size = get_image_size(filename);
if (bios_size > prom_size) {
fprintf(stderr, "qemu: could not load prom '%s': file too big\n",
filename);
exit(1);
}
if (bios_size > 0) {
ret = load_image_targphys(filename, 0x00000000, bios_size);
if (ret < 0 || ret > prom_size) {
fprintf(stderr, "qemu: could not load prom '%s'\n", filename);
exit(1);
}
} else if (kernel_filename == NULL) {
fprintf(stderr, "Can't read bios image %s\n", filename);
exit(1);
}
/* Can directly load an application. */
if (kernel_filename != NULL) {
long kernel_size;
uint64_t entry;
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
1 /* big endian */, ELF_MACHINE, 0);
if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
kernel_filename);
exit(1);
}
if (bios_size <= 0) {
/* If there is no bios/monitor, start the application. */
env->pc = entry;
env->npc = entry + 4;
reset_info->entry = entry;
}
}
/* Allocate timers */
grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
/* Allocate uart */
if (serial_hds[0]) {
grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
}
}
QEMUMachine leon3_generic_machine = {
.name = "leon3_generic",
.desc = "Leon-3 generic",
.init = leon3_generic_hw_init,
.use_scsi = 0,
};
static void leon3_machine_init(void)
{
qemu_register_machine(&leon3_generic_machine);
}
machine_init(leon3_machine_init);

View File

@ -266,6 +266,7 @@ typedef struct sparc_def_t {
#define CPU_FEATURE_HYPV (1 << 11)
#define CPU_FEATURE_CMT (1 << 12)
#define CPU_FEATURE_GL (1 << 13)
#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */
#ifndef TARGET_SPARC64
#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \
CPU_FEATURE_MUL | CPU_FEATURE_DIV | \
@ -437,6 +438,12 @@ typedef struct CPUSPARCState {
#define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER)
#endif
sparc_def_t *def;
void *irq_manager;
void (*qemu_irq_ack) (void *irq_manager, int intno);
/* Leon3 cache control */
uint32_t cache_control;
} CPUSPARCState;
#ifndef NO_CPU_IO_DEFS
@ -469,6 +476,8 @@ int cpu_cwp_inc(CPUState *env1, int cwp);
int cpu_cwp_dec(CPUState *env1, int cwp);
void cpu_set_cwp(CPUState *env1, int new_cwp);
void leon3_cache_control_int(void);
/* sun4m.c, sun4u.c */
void cpu_check_irqs(CPUSPARCState *env);

View File

@ -770,6 +770,7 @@ void cpu_reset(CPUSPARCState *env)
env->pc = 0;
env->npc = env->pc + 4;
#endif
env->cache_control = 0;
}
static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model)
@ -1274,20 +1275,20 @@ static const sparc_def_t sparc_defs[] = {
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN,
},
{
.name = "LEON3",
.iu_version = 0xf3000000,
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0xf3000000,
.mmu_bm = 0x00004000,
.mmu_bm = 0x00000000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN,
},
#endif
};

View File

@ -85,6 +85,7 @@ DEF_HELPER_0(fcmpeq_fcc2, void)
DEF_HELPER_0(fcmpeq_fcc3, void)
#endif
DEF_HELPER_1(raise_exception, void, int)
DEF_HELPER_0(shutdown, void)
#define F_HELPER_0_0(name) DEF_HELPER_0(f ## name, void)
#define F_HELPER_DQ_0_0(name) \
F_HELPER_0_0(name ## d); \

View File

@ -1,6 +1,7 @@
#include "exec.h"
#include "host-utils.h"
#include "helper.h"
#include "sysemu.h"
//#define DEBUG_MMU
//#define DEBUG_MXCC
@ -9,6 +10,7 @@
//#define DEBUG_ASI
//#define DEBUG_PCALL
//#define DEBUG_PSTATE
//#define DEBUG_CACHE_CONTROL
#ifdef DEBUG_MMU
#define DPRINTF_MMU(fmt, ...) \
@ -36,6 +38,13 @@
#define DPRINTF_PSTATE(fmt, ...) do {} while (0)
#endif
#ifdef DEBUG_CACHE_CONTROL
#define DPRINTF_CACHE_CONTROL(fmt, ...) \
do { printf("CACHE_CONTROL: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF_CACHE_CONTROL(fmt, ...) do {} while (0)
#endif
#ifdef TARGET_SPARC64
#ifndef TARGET_ABI32
#define AM_CHECK(env1) ((env1)->pstate & PS_AM)
@ -49,6 +58,27 @@
#define QT0 (env->qt0)
#define QT1 (env->qt1)
/* Leon3 cache control */
/* Cache control: emulate the behavior of cache control registers but without
any effect on the emulated */
#define CACHE_STATE_MASK 0x3
#define CACHE_DISABLED 0x0
#define CACHE_FROZEN 0x1
#define CACHE_ENABLED 0x3
/* Cache Control register fields */
#define CACHE_CTRL_IF (1 << 4) /* Instruction Cache Freeze on Interrupt */
#define CACHE_CTRL_DF (1 << 5) /* Data Cache Freeze on Interrupt */
#define CACHE_CTRL_DP (1 << 14) /* Data cache flush pending */
#define CACHE_CTRL_IP (1 << 15) /* Instruction cache flush pending */
#define CACHE_CTRL_IB (1 << 16) /* Instruction burst fetch */
#define CACHE_CTRL_FI (1 << 21) /* Flush Instruction cache (Write only) */
#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */
#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */
#if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64)
static void do_unassigned_access(target_ulong addr, int is_write, int is_exec,
int is_asi, int size);
@ -294,6 +324,13 @@ void HELPER(raise_exception)(int tt)
raise_exception(tt);
}
void helper_shutdown(void)
{
#if !defined(CONFIG_USER_ONLY)
qemu_system_shutdown_request();
#endif
}
void helper_check_align(target_ulong addr, uint32_t align)
{
if (addr & align) {
@ -1612,6 +1649,103 @@ static void dump_asi(const char *txt, target_ulong addr, int asi, int size,
#ifndef TARGET_SPARC64
#ifndef CONFIG_USER_ONLY
/* Leon3 cache control */
void leon3_cache_control_int(void)
{
uint32_t state = 0;
if (env->cache_control & CACHE_CTRL_IF) {
/* Instruction cache state */
state = env->cache_control & CACHE_STATE_MASK;
if (state == CACHE_ENABLED) {
state = CACHE_FROZEN;
DPRINTF_CACHE_CONTROL("Instruction cache: freeze\n");
}
env->cache_control &= ~CACHE_STATE_MASK;
env->cache_control |= state;
}
if (env->cache_control & CACHE_CTRL_DF) {
/* Data cache state */
state = (env->cache_control >> 2) & CACHE_STATE_MASK;
if (state == CACHE_ENABLED) {
state = CACHE_FROZEN;
DPRINTF_CACHE_CONTROL("Data cache: freeze\n");
}
env->cache_control &= ~(CACHE_STATE_MASK << 2);
env->cache_control |= (state << 2);
}
}
static void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
{
DPRINTF_CACHE_CONTROL("st addr:%08x, val:%" PRIx64 ", size:%d\n",
addr, val, size);
if (size != 4) {
DPRINTF_CACHE_CONTROL("32bits only\n");
return;
}
switch (addr) {
case 0x00: /* Cache control */
/* These values must always be read as zeros */
val &= ~CACHE_CTRL_FD;
val &= ~CACHE_CTRL_FI;
val &= ~CACHE_CTRL_IB;
val &= ~CACHE_CTRL_IP;
val &= ~CACHE_CTRL_DP;
env->cache_control = val;
break;
case 0x04: /* Instruction cache configuration */
case 0x08: /* Data cache configuration */
/* Read Only */
break;
default:
DPRINTF_CACHE_CONTROL("write unknown register %08x\n", addr);
break;
};
}
static uint64_t leon3_cache_control_ld(target_ulong addr, int size)
{
uint64_t ret = 0;
if (size != 4) {
DPRINTF_CACHE_CONTROL("32bits only\n");
return 0;
}
switch (addr) {
case 0x00: /* Cache control */
ret = env->cache_control;
break;
/* Configuration registers are read and only always keep those
predefined values */
case 0x04: /* Instruction cache configuration */
ret = 0x10220000;
break;
case 0x08: /* Data cache configuration */
ret = 0x18220000;
break;
default:
DPRINTF_CACHE_CONTROL("read unknown register %08x\n", addr);
break;
};
DPRINTF_CACHE_CONTROL("st addr:%08x, ret:%" PRIx64 ", size:%d\n",
addr, ret, size);
return ret;
}
uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
{
uint64_t ret = 0;
@ -1621,8 +1755,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
helper_check_align(addr, size - 1);
switch (asi) {
case 2: /* SuperSparc MXCC registers */
case 2: /* SuperSparc MXCC registers and Leon3 cache control */
switch (addr) {
case 0x00: /* Leon3 Cache Control */
case 0x08: /* Leon3 Instruction Cache config */
case 0x0C: /* Leon3 Date Cache config */
ret = leon3_cache_control_ld(addr, size);
break;
case 0x01c00a00: /* MXCC control register */
if (size == 8)
ret = env->mxccregs[3];
@ -1850,8 +1989,14 @@ void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size)
{
helper_check_align(addr, size - 1);
switch(asi) {
case 2: /* SuperSparc MXCC registers */
case 2: /* SuperSparc MXCC registers and Leon3 cache control */
switch (addr) {
case 0x00: /* Leon3 Cache Control */
case 0x08: /* Leon3 Instruction Cache config */
case 0x0C: /* Leon3 Date Cache config */
leon3_cache_control_st(addr, val, size);
break;
case 0x01c00000: /* MXCC stream data register 0 */
if (size == 8)
env->mxccdata[0] = val;
@ -4177,6 +4322,13 @@ void do_interrupt(CPUState *env)
env->pc = env->tbr;
env->npc = env->pc + 4;
env->exception_index = -1;
#if !defined(CONFIG_USER_ONLY)
/* IRQ acknowledgment */
if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) {
env->qemu_irq_ack(env->irq_manager, intno);
}
#endif
}
#endif

View File

@ -1997,8 +1997,9 @@ static void disas_sparc_insn(DisasContext * dc)
} else
tcg_gen_mov_tl(cpu_dst, cpu_src1);
}
cond = GET_FIELD(insn, 3, 6);
if (cond == 0x8) {
if (cond == 0x8) { /* Trap Always */
save_state(dc, cpu_cond);
if ((dc->def->features & CPU_FEATURE_HYPV) &&
supervisor(dc))
@ -2007,7 +2008,15 @@ static void disas_sparc_insn(DisasContext * dc)
tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK);
tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP);
tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_dst);
if (rs2 == 0 &&
dc->def->features & CPU_FEATURE_TA0_SHUTDOWN) {
gen_helper_shutdown();
} else {
gen_helper_raise_exception(cpu_tmp32);
}
} else if (cond != 0) {
TCGv r_cond = tcg_temp_new();
int l1;

View File

@ -244,3 +244,7 @@ disable grlib_irqmp_unknown_register(const char *op, uint64_t val) "%s unknown r
# hw/grlib_apbuart.c
disable grlib_apbuart_event(int event) "event:%d"
disable grlib_apbuart_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64""
# hw/leon3.c
disable leon3_set_irq(int intno) "Set CPU IRQ %d"
disable leon3_reset_irq(int intno) "Reset CPU IRQ %d"