diff mbox series

[3/3] Andes AE350 RISC-V Machine

Message ID 20210310033358.30499-4-dylan@andestech.com (mailing list archive)
State New, archived
Headers show
Series Support Andes AE350 Platform | expand

Commit Message

Dylan Jhong March 10, 2021, 3:33 a.m. UTC
This provides a RISC-V Board based on Andes's AE350 specification.
The following machine is implemented:

- 'andes_ae350'; PLIC, PLICSW, PLMT, 16550a UART, VirtIO MMIO, device-tree

Signed-off-by: Dylan Jhong <dylan@andestech.com>
Signed-off-by: Ruinland ChuanTzu Tsai <ruinland@andestech.com>
---
 default-configs/devices/riscv32-softmmu.mak |   1 +
 default-configs/devices/riscv64-softmmu.mak |   1 +
 hw/riscv/Kconfig                            |   7 +
 hw/riscv/andes_ae350.c                      | 501 ++++++++++++++++++++
 hw/riscv/meson.build                        |   1 +
 include/hw/riscv/andes_ae350.h              |  93 ++++
 6 files changed, 604 insertions(+)
 create mode 100644 hw/riscv/andes_ae350.c
 create mode 100644 include/hw/riscv/andes_ae350.h

Comments

Bin Meng March 10, 2021, 6:15 a.m. UTC | #1
On Wed, Mar 10, 2021 at 11:36 AM Dylan Jhong <dylan@andestech.com> wrote:
>
> This provides a RISC-V Board based on Andes's AE350 specification.
> The following machine is implemented:
>
> - 'andes_ae350'; PLIC, PLICSW, PLMT, 16550a UART, VirtIO MMIO, device-tree

Is this a virtual target because virtio is added? Or does the hardware
provide the virtio programming interface?

>
> Signed-off-by: Dylan Jhong <dylan@andestech.com>
> Signed-off-by: Ruinland ChuanTzu Tsai <ruinland@andestech.com>
> ---
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/riscv/Kconfig                            |   7 +
>  hw/riscv/andes_ae350.c                      | 501 ++++++++++++++++++++
>  hw/riscv/meson.build                        |   1 +
>  include/hw/riscv/andes_ae350.h              |  93 ++++
>  6 files changed, 604 insertions(+)
>  create mode 100644 hw/riscv/andes_ae350.c
>  create mode 100644 include/hw/riscv/andes_ae350.h
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
> index d847bd5692..a268007e72 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>
>  # Boards:
>  #
> +CONFIG_ANDES_AE350=y
>  CONFIG_SPIKE=y
>  CONFIG_SIFIVE_E=y
>  CONFIG_SIFIVE_U=y
> diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
> index d5eec75f05..9a37dfd8c0 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>
>  # Boards:
>  #
> +CONFIG_ANDES_AE350=y
>  CONFIG_SPIKE=y
>  CONFIG_SIFIVE_E=y
>  CONFIG_SIFIVE_U=y
> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> index d139074b02..04f6369ab7 100644
> --- a/hw/riscv/Kconfig
> +++ b/hw/riscv/Kconfig
> @@ -1,6 +1,13 @@
>  config IBEX
>      bool
>
> +config ANDES_AE350

This needs to be sorted in alphabetical order

> +    bool
> +    select SERIAL
> +    select VIRTIO_MMIO
> +    select ANDES_PLIC
> +    select ANDES_PLMT
> +
>  config MICROCHIP_PFSOC
>      bool
>      select CADENCE_SDHCI
> diff --git a/hw/riscv/andes_ae350.c b/hw/riscv/andes_ae350.c
> new file mode 100644
> index 0000000000..ed5f9701ad
> --- /dev/null
> +++ b/hw/riscv/andes_ae350.c
> @@ -0,0 +1,501 @@
> +/*
> + * Andes RISC-V AE350 Board
> + *
> + * Copyright (c) 2021 Andes Tech. Corp.
> + *
> + * Andes AE350 Board supports ns16550a UART and VirtIO MMIO.
> + * The interrupt controllers are andes PLIC and andes PLICSW.
> + * Timer is Andes PLMT.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * 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 "qemu/units.h"
> +#include "qemu/log.h"
> +#include "qemu/error-report.h"
> +#include "qapi/error.h"
> +#include "hw/boards.h"
> +#include "hw/loader.h"
> +#include "hw/sysbus.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/char/serial.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/riscv_hart.h"
> +#include "hw/riscv/boot.h"
> +#include "hw/riscv/numa.h"
> +#include "chardev/char.h"
> +#include "sysemu/arch_init.h"
> +#include "sysemu/device_tree.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci-host/gpex.h"
> +
> +#include "hw/intc/andes_plic.h"
> +#include "hw/timer/andes_plmt.h"
> +#include "hw/riscv/andes_ae350.h"
> +
> +# define BIOS_FILENAME ""
> +
> +static const struct MemmapEntry {
> +    hwaddr base;
> +    hwaddr size;
> +} andes_ae350_memmap[] = {
> +    [ANDES_AE350_DEBUG]     = { 0x00000000,      0x100 },
> +    [ANDES_AE350_DRAM]      = { 0x00000000, 0x80000000 },
> +    [ANDES_AE350_MROM]      = { 0xb0000000,   0x100000 },
> +    [ANDES_AE350_MAC]       = { 0xe0100000,   0x100000 },
> +    [ANDES_AE350_GEM]       = { 0xe0200000,   0x100000 },
> +    [ANDES_AE350_PLIC]      = { 0xe4000000,   0x400000 },
> +    [ANDES_AE350_PLMT]      = { 0xe6000000,   0x100000 },
> +    [ANDES_AE350_PLICSW]    = { 0xe6400000,   0x400000 },
> +    [ANDES_AE350_UART1]     = { 0xf0200000,      0x100 },
> +    [ANDES_AE350_UART2]     = { 0xf0300000,      0x100 },
> +    [ANDES_AE350_PIT]       = { 0xf0400000,   0x100000 },
> +    [ANDES_AE350_SDC]       = { 0xf0e00000,   0x100000 },
> +    [ANDES_AE350_VIRTIO]    = { 0xfe000000,     0x1000 },
> +};
> +
> +static void
> +create_fdt(AndesAe350BoardState *bs, const struct MemmapEntry *memmap,
> +    uint64_t mem_size, const char *cmdline)
> +{
> +    AndesAe350SocState *s = &bs->soc;
> +    MachineState *ms = MACHINE(qdev_get_machine());
> +    void *fdt;
> +    int cpu, i;
> +    uint64_t mem_addr;
> +    uint32_t *plic_irq_ext, *plicsw_irq_ext, *plmt_irq_ext;
> +    unsigned long plic_addr, plicsw_addr, plmt_addr;
> +    char *plic_name, *plicsw_name, *plmt_name;
> +    uint32_t intc_phandle = 0, plic_phandle = 0;
> +    uint32_t phandle = 1;
> +    char *isa_name, *mem_name, *cpu_name, *intc_name, *uart_name, *virtio_name;
> +
> +    if (ms->dtb) {
> +        fdt = bs->fdt = load_device_tree(ms->dtb, &bs->fdt_size);
> +        if (!fdt) {
> +            error_report("load_device_tree() failed");
> +            exit(1);
> +        }
> +        goto update_bootargs;
> +    } else {
> +        fdt = bs->fdt = create_device_tree(&bs->fdt_size);
> +        if (!fdt) {
> +            error_report("create_device_tree() failed");
> +            exit(1);
> +        }
> +    }
> +
> +    qemu_fdt_setprop_string(fdt, "/", "model", "Andes AE350 Board");
> +    qemu_fdt_setprop_string(fdt, "/", "compatible", "andestech,ae350");
> +    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
> +
> +    qemu_fdt_add_subnode(fdt, "/soc");
> +    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
> +    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
> +    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
> +
> +    qemu_fdt_add_subnode(fdt, "/cpus");
> +    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> +                          ANDES_PLMT_TIMEBASE_FREQ);
> +    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> +    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> +    qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
> +
> +    plic_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 4);
> +    plicsw_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> +    plmt_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> +
> +    for (cpu = 0; cpu < s->cpus.num_harts; cpu++) {
> +        intc_phandle = phandle++;
> +
> +        cpu_name = g_strdup_printf("/cpus/cpu@%d",
> +            s->cpus.hartid_base + cpu);
> +        qemu_fdt_add_subnode(fdt, cpu_name);
> +#if defined(TARGET_RISCV32)
> +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32");
> +#else
> +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv39");
> +#endif
> +        isa_name = riscv_isa_string(&s->cpus.harts[cpu]);
> +        qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", isa_name);
> +        g_free(isa_name);
> +        qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
> +        qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
> +        qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
> +            s->cpus.hartid_base + cpu);
> +        qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
> +
> +        intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
> +        qemu_fdt_add_subnode(fdt, intc_name);
> +        qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle);
> +        qemu_fdt_setprop_string(fdt, intc_name, "compatible",
> +            "riscv,cpu-intc");
> +        qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
> +        qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
> +
> +        plic_irq_ext[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
> +        plic_irq_ext[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
> +        plic_irq_ext[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
> +        plic_irq_ext[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
> +
> +        plicsw_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> +        plicsw_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
> +
> +        plmt_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> +        plmt_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
> +
> +        g_free(intc_name);
> +    }
> +
> +    mem_addr = memmap[ANDES_AE350_DRAM].base;
> +    mem_name = g_strdup_printf("/memory@%lx", (long)mem_addr);
> +    qemu_fdt_add_subnode(fdt, mem_name);
> +    qemu_fdt_setprop_cells(fdt, mem_name, "reg",
> +        mem_addr >> 32, mem_addr, mem_size >> 32, mem_size);
> +    qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
> +    g_free(mem_name);
> +
> +    /* create plic */
> +    plic_phandle = phandle++;
> +    plic_addr = memmap[ANDES_AE350_PLIC].base;
> +    plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
> +    qemu_fdt_add_subnode(fdt, plic_name);
> +    qemu_fdt_setprop_cell(fdt, plic_name,
> +        "#address-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, plic_name,
> +        "#interrupt-cells", 0x2);
> +    qemu_fdt_setprop_string(fdt, plic_name, "compatible", "riscv,plic0");

This suggests PLIC is the same as the SiFive one. So why do we have a
different implementation of the PLIC model?

> +    qemu_fdt_setprop(fdt, plic_name, "interrupt-controller", NULL, 0);
> +    qemu_fdt_setprop(fdt, plic_name, "interrupts-extended",
> +        plic_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 4);
> +    qemu_fdt_setprop_cells(fdt, plic_name, "reg",
> +        0x0, plic_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
> +    qemu_fdt_setprop_cell(fdt, plic_name, "riscv,ndev", 0x47);
> +    qemu_fdt_setprop_cell(fdt, plic_name, "phandle", plic_phandle);
> +    g_free(plic_name);
> +    g_free(plic_irq_ext);
> +
> +    /* create plicsw */
> +    plicsw_addr = memmap[ANDES_AE350_PLICSW].base;
> +    plicsw_name = g_strdup_printf("/soc/interrupt-controller@%lx", plicsw_addr);
> +    qemu_fdt_add_subnode(fdt, plicsw_name);
> +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> +        "#address-cells", 0x2);
> +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> +        "#interrupt-cells", 0x2);
> +    qemu_fdt_setprop_string(fdt, plicsw_name, "compatible", "riscv,plic1");

Is this bindings in the Linux kernel upstream? I can't find any
reference in the kernel tree.

> +    qemu_fdt_setprop(fdt, plicsw_name, "interrupt-controller", NULL, 0);
> +    qemu_fdt_setprop(fdt, plicsw_name, "interrupts-extended",
> +        plicsw_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
> +    qemu_fdt_setprop_cells(fdt, plicsw_name, "reg",
> +        0x0, plicsw_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
> +    qemu_fdt_setprop_cell(fdt, plicsw_name, "riscv,ndev", 0x1);
> +    g_free(plicsw_name);
> +    g_free(plicsw_irq_ext);
> +
> +    /* create plmt */
> +    plmt_addr = memmap[ANDES_AE350_PLMT].base;
> +    plmt_name = g_strdup_printf("/soc/plmt0@%lx", plmt_addr);
> +    qemu_fdt_add_subnode(fdt, plmt_name);
> +    qemu_fdt_setprop_string(fdt, plmt_name, "compatible", "riscv,plmt0");

The same here.

> +    qemu_fdt_setprop(fdt, plmt_name, "interrupts-extended",
> +        plmt_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
> +    qemu_fdt_setprop_cells(fdt, plmt_name, "reg",
> +        0x0, plmt_addr, 0x0, memmap[ANDES_AE350_PLMT].size);
> +    g_free(plmt_name);
> +    g_free(plmt_irq_ext);
> +
> +    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART1].base);
> +    qemu_fdt_add_subnode(fdt, uart_name);
> +    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
> +    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
> +        0x0, memmap[ANDES_AE350_UART1].base,
> +        0x0, memmap[ANDES_AE350_UART1].size);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
> +    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART1_IRQ, 0x4);
> +
> +    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART2].base);
> +    qemu_fdt_add_subnode(fdt, uart_name);
> +    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
> +    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
> +        0x0, memmap[ANDES_AE350_UART2].base,
> +        0x0, memmap[ANDES_AE350_UART2].size);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
> +    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
> +    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART2_IRQ, 0x4);
> +
> +    qemu_fdt_add_subnode(fdt, "/chosen");
> +    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
> +            "console=ttyS0,38400n8 earlycon=sbi debug loglevel=7");
> +    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", uart_name);
> +    g_free(uart_name);
> +
> +    for (i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
> +        virtio_name = g_strdup_printf("/virtio_mmio@%lx",
> +            (memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size));
> +        qemu_fdt_add_subnode(fdt, virtio_name);
> +        qemu_fdt_setprop_string(fdt, virtio_name, "compatible", "virtio,mmio");
> +        qemu_fdt_setprop_cells(fdt, virtio_name, "reg",
> +            0x0, memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
> +            0x0, memmap[ANDES_AE350_VIRTIO].size);
> +        qemu_fdt_setprop_cell(fdt, virtio_name, "interrupt-parent",
> +                                plic_phandle);
> +        qemu_fdt_setprop_cells(fdt, virtio_name, "interrupts",
> +                                ANDES_AE350_VIRTIO_IRQ + i, 0x4);
> +        g_free(virtio_name);
> +    }
> +
> +update_bootargs:
> +    if (cmdline && cmdline[0] != '\0') {
> +        qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
> +    }
> +}
> +
> +static char *init_hart_config(const char *hart_config, int num_harts)
> +{
> +    int length = 0, i = 0;
> +    char *result;
> +
> +    length = (strlen(hart_config) + 1) * num_harts;
> +    result = g_malloc0(length);
> +    for (i = 0; i < num_harts; i++) {
> +        if (i != 0) {
> +            strncat(result, ",", length);
> +        }
> +        strncat(result, hart_config, length);
> +        length -= (strlen(hart_config) + 1);
> +    }
> +
> +    return result;
> +}
> +
> +static void andes_ae350_soc_realize(DeviceState *dev_soc, Error **errp)
> +{
> +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> +    MachineState *machine = MACHINE(qdev_get_machine());
> +    MemoryRegion *system_memory = get_system_memory();
> +    AndesAe350SocState *s = ANDES_AE350_SOC(dev_soc);
> +    char *plic_hart_config, *plicsw_hart_config;
> +
> +    plicsw_hart_config =
> +        init_hart_config(ANDES_AE350_PLICSW_HART_CONFIG, machine->smp.cpus);
> +
> +    /* Per-socket SW-PLIC */
> +    s->plic_sw = andes_plicsw_create(
> +        memmap[ANDES_AE350_PLICSW].base,
> +        ANDES_AE350_PLICSW_NAME,
> +        plicsw_hart_config,
> +        ANDES_AE350_PLICSW_NUM_SOURCES,
> +        ANDES_AE350_PLICSW_NUM_PRIORITIES,
> +        ANDES_AE350_PLICSW_PRIORITY_BASE,
> +        ANDES_AE350_PLICSW_PENDING_BASE,
> +        ANDES_AE350_PLICSW_ENABLE_BASE,
> +        ANDES_AE350_PLICSW_ENABLE_STRIDE,
> +        ANDES_AE350_PLICSW_THRESHOLD_BASE,
> +        ANDES_AE350_PLICSW_THRESHOLD_STRIDE,
> +        memmap[ANDES_AE350_PLICSW].size);
> +
> +    g_free(plicsw_hart_config);
> +
> +    andes_plmt_create(memmap[ANDES_AE350_PLMT].base,
> +                memmap[ANDES_AE350_PLMT].size,
> +                machine->smp.cpus, ANDES_PLMT_TIME_BASE, ANDES_PLMT_TIMECMP_BASE);
> +
> +    plic_hart_config =
> +        init_hart_config(ANDES_AE350_PLIC_HART_CONFIG, machine->smp.cpus);
> +
> +    /* Per-socket PLIC */
> +    s->plic = andes_plic_create(
> +        memmap[ANDES_AE350_PLIC].base,
> +        ANDES_AE350_PLIC_NAME,
> +        plic_hart_config,
> +        ANDES_AE350_PLIC_NUM_SOURCES,
> +        ANDES_AE350_PLIC_NUM_PRIORITIES,
> +        ANDES_AE350_PLIC_PRIORITY_BASE,
> +        ANDES_AE350_PLIC_PENDING_BASE,
> +        ANDES_AE350_PLIC_ENABLE_BASE,
> +        ANDES_AE350_PLIC_ENABLE_STRIDE,
> +        ANDES_AE350_PLIC_THRESHOLD_BASE,
> +        ANDES_AE350_PLIC_THRESHOLD_STRIDE,
> +        memmap[ANDES_AE350_PLIC].size);
> +
> +    g_free(plic_hart_config);
> +
> +    /* VIRTIO */
> +    for (int i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
> +        sysbus_create_simple("virtio-mmio",
> +            memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
> +            qdev_get_gpio_in(DEVICE(s->plic), (ANDES_AE350_VIRTIO_IRQ + i)));
> +    }
> +
> +    serial_mm_init(system_memory,
> +        memmap[ANDES_AE350_UART1].base + ANDES_UART_REG_OFFSET,
> +        ANDES_UART_REG_SHIFT,
> +        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART1_IRQ),
> +        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
> +
> +    serial_mm_init(system_memory,
> +        memmap[ANDES_AE350_UART2].base + ANDES_UART_REG_OFFSET,
> +        ANDES_UART_REG_SHIFT,
> +        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART2_IRQ),
> +        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
> +}
> +
> +static void andes_ae350_soc_instance_init(Object *obj)
> +{
> +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> +    MachineState *machine = MACHINE(qdev_get_machine());
> +    AndesAe350SocState *s = ANDES_AE350_SOC(obj);
> +
> +    object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
> +    object_property_set_str(OBJECT(&s->cpus), "cpu-type",
> +                            machine->cpu_type, &error_abort);
> +    object_property_set_int(OBJECT(&s->cpus), "num-harts",
> +                            machine->smp.cpus, &error_abort);
> +    qdev_prop_set_uint64(DEVICE(&s->cpus), "resetvec",
> +                            memmap[ANDES_AE350_MROM].base);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);
> +}
> +
> +static void andes_ae350_machine_init(MachineState *machine)
> +{
> +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> +
> +    AndesAe350BoardState *bs = ANDES_AE350_MACHINE(machine);
> +    MemoryRegion *system_memory = get_system_memory();
> +    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
> +    MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
> +    target_ulong start_addr = memmap[ANDES_AE350_DRAM].base;
> +    target_ulong firmware_end_addr, kernel_start_addr;
> +    uint32_t fdt_load_addr;
> +    uint64_t kernel_entry;
> +
> +    /* Initialize SoC */
> +    object_initialize_child(OBJECT(machine), "soc",
> +                    &bs->soc, TYPE_ANDES_AE350_SOC);
> +    qdev_realize(DEVICE(&bs->soc), NULL, &error_abort);
> +
> +    /* register system main memory (actual RAM) */
> +    memory_region_init_ram(main_mem, NULL, "riscv.andes.ae350.ram",
> +                           machine->ram_size, &error_fatal);
> +    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_DRAM].base,
> +        main_mem);
> +
> +    /* create device tree */
> +    create_fdt(bs, memmap, machine->ram_size, machine->kernel_cmdline);
> +
> +    /* boot rom */
> +    memory_region_init_rom(mask_rom, NULL, "riscv.andes.ae350.mrom",
> +                           memmap[ANDES_AE350_MROM].size, &error_fatal);
> +    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_MROM].base,
> +                                mask_rom);
> +
> +    firmware_end_addr = riscv_find_and_load_firmware(machine, BIOS_FILENAME,
> +                                                     start_addr, NULL);
> +    if (machine->kernel_filename) {
> +        kernel_start_addr = riscv_calc_kernel_start_addr(&bs->soc.cpus,
> +                                                         firmware_end_addr);
> +
> +        kernel_entry = riscv_load_kernel(machine->kernel_filename,
> +                                         kernel_start_addr, NULL);
> +
> +        if (machine->initrd_filename) {
> +            hwaddr start;
> +            hwaddr end = riscv_load_initrd(machine->initrd_filename,
> +                                           machine->ram_size, kernel_entry,
> +                                           &start);
> +            qemu_fdt_setprop_cell(bs->fdt, "/chosen",
> +                                  "linux,initrd-start", start);
> +            qemu_fdt_setprop_cell(bs->fdt, "/chosen", "linux,initrd-end",
> +                                  end);
> +        }
> +    } else {
> +       /*
> +        * If dynamic firmware is used, it doesn't know where is the next mode
> +        * if kernel argument is not set.
> +        */
> +        kernel_entry = 0;
> +    }
> +
> +    /* Compute the fdt load address in dram */
> +    fdt_load_addr = riscv_load_fdt(memmap[ANDES_AE350_DRAM].base,
> +                                   machine->ram_size, bs->fdt);
> +
> +    /* load the reset vector */
> +    riscv_setup_rom_reset_vec(machine, &bs->soc.cpus, start_addr,
> +                andes_ae350_memmap[ANDES_AE350_MROM].base,
> +                andes_ae350_memmap[ANDES_AE350_MROM].size, kernel_entry,
> +                fdt_load_addr, bs->fdt);
> +}
> +
> +static void andes_ae350_machine_class_init(ObjectClass *oc, void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "RISC-V Board compatible with Andes AE350";
> +    mc->init = andes_ae350_machine_init;
> +    mc->max_cpus = ANDES_CPUS_MAX;
> +    mc->default_cpu_type = VIRT_CPU;
> +}
> +
> +static void andes_ae350_machine_instance_init(Object *obj)
> +{
> +
> +}
> +
> +static const TypeInfo andes_ae350_machine_typeinfo = {
> +    .name       = MACHINE_TYPE_NAME("andes_ae350"),
> +    .parent     = TYPE_MACHINE,
> +    .class_init = andes_ae350_machine_class_init,
> +    .instance_init = andes_ae350_machine_instance_init,
> +    .instance_size = sizeof(AndesAe350BoardState),
> +};
> +
> +static void andes_ae350_machine_init_register_types(void)
> +{
> +    type_register_static(&andes_ae350_machine_typeinfo);
> +}
> +
> +type_init(andes_ae350_machine_init_register_types)
> +
> +static void andes_ae350_soc_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    dc->realize = andes_ae350_soc_realize;
> +    dc->user_creatable = false;
> +}
> +
> +static const TypeInfo andes_ae350_soc_type_info = {
> +    .name       = TYPE_ANDES_AE350_SOC,
> +    .parent     = TYPE_DEVICE,
> +    .instance_init = andes_ae350_soc_instance_init,
> +    .instance_size = sizeof(AndesAe350SocState),
> +    .class_init = andes_ae350_soc_class_init,
> +};
> +
> +static void andes_ae350_soc_init_register_types(void)
> +{
> +    type_register_static(&andes_ae350_soc_type_info);
> +}
> +
> +type_init(andes_ae350_soc_init_register_types)
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index 275c0f7eb7..dc0f2cb98b 100644
> --- a/hw/riscv/meson.build
> +++ b/hw/riscv/meson.build
> @@ -2,6 +2,7 @@ riscv_ss = ss.source_set()
>  riscv_ss.add(files('boot.c'), fdt)
>  riscv_ss.add(files('numa.c'))
>  riscv_ss.add(files('riscv_hart.c'))
> +riscv_ss.add(when: 'CONFIG_ANDES_AE350', if_true: files('andes_ae350.c'))
>  riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c'))
>  riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c'))
>  riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
> diff --git a/include/hw/riscv/andes_ae350.h b/include/hw/riscv/andes_ae350.h
> new file mode 100644
> index 0000000000..fb1a15e8cc
> --- /dev/null
> +++ b/include/hw/riscv/andes_ae350.h
> @@ -0,0 +1,93 @@
> +/*
> + * Andes RISC-V AE350 Board
> + *
> + * Copyright (c) 2021 Andes Tech. Corp.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * 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 HW_RISCV_ANDES_AE350_H
> +#define HW_RISCV_ANDES_AE350_H
> +
> +#include "hw/riscv/riscv_hart.h"
> +#include "hw/sysbus.h"
> +#include "hw/block/flash.h"
> +#include "qom/object.h"
> +
> +#define ANDES_CPUS_MAX 4
> +
> +#define TYPE_ANDES_AE350_SOC "riscv.andes.ae350.soc"
> +#define ANDES_AE350_SOC(obj) \
> +    OBJECT_CHECK(AndesAe350SocState, (obj), TYPE_ANDES_AE350_SOC)
> +
> +typedef struct AndesAe350SocState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    RISCVHartArrayState cpus;
> +    DeviceState *plic;
> +    DeviceState *plic_sw;
> +} AndesAe350SocState;
> +
> +#define TYPE_ANDES_AE350_MACHINE MACHINE_TYPE_NAME("andes_ae350")
> +#define ANDES_AE350_MACHINE(obj) \
> +    OBJECT_CHECK(AndesAe350BoardState, (obj), TYPE_ANDES_AE350_MACHINE)
> +
> +typedef struct AndesAe350BoardState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    AndesAe350SocState soc;
> +    void *fdt;
> +    int fdt_size;
> +} AndesAe350BoardState;
> +
> +enum {
> +    ANDES_AE350_DEBUG,
> +    ANDES_AE350_MROM,
> +    ANDES_AE350_PLMT,
> +    ANDES_AE350_PLICSW,
> +    ANDES_AE350_PLIC,
> +    ANDES_AE350_UART1,
> +    ANDES_AE350_UART2,
> +    ANDES_AE350_DRAM,
> +    ANDES_AE350_GEM,
> +    ANDES_AE350_PIT,
> +    ANDES_AE350_SDC,
> +    ANDES_AE350_MAC,
> +    ANDES_AE350_VIRTIO,
> +};
> +
> +enum {
> +    ANDES_AE350_PIT_IRQ = 3,
> +    ANDES_AE350_UART1_IRQ = 8,
> +    ANDES_AE350_UART2_IRQ = 9,
> +    ANDES_AE350_SDC_IRQ = 18,
> +    ANDES_AE350_MAC_IRQ = 19,
> +    ANDES_AE350_GEM_IRQ = 0x35,
> +    ANDES_AE350_VIRTIO_COUNT = 8,
> +    ANDES_AE350_VIRTIO_IRQ = 16, /* 16 to 23 */
> +};
> +
> +#define ANDES_UART_REG_SHIFT    0x2
> +#define ANDES_UART_REG_OFFSET   0x20
> +
> +#if defined(TARGET_RISCV32)
> +#define VIRT_CPU TYPE_RISCV_CPU_BASE32
> +#elif defined(TARGET_RISCV64)
> +#define VIRT_CPU TYPE_RISCV_CPU_BASE64
> +#endif
> +
> +#endif /* HW_RISCV_ANDES_AE350_H */
> --

Please add a target guide in docs/system/riscv for this new board.
Does it support running upstream U-Boot?

Regards,
Bin
Dylan Jhong March 11, 2021, 6:50 a.m. UTC | #2
On Wed, Mar 10, 2021 at 02:15:25PM +0800, Bin Meng wrote:
> On Wed, Mar 10, 2021 at 11:36 AM Dylan Jhong <dylan@andestech.com> wrote:
> >
> > This provides a RISC-V Board based on Andes's AE350 specification.
> > The following machine is implemented:
> >
> > - 'andes_ae350'; PLIC, PLICSW, PLMT, 16550a UART, VirtIO MMIO, device-tree
> 
> Is this a virtual target because virtio is added? Or does the hardware
> provide the virtio programming interface?

Andes ae350 is an FPGA evaluation board with many Andes's peripheral devices,
but we only provide the most basic functions in the qemu version of ae350.
Because we hope that customers can quickly develop and evaluate before getting the real ae350 board,
so we use virtio to replace some peripheral devices.

> 
> >
> > Signed-off-by: Dylan Jhong <dylan@andestech.com>
> > Signed-off-by: Ruinland ChuanTzu Tsai <ruinland@andestech.com>
> > ---
> >  default-configs/devices/riscv32-softmmu.mak |   1 +
> >  default-configs/devices/riscv64-softmmu.mak |   1 +
> >  hw/riscv/Kconfig                            |   7 +
> >  hw/riscv/andes_ae350.c                      | 501 ++++++++++++++++++++
> >  hw/riscv/meson.build                        |   1 +
> >  include/hw/riscv/andes_ae350.h              |  93 ++++
> >  6 files changed, 604 insertions(+)
> >  create mode 100644 hw/riscv/andes_ae350.c
> >  create mode 100644 include/hw/riscv/andes_ae350.h
> >
> > diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
> > index d847bd5692..a268007e72 100644
> > --- a/default-configs/devices/riscv32-softmmu.mak
> > +++ b/default-configs/devices/riscv32-softmmu.mak
> > @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> >
> >  # Boards:
> >  #
> > +CONFIG_ANDES_AE350=y
> >  CONFIG_SPIKE=y
> >  CONFIG_SIFIVE_E=y
> >  CONFIG_SIFIVE_U=y
> > diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
> > index d5eec75f05..9a37dfd8c0 100644
> > --- a/default-configs/devices/riscv64-softmmu.mak
> > +++ b/default-configs/devices/riscv64-softmmu.mak
> > @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> >
> >  # Boards:
> >  #
> > +CONFIG_ANDES_AE350=y
> >  CONFIG_SPIKE=y
> >  CONFIG_SIFIVE_E=y
> >  CONFIG_SIFIVE_U=y
> > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> > index d139074b02..04f6369ab7 100644
> > --- a/hw/riscv/Kconfig
> > +++ b/hw/riscv/Kconfig
> > @@ -1,6 +1,13 @@
> >  config IBEX
> >      bool
> >
> > +config ANDES_AE350
> 
> This needs to be sorted in alphabetical order
>

Thanks, this will be fixed in V2.

> > +    bool
> > +    select SERIAL
> > +    select VIRTIO_MMIO
> > +    select ANDES_PLIC
> > +    select ANDES_PLMT
> > +
> >  config MICROCHIP_PFSOC
> >      bool
> >      select CADENCE_SDHCI
> > diff --git a/hw/riscv/andes_ae350.c b/hw/riscv/andes_ae350.c
> > new file mode 100644
> > index 0000000000..ed5f9701ad
> > --- /dev/null
> > +++ b/hw/riscv/andes_ae350.c
> > @@ -0,0 +1,501 @@
> > +/*
> > + * Andes RISC-V AE350 Board
> > + *
> > + * Copyright (c) 2021 Andes Tech. Corp.
> > + *
> > + * Andes AE350 Board supports ns16550a UART and VirtIO MMIO.
> > + * The interrupt controllers are andes PLIC and andes PLICSW.
> > + * Timer is Andes PLMT.
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * 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 "qemu/units.h"
> > +#include "qemu/log.h"
> > +#include "qemu/error-report.h"
> > +#include "qapi/error.h"
> > +#include "hw/boards.h"
> > +#include "hw/loader.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/qdev-properties.h"
> > +#include "hw/char/serial.h"
> > +#include "target/riscv/cpu.h"
> > +#include "hw/riscv/riscv_hart.h"
> > +#include "hw/riscv/boot.h"
> > +#include "hw/riscv/numa.h"
> > +#include "chardev/char.h"
> > +#include "sysemu/arch_init.h"
> > +#include "sysemu/device_tree.h"
> > +#include "sysemu/sysemu.h"
> > +#include "hw/pci/pci.h"
> > +#include "hw/pci-host/gpex.h"
> > +
> > +#include "hw/intc/andes_plic.h"
> > +#include "hw/timer/andes_plmt.h"
> > +#include "hw/riscv/andes_ae350.h"
> > +
> > +# define BIOS_FILENAME ""
> > +
> > +static const struct MemmapEntry {
> > +    hwaddr base;
> > +    hwaddr size;
> > +} andes_ae350_memmap[] = {
> > +    [ANDES_AE350_DEBUG]     = { 0x00000000,      0x100 },
> > +    [ANDES_AE350_DRAM]      = { 0x00000000, 0x80000000 },
> > +    [ANDES_AE350_MROM]      = { 0xb0000000,   0x100000 },
> > +    [ANDES_AE350_MAC]       = { 0xe0100000,   0x100000 },
> > +    [ANDES_AE350_GEM]       = { 0xe0200000,   0x100000 },
> > +    [ANDES_AE350_PLIC]      = { 0xe4000000,   0x400000 },
> > +    [ANDES_AE350_PLMT]      = { 0xe6000000,   0x100000 },
> > +    [ANDES_AE350_PLICSW]    = { 0xe6400000,   0x400000 },
> > +    [ANDES_AE350_UART1]     = { 0xf0200000,      0x100 },
> > +    [ANDES_AE350_UART2]     = { 0xf0300000,      0x100 },
> > +    [ANDES_AE350_PIT]       = { 0xf0400000,   0x100000 },
> > +    [ANDES_AE350_SDC]       = { 0xf0e00000,   0x100000 },
> > +    [ANDES_AE350_VIRTIO]    = { 0xfe000000,     0x1000 },
> > +};
> > +
> > +static void
> > +create_fdt(AndesAe350BoardState *bs, const struct MemmapEntry *memmap,
> > +    uint64_t mem_size, const char *cmdline)
> > +{
> > +    AndesAe350SocState *s = &bs->soc;
> > +    MachineState *ms = MACHINE(qdev_get_machine());
> > +    void *fdt;
> > +    int cpu, i;
> > +    uint64_t mem_addr;
> > +    uint32_t *plic_irq_ext, *plicsw_irq_ext, *plmt_irq_ext;
> > +    unsigned long plic_addr, plicsw_addr, plmt_addr;
> > +    char *plic_name, *plicsw_name, *plmt_name;
> > +    uint32_t intc_phandle = 0, plic_phandle = 0;
> > +    uint32_t phandle = 1;
> > +    char *isa_name, *mem_name, *cpu_name, *intc_name, *uart_name, *virtio_name;
> > +
> > +    if (ms->dtb) {
> > +        fdt = bs->fdt = load_device_tree(ms->dtb, &bs->fdt_size);
> > +        if (!fdt) {
> > +            error_report("load_device_tree() failed");
> > +            exit(1);
> > +        }
> > +        goto update_bootargs;
> > +    } else {
> > +        fdt = bs->fdt = create_device_tree(&bs->fdt_size);
> > +        if (!fdt) {
> > +            error_report("create_device_tree() failed");
> > +            exit(1);
> > +        }
> > +    }
> > +
> > +    qemu_fdt_setprop_string(fdt, "/", "model", "Andes AE350 Board");
> > +    qemu_fdt_setprop_string(fdt, "/", "compatible", "andestech,ae350");
> > +    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
> > +    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
> > +
> > +    qemu_fdt_add_subnode(fdt, "/soc");
> > +    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
> > +    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
> > +    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
> > +    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
> > +
> > +    qemu_fdt_add_subnode(fdt, "/cpus");
> > +    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> > +                          ANDES_PLMT_TIMEBASE_FREQ);
> > +    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> > +    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> > +    qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
> > +
> > +    plic_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 4);
> > +    plicsw_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> > +    plmt_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> > +
> > +    for (cpu = 0; cpu < s->cpus.num_harts; cpu++) {
> > +        intc_phandle = phandle++;
> > +
> > +        cpu_name = g_strdup_printf("/cpus/cpu@%d",
> > +            s->cpus.hartid_base + cpu);
> > +        qemu_fdt_add_subnode(fdt, cpu_name);
> > +#if defined(TARGET_RISCV32)
> > +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32");
> > +#else
> > +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv39");
> > +#endif
> > +        isa_name = riscv_isa_string(&s->cpus.harts[cpu]);
> > +        qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", isa_name);
> > +        g_free(isa_name);
> > +        qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
> > +        qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
> > +        qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
> > +            s->cpus.hartid_base + cpu);
> > +        qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
> > +
> > +        intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
> > +        qemu_fdt_add_subnode(fdt, intc_name);
> > +        qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle);
> > +        qemu_fdt_setprop_string(fdt, intc_name, "compatible",
> > +            "riscv,cpu-intc");
> > +        qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
> > +        qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
> > +
> > +        plic_irq_ext[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
> > +        plic_irq_ext[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
> > +        plic_irq_ext[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
> > +        plic_irq_ext[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
> > +
> > +        plicsw_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> > +        plicsw_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
> > +
> > +        plmt_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> > +        plmt_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
> > +
> > +        g_free(intc_name);
> > +    }
> > +
> > +    mem_addr = memmap[ANDES_AE350_DRAM].base;
> > +    mem_name = g_strdup_printf("/memory@%lx", (long)mem_addr);
> > +    qemu_fdt_add_subnode(fdt, mem_name);
> > +    qemu_fdt_setprop_cells(fdt, mem_name, "reg",
> > +        mem_addr >> 32, mem_addr, mem_size >> 32, mem_size);
> > +    qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
> > +    g_free(mem_name);
> > +
> > +    /* create plic */
> > +    plic_phandle = phandle++;
> > +    plic_addr = memmap[ANDES_AE350_PLIC].base;
> > +    plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
> > +    qemu_fdt_add_subnode(fdt, plic_name);
> > +    qemu_fdt_setprop_cell(fdt, plic_name,
> > +        "#address-cells", 0x2);
> > +    qemu_fdt_setprop_cell(fdt, plic_name,
> > +        "#interrupt-cells", 0x2);
> > +    qemu_fdt_setprop_string(fdt, plic_name, "compatible", "riscv,plic0");
> 
> This suggests PLIC is the same as the SiFive one. So why do we have a
> different implementation of the PLIC model?
> 

The difference of these two PLICs, please refer to my reply of "[PATCH 1/3] Andes RISC-V PLIC"

> > +    qemu_fdt_setprop(fdt, plic_name, "interrupt-controller", NULL, 0);
> > +    qemu_fdt_setprop(fdt, plic_name, "interrupts-extended",
> > +        plic_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 4);
> > +    qemu_fdt_setprop_cells(fdt, plic_name, "reg",
> > +        0x0, plic_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
> > +    qemu_fdt_setprop_cell(fdt, plic_name, "riscv,ndev", 0x47);
> > +    qemu_fdt_setprop_cell(fdt, plic_name, "phandle", plic_phandle);
> > +    g_free(plic_name);
> > +    g_free(plic_irq_ext);
> > +
> > +    /* create plicsw */
> > +    plicsw_addr = memmap[ANDES_AE350_PLICSW].base;
> > +    plicsw_name = g_strdup_printf("/soc/interrupt-controller@%lx", plicsw_addr);
> > +    qemu_fdt_add_subnode(fdt, plicsw_name);
> > +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> > +        "#address-cells", 0x2);
> > +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> > +        "#interrupt-cells", 0x2);
> > +    qemu_fdt_setprop_string(fdt, plicsw_name, "compatible", "riscv,plic1");
> 
> Is this bindings in the Linux kernel upstream? I can't find any
> reference in the kernel tree.
> 

Currently only supports andes Linux BSP,
we have plans to push to linux upstream in the future.

> > +    qemu_fdt_setprop(fdt, plicsw_name, "interrupt-controller", NULL, 0);
> > +    qemu_fdt_setprop(fdt, plicsw_name, "interrupts-extended",
> > +        plicsw_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
> > +    qemu_fdt_setprop_cells(fdt, plicsw_name, "reg",
> > +        0x0, plicsw_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
> > +    qemu_fdt_setprop_cell(fdt, plicsw_name, "riscv,ndev", 0x1);
> > +    g_free(plicsw_name);
> > +    g_free(plicsw_irq_ext);
> > +
> > +    /* create plmt */
> > +    plmt_addr = memmap[ANDES_AE350_PLMT].base;
> > +    plmt_name = g_strdup_printf("/soc/plmt0@%lx", plmt_addr);
> > +    qemu_fdt_add_subnode(fdt, plmt_name);
> > +    qemu_fdt_setprop_string(fdt, plmt_name, "compatible", "riscv,plmt0");
> 
> The same here.
> 
> > +    qemu_fdt_setprop(fdt, plmt_name, "interrupts-extended",
> > +        plmt_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
> > +    qemu_fdt_setprop_cells(fdt, plmt_name, "reg",
> > +        0x0, plmt_addr, 0x0, memmap[ANDES_AE350_PLMT].size);
> > +    g_free(plmt_name);
> > +    g_free(plmt_irq_ext);
> > +
> > +    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART1].base);
> > +    qemu_fdt_add_subnode(fdt, uart_name);
> > +    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
> > +    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
> > +        0x0, memmap[ANDES_AE350_UART1].base,
> > +        0x0, memmap[ANDES_AE350_UART1].size);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
> > +    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART1_IRQ, 0x4);
> > +
> > +    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART2].base);
> > +    qemu_fdt_add_subnode(fdt, uart_name);
> > +    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
> > +    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
> > +        0x0, memmap[ANDES_AE350_UART2].base,
> > +        0x0, memmap[ANDES_AE350_UART2].size);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
> > +    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
> > +    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART2_IRQ, 0x4);
> > +
> > +    qemu_fdt_add_subnode(fdt, "/chosen");
> > +    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
> > +            "console=ttyS0,38400n8 earlycon=sbi debug loglevel=7");
> > +    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", uart_name);
> > +    g_free(uart_name);
> > +
> > +    for (i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
> > +        virtio_name = g_strdup_printf("/virtio_mmio@%lx",
> > +            (memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size));
> > +        qemu_fdt_add_subnode(fdt, virtio_name);
> > +        qemu_fdt_setprop_string(fdt, virtio_name, "compatible", "virtio,mmio");
> > +        qemu_fdt_setprop_cells(fdt, virtio_name, "reg",
> > +            0x0, memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
> > +            0x0, memmap[ANDES_AE350_VIRTIO].size);
> > +        qemu_fdt_setprop_cell(fdt, virtio_name, "interrupt-parent",
> > +                                plic_phandle);
> > +        qemu_fdt_setprop_cells(fdt, virtio_name, "interrupts",
> > +                                ANDES_AE350_VIRTIO_IRQ + i, 0x4);
> > +        g_free(virtio_name);
> > +    }
> > +
> > +update_bootargs:
> > +    if (cmdline && cmdline[0] != '\0') {
> > +        qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
> > +    }
> > +}
> > +
> > +static char *init_hart_config(const char *hart_config, int num_harts)
> > +{
> > +    int length = 0, i = 0;
> > +    char *result;
> > +
> > +    length = (strlen(hart_config) + 1) * num_harts;
> > +    result = g_malloc0(length);
> > +    for (i = 0; i < num_harts; i++) {
> > +        if (i != 0) {
> > +            strncat(result, ",", length);
> > +        }
> > +        strncat(result, hart_config, length);
> > +        length -= (strlen(hart_config) + 1);
> > +    }
> > +
> > +    return result;
> > +}
> > +
> > +static void andes_ae350_soc_realize(DeviceState *dev_soc, Error **errp)
> > +{
> > +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> > +    MachineState *machine = MACHINE(qdev_get_machine());
> > +    MemoryRegion *system_memory = get_system_memory();
> > +    AndesAe350SocState *s = ANDES_AE350_SOC(dev_soc);
> > +    char *plic_hart_config, *plicsw_hart_config;
> > +
> > +    plicsw_hart_config =
> > +        init_hart_config(ANDES_AE350_PLICSW_HART_CONFIG, machine->smp.cpus);
> > +
> > +    /* Per-socket SW-PLIC */
> > +    s->plic_sw = andes_plicsw_create(
> > +        memmap[ANDES_AE350_PLICSW].base,
> > +        ANDES_AE350_PLICSW_NAME,
> > +        plicsw_hart_config,
> > +        ANDES_AE350_PLICSW_NUM_SOURCES,
> > +        ANDES_AE350_PLICSW_NUM_PRIORITIES,
> > +        ANDES_AE350_PLICSW_PRIORITY_BASE,
> > +        ANDES_AE350_PLICSW_PENDING_BASE,
> > +        ANDES_AE350_PLICSW_ENABLE_BASE,
> > +        ANDES_AE350_PLICSW_ENABLE_STRIDE,
> > +        ANDES_AE350_PLICSW_THRESHOLD_BASE,
> > +        ANDES_AE350_PLICSW_THRESHOLD_STRIDE,
> > +        memmap[ANDES_AE350_PLICSW].size);
> > +
> > +    g_free(plicsw_hart_config);
> > +
> > +    andes_plmt_create(memmap[ANDES_AE350_PLMT].base,
> > +                memmap[ANDES_AE350_PLMT].size,
> > +                machine->smp.cpus, ANDES_PLMT_TIME_BASE, ANDES_PLMT_TIMECMP_BASE);
> > +
> > +    plic_hart_config =
> > +        init_hart_config(ANDES_AE350_PLIC_HART_CONFIG, machine->smp.cpus);
> > +
> > +    /* Per-socket PLIC */
> > +    s->plic = andes_plic_create(
> > +        memmap[ANDES_AE350_PLIC].base,
> > +        ANDES_AE350_PLIC_NAME,
> > +        plic_hart_config,
> > +        ANDES_AE350_PLIC_NUM_SOURCES,
> > +        ANDES_AE350_PLIC_NUM_PRIORITIES,
> > +        ANDES_AE350_PLIC_PRIORITY_BASE,
> > +        ANDES_AE350_PLIC_PENDING_BASE,
> > +        ANDES_AE350_PLIC_ENABLE_BASE,
> > +        ANDES_AE350_PLIC_ENABLE_STRIDE,
> > +        ANDES_AE350_PLIC_THRESHOLD_BASE,
> > +        ANDES_AE350_PLIC_THRESHOLD_STRIDE,
> > +        memmap[ANDES_AE350_PLIC].size);
> > +
> > +    g_free(plic_hart_config);
> > +
> > +    /* VIRTIO */
> > +    for (int i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
> > +        sysbus_create_simple("virtio-mmio",
> > +            memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
> > +            qdev_get_gpio_in(DEVICE(s->plic), (ANDES_AE350_VIRTIO_IRQ + i)));
> > +    }
> > +
> > +    serial_mm_init(system_memory,
> > +        memmap[ANDES_AE350_UART1].base + ANDES_UART_REG_OFFSET,
> > +        ANDES_UART_REG_SHIFT,
> > +        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART1_IRQ),
> > +        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
> > +
> > +    serial_mm_init(system_memory,
> > +        memmap[ANDES_AE350_UART2].base + ANDES_UART_REG_OFFSET,
> > +        ANDES_UART_REG_SHIFT,
> > +        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART2_IRQ),
> > +        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
> > +}
> > +
> > +static void andes_ae350_soc_instance_init(Object *obj)
> > +{
> > +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> > +    MachineState *machine = MACHINE(qdev_get_machine());
> > +    AndesAe350SocState *s = ANDES_AE350_SOC(obj);
> > +
> > +    object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
> > +    object_property_set_str(OBJECT(&s->cpus), "cpu-type",
> > +                            machine->cpu_type, &error_abort);
> > +    object_property_set_int(OBJECT(&s->cpus), "num-harts",
> > +                            machine->smp.cpus, &error_abort);
> > +    qdev_prop_set_uint64(DEVICE(&s->cpus), "resetvec",
> > +                            memmap[ANDES_AE350_MROM].base);
> > +    sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);
> > +}
> > +
> > +static void andes_ae350_machine_init(MachineState *machine)
> > +{
> > +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> > +
> > +    AndesAe350BoardState *bs = ANDES_AE350_MACHINE(machine);
> > +    MemoryRegion *system_memory = get_system_memory();
> > +    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
> > +    MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
> > +    target_ulong start_addr = memmap[ANDES_AE350_DRAM].base;
> > +    target_ulong firmware_end_addr, kernel_start_addr;
> > +    uint32_t fdt_load_addr;
> > +    uint64_t kernel_entry;
> > +
> > +    /* Initialize SoC */
> > +    object_initialize_child(OBJECT(machine), "soc",
> > +                    &bs->soc, TYPE_ANDES_AE350_SOC);
> > +    qdev_realize(DEVICE(&bs->soc), NULL, &error_abort);
> > +
> > +    /* register system main memory (actual RAM) */
> > +    memory_region_init_ram(main_mem, NULL, "riscv.andes.ae350.ram",
> > +                           machine->ram_size, &error_fatal);
> > +    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_DRAM].base,
> > +        main_mem);
> > +
> > +    /* create device tree */
> > +    create_fdt(bs, memmap, machine->ram_size, machine->kernel_cmdline);
> > +
> > +    /* boot rom */
> > +    memory_region_init_rom(mask_rom, NULL, "riscv.andes.ae350.mrom",
> > +                           memmap[ANDES_AE350_MROM].size, &error_fatal);
> > +    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_MROM].base,
> > +                                mask_rom);
> > +
> > +    firmware_end_addr = riscv_find_and_load_firmware(machine, BIOS_FILENAME,
> > +                                                     start_addr, NULL);
> > +    if (machine->kernel_filename) {
> > +        kernel_start_addr = riscv_calc_kernel_start_addr(&bs->soc.cpus,
> > +                                                         firmware_end_addr);
> > +
> > +        kernel_entry = riscv_load_kernel(machine->kernel_filename,
> > +                                         kernel_start_addr, NULL);
> > +
> > +        if (machine->initrd_filename) {
> > +            hwaddr start;
> > +            hwaddr end = riscv_load_initrd(machine->initrd_filename,
> > +                                           machine->ram_size, kernel_entry,
> > +                                           &start);
> > +            qemu_fdt_setprop_cell(bs->fdt, "/chosen",
> > +                                  "linux,initrd-start", start);
> > +            qemu_fdt_setprop_cell(bs->fdt, "/chosen", "linux,initrd-end",
> > +                                  end);
> > +        }
> > +    } else {
> > +       /*
> > +        * If dynamic firmware is used, it doesn't know where is the next mode
> > +        * if kernel argument is not set.
> > +        */
> > +        kernel_entry = 0;
> > +    }
> > +
> > +    /* Compute the fdt load address in dram */
> > +    fdt_load_addr = riscv_load_fdt(memmap[ANDES_AE350_DRAM].base,
> > +                                   machine->ram_size, bs->fdt);
> > +
> > +    /* load the reset vector */
> > +    riscv_setup_rom_reset_vec(machine, &bs->soc.cpus, start_addr,
> > +                andes_ae350_memmap[ANDES_AE350_MROM].base,
> > +                andes_ae350_memmap[ANDES_AE350_MROM].size, kernel_entry,
> > +                fdt_load_addr, bs->fdt);
> > +}
> > +
> > +static void andes_ae350_machine_class_init(ObjectClass *oc, void *data)
> > +{
> > +    MachineClass *mc = MACHINE_CLASS(oc);
> > +
> > +    mc->desc = "RISC-V Board compatible with Andes AE350";
> > +    mc->init = andes_ae350_machine_init;
> > +    mc->max_cpus = ANDES_CPUS_MAX;
> > +    mc->default_cpu_type = VIRT_CPU;
> > +}
> > +
> > +static void andes_ae350_machine_instance_init(Object *obj)
> > +{
> > +
> > +}
> > +
> > +static const TypeInfo andes_ae350_machine_typeinfo = {
> > +    .name       = MACHINE_TYPE_NAME("andes_ae350"),
> > +    .parent     = TYPE_MACHINE,
> > +    .class_init = andes_ae350_machine_class_init,
> > +    .instance_init = andes_ae350_machine_instance_init,
> > +    .instance_size = sizeof(AndesAe350BoardState),
> > +};
> > +
> > +static void andes_ae350_machine_init_register_types(void)
> > +{
> > +    type_register_static(&andes_ae350_machine_typeinfo);
> > +}
> > +
> > +type_init(andes_ae350_machine_init_register_types)
> > +
> > +static void andes_ae350_soc_class_init(ObjectClass *oc, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(oc);
> > +
> > +    dc->realize = andes_ae350_soc_realize;
> > +    dc->user_creatable = false;
> > +}
> > +
> > +static const TypeInfo andes_ae350_soc_type_info = {
> > +    .name       = TYPE_ANDES_AE350_SOC,
> > +    .parent     = TYPE_DEVICE,
> > +    .instance_init = andes_ae350_soc_instance_init,
> > +    .instance_size = sizeof(AndesAe350SocState),
> > +    .class_init = andes_ae350_soc_class_init,
> > +};
> > +
> > +static void andes_ae350_soc_init_register_types(void)
> > +{
> > +    type_register_static(&andes_ae350_soc_type_info);
> > +}
> > +
> > +type_init(andes_ae350_soc_init_register_types)
> > diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> > index 275c0f7eb7..dc0f2cb98b 100644
> > --- a/hw/riscv/meson.build
> > +++ b/hw/riscv/meson.build
> > @@ -2,6 +2,7 @@ riscv_ss = ss.source_set()
> >  riscv_ss.add(files('boot.c'), fdt)
> >  riscv_ss.add(files('numa.c'))
> >  riscv_ss.add(files('riscv_hart.c'))
> > +riscv_ss.add(when: 'CONFIG_ANDES_AE350', if_true: files('andes_ae350.c'))
> >  riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c'))
> >  riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c'))
> >  riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
> > diff --git a/include/hw/riscv/andes_ae350.h b/include/hw/riscv/andes_ae350.h
> > new file mode 100644
> > index 0000000000..fb1a15e8cc
> > --- /dev/null
> > +++ b/include/hw/riscv/andes_ae350.h
> > @@ -0,0 +1,93 @@
> > +/*
> > + * Andes RISC-V AE350 Board
> > + *
> > + * Copyright (c) 2021 Andes Tech. Corp.
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > + * more details.
> > + *
> > + * 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 HW_RISCV_ANDES_AE350_H
> > +#define HW_RISCV_ANDES_AE350_H
> > +
> > +#include "hw/riscv/riscv_hart.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/block/flash.h"
> > +#include "qom/object.h"
> > +
> > +#define ANDES_CPUS_MAX 4
> > +
> > +#define TYPE_ANDES_AE350_SOC "riscv.andes.ae350.soc"
> > +#define ANDES_AE350_SOC(obj) \
> > +    OBJECT_CHECK(AndesAe350SocState, (obj), TYPE_ANDES_AE350_SOC)
> > +
> > +typedef struct AndesAe350SocState {
> > +    /*< private >*/
> > +    SysBusDevice parent_obj;
> > +
> > +    /*< public >*/
> > +    RISCVHartArrayState cpus;
> > +    DeviceState *plic;
> > +    DeviceState *plic_sw;
> > +} AndesAe350SocState;
> > +
> > +#define TYPE_ANDES_AE350_MACHINE MACHINE_TYPE_NAME("andes_ae350")
> > +#define ANDES_AE350_MACHINE(obj) \
> > +    OBJECT_CHECK(AndesAe350BoardState, (obj), TYPE_ANDES_AE350_MACHINE)
> > +
> > +typedef struct AndesAe350BoardState {
> > +    /*< private >*/
> > +    SysBusDevice parent_obj;
> > +
> > +    /*< public >*/
> > +    AndesAe350SocState soc;
> > +    void *fdt;
> > +    int fdt_size;
> > +} AndesAe350BoardState;
> > +
> > +enum {
> > +    ANDES_AE350_DEBUG,
> > +    ANDES_AE350_MROM,
> > +    ANDES_AE350_PLMT,
> > +    ANDES_AE350_PLICSW,
> > +    ANDES_AE350_PLIC,
> > +    ANDES_AE350_UART1,
> > +    ANDES_AE350_UART2,
> > +    ANDES_AE350_DRAM,
> > +    ANDES_AE350_GEM,
> > +    ANDES_AE350_PIT,
> > +    ANDES_AE350_SDC,
> > +    ANDES_AE350_MAC,
> > +    ANDES_AE350_VIRTIO,
> > +};
> > +
> > +enum {
> > +    ANDES_AE350_PIT_IRQ = 3,
> > +    ANDES_AE350_UART1_IRQ = 8,
> > +    ANDES_AE350_UART2_IRQ = 9,
> > +    ANDES_AE350_SDC_IRQ = 18,
> > +    ANDES_AE350_MAC_IRQ = 19,
> > +    ANDES_AE350_GEM_IRQ = 0x35,
> > +    ANDES_AE350_VIRTIO_COUNT = 8,
> > +    ANDES_AE350_VIRTIO_IRQ = 16, /* 16 to 23 */
> > +};
> > +
> > +#define ANDES_UART_REG_SHIFT    0x2
> > +#define ANDES_UART_REG_OFFSET   0x20
> > +
> > +#if defined(TARGET_RISCV32)
> > +#define VIRT_CPU TYPE_RISCV_CPU_BASE32
> > +#elif defined(TARGET_RISCV64)
> > +#define VIRT_CPU TYPE_RISCV_CPU_BASE64
> > +#endif
> > +
> > +#endif /* HW_RISCV_ANDES_AE350_H */
> > --
> 
> Please add a target guide in docs/system/riscv for this new board.

OK.

> Does it support running upstream U-Boot?
> 

The status is same as linux upstream.
Will be support later.

> Regards,
> Bin
Alistair Francis March 11, 2021, 3:46 p.m. UTC | #3
On Thu, Mar 11, 2021 at 2:03 AM Dylan Jhong <dylan@andestech.com> wrote:
>
> On Wed, Mar 10, 2021 at 02:15:25PM +0800, Bin Meng wrote:
> > On Wed, Mar 10, 2021 at 11:36 AM Dylan Jhong <dylan@andestech.com> wrote:
> > >
> > > This provides a RISC-V Board based on Andes's AE350 specification.
> > > The following machine is implemented:
> > >
> > > - 'andes_ae350'; PLIC, PLICSW, PLMT, 16550a UART, VirtIO MMIO, device-tree
> >
> > Is this a virtual target because virtio is added? Or does the hardware
> > provide the virtio programming interface?
>
> Andes ae350 is an FPGA evaluation board with many Andes's peripheral devices,
> but we only provide the most basic functions in the qemu version of ae350.
> Because we hope that customers can quickly develop and evaluate before getting the real ae350 board,
> so we use virtio to replace some peripheral devices.

I don't think that's a good idea. The QEMU model should match the
hardware device.

>
> >
> > >
> > > Signed-off-by: Dylan Jhong <dylan@andestech.com>
> > > Signed-off-by: Ruinland ChuanTzu Tsai <ruinland@andestech.com>
> > > ---
> > >  default-configs/devices/riscv32-softmmu.mak |   1 +
> > >  default-configs/devices/riscv64-softmmu.mak |   1 +
> > >  hw/riscv/Kconfig                            |   7 +
> > >  hw/riscv/andes_ae350.c                      | 501 ++++++++++++++++++++
> > >  hw/riscv/meson.build                        |   1 +
> > >  include/hw/riscv/andes_ae350.h              |  93 ++++
> > >  6 files changed, 604 insertions(+)
> > >  create mode 100644 hw/riscv/andes_ae350.c
> > >  create mode 100644 include/hw/riscv/andes_ae350.h
> > >
> > > diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
> > > index d847bd5692..a268007e72 100644
> > > --- a/default-configs/devices/riscv32-softmmu.mak
> > > +++ b/default-configs/devices/riscv32-softmmu.mak
> > > @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> > >
> > >  # Boards:
> > >  #
> > > +CONFIG_ANDES_AE350=y
> > >  CONFIG_SPIKE=y
> > >  CONFIG_SIFIVE_E=y
> > >  CONFIG_SIFIVE_U=y
> > > diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
> > > index d5eec75f05..9a37dfd8c0 100644
> > > --- a/default-configs/devices/riscv64-softmmu.mak
> > > +++ b/default-configs/devices/riscv64-softmmu.mak
> > > @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> > >
> > >  # Boards:
> > >  #
> > > +CONFIG_ANDES_AE350=y
> > >  CONFIG_SPIKE=y
> > >  CONFIG_SIFIVE_E=y
> > >  CONFIG_SIFIVE_U=y
> > > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> > > index d139074b02..04f6369ab7 100644
> > > --- a/hw/riscv/Kconfig
> > > +++ b/hw/riscv/Kconfig
> > > @@ -1,6 +1,13 @@
> > >  config IBEX
> > >      bool
> > >
> > > +config ANDES_AE350
> >
> > This needs to be sorted in alphabetical order
> >
>
> Thanks, this will be fixed in V2.
>
> > > +    bool
> > > +    select SERIAL
> > > +    select VIRTIO_MMIO
> > > +    select ANDES_PLIC
> > > +    select ANDES_PLMT
> > > +
> > >  config MICROCHIP_PFSOC
> > >      bool
> > >      select CADENCE_SDHCI
> > > diff --git a/hw/riscv/andes_ae350.c b/hw/riscv/andes_ae350.c
> > > new file mode 100644
> > > index 0000000000..ed5f9701ad
> > > --- /dev/null
> > > +++ b/hw/riscv/andes_ae350.c
> > > @@ -0,0 +1,501 @@
> > > +/*
> > > + * Andes RISC-V AE350 Board
> > > + *
> > > + * Copyright (c) 2021 Andes Tech. Corp.
> > > + *
> > > + * Andes AE350 Board supports ns16550a UART and VirtIO MMIO.
> > > + * The interrupt controllers are andes PLIC and andes PLICSW.
> > > + * Timer is Andes PLMT.
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms and conditions of the GNU General Public License,
> > > + * version 2 or later, as published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed in the hope it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > > + * more details.
> > > + *
> > > + * 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 "qemu/units.h"
> > > +#include "qemu/log.h"
> > > +#include "qemu/error-report.h"
> > > +#include "qapi/error.h"
> > > +#include "hw/boards.h"
> > > +#include "hw/loader.h"
> > > +#include "hw/sysbus.h"
> > > +#include "hw/qdev-properties.h"
> > > +#include "hw/char/serial.h"
> > > +#include "target/riscv/cpu.h"
> > > +#include "hw/riscv/riscv_hart.h"
> > > +#include "hw/riscv/boot.h"
> > > +#include "hw/riscv/numa.h"
> > > +#include "chardev/char.h"
> > > +#include "sysemu/arch_init.h"
> > > +#include "sysemu/device_tree.h"
> > > +#include "sysemu/sysemu.h"
> > > +#include "hw/pci/pci.h"
> > > +#include "hw/pci-host/gpex.h"
> > > +
> > > +#include "hw/intc/andes_plic.h"
> > > +#include "hw/timer/andes_plmt.h"
> > > +#include "hw/riscv/andes_ae350.h"
> > > +
> > > +# define BIOS_FILENAME ""
> > > +
> > > +static const struct MemmapEntry {
> > > +    hwaddr base;
> > > +    hwaddr size;
> > > +} andes_ae350_memmap[] = {
> > > +    [ANDES_AE350_DEBUG]     = { 0x00000000,      0x100 },
> > > +    [ANDES_AE350_DRAM]      = { 0x00000000, 0x80000000 },
> > > +    [ANDES_AE350_MROM]      = { 0xb0000000,   0x100000 },
> > > +    [ANDES_AE350_MAC]       = { 0xe0100000,   0x100000 },
> > > +    [ANDES_AE350_GEM]       = { 0xe0200000,   0x100000 },
> > > +    [ANDES_AE350_PLIC]      = { 0xe4000000,   0x400000 },
> > > +    [ANDES_AE350_PLMT]      = { 0xe6000000,   0x100000 },
> > > +    [ANDES_AE350_PLICSW]    = { 0xe6400000,   0x400000 },
> > > +    [ANDES_AE350_UART1]     = { 0xf0200000,      0x100 },
> > > +    [ANDES_AE350_UART2]     = { 0xf0300000,      0x100 },
> > > +    [ANDES_AE350_PIT]       = { 0xf0400000,   0x100000 },
> > > +    [ANDES_AE350_SDC]       = { 0xf0e00000,   0x100000 },
> > > +    [ANDES_AE350_VIRTIO]    = { 0xfe000000,     0x1000 },
> > > +};
> > > +
> > > +static void
> > > +create_fdt(AndesAe350BoardState *bs, const struct MemmapEntry *memmap,
> > > +    uint64_t mem_size, const char *cmdline)
> > > +{
> > > +    AndesAe350SocState *s = &bs->soc;
> > > +    MachineState *ms = MACHINE(qdev_get_machine());
> > > +    void *fdt;
> > > +    int cpu, i;
> > > +    uint64_t mem_addr;
> > > +    uint32_t *plic_irq_ext, *plicsw_irq_ext, *plmt_irq_ext;
> > > +    unsigned long plic_addr, plicsw_addr, plmt_addr;
> > > +    char *plic_name, *plicsw_name, *plmt_name;
> > > +    uint32_t intc_phandle = 0, plic_phandle = 0;
> > > +    uint32_t phandle = 1;
> > > +    char *isa_name, *mem_name, *cpu_name, *intc_name, *uart_name, *virtio_name;
> > > +
> > > +    if (ms->dtb) {
> > > +        fdt = bs->fdt = load_device_tree(ms->dtb, &bs->fdt_size);
> > > +        if (!fdt) {
> > > +            error_report("load_device_tree() failed");
> > > +            exit(1);
> > > +        }
> > > +        goto update_bootargs;
> > > +    } else {
> > > +        fdt = bs->fdt = create_device_tree(&bs->fdt_size);
> > > +        if (!fdt) {
> > > +            error_report("create_device_tree() failed");
> > > +            exit(1);
> > > +        }
> > > +    }
> > > +
> > > +    qemu_fdt_setprop_string(fdt, "/", "model", "Andes AE350 Board");
> > > +    qemu_fdt_setprop_string(fdt, "/", "compatible", "andestech,ae350");
> > > +    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
> > > +    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
> > > +
> > > +    qemu_fdt_add_subnode(fdt, "/soc");
> > > +    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
> > > +    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
> > > +    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
> > > +    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
> > > +
> > > +    qemu_fdt_add_subnode(fdt, "/cpus");
> > > +    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> > > +                          ANDES_PLMT_TIMEBASE_FREQ);
> > > +    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> > > +    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> > > +    qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
> > > +
> > > +    plic_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 4);
> > > +    plicsw_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> > > +    plmt_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> > > +
> > > +    for (cpu = 0; cpu < s->cpus.num_harts; cpu++) {
> > > +        intc_phandle = phandle++;
> > > +
> > > +        cpu_name = g_strdup_printf("/cpus/cpu@%d",
> > > +            s->cpus.hartid_base + cpu);
> > > +        qemu_fdt_add_subnode(fdt, cpu_name);
> > > +#if defined(TARGET_RISCV32)
> > > +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32");
> > > +#else
> > > +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv39");
> > > +#endif
> > > +        isa_name = riscv_isa_string(&s->cpus.harts[cpu]);
> > > +        qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", isa_name);
> > > +        g_free(isa_name);
> > > +        qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
> > > +        qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
> > > +        qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
> > > +            s->cpus.hartid_base + cpu);
> > > +        qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
> > > +
> > > +        intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
> > > +        qemu_fdt_add_subnode(fdt, intc_name);
> > > +        qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle);
> > > +        qemu_fdt_setprop_string(fdt, intc_name, "compatible",
> > > +            "riscv,cpu-intc");
> > > +        qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
> > > +        qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
> > > +
> > > +        plic_irq_ext[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
> > > +        plic_irq_ext[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
> > > +        plic_irq_ext[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
> > > +        plic_irq_ext[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
> > > +
> > > +        plicsw_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> > > +        plicsw_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
> > > +
> > > +        plmt_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> > > +        plmt_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
> > > +
> > > +        g_free(intc_name);
> > > +    }
> > > +
> > > +    mem_addr = memmap[ANDES_AE350_DRAM].base;
> > > +    mem_name = g_strdup_printf("/memory@%lx", (long)mem_addr);
> > > +    qemu_fdt_add_subnode(fdt, mem_name);
> > > +    qemu_fdt_setprop_cells(fdt, mem_name, "reg",
> > > +        mem_addr >> 32, mem_addr, mem_size >> 32, mem_size);
> > > +    qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
> > > +    g_free(mem_name);
> > > +
> > > +    /* create plic */
> > > +    plic_phandle = phandle++;
> > > +    plic_addr = memmap[ANDES_AE350_PLIC].base;
> > > +    plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
> > > +    qemu_fdt_add_subnode(fdt, plic_name);
> > > +    qemu_fdt_setprop_cell(fdt, plic_name,
> > > +        "#address-cells", 0x2);
> > > +    qemu_fdt_setprop_cell(fdt, plic_name,
> > > +        "#interrupt-cells", 0x2);
> > > +    qemu_fdt_setprop_string(fdt, plic_name, "compatible", "riscv,plic0");
> >
> > This suggests PLIC is the same as the SiFive one. So why do we have a
> > different implementation of the PLIC model?
> >
>
> The difference of these two PLICs, please refer to my reply of "[PATCH 1/3] Andes RISC-V PLIC"
>
> > > +    qemu_fdt_setprop(fdt, plic_name, "interrupt-controller", NULL, 0);
> > > +    qemu_fdt_setprop(fdt, plic_name, "interrupts-extended",
> > > +        plic_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 4);
> > > +    qemu_fdt_setprop_cells(fdt, plic_name, "reg",
> > > +        0x0, plic_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
> > > +    qemu_fdt_setprop_cell(fdt, plic_name, "riscv,ndev", 0x47);
> > > +    qemu_fdt_setprop_cell(fdt, plic_name, "phandle", plic_phandle);
> > > +    g_free(plic_name);
> > > +    g_free(plic_irq_ext);
> > > +
> > > +    /* create plicsw */
> > > +    plicsw_addr = memmap[ANDES_AE350_PLICSW].base;
> > > +    plicsw_name = g_strdup_printf("/soc/interrupt-controller@%lx", plicsw_addr);
> > > +    qemu_fdt_add_subnode(fdt, plicsw_name);
> > > +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> > > +        "#address-cells", 0x2);
> > > +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> > > +        "#interrupt-cells", 0x2);
> > > +    qemu_fdt_setprop_string(fdt, plicsw_name, "compatible", "riscv,plic1");
> >
> > Is this bindings in the Linux kernel upstream? I can't find any
> > reference in the kernel tree.
> >
>
> Currently only supports andes Linux BSP,
> we have plans to push to linux upstream in the future.

This raises a good question about device tree bindings being generated
from QEMU.

Do we want QEMU to generate a DT that doesn't have matching bindings?

I guess if the bindings change during the upstream process we can just
add a second compatible string.

Also if there is no upstream support how can I test the board?

Alistair

>
> > > +    qemu_fdt_setprop(fdt, plicsw_name, "interrupt-controller", NULL, 0);
> > > +    qemu_fdt_setprop(fdt, plicsw_name, "interrupts-extended",
> > > +        plicsw_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
> > > +    qemu_fdt_setprop_cells(fdt, plicsw_name, "reg",
> > > +        0x0, plicsw_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
> > > +    qemu_fdt_setprop_cell(fdt, plicsw_name, "riscv,ndev", 0x1);
> > > +    g_free(plicsw_name);
> > > +    g_free(plicsw_irq_ext);
> > > +
> > > +    /* create plmt */
> > > +    plmt_addr = memmap[ANDES_AE350_PLMT].base;
> > > +    plmt_name = g_strdup_printf("/soc/plmt0@%lx", plmt_addr);
> > > +    qemu_fdt_add_subnode(fdt, plmt_name);
> > > +    qemu_fdt_setprop_string(fdt, plmt_name, "compatible", "riscv,plmt0");
> >
> > The same here.
> >
> > > +    qemu_fdt_setprop(fdt, plmt_name, "interrupts-extended",
> > > +        plmt_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
> > > +    qemu_fdt_setprop_cells(fdt, plmt_name, "reg",
> > > +        0x0, plmt_addr, 0x0, memmap[ANDES_AE350_PLMT].size);
> > > +    g_free(plmt_name);
> > > +    g_free(plmt_irq_ext);
> > > +
> > > +    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART1].base);
> > > +    qemu_fdt_add_subnode(fdt, uart_name);
> > > +    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
> > > +    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
> > > +        0x0, memmap[ANDES_AE350_UART1].base,
> > > +        0x0, memmap[ANDES_AE350_UART1].size);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
> > > +    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART1_IRQ, 0x4);
> > > +
> > > +    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART2].base);
> > > +    qemu_fdt_add_subnode(fdt, uart_name);
> > > +    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
> > > +    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
> > > +        0x0, memmap[ANDES_AE350_UART2].base,
> > > +        0x0, memmap[ANDES_AE350_UART2].size);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
> > > +    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
> > > +    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART2_IRQ, 0x4);
> > > +
> > > +    qemu_fdt_add_subnode(fdt, "/chosen");
> > > +    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
> > > +            "console=ttyS0,38400n8 earlycon=sbi debug loglevel=7");
> > > +    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", uart_name);
> > > +    g_free(uart_name);
> > > +
> > > +    for (i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
> > > +        virtio_name = g_strdup_printf("/virtio_mmio@%lx",
> > > +            (memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size));
> > > +        qemu_fdt_add_subnode(fdt, virtio_name);
> > > +        qemu_fdt_setprop_string(fdt, virtio_name, "compatible", "virtio,mmio");
> > > +        qemu_fdt_setprop_cells(fdt, virtio_name, "reg",
> > > +            0x0, memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
> > > +            0x0, memmap[ANDES_AE350_VIRTIO].size);
> > > +        qemu_fdt_setprop_cell(fdt, virtio_name, "interrupt-parent",
> > > +                                plic_phandle);
> > > +        qemu_fdt_setprop_cells(fdt, virtio_name, "interrupts",
> > > +                                ANDES_AE350_VIRTIO_IRQ + i, 0x4);
> > > +        g_free(virtio_name);
> > > +    }
> > > +
> > > +update_bootargs:
> > > +    if (cmdline && cmdline[0] != '\0') {
> > > +        qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
> > > +    }
> > > +}
> > > +
> > > +static char *init_hart_config(const char *hart_config, int num_harts)
> > > +{
> > > +    int length = 0, i = 0;
> > > +    char *result;
> > > +
> > > +    length = (strlen(hart_config) + 1) * num_harts;
> > > +    result = g_malloc0(length);
> > > +    for (i = 0; i < num_harts; i++) {
> > > +        if (i != 0) {
> > > +            strncat(result, ",", length);
> > > +        }
> > > +        strncat(result, hart_config, length);
> > > +        length -= (strlen(hart_config) + 1);
> > > +    }
> > > +
> > > +    return result;
> > > +}
> > > +
> > > +static void andes_ae350_soc_realize(DeviceState *dev_soc, Error **errp)
> > > +{
> > > +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> > > +    MachineState *machine = MACHINE(qdev_get_machine());
> > > +    MemoryRegion *system_memory = get_system_memory();
> > > +    AndesAe350SocState *s = ANDES_AE350_SOC(dev_soc);
> > > +    char *plic_hart_config, *plicsw_hart_config;
> > > +
> > > +    plicsw_hart_config =
> > > +        init_hart_config(ANDES_AE350_PLICSW_HART_CONFIG, machine->smp.cpus);
> > > +
> > > +    /* Per-socket SW-PLIC */
> > > +    s->plic_sw = andes_plicsw_create(
> > > +        memmap[ANDES_AE350_PLICSW].base,
> > > +        ANDES_AE350_PLICSW_NAME,
> > > +        plicsw_hart_config,
> > > +        ANDES_AE350_PLICSW_NUM_SOURCES,
> > > +        ANDES_AE350_PLICSW_NUM_PRIORITIES,
> > > +        ANDES_AE350_PLICSW_PRIORITY_BASE,
> > > +        ANDES_AE350_PLICSW_PENDING_BASE,
> > > +        ANDES_AE350_PLICSW_ENABLE_BASE,
> > > +        ANDES_AE350_PLICSW_ENABLE_STRIDE,
> > > +        ANDES_AE350_PLICSW_THRESHOLD_BASE,
> > > +        ANDES_AE350_PLICSW_THRESHOLD_STRIDE,
> > > +        memmap[ANDES_AE350_PLICSW].size);
> > > +
> > > +    g_free(plicsw_hart_config);
> > > +
> > > +    andes_plmt_create(memmap[ANDES_AE350_PLMT].base,
> > > +                memmap[ANDES_AE350_PLMT].size,
> > > +                machine->smp.cpus, ANDES_PLMT_TIME_BASE, ANDES_PLMT_TIMECMP_BASE);
> > > +
> > > +    plic_hart_config =
> > > +        init_hart_config(ANDES_AE350_PLIC_HART_CONFIG, machine->smp.cpus);
> > > +
> > > +    /* Per-socket PLIC */
> > > +    s->plic = andes_plic_create(
> > > +        memmap[ANDES_AE350_PLIC].base,
> > > +        ANDES_AE350_PLIC_NAME,
> > > +        plic_hart_config,
> > > +        ANDES_AE350_PLIC_NUM_SOURCES,
> > > +        ANDES_AE350_PLIC_NUM_PRIORITIES,
> > > +        ANDES_AE350_PLIC_PRIORITY_BASE,
> > > +        ANDES_AE350_PLIC_PENDING_BASE,
> > > +        ANDES_AE350_PLIC_ENABLE_BASE,
> > > +        ANDES_AE350_PLIC_ENABLE_STRIDE,
> > > +        ANDES_AE350_PLIC_THRESHOLD_BASE,
> > > +        ANDES_AE350_PLIC_THRESHOLD_STRIDE,
> > > +        memmap[ANDES_AE350_PLIC].size);
> > > +
> > > +    g_free(plic_hart_config);
> > > +
> > > +    /* VIRTIO */
> > > +    for (int i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
> > > +        sysbus_create_simple("virtio-mmio",
> > > +            memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
> > > +            qdev_get_gpio_in(DEVICE(s->plic), (ANDES_AE350_VIRTIO_IRQ + i)));
> > > +    }
> > > +
> > > +    serial_mm_init(system_memory,
> > > +        memmap[ANDES_AE350_UART1].base + ANDES_UART_REG_OFFSET,
> > > +        ANDES_UART_REG_SHIFT,
> > > +        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART1_IRQ),
> > > +        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
> > > +
> > > +    serial_mm_init(system_memory,
> > > +        memmap[ANDES_AE350_UART2].base + ANDES_UART_REG_OFFSET,
> > > +        ANDES_UART_REG_SHIFT,
> > > +        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART2_IRQ),
> > > +        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
> > > +}
> > > +
> > > +static void andes_ae350_soc_instance_init(Object *obj)
> > > +{
> > > +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> > > +    MachineState *machine = MACHINE(qdev_get_machine());
> > > +    AndesAe350SocState *s = ANDES_AE350_SOC(obj);
> > > +
> > > +    object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
> > > +    object_property_set_str(OBJECT(&s->cpus), "cpu-type",
> > > +                            machine->cpu_type, &error_abort);
> > > +    object_property_set_int(OBJECT(&s->cpus), "num-harts",
> > > +                            machine->smp.cpus, &error_abort);
> > > +    qdev_prop_set_uint64(DEVICE(&s->cpus), "resetvec",
> > > +                            memmap[ANDES_AE350_MROM].base);
> > > +    sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);
> > > +}
> > > +
> > > +static void andes_ae350_machine_init(MachineState *machine)
> > > +{
> > > +    const struct MemmapEntry *memmap = andes_ae350_memmap;
> > > +
> > > +    AndesAe350BoardState *bs = ANDES_AE350_MACHINE(machine);
> > > +    MemoryRegion *system_memory = get_system_memory();
> > > +    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
> > > +    MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
> > > +    target_ulong start_addr = memmap[ANDES_AE350_DRAM].base;
> > > +    target_ulong firmware_end_addr, kernel_start_addr;
> > > +    uint32_t fdt_load_addr;
> > > +    uint64_t kernel_entry;
> > > +
> > > +    /* Initialize SoC */
> > > +    object_initialize_child(OBJECT(machine), "soc",
> > > +                    &bs->soc, TYPE_ANDES_AE350_SOC);
> > > +    qdev_realize(DEVICE(&bs->soc), NULL, &error_abort);
> > > +
> > > +    /* register system main memory (actual RAM) */
> > > +    memory_region_init_ram(main_mem, NULL, "riscv.andes.ae350.ram",
> > > +                           machine->ram_size, &error_fatal);
> > > +    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_DRAM].base,
> > > +        main_mem);
> > > +
> > > +    /* create device tree */
> > > +    create_fdt(bs, memmap, machine->ram_size, machine->kernel_cmdline);
> > > +
> > > +    /* boot rom */
> > > +    memory_region_init_rom(mask_rom, NULL, "riscv.andes.ae350.mrom",
> > > +                           memmap[ANDES_AE350_MROM].size, &error_fatal);
> > > +    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_MROM].base,
> > > +                                mask_rom);
> > > +
> > > +    firmware_end_addr = riscv_find_and_load_firmware(machine, BIOS_FILENAME,
> > > +                                                     start_addr, NULL);
> > > +    if (machine->kernel_filename) {
> > > +        kernel_start_addr = riscv_calc_kernel_start_addr(&bs->soc.cpus,
> > > +                                                         firmware_end_addr);
> > > +
> > > +        kernel_entry = riscv_load_kernel(machine->kernel_filename,
> > > +                                         kernel_start_addr, NULL);
> > > +
> > > +        if (machine->initrd_filename) {
> > > +            hwaddr start;
> > > +            hwaddr end = riscv_load_initrd(machine->initrd_filename,
> > > +                                           machine->ram_size, kernel_entry,
> > > +                                           &start);
> > > +            qemu_fdt_setprop_cell(bs->fdt, "/chosen",
> > > +                                  "linux,initrd-start", start);
> > > +            qemu_fdt_setprop_cell(bs->fdt, "/chosen", "linux,initrd-end",
> > > +                                  end);
> > > +        }
> > > +    } else {
> > > +       /*
> > > +        * If dynamic firmware is used, it doesn't know where is the next mode
> > > +        * if kernel argument is not set.
> > > +        */
> > > +        kernel_entry = 0;
> > > +    }
> > > +
> > > +    /* Compute the fdt load address in dram */
> > > +    fdt_load_addr = riscv_load_fdt(memmap[ANDES_AE350_DRAM].base,
> > > +                                   machine->ram_size, bs->fdt);
> > > +
> > > +    /* load the reset vector */
> > > +    riscv_setup_rom_reset_vec(machine, &bs->soc.cpus, start_addr,
> > > +                andes_ae350_memmap[ANDES_AE350_MROM].base,
> > > +                andes_ae350_memmap[ANDES_AE350_MROM].size, kernel_entry,
> > > +                fdt_load_addr, bs->fdt);
> > > +}
> > > +
> > > +static void andes_ae350_machine_class_init(ObjectClass *oc, void *data)
> > > +{
> > > +    MachineClass *mc = MACHINE_CLASS(oc);
> > > +
> > > +    mc->desc = "RISC-V Board compatible with Andes AE350";
> > > +    mc->init = andes_ae350_machine_init;
> > > +    mc->max_cpus = ANDES_CPUS_MAX;
> > > +    mc->default_cpu_type = VIRT_CPU;
> > > +}
> > > +
> > > +static void andes_ae350_machine_instance_init(Object *obj)
> > > +{
> > > +
> > > +}
> > > +
> > > +static const TypeInfo andes_ae350_machine_typeinfo = {
> > > +    .name       = MACHINE_TYPE_NAME("andes_ae350"),
> > > +    .parent     = TYPE_MACHINE,
> > > +    .class_init = andes_ae350_machine_class_init,
> > > +    .instance_init = andes_ae350_machine_instance_init,
> > > +    .instance_size = sizeof(AndesAe350BoardState),
> > > +};
> > > +
> > > +static void andes_ae350_machine_init_register_types(void)
> > > +{
> > > +    type_register_static(&andes_ae350_machine_typeinfo);
> > > +}
> > > +
> > > +type_init(andes_ae350_machine_init_register_types)
> > > +
> > > +static void andes_ae350_soc_class_init(ObjectClass *oc, void *data)
> > > +{
> > > +    DeviceClass *dc = DEVICE_CLASS(oc);
> > > +
> > > +    dc->realize = andes_ae350_soc_realize;
> > > +    dc->user_creatable = false;
> > > +}
> > > +
> > > +static const TypeInfo andes_ae350_soc_type_info = {
> > > +    .name       = TYPE_ANDES_AE350_SOC,
> > > +    .parent     = TYPE_DEVICE,
> > > +    .instance_init = andes_ae350_soc_instance_init,
> > > +    .instance_size = sizeof(AndesAe350SocState),
> > > +    .class_init = andes_ae350_soc_class_init,
> > > +};
> > > +
> > > +static void andes_ae350_soc_init_register_types(void)
> > > +{
> > > +    type_register_static(&andes_ae350_soc_type_info);
> > > +}
> > > +
> > > +type_init(andes_ae350_soc_init_register_types)
> > > diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> > > index 275c0f7eb7..dc0f2cb98b 100644
> > > --- a/hw/riscv/meson.build
> > > +++ b/hw/riscv/meson.build
> > > @@ -2,6 +2,7 @@ riscv_ss = ss.source_set()
> > >  riscv_ss.add(files('boot.c'), fdt)
> > >  riscv_ss.add(files('numa.c'))
> > >  riscv_ss.add(files('riscv_hart.c'))
> > > +riscv_ss.add(when: 'CONFIG_ANDES_AE350', if_true: files('andes_ae350.c'))
> > >  riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c'))
> > >  riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c'))
> > >  riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
> > > diff --git a/include/hw/riscv/andes_ae350.h b/include/hw/riscv/andes_ae350.h
> > > new file mode 100644
> > > index 0000000000..fb1a15e8cc
> > > --- /dev/null
> > > +++ b/include/hw/riscv/andes_ae350.h
> > > @@ -0,0 +1,93 @@
> > > +/*
> > > + * Andes RISC-V AE350 Board
> > > + *
> > > + * Copyright (c) 2021 Andes Tech. Corp.
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms and conditions of the GNU General Public License,
> > > + * version 2 or later, as published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed in the hope it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > > + * more details.
> > > + *
> > > + * 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 HW_RISCV_ANDES_AE350_H
> > > +#define HW_RISCV_ANDES_AE350_H
> > > +
> > > +#include "hw/riscv/riscv_hart.h"
> > > +#include "hw/sysbus.h"
> > > +#include "hw/block/flash.h"
> > > +#include "qom/object.h"
> > > +
> > > +#define ANDES_CPUS_MAX 4
> > > +
> > > +#define TYPE_ANDES_AE350_SOC "riscv.andes.ae350.soc"
> > > +#define ANDES_AE350_SOC(obj) \
> > > +    OBJECT_CHECK(AndesAe350SocState, (obj), TYPE_ANDES_AE350_SOC)
> > > +
> > > +typedef struct AndesAe350SocState {
> > > +    /*< private >*/
> > > +    SysBusDevice parent_obj;
> > > +
> > > +    /*< public >*/
> > > +    RISCVHartArrayState cpus;
> > > +    DeviceState *plic;
> > > +    DeviceState *plic_sw;
> > > +} AndesAe350SocState;
> > > +
> > > +#define TYPE_ANDES_AE350_MACHINE MACHINE_TYPE_NAME("andes_ae350")
> > > +#define ANDES_AE350_MACHINE(obj) \
> > > +    OBJECT_CHECK(AndesAe350BoardState, (obj), TYPE_ANDES_AE350_MACHINE)
> > > +
> > > +typedef struct AndesAe350BoardState {
> > > +    /*< private >*/
> > > +    SysBusDevice parent_obj;
> > > +
> > > +    /*< public >*/
> > > +    AndesAe350SocState soc;
> > > +    void *fdt;
> > > +    int fdt_size;
> > > +} AndesAe350BoardState;
> > > +
> > > +enum {
> > > +    ANDES_AE350_DEBUG,
> > > +    ANDES_AE350_MROM,
> > > +    ANDES_AE350_PLMT,
> > > +    ANDES_AE350_PLICSW,
> > > +    ANDES_AE350_PLIC,
> > > +    ANDES_AE350_UART1,
> > > +    ANDES_AE350_UART2,
> > > +    ANDES_AE350_DRAM,
> > > +    ANDES_AE350_GEM,
> > > +    ANDES_AE350_PIT,
> > > +    ANDES_AE350_SDC,
> > > +    ANDES_AE350_MAC,
> > > +    ANDES_AE350_VIRTIO,
> > > +};
> > > +
> > > +enum {
> > > +    ANDES_AE350_PIT_IRQ = 3,
> > > +    ANDES_AE350_UART1_IRQ = 8,
> > > +    ANDES_AE350_UART2_IRQ = 9,
> > > +    ANDES_AE350_SDC_IRQ = 18,
> > > +    ANDES_AE350_MAC_IRQ = 19,
> > > +    ANDES_AE350_GEM_IRQ = 0x35,
> > > +    ANDES_AE350_VIRTIO_COUNT = 8,
> > > +    ANDES_AE350_VIRTIO_IRQ = 16, /* 16 to 23 */
> > > +};
> > > +
> > > +#define ANDES_UART_REG_SHIFT    0x2
> > > +#define ANDES_UART_REG_OFFSET   0x20
> > > +
> > > +#if defined(TARGET_RISCV32)
> > > +#define VIRT_CPU TYPE_RISCV_CPU_BASE32
> > > +#elif defined(TARGET_RISCV64)
> > > +#define VIRT_CPU TYPE_RISCV_CPU_BASE64
> > > +#endif
> > > +
> > > +#endif /* HW_RISCV_ANDES_AE350_H */
> > > --
> >
> > Please add a target guide in docs/system/riscv for this new board.
>
> OK.
>
> > Does it support running upstream U-Boot?
> >
>
> The status is same as linux upstream.
> Will be support later.
>
> > Regards,
> > Bin
>
Bin Meng March 12, 2021, 1:07 a.m. UTC | #4
On Thu, Mar 11, 2021 at 11:47 PM Alistair Francis <alistair23@gmail.com> wrote:
>
> On Thu, Mar 11, 2021 at 2:03 AM Dylan Jhong <dylan@andestech.com> wrote:
> >
> > On Wed, Mar 10, 2021 at 02:15:25PM +0800, Bin Meng wrote:
> > > On Wed, Mar 10, 2021 at 11:36 AM Dylan Jhong <dylan@andestech.com> wrote:
> > > >
> > > > This provides a RISC-V Board based on Andes's AE350 specification.
> > > > The following machine is implemented:
> > > >
> > > > - 'andes_ae350'; PLIC, PLICSW, PLMT, 16550a UART, VirtIO MMIO, device-tree
> > >
> > > Is this a virtual target because virtio is added? Or does the hardware
> > > provide the virtio programming interface?
> >
> > Andes ae350 is an FPGA evaluation board with many Andes's peripheral devices,
> > but we only provide the most basic functions in the qemu version of ae350.
> > Because we hope that customers can quickly develop and evaluate before getting the real ae350 board,
> > so we use virtio to replace some peripheral devices.
>
> I don't think that's a good idea. The QEMU model should match the
> hardware device.
>

Agree. It is very confusing to add a virtio to a machine that has a
real world board.

> >
> > >
> > > >
> > > > Signed-off-by: Dylan Jhong <dylan@andestech.com>
> > > > Signed-off-by: Ruinland ChuanTzu Tsai <ruinland@andestech.com>
> > > > ---
> > > >  default-configs/devices/riscv32-softmmu.mak |   1 +
> > > >  default-configs/devices/riscv64-softmmu.mak |   1 +
> > > >  hw/riscv/Kconfig                            |   7 +
> > > >  hw/riscv/andes_ae350.c                      | 501 ++++++++++++++++++++
> > > >  hw/riscv/meson.build                        |   1 +
> > > >  include/hw/riscv/andes_ae350.h              |  93 ++++
> > > >  6 files changed, 604 insertions(+)
> > > >  create mode 100644 hw/riscv/andes_ae350.c
> > > >  create mode 100644 include/hw/riscv/andes_ae350.h
> > > >
> > > > diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
> > > > index d847bd5692..a268007e72 100644
> > > > --- a/default-configs/devices/riscv32-softmmu.mak
> > > > +++ b/default-configs/devices/riscv32-softmmu.mak
> > > > @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> > > >
> > > >  # Boards:
> > > >  #
> > > > +CONFIG_ANDES_AE350=y
> > > >  CONFIG_SPIKE=y
> > > >  CONFIG_SIFIVE_E=y
> > > >  CONFIG_SIFIVE_U=y
> > > > diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
> > > > index d5eec75f05..9a37dfd8c0 100644
> > > > --- a/default-configs/devices/riscv64-softmmu.mak
> > > > +++ b/default-configs/devices/riscv64-softmmu.mak
> > > > @@ -8,6 +8,7 @@ CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> > > >
> > > >  # Boards:
> > > >  #
> > > > +CONFIG_ANDES_AE350=y
> > > >  CONFIG_SPIKE=y
> > > >  CONFIG_SIFIVE_E=y
> > > >  CONFIG_SIFIVE_U=y
> > > > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> > > > index d139074b02..04f6369ab7 100644
> > > > --- a/hw/riscv/Kconfig
> > > > +++ b/hw/riscv/Kconfig
> > > > @@ -1,6 +1,13 @@
> > > >  config IBEX
> > > >      bool
> > > >
> > > > +config ANDES_AE350
> > >
> > > This needs to be sorted in alphabetical order
> > >
> >
> > Thanks, this will be fixed in V2.
> >
> > > > +    bool
> > > > +    select SERIAL
> > > > +    select VIRTIO_MMIO
> > > > +    select ANDES_PLIC
> > > > +    select ANDES_PLMT
> > > > +
> > > >  config MICROCHIP_PFSOC
> > > >      bool
> > > >      select CADENCE_SDHCI
> > > > diff --git a/hw/riscv/andes_ae350.c b/hw/riscv/andes_ae350.c
> > > > new file mode 100644
> > > > index 0000000000..ed5f9701ad
> > > > --- /dev/null
> > > > +++ b/hw/riscv/andes_ae350.c
> > > > @@ -0,0 +1,501 @@
> > > > +/*
> > > > + * Andes RISC-V AE350 Board
> > > > + *
> > > > + * Copyright (c) 2021 Andes Tech. Corp.
> > > > + *
> > > > + * Andes AE350 Board supports ns16550a UART and VirtIO MMIO.
> > > > + * The interrupt controllers are andes PLIC and andes PLICSW.
> > > > + * Timer is Andes PLMT.
> > > > + *
> > > > + * This program is free software; you can redistribute it and/or modify it
> > > > + * under the terms and conditions of the GNU General Public License,
> > > > + * version 2 or later, as published by the Free Software Foundation.
> > > > + *
> > > > + * This program is distributed in the hope it will be useful, but WITHOUT
> > > > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> > > > + * more details.
> > > > + *
> > > > + * 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 "qemu/units.h"
> > > > +#include "qemu/log.h"
> > > > +#include "qemu/error-report.h"
> > > > +#include "qapi/error.h"
> > > > +#include "hw/boards.h"
> > > > +#include "hw/loader.h"
> > > > +#include "hw/sysbus.h"
> > > > +#include "hw/qdev-properties.h"
> > > > +#include "hw/char/serial.h"
> > > > +#include "target/riscv/cpu.h"
> > > > +#include "hw/riscv/riscv_hart.h"
> > > > +#include "hw/riscv/boot.h"
> > > > +#include "hw/riscv/numa.h"
> > > > +#include "chardev/char.h"
> > > > +#include "sysemu/arch_init.h"
> > > > +#include "sysemu/device_tree.h"
> > > > +#include "sysemu/sysemu.h"
> > > > +#include "hw/pci/pci.h"
> > > > +#include "hw/pci-host/gpex.h"
> > > > +
> > > > +#include "hw/intc/andes_plic.h"
> > > > +#include "hw/timer/andes_plmt.h"
> > > > +#include "hw/riscv/andes_ae350.h"
> > > > +
> > > > +# define BIOS_FILENAME ""
> > > > +
> > > > +static const struct MemmapEntry {
> > > > +    hwaddr base;
> > > > +    hwaddr size;
> > > > +} andes_ae350_memmap[] = {
> > > > +    [ANDES_AE350_DEBUG]     = { 0x00000000,      0x100 },
> > > > +    [ANDES_AE350_DRAM]      = { 0x00000000, 0x80000000 },
> > > > +    [ANDES_AE350_MROM]      = { 0xb0000000,   0x100000 },
> > > > +    [ANDES_AE350_MAC]       = { 0xe0100000,   0x100000 },
> > > > +    [ANDES_AE350_GEM]       = { 0xe0200000,   0x100000 },
> > > > +    [ANDES_AE350_PLIC]      = { 0xe4000000,   0x400000 },
> > > > +    [ANDES_AE350_PLMT]      = { 0xe6000000,   0x100000 },
> > > > +    [ANDES_AE350_PLICSW]    = { 0xe6400000,   0x400000 },
> > > > +    [ANDES_AE350_UART1]     = { 0xf0200000,      0x100 },
> > > > +    [ANDES_AE350_UART2]     = { 0xf0300000,      0x100 },
> > > > +    [ANDES_AE350_PIT]       = { 0xf0400000,   0x100000 },
> > > > +    [ANDES_AE350_SDC]       = { 0xf0e00000,   0x100000 },
> > > > +    [ANDES_AE350_VIRTIO]    = { 0xfe000000,     0x1000 },
> > > > +};
> > > > +
> > > > +static void
> > > > +create_fdt(AndesAe350BoardState *bs, const struct MemmapEntry *memmap,
> > > > +    uint64_t mem_size, const char *cmdline)
> > > > +{
> > > > +    AndesAe350SocState *s = &bs->soc;
> > > > +    MachineState *ms = MACHINE(qdev_get_machine());
> > > > +    void *fdt;
> > > > +    int cpu, i;
> > > > +    uint64_t mem_addr;
> > > > +    uint32_t *plic_irq_ext, *plicsw_irq_ext, *plmt_irq_ext;
> > > > +    unsigned long plic_addr, plicsw_addr, plmt_addr;
> > > > +    char *plic_name, *plicsw_name, *plmt_name;
> > > > +    uint32_t intc_phandle = 0, plic_phandle = 0;
> > > > +    uint32_t phandle = 1;
> > > > +    char *isa_name, *mem_name, *cpu_name, *intc_name, *uart_name, *virtio_name;
> > > > +
> > > > +    if (ms->dtb) {
> > > > +        fdt = bs->fdt = load_device_tree(ms->dtb, &bs->fdt_size);
> > > > +        if (!fdt) {
> > > > +            error_report("load_device_tree() failed");
> > > > +            exit(1);
> > > > +        }
> > > > +        goto update_bootargs;
> > > > +    } else {
> > > > +        fdt = bs->fdt = create_device_tree(&bs->fdt_size);
> > > > +        if (!fdt) {
> > > > +            error_report("create_device_tree() failed");
> > > > +            exit(1);
> > > > +        }
> > > > +    }
> > > > +
> > > > +    qemu_fdt_setprop_string(fdt, "/", "model", "Andes AE350 Board");
> > > > +    qemu_fdt_setprop_string(fdt, "/", "compatible", "andestech,ae350");
> > > > +    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
> > > > +    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
> > > > +
> > > > +    qemu_fdt_add_subnode(fdt, "/soc");
> > > > +    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
> > > > +    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
> > > > +    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
> > > > +    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
> > > > +
> > > > +    qemu_fdt_add_subnode(fdt, "/cpus");
> > > > +    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> > > > +                          ANDES_PLMT_TIMEBASE_FREQ);
> > > > +    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> > > > +    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> > > > +    qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
> > > > +
> > > > +    plic_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 4);
> > > > +    plicsw_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> > > > +    plmt_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
> > > > +
> > > > +    for (cpu = 0; cpu < s->cpus.num_harts; cpu++) {
> > > > +        intc_phandle = phandle++;
> > > > +
> > > > +        cpu_name = g_strdup_printf("/cpus/cpu@%d",
> > > > +            s->cpus.hartid_base + cpu);
> > > > +        qemu_fdt_add_subnode(fdt, cpu_name);
> > > > +#if defined(TARGET_RISCV32)
> > > > +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32");
> > > > +#else
> > > > +        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv39");
> > > > +#endif
> > > > +        isa_name = riscv_isa_string(&s->cpus.harts[cpu]);
> > > > +        qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", isa_name);
> > > > +        g_free(isa_name);
> > > > +        qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
> > > > +        qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
> > > > +        qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
> > > > +            s->cpus.hartid_base + cpu);
> > > > +        qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
> > > > +
> > > > +        intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
> > > > +        qemu_fdt_add_subnode(fdt, intc_name);
> > > > +        qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle);
> > > > +        qemu_fdt_setprop_string(fdt, intc_name, "compatible",
> > > > +            "riscv,cpu-intc");
> > > > +        qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
> > > > +        qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
> > > > +
> > > > +        plic_irq_ext[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
> > > > +        plic_irq_ext[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
> > > > +        plic_irq_ext[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
> > > > +        plic_irq_ext[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
> > > > +
> > > > +        plicsw_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> > > > +        plicsw_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
> > > > +
> > > > +        plmt_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
> > > > +        plmt_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
> > > > +
> > > > +        g_free(intc_name);
> > > > +    }
> > > > +
> > > > +    mem_addr = memmap[ANDES_AE350_DRAM].base;
> > > > +    mem_name = g_strdup_printf("/memory@%lx", (long)mem_addr);
> > > > +    qemu_fdt_add_subnode(fdt, mem_name);
> > > > +    qemu_fdt_setprop_cells(fdt, mem_name, "reg",
> > > > +        mem_addr >> 32, mem_addr, mem_size >> 32, mem_size);
> > > > +    qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
> > > > +    g_free(mem_name);
> > > > +
> > > > +    /* create plic */
> > > > +    plic_phandle = phandle++;
> > > > +    plic_addr = memmap[ANDES_AE350_PLIC].base;
> > > > +    plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
> > > > +    qemu_fdt_add_subnode(fdt, plic_name);
> > > > +    qemu_fdt_setprop_cell(fdt, plic_name,
> > > > +        "#address-cells", 0x2);
> > > > +    qemu_fdt_setprop_cell(fdt, plic_name,
> > > > +        "#interrupt-cells", 0x2);
> > > > +    qemu_fdt_setprop_string(fdt, plic_name, "compatible", "riscv,plic0");
> > >
> > > This suggests PLIC is the same as the SiFive one. So why do we have a
> > > different implementation of the PLIC model?
> > >
> >
> > The difference of these two PLICs, please refer to my reply of "[PATCH 1/3] Andes RISC-V PLIC"
> >
> > > > +    qemu_fdt_setprop(fdt, plic_name, "interrupt-controller", NULL, 0);
> > > > +    qemu_fdt_setprop(fdt, plic_name, "interrupts-extended",
> > > > +        plic_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 4);
> > > > +    qemu_fdt_setprop_cells(fdt, plic_name, "reg",
> > > > +        0x0, plic_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
> > > > +    qemu_fdt_setprop_cell(fdt, plic_name, "riscv,ndev", 0x47);
> > > > +    qemu_fdt_setprop_cell(fdt, plic_name, "phandle", plic_phandle);
> > > > +    g_free(plic_name);
> > > > +    g_free(plic_irq_ext);
> > > > +
> > > > +    /* create plicsw */
> > > > +    plicsw_addr = memmap[ANDES_AE350_PLICSW].base;
> > > > +    plicsw_name = g_strdup_printf("/soc/interrupt-controller@%lx", plicsw_addr);
> > > > +    qemu_fdt_add_subnode(fdt, plicsw_name);
> > > > +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> > > > +        "#address-cells", 0x2);
> > > > +    qemu_fdt_setprop_cell(fdt, plicsw_name,
> > > > +        "#interrupt-cells", 0x2);
> > > > +    qemu_fdt_setprop_string(fdt, plicsw_name, "compatible", "riscv,plic1");
> > >
> > > Is this bindings in the Linux kernel upstream? I can't find any
> > > reference in the kernel tree.
> > >
> >
> > Currently only supports andes Linux BSP,
> > we have plans to push to linux upstream in the future.
>
> This raises a good question about device tree bindings being generated
> from QEMU.
>
> Do we want QEMU to generate a DT that doesn't have matching bindings?

I'd prefer new bindings are sent to kernel upstream to get it reviewed
there first. It might not just be the compatible string, but some
other properties that may change.

>
> I guess if the bindings change during the upstream process we can just
> add a second compatible string.
>
> Also if there is no upstream support how can I test the board?
>

Regards,
Bin
diff mbox series

Patch

diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
index d847bd5692..a268007e72 100644
--- a/default-configs/devices/riscv32-softmmu.mak
+++ b/default-configs/devices/riscv32-softmmu.mak
@@ -8,6 +8,7 @@  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
 
 # Boards:
 #
+CONFIG_ANDES_AE350=y
 CONFIG_SPIKE=y
 CONFIG_SIFIVE_E=y
 CONFIG_SIFIVE_U=y
diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
index d5eec75f05..9a37dfd8c0 100644
--- a/default-configs/devices/riscv64-softmmu.mak
+++ b/default-configs/devices/riscv64-softmmu.mak
@@ -8,6 +8,7 @@  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
 
 # Boards:
 #
+CONFIG_ANDES_AE350=y
 CONFIG_SPIKE=y
 CONFIG_SIFIVE_E=y
 CONFIG_SIFIVE_U=y
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index d139074b02..04f6369ab7 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -1,6 +1,13 @@ 
 config IBEX
     bool
 
+config ANDES_AE350
+    bool
+    select SERIAL
+    select VIRTIO_MMIO
+    select ANDES_PLIC
+    select ANDES_PLMT
+
 config MICROCHIP_PFSOC
     bool
     select CADENCE_SDHCI
diff --git a/hw/riscv/andes_ae350.c b/hw/riscv/andes_ae350.c
new file mode 100644
index 0000000000..ed5f9701ad
--- /dev/null
+++ b/hw/riscv/andes_ae350.c
@@ -0,0 +1,501 @@ 
+/*
+ * Andes RISC-V AE350 Board
+ *
+ * Copyright (c) 2021 Andes Tech. Corp.
+ *
+ * Andes AE350 Board supports ns16550a UART and VirtIO MMIO.
+ * The interrupt controllers are andes PLIC and andes PLICSW.
+ * Timer is Andes PLMT.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * 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 "qemu/units.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-properties.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/boot.h"
+#include "hw/riscv/numa.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/sysemu.h"
+#include "hw/pci/pci.h"
+#include "hw/pci-host/gpex.h"
+
+#include "hw/intc/andes_plic.h"
+#include "hw/timer/andes_plmt.h"
+#include "hw/riscv/andes_ae350.h"
+
+# define BIOS_FILENAME ""
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} andes_ae350_memmap[] = {
+    [ANDES_AE350_DEBUG]     = { 0x00000000,      0x100 },
+    [ANDES_AE350_DRAM]      = { 0x00000000, 0x80000000 },
+    [ANDES_AE350_MROM]      = { 0xb0000000,   0x100000 },
+    [ANDES_AE350_MAC]       = { 0xe0100000,   0x100000 },
+    [ANDES_AE350_GEM]       = { 0xe0200000,   0x100000 },
+    [ANDES_AE350_PLIC]      = { 0xe4000000,   0x400000 },
+    [ANDES_AE350_PLMT]      = { 0xe6000000,   0x100000 },
+    [ANDES_AE350_PLICSW]    = { 0xe6400000,   0x400000 },
+    [ANDES_AE350_UART1]     = { 0xf0200000,      0x100 },
+    [ANDES_AE350_UART2]     = { 0xf0300000,      0x100 },
+    [ANDES_AE350_PIT]       = { 0xf0400000,   0x100000 },
+    [ANDES_AE350_SDC]       = { 0xf0e00000,   0x100000 },
+    [ANDES_AE350_VIRTIO]    = { 0xfe000000,     0x1000 },
+};
+
+static void
+create_fdt(AndesAe350BoardState *bs, const struct MemmapEntry *memmap,
+    uint64_t mem_size, const char *cmdline)
+{
+    AndesAe350SocState *s = &bs->soc;
+    MachineState *ms = MACHINE(qdev_get_machine());
+    void *fdt;
+    int cpu, i;
+    uint64_t mem_addr;
+    uint32_t *plic_irq_ext, *plicsw_irq_ext, *plmt_irq_ext;
+    unsigned long plic_addr, plicsw_addr, plmt_addr;
+    char *plic_name, *plicsw_name, *plmt_name;
+    uint32_t intc_phandle = 0, plic_phandle = 0;
+    uint32_t phandle = 1;
+    char *isa_name, *mem_name, *cpu_name, *intc_name, *uart_name, *virtio_name;
+
+    if (ms->dtb) {
+        fdt = bs->fdt = load_device_tree(ms->dtb, &bs->fdt_size);
+        if (!fdt) {
+            error_report("load_device_tree() failed");
+            exit(1);
+        }
+        goto update_bootargs;
+    } else {
+        fdt = bs->fdt = create_device_tree(&bs->fdt_size);
+        if (!fdt) {
+            error_report("create_device_tree() failed");
+            exit(1);
+        }
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "model", "Andes AE350 Board");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "andestech,ae350");
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+    qemu_fdt_add_subnode(fdt, "/soc");
+    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
+    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
+                          ANDES_PLMT_TIMEBASE_FREQ);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+    qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
+
+    plic_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 4);
+    plicsw_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
+    plmt_irq_ext = g_new0(uint32_t, s->cpus.num_harts * 2);
+
+    for (cpu = 0; cpu < s->cpus.num_harts; cpu++) {
+        intc_phandle = phandle++;
+
+        cpu_name = g_strdup_printf("/cpus/cpu@%d",
+            s->cpus.hartid_base + cpu);
+        qemu_fdt_add_subnode(fdt, cpu_name);
+#if defined(TARGET_RISCV32)
+        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32");
+#else
+        qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv39");
+#endif
+        isa_name = riscv_isa_string(&s->cpus.harts[cpu]);
+        qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", isa_name);
+        g_free(isa_name);
+        qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
+        qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
+        qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
+            s->cpus.hartid_base + cpu);
+        qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
+
+        intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
+        qemu_fdt_add_subnode(fdt, intc_name);
+        qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle);
+        qemu_fdt_setprop_string(fdt, intc_name, "compatible",
+            "riscv,cpu-intc");
+        qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
+
+        plic_irq_ext[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        plic_irq_ext[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+        plic_irq_ext[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        plic_irq_ext[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+
+        plicsw_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
+        plicsw_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
+
+        plmt_irq_ext[cpu * 2 + 0] = cpu_to_be32(intc_phandle);
+        plmt_irq_ext[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
+
+        g_free(intc_name);
+    }
+
+    mem_addr = memmap[ANDES_AE350_DRAM].base;
+    mem_name = g_strdup_printf("/memory@%lx", (long)mem_addr);
+    qemu_fdt_add_subnode(fdt, mem_name);
+    qemu_fdt_setprop_cells(fdt, mem_name, "reg",
+        mem_addr >> 32, mem_addr, mem_size >> 32, mem_size);
+    qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
+    g_free(mem_name);
+
+    /* create plic */
+    plic_phandle = phandle++;
+    plic_addr = memmap[ANDES_AE350_PLIC].base;
+    plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
+    qemu_fdt_add_subnode(fdt, plic_name);
+    qemu_fdt_setprop_cell(fdt, plic_name,
+        "#address-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, plic_name,
+        "#interrupt-cells", 0x2);
+    qemu_fdt_setprop_string(fdt, plic_name, "compatible", "riscv,plic0");
+    qemu_fdt_setprop(fdt, plic_name, "interrupt-controller", NULL, 0);
+    qemu_fdt_setprop(fdt, plic_name, "interrupts-extended",
+        plic_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 4);
+    qemu_fdt_setprop_cells(fdt, plic_name, "reg",
+        0x0, plic_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
+    qemu_fdt_setprop_cell(fdt, plic_name, "riscv,ndev", 0x47);
+    qemu_fdt_setprop_cell(fdt, plic_name, "phandle", plic_phandle);
+    g_free(plic_name);
+    g_free(plic_irq_ext);
+
+    /* create plicsw */
+    plicsw_addr = memmap[ANDES_AE350_PLICSW].base;
+    plicsw_name = g_strdup_printf("/soc/interrupt-controller@%lx", plicsw_addr);
+    qemu_fdt_add_subnode(fdt, plicsw_name);
+    qemu_fdt_setprop_cell(fdt, plicsw_name,
+        "#address-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, plicsw_name,
+        "#interrupt-cells", 0x2);
+    qemu_fdt_setprop_string(fdt, plicsw_name, "compatible", "riscv,plic1");
+    qemu_fdt_setprop(fdt, plicsw_name, "interrupt-controller", NULL, 0);
+    qemu_fdt_setprop(fdt, plicsw_name, "interrupts-extended",
+        plicsw_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
+    qemu_fdt_setprop_cells(fdt, plicsw_name, "reg",
+        0x0, plicsw_addr, 0x0, memmap[ANDES_AE350_PLIC].size);
+    qemu_fdt_setprop_cell(fdt, plicsw_name, "riscv,ndev", 0x1);
+    g_free(plicsw_name);
+    g_free(plicsw_irq_ext);
+
+    /* create plmt */
+    plmt_addr = memmap[ANDES_AE350_PLMT].base;
+    plmt_name = g_strdup_printf("/soc/plmt0@%lx", plmt_addr);
+    qemu_fdt_add_subnode(fdt, plmt_name);
+    qemu_fdt_setprop_string(fdt, plmt_name, "compatible", "riscv,plmt0");
+    qemu_fdt_setprop(fdt, plmt_name, "interrupts-extended",
+        plmt_irq_ext, s->cpus.num_harts * sizeof(uint32_t) * 2);
+    qemu_fdt_setprop_cells(fdt, plmt_name, "reg",
+        0x0, plmt_addr, 0x0, memmap[ANDES_AE350_PLMT].size);
+    g_free(plmt_name);
+    g_free(plmt_irq_ext);
+
+    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART1].base);
+    qemu_fdt_add_subnode(fdt, uart_name);
+    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
+    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
+        0x0, memmap[ANDES_AE350_UART1].base,
+        0x0, memmap[ANDES_AE350_UART1].size);
+    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
+    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
+    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
+    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
+    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART1_IRQ, 0x4);
+
+    uart_name = g_strdup_printf("/serial@%lx", memmap[ANDES_AE350_UART2].base);
+    qemu_fdt_add_subnode(fdt, uart_name);
+    qemu_fdt_setprop_string(fdt, uart_name, "compatible", "ns16550a");
+    qemu_fdt_setprop_cells(fdt, uart_name, "reg",
+        0x0, memmap[ANDES_AE350_UART2].base,
+        0x0, memmap[ANDES_AE350_UART2].size);
+    qemu_fdt_setprop_cell(fdt, uart_name, "reg-shift", ANDES_UART_REG_SHIFT);
+    qemu_fdt_setprop_cell(fdt, uart_name, "reg-offset", ANDES_UART_REG_OFFSET);
+    qemu_fdt_setprop_cell(fdt, uart_name, "clock-frequency", 3686400);
+    qemu_fdt_setprop_cell(fdt, uart_name, "interrupt-parent", plic_phandle);
+    qemu_fdt_setprop_cells(fdt, uart_name, "interrupts", ANDES_AE350_UART2_IRQ, 0x4);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
+            "console=ttyS0,38400n8 earlycon=sbi debug loglevel=7");
+    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", uart_name);
+    g_free(uart_name);
+
+    for (i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
+        virtio_name = g_strdup_printf("/virtio_mmio@%lx",
+            (memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size));
+        qemu_fdt_add_subnode(fdt, virtio_name);
+        qemu_fdt_setprop_string(fdt, virtio_name, "compatible", "virtio,mmio");
+        qemu_fdt_setprop_cells(fdt, virtio_name, "reg",
+            0x0, memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
+            0x0, memmap[ANDES_AE350_VIRTIO].size);
+        qemu_fdt_setprop_cell(fdt, virtio_name, "interrupt-parent",
+                                plic_phandle);
+        qemu_fdt_setprop_cells(fdt, virtio_name, "interrupts",
+                                ANDES_AE350_VIRTIO_IRQ + i, 0x4);
+        g_free(virtio_name);
+    }
+
+update_bootargs:
+    if (cmdline && cmdline[0] != '\0') {
+        qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+    }
+}
+
+static char *init_hart_config(const char *hart_config, int num_harts)
+{
+    int length = 0, i = 0;
+    char *result;
+
+    length = (strlen(hart_config) + 1) * num_harts;
+    result = g_malloc0(length);
+    for (i = 0; i < num_harts; i++) {
+        if (i != 0) {
+            strncat(result, ",", length);
+        }
+        strncat(result, hart_config, length);
+        length -= (strlen(hart_config) + 1);
+    }
+
+    return result;
+}
+
+static void andes_ae350_soc_realize(DeviceState *dev_soc, Error **errp)
+{
+    const struct MemmapEntry *memmap = andes_ae350_memmap;
+    MachineState *machine = MACHINE(qdev_get_machine());
+    MemoryRegion *system_memory = get_system_memory();
+    AndesAe350SocState *s = ANDES_AE350_SOC(dev_soc);
+    char *plic_hart_config, *plicsw_hart_config;
+
+    plicsw_hart_config =
+        init_hart_config(ANDES_AE350_PLICSW_HART_CONFIG, machine->smp.cpus);
+
+    /* Per-socket SW-PLIC */
+    s->plic_sw = andes_plicsw_create(
+        memmap[ANDES_AE350_PLICSW].base,
+        ANDES_AE350_PLICSW_NAME,
+        plicsw_hart_config,
+        ANDES_AE350_PLICSW_NUM_SOURCES,
+        ANDES_AE350_PLICSW_NUM_PRIORITIES,
+        ANDES_AE350_PLICSW_PRIORITY_BASE,
+        ANDES_AE350_PLICSW_PENDING_BASE,
+        ANDES_AE350_PLICSW_ENABLE_BASE,
+        ANDES_AE350_PLICSW_ENABLE_STRIDE,
+        ANDES_AE350_PLICSW_THRESHOLD_BASE,
+        ANDES_AE350_PLICSW_THRESHOLD_STRIDE,
+        memmap[ANDES_AE350_PLICSW].size);
+
+    g_free(plicsw_hart_config);
+
+    andes_plmt_create(memmap[ANDES_AE350_PLMT].base,
+                memmap[ANDES_AE350_PLMT].size,
+                machine->smp.cpus, ANDES_PLMT_TIME_BASE, ANDES_PLMT_TIMECMP_BASE);
+
+    plic_hart_config =
+        init_hart_config(ANDES_AE350_PLIC_HART_CONFIG, machine->smp.cpus);
+
+    /* Per-socket PLIC */
+    s->plic = andes_plic_create(
+        memmap[ANDES_AE350_PLIC].base,
+        ANDES_AE350_PLIC_NAME,
+        plic_hart_config,
+        ANDES_AE350_PLIC_NUM_SOURCES,
+        ANDES_AE350_PLIC_NUM_PRIORITIES,
+        ANDES_AE350_PLIC_PRIORITY_BASE,
+        ANDES_AE350_PLIC_PENDING_BASE,
+        ANDES_AE350_PLIC_ENABLE_BASE,
+        ANDES_AE350_PLIC_ENABLE_STRIDE,
+        ANDES_AE350_PLIC_THRESHOLD_BASE,
+        ANDES_AE350_PLIC_THRESHOLD_STRIDE,
+        memmap[ANDES_AE350_PLIC].size);
+
+    g_free(plic_hart_config);
+
+    /* VIRTIO */
+    for (int i = 0; i < ANDES_AE350_VIRTIO_COUNT; i++) {
+        sysbus_create_simple("virtio-mmio",
+            memmap[ANDES_AE350_VIRTIO].base + i * memmap[ANDES_AE350_VIRTIO].size,
+            qdev_get_gpio_in(DEVICE(s->plic), (ANDES_AE350_VIRTIO_IRQ + i)));
+    }
+
+    serial_mm_init(system_memory,
+        memmap[ANDES_AE350_UART1].base + ANDES_UART_REG_OFFSET,
+        ANDES_UART_REG_SHIFT,
+        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART1_IRQ),
+        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+
+    serial_mm_init(system_memory,
+        memmap[ANDES_AE350_UART2].base + ANDES_UART_REG_OFFSET,
+        ANDES_UART_REG_SHIFT,
+        qdev_get_gpio_in(DEVICE(s->plic), ANDES_AE350_UART2_IRQ),
+        38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+}
+
+static void andes_ae350_soc_instance_init(Object *obj)
+{
+    const struct MemmapEntry *memmap = andes_ae350_memmap;
+    MachineState *machine = MACHINE(qdev_get_machine());
+    AndesAe350SocState *s = ANDES_AE350_SOC(obj);
+
+    object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
+    object_property_set_str(OBJECT(&s->cpus), "cpu-type",
+                            machine->cpu_type, &error_abort);
+    object_property_set_int(OBJECT(&s->cpus), "num-harts",
+                            machine->smp.cpus, &error_abort);
+    qdev_prop_set_uint64(DEVICE(&s->cpus), "resetvec",
+                            memmap[ANDES_AE350_MROM].base);
+    sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);
+}
+
+static void andes_ae350_machine_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = andes_ae350_memmap;
+
+    AndesAe350BoardState *bs = ANDES_AE350_MACHINE(machine);
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
+    target_ulong start_addr = memmap[ANDES_AE350_DRAM].base;
+    target_ulong firmware_end_addr, kernel_start_addr;
+    uint32_t fdt_load_addr;
+    uint64_t kernel_entry;
+
+    /* Initialize SoC */
+    object_initialize_child(OBJECT(machine), "soc",
+                    &bs->soc, TYPE_ANDES_AE350_SOC);
+    qdev_realize(DEVICE(&bs->soc), NULL, &error_abort);
+
+    /* register system main memory (actual RAM) */
+    memory_region_init_ram(main_mem, NULL, "riscv.andes.ae350.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_DRAM].base,
+        main_mem);
+
+    /* create device tree */
+    create_fdt(bs, memmap, machine->ram_size, machine->kernel_cmdline);
+
+    /* boot rom */
+    memory_region_init_rom(mask_rom, NULL, "riscv.andes.ae350.mrom",
+                           memmap[ANDES_AE350_MROM].size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[ANDES_AE350_MROM].base,
+                                mask_rom);
+
+    firmware_end_addr = riscv_find_and_load_firmware(machine, BIOS_FILENAME,
+                                                     start_addr, NULL);
+    if (machine->kernel_filename) {
+        kernel_start_addr = riscv_calc_kernel_start_addr(&bs->soc.cpus,
+                                                         firmware_end_addr);
+
+        kernel_entry = riscv_load_kernel(machine->kernel_filename,
+                                         kernel_start_addr, NULL);
+
+        if (machine->initrd_filename) {
+            hwaddr start;
+            hwaddr end = riscv_load_initrd(machine->initrd_filename,
+                                           machine->ram_size, kernel_entry,
+                                           &start);
+            qemu_fdt_setprop_cell(bs->fdt, "/chosen",
+                                  "linux,initrd-start", start);
+            qemu_fdt_setprop_cell(bs->fdt, "/chosen", "linux,initrd-end",
+                                  end);
+        }
+    } else {
+       /*
+        * If dynamic firmware is used, it doesn't know where is the next mode
+        * if kernel argument is not set.
+        */
+        kernel_entry = 0;
+    }
+
+    /* Compute the fdt load address in dram */
+    fdt_load_addr = riscv_load_fdt(memmap[ANDES_AE350_DRAM].base,
+                                   machine->ram_size, bs->fdt);
+
+    /* load the reset vector */
+    riscv_setup_rom_reset_vec(machine, &bs->soc.cpus, start_addr,
+                andes_ae350_memmap[ANDES_AE350_MROM].base,
+                andes_ae350_memmap[ANDES_AE350_MROM].size, kernel_entry,
+                fdt_load_addr, bs->fdt);
+}
+
+static void andes_ae350_machine_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "RISC-V Board compatible with Andes AE350";
+    mc->init = andes_ae350_machine_init;
+    mc->max_cpus = ANDES_CPUS_MAX;
+    mc->default_cpu_type = VIRT_CPU;
+}
+
+static void andes_ae350_machine_instance_init(Object *obj)
+{
+
+}
+
+static const TypeInfo andes_ae350_machine_typeinfo = {
+    .name       = MACHINE_TYPE_NAME("andes_ae350"),
+    .parent     = TYPE_MACHINE,
+    .class_init = andes_ae350_machine_class_init,
+    .instance_init = andes_ae350_machine_instance_init,
+    .instance_size = sizeof(AndesAe350BoardState),
+};
+
+static void andes_ae350_machine_init_register_types(void)
+{
+    type_register_static(&andes_ae350_machine_typeinfo);
+}
+
+type_init(andes_ae350_machine_init_register_types)
+
+static void andes_ae350_soc_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = andes_ae350_soc_realize;
+    dc->user_creatable = false;
+}
+
+static const TypeInfo andes_ae350_soc_type_info = {
+    .name       = TYPE_ANDES_AE350_SOC,
+    .parent     = TYPE_DEVICE,
+    .instance_init = andes_ae350_soc_instance_init,
+    .instance_size = sizeof(AndesAe350SocState),
+    .class_init = andes_ae350_soc_class_init,
+};
+
+static void andes_ae350_soc_init_register_types(void)
+{
+    type_register_static(&andes_ae350_soc_type_info);
+}
+
+type_init(andes_ae350_soc_init_register_types)
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 275c0f7eb7..dc0f2cb98b 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -2,6 +2,7 @@  riscv_ss = ss.source_set()
 riscv_ss.add(files('boot.c'), fdt)
 riscv_ss.add(files('numa.c'))
 riscv_ss.add(files('riscv_hart.c'))
+riscv_ss.add(when: 'CONFIG_ANDES_AE350', if_true: files('andes_ae350.c'))
 riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c'))
 riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c'))
 riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
diff --git a/include/hw/riscv/andes_ae350.h b/include/hw/riscv/andes_ae350.h
new file mode 100644
index 0000000000..fb1a15e8cc
--- /dev/null
+++ b/include/hw/riscv/andes_ae350.h
@@ -0,0 +1,93 @@ 
+/*
+ * Andes RISC-V AE350 Board
+ *
+ * Copyright (c) 2021 Andes Tech. Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * 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 HW_RISCV_ANDES_AE350_H
+#define HW_RISCV_ANDES_AE350_H
+
+#include "hw/riscv/riscv_hart.h"
+#include "hw/sysbus.h"
+#include "hw/block/flash.h"
+#include "qom/object.h"
+
+#define ANDES_CPUS_MAX 4
+
+#define TYPE_ANDES_AE350_SOC "riscv.andes.ae350.soc"
+#define ANDES_AE350_SOC(obj) \
+    OBJECT_CHECK(AndesAe350SocState, (obj), TYPE_ANDES_AE350_SOC)
+
+typedef struct AndesAe350SocState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState cpus;
+    DeviceState *plic;
+    DeviceState *plic_sw;
+} AndesAe350SocState;
+
+#define TYPE_ANDES_AE350_MACHINE MACHINE_TYPE_NAME("andes_ae350")
+#define ANDES_AE350_MACHINE(obj) \
+    OBJECT_CHECK(AndesAe350BoardState, (obj), TYPE_ANDES_AE350_MACHINE)
+
+typedef struct AndesAe350BoardState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    AndesAe350SocState soc;
+    void *fdt;
+    int fdt_size;
+} AndesAe350BoardState;
+
+enum {
+    ANDES_AE350_DEBUG,
+    ANDES_AE350_MROM,
+    ANDES_AE350_PLMT,
+    ANDES_AE350_PLICSW,
+    ANDES_AE350_PLIC,
+    ANDES_AE350_UART1,
+    ANDES_AE350_UART2,
+    ANDES_AE350_DRAM,
+    ANDES_AE350_GEM,
+    ANDES_AE350_PIT,
+    ANDES_AE350_SDC,
+    ANDES_AE350_MAC,
+    ANDES_AE350_VIRTIO,
+};
+
+enum {
+    ANDES_AE350_PIT_IRQ = 3,
+    ANDES_AE350_UART1_IRQ = 8,
+    ANDES_AE350_UART2_IRQ = 9,
+    ANDES_AE350_SDC_IRQ = 18,
+    ANDES_AE350_MAC_IRQ = 19,
+    ANDES_AE350_GEM_IRQ = 0x35,
+    ANDES_AE350_VIRTIO_COUNT = 8,
+    ANDES_AE350_VIRTIO_IRQ = 16, /* 16 to 23 */
+};
+
+#define ANDES_UART_REG_SHIFT    0x2
+#define ANDES_UART_REG_OFFSET   0x20
+
+#if defined(TARGET_RISCV32)
+#define VIRT_CPU TYPE_RISCV_CPU_BASE32
+#elif defined(TARGET_RISCV64)
+#define VIRT_CPU TYPE_RISCV_CPU_BASE64
+#endif
+
+#endif /* HW_RISCV_ANDES_AE350_H */