Provide float and double boot ROM helpers through the synthetic QEMU helper
aperture, and accelerate the boot ROM flash entry points by calling the
existing XIP flash model directly.

These shortcuts are installed only when the built-in synthetic ROM is
selected. They are not RP2040 hardware interfaces; an external mask ROM
continues to exercise the modeled SSI/XIP path.

Add check-tcg coverage for floating-point tables and multicore flash
lockout.

Signed-off-by: gilles grimaud <[email protected]>
---
 hw/arm/rp2040.c                               | 901 +++++++++++++++++-
 hw/arm/trace-events                           |   3 +
 include/hw/arm/rp2040.h                       |   5 +
 tests/tcg/arm/system/meson.build              |  10 +
 tests/tcg/arm/system/rp2040-bootrom-float.S   | 247 +++++
 tests/tcg/arm/system/rp2040-bootrom-float.ref |   4 +
 tests/tcg/arm/system/rp2040-flash-lockout.S   | 387 ++++++++
 tests/tcg/arm/system/rp2040-flash-lockout.ref |   6 +
 8 files changed, 1537 insertions(+), 26 deletions(-)
 create mode 100644 tests/tcg/arm/system/rp2040-bootrom-float.S
 create mode 100644 tests/tcg/arm/system/rp2040-bootrom-float.ref
 create mode 100644 tests/tcg/arm/system/rp2040-flash-lockout.S
 create mode 100644 tests/tcg/arm/system/rp2040-flash-lockout.ref

diff --git a/hw/arm/rp2040.c b/hw/arm/rp2040.c
index a3ad360462..436b3be7f2 100644
--- a/hw/arm/rp2040.c
+++ b/hw/arm/rp2040.c
@@ -16,11 +16,15 @@
 #include "hw/core/irq.h"
 #include "hw/misc/rp2040.h"
 #include "hw/misc/unimp.h"
+#include "fpu/softfloat.h"
 #include "qemu/datadir.h"
 #include "qemu/log.h"
 #include "system/address-spaces.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
@@ -61,6 +65,70 @@
 #define RP2040_BOOTROM_ROM_VERSION_OFFSET 0x13
 #define RP2040_BOOTROM_SYNTHETIC_ROM_VERSION 2
 #define RP2040_BOOTROM_FLOAT_TABLE_WORDS 32
+#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_FLOAT_STUBS_OFFSET 0x0600
+#define RP2040_BOOTROM_DOUBLE_STUBS_OFFSET 0x0b00
+#define RP2040_BOOTROM_FP_STUB_SIZE 36
+
+#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_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
 
 /*
  * Temporary boot ROM used until a faithful RP2040 boot ROM is requested.  It
@@ -189,6 +257,7 @@ typedef struct RP2040BootromFunction {
     const char *name;
     const uint8_t *impl;
     size_t impl_size;
+    uint32_t code_literal_offset;
 } RP2040BootromFunction;
 
 typedef struct RP2040BootromData {
@@ -251,23 +320,98 @@ static const uint8_t rp2040_bootrom_memset4[] = {
     0x10, 0xbc, 0x70, 0x47,
 };
 
+static const uint8_t rp2040_bootrom_flash_noarg[] = {
+    /*
+     * Synthetic-ROM-only acceleration.  These stubs call the QEMU helper
+     * aperture directly; they do not model RP2040 SSI transactions.
+     */
+    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) }
+    { _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 }
+    { _code, _name, NULL, 0, UINT32_MAX }
 
 static const RP2040BootromFunction rp2040_bootrom_functions[] = {
-    RP2040_BOOTROM_NYI(RP2040_ROM_TABLE_CODE('C', 'X'),
-                       "flash_enter_cmd_xip"),
-    RP2040_BOOTROM_NYI(RP2040_ROM_TABLE_CODE('E', 'X'), "flash_exit_xip"),
-    RP2040_BOOTROM_NYI(RP2040_ROM_TABLE_CODE('F', 'C'),
-                       "flash_flush_cache"),
-    RP2040_BOOTROM_NYI(RP2040_ROM_TABLE_CODE('I', 'F'),
-                       "connect_internal_flash"),
-    RP2040_BOOTROM_NYI(RP2040_ROM_TABLE_CODE('R', 'E'),
-                       "flash_range_erase"),
-    RP2040_BOOTROM_NYI(RP2040_ROM_TABLE_CODE('R', 'P'),
-                       "flash_range_program"),
+    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",
@@ -293,17 +437,38 @@ static const RP2040BootromData rp2040_bootrom_data[] = {
 };
 
 static const uint8_t rp2040_bootrom_lookup[] = {
-    0x02, 0x88, 0x00, 0x2a, 0x05, 0xd0, 0x91, 0x42,
-    0x01, 0xd0, 0x04, 0x30, 0xf8, 0xe7, 0x40, 0x88,
-    0x70, 0x47, 0x5f, 0x22, 0x12, 0x06, 0xff, 0x23,
-    0x1b, 0x04, 0x1a, 0x43, 0x11, 0x60, 0x00, 0xbe,
-    0x00, 0x20, 0x70, 0x47,
+    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, 0x5f, 0x20, 0x00, 0x06, 0xff, 0x22,
-    0x12, 0x04, 0x10, 0x43, 0x01, 0x60, 0x00, 0xbe,
-    0xfe, 0xe7, 0xc0, 0x46, 0x00, 0x00, 0x00, 0x00,
+    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 void rp2040_store_hword(uint8_t *rom, uint32_t offset, uint16_t value)
@@ -320,6 +485,56 @@ static void rp2040_store_word(uint8_t *rom, uint32_t 
offset, uint32_t value)
     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);
@@ -335,6 +550,7 @@ static void rp2040_install_synthetic_bootrom(void)
         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,
@@ -348,6 +564,10 @@ static void rp2040_install_synthetic_bootrom(void)
 
         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,
@@ -358,6 +578,7 @@ static void rp2040_install_synthetic_bootrom(void)
                               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);
@@ -376,13 +597,34 @@ static void rp2040_install_synthetic_bootrom(void)
     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++) {
-        rp2040_store_word(rom, RP2040_BOOTROM_FLOAT_TABLE_OFFSET + 2 +
-                          i * sizeof(uint32_t), float_nyi);
-        rp2040_store_word(rom, RP2040_BOOTROM_DOUBLE_TABLE_OFFSET + 2 +
-                          i * sizeof(uint32_t), double_nyi);
+        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++) {
@@ -476,14 +718,595 @@ static const char *rp2040_bootrom_function_name(uint16_t 
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)
+{
+    qemu_log_mask(LOG_UNIMP,
+                  "%s: unimplemented %s table offset 0x%02" PRIx32 "\n",
+                  __func__, is_double ? "double" : "float", offset);
+}
+
+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)) {
+        a = copysign(INFINITY, a);
+    }
+    if (isnan(b)) {
+        b = copysign(INFINITY, b);
+    }
+    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);
+    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:
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: write to unknown synthetic register\n",
+                      __func__);
+        return;
+    }
+
+    if (rp2040_synthetic_fp_op(s, command)) {
+        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");
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: flash_enter_cmd_xip does not reconfigure SSI\n",
+                      __func__);
+        return;
+    case RP2040_ROM_TABLE_CODE('E', 'X'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_EXIT_XIP, "flash_exit_xip");
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: flash_exit_xip sends no flash commands\n",
+                      __func__);
+        return;
+    case RP2040_ROM_TABLE_CODE('F', 'C'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_FLUSH_CACHE, "flash_flush_cache");
+        qemu_log_mask(LOG_UNIMP, "%s: XIP cache is not modeled\n",
+                      __func__);
+        return;
+    case RP2040_ROM_TABLE_CODE('I', 'F'):
+        rp2040_synthetic_flash_helper_hit(
+            s, RP2040_SYNTHETIC_FLASH_CONNECT_INTERNAL_FLASH,
+            "connect_internal_flash");
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: synthetic helper assumes connected QSPI flash\n",
+                      __func__);
+        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;
+    }
+
     qemu_log_mask(LOG_UNIMP,
                   "%s: unimplemented boot ROM function '%c%c' (%s)\n",
                   __func__, code & 0xff, (code >> 8) & 0xff,
@@ -493,8 +1316,34 @@ static void rp2040_synthetic_rom_dbg_write(void *opaque, 
hwaddr addr,
 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)];
+    }
+
     qemu_log_mask(LOG_UNIMP,
-                  "%s: read from write-only synthetic register\n",
+                  "%s: read from unsupported synthetic register\n",
                   __func__);
     return 0;
 }
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
index 17235827c4..e13610d71c 100644
--- a/include/hw/arm/rp2040.h
+++ b/include/hw/arm/rp2040.h
@@ -54,6 +54,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(RP2040State, RP2040)
 #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
@@ -105,6 +106,10 @@ struct RP2040State {
     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;
 };
diff --git a/tests/tcg/arm/system/meson.build b/tests/tcg/arm/system/meson.build
index 9e1024cf11..b410e004d0 100644
--- a/tests/tcg/arm/system/meson.build
+++ b/tests/tcg/arm/system/meson.build
@@ -162,6 +162,16 @@ tests += {
     'qemu_args': rp2040_uart_qemu_args,
     'expected_output': 'rp2040-bootrom-helpers.ref',
   },
+  'rp2040-bootrom-float.S': {
+    'cflags': rp2040_cflags,
+    'qemu_args': rp2040_uart_qemu_args,
+    'expected_output': 'rp2040-bootrom-float.ref',
+  },
+  'rp2040-flash-lockout.S': {
+    'cflags': rp2040_cflags,
+    'qemu_args': rp2040_uart_qemu_args,
+    'expected_output': 'rp2040-flash-lockout.ref',
+  },
   'semiconsole.c': {
     'cflags': cflags,
     'qemu_args': ['-serial', 'none', '-chardev', 'stdio,mux=on,id=stdio0',
diff --git a/tests/tcg/arm/system/rp2040-bootrom-float.S 
b/tests/tcg/arm/system/rp2040-bootrom-float.S
new file mode 100644
index 0000000000..8c89a55e92
--- /dev/null
+++ b/tests/tcg/arm/system/rp2040-bootrom-float.S
@@ -0,0 +1,247 @@
+/*
+ * Raspberry Pi Pico boot ROM floating-point table test.
+ *
+ * Copyright 2026 Gilles Grimaud
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+.syntax unified
+.cpu cortex-m0plus
+.thumb
+
+#define SRAM_END               0x20042000
+#define UART0_DR               0x40034000
+#define ROM_DATA_TABLE_PTR     0x00000016
+#define ROM_TABLE_LOOKUP_PTR   0x00000018
+#define ROM_DATA_FLOAT_TABLE   0x4653
+#define ROM_DATA_DOUBLE_TABLE  0x4453
+#define FP_TABLE_FADD          0x00
+#define FP_TABLE_FMUL          0x08
+#define FP_TABLE_FDIV          0x0c
+#define FP_TABLE_FCMP_BASIC    0x54
+#define FP_TABLE_FLOAT2DOUBLE  0x7c
+#define SYS_EXIT               0x18
+#define ADP_STOPPED_APPLICATION_EXIT 0x20026
+#define ADP_STOPPED_RUNTIME_ERROR    0x20023
+
+.macro lookup_data code, dst
+    ldr r0, =\code
+    bl rom_data_lookup
+    cmp r0, 0
+    bne 0f
+    b fail_lookup
+0:
+    mov \dst, r0
+.endm
+
+.macro load_table_func basereg, offset
+    mov r4, \basereg
+    adds r4, \offset
+    ldrb r3, [r4]
+    ldrb r5, [r4, 1]
+    lsls r5, 8
+    orrs r3, r5
+    ldrb r5, [r4, 2]
+    lsls r5, 16
+    orrs r3, r5
+    ldrb r5, [r4, 3]
+    lsls r5, 24
+    orrs r3, r5
+    mov r4, r3
+.endm
+
+.macro fail_if_ne reg, expected, target
+    ldr r2, =\expected
+    cmp \reg, r2
+    beq 0f
+    b \target
+0:
+.endm
+
+.section .vectors, "a", %progbits
+.global vector_table
+vector_table:
+    .word SRAM_END
+    .word reset_handler + 1
+    .word default_handler + 1
+    .word default_handler + 1
+
+.section .text, "ax", %progbits
+.thumb_func
+.global reset_handler
+reset_handler:
+    ldr r0, =start_message
+    bl puts
+
+    lookup_data ROM_DATA_FLOAT_TABLE, r6
+
+    /* 1.25f + 2.5f = 3.75f. */
+    load_table_func r6, FP_TABLE_FADD
+    ldr r0, =0x3fa00000
+    ldr r1, =0x40200000
+    blx r4
+    fail_if_ne r0, 0x40700000, fail_single
+
+    /* 3.75f * 2.0f = 7.5f. */
+    load_table_func r6, FP_TABLE_FMUL
+    ldr r0, =0x40700000
+    ldr r1, =0x40000000
+    blx r4
+    fail_if_ne r0, 0x40f00000, fail_single
+
+    /* 7.5f / 3.0f = 2.5f. */
+    load_table_func r6, FP_TABLE_FDIV
+    ldr r0, =0x40f00000
+    ldr r1, =0x40400000
+    blx r4
+    fail_if_ne r0, 0x40200000, fail_single
+
+    /* The ROM treats an input NaN as an infinity. */
+    load_table_func r6, FP_TABLE_FCMP_BASIC
+    ldr r0, =0x7fc00000
+    ldr r1, =0x7f800000
+    blx r4
+    fail_if_ne r0, 0, fail_single
+
+    ldr r0, =0x3f800000
+    ldr r1, =0x7fc00000
+    blx r4
+    fail_if_ne r0, 0xffffffff, fail_single
+
+    ldr r0, =0x7fc00000
+    ldr r1, =0x3f800000
+    blx r4
+    fail_if_ne r0, 1, fail_single
+
+    /* Convert 1.25f to 0x3ff4000000000000. */
+    load_table_func r6, FP_TABLE_FLOAT2DOUBLE
+    ldr r0, =0x3fa00000
+    blx r4
+    fail_if_ne r0, 0x00000000, fail_single
+    fail_if_ne r1, 0x3ff40000, fail_single
+
+    ldr r0, =single_ok_message
+    bl puts
+
+    lookup_data ROM_DATA_DOUBLE_TABLE, r7
+
+    /* 1.5 + 4.0 = 5.5. */
+    load_table_func r7, FP_TABLE_FADD
+    movs r0, 0
+    ldr r1, =0x3ff80000
+    movs r2, 0
+    ldr r3, =0x40100000
+    blx r4
+    fail_if_ne r0, 0x00000000, fail_double
+    fail_if_ne r1, 0x40160000, fail_double
+
+    /* The double table applies the same NaN-as-infinity rule. */
+    load_table_func r7, FP_TABLE_FCMP_BASIC
+    movs r0, 0
+    ldr r1, =0x7ff80000
+    movs r2, 0
+    ldr r3, =0x7ff00000
+    blx r4
+    fail_if_ne r0, 0, fail_double
+
+    movs r0, 0
+    ldr r1, =0x3ff00000
+    movs r2, 0
+    ldr r3, =0x7ff80000
+    blx r4
+    fail_if_ne r0, 0xffffffff, fail_double
+
+    movs r0, 0
+    ldr r1, =0x7ff80000
+    movs r2, 0
+    ldr r3, =0x3ff00000
+    blx r4
+    fail_if_ne r0, 1, fail_double
+
+    /* The SD entry at 0x7c converts 1.5 to the float 1.5f. */
+    load_table_func r7, FP_TABLE_FLOAT2DOUBLE
+    movs r0, 0
+    ldr r1, =0x3ff80000
+    blx r4
+    fail_if_ne r0, 0x3fc00000, fail_double
+
+    ldr r0, =double_ok_message
+    bl puts
+    ldr r0, =ok_message
+    bl puts
+    movs r0, SYS_EXIT
+    ldr r1, =ADP_STOPPED_APPLICATION_EXIT
+    bkpt 0xab
+
+1:
+    b 1b
+
+fail_lookup:
+    ldr r0, =fail_lookup_message
+    b test_failed
+
+fail_single:
+    ldr r0, =fail_single_message
+    b test_failed
+
+fail_double:
+    ldr r0, =fail_double_message
+
+test_failed:
+    bl puts
+    movs r0, SYS_EXIT
+    ldr r1, =ADP_STOPPED_RUNTIME_ERROR
+    bkpt 0xab
+
+2:
+    b 2b
+
+.thumb_func
+rom_data_lookup:
+    push {r4, lr}
+    mov r1, r0
+    ldr r0, =ROM_DATA_TABLE_PTR
+    ldrh r0, [r0]
+    ldr r4, =ROM_TABLE_LOOKUP_PTR
+    ldrh r4, [r4]
+    blx r4
+    pop {r4, pc}
+
+.thumb_func
+default_handler:
+    ldr r0, =fail_exception_message
+    b test_failed
+
+.thumb_func
+puts:
+    ldr r2, =UART0_DR
+
+3:
+    ldrb r1, [r0]
+    cmp r1, 0
+    beq 4f
+    str r1, [r2]
+    adds r0, 1
+    b 3b
+
+4:
+    bx lr
+
+.section .rodata, "a", %progbits
+start_message:
+    .asciz "PICO TCG BOOTROM FLOAT START\n"
+single_ok_message:
+    .asciz "PICO TCG BOOTROM FLOAT SF OK\n"
+double_ok_message:
+    .asciz "PICO TCG BOOTROM FLOAT SD OK\n"
+ok_message:
+    .asciz "PICO TCG BOOTROM FLOAT OK\n"
+fail_lookup_message:
+    .asciz "PICO TCG BOOTROM FLOAT FAIL: LOOKUP\n"
+fail_single_message:
+    .asciz "PICO TCG BOOTROM FLOAT FAIL: SF\n"
+fail_double_message:
+    .asciz "PICO TCG BOOTROM FLOAT FAIL: SD\n"
+fail_exception_message:
+    .asciz "PICO TCG BOOTROM FLOAT FAIL: EXCEPTION\n"
diff --git a/tests/tcg/arm/system/rp2040-bootrom-float.ref 
b/tests/tcg/arm/system/rp2040-bootrom-float.ref
new file mode 100644
index 0000000000..d1eb0e7463
--- /dev/null
+++ b/tests/tcg/arm/system/rp2040-bootrom-float.ref
@@ -0,0 +1,4 @@
+PICO TCG BOOTROM FLOAT START
+PICO TCG BOOTROM FLOAT SF OK
+PICO TCG BOOTROM FLOAT SD OK
+PICO TCG BOOTROM FLOAT OK
diff --git a/tests/tcg/arm/system/rp2040-flash-lockout.S 
b/tests/tcg/arm/system/rp2040-flash-lockout.S
new file mode 100644
index 0000000000..a7be6bbb51
--- /dev/null
+++ b/tests/tcg/arm/system/rp2040-flash-lockout.S
@@ -0,0 +1,387 @@
+/*
+ * Raspberry Pi Pico multicore flash lockout test.
+ *
+ * Copyright 2026 Gilles Grimaud
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+.syntax unified
+.cpu cortex-m0plus
+.thumb
+
+#define SRAM_END               0x20042000
+#define CORE1_STACK            0x20041000
+#define PAGE_BUFFER            0x20000000
+#define UART0_DR               0x40034000
+#define SIO_BASE               0xd0000000
+#define SIO_FIFO_ST            0x50
+#define SIO_FIFO_WR            0x54
+#define SIO_FIFO_RD            0x58
+#define SIO_SPINLOCK0          0x100
+#define SIO_FIFO_ST_VLD        0x01
+#define SIO_FIFO_ST_RDY        0x02
+#define NVIC_ISER              0xe000e100
+#define SIO_IRQ_PROC1_MASK     0x00010000
+#define DEBUG_BASE             0x5fff0000
+#define DEBUG_CMD              0x00
+#define DEBUG_ARG0             0x04
+#define DEBUG_ARG1             0x08
+#define DEBUG_ARG2             0x0c
+#define DEBUG_ARG3             0x10
+#define DEBUG_COUNT_ERASE      0x50
+#define DEBUG_COUNT_PROGRAM    0x54
+#define DEBUG_FLASH_ERASE      0x4552
+#define DEBUG_FLASH_PROGRAM    0x5052
+#define FLASH_OFFSET           0x00101000
+#define FLASH_ADDRESS          0x10101000
+#define LOCK_REQUEST           0xfacefeed
+#define LOCK_ACK               0xface0001
+#define RELEASE_REQUEST        0xfacebeef
+#define RELEASE_ACK            0xface0002
+#define BAD_ACK                0xbad10002
+#define SYS_EXIT               0x18
+#define ADP_STOPPED_APPLICATION_EXIT 0x20026
+#define ADP_STOPPED_RUNTIME_ERROR    0x20023
+
+.section .vectors, "a", %progbits
+.global vector_table
+vector_table:
+    .word SRAM_END
+    .word reset_handler + 1
+    .word default_handler + 1
+    .word default_handler + 1
+    .rept 28
+    .word default_handler + 1
+    .endr
+    .word core1_fifo_irq + 1
+
+.section .text, "ax", %progbits
+.thumb_func
+.global reset_handler
+reset_handler:
+    ldr r0, =start_message
+    bl puts
+    bl launch_core1
+
+    ldr r0, =launch_message
+    bl puts
+    bl claim_spinlock
+    ldr r0, =LOCK_REQUEST
+    bl fifo_push
+    sev
+    ldr r0, =LOCK_ACK
+    bl fifo_pop_expect
+
+    ldr r0, =locked_message
+    bl puts
+    bl flash_erase_program_verify
+
+    ldr r0, =flash_message
+    bl puts
+    ldr r0, =RELEASE_REQUEST
+    bl fifo_push
+    sev
+    ldr r0, =RELEASE_ACK
+    bl fifo_pop_expect
+    bl release_spinlock
+
+    ldr r0, =released_message
+    bl puts
+    ldr r0, =ok_message
+    bl puts
+    movs r0, SYS_EXIT
+    ldr r1, =ADP_STOPPED_APPLICATION_EXIT
+    bkpt 0xab
+
+1:
+    b 1b
+
+.thumb_func
+core1_entry:
+    ldr r0, =NVIC_ISER
+    ldr r1, =SIO_IRQ_PROC1_MASK
+    str r1, [r0]
+    cpsie i
+
+2:
+    wfe
+    b 2b
+
+.thumb_func
+core1_fifo_irq:
+    push {r0, r1, r2, r3, r4, lr}
+    bl fifo_pop
+    ldr r1, =LOCK_REQUEST
+    cmp r0, r1
+    bne core1_bad_request
+    ldr r0, =LOCK_ACK
+    bl fifo_push
+
+3:
+    wfe
+    ldr r0, =SIO_BASE
+    ldr r1, [r0, SIO_FIFO_ST]
+    movs r0, SIO_FIFO_ST_VLD
+    tst r1, r0
+    beq 3b
+    bl fifo_pop
+    ldr r1, =RELEASE_REQUEST
+    cmp r0, r1
+    bne core1_bad_request
+    ldr r0, =RELEASE_ACK
+    bl fifo_push
+    pop {r0, r1, r2, r3, r4, pc}
+
+core1_bad_request:
+    ldr r0, =BAD_ACK
+    bl fifo_push
+
+4:
+    b 4b
+
+.thumb_func
+launch_core1:
+    push {r4, r5, lr}
+    ldr r4, =launch_sequence
+    movs r5, 6
+
+5:
+    ldr r0, [r4]
+    cmp r0, 0
+    bne 6f
+    bl fifo_drain
+    sev
+    ldr r0, [r4]
+
+6:
+    bl fifo_push
+    sev
+    bl fifo_pop
+    ldr r1, [r4]
+    cmp r0, r1
+    bne fail_launch
+    adds r4, 4
+    subs r5, 1
+    bne 5b
+    pop {r4, r5, pc}
+
+.thumb_func
+claim_spinlock:
+    ldr r0, =(SIO_BASE + SIO_SPINLOCK0)
+    ldr r1, [r0]
+    cmp r1, 0
+    beq fail_spinlock
+    bx lr
+
+.thumb_func
+release_spinlock:
+    ldr r0, =(SIO_BASE + SIO_SPINLOCK0)
+    movs r1, 0
+    str r1, [r0]
+    bx lr
+
+.thumb_func
+flash_erase_program_verify:
+    push {r4, r5, r6, lr}
+    ldr r0, =PAGE_BUFFER
+    movs r1, 0
+    movs r2, 0
+    movs r3, 1
+    lsls r3, r3, 8
+
+7:
+    strb r1, [r0, r2]
+    adds r1, 1
+    adds r2, 1
+    cmp r2, r3
+    bne 7b
+
+    ldr r4, =DEBUG_BASE
+    ldr r0, =FLASH_OFFSET
+    str r0, [r4, DEBUG_ARG0]
+    movs r0, 1
+    lsls r0, r0, 12
+    str r0, [r4, DEBUG_ARG1]
+    movs r0, 0
+    str r0, [r4, DEBUG_ARG2]
+    movs r0, 0x20
+    str r0, [r4, DEBUG_ARG3]
+    ldr r0, =DEBUG_FLASH_ERASE
+    str r0, [r4, DEBUG_CMD]
+
+    ldr r0, =FLASH_ADDRESS
+    movs r2, 0
+    movs r3, 1
+    lsls r3, r3, 8
+
+8:
+    ldrb r5, [r0, r2]
+    movs r6, 0xff
+    cmp r5, r6
+    bne fail_flash
+    adds r2, 1
+    cmp r2, r3
+    bne 8b
+
+    ldr r0, =FLASH_OFFSET
+    str r0, [r4, DEBUG_ARG0]
+    ldr r0, =PAGE_BUFFER
+    str r0, [r4, DEBUG_ARG1]
+    movs r0, 1
+    lsls r0, r0, 8
+    str r0, [r4, DEBUG_ARG2]
+    ldr r0, =DEBUG_FLASH_PROGRAM
+    str r0, [r4, DEBUG_CMD]
+
+    ldr r0, =FLASH_ADDRESS
+    movs r1, 0
+    movs r2, 0
+    movs r3, 1
+    lsls r3, r3, 8
+
+9:
+    ldrb r5, [r0, r2]
+    cmp r5, r1
+    bne fail_flash
+    adds r1, 1
+    adds r2, 1
+    cmp r2, r3
+    bne 9b
+
+    ldr r0, [r4, DEBUG_COUNT_ERASE]
+    cmp r0, 1
+    bne fail_flash
+    ldr r0, [r4, DEBUG_COUNT_PROGRAM]
+    cmp r0, 1
+    bne fail_flash
+    pop {r4, r5, r6, pc}
+
+.thumb_func
+fifo_push:
+    push {r1, r2, r3, lr}
+    ldr r1, =SIO_BASE
+
+10:
+    ldr r2, [r1, SIO_FIFO_ST]
+    movs r3, SIO_FIFO_ST_RDY
+    tst r2, r3
+    beq 10b
+    str r0, [r1, SIO_FIFO_WR]
+    pop {r1, r2, r3, pc}
+
+.thumb_func
+fifo_pop:
+    push {r1, r2, r3, lr}
+    ldr r1, =SIO_BASE
+
+11:
+    ldr r2, [r1, SIO_FIFO_ST]
+    movs r3, SIO_FIFO_ST_VLD
+    tst r2, r3
+    beq 11b
+    ldr r0, [r1, SIO_FIFO_RD]
+    pop {r1, r2, r3, pc}
+
+.thumb_func
+fifo_drain:
+    push {r0, r1, r2, r3, lr}
+    ldr r1, =SIO_BASE
+
+12:
+    ldr r2, [r1, SIO_FIFO_ST]
+    movs r3, SIO_FIFO_ST_VLD
+    tst r2, r3
+    beq 13f
+    ldr r0, [r1, SIO_FIFO_RD]
+    b 12b
+
+13:
+    pop {r0, r1, r2, r3, pc}
+
+.thumb_func
+fifo_pop_expect:
+    push {r4, lr}
+    mov r4, r0
+    bl fifo_pop
+    cmp r0, r4
+    bne fail_fifo
+    pop {r4, pc}
+
+fail_launch:
+    ldr r0, =fail_launch_message
+    b test_failed
+
+fail_spinlock:
+    ldr r0, =fail_spinlock_message
+    b test_failed
+
+fail_fifo:
+    ldr r0, =fail_fifo_message
+    b test_failed
+
+fail_flash:
+    ldr r0, =fail_flash_message
+
+test_failed:
+    bl puts
+    movs r0, SYS_EXIT
+    ldr r1, =ADP_STOPPED_RUNTIME_ERROR
+    bkpt 0xab
+
+14:
+    b 14b
+
+.thumb_func
+default_handler:
+    ldr r0, =fail_exception_message
+    b test_failed
+
+.thumb_func
+puts:
+    push {r1, r2, lr}
+    ldr r1, =UART0_DR
+
+15:
+    ldrb r2, [r0]
+    cmp r2, 0
+    beq 16f
+    str r2, [r1]
+    adds r0, 1
+    b 15b
+
+16:
+    pop {r1, r2, pc}
+
+.section .rodata, "a", %progbits
+.align 2
+launch_sequence:
+    .word 0
+    .word 0
+    .word 1
+    .word vector_table
+    .word CORE1_STACK
+    .word core1_entry + 1
+
+start_message:
+    .asciz "PICO TCG FLASH LOCKOUT START\n"
+launch_message:
+    .asciz "PICO TCG FLASH LOCKOUT CORE1 OK\n"
+locked_message:
+    .asciz "PICO TCG FLASH LOCKOUT LOCKED\n"
+flash_message:
+    .asciz "PICO TCG FLASH LOCKOUT PROGRAM OK\n"
+released_message:
+    .asciz "PICO TCG FLASH LOCKOUT RELEASED\n"
+ok_message:
+    .asciz "PICO TCG FLASH LOCKOUT OK\n"
+fail_launch_message:
+    .asciz "PICO TCG FLASH LOCKOUT FAIL: LAUNCH\n"
+fail_spinlock_message:
+    .asciz "PICO TCG FLASH LOCKOUT FAIL: SPINLOCK\n"
+fail_fifo_message:
+    .asciz "PICO TCG FLASH LOCKOUT FAIL: FIFO\n"
+fail_flash_message:
+    .asciz "PICO TCG FLASH LOCKOUT FAIL: FLASH\n"
+fail_exception_message:
+    .asciz "PICO TCG FLASH LOCKOUT FAIL: EXCEPTION\n"
diff --git a/tests/tcg/arm/system/rp2040-flash-lockout.ref 
b/tests/tcg/arm/system/rp2040-flash-lockout.ref
new file mode 100644
index 0000000000..300d9b134b
--- /dev/null
+++ b/tests/tcg/arm/system/rp2040-flash-lockout.ref
@@ -0,0 +1,6 @@
+PICO TCG FLASH LOCKOUT START
+PICO TCG FLASH LOCKOUT CORE1 OK
+PICO TCG FLASH LOCKOUT LOCKED
+PICO TCG FLASH LOCKOUT PROGRAM OK
+PICO TCG FLASH LOCKOUT RELEASED
+PICO TCG FLASH LOCKOUT OK
-- 
2.55.0


Reply via email to