Hi Alex, Yes. The v2 patch introduced the machine as single-core and deferred core 1 to a later patch. That was the wrong split.
For v3 I will instantiate both Cortex-M0+ cores in the initial SoC patch. Core 1 may remain parked until the later SIO and boot ROM patches introduce its launch protocol, but the machine topology will be dual-core from the start. I assume that by the last two regions you mean SIO and PPB. I will give each core a separate memory container and address space. Each ARMv7M instance will therefore retain its own PPB, NVIC, and SysTick mapping. SIO will expose per-core MMIO views over the shared SIO state, instead of inferring the accessing core from current_cpu. Thanks, -- Gilles Grimaud - 2XS/SISE - CRIStAL/IRCICA - CNRS/Univ. Lille > Le 1 sept. 2026 à 20:33, Alex Bennée <[email protected]> a écrit : > > Gilles Grimaud <[email protected] > <mailto:[email protected]>> writes: > >> From: gilles grimaud <[email protected]> >> >> Continue the Raspberry Pi Pico machine model introduced by Alex Bennée's >> 2022 RFC on current QEMU APIs. Add the RP2040 QOM skeleton, internal ROM and >> SRAM map, the board XIP window, two PL011-backed UARTs, and explicit >> unimplemented-device placeholders for the remaining MMIO blocks.\n\nKeep the >> model single-core for now. Core 1 startup and the SIO launch protocol are >> introduced later with the multicore support. >> >> Signed-off-by: gilles grimaud <[email protected]> >> --- >> MAINTAINERS | 1 + >> configs/devices/arm-softmmu/default.mak | 1 + >> hw/arm/Kconfig | 13 ++ >> hw/arm/meson.build | 2 + >> hw/arm/raspi_pico.c | 85 +++++++++ >> hw/arm/rp2040.c | 242 ++++++++++++++++++++++++ >> include/hw/arm/rp2040.h | 46 +++++ >> 7 files changed, 390 insertions(+) >> create mode 100644 hw/arm/raspi_pico.c >> create mode 100644 hw/arm/rp2040.c >> create mode 100644 include/hw/arm/rp2040.h >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> index db641d9c4c..8fd7385d83 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -1041,6 +1041,7 @@ L: [email protected] >> S: Maintained >> F: hw/*/rp2040* >> F: include/hw/*/rp2040* >> +F: hw/arm/raspi_pico.c >> >> Real View >> M: Peter Maydell <[email protected]> >> diff --git a/configs/devices/arm-softmmu/default.mak >> b/configs/devices/arm-softmmu/default.mak >> index 71cf164511..e7f815dd0d 100644 >> --- a/configs/devices/arm-softmmu/default.mak >> +++ b/configs/devices/arm-softmmu/default.mak >> @@ -33,6 +33,7 @@ >> # CONFIG_OLIMEX_STM32_H405=n >> # CONFIG_MPS2=n >> # CONFIG_RASPI=n >> +CONFIG_RASPI_PICO=y >> # CONFIG_DIGIC=n >> # CONFIG_SABRELITE=n >> # CONFIG_EMCRAFT_SF2=n >> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig >> index 260d2f0751..d93303ab80 100644 >> --- a/hw/arm/Kconfig >> +++ b/hw/arm/Kconfig >> @@ -368,6 +368,19 @@ config RASPI >> select BCM2835_SPI >> select BCM2835_I2C >> >> +config RASPI_PICO >> + bool >> + default y >> + depends on TCG && ARM >> + select RP2040 >> + >> +config RP2040 >> + bool >> + select ARM_V7M >> + select PL011 >> + select RP2040_NYI >> + select UNIMP >> + >> config STM32F100_SOC >> bool >> select ARM_V7M >> diff --git a/hw/arm/meson.build b/hw/arm/meson.build >> index 8ee5307a91..3240ef6637 100644 >> --- a/hw/arm/meson.build >> +++ b/hw/arm/meson.build >> @@ -32,6 +32,8 @@ arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files( >> 'raspi.c', >> 'raspi4b.c' >> )) >> +arm_common_ss.add(when: 'CONFIG_RP2040', if_true: files('rp2040.c')) >> +arm_common_ss.add(when: 'CONFIG_RASPI_PICO', if_true: files('raspi_pico.c')) >> arm_common_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: >> files('stm32f100_soc.c')) >> arm_common_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: >> files('stm32f205_soc.c')) >> arm_common_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: >> files('stm32f405_soc.c')) >> diff --git a/hw/arm/raspi_pico.c b/hw/arm/raspi_pico.c >> new file mode 100644 >> index 0000000000..2bb7d4e511 >> --- /dev/null >> +++ b/hw/arm/raspi_pico.c >> @@ -0,0 +1,85 @@ >> +/* >> + * Raspberry Pi Pico machine >> + * >> + * Copyright (c) 2021 Linaro Ltd >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "qemu/units.h" >> +#include "qapi/error.h" >> +#include "hw/arm/boot.h" >> +#include "hw/arm/machines-qom.h" >> +#include "hw/arm/rp2040.h" >> +#include "hw/core/boards.h" >> +#include "hw/core/qdev-properties.h" >> +#include "system/address-spaces.h" >> +#include "system/system.h" >> +#include "qom/object.h" >> + >> +#define TYPE_RASPI_PICO_MACHINE MACHINE_TYPE_NAME("raspi-pico") >> +OBJECT_DECLARE_SIMPLE_TYPE(RaspiPicoMachineState, RASPI_PICO_MACHINE) >> + >> +#define PICO_FLASH_SIZE (2 * MiB) >> + >> +struct RaspiPicoMachineState { >> + MachineState parent_obj; >> + >> + RP2040State soc; >> + MemoryRegion flash; >> +}; >> + >> +static void raspi_pico_init(MachineState *machine) >> +{ >> + RaspiPicoMachineState *s = RASPI_PICO_MACHINE(machine); >> + MemoryRegion *system_memory = get_system_memory(); >> + >> + object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RP2040); >> + qdev_prop_set_chr(DEVICE(&s->soc), "serial0", serial_hd(0)); >> + qdev_prop_set_chr(DEVICE(&s->soc), "serial1", serial_hd(1)); >> + if (machine->firmware) { >> + qdev_prop_set_string(DEVICE(&s->soc), "bootrom-file", >> + machine->firmware); >> + } >> + object_property_set_link(OBJECT(&s->soc), "memory", >> + OBJECT(system_memory), &error_fatal); >> + >> + memory_region_init_rom(&s->flash, NULL, "raspi-pico.flash", >> + PICO_FLASH_SIZE, &error_fatal); >> + memory_region_add_subregion(system_memory, RP2040_XIP_BASE, &s->flash); >> + >> + sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); >> + >> + armv7m_load_kernel(s->soc.armv7m.cpu, machine->kernel_filename, >> + RP2040_XIP_BASE, PICO_FLASH_SIZE); >> +} >> + >> +static void raspi_pico_machine_class_init(ObjectClass *oc, const void *data) >> +{ >> + MachineClass *mc = MACHINE_CLASS(oc); >> + >> + mc->desc = "Raspberry Pi Pico (Cortex-M0+)"; >> + mc->init = raspi_pico_init; >> + mc->default_cpus = 1; >> + mc->min_cpus = 1; >> + mc->max_cpus = 1; > > picos are duel core aren't they? > >> + mc->default_ram_size = 0; >> + mc->no_parallel = 1; >> + mc->no_floppy = 1; >> + mc->no_cdrom = 1; >> +} >> + >> +static const TypeInfo raspi_pico_machine_info = { >> + .name = TYPE_RASPI_PICO_MACHINE, >> + .parent = TYPE_MACHINE, >> + .instance_size = sizeof(RaspiPicoMachineState), >> + .class_init = raspi_pico_machine_class_init, >> + .interfaces = arm_machine_interfaces, >> +}; >> + >> +static void raspi_pico_machine_init(void) >> +{ >> + type_register_static(&raspi_pico_machine_info); >> +} >> +type_init(raspi_pico_machine_init) >> diff --git a/hw/arm/rp2040.c b/hw/arm/rp2040.c >> new file mode 100644 >> index 0000000000..2196bad18d >> --- /dev/null >> +++ b/hw/arm/rp2040.c >> @@ -0,0 +1,242 @@ >> +/* >> + * RP2040 SoC emulation >> + * >> + * Copyright (c) 2021 Linaro Ltd >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "qemu/units.h" >> +#include "qapi/error.h" >> +#include "hw/arm/rp2040.h" >> +#include "hw/core/loader.h" >> +#include "hw/core/qdev-clock.h" >> +#include "hw/core/qdev-properties.h" >> +#include "hw/misc/unimp.h" >> +#include "qemu/datadir.h" >> +#include "target/arm/cpu-qom.h" >> + >> +#define RP2040_SYSCLK_FRQ 125000000 >> + >> +#define RP2040_UART0_BASE 0x40034000 >> +#define RP2040_UART0_IRQ 20 >> +#define RP2040_UART1_BASE 0x40038000 >> +#define RP2040_UART1_IRQ 21 >> + >> +/* >> + * Temporary boot ROM used until the synthetic ROM is introduced. It loads >> + * the reset handler from a vector table at the base of the XIP window. >> + */ >> +static const uint8_t rp2040_bootrom[] = { >> + 0x00, 0x20, 0x04, 0x20, /* initial SP: 0x20042000 */ >> + 0x09, 0x00, 0x00, 0x00, /* reset handler: 0x00000009 */ >> + 0x03, 0x48, /* ldr r0, vtor */ >> + 0x04, 0x49, /* ldr r1, xip_base */ >> + 0x01, 0x60, /* str r1, [r0] */ >> + 0x04, 0x48, /* ldr r0, xip_reset_vector */ >> + 0x01, 0x68, /* ldr r1, [r0] */ >> + 0x08, 0x47, /* bx r1 */ >> + 0xfe, 0xe7, /* b . */ >> + 0xc0, 0x46, /* nop */ >> + 0x08, 0xed, 0x00, 0xe0, /* VTOR: 0xe000ed08 */ >> + 0x00, 0x00, 0x00, 0x10, /* XIP base: 0x10000000 */ >> + 0x04, 0x00, 0x00, 0x10, /* XIP reset vector: 0x10000004 */ >> +}; >> + >> +static const struct { >> + const char *name; >> + hwaddr base; >> + hwaddr size; >> +} rp2040_unimplemented[] = { >> + { "rp2040.sysinfo", 0x40000000, 0x4000 }, >> + { "rp2040.syscfg", 0x40004000, 0x4000 }, >> + { "rp2040.clocks", 0x40008000, 0x4000 }, >> + { "rp2040.resets", 0x4000c000, 0x4000 }, >> + { "rp2040.psm", 0x40010000, 0x4000 }, >> + { "rp2040.iobank0", 0x40014000, 0x4000 }, >> + { "rp2040.ioqspi", 0x40018000, 0x4000 }, >> + { "rp2040.padsbank0", 0x4001c000, 0x4000 }, >> + { "rp2040.padsqspi", 0x40020000, 0x4000 }, >> + { "rp2040.xosc", 0x40024000, 0x4000 }, >> + { "rp2040.pll_sys", 0x40028000, 0x4000 }, >> + { "rp2040.pll_usb", 0x4002c000, 0x4000 }, >> + { "rp2040.busctrl", 0x40030000, 0x4000 }, >> + { "rp2040.uart0_aliases", 0x40035000, 0x3000 }, >> + { "rp2040.uart1_aliases", 0x40039000, 0x3000 }, >> + { "rp2040.spi0", 0x4003c000, 0x4000 }, >> + { "rp2040.spi1", 0x40040000, 0x4000 }, >> + { "rp2040.i2c0", 0x40044000, 0x4000 }, >> + { "rp2040.i2c1", 0x40048000, 0x4000 }, >> + { "rp2040.adc", 0x4004c000, 0x4000 }, >> + { "rp2040.pwm", 0x40050000, 0x4000 }, >> + { "rp2040.timer", 0x40054000, 0x4000 }, >> + { "rp2040.watchdog", 0x40058000, 0x4000 }, >> + { "rp2040.rtc", 0x4005c000, 0x4000 }, >> + { "rp2040.rosc", 0x40060000, 0x4000 }, >> + { "rp2040.vreg_and_chip_reset", 0x40064000, 0x4000 }, >> + { "rp2040.tbman", 0x4006c000, 0x4000 }, >> + { "rp2040.dma", 0x50000000, 0x1000 }, >> + { "rp2040.usbctrl_dpram", 0x50100000, 0x10000 }, >> + { "rp2040.usbctrl_regs", 0x50110000, 0x10000 }, >> + { "rp2040.pio0", 0x50200000, 0x10000 }, >> + { "rp2040.pio1", 0x50300000, 0x10000 }, >> + { "rp2040.sio", 0xd0000000, 0x1000 }, >> +}; >> + >> +static void rp2040_soc_init(Object *obj) >> +{ >> + RP2040State *s = RP2040(obj); >> + int i; >> + >> + object_initialize_child(obj, "proc0", &s->armv7m, TYPE_ARMV7M); >> + qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", >> + ARM_CPU_TYPE_NAME("cortex-m0")); >> + qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", RP2040_NUM_IRQS); >> + qdev_prop_set_uint32(DEVICE(&s->armv7m), "mpu-ns-regions", 8); >> + >> + for (i = 0; i < ARRAY_SIZE(s->uart); i++) { >> + g_autofree char *name = g_strdup_printf("uart%d", i); >> + g_autofree char *property = g_strdup_printf("serial%d", i); >> + >> + object_initialize_child(obj, name, &s->uart[i], TYPE_PL011); >> + object_property_add_alias(obj, property, OBJECT(&s->uart[i]), >> + "chardev"); >> + } >> + >> + s->sysclk = clock_new(obj, "sysclk"); >> + clock_set_hz(s->sysclk, RP2040_SYSCLK_FRQ); >> +} >> + >> +static bool rp2040_init_memory(RP2040State *s, Error **errp) >> +{ >> + int i; >> + >> + if (!memory_region_init_rom(&s->rom, OBJECT(s), "rp2040.rom", >> + RP2040_ROM_SIZE, errp)) { >> + return false; >> + } >> + memory_region_add_subregion(s->board_memory, RP2040_ROM_BASE, &s->rom); >> + >> + for (i = 0; i < 4; i++) { >> + g_autofree char *name = g_strdup_printf("rp2040.sram%d", i); >> + >> + if (!memory_region_init_ram(&s->sram[i], OBJECT(s), name, >> + RP2040_SRAM_BANK_SIZE, errp)) { >> + return false; >> + } >> + memory_region_add_subregion(s->board_memory, >> + RP2040_SRAM_BASE + >> + i * RP2040_SRAM_BANK_SIZE, >> + &s->sram[i]); >> + } >> + >> + if (!memory_region_init_ram(&s->sram[4], OBJECT(s), "rp2040.sram4", >> + RP2040_SRAM_HI_SIZE, errp)) { >> + return false; >> + } >> + memory_region_add_subregion(s->board_memory, RP2040_SRAM4_BASE, >> + &s->sram[4]); >> + >> + if (!memory_region_init_ram(&s->sram[5], OBJECT(s), "rp2040.sram5", >> + RP2040_SRAM_HI_SIZE, errp)) { >> + return false; >> + } >> + memory_region_add_subregion(s->board_memory, RP2040_SRAM5_BASE, >> + &s->sram[5]); > > I think you need multiple addresses spaces for the last two as they are > banked per-CPU right? > >> + >> + return true; >> +} >> + >> +static bool rp2040_load_bootrom(RP2040State *s, Error **errp) >> +{ >> + g_autofree char *filename = NULL; >> + >> + if (!s->bootrom_file) { >> + rom_add_blob_fixed("rp2040.bootrom", rp2040_bootrom, >> + sizeof(rp2040_bootrom), RP2040_ROM_BASE); >> + return true; >> + } >> + >> + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bootrom_file); >> + if (!filename) { >> + error_setg(errp, "could not find RP2040 boot ROM image '%s'", >> + s->bootrom_file); >> + return false; >> + } >> + >> + return load_image_targphys(filename, RP2040_ROM_BASE, >> + RP2040_ROM_SIZE, errp) >= 0; >> +} >> + >> +static void rp2040_soc_realize(DeviceState *dev, Error **errp) >> +{ >> + RP2040State *s = RP2040(dev); >> + static const hwaddr uart_base[] = { RP2040_UART0_BASE, >> RP2040_UART1_BASE }; >> + static const int uart_irq[] = { RP2040_UART0_IRQ, RP2040_UART1_IRQ }; >> + Error *err = NULL; >> + int i; >> + >> + if (!s->board_memory) { >> + error_setg(errp, "memory property was not set"); >> + return; >> + } >> + if (!rp2040_init_memory(s, errp) || !rp2040_load_bootrom(s, errp)) { >> + return; >> + } >> + >> + for (i = 0; i < ARRAY_SIZE(rp2040_unimplemented); i++) { >> + create_unimplemented_device(rp2040_unimplemented[i].name, >> + rp2040_unimplemented[i].base, >> + rp2040_unimplemented[i].size); >> + } >> + >> + qdev_connect_clock_in(DEVICE(&s->armv7m), "cpuclk", s->sysclk); >> + object_property_set_link(OBJECT(&s->armv7m), "memory", >> + OBJECT(s->board_memory), &err); >> + if (err) { >> + error_propagate(errp, err); >> + return; >> + } >> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { >> + return; >> + } >> + >> + for (i = 0; i < ARRAY_SIZE(s->uart); i++) { >> + qdev_connect_clock_in(DEVICE(&s->uart[i]), "clk", s->sysclk); >> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { >> + return; >> + } >> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_base[i]); >> + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, >> + qdev_get_gpio_in(DEVICE(&s->armv7m), >> uart_irq[i])); >> + } >> +} >> + >> +static const Property rp2040_soc_properties[] = { >> + DEFINE_PROP_LINK("memory", RP2040State, board_memory, >> TYPE_MEMORY_REGION, >> + MemoryRegion *), >> + DEFINE_PROP_STRING("bootrom-file", RP2040State, bootrom_file), >> +}; >> + >> +static void rp2040_soc_class_init(ObjectClass *klass, const void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->realize = rp2040_soc_realize; >> + device_class_set_props(dc, rp2040_soc_properties); >> +} >> + >> +static const TypeInfo rp2040_soc_info = { >> + .name = TYPE_RP2040, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(RP2040State), >> + .instance_init = rp2040_soc_init, >> + .class_init = rp2040_soc_class_init, >> +}; >> + >> +static void rp2040_soc_types(void) >> +{ >> + type_register_static(&rp2040_soc_info); >> +} >> +type_init(rp2040_soc_types) >> diff --git a/include/hw/arm/rp2040.h b/include/hw/arm/rp2040.h >> new file mode 100644 >> index 0000000000..4e5cd06add >> --- /dev/null >> +++ b/include/hw/arm/rp2040.h >> @@ -0,0 +1,46 @@ >> +/* >> + * RP2040 SoC emulation >> + * >> + * Copyright (c) 2021 Linaro Ltd >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#ifndef HW_ARM_RP2040_H >> +#define HW_ARM_RP2040_H >> + >> +#include "hw/arm/armv7m.h" >> +#include "hw/char/pl011.h" >> +#include "hw/core/clock.h" >> +#include "hw/core/sysbus.h" >> +#include "qom/object.h" >> + >> +#define TYPE_RP2040 "rp2040" >> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040State, RP2040) >> + >> +#define RP2040_ROM_BASE 0x00000000 >> +#define RP2040_ROM_SIZE (16 * KiB) >> +#define RP2040_XIP_BASE 0x10000000 >> +#define RP2040_SRAM_BASE 0x20000000 >> +#define RP2040_SRAM_BANK_SIZE (64 * KiB) >> +#define RP2040_SRAM4_BASE 0x20040000 >> +#define RP2040_SRAM5_BASE 0x20041000 >> +#define RP2040_SRAM_HI_SIZE (4 * KiB) >> + >> +#define RP2040_NUM_IRQS 32 >> + >> +struct RP2040State { >> + SysBusDevice parent_obj; >> + >> + ARMv7MState armv7m; >> + PL011State uart[2]; >> + >> + MemoryRegion *board_memory; >> + MemoryRegion rom; >> + MemoryRegion sram[6]; >> + char *bootrom_file; >> + >> + Clock *sysclk; >> +}; >> + >> +#endif > > -- > Alex Bennée > Virtualisation Tech Lead @ Linaro
