Hi Daniel, Thanks a lot for your comments!
On 18. 6. 25. 20:16, Daniel Henrique Barboza wrote: > [You don't often get email from dbarb...@ventanamicro.com. Learn why > this is important at https://aka.ms/LearnAboutSenderIdentification ] > > CAUTION: This email originated from outside of the organization. Do > not click links or open attachments unless you recognize the sender > and know the content is safe. > > > On 6/18/25 9:27 AM, Djordje Todorovic wrote: >> The board model supports up to 64 harts with MIPS CPS, MIPS GCR, >> MIPS CPC, AIA plic, and AIA clint devices. The model can create >> boot code, if there is no -bios parameter. We can specify -smp x, >> cores=y,thread=z. Ex: Use 4 cores and 2 threads with each core to >> have 8 smp cpus as follows. >> qemu-system-riscv64 -cpu mips-p8700 \ >> -m 2G -M boston-aia \ >> -smp 8,cores=4,threads=2 -kernel fw_payload.bin \ >> -drive file=rootfs.ext2,format=raw -serial stdio >> >> Signed-off-by: Chao-ying Fu <c...@mips.com> >> Signed-off-by: Djordje Todorovic <djordje.todoro...@htecgroup.com> >> --- > > > > > The changes in docs/system/riscv/mips.rst breaks the build: > > > $ ninja -C build > ninja: Entering directory `build' > [25/26] Generating docs/QEMU manual with a custom command > FAILED: docs/docs.stamp > /usr/bin/env CONFDIR=etc/qemu > /home/danielhb/work/qemu/build/pyvenv/bin/sphinx-build -q -W > -Dkerneldoc_werror=1 -j auto -Dversion=10.0.50 -Drelease= > -Ddepfile=docs/docs.d -Ddepfile_stamp=docs/docs.stamp -b html -d > /home/danielhb/work/qemu/build/docs/manual.p > /home/danielhb/work/qemu/docs /home/danielhb/work/qemu/build/docs/manual > /home/danielhb/work/qemu/docs/system/riscv/mips.rst:22: ERROR: > Unexpected indentation. [docutils] > /home/danielhb/work/qemu/docs/system/riscv/mips.rst:20: WARNING: > Inline literal start-string without end-string. [docutils] > /home/danielhb/work/qemu/docs/system/riscv/mips.rst:25: WARNING: Block > quote ends without a blank line; unexpected unindent. [docutils] > /home/danielhb/work/qemu/docs/system/riscv/mips.rst:25: WARNING: > Inline literal start-string without end-string. [docutils] > /home/danielhb/work/qemu/docs/system/riscv/mips.rst:25: WARNING: > Inline interpreted text or phrase reference start-string without > end-string. [docutils] > /home/danielhb/work/qemu/docs/system/riscv/mips.rst:12: WARNING: 'any' > reference target not found: Boston-aia [ref.any] > ninja: build stopped: subcommand failed. > $ > > This diff fixes it: > > $ git diff > diff --git a/docs/system/riscv/mips.rst b/docs/system/riscv/mips.rst > index 12f1cfa94c..61a04c97ca 100644 > --- a/docs/system/riscv/mips.rst > +++ b/docs/system/riscv/mips.rst > @@ -9,7 +9,7 @@ and AIA clint devices. The model can create boot code, > if there is no > Enable Boston-aia > ----------------- > > -To build qemu with support for `Boston-aia` set > ```CONFIG_MIPS_BOSTON_AIA=y```. > +To build qemu with support for ``Boston-aia`` set > ```CONFIG_MIPS_BOSTON_AIA=y```. > > Running Linux kernel > -------------------- > @@ -17,9 +17,9 @@ Running Linux kernel > For example, to use 4 cores and 2 threads with each core to have 8 > smp cpus, > that runs on the ```mips-p8700``` CPU, run qemu as follows: > > -``` > +.. code-block:: bash > + > qemu-system-riscv64 -cpu mips-p8700 \ > -m 2G -M boston-aia \ > -smp 8,cores=4,threads=2 -kernel fw_payload.bin \ > -drive file=rootfs.ext2,format=raw -serial stdio > -``` > > > As for the rest, we don't have a strict rule on patch size but, if you > could add an > pre-patch with all the hw/misc changes first, then this patch with the > remaining > changes, that would be terrific. > > Yes, I will do it in the v4. Thanks! > Thanks, > > Daniel > >> configs/devices/riscv64-softmmu/default.mak | 1 + >> docs/system/riscv/mips.rst | 25 + >> docs/system/target-riscv.rst | 1 + >> hw/misc/Kconfig | 5 + >> hw/misc/meson.build | 1 + >> hw/misc/riscv_cmgcr.c | 206 +++++++++ >> hw/misc/riscv_cpc.c | 225 +++++++++ >> hw/riscv/Kconfig | 6 + >> hw/riscv/boston-aia.c | 484 ++++++++++++++++++++ >> hw/riscv/cps.c | 187 ++++++++ >> hw/riscv/meson.build | 1 + >> include/hw/misc/riscv_cmgcr.h | 77 ++++ >> include/hw/misc/riscv_cpc.h | 69 +++ >> include/hw/riscv/cps.h | 75 +++ >> 14 files changed, 1363 insertions(+) >> create mode 100644 docs/system/riscv/mips.rst >> create mode 100644 hw/misc/riscv_cmgcr.c >> create mode 100644 hw/misc/riscv_cpc.c >> create mode 100644 hw/riscv/boston-aia.c >> create mode 100644 hw/riscv/cps.c >> create mode 100644 include/hw/misc/riscv_cmgcr.h >> create mode 100644 include/hw/misc/riscv_cpc.h >> create mode 100644 include/hw/riscv/cps.h >> >> diff --git a/configs/devices/riscv64-softmmu/default.mak >> b/configs/devices/riscv64-softmmu/default.mak >> index 39ed3a0061..2f4f92b978 100644 >> --- a/configs/devices/riscv64-softmmu/default.mak >> +++ b/configs/devices/riscv64-softmmu/default.mak >> @@ -11,3 +11,4 @@ >> # CONFIG_RISCV_VIRT=n >> # CONFIG_MICROCHIP_PFSOC=n >> # CONFIG_SHAKTI_C=n >> +# CONFIG_MIPS_BOSTON_AIA=n >> diff --git a/docs/system/riscv/mips.rst b/docs/system/riscv/mips.rst >> new file mode 100644 >> index 0000000000..12f1cfa94c >> --- /dev/null >> +++ b/docs/system/riscv/mips.rst >> @@ -0,0 +1,25 @@ >> +Boards for RISC-V Processors by MIPS >> +==================================== >> + >> +RISC-V processors developed by MIPS support Boston-aia board model. >> The board >> +model supports up to 64 harts with MIPS CPS, MIPS GCR, MIPS CPC, AIA >> plic, >> +and AIA clint devices. The model can create boot code, if there is no >> +```-bios``` parameter. Also, we can specify ```-smp >> x,cores=y,thread=z```. >> + >> +Enable Boston-aia >> +----------------- >> + >> +To build qemu with support for `Boston-aia` set >> ```CONFIG_MIPS_BOSTON_AIA=y```. >> + >> +Running Linux kernel >> +-------------------- >> + >> +For example, to use 4 cores and 2 threads with each core to have 8 >> smp cpus, >> +that runs on the ```mips-p8700``` CPU, run qemu as follows: >> + >> +``` >> +qemu-system-riscv64 -cpu mips-p8700 \ >> + -m 2G -M boston-aia \ >> + -smp 8,cores=4,threads=2 -kernel fw_payload.bin \ >> + -drive file=rootfs.ext2,format=raw -serial stdio >> +``` >> diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst >> index 95457af130..9e11bb25c9 100644 >> --- a/docs/system/target-riscv.rst >> +++ b/docs/system/target-riscv.rst >> @@ -68,6 +68,7 @@ undocumented; you can get a complete list by running >> >> riscv/microblaze-v-generic >> riscv/microchip-icicle-kit >> + riscv/mips >> riscv/shakti-c >> riscv/sifive_u >> riscv/virt >> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig >> index ec0fa5aa9f..65a89637ed 100644 >> --- a/hw/misc/Kconfig >> +++ b/hw/misc/Kconfig >> @@ -108,6 +108,11 @@ config STM32L4X5_RCC >> config MIPS_ITU >> bool >> >> +config MIPS_BOSTON_AIA >> + bool >> + default n >> + depends on RISCV64 >> + >> config MPS2_FPGAIO >> bool >> select LED >> diff --git a/hw/misc/meson.build b/hw/misc/meson.build >> index 6d47de482c..05b1c0f3cc 100644 >> --- a/hw/misc/meson.build >> +++ b/hw/misc/meson.build >> @@ -152,6 +152,7 @@ specific_ss.add(when: 'CONFIG_MAC_VIA', if_true: >> files('mac_via.c')) >> >> specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: >> files('mips_cmgcr.c', 'mips_cpc.c')) >> specific_ss.add(when: 'CONFIG_MIPS_ITU', if_true: files('mips_itu.c')) >> +specific_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: >> files('riscv_cmgcr.c', 'riscv_cpc.c')) >> >> system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) >> >> diff --git a/hw/misc/riscv_cmgcr.c b/hw/misc/riscv_cmgcr.c >> new file mode 100644 >> index 0000000000..2954e3b406 >> --- /dev/null >> +++ b/hw/misc/riscv_cmgcr.c >> @@ -0,0 +1,206 @@ >> +/* >> + * This file is subject to the terms and conditions of the GNU >> General Public >> + * License. See the file "COPYING" in the main directory of this >> archive >> + * for more details. >> + * >> + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. >> + * Authors: Sanjay Lal <sanj...@kymasys.com> >> + * >> + * Copyright (C) 2015 Imagination Technologies >> + * >> + * Copyright (C) 2025 MIPS >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + * >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "qemu/log.h" >> +#include "qemu/module.h" >> +#include "hw/sysbus.h" >> +#include "migration/vmstate.h" >> +#include "hw/misc/riscv_cmgcr.h" >> +#include "hw/misc/riscv_cpc.h" >> +#include "hw/qdev-properties.h" >> + >> +#include "cpu.h" >> + >> +#define CM_RESET_VEC 0x1FC00000 >> + >> +static inline bool is_cpc_connected(RISCVGCRState *s) >> +{ >> + return s->cpc_mr != NULL; >> +} >> + >> +static inline void update_cpc_base(RISCVGCRState *gcr, uint64_t val) >> +{ >> + if (is_cpc_connected(gcr)) { >> + gcr->cpc_base = val & GCR_CPC_BASE_MSK; >> + memory_region_transaction_begin(); >> + memory_region_set_address(gcr->cpc_mr, >> + gcr->cpc_base & >> GCR_CPC_BASE_CPCBASE_MSK); >> + memory_region_set_enabled(gcr->cpc_mr, >> + gcr->cpc_base & >> GCR_CPC_BASE_CPCEN_MSK); >> + memory_region_transaction_commit(); >> + } >> +} >> + >> +static inline void update_gcr_base(RISCVGCRState *gcr, uint64_t val) >> +{ >> + gcr->gcr_base = val & GCR_BASE_GCRBASE_MSK; >> + memory_region_set_address(&gcr->iomem, gcr->gcr_base); >> + >> + /* For boston-aia, cpc_base is set to gcr_base + 0x8001 to enable >> + cpc automatically. */ >> + update_cpc_base(gcr, val + 0x8001); >> +} >> + >> +/* Read GCR registers */ >> +static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size) >> +{ >> + RISCVGCRState *gcr = (RISCVGCRState *) opaque; >> + >> + switch (addr) { >> + /* Global Control Block Register */ >> + case GCR_CONFIG_OFS: >> + /* Set PCORES to 0 */ >> + return 0; >> + case GCR_BASE_OFS: >> + return gcr->gcr_base; >> + case GCR_REV_OFS: >> + return gcr->gcr_rev; >> + case GCR_CPC_STATUS_OFS: >> + return is_cpc_connected(gcr); >> + case GCR_L2_CONFIG_OFS: >> + /* L2 BYPASS */ >> + return GCR_L2_CONFIG_BYPASS_MSK; >> + default: >> + qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" >> HWADDR_PRIx >> + "\n", size, addr); >> + return 0; >> + } >> + return 0; >> +} >> + >> +static inline target_ulong get_exception_base(RISCVGCRVPState *vps) >> +{ >> + return vps->reset_base & GCR_CL_RESET_BASE_RESETBASE_MSK; >> +} >> + >> +/* Write GCR registers */ >> +static void gcr_write(void *opaque, hwaddr addr, uint64_t data, >> unsigned size) >> +{ >> + RISCVGCRState *gcr = (RISCVGCRState *)opaque; >> + RISCVGCRVPState *current_vps; >> + int cpu_index, c, h; >> + >> + for (c = 0; c < gcr->num_core; c++) { >> + for (h = 0; h < gcr->num_hart; h++) { >> + if (addr == RISCV_CLCB_OFS + c * 0x100 + h * 8) { >> + cpu_index = c * gcr->num_hart + h; >> + current_vps = &gcr->vps[cpu_index]; >> + current_vps->reset_base = data & GCR_CL_RESET_BASE_MSK; >> + cpu_set_exception_base(cpu_index + gcr->cluster_id * >> + gcr->num_core * gcr->num_hart, >> + get_exception_base(current_vps)); >> + return; >> + } >> + } >> + } >> + >> + switch (addr) { >> + case GCR_BASE_OFS: >> + update_gcr_base(gcr, data); >> + break; >> + default: >> + qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" >> HWADDR_PRIx >> + " 0x%" PRIx64 "\n", size, addr, data); >> + break; >> + } >> +} >> + >> +static const MemoryRegionOps gcr_ops = { >> + .read = gcr_read, >> + .write = gcr_write, >> + .endianness = DEVICE_NATIVE_ENDIAN, >> + .impl = { >> + .max_access_size = 8, >> + }, >> +}; >> + >> +static void riscv_gcr_init(Object *obj) >> +{ >> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); >> + RISCVGCRState *s = RISCV_GCR(obj); >> + >> + memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s, >> + "riscv-gcr", GCR_ADDRSPACE_SZ); >> + sysbus_init_mmio(sbd, &s->iomem); >> +} >> + >> +static void riscv_gcr_reset(DeviceState *dev) >> +{ >> + RISCVGCRState *s = RISCV_GCR(dev); >> + int i; >> + >> + /* Update cpc_base to gcr_base + 0x8001 to enable cpc >> automatically. */ >> + update_cpc_base(s, s->gcr_base + 0x8001); >> + >> + for (i = 0; i < s->num_vps; i++) { >> + s->vps[i].reset_base = CM_RESET_VEC & GCR_CL_RESET_BASE_MSK; >> + cpu_set_exception_base(i, get_exception_base(&s->vps[i])); >> + } >> +} >> + >> +static const VMStateDescription vmstate_riscv_gcr = { >> + .name = "riscv-gcr", >> + .version_id = 0, >> + .minimum_version_id = 0, >> + .fields = (VMStateField[]) { >> + VMSTATE_UINT64(cpc_base, RISCVGCRState), >> + VMSTATE_END_OF_LIST() >> + }, >> +}; >> + >> +static const Property riscv_gcr_properties[] = { >> + DEFINE_PROP_UINT32("cluster-id", RISCVGCRState, cluster_id, 0), >> + DEFINE_PROP_UINT32("num-vp", RISCVGCRState, num_vps, 1), >> + DEFINE_PROP_UINT32("num-hart", RISCVGCRState, num_hart, 1), >> + DEFINE_PROP_UINT32("num-core", RISCVGCRState, num_core, 1), >> + DEFINE_PROP_INT32("gcr-rev", RISCVGCRState, gcr_rev, 0xa00), >> + DEFINE_PROP_UINT64("gcr-base", RISCVGCRState, gcr_base, >> GCR_BASE_ADDR), >> + DEFINE_PROP_LINK("cpc", RISCVGCRState, cpc_mr, TYPE_MEMORY_REGION, >> + MemoryRegion *), >> +}; >> + >> +static void riscv_gcr_realize(DeviceState *dev, Error **errp) >> +{ >> + RISCVGCRState *s = RISCV_GCR(dev); >> + >> + /* Create local set of registers for each VP */ >> + s->vps = g_new(RISCVGCRVPState, s->num_vps); >> +} >> + >> +static void riscv_gcr_class_init(ObjectClass *klass, const void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + device_class_set_props(dc, riscv_gcr_properties); >> + dc->vmsd = &vmstate_riscv_gcr; >> + device_class_set_legacy_reset(dc, riscv_gcr_reset); >> + dc->realize = riscv_gcr_realize; >> +} >> + >> +static const TypeInfo riscv_gcr_info = { >> + .name = TYPE_RISCV_GCR, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(RISCVGCRState), >> + .instance_init = riscv_gcr_init, >> + .class_init = riscv_gcr_class_init, >> +}; >> + >> +static void riscv_gcr_register_types(void) >> +{ >> + type_register_static(&riscv_gcr_info); >> +} >> + >> +type_init(riscv_gcr_register_types) >> diff --git a/hw/misc/riscv_cpc.c b/hw/misc/riscv_cpc.c >> new file mode 100644 >> index 0000000000..0d89de95f2 >> --- /dev/null >> +++ b/hw/misc/riscv_cpc.c >> @@ -0,0 +1,225 @@ >> +/* >> + * Cluster Power Controller emulation >> + * >> + * Copyright (c) 2016 Imagination Technologies >> + * >> + * Copyright (c) 2025 MIPS >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2.1 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + * >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "qapi/error.h" >> +#include "cpu.h" >> +#include "qemu/log.h" >> +#include "qemu/module.h" >> +#include "qemu/timer.h" >> +#include "hw/sysbus.h" >> +#include "migration/vmstate.h" >> + >> +#include "hw/misc/riscv_cpc.h" >> +#include "hw/qdev-properties.h" >> +#include "hw/intc/riscv_aclint.h" >> + >> +static inline uint64_t cpc_vp_run_mask(RISCVCPCState *cpc) >> +{ >> + if (cpc->num_vp == 64) { >> + return 0xffffffffffffffff; >> + } >> + return (1ULL << cpc->num_vp) - 1; >> +} >> + >> +static void riscv_cpu_reset_async_work(CPUState *cs, run_on_cpu_data >> data) >> +{ >> + RISCVCPCState *cpc = (RISCVCPCState *) data.host_ptr; >> + >> + cpu_reset(cs); >> + cs->halted = 0; >> + cpc->vp_running |= 1ULL << cs->cpu_index; >> +} >> + >> +static void cpc_run_vp(RISCVCPCState *cpc, uint64_t vp_run) >> +{ >> + CPUState *cs = first_cpu; >> + >> + CPU_FOREACH(cs) { >> + uint64_t i = 1ULL << cs->cpu_index; >> + if (i & vp_run & ~cpc->vp_running) { >> + /* >> + * To avoid racing with a CPU we are just kicking off. >> + * We do the final bit of preparation for the work in >> + * the target CPUs context. >> + */ >> + async_safe_run_on_cpu(cs, riscv_cpu_reset_async_work, >> + RUN_ON_CPU_HOST_PTR(cpc)); >> + } >> + } >> +} >> + >> +static void cpc_stop_vp(RISCVCPCState *cpc, uint64_t vp_stop) >> +{ >> + CPUState *cs = first_cpu; >> + >> + CPU_FOREACH(cs) { >> + uint64_t i = 1ULL << cs->cpu_index; >> + if (i & vp_stop & cpc->vp_running) { >> + cpu_interrupt(cs, CPU_INTERRUPT_HALT); >> + cpc->vp_running &= ~i; >> + } >> + } >> +} >> + >> +static void cpc_write(void *opaque, hwaddr offset, uint64_t data, >> + unsigned size) >> +{ >> + RISCVCPCState *s = opaque; >> + int cpu_index, c; >> + >> + for (c = 0; c < s->num_core; c++) { >> + cpu_index = c * s->num_hart + >> + s->cluster_id * s->num_core * s->num_hart; >> + if (offset == CPC_CL_BASE_OFS + CPC_VP_RUN_OFS + c * 0x100) { >> + cpc_run_vp(s, (data << cpu_index) & cpc_vp_run_mask(s)); >> + return; >> + } >> + if (offset == CPC_CL_BASE_OFS + CPC_VP_STOP_OFS + c * 0x100) { >> + cpc_stop_vp(s, (data << cpu_index) & cpc_vp_run_mask(s)); >> + return; >> + } >> + } >> + >> + switch (offset) { >> + default: >> + qemu_log_mask(LOG_UNIMP, >> + "%s: Bad offset 0x%x\n", __func__, (int)offset); >> + break; >> + } >> + >> + return; >> +} >> + >> +static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size) >> +{ >> + RISCVCPCState *s = opaque; >> + int c; >> + >> + for (c = 0; c < s->num_core; c++) { >> + if (offset == CPC_CL_BASE_OFS + CPC_STAT_CONF_OFS + c * >> 0x100) { >> + /* Return the state as U6. */ >> + return CPC_Cx_STAT_CONF_SEQ_STATE_U6; >> + } >> + } >> + >> + switch (offset) { >> + case CPC_CM_STAT_CONF_OFS: >> + return CPC_Cx_STAT_CONF_SEQ_STATE_U5; >> + case CPC_MTIME_REG_OFS: >> + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), >> + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, >> + NANOSECONDS_PER_SECOND); >> + return 0; >> + default: >> + qemu_log_mask(LOG_UNIMP, >> + "%s: Bad offset 0x%x\n", __func__, (int)offset); >> + return 0; >> + } >> +} >> + >> +static const MemoryRegionOps cpc_ops = { >> + .read = cpc_read, >> + .write = cpc_write, >> + .endianness = DEVICE_NATIVE_ENDIAN, >> + .impl = { >> + .max_access_size = 8, >> + }, >> +}; >> + >> +static void riscv_cpc_init(Object *obj) >> +{ >> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); >> + RISCVCPCState *s = RISCV_CPC(obj); >> + >> + memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "riscv-cpc", >> + CPC_ADDRSPACE_SZ); >> + sysbus_init_mmio(sbd, &s->mr); >> +} >> + >> +static void riscv_cpc_realize(DeviceState *dev, Error **errp) >> +{ >> + RISCVCPCState *s = RISCV_CPC(dev); >> + >> + if (s->vp_start_running > cpc_vp_run_mask(s)) { >> + error_setg(errp, >> + "incorrect vp_start_running 0x%" PRIx64 " for >> num_vp = %d", >> + s->vp_running, s->num_vp); >> + return; >> + } >> +} >> + >> +static void riscv_cpc_reset(DeviceState *dev) >> +{ >> + RISCVCPCState *s = RISCV_CPC(dev); >> + >> + /* Reflect the fact that all VPs are halted on reset */ >> + s->vp_running = 0; >> + >> + /* Put selected VPs into run state */ >> + cpc_run_vp(s, s->vp_start_running); >> +} >> + >> +static const VMStateDescription vmstate_riscv_cpc = { >> + .name = "riscv-cpc", >> + .version_id = 0, >> + .minimum_version_id = 0, >> + .fields = (VMStateField[]) { >> + VMSTATE_UINT64(vp_running, RISCVCPCState), >> + VMSTATE_END_OF_LIST() >> + }, >> +}; >> + >> +static const Property riscv_cpc_properties[] = { >> + DEFINE_PROP_UINT32("cluster-id", RISCVCPCState, cluster_id, 0x0), >> + DEFINE_PROP_UINT32("num-vp", RISCVCPCState, num_vp, 0x1), >> + DEFINE_PROP_UINT32("num-hart", RISCVCPCState, num_hart, 0x1), >> + DEFINE_PROP_UINT32("num-core", RISCVCPCState, num_core, 0x1), >> + DEFINE_PROP_UINT64("vp-start-running", RISCVCPCState, >> vp_start_running, 0x1), >> +}; >> + >> +static void riscv_cpc_class_init(ObjectClass *klass, const void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->realize = riscv_cpc_realize; >> + device_class_set_legacy_reset(dc, riscv_cpc_reset); >> + dc->vmsd = &vmstate_riscv_cpc; >> + device_class_set_props(dc, riscv_cpc_properties); >> +} >> + >> +static const TypeInfo riscv_cpc_info = { >> + .name = TYPE_RISCV_CPC, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(RISCVCPCState), >> + .instance_init = riscv_cpc_init, >> + .class_init = riscv_cpc_class_init, >> +}; >> + >> +static void riscv_cpc_register_types(void) >> +{ >> + type_register_static(&riscv_cpc_info); >> +} >> + >> +type_init(riscv_cpc_register_types) >> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig >> index e6a0ac1fa1..047c6d8ae7 100644 >> --- a/hw/riscv/Kconfig >> +++ b/hw/riscv/Kconfig >> @@ -119,3 +119,9 @@ config SPIKE >> select HTIF >> select RISCV_ACLINT >> select SIFIVE_PLIC >> + >> +config MIPS_BOSTON_AIA >> + bool >> + default y >> + select PCI_EXPRESS >> + select PCI_EXPRESS_XILINX >> diff --git a/hw/riscv/boston-aia.c b/hw/riscv/boston-aia.c >> new file mode 100644 >> index 0000000000..6ed5c636cc >> --- /dev/null >> +++ b/hw/riscv/boston-aia.c >> @@ -0,0 +1,484 @@ >> +/* >> + * MIPS Boston-aia development board emulation. >> + * >> + * Copyright (c) 2016 Imagination Technologies >> + * >> + * Copyright (c) 2025 MIPS >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2.1 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + * >> +*/ >> + >> +#include "qemu/osdep.h" >> +#include "qemu/units.h" >> + >> +#include "hw/boards.h" >> +#include "hw/char/serial-mm.h" >> +#include "hw/ide/pci.h" >> +#include "hw/ide/ahci-pci.h" >> +#include "hw/loader.h" >> +#include "hw/riscv/cps.h" >> +#include "hw/pci-host/xilinx-pcie.h" >> +#include "hw/qdev-properties.h" >> +#include "qapi/error.h" >> +#include "qemu/error-report.h" >> +#include "qemu/log.h" >> +#include "chardev/char.h" >> +#include "system/address-spaces.h" >> +#include "system/device_tree.h" >> +#include "system/system.h" >> +#include "system/qtest.h" >> +#include "system/runstate.h" >> + >> +#include <libfdt.h> >> +#include "qom/object.h" >> + >> +#define TYPE_MIPS_BOSTON_AIA "mips-boston-aia" >> +typedef struct BostonState BostonState; >> +DECLARE_INSTANCE_CHECKER(BostonState, BOSTON, >> + TYPE_MIPS_BOSTON_AIA) >> + >> +enum { >> + BOSTON_PCIE2, >> + BOSTON_PCIE2_MMIO, >> + BOSTON_PLATREG, >> + BOSTON_UART, >> + BOSTON_LCD, >> + BOSTON_FLASH, >> + BOSTON_HIGHDDR, >> +}; >> + >> +static const MemMapEntry boston_memmap[] = { >> + [BOSTON_PCIE2] = { 0x14000000, 0x2000000 }, >> + [BOSTON_PCIE2_MMIO] = { 0x16000000, 0x100000 }, >> + [BOSTON_PLATREG] = { 0x17ffd000, 0x1000 }, >> + [BOSTON_UART] = { 0x17ffe000, 0x20 }, >> + [BOSTON_LCD] = { 0x17fff000, 0x8 }, >> + [BOSTON_FLASH] = { 0x18000000, 0x8000000 }, >> + [BOSTON_HIGHDDR] = { 0x80000000, 0x0 }, >> +}; >> + >> +/* Interrupt numbers for APLIC. */ >> +#define UART_INT 4 >> +#define PCIE2_INT 7 >> + >> +struct BostonState { >> + SysBusDevice parent_obj; >> + >> + MachineState *mach; >> + RISCVCPSState cps; >> + SerialMM *uart; >> + >> + CharBackend lcd_display; >> + char lcd_content[8]; >> + bool lcd_inited; >> +}; >> + >> +enum boston_plat_reg { >> + PLAT_FPGA_BUILD = 0x00, >> + PLAT_CORE_CL = 0x04, >> + PLAT_WRAPPER_CL = 0x08, >> + PLAT_SYSCLK_STATUS = 0x0c, >> + PLAT_SOFTRST_CTL = 0x10, >> +#define PLAT_SOFTRST_CTL_SYSRESET (1 << 4) >> + PLAT_DDR3_STATUS = 0x14, >> +#define PLAT_DDR3_STATUS_LOCKED (1 << 0) >> +#define PLAT_DDR3_STATUS_CALIBRATED (1 << 2) >> +#define PLAT_DDR3_INTERFACE_RESET (1 << 3) >> + PLAT_PCIE_STATUS = 0x18, >> +#define PLAT_PCIE_STATUS_PCIE0_LOCKED (1 << 0) >> +#define PLAT_PCIE_STATUS_PCIE1_LOCKED (1 << 8) >> +#define PLAT_PCIE_STATUS_PCIE2_LOCKED (1 << 16) >> + PLAT_FLASH_CTL = 0x1c, >> + PLAT_SPARE0 = 0x20, >> + PLAT_SPARE1 = 0x24, >> + PLAT_SPARE2 = 0x28, >> + PLAT_SPARE3 = 0x2c, >> + PLAT_MMCM_DIV = 0x30, >> +#define PLAT_MMCM_DIV_CLK0DIV_SHIFT 0 >> +#define PLAT_MMCM_DIV_INPUT_SHIFT 8 >> +#define PLAT_MMCM_DIV_MUL_SHIFT 16 >> +#define PLAT_MMCM_DIV_CLK1DIV_SHIFT 24 >> + PLAT_BUILD_CFG = 0x34, >> +#define PLAT_BUILD_CFG_IOCU_EN (1 << 0) >> +#define PLAT_BUILD_CFG_PCIE0_EN (1 << 1) >> +#define PLAT_BUILD_CFG_PCIE1_EN (1 << 2) >> +#define PLAT_BUILD_CFG_PCIE2_EN (1 << 3) >> + PLAT_DDR_CFG = 0x38, >> +#define PLAT_DDR_CFG_SIZE (0xf << 0) >> +#define PLAT_DDR_CFG_MHZ (0xfff << 4) >> + PLAT_NOC_PCIE0_ADDR = 0x3c, >> + PLAT_NOC_PCIE1_ADDR = 0x40, >> + PLAT_NOC_PCIE2_ADDR = 0x44, >> + PLAT_SYS_CTL = 0x48, >> +}; >> + >> +static void boston_lcd_event(void *opaque, QEMUChrEvent event) >> +{ >> + BostonState *s = opaque; >> + if (event == CHR_EVENT_OPENED && !s->lcd_inited) { >> + qemu_chr_fe_printf(&s->lcd_display, " "); >> + s->lcd_inited = true; >> + } >> +} >> + >> +static uint64_t boston_lcd_read(void *opaque, hwaddr addr, >> + unsigned size) >> +{ >> + BostonState *s = opaque; >> + uint64_t val = 0; >> + >> + switch (size) { >> + case 8: >> + val |= (uint64_t)s->lcd_content[(addr + 7) & 0x7] << 56; >> + val |= (uint64_t)s->lcd_content[(addr + 6) & 0x7] << 48; >> + val |= (uint64_t)s->lcd_content[(addr + 5) & 0x7] << 40; >> + val |= (uint64_t)s->lcd_content[(addr + 4) & 0x7] << 32; >> + /* fall through */ >> + case 4: >> + val |= (uint64_t)s->lcd_content[(addr + 3) & 0x7] << 24; >> + val |= (uint64_t)s->lcd_content[(addr + 2) & 0x7] << 16; >> + /* fall through */ >> + case 2: >> + val |= (uint64_t)s->lcd_content[(addr + 1) & 0x7] << 8; >> + /* fall through */ >> + case 1: >> + val |= (uint64_t)s->lcd_content[(addr + 0) & 0x7]; >> + break; >> + } >> + >> + return val; >> +} >> + >> +static void boston_lcd_write(void *opaque, hwaddr addr, >> + uint64_t val, unsigned size) >> +{ >> + BostonState *s = opaque; >> + >> + switch (size) { >> + case 8: >> + s->lcd_content[(addr + 7) & 0x7] = val >> 56; >> + s->lcd_content[(addr + 6) & 0x7] = val >> 48; >> + s->lcd_content[(addr + 5) & 0x7] = val >> 40; >> + s->lcd_content[(addr + 4) & 0x7] = val >> 32; >> + /* fall through */ >> + case 4: >> + s->lcd_content[(addr + 3) & 0x7] = val >> 24; >> + s->lcd_content[(addr + 2) & 0x7] = val >> 16; >> + /* fall through */ >> + case 2: >> + s->lcd_content[(addr + 1) & 0x7] = val >> 8; >> + /* fall through */ >> + case 1: >> + s->lcd_content[(addr + 0) & 0x7] = val; >> + break; >> + } >> + >> + qemu_chr_fe_printf(&s->lcd_display, >> + "\r%-8.8s", s->lcd_content); >> +} >> + >> +static const MemoryRegionOps boston_lcd_ops = { >> + .read = boston_lcd_read, >> + .write = boston_lcd_write, >> + .endianness = DEVICE_NATIVE_ENDIAN, >> +}; >> + >> +static uint64_t boston_platreg_read(void *opaque, hwaddr addr, >> + unsigned size) >> +{ >> + BostonState *s = opaque; >> + uint32_t gic_freq, val; >> + >> + if (size != 4) { >> + qemu_log_mask(LOG_UNIMP, "%uB platform register read\n", size); >> + return 0; >> + } >> + >> + switch (addr & 0xffff) { >> + case PLAT_FPGA_BUILD: >> + case PLAT_CORE_CL: >> + case PLAT_WRAPPER_CL: >> + return 0; >> + case PLAT_DDR3_STATUS: >> + return PLAT_DDR3_STATUS_LOCKED | PLAT_DDR3_STATUS_CALIBRATED >> + | PLAT_DDR3_INTERFACE_RESET; >> + case PLAT_MMCM_DIV: >> + gic_freq = 25000000 / 1000000; >> + val = gic_freq << PLAT_MMCM_DIV_INPUT_SHIFT; >> + val |= 1 << PLAT_MMCM_DIV_MUL_SHIFT; >> + val |= 1 << PLAT_MMCM_DIV_CLK0DIV_SHIFT; >> + val |= 1 << PLAT_MMCM_DIV_CLK1DIV_SHIFT; >> + return val; >> + case PLAT_BUILD_CFG: >> + val = PLAT_BUILD_CFG_PCIE0_EN; >> + val |= PLAT_BUILD_CFG_PCIE1_EN; >> + val |= PLAT_BUILD_CFG_PCIE2_EN; >> + return val; >> + case PLAT_DDR_CFG: >> + val = s->mach->ram_size / GiB; >> + assert(!(val & ~PLAT_DDR_CFG_SIZE)); >> + val |= PLAT_DDR_CFG_MHZ; >> + return val; >> + default: >> + qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" >> HWADDR_PRIx "\n", >> + addr & 0xffff); >> + return 0; >> + } >> +} >> + >> +static void boston_platreg_write(void *opaque, hwaddr addr, >> + uint64_t val, unsigned size) >> +{ >> + if (size != 4) { >> + qemu_log_mask(LOG_UNIMP, "%uB platform register write\n", >> size); >> + return; >> + } >> + >> + switch (addr & 0xffff) { >> + case PLAT_FPGA_BUILD: >> + case PLAT_CORE_CL: >> + case PLAT_WRAPPER_CL: >> + case PLAT_DDR3_STATUS: >> + case PLAT_PCIE_STATUS: >> + case PLAT_MMCM_DIV: >> + case PLAT_BUILD_CFG: >> + case PLAT_DDR_CFG: >> + /* read only */ >> + break; >> + case PLAT_SOFTRST_CTL: >> + if (val & PLAT_SOFTRST_CTL_SYSRESET) { >> + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); >> + } >> + break; >> + default: >> + qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" >> HWADDR_PRIx >> + " = 0x%" PRIx64 "\n", addr & 0xffff, val); >> + break; >> + } >> +} >> + >> +static const MemoryRegionOps boston_platreg_ops = { >> + .read = boston_platreg_read, >> + .write = boston_platreg_write, >> + .endianness = DEVICE_NATIVE_ENDIAN, >> +}; >> + >> +static const TypeInfo boston_device = { >> + .name = TYPE_MIPS_BOSTON_AIA, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(BostonState), >> +}; >> + >> +static void boston_register_types(void) >> +{ >> + type_register_static(&boston_device); >> +} >> +type_init(boston_register_types) >> + >> +#define NUM_INSNS 6 >> +static void gen_firmware(uint32_t *p) >> +{ >> + int i; >> + uint32_t reset_vec[NUM_INSNS] = { >> + /* CM relocate */ >> + 0x1fb802b7, /* li t0,0x1fb80000 */ >> + 0x16100337, /* li t1,0x16100000 */ >> + 0x0062b423, /* sd t1,8(t0) */ >> + /* Jump to 0x80000000 */ >> + 0x00100293, /* li t0,1 */ >> + 0x01f29293, /* slli t0,t0,1f */ >> + 0x00028067 /* jr t0 */ >> + }; >> + >> + for (i = 0; i < NUM_INSNS; i++) { >> + *p++ = reset_vec[i]; >> + } >> +} >> + >> +static inline XilinxPCIEHost * >> +xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr, >> + hwaddr cfg_base, uint64_t cfg_size, >> + hwaddr mmio_base, uint64_t mmio_size, >> + qemu_irq irq) >> +{ >> + DeviceState *dev; >> + MemoryRegion *cfg, *mmio; >> + >> + dev = qdev_new(TYPE_XILINX_PCIE_HOST); >> + >> + qdev_prop_set_uint32(dev, "bus_nr", bus_nr); >> + qdev_prop_set_uint64(dev, "cfg_base", cfg_base); >> + qdev_prop_set_uint64(dev, "cfg_size", cfg_size); >> + qdev_prop_set_uint64(dev, "mmio_base", mmio_base); >> + qdev_prop_set_uint64(dev, "mmio_size", mmio_size); >> + >> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); >> + >> + cfg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); >> + memory_region_add_subregion_overlap(sys_mem, cfg_base, cfg, 0); >> + >> + mmio = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); >> + memory_region_add_subregion_overlap(sys_mem, 0, mmio, 0); >> + >> + qdev_connect_gpio_out_named(dev, "interrupt_out", 0, irq); >> + >> + return XILINX_PCIE_HOST(dev); >> +} >> + >> +static void boston_mach_init(MachineState *machine) >> +{ >> + DeviceState *dev; >> + BostonState *s; >> + MemoryRegion *flash, *ddr_low_alias, *lcd, *platreg; >> + MemoryRegion *sys_mem = get_system_memory(); >> + XilinxPCIEHost *pcie2; >> + PCIDevice *pdev; >> + AHCIPCIState *ich9; >> + DriveInfo *hd[6]; >> + Chardev *chr; >> + int fw_size; >> + >> + if ((machine->ram_size % GiB) || >> + (machine->ram_size > (4 * GiB))) { >> + error_report("Memory size must be 1GB, 2GB, 3GB, or 4GB"); >> + exit(1); >> + } >> + >> + if (machine->smp.cpus / machine->smp.cores / >> machine->smp.threads > 1) { >> + error_report("Invalid -smp x,cores=y,threads=z. The max >> number of clusters " >> + "supported is 1"); >> + exit(1); >> + } >> + >> + dev = qdev_new(TYPE_MIPS_BOSTON_AIA); >> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); >> + >> + s = BOSTON(dev); >> + s->mach = machine; >> + >> + object_initialize_child(OBJECT(machine), "cps", &s->cps, >> TYPE_RISCV_CPS); >> + object_property_set_str(OBJECT(&s->cps), "cpu-type", >> machine->cpu_type, >> + &error_fatal); >> + object_property_set_uint(OBJECT(&s->cps), "num-vp", >> machine->smp.cpus, >> + &error_fatal); >> + object_property_set_uint(OBJECT(&s->cps), "num-hart", >> machine->smp.threads, >> + &error_fatal); >> + object_property_set_uint(OBJECT(&s->cps), "num-core", >> machine->smp.cores, >> + &error_fatal); >> + object_property_set_uint(OBJECT(&s->cps), "gcr-base", >> GCR_BASE_ADDR, >> + &error_fatal); >> + sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal); >> + >> + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1); >> + >> + flash = g_new(MemoryRegion, 1); >> + memory_region_init_rom(flash, NULL, "boston.flash", >> + boston_memmap[BOSTON_FLASH].size, >> &error_fatal); >> + memory_region_add_subregion_overlap(sys_mem, >> + boston_memmap[BOSTON_FLASH].base, >> + flash, 0); >> + >> + memory_region_add_subregion_overlap(sys_mem, >> + boston_memmap[BOSTON_HIGHDDR].base, >> + machine->ram, 0); >> + >> + ddr_low_alias = g_new(MemoryRegion, 1); >> + memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", >> + machine->ram, 0, >> + MIN(machine->ram_size, (256 * MiB))); >> + memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); >> + >> + pcie2 = xilinx_pcie_init(sys_mem, 2, >> + boston_memmap[BOSTON_PCIE2].base, >> + boston_memmap[BOSTON_PCIE2].size, >> + boston_memmap[BOSTON_PCIE2_MMIO].base, >> + boston_memmap[BOSTON_PCIE2_MMIO].size, >> + qdev_get_gpio_in(s->cps.aplic, >> PCIE2_INT)); >> + >> + platreg = g_new(MemoryRegion, 1); >> + memory_region_init_io(platreg, NULL, &boston_platreg_ops, s, >> + "boston-platregs", >> + boston_memmap[BOSTON_PLATREG].size); >> + memory_region_add_subregion_overlap(sys_mem, >> + boston_memmap[BOSTON_PLATREG].base, >> platreg, 0); >> + >> + s->uart = serial_mm_init(sys_mem, >> boston_memmap[BOSTON_UART].base, 2, >> + qdev_get_gpio_in(s->cps.aplic, >> UART_INT), 10000000, >> + serial_hd(0), DEVICE_NATIVE_ENDIAN); >> + >> + lcd = g_new(MemoryRegion, 1); >> + memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, >> "boston-lcd", 0x8); >> + memory_region_add_subregion_overlap(sys_mem, >> + boston_memmap[BOSTON_LCD].base, lcd, 0); >> + >> + chr = qemu_chr_new("lcd", "vc:320x240", NULL); >> + qemu_chr_fe_init(&s->lcd_display, chr, NULL); >> + qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, >> + boston_lcd_event, NULL, s, NULL, true); >> + >> + pdev = >> pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, >> + PCI_DEVFN(0, 0), >> TYPE_ICH9_AHCI); >> + ich9 = ICH9_AHCI(pdev); >> + g_assert(ARRAY_SIZE(hd) == ich9->ahci.ports); >> + ide_drive_get(hd, ich9->ahci.ports); >> + ahci_ide_create_devs(&ich9->ahci, hd); >> + >> + if (machine->firmware) { >> + fw_size = load_image_targphys(machine->firmware, >> + 0x1fc00000, 4 * MiB); >> + if (fw_size == -1) { >> + error_report("unable to load firmware image '%s'", >> + machine->firmware); >> + exit(1); >> + } >> + if (machine->kernel_filename) { >> + fw_size = load_image_targphys(machine->kernel_filename, >> + 0x80000000, 64 * MiB); >> + if (fw_size == -1) { >> + error_report("unable to load kernel image '%s'", >> + machine->kernel_filename); >> + exit(1); >> + } >> + } >> + } else if (machine->kernel_filename) { >> + fw_size = load_image_targphys(machine->kernel_filename, >> + 0x80000000, 64 * MiB); >> + if (fw_size == -1) { >> + error_report("unable to load kernel image '%s'", >> + machine->kernel_filename); >> + exit(1); >> + } >> + >> + gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000); >> + } else if (!qtest_enabled()) { >> + error_report("Please provide either a -kernel or -bios >> argument"); >> + exit(1); >> + } >> +} >> + >> +static void boston_mach_class_init(MachineClass *mc) >> +{ >> + mc->desc = "MIPS Boston-aia"; >> + mc->init = boston_mach_init; >> + mc->block_default_type = IF_IDE; >> + mc->default_ram_size = 2 * GiB; >> + mc->default_ram_id = "boston.ddr"; >> + mc->max_cpus = MAX_HARTS; >> + mc->default_cpu_type = TYPE_RISCV_CPU_MIPS_P8700; >> +} >> + >> +DEFINE_MACHINE("boston-aia", boston_mach_class_init) >> diff --git a/hw/riscv/cps.c b/hw/riscv/cps.c >> new file mode 100644 >> index 0000000000..ab4c27c7ee >> --- /dev/null >> +++ b/hw/riscv/cps.c >> @@ -0,0 +1,187 @@ >> +/* >> + * Coherent Processing System emulation. >> + * >> + * Copyright (c) 2016 Imagination Technologies >> + * >> + * Copyright (c) 2025 MIPS >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2.1 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + * >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "qapi/error.h" >> +#include "qemu/module.h" >> +#include "hw/riscv/cps.h" >> +#include "hw/qdev-properties.h" >> +#include "system/reset.h" >> +#include "hw/intc/riscv_aclint.h" >> +#include "hw/intc/riscv_aplic.h" >> +#include "hw/intc/riscv_imsic.h" >> +#include "hw/pci/msi.h" >> + >> +static void riscv_cps_init(Object *obj) >> +{ >> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); >> + RISCVCPSState *s = RISCV_CPS(obj); >> + >> + /* >> + * Cover entire address space as there do not seem to be any >> + * constraints for the base address of CPC . >> + */ >> + memory_region_init(&s->container, obj, "mips-cps-container", >> UINT64_MAX); >> + sysbus_init_mmio(sbd, &s->container); >> +} >> + >> +static void main_cpu_reset(void *opaque) >> +{ >> + RISCVCPU *cpu = opaque; >> + CPUState *cs = CPU(cpu); >> + >> + cpu_reset(cs); >> +} >> + >> +static void riscv_cps_realize(DeviceState *dev, Error **errp) >> +{ >> + RISCVCPSState *s = RISCV_CPS(dev); >> + RISCVCPU *cpu; >> + int i; >> + >> + /* Set up cpu_index and mhartid for avaiable CPUs. */ >> + int harts_in_cluster = s->num_hart * s->num_core; >> + int num_of_clusters = s->num_vp / harts_in_cluster; >> + for (i = 0; i < s->num_vp; i++) { >> + cpu = RISCV_CPU(object_new(s->cpu_type)); >> + >> + /* All VPs are halted on reset. Leave powering up to CPC. */ >> + object_property_set_bool(OBJECT(cpu), "start-powered-off", >> true, >> + &error_abort); >> + >> + if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) { >> + return; >> + } >> + >> + /* Set up mhartid */ >> + int cluster_id = i / harts_in_cluster; >> + int hart_id = (i % harts_in_cluster) % s->num_hart; >> + int core_id = (i % harts_in_cluster) / s->num_hart; >> + int mhartid = (cluster_id << MHARTID_CLUSTER_SHIFT) + >> + (core_id << MHARTID_CORE_SHIFT) + >> + (hart_id << MHARTID_HART_SHIFT); >> + cpu->env.mhartid = mhartid; >> + qemu_register_reset(main_cpu_reset, cpu); >> + } >> + >> + /* Cluster Power Controller */ >> + object_initialize_child(OBJECT(dev), "cpc", &s->cpc, >> TYPE_RISCV_CPC); >> + object_property_set_uint(OBJECT(&s->cpc), "cluster-id", 0, >> + &error_abort); >> + object_property_set_uint(OBJECT(&s->cpc), "num-vp", s->num_vp, >> + &error_abort); >> + object_property_set_uint(OBJECT(&s->cpc), "num-hart", s->num_hart, >> + &error_abort); >> + object_property_set_uint(OBJECT(&s->cpc), "num-core", s->num_core, >> + &error_abort); >> + object_property_set_int(OBJECT(&s->cpc), "vp-start-running", 1, >> + &error_abort); >> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpc), errp)) { >> + return; >> + } >> + >> + memory_region_add_subregion(&s->container, 0, >> + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0)); >> + >> + /* Global Configuration Registers */ >> + object_initialize_child(OBJECT(dev), "gcr", &s->gcr, >> TYPE_RISCV_GCR); >> + object_property_set_uint(OBJECT(&s->gcr), "cluster-id", 0, >> + &error_abort); >> + object_property_set_uint(OBJECT(&s->gcr), "num-vp", s->num_vp, >> + &error_abort); >> + object_property_set_int(OBJECT(&s->gcr), "gcr-rev", 0xa00, >> + &error_abort); >> + object_property_set_int(OBJECT(&s->gcr), "gcr-base", s->gcr_base, >> + &error_abort); >> + object_property_set_link(OBJECT(&s->gcr), "cpc", >> OBJECT(&s->cpc.mr), >> + &error_abort); >> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) { >> + return; >> + } >> + >> + memory_region_add_subregion(&s->container, s->gcr_base, >> + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0)); >> + >> + for (i = 0; i < num_of_clusters; i++) { >> + uint64_t cm_base = GLOBAL_CM_BASE + (CM_SIZE * i); >> + uint32_t hartid_base = i << MHARTID_CLUSTER_SHIFT; >> + s->aplic = riscv_aplic_create(cm_base + AIA_PLIC_M_OFFSET, >> + AIA_PLIC_M_SIZE, >> + hartid_base, /* hartid_base */ >> + MAX_HARTS, /* num_harts */ >> + APLIC_NUM_SOURCES, >> + APLIC_NUM_PRIO_BITS, >> + false, true, NULL); >> + riscv_aplic_create(cm_base + AIA_PLIC_S_OFFSET, >> + AIA_PLIC_S_SIZE, >> + hartid_base, /* hartid_base */ >> + MAX_HARTS, /* num_harts */ >> + APLIC_NUM_SOURCES, >> + APLIC_NUM_PRIO_BITS, >> + false, false, s->aplic); >> + /* PLIC changes msi_nonbroken to ture. We revert the change. */ >> + msi_nonbroken = false; >> + riscv_aclint_swi_create(cm_base + AIA_CLINT_OFFSET, >> + hartid_base, MAX_HARTS, false); >> + riscv_aclint_mtimer_create(cm_base + AIA_CLINT_OFFSET + >> + RISCV_ACLINT_SWI_SIZE, >> + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, >> + hartid_base, >> + MAX_HARTS, >> + RISCV_ACLINT_DEFAULT_MTIMECMP, >> + RISCV_ACLINT_DEFAULT_MTIME, >> + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false); >> + } >> +} >> + >> +static const Property riscv_cps_properties[] = { >> + DEFINE_PROP_UINT32("num-vp", RISCVCPSState, num_vp, 1), >> + DEFINE_PROP_UINT32("num-hart", RISCVCPSState, num_hart, 1), >> + DEFINE_PROP_UINT32("num-core", RISCVCPSState, num_core, 1), >> + DEFINE_PROP_UINT64("gcr-base", RISCVCPSState, gcr_base, >> GCR_BASE_ADDR), >> + DEFINE_PROP_STRING("cpu-type", RISCVCPSState, cpu_type), >> +}; >> + >> +static void riscv_cps_class_init(ObjectClass *klass, const void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->realize = riscv_cps_realize; >> + device_class_set_props(dc, riscv_cps_properties); >> +} >> + >> +static const TypeInfo riscv_cps_info = { >> + .name = TYPE_RISCV_CPS, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(RISCVCPSState), >> + .instance_init = riscv_cps_init, >> + .class_init = riscv_cps_class_init, >> +}; >> + >> +static void riscv_cps_register_types(void) >> +{ >> + type_register_static(&riscv_cps_info); >> +} >> + >> +type_init(riscv_cps_register_types) >> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build >> index c22f3a7216..76a038f60e 100644 >> --- a/hw/riscv/meson.build >> +++ b/hw/riscv/meson.build >> @@ -13,5 +13,6 @@ riscv_ss.add(when: 'CONFIG_ACPI', if_true: >> files('virt-acpi-build.c')) >> riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files( >> 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', >> 'riscv-iommu-hpm.c')) >> riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: >> files('microblaze-v-generic.c')) >> +riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: >> files('boston-aia.c', 'cps.c')) >> >> hw_arch += {'riscv': riscv_ss} >> diff --git a/include/hw/misc/riscv_cmgcr.h >> b/include/hw/misc/riscv_cmgcr.h >> new file mode 100644 >> index 0000000000..c2c07fa0a6 >> --- /dev/null >> +++ b/include/hw/misc/riscv_cmgcr.h >> @@ -0,0 +1,77 @@ >> +/* >> + * This file is subject to the terms and conditions of the GNU >> General Public >> + * License. See the file "COPYING" in the main directory of this >> archive >> + * for more details. >> + * >> + * Copyright (C) 2015 Imagination Technologies >> + * >> + * Copyright (C) 2025 MIPS >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + * >> + */ >> + >> +#ifndef RISCV_CMGCR_H >> +#define RISCV_CMGCR_H >> + >> +#include "hw/sysbus.h" >> +#include "qom/object.h" >> + >> +#define TYPE_RISCV_GCR "riscv-gcr" >> +OBJECT_DECLARE_SIMPLE_TYPE(RISCVGCRState, RISCV_GCR) >> + >> +#define GCR_BASE_ADDR 0x1fb80000ULL >> +#define GCR_ADDRSPACE_SZ 0x8000 >> + >> +/* Offsets to register blocks */ >> +#define RISCV_GCB_OFS 0x0000 /* Global Control Block */ >> +#define RISCV_CLCB_OFS 0x2000 /* Core Control Block */ >> + >> +/* Global Control Block Register Map */ >> +#define GCR_CONFIG_OFS 0x0000 >> +#define GCR_BASE_OFS 0x0008 >> +#define GCR_REV_OFS 0x0030 >> +#define GCR_CPC_STATUS_OFS 0x00F0 >> +#define GCR_L2_CONFIG_OFS 0x0130 >> + >> +/* GCR_L2_CONFIG register fields */ >> +#define GCR_L2_CONFIG_BYPASS_SHF 20 >> +#define GCR_L2_CONFIG_BYPASS_MSK ((0x1ULL) << >> GCR_L2_CONFIG_BYPASS_SHF) >> + >> +/* GCR_BASE register fields */ >> +#define GCR_BASE_GCRBASE_MSK 0xffffffff8000ULL >> + >> +/* GCR_CPC_BASE register fields */ >> +#define GCR_CPC_BASE_CPCEN_MSK 1 >> +#define GCR_CPC_BASE_CPCBASE_MSK 0xFFFFFFFF8000ULL >> +#define GCR_CPC_BASE_MSK (GCR_CPC_BASE_CPCEN_MSK | >> GCR_CPC_BASE_CPCBASE_MSK) >> + >> +/* GCR_CL_RESETBASE_OFS register fields */ >> +#define GCR_CL_RESET_BASE_RESETBASE_MSK 0xFFFFFFFFFFFFF000U >> +#define GCR_CL_RESET_BASE_MSK GCR_CL_RESET_BASE_RESETBASE_MSK >> + >> +typedef struct RISCVGCRVPState RISCVGCRVPState; >> +struct RISCVGCRVPState { >> + uint64_t reset_base; >> +}; >> + >> +typedef struct RISCVGCRState RISCVGCRState; >> +struct RISCVGCRState { >> + SysBusDevice parent_obj; >> + >> + int32_t gcr_rev; >> + uint32_t cluster_id; >> + uint32_t num_vps; >> + uint32_t num_hart; >> + uint32_t num_core; >> + hwaddr gcr_base; >> + MemoryRegion iomem; >> + MemoryRegion *cpc_mr; >> + >> + uint64_t cpc_base; >> + >> + /* VP Local/Other Registers */ >> + RISCVGCRVPState *vps; >> +}; >> + >> +#endif /* RISCV_CMGCR_H */ >> diff --git a/include/hw/misc/riscv_cpc.h b/include/hw/misc/riscv_cpc.h >> new file mode 100644 >> index 0000000000..36d20c8f20 >> --- /dev/null >> +++ b/include/hw/misc/riscv_cpc.h >> @@ -0,0 +1,69 @@ >> +/* >> + * Cluster Power Controller emulation >> + * >> + * Copyright (c) 2016 Imagination Technologies >> + * >> + * Copyright (c) 2025 MIPS >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2.1 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + * >> + */ >> + >> +#ifndef RISCV_CPC_H >> +#define RISCV_CPC_H >> + >> +#include "hw/sysbus.h" >> +#include "qom/object.h" >> + >> +#define CPC_ADDRSPACE_SZ 0x6000 >> + >> +/* CPC global register offsets relative to base address */ >> +#define CPC_MTIME_REG_OFS 0x50 >> + >> +#define CPC_CM_STAT_CONF_OFS 0x1008 >> + >> +/* CPC blocks offsets relative to base address */ >> +#define CPC_CL_BASE_OFS 0x2000 >> + >> +/* CPC register offsets relative to block offsets */ >> +#define CPC_STAT_CONF_OFS 0x08 >> +#define CPC_VP_STOP_OFS 0x20 >> +#define CPC_VP_RUN_OFS 0x28 >> +#define CPC_VP_RUNNING_OFS 0x30 >> + >> +#define SEQ_STATE_BIT 19 >> +#define SEQ_STATE_U5 0x6 >> +#define SEQ_STATE_U6 0x7 >> +#define CPC_Cx_STAT_CONF_SEQ_STATE_U5 (SEQ_STATE_U5 << >> SEQ_STATE_BIT) >> +#define CPC_Cx_STAT_CONF_SEQ_STATE_U6 (SEQ_STATE_U6 << >> SEQ_STATE_BIT) >> + >> +#define TYPE_RISCV_CPC "riscv-cpc" >> +OBJECT_DECLARE_SIMPLE_TYPE(RISCVCPCState, RISCV_CPC) >> + >> +typedef struct RISCVCPCState { >> + SysBusDevice parent_obj; >> + >> + uint32_t cluster_id; >> + uint32_t num_vp; >> + uint32_t num_hart; >> + uint32_t num_core; >> + uint64_t vp_start_running; /* VPs running from restart */ >> + >> + MemoryRegion mr; >> + uint64_t vp_running; /* Indicates which VPs are in the run state */ >> +} RISCVCPCState; >> + >> +#endif /* RISCV_CPC_H */ >> diff --git a/include/hw/riscv/cps.h b/include/hw/riscv/cps.h >> new file mode 100644 >> index 0000000000..dfc45f5797 >> --- /dev/null >> +++ b/include/hw/riscv/cps.h >> @@ -0,0 +1,75 @@ >> +/* >> + * Coherent Processing System emulation. >> + * >> + * Copyright (c) 2016 Imagination Technologies >> + * >> + * Copyright (c) 2025 MIPS >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2.1 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + * >> + */ >> + >> +#ifndef RISCV_CPS_H >> +#define RISCV_CPS_H >> + >> +#include "hw/sysbus.h" >> +#include "hw/misc/riscv_cmgcr.h" >> +#include "hw/misc/riscv_cpc.h" >> +#include "target/riscv/cpu.h" >> +#include "qom/object.h" >> + >> +#define TYPE_RISCV_CPS "riscv-cps" >> +OBJECT_DECLARE_SIMPLE_TYPE(RISCVCPSState, RISCV_CPS) >> + >> +/* The model supports up to 64 harts. */ >> +#define MAX_HARTS 64 >> + >> +/* The global CM base for the boston-aia model. */ >> +#define GLOBAL_CM_BASE 0x16100000 >> +/* The CM block is 512 KiB. */ >> +#define CM_SIZE (1 << 19) >> + >> +/* The mhartid bits has cluster at bit 16, core at bit 4, and hart at >> + bit 0. */ >> +#define MHARTID_CLUSTER_SHIFT 16 >> +#define MHARTID_CORE_SHIFT 4 >> +#define MHARTID_HART_SHIFT 0 >> + >> +#define APLIC_NUM_SOURCES 0x35 /* Arbitray maximum number of >> interrupts. */ >> +#define APLIC_NUM_PRIO_BITS 3 >> +#define AIA_PLIC_M_OFFSET 0x40000 >> +#define AIA_PLIC_M_SIZE 0x8000 >> +#define AIA_PLIC_S_OFFSET 0x60000 >> +#define AIA_PLIC_S_SIZE 0x8000 >> +#define AIA_CLINT_OFFSET 0x50000 >> + >> +typedef struct RISCVCPSState { >> + SysBusDevice parent_obj; >> + >> + uint32_t num_vp; >> + uint32_t num_hart; >> + uint32_t num_core; >> + uint64_t gcr_base; >> + char *cpu_type; >> + >> + MemoryRegion container; >> + RISCVGCRState gcr; >> + RISCVCPCState cpc; >> + >> + DeviceState *aplic; >> +} RISCVCPSState; >> + >> +#endif >