diff --git a/.github/workflows/build-tenstorrent.yml b/.github/workflows/build-tenstorrent.yml
new file mode 100644
index 00000000..7bf1a2d3
--- /dev/null
+++ b/.github/workflows/build-tenstorrent.yml
@@ -0,0 +1,15 @@
+name: Build and Test Tenstorrent Backend
+
+on:
+ pull_request:
+ branches: [main, spatter-devel]
+ schedule:
+ - cron: '30 8 * * *'
+
+jobs:
+ build-tenstorrent:
+ runs-on: self-hosted
+ steps:
+ - uses: actions/checkout@v4
+ - name: Run batch file
+ run: cd tests/misc && chmod +x run-crnch-tenstorrent.sh && sbatch run-crnch-tenstorrent.sh
diff --git a/.gitignore b/.gitignore
index f1ab5ffc..cf890055 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ src/python/env
*.pyc
spatter-cuda-test.out
json.tar.xz
+generated/
diff --git a/AUTHORS b/AUTHORS
index c67dcbf4..ece1d0fd 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -20,3 +20,6 @@ Vincent Huang
James Wood
OneAPI backend support
+
+Roy Cheng
+ Tenstorrent backend
diff --git a/Build.md b/Build.md
index d65fdc26..34c2b8a7 100644
--- a/Build.md
+++ b/Build.md
@@ -28,4 +28,54 @@
* avx_crossplatform
* non_avx
* `-DUSE_MPI=1`
- * `-DUSE_PAPI=1`
\ No newline at end of file
+ * `-DUSE_PAPI=1`
+
+## Tenstorrent
+Blackhole and other Tenstorrent accelerators, via tt-metal. Enable with
+`-DUSE_TENSTORRENT=ON`.
+
+Device kernels are compiled at run time by tt-metal's vendored sfpi toolchain,
+so no extra device compiler is needed at build time. The host side needs a C++20
+compiler and `libtt_metal`.
+
+### Optional arguments
+* `-DTT_METAL_LIB_DIR=
` - directory holding `libtt_metal.so`
+* `-DTT_METAL_INCLUDE_DIRS=` - include roots for the host headers
+
+### Runtime environment
+* `TT_METAL_RUNTIME_ROOT` must point at the directory containing `tt_metal/`,
+ otherwise tt-metal aborts with "Root Directory is not set" before it opens a
+ device.
+* `TT_VISIBLE_DEVICES` selects which chip(s) to use on a multi-card host.
+
+### If you installed Tenstorrent support with a pip `ttnn` wheel
+The wheel ships `libtt_metal.so` and the device-kernel headers, but **not** the
+host API headers, so CMake will find the library and then report the headers
+missing. They can be assembled without root and without building tt-metal:
+
+```
+git clone --depth 1 --branch --filter=blob:none --sparse \
+ https://github.com/tenstorrent/tt-metal ~/ttmetal-src
+cd ~/ttmetal-src && git sparse-checkout set tt_metal tt_stl
+git submodule update --init --depth 1 tt_metal/third_party/umd
+```
+
+That covers `tt-metalium`, `tt_stl`, `hostdevcommon` and `umd`. The remaining
+four are header-only and are fetched by tt-metal's build rather than vendored,
+so clone them at the versions pinned in `~/ttmetal-src/third_party/CMakeLists.txt`:
+fmt, nlohmann/json, tt-logger and spdlog, plus enchantum. Pass every include
+root via `-DTT_METAL_INCLUDE_DIRS`.
+
+Do **not** run tt-metal's own CMake configure just to obtain these: it pulls
+system packages (boost, capnproto, protobuf) that require root, whereas the
+header-only clones do not.
+
+### Notes
+* Blackhole has no FP64 fabric. This does not affect Spatter, because gather and
+ scatter perform no arithmetic; each element is moved as an opaque 8-byte
+ payload.
+* The DRAM allocator aligns pages to 64 bytes, so a buffer with one 8-byte
+ element per page occupies 8x its logical size on device. Size `-l` accordingly.
+* `gather` and `scatter` are implemented. The `multi_*` and atomic variants are
+ not yet, and report so at run time.
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 606d85e1..a235f22a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,7 @@ include(pkgs/JSONSupport)
include(pkgs/MPISupport)
include(pkgs/OpenMPSupport)
include(pkgs/CUDASupport)
+include(pkgs/TenstorrentSupport)
if (APPLE)
set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
diff --git a/README.md b/README.md
index cfb944e2..455e1ad6 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ CMake is required to build Spatter. Currently we require CMake 3.25 or newer.
To build with CMake from the main source directory, use the following command structure:
```
-cmake -DCMAKE_BUILD_TYPE= -DUSE_=1 -B build_ -S .
+cmake -DCMAKE_BUILD_TYPE= -DUSE_=1 -B build_ -S .
cd build_
make
```
@@ -63,6 +63,12 @@ For CUDA builds, we normally load CUDA 11/12 using NVHPC:
```
cmake -DUSE_CUDA=1 -B build_cuda -S .
```
+
+For Tenstorrent builds (see `Build.md` for the header requirements):
+
+```
+cmake -DUSE_TENSTORRENT=ON -B build_tenstorrent -S .
+```
For a complete list of build options, see [Build.md](Build.md)
## Running Spatter
diff --git a/cmake/pkgs/TenstorrentSupport.cmake b/cmake/pkgs/TenstorrentSupport.cmake
new file mode 100644
index 00000000..5455a88e
--- /dev/null
+++ b/cmake/pkgs/TenstorrentSupport.cmake
@@ -0,0 +1,79 @@
+# Tenstorrent device kernels are compiled at run time by tt-metal's vendored
+# sfpi toolchain, so there is no device compiler to enable here; only the host
+# library matters at build time. There is no upstream FindTTMetal module.
+
+option(USE_TENSTORRENT "Enable support for Tenstorrent accelerators")
+
+if (USE_TENSTORRENT)
+ set(TT_METAL_INCLUDE_DIRS "" CACHE STRING
+ "Semicolon-separated include roots for tt-metal host headers")
+ set(TT_METAL_LIB_DIR "" CACHE PATH "Directory containing libtt_metal.so")
+
+ find_package(Python3 COMPONENTS Interpreter QUIET)
+ if (Python3_Interpreter_FOUND)
+ execute_process(
+ COMMAND ${Python3_EXECUTABLE} -c
+ "import ttnn, os; print(os.path.dirname(ttnn.__file__))"
+ OUTPUT_VARIABLE TTNN_DIR
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+ if (TTNN_DIR)
+ message(STATUS "Tenstorrent: found ttnn wheel at ${TTNN_DIR}")
+ endif()
+ endif()
+
+ find_library(TT_METAL_LIB
+ NAMES tt_metal
+ HINTS ${TT_METAL_LIB_DIR}
+ ${TTNN_DIR}/build/lib
+ $ENV{TT_METAL_HOME}/build/lib
+ $ENV{TT_METAL_RUNTIME_ROOT}/build/lib)
+
+ # Headers include each other as , so the root is the parent
+ # of tt-metalium/.
+ find_path(TT_METALIUM_INCLUDE_DIR
+ NAMES tt-metalium/host_api.hpp
+ HINTS ${TT_METAL_INCLUDE_DIRS}
+ $ENV{TT_METAL_HOME}/tt_metal/api
+ ${TTNN_DIR}/tt_metal/api)
+
+ if (TT_METAL_LIB AND TT_METALIUM_INCLUDE_DIR)
+ message(STATUS "Found libtt_metal: ${TT_METAL_LIB}")
+ message(STATUS "Found tt-metalium headers: ${TT_METALIUM_INCLUDE_DIR}")
+
+ set(CMAKE_CXX_STANDARD 20)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+ include_directories(${TT_METALIUM_INCLUDE_DIR})
+ if (TT_METAL_INCLUDE_DIRS)
+ include_directories(${TT_METAL_INCLUDE_DIRS})
+ endif()
+
+ set(COMMON_LINK_LIBRARIES ${COMMON_LINK_LIBRARIES} ${TT_METAL_LIB})
+ add_definitions(-DUSE_TENSTORRENT)
+ else()
+ if (TT_METAL_LIB AND NOT TT_METALIUM_INCLUDE_DIR)
+ message(STATUS
+ "Tenstorrent: libtt_metal was found but the host headers were not. "
+ "A pip `ttnn` wheel ships the library and the device-kernel headers "
+ "but no host API headers. Point -DTT_METAL_INCLUDE_DIRS at a matching "
+ "tt-metal checkout plus its header-only dependencies, e.g.\n"
+ " git clone --depth 1 --branch v0.72.0 --filter=blob:none --sparse \\\n"
+ " https://github.com/tenstorrent/tt-metal ~/ttmetal-src\n"
+ " cd ~/ttmetal-src && git sparse-checkout set tt_metal tt_stl\n"
+ " git submodule update --init --depth 1 tt_metal/third_party/umd\n"
+ "then clone fmt, nlohmann/json, tt-logger, spdlog and enchantum at the "
+ "versions pinned in that checkout's third_party/CMakeLists.txt and pass "
+ "every include root. Do NOT run tt-metal's own configure for this: it "
+ "pulls system packages (boost, capnproto, protobuf) that need root, "
+ "whereas the header-only clones do not.")
+ elseif (NOT TT_METAL_LIB)
+ message(STATUS
+ "Tenstorrent: libtt_metal not found. Install tt-metal, or pip install "
+ "ttnn, or set -DTT_METAL_LIB_DIR.")
+ endif()
+ message(FATAL_ERROR
+ "USE_TENSTORRENT=ON but no usable Tenstorrent installation was found. "
+ "See the diagnostic above, or configure without -DUSE_TENSTORRENT=ON.")
+ endif()
+endif()
diff --git a/src/Spatter/CMakeLists.txt b/src/Spatter/CMakeLists.txt
index 84329bee..1789398d 100644
--- a/src/Spatter/CMakeLists.txt
+++ b/src/Spatter/CMakeLists.txt
@@ -6,8 +6,19 @@ if (USE_CUDA)
set(CUDA_INCLUDE_FILES CudaBackend.hh)
endif()
+if (USE_TENSTORRENT)
+ add_library(tenstorrent_backend SHARED TenstorrentBackend.cc)
+ target_link_libraries(tenstorrent_backend PUBLIC ${TT_METAL_LIB})
+ target_compile_features(tenstorrent_backend PUBLIC cxx_std_20)
+ # tt-metal's headers pull in fmt and spdlog; use them header-only so the
+ # backend does not need those libraries at link time.
+ target_compile_definitions(tenstorrent_backend PRIVATE FMT_HEADER_ONLY)
+ set(TENSTORRENT_INCLUDE_FILES TenstorrentBackend.hh)
+endif()
+
set(SPATTER_INCLUDE_FILES
${CUDA_INCLUDE_FILES}
+ ${TENSTORRENT_INCLUDE_FILES}
Configuration.hh
Input.hh
JSONParser.hh
@@ -61,6 +72,10 @@ if (USE_CUDA)
set(COMMON_LINK_LIBRARIES ${COMMON_LINK_LIBRARIES} cuda_backend)
endif()
+if (USE_TENSTORRENT)
+ set(COMMON_LINK_LIBRARIES ${COMMON_LINK_LIBRARIES} tenstorrent_backend)
+endif()
+
target_link_libraries(Spatter
PUBLIC
${COMMON_LINK_LIBRARIES}
@@ -93,6 +108,12 @@ if (USE_CUDA)
ARCHIVE DESTINATION lib)
endif()
+if (USE_TENSTORRENT)
+ install (TARGETS tenstorrent_backend
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib)
+endif()
+
# Library/Header installation section
#set(ConfigPackageLocation lib/cmake/Spatter)
diff --git a/src/Spatter/Configuration.cc b/src/Spatter/Configuration.cc
index 7c7079a3..6b9f9e48 100644
--- a/src/Spatter/Configuration.cc
+++ b/src/Spatter/Configuration.cc
@@ -921,4 +921,150 @@ void Configuration::setup() {
}
#endif
+#ifdef USE_TENSTORRENT
+Configuration::Configuration(const size_t id,
+ const std::string name, const std::string kernel,
+ const aligned_vector &pattern,
+ const aligned_vector &pattern_gather,
+ const aligned_vector &pattern_scatter,
+ aligned_vector &sparse, double *&dev_sparse, size_t &sparse_size,
+ aligned_vector &sparse_gather, double *&dev_sparse_gather,
+ size_t &sparse_gather_size, aligned_vector &sparse_scatter,
+ double *&dev_sparse_scatter, size_t &sparse_scatter_size,
+ aligned_vector &dense,
+ aligned_vector> &dense_perthread, double *&dev_dense,
+ size_t &dense_size, const size_t delta, const size_t delta_gather,
+ const size_t delta_scatter, const long int seed, const size_t wrap,
+ const size_t count, const size_t shared_mem, const size_t local_work_size,
+ const unsigned long nruns, const bool aggregate, const bool atomic,
+ const unsigned long verbosity)
+ : ConfigurationBase(id, name, kernel, pattern, pattern_gather,
+ pattern_scatter, sparse, dev_sparse, sparse_size, sparse_gather,
+ dev_sparse_gather, sparse_gather_size, sparse_scatter,
+ dev_sparse_scatter, sparse_scatter_size, dense, dense_perthread,
+ dev_dense, dense_size, delta, delta_gather, delta_scatter, seed,
+ wrap, count, shared_mem, local_work_size, 1, nruns, aggregate, atomic,
+ false, false, verbosity),
+ dev_pattern(nullptr), dev_pattern_gather(nullptr),
+ dev_pattern_scatter(nullptr) {
+
+ setup();
+}
+
+Configuration::~Configuration() {
+ tt_device_free(dev_pattern);
+ tt_device_free(dev_pattern_gather);
+ tt_device_free(dev_pattern_scatter);
+
+ if (dev_sparse) {
+ tt_device_free(dev_sparse);
+ dev_sparse = nullptr;
+ }
+ if (dev_sparse_gather) {
+ tt_device_free(dev_sparse_gather);
+ dev_sparse_gather = nullptr;
+ }
+ if (dev_sparse_scatter) {
+ tt_device_free(dev_sparse_scatter);
+ dev_sparse_scatter = nullptr;
+ }
+ if (dev_dense) {
+ tt_device_free(dev_dense);
+ dev_dense = nullptr;
+ }
+}
+
+int Configuration::run(bool timed, unsigned long run_id) {
+ return ConfigurationBase::run(timed, run_id);
+}
+
+void Configuration::gather(
+ bool timed, unsigned long run_id) {
+ size_t pattern_length = pattern.size();
+
+#ifdef USE_MPI
+ MPI_Barrier(MPI_COMM_WORLD);
+#endif
+
+ // The wrapper is synchronous: it ends with Finish() on the mesh command
+ // queue, so no separate device synchronize is needed here.
+ float time_ms = tt_gather_wrapper(
+ dev_pattern, dev_sparse, dev_dense, pattern_length, delta, wrap, count);
+
+ if (timed)
+ time_seconds[run_id] = ((double)time_ms / 1000.0);
+}
+
+void Configuration::scatter(
+ bool timed, unsigned long run_id) {
+ size_t pattern_length = pattern.size();
+
+#ifdef USE_MPI
+ MPI_Barrier(MPI_COMM_WORLD);
+#endif
+
+ float time_ms = 0.0;
+
+ if (atomic)
+ time_ms = tt_scatter_atomic_wrapper(
+ dev_pattern, dev_sparse, dev_dense, pattern_length, delta, wrap, count);
+ else
+ time_ms = tt_scatter_wrapper(
+ dev_pattern, dev_sparse, dev_dense, pattern_length, delta, wrap, count);
+
+ if (time_ms < 0.0f) {
+ std::cerr << "Tenstorrent backend: scatter variant not implemented"
+ << std::endl;
+ return;
+ }
+
+ if (timed)
+ time_seconds[run_id] = ((double)time_ms / 1000.0);
+}
+
+// gather_scatter / multi_gather / multi_scatter are declared so the class is
+// concrete, but the corresponding wrappers return a negative sentinel in this
+// draft. Neither is exercised by the stream or ustride suites.
+void Configuration::gather_scatter(
+ bool timed, unsigned long run_id) {
+ (void)timed;
+ (void)run_id;
+ std::cerr << "Tenstorrent backend: gather_scatter not implemented"
+ << std::endl;
+}
+
+void Configuration::multi_gather(
+ bool timed, unsigned long run_id) {
+ (void)timed;
+ (void)run_id;
+ std::cerr << "Tenstorrent backend: multi_gather not implemented" << std::endl;
+}
+
+void Configuration::multi_scatter(
+ bool timed, unsigned long run_id) {
+ (void)timed;
+ (void)run_id;
+ std::cerr << "Tenstorrent backend: multi_scatter not implemented"
+ << std::endl;
+}
+
+void Configuration::setup() {
+ ConfigurationBase::setup();
+
+ // One page holds the whole pattern; the kernel reads page 0 once at start.
+ // Empty pattern vectors stay null -- a zero-byte allocation is an error.
+ auto upload = [](size_t *&handle, const aligned_vector &p) {
+ if (p.empty())
+ return;
+ const size_t bytes = p.size() * sizeof(uint32_t);
+ handle = static_cast(tt_device_alloc(bytes, bytes));
+ tt_pattern_upload(handle, p.data(), p.size());
+ };
+
+ upload(dev_pattern, pattern);
+ upload(dev_pattern_gather, pattern_gather);
+ upload(dev_pattern_scatter, pattern_scatter);
+}
+#endif
+
} // namespace Spatter
diff --git a/src/Spatter/Configuration.hh b/src/Spatter/Configuration.hh
index 1034463c..bb6ae799 100644
--- a/src/Spatter/Configuration.hh
+++ b/src/Spatter/Configuration.hh
@@ -42,6 +42,10 @@ inline void gpuAssert(
}
#endif
+
+#ifdef USE_TENSTORRENT
+#include "TenstorrentBackend.hh"
+#endif
#include "AlignedAllocator.hh"
#include "SpatterTypes.hh"
#include "Timer.hh"
@@ -242,6 +246,45 @@ public:
};
#endif
+#ifdef USE_TENSTORRENT
+template <> class Configuration : public ConfigurationBase {
+public:
+ Configuration(const size_t id, const std::string name,
+ const std::string kernel, const aligned_vector &pattern,
+ const aligned_vector &pattern_gather,
+ const aligned_vector &pattern_scatter,
+ aligned_vector &sparse, double *&dev_sparse, size_t &sparse_size,
+ aligned_vector &sparse_gather, double *&dev_sparse_gather,
+ size_t &sparse_gather_size, aligned_vector &sparse_scatter,
+ double *&dev_sparse_scatter, size_t &sparse_scatter_size,
+ aligned_vector &dense,
+ aligned_vector> &dense_perthread,
+ double *&dev_dense, size_t &dense_size, const size_t delta,
+ const size_t delta_gather, const size_t delta_scatter,
+ const long int seed, const size_t wrap, const size_t count,
+ const size_t shared_mem, const size_t local_work_size,
+ const unsigned long nruns, const bool aggregate, const bool atomic,
+ const unsigned long verbosity);
+
+ ~Configuration();
+
+ int run(bool timed, unsigned long run_id);
+ void gather(bool timed, unsigned long run_id);
+ void scatter(bool timed, unsigned long run_id);
+ void gather_scatter(bool timed, unsigned long run_id);
+ void multi_gather(bool timed, unsigned long run_id);
+ void multi_scatter(bool timed, unsigned long run_id);
+ void setup();
+
+public:
+ // Opaque MeshBuffer handles, typed as size_t* to match the wrapper
+ // signatures inherited from CudaBackend.hh. Never dereferenced on the host.
+ size_t *dev_pattern;
+ size_t *dev_pattern_gather;
+ size_t *dev_pattern_scatter;
+};
+#endif
+
} // namespace Spatter
#endif
diff --git a/src/Spatter/Input.hh b/src/Spatter/Input.hh
index 625a0d0a..7574444f 100644
--- a/src/Spatter/Input.hh
+++ b/src/Spatter/Input.hh
@@ -365,8 +365,10 @@ int parse_input(const int argc, char **argv, ClArgs &cl) {
[](unsigned char c) { return std::tolower(c); });
if ((backend.compare("serial") != 0) &&
- (backend.compare("openmp") != 0) && (backend.compare("cuda") != 0)) {
- std::cerr << "Valid Backends are: serial, openmp, cuda" << std::endl;
+ (backend.compare("openmp") != 0) && (backend.compare("cuda") != 0) &&
+ (backend.compare("tenstorrent") != 0)) {
+ std::cerr << "Valid Backends are: serial, openmp, cuda, tenstorrent"
+ << std::endl;
return -1;
}
if (backend.compare("openmp") == 0) {
@@ -379,6 +381,12 @@ int parse_input(const int argc, char **argv, ClArgs &cl) {
#ifndef USE_CUDA
std::cerr << "FAIL - CUDA Backend is not Enabled" << std::endl;
return -1;
+#endif
+ }
+ if (backend.compare("tenstorrent") == 0) {
+#ifndef USE_TENSTORRENT
+ std::cerr << "FAIL - Tenstorrent Backend is not Enabled" << std::endl;
+ return -1;
#endif
}
break;
@@ -533,6 +541,9 @@ int parse_input(const int argc, char **argv, ClArgs &cl) {
#ifdef USE_OPENMP
backend = "openmp";
#endif
+#ifdef USE_TENSTORRENT
+ backend = "tenstorrent";
+#endif
#ifdef USE_CUDA
backend = "cuda";
#endif
@@ -683,6 +694,17 @@ int parse_input(const int argc, char **argv, ClArgs &cl) {
cl.dense_perthread, cl.dev_dense, cl.dense_size, delta, delta_gather,
delta_scatter, seed, wrap, count, shared_mem, local_work_size, nruns,
aggregate, atomic, verbosity);
+#endif
+#ifdef USE_TENSTORRENT
+ else if (backend.compare("tenstorrent") == 0)
+ c = std::make_unique>(0,
+ config_name, kernel, pattern, pattern_gather, pattern_scatter,
+ cl.sparse, cl.dev_sparse, cl.sparse_size, cl.sparse_gather,
+ cl.dev_sparse_gather, cl.sparse_gather_size, cl.sparse_scatter,
+ cl.dev_sparse_scatter, cl.sparse_scatter_size, cl.dense,
+ cl.dense_perthread, cl.dev_dense, cl.dense_size, delta, delta_gather,
+ delta_scatter, seed, wrap, count, shared_mem, local_work_size, nruns,
+ aggregate, atomic, verbosity);
#endif
else {
std::cerr << "Invalid Backend " << backend << std::endl;
@@ -790,6 +812,23 @@ int parse_input(const int argc, char **argv, ClArgs &cl) {
checkCudaErrors(cudaDeviceSynchronize());
}
#endif
+#ifdef USE_TENSTORRENT
+ if (backend.compare("tenstorrent") == 0) {
+ auto alloc_and_copy = [](double *&dev, const aligned_vector &host) {
+ if (host.empty())
+ return;
+ const size_t bytes = sizeof(double) * host.size();
+ // page size == element size, so element index == DRAM page id
+ dev = static_cast(tt_device_alloc(bytes, sizeof(double)));
+ tt_memcpy_h2d(dev, host.data(), bytes);
+ };
+
+ alloc_and_copy(cl.dev_sparse, cl.sparse);
+ alloc_and_copy(cl.dev_sparse_gather, cl.sparse_gather);
+ alloc_and_copy(cl.dev_sparse_scatter, cl.sparse_scatter);
+ alloc_and_copy(cl.dev_dense, cl.dense);
+ }
+#endif
for (auto const &config : cl.configs) {
if (config->aggregate != aggregate) {
diff --git a/src/Spatter/JSONParser.cc b/src/Spatter/JSONParser.cc
index 54dfd970..27093fa9 100644
--- a/src/Spatter/JSONParser.cc
+++ b/src/Spatter/JSONParser.cc
@@ -288,6 +288,19 @@ std::unique_ptr JSONParser::operator[](
(*data_json_ptr)[index]["wrap"], (*data_json_ptr)[index]["count"],
shared_mem_, (*data_json_ptr)[index]["local-work-size"],
(*data_json_ptr)[index]["nruns"], aggregate_, atomic_, verbosity_);
+#endif
+#ifdef USE_TENSTORRENT
+ else if (backend_.compare("tenstorrent") == 0)
+ c = std::make_unique>(index,
+ (*data_json_ptr)[index]["name"], (*data_json_ptr)[index]["kernel"],
+ pattern, pattern_gather, pattern_scatter, sparse, dev_sparse,
+ sparse_size, sparse_gather, dev_sparse_gather, sparse_gather_size,
+ sparse_scatter, dev_sparse_scatter, sparse_scatter_size, dense,
+ dense_perthread, dev_dense, dense_size, delta, delta_gather,
+ delta_scatter, (*data_json_ptr)[index]["seed"],
+ (*data_json_ptr)[index]["wrap"], (*data_json_ptr)[index]["count"],
+ shared_mem_, (*data_json_ptr)[index]["local-work-size"],
+ (*data_json_ptr)[index]["nruns"], aggregate_, atomic_, verbosity_);
#endif
else {
std::cerr << "Invalid Backend " << backend_ << std::endl;
diff --git a/src/Spatter/SpatterTypes.hh b/src/Spatter/SpatterTypes.hh
index a486514c..52b679c1 100644
--- a/src/Spatter/SpatterTypes.hh
+++ b/src/Spatter/SpatterTypes.hh
@@ -11,6 +11,7 @@ namespace Spatter {
struct Serial {};
struct OpenMP {};
struct CUDA {};
+struct Tenstorrent {};
} // namespace Spatter
#endif
diff --git a/src/Spatter/TenstorrentBackend.cc b/src/Spatter/TenstorrentBackend.cc
new file mode 100644
index 00000000..bc6f94fe
--- /dev/null
+++ b/src/Spatter/TenstorrentBackend.cc
@@ -0,0 +1,438 @@
+// Tenstorrent backend, mirroring CudaBackend.cu. Requires tt-metal.
+//
+// Blackhole has no FP64 fabric. That does not matter here: gather and scatter
+// move bytes and do no arithmetic, so each double is treated as an opaque
+// 8-byte payload.
+
+#include "TenstorrentBackend.hh"
+
+#include
+#include
+#include
+#include
+#include