OCDEN fuse bit management
This commit is contained in:
@@ -182,6 +182,13 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
* to access general purpose registers when other variants are in use.
|
* to access general purpose registers when other variants are in use.
|
||||||
*/
|
*/
|
||||||
REGISTER_FILE = 0xB8,
|
REGISTER_FILE = 0xB8,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FUSES memory type can be used to read and write AVR fuses in programming mode.
|
||||||
|
*
|
||||||
|
* Not available for the debugWire config variant.
|
||||||
|
*/
|
||||||
|
FUSES = 0xB2,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Avr8ResponseId: unsigned char
|
enum class Avr8ResponseId: unsigned char
|
||||||
|
|||||||
@@ -305,10 +305,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->stop();
|
|
||||||
this->clearAllBreakpoints();
|
|
||||||
this->run();
|
|
||||||
|
|
||||||
this->detach();
|
this->detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,6 +660,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
// EEPROM addresses should be in relative form, for XMEGA (PDI) targets
|
// EEPROM addresses should be in relative form, for XMEGA (PDI) targets
|
||||||
startAddress -= this->targetParameters.eepromStartAddress.value();
|
startAddress -= this->targetParameters.eepromStartAddress.value();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TargetMemoryType::FUSES: {
|
||||||
|
avr8MemoryType = Avr8MemoryType::FUSES;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
@@ -759,6 +760,11 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TargetMemoryType::FUSES: {
|
||||||
|
avr8MemoryType = Avr8MemoryType::FUSES;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
@@ -881,14 +887,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->programmingModeEnabled = false;
|
this->programmingModeEnabled = false;
|
||||||
|
|
||||||
if (this->configVariant == Avr8ConfigVariant::MEGAJTAG) {
|
|
||||||
this->deactivatePhysical();
|
|
||||||
this->setTargetParameters(this->targetParameters);
|
|
||||||
this->targetAttached = false;
|
|
||||||
this->activate();
|
|
||||||
this->stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<Family, std::map<PhysicalInterface, Avr8ConfigVariant>>
|
std::map<Family, std::map<PhysicalInterface, Avr8ConfigVariant>>
|
||||||
@@ -1687,6 +1685,12 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
TargetMemorySize bytes,
|
TargetMemorySize bytes,
|
||||||
const std::set<TargetMemoryAddress>& excludedAddresses
|
const std::set<TargetMemoryAddress>& excludedAddresses
|
||||||
) {
|
) {
|
||||||
|
if (type == Avr8MemoryType::FUSES) {
|
||||||
|
if (this->configVariant == Avr8ConfigVariant::DEBUG_WIRE) {
|
||||||
|
throw Exception("Cannot access AVR fuses via the debugWire interface");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!excludedAddresses.empty() && (this->avoidMaskedMemoryRead || type != Avr8MemoryType::SRAM)) {
|
if (!excludedAddresses.empty() && (this->avoidMaskedMemoryRead || type != Avr8MemoryType::SRAM)) {
|
||||||
/*
|
/*
|
||||||
* Driver-side masked memory read.
|
* Driver-side masked memory read.
|
||||||
@@ -1805,6 +1809,12 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
TargetMemoryAddress startAddress,
|
TargetMemoryAddress startAddress,
|
||||||
const TargetMemoryBuffer& buffer
|
const TargetMemoryBuffer& buffer
|
||||||
) {
|
) {
|
||||||
|
if (type == Avr8MemoryType::FUSES) {
|
||||||
|
if (this->configVariant == Avr8ConfigVariant::DEBUG_WIRE) {
|
||||||
|
throw Exception("Cannot access AVR fuses via the debugWire interface");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const auto bytes = static_cast<TargetMemorySize>(buffer.size());
|
const auto bytes = static_cast<TargetMemorySize>(buffer.size());
|
||||||
|
|
||||||
if (this->alignmentRequired(type)) {
|
if (this->alignmentRequired(type)) {
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ namespace Bloom::DebugToolDrivers::TargetInterfaces::Microchip::Avr
|
|||||||
/**
|
/**
|
||||||
* Many AVRs can be programmed via an SPI interface. Some debug tools provide access to this interface via the AVR
|
* Many AVRs can be programmed via an SPI interface. Some debug tools provide access to this interface via the AVR
|
||||||
* In-System Programming (ISP) protocol.
|
* In-System Programming (ISP) protocol.
|
||||||
|
*
|
||||||
|
* This interface class is incomplete - it only provides the ability to read the device ID and access AVR fuses and
|
||||||
|
* lockbit bytes (as that's all we need, for now).
|
||||||
|
*
|
||||||
|
* Currently, Bloom only uses the ISP interface for accessing fuses and lockbits on debugWire targets. We can't
|
||||||
|
* access fuses via the debugWire interface, so we have to use the ISP interface.
|
||||||
|
*
|
||||||
|
* @see Avr8::updateDwenFuseBit() for more.
|
||||||
*/
|
*/
|
||||||
class AvrIspInterface
|
class AvrIspInterface
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/Services/PathService.hpp"
|
#include "src/Services/PathService.hpp"
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
|
|
||||||
#include "src/Exceptions/InvalidConfig.hpp"
|
#include "src/Exceptions/InvalidConfig.hpp"
|
||||||
#include "Exceptions/DebugWirePhysicalInterfaceError.hpp"
|
#include "Exceptions/DebugWirePhysicalInterfaceError.hpp"
|
||||||
@@ -92,7 +93,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this->targetConfig->manageDwenFuseBit && this->avrIspInterface == nullptr
|
this->targetConfig->manageDwenFuseBit
|
||||||
|
&& this->avrIspInterface == nullptr
|
||||||
&& this->targetConfig->physicalInterface == PhysicalInterface::DEBUG_WIRE
|
&& this->targetConfig->physicalInterface == PhysicalInterface::DEBUG_WIRE
|
||||||
) {
|
) {
|
||||||
Logger::warning(
|
Logger::warning(
|
||||||
@@ -101,10 +103,19 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->avr8DebugInterface->configure(this->targetConfig.value());
|
if (
|
||||||
|
this->targetConfig->manageOcdenFuseBit
|
||||||
|
&& this->targetConfig->physicalInterface != PhysicalInterface::JTAG
|
||||||
|
) {
|
||||||
|
Logger::warning(
|
||||||
|
"The 'manageOcdenFuseBit' parameter only applies to JTAG targets. It will be ignored in this session."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->avr8DebugInterface->configure(*(this->targetConfig));
|
||||||
|
|
||||||
if (this->avrIspInterface != nullptr) {
|
if (this->avrIspInterface != nullptr) {
|
||||||
this->avrIspInterface->configure(targetConfig);
|
this->avrIspInterface->configure(*(this->targetConfig));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,13 +222,34 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
this->avr8DebugInterface->activate();
|
this->avr8DebugInterface->activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this->targetConfig->physicalInterface == PhysicalInterface::JTAG
|
||||||
|
&& this->targetConfig->manageOcdenFuseBit
|
||||||
|
) {
|
||||||
|
Logger::debug("Attempting OCDEN fuse bit management");
|
||||||
|
this->updateOcdenFuseBit(true);
|
||||||
|
}
|
||||||
|
|
||||||
this->activated = true;
|
this->activated = true;
|
||||||
this->avr8DebugInterface->reset();
|
this->avr8DebugInterface->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avr8::deactivate() {
|
void Avr8::deactivate() {
|
||||||
try {
|
try {
|
||||||
this->avr8DebugInterface->deactivate();
|
this->stop();
|
||||||
|
this->clearAllBreakpoints();
|
||||||
|
|
||||||
|
if (
|
||||||
|
this->targetConfig->physicalInterface == PhysicalInterface::JTAG
|
||||||
|
&& this->targetConfig->manageOcdenFuseBit
|
||||||
|
) {
|
||||||
|
Logger::debug("Attempting OCDEN fuse bit management");
|
||||||
|
this->updateOcdenFuseBit(false);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this->avr8DebugInterface->deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
this->activated = false;
|
this->activated = false;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
@@ -946,6 +978,110 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avr8::updateOcdenFuseBit(bool enable) {
|
||||||
|
using Services::PathService;
|
||||||
|
using Services::StringService;
|
||||||
|
|
||||||
|
if (!this->targetDescriptionFile.has_value() || !this->id.has_value()) {
|
||||||
|
throw Exception(
|
||||||
|
"Insufficient target information for managing OCDEN fuse bit - do not use the generic \"avr8\" "
|
||||||
|
"target name in conjunction with the \"manageOcdenFuseBit\" function. Please update your target "
|
||||||
|
"configuration."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->supportedPhysicalInterfaces.contains(PhysicalInterface::JTAG)) {
|
||||||
|
throw Exception(
|
||||||
|
"Target does not support JTAG physical interface - check target configuration or "
|
||||||
|
"report this issue via " + PathService::homeDomainName() + "/report-issue"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ocdenFuseBitsDescriptor = this->targetDescriptionFile->getOcdenFuseBitsDescriptor();
|
||||||
|
const auto jtagenFuseBitsDescriptor = this->targetDescriptionFile->getJtagenFuseBitsDescriptor();
|
||||||
|
|
||||||
|
if (!ocdenFuseBitsDescriptor.has_value()) {
|
||||||
|
throw Exception("Could not find OCDEN bit field in TDF.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jtagenFuseBitsDescriptor.has_value()) {
|
||||||
|
throw Exception("Could not find JTAGEN bit field in TDF.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this->enableProgrammingMode();
|
||||||
|
|
||||||
|
const auto ocdenFuseByteValue = this->avr8DebugInterface->readMemory(
|
||||||
|
TargetMemoryType::FUSES,
|
||||||
|
ocdenFuseBitsDescriptor->byteAddress,
|
||||||
|
1
|
||||||
|
).at(0);
|
||||||
|
const auto jtagenFuseByteValue = jtagenFuseBitsDescriptor->byteAddress == ocdenFuseBitsDescriptor->byteAddress
|
||||||
|
? ocdenFuseByteValue
|
||||||
|
: this->avr8DebugInterface->readMemory(
|
||||||
|
TargetMemoryType::FUSES,
|
||||||
|
jtagenFuseBitsDescriptor->byteAddress,
|
||||||
|
1
|
||||||
|
).at(0)
|
||||||
|
;
|
||||||
|
|
||||||
|
Logger::debug("OCDEN fuse byte value (before update): 0x" + StringService::toHex(ocdenFuseByteValue));
|
||||||
|
|
||||||
|
if ((jtagenFuseByteValue & jtagenFuseBitsDescriptor->bitMask) != 0) {
|
||||||
|
/*
|
||||||
|
* If we get here, something has gone wrong. The JTAGEN fuse should always be programmed by this point.
|
||||||
|
* We wouldn't have been able to activate the JTAG physical interface if the fuse wasn't programmed.
|
||||||
|
*
|
||||||
|
* This means the data we have on the JTAGEN fuse bit, from the TDF, is likely incorrect. And if that's
|
||||||
|
* the case, we cannot rely on the data for the OCDEN fuse bit being any better.
|
||||||
|
*/
|
||||||
|
throw Exception(
|
||||||
|
"Invalid JTAGEN fuse bit value - suspected inaccuracies in TDF data. Please report this to "
|
||||||
|
"Bloom developers as a matter of urgency, via " + PathService::homeDomainName() + "/report-issue"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!static_cast<bool>(ocdenFuseByteValue & ocdenFuseBitsDescriptor->bitMask) == enable) {
|
||||||
|
Logger::debug("OCDEN fuse bit already set to desired value - aborting update operation");
|
||||||
|
|
||||||
|
this->disableProgrammingMode();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto newValue = (enable)
|
||||||
|
? static_cast<unsigned char>(ocdenFuseByteValue & ~(ocdenFuseBitsDescriptor->bitMask))
|
||||||
|
: static_cast<unsigned char>(ocdenFuseByteValue | ocdenFuseBitsDescriptor->bitMask);
|
||||||
|
|
||||||
|
Logger::debug("New OCDEN fuse byte value (to be written): 0x" + StringService::toHex(newValue));
|
||||||
|
|
||||||
|
Logger::warning("Updating OCDEN fuse bit");
|
||||||
|
this->avr8DebugInterface->writeMemory(
|
||||||
|
TargetMemoryType::FUSES,
|
||||||
|
ocdenFuseBitsDescriptor->byteAddress,
|
||||||
|
{newValue}
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger::debug("Verifying OCDEN fuse bit");
|
||||||
|
const auto postUpdateOcdenByteValue = this->avr8DebugInterface->readMemory(
|
||||||
|
TargetMemoryType::FUSES,
|
||||||
|
ocdenFuseBitsDescriptor->byteAddress,
|
||||||
|
1
|
||||||
|
).at(0);
|
||||||
|
|
||||||
|
if (postUpdateOcdenByteValue != newValue) {
|
||||||
|
throw Exception("Failed to update OCDEN fuse bit - post-update verification failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info("OCDEN fuse bit updated");
|
||||||
|
|
||||||
|
this->disableProgrammingMode();
|
||||||
|
|
||||||
|
} catch (const Exception& exception) {
|
||||||
|
this->disableProgrammingMode();
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ProgramMemorySection Avr8::getProgramMemorySectionFromAddress(std::uint32_t address) {
|
ProgramMemorySection Avr8::getProgramMemorySectionFromAddress(std::uint32_t address) {
|
||||||
return this->targetParameters->bootSectionStartAddress.has_value()
|
return this->targetParameters->bootSectionStartAddress.has_value()
|
||||||
&& address >= this->targetParameters->bootSectionStartAddress.value()
|
&& address >= this->targetParameters->bootSectionStartAddress.value()
|
||||||
|
|||||||
@@ -183,6 +183,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
*/
|
*/
|
||||||
void updateDwenFuseBit(bool enable);
|
void updateDwenFuseBit(bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the On-chip debug enable (OCDEN) fuse bit on the AVR target.
|
||||||
|
*
|
||||||
|
* @param enable
|
||||||
|
* True to enable the fuse, false to disable it.
|
||||||
|
*/
|
||||||
|
void updateOcdenFuseBit(bool enable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the program memory section from a program memory address.
|
* Resolves the program memory section from a program memory address.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -53,5 +53,9 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
if (targetNode["targetPowerCycleDelay"]) {
|
if (targetNode["targetPowerCycleDelay"]) {
|
||||||
this->targetPowerCycleDelay = std::chrono::milliseconds(targetNode["targetPowerCycleDelay"].as<int>());
|
this->targetPowerCycleDelay = std::chrono::milliseconds(targetNode["targetPowerCycleDelay"].as<int>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetNode["manageOcdenFuseBit"]) {
|
||||||
|
this->manageOcdenFuseBit = targetNode["manageOcdenFuseBit"].as<bool>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,15 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
*/
|
*/
|
||||||
std::chrono::milliseconds targetPowerCycleDelay = std::chrono::milliseconds(250);
|
std::chrono::milliseconds targetPowerCycleDelay = std::chrono::milliseconds(250);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The manageOcdenFuseBit flag determines if Bloom should manage the OCDEN fuse but on JTAG-enabled AVR
|
||||||
|
* targets.
|
||||||
|
*
|
||||||
|
* This parameter is optional, and the function is disabled by default. Users must explicitly enable it in
|
||||||
|
* their target configuration.
|
||||||
|
*/
|
||||||
|
bool manageOcdenFuseBit = false;
|
||||||
|
|
||||||
explicit Avr8TargetConfig(const TargetConfig& targetConfig);
|
explicit Avr8TargetConfig(const TargetConfig& targetConfig);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -394,6 +394,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
|||||||
return this->getFuseBitsDescriptorByName("spien");
|
return this->getFuseBitsDescriptorByName("spien");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<FuseBitsDescriptor> TargetDescriptionFile::getOcdenFuseBitsDescriptor() const {
|
||||||
|
return this->getFuseBitsDescriptorByName("ocden");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<FuseBitsDescriptor> TargetDescriptionFile::getJtagenFuseBitsDescriptor() const {
|
||||||
|
return this->getFuseBitsDescriptorByName("jtagen");
|
||||||
|
}
|
||||||
|
|
||||||
void TargetDescriptionFile::loadSupportedPhysicalInterfaces() {
|
void TargetDescriptionFile::loadSupportedPhysicalInterfaces() {
|
||||||
auto interfaceNamesToInterfaces = std::map<std::string, PhysicalInterface>({
|
auto interfaceNamesToInterfaces = std::map<std::string, PhysicalInterface>({
|
||||||
{"updi", PhysicalInterface::UPDI},
|
{"updi", PhysicalInterface::UPDI},
|
||||||
@@ -665,6 +673,24 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
|||||||
std::optional<FuseBitsDescriptor> TargetDescriptionFile::getFuseBitsDescriptorByName(
|
std::optional<FuseBitsDescriptor> TargetDescriptionFile::getFuseBitsDescriptorByName(
|
||||||
const std::string& fuseBitName
|
const std::string& fuseBitName
|
||||||
) const {
|
) const {
|
||||||
|
const auto& peripheralModules = this->getPeripheralModulesMappedByName();
|
||||||
|
std::uint32_t fuseAddressOffset = 0;
|
||||||
|
|
||||||
|
const auto fusePeripheralModuleIt = peripheralModules.find("fuse");
|
||||||
|
if (fusePeripheralModuleIt != peripheralModules.end()) {
|
||||||
|
const auto& fusePeripheralModule = fusePeripheralModuleIt->second;
|
||||||
|
|
||||||
|
const auto fuseInstanceIt = fusePeripheralModule.instancesMappedByName.find("fuse");
|
||||||
|
if (fuseInstanceIt != fusePeripheralModule.instancesMappedByName.end()) {
|
||||||
|
const auto& fuseInstance = fuseInstanceIt->second;
|
||||||
|
|
||||||
|
const auto fuseRegisterGroupIt = fuseInstance.registerGroupsMappedByName.find("fuse");
|
||||||
|
if (fuseRegisterGroupIt != fuseInstance.registerGroupsMappedByName.end()) {
|
||||||
|
fuseAddressOffset = fuseRegisterGroupIt->second.offset.value_or(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const auto fuseModuleIt = this->modulesMappedByName.find("fuse");
|
const auto fuseModuleIt = this->modulesMappedByName.find("fuse");
|
||||||
|
|
||||||
if (fuseModuleIt == this->modulesMappedByName.end()) {
|
if (fuseModuleIt == this->modulesMappedByName.end()) {
|
||||||
@@ -687,7 +713,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
|||||||
{"extended", FuseType::EXTENDED},
|
{"extended", FuseType::EXTENDED},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const auto&[fuseTypeName, fuse] : fuseRegisterGroup.registersMappedByName) {
|
|
||||||
|
for (const auto& [fuseTypeName, fuse] : fuseRegisterGroup.registersMappedByName) {
|
||||||
const auto fuseTypeIt = fuseTypesByName.find(fuseTypeName);
|
const auto fuseTypeIt = fuseTypesByName.find(fuseTypeName);
|
||||||
if (fuseTypeIt == fuseTypesByName.end()) {
|
if (fuseTypeIt == fuseTypesByName.end()) {
|
||||||
// Unknown fuse type name
|
// Unknown fuse type name
|
||||||
@@ -698,6 +725,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
|||||||
|
|
||||||
if (fuseBitFieldIt != fuse.bitFieldsMappedByName.end()) {
|
if (fuseBitFieldIt != fuse.bitFieldsMappedByName.end()) {
|
||||||
return FuseBitsDescriptor(
|
return FuseBitsDescriptor(
|
||||||
|
fuseAddressOffset + fuse.offset,
|
||||||
fuseTypeIt->second,
|
fuseTypeIt->second,
|
||||||
fuseBitFieldIt->second.mask
|
fuseBitFieldIt->second.mask
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -102,6 +102,24 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] std::optional<FuseBitsDescriptor> getSpienFuseBitsDescriptor() const;
|
[[nodiscard]] std::optional<FuseBitsDescriptor> getSpienFuseBitsDescriptor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a FuseBitDescriptor for the OCD enable (OCDEN) fuse bit, with information extracted from
|
||||||
|
* the TDF.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* std::nullopt if the OCDEN bit field could not be found in the TDF.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::optional<FuseBitsDescriptor> getOcdenFuseBitsDescriptor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a FuseBitDescriptor for the JTAG enable (JTAGEN) fuse bit, with information extracted from
|
||||||
|
* the TDF.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* std::nullopt if the JTAGEN bit field could not be found in the TDF.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::optional<FuseBitsDescriptor> getJtagenFuseBitsDescriptor() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a set of all supported physical interfaces for debugging.
|
* Returns a set of all supported physical interfaces for debugging.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
namespace Bloom::Targets::Microchip::Avr
|
namespace Bloom::Targets::Microchip::Avr
|
||||||
{
|
{
|
||||||
enum class FuseType: std::uint8_t
|
enum class FuseType: std::uint8_t
|
||||||
@@ -24,6 +26,8 @@ namespace Bloom::Targets::Microchip::Avr
|
|||||||
|
|
||||||
struct FuseBitsDescriptor
|
struct FuseBitsDescriptor
|
||||||
{
|
{
|
||||||
|
TargetMemoryAddress byteAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the fuse byte in which the fuse bits resides.
|
* The type of the fuse byte in which the fuse bits resides.
|
||||||
*/
|
*/
|
||||||
@@ -34,8 +38,9 @@ namespace Bloom::Targets::Microchip::Avr
|
|||||||
*/
|
*/
|
||||||
std::uint8_t bitMask;
|
std::uint8_t bitMask;
|
||||||
|
|
||||||
FuseBitsDescriptor(FuseType fuseType, std::uint8_t bitMask)
|
FuseBitsDescriptor(TargetMemoryAddress byteAddress, FuseType fuseType, std::uint8_t bitMask)
|
||||||
: fuseType(fuseType)
|
: byteAddress(byteAddress)
|
||||||
|
, fuseType(fuseType)
|
||||||
, bitMask(bitMask)
|
, bitMask(bitMask)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace Bloom::Targets
|
|||||||
FLASH,
|
FLASH,
|
||||||
RAM,
|
RAM,
|
||||||
EEPROM,
|
EEPROM,
|
||||||
|
FUSES,
|
||||||
OTHER,
|
OTHER,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user