ARMv7 support.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3572 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
pbrook 2007-11-11 00:04:49 +00:00
parent ee4e83ed8d
commit 9ee6e8bb85
35 changed files with 11799 additions and 653 deletions

View file

@ -17,6 +17,7 @@
- MIPS mipssim pequdo machine (Thiemo Seufer)
- Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh)
- OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski)
- ARM v6, v7, NEON SIMD and SMP emulation (Paul Brook/CodeSourcery)
version 0.9.0:

View file

@ -493,7 +493,9 @@ ifeq ($(TARGET_BASE_ARCH), arm)
VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
VL_OBJS+= versatile_pci.o sd.o ptimer.o
VL_OBJS+= arm_gic.o realview.o arm_sysctl.o
VL_OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
VL_OBJS+= armv7m.o armv7m_nvic.o stellaris.o i2c.o ssd0303.o pl022.o
VL_OBJS+= ssd0323.o pl061.o
VL_OBJS+= arm-semi.o
VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o

View file

@ -173,6 +173,7 @@ static inline TranslationBlock *tb_find_fast(void)
flags |= (1 << 6);
if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30))
flags |= (1 << 7);
flags |= (env->condexec_bits << 8);
cs_base = 0;
pc = env->regs[15];
#elif defined(TARGET_SPARC)
@ -511,8 +512,18 @@ int cpu_exec(CPUState *env1)
env->exception_index = EXCP_FIQ;
do_interrupt(env);
}
/* ARMv7-M interrupt return works by loading a magic value
into the PC. On real hardware the load causes the
return to occur. The qemu implementation performs the
jump normally, then does the exception return when the
CPU tries to execute code at the magic address.
This will cause the magic PC value to be pushed to
the stack if an interrupt occured at the wrong time.
We avoid this by disabling interrupts when
pc contains a magic address. */
if (interrupt_request & CPU_INTERRUPT_HARD
&& !(env->uncached_cpsr & CPSR_I)) {
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|| !(env->uncached_cpsr & CPSR_I))) {
env->exception_index = EXCP_IRQ;
do_interrupt(env);
}

View file

@ -224,6 +224,11 @@ INLINE float32 float32_chs(float32 a)
return -a;
}
INLINE float32 float32_scalbn(float32 a, int n)
{
return scalbnf(a, n);
}
/*----------------------------------------------------------------------------
| Software IEC/IEEE double-precision conversion routines.
*----------------------------------------------------------------------------*/
@ -311,6 +316,11 @@ INLINE float64 float64_chs(float64 a)
return -a;
}
INLINE float64 float64_scalbn(float64 a, int n)
{
return scalbn(a, n);
}
#ifdef FLOATX80
/*----------------------------------------------------------------------------
@ -391,4 +401,10 @@ INLINE floatx80 floatx80_chs(floatx80 a)
{
return -a;
}
INLINE floatx80 floatx80_scalbn(floatx80 a, int n)
{
return scalbnl(a, n);
}
#endif

View file

@ -5377,3 +5377,78 @@ int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \
COMPARE(32, 0xff)
COMPARE(64, 0x7ff)
/* Multiply A by 2 raised to the power N. */
float32 float32_scalbn( float32 a, int n STATUS_PARAM )
{
flag aSign;
int16 aExp;
bits32 aSig;
aSig = extractFloat32Frac( a );
aExp = extractFloat32Exp( a );
aSign = extractFloat32Sign( a );
if ( aExp == 0xFF ) {
return a;
}
aExp += n;
return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
}
float64 float64_scalbn( float64 a, int n STATUS_PARAM )
{
flag aSign;
int16 aExp;
bits64 aSig;
aSig = extractFloat64Frac( a );
aExp = extractFloat64Exp( a );
aSign = extractFloat64Sign( a );
if ( aExp == 0x7FF ) {
return a;
}
aExp += n;
return roundAndPackFloat64( aSign, aExp, aSig STATUS_VAR );
}
#ifdef FLOATX80
floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
{
flag aSign;
int16 aExp;
bits64 aSig;
aSig = extractFloatx80Frac( a );
aExp = extractFloatx80Exp( a );
aSign = extractFloatx80Sign( a );
if ( aExp == 0x7FF ) {
return a;
}
aExp += n;
return roundAndPackFloatx80( STATUS(floatx80_rounding_precision),
aSign, aExp, aSig, 0 STATUS_VAR );
}
#endif
#ifdef FLOAT128
float128 float128_scalbn( float128 a, int n STATUS_PARAM )
{
flag aSign;
int32 aExp;
bits64 aSig0, aSig1;
aSig1 = extractFloat128Frac1( a );
aSig0 = extractFloat128Frac0( a );
aExp = extractFloat128Exp( a );
aSign = extractFloat128Sign( a );
if ( aExp == 0x7FFF ) {
return a;
}
aExp += n;
return roundAndPackFloat128( aSign, aExp, aSig0, aSig1, 0 STATUS_VAR );
}
#endif

View file

@ -244,6 +244,7 @@ int float32_compare( float32, float32 STATUS_PARAM );
int float32_compare_quiet( float32, float32 STATUS_PARAM );
int float32_is_nan( float32 );
int float32_is_signaling_nan( float32 );
float32 float32_scalbn( float32, int STATUS_PARAM );
INLINE float32 float32_abs(float32 a)
{
@ -295,6 +296,7 @@ int float64_compare( float64, float64 STATUS_PARAM );
int float64_compare_quiet( float64, float64 STATUS_PARAM );
int float64_is_nan( float64 a );
int float64_is_signaling_nan( float64 );
float64 float64_scalbn( float64, int STATUS_PARAM );
INLINE float64 float64_abs(float64 a)
{
@ -339,6 +341,7 @@ int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
int floatx80_is_nan( floatx80 );
int floatx80_is_signaling_nan( floatx80 );
floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM );
INLINE floatx80 floatx80_abs(floatx80 a)
{
@ -387,6 +390,7 @@ int float128_le_quiet( float128, float128 STATUS_PARAM );
int float128_lt_quiet( float128, float128 STATUS_PARAM );
int float128_is_nan( float128 );
int float128_is_signaling_nan( float128 );
float128 float128_scalbn( float128, int STATUS_PARAM );
INLINE float128 float128_abs(float128 a)
{

View file

@ -1,7 +1,7 @@
/*
* ARM kernel loader.
*
* Copyright (c) 2006 CodeSourcery.
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
@ -24,6 +24,22 @@ static uint32_t bootloader[] = {
0 /* Kernel entry point. Set by integratorcp_init. */
};
/* Entry point for secondary CPUs. Enable interrupt controller and
Issue WFI until start address is written to system controller. */
static uint32_t smpboot[] = {
0xe3a00201, /* mov r0, #0x10000000 */
0xe3800601, /* orr r0, r0, #0x001000000 */
0xe3a01001, /* mov r1, #1 */
0xe5801100, /* str r1, [r0, #0x100] */
0xe3a00201, /* mov r0, #0x10000000 */
0xe3800030, /* orr r0, #0x30 */
0xe320f003, /* wfi */
0xe5901000, /* ldr r1, [r0] */
0xe3110003, /* tst r1, #3 */
0x1afffffb, /* bne <wfi> */
0xe12fff11 /* bx r1 */
};
static void main_cpu_reset(void *opaque)
{
CPUState *env = opaque;
@ -33,6 +49,8 @@ static void main_cpu_reset(void *opaque)
arm_load_kernel(env, env->ram_size, env->kernel_filename,
env->kernel_cmdline, env->initrd_filename,
env->board_id, env->loader_start);
/* TODO: Reset secondary CPUs. */
}
static void set_kernel_args(uint32_t ram_size, int initrd_size,
@ -211,6 +229,8 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
bootloader[6] = entry;
for (n = 0; n < sizeof(bootloader) / 4; n++)
stl_raw(phys_ram_base + (n * 4), bootloader[n]);
for (n = 0; n < sizeof(smpboot) / 4; n++)
stl_raw(phys_ram_base + ram_size + (n * 4), smpboot[n]);
if (old_param)
set_kernel_args_old(ram_size, initrd_size,
kernel_cmdline, loader_start);

View file

@ -1,17 +1,15 @@
/*
* ARM AMBA Generic/Distributed Interrupt Controller
* ARM Generic/Distributed Interrupt Controller
*
* Copyright (c) 2006 CodeSourcery.
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
/* TODO: Some variants of this controller can handle multiple CPUs.
Currently only single CPU operation is implemented. */
#include "vl.h"
#include "arm_pic.h"
/* This file contains implementation code for the RealView EB interrupt
controller, MPCore distributed interrupt controller and ARMv7-M
Nested Vectored Interrupt Controller. */
//#define DEBUG_GIC
@ -22,58 +20,84 @@ do { printf("arm_gic: " fmt , ##args); } while (0)
#define DPRINTF(fmt, args...) do {} while(0)
#endif
/* Distributed interrupt controller. */
#ifdef NVIC
static const uint8_t gic_id[] =
{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
#define GIC_DIST_OFFSET 0
/* The NVIC has 16 internal vectors. However these are not exposed
through the normal GIC interface. */
#define GIC_BASE_IRQ 32
#else
static const uint8_t gic_id[] =
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
#define GIC_NIRQ 96
#define GIC_DIST_OFFSET 0x1000
#define GIC_BASE_IRQ 0
#endif
typedef struct gic_irq_state
{
/* ??? The documentation seems to imply the enable bits are global, even
for per-cpu interrupts. This seems strange. */
unsigned enabled:1;
unsigned pending:1;
unsigned active:1;
unsigned pending:NCPU;
unsigned active:NCPU;
unsigned level:1;
unsigned model:1; /* 0 = 1:N, 1 = N:N */
unsigned model:1; /* 0 = N:N, 1 = 1:N */
unsigned trigger:1; /* nonzero = edge triggered. */
} gic_irq_state;
#define ALL_CPU_MASK ((1 << NCPU) - 1)
#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
#define GIC_TEST_LEVEL(irq, cm) (s->irq_state[irq].level & (cm)) != 0
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
#define GIC_GET_PRIORITY(irq, cpu) \
(((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
#ifdef NVIC
#define GIC_TARGET(irq) 1
#else
#define GIC_TARGET(irq) s->irq_target[irq]
#endif
typedef struct gic_state
{
uint32_t base;
qemu_irq parent_irq;
qemu_irq parent_irq[NCPU];
int enabled;
int cpu_enabled;
int cpu_enabled[NCPU];
gic_irq_state irq_state[GIC_NIRQ];
#ifndef NVIC
int irq_target[GIC_NIRQ];
int priority[GIC_NIRQ];
int last_active[GIC_NIRQ];
#endif
int priority1[32][NCPU];
int priority2[GIC_NIRQ - 32];
int last_active[GIC_NIRQ][NCPU];
int priority_mask;
int running_irq;
int running_priority;
int current_pending;
int priority_mask[NCPU];
int running_irq[NCPU];
int running_priority[NCPU];
int current_pending[NCPU];
qemu_irq *in;
#ifdef NVIC
void *nvic;
#endif
} gic_state;
/* TODO: Many places that call this routine could be optimized. */
@ -83,112 +107,136 @@ static void gic_update(gic_state *s)
int best_irq;
int best_prio;
int irq;
int level;
int cpu;
int cm;
s->current_pending = 1023;
if (!s->enabled || !s->cpu_enabled) {
qemu_irq_lower(s->parent_irq);
return;
}
best_prio = 0x100;
best_irq = 1023;
for (irq = 0; irq < 96; irq++) {
if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) {
if (s->priority[irq] < best_prio) {
best_prio = s->priority[irq];
best_irq = irq;
for (cpu = 0; cpu < NCPU; cpu++) {
cm = 1 << cpu;
s->current_pending[cpu] = 1023;
if (!s->enabled || !s->cpu_enabled[cpu]) {
qemu_irq_lower(s->parent_irq[cpu]);
return;
}
best_prio = 0x100;
best_irq = 1023;
for (irq = 0; irq < GIC_NIRQ; irq++) {
if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
best_prio = GIC_GET_PRIORITY(irq, cpu);
best_irq = irq;
}
}
}
}
if (best_prio > s->priority_mask) {
qemu_irq_lower(s->parent_irq);
} else {
s->current_pending = best_irq;
if (best_prio < s->running_priority) {
DPRINTF("Raised pending IRQ %d\n", best_irq);
qemu_irq_raise(s->parent_irq);
level = 0;
if (best_prio <= s->priority_mask[cpu]) {
s->current_pending[cpu] = best_irq;
if (best_prio < s->running_priority[cpu]) {
DPRINTF("Raised pending IRQ %d\n", best_irq);
level = 1;
}
}
qemu_set_irq(s->parent_irq[cpu], level);
}
}
static void __attribute__((unused))
gic_set_pending_private(gic_state *s, int cpu, int irq)
{
int cm = 1 << cpu;
if (GIC_TEST_PENDING(irq, cm))
return;
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
GIC_SET_PENDING(irq, cm);
gic_update(s);
}
/* Process a change in an external IRQ input. */
static void gic_set_irq(void *opaque, int irq, int level)
{
gic_state *s = (gic_state *)opaque;
/* The first external input line is internal interrupt 32. */
irq += 32;
if (level == GIC_TEST_LEVEL(irq))
if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
return;
if (level) {
GIC_SET_LEVEL(irq);
GIC_SET_LEVEL(irq, ALL_CPU_MASK);
if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
DPRINTF("Set %d pending\n", irq);
GIC_SET_PENDING(irq);
DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
GIC_SET_PENDING(irq, GIC_TARGET(irq));
}
} else {
GIC_CLEAR_LEVEL(irq);
GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
}
gic_update(s);
}
static void gic_set_running_irq(gic_state *s, int irq)
static void gic_set_running_irq(gic_state *s, int cpu, int irq)
{
s->running_irq = irq;
if (irq == 1023)
s->running_priority = 0x100;
else
s->running_priority = s->priority[irq];
s->running_irq[cpu] = irq;
if (irq == 1023) {
s->running_priority[cpu] = 0x100;
} else {
s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
}
gic_update(s);
}
static uint32_t gic_acknowledge_irq(gic_state *s)
static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
{
int new_irq;
new_irq = s->current_pending;
if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) {
int cm = 1 << cpu;
new_irq = s->current_pending[cpu];
if (new_irq == 1023
|| GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
DPRINTF("ACK no pending IRQ\n");
return 1023;
}
qemu_irq_lower(s->parent_irq);
s->last_active[new_irq] = s->running_irq;
/* For level triggered interrupts we clear the pending bit while
the interrupt is active. */
GIC_CLEAR_PENDING(new_irq);
gic_set_running_irq(s, new_irq);
s->last_active[new_irq][cpu] = s->running_irq[cpu];
/* Clear pending flags for both level and edge triggered interrupts.
Level triggered IRQs will be reasserted once they become inactive. */
GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
gic_set_running_irq(s, cpu, new_irq);
DPRINTF("ACK %d\n", new_irq);
return new_irq;
}
static void gic_complete_irq(gic_state * s, int irq)
static void gic_complete_irq(gic_state * s, int cpu, int irq)
{
int update = 0;
int cm = 1 << cpu;
DPRINTF("EOI %d\n", irq);
if (s->running_irq == 1023)
if (s->running_irq[cpu] == 1023)
return; /* No active IRQ. */
if (irq != 1023) {
/* Mark level triggered interrupts as pending if they are still
raised. */
if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
&& GIC_TEST_LEVEL(irq)) {
GIC_SET_PENDING(irq);
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
DPRINTF("Set %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
update = 1;
}
}
if (irq != s->running_irq) {
if (irq != s->running_irq[cpu]) {
/* Complete an IRQ that is not currently running. */
int tmp = s->running_irq;
while (s->last_active[tmp] != 1023) {
if (s->last_active[tmp] == irq) {
s->last_active[tmp] = s->last_active[irq];
int tmp = s->running_irq[cpu];
while (s->last_active[tmp][cpu] != 1023) {
if (s->last_active[tmp][cpu] == irq) {
s->last_active[tmp][cpu] = s->last_active[irq][cpu];
break;
}
tmp = s->last_active[tmp];
tmp = s->last_active[tmp][cpu];
}
if (update) {
gic_update(s);
}
} else {
/* Complete the current running IRQ. */
gic_set_running_irq(s, s->last_active[s->running_irq]);
gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
}
}
@ -198,15 +246,22 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
uint32_t res;
int irq;
int i;
int cpu;
int cm;
int mask;
offset -= s->base + 0x1000;
cpu = gic_get_current_cpu();
cm = 1 << cpu;
offset -= s->base + GIC_DIST_OFFSET;
if (offset < 0x100) {
#ifndef NVIC
if (offset == 0)
return s->enabled;
if (offset == 4)
return (GIC_NIRQ / 32) - 1;
return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
if (offset < 0x08)
return 0;
#endif
goto bad_reg;
} else if (offset < 0x200) {
/* Interrupt Set/Clear Enable. */
@ -214,6 +269,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
irq = (offset - 0x100) * 8;
else
irq = (offset - 0x180) * 8;
irq += GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
@ -228,40 +284,48 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
irq = (offset - 0x200) * 8;
else
irq = (offset - 0x280) * 8;
irq += GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
mask = (irq < 32) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
if (GIC_TEST_PENDING(irq + i)) {
if (GIC_TEST_PENDING(irq + i, mask)) {
res |= (1 << i);
}
}
} else if (offset < 0x400) {
/* Interrupt Active. */
irq = (offset - 0x300) * 8;
irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
mask = (irq < 32) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
if (GIC_TEST_ACTIVE(irq + i)) {
if (GIC_TEST_ACTIVE(irq + i, mask)) {
res |= (1 << i);
}
}
} else if (offset < 0x800) {
/* Interrupt Priority. */
irq = offset - 0x400;
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = s->priority[irq];
res = GIC_GET_PRIORITY(irq, cpu);
#ifndef NVIC
} else if (offset < 0xc00) {
/* Interrupt CPU Target. */
irq = offset - 0x800;
irq = (offset - 0x800) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = s->irq_target[irq];
if (irq >= 29 && irq <= 31) {
res = cm;
} else {
res = GIC_TARGET(irq);
}
} else if (offset < 0xf00) {
/* Interrupt Configuration. */
irq = (offset - 0xc00) * 2;
irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
res = 0;
@ -271,6 +335,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
if (GIC_TEST_TRIGGER(irq + i))
res |= (2 << (i * 2));
}
#endif
} else if (offset < 0xfe0) {
goto bad_reg;
} else /* offset >= 0xfe0 */ {
@ -282,7 +347,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
}
return res;
bad_reg:
cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset);
return 0;
}
@ -297,6 +362,13 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
{
uint32_t val;
#ifdef NVIC
gic_state *s = (gic_state *)opaque;
uint32_t addr;
addr = offset - s->base;
if (addr < 0x100 || addr > 0xd00)
return nvic_readl(s->nvic, addr);
#endif
val = gic_dist_readw(opaque, offset);
val |= gic_dist_readw(opaque, offset + 2) << 16;
return val;
@ -308,9 +380,14 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
gic_state *s = (gic_state *)opaque;
int irq;
int i;
int cpu;
offset -= s->base + 0x1000;
cpu = gic_get_current_cpu();
offset -= s->base + GIC_DIST_OFFSET;
if (offset < 0x100) {
#ifdef NVIC
goto bad_reg;
#else
if (offset == 0) {
s->enabled = (value & 1);
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
@ -319,27 +396,36 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
} else {
goto bad_reg;
}
#endif
} else if (offset < 0x180) {
/* Interrupt Set Enable. */
irq = (offset - 0x100) * 8;
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
if (irq < 16)
value = 0xff;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
if (!GIC_TEST_ENABLED(irq + i))
DPRINTF("Enabled IRQ %d\n", irq + i);
GIC_SET_ENABLED(irq + i);
/* If a raised level triggered IRQ enabled then mark
is as pending. */
if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i))
GIC_SET_PENDING(irq + i);
if (GIC_TEST_LEVEL(irq + i, mask)
&& !GIC_TEST_TRIGGER(irq + i)) {
DPRINTF("Set %d pending mask %x\n", irq + i, mask);
GIC_SET_PENDING(irq + i, mask);
}
}
}
} else if (offset < 0x200) {
/* Interrupt Clear Enable. */
irq = (offset - 0x180) * 8;
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
if (irq < 16)
value = 0;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
if (GIC_TEST_ENABLED(irq + i))
@ -349,22 +435,28 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
}
} else if (offset < 0x280) {
/* Interrupt Set Pending. */
irq = (offset - 0x200) * 8;
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
if (irq < 16)
irq = 0;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
GIC_SET_PENDING(irq + i);
GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
}
}
} else if (offset < 0x300) {
/* Interrupt Clear Pending. */
irq = (offset - 0x280) * 8;
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
for (i = 0; i < 8; i++) {
/* ??? This currently clears the pending bit for all CPUs, even
for per-CPU interrupts. It's unclear whether this is the
corect behavior. */
if (value & (1 << i)) {
GIC_CLEAR_PENDING(irq + i);
GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
}
}
} else if (offset < 0x400) {
@ -372,21 +464,32 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
goto bad_reg;
} else if (offset < 0x800) {
/* Interrupt Priority. */
irq = offset - 0x400;
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
s->priority[irq] = value;
if (irq < 32) {
s->priority1[irq][cpu] = value;
} else {
s->priority2[irq - 32] = value;
}
#ifndef NVIC
} else if (offset < 0xc00) {
/* Interrupt CPU Target. */
irq = offset - 0x800;
irq = (offset - 0x800) + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
s->irq_target[irq] = value;
if (irq < 29)
value = 0;
else if (irq < 32)
value = ALL_CPU_MASK;
s->irq_target[irq] = value & ALL_CPU_MASK;
} else if (offset < 0xf00) {
/* Interrupt Configuration. */
irq = (offset - 0xc00) * 4;
irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
if (irq >= GIC_NIRQ)
goto bad_reg;
if (irq < 32)
value |= 0xaa;
for (i = 0; i < 4; i++) {
if (value & (1 << (i * 2))) {
GIC_SET_MODEL(irq + i);
@ -399,25 +502,20 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
GIC_CLEAR_TRIGGER(irq + i);
}
}
#endif
} else {
/* 0xf00 is only handled for word writes. */
/* 0xf00 is only handled for 32-bit writes. */
goto bad_reg;
}
gic_update(s);
return;
bad_reg:
cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset);
}
static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
gic_state *s = (gic_state *)opaque;
if (offset - s->base == 0xf00) {
GIC_SET_PENDING(value & 0x3ff);
gic_update(s);
return;
}
gic_dist_writeb(opaque, offset, value & 0xff);
gic_dist_writeb(opaque, offset + 1, value >> 8);
}
@ -425,6 +523,41 @@ static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
gic_state *s = (gic_state *)opaque;
#ifdef NVIC
uint32_t addr;
addr = offset - s->base;
if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
nvic_writel(s->nvic, addr, value);
return;
}
#endif
if (offset - s->base == GIC_DIST_OFFSET + 0xf00) {
int cpu;
int irq;
int mask;
cpu = gic_get_current_cpu();
irq = value & 0x3ff;
switch ((value >> 24) & 3) {
case 0:
mask = (value >> 16) & ALL_CPU_MASK;
break;
case 1:
mask = 1 << cpu;
break;
case 2:
mask = ALL_CPU_MASK ^ (1 << cpu);
break;
default:
DPRINTF("Bad Soft Int target filter\n");
mask = ALL_CPU_MASK;
break;
}
GIC_SET_PENDING(irq, mask);
gic_update(s);
return;
}
gic_dist_writew(opaque, offset, value & 0xffff);
gic_dist_writew(opaque, offset + 2, value >> 16);
}
@ -441,105 +574,100 @@ static CPUWriteMemoryFunc *gic_dist_writefn[] = {
gic_dist_writel
};
static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset)
#ifndef NVIC
static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
{
gic_state *s = (gic_state *)opaque;
offset -= s->base;
switch (offset) {
case 0x00: /* Control */
return s->cpu_enabled;
return s->cpu_enabled[cpu];
case 0x04: /* Priority mask */
return s->priority_mask;
return s->priority_mask[cpu];
case 0x08: /* Binary Point */
/* ??? Not implemented. */
return 0;
case 0x0c: /* Acknowledge */
return gic_acknowledge_irq(s);
return gic_acknowledge_irq(s, cpu);
case 0x14: /* Runing Priority */
return s->running_priority;
return s->running_priority[cpu];
case 0x18: /* Highest Pending Interrupt */
return s->current_pending;
return s->current_pending[cpu];
default:
cpu_abort (cpu_single_env, "gic_cpu_read: Bad offset %x\n", offset);
cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n",
(int)offset);
return 0;
}
}
static void gic_cpu_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
{
gic_state *s = (gic_state *)opaque;
offset -= s->base;
switch (offset) {
case 0x00: /* Control */
s->cpu_enabled = (value & 1);
s->cpu_enabled[cpu] = (value & 1);
DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
break;
case 0x04: /* Priority mask */
s->priority_mask = (value & 0x3ff);
s->priority_mask[cpu] = (value & 0xff);
break;
case 0x08: /* Binary Point */
/* ??? Not implemented. */
break;
case 0x10: /* End Of Interrupt */
return gic_complete_irq(s, value & 0x3ff);
return gic_complete_irq(s, cpu, value & 0x3ff);
default:
cpu_abort (cpu_single_env, "gic_cpu_write: Bad offset %x\n", offset);
cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n",
(int)offset);
return;
}
gic_update(s);
}
static CPUReadMemoryFunc *gic_cpu_readfn[] = {
gic_cpu_read,
gic_cpu_read,
gic_cpu_read
};
static CPUWriteMemoryFunc *gic_cpu_writefn[] = {
gic_cpu_write,
gic_cpu_write,
gic_cpu_write
};
#endif
static void gic_reset(gic_state *s)
{
int i;
memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
s->priority_mask = 0xf0;
s->current_pending = 1023;
s->running_irq = 1023;
s->running_priority = 0x100;
for (i = 0 ; i < NCPU; i++) {
s->priority_mask[i] = 0xf0;
s->current_pending[i] = 1023;
s->running_irq[i] = 1023;
s->running_priority[i] = 0x100;
#ifdef NVIC
/* The NVIC doesn't have per-cpu interfaces, so enable by default. */
s->cpu_enabled[i] = 1;
#else
s->cpu_enabled[i] = 0;
#endif
}
for (i = 0; i < 15; i++) {
GIC_SET_ENABLED(i);
GIC_SET_TRIGGER(i);
}
#ifdef NVIC
/* The NVIC is always enabled. */
s->enabled = 1;
#else
s->enabled = 0;
s->cpu_enabled = 0;
#endif
}
qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq)
static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
{
gic_state *s;
qemu_irq *qi;
int iomemtype;
int i;
s = (gic_state *)qemu_mallocz(sizeof(gic_state));
if (!s)
return NULL;
qi = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
s->parent_irq = parent_irq;
if (base != 0xffffffff) {
iomemtype = cpu_register_io_memory(0, gic_cpu_readfn,
gic_cpu_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
gic_dist_writefn, s);
cpu_register_physical_memory(base + 0x1000, 0x00001000, iomemtype);
s->base = base;
} else {
s->base = 0;
s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
for (i = 0; i < NCPU; i++) {
s->parent_irq[i] = parent_irq[i];
}
iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
gic_dist_writefn, s);
cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000,
iomemtype);
s->base = base;
gic_reset(s);
return qi;
return s;
}

View file

@ -1,7 +1,7 @@
/*
* Status and system control registers for ARM RealView/Versatile boards.
*
* Copyright (c) 2006 CodeSourcery.
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
@ -200,6 +200,9 @@ void arm_sysctl_init(uint32_t base, uint32_t sys_id)
return;
s->base = base;
s->sys_id = sys_id;
/* The MPcore bootloader uses these flags to start secondary CPUs.
We don't use a bootloader, so do this here. */
s->flags = 3;
iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn,
arm_sysctl_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);

204
hw/armv7m.c Normal file
View file

@ -0,0 +1,204 @@
/*
* ARMV7M System emulation.
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
/* Bitbanded IO. Each word corresponds to a single bit. */
/* Get the byte address of the real memory for a bitband acess. */
static inline uint32_t bitband_addr(uint32_t addr)
{
uint32_t res;
res = addr & 0xe0000000;
res |= (addr & 0x1ffffff) >> 5;
return res;
}
static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
{
uint8_t v;
cpu_physical_memory_read(bitband_addr(offset), &v, 1);
return (v & (1 << ((offset >> 2) & 7))) != 0;
}
static void bitband_writeb(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
uint32_t addr;
uint8_t mask;
uint8_t v;
addr = bitband_addr(offset);
mask = (1 << ((offset >> 2) & 7));
cpu_physical_memory_read(addr, &v, 1);
if (value & 1)
v |= mask;
else
v &= ~mask;
cpu_physical_memory_write(addr, &v, 1);
}
static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
{
uint32_t addr;
uint16_t mask;
uint16_t v;
addr = bitband_addr(offset) & ~1;
mask = (1 << ((offset >> 2) & 15));
mask = tswap16(mask);
cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
return (v & mask) != 0;
}
static void bitband_writew(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
uint32_t addr;
uint16_t mask;
uint16_t v;
addr = bitband_addr(offset) & ~1;
mask = (1 << ((offset >> 2) & 15));
mask = tswap16(mask);
cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
if (value & 1)
v |= mask;
else
v &= ~mask;
cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
}
static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
{
uint32_t addr;
uint32_t mask;
uint32_t v;
addr = bitband_addr(offset) & ~3;
mask = (1 << ((offset >> 2) & 31));
mask = tswap32(mask);
cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
return (v & mask) != 0;
}
static void bitband_writel(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
uint32_t addr;
uint32_t mask;
uint32_t v;
addr = bitband_addr(offset) & ~3;
mask = (1 << ((offset >> 2) & 31));
mask = tswap32(mask);
cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
if (value & 1)
v |= mask;
else
v &= ~mask;
cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
}
static CPUReadMemoryFunc *bitband_readfn[] = {
bitband_readb,
bitband_readw,
bitband_readl
};
static CPUWriteMemoryFunc *bitband_writefn[] = {
bitband_writeb,
bitband_writew,
bitband_writel
};
static void armv7m_bitband_init(void)
{
int iomemtype;
iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn,
NULL);
cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype);
cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype);
}
/* Board init. */
/* Init CPU and memory for a v7-M based board.
flash_size and sram_size are in kb.
Returns the NVIC array. */
qemu_irq *armv7m_init(int flash_size, int sram_size,
const char *kernel_filename, const char *cpu_model)
{
CPUState *env;
qemu_irq *pic;
uint32_t pc;
int image_size;
uint64_t entry;
uint64_t lowaddr;
flash_size *= 1024;
sram_size *= 1024;
if (!cpu_model)
cpu_model = "cortex-m3";
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
#if 0
/* > 32Mb SRAM gets complicated because it overlaps the bitband area.
We don't have proper commandline options, so allocate half of memory
as SRAM, up to a maximum of 32Mb, and the rest as code. */
if (ram_size > (512 + 32) * 1024 * 1024)
ram_size = (512 + 32) * 1024 * 1024;
sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
if (sram_size > 32 * 1024 * 1024)
sram_size = 32 * 1024 * 1024;
code_size = ram_size - sram_size;
#endif
/* Flash programming is done via the SCU, so pretend it is ROM. */
cpu_register_physical_memory(0, flash_size, IO_MEM_ROM);
cpu_register_physical_memory(0x20000000, sram_size,
flash_size + IO_MEM_RAM);
armv7m_bitband_init();
pic = armv7m_nvic_init(env);
image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL);
if (image_size < 0) {
image_size = load_image(kernel_filename, phys_ram_base);
lowaddr = 0;
}
if (image_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
kernel_filename);
exit(1);
}
/* If the image was loaded at address zero then assume it is a
regular ROM image and perform the normal CPU reset sequence.
Otherwise jump directly to the entry point. */
if (lowaddr == 0) {
env->regs[13] = tswap32(*(uint32_t *)phys_ram_base);
pc = tswap32(*(uint32_t *)(phys_ram_base + 4));
} else {
pc = entry;
}
env->thumb = pc & 1;
env->regs[15] = pc & ~1;
/* Hack to map an additional page of ram at the top of the address
space. This stops qemu complaining about executing code outside RAM
when returning from an exception. */
cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size);
return pic;
}

381
hw/armv7m_nvic.c Normal file
View file

@ -0,0 +1,381 @@
/*
* ARM Nested Vectored Interrupt Controller
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*
* The ARMv7M System controller is fairly tightly tied in with the
* NVIC. Much of that is also implemented here.
*/
#include "vl.h"
#include "arm_pic.h"
#define GIC_NIRQ 64
#define NCPU 1
#define NVIC 1
/* Only a single "CPU" interface is present. */
static inline int
gic_get_current_cpu(void)
{
return 0;
}
static uint32_t nvic_readl(void *opaque, uint32_t offset);
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
#include "arm_gic.c"
typedef struct {
struct {
uint32_t control;
uint32_t reload;
int64_t tick;
QEMUTimer *timer;
} systick;
gic_state *gic;
} nvic_state;
/* qemu timers run at 1GHz. We want something closer to 1MHz. */
#define SYSTICK_SCALE 1000ULL
#define SYSTICK_ENABLE (1 << 0)
#define SYSTICK_TICKINT (1 << 1)
#define SYSTICK_CLKSOURCE (1 << 2)
#define SYSTICK_COUNTFLAG (1 << 16)
/* Conversion factor from qemu timer to SysTick frequencies.
QEMU uses a base of 1GHz, so these give 20MHz and 1MHz for core and
reference frequencies. */
static inline int64_t systick_scale(nvic_state *s)
{
if (s->systick.control & SYSTICK_CLKSOURCE)
return 50;
else
return 1000;
}
static void systick_reload(nvic_state *s, int reset)
{
if (reset)
s->systick.tick = qemu_get_clock(vm_clock);
s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
qemu_mod_timer(s->systick.timer, s->systick.tick);
}
static void systick_timer_tick(void * opaque)
{
nvic_state *s = (nvic_state *)opaque;
s->systick.control |= SYSTICK_COUNTFLAG;
if (s->systick.control & SYSTICK_TICKINT) {
/* Trigger the interrupt. */
armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
}
if (s->systick.reload == 0) {
s->systick.control &= ~SYSTICK_ENABLE;
} else {
systick_reload(s, 0);
}
}
/* The external routines use the hardware vector numbering, ie. the first
IRQ is #16. The internal GIC routines use #32 as the first IRQ. */
void armv7m_nvic_set_pending(void *opaque, int irq)
{
nvic_state *s = (nvic_state *)opaque;
if (irq >= 16)
irq += 16;
gic_set_pending_private(s->gic, 0, irq);
}
/* Make pending IRQ active. */
int armv7m_nvic_acknowledge_irq(void *opaque)
{
nvic_state *s = (nvic_state *)opaque;
uint32_t irq;
irq = gic_acknowledge_irq(s->gic, 0);
if (irq == 1023)
cpu_abort(cpu_single_env, "Interrupt but no vector\n");
if (irq >= 32)
irq -= 16;
return irq;
}
void armv7m_nvic_complete_irq(void *opaque, int irq)
{
nvic_state *s = (nvic_state *)opaque;
if (irq >= 16)
irq += 16;
gic_complete_irq(s->gic, 0, irq);
}
static uint32_t nvic_readl(void *opaque, uint32_t offset)
{
nvic_state *s = (nvic_state *)opaque;
uint32_t val;
int irq;
switch (offset) {
case 4: /* Interrupt Control Type. */
return (GIC_NIRQ / 32) - 1;
case 0x10: /* SysTick Control and Status. */
val = s->systick.control;
s->systick.control &= ~SYSTICK_COUNTFLAG;
return val;
case 0x14: /* SysTick Reload Value. */
return s->systick.reload;
case 0x18: /* SysTick Current Value. */
{
int64_t t;
if ((s->systick.control & SYSTICK_ENABLE) == 0)
return 0;
t = qemu_get_clock(vm_clock);
if (t >= s->systick.tick)
return 0;
val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
/* The interrupt in triggered when the timer reaches zero.
However the counter is not reloaded until the next clock
tick. This is a hack to return zero during the first tick. */
if (val > s->systick.reload)
val = 0;
return val;
}
case 0x1c: /* SysTick Calibration Value. */
return 10000;
case 0xd00: /* CPUID Base. */
return cpu_single_env->cp15.c0_cpuid;
case 0xd04: /* Interrypt Control State. */
/* VECTACTIVE */
val = s->gic->running_irq[0];
if (val == 1023) {
val = 0;
} else if (val >= 32) {
val -= 16;
}
/* RETTOBASE */
if (s->gic->running_irq[0] == 1023
|| s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
val |= (1 << 11);
}
/* VECTPENDING */
if (s->gic->current_pending[0] != 1023)
val |= (s->gic->current_pending[0] << 12);
/* ISRPENDING */
for (irq = 32; irq < GIC_NIRQ; irq++) {
if (s->gic->irq_state[irq].pending) {
val |= (1 << 22);
break;
}
}
/* PENDSTSET */
if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
val |= (1 << 26);
/* PENDSVSET */
if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
val |= (1 << 28);
/* NMIPENDSET */
if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
val |= (1 << 31);
return val;
case 0xd08: /* Vector Table Offset. */
return cpu_single_env->v7m.vecbase;
case 0xd0c: /* Application Interrupt/Reset Control. */
return 0xfa05000;
case 0xd10: /* System Control. */
/* TODO: Implement SLEEPONEXIT. */
return 0;
case 0xd14: /* Configuration Control. */
/* TODO: Implement Configuration Control bits. */
return 0;
case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
irq = offset - 0xd14;
val = 0;
val = s->gic->priority1[irq++][0];
val = s->gic->priority1[irq++][0] << 8;
val = s->gic->priority1[irq++][0] << 16;
val = s->gic->priority1[irq][0] << 24;
return val;
case 0xd24: /* System Handler Status. */
val = 0;
if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
return val;
case 0xd28: /* Configurable Fault Status. */
/* TODO: Implement Fault Status. */
cpu_abort(cpu_single_env,
"Not implemented: Configurable Fault Status.");
return 0;
case 0xd2c: /* Hard Fault Status. */
case 0xd30: /* Debug Fault Status. */
case 0xd34: /* Mem Manage Address. */
case 0xd38: /* Bus Fault Address. */
case 0xd3c: /* Aux Fault Status. */
/* TODO: Implement fault status registers. */
goto bad_reg;
case 0xd40: /* PFR0. */
return 0x00000030;
case 0xd44: /* PRF1. */
return 0x00000200;
case 0xd48: /* DFR0. */
return 0x00100000;
case 0xd4c: /* AFR0. */
return 0x00000000;
case 0xd50: /* MMFR0. */
return 0x00000030;
case 0xd54: /* MMFR1. */
return 0x00000000;
case 0xd58: /* MMFR2. */
return 0x00000000;
case 0xd5c: /* MMFR3. */
return 0x00000000;
case 0xd60: /* ISAR0. */
return 0x01141110;
case 0xd64: /* ISAR1. */
return 0x02111000;
case 0xd68: /* ISAR2. */
return 0x21112231;
case 0xd6c: /* ISAR3. */
return 0x01111110;
case 0xd70: /* ISAR4. */
return 0x01310102;
/* TODO: Implement debug registers. */
default:
bad_reg:
cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
}
}
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
{
nvic_state *s = (nvic_state *)opaque;
uint32_t oldval;
switch (offset) {
case 0x10: /* SysTick Control and Status. */
oldval = s->systick.control;
s->systick.control &= 0xfffffff8;
s->systick.control |= value & 7;
if ((oldval ^ value) & SYSTICK_ENABLE) {
int64_t now = qemu_get_clock(vm_clock);
if (value & SYSTICK_ENABLE) {
if (s->systick.tick) {
s->systick.tick += now;
qemu_mod_timer(s->systick.timer, s->systick.tick);
} else {
systick_reload(s, 1);
}
} else {
qemu_del_timer(s->systick.timer);
s->systick.tick -= now;
if (s->systick.tick < 0)
s->systick.tick = 0;
}
} else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
/* This is a hack. Force the timer to be reloaded
when the reference clock is changed. */
systick_reload(s, 1);
}
break;
case 0x14: /* SysTick Reload Value. */
s->systick.reload = value;
break;
case 0x18: /* SysTick Current Value. Writes reload the timer. */
systick_reload(s, 1);
s->systick.control &= ~SYSTICK_COUNTFLAG;
break;
case 0xd04: /* Interrupt Control State. */
if (value & (1 << 31)) {
armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
}
if (value & (1 << 28)) {
armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
} else if (value & (1 << 27)) {
s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
gic_update(s->gic);
}
if (value & (1 << 26)) {
armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
} else if (value & (1 << 25)) {
s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
gic_update(s->gic);
}
break;
case 0xd08: /* Vector Table Offset. */
cpu_single_env->v7m.vecbase = value & 0xffffff80;
break;
case 0xd0c: /* Application Interrupt/Reset Control. */
if ((value >> 16) == 0x05fa) {
if (value & 2) {
cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
}
if (value & 5) {
cpu_abort(cpu_single_env, "System reset");
}
}
break;
case 0xd10: /* System Control. */
case 0xd14: /* Configuration Control. */
/* TODO: Implement control registers. */
goto bad_reg;
case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
{
int irq;
irq = offset - 0xd14;
s->gic->priority1[irq++][0] = value & 0xff;
s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
s->gic->priority1[irq][0] = (value >> 24) & 0xff;
gic_update(s->gic);
}
break;
case 0xd24: /* System Handler Control. */
/* TODO: Real hardware allows you to set/clear the active bits
under some circumstances. We don't implement this. */
s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
break;
case 0xd28: /* Configurable Fault Status. */
case 0xd2c: /* Hard Fault Status. */
case 0xd30: /* Debug Fault Status. */
case 0xd34: /* Mem Manage Address. */
case 0xd38: /* Bus Fault Address. */
case 0xd3c: /* Aux Fault Status. */
goto bad_reg;
default:
bad_reg:
cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
}
}
qemu_irq *armv7m_nvic_init(CPUState *env)
{
nvic_state *s;
qemu_irq *parent;
parent = arm_pic_init_cpu(env);
s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
s->gic->nvic = s;
s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
if (env->v7m.nvic)
cpu_abort(env, "CPU can only have one NVIC\n");
env->v7m.nvic = s;
return s->gic->in;
}

View file

@ -497,8 +497,8 @@ static void integratorcp_init(int ram_size, int vga_ram_size,
icp_pic_init(0xca000000, pic[26], NULL);
icp_pit_init(0x13000000, pic, 5);
pl031_init(0x15000000, pic[8]);
pl011_init(0x16000000, pic[1], serial_hds[0]);
pl011_init(0x17000000, pic[2], serial_hds[1]);
pl011_init(0x16000000, pic[1], serial_hds[0], PL011_ARM);
pl011_init(0x17000000, pic[2], serial_hds[1], PL011_ARM);
icp_control_init(0xcb000000);
pl050_init(0x18000000, pic[3], 0);
pl050_init(0x19000000, pic[4], 1);

323
hw/mpcore.c Normal file
View file

@ -0,0 +1,323 @@
/*
* ARM MPCore internal peripheral emulation.
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
#define MPCORE_PRIV_BASE 0x10100000
#define NCPU 4
/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
(+ 32 internal). However my test chip only exposes/reports 32.
More importantly Linux falls over if more than 32 are present! */
#define GIC_NIRQ 64
static inline int
gic_get_current_cpu(void)
{
return cpu_single_env->cpu_index;
}
#include "arm_gic.c"
/* MPCore private memory region. */
typedef struct {
uint32_t count;
uint32_t load;
uint32_t control;
uint32_t status;
uint32_t old_status;
int64_t tick;
QEMUTimer *timer;
struct mpcore_priv_state *mpcore;
int id; /* Encodes both timer/watchdog and CPU. */
} mpcore_timer_state;
typedef struct mpcore_priv_state {
gic_state *gic;
uint32_t scu_control;
mpcore_timer_state timer[8];
} mpcore_priv_state;
/* Per-CPU Timers. */
static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
{
if (s->status & ~s->old_status) {
gic_set_pending_private(s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
}
s->old_status = s->status;
}
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
{
return (((s->control >> 8) & 0xff) + 1) * 10;
}
static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
{
if (s->count == 0)
return;
if (restart)
s->tick = qemu_get_clock(vm_clock);
s->tick += (int64_t)s->count * mpcore_timer_scale(s);
qemu_mod_timer(s->timer, s->tick);
}
static void mpcore_timer_tick(void *opaque)
{
mpcore_timer_state *s = (mpcore_timer_state *)opaque;
s->status = 1;
if (s->control & 2) {
s->count = s->load;
mpcore_timer_reload(s, 0);
} else {
s->count = 0;
}
mpcore_timer_update_irq(s);
}
static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
{
int64_t val;
switch (offset) {
case 0: /* Load */
return s->load;
/* Fall through. */
case 4: /* Counter. */
if (((s->control & 1) == 0) || (s->count == 0))
return 0;
/* Slow and ugly, but hopefully won't happen too often. */
val = s->tick - qemu_get_clock(vm_clock);
val /= mpcore_timer_scale(s);
if (val < 0)
val = 0;
return val;
case 8: /* Control. */
return s->control;
case 12: /* Interrupt status. */
return s->status;
}
}
static void mpcore_timer_write(mpcore_timer_state *s, int offset,
uint32_t value)
{
int64_t old;
switch (offset) {
case 0: /* Load */
s->load = value;
/* Fall through. */
case 4: /* Counter. */
if ((s->control & 1) && s->count) {
/* Cancel the previous timer. */
qemu_del_timer(s->timer);
}
s->count = value;
if (s->control & 1) {
mpcore_timer_reload(s, 1);
}
break;
case 8: /* Control. */
old = s->control;
s->control = value;
if (((old & 1) == 0) && (value & 1)) {
if (s->count == 0 && (s->control & 2))
s->count = s->load;
mpcore_timer_reload(s, 1);
}
break;
case 12: /* Interrupt status. */
s->status &= ~value;
mpcore_timer_update_irq(s);
break;
}
}
static void mpcore_timer_init(mpcore_priv_state *mpcore,
mpcore_timer_state *s, int id)
{
s->id = id;
s->mpcore = mpcore;
s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
}
/* Per-CPU private memory mapped IO. */
static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
{
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
int id;
offset &= 0xfff;
if (offset < 0x100) {
/* SCU */
switch (offset) {
case 0x00: /* Control. */
return s->scu_control;
case 0x04: /* Configuration. */
return 0xf3;
case 0x08: /* CPU status. */
return 0;
case 0x0c: /* Invalidate all. */
return 0;
default:
goto bad_reg;
}
} else if (offset < 0x600) {
/* Interrupt controller. */
if (offset < 0x200) {
id = gic_get_current_cpu();
} else {
id = (offset - 0x200) >> 8;
}
return gic_cpu_read(s->gic, id, offset & 0xff);
} else if (offset < 0xb00) {
/* Timers. */
if (offset < 0x700) {
id = gic_get_current_cpu();
} else {
id = (offset - 0x700) >> 8;
}
id <<= 1;
if (offset & 0x20)
id++;
return mpcore_timer_read(&s->timer[id], offset & 0xf);
}
bad_reg:
cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
(int)offset);
return 0;
}
static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
int id;
offset &= 0xfff;
if (offset < 0x100) {
/* SCU */
switch (offset) {
case 0: /* Control register. */
s->scu_control = value & 1;
break;
case 0x0c: /* Invalidate all. */
/* This is a no-op as cache is not emulated. */
break;
default:
goto bad_reg;
}
} else if (offset < 0x600) {
/* Interrupt controller. */
if (offset < 0x200) {
id = gic_get_current_cpu();
} else {
id = (offset - 0x200) >> 8;
}
gic_cpu_write(s->gic, id, offset & 0xff, value);
} else if (offset < 0xb00) {
/* Timers. */
if (offset < 0x700) {
id = gic_get_current_cpu();
} else {
id = (offset - 0x700) >> 8;
}
id <<= 1;
if (offset & 0x20)
id++;
mpcore_timer_write(&s->timer[id], offset & 0xf, value);
return;
}
return;
bad_reg:
cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
(int)offset);
}
static CPUReadMemoryFunc *mpcore_priv_readfn[] = {
mpcore_priv_read,
mpcore_priv_read,
mpcore_priv_read
};
static CPUWriteMemoryFunc *mpcore_priv_writefn[] = {
mpcore_priv_write,
mpcore_priv_write,
mpcore_priv_write
};
static qemu_irq *mpcore_priv_init(uint32_t base, qemu_irq *pic_irq)
{
mpcore_priv_state *s;
int iomemtype;
int i;
s = (mpcore_priv_state *)qemu_mallocz(sizeof(mpcore_priv_state));
if (!s)
return NULL;
s->gic = gic_init(base, pic_irq);
if (!s->gic)
return NULL;
iomemtype = cpu_register_io_memory(0, mpcore_priv_readfn,
mpcore_priv_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
for (i = 0; i < 8; i++) {
mpcore_timer_init(s, &s->timer[i], i);
}
return s->gic->in;
}
/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
controllers. The output of these, plus some of the raw input lines
are fed into a single SMP-aware interrupt controller on the CPU. */
typedef struct {
qemu_irq *cpuic;
qemu_irq *rvic[4];
} mpcore_rirq_state;
/* Map baseboard IRQs onto CPU IRQ lines. */
static const int mpcore_irq_map[32] = {
-1, -1, -1, -1, 1, 2, -1, -1,
-1, -1, 6, -1, 4, 5, -1, -1,
-1, 14, 15, 0, 7, 8, -1, -1,
-1, -1, -1, -1, 9, 3, -1, -1,
};
static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
{
mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
int i;
for (i = 0; i < 4; i++) {
qemu_set_irq(s->rvic[i][irq], level);
}
if (irq < 32) {
irq = mpcore_irq_map[irq];
if (irq >= 0) {
qemu_set_irq(s->cpuic[irq], level);
}
}
}
qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq)
{
mpcore_rirq_state *s;
int n;
/* ??? IRQ routing is hardcoded to "normal" mode. */
s = qemu_mallocz(sizeof(mpcore_rirq_state));
s->cpuic = mpcore_priv_init(MPCORE_PRIV_BASE, cpu_irq);
for (n = 0; n < 4; n++) {
s->rvic[n] = realview_gic_init(0x10040000 + n * 0x10000,
s->cpuic[10 + n]);
}
return qemu_allocate_irqs(mpcore_rirq_set_irq, s, 64);
}

View file

@ -28,6 +28,7 @@ typedef struct {
int read_trigger;
CharDriverState *chr;
qemu_irq irq;
enum pl011_type type;
} pl011_state;
#define PL011_INT_TX 0x20
@ -38,8 +39,10 @@ typedef struct {
#define PL011_FLAG_TXFF 0x20
#define PL011_FLAG_RXFE 0x10
static const unsigned char pl011_id[] =
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
static const unsigned char pl011_id[2][8] = {
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
{ 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
};
static void pl011_update(pl011_state *s)
{
@ -56,7 +59,7 @@ static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000) {
return pl011_id[(offset - 0xfe0) >> 2];
return pl011_id[s->type][(offset - 0xfe0) >> 2];
}
switch (offset >> 2) {
case 0: /* UARTDR */
@ -137,6 +140,9 @@ static void pl011_write(void *opaque, target_phys_addr_t offset,
case 1: /* UARTCR */
s->cr = value;
break;
case 6: /* UARTFR */
/* Writes to Flag register are ignored. */
break;
case 8: /* UARTUARTILPR */
s->ilpr = value;
break;
@ -224,7 +230,7 @@ static CPUWriteMemoryFunc *pl011_writefn[] = {
};
void pl011_init(uint32_t base, qemu_irq irq,
CharDriverState *chr)
CharDriverState *chr, enum pl011_type type)
{
int iomemtype;
pl011_state *s;
@ -235,6 +241,7 @@ void pl011_init(uint32_t base, qemu_irq irq,
cpu_register_physical_memory(base, 0x00001000, iomemtype);
s->base = base;
s->irq = irq;
s->type = type;
s->chr = chr;
s->read_trigger = 1;
s->ifl = 0x12;

264
hw/pl022.c Normal file
View file

@ -0,0 +1,264 @@
/*
* Arm PrimeCell PL022 Synchronous Serial Port
*
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
//#define DEBUG_PL022 1
#ifdef DEBUG_PL022
#define DPRINTF(fmt, args...) \
do { printf("pl022: " fmt , ##args); } while (0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0)
#endif
#define PL022_CR1_LBM 0x01
#define PL022_CR1_SSE 0x02
#define PL022_CR1_MS 0x04
#define PL022_CR1_SDO 0x08
#define PL022_SR_TFE 0x01
#define PL022_SR_TNF 0x02
#define PL022_SR_RNE 0x04
#define PL022_SR_RFF 0x08
#define PL022_SR_BSY 0x10
#define PL022_INT_ROR 0x01
#define PL022_INT_RT 0x04
#define PL022_INT_RX 0x04
#define PL022_INT_TX 0x08
typedef struct {
uint32_t base;
uint32_t cr0;
uint32_t cr1;
uint32_t bitmask;
uint32_t sr;
uint32_t cpsr;
uint32_t is;
uint32_t im;
/* The FIFO head points to the next empty entry. */
int tx_fifo_head;
int rx_fifo_head;
int tx_fifo_len;
int rx_fifo_len;
uint16_t tx_fifo[8];
uint16_t rx_fifo[8];
qemu_irq irq;
int (*xfer_cb)(void *, int);
void *opaque;
} pl022_state;
static const unsigned char pl022_id[8] =
{ 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
static void pl022_update(pl022_state *s)
{
s->sr = 0;
if (s->tx_fifo_len == 0)
s->sr |= PL022_SR_TFE;
if (s->tx_fifo_len != 8)
s->sr |= PL022_SR_TNF;
if (s->rx_fifo_len != 0)
s->sr |= PL022_SR_RNE;
if (s->rx_fifo_len == 8)
s->sr |= PL022_SR_RFF;
if (s->tx_fifo_len)
s->sr |= PL022_SR_BSY;
s->is = 0;
if (s->rx_fifo_len >= 4)
s->is |= PL022_INT_RX;
if (s->tx_fifo_len <= 4)
s->is |= PL022_INT_TX;
qemu_set_irq(s->irq, (s->is & s->im) != 0);
}
static void pl022_xfer(pl022_state *s)
{
int i;
int o;
int val;
if ((s->cr1 & PL022_CR1_SSE) == 0) {
pl022_update(s);
DPRINTF("Disabled\n");
return;
}
DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
o = s->rx_fifo_head;
/* ??? We do not emulate the line speed.
This may break some applications. The are two problematic cases:
(a) A driver feeds data into the TX FIFO until it is full,
and only then drains the RX FIFO. On real hardware the CPU can
feed data fast enough that the RX fifo never gets chance to overflow.
(b) A driver transmits data, deliberately allowing the RX FIFO to
overflow because it ignores the RX data anyway.
We choose to support (a) by stalling the transmit engine if it would
cause the RX FIFO to overflow. In practice much transmit-only code
falls into (a) because it flushes the RX FIFO to determine when
the transfer has completed. */
while (s->tx_fifo_len && s->rx_fifo_len < 8) {
DPRINTF("xfer\n");
val = s->tx_fifo[i];
if (s->cr1 & PL022_CR1_LBM) {
/* Loopback mode. */
} else if (s->xfer_cb) {
val = s->xfer_cb(s->opaque, val);
} else {
val = 0;
}
s->rx_fifo[o] = val & s->bitmask;
i = (i + 1) & 7;
o = (o + 1) & 7;
s->tx_fifo_len--;
s->rx_fifo_len++;
}
s->rx_fifo_head = o;
pl022_update(s);
}
static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
{
pl022_state *s = (pl022_state *)opaque;
int val;
offset -= s->base;
if (offset >= 0xfe0 && offset < 0x1000) {
return pl022_id[(offset - 0xfe0) >> 2];
}
switch (offset) {
case 0x00: /* CR0 */
return s->cr0;
case 0x04: /* CR1 */
return s->cr1;
case 0x08: /* DR */
if (s->rx_fifo_len) {
val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
DPRINTF("RX %02x\n", val);
s->rx_fifo_len--;
pl022_xfer(s);
} else {
val = 0;
}
return val;
case 0x0c: /* SR */
return s->sr;
case 0x10: /* CPSR */
return s->cpsr;
case 0x14: /* IMSC */
return s->im;
case 0x18: /* RIS */
return s->is;
case 0x1c: /* MIS */
return s->im & s->is;
case 0x20: /* DMACR */
/* Not implemented. */
return 0;
default:
cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n",
(int)offset);
return 0;
}
}
static void pl022_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
pl022_state *s = (pl022_state *)opaque;
offset -= s->base;
switch (offset) {
case 0x00: /* CR0 */
s->cr0 = value;
/* Clock rate and format are ignored. */
s->bitmask = (1 << ((value & 15) + 1)) - 1;
break;
case 0x04: /* CR1 */
s->cr1 = value;
if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
== (PL022_CR1_MS | PL022_CR1_SSE)) {
BADF("SPI slave mode not implemented\n");
}
pl022_xfer(s);
break;
case 0x08: /* DR */
if (s->tx_fifo_len < 8) {
DPRINTF("TX %02x\n", value);
s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
s->tx_fifo_len++;
pl022_xfer(s);
}
break;
case 0x10: /* CPSR */
/* Prescaler. Ignored. */
s->cpsr = value & 0xff;
break;
case 0x14: /* IMSC */
s->im = value;
pl022_update(s);
break;
case 0x20: /* DMACR */
if (value)
cpu_abort (cpu_single_env, "pl022: DMA not implemented\n");
break;
default:
cpu_abort (cpu_single_env, "pl022_write: Bad offset %x\n",
(int)offset);
}
}
static void pl022_reset(pl022_state *s)
{
s->rx_fifo_len = 0;
s->tx_fifo_len = 0;
s->im = 0;
s->is = PL022_INT_TX;
s->sr = PL022_SR_TFE | PL022_SR_TNF;
}
static CPUReadMemoryFunc *pl022_readfn[] = {
pl022_read,
pl022_read,
pl022_read
};
static CPUWriteMemoryFunc *pl022_writefn[] = {
pl022_write,
pl022_write,
pl022_write
};
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
void * opaque)
{
int iomemtype;
pl022_state *s;
s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
iomemtype = cpu_register_io_memory(0, pl022_readfn,
pl022_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
s->base = base;
s->irq = irq;
s->xfer_cb = xfer_cb;
s->opaque = opaque;
pl022_reset(s);
/* ??? Save/restore. */
}

256
hw/pl061.c Normal file
View file

@ -0,0 +1,256 @@
/*
* Arm PrimeCell PL061 General Purpose IO with additional
* Luminary Micro Stellaris bits.
*
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
//#define DEBUG_PL061 1
#ifdef DEBUG_PL061
#define DPRINTF(fmt, args...) \
do { printf("pl061: " fmt , ##args); } while (0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0)
#endif
static const uint8_t pl061_id[12] =
{ 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
typedef struct {
uint32_t base;
int locked;
uint8_t data;
uint8_t old_data;
uint8_t dir;
uint8_t isense;
uint8_t ibe;
uint8_t iev;
uint8_t im;
uint8_t istate;
uint8_t afsel;
uint8_t dr2r;
uint8_t dr4r;
uint8_t dr8r;
uint8_t odr;
uint8_t pur;
uint8_t pdr;
uint8_t slr;
uint8_t den;
uint8_t cr;
qemu_irq irq;
qemu_irq out[8];
} pl061_state;
static void pl061_update(pl061_state *s)
{
uint8_t changed;
uint8_t mask;
int i;
changed = s->old_data ^ s->data;
if (!changed)
return;
s->old_data = s->data;
for (i = 0; i < 8; i++) {
mask = 1 << i;
if ((changed & mask & s->dir) && s->out) {
DPRINTF("Set output %d = %d\n", i, (s->data & mask) != 0);
qemu_set_irq(s->out[i], (s->data & mask) != 0);
}
}
/* FIXME: Implement input interrupts. */
}
static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
{
pl061_state *s = (pl061_state *)opaque;
offset -= s->base;
if (offset >= 0xfd0 && offset < 0x1000) {
return pl061_id[(offset - 0xfd0) >> 2];
}
if (offset < 0x400) {
return s->data & (offset >> 2);
}
switch (offset) {
case 0x400: /* Direction */
return s->dir;
case 0x404: /* Interrupt sense */
return s->isense;
case 0x408: /* Interrupt both edges */
return s->ibe;
case 0x40c: /* Interupt event */
return s->iev;
case 0x410: /* Interrupt mask */
return s->im;
case 0x414: /* Raw interrupt status */
return s->istate;
case 0x418: /* Masked interrupt status */
return s->istate | s->im;
case 0x420: /* Alternate function select */
return s->afsel;
case 0x500: /* 2mA drive */
return s->dr2r;
case 0x504: /* 4mA drive */
return s->dr4r;
case 0x508: /* 8mA drive */
return s->dr8r;
case 0x50c: /* Open drain */
return s->odr;
case 0x510: /* Pull-up */
return s->pur;
case 0x514: /* Pull-down */
return s->pdr;
case 0x518: /* Slew rate control */
return s->slr;
case 0x51c: /* Digital enable */
return s->den;
case 0x520: /* Lock */
return s->locked;
case 0x524: /* Commit */
return s->cr;
default:
cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n",
(int)offset);
return 0;
}
}
static void pl061_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
pl061_state *s = (pl061_state *)opaque;
uint8_t mask;
offset -= s->base;
if (offset < 0x400) {
mask = (offset >> 2) & s->dir;
s->data = (s->data & ~mask) | (value & mask);
pl061_update(s);
return;
}
switch (offset) {
case 0x400: /* Direction */
s->dir = value;
break;
case 0x404: /* Interrupt sense */
s->isense = value;
break;
case 0x408: /* Interrupt both edges */
s->ibe = value;
break;
case 0x40c: /* Interupt event */
s->iev = value;
break;
case 0x410: /* Interrupt mask */
s->im = value;
break;
case 0x41c: /* Interrupt clear */
s->istate &= ~value;
break;
case 0x420: /* Alternate function select */
mask = s->cr;
s->afsel = (s->afsel & ~mask) | (value & mask);
break;
case 0x500: /* 2mA drive */
s->dr2r = value;
break;
case 0x504: /* 4mA drive */
s->dr4r = value;
break;
case 0x508: /* 8mA drive */
s->dr8r = value;
break;
case 0x50c: /* Open drain */
s->odr = value;
break;
case 0x510: /* Pull-up */
s->pur = value;
break;
case 0x514: /* Pull-down */
s->pdr = value;
break;
case 0x518: /* Slew rate control */
s->slr = value;
break;
case 0x51c: /* Digital enable */
s->den = value;
break;
case 0x520: /* Lock */
s->locked = (value != 0xacce551);
break;
case 0x524: /* Commit */
if (!s->locked)
s->cr = value;
break;
default:
cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n",
(int)offset);
}
pl061_update(s);
}
static void pl061_reset(pl061_state *s)
{
s->locked = 1;
s->cr = 0xff;
}
void pl061_set_irq(void * opaque, int irq, int level)
{
pl061_state *s = (pl061_state *)opaque;
uint8_t mask;
mask = 1 << irq;
if ((s->dir & mask) == 0) {
s->data &= ~mask;
if (level)
s->data |= mask;
pl061_update(s);
}
}
static CPUReadMemoryFunc *pl061_readfn[] = {
pl061_read,
pl061_read,
pl061_read
};
static CPUWriteMemoryFunc *pl061_writefn[] = {
pl061_write,
pl061_write,
pl061_write
};
/* Returns an array of inputs. */
qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out)
{
int iomemtype;
pl061_state *s;
s = (pl061_state *)qemu_mallocz(sizeof(pl061_state));
iomemtype = cpu_register_io_memory(0, pl061_readfn,
pl061_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
s->base = base;
s->irq = irq;
pl061_reset(s);
if (out)
*out = s->out;
/* ??? Save/restore. */
return qemu_allocate_irqs(pl061_set_irq, s, 8);
}

View file

@ -297,7 +297,7 @@ static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm,
ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
s->env->cp15.c1_sys = 0;
s->env->cp15.c1_coproc = 0;
s->env->cp15.c2_base = 0;
s->env->cp15.c2_base0 = 0;
s->env->cp15.c3 = 0;
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
@ -2031,7 +2031,8 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size,
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
s->env);
/* SDRAM & Internal Memory Storage */
cpu_register_physical_memory(PXA2XX_SDRAM_BASE,
@ -2145,7 +2146,8 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size,
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
s->env);
/* SDRAM & Internal Memory Storage */
cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size,

View file

@ -25,13 +25,32 @@ static void realview_init(int ram_size, int vga_ram_size,
NICInfo *nd;
int n;
int done_smc = 0;
qemu_irq cpu_irq[4];
int ncpu;
if (!cpu_model)
cpu_model = "arm926";
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
/* FIXME: obey smp_cpus. */
if (strcmp(cpu_model, "arm11mpcore") == 0) {
ncpu = 4;
} else {
ncpu = 1;
}
for (n = 0; n < ncpu; n++) {
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
pic = arm_pic_init_cpu(env);
cpu_irq[n] = pic[ARM_PIC_CPU_IRQ];
if (n > 0) {
/* Set entry point for secondary CPUs. This assumes we're using
the init code from arm_boot.c. Real hardware resets all CPUs
the same. */
env->regs[15] = 0x80000000;
}
}
/* ??? RAM shoud repeat to fill physical memory space. */
@ -39,18 +58,23 @@ static void realview_init(int ram_size, int vga_ram_size,
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
arm_sysctl_init(0x10000000, 0xc1400400);
pic = arm_pic_init_cpu(env);
/* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
is nIRQ (there are inconsistencies). However Linux 2.6.17 expects
GIC1 to be nIRQ and ignores all the others, so do that for now. */
pic = arm_gic_init(0x10040000, pic[ARM_PIC_CPU_IRQ]);
if (ncpu == 1) {
/* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
is nIRQ (there are inconsistencies). However Linux 2.6.17 expects
GIC1 to be nIRQ and ignores all the others, so do that for now. */
pic = realview_gic_init(0x10040000, cpu_irq[0]);
} else {
pic = mpcore_irq_init(cpu_irq);
}
pl050_init(0x10006000, pic[20], 0);
pl050_init(0x10007000, pic[21], 1);
pl011_init(0x10009000, pic[12], serial_hds[0]);
pl011_init(0x1000a000, pic[13], serial_hds[1]);
pl011_init(0x1000b000, pic[14], serial_hds[2]);
pl011_init(0x1000c000, pic[15], serial_hds[3]);
pl011_init(0x10009000, pic[12], serial_hds[0], PL011_ARM);
pl011_init(0x1000a000, pic[13], serial_hds[1], PL011_ARM);
pl011_init(0x1000b000, pic[14], serial_hds[2], PL011_ARM);
pl011_init(0x1000c000, pic[15], serial_hds[3], PL011_ARM);
/* DMA controller is optional, apparently. */
pl080_init(0x10030000, pic[24], 2);
@ -114,10 +138,10 @@ static void realview_init(int ram_size, int vga_ram_size,
/* 0x10019000 PCI controller config. */
/* 0x10020000 CLCD. */
/* 0x10030000 DMA Controller. */
/* 0x10040000 GIC1 (FIQ1). */
/* 0x10050000 GIC2 (IRQ1). */
/* 0x10060000 GIC3 (FIQ2). */
/* 0x10070000 GIC4 (IRQ2). */
/* 0x10040000 GIC1. */
/* 0x10050000 GIC2. */
/* 0x10060000 GIC3. */
/* 0x10070000 GIC4. */
/* 0x10080000 SMC. */
/* 0x40000000 NOR flash. */
/* 0x44000000 DoC flash. */
@ -137,8 +161,14 @@ static void realview_init(int ram_size, int vga_ram_size,
/* 0x68000000 PCI mem 1. */
/* 0x6c000000 PCI mem 2. */
arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline,
arm_load_kernel(first_cpu, ram_size, kernel_filename, kernel_cmdline,
initrd_filename, 0x33b, 0x0);
/* ??? Hack to map an additional page of ram for the secondary CPU
startup code. I guess this works on real hardware because the
BootROM happens to be in ROM/flash or in memory that isn't clobbered
until after Linux boots the secondary CPUs. */
cpu_register_physical_memory(0x80000000, 0x1000, IO_MEM_RAM + ram_size);
}
QEMUMachine realview_machine = {

64
hw/realview_gic.c Normal file
View file

@ -0,0 +1,64 @@
/*
* ARM RealView Emulation Baseboard Interrupt Controller
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "vl.h"
#include "arm_pic.h"
#define GIC_NIRQ 96
#define NCPU 1
/* Only a single "CPU" interface is present. */
static inline int
gic_get_current_cpu(void)
{
return 0;
}
#include "arm_gic.c"
static uint32_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset)
{
gic_state *s = (gic_state *)opaque;
offset -= s->base;
return gic_cpu_read(s, gic_get_current_cpu(), offset);
}
static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
gic_state *s = (gic_state *)opaque;
offset -= s->base;
gic_cpu_write(s, gic_get_current_cpu(), offset, value);
}
static CPUReadMemoryFunc *realview_gic_cpu_readfn[] = {
realview_gic_cpu_read,
realview_gic_cpu_read,
realview_gic_cpu_read
};
static CPUWriteMemoryFunc *realview_gic_cpu_writefn[] = {
realview_gic_cpu_write,
realview_gic_cpu_write,
realview_gic_cpu_write
};
qemu_irq *realview_gic_init(uint32_t base, qemu_irq parent_irq)
{
gic_state *s;
int iomemtype;
s = gic_init(base, &parent_irq);
if (!s)
return NULL;
iomemtype = cpu_register_io_memory(0, realview_gic_cpu_readfn,
realview_gic_cpu_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
return s->in;
}

273
hw/ssd0303.c Normal file
View file

@ -0,0 +1,273 @@
/*
* SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
/* The controller can support a variety of different displays, but we only
implement one. Most of the commends relating to brightness and geometry
setup are ignored. */
#include "vl.h"
//#define DEBUG_SSD0303 1
#ifdef DEBUG_SSD0303
#define DPRINTF(fmt, args...) \
do { printf("ssd0303: " fmt , ##args); } while (0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "ssd0303: error: " fmt , ##args); exit(1);} while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "ssd0303: error: " fmt , ##args);} while (0)
#endif
/* Scaling factor for pixels. */
#define MAGNIFY 4
enum ssd0303_mode
{
SSD0303_IDLE,
SSD0303_DATA,
SSD0303_CMD
};
enum ssd0303_cmd {
SSD0303_CMD_NONE,
SSD0303_CMD_SKIP1
};
typedef struct {
i2c_slave i2c;
DisplayState *ds;
int row;
int col;
int start_line;
int mirror;
int flash;
int enabled;
int inverse;
int redraw;
enum ssd0303_mode mode;
enum ssd0303_cmd cmd_state;
uint8_t framebuffer[132*8];
} ssd0303_state;
static int ssd0303_recv(i2c_slave *i2c)
{
BADF("Reads not implemented\n");
return -1;
}
static int ssd0303_send(i2c_slave *i2c, uint8_t data)
{
ssd0303_state *s = (ssd0303_state *)i2c;
enum ssd0303_cmd old_cmd_state;
switch (s->mode) {
case SSD0303_IDLE:
DPRINTF("byte 0x%02x\n", data);
if (data == 0x80)
s->mode = SSD0303_CMD;
else if (data == 0x40)
s->mode = SSD0303_DATA;
else
BADF("Unexpected byte 0x%x\n", data);
break;
case SSD0303_DATA:
DPRINTF("data 0x%02x\n", data);
if (s->col < 132) {
s->framebuffer[s->col + s->row * 132] = data;
s->col++;
s->redraw = 1;
}
break;
case SSD0303_CMD:
old_cmd_state = s->cmd_state;
s->cmd_state = SSD0303_CMD_NONE;
switch (old_cmd_state) {
case SSD0303_CMD_NONE:
DPRINTF("cmd 0x%02x\n", data);
s->mode = SSD0303_IDLE;
switch (data) {
case 0x00 ... 0x0f: /* Set lower colum address. */
s->col = (s->col & 0xf0) | (data & 0xf);
break;
case 0x10 ... 0x20: /* Set higher column address. */
s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
break;
case 0x40 ... 0x7f: /* Set start line. */
s->start_line = 0;
break;
case 0x81: /* Set contrast (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xa0: /* Mirror off. */
s->mirror = 0;
break;
case 0xa1: /* Mirror off. */
s->mirror = 1;
break;
case 0xa4: /* Entire display off. */
s->flash = 0;
break;
case 0xa5: /* Entire display on. */
s->flash = 1;
break;
case 0xa6: /* Inverse off. */
s->inverse = 0;
break;
case 0xa7: /* Inverse on. */
s->inverse = 1;
break;
case 0xa8: /* Set multipled ratio (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xad: /* DC-DC power control. */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xae: /* Display off. */
s->enabled = 0;
break;
case 0xaf: /* Display on. */
s->enabled = 1;
break;
case 0xb0 ... 0xbf: /* Set Page address. */
s->row = data & 7;
break;
case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */
break;
case 0xd3: /* Set display offset (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xd5: /* Set display clock (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xd8: /* Set color and power mode (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xd9: /* Set pre-charge period (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xda: /* Set COM pin configuration (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xdb: /* Set VCOM dselect level (Ignored). */
s->cmd_state = SSD0303_CMD_SKIP1;
break;
case 0xe3: /* no-op. */
break;
default:
BADF("Unknown command: 0x%x\n", data);
}
break;
case SSD0303_CMD_SKIP1:
DPRINTF("skip 0x%02x\n", data);
break;
}
break;
}
return 0;
}
static void ssd0303_event(i2c_slave *i2c, enum i2c_event event)
{
ssd0303_state *s = (ssd0303_state *)i2c;
switch (event) {
case I2C_FINISH:
s->mode = SSD0303_IDLE;
break;
case I2C_START_RECV:
case I2C_START_SEND:
case I2C_NACK:
/* Nothing to do. */
break;
}
}
static void ssd0303_update_display(void *opaque)
{
ssd0303_state *s = (ssd0303_state *)opaque;
uint8_t *dest;
uint8_t *src;
int x;
int y;
int line;
char *colors[2];
char colortab[MAGNIFY * 8];
int dest_width;
uint8_t mask;
if (s->redraw) {
switch (s->ds->depth) {
case 0:
return;
case 15:
dest_width = 2;
break;
case 16:
dest_width = 2;
break;
case 24:
dest_width = 3;
break;
case 32:
dest_width = 4;
break;
default:
BADF("Bad color depth\n");
return;
}
dest_width *= MAGNIFY;
memset(colortab, 0xff, dest_width);
memset(colortab + dest_width, 0, dest_width);
if (s->flash) {
colors[0] = colortab;
colors[1] = colortab;
} else if (s->inverse) {
colors[0] = colortab;
colors[1] = colortab + dest_width;
} else {
colors[0] = colortab + dest_width;
colors[1] = colortab;
}
dest = s->ds->data;
for (y = 0; y < 16; y++) {
line = (y + s->start_line) & 63;
src = s->framebuffer + 132 * (line >> 3) + 36;
mask = 1 << (line & 7);
for (x = 0; x < 96; x++) {
memcpy(dest, colors[(*src & mask) != 0], dest_width);
dest += dest_width;
src++;
}
for (x = 1; x < MAGNIFY; x++) {
memcpy(dest, dest - dest_width * 96, dest_width * 96);
dest += dest_width * 96;
}
}
}
dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
}
static void ssd0303_invalidate_display(void * opaque)
{
ssd0303_state *s = (ssd0303_state *)opaque;
s->redraw = 1;
}
void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address)
{
ssd0303_state *s;
s = (ssd0303_state *)i2c_slave_init(bus, address, sizeof(ssd0303_state));
s->ds = ds;
s->i2c.event = ssd0303_event;
s->i2c.recv = ssd0303_recv;
s->i2c.send = ssd0303_send;
graphic_console_init(ds, ssd0303_update_display, ssd0303_invalidate_display,
NULL, s);
dpy_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY);
}

267
hw/ssd0323.c Normal file
View file

@ -0,0 +1,267 @@
/*
* SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
/* The controller can support a variety of different displays, but we only
implement one. Most of the commends relating to brightness and geometry
setup are ignored. */
#include "vl.h"
//#define DEBUG_SSD0323 1
#ifdef DEBUG_SSD0323
#define DPRINTF(fmt, args...) \
do { printf("ssd0323: " fmt , ##args); } while (0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0)
#endif
/* Scaling factor for pixels. */
#define MAGNIFY 4
enum ssd0323_mode
{
SSD0323_CMD,
SSD0323_DATA
};
typedef struct {
DisplayState *ds;
int cmd_len;
int cmd;
int cmd_data[8];
int row;
int row_start;
int row_end;
int col;
int col_start;
int col_end;
int redraw;
enum ssd0323_mode mode;
uint8_t framebuffer[128 * 80 / 2];
} ssd0323_state;
int ssd0323_xfer_ssi(void *opaque, int data)
{
ssd0323_state *s = (ssd0323_state *)opaque;
switch (s->mode) {
case SSD0323_DATA:
DPRINTF("data 0x%02x\n", data);
s->framebuffer[s->col + s->row * 64] = data;
s->col++;
if (s->col > s->col_end) {
s->row++;
s->col = s->col_start;
}
if (s->row > s->row_end) {
s->row = s->row_start;
}
s->redraw = 1;
break;
case SSD0323_CMD:
DPRINTF("cmd 0x%02x\n", data);
if (s->cmd_len == 0) {
s->cmd = data;
} else {
s->cmd_data[s->cmd_len - 1] = data;
}
s->cmd_len++;
switch (s->cmd) {
#define DATA(x) if (s->cmd_len <= (x)) return 0
case 0x15: /* Set column. */
DATA(2);
s->col_start = s->cmd_data[0] % 64;
s->col_end = s->cmd_data[1] % 64;
break;
case 0x75: /* Set row. */
DATA(2);
s->row_start = s->cmd_data[0] % 80;
s->row_end = s->cmd_data[1] % 80;
break;
case 0x81: /* Set contrast */
DATA(1);
break;
case 0x84: case 0x85: case 0x86: /* Max current. */
DATA(0);
break;
case 0xa0: /* Set remapping. */
/* FIXME: Implement this. */
DATA(1);
break;
case 0xa1: /* Set display start line. */
case 0xa2: /* Set display offset. */
/* FIXME: Implement these. */
DATA(1);
break;
case 0xa4: /* Normal mode. */
case 0xa5: /* All on. */
case 0xa6: /* All off. */
case 0xa7: /* Inverse. */
/* FIXME: Implement these. */
DATA(0);
break;
case 0xa8: /* Set multiplex ratio. */
case 0xad: /* Set DC-DC converter. */
DATA(1);
/* Ignored. Don't care. */
break;
case 0xae: /* Display off. */
case 0xaf: /* Display on. */
DATA(0);
/* TODO: Implement power control. */
break;
case 0xb1: /* Set phase length. */
case 0xb2: /* Set row period. */
case 0xb3: /* Set clock rate. */
case 0xbc: /* Set precharge. */
case 0xbe: /* Set VCOMH. */
case 0xbf: /* Set segment low. */
DATA(1);
/* Ignored. Don't care. */
break;
case 0xb8: /* Set grey scale table. */
/* FIXME: Implement this. */
DATA(8);
break;
case 0xe3: /* NOP. */
DATA(0);
break;
default:
BADF("Unknown command: 0x%x\n", data);
}
s->cmd_len = 0;
return 0;
}
return 0;
}
static void ssd0323_update_display(void *opaque)
{
ssd0323_state *s = (ssd0323_state *)opaque;
uint8_t *dest;
uint8_t *src;
int x;
int y;
int i;
int line;
char *colors[16];
char colortab[MAGNIFY * 64];
char *p;
int dest_width;
if (s->redraw) {
switch (s->ds->depth) {
case 0:
return;
case 15:
dest_width = 2;
break;
case 16:
dest_width = 2;
break;
case 24:
dest_width = 3;
break;
case 32:
dest_width = 4;
break;
default:
BADF("Bad color depth\n");
return;
}
p = colortab;
for (i = 0; i < 16; i++) {
int n;
colors[i] = p;
switch (s->ds->depth) {
case 15:
n = i * 2 + (i >> 3);
p[0] = n | (n << 5);
p[1] = (n << 2) | (n >> 3);
break;
case 16:
n = i * 2 + (i >> 3);
p[0] = n | (n << 6) | ((n << 1) & 0x20);
p[1] = (n << 3) | (n >> 2);
break;
case 24:
case 32:
n = (i << 4) | i;
p[0] = p[1] = p[2] = n;
break;
default:
BADF("Bad color depth\n");
return;
}
p += dest_width;
}
dest = s->ds->data;
for (y = 0; y < 64; y++) {
line = y;
src = s->framebuffer + 64 * line;
for (x = 0; x < 64; x++) {
int val;
val = *src >> 4;
for (i = 0; i < MAGNIFY; i++) {
memcpy(dest, colors[val], dest_width);
dest += dest_width;
}
val = *src & 0xf;
for (i = 0; i < MAGNIFY; i++) {
memcpy(dest, colors[val], dest_width);
dest += dest_width;
}
src++;
}
for (i = 1; i < MAGNIFY; i++) {
memcpy(dest, dest - dest_width * MAGNIFY * 128,
dest_width * 128 * MAGNIFY);
dest += dest_width * 128 * MAGNIFY;
}
}
}
dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
}
static void ssd0323_invalidate_display(void * opaque)
{
ssd0323_state *s = (ssd0323_state *)opaque;
s->redraw = 1;
}
/* Command/data input. */
static void ssd0323_cd(void *opaque, int n, int level)
{
ssd0323_state *s = (ssd0323_state *)opaque;
DPRINTF("%s mode\n", level ? "Data" : "Command");
s->mode = level ? SSD0323_DATA : SSD0323_CMD;
}
void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p)
{
ssd0323_state *s;
qemu_irq *cmd;
s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state));
s->ds = ds;
graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display,
NULL, s);
dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
s->col_end = 63;
s->row_end = 79;
cmd = qemu_allocate_irqs(ssd0323_cd, s, 1);
*cmd_p = *cmd;
return s;
}

1101
hw/stellaris.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -208,10 +208,10 @@ static void versatile_init(int ram_size, int vga_ram_size,
}
}
pl011_init(0x101f1000, pic[12], serial_hds[0]);
pl011_init(0x101f2000, pic[13], serial_hds[1]);
pl011_init(0x101f3000, pic[14], serial_hds[2]);
pl011_init(0x10009000, sic[6], serial_hds[3]);
pl011_init(0x101f1000, pic[12], serial_hds[0], PL011_ARM);
pl011_init(0x101f2000, pic[13], serial_hds[1], PL011_ARM);
pl011_init(0x101f3000, pic[14], serial_hds[2], PL011_ARM);
pl011_init(0x10009000, sic[6], serial_hds[3], PL011_ARM);
pl080_init(0x10130000, pic[17], 8);
sp804_init(0x101e2000, pic[4]);

View file

@ -77,10 +77,12 @@ For system emulation, the following hardware targets are supported:
@item Sun4m (32-bit Sparc processor)
@item Sun4u (64-bit Sparc processor, in progress)
@item Malta board (32-bit MIPS processor)
@item ARM Integrator/CP (ARM926E, 1026E or 946E processor)
@item ARM Versatile baseboard (ARM926E)
@item ARM RealView Emulation baseboard (ARM926EJ-S)
@item ARM Integrator/CP (ARM)
@item ARM Versatile baseboard (ARM)
@item ARM RealView Emulation baseboard (ARM)
@item Spitz, Akita, Borzoi and Terrier PDAs (PXA270 processor)
@item Luminary Micro LM3S811EVB (ARM Cortex-M3)
@item Luminary Micro LM3S6965EVB (ARM Cortex-M3)
@item Freescale MCF5208EVB (ColdFire V2).
@item Arnewsh MCF5206 evaluation board (ColdFire V2).
@item Palm Tungsten|E PDA (OMAP310 processor)
@ -2117,7 +2119,7 @@ devices:
@itemize @minus
@item
ARM926E, ARM1026E or ARM946E CPU
ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU
@item
Two PL011 UARTs
@item
@ -2134,7 +2136,7 @@ The ARM Versatile baseboard is emulated with the following devices:
@itemize @minus
@item
ARM926E CPU
ARM926E, ARM1136 or Cortex-A8 CPU
@item
PL190 Vectored Interrupt Controller
@item
@ -2163,7 +2165,7 @@ The ARM RealView Emulation baseboard is emulated with the following devices:
@itemize @minus
@item
ARM926E CPU
ARM926E, ARM1136, ARM11MPCORE(x4) or Cortex-A8 CPU
@item
ARM AMBA Generic/Distributed Interrupt Controller
@item
@ -2237,6 +2239,34 @@ Secure Digital card connected to OMAP MMC/SD host
Three on-chip UARTs
@end itemize
The Luminary Micro Stellaris LM3S811EVB emulation includes the following
devices:
@itemize @minus
@item
Cortex-M3 CPU core.
@item
64k Flash and 8k SRAM.
@item
Timers, UARTs, ADC and I@math{^2}C interface.
@item
OSRAM Pictiva 96x16 OLED with SSD0303 controller on I@math{^2}C bus.
@end itemize
The Luminary Micro Stellaris LM3S6965EVB emulation includes the following
devices:
@itemize @minus
@item
Cortex-M3 CPU core.
@item
256k Flash and 64k SRAM.
@item
Timers, UARTs, ADC, I@math{^2}C and SSI interfaces.
@item
OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via SSI.
@end itemize
A Linux 2.6 test image is available on the QEMU web site. More
information is available in the QEMU mailing-list archive.

View file

@ -37,6 +37,18 @@
#define EXCP_IRQ 5
#define EXCP_FIQ 6
#define EXCP_BKPT 7
#define EXCP_EXCEPTION_EXIT 8 /* Return from v7M exception. */
#define ARMV7M_EXCP_RESET 1
#define ARMV7M_EXCP_NMI 2
#define ARMV7M_EXCP_HARD 3
#define ARMV7M_EXCP_MEM 4
#define ARMV7M_EXCP_BUS 5
#define ARMV7M_EXCP_USAGE 6
#define ARMV7M_EXCP_SVC 11
#define ARMV7M_EXCP_DEBUG 12
#define ARMV7M_EXCP_PENDSV 14
#define ARMV7M_EXCP_SYSTICK 15
typedef void ARMWriteCPFunc(void *opaque, int cp_info,
int srcreg, int operand, uint32_t value);
@ -76,17 +88,22 @@ typedef struct CPUARMState {
uint32_t VF; /* V is the bit 31. All other bits are undefined */
uint32_t NZF; /* N is bit 31. Z is computed from NZF */
uint32_t QF; /* 0 or 1 */
int thumb; /* 0 = arm mode, 1 = thumb mode */
uint32_t GE; /* cpsr[19:16] */
int thumb; /* cprs[5]. 0 = arm mode, 1 = thumb mode. */
uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */
/* System control coprocessor (cp15) */
struct {
uint32_t c0_cpuid;
uint32_t c0_cachetype;
uint32_t c0_c1[8]; /* Feature registers. */
uint32_t c0_c2[8]; /* Instruction set registers. */
uint32_t c1_sys; /* System control register. */
uint32_t c1_coproc; /* Coprocessor access register. */
uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */
uint32_t c2_base; /* MMU translation table base. */
uint32_t c2_base0; /* MMU translation table base 0. */
uint32_t c2_base1; /* MMU translation table base 1. */
uint32_t c2_mask; /* MMU translation table base mask. */
uint32_t c2_data; /* MPU data cachable bits. */
uint32_t c2_insn; /* MPU instruction cachable bits. */
uint32_t c3; /* MMU domain access control register
@ -100,6 +117,9 @@ typedef struct CPUARMState {
uint32_t c9_data;
uint32_t c13_fcse; /* FCSE PID. */
uint32_t c13_context; /* Context ID. */
uint32_t c13_tls1; /* User RW Thread register. */
uint32_t c13_tls2; /* User RO Thread register. */
uint32_t c13_tls3; /* Privileged Thread register. */
uint32_t c15_cpar; /* XScale Coprocessor Access Register */
uint32_t c15_ticonfig; /* TI925T configuration byte. */
uint32_t c15_i_max; /* Maximum D-cache dirty line index. */
@ -107,6 +127,17 @@ typedef struct CPUARMState {
uint32_t c15_threadid; /* TI debugger thread-ID. */
} cp15;
struct {
uint32_t other_sp;
uint32_t vecbase;
uint32_t basepri;
uint32_t control;
int current_sp;
int exception;
int pending_exception;
void *nvic;
} v7m;
/* Coprocessor IO used by peripherals */
struct {
ARMReadCPFunc *cp_read;
@ -117,6 +148,10 @@ typedef struct CPUARMState {
/* Internal CPU feature flags. */
uint32_t features;
/* Callback for vectored interrupt controller. */
int (*get_irq_vector)(struct CPUARMState *);
void *irq_opaque;
/* exception/interrupt handling */
jmp_buf jmp_env;
int exception_index;
@ -126,7 +161,7 @@ typedef struct CPUARMState {
/* VFP coprocessor state. */
struct {
float64 regs[16];
float64 regs[32];
uint32_t xregs[16];
/* We store these fpcsr fields separately for convenience. */
@ -136,9 +171,16 @@ typedef struct CPUARMState {
/* Temporary variables if we don't have spare fp regs. */
float32 tmp0s, tmp1s;
float64 tmp0d, tmp1d;
/* scratch space when Tn are not sufficient. */
uint32_t scratch[8];
float_status fp_status;
} vfp;
#if defined(CONFIG_USER_ONLY)
struct mmon_state *mmon_entry;
#else
uint32_t mmon_addr;
#endif
/* iwMMXt coprocessor state. */
struct {
@ -169,6 +211,7 @@ int cpu_arm_exec(CPUARMState *s);
void cpu_arm_close(CPUARMState *s);
void do_interrupt(CPUARMState *);
void switch_mode(CPUARMState *, int);
uint32_t do_arm_semihosting(CPUARMState *env);
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
@ -176,6 +219,9 @@ void switch_mode(CPUARMState *, int);
int cpu_arm_signal_handler(int host_signum, void *pinfo,
void *puc);
void cpu_lock(void);
void cpu_unlock(void);
#define CPSR_M (0x1f)
#define CPSR_T (1 << 5)
#define CPSR_F (1 << 6)
@ -183,13 +229,24 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo,
#define CPSR_A (1 << 8)
#define CPSR_E (1 << 9)
#define CPSR_IT_2_7 (0xfc00)
/* Bits 20-23 reserved. */
#define CPSR_GE (0xf << 16)
#define CPSR_RESERVED (0xf << 20)
#define CPSR_J (1 << 24)
#define CPSR_IT_0_1 (3 << 25)
#define CPSR_Q (1 << 27)
#define CPSR_NZCV (0xf << 28)
#define CPSR_V (1 << 28)
#define CPSR_C (1 << 29)
#define CPSR_Z (1 << 30)
#define CPSR_N (1 << 31)
#define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V)
#define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7)
#define CACHED_CPSR_BITS (CPSR_T | CPSR_GE | CPSR_IT | CPSR_Q | CPSR_NZCV)
/* Bits writable in user mode. */
#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE)
/* Execution state bits. MRS read as zero, MSR writes ignored. */
#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
#define CACHED_CPSR_BITS (CPSR_T | CPSR_Q | CPSR_NZCV)
/* Return the current CPSR value. */
static inline uint32_t cpsr_read(CPUARMState *env)
{
@ -197,7 +254,21 @@ static inline uint32_t cpsr_read(CPUARMState *env)
ZF = (env->NZF == 0);
return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
(env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
| (env->thumb << 5);
| (env->thumb << 5) | ((env->condexec_bits & 3) << 25)
| ((env->condexec_bits & 0xfc) << 8)
| (env->GE << 16);
}
/* Return the current xPSR value. */
static inline uint32_t xpsr_read(CPUARMState *env)
{
int ZF;
ZF = (env->NZF == 0);
return (env->NZF & 0x80000000) | (ZF << 30)
| (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
| (env->thumb << 24) | ((env->condexec_bits & 3) << 25)
| ((env->condexec_bits & 0xfc) << 8)
| env->v7m.exception;
}
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
@ -213,6 +284,17 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
env->QF = ((val & CPSR_Q) != 0);
if (mask & CPSR_T)
env->thumb = ((val & CPSR_T) != 0);
if (mask & CPSR_IT_0_1) {
env->condexec_bits &= ~3;
env->condexec_bits |= (val >> 25) & 3;
}
if (mask & CPSR_IT_2_7) {
env->condexec_bits &= 3;
env->condexec_bits |= (val >> 8) & 0xfc;
}
if (mask & CPSR_GE) {
env->GE = (val >> 16) & 0xf;
}
if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
switch_mode(env, val & CPSR_M);
@ -221,6 +303,32 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
}
/* Set the xPSR. Note that some bits of mask must be all-set or all-clear. */
static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
{
/* NOTE: N = 1 and Z = 1 cannot be stored currently */
if (mask & CPSR_NZCV) {
env->NZF = (val & 0xc0000000) ^ 0x40000000;
env->CF = (val >> 29) & 1;
env->VF = (val << 3) & 0x80000000;
}
if (mask & CPSR_Q)
env->QF = ((val & CPSR_Q) != 0);
if (mask & (1 << 24))
env->thumb = ((val & (1 << 24)) != 0);
if (mask & CPSR_IT_0_1) {
env->condexec_bits &= ~3;
env->condexec_bits |= (val >> 25) & 3;
}
if (mask & CPSR_IT_2_7) {
env->condexec_bits &= 3;
env->condexec_bits |= (val >> 8) & 0xfc;
}
if (mask & 0x1ff) {
env->v7m.exception = val & 0x1ff;
}
}
enum arm_cpu_mode {
ARM_CPU_MODE_USR = 0x10,
ARM_CPU_MODE_FIQ = 0x11,
@ -234,6 +342,8 @@ enum arm_cpu_mode {
/* VFP system registers. */
#define ARM_VFP_FPSID 0
#define ARM_VFP_FPSCR 1
#define ARM_VFP_MVFR1 6
#define ARM_VFP_MVFR0 7
#define ARM_VFP_FPEXC 8
#define ARM_VFP_FPINST 9
#define ARM_VFP_FPINST2 10
@ -253,7 +363,15 @@ enum arm_features {
ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */
ARM_FEATURE_XSCALE, /* Intel XScale extensions. */
ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */
ARM_FEATURE_V6,
ARM_FEATURE_V6K,
ARM_FEATURE_V7,
ARM_FEATURE_THUMB2,
ARM_FEATURE_MPU, /* Only has Memory Protection Unit, not full MMU. */
ARM_FEATURE_VFP3,
ARM_FEATURE_NEON,
ARM_FEATURE_DIV,
ARM_FEATURE_M, /* Microcontroller profile. */
ARM_FEATURE_OMAPCP /* OMAP specific CP15 ops handling. */
};
@ -264,27 +382,44 @@ static inline int arm_feature(CPUARMState *env, int feature)
void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
/* Interface between CPU and Interrupt controller. */
void armv7m_nvic_set_pending(void *opaque, int irq);
int armv7m_nvic_acknowledge_irq(void *opaque);
void armv7m_nvic_complete_irq(void *opaque, int irq);
void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
void *opaque);
#define ARM_CPUID_ARM1026 0x4106a262
#define ARM_CPUID_ARM926 0x41069265
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
#define ARM_CPUID_PXA261 0x69052d05
#define ARM_CPUID_PXA262 0x69052d06
#define ARM_CPUID_PXA270 0x69054110
#define ARM_CPUID_PXA270_A0 0x69054110
#define ARM_CPUID_PXA270_A1 0x69054111
#define ARM_CPUID_PXA270_B0 0x69054112
#define ARM_CPUID_PXA270_B1 0x69054113
#define ARM_CPUID_PXA270_C0 0x69054114
#define ARM_CPUID_PXA270_C5 0x69054117
/* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3.
Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are
conventional cores (ie. Application or Realtime profile). */
#define IS_M(env) arm_feature(env, ARM_FEATURE_M)
#define ARM_CPUID(env) (env->cp15.c0_cpuid)
#define ARM_CPUID_ARM1026 0x4106a262
#define ARM_CPUID_ARM926 0x41069265
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
#define ARM_CPUID_PXA261 0x69052d05
#define ARM_CPUID_PXA262 0x69052d06
#define ARM_CPUID_PXA270 0x69054110
#define ARM_CPUID_PXA270_A0 0x69054110
#define ARM_CPUID_PXA270_A1 0x69054111
#define ARM_CPUID_PXA270_B0 0x69054112
#define ARM_CPUID_PXA270_B1 0x69054113
#define ARM_CPUID_PXA270_C0 0x69054114
#define ARM_CPUID_PXA270_C5 0x69054117
#define ARM_CPUID_ARM1136 0x4117b363
#define ARM_CPUID_ARM11MPCORE 0x410fb022
#define ARM_CPUID_CORTEXA8 0x410fc080
#define ARM_CPUID_CORTEXM3 0x410fc231
#define ARM_CPUID_ANY 0xffffffff
#if defined(CONFIG_USER_ONLY)
#define TARGET_PAGE_BITS 12
@ -302,6 +437,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define cpu_signal_handler cpu_arm_signal_handler
#define cpu_list arm_cpu_list
#define ARM_CPU_SAVE_VERSION 1
/* MMU modes definitions */
#define MMU_MODE0_SUFFIX _kernel
#define MMU_MODE1_SUFFIX _user

View file

@ -68,12 +68,18 @@ static inline int cpu_halted(CPUState *env) {
/* In op_helper.c */
void cpu_lock(void);
void cpu_unlock(void);
void helper_set_cp(CPUState *, uint32_t, uint32_t);
uint32_t helper_get_cp(CPUState *, uint32_t);
void helper_set_cp15(CPUState *, uint32_t, uint32_t);
uint32_t helper_get_cp15(CPUState *, uint32_t);
void helper_set_r13_banked(CPUState *env, int mode, uint32_t val);
uint32_t helper_get_r13_banked(CPUState *env, int mode);
uint32_t helper_v7m_mrs(CPUState *env, int reg);
void helper_v7m_msr(CPUState *env, int reg, uint32_t val);
void helper_mark_exclusive(CPUARMState *, uint32_t addr);
int helper_test_exclusive(CPUARMState *, uint32_t addr);
void helper_clrex(CPUARMState *env);
void cpu_loop_exit(void);
@ -91,4 +97,11 @@ void do_vfp_cmpes(void);
void do_vfp_cmped(void);
void do_vfp_set_fpscr(void);
void do_vfp_get_fpscr(void);
float32 helper_recps_f32(float32, float32);
float32 helper_rsqrts_f32(float32, float32);
uint32_t helper_recpe_u32(uint32_t);
uint32_t helper_rsqrte_u32(uint32_t);
float32 helper_recpe_f32(float32);
float32 helper_rsqrte_f32(float32);
void helper_neon_tbl(int rn, int maxindex);
uint32_t helper_neon_mul_p8(uint32_t op1, uint32_t op2);

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
* ARM micro operations
*
* Copyright (c) 2003 Fabrice Bellard
* Copyright (c) 2005 CodeSourcery, LLC
* Copyright (c) 2005-2007 CodeSourcery, LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -101,11 +101,6 @@ void OPPROTO op_movl_T0_im(void)
T0 = PARAM1;
}
void OPPROTO op_movl_T0_T1(void)
{
T0 = T1;
}
void OPPROTO op_movl_T1_im(void)
{
T1 = PARAM1;
@ -236,6 +231,11 @@ void OPPROTO op_bicl_T0_T1(void)
T0 &= ~T1;
}
void OPPROTO op_notl_T0(void)
{
T0 = ~T0;
}
void OPPROTO op_notl_T1(void)
{
T1 = ~T1;
@ -351,6 +351,19 @@ void OPPROTO op_test_le(void)
FORCE_RET();
}
void OPPROTO op_test_T0(void)
{
if (T0)
GOTO_LABEL_PARAM(1);
FORCE_RET();
}
void OPPROTO op_testn_T0(void)
{
if (!T0)
GOTO_LABEL_PARAM(1);
FORCE_RET();
}
void OPPROTO op_goto_tb0(void)
{
GOTO_TB(op_goto_tb0, PARAM1, 0);
@ -368,7 +381,8 @@ void OPPROTO op_exit_tb(void)
void OPPROTO op_movl_T0_cpsr(void)
{
T0 = cpsr_read(env);
/* Execution state bits always read as zero. */
T0 = cpsr_read(env) & ~CPSR_EXEC;
FORCE_RET();
}
@ -438,6 +452,28 @@ void OPPROTO op_addq_lo_T0_T1(void)
T0 = res;
}
/* Dual 16-bit accumulate. */
void OPPROTO op_addq_T0_T1_dual(void)
{
uint64_t res;
res = ((uint64_t)(env->regs[PARAM2]) << 32) | (env->regs[PARAM1]);
res += (int32_t)T0;
res += (int32_t)T1;
env->regs[PARAM1] = (uint32_t)res;
env->regs[PARAM2] = res >> 32;
}
/* Dual 16-bit subtract accumulate. */
void OPPROTO op_subq_T0_T1_dual(void)
{
uint64_t res;
res = ((uint64_t)(env->regs[PARAM2]) << 32) | (env->regs[PARAM1]);
res += (int32_t)T0;
res -= (int32_t)T1;
env->regs[PARAM1] = (uint32_t)res;
env->regs[PARAM2] = res >> 32;
}
void OPPROTO op_logicq_cc(void)
{
env->NZF = (T1 & 0x80000000) | ((T0 | T1) != 0);
@ -455,8 +491,21 @@ void OPPROTO op_logicq_cc(void)
#include "op_mem.h"
#endif
void OPPROTO op_clrex(void)
{
cpu_lock();
helper_clrex(env);
cpu_unlock();
}
/* shifts */
/* Used by NEON. */
void OPPROTO op_shll_T0_im(void)
{
T1 = T1 << PARAM1;
}
/* T1 based */
void OPPROTO op_shll_T1_im(void)
@ -813,8 +862,39 @@ void OPPROTO op_double_T1_saturate(void)
FORCE_RET();
}
/* thumb shift by immediate */
void OPPROTO op_shll_T0_im_thumb(void)
/* Unsigned saturating arithmetic for NEON. */
void OPPROTO op_addl_T0_T1_usaturate(void)
{
uint32_t res;
res = T0 + T1;
if (res < T0) {
env->QF = 1;
T0 = 0xffffffff;
} else {
T0 = res;
}
FORCE_RET();
}
void OPPROTO op_subl_T0_T1_usaturate(void)
{
uint32_t res;
res = T0 - T1;
if (res > T0) {
env->QF = 1;
T0 = 0;
} else {
T0 = res;
}
FORCE_RET();
}
/* Thumb shift by immediate */
void OPPROTO op_shll_T0_im_thumb_cc(void)
{
int shift;
shift = PARAM1;
@ -826,7 +906,13 @@ void OPPROTO op_shll_T0_im_thumb(void)
FORCE_RET();
}
void OPPROTO op_shrl_T0_im_thumb(void)
void OPPROTO op_shll_T0_im_thumb(void)
{
T0 = T0 << PARAM1;
FORCE_RET();
}
void OPPROTO op_shrl_T0_im_thumb_cc(void)
{
int shift;
@ -842,7 +928,20 @@ void OPPROTO op_shrl_T0_im_thumb(void)
FORCE_RET();
}
void OPPROTO op_sarl_T0_im_thumb(void)
void OPPROTO op_shrl_T0_im_thumb(void)
{
int shift;
shift = PARAM1;
if (shift == 0) {
T0 = 0;
} else {
T0 = T0 >> shift;
}
FORCE_RET();
}
void OPPROTO op_sarl_T0_im_thumb_cc(void)
{
int shift;
@ -858,6 +957,19 @@ void OPPROTO op_sarl_T0_im_thumb(void)
FORCE_RET();
}
void OPPROTO op_sarl_T0_im_thumb(void)
{
int shift;
shift = PARAM1;
if (shift == 0) {
env->CF = T0 & 1;
} else {
T0 = ((int32_t)T0) >> shift;
}
FORCE_RET();
}
/* exceptions */
void OPPROTO op_swi(void)
@ -891,6 +1003,12 @@ void OPPROTO op_bkpt(void)
cpu_loop_exit();
}
void OPPROTO op_exception_exit(void)
{
env->exception_index = EXCP_EXCEPTION_EXIT;
cpu_loop_exit();
}
/* VFP support. We follow the convention used for VFP instrunctions:
Single precition routines have a "s" suffix, double precision a
"d" suffix. */
@ -982,6 +1100,28 @@ static inline uint32_t vfp_stoi(float32 s)
return v.i;
}
static inline float64 vfp_itod(uint64_t i)
{
union {
uint64_t i;
float64 d;
} v;
v.i = i;
return v.d;
}
static inline uint64_t vfp_dtoi(float64 d)
{
union {
uint64_t i;
float64 d;
} v;
v.d = d;
return v.i;
}
/* Integer to float conversion. */
VFP_OP(uito, s)
{
@ -1056,6 +1196,32 @@ VFP_OP(fcvts, d)
FT0s = float64_to_float32(FT0d, &env->vfp.fp_status);
}
/* VFP3 fixed point conversion. */
#define VFP_CONV_FIX(name, p, ftype, itype, sign) \
VFP_OP(name##to, p) \
{ \
ftype tmp; \
tmp = sign##int32_to_##ftype ((itype)vfp_##p##toi(FT0##p), \
&env->vfp.fp_status); \
FT0##p = ftype##_scalbn(tmp, PARAM1, &env->vfp.fp_status); \
} \
VFP_OP(to##name, p) \
{ \
ftype tmp; \
tmp = ftype##_scalbn(FT0##p, PARAM1, &env->vfp.fp_status); \
FT0##p = vfp_ito##p((itype)ftype##_to_##sign##int32_round_to_zero(tmp, \
&env->vfp.fp_status)); \
}
VFP_CONV_FIX(sh, d, float64, int16, )
VFP_CONV_FIX(sl, d, float64, int32, )
VFP_CONV_FIX(uh, d, float64, uint16, u)
VFP_CONV_FIX(ul, d, float64, uint32, u)
VFP_CONV_FIX(sh, s, float32, int16, )
VFP_CONV_FIX(sl, s, float32, int32, )
VFP_CONV_FIX(uh, s, float32, uint16, u)
VFP_CONV_FIX(ul, s, float32, uint32, u)
/* Get and Put values from registers. */
VFP_OP(getreg_F0, d)
{
@ -1142,6 +1308,20 @@ void OPPROTO op_vfp_mdrr(void)
FT0d = u.d;
}
/* Load immediate. PARAM1 is the 32 most significant bits of the value. */
void OPPROTO op_vfp_fconstd(void)
{
CPU_DoubleU u;
u.l.upper = PARAM1;
u.l.lower = 0;
FT0d = u.d;
}
void OPPROTO op_vfp_fconsts(void)
{
FT0s = vfp_itos(PARAM1);
}
/* Copy the most significant bit of T0 to all bits of T1. */
void OPPROTO op_signbit_T1_T0(void)
{
@ -1204,9 +1384,9 @@ void OPPROTO op_movl_user_T0(void)
FORCE_RET();
}
void OPPROTO op_movl_T2_T0(void)
void OPPROTO op_movl_T0_T1(void)
{
T2 = T0;
T0 = T1;
}
void OPPROTO op_movl_T0_T2(void)
@ -1214,5 +1394,530 @@ void OPPROTO op_movl_T0_T2(void)
T0 = T2;
}
void OPPROTO op_movl_T1_T0(void)
{
T1 = T0;
}
void OPPROTO op_movl_T1_T2(void)
{
T1 = T2;
}
void OPPROTO op_movl_T2_T0(void)
{
T2 = T0;
}
/* ARMv6 Media instructions. */
/* Note that signed overflow is undefined in C. The following routines are
careful to use unsigned types where modulo arithmetic is required.
Failure to do so _will_ break on newer gcc. */
/* Signed saturating arithmetic. */
/* Perform 16-bit signed satruating addition. */
static inline uint16_t add16_sat(uint16_t a, uint16_t b)
{
uint16_t res;
res = a + b;
if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) {
if (a & 0x8000)
res = 0x8000;
else
res = 0x7fff;
}
return res;
}
/* Perform 8-bit signed satruating addition. */
static inline uint8_t add8_sat(uint8_t a, uint8_t b)
{
uint8_t res;
res = a + b;
if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) {
if (a & 0x80)
res = 0x80;
else
res = 0x7f;
}
return res;
}
/* Perform 16-bit signed satruating subtraction. */
static inline uint16_t sub16_sat(uint16_t a, uint16_t b)
{
uint16_t res;
res = a - b;
if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) {
if (a & 0x8000)
res = 0x8000;
else
res = 0x7fff;
}
return res;
}
/* Perform 8-bit signed satruating subtraction. */
static inline uint8_t sub8_sat(uint8_t a, uint8_t b)
{
uint8_t res;
res = a - b;
if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) {
if (a & 0x80)
res = 0x80;
else
res = 0x7f;
}
return res;
}
#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16);
#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16);
#define ADD8(a, b, n) RESULT(add8_sat(a, b), n, 8);
#define SUB8(a, b, n) RESULT(sub8_sat(a, b), n, 8);
#define PFX q
#include "op_addsub.h"
/* Unsigned saturating arithmetic. */
static inline uint16_t add16_usat(uint16_t a, uint8_t b)
{
uint16_t res;
res = a + b;
if (res < a)
res = 0xffff;
return res;
}
static inline uint16_t sub16_usat(uint16_t a, uint8_t b)
{
if (a < b)
return a - b;
else
return 0;
}
static inline uint8_t add8_usat(uint8_t a, uint8_t b)
{
uint8_t res;
res = a + b;
if (res < a)
res = 0xff;
return res;
}
static inline uint8_t sub8_usat(uint8_t a, uint8_t b)
{
if (a < b)
return a - b;
else
return 0;
}
#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16);
#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16);
#define ADD8(a, b, n) RESULT(add8_usat(a, b), n, 8);
#define SUB8(a, b, n) RESULT(sub8_usat(a, b), n, 8);
#define PFX uq
#include "op_addsub.h"
/* Signed modulo arithmetic. */
#define SARITH16(a, b, n, op) do { \
int32_t sum; \
sum = (int16_t)((uint16_t)(a) op (uint16_t)(b)); \
RESULT(sum, n, 16); \
if (sum >= 0) \
ge |= 3 << (n * 2); \
} while(0)
#define SARITH8(a, b, n, op) do { \
int32_t sum; \
sum = (int8_t)((uint8_t)(a) op (uint8_t)(b)); \
RESULT(sum, n, 8); \
if (sum >= 0) \
ge |= 1 << n; \
} while(0)
#define ADD16(a, b, n) SARITH16(a, b, n, +)
#define SUB16(a, b, n) SARITH16(a, b, n, -)
#define ADD8(a, b, n) SARITH8(a, b, n, +)
#define SUB8(a, b, n) SARITH8(a, b, n, -)
#define PFX s
#define ARITH_GE
#include "op_addsub.h"
/* Unsigned modulo arithmetic. */
#define ADD16(a, b, n) do { \
uint32_t sum; \
sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \
RESULT(sum, n, 16); \
if ((sum >> 16) == 0) \
ge |= 3 << (n * 2); \
} while(0)
#define ADD8(a, b, n) do { \
uint32_t sum; \
sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \
RESULT(sum, n, 8); \
if ((sum >> 8) == 0) \
ge |= 3 << (n * 2); \
} while(0)
#define SUB16(a, b, n) do { \
uint32_t sum; \
sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \
RESULT(sum, n, 16); \
if ((sum >> 16) == 0) \
ge |= 3 << (n * 2); \
} while(0)
#define SUB8(a, b, n) do { \
uint32_t sum; \
sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \
RESULT(sum, n, 8); \
if ((sum >> 8) == 0) \
ge |= 3 << (n * 2); \
} while(0)
#define PFX u
#define ARITH_GE
#include "op_addsub.h"
/* Halved signed arithmetic. */
#define ADD16(a, b, n) \
RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16)
#define SUB16(a, b, n) \
RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16)
#define ADD8(a, b, n) \
RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8)
#define SUB8(a, b, n) \
RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8)
#define PFX sh
#include "op_addsub.h"
/* Halved unsigned arithmetic. */
#define ADD16(a, b, n) \
RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16)
#define SUB16(a, b, n) \
RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16)
#define ADD8(a, b, n) \
RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8)
#define SUB8(a, b, n) \
RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8)
#define PFX uh
#include "op_addsub.h"
void OPPROTO op_pkhtb_T0_T1(void)
{
T0 = (T0 & 0xffff0000) | (T1 & 0xffff);
}
void OPPROTO op_pkhbt_T0_T1(void)
{
T0 = (T0 & 0xffff) | (T1 & 0xffff0000);
}
void OPPROTO op_rev_T0(void)
{
T0 = ((T0 & 0xff000000) >> 24)
| ((T0 & 0x00ff0000) >> 8)
| ((T0 & 0x0000ff00) << 8)
| ((T0 & 0x000000ff) << 24);
}
void OPPROTO op_revh_T0(void)
{
T0 = (T0 >> 16) | (T0 << 16);
}
void OPPROTO op_rev16_T0(void)
{
T0 = ((T0 & 0xff000000) >> 8)
| ((T0 & 0x00ff0000) << 8)
| ((T0 & 0x0000ff00) >> 8)
| ((T0 & 0x000000ff) << 8);
}
void OPPROTO op_revsh_T0(void)
{
T0 = (int16_t)( ((T0 & 0x0000ff00) >> 8)
| ((T0 & 0x000000ff) << 8));
}
void OPPROTO op_rbit_T0(void)
{
T0 = ((T0 & 0xff000000) >> 24)
| ((T0 & 0x00ff0000) >> 8)
| ((T0 & 0x0000ff00) << 8)
| ((T0 & 0x000000ff) << 24);
T0 = ((T0 & 0xf0f0f0f0) >> 4)
| ((T0 & 0x0f0f0f0f) << 4);
T0 = ((T0 & 0x88888888) >> 3)
| ((T0 & 0x44444444) >> 1)
| ((T0 & 0x22222222) << 1)
| ((T0 & 0x11111111) << 3);
}
/* Swap low and high halfwords. */
void OPPROTO op_swap_half_T1(void)
{
T1 = (T1 >> 16) | (T1 << 16);
FORCE_RET();
}
/* Dual 16-bit signed multiply. */
void OPPROTO op_mul_dual_T0_T1(void)
{
int32_t low;
int32_t high;
low = (int32_t)(int16_t)T0 * (int32_t)(int16_t)T1;
high = (((int32_t)T0) >> 16) * (((int32_t)T1) >> 16);
T0 = low;
T1 = high;
}
void OPPROTO op_sel_T0_T1(void)
{
uint32_t mask;
uint32_t flags;
flags = env->GE;
mask = 0;
if (flags & 1)
mask |= 0xff;
if (flags & 2)
mask |= 0xff00;
if (flags & 4)
mask |= 0xff0000;
if (flags & 8)
mask |= 0xff000000;
T0 = (T0 & mask) | (T1 & ~mask);
FORCE_RET();
}
void OPPROTO op_roundqd_T0_T1(void)
{
T0 = T1 + ((uint32_t)T0 >> 31);
}
/* Signed saturation. */
static inline uint32_t do_ssat(int32_t val, int shift)
{
int32_t top;
uint32_t mask;
shift = PARAM1;
top = val >> shift;
mask = (1u << shift) - 1;
if (top > 0) {
env->QF = 1;
return mask;
} else if (top < -1) {
env->QF = 1;
return ~mask;
}
return val;
}
/* Unsigned saturation. */
static inline uint32_t do_usat(int32_t val, int shift)
{
uint32_t max;
shift = PARAM1;
max = (1u << shift) - 1;
if (val < 0) {
env->QF = 1;
return 0;
} else if (val > max) {
env->QF = 1;
return max;
}
return val;
}
/* Signed saturate. */
void OPPROTO op_ssat_T1(void)
{
T0 = do_ssat(T0, PARAM1);
FORCE_RET();
}
/* Dual halfword signed saturate. */
void OPPROTO op_ssat16_T1(void)
{
uint32_t res;
res = (uint16_t)do_ssat((int16_t)T0, PARAM1);
res |= do_ssat(((int32_t)T0) >> 16, PARAM1) << 16;
T0 = res;
FORCE_RET();
}
/* Unsigned saturate. */
void OPPROTO op_usat_T1(void)
{
T0 = do_usat(T0, PARAM1);
FORCE_RET();
}
/* Dual halfword unsigned saturate. */
void OPPROTO op_usat16_T1(void)
{
uint32_t res;
res = (uint16_t)do_usat((int16_t)T0, PARAM1);
res |= do_usat(((int32_t)T0) >> 16, PARAM1) << 16;
T0 = res;
FORCE_RET();
}
/* Dual 16-bit add. */
void OPPROTO op_add16_T1_T2(void)
{
uint32_t mask;
mask = (T0 & T1) & 0x8000;
T0 ^= ~0x8000;
T1 ^= ~0x8000;
T0 = (T0 + T1) ^ mask;
}
static inline uint8_t do_usad(uint8_t a, uint8_t b)
{
if (a > b)
return a - b;
else
return b - a;
}
/* Unsigned sum of absolute byte differences. */
void OPPROTO op_usad8_T0_T1(void)
{
uint32_t sum;
sum = do_usad(T0, T1);
sum += do_usad(T0 >> 8, T1 >> 8);
sum += do_usad(T0 >> 16, T1 >>16);
sum += do_usad(T0 >> 24, T1 >> 24);
T0 = sum;
}
/* Thumb-2 instructions. */
/* Insert T1 into T0. Result goes in T1. */
void OPPROTO op_bfi_T1_T0(void)
{
int shift = PARAM1;
uint32_t mask = PARAM2;
uint32_t bits;
bits = (T1 << shift) & mask;
T1 = (T0 & ~mask) | bits;
}
/* Unsigned bitfield extract. */
void OPPROTO op_ubfx_T1(void)
{
uint32_t shift = PARAM1;
uint32_t mask = PARAM2;
T1 >>= shift;
T1 &= mask;
}
/* Signed bitfield extract. */
void OPPROTO op_sbfx_T1(void)
{
uint32_t shift = PARAM1;
uint32_t width = PARAM2;
int32_t val;
val = T1 << (32 - (shift + width));
T1 = val >> (32 - width);
}
void OPPROTO op_movtop_T0_im(void)
{
T0 = (T0 & 0xffff) | PARAM1;
}
/* Used by table branch instructions. */
void OPPROTO op_jmp_T0_im(void)
{
env->regs[15] = PARAM1 + (T0 << 1);
}
void OPPROTO op_set_condexec(void)
{
env->condexec_bits = PARAM1;
}
void OPPROTO op_sdivl_T0_T1(void)
{
int32_t num;
int32_t den;
num = T0;
den = T1;
if (den == 0)
T0 = 0;
else
T0 = num / den;
FORCE_RET();
}
void OPPROTO op_udivl_T0_T1(void)
{
uint32_t num;
uint32_t den;
num = T0;
den = T1;
if (den == 0)
T0 = 0;
else
T0 = num / den;
FORCE_RET();
}
void OPPROTO op_movl_T1_r13_banked(void)
{
T1 = helper_get_r13_banked(env, PARAM1);
}
void OPPROTO op_movl_r13_T1_banked(void)
{
helper_set_r13_banked(env, PARAM1, T1);
}
void OPPROTO op_v7m_mrs_T0(void)
{
T0 = helper_v7m_mrs(env, PARAM1);
}
void OPPROTO op_v7m_msr_T0(void)
{
helper_v7m_msr(env, PARAM1, T0);
}
void OPPROTO op_movl_T0_sp(void)
{
if (PARAM1 == env->v7m.current_sp)
T0 = env->regs[13];
else
T0 = env->v7m.other_sp;
FORCE_RET();
}
#include "op_neon.h"
/* iwMMXt support */
#include "op_iwmmxt.c"

106
target-arm/op_addsub.h Normal file
View file

@ -0,0 +1,106 @@
/*
* ARMv6 integer SIMD operations.
*
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#ifdef ARITH_GE
#define DECLARE_GE uint32_t ge = 0
#define SET_GE env->GE = ge
#else
#define DECLARE_GE do{}while(0)
#define SET_GE do{}while(0)
#endif
#define RESULT(val, n, width) \
res |= ((uint32_t)(glue(glue(uint,width),_t))(val)) << (n * width)
void OPPROTO glue(glue(op_,PFX),add16_T0_T1)(void)
{
uint32_t res = 0;
DECLARE_GE;
ADD16(T0, T1, 0);
ADD16(T0 >> 16, T1 >> 16, 1);
SET_GE;
T0 = res;
FORCE_RET();
}
void OPPROTO glue(glue(op_,PFX),add8_T0_T1)(void)
{
uint32_t res = 0;
DECLARE_GE;
ADD8(T0, T1, 0);
ADD8(T0 >> 8, T1 >> 8, 1);
ADD8(T0 >> 16, T1 >> 16, 2);
ADD8(T0 >> 24, T1 >> 24, 3);
SET_GE;
T0 = res;
FORCE_RET();
}
void OPPROTO glue(glue(op_,PFX),sub16_T0_T1)(void)
{
uint32_t res = 0;
DECLARE_GE;
SUB16(T0, T1, 0);
SUB16(T0 >> 16, T1 >> 16, 1);
SET_GE;
T0 = res;
FORCE_RET();
}
void OPPROTO glue(glue(op_,PFX),sub8_T0_T1)(void)
{
uint32_t res = 0;
DECLARE_GE;
SUB8(T0, T1, 0);
SUB8(T0 >> 8, T1 >> 8, 1);
SUB8(T0 >> 16, T1 >> 16, 2);
SUB8(T0 >> 24, T1 >> 24, 3);
SET_GE;
T0 = res;
FORCE_RET();
}
void OPPROTO glue(glue(op_,PFX),subaddx_T0_T1)(void)
{
uint32_t res = 0;
DECLARE_GE;
ADD16(T0, T1, 0);
SUB16(T0 >> 16, T1 >> 16, 1);
SET_GE;
T0 = res;
FORCE_RET();
}
void OPPROTO glue(glue(op_,PFX),addsubx_T0_T1)(void)
{
uint32_t res = 0;
DECLARE_GE;
SUB16(T0, T1, 0);
ADD16(T0 >> 16, T1 >> 16, 1);
SET_GE;
T0 = res;
FORCE_RET();
}
#undef DECLARE_GE
#undef SET_GE
#undef RESULT
#undef ARITH_GE
#undef PFX
#undef ADD16
#undef SUB16
#undef ADD8
#undef SUB8

View file

@ -1,7 +1,7 @@
/*
* ARM helper routines
*
* Copyright (c) 2005 CodeSourcery, LLC
* Copyright (c) 2005-2007 CodeSourcery, LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -175,6 +175,81 @@ void do_vfp_get_fpscr(void)
T0 |= vfp_exceptbits_from_host(i);
}
float32 helper_recps_f32(float32 a, float32 b)
{
float_status *s = &env->vfp.fp_status;
float32 two = int32_to_float32(2, s);
return float32_sub(two, float32_mul(a, b, s), s);
}
float32 helper_rsqrts_f32(float32 a, float32 b)
{
float_status *s = &env->vfp.fp_status;
float32 three = int32_to_float32(3, s);
return float32_sub(three, float32_mul(a, b, s), s);
}
/* TODO: The architecture specifies the value that the estimate functions
should return. We return the exact reciprocal/root instead. */
float32 helper_recpe_f32(float32 a)
{
float_status *s = &env->vfp.fp_status;
float32 one = int32_to_float32(1, s);
return float32_div(one, a, s);
}
float32 helper_rsqrte_f32(float32 a)
{
float_status *s = &env->vfp.fp_status;
float32 one = int32_to_float32(1, s);
return float32_div(one, float32_sqrt(a, s), s);
}
uint32_t helper_recpe_u32(uint32_t a)
{
float_status *s = &env->vfp.fp_status;
float32 tmp;
tmp = int32_to_float32(a, s);
tmp = float32_scalbn(tmp, -32, s);
tmp = helper_recpe_f32(tmp);
tmp = float32_scalbn(tmp, 31, s);
return float32_to_int32(tmp, s);
}
uint32_t helper_rsqrte_u32(uint32_t a)
{
float_status *s = &env->vfp.fp_status;
float32 tmp;
tmp = int32_to_float32(a, s);
tmp = float32_scalbn(tmp, -32, s);
tmp = helper_rsqrte_f32(tmp);
tmp = float32_scalbn(tmp, 31, s);
return float32_to_int32(tmp, s);
}
void helper_neon_tbl(int rn, int maxindex)
{
uint32_t val;
uint32_t mask;
uint32_t tmp;
int index;
int shift;
uint64_t *table;
table = (uint64_t *)&env->vfp.regs[rn];
val = 0;
mask = 0;
for (shift = 0; shift < 32; shift += 8) {
index = (T1 >> shift) & 0xff;
if (index <= maxindex) {
tmp = (table[index >> 3] >> (index & 7)) & 0xff;
val |= tmp << shift;
} else {
val |= T0 & (0xff << shift);
}
}
T0 = val;
}
#if !defined(CONFIG_USER_ONLY)
#define MMUSUFFIX _mmu
@ -227,5 +302,4 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
}
env = saved_env;
}
#endif

View file

@ -1,5 +1,6 @@
/* ARM memory operations. */
void helper_ld(uint32_t);
/* Load from address T1 into T0. */
#define MEM_LD_OP(name) \
void OPPROTO glue(op_ld##name,MEMSUFFIX)(void) \
@ -49,6 +50,64 @@ MEM_SWP_OP(l, l)
#undef MEM_SWP_OP
/* Load-locked, store exclusive. */
#define EXCLUSIVE_OP(suffix, ldsuffix) \
void OPPROTO glue(op_ld##suffix##ex,MEMSUFFIX)(void) \
{ \
cpu_lock(); \
helper_mark_exclusive(env, T1); \
T0 = glue(ld##ldsuffix,MEMSUFFIX)(T1); \
cpu_unlock(); \
FORCE_RET(); \
} \
\
void OPPROTO glue(op_st##suffix##ex,MEMSUFFIX)(void) \
{ \
int failed; \
cpu_lock(); \
failed = helper_test_exclusive(env, T1); \
/* ??? Is it safe to hold the cpu lock over a store? */ \
if (!failed) { \
glue(st##suffix,MEMSUFFIX)(T1, T0); \
} \
T0 = failed; \
cpu_unlock(); \
FORCE_RET(); \
}
EXCLUSIVE_OP(b, ub)
EXCLUSIVE_OP(w, uw)
EXCLUSIVE_OP(l, l)
#undef EXCLUSIVE_OP
/* Load exclusive T0:T1 from address T1. */
void OPPROTO glue(op_ldqex,MEMSUFFIX)(void)
{
cpu_lock();
helper_mark_exclusive(env, T1);
T0 = glue(ldl,MEMSUFFIX)(T1);
T1 = glue(ldl,MEMSUFFIX)((T1 + 4));
cpu_unlock();
FORCE_RET();
}
/* Store exclusive T0:T2 to address T1. */
void OPPROTO glue(op_stqex,MEMSUFFIX)(void)
{
int failed;
cpu_lock();
failed = helper_test_exclusive(env, T1);
/* ??? Is it safe to hold the cpu lock over a store? */
if (!failed) {
glue(stl,MEMSUFFIX)(T1, T0);
glue(stl,MEMSUFFIX)((T1 + 4), T2);
}
T0 = failed;
cpu_unlock();
FORCE_RET();
}
/* Floating point load/store. Address is in T1 */
#define VFP_MEM_OP(p, w) \
void OPPROTO glue(op_vfp_ld##p,MEMSUFFIX)(void) \

1754
target-arm/op_neon.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

54
vl.c
View file

@ -6126,7 +6126,9 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, env->cp15.c1_sys);
qemu_put_be32(f, env->cp15.c1_coproc);
qemu_put_be32(f, env->cp15.c1_xscaleauxcr);
qemu_put_be32(f, env->cp15.c2_base);
qemu_put_be32(f, env->cp15.c2_base0);
qemu_put_be32(f, env->cp15.c2_base1);
qemu_put_be32(f, env->cp15.c2_mask);
qemu_put_be32(f, env->cp15.c2_data);
qemu_put_be32(f, env->cp15.c2_insn);
qemu_put_be32(f, env->cp15.c3);
@ -6141,6 +6143,9 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, env->cp15.c9_data);
qemu_put_be32(f, env->cp15.c13_fcse);
qemu_put_be32(f, env->cp15.c13_context);
qemu_put_be32(f, env->cp15.c13_tls1);
qemu_put_be32(f, env->cp15.c13_tls2);
qemu_put_be32(f, env->cp15.c13_tls3);
qemu_put_be32(f, env->cp15.c15_cpar);
qemu_put_be32(f, env->features);
@ -6159,6 +6164,15 @@ void cpu_save(QEMUFile *f, void *opaque)
/* TODO: Should use proper FPSCR access functions. */
qemu_put_be32(f, env->vfp.vec_len);
qemu_put_be32(f, env->vfp.vec_stride);
if (arm_feature(env, ARM_FEATURE_VFP3)) {
for (i = 16; i < 32; i++) {
CPU_DoubleU u;
u.d = env->vfp.regs[i];
qemu_put_be32(f, u.l.upper);
qemu_put_be32(f, u.l.lower);
}
}
}
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
@ -6169,6 +6183,15 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, env->iwmmxt.cregs[i]);
}
}
if (arm_feature(env, ARM_FEATURE_M)) {
qemu_put_be32(f, env->v7m.other_sp);
qemu_put_be32(f, env->v7m.vecbase);
qemu_put_be32(f, env->v7m.basepri);
qemu_put_be32(f, env->v7m.control);
qemu_put_be32(f, env->v7m.current_sp);
qemu_put_be32(f, env->v7m.exception);
}
}
int cpu_load(QEMUFile *f, void *opaque, int version_id)
@ -6176,7 +6199,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
CPUARMState *env = (CPUARMState *)opaque;
int i;
if (version_id != 0)
if (version_id != ARM_CPU_SAVE_VERSION)
return -EINVAL;
for (i = 0; i < 16; i++) {
@ -6198,7 +6221,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
env->cp15.c1_sys = qemu_get_be32(f);
env->cp15.c1_coproc = qemu_get_be32(f);
env->cp15.c1_xscaleauxcr = qemu_get_be32(f);
env->cp15.c2_base = qemu_get_be32(f);
env->cp15.c2_base0 = qemu_get_be32(f);
env->cp15.c2_base1 = qemu_get_be32(f);
env->cp15.c2_mask = qemu_get_be32(f);
env->cp15.c2_data = qemu_get_be32(f);
env->cp15.c2_insn = qemu_get_be32(f);
env->cp15.c3 = qemu_get_be32(f);
@ -6213,6 +6238,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
env->cp15.c9_data = qemu_get_be32(f);
env->cp15.c13_fcse = qemu_get_be32(f);
env->cp15.c13_context = qemu_get_be32(f);
env->cp15.c13_tls1 = qemu_get_be32(f);
env->cp15.c13_tls2 = qemu_get_be32(f);
env->cp15.c13_tls3 = qemu_get_be32(f);
env->cp15.c15_cpar = qemu_get_be32(f);
env->features = qemu_get_be32(f);
@ -6231,6 +6259,15 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
/* TODO: Should use proper FPSCR access functions. */
env->vfp.vec_len = qemu_get_be32(f);
env->vfp.vec_stride = qemu_get_be32(f);
if (arm_feature(env, ARM_FEATURE_VFP3)) {
for (i = 0; i < 16; i++) {
CPU_DoubleU u;
u.l.upper = qemu_get_be32(f);
u.l.lower = qemu_get_be32(f);
env->vfp.regs[i] = u.d;
}
}
}
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
@ -6242,6 +6279,15 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
}
}
if (arm_feature(env, ARM_FEATURE_M)) {
env->v7m.other_sp = qemu_get_be32(f);
env->v7m.vecbase = qemu_get_be32(f);
env->v7m.basepri = qemu_get_be32(f);
env->v7m.control = qemu_get_be32(f);
env->v7m.current_sp = qemu_get_be32(f);
env->v7m.exception = qemu_get_be32(f);
}
return 0;
}
@ -7392,6 +7438,8 @@ void register_machines(void)
qemu_register_machine(&borzoipda_machine);
qemu_register_machine(&terrierpda_machine);
qemu_register_machine(&palmte_machine);
qemu_register_machine(&lm3s811evb_machine);
qemu_register_machine(&lm3s6965evb_machine);
#elif defined(TARGET_SH4)
qemu_register_machine(&shix_machine);
qemu_register_machine(&r2d_machine);

40
vl.h
View file

@ -1482,6 +1482,14 @@ extern QEMUMachine terrierpda_machine;
/* palm.c */
extern QEMUMachine palmte_machine;
/* armv7m.c */
qemu_irq *armv7m_init(int flash_size, int sram_size,
const char *kernel_filename, const char *cpu_model);
/* stellaris.c */
extern QEMUMachine lm3s811evb_machine;
extern QEMUMachine lm3s6965evb_machine;
/* ps2.c */
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
@ -1502,11 +1510,24 @@ void pl031_init(uint32_t base, qemu_irq irq);
void *pl110_init(DisplayState *ds, uint32_t base, qemu_irq irq, int);
/* pl011.c */
void pl011_init(uint32_t base, qemu_irq irq, CharDriverState *chr);
enum pl011_type {
PL011_ARM,
PL011_LUMINARY
};
void pl011_init(uint32_t base, qemu_irq irq, CharDriverState *chr,
enum pl011_type type);
/* pl022.c */
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
void *opaque);
/* pl050.c */
void pl050_init(uint32_t base, qemu_irq irq, int is_mouse);
/* pl061.c */
qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out);
/* pl080.c */
void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
@ -1524,8 +1545,11 @@ void icp_pit_init(uint32_t base, qemu_irq *pic, int irq);
/* arm_sysctl.c */
void arm_sysctl_init(uint32_t base, uint32_t sys_id);
/* arm_gic.c */
qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq);
/* realview_gic.c */
qemu_irq *realview_gic_init(uint32_t base, qemu_irq parent_irq);
/* mpcore.c */
extern qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq);
/* arm_boot.c */
@ -1533,6 +1557,16 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
const char *kernel_cmdline, const char *initrd_filename,
int board_id, target_phys_addr_t loader_start);
/* armv7m_nvic.c */
qemu_irq *armv7m_nvic_init(CPUState *env);
/* ssd0303.c */
void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address);
/* ssd0323.c */
int ssd0323_xfer_ssi(void *opaque, int data);
void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p);
/* sh7750.c */
struct SH7750State;