Add an RP2040 SoC model and a Raspberry Pi Pico board machine. The machine 
provides Cortex-M0+ cores, SRAM, XIP flash, UART0/UART1, DMA, a synthetic ROM 
boot path, optional external mask ROM loading through -bios, flash-file 
persistence, flash UID configuration, and -kernel loading for ELF, UF2 and raw 
images.

The implementation follows the direction of Alex Bennée's 2022 RP2040/Pico RFC 
series while adapting the code to current QEMU APIs and the functional model 
developed here.

Signed-off-by: Gilles Grimaud <[email protected]>
---
 MAINTAINERS                             |   20 +
 configs/devices/arm-softmmu/default.mak |    1 +
 hw/arm/Kconfig                          |   32 +
 hw/arm/meson.build                      |    2 +
 hw/arm/raspi_pico.c                     |  265 +++
 hw/arm/rp2040.c                         | 2052 +++++++++++++++++++++++
 hw/arm/trace-events                     |    3 +
 include/hw/arm/rp2040.h                 |  122 ++
 8 files changed, 2497 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 97dcc78ded..104c02ff76 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1023,6 +1023,26 @@ F: docs/system/arm/raspi.rst
 F: tests/functional/arm/test_raspi2.py
 F: tests/functional/aarch64/test_raspi*.py
 
+Raspberry Pi Pico / RP2040
+M: Gilles Grimaud <[email protected]>
+R: Alex Bennée <[email protected]>
+L: [email protected]
+S: Maintained
+F: configs/devices/arm-softmmu/default.mak
+F: docs/system/arm/raspi-pico.rst
+F: hw/arm/raspi_pico.c
+F: hw/arm/rp2040.c
+F: hw/dma/rp2040_dma.c
+F: hw/misc/rp2040_*.c
+F: hw/ssi/rp2040_xip.c
+F: include/hw/arm/rp2040.h
+F: include/hw/dma/rp2040_dma.h
+F: include/hw/misc/rp2040_*.h
+F: include/hw/ssi/rp2040_xip.h
+F: scripts/uf2-to-flash.py
+F: tests/functional/arm/test_raspi_pico.py
+F: tests/qtest/rp2040-*.c
+
 Real View
 M: Peter Maydell <[email protected]>
 L: [email protected]
diff --git a/configs/devices/arm-softmmu/default.mak 
b/configs/devices/arm-softmmu/default.mak
index 71cf164511..824d6ebfd9 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=n
 # CONFIG_DIGIC=n
 # CONFIG_SABRELITE=n
 # CONFIG_EMCRAFT_SF2=n
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 5f5c4899ad..125c36cc99 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -367,6 +367,38 @@ 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_BUSCTRL
+    select RP2040_CLOCKS
+    select RP2040_DMA
+    select RP2040_IOBANK0
+    select RP2040_IOQSPI
+    select RP2040_NYI
+    select RP2040_PADS
+    select RP2040_PLL
+    select RP2040_PSM
+    select RP2040_RESETS
+    select RP2040_ROSC
+    select RP2040_SIO
+    select RP2040_SYSCFG
+    select RP2040_SYSINFO
+    select RP2040_TBMAN
+    select RP2040_TIMER
+    select RP2040_VREG
+    select RP2040_WATCHDOG
+    select RP2040_XIP
+    select RP2040_XOSC
+    select UNIMP
+
 config STM32F100_SOC
     bool
     select ARM_V7M
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 4233a800be..0b79b9414a 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -11,6 +11,7 @@ arm_common_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: 
files('netduinoplus2.c'
 arm_common_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: 
files('olimex-stm32-h405.c'))
 arm_common_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 
'npcm7xx_boards.c'))
 arm_common_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 
'npcm8xx_boards.c'))
+arm_common_ss.add(when: 'CONFIG_RASPI_PICO', if_true: files('raspi_pico.c'))
 arm_common_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c'))
 arm_common_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c'))
 arm_common_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c'))
@@ -93,6 +94,7 @@ arm_common_ss.add(when: 'CONFIG_TEGRA241_CMDQV', if_true: 
files('tegra241-cmdqv.
 stub_ss.add(files('tegra241-cmdqv-stubs.c'))
 arm_common_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 
'mcimx6ul-evk.c'))
 arm_common_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
+arm_common_ss.add(when: 'CONFIG_RP2040', if_true: files('rp2040.c'))
 arm_common_ss.add(when: 'CONFIG_XEN', if_true: files(
   'xen-stubs.c',
   'xen-pvh.c',
diff --git a/hw/arm/raspi_pico.c b/hw/arm/raspi_pico.c
new file mode 100644
index 0000000000..ea229214b0
--- /dev/null
+++ b/hw/arm/raspi_pico.c
@@ -0,0 +1,265 @@
+/*
+ * 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 "qemu/cutils.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)
+
+struct RaspiPicoMachineState {
+    MachineState parent_obj;
+
+    RP2040State soc;
+    char *flash_file;
+    char *flash_uid;
+    uint64_t flash_uid_value;
+    bool flash_uid_set;
+    char *rosc_random_seed;
+    uint64_t rosc_random_seed_value;
+    bool rosc_random_seed_set;
+    bool strict_uart_pins;
+};
+
+static char *raspi_pico_get_flash_file(Object *obj, Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    return g_strdup(s->flash_file ?: "");
+}
+
+static void raspi_pico_set_flash_file(Object *obj, const char *value,
+                                      Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    g_free(s->flash_file);
+    s->flash_file = g_strdup(value);
+}
+
+static char *raspi_pico_get_flash_uid(Object *obj, Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    return g_strdup(s->flash_uid ?: "");
+}
+
+static void raspi_pico_set_flash_uid(Object *obj, const char *value,
+                                     Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+    const char *p;
+    uint64_t uid;
+    int i;
+
+    if (!value || !*value) {
+        g_free(s->flash_uid);
+        s->flash_uid = NULL;
+        s->flash_uid_value = 0;
+        s->flash_uid_set = false;
+        return;
+    }
+
+    p = g_str_has_prefix(value, "0x") || g_str_has_prefix(value, "0X") ?
+        value + 2 : value;
+    if (strlen(p) != 16) {
+        error_setg(errp, "flash-uid must be exactly 16 hexadecimal digits");
+        return;
+    }
+    for (i = 0; i < 16; i++) {
+        if (!g_ascii_isxdigit(p[i])) {
+            error_setg(errp, "invalid flash-uid '%s'", value);
+            return;
+        }
+    }
+    if (qemu_strtou64(p, NULL, 16, &uid) < 0) {
+        error_setg(errp, "invalid flash-uid '%s'", value);
+        return;
+    }
+
+    g_free(s->flash_uid);
+    s->flash_uid = g_strdup(value);
+    s->flash_uid_value = uid;
+    s->flash_uid_set = true;
+}
+
+static char *raspi_pico_get_rosc_random_seed(Object *obj, Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    return g_strdup(s->rosc_random_seed ?: "");
+}
+
+static void raspi_pico_set_rosc_random_seed(Object *obj, const char *value,
+                                            Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+    uint64_t seed;
+
+    if (!value || !*value) {
+        g_free(s->rosc_random_seed);
+        s->rosc_random_seed = NULL;
+        s->rosc_random_seed_value = 0;
+        s->rosc_random_seed_set = false;
+        return;
+    }
+
+    if (qemu_strtou64(value, NULL, 0, &seed) < 0) {
+        error_setg(errp, "invalid ROSC random seed '%s'", value);
+        return;
+    }
+
+    g_free(s->rosc_random_seed);
+    s->rosc_random_seed = g_strdup(value);
+    s->rosc_random_seed_value = seed;
+    s->rosc_random_seed_set = true;
+}
+
+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));
+    qdev_prop_set_bit(DEVICE(&s->soc), "strict-uart-pins",
+                      s->strict_uart_pins);
+    qdev_prop_set_uint64(DEVICE(&s->soc.rosc), "random-seed",
+                         s->rosc_random_seed_value);
+    qdev_prop_set_bit(DEVICE(&s->soc.rosc), "random-seed-set",
+                      s->rosc_random_seed_set);
+    /*
+     * BOOTSEL is not pressed by default on a Pico board, so the mask ROM sees
+     * the QSPI SS input deasserted and tries to boot from external flash.
+     */
+    qdev_prop_set_uint32(DEVICE(&s->soc.sio), "gpio-hi-in", 1u << 1);
+    if (s->flash_file) {
+        qdev_prop_set_string(DEVICE(&s->soc.xip), "flash-file",
+                             s->flash_file);
+    }
+    if (s->flash_uid_set) {
+        qdev_prop_set_uint64(DEVICE(&s->soc.xip), "flash-uid",
+                             s->flash_uid_value);
+    }
+    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);
+
+    sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal);
+
+    /*
+     * For now, -kernel images are loaded directly into the XIP window.
+     * rp2040_xip_load_image() accepts ELF images first, then raw images.
+     */
+    rp2040_xip_load_image(&s->soc.xip, machine->kernel_filename,
+                          &error_fatal);
+    armv7m_load_kernel(s->soc.armv7m[0].cpu, NULL, RP2040_XIP_BASE, 2 * MiB);
+    rp2040_xip_set_writable(&s->soc.xip, false);
+}
+
+static void raspi_pico_machine_finalize(Object *obj)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    g_free(s->flash_file);
+    g_free(s->flash_uid);
+    g_free(s->rosc_random_seed);
+}
+
+static bool raspi_pico_get_strict_uart_pins(Object *obj, Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    return s->strict_uart_pins;
+}
+
+static void raspi_pico_set_strict_uart_pins(Object *obj, bool value,
+                                            Error **errp)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    s->strict_uart_pins = value;
+}
+
+static void raspi_pico_machine_initfn(Object *obj)
+{
+    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
+
+    s->strict_uart_pins = true;
+}
+
+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 = RP2040_NUM_CORES;
+    mc->min_cpus = RP2040_NUM_CORES;
+    mc->max_cpus = RP2040_NUM_CORES;
+    mc->no_parallel = 1;
+    mc->no_floppy = 1;
+    mc->no_cdrom = 1;
+
+    object_class_property_add_str(oc, "flash-file",
+                                  raspi_pico_get_flash_file,
+                                  raspi_pico_set_flash_file);
+    object_class_property_set_description(oc, "flash-file",
+                                          "Load initial XIP flash contents "
+                                          "from a raw host file");
+    object_class_property_add_str(oc, "flash-uid",
+                                  raspi_pico_get_flash_uid,
+                                  raspi_pico_set_flash_uid);
+    object_class_property_set_description(oc, "flash-uid",
+                                          "Set the emulated external flash "
+                                          "64-bit unique ID as 16 hex digits");
+    object_class_property_add_str(oc, "rosc-random-seed",
+                                  raspi_pico_get_rosc_random_seed,
+                                  raspi_pico_set_rosc_random_seed);
+    object_class_property_set_description(oc, "rosc-random-seed",
+                                          "Use a deterministic seed for the "
+                                          "ROSC RANDOMBIT stream; if unset, "
+                                          "QEMU guest entropy is used");
+    object_class_property_add_bool(oc, "strict-uart-pins",
+                                   raspi_pico_get_strict_uart_pins,
+                                   raspi_pico_set_strict_uart_pins);
+    object_class_property_set_description(oc, "strict-uart-pins",
+                                          "Require GPIO0/GPIO1 IO_BANK0 "
+                                          "FUNCSEL=UART before UART0 reaches "
+                                          "the host serial backend");
+}
+
+static const TypeInfo raspi_pico_machine_info = {
+    .name = TYPE_RASPI_PICO_MACHINE,
+    .parent = TYPE_MACHINE,
+    .instance_size = sizeof(RaspiPicoMachineState),
+    .instance_init = raspi_pico_machine_initfn,
+    .class_init = raspi_pico_machine_class_init,
+    .instance_finalize = raspi_pico_machine_finalize,
+    .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..aecfb9284b
--- /dev/null
+++ b/hw/arm/rp2040.c
@@ -0,0 +1,2052 @@
+/*
+ * 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/qdev-clock.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/loader.h"
+#include "hw/misc/rp2040_nyi.h"
+#include "hw/misc/unimp.h"
+#include "fpu/softfloat.h"
+#include "qemu/datadir.h"
+#include "qemu/log.h"
+#include "system/runstate.h"
+#include "target/arm/cpu.h"
+#include "target/arm/cpu-qom.h"
+#include "trace.h"
+
+#include <math.h>
+
+#define RP2040_UART0_BASE 0x40034000
+#define RP2040_UART0_IRQ  20
+#define RP2040_UART1_BASE 0x40038000
+#define RP2040_UART1_IRQ  21
+#define RP2040_DMA_IRQ_0  11
+#define RP2040_DMA_IRQ_1  12
+#define RP2040_IO_IRQ_BANK0 13
+#define RP2040_SIO_IRQ_PROC0 15
+#define RP2040_SIO_IRQ_PROC1 16
+#define RP2040_PROC1       1
+
+#define RP2040_BOOTROM_FUNC_TABLE_PTR_OFFSET 0x14
+#define RP2040_BOOTROM_DATA_TABLE_PTR_OFFSET 0x16
+#define RP2040_BOOTROM_TABLE_LOOKUP_PTR_OFFSET 0x18
+#define RP2040_BOOTROM_LOOKUP_OFFSET 0x0100
+#define RP2040_BOOTROM_STUBS_OFFSET 0x0120
+#define RP2040_BOOTROM_FUNC_TABLE_OFFSET 0x0300
+#define RP2040_BOOTROM_DATA_TABLE_OFFSET 0x0340
+#define RP2040_BOOTROM_FLOAT_NYI_STUB_OFFSET 0x0360
+#define RP2040_BOOTROM_DOUBLE_NYI_STUB_OFFSET 0x0380
+#define RP2040_BOOTROM_FLOAT_TABLE_OFFSET 0x03c0
+#define RP2040_BOOTROM_DOUBLE_TABLE_OFFSET 0x0460
+#define RP2040_BOOTROM_FLOAT_STUBS_OFFSET 0x0600
+#define RP2040_BOOTROM_DOUBLE_STUBS_OFFSET 0x0b00
+#define RP2040_BOOTROM_FP_STUB_SIZE 36
+#define RP2040_BOOTROM_HARDFAULT_EXIT_OFFSET 0x0f80
+#define RP2040_BOOTROM_FUNC_TABLE_ENTRY_SIZE 4
+#define RP2040_BOOTROM_DATA_TABLE_ENTRY_SIZE 4
+#define RP2040_BOOTROM_NYI_CODE_LITERAL_OFFSET 20
+#define RP2040_BOOTROM_HELPER_NOARG_CODE_LITERAL_OFFSET 8
+#define RP2040_BOOTROM_HELPER_ARGS4_CODE_LITERAL_OFFSET 24
+#define RP2040_BOOTROM_HELPER_ARGS3_CODE_LITERAL_OFFSET 20
+#define RP2040_BOOTROM_FP_STUB_CODE_LITERAL_OFFSET 32
+#define RP2040_BOOTROM_ROM_VERSION_OFFSET 0x13
+#define RP2040_BOOTROM_SYNTHETIC_ROM_VERSION 2
+#define RP2040_BOOTROM_FLOAT_TABLE_WORDS 32
+
+#define RP2040_SYNTHETIC_ROM_DBG_CMD  0x00
+#define RP2040_SYNTHETIC_ROM_DBG_ARG0 0x04
+#define RP2040_SYNTHETIC_ROM_DBG_ARG1 0x08
+#define RP2040_SYNTHETIC_ROM_DBG_ARG2 0x0c
+#define RP2040_SYNTHETIC_ROM_DBG_ARG3 0x10
+#define RP2040_SYNTHETIC_ROM_DBG_RESULT0 0x14
+#define RP2040_SYNTHETIC_ROM_DBG_RESULT1 0x18
+#define RP2040_SYNTHETIC_ROM_DBG_RESULT2 0x1c
+#define RP2040_SYNTHETIC_ROM_DBG_RESULT3 0x20
+#define RP2040_SYNTHETIC_ROM_DBG_FLASH_COUNT0 0x40
+
+#define RP2040_SYNTHETIC_ROM_DBG_CMD_EXIT 0x54495845 /* "EXIT" */
+
+#define RP2040_SYNTHETIC_FP_CMD_MASK   0xffffff00
+#define RP2040_SYNTHETIC_FP_CMD_FLOAT  0x80000000
+#define RP2040_SYNTHETIC_FP_CMD_DOUBLE 0x80000100
+
+enum RP2040SyntheticFlashHelper {
+    RP2040_SYNTHETIC_FLASH_CONNECT_INTERNAL_FLASH,
+    RP2040_SYNTHETIC_FLASH_EXIT_XIP,
+    RP2040_SYNTHETIC_FLASH_FLUSH_CACHE,
+    RP2040_SYNTHETIC_FLASH_ENTER_CMD_XIP,
+    RP2040_SYNTHETIC_FLASH_RANGE_ERASE,
+    RP2040_SYNTHETIC_FLASH_RANGE_PROGRAM,
+};
+
+#define RP2040_SF_TABLE_FADD            0x00
+#define RP2040_SF_TABLE_FSUB            0x04
+#define RP2040_SF_TABLE_FMUL            0x08
+#define RP2040_SF_TABLE_FDIV            0x0c
+#define RP2040_SF_TABLE_FCMP_FAST       0x10
+#define RP2040_SF_TABLE_FCMP_FAST_FLAGS 0x14
+#define RP2040_SF_TABLE_FSQRT           0x18
+#define RP2040_SF_TABLE_FLOAT2INT       0x1c
+#define RP2040_SF_TABLE_FLOAT2FIX       0x20
+#define RP2040_SF_TABLE_FLOAT2UINT      0x24
+#define RP2040_SF_TABLE_FLOAT2UFIX      0x28
+#define RP2040_SF_TABLE_INT2FLOAT       0x2c
+#define RP2040_SF_TABLE_FIX2FLOAT       0x30
+#define RP2040_SF_TABLE_UINT2FLOAT      0x34
+#define RP2040_SF_TABLE_UFIX2FLOAT      0x38
+#define RP2040_SF_TABLE_FCOS            0x3c
+#define RP2040_SF_TABLE_FSIN            0x40
+#define RP2040_SF_TABLE_FTAN            0x44
+#define RP2040_SF_TABLE_V3_FSINCOS      0x48
+#define RP2040_SF_TABLE_FEXP            0x4c
+#define RP2040_SF_TABLE_FLN             0x50
+#define RP2040_SF_TABLE_FCMP_BASIC      0x54
+#define RP2040_SF_TABLE_FATAN2          0x58
+#define RP2040_SF_TABLE_INT642FLOAT     0x5c
+#define RP2040_SF_TABLE_FIX642FLOAT     0x60
+#define RP2040_SF_TABLE_UINT642FLOAT    0x64
+#define RP2040_SF_TABLE_UFIX642FLOAT    0x68
+#define RP2040_SF_TABLE_FLOAT2INT64     0x6c
+#define RP2040_SF_TABLE_FLOAT2FIX64     0x70
+#define RP2040_SF_TABLE_FLOAT2UINT64    0x74
+#define RP2040_SF_TABLE_FLOAT2UFIX64    0x78
+#define RP2040_SF_TABLE_FLOAT2DOUBLE    0x7c
+
+#define USBCTRL_ADDR_ENDP       0x00
+#define USBCTRL_SIE_CTRL        0x4c
+#define USBCTRL_SIE_STATUS      0x50
+#define USBCTRL_INT_EP_CTRL     0x54
+#define USBCTRL_BUFF_STATUS     0x58
+#define USBCTRL_BUFF_CPU_HANDLE 0x5c
+#define USBCTRL_EP_ABORT        0x60
+#define USBCTRL_EP_ABORT_DONE   0x64
+#define USBCTRL_EP_STALL_ARM    0x68
+#define USBCTRL_NAK_POLL        0x6c
+#define USBCTRL_EP_STATUS       0x70
+#define USBCTRL_USB_MUXING      0x74
+#define USBCTRL_USB_PWR         0x78
+#define USBCTRL_USBPHY_DIRECT   0x7c
+#define USBCTRL_USBPHY_TRIM     0x80
+#define USBCTRL_INTR            0x8c
+#define USBCTRL_INTE            0x90
+#define USBCTRL_INTF            0x94
+#define USBCTRL_INTS            0x98
+
+#define USBCTRL_SIE_STATUS_VBUS_DETECTED BIT(11)
+
+#define ATOMIC_ALIAS_MASK 0x3000
+#define ATOMIC_XOR        0x1000
+#define ATOMIC_SET        0x2000
+#define ATOMIC_CLR        0x3000
+
+/*
+ * Temporary boot ROM used until a faithful RP2040 boot ROM is requested.  It
+ * uses SIO_CPUID to split core behavior: core 0 copies the 256-byte XIP
+ * second-stage boot code into SRAM and branches to the SRAM copy; core 1 waits
+ * in ROM for the Pico SDK launch FIFO sequence, echoes the received words,
+ * installs VTOR/MSP, then branches to the received entry point.  Real RP2040
+ * mask ROM performs more checks, but boot2 expects to run from SRAM while it
+ * configures XIP.
+ */
+static const uint8_t rp2040_bootrom[] = {
+    0x00, 0x20, 0x04, 0x20, /* initial SP: 0x20042000 */
+    0x41, 0x00, 0x00, 0x00, /* reset handler: 0x00000041 */
+    0xb3, 0x00, 0x00, 0x00, /* NMI handler: 0x000000b3 */
+    0xb3, 0x00, 0x00, 0x00, /* HardFault handler: 0x000000b3 */
+    0xb3, 0x00, 0x00, 0x00, /* reserved */
+    0xb3, 0x00, 0x00, 0x00, /* reserved */
+    0xb3, 0x00, 0x00, 0x00, /* reserved */
+    0x00, 0x00, 0x00, 0x00, /* reserved */
+    0x00, 0x00, 0x00, 0x00, /* reserved */
+    0x00, 0x00, 0x00, 0x00, /* reserved */
+    0x00, 0x00, 0x00, 0x00, /* reserved */
+    0xb3, 0x00, 0x00, 0x00, /* SVC handler: 0x000000b3 */
+    0x00, 0x00, 0x00, 0x00, /* reserved */
+    0x00, 0x00, 0x00, 0x00, /* reserved */
+    0xb3, 0x00, 0x00, 0x00, /* PendSV handler: 0x000000b3 */
+    0xb3, 0x00, 0x00, 0x00, /* SysTick handler: 0x000000b3 */
+    0x26, 0x4c,             /* ldr r4, [pc, #152] ; SIO_BASE */
+    0x20, 0x68,             /* ldr r0, [r4] ; SIO_CPUID */
+    0x00, 0x28,             /* cmp r0, #0 */
+    0x13, 0xd1,             /* bne core1 path */
+    0x25, 0x48,             /* ldr r0, [pc, #148] ; 0x10000000 */
+    0x26, 0x49,             /* ldr r1, [pc, #152] ; 0x20041f00 */
+    0x40, 0x22,             /* movs r2, #64 */
+    0x03, 0x68,             /* ldr r3, [r0] */
+    0x0b, 0x60,             /* str r3, [r1] */
+    0x04, 0x30,             /* adds r0, #4 */
+    0x04, 0x31,             /* adds r1, #4 */
+    0x01, 0x3a,             /* subs r2, #1 */
+    0xf9, 0xd1,             /* bne copy loop */
+    0x23, 0x4b,             /* ldr r3, [pc, #140] ; launch entry */
+    0x9e, 0x46,             /* mov lr, r3 */
+    0x23, 0x48,             /* ldr r0, [pc, #140] ; 0x20041f01 */
+    0x00, 0x47,             /* bx r0 */
+    0x23, 0x48,             /* ldr r0, [pc, #140] ; 0x10000100 */
+    0x23, 0x49,             /* ldr r1, [pc, #140] ; VTOR */
+    0x08, 0x60,             /* str r0, [r1] */
+    0x06, 0xc8,             /* ldm r0!, {r1, r2} */
+    0x81, 0xf3, 0x08, 0x88, /* msr msp, r1 */
+    0x10, 0x47,             /* bx r2 */
+    0x21, 0x4d,             /* ldr r5, [pc, #132] ; sequence */
+    0x00, 0x26,             /* movs r6, #0 */
+    0x00, 0xf0, 0x1e, 0xf8, /* bl fifo_pop */
+    0x03, 0x2e,             /* cmp r6, #3 */
+    0x07, 0xd2,             /* bhs echo */
+    0xb1, 0x00,             /* lsls r1, r6, #2 */
+    0x6a, 0x58,             /* ldr r2, [r5, r1] */
+    0x90, 0x42,             /* cmp r0, r2 */
+    0x0c, 0xd0,             /* beq echo */
+    0x00, 0x26,             /* movs r6, #0 */
+    0x00, 0xf0, 0x1c, 0xf8, /* bl fifo_push */
+    0xf3, 0xe7,             /* b core1 loop */
+    0x03, 0x2e,             /* cmp r6, #3 */
+    0x01, 0xd1,             /* bne maybe SP */
+    0x07, 0x46,             /* mov r7, r0 */
+    0x04, 0xe0,             /* b echo */
+    0x04, 0x2e,             /* cmp r6, #4 */
+    0x01, 0xd1,             /* bne save PC */
+    0x03, 0x46,             /* mov r3, r0 */
+    0x00, 0xe0,             /* b echo */
+    0x05, 0x46,             /* mov r5, r0 */
+    0x00, 0xf0, 0x10, 0xf8, /* bl fifo_push */
+    0x01, 0x36,             /* adds r6, #1 */
+    0x06, 0x2e,             /* cmp r6, #6 */
+    0xe5, 0xd1,             /* bne core1 loop */
+    0x12, 0x49,             /* ldr r1, [pc, #72] ; VTOR */
+    0x0f, 0x60,             /* str r7, [r1] */
+    0x83, 0xf3, 0x08, 0x88, /* msr msp, r3 */
+    0x28, 0x47,             /* bx r5 */
+    0xfe, 0xe7,             /* hang */
+    0x09, 0x4c,             /* ldr r4, [pc, #36] ; SIO_BASE */
+    0x20, 0x6d,             /* ldr r0, [r4, #0x50] */
+    0x01, 0x21,             /* movs r1, #1 */
+    0x08, 0x42,             /* tst r0, r1 */
+    0xfb, 0xd0,             /* beq fifo_pop */
+    0xa0, 0x6d,             /* ldr r0, [r4, #0x58] */
+    0x70, 0x47,             /* bx lr */
+    0x06, 0x4c,             /* ldr r4, [pc, #24] ; SIO_BASE */
+    0x21, 0x6d,             /* ldr r1, [r4, #0x50] */
+    0x02, 0x22,             /* movs r2, #2 */
+    0x11, 0x42,             /* tst r1, r2 */
+    0xfb, 0xd0,             /* beq fifo_push */
+    0x60, 0x65,             /* str r0, [r4, #0x54] */
+    0x70, 0x47,             /* bx lr */
+    0x00, 0x00, 0x00, 0x00, /* core1 sequence[0] */
+    0x00, 0x00, 0x00, 0x00, /* core1 sequence[1] */
+    0x01, 0x00, 0x00, 0x00, /* core1 sequence[2] */
+    0x00, 0x00, 0x00, 0xd0, /* SIO_BASE */
+    0x00, 0x00, 0x00, 0x10, /* boot2 source: 0x10000000 */
+    0x00, 0x1f, 0x04, 0x20, /* boot2 SRAM copy: 0x20041f00 */
+    0x63, 0x00, 0x00, 0x00, /* post-boot2 launch entry: 0x00000063 */
+    0x01, 0x1f, 0x04, 0x20, /* boot2 SRAM entry: 0x20041f01 */
+    0x00, 0x01, 0x00, 0x10, /* application vectors: 0x10000100 */
+    0x08, 0xed, 0x00, 0xe0, /* VTOR: 0xe000ed08 */
+    0xd0, 0x00, 0x00, 0x00, /* core1 sequence table */
+};
+
+static const struct {
+    const char *name;
+    hwaddr base;
+    hwaddr size;
+} rp2040_unimplemented[] = {
+    { "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.rtc",      0x4005c000, 0x4000 },
+    { "rp2040.pio0",     0x50200000, 0x10000 },
+    { "rp2040.pio1",     0x50300000, 0x10000 },
+};
+
+typedef struct RP2040BootromFunction {
+    uint16_t code;
+    const char *name;
+    const uint8_t *impl;
+    size_t impl_size;
+    uint32_t code_literal_offset;
+} RP2040BootromFunction;
+
+typedef struct RP2040BootromData {
+    uint16_t code;
+    uint16_t offset;
+} RP2040BootromData;
+
+#define RP2040_ROM_TABLE_CODE(c1, c2) ((uint16_t)(c1) | ((uint16_t)(c2) << 8))
+
+static const uint8_t rp2040_bootrom_clz32[] = {
+    0x00, 0x21, 0x00, 0x28, 0x01, 0xd1, 0x20, 0x20,
+    0x70, 0x47, 0x00, 0x28, 0x02, 0xd4, 0x40, 0x00,
+    0x01, 0x31, 0xfa, 0xe7, 0x08, 0x46, 0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_ctz32[] = {
+    0x00, 0x21, 0x00, 0x28, 0x01, 0xd1, 0x20, 0x20,
+    0x70, 0x47, 0x01, 0x22, 0x10, 0x42, 0x02, 0xd1,
+    0x40, 0x08, 0x01, 0x31, 0xf9, 0xe7, 0x08, 0x46,
+    0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_popcount32[] = {
+    0x00, 0x21, 0x00, 0x28, 0x04, 0xd0, 0x01, 0x22,
+    0x02, 0x40, 0x89, 0x18, 0x40, 0x08, 0xf8, 0xe7,
+    0x08, 0x46, 0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_reverse32[] = {
+    0x00, 0x21, 0x20, 0x22, 0x49, 0x00, 0x01, 0x23,
+    0x03, 0x40, 0x19, 0x43, 0x40, 0x08, 0x01, 0x3a,
+    0xf8, 0xd1, 0x08, 0x46, 0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_memcpy[] = {
+    0x10, 0xb4, 0x03, 0x46, 0x00, 0x2a, 0x05, 0xd0,
+    0x0c, 0x78, 0x04, 0x70, 0x01, 0x31, 0x01, 0x30,
+    0x01, 0x3a, 0xf7, 0xe7, 0x18, 0x46, 0x10, 0xbc,
+    0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_memset[] = {
+    0x03, 0x46, 0x00, 0x2a, 0x03, 0xd0, 0x01, 0x70,
+    0x01, 0x30, 0x01, 0x3a, 0xf9, 0xe7, 0x18, 0x46,
+    0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_memcpy44[] = {
+    0x10, 0xb4, 0x03, 0x46, 0x00, 0x2a, 0x05, 0xd0,
+    0x0c, 0x68, 0x04, 0x60, 0x04, 0x31, 0x04, 0x30,
+    0x01, 0x3a, 0xf7, 0xe7, 0x18, 0x46, 0x10, 0xbc,
+    0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_memset4[] = {
+    0x03, 0x46, 0x00, 0x2a, 0x03, 0xd0, 0x01, 0x60,
+    0x04, 0x30, 0x01, 0x3a, 0xf9, 0xe7, 0x18, 0x46,
+    0x70, 0x47,
+};
+
+static const uint8_t rp2040_bootrom_flash_noarg[] = {
+    0x01, 0x49,             /* ldr r1, [pc, #4] ; function code */
+    0x02, 0x48,             /* ldr r0, [pc, #8] ; debug base */
+    0x01, 0x60,             /* str r1, [r0] */
+    0x70, 0x47,             /* bx lr */
+    0x00, 0x00, 0x00, 0x00, /* function code literal */
+    0x00, 0x00, 0xff, 0x5f, /* 0x5fff0000 */
+};
+
+static const uint8_t rp2040_bootrom_flash_args4[] = {
+    0x10, 0xb5,             /* push {r4, lr} */
+    0x04, 0x4c,             /* ldr r4, [pc, #16] ; debug base */
+    0x60, 0x60,             /* str r0, [r4, #4] */
+    0xa1, 0x60,             /* str r1, [r4, #8] */
+    0xe2, 0x60,             /* str r2, [r4, #12] */
+    0x23, 0x61,             /* str r3, [r4, #16] */
+    0x02, 0x48,             /* ldr r0, [pc, #8] ; function code */
+    0x20, 0x60,             /* str r0, [r4] */
+    0x10, 0xbd,             /* pop {r4, pc} */
+    0xc0, 0x46,             /* nop; align literal */
+    0x00, 0x00, 0xff, 0x5f, /* 0x5fff0000 */
+    0x00, 0x00, 0x00, 0x00, /* function code literal */
+};
+
+static const uint8_t rp2040_bootrom_flash_args3[] = {
+    0x10, 0xb5,             /* push {r4, lr} */
+    0x03, 0x4c,             /* ldr r4, [pc, #12] ; debug base */
+    0x60, 0x60,             /* str r0, [r4, #4] */
+    0xa1, 0x60,             /* str r1, [r4, #8] */
+    0xe2, 0x60,             /* str r2, [r4, #12] */
+    0x02, 0x48,             /* ldr r0, [pc, #8] ; function code */
+    0x20, 0x60,             /* str r0, [r4] */
+    0x10, 0xbd,             /* pop {r4, pc} */
+    0x00, 0x00, 0xff, 0x5f, /* 0x5fff0000 */
+    0x00, 0x00, 0x00, 0x00, /* function code literal */
+};
+
+static const uint8_t rp2040_bootrom_fp_stub[] = {
+    0x10, 0xb5,             /* push {r4, lr} */
+    0x06, 0x4c,             /* ldr r4, [pc, #24] ; debug base */
+    0x60, 0x60,             /* str r0, [r4, #4] */
+    0xa1, 0x60,             /* str r1, [r4, #8] */
+    0xe2, 0x60,             /* str r2, [r4, #12] */
+    0x23, 0x61,             /* str r3, [r4, #16] */
+    0x04, 0x48,             /* ldr r0, [pc, #16] ; command */
+    0x20, 0x60,             /* str r0, [r4] */
+    0x60, 0x69,             /* ldr r0, [r4, #20] */
+    0xa1, 0x69,             /* ldr r1, [r4, #24] */
+    0xe2, 0x69,             /* ldr r2, [r4, #28] */
+    0x23, 0x6a,             /* ldr r3, [r4, #32] */
+    0x10, 0xbd,             /* pop {r4, pc} */
+    0xc0, 0x46,             /* nop; align literal */
+    0x00, 0x00, 0xff, 0x5f, /* 0x5fff0000 */
+    0x00, 0x00, 0x00, 0x00, /* command literal */
+};
+
+#define RP2040_BOOTROM_IMPL(_code, _name, _impl) \
+    { _code, _name, _impl, sizeof(_impl), UINT32_MAX }
+#define RP2040_BOOTROM_IMPL_CODE(_code, _name, _impl, _offset) \
+    { _code, _name, _impl, sizeof(_impl), _offset }
+#define RP2040_BOOTROM_NYI(_code, _name) \
+    { _code, _name, NULL, 0, UINT32_MAX }
+
+static const RP2040BootromFunction rp2040_bootrom_functions[] = {
+    RP2040_BOOTROM_IMPL_CODE(RP2040_ROM_TABLE_CODE('C', 'X'),
+                             "flash_enter_cmd_xip",
+                             rp2040_bootrom_flash_noarg,
+                             RP2040_BOOTROM_HELPER_NOARG_CODE_LITERAL_OFFSET),
+    RP2040_BOOTROM_IMPL_CODE(RP2040_ROM_TABLE_CODE('E', 'X'),
+                             "flash_exit_xip",
+                             rp2040_bootrom_flash_noarg,
+                             RP2040_BOOTROM_HELPER_NOARG_CODE_LITERAL_OFFSET),
+    RP2040_BOOTROM_IMPL_CODE(RP2040_ROM_TABLE_CODE('F', 'C'),
+                             "flash_flush_cache",
+                             rp2040_bootrom_flash_noarg,
+                             RP2040_BOOTROM_HELPER_NOARG_CODE_LITERAL_OFFSET),
+    RP2040_BOOTROM_IMPL_CODE(RP2040_ROM_TABLE_CODE('I', 'F'),
+                             "connect_internal_flash",
+                             rp2040_bootrom_flash_noarg,
+                             RP2040_BOOTROM_HELPER_NOARG_CODE_LITERAL_OFFSET),
+    RP2040_BOOTROM_IMPL_CODE(RP2040_ROM_TABLE_CODE('R', 'E'),
+                             "flash_range_erase",
+                             rp2040_bootrom_flash_args4,
+                             RP2040_BOOTROM_HELPER_ARGS4_CODE_LITERAL_OFFSET),
+    RP2040_BOOTROM_IMPL_CODE(RP2040_ROM_TABLE_CODE('R', 'P'),
+                             "flash_range_program",
+                             rp2040_bootrom_flash_args3,
+                             RP2040_BOOTROM_HELPER_ARGS3_CODE_LITERAL_OFFSET),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('C', '4'), "memcpy44",
+                        rp2040_bootrom_memcpy44),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('L', '3'), "clz32",
+                        rp2040_bootrom_clz32),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('M', 'C'), "memcpy",
+                        rp2040_bootrom_memcpy),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('M', 'S'), "memset",
+                        rp2040_bootrom_memset),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('P', '3'), "popcount32",
+                        rp2040_bootrom_popcount32),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('R', '3'), "reverse32",
+                        rp2040_bootrom_reverse32),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('S', '4'), "memset4",
+                        rp2040_bootrom_memset4),
+    RP2040_BOOTROM_IMPL(RP2040_ROM_TABLE_CODE('T', '3'), "ctz32",
+                        rp2040_bootrom_ctz32),
+    RP2040_BOOTROM_NYI(RP2040_ROM_TABLE_CODE('U', 'B'), "reset_usb_boot"),
+};
+
+static const RP2040BootromData rp2040_bootrom_data[] = {
+    { RP2040_ROM_TABLE_CODE('S', 'F'), RP2040_BOOTROM_FLOAT_TABLE_OFFSET + 2 },
+    { RP2040_ROM_TABLE_CODE('S', 'D'), RP2040_BOOTROM_DOUBLE_TABLE_OFFSET + 2 
},
+};
+
+static const uint8_t rp2040_bootrom_lookup[] = {
+    0x02, 0x88,             /* ldrh r2, [r0] */
+    0x00, 0x2a,             /* cmp r2, #0 */
+    0x05, 0xd0,             /* beq not_found */
+    0x91, 0x42,             /* cmp r1, r2 */
+    0x01, 0xd0,             /* beq found */
+    0x04, 0x30,             /* adds r0, #4 */
+    0xf8, 0xe7,             /* b loop */
+    0x40, 0x88,             /* found: ldrh r0, [r0, #2] */
+    0x70, 0x47,             /* bx lr */
+    0x5f, 0x22,             /* not_found: movs r2, #0x5f */
+    0x12, 0x06,             /* lsls r2, r2, #24 */
+    0xff, 0x23,             /* movs r3, #0xff */
+    0x1b, 0x04,             /* lsls r3, r3, #16 */
+    0x1a, 0x43,             /* orrs r2, r3 ; 0x5fff0000 */
+    0x11, 0x60,             /* str r1, [r2] */
+    0x00, 0xbe,             /* bkpt #0 */
+    0x00, 0x20,             /* movs r0, #0 */
+    0x70, 0x47,             /* bx lr */
+};
+
+static const uint8_t rp2040_bootrom_nyi_stub[] = {
+    0x04, 0x49,             /* ldr r1, [pc, #16] ; function code */
+    0x5f, 0x20,             /* movs r0, #0x5f */
+    0x00, 0x06,             /* lsls r0, r0, #24 */
+    0xff, 0x22,             /* movs r2, #0xff */
+    0x12, 0x04,             /* lsls r2, r2, #16 */
+    0x10, 0x43,             /* orrs r0, r2 ; 0x5fff0000 */
+    0x01, 0x60,             /* str r1, [r0] */
+    0x00, 0xbe,             /* bkpt #0 */
+    0xfe, 0xe7,             /* b . */
+    0xc0, 0x46,             /* nop; align literal */
+    0x00, 0x00, 0x00, 0x00, /* function code literal */
+};
+
+static const uint8_t rp2040_bootrom_hardfault_exit[] = {
+    0xef, 0xf3, 0x08, 0x81, /* mrs r1, msp */
+    0x8a, 0x69,             /* ldr r2, [r1, #24] ; stacked PC */
+    0x13, 0x88,             /* ldrh r3, [r2] */
+    0x04, 0x4c,             /* ldr r4, [pc, #16] ; 0xbe00 */
+    0xa3, 0x42,             /* cmp r3, r4 */
+    0x04, 0xd1,             /* bne hang */
+    0x08, 0x68,             /* ldr r0, [r1] ; stacked R0/status */
+    0x03, 0x4c,             /* ldr r4, [pc, #12] ; debug base */
+    0x60, 0x60,             /* str r0, [r4, #4] */
+    0x03, 0x48,             /* ldr r0, [pc, #12] ; EXIT command */
+    0x20, 0x60,             /* str r0, [r4] */
+    0xfe, 0xe7,             /* hang: b hang */
+    0x00, 0x00,             /* align literal pool */
+    0x00, 0xbe, 0x00, 0x00, /* 0xbe00 */
+    0x00, 0x00, 0xff, 0x5f, /* 0x5fff0000 */
+    0x45, 0x58, 0x49, 0x54, /* "EXIT" */
+};
+
+static void rp2040_store_hword(uint8_t *rom, uint32_t offset, uint16_t value)
+{
+    rom[offset] = value;
+    rom[offset + 1] = value >> 8;
+}
+
+static void rp2040_store_word(uint8_t *rom, uint32_t offset, uint32_t value)
+{
+    rom[offset] = value;
+    rom[offset + 1] = value >> 8;
+    rom[offset + 2] = value >> 16;
+    rom[offset + 3] = value >> 24;
+}
+
+static bool rp2040_bootrom_fp_supported(uint32_t offset)
+{
+    switch (offset) {
+    case RP2040_SF_TABLE_FADD:
+    case RP2040_SF_TABLE_FSUB:
+    case RP2040_SF_TABLE_FMUL:
+    case RP2040_SF_TABLE_FDIV:
+    case RP2040_SF_TABLE_FCMP_FAST:
+    case RP2040_SF_TABLE_FCMP_FAST_FLAGS:
+    case RP2040_SF_TABLE_FSQRT:
+    case RP2040_SF_TABLE_FLOAT2INT:
+    case RP2040_SF_TABLE_FLOAT2FIX:
+    case RP2040_SF_TABLE_FLOAT2UINT:
+    case RP2040_SF_TABLE_FLOAT2UFIX:
+    case RP2040_SF_TABLE_INT2FLOAT:
+    case RP2040_SF_TABLE_FIX2FLOAT:
+    case RP2040_SF_TABLE_UINT2FLOAT:
+    case RP2040_SF_TABLE_UFIX2FLOAT:
+    case RP2040_SF_TABLE_FCOS:
+    case RP2040_SF_TABLE_FSIN:
+    case RP2040_SF_TABLE_FTAN:
+    case RP2040_SF_TABLE_V3_FSINCOS:
+    case RP2040_SF_TABLE_FEXP:
+    case RP2040_SF_TABLE_FLN:
+    case RP2040_SF_TABLE_FCMP_BASIC:
+    case RP2040_SF_TABLE_FATAN2:
+    case RP2040_SF_TABLE_INT642FLOAT:
+    case RP2040_SF_TABLE_FIX642FLOAT:
+    case RP2040_SF_TABLE_UINT642FLOAT:
+    case RP2040_SF_TABLE_UFIX642FLOAT:
+    case RP2040_SF_TABLE_FLOAT2INT64:
+    case RP2040_SF_TABLE_FLOAT2FIX64:
+    case RP2040_SF_TABLE_FLOAT2UINT64:
+    case RP2040_SF_TABLE_FLOAT2UFIX64:
+    case RP2040_SF_TABLE_FLOAT2DOUBLE:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static void rp2040_install_bootrom_fp_stub(uint8_t *rom, uint32_t offset,
+                                          uint32_t command)
+{
+    memcpy(rom + offset, rp2040_bootrom_fp_stub,
+           sizeof(rp2040_bootrom_fp_stub));
+    rp2040_store_word(rom, offset + RP2040_BOOTROM_FP_STUB_CODE_LITERAL_OFFSET,
+                      command);
+}
+
+static void rp2040_install_synthetic_bootrom(void)
+{
+    g_autofree uint8_t *rom = g_malloc0(RP2040_ROM_SIZE);
+    uint32_t func_base = RP2040_BOOTROM_STUBS_OFFSET;
+    uint32_t func_table = RP2040_BOOTROM_FUNC_TABLE_OFFSET;
+    uint32_t data_table = RP2040_BOOTROM_DATA_TABLE_OFFSET;
+    uint32_t float_nyi = RP2040_BOOTROM_FLOAT_NYI_STUB_OFFSET | 1;
+    uint32_t double_nyi = RP2040_BOOTROM_DOUBLE_NYI_STUB_OFFSET | 1;
+    int i;
+
+    memcpy(rom, rp2040_bootrom, sizeof(rp2040_bootrom));
+    memcpy(rom + RP2040_BOOTROM_HARDFAULT_EXIT_OFFSET,
+           rp2040_bootrom_hardfault_exit,
+           sizeof(rp2040_bootrom_hardfault_exit));
+    rp2040_store_word(rom, 0x0c, RP2040_BOOTROM_HARDFAULT_EXIT_OFFSET | 1);
+    rom[RP2040_BOOTROM_ROM_VERSION_OFFSET] =
+        RP2040_BOOTROM_SYNTHETIC_ROM_VERSION;
+    memcpy(rom + RP2040_BOOTROM_LOOKUP_OFFSET, rp2040_bootrom_lookup,
+           sizeof(rp2040_bootrom_lookup));
+
+    rp2040_store_hword(rom, RP2040_BOOTROM_FUNC_TABLE_PTR_OFFSET,
+                       RP2040_BOOTROM_FUNC_TABLE_OFFSET);
+    rp2040_store_hword(rom, RP2040_BOOTROM_DATA_TABLE_PTR_OFFSET,
+                       RP2040_BOOTROM_DATA_TABLE_OFFSET);
+    rp2040_store_hword(rom, RP2040_BOOTROM_TABLE_LOOKUP_PTR_OFFSET,
+                       RP2040_BOOTROM_LOOKUP_OFFSET | 1);
+
+    for (i = 0; i < ARRAY_SIZE(rp2040_bootrom_functions); i++) {
+        const RP2040BootromFunction *func = &rp2040_bootrom_functions[i];
+        uint32_t entry = func_base | 1;
+
+        if (func->impl) {
+            memcpy(rom + func_base, func->impl, func->impl_size);
+            if (func->code_literal_offset != UINT32_MAX) {
+                rp2040_store_word(rom, func_base + func->code_literal_offset,
+                                  func->code);
+            }
+            func_base += ROUND_UP(func->impl_size, 4);
+        } else {
+            memcpy(rom + func_base, rp2040_bootrom_nyi_stub,
+                   sizeof(rp2040_bootrom_nyi_stub));
+            rp2040_store_word(rom,
+                              func_base +
+                              RP2040_BOOTROM_NYI_CODE_LITERAL_OFFSET,
+                              func->code);
+            func_base += ROUND_UP(sizeof(rp2040_bootrom_nyi_stub), 4);
+        }
+
+        rp2040_store_hword(rom, func_table +
+                           i * RP2040_BOOTROM_FUNC_TABLE_ENTRY_SIZE,
+                           func->code);
+        rp2040_store_hword(rom, func_table +
+                           i * RP2040_BOOTROM_FUNC_TABLE_ENTRY_SIZE + 2,
+                           entry);
+    }
+
+    memcpy(rom + RP2040_BOOTROM_FLOAT_NYI_STUB_OFFSET, rp2040_bootrom_nyi_stub,
+           sizeof(rp2040_bootrom_nyi_stub));
+    rp2040_store_word(rom, RP2040_BOOTROM_FLOAT_NYI_STUB_OFFSET +
+                      RP2040_BOOTROM_NYI_CODE_LITERAL_OFFSET,
+                      RP2040_ROM_TABLE_CODE('S', 'F'));
+    memcpy(rom + RP2040_BOOTROM_DOUBLE_NYI_STUB_OFFSET, 
rp2040_bootrom_nyi_stub,
+           sizeof(rp2040_bootrom_nyi_stub));
+    rp2040_store_word(rom, RP2040_BOOTROM_DOUBLE_NYI_STUB_OFFSET +
+                      RP2040_BOOTROM_NYI_CODE_LITERAL_OFFSET,
+                      RP2040_ROM_TABLE_CODE('S', 'D'));
+
+    rom[RP2040_BOOTROM_FLOAT_TABLE_OFFSET] = RP2040_BOOTROM_FLOAT_TABLE_WORDS;
+    rom[RP2040_BOOTROM_DOUBLE_TABLE_OFFSET] = RP2040_BOOTROM_FLOAT_TABLE_WORDS;
+    for (i = 0; i < RP2040_BOOTROM_FLOAT_TABLE_WORDS; i++) {
+        uint32_t table_offset = i * sizeof(uint32_t);
+
+        if (rp2040_bootrom_fp_supported(table_offset)) {
+            uint32_t float_stub = RP2040_BOOTROM_FLOAT_STUBS_OFFSET +
+                                  i * RP2040_BOOTROM_FP_STUB_SIZE;
+            uint32_t double_stub = RP2040_BOOTROM_DOUBLE_STUBS_OFFSET +
+                                   i * RP2040_BOOTROM_FP_STUB_SIZE;
+
+            rp2040_install_bootrom_fp_stub(rom, float_stub,
+                                           RP2040_SYNTHETIC_FP_CMD_FLOAT |
+                                           table_offset);
+            rp2040_install_bootrom_fp_stub(rom, double_stub,
+                                           RP2040_SYNTHETIC_FP_CMD_DOUBLE |
+                                           table_offset);
+            rp2040_store_word(rom, RP2040_BOOTROM_FLOAT_TABLE_OFFSET + 2 +
+                              table_offset, float_stub | 1);
+            rp2040_store_word(rom, RP2040_BOOTROM_DOUBLE_TABLE_OFFSET + 2 +
+                              table_offset, double_stub | 1);
+        } else {
+            rp2040_store_word(rom, RP2040_BOOTROM_FLOAT_TABLE_OFFSET + 2 +
+                              table_offset, float_nyi);
+            rp2040_store_word(rom, RP2040_BOOTROM_DOUBLE_TABLE_OFFSET + 2 +
+                              table_offset, double_nyi);
+        }
+    }
+
+    for (i = 0; i < ARRAY_SIZE(rp2040_bootrom_data); i++) {
+        const RP2040BootromData *data = &rp2040_bootrom_data[i];
+
+        rp2040_store_hword(rom, data_table +
+                           i * RP2040_BOOTROM_DATA_TABLE_ENTRY_SIZE,
+                           data->code);
+        rp2040_store_hword(rom, data_table +
+                           i * RP2040_BOOTROM_DATA_TABLE_ENTRY_SIZE + 2,
+                           data->offset);
+    }
+
+    rom_add_blob_fixed("rp2040.bootrom", rom, RP2040_ROM_SIZE,
+                       RP2040_ROM_BASE);
+}
+
+static uint32_t rp2040_apply_atomic_alias(uint32_t old, uint32_t value,
+                                          hwaddr alias)
+{
+    switch (alias) {
+    case ATOMIC_XOR:
+        return old ^ value;
+    case ATOMIC_SET:
+        return old | value;
+    case ATOMIC_CLR:
+        return old & ~value;
+    default:
+        return value;
+    }
+}
+
+static MemTxResult rp2040_powered_off_read(void *opaque, hwaddr addr,
+                                           uint64_t *data, unsigned size,
+                                           MemTxAttrs attrs)
+{
+    *data = 0;
+    return MEMTX_ERROR;
+}
+
+static MemTxResult rp2040_powered_off_write(void *opaque, hwaddr addr,
+                                            uint64_t data, unsigned size,
+                                            MemTxAttrs attrs)
+{
+    return MEMTX_ERROR;
+}
+
+static const MemoryRegionOps rp2040_powered_off_ops = {
+    .read_with_attrs = rp2040_powered_off_read,
+    .write_with_attrs = rp2040_powered_off_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void rp2040_update_mempowerdown(RP2040State *s)
+{
+    uint32_t mempowerdown;
+    int i;
+
+    if (!s->mempowerdown_ready) {
+        return;
+    }
+
+    mempowerdown = rp2040_syscfg_get_mempowerdown(&s->syscfg);
+
+    for (i = 0; i < ARRAY_SIZE(s->sram_poweroff); i++) {
+        memory_region_set_enabled(&s->sram_poweroff[i],
+                                  mempowerdown & BIT(i));
+    }
+
+    memory_region_set_enabled(&s->usbctrl_dpram_poweroff,
+                              mempowerdown & BIT(6));
+    memory_region_set_enabled(&s->rom_poweroff, mempowerdown & BIT(7));
+}
+
+static void rp2040_update_nmi(RP2040State *s)
+{
+    uint32_t nmi_mask = rp2040_syscfg_get_proc0_nmi_mask(&s->syscfg);
+    bool nmi_level = false;
+    int i;
+
+    for (i = 0; i < RP2040_NUM_IRQS; i++) {
+        bool irq_level = s->irq_level[0][i];
+        bool route_to_nmi = nmi_mask & BIT(i);
+
+        qemu_set_irq(s->cpu_irq[0][i], irq_level && !route_to_nmi);
+        nmi_level |= irq_level && route_to_nmi;
+    }
+
+    qemu_set_irq(s->nmi_irq[0], nmi_level);
+}
+
+static void rp2040_syscfg_update(void *opaque)
+{
+    RP2040State *s = opaque;
+
+    rp2040_update_mempowerdown(s);
+    rp2040_update_nmi(s);
+}
+
+static void rp2040_start_core1_async_work(CPUState *cs, run_on_cpu_data data)
+{
+    ARMCPU *cpu = ARM_CPU(cs);
+    CPUARMState *env = &cpu->env;
+
+    cpu_reset(cs);
+    cpu->power_state = PSCI_ON;
+    env->halt_reason = NOT_HALTED;
+    arm_rebuild_hflags(env);
+    cs->halted = 0;
+    cpu_resume(cs);
+}
+
+static bool rp2040_core1_powered_off(RP2040State *s)
+{
+    ARMCPU *cpu = s->armv7m[RP2040_PROC1].cpu;
+
+    return !cpu || cpu->power_state == PSCI_OFF;
+}
+
+static void rp2040_start_core1(RP2040State *s)
+{
+    if (!s->armv7m[RP2040_PROC1].cpu ||
+        !rp2040_core1_powered_off(s)) {
+        return;
+    }
+
+    async_run_on_cpu(CPU(s->armv7m[RP2040_PROC1].cpu),
+                     rp2040_start_core1_async_work,
+                     RUN_ON_CPU_NULL);
+}
+
+static void rp2040_stop_core1_async_work(CPUState *cs, run_on_cpu_data data)
+{
+    ARMCPU *cpu = ARM_CPU(cs);
+
+    cpu->power_state = PSCI_OFF;
+    cpu->env.halt_reason = HALT_PSCI;
+    cs->halted = 1;
+    cs->exception_index = EXCP_HLT;
+}
+
+static void rp2040_stop_core1(RP2040State *s)
+{
+    if (!s->armv7m[RP2040_PROC1].cpu ||
+        rp2040_core1_powered_off(s)) {
+        return;
+    }
+
+    async_run_on_cpu(CPU(s->armv7m[RP2040_PROC1].cpu),
+                     rp2040_stop_core1_async_work,
+                     RUN_ON_CPU_NULL);
+}
+
+static void rp2040_psm_update(void *opaque)
+{
+    RP2040State *s = opaque;
+    bool proc1_forced_off = rp2040_psm_get_frce_off(&s->psm) &
+                            RP2040_PSM_PROC1;
+
+    if (proc1_forced_off) {
+        rp2040_stop_core1(s);
+    } else {
+        rp2040_start_core1(s);
+    }
+}
+
+static const char *rp2040_bootrom_function_name(uint16_t code)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(rp2040_bootrom_functions); i++) {
+        if (rp2040_bootrom_functions[i].code == code) {
+            return rp2040_bootrom_functions[i].name;
+        }
+    }
+
+    return NULL;
+}
+
+static void rp2040_init_float_status(float_status *status)
+{
+    *status = (float_status) { 0 };
+    set_float_rounding_mode(float_round_nearest_even, status);
+}
+
+static void rp2040_synthetic_fp_nyi(bool is_double, uint32_t offset)
+{
+    g_autofree char *detail = g_strdup_printf("%s table offset 0x%02" PRIx32,
+                                              is_double ? "double" : "float",
+                                              offset);
+
+    rp2040_log_nyi("bootrom", "floating-point helper", detail);
+}
+
+static float rp2040_u32_to_host_float(uint32_t value)
+{
+    union {
+        uint32_t u;
+        float f;
+    } v = { .u = value };
+
+    return v.f;
+}
+
+static uint32_t rp2040_host_float_to_u32(float value)
+{
+    union {
+        float f;
+        uint32_t u;
+    } v = { .f = value };
+
+    return v.u;
+}
+
+static double rp2040_u64_to_host_double(uint64_t value)
+{
+    union {
+        uint64_t u;
+        double d;
+    } v = { .u = value };
+
+    return v.d;
+}
+
+static uint64_t rp2040_host_double_to_u64(double value)
+{
+    union {
+        double d;
+        uint64_t u;
+    } v = { .d = value };
+
+    return v.u;
+}
+
+static uint32_t rp2040_synthetic_fp_compare(double a, double b)
+{
+    if (isnan(a) || isnan(b)) {
+        return 1;
+    }
+    if (a < b) {
+        return -1;
+    }
+    if (a > b) {
+        return 1;
+    }
+    return 0;
+}
+
+static int32_t rp2040_synthetic_double_to_i32(double value)
+{
+    if (isnan(value)) {
+        return 0;
+    }
+    if (value >= INT32_MAX) {
+        return INT32_MAX;
+    }
+    if (value <= INT32_MIN) {
+        return INT32_MIN;
+    }
+    return value;
+}
+
+static uint32_t rp2040_synthetic_double_to_u32(double value)
+{
+    if (isnan(value) || value <= 0) {
+        return 0;
+    }
+    if (value >= UINT32_MAX) {
+        return UINT32_MAX;
+    }
+    return value;
+}
+
+static int64_t rp2040_synthetic_double_to_i64(double value)
+{
+    if (isnan(value)) {
+        return 0;
+    }
+    if (value >= 0x1p63) {
+        return INT64_MAX;
+    }
+    if (value <= -0x1p63) {
+        return INT64_MIN;
+    }
+    return value;
+}
+
+static uint64_t rp2040_synthetic_double_to_u64(double value)
+{
+    if (isnan(value) || value <= 0) {
+        return 0;
+    }
+    if (value >= 0x1p64) {
+        return UINT64_MAX;
+    }
+    return value;
+}
+
+static void rp2040_synthetic_float_op(RP2040State *s, uint32_t offset)
+{
+    float_status status;
+    float32 a = make_float32(s->synthetic_rom_dbg_arg[0]);
+    float32 b = make_float32(s->synthetic_rom_dbg_arg[1]);
+    float host_a = rp2040_u32_to_host_float(s->synthetic_rom_dbg_arg[0]);
+    float host_b = rp2040_u32_to_host_float(s->synthetic_rom_dbg_arg[1]);
+    float32 r32;
+    float64 r64;
+    uint64_t r;
+    double scaled;
+
+    rp2040_init_float_status(&status);
+    memset(s->synthetic_rom_dbg_result, 0, 
sizeof(s->synthetic_rom_dbg_result));
+    s->synthetic_rom_dbg_result[1] = 0;
+
+    switch (offset) {
+    case RP2040_SF_TABLE_FADD:
+        s->synthetic_rom_dbg_result[0] =
+            float32_val(float32_add(a, b, &status));
+        break;
+    case RP2040_SF_TABLE_FSUB:
+        s->synthetic_rom_dbg_result[0] =
+            float32_val(float32_sub(a, b, &status));
+        break;
+    case RP2040_SF_TABLE_FMUL:
+        s->synthetic_rom_dbg_result[0] =
+            float32_val(float32_mul(a, b, &status));
+        break;
+    case RP2040_SF_TABLE_FDIV:
+        s->synthetic_rom_dbg_result[0] =
+            float32_val(float32_div(a, b, &status));
+        break;
+    case RP2040_SF_TABLE_FCMP_FAST:
+    case RP2040_SF_TABLE_FCMP_FAST_FLAGS:
+    case RP2040_SF_TABLE_FCMP_BASIC:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_synthetic_fp_compare(host_a, host_b);
+        break;
+    case RP2040_SF_TABLE_FSQRT:
+        s->synthetic_rom_dbg_result[0] =
+            float32_val(float32_sqrt(a, &status));
+        break;
+    case RP2040_SF_TABLE_FLOAT2INT:
+        s->synthetic_rom_dbg_result[0] =
+            float32_to_int32_round_to_zero(a, &status);
+        break;
+    case RP2040_SF_TABLE_FLOAT2FIX:
+        scaled = ldexp((double)host_a, s->synthetic_rom_dbg_arg[1] & 0xff);
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_synthetic_double_to_i32(scaled);
+        break;
+    case RP2040_SF_TABLE_FLOAT2UINT:
+        s->synthetic_rom_dbg_result[0] =
+            float32_to_uint32_round_to_zero(a, &status);
+        break;
+    case RP2040_SF_TABLE_FLOAT2UFIX:
+        scaled = ldexp((double)host_a, s->synthetic_rom_dbg_arg[1] & 0xff);
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_synthetic_double_to_u32(scaled);
+        break;
+    case RP2040_SF_TABLE_INT2FLOAT:
+        s->synthetic_rom_dbg_result[0] =
+            float32_val(int32_to_float32(s->synthetic_rom_dbg_arg[0],
+                                         &status));
+        break;
+    case RP2040_SF_TABLE_FIX2FLOAT:
+        r32 = float64_to_float32(make_float64(rp2040_host_double_to_u64(
+                                  ldexp((double)(int32_t)
+                                        s->synthetic_rom_dbg_arg[0],
+                                        -(int)(s->synthetic_rom_dbg_arg[1] &
+                                               0xff)))),
+                                 &status);
+        s->synthetic_rom_dbg_result[0] = float32_val(r32);
+        break;
+    case RP2040_SF_TABLE_UINT2FLOAT:
+        s->synthetic_rom_dbg_result[0] =
+            float32_val(uint32_to_float32(s->synthetic_rom_dbg_arg[0],
+                                          &status));
+        break;
+    case RP2040_SF_TABLE_UFIX2FLOAT:
+        r32 = float64_to_float32(make_float64(rp2040_host_double_to_u64(
+                                  ldexp((double)
+                                        s->synthetic_rom_dbg_arg[0],
+                                        -(int)(s->synthetic_rom_dbg_arg[1] &
+                                               0xff)))),
+                                 &status);
+        s->synthetic_rom_dbg_result[0] = float32_val(r32);
+        break;
+    case RP2040_SF_TABLE_FCOS:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_host_float_to_u32(cosf(host_a));
+        break;
+    case RP2040_SF_TABLE_FSIN:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_host_float_to_u32(sinf(host_a));
+        s->synthetic_rom_dbg_result[1] =
+            rp2040_host_float_to_u32(cosf(host_a));
+        break;
+    case RP2040_SF_TABLE_FTAN:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_host_float_to_u32(tanf(host_a));
+        break;
+    case RP2040_SF_TABLE_V3_FSINCOS:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_host_float_to_u32(sinf(host_a));
+        s->synthetic_rom_dbg_result[1] =
+            rp2040_host_float_to_u32(cosf(host_a));
+        break;
+    case RP2040_SF_TABLE_FEXP:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_host_float_to_u32(expf(host_a));
+        break;
+    case RP2040_SF_TABLE_FLN:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_host_float_to_u32(logf(host_a));
+        break;
+    case RP2040_SF_TABLE_FATAN2:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_host_float_to_u32(atan2f(host_a, host_b));
+        break;
+    case RP2040_SF_TABLE_INT642FLOAT:
+        r = deposit64(s->synthetic_rom_dbg_arg[0], 32, 32,
+                      s->synthetic_rom_dbg_arg[1]);
+        r32 = int64_to_float32((int64_t)r, &status);
+        s->synthetic_rom_dbg_result[0] = float32_val(r32);
+        break;
+    case RP2040_SF_TABLE_FIX642FLOAT:
+        r = deposit64(s->synthetic_rom_dbg_arg[0], 32, 32,
+                      s->synthetic_rom_dbg_arg[1]);
+        r32 = float64_to_float32(make_float64(rp2040_host_double_to_u64(
+                                  ldexp((double)(int64_t)r,
+                                        -(int)(s->synthetic_rom_dbg_arg[2] &
+                                               0xff)))),
+                                 &status);
+        s->synthetic_rom_dbg_result[0] = float32_val(r32);
+        break;
+    case RP2040_SF_TABLE_UINT642FLOAT:
+        r = deposit64(s->synthetic_rom_dbg_arg[0], 32, 32,
+                      s->synthetic_rom_dbg_arg[1]);
+        r32 = uint64_to_float32(r, &status);
+        s->synthetic_rom_dbg_result[0] = float32_val(r32);
+        break;
+    case RP2040_SF_TABLE_UFIX642FLOAT:
+        r = deposit64(s->synthetic_rom_dbg_arg[0], 32, 32,
+                      s->synthetic_rom_dbg_arg[1]);
+        r32 = float64_to_float32(make_float64(rp2040_host_double_to_u64(
+                                  ldexp((double)r,
+                                        -(int)(s->synthetic_rom_dbg_arg[2] &
+                                               0xff)))),
+                                 &status);
+        s->synthetic_rom_dbg_result[0] = float32_val(r32);
+        break;
+    case RP2040_SF_TABLE_FLOAT2INT64:
+        r = float32_to_int64_round_to_zero(a, &status);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2FIX64:
+        scaled = ldexp((double)host_a, s->synthetic_rom_dbg_arg[1] & 0xff);
+        r = rp2040_synthetic_double_to_i64(scaled);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2UINT64:
+        r = float32_to_uint64_round_to_zero(a, &status);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2UFIX64:
+        scaled = ldexp((double)host_a, s->synthetic_rom_dbg_arg[1] & 0xff);
+        r = rp2040_synthetic_double_to_u64(scaled);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2DOUBLE:
+        r64 = float32_to_float64(a, &status);
+        r = float64_val(r64);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    default:
+        s->synthetic_rom_dbg_result[0] = 0;
+        rp2040_synthetic_fp_nyi(false, offset);
+        break;
+    }
+}
+
+static void rp2040_synthetic_double_op(RP2040State *s, uint32_t offset)
+{
+    float_status status;
+    uint64_t av = deposit64(s->synthetic_rom_dbg_arg[0], 32, 32,
+                            s->synthetic_rom_dbg_arg[1]);
+    uint64_t bv = deposit64(s->synthetic_rom_dbg_arg[2], 32, 32,
+                            s->synthetic_rom_dbg_arg[3]);
+    float64 a = make_float64(av);
+    float64 b = make_float64(bv);
+    double host_a = rp2040_u64_to_host_double(av);
+    double host_b = rp2040_u64_to_host_double(bv);
+    float64 r64;
+    float32 r32;
+    uint64_t r;
+    double scaled;
+
+    rp2040_init_float_status(&status);
+    memset(s->synthetic_rom_dbg_result, 0, 
sizeof(s->synthetic_rom_dbg_result));
+
+    switch (offset) {
+    case RP2040_SF_TABLE_FADD:
+        r64 = float64_add(a, b, &status);
+        goto return_double;
+    case RP2040_SF_TABLE_FSUB:
+        r64 = float64_sub(a, b, &status);
+        goto return_double;
+    case RP2040_SF_TABLE_FMUL:
+        r64 = float64_mul(a, b, &status);
+        goto return_double;
+    case RP2040_SF_TABLE_FDIV:
+        r64 = float64_div(a, b, &status);
+        goto return_double;
+    case RP2040_SF_TABLE_FCMP_FAST:
+    case RP2040_SF_TABLE_FCMP_FAST_FLAGS:
+    case RP2040_SF_TABLE_FCMP_BASIC:
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_synthetic_fp_compare(host_a, host_b);
+        s->synthetic_rom_dbg_result[1] = 0;
+        break;
+    case RP2040_SF_TABLE_FSQRT:
+        r64 = float64_sqrt(a, &status);
+        goto return_double;
+    case RP2040_SF_TABLE_FLOAT2INT:
+        s->synthetic_rom_dbg_result[0] =
+            float64_to_int32_round_to_zero(a, &status);
+        s->synthetic_rom_dbg_result[1] = 0;
+        break;
+    case RP2040_SF_TABLE_FLOAT2FIX:
+        scaled = ldexp(host_a, s->synthetic_rom_dbg_arg[2] & 0xff);
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_synthetic_double_to_i32(scaled);
+        s->synthetic_rom_dbg_result[1] = 0;
+        break;
+    case RP2040_SF_TABLE_FLOAT2UINT:
+        s->synthetic_rom_dbg_result[0] =
+            float64_to_uint32_round_to_zero(a, &status);
+        s->synthetic_rom_dbg_result[1] = 0;
+        break;
+    case RP2040_SF_TABLE_FLOAT2UFIX:
+        scaled = ldexp(host_a, s->synthetic_rom_dbg_arg[2] & 0xff);
+        s->synthetic_rom_dbg_result[0] =
+            rp2040_synthetic_double_to_u32(scaled);
+        s->synthetic_rom_dbg_result[1] = 0;
+        break;
+    case RP2040_SF_TABLE_INT2FLOAT:
+        r64 = int32_to_float64(s->synthetic_rom_dbg_arg[0], &status);
+        goto return_double;
+    case RP2040_SF_TABLE_FIX2FLOAT:
+        r64 = make_float64(rp2040_host_double_to_u64(
+              ldexp((double)(int32_t)s->synthetic_rom_dbg_arg[0],
+                    -(int)(s->synthetic_rom_dbg_arg[1] & 0xff))));
+        goto return_double;
+    case RP2040_SF_TABLE_UINT2FLOAT:
+        r64 = uint32_to_float64(s->synthetic_rom_dbg_arg[0], &status);
+        goto return_double;
+    case RP2040_SF_TABLE_UFIX2FLOAT:
+        r64 = make_float64(rp2040_host_double_to_u64(
+              ldexp((double)s->synthetic_rom_dbg_arg[0],
+                    -(int)(s->synthetic_rom_dbg_arg[1] & 0xff))));
+        goto return_double;
+    case RP2040_SF_TABLE_FCOS:
+        r64 = make_float64(rp2040_host_double_to_u64(cos(host_a)));
+        goto return_double;
+    case RP2040_SF_TABLE_FSIN:
+        r64 = make_float64(rp2040_host_double_to_u64(sin(host_a)));
+        goto return_double;
+    case RP2040_SF_TABLE_FTAN:
+        r64 = make_float64(rp2040_host_double_to_u64(tan(host_a)));
+        goto return_double;
+    case RP2040_SF_TABLE_V3_FSINCOS:
+        r = rp2040_host_double_to_u64(sin(host_a));
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        r = rp2040_host_double_to_u64(cos(host_a));
+        s->synthetic_rom_dbg_result[2] = r;
+        s->synthetic_rom_dbg_result[3] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FEXP:
+        r64 = make_float64(rp2040_host_double_to_u64(exp(host_a)));
+        goto return_double;
+    case RP2040_SF_TABLE_FLN:
+        r64 = make_float64(rp2040_host_double_to_u64(log(host_a)));
+        goto return_double;
+    case RP2040_SF_TABLE_FATAN2:
+        r64 = make_float64(rp2040_host_double_to_u64(atan2(host_a, host_b)));
+        goto return_double;
+    case RP2040_SF_TABLE_INT642FLOAT:
+        r64 = int64_to_float64((int64_t)av, &status);
+        goto return_double;
+    case RP2040_SF_TABLE_FIX642FLOAT:
+        r64 = make_float64(rp2040_host_double_to_u64(
+              ldexp((double)(int64_t)av,
+                    -(int)(s->synthetic_rom_dbg_arg[2] & 0xff))));
+        goto return_double;
+    case RP2040_SF_TABLE_UINT642FLOAT:
+        r64 = uint64_to_float64(av, &status);
+        goto return_double;
+    case RP2040_SF_TABLE_UFIX642FLOAT:
+        r64 = make_float64(rp2040_host_double_to_u64(
+              ldexp((double)av,
+                    -(int)(s->synthetic_rom_dbg_arg[2] & 0xff))));
+        goto return_double;
+    case RP2040_SF_TABLE_FLOAT2INT64:
+        r = float64_to_int64_round_to_zero(a, &status);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2FIX64:
+        scaled = ldexp(host_a, s->synthetic_rom_dbg_arg[2] & 0xff);
+        r = rp2040_synthetic_double_to_i64(scaled);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2UINT64:
+        r = float64_to_uint64_round_to_zero(a, &status);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2UFIX64:
+        scaled = ldexp(host_a, s->synthetic_rom_dbg_arg[2] & 0xff);
+        r = rp2040_synthetic_double_to_u64(scaled);
+        s->synthetic_rom_dbg_result[0] = r;
+        s->synthetic_rom_dbg_result[1] = r >> 32;
+        break;
+    case RP2040_SF_TABLE_FLOAT2DOUBLE:
+        r32 = float64_to_float32(a, &status);
+        s->synthetic_rom_dbg_result[0] = float32_val(r32);
+        s->synthetic_rom_dbg_result[1] = 0;
+        break;
+    default:
+        s->synthetic_rom_dbg_result[0] = 0;
+        s->synthetic_rom_dbg_result[1] = 0;
+        rp2040_synthetic_fp_nyi(true, offset);
+        break;
+    }
+    return;
+
+return_double:
+    r = float64_val(r64);
+    s->synthetic_rom_dbg_result[0] = r;
+    s->synthetic_rom_dbg_result[1] = r >> 32;
+}
+
+static bool rp2040_synthetic_fp_op(RP2040State *s, uint32_t command)
+{
+    uint32_t offset = command & 0xff;
+
+    switch (command & RP2040_SYNTHETIC_FP_CMD_MASK) {
+    case RP2040_SYNTHETIC_FP_CMD_FLOAT:
+        rp2040_synthetic_float_op(s, offset);
+        return true;
+    case RP2040_SYNTHETIC_FP_CMD_DOUBLE:
+        rp2040_synthetic_double_op(s, offset);
+        return true;
+    default:
+        return false;
+    }
+}
+
+static void rp2040_synthetic_flash_helper_hit(RP2040State *s,
+                                              unsigned int helper,
+                                              const char *name)
+{
+    uint32_t count;
+
+    g_assert(helper < RP2040_SYNTHETIC_ROM_FLASH_HELPER_COUNT);
+
+    count = ++s->synthetic_rom_flash_helper_count[helper];
+    trace_rp2040_synthetic_flash_helper(name, count);
+}
+
+static void rp2040_synthetic_rom_dbg_write(void *opaque, hwaddr addr,
+                                           uint64_t value, unsigned size)
+{
+    RP2040State *s = opaque;
+    hwaddr offset = addr & 0xfff;
+    uint32_t command = value;
+    uint16_t code = value;
+    const char *name = rp2040_bootrom_function_name(code);
+    char feature[64];
+    char detail[64];
+    Error *local_err = NULL;
+
+    switch (offset) {
+    case RP2040_SYNTHETIC_ROM_DBG_ARG0:
+    case RP2040_SYNTHETIC_ROM_DBG_ARG1:
+    case RP2040_SYNTHETIC_ROM_DBG_ARG2:
+    case RP2040_SYNTHETIC_ROM_DBG_ARG3:
+        s->synthetic_rom_dbg_arg[(offset - RP2040_SYNTHETIC_ROM_DBG_ARG0) /
+                                 sizeof(uint32_t)] = value;
+        return;
+    case RP2040_SYNTHETIC_ROM_DBG_CMD:
+        break;
+    default:
+        rp2040_log_nyi("bootrom", "synthetic diagnostic register write",
+                       "unknown register");
+        return;
+    }
+
+    if (rp2040_synthetic_fp_op(s, command)) {
+        return;
+    }
+
+    if (command == RP2040_SYNTHETIC_ROM_DBG_CMD_EXIT) {
+        qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN,
+                                               s->synthetic_rom_dbg_arg[0]);
+        return;
+    }
+
+    switch (code) {
+    case RP2040_ROM_TABLE_CODE('C', 'X'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_ENTER_CMD_XIP,
+            "flash_enter_cmd_xip");
+        rp2040_log_nyi("bootrom", "flash_enter_cmd_xip",
+                       "synthetic helper does not reconfigure SSI hardware");
+        return;
+    case RP2040_ROM_TABLE_CODE('E', 'X'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_EXIT_XIP, "flash_exit_xip");
+        rp2040_log_nyi("bootrom", "flash_exit_xip",
+                       "synthetic helper does not send serial flash commands");
+        return;
+    case RP2040_ROM_TABLE_CODE('F', 'C'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_FLUSH_CACHE, "flash_flush_cache");
+        rp2040_log_nyi("bootrom", "flash_flush_cache",
+                       "XIP cache is not modeled");
+        return;
+    case RP2040_ROM_TABLE_CODE('I', 'F'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_CONNECT_INTERNAL_FLASH,
+            "connect_internal_flash");
+        rp2040_log_nyi("bootrom", "connect_internal_flash",
+                       "synthetic helper assumes the QSPI flash is connected");
+        return;
+    case RP2040_ROM_TABLE_CODE('R', 'E'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_RANGE_ERASE, "flash_range_erase");
+        if (!rp2040_xip_flash_range_erase(&s->xip,
+                                          s->synthetic_rom_dbg_arg[0],
+                                          s->synthetic_rom_dbg_arg[1],
+                                          s->synthetic_rom_dbg_arg[2],
+                                          s->synthetic_rom_dbg_arg[3],
+                                          &local_err)) {
+            warn_report_err(local_err);
+        }
+        return;
+    case RP2040_ROM_TABLE_CODE('R', 'P'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_RANGE_PROGRAM, "flash_range_program");
+        if (!rp2040_xip_flash_range_program(&s->xip,
+                                            s->synthetic_rom_dbg_arg[0],
+                                            s->synthetic_rom_dbg_arg[1],
+                                            s->synthetic_rom_dbg_arg[2],
+                                            &local_err)) {
+            warn_report_err(local_err);
+        }
+        return;
+    }
+
+    snprintf(feature, sizeof(feature), "boot ROM function '%c%c'",
+             code & 0xff, (code >> 8) & 0xff);
+    snprintf(detail, sizeof(detail), "%s%s%s",
+             name ? "SDK name: " : "",
+             name ? name : "unknown lookup code",
+             name ? "" : "");
+    rp2040_log_nyi("bootrom", feature, detail);
+}
+
+static uint64_t rp2040_synthetic_rom_dbg_read(void *opaque, hwaddr addr,
+                                              unsigned size)
+{
+    RP2040State *s = opaque;
+    hwaddr offset = addr & 0xfff;
+
+    if (offset >= RP2040_SYNTHETIC_ROM_DBG_ARG0 &&
+        offset <= RP2040_SYNTHETIC_ROM_DBG_ARG3 &&
+        QEMU_IS_ALIGNED(offset, sizeof(uint32_t))) {
+        return s->synthetic_rom_dbg_arg[(offset -
+                                         RP2040_SYNTHETIC_ROM_DBG_ARG0) /
+                                        sizeof(uint32_t)];
+    }
+    if (offset >= RP2040_SYNTHETIC_ROM_DBG_RESULT0 &&
+        offset <= RP2040_SYNTHETIC_ROM_DBG_RESULT3 &&
+        QEMU_IS_ALIGNED(offset, sizeof(uint32_t))) {
+        return s->synthetic_rom_dbg_result[(offset -
+                                            RP2040_SYNTHETIC_ROM_DBG_RESULT0) /
+                                           sizeof(uint32_t)];
+    }
+    if (offset >= RP2040_SYNTHETIC_ROM_DBG_FLASH_COUNT0 &&
+        offset < RP2040_SYNTHETIC_ROM_DBG_FLASH_COUNT0 +
+                 sizeof(s->synthetic_rom_flash_helper_count) &&
+        QEMU_IS_ALIGNED(offset, sizeof(uint32_t))) {
+        return s->synthetic_rom_flash_helper_count[
+            (offset - RP2040_SYNTHETIC_ROM_DBG_FLASH_COUNT0) /
+            sizeof(uint32_t)];
+    }
+
+    rp2040_log_nyi("bootrom", "synthetic diagnostic register read",
+                   "unsupported register");
+    return 0;
+}
+
+static const MemoryRegionOps rp2040_synthetic_rom_dbg_ops = {
+    .read = rp2040_synthetic_rom_dbg_read,
+    .write = rp2040_synthetic_rom_dbg_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void rp2040_set_irq(void *opaque, int irq, int level)
+{
+    RP2040State *s = opaque;
+
+    assert(irq >= 0 && irq < RP2040_NUM_IRQS);
+    s->irq_level[0][irq] = level;
+    rp2040_update_nmi(s);
+}
+
+static void rp2040_update_uart_pins(RP2040State *s)
+{
+    pl011_set_tx_connected(&s->uart0,
+                           !s->strict_uart_pins ||
+                           s->uart0_tx_pin_enabled);
+    pl011_set_rx_connected(&s->uart0,
+                           !s->strict_uart_pins ||
+                           s->uart0_rx_pin_enabled);
+    pl011_set_tx_connected(&s->uart1,
+                           !s->strict_uart_pins ||
+                           s->uart1_tx_pin_enabled);
+    pl011_set_rx_connected(&s->uart1,
+                           !s->strict_uart_pins ||
+                           s->uart1_rx_pin_enabled);
+}
+
+static void rp2040_set_uart_pin(void *opaque, int pin, int level)
+{
+    RP2040State *s = opaque;
+
+    switch (pin) {
+    case 0:
+        s->uart0_tx_pin_enabled = level;
+        break;
+    case 1:
+        s->uart0_rx_pin_enabled = level;
+        break;
+    case 2:
+        s->uart1_tx_pin_enabled = level;
+        break;
+    case 3:
+        s->uart1_rx_pin_enabled = level;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    rp2040_update_uart_pins(s);
+}
+
+static uint64_t rp2040_usbctrl_regs_read(void *opaque, hwaddr addr,
+                                         unsigned size)
+{
+    RP2040State *s = opaque;
+    hwaddr offset = addr & 0xfff;
+    uint64_t value;
+
+    switch (offset) {
+    case USBCTRL_SIE_STATUS:
+        value = s->usbctrl_reg[offset / sizeof(uint32_t)] |
+                USBCTRL_SIE_STATUS_VBUS_DETECTED;
+        break;
+    case USBCTRL_BUFF_CPU_HANDLE:
+    case USBCTRL_EP_ABORT_DONE:
+    case USBCTRL_INTR:
+        value = s->usbctrl_reg[offset / sizeof(uint32_t)];
+        break;
+    case USBCTRL_INTS:
+        value = (s->usbctrl_reg[USBCTRL_INTR / sizeof(uint32_t)] |
+                 s->usbctrl_reg[USBCTRL_INTF / sizeof(uint32_t)]) &
+                s->usbctrl_reg[USBCTRL_INTE / sizeof(uint32_t)];
+        break;
+    default:
+        if (offset < sizeof(s->usbctrl_reg)) {
+            value = s->usbctrl_reg[offset / sizeof(uint32_t)];
+        } else {
+            value = 0;
+            rp2040_log_unimplemented_read("usbctrl_regs", size,
+                                          RP2040_USBCTRL_REGS_BASE + addr,
+                                          offset, value);
+        }
+        break;
+    }
+
+    return value;
+}
+
+static void rp2040_usbctrl_regs_write(void *opaque, hwaddr addr,
+                                      uint64_t value64, unsigned size)
+{
+    RP2040State *s = opaque;
+    hwaddr alias = addr & ATOMIC_ALIAS_MASK;
+    hwaddr offset = addr & 0xfff;
+    uint32_t value = value64;
+    uint32_t old;
+
+    if (offset < sizeof(s->usbctrl_reg)) {
+        old = s->usbctrl_reg[offset / sizeof(uint32_t)];
+        s->usbctrl_reg[offset / sizeof(uint32_t)] =
+            rp2040_apply_atomic_alias(old, value, alias);
+    } else {
+        rp2040_log_unimplemented_write("usbctrl_regs", size,
+                                       RP2040_USBCTRL_REGS_BASE + addr,
+                                       offset, value64);
+    }
+}
+
+static const MemoryRegionOps rp2040_usbctrl_regs_ops = {
+    .read = rp2040_usbctrl_regs_read,
+    .write = rp2040_usbctrl_regs_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void rp2040_soc_init(Object *obj)
+{
+    RP2040State *s = RP2040(obj);
+    int i;
+
+    for (i = 0; i < RP2040_NUM_CORES; i++) {
+        g_autofree char *name = g_strdup_printf("proc%d", i);
+
+        object_initialize_child(obj, name, &s->armv7m[i], TYPE_ARMV7M);
+        qdev_prop_set_string(DEVICE(&s->armv7m[i]), "cpu-type",
+                             ARM_CPU_TYPE_NAME("cortex-m0"));
+        qdev_prop_set_uint32(DEVICE(&s->armv7m[i]), "num-irq", 32);
+    }
+    object_initialize_child(obj, "uart0", &s->uart0, TYPE_PL011);
+    object_property_add_alias(obj, "serial0", OBJECT(&s->uart0), "chardev");
+    object_initialize_child(obj, "uart1", &s->uart1, TYPE_PL011);
+    object_property_add_alias(obj, "serial1", OBJECT(&s->uart1), "chardev");
+
+    object_initialize_child(obj, "xip", &s->xip, TYPE_RP2040_XIP);
+    object_initialize_child(obj, "busctrl", &s->busctrl, TYPE_RP2040_BUSCTRL);
+    object_initialize_child(obj, "clocks", &s->clocks, TYPE_RP2040_CLOCKS);
+    object_initialize_child(obj, "dma", &s->dma, TYPE_RP2040_DMA);
+    object_initialize_child(obj, "iobank0", &s->iobank0,
+                            TYPE_RP2040_IOBANK0);
+    object_initialize_child(obj, "ioqspi", &s->ioqspi, TYPE_RP2040_IOQSPI);
+    object_initialize_child(obj, "pads-bank0", &s->pads_bank0,
+                            TYPE_RP2040_PADS_BANK0);
+    object_initialize_child(obj, "pads-qspi", &s->pads_qspi,
+                            TYPE_RP2040_PADS_QSPI);
+    object_initialize_child(obj, "pll-sys", &s->pll_sys, TYPE_RP2040_PLL);
+    qdev_prop_set_string(DEVICE(&s->pll_sys), "trace-name",
+                         "rp2040.pll_sys");
+    qdev_prop_set_uint32(DEVICE(&s->pll_sys), "base", RP2040_PLL_SYS_BASE);
+    qdev_prop_set_uint32(DEVICE(&s->pll_sys), "fallback-hz", 125000000);
+
+    object_initialize_child(obj, "pll-usb", &s->pll_usb, TYPE_RP2040_PLL);
+    qdev_prop_set_string(DEVICE(&s->pll_usb), "trace-name",
+                         "rp2040.pll_usb");
+    qdev_prop_set_uint32(DEVICE(&s->pll_usb), "base", RP2040_PLL_USB_BASE);
+    qdev_prop_set_uint32(DEVICE(&s->pll_usb), "fallback-hz", 48000000);
+
+    object_initialize_child(obj, "psm", &s->psm, TYPE_RP2040_PSM);
+    object_initialize_child(obj, "resets", &s->resets, TYPE_RP2040_RESETS);
+    object_initialize_child(obj, "rosc", &s->rosc, TYPE_RP2040_ROSC);
+    object_initialize_child(obj, "sio", &s->sio, TYPE_RP2040_SIO);
+    object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_RP2040_SYSCFG);
+    object_initialize_child(obj, "sysinfo", &s->sysinfo, TYPE_RP2040_SYSINFO);
+    object_initialize_child(obj, "tbman", &s->tbman, TYPE_RP2040_TBMAN);
+    object_initialize_child(obj, "timer", &s->timer, TYPE_RP2040_TIMER);
+    object_initialize_child(obj, "vreg", &s->vreg, TYPE_RP2040_VREG);
+    object_initialize_child(obj, "watchdog", &s->watchdog,
+                            TYPE_RP2040_WATCHDOG);
+    object_initialize_child(obj, "xosc", &s->xosc, TYPE_RP2040_XOSC);
+
+    s->irq = qemu_allocate_irqs(rp2040_set_irq, s, RP2040_NUM_IRQS);
+    s->sysclk = clock_new(obj, "sysclk");
+}
+
+static void rp2040_soc_realize(DeviceState *dev, Error **errp)
+{
+    RP2040State *s = RP2040(dev);
+    Error *err = NULL;
+    g_autofree char *filename = NULL;
+    ssize_t image_size;
+    int i;
+
+    if (!s->board_memory) {
+        error_setg(errp, "memory property was not set");
+        return;
+    }
+    qdev_prop_set_bit(DEVICE(&s->armv7m[RP2040_PROC1]), "start-powered-off",
+                      s->bootrom_file != NULL);
+
+    if (!memory_region_init_rom(&s->rom, OBJECT(dev), "rp2040.rom",
+                                RP2040_ROM_SIZE, errp)) {
+        return;
+    }
+    memory_region_add_subregion(s->board_memory, RP2040_ROM_BASE, &s->rom);
+    memory_region_init_io(&s->rom_poweroff, OBJECT(dev),
+                          &rp2040_powered_off_ops, s,
+                          "rp2040.rom.poweroff", RP2040_ROM_SIZE);
+    memory_region_add_subregion_overlap(s->board_memory, RP2040_ROM_BASE,
+                                        &s->rom_poweroff, 1);
+    memory_region_set_enabled(&s->rom_poweroff, false);
+    memory_region_init_io(&s->synthetic_rom_dbg, OBJECT(dev),
+                          &rp2040_synthetic_rom_dbg_ops, s,
+                          "rp2040.synthetic-rom-dbg",
+                          RP2040_SYNTHETIC_ROM_DBG_SIZE);
+    memory_region_add_subregion(s->board_memory,
+                                RP2040_SYNTHETIC_ROM_DBG_BASE,
+                                &s->synthetic_rom_dbg);
+
+    if (s->bootrom_file) {
+        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;
+        }
+
+        image_size = load_image_targphys(filename, RP2040_ROM_BASE,
+                                         RP2040_ROM_SIZE, errp);
+        if (image_size < 0) {
+            return;
+        }
+    } else {
+        rp2040_install_synthetic_bootrom();
+    }
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->xip), errp)) {
+        return;
+    }
+    if (!s->bootrom_file) {
+        rp2040_xip_set_synthetic_hardfault_vector(
+            &s->xip, RP2040_BOOTROM_HARDFAULT_EXIT_OFFSET | 1);
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->xip), 0, RP2040_XIP_BASE);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->xip), 1, RP2040_XIP_CTRL_BASE);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->xip), 2, RP2040_XIP_SSI_BASE);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->xip), 3, RP2040_XIP_AUX_BASE);
+    memory_region_add_subregion(get_system_memory(), RP2040_XIP_NOALLOC_BASE,
+                                &s->xip.xip_noalloc);
+    memory_region_add_subregion(get_system_memory(), RP2040_XIP_NOCACHE_BASE,
+                                &s->xip.xip_nocache);
+    memory_region_add_subregion(get_system_memory(),
+                                RP2040_XIP_NOCACHE_NOALLOC_BASE,
+                                &s->xip.xip_nocache_noalloc);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->busctrl), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->busctrl), 0, RP2040_BUSCTRL_BASE);
+
+    object_property_set_link(OBJECT(&s->dma), "memory",
+                             OBJECT(s->board_memory), &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->dma), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->dma), 0, RP2040_DMA_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), 0, s->irq[RP2040_DMA_IRQ_0]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), 1, s->irq[RP2040_DMA_IRQ_1]);
+    qdev_connect_gpio_out_named(DEVICE(&s->xip), "dreq-rx", 0,
+                                qdev_get_gpio_in_named(DEVICE(&s->dma),
+                                                       "dreq",
+                                                       RP2040_DREQ_XIP_SSIRX));
+    qdev_connect_gpio_out_named(DEVICE(&s->xip), "dreq-stream", 0,
+                                qdev_get_gpio_in_named(DEVICE(&s->dma),
+                                                       "dreq",
+                                                       
RP2040_DREQ_XIP_STREAM));
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->iobank0), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->iobank0), 0, RP2040_IOBANK0_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->iobank0), 0,
+                       s->irq[RP2040_IO_IRQ_BANK0]);
+    qdev_connect_gpio_out_named(DEVICE(&s->iobank0), "uart0-pin", 0,
+                                qemu_allocate_irq(rp2040_set_uart_pin, s, 0));
+    qdev_connect_gpio_out_named(DEVICE(&s->iobank0), "uart0-pin", 1,
+                                qemu_allocate_irq(rp2040_set_uart_pin, s, 1));
+    qdev_connect_gpio_out_named(DEVICE(&s->iobank0), "uart1-pin", 0,
+                                qemu_allocate_irq(rp2040_set_uart_pin, s, 2));
+    qdev_connect_gpio_out_named(DEVICE(&s->iobank0), "uart1-pin", 1,
+                                qemu_allocate_irq(rp2040_set_uart_pin, s, 3));
+
+    object_property_set_link(OBJECT(&s->ioqspi), "xip", OBJECT(&s->xip),
+                             &error_abort);
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->ioqspi), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ioqspi), 0, RP2040_IOQSPI_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->pads_bank0), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pads_bank0), 0,
+                    RP2040_PADS_BANK0_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->pads_qspi), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pads_qspi), 0,
+                    RP2040_PADS_QSPI_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->pll_sys), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pll_sys), 0, RP2040_PLL_SYS_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->pll_usb), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pll_usb), 0, RP2040_PLL_USB_BASE);
+
+    qdev_connect_clock_in(DEVICE(&s->clocks), "pll-sys",
+                          qdev_get_clock_out(DEVICE(&s->pll_sys), "clk"));
+    qdev_connect_clock_in(DEVICE(&s->clocks), "pll-usb",
+                          qdev_get_clock_out(DEVICE(&s->pll_usb), "clk"));
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->clocks), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->clocks), 0, RP2040_CLOCKS_BASE);
+    clock_set_source(s->sysclk, qdev_get_clock_out(DEVICE(&s->clocks),
+                                                   "clk-sys"));
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->psm), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->psm), 0, RP2040_PSM_BASE);
+    rp2040_psm_set_update_callback(&s->psm, rp2040_psm_update, s);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->resets), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->resets), 0, RP2040_RESETS_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->rosc), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->rosc), 0, RP2040_ROSC_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->sio), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sio), 0, RP2040_SIO_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->sio), 0,
+                       s->irq[RP2040_SIO_IRQ_PROC0]);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->syscfg), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscfg), 0, RP2040_SYSCFG_BASE);
+    rp2040_syscfg_set_update_callback(&s->syscfg, rp2040_syscfg_update, s);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->sysinfo), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, RP2040_SYSINFO_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->tbman), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->tbman), 0, RP2040_TBMAN_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->timer), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, RP2040_TIMER_BASE);
+    for (i = 0; i < 4; i++) {
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), i, s->irq[i]);
+    }
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->vreg), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->vreg), 0, RP2040_VREG_BASE);
+
+    qdev_connect_clock_in(DEVICE(&s->watchdog), "clk-ref",
+                          qdev_get_clock_out(DEVICE(&s->clocks), "clk-ref"));
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->watchdog), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->watchdog), 0, RP2040_WATCHDOG_BASE);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->xosc), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->xosc), 0, RP2040_XOSC_BASE);
+
+    for (i = 0; i < 4; i++) {
+        g_autofree char *name = g_strdup_printf("rp2040.sram%d", i);
+        g_autofree char *poweroff_name =
+            g_strdup_printf("rp2040.sram%d.poweroff", i);
+
+        if (!memory_region_init_ram(&s->sram[i], OBJECT(dev), name,
+                                    RP2040_SRAM_BANK_SIZE, errp)) {
+            return;
+        }
+        memory_region_add_subregion(s->board_memory,
+                                    RP2040_SRAM_BASE +
+                                    i * RP2040_SRAM_BANK_SIZE,
+                                    &s->sram[i]);
+        memory_region_init_io(&s->sram_poweroff[i], OBJECT(dev),
+                              &rp2040_powered_off_ops, s, poweroff_name,
+                              RP2040_SRAM_BANK_SIZE);
+        memory_region_add_subregion_overlap(s->board_memory,
+                                            RP2040_SRAM_BASE +
+                                            i * RP2040_SRAM_BANK_SIZE,
+                                            &s->sram_poweroff[i], 1);
+        memory_region_set_enabled(&s->sram_poweroff[i], false);
+    }
+
+    if (!memory_region_init_ram(&s->sram[4], OBJECT(dev), "rp2040.sram4",
+                                RP2040_SRAM_HI_SIZE, errp)) {
+        return;
+    }
+    memory_region_add_subregion(s->board_memory, RP2040_SRAM4_BASE,
+                                &s->sram[4]);
+    memory_region_init_io(&s->sram_poweroff[4], OBJECT(dev),
+                          &rp2040_powered_off_ops, s,
+                          "rp2040.sram4.poweroff", RP2040_SRAM_HI_SIZE);
+    memory_region_add_subregion_overlap(s->board_memory, RP2040_SRAM4_BASE,
+                                        &s->sram_poweroff[4], 1);
+    memory_region_set_enabled(&s->sram_poweroff[4], false);
+
+    if (!memory_region_init_ram(&s->sram[5], OBJECT(dev), "rp2040.sram5",
+                                RP2040_SRAM_HI_SIZE, errp)) {
+        return;
+    }
+    memory_region_add_subregion(s->board_memory, RP2040_SRAM5_BASE,
+                                &s->sram[5]);
+    memory_region_init_io(&s->sram_poweroff[5], OBJECT(dev),
+                          &rp2040_powered_off_ops, s,
+                          "rp2040.sram5.poweroff", RP2040_SRAM_HI_SIZE);
+    memory_region_add_subregion_overlap(s->board_memory, RP2040_SRAM5_BASE,
+                                        &s->sram_poweroff[5], 1);
+    memory_region_set_enabled(&s->sram_poweroff[5], false);
+
+    if (!memory_region_init_ram(&s->usbctrl_dpram, OBJECT(dev),
+                                "rp2040.usbctrl_dpram",
+                                RP2040_USBCTRL_DPRAM_SIZE, errp)) {
+        return;
+    }
+    memory_region_add_subregion(s->board_memory, RP2040_USBCTRL_DPRAM_BASE,
+                                &s->usbctrl_dpram);
+    memory_region_init_io(&s->usbctrl_dpram_poweroff, OBJECT(dev),
+                          &rp2040_powered_off_ops, s,
+                          "rp2040.usbctrl_dpram.poweroff",
+                          RP2040_USBCTRL_DPRAM_SIZE);
+    memory_region_add_subregion_overlap(s->board_memory,
+                                        RP2040_USBCTRL_DPRAM_BASE,
+                                        &s->usbctrl_dpram_poweroff, 1);
+    memory_region_set_enabled(&s->usbctrl_dpram_poweroff, false);
+
+    s->mempowerdown_ready = true;
+    rp2040_update_mempowerdown(s);
+
+    memory_region_init_io(&s->usbctrl_regs, OBJECT(dev),
+                          &rp2040_usbctrl_regs_ops, s,
+                          "rp2040.usbctrl_regs",
+                          RP2040_USBCTRL_REGS_SIZE);
+    memory_region_add_subregion(s->board_memory, RP2040_USBCTRL_REGS_BASE,
+                                &s->usbctrl_regs);
+
+    for (i = 0; i < ARRAY_SIZE(rp2040_unimplemented); i++) {
+        create_unimplemented_device(rp2040_unimplemented[i].name,
+                                    rp2040_unimplemented[i].base,
+                                    rp2040_unimplemented[i].size);
+    }
+
+    for (i = 0; i < RP2040_NUM_CORES; i++) {
+        int irq;
+
+        qdev_connect_clock_in(DEVICE(&s->armv7m[i]), "cpuclk", s->sysclk);
+        g_autofree char *name = g_strdup_printf("rp2040.proc%d-memory", i);
+
+        memory_region_init_alias(&s->cpu_memory[i], OBJECT(dev), name,
+                                 s->board_memory, 0,
+                                 memory_region_size(s->board_memory));
+        object_property_set_link(OBJECT(&s->armv7m[i]), "memory",
+                                 OBJECT(&s->cpu_memory[i]), &err);
+        if (err != NULL) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m[i]), errp)) {
+            return;
+        }
+        for (irq = 0; irq < RP2040_NUM_IRQS; irq++) {
+            s->cpu_irq[i][irq] = qdev_get_gpio_in(DEVICE(&s->armv7m[i]),
+                                                  irq);
+        }
+        s->nmi_irq[i] = qdev_get_gpio_in_named(DEVICE(&s->armv7m[i]),
+                                               "NMI", 0);
+    }
+    rp2040_update_nmi(s);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->iobank0), 1,
+                       s->cpu_irq[RP2040_PROC1][RP2040_IO_IRQ_BANK0]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->sio), 1,
+                       s->cpu_irq[RP2040_PROC1][RP2040_SIO_IRQ_PROC1]);
+
+    qdev_connect_clock_in(DEVICE(&s->uart0), "clk",
+                          qdev_get_clock_out(DEVICE(&s->clocks), "clk-peri"));
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart0), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart0), 0, RP2040_UART0_BASE);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart0), 1, RP2040_UART0_BASE + 0x1000);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart0), 0, s->irq[RP2040_UART0_IRQ]);
+    qdev_connect_gpio_out_named(DEVICE(&s->uart0), "dreq-tx", 0,
+                                qdev_get_gpio_in_named(DEVICE(&s->dma),
+                                                       "dreq",
+                                                       RP2040_DREQ_UART0_TX));
+    qdev_connect_gpio_out_named(DEVICE(&s->uart0), "dreq-rx", 0,
+                                qdev_get_gpio_in_named(DEVICE(&s->dma),
+                                                       "dreq",
+                                                       RP2040_DREQ_UART0_RX));
+
+    qdev_connect_clock_in(DEVICE(&s->uart1), "clk",
+                          qdev_get_clock_out(DEVICE(&s->clocks), "clk-peri"));
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart1), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart1), 0, RP2040_UART1_BASE);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart1), 1, RP2040_UART1_BASE + 0x1000);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart1), 0, s->irq[RP2040_UART1_IRQ]);
+    qdev_connect_gpio_out_named(DEVICE(&s->uart1), "dreq-tx", 0,
+                                qdev_get_gpio_in_named(DEVICE(&s->dma),
+                                                       "dreq",
+                                                       RP2040_DREQ_UART1_TX));
+    qdev_connect_gpio_out_named(DEVICE(&s->uart1), "dreq-rx", 0,
+                                qdev_get_gpio_in_named(DEVICE(&s->dma),
+                                                       "dreq",
+                                                       RP2040_DREQ_UART1_RX));
+    rp2040_update_uart_pins(s);
+}
+
+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),
+    DEFINE_PROP_BOOL("strict-uart-pins", RP2040State, strict_uart_pins, true),
+};
+
+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/hw/arm/trace-events b/hw/arm/trace-events
index 1b16f710fe..a888466ca0 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -1,5 +1,8 @@
 # See docs/devel/tracing.rst for syntax documentation.
 
+# rp2040.c
+rp2040_synthetic_flash_helper(const char *name, uint32_t count) "helper %s 
count %" PRIu32
+
 # omap1.c
 omap1_pwl_clocking_scheme(const char *scheme) "omap1 CLKM: clocking scheme set 
to %s"
 omap1_pwl_backlight(int output) "omap1 PWL: backlight now at %d/256"
diff --git a/include/hw/arm/rp2040.h b/include/hw/arm/rp2040.h
new file mode 100644
index 0000000000..90504264c7
--- /dev/null
+++ b/include/hw/arm/rp2040.h
@@ -0,0 +1,122 @@
+/*
+ * 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/irq.h"
+#include "hw/core/sysbus.h"
+#include "hw/dma/rp2040_dma.h"
+#include "hw/misc/rp2040_busctrl.h"
+#include "hw/misc/rp2040_clocks.h"
+#include "hw/misc/rp2040_iobank0.h"
+#include "hw/misc/rp2040_ioqspi.h"
+#include "hw/misc/rp2040_pads.h"
+#include "hw/misc/rp2040_pll.h"
+#include "hw/misc/rp2040_psm.h"
+#include "hw/misc/rp2040_resets.h"
+#include "hw/misc/rp2040_rosc.h"
+#include "hw/misc/rp2040_sio.h"
+#include "hw/misc/rp2040_syscfg.h"
+#include "hw/misc/rp2040_sysinfo.h"
+#include "hw/misc/rp2040_tbman.h"
+#include "hw/misc/rp2040_timer.h"
+#include "hw/misc/rp2040_vreg.h"
+#include "hw/misc/rp2040_watchdog.h"
+#include "hw/misc/rp2040_xosc.h"
+#include "hw/ssi/rp2040_xip.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_XIP_NOALLOC_BASE 0x11000000
+#define RP2040_XIP_NOCACHE_BASE 0x12000000
+#define RP2040_XIP_NOCACHE_NOALLOC_BASE 0x13000000
+#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_LO_SIZE   (4 * RP2040_SRAM_BANK_SIZE)
+#define RP2040_SRAM_HI_SIZE   (4 * KiB)
+#define RP2040_USBCTRL_DPRAM_BASE 0x50100000
+#define RP2040_USBCTRL_DPRAM_SIZE (4 * KiB)
+#define RP2040_USBCTRL_REGS_BASE  0x50110000
+#define RP2040_USBCTRL_REGS_SIZE  0x4000
+#define RP2040_SYNTHETIC_ROM_DBG_BASE 0x5fff0000
+#define RP2040_SYNTHETIC_ROM_DBG_SIZE 0x1000
+#define RP2040_SYNTHETIC_ROM_FLASH_HELPER_COUNT 6
+#define RP2040_NUM_CORES          2
+#define RP2040_NUM_IRQS           32
+
+struct RP2040State {
+    SysBusDevice parent_obj;
+
+    ARMv7MState armv7m[RP2040_NUM_CORES];
+    PL011State uart0;
+    PL011State uart1;
+    RP2040BusCtrlState busctrl;
+    RP2040ClocksState clocks;
+    RP2040DmaState dma;
+    RP2040IoBank0State iobank0;
+    RP2040IoQspiState ioqspi;
+    RP2040PadsBank0State pads_bank0;
+    RP2040PadsQspiState pads_qspi;
+    RP2040PllState pll_sys;
+    RP2040PllState pll_usb;
+    RP2040PsmState psm;
+    RP2040ResetsState resets;
+    RP2040RoscState rosc;
+    RP2040SioState sio;
+    RP2040SysCfgState syscfg;
+    RP2040SysInfoState sysinfo;
+    RP2040TbmanState tbman;
+    RP2040TimerState timer;
+    RP2040VregState vreg;
+    RP2040WatchdogState watchdog;
+    RP2040XoscState xosc;
+    RP2040XipState xip;
+
+    MemoryRegion *board_memory;
+    MemoryRegion cpu_memory[RP2040_NUM_CORES];
+    MemoryRegion rom;
+    MemoryRegion rom_poweroff;
+    MemoryRegion sram[6];
+    MemoryRegion sram_poweroff[6];
+    MemoryRegion usbctrl_dpram;
+    MemoryRegion usbctrl_dpram_poweroff;
+    MemoryRegion usbctrl_regs;
+    MemoryRegion synthetic_rom_dbg;
+    uint32_t usbctrl_reg[0x100 / sizeof(uint32_t)];
+    char *bootrom_file;
+
+    qemu_irq *irq;
+    qemu_irq cpu_irq[RP2040_NUM_CORES][RP2040_NUM_IRQS];
+    qemu_irq nmi_irq[RP2040_NUM_CORES];
+    bool irq_level[RP2040_NUM_CORES][RP2040_NUM_IRQS];
+    bool mempowerdown_ready;
+    bool strict_uart_pins;
+    bool uart0_tx_pin_enabled;
+    bool uart0_rx_pin_enabled;
+    bool uart1_tx_pin_enabled;
+    bool uart1_rx_pin_enabled;
+    uint32_t synthetic_rom_dbg_arg[4];
+    uint32_t synthetic_rom_dbg_result[4];
+    uint32_t synthetic_rom_flash_helper_count[
+        RP2040_SYNTHETIC_ROM_FLASH_HELPER_COUNT];
+
+    Clock *sysclk;
+};
+
+#endif
-- 
2.53.0


Reply via email to