On Tue, Jun 30, 2026 at 12:52 PM Joel Stanley <[email protected]> wrote: > > The Tenstorrent Atlantis platform is a collaboration between Tenstorrent > and CoreLab Technology. It is based on the Atlantis SoC, which includes > the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology. > The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant > RISC-V CPU. > > Add the tt-atlantis machine containing serial console, interrupt > controllers, and device tree support. > > The Atlantis boot images loaded from include OpenSBI and an initial DTB > that is passed to OpenSBI. This is approximated in the model by having > QEMU build the device tree rather than load a DTB image directly. > Subsequent stages may use the modified DTB provided by OpenSBI or opt to > supply their own. > > qemu-system-riscv64 -M tt-atlantis -m 512M \ > -kernel Image -initrd rootfs.cpio -nographic > > Co-Developed-by: Nicholas Piggin <[email protected]> > Signed-off-by: Nicholas Piggin <[email protected]> > Reviewed-by: Philippe Mathieu-Daudé <[email protected]> > Reviewed-by: Chao Liu <[email protected]> > Signed-off-by: Joel Stanley <[email protected]>
Reviewed-by: Alistair Francis <[email protected]> Alistair > --- > MAINTAINERS | 12 + > docs/system/riscv/tt_atlantis.rst | 41 +++ > docs/system/target-riscv.rst | 1 + > include/hw/riscv/tt_atlantis.h | 50 +++ > hw/riscv/tt_atlantis.c | 523 ++++++++++++++++++++++++++++++ > hw/riscv/Kconfig | 10 + > hw/riscv/meson.build | 1 + > 7 files changed, 638 insertions(+) > create mode 100644 docs/system/riscv/tt_atlantis.rst > create mode 100644 include/hw/riscv/tt_atlantis.h > create mode 100644 hw/riscv/tt_atlantis.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 72357df7c4b7..0d7c7720d9f2 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1774,6 +1774,18 @@ F: hw/*/*sifive*.c > F: include/hw/*/*sifive*.h > F: tests/functional/riscv64/test_sifive_u.py > > +Tenstorrent Machines > +M: Joel Stanley <[email protected]> > +M: Nicholas Piggin <[email protected]> > +R: Anirudh Srinivasan <[email protected]> > +R: Michael Ellerman <[email protected]> > +R: Portia Stephens <[email protected]> > +L: [email protected] > +S: Supported > +F: docs/system/riscv/tt_*.rst > +F: hw/riscv/tt_*.c > +F: include/hw/riscv/tt_*.h > + > AMD Microblaze-V Generic Board > M: Sai Pavan Boddu <[email protected]> > S: Maintained > diff --git a/docs/system/riscv/tt_atlantis.rst > b/docs/system/riscv/tt_atlantis.rst > new file mode 100644 > index 000000000000..1f2880d61773 > --- /dev/null > +++ b/docs/system/riscv/tt_atlantis.rst > @@ -0,0 +1,41 @@ > +Tenstorrent Atlantis (``tt-atlantis``) > +====================================== > + > +The Tenstorrent Atlantis platform is a collaboration between Tenstorrent > +and CoreLab Technology. It is based on the Atlantis SoC, which includes > +the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology. > + > +The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant > +RISC-V CPU. > + > +tt-atlantis QEMU model features > +------------------------------- > + > +* 8-core Ascalon-X CPU Cluster > +* RISC-V compliant Advanced Interrupt Architecture > +* 16550A compatible UART > + > +Known limitations > +----------------- > + > +The QEMU tt-atlantis machine does not yet model every device on the > +real platform. Notably: > + > +* There is no PCI host bridge, so virtio-pci devices cannot be > + attached. Boots that need block storage must use ``-initrd`` with > + an initramfs. > +* The DesignWare UART is modelled with QEMU's ns16550-compatible > + ``serial_mm`` device; DesignWare-specific registers beyond that > + set return 0. > + > +Supported software > +------------------ > + > +The Tenstorrent Ascalon CPUs avoid proprietary or non-standard > +extensions, so compatibility with existing software is generally > +good. The QEMU tt-atlantis machine works with upstream OpenSBI > +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. > diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst > index 896f14e78bd0..2639866a3e8a 100644 > --- a/docs/system/target-riscv.rst > +++ b/docs/system/target-riscv.rst > @@ -72,6 +72,7 @@ undocumented; you can get a complete list by running > riscv/mips > riscv/shakti-c > riscv/sifive_u > + riscv/tt_atlantis > riscv/virt > riscv/xiangshan-kunminghu > > diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h > new file mode 100644 > index 000000000000..a17732ce8114 > --- /dev/null > +++ b/include/hw/riscv/tt_atlantis.h > @@ -0,0 +1,50 @@ > +/* > + * Tenstorrent Atlantis RISC-V System on Chip > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + * > + * Copyright 2025 Tenstorrent, Joel Stanley <[email protected]> > + */ > + > +#ifndef HW_RISCV_TT_ATLANTIS_H > +#define HW_RISCV_TT_ATLANTIS_H > + > +#include "hw/core/boards.h" > +#include "hw/core/sysbus.h" > +#include "hw/intc/riscv_imsic.h" > +#include "hw/riscv/riscv_hart.h" > + > +#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis") > +OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE) > + > +struct TTAtlantisState { > + /*< private >*/ > + MachineState parent; > + > + /*< public >*/ > + Notifier machine_done; > + const MemMapEntry *memmap; > + > + RISCVHartArrayState soc; > + DeviceState *irqchip; > + > + int fdt_size; > +}; > + > +enum { > + TT_ATL_UART1_IRQ = 39, > +}; > + > +enum { > + TT_ATL_ACLINT, > + TT_ATL_BOOTROM, > + TT_ATL_DDR_LO, > + TT_ATL_DDR_HI, > + TT_ATL_MAPLIC, > + TT_ATL_MIMSIC, > + TT_ATL_SAPLIC, > + TT_ATL_SIMSIC, > + TT_ATL_UART1, > +}; > + > +#endif > diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c > new file mode 100644 > index 000000000000..1f0cd08ac959 > --- /dev/null > +++ b/hw/riscv/tt_atlantis.c > @@ -0,0 +1,523 @@ > +/* > + * Tenstorrent Atlantis RISC-V System on Chip > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + * > + * Copyright 2025 Tenstorrent, Joel Stanley <[email protected]> > + */ > + > +#include "qemu/osdep.h" > +#include "qemu/cutils.h" > +#include "qemu/error-report.h" > +#include "qemu/guest-random.h" > +#include "qemu/units.h" > + > +#include "hw/core/boards.h" > +#include "hw/core/loader.h" > +#include "hw/core/sysbus.h" > + > +#include "target/riscv/cpu.h" > +#include "target/riscv/pmu.h" > + > +#include "hw/riscv/boot.h" > +#include "hw/riscv/fdt-common.h" > +#include "hw/riscv/machines-qom.h" > +#include "hw/riscv/riscv_hart.h" > + > +#include "hw/char/serial-mm.h" > +#include "hw/intc/riscv_aclint.h" > +#include "hw/misc/unimp.h" > + > +#include "system/system.h" > +#include "system/device_tree.h" > + > +#include "hw/riscv/tt_atlantis.h" > + > +#include "aia.h" > + > +#define TT_IRQCHIP_NUM_MSIS 255 > +#define TT_IRQCHIP_NUM_SOURCES 128 > +#define TT_IRQCHIP_NUM_PRIO_BITS 3 > +#define TT_IRQCHIP_GUESTS 63 /* aia_guests, gives guest_index_bits=6 > */ > +#define TT_IRQCHIP_MIMSIC_STRIDE 0x40000 > + > +#define TT_ACLINT_MTIME_SIZE 0x8050 > +#define TT_ACLINT_MTIME 0x0 > +#define TT_ACLINT_MTIMECMP 0x8000 > +#define TT_ACLINT_TIMEBASE_FREQ 1000000000 > + > +static const MemMapEntry tt_atlantis_memmap[] = { > + /* Keep sorted with :'<,'>!sort -g -k 4 */ > + [TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 }, > + [TT_ATL_BOOTROM] = { 0x80000000, 0x2000 }, > + [TT_ATL_MIMSIC] = { 0xa0000000, 0x200000 }, > + [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 }, > + [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 }, > + [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 }, > + [TT_ATL_UART1] = { 0xd4110000, 0x10000 }, > + [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 }, > + [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 }, > +}; > + > +static uint32_t fdt_phandle = 1; > +static uint32_t next_phandle(void) > +{ > + return fdt_phandle++; > +} > + > +static void create_fdt_memory(TTAtlantisState *s) > +{ > + void *fdt = MACHINE(s)->fdt; > + hwaddr size_lo = MACHINE(s)->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; > + size_hi = MACHINE(s)->ram_size - size_lo; > + } > + > + create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_LO].base, size_lo, > + 0, false); > + if (size_hi) { > + /* > + * The first part of the HI address is aliased at the LO address > + * so do not include that as usable memory. Is there any way > + * (or good reason) to describe that aliasing 2GB with DT? > + */ > + create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_HI].base + > size_lo, > + size_hi, 0, false); > + } > +} > + > +static void create_fdt_aclint(TTAtlantisState *s, uint32_t *intc_phandles) > +{ > + void *fdt = MACHINE(s)->fdt; > + g_autofree char *name = NULL; > + g_autofree uint32_t *aclint_mtimer_cells = NULL; > + uint32_t aclint_cells_size; > + hwaddr addr; > + > + aclint_mtimer_cells = g_new0(uint32_t, s->soc.num_harts * 2); > + > + for (int cpu = 0; cpu < s->soc.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->soc.num_harts * sizeof(uint32_t) * 2; > + > + addr = s->memmap[TT_ATL_ACLINT].base; > + > + name = g_strdup_printf("/soc/mtimer@%"HWADDR_PRIX, addr); > + qemu_fdt_add_subnode(fdt, name); > + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aclint-mtimer"); > + qemu_fdt_setprop_sized_cells(fdt, name, "reg", > + 2, addr + TT_ACLINT_MTIME, > + 2, 0x1000, > + 2, addr + TT_ACLINT_MTIMECMP, > + 2, 0x1000); > + qemu_fdt_setprop(fdt, name, "interrupts-extended", > + aclint_mtimer_cells, aclint_cells_size); > +} > + > +static void create_fdt_one_imsic(void *fdt, const MemMapEntry *mem, int cpus, > + uint32_t *intc_phandles, uint32_t > msi_phandle, > + int irq_line, uint32_t imsic_guest_bits) > +{ > + g_autofree char *name = NULL; > + g_autofree uint32_t *imsic_cells = g_new0(uint32_t, cpus * 2); > + > + for (int cpu = 0; cpu < cpus; cpu++) { > + imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); > + imsic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line); > + } > + > + name = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, > mem->base); > + qemu_fdt_add_subnode(fdt, name); > + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,imsics"); > + > + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 0); > + qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0); > + qemu_fdt_setprop(fdt, name, "msi-controller", NULL, 0); > + qemu_fdt_setprop(fdt, name, "interrupts-extended", > + imsic_cells, sizeof(uint32_t) * cpus * 2); > + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, > mem->size); > + qemu_fdt_setprop_cell(fdt, name, "riscv,num-ids", TT_IRQCHIP_NUM_MSIS); > + > + if (imsic_guest_bits) { > + qemu_fdt_setprop_cell(fdt, name, "riscv,guest-index-bits", > + imsic_guest_bits); > + } > + qemu_fdt_setprop_cell(fdt, name, "phandle", msi_phandle); > +} > + > +static void create_fdt_one_aplic(void *fdt, > + const MemMapEntry *mem, > + uint32_t msi_phandle, > + uint32_t *intc_phandles, > + uint32_t aplic_phandle, > + uint32_t aplic_child_phandle, > + int irq_line, int num_harts) > +{ > + g_autofree char *name = > + g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base); > + g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2); > + > + for (int cpu = 0; cpu < num_harts; cpu++) { > + aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); > + aplic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line); > + } > + > + qemu_fdt_add_subnode(fdt, name); > + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aplic"); > + qemu_fdt_setprop_cell(fdt, name, "#address-cells", 0); > + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 2); > + qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0); > + > + qemu_fdt_setprop(fdt, name, "interrupts-extended", > + aplic_cells, num_harts * sizeof(uint32_t) * 2); > + qemu_fdt_setprop_cell(fdt, name, "msi-parent", msi_phandle); > + > + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, > mem->size); > + qemu_fdt_setprop_cell(fdt, name, "riscv,num-sources", > + TT_IRQCHIP_NUM_SOURCES); > + > + if (aplic_child_phandle) { > + qemu_fdt_setprop_cell(fdt, name, "riscv,children", > + aplic_child_phandle); > + qemu_fdt_setprop_cells(fdt, name, "riscv,delegation", > + aplic_child_phandle, 1, > TT_IRQCHIP_NUM_SOURCES); > + } > + > + qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle); > +} > + > +static void create_fdt_pmu(TTAtlantisState *s) > +{ > + char pmu_name[] = "/pmu"; > + void *fdt = MACHINE(s)->fdt; > + RISCVCPU *hart = &s->soc.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(TTAtlantisState *s, const MemMapEntry *memmap, > + uint32_t aplic_s_phandle, > + uint32_t imsic_s_phandle) > +{ > + MachineState *ms = MACHINE(s); > + void *fdt = MACHINE(s)->fdt; > + g_autofree uint32_t *intc_phandles = g_new0(uint32_t, ms->smp.cpus); > + > + fdt_create_cpu_socket_subnode(fdt, TT_ACLINT_TIMEBASE_FREQ); > + > + create_fdt_socket_cpus(fdt, s->soc.harts, 0, s->soc.num_harts, > + s->soc.hartid_base, &fdt_phandle, intc_phandles, > + false, false); > + > + create_fdt_memory(s); > + > + create_fdt_aclint(s, intc_phandles); > + > + uint32_t imsic_guest_bits = imsic_num_bits(TT_IRQCHIP_GUESTS + 1); > + > + /* M-level IMSIC node */ > + uint32_t msi_m_phandle = next_phandle(); > + create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_MIMSIC], ms->smp.cpus, > + intc_phandles, msi_m_phandle, > + IRQ_M_EXT, imsic_guest_bits); > + > + /* S-level IMSIC node */ > + create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_SIMSIC], ms->smp.cpus, > + intc_phandles, imsic_s_phandle, > + IRQ_S_EXT, imsic_guest_bits); > + > + uint32_t aplic_m_phandle = next_phandle(); > + > + /* M-level APLIC node */ > + create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_MAPLIC], > + msi_m_phandle, intc_phandles, > + aplic_m_phandle, aplic_s_phandle, > + IRQ_M_EXT, s->soc.num_harts); > + > + /* S-level APLIC node */ > + create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_SAPLIC], > + imsic_s_phandle, intc_phandles, > + aplic_s_phandle, 0, > + IRQ_S_EXT, s->soc.num_harts); > +} > + > +static void create_fdt_uart(void *fdt, const MemMapEntry *mem, int irq, > + int irqchip_phandle) > +{ > + g_autofree char *name = g_strdup_printf("/soc/serial@%"HWADDR_PRIX, > + mem->base); > + > + qemu_fdt_add_subnode(fdt, name); > + qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a"); > + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, > mem->size); > + qemu_fdt_setprop_cell(fdt, name, "reg-shift", 2); > + qemu_fdt_setprop_cell(fdt, name, "reg-io-width", 4); > + qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400); > + qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle); > + qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4); > + > + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name); > + qemu_fdt_setprop_string(fdt, "/aliases", "serial0", name); > +} > + > +static void create_fdt_rng(void *fdt) > +{ > + uint8_t rng_seed[32]; > + > + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); > + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); > +} > + > +static void finalize_fdt(TTAtlantisState *s) > +{ > + uint32_t aplic_s_phandle = next_phandle(); > + uint32_t imsic_s_phandle = next_phandle(); > + void *fdt = MACHINE(s)->fdt; > + > + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle); > + > + /* > + * We want to do this, but the Linux aplic driver was broken before v6.16 > + * > + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent", > + * aplic_s_phandle); > + */ > + > + create_fdt_uart(fdt, &s->memmap[TT_ATL_UART1], TT_ATL_UART1_IRQ, > + aplic_s_phandle); > +} > + > +static void create_fdt(TTAtlantisState *s) > +{ > + MachineState *ms = MACHINE(s); > + > + ms->fdt = create_board_device_tree("Tenstorrent Atlantis RISC-V Machine", > + "tenstorrent,atlantis", &s->fdt_size); > + > + qemu_fdt_add_subnode(ms->fdt, "/chosen"); > + > + create_fdt_rng(ms->fdt); > + > + qemu_fdt_add_subnode(ms->fdt, "/aliases"); > + > + create_fdt_pmu(s); > +} > + > +static void load_fdt(TTAtlantisState *s) > +{ > + MachineState *ms = MACHINE(s); > + char **node_path; > + Error *err = NULL; > + > + ms->fdt = load_device_tree(ms->dtb, &s->fdt_size); > + if (!ms->fdt) { > + error_report("load_device_tree() failed"); > + exit(1); > + } > + > + qemu_fdt_add_path(ms->fdt, "/chosen"); > + > + /* Clear memory nodes and update with the specified RAM size */ > + node_path = qemu_fdt_node_unit_path(ms->fdt, "memory", &err); > + if (err) { > + warn_report_err(err); > + } else { > + for (int i = 0; node_path[i]; i++) { > + warn_report("Replacing device tree %s with the requested RAM > size", > + node_path[i]); > + qemu_fdt_nop_node(ms->fdt, node_path[i]); > + } > + g_strfreev(node_path); > + } > + > + create_fdt_memory(s); > +} > + > +static void tt_atlantis_machine_done(Notifier *notifier, void *data) > +{ > + TTAtlantisState *s = container_of(notifier, TTAtlantisState, > machine_done); > + MachineState *machine = MACHINE(s); > + hwaddr start_addr = s->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->soc); > + uint64_t fdt_load_addr; > + uint64_t kernel_entry; > + RISCVBootInfo boot_info; > + > + /* > + * A user provided dtb must include everything, including > + * dynamic sysbus devices. Our FDT needs to be finalized. > + */ > + if (machine->dtb == NULL) { > + finalize_fdt(s); > + } > + > + mem_size = machine->ram_size; > + if (mem_size > s->memmap[TT_ATL_DDR_LO].size) { > + mem_size = s->memmap[TT_ATL_DDR_LO].size; > + } > + riscv_boot_info_init_discontig_mem(&boot_info, &s->soc, > + s->memmap[TT_ATL_DDR_LO].base, > + mem_size); > + > + firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info, > + firmware_name, > + &start_addr, NULL); > + > + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, > + firmware_end_addr); > + if (machine->kernel_filename) { > + riscv_load_kernel(machine, &boot_info, kernel_start_addr, > + true, NULL); > + } > + kernel_entry = boot_info.image_low_addr; > + > + fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base, > + s->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->soc, start_addr, > + s->memmap[TT_ATL_BOOTROM].base, > + s->memmap[TT_ATL_BOOTROM].size, > + kernel_entry, > + fdt_load_addr); > +} > + > +static void tt_atlantis_machine_init(MachineState *machine) > +{ > + TTAtlantisState *s = TT_ATLANTIS_MACHINE(machine); > + > + MemoryRegion *system_memory = get_system_memory(); > + MemoryRegion *ram_hi = g_new(MemoryRegion, 1); > + MemoryRegion *ram_lo = g_new(MemoryRegion, 1); > + MemoryRegion *bootrom = g_new(MemoryRegion, 1); > + ram_addr_t lo_ram_size; > + int hart_count = machine->smp.cpus; > + > + s->memmap = tt_atlantis_memmap; > + > + object_initialize_child(OBJECT(machine), "soc", &s->soc, > + TYPE_RISCV_HART_ARRAY); > + object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type, > + &error_abort); > + object_property_set_int(OBJECT(&s->soc), "hartid-base", 0, > + &error_abort); > + object_property_set_int(OBJECT(&s->soc), "num-harts", hart_count, > + &error_abort); > + object_property_set_int(OBJECT(&s->soc), "resetvec", > + s->memmap[TT_ATL_BOOTROM].base, > + &error_abort); > + sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); > + > + s->irqchip = riscv_create_aia(true, TT_IRQCHIP_GUESTS, > + TT_IRQCHIP_MIMSIC_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], > + 0, 0, hart_count, > + TT_IRQCHIP_NUM_MSIS, > + TT_IRQCHIP_NUM_PRIO_BITS); > + > + riscv_aclint_mtimer_create(s->memmap[TT_ATL_ACLINT].base, > + TT_ACLINT_MTIME_SIZE, > + 0, hart_count, > + TT_ACLINT_MTIMECMP, > + TT_ACLINT_MTIME, > + TT_ACLINT_TIMEBASE_FREQ, true); > + > + /* > + * DDR > + * > + * 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 (machine->ram_size > s->memmap[TT_ATL_DDR_HI].size) { > + char *sz = size_to_str(s->memmap[TT_ATL_DDR_HI].size); > + error_report("RAM size is too large, maximum is %s", sz); > + g_free(sz); > + exit(EXIT_FAILURE); > + } > + > + memory_region_init_alias(ram_hi, OBJECT(machine), "ram.high", > machine->ram, > + 0, machine->ram_size); > + memory_region_add_subregion(system_memory, > + s->memmap[TT_ATL_DDR_HI].base, ram_hi); > + > + lo_ram_size = MIN(machine->ram_size, s->memmap[TT_ATL_DDR_LO].size); > + memory_region_init_alias(ram_lo, OBJECT(machine), "ram.low", > machine->ram, > + 0, lo_ram_size); > + memory_region_add_subregion(system_memory, > + s->memmap[TT_ATL_DDR_LO].base, ram_lo); > + > + /* Boot ROM */ > + memory_region_init_rom(bootrom, NULL, "tt-atlantis.bootrom", > + s->memmap[TT_ATL_BOOTROM].size, &error_fatal); > + memory_region_add_subregion(system_memory, > s->memmap[TT_ATL_BOOTROM].base, > + bootrom); > + > + /* UART1, the soc console (UART0 is for the boot microcontroller) */ > + serial_mm_init(system_memory, s->memmap[TT_ATL_UART1].base, 2, > + qdev_get_gpio_in(s->irqchip, TT_ATL_UART1_IRQ), > + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); > + /* > + * Atlantis contains a DesignWare uart while the QEMU machine > + * uses the serial_mm model with the base ns16550 register set. > + * Linux's dw driver writes outside of serial_mm's 0x20 sized > + * mapping and faults. > + * > + * Create an unimplemented device region so writes don't fault > + * and reads return zero, which keeps Linux happy. > + */ > + create_unimplemented_device("tt-atlantis.uart0", > + s->memmap[TT_ATL_UART1].base, > + s->memmap[TT_ATL_UART1].size); > + > + /* Load or create device tree */ > + if (machine->dtb) { > + load_fdt(s); > + } else { > + create_fdt(s); > + } > + > + s->machine_done.notify = tt_atlantis_machine_done; > + qemu_add_machine_init_done_notifier(&s->machine_done); > +} > + > +static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data) > +{ > + MachineClass *mc = MACHINE_CLASS(oc); > + > + mc->desc = "Tenstorrent Atlantis RISC-V SoC (Experimental)"; > + mc->init = tt_atlantis_machine_init; > + mc->max_cpus = 8; > + mc->default_cpus = 8; > + mc->default_ram_size = 4 * GiB; > + mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON; > + mc->block_default_type = IF_VIRTIO; > + mc->no_cdrom = 1; > + mc->default_ram_id = "tt_atlantis.ram"; > +} > + > +static const TypeInfo tt_atlantis_types[] = { > + { > + .name = MACHINE_TYPE_NAME("tt-atlantis"), > + .parent = TYPE_MACHINE, > + .class_init = tt_atlantis_machine_class_init, > + .instance_size = sizeof(TTAtlantisState), > + .interfaces = riscv64_machine_interfaces, > + }, > +}; > + > +DEFINE_TYPES(tt_atlantis_types) > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > index 54e41a6afc90..5033415440d4 100644 > --- a/hw/riscv/Kconfig > +++ b/hw/riscv/Kconfig > @@ -120,6 +120,16 @@ config SPIKE > select RISCV_ACLINT > select SIFIVE_PLIC > > +config TENSTORRENT > + bool > + default y > + depends on RISCV64 > + select RISCV_ACLINT > + select RISCV_APLIC > + select RISCV_IMSIC > + select SERIAL_MM > + select DEVICE_TREE > + > config XIANGSHAN_KUNMINGHU > bool > default y > diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build > index 798477b7882a..0d82ceacc430 100644 > --- a/hw/riscv/meson.build > +++ b/hw/riscv/meson.build > @@ -11,6 +11,7 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: > files('sifive_e.c')) > riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) > riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) > riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: > files('microchip_pfsoc.c')) > +riscv_ss.add(when: 'CONFIG_TENSTORRENT', if_true: files('tt_atlantis.c')) > riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) > riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files( > 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', > 'riscv-iommu-hpm.c')) > -- > 2.47.3 > >
