diff --git a/src/Targets/CMakeLists.txt b/src/Targets/CMakeLists.txt index d5335f33..b0239e85 100755 --- a/src/Targets/CMakeLists.txt +++ b/src/Targets/CMakeLists.txt @@ -24,6 +24,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/RiscV.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/RiscVTargetConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/TargetDescriptionFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/IsaDescriptor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/Wch/WchRiscV.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/Wch/TargetDescriptionFile.cpp ) diff --git a/src/Targets/RiscV/IsaDescriptor.cpp b/src/Targets/RiscV/IsaDescriptor.cpp new file mode 100644 index 00000000..4106a43d --- /dev/null +++ b/src/Targets/RiscV/IsaDescriptor.cpp @@ -0,0 +1,297 @@ +#include "IsaDescriptor.hpp" + +#include +#include + +#include "src/Services/StringService.hpp" + +#include "src/Exceptions/Exception.hpp" + +namespace Targets::RiscV +{ + using Exceptions::Exception; + + IsaDescriptor::IsaDescriptor(std::string_view isaString) + : IsaDescriptor(Services::StringService::asciiToLower(isaString), 0) + {} + + bool IsaDescriptor::hasExtension(IsaExtension extension) const { + return this->extensionDescriptorsByExtension.contains(extension); + } + + bool IsaDescriptor::isReduced() const { + return this->baseDescriptor.base == IsaBase::RV32E || this->baseDescriptor.base == IsaBase::RV64E; + } + + std::optional> IsaDescriptor::tryGetExtensionDescriptor( + IsaExtension extension + ) const { + const auto descriptorIt = this->extensionDescriptorsByExtension.find(extension); + if (descriptorIt == this->extensionDescriptorsByExtension.end()) { + return std::nullopt; + } + + return std::cref(descriptorIt->second); + } + + const IsaExtensionDescriptor& IsaDescriptor::getExtensionDescriptor(IsaExtension extension) const { + const auto descriptor = this->tryGetExtensionDescriptor(extension); + if (!descriptor.has_value()) { + throw Exception{"Failed to get extension descriptor in RISC-V ISA descriptor - extension not found"}; + } + + return descriptor->get(); + } + + IsaDescriptor::IsaDescriptor(std::string_view isaStringLower, std::size_t stringOffset) + : baseDescriptor(IsaDescriptor::extractBaseDescriptorFromIsaString(isaStringLower, stringOffset)) + , extensionDescriptorsByExtension( + IsaDescriptor::extractExtensionDescriptorsFromIsaString(isaStringLower, stringOffset) + ) + {} + + IsaBaseDescriptor IsaDescriptor::extractBaseDescriptorFromIsaString( + std::string_view isaStringLower, + std::size_t& stringOffset + ) { + struct BriefBaseDescriptor + { + IsaBase base; + std::string_view name; + IsaVersionNumber defaultVersion = {2, 0}; + }; + + static constexpr auto briefDescriptors = std::to_array({ + {.base = IsaBase::RV32I, .name = "rv32i"}, + {.base = IsaBase::RV32E, .name = "rv32e"}, + {.base = IsaBase::RV64I, .name = "rv64i"}, + {.base = IsaBase::RV64E, .name = "rv64e"}, + }); + + for (const auto& briefDescriptor : briefDescriptors) { + if (isaStringLower.find(briefDescriptor.name) == 0) { + stringOffset += briefDescriptor.name.length(); + + return IsaBaseDescriptor{ + .base = briefDescriptor.base, + .versionNumber = IsaDescriptor::extractVersionNumberFromIsaString( + isaStringLower, + stringOffset + ).value_or(briefDescriptor.defaultVersion) + }; + } + } + + throw Exception{"Failed to extract RISC-V ISA base from ISA string \"" + std::string{isaStringLower} + "\""}; + } + + std::unordered_map IsaDescriptor::extractExtensionDescriptorsFromIsaString( + std::string_view isaStringLower, + size_t& stringOffset + ) { + struct BriefExtensionDescriptor + { + IsaExtension extension; + std::string_view name; + IsaVersionNumber defaultVersion = {2, 0}; + }; + + static constexpr auto singleLetterBriefDescriptors = std::to_array({ + {.extension = IsaExtension::INTEGER_MULTIPLICATION_DIVISION, .name = "m"}, + {.extension = IsaExtension::ATOMICS, .name = "a"}, + {.extension = IsaExtension::SINGLE_PRECISION_FLOATING_POINT, .name = "f"}, + {.extension = IsaExtension::DOUBLE_PRECISION_FLOATING_POINT, .name = "d"}, + {.extension = IsaExtension::BIT_MANIPULATION, .name = "b"}, + {.extension = IsaExtension::COMPRESSED_INSTRUCTIONS, .name = "c"}, + {.extension = IsaExtension::CRYPTOGRAPHY, .name = "k"}, + }); + + auto output = std::unordered_map{}; + + const auto commitMultiLetterExtension = [&output] (std::string_view extensionName) { + static constexpr auto multiLetterBriefDescriptors = std::to_array({ + {.extension = IsaExtension::CSR_INSTRUCTIONS, .name = "zicsr"}, + {.extension = IsaExtension::INSTRUCTION_FETCH_FENCE, .name = "zifencei"}, + }); + + for (const auto& briefDescriptor : multiLetterBriefDescriptors) { + if (extensionName.find(briefDescriptor.name) == 0) { + auto offset = briefDescriptor.name.size(); + output.emplace( + briefDescriptor.extension, + IsaExtensionDescriptor{ + .extension = briefDescriptor.extension, + .versionNumber = IsaDescriptor::extractVersionNumberFromIsaString( + extensionName, + offset + ).value_or(briefDescriptor.defaultVersion), + } + ); + + return; + } + } + }; + + auto multiLetterExtension = std::optional{}; + while (stringOffset <= (isaStringLower.length() - 1)) { + const auto character = isaStringLower.at(stringOffset); + + if (multiLetterExtension.has_value()) { + if (character == '_') { + commitMultiLetterExtension(*multiLetterExtension); + multiLetterExtension = std::nullopt; + } + + multiLetterExtension->push_back(character); + ++stringOffset; + continue; + } + + for (const auto& briefDescriptor : singleLetterBriefDescriptors) { + if (character == briefDescriptor.name.at(0)) { + output.emplace( + briefDescriptor.extension, + IsaExtensionDescriptor{ + .extension = briefDescriptor.extension, + .versionNumber = IsaDescriptor::extractVersionNumberFromIsaString( + isaStringLower, + ++stringOffset + ).value_or(briefDescriptor.defaultVersion), + } + ); + + goto CONTINUE_OUTER; + } + } + + if (character == 'g') { + const auto versionNumber = IsaDescriptor::extractVersionNumberFromIsaString( + isaStringLower, + ++stringOffset + ).value_or(IsaVersionNumber{2, 0}); + + output.emplace( + IsaExtension::INTEGER_MULTIPLICATION_DIVISION, + IsaExtensionDescriptor{ + .extension = IsaExtension::INTEGER_MULTIPLICATION_DIVISION, + .versionNumber = versionNumber + } + ); + + output.emplace( + IsaExtension::ATOMICS, + IsaExtensionDescriptor{.extension = IsaExtension::ATOMICS, .versionNumber = versionNumber} + ); + + output.emplace( + IsaExtension::DOUBLE_PRECISION_FLOATING_POINT, + IsaExtensionDescriptor{ + .extension = IsaExtension::DOUBLE_PRECISION_FLOATING_POINT, + .versionNumber = versionNumber + } + ); + + output.emplace( + IsaExtension::INSTRUCTION_FETCH_FENCE, + IsaExtensionDescriptor{ + .extension = IsaExtension::INSTRUCTION_FETCH_FENCE, + .versionNumber = versionNumber + } + ); + + continue; + } + + if (character == 'z' || character == 'x') { + multiLetterExtension = std::string{character}; + ++stringOffset; + continue; + } + + ++stringOffset; + + CONTINUE_OUTER: + continue; + } + + if (multiLetterExtension.has_value()) { + commitMultiLetterExtension(*multiLetterExtension); + } + + // Finally, handle implied extensions + if ( + output.contains(IsaExtension::DOUBLE_PRECISION_FLOATING_POINT) + && !output.contains(IsaExtension::SINGLE_PRECISION_FLOATING_POINT) + ) { + output.emplace( + IsaExtension::SINGLE_PRECISION_FLOATING_POINT, + IsaExtensionDescriptor{ + .extension = IsaExtension::SINGLE_PRECISION_FLOATING_POINT, + .versionNumber = {2, 0} + } + ); + } + + if ( + output.contains(IsaExtension::SINGLE_PRECISION_FLOATING_POINT) + && !output.contains(IsaExtension::CSR_INSTRUCTIONS) + ) { + output.emplace( + IsaExtension::CSR_INSTRUCTIONS, + IsaExtensionDescriptor{.extension = IsaExtension::CSR_INSTRUCTIONS, .versionNumber = {2, 0}} + ); + } + + return output; + } + + std::optional IsaDescriptor::extractVersionNumberFromIsaString( + std::string_view isaStringLower, + size_t& stringOffset + ) { + static constexpr auto getNextDigitSequence = [] (std::string_view string, std::size_t offset) { + auto sequence = std::string{}; + + for (auto i = offset; i <= (string.length() - 1); ++i) { + const auto character = string.at(i); + if (!std::isdigit(character)) { + break; + } + + sequence.push_back(character); + } + + return !sequence.empty() ? std::optional{sequence} : std::nullopt; + }; + + if (isaStringLower.empty()) { + return std::nullopt; + } + + const auto majorString = getNextDigitSequence(isaStringLower, stringOffset); + if (!majorString.has_value()) { + return std::nullopt; + } + + stringOffset += majorString->length(); + + auto output = IsaVersionNumber{ + .major = static_cast(std::stoul(*majorString, nullptr, 10)), + .minor = 0 + }; + + if ((stringOffset + 1) > (isaStringLower.length() - 1) || isaStringLower.at(stringOffset) != 'p') { + // The ISA string cannot accommodate a minor number, or the 'p' delimiter is missing + return output; + } + + const auto minorString = getNextDigitSequence(isaStringLower, stringOffset + 1); + if (minorString.has_value()) { + output.minor = static_cast(std::stoul(*minorString, nullptr, 10)); + stringOffset += minorString->length() + 1; + } + + return output; + } +} diff --git a/src/Targets/RiscV/IsaDescriptor.hpp b/src/Targets/RiscV/IsaDescriptor.hpp new file mode 100644 index 00000000..c4d80630 --- /dev/null +++ b/src/Targets/RiscV/IsaDescriptor.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Targets::RiscV +{ + enum class IsaBase: std::uint8_t + { + RV32I, + RV32E, + RV64I, + RV64E, + }; + + /** + * I've not covered all standard extensions here, as I really don't think Bloom will ever need them. If that ever + * changes, I will add the others as and when I need them. + */ + enum class IsaExtension: std::uint8_t + { + INTEGER_MULTIPLICATION_DIVISION, + ATOMICS, + SINGLE_PRECISION_FLOATING_POINT, + DOUBLE_PRECISION_FLOATING_POINT, + BIT_MANIPULATION, + COMPRESSED_INSTRUCTIONS, + CRYPTOGRAPHY, + CSR_INSTRUCTIONS, + INSTRUCTION_FETCH_FENCE, + }; + + struct IsaVersionNumber + { + std::uint16_t major = 0; + std::uint16_t minor = 0; + }; + + struct IsaBaseDescriptor + { + IsaBase base; + IsaVersionNumber versionNumber; + }; + + struct IsaExtensionDescriptor + { + IsaExtension extension; + IsaVersionNumber versionNumber; + }; + + struct IsaDescriptor + { + IsaBaseDescriptor baseDescriptor; + std::unordered_map extensionDescriptorsByExtension; + + explicit IsaDescriptor(std::string_view isaString); + + bool hasExtension(IsaExtension extension) const; + bool isReduced() const; + + std::optional> tryGetExtensionDescriptor( + IsaExtension extension + ) const; + const IsaExtensionDescriptor& getExtensionDescriptor(IsaExtension extension) const; + + private: + IsaDescriptor(std::string_view isaStringLower, std::size_t stringOffset); + + static IsaBaseDescriptor extractBaseDescriptorFromIsaString( + std::string_view isaStringLower, + std::size_t& stringOffset + ); + static std::unordered_map extractExtensionDescriptorsFromIsaString( + std::string_view isaStringLower, + std::size_t& stringOffset + ); + static std::optional extractVersionNumberFromIsaString( + std::string_view isaStringLower, + std::size_t& stringOffset + ); + }; +} diff --git a/src/Targets/RiscV/RiscV.cpp b/src/Targets/RiscV/RiscV.cpp index acf9c1e6..fec75581 100644 --- a/src/Targets/RiscV/RiscV.cpp +++ b/src/Targets/RiscV/RiscV.cpp @@ -21,11 +21,13 @@ namespace Targets::RiscV ) : targetConfig(RiscVTargetConfig{targetConfig}) , targetDescriptionFile(targetDescriptionFile) + , isaDescriptor(this->targetDescriptionFile.getIsaDescriptor()) , cpuRegisterAddressSpaceDescriptor(RiscV::generateCpuRegisterAddressSpaceDescriptor()) , csrMemorySegmentDescriptor(this->cpuRegisterAddressSpaceDescriptor.getMemorySegmentDescriptor("cs_registers")) , gprMemorySegmentDescriptor(this->cpuRegisterAddressSpaceDescriptor.getMemorySegmentDescriptor("gp_registers")) , cpuPeripheralDescriptor( RiscV::generateCpuPeripheralDescriptor( + this->isaDescriptor, this->cpuRegisterAddressSpaceDescriptor, this->csrMemorySegmentDescriptor, this->gprMemorySegmentDescriptor @@ -401,6 +403,7 @@ namespace Targets::RiscV } TargetPeripheralDescriptor RiscV::generateCpuPeripheralDescriptor( + const IsaDescriptor& isaDescriptor, const TargetAddressSpaceDescriptor& addressSpaceDescriptor, const TargetMemorySegmentDescriptor& csrMemorySegmentDescriptor, const TargetMemorySegmentDescriptor& gprMemorySegmentDescriptor @@ -427,7 +430,7 @@ namespace Targets::RiscV } ).first->second; - for (auto i = std::uint8_t{0}; i <= 31; ++i) { + for (auto i = std::uint8_t{0}; i <= static_cast(isaDescriptor.isReduced() ? 15 : 31); ++i) { const auto key = "x" + std::to_string(i); gprGroup.registerDescriptorsByKey.emplace( key, diff --git a/src/Targets/RiscV/RiscV.hpp b/src/Targets/RiscV/RiscV.hpp index 67a3e2ea..863a054d 100644 --- a/src/Targets/RiscV/RiscV.hpp +++ b/src/Targets/RiscV/RiscV.hpp @@ -9,6 +9,7 @@ #include "RiscVTargetConfig.hpp" #include "TargetDescriptionFile.hpp" +#include "IsaDescriptor.hpp" #include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp" @@ -93,6 +94,7 @@ namespace Targets::RiscV protected: RiscVTargetConfig targetConfig; TargetDescriptionFile targetDescriptionFile; + IsaDescriptor isaDescriptor; DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* riscVDebugInterface = nullptr; @@ -135,6 +137,7 @@ namespace Targets::RiscV static TargetAddressSpaceDescriptor generateCpuRegisterAddressSpaceDescriptor(); static TargetPeripheralDescriptor generateCpuPeripheralDescriptor( + const IsaDescriptor& isaDescriptor, const TargetAddressSpaceDescriptor& addressSpaceDescriptor, const TargetMemorySegmentDescriptor& csrMemorySegmentDescriptor, const TargetMemorySegmentDescriptor& gprMemorySegmentDescriptor diff --git a/src/Targets/RiscV/TargetDescriptionFile.cpp b/src/Targets/RiscV/TargetDescriptionFile.cpp index d8996d7d..c269762b 100644 --- a/src/Targets/RiscV/TargetDescriptionFile.cpp +++ b/src/Targets/RiscV/TargetDescriptionFile.cpp @@ -13,4 +13,8 @@ namespace Targets::RiscV TargetAddressSpaceDescriptor TargetDescriptionFile::getSystemAddressSpaceDescriptor() const { return this->targetAddressSpaceDescriptorFromAddressSpace(this->getSystemAddressSpace()); } + + IsaDescriptor TargetDescriptionFile::getIsaDescriptor() const { + return IsaDescriptor{this->getDeviceAttribute("architecture")}; + } } diff --git a/src/Targets/RiscV/TargetDescriptionFile.hpp b/src/Targets/RiscV/TargetDescriptionFile.hpp index df382d9f..248ddcf3 100644 --- a/src/Targets/RiscV/TargetDescriptionFile.hpp +++ b/src/Targets/RiscV/TargetDescriptionFile.hpp @@ -2,6 +2,8 @@ #include "src/Targets/TargetDescription/TargetDescriptionFile.hpp" +#include "IsaDescriptor.hpp" + namespace Targets::RiscV { /** @@ -15,7 +17,7 @@ namespace Targets::RiscV explicit TargetDescriptionFile(const std::string& xmlFilePath); [[nodiscard]] const TargetDescription::AddressSpace& getSystemAddressSpace() const; - [[nodiscard]] TargetAddressSpaceDescriptor getSystemAddressSpaceDescriptor() const; + [[nodiscard]] IsaDescriptor getIsaDescriptor() const; }; }