From df51da44794eede9ef5309954c95e467bd1ac0ed Mon Sep 17 00:00:00 2001 From: Nav Date: Fri, 26 May 2023 22:36:43 +0100 Subject: [PATCH] Added AVR8 fuse enable strategy --- src/Targets/Microchip/AVR/AVR8/Avr8.cpp | 40 ++++++++++++++----- src/Targets/Microchip/AVR/AVR8/Avr8.hpp | 39 ++++++++++++++++++ .../TargetDescriptionFile.cpp | 26 ++++++++++++ .../TargetDescriptionFile.hpp | 8 ++++ src/Targets/Microchip/AVR/Fuse.hpp | 6 +++ 5 files changed, 108 insertions(+), 11 deletions(-) diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp index a1b9e9d3..320f1a13 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp @@ -14,8 +14,6 @@ #include "Exceptions/DebugWirePhysicalInterfaceError.hpp" #include "src/Targets/TargetRegister.hpp" -#include "src/Targets/Microchip/AVR/Fuse.hpp" - namespace Bloom::Targets::Microchip::Avr::Avr8Bit { using namespace Exceptions; @@ -54,6 +52,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit TargetRegisterAccess(true, true) ) ) + , fuseEnableStrategy(this->targetDescriptionFile.getFuseEnableStrategy().value_or(FuseEnableStrategy::CLEAR)) { if (!this->supportedPhysicalInterfaces.contains(this->targetConfig.physicalInterface)) { /* @@ -648,6 +647,28 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit } } + bool Avr8::isFuseEnabled(const FuseBitsDescriptor& descriptor, unsigned char fuseByteValue) const { + const auto programmedValue = static_cast( + 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( + this->fuseEnableStrategy == FuseEnableStrategy::SET + ? enabled ? (fuseByteValue | descriptor.bitMask) : fuseByteValue & ~(descriptor.bitMask) + : enabled ? fuseByteValue & ~(descriptor.bitMask) : (fuseByteValue | descriptor.bitMask) + ); + } + void Avr8::updateDwenFuseBit(bool enable) { if (this->avrIspInterface == nullptr) { throw Exception( @@ -750,7 +771,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit * cleared bit (0b0), means the fuse/lock is set. */ - if ((spienFuseByte & spienFuseBitsDescriptor->bitMask) != 0) { + if (!this->isFuseEnabled(*spienFuseBitsDescriptor, spienFuseByte)) { /* * 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 @@ -771,7 +792,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit Logger::info("Current SPIEN fuse bit value confirmed"); - if (!static_cast(dwenFuseByte & dwenFuseBitsDescriptor->bitMask) == enable) { + if (this->isFuseEnabled(*dwenFuseBitsDescriptor, dwenFuseByte) == enable) { /* * 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. @@ -801,8 +822,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit const auto newFuse = Fuse( dwenFuseBitsDescriptor->fuseType, - (enable) ? static_cast(dwenFuseByte & ~(dwenFuseBitsDescriptor->bitMask)) - : static_cast(dwenFuseByte | dwenFuseBitsDescriptor->bitMask) + this->setFuseEnabled(*dwenFuseBitsDescriptor, dwenFuseByte, enable) ); Logger::warning("Updating DWEN fuse bit"); @@ -876,7 +896,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit Logger::debug("OCDEN fuse byte value (before update): 0x" + StringService::toHex(ocdenFuseByteValue)); - if ((jtagenFuseByteValue & jtagenFuseBitsDescriptor->bitMask) != 0) { + 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. @@ -890,16 +910,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit ); } - if (!static_cast(ocdenFuseByteValue & ocdenFuseBitsDescriptor->bitMask) == enable) { + if (this->isFuseEnabled(*ocdenFuseBitsDescriptor, ocdenFuseByteValue) == enable) { Logger::debug("OCDEN fuse bit already set to desired value - aborting update operation"); this->disableProgrammingMode(); return; } - const auto newValue = (enable) - ? static_cast(ocdenFuseByteValue & ~(ocdenFuseBitsDescriptor->bitMask)) - : static_cast(ocdenFuseByteValue | ocdenFuseBitsDescriptor->bitMask); + const auto newValue = this->setFuseEnabled(*ocdenFuseBitsDescriptor, ocdenFuseByteValue, enable); Logger::debug("New OCDEN fuse byte value (to be written): 0x" + StringService::toHex(newValue)); diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp index 78b6f5d3..4059246d 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp @@ -15,6 +15,8 @@ #include "TargetParameters.hpp" #include "PadDescriptor.hpp" #include "ProgramMemorySection.hpp" + +#include "src/Targets/Microchip/AVR/Fuse.hpp" #include "src/Targets/TargetRegister.hpp" #include "TargetDescription/TargetDescriptionFile.hpp" @@ -116,6 +118,16 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit TargetRegisterDescriptor stackPointerRegisterDescriptor; TargetRegisterDescriptor statusRegisterDescriptor; + /** + * On some AVR8 targets, like the ATmega328P, a cleared fuse bit means the fuse is "programmed" (enabled). + * And a set bit means the fuse is "un-programmed" (disabled). But on others, like the ATmega4809, it's the + * other way around (set bit == enabled, cleared bit == disabled). + * + * The FuseEnableStrategy specifies the strategy of enabling a fuse. It's extracted from the TDF. + * See TargetDescription::getFuseEnableStrategy() for more. + */ + FuseEnableStrategy fuseEnableStrategy; + std::map targetRegisterDescriptorsById; std::map targetMemoryDescriptorsByType; @@ -130,6 +142,33 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit void loadTargetMemoryDescriptors(); + /** + * Checks if a particular fuse is enabled in the given fuse byte value. Takes the target's fuse enable strategy + * into account. + * + * @param descriptor + * @param fuseByteValue + * + * @return + */ + bool isFuseEnabled(const FuseBitsDescriptor& descriptor, unsigned char fuseByteValue) const; + + /** + * Enables/disables a fuse within the given fuse byte, using the target's fuse enable strategy. + * + * @param descriptor + * @param fuseByteValue + * @param enabled + * + * @return + * The updated fuse byte value. + */ + unsigned char setFuseEnabled( + const FuseBitsDescriptor& descriptor, + unsigned char fuseByteValue, + bool enabled + ) const; + /** * Updates the debugWire enable (DWEN) fuse bit on the AVR target. * diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp index dd915b27..0ad7f4b0 100644 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp @@ -339,6 +339,32 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription return output; } + std::optional TargetDescriptionFile::getFuseEnableStrategy() const { + static const auto fuseEnableStrategies = std::map({ + {"0", FuseEnableStrategy::CLEAR}, + {"1", FuseEnableStrategy::SET}, + }); + + const auto programmingInfoPropertyGroupIt = this->propertyGroupsMappedByName.find("programming_info"); + + if (programmingInfoPropertyGroupIt != this->propertyGroupsMappedByName.end()) { + const auto& programmingInfoParamsByName = programmingInfoPropertyGroupIt->second.propertiesMappedByName; + const auto fuseEnabledValuePropertyIt = programmingInfoParamsByName.find("fuse_enabled_value"); + + if (fuseEnabledValuePropertyIt != programmingInfoParamsByName.end()) { + const auto fuseEnableStrategyIt = fuseEnableStrategies.find( + fuseEnabledValuePropertyIt->second.value.toStdString() + ); + + if (fuseEnableStrategyIt != fuseEnableStrategies.end()) { + return fuseEnableStrategyIt->second; + } + } + } + + return std::nullopt; + } + std::optional TargetDescriptionFile::getDwenFuseBitsDescriptor() const { return this->getFuseBitsDescriptorByName("dwen"); } diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp index 69af022c..4d1f3463 100644 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.hpp @@ -83,6 +83,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription */ [[nodiscard]] IspParameters getIspParameters() const; + /** + * Extracts the target's fuse enable strategy. + * + * @return + * std::nullopt if the TDF doesn't contain a fuse enable strategy. + */ + [[nodiscard]] std::optional getFuseEnableStrategy() const; + /** * Constructs a FuseBitDescriptor for the debugWire enable (DWEN) fuse bit, with information extracted from * the TDF. diff --git a/src/Targets/Microchip/AVR/Fuse.hpp b/src/Targets/Microchip/AVR/Fuse.hpp index 4afc5f7d..901e483c 100644 --- a/src/Targets/Microchip/AVR/Fuse.hpp +++ b/src/Targets/Microchip/AVR/Fuse.hpp @@ -13,6 +13,12 @@ namespace Bloom::Targets::Microchip::Avr EXTENDED, }; + enum class FuseEnableStrategy: std::uint8_t + { + CLEAR, + SET, + }; + struct Fuse { FuseType type;