509 lines
20 KiB
C++
509 lines
20 KiB
C++
#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;
|
|
|
|
using Bloom::Targets::TargetDescription::RegisterGroup;
|
|
using Bloom::Targets::TargetDescription::MemorySegment;
|
|
using Bloom::Targets::TargetDescription::MemorySegmentType;
|
|
using Bloom::Targets::TargetDescription::Register;
|
|
|
|
TargetDescriptionFile::TargetDescriptionFile(
|
|
const TargetSignature& targetSignature,
|
|
std::optional<std::string> targetName
|
|
) {
|
|
auto targetSignatureHex = targetSignature.toHex();
|
|
auto mapping = TargetDescriptionFile::getTargetDescriptionMapping();
|
|
auto qTargetSignatureHex = QString::fromStdString(targetSignatureHex).toLower();
|
|
|
|
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->propertyGroupsMappedByName;
|
|
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.contains(familyName)) {
|
|
throw Exception("Unknown family name in target description file.");
|
|
}
|
|
|
|
return familyNameToEnums.at(familyName);
|
|
}
|
|
|
|
std::optional<MemorySegment> TargetDescriptionFile::getFlashMemorySegment() const {
|
|
auto& addressMapping = this->addressSpacesMappedById;
|
|
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;
|
|
|
|
/*
|
|
* In AVR8 TDFs, flash memory segments are typically named "APP_SECTION", "PROGMEM" or "FLASH".
|
|
*/
|
|
auto flashSegmentIt = flashMemorySegments.find("app_section") != flashMemorySegments.end() ?
|
|
flashMemorySegments.find("app_section")
|
|
: flashMemorySegments.find("progmem") != flashMemorySegments.end()
|
|
? flashMemorySegments.find("progmem") : flashMemorySegments.find("flash");
|
|
|
|
if (flashSegmentIt != flashMemorySegments.end()) {
|
|
return flashSegmentIt->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MemorySegment> TargetDescriptionFile::getRamMemorySegment() const {
|
|
auto& addressMapping = this->addressSpacesMappedById;
|
|
|
|
// Internal RAM ®ister 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> TargetDescriptionFile::getRegisterMemorySegment() const {
|
|
auto& addressMapping = this->addressSpacesMappedById;
|
|
|
|
// Internal RAM ®ister 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> TargetDescriptionFile::getEepromMemorySegment() const {
|
|
auto& addressMapping = this->addressSpacesMappedById;
|
|
|
|
if (addressMapping.contains("eeprom")) {
|
|
auto& eepromAddressSpace = addressMapping.at("eeprom");
|
|
auto& eepromAddressSpaceSegments = eepromAddressSpace.memorySegmentsByTypeAndName;
|
|
|
|
if (eepromAddressSpaceSegments.contains(MemorySegmentType::EEPROM)) {
|
|
return eepromAddressSpaceSegments.at(MemorySegmentType::EEPROM).begin()->second;
|
|
}
|
|
|
|
} else {
|
|
// The EEPROM memory segment may be part of the data address space
|
|
if (addressMapping.contains("data")) {
|
|
auto dataAddressSpace = addressMapping.at("data");
|
|
|
|
if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::EEPROM)) {
|
|
return dataAddressSpace.memorySegmentsByTypeAndName.at(MemorySegmentType::EEPROM).begin()->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MemorySegment> TargetDescriptionFile::getFirstBootSectionMemorySegment() const {
|
|
auto& addressMapping = this->addressSpacesMappedById;
|
|
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;
|
|
|
|
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> TargetDescriptionFile::getCpuRegisterGroup() const {
|
|
auto& modulesByName = this->modulesMappedByName;
|
|
|
|
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> TargetDescriptionFile::getBootLoadRegisterGroup() const {
|
|
auto& modulesByName = this->modulesMappedByName;
|
|
|
|
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> TargetDescriptionFile::getEepromRegisterGroup() const {
|
|
auto& modulesByName = this->modulesMappedByName;
|
|
|
|
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> TargetDescriptionFile::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> TargetDescriptionFile::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> TargetDescriptionFile::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> TargetDescriptionFile::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> TargetDescriptionFile::getOscillatorCalibrationRegister() const {
|
|
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
|
|
|
if (cpuRegisterGroup.has_value()) {
|
|
auto& cpuRegisters = cpuRegisterGroup->registersMappedByName;
|
|
|
|
if (cpuRegisters.contains("osccal")) {
|
|
return cpuRegisters.at("osccal");
|
|
|
|
} else if (cpuRegisters.contains("osccal0")) {
|
|
return cpuRegisters.at("osccal0");
|
|
|
|
} else if (cpuRegisters.contains("osccal1")) {
|
|
return cpuRegisters.at("osccal1");
|
|
|
|
} else if (cpuRegisters.contains("fosccal")) {
|
|
return cpuRegisters.at("fosccal");
|
|
|
|
} else if (cpuRegisters.contains("sosccala")) {
|
|
return cpuRegisters.at("sosccala");
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Register> TargetDescriptionFile::getSpmcsRegister() const {
|
|
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
|
|
|
if (cpuRegisterGroup.has_value() && cpuRegisterGroup->registersMappedByName.contains("spmcsr")) {
|
|
return cpuRegisterGroup->registersMappedByName.at("spmcsr");
|
|
|
|
} else {
|
|
auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup();
|
|
|
|
if (bootLoadRegisterGroup.has_value() && bootLoadRegisterGroup->registersMappedByName.contains("spmcsr")) {
|
|
return bootLoadRegisterGroup->registersMappedByName.at("spmcsr");
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Register> TargetDescriptionFile::getSpmcRegister() const {
|
|
auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup();
|
|
|
|
if (bootLoadRegisterGroup.has_value() && bootLoadRegisterGroup->registersMappedByName.contains("spmcr")) {
|
|
return bootLoadRegisterGroup->registersMappedByName.at("spmcr");
|
|
|
|
} else {
|
|
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
|
|
|
if (cpuRegisterGroup.has_value() && cpuRegisterGroup->registersMappedByName.contains("spmcr")) {
|
|
return cpuRegisterGroup->registersMappedByName.at("spmcr");
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Register> TargetDescriptionFile::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> TargetDescriptionFile::getEepromAddressLowRegister() const {
|
|
auto eepromRegisterGroup = this->getEepromRegisterGroup();
|
|
|
|
if (eepromRegisterGroup.has_value()) {
|
|
auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearl");
|
|
|
|
if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
|
|
return eepromAddressRegisterIt->second;
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Register> TargetDescriptionFile::getEepromAddressHighRegister() const {
|
|
auto eepromRegisterGroup = this->getEepromRegisterGroup();
|
|
|
|
if (eepromRegisterGroup.has_value()) {
|
|
auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearh");
|
|
|
|
if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
|
|
return eepromAddressRegisterIt->second;
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Register> TargetDescriptionFile::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> TargetDescriptionFile::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;
|
|
}
|