diff --git a/src/Targets/TargetDescription/BitField.hpp b/src/Targets/TargetDescription/BitField.hpp index 4ea62539..b5e30f27 100644 --- a/src/Targets/TargetDescription/BitField.hpp +++ b/src/Targets/TargetDescription/BitField.hpp @@ -2,12 +2,30 @@ #include #include +#include namespace Targets::TargetDescription { struct BitField { + std::string key; std::string name; - std::uint8_t mask; + std::optional description; + std::uint64_t mask; + std::optional access; + + BitField( + const std::string& key, + const std::string& name, + const std::optional& description, + std::uint64_t mask, + const std::optional& access + ) + : key(key) + , name(name) + , description(description) + , mask(mask) + , access(access) + {} }; } diff --git a/src/Targets/TargetDescription/Module.hpp b/src/Targets/TargetDescription/Module.hpp index 66fbba43..722d8ffc 100644 --- a/src/Targets/TargetDescription/Module.hpp +++ b/src/Targets/TargetDescription/Module.hpp @@ -1,14 +1,59 @@ #pragma once +#include +#include +#include +#include +#include +#include + #include "ModuleInstance.hpp" #include "RegisterGroup.hpp" +#include "src/Services/StringService.hpp" + namespace Targets::TargetDescription { struct Module { + std::string key; std::string name; - std::map instancesMappedByName; - std::map registerGroupsMappedByName; + std::string description; + std::map> registerGroupsByKey; + + Module( + const std::string& key, + const std::string& name, + const std::string& description, + const std::map>& registerGroupsByKey + ) + : key(key) + , name(name) + , description(description) + , registerGroupsByKey(registerGroupsByKey) + {} + + std::optional> tryGetRegisterGroup(std::string_view keyStr) const { + const auto keys = Services::StringService::split(keyStr, '.'); + + const auto firstGroupIt = this->registerGroupsByKey.find(*keys.begin()); + return firstGroupIt != this->registerGroupsByKey.end() + ? keys.size() > 1 + ? firstGroupIt->second.tryGetSubgroup(keys | std::ranges::views::drop(1)) + : std::optional(std::cref(firstGroupIt->second)) + : std::nullopt; + } + + std::optional> getRegisterGroup(std::string_view keyStr) const { + const auto group = this->tryGetRegisterGroup(keyStr); + if (!group.has_value()) { + throw Exceptions::InvalidTargetDescriptionDataException( + "Failed to get register group \"" + std::string(keyStr) + + "\" from module in TDF - register group not found" + ); + } + + return group->get(); + } }; } diff --git a/src/Targets/TargetDescription/Register.hpp b/src/Targets/TargetDescription/Register.hpp new file mode 100644 index 00000000..8ec365ea --- /dev/null +++ b/src/Targets/TargetDescription/Register.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "BitField.hpp" + +#include "Exceptions/InvalidTargetDescriptionDataException.hpp" + +namespace Targets::TargetDescription +{ + struct Register + { + std::string key; + std::string name; + std::optional description; + std::uint32_t offset; + std::uint16_t size; + std::optional initialValue; + std::optional access; + std::optional alternative; + std::map> bitFieldsByKey; + + Register( + const std::string& key, + const std::string& name, + const std::optional& description, + std::uint32_t offset, + std::uint16_t size, + const std::optional& initialValue, + const std::optional& access, + const std::optional& alternative, + const std::map>& bitFieldsByKey + ) + : key(key) + , name(name) + , description(description) + , offset(offset) + , size(size) + , initialValue(initialValue) + , access(access) + , alternative(alternative) + , bitFieldsByKey(bitFieldsByKey) + {} + + std::optional> tryGetBitField(std::string_view key) const { + const auto bitFieldIt = this->bitFieldsByKey.find(key); + + if (bitFieldIt == this->bitFieldsByKey.end()) { + return std::nullopt; + } + + return std::cref(bitFieldIt->second); + } + + const BitField& getBitField(std::string_view key) const { + const auto bitField = this->tryGetBitField(key); + if (!bitField.has_value()) { + throw Exceptions::InvalidTargetDescriptionDataException( + "Failed to get bit field \"" + std::string(key) + "\" from register in TDF - bit field not found" + ); + } + + return bitField->get(); + } + }; +} diff --git a/src/Targets/TargetDescription/RegisterGroup.hpp b/src/Targets/TargetDescription/RegisterGroup.hpp index 35cac923..62a74c84 100644 --- a/src/Targets/TargetDescription/RegisterGroup.hpp +++ b/src/Targets/TargetDescription/RegisterGroup.hpp @@ -2,29 +2,100 @@ #include #include -#include #include +#include +#include +#include +#include +#include -#include "BitField.hpp" +#include "Register.hpp" +#include "RegisterGroupReference.hpp" namespace Targets::TargetDescription { - struct Register - { - std::string name; - std::optional caption; - std::uint16_t offset; - std::uint16_t size; - std::optional readWriteAccess; - std::map bitFieldsMappedByName; - }; - struct RegisterGroup { + std::string key; std::string name; - std::optional moduleName; - std::optional offset; - std::optional addressSpaceId; - std::map registersMappedByName; + std::optional offset; + std::map> registersByKey; + std::map> subgroupsByKey; + std::map> subgroupReferencesByKey; + + RegisterGroup( + const std::string& key, + const std::string& name, + const std::optional& offset, + const std::map>& registersByKey, + const std::map>& subgroupsByKey, + const std::map>& subgroupReferencesByKey + ) + : key(key) + , name(name) + , offset(offset) + , registersByKey(registersByKey) + , subgroupsByKey(subgroupsByKey) + , subgroupReferencesByKey(subgroupReferencesByKey) + {} + + template + requires + std::ranges::sized_range + std::optional> tryGetSubgroup(KeysType keys) const { + auto firstSubgroupIt = this->subgroupsByKey.find(*(keys.begin())); + if (firstSubgroupIt == this->subgroupsByKey.end()) { + return std::nullopt; + } + + auto subgroup = std::optional(std::cref(firstSubgroupIt->second)); + for (const auto key : keys | std::ranges::views::drop(1)) { + subgroup = subgroup->get().tryGetSubgroup(key); + + if (!subgroup.has_value()) { + break; + } + } + + return subgroup; + } + + std::optional> tryGetSubgroup(std::string_view keyStr) const { + return this->tryGetSubgroup(Services::StringService::split(keyStr, '.')); + } + + std::optional> getSubgroup(std::string_view keyStr) const { + const auto subgroup = this->tryGetSubgroup(keyStr); + if (!subgroup.has_value()) { + throw Exceptions::InvalidTargetDescriptionDataException( + "Failed to get subgroup \"" + std::string(keyStr) + + "\" from register group in TDF - subgroup not found" + ); + } + + return subgroup->get(); + } + + std::optional> tryGetRegister(std::string_view key) const { + const auto registerIt = this->registersByKey.find(key); + + if (registerIt == this->registersByKey.end()) { + return std::nullopt; + } + + return std::cref(registerIt->second); + } + + const Register& getRegister(std::string_view key) const { + const auto reg = this->tryGetRegister(key); + if (!reg.has_value()) { + throw Exceptions::InvalidTargetDescriptionDataException( + "Failed to get register \"" + std::string(key) + "\" from register group in TDF - register " + "not found" + ); + } + + return reg->get(); + } }; } diff --git a/src/Targets/TargetDescription/RegisterGroupReference.hpp b/src/Targets/TargetDescription/RegisterGroupReference.hpp new file mode 100644 index 00000000..2ea4deb9 --- /dev/null +++ b/src/Targets/TargetDescription/RegisterGroupReference.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace Targets::TargetDescription +{ + struct RegisterGroupReference + { + std::string key; + std::string name; + std::string registerGroupKey; + std::uint32_t offset; + std::optional description; + + RegisterGroupReference( + const std::string& key, + const std::string& name, + const std::string& registerGroupKey, + std::uint32_t offset, + const std::optional& description + ) + : key(key) + , name(name) + , registerGroupKey(registerGroupKey) + , offset(offset) + , description(description) + {} + }; +} diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index 58ab1465..337b9057 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -121,6 +121,27 @@ namespace Targets::TargetDescription return output; } + std::optional> TargetDescriptionFile::tryGetModule( + std::string_view key + ) const { + const auto moduleIt = this->modulesByKey.find(key); + return moduleIt != this->modulesByKey.end() + ? std::optional(std::cref(moduleIt->second)) + : std::nullopt; + } + + const Module& TargetDescriptionFile::getModule(std::string_view key) const { + const auto module = this->tryGetModule(key); + + if (!module.has_value()) { + throw InvalidTargetDescriptionDataException( + "Failed to get module \"" + std::string(key) + "\" from TDF - module not found" + ); + } + + return module->get(); + } + void TargetDescriptionFile::init(const std::string& xmlFilePath) { auto file = QFile(QString::fromStdString(xmlFilePath)); if (!file.exists()) { @@ -188,7 +209,17 @@ namespace Targets::TargetDescription ); } - this->loadModules(document); + for ( + auto element = deviceElement.firstChildElement("modules").firstChildElement("module"); + !element.isNull(); + element = element.nextSiblingElement("module") + ) { + auto module = TargetDescriptionFile::moduleFromXml(element); + this->modulesByKey.insert( + std::pair(module.key, std::move(module)) + ); + } + this->loadPeripheralModules(document); this->loadVariants(document); this->loadPinouts(document); @@ -365,124 +396,116 @@ namespace Targets::TargetDescription ); } + Module TargetDescriptionFile::moduleFromXml(const QDomElement& xmlElement) { + auto output = Module( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "name"), + TargetDescriptionFile::getAttribute(xmlElement, "description"), + {} + ); + + for ( + auto element = xmlElement.firstChildElement("register-group"); + !element.isNull(); + element = element.nextSiblingElement("register-group") + ) { + auto registerGroup = TargetDescriptionFile::registerGroupFromXml(element); + output.registerGroupsByKey.insert(std::pair(registerGroup.key, std::move(registerGroup))); + } + + return output; + } + RegisterGroup TargetDescriptionFile::registerGroupFromXml(const QDomElement& xmlElement) { - if (!xmlElement.hasAttribute("name")) { - throw Exception("Missing register group name attribute"); + const auto offset = TargetDescriptionFile::tryGetAttribute(xmlElement, "offset"); + + auto output = RegisterGroup( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "name"), + offset.has_value() ? std::optional(StringService::toUint32(*offset)) : std::nullopt, + {}, + {}, + {} + ); + + for ( + auto element = xmlElement.firstChildElement("register"); + !element.isNull(); + element = element.nextSiblingElement("register") + ) { + auto reg = TargetDescriptionFile::registerFromXml(element); + output.registersByKey.insert(std::pair(reg.key, std::move(reg))); } - auto registerGroup = RegisterGroup(); - registerGroup.name = xmlElement.attribute("name").toLower().toStdString(); - - if (registerGroup.name.empty()) { - throw Exception("Empty register group name"); + for ( + auto element = xmlElement.firstChildElement("register-group"); + !element.isNull(); + element = element.nextSiblingElement("register-group") + ) { + auto registerGroup = TargetDescriptionFile::registerGroupFromXml(element); + output.subgroupsByKey.insert(std::pair(registerGroup.key, std::move(registerGroup))); } - if (xmlElement.hasAttribute("name-in-module")) { - registerGroup.moduleName = xmlElement.attribute("name-in-module").toLower().toStdString(); + for ( + auto element = xmlElement.firstChildElement("register-group-reference"); + !element.isNull(); + element = element.nextSiblingElement("register-group-reference") + ) { + auto registerGroupReference = TargetDescriptionFile::registerGroupReferenceFromXml(element); + output.subgroupReferencesByKey.insert( + std::pair(registerGroupReference.key, std::move(registerGroupReference)) + ); } - if (xmlElement.hasAttribute("address-space")) { - registerGroup.addressSpaceId = xmlElement.attribute("address-space").toLower().toStdString(); - } + return output; + } - if (xmlElement.hasAttribute("offset")) { - registerGroup.offset = xmlElement.attribute("offset").toInt(nullptr, 16); - } - - auto& registers = registerGroup.registersMappedByName; - auto registerNodes = xmlElement.elementsByTagName("register"); - for (int registerIndex = 0; registerIndex < registerNodes.count(); registerIndex++) { - try { - auto reg = TargetDescriptionFile::registerFromXml( - registerNodes.item(registerIndex).toElement() - ); - registers.insert(std::pair(reg.name, reg)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract register from register group target description element - " - + exception.getMessage()); - } - } - - return registerGroup; + RegisterGroupReference TargetDescriptionFile::registerGroupReferenceFromXml(const QDomElement& xmlElement) { + return RegisterGroupReference( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "name"), + TargetDescriptionFile::getAttribute(xmlElement, "register-group-key"), + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")), + TargetDescriptionFile::tryGetAttribute(xmlElement, "description") + ); } Register TargetDescriptionFile::registerFromXml(const QDomElement& xmlElement) { - if ( - !xmlElement.hasAttribute("name") - || !xmlElement.hasAttribute("offset") - || !xmlElement.hasAttribute("size") + const auto initialValue = TargetDescriptionFile::tryGetAttribute(xmlElement, "initial-value"); + const auto alternative = TargetDescriptionFile::tryGetAttribute(xmlElement, "alternative"); + + auto output = Register( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "name"), + TargetDescriptionFile::tryGetAttribute(xmlElement, "description"), + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")), + StringService::toUint16(TargetDescriptionFile::getAttribute(xmlElement, "size")), + initialValue.has_value() ? std::optional(StringService::toUint64(*initialValue)) : std::nullopt, + TargetDescriptionFile::tryGetAttribute(xmlElement, "access"), + alternative.has_value() ? std::optional(*alternative == "true") : std::nullopt, + {} + ); + + for ( + auto element = xmlElement.firstChildElement("bit-field"); + !element.isNull(); + element = element.nextSiblingElement("bit-field") ) { - throw Exception("Missing register name/offset/size attribute"); + auto bitField = TargetDescriptionFile::bitFieldFromXml(element); + output.bitFieldsByKey.insert(std::pair(bitField.key, std::move(bitField))); } - auto reg = Register(); - reg.name = xmlElement.attribute("name").toLower().toStdString(); - - if (reg.name.empty()) { - throw Exception("Empty register name"); - } - - if (xmlElement.hasAttribute("caption")) { - reg.caption = xmlElement.attribute("caption").toStdString(); - } - - if (xmlElement.hasAttribute("ocd-rw")) { - reg.readWriteAccess = xmlElement.attribute("ocd-rw").toLower().toStdString(); - - } else if (xmlElement.hasAttribute("rw")) { - reg.readWriteAccess = xmlElement.attribute("rw").toLower().toStdString(); - } - - bool conversionStatus = false; - reg.size = xmlElement.attribute("size").toUShort(nullptr, 10); - reg.offset = xmlElement.attribute("offset").toUShort(&conversionStatus, 16); - - if (!conversionStatus) { - // Failed to convert offset hex value as string to uint16_t - throw Exception("Invalid register offset"); - } - - auto& bitFields = reg.bitFieldsMappedByName; - auto bitFieldNodes = xmlElement.elementsByTagName("bitfield"); - for (int bitFieldIndex = 0; bitFieldIndex < bitFieldNodes.count(); bitFieldIndex++) { - try { - auto bitField = TargetDescriptionFile::bitFieldFromXml( - bitFieldNodes.item(bitFieldIndex).toElement() - ); - bitFields.insert(std::pair(bitField.name, bitField)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract bit field from register target description element - " - + exception.getMessage()); - } - } - - return reg; + return output; } BitField TargetDescriptionFile::bitFieldFromXml(const QDomElement& xmlElement) { - if (!xmlElement.hasAttribute("name") || !xmlElement.hasAttribute("mask")) { - throw Exception("Missing bit field name/mask attribute"); - } - - auto bitField = BitField(); - bitField.name = xmlElement.attribute("name").toLower().toStdString(); - - auto maskConversion = false; - bitField.mask = static_cast( - xmlElement.attribute("mask").toUShort(&maskConversion, 16) + return BitField( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "name"), + TargetDescriptionFile::tryGetAttribute(xmlElement, "description"), + StringService::toUint64(TargetDescriptionFile::getAttribute(xmlElement, "mask")), + TargetDescriptionFile::tryGetAttribute(xmlElement, "access") ); - - if (!maskConversion) { - throw Exception("Failed to convert bit field mask to integer (from hex string)"); - } - - if (bitField.name.empty()) { - throw Exception("Empty bit field name"); - } - - return bitField; } const std::string& TargetDescriptionFile::deviceAttribute(const std::string& attributeName) const { @@ -495,105 +518,11 @@ namespace Targets::TargetDescription return attributeIt->second; } - void TargetDescriptionFile::loadModules(const QDomDocument& document) { - const auto deviceElement = document.elementsByTagName("device").item(0).toElement(); - - auto moduleNodes = document.elementsByTagName("modules").item(0).toElement() - .elementsByTagName("module"); - - for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) { - auto moduleElement = moduleNodes.item(moduleIndex).toElement(); - auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString(); - Module module; - module.name = moduleName; - - auto registerGroupNodes = moduleElement.elementsByTagName("register-group"); - for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) { - auto registerGroup = TargetDescriptionFile::registerGroupFromXml( - registerGroupNodes.item(registerGroupIndex).toElement() - ); - - module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); - } - - this->modulesMappedByName.insert(std::pair(module.name, module)); - } - } - void TargetDescriptionFile::loadPeripheralModules(const QDomDocument& document) { const auto deviceElement = document.elementsByTagName("device").item(0).toElement(); auto moduleNodes = deviceElement.elementsByTagName("peripherals").item(0).toElement() .elementsByTagName("module"); - - for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) { - auto moduleElement = moduleNodes.item(moduleIndex).toElement(); - auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString(); - Module module; - module.name = moduleName; - - auto registerGroupNodes = moduleElement.elementsByTagName("register-group"); - for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) { - auto registerGroup = TargetDescriptionFile::registerGroupFromXml( - registerGroupNodes.item(registerGroupIndex).toElement() - ); - - module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); - - if (registerGroup.moduleName.has_value()) { - this->peripheralRegisterGroupsMappedByModuleRegisterGroupName[registerGroup.moduleName.value()] - .emplace_back(registerGroup); - } - } - - auto instanceNodes = moduleElement.elementsByTagName("instance"); - for (int instanceIndex = 0; instanceIndex < instanceNodes.count(); instanceIndex++) { - auto instanceXml = instanceNodes.item(instanceIndex).toElement(); - auto instance = ModuleInstance(); - instance.name = instanceXml.attribute("name").toLower().toStdString(); - - auto registerGroupNodes = instanceXml.elementsByTagName("register-group"); - for ( - int registerGroupIndex = 0; - registerGroupIndex < registerGroupNodes.count(); - registerGroupIndex++ - ) { - auto registerGroup = TargetDescriptionFile::registerGroupFromXml( - registerGroupNodes.item(registerGroupIndex).toElement() - ); - - instance.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); - } - - auto signalNodes = instanceXml.elementsByTagName("signals").item(0).toElement() - .elementsByTagName("signal"); - for (int signalIndex = 0; signalIndex < signalNodes.count(); signalIndex++) { - auto signalXml = signalNodes.item(signalIndex).toElement(); - auto signal = Signal(); - - if (!signalXml.hasAttribute("pad")) { - continue; - } - - signal.padName = signalXml.attribute("pad").toLower().toStdString(); - signal.function = signalXml.attribute("function").toStdString(); - signal.group = signalXml.attribute("group").toStdString(); - auto indexAttribute = signalXml.attribute("index"); - bool indexValid = false; - auto indexValue = indexAttribute.toInt(&indexValid, 10); - - if (!indexAttribute.isEmpty() && indexValid) { - signal.index = indexValue; - } - - instance.instanceSignals.emplace_back(signal); - } - - module.instancesMappedByName.insert(std::pair(instance.name, instance)); - } - - this->peripheralModulesMappedByName.insert(std::pair(module.name, module)); - } } void TargetDescriptionFile::loadVariants(const QDomDocument& document) { diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/TargetDescription/TargetDescriptionFile.hpp index 67aa995f..c8295a18 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.hpp @@ -102,12 +102,17 @@ namespace Targets::TargetDescription [[nodiscard]] std::set getPhysicalInterfaces() const; + [[nodiscard]] std::optional> tryGetModule( + std::string_view key + ) const; + [[nodiscard]] const Module& getModule(std::string_view key) const; + protected: std::map deviceAttributesByName; std::map> propertyGroupsByKey; std::map> addressSpacesByKey; std::vector physicalInterfaces; - std::map modulesMappedByName; + std::map> modulesByKey; std::map peripheralModulesMappedByName; std::map> peripheralRegisterGroupsMappedByModuleRegisterGroupName; std::vector variants; @@ -163,6 +168,14 @@ namespace Targets::TargetDescription */ static PhysicalInterface physicalInterfaceFromXml(const QDomElement& xmlElement); + /** + * Constructs a Module object from an XML element. + * + * @param xmlElement + * @return + */ + static Module moduleFromXml(const QDomElement& xmlElement); + /** * Constructs a RegisterGroup object from an XML element. * @@ -171,6 +184,14 @@ namespace Targets::TargetDescription */ static RegisterGroup registerGroupFromXml(const QDomElement& xmlElement); + /** + * Constructs a RegisterGroupReference object from an XML element. + * + * @param xmlElement + * @return + */ + static RegisterGroupReference registerGroupReferenceFromXml(const QDomElement& xmlElement); + /** * Constructs a Register object from an XML element. * @@ -180,7 +201,7 @@ namespace Targets::TargetDescription static Register registerFromXml(const QDomElement& xmlElement); /** - * Consturcts a BitField object from an XML element. + * Constructs a BitField object from an XML element. * * @param xmlElement * @return @@ -195,11 +216,6 @@ namespace Targets::TargetDescription */ const std::string& deviceAttribute(const std::string& attributeName) const; - /** - * Extracts all modules and loads them into this->modulesMappedByName. - */ - void loadModules(const QDomDocument& document); - /** * Extracts all peripheral modules and loads them into this->peripheralModulesMappedByName. */