Renamed part description files to target description files.

Introduced a generic target description file class with an AVR8 derivation.
Moved AVR8 target description files
This commit is contained in:
Nav
2021-05-31 01:01:14 +01:00
parent 08914372b9
commit 571211b337
270 changed files with 877 additions and 820 deletions

View File

@@ -10,7 +10,7 @@
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/InvalidConfig.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Targets/Microchip/AVR/AVR8/PartDescription/PartDescriptionFile.hpp"
#include "src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp"
// Derived AVR8 targets
#include "XMega/XMega.hpp"
@@ -42,9 +42,9 @@ void Avr8::postActivationConfigure() {
}
/*
* The signature obtained from the device should match what is in the part description file
* The signature obtained from the device should match what is in the target description file
*
* We don't use this->getId() here as that could return the ID that was extracted from the part description file
* We don't use this->getId() here as that could return the ID that was extracted from the target description file
* (which it would, if the user specified the exact target name in their project config - see Avr8::getId() and
* TargetController::getSupportedTargets() for more).
*/
@@ -53,7 +53,7 @@ void Avr8::postActivationConfigure() {
if (targetSignature != pdSignature) {
throw Exception("Failed to validate connected target - target signature mismatch.\nThe target signature"
"(\"" + targetSignature.toHex() + "\") does not match the AVR8 part description signature (\""
"(\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\""
+ pdSignature.toHex() + "\"). This will likely be due to an incorrect target name in the configuration file"
+ " (bloom.json)."
);
@@ -68,7 +68,7 @@ void Avr8::postPromotionConfigure() {
void Avr8::loadPartDescription() {
auto targetSignature = this->getId();
auto partDescription = PartDescription::PartDescriptionFile(
auto partDescription = TargetDescription::TargetDescriptionFile(
targetSignature.toHex(),
(!this->name.empty()) ? std::optional(this->name) : std::nullopt
);
@@ -82,7 +82,7 @@ void Avr8::loadPadDescriptors() {
auto& targetParameters = this->getTargetParameters();
/*
* Every port address we extract from the part description will be stored in portAddresses, so that
* Every port address we extract from the target description will be stored in portAddresses, so that
* we can extract the start (min) and end (max) for the target's IO port address
* range (TargetParameters::ioPortAddressRangeStart & TargetParameters::ioPortAddressRangeEnd)
*/
@@ -493,7 +493,7 @@ std::vector<TargetVariant> Avr8::generateVariantsFromPartDescription() {
}
if (!pdPinoutsByName.contains(pdVariant.pinoutName)) {
// Missing pinouts in the part description file
// Missing pinouts in the target description file
continue;
}
@@ -883,4 +883,3 @@ bool Avr8::memoryAddressRangeClashesWithIoPortRegisters(TargetMemoryType memoryT
return false;
}

View File

@@ -14,8 +14,8 @@
#include "Family.hpp"
#include "PadDescriptor.hpp"
// Part Description
#include "PartDescription/PartDescriptionFile.hpp"
// target description
#include "TargetDescription/TargetDescriptionFile.hpp"
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
{
@@ -25,7 +25,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8Interface* avr8Interface;
std::string name = "";
std::optional<Family> family;
std::optional<PartDescription::PartDescriptionFile> partDescription;
std::optional<TargetDescription::TargetDescriptionFile> partDescription;
std::optional<TargetParameters> targetParameters;
std::map<std::string, PadDescriptor> padDescriptorsByName;
std::map<int, TargetVariant> targetVariantsById;
@@ -40,20 +40,20 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
TargetSignature getId() override;
/**
* Extracts the AVR8 target parameters from the loaded part description file.
* Extracts the AVR8 target parameters from the loaded target description file.
*
* @return
*/
virtual TargetParameters& getTargetParameters();
/**
* Generates a collection of PadDescriptor object from data in the loaded part description file and
* Generates a collection of PadDescriptor object from data in the loaded target description file and
* populates this->padDescriptorsByName.
*/
virtual void loadPadDescriptors();
/**
* Extracts target variant information from the loaded part description file and generates a collection
* Extracts target variant information from the loaded target description file and generates a collection
* of TargetVariant objects.
*
* @return

View File

@@ -13,7 +13,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
* pin 4 is mapped to a GND pad.
*
* PadDescriptor describes a single pad on an AVR8 target. On target configuration, PadDescriptors are
* generated from the AVR8 part description file. These descriptors are mapped to pad names.
* generated from the AVR8 target description file. These descriptors are mapped to pad names.
* See Avr8::loadPadDescriptors() for more.
*/
struct PadDescriptor

View File

@@ -1,16 +0,0 @@
#pragma once
#include <cstdint>
#include "MemorySegment.hpp"
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct AddressSpace {
std::string id;
std::string name;
std::uint16_t startAddress;
std::uint16_t size;
bool littleEndian = true;
std::map<MemorySegmentType, std::map<std::string, MemorySegment>> memorySegmentsByTypeAndName;
};
}

View File

@@ -1,45 +0,0 @@
#pragma once
#include <cstdint>
#include <QDomElement>
#include "src/Helpers/BiMap.hpp"
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
enum MemorySegmentType {
REGISTERS,
IO,
EEPROM,
RAM,
FLASH,
SIGNATURES,
FUSES,
LOCKBITS,
OSCCAL,
};
struct MemorySegment {
std::string name;
MemorySegmentType type;
std::uint32_t startAddress;
std::uint32_t size;
std::optional<std::uint16_t> pageSize;
/**
* 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<std::string, MemorySegmentType> typesMappedByName = {
{"regs", MemorySegmentType::REGISTERS},
{"io", MemorySegmentType::IO},
{"eeprom", MemorySegmentType::EEPROM},
{"ram", MemorySegmentType::RAM},
{"flash", MemorySegmentType::FLASH},
{"signatures", MemorySegmentType::SIGNATURES},
{"fuses", MemorySegmentType::FUSES},
{"lockbits", MemorySegmentType::LOCKBITS},
{"osccal", MemorySegmentType::OSCCAL},
};
};
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include "ModuleInstance.hpp"
#include "RegisterGroup.hpp"
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct Module {
std::string name;
std::map<std::string, ModuleInstance> instancesMappedByName;
std::map<std::string, RegisterGroup> registerGroupsMappedByName;
};
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include <map>
#include <vector>
#include "RegisterGroup.hpp"
#include "Signal.hpp"
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct ModuleInstance {
std::string name;
std::map<std::string, RegisterGroup> registerGroupsMappedByName;
std::vector<Signal> instanceSignals;
};
}

View File

@@ -1,896 +0,0 @@
#include <QJsonDocument>
#include <QJsonArray>
#include "PartDescriptionFile.hpp"
#include "src/Targets/Microchip/AVR/Exceptions/PartDescriptionParsingFailureException.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Helpers/Paths.hpp"
using namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription;
using namespace Bloom::Targets::Microchip::Avr::Avr8Bit;
using namespace Bloom::Targets::Microchip::Avr;
using namespace Bloom::Exceptions;
// TODO: Move this into a resolvePartDescriptionFile() method.
PartDescriptionFile::PartDescriptionFile(const std::string& targetSignatureHex, std::optional<std::string> targetName) {
auto mapping = this->getPartDescriptionMapping();
auto qTargetSignatureHex = QString::fromStdString(targetSignatureHex);
if (mapping.contains(qTargetSignatureHex)) {
// We have a match for the target signature.
auto descriptionFilesJsonArray = mapping.find(qTargetSignatureHex).value().toArray();
auto matchingDescriptionFiles = std::vector<QJsonValue>();
std::copy_if(
descriptionFilesJsonArray.begin(),
descriptionFilesJsonArray.end(),
std::back_inserter(matchingDescriptionFiles),
[&targetName] (const QJsonValue& value) {
auto pdTargetName = value.toObject().find("targetName")->toString().toLower().toStdString();
return !targetName.has_value() || (targetName.has_value() && targetName.value() == pdTargetName);
}
);
if (targetName.has_value() && matchingDescriptionFiles.empty()) {
throw Exception("Failed to resolve target description file for target \"" + targetName.value()
+ "\" - target signature \"" + targetSignatureHex + "\" does not belong to target with name \"" +
targetName.value() + "\". Please review your bloom.json configuration.");
}
if (matchingDescriptionFiles.size() == 1) {
// Attempt to load the XML part description file
auto descriptionFilePath = QString::fromStdString(Paths::applicationDirPath()) + "/"
+ matchingDescriptionFiles.front().toObject().find("targetDescriptionFilePath")->toString();
Logger::debug("Loading AVR8 part description file: " + descriptionFilePath.toStdString());
this->init(descriptionFilePath);
} else if (matchingDescriptionFiles.size() > 1) {
/*
* There are numerous part description files mapped to this target signature. There's really not
* much we can do at this point, so we'll just instruct the user to use a more specific target name.
*/
QStringList targetNames;
std::transform(
matchingDescriptionFiles.begin(),
matchingDescriptionFiles.end(),
std::back_inserter(targetNames),
[](const QJsonValue& descriptionFile) {
return QString("\"" + descriptionFile.toObject().find("targetName")->toString().toLower() + "\"");
}
);
throw Exception("Failed to resolve part description file for target \""
+ targetSignatureHex + "\" - ambiguous signature.\nThe signature is mapped to numerous targets: "
+ targetNames.join(", ").toStdString() + ".\n\nPlease update the target name in your Bloom " +
"configuration to one of the above."
);
} else {
throw Exception("Failed to resolve part description file for target \""
+ targetSignatureHex + "\" - invalid AVR8 target description mapping."
);
}
} else {
throw Exception("Failed to resolve part description file for target \""
+ targetSignatureHex + "\" - unknown target signature.");
}
}
void PartDescriptionFile::init(const QString& xmlFilePath) {
auto file = QFile(xmlFilePath);
if (!file.exists()) {
// This can happen if someone has been messing with the Resources directory.
throw Exception("Failed to load part description file - file not found");
}
file.open(QIODevice::ReadOnly);
auto xml = QDomDocument();
xml.setContent(file.readAll());
this->init(xml);
}
void PartDescriptionFile::init(const QDomDocument& xml) {
this->xml = xml;
auto device = xml.elementsByTagName("devices").item(0)
.toElement().elementsByTagName("device").item(0).toElement();
if (!device.isElement()) {
throw PartDescriptionParsingFailureException("Device element not found.");
}
this->deviceElement = device;
}
QJsonObject PartDescriptionFile::getPartDescriptionMapping() {
auto mappingFile = QFile(
QString::fromStdString(Paths::resourcesDirPath() + "/TargetPartDescriptions/AVR/Mapping.json")
);
if (!mappingFile.exists()) {
throw TargetControllerStartupFailure("Failed to load AVR part description mapping - mapping file not found");
}
mappingFile.open(QIODevice::ReadOnly);
return QJsonDocument::fromJson(mappingFile.readAll()).object();
}
std::string PartDescriptionFile::getTargetName() const {
return this->deviceElement.attributes().namedItem("name").nodeValue().toStdString();
}
TargetSignature PartDescriptionFile::getTargetSignature() const {
auto propertyGroups = this->getPropertyGroupsMappedByName();
auto signaturePropertyGroupIt = propertyGroups.find("signatures");
if (signaturePropertyGroupIt == propertyGroups.end()) {
throw PartDescriptionParsingFailureException("Signature property group not found");
}
auto signaturePropertyGroup = signaturePropertyGroupIt->second;
auto& signatureProperties = signaturePropertyGroup.propertiesMappedByName;
std::optional<unsigned char> signatureByteZero;
std::optional<unsigned char> signatureByteOne;
std::optional<unsigned char> signatureByteTwo;
if (signatureProperties.find("signature0") != signatureProperties.end()) {
signatureByteZero = static_cast<unsigned char>(
signatureProperties.find("signature0")->second.value.toShort(nullptr, 16)
);
}
if (signatureProperties.find("signature1") != signatureProperties.end()) {
signatureByteOne = static_cast<unsigned char>(
signatureProperties.find("signature1")->second.value.toShort(nullptr, 16)
);
}
if (signatureProperties.find("signature2") != signatureProperties.end()) {
signatureByteTwo = static_cast<unsigned char>(
signatureProperties.find("signature2")->second.value.toShort(nullptr, 16)
);
}
if (signatureByteZero.has_value() && signatureByteOne.has_value() && signatureByteTwo.has_value()) {
return TargetSignature(signatureByteZero.value(), signatureByteOne.value(), signatureByteTwo.value());
}
throw PartDescriptionParsingFailureException("Failed to extract target signature from AVR8 part description.");
}
AddressSpace PartDescriptionFile::generateAddressSpaceFromXml(const QDomElement& xmlElement) const {
if (
!xmlElement.hasAttribute("id")
|| !xmlElement.hasAttribute("name")
|| !xmlElement.hasAttribute("size")
|| !xmlElement.hasAttribute("start")
) {
throw Exception("Address space element missing id/name/size/start attributes.");
}
auto addressSpace = AddressSpace();
addressSpace.name = xmlElement.attribute("name").toStdString();
addressSpace.id = xmlElement.attribute("id").toStdString();
bool conversionStatus;
addressSpace.startAddress = xmlElement.attribute("start").toUShort(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert start address hex value to integer.");
}
addressSpace.size = xmlElement.attribute("size").toUShort(&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 = this->generateMemorySegmentFromXml(segmentNodes.item(segmentIndex).toElement());
if (memorySegments.find(segment.type) == memorySegments.end()) {
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 part description element - "
+ exception.getMessage());
}
}
return addressSpace;
}
MemorySegment PartDescriptionFile::generateMemorySegmentFromXml(const QDomElement& xmlElement) const {
if (
!xmlElement.hasAttribute("type")
|| !xmlElement.hasAttribute("name")
|| !xmlElement.hasAttribute("size")
|| !xmlElement.hasAttribute("start")
) {
throw Exception("Missing type/name/size/start attributes");
}
auto segment = MemorySegment();
auto typeName = xmlElement.attribute("type").toStdString();
auto type = MemorySegment::typesMappedByName.valueAt(typeName);
if (!type.has_value()) {
throw Exception("Unknown type: \"" + typeName + "\"");
}
segment.type = type.value();
segment.name = xmlElement.attribute("name").toLower().toStdString();
bool conversionStatus;
segment.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16);
if (!conversionStatus) {
// Failed to convert startAddress hex value as string to uint16_t
throw Exception("Invalid start address");
}
segment.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16);
if (!conversionStatus) {
// Failed to convert size hex value as string to uint16_t
throw Exception("Invalid size");
}
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;
}
RegisterGroup PartDescriptionFile::generateRegisterGroupFromXml(const QDomElement& xmlElement) const {
if (!xmlElement.hasAttribute("name")) {
throw Exception("Missing register group name attribute");
}
auto registerGroup = RegisterGroup();
registerGroup.name = xmlElement.attribute("name").toLower().toStdString();
if (registerGroup.name.empty()) {
throw Exception("Empty register group name");
}
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 = this->generateRegisterFromXml(registerNodes.item(registerIndex).toElement());
registers.insert(std::pair(reg.name, reg));
} catch (const Exception& exception) {
Logger::debug("Failed to extract register from register group part description element - "
+ exception.getMessage());
}
}
return registerGroup;
}
Register PartDescriptionFile::generateRegisterFromXml(const QDomElement& xmlElement) const {
if (
!xmlElement.hasAttribute("name")
|| !xmlElement.hasAttribute("offset")
|| !xmlElement.hasAttribute("size")
) {
throw Exception("Missing register name/offset/size attribute");
}
auto reg = Register();
reg.name = xmlElement.attribute("name").toLower().toStdString();
if (reg.name.empty()) {
throw Exception("Empty register name");
}
bool conversionStatus;
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");
}
return reg;
}
Family PartDescriptionFile::getFamily() const {
static auto familyNameToEnums = this->getFamilyNameToEnumMapping();
auto familyName = this->deviceElement.attributes().namedItem("family").nodeValue().toLower().toStdString();
if (familyName.empty()) {
throw Exception("Could not find target family name in part description file.");
}
if (familyNameToEnums.find(familyName) == familyNameToEnums.end()) {
throw Exception("Unknown family name in part description file.");
}
return familyNameToEnums.find(familyName)->second;
}
const std::map<std::string, PropertyGroup>& PartDescriptionFile::getPropertyGroupsMappedByName() const {
if (!this->cachedPropertyGroupMapping.has_value()) {
if (!this->deviceElement.isElement()) {
throw PartDescriptionParsingFailureException("Device element not found.");
}
std::map<std::string, PropertyGroup> output;
auto propertyGroupNodes = this->deviceElement.elementsByTagName("property-groups").item(0).toElement()
.elementsByTagName("property-group");
for (int propertyGroupIndex = 0; propertyGroupIndex < propertyGroupNodes.count(); propertyGroupIndex++) {
auto propertyGroupElement = propertyGroupNodes.item(propertyGroupIndex).toElement();
auto propertyGroupName = propertyGroupElement.attributes().namedItem("name").nodeValue().toLower().toStdString();
PropertyGroup propertyGroup;
propertyGroup.name = propertyGroupName;
auto propertyNodes = propertyGroupElement.elementsByTagName("property");
for (int propertyIndex = 0; propertyIndex < propertyNodes.count(); propertyIndex++) {
auto propertyElement = propertyNodes.item(propertyIndex).toElement();
auto propertyName = propertyElement.attributes().namedItem("name").nodeValue();
Property property;
property.name = propertyName.toStdString();
property.value = propertyElement.attributes().namedItem("value").nodeValue();
propertyGroup.propertiesMappedByName.insert(std::pair(propertyName.toLower().toStdString(), property));
}
output.insert(std::pair(propertyGroup.name, propertyGroup));
}
this->cachedPropertyGroupMapping.emplace(output);
}
return this->cachedPropertyGroupMapping.value();
}
const std::map<std::string, Module>& PartDescriptionFile::getModulesMappedByName() const {
if (!this->cachedModuleByNameMapping.has_value()) {
std::map<std::string, Module> output;
auto moduleNodes = this->xml.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 = this->generateRegisterGroupFromXml(registerGroupNodes.item(registerGroupIndex).toElement());
module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup));
}
output.insert(std::pair(module.name, module));
}
this->cachedModuleByNameMapping.emplace(output);
}
return this->cachedModuleByNameMapping.value();
}
const std::map<std::string, Module>& PartDescriptionFile::getPeripheralModulesMappedByName() const {
if (!this->cachedPeripheralModuleByNameMapping.has_value()) {
std::map<std::string, Module> output;
auto moduleNodes = this->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 = this->generateRegisterGroupFromXml(registerGroupNodes.item(registerGroupIndex).toElement());
module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, 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 = this->generateRegisterGroupFromXml(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));
}
output.insert(std::pair(module.name, module));
}
this->cachedPeripheralModuleByNameMapping.emplace(output);
}
return this->cachedPeripheralModuleByNameMapping.value();
}
std::map<std::string, AddressSpace> PartDescriptionFile::getAddressSpacesMappedById() const {
std::map<std::string, AddressSpace> output;
auto addressSpaceNodes = this->deviceElement.elementsByTagName("address-spaces").item(0).toElement()
.elementsByTagName("address-space");
for (int addressSpaceIndex = 0; addressSpaceIndex < addressSpaceNodes.count(); addressSpaceIndex++) {
try {
auto addressSpace = this->generateAddressSpaceFromXml(addressSpaceNodes.item(addressSpaceIndex).toElement());
output.insert(std::pair(addressSpace.id, addressSpace));
} catch (const Exception& exception) {
Logger::debug("Failed to extract address space from part description element - " + exception.getMessage());
}
}
return output;
}
std::optional<MemorySegment> PartDescriptionFile::getFlashMemorySegment() const {
auto addressMapping = this->getAddressSpacesMappedById();
auto programAddressSpaceIt = addressMapping.find("prog");
// Flash memory attributes are typically found in memory segments within the program address space.
if (programAddressSpaceIt != addressMapping.end()) {
auto& programAddressSpace = programAddressSpaceIt->second;
auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName;
if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) {
auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second;
/*
* Some part descriptions describe the flash memory segments in the "APP_SECTION" segment, whereas
* others use the "FLASH" segment.
*/
auto flashSegmentIt = flashMemorySegments.find("app_section") != flashMemorySegments.end() ?
flashMemorySegments.find("app_section") : flashMemorySegments.find("flash");
if (flashSegmentIt != flashMemorySegments.end()) {
return flashSegmentIt->second;
}
}
}
return std::nullopt;
}
std::optional<MemorySegment> PartDescriptionFile::getRamMemorySegment() const {
auto addressMapping = this->getAddressSpacesMappedById();
// Internal RAM &register attributes are usually found in the data address space
auto dataAddressSpaceIt = addressMapping.find("data");
if (dataAddressSpaceIt != addressMapping.end()) {
auto& dataAddressSpace = dataAddressSpaceIt->second;
auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName;
if (dataMemorySegments.find(MemorySegmentType::RAM) != dataMemorySegments.end()) {
auto& ramMemorySegments = dataMemorySegments.find(MemorySegmentType::RAM)->second;
auto ramMemorySegmentIt = ramMemorySegments.begin();
if (ramMemorySegmentIt != ramMemorySegments.end()) {
return ramMemorySegmentIt->second;
}
}
}
return std::nullopt;
}
std::optional<MemorySegment> PartDescriptionFile::getRegisterMemorySegment() const {
auto addressMapping = this->getAddressSpacesMappedById();
// Internal RAM &register attributes are usually found in the data address space
auto dataAddressSpaceIt = addressMapping.find("data");
if (dataAddressSpaceIt != addressMapping.end()) {
auto& dataAddressSpace = dataAddressSpaceIt->second;
auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName;
if (dataMemorySegments.find(MemorySegmentType::REGISTERS) != dataMemorySegments.end()) {
auto& registerMemorySegments = dataMemorySegments.find(MemorySegmentType::REGISTERS)->second;
auto registerMemorySegmentIt = registerMemorySegments.begin();
if (registerMemorySegmentIt != registerMemorySegments.end()) {
return registerMemorySegmentIt->second;
}
}
}
return std::nullopt;
}
std::optional<MemorySegment> PartDescriptionFile::getEepromMemorySegment() const {
auto addressMapping = this->getAddressSpacesMappedById();
// EEPROM attributes are usually found in the data address space
auto eepromAddressSpaceIt = addressMapping.find("eeprom");
if (eepromAddressSpaceIt != addressMapping.end()) {
auto& eepromAddressSpace = eepromAddressSpaceIt->second;
auto& eepromAddressSpaceSegments = eepromAddressSpace.memorySegmentsByTypeAndName;
if (eepromAddressSpaceSegments.find(MemorySegmentType::EEPROM) != eepromAddressSpaceSegments.end()) {
auto& eepromMemorySegments = eepromAddressSpaceSegments.find(MemorySegmentType::EEPROM)->second;
auto eepromMemorySegmentIt = eepromMemorySegments.begin();
if (eepromMemorySegmentIt != eepromMemorySegments.end()) {
return eepromMemorySegmentIt->second;
}
}
}
return std::nullopt;
}
std::optional<MemorySegment> PartDescriptionFile::getFirstBootSectionMemorySegment() const {
auto addressMapping = this->getAddressSpacesMappedById();
auto programAddressSpaceIt = addressMapping.find("prog");
if (programAddressSpaceIt != addressMapping.end()) {
auto& programAddressSpace = programAddressSpaceIt->second;
auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName;
if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) {
auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second;
auto firstBootSectionSegmentIt = flashMemorySegments.find("boot_section_1");
if (flashMemorySegments.contains("boot_section_1")) {
return flashMemorySegments.at("boot_section_1");
} else if (flashMemorySegments.contains("boot_section")) {
return flashMemorySegments.at("boot_section");
}
}
}
return std::nullopt;
}
std::optional<RegisterGroup> PartDescriptionFile::getCpuRegisterGroup() const {
auto& modulesByName = this->getModulesMappedByName();
if (modulesByName.find("cpu") != modulesByName.end()) {
auto cpuModule = modulesByName.find("cpu")->second;
auto cpuRegisterGroupIt = cpuModule.registerGroupsMappedByName.find("cpu");
if (cpuRegisterGroupIt != cpuModule.registerGroupsMappedByName.end()) {
return cpuRegisterGroupIt->second;
}
}
return std::nullopt;
}
std::optional<RegisterGroup> PartDescriptionFile::getBootLoadRegisterGroup() const {
auto& modulesByName = this->getModulesMappedByName();
if (modulesByName.contains("boot_load")) {
auto& bootLoadModule = modulesByName.at("boot_load");
auto bootLoadRegisterGroupIt = bootLoadModule.registerGroupsMappedByName.find("boot_load");
if (bootLoadRegisterGroupIt != bootLoadModule.registerGroupsMappedByName.end()) {
return bootLoadRegisterGroupIt->second;
}
}
return std::nullopt;
}
std::optional<RegisterGroup> PartDescriptionFile::getEepromRegisterGroup() const {
auto& modulesByName = this->getModulesMappedByName();
if (modulesByName.find("eeprom") != modulesByName.end()) {
auto eepromModule = modulesByName.find("eeprom")->second;
auto eepromRegisterGroupIt = eepromModule.registerGroupsMappedByName.find("eeprom");
if (eepromRegisterGroupIt != eepromModule.registerGroupsMappedByName.end()) {
return eepromRegisterGroupIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getStatusRegister() const {
auto cpuRegisterGroup = this->getCpuRegisterGroup();
if (cpuRegisterGroup.has_value()) {
auto statusRegisterIt = cpuRegisterGroup->registersMappedByName.find("sreg");
if (statusRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
return statusRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getStackPointerRegister() const {
auto cpuRegisterGroup = this->getCpuRegisterGroup();
if (cpuRegisterGroup.has_value()) {
auto stackPointerRegisterIt = cpuRegisterGroup->registersMappedByName.find("sp");
if (stackPointerRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
return stackPointerRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getStackPointerHighRegister() const {
auto cpuRegisterGroup = this->getCpuRegisterGroup();
if (cpuRegisterGroup.has_value()) {
auto stackPointerHighRegisterIt = cpuRegisterGroup->registersMappedByName.find("sph");
if (stackPointerHighRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
return stackPointerHighRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getStackPointerLowRegister() const {
auto cpuRegisterGroup = this->getCpuRegisterGroup();
if (cpuRegisterGroup.has_value()) {
auto stackPointerLowRegisterIt = cpuRegisterGroup->registersMappedByName.find("spl");
if (stackPointerLowRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
return stackPointerLowRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getOscillatorCalibrationRegister() const {
auto cpuRegisterGroup = this->getCpuRegisterGroup();
if (cpuRegisterGroup.has_value()) {
auto osccalRegisterIt = cpuRegisterGroup->registersMappedByName.find("osccal");
if (osccalRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
return osccalRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getSpmcsRegister() const {
auto cpuRegisterGroup = this->getCpuRegisterGroup();
if (cpuRegisterGroup.has_value()) {
auto spmcsRegisterIt = cpuRegisterGroup->registersMappedByName.find("spmcsr");
if (spmcsRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
return spmcsRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getSpmcRegister() const {
auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup();
if (bootLoadRegisterGroup.has_value()) {
auto spmcRegisterIt = bootLoadRegisterGroup->registersMappedByName.find("spmcr");
if (spmcRegisterIt != bootLoadRegisterGroup->registersMappedByName.end()) {
return spmcRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getEepromAddressRegister() const {
auto eepromRegisterGroup = this->getEepromRegisterGroup();
if (eepromRegisterGroup.has_value()) {
auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eear");
if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
return eepromAddressRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getEepromDataRegister() const {
auto eepromRegisterGroup = this->getEepromRegisterGroup();
if (eepromRegisterGroup.has_value()) {
auto eepromDataRegisterIt = eepromRegisterGroup->registersMappedByName.find("eedr");
if (eepromDataRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
return eepromDataRegisterIt->second;
}
}
return std::nullopt;
}
std::optional<Register> PartDescriptionFile::getEepromControlRegister() const {
auto eepromRegisterGroup = this->getEepromRegisterGroup();
if (eepromRegisterGroup.has_value()) {
auto eepromControlRegisterIt = eepromRegisterGroup->registersMappedByName.find("eecr");
if (eepromControlRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
return eepromControlRegisterIt->second;
}
}
return std::nullopt;
}
std::vector<Variant> PartDescriptionFile::getVariants() const {
std::vector<Variant> output;
auto variantNodes = this->xml.elementsByTagName("variants").item(0).toElement()
.elementsByTagName("variant");
for (int variantIndex = 0; variantIndex < variantNodes.count(); variantIndex++) {
try {
auto variantXml = variantNodes.item(variantIndex).toElement();
if (!variantXml.hasAttribute("ordercode")) {
throw Exception("Missing ordercode attribute");
}
if (!variantXml.hasAttribute("package")) {
throw Exception("Missing package attribute");
}
if (!variantXml.hasAttribute("pinout")) {
throw Exception("Missing pinout attribute");
}
auto variant = Variant();
variant.orderCode = variantXml.attribute("ordercode").toStdString();
variant.pinoutName = variantXml.attribute("pinout").toLower().toStdString();
variant.package = variantXml.attribute("package").toUpper().toStdString();
if (variantXml.hasAttribute("disabled")) {
variant.disabled = (variantXml.attribute("disabled") == "1");
}
output.push_back(variant);
} catch (const Exception& exception) {
Logger::debug("Failed to extract variant from part description element - " + exception.getMessage());
}
}
return output;
}
const std::map<std::string, Pinout>& PartDescriptionFile::getPinoutsMappedByName() const {
if (!this->cachedPinoutByNameMapping.has_value()) {
this->cachedPinoutByNameMapping = std::map<std::string, Pinout>();
auto pinoutNodes = this->xml.elementsByTagName("pinouts").item(0).toElement()
.elementsByTagName("pinout");
for (int pinoutIndex = 0; pinoutIndex < pinoutNodes.count(); pinoutIndex++) {
try {
auto pinoutXml = pinoutNodes.item(pinoutIndex).toElement();
if (!pinoutXml.hasAttribute("name")) {
throw Exception("Missing name attribute");
}
auto pinout = Pinout();
pinout.name = pinoutXml.attribute("name").toLower().toStdString();
auto pinNodes = pinoutXml.elementsByTagName("pin");
for (int pinIndex = 0; pinIndex < pinNodes.count(); pinIndex++) {
auto pinXml = pinNodes.item(pinIndex).toElement();
if (!pinXml.hasAttribute("position")) {
throw Exception("Missing position attribute on pin element " + std::to_string(pinIndex));
}
if (!pinXml.hasAttribute("pad")) {
throw Exception("Missing pad attribute on pin element " + std::to_string(pinIndex));
}
auto pin = Pin();
bool positionConversionSucceeded = true;
pin.position = pinXml.attribute("position").toInt(&positionConversionSucceeded, 10);
pin.pad = pinXml.attribute("pad").toLower().toStdString();
if (!positionConversionSucceeded) {
throw Exception("Failed to convert position attribute value to integer on pin element "
+ std::to_string(pinIndex));
}
pinout.pins.push_back(pin);
}
this->cachedPinoutByNameMapping->insert(std::pair(pinout.name, pinout));
} catch (const Exception& exception) {
Logger::debug("Failed to extract pinout from part description element - " + exception.getMessage());
}
}
}
return this->cachedPinoutByNameMapping.value();
}

View File

@@ -1,174 +0,0 @@
#pragma once
#include <QFile>
#include <QDomDocument>
#include "AddressSpace.hpp"
#include "MemorySegment.hpp"
#include "PropertyGroup.hpp"
#include "RegisterGroup.hpp"
#include "Module.hpp"
#include "Variant.hpp"
#include "Pinout.hpp"
#include "src/Targets/Microchip/AVR/TargetSignature.hpp"
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
/**
* An AVR8 part description file is an XML file that describes a particular AVR8 target.
* All supported AVR8 targets come with a part description file.
*
* Part description files are part of the Bloom codebase.
* For AVR8 part description files, see directory "src/Targets/Microchip/AVR/PartDescriptionFiles/AVR8".
*
* During the build process, all part description files are copied to the distribution directory, ready
* to be shipped with the Bloom binary. Alongside these files is a JSON file, containing a mapping of AVR8 target
* signatures to part description file paths. Bloom uses this mapping to find a particular part description
* file, given a target signature. See directory "build/resources/TargetPartDescriptions".
* The copying of the part description files, and the generation of the JSON mapping, is done by a PHP script:
* "build/scripts/CopyAvrPartFilesAndCreateMapping.php". This script is invoked via a custom command, at build time.
*
* All processing of AVR8 part description files is done in this class.
*/
class PartDescriptionFile
{
private:
QDomDocument xml;
QDomElement deviceElement;
mutable std::optional<std::map<std::string, PropertyGroup>> cachedPropertyGroupMapping;
mutable std::optional<std::map<std::string, Module>> cachedModuleByNameMapping;
mutable std::optional<std::map<std::string, Module>> cachedPeripheralModuleByNameMapping;
mutable std::optional<std::map<std::string, Pinout>> cachedPinoutByNameMapping;
/**`
* AVR8 part description files include the target family name. This method returns a mapping of part
* description family name strings to Family enum values.
*
* TODO: the difference in AVR8 family variations, like "tinyAVR" and "tinyAVR 2" may require attention.
*
* @return
*/
static inline auto getFamilyNameToEnumMapping() {
// All keys should be lower case.
return std::map<std::string, Family> {
{"megaavr", Family::MEGA},
{"avr mega", Family::MEGA},
{"avr xmega", Family::XMEGA},
{"avr tiny", Family::TINY},
{"tinyavr", Family::TINY},
{"tinyavr 2", Family::TINY},
};
};
void init(const QDomDocument& xml);
void init(const QString& xmlFilePath);
/**
* Constructs an AddressSpace object from an XML element (in the form of a QDomElement), taken from
* an AVR part description file.
*
* @param xmlElement
* @return
*/
AddressSpace generateAddressSpaceFromXml(const QDomElement& xmlElement) const;
/**
* Constructs a MemorySegment from an XML element (in the form of a QDomElement) taken from
* an AVR part description file.
*
* @param xmlElement
* @return
*/
MemorySegment generateMemorySegmentFromXml(const QDomElement& xmlElement) const;
RegisterGroup generateRegisterGroupFromXml(const QDomElement& xmlElement) const;
Register generateRegisterFromXml(const QDomElement& xmlElement) const;
public:
/**
* Will construct a PartDescription instance from the XML of a part description file, the path to which
* is given via xmlFilePath.
*
* @param xmlFilePath
*/
PartDescriptionFile(const QString& xmlFilePath) {
this->init(xmlFilePath);
}
/**
* Will construct a PartDescription instance from pre-loaded XML.
*
* @param xml
*/
PartDescriptionFile(const QDomDocument& xml) {
this->init(xml);
}
/**
* Will resolve the part description file using the part description JSON mapping and a given target signature.
*
* @param targetSignatureHex
* @param targetName
*/
PartDescriptionFile(const std::string& targetSignatureHex, std::optional<std::string> targetName);
/**
* Loads the AVR8 target description JSON mapping file.
*
* @return
*/
static QJsonObject getPartDescriptionMapping();
std::string getTargetName() const;
/**
* Extracts the AVR8 target signature from the part description XML.
*
* @return
*/
TargetSignature getTargetSignature() const;
/**
* Extracts all address spaces for the AVR8 target, from the part description XML.
*
* Will return a mapping of the extracted address spaces, mapped by id.
*
* @return
*/
std::map<std::string, AddressSpace> getAddressSpacesMappedById() const;
/**
* Extracts the AVR8 target family from the part description XML.
*
* @return
*/
Family getFamily() const;
const std::map<std::string, PropertyGroup>& getPropertyGroupsMappedByName() const;
const std::map<std::string, Module>& getModulesMappedByName() const;
const std::map<std::string, Module>& getPeripheralModulesMappedByName() const;
std::optional<MemorySegment> getFlashMemorySegment() const;
std::optional<MemorySegment> getRamMemorySegment() const;
std::optional<MemorySegment> getRegisterMemorySegment() const;
std::optional<MemorySegment> getEepromMemorySegment() const;
std::optional<MemorySegment> getFirstBootSectionMemorySegment() const;
std::optional<RegisterGroup> getCpuRegisterGroup() const;
std::optional<RegisterGroup> getBootLoadRegisterGroup() const;
std::optional<RegisterGroup> getEepromRegisterGroup() const;
std::optional<Register> getStatusRegister() const;
std::optional<Register> getStackPointerRegister() const;
std::optional<Register> getStackPointerHighRegister() const;
std::optional<Register> getStackPointerLowRegister() const;
std::optional<Register> getOscillatorCalibrationRegister() const;
std::optional<Register> getSpmcsRegister() const;
std::optional<Register> getSpmcRegister() const;
std::optional<Register> getEepromAddressRegister() const;
std::optional<Register> getEepromDataRegister() const;
std::optional<Register> getEepromControlRegister() const;
std::vector<Variant> getVariants() const;
const std::map<std::string, Pinout>& getPinoutsMappedByName() const;
};
}

View File

@@ -1,17 +0,0 @@
#pragma once
#include <string>
#include <vector>
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct Pin {
std::string pad;
int position;
};
struct Pinout {
std::string name;
std::vector<Pin> pins;
};
}

View File

@@ -1,19 +0,0 @@
#pragma once
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct Property {
std::string name;
/*
* We use QString here as we're dealing with arbitrary values and QString provides many helpful
* functions to make this easier.
*/
QString value;
};
struct PropertyGroup {
std::string name;
std::map<std::string, Property> propertiesMappedByName;
};
}

View File

@@ -1,20 +0,0 @@
#pragma once
#include <cstdint>
#include <map>
#include <optional>
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct Register {
std::string name;
std::uint16_t offset;
std::uint16_t size;
};
struct RegisterGroup {
std::string name;
std::optional<std::uint16_t> offset;
std::map<std::string, Register> registersMappedByName;
};
}

View File

@@ -1,14 +0,0 @@
#pragma once
#include <string>
#include <optional>
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct Signal {
std::string padName;
std::string function;
std::optional<int> index;
std::string group;
};
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include <string>
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
{
struct Variant {
std::string orderCode;
std::string pinoutName;
std::string package;
bool disabled = false;
};
}

View File

@@ -0,0 +1,148 @@
#include <QJsonDocument>
#include <QJsonArray>
#include "TargetDescriptionFile.hpp"
#include "src/Exceptions/Exception.hpp"
#include "src/Targets/TargetDescription/Exceptions/TargetDescriptionParsingFailureException.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Helpers/Paths.hpp"
using namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription;
using namespace Bloom::Targets::Microchip::Avr::Avr8Bit;
using namespace Bloom::Targets::Microchip::Avr;
using namespace Bloom::Exceptions;
TargetDescriptionFile::TargetDescriptionFile(
const std::string& targetSignatureHex,
std::optional<std::string> targetName
) {
auto mapping = this->getTargetDescriptionMapping();
auto qTargetSignatureHex = QString::fromStdString(targetSignatureHex);
if (mapping.contains(qTargetSignatureHex)) {
// We have a match for the target signature.
auto descriptionFilesJsonArray = mapping.find(qTargetSignatureHex).value().toArray();
auto matchingDescriptionFiles = std::vector<QJsonValue>();
std::copy_if(
descriptionFilesJsonArray.begin(),
descriptionFilesJsonArray.end(),
std::back_inserter(matchingDescriptionFiles),
[&targetName] (const QJsonValue& value) {
auto pdTargetName = value.toObject().find("targetName")->toString().toLower().toStdString();
return !targetName.has_value() || (targetName.has_value() && targetName.value() == pdTargetName);
}
);
if (targetName.has_value() && matchingDescriptionFiles.empty()) {
throw Exception("Failed to resolve target description file for target \"" + targetName.value()
+ "\" - target signature \"" + targetSignatureHex + "\" does not belong to target with name \"" +
targetName.value() + "\". Please review your bloom.json configuration.");
}
if (matchingDescriptionFiles.size() == 1) {
// Attempt to load the XML target description file
auto descriptionFilePath = QString::fromStdString(Paths::applicationDirPath()) + "/"
+ matchingDescriptionFiles.front().toObject().find("targetDescriptionFilePath")->toString();
Logger::debug("Loading AVR8 target description file: " + descriptionFilePath.toStdString());
this->init(descriptionFilePath);
} else if (matchingDescriptionFiles.size() > 1) {
/*
* There are numerous target description files mapped to this target signature. There's really not
* much we can do at this point, so we'll just instruct the user to use a more specific target name.
*/
QStringList targetNames;
std::transform(
matchingDescriptionFiles.begin(),
matchingDescriptionFiles.end(),
std::back_inserter(targetNames),
[](const QJsonValue& descriptionFile) {
return QString("\"" + descriptionFile.toObject().find("targetName")->toString().toLower() + "\"");
}
);
throw Exception("Failed to resolve target description file for target \""
+ targetSignatureHex + "\" - ambiguous signature.\nThe signature is mapped to numerous targets: "
+ targetNames.join(", ").toStdString() + ".\n\nPlease update the target name in your Bloom " +
"configuration to one of the above."
);
} else {
throw Exception("Failed to resolve target description file for target \""
+ targetSignatureHex + "\" - invalid AVR8 target description mapping."
);
}
} else {
throw Exception("Failed to resolve target description file for target \""
+ targetSignatureHex + "\" - unknown target signature.");
}
}
QJsonObject TargetDescriptionFile::getTargetDescriptionMapping() {
auto mappingFile = QFile(
QString::fromStdString(Paths::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 {
auto propertyGroups = this->getPropertyGroupsMappedByName();
auto signaturePropertyGroupIt = propertyGroups.find("signatures");
if (signaturePropertyGroupIt == propertyGroups.end()) {
throw TargetDescriptionParsingFailureException("Signature property group not found");
}
auto signaturePropertyGroup = signaturePropertyGroupIt->second;
auto& signatureProperties = signaturePropertyGroup.propertiesMappedByName;
std::optional<unsigned char> signatureByteZero;
std::optional<unsigned char> signatureByteOne;
std::optional<unsigned char> signatureByteTwo;
if (signatureProperties.find("signature0") != signatureProperties.end()) {
signatureByteZero = static_cast<unsigned char>(
signatureProperties.find("signature0")->second.value.toShort(nullptr, 16)
);
}
if (signatureProperties.find("signature1") != signatureProperties.end()) {
signatureByteOne = static_cast<unsigned char>(
signatureProperties.find("signature1")->second.value.toShort(nullptr, 16)
);
}
if (signatureProperties.find("signature2") != signatureProperties.end()) {
signatureByteTwo = static_cast<unsigned char>(
signatureProperties.find("signature2")->second.value.toShort(nullptr, 16)
);
}
if (signatureByteZero.has_value() && signatureByteOne.has_value() && signatureByteTwo.has_value()) {
return TargetSignature(signatureByteZero.value(), signatureByteOne.value(), signatureByteTwo.value());
}
throw TargetDescriptionParsingFailureException("Failed to extract target signature from AVR8 target description.");
}
Family TargetDescriptionFile::getFamily() const {
static auto familyNameToEnums = this->getFamilyNameToEnumMapping();
auto familyName = this->deviceElement.attributes().namedItem("family").nodeValue().toLower().toStdString();
if (familyName.empty()) {
throw Exception("Could not find target family name in target description file.");
}
if (familyNameToEnums.find(familyName) == familyNameToEnums.end()) {
throw Exception("Unknown family name in target description file.");
}
return familyNameToEnums.find(familyName)->second;
}

View File

@@ -0,0 +1,72 @@
#pragma once
#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp"
#include "src/Targets/Microchip/AVR/TargetSignature.hpp"
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
{
/**
* Represents an AVR8 TDF. See the Targets::TargetDescription::TargetDescriptionFile close for more on TDFs.
*
* During the build process, we generate a JSON file containing a mapping of AVR8 target signatures to target
* description file paths. Bloom uses this mapping to find a particular target description file, for AVR8 targets,
* given a target signature. See directory "build/resources/TargetPartDescriptions".
* The generation of the JSON mapping, is done by a PHP script:
* "build/scripts/CopyAvrPartFilesAndCreateMapping.php". This script is invoked via a custom command, at build time.
*/
class TargetDescriptionFile: public Targets::TargetDescription::TargetDescriptionFile
{
private:
/**`
* AVR8 target description files include the target family name. This method returns a mapping of part
* description family name strings to Family enum values.
*
* TODO: the difference in AVR8 family variations, like "tinyAVR" and "tinyAVR 2" may require attention.
*
* @return
*/
static inline auto getFamilyNameToEnumMapping() {
// All keys should be lower case.
return std::map<std::string, Family> {
{"megaavr", Family::MEGA},
{"avr mega", Family::MEGA},
{"avr xmega", Family::XMEGA},
{"avr tiny", Family::TINY},
{"tinyavr", Family::TINY},
{"tinyavr 2", Family::TINY},
};
};
public:
/**
* Will resolve the target description file using the target description JSON mapping and a given target signature.
*
* @param targetSignatureHex
* @param targetName
*/
TargetDescriptionFile(const std::string& targetSignatureHex, std::optional<std::string> targetName);
/**
* Loads the AVR8 target description JSON mapping file.
*
* @return
*/
static QJsonObject getTargetDescriptionMapping();
/**
* Extracts the AVR8 target signature from the target description XML.
*
* @return
*/
TargetSignature getTargetSignature() const;
/**
* Extracts the AVR8 target family from the target description XML.
*
* @return
*/
Family getFamily() const;
};
}

View File

@@ -3,7 +3,6 @@
#include <cstdint>
#include <optional>
#include "src/Targets/Microchip/AVR/AVR8/PartDescription/AddressSpace.hpp"
#include "../TargetSignature.hpp"
#include "Family.hpp"