Added AVR8 fuse enable strategy

This commit is contained in:
Nav
2023-05-26 22:36:43 +01:00
parent 142f844f83
commit df51da4479
5 changed files with 108 additions and 11 deletions

View File

@@ -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<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)
);
}
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<bool>(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<unsigned char>(dwenFuseByte & ~(dwenFuseBitsDescriptor->bitMask))
: static_cast<unsigned char>(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<bool>(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<unsigned char>(ocdenFuseByteValue & ~(ocdenFuseBitsDescriptor->bitMask))
: static_cast<unsigned char>(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));

View File

@@ -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<TargetRegisterDescriptorId, TargetRegisterDescriptor> targetRegisterDescriptorsById;
std::map<TargetMemoryType, TargetMemoryDescriptor> 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.
*

View File

@@ -339,6 +339,32 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
return output;
}
std::optional<FuseEnableStrategy> TargetDescriptionFile::getFuseEnableStrategy() const {
static const auto fuseEnableStrategies = std::map<std::string, FuseEnableStrategy>({
{"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<FuseBitsDescriptor> TargetDescriptionFile::getDwenFuseBitsDescriptor() const {
return this->getFuseBitsDescriptorByName("dwen");
}

View File

@@ -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<FuseEnableStrategy> getFuseEnableStrategy() const;
/**
* Constructs a FuseBitDescriptor for the debugWire enable (DWEN) fuse bit, with information extracted from
* the TDF.

View File

@@ -13,6 +13,12 @@ namespace Bloom::Targets::Microchip::Avr
EXTENDED,
};
enum class FuseEnableStrategy: std::uint8_t
{
CLEAR,
SET,
};
struct Fuse
{
FuseType type;