All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis
@ 2026-10-15  3:17 Portia Stephens
  2026-10-15  3:17 ` [PATCH 01/12] hw/riscv/atlantis: Set valid_cpu_types for Atlantis machine Portia Stephens
                   ` (11 more replies)
  0 siblings, 12 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

Atlantis includes a 32-bit RISC-V core (RCPU) used for boot, low-level
control, and ongoing system management [1]. This series is the first step in
adding the RCPU to the tt-atlantis machine. The RCPU shares access to most
peripherals with the Ascalon cores. However, the RCPU has a different memory view.
This requires a different memory map and a private memory container that the
SoC aliases the shared devices into, at the addresses the RCPU expects.

On hardware, the RCPU is responsible for loading images from the boot medium
into DRAM and releasing the Ascalon cores from reset, via the CPL. This
functionality is not implemented in these changes. The images for the Ascalon
cores continue to be loaded directly into DRAM via command line arguments and
the Ascalon cores start executing at reset.

With these changes, running tt-atlantis with rcpu=on will create a separate
CPUCluster with the RCPU, giving gdb two inferiors. If no bootrom is provided,
the RCPU hart will be held powered off. With a bootrom provided,
via tt-atlantis,rcpu=on,bootrom=, the RCPU will execute the image and console
output is on the second command line serial. This series does not provide a
binary to test on the RCPU, it is in active development and not publicly
available. The QEMU tests are updated to test booting of the Ascalon cores with
and without the RCPU modeled. The machine properties related to the cpus, smp
count and cpu-type, apply to the Ascalon cluster. The RCPU is an auxiliary
core and is not configurable.

See docs/system/riscv/tt_atlantis.rst for usage.

[1] https://github.com/tenstorrent/atlantis-soc-docs/blob/main/data_sheet/atl_soc_data_sheet.pdf

Joel Stanley (1):
  machine: Support extra board CPUs outside -smp

Portia Stephens (11):
  hw/riscv/atlantis: Set valid_cpu_types for Atlantis machine
  hw/riscv/atlantis: Update fdt functions to take generic parameters
  hw/riscv/atlantis: Move Ascalon into cluster struct
  hw/riscv/atlantis: Store Ascalon memory in cluster struct
  hw/riscv/atlantis: Create CPU cluster for Atlantis cpus
  target/riscv: Add Tenstorrent Real-time CPU
  hw/riscv/atlantis: Add the RCPU to the Atlantis SoC
  hw/riscv/atlantis: Back RCPU memory region by unimplemented device
  hw/riscv/atlantis: Load the RCPU bootrom
  tests/functional/riscv64: Add rcpu to tt-atlantis tests
  docs/atlantis: Update Atlantis documentation to include the RCPU

 MAINTAINERS                                  |   1 +
 accel/tcg/tcg-all.c                          |   2 +-
 docs/system/gdb.rst                          |   2 +
 docs/system/riscv/tt_atlantis.rst            |  30 +
 hw/riscv/Kconfig                             |   2 +
 hw/riscv/tt_atlantis.c                       | 613 +++++++++++++++----
 include/hw/core/boards.h                     |   4 +
 include/hw/riscv/tt_atlantis.h               |  81 ++-
 target/riscv/cpu-qom.h                       |   1 +
 target/riscv/cpu.c                           |  28 +
 target/riscv/tcg/csr.h                       |   3 +
 target/riscv/tcg/meson.build                 |   1 +
 target/riscv/tcg/tt_csr.c                    |  58 ++
 tests/functional/riscv64/test_tt_atlantis.py |  10 +-
 14 files changed, 709 insertions(+), 127 deletions(-)
 create mode 100644 target/riscv/tcg/tt_csr.c

-- 
2.43.0



^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 01/12] hw/riscv/atlantis: Set valid_cpu_types for Atlantis machine
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
@ 2026-10-15  3:17 ` Portia Stephens
  2026-10-15  3:17 ` [PATCH 02/12] hw/riscv/atlantis: Update fdt functions to take generic parameters Portia Stephens
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

The Atlantis machine only supports setting the cpu_type to an Ascalon
CPU. Set the valid_cpu_types to reflect this.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/tt_atlantis.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index 311de64eda..ec52faf17f 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -76,6 +76,11 @@ static const MemMapEntry tt_atlantis_memmap[] = {
     [TT_ATL_DDR_HI] =          { 0x100000000,  0x1000000000 },
 };
 
+static const char *const tt_atlantis_valid_cpu_types[] = {
+    TYPE_RISCV_CPU_TT_ASCALON,
+    NULL
+};
+
 static I2CBus *i2c_get_bus(TTAtlantisSoCState *s, unsigned busnr)
 {
     assert(busnr < TT_ATL_NUM_I2C);
@@ -792,6 +797,7 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
     mc->default_cpus = 8;
     mc->default_ram_size = 4 * GiB;
     mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
+    mc->valid_cpu_types = tt_atlantis_valid_cpu_types;
     mc->block_default_type = IF_VIRTIO;
     mc->no_cdrom = 1;
     mc->default_ram_id = "tt_atlantis.ram";
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 02/12] hw/riscv/atlantis: Update fdt functions to take generic parameters
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
  2026-10-15  3:17 ` [PATCH 01/12] hw/riscv/atlantis: Set valid_cpu_types for Atlantis machine Portia Stephens
@ 2026-10-15  3:17 ` Portia Stephens
  2026-10-15  3:17 ` [PATCH 03/12] hw/riscv/atlantis: Move Ascalon into cluster struct Portia Stephens
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

Currently the Atlantis fdt functions take TTAtlantisSoCState as
parameter and derive all needed values from there. This doesn't easily
allow for using these functions once the system has multiple memmaps and
hart arrays. This change updates the fdt functions to take more generic
parameters. This is in preparation for adding more hart arrays of
different clusters to the Atlantis SoC.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/tt_atlantis.c | 83 ++++++++++++++++++++++--------------------
 1 file changed, 43 insertions(+), 40 deletions(-)

diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index ec52faf17f..684e12195d 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -94,18 +94,18 @@ static uint32_t next_phandle(void)
     return fdt_phandle++;
 }
 
-static void create_fdt_memory(void *fdt, TTAtlantisSoCState *s)
+static void create_fdt_memory(void *fdt, const MemMapEntry *memmap,
+                              hwaddr ram_size)
 {
-    hwaddr ram_size = memory_region_size(s->dram);
     hwaddr size_lo = ram_size;
     hwaddr size_hi = 0;
 
-    if (size_lo > s->memmap[TT_ATL_DDR_LO].size) {
-        size_lo = s->memmap[TT_ATL_DDR_LO].size;
+    if (size_lo > memmap[TT_ATL_DDR_LO].size) {
+        size_lo = memmap[TT_ATL_DDR_LO].size;
         size_hi = ram_size - size_lo;
     }
 
-    riscv_create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_LO].base,
+    riscv_create_fdt_socket_memory(fdt, memmap[TT_ATL_DDR_LO].base,
                                    size_lo, 0, false);
     if (size_hi) {
         /*
@@ -114,12 +114,13 @@ static void create_fdt_memory(void *fdt, TTAtlantisSoCState *s)
          * (or good reason) to describe that aliasing 2GB with DT?
          */
         riscv_create_fdt_socket_memory(fdt,
-                                       s->memmap[TT_ATL_DDR_HI].base + size_lo,
+                                       memmap[TT_ATL_DDR_HI].base + size_lo,
                                        size_hi, 0, false);
     }
 }
 
-static void create_fdt_aclint(void *fdt, TTAtlantisSoCState *s,
+static void create_fdt_aclint(void *fdt, RISCVHartArrayState *harts,
+                              const MemMapEntry *memmap,
                               uint32_t *intc_phandles)
 {
     g_autofree char *name = NULL;
@@ -127,15 +128,15 @@ static void create_fdt_aclint(void *fdt, TTAtlantisSoCState *s,
     uint32_t aclint_cells_size;
     hwaddr addr;
 
-    aclint_mtimer_cells = g_new0(uint32_t, s->cpus.num_harts * 2);
+    aclint_mtimer_cells = g_new0(uint32_t, harts->num_harts * 2);
 
-    for (int cpu = 0; cpu < s->cpus.num_harts; cpu++) {
+    for (int cpu = 0; cpu < harts->num_harts; cpu++) {
         aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
         aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
     }
-    aclint_cells_size = s->cpus.num_harts * sizeof(uint32_t) * 2;
+    aclint_cells_size = harts->num_harts * sizeof(uint32_t) * 2;
 
-    addr = s->memmap[TT_ATL_ACLINT].base;
+    addr = memmap[TT_ATL_ACLINT].base;
 
     name = g_strdup_printf("/soc/mtimer@%"HWADDR_PRIX, addr);
     qemu_fdt_add_subnode(fdt, name);
@@ -221,52 +222,53 @@ static void create_fdt_one_aplic(void *fdt,
     qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle);
 }
 
-static void create_fdt_pmu(void *fdt, TTAtlantisSoCState *s)
+static void create_fdt_pmu(void *fdt, RISCVHartArrayState *harts)
 {
     char pmu_name[] = "/pmu";
-    RISCVCPU *hart = &s->cpus.harts[0];
+    RISCVCPU *hart = &harts->harts[0];
 
     qemu_fdt_add_subnode(fdt, pmu_name);
     qemu_fdt_setprop_string(fdt, pmu_name, "compatible", "riscv,pmu");
     riscv_pmu_generate_fdt_node(fdt, hart->pmu_avail_ctrs, pmu_name);
 }
 
-static void create_fdt_cpu(void *fdt, TTAtlantisSoCState *s,
+static void create_fdt_cpu(void *fdt, RISCVHartArrayState *harts,
+                           const MemMapEntry *memmap,
                            uint32_t aplic_s_phandle,
                            uint32_t imsic_s_phandle)
 {
-    g_autofree uint32_t *intc_phandles = g_new0(uint32_t, s->cpus.num_harts);
-    int num_harts = s->cpus.num_harts;
+    int num_harts = harts->num_harts;
+    g_autofree uint32_t *intc_phandles = g_new0(uint32_t, num_harts);
 
     riscv_fdt_create_cpu_socket_subnode(fdt, TT_ACLINT_TIMEBASE_FREQ);
 
-    riscv_create_fdt_socket_cpus(fdt, s->cpus.harts, 0, num_harts,
-                                 s->cpus.hartid_base, &fdt_phandle,
+    riscv_create_fdt_socket_cpus(fdt, harts->harts, 0, num_harts,
+                                 harts->hartid_base, &fdt_phandle,
                                  intc_phandles, false, false);
 
-    create_fdt_aclint(fdt, s, intc_phandles);
+    create_fdt_aclint(fdt, harts, memmap, intc_phandles);
 
     /* M-level IMSIC node */
     uint32_t msi_m_phandle = next_phandle();
-    create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_MIMSIC], num_harts,
+    create_fdt_one_imsic(fdt, &memmap[TT_ATL_MIMSIC], num_harts,
                          intc_phandles, msi_m_phandle,
                          IRQ_M_EXT, TT_IMSIC_GUEST_BITS);
 
     /* S-level IMSIC node */
-    create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_SIMSIC], num_harts,
+    create_fdt_one_imsic(fdt, &memmap[TT_ATL_SIMSIC], num_harts,
                          intc_phandles, imsic_s_phandle,
                          IRQ_S_EXT, TT_IMSIC_GUEST_BITS);
 
     uint32_t aplic_m_phandle = next_phandle();
 
     /* M-level APLIC node */
-    create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_MAPLIC],
+    create_fdt_one_aplic(fdt, &memmap[TT_ATL_MAPLIC],
                          msi_m_phandle, intc_phandles,
                          aplic_m_phandle, aplic_s_phandle,
                          IRQ_M_EXT, num_harts);
 
     /* S-level APLIC node */
-    create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_SAPLIC],
+    create_fdt_one_aplic(fdt, &memmap[TT_ATL_SAPLIC],
                          imsic_s_phandle, intc_phandles,
                          aplic_s_phandle, 0,
                          IRQ_S_EXT, num_harts);
@@ -338,10 +340,10 @@ static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq,
     qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0);
 }
 
-static void create_fdt_i2c_device(void *fdt, TTAtlantisSoCState *s, int bus,
-                                  const char *compat, int addr)
+static void create_fdt_i2c_device(void *fdt, const MemMapEntry *memmap,
+                                  int bus, const char *compat, int addr)
 {
-    hwaddr base = s->memmap[TT_ATL_I2C0 + bus].base;
+    hwaddr base = memmap[TT_ATL_I2C0 + bus].base;
     g_autofree char *name = g_strdup_printf("/soc/i2c@%"HWADDR_PRIX"/sensor@%x",
                                             base, addr);
 
@@ -370,7 +372,8 @@ static char *create_fdt_prcm(void *fdt, const MemMapEntry *mem,
     return name;
 }
 
-static void finalize_fdt(void *fdt, TTAtlantisSoCState *s)
+static void finalize_fdt(void *fdt, RISCVHartArrayState *harts,
+                         const MemMapEntry *memmap, hwaddr ram_size)
 {
     uint32_t aplic_s_phandle = next_phandle();
     uint32_t imsic_s_phandle = next_phandle();
@@ -378,9 +381,9 @@ static void finalize_fdt(void *fdt, TTAtlantisSoCState *s)
     uint32_t prcm_rcpu_phandle;
     g_autofree char *rcpu_name, *hsio_name, *pcie_name, *mm_name;
 
-    create_fdt_cpu(fdt, s, aplic_s_phandle, imsic_s_phandle);
+    create_fdt_cpu(fdt, harts, memmap, aplic_s_phandle, imsic_s_phandle);
 
-    create_fdt_memory(fdt, s);
+    create_fdt_memory(fdt, memmap, ram_size);
 
     /*
      * We want to do this, but the Linux aplic driver was broken before v6.16
@@ -391,7 +394,7 @@ static void finalize_fdt(void *fdt, TTAtlantisSoCState *s)
 
     create_fdt_clk(fdt, "osc-24m", 24000000, osc_24m_phandle);
 
-    rcpu_name = create_fdt_prcm(fdt, &s->memmap[TT_ATL_PRCM_RCPU], "rcpu");
+    rcpu_name = create_fdt_prcm(fdt, &memmap[TT_ATL_PRCM_RCPU], "rcpu");
     prcm_rcpu_phandle = qemu_fdt_get_phandle(fdt, rcpu_name);
     qemu_fdt_setprop_cells(fdt, rcpu_name, "clocks", osc_24m_phandle);
     qemu_fdt_setprop_cells(fdt, rcpu_name, "assigned-clocks",
@@ -401,33 +404,33 @@ static void finalize_fdt(void *fdt, TTAtlantisSoCState *s)
                            prcm_rcpu_phandle, TT_ATL_CLK_RCPU_PLL,
                            prcm_rcpu_phandle, TT_ATL_CLK_NOC_PLL);
 
-    hsio_name = create_fdt_prcm(fdt, &s->memmap[TT_ATL_PRCM_HSIO], "hsio");
+    hsio_name = create_fdt_prcm(fdt, &memmap[TT_ATL_PRCM_HSIO], "hsio");
     qemu_fdt_setprop_cells(fdt, hsio_name, "clocks", osc_24m_phandle,
                            prcm_rcpu_phandle, TT_ATL_CLK_HSIO_PLL);
 
-    pcie_name = create_fdt_prcm(fdt, &s->memmap[TT_ATL_PRCM_PCIE], "pcie");
+    pcie_name = create_fdt_prcm(fdt, &memmap[TT_ATL_PRCM_PCIE], "pcie");
     qemu_fdt_setprop_cells(fdt, pcie_name, "clocks", osc_24m_phandle,
                            prcm_rcpu_phandle, TT_ATL_CLK_PCIE_PLL);
 
-    mm_name = create_fdt_prcm(fdt, &s->memmap[TT_ATL_PRCM_MM], "mm");
+    mm_name = create_fdt_prcm(fdt, &memmap[TT_ATL_PRCM_MM], "mm");
     qemu_fdt_setprop_cells(fdt, mm_name, "clocks", osc_24m_phandle,
                            prcm_rcpu_phandle, TT_ATL_CLK_MM_PLL0,
                            prcm_rcpu_phandle, TT_ATL_CLK_MM_PLL1);
 
-    create_fdt_uart(fdt, &s->memmap[TT_ATL_UART1], TT_ATL_UART1_IRQ,
+    create_fdt_uart(fdt, &memmap[TT_ATL_UART1], TT_ATL_UART1_IRQ,
                     aplic_s_phandle, prcm_rcpu_phandle, TT_ATL_CLK_UART1_PCLK);
 
     for (int i = 0; i < TT_ATL_NUM_I2C; i++) {
         create_fdt_i2c(fdt,
-                       &s->memmap[TT_ATL_I2C0 + i],
+                       &memmap[TT_ATL_I2C0 + i],
                        TT_ATL_I2C0_IRQ + i,
                        aplic_s_phandle, prcm_rcpu_phandle,
                        TT_ATL_CLK_I2C0_PCLK + i);
     }
 
     /* I2C peripherals: qemu specific */
-    create_fdt_i2c_device(fdt, s, 0, "dallas,ds1338", 0x6f);
-    create_fdt_i2c_device(fdt, s, 4, "ti,tmp105", 0x48);
+    create_fdt_i2c_device(fdt, memmap, 0, "dallas,ds1338", 0x6f);
+    create_fdt_i2c_device(fdt, memmap, 4, "ti,tmp105", 0x48);
 }
 
 static void create_fdt(TTAtlantisState *ams)
@@ -444,7 +447,7 @@ static void create_fdt(TTAtlantisState *ams)
 
     qemu_fdt_add_subnode(ms->fdt, "/aliases");
 
-    create_fdt_pmu(ms->fdt, &ams->soc);
+    create_fdt_pmu(ms->fdt, &ams->soc.cpus);
 }
 
 static void load_fdt(TTAtlantisState *ams)
@@ -475,7 +478,7 @@ static void load_fdt(TTAtlantisState *ams)
         g_strfreev(node_path);
     }
 
-    create_fdt_memory(ms->fdt, &ams->soc);
+    create_fdt_memory(ms->fdt, ams->soc.memmap, ms->ram_size);
 }
 
 static void tt_atlantis_machine_done(Notifier *n, void *data)
@@ -496,7 +499,7 @@ static void tt_atlantis_machine_done(Notifier *n, void *data)
      * dynamic sysbus devices. Our FDT needs to be finalized.
      */
     if (machine->dtb == NULL) {
-        finalize_fdt(machine->fdt, s);
+        finalize_fdt(machine->fdt, &s->cpus, s->memmap, machine->ram_size);
     }
 
     mem_size = machine->ram_size;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 03/12] hw/riscv/atlantis: Move Ascalon into cluster struct
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
  2026-10-15  3:17 ` [PATCH 01/12] hw/riscv/atlantis: Set valid_cpu_types for Atlantis machine Portia Stephens
  2026-10-15  3:17 ` [PATCH 02/12] hw/riscv/atlantis: Update fdt functions to take generic parameters Portia Stephens
@ 2026-10-15  3:17 ` Portia Stephens
  2026-10-15  3:17 ` [PATCH 04/12] hw/riscv/atlantis: Store Ascalon memory in " Portia Stephens
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

The Atlantis SoC has 3 CPU clusters of different CPU types and with
different memory views of the system; Ascalon, RCPU, and CPL. In preparation
for adding additional CPU clusters, move the Ascalon state information
into it's own device.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/tt_atlantis.c         | 191 ++++++++++++++++++++-------------
 include/hw/riscv/tt_atlantis.h |  25 +++--
 2 files changed, 137 insertions(+), 79 deletions(-)

diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index 684e12195d..8f217088c6 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -436,6 +436,7 @@ static void finalize_fdt(void *fdt, RISCVHartArrayState *harts,
 static void create_fdt(TTAtlantisState *ams)
 {
     MachineState *ms = MACHINE(ams);
+    TTAscalonClusterState *ac = &ams->soc.ascalon;
     int fdt_size = 0;
 
     ms->fdt = riscv_create_board_device_tree("Tenstorrent Atlantis RISC-V Machine",
@@ -447,12 +448,13 @@ static void create_fdt(TTAtlantisState *ams)
 
     qemu_fdt_add_subnode(ms->fdt, "/aliases");
 
-    create_fdt_pmu(ms->fdt, &ams->soc.cpus);
+    create_fdt_pmu(ms->fdt, &ac->cpus);
 }
 
 static void load_fdt(TTAtlantisState *ams)
 {
     MachineState *ms = MACHINE(ams);
+    TTAscalonClusterState *ac = &ams->soc.ascalon;
     char **node_path;
     Error *err = NULL;
     int fdt_size = 0;
@@ -478,18 +480,19 @@ static void load_fdt(TTAtlantisState *ams)
         g_strfreev(node_path);
     }
 
-    create_fdt_memory(ms->fdt, ams->soc.memmap, ms->ram_size);
+    create_fdt_memory(ms->fdt, ac->memmap, ms->ram_size);
 }
 
 static void tt_atlantis_machine_done(Notifier *n, void *data)
 {
     TTAtlantisState *ams = container_of(n, TTAtlantisState, machine_done);
     TTAtlantisSoCState *s = &ams->soc;
+    TTAscalonClusterState *ac = &s->ascalon;
     MachineState *machine = MACHINE(ams);
-    hwaddr start_addr = s->memmap[TT_ATL_DDR_LO].base;
+    hwaddr start_addr = ac->memmap[TT_ATL_DDR_LO].base;
     hwaddr mem_size;
     target_ulong firmware_end_addr, kernel_start_addr;
-    const char *firmware_name = riscv_default_firmware_name(&s->cpus);
+    const char *firmware_name = riscv_default_firmware_name(&ac->cpus);
     uint64_t fdt_load_addr;
     uint64_t kernel_entry;
     RISCVBootInfo boot_info;
@@ -499,15 +502,16 @@ static void tt_atlantis_machine_done(Notifier *n, void *data)
      * dynamic sysbus devices. Our FDT needs to be finalized.
      */
     if (machine->dtb == NULL) {
-        finalize_fdt(machine->fdt, &s->cpus, s->memmap, machine->ram_size);
+        finalize_fdt(machine->fdt, &ac->cpus, ac->memmap,
+                     machine->ram_size);
     }
 
     mem_size = machine->ram_size;
-    if (mem_size > s->memmap[TT_ATL_DDR_LO].size) {
-        mem_size = s->memmap[TT_ATL_DDR_LO].size;
+    if (mem_size > ac->memmap[TT_ATL_DDR_LO].size) {
+        mem_size = ac->memmap[TT_ATL_DDR_LO].size;
     }
-    riscv_boot_info_init_discontig_mem(&boot_info, &s->cpus,
-                                       s->memmap[TT_ATL_DDR_LO].base,
+    riscv_boot_info_init_discontig_mem(&boot_info, &ac->cpus,
+                                       ac->memmap[TT_ATL_DDR_LO].base,
                                        mem_size);
 
     firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
@@ -530,15 +534,15 @@ static void tt_atlantis_machine_done(Notifier *n, void *data)
         kernel_entry = kernel_start_addr;
     }
 
-    fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base,
-                                           s->memmap[TT_ATL_DDR_LO].size,
+    fdt_load_addr = riscv_compute_fdt_addr(ac->memmap[TT_ATL_DDR_LO].base,
+                                           ac->memmap[TT_ATL_DDR_LO].size,
                                            machine, &boot_info);
     riscv_load_fdt(fdt_load_addr, machine->fdt);
 
     /* load the reset vector */
-    riscv_setup_rom_reset_vec(machine, &s->cpus, start_addr,
-                              s->memmap[TT_ATL_BOOTROM].base,
-                              s->memmap[TT_ATL_BOOTROM].size,
+    riscv_setup_rom_reset_vec(machine, &ac->cpus, start_addr,
+                              ac->memmap[TT_ATL_BOOTROM].base,
+                              ac->memmap[TT_ATL_BOOTROM].size,
                               kernel_entry,
                               fdt_load_addr);
 }
@@ -548,11 +552,69 @@ static inline int tt_atlantis_uart_index(int uart_dev)
     return uart_dev - TT_ATL_UART0;
 }
 
+static void tt_ascalon_cluster_init(Object *obj)
+{
+    TTAscalonClusterState *ac = TT_ASCALON_CLUSTER(obj);
+
+    object_initialize_child(obj, "cpus", &ac->cpus, TYPE_RISCV_HART_ARRAY);
+}
+
+static void tt_ascalon_cluster_realize(DeviceState *dev, Error **errp)
+{
+    TTAscalonClusterState *ac = TT_ASCALON_CLUSTER(dev);
+
+    if (!ac->memory) {
+        error_setg(errp, "'memory' link is not set");
+        return;
+    }
+    ac->memmap = tt_atlantis_memmap;
+
+    /* Boot ROM */
+    if (!memory_region_init_rom(&ac->bootrom, OBJECT(ac), "tt-atlantis.bootrom",
+                                ac->memmap[TT_ATL_BOOTROM].size, errp)) {
+        return;
+    }
+    memory_region_add_subregion(ac->memory, ac->memmap[TT_ATL_BOOTROM].base,
+                                &ac->bootrom);
+
+    /* CPUs */
+    object_property_set_str(OBJECT(&ac->cpus), "cpu-type", ac->cpu_type,
+                            &error_abort);
+    object_property_set_int(OBJECT(&ac->cpus), "hartid-base", 0,
+                            &error_abort);
+    object_property_set_int(OBJECT(&ac->cpus), "num-harts", ac->num_harts,
+                            &error_abort);
+    object_property_set_int(OBJECT(&ac->cpus), "resetvec",
+                            ac->memmap[TT_ATL_BOOTROM].base,
+                            &error_abort);
+    object_property_set_link(OBJECT(&ac->cpus), "memory",
+                             OBJECT(ac->memory),
+                             &error_abort);
+    if (!sysbus_realize(SYS_BUS_DEVICE(&ac->cpus), errp)) {
+        return;
+    }
+}
+
+static const Property tt_ascalon_cluster_props[] = {
+    DEFINE_PROP_STRING("cpu-type", TTAscalonClusterState, cpu_type),
+    DEFINE_PROP_UINT32("num-harts", TTAscalonClusterState, num_harts, 8),
+    DEFINE_PROP_LINK("memory", TTAscalonClusterState, memory,
+                     TYPE_MEMORY_REGION, MemoryRegion *),
+};
+
+static void tt_ascalon_cluster_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = tt_ascalon_cluster_realize;
+    device_class_set_props(dc, tt_ascalon_cluster_props);
+}
+
 static void tt_atlantis_soc_init(Object *obj)
 {
     TTAtlantisSoCState *s = TT_ATLANTIS_SOC(obj);
 
-    object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
+    object_initialize_child(obj, "ascalon", &s->ascalon, TYPE_TT_ASCALON_CLUSTER);
 
     for (int i = 0; i < TT_ATL_NUM_UARTS; i++) {
         object_initialize_child(obj, "uart[*]", &s->uart[i],
@@ -581,52 +643,36 @@ static void tt_atlantis_soc_init(Object *obj)
 static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
 {
     TTAtlantisSoCState *s = TT_ATLANTIS_SOC(dev);
+    TTAscalonClusterState *ac = &s->ascalon;
     ram_addr_t lo_ram_size, ram_size;
-    int hart_count = s->num_harts;
+    int hart_count = ac->num_harts;
 
-    if (!s->memory) {
-        error_setg(errp, "'memory' link is not set");
-        return;
-    }
     if (!s->dram) {
         error_setg(errp, "'dram' link is not set");
         return;
     }
-    ram_size = memory_region_size(s->dram);
-
-    s->memmap = tt_atlantis_memmap;
 
-    /* CPUs */
-    object_property_set_str(OBJECT(&s->cpus), "cpu-type", s->cpu_type,
-                            &error_abort);
-    object_property_set_int(OBJECT(&s->cpus), "hartid-base", 0,
-                            &error_abort);
-    object_property_set_int(OBJECT(&s->cpus), "num-harts", hart_count,
-                            &error_abort);
-    object_property_set_int(OBJECT(&s->cpus), "resetvec",
-                            s->memmap[TT_ATL_BOOTROM].base,
-                            &error_abort);
-    object_property_set_link(OBJECT(&s->cpus), "memory", OBJECT(s->memory),
-                             &error_abort);
-    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpus), errp)) {
+    if (!qdev_realize(DEVICE(&s->ascalon), NULL, errp)) {
         return;
     }
 
-    s->irqchip = riscv_create_aia(s->memory,
+    ram_size = memory_region_size(s->dram);
+
+    s->irqchip = riscv_create_aia(ac->memory,
                                   true, TT_IMSIC_GUESTS,
                                   TT_IMSIC_STRIDE,
                                   TT_IMSIC_STRIDE,
                                   TT_IRQCHIP_NUM_SOURCES,
-                                  &s->memmap[TT_ATL_MAPLIC],
-                                  &s->memmap[TT_ATL_SAPLIC],
-                                  &s->memmap[TT_ATL_MIMSIC],
-                                  &s->memmap[TT_ATL_SIMSIC],
+                                  &ac->memmap[TT_ATL_MAPLIC],
+                                  &ac->memmap[TT_ATL_SAPLIC],
+                                  &ac->memmap[TT_ATL_MIMSIC],
+                                  &ac->memmap[TT_ATL_SIMSIC],
                                   0, 0, hart_count,
                                   TT_IRQCHIP_NUM_MSIS,
                                   TT_IRQCHIP_NUM_PRIO_BITS);
 
-    riscv_aclint_mtimer_create(s->memory,
-            s->memmap[TT_ATL_ACLINT].base,
+    riscv_aclint_mtimer_create(ac->memory,
+            ac->memmap[TT_ATL_ACLINT].base,
             TT_ACLINT_MTIME_SIZE,
             0, hart_count,
             TT_ACLINT_MTIMECMP,
@@ -639,30 +685,22 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
      * The high address is where RAM lives. It is always present and may be
      * up to 64GB. The low address is an alias of the first 2GB of that RAM.
      */
-    if (ram_size > s->memmap[TT_ATL_DDR_HI].size) {
-        g_autofree char *sz = size_to_str(s->memmap[TT_ATL_DDR_HI].size);
+    if (ram_size > ac->memmap[TT_ATL_DDR_HI].size) {
+        g_autofree char *sz = size_to_str(ac->memmap[TT_ATL_DDR_HI].size);
         error_setg(errp, "RAM size is too large, maximum is %s", sz);
         return;
     }
 
     memory_region_init_alias(&s->ram_hi, OBJECT(s), "ram.high", s->dram,
                              0, ram_size);
-    memory_region_add_subregion(s->memory,
-                                s->memmap[TT_ATL_DDR_HI].base, &s->ram_hi);
+    memory_region_add_subregion(ac->memory,
+                                ac->memmap[TT_ATL_DDR_HI].base, &s->ram_hi);
 
-    lo_ram_size = MIN(ram_size, s->memmap[TT_ATL_DDR_LO].size);
+    lo_ram_size = MIN(ram_size, ac->memmap[TT_ATL_DDR_LO].size);
     memory_region_init_alias(&s->ram_lo, OBJECT(s), "ram.low", s->dram,
                              0, lo_ram_size);
-    memory_region_add_subregion(s->memory,
-                                s->memmap[TT_ATL_DDR_LO].base, &s->ram_lo);
-
-    /* Boot ROM */
-    if (!memory_region_init_rom(&s->bootrom, OBJECT(s), "tt-atlantis.bootrom",
-                                s->memmap[TT_ATL_BOOTROM].size, errp)) {
-        return;
-    }
-    memory_region_add_subregion(s->memory, s->memmap[TT_ATL_BOOTROM].base,
-                                &s->bootrom);
+    memory_region_add_subregion(ac->memory,
+                                ac->memmap[TT_ATL_DDR_LO].base, &s->ram_lo);
 
     /* 4 PRCM blocks */
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU]),
@@ -670,7 +708,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        s->memory, s->memmap[TT_ATL_PRCM_RCPU].base,
+        ac->memory, ac->memmap[TT_ATL_PRCM_RCPU].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU]), 0));
 
@@ -679,7 +717,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        s->memory, s->memmap[TT_ATL_PRCM_HSIO].base,
+        ac->memory, ac->memmap[TT_ATL_PRCM_HSIO].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_HSIO]), 0));
 
@@ -688,7 +726,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        s->memory, s->memmap[TT_ATL_PRCM_PCIE].base,
+        ac->memory, ac->memmap[TT_ATL_PRCM_PCIE].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_PCIE]), 0));
 
@@ -697,7 +735,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        s->memory, s->memmap[TT_ATL_PRCM_MM].base,
+        ac->memory, ac->memmap[TT_ATL_PRCM_MM].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_MM]), 0));
 
@@ -711,8 +749,8 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
             return;
         }
 
-        memory_region_add_subregion(s->memory,
-                                    s->memmap[TT_ATL_UART0 + i].base,
+        memory_region_add_subregion(ac->memory,
+                                    ac->memmap[TT_ATL_UART0 + i].base,
                                     sysbus_mmio_get_region(sbd, 0));
         sysbus_connect_irq(sbd, 0,
                            qdev_get_gpio_in(s->irqchip, TT_ATL_UART0_IRQ + i));
@@ -725,8 +763,8 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         if (!sysbus_realize(sbd, errp)) {
             return;
         }
-        memory_region_add_subregion(s->memory,
-                                    s->memmap[TT_ATL_I2C0 + i].base,
+        memory_region_add_subregion(ac->memory,
+                                    ac->memmap[TT_ATL_I2C0 + i].base,
                                     sysbus_mmio_get_region(sbd, 0));
         sysbus_connect_irq(sbd, 0,
                            qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i));
@@ -734,10 +772,6 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
 }
 
 static const Property tt_atlantis_soc_props[] = {
-    DEFINE_PROP_STRING("cpu-type", TTAtlantisSoCState, cpu_type),
-    DEFINE_PROP_UINT32("num-harts", TTAtlantisSoCState, num_harts, 8),
-    DEFINE_PROP_LINK("memory", TTAtlantisSoCState, memory,
-                     TYPE_MEMORY_REGION, MemoryRegion *),
     DEFINE_PROP_LINK("dram", TTAtlantisSoCState, dram,
                      TYPE_MEMORY_REGION, MemoryRegion *),
 };
@@ -756,6 +790,7 @@ static void tt_atlantis_machine_init(MachineState *machine)
 {
     TTAtlantisState *ams = TT_ATLANTIS_MACHINE(machine);
     TTAtlantisSoCState *s = &ams->soc;
+    TTAscalonClusterState *ac = &s->ascalon;
 
     memory_region_init(&ams->soc_memory, OBJECT(machine),
                        "tt-atlantis.soc-memory", UINT64_MAX);
@@ -763,12 +798,16 @@ static void tt_atlantis_machine_init(MachineState *machine)
 
     object_initialize_child(OBJECT(machine), "soc", &ams->soc,
                             TYPE_TT_ATLANTIS_SOC);
-    object_property_set_str(OBJECT(&ams->soc), "cpu-type", machine->cpu_type,
+
+    /* User input machine properties, cpu_type and smp.cpus apply to Ascalon */
+    object_property_set_str(OBJECT(ac), "cpu-type",
+                            machine->cpu_type,
                             &error_abort);
-    object_property_set_int(OBJECT(&ams->soc), "num-harts", machine->smp.cpus,
+    object_property_set_int(OBJECT(ac), "num-harts",
+                            machine->smp.cpus,
                             &error_abort);
 
-    object_property_set_link(OBJECT(&ams->soc), "memory",
+    object_property_set_link(OBJECT(ac), "memory",
                              OBJECT(&ams->soc_memory), &error_abort);
     object_property_set_link(OBJECT(&ams->soc), "dram", OBJECT(machine->ram),
                              &error_abort);
@@ -808,6 +847,12 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
 
 static const TypeInfo tt_atlantis_types[] = {
     {
+        .name       = TYPE_TT_ASCALON_CLUSTER,
+        .parent     = TYPE_DEVICE,
+        .instance_size = sizeof(TTAscalonClusterState),
+        .instance_init = tt_ascalon_cluster_init,
+        .class_init = tt_ascalon_cluster_class_init,
+    }, {
         .name       = TYPE_TT_ATLANTIS_SOC,
         .parent     = TYPE_DEVICE,
         .instance_size = sizeof(TTAtlantisSoCState),
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index f965bf23fc..4e3d5d4b5f 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -23,11 +23,14 @@ OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE)
 #define TYPE_TT_ATLANTIS_SOC "tt-atlantis-soc"
 OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisSoCState, TT_ATLANTIS_SOC)
 
+#define TYPE_TT_ASCALON_CLUSTER "riscv.tt-ascalon-cluster"
+OBJECT_DECLARE_SIMPLE_TYPE(TTAscalonClusterState, TT_ASCALON_CLUSTER)
+
 #define TT_ATL_NUM_I2C 5
 #define TT_ATL_NUM_PRCM 4
 #define TT_ATL_NUM_UARTS 5
 
-struct TTAtlantisSoCState {
+struct TTAscalonClusterState {
     /*< private >*/
     DeviceState parent;
 
@@ -35,19 +38,29 @@ struct TTAtlantisSoCState {
     const MemMapEntry *memmap;
 
     MemoryRegion *memory;
+
+    RISCVHartArrayState cpus;
+    MemoryRegion bootrom;
+
+    uint32_t num_harts;
+    char *cpu_type;
+};
+
+struct TTAtlantisSoCState {
+    /*< private >*/
+    DeviceState parent;
+
+    /*< public >*/
+    TTAscalonClusterState ascalon;
+
     MemoryRegion *dram;
     MemoryRegion ram_hi;
     MemoryRegion ram_lo;
 
-    RISCVHartArrayState cpus;
     DeviceState *irqchip;
     DesignWareI2CState i2c[TT_ATL_NUM_I2C];
     DW8250State uart[TT_ATL_NUM_UARTS];
-    MemoryRegion bootrom;
     TTAtlantisPRCMState prcm[TT_ATL_NUM_PRCM];
-
-    uint32_t num_harts;
-    char *cpu_type;
 };
 
 struct TTAtlantisState {
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 04/12] hw/riscv/atlantis: Store Ascalon memory in cluster struct
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (2 preceding siblings ...)
  2026-10-15  3:17 ` [PATCH 03/12] hw/riscv/atlantis: Move Ascalon into cluster struct Portia Stephens
@ 2026-10-15  3:17 ` Portia Stephens
  2026-10-15  3:17 ` [PATCH 05/12] hw/riscv/atlantis: Create CPU cluster for Atlantis cpus Portia Stephens
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

The system memory is the memory view of the Ascalon CPU. Store the
memory directly in the TTAscalonClusterState and set it to
system_memory. There is no need to track it at the machine level,
get_system_memory() can be used when needed.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/tt_atlantis.c         | 43 ++++++++++++++--------------------
 include/hw/riscv/tt_atlantis.h |  3 +--
 2 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index 8f217088c6..f087fd16e3 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -563,18 +563,19 @@ static void tt_ascalon_cluster_realize(DeviceState *dev, Error **errp)
 {
     TTAscalonClusterState *ac = TT_ASCALON_CLUSTER(dev);
 
-    if (!ac->memory) {
-        error_setg(errp, "'memory' link is not set");
-        return;
-    }
     ac->memmap = tt_atlantis_memmap;
 
+    /* Ascalon's memory view is system memory */
+    memory_region_init(&ac->memory, OBJECT(ac),
+                       "tt-ascalon-memory", UINT64_MAX);
+    memory_region_add_subregion(get_system_memory(), 0, &ac->memory);
+
     /* Boot ROM */
     if (!memory_region_init_rom(&ac->bootrom, OBJECT(ac), "tt-atlantis.bootrom",
                                 ac->memmap[TT_ATL_BOOTROM].size, errp)) {
         return;
     }
-    memory_region_add_subregion(ac->memory, ac->memmap[TT_ATL_BOOTROM].base,
+    memory_region_add_subregion(&ac->memory, ac->memmap[TT_ATL_BOOTROM].base,
                                 &ac->bootrom);
 
     /* CPUs */
@@ -588,7 +589,7 @@ static void tt_ascalon_cluster_realize(DeviceState *dev, Error **errp)
                             ac->memmap[TT_ATL_BOOTROM].base,
                             &error_abort);
     object_property_set_link(OBJECT(&ac->cpus), "memory",
-                             OBJECT(ac->memory),
+                             OBJECT(&ac->memory),
                              &error_abort);
     if (!sysbus_realize(SYS_BUS_DEVICE(&ac->cpus), errp)) {
         return;
@@ -598,8 +599,6 @@ static void tt_ascalon_cluster_realize(DeviceState *dev, Error **errp)
 static const Property tt_ascalon_cluster_props[] = {
     DEFINE_PROP_STRING("cpu-type", TTAscalonClusterState, cpu_type),
     DEFINE_PROP_UINT32("num-harts", TTAscalonClusterState, num_harts, 8),
-    DEFINE_PROP_LINK("memory", TTAscalonClusterState, memory,
-                     TYPE_MEMORY_REGION, MemoryRegion *),
 };
 
 static void tt_ascalon_cluster_class_init(ObjectClass *oc, const void *data)
@@ -658,7 +657,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
 
     ram_size = memory_region_size(s->dram);
 
-    s->irqchip = riscv_create_aia(ac->memory,
+    s->irqchip = riscv_create_aia(&ac->memory,
                                   true, TT_IMSIC_GUESTS,
                                   TT_IMSIC_STRIDE,
                                   TT_IMSIC_STRIDE,
@@ -671,7 +670,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
                                   TT_IRQCHIP_NUM_MSIS,
                                   TT_IRQCHIP_NUM_PRIO_BITS);
 
-    riscv_aclint_mtimer_create(ac->memory,
+    riscv_aclint_mtimer_create(&ac->memory,
             ac->memmap[TT_ATL_ACLINT].base,
             TT_ACLINT_MTIME_SIZE,
             0, hart_count,
@@ -693,13 +692,13 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
 
     memory_region_init_alias(&s->ram_hi, OBJECT(s), "ram.high", s->dram,
                              0, ram_size);
-    memory_region_add_subregion(ac->memory,
+    memory_region_add_subregion(&ac->memory,
                                 ac->memmap[TT_ATL_DDR_HI].base, &s->ram_hi);
 
     lo_ram_size = MIN(ram_size, ac->memmap[TT_ATL_DDR_LO].size);
     memory_region_init_alias(&s->ram_lo, OBJECT(s), "ram.low", s->dram,
                              0, lo_ram_size);
-    memory_region_add_subregion(ac->memory,
+    memory_region_add_subregion(&ac->memory,
                                 ac->memmap[TT_ATL_DDR_LO].base, &s->ram_lo);
 
     /* 4 PRCM blocks */
@@ -708,7 +707,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        ac->memory, ac->memmap[TT_ATL_PRCM_RCPU].base,
+        &ac->memory, ac->memmap[TT_ATL_PRCM_RCPU].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU]), 0));
 
@@ -717,7 +716,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        ac->memory, ac->memmap[TT_ATL_PRCM_HSIO].base,
+        &ac->memory, ac->memmap[TT_ATL_PRCM_HSIO].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_HSIO]), 0));
 
@@ -726,7 +725,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        ac->memory, ac->memmap[TT_ATL_PRCM_PCIE].base,
+        &ac->memory, ac->memmap[TT_ATL_PRCM_PCIE].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_PCIE]), 0));
 
@@ -735,7 +734,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
     memory_region_add_subregion(
-        ac->memory, ac->memmap[TT_ATL_PRCM_MM].base,
+        &ac->memory, ac->memmap[TT_ATL_PRCM_MM].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_MM]), 0));
 
@@ -749,7 +748,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
             return;
         }
 
-        memory_region_add_subregion(ac->memory,
+        memory_region_add_subregion(&ac->memory,
                                     ac->memmap[TT_ATL_UART0 + i].base,
                                     sysbus_mmio_get_region(sbd, 0));
         sysbus_connect_irq(sbd, 0,
@@ -763,7 +762,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         if (!sysbus_realize(sbd, errp)) {
             return;
         }
-        memory_region_add_subregion(ac->memory,
+        memory_region_add_subregion(&ac->memory,
                                     ac->memmap[TT_ATL_I2C0 + i].base,
                                     sysbus_mmio_get_region(sbd, 0));
         sysbus_connect_irq(sbd, 0,
@@ -792,10 +791,6 @@ static void tt_atlantis_machine_init(MachineState *machine)
     TTAtlantisSoCState *s = &ams->soc;
     TTAscalonClusterState *ac = &s->ascalon;
 
-    memory_region_init(&ams->soc_memory, OBJECT(machine),
-                       "tt-atlantis.soc-memory", UINT64_MAX);
-    memory_region_add_subregion(get_system_memory(), 0, &ams->soc_memory);
-
     object_initialize_child(OBJECT(machine), "soc", &ams->soc,
                             TYPE_TT_ATLANTIS_SOC);
 
@@ -807,9 +802,7 @@ static void tt_atlantis_machine_init(MachineState *machine)
                             machine->smp.cpus,
                             &error_abort);
 
-    object_property_set_link(OBJECT(ac), "memory",
-                             OBJECT(&ams->soc_memory), &error_abort);
-    object_property_set_link(OBJECT(&ams->soc), "dram", OBJECT(machine->ram),
+    object_property_set_link(OBJECT(s), "dram", OBJECT(machine->ram),
                              &error_abort);
 
     qdev_realize(DEVICE(&ams->soc), NULL, &error_fatal);
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index 4e3d5d4b5f..52b44504ae 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -37,7 +37,7 @@ struct TTAscalonClusterState {
     /*< public >*/
     const MemMapEntry *memmap;
 
-    MemoryRegion *memory;
+    MemoryRegion memory;
 
     RISCVHartArrayState cpus;
     MemoryRegion bootrom;
@@ -70,7 +70,6 @@ struct TTAtlantisState {
     /*< public >*/
     Notifier machine_done;
 
-    MemoryRegion soc_memory;
     TTAtlantisSoCState soc;
 };
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 05/12] hw/riscv/atlantis: Create CPU cluster for Atlantis cpus
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (3 preceding siblings ...)
  2026-10-15  3:17 ` [PATCH 04/12] hw/riscv/atlantis: Store Ascalon memory in " Portia Stephens
@ 2026-10-15  3:17 ` Portia Stephens
  2026-10-15  3:17 ` [PATCH 06/12] target/riscv: Add Tenstorrent Real-time CPU Portia Stephens
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

In preparation for adding the RCPU, put the Ascalon harts in a
CPUCluster. This will allow gdb to properly handle CPUs of different
types.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/Kconfig               |  1 +
 hw/riscv/tt_atlantis.c         | 13 ++++++++++++-
 include/hw/riscv/tt_atlantis.h |  2 ++
 3 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index d89fdfcc86..4eccfa443e 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -149,6 +149,7 @@ config TENSTORRENT
     select DESIGNWARE_I2C
     select DS1338
     select TMP105
+    select CPU_CLUSTER
 
 config XIANGSHAN_KUNMINGHU
     bool
diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index f087fd16e3..5a2665b211 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -50,6 +50,8 @@ QEMU_BUILD_BUG_ON(TT_IMSIC_STRIDE != (0x1000 << TT_IMSIC_GUEST_BITS));
 #define TT_ACLINT_MTIMECMP      0x8000
 #define TT_ACLINT_TIMEBASE_FREQ 1000000000
 
+#define ASCALON_CLUSTER_ID     0
+
 static const MemMapEntry tt_atlantis_memmap[] = {
     /* Keep sorted with :'<,'>!sort -g -k 4 */
     [TT_ATL_DDR_LO] =           { 0x00000000,    0x80000000 },
@@ -556,7 +558,10 @@ static void tt_ascalon_cluster_init(Object *obj)
 {
     TTAscalonClusterState *ac = TT_ASCALON_CLUSTER(obj);
 
-    object_initialize_child(obj, "cpus", &ac->cpus, TYPE_RISCV_HART_ARRAY);
+    object_initialize_child(obj, "tt-ascalon.cluster", &ac->cluster,
+                            TYPE_CPU_CLUSTER);
+    object_initialize_child(OBJECT(&ac->cluster), "cpus", &ac->cpus,
+                            TYPE_RISCV_HART_ARRAY);
 }
 
 static void tt_ascalon_cluster_realize(DeviceState *dev, Error **errp)
@@ -594,6 +599,12 @@ static void tt_ascalon_cluster_realize(DeviceState *dev, Error **errp)
     if (!sysbus_realize(SYS_BUS_DEVICE(&ac->cpus), errp)) {
         return;
     }
+
+    qdev_prop_set_uint32(DEVICE(&ac->cluster), "cluster-id", ASCALON_CLUSTER_ID);
+    /* cluster must be realized after the hart array */
+    if (!qdev_realize(DEVICE(&ac->cluster), NULL, errp)) {
+        return;
+    }
 }
 
 static const Property tt_ascalon_cluster_props[] = {
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index 52b44504ae..e5fce1f675 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -15,6 +15,7 @@
 #include "hw/intc/riscv_imsic.h"
 #include "hw/misc/tt_atlantis_prcm.h"
 #include "hw/char/dw8250.h"
+#include "hw/cpu/cluster.h"
 #include "hw/riscv/riscv_hart.h"
 
 #define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
@@ -40,6 +41,7 @@ struct TTAscalonClusterState {
     MemoryRegion memory;
 
     RISCVHartArrayState cpus;
+    CPUClusterState cluster;
     MemoryRegion bootrom;
 
     uint32_t num_harts;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 06/12] target/riscv: Add Tenstorrent Real-time CPU
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (4 preceding siblings ...)
  2026-10-15  3:17 ` [PATCH 05/12] hw/riscv/atlantis: Create CPU cluster for Atlantis cpus Portia Stephens
@ 2026-10-15  3:17 ` Portia Stephens
  2026-10-15  3:17 ` [PATCH 07/12] machine: Support extra board CPUs outside -smp Portia Stephens
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

The RCPU is a high-performance 32-bit RISC-V embedded processor featuring
a 6-stage, dual-issue superscalar pipeline, with tightly-coupled memories
(TCMs), advanced interrupt controller (CLIC), and comprehensive
debug/trace capabilities. It is designed for real-time, system
management, and safety-critical applications requiring deterministic
execution and low interrupt latency. See the Atlantis Datasheet, section
2.2 for more information [1].

The Atlantis SoC includes an RCPU used for boot, low-level control, and
ongoing system management [1]. This adds a cpu entry for the RCPU
and implementation specific CSRs. The CSRs are stubbed to allow code to
run without exceptions but does not implement their functionality.

[1] https://github.com/tenstorrent/atlantis-soc-docs/blob/main/data_sheet/atl_soc_data_sheet.pdf

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 MAINTAINERS                  |  1 +
 target/riscv/cpu-qom.h       |  1 +
 target/riscv/cpu.c           | 28 +++++++++++++++++
 target/riscv/tcg/csr.h       |  3 ++
 target/riscv/tcg/meson.build |  1 +
 target/riscv/tcg/tt_csr.c    | 58 ++++++++++++++++++++++++++++++++++++
 6 files changed, 92 insertions(+)
 create mode 100644 target/riscv/tcg/tt_csr.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 8cc426451c..b5b019b3e8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1847,6 +1847,7 @@ R: Portia Stephens <portias@oss.tenstorrent.com>
 L: qemu-riscv@nongnu.org
 S: Supported
 F: docs/system/riscv/tt_*.rst
+F: target/riscv/tcg/tt_*.c
 F: hw/misc/tt_*.c
 F: hw/riscv/tt_*.c
 F: include/hw/misc/tt_*.h
diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h
index 61234842e3..72570ed47e 100644
--- a/target/riscv/cpu-qom.h
+++ b/target/riscv/cpu-qom.h
@@ -57,6 +57,7 @@
 #define TYPE_RISCV_CPU_THEAD_C908V      RISCV_CPU_TYPE_NAME("thead-c908v")
 #define TYPE_RISCV_CPU_VEYRON_V1        RISCV_CPU_TYPE_NAME("veyron-v1")
 #define TYPE_RISCV_CPU_TT_ASCALON       RISCV_CPU_TYPE_NAME("tt-ascalon")
+#define TYPE_RISCV_CPU_TT_RCPU          RISCV_CPU_TYPE_NAME("tt-rcpu")
 #define TYPE_RISCV_CPU_XIANGSHAN_NANHU  RISCV_CPU_TYPE_NAME("xiangshan-nanhu")
 #define TYPE_RISCV_CPU_XIANGSHAN_KMH    RISCV_CPU_TYPE_NAME("xiangshan-kunminghu")
 #define TYPE_RISCV_CPU_MIPS_P8700       RISCV_CPU_TYPE_NAME("mips-p8700")
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index a682350d28..fdedf16ebe 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -3455,6 +3455,34 @@ static const TypeInfo riscv_cpu_type_infos[] = {
         .cfg.pmp_regions = 8
     ),
 
+#if defined(TARGET_RISCV32) || \
+    (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY))
+    DEFINE_RISCV_CPU(TYPE_RISCV_CPU_TT_RCPU, TYPE_RISCV_VENDOR_CPU,
+        .misa_mxl_max = MXL_RV32,
+        .misa_ext = RVI | RVM | RVA | RVB | RVC | RVD | RVF | RVU,
+        .priv_spec = PRIV_VERSION_1_13_0,
+
+        .cfg.mmu = false,
+        .cfg.ext_zbc = true,
+
+        .cfg.ext_zba = true,
+        .cfg.ext_zbb = true,
+        .cfg.ext_zbs = true,
+        .cfg.ext_zca = true,
+        .cfg.ext_zcb = true,
+        .cfg.ext_zifencei = true,
+        .cfg.ext_zicsr = true,
+        .cfg.ext_zicbom = true,
+        .cfg.ext_zicntr = true,
+        .cfg.ext_zihpm = true,
+        .cfg.ext_zicond = true,
+        .cfg.pmp = true,
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+        .custom_csrs = tt_csr_list,
+#endif
+    ),
+#endif
+
 #if defined(TARGET_RISCV32) || \
     (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY))
     DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE32, TYPE_RISCV_DYNAMIC_CPU,
diff --git a/target/riscv/tcg/csr.h b/target/riscv/tcg/csr.h
index 53ba3b4a4d..4bc55b70d6 100644
--- a/target/riscv/tcg/csr.h
+++ b/target/riscv/tcg/csr.h
@@ -87,6 +87,9 @@ extern const RISCVCSR th_csr_list[];
 /* Implemented in mips_csr.c */
 extern const RISCVCSR mips_csr_list[];
 
+/* In tt_csr.c */
+extern const RISCVCSR tt_csr_list[];
+
 /* PMP CSRs, defined in pmp.c */
 void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index,
                       target_ulong val);
diff --git a/target/riscv/tcg/meson.build b/target/riscv/tcg/meson.build
index 86805b30cb..77357475c3 100644
--- a/target/riscv/tcg/meson.build
+++ b/target/riscv/tcg/meson.build
@@ -25,6 +25,7 @@ riscv_system_ss.add(files(
   'pmp.c',
   'pmu.c',
   'th_csr.c',
+  'tt_csr.c',
 ))
 
 
diff --git a/target/riscv/tcg/tt_csr.c b/target/riscv/tcg/tt_csr.c
new file mode 100644
index 0000000000..c76cd1a9cb
--- /dev/null
+++ b/target/riscv/tcg/tt_csr.c
@@ -0,0 +1,58 @@
+/*
+ * Tenstorrent CPU implementation specifics.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright 2026 Tenstorrent
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "target/riscv/tcg/csr.h"
+
+/* RCPU custom CSRs */
+#define RCPU_CSR_MICACHE          0x7c0
+#define RCPU_CSR_MDCACHE          0x7c1
+#define RCPU_CSR_MITCMCR          0x7c5
+#define RCPU_CSR_MDTCMCR          0x7c6
+#define RCPU_CSR_MHCR             0x7c7
+#define RCPU_CSR_MEXINFO          0x7ca
+#define RCPU_CSR_MNMIE            0xbc6
+
+/* predicate for an RCPU core */
+static RISCVException any(CPURISCVState *env, int csrno)
+{
+    return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_zero(CPURISCVState *env, int csrno,
+                                target_ulong *val)
+{
+    *val = 0;
+
+    return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_none(CPURISCVState *env, int csrno,
+                                 target_ulong val, uintptr_t ra)
+{
+    return RISCV_EXCP_NONE;
+}
+
+const RISCVCSR tt_csr_list[] = {
+    { .csrno = RCPU_CSR_MICACHE,
+      .csr_ops = { "micache", any, read_zero, write_none} },
+    { .csrno = RCPU_CSR_MDCACHE,
+      .csr_ops = { "mdcache", any, read_zero, write_none} },
+    { .csrno = RCPU_CSR_MITCMCR,
+      .csr_ops = { "mitcmcr", any, read_zero, write_none} },
+    { .csrno = RCPU_CSR_MDTCMCR,
+      .csr_ops = { "mdtcmcr", any, read_zero, write_none} },
+    { .csrno = RCPU_CSR_MHCR,
+      .csr_ops = { "mhcr", any, read_zero, write_none} },
+    { .csrno = RCPU_CSR_MEXINFO,
+      .csr_ops = { "mexinfo", any, read_zero, write_none} },
+    { .csrno = RCPU_CSR_MNMIE,
+       .csr_ops = { "mnmie", any, read_zero, write_none} },
+    { },
+};
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 07/12] machine: Support extra board CPUs outside -smp
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (5 preceding siblings ...)
  2026-10-15  3:17 ` [PATCH 06/12] target/riscv: Add Tenstorrent Real-time CPU Portia Stephens
@ 2026-10-15  3:17 ` Portia Stephens
  2026-10-15 20:31   ` Daniel Henrique Barboza
  2026-10-15  3:18 ` [PATCH 08/12] hw/riscv/atlantis: Add the RCPU to the Atlantis SoC Portia Stephens
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

From: Joel Stanley <joel@jms.id.au>

Boards may instantiate one or more CPUs that are not part of the
user's -smp layout, for example an a management or boot core of a
different CPU model. As TCG sizes its vCPU context from smp.max_cpus
during init, creating more CPUState objects than smp.max_cpus trips the
assert in tcg_register_thread().

smp.max_cpus is finalized before the accelerator is initialised and the
board's init() runs later still, so a board's initialise cannot influence
the count TCG uses.

Add a MachineClass::extra_cpus that boards set at class init time to
declare these additional CPUs. TCG can then add it to max_threads. The
field defaults to 0, so existing machines are unaffected.

Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 accel/tcg/tcg-all.c      | 2 +-
 include/hw/core/boards.h | 4 ++++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c
index 767e880555..655609ebbb 100644
--- a/accel/tcg/tcg-all.c
+++ b/accel/tcg/tcg-all.c
@@ -126,7 +126,7 @@ static int tcg_init_machine(AccelState *as, MachineState *ms)
          */
         if (mttcg_supported && !icount_enabled()) {
             s->mttcg_enabled = ON_OFF_AUTO_ON;
-            max_threads = ms->smp.max_cpus;
+            max_threads = ms->smp.max_cpus + MACHINE_GET_CLASS(ms)->extra_cpus;
         } else {
             s->mttcg_enabled = ON_OFF_AUTO_OFF;
         }
diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h
index d0e4ad9820..575c6bcb31 100644
--- a/include/hw/core/boards.h
+++ b/include/hw/core/boards.h
@@ -189,6 +189,9 @@ typedef struct {
  * @max_cpus: maximum number of CPUs supported. Default: 1
  * @min_cpus: minimum number of CPUs supported. Default: 1
  * @default_cpus: number of CPUs instantiated if none are specified. Default: 1
+ * @extra_cpus: number of additional CPUs the board always instantiates beyond
+ *    the -smp topology (e.g. an always-present management core). Accelerators
+ *    add this to the per-vCPU state they allocate. Default: 0
  * @is_default:
  *    If true QEMU will use this machine by default if no '-M' option is given.
  * @get_hotplug_handler: this function is called during bus-less
@@ -287,6 +290,7 @@ struct MachineClass {
     int max_cpus;
     int min_cpus;
     int default_cpus;
+    int extra_cpus;
     unsigned int no_serial:1,
         no_parallel:1,
         no_floppy:1,
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 08/12] hw/riscv/atlantis: Add the RCPU to the Atlantis SoC
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (6 preceding siblings ...)
  2026-10-15  3:17 ` [PATCH 07/12] machine: Support extra board CPUs outside -smp Portia Stephens
@ 2026-10-15  3:18 ` Portia Stephens
  2026-10-15  3:18 ` [PATCH 09/12] hw/riscv/atlantis: Back RCPU memory region by unimplemented device Portia Stephens
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

The Atlantis SoC has a Realtime CPU, RV32 core. It is part of
the security subsystem and is responsible for booting the Ascalon
cluster. The RCPU has a different memory view from the Ascalon cores but
shares access to many peripherals.

This adds the RCPU to the Atlantis SoC with the minimum changes
to allow the bootrom to run and wait for the next stage
bootloader (FSBL) over UART. The RCPU has its own console (uart0),
separate from Ascalon which is exposed on serial_hd 1 on the command
line.

On hardware, the RCPU will be responsible for loading the Ascalon images
into DRAM and releasing the resets of the Ascalon cores. For now the
Ascalon cores are still bootstrapping their images directly into DRAM
via the command line arguments.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/tt_atlantis.c         | 245 ++++++++++++++++++++++++++++++++-
 include/hw/riscv/tt_atlantis.h |  43 ++++++
 2 files changed, 286 insertions(+), 2 deletions(-)

diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index 5a2665b211..e2bd1fce08 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -51,6 +51,7 @@ QEMU_BUILD_BUG_ON(TT_IMSIC_STRIDE != (0x1000 << TT_IMSIC_GUEST_BITS));
 #define TT_ACLINT_TIMEBASE_FREQ 1000000000
 
 #define ASCALON_CLUSTER_ID     0
+#define RCPU_CLUSTER_ID        1
 
 static const MemMapEntry tt_atlantis_memmap[] = {
     /* Keep sorted with :'<,'>!sort -g -k 4 */
@@ -78,6 +79,44 @@ static const MemMapEntry tt_atlantis_memmap[] = {
     [TT_ATL_DDR_HI] =          { 0x100000000,  0x1000000000 },
 };
 
+static const MemMapEntry tt_atlantis_rcpu_memmap[] = {
+    /* Keep sorted with :'<,'>!sort -g -k 4 */
+    [TT_ATL_BOOTROM] =           { 0x00040000,    0x40000 },
+    [TT_ATL_RCPU_SRAM] =         { 0x20100000,   0x100000 },
+    [TT_ATL_PRCM_RCPU] =         { 0x40000000,    0x10000 },
+    [TT_ATL_WDT0] =              { 0x40030000,    0x10000 },
+    [TT_ATL_WDT1] =              { 0x40040000,    0x10000 },
+    [TT_ATL_IO_CTRL] =           { 0x40060000,    0x10000 },
+    [TT_ATL_RCPU_MAILBOX] =      { 0x40100000,    0x10000 },
+    [TT_ATL_RCPU_SMN_FIREWALL] = { 0x41010000,    0x10000 },
+    [TT_ATL_NOCM_FIREWALL] =     { 0x41020000,    0x10000 },
+    [TT_ATL_UART0] =             { 0x48100000,    0x10000 },
+    [TT_ATL_UART1] =             { 0x48110000,    0x10000 },
+    [TT_ATL_UART2] =             { 0x48120000,    0x10000 },
+    [TT_ATL_UART3] =             { 0x48130000,    0x10000 },
+    [TT_ATL_UART4] =             { 0x48140000,    0x10000 },
+    [TT_ATL_GPIO] =              { 0x48150000,    0x10000 },
+    [TT_ATL_QSPI] =              { 0x4a000000,  0x2000000 },
+    [TT_ATL_OTP] =               { 0x50000000,   0x100000 },
+    [TT_ATL_PRCM_TRNG] =         { 0x50600000,    0x10000 },
+    [TT_ATL_SPACC] =             { 0x50800000,   0x100000 },
+    [TT_ATL_DDR_LO] =            { 0x60000000, 0x40000000 },
+    [TT_ATL_MIMSIC] =            { 0xa0000000,  0x8000000 },
+    [TT_ATL_PRCM_PCIE] =         { 0xa8000000,    0x10000 },
+    [TT_ATL_PRCM_MM] =           { 0xb0000000,   0x100000 },
+    [TT_ATL_SDHCI0] =            { 0xb4020000,    0x10000 },
+    [TT_ATL_SDHCI1] =            { 0xb4030000,    0x10000 },
+    [TT_ATL_PRCM_HSIO] =         { 0xb40c0000,    0x10000 },
+    [TT_ATL_PRCM_LPDDR1] =       { 0xb8000000,    0x10000 },
+    [TT_ATL_PRCM_LPDDR2] =       { 0xb8800000,    0x10000 },
+    [TT_ATL_PRCM_DDRC0] =        { 0xb9000000,   0x800000 },
+    [TT_ATL_PRCM_DDRC1] =        { 0xb9800000,   0x800000 },
+    [TT_ATL_MAPLIC] =            { 0xbc000000,  0x3000000 },
+    [TT_ATL_PRCM_ASC] =          { 0xbf000000,  0x1000000 },
+    [TT_ATL_RCPU_CLIC] =         { 0xe000e000,     0x2000 },
+    [TT_ATL_RCPU_CLINT] =        { 0xe0020000,     0xc000 },
+};
+
 static const char *const tt_atlantis_valid_cpu_types[] = {
     TYPE_RISCV_CPU_TT_ASCALON,
     NULL
@@ -554,6 +593,78 @@ static inline int tt_atlantis_uart_index(int uart_dev)
     return uart_dev - TT_ATL_UART0;
 }
 
+static void tt_rcpu_cluster_init(Object *obj)
+{
+    TTRCPUClusterState *rc = TT_RCPU_CLUSTER(obj);
+
+    object_initialize_child(obj, "tt-rcpu.cluster", &rc->cluster,
+                            TYPE_CPU_CLUSTER);
+    object_initialize_child(OBJECT(&rc->cluster), "cpus", &rc->cpus,
+                            TYPE_RISCV_HART_ARRAY);
+}
+
+static void tt_rcpu_cluster_realize(DeviceState *dev, Error **errp)
+{
+    TTRCPUClusterState *rc = TT_RCPU_CLUSTER(dev);
+
+    rc->memmap = tt_atlantis_rcpu_memmap;
+
+    /* The RCPU has its own memory view. Create it here before the CPU uses it */
+    memory_region_init(&rc->memory, OBJECT(rc), "tt-rcpu.memory",
+                       UINT32_MAX);
+
+    /* The RCPU's maskrom which is inaccessible outside of the RCPU */
+    if (!memory_region_init_rom(&rc->bootrom, OBJECT(rc),
+                                "tt-rcpu.bootrom",
+                                rc->memmap[TT_ATL_BOOTROM].size, errp)) {
+        return;
+    }
+    memory_region_add_subregion(&rc->memory,
+                                rc->memmap[TT_ATL_BOOTROM].base,
+                                &rc->bootrom);
+
+    /* RCPU SRAM */
+    if (!memory_region_init_ram(&rc->sram, OBJECT(rc),
+                                "tt-rcpu.sram",
+                                rc->memmap[TT_ATL_RCPU_SRAM].size, errp)) {
+        return;
+    }
+    memory_region_add_subregion(&rc->memory,
+                                rc->memmap[TT_ATL_RCPU_SRAM].base,
+                                &rc->sram);
+
+    /* CPUs */
+    object_property_set_str(OBJECT(&rc->cpus), "cpu-type", TYPE_RISCV_CPU_TT_RCPU,
+                            &error_abort);
+    object_property_set_int(OBJECT(&rc->cpus), "hartid-base", 0,
+                            &error_abort);
+    object_property_set_int(OBJECT(&rc->cpus), "num-harts", 1,
+                            &error_abort);
+    object_property_set_int(OBJECT(&rc->cpus), "resetvec",
+                            rc->memmap[TT_ATL_BOOTROM].base,
+                            &error_abort);
+    object_property_set_link(OBJECT(&rc->cpus), "memory",
+                             OBJECT(&rc->memory),
+                             &error_abort);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&rc->cpus), errp)) {
+        return;
+    }
+
+    qdev_prop_set_int32(DEVICE(&rc->cluster), "cluster-id", RCPU_CLUSTER_ID);
+    /* cluster must be realized after the hart array */
+    if (!qdev_realize(DEVICE(&rc->cluster), NULL, errp)) {
+        return;
+    }
+}
+
+static void tt_rcpu_cluster_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = tt_rcpu_cluster_realize;
+}
+
 static void tt_ascalon_cluster_init(Object *obj)
 {
     TTAscalonClusterState *ac = TT_ASCALON_CLUSTER(obj);
@@ -650,11 +761,48 @@ static void tt_atlantis_soc_init(Object *obj)
     }
 }
 
+/*
+ * Create and add a new alias to a memory container
+ * @obj
+ * @m MemoryRegion container for the alias to be added to
+ * @orig MemoryRegion original region to be aliased
+ *    Ascalon memory
+ * @size of the subregion
+ * @base address of the subregion
+ * @name for the alias being added to the RCPU memory
+ */
+static void create_alias(Object *obj, MemoryRegion *m,
+        MemoryRegion *orig, hwaddr size, hwaddr base, const char *name)
+{
+    MemoryRegion *alias = g_new(MemoryRegion, 1);
+
+    memory_region_init_alias(alias, obj, name, orig, 0,
+                             size);
+    memory_region_add_subregion(m, base, alias);
+}
+
+/*
+ * Alias a device to a memory container
+ * @obj
+ * @m MemoryRegion container for the alias to be added to
+ * @dev Device to be added.
+ * @size of the subregion
+ * @base address of the subregion
+ * @name for the alias being added to the RCPU memory
+ */
+static void create_device_alias(Object *obj, MemoryRegion *m, DeviceState *dev,
+        hwaddr size, hwaddr base, const char *name)
+{
+    MemoryRegion *orig = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+    create_alias(obj, m, orig, size, base, name);
+}
+
 static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
 {
     TTAtlantisSoCState *s = TT_ATLANTIS_SOC(dev);
     TTAscalonClusterState *ac = &s->ascalon;
-    ram_addr_t lo_ram_size, ram_size;
+    TTRCPUClusterState *rc = &s->rcpu;
+    ram_addr_t lo_ram_size, ram_size, rcpu_ram_size;
     int hart_count = ac->num_harts;
 
     if (!s->dram) {
@@ -666,6 +814,14 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         return;
     }
 
+    if (s->has_rcpu) {
+        /* Only create the RCPU if it is enabled */
+        object_initialize_child(OBJECT(s), "rcpu", &s->rcpu, TYPE_TT_RCPU_CLUSTER);
+        if (!qdev_realize(DEVICE(&s->rcpu), NULL, errp)) {
+            return;
+        }
+    }
+
     ram_size = memory_region_size(s->dram);
 
     s->irqchip = riscv_create_aia(&ac->memory,
@@ -711,6 +867,23 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
                              0, lo_ram_size);
     memory_region_add_subregion(&ac->memory,
                                 ac->memmap[TT_ATL_DDR_LO].base, &s->ram_lo);
+    if (s->has_rcpu) {
+        /*
+         * The RCPU has a 1G view of lower DDR. DDR low size can be configured by
+         * the command line parameters so pick the minimum between 1G and the user
+         * provided size.
+         */
+        rcpu_ram_size = MIN(rc->memmap[TT_ATL_DDR_LO].size, lo_ram_size);
+        create_alias(OBJECT(s), &rc->memory, &s->ram_lo,
+                     rcpu_ram_size,
+                     rc->memmap[TT_ATL_DDR_LO].base,
+                     "tt-rcpu.ram");
+
+        /* UART0, the RCPU console */
+        qdev_prop_set_chr(DEVICE(&s->uart[tt_atlantis_uart_index(TT_ATL_UART0)]),
+                          "chardev", serial_hd(1));
+    }
+
 
     /* 4 PRCM blocks */
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU]),
@@ -721,6 +894,13 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         &ac->memory, ac->memmap[TT_ATL_PRCM_RCPU].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU]), 0));
+    if (s->has_rcpu) {
+        create_device_alias(OBJECT(s), &rc->memory,
+                            DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU]),
+                            rc->memmap[TT_ATL_PRCM_RCPU].size,
+                            rc->memmap[TT_ATL_PRCM_RCPU].base,
+                            "tt-rcpu.rcpu-prcm");
+    }
 
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_HSIO]),
                         errp)) {
@@ -730,6 +910,13 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         &ac->memory, ac->memmap[TT_ATL_PRCM_HSIO].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_HSIO]), 0));
+    if (s->has_rcpu) {
+        create_device_alias(OBJECT(s), &rc->memory,
+                            DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_HSIO]),
+                            rc->memmap[TT_ATL_PRCM_HSIO].size,
+                            rc->memmap[TT_ATL_PRCM_HSIO].base,
+                            "tt-rcpu.rcpu-hsio");
+    }
 
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_PCIE]),
                         errp)) {
@@ -739,6 +926,13 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         &ac->memory, ac->memmap[TT_ATL_PRCM_PCIE].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_PCIE]), 0));
+    if (s->has_rcpu) {
+        create_device_alias(OBJECT(s), &rc->memory,
+                            DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_PCIE]),
+                            rc->memmap[TT_ATL_PRCM_PCIE].size,
+                            rc->memmap[TT_ATL_PRCM_PCIE].base,
+                            "tt-rcpu.rcpu-pcie");
+    }
 
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_MM]),
                         errp)) {
@@ -748,8 +942,15 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
         &ac->memory, ac->memmap[TT_ATL_PRCM_MM].base,
         sysbus_mmio_get_region(
             SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_MM]), 0));
+    if (s->has_rcpu) {
+        create_device_alias(OBJECT(s), &rc->memory,
+                            DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_MM]),
+                            rc->memmap[TT_ATL_PRCM_MM].size,
+                            rc->memmap[TT_ATL_PRCM_MM].base,
+                            "tt-rcpu.rcpu-mm");
+    }
 
-    /* UART1, the soc console (UART0 is for the boot microcontroller) */
+    /* UART1, the soc console */
     qdev_prop_set_chr(DEVICE(&s->uart[tt_atlantis_uart_index(TT_ATL_UART1)]),
                       "chardev", serial_hd(0));
 
@@ -764,6 +965,16 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
                                     sysbus_mmio_get_region(sbd, 0));
         sysbus_connect_irq(sbd, 0,
                            qdev_get_gpio_in(s->irqchip, TT_ATL_UART0_IRQ + i));
+
+        if (s->has_rcpu) {
+            /* Alias the devices to the rcpu memory view */
+            g_autofree char *name = g_strdup_printf("tt-rcpu.uart%d", i);
+
+            create_device_alias(OBJECT(s), &rc->memory, DEVICE(&s->uart[i]),
+                                rc->memmap[TT_ATL_UART0 + i].size,
+                                rc->memmap[TT_ATL_UART0 + i].base,
+                                name);
+        }
     }
 
     /* I2C */
@@ -784,6 +995,7 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
 static const Property tt_atlantis_soc_props[] = {
     DEFINE_PROP_LINK("dram", TTAtlantisSoCState, dram,
                      TYPE_MEMORY_REGION, MemoryRegion *),
+    DEFINE_PROP_BOOL("has-rcpu", TTAtlantisSoCState, has_rcpu, false)
 };
 
 static void tt_atlantis_soc_class_init(ObjectClass *oc, const void *data)
@@ -815,6 +1027,8 @@ static void tt_atlantis_machine_init(MachineState *machine)
 
     object_property_set_link(OBJECT(s), "dram", OBJECT(machine->ram),
                              &error_abort);
+    object_property_set_bool(OBJECT(&ams->soc), "has-rcpu", ams->has_rcpu,
+                             &error_abort);
 
     qdev_realize(DEVICE(&ams->soc), NULL, &error_fatal);
 
@@ -833,6 +1047,20 @@ static void tt_atlantis_machine_init(MachineState *machine)
     qemu_add_machine_init_done_notifier(&ams->machine_done);
 }
 
+static bool tt_atlantis_machine_get_rcpu(Object *obj, Error **errp)
+{
+    TTAtlantisState *s = TT_ATLANTIS_MACHINE(obj);
+
+    return s->has_rcpu;
+}
+
+static void tt_atlantis_machine_set_rcpu(Object *obj, bool value, Error **errp)
+{
+    TTAtlantisState *s = TT_ATLANTIS_MACHINE(obj);
+
+    s->has_rcpu = value;
+}
+
 static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
@@ -841,12 +1069,19 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
     mc->init = tt_atlantis_machine_init;
     mc->max_cpus = 8;
     mc->default_cpus = 8;
+    mc->extra_cpus = 1; /* RCPU */
     mc->default_ram_size = 4 * GiB;
     mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
     mc->valid_cpu_types = tt_atlantis_valid_cpu_types;
     mc->block_default_type = IF_VIRTIO;
     mc->no_cdrom = 1;
     mc->default_ram_id = "tt_atlantis.ram";
+
+    object_class_property_add_bool(oc, "rcpu", tt_atlantis_machine_get_rcpu,
+                                   tt_atlantis_machine_set_rcpu);
+    object_class_property_set_description(oc, "rcpu",
+                                          "Set to enable/disable modeling the RCPU.");
+
 }
 
 static const TypeInfo tt_atlantis_types[] = {
@@ -856,6 +1091,12 @@ static const TypeInfo tt_atlantis_types[] = {
         .instance_size = sizeof(TTAscalonClusterState),
         .instance_init = tt_ascalon_cluster_init,
         .class_init = tt_ascalon_cluster_class_init,
+    }, {
+        .name       = TYPE_TT_RCPU_CLUSTER,
+        .parent     = TYPE_DEVICE,
+        .instance_size = sizeof(TTRCPUClusterState),
+        .instance_init = tt_rcpu_cluster_init,
+        .class_init = tt_rcpu_cluster_class_init,
     }, {
         .name       = TYPE_TT_ATLANTIS_SOC,
         .parent     = TYPE_DEVICE,
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index e5fce1f675..4f79ef5af5 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -27,6 +27,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisSoCState, TT_ATLANTIS_SOC)
 #define TYPE_TT_ASCALON_CLUSTER "riscv.tt-ascalon-cluster"
 OBJECT_DECLARE_SIMPLE_TYPE(TTAscalonClusterState, TT_ASCALON_CLUSTER)
 
+#define TYPE_TT_RCPU_CLUSTER "riscv.tt-rcpu-cluster"
+OBJECT_DECLARE_SIMPLE_TYPE(TTRCPUClusterState, TT_RCPU_CLUSTER)
+
 #define TT_ATL_NUM_I2C 5
 #define TT_ATL_NUM_PRCM 4
 #define TT_ATL_NUM_UARTS 5
@@ -48,12 +51,27 @@ struct TTAscalonClusterState {
     char *cpu_type;
 };
 
+struct TTRCPUClusterState {
+    /*< private >*/
+    DeviceState parent;
+
+    /*< public >*/
+    const MemMapEntry *memmap;
+    MemoryRegion memory;
+
+    RISCVHartArrayState cpus;
+    CPUClusterState cluster;
+    MemoryRegion bootrom;
+    MemoryRegion sram;
+};
+
 struct TTAtlantisSoCState {
     /*< private >*/
     DeviceState parent;
 
     /*< public >*/
     TTAscalonClusterState ascalon;
+    TTRCPUClusterState rcpu;
 
     MemoryRegion *dram;
     MemoryRegion ram_hi;
@@ -63,6 +81,8 @@ struct TTAtlantisSoCState {
     DesignWareI2CState i2c[TT_ATL_NUM_I2C];
     DW8250State uart[TT_ATL_NUM_UARTS];
     TTAtlantisPRCMState prcm[TT_ATL_NUM_PRCM];
+
+    bool has_rcpu;              /* Enable RCPU */
 };
 
 struct TTAtlantisState {
@@ -73,6 +93,8 @@ struct TTAtlantisState {
     Notifier machine_done;
 
     TTAtlantisSoCState soc;
+
+    bool has_rcpu;              /* Enable RCPU */
 };
 
 enum {
@@ -93,15 +115,28 @@ enum {
     TT_ATL_BOOTROM,
     TT_ATL_DDR_LO,
     TT_ATL_DDR_HI,
+    TT_ATL_GPIO,
     TT_ATL_I2C0,
     TT_ATL_I2C1,
     TT_ATL_I2C2,
     TT_ATL_I2C3,
     TT_ATL_I2C4,
+    TT_ATL_IO_CTRL,
     TT_ATL_MAPLIC,
     TT_ATL_MIMSIC,
+    TT_ATL_NOCM_FIREWALL,
+    TT_ATL_OTP,
+    TT_ATL_QSPI,
+    TT_ATL_RCPU_CLIC,
+    TT_ATL_RCPU_CLINT,
+    TT_ATL_RCPU_MAILBOX,
+    TT_ATL_RCPU_SMN_FIREWALL,
+    TT_ATL_RCPU_SRAM,
     TT_ATL_SAPLIC,
+    TT_ATL_SDHCI0,
+    TT_ATL_SDHCI1,
     TT_ATL_SIMSIC,
+    TT_ATL_SPACC,
     TT_ATL_UART0,
     TT_ATL_UART1,
     TT_ATL_UART2,
@@ -111,6 +146,14 @@ enum {
     TT_ATL_PRCM_HSIO,
     TT_ATL_PRCM_PCIE,
     TT_ATL_PRCM_MM,
+    TT_ATL_PRCM_DDRC0,
+    TT_ATL_PRCM_DDRC1,
+    TT_ATL_PRCM_ASC,
+    TT_ATL_PRCM_LPDDR1,
+    TT_ATL_PRCM_LPDDR2,
+    TT_ATL_PRCM_TRNG,
+    TT_ATL_WDT0,
+    TT_ATL_WDT1,
 };
 
 /*
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 09/12] hw/riscv/atlantis: Back RCPU memory region by unimplemented device
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (7 preceding siblings ...)
  2026-10-15  3:18 ` [PATCH 08/12] hw/riscv/atlantis: Add the RCPU to the Atlantis SoC Portia Stephens
@ 2026-10-15  3:18 ` Portia Stephens
  2026-10-15  3:18 ` [PATCH 10/12] hw/riscv/atlantis: Load the RCPU bootrom Portia Stephens
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

Most devices on the RCPU memmap are not modeled yet in QEMU. Back the
entire 4G memory view of the RCPU by a low priority unimplemented
device. This prevents accesses to non-modeled device from faulting.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/Kconfig               |  1 +
 hw/riscv/tt_atlantis.c         | 20 ++++++++++++++++++++
 include/hw/riscv/tt_atlantis.h |  6 ++++++
 3 files changed, 27 insertions(+)

diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 4eccfa443e..8e1812923a 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -150,6 +150,7 @@ config TENSTORRENT
     select DS1338
     select TMP105
     select CPU_CLUSTER
+    select UNIMP
 
 config XIANGSHAN_KUNMINGHU
     bool
diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index e2bd1fce08..635f85e44b 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -524,6 +524,17 @@ static void load_fdt(TTAtlantisState *ams)
     create_fdt_memory(ms->fdt, ac->memmap, ms->ram_size);
 }
 
+static void mmio_map_unimplemented(MemoryRegion *memory, SysBusDevice *dev,
+                                   const char *name, hwaddr addr, uint64_t size)
+{
+    qdev_prop_set_string(DEVICE(dev), "name", name);
+    qdev_prop_set_uint64(DEVICE(dev), "size", size);
+    sysbus_realize(dev, &error_abort);
+
+    memory_region_add_subregion_overlap(memory, addr,
+                                        sysbus_mmio_get_region(dev, 0), -1000);
+}
+
 static void tt_atlantis_machine_done(Notifier *n, void *data)
 {
     TTAtlantisState *ams = container_of(n, TTAtlantisState, machine_done);
@@ -601,6 +612,8 @@ static void tt_rcpu_cluster_init(Object *obj)
                             TYPE_CPU_CLUSTER);
     object_initialize_child(OBJECT(&rc->cluster), "cpus", &rc->cpus,
                             TYPE_RISCV_HART_ARRAY);
+    object_initialize_child(OBJECT(rc), "tt-rcpu.unimp-mem", &rc->unimp_mem,
+                            TYPE_UNIMPLEMENTED_DEVICE);
 }
 
 static void tt_rcpu_cluster_realize(DeviceState *dev, Error **errp)
@@ -656,6 +669,13 @@ static void tt_rcpu_cluster_realize(DeviceState *dev, Error **errp)
     if (!qdev_realize(DEVICE(&rc->cluster), NULL, errp)) {
         return;
     }
+
+    /*
+     * Map the entire 4G memory view as unimplemented to prevent faults from
+     * devices that have not been modeled yet.
+     */
+    mmio_map_unimplemented(&rc->memory, SYS_BUS_DEVICE(&rc->unimp_mem),
+                           "tt-rcpu.unimp", 0, UINT32_MAX);
 }
 
 static void tt_rcpu_cluster_class_init(ObjectClass *oc, const void *data)
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index 4f79ef5af5..7ef290ca9d 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -16,6 +16,7 @@
 #include "hw/misc/tt_atlantis_prcm.h"
 #include "hw/char/dw8250.h"
 #include "hw/cpu/cluster.h"
+#include "hw/misc/unimp.h"
 #include "hw/riscv/riscv_hart.h"
 
 #define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
@@ -63,6 +64,11 @@ struct TTRCPUClusterState {
     CPUClusterState cluster;
     MemoryRegion bootrom;
     MemoryRegion sram;
+    /*
+     * All device memory is backed by a catch-all unimplemented device to
+     * prevent faults on accesses to regions not modeled.
+     */
+    UnimplementedDeviceState unimp_mem;
 };
 
 struct TTAtlantisSoCState {
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 10/12] hw/riscv/atlantis: Load the RCPU bootrom
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (8 preceding siblings ...)
  2026-10-15  3:18 ` [PATCH 09/12] hw/riscv/atlantis: Back RCPU memory region by unimplemented device Portia Stephens
@ 2026-10-15  3:18 ` Portia Stephens
  2026-10-15  3:18 ` [PATCH 11/12] tests/functional/riscv64: Add rcpu to tt-atlantis tests Portia Stephens
  2026-10-15  3:18 ` [PATCH 12/12] docs/atlantis: Update Atlantis documentation to include the RCPU Portia Stephens
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

Update tt-atlantis to, when the RCPU is enabled and bootrom binary is
provided, load the provided bootrom binary for the RCPU to execute.

This adds a tt-atlantis machine property 'bootrom' that takes a file
name. qemu_find_file() tries the filename before the search path, so both
full file paths and filenames relative to -L work:

    -M tt-atlantis,rcpu=on,bootrom=out/bootrom.bin

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 hw/riscv/tt_atlantis.c         | 62 +++++++++++++++++++++++++++++++++-
 include/hw/riscv/tt_atlantis.h |  4 +++
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index 635f85e44b..652c954898 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -9,6 +9,7 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu/cutils.h"
+#include "qemu/datadir.h"
 #include "qemu/error-report.h"
 #include "qemu/guest-random.h"
 #include "qemu/units.h"
@@ -676,13 +677,37 @@ static void tt_rcpu_cluster_realize(DeviceState *dev, Error **errp)
      */
     mmio_map_unimplemented(&rc->memory, SYS_BUS_DEVICE(&rc->unimp_mem),
                            "tt-rcpu.unimp", 0, UINT32_MAX);
+
+    if (rc->bootrom_name) {
+        ssize_t rom_size;
+
+        g_autofree char *rom_file = qemu_find_file(QEMU_FILE_TYPE_BIOS, rc->bootrom_name);
+        if (!rom_file) {
+            error_setg(errp, "could not find bootrom image '%s'", rc->bootrom_name);
+            return;
+        }
+
+        rom_size = load_image_mr(rom_file, &rc->bootrom);
+        if (rom_size <= 0) {
+            error_setg(errp, "could not load RCPU bootrom");
+            return;
+        }
+    } else {
+        /* If no bootrom is provided, don't let the core run */
+        CPU(&rc->cpus.harts[0])->start_powered_off = true;
+    }
 }
 
+static const Property tt_rcpu_cluster_props[] = {
+    DEFINE_PROP_STRING("bootrom-name", TTRCPUClusterState, bootrom_name),
+};
+
 static void tt_rcpu_cluster_class_init(ObjectClass *oc, const void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
 
     dc->realize = tt_rcpu_cluster_realize;
+    device_class_set_props(dc, tt_rcpu_cluster_props);
 }
 
 static void tt_ascalon_cluster_init(Object *obj)
@@ -837,6 +862,13 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
     if (s->has_rcpu) {
         /* Only create the RCPU if it is enabled */
         object_initialize_child(OBJECT(s), "rcpu", &s->rcpu, TYPE_TT_RCPU_CLUSTER);
+
+        if (s->bootrom) {
+            object_property_set_str(OBJECT(rc), "bootrom-name",
+                                    s->bootrom,
+                                    &error_abort);
+        }
+
         if (!qdev_realize(DEVICE(&s->rcpu), NULL, errp)) {
             return;
         }
@@ -1015,7 +1047,8 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp)
 static const Property tt_atlantis_soc_props[] = {
     DEFINE_PROP_LINK("dram", TTAtlantisSoCState, dram,
                      TYPE_MEMORY_REGION, MemoryRegion *),
-    DEFINE_PROP_BOOL("has-rcpu", TTAtlantisSoCState, has_rcpu, false)
+    DEFINE_PROP_BOOL("has-rcpu", TTAtlantisSoCState, has_rcpu, false),
+    DEFINE_PROP_STRING("bootrom-name", TTAtlantisSoCState, bootrom)
 };
 
 static void tt_atlantis_soc_class_init(ObjectClass *oc, const void *data)
@@ -1045,6 +1078,13 @@ static void tt_atlantis_machine_init(MachineState *machine)
                             machine->smp.cpus,
                             &error_abort);
 
+    if (ams->has_rcpu && ams->bootrom) {
+        /* Set the RCPU bootrom file name provided on the command line */
+        object_property_set_str(OBJECT(s), "bootrom-name",
+                                ams->bootrom,
+                                &error_abort);
+    }
+
     object_property_set_link(OBJECT(s), "dram", OBJECT(machine->ram),
                              &error_abort);
     object_property_set_bool(OBJECT(&ams->soc), "has-rcpu", ams->has_rcpu,
@@ -1081,6 +1121,22 @@ static void tt_atlantis_machine_set_rcpu(Object *obj, bool value, Error **errp)
     s->has_rcpu = value;
 }
 
+static char *tt_atlantis_get_bootrom(Object *obj, Error **errp)
+{
+    TTAtlantisState *s = TT_ATLANTIS_MACHINE(obj);
+
+    return g_strdup(s->bootrom);
+}
+
+static void tt_atlantis_set_bootrom(Object *obj, const char *value,
+                                   Error **errp)
+{
+    TTAtlantisState *s = TT_ATLANTIS_MACHINE(obj);
+
+    g_free(s->bootrom);
+    s->bootrom = g_strdup(value);
+}
+
 static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
@@ -1101,6 +1157,10 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
                                    tt_atlantis_machine_set_rcpu);
     object_class_property_set_description(oc, "rcpu",
                                           "Set to enable/disable modeling the RCPU.");
+    object_class_property_add_str(oc, "bootrom",
+                                  tt_atlantis_get_bootrom,
+                                  tt_atlantis_set_bootrom);
+    object_class_property_set_description(oc, "bootrom", "RCPU bootrom image");
 
 }
 
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index 7ef290ca9d..28ae1120f7 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -69,6 +69,8 @@ struct TTRCPUClusterState {
      * prevent faults on accesses to regions not modeled.
      */
     UnimplementedDeviceState unimp_mem;
+
+    char *bootrom_name;
 };
 
 struct TTAtlantisSoCState {
@@ -89,6 +91,7 @@ struct TTAtlantisSoCState {
     TTAtlantisPRCMState prcm[TT_ATL_NUM_PRCM];
 
     bool has_rcpu;              /* Enable RCPU */
+    char *bootrom;              /* RCPU boot ROM image */
 };
 
 struct TTAtlantisState {
@@ -101,6 +104,7 @@ struct TTAtlantisState {
     TTAtlantisSoCState soc;
 
     bool has_rcpu;              /* Enable RCPU */
+    char *bootrom;              /* RCPU boot ROM image */
 };
 
 enum {
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 11/12] tests/functional/riscv64: Add rcpu to tt-atlantis tests
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (9 preceding siblings ...)
  2026-10-15  3:18 ` [PATCH 10/12] hw/riscv/atlantis: Load the RCPU bootrom Portia Stephens
@ 2026-10-15  3:18 ` Portia Stephens
  2026-10-15  3:18 ` [PATCH 12/12] docs/atlantis: Update Atlantis documentation to include the RCPU Portia Stephens
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

This updates the tt_atlantis test to test Ascalon boot with the rcpu
enabled and disabled. There is currently no code executing on the rcpu
but this tests that the rcpu model does not break Ascalon boot.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 tests/functional/riscv64/test_tt_atlantis.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/tests/functional/riscv64/test_tt_atlantis.py b/tests/functional/riscv64/test_tt_atlantis.py
index 48abd5cd27..ec5d560811 100755
--- a/tests/functional/riscv64/test_tt_atlantis.py
+++ b/tests/functional/riscv64/test_tt_atlantis.py
@@ -22,8 +22,10 @@ class TTAtlantis(LinuxKernelTest):
          'rootfs.ext2.gz'),
         'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b')
 
-    def do_test_riscv64_tt_atlantis(self, connect_disk):
+    def do_test_riscv64_tt_atlantis(self, connect_disk, rcpu):
         self.set_machine('tt-atlantis')
+        if rcpu:
+            self.machine += ',rcpu=on'
         kernel_path = self.ASSET_KERNEL.fetch()
         rootfs_path = self.uncompress(self.ASSET_ROOTFS)
 
@@ -48,9 +50,13 @@ def do_test_riscv64_tt_atlantis(self, connect_disk):
         self.vm.launch()
         self.wait_for_console_pattern(pattern)
 
+    def test_riscv64_tt_atlantis_rcpu(self):
+        # test with the rcpu enabled
+        self.do_test_riscv64_tt_atlantis(False, True)
+
     def test_riscv64_tt_atlantis(self):
         # tt-atlantis machine has no PCI host yet, so no disk
-        self.do_test_riscv64_tt_atlantis(False)
+        self.do_test_riscv64_tt_atlantis(False, False)
 
 
 if __name__ == '__main__':
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 12/12] docs/atlantis: Update Atlantis documentation to include the RCPU
  2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
                   ` (10 preceding siblings ...)
  2026-10-15  3:18 ` [PATCH 11/12] tests/functional/riscv64: Add rcpu to tt-atlantis tests Portia Stephens
@ 2026-10-15  3:18 ` Portia Stephens
  11 siblings, 0 replies; 16+ messages in thread
From: Portia Stephens @ 2026-10-15  3:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Daniel Henrique Barboza, Portia Stephens, Michael Ellerman

This updates the Atlantis documentation to include new machine
properties for running the RCPU.

Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
---
 docs/system/gdb.rst               |  2 ++
 docs/system/riscv/tt_atlantis.rst | 30 ++++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/docs/system/gdb.rst b/docs/system/gdb.rst
index b6d8f8e77d..3777630337 100644
--- a/docs/system/gdb.rst
+++ b/docs/system/gdb.rst
@@ -79,6 +79,8 @@ debug some interaction between kernel and user-space you are better
 off running your guest program with gdb either in the guest or using
 a gdbserver exposed via a port to the outside world.
 
+.. _GDB multicore:
+
 Debugging multicore machines
 ============================
 
diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst
index d94d064f4f..f7265813d1 100644
--- a/docs/system/riscv/tt_atlantis.rst
+++ b/docs/system/riscv/tt_atlantis.rst
@@ -12,10 +12,27 @@ tt-atlantis QEMU model features
 -------------------------------
 
 * 8-core Ascalon-X CPU Cluster
+* RV32 Real-time CPU (RCPU)
 * RISC-V compliant Advanced Interrupt Architecture
 * DW8250 compatible UART x5
 * 4 PRCMs (Clock and Reset Controllers)
 
+Boot options
+------------
+
+ * ``rcpu`` enables the RCPU model to run alongside the Ascalon cluster. When
+   the RCPU is enabled, ``UART0``, the RCPU's console, will be mapped to the second
+   -serial on the command line.
+ * ``bootrom`` binary to be loaded into mask ROM and run by the RCPU (no effect if the
+   rcpu is not enabled)
+
+To enable the RCPU model with a custom binary and the RCPU console on stdio:
+
+.. code-block:: bash
+
+  -M tt-atlantis,rcpu=on,bootrom=../path/to/binary.bin -serial null -serial stdio
+
+
 Known limitations
 -----------------
 
@@ -39,3 +56,16 @@ and Linux with default configurations.
 The development board hardware will require some implementation
 specific setup in firmware which is being developed and may
 become a requirement or option for the tt-atlantis machine.
+
+Debugging
+----------
+
+Ascalon and RCPU are on separate CPUClusters which allows gdb to put Ascalon
+and RCPU on separate inferiors. This prevents issues with switching between
+RV32 (RCPU) and RV64 (Ascalon) targets.
+
+By default, gdb will connect to inferior 1, the Ascalon cluster and the RCPU
+will be on inferior 2.
+
+gbd does not handle multiple inferiors by default, see :ref:`gdb multicore`
+for how to connect to inferior 2.
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 07/12] machine: Support extra board CPUs outside -smp
  2026-10-15  3:17 ` [PATCH 07/12] machine: Support extra board CPUs outside -smp Portia Stephens
@ 2026-10-15 20:31   ` Daniel Henrique Barboza
  2026-10-16 10:26     ` Daniel P. Berrangé
  0 siblings, 1 reply; 16+ messages in thread
From: Daniel Henrique Barboza @ 2026-10-15 20:31 UTC (permalink / raw)
  To: Portia Stephens, qemu-devel
  Cc: Nicholas Piggin, Palmer Dabbelt, Chao Liu, Alistair Francis,
	qemu-riscv, Philippe Mathieu-Daudé, Paolo Bonzini,
	Joel Stanley, Liu Zhiwei, Alex Bennée, Pierrick Bouvier,
	Weiwei Li, Anirudh Srinivasan, Richard Henderson, Zhao Liu,
	Michael Ellerman

Phil, Pierrick,

This patch seems to dwell around your turf.  Did you think about how to
instantiate different CPUs that might have different SMP topologies in
the context of single binary/split accel work?


Thanks,
Daniel

On 10/15/2026 12:17 AM, Portia Stephens wrote:
> From: Joel Stanley <joel@jms.id.au>
> 
> Boards may instantiate one or more CPUs that are not part of the
> user's -smp layout, for example an a management or boot core of a
> different CPU model. As TCG sizes its vCPU context from smp.max_cpus
> during init, creating more CPUState objects than smp.max_cpus trips the
> assert in tcg_register_thread().
> 
> smp.max_cpus is finalized before the accelerator is initialised and the
> board's init() runs later still, so a board's initialise cannot influence
> the count TCG uses.
> 
> Add a MachineClass::extra_cpus that boards set at class init time to
> declare these additional CPUs. TCG can then add it to max_threads. The
> field defaults to 0, so existing machines are unaffected.
> 
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Portia Stephens <portias@oss.tenstorrent.com>
> ---
>   accel/tcg/tcg-all.c      | 2 +-
>   include/hw/core/boards.h | 4 ++++
>   2 files changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c
> index 767e880555..655609ebbb 100644
> --- a/accel/tcg/tcg-all.c
> +++ b/accel/tcg/tcg-all.c
> @@ -126,7 +126,7 @@ static int tcg_init_machine(AccelState *as, MachineState *ms)
>            */
>           if (mttcg_supported && !icount_enabled()) {
>               s->mttcg_enabled = ON_OFF_AUTO_ON;
> -            max_threads = ms->smp.max_cpus;
> +            max_threads = ms->smp.max_cpus + MACHINE_GET_CLASS(ms)->extra_cpus;
>           } else {
>               s->mttcg_enabled = ON_OFF_AUTO_OFF;
>           }
> diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h
> index d0e4ad9820..575c6bcb31 100644
> --- a/include/hw/core/boards.h
> +++ b/include/hw/core/boards.h
> @@ -189,6 +189,9 @@ typedef struct {
>    * @max_cpus: maximum number of CPUs supported. Default: 1
>    * @min_cpus: minimum number of CPUs supported. Default: 1
>    * @default_cpus: number of CPUs instantiated if none are specified. Default: 1
> + * @extra_cpus: number of additional CPUs the board always instantiates beyond
> + *    the -smp topology (e.g. an always-present management core). Accelerators
> + *    add this to the per-vCPU state they allocate. Default: 0
>    * @is_default:
>    *    If true QEMU will use this machine by default if no '-M' option is given.
>    * @get_hotplug_handler: this function is called during bus-less
> @@ -287,6 +290,7 @@ struct MachineClass {
>       int max_cpus;
>       int min_cpus;
>       int default_cpus;
> +    int extra_cpus;
>       unsigned int no_serial:1,
>           no_parallel:1,
>           no_floppy:1,



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 07/12] machine: Support extra board CPUs outside -smp
  2026-10-15 20:31   ` Daniel Henrique Barboza
@ 2026-10-16 10:26     ` Daniel P. Berrangé
  2026-10-16 11:23       ` Daniel Henrique Barboza
  0 siblings, 1 reply; 16+ messages in thread
From: Daniel P. Berrangé @ 2026-10-16 10:26 UTC (permalink / raw)
  To: Daniel Henrique Barboza
  Cc: Portia Stephens, qemu-devel, Nicholas Piggin, Palmer Dabbelt,
	Chao Liu, Alistair Francis, qemu-riscv,
	Philippe Mathieu-Daudé, Paolo Bonzini, Joel Stanley,
	Liu Zhiwei, Alex Bennée, Pierrick Bouvier, Weiwei Li,
	Anirudh Srinivasan, Richard Henderson, Zhao Liu, Michael Ellerman

On Thu, Oct 15, 2026 at 05:31:33PM -0300, Daniel Henrique Barboza wrote:
> Phil, Pierrick,
> 
> This patch seems to dwell around your turf.  Did you think about how to
> instantiate different CPUs that might have different SMP topologies in
> the context of single binary/split accel work?

IMHO the -smp argument as it exists today is essentially unfixable
for the general case and will ultimately need to be declared legacy
/ for simplistic usage only. Trying to paper over the limitations of
-smp with special cases like "extra cpus" is unlikely to deliver
attractive solutions long term.

The eventual emulation of heterogeneous CPUS in the single binary
work will bring this to a head, but it already causes problems
today with our per-target binaries because even individual the real
world CPUs we want to be able to model are not symmetric.

As a historical reference, a while back, Zhao had an RFC that proposed
we use '-device' to define each topology level discretely, which would
allow for arbitrary asymmetric topologies:

  https://lists.gnu.org/archive/html/qemu-devel/2024-09/msg03727.html


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 07/12] machine: Support extra board CPUs outside -smp
  2026-10-16 10:26     ` Daniel P. Berrangé
@ 2026-10-16 11:23       ` Daniel Henrique Barboza
  0 siblings, 0 replies; 16+ messages in thread
From: Daniel Henrique Barboza @ 2026-10-16 11:23 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Portia Stephens, qemu-devel, Nicholas Piggin, Palmer Dabbelt,
	Chao Liu, Alistair Francis, qemu-riscv,
	Philippe Mathieu-Daudé, Paolo Bonzini, Joel Stanley,
	Liu Zhiwei, Alex Bennée, Pierrick Bouvier, Weiwei Li,
	Anirudh Srinivasan, Richard Henderson, Zhao Liu, Michael Ellerman



On 10/16/2026 7:26 AM, Daniel P. Berrangé wrote:
> On Thu, Oct 15, 2026 at 05:31:33PM -0300, Daniel Henrique Barboza wrote:
>> Phil, Pierrick,
>>
>> This patch seems to dwell around your turf.  Did you think about how to
>> instantiate different CPUs that might have different SMP topologies in
>> the context of single binary/split accel work?
> 
> IMHO the -smp argument as it exists today is essentially unfixable
> for the general case and will ultimately need to be declared legacy
> / for simplistic usage only. Trying to paper over the limitations of
> -smp with special cases like "extra cpus" is unlikely to deliver
> attractive solutions long term.
> 
> The eventual emulation of heterogeneous CPUS in the single binary
> work will bring this to a head, but it already causes problems
> today with our per-target binaries because even individual the real
> world CPUs we want to be able to model are not symmetric.
> 
> As a historical reference, a while back, Zhao had an RFC that proposed
> we use '-device' to define each topology level discretely, which would
> allow for arbitrary asymmetric topologies:
> 
>    https://lists.gnu.org/archive/html/qemu-devel/2024-09/msg03727.html

This RFC looks solid.  Any reason why we didn't go through with it back then?
The conversation just fizzled out quietly from what I'm reading in the archives ...

I am not sure about the schedule/planning around the heterogeneous CPU work
but if this RFC is aligned with that effort it would be great if we could push
that forward.   Enabling multiple CPUs of the same architecture/target into
a single board seems like a step into that direction.

We have the tt-atlantis RISC-V board that has a use case for additional CPUs outside
of the main smp topology, i.e. we can use the board as a testcase/validation for
the RFC.


Cheers,
Daniel


> 
> 
> With regards,
> Daniel



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-10-16 11:24 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15  3:17 [PATCH 00/12] Add the RCPU to Tenstorrent Atlantis Portia Stephens
2026-10-15  3:17 ` [PATCH 01/12] hw/riscv/atlantis: Set valid_cpu_types for Atlantis machine Portia Stephens
2026-10-15  3:17 ` [PATCH 02/12] hw/riscv/atlantis: Update fdt functions to take generic parameters Portia Stephens
2026-10-15  3:17 ` [PATCH 03/12] hw/riscv/atlantis: Move Ascalon into cluster struct Portia Stephens
2026-10-15  3:17 ` [PATCH 04/12] hw/riscv/atlantis: Store Ascalon memory in " Portia Stephens
2026-10-15  3:17 ` [PATCH 05/12] hw/riscv/atlantis: Create CPU cluster for Atlantis cpus Portia Stephens
2026-10-15  3:17 ` [PATCH 06/12] target/riscv: Add Tenstorrent Real-time CPU Portia Stephens
2026-10-15  3:17 ` [PATCH 07/12] machine: Support extra board CPUs outside -smp Portia Stephens
2026-10-15 20:31   ` Daniel Henrique Barboza
2026-10-16 10:26     ` Daniel P. Berrangé
2026-10-16 11:23       ` Daniel Henrique Barboza
2026-10-15  3:18 ` [PATCH 08/12] hw/riscv/atlantis: Add the RCPU to the Atlantis SoC Portia Stephens
2026-10-15  3:18 ` [PATCH 09/12] hw/riscv/atlantis: Back RCPU memory region by unimplemented device Portia Stephens
2026-10-15  3:18 ` [PATCH 10/12] hw/riscv/atlantis: Load the RCPU bootrom Portia Stephens
2026-10-15  3:18 ` [PATCH 11/12] tests/functional/riscv64: Add rcpu to tt-atlantis tests Portia Stephens
2026-10-15  3:18 ` [PATCH 12/12] docs/atlantis: Update Atlantis documentation to include the RCPU Portia Stephens

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.