All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3
@ 2010-12-06  9:26 Fabien Chouteau
  2010-12-06  9:26 ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual Fabien Chouteau
                   ` (2 more replies)
  0 siblings, 3 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06  9:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fabien Chouteau

Hi everyone,
I'm glad to submit my first patches to the Qemu-devel list.

This patch set introduces a new SPARC V8 machine: Leon3. It's an open-source
VHDL System-On-Chip, well known in space industry (more information on
http://www.gaisler.com).

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

Modifications have been done to the SPARC cpu emulation code to handle
Leon3's specific behavior:
 - IRQ management
 - Cache control
 - Asr17 (implementation-dependent Ancillary State Registers)
 - Shutdown

Please feel free to comment.

Regards,

Fabien Chouteau (6):
  Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  Emulation of GRLIB APB UART as defined in GRLIB IP Core User's
    Manual.
  Header file for the GRLIB components.
  Emulation of Leon3.
  SPARCV8 asr17 register support.

 Makefile.target          |    5 +-
 hw/grlib.h               |   27 +++
 hw/grlib_apbuart.c       |  231 ++++++++++++++++++++++++
 hw/grlib_gptimer.c       |  448 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/grlib_irqmp.c         |  416 ++++++++++++++++++++++++++++++++++++++++++
 hw/leon3.c               |  316 ++++++++++++++++++++++++++++++++
 target-sparc/cpu.h       |   11 ++
 target-sparc/helper.c    |    2 +-
 target-sparc/machine.c   |    2 +
 target-sparc/op_helper.c |   30 +++-
 target-sparc/translate.c |   10 +
 11 files changed, 1494 insertions(+), 4 deletions(-)
 create mode 100644 hw/grlib.h
 create mode 100644 hw/grlib_apbuart.c
 create mode 100644 hw/grlib_gptimer.c
 create mode 100644 hw/grlib_irqmp.c
 create mode 100644 hw/leon3.c

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26 [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Fabien Chouteau
@ 2010-12-06  9:26 ` Fabien Chouteau
  2010-12-06  9:26   ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Fabien Chouteau
                     ` (2 more replies)
  2010-12-06 10:44 ` [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Artyom Tarasenko
  2010-12-06 18:05 ` Blue Swirl
  2 siblings, 3 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06  9:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fabien Chouteau


Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 448 insertions(+), 0 deletions(-)

diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
new file mode 100644
index 0000000..41edbe4
--- /dev/null
+++ b/hw/grlib_gptimer.c
@@ -0,0 +1,448 @@
+/*
+ * QEMU GRLIB GPTimer Emulator
+ *
+ * Copyright (c) 2010 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+#include "grlib.h"
+
+/* #define DEBUG_TIMER */
+
+#ifdef DEBUG_TIMER
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
+#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
+
+#define GPTIMER_MAX_TIMERS 8
+
+/* GPTimer Config register fields */
+#define GPTIMER_ENABLE      (1 << 0)
+#define GPTIMER_RESTART     (1 << 1)
+#define GPTIMER_LOAD        (1 << 2)
+#define GPTIMER_INT_ENABLE  (1 << 3)
+#define GPTIMER_INT_PENDING (1 << 4)
+#define GPTIMER_CHAIN       (1 << 5) /* Not supported */
+#define GPTIMER_DEBUG_HALT  (1 << 6) /* Not supported */
+
+/* Memory mapped register offsets */
+#define SCALER_OFFSET         0x00
+#define SCALER_RELOAD_OFFSET  0x04
+#define CONFIG_OFFSET         0x08
+#define COUNTER_OFFSET        0x00
+#define COUNTER_RELOAD_OFFSET 0x04
+#define TIMER_BASE            0x10
+
+typedef struct GPTimer     GPTimer;
+typedef struct GPTimerUnit GPTimerUnit;
+
+struct GPTimer
+{
+    QEMUBH *bh;
+    struct ptimer_state *ptimer;
+
+    qemu_irq     irq;
+    int          id;
+    GPTimerUnit *unit;
+
+    /* registers */
+    uint32_t counter;
+    uint32_t reload;
+    uint32_t config;
+};
+
+struct GPTimerUnit
+{
+    SysBusDevice  busdev;
+
+    uint32_t nr_timers;         /* Number of timers available */
+    uint32_t freq_hz;           /* System frequency */
+    uint32_t irq_line;          /* Base irq line */
+
+    GPTimer *timers;
+
+    /* registers */
+    uint32_t scaler;
+    uint32_t reload;
+    uint32_t config;
+};
+
+DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
+                                  uint32_t            nr_timers,
+                                  uint32_t            freq,
+                                  qemu_irq           *cpu_irqs,
+                                  int                 base_irq)
+{
+    DeviceState *dev;
+    int i;
+
+    dev = qdev_create(NULL, "grlib,gptimer");
+    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
+    qdev_prop_set_uint32(dev, "frequency", freq);
+    qdev_prop_set_uint32(dev, "irq-line", base_irq);
+
+    if (qdev_init(dev)) {
+        return NULL;
+    }
+
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+
+    for (i = 0; i < nr_timers; i++)
+        sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
+
+    return dev;
+}
+
+static void grlib_gptimer_enable(GPTimer *timer)
+{
+    assert(timer != NULL);
+
+    DPRINTF("%s id:%d\n", __func__, timer->id);
+
+    ptimer_stop(timer->ptimer);
+
+    if (!(timer->config & GPTIMER_ENABLE)) {
+        /* Timer disabled */
+        DPRINTF("%s id:%d Timer disabled (config 0x%x)\n", __func__,
+                timer->id, timer->config);
+        return;
+    }
+
+    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
+       underflow. Set count + 1 to simulate the GPTimer behavior. */
+
+    DPRINTF("%s id:%d set count 0x%x and run\n",
+            __func__,
+            timer->id,
+            timer->counter + 1);
+
+    ptimer_set_count(timer->ptimer, timer->counter + 1);
+    ptimer_run(timer->ptimer, 1);
+}
+
+static void grlib_gptimer_restart(GPTimer *timer)
+{
+    assert(timer != NULL);
+
+    DPRINTF("%s id:%d reload val: 0x%x\n", __func__, timer->id, timer->reload);
+
+    timer->counter = timer->reload;
+    grlib_gptimer_enable(timer);
+}
+
+static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
+{
+    int i = 0;
+    uint32_t value = 0;
+
+    assert(unit != NULL);
+
+
+    if (scaler > 0) {
+        value = unit->freq_hz / (scaler + 1);
+    } else {
+        value = unit->freq_hz;
+    }
+
+    DPRINTF("%s scaler:%d freq:0x%x\n", __func__, scaler, value);
+
+    for (i = 0; i < unit->nr_timers; i++) {
+        ptimer_set_freq(unit->timers[i].ptimer, value);
+    }
+}
+
+static void grlib_gptimer_hit(void *opaque)
+{
+    GPTimer *timer = opaque;
+    assert(timer != NULL);
+
+    DPRINTF("%s id:%d\n", __func__, timer->id);
+
+    /* Timer expired */
+
+    if (timer->config & GPTIMER_INT_ENABLE) {
+        /* Set the pending bit (only unset by write in the config register) */
+        timer->config &= GPTIMER_INT_PENDING;
+        qemu_set_irq(timer->irq, 1);
+    }
+
+    if (timer->config & GPTIMER_RESTART) {
+        grlib_gptimer_restart(timer);
+    }
+}
+
+static uint32_t grlib_gptimer_readl (void *opaque, target_phys_addr_t addr)
+{
+    GPTimerUnit *unit  = opaque;
+    uint32_t     value = 0;
+
+    addr &= 0xff;
+
+    assert(unit != NULL);
+
+    /* Unit registers */
+    switch (addr)
+    {
+        case SCALER_OFFSET:
+            DPRINTF("%s scaler: 0x%x\n", __func__, unit->scaler);
+            return unit->scaler;
+
+        case SCALER_RELOAD_OFFSET:
+            DPRINTF("%s reload: 0x%x\n", __func__, unit->reload);
+            return unit->reload;
+
+        case CONFIG_OFFSET:
+            DPRINTF("%s unit config: 0x%x\n", __func__, unit->config);
+            return unit->config;
+
+        default:
+            break;
+    }
+
+    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
+    int                id         = (addr - TIMER_BASE) / TIMER_BASE;
+
+    if (id >= 0 && id < unit->nr_timers) {
+
+        /* GPTimer registers */
+        switch (timer_addr)
+        {
+            case COUNTER_OFFSET:
+                value = ptimer_get_count (unit->timers[id].ptimer);
+                DPRINTF("%s counter value for timer %d: 0x%x\n",
+                        __func__, id, value);
+                return value;
+
+            case COUNTER_RELOAD_OFFSET:
+                value = unit->timers[id].reload;
+                DPRINTF("%s reload value for timer %d: 0x%x\n",
+                        __func__, id, value);
+                return value;
+
+            case CONFIG_OFFSET:
+                DPRINTF("%s config for timer %d: 0x%x\n",
+                        __func__, id, unit->timers[id].config);
+                return unit->timers[id].config;
+
+            default:
+                break;
+        }
+
+    }
+
+    DPRINTF("read unknown register 0x%04x\n", (int)addr);
+    return 0;
+}
+
+static void
+grlib_gptimer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    GPTimerUnit *unit = opaque;
+
+    addr &= 0xff;
+
+    assert(unit != NULL);
+
+    /* Unit registers */
+    switch (addr)
+    {
+        case SCALER_OFFSET:
+            value &= 0xFFFF; /* clean up the value */
+            unit->scaler = value;
+            return;
+
+        case SCALER_RELOAD_OFFSET:
+            value &= 0xFFFF; /* clean up the value */
+            unit->reload = value;
+            grlib_gptimer_set_scaler(unit, value);
+            return;
+
+        case CONFIG_OFFSET:
+            /* Read Only (disable timer freeze not supported) */
+            return;
+
+        default:
+            break;
+    }
+
+    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
+    int                id         = (addr - TIMER_BASE) / TIMER_BASE;
+
+    if (id >= 0 && id < unit->nr_timers) {
+
+        /* GPTimer registers */
+        switch (timer_addr)
+        {
+            case COUNTER_OFFSET:
+                DPRINTF("%s counter value for timer %d: 0x%x\n",
+                        __func__, id, value);
+                unit->timers[id].counter = value;
+                grlib_gptimer_enable(&unit->timers[id]);
+                return;
+
+            case COUNTER_RELOAD_OFFSET:
+                DPRINTF("%s reload value for timer %d: 0x%x\n",
+                        __func__, id, value);
+                unit->timers[id].reload = value;
+                return;
+
+            case CONFIG_OFFSET:
+                DPRINTF("%s config for timer %d: 0x%x\n", __func__, id, value);
+
+                unit->timers[id].config = value;
+
+                /* gptimer_restart calls gptimer_enable, so if "enable" and
+                   "load" bits are present, we just have to call restart. */
+
+                if (value & GPTIMER_LOAD) {
+                    grlib_gptimer_restart(&unit->timers[id]);
+                } else if (value & GPTIMER_ENABLE) {
+                    grlib_gptimer_enable(&unit->timers[id]);
+                }
+
+                /* This fields must always be read as 0 */
+                value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
+
+                unit->timers[id].config = value;
+                return;
+
+            default:
+                break;
+        }
+
+    }
+
+    DPRINTF("write unknown register 0x%04x\n", (int)addr);
+}
+
+static CPUReadMemoryFunc *grlib_gptimer_read[] = {
+    NULL, NULL, grlib_gptimer_readl,
+};
+
+static CPUWriteMemoryFunc *grlib_gptimer_write[] = {
+    NULL, NULL, grlib_gptimer_writel,
+};
+
+static void grlib_gptimer_reset(void *opaque)
+{
+    /* int          i    = 0; */
+    /* GPTimerUnit *unit = (GPTimerUnit *)opaque; */
+    /* assert(unit != NULL); */
+
+    /* unit->scaler = 0; */
+    /* unit->reload = 0; */
+    /* unit->config = 0; */
+
+    /* unit->config  = unit->nr_timers; */
+    /* unit->config |= unit->irq_line << 3; */
+    /* unit->config |= 1 << 8;     /\* separate interrupt *\/ */
+    /* unit->config |= 1 << 9;     /\* Disable timer freeze *\/ */
+
+
+    /* for (i = 0; i < unit->nr_timers; i++) { */
+    /*     GPTimer *timer = &unit->timers[i]; */
+
+    /*     timer->counter = 0; */
+    /*     timer->reload = 0; */
+    /*     timer->config = 0; */
+    /*     ptimer_stop(timer->ptimer); */
+    /*     ptimer_set_count(timer->ptimer, 0); */
+    /*     ptimer_set_freq(timer->ptimer, unit->freq_hz); */
+    /* } */
+
+}
+
+static int grlib_gptimer_init(SysBusDevice *dev)
+{
+    GPTimerUnit  *unit = FROM_SYSBUS(typeof (*unit), dev);
+    unsigned int  i;
+    int           timer_regs;
+
+    assert(unit->nr_timers > 0);
+    assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
+    unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers);
+
+    for (i = 0; i < unit->nr_timers; i++) {
+        GPTimer *timer = &unit->timers[i];
+
+        timer->unit   = unit;
+        timer->bh     = qemu_bh_new(grlib_gptimer_hit, timer);
+        timer->ptimer = ptimer_init(timer->bh);
+        timer->id     = i;
+
+        /* One IRQ line for each timer */
+        sysbus_init_irq(dev, &timer->irq);
+
+        ptimer_set_freq(timer->ptimer, unit->freq_hz);
+    }
+
+    qemu_register_reset(grlib_gptimer_reset, unit);
+
+    timer_regs = cpu_register_io_memory(grlib_gptimer_read,
+                                        grlib_gptimer_write,
+                                        unit);
+    if (timer_regs < 0) {
+        return -1;
+    }
+
+    sysbus_init_mmio(dev, UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers,
+                     timer_regs);
+    return 0;
+}
+
+static SysBusDeviceInfo grlib_gptimer_info = {
+    .init       = grlib_gptimer_init,
+    .qdev.name  = "grlib,gptimer",
+    .qdev.size  = sizeof(GPTimerUnit),
+    .qdev.props = (Property[]) {
+        {
+            .name   = "frequency",
+            .info   = &qdev_prop_uint32,
+            .offset = offsetof(GPTimerUnit, freq_hz),
+            .defval = (uint32_t[]) { 2 },
+        },{
+            .name   = "irq-line",
+            .info   = &qdev_prop_uint32,
+            .offset = offsetof(GPTimerUnit, irq_line),
+            .defval = (uint32_t[]) { 8 },
+        },{
+            .name   = "nr-timers",
+            .info   = &qdev_prop_uint32,
+            .offset = offsetof(GPTimerUnit, nr_timers),
+            .defval = (uint32_t[]) { 2 },
+        },
+        {/* end of list */}
+    }
+};
+
+static void grlib_gptimer_register(void)
+{
+    sysbus_register_withprop(&grlib_gptimer_info);
+}
+
+device_init(grlib_gptimer_register)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26 ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual Fabien Chouteau
@ 2010-12-06  9:26   ` Fabien Chouteau
  2010-12-06  9:26     ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART " Fabien Chouteau
                       ` (2 more replies)
  2010-12-06 17:12   ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer " Blue Swirl
  2010-12-08 22:51   ` Edgar E. Iglesias
  2 siblings, 3 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06  9:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fabien Chouteau


Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 hw/grlib_irqmp.c |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 416 insertions(+), 0 deletions(-)

diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
new file mode 100644
index 0000000..69e1553
--- /dev/null
+++ b/hw/grlib_irqmp.c
@@ -0,0 +1,416 @@
+/*
+ * QEMU GRLIB IRQMP Emulator
+ *
+ * (Multiprocessor and extended interrupt not supported)
+ *
+ * Copyright (c) 2010 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "cpu.h"
+
+#include "grlib.h"
+
+/* #define DEBUG_IRQ */
+
+#ifdef DEBUG_IRQ
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define IRQMP_MAX_CPU 16
+#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
+
+/* Memory mapped register offsets */
+#define LEVEL_OFFSET     0x00
+#define PENDING_OFFSET   0x04
+#define FORCE0_OFFSET    0x08
+#define CLEAR_OFFSET     0x0C
+#define MP_STATUS_OFFSET 0x10
+#define BROADCAST_OFFSET 0x14
+#define MASK_OFFSET      0x40
+#define FORCE_OFFSET     0x80
+#define EXTENDED_OFFSET  0xC0
+
+typedef struct IRQMP
+{
+    SysBusDevice busdev;
+
+    CPUSPARCState *env;
+} IRQMP;
+
+typedef struct IRQMPState
+{
+    uint32_t level;
+    uint32_t pending;
+    uint32_t clear;
+    uint32_t broadcast;
+
+    uint32_t mask[IRQMP_MAX_CPU];
+    uint32_t force[IRQMP_MAX_CPU];
+    uint32_t extended[IRQMP_MAX_CPU];
+
+    IRQMP    *parent;
+} IRQMPState;
+
+IRQMPState grlib_irqmp_state;
+
+void grlib_irqmp_set_irq(void *opaque, int irq, int level);
+
+DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
+                                CPUState            *env,
+                                qemu_irq           **cpu_irqs,
+                                uint32_t             nr_irqs)
+{
+    DeviceState *dev;
+
+    assert(cpu_irqs != NULL);
+
+    dev = qdev_create(NULL, "grlib,irqmp");
+    qdev_prop_set_ptr(dev, "cpustate", env);
+
+    if (qdev_init(dev)) {
+        return NULL;
+    }
+
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+
+    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
+                                   &grlib_irqmp_state,
+                                   nr_irqs);
+
+    return dev;
+}
+
+static void grlib_irqmp_check_irqs(CPUState *env)
+{
+    uint32_t pend   = 0;
+    uint32_t level0 = 0;
+    uint32_t level1 = 0;
+
+    assert(env != NULL);
+
+    /* IRQ for CPU 0 (no SMP support) */
+    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
+        & grlib_irqmp_state.mask[0];
+
+
+    level0 = pend & ~grlib_irqmp_state.level;
+    level1 = pend &  grlib_irqmp_state.level;
+
+    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n",
+            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
+            grlib_irqmp_state.mask[0], level1, level0);
+
+    /* Trigger level1 interrupt first and level0 if there is no level1 */
+    if (level1 != 0) {
+        env->pil_in = level1;
+    } else {
+        env->pil_in = level0;
+    }
+
+    if (env->pil_in && (env->interrupt_index == 0 ||
+                        (env->interrupt_index & ~15) == TT_EXTINT)) {
+        unsigned int i;
+
+        for (i = 15; i > 0; i--) {
+            if (env->pil_in & (1 << i)) {
+                int old_interrupt = env->interrupt_index;
+
+                env->interrupt_index = TT_EXTINT | i;
+                if (old_interrupt != env->interrupt_index) {
+                    DPRINTF("Set CPU IRQ %d\n", i);
+                    cpu_interrupt(env, CPU_INTERRUPT_HARD);
+                }
+                break;
+            }
+        }
+    } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) {
+        DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index & 15);
+        env->interrupt_index = 0;
+        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+    }
+}
+
+void grlib_irqmp_ack(CPUSPARCState *env, int intno)
+{
+    assert(env != NULL);
+
+    uint32_t mask;
+
+    intno &= 15;
+    mask = 1 << intno;
+
+    DPRINTF("grlib_irqmp_ack %d\n", intno);
+
+    /* Clear registers */
+    grlib_irqmp_state.pending  &= ~mask;
+    grlib_irqmp_state.force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
+
+    grlib_irqmp_check_irqs(env);
+}
+
+void grlib_irqmp_set_irq(void *opaque, int irq, int level)
+{
+    IRQMPState *s = opaque;
+    int         i = 0;
+
+    assert(opaque != NULL);
+    assert(s->parent != NULL);
+
+    if (level) {
+        DPRINTF("Raise CPU IRQ %d\n", irq);
+
+        if (s->broadcast & 1 << irq) {
+            /* Broadcasted IRQ */
+            for (i = 0; i < IRQMP_MAX_CPU; i++) {
+                s->force[i] |= 1 << irq;
+            }
+        } else {
+            s->pending |= 1 << irq;
+        }
+        grlib_irqmp_check_irqs(s->parent->env);
+
+    } else {
+
+        DPRINTF("Lower CPU IRQ %d\n", irq);
+        if (s->broadcast & 1 << irq) {
+            /* Broadcasted IRQ */
+            for (i = 0; i < IRQMP_MAX_CPU; i++) {
+                s->force[i] &= ~(1 << irq);
+            }
+        } else {
+            s->pending &= ~(1 << irq);
+        }
+        grlib_irqmp_check_irqs(s->parent->env);
+    }
+}
+
+static uint32_t grlib_irqmp_readl (void *opaque, target_phys_addr_t addr)
+{
+    IRQMP *irqmp = opaque;
+
+    assert(irqmp != NULL);
+
+    addr &= 0xff;
+
+    /* global registers */
+    switch (addr)
+    {
+        case LEVEL_OFFSET:
+            return grlib_irqmp_state.level;
+
+        case PENDING_OFFSET:
+            return grlib_irqmp_state.pending;
+
+        case FORCE0_OFFSET:
+            /* This register is an "alias" for the force register of CPU 0 */
+            return grlib_irqmp_state.force[0];
+
+        case CLEAR_OFFSET:
+        case MP_STATUS_OFFSET:
+            /* Always read as 0 */
+            return 0;
+
+        case BROADCAST_OFFSET:
+            return grlib_irqmp_state.broadcast;
+
+        default:
+            break;
+    }
+
+    /* mask registers */
+    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
+        int cpu = (addr - MASK_OFFSET) / 4;
+        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+        return grlib_irqmp_state.mask[cpu] ;
+    }
+
+    /* force registers */
+    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
+        int cpu = (addr - FORCE_OFFSET) / 4;
+        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+        return grlib_irqmp_state.force[cpu];
+    }
+
+    /* extended (not supported) */
+    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
+        int cpu = (addr - EXTENDED_OFFSET) / 4;
+        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+        return grlib_irqmp_state.extended[cpu];
+    }
+
+    DPRINTF("read unknown register 0x%04x\n", (int)addr);
+    return 0;
+}
+
+static void
+grlib_irqmp_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    IRQMP *irqmp = opaque;
+
+    assert(irqmp != NULL);
+
+    addr &= 0xff;
+
+    /* global registers */
+    switch (addr)
+    {
+        case LEVEL_OFFSET:
+            value &= 0xFFFF << 1; /* clean up the value */
+            grlib_irqmp_state.level = value;
+            return;
+
+        case PENDING_OFFSET:
+            /* Read Only */
+            return;
+
+        case FORCE0_OFFSET:
+            /* This register is an "alias" for the force register of CPU 0 */
+
+            value &= 0xFFFE; /* clean up the value */
+            grlib_irqmp_state.force[0] = value;
+            grlib_irqmp_check_irqs(irqmp->env);
+            return;
+
+        case CLEAR_OFFSET:
+            value &= ~1; /* clean up the value */
+            grlib_irqmp_state.pending &= ~value;
+            return;
+
+        case MP_STATUS_OFFSET:
+            /* Read Only (no SMP support) */
+            return;
+
+        case BROADCAST_OFFSET:
+            value &= 0xFFFE; /* clean up the value */
+            grlib_irqmp_state.broadcast = value;
+            return;
+
+        default:
+            break;
+    }
+
+    /* mask registers */
+    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
+        int cpu = (addr - MASK_OFFSET) / 4;
+        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+        value &= ~1; /* clean up the value */
+        grlib_irqmp_state.mask[cpu] = value;
+        grlib_irqmp_check_irqs(irqmp->env);
+        return;
+    }
+
+    /* force registers */
+    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
+        int cpu = (addr - FORCE_OFFSET) / 4;
+        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+        uint32_t force = value & 0xFFFE;
+        uint32_t clear = (value >> 16) & 0xFFFE;
+        uint32_t old   = grlib_irqmp_state.force[cpu];
+
+        grlib_irqmp_state.force[cpu] = (old | force) & ~clear;
+        grlib_irqmp_check_irqs(irqmp->env);
+        return;
+    }
+
+    /* extended (not supported) */
+    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
+        int cpu = (addr - EXTENDED_OFFSET) / 4;
+        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+        value &= 0xF; /* clean up the value */
+        grlib_irqmp_state.extended[cpu] = value;
+        return;
+    }
+
+    DPRINTF("write unknown register 0x%04x\n", (int)addr);
+}
+
+static CPUReadMemoryFunc *grlib_irqmp_read[] = {
+    NULL, NULL, &grlib_irqmp_readl,
+};
+
+static CPUWriteMemoryFunc *grlib_irqmp_write[] = {
+    NULL, NULL, &grlib_irqmp_writel,
+};
+
+static void grlib_irqmp_reset(void *opaque)
+{
+    IRQMP *irqmp = (IRQMP *)opaque;
+    assert(irqmp != NULL);
+
+    memset(&grlib_irqmp_state, 0, sizeof grlib_irqmp_state);
+    grlib_irqmp_state.parent = irqmp;
+}
+
+static int grlib_irqmp_init(SysBusDevice *dev)
+{
+    IRQMP *irqmp = FROM_SYSBUS(typeof (*irqmp), dev);
+    int    irqmp_regs;
+
+    assert(irqmp != NULL);
+    assert(irqmp->env != NULL);
+
+    /* qemu_register_reset(grlib_irqmp_reset, irqmp); */
+    grlib_irqmp_reset(irqmp);
+
+    irqmp_regs = cpu_register_io_memory(grlib_irqmp_read,
+                                        grlib_irqmp_write,
+                                        irqmp);
+
+    if (irqmp_regs < 0) {
+        return -1;
+    }
+
+    sysbus_init_mmio(dev, IRQMP_REG_SIZE, irqmp_regs);
+
+    return 0;
+}
+
+static SysBusDeviceInfo grlib_irqmp_info = {
+    .init = grlib_irqmp_init,
+    .qdev.name  = "grlib,irqmp",
+    .qdev.size  = sizeof(IRQMP),
+    .qdev.props = (Property[]) {
+        {
+            .name   = "cpustate",
+            .info   = &qdev_prop_ptr,
+            .offset = offsetof(IRQMP, env),
+            .defval = (void*[]) { NULL },
+        },
+        {/* end of list */}
+    }
+};
+
+static void grlib_irqmp_register(void)
+{
+    sysbus_register_withprop(&grlib_irqmp_info);
+}
+
+device_init(grlib_irqmp_register)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26   ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Fabien Chouteau
@ 2010-12-06  9:26     ` Fabien Chouteau
  2010-12-06  9:26       ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Fabien Chouteau
  2010-12-06 17:29       ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART as defined in GRLIB IP Core User's Manual Blue Swirl
  2010-12-06 17:25     ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Blue Swirl
  2010-12-09 10:32     ` Edgar E. Iglesias
  2 siblings, 2 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06  9:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fabien Chouteau


Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 hw/grlib_apbuart.c |  231 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 231 insertions(+), 0 deletions(-)

diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c
new file mode 100644
index 0000000..32a5362
--- /dev/null
+++ b/hw/grlib_apbuart.c
@@ -0,0 +1,231 @@
+/*
+ * QEMU GRLIB APB UART Emulator
+ *
+ * Copyright (c) 2010 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+
+#include "grlib.h"
+
+/* #define DEBUG_UART */
+
+#ifdef DEBUG_UART
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("APBUART: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define UART_REG_SIZE 20     /* Size of memory mapped registers */
+
+/* UART status register fields */
+#define UART_DATA_READY           (1 <<  0)
+#define UART_TRANSMIT_SHIFT_EMPTY (1 <<  1)
+#define UART_TRANSMIT_FIFO_EMPTY  (1 <<  2)
+#define UART_BREAK_RECEIVED       (1 <<  3)
+#define UART_OVERRUN              (1 <<  4)
+#define UART_PARITY_ERROR         (1 <<  5)
+#define UART_FRAMING_ERROR        (1 <<  6)
+#define UART_TRANSMIT_FIFO_HALF   (1 <<  7)
+#define UART_RECEIV_FIFO_HALF     (1 <<  8)
+#define UART_TRANSMIT_FIFO_FULL   (1 <<  9)
+#define UART_RECEIV_FIFO_FULL     (1 << 10)
+
+/* UART control register fields */
+#define UART_RECEIV_ENABLE           (1 <<  0)
+#define UART_TRANSMIT_ENABLE         (1 <<  1)
+#define UART_RECEIV_INTERRUPT        (1 <<  2)
+#define UART_TRANSMIT_INTERRUPT      (1 <<  3)
+#define UART_PARITY_SELECT           (1 <<  4)
+#define UART_PARITY_ENABLE           (1 <<  5)
+#define UART_FLOW_CONTROL            (1 <<  6)
+#define UART_LOOPBACK                (1 <<  7)
+#define UART_EXTERNAL_CLOCK          (1 <<  8)
+#define UART_RECEIV_FIFO_INTERRUPT   (1 <<  9)
+#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10)
+#define UART_FIFO_DEBUG_MODE         (1 << 11)
+#define UART_OUTPUT_ENABLE           (1 << 12)
+#define UART_FIFO_AVAILABLE          (1 << 31)
+
+/* Memory mapped register offsets */
+#define DATA_OFFSET       0x00
+#define STATUS_OFFSET     0x04
+#define CONTROL_OFFSET    0x08
+#define SCALER_OFFSET     0x0C  /* not supported */
+#define FIFO_DEBUG_OFFSET 0x10  /* not supported */
+
+typedef struct UART
+{
+    SysBusDevice busdev;
+
+    qemu_irq irq;
+
+    CharDriverState *chr;
+
+    /* registers */
+    uint32_t receive;
+    uint32_t status;
+    uint32_t control;
+} UART;
+
+
+DeviceState *grlib_apbuart_create(target_phys_addr_t  base,
+                                  CharDriverState    *serial,
+                                  qemu_irq            irq)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, "grlib,apbuart");
+    qdev_prop_set_ptr(dev, "chrdev", serial);
+
+    if (qdev_init(dev)) {
+        return NULL;
+    }
+
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+
+    return dev;
+}
+
+static int grlib_apbuart_can_receive(void *opaque)
+{
+    UART *uart = opaque;
+    assert(uart != NULL);
+
+    return !!(uart->status & UART_DATA_READY);
+}
+
+static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size)
+{
+    UART *uart = opaque;
+    assert(uart != NULL);
+
+    uart->receive  = *buf;
+    uart->status  |= UART_DATA_READY;
+
+    if (uart->control & UART_RECEIV_INTERRUPT)
+        qemu_set_irq(uart->irq, 1);
+}
+
+static void grlib_apbuart_event(void *opaque, int event)
+{
+#ifdef DEBUG_UART
+    printf("uart: event %x\n", event);
+#endif
+}
+
+static void
+grlib_apbuart_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    UART          *uart = opaque;
+    unsigned char  c    = 0;
+
+    addr &= 0xff;
+
+    assert(uart != NULL);
+
+    /* Unit registers */
+    switch (addr)
+    {
+        case DATA_OFFSET:
+            c = value & 0xFF;
+            qemu_chr_write(uart->chr, &c, 1);
+            return;
+
+        case STATUS_OFFSET:
+            /* Read Only (disable timer freeze not supported) */
+            return;
+
+        case CONTROL_OFFSET:
+            return;
+
+        case SCALER_OFFSET:
+            /* Not supported */
+            return;
+
+        default:
+            break;
+    }
+
+    DPRINTF("write unknown register 0x%04x\n", (int)addr);
+}
+
+static CPUReadMemoryFunc *grlib_apbuart_read[] = {
+    NULL, NULL, NULL,
+};
+
+static CPUWriteMemoryFunc *grlib_apbuart_write[] = {
+    NULL, NULL, grlib_apbuart_writel,
+};
+
+static int grlib_gptimer_init(SysBusDevice *dev)
+{
+    UART *uart      = FROM_SYSBUS(typeof (*uart), dev);
+    int   uart_regs = 0;
+
+    assert(uart != NULL);
+    assert(uart->chr != NULL);
+
+    qemu_chr_add_handlers(uart->chr,
+                          grlib_apbuart_can_receive,
+                          grlib_apbuart_receive,
+                          grlib_apbuart_event,
+                          uart);
+
+    sysbus_init_irq(dev, &uart->irq);
+
+    uart_regs = cpu_register_io_memory(grlib_apbuart_read,
+                                       grlib_apbuart_write,
+                                       uart);
+    if (uart_regs < 0) {
+        return -1;
+    }
+
+    sysbus_init_mmio(dev, UART_REG_SIZE, uart_regs);
+
+    return 0;
+}
+
+static SysBusDeviceInfo grlib_gptimer_info = {
+    .init       = grlib_gptimer_init,
+    .qdev.name  = "grlib,apbuart",
+    .qdev.size  = sizeof(UART),
+    .qdev.props = (Property[]) {
+        {
+            .name   = "chrdev",
+            .info   = &qdev_prop_ptr,
+            .offset = offsetof(UART, chr),
+            .defval = (void*[]) { NULL },
+        },
+        {/* end of list */}
+    }
+};
+
+static void grlib_gptimer_register(void)
+{
+    sysbus_register_withprop(&grlib_gptimer_info);
+}
+
+device_init(grlib_gptimer_register)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components.
  2010-12-06  9:26     ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART " Fabien Chouteau
@ 2010-12-06  9:26       ` Fabien Chouteau
  2010-12-06  9:26         ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Fabien Chouteau
  2010-12-06 17:31         ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Blue Swirl
  2010-12-06 17:29       ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART as defined in GRLIB IP Core User's Manual Blue Swirl
  1 sibling, 2 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06  9:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fabien Chouteau


Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 hw/grlib.h |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/hw/grlib.h b/hw/grlib.h
new file mode 100644
index 0000000..eab7974
--- /dev/null
+++ b/hw/grlib.h
@@ -0,0 +1,27 @@
+#ifndef _GRLIB_H_
+#define _GRLIB_H_
+
+#include "qdev.h"
+
+/* IRQMP */
+
+DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
+                                CPUState            *env,
+                                qemu_irq           **cpu_irqs,
+                                uint32_t             nr_irqs);
+
+/* GPTimer */
+
+DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
+                                  uint32_t            nr_timers,
+                                  uint32_t            freq,
+                                  qemu_irq           *cpu_irqs,
+                                  int                 base_irq);
+
+/* APB UART */
+
+DeviceState *grlib_apbuart_create(target_phys_addr_t  base,
+                                  CharDriverState    *serial,
+                                  qemu_irq            irq);
+
+#endif /* ! _GRLIB_H_ */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-06  9:26       ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Fabien Chouteau
@ 2010-12-06  9:26         ` Fabien Chouteau
  2010-12-06  9:26           ` [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support Fabien Chouteau
                             ` (2 more replies)
  2010-12-06 17:31         ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Blue Swirl
  1 sibling, 3 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06  9:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fabien Chouteau


Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 Makefile.target          |    5 +-
 hw/leon3.c               |  310 ++++++++++++++++++++++++++++++++++++++++++++++
 target-sparc/cpu.h       |   10 ++
 target-sparc/helper.c    |    2 +-
 target-sparc/op_helper.c |   30 ++++-
 5 files changed, 353 insertions(+), 4 deletions(-)

diff --git a/Makefile.target b/Makefile.target
index 2800f47..f40e04f 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
 else
 obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
 obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
-obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
+obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
+
+# GRLIB
+obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
 endif
 
 obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
diff --git a/hw/leon3.c b/hw/leon3.c
new file mode 100644
index 0000000..ba61081
--- /dev/null
+++ b/hw/leon3.c
@@ -0,0 +1,310 @@
+/*
+ * QEMU Leon3 System Emulator
+ *
+ * Copyright (c) 2010 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "loader.h"
+#include "elf.h"
+
+#include "grlib.h"
+
+/* #define DEBUG_LEON3 */
+
+#ifdef DEBUG_LEON3
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/* Default system clock.  */
+#define CPU_CLK (40 * 1000 * 1000)
+
+#define PROM_FILENAME        "u-boot.bin"
+
+#define MAX_PILS 16
+
+typedef struct Leon3State
+{
+    uint32_t cache_control;
+    uint32_t inst_cache_conf;
+    uint32_t data_cache_conf;
+
+    uint64_t entry;             /* save kernel entry in case of reset */
+} Leon3State;
+
+Leon3State leon3_state;
+
+/* Cache control: emulate the behavior of cache control registers but without
+   any effect on the emulated CPU */
+
+#define CACHE_DISABLED 0x0
+#define CACHE_FROZEN   0x1
+#define CACHE_ENABLED  0x3
+
+/* Cache Control register fields */
+
+#define CACHE_CTRL_IF (1 <<  4)  /* Instruction Cache Freeze on Interrupt */
+#define CACHE_CTRL_DF (1 <<  5)  /* Data Cache Freeze on Interrupt */
+#define CACHE_CTRL_DP (1 << 14)  /* Data cache flush pending */
+#define CACHE_CTRL_IP (1 << 15)  /* Instruction cache flush pending */
+#define CACHE_CTRL_IB (1 << 16)  /* Instruction burst fetch */
+#define CACHE_CTRL_FI (1 << 21)  /* Flush Instruction cache (Write only) */
+#define CACHE_CTRL_FD (1 << 22)  /* Flush Data cache (Write only) */
+#define CACHE_CTRL_DS (1 << 23)  /* Data cache snoop enable */
+
+void leon3_cache_control_int(void)
+{
+    uint32_t state = 0;
+
+    if (leon3_state.cache_control & CACHE_CTRL_IF) {
+        /* Instruction cache state */
+        state = leon3_state.cache_control & 0x3;
+        if (state == CACHE_ENABLED) {
+            state = CACHE_FROZEN;
+            DPRINTF("Instruction cache: freeze\n");
+        }
+
+        leon3_state.cache_control &= ~0x3;
+        leon3_state.cache_control |= state;
+    }
+
+    if (leon3_state.cache_control & CACHE_CTRL_DF) {
+        /* Data cache state */
+        state = (leon3_state.cache_control >> 2) & 0x3;
+        if (state == CACHE_ENABLED) {
+            state = CACHE_FROZEN;
+            DPRINTF("Data cache: freeze\n");
+        }
+
+        leon3_state.cache_control &= ~(0x3 << 2);
+        leon3_state.cache_control |= (state << 2);
+    }
+}
+
+void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
+{
+    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned int)addr,
+            (unsigned int)val, size);
+
+    if (size != 4) {
+        DPRINTF(" CC 32bits only\n");
+        return;
+    }
+
+    switch (addr) {
+        case 0x00:              /* Cache control */
+
+            /* These values must always be read as zeros */
+            val &= ~CACHE_CTRL_FD;
+            val &= ~CACHE_CTRL_FI;
+            val &= ~CACHE_CTRL_IB;
+            val &= ~CACHE_CTRL_IP;
+            val &= ~CACHE_CTRL_DP;
+
+            leon3_state.cache_control = val;
+            break;
+        case 0x04:              /* Instruction cache configuration */
+        case 0x08:              /* Data cache configuration */
+            /* Read Only */
+            break;
+        default:
+            DPRINTF(" CC write unknown register 0x%04x\n", (int)addr);
+            break;
+    };
+}
+
+uint64_t leon3_cache_control_ld(target_ulong addr, int size)
+{
+    uint64_t ret = 0;
+
+    if (size != 4) {
+        DPRINTF(" CC 32bits only\n");
+        return 0;
+    }
+
+    switch (addr) {
+        case 0x00:              /* Cache control */
+            ret = leon3_state.cache_control;
+            break;
+        case 0x04:              /* Instruction cache configuration */
+            ret = leon3_state.inst_cache_conf;
+            break;
+        case 0x08:              /* Data cache configuration */
+            ret = leon3_state.data_cache_conf;
+            break;
+        default:
+            DPRINTF(" CC read unknown register 0x%04x\n", (int)addr);
+            break;
+    };
+    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned int)addr,
+            size, (long unsigned int)ret );
+    return ret;
+}
+
+void leon3_shutdown(void)
+{
+    qemu_system_shutdown_request();
+}
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUState *env = opaque;
+
+    cpu_reset(env);
+
+    env->halted = 0;
+    env->pc     = leon3_state.entry;
+    env->npc    = leon3_state.entry + 4;
+
+    /* Initialize cache control */
+    leon3_state.cache_control   = 0x0;
+
+    /* Configuration registers are read and only always keep those predefined
+       values */
+    leon3_state.inst_cache_conf = 0x10220000;
+    leon3_state.data_cache_conf = 0x18220000;
+}
+
+static void leon3_generic_hw_init(ram_addr_t  ram_size,
+                                  const char *boot_device,
+                                  const char *kernel_filename,
+                                  const char *kernel_cmdline,
+                                  const char *initrd_filename,
+                                  const char *cpu_model)
+{
+    CPUState   *env;
+    ram_addr_t  ram_offset, prom_offset;
+    int         ret;
+    char       *filename;
+    qemu_irq   *cpu_irqs = NULL;
+    int         bios_size;
+    int         prom_size;
+    int         aligned_bios_size;
+
+    /* Init CPU */
+    if (!cpu_model)
+        cpu_model = "LEON3";
+
+    env = cpu_init(cpu_model);
+    if (!env) {
+        fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
+        exit(1);
+    }
+
+    cpu_sparc_set_id(env, 0);
+
+    qemu_register_reset(main_cpu_reset, env);
+
+    /* Allocate IRQ manager */
+    grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS);
+
+    /* Allocate RAM */
+    if ((uint64_t)ram_size > (1UL << 30)) {
+        fprintf(stderr,
+                "qemu: Too much memory for this machine: %d, maximum 1G\n",
+                (unsigned int)(ram_size / (1024 * 1024)));
+        exit(1);
+    }
+
+    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
+    cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM);
+
+    /* Allocate BIOS */
+    prom_size = 8 * 1024 * 1024; /* 8Mb */
+    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
+    cpu_register_physical_memory(0x00000000, prom_size,
+                                 prom_offset | IO_MEM_ROM);
+
+    /* Load boot prom */
+    if (bios_name == NULL)
+        bios_name = PROM_FILENAME;
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+
+    bios_size = get_image_size(filename);
+
+    if (bios_size > prom_size) {
+        fprintf(stderr, "qemu: could not load prom '%s': file too big \n",
+                filename);
+        exit(1);
+    }
+
+    if (bios_size > 0) {
+        aligned_bios_size =
+            (bios_size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+
+        ret = load_image_targphys(filename, 0x00000000, bios_size);
+        if (ret < 0 || ret > prom_size) {
+            fprintf(stderr, "qemu: could not load prom '%s'\n", filename);
+            exit(1);
+        }
+    }
+    else if (kernel_filename == NULL) {
+        fprintf(stderr,"Can't read bios image %s\n", filename);
+        exit(1);
+    }
+
+    /* Can directly load an application. */
+    if (kernel_filename != NULL) {
+        long     kernel_size;
+        uint64_t entry;
+
+        kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
+                               1 /* big endian */, ELF_MACHINE, 0);
+        if (kernel_size < 0) {
+            fprintf(stderr, "qemu: could not load kernel '%s'\n",
+                    kernel_filename);
+            exit(1);
+        }
+        if (bios_size <= 0) {
+            /* If there is no bios/monitor, start the application.  */
+            env->pc = entry;
+            env->npc = entry + 4;
+            leon3_state.entry = entry;
+        }
+    }
+
+    /* Allocate timers */
+    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
+
+    /* Allocate uart */
+    if (serial_hds[0])
+        grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
+}
+
+QEMUMachine leon3_generic_machine = {
+    .name     = "leon3_generic",
+    .desc     = "Leon-3 generic",
+    .init     = leon3_generic_hw_init,
+    .use_scsi = 0,
+};
+
+static void leon3_machine_init(void)
+{
+    qemu_register_machine(&leon3_generic_machine);
+}
+
+machine_init(leon3_machine_init);
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index 7e0d17c..6020ffd 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
 /* sun4m.c, sun4u.c */
 void cpu_check_irqs(CPUSPARCState *env);
 
+/* grlib_irqmp.c */
+void grlib_irqmp_ack(CPUSPARCState *env, int intno);
+
+/* leon3.c */
+void     leon3_shutdown(void);
+void     leon3_cache_control_st(target_ulong addr, uint64_t val, int size);
+uint64_t leon3_cache_control_ld(target_ulong addr, int size);
+void     leon3_cache_control_int(void);
+
+
 #if defined (TARGET_SPARC64)
 
 static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask)
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
index e84c312..3bf990f 100644
--- a/target-sparc/helper.c
+++ b/target-sparc/helper.c
@@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
         .iu_version = 0xf3000000,
         .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
         .mmu_version = 0xf3000000,
-        .mmu_bm = 0x00004000,
+        .mmu_bm = 0x00000000,
         .mmu_ctpr_mask = 0x007ffff0,
         .mmu_cxr_mask = 0x0000003f,
         .mmu_sfsr_mask = 0xffffffff,
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
index be3c1e0..85df077 100644
--- a/target-sparc/op_helper.c
+++ b/target-sparc/op_helper.c
@@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
 
     helper_check_align(addr, size - 1);
     switch (asi) {
-    case 2: /* SuperSparc MXCC registers */
+    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
         switch (addr) {
+        case 0x00:          /* Leon3 Cache Control */
+        case 0x08:          /* Leon3 Instruction Cache config */
+        case 0x0C:          /* Leon3 Date Cache config */
+            ret = leon3_cache_control_ld(addr, size);
+            break;
         case 0x01c00a00: /* MXCC control register */
             if (size == 8)
                 ret = env->mxccregs[3];
@@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size)
 {
     helper_check_align(addr, size - 1);
     switch(asi) {
-    case 2: /* SuperSparc MXCC registers */
+    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
         switch (addr) {
+        case 0x00:          /* Leon3 Cache Control */
+        case 0x08:          /* Leon3 Instruction Cache config */
+        case 0x0C:          /* Leon3 Date Cache config */
+            leon3_cache_control_st(addr, val, size);
+            break;
+
         case 0x01c00000: /* MXCC stream data register 0 */
             if (size == 8)
                 env->mxccdata[0] = val;
@@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
 {
     int cwp, intno = env->exception_index;
 
+#if !defined(CONFIG_USER_ONLY)
+    /* Leon3 shutdown */
+    if (intno == 0x80 && env->version == 0xf3000000) {
+        leon3_shutdown();
+    }
+#endif
+
 #ifdef DEBUG_PCALL
     if (qemu_loglevel_mask(CPU_LOG_INT)) {
         static int count;
@@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
     env->pc = env->tbr;
     env->npc = env->pc + 4;
     env->exception_index = -1;
+
+#if !defined(CONFIG_USER_ONLY)
+    /* IRQ acknowledgment for Leon3 */
+    if (env->version == 0xf3000000 && (intno & ~15) == TT_EXTINT) {
+        grlib_irqmp_ack (env, intno);
+        leon3_cache_control_int();
+    }
+#endif
 }
 #endif
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support.
  2010-12-06  9:26         ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Fabien Chouteau
@ 2010-12-06  9:26           ` Fabien Chouteau
  2010-12-06 18:01             ` Blue Swirl
  2010-12-06 17:53           ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Blue Swirl
  2010-12-12 14:41           ` Andreas Färber
  2 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06  9:26 UTC (permalink / raw)
  To: qemu-devel; +Cc: Fabien Chouteau


Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 hw/leon3.c               |    6 ++++++
 target-sparc/cpu.h       |    1 +
 target-sparc/machine.c   |    2 ++
 target-sparc/translate.c |   10 ++++++++++
 4 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/hw/leon3.c b/hw/leon3.c
index ba61081..9605ce8 100644
--- a/hw/leon3.c
+++ b/hw/leon3.c
@@ -187,6 +187,12 @@ static void main_cpu_reset(void *opaque)
        values */
     leon3_state.inst_cache_conf = 0x10220000;
     leon3_state.data_cache_conf = 0x18220000;
+
+    /* Asr17 for Leon3 mono-processor */
+    env->asr17 &= 0 << 28;          /* CPU id */
+    env->asr17 &= 1 << 8;           /* SPARC V8 multiply and divide available */
+    env->asr17 &= env->nwindows -1; /* Number of implemented registers
+                                       windows */
 }
 
 static void leon3_generic_hw_init(ram_addr_t  ram_size,
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index 6020ffd..36d49fc 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -341,6 +341,7 @@ typedef struct CPUSPARCState {
                           from PSR) */
 #if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
     uint32_t wim;      /* window invalid mask */
+    uint32_t asr17;    /* asr17 */
 #endif
     target_ulong tbr;  /* trap base register */
 #if !defined(TARGET_SPARC64)
diff --git a/target-sparc/machine.c b/target-sparc/machine.c
index 752e431..c530bd3 100644
--- a/target-sparc/machine.c
+++ b/target-sparc/machine.c
@@ -42,6 +42,7 @@ void cpu_save(QEMUFile *f, void *opaque)
     qemu_put_be32s(f, &env->pil_in);
 #ifndef TARGET_SPARC64
     qemu_put_be32s(f, &env->wim);
+    qemu_put_be32s(f, &env->asr17);
     /* MMU */
     for (i = 0; i < 32; i++)
         qemu_put_be32s(f, &env->mmuregs[i]);
@@ -138,6 +139,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     qemu_get_be32s(f, &env->pil_in);
 #ifndef TARGET_SPARC64
     qemu_get_be32s(f, &env->wim);
+    qemu_get_be32s(f, &env->asr17);
     /* MMU */
     for (i = 0; i < 32; i++)
         qemu_get_be32s(f, &env->mmuregs[i]);
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index 23f9519..65de614 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -58,6 +58,7 @@ static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver;
 static TCGv_i32 cpu_softint;
 #else
 static TCGv cpu_wim;
+static TCGv cpu_asr17;
 #endif
 /* local register indexes (only used inside old micro ops) */
 static TCGv cpu_tmp0;
@@ -2049,6 +2050,8 @@ static void disas_sparc_insn(DisasContext * dc)
                 rs1 = GET_FIELD(insn, 13, 17);
                 switch(rs1) {
                 case 0: /* rdy */
+                    gen_movl_TN_reg(rd, cpu_y);
+                    break;
 #ifndef TARGET_SPARC64
                 case 0x01 ... 0x0e: /* undefined in the SPARCv8
                                        manual, rdy on the microSPARC
@@ -2058,6 +2061,11 @@ static void disas_sparc_insn(DisasContext * dc)
                 case 0x10 ... 0x1f: /* implementation-dependent in the
                                        SPARCv8 manual, rdy on the
                                        microSPARC II */
+
+                    if (rs1 == 0x11) { /* Read %asr17 */
+                        gen_movl_TN_reg(rd, cpu_asr17);
+                        break;
+                    }
 #endif
                     gen_movl_TN_reg(rd, cpu_y);
                     break;
@@ -5019,6 +5027,8 @@ void gen_intermediate_code_init(CPUSPARCState *env)
 #else
         cpu_wim = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, wim),
                                      "wim");
+        cpu_asr17 = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, asr17),
+                                     "asr17");
 #endif
         cpu_cond = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cond),
                                       "cond");
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3
  2010-12-06  9:26 [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Fabien Chouteau
  2010-12-06  9:26 ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual Fabien Chouteau
@ 2010-12-06 10:44 ` Artyom Tarasenko
  2010-12-06 15:07   ` Fabien Chouteau
  2010-12-06 18:05 ` Blue Swirl
  2 siblings, 1 reply; 51+ messages in thread
From: Artyom Tarasenko @ 2010-12-06 10:44 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 10:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> Hi everyone,
> I'm glad to submit my first patches to the Qemu-devel list.
>
> This patch set introduces a new SPARC V8 machine: Leon3. It's an open-source
> VHDL System-On-Chip, well known in space industry (more information on
> http://www.gaisler.com).

Nice! Haven't looked into the code yet, but it's great to have someone
who cares for V8.
Do you also have a firmware which runs on these machines?

> Leon3 is made of multiple components available in the GrLib VHDL library.
> Three devices are implemented: uart, timers and IRQ manager.
> You can find code for these peripherals in the grlib_* files.
>
> Modifications have been done to the SPARC cpu emulation code to handle
> Leon3's specific behavior:
>  - IRQ management
>  - Cache control
>  - Asr17 (implementation-dependent Ancillary State Registers)

Is it the only implementation-dependent asr in Leon3? Thought there were more.

>  - Shutdown
>
> Please feel free to comment.
>
> Regards,
>
> Fabien Chouteau (6):
>  Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
>  Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
>  Emulation of GRLIB APB UART as defined in GRLIB IP Core User's
>    Manual.
>  Header file for the GRLIB components.
>  Emulation of Leon3.
>  SPARCV8 asr17 register support.
>
>  Makefile.target          |    5 +-
>  hw/grlib.h               |   27 +++
>  hw/grlib_apbuart.c       |  231 ++++++++++++++++++++++++
>  hw/grlib_gptimer.c       |  448 ++++++++++++++++++++++++++++++++++++++++++++++
>  hw/grlib_irqmp.c         |  416 ++++++++++++++++++++++++++++++++++++++++++
>  hw/leon3.c               |  316 ++++++++++++++++++++++++++++++++
>  target-sparc/cpu.h       |   11 ++
>  target-sparc/helper.c    |    2 +-
>  target-sparc/machine.c   |    2 +
>  target-sparc/op_helper.c |   30 +++-
>  target-sparc/translate.c |   10 +
>  11 files changed, 1494 insertions(+), 4 deletions(-)
>  create mode 100644 hw/grlib.h
>  create mode 100644 hw/grlib_apbuart.c
>  create mode 100644 hw/grlib_gptimer.c
>  create mode 100644 hw/grlib_irqmp.c
>  create mode 100644 hw/leon3.c
>


-- 
Regards,
Artyom Tarasenko

solaris/sparc under qemu blog: http://tyom.blogspot.com/

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3
  2010-12-06 10:44 ` [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Artyom Tarasenko
@ 2010-12-06 15:07   ` Fabien Chouteau
  2010-12-06 18:12     ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-06 15:07 UTC (permalink / raw)
  To: Artyom Tarasenko; +Cc: qemu-devel

On 12/06/2010 11:44 AM, Artyom Tarasenko wrote:
> On Mon, Dec 6, 2010 at 10:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> Hi everyone,
>> I'm glad to submit my first patches to the Qemu-devel list.
>>
>> This patch set introduces a new SPARC V8 machine: Leon3. It's an open-source
>> VHDL System-On-Chip, well known in space industry (more information on
>> http://www.gaisler.com).
> Nice! Haven't looked into the code yet, but it's great to have someone
> who cares for V8.

And if this patch is accepted, we will try to submit more machines like 
erc32 and leon2.

> Do you also have a firmware which runs on these machines?
>

I can give you a binary running some basic tests.

>> Leon3 is made of multiple components available in the GrLib VHDL library.
>> Three devices are implemented: uart, timers and IRQ manager.
>> You can find code for these peripherals in the grlib_* files.
>>
>> Modifications have been done to the SPARC cpu emulation code to handle
>> Leon3's specific behavior:
>>   - IRQ management
>>   - Cache control
>>   - Asr17 (implementation-dependent Ancillary State Registers)
> Is it the only implementation-dependent asr in Leon3? Thought there were more.
>

Yes, there's also asr19 for power-down, asr16 for FPU control and others 
for hardware breakpoints.
These are not required for this first implementation, but If there's a 
need for more ASRs, we may have to find a generic implementation to 
handle those registers.

Regards,

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26 ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual Fabien Chouteau
  2010-12-06  9:26   ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Fabien Chouteau
@ 2010-12-06 17:12   ` Blue Swirl
  2010-12-07  9:55     ` Fabien Chouteau
  2010-12-08 22:51   ` Edgar E. Iglesias
  2 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 17:12 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 448 insertions(+), 0 deletions(-)
>
> diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
> new file mode 100644
> index 0000000..41edbe4
> --- /dev/null
> +++ b/hw/grlib_gptimer.c
> @@ -0,0 +1,448 @@
> +/*
> + * QEMU GRLIB GPTimer Emulator
> + *
> + * Copyright (c) 2010 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "qemu-timer.h"
> +
> +#include "grlib.h"
> +
> +/* #define DEBUG_TIMER */

The usual convention is
//#define DEBUG_TIMER
for easy editing.

However, very often the much more powerful tracepoints can replace
debug statements.

> +
> +#ifdef DEBUG_TIMER
> +#define DPRINTF(fmt, ...)                                       \
> +    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...)
> +#endif
> +
> +#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
> +#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
> +
> +#define GPTIMER_MAX_TIMERS 8
> +
> +/* GPTimer Config register fields */
> +#define GPTIMER_ENABLE      (1 << 0)
> +#define GPTIMER_RESTART     (1 << 1)
> +#define GPTIMER_LOAD        (1 << 2)
> +#define GPTIMER_INT_ENABLE  (1 << 3)
> +#define GPTIMER_INT_PENDING (1 << 4)
> +#define GPTIMER_CHAIN       (1 << 5) /* Not supported */
> +#define GPTIMER_DEBUG_HALT  (1 << 6) /* Not supported */
> +
> +/* Memory mapped register offsets */
> +#define SCALER_OFFSET         0x00
> +#define SCALER_RELOAD_OFFSET  0x04
> +#define CONFIG_OFFSET         0x08
> +#define COUNTER_OFFSET        0x00
> +#define COUNTER_RELOAD_OFFSET 0x04
> +#define TIMER_BASE            0x10
> +
> +typedef struct GPTimer     GPTimer;
> +typedef struct GPTimerUnit GPTimerUnit;
> +
> +struct GPTimer
> +{
> +    QEMUBH *bh;
> +    struct ptimer_state *ptimer;
> +
> +    qemu_irq     irq;
> +    int          id;
> +    GPTimerUnit *unit;
> +
> +    /* registers */
> +    uint32_t counter;
> +    uint32_t reload;
> +    uint32_t config;
> +};
> +
> +struct GPTimerUnit
> +{
> +    SysBusDevice  busdev;
> +
> +    uint32_t nr_timers;         /* Number of timers available */
> +    uint32_t freq_hz;           /* System frequency */
> +    uint32_t irq_line;          /* Base irq line */
> +
> +    GPTimer *timers;
> +
> +    /* registers */
> +    uint32_t scaler;
> +    uint32_t reload;
> +    uint32_t config;
> +};
> +
> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
> +                                  uint32_t            nr_timers,
> +                                  uint32_t            freq,
> +                                  qemu_irq           *cpu_irqs,
> +                                  int                 base_irq)

This function belongs to leon3.c.

> +{
> +    DeviceState *dev;
> +    int i;
> +
> +    dev = qdev_create(NULL, "grlib,gptimer");
> +    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
> +    qdev_prop_set_uint32(dev, "frequency", freq);
> +    qdev_prop_set_uint32(dev, "irq-line", base_irq);

Base irq is not device property, but part of board configuration. Thus
leon3.c should just pass &cpu_irqs[base_irq] to this function.

> +
> +    if (qdev_init(dev)) {
> +        return NULL;
> +    }
> +
> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> +
> +    for (i = 0; i < nr_timers; i++)
> +        sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
> +
> +    return dev;
> +}
> +
> +static void grlib_gptimer_enable(GPTimer *timer)
> +{
> +    assert(timer != NULL);
> +
> +    DPRINTF("%s id:%d\n", __func__, timer->id);
> +
> +    ptimer_stop(timer->ptimer);
> +
> +    if (!(timer->config & GPTIMER_ENABLE)) {
> +        /* Timer disabled */
> +        DPRINTF("%s id:%d Timer disabled (config 0x%x)\n", __func__,
> +                timer->id, timer->config);
> +        return;
> +    }
> +
> +    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
> +       underflow. Set count + 1 to simulate the GPTimer behavior. */
> +
> +    DPRINTF("%s id:%d set count 0x%x and run\n",
> +            __func__,
> +            timer->id,
> +            timer->counter + 1);
> +
> +    ptimer_set_count(timer->ptimer, timer->counter + 1);
> +    ptimer_run(timer->ptimer, 1);
> +}
> +
> +static void grlib_gptimer_restart(GPTimer *timer)
> +{
> +    assert(timer != NULL);
> +
> +    DPRINTF("%s id:%d reload val: 0x%x\n", __func__, timer->id, timer->reload);
> +
> +    timer->counter = timer->reload;
> +    grlib_gptimer_enable(timer);
> +}
> +
> +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
> +{
> +    int i = 0;
> +    uint32_t value = 0;
> +
> +    assert(unit != NULL);
> +
> +
> +    if (scaler > 0) {
> +        value = unit->freq_hz / (scaler + 1);
> +    } else {
> +        value = unit->freq_hz;
> +    }
> +
> +    DPRINTF("%s scaler:%d freq:0x%x\n", __func__, scaler, value);
> +
> +    for (i = 0; i < unit->nr_timers; i++) {
> +        ptimer_set_freq(unit->timers[i].ptimer, value);
> +    }
> +}
> +
> +static void grlib_gptimer_hit(void *opaque)
> +{
> +    GPTimer *timer = opaque;
> +    assert(timer != NULL);
> +
> +    DPRINTF("%s id:%d\n", __func__, timer->id);
> +
> +    /* Timer expired */
> +
> +    if (timer->config & GPTIMER_INT_ENABLE) {
> +        /* Set the pending bit (only unset by write in the config register) */
> +        timer->config &= GPTIMER_INT_PENDING;
> +        qemu_set_irq(timer->irq, 1);
> +    }
> +
> +    if (timer->config & GPTIMER_RESTART) {
> +        grlib_gptimer_restart(timer);
> +    }
> +}
> +
> +static uint32_t grlib_gptimer_readl (void *opaque, target_phys_addr_t addr)

Extra space between function name and its arguments.

> +{
> +    GPTimerUnit *unit  = opaque;
> +    uint32_t     value = 0;
> +
> +    addr &= 0xff;
> +
> +    assert(unit != NULL);
> +
> +    /* Unit registers */
> +    switch (addr)
> +    {
> +        case SCALER_OFFSET:
> +            DPRINTF("%s scaler: 0x%x\n", __func__, unit->scaler);
> +            return unit->scaler;
> +
> +        case SCALER_RELOAD_OFFSET:
> +            DPRINTF("%s reload: 0x%x\n", __func__, unit->reload);
> +            return unit->reload;
> +
> +        case CONFIG_OFFSET:
> +            DPRINTF("%s unit config: 0x%x\n", __func__, unit->config);
> +            return unit->config;
> +
> +        default:
> +            break;
> +    }
> +
> +    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
> +    int                id         = (addr - TIMER_BASE) / TIMER_BASE;

Variables should be declared at the beginning of the function. But I'd
actually make two functions and use the first one for the general
registers above and second one for the timer registers below. Then the
opaque passed to the latter could be GPTimer instead of GPTimerUnit.

> +
> +    if (id >= 0 && id < unit->nr_timers) {
> +
> +        /* GPTimer registers */
> +        switch (timer_addr)
> +        {
> +            case COUNTER_OFFSET:
> +                value = ptimer_get_count (unit->timers[id].ptimer);
> +                DPRINTF("%s counter value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                return value;
> +
> +            case COUNTER_RELOAD_OFFSET:
> +                value = unit->timers[id].reload;
> +                DPRINTF("%s reload value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                return value;
> +
> +            case CONFIG_OFFSET:
> +                DPRINTF("%s config for timer %d: 0x%x\n",
> +                        __func__, id, unit->timers[id].config);
> +                return unit->timers[id].config;
> +
> +            default:
> +                break;
> +        }
> +
> +    }
> +
> +    DPRINTF("read unknown register 0x%04x\n", (int)addr);
> +    return 0;
> +}
> +
> +static void
> +grlib_gptimer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
> +{
> +    GPTimerUnit *unit = opaque;
> +
> +    addr &= 0xff;
> +
> +    assert(unit != NULL);
> +
> +    /* Unit registers */
> +    switch (addr)
> +    {
> +        case SCALER_OFFSET:
> +            value &= 0xFFFF; /* clean up the value */
> +            unit->scaler = value;
> +            return;
> +
> +        case SCALER_RELOAD_OFFSET:
> +            value &= 0xFFFF; /* clean up the value */
> +            unit->reload = value;
> +            grlib_gptimer_set_scaler(unit, value);
> +            return;
> +
> +        case CONFIG_OFFSET:
> +            /* Read Only (disable timer freeze not supported) */
> +            return;
> +
> +        default:
> +            break;
> +    }
> +
> +    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
> +    int                id         = (addr - TIMER_BASE) / TIMER_BASE;
> +
> +    if (id >= 0 && id < unit->nr_timers) {
> +
> +        /* GPTimer registers */
> +        switch (timer_addr)
> +        {
> +            case COUNTER_OFFSET:
> +                DPRINTF("%s counter value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                unit->timers[id].counter = value;
> +                grlib_gptimer_enable(&unit->timers[id]);
> +                return;
> +
> +            case COUNTER_RELOAD_OFFSET:
> +                DPRINTF("%s reload value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                unit->timers[id].reload = value;
> +                return;
> +
> +            case CONFIG_OFFSET:
> +                DPRINTF("%s config for timer %d: 0x%x\n", __func__, id, value);
> +
> +                unit->timers[id].config = value;
> +
> +                /* gptimer_restart calls gptimer_enable, so if "enable" and
> +                   "load" bits are present, we just have to call restart. */
> +
> +                if (value & GPTIMER_LOAD) {
> +                    grlib_gptimer_restart(&unit->timers[id]);
> +                } else if (value & GPTIMER_ENABLE) {
> +                    grlib_gptimer_enable(&unit->timers[id]);
> +                }
> +
> +                /* This fields must always be read as 0 */
> +                value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
> +
> +                unit->timers[id].config = value;
> +                return;
> +
> +            default:
> +                break;
> +        }
> +
> +    }
> +
> +    DPRINTF("write unknown register 0x%04x\n", (int)addr);
> +}
> +
> +static CPUReadMemoryFunc *grlib_gptimer_read[] = {

'const'

> +    NULL, NULL, grlib_gptimer_readl,
> +};
> +
> +static CPUWriteMemoryFunc *grlib_gptimer_write[] = {
> +    NULL, NULL, grlib_gptimer_writel,
> +};
> +
> +static void grlib_gptimer_reset(void *opaque)
> +{
> +    /* int          i    = 0; */
> +    /* GPTimerUnit *unit = (GPTimerUnit *)opaque; */
> +    /* assert(unit != NULL); */
> +
> +    /* unit->scaler = 0; */
> +    /* unit->reload = 0; */
> +    /* unit->config = 0; */
> +
> +    /* unit->config  = unit->nr_timers; */
> +    /* unit->config |= unit->irq_line << 3; */
> +    /* unit->config |= 1 << 8;     /\* separate interrupt *\/ */
> +    /* unit->config |= 1 << 9;     /\* Disable timer freeze *\/ */
> +
> +
> +    /* for (i = 0; i < unit->nr_timers; i++) { */
> +    /*     GPTimer *timer = &unit->timers[i]; */
> +
> +    /*     timer->counter = 0; */
> +    /*     timer->reload = 0; */
> +    /*     timer->config = 0; */
> +    /*     ptimer_stop(timer->ptimer); */
> +    /*     ptimer_set_count(timer->ptimer, 0); */
> +    /*     ptimer_set_freq(timer->ptimer, unit->freq_hz); */
> +    /* } */

Why dead code?

> +
> +}
> +
> +static int grlib_gptimer_init(SysBusDevice *dev)
> +{
> +    GPTimerUnit  *unit = FROM_SYSBUS(typeof (*unit), dev);
> +    unsigned int  i;
> +    int           timer_regs;
> +
> +    assert(unit->nr_timers > 0);
> +    assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
> +    unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers);
> +
> +    for (i = 0; i < unit->nr_timers; i++) {
> +        GPTimer *timer = &unit->timers[i];
> +
> +        timer->unit   = unit;
> +        timer->bh     = qemu_bh_new(grlib_gptimer_hit, timer);
> +        timer->ptimer = ptimer_init(timer->bh);
> +        timer->id     = i;
> +
> +        /* One IRQ line for each timer */
> +        sysbus_init_irq(dev, &timer->irq);
> +
> +        ptimer_set_freq(timer->ptimer, unit->freq_hz);
> +    }
> +
> +    qemu_register_reset(grlib_gptimer_reset, unit);

This can be replaced by qdev.reset field.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26   ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Fabien Chouteau
  2010-12-06  9:26     ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART " Fabien Chouteau
@ 2010-12-06 17:25     ` Blue Swirl
  2010-12-07 10:43       ` Fabien Chouteau
  2010-12-09 10:32     ` Edgar E. Iglesias
  2 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 17:25 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  hw/grlib_irqmp.c |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 416 insertions(+), 0 deletions(-)
>
> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
> new file mode 100644
> index 0000000..69e1553
> --- /dev/null
> +++ b/hw/grlib_irqmp.c
> @@ -0,0 +1,416 @@
> +/*
> + * QEMU GRLIB IRQMP Emulator
> + *
> + * (Multiprocessor and extended interrupt not supported)
> + *
> + * Copyright (c) 2010 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "cpu.h"
> +
> +#include "grlib.h"
> +
> +/* #define DEBUG_IRQ */
> +
> +#ifdef DEBUG_IRQ
> +#define DPRINTF(fmt, ...)                                       \
> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...)
> +#endif
> +
> +#define IRQMP_MAX_CPU 16
> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
> +
> +/* Memory mapped register offsets */
> +#define LEVEL_OFFSET     0x00
> +#define PENDING_OFFSET   0x04
> +#define FORCE0_OFFSET    0x08
> +#define CLEAR_OFFSET     0x0C
> +#define MP_STATUS_OFFSET 0x10
> +#define BROADCAST_OFFSET 0x14
> +#define MASK_OFFSET      0x40
> +#define FORCE_OFFSET     0x80
> +#define EXTENDED_OFFSET  0xC0
> +
> +typedef struct IRQMP
> +{
> +    SysBusDevice busdev;
> +
> +    CPUSPARCState *env;

Devices should never access CPUState directly. Instead, board level
should create CPU irqs and these should then be passed here.

> +} IRQMP;
> +
> +typedef struct IRQMPState
> +{
> +    uint32_t level;
> +    uint32_t pending;
> +    uint32_t clear;
> +    uint32_t broadcast;
> +
> +    uint32_t mask[IRQMP_MAX_CPU];
> +    uint32_t force[IRQMP_MAX_CPU];
> +    uint32_t extended[IRQMP_MAX_CPU];
> +
> +    IRQMP    *parent;
> +} IRQMPState;
> +
> +IRQMPState grlib_irqmp_state;

Global state indicates poor design. Why separate IRQMP and IRQMPState?

> +
> +void grlib_irqmp_set_irq(void *opaque, int irq, int level);

This should not be global. Again, creating qemu_irqs or moving some of
the code to board level should help.

> +
> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
> +                                CPUState            *env,
> +                                qemu_irq           **cpu_irqs,
> +                                uint32_t             nr_irqs)
> +{
> +    DeviceState *dev;
> +
> +    assert(cpu_irqs != NULL);
> +
> +    dev = qdev_create(NULL, "grlib,irqmp");
> +    qdev_prop_set_ptr(dev, "cpustate", env);
> +
> +    if (qdev_init(dev)) {
> +        return NULL;
> +    }
> +
> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> +
> +    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
> +                                   &grlib_irqmp_state,
> +                                   nr_irqs);
> +
> +    return dev;
> +}
> +
> +static void grlib_irqmp_check_irqs(CPUState *env)
> +{
> +    uint32_t pend   = 0;
> +    uint32_t level0 = 0;
> +    uint32_t level1 = 0;
> +
> +    assert(env != NULL);
> +
> +    /* IRQ for CPU 0 (no SMP support) */
> +    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
> +        & grlib_irqmp_state.mask[0];
> +
> +
> +    level0 = pend & ~grlib_irqmp_state.level;
> +    level1 = pend &  grlib_irqmp_state.level;
> +
> +    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n",
> +            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
> +            grlib_irqmp_state.mask[0], level1, level0);

The above should stay here, but code below should to go to board level
(leon3.c). Then you need to separate device IRQ handling from CPU PIL
handling.

> +
> +    /* Trigger level1 interrupt first and level0 if there is no level1 */
> +    if (level1 != 0) {
> +        env->pil_in = level1;
> +    } else {
> +        env->pil_in = level0;
> +    }
> +
> +    if (env->pil_in && (env->interrupt_index == 0 ||
> +                        (env->interrupt_index & ~15) == TT_EXTINT)) {
> +        unsigned int i;
> +
> +        for (i = 15; i > 0; i--) {
> +            if (env->pil_in & (1 << i)) {
> +                int old_interrupt = env->interrupt_index;
> +
> +                env->interrupt_index = TT_EXTINT | i;
> +                if (old_interrupt != env->interrupt_index) {
> +                    DPRINTF("Set CPU IRQ %d\n", i);
> +                    cpu_interrupt(env, CPU_INTERRUPT_HARD);
> +                }
> +                break;
> +            }
> +        }
> +    } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) {
> +        DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index & 15);
> +        env->interrupt_index = 0;
> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
> +    }
> +}
> +
> +void grlib_irqmp_ack(CPUSPARCState *env, int intno)
> +{
> +    assert(env != NULL);
> +
> +    uint32_t mask;
> +
> +    intno &= 15;
> +    mask = 1 << intno;
> +
> +    DPRINTF("grlib_irqmp_ack %d\n", intno);
> +
> +    /* Clear registers */
> +    grlib_irqmp_state.pending  &= ~mask;
> +    grlib_irqmp_state.force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
> +
> +    grlib_irqmp_check_irqs(env);
> +}
> +
> +void grlib_irqmp_set_irq(void *opaque, int irq, int level)
> +{
> +    IRQMPState *s = opaque;
> +    int         i = 0;
> +
> +    assert(opaque != NULL);
> +    assert(s->parent != NULL);
> +
> +    if (level) {
> +        DPRINTF("Raise CPU IRQ %d\n", irq);
> +
> +        if (s->broadcast & 1 << irq) {
> +            /* Broadcasted IRQ */
> +            for (i = 0; i < IRQMP_MAX_CPU; i++) {
> +                s->force[i] |= 1 << irq;
> +            }
> +        } else {
> +            s->pending |= 1 << irq;
> +        }
> +        grlib_irqmp_check_irqs(s->parent->env);
> +
> +    } else {
> +
> +        DPRINTF("Lower CPU IRQ %d\n", irq);
> +        if (s->broadcast & 1 << irq) {
> +            /* Broadcasted IRQ */
> +            for (i = 0; i < IRQMP_MAX_CPU; i++) {
> +                s->force[i] &= ~(1 << irq);
> +            }
> +        } else {
> +            s->pending &= ~(1 << irq);
> +        }
> +        grlib_irqmp_check_irqs(s->parent->env);
> +    }
> +}
> +
> +static uint32_t grlib_irqmp_readl (void *opaque, target_phys_addr_t addr)
> +{
> +    IRQMP *irqmp = opaque;
> +
> +    assert(irqmp != NULL);
> +
> +    addr &= 0xff;
> +
> +    /* global registers */
> +    switch (addr)
> +    {
> +        case LEVEL_OFFSET:
> +            return grlib_irqmp_state.level;
> +
> +        case PENDING_OFFSET:
> +            return grlib_irqmp_state.pending;
> +
> +        case FORCE0_OFFSET:
> +            /* This register is an "alias" for the force register of CPU 0 */
> +            return grlib_irqmp_state.force[0];
> +
> +        case CLEAR_OFFSET:
> +        case MP_STATUS_OFFSET:
> +            /* Always read as 0 */
> +            return 0;
> +
> +        case BROADCAST_OFFSET:
> +            return grlib_irqmp_state.broadcast;
> +
> +        default:
> +            break;
> +    }
> +
> +    /* mask registers */
> +    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
> +        int cpu = (addr - MASK_OFFSET) / 4;
> +        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
> +
> +        return grlib_irqmp_state.mask[cpu] ;
> +    }
> +
> +    /* force registers */
> +    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
> +        int cpu = (addr - FORCE_OFFSET) / 4;
> +        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
> +
> +        return grlib_irqmp_state.force[cpu];
> +    }
> +
> +    /* extended (not supported) */
> +    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
> +        int cpu = (addr - EXTENDED_OFFSET) / 4;
> +        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
> +
> +        return grlib_irqmp_state.extended[cpu];
> +    }
> +
> +    DPRINTF("read unknown register 0x%04x\n", (int)addr);
> +    return 0;
> +}
> +
> +static void
> +grlib_irqmp_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
> +{
> +    IRQMP *irqmp = opaque;
> +
> +    assert(irqmp != NULL);
> +
> +    addr &= 0xff;
> +
> +    /* global registers */
> +    switch (addr)
> +    {
> +        case LEVEL_OFFSET:
> +            value &= 0xFFFF << 1; /* clean up the value */
> +            grlib_irqmp_state.level = value;
> +            return;
> +
> +        case PENDING_OFFSET:
> +            /* Read Only */
> +            return;
> +
> +        case FORCE0_OFFSET:
> +            /* This register is an "alias" for the force register of CPU 0 */
> +
> +            value &= 0xFFFE; /* clean up the value */
> +            grlib_irqmp_state.force[0] = value;
> +            grlib_irqmp_check_irqs(irqmp->env);
> +            return;
> +
> +        case CLEAR_OFFSET:
> +            value &= ~1; /* clean up the value */
> +            grlib_irqmp_state.pending &= ~value;
> +            return;
> +
> +        case MP_STATUS_OFFSET:
> +            /* Read Only (no SMP support) */
> +            return;
> +
> +        case BROADCAST_OFFSET:
> +            value &= 0xFFFE; /* clean up the value */
> +            grlib_irqmp_state.broadcast = value;
> +            return;
> +
> +        default:
> +            break;
> +    }
> +
> +    /* mask registers */
> +    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
> +        int cpu = (addr - MASK_OFFSET) / 4;
> +        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
> +
> +        value &= ~1; /* clean up the value */
> +        grlib_irqmp_state.mask[cpu] = value;
> +        grlib_irqmp_check_irqs(irqmp->env);
> +        return;
> +    }
> +
> +    /* force registers */
> +    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
> +        int cpu = (addr - FORCE_OFFSET) / 4;
> +        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
> +
> +        uint32_t force = value & 0xFFFE;
> +        uint32_t clear = (value >> 16) & 0xFFFE;
> +        uint32_t old   = grlib_irqmp_state.force[cpu];
> +
> +        grlib_irqmp_state.force[cpu] = (old | force) & ~clear;
> +        grlib_irqmp_check_irqs(irqmp->env);
> +        return;
> +    }
> +
> +    /* extended (not supported) */
> +    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
> +        int cpu = (addr - EXTENDED_OFFSET) / 4;
> +        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
> +
> +        value &= 0xF; /* clean up the value */
> +        grlib_irqmp_state.extended[cpu] = value;
> +        return;
> +    }
> +
> +    DPRINTF("write unknown register 0x%04x\n", (int)addr);

Please use TARGET_FMT_plx, so the cast can be removed.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26     ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART " Fabien Chouteau
  2010-12-06  9:26       ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Fabien Chouteau
@ 2010-12-06 17:29       ` Blue Swirl
  2010-12-07 10:55         ` Fabien Chouteau
  1 sibling, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 17:29 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  hw/grlib_apbuart.c |  231 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 231 insertions(+), 0 deletions(-)
>
> diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c
> new file mode 100644
> index 0000000..32a5362
> --- /dev/null
> +++ b/hw/grlib_apbuart.c
> @@ -0,0 +1,231 @@
> +/*
> + * QEMU GRLIB APB UART Emulator
> + *
> + * Copyright (c) 2010 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "qemu-char.h"
> +
> +#include "grlib.h"
> +
> +/* #define DEBUG_UART */
> +
> +#ifdef DEBUG_UART
> +#define DPRINTF(fmt, ...)                                       \
> +    do { printf("APBUART: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...)
> +#endif
> +
> +#define UART_REG_SIZE 20     /* Size of memory mapped registers */
> +
> +/* UART status register fields */
> +#define UART_DATA_READY           (1 <<  0)
> +#define UART_TRANSMIT_SHIFT_EMPTY (1 <<  1)
> +#define UART_TRANSMIT_FIFO_EMPTY  (1 <<  2)
> +#define UART_BREAK_RECEIVED       (1 <<  3)
> +#define UART_OVERRUN              (1 <<  4)
> +#define UART_PARITY_ERROR         (1 <<  5)
> +#define UART_FRAMING_ERROR        (1 <<  6)
> +#define UART_TRANSMIT_FIFO_HALF   (1 <<  7)
> +#define UART_RECEIV_FIFO_HALF     (1 <<  8)

RECEIVE

> +#define UART_TRANSMIT_FIFO_FULL   (1 <<  9)
> +#define UART_RECEIV_FIFO_FULL     (1 << 10)
> +
> +/* UART control register fields */
> +#define UART_RECEIV_ENABLE           (1 <<  0)
> +#define UART_TRANSMIT_ENABLE         (1 <<  1)
> +#define UART_RECEIV_INTERRUPT        (1 <<  2)
> +#define UART_TRANSMIT_INTERRUPT      (1 <<  3)
> +#define UART_PARITY_SELECT           (1 <<  4)
> +#define UART_PARITY_ENABLE           (1 <<  5)
> +#define UART_FLOW_CONTROL            (1 <<  6)
> +#define UART_LOOPBACK                (1 <<  7)
> +#define UART_EXTERNAL_CLOCK          (1 <<  8)
> +#define UART_RECEIV_FIFO_INTERRUPT   (1 <<  9)
> +#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10)
> +#define UART_FIFO_DEBUG_MODE         (1 << 11)
> +#define UART_OUTPUT_ENABLE           (1 << 12)
> +#define UART_FIFO_AVAILABLE          (1 << 31)
> +
> +/* Memory mapped register offsets */
> +#define DATA_OFFSET       0x00
> +#define STATUS_OFFSET     0x04
> +#define CONTROL_OFFSET    0x08
> +#define SCALER_OFFSET     0x0C  /* not supported */
> +#define FIFO_DEBUG_OFFSET 0x10  /* not supported */
> +
> +typedef struct UART
> +{
> +    SysBusDevice busdev;
> +
> +    qemu_irq irq;
> +
> +    CharDriverState *chr;
> +
> +    /* registers */
> +    uint32_t receive;
> +    uint32_t status;
> +    uint32_t control;
> +} UART;
> +
> +
> +DeviceState *grlib_apbuart_create(target_phys_addr_t  base,
> +                                  CharDriverState    *serial,
> +                                  qemu_irq            irq)
> +{
> +    DeviceState *dev;
> +
> +    dev = qdev_create(NULL, "grlib,apbuart");
> +    qdev_prop_set_ptr(dev, "chrdev", serial);
> +
> +    if (qdev_init(dev)) {
> +        return NULL;
> +    }
> +
> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> +
> +    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> +
> +    return dev;
> +}
> +
> +static int grlib_apbuart_can_receive(void *opaque)
> +{
> +    UART *uart = opaque;
> +    assert(uart != NULL);
> +
> +    return !!(uart->status & UART_DATA_READY);
> +}
> +
> +static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size)
> +{
> +    UART *uart = opaque;
> +    assert(uart != NULL);
> +
> +    uart->receive  = *buf;
> +    uart->status  |= UART_DATA_READY;
> +
> +    if (uart->control & UART_RECEIV_INTERRUPT)
> +        qemu_set_irq(uart->irq, 1);
> +}
> +
> +static void grlib_apbuart_event(void *opaque, int event)
> +{
> +#ifdef DEBUG_UART
> +    printf("uart: event %x\n", event);
> +#endif

DPRINTF or tracepoint.

> +}
> +
> +static void
> +grlib_apbuart_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
> +{
> +    UART          *uart = opaque;
> +    unsigned char  c    = 0;
> +
> +    addr &= 0xff;
> +
> +    assert(uart != NULL);
> +
> +    /* Unit registers */
> +    switch (addr)
> +    {
> +        case DATA_OFFSET:
> +            c = value & 0xFF;
> +            qemu_chr_write(uart->chr, &c, 1);
> +            return;
> +
> +        case STATUS_OFFSET:
> +            /* Read Only (disable timer freeze not supported) */
> +            return;
> +
> +        case CONTROL_OFFSET:
> +            return;
> +
> +        case SCALER_OFFSET:
> +            /* Not supported */
> +            return;
> +
> +        default:
> +            break;
> +    }
> +
> +    DPRINTF("write unknown register 0x%04x\n", (int)addr);
> +}
> +
> +static CPUReadMemoryFunc *grlib_apbuart_read[] = {
> +    NULL, NULL, NULL,
> +};
> +
> +static CPUWriteMemoryFunc *grlib_apbuart_write[] = {
> +    NULL, NULL, grlib_apbuart_writel,
> +};
> +
> +static int grlib_gptimer_init(SysBusDevice *dev)
> +{
> +    UART *uart      = FROM_SYSBUS(typeof (*uart), dev);
> +    int   uart_regs = 0;

Useless initialization.

Please consider also adding reset and savevm/loadvm support.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components.
  2010-12-06  9:26       ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Fabien Chouteau
  2010-12-06  9:26         ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Fabien Chouteau
@ 2010-12-06 17:31         ` Blue Swirl
  2010-12-07 11:04           ` Fabien Chouteau
  1 sibling, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 17:31 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  hw/grlib.h |   27 +++++++++++++++++++++++++++
>  1 files changed, 27 insertions(+), 0 deletions(-)
>
> diff --git a/hw/grlib.h b/hw/grlib.h
> new file mode 100644
> index 0000000..eab7974
> --- /dev/null
> +++ b/hw/grlib.h
> @@ -0,0 +1,27 @@
> +#ifndef _GRLIB_H_
> +#define _GRLIB_H_
> +
> +#include "qdev.h"
> +
> +/* IRQMP */
> +
> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
> +                                CPUState            *env,
> +                                qemu_irq           **cpu_irqs,
> +                                uint32_t             nr_irqs);
> +
> +/* GPTimer */
> +
> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
> +                                  uint32_t            nr_timers,
> +                                  uint32_t            freq,
> +                                  qemu_irq           *cpu_irqs,
> +                                  int                 base_irq);
> +
> +/* APB UART */
> +
> +DeviceState *grlib_apbuart_create(target_phys_addr_t  base,
> +                                  CharDriverState    *serial,
> +                                  qemu_irq            irq);
> +

The file should not be needed when you move the functions to leon3.c.

By the way, the patch series is now broken because earlier patches in
the series needed this file. This would break bisection which is bad.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-06  9:26         ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Fabien Chouteau
  2010-12-06  9:26           ` [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support Fabien Chouteau
@ 2010-12-06 17:53           ` Blue Swirl
  2010-12-07 11:40             ` Fabien Chouteau
  2010-12-12 14:41           ` Andreas Färber
  2 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 17:53 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  Makefile.target          |    5 +-
>  hw/leon3.c               |  310 ++++++++++++++++++++++++++++++++++++++++++++++
>  target-sparc/cpu.h       |   10 ++
>  target-sparc/helper.c    |    2 +-
>  target-sparc/op_helper.c |   30 ++++-
>  5 files changed, 353 insertions(+), 4 deletions(-)
>
> diff --git a/Makefile.target b/Makefile.target
> index 2800f47..f40e04f 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>  else
>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
> +
> +# GRLIB
> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>  endif
>
>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
> diff --git a/hw/leon3.c b/hw/leon3.c
> new file mode 100644
> index 0000000..ba61081
> --- /dev/null
> +++ b/hw/leon3.c
> @@ -0,0 +1,310 @@
> +/*
> + * QEMU Leon3 System Emulator
> + *
> + * Copyright (c) 2010 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "hw.h"
> +#include "qemu-timer.h"
> +#include "qemu-char.h"
> +#include "sysemu.h"
> +#include "boards.h"
> +#include "loader.h"
> +#include "elf.h"
> +
> +#include "grlib.h"
> +
> +/* #define DEBUG_LEON3 */
> +
> +#ifdef DEBUG_LEON3
> +#define DPRINTF(fmt, ...)                                       \
> +    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...)
> +#endif
> +
> +/* Default system clock.  */
> +#define CPU_CLK (40 * 1000 * 1000)
> +
> +#define PROM_FILENAME        "u-boot.bin"
> +
> +#define MAX_PILS 16
> +
> +typedef struct Leon3State
> +{
> +    uint32_t cache_control;
> +    uint32_t inst_cache_conf;
> +    uint32_t data_cache_conf;
> +
> +    uint64_t entry;             /* save kernel entry in case of reset */
> +} Leon3State;
> +
> +Leon3State leon3_state;

Again global state, please refactor. Perhaps most of the cache
handling code belong to target-sparc/op_helper.c and this structure to
CPUSPARCState.

> +
> +/* Cache control: emulate the behavior of cache control registers but without
> +   any effect on the emulated CPU */
> +
> +#define CACHE_DISABLED 0x0
> +#define CACHE_FROZEN   0x1
> +#define CACHE_ENABLED  0x3
> +
> +/* Cache Control register fields */
> +
> +#define CACHE_CTRL_IF (1 <<  4)  /* Instruction Cache Freeze on Interrupt */
> +#define CACHE_CTRL_DF (1 <<  5)  /* Data Cache Freeze on Interrupt */
> +#define CACHE_CTRL_DP (1 << 14)  /* Data cache flush pending */
> +#define CACHE_CTRL_IP (1 << 15)  /* Instruction cache flush pending */
> +#define CACHE_CTRL_IB (1 << 16)  /* Instruction burst fetch */
> +#define CACHE_CTRL_FI (1 << 21)  /* Flush Instruction cache (Write only) */
> +#define CACHE_CTRL_FD (1 << 22)  /* Flush Data cache (Write only) */
> +#define CACHE_CTRL_DS (1 << 23)  /* Data cache snoop enable */
> +
> +void leon3_cache_control_int(void)
> +{
> +    uint32_t state = 0;
> +
> +    if (leon3_state.cache_control & CACHE_CTRL_IF) {
> +        /* Instruction cache state */
> +        state = leon3_state.cache_control & 0x3;

Please add a new define CACHE_CTRL_xxx to replace 0x3.

> +        if (state == CACHE_ENABLED) {
> +            state = CACHE_FROZEN;
> +            DPRINTF("Instruction cache: freeze\n");
> +        }
> +
> +        leon3_state.cache_control &= ~0x3;
> +        leon3_state.cache_control |= state;
> +    }
> +
> +    if (leon3_state.cache_control & CACHE_CTRL_DF) {
> +        /* Data cache state */
> +        state = (leon3_state.cache_control >> 2) & 0x3;
> +        if (state == CACHE_ENABLED) {
> +            state = CACHE_FROZEN;
> +            DPRINTF("Data cache: freeze\n");
> +        }
> +
> +        leon3_state.cache_control &= ~(0x3 << 2);
> +        leon3_state.cache_control |= (state << 2);
> +    }
> +}
> +
> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
> +{
> +    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned int)addr,
> +            (unsigned int)val, size);

There's PRIx64 to print uint64_t portably, then the casts can be removed.

> +
> +    if (size != 4) {
> +        DPRINTF(" CC 32bits only\n");
> +        return;
> +    }
> +
> +    switch (addr) {
> +        case 0x00:              /* Cache control */
> +
> +            /* These values must always be read as zeros */
> +            val &= ~CACHE_CTRL_FD;
> +            val &= ~CACHE_CTRL_FI;
> +            val &= ~CACHE_CTRL_IB;
> +            val &= ~CACHE_CTRL_IP;
> +            val &= ~CACHE_CTRL_DP;
> +
> +            leon3_state.cache_control = val;
> +            break;
> +        case 0x04:              /* Instruction cache configuration */
> +        case 0x08:              /* Data cache configuration */
> +            /* Read Only */
> +            break;
> +        default:
> +            DPRINTF(" CC write unknown register 0x%04x\n", (int)addr);
> +            break;
> +    };
> +}
> +
> +uint64_t leon3_cache_control_ld(target_ulong addr, int size)
> +{
> +    uint64_t ret = 0;
> +
> +    if (size != 4) {
> +        DPRINTF(" CC 32bits only\n");
> +        return 0;
> +    }
> +
> +    switch (addr) {
> +        case 0x00:              /* Cache control */
> +            ret = leon3_state.cache_control;
> +            break;
> +        case 0x04:              /* Instruction cache configuration */
> +            ret = leon3_state.inst_cache_conf;
> +            break;
> +        case 0x08:              /* Data cache configuration */
> +            ret = leon3_state.data_cache_conf;
> +            break;
> +        default:
> +            DPRINTF(" CC read unknown register 0x%04x\n", (int)addr);
> +            break;
> +    };
> +    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned int)addr,
> +            size, (long unsigned int)ret );
> +    return ret;
> +}
> +
> +void leon3_shutdown(void)
> +{
> +    qemu_system_shutdown_request();
> +}
> +
> +static void main_cpu_reset(void *opaque)
> +{
> +    CPUState *env = opaque;

Here you can introduce a helper structure to pass PC and NPC, like
sun4u.c ResetData. Then the global state should not be needed anymore.

> +
> +    cpu_reset(env);
> +
> +    env->halted = 0;
> +    env->pc     = leon3_state.entry;
> +    env->npc    = leon3_state.entry + 4;
> +
> +    /* Initialize cache control */
> +    leon3_state.cache_control   = 0x0;
> +
> +    /* Configuration registers are read and only always keep those predefined
> +       values */
> +    leon3_state.inst_cache_conf = 0x10220000;
> +    leon3_state.data_cache_conf = 0x18220000;
> +}
> +
> +static void leon3_generic_hw_init(ram_addr_t  ram_size,
> +                                  const char *boot_device,
> +                                  const char *kernel_filename,
> +                                  const char *kernel_cmdline,
> +                                  const char *initrd_filename,
> +                                  const char *cpu_model)
> +{
> +    CPUState   *env;
> +    ram_addr_t  ram_offset, prom_offset;
> +    int         ret;
> +    char       *filename;
> +    qemu_irq   *cpu_irqs = NULL;
> +    int         bios_size;
> +    int         prom_size;
> +    int         aligned_bios_size;
> +
> +    /* Init CPU */
> +    if (!cpu_model)
> +        cpu_model = "LEON3";

Missing braces.

> +
> +    env = cpu_init(cpu_model);
> +    if (!env) {
> +        fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
> +        exit(1);
> +    }
> +
> +    cpu_sparc_set_id(env, 0);
> +
> +    qemu_register_reset(main_cpu_reset, env);
> +
> +    /* Allocate IRQ manager */
> +    grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS);
> +
> +    /* Allocate RAM */
> +    if ((uint64_t)ram_size > (1UL << 30)) {
> +        fprintf(stderr,
> +                "qemu: Too much memory for this machine: %d, maximum 1G\n",
> +                (unsigned int)(ram_size / (1024 * 1024)));
> +        exit(1);
> +    }
> +
> +    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
> +    cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM);
> +
> +    /* Allocate BIOS */
> +    prom_size = 8 * 1024 * 1024; /* 8Mb */
> +    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
> +    cpu_register_physical_memory(0x00000000, prom_size,
> +                                 prom_offset | IO_MEM_ROM);
> +
> +    /* Load boot prom */
> +    if (bios_name == NULL)
> +        bios_name = PROM_FILENAME;
> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
> +
> +    bios_size = get_image_size(filename);
> +
> +    if (bios_size > prom_size) {
> +        fprintf(stderr, "qemu: could not load prom '%s': file too big \n",
> +                filename);
> +        exit(1);
> +    }
> +
> +    if (bios_size > 0) {
> +        aligned_bios_size =
> +            (bios_size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
> +
> +        ret = load_image_targphys(filename, 0x00000000, bios_size);
> +        if (ret < 0 || ret > prom_size) {
> +            fprintf(stderr, "qemu: could not load prom '%s'\n", filename);
> +            exit(1);
> +        }
> +    }
> +    else if (kernel_filename == NULL) {
> +        fprintf(stderr,"Can't read bios image %s\n", filename);
> +        exit(1);
> +    }
> +
> +    /* Can directly load an application. */
> +    if (kernel_filename != NULL) {
> +        long     kernel_size;
> +        uint64_t entry;
> +
> +        kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
> +                               1 /* big endian */, ELF_MACHINE, 0);
> +        if (kernel_size < 0) {
> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
> +                    kernel_filename);
> +            exit(1);
> +        }
> +        if (bios_size <= 0) {
> +            /* If there is no bios/monitor, start the application.  */
> +            env->pc = entry;
> +            env->npc = entry + 4;
> +            leon3_state.entry = entry;
> +        }
> +    }
> +
> +    /* Allocate timers */
> +    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
> +
> +    /* Allocate uart */
> +    if (serial_hds[0])
> +        grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
> +}
> +
> +QEMUMachine leon3_generic_machine = {
> +    .name     = "leon3_generic",
> +    .desc     = "Leon-3 generic",
> +    .init     = leon3_generic_hw_init,
> +    .use_scsi = 0,
> +};
> +
> +static void leon3_machine_init(void)
> +{
> +    qemu_register_machine(&leon3_generic_machine);
> +}
> +
> +machine_init(leon3_machine_init);
> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
> index 7e0d17c..6020ffd 100644
> --- a/target-sparc/cpu.h
> +++ b/target-sparc/cpu.h
> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
>  /* sun4m.c, sun4u.c */
>  void cpu_check_irqs(CPUSPARCState *env);
>
> +/* grlib_irqmp.c */
> +void grlib_irqmp_ack(CPUSPARCState *env, int intno);
> +
> +/* leon3.c */
> +void     leon3_shutdown(void);
> +void     leon3_cache_control_st(target_ulong addr, uint64_t val, int size);
> +uint64_t leon3_cache_control_ld(target_ulong addr, int size);
> +void     leon3_cache_control_int(void);
> +
> +
>  #if defined (TARGET_SPARC64)
>
>  static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask)
> diff --git a/target-sparc/helper.c b/target-sparc/helper.c
> index e84c312..3bf990f 100644
> --- a/target-sparc/helper.c
> +++ b/target-sparc/helper.c
> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
>         .iu_version = 0xf3000000,
>         .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
>         .mmu_version = 0xf3000000,
> -        .mmu_bm = 0x00004000,
> +        .mmu_bm = 0x00000000,
>         .mmu_ctpr_mask = 0x007ffff0,
>         .mmu_cxr_mask = 0x0000003f,
>         .mmu_sfsr_mask = 0xffffffff,
> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
> index be3c1e0..85df077 100644
> --- a/target-sparc/op_helper.c
> +++ b/target-sparc/op_helper.c
> @@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
>
>     helper_check_align(addr, size - 1);
>     switch (asi) {
> -    case 2: /* SuperSparc MXCC registers */
> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>         switch (addr) {
> +        case 0x00:          /* Leon3 Cache Control */
> +        case 0x08:          /* Leon3 Instruction Cache config */
> +        case 0x0C:          /* Leon3 Date Cache config */
> +            ret = leon3_cache_control_ld(addr, size);
> +            break;
>         case 0x01c00a00: /* MXCC control register */
>             if (size == 8)
>                 ret = env->mxccregs[3];
> @@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size)
>  {
>     helper_check_align(addr, size - 1);
>     switch(asi) {
> -    case 2: /* SuperSparc MXCC registers */
> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>         switch (addr) {
> +        case 0x00:          /* Leon3 Cache Control */
> +        case 0x08:          /* Leon3 Instruction Cache config */
> +        case 0x0C:          /* Leon3 Date Cache config */
> +            leon3_cache_control_st(addr, val, size);
> +            break;
> +
>         case 0x01c00000: /* MXCC stream data register 0 */
>             if (size == 8)
>                 env->mxccdata[0] = val;
> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
>  {
>     int cwp, intno = env->exception_index;
>
> +#if !defined(CONFIG_USER_ONLY)
> +    /* Leon3 shutdown */
> +    if (intno == 0x80 && env->version == 0xf3000000) {
> +        leon3_shutdown();
> +    }

This looks like a hack. Should a trap instruction initiate a shutdown?

> +#endif
> +
>  #ifdef DEBUG_PCALL
>     if (qemu_loglevel_mask(CPU_LOG_INT)) {
>         static int count;
> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>     env->pc = env->tbr;
>     env->npc = env->pc + 4;
>     env->exception_index = -1;
> +
> +#if !defined(CONFIG_USER_ONLY)
> +    /* IRQ acknowledgment for Leon3 */
> +    if (env->version == 0xf3000000 && (intno & ~15) == TT_EXTINT) {
> +        grlib_irqmp_ack (env, intno);
> +        leon3_cache_control_int();
> +    }

Like this. I don't think a CPU should immediately ack any incoming interrupts.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support.
  2010-12-06  9:26           ` [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support Fabien Chouteau
@ 2010-12-06 18:01             ` Blue Swirl
  2010-12-07 11:51               ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 18:01 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  hw/leon3.c               |    6 ++++++
>  target-sparc/cpu.h       |    1 +
>  target-sparc/machine.c   |    2 ++
>  target-sparc/translate.c |   10 ++++++++++
>  4 files changed, 19 insertions(+), 0 deletions(-)
>
> diff --git a/hw/leon3.c b/hw/leon3.c
> index ba61081..9605ce8 100644
> --- a/hw/leon3.c
> +++ b/hw/leon3.c
> @@ -187,6 +187,12 @@ static void main_cpu_reset(void *opaque)
>        values */
>     leon3_state.inst_cache_conf = 0x10220000;
>     leon3_state.data_cache_conf = 0x18220000;
> +
> +    /* Asr17 for Leon3 mono-processor */
> +    env->asr17 &= 0 << 28;          /* CPU id */
> +    env->asr17 &= 1 << 8;           /* SPARC V8 multiply and divide available */
> +    env->asr17 &= env->nwindows -1; /* Number of implemented registers
> +                                       windows */

This is constant...

>  }
>
>  static void leon3_generic_hw_init(ram_addr_t  ram_size,
> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
> index 6020ffd..36d49fc 100644
> --- a/target-sparc/cpu.h
> +++ b/target-sparc/cpu.h
> @@ -341,6 +341,7 @@ typedef struct CPUSPARCState {
>                           from PSR) */
>  #if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
>     uint32_t wim;      /* window invalid mask */
> +    uint32_t asr17;    /* asr17 */

... so no new env fields are needed...

>  #endif
>     target_ulong tbr;  /* trap base register */
>  #if !defined(TARGET_SPARC64)
> diff --git a/target-sparc/machine.c b/target-sparc/machine.c
> index 752e431..c530bd3 100644
> --- a/target-sparc/machine.c
> +++ b/target-sparc/machine.c
> @@ -42,6 +42,7 @@ void cpu_save(QEMUFile *f, void *opaque)
>     qemu_put_be32s(f, &env->pil_in);
>  #ifndef TARGET_SPARC64
>     qemu_put_be32s(f, &env->wim);
> +    qemu_put_be32s(f, &env->asr17);

... there's also nothing to save/load...

>     /* MMU */
>     for (i = 0; i < 32; i++)
>         qemu_put_be32s(f, &env->mmuregs[i]);
> @@ -138,6 +139,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
>     qemu_get_be32s(f, &env->pil_in);
>  #ifndef TARGET_SPARC64
>     qemu_get_be32s(f, &env->wim);
> +    qemu_get_be32s(f, &env->asr17);
>     /* MMU */
>     for (i = 0; i < 32; i++)
>         qemu_get_be32s(f, &env->mmuregs[i]);
> diff --git a/target-sparc/translate.c b/target-sparc/translate.c
> index 23f9519..65de614 100644
> --- a/target-sparc/translate.c
> +++ b/target-sparc/translate.c
> @@ -58,6 +58,7 @@ static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver;
>  static TCGv_i32 cpu_softint;
>  #else
>  static TCGv cpu_wim;
> +static TCGv cpu_asr17;
>  #endif
>  /* local register indexes (only used inside old micro ops) */
>  static TCGv cpu_tmp0;
> @@ -2049,6 +2050,8 @@ static void disas_sparc_insn(DisasContext * dc)
>                 rs1 = GET_FIELD(insn, 13, 17);
>                 switch(rs1) {
>                 case 0: /* rdy */
> +                    gen_movl_TN_reg(rd, cpu_y);
> +                    break;
>  #ifndef TARGET_SPARC64
>                 case 0x01 ... 0x0e: /* undefined in the SPARCv8
>                                        manual, rdy on the microSPARC
> @@ -2058,6 +2061,11 @@ static void disas_sparc_insn(DisasContext * dc)
>                 case 0x10 ... 0x1f: /* implementation-dependent in the
>                                        SPARCv8 manual, rdy on the
>                                        microSPARC II */
> +
> +                    if (rs1 == 0x11) { /* Read %asr17 */
> +                        gen_movl_TN_reg(rd, cpu_asr17);

Instead:
r_const = tcg_const_tl(asr constants  | dc->def->nwindows - 1);
gen_movl_TN_reg(rd, r_const);
tcg_temp_free(r_const);

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3
  2010-12-06  9:26 [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Fabien Chouteau
  2010-12-06  9:26 ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual Fabien Chouteau
  2010-12-06 10:44 ` [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Artyom Tarasenko
@ 2010-12-06 18:05 ` Blue Swirl
  2 siblings, 0 replies; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 18:05 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> Hi everyone,
> I'm glad to submit my first patches to the Qemu-devel list.
>
> This patch set introduces a new SPARC V8 machine: Leon3. It's an open-source
> VHDL System-On-Chip, well known in space industry (more information on
> http://www.gaisler.com).
>
> Leon3 is made of multiple components available in the GrLib VHDL library.
> Three devices are implemented: uart, timers and IRQ manager.
> You can find code for these peripherals in the grlib_* files.
>
> Modifications have been done to the SPARC cpu emulation code to handle
> Leon3's specific behavior:
>  - IRQ management
>  - Cache control
>  - Asr17 (implementation-dependent Ancillary State Registers)
>  - Shutdown
>
> Please feel free to comment.

One general comment: each device file should provide pointers to chipset docs.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3
  2010-12-06 15:07   ` Fabien Chouteau
@ 2010-12-06 18:12     ` Blue Swirl
  2010-12-07 17:43       ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-06 18:12 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel, Artyom Tarasenko

On Mon, Dec 6, 2010 at 3:07 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/06/2010 11:44 AM, Artyom Tarasenko wrote:
>>
>> On Mon, Dec 6, 2010 at 10:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> Hi everyone,
>>> I'm glad to submit my first patches to the Qemu-devel list.
>>>
>>> This patch set introduces a new SPARC V8 machine: Leon3. It's an
>>> open-source
>>> VHDL System-On-Chip, well known in space industry (more information on
>>> http://www.gaisler.com).
>>
>> Nice! Haven't looked into the code yet, but it's great to have someone
>> who cares for V8.
>
> And if this patch is accepted, we will try to submit more machines like
> erc32 and leon2.
>
>> Do you also have a firmware which runs on these machines?
>>
>
> I can give you a binary running some basic tests.

Is the binary generally available? Otherwise it would be very hard to
test any changes and the code would bitrot. I'm not sure we even want
to support such machines.

Are the sources available? That would help debugging.

>>> Leon3 is made of multiple components available in the GrLib VHDL library.
>>> Three devices are implemented: uart, timers and IRQ manager.
>>> You can find code for these peripherals in the grlib_* files.
>>>
>>> Modifications have been done to the SPARC cpu emulation code to handle
>>> Leon3's specific behavior:
>>>  - IRQ management
>>>  - Cache control
>>>  - Asr17 (implementation-dependent Ancillary State Registers)
>>
>> Is it the only implementation-dependent asr in Leon3? Thought there were
>> more.
>>
>
> Yes, there's also asr19 for power-down, asr16 for FPU control and others for
> hardware breakpoints.
> These are not required for this first implementation, but If there's a need
> for more ASRs, we may have to find a generic implementation to handle those
> registers.

So far I'd handle these in target-sparc/op_helper.c. If the registers
are also available as MMIO like MXCC, then we should invent a way to
handle both.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-06 17:12   ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer " Blue Swirl
@ 2010-12-07  9:55     ` Fabien Chouteau
  2010-12-08  8:30       ` Edgar E. Iglesias
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-07  9:55 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/06/2010 06:12 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 files changed, 448 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
>> new file mode 100644
>> index 0000000..41edbe4
>> --- /dev/null
>> +++ b/hw/grlib_gptimer.c
>> @@ -0,0 +1,448 @@
>> +/*
>> + * QEMU GRLIB GPTimer Emulator
>> + *
>> + * Copyright (c) 2010 AdaCore
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +
>> +#include "sysbus.h"
>> +#include "qemu-timer.h"
>> +
>> +#include "grlib.h"
>> +
>> +/* #define DEBUG_TIMER */
>
> The usual convention is
> //#define DEBUG_TIMER
> for easy editing.
>

Actually, it's easier for me with the /* */, but OK.

> However, very often the much more powerful tracepoints can replace
> debug statements.
>
>> +
>> +#ifdef DEBUG_TIMER
>> +#define DPRINTF(fmt, ...)                                       \
>> +    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...)
>> +#endif
>> +
>> +#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
>> +#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
>> +
>> +#define GPTIMER_MAX_TIMERS 8
>> +
>> +/* GPTimer Config register fields */
>> +#define GPTIMER_ENABLE      (1<<  0)
>> +#define GPTIMER_RESTART     (1<<  1)
>> +#define GPTIMER_LOAD        (1<<  2)
>> +#define GPTIMER_INT_ENABLE  (1<<  3)
>> +#define GPTIMER_INT_PENDING (1<<  4)
>> +#define GPTIMER_CHAIN       (1<<  5) /* Not supported */
>> +#define GPTIMER_DEBUG_HALT  (1<<  6) /* Not supported */
>> +
>> +/* Memory mapped register offsets */
>> +#define SCALER_OFFSET         0x00
>> +#define SCALER_RELOAD_OFFSET  0x04
>> +#define CONFIG_OFFSET         0x08
>> +#define COUNTER_OFFSET        0x00
>> +#define COUNTER_RELOAD_OFFSET 0x04
>> +#define TIMER_BASE            0x10
>> +
>> +typedef struct GPTimer     GPTimer;
>> +typedef struct GPTimerUnit GPTimerUnit;
>> +
>> +struct GPTimer
>> +{
>> +    QEMUBH *bh;
>> +    struct ptimer_state *ptimer;
>> +
>> +    qemu_irq     irq;
>> +    int          id;
>> +    GPTimerUnit *unit;
>> +
>> +    /* registers */
>> +    uint32_t counter;
>> +    uint32_t reload;
>> +    uint32_t config;
>> +};
>> +
>> +struct GPTimerUnit
>> +{
>> +    SysBusDevice  busdev;
>> +
>> +    uint32_t nr_timers;         /* Number of timers available */
>> +    uint32_t freq_hz;           /* System frequency */
>> +    uint32_t irq_line;          /* Base irq line */
>> +
>> +    GPTimer *timers;
>> +
>> +    /* registers */
>> +    uint32_t scaler;
>> +    uint32_t reload;
>> +    uint32_t config;
>> +};
>> +
>> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
>> +                                  uint32_t            nr_timers,
>> +                                  uint32_t            freq,
>> +                                  qemu_irq           *cpu_irqs,
>> +                                  int                 base_irq)
>
> This function belongs to leon3.c.

I don't see why. GPTimer is a peripheral and you may want to use it in 
an other system.

>> +{
>> +    DeviceState *dev;
>> +    int i;
>> +_ir
>> +    dev = qdev_create(NULL, "grlib,gptimer");
>> +    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
>> +    qdev_prop_set_uint32(dev, "frequency", freq);
>> +    qdev_prop_set_uint32(dev, "irq-line", base_irq);
>
> Base irq is not device property, but part of board configuration. Thus
> leon3.c should just pass&cpu_irqs[base_irq] to this function.
>

I need this property to put the IRQ line in the configuration register. 
Is there a way to get this number from a qemu_irq structure?

>> +
>> +    if (qdev_init(dev)) {
>> +        return NULL;
>> +    }
>> +
>> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
>> +
>> +    for (i = 0; i<  nr_timers; i++)
>> +        sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
>> +
>> +    return dev;
>> +}
>> +
>> +static void grlib_gptimer_enable(GPTimer *timer)
>> +{
>> +    assert(timer != NULL);
>> +
>> +    DPRINTF("%s id:%d\n", __func__, timer->id);
>> +
>> +    ptimer_stop(timer->ptimer);
>> +
>> +    if (!(timer->config&  GPTIMER_ENABLE)) {
>> +        /* Timer disabled */
>> +        DPRINTF("%s id:%d Timer disabled (config 0x%x)\n", __func__,
>> +                timer->id, timer->config);
>> +        return;
>> +    }
>> +
>> +    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
>> +       underflow. Set count + 1 to simulate the GPTimer behavior. */
>> +
>> +    DPRINTF("%s id:%d set count 0x%x and run\n",
>> +            __func__,
>> +            timer->id,
>> +            timer->counter + 1);
>> +
>> +    ptimer_set_count(timer->ptimer, timer->counter + 1);
>> +    ptimer_run(timer->ptimer, 1);
>> +}
>> +
>> +static void grlib_gptimer_restart(GPTimer *timer)
>> +{
>> +    assert(timer != NULL);
>> +
>> +    DPRINTF("%s id:%d reload val: 0x%x\n", __func__, timer->id, timer->reload);
>> +
>> +    timer->counter = timer->reload;
>> +    grlib_gptimer_enable(timer);
>> +}
>> +
>> +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
>> +{
>> +    int i = 0;
>> +    uint32_t value = 0;
>> +
>> +    assert(unit != NULL);
>> +
>> +
>> +    if (scaler>  0) {
>> +        value = unit->freq_hz / (scaler + 1);
>> +    } else {
>> +        value = unit->freq_hz;
>> +    }
>> +
>> +    DPRINTF("%s scaler:%d freq:0x%x\n", __func__, scaler, value);
>> +
>> +    for (i = 0; i<  unit->nr_timers; i++) {
>> +        ptimer_set_freq(unit->timers[i].ptimer, value);
>> +    }
>> +}
>> +
>> +static void grlib_gptimer_hit(void *opaque)
>> +{
>> +    GPTimer *timer = opaque;
>> +    assert(timer != NULL);
>> +
>> +    DPRINTF("%s id:%d\n", __func__, timer->id);
>> +
>> +    /* Timer expired */
>> +
>> +    if (timer->config&  GPTIMER_INT_ENABLE) {
>> +        /* Set the pending bit (only unset by write in the config register) */
>> +        timer->config&= GPTIMER_INT_PENDING;
>> +        qemu_set_irq(timer->irq, 1);
>> +    }
>> +
>> +    if (timer->config&  GPTIMER_RESTART) {
>> +        grlib_gptimer_restart(timer);
>> +    }
>> +}
>> +
>> +static uint32_t grlib_gptimer_readl (void *opaque, target_phys_addr_t addr)
>
> Extra space between function name and its arguments.

Fixed.

>
>> +{
>> +    GPTimerUnit *unit  = opaque;
>> +    uint32_t     value = 0;
>> +
>> +    addr&= 0xff;
>> +
>> +    assert(unit != NULL);
>> +
>> +    /* Unit registers */
>> +    switch (addr)
>> +    {
>> +        case SCALER_OFFSET:
>> +            DPRINTF("%s scaler: 0x%x\n", __func__, unit->scaler);
>> +            return unit->scaler;
>> +
>> +        case SCALER_RELOAD_OFFSET:
>> +            DPRINTF("%s reload: 0x%x\n", __func__, unit->reload);
>> +            return unit->reload;
>> +
>> +        case CONFIG_OFFSET:
>> +            DPRINTF("%s unit config: 0x%x\n", __func__, unit->config);
>> +            return unit->config;
>> +
>> +        default:
>> +            break;
>> +    }
>> +
>> +    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
>> +    int                id         = (addr - TIMER_BASE) / TIMER_BASE;
>
> Variables should be declared at the beginning of the function. But I'd
> actually make two functions and use the first one for the general
> registers above and second one for the timer registers below. Then the
> opaque passed to the latter could be GPTimer instead of GPTimerUnit.
>

If you want, I can put the variables at the beginning of the function, 
but I think that split the function will just obfuscate the code.

>> +
>> +    if (id>= 0&&  id<  unit->nr_timers) {
>> +
>> +        /* GPTimer registers */
>> +        switch (timer_addr)
>> +        {
>> +            case COUNTER_OFFSET:
>> +                value = ptimer_get_count (unit->timers[id].ptimer);
>> +                DPRINTF("%s counter value for timer %d: 0x%x\n",
>> +                        __func__, id, value);
>> +                return value;
>> +
>> +            case COUNTER_RELOAD_OFFSET:
>> +                value = unit->timers[id].reload;
>> +                DPRINTF("%s reload value for timer %d: 0x%x\n",
>> +                        __func__, id, value);
>> +                return value;
>> +
>> +            case CONFIG_OFFSET:
>> +                DPRINTF("%s config for timer %d: 0x%x\n",
>> +                        __func__, id, unit->timers[id].config);
>> +                return unit->timers[id].config;
>> +
>> +            default:
>> +                break;
>> +        }
>> +
>> +    }
>> +
>> +    DPRINTF("read unknown register 0x%04x\n", (int)addr);
>> +    return 0;
>> +}
>> +
>> +static void
>> +grlib_gptimer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
>> +{
>> +    GPTimerUnit *unit = opaque;
>> +
>> +    addr&= 0xff;
>> +
>> +    assert(unit != NULL);
>> +
>> +    /* Unit registers */
>> +    switch (addr)
>> +    {
>> +        case SCALER_OFFSET:
>> +            value&= 0xFFFF; /* clean up the value */
>> +            unit->scaler = value;
>> +            return;
>> +
>> +        case SCALER_RELOAD_OFFSET:
>> +            value&= 0xFFFF; /* clean up the value */
>> +            unit->reload = value;
>> +            grlib_gptimer_set_scaler(unit, value);
>> +            return;
>> +
>> +        case CONFIG_OFFSET:
>> +            /* Read Only (disable timer freeze not supported) */
>> +            return;
>> +
>> +        default:
>> +            break;
>> +    }
>> +
>> +    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
>> +    int                id         = (addr - TIMER_BASE) / TIMER_BASE;
>> +
>> +    if (id>= 0&&  id<  unit->nr_timers) {
>> +
>> +        /* GPTimer registers */
>> +        switch (timer_addr)
>> +        {
>> +            case COUNTER_OFFSET:
>> +                DPRINTF("%s counter value for timer %d: 0x%x\n",
>> +                        __func__, id, value);
>> +                unit->timers[id].counter = value;
>> +                grlib_gptimer_enable(&unit->timers[id]);
>> +                return;
>> +
>> +            case COUNTER_RELOAD_OFFSET:
>> +                DPRINTF("%s reload value for timer %d: 0x%x\n",
>> +                        __func__, id, value);
>> +                unit->timers[id].reload = value;
>> +                return;
>> +
>> +            case CONFIG_OFFSET:
>> +                DPRINTF("%s config for timer %d: 0x%x\n", __func__, id, value);
>> +
>> +                unit->timers[id].config = value;
>> +
>> +                /* gptimer_restart calls gptimer_enable, so if "enable" and
>> +                   "load" bits are present, we just have to call restart. */
>> +
>> +                if (value&  GPTIMER_LOAD) {
>> +                    grlib_gptimer_restart(&unit->timers[id]);
>> +                } else if (value&  GPTIMER_ENABLE) {
>> +                    grlib_gptimer_enable(&unit->timers[id]);
>> +                }
>> +
>> +                /* This fields must always be read as 0 */
>> +                value&= ~(GPTIMER_LOAD&  GPTIMER_DEBUG_HALT);
>> +
>> +                unit->timers[id].config = value;
>> +                return;
>> +
>> +            default:
>> +                break;
>> +        }
>> +
>> +    }
>> +
>> +    DPRINTF("write unknown register 0x%04x\n", (int)addr);
>> +}
>> +
>> +static CPUReadMemoryFunc *grlib_gptimer_read[] = {
>
> 'const'
>

Fixed.

>> +    NULL, NULL, grlib_gptimer_readl,
>> +};
>> +
>> +static CPUWriteMemoryFunc *grlib_gptimer_write[] = {
>> +    NULL, NULL, grlib_gptimer_writel,
>> +};
>> +
>> +static void grlib_gptimer_reset(void *opaque)
>> +{
>> +    /* int          i    = 0; */
>> +    /* GPTimerUnit *unit = (GPTimerUnit *)opaque; */
>> +    /* assert(unit != NULL); */
>> +
>> +    /* unit->scaler = 0; */
>> +    /* unit->reload = 0; */
>> +    /* unit->config = 0; */
>> +
>> +    /* unit->config  = unit->nr_timers; */
>> +    /* unit->config |= unit->irq_line<<  3; */
>> +    /* unit->config |= 1<<  8;     /\* separate interrupt *\/ */
>> +    /* unit->config |= 1<<  9;     /\* Disable timer freeze *\/ */
>> +
>> +
>> +    /* for (i = 0; i<  unit->nr_timers; i++) { */
>> +    /*     GPTimer *timer =&unit->timers[i]; */
>> +
>> +    /*     timer->counter = 0; */
>> +    /*     timer->reload = 0; */
>> +    /*     timer->config = 0; */
>> +    /*     ptimer_stop(timer->ptimer); */
>> +    /*     ptimer_set_count(timer->ptimer, 0); */
>> +    /*     ptimer_set_freq(timer->ptimer, unit->freq_hz); */
>> +    /* } */
>
> Why dead code?

No reason, this is just a mistake, the code should be un-commented.

>
>> +
>> +}
>> +
>> +static int grlib_gptimer_init(SysBusDevice *dev)
>> +{
>> +    GPTimerUnit  *unit = FROM_SYSBUS(typeof (*unit), dev);
>> +    unsigned int  i;
>> +    int           timer_regs;
>> +
>> +    assert(unit->nr_timers>  0);
>> +    assert(unit->nr_timers<= GPTIMER_MAX_TIMERS);
>> +    unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers);
>> +
>> +    for (i = 0; i<  unit->nr_timers; i++) {
>> +        GPTimer *timer =&unit->timers[i];
>> +
>> +        timer->unit   = unit;
>> +        timer->bh     = qemu_bh_new(grlib_gptimer_hit, timer);
>> +        timer->ptimer = ptimer_init(timer->bh);
>> +        timer->id     = i;
>> +
>> +        /* One IRQ line for each timer */
>> +        sysbus_init_irq(dev,&timer->irq);
>> +
>> +        ptimer_set_freq(timer->ptimer, unit->freq_hz);
>> +    }
>> +
>> +    qemu_register_reset(grlib_gptimer_reset, unit);
>
> This can be replaced by qdev.reset field.

All right, it's clearer indeed.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-06 17:25     ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Blue Swirl
@ 2010-12-07 10:43       ` Fabien Chouteau
  2010-12-11 10:31         ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-07 10:43 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/06/2010 06:25 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   hw/grlib_irqmp.c |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 files changed, 416 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
>> new file mode 100644
>> index 0000000..69e1553
>> --- /dev/null
>> +++ b/hw/grlib_irqmp.c
>> @@ -0,0 +1,416 @@
>> +/*
>> + * QEMU GRLIB IRQMP Emulator
>> + *
>> + * (Multiprocessor and extended interrupt not supported)
>> + *
>> + * Copyright (c) 2010 AdaCore
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +
>> +#include "sysbus.h"
>> +#include "cpu.h"
>> +
>> +#include "grlib.h"
>> +
>> +/* #define DEBUG_IRQ */
>> +
>> +#ifdef DEBUG_IRQ
>> +#define DPRINTF(fmt, ...)                                       \
>> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...)
>> +#endif
>> +
>> +#define IRQMP_MAX_CPU 16
>> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
>> +
>> +/* Memory mapped register offsets */
>> +#define LEVEL_OFFSET     0x00
>> +#define PENDING_OFFSET   0x04
>> +#define FORCE0_OFFSET    0x08
>> +#define CLEAR_OFFSET     0x0C
>> +#define MP_STATUS_OFFSET 0x10
>> +#define BROADCAST_OFFSET 0x14
>> +#define MASK_OFFSET      0x40
>> +#define FORCE_OFFSET     0x80
>> +#define EXTENDED_OFFSET  0xC0
>> +
>> +typedef struct IRQMP
>> +{
>> +    SysBusDevice busdev;
>> +
>> +    CPUSPARCState *env;
>
> Devices should never access CPUState directly. Instead, board level
> should create CPU irqs and these should then be passed here.
>

This case is special, Leon3 is a System-On-Chip and some of the 
components are very close to the processor.
IRQMP is not really a peripheral nor a part of the CPU, it's both...

>> +} IRQMP;
>> +
>> +typedef struct IRQMPState
>> +{
>> +    uint32_t level;
>> +    uint32_t pending;
>> +    uint32_t clear;
>> +    uint32_t broadcast;
>> +
>> +    uint32_t mask[IRQMP_MAX_CPU];
>> +    uint32_t force[IRQMP_MAX_CPU];
>> +    uint32_t extended[IRQMP_MAX_CPU];
>> +
>> +    IRQMP    *parent;
>> +} IRQMPState;
>> +
>> +IRQMPState grlib_irqmp_state;
>
> Global state indicates poor design. Why separate IRQMP and IRQMPState?

I have to access IRQMPState in grlib_irqmp_ack and 
grlib_irqmp_check_irqs, but I don't see how I can do it without a global 
variable.
Again, I think that it's related to the special case of IRQMP.

>> +
>> +void grlib_irqmp_set_irq(void *opaque, int irq, int level);
>
> This should not be global. Again, creating qemu_irqs or moving some of
> the code to board level should help.

This one should be static indeed.

>> +
>> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
>> +                                CPUState            *env,
>> +                                qemu_irq           **cpu_irqs,
>> +                                uint32_t             nr_irqs)
>> +{
>> +    DeviceState *dev;
>> +
>> +    assert(cpu_irqs != NULL);
>> +
>> +    dev = qdev_create(NULL, "grlib,irqmp");
>> +    qdev_prop_set_ptr(dev, "cpustate", env);
>> +
>> +    if (qdev_init(dev)) {
>> +        return NULL;
>> +    }
>> +
>> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
>> +
>> +    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
>> +&grlib_irqmp_state,
>> +                                   nr_irqs);
>> +
>> +    return dev;
>> +}
>> +
>> +static void grlib_irqmp_check_irqs(CPUState *env)
>> +{
>> +    uint32_t pend   = 0;
>> +    uint32_t level0 = 0;
>> +    uint32_t level1 = 0;
>> +
>> +    assert(env != NULL);
>> +
>> +    /* IRQ for CPU 0 (no SMP support) */
>> +    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
>> +&  grlib_irqmp_state.mask[0];
>> +
>> +
>> +    level0 = pend&  ~grlib_irqmp_state.level;
>> +    level1 = pend&    grlib_irqmp_state.level;
>> +
>> +    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n",
>> +            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
>> +            grlib_irqmp_state.mask[0], level1, level0);
>
> The above should stay here, but code below should to go to board level
> (leon3.c). Then you need to separate device IRQ handling from CPU PIL
> handling.

If I want to use IRQMP for another machine I will have to duplicate the 
code.
So I think it is the right place for this this code.

>> +
>> +    /* Trigger level1 interrupt first and level0 if there is no level1 */
>> +    if (level1 != 0) {
>> +        env->pil_in = level1;
>> +    } else {
>> +        env->pil_in = level0;
>> +    }
>> +
>> +    if (env->pil_in&&  (env->interrupt_index == 0 ||
>> +                        (env->interrupt_index&  ~15) == TT_EXTINT)) {
>> +        unsigned int i;
>> +
>> +        for (i = 15; i>  0; i--) {
>> +            if (env->pil_in&  (1<<  i)) {
>> +                int old_interrupt = env->interrupt_index;
>> +
>> +                env->interrupt_index = TT_EXTINT | i;
>> +                if (old_interrupt != env->interrupt_index) {
>> +                    DPRINTF("Set CPU IRQ %d\n", i);
>> +                    cpu_interrupt(env, CPU_INTERRUPT_HARD);
>> +                }
>> +                break;
>> +            }
>> +        }
>> +    } else if (!env->pil_in&&  (env->interrupt_index&  ~15) == TT_EXTINT) {
>> +        DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index&  15);
>> +        env->interrupt_index = 0;
>> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
>> +    }
>> +}
>> +
>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno)
>> +{
>> +    assert(env != NULL);
>> +
>> +    uint32_t mask;
>> +
>> +    intno&= 15;
>> +    mask = 1<<  intno;
>> +
>> +    DPRINTF("grlib_irqmp_ack %d\n", intno);
>> +
>> +    /* Clear registers */
>> +    grlib_irqmp_state.pending&= ~mask;
>> +    grlib_irqmp_state.force[0]&= ~mask; /* Only CPU 0 (No SMP support) */
>> +
>> +    grlib_irqmp_check_irqs(env);
>> +}
>> +
>> +void grlib_irqmp_set_irq(void *opaque, int irq, int level)
>> +{
>> +    IRQMPState *s = opaque;
>> +    int         i = 0;
>> +
>> +    assert(opaque != NULL);
>> +    assert(s->parent != NULL);
>> +
>> +    if (level) {
>> +        DPRINTF("Raise CPU IRQ %d\n", irq);
>> +
>> +        if (s->broadcast&  1<<  irq) {
>> +            /* Broadcasted IRQ */
>> +            for (i = 0; i<  IRQMP_MAX_CPU; i++) {
>> +                s->force[i] |= 1<<  irq;
>> +            }
>> +        } else {
>> +            s->pending |= 1<<  irq;
>> +        }
>> +        grlib_irqmp_check_irqs(s->parent->env);
>> +
>> +    } else {
>> +
>> +        DPRINTF("Lower CPU IRQ %d\n", irq);
>> +        if (s->broadcast&  1<<  irq) {
>> +            /* Broadcasted IRQ */
>> +            for (i = 0; i<  IRQMP_MAX_CPU; i++) {
>> +                s->force[i]&= ~(1<<  irq);
>> +            }
>> +        } else {
>> +            s->pending&= ~(1<<  irq);
>> +        }
>> +        grlib_irqmp_check_irqs(s->parent->env);
>> +    }
>> +}
>> +
>> +static uint32_t grlib_irqmp_readl (void *opaque, target_phys_addr_t addr)
>> +{
>> +    IRQMP *irqmp = opaque;
>> +
>> +    assert(irqmp != NULL);
>> +
>> +    addr&= 0xff;
>> +
>> +    /* global registers */
>> +    switch (addr)
>> +    {
>> +        case LEVEL_OFFSET:
>> +            return grlib_irqmp_state.level;
>> +
>> +        case PENDING_OFFSET:
>> +            return grlib_irqmp_state.pending;
>> +
>> +        case FORCE0_OFFSET:
>> +            /* This register is an "alias" for the force register of CPU 0 */
>> +            return grlib_irqmp_state.force[0];
>> +
>> +        case CLEAR_OFFSET:
>> +        case MP_STATUS_OFFSET:
>> +            /* Always read as 0 */
>> +            return 0;
>> +
>> +        case BROADCAST_OFFSET:
>> +            return grlib_irqmp_state.broadcast;
>> +
>> +        default:
>> +            break;
>> +    }
>> +
>> +    /* mask registers */
>> +    if (addr>= MASK_OFFSET&&  addr<  FORCE_OFFSET) {
>> +        int cpu = (addr - MASK_OFFSET) / 4;
>> +        assert(cpu>= 0&&  cpu<  IRQMP_MAX_CPU);
>> +
>> +        return grlib_irqmp_state.mask[cpu] ;
>> +    }
>> +
>> +    /* force registers */
>> +    if (addr>= FORCE_OFFSET&&  addr<  EXTENDED_OFFSET) {
>> +        int cpu = (addr - FORCE_OFFSET) / 4;
>> +        assert(cpu>= 0&&  cpu<  IRQMP_MAX_CPU);
>> +
>> +        return grlib_irqmp_state.force[cpu];
>> +    }
>> +
>> +    /* extended (not supported) */
>> +    if (addr>= EXTENDED_OFFSET&&  addr<  IRQMP_REG_SIZE) {
>> +        int cpu = (addr - EXTENDED_OFFSET) / 4;
>> +        assert(cpu>= 0&&  cpu<  IRQMP_MAX_CPU);
>> +
>> +        return grlib_irqmp_state.extended[cpu];
>> +    }
>> +
>> +    DPRINTF("read unknown register 0x%04x\n", (int)addr);
>> +    return 0;
>> +}
>> +
>> +static void
>> +grlib_irqmp_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
>> +{
>> +    IRQMP *irqmp = opaque;
>> +
>> +    assert(irqmp != NULL);
>> +
>> +    addr&= 0xff;
>> +
>> +    /* global registers */
>> +    switch (addr)
>> +    {
>> +        case LEVEL_OFFSET:
>> +            value&= 0xFFFF<<  1; /* clean up the value */
>> +            grlib_irqmp_state.level = value;
>> +            return;
>> +
>> +        case PENDING_OFFSET:
>> +            /* Read Only */
>> +            return;
>> +
>> +        case FORCE0_OFFSET:
>> +            /* This register is an "alias" for the force register of CPU 0 */
>> +
>> +            value&= 0xFFFE; /* clean up the value */
>> +            grlib_irqmp_state.force[0] = value;
>> +            grlib_irqmp_check_irqs(irqmp->env);
>> +            return;
>> +
>> +        case CLEAR_OFFSET:
>> +            value&= ~1; /* clean up the value */
>> +            grlib_irqmp_state.pending&= ~value;
>> +            return;
>> +
>> +        case MP_STATUS_OFFSET:
>> +            /* Read Only (no SMP support) */
>> +            return;
>> +
>> +        case BROADCAST_OFFSET:
>> +            value&= 0xFFFE; /* clean up the value */
>> +            grlib_irqmp_state.broadcast = value;
>> +            return;
>> +
>> +        default:
>> +            break;
>> +    }
>> +
>> +    /* mask registers */
>> +    if (addr>= MASK_OFFSET&&  addr<  FORCE_OFFSET) {
>> +        int cpu = (addr - MASK_OFFSET) / 4;
>> +        assert(cpu>= 0&&  cpu<  IRQMP_MAX_CPU);
>> +
>> +        value&= ~1; /* clean up the value */
>> +        grlib_irqmp_state.mask[cpu] = value;
>> +        grlib_irqmp_check_irqs(irqmp->env);
>> +        return;
>> +    }
>> +
>> +    /* force registers */
>> +    if (addr>= FORCE_OFFSET&&  addr<  EXTENDED_OFFSET) {
>> +        int cpu = (addr - FORCE_OFFSET) / 4;
>> +        assert(cpu>= 0&&  cpu<  IRQMP_MAX_CPU);
>> +
>> +        uint32_t force = value&  0xFFFE;
>> +        uint32_t clear = (value>>  16)&  0xFFFE;
>> +        uint32_t old   = grlib_irqmp_state.force[cpu];
>> +
>> +        grlib_irqmp_state.force[cpu] = (old | force)&  ~clear;
>> +        grlib_irqmp_check_irqs(irqmp->env);
>> +        return;
>> +    }
>> +
>> +    /* extended (not supported) */
>> +    if (addr>= EXTENDED_OFFSET&&  addr<  IRQMP_REG_SIZE) {
>> +        int cpu = (addr - EXTENDED_OFFSET) / 4;
>> +        assert(cpu>= 0&&  cpu<  IRQMP_MAX_CPU);
>> +
>> +        value&= 0xF; /* clean up the value */
>> +        grlib_irqmp_state.extended[cpu] = value;
>> +        return;
>> +    }
>> +
>> +    DPRINTF("write unknown register 0x%04x\n", (int)addr);
>
> Please use TARGET_FMT_plx, so the cast can be removed.
OK, I didn't know about that.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART as defined in GRLIB IP Core User's Manual.
  2010-12-06 17:29       ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART as defined in GRLIB IP Core User's Manual Blue Swirl
@ 2010-12-07 10:55         ` Fabien Chouteau
  0 siblings, 0 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-07 10:55 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/06/2010 06:29 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   hw/grlib_apbuart.c |  231 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 files changed, 231 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c
>> new file mode 100644
>> index 0000000..32a5362
>> --- /dev/null
>> +++ b/hw/grlib_apbuart.c
>> @@ -0,0 +1,231 @@
>> +/*
>> + * QEMU GRLIB APB UART Emulator
>> + *
>> + * Copyright (c) 2010 AdaCore
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +
>> +#include "sysbus.h"
>> +#include "qemu-char.h"
>> +
>> +#include "grlib.h"
>> +
>> +/* #define DEBUG_UART */
>> +
>> +#ifdef DEBUG_UART
>> +#define DPRINTF(fmt, ...)                                       \
>> +    do { printf("APBUART: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...)
>> +#endif
>> +
>> +#define UART_REG_SIZE 20     /* Size of memory mapped registers */
>> +
>> +/* UART status register fields */
>> +#define UART_DATA_READY           (1<<    0)
>> +#define UART_TRANSMIT_SHIFT_EMPTY (1<<    1)
>> +#define UART_TRANSMIT_FIFO_EMPTY  (1<<    2)
>> +#define UART_BREAK_RECEIVED       (1<<    3)
>> +#define UART_OVERRUN              (1<<    4)
>> +#define UART_PARITY_ERROR         (1<<    5)
>> +#define UART_FRAMING_ERROR        (1<<    6)
>> +#define UART_TRANSMIT_FIFO_HALF   (1<<    7)
>> +#define UART_RECEIV_FIFO_HALF     (1<<    8)
>
> RECEIVE

Fixed.

>
>> +#define UART_TRANSMIT_FIFO_FULL   (1<<    9)
>> +#define UART_RECEIV_FIFO_FULL     (1<<  10)
>> +
>> +/* UART control register fields */
>> +#define UART_RECEIV_ENABLE           (1<<    0)
>> +#define UART_TRANSMIT_ENABLE         (1<<    1)
>> +#define UART_RECEIV_INTERRUPT        (1<<    2)
>> +#define UART_TRANSMIT_INTERRUPT      (1<<    3)
>> +#define UART_PARITY_SELECT           (1<<    4)
>> +#define UART_PARITY_ENABLE           (1<<    5)
>> +#define UART_FLOW_CONTROL            (1<<    6)
>> +#define UART_LOOPBACK                (1<<    7)
>> +#define UART_EXTERNAL_CLOCK          (1<<    8)
>> +#define UART_RECEIV_FIFO_INTERRUPT   (1<<    9)
>> +#define UART_TRANSMIT_FIFO_INTERRUPT (1<<  10)
>> +#define UART_FIFO_DEBUG_MODE         (1<<  11)
>> +#define UART_OUTPUT_ENABLE           (1<<  12)
>> +#define UART_FIFO_AVAILABLE          (1<<  31)
>> +
>> +/* Memory mapped register offsets */
>> +#define DATA_OFFSET       0x00
>> +#define STATUS_OFFSET     0x04
>> +#define CONTROL_OFFSET    0x08
>> +#define SCALER_OFFSET     0x0C  /* not supported */
>> +#define FIFO_DEBUG_OFFSET 0x10  /* not supported */
>> +
>> +typedef struct UART
>> +{
>> +    SysBusDevice busdev;
>> +
>> +    qemu_irq irq;
>> +
>> +    CharDriverState *chr;
>> +
>> +    /* registers */
>> +    uint32_t receive;
>> +    uint32_t status;
>> +    uint32_t control;
>> +} UART;
>> +
>> +
>> +DeviceState *grlib_apbuart_create(target_phys_addr_t  base,
>> +                                  CharDriverState    *serial,
>> +                                  qemu_irq            irq)
>> +{
>> +    DeviceState *dev;
>> +
>> +    dev = qdev_create(NULL, "grlib,apbuart");
>> +    qdev_prop_set_ptr(dev, "chrdev", serial);
>> +
>> +    if (qdev_init(dev)) {
>> +        return NULL;
>> +    }
>> +
>> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
>> +
>> +    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
>> +
>> +    return dev;
>> +}
>> +
>> +static int grlib_apbuart_can_receive(void *opaque)
>> +{
>> +    UART *uart = opaque;
>> +    assert(uart != NULL);
>> +
>> +    return !!(uart->status&  UART_DATA_READY);
>> +}
>> +
>> +static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size)
>> +{
>> +    UART *uart = opaque;
>> +    assert(uart != NULL);
>> +
>> +    uart->receive  = *buf;
>> +    uart->status  |= UART_DATA_READY;
>> +
>> +    if (uart->control&  UART_RECEIV_INTERRUPT)
>> +        qemu_set_irq(uart->irq, 1);
>> +}
>> +
>> +static void grlib_apbuart_event(void *opaque, int event)
>> +{
>> +#ifdef DEBUG_UART
>> +    printf("uart: event %x\n", event);
>> +#endif
>
> DPRINTF or tracepoint.

Fixed.

>
>> +}
>> +
>> +static void
>> +grlib_apbuart_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
>> +{
>> +    UART          *uart = opaque;
>> +    unsigned char  c    = 0;
>> +
>> +    addr&= 0xff;
>> +
>> +    assert(uart != NULL);
>> +
>> +    /* Unit registers */
>> +    switch (addr)
>> +    {
>> +        case DATA_OFFSET:
>> +            c = value&  0xFF;
>> +            qemu_chr_write(uart->chr,&c, 1);
>> +            return;
>> +
>> +        case STATUS_OFFSET:
>> +            /* Read Only (disable timer freeze not supported) */
>> +            return;
>> +
>> +        case CONTROL_OFFSET:
>> +            return;
>> +
>> +        case SCALER_OFFSET:
>> +            /* Not supported */
>> +            return;
>> +
>> +        default:
>> +            break;
>> +    }
>> +
>> +    DPRINTF("write unknown register 0x%04x\n", (int)addr);
>> +}
>> +
>> +static CPUReadMemoryFunc *grlib_apbuart_read[] = {
>> +    NULL, NULL, NULL,
>> +};
>> +
>> +static CPUWriteMemoryFunc *grlib_apbuart_write[] = {
>> +    NULL, NULL, grlib_apbuart_writel,
>> +};
>> +
>> +static int grlib_gptimer_init(SysBusDevice *dev)
>> +{
>> +    UART *uart      = FROM_SYSBUS(typeof (*uart), dev);
>> +    int   uart_regs = 0;
>
> Useless initialization.
>
> Please consider also adding reset and savevm/loadvm support.

There's nothing to reset or to save/load.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components.
  2010-12-06 17:31         ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Blue Swirl
@ 2010-12-07 11:04           ` Fabien Chouteau
  0 siblings, 0 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-07 11:04 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/06/2010 06:31 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   hw/grlib.h |   27 +++++++++++++++++++++++++++
>>   1 files changed, 27 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/grlib.h b/hw/grlib.h
>> new file mode 100644
>> index 0000000..eab7974
>> --- /dev/null
>> +++ b/hw/grlib.h
>> @@ -0,0 +1,27 @@
>> +#ifndef _GRLIB_H_
>> +#define _GRLIB_H_
>> +
>> +#include "qdev.h"
>> +
>> +/* IRQMP */
>> +
>> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
>> +                                CPUState            *env,
>> +                                qemu_irq           **cpu_irqs,
>> +                                uint32_t             nr_irqs);
>> +
>> +/* GPTimer */
>> +
>> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
>> +                                  uint32_t            nr_timers,
>> +                                  uint32_t            freq,
>> +                                  qemu_irq           *cpu_irqs,
>> +                                  int                 base_irq);
>> +
>> +/* APB UART */
>> +
>> +DeviceState *grlib_apbuart_create(target_phys_addr_t  base,
>> +                                  CharDriverState    *serial,
>> +                                  qemu_irq            irq);
>> +
>
> The file should not be needed when you move the functions to leon3.c.

These functions can be used to create more machines with GrLib 
components, that's why they are implemented in the grlib_* files.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-06 17:53           ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Blue Swirl
@ 2010-12-07 11:40             ` Fabien Chouteau
  2010-12-11  9:56               ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-07 11:40 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/06/2010 06:53 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   Makefile.target          |    5 +-
>>   hw/leon3.c               |  310 ++++++++++++++++++++++++++++++++++++++++++++++
>>   target-sparc/cpu.h       |   10 ++
>>   target-sparc/helper.c    |    2 +-
>>   target-sparc/op_helper.c |   30 ++++-
>>   5 files changed, 353 insertions(+), 4 deletions(-)
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index 2800f47..f40e04f 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>>   else
>>   obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>   obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
>> +
>> +# GRLIB
>> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>>   endif
>>
>>   obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>> diff --git a/hw/leon3.c b/hw/leon3.c
>> new file mode 100644
>> index 0000000..ba61081
>> --- /dev/null
>> +++ b/hw/leon3.c
>> @@ -0,0 +1,310 @@
>> +/*
>> + * QEMU Leon3 System Emulator
>> + *
>> + * Copyright (c) 2010 AdaCore
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +#include "hw.h"
>> +#include "qemu-timer.h"
>> +#include "qemu-char.h"
>> +#include "sysemu.h"
>> +#include "boards.h"
>> +#include "loader.h"
>> +#include "elf.h"
>> +
>> +#include "grlib.h"
>> +
>> +/* #define DEBUG_LEON3 */
>> +
>> +#ifdef DEBUG_LEON3
>> +#define DPRINTF(fmt, ...)                                       \
>> +    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...)
>> +#endif
>> +
>> +/* Default system clock.  */
>> +#define CPU_CLK (40 * 1000 * 1000)
>> +
>> +#define PROM_FILENAME        "u-boot.bin"
>> +
>> +#define MAX_PILS 16
>> +
>> +typedef struct Leon3State
>> +{
>> +    uint32_t cache_control;
>> +    uint32_t inst_cache_conf;
>> +    uint32_t data_cache_conf;
>> +
>> +    uint64_t entry;             /* save kernel entry in case of reset */
>> +} Leon3State;
>> +
>> +Leon3State leon3_state;
>
> Again global state, please refactor. Perhaps most of the cache
> handling code belong to target-sparc/op_helper.c and this structure to
> CPUSPARCState.

I will try to find a solution for that.
Is it OK to add some Leon3 specific stuff in the CPUSPARCState?

>> +
>> +/* Cache control: emulate the behavior of cache control registers but without
>> +   any effect on the emulated CPU */
>> +
>> +#define CACHE_DISABLED 0x0
>> +#define CACHE_FROZEN   0x1
>> +#define CACHE_ENABLED  0x3
>> +
>> +/* Cache Control register fields */
>> +
>> +#define CACHE_CTRL_IF (1<<    4)  /* Instruction Cache Freeze on Interrupt */
>> +#define CACHE_CTRL_DF (1<<    5)  /* Data Cache Freeze on Interrupt */
>> +#define CACHE_CTRL_DP (1<<  14)  /* Data cache flush pending */
>> +#define CACHE_CTRL_IP (1<<  15)  /* Instruction cache flush pending */
>> +#define CACHE_CTRL_IB (1<<  16)  /* Instruction burst fetch */
>> +#define CACHE_CTRL_FI (1<<  21)  /* Flush Instruction cache (Write only) */
>> +#define CACHE_CTRL_FD (1<<  22)  /* Flush Data cache (Write only) */
>> +#define CACHE_CTRL_DS (1<<  23)  /* Data cache snoop enable */
>> +
>> +void leon3_cache_control_int(void)
>> +{
>> +    uint32_t state = 0;
>> +
>> +    if (leon3_state.cache_control&  CACHE_CTRL_IF) {
>> +        /* Instruction cache state */
>> +        state = leon3_state.cache_control&  0x3;
>
> Please add a new define CACHE_CTRL_xxx to replace 0x3.
>

Done.

>> +        if (state == CACHE_ENABLED) {
>> +            state = CACHE_FROZEN;
>> +            DPRINTF("Instruction cache: freeze\n");
>> +        }
>> +
>> +        leon3_state.cache_control&= ~0x3;
>> +        leon3_state.cache_control |= state;
>> +    }
>> +
>> +    if (leon3_state.cache_control&  CACHE_CTRL_DF) {
>> +        /* Data cache state */
>> +        state = (leon3_state.cache_control>>  2)&  0x3;
>> +        if (state == CACHE_ENABLED) {
>> +            state = CACHE_FROZEN;
>> +            DPRINTF("Data cache: freeze\n");
>> +        }
>> +
>> +        leon3_state.cache_control&= ~(0x3<<  2);
>> +        leon3_state.cache_control |= (state<<  2);
>> +    }
>> +}
>> +
>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
>> +{
>> +    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned int)addr,
>> +            (unsigned int)val, size);
>
> There's PRIx64 to print uint64_t portably, then the casts can be removed.
>

Fixed.

>> +
>> +    if (size != 4) {
>> +        DPRINTF(" CC 32bits only\n");
>> +        return;
>> +    }
>> +
>> +    switch (addr) {
>> +        case 0x00:              /* Cache control */
>> +
>> +            /* These values must always be read as zeros */
>> +            val&= ~CACHE_CTRL_FD;
>> +            val&= ~CACHE_CTRL_FI;
>> +            val&= ~CACHE_CTRL_IB;
>> +            val&= ~CACHE_CTRL_IP;
>> +            val&= ~CACHE_CTRL_DP;
>> +
>> +            leon3_state.cache_control = val;
>> +            break;
>> +        case 0x04:              /* Instruction cache configuration */
>> +        case 0x08:              /* Data cache configuration */
>> +            /* Read Only */
>> +            break;
>> +        default:
>> +            DPRINTF(" CC write unknown register 0x%04x\n", (int)addr);
>> +            break;
>> +    };
>> +}
>> +
>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size)
>> +{
>> +    uint64_t ret = 0;
>> +
>> +    if (size != 4) {
>> +        DPRINTF(" CC 32bits only\n");
>> +        return 0;
>> +    }
>> +
>> +    switch (addr) {
>> +        case 0x00:              /* Cache control */
>> +            ret = leon3_state.cache_control;
>> +            break;
>> +        case 0x04:              /* Instruction cache configuration */
>> +            ret = leon3_state.inst_cache_conf;
>> +            break;
>> +        case 0x08:              /* Data cache configuration */
>> +            ret = leon3_state.data_cache_conf;
>> +            break;
>> +        default:
>> +            DPRINTF(" CC read unknown register 0x%04x\n", (int)addr);
>> +            break;
>> +    };
>> +    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned int)addr,
>> +            size, (long unsigned int)ret );
>> +    return ret;
>> +}
>> +
>> +void leon3_shutdown(void)
>> +{
>> +    qemu_system_shutdown_request();
>> +}
>> +
>> +static void main_cpu_reset(void *opaque)
>> +{
>> +    CPUState *env = opaque;
>
> Here you can introduce a helper structure to pass PC and NPC, like
> sun4u.c ResetData. Then the global state should not be needed anymore.
>

OK, I've used the sun4u.c reset scheme.

>> +
>> +    cpu_reset(env);
>> +
>> +    env->halted = 0;
>> +    env->pc     = leon3_state.entry;
>> +    env->npc    = leon3_state.entry + 4;
>> +
>> +    /* Initialize cache control */
>> +    leon3_state.cache_control   = 0x0;
>> +
>> +    /* Configuration registers are read and only always keep those predefined
>> +       values */
>> +    leon3_state.inst_cache_conf = 0x10220000;
>> +    leon3_state.data_cache_conf = 0x18220000;
>> +}
>> +
>> +static void leon3_generic_hw_init(ram_addr_t  ram_size,
>> +                                  const char *boot_device,
>> +                                  const char *kernel_filename,
>> +                                  const char *kernel_cmdline,
>> +                                  const char *initrd_filename,
>> +                                  const char *cpu_model)
>> +{
>> +    CPUState   *env;
>> +    ram_addr_t  ram_offset, prom_offset;
>> +    int         ret;
>> +    char       *filename;
>> +    qemu_irq   *cpu_irqs = NULL;
>> +    int         bios_size;
>> +    int         prom_size;
>> +    int         aligned_bios_size;
>> +
>> +    /* Init CPU */
>> +    if (!cpu_model)
>> +        cpu_model = "LEON3";
>
> Missing braces.

Fixed.

>
>> +
>> +    env = cpu_init(cpu_model);
>> +    if (!env) {
>> +        fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
>> +        exit(1);
>> +    }
>> +
>> +    cpu_sparc_set_id(env, 0);
>> +
>> +    qemu_register_reset(main_cpu_reset, env);
>> +
>> +    /* Allocate IRQ manager */
>> +    grlib_irqmp_create(0x80000200, env,&cpu_irqs, MAX_PILS);
>> +
>> +    /* Allocate RAM */
>> +    if ((uint64_t)ram_size>  (1UL<<  30)) {
>> +        fprintf(stderr,
>> +                "qemu: Too much memory for this machine: %d, maximum 1G\n",
>> +                (unsigned int)(ram_size / (1024 * 1024)));
>> +        exit(1);
>> +    }
>> +
>> +    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
>> +    cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM);
>> +
>> +    /* Allocate BIOS */
>> +    prom_size = 8 * 1024 * 1024; /* 8Mb */
>> +    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
>> +    cpu_register_physical_memory(0x00000000, prom_size,
>> +                                 prom_offset | IO_MEM_ROM);
>> +
>> +    /* Load boot prom */
>> +    if (bios_name == NULL)
>> +        bios_name = PROM_FILENAME;
>> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
>> +
>> +    bios_size = get_image_size(filename);
>> +
>> +    if (bios_size>  prom_size) {
>> +        fprintf(stderr, "qemu: could not load prom '%s': file too big \n",
>> +                filename);
>> +        exit(1);
>> +    }
>> +
>> +    if (bios_size>  0) {
>> +        aligned_bios_size =
>> +            (bios_size + TARGET_PAGE_SIZE - 1)&  TARGET_PAGE_MASK;
>> +
>> +        ret = load_image_targphys(filename, 0x00000000, bios_size);
>> +        if (ret<  0 || ret>  prom_size) {
>> +            fprintf(stderr, "qemu: could not load prom '%s'\n", filename);
>> +            exit(1);
>> +        }
>> +    }
>> +    else if (kernel_filename == NULL) {
>> +        fprintf(stderr,"Can't read bios image %s\n", filename);
>> +        exit(1);
>> +    }
>> +
>> +    /* Can directly load an application. */
>> +    if (kernel_filename != NULL) {
>> +        long     kernel_size;
>> +        uint64_t entry;
>> +
>> +        kernel_size = load_elf(kernel_filename, NULL, NULL,&entry, NULL, NULL,
>> +                               1 /* big endian */, ELF_MACHINE, 0);
>> +        if (kernel_size<  0) {
>> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
>> +                    kernel_filename);
>> +            exit(1);
>> +        }
>> +        if (bios_size<= 0) {
>> +            /* If there is no bios/monitor, start the application.  */
>> +            env->pc = entry;
>> +            env->npc = entry + 4;
>> +            leon3_state.entry = entry;
>> +        }
>> +    }
>> +
>> +    /* Allocate timers */
>> +    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
>> +
>> +    /* Allocate uart */
>> +    if (serial_hds[0])
>> +        grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
>> +}
>> +
>> +QEMUMachine leon3_generic_machine = {
>> +    .name     = "leon3_generic",
>> +    .desc     = "Leon-3 generic",
>> +    .init     = leon3_generic_hw_init,
>> +    .use_scsi = 0,
>> +};
>> +
>> +static void leon3_machine_init(void)
>> +{
>> +    qemu_register_machine(&leon3_generic_machine);
>> +}
>> +
>> +machine_init(leon3_machine_init);
>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>> index 7e0d17c..6020ffd 100644
>> --- a/target-sparc/cpu.h
>> +++ b/target-sparc/cpu.h
>> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
>>   /* sun4m.c, sun4u.c */
>>   void cpu_check_irqs(CPUSPARCState *env);
>>
>> +/* grlib_irqmp.c */
>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno);
>> +
>> +/* leon3.c */
>> +void     leon3_shutdown(void);
>> +void     leon3_cache_control_st(target_ulong addr, uint64_t val, int size);
>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size);
>> +void     leon3_cache_control_int(void);
>> +
>> +
>>   #if defined (TARGET_SPARC64)
>>
>>   static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask)
>> diff --git a/target-sparc/helper.c b/target-sparc/helper.c
>> index e84c312..3bf990f 100644
>> --- a/target-sparc/helper.c
>> +++ b/target-sparc/helper.c
>> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
>>          .iu_version = 0xf3000000,
>>          .fpu_version = 4<<  17, /* FPU version 4 (Meiko) */
>>          .mmu_version = 0xf3000000,
>> -        .mmu_bm = 0x00004000,
>> +        .mmu_bm = 0x00000000,
>>          .mmu_ctpr_mask = 0x007ffff0,
>>          .mmu_cxr_mask = 0x0000003f,
>>          .mmu_sfsr_mask = 0xffffffff,
>> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
>> index be3c1e0..85df077 100644
>> --- a/target-sparc/op_helper.c
>> +++ b/target-sparc/op_helper.c
>> @@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
>>
>>      helper_check_align(addr, size - 1);
>>      switch (asi) {
>> -    case 2: /* SuperSparc MXCC registers */
>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>          switch (addr) {
>> +        case 0x00:          /* Leon3 Cache Control */
>> +        case 0x08:          /* Leon3 Instruction Cache config */
>> +        case 0x0C:          /* Leon3 Date Cache config */
>> +            ret = leon3_cache_control_ld(addr, size);
>> +            break;
>>          case 0x01c00a00: /* MXCC control register */
>>              if (size == 8)
>>                  ret = env->mxccregs[3];
>> @@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size)
>>   {
>>      helper_check_align(addr, size - 1);
>>      switch(asi) {
>> -    case 2: /* SuperSparc MXCC registers */
>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>          switch (addr) {
>> +        case 0x00:          /* Leon3 Cache Control */
>> +        case 0x08:          /* Leon3 Instruction Cache config */
>> +        case 0x0C:          /* Leon3 Date Cache config */
>> +            leon3_cache_control_st(addr, val, size);
>> +            break;
>> +
>>          case 0x01c00000: /* MXCC stream data register 0 */
>>              if (size == 8)
>>                  env->mxccdata[0] = val;
>> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
>>   {
>>      int cwp, intno = env->exception_index;
>>
>> +#if !defined(CONFIG_USER_ONLY)
>> +    /* Leon3 shutdown */
>> +    if (intno == 0x80&&  env->version == 0xf3000000) {
>> +        leon3_shutdown();
>> +    }
>
> This looks like a hack. Should a trap instruction initiate a shutdown?

Yes, on Leon3 "ta 0x0" initiates a shutdown.

>> +#endif
>> +
>>   #ifdef DEBUG_PCALL
>>      if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>          static int count;
>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>      env->pc = env->tbr;
>>      env->npc = env->pc + 4;
>>      env->exception_index = -1;
>> +
>> +#if !defined(CONFIG_USER_ONLY)
>> +    /* IRQ acknowledgment for Leon3 */
>> +    if (env->version == 0xf3000000&&  (intno&  ~15) == TT_EXTINT) {
>> +        grlib_irqmp_ack (env, intno);
>> +        leon3_cache_control_int();
>> +    }
>
> Like this. I don't think a CPU should immediately ack any incoming interrupts.

Leon3 does...

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support.
  2010-12-06 18:01             ` Blue Swirl
@ 2010-12-07 11:51               ` Fabien Chouteau
  2010-12-11  9:59                 ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-07 11:51 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/06/2010 07:01 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   hw/leon3.c               |    6 ++++++
>>   target-sparc/cpu.h       |    1 +
>>   target-sparc/machine.c   |    2 ++
>>   target-sparc/translate.c |   10 ++++++++++
>>   4 files changed, 19 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/leon3.c b/hw/leon3.c
>> index ba61081..9605ce8 100644
>> --- a/hw/leon3.c
>> +++ b/hw/leon3.c
>> @@ -187,6 +187,12 @@ static void main_cpu_reset(void *opaque)
>>         values */
>>      leon3_state.inst_cache_conf = 0x10220000;
>>      leon3_state.data_cache_conf = 0x18220000;
>> +
>> +    /* Asr17 for Leon3 mono-processor */
>> +    env->asr17&= 0<<  28;          /* CPU id */
>> +    env->asr17&= 1<<  8;           /* SPARC V8 multiply and divide available */
>> +    env->asr17&= env->nwindows -1; /* Number of implemented registers
>> +                                       windows */
>
> This is constant...
>
>>   }
>>
>>   static void leon3_generic_hw_init(ram_addr_t  ram_size,
>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>> index 6020ffd..36d49fc 100644
>> --- a/target-sparc/cpu.h
>> +++ b/target-sparc/cpu.h
>> @@ -341,6 +341,7 @@ typedef struct CPUSPARCState {
>>                            from PSR) */
>>   #if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
>>      uint32_t wim;      /* window invalid mask */
>> +    uint32_t asr17;    /* asr17 */
>
> ... so no new env fields are needed...
>
>>   #endif
>>      target_ulong tbr;  /* trap base register */
>>   #if !defined(TARGET_SPARC64)
>> diff --git a/target-sparc/machine.c b/target-sparc/machine.c
>> index 752e431..c530bd3 100644
>> --- a/target-sparc/machine.c
>> +++ b/target-sparc/machine.c
>> @@ -42,6 +42,7 @@ void cpu_save(QEMUFile *f, void *opaque)
>>      qemu_put_be32s(f,&env->pil_in);
>>   #ifndef TARGET_SPARC64
>>      qemu_put_be32s(f,&env->wim);
>> +    qemu_put_be32s(f,&env->asr17);
>
> ... there's also nothing to save/load...
>
>>      /* MMU */
>>      for (i = 0; i<  32; i++)
>>          qemu_put_be32s(f,&env->mmuregs[i]);
>> @@ -138,6 +139,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
>>      qemu_get_be32s(f,&env->pil_in);
>>   #ifndef TARGET_SPARC64
>>      qemu_get_be32s(f,&env->wim);
>> +    qemu_get_be32s(f,&env->asr17);
>>      /* MMU */
>>      for (i = 0; i<  32; i++)
>>          qemu_get_be32s(f,&env->mmuregs[i]);
>> diff --git a/target-sparc/translate.c b/target-sparc/translate.c
>> index 23f9519..65de614 100644
>> --- a/target-sparc/translate.c
>> +++ b/target-sparc/translate.c
>> @@ -58,6 +58,7 @@ static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver;
>>   static TCGv_i32 cpu_softint;
>>   #else
>>   static TCGv cpu_wim;
>> +static TCGv cpu_asr17;
>>   #endif
>>   /* local register indexes (only used inside old micro ops) */
>>   static TCGv cpu_tmp0;
>> @@ -2049,6 +2050,8 @@ static void disas_sparc_insn(DisasContext * dc)
>>                  rs1 = GET_FIELD(insn, 13, 17);
>>                  switch(rs1) {
>>                  case 0: /* rdy */
>> +                    gen_movl_TN_reg(rd, cpu_y);
>> +                    break;
>>   #ifndef TARGET_SPARC64
>>                  case 0x01 ... 0x0e: /* undefined in the SPARCv8
>>                                         manual, rdy on the microSPARC
>> @@ -2058,6 +2061,11 @@ static void disas_sparc_insn(DisasContext * dc)
>>                  case 0x10 ... 0x1f: /* implementation-dependent in the
>>                                         SPARCv8 manual, rdy on the
>>                                         microSPARC II */
>> +
>> +                    if (rs1 == 0x11) { /* Read %asr17 */
>> +                        gen_movl_TN_reg(rd, cpu_asr17);
>
> Instead:
> r_const = tcg_const_tl(asr constants  | dc->def->nwindows - 1);
> gen_movl_TN_reg(rd, r_const);
> tcg_temp_free(r_const);

OK for me, if it is acceptable to have this Leon3's specific behavior 
for all the SPARC32 CPUs.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3
  2010-12-06 18:12     ` Blue Swirl
@ 2010-12-07 17:43       ` Fabien Chouteau
  0 siblings, 0 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-07 17:43 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel, Artyom Tarasenko

On 12/06/2010 07:12 PM, Blue Swirl wrote:
> On Mon, Dec 6, 2010 at 3:07 PM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/06/2010 11:44 AM, Artyom Tarasenko wrote:
>>>
>>> On Mon, Dec 6, 2010 at 10:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> Hi everyone,
>>>> I'm glad to submit my first patches to the Qemu-devel list.
>>>>
>>>> This patch set introduces a new SPARC V8 machine: Leon3. It's an
>>>> open-source
>>>> VHDL System-On-Chip, well known in space industry (more information on
>>>> http://www.gaisler.com).
>>>
>>> Nice! Haven't looked into the code yet, but it's great to have someone
>>> who cares for V8.
>>
>> And if this patch is accepted, we will try to submit more machines like
>> erc32 and leon2.
>>
>>> Do you also have a firmware which runs on these machines?
>>>
>>
>> I can give you a binary running some basic tests.
>
> Is the binary generally available?

No it's just a program that I use to test my work.

> Otherwise it would be very hard to
> test any changes and the code would bitrot. I'm not sure we even want
> to support such machines.

I think we can find many people interested in Leon3, the processor is well
known in the space industry and used in many research projects. The only
emulator available is Tsim and it's not free nor open-source, so Qemu has a
real interest here.

> Are the sources available? That would help debugging.

I can give you the sources but it will be difficult for you to compile them.
It's an Ada program compiled with Gnat Pro for Leon3, using the Ravenscar
run-time. If you are not familiar with the Ada language, you can see the
Ravenscar run-time as a very small kernel providing basic tasking 
support for
safety critical applications.

>>>> Leon3 is made of multiple components available in the GrLib VHDL library.
>>>> Three devices are implemented: uart, timers and IRQ manager.
>>>> You can find code for these peripherals in the grlib_* files.
>>>>
>>>> Modifications have been done to the SPARC cpu emulation code to handle
>>>> Leon3's specific behavior:
>>>>   - IRQ management
>>>>   - Cache control
>>>>   - Asr17 (implementation-dependent Ancillary State Registers)
>>>
>>> Is it the only implementation-dependent asr in Leon3? Thought there were
>>> more.
>>>
>>
>> Yes, there's also asr19 for power-down, asr16 for FPU control and others for
>> hardware breakpoints.
>> These are not required for this first implementation, but If there's a need
>> for more ASRs, we may have to find a generic implementation to handle those
>> registers.
>
> So far I'd handle these in target-sparc/op_helper.c. If the registers
> are also available as MMIO like MXCC, then we should invent a way to
> handle both.

I don't see what you mean by "also available as MMIO", it's just regular
registers in the CPU, except that they are available for
implementation-dependent uses.


Thank you for your review ;)

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-07  9:55     ` Fabien Chouteau
@ 2010-12-08  8:30       ` Edgar E. Iglesias
  2010-12-08  9:39         ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Edgar E. Iglesias @ 2010-12-08  8:30 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: Blue Swirl, qemu-devel

On Tue, Dec 07, 2010 at 10:55:33AM +0100, Fabien Chouteau wrote:
> On 12/06/2010 06:12 PM, Blue Swirl wrote:
> > On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
> >>
> >> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
> >> ---
> >>   hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>   1 files changed, 448 insertions(+), 0 deletions(-)
> >>
> >> diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
> >> new file mode 100644
> >> index 0000000..41edbe4
> >> --- /dev/null
> >> +++ b/hw/grlib_gptimer.c
> >> @@ -0,0 +1,448 @@
> >> +/*
> >> + * QEMU GRLIB GPTimer Emulator
> >> + *
> >> + * Copyright (c) 2010 AdaCore
> >> + *
> >> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> >> + * of this software and associated documentation files (the "Software"), to deal
> >> + * in the Software without restriction, including without limitation the rights
> >> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> >> + * copies of the Software, and to permit persons to whom the Software is
> >> + * furnished to do so, subject to the following conditions:
> >> + *
> >> + * The above copyright notice and this permission notice shall be included in
> >> + * all copies or substantial portions of the Software.
> >> + *
> >> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> >> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> >> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> >> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> >> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> >> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> >> + * THE SOFTWARE.
> >> + */
> >> +
> >> +#include "sysbus.h"
> >> +#include "qemu-timer.h"
> >> +
> >> +#include "grlib.h"
> >> +
> >> +/* #define DEBUG_TIMER */
> >
> > The usual convention is
> > //#define DEBUG_TIMER
> > for easy editing.
> >
> 
> Actually, it's easier for me with the /* */, but OK.
> 
> > However, very often the much more powerful tracepoints can replace
> > debug statements.
> >
> >> +
> >> +#ifdef DEBUG_TIMER
> >> +#define DPRINTF(fmt, ...)                                       \
> >> +    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
> >> +#else
> >> +#define DPRINTF(fmt, ...)
> >> +#endif
> >> +
> >> +#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
> >> +#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
> >> +
> >> +#define GPTIMER_MAX_TIMERS 8
> >> +
> >> +/* GPTimer Config register fields */
> >> +#define GPTIMER_ENABLE      (1<<  0)
> >> +#define GPTIMER_RESTART     (1<<  1)
> >> +#define GPTIMER_LOAD        (1<<  2)
> >> +#define GPTIMER_INT_ENABLE  (1<<  3)
> >> +#define GPTIMER_INT_PENDING (1<<  4)
> >> +#define GPTIMER_CHAIN       (1<<  5) /* Not supported */
> >> +#define GPTIMER_DEBUG_HALT  (1<<  6) /* Not supported */
> >> +
> >> +/* Memory mapped register offsets */
> >> +#define SCALER_OFFSET         0x00
> >> +#define SCALER_RELOAD_OFFSET  0x04
> >> +#define CONFIG_OFFSET         0x08
> >> +#define COUNTER_OFFSET        0x00
> >> +#define COUNTER_RELOAD_OFFSET 0x04
> >> +#define TIMER_BASE            0x10
> >> +
> >> +typedef struct GPTimer     GPTimer;
> >> +typedef struct GPTimerUnit GPTimerUnit;
> >> +
> >> +struct GPTimer
> >> +{
> >> +    QEMUBH *bh;
> >> +    struct ptimer_state *ptimer;
> >> +
> >> +    qemu_irq     irq;
> >> +    int          id;
> >> +    GPTimerUnit *unit;
> >> +
> >> +    /* registers */
> >> +    uint32_t counter;
> >> +    uint32_t reload;
> >> +    uint32_t config;
> >> +};
> >> +
> >> +struct GPTimerUnit
> >> +{
> >> +    SysBusDevice  busdev;
> >> +
> >> +    uint32_t nr_timers;         /* Number of timers available */
> >> +    uint32_t freq_hz;           /* System frequency */
> >> +    uint32_t irq_line;          /* Base irq line */
> >> +
> >> +    GPTimer *timers;
> >> +
> >> +    /* registers */
> >> +    uint32_t scaler;
> >> +    uint32_t reload;
> >> +    uint32_t config;
> >> +};
> >> +
> >> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
> >> +                                  uint32_t            nr_timers,
> >> +                                  uint32_t            freq,
> >> +                                  qemu_irq           *cpu_irqs,
> >> +                                  int                 base_irq)
> >
> > This function belongs to leon3.c.
> 
> I don't see why. GPTimer is a peripheral and you may want to use it in 
> an other system.

This might depend a bit on taste, but I agree with Blue that we shouldn't
clutter the device models with this kind of instantiator helper calls.
IMO it's better to put them higher up (e.g board code or similar).

> 
> >> +{
> >> +    DeviceState *dev;
> >> +    int i;
> >> +_ir
> >> +    dev = qdev_create(NULL, "grlib,gptimer");
> >> +    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
> >> +    qdev_prop_set_uint32(dev, "frequency", freq);
> >> +    qdev_prop_set_uint32(dev, "irq-line", base_irq);
> >
> > Base irq is not device property, but part of board configuration. Thus
> > leon3.c should just pass&cpu_irqs[base_irq] to this function.
> >
> 
> I need this property to put the IRQ line in the configuration register. 
> Is there a way to get this number from a qemu_irq structure?

I don't think so. Also, I suspect that if you connect the device into
a larger interrupt structure (possibly with cascaded interrupt
controllers etc) the config value won't necessarily have much to do
with the particular qemu_irq object. So I think you need the
separate property. I might be missing something though.

Cheers

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-08  8:30       ` Edgar E. Iglesias
@ 2010-12-08  9:39         ` Fabien Chouteau
  2010-12-08 21:02           ` Edgar E. Iglesias
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-08  9:39 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: Blue Swirl, qemu-devel

On 12/08/2010 09:30 AM, Edgar E. Iglesias wrote:
> On Tue, Dec 07, 2010 at 10:55:33AM +0100, Fabien Chouteau wrote:
>> On 12/06/2010 06:12 PM, Blue Swirl wrote:
>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>   wrote:
>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>> ---
>>>>    hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    1 files changed, 448 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
>>>> new file mode 100644
>>>> index 0000000..41edbe4
>>>> --- /dev/null
>>>> +++ b/hw/grlib_gptimer.c
>>>> @@ -0,0 +1,448 @@
>>>> +/*
>>>> + * QEMU GRLIB GPTimer Emulator
>>>> + *
>>>> + * Copyright (c) 2010 AdaCore
>>>> + *
>>>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>>>> + * of this software and associated documentation files (the "Software"), to deal
>>>> + * in the Software without restriction, including without limitation the rights
>>>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>>>> + * copies of the Software, and to permit persons to whom the Software is
>>>> + * furnished to do so, subject to the following conditions:
>>>> + *
>>>> + * The above copyright notice and this permission notice shall be included in
>>>> + * all copies or substantial portions of the Software.
>>>> + *
>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>>>> + * THE SOFTWARE.
>>>> + */
>>>> +
>>>> +#include "sysbus.h"
>>>> +#include "qemu-timer.h"
>>>> +
>>>> +#include "grlib.h"
>>>> +
>>>> +/* #define DEBUG_TIMER */
>>> The usual convention is
>>> //#define DEBUG_TIMER
>>> for easy editing.
>>>
>> Actually, it's easier for me with the /* */, but OK.
>>
>>> However, very often the much more powerful tracepoints can replace
>>> debug statements.
>>>
>>>> +
>>>> +#ifdef DEBUG_TIMER
>>>> +#define DPRINTF(fmt, ...)                                       \
>>>> +    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#else
>>>> +#define DPRINTF(fmt, ...)
>>>> +#endif
>>>> +
>>>> +#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
>>>> +#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
>>>> +
>>>> +#define GPTIMER_MAX_TIMERS 8
>>>> +
>>>> +/* GPTimer Config register fields */
>>>> +#define GPTIMER_ENABLE      (1<<   0)
>>>> +#define GPTIMER_RESTART     (1<<   1)
>>>> +#define GPTIMER_LOAD        (1<<   2)
>>>> +#define GPTIMER_INT_ENABLE  (1<<   3)
>>>> +#define GPTIMER_INT_PENDING (1<<   4)
>>>> +#define GPTIMER_CHAIN       (1<<   5) /* Not supported */
>>>> +#define GPTIMER_DEBUG_HALT  (1<<   6) /* Not supported */
>>>> +
>>>> +/* Memory mapped register offsets */
>>>> +#define SCALER_OFFSET         0x00
>>>> +#define SCALER_RELOAD_OFFSET  0x04
>>>> +#define CONFIG_OFFSET         0x08
>>>> +#define COUNTER_OFFSET        0x00
>>>> +#define COUNTER_RELOAD_OFFSET 0x04
>>>> +#define TIMER_BASE            0x10
>>>> +
>>>> +typedef struct GPTimer     GPTimer;
>>>> +typedef struct GPTimerUnit GPTimerUnit;
>>>> +
>>>> +struct GPTimer
>>>> +{
>>>> +    QEMUBH *bh;
>>>> +    struct ptimer_state *ptimer;
>>>> +
>>>> +    qemu_irq     irq;
>>>> +    int          id;
>>>> +    GPTimerUnit *unit;
>>>> +
>>>> +    /* registers */
>>>> +    uint32_t counter;
>>>> +    uint32_t reload;
>>>> +    uint32_t config;
>>>> +};
>>>> +
>>>> +struct GPTimerUnit
>>>> +{
>>>> +    SysBusDevice  busdev;
>>>> +
>>>> +    uint32_t nr_timers;         /* Number of timers available */
>>>> +    uint32_t freq_hz;           /* System frequency */
>>>> +    uint32_t irq_line;          /* Base irq line */
>>>> +
>>>> +    GPTimer *timers;
>>>> +
>>>> +    /* registers */
>>>> +    uint32_t scaler;
>>>> +    uint32_t reload;
>>>> +    uint32_t config;
>>>> +};
>>>> +
>>>> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
>>>> +                                  uint32_t            nr_timers,
>>>> +                                  uint32_t            freq,
>>>> +                                  qemu_irq           *cpu_irqs,
>>>> +                                  int                 base_irq)
>>> This function belongs to leon3.c.
>> I don't see why. GPTimer is a peripheral and you may want to use it in
>> an other system.
> This might depend a bit on taste, but I agree with Blue that we shouldn't
> clutter the device models with this kind of instantiator helper calls.
> IMO it's better to put them higher up (e.g board code or similar).

Do you mean like Xilinx devices where the instantiators are in-lined 
functions in hw/xilinx.h?

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-08  9:39         ` Fabien Chouteau
@ 2010-12-08 21:02           ` Edgar E. Iglesias
  0 siblings, 0 replies; 51+ messages in thread
From: Edgar E. Iglesias @ 2010-12-08 21:02 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: Blue Swirl, qemu-devel

On Wed, Dec 08, 2010 at 10:39:43AM +0100, Fabien Chouteau wrote:
> On 12/08/2010 09:30 AM, Edgar E. Iglesias wrote:
> > On Tue, Dec 07, 2010 at 10:55:33AM +0100, Fabien Chouteau wrote:
> >> On 12/06/2010 06:12 PM, Blue Swirl wrote:
> >>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>   wrote:
> >>>> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
> >>>> +                                  uint32_t            nr_timers,
> >>>> +                                  uint32_t            freq,
> >>>> +                                  qemu_irq           *cpu_irqs,
> >>>> +                                  int                 base_irq)
> >>> This function belongs to leon3.c.
> >> I don't see why. GPTimer is a peripheral and you may want to use it in
> >> an other system.
> > This might depend a bit on taste, but I agree with Blue that we shouldn't
> > clutter the device models with this kind of instantiator helper calls.
> > IMO it's better to put them higher up (e.g board code or similar).
> 
> Do you mean like Xilinx devices where the instantiators are in-lined 
> functions in hw/xilinx.h?

Yes, that's one way. But if you only have a single board you can also just
put the function with the board code.

Cheers

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26 ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual Fabien Chouteau
  2010-12-06  9:26   ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Fabien Chouteau
  2010-12-06 17:12   ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer " Blue Swirl
@ 2010-12-08 22:51   ` Edgar E. Iglesias
  2010-12-09 10:04     ` Fabien Chouteau
  2 siblings, 1 reply; 51+ messages in thread
From: Edgar E. Iglesias @ 2010-12-08 22:51 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 06, 2010 at 10:26:02AM +0100, Fabien Chouteau wrote:
> 
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 448 insertions(+), 0 deletions(-)
> 
> diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
> new file mode 100644
> index 0000000..41edbe4
> --- /dev/null
> +++ b/hw/grlib_gptimer.c
> @@ -0,0 +1,448 @@
> +/*
> + * QEMU GRLIB GPTimer Emulator
> + *
> + * Copyright (c) 2010 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "qemu-timer.h"
> +
> +#include "grlib.h"
> +
> +/* #define DEBUG_TIMER */
> +
> +#ifdef DEBUG_TIMER
> +#define DPRINTF(fmt, ...)                                       \
> +    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...)
> +#endif
> +
> +#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
> +#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
> +
> +#define GPTIMER_MAX_TIMERS 8
> +
> +/* GPTimer Config register fields */
> +#define GPTIMER_ENABLE      (1 << 0)
> +#define GPTIMER_RESTART     (1 << 1)
> +#define GPTIMER_LOAD        (1 << 2)
> +#define GPTIMER_INT_ENABLE  (1 << 3)
> +#define GPTIMER_INT_PENDING (1 << 4)
> +#define GPTIMER_CHAIN       (1 << 5) /* Not supported */
> +#define GPTIMER_DEBUG_HALT  (1 << 6) /* Not supported */
> +
> +/* Memory mapped register offsets */
> +#define SCALER_OFFSET         0x00
> +#define SCALER_RELOAD_OFFSET  0x04
> +#define CONFIG_OFFSET         0x08
> +#define COUNTER_OFFSET        0x00
> +#define COUNTER_RELOAD_OFFSET 0x04
> +#define TIMER_BASE            0x10
> +
> +typedef struct GPTimer     GPTimer;
> +typedef struct GPTimerUnit GPTimerUnit;
> +
> +struct GPTimer
> +{
> +    QEMUBH *bh;
> +    struct ptimer_state *ptimer;
> +
> +    qemu_irq     irq;
> +    int          id;
> +    GPTimerUnit *unit;
> +
> +    /* registers */
> +    uint32_t counter;
> +    uint32_t reload;
> +    uint32_t config;
> +};
> +
> +struct GPTimerUnit
> +{
> +    SysBusDevice  busdev;
> +
> +    uint32_t nr_timers;         /* Number of timers available */
> +    uint32_t freq_hz;           /* System frequency */
> +    uint32_t irq_line;          /* Base irq line */
> +
> +    GPTimer *timers;
> +
> +    /* registers */
> +    uint32_t scaler;
> +    uint32_t reload;
> +    uint32_t config;
> +};
> +
> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
> +                                  uint32_t            nr_timers,
> +                                  uint32_t            freq,
> +                                  qemu_irq           *cpu_irqs,
> +                                  int                 base_irq)
> +{
> +    DeviceState *dev;
> +    int i;
> +
> +    dev = qdev_create(NULL, "grlib,gptimer");
> +    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
> +    qdev_prop_set_uint32(dev, "frequency", freq);
> +    qdev_prop_set_uint32(dev, "irq-line", base_irq);
> +
> +    if (qdev_init(dev)) {
> +        return NULL;
> +    }
> +
> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> +
> +    for (i = 0; i < nr_timers; i++)
> +        sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
> +
> +    return dev;
> +}
> +
> +static void grlib_gptimer_enable(GPTimer *timer)
> +{
> +    assert(timer != NULL);
> +
> +    DPRINTF("%s id:%d\n", __func__, timer->id);
> +
> +    ptimer_stop(timer->ptimer);
> +
> +    if (!(timer->config & GPTIMER_ENABLE)) {
> +        /* Timer disabled */
> +        DPRINTF("%s id:%d Timer disabled (config 0x%x)\n", __func__,
> +                timer->id, timer->config);
> +        return;
> +    }
> +
> +    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
> +       underflow. Set count + 1 to simulate the GPTimer behavior. */
> +
> +    DPRINTF("%s id:%d set count 0x%x and run\n",
> +            __func__,
> +            timer->id,
> +            timer->counter + 1);
> +
> +    ptimer_set_count(timer->ptimer, timer->counter + 1);
> +    ptimer_run(timer->ptimer, 1);
> +}
> +
> +static void grlib_gptimer_restart(GPTimer *timer)
> +{
> +    assert(timer != NULL);
> +
> +    DPRINTF("%s id:%d reload val: 0x%x\n", __func__, timer->id, timer->reload);
> +
> +    timer->counter = timer->reload;
> +    grlib_gptimer_enable(timer);
> +}
> +
> +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
> +{
> +    int i = 0;
> +    uint32_t value = 0;
> +
> +    assert(unit != NULL);
> +
> +
> +    if (scaler > 0) {
> +        value = unit->freq_hz / (scaler + 1);
> +    } else {
> +        value = unit->freq_hz;
> +    }
> +
> +    DPRINTF("%s scaler:%d freq:0x%x\n", __func__, scaler, value);
> +
> +    for (i = 0; i < unit->nr_timers; i++) {
> +        ptimer_set_freq(unit->timers[i].ptimer, value);
> +    }
> +}
> +
> +static void grlib_gptimer_hit(void *opaque)
> +{
> +    GPTimer *timer = opaque;
> +    assert(timer != NULL);
> +
> +    DPRINTF("%s id:%d\n", __func__, timer->id);
> +
> +    /* Timer expired */
> +
> +    if (timer->config & GPTIMER_INT_ENABLE) {
> +        /* Set the pending bit (only unset by write in the config register) */
> +        timer->config &= GPTIMER_INT_PENDING;

Do you mean |= GPTIMER_INT_PENDING here?


> +        qemu_set_irq(timer->irq, 1);

Hmm, this interrupt logic doesn't seem right. You are never clearing
the timer->irq line?

Once you've got the INT_PENDING bit logic right (more on that later),
I suspect you simply want to connect the irq state to the interrupt
pending bit in the config reg. e.g:

qemu_set_irq(timer->irq, !!(timer->config & GPTIMER_INT_PENDING));

You'll need to update the timer->irq state after every piece of logic
that potentially changes the int-pending bit.


> +    }
> +
> +    if (timer->config & GPTIMER_RESTART) {
> +        grlib_gptimer_restart(timer);
> +    }
> +}
> +
> +static uint32_t grlib_gptimer_readl (void *opaque, target_phys_addr_t addr)
> +{
> +    GPTimerUnit *unit  = opaque;
> +    uint32_t     value = 0;
> +
> +    addr &= 0xff;
> +
> +    assert(unit != NULL);
> +
> +    /* Unit registers */
> +    switch (addr)
> +    {
> +        case SCALER_OFFSET:
> +            DPRINTF("%s scaler: 0x%x\n", __func__, unit->scaler);
> +            return unit->scaler;
> +
> +        case SCALER_RELOAD_OFFSET:
> +            DPRINTF("%s reload: 0x%x\n", __func__, unit->reload);
> +            return unit->reload;
> +
> +        case CONFIG_OFFSET:
> +            DPRINTF("%s unit config: 0x%x\n", __func__, unit->config);
> +            return unit->config;
> +
> +        default:
> +            break;
> +    }
> +
> +    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
> +    int                id         = (addr - TIMER_BASE) / TIMER_BASE;
> +
> +    if (id >= 0 && id < unit->nr_timers) {
> +
> +        /* GPTimer registers */
> +        switch (timer_addr)
> +        {
> +            case COUNTER_OFFSET:
> +                value = ptimer_get_count (unit->timers[id].ptimer);
> +                DPRINTF("%s counter value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                return value;
> +
> +            case COUNTER_RELOAD_OFFSET:
> +                value = unit->timers[id].reload;
> +                DPRINTF("%s reload value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                return value;
> +
> +            case CONFIG_OFFSET:
> +                DPRINTF("%s config for timer %d: 0x%x\n",
> +                        __func__, id, unit->timers[id].config);
> +                return unit->timers[id].config;
> +
> +            default:
> +                break;
> +        }
> +
> +    }
> +
> +    DPRINTF("read unknown register 0x%04x\n", (int)addr);
> +    return 0;
> +}
> +
> +static void
> +grlib_gptimer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
> +{
> +    GPTimerUnit *unit = opaque;
> +
> +    addr &= 0xff;
> +
> +    assert(unit != NULL);
> +
> +    /* Unit registers */
> +    switch (addr)
> +    {
> +        case SCALER_OFFSET:
> +            value &= 0xFFFF; /* clean up the value */
> +            unit->scaler = value;
> +            return;
> +
> +        case SCALER_RELOAD_OFFSET:
> +            value &= 0xFFFF; /* clean up the value */
> +            unit->reload = value;
> +            grlib_gptimer_set_scaler(unit, value);
> +            return;
> +
> +        case CONFIG_OFFSET:
> +            /* Read Only (disable timer freeze not supported) */
> +            return;
> +
> +        default:
> +            break;
> +    }
> +
> +    target_phys_addr_t timer_addr = (addr % TIMER_BASE);
> +    int                id         = (addr - TIMER_BASE) / TIMER_BASE;
> +
> +    if (id >= 0 && id < unit->nr_timers) {
> +
> +        /* GPTimer registers */
> +        switch (timer_addr)
> +        {
> +            case COUNTER_OFFSET:
> +                DPRINTF("%s counter value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                unit->timers[id].counter = value;
> +                grlib_gptimer_enable(&unit->timers[id]);
> +                return;
> +
> +            case COUNTER_RELOAD_OFFSET:
> +                DPRINTF("%s reload value for timer %d: 0x%x\n",
> +                        __func__, id, value);
> +                unit->timers[id].reload = value;
> +                return;
> +
> +            case CONFIG_OFFSET:
> +                DPRINTF("%s config for timer %d: 0x%x\n", __func__, id, value);
> +
> +                unit->timers[id].config = value;

Writing a 1 to the interrupt-pending bit should clear it,
writing a zero should keep the IP bit untouched, e.g:

                   unit->timers[id].config &= GPTIMER_INT_PENDING;
                   unit->timers[id].config &= (~value) & GPTIMER_INT_PENDING;
                   unit->timers[id].config |= value & (~(GPTIMER_INT_PENDING | ... other magic bits...));


IIUC, writing to the config register and clearing the IP bit acks
the interrupt to the device. So it's a good time to update the
timer->irq state.


> +
> +                /* gptimer_restart calls gptimer_enable, so if "enable" and
> +                   "load" bits are present, we just have to call restart. */
> +
> +                if (value & GPTIMER_LOAD) {
> +                    grlib_gptimer_restart(&unit->timers[id]);
> +                } else if (value & GPTIMER_ENABLE) {
> +                    grlib_gptimer_enable(&unit->timers[id]);
> +                }
> +
> +                /* This fields must always be read as 0 */
> +                value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
> +
> +                unit->timers[id].config = value;
> +                return;
> +
> +            default:
> +                break;
> +        }
> +
> +    }
> +
> +    DPRINTF("write unknown register 0x%04x\n", (int)addr);
> +}
> +
> +static CPUReadMemoryFunc *grlib_gptimer_read[] = {
> +    NULL, NULL, grlib_gptimer_readl,
> +};
> +
> +static CPUWriteMemoryFunc *grlib_gptimer_write[] = {
> +    NULL, NULL, grlib_gptimer_writel,
> +};
> +
> +static void grlib_gptimer_reset(void *opaque)
> +{
> +    /* int          i    = 0; */
> +    /* GPTimerUnit *unit = (GPTimerUnit *)opaque; */
> +    /* assert(unit != NULL); */
> +
> +    /* unit->scaler = 0; */
> +    /* unit->reload = 0; */
> +    /* unit->config = 0; */
> +
> +    /* unit->config  = unit->nr_timers; */
> +    /* unit->config |= unit->irq_line << 3; */
> +    /* unit->config |= 1 << 8;     /\* separate interrupt *\/ */
> +    /* unit->config |= 1 << 9;     /\* Disable timer freeze *\/ */
> +
> +
> +    /* for (i = 0; i < unit->nr_timers; i++) { */
> +    /*     GPTimer *timer = &unit->timers[i]; */
> +
> +    /*     timer->counter = 0; */
> +    /*     timer->reload = 0; */
> +    /*     timer->config = 0; */
> +    /*     ptimer_stop(timer->ptimer); */
> +    /*     ptimer_set_count(timer->ptimer, 0); */
> +    /*     ptimer_set_freq(timer->ptimer, unit->freq_hz); */
> +    /* } */
> +
> +}
> +
> +static int grlib_gptimer_init(SysBusDevice *dev)
> +{
> +    GPTimerUnit  *unit = FROM_SYSBUS(typeof (*unit), dev);
> +    unsigned int  i;
> +    int           timer_regs;
> +
> +    assert(unit->nr_timers > 0);
> +    assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
> +    unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers);
> +
> +    for (i = 0; i < unit->nr_timers; i++) {
> +        GPTimer *timer = &unit->timers[i];
> +
> +        timer->unit   = unit;
> +        timer->bh     = qemu_bh_new(grlib_gptimer_hit, timer);
> +        timer->ptimer = ptimer_init(timer->bh);
> +        timer->id     = i;
> +
> +        /* One IRQ line for each timer */
> +        sysbus_init_irq(dev, &timer->irq);
> +
> +        ptimer_set_freq(timer->ptimer, unit->freq_hz);
> +    }
> +
> +    qemu_register_reset(grlib_gptimer_reset, unit);
> +
> +    timer_regs = cpu_register_io_memory(grlib_gptimer_read,
> +                                        grlib_gptimer_write,
> +                                        unit);
> +    if (timer_regs < 0) {
> +        return -1;
> +    }
> +
> +    sysbus_init_mmio(dev, UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers,
> +                     timer_regs);
> +    return 0;
> +}
> +
> +static SysBusDeviceInfo grlib_gptimer_info = {
> +    .init       = grlib_gptimer_init,
> +    .qdev.name  = "grlib,gptimer",
> +    .qdev.size  = sizeof(GPTimerUnit),
> +    .qdev.props = (Property[]) {
> +        {
> +            .name   = "frequency",
> +            .info   = &qdev_prop_uint32,
> +            .offset = offsetof(GPTimerUnit, freq_hz),
> +            .defval = (uint32_t[]) { 2 },
> +        },{
> +            .name   = "irq-line",
> +            .info   = &qdev_prop_uint32,
> +            .offset = offsetof(GPTimerUnit, irq_line),
> +            .defval = (uint32_t[]) { 8 },
> +        },{
> +            .name   = "nr-timers",
> +            .info   = &qdev_prop_uint32,
> +            .offset = offsetof(GPTimerUnit, nr_timers),
> +            .defval = (uint32_t[]) { 2 },
> +        },
> +        {/* end of list */}
> +    }
> +};
> +
> +static void grlib_gptimer_register(void)
> +{
> +    sysbus_register_withprop(&grlib_gptimer_info);
> +}
> +
> +device_init(grlib_gptimer_register)
> -- 
> 1.7.1
> 
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-08 22:51   ` Edgar E. Iglesias
@ 2010-12-09 10:04     ` Fabien Chouteau
  2010-12-09 10:22       ` Edgar E. Iglesias
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-09 10:04 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: qemu-devel

On 12/08/2010 11:51 PM, Edgar E. Iglesias wrote:
> On Mon, Dec 06, 2010 at 10:26:02AM +0100, Fabien Chouteau wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 files changed, 448 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
>> new file mode 100644
>> index 0000000..41edbe4
>> --- /dev/null
>> +++ b/hw/grlib_gptimer.c
>> @@ -0,0 +1,448 @@
>> +/*
>> + * QEMU GRLIB GPTimer Emulator
>> + *
>> + * Copyright (c) 2010 AdaCore
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +
>> +#include "sysbus.h"
>> +#include "qemu-timer.h"
>> +
>> +#include "grlib.h"
>> +
>> +/* #define DEBUG_TIMER */
>> +
>> +#ifdef DEBUG_TIMER
>> +#define DPRINTF(fmt, ...)                                       \
>> +    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...)
>> +#endif
>> +
>> +#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
>> +#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
>> +
>> +#define GPTIMER_MAX_TIMERS 8
>> +
>> +/* GPTimer Config register fields */
>> +#define GPTIMER_ENABLE      (1<<  0)
>> +#define GPTIMER_RESTART     (1<<  1)
>> +#define GPTIMER_LOAD        (1<<  2)
>> +#define GPTIMER_INT_ENABLE  (1<<  3)
>> +#define GPTIMER_INT_PENDING (1<<  4)
>> +#define GPTIMER_CHAIN       (1<<  5) /* Not supported */
>> +#define GPTIMER_DEBUG_HALT  (1<<  6) /* Not supported */
>> +
>> +/* Memory mapped register offsets */
>> +#define SCALER_OFFSET         0x00
>> +#define SCALER_RELOAD_OFFSET  0x04
>> +#define CONFIG_OFFSET         0x08
>> +#define COUNTER_OFFSET        0x00
>> +#define COUNTER_RELOAD_OFFSET 0x04
>> +#define TIMER_BASE            0x10
>> +
>> +typedef struct GPTimer     GPTimer;
>> +typedef struct GPTimerUnit GPTimerUnit;
>> +
>> +struct GPTimer
>> +{
>> +    QEMUBH *bh;
>> +    struct ptimer_state *ptimer;
>> +
>> +    qemu_irq     irq;
>> +    int          id;
>> +    GPTimerUnit *unit;
>> +
>> +    /* registers */
>> +    uint32_t counter;
>> +    uint32_t reload;
>> +    uint32_t config;
>> +};
>> +
>> +struct GPTimerUnit
>> +{
>> +    SysBusDevice  busdev;
>> +
>> +    uint32_t nr_timers;         /* Number of timers available */
>> +    uint32_t freq_hz;           /* System frequency */
>> +    uint32_t irq_line;          /* Base irq line */
>> +
>> +    GPTimer *timers;
>> +
>> +    /* registers */
>> +    uint32_t scaler;
>> +    uint32_t reload;
>> +    uint32_t config;
>> +};
>> +
>> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
>> +                                  uint32_t            nr_timers,
>> +                                  uint32_t            freq,
>> +                                  qemu_irq           *cpu_irqs,
>> +                                  int                 base_irq)
>> +{
>> +    DeviceState *dev;
>> +    int i;
>> +
>> +    dev = qdev_create(NULL, "grlib,gptimer");
>> +    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
>> +    qdev_prop_set_uint32(dev, "frequency", freq);
>> +    qdev_prop_set_uint32(dev, "irq-line", base_irq);
>> +
>> +    if (qdev_init(dev)) {
>> +        return NULL;
>> +    }
>> +
>> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
>> +
>> +    for (i = 0; i<  nr_timers; i++)
>> +        sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
>> +
>> +    return dev;
>> +}
>> +
>> +static void grlib_gptimer_enable(GPTimer *timer)
>> +{
>> +    assert(timer != NULL);
>> +
>> +    DPRINTF("%s id:%d\n", __func__, timer->id);
>> +
>> +    ptimer_stop(timer->ptimer);
>> +
>> +    if (!(timer->config&  GPTIMER_ENABLE)) {
>> +        /* Timer disabled */
>> +        DPRINTF("%s id:%d Timer disabled (config 0x%x)\n", __func__,
>> +                timer->id, timer->config);
>> +        return;
>> +    }
>> +
>> +    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
>> +       underflow. Set count + 1 to simulate the GPTimer behavior. */
>> +
>> +    DPRINTF("%s id:%d set count 0x%x and run\n",
>> +            __func__,
>> +            timer->id,
>> +            timer->counter + 1);
>> +
>> +    ptimer_set_count(timer->ptimer, timer->counter + 1);
>> +    ptimer_run(timer->ptimer, 1);
>> +}
>> +
>> +static void grlib_gptimer_restart(GPTimer *timer)
>> +{
>> +    assert(timer != NULL);
>> +
>> +    DPRINTF("%s id:%d reload val: 0x%x\n", __func__, timer->id, timer->reload);
>> +
>> +    timer->counter = timer->reload;
>> +    grlib_gptimer_enable(timer);
>> +}
>> +
>> +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
>> +{
>> +    int i = 0;
>> +    uint32_t value = 0;
>> +
>> +    assert(unit != NULL);
>> +
>> +
>> +    if (scaler>  0) {
>> +        value = unit->freq_hz / (scaler + 1);
>> +    } else {
>> +        value = unit->freq_hz;
>> +    }
>> +
>> +    DPRINTF("%s scaler:%d freq:0x%x\n", __func__, scaler, value);
>> +
>> +    for (i = 0; i<  unit->nr_timers; i++) {
>> +        ptimer_set_freq(unit->timers[i].ptimer, value);
>> +    }
>> +}
>> +
>> +static void grlib_gptimer_hit(void *opaque)
>> +{
>> +    GPTimer *timer = opaque;
>> +    assert(timer != NULL);
>> +
>> +    DPRINTF("%s id:%d\n", __func__, timer->id);
>> +
>> +    /* Timer expired */
>> +
>> +    if (timer->config&  GPTIMER_INT_ENABLE) {
>> +        /* Set the pending bit (only unset by write in the config register) */
>> +        timer->config&= GPTIMER_INT_PENDING;
>
> Do you mean |= GPTIMER_INT_PENDING here?

Yep, thanks.

>> +        qemu_set_irq(timer->irq, 1);
>
> Hmm, this interrupt logic doesn't seem right. You are never clearing
> the timer->irq line?
>
> Once you've got the INT_PENDING bit logic right (more on that later),
> I suspect you simply want to connect the irq state to the interrupt
> pending bit in the config reg. e.g:
>
> qemu_set_irq(timer->irq, !!(timer->config&  GPTIMER_INT_PENDING));
>
> You'll need to update the timer->irq state after every piece of logic
> that potentially changes the int-pending bit.
>

GrLib peripherals just throw interrupts and never expect any kind of
acknowledgment, that's why the interrupt pending bit has to be cleared by
software. IRQs are acknowledged by the CPU at the beginning of the trap
process.

The interrupt pending bit is only useful when all the timers use the 
same IRQ
line and you want to know which one expired. For instance, our software 
never
read or clear it at all.

> writing a zero should keep the IP bit untouched, e.g:
>
>                     unit->timers[id].config&= GPTIMER_INT_PENDING;
>                     unit->timers[id].config&= (~value)&  GPTIMER_INT_PENDING;
>                     unit->timers[id].config |= value&  (~(GPTIMER_INT_PENDING | ... other magic bits...));
>

Right, I've missed the "write 1 to clear" part, but I don't understand 
your code.
I prefer something clearer, like:

if (value & GPTIMER_INT_PENDING) {
     /* clear pending bit */
     value &= ~GPTIMER_INT_PENDING;
} else {
     /* keep pending bit */
     value |= unit->timers[id].config & GPTIMER_INT_PENDING;
}

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
  2010-12-09 10:04     ` Fabien Chouteau
@ 2010-12-09 10:22       ` Edgar E. Iglesias
  0 siblings, 0 replies; 51+ messages in thread
From: Edgar E. Iglesias @ 2010-12-09 10:22 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Thu, Dec 09, 2010 at 11:04:58AM +0100, Fabien Chouteau wrote:
> On 12/08/2010 11:51 PM, Edgar E. Iglesias wrote:
> > On Mon, Dec 06, 2010 at 10:26:02AM +0100, Fabien Chouteau wrote:
> >>
> >> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
> >> ---
> >>   hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>   1 files changed, 448 insertions(+), 0 deletions(-)
> >>
> >> diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
> >> new file mode 100644
> >> index 0000000..41edbe4
> >> --- /dev/null
> >> +++ b/hw/grlib_gptimer.c
> >> @@ -0,0 +1,448 @@
> >> +/*
> >> + * QEMU GRLIB GPTimer Emulator
> >> + *
> >> + * Copyright (c) 2010 AdaCore
> >> + *
> >> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> >> + * of this software and associated documentation files (the "Software"), to deal
> >> + * in the Software without restriction, including without limitation the rights
> >> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> >> + * copies of the Software, and to permit persons to whom the Software is
> >> + * furnished to do so, subject to the following conditions:
> >> + *
> >> + * The above copyright notice and this permission notice shall be included in
> >> + * all copies or substantial portions of the Software.
> >> + *
> >> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> >> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> >> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> >> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> >> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> >> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> >> + * THE SOFTWARE.
> >> + */
> >> +
> >> +#include "sysbus.h"
> >> +#include "qemu-timer.h"
> >> +
> >> +#include "grlib.h"
> >> +
> >> +/* #define DEBUG_TIMER */
> >> +
> >> +#ifdef DEBUG_TIMER
> >> +#define DPRINTF(fmt, ...)                                       \
> >> +    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
> >> +#else
> >> +#define DPRINTF(fmt, ...)
> >> +#endif
> >> +
> >> +#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
> >> +#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
> >> +
> >> +#define GPTIMER_MAX_TIMERS 8
> >> +
> >> +/* GPTimer Config register fields */
> >> +#define GPTIMER_ENABLE      (1<<  0)
> >> +#define GPTIMER_RESTART     (1<<  1)
> >> +#define GPTIMER_LOAD        (1<<  2)
> >> +#define GPTIMER_INT_ENABLE  (1<<  3)
> >> +#define GPTIMER_INT_PENDING (1<<  4)
> >> +#define GPTIMER_CHAIN       (1<<  5) /* Not supported */
> >> +#define GPTIMER_DEBUG_HALT  (1<<  6) /* Not supported */
> >> +
> >> +/* Memory mapped register offsets */
> >> +#define SCALER_OFFSET         0x00
> >> +#define SCALER_RELOAD_OFFSET  0x04
> >> +#define CONFIG_OFFSET         0x08
> >> +#define COUNTER_OFFSET        0x00
> >> +#define COUNTER_RELOAD_OFFSET 0x04
> >> +#define TIMER_BASE            0x10
> >> +
> >> +typedef struct GPTimer     GPTimer;
> >> +typedef struct GPTimerUnit GPTimerUnit;
> >> +
> >> +struct GPTimer
> >> +{
> >> +    QEMUBH *bh;
> >> +    struct ptimer_state *ptimer;
> >> +
> >> +    qemu_irq     irq;
> >> +    int          id;
> >> +    GPTimerUnit *unit;
> >> +
> >> +    /* registers */
> >> +    uint32_t counter;
> >> +    uint32_t reload;
> >> +    uint32_t config;
> >> +};
> >> +
> >> +struct GPTimerUnit
> >> +{
> >> +    SysBusDevice  busdev;
> >> +
> >> +    uint32_t nr_timers;         /* Number of timers available */
> >> +    uint32_t freq_hz;           /* System frequency */
> >> +    uint32_t irq_line;          /* Base irq line */
> >> +
> >> +    GPTimer *timers;
> >> +
> >> +    /* registers */
> >> +    uint32_t scaler;
> >> +    uint32_t reload;
> >> +    uint32_t config;
> >> +};
> >> +
> >> +DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
> >> +                                  uint32_t            nr_timers,
> >> +                                  uint32_t            freq,
> >> +                                  qemu_irq           *cpu_irqs,
> >> +                                  int                 base_irq)
> >> +{
> >> +    DeviceState *dev;
> >> +    int i;
> >> +
> >> +    dev = qdev_create(NULL, "grlib,gptimer");
> >> +    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
> >> +    qdev_prop_set_uint32(dev, "frequency", freq);
> >> +    qdev_prop_set_uint32(dev, "irq-line", base_irq);
> >> +
> >> +    if (qdev_init(dev)) {
> >> +        return NULL;
> >> +    }
> >> +
> >> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> >> +
> >> +    for (i = 0; i<  nr_timers; i++)
> >> +        sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
> >> +
> >> +    return dev;
> >> +}
> >> +
> >> +static void grlib_gptimer_enable(GPTimer *timer)
> >> +{
> >> +    assert(timer != NULL);
> >> +
> >> +    DPRINTF("%s id:%d\n", __func__, timer->id);
> >> +
> >> +    ptimer_stop(timer->ptimer);
> >> +
> >> +    if (!(timer->config&  GPTIMER_ENABLE)) {
> >> +        /* Timer disabled */
> >> +        DPRINTF("%s id:%d Timer disabled (config 0x%x)\n", __func__,
> >> +                timer->id, timer->config);
> >> +        return;
> >> +    }
> >> +
> >> +    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
> >> +       underflow. Set count + 1 to simulate the GPTimer behavior. */
> >> +
> >> +    DPRINTF("%s id:%d set count 0x%x and run\n",
> >> +            __func__,
> >> +            timer->id,
> >> +            timer->counter + 1);
> >> +
> >> +    ptimer_set_count(timer->ptimer, timer->counter + 1);
> >> +    ptimer_run(timer->ptimer, 1);
> >> +}
> >> +
> >> +static void grlib_gptimer_restart(GPTimer *timer)
> >> +{
> >> +    assert(timer != NULL);
> >> +
> >> +    DPRINTF("%s id:%d reload val: 0x%x\n", __func__, timer->id, timer->reload);
> >> +
> >> +    timer->counter = timer->reload;
> >> +    grlib_gptimer_enable(timer);
> >> +}
> >> +
> >> +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
> >> +{
> >> +    int i = 0;
> >> +    uint32_t value = 0;
> >> +
> >> +    assert(unit != NULL);
> >> +
> >> +
> >> +    if (scaler>  0) {
> >> +        value = unit->freq_hz / (scaler + 1);
> >> +    } else {
> >> +        value = unit->freq_hz;
> >> +    }
> >> +
> >> +    DPRINTF("%s scaler:%d freq:0x%x\n", __func__, scaler, value);
> >> +
> >> +    for (i = 0; i<  unit->nr_timers; i++) {
> >> +        ptimer_set_freq(unit->timers[i].ptimer, value);
> >> +    }
> >> +}
> >> +
> >> +static void grlib_gptimer_hit(void *opaque)
> >> +{
> >> +    GPTimer *timer = opaque;
> >> +    assert(timer != NULL);
> >> +
> >> +    DPRINTF("%s id:%d\n", __func__, timer->id);
> >> +
> >> +    /* Timer expired */
> >> +
> >> +    if (timer->config&  GPTIMER_INT_ENABLE) {
> >> +        /* Set the pending bit (only unset by write in the config register) */
> >> +        timer->config&= GPTIMER_INT_PENDING;
> >
> > Do you mean |= GPTIMER_INT_PENDING here?
> 
> Yep, thanks.
> 
> >> +        qemu_set_irq(timer->irq, 1);
> >
> > Hmm, this interrupt logic doesn't seem right. You are never clearing
> > the timer->irq line?
> >
> > Once you've got the INT_PENDING bit logic right (more on that later),
> > I suspect you simply want to connect the irq state to the interrupt
> > pending bit in the config reg. e.g:
> >
> > qemu_set_irq(timer->irq, !!(timer->config&  GPTIMER_INT_PENDING));
> >
> > You'll need to update the timer->irq state after every piece of logic
> > that potentially changes the int-pending bit.
> >
> 
> GrLib peripherals just throw interrupts and never expect any kind of
> acknowledgment, that's why the interrupt pending bit has to be cleared by
> software. IRQs are acknowledged by the CPU at the beginning of the trap
> process.

After a quick look at the manuals it wasn't clear to me if it generates
interrupts as pulses or if the line is directly connected to the IP bit. 

I think you either need the logic I described previously or you maybe
want to use qemu_irq_pulse to signal an interrupt pulse.


> The interrupt pending bit is only useful when all the timers use the 
> same IRQ
> line and you want to know which one expired. For instance, our software 
> never
> read or clear it at all.

If this is the case, more specifically that you don't need to clear the
timer specific IP bits in the interrupt handlers unless using the shared
irq-line mode, then that points to the interrupt line not beeing
connected to the IP bit. Then you should probably use qemu_irq_pulse
to signal the interrupt.

Note that this decision is tighly coupled with how the interrupt
controller works.


> > writing a zero should keep the IP bit untouched, e.g:
> >
> >                     unit->timers[id].config&= GPTIMER_INT_PENDING;
> >                     unit->timers[id].config&= (~value)&  GPTIMER_INT_PENDING;
> >                     unit->timers[id].config |= value&  (~(GPTIMER_INT_PENDING | ... other magic bits...));
> >
> 
> Right, I've missed the "write 1 to clear" part, but I don't understand 
> your code.
> I prefer something clearer, like:
> 
> if (value & GPTIMER_INT_PENDING) {
>      /* clear pending bit */
>      value &= ~GPTIMER_INT_PENDING;
> } else {
>      /* keep pending bit */
>      value |= unit->timers[id].config & GPTIMER_INT_PENDING;
> }

OK

Cheers

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-06  9:26   ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Fabien Chouteau
  2010-12-06  9:26     ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART " Fabien Chouteau
  2010-12-06 17:25     ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Blue Swirl
@ 2010-12-09 10:32     ` Edgar E. Iglesias
  2010-12-09 11:03       ` Fabien Chouteau
  2 siblings, 1 reply; 51+ messages in thread
From: Edgar E. Iglesias @ 2010-12-09 10:32 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 06, 2010 at 10:26:03AM +0100, Fabien Chouteau wrote:
> 
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  hw/grlib_irqmp.c |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 416 insertions(+), 0 deletions(-)
> 
> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
> new file mode 100644
> index 0000000..69e1553
> --- /dev/null
> +++ b/hw/grlib_irqmp.c
> @@ -0,0 +1,416 @@
> +/*
> + * QEMU GRLIB IRQMP Emulator
> + *
> + * (Multiprocessor and extended interrupt not supported)
> + *
> + * Copyright (c) 2010 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "cpu.h"
> +
> +#include "grlib.h"
> +
> +/* #define DEBUG_IRQ */
> +
> +#ifdef DEBUG_IRQ
> +#define DPRINTF(fmt, ...)                                       \
> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...)
> +#endif
> +
> +#define IRQMP_MAX_CPU 16
> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
> +
> +/* Memory mapped register offsets */
> +#define LEVEL_OFFSET     0x00
> +#define PENDING_OFFSET   0x04
> +#define FORCE0_OFFSET    0x08
> +#define CLEAR_OFFSET     0x0C
> +#define MP_STATUS_OFFSET 0x10
> +#define BROADCAST_OFFSET 0x14
> +#define MASK_OFFSET      0x40
> +#define FORCE_OFFSET     0x80
> +#define EXTENDED_OFFSET  0xC0
> +
> +typedef struct IRQMP
> +{
> +    SysBusDevice busdev;
> +
> +    CPUSPARCState *env;
> +} IRQMP;
> +
> +typedef struct IRQMPState
> +{
> +    uint32_t level;
> +    uint32_t pending;
> +    uint32_t clear;
> +    uint32_t broadcast;
> +
> +    uint32_t mask[IRQMP_MAX_CPU];
> +    uint32_t force[IRQMP_MAX_CPU];
> +    uint32_t extended[IRQMP_MAX_CPU];
> +
> +    IRQMP    *parent;
> +} IRQMPState;
> +
> +IRQMPState grlib_irqmp_state;
> +
> +void grlib_irqmp_set_irq(void *opaque, int irq, int level);
> +
> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
> +                                CPUState            *env,
> +                                qemu_irq           **cpu_irqs,
> +                                uint32_t             nr_irqs)
> +{
> +    DeviceState *dev;
> +
> +    assert(cpu_irqs != NULL);
> +
> +    dev = qdev_create(NULL, "grlib,irqmp");
> +    qdev_prop_set_ptr(dev, "cpustate", env);
> +
> +    if (qdev_init(dev)) {
> +        return NULL;
> +    }
> +
> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> +
> +    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
> +                                   &grlib_irqmp_state,
> +                                   nr_irqs);
> +
> +    return dev;
> +}
> +
> +static void grlib_irqmp_check_irqs(CPUState *env)
> +{
> +    uint32_t pend   = 0;
> +    uint32_t level0 = 0;
> +    uint32_t level1 = 0;
> +
> +    assert(env != NULL);
> +
> +    /* IRQ for CPU 0 (no SMP support) */
> +    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
> +        & grlib_irqmp_state.mask[0];
> +
> +
> +    level0 = pend & ~grlib_irqmp_state.level;
> +    level1 = pend &  grlib_irqmp_state.level;
> +
> +    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n",
> +            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
> +            grlib_irqmp_state.mask[0], level1, level0);
> +
> +    /* Trigger level1 interrupt first and level0 if there is no level1 */
> +    if (level1 != 0) {
> +        env->pil_in = level1;
> +    } else {
> +        env->pil_in = level0;
> +    }
> +
> +    if (env->pil_in && (env->interrupt_index == 0 ||
> +                        (env->interrupt_index & ~15) == TT_EXTINT)) {
> +        unsigned int i;
> +
> +        for (i = 15; i > 0; i--) {
> +            if (env->pil_in & (1 << i)) {
> +                int old_interrupt = env->interrupt_index;
> +
> +                env->interrupt_index = TT_EXTINT | i;
> +                if (old_interrupt != env->interrupt_index) {
> +                    DPRINTF("Set CPU IRQ %d\n", i);
> +                    cpu_interrupt(env, CPU_INTERRUPT_HARD);
> +                }
> +                break;
> +            }
> +        }
> +    } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) {
> +        DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index & 15);
> +        env->interrupt_index = 0;
> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
> +    }
> +}
> +
> +void grlib_irqmp_ack(CPUSPARCState *env, int intno)
> +{
> +    assert(env != NULL);
> +
> +    uint32_t mask;
> +
> +    intno &= 15;
> +    mask = 1 << intno;
> +
> +    DPRINTF("grlib_irqmp_ack %d\n", intno);
> +
> +    /* Clear registers */
> +    grlib_irqmp_state.pending  &= ~mask;
> +    grlib_irqmp_state.force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
> +
> +    grlib_irqmp_check_irqs(env);
> +}
> +
> +void grlib_irqmp_set_irq(void *opaque, int irq, int level)
> +{
> +    IRQMPState *s = opaque;
> +    int         i = 0;
> +
> +    assert(opaque != NULL);
> +    assert(s->parent != NULL);
> +
> +    if (level) {
> +        DPRINTF("Raise CPU IRQ %d\n", irq);
> +
> +        if (s->broadcast & 1 << irq) {
> +            /* Broadcasted IRQ */
> +            for (i = 0; i < IRQMP_MAX_CPU; i++) {
> +                s->force[i] |= 1 << irq;
> +            }
> +        } else {
> +            s->pending |= 1 << irq;
> +        }
> +        grlib_irqmp_check_irqs(s->parent->env);
> +
> +    } else {
> +
> +        DPRINTF("Lower CPU IRQ %d\n", irq);
> +        if (s->broadcast & 1 << irq) {
> +            /* Broadcasted IRQ */
> +            for (i = 0; i < IRQMP_MAX_CPU; i++) {
> +                s->force[i] &= ~(1 << irq);
> +            }
> +        } else {
> +            s->pending &= ~(1 << irq);
> +        }

If you use the edge triggered interrupt model in the devices, then you
shouldn't clear the pending bit here. A pulse from the device should
set it and it should only get cleared when the CPU acks it.

The model you've coded here indicates that the devices use a level
triggered approach. And the clearing of the pending bit in
grlib_irqmp_ack becomes meaningless...

Cheers

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-09 10:32     ` Edgar E. Iglesias
@ 2010-12-09 11:03       ` Fabien Chouteau
  2010-12-09 11:06         ` Edgar E. Iglesias
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-09 11:03 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: qemu-devel

On 12/09/2010 11:32 AM, Edgar E. Iglesias wrote:
> On Mon, Dec 06, 2010 at 10:26:03AM +0100, Fabien Chouteau wrote:
>>
>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> ---
>>   hw/grlib_irqmp.c |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 files changed, 416 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
>> new file mode 100644
>> index 0000000..69e1553
>> --- /dev/null
>> +++ b/hw/grlib_irqmp.c
>> @@ -0,0 +1,416 @@
>> +/*
>> + * QEMU GRLIB IRQMP Emulator
>> + *
>> + * (Multiprocessor and extended interrupt not supported)
>> + *
>> + * Copyright (c) 2010 AdaCore
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +
>> +#include "sysbus.h"
>> +#include "cpu.h"
>> +
>> +#include "grlib.h"
>> +
>> +/* #define DEBUG_IRQ */
>> +
>> +#ifdef DEBUG_IRQ
>> +#define DPRINTF(fmt, ...)                                       \
>> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...)
>> +#endif
>> +
>> +#define IRQMP_MAX_CPU 16
>> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
>> +
>> +/* Memory mapped register offsets */
>> +#define LEVEL_OFFSET     0x00
>> +#define PENDING_OFFSET   0x04
>> +#define FORCE0_OFFSET    0x08
>> +#define CLEAR_OFFSET     0x0C
>> +#define MP_STATUS_OFFSET 0x10
>> +#define BROADCAST_OFFSET 0x14
>> +#define MASK_OFFSET      0x40
>> +#define FORCE_OFFSET     0x80
>> +#define EXTENDED_OFFSET  0xC0
>> +
>> +typedef struct IRQMP
>> +{
>> +    SysBusDevice busdev;
>> +
>> +    CPUSPARCState *env;
>> +} IRQMP;
>> +
>> +typedef struct IRQMPState
>> +{
>> +    uint32_t level;
>> +    uint32_t pending;
>> +    uint32_t clear;
>> +    uint32_t broadcast;
>> +
>> +    uint32_t mask[IRQMP_MAX_CPU];
>> +    uint32_t force[IRQMP_MAX_CPU];
>> +    uint32_t extended[IRQMP_MAX_CPU];
>> +
>> +    IRQMP    *parent;
>> +} IRQMPState;
>> +
>> +IRQMPState grlib_irqmp_state;
>> +
>> +void grlib_irqmp_set_irq(void *opaque, int irq, int level);
>> +
>> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
>> +                                CPUState            *env,
>> +                                qemu_irq           **cpu_irqs,
>> +                                uint32_t             nr_irqs)
>> +{
>> +    DeviceState *dev;
>> +
>> +    assert(cpu_irqs != NULL);
>> +
>> +    dev = qdev_create(NULL, "grlib,irqmp");
>> +    qdev_prop_set_ptr(dev, "cpustate", env);
>> +
>> +    if (qdev_init(dev)) {
>> +        return NULL;
>> +    }
>> +
>> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
>> +
>> +    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
>> +&grlib_irqmp_state,
>> +                                   nr_irqs);
>> +
>> +    return dev;
>> +}
>> +
>> +static void grlib_irqmp_check_irqs(CPUState *env)
>> +{
>> +    uint32_t pend   = 0;
>> +    uint32_t level0 = 0;
>> +    uint32_t level1 = 0;
>> +
>> +    assert(env != NULL);
>> +
>> +    /* IRQ for CPU 0 (no SMP support) */
>> +    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
>> +&  grlib_irqmp_state.mask[0];
>> +
>> +
>> +    level0 = pend&  ~grlib_irqmp_state.level;
>> +    level1 = pend&   grlib_irqmp_state.level;
>> +
>> +    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n",
>> +            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
>> +            grlib_irqmp_state.mask[0], level1, level0);
>> +
>> +    /* Trigger level1 interrupt first and level0 if there is no level1 */
>> +    if (level1 != 0) {
>> +        env->pil_in = level1;
>> +    } else {
>> +        env->pil_in = level0;
>> +    }
>> +
>> +    if (env->pil_in&&  (env->interrupt_index == 0 ||
>> +                        (env->interrupt_index&  ~15) == TT_EXTINT)) {
>> +        unsigned int i;
>> +
>> +        for (i = 15; i>  0; i--) {
>> +            if (env->pil_in&  (1<<  i)) {
>> +                int old_interrupt = env->interrupt_index;
>> +
>> +                env->interrupt_index = TT_EXTINT | i;
>> +                if (old_interrupt != env->interrupt_index) {
>> +                    DPRINTF("Set CPU IRQ %d\n", i);
>> +                    cpu_interrupt(env, CPU_INTERRUPT_HARD);
>> +                }
>> +                break;
>> +            }
>> +        }
>> +    } else if (!env->pil_in&&  (env->interrupt_index&  ~15) == TT_EXTINT) {
>> +        DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index&  15);
>> +        env->interrupt_index = 0;
>> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
>> +    }
>> +}
>> +
>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno)
>> +{
>> +    assert(env != NULL);
>> +
>> +    uint32_t mask;
>> +
>> +    intno&= 15;
>> +    mask = 1<<  intno;
>> +
>> +    DPRINTF("grlib_irqmp_ack %d\n", intno);
>> +
>> +    /* Clear registers */
>> +    grlib_irqmp_state.pending&= ~mask;
>> +    grlib_irqmp_state.force[0]&= ~mask; /* Only CPU 0 (No SMP support) */
>> +
>> +    grlib_irqmp_check_irqs(env);
>> +}
>> +
>> +void grlib_irqmp_set_irq(void *opaque, int irq, int level)
>> +{
>> +    IRQMPState *s = opaque;
>> +    int         i = 0;
>> +
>> +    assert(opaque != NULL);
>> +    assert(s->parent != NULL);
>> +
>> +    if (level) {
>> +        DPRINTF("Raise CPU IRQ %d\n", irq);
>> +
>> +        if (s->broadcast&  1<<  irq) {
>> +            /* Broadcasted IRQ */
>> +            for (i = 0; i<  IRQMP_MAX_CPU; i++) {
>> +                s->force[i] |= 1<<  irq;
>> +            }
>> +        } else {
>> +            s->pending |= 1<<  irq;
>> +        }
>> +        grlib_irqmp_check_irqs(s->parent->env);
>> +
>> +    } else {
>> +
>> +        DPRINTF("Lower CPU IRQ %d\n", irq);
>> +        if (s->broadcast&  1<<  irq) {
>> +            /* Broadcasted IRQ */
>> +            for (i = 0; i<  IRQMP_MAX_CPU; i++) {
>> +                s->force[i]&= ~(1<<  irq);
>> +            }
>> +        } else {
>> +            s->pending&= ~(1<<  irq);
>> +        }
>
> If you use the edge triggered interrupt model in the devices, then you
> shouldn't clear the pending bit here. A pulse from the device should
> set it and it should only get cleared when the CPU acks it.
>
> The model you've coded here indicates that the devices use a level
> triggered approach. And the clearing of the pending bit in
> grlib_irqmp_ack becomes meaningless...

OK, so I use qemu_irq_pulse and do nothing in grlib_irqmp_set_irq when 
level == 0...

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-09 11:03       ` Fabien Chouteau
@ 2010-12-09 11:06         ` Edgar E. Iglesias
  2010-12-09 11:32           ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Edgar E. Iglesias @ 2010-12-09 11:06 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Thu, Dec 09, 2010 at 12:03:35PM +0100, Fabien Chouteau wrote:
> On 12/09/2010 11:32 AM, Edgar E. Iglesias wrote:
> > On Mon, Dec 06, 2010 at 10:26:03AM +0100, Fabien Chouteau wrote:
> >>
> >> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
> >> ---
> >>   hw/grlib_irqmp.c |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>   1 files changed, 416 insertions(+), 0 deletions(-)
> >>
> >> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
> >> new file mode 100644
> >> index 0000000..69e1553
> >> --- /dev/null
> >> +++ b/hw/grlib_irqmp.c
> >> @@ -0,0 +1,416 @@
> >> +/*
> >> + * QEMU GRLIB IRQMP Emulator
> >> + *
> >> + * (Multiprocessor and extended interrupt not supported)
> >> + *
> >> + * Copyright (c) 2010 AdaCore
> >> + *
> >> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> >> + * of this software and associated documentation files (the "Software"), to deal
> >> + * in the Software without restriction, including without limitation the rights
> >> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> >> + * copies of the Software, and to permit persons to whom the Software is
> >> + * furnished to do so, subject to the following conditions:
> >> + *
> >> + * The above copyright notice and this permission notice shall be included in
> >> + * all copies or substantial portions of the Software.
> >> + *
> >> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> >> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> >> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> >> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> >> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> >> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> >> + * THE SOFTWARE.
> >> + */
> >> +
> >> +#include "sysbus.h"
> >> +#include "cpu.h"
> >> +
> >> +#include "grlib.h"
> >> +
> >> +/* #define DEBUG_IRQ */
> >> +
> >> +#ifdef DEBUG_IRQ
> >> +#define DPRINTF(fmt, ...)                                       \
> >> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
> >> +#else
> >> +#define DPRINTF(fmt, ...)
> >> +#endif
> >> +
> >> +#define IRQMP_MAX_CPU 16
> >> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
> >> +
> >> +/* Memory mapped register offsets */
> >> +#define LEVEL_OFFSET     0x00
> >> +#define PENDING_OFFSET   0x04
> >> +#define FORCE0_OFFSET    0x08
> >> +#define CLEAR_OFFSET     0x0C
> >> +#define MP_STATUS_OFFSET 0x10
> >> +#define BROADCAST_OFFSET 0x14
> >> +#define MASK_OFFSET      0x40
> >> +#define FORCE_OFFSET     0x80
> >> +#define EXTENDED_OFFSET  0xC0
> >> +
> >> +typedef struct IRQMP
> >> +{
> >> +    SysBusDevice busdev;
> >> +
> >> +    CPUSPARCState *env;
> >> +} IRQMP;
> >> +
> >> +typedef struct IRQMPState
> >> +{
> >> +    uint32_t level;
> >> +    uint32_t pending;
> >> +    uint32_t clear;
> >> +    uint32_t broadcast;
> >> +
> >> +    uint32_t mask[IRQMP_MAX_CPU];
> >> +    uint32_t force[IRQMP_MAX_CPU];
> >> +    uint32_t extended[IRQMP_MAX_CPU];
> >> +
> >> +    IRQMP    *parent;
> >> +} IRQMPState;
> >> +
> >> +IRQMPState grlib_irqmp_state;
> >> +
> >> +void grlib_irqmp_set_irq(void *opaque, int irq, int level);
> >> +
> >> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
> >> +                                CPUState            *env,
> >> +                                qemu_irq           **cpu_irqs,
> >> +                                uint32_t             nr_irqs)
> >> +{
> >> +    DeviceState *dev;
> >> +
> >> +    assert(cpu_irqs != NULL);
> >> +
> >> +    dev = qdev_create(NULL, "grlib,irqmp");
> >> +    qdev_prop_set_ptr(dev, "cpustate", env);
> >> +
> >> +    if (qdev_init(dev)) {
> >> +        return NULL;
> >> +    }
> >> +
> >> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> >> +
> >> +    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
> >> +&grlib_irqmp_state,
> >> +                                   nr_irqs);
> >> +
> >> +    return dev;
> >> +}
> >> +
> >> +static void grlib_irqmp_check_irqs(CPUState *env)
> >> +{
> >> +    uint32_t pend   = 0;
> >> +    uint32_t level0 = 0;
> >> +    uint32_t level1 = 0;
> >> +
> >> +    assert(env != NULL);
> >> +
> >> +    /* IRQ for CPU 0 (no SMP support) */
> >> +    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
> >> +&  grlib_irqmp_state.mask[0];
> >> +
> >> +
> >> +    level0 = pend&  ~grlib_irqmp_state.level;
> >> +    level1 = pend&   grlib_irqmp_state.level;
> >> +
> >> +    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n",
> >> +            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
> >> +            grlib_irqmp_state.mask[0], level1, level0);
> >> +
> >> +    /* Trigger level1 interrupt first and level0 if there is no level1 */
> >> +    if (level1 != 0) {
> >> +        env->pil_in = level1;
> >> +    } else {
> >> +        env->pil_in = level0;
> >> +    }
> >> +
> >> +    if (env->pil_in&&  (env->interrupt_index == 0 ||
> >> +                        (env->interrupt_index&  ~15) == TT_EXTINT)) {
> >> +        unsigned int i;
> >> +
> >> +        for (i = 15; i>  0; i--) {
> >> +            if (env->pil_in&  (1<<  i)) {
> >> +                int old_interrupt = env->interrupt_index;
> >> +
> >> +                env->interrupt_index = TT_EXTINT | i;
> >> +                if (old_interrupt != env->interrupt_index) {
> >> +                    DPRINTF("Set CPU IRQ %d\n", i);
> >> +                    cpu_interrupt(env, CPU_INTERRUPT_HARD);
> >> +                }
> >> +                break;
> >> +            }
> >> +        }
> >> +    } else if (!env->pil_in&&  (env->interrupt_index&  ~15) == TT_EXTINT) {
> >> +        DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index&  15);
> >> +        env->interrupt_index = 0;
> >> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
> >> +    }
> >> +}
> >> +
> >> +void grlib_irqmp_ack(CPUSPARCState *env, int intno)
> >> +{
> >> +    assert(env != NULL);
> >> +
> >> +    uint32_t mask;
> >> +
> >> +    intno&= 15;
> >> +    mask = 1<<  intno;
> >> +
> >> +    DPRINTF("grlib_irqmp_ack %d\n", intno);
> >> +
> >> +    /* Clear registers */
> >> +    grlib_irqmp_state.pending&= ~mask;
> >> +    grlib_irqmp_state.force[0]&= ~mask; /* Only CPU 0 (No SMP support) */
> >> +
> >> +    grlib_irqmp_check_irqs(env);
> >> +}
> >> +
> >> +void grlib_irqmp_set_irq(void *opaque, int irq, int level)
> >> +{
> >> +    IRQMPState *s = opaque;
> >> +    int         i = 0;
> >> +
> >> +    assert(opaque != NULL);
> >> +    assert(s->parent != NULL);
> >> +
> >> +    if (level) {
> >> +        DPRINTF("Raise CPU IRQ %d\n", irq);
> >> +
> >> +        if (s->broadcast&  1<<  irq) {
> >> +            /* Broadcasted IRQ */
> >> +            for (i = 0; i<  IRQMP_MAX_CPU; i++) {
> >> +                s->force[i] |= 1<<  irq;
> >> +            }
> >> +        } else {
> >> +            s->pending |= 1<<  irq;
> >> +        }
> >> +        grlib_irqmp_check_irqs(s->parent->env);
> >> +
> >> +    } else {
> >> +
> >> +        DPRINTF("Lower CPU IRQ %d\n", irq);
> >> +        if (s->broadcast&  1<<  irq) {
> >> +            /* Broadcasted IRQ */
> >> +            for (i = 0; i<  IRQMP_MAX_CPU; i++) {
> >> +                s->force[i]&= ~(1<<  irq);
> >> +            }
> >> +        } else {
> >> +            s->pending&= ~(1<<  irq);
> >> +        }
> >
> > If you use the edge triggered interrupt model in the devices, then you
> > shouldn't clear the pending bit here. A pulse from the device should
> > set it and it should only get cleared when the CPU acks it.
> >
> > The model you've coded here indicates that the devices use a level
> > triggered approach. And the clearing of the pending bit in
> > grlib_irqmp_ack becomes meaningless...
> 
> OK, so I use qemu_irq_pulse and do nothing in grlib_irqmp_set_irq when 
> level == 0...

Right. But I'm still not convinced about the edge triggered nature of the
device irq signals.

One question to ask is:
What happens if you setup a timer without auto-reload and let an
interrupt hit. In the interrupt handler you dont restart the timer
and you don't clear the IP bit. You just return.

Will you get re-hit by the timer interrupt over and over?

If yes, the device interrupt line is connected to the IP bit and
needs to be acked by writing a 1 to the config reg. You shouldn't
use qemu_irq_pulse.

If no, the device interrupt is signaled as a pulse.

Cheers

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-09 11:06         ` Edgar E. Iglesias
@ 2010-12-09 11:32           ` Fabien Chouteau
  0 siblings, 0 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-09 11:32 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: qemu-devel

On 12/09/2010 12:06 PM, Edgar E. Iglesias wrote:
> On Thu, Dec 09, 2010 at 12:03:35PM +0100, Fabien Chouteau wrote:
>> On 12/09/2010 11:32 AM, Edgar E. Iglesias wrote:
>>> On Mon, Dec 06, 2010 at 10:26:03AM +0100, Fabien Chouteau wrote:
>>>>
>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>> ---
>>>>    hw/grlib_irqmp.c |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    1 files changed, 416 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
>>>> new file mode 100644
>>>> index 0000000..69e1553
>>>> --- /dev/null
>>>> +++ b/hw/grlib_irqmp.c
>>>> @@ -0,0 +1,416 @@
>>>> +/*
>>>> + * QEMU GRLIB IRQMP Emulator
>>>> + *
>>>> + * (Multiprocessor and extended interrupt not supported)
>>>> + *
>>>> + * Copyright (c) 2010 AdaCore
>>>> + *
>>>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>>>> + * of this software and associated documentation files (the "Software"), to deal
>>>> + * in the Software without restriction, including without limitation the rights
>>>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>>>> + * copies of the Software, and to permit persons to whom the Software is
>>>> + * furnished to do so, subject to the following conditions:
>>>> + *
>>>> + * The above copyright notice and this permission notice shall be included in
>>>> + * all copies or substantial portions of the Software.
>>>> + *
>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>>>> + * THE SOFTWARE.
>>>> + */
>>>> +
>>>> +#include "sysbus.h"
>>>> +#include "cpu.h"
>>>> +
>>>> +#include "grlib.h"
>>>> +
>>>> +/* #define DEBUG_IRQ */
>>>> +
>>>> +#ifdef DEBUG_IRQ
>>>> +#define DPRINTF(fmt, ...)                                       \
>>>> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#else
>>>> +#define DPRINTF(fmt, ...)
>>>> +#endif
>>>> +
>>>> +#define IRQMP_MAX_CPU 16
>>>> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
>>>> +
>>>> +/* Memory mapped register offsets */
>>>> +#define LEVEL_OFFSET     0x00
>>>> +#define PENDING_OFFSET   0x04
>>>> +#define FORCE0_OFFSET    0x08
>>>> +#define CLEAR_OFFSET     0x0C
>>>> +#define MP_STATUS_OFFSET 0x10
>>>> +#define BROADCAST_OFFSET 0x14
>>>> +#define MASK_OFFSET      0x40
>>>> +#define FORCE_OFFSET     0x80
>>>> +#define EXTENDED_OFFSET  0xC0
>>>> +
>>>> +typedef struct IRQMP
>>>> +{
>>>> +    SysBusDevice busdev;
>>>> +
>>>> +    CPUSPARCState *env;
>>>> +} IRQMP;
>>>> +
>>>> +typedef struct IRQMPState
>>>> +{
>>>> +    uint32_t level;
>>>> +    uint32_t pending;
>>>> +    uint32_t clear;
>>>> +    uint32_t broadcast;
>>>> +
>>>> +    uint32_t mask[IRQMP_MAX_CPU];
>>>> +    uint32_t force[IRQMP_MAX_CPU];
>>>> +    uint32_t extended[IRQMP_MAX_CPU];
>>>> +
>>>> +    IRQMP    *parent;
>>>> +} IRQMPState;
>>>> +
>>>> +IRQMPState grlib_irqmp_state;
>>>> +
>>>> +void grlib_irqmp_set_irq(void *opaque, int irq, int level);
>>>> +
>>>> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
>>>> +                                CPUState            *env,
>>>> +                                qemu_irq           **cpu_irqs,
>>>> +                                uint32_t             nr_irqs)
>>>> +{
>>>> +    DeviceState *dev;
>>>> +
>>>> +    assert(cpu_irqs != NULL);
>>>> +
>>>> +    dev = qdev_create(NULL, "grlib,irqmp");
>>>> +    qdev_prop_set_ptr(dev, "cpustate", env);
>>>> +
>>>> +    if (qdev_init(dev)) {
>>>> +        return NULL;
>>>> +    }
>>>> +
>>>> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
>>>> +
>>>> +    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
>>>> +&grlib_irqmp_state,
>>>> +                                   nr_irqs);
>>>> +
>>>> +    return dev;
>>>> +}
>>>> +
>>>> +static void grlib_irqmp_check_irqs(CPUState *env)
>>>> +{
>>>> +    uint32_t pend   = 0;
>>>> +    uint32_t level0 = 0;
>>>> +    uint32_t level1 = 0;
>>>> +
>>>> +    assert(env != NULL);
>>>> +
>>>> +    /* IRQ for CPU 0 (no SMP support) */
>>>> +    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
>>>> +&   grlib_irqmp_state.mask[0];
>>>> +
>>>> +
>>>> +    level0 = pend&   ~grlib_irqmp_state.level;
>>>> +    level1 = pend&    grlib_irqmp_state.level;
>>>> +
>>>> +    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n",
>>>> +            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
>>>> +            grlib_irqmp_state.mask[0], level1, level0);
>>>> +
>>>> +    /* Trigger level1 interrupt first and level0 if there is no level1 */
>>>> +    if (level1 != 0) {
>>>> +        env->pil_in = level1;
>>>> +    } else {
>>>> +        env->pil_in = level0;
>>>> +    }
>>>> +
>>>> +    if (env->pil_in&&   (env->interrupt_index == 0 ||
>>>> +                        (env->interrupt_index&   ~15) == TT_EXTINT)) {
>>>> +        unsigned int i;
>>>> +
>>>> +        for (i = 15; i>   0; i--) {
>>>> +            if (env->pil_in&   (1<<   i)) {
>>>> +                int old_interrupt = env->interrupt_index;
>>>> +
>>>> +                env->interrupt_index = TT_EXTINT | i;
>>>> +                if (old_interrupt != env->interrupt_index) {
>>>> +                    DPRINTF("Set CPU IRQ %d\n", i);
>>>> +                    cpu_interrupt(env, CPU_INTERRUPT_HARD);
>>>> +                }
>>>> +                break;
>>>> +            }
>>>> +        }
>>>> +    } else if (!env->pil_in&&   (env->interrupt_index&   ~15) == TT_EXTINT) {
>>>> +        DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index&   15);
>>>> +        env->interrupt_index = 0;
>>>> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
>>>> +    }
>>>> +}
>>>> +
>>>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno)
>>>> +{
>>>> +    assert(env != NULL);
>>>> +
>>>> +    uint32_t mask;
>>>> +
>>>> +    intno&= 15;
>>>> +    mask = 1<<   intno;
>>>> +
>>>> +    DPRINTF("grlib_irqmp_ack %d\n", intno);
>>>> +
>>>> +    /* Clear registers */
>>>> +    grlib_irqmp_state.pending&= ~mask;
>>>> +    grlib_irqmp_state.force[0]&= ~mask; /* Only CPU 0 (No SMP support) */
>>>> +
>>>> +    grlib_irqmp_check_irqs(env);
>>>> +}
>>>> +
>>>> +void grlib_irqmp_set_irq(void *opaque, int irq, int level)
>>>> +{
>>>> +    IRQMPState *s = opaque;
>>>> +    int         i = 0;
>>>> +
>>>> +    assert(opaque != NULL);
>>>> +    assert(s->parent != NULL);
>>>> +
>>>> +    if (level) {
>>>> +        DPRINTF("Raise CPU IRQ %d\n", irq);
>>>> +
>>>> +        if (s->broadcast&   1<<   irq) {
>>>> +            /* Broadcasted IRQ */
>>>> +            for (i = 0; i<   IRQMP_MAX_CPU; i++) {
>>>> +                s->force[i] |= 1<<   irq;
>>>> +            }
>>>> +        } else {
>>>> +            s->pending |= 1<<   irq;
>>>> +        }
>>>> +        grlib_irqmp_check_irqs(s->parent->env);
>>>> +
>>>> +    } else {
>>>> +
>>>> +        DPRINTF("Lower CPU IRQ %d\n", irq);
>>>> +        if (s->broadcast&   1<<   irq) {
>>>> +            /* Broadcasted IRQ */
>>>> +            for (i = 0; i<   IRQMP_MAX_CPU; i++) {
>>>> +                s->force[i]&= ~(1<<   irq);
>>>> +            }
>>>> +        } else {
>>>> +            s->pending&= ~(1<<   irq);
>>>> +        }
>>>
>>> If you use the edge triggered interrupt model in the devices, then you
>>> shouldn't clear the pending bit here. A pulse from the device should
>>> set it and it should only get cleared when the CPU acks it.
>>>
>>> The model you've coded here indicates that the devices use a level
>>> triggered approach. And the clearing of the pending bit in
>>> grlib_irqmp_ack becomes meaningless...
>>
>> OK, so I use qemu_irq_pulse and do nothing in grlib_irqmp_set_irq when
>> level == 0...
>
> Right. But I'm still not convinced about the edge triggered nature of the
> device irq signals.
>
> One question to ask is:
> What happens if you setup a timer without auto-reload and let an
> interrupt hit. In the interrupt handler you dont restart the timer
> and you don't clear the IP bit. You just return.
>
> Will you get re-hit by the timer interrupt over and over?

No, just one time.

> If yes, the device interrupt line is connected to the IP bit and
> needs to be acked by writing a 1 to the config reg. You shouldn't
> use qemu_irq_pulse.
>
> If no, the device interrupt is signaled as a pulse

Let's go with the pulse ;)

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-07 11:40             ` Fabien Chouteau
@ 2010-12-11  9:56               ` Blue Swirl
  2010-12-13 15:51                 ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-11  9:56 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>
>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>> ---
>>>  Makefile.target          |    5 +-
>>>  hw/leon3.c               |  310
>>> ++++++++++++++++++++++++++++++++++++++++++++++
>>>  target-sparc/cpu.h       |   10 ++
>>>  target-sparc/helper.c    |    2 +-
>>>  target-sparc/op_helper.c |   30 ++++-
>>>  5 files changed, 353 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/Makefile.target b/Makefile.target
>>> index 2800f47..f40e04f 100644
>>> --- a/Makefile.target
>>> +++ b/Makefile.target
>>> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>>>  else
>>>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
>>> +
>>> +# GRLIB
>>> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>>>  endif
>>>
>>>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>> diff --git a/hw/leon3.c b/hw/leon3.c
>>> new file mode 100644
>>> index 0000000..ba61081
>>> --- /dev/null
>>> +++ b/hw/leon3.c
>>> @@ -0,0 +1,310 @@
>>> +/*
>>> + * QEMU Leon3 System Emulator
>>> + *
>>> + * Copyright (c) 2010 AdaCore
>>> + *
>>> + * Permission is hereby granted, free of charge, to any person obtaining
>>> a copy
>>> + * of this software and associated documentation files (the "Software"),
>>> to deal
>>> + * in the Software without restriction, including without limitation the
>>> rights
>>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
>>> sell
>>> + * copies of the Software, and to permit persons to whom the Software is
>>> + * furnished to do so, subject to the following conditions:
>>> + *
>>> + * The above copyright notice and this permission notice shall be
>>> included in
>>> + * all copies or substantial portions of the Software.
>>> + *
>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>> EXPRESS OR
>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>> MERCHANTABILITY,
>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>> SHALL
>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
>>> OTHER
>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>> ARISING FROM,
>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>> DEALINGS IN
>>> + * THE SOFTWARE.
>>> + */
>>> +#include "hw.h"
>>> +#include "qemu-timer.h"
>>> +#include "qemu-char.h"
>>> +#include "sysemu.h"
>>> +#include "boards.h"
>>> +#include "loader.h"
>>> +#include "elf.h"
>>> +
>>> +#include "grlib.h"
>>> +
>>> +/* #define DEBUG_LEON3 */
>>> +
>>> +#ifdef DEBUG_LEON3
>>> +#define DPRINTF(fmt, ...)                                       \
>>> +    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
>>> +#else
>>> +#define DPRINTF(fmt, ...)
>>> +#endif
>>> +
>>> +/* Default system clock.  */
>>> +#define CPU_CLK (40 * 1000 * 1000)
>>> +
>>> +#define PROM_FILENAME        "u-boot.bin"
>>> +
>>> +#define MAX_PILS 16
>>> +
>>> +typedef struct Leon3State
>>> +{
>>> +    uint32_t cache_control;
>>> +    uint32_t inst_cache_conf;
>>> +    uint32_t data_cache_conf;
>>> +
>>> +    uint64_t entry;             /* save kernel entry in case of reset */
>>> +} Leon3State;
>>> +
>>> +Leon3State leon3_state;
>>
>> Again global state, please refactor. Perhaps most of the cache
>> handling code belong to target-sparc/op_helper.c and this structure to
>> CPUSPARCState.
>
> I will try to find a solution for that.
> Is it OK to add some Leon3 specific stuff in the CPUSPARCState?

Yes, no problem. You can also drop the intermediate Leon3State
structure if there is no benefit.

>>> +
>>> +/* Cache control: emulate the behavior of cache control registers but
>>> without
>>> +   any effect on the emulated CPU */
>>> +
>>> +#define CACHE_DISABLED 0x0
>>> +#define CACHE_FROZEN   0x1
>>> +#define CACHE_ENABLED  0x3
>>> +
>>> +/* Cache Control register fields */
>>> +
>>> +#define CACHE_CTRL_IF (1<<    4)  /* Instruction Cache Freeze on
>>> Interrupt */
>>> +#define CACHE_CTRL_DF (1<<    5)  /* Data Cache Freeze on Interrupt */
>>> +#define CACHE_CTRL_DP (1<<  14)  /* Data cache flush pending */
>>> +#define CACHE_CTRL_IP (1<<  15)  /* Instruction cache flush pending */
>>> +#define CACHE_CTRL_IB (1<<  16)  /* Instruction burst fetch */
>>> +#define CACHE_CTRL_FI (1<<  21)  /* Flush Instruction cache (Write only)
>>> */
>>> +#define CACHE_CTRL_FD (1<<  22)  /* Flush Data cache (Write only) */
>>> +#define CACHE_CTRL_DS (1<<  23)  /* Data cache snoop enable */
>>> +
>>> +void leon3_cache_control_int(void)
>>> +{
>>> +    uint32_t state = 0;
>>> +
>>> +    if (leon3_state.cache_control&  CACHE_CTRL_IF) {
>>> +        /* Instruction cache state */
>>> +        state = leon3_state.cache_control&  0x3;
>>
>> Please add a new define CACHE_CTRL_xxx to replace 0x3.
>>
>
> Done.
>
>>> +        if (state == CACHE_ENABLED) {
>>> +            state = CACHE_FROZEN;
>>> +            DPRINTF("Instruction cache: freeze\n");
>>> +        }
>>> +
>>> +        leon3_state.cache_control&= ~0x3;
>>> +        leon3_state.cache_control |= state;
>>> +    }
>>> +
>>> +    if (leon3_state.cache_control&  CACHE_CTRL_DF) {
>>> +        /* Data cache state */
>>> +        state = (leon3_state.cache_control>>  2)&  0x3;
>>> +        if (state == CACHE_ENABLED) {
>>> +            state = CACHE_FROZEN;
>>> +            DPRINTF("Data cache: freeze\n");
>>> +        }
>>> +
>>> +        leon3_state.cache_control&= ~(0x3<<  2);
>>> +        leon3_state.cache_control |= (state<<  2);
>>> +    }
>>> +}
>>> +
>>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
>>> +{
>>> +    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned
>>> int)addr,
>>> +            (unsigned int)val, size);
>>
>> There's PRIx64 to print uint64_t portably, then the casts can be removed.
>>
>
> Fixed.
>
>>> +
>>> +    if (size != 4) {
>>> +        DPRINTF(" CC 32bits only\n");
>>> +        return;
>>> +    }
>>> +
>>> +    switch (addr) {
>>> +        case 0x00:              /* Cache control */
>>> +
>>> +            /* These values must always be read as zeros */
>>> +            val&= ~CACHE_CTRL_FD;
>>> +            val&= ~CACHE_CTRL_FI;
>>> +            val&= ~CACHE_CTRL_IB;
>>> +            val&= ~CACHE_CTRL_IP;
>>> +            val&= ~CACHE_CTRL_DP;
>>> +
>>> +            leon3_state.cache_control = val;
>>> +            break;
>>> +        case 0x04:              /* Instruction cache configuration */
>>> +        case 0x08:              /* Data cache configuration */
>>> +            /* Read Only */
>>> +            break;
>>> +        default:
>>> +            DPRINTF(" CC write unknown register 0x%04x\n", (int)addr);
>>> +            break;
>>> +    };
>>> +}
>>> +
>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size)
>>> +{
>>> +    uint64_t ret = 0;
>>> +
>>> +    if (size != 4) {
>>> +        DPRINTF(" CC 32bits only\n");
>>> +        return 0;
>>> +    }
>>> +
>>> +    switch (addr) {
>>> +        case 0x00:              /* Cache control */
>>> +            ret = leon3_state.cache_control;
>>> +            break;
>>> +        case 0x04:              /* Instruction cache configuration */
>>> +            ret = leon3_state.inst_cache_conf;
>>> +            break;
>>> +        case 0x08:              /* Data cache configuration */
>>> +            ret = leon3_state.data_cache_conf;
>>> +            break;
>>> +        default:
>>> +            DPRINTF(" CC read unknown register 0x%04x\n", (int)addr);
>>> +            break;
>>> +    };
>>> +    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned
>>> int)addr,
>>> +            size, (long unsigned int)ret );
>>> +    return ret;
>>> +}
>>> +
>>> +void leon3_shutdown(void)
>>> +{
>>> +    qemu_system_shutdown_request();
>>> +}
>>> +
>>> +static void main_cpu_reset(void *opaque)
>>> +{
>>> +    CPUState *env = opaque;
>>
>> Here you can introduce a helper structure to pass PC and NPC, like
>> sun4u.c ResetData. Then the global state should not be needed anymore.
>>
>
> OK, I've used the sun4u.c reset scheme.
>
>>> +
>>> +    cpu_reset(env);
>>> +
>>> +    env->halted = 0;
>>> +    env->pc     = leon3_state.entry;
>>> +    env->npc    = leon3_state.entry + 4;
>>> +
>>> +    /* Initialize cache control */
>>> +    leon3_state.cache_control   = 0x0;
>>> +
>>> +    /* Configuration registers are read and only always keep those
>>> predefined
>>> +       values */
>>> +    leon3_state.inst_cache_conf = 0x10220000;
>>> +    leon3_state.data_cache_conf = 0x18220000;
>>> +}
>>> +
>>> +static void leon3_generic_hw_init(ram_addr_t  ram_size,
>>> +                                  const char *boot_device,
>>> +                                  const char *kernel_filename,
>>> +                                  const char *kernel_cmdline,
>>> +                                  const char *initrd_filename,
>>> +                                  const char *cpu_model)
>>> +{
>>> +    CPUState   *env;
>>> +    ram_addr_t  ram_offset, prom_offset;
>>> +    int         ret;
>>> +    char       *filename;
>>> +    qemu_irq   *cpu_irqs = NULL;
>>> +    int         bios_size;
>>> +    int         prom_size;
>>> +    int         aligned_bios_size;
>>> +
>>> +    /* Init CPU */
>>> +    if (!cpu_model)
>>> +        cpu_model = "LEON3";
>>
>> Missing braces.
>
> Fixed.
>
>>
>>> +
>>> +    env = cpu_init(cpu_model);
>>> +    if (!env) {
>>> +        fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
>>> +        exit(1);
>>> +    }
>>> +
>>> +    cpu_sparc_set_id(env, 0);
>>> +
>>> +    qemu_register_reset(main_cpu_reset, env);
>>> +
>>> +    /* Allocate IRQ manager */
>>> +    grlib_irqmp_create(0x80000200, env,&cpu_irqs, MAX_PILS);
>>> +
>>> +    /* Allocate RAM */
>>> +    if ((uint64_t)ram_size>  (1UL<<  30)) {
>>> +        fprintf(stderr,
>>> +                "qemu: Too much memory for this machine: %d, maximum
>>> 1G\n",
>>> +                (unsigned int)(ram_size / (1024 * 1024)));
>>> +        exit(1);
>>> +    }
>>> +
>>> +    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
>>> +    cpu_register_physical_memory(0x40000000, ram_size, ram_offset |
>>> IO_MEM_RAM);
>>> +
>>> +    /* Allocate BIOS */
>>> +    prom_size = 8 * 1024 * 1024; /* 8Mb */
>>> +    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
>>> +    cpu_register_physical_memory(0x00000000, prom_size,
>>> +                                 prom_offset | IO_MEM_ROM);
>>> +
>>> +    /* Load boot prom */
>>> +    if (bios_name == NULL)
>>> +        bios_name = PROM_FILENAME;
>>> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
>>> +
>>> +    bios_size = get_image_size(filename);
>>> +
>>> +    if (bios_size>  prom_size) {
>>> +        fprintf(stderr, "qemu: could not load prom '%s': file too big
>>> \n",
>>> +                filename);
>>> +        exit(1);
>>> +    }
>>> +
>>> +    if (bios_size>  0) {
>>> +        aligned_bios_size =
>>> +            (bios_size + TARGET_PAGE_SIZE - 1)&  TARGET_PAGE_MASK;
>>> +
>>> +        ret = load_image_targphys(filename, 0x00000000, bios_size);
>>> +        if (ret<  0 || ret>  prom_size) {
>>> +            fprintf(stderr, "qemu: could not load prom '%s'\n",
>>> filename);
>>> +            exit(1);
>>> +        }
>>> +    }
>>> +    else if (kernel_filename == NULL) {
>>> +        fprintf(stderr,"Can't read bios image %s\n", filename);
>>> +        exit(1);
>>> +    }
>>> +
>>> +    /* Can directly load an application. */
>>> +    if (kernel_filename != NULL) {
>>> +        long     kernel_size;
>>> +        uint64_t entry;
>>> +
>>> +        kernel_size = load_elf(kernel_filename, NULL, NULL,&entry, NULL,
>>> NULL,
>>> +                               1 /* big endian */, ELF_MACHINE, 0);
>>> +        if (kernel_size<  0) {
>>> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
>>> +                    kernel_filename);
>>> +            exit(1);
>>> +        }
>>> +        if (bios_size<= 0) {
>>> +            /* If there is no bios/monitor, start the application.  */
>>> +            env->pc = entry;
>>> +            env->npc = entry + 4;
>>> +            leon3_state.entry = entry;
>>> +        }
>>> +    }
>>> +
>>> +    /* Allocate timers */
>>> +    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
>>> +
>>> +    /* Allocate uart */
>>> +    if (serial_hds[0])
>>> +        grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
>>> +}
>>> +
>>> +QEMUMachine leon3_generic_machine = {
>>> +    .name     = "leon3_generic",
>>> +    .desc     = "Leon-3 generic",
>>> +    .init     = leon3_generic_hw_init,
>>> +    .use_scsi = 0,
>>> +};
>>> +
>>> +static void leon3_machine_init(void)
>>> +{
>>> +    qemu_register_machine(&leon3_generic_machine);
>>> +}
>>> +
>>> +machine_init(leon3_machine_init);
>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>> index 7e0d17c..6020ffd 100644
>>> --- a/target-sparc/cpu.h
>>> +++ b/target-sparc/cpu.h
>>> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
>>>  /* sun4m.c, sun4u.c */
>>>  void cpu_check_irqs(CPUSPARCState *env);
>>>
>>> +/* grlib_irqmp.c */
>>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno);
>>> +
>>> +/* leon3.c */
>>> +void     leon3_shutdown(void);
>>> +void     leon3_cache_control_st(target_ulong addr, uint64_t val, int
>>> size);
>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size);
>>> +void     leon3_cache_control_int(void);
>>> +
>>> +
>>>  #if defined (TARGET_SPARC64)
>>>
>>>  static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask)
>>> diff --git a/target-sparc/helper.c b/target-sparc/helper.c
>>> index e84c312..3bf990f 100644
>>> --- a/target-sparc/helper.c
>>> +++ b/target-sparc/helper.c
>>> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
>>>         .iu_version = 0xf3000000,
>>>         .fpu_version = 4<<  17, /* FPU version 4 (Meiko) */
>>>         .mmu_version = 0xf3000000,
>>> -        .mmu_bm = 0x00004000,
>>> +        .mmu_bm = 0x00000000,
>>>         .mmu_ctpr_mask = 0x007ffff0,
>>>         .mmu_cxr_mask = 0x0000003f,
>>>         .mmu_sfsr_mask = 0xffffffff,
>>> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
>>> index be3c1e0..85df077 100644
>>> --- a/target-sparc/op_helper.c
>>> +++ b/target-sparc/op_helper.c
>>> @@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi,
>>> int size, int sign)
>>>
>>>     helper_check_align(addr, size - 1);
>>>     switch (asi) {
>>> -    case 2: /* SuperSparc MXCC registers */
>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>         switch (addr) {
>>> +        case 0x00:          /* Leon3 Cache Control */
>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>> +            ret = leon3_cache_control_ld(addr, size);
>>> +            break;
>>>         case 0x01c00a00: /* MXCC control register */
>>>             if (size == 8)
>>>                 ret = env->mxccregs[3];
>>> @@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t
>>> val, int asi, int size)
>>>  {
>>>     helper_check_align(addr, size - 1);
>>>     switch(asi) {
>>> -    case 2: /* SuperSparc MXCC registers */
>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>         switch (addr) {
>>> +        case 0x00:          /* Leon3 Cache Control */
>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>> +            leon3_cache_control_st(addr, val, size);
>>> +            break;
>>> +
>>>         case 0x01c00000: /* MXCC stream data register 0 */
>>>             if (size == 8)
>>>                 env->mxccdata[0] = val;
>>> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
>>>  {
>>>     int cwp, intno = env->exception_index;
>>>
>>> +#if !defined(CONFIG_USER_ONLY)
>>> +    /* Leon3 shutdown */
>>> +    if (intno == 0x80&&  env->version == 0xf3000000) {
>>> +        leon3_shutdown();
>>> +    }
>>
>> This looks like a hack. Should a trap instruction initiate a shutdown?
>
> Yes, on Leon3 "ta 0x0" initiates a shutdown.

Then this should be handled during translation. A Leon3 specific CPU
feature should be added and used much like CHECK_IU_FEATURE (in
translate.c). Then execution speed would not be affected for non-Leon3
CPUs.

>>> +#endif
>>> +
>>>  #ifdef DEBUG_PCALL
>>>     if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>>         static int count;
>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>>     env->pc = env->tbr;
>>>     env->npc = env->pc + 4;
>>>     env->exception_index = -1;
>>> +
>>> +#if !defined(CONFIG_USER_ONLY)
>>> +    /* IRQ acknowledgment for Leon3 */
>>> +    if (env->version == 0xf3000000&&  (intno&  ~15) == TT_EXTINT) {
>>> +        grlib_irqmp_ack (env, intno);
>>> +        leon3_cache_control_int();
>>> +    }
>>
>> Like this. I don't think a CPU should immediately ack any incoming
>> interrupts.
>
> Leon3 does...

Strange. Then this should be handled at board level (leon3.c).

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support.
  2010-12-07 11:51               ` Fabien Chouteau
@ 2010-12-11  9:59                 ` Blue Swirl
  2010-12-13 17:01                   ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-11  9:59 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Tue, Dec 7, 2010 at 11:51 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/06/2010 07:01 PM, Blue Swirl wrote:
>>
>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>> ---
>>>  hw/leon3.c               |    6 ++++++
>>>  target-sparc/cpu.h       |    1 +
>>>  target-sparc/machine.c   |    2 ++
>>>  target-sparc/translate.c |   10 ++++++++++
>>>  4 files changed, 19 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/hw/leon3.c b/hw/leon3.c
>>> index ba61081..9605ce8 100644
>>> --- a/hw/leon3.c
>>> +++ b/hw/leon3.c
>>> @@ -187,6 +187,12 @@ static void main_cpu_reset(void *opaque)
>>>        values */
>>>     leon3_state.inst_cache_conf = 0x10220000;
>>>     leon3_state.data_cache_conf = 0x18220000;
>>> +
>>> +    /* Asr17 for Leon3 mono-processor */
>>> +    env->asr17&= 0<<  28;          /* CPU id */
>>> +    env->asr17&= 1<<  8;           /* SPARC V8 multiply and divide
>>> available */
>>> +    env->asr17&= env->nwindows -1; /* Number of implemented registers
>>> +                                       windows */
>>
>> This is constant...
>>
>>>  }
>>>
>>>  static void leon3_generic_hw_init(ram_addr_t  ram_size,
>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>> index 6020ffd..36d49fc 100644
>>> --- a/target-sparc/cpu.h
>>> +++ b/target-sparc/cpu.h
>>> @@ -341,6 +341,7 @@ typedef struct CPUSPARCState {
>>>                           from PSR) */
>>>  #if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
>>>     uint32_t wim;      /* window invalid mask */
>>> +    uint32_t asr17;    /* asr17 */
>>
>> ... so no new env fields are needed...
>>
>>>  #endif
>>>     target_ulong tbr;  /* trap base register */
>>>  #if !defined(TARGET_SPARC64)
>>> diff --git a/target-sparc/machine.c b/target-sparc/machine.c
>>> index 752e431..c530bd3 100644
>>> --- a/target-sparc/machine.c
>>> +++ b/target-sparc/machine.c
>>> @@ -42,6 +42,7 @@ void cpu_save(QEMUFile *f, void *opaque)
>>>     qemu_put_be32s(f,&env->pil_in);
>>>  #ifndef TARGET_SPARC64
>>>     qemu_put_be32s(f,&env->wim);
>>> +    qemu_put_be32s(f,&env->asr17);
>>
>> ... there's also nothing to save/load...
>>
>>>     /* MMU */
>>>     for (i = 0; i<  32; i++)
>>>         qemu_put_be32s(f,&env->mmuregs[i]);
>>> @@ -138,6 +139,7 @@ int cpu_load(QEMUFile *f, void *opaque, int
>>> version_id)
>>>     qemu_get_be32s(f,&env->pil_in);
>>>  #ifndef TARGET_SPARC64
>>>     qemu_get_be32s(f,&env->wim);
>>> +    qemu_get_be32s(f,&env->asr17);
>>>     /* MMU */
>>>     for (i = 0; i<  32; i++)
>>>         qemu_get_be32s(f,&env->mmuregs[i]);
>>> diff --git a/target-sparc/translate.c b/target-sparc/translate.c
>>> index 23f9519..65de614 100644
>>> --- a/target-sparc/translate.c
>>> +++ b/target-sparc/translate.c
>>> @@ -58,6 +58,7 @@ static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr,
>>> cpu_ver;
>>>  static TCGv_i32 cpu_softint;
>>>  #else
>>>  static TCGv cpu_wim;
>>> +static TCGv cpu_asr17;
>>>  #endif
>>>  /* local register indexes (only used inside old micro ops) */
>>>  static TCGv cpu_tmp0;
>>> @@ -2049,6 +2050,8 @@ static void disas_sparc_insn(DisasContext * dc)
>>>                 rs1 = GET_FIELD(insn, 13, 17);
>>>                 switch(rs1) {
>>>                 case 0: /* rdy */
>>> +                    gen_movl_TN_reg(rd, cpu_y);
>>> +                    break;
>>>  #ifndef TARGET_SPARC64
>>>                 case 0x01 ... 0x0e: /* undefined in the SPARCv8
>>>                                        manual, rdy on the microSPARC
>>> @@ -2058,6 +2061,11 @@ static void disas_sparc_insn(DisasContext * dc)
>>>                 case 0x10 ... 0x1f: /* implementation-dependent in the
>>>                                        SPARCv8 manual, rdy on the
>>>                                        microSPARC II */
>>> +
>>> +                    if (rs1 == 0x11) { /* Read %asr17 */
>>> +                        gen_movl_TN_reg(rd, cpu_asr17);
>>
>> Instead:
>> r_const = tcg_const_tl(asr constants  | dc->def->nwindows - 1);
>> gen_movl_TN_reg(rd, r_const);
>> tcg_temp_free(r_const);
>
> OK for me, if it is acceptable to have this Leon3's specific behavior for
> all the SPARC32 CPUs.

This will not affect other CPUs when you use CPU feature bits to make
the ASR only available to Leon3.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-07 10:43       ` Fabien Chouteau
@ 2010-12-11 10:31         ` Blue Swirl
  2010-12-13 16:23           ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-11 10:31 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Tue, Dec 7, 2010 at 10:43 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/06/2010 06:25 PM, Blue Swirl wrote:
>>
>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>> ---
>>>  hw/grlib_irqmp.c |  416
>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  1 files changed, 416 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
>>> new file mode 100644
>>> index 0000000..69e1553
>>> --- /dev/null
>>> +++ b/hw/grlib_irqmp.c
>>> @@ -0,0 +1,416 @@
>>> +/*
>>> + * QEMU GRLIB IRQMP Emulator
>>> + *
>>> + * (Multiprocessor and extended interrupt not supported)
>>> + *
>>> + * Copyright (c) 2010 AdaCore
>>> + *
>>> + * Permission is hereby granted, free of charge, to any person obtaining
>>> a copy
>>> + * of this software and associated documentation files (the "Software"),
>>> to deal
>>> + * in the Software without restriction, including without limitation the
>>> rights
>>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
>>> sell
>>> + * copies of the Software, and to permit persons to whom the Software is
>>> + * furnished to do so, subject to the following conditions:
>>> + *
>>> + * The above copyright notice and this permission notice shall be
>>> included in
>>> + * all copies or substantial portions of the Software.
>>> + *
>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>> EXPRESS OR
>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>> MERCHANTABILITY,
>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>> SHALL
>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
>>> OTHER
>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>> ARISING FROM,
>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>> DEALINGS IN
>>> + * THE SOFTWARE.
>>> + */
>>> +
>>> +#include "sysbus.h"
>>> +#include "cpu.h"
>>> +
>>> +#include "grlib.h"
>>> +
>>> +/* #define DEBUG_IRQ */
>>> +
>>> +#ifdef DEBUG_IRQ
>>> +#define DPRINTF(fmt, ...)                                       \
>>> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
>>> +#else
>>> +#define DPRINTF(fmt, ...)
>>> +#endif
>>> +
>>> +#define IRQMP_MAX_CPU 16
>>> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
>>> +
>>> +/* Memory mapped register offsets */
>>> +#define LEVEL_OFFSET     0x00
>>> +#define PENDING_OFFSET   0x04
>>> +#define FORCE0_OFFSET    0x08
>>> +#define CLEAR_OFFSET     0x0C
>>> +#define MP_STATUS_OFFSET 0x10
>>> +#define BROADCAST_OFFSET 0x14
>>> +#define MASK_OFFSET      0x40
>>> +#define FORCE_OFFSET     0x80
>>> +#define EXTENDED_OFFSET  0xC0
>>> +
>>> +typedef struct IRQMP
>>> +{
>>> +    SysBusDevice busdev;
>>> +
>>> +    CPUSPARCState *env;
>>
>> Devices should never access CPUState directly. Instead, board level
>> should create CPU irqs and these should then be passed here.
>>
>
> This case is special, Leon3 is a System-On-Chip and some of the components
> are very close to the processor.
> IRQMP is not really a peripheral nor a part of the CPU, it's both...

It's not a special case, it could be easily implemented separately.
MMUs, FPUs or co-processors could be special even if they have been
implemented as separate chips with real hardware. But we are actually
not looking at the (historical or current) chip boundaries but more
like what makes sense from QEMU architecture point of view.

>>> +} IRQMP;
>>> +
>>> +typedef struct IRQMPState
>>> +{
>>> +    uint32_t level;
>>> +    uint32_t pending;
>>> +    uint32_t clear;
>>> +    uint32_t broadcast;
>>> +
>>> +    uint32_t mask[IRQMP_MAX_CPU];
>>> +    uint32_t force[IRQMP_MAX_CPU];
>>> +    uint32_t extended[IRQMP_MAX_CPU];
>>> +
>>> +    IRQMP    *parent;
>>> +} IRQMPState;
>>> +
>>> +IRQMPState grlib_irqmp_state;
>>
>> Global state indicates poor design. Why separate IRQMP and IRQMPState?
>
> I have to access IRQMPState in grlib_irqmp_ack and grlib_irqmp_check_irqs,
> but I don't see how I can do it without a global variable.
> Again, I think that it's related to the special case of IRQMP.

Adding another set of signals for ack, going from board level to the
device should solve the problem cleanly.

>>> +
>>> +void grlib_irqmp_set_irq(void *opaque, int irq, int level);
>>
>> This should not be global. Again, creating qemu_irqs or moving some of
>> the code to board level should help.
>
> This one should be static indeed.
>
>>> +
>>> +DeviceState *grlib_irqmp_create(target_phys_addr_t   base,
>>> +                                CPUState            *env,
>>> +                                qemu_irq           **cpu_irqs,
>>> +                                uint32_t             nr_irqs)
>>> +{
>>> +    DeviceState *dev;
>>> +
>>> +    assert(cpu_irqs != NULL);
>>> +
>>> +    dev = qdev_create(NULL, "grlib,irqmp");
>>> +    qdev_prop_set_ptr(dev, "cpustate", env);
>>> +
>>> +    if (qdev_init(dev)) {
>>> +        return NULL;
>>> +    }
>>> +
>>> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
>>> +
>>> +    *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
>>> +&grlib_irqmp_state,
>>> +                                   nr_irqs);
>>> +
>>> +    return dev;
>>> +}
>>> +
>>> +static void grlib_irqmp_check_irqs(CPUState *env)
>>> +{
>>> +    uint32_t pend   = 0;
>>> +    uint32_t level0 = 0;
>>> +    uint32_t level1 = 0;
>>> +
>>> +    assert(env != NULL);
>>> +
>>> +    /* IRQ for CPU 0 (no SMP support) */
>>> +    pend = (grlib_irqmp_state.pending | grlib_irqmp_state.force[0])
>>> +&  grlib_irqmp_state.mask[0];
>>> +
>>> +
>>> +    level0 = pend&  ~grlib_irqmp_state.level;
>>> +    level1 = pend&    grlib_irqmp_state.level;
>>> +
>>> +    DPRINTF("pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x
>>> lvl0:0x%04x\n",
>>> +            grlib_irqmp_state.pending, grlib_irqmp_state.force[0],
>>> +            grlib_irqmp_state.mask[0], level1, level0);
>>
>> The above should stay here, but code below should to go to board level
>> (leon3.c). Then you need to separate device IRQ handling from CPU PIL
>> handling.
>
> If I want to use IRQMP for another machine I will have to duplicate the
> code.
> So I think it is the right place for this this code.

Maybe with the ack signals you can also keep this here.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-06  9:26         ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Fabien Chouteau
  2010-12-06  9:26           ` [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support Fabien Chouteau
  2010-12-06 17:53           ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Blue Swirl
@ 2010-12-12 14:41           ` Andreas Färber
  2010-12-13 17:00             ` Fabien Chouteau
  2 siblings, 1 reply; 51+ messages in thread
From: Andreas Färber @ 2010-12-12 14:41 UTC (permalink / raw)
  To: Blue Swirl, Fabien Chouteau; +Cc: qemu-devel

Am 06.12.2010 um 10:26 schrieb Fabien Chouteau:

> diff --git a/Makefile.target b/Makefile.target
> index 2800f47..f40e04f 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
> else
> obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
> obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o

> +
> +# GRLIB
> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o

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

Andreas

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-11  9:56               ` Blue Swirl
@ 2010-12-13 15:51                 ` Fabien Chouteau
  2010-12-13 18:18                   ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-13 15:51 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/11/2010 10:56 AM, Blue Swirl wrote:
> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>
>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>> ---
>>>>   Makefile.target          |    5 +-
>>>>   hw/leon3.c               |  310
>>>> ++++++++++++++++++++++++++++++++++++++++++++++
>>>>   target-sparc/cpu.h       |   10 ++
>>>>   target-sparc/helper.c    |    2 +-
>>>>   target-sparc/op_helper.c |   30 ++++-
>>>>   5 files changed, 353 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/Makefile.target b/Makefile.target
>>>> index 2800f47..f40e04f 100644
>>>> --- a/Makefile.target
>>>> +++ b/Makefile.target
>>>> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>>>>   else
>>>>   obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>>   obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>>> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>>> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
>>>> +
>>>> +# GRLIB
>>>> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>>>>   endif
>>>>
>>>>   obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>>> diff --git a/hw/leon3.c b/hw/leon3.c
>>>> new file mode 100644
>>>> index 0000000..ba61081
>>>> --- /dev/null
>>>> +++ b/hw/leon3.c
>>>> @@ -0,0 +1,310 @@
>>>> +/*
>>>> + * QEMU Leon3 System Emulator
>>>> + *
>>>> + * Copyright (c) 2010 AdaCore
>>>> + *
>>>> + * Permission is hereby granted, free of charge, to any person obtaining
>>>> a copy
>>>> + * of this software and associated documentation files (the "Software"),
>>>> to deal
>>>> + * in the Software without restriction, including without limitation the
>>>> rights
>>>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
>>>> sell
>>>> + * copies of the Software, and to permit persons to whom the Software is
>>>> + * furnished to do so, subject to the following conditions:
>>>> + *
>>>> + * The above copyright notice and this permission notice shall be
>>>> included in
>>>> + * all copies or substantial portions of the Software.
>>>> + *
>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>> EXPRESS OR
>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>> MERCHANTABILITY,
>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>> SHALL
>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
>>>> OTHER
>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>> ARISING FROM,
>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>>> DEALINGS IN
>>>> + * THE SOFTWARE.
>>>> + */
>>>> +#include "hw.h"
>>>> +#include "qemu-timer.h"
>>>> +#include "qemu-char.h"
>>>> +#include "sysemu.h"
>>>> +#include "boards.h"
>>>> +#include "loader.h"
>>>> +#include "elf.h"
>>>> +
>>>> +#include "grlib.h"
>>>> +
>>>> +/* #define DEBUG_LEON3 */
>>>> +
>>>> +#ifdef DEBUG_LEON3
>>>> +#define DPRINTF(fmt, ...)                                       \
>>>> +    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#else
>>>> +#define DPRINTF(fmt, ...)
>>>> +#endif
>>>> +
>>>> +/* Default system clock.  */
>>>> +#define CPU_CLK (40 * 1000 * 1000)
>>>> +
>>>> +#define PROM_FILENAME        "u-boot.bin"
>>>> +
>>>> +#define MAX_PILS 16
>>>> +
>>>> +typedef struct Leon3State
>>>> +{
>>>> +    uint32_t cache_control;
>>>> +    uint32_t inst_cache_conf;
>>>> +    uint32_t data_cache_conf;
>>>> +
>>>> +    uint64_t entry;             /* save kernel entry in case of reset */
>>>> +} Leon3State;
>>>> +
>>>> +Leon3State leon3_state;
>>>
>>> Again global state, please refactor. Perhaps most of the cache
>>> handling code belong to target-sparc/op_helper.c and this structure to
>>> CPUSPARCState.
>>
>> I will try to find a solution for that.
>> Is it OK to add some Leon3 specific stuff in the CPUSPARCState?
>
> Yes, no problem. You can also drop the intermediate Leon3State
> structure if there is no benefit.
>
>>>> +
>>>> +/* Cache control: emulate the behavior of cache control registers but
>>>> without
>>>> +   any effect on the emulated CPU */
>>>> +
>>>> +#define CACHE_DISABLED 0x0
>>>> +#define CACHE_FROZEN   0x1
>>>> +#define CACHE_ENABLED  0x3
>>>> +
>>>> +/* Cache Control register fields */
>>>> +
>>>> +#define CACHE_CTRL_IF (1<<      4)  /* Instruction Cache Freeze on
>>>> Interrupt */
>>>> +#define CACHE_CTRL_DF (1<<      5)  /* Data Cache Freeze on Interrupt */
>>>> +#define CACHE_CTRL_DP (1<<    14)  /* Data cache flush pending */
>>>> +#define CACHE_CTRL_IP (1<<    15)  /* Instruction cache flush pending */
>>>> +#define CACHE_CTRL_IB (1<<    16)  /* Instruction burst fetch */
>>>> +#define CACHE_CTRL_FI (1<<    21)  /* Flush Instruction cache (Write only)
>>>> */
>>>> +#define CACHE_CTRL_FD (1<<    22)  /* Flush Data cache (Write only) */
>>>> +#define CACHE_CTRL_DS (1<<    23)  /* Data cache snoop enable */
>>>> +
>>>> +void leon3_cache_control_int(void)
>>>> +{
>>>> +    uint32_t state = 0;
>>>> +
>>>> +    if (leon3_state.cache_control&    CACHE_CTRL_IF) {
>>>> +        /* Instruction cache state */
>>>> +        state = leon3_state.cache_control&    0x3;
>>>
>>> Please add a new define CACHE_CTRL_xxx to replace 0x3.
>>>
>>
>> Done.
>>
>>>> +        if (state == CACHE_ENABLED) {
>>>> +            state = CACHE_FROZEN;
>>>> +            DPRINTF("Instruction cache: freeze\n");
>>>> +        }
>>>> +
>>>> +        leon3_state.cache_control&= ~0x3;
>>>> +        leon3_state.cache_control |= state;
>>>> +    }
>>>> +
>>>> +    if (leon3_state.cache_control&    CACHE_CTRL_DF) {
>>>> +        /* Data cache state */
>>>> +        state = (leon3_state.cache_control>>    2)&    0x3;
>>>> +        if (state == CACHE_ENABLED) {
>>>> +            state = CACHE_FROZEN;
>>>> +            DPRINTF("Data cache: freeze\n");
>>>> +        }
>>>> +
>>>> +        leon3_state.cache_control&= ~(0x3<<    2);
>>>> +        leon3_state.cache_control |= (state<<    2);
>>>> +    }
>>>> +}
>>>> +
>>>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
>>>> +{
>>>> +    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned
>>>> int)addr,
>>>> +            (unsigned int)val, size);
>>>
>>> There's PRIx64 to print uint64_t portably, then the casts can be removed.
>>>
>>
>> Fixed.
>>
>>>> +
>>>> +    if (size != 4) {
>>>> +        DPRINTF(" CC 32bits only\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    switch (addr) {
>>>> +        case 0x00:              /* Cache control */
>>>> +
>>>> +            /* These values must always be read as zeros */
>>>> +            val&= ~CACHE_CTRL_FD;
>>>> +            val&= ~CACHE_CTRL_FI;
>>>> +            val&= ~CACHE_CTRL_IB;
>>>> +            val&= ~CACHE_CTRL_IP;
>>>> +            val&= ~CACHE_CTRL_DP;
>>>> +
>>>> +            leon3_state.cache_control = val;
>>>> +            break;
>>>> +        case 0x04:              /* Instruction cache configuration */
>>>> +        case 0x08:              /* Data cache configuration */
>>>> +            /* Read Only */
>>>> +            break;
>>>> +        default:
>>>> +            DPRINTF(" CC write unknown register 0x%04x\n", (int)addr);
>>>> +            break;
>>>> +    };
>>>> +}
>>>> +
>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size)
>>>> +{
>>>> +    uint64_t ret = 0;
>>>> +
>>>> +    if (size != 4) {
>>>> +        DPRINTF(" CC 32bits only\n");
>>>> +        return 0;
>>>> +    }
>>>> +
>>>> +    switch (addr) {
>>>> +        case 0x00:              /* Cache control */
>>>> +            ret = leon3_state.cache_control;
>>>> +            break;
>>>> +        case 0x04:              /* Instruction cache configuration */
>>>> +            ret = leon3_state.inst_cache_conf;
>>>> +            break;
>>>> +        case 0x08:              /* Data cache configuration */
>>>> +            ret = leon3_state.data_cache_conf;
>>>> +            break;
>>>> +        default:
>>>> +            DPRINTF(" CC read unknown register 0x%04x\n", (int)addr);
>>>> +            break;
>>>> +    };
>>>> +    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned
>>>> int)addr,
>>>> +            size, (long unsigned int)ret );
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +void leon3_shutdown(void)
>>>> +{
>>>> +    qemu_system_shutdown_request();
>>>> +}
>>>> +
>>>> +static void main_cpu_reset(void *opaque)
>>>> +{
>>>> +    CPUState *env = opaque;
>>>
>>> Here you can introduce a helper structure to pass PC and NPC, like
>>> sun4u.c ResetData. Then the global state should not be needed anymore.
>>>
>>
>> OK, I've used the sun4u.c reset scheme.
>>
>>>> +
>>>> +    cpu_reset(env);
>>>> +
>>>> +    env->halted = 0;
>>>> +    env->pc     = leon3_state.entry;
>>>> +    env->npc    = leon3_state.entry + 4;
>>>> +
>>>> +    /* Initialize cache control */
>>>> +    leon3_state.cache_control   = 0x0;
>>>> +
>>>> +    /* Configuration registers are read and only always keep those
>>>> predefined
>>>> +       values */
>>>> +    leon3_state.inst_cache_conf = 0x10220000;
>>>> +    leon3_state.data_cache_conf = 0x18220000;
>>>> +}
>>>> +
>>>> +static void leon3_generic_hw_init(ram_addr_t  ram_size,
>>>> +                                  const char *boot_device,
>>>> +                                  const char *kernel_filename,
>>>> +                                  const char *kernel_cmdline,
>>>> +                                  const char *initrd_filename,
>>>> +                                  const char *cpu_model)
>>>> +{
>>>> +    CPUState   *env;
>>>> +    ram_addr_t  ram_offset, prom_offset;
>>>> +    int         ret;
>>>> +    char       *filename;
>>>> +    qemu_irq   *cpu_irqs = NULL;
>>>> +    int         bios_size;
>>>> +    int         prom_size;
>>>> +    int         aligned_bios_size;
>>>> +
>>>> +    /* Init CPU */
>>>> +    if (!cpu_model)
>>>> +        cpu_model = "LEON3";
>>>
>>> Missing braces.
>>
>> Fixed.
>>
>>>
>>>> +
>>>> +    env = cpu_init(cpu_model);
>>>> +    if (!env) {
>>>> +        fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
>>>> +        exit(1);
>>>> +    }
>>>> +
>>>> +    cpu_sparc_set_id(env, 0);
>>>> +
>>>> +    qemu_register_reset(main_cpu_reset, env);
>>>> +
>>>> +    /* Allocate IRQ manager */
>>>> +    grlib_irqmp_create(0x80000200, env,&cpu_irqs, MAX_PILS);
>>>> +
>>>> +    /* Allocate RAM */
>>>> +    if ((uint64_t)ram_size>    (1UL<<    30)) {
>>>> +        fprintf(stderr,
>>>> +                "qemu: Too much memory for this machine: %d, maximum
>>>> 1G\n",
>>>> +                (unsigned int)(ram_size / (1024 * 1024)));
>>>> +        exit(1);
>>>> +    }
>>>> +
>>>> +    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
>>>> +    cpu_register_physical_memory(0x40000000, ram_size, ram_offset |
>>>> IO_MEM_RAM);
>>>> +
>>>> +    /* Allocate BIOS */
>>>> +    prom_size = 8 * 1024 * 1024; /* 8Mb */
>>>> +    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
>>>> +    cpu_register_physical_memory(0x00000000, prom_size,
>>>> +                                 prom_offset | IO_MEM_ROM);
>>>> +
>>>> +    /* Load boot prom */
>>>> +    if (bios_name == NULL)
>>>> +        bios_name = PROM_FILENAME;
>>>> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
>>>> +
>>>> +    bios_size = get_image_size(filename);
>>>> +
>>>> +    if (bios_size>    prom_size) {
>>>> +        fprintf(stderr, "qemu: could not load prom '%s': file too big
>>>> \n",
>>>> +                filename);
>>>> +        exit(1);
>>>> +    }
>>>> +
>>>> +    if (bios_size>    0) {
>>>> +        aligned_bios_size =
>>>> +            (bios_size + TARGET_PAGE_SIZE - 1)&    TARGET_PAGE_MASK;
>>>> +
>>>> +        ret = load_image_targphys(filename, 0x00000000, bios_size);
>>>> +        if (ret<    0 || ret>    prom_size) {
>>>> +            fprintf(stderr, "qemu: could not load prom '%s'\n",
>>>> filename);
>>>> +            exit(1);
>>>> +        }
>>>> +    }
>>>> +    else if (kernel_filename == NULL) {
>>>> +        fprintf(stderr,"Can't read bios image %s\n", filename);
>>>> +        exit(1);
>>>> +    }
>>>> +
>>>> +    /* Can directly load an application. */
>>>> +    if (kernel_filename != NULL) {
>>>> +        long     kernel_size;
>>>> +        uint64_t entry;
>>>> +
>>>> +        kernel_size = load_elf(kernel_filename, NULL, NULL,&entry, NULL,
>>>> NULL,
>>>> +                               1 /* big endian */, ELF_MACHINE, 0);
>>>> +        if (kernel_size<    0) {
>>>> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
>>>> +                    kernel_filename);
>>>> +            exit(1);
>>>> +        }
>>>> +        if (bios_size<= 0) {
>>>> +            /* If there is no bios/monitor, start the application.  */
>>>> +            env->pc = entry;
>>>> +            env->npc = entry + 4;
>>>> +            leon3_state.entry = entry;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    /* Allocate timers */
>>>> +    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
>>>> +
>>>> +    /* Allocate uart */
>>>> +    if (serial_hds[0])
>>>> +        grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
>>>> +}
>>>> +
>>>> +QEMUMachine leon3_generic_machine = {
>>>> +    .name     = "leon3_generic",
>>>> +    .desc     = "Leon-3 generic",
>>>> +    .init     = leon3_generic_hw_init,
>>>> +    .use_scsi = 0,
>>>> +};
>>>> +
>>>> +static void leon3_machine_init(void)
>>>> +{
>>>> +    qemu_register_machine(&leon3_generic_machine);
>>>> +}
>>>> +
>>>> +machine_init(leon3_machine_init);
>>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>>> index 7e0d17c..6020ffd 100644
>>>> --- a/target-sparc/cpu.h
>>>> +++ b/target-sparc/cpu.h
>>>> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
>>>>   /* sun4m.c, sun4u.c */
>>>>   void cpu_check_irqs(CPUSPARCState *env);
>>>>
>>>> +/* grlib_irqmp.c */
>>>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno);
>>>> +
>>>> +/* leon3.c */
>>>> +void     leon3_shutdown(void);
>>>> +void     leon3_cache_control_st(target_ulong addr, uint64_t val, int
>>>> size);
>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size);
>>>> +void     leon3_cache_control_int(void);
>>>> +
>>>> +
>>>>   #if defined (TARGET_SPARC64)
>>>>
>>>>   static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask)
>>>> diff --git a/target-sparc/helper.c b/target-sparc/helper.c
>>>> index e84c312..3bf990f 100644
>>>> --- a/target-sparc/helper.c
>>>> +++ b/target-sparc/helper.c
>>>> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
>>>>          .iu_version = 0xf3000000,
>>>>          .fpu_version = 4<<    17, /* FPU version 4 (Meiko) */
>>>>          .mmu_version = 0xf3000000,
>>>> -        .mmu_bm = 0x00004000,
>>>> +        .mmu_bm = 0x00000000,
>>>>          .mmu_ctpr_mask = 0x007ffff0,
>>>>          .mmu_cxr_mask = 0x0000003f,
>>>>          .mmu_sfsr_mask = 0xffffffff,
>>>> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
>>>> index be3c1e0..85df077 100644
>>>> --- a/target-sparc/op_helper.c
>>>> +++ b/target-sparc/op_helper.c
>>>> @@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi,
>>>> int size, int sign)
>>>>
>>>>      helper_check_align(addr, size - 1);
>>>>      switch (asi) {
>>>> -    case 2: /* SuperSparc MXCC registers */
>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>          switch (addr) {
>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>> +            ret = leon3_cache_control_ld(addr, size);
>>>> +            break;
>>>>          case 0x01c00a00: /* MXCC control register */
>>>>              if (size == 8)
>>>>                  ret = env->mxccregs[3];
>>>> @@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t
>>>> val, int asi, int size)
>>>>   {
>>>>      helper_check_align(addr, size - 1);
>>>>      switch(asi) {
>>>> -    case 2: /* SuperSparc MXCC registers */
>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>          switch (addr) {
>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>> +            leon3_cache_control_st(addr, val, size);
>>>> +            break;
>>>> +
>>>>          case 0x01c00000: /* MXCC stream data register 0 */
>>>>              if (size == 8)
>>>>                  env->mxccdata[0] = val;
>>>> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
>>>>   {
>>>>      int cwp, intno = env->exception_index;
>>>>
>>>> +#if !defined(CONFIG_USER_ONLY)
>>>> +    /* Leon3 shutdown */
>>>> +    if (intno == 0x80&&    env->version == 0xf3000000) {
>>>> +        leon3_shutdown();
>>>> +    }
>>>
>>> This looks like a hack. Should a trap instruction initiate a shutdown?
>>
>> Yes, on Leon3 "ta 0x0" initiates a shutdown.
>
> Then this should be handled during translation. A Leon3 specific CPU
> feature should be added and used much like CHECK_IU_FEATURE (in
> translate.c). Then execution speed would not be affected for non-Leon3
> CPUs.
>

OK, but I don't see how to request a shutdown during translation.

>>>> +#endif
>>>> +
>>>>   #ifdef DEBUG_PCALL
>>>>      if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>>>          static int count;
>>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>>>      env->pc = env->tbr;
>>>>      env->npc = env->pc + 4;
>>>>      env->exception_index = -1;
>>>> +
>>>> +#if !defined(CONFIG_USER_ONLY)
>>>> +    /* IRQ acknowledgment for Leon3 */
>>>> +    if (env->version == 0xf3000000&&    (intno&    ~15) == TT_EXTINT) {
>>>> +        grlib_irqmp_ack (env, intno);
>>>> +        leon3_cache_control_int();
>>>> +    }
>>>
>>> Like this. I don't think a CPU should immediately ack any incoming
>>> interrupts.
>>
>> Leon3 does...
>
> Strange. Then this should be handled at board level (leon3.c).

Well, it's a CPU feature not a board feature.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-11 10:31         ` Blue Swirl
@ 2010-12-13 16:23           ` Fabien Chouteau
  2010-12-13 18:13             ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-13 16:23 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/11/2010 11:31 AM, Blue Swirl wrote:
> On Tue, Dec 7, 2010 at 10:43 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/06/2010 06:25 PM, Blue Swirl wrote:
>>>
>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>> ---
>>>>   hw/grlib_irqmp.c |  416
>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>   1 files changed, 416 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
>>>> new file mode 100644
>>>> index 0000000..69e1553
>>>> --- /dev/null
>>>> +++ b/hw/grlib_irqmp.c
>>>> @@ -0,0 +1,416 @@
>>>> +/*
>>>> + * QEMU GRLIB IRQMP Emulator
>>>> + *
>>>> + * (Multiprocessor and extended interrupt not supported)
>>>> + *
>>>> + * Copyright (c) 2010 AdaCore
>>>> + *
>>>> + * Permission is hereby granted, free of charge, to any person obtaining
>>>> a copy
>>>> + * of this software and associated documentation files (the "Software"),
>>>> to deal
>>>> + * in the Software without restriction, including without limitation the
>>>> rights
>>>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
>>>> sell
>>>> + * copies of the Software, and to permit persons to whom the Software is
>>>> + * furnished to do so, subject to the following conditions:
>>>> + *
>>>> + * The above copyright notice and this permission notice shall be
>>>> included in
>>>> + * all copies or substantial portions of the Software.
>>>> + *
>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>> EXPRESS OR
>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>> MERCHANTABILITY,
>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>> SHALL
>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
>>>> OTHER
>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>> ARISING FROM,
>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>>> DEALINGS IN
>>>> + * THE SOFTWARE.
>>>> + */
>>>> +
>>>> +#include "sysbus.h"
>>>> +#include "cpu.h"
>>>> +
>>>> +#include "grlib.h"
>>>> +
>>>> +/* #define DEBUG_IRQ */
>>>> +
>>>> +#ifdef DEBUG_IRQ
>>>> +#define DPRINTF(fmt, ...)                                       \
>>>> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#else
>>>> +#define DPRINTF(fmt, ...)
>>>> +#endif
>>>> +
>>>> +#define IRQMP_MAX_CPU 16
>>>> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
>>>> +
>>>> +/* Memory mapped register offsets */
>>>> +#define LEVEL_OFFSET     0x00
>>>> +#define PENDING_OFFSET   0x04
>>>> +#define FORCE0_OFFSET    0x08
>>>> +#define CLEAR_OFFSET     0x0C
>>>> +#define MP_STATUS_OFFSET 0x10
>>>> +#define BROADCAST_OFFSET 0x14
>>>> +#define MASK_OFFSET      0x40
>>>> +#define FORCE_OFFSET     0x80
>>>> +#define EXTENDED_OFFSET  0xC0
>>>> +
>>>> +typedef struct IRQMP
>>>> +{
>>>> +    SysBusDevice busdev;
>>>> +
>>>> +    CPUSPARCState *env;
>>>
>>> Devices should never access CPUState directly. Instead, board level
>>> should create CPU irqs and these should then be passed here.
>>>
>>
>> This case is special, Leon3 is a System-On-Chip and some of the components
>> are very close to the processor.
>> IRQMP is not really a peripheral nor a part of the CPU, it's both...
>
> It's not a special case, it could be easily implemented separately.
> MMUs, FPUs or co-processors could be special even if they have been
> implemented as separate chips with real hardware. But we are actually
> not looking at the (historical or current) chip boundaries but more
> like what makes sense from QEMU architecture point of view.

OK then, let's go back to your first comment, why a device can't access
CPUState directly? And why Leon3.c would be better to do that.


-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-12 14:41           ` Andreas Färber
@ 2010-12-13 17:00             ` Fabien Chouteau
  0 siblings, 0 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-13 17:00 UTC (permalink / raw)
  To: Andreas Färber; +Cc: Blue Swirl, qemu-devel

On 12/12/2010 03:41 PM, Andreas Färber wrote:
> Am 06.12.2010 um 10:26 schrieb Fabien Chouteau:
>
>> diff --git a/Makefile.target b/Makefile.target
>> index 2800f47..f40e04f 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>> else
>> obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>> obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
>
>> +
>> +# GRLIB
>> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>
> Aren't these three candidates for Makefile.hw if, as I understood it, 
> they are from some non-sparc-specific component library?

They are sparc specific, but non-leon3-specific.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support.
  2010-12-11  9:59                 ` Blue Swirl
@ 2010-12-13 17:01                   ` Fabien Chouteau
  0 siblings, 0 replies; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-13 17:01 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/11/2010 10:59 AM, Blue Swirl wrote:
> On Tue, Dec 7, 2010 at 11:51 AM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/06/2010 07:01 PM, Blue Swirl wrote:
>>>
>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>> ---
>>>>   hw/leon3.c               |    6 ++++++
>>>>   target-sparc/cpu.h       |    1 +
>>>>   target-sparc/machine.c   |    2 ++
>>>>   target-sparc/translate.c |   10 ++++++++++
>>>>   4 files changed, 19 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/hw/leon3.c b/hw/leon3.c
>>>> index ba61081..9605ce8 100644
>>>> --- a/hw/leon3.c
>>>> +++ b/hw/leon3.c
>>>> @@ -187,6 +187,12 @@ static void main_cpu_reset(void *opaque)
>>>>         values */
>>>>      leon3_state.inst_cache_conf = 0x10220000;
>>>>      leon3_state.data_cache_conf = 0x18220000;
>>>> +
>>>> +    /* Asr17 for Leon3 mono-processor */
>>>> +    env->asr17&= 0<<    28;          /* CPU id */
>>>> +    env->asr17&= 1<<    8;           /* SPARC V8 multiply and divide
>>>> available */
>>>> +    env->asr17&= env->nwindows -1; /* Number of implemented registers
>>>> +                                       windows */
>>>
>>> This is constant...
>>>
>>>>   }
>>>>
>>>>   static void leon3_generic_hw_init(ram_addr_t  ram_size,
>>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>>> index 6020ffd..36d49fc 100644
>>>> --- a/target-sparc/cpu.h
>>>> +++ b/target-sparc/cpu.h
>>>> @@ -341,6 +341,7 @@ typedef struct CPUSPARCState {
>>>>                            from PSR) */
>>>>   #if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
>>>>      uint32_t wim;      /* window invalid mask */
>>>> +    uint32_t asr17;    /* asr17 */
>>>
>>> ... so no new env fields are needed...
>>>
>>>>   #endif
>>>>      target_ulong tbr;  /* trap base register */
>>>>   #if !defined(TARGET_SPARC64)
>>>> diff --git a/target-sparc/machine.c b/target-sparc/machine.c
>>>> index 752e431..c530bd3 100644
>>>> --- a/target-sparc/machine.c
>>>> +++ b/target-sparc/machine.c
>>>> @@ -42,6 +42,7 @@ void cpu_save(QEMUFile *f, void *opaque)
>>>>      qemu_put_be32s(f,&env->pil_in);
>>>>   #ifndef TARGET_SPARC64
>>>>      qemu_put_be32s(f,&env->wim);
>>>> +    qemu_put_be32s(f,&env->asr17);
>>>
>>> ... there's also nothing to save/load...
>>>
>>>>      /* MMU */
>>>>      for (i = 0; i<    32; i++)
>>>>          qemu_put_be32s(f,&env->mmuregs[i]);
>>>> @@ -138,6 +139,7 @@ int cpu_load(QEMUFile *f, void *opaque, int
>>>> version_id)
>>>>      qemu_get_be32s(f,&env->pil_in);
>>>>   #ifndef TARGET_SPARC64
>>>>      qemu_get_be32s(f,&env->wim);
>>>> +    qemu_get_be32s(f,&env->asr17);
>>>>      /* MMU */
>>>>      for (i = 0; i<    32; i++)
>>>>          qemu_get_be32s(f,&env->mmuregs[i]);
>>>> diff --git a/target-sparc/translate.c b/target-sparc/translate.c
>>>> index 23f9519..65de614 100644
>>>> --- a/target-sparc/translate.c
>>>> +++ b/target-sparc/translate.c
>>>> @@ -58,6 +58,7 @@ static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr,
>>>> cpu_ver;
>>>>   static TCGv_i32 cpu_softint;
>>>>   #else
>>>>   static TCGv cpu_wim;
>>>> +static TCGv cpu_asr17;
>>>>   #endif
>>>>   /* local register indexes (only used inside old micro ops) */
>>>>   static TCGv cpu_tmp0;
>>>> @@ -2049,6 +2050,8 @@ static void disas_sparc_insn(DisasContext * dc)
>>>>                  rs1 = GET_FIELD(insn, 13, 17);
>>>>                  switch(rs1) {
>>>>                  case 0: /* rdy */
>>>> +                    gen_movl_TN_reg(rd, cpu_y);
>>>> +                    break;
>>>>   #ifndef TARGET_SPARC64
>>>>                  case 0x01 ... 0x0e: /* undefined in the SPARCv8
>>>>                                         manual, rdy on the microSPARC
>>>> @@ -2058,6 +2061,11 @@ static void disas_sparc_insn(DisasContext * dc)
>>>>                  case 0x10 ... 0x1f: /* implementation-dependent in the
>>>>                                         SPARCv8 manual, rdy on the
>>>>                                         microSPARC II */
>>>> +
>>>> +                    if (rs1 == 0x11) { /* Read %asr17 */
>>>> +                        gen_movl_TN_reg(rd, cpu_asr17);
>>>
>>> Instead:
>>> r_const = tcg_const_tl(asr constants  | dc->def->nwindows - 1);
>>> gen_movl_TN_reg(rd, r_const);
>>> tcg_temp_free(r_const);
>>
>> OK for me, if it is acceptable to have this Leon3's specific behavior for
>> all the SPARC32 CPUs.
>
> This will not affect other CPUs when you use CPU feature bits to make
> the ASR only available to Leon3.

OK, I will try that.

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP as defined in GRLIB IP Core User's Manual.
  2010-12-13 16:23           ` Fabien Chouteau
@ 2010-12-13 18:13             ` Blue Swirl
  0 siblings, 0 replies; 51+ messages in thread
From: Blue Swirl @ 2010-12-13 18:13 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 13, 2010 at 4:23 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/11/2010 11:31 AM, Blue Swirl wrote:
>>
>> On Tue, Dec 7, 2010 at 10:43 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> On 12/06/2010 06:25 PM, Blue Swirl wrote:
>>>>
>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>  wrote:
>>>>>
>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>> ---
>>>>>  hw/grlib_irqmp.c |  416
>>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>  1 files changed, 416 insertions(+), 0 deletions(-)
>>>>>
>>>>> diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
>>>>> new file mode 100644
>>>>> index 0000000..69e1553
>>>>> --- /dev/null
>>>>> +++ b/hw/grlib_irqmp.c
>>>>> @@ -0,0 +1,416 @@
>>>>> +/*
>>>>> + * QEMU GRLIB IRQMP Emulator
>>>>> + *
>>>>> + * (Multiprocessor and extended interrupt not supported)
>>>>> + *
>>>>> + * Copyright (c) 2010 AdaCore
>>>>> + *
>>>>> + * Permission is hereby granted, free of charge, to any person
>>>>> obtaining
>>>>> a copy
>>>>> + * of this software and associated documentation files (the
>>>>> "Software"),
>>>>> to deal
>>>>> + * in the Software without restriction, including without limitation
>>>>> the
>>>>> rights
>>>>> + * to use, copy, modify, merge, publish, distribute, sublicense,
>>>>> and/or
>>>>> sell
>>>>> + * copies of the Software, and to permit persons to whom the Software
>>>>> is
>>>>> + * furnished to do so, subject to the following conditions:
>>>>> + *
>>>>> + * The above copyright notice and this permission notice shall be
>>>>> included in
>>>>> + * all copies or substantial portions of the Software.
>>>>> + *
>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>> EXPRESS OR
>>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>>> MERCHANTABILITY,
>>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>>> SHALL
>>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
>>>>> OR
>>>>> OTHER
>>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>>> ARISING FROM,
>>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>>>> DEALINGS IN
>>>>> + * THE SOFTWARE.
>>>>> + */
>>>>> +
>>>>> +#include "sysbus.h"
>>>>> +#include "cpu.h"
>>>>> +
>>>>> +#include "grlib.h"
>>>>> +
>>>>> +/* #define DEBUG_IRQ */
>>>>> +
>>>>> +#ifdef DEBUG_IRQ
>>>>> +#define DPRINTF(fmt, ...)                                       \
>>>>> +    do { printf("IRQMP: " fmt , ## __VA_ARGS__); } while (0)
>>>>> +#else
>>>>> +#define DPRINTF(fmt, ...)
>>>>> +#endif
>>>>> +
>>>>> +#define IRQMP_MAX_CPU 16
>>>>> +#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
>>>>> +
>>>>> +/* Memory mapped register offsets */
>>>>> +#define LEVEL_OFFSET     0x00
>>>>> +#define PENDING_OFFSET   0x04
>>>>> +#define FORCE0_OFFSET    0x08
>>>>> +#define CLEAR_OFFSET     0x0C
>>>>> +#define MP_STATUS_OFFSET 0x10
>>>>> +#define BROADCAST_OFFSET 0x14
>>>>> +#define MASK_OFFSET      0x40
>>>>> +#define FORCE_OFFSET     0x80
>>>>> +#define EXTENDED_OFFSET  0xC0
>>>>> +
>>>>> +typedef struct IRQMP
>>>>> +{
>>>>> +    SysBusDevice busdev;
>>>>> +
>>>>> +    CPUSPARCState *env;
>>>>
>>>> Devices should never access CPUState directly. Instead, board level
>>>> should create CPU irqs and these should then be passed here.
>>>>
>>>
>>> This case is special, Leon3 is a System-On-Chip and some of the
>>> components
>>> are very close to the processor.
>>> IRQMP is not really a peripheral nor a part of the CPU, it's both...
>>
>> It's not a special case, it could be easily implemented separately.
>> MMUs, FPUs or co-processors could be special even if they have been
>> implemented as separate chips with real hardware. But we are actually
>> not looking at the (historical or current) chip boundaries but more
>> like what makes sense from QEMU architecture point of view.
>
> OK then, let's go back to your first comment, why a device can't access
> CPUState directly? And why Leon3.c would be better to do that.

Devices should mind their own business, not other devices' or
especially CPUs' businesses. The signals between devices should be
made with qemu_irq or bus style interfaces. Board case is different
because there we interface with QEMU host. Not all devices are very
clean yet.

This has been discussed a few times earlier, please see the list
archives if you really are interested.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-13 15:51                 ` Fabien Chouteau
@ 2010-12-13 18:18                   ` Blue Swirl
  2010-12-15 17:47                     ` Fabien Chouteau
  0 siblings, 1 reply; 51+ messages in thread
From: Blue Swirl @ 2010-12-13 18:18 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>
>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>
>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>  wrote:
>>>>>
>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>> ---
>>>>>  Makefile.target          |    5 +-
>>>>>  hw/leon3.c               |  310
>>>>> ++++++++++++++++++++++++++++++++++++++++++++++
>>>>>  target-sparc/cpu.h       |   10 ++
>>>>>  target-sparc/helper.c    |    2 +-
>>>>>  target-sparc/op_helper.c |   30 ++++-
>>>>>  5 files changed, 353 insertions(+), 4 deletions(-)
>>>>>
>>>>> diff --git a/Makefile.target b/Makefile.target
>>>>> index 2800f47..f40e04f 100644
>>>>> --- a/Makefile.target
>>>>> +++ b/Makefile.target
>>>>> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>>>>>  else
>>>>>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>>>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>>>> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>>>> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
>>>>> +
>>>>> +# GRLIB
>>>>> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>>>>>  endif
>>>>>
>>>>>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>>>> diff --git a/hw/leon3.c b/hw/leon3.c
>>>>> new file mode 100644
>>>>> index 0000000..ba61081
>>>>> --- /dev/null
>>>>> +++ b/hw/leon3.c
>>>>> @@ -0,0 +1,310 @@
>>>>> +/*
>>>>> + * QEMU Leon3 System Emulator
>>>>> + *
>>>>> + * Copyright (c) 2010 AdaCore
>>>>> + *
>>>>> + * Permission is hereby granted, free of charge, to any person
>>>>> obtaining
>>>>> a copy
>>>>> + * of this software and associated documentation files (the
>>>>> "Software"),
>>>>> to deal
>>>>> + * in the Software without restriction, including without limitation
>>>>> the
>>>>> rights
>>>>> + * to use, copy, modify, merge, publish, distribute, sublicense,
>>>>> and/or
>>>>> sell
>>>>> + * copies of the Software, and to permit persons to whom the Software
>>>>> is
>>>>> + * furnished to do so, subject to the following conditions:
>>>>> + *
>>>>> + * The above copyright notice and this permission notice shall be
>>>>> included in
>>>>> + * all copies or substantial portions of the Software.
>>>>> + *
>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>> EXPRESS OR
>>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>>> MERCHANTABILITY,
>>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>>> SHALL
>>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
>>>>> OR
>>>>> OTHER
>>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>>> ARISING FROM,
>>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>>>> DEALINGS IN
>>>>> + * THE SOFTWARE.
>>>>> + */
>>>>> +#include "hw.h"
>>>>> +#include "qemu-timer.h"
>>>>> +#include "qemu-char.h"
>>>>> +#include "sysemu.h"
>>>>> +#include "boards.h"
>>>>> +#include "loader.h"
>>>>> +#include "elf.h"
>>>>> +
>>>>> +#include "grlib.h"
>>>>> +
>>>>> +/* #define DEBUG_LEON3 */
>>>>> +
>>>>> +#ifdef DEBUG_LEON3
>>>>> +#define DPRINTF(fmt, ...)                                       \
>>>>> +    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
>>>>> +#else
>>>>> +#define DPRINTF(fmt, ...)
>>>>> +#endif
>>>>> +
>>>>> +/* Default system clock.  */
>>>>> +#define CPU_CLK (40 * 1000 * 1000)
>>>>> +
>>>>> +#define PROM_FILENAME        "u-boot.bin"
>>>>> +
>>>>> +#define MAX_PILS 16
>>>>> +
>>>>> +typedef struct Leon3State
>>>>> +{
>>>>> +    uint32_t cache_control;
>>>>> +    uint32_t inst_cache_conf;
>>>>> +    uint32_t data_cache_conf;
>>>>> +
>>>>> +    uint64_t entry;             /* save kernel entry in case of reset
>>>>> */
>>>>> +} Leon3State;
>>>>> +
>>>>> +Leon3State leon3_state;
>>>>
>>>> Again global state, please refactor. Perhaps most of the cache
>>>> handling code belong to target-sparc/op_helper.c and this structure to
>>>> CPUSPARCState.
>>>
>>> I will try to find a solution for that.
>>> Is it OK to add some Leon3 specific stuff in the CPUSPARCState?
>>
>> Yes, no problem. You can also drop the intermediate Leon3State
>> structure if there is no benefit.
>>
>>>>> +
>>>>> +/* Cache control: emulate the behavior of cache control registers but
>>>>> without
>>>>> +   any effect on the emulated CPU */
>>>>> +
>>>>> +#define CACHE_DISABLED 0x0
>>>>> +#define CACHE_FROZEN   0x1
>>>>> +#define CACHE_ENABLED  0x3
>>>>> +
>>>>> +/* Cache Control register fields */
>>>>> +
>>>>> +#define CACHE_CTRL_IF (1<<      4)  /* Instruction Cache Freeze on
>>>>> Interrupt */
>>>>> +#define CACHE_CTRL_DF (1<<      5)  /* Data Cache Freeze on Interrupt
>>>>> */
>>>>> +#define CACHE_CTRL_DP (1<<    14)  /* Data cache flush pending */
>>>>> +#define CACHE_CTRL_IP (1<<    15)  /* Instruction cache flush pending
>>>>> */
>>>>> +#define CACHE_CTRL_IB (1<<    16)  /* Instruction burst fetch */
>>>>> +#define CACHE_CTRL_FI (1<<    21)  /* Flush Instruction cache (Write
>>>>> only)
>>>>> */
>>>>> +#define CACHE_CTRL_FD (1<<    22)  /* Flush Data cache (Write only) */
>>>>> +#define CACHE_CTRL_DS (1<<    23)  /* Data cache snoop enable */
>>>>> +
>>>>> +void leon3_cache_control_int(void)
>>>>> +{
>>>>> +    uint32_t state = 0;
>>>>> +
>>>>> +    if (leon3_state.cache_control&    CACHE_CTRL_IF) {
>>>>> +        /* Instruction cache state */
>>>>> +        state = leon3_state.cache_control&    0x3;
>>>>
>>>> Please add a new define CACHE_CTRL_xxx to replace 0x3.
>>>>
>>>
>>> Done.
>>>
>>>>> +        if (state == CACHE_ENABLED) {
>>>>> +            state = CACHE_FROZEN;
>>>>> +            DPRINTF("Instruction cache: freeze\n");
>>>>> +        }
>>>>> +
>>>>> +        leon3_state.cache_control&= ~0x3;
>>>>> +        leon3_state.cache_control |= state;
>>>>> +    }
>>>>> +
>>>>> +    if (leon3_state.cache_control&    CACHE_CTRL_DF) {
>>>>> +        /* Data cache state */
>>>>> +        state = (leon3_state.cache_control>>    2)&    0x3;
>>>>> +        if (state == CACHE_ENABLED) {
>>>>> +            state = CACHE_FROZEN;
>>>>> +            DPRINTF("Data cache: freeze\n");
>>>>> +        }
>>>>> +
>>>>> +        leon3_state.cache_control&= ~(0x3<<    2);
>>>>> +        leon3_state.cache_control |= (state<<    2);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
>>>>> +{
>>>>> +    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned
>>>>> int)addr,
>>>>> +            (unsigned int)val, size);
>>>>
>>>> There's PRIx64 to print uint64_t portably, then the casts can be
>>>> removed.
>>>>
>>>
>>> Fixed.
>>>
>>>>> +
>>>>> +    if (size != 4) {
>>>>> +        DPRINTF(" CC 32bits only\n");
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    switch (addr) {
>>>>> +        case 0x00:              /* Cache control */
>>>>> +
>>>>> +            /* These values must always be read as zeros */
>>>>> +            val&= ~CACHE_CTRL_FD;
>>>>> +            val&= ~CACHE_CTRL_FI;
>>>>> +            val&= ~CACHE_CTRL_IB;
>>>>> +            val&= ~CACHE_CTRL_IP;
>>>>> +            val&= ~CACHE_CTRL_DP;
>>>>> +
>>>>> +            leon3_state.cache_control = val;
>>>>> +            break;
>>>>> +        case 0x04:              /* Instruction cache configuration */
>>>>> +        case 0x08:              /* Data cache configuration */
>>>>> +            /* Read Only */
>>>>> +            break;
>>>>> +        default:
>>>>> +            DPRINTF(" CC write unknown register 0x%04x\n", (int)addr);
>>>>> +            break;
>>>>> +    };
>>>>> +}
>>>>> +
>>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size)
>>>>> +{
>>>>> +    uint64_t ret = 0;
>>>>> +
>>>>> +    if (size != 4) {
>>>>> +        DPRINTF(" CC 32bits only\n");
>>>>> +        return 0;
>>>>> +    }
>>>>> +
>>>>> +    switch (addr) {
>>>>> +        case 0x00:              /* Cache control */
>>>>> +            ret = leon3_state.cache_control;
>>>>> +            break;
>>>>> +        case 0x04:              /* Instruction cache configuration */
>>>>> +            ret = leon3_state.inst_cache_conf;
>>>>> +            break;
>>>>> +        case 0x08:              /* Data cache configuration */
>>>>> +            ret = leon3_state.data_cache_conf;
>>>>> +            break;
>>>>> +        default:
>>>>> +            DPRINTF(" CC read unknown register 0x%04x\n", (int)addr);
>>>>> +            break;
>>>>> +    };
>>>>> +    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned
>>>>> int)addr,
>>>>> +            size, (long unsigned int)ret );
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +void leon3_shutdown(void)
>>>>> +{
>>>>> +    qemu_system_shutdown_request();
>>>>> +}
>>>>> +
>>>>> +static void main_cpu_reset(void *opaque)
>>>>> +{
>>>>> +    CPUState *env = opaque;
>>>>
>>>> Here you can introduce a helper structure to pass PC and NPC, like
>>>> sun4u.c ResetData. Then the global state should not be needed anymore.
>>>>
>>>
>>> OK, I've used the sun4u.c reset scheme.
>>>
>>>>> +
>>>>> +    cpu_reset(env);
>>>>> +
>>>>> +    env->halted = 0;
>>>>> +    env->pc     = leon3_state.entry;
>>>>> +    env->npc    = leon3_state.entry + 4;
>>>>> +
>>>>> +    /* Initialize cache control */
>>>>> +    leon3_state.cache_control   = 0x0;
>>>>> +
>>>>> +    /* Configuration registers are read and only always keep those
>>>>> predefined
>>>>> +       values */
>>>>> +    leon3_state.inst_cache_conf = 0x10220000;
>>>>> +    leon3_state.data_cache_conf = 0x18220000;
>>>>> +}
>>>>> +
>>>>> +static void leon3_generic_hw_init(ram_addr_t  ram_size,
>>>>> +                                  const char *boot_device,
>>>>> +                                  const char *kernel_filename,
>>>>> +                                  const char *kernel_cmdline,
>>>>> +                                  const char *initrd_filename,
>>>>> +                                  const char *cpu_model)
>>>>> +{
>>>>> +    CPUState   *env;
>>>>> +    ram_addr_t  ram_offset, prom_offset;
>>>>> +    int         ret;
>>>>> +    char       *filename;
>>>>> +    qemu_irq   *cpu_irqs = NULL;
>>>>> +    int         bios_size;
>>>>> +    int         prom_size;
>>>>> +    int         aligned_bios_size;
>>>>> +
>>>>> +    /* Init CPU */
>>>>> +    if (!cpu_model)
>>>>> +        cpu_model = "LEON3";
>>>>
>>>> Missing braces.
>>>
>>> Fixed.
>>>
>>>>
>>>>> +
>>>>> +    env = cpu_init(cpu_model);
>>>>> +    if (!env) {
>>>>> +        fprintf(stderr, "qemu: Unable to find Sparc CPU
>>>>> definition\n");
>>>>> +        exit(1);
>>>>> +    }
>>>>> +
>>>>> +    cpu_sparc_set_id(env, 0);
>>>>> +
>>>>> +    qemu_register_reset(main_cpu_reset, env);
>>>>> +
>>>>> +    /* Allocate IRQ manager */
>>>>> +    grlib_irqmp_create(0x80000200, env,&cpu_irqs, MAX_PILS);
>>>>> +
>>>>> +    /* Allocate RAM */
>>>>> +    if ((uint64_t)ram_size>    (1UL<<    30)) {
>>>>> +        fprintf(stderr,
>>>>> +                "qemu: Too much memory for this machine: %d, maximum
>>>>> 1G\n",
>>>>> +                (unsigned int)(ram_size / (1024 * 1024)));
>>>>> +        exit(1);
>>>>> +    }
>>>>> +
>>>>> +    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
>>>>> +    cpu_register_physical_memory(0x40000000, ram_size, ram_offset |
>>>>> IO_MEM_RAM);
>>>>> +
>>>>> +    /* Allocate BIOS */
>>>>> +    prom_size = 8 * 1024 * 1024; /* 8Mb */
>>>>> +    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
>>>>> +    cpu_register_physical_memory(0x00000000, prom_size,
>>>>> +                                 prom_offset | IO_MEM_ROM);
>>>>> +
>>>>> +    /* Load boot prom */
>>>>> +    if (bios_name == NULL)
>>>>> +        bios_name = PROM_FILENAME;
>>>>> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
>>>>> +
>>>>> +    bios_size = get_image_size(filename);
>>>>> +
>>>>> +    if (bios_size>    prom_size) {
>>>>> +        fprintf(stderr, "qemu: could not load prom '%s': file too big
>>>>> \n",
>>>>> +                filename);
>>>>> +        exit(1);
>>>>> +    }
>>>>> +
>>>>> +    if (bios_size>    0) {
>>>>> +        aligned_bios_size =
>>>>> +            (bios_size + TARGET_PAGE_SIZE - 1)&    TARGET_PAGE_MASK;
>>>>> +
>>>>> +        ret = load_image_targphys(filename, 0x00000000, bios_size);
>>>>> +        if (ret<    0 || ret>    prom_size) {
>>>>> +            fprintf(stderr, "qemu: could not load prom '%s'\n",
>>>>> filename);
>>>>> +            exit(1);
>>>>> +        }
>>>>> +    }
>>>>> +    else if (kernel_filename == NULL) {
>>>>> +        fprintf(stderr,"Can't read bios image %s\n", filename);
>>>>> +        exit(1);
>>>>> +    }
>>>>> +
>>>>> +    /* Can directly load an application. */
>>>>> +    if (kernel_filename != NULL) {
>>>>> +        long     kernel_size;
>>>>> +        uint64_t entry;
>>>>> +
>>>>> +        kernel_size = load_elf(kernel_filename, NULL, NULL,&entry,
>>>>> NULL,
>>>>> NULL,
>>>>> +                               1 /* big endian */, ELF_MACHINE, 0);
>>>>> +        if (kernel_size<    0) {
>>>>> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
>>>>> +                    kernel_filename);
>>>>> +            exit(1);
>>>>> +        }
>>>>> +        if (bios_size<= 0) {
>>>>> +            /* If there is no bios/monitor, start the application.  */
>>>>> +            env->pc = entry;
>>>>> +            env->npc = entry + 4;
>>>>> +            leon3_state.entry = entry;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    /* Allocate timers */
>>>>> +    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
>>>>> +
>>>>> +    /* Allocate uart */
>>>>> +    if (serial_hds[0])
>>>>> +        grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
>>>>> +}
>>>>> +
>>>>> +QEMUMachine leon3_generic_machine = {
>>>>> +    .name     = "leon3_generic",
>>>>> +    .desc     = "Leon-3 generic",
>>>>> +    .init     = leon3_generic_hw_init,
>>>>> +    .use_scsi = 0,
>>>>> +};
>>>>> +
>>>>> +static void leon3_machine_init(void)
>>>>> +{
>>>>> +    qemu_register_machine(&leon3_generic_machine);
>>>>> +}
>>>>> +
>>>>> +machine_init(leon3_machine_init);
>>>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>>>> index 7e0d17c..6020ffd 100644
>>>>> --- a/target-sparc/cpu.h
>>>>> +++ b/target-sparc/cpu.h
>>>>> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
>>>>>  /* sun4m.c, sun4u.c */
>>>>>  void cpu_check_irqs(CPUSPARCState *env);
>>>>>
>>>>> +/* grlib_irqmp.c */
>>>>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno);
>>>>> +
>>>>> +/* leon3.c */
>>>>> +void     leon3_shutdown(void);
>>>>> +void     leon3_cache_control_st(target_ulong addr, uint64_t val, int
>>>>> size);
>>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size);
>>>>> +void     leon3_cache_control_int(void);
>>>>> +
>>>>> +
>>>>>  #if defined (TARGET_SPARC64)
>>>>>
>>>>>  static inline int compare_masked(uint64_t x, uint64_t y, uint64_t
>>>>> mask)
>>>>> diff --git a/target-sparc/helper.c b/target-sparc/helper.c
>>>>> index e84c312..3bf990f 100644
>>>>> --- a/target-sparc/helper.c
>>>>> +++ b/target-sparc/helper.c
>>>>> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
>>>>>         .iu_version = 0xf3000000,
>>>>>         .fpu_version = 4<<    17, /* FPU version 4 (Meiko) */
>>>>>         .mmu_version = 0xf3000000,
>>>>> -        .mmu_bm = 0x00004000,
>>>>> +        .mmu_bm = 0x00000000,
>>>>>         .mmu_ctpr_mask = 0x007ffff0,
>>>>>         .mmu_cxr_mask = 0x0000003f,
>>>>>         .mmu_sfsr_mask = 0xffffffff,
>>>>> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
>>>>> index be3c1e0..85df077 100644
>>>>> --- a/target-sparc/op_helper.c
>>>>> +++ b/target-sparc/op_helper.c
>>>>> @@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int
>>>>> asi,
>>>>> int size, int sign)
>>>>>
>>>>>     helper_check_align(addr, size - 1);
>>>>>     switch (asi) {
>>>>> -    case 2: /* SuperSparc MXCC registers */
>>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>>         switch (addr) {
>>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>>> +            ret = leon3_cache_control_ld(addr, size);
>>>>> +            break;
>>>>>         case 0x01c00a00: /* MXCC control register */
>>>>>             if (size == 8)
>>>>>                 ret = env->mxccregs[3];
>>>>> @@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t
>>>>> val, int asi, int size)
>>>>>  {
>>>>>     helper_check_align(addr, size - 1);
>>>>>     switch(asi) {
>>>>> -    case 2: /* SuperSparc MXCC registers */
>>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>>         switch (addr) {
>>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>>> +            leon3_cache_control_st(addr, val, size);
>>>>> +            break;
>>>>> +
>>>>>         case 0x01c00000: /* MXCC stream data register 0 */
>>>>>             if (size == 8)
>>>>>                 env->mxccdata[0] = val;
>>>>> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
>>>>>  {
>>>>>     int cwp, intno = env->exception_index;
>>>>>
>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>> +    /* Leon3 shutdown */
>>>>> +    if (intno == 0x80&&    env->version == 0xf3000000) {
>>>>> +        leon3_shutdown();
>>>>> +    }
>>>>
>>>> This looks like a hack. Should a trap instruction initiate a shutdown?
>>>
>>> Yes, on Leon3 "ta 0x0" initiates a shutdown.
>>
>> Then this should be handled during translation. A Leon3 specific CPU
>> feature should be added and used much like CHECK_IU_FEATURE (in
>> translate.c). Then execution speed would not be affected for non-Leon3
>> CPUs.
>>
>
> OK, but I don't see how to request a shutdown during translation.

Just create a helper which calls shutdown, translator should insert a
call to that.

>>>>> +#endif
>>>>> +
>>>>>  #ifdef DEBUG_PCALL
>>>>>     if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>>>>         static int count;
>>>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>>>>     env->pc = env->tbr;
>>>>>     env->npc = env->pc + 4;
>>>>>     env->exception_index = -1;
>>>>> +
>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>> +    /* IRQ acknowledgment for Leon3 */
>>>>> +    if (env->version == 0xf3000000&&    (intno&    ~15) == TT_EXTINT)
>>>>> {
>>>>> +        grlib_irqmp_ack (env, intno);
>>>>> +        leon3_cache_control_int();
>>>>> +    }
>>>>
>>>> Like this. I don't think a CPU should immediately ack any incoming
>>>> interrupts.
>>>
>>> Leon3 does...
>>
>> Strange. Then this should be handled at board level (leon3.c).
>
> Well, it's a CPU feature not a board feature.

Maybe, but we don't want to clutter interrupt handling with this.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-13 18:18                   ` Blue Swirl
@ 2010-12-15 17:47                     ` Fabien Chouteau
  2010-12-17 19:14                       ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-15 17:47 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/13/2010 07:18 PM, Blue Swirl wrote:
> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>
>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>
>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>   wrote:
>>>>>>
>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>>> ---
>>>>>>   Makefile.target          |    5 +-
>>>>>>   hw/leon3.c               |  310
>>>>>> ++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>   target-sparc/cpu.h       |   10 ++
>>>>>>   target-sparc/helper.c    |    2 +-
>>>>>>   target-sparc/op_helper.c |   30 ++++-
>>>>>>   5 files changed, 353 insertions(+), 4 deletions(-)
>>>>>>
>>>>>> diff --git a/Makefile.target b/Makefile.target
>>>>>> index 2800f47..f40e04f 100644
>>>>>> --- a/Makefile.target
>>>>>> +++ b/Makefile.target
>>>>>> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>>>>>>   else
>>>>>>   obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>>>>   obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>>>>> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>>>>> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
>>>>>> +
>>>>>> +# GRLIB
>>>>>> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>>>>>>   endif
>>>>>>
>>>>>>   obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>>>>> diff --git a/hw/leon3.c b/hw/leon3.c
>>>>>> new file mode 100644
>>>>>> index 0000000..ba61081
>>>>>> --- /dev/null
>>>>>> +++ b/hw/leon3.c
>>>>>> @@ -0,0 +1,310 @@
>>>>>> +/*
>>>>>> + * QEMU Leon3 System Emulator
>>>>>> + *
>>>>>> + * Copyright (c) 2010 AdaCore
>>>>>> + *
>>>>>> + * Permission is hereby granted, free of charge, to any person
>>>>>> obtaining
>>>>>> a copy
>>>>>> + * of this software and associated documentation files (the
>>>>>> "Software"),
>>>>>> to deal
>>>>>> + * in the Software without restriction, including without limitation
>>>>>> the
>>>>>> rights
>>>>>> + * to use, copy, modify, merge, publish, distribute, sublicense,
>>>>>> and/or
>>>>>> sell
>>>>>> + * copies of the Software, and to permit persons to whom the Software
>>>>>> is
>>>>>> + * furnished to do so, subject to the following conditions:
>>>>>> + *
>>>>>> + * The above copyright notice and this permission notice shall be
>>>>>> included in
>>>>>> + * all copies or substantial portions of the Software.
>>>>>> + *
>>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>>> EXPRESS OR
>>>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>>>> MERCHANTABILITY,
>>>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>>>> SHALL
>>>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
>>>>>> OR
>>>>>> OTHER
>>>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>>>> ARISING FROM,
>>>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>>>>> DEALINGS IN
>>>>>> + * THE SOFTWARE.
>>>>>> + */
>>>>>> +#include "hw.h"
>>>>>> +#include "qemu-timer.h"
>>>>>> +#include "qemu-char.h"
>>>>>> +#include "sysemu.h"
>>>>>> +#include "boards.h"
>>>>>> +#include "loader.h"
>>>>>> +#include "elf.h"
>>>>>> +
>>>>>> +#include "grlib.h"
>>>>>> +
>>>>>> +/* #define DEBUG_LEON3 */
>>>>>> +
>>>>>> +#ifdef DEBUG_LEON3
>>>>>> +#define DPRINTF(fmt, ...)                                       \
>>>>>> +    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
>>>>>> +#else
>>>>>> +#define DPRINTF(fmt, ...)
>>>>>> +#endif
>>>>>> +
>>>>>> +/* Default system clock.  */
>>>>>> +#define CPU_CLK (40 * 1000 * 1000)
>>>>>> +
>>>>>> +#define PROM_FILENAME        "u-boot.bin"
>>>>>> +
>>>>>> +#define MAX_PILS 16
>>>>>> +
>>>>>> +typedef struct Leon3State
>>>>>> +{
>>>>>> +    uint32_t cache_control;
>>>>>> +    uint32_t inst_cache_conf;
>>>>>> +    uint32_t data_cache_conf;
>>>>>> +
>>>>>> +    uint64_t entry;             /* save kernel entry in case of reset
>>>>>> */
>>>>>> +} Leon3State;
>>>>>> +
>>>>>> +Leon3State leon3_state;
>>>>>
>>>>> Again global state, please refactor. Perhaps most of the cache
>>>>> handling code belong to target-sparc/op_helper.c and this structure to
>>>>> CPUSPARCState.
>>>>
>>>> I will try to find a solution for that.
>>>> Is it OK to add some Leon3 specific stuff in the CPUSPARCState?
>>>
>>> Yes, no problem. You can also drop the intermediate Leon3State
>>> structure if there is no benefit.
>>>
>>>>>> +
>>>>>> +/* Cache control: emulate the behavior of cache control registers but
>>>>>> without
>>>>>> +   any effect on the emulated CPU */
>>>>>> +
>>>>>> +#define CACHE_DISABLED 0x0
>>>>>> +#define CACHE_FROZEN   0x1
>>>>>> +#define CACHE_ENABLED  0x3
>>>>>> +
>>>>>> +/* Cache Control register fields */
>>>>>> +
>>>>>> +#define CACHE_CTRL_IF (1<<        4)  /* Instruction Cache Freeze on
>>>>>> Interrupt */
>>>>>> +#define CACHE_CTRL_DF (1<<        5)  /* Data Cache Freeze on Interrupt
>>>>>> */
>>>>>> +#define CACHE_CTRL_DP (1<<      14)  /* Data cache flush pending */
>>>>>> +#define CACHE_CTRL_IP (1<<      15)  /* Instruction cache flush pending
>>>>>> */
>>>>>> +#define CACHE_CTRL_IB (1<<      16)  /* Instruction burst fetch */
>>>>>> +#define CACHE_CTRL_FI (1<<      21)  /* Flush Instruction cache (Write
>>>>>> only)
>>>>>> */
>>>>>> +#define CACHE_CTRL_FD (1<<      22)  /* Flush Data cache (Write only) */
>>>>>> +#define CACHE_CTRL_DS (1<<      23)  /* Data cache snoop enable */
>>>>>> +
>>>>>> +void leon3_cache_control_int(void)
>>>>>> +{
>>>>>> +    uint32_t state = 0;
>>>>>> +
>>>>>> +    if (leon3_state.cache_control&      CACHE_CTRL_IF) {
>>>>>> +        /* Instruction cache state */
>>>>>> +        state = leon3_state.cache_control&      0x3;
>>>>>
>>>>> Please add a new define CACHE_CTRL_xxx to replace 0x3.
>>>>>
>>>>
>>>> Done.
>>>>
>>>>>> +        if (state == CACHE_ENABLED) {
>>>>>> +            state = CACHE_FROZEN;
>>>>>> +            DPRINTF("Instruction cache: freeze\n");
>>>>>> +        }
>>>>>> +
>>>>>> +        leon3_state.cache_control&= ~0x3;
>>>>>> +        leon3_state.cache_control |= state;
>>>>>> +    }
>>>>>> +
>>>>>> +    if (leon3_state.cache_control&      CACHE_CTRL_DF) {
>>>>>> +        /* Data cache state */
>>>>>> +        state = (leon3_state.cache_control>>      2)&      0x3;
>>>>>> +        if (state == CACHE_ENABLED) {
>>>>>> +            state = CACHE_FROZEN;
>>>>>> +            DPRINTF("Data cache: freeze\n");
>>>>>> +        }
>>>>>> +
>>>>>> +        leon3_state.cache_control&= ~(0x3<<      2);
>>>>>> +        leon3_state.cache_control |= (state<<      2);
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
>>>>>> +{
>>>>>> +    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned
>>>>>> int)addr,
>>>>>> +            (unsigned int)val, size);
>>>>>
>>>>> There's PRIx64 to print uint64_t portably, then the casts can be
>>>>> removed.
>>>>>
>>>>
>>>> Fixed.
>>>>
>>>>>> +
>>>>>> +    if (size != 4) {
>>>>>> +        DPRINTF(" CC 32bits only\n");
>>>>>> +        return;
>>>>>> +    }
>>>>>> +
>>>>>> +    switch (addr) {
>>>>>> +        case 0x00:              /* Cache control */
>>>>>> +
>>>>>> +            /* These values must always be read as zeros */
>>>>>> +            val&= ~CACHE_CTRL_FD;
>>>>>> +            val&= ~CACHE_CTRL_FI;
>>>>>> +            val&= ~CACHE_CTRL_IB;
>>>>>> +            val&= ~CACHE_CTRL_IP;
>>>>>> +            val&= ~CACHE_CTRL_DP;
>>>>>> +
>>>>>> +            leon3_state.cache_control = val;
>>>>>> +            break;
>>>>>> +        case 0x04:              /* Instruction cache configuration */
>>>>>> +        case 0x08:              /* Data cache configuration */
>>>>>> +            /* Read Only */
>>>>>> +            break;
>>>>>> +        default:
>>>>>> +            DPRINTF(" CC write unknown register 0x%04x\n", (int)addr);
>>>>>> +            break;
>>>>>> +    };
>>>>>> +}
>>>>>> +
>>>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size)
>>>>>> +{
>>>>>> +    uint64_t ret = 0;
>>>>>> +
>>>>>> +    if (size != 4) {
>>>>>> +        DPRINTF(" CC 32bits only\n");
>>>>>> +        return 0;
>>>>>> +    }
>>>>>> +
>>>>>> +    switch (addr) {
>>>>>> +        case 0x00:              /* Cache control */
>>>>>> +            ret = leon3_state.cache_control;
>>>>>> +            break;
>>>>>> +        case 0x04:              /* Instruction cache configuration */
>>>>>> +            ret = leon3_state.inst_cache_conf;
>>>>>> +            break;
>>>>>> +        case 0x08:              /* Data cache configuration */
>>>>>> +            ret = leon3_state.data_cache_conf;
>>>>>> +            break;
>>>>>> +        default:
>>>>>> +            DPRINTF(" CC read unknown register 0x%04x\n", (int)addr);
>>>>>> +            break;
>>>>>> +    };
>>>>>> +    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned
>>>>>> int)addr,
>>>>>> +            size, (long unsigned int)ret );
>>>>>> +    return ret;
>>>>>> +}
>>>>>> +
>>>>>> +void leon3_shutdown(void)
>>>>>> +{
>>>>>> +    qemu_system_shutdown_request();
>>>>>> +}
>>>>>> +
>>>>>> +static void main_cpu_reset(void *opaque)
>>>>>> +{
>>>>>> +    CPUState *env = opaque;
>>>>>
>>>>> Here you can introduce a helper structure to pass PC and NPC, like
>>>>> sun4u.c ResetData. Then the global state should not be needed anymore.
>>>>>
>>>>
>>>> OK, I've used the sun4u.c reset scheme.
>>>>
>>>>>> +
>>>>>> +    cpu_reset(env);
>>>>>> +
>>>>>> +    env->halted = 0;
>>>>>> +    env->pc     = leon3_state.entry;
>>>>>> +    env->npc    = leon3_state.entry + 4;
>>>>>> +
>>>>>> +    /* Initialize cache control */
>>>>>> +    leon3_state.cache_control   = 0x0;
>>>>>> +
>>>>>> +    /* Configuration registers are read and only always keep those
>>>>>> predefined
>>>>>> +       values */
>>>>>> +    leon3_state.inst_cache_conf = 0x10220000;
>>>>>> +    leon3_state.data_cache_conf = 0x18220000;
>>>>>> +}
>>>>>> +
>>>>>> +static void leon3_generic_hw_init(ram_addr_t  ram_size,
>>>>>> +                                  const char *boot_device,
>>>>>> +                                  const char *kernel_filename,
>>>>>> +                                  const char *kernel_cmdline,
>>>>>> +                                  const char *initrd_filename,
>>>>>> +                                  const char *cpu_model)
>>>>>> +{
>>>>>> +    CPUState   *env;
>>>>>> +    ram_addr_t  ram_offset, prom_offset;
>>>>>> +    int         ret;
>>>>>> +    char       *filename;
>>>>>> +    qemu_irq   *cpu_irqs = NULL;
>>>>>> +    int         bios_size;
>>>>>> +    int         prom_size;
>>>>>> +    int         aligned_bios_size;
>>>>>> +
>>>>>> +    /* Init CPU */
>>>>>> +    if (!cpu_model)
>>>>>> +        cpu_model = "LEON3";
>>>>>
>>>>> Missing braces.
>>>>
>>>> Fixed.
>>>>
>>>>>
>>>>>> +
>>>>>> +    env = cpu_init(cpu_model);
>>>>>> +    if (!env) {
>>>>>> +        fprintf(stderr, "qemu: Unable to find Sparc CPU
>>>>>> definition\n");
>>>>>> +        exit(1);
>>>>>> +    }
>>>>>> +
>>>>>> +    cpu_sparc_set_id(env, 0);
>>>>>> +
>>>>>> +    qemu_register_reset(main_cpu_reset, env);
>>>>>> +
>>>>>> +    /* Allocate IRQ manager */
>>>>>> +    grlib_irqmp_create(0x80000200, env,&cpu_irqs, MAX_PILS);
>>>>>> +
>>>>>> +    /* Allocate RAM */
>>>>>> +    if ((uint64_t)ram_size>      (1UL<<      30)) {
>>>>>> +        fprintf(stderr,
>>>>>> +                "qemu: Too much memory for this machine: %d, maximum
>>>>>> 1G\n",
>>>>>> +                (unsigned int)(ram_size / (1024 * 1024)));
>>>>>> +        exit(1);
>>>>>> +    }
>>>>>> +
>>>>>> +    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
>>>>>> +    cpu_register_physical_memory(0x40000000, ram_size, ram_offset |
>>>>>> IO_MEM_RAM);
>>>>>> +
>>>>>> +    /* Allocate BIOS */
>>>>>> +    prom_size = 8 * 1024 * 1024; /* 8Mb */
>>>>>> +    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
>>>>>> +    cpu_register_physical_memory(0x00000000, prom_size,
>>>>>> +                                 prom_offset | IO_MEM_ROM);
>>>>>> +
>>>>>> +    /* Load boot prom */
>>>>>> +    if (bios_name == NULL)
>>>>>> +        bios_name = PROM_FILENAME;
>>>>>> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
>>>>>> +
>>>>>> +    bios_size = get_image_size(filename);
>>>>>> +
>>>>>> +    if (bios_size>      prom_size) {
>>>>>> +        fprintf(stderr, "qemu: could not load prom '%s': file too big
>>>>>> \n",
>>>>>> +                filename);
>>>>>> +        exit(1);
>>>>>> +    }
>>>>>> +
>>>>>> +    if (bios_size>      0) {
>>>>>> +        aligned_bios_size =
>>>>>> +            (bios_size + TARGET_PAGE_SIZE - 1)&      TARGET_PAGE_MASK;
>>>>>> +
>>>>>> +        ret = load_image_targphys(filename, 0x00000000, bios_size);
>>>>>> +        if (ret<      0 || ret>      prom_size) {
>>>>>> +            fprintf(stderr, "qemu: could not load prom '%s'\n",
>>>>>> filename);
>>>>>> +            exit(1);
>>>>>> +        }
>>>>>> +    }
>>>>>> +    else if (kernel_filename == NULL) {
>>>>>> +        fprintf(stderr,"Can't read bios image %s\n", filename);
>>>>>> +        exit(1);
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Can directly load an application. */
>>>>>> +    if (kernel_filename != NULL) {
>>>>>> +        long     kernel_size;
>>>>>> +        uint64_t entry;
>>>>>> +
>>>>>> +        kernel_size = load_elf(kernel_filename, NULL, NULL,&entry,
>>>>>> NULL,
>>>>>> NULL,
>>>>>> +                               1 /* big endian */, ELF_MACHINE, 0);
>>>>>> +        if (kernel_size<      0) {
>>>>>> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
>>>>>> +                    kernel_filename);
>>>>>> +            exit(1);
>>>>>> +        }
>>>>>> +        if (bios_size<= 0) {
>>>>>> +            /* If there is no bios/monitor, start the application.  */
>>>>>> +            env->pc = entry;
>>>>>> +            env->npc = entry + 4;
>>>>>> +            leon3_state.entry = entry;
>>>>>> +        }
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Allocate timers */
>>>>>> +    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
>>>>>> +
>>>>>> +    /* Allocate uart */
>>>>>> +    if (serial_hds[0])
>>>>>> +        grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
>>>>>> +}
>>>>>> +
>>>>>> +QEMUMachine leon3_generic_machine = {
>>>>>> +    .name     = "leon3_generic",
>>>>>> +    .desc     = "Leon-3 generic",
>>>>>> +    .init     = leon3_generic_hw_init,
>>>>>> +    .use_scsi = 0,
>>>>>> +};
>>>>>> +
>>>>>> +static void leon3_machine_init(void)
>>>>>> +{
>>>>>> +    qemu_register_machine(&leon3_generic_machine);
>>>>>> +}
>>>>>> +
>>>>>> +machine_init(leon3_machine_init);
>>>>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>>>>> index 7e0d17c..6020ffd 100644
>>>>>> --- a/target-sparc/cpu.h
>>>>>> +++ b/target-sparc/cpu.h
>>>>>> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
>>>>>>   /* sun4m.c, sun4u.c */
>>>>>>   void cpu_check_irqs(CPUSPARCState *env);
>>>>>>
>>>>>> +/* grlib_irqmp.c */
>>>>>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno);
>>>>>> +
>>>>>> +/* leon3.c */
>>>>>> +void     leon3_shutdown(void);
>>>>>> +void     leon3_cache_control_st(target_ulong addr, uint64_t val, int
>>>>>> size);
>>>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size);
>>>>>> +void     leon3_cache_control_int(void);
>>>>>> +
>>>>>> +
>>>>>>   #if defined (TARGET_SPARC64)
>>>>>>
>>>>>>   static inline int compare_masked(uint64_t x, uint64_t y, uint64_t
>>>>>> mask)
>>>>>> diff --git a/target-sparc/helper.c b/target-sparc/helper.c
>>>>>> index e84c312..3bf990f 100644
>>>>>> --- a/target-sparc/helper.c
>>>>>> +++ b/target-sparc/helper.c
>>>>>> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
>>>>>>          .iu_version = 0xf3000000,
>>>>>>          .fpu_version = 4<<      17, /* FPU version 4 (Meiko) */
>>>>>>          .mmu_version = 0xf3000000,
>>>>>> -        .mmu_bm = 0x00004000,
>>>>>> +        .mmu_bm = 0x00000000,
>>>>>>          .mmu_ctpr_mask = 0x007ffff0,
>>>>>>          .mmu_cxr_mask = 0x0000003f,
>>>>>>          .mmu_sfsr_mask = 0xffffffff,
>>>>>> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
>>>>>> index be3c1e0..85df077 100644
>>>>>> --- a/target-sparc/op_helper.c
>>>>>> +++ b/target-sparc/op_helper.c
>>>>>> @@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int
>>>>>> asi,
>>>>>> int size, int sign)
>>>>>>
>>>>>>      helper_check_align(addr, size - 1);
>>>>>>      switch (asi) {
>>>>>> -    case 2: /* SuperSparc MXCC registers */
>>>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>>>          switch (addr) {
>>>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>>>> +            ret = leon3_cache_control_ld(addr, size);
>>>>>> +            break;
>>>>>>          case 0x01c00a00: /* MXCC control register */
>>>>>>              if (size == 8)
>>>>>>                  ret = env->mxccregs[3];
>>>>>> @@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t
>>>>>> val, int asi, int size)
>>>>>>   {
>>>>>>      helper_check_align(addr, size - 1);
>>>>>>      switch(asi) {
>>>>>> -    case 2: /* SuperSparc MXCC registers */
>>>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>>>          switch (addr) {
>>>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>>>> +            leon3_cache_control_st(addr, val, size);
>>>>>> +            break;
>>>>>> +
>>>>>>          case 0x01c00000: /* MXCC stream data register 0 */
>>>>>>              if (size == 8)
>>>>>>                  env->mxccdata[0] = val;
>>>>>> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
>>>>>>   {
>>>>>>      int cwp, intno = env->exception_index;
>>>>>>
>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>> +    /* Leon3 shutdown */
>>>>>> +    if (intno == 0x80&&      env->version == 0xf3000000) {
>>>>>> +        leon3_shutdown();
>>>>>> +    }
>>>>>
>>>>> This looks like a hack. Should a trap instruction initiate a shutdown?
>>>>
>>>> Yes, on Leon3 "ta 0x0" initiates a shutdown.
>>>
>>> Then this should be handled during translation. A Leon3 specific CPU
>>> feature should be added and used much like CHECK_IU_FEATURE (in
>>> translate.c). Then execution speed would not be affected for non-Leon3
>>> CPUs.
>>>
>>
>> OK, but I don't see how to request a shutdown during translation.
>
> Just create a helper which calls shutdown, translator should insert a
> call to that.

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

>>>>>> +#endif
>>>>>> +
>>>>>>   #ifdef DEBUG_PCALL
>>>>>>      if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>>>>>          static int count;
>>>>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>>>>>      env->pc = env->tbr;
>>>>>>      env->npc = env->pc + 4;
>>>>>>      env->exception_index = -1;
>>>>>> +
>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>> +    /* IRQ acknowledgment for Leon3 */
>>>>>> +    if (env->version == 0xf3000000&&      (intno&      ~15) == TT_EXTINT)
>>>>>> {
>>>>>> +        grlib_irqmp_ack (env, intno);
>>>>>> +        leon3_cache_control_int();
>>>>>> +    }
>>>>>
>>>>> Like this. I don't think a CPU should immediately ack any incoming
>>>>> interrupts.
>>>>
>>>> Leon3 does...
>>>
>>> Strange. Then this should be handled at board level (leon3.c).
>>
>> Well, it's a CPU feature not a board feature.
>
> Maybe, but we don't want to clutter interrupt handling with this.

I don't see what you expect here... How can I get the acknowledgment 
information without changing the do_interrupt function?

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-15 17:47                     ` Fabien Chouteau
@ 2010-12-17 19:14                       ` Blue Swirl
  2010-12-20  6:46                         ` Edgar E. Iglesias
  2010-12-20  9:25                         ` Fabien Chouteau
  0 siblings, 2 replies; 51+ messages in thread
From: Blue Swirl @ 2010-12-17 19:14 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>
>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>
>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>  wrote:
>>>>>
>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>
>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>  wrote:
>>>>>>>
>>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>>>> ---
>>>>>>>  Makefile.target          |    5 +-
>>>>>>>  hw/leon3.c               |  310
>>>>>>> ++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>  target-sparc/cpu.h       |   10 ++
>>>>>>>  target-sparc/helper.c    |    2 +-
>>>>>>>  target-sparc/op_helper.c |   30 ++++-
>>>>>>>  5 files changed, 353 insertions(+), 4 deletions(-)
>>>>>>>
>>>>>>> diff --git a/Makefile.target b/Makefile.target
>>>>>>> index 2800f47..f40e04f 100644
>>>>>>> --- a/Makefile.target
>>>>>>> +++ b/Makefile.target
>>>>>>> @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o
>>>>>>>  else
>>>>>>>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>>>>>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>>>>>> -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>>>>>> +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
>>>>>>> +
>>>>>>> +# GRLIB
>>>>>>> +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
>>>>>>>  endif
>>>>>>>
>>>>>>>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>>>>>> diff --git a/hw/leon3.c b/hw/leon3.c
>>>>>>> new file mode 100644
>>>>>>> index 0000000..ba61081
>>>>>>> --- /dev/null
>>>>>>> +++ b/hw/leon3.c
>>>>>>> @@ -0,0 +1,310 @@
>>>>>>> +/*
>>>>>>> + * QEMU Leon3 System Emulator
>>>>>>> + *
>>>>>>> + * Copyright (c) 2010 AdaCore
>>>>>>> + *
>>>>>>> + * Permission is hereby granted, free of charge, to any person
>>>>>>> obtaining
>>>>>>> a copy
>>>>>>> + * of this software and associated documentation files (the
>>>>>>> "Software"),
>>>>>>> to deal
>>>>>>> + * in the Software without restriction, including without limitation
>>>>>>> the
>>>>>>> rights
>>>>>>> + * to use, copy, modify, merge, publish, distribute, sublicense,
>>>>>>> and/or
>>>>>>> sell
>>>>>>> + * copies of the Software, and to permit persons to whom the
>>>>>>> Software
>>>>>>> is
>>>>>>> + * furnished to do so, subject to the following conditions:
>>>>>>> + *
>>>>>>> + * The above copyright notice and this permission notice shall be
>>>>>>> included in
>>>>>>> + * all copies or substantial portions of the Software.
>>>>>>> + *
>>>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>>>> EXPRESS OR
>>>>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>>>>> MERCHANTABILITY,
>>>>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>>>>> SHALL
>>>>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
>>>>>>> OR
>>>>>>> OTHER
>>>>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>>>>> ARISING FROM,
>>>>>>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>>>>>> DEALINGS IN
>>>>>>> + * THE SOFTWARE.
>>>>>>> + */
>>>>>>> +#include "hw.h"
>>>>>>> +#include "qemu-timer.h"
>>>>>>> +#include "qemu-char.h"
>>>>>>> +#include "sysemu.h"
>>>>>>> +#include "boards.h"
>>>>>>> +#include "loader.h"
>>>>>>> +#include "elf.h"
>>>>>>> +
>>>>>>> +#include "grlib.h"
>>>>>>> +
>>>>>>> +/* #define DEBUG_LEON3 */
>>>>>>> +
>>>>>>> +#ifdef DEBUG_LEON3
>>>>>>> +#define DPRINTF(fmt, ...)                                       \
>>>>>>> +    do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0)
>>>>>>> +#else
>>>>>>> +#define DPRINTF(fmt, ...)
>>>>>>> +#endif
>>>>>>> +
>>>>>>> +/* Default system clock.  */
>>>>>>> +#define CPU_CLK (40 * 1000 * 1000)
>>>>>>> +
>>>>>>> +#define PROM_FILENAME        "u-boot.bin"
>>>>>>> +
>>>>>>> +#define MAX_PILS 16
>>>>>>> +
>>>>>>> +typedef struct Leon3State
>>>>>>> +{
>>>>>>> +    uint32_t cache_control;
>>>>>>> +    uint32_t inst_cache_conf;
>>>>>>> +    uint32_t data_cache_conf;
>>>>>>> +
>>>>>>> +    uint64_t entry;             /* save kernel entry in case of
>>>>>>> reset
>>>>>>> */
>>>>>>> +} Leon3State;
>>>>>>> +
>>>>>>> +Leon3State leon3_state;
>>>>>>
>>>>>> Again global state, please refactor. Perhaps most of the cache
>>>>>> handling code belong to target-sparc/op_helper.c and this structure to
>>>>>> CPUSPARCState.
>>>>>
>>>>> I will try to find a solution for that.
>>>>> Is it OK to add some Leon3 specific stuff in the CPUSPARCState?
>>>>
>>>> Yes, no problem. You can also drop the intermediate Leon3State
>>>> structure if there is no benefit.
>>>>
>>>>>>> +
>>>>>>> +/* Cache control: emulate the behavior of cache control registers
>>>>>>> but
>>>>>>> without
>>>>>>> +   any effect on the emulated CPU */
>>>>>>> +
>>>>>>> +#define CACHE_DISABLED 0x0
>>>>>>> +#define CACHE_FROZEN   0x1
>>>>>>> +#define CACHE_ENABLED  0x3
>>>>>>> +
>>>>>>> +/* Cache Control register fields */
>>>>>>> +
>>>>>>> +#define CACHE_CTRL_IF (1<<        4)  /* Instruction Cache Freeze on
>>>>>>> Interrupt */
>>>>>>> +#define CACHE_CTRL_DF (1<<        5)  /* Data Cache Freeze on
>>>>>>> Interrupt
>>>>>>> */
>>>>>>> +#define CACHE_CTRL_DP (1<<      14)  /* Data cache flush pending */
>>>>>>> +#define CACHE_CTRL_IP (1<<      15)  /* Instruction cache flush
>>>>>>> pending
>>>>>>> */
>>>>>>> +#define CACHE_CTRL_IB (1<<      16)  /* Instruction burst fetch */
>>>>>>> +#define CACHE_CTRL_FI (1<<      21)  /* Flush Instruction cache
>>>>>>> (Write
>>>>>>> only)
>>>>>>> */
>>>>>>> +#define CACHE_CTRL_FD (1<<      22)  /* Flush Data cache (Write
>>>>>>> only) */
>>>>>>> +#define CACHE_CTRL_DS (1<<      23)  /* Data cache snoop enable */
>>>>>>> +
>>>>>>> +void leon3_cache_control_int(void)
>>>>>>> +{
>>>>>>> +    uint32_t state = 0;
>>>>>>> +
>>>>>>> +    if (leon3_state.cache_control&      CACHE_CTRL_IF) {
>>>>>>> +        /* Instruction cache state */
>>>>>>> +        state = leon3_state.cache_control&      0x3;
>>>>>>
>>>>>> Please add a new define CACHE_CTRL_xxx to replace 0x3.
>>>>>>
>>>>>
>>>>> Done.
>>>>>
>>>>>>> +        if (state == CACHE_ENABLED) {
>>>>>>> +            state = CACHE_FROZEN;
>>>>>>> +            DPRINTF("Instruction cache: freeze\n");
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        leon3_state.cache_control&= ~0x3;
>>>>>>> +        leon3_state.cache_control |= state;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    if (leon3_state.cache_control&      CACHE_CTRL_DF) {
>>>>>>> +        /* Data cache state */
>>>>>>> +        state = (leon3_state.cache_control>>      2)&      0x3;
>>>>>>> +        if (state == CACHE_ENABLED) {
>>>>>>> +            state = CACHE_FROZEN;
>>>>>>> +            DPRINTF("Data cache: freeze\n");
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        leon3_state.cache_control&= ~(0x3<<      2);
>>>>>>> +        leon3_state.cache_control |= (state<<      2);
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>> +void leon3_cache_control_st(target_ulong addr, uint64_t val, int
>>>>>>> size)
>>>>>>> +{
>>>>>>> +    DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned
>>>>>>> int)addr,
>>>>>>> +            (unsigned int)val, size);
>>>>>>
>>>>>> There's PRIx64 to print uint64_t portably, then the casts can be
>>>>>> removed.
>>>>>>
>>>>>
>>>>> Fixed.
>>>>>
>>>>>>> +
>>>>>>> +    if (size != 4) {
>>>>>>> +        DPRINTF(" CC 32bits only\n");
>>>>>>> +        return;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    switch (addr) {
>>>>>>> +        case 0x00:              /* Cache control */
>>>>>>> +
>>>>>>> +            /* These values must always be read as zeros */
>>>>>>> +            val&= ~CACHE_CTRL_FD;
>>>>>>> +            val&= ~CACHE_CTRL_FI;
>>>>>>> +            val&= ~CACHE_CTRL_IB;
>>>>>>> +            val&= ~CACHE_CTRL_IP;
>>>>>>> +            val&= ~CACHE_CTRL_DP;
>>>>>>> +
>>>>>>> +            leon3_state.cache_control = val;
>>>>>>> +            break;
>>>>>>> +        case 0x04:              /* Instruction cache configuration
>>>>>>> */
>>>>>>> +        case 0x08:              /* Data cache configuration */
>>>>>>> +            /* Read Only */
>>>>>>> +            break;
>>>>>>> +        default:
>>>>>>> +            DPRINTF(" CC write unknown register 0x%04x\n",
>>>>>>> (int)addr);
>>>>>>> +            break;
>>>>>>> +    };
>>>>>>> +}
>>>>>>> +
>>>>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size)
>>>>>>> +{
>>>>>>> +    uint64_t ret = 0;
>>>>>>> +
>>>>>>> +    if (size != 4) {
>>>>>>> +        DPRINTF(" CC 32bits only\n");
>>>>>>> +        return 0;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    switch (addr) {
>>>>>>> +        case 0x00:              /* Cache control */
>>>>>>> +            ret = leon3_state.cache_control;
>>>>>>> +            break;
>>>>>>> +        case 0x04:              /* Instruction cache configuration
>>>>>>> */
>>>>>>> +            ret = leon3_state.inst_cache_conf;
>>>>>>> +            break;
>>>>>>> +        case 0x08:              /* Data cache configuration */
>>>>>>> +            ret = leon3_state.data_cache_conf;
>>>>>>> +            break;
>>>>>>> +        default:
>>>>>>> +            DPRINTF(" CC read unknown register 0x%04x\n",
>>>>>>> (int)addr);
>>>>>>> +            break;
>>>>>>> +    };
>>>>>>> +    DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned
>>>>>>> int)addr,
>>>>>>> +            size, (long unsigned int)ret );
>>>>>>> +    return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +void leon3_shutdown(void)
>>>>>>> +{
>>>>>>> +    qemu_system_shutdown_request();
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void main_cpu_reset(void *opaque)
>>>>>>> +{
>>>>>>> +    CPUState *env = opaque;
>>>>>>
>>>>>> Here you can introduce a helper structure to pass PC and NPC, like
>>>>>> sun4u.c ResetData. Then the global state should not be needed anymore.
>>>>>>
>>>>>
>>>>> OK, I've used the sun4u.c reset scheme.
>>>>>
>>>>>>> +
>>>>>>> +    cpu_reset(env);
>>>>>>> +
>>>>>>> +    env->halted = 0;
>>>>>>> +    env->pc     = leon3_state.entry;
>>>>>>> +    env->npc    = leon3_state.entry + 4;
>>>>>>> +
>>>>>>> +    /* Initialize cache control */
>>>>>>> +    leon3_state.cache_control   = 0x0;
>>>>>>> +
>>>>>>> +    /* Configuration registers are read and only always keep those
>>>>>>> predefined
>>>>>>> +       values */
>>>>>>> +    leon3_state.inst_cache_conf = 0x10220000;
>>>>>>> +    leon3_state.data_cache_conf = 0x18220000;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void leon3_generic_hw_init(ram_addr_t  ram_size,
>>>>>>> +                                  const char *boot_device,
>>>>>>> +                                  const char *kernel_filename,
>>>>>>> +                                  const char *kernel_cmdline,
>>>>>>> +                                  const char *initrd_filename,
>>>>>>> +                                  const char *cpu_model)
>>>>>>> +{
>>>>>>> +    CPUState   *env;
>>>>>>> +    ram_addr_t  ram_offset, prom_offset;
>>>>>>> +    int         ret;
>>>>>>> +    char       *filename;
>>>>>>> +    qemu_irq   *cpu_irqs = NULL;
>>>>>>> +    int         bios_size;
>>>>>>> +    int         prom_size;
>>>>>>> +    int         aligned_bios_size;
>>>>>>> +
>>>>>>> +    /* Init CPU */
>>>>>>> +    if (!cpu_model)
>>>>>>> +        cpu_model = "LEON3";
>>>>>>
>>>>>> Missing braces.
>>>>>
>>>>> Fixed.
>>>>>
>>>>>>
>>>>>>> +
>>>>>>> +    env = cpu_init(cpu_model);
>>>>>>> +    if (!env) {
>>>>>>> +        fprintf(stderr, "qemu: Unable to find Sparc CPU
>>>>>>> definition\n");
>>>>>>> +        exit(1);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    cpu_sparc_set_id(env, 0);
>>>>>>> +
>>>>>>> +    qemu_register_reset(main_cpu_reset, env);
>>>>>>> +
>>>>>>> +    /* Allocate IRQ manager */
>>>>>>> +    grlib_irqmp_create(0x80000200, env,&cpu_irqs, MAX_PILS);
>>>>>>> +
>>>>>>> +    /* Allocate RAM */
>>>>>>> +    if ((uint64_t)ram_size>      (1UL<<      30)) {
>>>>>>> +        fprintf(stderr,
>>>>>>> +                "qemu: Too much memory for this machine: %d, maximum
>>>>>>> 1G\n",
>>>>>>> +                (unsigned int)(ram_size / (1024 * 1024)));
>>>>>>> +        exit(1);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
>>>>>>> +    cpu_register_physical_memory(0x40000000, ram_size, ram_offset |
>>>>>>> IO_MEM_RAM);
>>>>>>> +
>>>>>>> +    /* Allocate BIOS */
>>>>>>> +    prom_size = 8 * 1024 * 1024; /* 8Mb */
>>>>>>> +    prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
>>>>>>> +    cpu_register_physical_memory(0x00000000, prom_size,
>>>>>>> +                                 prom_offset | IO_MEM_ROM);
>>>>>>> +
>>>>>>> +    /* Load boot prom */
>>>>>>> +    if (bios_name == NULL)
>>>>>>> +        bios_name = PROM_FILENAME;
>>>>>>> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
>>>>>>> +
>>>>>>> +    bios_size = get_image_size(filename);
>>>>>>> +
>>>>>>> +    if (bios_size>      prom_size) {
>>>>>>> +        fprintf(stderr, "qemu: could not load prom '%s': file too
>>>>>>> big
>>>>>>> \n",
>>>>>>> +                filename);
>>>>>>> +        exit(1);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    if (bios_size>      0) {
>>>>>>> +        aligned_bios_size =
>>>>>>> +            (bios_size + TARGET_PAGE_SIZE - 1)&
>>>>>>>  TARGET_PAGE_MASK;
>>>>>>> +
>>>>>>> +        ret = load_image_targphys(filename, 0x00000000, bios_size);
>>>>>>> +        if (ret<      0 || ret>      prom_size) {
>>>>>>> +            fprintf(stderr, "qemu: could not load prom '%s'\n",
>>>>>>> filename);
>>>>>>> +            exit(1);
>>>>>>> +        }
>>>>>>> +    }
>>>>>>> +    else if (kernel_filename == NULL) {
>>>>>>> +        fprintf(stderr,"Can't read bios image %s\n", filename);
>>>>>>> +        exit(1);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    /* Can directly load an application. */
>>>>>>> +    if (kernel_filename != NULL) {
>>>>>>> +        long     kernel_size;
>>>>>>> +        uint64_t entry;
>>>>>>> +
>>>>>>> +        kernel_size = load_elf(kernel_filename, NULL, NULL,&entry,
>>>>>>> NULL,
>>>>>>> NULL,
>>>>>>> +                               1 /* big endian */, ELF_MACHINE, 0);
>>>>>>> +        if (kernel_size<      0) {
>>>>>>> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
>>>>>>> +                    kernel_filename);
>>>>>>> +            exit(1);
>>>>>>> +        }
>>>>>>> +        if (bios_size<= 0) {
>>>>>>> +            /* If there is no bios/monitor, start the application.
>>>>>>>  */
>>>>>>> +            env->pc = entry;
>>>>>>> +            env->npc = entry + 4;
>>>>>>> +            leon3_state.entry = entry;
>>>>>>> +        }
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    /* Allocate timers */
>>>>>>> +    grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
>>>>>>> +
>>>>>>> +    /* Allocate uart */
>>>>>>> +    if (serial_hds[0])
>>>>>>> +        grlib_apbuart_create(0x80000100, serial_hds[0],
>>>>>>> cpu_irqs[3]);
>>>>>>> +}
>>>>>>> +
>>>>>>> +QEMUMachine leon3_generic_machine = {
>>>>>>> +    .name     = "leon3_generic",
>>>>>>> +    .desc     = "Leon-3 generic",
>>>>>>> +    .init     = leon3_generic_hw_init,
>>>>>>> +    .use_scsi = 0,
>>>>>>> +};
>>>>>>> +
>>>>>>> +static void leon3_machine_init(void)
>>>>>>> +{
>>>>>>> +    qemu_register_machine(&leon3_generic_machine);
>>>>>>> +}
>>>>>>> +
>>>>>>> +machine_init(leon3_machine_init);
>>>>>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>>>>>> index 7e0d17c..6020ffd 100644
>>>>>>> --- a/target-sparc/cpu.h
>>>>>>> +++ b/target-sparc/cpu.h
>>>>>>> @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp);
>>>>>>>  /* sun4m.c, sun4u.c */
>>>>>>>  void cpu_check_irqs(CPUSPARCState *env);
>>>>>>>
>>>>>>> +/* grlib_irqmp.c */
>>>>>>> +void grlib_irqmp_ack(CPUSPARCState *env, int intno);
>>>>>>> +
>>>>>>> +/* leon3.c */
>>>>>>> +void     leon3_shutdown(void);
>>>>>>> +void     leon3_cache_control_st(target_ulong addr, uint64_t val, int
>>>>>>> size);
>>>>>>> +uint64_t leon3_cache_control_ld(target_ulong addr, int size);
>>>>>>> +void     leon3_cache_control_int(void);
>>>>>>> +
>>>>>>> +
>>>>>>>  #if defined (TARGET_SPARC64)
>>>>>>>
>>>>>>>  static inline int compare_masked(uint64_t x, uint64_t y, uint64_t
>>>>>>> mask)
>>>>>>> diff --git a/target-sparc/helper.c b/target-sparc/helper.c
>>>>>>> index e84c312..3bf990f 100644
>>>>>>> --- a/target-sparc/helper.c
>>>>>>> +++ b/target-sparc/helper.c
>>>>>>> @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = {
>>>>>>>         .iu_version = 0xf3000000,
>>>>>>>         .fpu_version = 4<<      17, /* FPU version 4 (Meiko) */
>>>>>>>         .mmu_version = 0xf3000000,
>>>>>>> -        .mmu_bm = 0x00004000,
>>>>>>> +        .mmu_bm = 0x00000000,
>>>>>>>         .mmu_ctpr_mask = 0x007ffff0,
>>>>>>>         .mmu_cxr_mask = 0x0000003f,
>>>>>>>         .mmu_sfsr_mask = 0xffffffff,
>>>>>>> diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
>>>>>>> index be3c1e0..85df077 100644
>>>>>>> --- a/target-sparc/op_helper.c
>>>>>>> +++ b/target-sparc/op_helper.c
>>>>>>> @@ -1609,8 +1609,13 @@ uint64_t helper_ld_asi(target_ulong addr, int
>>>>>>> asi,
>>>>>>> int size, int sign)
>>>>>>>
>>>>>>>     helper_check_align(addr, size - 1);
>>>>>>>     switch (asi) {
>>>>>>> -    case 2: /* SuperSparc MXCC registers */
>>>>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>>>>         switch (addr) {
>>>>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>>>>> +            ret = leon3_cache_control_ld(addr, size);
>>>>>>> +            break;
>>>>>>>         case 0x01c00a00: /* MXCC control register */
>>>>>>>             if (size == 8)
>>>>>>>                 ret = env->mxccregs[3];
>>>>>>> @@ -1838,8 +1843,14 @@ void helper_st_asi(target_ulong addr, uint64_t
>>>>>>> val, int asi, int size)
>>>>>>>  {
>>>>>>>     helper_check_align(addr, size - 1);
>>>>>>>     switch(asi) {
>>>>>>> -    case 2: /* SuperSparc MXCC registers */
>>>>>>> +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */
>>>>>>>         switch (addr) {
>>>>>>> +        case 0x00:          /* Leon3 Cache Control */
>>>>>>> +        case 0x08:          /* Leon3 Instruction Cache config */
>>>>>>> +        case 0x0C:          /* Leon3 Date Cache config */
>>>>>>> +            leon3_cache_control_st(addr, val, size);
>>>>>>> +            break;
>>>>>>> +
>>>>>>>         case 0x01c00000: /* MXCC stream data register 0 */
>>>>>>>             if (size == 8)
>>>>>>>                 env->mxccdata[0] = val;
>>>>>>> @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env)
>>>>>>>  {
>>>>>>>     int cwp, intno = env->exception_index;
>>>>>>>
>>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>>> +    /* Leon3 shutdown */
>>>>>>> +    if (intno == 0x80&&      env->version == 0xf3000000) {
>>>>>>> +        leon3_shutdown();
>>>>>>> +    }
>>>>>>
>>>>>> This looks like a hack. Should a trap instruction initiate a shutdown?
>>>>>
>>>>> Yes, on Leon3 "ta 0x0" initiates a shutdown.
>>>>
>>>> Then this should be handled during translation. A Leon3 specific CPU
>>>> feature should be added and used much like CHECK_IU_FEATURE (in
>>>> translate.c). Then execution speed would not be affected for non-Leon3
>>>> CPUs.
>>>>
>>>
>>> OK, but I don't see how to request a shutdown during translation.
>>
>> Just create a helper which calls shutdown, translator should insert a
>> call to that.
>
> I think I understand what you mean, but I don't see why this would be faster
> than my solution.

Shutdown is not performance critical, but interrupt handling is.

>>>>>>> +#endif
>>>>>>> +
>>>>>>>  #ifdef DEBUG_PCALL
>>>>>>>     if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>>>>>>         static int count;
>>>>>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>>>>>>     env->pc = env->tbr;
>>>>>>>     env->npc = env->pc + 4;
>>>>>>>     env->exception_index = -1;
>>>>>>> +
>>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>>> +    /* IRQ acknowledgment for Leon3 */
>>>>>>> +    if (env->version == 0xf3000000&&      (intno&      ~15) ==
>>>>>>> TT_EXTINT)
>>>>>>> {
>>>>>>> +        grlib_irqmp_ack (env, intno);
>>>>>>> +        leon3_cache_control_int();
>>>>>>> +    }
>>>>>>
>>>>>> Like this. I don't think a CPU should immediately ack any incoming
>>>>>> interrupts.
>>>>>
>>>>> Leon3 does...
>>>>
>>>> Strange. Then this should be handled at board level (leon3.c).
>>>
>>> Well, it's a CPU feature not a board feature.
>>
>> Maybe, but we don't want to clutter interrupt handling with this.
>
> I don't see what you expect here... How can I get the acknowledgment
> information without changing the do_interrupt function?

Board can acknowledge the interrupt just before calling cpu_interrupt().

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-17 19:14                       ` Blue Swirl
@ 2010-12-20  6:46                         ` Edgar E. Iglesias
  2010-12-20  9:40                           ` Fabien Chouteau
  2010-12-20  9:25                         ` Fabien Chouteau
  1 sibling, 1 reply; 51+ messages in thread
From: Edgar E. Iglesias @ 2010-12-20  6:46 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel, Fabien Chouteau

On Fri, Dec 17, 2010 at 07:14:20PM +0000, Blue Swirl wrote:
> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau <chouteau@adacore.com> wrote:
> > On 12/13/2010 07:18 PM, Blue Swirl wrote:
> >>
> >> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
> >>  wrote:
> >>>
> >>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
> >>>>
> >>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
> >>>>  wrote:
> >>>>>
> >>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
> >>>>>>
> >>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
> >>>>>>  wrote:
> >>>>>>>
> >>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
> >>>>>>> ---

...

> >>>>>>>  #ifdef DEBUG_PCALL
> >>>>>>>     if (qemu_loglevel_mask(CPU_LOG_INT)) {
> >>>>>>>         static int count;
> >>>>>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
> >>>>>>>     env->pc = env->tbr;
> >>>>>>>     env->npc = env->pc + 4;
> >>>>>>>     env->exception_index = -1;
> >>>>>>> +
> >>>>>>> +#if !defined(CONFIG_USER_ONLY)
> >>>>>>> +    /* IRQ acknowledgment for Leon3 */
> >>>>>>> +    if (env->version == 0xf3000000&&      (intno&      ~15) ==
> >>>>>>> TT_EXTINT)
> >>>>>>> {
> >>>>>>> +        grlib_irqmp_ack (env, intno);
> >>>>>>> +        leon3_cache_control_int();
> >>>>>>> +    }
> >>>>>>
> >>>>>> Like this. I don't think a CPU should immediately ack any incoming
> >>>>>> interrupts.
> >>>>>
> >>>>> Leon3 does...
> >>>>
> >>>> Strange. Then this should be handled at board level (leon3.c).
> >>>
> >>> Well, it's a CPU feature not a board feature.
> >>
> >> Maybe, but we don't want to clutter interrupt handling with this.
> >
> > I don't see what you expect here... How can I get the acknowledgment
> > information without changing the do_interrupt function?
> 
> Board can acknowledge the interrupt just before calling cpu_interrupt().

Hi,

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

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

Cheers

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-17 19:14                       ` Blue Swirl
  2010-12-20  6:46                         ` Edgar E. Iglesias
@ 2010-12-20  9:25                         ` Fabien Chouteau
  2010-12-20 19:27                           ` Blue Swirl
  1 sibling, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-20  9:25 UTC (permalink / raw)
  To: Blue Swirl; +Cc: qemu-devel

On 12/17/2010 08:14 PM, Blue Swirl wrote:
> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>
>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>   wrote:
>>>>
>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>
>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>   wrote:
>>>>>>
>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>
>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>>   wrote:
>>>>>>>
>>>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>>>> +    /* Leon3 shutdown */
>>>>>>>> +    if (intno == 0x80&&        env->version == 0xf3000000) {
>>>>>>>> +        leon3_shutdown();
>>>>>>>> +    }
>>>>>>>
>>>>>>> This looks like a hack. Should a trap instruction initiate a shutdown?
>>>>>>
>>>>>> Yes, on Leon3 "ta 0x0" initiates a shutdown.
>>>>>
>>>>> Then this should be handled during translation. A Leon3 specific CPU
>>>>> feature should be added and used much like CHECK_IU_FEATURE (in
>>>>> translate.c). Then execution speed would not be affected for non-Leon3
>>>>> CPUs.
>>>>>
>>>>
>>>> OK, but I don't see how to request a shutdown during translation.
>>>
>>> Just create a helper which calls shutdown, translator should insert a
>>> call to that.
>>
>> I think I understand what you mean, but I don't see why this would be faster
>> than my solution.
>
> Shutdown is not performance critical, but interrupt handling is.
>

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

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

BTW, I didn't use the "unlikely" function, but this will even more 
reduce the impact...

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-20  6:46                         ` Edgar E. Iglesias
@ 2010-12-20  9:40                           ` Fabien Chouteau
  2010-12-20 20:09                             ` Blue Swirl
  0 siblings, 1 reply; 51+ messages in thread
From: Fabien Chouteau @ 2010-12-20  9:40 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: Blue Swirl, qemu-devel

On 12/20/2010 07:46 AM, Edgar E. Iglesias wrote:
> On Fri, Dec 17, 2010 at 07:14:20PM +0000, Blue Swirl wrote:
>> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>  wrote:
>>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>>
>>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>>   wrote:
>>>>>
>>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>>
>>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>   wrote:
>>>>>>>
>>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>>
>>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>>>   wrote:
>>>>>>>>>
>>>>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>>>>>> ---
>
> ...
>
>>>>>>>>>   #ifdef DEBUG_PCALL
>>>>>>>>>      if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>>>>>>>>          static int count;
>>>>>>>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>>>>>>>>      env->pc = env->tbr;
>>>>>>>>>      env->npc = env->pc + 4;
>>>>>>>>>      env->exception_index = -1;
>>>>>>>>> +
>>>>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>>>>> +    /* IRQ acknowledgment for Leon3 */
>>>>>>>>> +    if (env->version == 0xf3000000&&        (intno&        ~15) ==
>>>>>>>>> TT_EXTINT)
>>>>>>>>> {
>>>>>>>>> +        grlib_irqmp_ack (env, intno);
>>>>>>>>> +        leon3_cache_control_int();
>>>>>>>>> +    }
>>>>>>>>
>>>>>>>> Like this. I don't think a CPU should immediately ack any incoming
>>>>>>>> interrupts.
>>>>>>>
>>>>>>> Leon3 does...
>>>>>>
>>>>>> Strange. Then this should be handled at board level (leon3.c).
>>>>>
>>>>> Well, it's a CPU feature not a board feature.
>>>>
>>>> Maybe, but we don't want to clutter interrupt handling with this.
>>>
>>> I don't see what you expect here... How can I get the acknowledgment
>>> information without changing the do_interrupt function?
>>
>> Board can acknowledge the interrupt just before calling cpu_interrupt().
>
> Hi,
>
> I don't think that will work properly. IIUC, the leon acks the irq when it
> is actually taken by the CPU. Due to CPU internal masking, that may be at
> a later point than when the irq is signalled to the CPU.

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

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

The function pointer is a good idea, something like:

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

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

Are you OK with that?

-- 
Fabien Chouteau

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-20  9:25                         ` Fabien Chouteau
@ 2010-12-20 19:27                           ` Blue Swirl
  0 siblings, 0 replies; 51+ messages in thread
From: Blue Swirl @ 2010-12-20 19:27 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: qemu-devel

On Mon, Dec 20, 2010 at 9:25 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/17/2010 08:14 PM, Blue Swirl wrote:
>>
>> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>
>>  wrote:
>>>
>>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>>
>>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>>  wrote:
>>>>>
>>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>>
>>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien Chouteau<chouteau@adacore.com>
>>>>>>  wrote:
>>>>>>>
>>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>>
>>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien
>>>>>>>> Chouteau<chouteau@adacore.com>
>>>>>>>>  wrote:
>>>>>>>>
>>>>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>>>>> +    /* Leon3 shutdown */
>>>>>>>>> +    if (intno == 0x80&&        env->version == 0xf3000000) {
>>>>>>>>> +        leon3_shutdown();
>>>>>>>>> +    }
>>>>>>>>
>>>>>>>> This looks like a hack. Should a trap instruction initiate a
>>>>>>>> shutdown?
>>>>>>>
>>>>>>> Yes, on Leon3 "ta 0x0" initiates a shutdown.
>>>>>>
>>>>>> Then this should be handled during translation. A Leon3 specific CPU
>>>>>> feature should be added and used much like CHECK_IU_FEATURE (in
>>>>>> translate.c). Then execution speed would not be affected for non-Leon3
>>>>>> CPUs.
>>>>>>
>>>>>
>>>>> OK, but I don't see how to request a shutdown during translation.
>>>>
>>>> Just create a helper which calls shutdown, translator should insert a
>>>> call to that.
>>>
>>> I think I understand what you mean, but I don't see why this would be
>>> faster
>>> than my solution.
>>
>> Shutdown is not performance critical, but interrupt handling is.
>>
>
> I understand that, but why is it faster to do it during translation?
>
> I don't see how one "if" statement in "do_interrupt" will even slightly
> impact
> the performances, and why it will be slower than the same "if' statement in
> the translation.

Because the whole point of translation is that the translated block
will be executed many times but the translation only once. Therefore
computing should be performed during translation instead of TB
execution time, if possible.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3.
  2010-12-20  9:40                           ` Fabien Chouteau
@ 2010-12-20 20:09                             ` Blue Swirl
  0 siblings, 0 replies; 51+ messages in thread
From: Blue Swirl @ 2010-12-20 20:09 UTC (permalink / raw)
  To: Fabien Chouteau; +Cc: Edgar E. Iglesias, qemu-devel

On Mon, Dec 20, 2010 at 9:40 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 12/20/2010 07:46 AM, Edgar E. Iglesias wrote:
>>
>> On Fri, Dec 17, 2010 at 07:14:20PM +0000, Blue Swirl wrote:
>>>
>>> On Wed, Dec 15, 2010 at 5:47 PM, Fabien Chouteau<chouteau@adacore.com>
>>>  wrote:
>>>>
>>>> On 12/13/2010 07:18 PM, Blue Swirl wrote:
>>>>>
>>>>> On Mon, Dec 13, 2010 at 3:51 PM, Fabien Chouteau<chouteau@adacore.com>
>>>>>  wrote:
>>>>>>
>>>>>> On 12/11/2010 10:56 AM, Blue Swirl wrote:
>>>>>>>
>>>>>>> On Tue, Dec 7, 2010 at 11:40 AM, Fabien
>>>>>>> Chouteau<chouteau@adacore.com>
>>>>>>>  wrote:
>>>>>>>>
>>>>>>>> On 12/06/2010 06:53 PM, Blue Swirl wrote:
>>>>>>>>>
>>>>>>>>> On Mon, Dec 6, 2010 at 9:26 AM, Fabien
>>>>>>>>> Chouteau<chouteau@adacore.com>
>>>>>>>>>  wrote:
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>>>>>>> ---
>>
>> ...
>>
>>>>>>>>>>  #ifdef DEBUG_PCALL
>>>>>>>>>>     if (qemu_loglevel_mask(CPU_LOG_INT)) {
>>>>>>>>>>         static int count;
>>>>>>>>>> @@ -4135,6 +4153,14 @@ void do_interrupt(CPUState *env)
>>>>>>>>>>     env->pc = env->tbr;
>>>>>>>>>>     env->npc = env->pc + 4;
>>>>>>>>>>     env->exception_index = -1;
>>>>>>>>>> +
>>>>>>>>>> +#if !defined(CONFIG_USER_ONLY)
>>>>>>>>>> +    /* IRQ acknowledgment for Leon3 */
>>>>>>>>>> +    if (env->version == 0xf3000000&&        (intno&        ~15)
>>>>>>>>>> ==
>>>>>>>>>> TT_EXTINT)
>>>>>>>>>> {
>>>>>>>>>> +        grlib_irqmp_ack (env, intno);
>>>>>>>>>> +        leon3_cache_control_int();
>>>>>>>>>> +    }
>>>>>>>>>
>>>>>>>>> Like this. I don't think a CPU should immediately ack any incoming
>>>>>>>>> interrupts.
>>>>>>>>
>>>>>>>> Leon3 does...
>>>>>>>
>>>>>>> Strange. Then this should be handled at board level (leon3.c).
>>>>>>
>>>>>> Well, it's a CPU feature not a board feature.
>>>>>
>>>>> Maybe, but we don't want to clutter interrupt handling with this.
>>>>
>>>> I don't see what you expect here... How can I get the acknowledgment
>>>> information without changing the do_interrupt function?
>>>
>>> Board can acknowledge the interrupt just before calling cpu_interrupt().
>>
>> Hi,
>>
>> I don't think that will work properly. IIUC, the leon acks the irq when it
>> is actually taken by the CPU. Due to CPU internal masking, that may be at
>> a later point than when the irq is signalled to the CPU.
>
> Exactly I've forget to mention that. Raising the interrupt do not mean that
> the
> CPU will handle it directly, for example if traps are disabled or if the CPU
> handles an higher priority interrupt at the moment.

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

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

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

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

^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2010-12-20 20:09 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-06  9:26 [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Fabien Chouteau
2010-12-06  9:26 ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual Fabien Chouteau
2010-12-06  9:26   ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Fabien Chouteau
2010-12-06  9:26     ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART " Fabien Chouteau
2010-12-06  9:26       ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Fabien Chouteau
2010-12-06  9:26         ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Fabien Chouteau
2010-12-06  9:26           ` [Qemu-devel] [PATCH 6/6] [RFC] SPARCV8 asr17 register support Fabien Chouteau
2010-12-06 18:01             ` Blue Swirl
2010-12-07 11:51               ` Fabien Chouteau
2010-12-11  9:59                 ` Blue Swirl
2010-12-13 17:01                   ` Fabien Chouteau
2010-12-06 17:53           ` [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3 Blue Swirl
2010-12-07 11:40             ` Fabien Chouteau
2010-12-11  9:56               ` Blue Swirl
2010-12-13 15:51                 ` Fabien Chouteau
2010-12-13 18:18                   ` Blue Swirl
2010-12-15 17:47                     ` Fabien Chouteau
2010-12-17 19:14                       ` Blue Swirl
2010-12-20  6:46                         ` Edgar E. Iglesias
2010-12-20  9:40                           ` Fabien Chouteau
2010-12-20 20:09                             ` Blue Swirl
2010-12-20  9:25                         ` Fabien Chouteau
2010-12-20 19:27                           ` Blue Swirl
2010-12-12 14:41           ` Andreas Färber
2010-12-13 17:00             ` Fabien Chouteau
2010-12-06 17:31         ` [Qemu-devel] [PATCH 4/6] [RFC] Header file for the GRLIB components Blue Swirl
2010-12-07 11:04           ` Fabien Chouteau
2010-12-06 17:29       ` [Qemu-devel] [PATCH 3/6] [RFC] Emulation of GRLIB APB UART as defined in GRLIB IP Core User's Manual Blue Swirl
2010-12-07 10:55         ` Fabien Chouteau
2010-12-06 17:25     ` [Qemu-devel] [PATCH 2/6] [RFC] Emulation of GRLIB IRQMP " Blue Swirl
2010-12-07 10:43       ` Fabien Chouteau
2010-12-11 10:31         ` Blue Swirl
2010-12-13 16:23           ` Fabien Chouteau
2010-12-13 18:13             ` Blue Swirl
2010-12-09 10:32     ` Edgar E. Iglesias
2010-12-09 11:03       ` Fabien Chouteau
2010-12-09 11:06         ` Edgar E. Iglesias
2010-12-09 11:32           ` Fabien Chouteau
2010-12-06 17:12   ` [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer " Blue Swirl
2010-12-07  9:55     ` Fabien Chouteau
2010-12-08  8:30       ` Edgar E. Iglesias
2010-12-08  9:39         ` Fabien Chouteau
2010-12-08 21:02           ` Edgar E. Iglesias
2010-12-08 22:51   ` Edgar E. Iglesias
2010-12-09 10:04     ` Fabien Chouteau
2010-12-09 10:22       ` Edgar E. Iglesias
2010-12-06 10:44 ` [Qemu-devel] [PATCH 0/6] [RFC] New SPARC machine: Leon3 Artyom Tarasenko
2010-12-06 15:07   ` Fabien Chouteau
2010-12-06 18:12     ` Blue Swirl
2010-12-07 17:43       ` Fabien Chouteau
2010-12-06 18:05 ` Blue Swirl

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.