Files
BloomPatched/src/Targets/TargetDescription/TargetDescriptionFile.cpp

471 lines
18 KiB
C++
Raw Normal View History

#include <QJsonDocument>
#include <QJsonArray>
#include "TargetDescriptionFile.hpp"
#include "Exceptions/TargetDescriptionParsingFailureException.hpp"
2021-04-04 21:04:12 +01:00
#include "src/Logger/Logger.hpp"
using namespace Bloom::Targets::TargetDescription;
2021-04-04 21:04:12 +01:00
using namespace Bloom::Exceptions;
void TargetDescriptionFile::init(const QString& xmlFilePath) {
2021-04-04 21:04:12 +01:00
auto file = QFile(xmlFilePath);
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
}
file.open(QIODevice::ReadOnly);
auto xml = QDomDocument();
if (!xml.setContent(file.readAll())) {
throw Exception("Failed to parse target description file - please report this error "
"to Bloom developers via https://bloom.oscillate.io/report-issue");
}
2021-04-04 21:04:12 +01:00
this->init(xml);
}
void TargetDescriptionFile::init(const QDomDocument& xml) {
2021-04-04 21:04:12 +01:00
this->xml = xml;
auto device = xml.elementsByTagName("device").item(0).toElement();
2021-04-04 21:04:12 +01:00
if (!device.isElement()) {
throw TargetDescriptionParsingFailureException("Device element not found.");
2021-04-04 21:04:12 +01:00
}
this->deviceElement = device;
this->loadAddressSpaces();
this->loadPropertyGroups();
this->loadModules();
this->loadPeripheralModules();
this->loadVariants();
this->loadPinouts();
2021-06-26 03:47:23 +01:00
this->loadInterfaces();
2021-04-04 21:04:12 +01:00
}
std::string TargetDescriptionFile::getTargetName() const {
2021-04-04 21:04:12 +01:00
return this->deviceElement.attributes().namedItem("name").nodeValue().toStdString();
}
AddressSpace TargetDescriptionFile::generateAddressSpaceFromXml(const QDomElement& xmlElement) {
2021-04-04 21:04:12 +01:00
if (
!xmlElement.hasAttribute("id")
2021-06-11 23:59:17 +01:00
|| !xmlElement.hasAttribute("name")
|| !xmlElement.hasAttribute("size")
|| !xmlElement.hasAttribute("start")
2021-04-04 21:04:12 +01:00
) {
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;
2021-06-26 03:47:23 +01:00
addressSpace.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16);
2021-04-04 21:04:12 +01:00
if (!conversionStatus) {
throw Exception("Failed to convert start address hex value to integer.");
}
2021-06-26 03:47:23 +01:00
addressSpace.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16);
2021-04-04 21:04:12 +01:00
if (!conversionStatus) {
throw Exception("Failed to convert size hex value to integer.");
}
if (xmlElement.hasAttribute("endianness")) {
addressSpace.littleEndian = (xmlElement.attribute("endianness").toStdString() == "little");
}
// Create memory segment objects and add them to the mapping.
auto segmentNodes = xmlElement.elementsByTagName("memory-segment");
auto& memorySegments = addressSpace.memorySegmentsByTypeAndName;
for (int segmentIndex = 0; segmentIndex < segmentNodes.count(); segmentIndex++) {
try {
auto segment = TargetDescriptionFile::generateMemorySegmentFromXml(
segmentNodes.item(segmentIndex).toElement()
);
2021-04-04 21:04:12 +01:00
2021-06-11 23:59:17 +01:00
if (!memorySegments.contains(segment.type)) {
2021-04-04 21:04:12 +01:00
memorySegments.insert(
std::pair<
MemorySegmentType,
decltype(addressSpace.memorySegmentsByTypeAndName)::mapped_type
>(segment.type, {}));
}
memorySegments.find(segment.type)->second.insert(std::pair(segment.name, segment));
} catch (const Exception& exception) {
Logger::debug("Failed to extract memory segment from target description element - "
2021-04-04 21:04:12 +01:00
+ exception.getMessage());
}
}
return addressSpace;
}
MemorySegment TargetDescriptionFile::generateMemorySegmentFromXml(const QDomElement& xmlElement) {
2021-04-04 21:04:12 +01:00
if (
!xmlElement.hasAttribute("type")
2021-06-11 23:59:17 +01:00
|| !xmlElement.hasAttribute("name")
|| !xmlElement.hasAttribute("size")
|| !xmlElement.hasAttribute("start")
2021-04-04 21:04:12 +01:00
) {
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 TargetDescriptionFile::generateRegisterGroupFromXml(const QDomElement& xmlElement) {
2021-04-04 21:04:12 +01:00
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 = TargetDescriptionFile::generateRegisterFromXml(
registerNodes.item(registerIndex).toElement()
);
2021-04-04 21:04:12 +01:00
registers.insert(std::pair(reg.name, reg));
} catch (const Exception& exception) {
Logger::debug("Failed to extract register from register group target description element - "
+ exception.getMessage());
2021-04-04 21:04:12 +01:00
}
}
return registerGroup;
}
Register TargetDescriptionFile::generateRegisterFromXml(const QDomElement& xmlElement) {
2021-04-04 21:04:12 +01:00
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;
}
void TargetDescriptionFile::loadAddressSpaces() {
auto addressSpaceNodes = this->deviceElement.elementsByTagName("address-spaces").item(0).toElement()
.elementsByTagName("address-space");
for (int addressSpaceIndex = 0; addressSpaceIndex < addressSpaceNodes.count(); addressSpaceIndex++) {
try {
auto addressSpace = TargetDescriptionFile::generateAddressSpaceFromXml(
addressSpaceNodes.item(addressSpaceIndex).toElement()
);
this->addressSpacesMappedById.insert(std::pair(addressSpace.id, addressSpace));
} catch (const Exception& exception) {
Logger::debug("Failed to extract address space from target description element - " + exception.getMessage());
2021-04-04 21:04:12 +01:00
}
}
}
2021-04-04 21:04:12 +01:00
void TargetDescriptionFile::loadPropertyGroups() {
if (!this->deviceElement.isElement()) {
throw TargetDescriptionParsingFailureException("Device element not found.");
}
2021-04-04 21:04:12 +01:00
auto propertyGroupNodes = this->deviceElement.elementsByTagName("property-groups").item(0).toElement()
.elementsByTagName("property-group");
2021-04-04 21:04:12 +01:00
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;
2021-04-04 21:04:12 +01:00
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();
2021-04-04 21:04:12 +01:00
Property property;
property.name = propertyName.toStdString();
property.value = propertyElement.attributes().namedItem("value").nodeValue();
2021-04-04 21:04:12 +01:00
propertyGroup.propertiesMappedByName.insert(std::pair(propertyName.toLower().toStdString(), property));
2021-04-04 21:04:12 +01:00
}
this->propertyGroupsMappedByName.insert(std::pair(propertyGroup.name, propertyGroup));
2021-04-04 21:04:12 +01:00
}
}
void TargetDescriptionFile::loadModules() {
auto moduleNodes = this->xml.elementsByTagName("modules").item(0).toElement()
.elementsByTagName("module");
2021-04-04 21:04:12 +01:00
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;
2021-04-04 21:04:12 +01:00
auto registerGroupNodes = moduleElement.elementsByTagName("register-group");
for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) {
auto registerGroup = TargetDescriptionFile::generateRegisterGroupFromXml(
registerGroupNodes.item(registerGroupIndex).toElement()
);
2021-04-04 21:04:12 +01:00
module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup));
2021-04-04 21:04:12 +01:00
}
this->modulesMappedByName.insert(std::pair(module.name, module));
2021-04-04 21:04:12 +01:00
}
}
void TargetDescriptionFile::loadPeripheralModules() {
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 = TargetDescriptionFile::generateRegisterGroupFromXml(
registerGroupNodes.item(registerGroupIndex).toElement()
);
module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup));
}
2021-04-04 21:04:12 +01:00
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();
2021-04-04 21:04:12 +01:00
auto registerGroupNodes = instanceXml.elementsByTagName("register-group");
2021-04-04 21:04:12 +01:00
for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) {
auto registerGroup = TargetDescriptionFile::generateRegisterGroupFromXml(
registerGroupNodes.item(registerGroupIndex).toElement()
);
2021-04-04 21:04:12 +01:00
instance.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup));
2021-04-04 21:04:12 +01:00
}
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();
2021-04-04 21:04:12 +01:00
if (!signalXml.hasAttribute("pad")) {
continue;
2021-04-04 21:04:12 +01:00
}
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);
2021-04-04 21:04:12 +01:00
if (!indexAttribute.isEmpty() && indexValid) {
signal.index = indexValue;
2021-04-04 21:04:12 +01:00
}
instance.instanceSignals.emplace_back(signal);
2021-04-04 21:04:12 +01:00
}
module.instancesMappedByName.insert(std::pair(instance.name, instance));
2021-04-04 21:04:12 +01:00
}
this->peripheralModulesMappedByName.insert(std::pair(module.name, module));
2021-04-04 21:04:12 +01:00
}
}
void TargetDescriptionFile::loadVariants() {
2021-04-04 21:04:12 +01:00
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.name = variantXml.attribute("ordercode").toStdString();
2021-04-04 21:04:12 +01:00
variant.pinoutName = variantXml.attribute("pinout").toLower().toStdString();
variant.package = variantXml.attribute("package").toUpper().toStdString();
if (variantXml.hasAttribute("disabled")) {
variant.disabled = (variantXml.attribute("disabled") == "1");
}
this->variants.push_back(variant);
2021-04-04 21:04:12 +01: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
}
}
}
void TargetDescriptionFile::loadPinouts() {
auto pinoutNodes = this->xml.elementsByTagName("pinouts").item(0).toElement()
.elementsByTagName("pinout");
2021-04-04 21:04:12 +01:00
for (int pinoutIndex = 0; pinoutIndex < pinoutNodes.count(); pinoutIndex++) {
try {
auto pinoutXml = pinoutNodes.item(pinoutIndex).toElement();
2021-04-04 21:04:12 +01:00
if (!pinoutXml.hasAttribute("name")) {
throw Exception("Missing name attribute");
}
2021-04-04 21:04:12 +01:00
auto pinout = Pinout();
pinout.name = pinoutXml.attribute("name").toLower().toStdString();
2021-04-04 21:04:12 +01:00
auto pinNodes = pinoutXml.elementsByTagName("pin");
2021-04-04 21:04:12 +01:00
for (int pinIndex = 0; pinIndex < pinNodes.count(); pinIndex++) {
auto pinXml = pinNodes.item(pinIndex).toElement();
2021-04-04 21:04:12 +01: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
if (!pinXml.hasAttribute("pad")) {
throw Exception("Missing pad attribute on pin element " + std::to_string(pinIndex));
}
2021-04-04 21:04:12 +01: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
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
}
pinout.pins.push_back(pin);
2021-04-04 21:04:12 +01:00
}
this->pinoutsMappedByName.insert(std::pair(pinout.name, pinout));
} 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
void TargetDescriptionFile::loadInterfaces() {
auto interfaceNodes = this->deviceElement.elementsByTagName("interfaces").item(0).toElement()
.elementsByTagName("interface");
for (int interfaceIndex = 0; interfaceIndex < interfaceNodes.count(); interfaceIndex++) {
try {
auto interfaceXml = interfaceNodes.item(interfaceIndex).toElement();
if (!interfaceXml.hasAttribute("name")) {
throw Exception("Missing name attribute");
}
auto interface = Interface();
interface.name = interfaceXml.attribute("name").toLower().toStdString();
if (interfaceXml.hasAttribute("type")) {
interface.type = interfaceXml.attribute("type").toStdString();
}
this->interfacesByName.insert(std::pair(interface.name, interface));
} catch (const Exception& exception) {
Logger::debug("Failed to extract interface from target description element - " + exception.getMessage());
}
}
}