Files
BloomPatched/src/Targets/TargetDescription/TargetDescriptionFile.cpp
Nav 4dc019e915 Moved RISC-V CSR and GPR address spaces to TDF.
Some other bits of refactoring/tidying
2024-12-27 03:41:39 +00:00

1232 lines
46 KiB
C++

#include "TargetDescriptionFile.hpp"
#include <QFile>
#include "src/Services/PathService.hpp"
#include "src/Services/StringService.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Targets/TargetPinoutDescriptor.hpp"
#include "src/Helpers/BiMap.hpp"
#include "src/Logger/Logger.hpp"
#include "Exceptions/TargetDescriptionParsingFailureException.hpp"
#include "Exceptions/InvalidTargetDescriptionDataException.hpp"
namespace Targets::TargetDescription
{
using namespace Exceptions;
using namespace ::Exceptions;
using Services::StringService;
TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) {
this->init(xmlFilePath);
}
TargetDescriptionFile::TargetDescriptionFile(const QDomDocument& xml) {
this->init(xml);
}
const std::string& TargetDescriptionFile::getName() const {
return this->getDeviceAttribute("name");
}
TargetFamily TargetDescriptionFile::getFamily() const {
const auto& familyName = this->getDeviceAttribute("family");
if (familyName == "AVR8") {
return TargetFamily::AVR_8;
}
if (familyName == "RISCV") {
return TargetFamily::RISC_V;
}
throw InvalidTargetDescriptionDataException{"Failed to resolve target family - invalid family name"};
}
std::optional<std::string> TargetDescriptionFile::tryGetVendorName() const {
return this->tryGetDeviceAttribute("vendor");
}
const std::string& TargetDescriptionFile::getVendorName() const {
return this->getDeviceAttribute("vendor");
}
std::optional<std::reference_wrapper<const PropertyGroup>> TargetDescriptionFile::tryGetPropertyGroup(
std::string_view keyStr
) const {
const auto keys = StringService::split(keyStr, '.');
const auto firstSubgroupIt = this->propertyGroupsByKey.find(*keys.begin());
return firstSubgroupIt != this->propertyGroupsByKey.end()
? keys.size() > 1
? firstSubgroupIt->second.tryGetSubgroup(keys | std::ranges::views::drop(1))
: std::optional{std::cref(firstSubgroupIt->second)}
: std::nullopt;
}
const PropertyGroup& TargetDescriptionFile::getPropertyGroup(std::string_view keyStr) const {
const auto propertyGroup = this->tryGetPropertyGroup(keyStr);
if (!propertyGroup.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to get property group \"" + std::string{keyStr} + "\" from TDF - property group not found"
};
}
return propertyGroup->get();
}
std::optional<std::reference_wrapper<const Property>> TargetDescriptionFile::tryGetProperty(
std::string_view groupKey,
std::string_view propertyKey
) const {
const auto propertyGroup = this->tryGetPropertyGroup(groupKey);
if (!propertyGroup.has_value()) {
return std::nullopt;
}
return propertyGroup->get().tryGetProperty(propertyKey);
}
const Property& TargetDescriptionFile::getProperty(std::string_view groupKey, std::string_view propertyKey) const {
const auto property = this->tryGetProperty(groupKey, propertyKey);
if (!property.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to get property \"" + std::string{propertyKey} + "\" from group \"" + std::string{groupKey}
+ "\", from TDF - property/group not found"
};
}
return property->get();
}
std::optional<std::reference_wrapper<const AddressSpace>> 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 InvalidTargetDescriptionDataException{
"Failed to get address space \"" + std::string{key} + "\" from TDF - address space not found"
};
}
return addressSpace->get();
}
std::optional<std::reference_wrapper<const MemorySegment>> TargetDescriptionFile::tryGetMemorySegment(
std::string_view addressSpaceKey,
std::string_view segmentKey
) const {
const auto addressSpace = this->tryGetAddressSpace(addressSpaceKey);
if (!addressSpace.has_value()) {
return std::nullopt;
}
return addressSpace->get().tryGetMemorySegment(segmentKey);
}
const MemorySegment& TargetDescriptionFile::getMemorySegment(
std::string_view addressSpaceKey,
std::string_view segmentKey
) const {
const auto segment = this->tryGetMemorySegment(addressSpaceKey, segmentKey);
if (!segment.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to get memory segment \"" + std::string{segmentKey} + "\" from address space \""
+ std::string{addressSpaceKey} + "\" from TDF"
};
}
return segment->get();
}
std::set<TargetPhysicalInterface> TargetDescriptionFile::getPhysicalInterfaces() const {
static const auto physicalInterfacesByValue = BiMap<std::string, TargetPhysicalInterface>{
{"updi", TargetPhysicalInterface::UPDI},
{"debug_wire", TargetPhysicalInterface::DEBUG_WIRE},
{"jtag", TargetPhysicalInterface::JTAG},
{"pdi", TargetPhysicalInterface::PDI},
{"isp", TargetPhysicalInterface::ISP},
{"sdi", TargetPhysicalInterface::SDI},
};
auto output = std::set<TargetPhysicalInterface>{};
for (const auto& physicalInterface : this->physicalInterfaces) {
const auto interface = physicalInterfacesByValue.valueAt(physicalInterface.value);
if (interface.has_value()) {
output.insert(*interface);
}
}
return output;
}
std::optional<std::reference_wrapper<const Module>> 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();
}
std::optional<std::reference_wrapper<const Peripheral>> TargetDescriptionFile::tryGetPeripheral(
std::string_view key
) const {
const auto peripheralIt = this->peripheralsByKey.find(key);
return peripheralIt != this->peripheralsByKey.end()
? std::optional{std::cref(peripheralIt->second)}
: std::nullopt;
}
const Peripheral& TargetDescriptionFile::getPeripheral(std::string_view key) const {
const auto peripheral = this->tryGetPeripheral(key);
if (!peripheral.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to get peripheral \"" + std::string{key} + "\" from TDF - peripheral not found"
};
}
return peripheral->get();
}
std::set<const Peripheral*> TargetDescriptionFile::getModulePeripherals(const std::string& moduleKey) const {
auto output = std::set<const Peripheral*>{};
for (const auto& [peripheralKey, peripheral] : this->peripheralsByKey) {
if (peripheral.moduleKey == moduleKey) {
output.insert(&peripheral);
}
}
return output;
}
std::set<const Peripheral*> TargetDescriptionFile::getGpioPeripherals() const {
return this->getModulePeripherals("gpio_port");
}
std::optional<TargetMemorySegmentDescriptor> TargetDescriptionFile::tryGetTargetMemorySegmentDescriptor(
std::string_view addressSpaceKey,
std::string_view segmentKey
) const {
const auto addressSpace = this->tryGetAddressSpace(addressSpaceKey);
if (!addressSpace.has_value()) {
return std::nullopt;
}
const auto segment = addressSpace->get().tryGetMemorySegment(segmentKey);
if (!segment.has_value()) {
return std::nullopt;
}
return TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment(
segment->get(),
addressSpace->get()
);
}
TargetMemorySegmentDescriptor TargetDescriptionFile::getTargetMemorySegmentDescriptor(
std::string_view addressSpaceKey,
std::string_view segmentKey
) const {
const auto& addressSpace = this->getAddressSpace(addressSpaceKey);
return TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment(
addressSpace.getMemorySegment(segmentKey),
addressSpace
);
}
std::optional<TargetPeripheralDescriptor> TargetDescriptionFile::tryGetTargetPeripheralDescriptor(
std::string_view key
) const {
const auto peripheral = this->tryGetPeripheral(key);
if (!peripheral.has_value()) {
return std::nullopt;
}
return TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral(
peripheral->get(),
this->getModule(peripheral->get().moduleKey)
);
}
TargetPeripheralDescriptor TargetDescriptionFile::getTargetPeripheralDescriptor(std::string_view key) const {
const auto& peripheral = this->getPeripheral(key);
return TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral(
peripheral,
this->getModule(peripheral.moduleKey)
);
}
std::map<
std::string,
TargetAddressSpaceDescriptor
> TargetDescriptionFile::targetAddressSpaceDescriptorsByKey() const {
auto output = std::map<std::string, TargetAddressSpaceDescriptor>{};
for (const auto& [key, addressSpace] : this->addressSpacesByKey) {
output.emplace(key, TargetDescriptionFile::targetAddressSpaceDescriptorFromAddressSpace(addressSpace));
}
return output;
}
std::map<std::string, TargetPeripheralDescriptor> TargetDescriptionFile::targetPeripheralDescriptorsByKey() const {
auto output = std::map<std::string, TargetPeripheralDescriptor>{};
for (const auto& [key, peripheral] : this->peripheralsByKey) {
output.emplace(
key,
TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral(
peripheral,
this->getModule(peripheral.moduleKey)
)
);
}
return output;
}
std::map<std::string, TargetPadDescriptor> TargetDescriptionFile::targetPadDescriptorsByKey() const {
auto output = std::map<std::string, TargetPadDescriptor>{};
const auto gpioPeripheralSignalPadKeys = this->getGpioPeripheralSignalPadKeys();
for (const auto& [key, pad] : this->padsByKey) {
output.emplace(
key,
TargetDescriptionFile::targetPadDescriptorFromPad(pad, gpioPeripheralSignalPadKeys)
);
}
return output;
}
std::map<std::string, TargetPinoutDescriptor> TargetDescriptionFile::targetPinoutDescriptorsByKey() const {
auto output = std::map<std::string, TargetPinoutDescriptor>{};
for (const auto& [key, pinout] : this->pinoutsByKey) {
output.emplace(
key,
TargetDescriptionFile::targetPinoutDescriptorFromPinout(pinout)
);
}
return output;
}
std::map<std::string, TargetVariantDescriptor> TargetDescriptionFile::targetVariantDescriptorsByKey() const {
auto output = std::map<std::string, TargetVariantDescriptor>{};
for (const auto& [key, variant] : this->variantsByKey) {
output.emplace(
key,
TargetDescriptionFile::targetVariantDescriptorFromVariant(variant)
);
}
return output;
}
std::vector<TargetPadDescriptor> TargetDescriptionFile::targetPadDescriptors() const {
auto output = std::vector<TargetPadDescriptor>{};
const auto gpioPeripheralSignalPadKeys = this->getGpioPeripheralSignalPadKeys();
for (const auto& [key, pad] : this->padsByKey) {
output.emplace_back(TargetDescriptionFile::targetPadDescriptorFromPad(pad, gpioPeripheralSignalPadKeys));
}
return output;
}
std::vector<TargetPeripheralDescriptor> TargetDescriptionFile::gpioPortPeripheralDescriptors() const {
auto output = std::vector<TargetPeripheralDescriptor>{};
const auto& gpioPortModule = this->getModule("gpio_port");
for (const auto& [peripheralKey, peripheral] : this->peripheralsByKey) {
if (peripheral.moduleKey != gpioPortModule.key) {
continue;
}
output.emplace_back(
TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral(peripheral, gpioPortModule)
);
}
return output;
}
void TargetDescriptionFile::init(const std::string& xmlFilePath) {
auto file = QFile{QString::fromStdString(xmlFilePath)};
if (!file.exists()) {
throw InternalFatalErrorException{"Failed to load target description file - file not found"};
}
file.open(QIODevice::ReadOnly);
auto document = QDomDocument{};
if (!document.setContent(file.readAll())) {
throw TargetDescriptionParsingFailureException{};
}
this->init(document);
}
void TargetDescriptionFile::init(const QDomDocument& document) {
const auto deviceElement = document.documentElement();
if (deviceElement.nodeName() != "device") {
throw TargetDescriptionParsingFailureException{"Root \"device\" element not found."};
}
const auto deviceAttributes = deviceElement.attributes();
for (auto i = 0; i < deviceAttributes.length(); ++i) {
const auto deviceAttribute = deviceAttributes.item(i);
this->deviceAttributesByName.emplace(
deviceAttribute.nodeName().toStdString(),
deviceAttribute.nodeValue().toStdString()
);
}
for (
auto element = deviceElement.firstChildElement("property-groups").firstChildElement("property-group");
!element.isNull();
element = element.nextSiblingElement("property-group")
) {
auto propertyGroup = TargetDescriptionFile::propertyGroupFromXml(element);
this->propertyGroupsByKey.emplace(propertyGroup.key, std::move(propertyGroup));
}
for (
auto element = deviceElement.firstChildElement("address-spaces").firstChildElement("address-space");
!element.isNull();
element = element.nextSiblingElement("address-space")
) {
auto addressSpace = TargetDescriptionFile::addressSpaceFromXml(element);
this->addressSpacesByKey.emplace(addressSpace.key, std::move(addressSpace));
}
for (
auto element = deviceElement.firstChildElement("physical-interfaces")
.firstChildElement("physical-interface");
!element.isNull();
element = element.nextSiblingElement("physical-interface")
) {
this->physicalInterfaces.emplace_back(TargetDescriptionFile::physicalInterfaceFromXml(element));
}
for (
auto element = deviceElement.firstChildElement("modules").firstChildElement("module");
!element.isNull();
element = element.nextSiblingElement("module")
) {
auto module = TargetDescriptionFile::moduleFromXml(element);
this->modulesByKey.emplace(module.key, std::move(module));
}
for (
auto element = deviceElement.firstChildElement("peripherals").firstChildElement("peripheral");
!element.isNull();
element = element.nextSiblingElement("peripheral")
) {
auto peripheral = TargetDescriptionFile::peripheralFromXml(element);
this->peripheralsByKey.emplace(peripheral.key, std::move(peripheral));
}
for (
auto element = deviceElement.firstChildElement("pads").firstChildElement("pad");
!element.isNull();
element = element.nextSiblingElement("pad")
) {
auto pad = TargetDescriptionFile::padFromXml(element);
this->padsByKey.emplace(pad.key, std::move(pad));
}
for (
auto element = deviceElement.firstChildElement("pinouts").firstChildElement("pinout");
!element.isNull();
element = element.nextSiblingElement("pinout")
) {
auto pinout = TargetDescriptionFile::pinoutFromXml(element);
this->pinoutsByKey.emplace(pinout.key, std::move(pinout));
}
for (
auto element = deviceElement.firstChildElement("variants").firstChildElement("variant");
!element.isNull();
element = element.nextSiblingElement("variant")
) {
auto variant = TargetDescriptionFile::variantFromXml(element);
this->variantsByKey.emplace(variant.key, std::move(variant));
}
}
std::optional<std::reference_wrapper<const std::string>> TargetDescriptionFile::tryGetDeviceAttribute(
const std::string& attributeName
) const {
const auto attributeIt = this->deviceAttributesByName.find(attributeName);
if (attributeIt == this->deviceAttributesByName.end()) {
return std::nullopt;
}
return std::cref(attributeIt->second);
}
const std::string& TargetDescriptionFile::getDeviceAttribute(const std::string& attributeName) const {
const auto attribute = this->tryGetDeviceAttribute(attributeName);
if (!attribute.has_value()) {
throw InvalidTargetDescriptionDataException{"Missing target device attribute (\"" + attributeName + "\")"};
}
return attribute->get();
}
std::set<std::string> TargetDescriptionFile::getGpioPeripheralSignalPadKeys() const {
auto output = std::set<std::string>{};
for (const auto* peripheral : this->getGpioPeripherals()) {
for (const auto& signal : peripheral->sigs) {
output.insert(signal.padKey);
}
}
return output;
}
std::optional<std::string> TargetDescriptionFile::tryGetAttribute(
const QDomElement& element,
const QString& attributeName
) {
return element.hasAttribute(attributeName)
? std::optional{element.attribute(attributeName).toStdString()}
: std::nullopt;
}
std::string TargetDescriptionFile::getAttribute(const QDomElement& element, const QString& attributeName) {
const auto attribute = TargetDescriptionFile::tryGetAttribute(element, attributeName);
if (!attribute.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to fetch attribute from TDF element \"" + element.nodeName().toStdString()
+ "\" - attribute \"" + attributeName.toStdString() + "\" not found"
};
}
return *attribute;
}
PropertyGroup TargetDescriptionFile::propertyGroupFromXml(const QDomElement& xmlElement) {
auto output = PropertyGroup{TargetDescriptionFile::getAttribute(xmlElement, "key"), {}, {}};
for (
auto element = xmlElement.firstChildElement("property");
!element.isNull();
element = element.nextSiblingElement("property")
) {
auto property = TargetDescriptionFile::propertyFromXml(element);
output.propertiesByKey.emplace(property.key, std::move(property));
}
for (
auto element = xmlElement.firstChildElement("property-group");
!element.isNull();
element = element.nextSiblingElement("property-group")
) {
auto subgroup = TargetDescriptionFile::propertyGroupFromXml(element);
output.subgroupsByKey.emplace(subgroup.key, std::move(subgroup));
}
return output;
}
Property TargetDescriptionFile::propertyFromXml(const QDomElement& xmlElement) {
return {
TargetDescriptionFile::getAttribute(xmlElement, "key"),
TargetDescriptionFile::getAttribute(xmlElement, "value")
};
}
AddressSpace TargetDescriptionFile::addressSpaceFromXml(const QDomElement& xmlElement) {
static const auto endiannessByName = BiMap<std::string, TargetMemoryEndianness>{
{"big", TargetMemoryEndianness::BIG},
{"little", TargetMemoryEndianness::LITTLE},
};
const auto endiannessName = TargetDescriptionFile::tryGetAttribute(xmlElement, "endianness");
auto endianness = std::optional<TargetMemoryEndianness>{};
if (endiannessName.has_value()) {
endianness = endiannessByName.valueAt(*endiannessName);
if (!endianness.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to extract address space from TDF - invalid endianness name \"" + *endiannessName + "\""
};
}
}
const auto unitSize = TargetDescriptionFile::tryGetAttribute(xmlElement, "unit-size");
auto output = AddressSpace{
TargetDescriptionFile::getAttribute(xmlElement, "key"),
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")),
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")),
unitSize.has_value() ? StringService::toUint8(*unitSize) : std::uint8_t{1},
endianness,
{}
};
for (
auto element = xmlElement.firstChildElement("memory-segment");
!element.isNull();
element = element.nextSiblingElement("memory-segment")
) {
auto section = TargetDescriptionFile::memorySegmentFromXml(element);
output.memorySegmentsByKey.emplace(section.key, std::move(section));
}
return output;
}
MemorySegment TargetDescriptionFile::memorySegmentFromXml(const QDomElement& xmlElement) {
static const auto typesByName = BiMap<std::string, TargetMemorySegmentType>{
{"gp_registers", TargetMemorySegmentType::GENERAL_PURPOSE_REGISTERS},
{"registers", TargetMemorySegmentType::REGISTERS},
{"aliased", TargetMemorySegmentType::ALIASED},
{"eeprom", TargetMemorySegmentType::EEPROM},
{"flash", TargetMemorySegmentType::FLASH},
{"fuses", TargetMemorySegmentType::FUSES},
{"io", TargetMemorySegmentType::IO},
{"ram", TargetMemorySegmentType::RAM},
{"lockbits", TargetMemorySegmentType::LOCKBITS},
{"osccal", TargetMemorySegmentType::OSCCAL},
{"production_signatures", TargetMemorySegmentType::PRODUCTION_SIGNATURES},
{"signatures", TargetMemorySegmentType::SIGNATURES},
{"user_signatures", TargetMemorySegmentType::USER_SIGNATURES},
};
const auto typeName = TargetDescriptionFile::getAttribute(xmlElement, "type");
const auto type = typesByName.valueAt(typeName);
if (!type.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to extract memory segment from TDF - invalid memory segment type name \"" + typeName + "\""
};
}
const auto pageSize = TargetDescriptionFile::tryGetAttribute(xmlElement, "page-size");
const auto accessString = TargetDescriptionFile::tryGetAttribute(xmlElement, "access");
auto output = MemorySegment{
TargetDescriptionFile::getAttribute(xmlElement, "key"),
TargetDescriptionFile::getAttribute(xmlElement, "name"),
*type,
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")),
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")),
TargetDescriptionFile::getAttribute(xmlElement, "executable") == "1",
TargetMemoryAccess{
!accessString.has_value() || accessString->find('R') != std::string::npos,
!accessString.has_value() || accessString->find('W') != std::string::npos
},
pageSize.has_value()
? std::optional{StringService::toUint32(*pageSize)}
: std::nullopt,
{}
};
for (
auto element = xmlElement.firstChildElement("section");
!element.isNull();
element = element.nextSiblingElement("section")
) {
auto section = TargetDescriptionFile::memorySegmentSectionFromXml(element);
output.sectionsByKey.emplace(section.key, std::move(section));
}
return output;
}
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.emplace(section.key, std::move(section));
}
return output;
}
PhysicalInterface TargetDescriptionFile::physicalInterfaceFromXml(const QDomElement& xmlElement) {
return {
TargetDescriptionFile::getAttribute(xmlElement, "value")
};
}
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.emplace(registerGroup.key, std::move(registerGroup));
}
return output;
}
RegisterGroup TargetDescriptionFile::registerGroupFromXml(const QDomElement& xmlElement) {
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.emplace(reg.key, std::move(reg));
}
for (
auto element = xmlElement.firstChildElement("register-group");
!element.isNull();
element = element.nextSiblingElement("register-group")
) {
auto registerGroup = TargetDescriptionFile::registerGroupFromXml(element);
output.subgroupsByKey.emplace(registerGroup.key, std::move(registerGroup));
}
for (
auto element = xmlElement.firstChildElement("register-group-reference");
!element.isNull();
element = element.nextSiblingElement("register-group-reference")
) {
auto registerGroupReference = TargetDescriptionFile::registerGroupReferenceFromXml(element);
output.subgroupReferencesByKey.emplace(registerGroupReference.key, std::move(registerGroupReference));
}
return output;
}
RegisterGroupReference TargetDescriptionFile::registerGroupReferenceFromXml(const QDomElement& xmlElement) {
return {
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) {
const auto initialValue = TargetDescriptionFile::tryGetAttribute(xmlElement, "initial-value");
const auto alternative = TargetDescriptionFile::tryGetAttribute(xmlElement, "alternative");
const auto accessString = TargetDescriptionFile::tryGetAttribute(xmlElement, "access");
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,
accessString.has_value()
? std::optional{TargetRegisterAccess{
accessString->find('R') != std::string::npos,
accessString->find('W') != std::string::npos
}}
: std::nullopt,
alternative.has_value() ? std::optional{*alternative == "true"} : std::nullopt,
{}
};
for (
auto element = xmlElement.firstChildElement("bit-field");
!element.isNull();
element = element.nextSiblingElement("bit-field")
) {
auto bitField = TargetDescriptionFile::bitFieldFromXml(element);
output.bitFieldsByKey.emplace(bitField.key, std::move(bitField));
}
return output;
}
BitField TargetDescriptionFile::bitFieldFromXml(const QDomElement& xmlElement) {
return {
TargetDescriptionFile::getAttribute(xmlElement, "key"),
TargetDescriptionFile::getAttribute(xmlElement, "name"),
TargetDescriptionFile::tryGetAttribute(xmlElement, "description"),
StringService::toUint64(TargetDescriptionFile::getAttribute(xmlElement, "mask")),
TargetDescriptionFile::tryGetAttribute(xmlElement, "access")
};
}
Peripheral TargetDescriptionFile::peripheralFromXml(const QDomElement& xmlElement) {
const auto offset = TargetDescriptionFile::tryGetAttribute(xmlElement, "offset");
auto output = Peripheral{
TargetDescriptionFile::getAttribute(xmlElement, "key"),
TargetDescriptionFile::getAttribute(xmlElement, "name"),
TargetDescriptionFile::getAttribute(xmlElement, "module-key"),
{},
{}
};
for (
auto element = xmlElement.firstChildElement("register-group-instance");
!element.isNull();
element = element.nextSiblingElement("register-group-instance")
) {
auto registerGroupInstance = TargetDescriptionFile::registerGroupInstanceFromXml(element);
output.registerGroupInstancesByKey.emplace(
registerGroupInstance.key.value_or(registerGroupInstance.registerGroupKey),
std::move(registerGroupInstance)
);
}
for (
auto element = xmlElement.firstChildElement("signals").firstChildElement("signal");
!element.isNull();
element = element.nextSiblingElement("signal")
) {
output.sigs.emplace_back(TargetDescriptionFile::signalFromXml(element));
}
return output;
}
RegisterGroupInstance TargetDescriptionFile::registerGroupInstanceFromXml(const QDomElement& xmlElement) {
return {
TargetDescriptionFile::tryGetAttribute(xmlElement, "key"),
TargetDescriptionFile::tryGetAttribute(xmlElement, "name"),
TargetDescriptionFile::getAttribute(xmlElement, "register-group-key"),
TargetDescriptionFile::getAttribute(xmlElement, "address-space-key"),
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")),
TargetDescriptionFile::tryGetAttribute(xmlElement, "description")
};
}
Signal TargetDescriptionFile::signalFromXml(const QDomElement& xmlElement) {
const auto alternative = TargetDescriptionFile::tryGetAttribute(xmlElement, "alternative");
const auto index = TargetDescriptionFile::tryGetAttribute(xmlElement, "index");
return {
TargetDescriptionFile::getAttribute(xmlElement, "name"),
TargetDescriptionFile::getAttribute(xmlElement, "pad-key"),
alternative.has_value() ? std::optional{*alternative == "true"} : std::nullopt,
index.has_value() ? std::optional{StringService::toUint64(*index)} : std::nullopt,
TargetDescriptionFile::tryGetAttribute(xmlElement, "function"),
TargetDescriptionFile::tryGetAttribute(xmlElement, "field")
};
}
Pad TargetDescriptionFile::padFromXml(const QDomElement& xmlElement) {
return {
TargetDescriptionFile::getAttribute(xmlElement, "key"),
TargetDescriptionFile::getAttribute(xmlElement, "name")
};
}
Pinout TargetDescriptionFile::pinoutFromXml(const QDomElement& xmlElement) {
static const auto typesByName = BiMap<std::string, TargetPinoutType>{
{"soic", TargetPinoutType::SOIC},
{"ssop", TargetPinoutType::SSOP},
{"dip", TargetPinoutType::DIP},
{"qfn", TargetPinoutType::QFN},
{"mlf", TargetPinoutType::MLF},
{"drqfn", TargetPinoutType::DUAL_ROW_QFN},
{"qfp", TargetPinoutType::QFP},
{"bga", TargetPinoutType::BGA},
};
const auto typeName = TargetDescriptionFile::getAttribute(xmlElement, "type");
const auto type = typesByName.valueAt(typeName);
if (!type.has_value()) {
throw InvalidTargetDescriptionDataException{
"Failed to extract pinout from TDF - invalid pinout type name \"" + typeName + "\""
};
}
auto output = Pinout{
TargetDescriptionFile::getAttribute(xmlElement, "key"),
TargetDescriptionFile::getAttribute(xmlElement, "name"),
*type,
TargetDescriptionFile::tryGetAttribute(xmlElement, "function"),
{}
};
for (
auto element = xmlElement.firstChildElement("pin");
!element.isNull();
element = element.nextSiblingElement("pin")
) {
output.pins.emplace_back(TargetDescriptionFile::pinFromXml(element));
}
return output;
}
Pin TargetDescriptionFile::pinFromXml(const QDomElement& xmlElement) {
return {
TargetDescriptionFile::getAttribute(xmlElement, "position"),
TargetDescriptionFile::tryGetAttribute(xmlElement, "pad-key")
};
}
Variant TargetDescriptionFile::variantFromXml(const QDomElement& xmlElement) {
auto output = Variant{
TargetDescriptionFile::getAttribute(xmlElement, "key"),
TargetDescriptionFile::getAttribute(xmlElement, "name"),
TargetDescriptionFile::getAttribute(xmlElement, "pinout-key"),
{}
};
for (
auto element = xmlElement.firstChildElement("property-groups").firstChildElement("property-group");
!element.isNull();
element = element.nextSiblingElement("property-group")
) {
auto propertyGroup = TargetDescriptionFile::propertyGroupFromXml(element);
output.propertyGroupsByKey.emplace(propertyGroup.key, std::move(propertyGroup));
}
return output;
}
TargetAddressSpaceDescriptor TargetDescriptionFile::targetAddressSpaceDescriptorFromAddressSpace(
const AddressSpace& addressSpace
) {
auto output = TargetAddressSpaceDescriptor{
addressSpace.key,
TargetMemoryAddressRange{
addressSpace.startAddress,
addressSpace.startAddress + addressSpace.size - 1
},
addressSpace.endianness.value_or(TargetMemoryEndianness::LITTLE),
{}
};
for (const auto& [key, memorySegment] : addressSpace.memorySegmentsByKey) {
output.segmentDescriptorsByKey.emplace(
key,
TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment(memorySegment, addressSpace)
);
}
return output;
}
TargetMemorySegmentDescriptor TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment(
const MemorySegment& memorySegment,
const AddressSpace& addressSpace
) {
return {
addressSpace.key,
memorySegment.key,
memorySegment.name,
memorySegment.type,
TargetMemoryAddressRange{
memorySegment.startAddress,
memorySegment.startAddress + (memorySegment.size / addressSpace.unitSize) - 1
},
addressSpace.unitSize,
memorySegment.executable,
memorySegment.access,
memorySegment.access,
false,
memorySegment.pageSize
};
}
TargetPeripheralDescriptor TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral(
const Peripheral& peripheral,
const Module& peripheralModule
) {
auto output = TargetPeripheralDescriptor{
peripheral.key,
peripheral.name,
peripheralModule.description,
{},
{}
};
for (const auto& [key, registerGroupInstance] : peripheral.registerGroupInstancesByKey) {
output.registerGroupDescriptorsByKey.emplace(
key,
TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup(
peripheralModule.getRegisterGroup(registerGroupInstance.registerGroupKey),
peripheralModule,
peripheral.key,
registerGroupInstance.addressSpaceKey,
registerGroupInstance.offset,
std::nullopt,
registerGroupInstance.description,
registerGroupInstance.key,
registerGroupInstance.name
)
);
}
for (const auto& signal : peripheral.sigs) {
output.signalDescriptors.emplace_back(
TargetDescriptionFile::targetPeripheralSignalDescriptorFromSignal(signal)
);
}
return output;
}
TargetPeripheralSignalDescriptor TargetDescriptionFile::targetPeripheralSignalDescriptorFromSignal(
const Signal& signal
) {
return {
signal.padKey,
signal.index
};
}
TargetRegisterGroupDescriptor TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup(
const RegisterGroup& registerGroup,
const Module& peripheralModule,
const std::string& peripheralKey,
const std::string& addressSpaceKey,
TargetMemoryAddress baseAddress,
const std::optional<std::string>& parentGroupAbsoluteKey,
const std::optional<std::string>& description,
const std::optional<std::string>& keyOverride,
const std::optional<std::string>& nameOverride
) {
const auto& key = keyOverride.has_value() ? *keyOverride : registerGroup.key;
const auto& name = nameOverride.has_value() ? *nameOverride : registerGroup.name;
const auto absoluteKey = parentGroupAbsoluteKey.has_value()
? *parentGroupAbsoluteKey + "." + key
: key;
auto output = TargetRegisterGroupDescriptor{
key,
absoluteKey,
name,
peripheralKey,
addressSpaceKey,
description,
{},
{}
};
for (const auto& [key, subgroup] : registerGroup.subgroupsByKey) {
output.subgroupDescriptorsByKey.emplace(
key,
TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup(
subgroup,
peripheralModule,
peripheralKey,
addressSpaceKey,
baseAddress + registerGroup.offset.value_or(0),
absoluteKey
)
);
}
for (const auto& [key, subgroupReference] : registerGroup.subgroupReferencesByKey) {
const auto& referencedGroup = peripheralModule.getRegisterGroup(subgroupReference.registerGroupKey);
output.subgroupDescriptorsByKey.emplace(
key,
TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup(
referencedGroup,
peripheralModule,
peripheralKey,
addressSpaceKey,
baseAddress + registerGroup.offset.value_or(0) + subgroupReference.offset,
absoluteKey,
subgroupReference.description,
subgroupReference.key,
subgroupReference.name
)
);
}
for (const auto& [key, reg] : registerGroup.registersByKey) {
output.registerDescriptorsByKey.emplace(
key,
TargetDescriptionFile::targetRegisterDescriptorFromRegister(
reg,
absoluteKey,
peripheralKey,
addressSpaceKey,
baseAddress + registerGroup.offset.value_or(0)
)
);
}
return output;
}
TargetRegisterDescriptor TargetDescriptionFile::targetRegisterDescriptorFromRegister(
const Register& reg,
const std::string& absoluteGroupKey,
const std::string& peripheralKey,
const std::string& addressSpaceKey,
TargetMemoryAddress baseAddress
) {
auto output = TargetRegisterDescriptor{
reg.key,
reg.name,
absoluteGroupKey,
peripheralKey,
addressSpaceKey,
baseAddress + reg.offset,
reg.size,
TargetRegisterType::OTHER,
reg.access.value_or(TargetRegisterAccess{true, true}),
reg.description,
{}
};
for (const auto& [key, bitField] : reg.bitFieldsByKey) {
output.bitFieldDescriptorsByKey.emplace(
key,
TargetDescriptionFile::targetBitFieldDescriptorFromBitField(bitField)
);
}
return output;
}
TargetBitFieldDescriptor TargetDescriptionFile::targetBitFieldDescriptorFromBitField(const BitField& bitField) {
return {
bitField.key,
bitField.name,
bitField.mask,
bitField.description
};
}
TargetPadDescriptor TargetDescriptionFile::targetPadDescriptorFromPad(
const Pad& pad,
const std::set<std::string>& gpioPeripheralSignalPadKeys
) {
const auto resolvePadType = [&gpioPeripheralSignalPadKeys] (const Pad& pad) -> TargetPadType {
if (gpioPeripheralSignalPadKeys.contains(pad.key)) {
return TargetPadType::GPIO;
}
const auto padNameLower = StringService::asciiToLower(pad.name);
if (
padNameLower.find("vcc") == 0
|| padNameLower.find("avcc") == 0
|| padNameLower.find("aref") == 0
|| padNameLower.find("avdd") == 0
|| padNameLower.find("vdd") == 0
) {
return TargetPadType::VCC;
}
if (padNameLower.find("gnd") == 0 || padNameLower.find("agnd") == 0) {
return TargetPadType::GND;
}
return TargetPadType::OTHER;
};
return {
pad.key,
pad.name,
resolvePadType(pad)
};
}
TargetPinoutDescriptor TargetDescriptionFile::targetPinoutDescriptorFromPinout(const Pinout& pinout) {
auto output = TargetPinoutDescriptor{
pinout.key,
pinout.name,
pinout.type,
{}
};
for (const auto& pin : pinout.pins) {
output.pinDescriptors.emplace_back(TargetDescriptionFile::targetPinDescriptorFromPin(pin));
}
return output;
}
TargetPinDescriptor TargetDescriptionFile::targetPinDescriptorFromPin(const Pin& pin) {
return {
pin.position,
pin.padKey
};
}
TargetVariantDescriptor TargetDescriptionFile::targetVariantDescriptorFromVariant(const Variant& variant) {
return {
variant.key,
variant.name,
variant.pinoutKey
};
}
}