2021-10-02 17:39:27 +01:00
|
|
|
#include "Avr8.hpp"
|
|
|
|
|
|
2021-04-04 21:04:12 +01:00
|
|
|
#include <cassert>
|
|
|
|
|
#include <bitset>
|
|
|
|
|
#include <limits>
|
2022-06-01 21:48:27 +01:00
|
|
|
#include <thread>
|
2022-08-04 21:07:16 +01:00
|
|
|
#include <algorithm>
|
2021-04-04 21:04:12 +01:00
|
|
|
|
|
|
|
|
#include "src/Logger/Logger.hpp"
|
2022-12-26 21:47:09 +00:00
|
|
|
#include "src/Services/PathService.hpp"
|
2023-05-07 16:49:45 +01:00
|
|
|
#include "src/Services/StringService.hpp"
|
2022-03-01 22:40:00 +00:00
|
|
|
|
2021-04-04 21:04:12 +01:00
|
|
|
#include "src/Exceptions/InvalidConfig.hpp"
|
2022-03-05 18:03:38 +00:00
|
|
|
#include "Exceptions/DebugWirePhysicalInterfaceError.hpp"
|
2021-04-04 21:04:12 +01:00
|
|
|
#include "src/Targets/TargetRegister.hpp"
|
|
|
|
|
|
2023-08-13 15:47:51 +01:00
|
|
|
namespace Targets::Microchip::Avr::Avr8Bit
|
2022-02-05 15:32:08 +00:00
|
|
|
{
|
|
|
|
|
using namespace Exceptions;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
Avr8::Avr8(const TargetConfig& targetConfig)
|
|
|
|
|
: targetConfig(Avr8TargetConfig(targetConfig))
|
|
|
|
|
, targetDescriptionFile(TargetDescription::TargetDescriptionFile(this->targetConfig.name))
|
|
|
|
|
, name(this->targetDescriptionFile.getTargetName())
|
|
|
|
|
, signature(this->targetDescriptionFile.getTargetSignature())
|
|
|
|
|
, family(this->targetDescriptionFile.getFamily())
|
|
|
|
|
, targetParameters(this->targetDescriptionFile.getTargetParameters())
|
|
|
|
|
, supportedPhysicalInterfaces(this->targetDescriptionFile.getSupportedPhysicalInterfaces())
|
|
|
|
|
, padDescriptorsByName(this->targetDescriptionFile.getPadDescriptorsMappedByName())
|
|
|
|
|
, targetVariantsById(this->targetDescriptionFile.getVariantsMappedById())
|
|
|
|
|
, stackPointerRegisterDescriptor(
|
|
|
|
|
TargetRegisterDescriptor(
|
|
|
|
|
TargetRegisterType::STACK_POINTER,
|
|
|
|
|
this->targetParameters.stackPointerRegisterLowAddress.value(),
|
|
|
|
|
this->targetParameters.stackPointerRegisterSize.value(),
|
|
|
|
|
TargetMemoryType::OTHER,
|
|
|
|
|
"SP",
|
|
|
|
|
"CPU",
|
|
|
|
|
"Stack Pointer Register",
|
|
|
|
|
TargetRegisterAccess(true, true)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
, statusRegisterDescriptor(
|
|
|
|
|
TargetRegisterDescriptor(
|
|
|
|
|
TargetRegisterType::STATUS_REGISTER,
|
|
|
|
|
this->targetParameters.statusRegisterStartAddress.value(),
|
|
|
|
|
this->targetParameters.statusRegisterSize.value(),
|
|
|
|
|
TargetMemoryType::OTHER,
|
|
|
|
|
"SREG",
|
|
|
|
|
"CPU",
|
|
|
|
|
"Status Register",
|
|
|
|
|
TargetRegisterAccess(true, true)
|
|
|
|
|
)
|
|
|
|
|
)
|
2023-05-26 22:36:43 +01:00
|
|
|
, fuseEnableStrategy(this->targetDescriptionFile.getFuseEnableStrategy().value_or(FuseEnableStrategy::CLEAR))
|
2023-05-21 21:08:25 +01:00
|
|
|
{
|
|
|
|
|
if (!this->supportedPhysicalInterfaces.contains(this->targetConfig.physicalInterface)) {
|
|
|
|
|
/*
|
|
|
|
|
* The user has selected a physical interface that does not appear to be supported by the selected
|
|
|
|
|
* target.
|
|
|
|
|
*
|
|
|
|
|
* Bloom's target description files provide a list of supported physical interfaces for each target
|
|
|
|
|
* (which is how this->supportedPhysicalInterfaces is populated), but it's possible that this list may
|
|
|
|
|
* be wrong/incomplete. For this reason, we don't throw an exception here. Instead, we just present the
|
|
|
|
|
* user with a warning and a list of physical interfaces known to be supported by their selected target.
|
|
|
|
|
*/
|
|
|
|
|
const auto physicalInterfaceNames = getPhysicalInterfaceNames();
|
|
|
|
|
|
|
|
|
|
const auto supportedPhysicalInterfaceList = std::accumulate(
|
|
|
|
|
this->supportedPhysicalInterfaces.begin(),
|
|
|
|
|
this->supportedPhysicalInterfaces.end(),
|
|
|
|
|
std::string(),
|
|
|
|
|
[&physicalInterfaceNames] (const std::string& string, PhysicalInterface physicalInterface) {
|
|
|
|
|
if (physicalInterface == PhysicalInterface::ISP) {
|
|
|
|
|
/*
|
|
|
|
|
* Don't include the ISP interface in the list of supported interfaces, as doing so may
|
|
|
|
|
* mislead the user into thinking the ISP interface can be used for debugging operations.
|
|
|
|
|
*/
|
|
|
|
|
return string;
|
2022-08-04 21:07:16 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
return string + "\n - " + physicalInterfaceNames.at(physicalInterface);
|
|
|
|
|
}
|
|
|
|
|
);
|
2022-03-01 22:40:00 +00:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
Logger::warning(
|
|
|
|
|
"\nThe selected target (" + this->name + ") does not support the selected physical interface ("
|
|
|
|
|
+ physicalInterfaceNames.at(this->targetConfig.physicalInterface) + "). Target activation "
|
|
|
|
|
"will likely fail. The target supports the following physical interfaces: \n"
|
|
|
|
|
+ supportedPhysicalInterfaceList + "\n\nFor physical interface configuration values, see "
|
|
|
|
|
+ Services::PathService::homeDomainName() + "/docs/configuration/avr8-physical-interfaces. \n\n"
|
|
|
|
|
+ "If this information is incorrect, please report this to Bloom developers via "
|
|
|
|
|
+ Services::PathService::homeDomainName() + "/report-issue.\n"
|
|
|
|
|
);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-07 16:49:45 +01:00
|
|
|
if (
|
2023-05-21 21:08:25 +01:00
|
|
|
this->targetConfig.manageOcdenFuseBit
|
|
|
|
|
&& this->targetConfig.physicalInterface != PhysicalInterface::JTAG
|
2023-05-07 16:49:45 +01:00
|
|
|
) {
|
|
|
|
|
Logger::warning(
|
|
|
|
|
"The 'manageOcdenFuseBit' parameter only applies to JTAG targets. It will be ignored in this session."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
this->loadTargetRegisterDescriptors();
|
|
|
|
|
this->loadTargetMemoryDescriptors();
|
2021-06-27 21:33:08 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
bool Avr8::supportsDebugTool(DebugTool* debugTool) {
|
|
|
|
|
return debugTool->getAvr8DebugInterface(
|
|
|
|
|
this->targetConfig,
|
|
|
|
|
this->family,
|
|
|
|
|
this->targetParameters,
|
|
|
|
|
this->targetRegisterDescriptorsById
|
|
|
|
|
) != nullptr;
|
|
|
|
|
}
|
2021-06-27 21:33:08 +01:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
void Avr8::setDebugTool(DebugTool* debugTool) {
|
|
|
|
|
this->targetPowerManagementInterface = debugTool->getTargetPowerManagementInterface();
|
|
|
|
|
this->avr8DebugInterface = debugTool->getAvr8DebugInterface(
|
|
|
|
|
this->targetConfig,
|
|
|
|
|
this->family,
|
|
|
|
|
this->targetParameters,
|
|
|
|
|
this->targetRegisterDescriptorsById
|
|
|
|
|
);
|
2022-02-05 15:32:08 +00:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
this->avrIspInterface = debugTool->getAvrIspInterface(
|
|
|
|
|
this->targetConfig
|
|
|
|
|
);
|
2021-06-27 21:33:08 +01:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
if (this->avrIspInterface != nullptr) {
|
|
|
|
|
this->avrIspInterface->configure(this->targetConfig);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2023-05-26 23:07:17 +01:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
this->targetConfig.manageDwenFuseBit
|
|
|
|
|
&& this->avrIspInterface == nullptr
|
|
|
|
|
&& this->targetConfig.physicalInterface == PhysicalInterface::DEBUG_WIRE
|
|
|
|
|
) {
|
|
|
|
|
Logger::warning(
|
|
|
|
|
"The connected debug tool (or associated driver) does not provide any ISP interface. "
|
|
|
|
|
"Bloom will be unable to manage the DWEN fuse bit."
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-06-27 21:33:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::activate() {
|
|
|
|
|
if (this->isActivated()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-06-27 21:33:08 +01:00
|
|
|
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->init();
|
2021-06-26 03:46:37 +01:00
|
|
|
|
2022-03-05 18:03:38 +00:00
|
|
|
try {
|
|
|
|
|
this->avr8DebugInterface->activate();
|
|
|
|
|
|
|
|
|
|
} catch (const Exceptions::DebugWirePhysicalInterfaceError& debugWireException) {
|
2022-10-09 13:10:30 +01:00
|
|
|
// We failed to activate the debugWire physical interface. DWEN fuse bit may need updating.
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
if (!this->targetConfig.manageDwenFuseBit) {
|
2022-03-13 20:45:52 +00:00
|
|
|
throw TargetOperationFailure(
|
|
|
|
|
"Failed to activate debugWire physical interface - check target connection and DWEN fuse "
|
2022-03-15 11:17:30 +00:00
|
|
|
"bit. Bloom can manage the DWEN fuse bit automatically. For instructions on enabling this "
|
2022-12-26 21:47:09 +00:00
|
|
|
"function, see " + Services::PathService::homeDomainName() + "/docs/debugging-avr-debugwire"
|
2022-03-13 20:45:52 +00:00
|
|
|
);
|
2022-03-05 18:03:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Logger::warning(
|
|
|
|
|
"Failed to activate the debugWire physical interface - attempting to access target via "
|
|
|
|
|
"the ISP interface, for DWEN fuse bit inspection."
|
|
|
|
|
);
|
2022-10-09 13:10:30 +01:00
|
|
|
this->updateDwenFuseBit(true);
|
2022-03-05 18:03:38 +00:00
|
|
|
|
2022-03-16 17:13:40 +00:00
|
|
|
// If the debug tool provides a TargetPowerManagementInterface, attempt to cycle the target power
|
2022-03-19 13:27:36 +00:00
|
|
|
if (
|
|
|
|
|
this->targetPowerManagementInterface != nullptr
|
2023-05-21 21:08:25 +01:00
|
|
|
&& this->targetConfig.cycleTargetPowerPostDwenUpdate
|
2022-03-19 13:27:36 +00:00
|
|
|
) {
|
2022-03-16 17:13:40 +00:00
|
|
|
Logger::info("Cycling target power");
|
2022-03-16 17:21:12 +00:00
|
|
|
|
2022-03-16 17:13:40 +00:00
|
|
|
Logger::debug("Disabling target power");
|
|
|
|
|
this->targetPowerManagementInterface->disableTargetPower();
|
2022-03-16 17:21:12 +00:00
|
|
|
|
2022-03-21 13:04:12 +00:00
|
|
|
Logger::debug(
|
2023-05-21 21:08:25 +01:00
|
|
|
"Holding power off for ~" + std::to_string(this->targetConfig.targetPowerCycleDelay.count())
|
2022-03-21 13:04:12 +00:00
|
|
|
+ " ms"
|
|
|
|
|
);
|
2023-05-21 21:08:25 +01:00
|
|
|
std::this_thread::sleep_for(this->targetConfig.targetPowerCycleDelay);
|
2022-03-16 17:21:12 +00:00
|
|
|
|
2022-03-16 17:13:40 +00:00
|
|
|
Logger::debug("Enabling target power");
|
|
|
|
|
this->targetPowerManagementInterface->enableTargetPower();
|
2022-03-21 13:05:02 +00:00
|
|
|
|
|
|
|
|
Logger::debug(
|
2023-05-21 21:08:25 +01:00
|
|
|
"Waiting ~" + std::to_string(this->targetConfig.targetPowerCycleDelay.count())
|
2022-03-21 13:05:02 +00:00
|
|
|
+ " ms for target power-up"
|
|
|
|
|
);
|
2023-05-21 21:08:25 +01:00
|
|
|
std::this_thread::sleep_for(this->targetConfig.targetPowerCycleDelay);
|
2022-03-16 17:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-05 18:03:38 +00:00
|
|
|
} catch (const Exception& exception) {
|
2022-03-13 20:46:38 +00:00
|
|
|
throw Exception(
|
2022-03-05 18:03:38 +00:00
|
|
|
"Failed to access/update DWEN fuse bit via ISP interface - " + exception.getMessage()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Logger::info("Retrying debugWire physical interface activation");
|
|
|
|
|
this->avr8DebugInterface->activate();
|
|
|
|
|
}
|
2022-03-05 18:05:31 +00:00
|
|
|
|
2023-05-07 16:49:45 +01:00
|
|
|
if (
|
2023-05-21 21:08:25 +01:00
|
|
|
this->targetConfig.physicalInterface == PhysicalInterface::JTAG
|
|
|
|
|
&& this->targetConfig.manageOcdenFuseBit
|
2023-05-07 16:49:45 +01:00
|
|
|
) {
|
|
|
|
|
Logger::debug("Attempting OCDEN fuse bit management");
|
|
|
|
|
this->updateOcdenFuseBit(true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-05 18:05:31 +00:00
|
|
|
this->activated = true;
|
2023-05-21 21:08:25 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Validate the target signature.
|
|
|
|
|
*
|
|
|
|
|
* The signature obtained from the device should match what we loaded from the target description file.
|
|
|
|
|
*/
|
|
|
|
|
const auto targetSignature = this->avr8DebugInterface->getDeviceId();
|
|
|
|
|
|
|
|
|
|
if (targetSignature != this->signature) {
|
|
|
|
|
throw Exception(
|
|
|
|
|
"Failed to validate connected target - target signature mismatch.\nThe target signature"
|
|
|
|
|
" (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\""
|
|
|
|
|
+ this->signature.toHex() + "\"). This will likely be due to an incorrect target name in the "
|
|
|
|
|
+ "configuration file (bloom.yaml)."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 19:58:44 +01:00
|
|
|
this->avr8DebugInterface->reset();
|
2021-06-26 03:46:37 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::deactivate() {
|
|
|
|
|
try {
|
2023-05-07 16:49:45 +01:00
|
|
|
this->stop();
|
|
|
|
|
this->clearAllBreakpoints();
|
|
|
|
|
|
|
|
|
|
if (
|
2023-05-21 21:08:25 +01:00
|
|
|
this->targetConfig.physicalInterface == PhysicalInterface::JTAG
|
|
|
|
|
&& this->targetConfig.manageOcdenFuseBit
|
2023-05-07 16:49:45 +01:00
|
|
|
) {
|
|
|
|
|
Logger::debug("Attempting OCDEN fuse bit management");
|
|
|
|
|
this->updateOcdenFuseBit(false);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
this->avr8DebugInterface->deactivate();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
this->activated = false;
|
2021-10-06 21:12:31 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
} catch (const Exception& exception) {
|
|
|
|
|
Logger::error("Failed to deactivate AVR8 target - " + exception.getMessage());
|
|
|
|
|
}
|
2021-10-06 21:12:31 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
TargetDescriptor Avr8::getDescriptor() {
|
|
|
|
|
auto descriptor = TargetDescriptor(
|
|
|
|
|
this->signature.toHex(),
|
|
|
|
|
this->name,
|
|
|
|
|
"Microchip",
|
|
|
|
|
this->targetMemoryDescriptorsByType,
|
|
|
|
|
this->targetRegisterDescriptorsById,
|
|
|
|
|
{},
|
|
|
|
|
Targets::TargetMemoryType::FLASH
|
|
|
|
|
);
|
2022-02-05 15:32:08 +00:00
|
|
|
|
|
|
|
|
std::transform(
|
|
|
|
|
this->targetVariantsById.begin(),
|
|
|
|
|
this->targetVariantsById.end(),
|
|
|
|
|
std::back_inserter(descriptor.variants),
|
|
|
|
|
[] (auto& variantToIdPair) {
|
|
|
|
|
return variantToIdPair.second;
|
|
|
|
|
}
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
return descriptor;
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2023-04-01 12:37:59 +01:00
|
|
|
void Avr8::run(std::optional<TargetMemoryAddress> toAddress) {
|
|
|
|
|
if (toAddress.has_value()) {
|
|
|
|
|
return this->avr8DebugInterface->runTo(*toAddress);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->run();
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::stop() {
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->stop();
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::step() {
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->step();
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::reset() {
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->reset();
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::setBreakpoint(std::uint32_t address) {
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->setBreakpoint(address);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::removeBreakpoint(std::uint32_t address) {
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->clearBreakpoint(address);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::clearAllBreakpoints() {
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->clearAllBreakpoints();
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::writeRegisters(TargetRegisters registers) {
|
2023-05-21 21:08:25 +01:00
|
|
|
this->avr8DebugInterface->writeRegisters(registers);
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
TargetRegisters Avr8::readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) {
|
|
|
|
|
return this->avr8DebugInterface->readRegisters(descriptorIds);
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
TargetMemoryBuffer Avr8::readMemory(
|
|
|
|
|
TargetMemoryType memoryType,
|
|
|
|
|
std::uint32_t startAddress,
|
|
|
|
|
std::uint32_t bytes,
|
|
|
|
|
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges
|
|
|
|
|
) {
|
2022-02-15 13:14:03 +00:00
|
|
|
return this->avr8DebugInterface->readMemory(memoryType, startAddress, bytes, excludedAddressRanges);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) {
|
2022-12-11 23:27:47 +00:00
|
|
|
if (memoryType == TargetMemoryType::FLASH && !this->programmingModeEnabled()) {
|
2023-05-28 21:27:01 +01:00
|
|
|
throw Exception("Attempted Flash memory write with no active programming session.");
|
2022-06-03 15:49:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->writeMemory(memoryType, startAddress, buffer);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-12-11 18:18:39 +00:00
|
|
|
void Avr8::eraseMemory(TargetMemoryType memoryType) {
|
|
|
|
|
if (memoryType == TargetMemoryType::FLASH) {
|
2023-05-21 21:08:25 +01:00
|
|
|
if (this->targetConfig.physicalInterface == PhysicalInterface::DEBUG_WIRE) {
|
2022-12-18 21:35:10 +00:00
|
|
|
// debugWire targets do not need to be erased
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
if (!this->programmingModeEnabled()) {
|
|
|
|
|
throw Exception("Attempted Flash memory erase with no active programming session.");
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 22:45:57 +01:00
|
|
|
/*
|
|
|
|
|
* For JTAG and UPDI targets, we must perform a chip erase. This means we could end up erasing EEPROM,
|
|
|
|
|
* unless the EESAVE fuse bit has been programmed.
|
|
|
|
|
*
|
2023-05-28 21:27:01 +01:00
|
|
|
* If configured to do so, we will ensure that the EESAVE fuse bit has been programmed before we perform
|
|
|
|
|
* the chip erase. The fuse will be restored to its original value at the end of the programming session.
|
2023-05-26 22:45:57 +01:00
|
|
|
*/
|
|
|
|
|
if (
|
|
|
|
|
this->targetConfig.physicalInterface == PhysicalInterface::JTAG
|
|
|
|
|
|| this->targetConfig.physicalInterface == PhysicalInterface::UPDI
|
|
|
|
|
) {
|
2023-05-28 21:27:01 +01:00
|
|
|
if (this->targetConfig.preserveEeprom) {
|
|
|
|
|
Logger::debug("Inspecting EESAVE fuse bit");
|
|
|
|
|
this->activeProgrammingSession->managingEesaveFuseBit = this->updateEesaveFuseBit(true);
|
2023-05-28 21:27:26 +01:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
Logger::warning(
|
|
|
|
|
"Performing chip-erase with preserveEeprom disabled. All EEPROM data will be lost!"
|
|
|
|
|
);
|
2023-05-28 21:27:01 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-26 22:45:57 +01:00
|
|
|
return this->avr8DebugInterface->eraseChip();
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-11 18:18:39 +00:00
|
|
|
return this->avr8DebugInterface->eraseProgramMemory();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Debug tools do not have to support the erasing of RAM or EEPROM memory. We just implement this as a
|
|
|
|
|
* write operation.
|
|
|
|
|
*/
|
|
|
|
|
this->writeMemory(
|
|
|
|
|
memoryType,
|
|
|
|
|
memoryType == TargetMemoryType::RAM
|
2023-05-21 21:08:25 +01:00
|
|
|
? this->targetParameters.ramStartAddress.value()
|
|
|
|
|
: this->targetParameters.eepromStartAddress.value(),
|
2022-12-11 18:18:39 +00:00
|
|
|
TargetMemoryBuffer(
|
|
|
|
|
memoryType == TargetMemoryType::RAM
|
2023-05-21 21:08:25 +01:00
|
|
|
? this->targetParameters.ramSize.value()
|
|
|
|
|
: this->targetParameters.eepromSize.value(),
|
2022-12-11 18:18:39 +00:00
|
|
|
0xFF
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
TargetState Avr8::getState() {
|
2022-02-15 13:14:03 +00:00
|
|
|
return this->avr8DebugInterface->getTargetState();
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
std::uint32_t Avr8::getProgramCounter() {
|
2022-02-15 13:14:03 +00:00
|
|
|
return this->avr8DebugInterface->getProgramCounter();
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::setProgramCounter(std::uint32_t programCounter) {
|
2022-02-15 13:14:03 +00:00
|
|
|
this->avr8DebugInterface->setProgramCounter(programCounter);
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
std::uint32_t Avr8::getStackPointer() {
|
|
|
|
|
const auto stackPointerRegister = this->readRegisters(
|
2023-05-21 21:08:25 +01:00
|
|
|
{this->stackPointerRegisterDescriptor.id}
|
2022-02-05 15:32:08 +00:00
|
|
|
).front();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
std::uint32_t stackPointer = 0;
|
|
|
|
|
for (std::size_t i = 0; i < stackPointerRegister.size() && i < 4; i++) {
|
|
|
|
|
stackPointer = (stackPointer << (8 * i)) | stackPointerRegister.value[i];
|
|
|
|
|
}
|
2021-11-11 19:03:23 +00:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
return stackPointer;
|
2021-11-11 19:03:23 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
std::map<int, TargetPinState> Avr8::getPinStates(int variantId) {
|
2022-12-03 22:16:21 +00:00
|
|
|
const auto targetVariantIt = this->targetVariantsById.find(variantId);
|
|
|
|
|
|
|
|
|
|
if (targetVariantIt == this->targetVariantsById.end()) {
|
2022-02-05 15:32:08 +00:00
|
|
|
throw Exception("Invalid target variant ID");
|
|
|
|
|
}
|
2021-11-11 19:03:23 +00:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
std::map<int, TargetPinState> output;
|
2022-12-03 22:16:21 +00:00
|
|
|
const auto& variant = targetVariantIt->second;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2022-12-03 22:16:21 +00:00
|
|
|
const auto readMemoryBitset = [this, &cachedMemoryByStartAddress] (std::uint16_t startAddress) {
|
|
|
|
|
auto cachedByteIt = cachedMemoryByStartAddress.find(startAddress);
|
|
|
|
|
|
|
|
|
|
if (cachedByteIt == cachedMemoryByStartAddress.end()) {
|
|
|
|
|
cachedByteIt = cachedMemoryByStartAddress.insert(
|
2022-02-05 15:32:08 +00:00
|
|
|
std::pair(
|
|
|
|
|
startAddress,
|
|
|
|
|
this->readMemory(TargetMemoryType::RAM, startAddress, 1)
|
|
|
|
|
)
|
2022-12-03 22:16:21 +00:00
|
|
|
).first;
|
2022-02-05 15:32:08 +00:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
return std::bitset<std::numeric_limits<unsigned char>::digits>(
|
2022-12-03 22:16:21 +00:00
|
|
|
cachedByteIt->second.at(0)
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
|
|
|
|
};
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
for (const auto& [pinNumber, pinDescriptor] : variant.pinDescriptorsByNumber) {
|
2022-12-03 22:16:21 +00:00
|
|
|
const auto padIt = this->padDescriptorsByName.find(pinDescriptor.padName);
|
|
|
|
|
|
|
|
|
|
if (padIt != this->padDescriptorsByName.end()) {
|
|
|
|
|
const auto& pad = padIt->second;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!pad.gpioPinNumber.has_value()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
auto pinState = TargetPinState();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-06-22 22:23:00 +01:00
|
|
|
if (pad.gpioDdrAddress.has_value()) {
|
|
|
|
|
const auto ddrValue = readMemoryBitset(pad.gpioDdrAddress.value());
|
|
|
|
|
|
|
|
|
|
pinState.ioDirection = ddrValue.test(pad.gpioPinNumber.value()) ?
|
2022-02-05 15:32:08 +00:00
|
|
|
TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT
|
2022-06-22 22:23:00 +01:00
|
|
|
&& pad.gpioPortAddress.has_value()
|
2022-02-05 15:32:08 +00:00
|
|
|
) {
|
2022-06-22 22:23:00 +01:00
|
|
|
const auto portRegisterValueBitset = readMemoryBitset(pad.gpioPortAddress.value());
|
|
|
|
|
pinState.ioState = portRegisterValueBitset.test(pad.gpioPinNumber.value()) ?
|
2022-02-05 15:32:08 +00:00
|
|
|
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
} else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT
|
|
|
|
|
&& pad.gpioPortInputAddress.has_value()
|
2021-04-04 21:04:12 +01:00
|
|
|
) {
|
2022-06-22 22:23:00 +01:00
|
|
|
const auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value());
|
2022-02-05 15:32:08 +00:00
|
|
|
pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ?
|
|
|
|
|
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
output.insert(std::pair(pinNumber, pinState));
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
return output;
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::setPinState(const TargetPinDescriptor& pinDescriptor, const TargetPinState& state) {
|
2022-12-03 22:16:21 +00:00
|
|
|
const auto targetVariantIt = this->targetVariantsById.find(pinDescriptor.variantId);
|
|
|
|
|
|
|
|
|
|
if (targetVariantIt == this->targetVariantsById.end()) {
|
2022-02-05 15:32:08 +00:00
|
|
|
throw Exception("Invalid target variant ID");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-12-03 22:16:21 +00:00
|
|
|
const auto padDescriptorIt = this->padDescriptorsByName.find(pinDescriptor.padName);
|
|
|
|
|
|
|
|
|
|
if (padDescriptorIt == this->padDescriptorsByName.end()) {
|
2022-02-05 15:32:08 +00:00
|
|
|
throw Exception("Unknown pad");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (!state.ioDirection.has_value()) {
|
|
|
|
|
throw Exception("Missing IO direction state");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-12-03 22:16:21 +00:00
|
|
|
const auto& variant = targetVariantIt->second;
|
|
|
|
|
const auto& padDescriptor = padDescriptorIt->second;
|
2022-02-05 15:32:08 +00:00
|
|
|
auto ioState = state.ioState;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (state.ioDirection == TargetPinState::IoDirection::INPUT) {
|
2022-06-22 22:23:00 +01:00
|
|
|
// When setting the direction to INPUT, we must always set the IO pin state to LOW
|
2022-02-05 15:32:08 +00:00
|
|
|
ioState = TargetPinState::IoState::LOW;
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-05-23 23:50:10 +01:00
|
|
|
if (
|
2022-06-22 22:23:00 +01:00
|
|
|
!padDescriptor.gpioDdrAddress.has_value()
|
|
|
|
|
|| !padDescriptor.gpioPortAddress.has_value()
|
2022-02-05 15:32:08 +00:00
|
|
|
|| !padDescriptor.gpioPinNumber.has_value()
|
|
|
|
|
) {
|
|
|
|
|
throw Exception("Inadequate pad descriptor");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-06-22 22:23:00 +01:00
|
|
|
const auto pinNumber = padDescriptor.gpioPinNumber.value();
|
|
|
|
|
const auto ddrAddress = padDescriptor.gpioDdrAddress.value();
|
|
|
|
|
const auto ddrValue = this->readMemory(TargetMemoryType::RAM, ddrAddress, 1);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-06-22 22:23:00 +01:00
|
|
|
if (ddrValue.empty()) {
|
|
|
|
|
throw Exception("Failed to read DDR value");
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-22 22:23:00 +01:00
|
|
|
auto ddrValueBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrValue.front());
|
|
|
|
|
if (ddrValueBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) {
|
2022-02-05 15:32:08 +00:00
|
|
|
// DDR needs updating
|
2022-06-22 22:23:00 +01:00
|
|
|
ddrValueBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT));
|
2021-04-04 21:04:12 +01:00
|
|
|
|
|
|
|
|
this->writeMemory(
|
|
|
|
|
TargetMemoryType::RAM,
|
2022-06-22 22:23:00 +01:00
|
|
|
ddrAddress,
|
|
|
|
|
{static_cast<unsigned char>(ddrValueBitset.to_ulong())}
|
2021-04-04 21:04:12 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
if (ioState.has_value()) {
|
2022-06-22 22:23:00 +01:00
|
|
|
const auto portRegisterAddress = padDescriptor.gpioPortAddress.value();
|
|
|
|
|
const auto portRegisterValue = this->readMemory(TargetMemoryType::RAM, portRegisterAddress, 1);
|
2022-02-05 15:32:08 +00:00
|
|
|
|
2022-06-22 22:23:00 +01:00
|
|
|
if (portRegisterValue.empty()) {
|
|
|
|
|
throw Exception("Failed to read PORT register value");
|
2021-06-27 20:09:15 +01:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-06-22 22:23:00 +01:00
|
|
|
auto portRegisterValueBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(
|
|
|
|
|
portRegisterValue.front()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (portRegisterValueBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) {
|
|
|
|
|
// PORT set register needs updating
|
|
|
|
|
portRegisterValueBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH));
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
this->writeMemory(
|
|
|
|
|
TargetMemoryType::RAM,
|
2022-06-22 22:23:00 +01:00
|
|
|
portRegisterAddress,
|
|
|
|
|
{static_cast<unsigned char>(portRegisterValueBitset.to_ulong())}
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-10-06 21:12:31 +01:00
|
|
|
|
2022-05-15 17:42:02 +01:00
|
|
|
void Avr8::enableProgrammingMode() {
|
2023-05-26 22:45:57 +01:00
|
|
|
if (this->activeProgrammingSession.has_value()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-15 17:42:02 +01:00
|
|
|
this->avr8DebugInterface->enableProgrammingMode();
|
2023-05-26 22:45:57 +01:00
|
|
|
this->activeProgrammingSession = ProgrammingSession();
|
2022-05-15 17:42:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Avr8::disableProgrammingMode() {
|
2023-05-26 22:45:57 +01:00
|
|
|
if (!this->activeProgrammingSession.has_value()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this->activeProgrammingSession->managingEesaveFuseBit) {
|
|
|
|
|
this->updateEesaveFuseBit(false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-15 17:42:02 +01:00
|
|
|
this->avr8DebugInterface->disableProgrammingMode();
|
2023-05-26 22:45:57 +01:00
|
|
|
this->activeProgrammingSession.reset();
|
2022-05-15 17:42:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Avr8::programmingModeEnabled() {
|
2023-05-26 22:45:57 +01:00
|
|
|
return this->activeProgrammingSession.has_value();
|
2022-05-15 17:42:02 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::loadTargetRegisterDescriptors() {
|
2023-05-21 21:08:25 +01:00
|
|
|
this->targetRegisterDescriptorsById = this->targetDescriptionFile.getRegisterDescriptorsMappedById();
|
2021-10-06 21:12:31 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
/*
|
|
|
|
|
* All AVR8 targets possess 32 general purpose CPU registers. These are not described in the TDF, so we
|
|
|
|
|
* construct the descriptors for them here.
|
|
|
|
|
*/
|
2023-05-21 21:08:25 +01:00
|
|
|
const auto gpRegisterStartAddress = this->targetParameters.gpRegisterStartAddress.value_or(0);
|
2022-02-05 15:32:08 +00:00
|
|
|
for (std::uint8_t i = 0; i <= 31; i++) {
|
2023-05-21 21:08:25 +01:00
|
|
|
auto generalPurposeRegisterDescriptor = TargetRegisterDescriptor(
|
|
|
|
|
TargetRegisterType::GENERAL_PURPOSE_REGISTER,
|
|
|
|
|
gpRegisterStartAddress + i,
|
|
|
|
|
1,
|
|
|
|
|
TargetMemoryType::OTHER,
|
|
|
|
|
"r" + std::to_string(i),
|
|
|
|
|
"CPU General Purpose",
|
|
|
|
|
std::nullopt,
|
|
|
|
|
TargetRegisterAccess(true, true)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this->targetRegisterDescriptorsById.emplace(
|
|
|
|
|
generalPurposeRegisterDescriptor.id,
|
|
|
|
|
std::move(generalPurposeRegisterDescriptor)
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
|
|
|
|
}
|
2021-10-10 23:17:36 +01:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
this->targetRegisterDescriptorsById.emplace(
|
|
|
|
|
this->stackPointerRegisterDescriptor.id,
|
|
|
|
|
this->stackPointerRegisterDescriptor
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
2023-05-21 21:08:25 +01:00
|
|
|
this->targetRegisterDescriptorsById.emplace(
|
|
|
|
|
this->statusRegisterDescriptor.id,
|
|
|
|
|
this->statusRegisterDescriptor
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
|
|
|
|
}
|
2021-11-05 23:44:09 +00:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void Avr8::loadTargetMemoryDescriptors() {
|
2023-05-21 21:08:25 +01:00
|
|
|
const auto ramStartAddress = this->targetParameters.ramStartAddress.value();
|
|
|
|
|
const auto flashStartAddress = this->targetParameters.flashStartAddress.value();
|
2021-11-05 23:44:09 +00:00
|
|
|
|
|
|
|
|
this->targetMemoryDescriptorsByType.insert(std::pair(
|
2022-02-05 15:32:08 +00:00
|
|
|
TargetMemoryType::RAM,
|
2021-11-05 23:44:09 +00:00
|
|
|
TargetMemoryDescriptor(
|
2022-02-05 15:32:08 +00:00
|
|
|
TargetMemoryType::RAM,
|
2021-11-05 23:44:09 +00:00
|
|
|
TargetMemoryAddressRange(
|
2022-02-05 15:32:08 +00:00
|
|
|
ramStartAddress,
|
2023-05-21 21:08:25 +01:00
|
|
|
ramStartAddress + this->targetParameters.ramSize.value() - 1
|
2023-05-03 23:13:22 +01:00
|
|
|
),
|
|
|
|
|
TargetMemoryAccess(true, true, true)
|
2021-11-05 23:44:09 +00:00
|
|
|
)
|
|
|
|
|
));
|
2021-10-09 19:18:27 +01:00
|
|
|
|
2022-05-04 19:48:41 +01:00
|
|
|
this->targetMemoryDescriptorsByType.insert(std::pair(
|
|
|
|
|
TargetMemoryType::FLASH,
|
|
|
|
|
TargetMemoryDescriptor(
|
|
|
|
|
TargetMemoryType::FLASH,
|
|
|
|
|
TargetMemoryAddressRange(
|
|
|
|
|
flashStartAddress,
|
2023-05-21 21:08:25 +01:00
|
|
|
flashStartAddress + this->targetParameters.flashSize.value() - 1
|
2022-05-14 22:39:37 +01:00
|
|
|
),
|
2023-05-03 23:13:22 +01:00
|
|
|
TargetMemoryAccess(true, true, false),
|
2023-05-21 21:08:25 +01:00
|
|
|
this->targetParameters.flashPageSize
|
2022-05-04 19:48:41 +01:00
|
|
|
)
|
|
|
|
|
));
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
if (this->targetParameters.eepromStartAddress.has_value() && this->targetParameters.eepromSize.has_value()) {
|
|
|
|
|
const auto eepromStartAddress = this->targetParameters.eepromStartAddress.value();
|
2022-02-05 15:32:08 +00:00
|
|
|
|
|
|
|
|
this->targetMemoryDescriptorsByType.insert(std::pair(
|
|
|
|
|
TargetMemoryType::EEPROM,
|
|
|
|
|
TargetMemoryDescriptor(
|
|
|
|
|
TargetMemoryType::EEPROM,
|
|
|
|
|
TargetMemoryAddressRange(
|
|
|
|
|
eepromStartAddress,
|
2023-05-21 21:08:25 +01:00
|
|
|
eepromStartAddress + this->targetParameters.eepromSize.value() - 1
|
2023-05-03 23:13:22 +01:00
|
|
|
),
|
|
|
|
|
TargetMemoryAccess(true, true, true)
|
2022-02-05 15:32:08 +00:00
|
|
|
)
|
|
|
|
|
));
|
|
|
|
|
}
|
2021-10-06 21:12:31 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-26 22:36:43 +01:00
|
|
|
bool Avr8::isFuseEnabled(const FuseBitsDescriptor& descriptor, unsigned char fuseByteValue) const {
|
|
|
|
|
const auto programmedValue = static_cast<unsigned char>(
|
|
|
|
|
this->fuseEnableStrategy == FuseEnableStrategy::SET
|
|
|
|
|
? (0xFF & descriptor.bitMask)
|
|
|
|
|
: 0
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (fuseByteValue & descriptor.bitMask) == programmedValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned char Avr8::setFuseEnabled(
|
|
|
|
|
const FuseBitsDescriptor& descriptor,
|
|
|
|
|
unsigned char fuseByteValue,
|
|
|
|
|
bool enabled
|
|
|
|
|
) const {
|
|
|
|
|
return static_cast<unsigned char>(
|
|
|
|
|
this->fuseEnableStrategy == FuseEnableStrategy::SET
|
|
|
|
|
? enabled ? (fuseByteValue | descriptor.bitMask) : fuseByteValue & ~(descriptor.bitMask)
|
|
|
|
|
: enabled ? fuseByteValue & ~(descriptor.bitMask) : (fuseByteValue | descriptor.bitMask)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-09 13:10:30 +01:00
|
|
|
void Avr8::updateDwenFuseBit(bool enable) {
|
2022-03-05 18:03:00 +00:00
|
|
|
if (this->avrIspInterface == nullptr) {
|
|
|
|
|
throw Exception(
|
|
|
|
|
"Debug tool or driver does not provide access to an ISP interface - please confirm that the "
|
2022-12-26 21:47:09 +00:00
|
|
|
"debug tool supports ISP and then report this issue via " + Services::PathService::homeDomainName()
|
2022-03-05 18:03:00 +00:00
|
|
|
+ "/report-issue"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-04 21:06:13 +01:00
|
|
|
if (!this->supportedPhysicalInterfaces.contains(PhysicalInterface::DEBUG_WIRE)) {
|
2022-03-05 18:03:00 +00:00
|
|
|
throw Exception(
|
|
|
|
|
"Target does not support debugWire physical interface - check target configuration or "
|
2022-12-26 21:47:09 +00:00
|
|
|
"report this issue via " + Services::PathService::homeDomainName() + "/report-issue"
|
2022-03-05 18:03:00 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
const auto dwenFuseBitsDescriptor = this->targetDescriptionFile.getDwenFuseBitsDescriptor();
|
|
|
|
|
const auto spienFuseBitsDescriptor = this->targetDescriptionFile.getSpienFuseBitsDescriptor();
|
2022-03-05 18:03:00 +00:00
|
|
|
|
|
|
|
|
if (!dwenFuseBitsDescriptor.has_value()) {
|
|
|
|
|
throw Exception("Could not find DWEN bit field in TDF.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!spienFuseBitsDescriptor.has_value()) {
|
|
|
|
|
throw Exception("Could not find SPIEN bit field in TDF.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Logger::debug("Extracting ISP parameters from TDF");
|
2023-05-21 21:08:25 +01:00
|
|
|
this->avrIspInterface->setIspParameters(this->targetDescriptionFile.getIspParameters());
|
2022-03-05 18:03:00 +00:00
|
|
|
|
|
|
|
|
Logger::info("Initiating ISP interface");
|
|
|
|
|
this->avrIspInterface->activate();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* It is crucial that we understand the potential consequences of this operation.
|
|
|
|
|
*
|
|
|
|
|
* AVR fuses are used to control certain functions within the AVR (including the debugWire interface). Care
|
|
|
|
|
* must be taken when updating these fuse bytes, as an incorrect value could render the AVR inaccessible to
|
|
|
|
|
* standard programmers.
|
|
|
|
|
*
|
|
|
|
|
* For example, consider the SPI enable (SPIEN) fuse bit. This fuse bit is used to enable/disable the SPI for
|
|
|
|
|
* serial programming. If the SPIEN fuse bit is cleared, most programming tools will not be able to gain access
|
|
|
|
|
* to the target via the SPI. This isn't too bad, if there is some other way for the programming tool to gain
|
|
|
|
|
* access (such as the debugWire interface). But now consider the DWEN fuse bit (which is used to enable/disable
|
|
|
|
|
* the debugWire interface). What if both the SPIEN *and* the DWEN fuse bits are cleared? Both interfaces will
|
|
|
|
|
* be disabled. Effectively, the AVR will be bricked, and the only course for recovery would be to use
|
|
|
|
|
* high-voltage programming.
|
|
|
|
|
*
|
|
|
|
|
* When updating the DWEN fuse, Bloom relies on data from the target description file (TDF). But there is no
|
|
|
|
|
* guarantee that this data is correct. For this reason, we perform additional checks in an attempt to reduce
|
|
|
|
|
* the likelihood of bricking the target:
|
|
|
|
|
*
|
|
|
|
|
* - Confirm target signature match - We read the AVR signature from the connected target and compare it to
|
|
|
|
|
* what we have in the TDF. The operation will be aborted if the signatures do not match.
|
|
|
|
|
*
|
|
|
|
|
* - SPIEN fuse bit check - we can be certain that the SPIEN fuse bit is set, because we couldn't have gotten
|
|
|
|
|
* this far (post ISP activation) if it wasn't. We use this axiom to verify the validity of the data in the
|
|
|
|
|
* TDF. If the SPIEN fuse bit appears to be cleared, we can be fairly certain that the data we have on the
|
|
|
|
|
* SPIEN fuse bit is incorrect. From this, we assume that the data for the DWEN fuse bit is also incorrect,
|
|
|
|
|
* and abort the operation.
|
|
|
|
|
*
|
|
|
|
|
* - Lock bits check - we read the lock bit byte from the target and confirm that all lock bits are cleared.
|
|
|
|
|
* If any lock bits are set, we abort the operation.
|
|
|
|
|
*
|
|
|
|
|
* - DWEN fuse bit check - if the DWEN fuse bit is already set to the desired value, then there is no need
|
|
|
|
|
* to update it. But we may be checking the wrong bit (if the TDF data is incorrect) - either way, we will
|
|
|
|
|
* abort the operation.
|
|
|
|
|
*
|
|
|
|
|
* The precautions described above may reduce the likelihood of Bloom bricking the connected target, but there
|
|
|
|
|
* is still a chance that all of the checks pass, and we still brick the device. Now would be a good time to
|
2022-07-16 19:12:45 +01:00
|
|
|
* remind the user of liabilities in regard to Bloom and its contributors.
|
2022-03-05 18:03:00 +00:00
|
|
|
*/
|
|
|
|
|
Logger::warning(
|
|
|
|
|
"Updating the DWEN fuse bit is a potentially dangerous operation. Bloom is provided \"AS IS\", "
|
|
|
|
|
"without warranty of any kind. You are using Bloom at your own risk. In no event shall the copyright "
|
|
|
|
|
"owner or contributors be liable for any damage caused as a result of using Bloom. For more details, "
|
2022-12-26 21:47:09 +00:00
|
|
|
"see the Bloom license at " + Services::PathService::homeDomainName() + "/license"
|
2022-03-05 18:03:00 +00:00
|
|
|
);
|
|
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
try {
|
|
|
|
|
Logger::info("Reading target signature via ISP");
|
2023-05-21 21:08:25 +01:00
|
|
|
const auto ispDeviceSignature = this->avrIspInterface->getDeviceId();
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
if (ispDeviceSignature != this->signature) {
|
2022-03-15 11:26:16 +00:00
|
|
|
throw Exception(
|
2023-05-21 21:08:25 +01:00
|
|
|
"AVR target signature mismatch - expected signature \"" + this->signature.toHex()
|
|
|
|
|
+ "\" but got \"" + ispDeviceSignature.toHex() + "\". Please check target configuration."
|
2022-03-15 11:26:16 +00:00
|
|
|
);
|
|
|
|
|
}
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
Logger::info("Target signature confirmed: " + ispDeviceSignature.toHex());
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
const auto dwenFuseByte = this->avrIspInterface->readFuse(dwenFuseBitsDescriptor->fuseType).value;
|
|
|
|
|
const auto spienFuseByte = (spienFuseBitsDescriptor->fuseType == dwenFuseBitsDescriptor->fuseType)
|
|
|
|
|
? dwenFuseByte
|
|
|
|
|
: this->avrIspInterface->readFuse(spienFuseBitsDescriptor->fuseType).value;
|
2022-03-05 18:03:00 +00:00
|
|
|
|
|
|
|
|
/*
|
2022-03-15 11:26:16 +00:00
|
|
|
* Keep in mind that, for AVR fuses and lock bits, a set bit (0b1) means the fuse/lock is cleared, and a
|
|
|
|
|
* cleared bit (0b0), means the fuse/lock is set.
|
2022-03-05 18:03:00 +00:00
|
|
|
*/
|
|
|
|
|
|
2023-05-26 22:36:43 +01:00
|
|
|
if (!this->isFuseEnabled(*spienFuseBitsDescriptor, spienFuseByte)) {
|
2022-03-15 11:26:16 +00:00
|
|
|
/*
|
|
|
|
|
* If we get here, something is very wrong. The SPIEN (SPI enable) fuse bit appears to be cleared, but
|
|
|
|
|
* this is not possible because we're connected to the target via the SPI (the ISP interface uses a
|
|
|
|
|
* physical SPI between the debug tool and the target).
|
|
|
|
|
*
|
|
|
|
|
* This could be (and likely is) caused by incorrect data for the SPIEN fuse bit, in the TDF (which was
|
|
|
|
|
* used to construct the spienFuseBitsDescriptor). And if the data for the SPIEN fuse bit is incorrect,
|
|
|
|
|
* then what's to say the data for the DWEN fuse bit (dwenFuseBitsDescriptor) is any better?
|
|
|
|
|
*
|
|
|
|
|
* We must assume the worst and abort the operation. Otherwise, we risk bricking the user's hardware.
|
|
|
|
|
*/
|
|
|
|
|
throw Exception(
|
|
|
|
|
"Invalid SPIEN fuse bit value - suspected inaccuracies in TDF data. Please report this to "
|
2023-05-21 21:08:25 +01:00
|
|
|
"Bloom developers as a matter of urgency, via " + Services::PathService::homeDomainName()
|
|
|
|
|
+ "/report-issue"
|
2022-03-15 11:26:16 +00:00
|
|
|
);
|
|
|
|
|
}
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
Logger::info("Current SPIEN fuse bit value confirmed");
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2023-05-26 22:36:43 +01:00
|
|
|
if (this->isFuseEnabled(*dwenFuseBitsDescriptor, dwenFuseByte) == enable) {
|
2022-03-15 11:26:16 +00:00
|
|
|
/*
|
|
|
|
|
* The DWEN fuse appears to already be set to the desired value. This may be a result of incorrect data
|
|
|
|
|
* in the TDF, but we're not taking any chances.
|
|
|
|
|
*
|
|
|
|
|
* We don't throw an exception here, because we don't know if this is due to an error, or if the fuse
|
|
|
|
|
* bit is simply already set to the desired value.
|
|
|
|
|
*/
|
|
|
|
|
Logger::debug("DWEN fuse bit already set to desired value - aborting update operation");
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
this->avrIspInterface->deactivate();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
const auto lockBitByte = this->avrIspInterface->readLockBitByte();
|
|
|
|
|
if (lockBitByte != 0xFF) {
|
|
|
|
|
/*
|
|
|
|
|
* There is at least one lock bit that is set. Setting the DWEN fuse bit with the lock bits set may
|
|
|
|
|
* brick the device. We must abort.
|
|
|
|
|
*/
|
|
|
|
|
throw Exception(
|
|
|
|
|
"At least one lock bit has been set - updating the DWEN fuse bit could potentially brick "
|
|
|
|
|
"the target."
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
Logger::info("Cleared lock bits confirmed");
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
const auto newFuse = Fuse(
|
|
|
|
|
dwenFuseBitsDescriptor->fuseType,
|
2023-05-26 22:36:43 +01:00
|
|
|
this->setFuseEnabled(*dwenFuseBitsDescriptor, dwenFuseByte, enable)
|
2022-03-15 11:26:16 +00:00
|
|
|
);
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2023-05-07 16:50:14 +01:00
|
|
|
Logger::warning("Updating DWEN fuse bit");
|
2022-03-15 11:26:16 +00:00
|
|
|
this->avrIspInterface->programFuse(newFuse);
|
|
|
|
|
|
2023-05-07 16:50:14 +01:00
|
|
|
Logger::debug("Verifying DWEN fuse bit");
|
2022-03-15 11:26:16 +00:00
|
|
|
if (this->avrIspInterface->readFuse(dwenFuseBitsDescriptor->fuseType).value != newFuse.value) {
|
2023-05-07 16:50:14 +01:00
|
|
|
throw Exception("Failed to update DWEN fuse bit - post-update verification failed");
|
2022-03-15 11:26:16 +00:00
|
|
|
}
|
2022-03-05 18:03:00 +00:00
|
|
|
|
2022-03-15 11:26:16 +00:00
|
|
|
Logger::info("DWEN fuse bit successfully updated");
|
|
|
|
|
|
|
|
|
|
this->avrIspInterface->deactivate();
|
|
|
|
|
|
|
|
|
|
} catch (const Exception& exception) {
|
|
|
|
|
this->avrIspInterface->deactivate();
|
|
|
|
|
throw exception;
|
|
|
|
|
}
|
2022-03-05 18:03:00 +00:00
|
|
|
}
|
2022-06-03 15:49:12 +01:00
|
|
|
|
2023-05-07 16:49:45 +01:00
|
|
|
void Avr8::updateOcdenFuseBit(bool enable) {
|
|
|
|
|
using Services::PathService;
|
|
|
|
|
using Services::StringService;
|
|
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
);
|
2023-05-08 13:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto targetSignature = this->avr8DebugInterface->getDeviceId();
|
2023-05-21 21:08:25 +01:00
|
|
|
const auto tdSignature = this->targetDescriptionFile.getTargetSignature();
|
2023-05-08 13:04:26 +01:00
|
|
|
|
|
|
|
|
if (targetSignature != tdSignature) {
|
|
|
|
|
throw Exception(
|
|
|
|
|
"Failed to validate connected target - target signature mismatch.\nThe target signature"
|
|
|
|
|
" (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\""
|
|
|
|
|
+ tdSignature.toHex() + "\"). This will likely be due to an incorrect target name in the "
|
|
|
|
|
+ "configuration file (bloom.yaml)."
|
|
|
|
|
);
|
2023-05-07 16:49:45 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-21 21:08:25 +01:00
|
|
|
const auto ocdenFuseBitsDescriptor = this->targetDescriptionFile.getOcdenFuseBitsDescriptor();
|
|
|
|
|
const auto jtagenFuseBitsDescriptor = this->targetDescriptionFile.getJtagenFuseBitsDescriptor();
|
2023-05-07 16:49:45 +01:00
|
|
|
|
|
|
|
|
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.");
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
const auto ocdenFuseByteValue = this->avr8DebugInterface->readMemory(
|
|
|
|
|
TargetMemoryType::FUSES,
|
|
|
|
|
ocdenFuseBitsDescriptor->byteAddress,
|
|
|
|
|
1
|
|
|
|
|
).at(0);
|
|
|
|
|
const auto jtagenFuseByteValue = jtagenFuseBitsDescriptor->byteAddress == ocdenFuseBitsDescriptor->byteAddress
|
|
|
|
|
? ocdenFuseByteValue
|
|
|
|
|
: this->avr8DebugInterface->readMemory(
|
2023-05-07 16:49:45 +01:00
|
|
|
TargetMemoryType::FUSES,
|
2023-05-28 21:27:01 +01:00
|
|
|
jtagenFuseBitsDescriptor->byteAddress,
|
2023-05-07 16:49:45 +01:00
|
|
|
1
|
2023-05-28 21:27:01 +01:00
|
|
|
).at(0)
|
|
|
|
|
;
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::debug("OCDEN fuse byte value (before update): 0x" + StringService::toHex(ocdenFuseByteValue));
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
if (!this->isFuseEnabled(*jtagenFuseBitsDescriptor, jtagenFuseByteValue)) {
|
|
|
|
|
/*
|
|
|
|
|
* 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"
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
if (this->isFuseEnabled(*ocdenFuseBitsDescriptor, ocdenFuseByteValue) == enable) {
|
|
|
|
|
Logger::debug("OCDEN fuse bit already set to desired value - aborting update operation");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
const auto newValue = this->setFuseEnabled(*ocdenFuseBitsDescriptor, ocdenFuseByteValue, enable);
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::debug("New OCDEN fuse byte value (to be written): 0x" + StringService::toHex(newValue));
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::warning("Updating OCDEN fuse bit");
|
|
|
|
|
this->avr8DebugInterface->writeMemory(
|
|
|
|
|
TargetMemoryType::FUSES,
|
|
|
|
|
ocdenFuseBitsDescriptor->byteAddress,
|
|
|
|
|
{newValue}
|
|
|
|
|
);
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::debug("Verifying OCDEN fuse bit");
|
|
|
|
|
const auto postUpdateOcdenByteValue = this->avr8DebugInterface->readMemory(
|
|
|
|
|
TargetMemoryType::FUSES,
|
|
|
|
|
ocdenFuseBitsDescriptor->byteAddress,
|
|
|
|
|
1
|
|
|
|
|
).at(0);
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
if (postUpdateOcdenByteValue != newValue) {
|
|
|
|
|
throw Exception("Failed to update OCDEN fuse bit - post-update verification failed");
|
|
|
|
|
}
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::info("OCDEN fuse bit updated");
|
2023-05-07 16:49:45 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
this->disableProgrammingMode();
|
2023-05-07 16:49:45 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-26 22:45:57 +01:00
|
|
|
bool Avr8::updateEesaveFuseBit(bool enable) {
|
|
|
|
|
using Services::StringService;
|
|
|
|
|
|
|
|
|
|
const auto eesaveFuseBitsDescriptor = this->targetDescriptionFile.getEesaveFuseBitsDescriptor();
|
|
|
|
|
|
|
|
|
|
if (!eesaveFuseBitsDescriptor.has_value()) {
|
|
|
|
|
throw Exception("Could not find EESAVE bit field in TDF.");
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
const auto eesaveFuseByteValue = this->avr8DebugInterface->readMemory(
|
|
|
|
|
TargetMemoryType::FUSES,
|
|
|
|
|
eesaveFuseBitsDescriptor->byteAddress,
|
|
|
|
|
1
|
|
|
|
|
).at(0);
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::debug("EESAVE fuse byte value (before update): 0x" + StringService::toHex(eesaveFuseByteValue));
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
if (this->isFuseEnabled(*eesaveFuseBitsDescriptor, eesaveFuseByteValue) == enable) {
|
|
|
|
|
Logger::debug("EESAVE fuse bit already set to desired value - aborting update operation");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
const auto newValue = this->setFuseEnabled(*eesaveFuseBitsDescriptor, eesaveFuseByteValue, enable);
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::debug("New EESAVE fuse byte value (to be written): 0x" + StringService::toHex(newValue));
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::warning("Updating EESAVE fuse bit");
|
|
|
|
|
this->avr8DebugInterface->writeMemory(
|
|
|
|
|
TargetMemoryType::FUSES,
|
|
|
|
|
eesaveFuseBitsDescriptor->byteAddress,
|
|
|
|
|
{newValue}
|
|
|
|
|
);
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::debug("Verifying EESAVE fuse bit");
|
|
|
|
|
const auto postUpdateEesaveByteValue = this->avr8DebugInterface->readMemory(
|
|
|
|
|
TargetMemoryType::FUSES,
|
|
|
|
|
eesaveFuseBitsDescriptor->byteAddress,
|
|
|
|
|
1
|
|
|
|
|
).at(0);
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
if (postUpdateEesaveByteValue != newValue) {
|
|
|
|
|
throw Exception("Failed to update EESAVE fuse bit - post-update verification failed");
|
|
|
|
|
}
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
Logger::info("EESAVE fuse bit updated");
|
2023-05-26 22:45:57 +01:00
|
|
|
|
2023-05-28 21:27:01 +01:00
|
|
|
return true;
|
2023-05-26 22:45:57 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-03 15:49:12 +01:00
|
|
|
ProgramMemorySection Avr8::getProgramMemorySectionFromAddress(std::uint32_t address) {
|
2023-05-21 21:08:25 +01:00
|
|
|
return this->targetParameters.bootSectionStartAddress.has_value()
|
|
|
|
|
&& address >= this->targetParameters.bootSectionStartAddress.value()
|
2022-06-03 15:49:12 +01:00
|
|
|
? ProgramMemorySection::BOOT
|
|
|
|
|
: ProgramMemorySection::APPLICATION;
|
|
|
|
|
}
|
2021-10-06 21:12:31 +01:00
|
|
|
}
|