OCDEN fuse bit management
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Services/PathService.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
#include "src/Exceptions/InvalidConfig.hpp"
|
||||
#include "Exceptions/DebugWirePhysicalInterfaceError.hpp"
|
||||
@@ -92,7 +93,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
}
|
||||
|
||||
if (
|
||||
this->targetConfig->manageDwenFuseBit && this->avrIspInterface == nullptr
|
||||
this->targetConfig->manageDwenFuseBit
|
||||
&& this->avrIspInterface == nullptr
|
||||
&& this->targetConfig->physicalInterface == PhysicalInterface::DEBUG_WIRE
|
||||
) {
|
||||
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) {
|
||||
this->avrIspInterface->configure(targetConfig);
|
||||
this->avrIspInterface->configure(*(this->targetConfig));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,13 +222,34 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
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->avr8DebugInterface->reset();
|
||||
}
|
||||
|
||||
void Avr8::deactivate() {
|
||||
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;
|
||||
|
||||
} 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) {
|
||||
return this->targetParameters->bootSectionStartAddress.has_value()
|
||||
&& address >= this->targetParameters->bootSectionStartAddress.value()
|
||||
|
||||
@@ -183,6 +183,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -53,5 +53,9 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
||||
if (targetNode["targetPowerCycleDelay"]) {
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
private:
|
||||
|
||||
@@ -394,6 +394,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
||||
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() {
|
||||
auto interfaceNamesToInterfaces = std::map<std::string, PhysicalInterface>({
|
||||
{"updi", PhysicalInterface::UPDI},
|
||||
@@ -665,6 +673,24 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
||||
std::optional<FuseBitsDescriptor> TargetDescriptionFile::getFuseBitsDescriptorByName(
|
||||
const std::string& fuseBitName
|
||||
) 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");
|
||||
|
||||
if (fuseModuleIt == this->modulesMappedByName.end()) {
|
||||
@@ -687,7 +713,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
||||
{"extended", FuseType::EXTENDED},
|
||||
});
|
||||
|
||||
for (const auto&[fuseTypeName, fuse] : fuseRegisterGroup.registersMappedByName) {
|
||||
|
||||
for (const auto& [fuseTypeName, fuse] : fuseRegisterGroup.registersMappedByName) {
|
||||
const auto fuseTypeIt = fuseTypesByName.find(fuseTypeName);
|
||||
if (fuseTypeIt == fuseTypesByName.end()) {
|
||||
// Unknown fuse type name
|
||||
@@ -698,6 +725,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
||||
|
||||
if (fuseBitFieldIt != fuse.bitFieldsMappedByName.end()) {
|
||||
return FuseBitsDescriptor(
|
||||
fuseAddressOffset + fuse.offset,
|
||||
fuseTypeIt->second,
|
||||
fuseBitFieldIt->second.mask
|
||||
);
|
||||
|
||||
@@ -102,6 +102,24 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
|
||||
*/
|
||||
[[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.
|
||||
*
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace Bloom::Targets::Microchip::Avr
|
||||
{
|
||||
enum class FuseType: std::uint8_t
|
||||
@@ -24,6 +26,8 @@ namespace Bloom::Targets::Microchip::Avr
|
||||
|
||||
struct FuseBitsDescriptor
|
||||
{
|
||||
TargetMemoryAddress byteAddress;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
FuseBitsDescriptor(FuseType fuseType, std::uint8_t bitMask)
|
||||
: fuseType(fuseType)
|
||||
FuseBitsDescriptor(TargetMemoryAddress byteAddress, FuseType fuseType, std::uint8_t bitMask)
|
||||
: byteAddress(byteAddress)
|
||||
, fuseType(fuseType)
|
||||
, bitMask(bitMask)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Bloom::Targets
|
||||
FLASH,
|
||||
RAM,
|
||||
EEPROM,
|
||||
FUSES,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user