qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 12/14] arc: Add Synopsys ARC emulation boards


From: cupertinomiranda
Subject: [PATCH 12/14] arc: Add Synopsys ARC emulation boards
Date: Wed, 30 Sep 2020 21:46:02 +0100

From: Claudiu Zissulescu <claziss@synopsys.com>

Add the Synopsys ARC boards, arc_sim for testing, sim-hs main emulation
board using standard UART and nsim which includes a Synopsys ARC specific
UART implementation.

Signed-off-by: Claudiu Zissulescu <claziss@synopsys.com>
---
 hw/arc/Makefile.objs      |  21 +++
 hw/arc/arc_sim.c          | 143 ++++++++++++++++++++
 hw/arc/arc_uart.c         | 267 ++++++++++++++++++++++++++++++++++++++
 hw/arc/board-hsdk.c       | 107 +++++++++++++++
 hw/arc/boot.c             |  95 ++++++++++++++
 hw/arc/boot.h             |  21 +++
 hw/arc/meson.build        |  13 ++
 hw/arc/nsim.c             |  86 ++++++++++++
 hw/arc/pic_cpu.c          | 111 ++++++++++++++++
 hw/arc/sample.c           |  77 +++++++++++
 hw/arc/sim-hs.c           | 107 +++++++++++++++
 include/hw/arc/arc_uart.h |  43 ++++++
 include/hw/arc/cpudevs.h  |  10 ++
 13 files changed, 1101 insertions(+)
 create mode 100644 hw/arc/Makefile.objs
 create mode 100644 hw/arc/arc_sim.c
 create mode 100644 hw/arc/arc_uart.c
 create mode 100644 hw/arc/board-hsdk.c
 create mode 100644 hw/arc/boot.c
 create mode 100644 hw/arc/boot.h
 create mode 100644 hw/arc/meson.build
 create mode 100644 hw/arc/nsim.c
 create mode 100644 hw/arc/pic_cpu.c
 create mode 100644 hw/arc/sample.c
 create mode 100644 hw/arc/sim-hs.c
 create mode 100644 include/hw/arc/arc_uart.h
 create mode 100644 include/hw/arc/cpudevs.h

diff --git a/hw/arc/Makefile.objs b/hw/arc/Makefile.objs
new file mode 100644
index 0000000000..28d7766cd9
--- /dev/null
+++ b/hw/arc/Makefile.objs
@@ -0,0 +1,21 @@
+#
+#  QEMU ARC CPU
+#
+#  Copyright (c) 2019
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, see
+#  http://www.gnu.org/licenses/lgpl-2.1.html
+#
+
+obj-y   = arc_sim.o arc_uart.o sample.o pic_cpu.o boot.o board-hsdk.o sim-hs.o 
nsim.o
diff --git a/hw/arc/arc_sim.c b/hw/arc/arc_sim.c
new file mode 100644
index 0000000000..8020a03d85
--- /dev/null
+++ b/hw/arc/arc_sim.c
@@ -0,0 +1,143 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "elf.h"
+#include "hw/char/serial.h"
+#include "net/net.h"
+#include "hw/loader.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "sysemu/reset.h"
+#include "sysemu/runstate.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/arc/cpudevs.h"
+#include "boot.h"
+
+static void arc_sim_net_init(MemoryRegion *address_space,
+                             hwaddr base,
+                             hwaddr descriptors,
+                             qemu_irq irq, NICInfo *nd)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_new("open_eth");
+    qdev_set_nic_properties(dev, nd);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_connect_irq(s, 0, irq);
+    memory_region_add_subregion(address_space, base,
+                                sysbus_mmio_get_region(s, 0));
+    memory_region_add_subregion(address_space, descriptors,
+                                sysbus_mmio_get_region(s, 1));
+}
+
+static uint64_t arc_io_read(void *opaque, hwaddr addr, unsigned size)
+{
+    return 0;
+}
+
+static void arc_io_write(void *opaque, hwaddr addr,
+                         uint64_t val, unsigned size)
+{
+    switch (addr) {
+    case 0x08: /* board reset. */
+        qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps arc_io_ops = {
+    .read = arc_io_read,
+    .write = arc_io_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void arc_sim_init(MachineState *machine)
+{
+    static struct arc_boot_info boot_info;
+    unsigned int smp_cpus = machine->smp.cpus;
+    ram_addr_t ram_base = 0;
+    ram_addr_t ram_size = machine->ram_size;
+    ARCCPU *cpu = NULL;
+    MemoryRegion *ram, *system_io;
+    int n;
+
+    boot_info.ram_start = ram_base;
+    boot_info.ram_size = ram_size;
+    boot_info.kernel_filename = machine->kernel_filename;
+
+    for (n = 0; n < smp_cpus; n++) {
+        cpu = ARC_CPU(object_new(machine->cpu_type));
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find CPU definition!\n");
+            exit(1);
+        }
+
+        /* Set the initial CPU properties. */
+        object_property_set_uint(OBJECT(cpu), "freq_hz", 1000000, 
&error_fatal);
+        object_property_set_bool(OBJECT(cpu), "rtc-opt", true, &error_fatal);
+        object_property_set_bool(OBJECT(cpu), "realized", true, &error_fatal);
+
+        /* Initialize internal devices. */
+        cpu_arc_pic_init(cpu);
+        cpu_arc_clock_init(cpu);
+
+        qemu_register_reset(arc_cpu_reset, cpu);
+    }
+
+    ram = g_new(MemoryRegion, 1);
+    memory_region_init_ram(ram, NULL, "arc.ram", ram_size, &error_fatal);
+    memory_region_add_subregion(get_system_memory(), ram_base, ram);
+
+    system_io = g_new(MemoryRegion, 1);
+    memory_region_init_io(system_io, NULL, &arc_io_ops, NULL, "arc.io",
+                           1024);
+    memory_region_add_subregion(get_system_memory(), 0xf0000000, system_io);
+
+    serial_mm_init(get_system_memory(), 0x90000000, 2, cpu->env.irq[20],
+                   115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
+
+    if (nd_table[0].used) {
+        arc_sim_net_init(get_system_memory(), 0x92000000,
+                         0x92000400, cpu->env.irq[4], nd_table);
+    }
+
+    arc_load_kernel(cpu, &boot_info);
+}
+
+static void arc_sim_machine_init(MachineClass *mc)
+{
+    mc->desc = "ARCxx simulation";
+    mc->init = arc_sim_init;
+    mc->max_cpus = 1;
+    mc->is_default = false;
+    mc->default_cpu_type = ARC_CPU_TYPE_NAME("archs");
+}
+
+DEFINE_MACHINE("arc-sim", arc_sim_machine_init)
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/arc_uart.c b/hw/arc/arc_uart.c
new file mode 100644
index 0000000000..ec7aad37be
--- /dev/null
+++ b/hw/arc/arc_uart.c
@@ -0,0 +1,267 @@
+/*
+ * ARC UART model for QEMU
+ * Copyright (c) 2019 Synopsys Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/arc/arc_uart.h"
+#include "qemu/log.h"
+
+#ifndef ARC_UART_ERR_DEBUG
+#define ARC_UART_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...)                \
+    do {                                             \
+        if (ARC_UART_ERR_DEBUG >= lvl) {             \
+            qemu_log("%s: " fmt, __func__, ## args); \
+        }                                            \
+    } while (0)
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+enum {
+    ARC_UART_REG_ID0    = 0x00,
+    ARC_UART_REG_ID1    = 0x04,
+    ARC_UART_REG_ID2    = 0x08,
+    ARC_UART_REG_ID3    = 0x0c,
+    ARC_UART_REG_DATA   = 0x10,
+    ARC_UART_REG_STATUS = 0x14,
+    ARC_UART_REG_BAUDL  = 0x18,
+    ARC_UART_REG_BAUDH  = 0x1c,
+    ARC_UART_REG_MAX    = 0x20
+};
+
+/*
+ * Bit definitions of STATUS register:
+ *
+ * UART_TXEMPTY      Transmit FIFO Empty, thus char can be written into
+ * UART_TX_IE        Transmit Interrupt Enable
+ * UART_RXEMPTY      Receive FIFO Empty: No char receivede
+ * UART_RX_FULL1     Receive FIFO has space for 1 char (tot space=4)
+ * UART_RX_FULL      Receive FIFO full
+ * UART_RX_IE        Receive Interrupt Enable
+ * UART_OVERFLOW_ERR OverFlow Err: Char recv but RXFULL still set
+ * UART_RX_FERR      Frame Error: Stop Bit not detected
+ */
+#define UART_TXEMPTY      (1 << 7)
+#define UART_TX_IE        (1 << 6)
+#define UART_RXEMPTY      (1 << 5)
+#define UART_RX_FULL1     (1 << 4)
+#define UART_RX_FULL      (1 << 3)
+#define UART_RX_IE        (1 << 2)
+#define UART_OVERFLOW_ERR (1 << 1)
+#define UART_RX_FERR      (1 << 0)
+
+static void arc_uart_update_irq(const ARC_UART_State *s)
+{
+    int cond = 0;
+
+    if ((s->rx_ie && s->rx_fifo_len) || s->tx_ie) {
+        cond = 1;
+    }
+
+    if (cond) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static uint32_t arc_status_get(const ARC_UART_State *s)
+{
+    uint32_t status = UART_TXEMPTY;
+
+    if (!s->rx_fifo_len) {
+        status |= UART_RXEMPTY;
+    }
+
+    if (s->rx_ie) {
+        status |= UART_RX_IE;
+    }
+
+    if (s->tx_ie) {
+        status |= UART_TX_IE;
+    }
+
+    if (s->rx_fifo_len == sizeof(s->rx_fifo)) {
+        status |= UART_RX_FULL;
+    }
+
+    if (s->rx_fifo_len == (sizeof(s->rx_fifo) - 1)) {
+        status |= UART_RX_FULL1;
+    }
+
+    return status;
+}
+
+static void arc_status_set(ARC_UART_State *s, char value)
+{
+    if (value & UART_TX_IE) {
+        s->tx_ie = true;
+    } else {
+        s->tx_ie = false;
+    }
+
+    /*
+     * Tx IRQ is active if (TXIE && TXEMPTY), but since in QEMU we
+     * transmit data immediately TXEMPTY is permanently set, thus
+     * for TX IRQ state we need to check TXIE only which we do here.
+     */
+    arc_uart_update_irq(s);
+
+    if (value & UART_RX_IE) {
+        s->rx_ie = true;
+    } else {
+        s->rx_ie = false;
+    }
+}
+
+static uint64_t arc_uart_read(void *opaque, hwaddr addr, unsigned size)
+{
+    ARC_UART_State *s = opaque;
+    uint32_t c;
+
+    DB_PRINT("Read reg with offset 0x%02x\n", (unsigned int)addr);
+
+    switch (addr) {
+    case ARC_UART_REG_ID0:
+    case ARC_UART_REG_ID1:
+    case ARC_UART_REG_ID2:
+    case ARC_UART_REG_ID3:
+        return 0;
+    case ARC_UART_REG_DATA:
+        c = s->rx_fifo[0];
+        memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
+        s->rx_fifo_len--;
+        qemu_chr_fe_accept_input(&s->chr);
+        arc_uart_update_irq(s);
+        DB_PRINT("Read char: %c\n", c);
+        return c;
+    case ARC_UART_REG_STATUS:
+        return arc_status_get(s);
+    case ARC_UART_REG_BAUDL:
+        return s->baud & 0xff;
+    case ARC_UART_REG_BAUDH:
+        return s->baud >> 8;
+    default:
+        break;
+    }
+
+    hw_error("%s@%d: Wrong register with offset 0x%02x is used!\n",
+             __func__, __LINE__, (unsigned int)addr);
+    return 0;
+}
+
+static void arc_uart_write(void *opaque, hwaddr addr,
+                           uint64_t value, unsigned size)
+{
+    ARC_UART_State *s = opaque;
+    unsigned char ch = value;
+
+    DB_PRINT("Write value 0x%02x to reg with offset 0x%02x\n",
+             ch, (unsigned int)addr);
+
+    switch (addr) {
+    case ARC_UART_REG_ID0:
+    case ARC_UART_REG_ID1:
+    case ARC_UART_REG_ID2:
+    case ARC_UART_REG_ID3:
+        break;
+    case ARC_UART_REG_DATA:
+        DB_PRINT("Write char: %c\n", ch);
+        qemu_chr_fe_write(&s->chr, &ch, 1);
+        break;
+    case ARC_UART_REG_STATUS:
+        arc_status_set(s, ch);
+        break;
+    case ARC_UART_REG_BAUDL:
+        s->baud = (s->baud & 0xff00) + value;
+        break;
+    case ARC_UART_REG_BAUDH:
+        s->baud = (s->baud & 0xff) + (value << 8);
+        break;
+    default:
+        hw_error("%s@%d: Wrong register with offset 0x%02x is used!\n",
+                 __func__, __LINE__, (unsigned int)addr);
+    }
+}
+
+static const MemoryRegionOps arc_uart_ops = {
+    .read = arc_uart_read,
+    .write = arc_uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+    ARC_UART_State *s = opaque;
+
+    /* Got a byte.  */
+    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
+        DB_PRINT("Rx FIFO is full dropping the chars\n");
+        return;
+    }
+    s->rx_fifo[s->rx_fifo_len++] = *buf;
+
+    arc_uart_update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+    ARC_UART_State *s = opaque;
+
+    return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, QEMUChrEvent event)
+{
+}
+
+static int uart_be_change(void *opaque)
+{
+    ARC_UART_State *s = opaque;
+
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+                             uart_be_change, s, NULL, true);
+    return 0;
+}
+
+ARC_UART_State *arc_uart_create(MemoryRegion *address_space, hwaddr base,
+                                Chardev *chr, qemu_irq irq)
+{
+    ARC_UART_State *s = g_malloc0(sizeof(ARC_UART_State));
+
+    DB_PRINT("Create ARC UART\n");
+
+    s->irq = irq;
+    qemu_chr_fe_init(&s->chr, chr, &error_abort);
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+                             uart_be_change, s, NULL, true);
+    memory_region_init_io(&s->mmio, NULL, &arc_uart_ops, s,
+                          TYPE_ARC_UART, ARC_UART_REG_MAX);
+    memory_region_add_subregion(address_space, base, &s->mmio);
+    return s;
+}
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/board-hsdk.c b/hw/arc/board-hsdk.c
new file mode 100644
index 0000000000..5440a73c2f
--- /dev/null
+++ b/hw/arc/board-hsdk.c
@@ -0,0 +1,107 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "boot.h"
+#include "hw/boards.h"
+#include "hw/char/serial.h"
+#include "exec/address-spaces.h"
+#include "sysemu/reset.h"
+#include "sysemu/sysemu.h"
+#include "hw/arc/cpudevs.h"
+#include "hw/sysbus.h"
+
+#define HSDK_RAM_BASE      0x80000000
+#define HSDK_RAM_SIZE      0x80000000
+#define HSDK_IO_BASE       0xf0000000
+#define HSDK_IO_SIZE       0x10000000
+#define HSDK_UART0_OFFSET  0x5000
+#define HSDK_UART0_IRQ     30
+
+/* VirtIO */
+#define HSDK_VIRTIO_NUMBER 5
+#define HSDK_VIRTIO_OFFSET 0x100000
+#define HSDK_VIRTIO_BASE   (HSDK_IO_BASE + HSDK_VIRTIO_OFFSET)
+#define HSDK_VIRTIO_SIZE   0x1000
+#define HSDK_VIRTIO_IRQ    31
+
+static void hsdk_init(MachineState *machine)
+{
+    static struct arc_boot_info boot_info;
+    unsigned int smp_cpus = machine->smp.cpus;
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *system_ram;
+    MemoryRegion *system_io;
+    ARCCPU *cpu = NULL;
+    int n;
+
+    boot_info.ram_start = HSDK_RAM_BASE;
+    boot_info.ram_size = HSDK_RAM_SIZE;
+    boot_info.kernel_filename = machine->kernel_filename;
+    boot_info.kernel_cmdline = machine->kernel_cmdline;
+
+    for (n = 0; n < smp_cpus; n++) {
+        cpu = ARC_CPU(cpu_create("archs-" TYPE_ARC_CPU));
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find CPU definition!\n");
+            exit(1);
+        }
+
+       /* Initialize internal devices. */
+        cpu_arc_pic_init(cpu);
+        cpu_arc_clock_init(cpu);
+
+        qemu_register_reset(arc_cpu_reset, cpu);
+    }
+
+    /* Init system DDR */
+    system_ram = g_new(MemoryRegion, 1);
+    memory_region_init_ram(system_ram, NULL, "arc.ram", HSDK_RAM_SIZE,
+                           &error_fatal);
+    memory_region_add_subregion(system_memory, HSDK_RAM_BASE, system_ram);
+
+    /* Init IO area */
+    system_io = g_new(MemoryRegion, 1);
+    memory_region_init_io(system_io, NULL, NULL, NULL, "arc.io",
+                          HSDK_IO_SIZE);
+    memory_region_add_subregion(system_memory, HSDK_IO_BASE, system_io);
+
+    serial_mm_init(system_io, HSDK_UART0_OFFSET, 2,
+                   cpu->env.irq[HSDK_UART0_IRQ], 115200, serial_hd(0),
+                   DEVICE_NATIVE_ENDIAN);
+
+    for (n = 0; n < HSDK_VIRTIO_NUMBER; n++) {
+        sysbus_create_simple("virtio-mmio",
+                             HSDK_VIRTIO_BASE + HSDK_VIRTIO_SIZE * n,
+                             cpu->env.irq[HSDK_VIRTIO_IRQ + n]);
+    }
+
+    arc_load_kernel(cpu, &boot_info);
+}
+
+static void hsdk_machine_init(MachineClass *mc)
+{
+    mc->desc = "ARC HSDK Emulator";
+    mc->init = hsdk_init;
+    mc->max_cpus = 1;
+    mc->is_default = false;
+}
+
+DEFINE_MACHINE("hsdk", hsdk_machine_init)
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/boot.c b/hw/arc/boot.c
new file mode 100644
index 0000000000..95ba3a7a56
--- /dev/null
+++ b/hw/arc/boot.c
@@ -0,0 +1,95 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "boot.h"
+#include "elf.h"
+#include "hw/loader.h"
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+
+void arc_cpu_reset(void *opaque)
+{
+    ARCCPU *cpu = opaque;
+    CPUARCState *env = &cpu->env;
+    const struct arc_boot_info *info = env->boot_info;
+
+    cpu_reset(CPU(cpu));
+
+    /*
+     * Right before start CPU gets reset wiping out everything
+     * but PC which we set on Elf load.
+     *
+     * And if we still want to pass something like U-Boot data
+     * via CPU registers we have to do it here.
+     */
+
+    if (info->kernel_cmdline && strlen(info->kernel_cmdline)) {
+        /*
+         * Load "cmdline" far enough from the kernel image.
+         * Round by MAX page size for ARC - 16 KiB.
+         */
+        hwaddr cmdline_addr = info->ram_start +
+            QEMU_ALIGN_UP(info->ram_size / 2, 16 * KiB);
+        cpu_physical_memory_write(cmdline_addr, info->kernel_cmdline,
+                                  strlen(info->kernel_cmdline));
+
+        /* We're passing "cmdline" */
+        cpu->env.r[0] = ARC_UBOOT_CMDLINE;
+        cpu->env.r[2] = cmdline_addr;
+    }
+}
+
+
+void arc_load_kernel(ARCCPU *cpu, struct arc_boot_info *info)
+{
+    hwaddr entry;
+    int elf_machine, kernel_size;
+
+    if (!info->kernel_filename) {
+        error_report("missing kernel file");
+        exit(EXIT_FAILURE);
+    }
+
+    elf_machine = cpu->env.family > 2 ? EM_ARC_COMPACT2 : EM_ARC_COMPACT;
+    kernel_size = load_elf(info->kernel_filename, NULL, NULL, NULL,
+                           &entry, NULL, NULL, NULL, ARC_ENDIANNESS_LE,
+                           elf_machine, 1, 0);
+
+    if (kernel_size < 0) {
+        int is_linux;
+
+        kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
+                                  &is_linux, NULL, NULL);
+        if (!is_linux) {
+            error_report("Wrong U-Boot image, only Linux kernel is supported");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    if (kernel_size < 0) {
+        error_report("No kernel image found");
+        exit(EXIT_FAILURE);
+    }
+
+    cpu->env.boot_info = info;
+
+    /* Set CPU's PC to point to the entry-point */
+    cpu->env.pc = entry;
+}
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/boot.h b/hw/arc/boot.h
new file mode 100644
index 0000000000..e46aa16fc6
--- /dev/null
+++ b/hw/arc/boot.h
@@ -0,0 +1,21 @@
+#ifndef ARC_BOOT_H
+#define ARC_BOOT_H
+
+#include "hw/hw.h"
+#include "cpu.h"
+
+struct arc_boot_info {
+    hwaddr ram_start;
+    uint64_t ram_size;
+    const char *kernel_filename;
+    const char *kernel_cmdline;
+};
+
+void arc_cpu_reset(void *opaque);
+void arc_load_kernel(ARCCPU *cpu, struct arc_boot_info *boot_info);
+
+#endif /* ARC_BOOT_H */
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/meson.build b/hw/arc/meson.build
new file mode 100644
index 0000000000..6a587307a4
--- /dev/null
+++ b/hw/arc/meson.build
@@ -0,0 +1,13 @@
+arc_ss = ss.source_set()
+arc_ss.add(files(
+  'arc_sim.c',
+  'arc_uart.c',
+  'sample.c',
+  'pic_cpu.c',
+  'boot.c',
+  'board-hsdk.c',
+  'sim-hs.c',
+  'nsim.c',
+))
+
+hw_arch += {'arc': arc_ss}
diff --git a/hw/arc/nsim.c b/hw/arc/nsim.c
new file mode 100644
index 0000000000..810b0efa1d
--- /dev/null
+++ b/hw/arc/nsim.c
@@ -0,0 +1,86 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "boot.h"
+#include "sysemu/reset.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/char/serial.h"
+#include "exec/address-spaces.h"
+#include "hw/arc/arc_uart.h"
+#include "hw/arc/cpudevs.h"
+#include "hw/sysbus.h"
+
+#define NSIM_RAM_BASE        0x80000000
+#define NSIM_RAM_SIZE        0x40000000
+#define NSIM_ARC_UART_OFFSET 0xc0fc1000
+
+static void nsim_init(MachineState *machine)
+{
+    static struct arc_boot_info boot_info;
+    unsigned int smp_cpus = machine->smp.cpus;
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *system_ram;
+    ARCCPU *cpu = NULL;
+    int n;
+
+    boot_info.ram_start = NSIM_RAM_BASE;
+    boot_info.ram_size = NSIM_RAM_SIZE;
+    boot_info.kernel_filename = machine->kernel_filename;
+    boot_info.kernel_cmdline = machine->kernel_cmdline;
+
+    for (n = 0; n < smp_cpus; n++) {
+        cpu = ARC_CPU (cpu_create("archs-" TYPE_ARC_CPU));
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find CPU definition!\n");
+            exit(1);
+        }
+
+       /* Initialize internal devices. */
+        cpu_arc_pic_init(cpu);
+        cpu_arc_clock_init(cpu);
+
+        qemu_register_reset(arc_cpu_reset, cpu);
+    }
+
+    /* Init system DDR */
+    system_ram = g_new(MemoryRegion, 1);
+    memory_region_init_ram(system_ram, NULL, "arc.ram", NSIM_RAM_SIZE,
+                           &error_fatal);
+    memory_region_add_subregion(system_memory, NSIM_RAM_BASE, system_ram);
+
+    /* Init ARC UART */
+    arc_uart_create(get_system_memory(), NSIM_ARC_UART_OFFSET,
+                    serial_hd(0), cpu->env.irq[24]);
+
+    arc_load_kernel(cpu, &boot_info);
+}
+
+static void nsim_machine_init(MachineClass *mc)
+{
+    mc->desc = "ARC nSIM";
+    mc->init = nsim_init;
+    mc->max_cpus = 1;
+    mc->is_default = false;
+}
+
+DEFINE_MACHINE("nsim", nsim_machine_init)
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/pic_cpu.c b/hw/arc/pic_cpu.c
new file mode 100644
index 0000000000..0a29a2f803
--- /dev/null
+++ b/hw/arc/pic_cpu.c
@@ -0,0 +1,111 @@
+/*
+ * ARC Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2019
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "qemu/log.h"
+#include "hw/arc/cpudevs.h"
+
+/*
+ * ARC pic handler
+ */
+static void arc_pic_cpu_handler(void *opaque, int irq, int level)
+{
+    ARCCPU *cpu = (ARCCPU *) opaque;
+    CPUState *cs = CPU(cpu);
+    CPUARCState *env = &cpu->env;
+    int i;
+    bool clear = false;
+    uint32_t irq_bit;
+
+    /* Assert if this handler is called in a system without interrupts. */
+    assert(cpu->cfg.has_interrupts);
+
+    /* Assert if the IRQ is not within the cpu configuration bounds. */
+    assert(irq >= 16 && irq < (cpu->cfg.number_of_interrupts + 15));
+
+    irq_bit = 1 << env->irq_bank[irq].priority;
+    if (level) {
+        /*
+         * An interrupt is enabled, update irq_priority_pendig and rise
+         * the qemu interrupt line.
+         */
+        env->irq_bank[irq].pending = 1;
+        qatomic_or(&env->irq_priority_pending, irq_bit);
+        cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+    } else {
+        env->irq_bank[irq].pending = 0;
+
+        /*
+         * First, check if we still have any pending interrupt at the
+         * given priority.
+         */
+        clear = true;
+        for (i = 16; i < cpu->cfg.number_of_interrupts; i++) {
+            if (env->irq_bank[i].pending
+                && env->irq_bank[i].priority == env->irq_bank[irq].priority) {
+                clear = false;
+                break;
+            }
+        }
+
+        /* If not, update (clear) irq_priority_pending. */
+        if (clear) {
+            qatomic_and(&env->irq_priority_pending, ~irq_bit);
+        }
+
+        /*
+         * If we don't have any pending priority, lower the qemu irq
+         * line. N.B. we can also check more here like IE bit, but we
+         * need to add a cpu_interrupt call when we enable the
+         * interrupts (e.g., sleep, seti).
+         */
+        if (!env->irq_priority_pending) {
+            cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+        }
+    }
+    qemu_log_mask(CPU_LOG_INT,
+                  "[IRQ] level = %d, clear = %d, irq = %d, priority = %d, "
+                  "pending = %08x, pc = %08x\n",
+                  level, clear, irq, env->irq_bank[irq].priority,
+                  env->irq_priority_pending, env->pc);
+}
+
+/*
+ * ARC PIC initialization helper
+ */
+void cpu_arc_pic_init(ARCCPU *cpu)
+{
+    CPUARCState *env = &cpu->env;
+    int i;
+    qemu_irq *qi;
+
+    qi = qemu_allocate_irqs(arc_pic_cpu_handler, cpu,
+                            16 + cpu->cfg.number_of_interrupts);
+
+    for (i = 0; i < cpu->cfg.number_of_interrupts; i++) {
+        env->irq[16 + i] = qi[16 + i];
+    }
+}
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/sample.c b/hw/arc/sample.c
new file mode 100644
index 0000000000..0ecc11cf15
--- /dev/null
+++ b/hw/arc/sample.c
@@ -0,0 +1,77 @@
+/*
+ * QEMU ARC CPU
+ *
+ * Copyright (c) 2016 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "ui/console.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "qemu/error-report.h"
+#include "exec/address-spaces.h"
+#include "include/hw/sysbus.h"
+
+#define SIZE_RAM 0x00020000
+
+static void sample_init(MachineState *machine)
+{
+    MemoryRegion *ram;
+
+    ARCCPU *cpu_arc ATTRIBUTE_UNUSED;
+
+    ram = g_new(MemoryRegion, 1);
+
+    cpu_arc = ARC_CPU(cpu_create("archs-" TYPE_ARC_CPU));
+
+    memory_region_init_ram(ram, NULL, "ram", SIZE_RAM, &error_fatal);
+    memory_region_add_subregion(get_system_memory(), PHYS_BASE_RAM, ram);
+
+    char const *firmware = NULL;
+    char const *filename;
+
+    if (machine->firmware) {
+        firmware = machine->firmware;
+    }
+
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware);
+    if (!filename) {
+        error_report("Could not find flash image file '%s'", firmware);
+        exit(1);
+    }
+
+    load_image_targphys(filename, PHYS_BASE_RAM + 0x100, SIZE_RAM);
+}
+
+static void sample_machine_init(MachineClass *mc)
+{
+    mc->desc = "ARC sample/example board";
+    mc->init = sample_init;
+    mc->is_default = false;
+}
+
+DEFINE_MACHINE("sample", sample_machine_init)
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/hw/arc/sim-hs.c b/hw/arc/sim-hs.c
new file mode 100644
index 0000000000..93cd9e3826
--- /dev/null
+++ b/hw/arc/sim-hs.c
@@ -0,0 +1,107 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "boot.h"
+#include "hw/boards.h"
+#include "hw/char/serial.h"
+#include "exec/address-spaces.h"
+#include "sysemu/reset.h"
+#include "sysemu/sysemu.h"
+#include "hw/arc/cpudevs.h"
+#include "hw/sysbus.h"
+
+#define SIMHS_RAM_BASE      0x80000000
+#define SIMHS_RAM_SIZE      0x80000000
+#define SIMHS_IO_BASE       0xf0000000
+#define SIMHS_IO_SIZE       0x10000000
+#define SIMHS_UART0_OFFSET  0x0
+#define SIMHS_UART0_IRQ     24
+
+/* VirtIO */
+#define SIMHS_VIRTIO_NUMBER 5
+#define SIMHS_VIRTIO_OFFSET 0x100000
+#define SIMHS_VIRTIO_BASE   (SIMHS_IO_BASE + SIMHS_VIRTIO_OFFSET)
+#define SIMHS_VIRTIO_SIZE   0x2000
+#define SIMHS_VIRTIO_IRQ    31
+
+static void simhs_init(MachineState *machine)
+{
+    static struct arc_boot_info boot_info;
+    unsigned int smp_cpus = machine->smp.cpus;
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *system_ram;
+    MemoryRegion *system_io;
+    ARCCPU *cpu = NULL;
+    int n;
+
+    boot_info.ram_start = SIMHS_RAM_BASE;
+    boot_info.ram_size = SIMHS_RAM_SIZE;
+    boot_info.kernel_filename = machine->kernel_filename;
+    boot_info.kernel_cmdline = machine->kernel_cmdline;
+
+    for (n = 0; n < smp_cpus; n++) {
+        cpu = ARC_CPU(cpu_create("archs-" TYPE_ARC_CPU));
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find CPU definition!\n");
+            exit(1);
+        }
+
+       /* Initialize internal devices. */
+        cpu_arc_pic_init(cpu);
+        cpu_arc_clock_init(cpu);
+
+        qemu_register_reset(arc_cpu_reset, cpu);
+    }
+
+    /* Init system DDR */
+    system_ram = g_new(MemoryRegion, 1);
+    memory_region_init_ram(system_ram, NULL, "arc.ram", SIMHS_RAM_SIZE,
+                           &error_fatal);
+    memory_region_add_subregion(system_memory, SIMHS_RAM_BASE, system_ram);
+
+    /* Init IO area */
+    system_io = g_new(MemoryRegion, 1);
+    memory_region_init_io(system_io, NULL, NULL, NULL, "arc.io",
+                          SIMHS_IO_SIZE);
+    memory_region_add_subregion(system_memory, SIMHS_IO_BASE, system_io);
+
+    serial_mm_init(system_io, SIMHS_UART0_OFFSET, 2,
+                   cpu->env.irq[SIMHS_UART0_IRQ], 115200, serial_hd(0),
+                   DEVICE_NATIVE_ENDIAN);
+
+    for (n = 0; n < SIMHS_VIRTIO_NUMBER; n++) {
+        sysbus_create_simple("virtio-mmio",
+                             SIMHS_VIRTIO_BASE + SIMHS_VIRTIO_SIZE * n,
+                             cpu->env.irq[SIMHS_VIRTIO_IRQ + n]);
+    }
+
+    arc_load_kernel(cpu, &boot_info);
+}
+
+static void simhs_machine_init(MachineClass *mc)
+{
+    mc->desc = "ARC HS Simulator";
+    mc->init = simhs_init;
+    mc->max_cpus = 1;
+    mc->is_default = true;
+}
+
+DEFINE_MACHINE("simhs", simhs_machine_init)
+
+
+/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/
+/* vim: set ts=4 sw=4 et: */
diff --git a/include/hw/arc/arc_uart.h b/include/hw/arc/arc_uart.h
new file mode 100644
index 0000000000..305f93d134
--- /dev/null
+++ b/include/hw/arc/arc_uart.h
@@ -0,0 +1,43 @@
+/*
+ * ARC UART model for QEMU
+ * Copyright (c) 2020 Synopsys Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ARC_UART_H
+#define ARC_UART_H
+
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+
+#define TYPE_ARC_UART "arc-uart"
+#define ARC_UART(obj) OBJECT_CHECK(ARC_UART_State, (obj), TYPE_ARC_UART)
+
+#define ARC_UART_RX_FIFO_LEN 4
+
+typedef struct ARC_UART_State {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+    CharBackend chr;
+    qemu_irq irq;
+    bool rx_ie, tx_ie;
+
+    uint8_t rx_fifo[ARC_UART_RX_FIFO_LEN];
+    unsigned int rx_fifo_len;
+    uint32_t baud;
+} ARC_UART_State;
+
+ARC_UART_State *arc_uart_create(MemoryRegion *address_space, hwaddr base,
+    Chardev *chr, qemu_irq irq);
+
+#endif
diff --git a/include/hw/arc/cpudevs.h b/include/hw/arc/cpudevs.h
new file mode 100644
index 0000000000..52f7eaf9f4
--- /dev/null
+++ b/include/hw/arc/cpudevs.h
@@ -0,0 +1,10 @@
+#ifndef HW_ARC_CPUDEVS_H
+#define HW_ARC_CPUDEVS_H
+
+/* Timer service routines.  */
+extern void cpu_arc_clock_init(ARCCPU *);
+
+/* PIC service routines. */
+extern void cpu_arc_pic_init(ARCCPU *);
+
+#endif /* !HW_ARC_CPUDEVS_H */
-- 
2.20.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]