| /* |
| * ARM V2M MPS2 board emulation. |
| * |
| * Copyright (c) 2017 Linaro Limited |
| * Written by Peter Maydell |
| * |
| * 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. |
| */ |
| |
| /* The MPS2 and MPS2+ dev boards are FPGA based (the 2+ has a bigger |
| * FPGA but is otherwise the same as the 2). Since the CPU itself |
| * and most of the devices are in the FPGA, the details of the board |
| * as seen by the guest depend significantly on the FPGA image. |
| * We model the following FPGA images: |
| * "mps2-an385" -- Cortex-M3 as documented in ARM Application Note AN385 |
| * "mps2-an511" -- Cortex-M3 'DesignStart' as documented in AN511 |
| * |
| * Links to the TRM for the board itself and to the various Application |
| * Notes which document the FPGA images can be found here: |
| * https://developer.arm.com/products/system-design/development-boards/cortex-m-prototyping-system |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qapi/error.h" |
| #include "qemu/error-report.h" |
| #include "hw/arm/arm.h" |
| #include "hw/arm/armv7m.h" |
| #include "hw/boards.h" |
| #include "exec/address-spaces.h" |
| #include "hw/misc/unimp.h" |
| |
| typedef enum MPS2FPGAType { |
| FPGA_AN385, |
| FPGA_AN511, |
| } MPS2FPGAType; |
| |
| typedef struct { |
| MachineClass parent; |
| MPS2FPGAType fpga_type; |
| const char *cpu_model; |
| } MPS2MachineClass; |
| |
| typedef struct { |
| MachineState parent; |
| |
| ARMv7MState armv7m; |
| MemoryRegion psram; |
| MemoryRegion ssram1; |
| MemoryRegion ssram1_m; |
| MemoryRegion ssram23; |
| MemoryRegion ssram23_m; |
| MemoryRegion blockram; |
| MemoryRegion blockram_m1; |
| MemoryRegion blockram_m2; |
| MemoryRegion blockram_m3; |
| MemoryRegion sram; |
| } MPS2MachineState; |
| |
| #define TYPE_MPS2_MACHINE "mps2" |
| #define TYPE_MPS2_AN385_MACHINE MACHINE_TYPE_NAME("mps2-an385") |
| #define TYPE_MPS2_AN511_MACHINE MACHINE_TYPE_NAME("mps2-an511") |
| |
| #define MPS2_MACHINE(obj) \ |
| OBJECT_CHECK(MPS2MachineState, obj, TYPE_MPS2_MACHINE) |
| #define MPS2_MACHINE_GET_CLASS(obj) \ |
| OBJECT_GET_CLASS(MPS2MachineClass, obj, TYPE_MPS2_MACHINE) |
| #define MPS2_MACHINE_CLASS(klass) \ |
| OBJECT_CLASS_CHECK(MPS2MachineClass, klass, TYPE_MPS2_MACHINE) |
| |
| /* Main SYSCLK frequency in Hz */ |
| #define SYSCLK_FRQ 25000000 |
| |
| /* Initialize the auxiliary RAM region @mr and map it into |
| * the memory map at @base. |
| */ |
| static void make_ram(MemoryRegion *mr, const char *name, |
| hwaddr base, hwaddr size) |
| { |
| memory_region_init_ram(mr, NULL, name, size, &error_fatal); |
| memory_region_add_subregion(get_system_memory(), base, mr); |
| } |
| |
| /* Create an alias of an entire original MemoryRegion @orig |
| * located at @base in the memory map. |
| */ |
| static void make_ram_alias(MemoryRegion *mr, const char *name, |
| MemoryRegion *orig, hwaddr base) |
| { |
| memory_region_init_alias(mr, NULL, name, orig, 0, |
| memory_region_size(orig)); |
| memory_region_add_subregion(get_system_memory(), base, mr); |
| } |
| |
| static void mps2_common_init(MachineState *machine) |
| { |
| MPS2MachineState *mms = MPS2_MACHINE(machine); |
| MPS2MachineClass *mmc = MPS2_MACHINE_GET_CLASS(machine); |
| MemoryRegion *system_memory = get_system_memory(); |
| DeviceState *armv7m; |
| |
| if (!machine->cpu_model) { |
| machine->cpu_model = mmc->cpu_model; |
| } |
| |
| if (strcmp(machine->cpu_model, mmc->cpu_model) != 0) { |
| error_report("This board can only be used with CPU %s", mmc->cpu_model); |
| exit(1); |
| } |
| |
| /* The FPGA images have an odd combination of different RAMs, |
| * because in hardware they are different implementations and |
| * connected to different buses, giving varying performance/size |
| * tradeoffs. For QEMU they're all just RAM, though. We arbitrarily |
| * call the 16MB our "system memory", as it's the largest lump. |
| * |
| * Common to both boards: |
| * 0x21000000..0x21ffffff : PSRAM (16MB) |
| * AN385 only: |
| * 0x00000000 .. 0x003fffff : ZBT SSRAM1 |
| * 0x00400000 .. 0x007fffff : mirror of ZBT SSRAM1 |
| * 0x20000000 .. 0x203fffff : ZBT SSRAM 2&3 |
| * 0x20400000 .. 0x207fffff : mirror of ZBT SSRAM 2&3 |
| * 0x01000000 .. 0x01003fff : block RAM (16K) |
| * 0x01004000 .. 0x01007fff : mirror of above |
| * 0x01008000 .. 0x0100bfff : mirror of above |
| * 0x0100c000 .. 0x0100ffff : mirror of above |
| * AN511 only: |
| * 0x00000000 .. 0x0003ffff : FPGA block RAM |
| * 0x00400000 .. 0x007fffff : ZBT SSRAM1 |
| * 0x20000000 .. 0x2001ffff : SRAM |
| * 0x20400000 .. 0x207fffff : ZBT SSRAM 2&3 |
| * |
| * The AN385 has a feature where the lowest 16K can be mapped |
| * either to the bottom of the ZBT SSRAM1 or to the block RAM. |
| * This is of no use for QEMU so we don't implement it (as if |
| * zbt_boot_ctrl is always zero). |
| */ |
| memory_region_allocate_system_memory(&mms->psram, |
| NULL, "mps.ram", 0x1000000); |
| memory_region_add_subregion(system_memory, 0x21000000, &mms->psram); |
| |
| switch (mmc->fpga_type) { |
| case FPGA_AN385: |
| make_ram(&mms->ssram1, "mps.ssram1", 0x0, 0x400000); |
| make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x400000); |
| make_ram(&mms->ssram23, "mps.ssram23", 0x20000000, 0x400000); |
| make_ram_alias(&mms->ssram23_m, "mps.ssram23_m", |
| &mms->ssram23, 0x20400000); |
| make_ram(&mms->blockram, "mps.blockram", 0x01000000, 0x4000); |
| make_ram_alias(&mms->blockram_m1, "mps.blockram_m1", |
| &mms->blockram, 0x01004000); |
| make_ram_alias(&mms->blockram_m2, "mps.blockram_m2", |
| &mms->blockram, 0x01008000); |
| make_ram_alias(&mms->blockram_m3, "mps.blockram_m3", |
| &mms->blockram, 0x0100c000); |
| break; |
| case FPGA_AN511: |
| make_ram(&mms->blockram, "mps.blockram", 0x0, 0x40000); |
| make_ram(&mms->ssram1, "mps.ssram1", 0x00400000, 0x00800000); |
| make_ram(&mms->sram, "mps.sram", 0x20000000, 0x20000); |
| make_ram(&mms->ssram23, "mps.ssram23", 0x20400000, 0x400000); |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| |
| object_initialize(&mms->armv7m, sizeof(mms->armv7m), TYPE_ARMV7M); |
| armv7m = DEVICE(&mms->armv7m); |
| qdev_set_parent_bus(armv7m, sysbus_get_default()); |
| switch (mmc->fpga_type) { |
| case FPGA_AN385: |
| qdev_prop_set_uint32(armv7m, "num-irq", 32); |
| break; |
| case FPGA_AN511: |
| qdev_prop_set_uint32(armv7m, "num-irq", 64); |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| qdev_prop_set_string(armv7m, "cpu-model", machine->cpu_model); |
| object_property_set_link(OBJECT(&mms->armv7m), OBJECT(system_memory), |
| "memory", &error_abort); |
| object_property_set_bool(OBJECT(&mms->armv7m), true, "realized", |
| &error_fatal); |
| |
| create_unimplemented_device("zbtsmram mirror", 0x00400000, 0x00400000); |
| create_unimplemented_device("RESERVED 1", 0x00800000, 0x00800000); |
| create_unimplemented_device("Block RAM", 0x01000000, 0x00010000); |
| create_unimplemented_device("RESERVED 2", 0x01010000, 0x1EFF0000); |
| create_unimplemented_device("RESERVED 3", 0x20800000, 0x00800000); |
| create_unimplemented_device("PSRAM", 0x21000000, 0x01000000); |
| /* These three ranges all cover multiple devices; we may implement |
| * some of them below (in which case the real device takes precedence |
| * over the unimplemented-region mapping). |
| */ |
| create_unimplemented_device("CMSDK APB peripheral region @0x40000000", |
| 0x40000000, 0x00010000); |
| create_unimplemented_device("CMSDK peripheral region @0x40010000", |
| 0x40010000, 0x00010000); |
| create_unimplemented_device("Extra peripheral region @0x40020000", |
| 0x40020000, 0x00010000); |
| create_unimplemented_device("RESERVED 4", 0x40030000, 0x001D0000); |
| create_unimplemented_device("Ethernet", 0x40200000, 0x00100000); |
| create_unimplemented_device("VGA", 0x41000000, 0x0200000); |
| |
| system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; |
| |
| armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, |
| 0x400000); |
| } |
| |
| static void mps2_class_init(ObjectClass *oc, void *data) |
| { |
| MachineClass *mc = MACHINE_CLASS(oc); |
| |
| mc->init = mps2_common_init; |
| mc->max_cpus = 1; |
| } |
| |
| static void mps2_an385_class_init(ObjectClass *oc, void *data) |
| { |
| MachineClass *mc = MACHINE_CLASS(oc); |
| MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); |
| |
| mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3"; |
| mmc->fpga_type = FPGA_AN385; |
| mmc->cpu_model = "cortex-m3"; |
| } |
| |
| static void mps2_an511_class_init(ObjectClass *oc, void *data) |
| { |
| MachineClass *mc = MACHINE_CLASS(oc); |
| MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); |
| |
| mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3"; |
| mmc->fpga_type = FPGA_AN511; |
| mmc->cpu_model = "cortex-m3"; |
| } |
| |
| static const TypeInfo mps2_info = { |
| .name = TYPE_MPS2_MACHINE, |
| .parent = TYPE_MACHINE, |
| .abstract = true, |
| .instance_size = sizeof(MPS2MachineState), |
| .class_size = sizeof(MPS2MachineClass), |
| .class_init = mps2_class_init, |
| }; |
| |
| static const TypeInfo mps2_an385_info = { |
| .name = TYPE_MPS2_AN385_MACHINE, |
| .parent = TYPE_MPS2_MACHINE, |
| .class_init = mps2_an385_class_init, |
| }; |
| |
| static const TypeInfo mps2_an511_info = { |
| .name = TYPE_MPS2_AN511_MACHINE, |
| .parent = TYPE_MPS2_MACHINE, |
| .class_init = mps2_an511_class_init, |
| }; |
| |
| static void mps2_machine_init(void) |
| { |
| type_register_static(&mps2_info); |
| type_register_static(&mps2_an385_info); |
| type_register_static(&mps2_an511_info); |
| } |
| |
| type_init(mps2_machine_init); |