2021-10-02 17:39:27 +01:00
|
|
|
#include "TargetDescriptionFile.hpp"
|
|
|
|
|
|
2021-05-30 19:05:18 +01:00
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
|
2022-12-26 21:47:09 +00:00
|
|
|
#include "src/Services/PathService.hpp"
|
2024-02-12 19:23:00 +00:00
|
|
|
#include "src/Services/StringService.hpp"
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
#include "src/Targets/TargetMemory.hpp"
|
|
|
|
|
#include "src/Helpers/BiMap.hpp"
|
|
|
|
|
#include "src/Logger/Logger.hpp"
|
|
|
|
|
|
|
|
|
|
#include "src/Exceptions/Exception.hpp"
|
|
|
|
|
#include "Exceptions/TargetDescriptionParsingFailureException.hpp"
|
|
|
|
|
|
2023-08-13 15:47:51 +01:00
|
|
|
namespace Targets::TargetDescription
|
2022-02-05 15:32:08 +00:00
|
|
|
{
|
2023-08-13 15:47:51 +01:00
|
|
|
using namespace Exceptions;
|
2024-02-13 20:48:06 +00:00
|
|
|
using namespace ::Exceptions;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-12 19:23:00 +00:00
|
|
|
using Services::StringService;
|
|
|
|
|
|
2023-12-17 18:12:53 +00:00
|
|
|
TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) {
|
2023-12-12 23:19:21 +00:00
|
|
|
this->init(xmlFilePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TargetDescriptionFile::TargetDescriptionFile(const QDomDocument& xml) {
|
|
|
|
|
this->init(xml);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 23:30:42 +00:00
|
|
|
const std::string& TargetDescriptionFile::getTargetName() const {
|
2023-12-17 18:12:53 +00:00
|
|
|
return this->deviceAttribute("name");
|
2023-03-05 23:30:42 +00:00
|
|
|
}
|
|
|
|
|
|
2023-12-13 00:50:10 +00:00
|
|
|
TargetFamily TargetDescriptionFile::getFamily() const {
|
2024-02-26 19:29:53 +00:00
|
|
|
const auto& family = this->deviceAttribute("family");
|
2023-12-13 00:50:10 +00:00
|
|
|
|
2024-02-26 19:29:53 +00:00
|
|
|
if (family == "AVR8") {
|
|
|
|
|
return TargetFamily::AVR_8;
|
|
|
|
|
}
|
2023-12-13 00:50:10 +00:00
|
|
|
|
2024-02-26 19:29:53 +00:00
|
|
|
if (family == "RISCV") {
|
|
|
|
|
return TargetFamily::RISC_V;
|
2023-12-13 00:50:10 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 19:29:53 +00:00
|
|
|
throw InvalidTargetDescriptionDataException("Failed to resolve target family - invalid family name");
|
2021-06-08 00:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-12 19:23:00 +00:00
|
|
|
std::optional<std::reference_wrapper<const PropertyGroup>> TargetDescriptionFile::tryGetPropertyGroup(
|
|
|
|
|
std::string_view keyStr
|
|
|
|
|
) const {
|
2024-02-13 20:24:52 +00:00
|
|
|
const auto keys = StringService::split(keyStr, '.');
|
2024-02-12 19:23:00 +00:00
|
|
|
|
2024-02-13 20:24:52 +00:00
|
|
|
const auto firstSubgroupIt = this->propertyGroupsByKey.find(*keys.begin());
|
|
|
|
|
return firstSubgroupIt != this->propertyGroupsByKey.end()
|
2024-02-12 19:23:00 +00:00
|
|
|
? keys.size() > 1
|
2024-02-12 19:39:21 +00:00
|
|
|
? firstSubgroupIt->second.tryGetSubgroup(keys | std::ranges::views::drop(1))
|
|
|
|
|
: std::optional(std::cref(firstSubgroupIt->second))
|
2024-02-12 19:23:00 +00:00
|
|
|
: std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const PropertyGroup& TargetDescriptionFile::getPropertyGroup(std::string_view keyStr) const {
|
|
|
|
|
const auto propertyGroup = this->tryGetPropertyGroup(keyStr);
|
|
|
|
|
|
|
|
|
|
if (!propertyGroup.has_value()) {
|
2024-02-26 19:28:18 +00:00
|
|
|
throw InvalidTargetDescriptionDataException(
|
2024-02-12 19:23:00 +00:00
|
|
|
"Failed to get property group \"" + std::string(keyStr) + "\" from TDF - property group not found"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return propertyGroup->get();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 21:25:32 +00:00
|
|
|
const AddressSpace& TargetDescriptionFile::getAddressSpace(std::string_view key) const {
|
2024-02-13 20:22:18 +00:00
|
|
|
const auto addressSpace = this->tryGetAddressSpace(key);
|
|
|
|
|
|
|
|
|
|
if (!addressSpace.has_value()) {
|
2024-02-26 19:28:18 +00:00
|
|
|
throw InvalidTargetDescriptionDataException(
|
2024-02-13 20:22:18 +00:00
|
|
|
"Failed to get address space \"" + std::string(key) + "\" from TDF - address space not found"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addressSpace->get();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 21:24:41 +00:00
|
|
|
std::set<TargetPhysicalInterface> TargetDescriptionFile::getPhysicalInterfaces() const {
|
|
|
|
|
static const auto physicalInterfacesByName = BiMap<std::string, TargetPhysicalInterface>({
|
|
|
|
|
{"updi", TargetPhysicalInterface::UPDI},
|
|
|
|
|
{"debugwire", TargetPhysicalInterface::DEBUG_WIRE},
|
|
|
|
|
{"jtag", TargetPhysicalInterface::JTAG},
|
|
|
|
|
{"pdi", TargetPhysicalInterface::PDI},
|
|
|
|
|
{"isp", TargetPhysicalInterface::ISP},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
auto output = std::set<TargetPhysicalInterface>();
|
|
|
|
|
|
|
|
|
|
for (const auto& physicalInterface : this->physicalInterfaces) {
|
|
|
|
|
const auto interface = physicalInterfacesByName.valueAt(
|
|
|
|
|
StringService::asciiToLower(physicalInterface.name)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (interface.has_value()) {
|
|
|
|
|
output.insert(*interface);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 21:01:36 +00:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-17 18:12:53 +00:00
|
|
|
void TargetDescriptionFile::init(const std::string& xmlFilePath) {
|
|
|
|
|
auto file = QFile(QString::fromStdString(xmlFilePath));
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!file.exists()) {
|
|
|
|
|
// This can happen if someone has been messing with the Resources directory.
|
|
|
|
|
throw Exception("Failed to load target description file - file not found");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
file.open(QIODevice::ReadOnly);
|
2023-03-05 23:30:42 +00:00
|
|
|
auto document = QDomDocument();
|
|
|
|
|
if (!document.setContent(file.readAll())) {
|
2022-02-05 15:32:08 +00:00
|
|
|
throw Exception("Failed to parse target description file - please report this error "
|
2022-12-26 21:47:09 +00:00
|
|
|
"to Bloom developers via " + Services::PathService::homeDomainName() + "/report-issue");
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2023-03-05 23:30:42 +00:00
|
|
|
this->init(document);
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-05 23:30:42 +00:00
|
|
|
void TargetDescriptionFile::init(const QDomDocument& document) {
|
2024-02-12 19:23:00 +00:00
|
|
|
const auto deviceElement = document.documentElement();
|
|
|
|
|
if (deviceElement.nodeName() != "device") {
|
|
|
|
|
throw TargetDescriptionParsingFailureException("Root \"device\" element not found.");
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-12 19:23:00 +00:00
|
|
|
const auto deviceAttributes = deviceElement.attributes();
|
2023-12-17 18:12:53 +00:00
|
|
|
for (auto i = 0; i < deviceAttributes.length(); ++i) {
|
|
|
|
|
const auto deviceAttribute = deviceAttributes.item(i);
|
|
|
|
|
this->deviceAttributesByName.insert(
|
|
|
|
|
std::pair(
|
|
|
|
|
deviceAttribute.nodeName().toStdString(),
|
|
|
|
|
deviceAttribute.nodeValue().toStdString()
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-02-05 15:32:08 +00:00
|
|
|
|
2024-02-12 19:23:00 +00:00
|
|
|
for (
|
|
|
|
|
auto element = deviceElement.firstChildElement("property-groups").firstChildElement("property-group");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("property-group")
|
|
|
|
|
) {
|
|
|
|
|
auto propertyGroup = TargetDescriptionFile::propertyGroupFromXml(element);
|
2024-02-13 20:24:52 +00:00
|
|
|
this->propertyGroupsByKey.insert(
|
2024-02-12 19:23:00 +00:00
|
|
|
std::pair(propertyGroup.key, std::move(propertyGroup))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
for (
|
|
|
|
|
auto element = deviceElement.firstChildElement("address-spaces").firstChildElement("address-space");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("address-space")
|
|
|
|
|
) {
|
|
|
|
|
auto addressSpace = TargetDescriptionFile::addressSpaceFromXml(element);
|
|
|
|
|
this->addressSpacesByKey.insert(
|
|
|
|
|
std::pair(addressSpace.key, std::move(addressSpace))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 19:40:22 +00:00
|
|
|
for (
|
|
|
|
|
auto element = deviceElement.firstChildElement("physical-interfaces")
|
|
|
|
|
.firstChildElement("physical-interface");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("physical-interface")
|
|
|
|
|
) {
|
|
|
|
|
this->physicalInterfaces.emplace_back(
|
|
|
|
|
TargetDescriptionFile::physicalInterfaceFromXml(element)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
for (
|
|
|
|
|
auto element = deviceElement.firstChildElement("modules").firstChildElement("module");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("module")
|
|
|
|
|
) {
|
|
|
|
|
auto module = TargetDescriptionFile::moduleFromXml(element);
|
|
|
|
|
this->modulesByKey.insert(
|
|
|
|
|
std::pair(module.key, std::move(module))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 21:01:36 +00:00
|
|
|
for (
|
|
|
|
|
auto element = deviceElement.firstChildElement("peripherals").firstChildElement("peripheral");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("peripheral")
|
|
|
|
|
) {
|
|
|
|
|
auto peripheral = TargetDescriptionFile::peripheralFromXml(element);
|
|
|
|
|
this->peripheralsByKey.insert(
|
|
|
|
|
std::pair(peripheral.key, std::move(peripheral))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 23:30:42 +00:00
|
|
|
this->loadVariants(document);
|
|
|
|
|
this->loadPinouts(document);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-27 21:27:41 +00:00
|
|
|
const std::string& TargetDescriptionFile::deviceAttribute(const std::string& attributeName) const {
|
|
|
|
|
const auto attributeIt = this->deviceAttributesByName.find(attributeName);
|
|
|
|
|
|
|
|
|
|
if (attributeIt == this->deviceAttributesByName.end()) {
|
|
|
|
|
throw Exception("Missing target device attribute (\"" + attributeName + "\")");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return attributeIt->second;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 19:23:00 +00:00
|
|
|
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 Exception(
|
|
|
|
|
"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);
|
2024-02-13 20:24:52 +00:00
|
|
|
output.propertiesByKey.insert(std::pair(property.key, std::move(property)));
|
2024-02-12 19:23:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("property-group");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("property-group")
|
|
|
|
|
) {
|
2024-02-12 19:39:21 +00:00
|
|
|
auto subgroup = TargetDescriptionFile::propertyGroupFromXml(element);
|
2024-02-25 16:41:57 +00:00
|
|
|
output.subgroupsByKey.insert(std::pair(subgroup.key, std::move(subgroup)));
|
2024-02-12 19:23:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Property TargetDescriptionFile::propertyFromXml(const QDomElement& xmlElement) {
|
|
|
|
|
return Property(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "value")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 19:29:53 +00:00
|
|
|
AddressSpace TargetDescriptionFile::addressSpaceFromXml(const QDomElement& xmlElement) {
|
2024-02-13 20:22:18 +00:00
|
|
|
static const auto endiannessByName = BiMap<std::string, TargetMemoryEndianness>({
|
|
|
|
|
{"big", TargetMemoryEndianness::BIG},
|
|
|
|
|
{"little", TargetMemoryEndianness::LITTLE},
|
|
|
|
|
});
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
const auto endiannessName = TargetDescriptionFile::tryGetAttribute(xmlElement, "endianness");
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
auto endianness = std::optional<TargetMemoryEndianness>();
|
|
|
|
|
if (endiannessName.has_value()) {
|
|
|
|
|
endianness = endiannessByName.valueAt(*endiannessName);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
if (!endianness.has_value()) {
|
|
|
|
|
throw Exception(
|
|
|
|
|
"Failed to extract address space from TDF - invalid endianness name \"" + *endiannessName + "\""
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
auto output = AddressSpace(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")),
|
|
|
|
|
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")),
|
|
|
|
|
endianness,
|
|
|
|
|
{}
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("memory-segment");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("memory-segment")
|
2022-02-05 15:32:08 +00:00
|
|
|
) {
|
2024-02-13 20:22:18 +00:00
|
|
|
auto section = TargetDescriptionFile::memorySegmentFromXml(element);
|
|
|
|
|
output.memorySegmentsByKey.insert(std::pair(section.key, std::move(section)));
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
return output;
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
MemorySegment TargetDescriptionFile::memorySegmentFromXml(const QDomElement& xmlElement) {
|
|
|
|
|
static const auto typesByName = BiMap<std::string, MemorySegmentType>({
|
|
|
|
|
{"aliased", MemorySegmentType::ALIASED},
|
|
|
|
|
{"regs", MemorySegmentType::REGISTERS},
|
|
|
|
|
{"eeprom", MemorySegmentType::EEPROM},
|
|
|
|
|
{"flash", MemorySegmentType::FLASH},
|
|
|
|
|
{"fuses", MemorySegmentType::FUSES},
|
|
|
|
|
{"io", MemorySegmentType::IO},
|
|
|
|
|
{"ram", MemorySegmentType::RAM},
|
|
|
|
|
{"lockbits", MemorySegmentType::LOCKBITS},
|
|
|
|
|
{"osccal", MemorySegmentType::OSCCAL},
|
|
|
|
|
{"production_signatures", MemorySegmentType::PRODUCTION_SIGNATURES},
|
|
|
|
|
{"signatures", MemorySegmentType::SIGNATURES},
|
|
|
|
|
{"user_signatures", MemorySegmentType::USER_SIGNATURES},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const auto typeName = TargetDescriptionFile::getAttribute(xmlElement, "type");
|
|
|
|
|
|
|
|
|
|
const auto type = typesByName.valueAt(typeName);
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!type.has_value()) {
|
2024-02-13 20:22:18 +00:00
|
|
|
throw Exception(
|
|
|
|
|
"Failed to extract memory segment from TDF - invalid memory segment type name \"" + typeName + "\""
|
|
|
|
|
);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
const auto pageSize = TargetDescriptionFile::tryGetAttribute(xmlElement, "page-size");
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
auto output = MemorySegment(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "name"),
|
|
|
|
|
*type,
|
|
|
|
|
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")),
|
|
|
|
|
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")),
|
|
|
|
|
pageSize.has_value()
|
|
|
|
|
? std::optional(StringService::toUint16(*pageSize))
|
|
|
|
|
: std::nullopt,
|
|
|
|
|
{}
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("section");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("section")
|
|
|
|
|
) {
|
|
|
|
|
auto section = TargetDescriptionFile::memorySegmentSectionFromXml(element);
|
|
|
|
|
output.sectionsByKey.insert(std::pair(section.key, std::move(section)));
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
return output;
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
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")),
|
|
|
|
|
{}
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("section");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("section")
|
|
|
|
|
) {
|
|
|
|
|
auto section = TargetDescriptionFile::memorySegmentSectionFromXml(element);
|
|
|
|
|
output.subSectionsByKey.insert(std::pair(section.key, std::move(section)));
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-13 20:22:18 +00:00
|
|
|
return output;
|
2021-08-27 23:51:21 +01:00
|
|
|
}
|
2021-08-07 17:15:48 +01:00
|
|
|
|
2024-02-15 19:40:22 +00:00
|
|
|
PhysicalInterface TargetDescriptionFile::physicalInterfaceFromXml(const QDomElement& xmlElement) {
|
|
|
|
|
return PhysicalInterface(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "name"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "type")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
Module TargetDescriptionFile::moduleFromXml(const QDomElement& xmlElement) {
|
|
|
|
|
auto output = Module(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "name"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "description"),
|
|
|
|
|
{}
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("register-group");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("register-group")
|
|
|
|
|
) {
|
|
|
|
|
auto registerGroup = TargetDescriptionFile::registerGroupFromXml(element);
|
|
|
|
|
output.registerGroupsByKey.insert(std::pair(registerGroup.key, std::move(registerGroup)));
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
return output;
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
RegisterGroup TargetDescriptionFile::registerGroupFromXml(const QDomElement& xmlElement) {
|
|
|
|
|
const auto offset = TargetDescriptionFile::tryGetAttribute(xmlElement, "offset");
|
2021-08-07 17:15:48 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
auto output = RegisterGroup(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "name"),
|
|
|
|
|
offset.has_value() ? std::optional(StringService::toUint32(*offset)) : std::nullopt,
|
|
|
|
|
{},
|
|
|
|
|
{},
|
|
|
|
|
{}
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("register");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("register")
|
2022-02-05 15:32:08 +00:00
|
|
|
) {
|
2024-02-26 19:27:36 +00:00
|
|
|
auto reg = TargetDescriptionFile::registerFromXml(element);
|
|
|
|
|
output.registersByKey.insert(std::pair(reg.key, std::move(reg)));
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-08-30 22:32:40 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("register-group");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("register-group")
|
|
|
|
|
) {
|
|
|
|
|
auto registerGroup = TargetDescriptionFile::registerGroupFromXml(element);
|
|
|
|
|
output.subgroupsByKey.insert(std::pair(registerGroup.key, std::move(registerGroup)));
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("register-group-reference");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("register-group-reference")
|
|
|
|
|
) {
|
|
|
|
|
auto registerGroupReference = TargetDescriptionFile::registerGroupReferenceFromXml(element);
|
|
|
|
|
output.subgroupReferencesByKey.insert(
|
|
|
|
|
std::pair(registerGroupReference.key, std::move(registerGroupReference))
|
|
|
|
|
);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
return output;
|
|
|
|
|
}
|
2021-06-06 20:06:43 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
RegisterGroupReference TargetDescriptionFile::registerGroupReferenceFromXml(const QDomElement& xmlElement) {
|
|
|
|
|
return RegisterGroupReference(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "name"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "register-group-key"),
|
|
|
|
|
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")),
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "description")
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-06-06 20:06:43 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
Register TargetDescriptionFile::registerFromXml(const QDomElement& xmlElement) {
|
|
|
|
|
const auto initialValue = TargetDescriptionFile::tryGetAttribute(xmlElement, "initial-value");
|
|
|
|
|
const auto alternative = TargetDescriptionFile::tryGetAttribute(xmlElement, "alternative");
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
auto output = Register(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "name"),
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "description"),
|
|
|
|
|
StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")),
|
|
|
|
|
StringService::toUint16(TargetDescriptionFile::getAttribute(xmlElement, "size")),
|
|
|
|
|
initialValue.has_value() ? std::optional(StringService::toUint64(*initialValue)) : std::nullopt,
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "access"),
|
|
|
|
|
alternative.has_value() ? std::optional(*alternative == "true") : std::nullopt,
|
|
|
|
|
{}
|
|
|
|
|
);
|
2022-03-04 15:33:31 +00:00
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
for (
|
|
|
|
|
auto element = xmlElement.firstChildElement("bit-field");
|
|
|
|
|
!element.isNull();
|
|
|
|
|
element = element.nextSiblingElement("bit-field")
|
|
|
|
|
) {
|
|
|
|
|
auto bitField = TargetDescriptionFile::bitFieldFromXml(element);
|
|
|
|
|
output.bitFieldsByKey.insert(std::pair(bitField.key, std::move(bitField)));
|
2022-03-04 15:33:31 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 19:27:36 +00:00
|
|
|
return output;
|
2021-06-06 20:06:43 +01:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2023-12-10 13:04:05 +00:00
|
|
|
BitField TargetDescriptionFile::bitFieldFromXml(const QDomElement& xmlElement) {
|
2024-02-26 19:27:36 +00:00
|
|
|
return BitField(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "name"),
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "description"),
|
|
|
|
|
StringService::toUint64(TargetDescriptionFile::getAttribute(xmlElement, "mask")),
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "access")
|
2022-03-04 15:33:31 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 21:01:36 +00:00
|
|
|
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.insert(
|
|
|
|
|
std::pair(registerGroupInstance.key, 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 RegisterGroupInstance(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "key"),
|
|
|
|
|
TargetDescriptionFile::getAttribute(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 index = TargetDescriptionFile::tryGetAttribute(xmlElement, "index");
|
|
|
|
|
|
|
|
|
|
return Signal(
|
|
|
|
|
TargetDescriptionFile::getAttribute(xmlElement, "pad-id"),
|
|
|
|
|
index.has_value() ? std::optional(StringService::toUint64(*index)) : std::nullopt,
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "function"),
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "group"),
|
|
|
|
|
TargetDescriptionFile::tryGetAttribute(xmlElement, "field")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 23:30:42 +00:00
|
|
|
void TargetDescriptionFile::loadVariants(const QDomDocument& document) {
|
|
|
|
|
const auto deviceElement = document.elementsByTagName("device").item(0).toElement();
|
|
|
|
|
|
|
|
|
|
auto variantNodes = document.elementsByTagName("variants").item(0).toElement()
|
2022-02-05 15:32:08 +00:00
|
|
|
.elementsByTagName("variant");
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
for (int variantIndex = 0; variantIndex < variantNodes.count(); variantIndex++) {
|
|
|
|
|
try {
|
|
|
|
|
auto variantXml = variantNodes.item(variantIndex).toElement();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2023-12-13 23:03:04 +00:00
|
|
|
if (!variantXml.hasAttribute("name")) {
|
|
|
|
|
throw Exception("Missing name attribute");
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!variantXml.hasAttribute("package")) {
|
|
|
|
|
throw Exception("Missing package attribute");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!variantXml.hasAttribute("pinout")) {
|
|
|
|
|
throw Exception("Missing pinout attribute");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
auto variant = Variant();
|
2023-12-13 23:03:04 +00:00
|
|
|
variant.name = variantXml.attribute("name").toStdString();
|
2022-02-05 15:32:08 +00:00
|
|
|
variant.pinoutName = variantXml.attribute("pinout").toLower().toStdString();
|
|
|
|
|
variant.package = variantXml.attribute("package").toUpper().toStdString();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (variantXml.hasAttribute("disabled")) {
|
|
|
|
|
variant.disabled = (variantXml.attribute("disabled") == "1");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
this->variants.push_back(variant);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
} catch (const Exception& exception) {
|
|
|
|
|
Logger::debug(
|
|
|
|
|
"Failed to extract variant from target description element - " + exception.getMessage()
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 23:30:42 +00:00
|
|
|
void TargetDescriptionFile::loadPinouts(const QDomDocument& document) {
|
|
|
|
|
const auto deviceElement = document.elementsByTagName("device").item(0).toElement();
|
|
|
|
|
|
|
|
|
|
auto pinoutNodes = document.elementsByTagName("pinouts").item(0).toElement()
|
2022-02-05 15:32:08 +00:00
|
|
|
.elementsByTagName("pinout");
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
for (int pinoutIndex = 0; pinoutIndex < pinoutNodes.count(); pinoutIndex++) {
|
|
|
|
|
try {
|
|
|
|
|
auto pinoutXml = pinoutNodes.item(pinoutIndex).toElement();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!pinoutXml.hasAttribute("name")) {
|
|
|
|
|
throw Exception("Missing name attribute");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
auto pinout = Pinout();
|
|
|
|
|
pinout.name = pinoutXml.attribute("name").toLower().toStdString();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
auto pinNodes = pinoutXml.elementsByTagName("pin");
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
for (int pinIndex = 0; pinIndex < pinNodes.count(); pinIndex++) {
|
|
|
|
|
auto pinXml = pinNodes.item(pinIndex).toElement();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!pinXml.hasAttribute("position")) {
|
|
|
|
|
throw Exception(
|
|
|
|
|
"Missing position attribute on pin element " + std::to_string(pinIndex)
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!pinXml.hasAttribute("pad")) {
|
|
|
|
|
throw Exception("Missing pad attribute on pin element " + std::to_string(pinIndex));
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
auto pin = Pin();
|
|
|
|
|
bool positionConversionSucceeded = true;
|
|
|
|
|
pin.position = pinXml.attribute("position").toInt(&positionConversionSucceeded, 10);
|
|
|
|
|
pin.pad = pinXml.attribute("pad").toLower().toStdString();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!positionConversionSucceeded) {
|
|
|
|
|
throw Exception("Failed to convert position attribute value to integer on pin element "
|
|
|
|
|
+ std::to_string(pinIndex));
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
pinout.pins.push_back(pin);
|
|
|
|
|
}
|
2021-06-06 20:06:43 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
this->pinoutsMappedByName.insert(std::pair(pinout.name, pinout));
|
2021-06-06 20:06:43 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
} catch (const Exception& exception) {
|
|
|
|
|
Logger::debug(
|
|
|
|
|
"Failed to extract pinout from target description element - " + exception.getMessage()
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-06-26 03:47:23 +01:00
|
|
|
}
|