From f5677b62354cbc347a7ce0369983c8e6cb491d6e Mon Sep 17 00:00:00 2001 From: Nav Date: Tue, 13 Feb 2024 20:22:18 +0000 Subject: [PATCH] Updated TDF address space, memory segment and memory segment section extraction to align with new TDF format --- .../TargetDescription/AddressSpace.hpp | 48 +++- .../TargetDescription/MemorySegment.hpp | 70 ++++-- .../MemorySegmentSection.hpp | 79 ++++++ .../TargetDescriptionFile.cpp | 236 ++++++++++-------- .../TargetDescriptionFile.hpp | 22 +- 5 files changed, 316 insertions(+), 139 deletions(-) create mode 100644 src/Targets/TargetDescription/MemorySegmentSection.hpp diff --git a/src/Targets/TargetDescription/AddressSpace.hpp b/src/Targets/TargetDescription/AddressSpace.hpp index 1c37aa58..70080824 100644 --- a/src/Targets/TargetDescription/AddressSpace.hpp +++ b/src/Targets/TargetDescription/AddressSpace.hpp @@ -1,18 +1,58 @@ #pragma once #include +#include +#include +#include #include "MemorySegment.hpp" +#include "src/Targets/TargetMemory.hpp" + namespace Targets::TargetDescription { struct AddressSpace { - std::string id; - std::string name; + std::string key; std::uint32_t startAddress; std::uint32_t size; - bool littleEndian = true; - std::map> memorySegmentsByTypeAndName; + std::optional endianness; + std::map> memorySegmentsByKey; + + AddressSpace( + const std::string& key, + std::uint32_t startAddress, + std::uint32_t size, + const std::optional& endianness, + const std::map>& memorySegmentsByKey + ) + : key(key) + , startAddress(startAddress) + , size(size) + , endianness(endianness) + , memorySegmentsByKey(memorySegmentsByKey) + {} + + std::optional> tryGetMemorySegment(std::string_view key) const { + const auto segmentIt = this->memorySegmentsByKey.find(key); + + if (segmentIt == this->memorySegmentsByKey.end()) { + return std::nullopt; + } + + return std::cref(segmentIt->second); + } + + const MemorySegment& getMemorySegment(std::string_view key) const { + const auto segment = this->tryGetMemorySegment(key); + if (!segment.has_value()) { + throw Exceptions::Exception( + "Failed to get memory segment \"" + std::string(key) + + "\" from address space in TDF - segment not found" + ); + } + + return segment->get(); + } }; } diff --git a/src/Targets/TargetDescription/MemorySegment.hpp b/src/Targets/TargetDescription/MemorySegment.hpp index 29afad03..d1b0803e 100644 --- a/src/Targets/TargetDescription/MemorySegment.hpp +++ b/src/Targets/TargetDescription/MemorySegment.hpp @@ -2,9 +2,11 @@ #include #include -#include -#include "src/Helpers/BiMap.hpp" +#include "MemorySegmentSection.hpp" + +#include "src/Services/StringService.hpp" +#include "src/Exceptions/Exception.hpp" namespace Targets::TargetDescription { @@ -19,34 +21,64 @@ namespace Targets::TargetDescription RAM, LOCKBITS, OSCCAL, + PRODUCTION_SIGNATURES, SIGNATURES, USER_SIGNATURES, }; struct MemorySegment { + std::string key; std::string name; MemorySegmentType type; std::uint32_t startAddress; std::uint32_t size; std::optional pageSize; + std::map> sectionsByKey; - /** - * Mapping of all known memory segment types by their name. Any memory segments belonging to a type - * not defined in here should be ignored. - */ - static const inline BiMap typesMappedByName = { - {"aliased", MemorySegmentType::ALIASED}, - {"regs", MemorySegmentType::REGISTERS}, - {"eeprom", MemorySegmentType::EEPROM}, - {"flash", MemorySegmentType::FLASH}, - {"fuses", MemorySegmentType::FUSES}, - {"io", MemorySegmentType::IO}, - {"ram", MemorySegmentType::RAM}, - {"lockbits", MemorySegmentType::LOCKBITS}, - {"osccal", MemorySegmentType::OSCCAL}, - {"signatures", MemorySegmentType::SIGNATURES}, - {"user_signatures", MemorySegmentType::USER_SIGNATURES}, - }; + MemorySegment( + const std::string& key, + const std::string& name, + MemorySegmentType type, + std::uint32_t startAddress, + std::uint32_t size, + const std::optional& pageSize, + const std::map>& sectionsByKey + ) + : key(key) + , name(name) + , type(type) + , startAddress(startAddress) + , size(size) + , pageSize(pageSize) + , sectionsByKey(sectionsByKey) + {} + + std::optional> tryGetSection( + std::string_view keyStr + ) const { + const auto keys = Services::StringService::split(keyStr, '.'); + + const auto firstSubgroupIt = this->sectionsByKey.find(*keys.begin()); + return firstSubgroupIt != this->sectionsByKey.end() + ? keys.size() > 1 + ? firstSubgroupIt->second.tryGetSubSection(keys | std::ranges::views::drop(1)) + : std::optional(std::cref(firstSubgroupIt->second)) + : std::nullopt; + } + + std::optional> getSection( + std::string_view keyStr + ) const { + const auto propertyGroup = this->tryGetSection(keyStr); + if (!propertyGroup.has_value()) { + throw Exceptions::Exception( + "Failed to get memory segment section \"" + std::string(keyStr) + + "\" from memory segment in TDF - section not found" + ); + } + + return propertyGroup->get(); + } }; } diff --git a/src/Targets/TargetDescription/MemorySegmentSection.hpp b/src/Targets/TargetDescription/MemorySegmentSection.hpp new file mode 100644 index 00000000..6b4d25a7 --- /dev/null +++ b/src/Targets/TargetDescription/MemorySegmentSection.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "src/Services/StringService.hpp" +#include "src/Exceptions/Exception.hpp" + +namespace Targets::TargetDescription +{ + struct MemorySegmentSection + { + std::string key; + std::string name; + std::uint32_t startAddress; + std::uint32_t size; + std::map> subSectionsByKey; + + MemorySegmentSection( + const std::string& key, + const std::string& name, + std::uint32_t startAddress, + std::uint32_t size, + const std::map>& subSectionsByKey + ) + : key(key) + , name(name) + , startAddress(startAddress) + , size(size) + , subSectionsByKey(subSectionsByKey) + {} + + template + requires + std::ranges::sized_range + std::optional> tryGetSubSection(KeysType keys) const { + const auto firstSubSectionIt = this->subSectionsByKey.find(*(keys.begin())); + if (firstSubSectionIt == this->subSectionsByKey.end()) { + return std::nullopt; + } + + auto subSection = std::optional(std::cref(firstSubSectionIt->second)); + for (const auto key : keys | std::ranges::views::drop(1)) { + subSection = subSection->get().tryGetSubSection(key); + + if (!subSection.has_value()) { + break; + } + } + + return subSection; + } + + std::optional> tryGetSubSection( + std::string_view keyStr + ) const { + return this->tryGetSubSection(Services::StringService::split(keyStr, '.')); + } + + std::optional> getSubSection( + std::string_view keyStr + ) const { + const auto propertyGroup = this->tryGetSubSection(keyStr); + if (!propertyGroup.has_value()) { + throw Exceptions::Exception( + "Failed to get memory segment sub-section \"" + std::string(keyStr) + + "\" from memory segment in TDF - sub-section not found" + ); + } + + return propertyGroup->get(); + } + }; +} diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index 16e063c0..51fd6d50 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -3,11 +3,16 @@ #include #include -#include "Exceptions/TargetDescriptionParsingFailureException.hpp" -#include "src/Logger/Logger.hpp" #include "src/Services/PathService.hpp" #include "src/Services/StringService.hpp" +#include "src/Targets/TargetMemory.hpp" +#include "src/Helpers/BiMap.hpp" +#include "src/Logger/Logger.hpp" + +#include "src/Exceptions/Exception.hpp" +#include "Exceptions/TargetDescriptionParsingFailureException.hpp" + namespace Targets::TargetDescription { using namespace Exceptions; @@ -70,6 +75,27 @@ namespace Targets::TargetDescription return propertyGroup->get(); } + std::optional> TargetDescriptionFile::tryGetAddressSpace( + std::string_view key + ) const { + const auto addressSpaceIt = this->addressSpacesByKey.find(key); + return addressSpaceIt != this->addressSpacesByKey.end() + ? std::optional(std::cref(addressSpaceIt->second)) + : std::nullopt; + } + + const AddressSpace & TargetDescriptionFile::getAddressSpace(std::string_view key) const { + const auto addressSpace = this->tryGetAddressSpace(key); + + if (!addressSpace.has_value()) { + throw Exception( + "Failed to get address space \"" + std::string(key) + "\" from TDF - address space not found" + ); + } + + return addressSpace->get(); + } + void TargetDescriptionFile::init(const std::string& xmlFilePath) { auto file = QFile(QString::fromStdString(xmlFilePath)); if (!file.exists()) { @@ -115,7 +141,17 @@ namespace Targets::TargetDescription ); } - this->loadAddressSpaces(document); + for ( + auto element = deviceElement.firstChildElement("address-spaces").firstChildElement("address-space"); + !element.isNull(); + element = element.nextSiblingElement("address-space") + ) { + auto addressSpace = TargetDescriptionFile::addressSpaceFromXml(element); + this->addressSpacesByKey.insert( + std::pair(addressSpace.key, std::move(addressSpace)) + ); + } + this->loadModules(document); this->loadPeripheralModules(document); this->loadVariants(document); @@ -176,113 +212,115 @@ namespace Targets::TargetDescription ); } - AddressSpace TargetDescriptionFile::addressSpaceFromXml(const QDomElement& xmlElement) { - if ( - !xmlElement.hasAttribute("id") - || !xmlElement.hasAttribute("name") - || !xmlElement.hasAttribute("size") - || !xmlElement.hasAttribute("start") - ) { - throw Exception("Address space element missing id/name/size/start attributes."); - } + AddressSpace TargetDescriptionFile::addressSpaceFromXml(const QDomElement &xmlElement) { + static const auto endiannessByName = BiMap({ + {"big", TargetMemoryEndianness::BIG}, + {"little", TargetMemoryEndianness::LITTLE}, + }); - auto addressSpace = AddressSpace(); - addressSpace.name = xmlElement.attribute("name").toStdString(); - addressSpace.id = xmlElement.attribute("id").toStdString(); + const auto endiannessName = TargetDescriptionFile::tryGetAttribute(xmlElement, "endianness"); - bool conversionStatus; - addressSpace.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16); + auto endianness = std::optional(); + if (endiannessName.has_value()) { + endianness = endiannessByName.valueAt(*endiannessName); - if (!conversionStatus) { - throw Exception("Failed to convert start address hex value to integer."); - } - - addressSpace.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to convert size hex value to integer."); - } - - if (xmlElement.hasAttribute("endianness")) { - addressSpace.littleEndian = (xmlElement.attribute("endianness").toStdString() == "little"); - } - - // Create memory segment objects and add them to the mapping. - auto segmentNodes = xmlElement.elementsByTagName("memory-segment"); - auto& memorySegments = addressSpace.memorySegmentsByTypeAndName; - for (int segmentIndex = 0; segmentIndex < segmentNodes.count(); segmentIndex++) { - try { - auto segment = TargetDescriptionFile::memorySegmentFromXml( - segmentNodes.item(segmentIndex).toElement() + if (!endianness.has_value()) { + throw Exception( + "Failed to extract address space from TDF - invalid endianness name \"" + *endiannessName + "\"" ); - - if (!memorySegments.contains(segment.type)) { - memorySegments.insert( - std::pair< - MemorySegmentType, - decltype(addressSpace.memorySegmentsByTypeAndName)::mapped_type - >(segment.type, {})); - } - - memorySegments.find(segment.type)->second.insert(std::pair(segment.name, segment)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract memory segment from target description element - " - + exception.getMessage()); } } - return addressSpace; + auto output = AddressSpace( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")), + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")), + endianness, + {} + ); + + for ( + auto element = xmlElement.firstChildElement("memory-segment"); + !element.isNull(); + element = element.nextSiblingElement("memory-segment") + ) { + auto section = TargetDescriptionFile::memorySegmentFromXml(element); + output.memorySegmentsByKey.insert(std::pair(section.key, std::move(section))); + } + + return output; } MemorySegment TargetDescriptionFile::memorySegmentFromXml(const QDomElement& xmlElement) { - if ( - !xmlElement.hasAttribute("type") - || !xmlElement.hasAttribute("name") - || !xmlElement.hasAttribute("size") - || !xmlElement.hasAttribute("start") - ) { - throw Exception("Missing type/name/size/start attributes"); - } + static const auto typesByName = BiMap({ + {"aliased", MemorySegmentType::ALIASED}, + {"regs", MemorySegmentType::REGISTERS}, + {"eeprom", MemorySegmentType::EEPROM}, + {"flash", MemorySegmentType::FLASH}, + {"fuses", MemorySegmentType::FUSES}, + {"io", MemorySegmentType::IO}, + {"ram", MemorySegmentType::RAM}, + {"lockbits", MemorySegmentType::LOCKBITS}, + {"osccal", MemorySegmentType::OSCCAL}, + {"production_signatures", MemorySegmentType::PRODUCTION_SIGNATURES}, + {"signatures", MemorySegmentType::SIGNATURES}, + {"user_signatures", MemorySegmentType::USER_SIGNATURES}, + }); - auto segment = MemorySegment(); - auto typeName = xmlElement.attribute("type").toStdString(); - auto type = MemorySegment::typesMappedByName.valueAt(typeName); + const auto typeName = TargetDescriptionFile::getAttribute(xmlElement, "type"); + const auto type = typesByName.valueAt(typeName); if (!type.has_value()) { - throw Exception("Unknown type: \"" + typeName + "\""); + throw Exception( + "Failed to extract memory segment from TDF - invalid memory segment type name \"" + typeName + "\"" + ); } - segment.type = type.value(); - segment.name = xmlElement.attribute("name").toLower().toStdString(); + const auto pageSize = TargetDescriptionFile::tryGetAttribute(xmlElement, "page-size"); - bool conversionStatus = false; - segment.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16); + auto output = MemorySegment( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "name"), + *type, + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")), + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")), + pageSize.has_value() + ? std::optional(StringService::toUint16(*pageSize)) + : std::nullopt, + {} + ); - if (!conversionStatus) { - // Failed to convert startAddress hex value as string to uint16_t - throw Exception("Invalid start address"); + for ( + auto element = xmlElement.firstChildElement("section"); + !element.isNull(); + element = element.nextSiblingElement("section") + ) { + auto section = TargetDescriptionFile::memorySegmentSectionFromXml(element); + output.sectionsByKey.insert(std::pair(section.key, std::move(section))); } - segment.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16); + return output; + } - if (!conversionStatus) { - // Failed to convert size hex value as string to uint16_t - throw Exception("Invalid size"); + MemorySegmentSection TargetDescriptionFile::memorySegmentSectionFromXml(const QDomElement& xmlElement) { + auto output = MemorySegmentSection( + TargetDescriptionFile::getAttribute(xmlElement, "key"), + TargetDescriptionFile::getAttribute(xmlElement, "name"), + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")), + StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")), + {} + ); + + for ( + auto element = xmlElement.firstChildElement("section"); + !element.isNull(); + element = element.nextSiblingElement("section") + ) { + auto section = TargetDescriptionFile::memorySegmentSectionFromXml(element); + output.subSectionsByKey.insert(std::pair(section.key, std::move(section))); } - if (xmlElement.hasAttribute("pagesize")) { - // The page size can be in single byte hexadecimal form ("0x01"), or it can be in plain integer form! - auto pageSize = xmlElement.attribute("pagesize"); - segment.pageSize = pageSize.toUInt(&conversionStatus, pageSize.contains("0x") ? 16 : 10); - - if (!conversionStatus) { - // Failed to convert size hex value as string to uint16_t - throw Exception("Invalid size"); - } - } - - return segment; + return output; } RegisterGroup TargetDescriptionFile::registerGroupFromXml(const QDomElement& xmlElement) { @@ -415,28 +453,6 @@ namespace Targets::TargetDescription return attributeIt->second; } - void TargetDescriptionFile::loadAddressSpaces(const QDomDocument& document) { - const auto deviceElement = document.elementsByTagName("device").item(0).toElement(); - - auto addressSpaceNodes = deviceElement.elementsByTagName("address-spaces").item(0).toElement() - .elementsByTagName("address-space"); - - for (int addressSpaceIndex = 0; addressSpaceIndex < addressSpaceNodes.count(); addressSpaceIndex++) { - try { - auto addressSpace = TargetDescriptionFile::addressSpaceFromXml( - addressSpaceNodes.item(addressSpaceIndex).toElement() - ); - this->addressSpacesMappedById.insert(std::pair(addressSpace.id, addressSpace)); - - } catch (const Exception& exception) { - Logger::debug( - "Failed to extract address space from target description element - " - + exception.getMessage() - ); - } - } - } - void TargetDescriptionFile::loadModules(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 02576853..9c3136a9 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.hpp @@ -12,6 +12,7 @@ #include "AddressSpace.hpp" #include "MemorySegment.hpp" #include "PropertyGroup.hpp" +#include "MemorySegmentSection.hpp" #include "RegisterGroup.hpp" #include "Module.hpp" #include "Variant.hpp" @@ -92,9 +93,15 @@ namespace Targets::TargetDescription std::string_view keyStr ) const; [[nodiscard]] const PropertyGroup& getPropertyGroup(std::string_view keyStr) const; + + [[nodiscard]] std::optional> tryGetAddressSpace( + std::string_view key + ) const; + [[nodiscard]] const AddressSpace& getAddressSpace(std::string_view key) const; + protected: std::map deviceAttributesByName; - std::map addressSpacesMappedById; + std::map> addressSpacesByKey; std::map> propertyGroupsMappedByKey; std::map modulesMappedByName; std::map peripheralModulesMappedByName; @@ -137,6 +144,14 @@ namespace Targets::TargetDescription */ static MemorySegment memorySegmentFromXml(const QDomElement& xmlElement); + /** + * Constructs a MemorySegmentSection from an XML element. + * + * @param xmlElement + * @return + */ + static MemorySegmentSection memorySegmentSectionFromXml(const QDomElement& xmlElement); + /** * Constructs a RegisterGroup object from an XML element. * @@ -169,11 +184,6 @@ namespace Targets::TargetDescription */ const std::string& deviceAttribute(const std::string& attributeName) const; - /** - * Extracts all address spaces and loads them into this->addressSpacesMappedById. - */ - void loadAddressSpaces(const QDomDocument& document); - /** * Extracts all modules and loads them into this->modulesMappedByName. */