From d709c8aac9826242d91b7b84a20cb001193fe007 Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 7 Aug 2021 17:15:48 +0100 Subject: [PATCH] Retrieving AVR8 target registers and including them in the TargetDescriptor --- src/Targets/Microchip/AVR/AVR8/Avr8.cpp | 73 +++++++++++++++++++ src/Targets/Microchip/AVR/AVR8/Avr8.hpp | 7 ++ .../TargetDescriptionFile.cpp | 63 ++++++++++++++++ .../TargetDescriptionFile.hpp | 26 ++++++- .../TargetDescription/RegisterGroup.hpp | 2 + .../TargetDescriptionFile.cpp | 8 ++ src/Targets/TargetDescriptor.hpp | 3 + src/Targets/TargetRegister.hpp | 35 ++++++--- 8 files changed, 205 insertions(+), 12 deletions(-) diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp index 8a2ebacb..1be87ec8 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp @@ -39,6 +39,78 @@ void Avr8::initFromTargetDescriptionFile() { this->targetParameters = this->targetDescriptionFile->getTargetParameters(); this->padDescriptorsByName = this->targetDescriptionFile->getPadDescriptorsMappedByName(); this->targetVariantsById = this->targetDescriptionFile->getVariantsMappedById(); + + if (!this->targetParameters->stackPointerRegisterLowAddress.has_value()) { + throw Exception("Failed to load sufficient AVR8 target paramters - missting stack pointer start address"); + } + + if (!this->targetParameters->statusRegisterStartAddress.has_value()) { + throw Exception("Failed to load sufficient AVR8 target parameters - missting status register start address"); + } + + this->loadTargetRegisterDescriptors(); +} + +void Avr8::loadTargetRegisterDescriptors() { + this->targetRegisterDescriptorsByType = this->targetDescriptionFile->getRegisterDescriptorsMappedByType(); + + /* + * All AVR8 targets possess 32 general purpose CPU registers. These are not described in the TDF, so we + * construct the descriptors for them here. + */ + for (std::uint8_t i = 0; i <= 31; i++) { + auto generalPurposeRegisterDescriptor = TargetRegisterDescriptor(); + generalPurposeRegisterDescriptor.id = i; + generalPurposeRegisterDescriptor.startAddress = + this->targetParameters->gpRegisterStartAddress.value_or(0) + i; + generalPurposeRegisterDescriptor.size = 1; + generalPurposeRegisterDescriptor.type = TargetRegisterType::GENERAL_PURPOSE_REGISTER; + generalPurposeRegisterDescriptor.name = "R" + std::to_string(i); + generalPurposeRegisterDescriptor.groupName = "General Purpose CPU Registers"; + + this->targetRegisterDescriptorsByType[generalPurposeRegisterDescriptor.type].emplace_back( + generalPurposeRegisterDescriptor + ); + } + + /* + * The SP and SREG registers are described in the TDF, so we could just use the descriptors extracted from the + * TDF. The problem with that is, sometimes the SP register consists of two bytes; an SPL and an SPH. These need + * to be combined into one register descriptor. This is why we just use what we already have in + * this->targetParameters. + */ + auto stackPointerRegisterDescriptor = TargetRegisterDescriptor(); + stackPointerRegisterDescriptor.type = TargetRegisterType::STACK_POINTER; + stackPointerRegisterDescriptor.startAddress = this->targetParameters->stackPointerRegisterLowAddress.value(); + stackPointerRegisterDescriptor.size = this->targetParameters->stackPointerRegisterSize.value(); + stackPointerRegisterDescriptor.name = "SP"; + stackPointerRegisterDescriptor.groupName = "CPU"; + stackPointerRegisterDescriptor.description = "Stack Pointer Register"; + + auto statusRegisterDescriptor = TargetRegisterDescriptor(); + statusRegisterDescriptor.type = TargetRegisterType::STATUS_REGISTER; + statusRegisterDescriptor.startAddress = this->targetParameters->statusRegisterStartAddress.value(); + statusRegisterDescriptor.size = this->targetParameters->statusRegisterSize.value(); + statusRegisterDescriptor.name = "SREG"; + statusRegisterDescriptor.groupName = "CPU"; + statusRegisterDescriptor.description = "Status Register"; + + auto programCounterRegisterDescriptor = TargetRegisterDescriptor(); + programCounterRegisterDescriptor.type = TargetRegisterType::PROGRAM_COUNTER; + programCounterRegisterDescriptor.size = 4; + programCounterRegisterDescriptor.name = "PC"; + programCounterRegisterDescriptor.groupName = "CPU"; + programCounterRegisterDescriptor.description = "Program Counter"; + + this->targetRegisterDescriptorsByType[stackPointerRegisterDescriptor.type].emplace_back( + stackPointerRegisterDescriptor + ); + this->targetRegisterDescriptorsByType[statusRegisterDescriptor.type].emplace_back( + statusRegisterDescriptor + ); + this->targetRegisterDescriptorsByType[programCounterRegisterDescriptor.type].emplace_back( + programCounterRegisterDescriptor + ); } TargetSignature Avr8::getId() { @@ -153,6 +225,7 @@ TargetDescriptor Avr8Bit::Avr8::getDescriptor() { descriptor.id = this->getHumanReadableId(); descriptor.name = this->getName(); descriptor.ramSize = this->targetParameters.value().ramSize.value_or(0); + descriptor.registerDescriptorsByType = this->targetRegisterDescriptorsByType; std::transform( this->targetVariantsById.begin(), diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp index a490aaf7..68e77b19 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp @@ -29,6 +29,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit std::optional targetParameters; std::map padDescriptorsByName; std::map targetVariantsById; + std::map> targetRegisterDescriptorsByType; /** * Resolves the appropriate TDF for the AVR8 target and populates this->targetDescriptionFile. @@ -40,6 +41,12 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit */ void initFromTargetDescriptionFile(); + /** + * Populates this->targetRegisterDescriptorsByType with registers extracted from the TDF, as well as general + * purpose and other CPU registers. + */ + void loadTargetRegisterDescriptors(); + /** * Extracts the ID from the target's memory. * diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp index 4da45314..86797032 100644 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp @@ -17,6 +17,7 @@ using Bloom::Targets::TargetDescription::MemorySegment; using Bloom::Targets::TargetDescription::MemorySegmentType; using Bloom::Targets::TargetDescription::Register; using Bloom::Targets::TargetVariant; +using Bloom::Targets::TargetRegisterDescriptor; TargetDescriptionFile::TargetDescriptionFile( const TargetSignature& targetSignature, @@ -93,6 +94,7 @@ void TargetDescriptionFile::init(const QDomDocument& xml) { this->loadDebugPhysicalInterfaces(); this->loadPadDescriptors(); this->loadTargetVariants(); + this->loadTargetRegisterDescriptors(); } QJsonObject TargetDescriptionFile::getTargetDescriptionMapping() { @@ -460,6 +462,34 @@ void TargetDescriptionFile::loadTargetVariants() { } } +void TargetDescriptionFile::loadTargetRegisterDescriptors() { + auto& modulesByName = this->modulesMappedByName; + auto& peripheralModulesByName = this->peripheralModulesMappedByName; + + for (const auto& [moduleName, module] : modulesByName) { + auto moduleRegisterAddressOffset = this->getRegisterAddressOffsetByModuleName(moduleName); + for (const auto& [registerGroupName, registerGroup] : module.registerGroupsMappedByName) { + for (const auto& [moduleRegisterName, moduleRegister] : registerGroup.registersMappedByName) { + if (moduleRegister.size < 1) { + continue; + } + + auto registerDescriptor = TargetRegisterDescriptor(); + registerDescriptor.type = TargetRegisterType::OTHER; + registerDescriptor.name = moduleRegisterName; + registerDescriptor.groupName = registerGroupName; + registerDescriptor.size = moduleRegister.size; + registerDescriptor.startAddress = moduleRegister.offset + moduleRegisterAddressOffset; + + if (moduleRegister.caption.has_value() && !moduleRegister.caption->empty()) { + registerDescriptor.description = moduleRegister.caption; + } + + this->targetRegisterDescriptorsByType[registerDescriptor.type].emplace_back(registerDescriptor); + } + } + } +} std::optional TargetDescriptionFile::getFlashMemorySegment() const { auto& addressMapping = this->addressSpacesMappedById; @@ -1068,3 +1098,36 @@ void TargetDescriptionFile::loadUpdiTargetParameters(TargetParameters& targetPar targetParameters.lockbitsSegmentStartAddress = lockbitsMemorySegment->startAddress; } } + +std::uint32_t TargetDescriptionFile::getRegisterAddressOffsetByModuleName(const std::string& moduleName) const { + if (this->peripheralModulesMappedByName.contains(moduleName)) { + auto& peripheralModule = this->peripheralModulesMappedByName.at(moduleName); + + if (peripheralModule.instancesMappedByName.contains(moduleName)) { + auto& moduleInstance = peripheralModule.instancesMappedByName.at(moduleName); + + if (moduleInstance.registerGroupsMappedByName.contains(moduleName)) { + auto& registerGroup = moduleInstance.registerGroupsMappedByName.at(moduleName); + + if (!registerGroup.moduleName.has_value() || registerGroup.moduleName.value() == moduleName) { + return registerGroup.offset.value_or(0); + } + } + + /* + * If we get here, that means we couldn't find the right register group by module name. This will happen + * if the register group name doesn't match the module name attribute ("name-in-module") against the + * register group node, in the TDF. + * + * As a final attempt, we'll brute force search all register groups in the module instance. + */ + for (const auto& [registerGroupName, registerGroup] : moduleInstance.registerGroupsMappedByName) { + if (registerGroup.moduleName.has_value() && registerGroup.moduleName.value() == moduleName) { + return registerGroup.offset.value_or(0); + } + } + } + } + + return 0; +} diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp index 90928cce..b8051640 100644 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp @@ -55,6 +55,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription std::map padDescriptorsByName; std::map targetVariantsById; + std::map targetRegisterDescriptorsByType; + /** * Populates this->supportedDebugPhysicalInterfaces with physical interfaces defined in the TDF. */ @@ -70,6 +72,11 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription */ void loadTargetVariants(); + /** + * Loads all register descriptors from the TDF, and populates this->targetRegisterDescriptorsByType. + */ + void loadTargetRegisterDescriptors(); + [[nodiscard]] std::optional getFlashMemorySegment() const; [[nodiscard]] std::optional getRamMemorySegment() const; [[nodiscard]] std::optional getIoMemorySegment() const; @@ -117,6 +124,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription */ virtual void loadUpdiTargetParameters(TargetParameters& targetParameters) const; + /** + * Extracts the register address offset, for registers from a particular module. + * + * @param moduleName + * @return + */ + [[nodiscard]] virtual std::uint32_t getRegisterAddressOffsetByModuleName(const std::string& moduleName) const; + public: /** * Will resolve the target description file using the target description JSON mapping and a given target signature. @@ -177,6 +192,15 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription [[nodiscard]] const auto& getVariantsMappedById() const { return this->targetVariantsById; - }; + } + + /** + * Returns a mapping of all target register descriptors extracted from the TDF, by type. + * + * @return + */ + [[nodiscard]] const auto& getRegisterDescriptorsMappedByType() const { + return this->targetRegisterDescriptorsByType; + } }; } diff --git a/src/Targets/TargetDescription/RegisterGroup.hpp b/src/Targets/TargetDescription/RegisterGroup.hpp index 51698759..54000e4e 100644 --- a/src/Targets/TargetDescription/RegisterGroup.hpp +++ b/src/Targets/TargetDescription/RegisterGroup.hpp @@ -10,6 +10,7 @@ namespace Bloom::Targets::TargetDescription struct Register { std::string name; + std::optional caption; std::uint16_t offset; std::uint16_t size; }; @@ -17,6 +18,7 @@ namespace Bloom::Targets::TargetDescription struct RegisterGroup { std::string name; + std::optional moduleName; std::optional offset; std::map registersMappedByName; }; diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index 004ea2ee..f0622c45 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -169,6 +169,10 @@ RegisterGroup TargetDescriptionFile::generateRegisterGroupFromXml(const QDomElem throw Exception("Empty register group name"); } + registerGroup.moduleName = xmlElement.hasAttribute("name-in-module") + ? xmlElement.attribute("name-in-module").toLower().toStdString() + : registerGroup.name; + if (xmlElement.hasAttribute("offset")) { registerGroup.offset = xmlElement.attribute("offset").toInt(nullptr, 16); } @@ -207,6 +211,10 @@ Register TargetDescriptionFile::generateRegisterFromXml(const QDomElement& xmlEl throw Exception("Empty register name"); } + if (xmlElement.hasAttribute("caption")) { + reg.caption = xmlElement.attribute("caption").toStdString(); + } + bool conversionStatus; reg.size = xmlElement.attribute("size").toUShort(nullptr, 10); reg.offset = xmlElement.attribute("offset").toUShort(&conversionStatus, 16); diff --git a/src/Targets/TargetDescriptor.hpp b/src/Targets/TargetDescriptor.hpp index 33939bb1..34a690b1 100644 --- a/src/Targets/TargetDescriptor.hpp +++ b/src/Targets/TargetDescriptor.hpp @@ -3,7 +3,9 @@ #include #include #include +#include +#include "TargetRegister.hpp" #include "TargetVariant.hpp" namespace Bloom::Targets @@ -13,6 +15,7 @@ namespace Bloom::Targets std::string name; std::string id; std::uint32_t ramSize; + std::map> registerDescriptorsByType; std::vector variants; }; } diff --git a/src/Targets/TargetRegister.hpp b/src/Targets/TargetRegister.hpp index bb135ceb..01fd2266 100644 --- a/src/Targets/TargetRegister.hpp +++ b/src/Targets/TargetRegister.hpp @@ -1,9 +1,14 @@ #pragma once +#include +#include +#include #include #include #include +#include "TargetMemory.hpp" + namespace Bloom::Targets { enum class TargetRegisterType: int @@ -17,14 +22,20 @@ namespace Bloom::Targets struct TargetRegisterDescriptor { - std::optional id; + std::optional startAddress; + std::uint32_t size = 0; TargetRegisterType type = TargetRegisterType::OTHER; + std::optional id; + std::optional name = ""; + std::optional groupName; + std::optional description; + TargetRegisterDescriptor() = default; explicit TargetRegisterDescriptor(TargetRegisterType type): type(type) {}; - TargetRegisterDescriptor(size_t id, TargetRegisterType type): id(id), type(type) {}; + TargetRegisterDescriptor(std::size_t id, TargetRegisterType type): id(id), type(type) {}; - bool operator==(const TargetRegisterDescriptor& other) const { + bool operator == (const TargetRegisterDescriptor& other) const { return this->id == other.id && this->type == other.type; } }; @@ -32,22 +43,23 @@ namespace Bloom::Targets struct TargetRegister { TargetRegisterDescriptor descriptor; - std::vector value; + TargetMemoryBuffer value; TargetRegister(TargetRegisterDescriptor descriptor, std::vector value): value(std::move(value)), - descriptor(descriptor) {}; + descriptor(std::move(descriptor)) {}; - [[nodiscard]] size_t size() const { + [[nodiscard]] std::size_t size() const { return this->value.size(); } }; - using TargetRegisterMap = std::map; + using TargetRegisterMap = std::map; using TargetRegisters = std::vector; using TargetRegisterDescriptors = std::vector; } -namespace std { +namespace std +{ /** * Hashing function for TargetRegisterDescriptor type. * @@ -55,10 +67,11 @@ namespace std { * class) */ template<> - class hash { + class hash + { public: - size_t operator()(const Bloom::Targets::TargetRegisterDescriptor& descriptor) const { - return descriptor.id.value_or(0) + static_cast(descriptor.type); + std::size_t operator()(const Bloom::Targets::TargetRegisterDescriptor& descriptor) const { + return descriptor.startAddress.value_or(0) + static_cast(descriptor.type); } }; }