diff --git a/src/Services/PathService.hpp b/src/Services/PathService.hpp index 91b970b9..534501d7 100644 --- a/src/Services/PathService.hpp +++ b/src/Services/PathService.hpp @@ -51,6 +51,15 @@ namespace Services return PathService::projectDirPath() + "/.bloom"; } + /** + * Returns the path to the directory containing Bloom's target description files. + * + * @return + */ + static std::string targetDescriptionDirPath() { + return PathService::resourcesDirPath() + "/TargetDescriptionFiles"; + } + /** * Returns the path to the current project's settings file. * diff --git a/src/TargetController/TargetControllerComponent.cpp b/src/TargetController/TargetControllerComponent.cpp index 87f565ca..57b3c0a0 100644 --- a/src/TargetController/TargetControllerComponent.cpp +++ b/src/TargetController/TargetControllerComponent.cpp @@ -4,8 +4,12 @@ #include #include +#include "src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp" +#include "src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp" + #include "Responses/Error.hpp" +#include "src/Services/PathService.hpp" #include "src/Services/ProcessService.hpp" #include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" @@ -371,32 +375,30 @@ namespace TargetController }; } - std::map< - std::string, - std::function(const TargetConfig&)> - > TargetControllerComponent::getSupportedTargets() { - using Avr8TargetDescriptionFile = Targets::Microchip::Avr::Avr8Bit::TargetDescription::TargetDescriptionFile; + std::unique_ptr TargetControllerComponent::constructTargetFromBrief( + const TargetDescription::GeneratedMapping::BriefTargetDescriptor &targetBrief + ) { + using Services::PathService; - auto mapping = std::map(const TargetConfig&)>>(); - - // Include all targets from AVR8 target description files - const auto avr8PdMapping = Avr8TargetDescriptionFile::getTargetDescriptionMapping(); - - for (auto mapIt = avr8PdMapping.begin(); mapIt != avr8PdMapping.end(); ++mapIt) { - const auto mappingObject = mapIt.value().toObject(); - const auto targetName = mappingObject.find("name").value().toString().toLower().toStdString(); - - if (!mapping.contains(targetName)) { - mapping.insert({ - targetName, - [targetName] (const TargetConfig& targetConfig) { - return std::make_unique(targetConfig); - } - }); - } + if (targetBrief.targetFamily == TargetFamily::AVR_8) { + return std::make_unique( + this->environmentConfig.targetConfig, + Microchip::Avr::Avr8Bit::TargetDescription::TargetDescriptionFile( + PathService::targetDescriptionDirPath() + "/" + targetBrief.relativeTdfPath + ) + ); } - return mapping; + if (targetBrief.targetFamily == TargetFamily::RISC_V) { + return std::make_unique( + this->environmentConfig.targetConfig, + RiscV::TargetDescription::TargetDescriptionFile( + PathService::targetDescriptionDirPath() + "/" + targetBrief.relativeTdfPath + ) + ); + } + + throw Exception("Cannot construct target instance - invalid target family in BriefTargetDescriptor"); } void TargetControllerComponent::processQueuedCommands() { @@ -468,10 +470,10 @@ namespace TargetController const auto& targetName = this->environmentConfig.targetConfig.name; static const auto supportedDebugTools = this->getSupportedDebugTools(); - static const auto supportedTargets = this->getSupportedTargets(); + static const auto targetsByConfigValue = TargetDescription::TargetDescriptionFile::mapping(); const auto debugToolIt = supportedDebugTools.find(debugToolName); - const auto targetIt = supportedTargets.find(targetName); + const auto targetBriefIt = targetsByConfigValue.find(targetName); if (debugToolIt == supportedDebugTools.end()) { throw Exceptions::InvalidConfig( @@ -479,7 +481,7 @@ namespace TargetController ); } - if (targetIt == supportedTargets.end()) { + if (targetBriefIt == targetsByConfigValue.end()) { throw Exceptions::InvalidConfig( "Target name (\"" + targetName + "\") not recognised. Please check your configuration!" ); @@ -495,7 +497,7 @@ namespace TargetController Logger::info("Debug tool serial: " + this->debugTool->getSerialNumber()); Logger::info("Debug tool firmware version: " + this->debugTool->getFirmwareVersionString()); - this->target = targetIt->second(this->environmentConfig.targetConfig); + this->target = this->constructTargetFromBrief(targetBriefIt->second); const auto& targetDescriptor = this->getTargetDescriptor(); if (!this->target->supportsDebugTool(this->debugTool.get())) { diff --git a/src/TargetController/TargetControllerComponent.hpp b/src/TargetController/TargetControllerComponent.hpp index 317ccb57..06db0d87 100644 --- a/src/TargetController/TargetControllerComponent.hpp +++ b/src/TargetController/TargetControllerComponent.hpp @@ -246,12 +246,14 @@ namespace TargetController std::map()>> getSupportedDebugTools(); /** - * Constructs a mapping of supported target names to lambdas. The lambdas should instantiate and return an - * instance to the appropriate Target class. + * Constructs a Target instance from a BriefTargetDescriptor object. * + * @param targetBrief * @return */ - std::map(const TargetConfig&)>> getSupportedTargets(); + std::unique_ptr constructTargetFromBrief( + const Targets::TargetDescription::GeneratedMapping::BriefTargetDescriptor& targetBrief + ); /** * Processes any pending commands in the queue. diff --git a/src/Targets/CMakeLists.txt b/src/Targets/CMakeLists.txt index 5c872714..c5f77f39 100755 --- a/src/Targets/CMakeLists.txt +++ b/src/Targets/CMakeLists.txt @@ -10,4 +10,5 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/RiscV.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/TargetDescription/TargetDescriptionFile.cpp ) diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp index fe3342c9..70fb55d3 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp @@ -18,9 +18,9 @@ namespace Targets::Microchip::Avr::Avr8Bit { using namespace Exceptions; - Avr8::Avr8(const TargetConfig& targetConfig) + Avr8::Avr8(const TargetConfig& targetConfig, TargetDescription::TargetDescriptionFile&& targetDescriptionFile) : targetConfig(Avr8TargetConfig(targetConfig)) - , targetDescriptionFile(TargetDescription::TargetDescriptionFile(this->targetConfig.name)) + , targetDescriptionFile(std::move(targetDescriptionFile)) , signature(this->targetDescriptionFile.getTargetSignature()) , name(this->targetDescriptionFile.getTargetName()) , family(this->targetDescriptionFile.getAvrFamily()) diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp index af592789..d27d68fc 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp @@ -30,7 +30,10 @@ namespace Targets::Microchip::Avr::Avr8Bit class Avr8: public Target { public: - explicit Avr8(const TargetConfig& targetConfig); + explicit Avr8( + const TargetConfig& targetConfig, + TargetDescription::TargetDescriptionFile&& targetDescriptionFile + ); /* * The functions below implement the Target interface for AVR8 targets. diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp index 799f2fce..9997cc06 100644 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp @@ -1,9 +1,5 @@ #include "TargetDescriptionFile.hpp" -#include -#include -#include - #include "src/Services/PathService.hpp" #include "src/Logger/Logger.hpp" @@ -22,54 +18,15 @@ namespace Targets::Microchip::Avr::Avr8Bit::TargetDescription using Targets::TargetVariant; using Targets::TargetRegisterDescriptor; - TargetDescriptionFile::TargetDescriptionFile(const std::string& targetName) { - const auto mapping = TargetDescriptionFile::getTargetDescriptionMapping(); - const auto descriptionFileObjectIt = mapping.find(QString::fromStdString(targetName).toLower()); - - if (descriptionFileObjectIt == mapping.end()) { - throw Exception( - "Failed to resolve target description file for target \"" + targetName + "\" - unknown target name." - ); - } - - const auto descriptionFileObject = descriptionFileObjectIt.value().toObject(); - const auto descriptionFilePath = QString::fromStdString( - Services::PathService::resourcesDirPath()) + "/" + descriptionFileObject.find("tdfPath")->toString(); - - Logger::debug("Loading AVR8 target description file: " + descriptionFilePath.toStdString()); - - Targets::TargetDescription::TargetDescriptionFile::init(descriptionFilePath); - } - - void TargetDescriptionFile::init(const QDomDocument& document) { - Targets::TargetDescription::TargetDescriptionFile::init(document); - - const auto device = document.elementsByTagName("device").item(0).toElement(); - if (!device.isElement()) { - throw TargetDescriptionParsingFailureException("Device element not found."); - } - - this->avrFamilyName = device.attributes().namedItem("avr-family").nodeValue().toLower().toStdString(); - + TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) + : Targets::TargetDescription::TargetDescriptionFile(xmlFilePath) + { this->loadSupportedPhysicalInterfaces(); this->loadPadDescriptors(); this->loadTargetVariants(); this->loadTargetRegisterDescriptors(); } - QJsonObject TargetDescriptionFile::getTargetDescriptionMapping() { - auto mappingFile = QFile( - QString::fromStdString(Services::PathService::resourcesDirPath() + "/TargetDescriptionFiles/AVR/Mapping.json") - ); - - if (!mappingFile.exists()) { - throw Exception("Failed to load AVR target description mapping - mapping file not found"); - } - - mappingFile.open(QIODevice::ReadOnly); - return QJsonDocument::fromJson(mappingFile.readAll()).object(); - } - TargetSignature TargetDescriptionFile::getTargetSignature() const { const auto& propertyGroups = this->propertyGroupsMappedByName; @@ -110,11 +67,9 @@ namespace Targets::Microchip::Avr::Avr8Bit::TargetDescription Family TargetDescriptionFile::getAvrFamily() const { static const auto targetFamiliesByName = TargetDescriptionFile::getFamilyNameToEnumMapping(); - if (this->avrFamilyName.empty()) { - throw Exception("Could not find target family name in target description file."); - } - - const auto familyIt = targetFamiliesByName.find(this->avrFamilyName); + const auto familyIt = targetFamiliesByName.find( + QString::fromStdString(this->deviceAttribute("avr-family")).toLower().toStdString() + ); if (familyIt == targetFamiliesByName.end()) { throw Exception("Unknown family name in target description file."); diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp index d27e15ca..be5935bd 100644 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp @@ -33,27 +33,13 @@ namespace Targets::Microchip::Avr::Avr8Bit::TargetDescription class TargetDescriptionFile: public Targets::TargetDescription::TargetDescriptionFile { public: - /** - * Will resolve the target description file using the target description JSON mapping and a given target name. - * - * @param targetName - */ - TargetDescriptionFile(const std::string& targetName); - /** * Extends TDF initialisation to include the loading of physical interfaces for debugging AVR8 targets, among * other things. * * @param xml */ - void init(const QDomDocument& document) override; - - /** - * Loads the AVR8 target description JSON mapping file. - * - * @return - */ - static QJsonObject getTargetDescriptionMapping(); + explicit TargetDescriptionFile(const std::string& xmlFilePath); /** * Extracts the AVR8 target signature from the TDF. diff --git a/src/Targets/RiscV/RiscV.cpp b/src/Targets/RiscV/RiscV.cpp index 3f8b9124..48e97944 100644 --- a/src/Targets/RiscV/RiscV.cpp +++ b/src/Targets/RiscV/RiscV.cpp @@ -27,8 +27,11 @@ namespace Targets::RiscV using DebugModule::Registers::AbstractControlStatusRegister; using DebugModule::Registers::AbstractCommandRegister; - RiscV::RiscV(const TargetConfig& targetConfig) - : name("CH32X035C8T6") // TODO: TDF + RiscV::RiscV( + const TargetConfig& targetConfig, + TargetDescription::TargetDescriptionFile&& targetDescriptionFile + ) + : targetDescriptionFile(targetDescriptionFile) , stackPointerRegisterDescriptor( RiscVRegisterDescriptor( TargetRegisterType::STACK_POINTER, @@ -107,10 +110,10 @@ namespace Targets::RiscV TargetDescriptor RiscV::getDescriptor() { return TargetDescriptor( - "TDF ID", + this->targetDescriptionFile.getTargetId(), TargetFamily::RISC_V, - this->name, - "TDF VENDOR NAME", + this->targetDescriptionFile.getTargetName(), + this->targetDescriptionFile.getVendorName(), { { Targets::TargetMemoryType::FLASH, diff --git a/src/Targets/RiscV/RiscV.hpp b/src/Targets/RiscV/RiscV.hpp index 5af469c7..a1a938e9 100644 --- a/src/Targets/RiscV/RiscV.hpp +++ b/src/Targets/RiscV/RiscV.hpp @@ -7,6 +7,8 @@ #include "src/Targets/Target.hpp" #include "src/DebugToolDrivers/DebugTool.hpp" +#include "TargetDescription/TargetDescriptionFile.hpp" + #include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp" #include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp" @@ -27,7 +29,10 @@ namespace Targets::RiscV class RiscV: public Target { public: - explicit RiscV(const TargetConfig& targetConfig); + explicit RiscV( + const TargetConfig& targetConfig, + TargetDescription::TargetDescriptionFile&& targetDescriptionFile + ); /* * The functions below implement the Target interface for RISC-V targets. @@ -99,7 +104,8 @@ namespace Targets::RiscV bool programmingModeEnabled() override; protected: - std::string name; + TargetDescription::TargetDescriptionFile targetDescriptionFile; + std::map registerDescriptorsById; RiscVRegisterDescriptor stackPointerRegisterDescriptor; diff --git a/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.cpp new file mode 100644 index 00000000..16c97052 --- /dev/null +++ b/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.cpp @@ -0,0 +1,18 @@ +#include "TargetDescriptionFile.hpp" + +#include + +namespace Targets::RiscV::TargetDescription +{ + TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) + : Targets::TargetDescription::TargetDescriptionFile(xmlFilePath) + {} + + std::string TargetDescriptionFile::getTargetId() const { + return this->deviceAttribute("id"); + } + + std::string TargetDescriptionFile::getVendorName() const { + return this->deviceAttribute("vendor"); + } +} diff --git a/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp new file mode 100644 index 00000000..569877b0 --- /dev/null +++ b/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp" + +#include "src/Targets/RiscV/RiscVGeneric.hpp" + +namespace Targets::RiscV::TargetDescription +{ + /** + * Represents an RISC-V TDF. + * + * For more information of TDFs, see src/Targets/TargetDescription/README.md + */ + class TargetDescriptionFile: public Targets::TargetDescription::TargetDescriptionFile + { + public: + explicit TargetDescriptionFile(const std::string& xmlFilePath); + + /** + * Returns the RISC-V target ID from the TDF. + * + * @return + */ + [[nodiscard]] std::string getTargetId() const; + + /** + * Returns the RISC-V vendor name from the TDF. + * + * @return + */ + [[nodiscard]] std::string getVendorName() const; + }; +} diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index defcc757..9385df54 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -15,7 +15,7 @@ namespace Targets::TargetDescription return GeneratedMapping::map; } - TargetDescriptionFile::TargetDescriptionFile(const QString& xmlFilePath) { + TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) { this->init(xmlFilePath); } @@ -24,7 +24,7 @@ namespace Targets::TargetDescription } const std::string& TargetDescriptionFile::getTargetName() const { - return this->targetName; + return this->deviceAttribute("name"); } TargetFamily TargetDescriptionFile::getFamily() const { @@ -33,7 +33,7 @@ namespace Targets::TargetDescription {"RISCV", TargetFamily::RISC_V}, }; - const auto familyIt = familiesByName.find(this->familyName); + const auto familyIt = familiesByName.find(this->deviceAttribute("family")); if (familyIt == familiesByName.end()) { throw Exception("Failed to resolve target family - invalid family name"); @@ -42,8 +42,8 @@ namespace Targets::TargetDescription return familyIt->second; } - void TargetDescriptionFile::init(const QString& xmlFilePath) { - auto file = QFile(xmlFilePath); + void TargetDescriptionFile::init(const std::string& xmlFilePath) { + auto file = QFile(QString::fromStdString(xmlFilePath)); if (!file.exists()) { // This can happen if someone has been messing with the Resources directory. throw Exception("Failed to load target description file - file not found"); @@ -65,8 +65,16 @@ namespace Targets::TargetDescription throw TargetDescriptionParsingFailureException("Device element not found."); } - this->targetName = device.attributes().namedItem("name").nodeValue().toStdString(); - this->familyName = device.attributes().namedItem("family").nodeValue().toLower().toStdString(); + const auto deviceAttributes = device.attributes(); + for (auto i = 0; i < deviceAttributes.length(); ++i) { + const auto deviceAttribute = deviceAttributes.item(i); + this->deviceAttributesByName.insert( + std::pair( + deviceAttribute.nodeName().toStdString(), + deviceAttribute.nodeValue().toStdString() + ) + ); + } this->loadAddressSpaces(document); this->loadPropertyGroups(document); @@ -306,6 +314,16 @@ namespace Targets::TargetDescription return bitField; } + const std::string& TargetDescriptionFile::deviceAttribute(const std::string& attributeName) const { + const auto attributeIt = this->deviceAttributesByName.find(attributeName); + + if (attributeIt == this->deviceAttributesByName.end()) { + throw Exception("Missing target device attribute (\"" + attributeName + "\")"); + } + + return attributeIt->second; + } + void TargetDescriptionFile::loadAddressSpaces(const QDomDocument& document) { const auto deviceElement = document.elementsByTagName("device").item(0).toElement(); diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/TargetDescription/TargetDescriptionFile.hpp index 19eb1f8a..8982abef 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.hpp @@ -59,7 +59,7 @@ namespace Targets::TargetDescription * * @param xmlFilePath */ - explicit TargetDescriptionFile(const QString& xmlFilePath); + explicit TargetDescriptionFile(const std::string& xmlFilePath); /** * Will construct a TargetDescriptionFile instance from pre-loaded XML. @@ -83,9 +83,7 @@ namespace Targets::TargetDescription [[nodiscard]] TargetFamily getFamily() const; protected: - std::string targetName; - std::string familyName; - + std::map deviceAttributesByName; std::map addressSpacesMappedById; std::map propertyGroupsMappedByName; std::map modulesMappedByName; @@ -104,8 +102,8 @@ namespace Targets::TargetDescription TargetDescriptionFile& operator = (const TargetDescriptionFile& other) = default; TargetDescriptionFile& operator = (TargetDescriptionFile&& other) = default; - virtual void init(const QDomDocument& document); - void init(const QString& xmlFilePath); + void init(const std::string& xmlFilePath); + void init(const QDomDocument& document); /** * Constructs an AddressSpace object from an XML element. @@ -147,6 +145,14 @@ namespace Targets::TargetDescription */ static BitField bitFieldFromXml(const QDomElement& xmlElement); + /** + * Fetches a device attribute value by name. Throws an exception if the attribute is not found. + * + * @param attributeName + * @return + */ + const std::string& deviceAttribute(const std::string& attributeName) const; + /** * Extracts all address spaces and loads them into this->addressSpacesMappedById. */