diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 98b2a526..8d8003bb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -102,3 +102,7 @@ jobs: - name: Run API tests run: | ${{ env.TEST_OUTPUT_DIR }}/umd/api/api_tests + + - name: Run MISC tests + run: | + ${{ env.TEST_OUTPUT_DIR }}/umd/misc/umd_misc_tests diff --git a/common/semver.cpp b/common/semver.cpp new file mode 100644 index 00000000..0d6d553b --- /dev/null +++ b/common/semver.cpp @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: (c) 2024 Tenstorrent Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semver.hpp" + +#include + +#include +#include + +namespace tt::umd { + +static semver_t parse(const std::string& version_str) { + std::istringstream iss(version_str); + std::string token; + uint64_t major = 0; + uint64_t minor = 0; + uint64_t patch = 0; + + if (std::getline(iss, token, '.')) { + major = std::stoull(token); + + if (std::getline(iss, token, '.')) { + minor = std::stoull(token); + + if (std::getline(iss, token, '.')) { + patch = std::stoull(token); + } + } + } + return semver_t(major, minor, patch); +} + +semver_t::semver_t(uint64_t major, uint64_t minor, uint64_t patch) : major(major), minor(minor), patch(patch) {} + +semver_t::semver_t(const std::string& version_str) : semver_t(parse(version_str)) {} + +bool semver_t::operator<(const semver_t& other) const { + return std::tie(major, minor, patch) < std::tie(other.major, other.minor, other.patch); +} + +bool semver_t::operator>(const semver_t& other) const { return other < *this; } + +bool semver_t::operator==(const semver_t& other) const { + return std::tie(major, minor, patch) == std::tie(other.major, other.minor, other.patch); +} + +bool semver_t::operator!=(const semver_t& other) const { return !(*this == other); } + +bool semver_t::operator<=(const semver_t& other) const { return !(other < *this); } + +bool semver_t::operator>=(const semver_t& other) const { return !(*this < other); } + +std::string semver_t::to_string() const { return fmt::format("{}.{}.{}", major, minor, patch); } + +} // namespace tt::umd diff --git a/common/semver.hpp b/common/semver.hpp new file mode 100644 index 00000000..63896373 --- /dev/null +++ b/common/semver.hpp @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: (c) 2024 Tenstorrent Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +namespace tt::umd { + +/** + * Based on Semantic Versioning 2.0.0 (https://semver.org/) but more permissive. + * TT-KMD reports version strings that are technically not semver compliant. + */ +struct semver_t { + const uint64_t major; + const uint64_t minor; + const uint64_t patch; + + semver_t(uint64_t major, uint64_t minor, uint64_t patch); + semver_t(const std::string& version_str); + + bool operator<(const semver_t& other) const; + bool operator>(const semver_t& other) const; + bool operator==(const semver_t& other) const; + bool operator!=(const semver_t& other) const; + bool operator<=(const semver_t& other) const; + bool operator>=(const semver_t& other) const; + std::string to_string() const; +}; + +} // namespace tt::umd diff --git a/device/CMakeLists.txt b/device/CMakeLists.txt index fb5bd0bb..a6a30b9a 100644 --- a/device/CMakeLists.txt +++ b/device/CMakeLists.txt @@ -22,6 +22,7 @@ set(UMD_DEVICE_SRCS wormhole/wormhole_coordinate_manager.cpp pcie/pci_device.cpp hugepage.cpp + ../common/semver.cpp ) add_library(device SHARED ${UMD_DEVICE_SRCS}) diff --git a/device/pcie/pci_device.cpp b/device/pcie/pci_device.cpp index 95baa27c..82c8e037 100644 --- a/device/pcie/pci_device.cpp +++ b/device/pcie/pci_device.cpp @@ -14,6 +14,7 @@ #include // for fstat #include // for PCI_SLOT, PCI_FUNC + #include "pci_device.hpp" #include "ioctl.h" @@ -254,7 +255,10 @@ PCIDevice::PCIDevice(int pci_device_number, int logical_device_id) , revision(read_sysfs(info, "revision")) , arch(detect_arch(info.device_id, revision)) , architecture_implementation(tt::umd::architecture_implementation::create(arch)) + , kmd_version(read_kmd_version()) { + log_info(LogSiliconDriver, "Opened device {}; KMD version: {}", pci_device_num, kmd_version.to_string()); + struct { tenstorrent_query_mappings query_mappings; tenstorrent_mapping mapping_array[8]; @@ -700,3 +704,18 @@ void PCIDevice::print_file_contents(std::string filename, std::string hint){ } } } + +semver_t PCIDevice::read_kmd_version() { + static const std::string path = "/sys/module/tenstorrent/version"; + std::ifstream file(path); + + if (!file.is_open()) { + log_warning(LogSiliconDriver, "Failed to open file: {}", path); + return {0, 0, 0}; + } + + std::string version_str; + std::getline(file, version_str); + + return semver_t(version_str); +} diff --git a/device/pcie/pci_device.hpp b/device/pcie/pci_device.hpp index 8c436f78..04cfb198 100644 --- a/device/pcie/pci_device.hpp +++ b/device/pcie/pci_device.hpp @@ -16,20 +16,23 @@ #include "device/tt_arch_types.h" #include "device/tt_cluster_descriptor_types.h" #include "device/tlb.h" +#include "common/semver.hpp" + +#include "fmt/format.h" // TODO: this is used up in tt_silicon_driver.cpp but that logic ought to be // lowered into the PCIDevice class since it is specific to PCIe cards. // See /vendor_ip/synopsys/052021/bh_pcie_ctl_gen5/export/configuration/DWC_pcie_ctl.h static const uint64_t UNROLL_ATU_OFFSET_BAR = 0x1200; -// TODO: this is a bit of a hack... something to revisit when we formalize an +// TODO: this is a bit of a hack... something to revisit when we formalize an // abstraction for IO. // BAR0 size for Blackhole, used to determine whether write block should use BAR0 or BAR4 static const uint64_t BAR0_BH_SIZE = 512 * 1024 * 1024; constexpr unsigned int c_hang_read_value = 0xffffffffu; -namespace tt::umd { class architecture_implementation; } +namespace tt::umd { class architecture_implementation; struct semver_t; } struct dynamic_tlb { uint64_t bar_offset; // Offset that address is mapped to, within the PCI BAR. @@ -56,6 +59,9 @@ struct PciDeviceInfo // onto this struct as methods? e.g. current_link_width etc. }; +// Do we want to put everything into this file into tt::umd namespace? +using tt::umd::semver_t; + class PCIDevice { const std::string device_path; // Path to character device: /dev/tenstorrent/N const int pci_device_num; // N in /dev/tenstorrent/N @@ -65,6 +71,7 @@ class PCIDevice { const int numa_node; // -1 if non-NUMA const int revision; // PCI revision value from sysfs const tt::ARCH arch; // e.g. Grayskull, Wormhole, Blackhole + const semver_t kmd_version; // KMD version std::unique_ptr architecture_implementation; public: @@ -83,7 +90,7 @@ class PCIDevice { * * Opens the character device file descriptor, reads device information from * sysfs, and maps device memory region(s) into the process address space. - * + * * @param pci_device_number N in /dev/tenstorrent/N * @param logical_device_id unique identifier for this device in the network topology */ @@ -211,6 +218,7 @@ class PCIDevice { // For debug purposes when various stages fails. void print_file_contents(std::string filename, std::string hint = ""); + semver_t read_kmd_version(); + std::vector hugepage_mapping_per_channel; }; - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6829c91b..62ffcd7c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -62,6 +62,7 @@ if(MASTER_PROJECT) endif() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/api) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/pcie) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/misc) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/simulation) if($ENV{ARCH_NAME} STREQUAL "wormhole_b0") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/wormhole) diff --git a/tests/misc/CMakeLists.txt b/tests/misc/CMakeLists.txt new file mode 100644 index 00000000..fbde42df --- /dev/null +++ b/tests/misc/CMakeLists.txt @@ -0,0 +1,12 @@ +set(UMD_MISC_TESTS_SRCS test_semver.cpp) + +add_executable(umd_misc_tests ${UMD_MISC_TESTS_SRCS}) +target_link_libraries(umd_misc_tests PRIVATE test_common) +set_target_properties( + umd_misc_tests + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/test/umd/misc + OUTPUT_NAME + umd_misc_tests +) diff --git a/tests/misc/test_semver.cpp b/tests/misc/test_semver.cpp new file mode 100644 index 00000000..2a0217b7 --- /dev/null +++ b/tests/misc/test_semver.cpp @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: (c) 2024 Tenstorrent Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "common/semver.hpp" + +using tt::umd::semver_t; + +TEST(Semver, Valid) { + const std::map valid_test_cases = { + {"1.29", semver_t(1, 29, 0)}, // technically invalid, but seen from TT-KMD + {"1.28-bh2", semver_t(1, 28, 0)}, // technically invalid, but seen from TT-KMD + {"0.0.4", semver_t(0, 0, 4)}, + {"1.2.3", semver_t(1, 2, 3)}, + {"10.20.30", semver_t(10, 20, 30)}, + {"1.1.2-prerelease+meta", semver_t(1, 1, 2)}, + {"1.1.2+meta", semver_t(1, 1, 2)}, + {"1.1.2+meta-valid", semver_t(1, 1, 2)}, + {"1.0.0-alpha", semver_t(1, 0, 0)}, + {"1.0.0-beta", semver_t(1, 0, 0)}, + {"1.0.0-alpha.beta", semver_t(1, 0, 0)}, + {"1.0.0-alpha.beta.1", semver_t(1, 0, 0)}, + {"1.0.0-alpha.1", semver_t(1, 0, 0)}, + {"1.0.0-alpha0.valid", semver_t(1, 0, 0)}, + {"1.0.0-alpha.0valid", semver_t(1, 0, 0)}, + {"1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay", semver_t(1, 0, 0)}, + {"1.0.0-rc.1+build.1", semver_t(1, 0, 0)}, + {"2.0.0-rc.1+build.123", semver_t(2, 0, 0)}, + {"1.2.3-beta", semver_t(1, 2, 3)}, + {"10.2.3-DEV-SNAPSHOT", semver_t(10, 2, 3)}, + {"1.2.3-SNAPSHOT-123", semver_t(1, 2, 3)}, + {"1.0.0", semver_t(1, 0, 0)}, + {"2.0.0", semver_t(2, 0, 0)}, + {"1.1.7", semver_t(1, 1, 7)}, + {"2.0.0+build.1848", semver_t(2, 0, 0)}, + {"2.0.1-alpha.1227", semver_t(2, 0, 1)}, + {"1.0.0-alpha+beta", semver_t(1, 0, 0)}, + {"1.2.3----RC-SNAPSHOT.12.9.1--.12+788", semver_t(1, 2, 3)}, + {"1.2.3----R-S.12.9.1--.12+meta", semver_t(1, 2, 3)}, + {"1.2.3----RC-SNAPSHOT.12.9.1--.12", semver_t(1, 2, 3)}, + {"1.0.0+0.build.1-rc.10000aaa-kk-0.1", semver_t(1, 0, 0)}, + {"1.0.0-0A.is.legal", semver_t(1, 0, 0)} + }; + + for (const auto &[version_str, expected] : valid_test_cases) { + semver_t actual(version_str); + EXPECT_EQ(actual.major, expected.major); + EXPECT_EQ(actual.minor, expected.minor); + EXPECT_EQ(actual.patch, expected.patch); + } +} + +TEST(Semver, Invalid) { + std::vector invalid_test_cases = { + "+invalid", + "-invalid", + "-invalid+invalid", + "-invalid.01", + "alpha", + "alpha.beta", + "alpha.beta.1", + "alpha.1", + "alpha+beta", + "alpha_beta", + "alpha.", + "alpha..", + "beta", + "-alpha.", + "+justmeta", + }; + + for (const auto &version_str : invalid_test_cases) { + EXPECT_THROW(semver_t{version_str}, std::exception) << "'" << version_str << "' should be invalid"; + } +} +