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

Reply via email to