Add support for the Milk-V Duo development board, which is powered by the Sophgo CV1800B SoC.
The implementation includes: - Board-level machine initialization with 64mb of default ram. - Integration of the CV1800B SoC. - Support for loading external FDT, kernel, and initrd images. - Proper setup of the reset vector to match the CV1800B's boot flow. Signed-off-by: Kuan-Wei Chiu <[email protected]> --- MAINTAINERS | 11 ++ configs/devices/riscv64-softmmu/default.mak | 1 + hw/riscv/Kconfig | 6 + hw/riscv/meson.build | 1 + hw/riscv/milkv_duo.c | 122 ++++++++++++++++++++ 5 files changed, 141 insertions(+) create mode 100644 hw/riscv/milkv_duo.c diff --git a/MAINTAINERS b/MAINTAINERS index afa178c5cc..a5649fd31f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1780,6 +1780,17 @@ F: docs/system/riscv/xiangshan-kunminghu.rst F: hw/riscv/xiangshan_kmh.c F: include/hw/riscv/xiangshan_kmh.h +Milk-V Duo +M: Kuan-Wei Chiu <[email protected]> +S: Maintained +F: hw/char/dw8250.c +F: hw/misc/cv1800b_clk.c +F: hw/riscv/cv1800b.c +F: hw/riscv/milkv_duo.c +F: include/hw/char/dw8250.h +F: include/hw/misc/cv1800b_clk.h +F: include/hw/riscv/cv1800b.h + RX Machines ----------- rx-gdbsim diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index a8e4d0ab33..2ba91b14d4 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -13,3 +13,4 @@ # CONFIG_SHAKTI_C=n # CONFIG_XIANGSHAN_KUNMINGHU=n # CONFIG_MIPS_BOSTON_AIA=n +# CONFIG_MILKV_DUO=n diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 5b68991edb..8615b1cc3e 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -146,3 +146,9 @@ config SOPHGO_CV1800B select SIFIVE_PLIC select SOPHGO_CV1800B_CLK select DW8250 + +config MILKV_DUO + bool + depends on RISCV64 + default y + select SOPHGO_CV1800B diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 04e25eeece..19a0bf8e5b 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -19,5 +19,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_MIPS_CPS', if_true: files('cps.c')) riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: files('boston-aia.c')) riscv_ss.add(when: 'CONFIG_SOPHGO_CV1800B', if_true: files('cv1800b.c')) +riscv_ss.add(when: 'CONFIG_MILKV_DUO', if_true: files('milkv_duo.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/milkv_duo.c b/hw/riscv/milkv_duo.c new file mode 100644 index 0000000000..5e8d5ea009 --- /dev/null +++ b/hw/riscv/milkv_duo.c @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Milk-V Duo board + * + * Copyright (c) 2026 Kuan-Wei Chiu <[email protected]> + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "hw/core/boards.h" +#include "hw/riscv/cv1800b.h" +#include "hw/riscv/boot.h" +#include "target/riscv/cpu-qom.h" +#include "system/system.h" +#include "system/device_tree.h" +#include "qemu/error-report.h" +#include "hw/core/loader.h" +#include <libfdt.h> + +struct MilkVDuoState { + MachineState parent_obj; + CV1800BSoCState soc; +}; + +#define TYPE_MILK_V_DUO MACHINE_TYPE_NAME("milkv-duo") +OBJECT_DECLARE_SIMPLE_TYPE(MilkVDuoState, MILK_V_DUO) + +static void milkv_duo_init(MachineState *machine) +{ + MilkVDuoState *s = MILK_V_DUO(machine); + MemoryRegion *system_memory = get_system_memory(); + RISCVBootInfo boot_info; + hwaddr firmware_load_addr, firmware_end_addr; + hwaddr fdt_load_addr = 0; + int fdt_size = 0; + uint64_t kernel_entry = 0; + + object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_CV1800B_SOC); + qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + + memory_region_add_subregion(system_memory, + cv1800b_memmap[CV1800B_DEV_DRAM].base, + machine->ram); + + riscv_boot_info_init(&boot_info, &s->soc.cpus); + + firmware_load_addr = cv1800b_memmap[CV1800B_DEV_DRAM].base; + firmware_end_addr = firmware_load_addr; + if (machine->firmware) { + firmware_end_addr = riscv_find_and_load_firmware(machine, machine->firmware, + &firmware_load_addr, NULL); + } + + if (machine->dtb) { + machine->fdt = load_device_tree(machine->dtb, &fdt_size); + if (!machine->fdt) { + error_report("Failed to load device tree"); + exit(1); + } + + if (machine->kernel_cmdline && *machine->kernel_cmdline) { + if (fdt_path_offset(machine->fdt, "/chosen") < 0) { + qemu_fdt_add_subnode(machine->fdt, "/chosen"); + } + qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs", + machine->kernel_cmdline); + } + } + + if (machine->kernel_filename) { + hwaddr kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, + firmware_end_addr); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, true, NULL); + kernel_entry = boot_info.image_low_addr; + } + + if (machine->dtb) { + fdt_load_addr = riscv_compute_fdt_addr(cv1800b_memmap[CV1800B_DEV_DRAM].base, + machine->ram_size, machine, &boot_info); + rom_add_blob_fixed_as("fdt", machine->fdt, fdt_size, fdt_load_addr, + &address_space_memory); + } + + riscv_setup_rom_reset_vec(machine, &s->soc.cpus, + firmware_load_addr, + cv1800b_memmap[CV1800B_DEV_ROM].base, + cv1800b_memmap[CV1800B_DEV_ROM].size, + kernel_entry, + fdt_load_addr); +} + +static void milkv_duo_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + static const char *const valid_cpu_types[] = { + TYPE_RISCV_CPU_THEAD_C906, + NULL + }; + + mc->desc = "Milk-V Duo Board (CV1800B)"; + mc->init = milkv_duo_init; + mc->max_cpus = 2; + mc->default_cpu_type = TYPE_RISCV_CPU_THEAD_C906; + mc->valid_cpu_types = valid_cpu_types; + mc->default_ram_size = 64 * MiB; + mc->default_ram_id = "riscv.milkv_duo.ram"; +} + +static const TypeInfo milkv_duo_machine_type_info = { + .name = TYPE_MILK_V_DUO, + .parent = TYPE_MACHINE, + .instance_size = sizeof(MilkVDuoState), + .class_init = milkv_duo_machine_class_init, +}; + +static void milkv_duo_machine_register_types(void) +{ + type_register_static(&milkv_duo_machine_type_info); +} + +type_init(milkv_duo_machine_register_types) -- 2.54.0.563.g4f69b47b94-goog
