Initial commit
This commit is contained in:
851
src/Targets/Microchip/AVR/AVR8/Avr8.cpp
Normal file
851
src/Targets/Microchip/AVR/AVR8/Avr8.cpp
Normal file
@@ -0,0 +1,851 @@
|
||||
#include <cstdint>
|
||||
#include <QtCore>
|
||||
#include <QJsonDocument>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <bitset>
|
||||
#include <limits>
|
||||
|
||||
#include "Avr8.hpp"
|
||||
#include "PadDescriptor.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/InvalidConfig.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/PartDescription/PartDescriptionFile.hpp"
|
||||
|
||||
// Derived AVR8 targets
|
||||
#include "XMega/XMega.hpp"
|
||||
#include "Mega/Mega.hpp"
|
||||
#include "Tiny/Tiny.hpp"
|
||||
|
||||
using namespace Bloom;
|
||||
using namespace Targets;
|
||||
using namespace Targets::Microchip::Avr;
|
||||
using namespace Avr8Bit;
|
||||
using namespace Exceptions;
|
||||
using Avr8Bit::Avr8;
|
||||
|
||||
/**
|
||||
* Initialises the target from config parameters extracted from user's config file.
|
||||
*
|
||||
* @see Application::extractConfig(); for more on config extraction.
|
||||
*
|
||||
* @param targetConfig
|
||||
*/
|
||||
void Avr8::preActivationConfigure(const TargetConfig& targetConfig) {
|
||||
Target::preActivationConfigure(targetConfig);
|
||||
|
||||
this->avr8Interface->configure(targetConfig);
|
||||
}
|
||||
|
||||
void Avr8::postActivationConfigure() {
|
||||
auto targetSignature = this->getId();
|
||||
auto partDescription = PartDescriptionFile(
|
||||
targetSignature.toHex(),
|
||||
(!this->name.empty()) ? std::optional(this->name) : std::nullopt
|
||||
);
|
||||
auto pdSignature = partDescription.getTargetSignature();
|
||||
|
||||
if (targetSignature != pdSignature) {
|
||||
// This should never happen. If it does, someone has screwed up the part description mapping file.
|
||||
throw Exception("Failed to activate target - target signature mismatch.\nThe target signature (\""
|
||||
+ targetSignature.toHex() + "\") does not match the AVR8 part description signature (\""
|
||||
+ pdSignature.toHex() + "\"). Please review your target configuration in bloom.json");
|
||||
}
|
||||
|
||||
this->partDescription = partDescription;
|
||||
this->id = partDescription.getTargetSignature();
|
||||
this->name = partDescription.getTargetName();
|
||||
this->family = partDescription.getFamily();
|
||||
}
|
||||
|
||||
void Avr8::postPromotionConfigure() {
|
||||
this->avr8Interface->setTargetParameters(this->getTargetParameters());
|
||||
this->loadPadDescriptors();
|
||||
this->loadTargetVariants();
|
||||
}
|
||||
|
||||
void Avr8::loadPadDescriptors() {
|
||||
auto& targetParameters = this->getTargetParameters();
|
||||
|
||||
/*
|
||||
* Every port address we extract from the part description will be stored in portAddresses, so that
|
||||
* we can extract the start (min) and end (max) for the target's IO port address
|
||||
* range (TargetParameters::ioPortAddressRangeStart & TargetParameters::ioPortAddressRangeEnd)
|
||||
*/
|
||||
std::vector<std::uint32_t> portAddresses;
|
||||
|
||||
auto& modules = this->partDescription->getModulesMappedByName();
|
||||
auto portModule = (modules.contains("port")) ? std::optional(modules.find("port")->second) : std::nullopt;
|
||||
auto& peripheralModules = this->partDescription->getPeripheralModulesMappedByName();
|
||||
|
||||
if (peripheralModules.contains("port")) {
|
||||
auto portPeripheralModule = peripheralModules.find("port")->second;
|
||||
|
||||
for (const auto& [instanceName, instance] : portPeripheralModule.instancesMappedByName) {
|
||||
if (instanceName.find("port") == 0) {
|
||||
auto portPeripheralRegisterGroup = (portPeripheralModule.registerGroupsMappedByName.contains(instanceName)) ?
|
||||
std::optional(portPeripheralModule.registerGroupsMappedByName.find(instanceName)->second) :
|
||||
std::nullopt;
|
||||
|
||||
for (const auto& signal : instance.instanceSignals) {
|
||||
if (!signal.index.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto padDescriptor = PadDescriptor();
|
||||
padDescriptor.name = signal.padName;
|
||||
padDescriptor.gpioPinNumber = signal.index.value();
|
||||
|
||||
if (portModule.has_value() && portModule->registerGroupsMappedByName.contains(instanceName)) {
|
||||
// We have register information for this port
|
||||
auto registerGroup = portModule->registerGroupsMappedByName.find(instanceName)->second;
|
||||
|
||||
for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) {
|
||||
if (registerName.find("port") == 0) {
|
||||
// This is the data register for the port
|
||||
padDescriptor.gpioPortSetAddress = portRegister.offset;
|
||||
padDescriptor.gpioPortClearAddress = portRegister.offset;
|
||||
|
||||
} else if (registerName.find("pin") == 0) {
|
||||
// This is the input data register for the port
|
||||
padDescriptor.gpioPortInputAddress = portRegister.offset;
|
||||
|
||||
} else if (registerName.find("ddr") == 0) {
|
||||
// This is the data direction register for the port
|
||||
padDescriptor.ddrSetAddress = portRegister.offset;
|
||||
padDescriptor.ddrClearAddress = portRegister.offset;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (portModule.has_value() && portModule->registerGroupsMappedByName.contains("port")) {
|
||||
// We have generic register information for all ports on the target
|
||||
auto registerGroup = portModule->registerGroupsMappedByName.find("port")->second;
|
||||
|
||||
for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) {
|
||||
if (registerName.find("outset") == 0) {
|
||||
// Include the port register offset
|
||||
padDescriptor.gpioPortSetAddress = (portPeripheralRegisterGroup.has_value() && portPeripheralRegisterGroup->offset.has_value()) ?
|
||||
portPeripheralRegisterGroup->offset.value_or(0) : 0;
|
||||
|
||||
padDescriptor.gpioPortSetAddress = padDescriptor.gpioPortSetAddress.value() + portRegister.offset;
|
||||
|
||||
} else if (registerName.find("outclr") == 0) {
|
||||
padDescriptor.gpioPortClearAddress = (portPeripheralRegisterGroup.has_value() && portPeripheralRegisterGroup->offset.has_value()) ?
|
||||
portPeripheralRegisterGroup->offset.value_or(0) : 0;
|
||||
|
||||
padDescriptor.gpioPortClearAddress = padDescriptor.gpioPortClearAddress.value() + portRegister.offset;
|
||||
|
||||
} else if (registerName.find("dirset") == 0) {
|
||||
padDescriptor.ddrSetAddress = (portPeripheralRegisterGroup.has_value() && portPeripheralRegisterGroup->offset.has_value()) ?
|
||||
portPeripheralRegisterGroup->offset.value_or(0) : 0;
|
||||
|
||||
padDescriptor.ddrSetAddress = padDescriptor.ddrSetAddress.value() + portRegister.offset;
|
||||
|
||||
} else if (registerName.find("dirclr") == 0) {
|
||||
padDescriptor.ddrClearAddress = (portPeripheralRegisterGroup.has_value() && portPeripheralRegisterGroup->offset.has_value()) ?
|
||||
portPeripheralRegisterGroup->offset.value_or(0) : 0;
|
||||
|
||||
padDescriptor.ddrClearAddress = padDescriptor.ddrClearAddress.value() + portRegister.offset;
|
||||
|
||||
} else if (registerName == "in") {
|
||||
padDescriptor.gpioPortInputAddress = (portPeripheralRegisterGroup.has_value() && portPeripheralRegisterGroup->offset.has_value()) ?
|
||||
portPeripheralRegisterGroup->offset.value_or(0) : 0;
|
||||
|
||||
padDescriptor.gpioPortInputAddress = padDescriptor.gpioPortInputAddress.value() + portRegister.offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (padDescriptor.gpioPortSetAddress.has_value()) {
|
||||
portAddresses.push_back(padDescriptor.gpioPortSetAddress.value());
|
||||
}
|
||||
|
||||
if (padDescriptor.gpioPortClearAddress.has_value()) {
|
||||
portAddresses.push_back(padDescriptor.gpioPortClearAddress.value());
|
||||
}
|
||||
|
||||
if (padDescriptor.ddrSetAddress.has_value()) {
|
||||
portAddresses.push_back(padDescriptor.ddrSetAddress.value());
|
||||
}
|
||||
|
||||
if (padDescriptor.ddrClearAddress.has_value()) {
|
||||
portAddresses.push_back(padDescriptor.ddrClearAddress.value());
|
||||
}
|
||||
|
||||
this->padDescriptorsByName.insert(std::pair(padDescriptor.name, padDescriptor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!portAddresses.empty()) {
|
||||
targetParameters.ioPortAddressRangeStart = *std::min_element(portAddresses.begin(), portAddresses.end());
|
||||
targetParameters.ioPortAddressRangeEnd = *std::max_element(portAddresses.begin(), portAddresses.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Avr8::loadTargetVariants() {
|
||||
auto variants = this->generateVariantsFromPartDescription();
|
||||
|
||||
for (auto& targetVariant : variants) {
|
||||
for (auto& [pinNumber, pinDescriptor] : targetVariant.pinDescriptorsByNumber) {
|
||||
if (this->padDescriptorsByName.contains(pinDescriptor.padName)) {
|
||||
auto& pad = this->padDescriptorsByName.at(pinDescriptor.padName);
|
||||
if (pad.gpioPortSetAddress.has_value() && pad.ddrSetAddress.has_value()) {
|
||||
pinDescriptor.type = TargetPinType::GPIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->targetVariantsById.insert(std::pair(targetVariant.id, targetVariant));
|
||||
}
|
||||
}
|
||||
|
||||
TargetParameters& Avr8::getTargetParameters() {
|
||||
if (!this->targetParameters.has_value()) {
|
||||
assert(this->partDescription.has_value());
|
||||
this->targetParameters = TargetParameters();
|
||||
this->targetParameters->family = this->family;
|
||||
auto& propertyGroups = this->partDescription->getPropertyGroupsMappedByName();
|
||||
|
||||
auto flashMemorySegment = this->partDescription->getFlashMemorySegment();
|
||||
if (flashMemorySegment.has_value()) {
|
||||
this->targetParameters->flashSize = flashMemorySegment->size;
|
||||
this->targetParameters->flashStartAddress = flashMemorySegment->startAddress;
|
||||
|
||||
if (flashMemorySegment->pageSize.has_value()) {
|
||||
this->targetParameters->flashPageSize = flashMemorySegment->pageSize.value();
|
||||
}
|
||||
}
|
||||
|
||||
auto ramMemorySegment = this->partDescription->getRamMemorySegment();
|
||||
if (ramMemorySegment.has_value()) {
|
||||
this->targetParameters->ramSize = ramMemorySegment->size;
|
||||
this->targetParameters->ramStartAddress = ramMemorySegment->startAddress;
|
||||
}
|
||||
|
||||
auto registerMemorySegment = this->partDescription->getRegisterMemorySegment();
|
||||
if (registerMemorySegment.has_value()) {
|
||||
this->targetParameters->gpRegisterSize = registerMemorySegment->size;
|
||||
this->targetParameters->gpRegisterStartAddress = registerMemorySegment->startAddress;
|
||||
}
|
||||
|
||||
auto eepromMemorySegment = this->partDescription->getEepromMemorySegment();
|
||||
if (eepromMemorySegment.has_value()) {
|
||||
this->targetParameters->eepromSize = eepromMemorySegment->size;
|
||||
|
||||
if (eepromMemorySegment->pageSize.has_value()) {
|
||||
this->targetParameters->eepromPageSize = eepromMemorySegment->pageSize.value();
|
||||
}
|
||||
}
|
||||
|
||||
auto firstBootSectionMemorySegment = this->partDescription->getFirstBootSectionMemorySegment();
|
||||
if (firstBootSectionMemorySegment.has_value()) {
|
||||
this->targetParameters->bootSectionStartAddress = firstBootSectionMemorySegment->startAddress / 2;
|
||||
this->targetParameters->bootSectionSize = firstBootSectionMemorySegment->size;
|
||||
}
|
||||
|
||||
// OCD attributes can be found in property groups
|
||||
if (propertyGroups.contains("ocd")) {
|
||||
auto& ocdProperties = propertyGroups.at("ocd").propertiesMappedByName;
|
||||
|
||||
if (ocdProperties.find("ocd_revision") != ocdProperties.end()) {
|
||||
this->targetParameters->ocdRevision = ocdProperties.find("ocd_revision")->second.value.toUShort(nullptr, 10);
|
||||
}
|
||||
|
||||
if (ocdProperties.find("ocd_datareg") != ocdProperties.end()) {
|
||||
this->targetParameters->ocdDataRegister = ocdProperties.find("ocd_datareg")->second.value.toUShort(nullptr, 16);
|
||||
}
|
||||
}
|
||||
|
||||
auto statusRegister = this->partDescription->getStatusRegister();
|
||||
if (statusRegister.has_value()) {
|
||||
this->targetParameters->statusRegisterStartAddress = statusRegister->offset;
|
||||
this->targetParameters->statusRegisterSize = statusRegister->size;
|
||||
}
|
||||
|
||||
auto stackPointerRegister = this->partDescription->getStackPointerRegister();
|
||||
if (stackPointerRegister.has_value()) {
|
||||
this->targetParameters->stackPointerRegisterStartAddress = stackPointerRegister->offset;
|
||||
this->targetParameters->stackPointerRegisterSize = stackPointerRegister->size;
|
||||
|
||||
} else {
|
||||
// Sometimes the SP register is split into two register nodes, one for low, the other for high
|
||||
auto stackPointerLowRegister = this->partDescription->getStackPointerLowRegister();
|
||||
auto stackPointerHighRegister = this->partDescription->getStackPointerHighRegister();
|
||||
|
||||
if (stackPointerLowRegister.has_value()) {
|
||||
this->targetParameters->stackPointerRegisterStartAddress = stackPointerLowRegister->offset;
|
||||
this->targetParameters->stackPointerRegisterSize = stackPointerLowRegister->size;
|
||||
}
|
||||
|
||||
if (stackPointerHighRegister.has_value()) {
|
||||
this->targetParameters->stackPointerRegisterSize = (this->targetParameters->stackPointerRegisterSize.has_value()) ?
|
||||
this->targetParameters->stackPointerRegisterSize.value() + stackPointerHighRegister->size :
|
||||
stackPointerHighRegister->size;
|
||||
}
|
||||
}
|
||||
|
||||
auto spmcsrRegister = this->partDescription->getSpmcsrRegister();
|
||||
if (spmcsrRegister.has_value()) {
|
||||
this->targetParameters->spmcsRegisterStartAddress = spmcsrRegister->offset;
|
||||
}
|
||||
|
||||
auto osccalRegister = this->partDescription->getOscillatorCalibrationRegister();
|
||||
if (osccalRegister.has_value()) {
|
||||
this->targetParameters->osccalAddress = osccalRegister->offset;
|
||||
}
|
||||
|
||||
auto eepromAddressRegister = this->partDescription->getEepromAddressRegister();
|
||||
if (eepromAddressRegister.has_value()) {
|
||||
this->targetParameters->eepromAddressRegisterLow = eepromAddressRegister->offset;
|
||||
this->targetParameters->eepromAddressRegisterHigh = (eepromAddressRegister->size == 2)
|
||||
? eepromAddressRegister->offset + 1 : eepromAddressRegister->offset;
|
||||
}
|
||||
|
||||
auto eepromDataRegister = this->partDescription->getEepromDataRegister();
|
||||
if (eepromDataRegister.has_value()) {
|
||||
this->targetParameters->eepromDataRegisterAddress = eepromDataRegister->offset;
|
||||
}
|
||||
|
||||
auto eepromControlRegister = this->partDescription->getEepromControlRegister();
|
||||
if (eepromControlRegister.has_value()) {
|
||||
this->targetParameters->eepromControlRegisterAddress = eepromControlRegister->offset;
|
||||
}
|
||||
|
||||
if (propertyGroups.contains("pdi_interface")) {
|
||||
auto& pdiInterfaceProperties = propertyGroups.at("pdi_interface").propertiesMappedByName;
|
||||
|
||||
if (pdiInterfaceProperties.contains("app_section_offset")) {
|
||||
this->targetParameters->appSectionPdiOffset = pdiInterfaceProperties
|
||||
.at("app_section_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
if (pdiInterfaceProperties.contains("boot_section_offset")) {
|
||||
this->targetParameters->bootSectionPdiOffset = pdiInterfaceProperties
|
||||
.at("boot_section_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
if (pdiInterfaceProperties.contains("datamem_offset")) {
|
||||
this->targetParameters->ramPdiOffset = pdiInterfaceProperties
|
||||
.at("datamem_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
if (pdiInterfaceProperties.contains("eeprom_offset")) {
|
||||
this->targetParameters->eepromPdiOffset = pdiInterfaceProperties
|
||||
.at("eeprom_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
if (pdiInterfaceProperties.contains("user_signatures_offset")) {
|
||||
this->targetParameters->userSignaturesPdiOffset = pdiInterfaceProperties
|
||||
.at("user_signatures_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
if (pdiInterfaceProperties.contains("prod_signatures_offset")) {
|
||||
this->targetParameters->productSignaturesPdiOffset = pdiInterfaceProperties
|
||||
.at("prod_signatures_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
if (pdiInterfaceProperties.contains("fuse_registers_offset")) {
|
||||
this->targetParameters->fuseRegistersPdiOffset = pdiInterfaceProperties
|
||||
.at("fuse_registers_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
if (pdiInterfaceProperties.contains("lock_registers_offset")) {
|
||||
this->targetParameters->lockRegistersPdiOffset = pdiInterfaceProperties
|
||||
.at("lock_registers_offset").value.toInt(nullptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this->targetParameters.value();
|
||||
}
|
||||
|
||||
std::unique_ptr<Targets::Target> Avr8::promote() {
|
||||
std::unique_ptr<Targets::Target> promoted = nullptr;
|
||||
|
||||
if (this->family.has_value()) {
|
||||
// Promote generic AVR8 target to correct family.
|
||||
switch (this->family.value()) {
|
||||
case Family::XMEGA: {
|
||||
Logger::info("AVR8 target promoted to XMega target");
|
||||
promoted = std::make_unique<XMega>(*this);
|
||||
break;
|
||||
}
|
||||
case Family::MEGA: {
|
||||
Logger::info("AVR8 target promoted to megaAVR target");
|
||||
promoted = std::make_unique<Mega>(*this);
|
||||
break;
|
||||
}
|
||||
case Family::TINY: {
|
||||
Logger::info("AVR8 target promoted to tinyAVR target");
|
||||
promoted = std::make_unique<Tiny>(*this);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return promoted;
|
||||
}
|
||||
|
||||
void Avr8::activate() {
|
||||
if (this->getActivated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->avr8Interface->init();
|
||||
this->avr8Interface->activate();
|
||||
this->activated = true;
|
||||
}
|
||||
|
||||
void Avr8::deactivate() {
|
||||
try {
|
||||
this->avr8Interface->deactivate();
|
||||
this->activated = false;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to deactivate AVR8 target - " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
TargetSignature Avr8::getId() {
|
||||
if (!this->id.has_value()) {
|
||||
this->id = this->avr8Interface->getDeviceId();
|
||||
}
|
||||
|
||||
return this->id.value();
|
||||
}
|
||||
|
||||
std::vector<TargetVariant> Avr8::generateVariantsFromPartDescription() {
|
||||
std::vector<TargetVariant> output;
|
||||
auto pdVariants = this->partDescription->getVariants();
|
||||
auto pdPinoutsByName = this->partDescription->getPinoutsMappedByName();
|
||||
auto& modules = this->partDescription->getModulesMappedByName();
|
||||
|
||||
for (const auto& pdVariant : pdVariants) {
|
||||
if (pdVariant.disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto targetVariant = TargetVariant();
|
||||
targetVariant.id = static_cast<int>(output.size());
|
||||
targetVariant.name = pdVariant.orderCode;
|
||||
targetVariant.packageName = pdVariant.package;
|
||||
|
||||
if (pdVariant.package.find("QFP") == 0 || pdVariant.package.find("TQFP") == 0) {
|
||||
targetVariant.package = TargetPackage::QFP;
|
||||
|
||||
} else if (pdVariant.package.find("PDIP") == 0 || pdVariant.package.find("DIP") == 0) {
|
||||
targetVariant.package = TargetPackage::DIP;
|
||||
|
||||
} else if (pdVariant.package.find("QFN") == 0 || pdVariant.package.find("VQFN") == 0) {
|
||||
targetVariant.package = TargetPackage::QFN;
|
||||
|
||||
} else if (pdVariant.package.find("SOIC") == 0) {
|
||||
targetVariant.package = TargetPackage::SOIC;
|
||||
}
|
||||
|
||||
if (!pdPinoutsByName.contains(pdVariant.pinoutName)) {
|
||||
// Missing pinouts in the part description file
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pdPinout = pdPinoutsByName.find(pdVariant.pinoutName)->second;
|
||||
for (const auto& pdPin : pdPinout.pins) {
|
||||
auto targetPin = TargetPinDescriptor();
|
||||
targetPin.name = pdPin.pad;
|
||||
targetPin.padName = pdPin.pad;
|
||||
targetPin.number = pdPin.position;
|
||||
|
||||
// TODO: REMOVE THIS:
|
||||
if (pdPin.pad.find("vcc") == 0 || pdPin.pad.find("avcc") == 0 || pdPin.pad.find("aref") == 0) {
|
||||
targetPin.type = TargetPinType::VCC;
|
||||
|
||||
} else if (pdPin.pad.find("gnd") == 0) {
|
||||
targetPin.type = TargetPinType::GND;
|
||||
}
|
||||
|
||||
targetVariant.pinDescriptorsByNumber.insert(std::pair(targetPin.number, targetPin));
|
||||
}
|
||||
|
||||
output.push_back(targetVariant);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
TargetDescriptor Avr8Bit::Avr8::getDescriptor() {
|
||||
auto parameters = this->getTargetParameters();
|
||||
|
||||
auto descriptor = TargetDescriptor();
|
||||
descriptor.id = this->getHumanReadableId();
|
||||
descriptor.name = this->getName();
|
||||
descriptor.ramSize = parameters.ramSize.value_or(0);
|
||||
|
||||
std::transform(
|
||||
this->targetVariantsById.begin(),
|
||||
this->targetVariantsById.end(),
|
||||
std::back_inserter(descriptor.variants),
|
||||
[](auto& variantToIdPair) {
|
||||
return variantToIdPair.second;
|
||||
}
|
||||
);
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
void Avr8::run() {
|
||||
this->avr8Interface->run();
|
||||
}
|
||||
|
||||
void Avr8::stop() {
|
||||
this->avr8Interface->stop();
|
||||
}
|
||||
|
||||
void Avr8::step() {
|
||||
this->avr8Interface->step();
|
||||
}
|
||||
|
||||
void Avr8::reset() {
|
||||
this->avr8Interface->reset();
|
||||
}
|
||||
|
||||
void Avr8::setBreakpoint(std::uint32_t address) {
|
||||
this->avr8Interface->setBreakpoint(address);
|
||||
}
|
||||
|
||||
void Avr8::removeBreakpoint(std::uint32_t address) {
|
||||
this->avr8Interface->clearBreakpoint(address);
|
||||
}
|
||||
|
||||
void Avr8::clearAllBreakpoints() {
|
||||
this->avr8Interface->clearAllBreakpoints();
|
||||
}
|
||||
|
||||
TargetRegisters Avr8::readGeneralPurposeRegisters(std::set<std::size_t> registerIds) {
|
||||
return this->avr8Interface->readGeneralPurposeRegisters(registerIds);
|
||||
}
|
||||
|
||||
void Avr8::writeRegisters(const TargetRegisters& registers) {
|
||||
TargetRegisters gpRegisters;
|
||||
|
||||
for (const auto& targetRegister : registers) {
|
||||
if (targetRegister.descriptor.type == TargetRegisterType::GENERAL_PURPOSE_REGISTER
|
||||
&& targetRegister.descriptor.id.has_value()) {
|
||||
gpRegisters.push_back(targetRegister);
|
||||
|
||||
} else if (targetRegister.descriptor.type == TargetRegisterType::PROGRAM_COUNTER) {
|
||||
Logger::debug("Setting PC register");
|
||||
|
||||
auto programCounterBytes = targetRegister.value;
|
||||
|
||||
if (programCounterBytes.size() < 4) {
|
||||
// All PC register values should be at least 4 bytes in size
|
||||
programCounterBytes.insert(programCounterBytes.begin(), 4 - programCounterBytes.size(), 0x00);
|
||||
}
|
||||
|
||||
this->setProgramCounter(static_cast<std::uint32_t>(
|
||||
programCounterBytes[0] << 24
|
||||
| programCounterBytes[1] << 16
|
||||
| programCounterBytes[2] << 8
|
||||
| programCounterBytes[3]
|
||||
));
|
||||
|
||||
} else if (targetRegister.descriptor.type == TargetRegisterType::STATUS_REGISTER) {
|
||||
Logger::error("Setting status register");
|
||||
this->avr8Interface->setStatusRegister(targetRegister);
|
||||
|
||||
} else if (targetRegister.descriptor.type == TargetRegisterType::STACK_POINTER) {
|
||||
Logger::error("Setting stack pointer register");
|
||||
this->avr8Interface->setStackPointerRegister(targetRegister);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpRegisters.empty()) {
|
||||
this->avr8Interface->writeGeneralPurposeRegisters(gpRegisters);
|
||||
}
|
||||
}
|
||||
|
||||
TargetRegisters Avr8::readRegisters(const TargetRegisterDescriptors& descriptors) {
|
||||
TargetRegisters registers;
|
||||
std::set<std::size_t> gpRegisterIds;
|
||||
|
||||
for (const auto& descriptor : descriptors) {
|
||||
if (descriptor.type == TargetRegisterType::GENERAL_PURPOSE_REGISTER && descriptor.id.has_value()) {
|
||||
gpRegisterIds.insert(descriptor.id.value());
|
||||
|
||||
} else if (descriptor.type == TargetRegisterType::PROGRAM_COUNTER) {
|
||||
registers.push_back(this->getProgramCounterRegister());
|
||||
|
||||
} else if (descriptor.type == TargetRegisterType::STATUS_REGISTER) {
|
||||
registers.push_back(this->getStatusRegister());
|
||||
|
||||
} else if (descriptor.type == TargetRegisterType::STACK_POINTER) {
|
||||
registers.push_back(this->getStackPointerRegister());
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpRegisterIds.empty()) {
|
||||
auto gpRegisters = this->readGeneralPurposeRegisters(gpRegisterIds);
|
||||
registers.insert(registers.end(), gpRegisters.begin(), gpRegisters.end());
|
||||
}
|
||||
|
||||
return registers;
|
||||
}
|
||||
|
||||
TargetMemoryBuffer Avr8::readMemory(TargetMemoryType memoryType, std::uint32_t startAddress, std::uint32_t bytes) {
|
||||
return this->avr8Interface->readMemory(memoryType, startAddress, bytes);
|
||||
}
|
||||
|
||||
void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) {
|
||||
this->avr8Interface->writeMemory(memoryType, startAddress, buffer);
|
||||
}
|
||||
|
||||
TargetState Avr8::getState() {
|
||||
return this->avr8Interface->getTargetState();
|
||||
}
|
||||
|
||||
std::uint32_t Avr8::getProgramCounter() {
|
||||
return this->avr8Interface->getProgramCounter();
|
||||
}
|
||||
|
||||
TargetRegister Avr8::getProgramCounterRegister() {
|
||||
auto programCounter = this->getProgramCounter();
|
||||
|
||||
return TargetRegister(TargetRegisterType::PROGRAM_COUNTER, {
|
||||
static_cast<unsigned char>(programCounter),
|
||||
static_cast<unsigned char>(programCounter >> 8),
|
||||
static_cast<unsigned char>(programCounter >> 16),
|
||||
static_cast<unsigned char>(programCounter >> 24),
|
||||
});
|
||||
}
|
||||
|
||||
TargetRegister Avr8::getStackPointerRegister() {
|
||||
return this->avr8Interface->getStackPointerRegister();
|
||||
}
|
||||
|
||||
TargetRegister Avr8::getStatusRegister() {
|
||||
return this->avr8Interface->getStatusRegister();
|
||||
}
|
||||
|
||||
void Avr8::setProgramCounter(std::uint32_t programCounter) {
|
||||
this->avr8Interface->setProgramCounter(programCounter);
|
||||
}
|
||||
|
||||
std::map<int, TargetPinState> Avr8::getPinStates(int variantId) {
|
||||
if (!this->targetVariantsById.contains(variantId)) {
|
||||
throw Exception("Invalid target variant ID");
|
||||
}
|
||||
|
||||
std::map<int, TargetPinState> output;
|
||||
auto& variant = this->targetVariantsById.at(variantId);
|
||||
|
||||
/*
|
||||
* To prevent the number of memory reads we perform here, we cache the data and map it by start address.
|
||||
*
|
||||
* This way, we only perform 3 memory reads for a target variant with 3 ports - one per port (instead of one
|
||||
* per pin).
|
||||
*
|
||||
* We may be able to make this more efficient by combining reads for ports with aligned memory addresses. This will
|
||||
* be considered when the need for it becomes apparent.
|
||||
*/
|
||||
std::map<std::uint16_t, TargetMemoryBuffer> cachedMemoryByStartAddress;
|
||||
auto readMemoryBitset = [this, &cachedMemoryByStartAddress](std::uint16_t startAddress) {
|
||||
if (!cachedMemoryByStartAddress.contains(startAddress)) {
|
||||
cachedMemoryByStartAddress.insert(
|
||||
std::pair(
|
||||
startAddress,
|
||||
this->readMemory(TargetMemoryType::RAM, startAddress, 1)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return std::bitset<std::numeric_limits<unsigned char>::digits>(
|
||||
cachedMemoryByStartAddress.at(startAddress).at(0)
|
||||
);
|
||||
};
|
||||
|
||||
for (const auto& [pinNumber, pinDescriptor] : variant.pinDescriptorsByNumber) {
|
||||
if (this->padDescriptorsByName.contains(pinDescriptor.padName)) {
|
||||
auto& pad = this->padDescriptorsByName.at(pinDescriptor.padName);
|
||||
|
||||
if (!pad.gpioPinNumber.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pinState = TargetPinState();
|
||||
|
||||
if (pad.ddrSetAddress.has_value()) {
|
||||
auto dataDirectionRegisterValue = readMemoryBitset(pad.ddrSetAddress.value());
|
||||
pinState.ioDirection = dataDirectionRegisterValue.test(pad.gpioPinNumber.value()) ?
|
||||
TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT;
|
||||
|
||||
if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT
|
||||
&& pad.gpioPortSetAddress.has_value()
|
||||
) {
|
||||
auto portRegisterValue = readMemoryBitset(pad.gpioPortSetAddress.value());
|
||||
pinState.ioState = portRegisterValue.test(pad.gpioPinNumber.value()) ?
|
||||
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
|
||||
|
||||
} else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT
|
||||
&& pad.gpioPortInputAddress.has_value()
|
||||
) {
|
||||
auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value());
|
||||
auto h = portInputRegisterValue.to_string();
|
||||
pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ?
|
||||
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
|
||||
}
|
||||
}
|
||||
|
||||
output.insert(std::pair(pinNumber, pinState));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Avr8::setPinState(int variantId, const TargetPinDescriptor& pinDescriptor, const TargetPinState& state) {
|
||||
if (!this->targetVariantsById.contains(variantId)) {
|
||||
throw Exception("Invalid target variant ID");
|
||||
}
|
||||
|
||||
if (!this->padDescriptorsByName.contains(pinDescriptor.padName)) {
|
||||
throw Exception("Unknown pad");
|
||||
}
|
||||
|
||||
if (!state.ioDirection.has_value()) {
|
||||
throw Exception("Missing IO direction state");
|
||||
}
|
||||
|
||||
auto& variant = this->targetVariantsById.at(variantId);
|
||||
auto& padDescriptor = this->padDescriptorsByName.at(pinDescriptor.padName);
|
||||
auto ioState = state.ioState;
|
||||
|
||||
if (state.ioDirection == TargetPinState::IoDirection::INPUT) {
|
||||
// When setting the direction to INPUT, we must always set the IO pinstate to LOW
|
||||
ioState = TargetPinState::IoState::LOW;
|
||||
}
|
||||
|
||||
if (!padDescriptor.ddrSetAddress.has_value()
|
||||
|| !padDescriptor.gpioPortSetAddress.has_value()
|
||||
|| !padDescriptor.gpioPinNumber.has_value()
|
||||
) {
|
||||
throw Exception("Inadequate pad descriptor");
|
||||
}
|
||||
|
||||
auto pinNumber = padDescriptor.gpioPinNumber.value();
|
||||
auto ddrSetAddress = padDescriptor.ddrSetAddress.value();
|
||||
auto ddrSetValue = this->readMemory(TargetMemoryType::RAM, ddrSetAddress, 1);
|
||||
|
||||
if (ddrSetValue.empty()) {
|
||||
throw Exception("Failed to read DDSR value");
|
||||
}
|
||||
|
||||
auto ddrSetBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrSetValue.front());
|
||||
if (ddrSetBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) {
|
||||
// DDR needs updating
|
||||
ddrSetBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT));
|
||||
|
||||
this->writeMemory(
|
||||
TargetMemoryType::RAM,
|
||||
ddrSetAddress,
|
||||
{static_cast<unsigned char>(ddrSetBitset.to_ulong())}
|
||||
);
|
||||
}
|
||||
|
||||
if (padDescriptor.ddrClearAddress.has_value() && padDescriptor.ddrClearAddress != ddrSetAddress) {
|
||||
// We also need to ensure the data direction clear register value is correct
|
||||
auto ddrClearAddress = padDescriptor.ddrClearAddress.value();
|
||||
auto ddrClearValue = this->readMemory(TargetMemoryType::RAM, ddrClearAddress, 1);
|
||||
|
||||
if (ddrClearValue.empty()) {
|
||||
throw Exception("Failed to read DDCR value");
|
||||
}
|
||||
|
||||
auto ddrClearBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrClearValue.front());
|
||||
if (ddrClearBitset.test(pinNumber) == (state.ioDirection == TargetPinState::IoDirection::INPUT)) {
|
||||
ddrClearBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::INPUT));
|
||||
|
||||
this->writeMemory(
|
||||
TargetMemoryType::RAM,
|
||||
ddrClearAddress,
|
||||
{static_cast<unsigned char>(ddrClearBitset.to_ulong())}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (ioState.has_value()) {
|
||||
auto portSetAddress = padDescriptor.gpioPortSetAddress.value();
|
||||
auto portSetRegisterValue = this->readMemory(TargetMemoryType::RAM, portSetAddress, 1);
|
||||
|
||||
if (portSetRegisterValue.empty()) {
|
||||
throw Exception("Failed to read PORT register value");
|
||||
}
|
||||
|
||||
auto portSetBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(portSetRegisterValue.front());
|
||||
if (portSetBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) {
|
||||
// PORT set register needs updating
|
||||
portSetBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH));
|
||||
|
||||
this->writeMemory(
|
||||
TargetMemoryType::RAM,
|
||||
portSetAddress,
|
||||
{static_cast<unsigned char>(portSetBitset.to_ulong())}
|
||||
);
|
||||
}
|
||||
|
||||
if (padDescriptor.gpioPortClearAddress.has_value() && padDescriptor.gpioPortClearAddress != portSetAddress) {
|
||||
// We also need to ensure the PORT clear register value is correct
|
||||
auto portClearAddress = padDescriptor.gpioPortClearAddress.value();
|
||||
auto portClearRegisterValue = this->readMemory(TargetMemoryType::RAM, portClearAddress, 1);
|
||||
|
||||
if (portClearRegisterValue.empty()) {
|
||||
throw Exception("Failed to read PORT (OUTSET) register value");
|
||||
}
|
||||
|
||||
auto portClearBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(portClearRegisterValue.front());
|
||||
if (portClearBitset.test(pinNumber) == (ioState == TargetPinState::IoState::LOW)) {
|
||||
// PORT clear register needs updating
|
||||
portClearBitset.set(pinNumber, (ioState == TargetPinState::IoState::LOW));
|
||||
|
||||
this->writeMemory(
|
||||
TargetMemoryType::RAM,
|
||||
portClearAddress,
|
||||
{static_cast<unsigned char>(portClearBitset.to_ulong())}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Avr8::willMemoryWriteAffectIoPorts(TargetMemoryType memoryType, std::uint32_t startAddress, std::uint32_t bytes) {
|
||||
auto& targetParameters = this->getTargetParameters();
|
||||
|
||||
/*
|
||||
* We're making an assumption here; that all IO port addresses for all AVR8 targets are aligned. I have no idea
|
||||
* how well this will hold.
|
||||
*
|
||||
* If they're not aligned, this function may report false positives.
|
||||
*/
|
||||
if (targetParameters.ioPortAddressRangeStart.has_value() && targetParameters.ioPortAddressRangeEnd.has_value()) {
|
||||
auto endAddress = startAddress + (bytes - 1);
|
||||
return (
|
||||
startAddress >= targetParameters.ioPortAddressRangeStart
|
||||
&& startAddress <= targetParameters.ioPortAddressRangeEnd
|
||||
) || (
|
||||
endAddress >= targetParameters.ioPortAddressRangeStart
|
||||
&& endAddress <= targetParameters.ioPortAddressRangeEnd
|
||||
) || (
|
||||
startAddress <= targetParameters.ioPortAddressRangeStart
|
||||
&& endAddress >= targetParameters.ioPortAddressRangeStart
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
132
src/Targets/Microchip/AVR/AVR8/Avr8.hpp
Normal file
132
src/Targets/Microchip/AVR/AVR8/Avr8.hpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <queue>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8Interface.hpp"
|
||||
#include "src/Targets/Microchip/AVR/Target.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||
#include "src/ApplicationConfig.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
#include "TargetParameters.hpp"
|
||||
#include "Family.hpp"
|
||||
#include "PadDescriptor.hpp"
|
||||
|
||||
// Part Description
|
||||
#include "PartDescription/PartDescriptionFile.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
{
|
||||
using namespace Exceptions;
|
||||
using namespace PartDescription;
|
||||
|
||||
using PartDescription::PartDescriptionFile;
|
||||
using DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8Interface;
|
||||
using Targets::TargetRegisterMap;
|
||||
|
||||
class Avr8: public Target
|
||||
{
|
||||
protected:
|
||||
Avr8Interface* avr8Interface;
|
||||
std::string name = "";
|
||||
std::optional<Family> family;
|
||||
std::optional<PartDescriptionFile> partDescription = std::nullopt;
|
||||
std::optional<TargetParameters> targetParameters = std::nullopt;
|
||||
std::map<std::string, PadDescriptor> padDescriptorsByName;
|
||||
std::map<int, TargetVariant> targetVariantsById;
|
||||
|
||||
virtual TargetParameters& getTargetParameters();
|
||||
|
||||
virtual void loadPadDescriptors();
|
||||
virtual void loadTargetVariants();
|
||||
|
||||
public:
|
||||
explicit Avr8() = default;
|
||||
Avr8(const std::string& name): name(name) {};
|
||||
|
||||
[[nodiscard]] std::string getName() const override {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if DebugTool is compatible with AVR8 targets.
|
||||
*
|
||||
* @param debugTool
|
||||
* @return
|
||||
*/
|
||||
bool isDebugToolSupported(DebugTool* debugTool) override {
|
||||
return debugTool->getAvr8Interface() != nullptr;
|
||||
}
|
||||
|
||||
void setDebugTool(DebugTool* debugTool) override {
|
||||
this->avr8Interface = debugTool->getAvr8Interface();
|
||||
};
|
||||
|
||||
void preActivationConfigure(const TargetConfig& targetConfig) override;
|
||||
void postActivationConfigure() override;
|
||||
|
||||
virtual void postPromotionConfigure() override;
|
||||
|
||||
void activate() override;
|
||||
|
||||
void deactivate() override;
|
||||
|
||||
TargetSignature getId() override;
|
||||
|
||||
void run() override;
|
||||
|
||||
void stop() override;
|
||||
|
||||
void step() override;
|
||||
|
||||
void reset() override;
|
||||
|
||||
void setBreakpoint(std::uint32_t address) override;
|
||||
void removeBreakpoint(std::uint32_t address) override;
|
||||
void clearAllBreakpoints() override;
|
||||
|
||||
/**
|
||||
* AVR8 targets are promotable. See promote() method for more.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool supportsPromotion() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual std::vector<TargetVariant> generateVariantsFromPartDescription();
|
||||
|
||||
virtual TargetDescriptor getDescriptor() override;
|
||||
|
||||
virtual std::unique_ptr<Targets::Target> promote() override;
|
||||
|
||||
virtual TargetRegisters readGeneralPurposeRegisters(std::set<std::size_t> registerIds) override;
|
||||
virtual void writeRegisters(const TargetRegisters& registers) override;
|
||||
virtual TargetRegisters readRegisters(const TargetRegisterDescriptors& descriptors) override;
|
||||
virtual TargetMemoryBuffer readMemory(TargetMemoryType memoryType, std::uint32_t startAddress, std::uint32_t bytes) override;
|
||||
virtual void writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) override;
|
||||
|
||||
virtual TargetState getState() override;
|
||||
|
||||
virtual std::uint32_t getProgramCounter() override;
|
||||
virtual TargetRegister getProgramCounterRegister() override;
|
||||
virtual TargetRegister getStackPointerRegister() override;
|
||||
virtual TargetRegister getStatusRegister() override;
|
||||
virtual void setProgramCounter(std::uint32_t programCounter) override;
|
||||
|
||||
virtual std::map<int, TargetPinState> getPinStates(int variantId) override;
|
||||
virtual void setPinState(
|
||||
int variantId,
|
||||
const TargetPinDescriptor& pinDescriptor,
|
||||
const TargetPinState& state
|
||||
) override;
|
||||
|
||||
virtual bool willMemoryWriteAffectIoPorts(
|
||||
TargetMemoryType memoryType,
|
||||
std::uint32_t startAddress,
|
||||
std::uint32_t bytes
|
||||
) override;
|
||||
};
|
||||
}
|
||||
11
src/Targets/Microchip/AVR/AVR8/Family.hpp
Normal file
11
src/Targets/Microchip/AVR/AVR8/Family.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
{
|
||||
enum class Family: int
|
||||
{
|
||||
MEGA,
|
||||
XMEGA,
|
||||
TINY,
|
||||
};
|
||||
}
|
||||
7
src/Targets/Microchip/AVR/AVR8/Mega/Mega.cpp
Normal file
7
src/Targets/Microchip/AVR/AVR8/Mega/Mega.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <QtCore>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "Mega.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
using namespace Bloom::Targets::Microchip::Avr::Avr8Bit;
|
||||
18
src/Targets/Microchip/AVR/AVR8/Mega/Mega.hpp
Normal file
18
src/Targets/Microchip/AVR/AVR8/Mega/Mega.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Avr8.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
{
|
||||
class Mega: public Avr8
|
||||
{
|
||||
protected:
|
||||
|
||||
public:
|
||||
Mega(const Avr8& avr8) : Avr8(avr8) {};
|
||||
|
||||
virtual bool supportsPromotion() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
42
src/Targets/Microchip/AVR/AVR8/PadDescriptor.hpp
Normal file
42
src/Targets/Microchip/AVR/AVR8/PadDescriptor.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
{
|
||||
/**
|
||||
* Pin configurations for AVR8 targets may vary across target variants. This is why we must differentiate a pin
|
||||
* to a pad. A pin is mapped to a pad, but this mapping is variant specific. For example, pin 4 on
|
||||
* the ATmega328P-PN (DIP variant) is mapped to a GPIO pad (PORTD/PIN2), but on the QFN variant (ATmega328P-MN),
|
||||
* pin 4 is mapped to a GND pad.
|
||||
*
|
||||
* PadDescriptor describes a single pad on an AVR8 target. On target configuration, PadDescriptors are
|
||||
* generated from the AVR8 part description file. These descriptors are mapped to pad names.
|
||||
* See Avr8::loadPadDescriptors() for more.
|
||||
*/
|
||||
struct PadDescriptor
|
||||
{
|
||||
std::string name;
|
||||
|
||||
std::optional<std::uint8_t> gpioPinNumber;
|
||||
|
||||
/**
|
||||
* AVR8 GPIO pins can be manipulated by writing to an IO register address. The gpioPortAddress member
|
||||
* holds this address.
|
||||
*/
|
||||
std::optional<std::uint16_t> gpioPortSetAddress;
|
||||
std::optional<std::uint16_t> gpioPortInputAddress;
|
||||
|
||||
std::optional<std::uint16_t> gpioPortClearAddress;
|
||||
|
||||
/**
|
||||
* The data direction of a pin is configured via a data direction register (DDR), which, like the
|
||||
* gpioPortSetAddress, is an IO register.
|
||||
*/
|
||||
std::optional<std::uint16_t> ddrSetAddress;
|
||||
|
||||
std::optional<std::uint16_t> ddrClearAddress;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "MemorySegment.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct AddressSpace {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::uint16_t startAddress;
|
||||
std::uint16_t size;
|
||||
bool littleEndian = true;
|
||||
std::map<MemorySegmentType, std::map<std::string, MemorySegment>> memorySegmentsByTypeAndName;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <QDomElement>
|
||||
|
||||
#include "src/Helpers/BiMap.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
enum MemorySegmentType {
|
||||
REGISTERS,
|
||||
IO,
|
||||
EEPROM,
|
||||
RAM,
|
||||
FLASH,
|
||||
SIGNATURES,
|
||||
FUSES,
|
||||
LOCKBITS,
|
||||
OSCCAL,
|
||||
};
|
||||
|
||||
struct MemorySegment {
|
||||
std::string name;
|
||||
MemorySegmentType type;
|
||||
std::uint32_t startAddress;
|
||||
std::uint32_t size;
|
||||
std::optional<std::uint16_t> pageSize;
|
||||
|
||||
/**
|
||||
* Mapping of all known memory segment types by their name. Any memory segments belonging to a type
|
||||
* not defined in here should be ignored.
|
||||
*/
|
||||
static const inline BiMap<std::string, MemorySegmentType> typesMappedByName = {
|
||||
{"regs", MemorySegmentType::REGISTERS},
|
||||
{"io", MemorySegmentType::IO},
|
||||
{"eeprom", MemorySegmentType::EEPROM},
|
||||
{"ram", MemorySegmentType::RAM},
|
||||
{"flash", MemorySegmentType::FLASH},
|
||||
{"signatures", MemorySegmentType::SIGNATURES},
|
||||
{"fuses", MemorySegmentType::FUSES},
|
||||
{"lockbits", MemorySegmentType::LOCKBITS},
|
||||
{"osccal", MemorySegmentType::OSCCAL},
|
||||
};
|
||||
};
|
||||
}
|
||||
13
src/Targets/Microchip/AVR/AVR8/PartDescription/Module.hpp
Normal file
13
src/Targets/Microchip/AVR/AVR8/PartDescription/Module.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "ModuleInstance.hpp"
|
||||
#include "RegisterGroup.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct Module {
|
||||
std::string name;
|
||||
std::map<std::string, ModuleInstance> instancesMappedByName;
|
||||
std::map<std::string, RegisterGroup> registerGroupsMappedByName;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "RegisterGroup.hpp"
|
||||
#include "Signal.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct ModuleInstance {
|
||||
std::string name;
|
||||
std::map<std::string, RegisterGroup> registerGroupsMappedByName;
|
||||
std::vector<Signal> instanceSignals;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,862 @@
|
||||
#include "PartDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR/Exceptions/PartDescriptionParsingFailureException.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Application.hpp"
|
||||
|
||||
using namespace Bloom::Targets::Microchip::Avr::Avr8Bit;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
// TODO: Move this into a resolvePartDescriptionFile() method.
|
||||
PartDescriptionFile::PartDescriptionFile(const std::string& targetSignatureHex, std::optional<std::string> targetName) {
|
||||
auto mapping = this->getPartDescriptionMapping();
|
||||
auto qTargetSignatureHex = QString::fromStdString(targetSignatureHex);
|
||||
|
||||
if (mapping.contains(qTargetSignatureHex)) {
|
||||
// We have a match for the target signature.
|
||||
auto descriptionFilesJsonArray = mapping.find(qTargetSignatureHex).value().toArray();
|
||||
auto descriptionFiles = std::vector<QJsonValue>();
|
||||
std::copy_if(
|
||||
descriptionFilesJsonArray.begin(),
|
||||
descriptionFilesJsonArray.end(),
|
||||
std::back_inserter(descriptionFiles),
|
||||
[&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() && descriptionFiles.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 (descriptionFiles.size() == 1) {
|
||||
// Attempt to load the XML part description file
|
||||
auto descriptionFilePath = QString::fromStdString(Application::getApplicationDirPath()) + "/"
|
||||
+ descriptionFiles.front().toObject().find("targetDescriptionFilePath")->toString();
|
||||
|
||||
Logger::debug("Loading AVR8 part description file: " + descriptionFilePath.toStdString());
|
||||
this->init(descriptionFilePath);
|
||||
|
||||
} else if (descriptionFiles.size() > 1) {
|
||||
/*
|
||||
* There are numerous part description files mapped to this target signature. There's really not
|
||||
* much we can do at this point, so we'll just instruct the user to use a more specific target name.
|
||||
*/
|
||||
QStringList targetNames;
|
||||
std::transform(
|
||||
descriptionFiles.begin(),
|
||||
descriptionFiles.end(),
|
||||
std::back_inserter(targetNames),
|
||||
[](const QJsonValue& descriptionFile) {
|
||||
return QString("\"" + descriptionFile.toObject().find("targetName")->toString().toLower() + "\"");
|
||||
}
|
||||
);
|
||||
|
||||
throw Exception("Failed to resolve part description file for target \""
|
||||
+ targetSignatureHex + "\" - ambiguous signature.\nThe signature is mapped to numerous targets: "
|
||||
+ targetNames.join(", ").toStdString() + ".\n\nPlease update the target name in your Bloom " +
|
||||
"configuration to one of the above."
|
||||
);
|
||||
|
||||
} else {
|
||||
throw Exception("Failed to resolve part description file for target \""
|
||||
+ targetSignatureHex + "\" - invalid AVR8 target description mapping."
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw Exception("Failed to resolve part description file for target \""
|
||||
+ targetSignatureHex + "\" - unknown target signature.");
|
||||
}
|
||||
}
|
||||
|
||||
void PartDescriptionFile::init(const QString& xmlFilePath) {
|
||||
auto file = QFile(xmlFilePath);
|
||||
if (!file.exists()) {
|
||||
// This can happen if someone has been messing with the Resources directory.
|
||||
throw Exception("Failed to load part description file - file not found");
|
||||
}
|
||||
|
||||
file.open(QIODevice::ReadOnly);
|
||||
auto xml = QDomDocument();
|
||||
xml.setContent(file.readAll());
|
||||
this->init(xml);
|
||||
}
|
||||
|
||||
void PartDescriptionFile::init(const QDomDocument& xml) {
|
||||
this->xml = xml;
|
||||
|
||||
auto device = xml.elementsByTagName("devices").item(0)
|
||||
.toElement().elementsByTagName("device").item(0).toElement();
|
||||
|
||||
if (!device.isElement()) {
|
||||
throw PartDescriptionParsingFailureException("Device element not found.");
|
||||
}
|
||||
|
||||
this->deviceElement = device;
|
||||
}
|
||||
|
||||
QJsonObject PartDescriptionFile::getPartDescriptionMapping() {
|
||||
auto mappingFile = QFile(
|
||||
QString::fromStdString(Application::getResourcesDirPath() + "/TargetPartDescriptions/AVR/Mapping.json")
|
||||
);
|
||||
|
||||
if (!mappingFile.exists()) {
|
||||
throw TargetControllerStartupFailure("Failed to load AVR part description mapping - mapping file not found");
|
||||
}
|
||||
|
||||
mappingFile.open(QIODevice::ReadOnly);
|
||||
return QJsonDocument::fromJson(mappingFile.readAll()).object();
|
||||
}
|
||||
|
||||
std::string PartDescriptionFile::getTargetName() const {
|
||||
return this->deviceElement.attributes().namedItem("name").nodeValue().toStdString();
|
||||
}
|
||||
|
||||
TargetSignature PartDescriptionFile::getTargetSignature() const {
|
||||
auto propertyGroups = this->getPropertyGroupsMappedByName();
|
||||
auto signaturePropertyGroupIt = propertyGroups.find("signatures");
|
||||
|
||||
if (signaturePropertyGroupIt == propertyGroups.end()) {
|
||||
throw PartDescriptionParsingFailureException("Signature property group not found");
|
||||
}
|
||||
|
||||
auto signaturePropertyGroup = signaturePropertyGroupIt->second;
|
||||
auto& signatureProperties = signaturePropertyGroup.propertiesMappedByName;
|
||||
std::optional<unsigned char> signatureByteZero;
|
||||
std::optional<unsigned char> signatureByteOne;
|
||||
std::optional<unsigned char> signatureByteTwo;
|
||||
|
||||
if (signatureProperties.find("signature0") != signatureProperties.end()) {
|
||||
signatureByteZero = static_cast<unsigned char>(
|
||||
signatureProperties.find("signature0")->second.value.toShort(nullptr, 16)
|
||||
);
|
||||
}
|
||||
|
||||
if (signatureProperties.find("signature1") != signatureProperties.end()) {
|
||||
signatureByteOne = static_cast<unsigned char>(
|
||||
signatureProperties.find("signature1")->second.value.toShort(nullptr, 16)
|
||||
);
|
||||
}
|
||||
|
||||
if (signatureProperties.find("signature2") != signatureProperties.end()) {
|
||||
signatureByteTwo = static_cast<unsigned char>(
|
||||
signatureProperties.find("signature2")->second.value.toShort(nullptr, 16)
|
||||
);
|
||||
}
|
||||
|
||||
if (signatureByteZero.has_value() && signatureByteOne.has_value() && signatureByteTwo.has_value()) {
|
||||
return TargetSignature(signatureByteZero.value(), signatureByteOne.value(), signatureByteTwo.value());
|
||||
}
|
||||
|
||||
throw PartDescriptionParsingFailureException("Failed to extract target signature from AVR8 part description.");
|
||||
}
|
||||
|
||||
AddressSpace PartDescriptionFile::generateAddressSpaceFromXml(const QDomElement& xmlElement) const {
|
||||
if (
|
||||
!xmlElement.hasAttribute("id")
|
||||
|| !xmlElement.hasAttribute("name")
|
||||
|| !xmlElement.hasAttribute("size")
|
||||
|| !xmlElement.hasAttribute("start")
|
||||
) {
|
||||
throw Exception("Address space element missing id/name/size/start attributes.");
|
||||
}
|
||||
|
||||
auto addressSpace = AddressSpace();
|
||||
addressSpace.name = xmlElement.attribute("name").toStdString();
|
||||
addressSpace.id = xmlElement.attribute("id").toStdString();
|
||||
|
||||
bool conversionStatus;
|
||||
addressSpace.startAddress = xmlElement.attribute("start").toUShort(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to convert start address hex value to integer.");
|
||||
}
|
||||
|
||||
addressSpace.size = xmlElement.attribute("size").toUShort(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to convert size hex value to integer.");
|
||||
}
|
||||
|
||||
if (xmlElement.hasAttribute("endianness")) {
|
||||
addressSpace.littleEndian = (xmlElement.attribute("endianness").toStdString() == "little");
|
||||
}
|
||||
|
||||
// Create memory segment objects and add them to the mapping.
|
||||
auto segmentNodes = xmlElement.elementsByTagName("memory-segment");
|
||||
auto& memorySegments = addressSpace.memorySegmentsByTypeAndName;
|
||||
for (int segmentIndex = 0; segmentIndex < segmentNodes.count(); segmentIndex++) {
|
||||
try {
|
||||
auto segment = this->generateMemorySegmentFromXml(segmentNodes.item(segmentIndex).toElement());
|
||||
|
||||
if (memorySegments.find(segment.type) == memorySegments.end()) {
|
||||
memorySegments.insert(
|
||||
std::pair<
|
||||
MemorySegmentType,
|
||||
decltype(addressSpace.memorySegmentsByTypeAndName)::mapped_type
|
||||
>(segment.type, {}));
|
||||
}
|
||||
|
||||
memorySegments.find(segment.type)->second.insert(std::pair(segment.name, segment));
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::debug("Failed to extract memory segment from part description element - "
|
||||
+ exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return addressSpace;
|
||||
}
|
||||
|
||||
MemorySegment PartDescriptionFile::generateMemorySegmentFromXml(const QDomElement& xmlElement) const {
|
||||
if (
|
||||
!xmlElement.hasAttribute("type")
|
||||
|| !xmlElement.hasAttribute("name")
|
||||
|| !xmlElement.hasAttribute("size")
|
||||
|| !xmlElement.hasAttribute("start")
|
||||
) {
|
||||
throw Exception("Missing type/name/size/start attributes");
|
||||
}
|
||||
|
||||
auto segment = MemorySegment();
|
||||
auto typeName = xmlElement.attribute("type").toStdString();
|
||||
auto type = MemorySegment::typesMappedByName.valueAt(typeName);
|
||||
|
||||
if (!type.has_value()) {
|
||||
throw Exception("Unknown type: \"" + typeName + "\"");
|
||||
}
|
||||
|
||||
segment.type = type.value();
|
||||
segment.name = xmlElement.attribute("name").toLower().toStdString();
|
||||
|
||||
bool conversionStatus;
|
||||
segment.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
// Failed to convert startAddress hex value as string to uint16_t
|
||||
throw Exception("Invalid start address");
|
||||
}
|
||||
|
||||
segment.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
// Failed to convert size hex value as string to uint16_t
|
||||
throw Exception("Invalid size");
|
||||
}
|
||||
|
||||
if (xmlElement.hasAttribute("pagesize")) {
|
||||
// The page size can be in single byte hexadecimal form ("0x01"), or it can be in plain integer form!
|
||||
auto pageSize = xmlElement.attribute("pagesize");
|
||||
segment.pageSize = pageSize.toUInt(&conversionStatus, pageSize.contains("0x") ? 16 : 10);
|
||||
|
||||
if (!conversionStatus) {
|
||||
// Failed to convert size hex value as string to uint16_t
|
||||
throw Exception("Invalid size");
|
||||
}
|
||||
}
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
RegisterGroup PartDescriptionFile::generateRegisterGroupFromXml(const QDomElement& xmlElement) const {
|
||||
if (!xmlElement.hasAttribute("name")) {
|
||||
throw Exception("Missing register group name attribute");
|
||||
}
|
||||
|
||||
auto registerGroup = RegisterGroup();
|
||||
registerGroup.name = xmlElement.attribute("name").toLower().toStdString();
|
||||
|
||||
if (registerGroup.name.empty()) {
|
||||
throw Exception("Empty register group name");
|
||||
}
|
||||
|
||||
if (xmlElement.hasAttribute("offset")) {
|
||||
registerGroup.offset = xmlElement.attribute("offset").toInt(nullptr, 16);
|
||||
}
|
||||
|
||||
auto& registers = registerGroup.registersMappedByName;
|
||||
auto registerNodes = xmlElement.elementsByTagName("register");
|
||||
for (int registerIndex = 0; registerIndex < registerNodes.count(); registerIndex++) {
|
||||
try {
|
||||
auto reg = this->generateRegisterFromXml(registerNodes.item(registerIndex).toElement());
|
||||
registers.insert(std::pair(reg.name, reg));
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::debug("Failed to extract register from register group part description element - "
|
||||
+ exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return registerGroup;
|
||||
}
|
||||
|
||||
Register PartDescriptionFile::generateRegisterFromXml(const QDomElement& xmlElement) const {
|
||||
if (
|
||||
!xmlElement.hasAttribute("name")
|
||||
|| !xmlElement.hasAttribute("offset")
|
||||
|| !xmlElement.hasAttribute("size")
|
||||
) {
|
||||
throw Exception("Missing register name/offset/size attribute");
|
||||
}
|
||||
|
||||
auto reg = Register();
|
||||
reg.name = xmlElement.attribute("name").toLower().toStdString();
|
||||
|
||||
if (reg.name.empty()) {
|
||||
throw Exception("Empty register name");
|
||||
}
|
||||
|
||||
bool conversionStatus;
|
||||
reg.size = xmlElement.attribute("size").toUShort(nullptr, 10);
|
||||
reg.offset = xmlElement.attribute("offset").toUShort(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
// Failed to convert offset hex value as string to uint16_t
|
||||
throw Exception("Invalid register offset");
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
Family PartDescriptionFile::getFamily() const {
|
||||
static auto familyNameToEnums = this->getFamilyNameToEnumMapping();
|
||||
auto familyName = this->deviceElement.attributes().namedItem("family").nodeValue().toLower().toStdString();
|
||||
|
||||
if (familyName.empty()) {
|
||||
throw Exception("Could not find target family name in part description file.");
|
||||
}
|
||||
|
||||
if (familyNameToEnums.find(familyName) == familyNameToEnums.end()) {
|
||||
throw Exception("Unknown family name in part description file.");
|
||||
}
|
||||
|
||||
return familyNameToEnums.find(familyName)->second;
|
||||
}
|
||||
|
||||
const std::map<std::string, PropertyGroup>& PartDescriptionFile::getPropertyGroupsMappedByName() const {
|
||||
if (!this->cachedPropertyGroupMapping.has_value()) {
|
||||
if (!this->deviceElement.isElement()) {
|
||||
throw PartDescriptionParsingFailureException("Device element not found.");
|
||||
}
|
||||
|
||||
std::map<std::string, PropertyGroup> output;
|
||||
auto propertyGroupNodes = this->deviceElement.elementsByTagName("property-groups").item(0).toElement()
|
||||
.elementsByTagName("property-group");
|
||||
|
||||
for (int propertyGroupIndex = 0; propertyGroupIndex < propertyGroupNodes.count(); propertyGroupIndex++) {
|
||||
auto propertyGroupElement = propertyGroupNodes.item(propertyGroupIndex).toElement();
|
||||
auto propertyGroupName = propertyGroupElement.attributes().namedItem("name").nodeValue().toLower().toStdString();
|
||||
PropertyGroup propertyGroup;
|
||||
propertyGroup.name = propertyGroupName;
|
||||
|
||||
auto propertyNodes = propertyGroupElement.elementsByTagName("property");
|
||||
for (int propertyIndex = 0; propertyIndex < propertyNodes.count(); propertyIndex++) {
|
||||
auto propertyElement = propertyNodes.item(propertyIndex).toElement();
|
||||
auto propertyName = propertyElement.attributes().namedItem("name").nodeValue();
|
||||
|
||||
Property property;
|
||||
property.name = propertyName.toStdString();
|
||||
property.value = propertyElement.attributes().namedItem("value").nodeValue();
|
||||
|
||||
propertyGroup.propertiesMappedByName.insert(std::pair(propertyName.toLower().toStdString(), property));
|
||||
}
|
||||
|
||||
output.insert(std::pair(propertyGroup.name, propertyGroup));
|
||||
}
|
||||
|
||||
this->cachedPropertyGroupMapping.emplace(output);
|
||||
}
|
||||
|
||||
return this->cachedPropertyGroupMapping.value();
|
||||
}
|
||||
|
||||
const std::map<std::string, Module>& PartDescriptionFile::getModulesMappedByName() const {
|
||||
if (!this->cachedModuleByNameMapping.has_value()) {
|
||||
std::map<std::string, Module> output;
|
||||
auto moduleNodes = this->xml.elementsByTagName("modules").item(0).toElement()
|
||||
.elementsByTagName("module");
|
||||
|
||||
for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) {
|
||||
auto moduleElement = moduleNodes.item(moduleIndex).toElement();
|
||||
auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString();
|
||||
Module module;
|
||||
module.name = moduleName;
|
||||
|
||||
auto registerGroupNodes = moduleElement.elementsByTagName("register-group");
|
||||
for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) {
|
||||
auto registerGroup = this->generateRegisterGroupFromXml(registerGroupNodes.item(registerGroupIndex).toElement());
|
||||
|
||||
module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup));
|
||||
}
|
||||
|
||||
output.insert(std::pair(module.name, module));
|
||||
}
|
||||
|
||||
this->cachedModuleByNameMapping.emplace(output);
|
||||
}
|
||||
|
||||
return this->cachedModuleByNameMapping.value();
|
||||
}
|
||||
|
||||
const std::map<std::string, Module>& PartDescriptionFile::getPeripheralModulesMappedByName() const {
|
||||
if (!this->cachedPeripheralModuleByNameMapping.has_value()) {
|
||||
std::map<std::string, Module> output;
|
||||
auto moduleNodes = this->deviceElement.elementsByTagName("peripherals").item(0).toElement()
|
||||
.elementsByTagName("module");
|
||||
|
||||
for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) {
|
||||
auto moduleElement = moduleNodes.item(moduleIndex).toElement();
|
||||
auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString();
|
||||
Module module;
|
||||
module.name = moduleName;
|
||||
|
||||
auto registerGroupNodes = moduleElement.elementsByTagName("register-group");
|
||||
for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) {
|
||||
auto registerGroup = this->generateRegisterGroupFromXml(registerGroupNodes.item(registerGroupIndex).toElement());
|
||||
|
||||
module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup));
|
||||
}
|
||||
|
||||
auto instanceNodes = moduleElement.elementsByTagName("instance");
|
||||
for (int instanceIndex = 0; instanceIndex < instanceNodes.count(); instanceIndex++) {
|
||||
auto instanceXml = instanceNodes.item(instanceIndex).toElement();
|
||||
auto instance = ModuleInstance();
|
||||
instance.name = instanceXml.attribute("name").toLower().toStdString();
|
||||
|
||||
auto registerGroupNodes = instanceXml.elementsByTagName("register-group");
|
||||
for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) {
|
||||
auto registerGroup = this->generateRegisterGroupFromXml(registerGroupNodes.item(registerGroupIndex).toElement());
|
||||
|
||||
instance.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup));
|
||||
}
|
||||
|
||||
auto signalNodes = instanceXml.elementsByTagName("signals").item(0).toElement()
|
||||
.elementsByTagName("signal");
|
||||
for (int signalIndex = 0; signalIndex < signalNodes.count(); signalIndex++) {
|
||||
auto signalXml = signalNodes.item(signalIndex).toElement();
|
||||
auto signal = Signal();
|
||||
|
||||
if (!signalXml.hasAttribute("pad")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
signal.padName = signalXml.attribute("pad").toLower().toStdString();
|
||||
signal.function = signalXml.attribute("function").toStdString();
|
||||
signal.group = signalXml.attribute("group").toStdString();
|
||||
auto indexAttribute = signalXml.attribute("index");
|
||||
bool indexValid = false;
|
||||
auto indexValue = indexAttribute.toInt(&indexValid, 10);
|
||||
|
||||
if (!indexAttribute.isEmpty() && indexValid) {
|
||||
signal.index = indexValue;
|
||||
}
|
||||
|
||||
instance.instanceSignals.emplace_back(signal);
|
||||
}
|
||||
|
||||
module.instancesMappedByName.insert(std::pair(instance.name, instance));
|
||||
}
|
||||
|
||||
output.insert(std::pair(module.name, module));
|
||||
}
|
||||
|
||||
this->cachedPeripheralModuleByNameMapping.emplace(output);
|
||||
}
|
||||
|
||||
return this->cachedPeripheralModuleByNameMapping.value();
|
||||
}
|
||||
|
||||
std::map<std::string, AddressSpace> PartDescriptionFile::getAddressSpacesMappedById() const {
|
||||
std::map<std::string, AddressSpace> output;
|
||||
|
||||
auto addressSpaceNodes = this->deviceElement.elementsByTagName("address-spaces").item(0).toElement()
|
||||
.elementsByTagName("address-space");
|
||||
|
||||
for (int addressSpaceIndex = 0; addressSpaceIndex < addressSpaceNodes.count(); addressSpaceIndex++) {
|
||||
try {
|
||||
auto addressSpace = this->generateAddressSpaceFromXml(addressSpaceNodes.item(addressSpaceIndex).toElement());
|
||||
output.insert(std::pair(addressSpace.id, addressSpace));
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::debug("Failed to extract address space from part description element - " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::optional<MemorySegment> PartDescriptionFile::getFlashMemorySegment() const {
|
||||
auto addressMapping = this->getAddressSpacesMappedById();
|
||||
auto programAddressSpaceIt = addressMapping.find("prog");
|
||||
|
||||
// Flash memory attributes are typically found in memory segments within the program address space.
|
||||
if (programAddressSpaceIt != addressMapping.end()) {
|
||||
auto& programAddressSpace = programAddressSpaceIt->second;
|
||||
auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName;
|
||||
|
||||
if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) {
|
||||
auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second;
|
||||
|
||||
/*
|
||||
* Some part descriptions describe the flash memory segments in the "APP_SECTION" segment, whereas
|
||||
* others use the "FLASH" segment.
|
||||
*/
|
||||
auto flashSegmentIt = flashMemorySegments.find("app_section") != flashMemorySegments.end() ?
|
||||
flashMemorySegments.find("app_section") : flashMemorySegments.find("flash");
|
||||
|
||||
if (flashSegmentIt != flashMemorySegments.end()) {
|
||||
return flashSegmentIt->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<MemorySegment> PartDescriptionFile::getRamMemorySegment() const {
|
||||
auto addressMapping = this->getAddressSpacesMappedById();
|
||||
|
||||
// Internal RAM ®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> PartDescriptionFile::getRegisterMemorySegment() const {
|
||||
auto addressMapping = this->getAddressSpacesMappedById();
|
||||
|
||||
// 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> PartDescriptionFile::getEepromMemorySegment() const {
|
||||
auto addressMapping = this->getAddressSpacesMappedById();
|
||||
|
||||
// EEPROM attributes are usually found in the data address space
|
||||
auto eepromAddressSpaceIt = addressMapping.find("eeprom");
|
||||
|
||||
if (eepromAddressSpaceIt != addressMapping.end()) {
|
||||
auto& eepromAddressSpace = eepromAddressSpaceIt->second;
|
||||
auto& eepromAddressSpaceSegments = eepromAddressSpace.memorySegmentsByTypeAndName;
|
||||
|
||||
if (eepromAddressSpaceSegments.find(MemorySegmentType::EEPROM) != eepromAddressSpaceSegments.end()) {
|
||||
auto& eepromMemorySegments = eepromAddressSpaceSegments.find(MemorySegmentType::EEPROM)->second;
|
||||
auto eepromMemorySegmentIt = eepromMemorySegments.begin();
|
||||
|
||||
if (eepromMemorySegmentIt != eepromMemorySegments.end()) {
|
||||
return eepromMemorySegmentIt->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<MemorySegment> PartDescriptionFile::getFirstBootSectionMemorySegment() const {
|
||||
auto addressMapping = this->getAddressSpacesMappedById();
|
||||
auto programAddressSpaceIt = addressMapping.find("prog");
|
||||
|
||||
if (programAddressSpaceIt != addressMapping.end()) {
|
||||
auto& programAddressSpace = programAddressSpaceIt->second;
|
||||
auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName;
|
||||
|
||||
if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) {
|
||||
auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second;
|
||||
auto firstBootSectionSegmentIt = flashMemorySegments.find("boot_section_1");
|
||||
|
||||
if (flashMemorySegments.contains("boot_section_1")) {
|
||||
return flashMemorySegments.at("boot_section_1");
|
||||
|
||||
} else if (flashMemorySegments.contains("boot_section")) {
|
||||
return flashMemorySegments.at("boot_section");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RegisterGroup> PartDescriptionFile::getCpuRegisterGroup() const {
|
||||
auto& modulesByName = this->getModulesMappedByName();
|
||||
|
||||
if (modulesByName.find("cpu") != modulesByName.end()) {
|
||||
auto cpuModule = modulesByName.find("cpu")->second;
|
||||
auto cpuRegisterGroupIt = cpuModule.registerGroupsMappedByName.find("cpu");
|
||||
|
||||
if (cpuRegisterGroupIt != cpuModule.registerGroupsMappedByName.end()) {
|
||||
return cpuRegisterGroupIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RegisterGroup> PartDescriptionFile::getEepromRegisterGroup() const {
|
||||
auto& modulesByName = this->getModulesMappedByName();
|
||||
|
||||
if (modulesByName.find("eeprom") != modulesByName.end()) {
|
||||
auto eepromModule = modulesByName.find("eeprom")->second;
|
||||
auto eepromRegisterGroupIt = eepromModule.registerGroupsMappedByName.find("eeprom");
|
||||
|
||||
if (eepromRegisterGroupIt != eepromModule.registerGroupsMappedByName.end()) {
|
||||
return eepromRegisterGroupIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getStatusRegister() const {
|
||||
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
||||
|
||||
if (cpuRegisterGroup.has_value()) {
|
||||
auto statusRegisterIt = cpuRegisterGroup->registersMappedByName.find("sreg");
|
||||
|
||||
if (statusRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
|
||||
return statusRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getStackPointerRegister() const {
|
||||
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
||||
|
||||
if (cpuRegisterGroup.has_value()) {
|
||||
auto stackPointerRegisterIt = cpuRegisterGroup->registersMappedByName.find("sp");
|
||||
|
||||
if (stackPointerRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
|
||||
return stackPointerRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getStackPointerHighRegister() const {
|
||||
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
||||
|
||||
if (cpuRegisterGroup.has_value()) {
|
||||
auto stackPointerHighRegisterIt = cpuRegisterGroup->registersMappedByName.find("sph");
|
||||
|
||||
if (stackPointerHighRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
|
||||
return stackPointerHighRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getStackPointerLowRegister() const {
|
||||
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
||||
|
||||
if (cpuRegisterGroup.has_value()) {
|
||||
auto stackPointerLowRegisterIt = cpuRegisterGroup->registersMappedByName.find("spl");
|
||||
|
||||
if (stackPointerLowRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
|
||||
return stackPointerLowRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getOscillatorCalibrationRegister() const {
|
||||
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
||||
|
||||
if (cpuRegisterGroup.has_value()) {
|
||||
auto osccalRegisterIt = cpuRegisterGroup->registersMappedByName.find("osccal");
|
||||
|
||||
if (osccalRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
|
||||
return osccalRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getSpmcsrRegister() const {
|
||||
auto cpuRegisterGroup = this->getCpuRegisterGroup();
|
||||
|
||||
if (cpuRegisterGroup.has_value()) {
|
||||
auto spmcsrRegisterIt = cpuRegisterGroup->registersMappedByName.find("spmcsr");
|
||||
|
||||
if (spmcsrRegisterIt != cpuRegisterGroup->registersMappedByName.end()) {
|
||||
return spmcsrRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getEepromAddressRegister() const {
|
||||
auto eepromRegisterGroup = this->getEepromRegisterGroup();
|
||||
|
||||
if (eepromRegisterGroup.has_value()) {
|
||||
auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eear");
|
||||
|
||||
if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
|
||||
return eepromAddressRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getEepromDataRegister() const {
|
||||
auto eepromRegisterGroup = this->getEepromRegisterGroup();
|
||||
|
||||
if (eepromRegisterGroup.has_value()) {
|
||||
auto eepromDataRegisterIt = eepromRegisterGroup->registersMappedByName.find("eedr");
|
||||
|
||||
if (eepromDataRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
|
||||
return eepromDataRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Register> PartDescriptionFile::getEepromControlRegister() const {
|
||||
auto eepromRegisterGroup = this->getEepromRegisterGroup();
|
||||
|
||||
if (eepromRegisterGroup.has_value()) {
|
||||
auto eepromControlRegisterIt = eepromRegisterGroup->registersMappedByName.find("eecr");
|
||||
|
||||
if (eepromControlRegisterIt != eepromRegisterGroup->registersMappedByName.end()) {
|
||||
return eepromControlRegisterIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<Variant> PartDescriptionFile::getVariants() const {
|
||||
std::vector<Variant> output;
|
||||
|
||||
auto variantNodes = this->xml.elementsByTagName("variants").item(0).toElement()
|
||||
.elementsByTagName("variant");
|
||||
|
||||
for (int variantIndex = 0; variantIndex < variantNodes.count(); variantIndex++) {
|
||||
try {
|
||||
auto variantXml = variantNodes.item(variantIndex).toElement();
|
||||
|
||||
if (!variantXml.hasAttribute("ordercode")) {
|
||||
throw Exception("Missing ordercode attribute");
|
||||
}
|
||||
|
||||
if (!variantXml.hasAttribute("package")) {
|
||||
throw Exception("Missing package attribute");
|
||||
}
|
||||
|
||||
if (!variantXml.hasAttribute("pinout")) {
|
||||
throw Exception("Missing pinout attribute");
|
||||
}
|
||||
|
||||
auto variant = Variant();
|
||||
variant.orderCode = variantXml.attribute("ordercode").toStdString();
|
||||
variant.pinoutName = variantXml.attribute("pinout").toLower().toStdString();
|
||||
variant.package = variantXml.attribute("package").toUpper().toStdString();
|
||||
|
||||
if (variantXml.hasAttribute("disabled")) {
|
||||
variant.disabled = (variantXml.attribute("disabled") == "1");
|
||||
}
|
||||
|
||||
output.push_back(variant);
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::debug("Failed to extract variant from part description element - " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
const std::map<std::string, Pinout>& PartDescriptionFile::getPinoutsMappedByName() const {
|
||||
if (!this->cachedPinoutByNameMapping.has_value()) {
|
||||
this->cachedPinoutByNameMapping = std::map<std::string, Pinout>();
|
||||
|
||||
auto pinoutNodes = this->xml.elementsByTagName("pinouts").item(0).toElement()
|
||||
.elementsByTagName("pinout");
|
||||
|
||||
for (int pinoutIndex = 0; pinoutIndex < pinoutNodes.count(); pinoutIndex++) {
|
||||
try {
|
||||
auto pinoutXml = pinoutNodes.item(pinoutIndex).toElement();
|
||||
|
||||
if (!pinoutXml.hasAttribute("name")) {
|
||||
throw Exception("Missing name attribute");
|
||||
}
|
||||
|
||||
auto pinout = Pinout();
|
||||
pinout.name = pinoutXml.attribute("name").toLower().toStdString();
|
||||
|
||||
auto pinNodes = pinoutXml.elementsByTagName("pin");
|
||||
|
||||
for (int pinIndex = 0; pinIndex < pinNodes.count(); pinIndex++) {
|
||||
auto pinXml = pinNodes.item(pinIndex).toElement();
|
||||
|
||||
if (!pinXml.hasAttribute("position")) {
|
||||
throw Exception("Missing position attribute on pin element " + std::to_string(pinIndex));
|
||||
}
|
||||
|
||||
if (!pinXml.hasAttribute("pad")) {
|
||||
throw Exception("Missing pad attribute on pin element " + std::to_string(pinIndex));
|
||||
}
|
||||
|
||||
auto pin = Pin();
|
||||
bool positionConversionSucceeded = true;
|
||||
pin.position = pinXml.attribute("position").toInt(&positionConversionSucceeded, 10);
|
||||
pin.pad = pinXml.attribute("pad").toLower().toStdString();
|
||||
|
||||
if (!positionConversionSucceeded) {
|
||||
throw Exception("Failed to convert position attribute value to integer on pin element "
|
||||
+ std::to_string(pinIndex));
|
||||
}
|
||||
|
||||
pinout.pins.push_back(pin);
|
||||
}
|
||||
|
||||
this->cachedPinoutByNameMapping->insert(std::pair(pinout.name, pinout));
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::debug("Failed to extract pinout from part description element - " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this->cachedPinoutByNameMapping.value();
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFile>
|
||||
#include <QDomDocument>
|
||||
|
||||
#include "AddressSpace.hpp"
|
||||
#include "MemorySegment.hpp"
|
||||
#include "PropertyGroup.hpp"
|
||||
#include "RegisterGroup.hpp"
|
||||
#include "Module.hpp"
|
||||
#include "Variant.hpp"
|
||||
#include "Pinout.hpp"
|
||||
#include "src/Targets/Microchip/AVR/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
using Avr::TargetSignature;
|
||||
|
||||
/**
|
||||
* An AVR8 part description file is an XML file that describes a particular AVR8 target.
|
||||
* All supported AVR8 targets come with a part description file.
|
||||
*
|
||||
* Part description files are part of the Bloom codebase.
|
||||
* For AVR8 part description files, see directory "src/Targets/Microchip/AVR/PartDescriptionFiles/AVR8".
|
||||
*
|
||||
* During the build process, all part description files are copied to the distribution directory, ready
|
||||
* to be shipped with the Bloom binary. Alongside these files is a JSON file, containing a mapping of AVR8 target
|
||||
* signatures to part description file paths. Bloom uses this mapping to find a particular part description
|
||||
* file, given a target signature. See directory "bin/Distribution/Resources/TargetPartDescriptions".
|
||||
* The copying of the part description files, and the generation of the JSON mapping, is done by a PHP script:
|
||||
* "build/scripts/CopyAvrPartFilesAndCreateMapping.php". This script is invoked via a custom command, at build time.
|
||||
*
|
||||
* All processing of AVR8 part description files is done in this class.
|
||||
*/
|
||||
class PartDescriptionFile
|
||||
{
|
||||
private:
|
||||
QDomDocument xml;
|
||||
QDomElement deviceElement;
|
||||
mutable std::optional<std::map<std::string, PropertyGroup>> cachedPropertyGroupMapping;
|
||||
mutable std::optional<std::map<std::string, Module>> cachedModuleByNameMapping;
|
||||
mutable std::optional<std::map<std::string, Module>> cachedPeripheralModuleByNameMapping;
|
||||
mutable std::optional<std::map<std::string, Pinout>> cachedPinoutByNameMapping;
|
||||
|
||||
/**`
|
||||
* AVR8 part description files include the target family name. This method returns a mapping of part
|
||||
* description family name strings to Family enum values.
|
||||
*
|
||||
* TODO: the difference in AVR8 family variations, like "tinyAVR" and "tinyAVR 2" may require attention.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static inline auto getFamilyNameToEnumMapping() {
|
||||
// All keys should be lower case.
|
||||
return std::map<std::string, Family> {
|
||||
{"megaavr", Family::MEGA},
|
||||
{"avr mega", Family::MEGA},
|
||||
{"avr xmega", Family::XMEGA},
|
||||
{"avr tiny", Family::TINY},
|
||||
{"tinyavr", Family::TINY},
|
||||
{"tinyavr 2", Family::TINY},
|
||||
};
|
||||
};
|
||||
|
||||
void init(const QDomDocument& xml);
|
||||
void init(const QString& xmlFilePath);
|
||||
|
||||
/**
|
||||
* Constructs an AddressSpace object from an XML element (in the form of a QDomElement), taken from
|
||||
* an AVR part description file.
|
||||
*
|
||||
* @param xmlElement
|
||||
* @return
|
||||
*/
|
||||
AddressSpace generateAddressSpaceFromXml(const QDomElement& xmlElement) const;
|
||||
|
||||
/**
|
||||
* Constructs a MemorySegment from an XML element (in the form of a QDomElement) taken from
|
||||
* an AVR part description file.
|
||||
*
|
||||
* @param xmlElement
|
||||
* @return
|
||||
*/
|
||||
MemorySegment generateMemorySegmentFromXml(const QDomElement& xmlElement) const;
|
||||
RegisterGroup generateRegisterGroupFromXml(const QDomElement& xmlElement) const;
|
||||
|
||||
Register generateRegisterFromXml(const QDomElement& xmlElement) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Will construct a PartDescription instance from the XML of a part description file, the path to which
|
||||
* is given via xmlFilePath.
|
||||
*
|
||||
* @param xmlFilePath
|
||||
*/
|
||||
PartDescriptionFile(const QString& xmlFilePath) {
|
||||
this->init(xmlFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will construct a PartDescription instance from pre-loaded XML.
|
||||
*
|
||||
* @param xml
|
||||
*/
|
||||
PartDescriptionFile(const QDomDocument& xml) {
|
||||
this->init(xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will resolve the part description file using the part description JSON mapping and a given target signature.
|
||||
*
|
||||
* @param targetSignatureHex
|
||||
* @param targetName
|
||||
*/
|
||||
PartDescriptionFile(const std::string& targetSignatureHex, std::optional<std::string> targetName);
|
||||
|
||||
/**
|
||||
* Loads the AVR8 target description JSON mapping file.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static QJsonObject getPartDescriptionMapping();
|
||||
|
||||
std::string getTargetName() const;
|
||||
|
||||
|
||||
/**
|
||||
* Extracts the AVR8 target signature from the part description XML.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TargetSignature getTargetSignature() const;
|
||||
|
||||
/**
|
||||
* Extracts all address spaces for the AVR8 target, from the part description XML.
|
||||
*
|
||||
* Will return a mapping of the extracted address spaces, mapped by id.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::map<std::string, AddressSpace> getAddressSpacesMappedById() const;
|
||||
|
||||
/**
|
||||
* Extracts the AVR8 target family from the part description XML.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Family getFamily() const;
|
||||
|
||||
const std::map<std::string, PropertyGroup>& getPropertyGroupsMappedByName() const;
|
||||
const std::map<std::string, Module>& getModulesMappedByName() const;
|
||||
const std::map<std::string, Module>& getPeripheralModulesMappedByName() const;
|
||||
|
||||
std::optional<MemorySegment> getFlashMemorySegment() const;
|
||||
std::optional<MemorySegment> getRamMemorySegment() const;
|
||||
std::optional<MemorySegment> getRegisterMemorySegment() const;
|
||||
std::optional<MemorySegment> getEepromMemorySegment() const;
|
||||
std::optional<MemorySegment> getFirstBootSectionMemorySegment() const;
|
||||
std::optional<RegisterGroup> getCpuRegisterGroup() const;
|
||||
std::optional<RegisterGroup> getEepromRegisterGroup() const;
|
||||
std::optional<Register> getStatusRegister() const;
|
||||
std::optional<Register> getStackPointerRegister() const;
|
||||
std::optional<Register> getStackPointerHighRegister() const;
|
||||
std::optional<Register> getStackPointerLowRegister() const;
|
||||
std::optional<Register> getOscillatorCalibrationRegister() const;
|
||||
std::optional<Register> getSpmcsrRegister() const;
|
||||
std::optional<Register> getEepromAddressRegister() const;
|
||||
std::optional<Register> getEepromDataRegister() const;
|
||||
std::optional<Register> getEepromControlRegister() const;
|
||||
std::vector<Variant> getVariants() const;
|
||||
const std::map<std::string, Pinout>& getPinoutsMappedByName() const;
|
||||
};
|
||||
}
|
||||
17
src/Targets/Microchip/AVR/AVR8/PartDescription/Pinout.hpp
Normal file
17
src/Targets/Microchip/AVR/AVR8/PartDescription/Pinout.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct Pin {
|
||||
std::string pad;
|
||||
int position;
|
||||
};
|
||||
|
||||
struct Pinout {
|
||||
std::string name;
|
||||
std::vector<Pin> pins;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct Property {
|
||||
std::string name;
|
||||
|
||||
/*
|
||||
* We use QString here as we're dealing with arbitrary values and QString provides many helpful
|
||||
* functions to make this easier.
|
||||
*/
|
||||
QString value;
|
||||
};
|
||||
|
||||
struct PropertyGroup {
|
||||
std::string name;
|
||||
std::map<std::string, Property> propertiesMappedByName;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct Register {
|
||||
std::string name;
|
||||
std::uint16_t offset;
|
||||
std::uint16_t size;
|
||||
};
|
||||
|
||||
struct RegisterGroup {
|
||||
std::string name;
|
||||
std::optional<std::uint16_t> offset;
|
||||
std::map<std::string, Register> registersMappedByName;
|
||||
};
|
||||
}
|
||||
14
src/Targets/Microchip/AVR/AVR8/PartDescription/Signal.hpp
Normal file
14
src/Targets/Microchip/AVR/AVR8/PartDescription/Signal.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct Signal {
|
||||
std::string padName;
|
||||
std::string function;
|
||||
std::optional<int> index;
|
||||
std::string group;
|
||||
};
|
||||
}
|
||||
13
src/Targets/Microchip/AVR/AVR8/PartDescription/Variant.hpp
Normal file
13
src/Targets/Microchip/AVR/AVR8/PartDescription/Variant.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit::PartDescription
|
||||
{
|
||||
struct Variant {
|
||||
std::string orderCode;
|
||||
std::string pinoutName;
|
||||
std::string package;
|
||||
bool disabled = false;
|
||||
};
|
||||
}
|
||||
54
src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp
Normal file
54
src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/PartDescription/AddressSpace.hpp"
|
||||
#include "../TargetSignature.hpp"
|
||||
#include "Family.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
{
|
||||
struct TargetParameters
|
||||
{
|
||||
std::optional<Family> family;
|
||||
|
||||
std::optional<std::uint32_t> bootSectionStartAddress;
|
||||
std::optional<std::uint32_t> gpRegisterStartAddress;
|
||||
std::optional<std::uint32_t> gpRegisterSize;
|
||||
std::optional<std::uint16_t> flashPageSize;
|
||||
std::optional<std::uint32_t> flashSize;
|
||||
std::optional<std::uint32_t> flashStartAddress;
|
||||
std::optional<std::uint16_t> ramStartAddress;
|
||||
std::optional<std::uint32_t> ramSize;
|
||||
std::optional<std::uint16_t> eepromSize;
|
||||
std::optional<std::uint16_t> eepromPageSize;
|
||||
std::optional<std::uint8_t> eepromAddressRegisterHigh;
|
||||
std::optional<std::uint8_t> eepromAddressRegisterLow;
|
||||
std::optional<std::uint8_t> eepromDataRegisterAddress;
|
||||
std::optional<std::uint8_t> eepromControlRegisterAddress;
|
||||
std::optional<std::uint8_t> ocdRevision;
|
||||
std::optional<std::uint8_t> ocdDataRegister;
|
||||
std::optional<std::uint16_t> statusRegisterStartAddress;
|
||||
std::optional<std::uint16_t> statusRegisterSize;
|
||||
std::optional<std::uint16_t> stackPointerRegisterStartAddress;
|
||||
std::optional<std::uint16_t> stackPointerRegisterSize;
|
||||
std::optional<std::uint8_t> spmcsRegisterStartAddress;
|
||||
std::optional<std::uint8_t> osccalAddress;
|
||||
|
||||
// XMega/PDI specific target params
|
||||
std::optional<std::uint32_t> appSectionPdiOffset;
|
||||
std::optional<std::uint16_t> bootSectionSize;
|
||||
std::optional<std::uint32_t> bootSectionPdiOffset;
|
||||
std::optional<std::uint32_t> eepromPdiOffset;
|
||||
std::optional<std::uint32_t> ramPdiOffset;
|
||||
std::optional<std::uint32_t> fuseRegistersPdiOffset;
|
||||
std::optional<std::uint32_t> lockRegistersPdiOffset;
|
||||
std::optional<std::uint32_t> userSignaturesPdiOffset;
|
||||
std::optional<std::uint32_t> productSignaturesPdiOffset;
|
||||
|
||||
|
||||
std::optional<std::uint32_t> ioPortAddressRangeStart;
|
||||
std::optional<std::uint32_t> ioPortAddressRangeEnd;
|
||||
};
|
||||
}
|
||||
16
src/Targets/Microchip/AVR/AVR8/Tiny/Tiny.hpp
Normal file
16
src/Targets/Microchip/AVR/AVR8/Tiny/Tiny.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Avr8.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
{
|
||||
class Tiny: public Avr8
|
||||
{
|
||||
public:
|
||||
Tiny(const Avr8& avr8) : Avr8(avr8) {};
|
||||
|
||||
virtual bool supportsPromotion() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
16
src/Targets/Microchip/AVR/AVR8/XMega/XMega.hpp
Normal file
16
src/Targets/Microchip/AVR/AVR8/XMega/XMega.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Avr8.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
{
|
||||
class XMega: public Avr8
|
||||
{
|
||||
public:
|
||||
XMega(const Avr8& avr8) : Avr8(avr8) {};
|
||||
|
||||
virtual bool supportsPromotion() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user