qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* RISC-V: Add CVA6 machine
@ 2025-06-09 13:17 Ben Dooks
  2025-06-09 13:17 ` [PATCH v3 1/3] hw/riscv: add " Ben Dooks
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Ben Dooks @ 2025-06-09 13:17 UTC (permalink / raw)
  To: nazar.kazakov, joseph.baker, fran.redondo, lawrence.hunter,
	liwei1518, dbarboza, zhiwei_liu, alistair.francis, qemu-riscv
  Cc: ben.dooks, qemu-devel

Add the CVA6 (the corev_apu from the fpga) model from
https://github.com/openhwgroup/cva6

Tree at:
https://gitlab.com/CodethinkLabs/qemu/-/tree/bjdooks/cva6-send-8jun2025?ref_type=heads

Fixes:

v3:
- fix missing file source
- set 64bit only for now
v2:
- rebased and fixed whitespace issues




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

* [PATCH v3 1/3] hw/riscv: add CVA6 machine
  2025-06-09 13:17 RISC-V: Add CVA6 machine Ben Dooks
@ 2025-06-09 13:17 ` Ben Dooks
  2025-06-09 23:29   ` Alistair Francis
  2025-06-09 13:17 ` [PATCH v3 2/3] target/riscv: add cva6 core type Ben Dooks
  2025-06-09 13:17 ` [PATCH v3 3/3] hw/riscv: set cva6 to use TYPE_RISCV_CPU_CVA6 Ben Dooks
  2 siblings, 1 reply; 7+ messages in thread
From: Ben Dooks @ 2025-06-09 13:17 UTC (permalink / raw)
  To: nazar.kazakov, joseph.baker, fran.redondo, lawrence.hunter,
	liwei1518, dbarboza, zhiwei_liu, alistair.francis, qemu-riscv
  Cc: ben.dooks, qemu-devel

Add a (currently Genesy2 based) CVA6 machine.

Has SPI and UART, the GPIO and Ethernet are currently black-holed
as there is no hardware model for them (lowRISC ethernet and Xilinx
GPIO)

Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
v3:
- fix missed plic comment
- made 64bit only for now
v2:
- whitespace fixes
- use g_autofree on plic
v1:
- squashed in fixes for sd-card and new qemu init
- move to spdx for cva6 machine
- code cleanups missed in first review

updated plic regmap

make cva6 64bit for now
---
 hw/riscv/Kconfig        |  11 ++
 hw/riscv/cva6.c         | 229 ++++++++++++++++++++++++++++++++++++++++
 hw/riscv/meson.build    |   1 +
 include/hw/riscv/cva6.h |  88 +++++++++++++++
 4 files changed, 329 insertions(+)
 create mode 100644 hw/riscv/cva6.c
 create mode 100644 include/hw/riscv/cva6.h

diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index e6a0ac1fa1..033e29dab1 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -9,6 +9,17 @@ config IBEX
 
 # RISC-V machines in alphabetical order
 
+# technically it might be possible to build cva6 32bit
+config CVA6
+    bool
+    default y
+    depends on RISCV64
+    select DEVICE_TREE
+    select SIFIVE_PLIC
+    select XILINX_SPI
+    select RISCV_ACLINT
+    select UNIMP
+
 config MICROCHIP_PFSOC
     bool
     default y
diff --git a/hw/riscv/cva6.c b/hw/riscv/cva6.c
new file mode 100644
index 0000000000..3adfa8b5cc
--- /dev/null
+++ b/hw/riscv/cva6.c
@@ -0,0 +1,229 @@
+/*
+ * QEMU RISC-V Board for OpenHW CVA6 SoC
+ *
+ * Copyright (c) 2025 Codethink Ltd
+ * Ben Dooks <ben.dooks@codethink.co.uk>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "hw/boards.h"
+#include "hw/irq.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/misc/unimp.h"
+
+#include "hw/sd/sd.h"
+#include "hw/ssi/ssi.h"
+
+#include "hw/riscv/cva6.h"
+#include "hw/riscv/boot.h"
+#include "hw/intc/riscv_aclint.h"
+
+#include "system/system.h"
+
+#include <libfdt.h>
+
+#define CVA6_ROM_BASE  0x10000
+
+static const MemMapEntry cva6_memmap[] = {
+    [CVA6_DEBUG] =              {  0x0000000,  0x1000 },
+    [CVA6_ROM] =                { CVA6_ROM_BASE, 0x10000 },
+    [CVA6_CLINT] =              {  0x2000000, 0xC0000 },
+    [CVA6_PLIC] =               {  0xC000000, 0x4000000 },
+    [CVA6_UART] =               { 0x10000000, 0x1000 },
+    [CVA6_TIMER] =              { 0x18000000, 0x10000 },
+    [CVA6_SPI] =                { 0x20000000, 0x800000 },
+    [CVA6_ETHERNET] =           { 0x30000000, 0x10000 },
+    [CVA6_GPIO] =               { 0x40000000, 0x1000 },
+    [CVA6_DRAM] =               { 0x80000000, 0x40000000 },
+};
+
+static void cva6_machine_init(MachineState *machine)
+{
+    MachineClass *mc = MACHINE_GET_CLASS(machine);
+    MemoryRegion *sys_mem = get_system_memory();
+    hwaddr dram_addr = cva6_memmap[CVA6_DRAM].base;
+    CVA6State *s = CVA6_MACHINE(machine);
+    RISCVBootInfo boot_info;
+
+    object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_CVA6);
+    qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
+
+    if (machine->ram_size > mc->default_ram_size) {
+        error_report("RAM size is too big for DRAM area");
+        exit(EXIT_FAILURE);
+    }
+
+    memory_region_add_subregion(sys_mem, dram_addr, machine->ram);
+    riscv_boot_info_init(&boot_info, &s->soc.cpus);
+
+    if (machine->firmware) {
+         hwaddr firmware_load_addr = cva6_memmap[CVA6_ROM].base;
+         riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL);
+    }
+
+     if (machine->kernel_filename) {
+         /* note - we've not tested just loading the kernel w/o uboot */
+         riscv_load_kernel(machine, &boot_info, dram_addr, false, NULL);
+    }
+
+}
+
+static void cva6_machine_class_init(ObjectClass *oc, const void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "RISC-V board for CVA6";
+    mc->init = cva6_machine_init;
+    mc->max_cpus = 1;
+    mc->default_ram_id = "cva6.ram";
+    /* start with "max" cpu type until we sort out CVA6 type */
+    mc->default_cpu_type = TYPE_RISCV_CPU_MAX;
+    mc->default_ram_size = cva6_memmap[CVA6_DRAM].size;
+};
+
+static void cva6_soc_init(Object *obj)
+{
+    CVA6SoCState *s = RISCV_CVA6(obj);
+
+    object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
+}
+
+static void cva6_add_spi(CVA6SoCState *s, const MemMapEntry *map)
+{
+    DriveInfo *dinfo;
+    BlockBackend *blk;
+    DeviceState *card_dev;
+    qemu_irq sd_cs;
+    DeviceState *sddev;
+    SysBusDevice *busdev;
+    DeviceState *spi_dev;
+    SSIBus *spi;
+
+    spi_dev = qdev_new("xlnx.xps-spi");
+    qdev_prop_set_uint8(spi_dev, "num-ss-bits", 1);
+    qdev_prop_set_string(spi_dev, "endianness", "little");
+
+    busdev = SYS_BUS_DEVICE(spi_dev);
+    sysbus_realize_and_unref(busdev, &error_fatal);
+    sysbus_mmio_map(busdev, 0, map->base);
+    sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(s->plic), CVA6_SPI_IRQ));
+
+    spi = (SSIBus *)qdev_get_child_bus(spi_dev, "spi");
+
+    sddev = ssi_create_peripheral(spi, "ssi-sd");
+    sd_cs = qdev_get_gpio_in_named(sddev, SSI_GPIO_CS, 0);
+    sysbus_connect_irq(busdev, 1, sd_cs);
+
+    dinfo = drive_get(IF_SD, 0, 0);
+    blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
+    card_dev = qdev_new(TYPE_SD_CARD_SPI);
+    qdev_prop_set_drive_err(card_dev, "drive", blk, &error_fatal);
+
+    qdev_realize_and_unref(card_dev, qdev_get_child_bus(sddev, "sd-bus"), &error_fatal);
+}
+
+static void not_implemented(const char *name, const MemMapEntry *map)
+{
+    create_unimplemented_device(name, map->base, map->size);
+}
+
+static void cva6_soc_realize(DeviceState *dev_soc, Error **errp)
+{
+    MemoryRegion *system_memory = get_system_memory();
+    MachineState *ms = MACHINE(qdev_get_machine());
+    CVA6SoCState *s = RISCV_CVA6(dev_soc);
+    const MemMapEntry *memmap = cva6_memmap;
+    MemoryRegion *rom = g_new(MemoryRegion, 1);
+    g_autofree char *plic_hart_config;
+
+    object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type,
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus,
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->cpus), "resetvec", CVA6_ROM_BASE,
+                            &error_abort);
+    sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal);
+
+    /* boot rom */
+    memory_region_init_rom(rom, OBJECT(dev_soc), "riscv.cva6.bootrom",
+                           memmap[CVA6_ROM].size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[CVA6_ROM].base,
+                                rom);
+
+    /* create PLIC hart topology configuration string */
+    plic_hart_config = riscv_plic_hart_config_string(ms->smp.cpus);
+
+    /* MMIO */
+    s->plic = sifive_plic_create(memmap[CVA6_PLIC].base,
+        plic_hart_config, ms->smp.cpus, 0,
+        CVA6_PLIC_NUM_SOURCES,
+        CVA6_PLIC_NUM_PRIORITIES,
+        CVA6_PLIC_PRIORITY_BASE,
+        CVA6_PLIC_PENDING_BASE,
+        CVA6_PLIC_ENABLE_BASE,
+        CVA6_PLIC_ENABLE_STRIDE,
+        CVA6_PLIC_CONTEXT_BASE,
+        CVA6_PLIC_CONTEXT_STRIDE,
+        memmap[CVA6_PLIC].size);
+
+    riscv_aclint_swi_create(memmap[CVA6_CLINT].base, 0,
+                            ms->smp.cpus, false);
+
+    riscv_aclint_mtimer_create(
+        memmap[CVA6_CLINT].base + RISCV_ACLINT_SWI_SIZE,
+        RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
+        RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
+        CLINT_TIMEBASE_FREQ, true);
+
+    /* something in cva6-sdk uboot seems to prod the debug
+     * unit by accident, so make it not implemented */
+    not_implemented("debug", &memmap[CVA6_DEBUG]);
+
+    /* 16550 uart, one 32bit register per 32bit word */
+
+    serial_mm_init(system_memory, memmap[CVA6_UART].base, 2,
+                   qdev_get_gpio_in(DEVICE(s->plic), CVA6_UART_IRQ),
+                   50*1000*10000,
+                   serial_hd(0), DEVICE_LITTLE_ENDIAN);
+
+    /* just unimplement the timers, network and gpio here for now.
+     * no-one seems to be using the apb timer block anyway*/
+    not_implemented("net", &memmap[CVA6_ETHERNET]);
+    not_implemented("gpio", &memmap[CVA6_GPIO]);
+    not_implemented("timer", &memmap[CVA6_TIMER]);
+
+    /* connect xilinx spi block here */
+    cva6_add_spi(s, &memmap[CVA6_SPI]);
+}
+
+static void cva6_soc_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = cva6_soc_realize;
+    dc->user_creatable = false;
+};
+
+static const TypeInfo cva6_types[] = {
+    {
+        .name           = TYPE_RISCV_CVA6,
+        .parent         = TYPE_DEVICE,
+        .instance_size  = sizeof(CVA6SoCState),
+        .instance_init  = cva6_soc_init,
+        .class_init     = cva6_soc_class_init,
+    }, {
+        .name           = TYPE_CVA6_MACHINE,
+        .parent         = TYPE_MACHINE,
+        .instance_size  = sizeof(CVA6State),
+        .class_init     = cva6_machine_class_init,
+    }
+};
+
+DEFINE_TYPES(cva6_types)
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index c22f3a7216..a32fffab63 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'))
 riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c'))
 riscv_ss.add(files('riscv_hart.c'))
+riscv_ss.add(when: 'CONFIG_CVA6', if_true: files('cva6.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_SHAKTI_C', if_true: files('shakti_c.c'))
diff --git a/include/hw/riscv/cva6.h b/include/hw/riscv/cva6.h
new file mode 100644
index 0000000000..48e0979a0a
--- /dev/null
+++ b/include/hw/riscv/cva6.h
@@ -0,0 +1,88 @@
+/*
+ * QEMU RISC-V Board for OpenHW CVA6 SoC
+ * https://github.com/openhwgroup/cva6/tree/master/corev_apu
+ *
+ * Copyright (c) 2025 Codethink Ltd
+ * Ben Dooks <ben.dooks@codethink.co.uk>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_CVA6_H
+#define HW_CVA6_H
+
+#include "hw/riscv/riscv_hart.h"
+#include "hw/intc/sifive_plic.h"
+#include "hw/char/serial-mm.h"
+
+#include "hw/boards.h"
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_RISCV_CVA6 "riscv.cva6.soc"
+OBJECT_DECLARE_SIMPLE_TYPE(CVA6SoCState, RISCV_CVA6)
+
+typedef struct CVA6SoCState {
+    /*< private >*/
+    DeviceState parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState cpus;
+    DeviceState *plic;
+    MemoryRegion rom;
+
+    uint32_t resetvec;
+} CVA6SoCState;
+
+#define TYPE_CVA6_MACHINE MACHINE_TYPE_NAME("cva6")
+OBJECT_DECLARE_SIMPLE_TYPE(CVA6State, CVA6_MACHINE)
+
+typedef struct CVA6State {
+    /*< private >*/
+    MachineState parent_obj;
+
+    /*< public >*/
+    CVA6SoCState soc;
+}
+CVA6State;
+
+enum {
+    CVA6_DEBUG,
+    CVA6_ROM,
+    CVA6_CLINT,
+    CVA6_PLIC,
+    CVA6_UART,
+    CVA6_TIMER,
+    CVA6_SPI,
+    CVA6_ETHERNET,
+    CVA6_GPIO,
+    CVA6_DRAM,
+};
+
+enum {
+    CVA6_UART_IRQ       = 1,
+    CVA6_SPI_IRQ        = 2,
+    CVA6_ETH_IRQ        = 3,
+    CVA6_TIMER0_OVF_IRQ = 4,
+    CVA6_TIMER0_CMP_IRQ = 5,
+    CVA6_TIMER1_OVF_IRQ = 6,
+    CVA6_TIMER1_CMP_IRQ = 7,
+};
+
+#define CLINT_TIMEBASE_FREQ 25000000
+
+/*
+ * plic register interface in corev_apu/rv_plic/rtl/plic_regmap.sv
+ * https://github.com/pulp-platform/rv_plic/blob/master/rtl/plic_regmap.sv
+*/
+
+#define CVA6_PLIC_NUM_SOURCES           32
+#define CVA6_PLIC_NUM_PRIORITIES        7
+#define CVA6_PLIC_PRIORITY_BASE         0x0000
+#define CVA6_PLIC_PENDING_BASE          0x1000
+#define CVA6_PLIC_ENABLE_BASE           0x2000
+#define CVA6_PLIC_ENABLE_STRIDE         0x80
+#define CVA6_PLIC_CONTEXT_BASE          0x200000
+#define CVA6_PLIC_CONTEXT_STRIDE        0x1000
+
+#endif /* HW_CVA6_H */
-- 
2.37.2.352.g3c44437643



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

* [PATCH v3 2/3] target/riscv: add cva6 core type
  2025-06-09 13:17 RISC-V: Add CVA6 machine Ben Dooks
  2025-06-09 13:17 ` [PATCH v3 1/3] hw/riscv: add " Ben Dooks
@ 2025-06-09 13:17 ` Ben Dooks
  2025-06-09 23:30   ` Alistair Francis
  2025-06-09 13:17 ` [PATCH v3 3/3] hw/riscv: set cva6 to use TYPE_RISCV_CPU_CVA6 Ben Dooks
  2 siblings, 1 reply; 7+ messages in thread
From: Ben Dooks @ 2025-06-09 13:17 UTC (permalink / raw)
  To: nazar.kazakov, joseph.baker, fran.redondo, lawrence.hunter,
	liwei1518, dbarboza, zhiwei_liu, alistair.francis, qemu-riscv
  Cc: ben.dooks, qemu-devel

Add TYPE_RISCV_CPU_CVA6 for the CVA6 core

Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
 target/riscv/cpu-qom.h |  1 +
 target/riscv/cpu.c     | 11 +++++++++++
 2 files changed, 12 insertions(+)

diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h
index 1ee05eb393..3daf75568c 100644
--- a/target/riscv/cpu-qom.h
+++ b/target/riscv/cpu-qom.h
@@ -34,6 +34,7 @@
 #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
 #define TYPE_RISCV_CPU_BASE64           RISCV_CPU_TYPE_NAME("rv64")
 #define TYPE_RISCV_CPU_BASE128          RISCV_CPU_TYPE_NAME("x-rv128")
+#define TYPE_RISCV_CPU_CVA6             RISCV_CPU_TYPE_NAME("cva6")
 #define TYPE_RISCV_CPU_RV32I            RISCV_CPU_TYPE_NAME("rv32i")
 #define TYPE_RISCV_CPU_RV32E            RISCV_CPU_TYPE_NAME("rv32e")
 #define TYPE_RISCV_CPU_RV64I            RISCV_CPU_TYPE_NAME("rv64i")
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 629ac37501..fca45dc9d9 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -3009,6 +3009,17 @@ static const TypeInfo riscv_cpu_type_infos[] = {
         .misa_mxl_max = MXL_RV64,
     ),
 
+    DEFINE_RISCV_CPU(TYPE_RISCV_CPU_CVA6, TYPE_RISCV_VENDOR_CPU,
+        .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVB | RVS | RVU,
+        .misa_mxl_max = MXL_RV64,
+        .cfg.max_satp_mode = VM_1_10_SV39,
+        .priv_spec = PRIV_VERSION_1_12_0,
+        .cfg.pmp = true,
+        .cfg.mmu = true,
+        .cfg.ext_zifencei = true,
+        .cfg.ext_zicsr = true,
+    ),
+
     DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E51, TYPE_RISCV_CPU_SIFIVE_E,
         .misa_mxl_max = MXL_RV64
     ),
-- 
2.37.2.352.g3c44437643



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

* [PATCH v3 3/3] hw/riscv: set cva6 to use TYPE_RISCV_CPU_CVA6
  2025-06-09 13:17 RISC-V: Add CVA6 machine Ben Dooks
  2025-06-09 13:17 ` [PATCH v3 1/3] hw/riscv: add " Ben Dooks
  2025-06-09 13:17 ` [PATCH v3 2/3] target/riscv: add cva6 core type Ben Dooks
@ 2025-06-09 13:17 ` Ben Dooks
  2025-06-09 23:30   ` Alistair Francis
  2 siblings, 1 reply; 7+ messages in thread
From: Ben Dooks @ 2025-06-09 13:17 UTC (permalink / raw)
  To: nazar.kazakov, joseph.baker, fran.redondo, lawrence.hunter,
	liwei1518, dbarboza, zhiwei_liu, alistair.francis, qemu-riscv
  Cc: ben.dooks, qemu-devel

Change to using TYPE_RISCV_CPU_CVA6 once this is merged.

Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
 hw/riscv/cva6.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/hw/riscv/cva6.c b/hw/riscv/cva6.c
index 3adfa8b5cc..e6fd0ebafc 100644
--- a/hw/riscv/cva6.c
+++ b/hw/riscv/cva6.c
@@ -83,8 +83,7 @@ static void cva6_machine_class_init(ObjectClass *oc, const void *data)
     mc->init = cva6_machine_init;
     mc->max_cpus = 1;
     mc->default_ram_id = "cva6.ram";
-    /* start with "max" cpu type until we sort out CVA6 type */
-    mc->default_cpu_type = TYPE_RISCV_CPU_MAX;
+    mc->default_cpu_type = TYPE_RISCV_CPU_CVA6;
     mc->default_ram_size = cva6_memmap[CVA6_DRAM].size;
 };
 
-- 
2.37.2.352.g3c44437643



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

* Re: [PATCH v3 1/3] hw/riscv: add CVA6 machine
  2025-06-09 13:17 ` [PATCH v3 1/3] hw/riscv: add " Ben Dooks
@ 2025-06-09 23:29   ` Alistair Francis
  0 siblings, 0 replies; 7+ messages in thread
From: Alistair Francis @ 2025-06-09 23:29 UTC (permalink / raw)
  To: Ben Dooks
  Cc: nazar.kazakov, joseph.baker, fran.redondo, lawrence.hunter,
	liwei1518, dbarboza, zhiwei_liu, alistair.francis, qemu-riscv,
	qemu-devel

On Mon, Jun 9, 2025 at 11:19 PM Ben Dooks <ben.dooks@codethink.co.uk> wrote:
>
> Add a (currently Genesy2 based) CVA6 machine.
>
> Has SPI and UART, the GPIO and Ethernet are currently black-holed
> as there is no hardware model for them (lowRISC ethernet and Xilinx
> GPIO)
>
> Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
> ---
> v3:
> - fix missed plic comment
> - made 64bit only for now
> v2:
> - whitespace fixes
> - use g_autofree on plic
> v1:
> - squashed in fixes for sd-card and new qemu init
> - move to spdx for cva6 machine
> - code cleanups missed in first review
>
> updated plic regmap
>
> make cva6 64bit for now
> ---
>  hw/riscv/Kconfig        |  11 ++
>  hw/riscv/cva6.c         | 229 ++++++++++++++++++++++++++++++++++++++++
>  hw/riscv/meson.build    |   1 +
>  include/hw/riscv/cva6.h |  88 +++++++++++++++
>  4 files changed, 329 insertions(+)
>  create mode 100644 hw/riscv/cva6.c
>  create mode 100644 include/hw/riscv/cva6.h
>
> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> index e6a0ac1fa1..033e29dab1 100644
> --- a/hw/riscv/Kconfig
> +++ b/hw/riscv/Kconfig
> @@ -9,6 +9,17 @@ config IBEX
>
>  # RISC-V machines in alphabetical order
>
> +# technically it might be possible to build cva6 32bit
> +config CVA6
> +    bool
> +    default y
> +    depends on RISCV64
> +    select DEVICE_TREE
> +    select SIFIVE_PLIC
> +    select XILINX_SPI
> +    select RISCV_ACLINT
> +    select UNIMP
> +
>  config MICROCHIP_PFSOC
>      bool
>      default y
> diff --git a/hw/riscv/cva6.c b/hw/riscv/cva6.c
> new file mode 100644
> index 0000000000..3adfa8b5cc
> --- /dev/null
> +++ b/hw/riscv/cva6.c
> @@ -0,0 +1,229 @@
> +/*
> + * QEMU RISC-V Board for OpenHW CVA6 SoC
> + *
> + * Copyright (c) 2025 Codethink Ltd
> + * Ben Dooks <ben.dooks@codethink.co.uk>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qemu/units.h"
> +#include "qapi/error.h"
> +#include "qapi/visitor.h"
> +#include "hw/boards.h"
> +#include "hw/irq.h"
> +#include "hw/loader.h"
> +#include "hw/sysbus.h"
> +#include "hw/misc/unimp.h"
> +
> +#include "hw/sd/sd.h"
> +#include "hw/ssi/ssi.h"
> +
> +#include "hw/riscv/cva6.h"
> +#include "hw/riscv/boot.h"
> +#include "hw/intc/riscv_aclint.h"
> +
> +#include "system/system.h"
> +
> +#include <libfdt.h>
> +
> +#define CVA6_ROM_BASE  0x10000
> +
> +static const MemMapEntry cva6_memmap[] = {
> +    [CVA6_DEBUG] =              {  0x0000000,  0x1000 },
> +    [CVA6_ROM] =                { CVA6_ROM_BASE, 0x10000 },
> +    [CVA6_CLINT] =              {  0x2000000, 0xC0000 },
> +    [CVA6_PLIC] =               {  0xC000000, 0x4000000 },
> +    [CVA6_UART] =               { 0x10000000, 0x1000 },
> +    [CVA6_TIMER] =              { 0x18000000, 0x10000 },
> +    [CVA6_SPI] =                { 0x20000000, 0x800000 },
> +    [CVA6_ETHERNET] =           { 0x30000000, 0x10000 },
> +    [CVA6_GPIO] =               { 0x40000000, 0x1000 },
> +    [CVA6_DRAM] =               { 0x80000000, 0x40000000 },
> +};
> +
> +static void cva6_machine_init(MachineState *machine)
> +{
> +    MachineClass *mc = MACHINE_GET_CLASS(machine);
> +    MemoryRegion *sys_mem = get_system_memory();
> +    hwaddr dram_addr = cva6_memmap[CVA6_DRAM].base;
> +    CVA6State *s = CVA6_MACHINE(machine);
> +    RISCVBootInfo boot_info;
> +
> +    object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_CVA6);
> +    qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
> +
> +    if (machine->ram_size > mc->default_ram_size) {

This should compare against cva6_memmap[CVA6_DRAM].size instead

> +        error_report("RAM size is too big for DRAM area");
> +        exit(EXIT_FAILURE);
> +    }
> +
> +    memory_region_add_subregion(sys_mem, dram_addr, machine->ram);
> +    riscv_boot_info_init(&boot_info, &s->soc.cpus);
> +
> +    if (machine->firmware) {
> +         hwaddr firmware_load_addr = cva6_memmap[CVA6_ROM].base;
> +         riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL);
> +    }
> +
> +     if (machine->kernel_filename) {
> +         /* note - we've not tested just loading the kernel w/o uboot */

I don't think this is a useful comment, users can't see it and it
seems prone to becoming stale.

If you want you can put something like this in the user documentation
(or the wiki page) but it shouldn't be here

> +         riscv_load_kernel(machine, &boot_info, dram_addr, false, NULL);
> +    }
> +
> +}
> +
> +static void cva6_machine_class_init(ObjectClass *oc, const void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "RISC-V board for CVA6";
> +    mc->init = cva6_machine_init;
> +    mc->max_cpus = 1;
> +    mc->default_ram_id = "cva6.ram";
> +    /* start with "max" cpu type until we sort out CVA6 type */
> +    mc->default_cpu_type = TYPE_RISCV_CPU_MAX;
> +    mc->default_ram_size = cva6_memmap[CVA6_DRAM].size;
> +};
> +
> +static void cva6_soc_init(Object *obj)
> +{
> +    CVA6SoCState *s = RISCV_CVA6(obj);
> +
> +    object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
> +}
> +
> +static void cva6_add_spi(CVA6SoCState *s, const MemMapEntry *map)
> +{
> +    DriveInfo *dinfo;
> +    BlockBackend *blk;
> +    DeviceState *card_dev;
> +    qemu_irq sd_cs;
> +    DeviceState *sddev;
> +    SysBusDevice *busdev;
> +    DeviceState *spi_dev;
> +    SSIBus *spi;
> +
> +    spi_dev = qdev_new("xlnx.xps-spi");
> +    qdev_prop_set_uint8(spi_dev, "num-ss-bits", 1);
> +    qdev_prop_set_string(spi_dev, "endianness", "little");
> +
> +    busdev = SYS_BUS_DEVICE(spi_dev);
> +    sysbus_realize_and_unref(busdev, &error_fatal);
> +    sysbus_mmio_map(busdev, 0, map->base);
> +    sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(s->plic), CVA6_SPI_IRQ));
> +
> +    spi = (SSIBus *)qdev_get_child_bus(spi_dev, "spi");
> +
> +    sddev = ssi_create_peripheral(spi, "ssi-sd");
> +    sd_cs = qdev_get_gpio_in_named(sddev, SSI_GPIO_CS, 0);
> +    sysbus_connect_irq(busdev, 1, sd_cs);
> +
> +    dinfo = drive_get(IF_SD, 0, 0);
> +    blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
> +    card_dev = qdev_new(TYPE_SD_CARD_SPI);
> +    qdev_prop_set_drive_err(card_dev, "drive", blk, &error_fatal);
> +
> +    qdev_realize_and_unref(card_dev, qdev_get_child_bus(sddev, "sd-bus"), &error_fatal);
> +}
> +
> +static void not_implemented(const char *name, const MemMapEntry *map)
> +{
> +    create_unimplemented_device(name, map->base, map->size);
> +}
> +
> +static void cva6_soc_realize(DeviceState *dev_soc, Error **errp)
> +{
> +    MemoryRegion *system_memory = get_system_memory();
> +    MachineState *ms = MACHINE(qdev_get_machine());
> +    CVA6SoCState *s = RISCV_CVA6(dev_soc);
> +    const MemMapEntry *memmap = cva6_memmap;
> +    MemoryRegion *rom = g_new(MemoryRegion, 1);
> +    g_autofree char *plic_hart_config;
> +
> +    object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type,
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus,
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->cpus), "resetvec", CVA6_ROM_BASE,
> +                            &error_abort);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal);
> +
> +    /* boot rom */
> +    memory_region_init_rom(rom, OBJECT(dev_soc), "riscv.cva6.bootrom",
> +                           memmap[CVA6_ROM].size, &error_fatal);
> +    memory_region_add_subregion(system_memory, memmap[CVA6_ROM].base,
> +                                rom);
> +
> +    /* create PLIC hart topology configuration string */
> +    plic_hart_config = riscv_plic_hart_config_string(ms->smp.cpus);
> +
> +    /* MMIO */
> +    s->plic = sifive_plic_create(memmap[CVA6_PLIC].base,
> +        plic_hart_config, ms->smp.cpus, 0,
> +        CVA6_PLIC_NUM_SOURCES,
> +        CVA6_PLIC_NUM_PRIORITIES,
> +        CVA6_PLIC_PRIORITY_BASE,
> +        CVA6_PLIC_PENDING_BASE,
> +        CVA6_PLIC_ENABLE_BASE,
> +        CVA6_PLIC_ENABLE_STRIDE,
> +        CVA6_PLIC_CONTEXT_BASE,
> +        CVA6_PLIC_CONTEXT_STRIDE,
> +        memmap[CVA6_PLIC].size);
> +
> +    riscv_aclint_swi_create(memmap[CVA6_CLINT].base, 0,
> +                            ms->smp.cpus, false);
> +
> +    riscv_aclint_mtimer_create(
> +        memmap[CVA6_CLINT].base + RISCV_ACLINT_SWI_SIZE,
> +        RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
> +        RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
> +        CLINT_TIMEBASE_FREQ, true);
> +
> +    /* something in cva6-sdk uboot seems to prod the debug
> +     * unit by accident, so make it not implemented */

The comment should follow the QEMU code style

https://www.qemu.org/docs/master/devel/style.html#comment-style

> +    not_implemented("debug", &memmap[CVA6_DEBUG]);
> +
> +    /* 16550 uart, one 32bit register per 32bit word */
> +
> +    serial_mm_init(system_memory, memmap[CVA6_UART].base, 2,
> +                   qdev_get_gpio_in(DEVICE(s->plic), CVA6_UART_IRQ),
> +                   50*1000*10000,
> +                   serial_hd(0), DEVICE_LITTLE_ENDIAN);
> +
> +    /* just unimplement the timers, network and gpio here for now.
> +     * no-one seems to be using the apb timer block anyway*/
> +    not_implemented("net", &memmap[CVA6_ETHERNET]);
> +    not_implemented("gpio", &memmap[CVA6_GPIO]);
> +    not_implemented("timer", &memmap[CVA6_TIMER]);
> +
> +    /* connect xilinx spi block here */
> +    cva6_add_spi(s, &memmap[CVA6_SPI]);

Is the SPI really part of the SoC? Usually SPI blocks are part of the
machine/board.

Alistair

> +}
> +
> +static void cva6_soc_class_init(ObjectClass *oc, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    dc->realize = cva6_soc_realize;
> +    dc->user_creatable = false;
> +};
> +
> +static const TypeInfo cva6_types[] = {
> +    {
> +        .name           = TYPE_RISCV_CVA6,
> +        .parent         = TYPE_DEVICE,
> +        .instance_size  = sizeof(CVA6SoCState),
> +        .instance_init  = cva6_soc_init,
> +        .class_init     = cva6_soc_class_init,
> +    }, {
> +        .name           = TYPE_CVA6_MACHINE,
> +        .parent         = TYPE_MACHINE,
> +        .instance_size  = sizeof(CVA6State),
> +        .class_init     = cva6_machine_class_init,
> +    }
> +};
> +
> +DEFINE_TYPES(cva6_types)
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index c22f3a7216..a32fffab63 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'))
>  riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c'))
>  riscv_ss.add(files('riscv_hart.c'))
> +riscv_ss.add(when: 'CONFIG_CVA6', if_true: files('cva6.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_SHAKTI_C', if_true: files('shakti_c.c'))
> diff --git a/include/hw/riscv/cva6.h b/include/hw/riscv/cva6.h
> new file mode 100644
> index 0000000000..48e0979a0a
> --- /dev/null
> +++ b/include/hw/riscv/cva6.h
> @@ -0,0 +1,88 @@
> +/*
> + * QEMU RISC-V Board for OpenHW CVA6 SoC
> + * https://github.com/openhwgroup/cva6/tree/master/corev_apu
> + *
> + * Copyright (c) 2025 Codethink Ltd
> + * Ben Dooks <ben.dooks@codethink.co.uk>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_CVA6_H
> +#define HW_CVA6_H
> +
> +#include "hw/riscv/riscv_hart.h"
> +#include "hw/intc/sifive_plic.h"
> +#include "hw/char/serial-mm.h"
> +
> +#include "hw/boards.h"
> +#include "hw/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_RISCV_CVA6 "riscv.cva6.soc"
> +OBJECT_DECLARE_SIMPLE_TYPE(CVA6SoCState, RISCV_CVA6)
> +
> +typedef struct CVA6SoCState {
> +    /*< private >*/
> +    DeviceState parent_obj;
> +
> +    /*< public >*/
> +    RISCVHartArrayState cpus;
> +    DeviceState *plic;
> +    MemoryRegion rom;
> +
> +    uint32_t resetvec;
> +} CVA6SoCState;
> +
> +#define TYPE_CVA6_MACHINE MACHINE_TYPE_NAME("cva6")
> +OBJECT_DECLARE_SIMPLE_TYPE(CVA6State, CVA6_MACHINE)
> +
> +typedef struct CVA6State {
> +    /*< private >*/
> +    MachineState parent_obj;
> +
> +    /*< public >*/
> +    CVA6SoCState soc;
> +}
> +CVA6State;
> +
> +enum {
> +    CVA6_DEBUG,
> +    CVA6_ROM,
> +    CVA6_CLINT,
> +    CVA6_PLIC,
> +    CVA6_UART,
> +    CVA6_TIMER,
> +    CVA6_SPI,
> +    CVA6_ETHERNET,
> +    CVA6_GPIO,
> +    CVA6_DRAM,
> +};
> +
> +enum {
> +    CVA6_UART_IRQ       = 1,
> +    CVA6_SPI_IRQ        = 2,
> +    CVA6_ETH_IRQ        = 3,
> +    CVA6_TIMER0_OVF_IRQ = 4,
> +    CVA6_TIMER0_CMP_IRQ = 5,
> +    CVA6_TIMER1_OVF_IRQ = 6,
> +    CVA6_TIMER1_CMP_IRQ = 7,
> +};
> +
> +#define CLINT_TIMEBASE_FREQ 25000000
> +
> +/*
> + * plic register interface in corev_apu/rv_plic/rtl/plic_regmap.sv
> + * https://github.com/pulp-platform/rv_plic/blob/master/rtl/plic_regmap.sv
> +*/
> +
> +#define CVA6_PLIC_NUM_SOURCES           32
> +#define CVA6_PLIC_NUM_PRIORITIES        7
> +#define CVA6_PLIC_PRIORITY_BASE         0x0000
> +#define CVA6_PLIC_PENDING_BASE          0x1000
> +#define CVA6_PLIC_ENABLE_BASE           0x2000
> +#define CVA6_PLIC_ENABLE_STRIDE         0x80
> +#define CVA6_PLIC_CONTEXT_BASE          0x200000
> +#define CVA6_PLIC_CONTEXT_STRIDE        0x1000
> +
> +#endif /* HW_CVA6_H */
> --
> 2.37.2.352.g3c44437643
>
>


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

* Re: [PATCH v3 2/3] target/riscv: add cva6 core type
  2025-06-09 13:17 ` [PATCH v3 2/3] target/riscv: add cva6 core type Ben Dooks
@ 2025-06-09 23:30   ` Alistair Francis
  0 siblings, 0 replies; 7+ messages in thread
From: Alistair Francis @ 2025-06-09 23:30 UTC (permalink / raw)
  To: Ben Dooks
  Cc: nazar.kazakov, joseph.baker, fran.redondo, lawrence.hunter,
	liwei1518, dbarboza, zhiwei_liu, alistair.francis, qemu-riscv,
	qemu-devel

On Mon, Jun 9, 2025 at 11:18 PM Ben Dooks <ben.dooks@codethink.co.uk> wrote:
>
> Add TYPE_RISCV_CPU_CVA6 for the CVA6 core
>
> Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>

Acked-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  target/riscv/cpu-qom.h |  1 +
>  target/riscv/cpu.c     | 11 +++++++++++
>  2 files changed, 12 insertions(+)
>
> diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h
> index 1ee05eb393..3daf75568c 100644
> --- a/target/riscv/cpu-qom.h
> +++ b/target/riscv/cpu-qom.h
> @@ -34,6 +34,7 @@
>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
>  #define TYPE_RISCV_CPU_BASE64           RISCV_CPU_TYPE_NAME("rv64")
>  #define TYPE_RISCV_CPU_BASE128          RISCV_CPU_TYPE_NAME("x-rv128")
> +#define TYPE_RISCV_CPU_CVA6             RISCV_CPU_TYPE_NAME("cva6")
>  #define TYPE_RISCV_CPU_RV32I            RISCV_CPU_TYPE_NAME("rv32i")
>  #define TYPE_RISCV_CPU_RV32E            RISCV_CPU_TYPE_NAME("rv32e")
>  #define TYPE_RISCV_CPU_RV64I            RISCV_CPU_TYPE_NAME("rv64i")
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 629ac37501..fca45dc9d9 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -3009,6 +3009,17 @@ static const TypeInfo riscv_cpu_type_infos[] = {
>          .misa_mxl_max = MXL_RV64,
>      ),
>
> +    DEFINE_RISCV_CPU(TYPE_RISCV_CPU_CVA6, TYPE_RISCV_VENDOR_CPU,
> +        .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVB | RVS | RVU,
> +        .misa_mxl_max = MXL_RV64,
> +        .cfg.max_satp_mode = VM_1_10_SV39,
> +        .priv_spec = PRIV_VERSION_1_12_0,
> +        .cfg.pmp = true,
> +        .cfg.mmu = true,
> +        .cfg.ext_zifencei = true,
> +        .cfg.ext_zicsr = true,
> +    ),
> +
>      DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E51, TYPE_RISCV_CPU_SIFIVE_E,
>          .misa_mxl_max = MXL_RV64
>      ),
> --
> 2.37.2.352.g3c44437643
>
>


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

* Re: [PATCH v3 3/3] hw/riscv: set cva6 to use TYPE_RISCV_CPU_CVA6
  2025-06-09 13:17 ` [PATCH v3 3/3] hw/riscv: set cva6 to use TYPE_RISCV_CPU_CVA6 Ben Dooks
@ 2025-06-09 23:30   ` Alistair Francis
  0 siblings, 0 replies; 7+ messages in thread
From: Alistair Francis @ 2025-06-09 23:30 UTC (permalink / raw)
  To: Ben Dooks
  Cc: nazar.kazakov, joseph.baker, fran.redondo, lawrence.hunter,
	liwei1518, dbarboza, zhiwei_liu, alistair.francis, qemu-riscv,
	qemu-devel

On Mon, Jun 9, 2025 at 11:20 PM Ben Dooks <ben.dooks@codethink.co.uk> wrote:
>
> Change to using TYPE_RISCV_CPU_CVA6 once this is merged.

You can also just change the patch order to not require this patch

>
> Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  hw/riscv/cva6.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/hw/riscv/cva6.c b/hw/riscv/cva6.c
> index 3adfa8b5cc..e6fd0ebafc 100644
> --- a/hw/riscv/cva6.c
> +++ b/hw/riscv/cva6.c
> @@ -83,8 +83,7 @@ static void cva6_machine_class_init(ObjectClass *oc, const void *data)
>      mc->init = cva6_machine_init;
>      mc->max_cpus = 1;
>      mc->default_ram_id = "cva6.ram";
> -    /* start with "max" cpu type until we sort out CVA6 type */
> -    mc->default_cpu_type = TYPE_RISCV_CPU_MAX;
> +    mc->default_cpu_type = TYPE_RISCV_CPU_CVA6;
>      mc->default_ram_size = cva6_memmap[CVA6_DRAM].size;
>  };
>
> --
> 2.37.2.352.g3c44437643
>
>


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

end of thread, other threads:[~2025-06-09 23:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-09 13:17 RISC-V: Add CVA6 machine Ben Dooks
2025-06-09 13:17 ` [PATCH v3 1/3] hw/riscv: add " Ben Dooks
2025-06-09 23:29   ` Alistair Francis
2025-06-09 13:17 ` [PATCH v3 2/3] target/riscv: add cva6 core type Ben Dooks
2025-06-09 23:30   ` Alistair Francis
2025-06-09 13:17 ` [PATCH v3 3/3] hw/riscv: set cva6 to use TYPE_RISCV_CPU_CVA6 Ben Dooks
2025-06-09 23:30   ` Alistair Francis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).