On 03/10/2018 17:07, Edgar E. Iglesias wrote: > From: "Edgar E. Iglesias" <edgar.igles...@xilinx.com> > > Add a model of Xilinx Versal SoC. > > Signed-off-by: Edgar E. Iglesias <edgar.igles...@xilinx.com> > --- > default-configs/aarch64-softmmu.mak | 1 + > hw/arm/Makefile.objs | 1 + > hw/arm/xlnx-versal.c | 339 > ++++++++++++++++++++++++++++++++++++ > include/hw/arm/xlnx-versal.h | 122 +++++++++++++ > 4 files changed, 463 insertions(+) > create mode 100644 hw/arm/xlnx-versal.c > create mode 100644 include/hw/arm/xlnx-versal.h > > diff --git a/default-configs/aarch64-softmmu.mak > b/default-configs/aarch64-softmmu.mak > index 6f790f0..4ea9add 100644 > --- a/default-configs/aarch64-softmmu.mak > +++ b/default-configs/aarch64-softmmu.mak > @@ -8,4 +8,5 @@ CONFIG_DDC=y > CONFIG_DPCD=y > CONFIG_XLNX_ZYNQMP=y > CONFIG_XLNX_ZYNQMP_ARM=y > +CONFIG_XLNX_VERSAL=y > CONFIG_ARM_SMMUV3=y > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs > index 5f88062..ec21d9b 100644 > --- a/hw/arm/Makefile.objs > +++ b/hw/arm/Makefile.objs > @@ -26,6 +26,7 @@ obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o > obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o > obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o > obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o > +obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o > obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o > obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o > obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o > diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c > new file mode 100644 > index 0000000..c12fc85 > --- /dev/null > +++ b/hw/arm/xlnx-versal.c > @@ -0,0 +1,339 @@ > +/* > + * Xilinx Versal SoC model. > + * > + * Copyright (c) 2018 Xilinx Inc. > + * Written by Edgar E. Iglesias > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 or > + * (at your option) any later version. > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qemu-common.h" > +#include "qemu/log.h" > +#include "hw/sysbus.h" > +#include "net/net.h" > +#include "sysemu/sysemu.h" > +#include "sysemu/kvm.h" > +#include "hw/arm/arm.h" > +#include "kvm_arm.h" > +#include "hw/misc/unimp.h" > +#include "hw/intc/arm_gicv3_common.h" > +#include "hw/arm/xlnx-versal.h" > + > +#define XLNX_VERSAL_ACPU_TYPE "cortex-a72" "-" TYPE_ARM_CPU > +#define GEM_REVISION 0x40070106 > + > +static void versal_create_apu_cpus(Versal *s, Error **errp) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(s->fpd.apu.cpu); i++) { > + Object *obj; > + char *name; > + > + obj = object_new(XLNX_VERSAL_ACPU_TYPE); > + if (!obj) { > + /* Secondary CPUs start in PSCI powered-down state */ > + error_setg(errp, "Unable to create apu.cpu[%d] of type %s", > + i, XLNX_VERSAL_ACPU_TYPE); > + return; > + } > + > + name = g_strdup_printf("apu-cpu[%d]", i); > + object_property_add_child(OBJECT(s), name, obj, &error_fatal); > + g_free(name); > + > + object_property_set_int(obj, s->cfg.psci_conduit, > + "psci-conduit", &error_abort); > + if (i) { > + object_property_set_bool(obj, true, > + "start-powered-off", &error_abort); > + } > + > + object_property_set_int(obj, ARRAY_SIZE(s->fpd.apu.cpu), > + "core-count", &error_abort); > + object_property_set_link(obj, OBJECT(&s->fpd.apu.mr), "memory", > + &error_abort); > + object_property_set_bool(obj, true, "realized", &error_fatal); > + s->fpd.apu.cpu[i] = ARM_CPU(obj); > + } > +} > + > +static void versal_create_apu_gic(Versal *s, qemu_irq *pic, Error **errp) > +{ > + static const uint64_t addrs[] = { > + MM_GIC_APU_DIST_MAIN, > + MM_GIC_APU_REDIST_0 > + }; > + SysBusDevice *gicbusdev; > + DeviceState *gicdev; > + int nr_apu_cpus = ARRAY_SIZE(s->fpd.apu.cpu); > + int i; > + > + sysbus_init_child_obj(OBJECT(s), "apu-gic", > + &s->fpd.apu.gic, sizeof(s->fpd.apu.gic), > + gicv3_class_name()); > + gicbusdev = SYS_BUS_DEVICE(&s->fpd.apu.gic); > + gicdev = DEVICE(&s->fpd.apu.gic); > + qdev_prop_set_uint32(gicdev, "revision", 3); > + qdev_prop_set_uint32(gicdev, "num-cpu", 2); > + qdev_prop_set_uint32(gicdev, "num-irq", XLNX_VERSAL_NR_IRQS + 32); > + qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); > + qdev_prop_set_uint32(gicdev, "redist-region-count[0]", 2); > + if (!kvm_irqchip_in_kernel()) { > + qdev_prop_set_bit(gicdev, "has-security-extensions", true); > + } > + > + object_property_set_bool(OBJECT(&s->fpd.apu.gic), true, "realized", > errp); > + > + for (i = 0; i < ARRAY_SIZE(addrs); i++) { > + MemoryRegion *mr; > + > + mr = sysbus_mmio_get_region(gicbusdev, i); > + memory_region_add_subregion(&s->fpd.apu.mr, addrs[i], mr); > + } > + > + for (i = 0; i < nr_apu_cpus; i++) { > + DeviceState *cpudev = DEVICE(s->fpd.apu.cpu[i]); > + int ppibase = XLNX_VERSAL_NR_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; > + qemu_irq maint_irq; > + int ti; > + /* Mapping from the output timer irq lines from the CPU to the > + * GIC PPI inputs we use for the virt board. > + */ > + const int timer_irq[] = { > + [GTIMER_PHYS] = VERSAL_TIMER_NS_EL1_IRQ, > + [GTIMER_VIRT] = VERSAL_TIMER_VIRT_IRQ, > + [GTIMER_HYP] = VERSAL_TIMER_NS_EL2_IRQ, > + [GTIMER_SEC] = VERSAL_TIMER_S_EL1_IRQ, > + }; > + > + for (ti = 0; ti < ARRAY_SIZE(timer_irq); ti++) { > + qdev_connect_gpio_out(cpudev, ti, > + qdev_get_gpio_in(gicdev, > + ppibase + timer_irq[ti])); > + } > + maint_irq = qdev_get_gpio_in(gicdev, > + ppibase + VERSAL_GIC_MAINT_IRQ); > + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", > + 0, maint_irq); > + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, > ARM_CPU_IRQ)); > + sysbus_connect_irq(gicbusdev, i + nr_apu_cpus, > + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); > + sysbus_connect_irq(gicbusdev, i + 2 * nr_apu_cpus, > + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); > + sysbus_connect_irq(gicbusdev, i + 3 * nr_apu_cpus, > + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); > + } > + > + for (i = 0; i < XLNX_VERSAL_NR_IRQS; i++) { > + pic[i] = qdev_get_gpio_in(gicdev, i); > + } > +} > + > +static void versal_create_uarts(Versal *s, qemu_irq *pic) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) { > + static const int irqs[] = { VERSAL_UART0_IRQ_0, VERSAL_UART1_IRQ_0}; > + static const uint64_t addrs[] = { MM_UART0, MM_UART1 }; > + char *name = g_strdup_printf("uart%d", i); > + DeviceState *dev; > + MemoryRegion *mr; > + > + dev = qdev_create(NULL, "pl011"); > + s->lpd.iou.uart[i] = SYS_BUS_DEVICE(dev); > + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); > + object_property_add_child(OBJECT(s), name, OBJECT(dev), > &error_fatal); > + qdev_init_nofail(dev); > + > + mr = sysbus_mmio_get_region(s->lpd.iou.uart[i], 0); > + memory_region_add_subregion(&s->mr_ps, addrs[i], mr); > + > + sysbus_connect_irq(s->lpd.iou.uart[i], 0, pic[irqs[i]]); > + g_free(name); > + } > +} > + > +static void versal_create_gems(Versal *s, qemu_irq *pic) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) { > + static const int irqs[] = { VERSAL_GEM0_IRQ_0, VERSAL_GEM1_IRQ_0}; > + static const uint64_t addrs[] = { MM_GEM0, MM_GEM1 }; > + char *name = g_strdup_printf("gem%d", i); > + NICInfo *nd = &nd_table[i]; > + DeviceState *dev; > + MemoryRegion *mr; > + > + dev = qdev_create(NULL, "cadence_gem"); > + s->lpd.iou.gem[i] = SYS_BUS_DEVICE(dev); > + object_property_add_child(OBJECT(s), name, OBJECT(dev), > &error_fatal); > + if (nd->used) { > + qemu_check_nic_model(nd, "cadence_gem"); > + qdev_set_nic_properties(dev, nd); > + } > + object_property_set_int(OBJECT(s->lpd.iou.gem[i]), > + 2, "num-priority-queues", > + &error_abort); > + object_property_set_link(OBJECT(s->lpd.iou.gem[i]), > + OBJECT(&s->mr_ps), "dma", > + &error_abort); > + qdev_init_nofail(dev); > + > + mr = sysbus_mmio_get_region(s->lpd.iou.gem[i], 0); > + memory_region_add_subregion(&s->mr_ps, addrs[i], mr); > + > + sysbus_connect_irq(s->lpd.iou.gem[i], 0, pic[irqs[i]]); > + g_free(name); > + } > +} > + > +/* This takes the board allocated linear DDR memory and creates aliases > + * for each split DDR range/apperture on the Versal address map. > + */ > +static void versal_map_ddr(Versal *s) > +{ > + uint64_t size = memory_region_size(s->cfg.mr_ddr); > + /* Describes the various split DDR access regions. */ > + static const struct { > + uint64_t base; > + uint64_t size; > + } addr_ranges[] = { > + { MM_TOP_DDR, MM_TOP_DDR_SIZE }, > + { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE }, > + { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE }, > + { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE } > + }; > + uint64_t offset = 0; > + int i; > + > + assert(ARRAY_SIZE(addr_ranges) == ARRAY_SIZE(s->noc.mr_ddr_ranges)); > + for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) { > + char *name; > + uint64_t mapsize; > + > + mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size; > + name = g_strdup_printf("noc-ddr-range%d", i); > + /* Create the MR alias. */ > + memory_region_init_alias(&s->noc.mr_ddr_ranges[i], OBJECT(s), > + name, s->cfg.mr_ddr, > + offset, mapsize); > + > + /* Map it onto the NoC MR. */ > + memory_region_add_subregion(&s->mr_ps, addr_ranges[i].base, > + &s->noc.mr_ddr_ranges[i]); > + offset += mapsize; > + size -= mapsize; > + g_free(name); > + } > +} > + > +static void versal_unimp_area(Versal *s, const char *name, > + MemoryRegion *mr, > + hwaddr base, hwaddr size) > +{ > + DeviceState *dev = qdev_create(NULL, TYPE_UNIMPLEMENTED_DEVICE); > + MemoryRegion *mr_dev; > + > + qdev_prop_set_string(dev, "name", name); > + qdev_prop_set_uint64(dev, "size", size); > + object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); > + qdev_init_nofail(dev); > + > + mr_dev = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); > + memory_region_add_subregion(mr, base, mr_dev); > +} > + > +static void versal_unimp(Versal *s) > +{ > + versal_unimp_area(s, "psm", &s->mr_ps, > + MM_PSM_START, MM_PSM_END - MM_PSM_START); > + versal_unimp_area(s, "crl", &s->mr_ps, > + MM_CRL, MM_CRL_SIZE); > + versal_unimp_area(s, "crf", &s->mr_ps, > + MM_FPD_CRF, MM_FPD_CRF_SIZE); > + versal_unimp_area(s, "iou-scntr", &s->mr_ps, > + MM_IOU_SCNTR, MM_IOU_SCNTR_SIZE); > + versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps, > + MM_IOU_SCNTRS, MM_IOU_SCNTRS_SIZE); > +} > + > +static void versal_realize(DeviceState *dev, Error **errp) > +{ > + Versal *s = XLNX_VERSAL(dev); > + qemu_irq pic[XLNX_VERSAL_NR_IRQS]; > + > + versal_create_apu_cpus(s, errp); > + versal_create_apu_gic(s, pic, errp); > + versal_create_uarts(s, pic); > + versal_create_gems(s, pic); > + versal_map_ddr(s); > + versal_unimp(s); > + > + /* Create the OCM. */ > + memory_region_init_ram(&s->lpd.mr_ocm, OBJECT(s), "ocm", > + MM_OCM_SIZE, &error_fatal); > + > + memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, > 0); > + memory_region_add_subregion_overlap(&s->fpd.apu.mr, 0, &s->mr_ps, 0); > +} > + > +static void versal_init(Object *obj) > +{ > + Versal *s = XLNX_VERSAL(obj); > + > + memory_region_init(&s->fpd.apu.mr, obj, "mr-apu", UINT64_MAX); > + memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); > +} > + > +static const VMStateDescription versal_vmstate = { > + .name = "xlnx-ve", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + /* FIXME. */ > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static Property versal_properties[] = { > + DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, > + MemoryRegion *), > + DEFINE_PROP_UINT32("psci-conduit", Versal, cfg.psci_conduit, 0), > + DEFINE_PROP_END_OF_LIST() > +}; > + > +static void versal_reset(DeviceState *dev) > +{ > +} > + > +static void versal_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = versal_realize; > + dc->vmsd = &versal_vmstate; > + dc->props = versal_properties; > + dc->reset = versal_reset; > +} > + > +static const TypeInfo versal_info = { > + .name = TYPE_XLNX_VERSAL, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(Versal), > + .instance_init = versal_init, > + .class_init = versal_class_init, > +}; > + > +static void versal_register_types(void) > +{ > + type_register_static(&versal_info); > +} > + > +type_init(versal_register_types); > diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h > new file mode 100644 > index 0000000..9da621e > --- /dev/null > +++ b/include/hw/arm/xlnx-versal.h > @@ -0,0 +1,122 @@ > +/* > + * Model of the Xilinx Versal > + * > + * Copyright (c) 2018 Xilinx Inc. > + * Written by Edgar E. Iglesias > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 or > + * (at your option) any later version. > + */ > + > +#ifndef XLNX_VERSAL_H > +#define XLNX_VERSAL_H > + > +#include "hw/sysbus.h" > +#include "hw/arm/arm.h" > +#include "hw/intc/arm_gicv3.h" > + > +#define TYPE_XLNX_VERSAL "xlnx-versal" > +#define XLNX_VERSAL(obj) OBJECT_CHECK(Versal, (obj), TYPE_XLNX_VERSAL) > + > +#define XLNX_VERSAL_NR_ACPUS 2 > +#define XLNX_VERSAL_NR_UARTS 2 > +#define XLNX_VERSAL_NR_GEMS 2 > +#define XLNX_VERSAL_NR_IRQS 256 > + > +typedef struct Versal { > + /*< private >*/ > + SysBusDevice parent_obj; > + > + /*< public >*/ > + struct { > + struct { > + MemoryRegion mr; > + ARMCPU *cpu[XLNX_VERSAL_NR_ACPUS]; > + GICv3State gic; > + } apu; > + } fpd; > + > + MemoryRegion mr_ps; > + > + struct { > + /* 4 ranges to access DDR. */ > + MemoryRegion mr_ddr_ranges[4]; > + } noc; > + > + struct { > + MemoryRegion mr_ocm; > + > + struct { > + SysBusDevice *uart[XLNX_VERSAL_NR_UARTS]; > + SysBusDevice *gem[XLNX_VERSAL_NR_GEMS]; > + } iou; > + } lpd;
Amazing how a so complex SoC is modelled that clear... You won my "clearest SoC model of the year" prize. > + > + struct { > + MemoryRegion *mr_ddr; > + uint32_t psci_conduit; > + } cfg; > +} Versal; > + > +/* Memory-map and IRQ definitions. Copied a subset from > + * auto-generated files. */ > + > +#define VERSAL_GIC_MAINT_IRQ 9 > +#define VERSAL_TIMER_VIRT_IRQ 11 > +#define VERSAL_TIMER_S_EL1_IRQ 13 > +#define VERSAL_TIMER_NS_EL1_IRQ 14 > +#define VERSAL_TIMER_NS_EL2_IRQ 10 > + > +#define VERSAL_UART0_IRQ_0 18 > +#define VERSAL_UART1_IRQ_0 19 > +#define VERSAL_GEM0_IRQ_0 56 > +#define VERSAL_GEM0_WAKE_IRQ_0 57 > +#define VERSAL_GEM1_IRQ_0 58 > +#define VERSAL_GEM1_WAKE_IRQ_0 59 > + > +/* Architecturally eserved IRQs suitable for virtualization. */ > +#define VERSAL_RSVD_HIGH_IRQ_FIRST 160 > +#define VERSAL_RSVD_HIGH_IRQ_LAST 255 > + > +#define MM_TOP_RSVD 0xa0000000U > +#define MM_TOP_RSVD_SIZE 0x4000000 > +#define MM_GIC_APU_DIST_MAIN 0xf9000000U > +#define MM_GIC_APU_DIST_MAIN_SIZE 0x10000 > +#define MM_GIC_APU_REDIST_0 0xf9080000U > +#define MM_GIC_APU_REDIST_0_SIZE 0x80000 > + > +#define MM_UART0 0xff000000U > +#define MM_UART0_SIZE 0x10000 > +#define MM_UART1 0xff010000U > +#define MM_UART1_SIZE 0x10000 > + > +#define MM_GEM0 0xff0c0000U > +#define MM_GEM0_SIZE 0x10000 > +#define MM_GEM1 0xff0d0000U > +#define MM_GEM1_SIZE 0x10000 > + > +#define MM_OCM 0xfffc0000U > +#define MM_OCM_SIZE 0x40000 > + > +#define MM_TOP_DDR 0x0 > +#define MM_TOP_DDR_SIZE 0x80000000U > +#define MM_TOP_DDR_2 0x800000000ULL > +#define MM_TOP_DDR_2_SIZE 0x800000000ULL > +#define MM_TOP_DDR_3 0xc000000000ULL > +#define MM_TOP_DDR_3_SIZE 0x4000000000ULL > +#define MM_TOP_DDR_4 0x10000000000ULL > +#define MM_TOP_DDR_4_SIZE 0xb780000000ULL > + > +#define MM_PSM_START 0xffc80000U > +#define MM_PSM_END 0xffcf0000U > + > +#define MM_CRL 0xff5e0000U > +#define MM_CRL_SIZE 0x300000 > +#define MM_IOU_SCNTR 0xff130000U > +#define MM_IOU_SCNTR_SIZE 0x10000 > +#define MM_IOU_SCNTRS 0xff140000U > +#define MM_IOU_SCNTRS_SIZE 0x10000 > +#define MM_FPD_CRF 0xfd1a0000U > +#define MM_FPD_CRF_SIZE 0x140000 > +#endif >