[PATCH 12/14] arc: Add Synopsys ARC emulation boards
cupertinomiranda at gmail.com
cupertinomiranda at gmail.com
Wed Sep 30 16:46:02 EDT 2020
From: Claudiu Zissulescu <claziss at 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 at 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
More information about the linux-snps-arc
mailing list