Re: [PATCH 4/4] hw/arm/mps2-tz.c: add new mps3-an555 board

Peter Maydell <[email protected]> Fri, 31 Jul 2026 13:43:44 +0100
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <CAFEAcA8_VTfU-7xvR=Nii8nC_SEPvv0VtZEVQD6zdg=bJFnHTA@mail.gmail.com>
On Fri, 24 Jul 2026 at 00:36, Simon Xu <[email protected]> wrote:
>
> Add mps3-an555 board modeled after the existing mps3-an547 board.
> - Add additional AHB PPC EXP 1. Add unimplemented timing adapters for SSE-310.
> - Include documentation for SSE-310 and mps3-an555.
>
> Arm mps3-an555 TRM: https://developer.arm.com/documentation/107642/latest/
>
> Reviewed-by: Owen Giles <[email protected]>
> Reviewed-by: Robert Elliott <[email protected]>
> Signed-off-by: Simon Xu <[email protected]>
> ---
>  docs/system/arm/mps2.rst |   6 +-
>  hw/arm/mps2-tz.c         | 143 ++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 144 insertions(+), 5 deletions(-)
>
> diff --git a/docs/system/arm/mps2.rst b/docs/system/arm/mps2.rst
> index a305935cc4..489a8b0fa7 100644
> --- a/docs/system/arm/mps2.rst
> +++ b/docs/system/arm/mps2.rst
> @@ -1,5 +1,5 @@
> -Arm MPS2 and MPS3 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``, ``mps3-an524``, ``mps3-an536``, ``mps3-an547``)
> -=========================================================================================================================================================================
> +Arm MPS2 and MPS3 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``, ``mps3-an524``, ``mps3-an536``, ``mps3-an547``, ``mps3-an555``)
> +=========================================================================================================================================================================================
>
>  These board models use Arm M-profile or R-profile CPUs.
>
> @@ -31,6 +31,8 @@ FPGA images using M-profile CPUs:
>    Dual Cortex-M33 on an MPS3, as documented in Arm Application Note AN524
>  ``mps3-an547``
>    Cortex-M55 on an MPS3, as documented in Arm Application Note AN547
> +``mps3-an555``
> +  Cortex-M85 on an MPS3, as documented in Arm Application Note AN555
>
>  FPGA images using R-profile CPUs:
>
> diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
> index f101c1b7c3..04a2b37fd8 100644
> --- a/hw/arm/mps2-tz.c
> +++ b/hw/arm/mps2-tz.c
> @@ -1,5 +1,5 @@
>  /*
> - * ARM V2M MPS2 board emulation, trustzone aware FPGA images
> + * Arm V2M MPS2 and MPS3 board emulation, trustzone aware FPGA images
>   *
>   * Copyright (c) 2017 Linaro Limited
>   * Written by Peter Maydell
> @@ -18,12 +18,13 @@
>   *  "mps2-an521" -- Dual Cortex-M33 as documented in Application Note AN521
>   *  "mps2-an524" -- Dual Cortex-M33 as documented in Application Note AN524
>   *  "mps2-an547" -- Single Cortex-M55 as documented in Application Note AN547
> + *  "mps3-an555" -- Single Cortex-M85 as documented in Application Note AN555
>   *
>   * Links to the TRM for the board itself and to the various Application
>   * Notes which document the FPGA images can be found here:
>   * https://developer.arm.com/products/system-design/development-boards/fpga-prototyping-boards/mps2
>   *
> - * Board TRM:
> + * MPS2 and MPS2+ Board TRM:
>   * https://developer.arm.com/documentation/100112/latest/
>   * Application Note AN505:
>   * https://developer.arm.com/documentation/dai0505/latest/
> @@ -31,8 +32,12 @@
>   * https://developer.arm.com/documentation/dai0521/latest/
>   * Application Note AN524:
>   * https://developer.arm.com/documentation/dai0524/latest/
> + * MPS3 Board TRM:
> + * https://developer.arm.com/documentation/100765/latest/

I think we should put the MPS3 board TRM link after the MPS2
board TRM link, and before all the application note links.

>   * Application Note AN547:
>   * https://developer.arm.com/documentation/dai0547/latest/
> + * Application Note AN555:
> + * https://developer.arm.com/documentation/107642/latest/
>   *
>   * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide
>   * (ARM ECM0601256) for the details of some of the device layout:
> @@ -42,6 +47,8 @@
>   *  https://developer.arm.com/documentation/101104/latest/
>   * and the AN547 uses the SSE-300, whose layout is in the SSE-300 TRM:
>   *  https://developer.arm.com/documentation/101773/latest/
> + * and the AN555 uses the SSE-310, whose layout is in the SSE-310 TRM:
> + *  https://developer.arm.com/documentation/102778/latest/
>   */
>
>  #include "qemu/osdep.h"
> @@ -77,7 +84,7 @@
>  #include "qom/object.h"
>  #include "hw/core/irq.h"
>
> -#define MPS2TZ_NUMIRQ_MAX 96
> +#define MPS2TZ_NUMIRQ_MAX 100

Why did you need to update this? The AN555 appnote documents that
interrupts go from 0 to 127; 0..31 are the ones in the SSE, and 32..127
are the "external" ones. NUMIRQ for this code is the number of external
interrupts, so 127 - 31 == 96. Like the AN547, the highest interrupt
is 127, from UART5. So I think that 96 should be the right value for
AN555 also.

> @@ -1103,6 +1121,72 @@ static void mps2tz_common_init(MachineState *machine)
>          },
>      };
>
> +    const PPCInfo an555_ppcs[] = { {
> +            .name = "apb_ppcexp0",
> +            .ports = {
> +                { "ssram-mpc", make_mpc, &mms->mpc[0], 0x57000000, 0x1000 },
> +                { "qspi-mpc", make_mpc, &mms->mpc[1], 0x57001000, 0x1000 },
> +                { "ddr-mpc", make_mpc, &mms->mpc[2], 0x57002000, 0x1000 },

The 555 appnote says that APB PPC EXP0 has the timing adapters in
it, not the MPCs. (Table 6-3.)

(We seem to get this differently wrong for SSE-300, where the MPCs
should be on ports 13, 14, 15, not 0, 1, 2.)

> +            },
> +        }, {
> +            .name = "apb_ppcexp1",
> +            .ports = {
> +                { "i2c0", make_i2c, &mms->i2c[0], 0x49200000, 0x1000, {},
> +                  { .i2c_internal = true /* touchscreen */ } },
> +                { "i2c1", make_i2c, &mms->i2c[1], 0x49201000, 0x1000, {},
> +                  { .i2c_internal = true /* audio conf */ } },
> +                { "spi0", make_spi, &mms->spi[0], 0x49202000, 0x1000, { 53 } },
> +                { "spi1", make_spi, &mms->spi[1], 0x49203000, 0x1000, { 54 } },
> +                { "spi2", make_spi, &mms->spi[2], 0x49204000, 0x1000, { 55 } },
> +                { "i2c2", make_i2c, &mms->i2c[2], 0x49205000, 0x1000, {},
> +                  { .i2c_internal = false /* shield 0 */ } },
> +                { "i2c3", make_i2c, &mms->i2c[3], 0x49206000, 0x1000, {},
> +                  { .i2c_internal = false /* shield 1 */ } },
> +                { /* port 7 reserved */ },
> +                { "i2c4", make_i2c, &mms->i2c[4], 0x49208000, 0x1000, {},
> +                  { .i2c_internal = true /* DDR4 EEPROM */ } },
> +            },
> +        }, {
> +            .name = "apb_ppcexp2",
> +            .ports = {
> +                { "scc", make_scc, &mms->scc, 0x49300000, 0x1000 },
> +                { "i2s-audio", make_unimp_dev, &mms->i2s_audio, 0x49301000, 0x1000 },
> +                { "fpgaio", make_fpgaio, &mms->fpgaio, 0x49302000, 0x1000 },
> +                { "uart0", make_uart, &mms->uart[0], 0x49303000, 0x1000, { 33, 34, 43 } },
> +                { "uart1", make_uart, &mms->uart[1], 0x49304000, 0x1000, { 35, 36, 44 } },
> +                { "uart2", make_uart, &mms->uart[2], 0x49305000, 0x1000, { 37, 38, 45 } },
> +                { "uart3", make_uart, &mms->uart[3], 0x49306000, 0x1000, { 39, 40, 46 } },
> +                { "uart4", make_uart, &mms->uart[4], 0x49307000, 0x1000, { 41, 42, 47 } },
> +                { "uart5", make_uart, &mms->uart[5], 0x49308000, 0x1000, { 125, 126, 127 } },
> +
> +                { /* port 9 reserved */ },
> +                { "clcd", make_unimp_dev, &mms->cldc, 0x4930a000, 0x1000 },
> +                { "rtc", make_rtc, &mms->rtc, 0x4930b000, 0x1000 },
> +            },
> +        }, {
> +            .name = "ahb_ppcexp0",
> +            .ports = {
> +                { "gpio0", make_unimp_dev, &mms->gpio[0], 0x41100000, 0x1000 },
> +                { "gpio1", make_unimp_dev, &mms->gpio[1], 0x41101000, 0x1000 },
> +                { "gpio2", make_unimp_dev, &mms->gpio[2], 0x41102000, 0x1000 },
> +                { "gpio3", make_unimp_dev, &mms->gpio[3], 0x41103000, 0x1000 },
> +                { /* port 4 USER AHB interface 0 */ },
> +                { /* port 5 USER AHB interface 1 */ },
> +                { /* port 6 USER AHB interface 2 */ },
> +                { /* port 7 USER AHB interface 3 */ },
> +                { "eth-usb", make_eth_usb, NULL, 0x41400000, 0x200000, { 49 } },

13..15 are QSPI read/write controllers; we should have make_unimp_dev
lines for them,
I think.

> +            },
> +        }, {
> +            .name = "ahb_ppcexp1",
> +            .ports = {
> +                { "dma0", make_dma, &mms->dma[0], 0x41200000, 0x1000, { 59, 57, 58 } },
> +                { "dma1", make_dma, &mms->dma[1], 0x41201000, 0x1000, { 62, 60, 61 } },
> +                { "dma2", make_dma, &mms->dma[2], 0x41202000, 0x1000, { 65, 63, 64 } },
> +                { "dma3", make_dma, &mms->dma[3], 0x41203000, 0x1000, { 68, 66, 67 } },

(It looks like we get this wrong on the AN547 -- it also has an AHB PPCEXP1,
which has DMA1, 2, 3 in it (but not 0).)

> +            },
> +        },
> +    };
> +
>      switch (mmc->fpga_type) {
>      case FPGA_AN505:
>      case FPGA_AN521:
> @@ -1117,6 +1201,10 @@ static void mps2tz_common_init(MachineState *machine)
>          ppcs = an547_ppcs;
>          num_ppcs = ARRAY_SIZE(an547_ppcs);
>          break;
> +    case FPGA_AN555:
> +        ppcs = an555_ppcs;
> +        num_ppcs = ARRAY_SIZE(an555_ppcs);
> +        break;
>      default:
>          g_assert_not_reached();
>      }
> @@ -1200,6 +1288,12 @@ static void mps2tz_common_init(MachineState *machine)
>          create_unimplemented_device("U55 timing adapter 1", 0x48103000, 0x1000);
>      }
>
> +    if (mmc->fpga_type == FPGA_AN555) {
> +        create_unimplemented_device("FPGA SRAM timing adapter", 0x41700000, 0x1000);
> +        create_unimplemented_device("QSPI timing adapter", 0x41701000, 0x1000);
> +        create_unimplemented_device("DDR4 timing adapter", 0x41702000, 0x1000);
> +    }

As noted above, these seem to be behind one of the PPCs, so we should
create those via the PPC data tables, and create the MPCs in code here.

> +
>      create_non_mpc_ram(mms);
>
>      if (mmc->fpga_type == FPGA_AN524) {
> @@ -1447,6 +1541,41 @@ static void mps3tz_an547_class_init(ObjectClass *oc, const void *data)
>      mps2tz_set_default_ram_info(mmc);
>  }
>
> +static void mps3tz_an555_class_init(ObjectClass *oc, const void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +    MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc);
> +    static const char * const valid_cpu_types[] = {
> +        ARM_CPU_TYPE_NAME("cortex-m85"),
> +        NULL
> +    };
> +
> +    mc->desc = "ARM MPS3 with AN555 FPGA image for Cortex-M85";
> +    mc->default_cpus = 1;
> +    mc->min_cpus = mc->default_cpus;
> +    mc->max_cpus = mc->default_cpus;
> +    mmc->fpga_type = FPGA_AN555;
> +    mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m85");
> +    mc->valid_cpu_types = valid_cpu_types;
> +    mmc->scc_id = 0x41055551;
> +    mmc->sysclk_frq = 25 * 1000 * 1000; /* 25MHz */
> +    mmc->apb_periph_frq = 25 * 1000 * 1000; /* 25MHz */
> +    mmc->oscclk = an555_oscclk;
> +    mmc->len_oscclk = ARRAY_SIZE(an555_oscclk);
> +    mmc->fpgaio_num_leds = 10;
> +    mmc->fpgaio_has_switches = true;
> +    mmc->fpgaio_has_dbgctrl = true;

These fpgaio tweaks are all correct, but the AN555 FPGAIO
block has an extra register, GPIOALT2 at 0xc. Ideally we should
implement that to at least be "reads as written, LOG_UNIMP
for writes". (The register technically exists on AN547 but
only as "all bits reserved".)

thanks
-- PMM