Massive refactor to accommodate RISC-V targets
- Refactored entire codebase (excluding the Insight component) to accommodate multiple target architectures (no longer specific to AVR) - Deleted 'generate SVD' GDB monitor command - I will eventually move this functionality to the Bloom website - Added unit size property to address spaces - Many other changes which I couldn't be bothered to describe here
This commit is contained in:
1154
src/Targets/Microchip/AVR8/Avr8.cpp
Normal file
1154
src/Targets/Microchip/AVR8/Avr8.cpp
Normal file
File diff suppressed because it is too large
Load Diff
232
src/Targets/Microchip/AVR8/Avr8.hpp
Normal file
232
src/Targets/Microchip/AVR8/Avr8.hpp
Normal file
@@ -0,0 +1,232 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/Target.hpp"
|
||||
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp"
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp"
|
||||
|
||||
#include "Family.hpp"
|
||||
#include "GpioPadDescriptor.hpp"
|
||||
#include "ProgramMemorySection.hpp"
|
||||
#include "ProgrammingSession.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR8/Fuse.hpp"
|
||||
#include "src/Targets/TargetPhysicalInterface.hpp"
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
#include "src/Targets/TargetBitFieldDescriptor.hpp"
|
||||
#include "src/Targets/TargetBreakpoint.hpp"
|
||||
|
||||
#include "TargetDescriptionFile.hpp"
|
||||
|
||||
#include "Avr8TargetConfig.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
class Avr8: public Target
|
||||
{
|
||||
public:
|
||||
explicit Avr8(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile);
|
||||
|
||||
/*
|
||||
* The functions below implement the Target interface for AVR8 targets.
|
||||
*
|
||||
* See the Targets::Target abstract class for documentation on the expected behaviour of
|
||||
* each function.
|
||||
*/
|
||||
|
||||
/**
|
||||
* All AVR8 compatible debug tools must provide a valid Avr8Interface.
|
||||
*
|
||||
* @param debugTool
|
||||
* @return
|
||||
*/
|
||||
bool supportsDebugTool(DebugTool* debugTool) override;
|
||||
|
||||
void setDebugTool(DebugTool* debugTool) override;
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
TargetDescriptor targetDescriptor() override;
|
||||
|
||||
void run(std::optional<TargetMemoryAddress> toAddress) override;
|
||||
void stop() override;
|
||||
void step() override;
|
||||
void reset() override;
|
||||
|
||||
void setSoftwareBreakpoint(TargetMemoryAddress address) override;
|
||||
void removeSoftwareBreakpoint(TargetMemoryAddress address) override;
|
||||
|
||||
void setHardwareBreakpoint(TargetMemoryAddress address) override;
|
||||
void removeHardwareBreakpoint(TargetMemoryAddress address) override;
|
||||
void clearAllBreakpoints() override;
|
||||
|
||||
TargetRegisterDescriptorAndValuePairs readRegisters(const TargetRegisterDescriptors& descriptors) override;
|
||||
void writeRegisters(const TargetRegisterDescriptorAndValuePairs& registers) override;
|
||||
|
||||
TargetMemoryBuffer readMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
TargetMemoryAddress startAddress,
|
||||
TargetMemorySize bytes,
|
||||
const std::set<TargetMemoryAddressRange>& excludedAddressRanges
|
||||
) override;
|
||||
void writeMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
TargetMemoryAddress startAddress,
|
||||
const TargetMemoryBuffer& buffer
|
||||
) override;
|
||||
bool isProgramMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
TargetMemoryAddress startAddress,
|
||||
TargetMemorySize size
|
||||
) override;
|
||||
void eraseMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||
) override;
|
||||
|
||||
TargetExecutionState getExecutionState() override;
|
||||
|
||||
TargetMemoryAddress getProgramCounter() override;
|
||||
void setProgramCounter(TargetMemoryAddress programCounter) override;
|
||||
|
||||
TargetStackPointer getStackPointer() override;
|
||||
void setStackPointer(TargetStackPointer stackPointer) override;
|
||||
|
||||
TargetGpioPinDescriptorAndStatePairs getGpioPinStates(const TargetPinoutDescriptor& pinoutDescriptor) override;
|
||||
void setGpioPinState(const TargetPinDescriptor& pinDescriptor, const TargetGpioPinState& state) override;
|
||||
|
||||
void enableProgrammingMode() override;
|
||||
|
||||
void disableProgrammingMode() override;
|
||||
|
||||
bool programmingModeEnabled() override;
|
||||
|
||||
protected:
|
||||
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* targetPowerManagementInterface = nullptr;
|
||||
DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* avr8DebugInterface = nullptr;
|
||||
DebugToolDrivers::TargetInterfaces::Microchip::Avr8::AvrIspInterface* avrIspInterface = nullptr;
|
||||
|
||||
Avr8TargetConfig targetConfig;
|
||||
TargetDescriptionFile targetDescriptionFile;
|
||||
|
||||
TargetAddressSpaceDescriptor dataAddressSpaceDescriptor;
|
||||
TargetAddressSpaceDescriptor fuseAddressSpaceDescriptor;
|
||||
|
||||
TargetMemorySegmentDescriptor ramMemorySegmentDescriptor;
|
||||
TargetMemorySegmentDescriptor ioMemorySegmentDescriptor;
|
||||
TargetMemorySegmentDescriptor fuseMemorySegmentDescriptor;
|
||||
|
||||
TargetSignature signature;
|
||||
Family family;
|
||||
|
||||
bool activated = false;
|
||||
|
||||
std::set<TargetPhysicalInterface> physicalInterfaces;
|
||||
|
||||
std::vector<TargetPeripheralDescriptor> gpioPortPeripheralDescriptors;
|
||||
std::map<std::string, GpioPadDescriptor> gpioPadDescriptorsByPadName;
|
||||
|
||||
/**
|
||||
* The stack pointer register on AVR8 targets can take several forms:
|
||||
*
|
||||
* 1. A single 8 or 16-bit register in the CPU peripheral, with key "sp"
|
||||
* 2. Two 8-bit registers for high and low bytes in a 16-bit stack pointer, in the CPU peripheral, with keys
|
||||
* "spl" and "sph"
|
||||
* 3. A single 8-bit low byte register for an 8-bit stack pointer, in the CPU peripheral. This is similar
|
||||
* to 1, but with key "spl"
|
||||
*/
|
||||
std::optional<TargetRegisterDescriptor> spRegisterDescriptor;
|
||||
std::optional<TargetRegisterDescriptor> spLowRegisterDescriptor;
|
||||
std::optional<TargetRegisterDescriptor> spHighRegisterDescriptor;
|
||||
|
||||
/**
|
||||
* 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::optional<ProgrammingSession> activeProgrammingSession = std::nullopt;
|
||||
|
||||
static std::map<std::string, GpioPadDescriptor> generateGpioPadDescriptorMapping(
|
||||
const std::vector<TargetPeripheralDescriptor>& portPeripheralDescriptors
|
||||
);
|
||||
|
||||
TargetMemoryBuffer readRegister(const TargetRegisterDescriptor& descriptor);
|
||||
void writeRegister(const TargetRegisterDescriptor& descriptor, const TargetMemoryBuffer& value) ;
|
||||
|
||||
void applyDebugInterfaceRegisterAccessRestrictions(
|
||||
TargetRegisterGroupDescriptor& groupDescriptor,
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor
|
||||
);
|
||||
|
||||
BreakpointResources getBreakpointResources();
|
||||
|
||||
/**
|
||||
* Checks if a particular fuse is enabled in the given fuse byte value. Takes the target's fuse enable strategy
|
||||
* into account.
|
||||
*
|
||||
* @param bitFieldDescriptor
|
||||
* @param value
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool isFuseEnabled(const TargetBitFieldDescriptor& bitFieldDescriptor, FuseValue value) const;
|
||||
|
||||
/**
|
||||
* Enables/disables a fuse within the given fuse byte, using the target's fuse enable strategy.
|
||||
*
|
||||
* @param bitFieldDescriptor
|
||||
* @param value
|
||||
* @param enabled
|
||||
*
|
||||
* @return
|
||||
* The updated fuse byte value.
|
||||
*/
|
||||
FuseValue setFuseEnabled(
|
||||
const TargetBitFieldDescriptor& bitFieldDescriptor,
|
||||
FuseValue value,
|
||||
bool enabled
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Updates the debugWIRE enable (DWEN) fuse bit on the AVR target.
|
||||
*
|
||||
* @param enable
|
||||
* True to enable the fuse, false to disable it.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Updates the "Preserve EEPROM" (EESAVE) fuse bit on the AVR target.
|
||||
*
|
||||
* @param enable
|
||||
* True to enable the fuse, false to disable it.
|
||||
*
|
||||
* @return
|
||||
* True if the fuse bit was updated. False if the fuse bit was already set to the desired value.
|
||||
*/
|
||||
bool updateEesaveFuseBit(bool enable);
|
||||
};
|
||||
}
|
||||
59
src/Targets/Microchip/AVR8/Avr8TargetConfig.cpp
Normal file
59
src/Targets/Microchip/AVR8/Avr8TargetConfig.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "Avr8TargetConfig.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
Avr8TargetConfig::Avr8TargetConfig(const TargetConfig& targetConfig)
|
||||
: TargetConfig(targetConfig)
|
||||
{
|
||||
const auto& targetNode = targetConfig.targetNode;
|
||||
|
||||
// The 'manageDwenFuseBit' param used to be 'updateDwenFuseBit' - we still support the old, for now.
|
||||
if (targetNode["updateDwenFuseBit"]) {
|
||||
this->manageDwenFuseBit = targetNode["updateDwenFuseBit"].as<bool>(
|
||||
this->manageDwenFuseBit
|
||||
);
|
||||
}
|
||||
|
||||
if (targetNode["manageDwenFuseBit"]) {
|
||||
this->manageDwenFuseBit = targetNode["manageDwenFuseBit"].as<bool>(
|
||||
this->manageDwenFuseBit
|
||||
);
|
||||
}
|
||||
|
||||
if (targetNode["cycleTargetPowerPostDwenUpdate"]) {
|
||||
this->cycleTargetPowerPostDwenUpdate = targetNode["cycleTargetPowerPostDwenUpdate"].as<bool>(
|
||||
this->cycleTargetPowerPostDwenUpdate
|
||||
);
|
||||
}
|
||||
|
||||
if (targetNode["disableDebugWirePreDisconnect"]) {
|
||||
this->disableDebugWireOnDeactivate = targetNode["disableDebugWirePreDisconnect"].as<bool>(
|
||||
this->disableDebugWireOnDeactivate
|
||||
);
|
||||
}
|
||||
|
||||
if (targetNode["targetPowerCycleDelay"]) {
|
||||
this->targetPowerCycleDelay = std::chrono::milliseconds{targetNode["targetPowerCycleDelay"].as<int>(
|
||||
this->targetPowerCycleDelay.count()
|
||||
)};
|
||||
}
|
||||
|
||||
if (targetNode["manageOcdenFuseBit"]) {
|
||||
this->manageOcdenFuseBit = targetNode["manageOcdenFuseBit"].as<bool>(
|
||||
this->manageOcdenFuseBit
|
||||
);
|
||||
}
|
||||
|
||||
if (targetNode["preserveEeprom"]) {
|
||||
this->preserveEeprom = targetNode["preserveEeprom"].as<bool>(
|
||||
this->preserveEeprom
|
||||
);
|
||||
}
|
||||
|
||||
if (targetNode["reserveSteppingBreakpoint"]) {
|
||||
this->reserveSteppingBreakpoint = targetNode["reserveSteppingBreakpoint"].as<bool>(
|
||||
this->reserveSteppingBreakpoint
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
89
src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp
Normal file
89
src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "src/ProjectConfig.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* Extending the generic TargetConfig struct to accommodate AVR8 target configuration parameters.
|
||||
*/
|
||||
struct Avr8TargetConfig: public TargetConfig
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Because the debugWIRE module requires control of the reset pin on the target, enabling this module will
|
||||
* effectively mean losing control of the reset pin. This means users won't be able to use other
|
||||
* interfaces that require access to the reset pin, such as ISP, until the debugWIRE module is disabled.
|
||||
*
|
||||
* The EdbgAvr8Interface provides a function for temporarily disabling the debugWIRE module on the target.
|
||||
* This doesn't change the DWEN fuse and its affect is only temporary - the debugWIRE module will be
|
||||
* reactivated upon the user cycling the power to the target.
|
||||
*
|
||||
* Bloom is able to temporarily disable the debugWIRE module, automatically, upon deactivating of the
|
||||
* target (which usually occurs after a debug session has ended). This allows users to program the target via
|
||||
* ISP, after they've finished a debug session. After programming the target, the user will need to cycle the
|
||||
* target power before Bloom can gain access for another debug session. This flag control this function.
|
||||
*
|
||||
* NOTE: Currently, this flag is only honoured by the EdbgAvr8Interface.
|
||||
*
|
||||
* See the EdbgAvr8Interface::disableDebugWire() function for more.
|
||||
*/
|
||||
bool disableDebugWireOnDeactivate = false;
|
||||
|
||||
/**
|
||||
* The manageDwenFuseBit flag determines if Bloom should manage the DWEN fuse bit, for debugWIRE sessions.
|
||||
*
|
||||
* This parameter is optional, and the function is disabled by default. Users must explicitly enable it in
|
||||
* their target configuration.
|
||||
*/
|
||||
bool manageDwenFuseBit = false;
|
||||
|
||||
/**
|
||||
* For debug tools that provide target power management functions (such as some evaluation boards), Bloom can
|
||||
* automatically cycle the target power after updating the DWEN fuse bit, for debugWIRE sessions. This parameter
|
||||
* controls this function.
|
||||
*
|
||||
* This parameter is optional. The function is enabled by default.
|
||||
*/
|
||||
bool cycleTargetPowerPostDwenUpdate = true;
|
||||
|
||||
/**
|
||||
* When cycling target power after updating the DWEN fuse bit, we wait for a number of milliseconds, for the
|
||||
* target to power-down and back up.
|
||||
*
|
||||
* This parameter determines how long we wait.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* With JTAG and UPDI targets, we have to perform a full chip erase when updating the target's flash memory.
|
||||
* This means the user will lose their EEPROM data whenever they wish to upload any program changes via Bloom.
|
||||
*
|
||||
* The preserveEeprom flag determines if Bloom should preserve the target's EEPROM by setting the EESAVE fuse
|
||||
* bit before performing the chip erase.
|
||||
*
|
||||
* This parameter is optional, and the function is enabled by default.
|
||||
*/
|
||||
bool preserveEeprom = true;
|
||||
|
||||
/**
|
||||
* Determines if Bloom will reserve a single hardware breakpoint for stepping operations.
|
||||
*/
|
||||
bool reserveSteppingBreakpoint = true;
|
||||
|
||||
explicit Avr8TargetConfig(const TargetConfig& targetConfig);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
|
||||
namespace Exceptions
|
||||
{
|
||||
class DebugWirePhysicalInterfaceError: public TargetOperationFailure
|
||||
{
|
||||
public:
|
||||
explicit DebugWirePhysicalInterfaceError(const std::string& message)
|
||||
: TargetOperationFailure(message)
|
||||
{}
|
||||
};
|
||||
}
|
||||
15
src/Targets/Microchip/AVR8/Family.hpp
Normal file
15
src/Targets/Microchip/AVR8/Family.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
enum class Family: int
|
||||
{
|
||||
MEGA,
|
||||
XMEGA,
|
||||
TINY,
|
||||
DA,
|
||||
DB,
|
||||
DD,
|
||||
EA,
|
||||
};
|
||||
}
|
||||
24
src/Targets/Microchip/AVR8/Fuse.hpp
Normal file
24
src/Targets/Microchip/AVR8/Fuse.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
using FuseValue = std::uint8_t;
|
||||
|
||||
enum class FuseType: std::uint8_t
|
||||
{
|
||||
LOW,
|
||||
HIGH,
|
||||
EXTENDED,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
enum class FuseEnableStrategy: std::uint8_t
|
||||
{
|
||||
CLEAR,
|
||||
SET,
|
||||
};
|
||||
}
|
||||
34
src/Targets/Microchip/AVR8/GpioPadDescriptor.hpp
Normal file
34
src/Targets/Microchip/AVR8/GpioPadDescriptor.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* This struct contains all of the relevant GPIO register descriptors for a particular pad, on an AVR8 target.
|
||||
*
|
||||
* We use this to read and manipulate the state of GPIO pins.
|
||||
*/
|
||||
struct GpioPadDescriptor
|
||||
{
|
||||
std::uint8_t registerMask;
|
||||
|
||||
const TargetRegisterDescriptor& dataDirectionRegisterDescriptor;
|
||||
const TargetRegisterDescriptor& inputRegisterDescriptor;
|
||||
const TargetRegisterDescriptor& outputRegisterDescriptor;
|
||||
|
||||
GpioPadDescriptor(
|
||||
std::uint8_t registerMask,
|
||||
const TargetRegisterDescriptor& dataDirectionRegisterDescriptor,
|
||||
const TargetRegisterDescriptor& inputRegisterDescriptor,
|
||||
const TargetRegisterDescriptor& outputRegisterDescriptor
|
||||
)
|
||||
: registerMask(registerMask)
|
||||
, dataDirectionRegisterDescriptor(dataDirectionRegisterDescriptor)
|
||||
, inputRegisterDescriptor(inputRegisterDescriptor)
|
||||
, outputRegisterDescriptor(outputRegisterDescriptor)
|
||||
{}
|
||||
};
|
||||
}
|
||||
29
src/Targets/Microchip/AVR8/IspParameters.cpp
Normal file
29
src/Targets/Microchip/AVR8/IspParameters.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "IspParameters.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
IspParameters::IspParameters(const TargetDescriptionFile& targetDescriptionFile) {
|
||||
using Services::StringService;
|
||||
|
||||
const auto& ispGroup = targetDescriptionFile.getPropertyGroup("isp_interface");
|
||||
|
||||
this->programModeTimeout = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_timeout").value);
|
||||
this->programModeStabilizationDelay = StringService::toUint8(
|
||||
ispGroup.getProperty("ispenterprogmode_stabdelay").value
|
||||
);
|
||||
this->programModeCommandExecutionDelay = StringService::toUint8(
|
||||
ispGroup.getProperty("ispenterprogmode_cmdexedelay").value
|
||||
);
|
||||
this->programModeSyncLoops = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_synchloops").value);
|
||||
this->programModeByteDelay = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_bytedelay").value);
|
||||
this->programModePollValue = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_pollvalue").value);
|
||||
this->programModePollIndex = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_pollindex").value);
|
||||
this->programModePreDelay = StringService::toUint8(ispGroup.getProperty("ispleaveprogmode_predelay").value);
|
||||
this->programModePostDelay = StringService::toUint8(ispGroup.getProperty("ispleaveprogmode_postdelay").value);
|
||||
this->readSignaturePollIndex = StringService::toUint8(ispGroup.getProperty("ispreadsign_pollindex").value);
|
||||
this->readFusePollIndex = StringService::toUint8(ispGroup.getProperty("ispreadfuse_pollindex").value);
|
||||
this->readLockPollIndex = StringService::toUint8(ispGroup.getProperty("ispreadlock_pollindex").value);
|
||||
}
|
||||
}
|
||||
32
src/Targets/Microchip/AVR8/IspParameters.hpp
Normal file
32
src/Targets/Microchip/AVR8/IspParameters.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "TargetDescriptionFile.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* These parameters are required by ISP interfaces, to enter programming mode.
|
||||
*
|
||||
* These parameters are not specific to the EDBG protocol, which is why they do not reside in the EDBG protocol
|
||||
* directory.
|
||||
*/
|
||||
struct IspParameters
|
||||
{
|
||||
std::uint8_t programModeTimeout;
|
||||
std::uint8_t programModeStabilizationDelay;
|
||||
std::uint8_t programModeCommandExecutionDelay;
|
||||
std::uint8_t programModeSyncLoops;
|
||||
std::uint8_t programModeByteDelay;
|
||||
std::uint8_t programModePollValue;
|
||||
std::uint8_t programModePollIndex;
|
||||
std::uint8_t programModePreDelay;
|
||||
std::uint8_t programModePostDelay;
|
||||
std::uint8_t readSignaturePollIndex;
|
||||
std::uint8_t readFusePollIndex;
|
||||
std::uint8_t readLockPollIndex;
|
||||
|
||||
explicit IspParameters(const TargetDescriptionFile& targetDescriptionFile);
|
||||
};
|
||||
}
|
||||
213
src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.cpp
Normal file
213
src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
#include "Decoder.hpp"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "Opcodes.hpp"
|
||||
|
||||
#include "Exceptions/DecodeFailure.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8::OpcodeDecoder
|
||||
{
|
||||
Decoder::InstructionMapping Decoder::decode(
|
||||
Targets::TargetMemoryAddress startByteAddress,
|
||||
const TargetMemoryBuffer& data,
|
||||
bool throwOnFailure
|
||||
) {
|
||||
auto output = Decoder::InstructionMapping{};
|
||||
|
||||
static const auto decoders = Decoder::opcodeDecoders();
|
||||
|
||||
auto instructionByteAddress = startByteAddress;
|
||||
auto dataIt = data.begin();
|
||||
const auto dataEndIt = data.end();
|
||||
|
||||
while (std::distance(dataIt, dataEndIt) >= 2) {
|
||||
auto opcodeMatched = false;
|
||||
|
||||
for (const auto& decoder : decoders) {
|
||||
auto instruction = decoder(dataIt, dataEndIt);
|
||||
|
||||
if (instruction.has_value()) {
|
||||
const auto instructionSize = instruction->byteSize;
|
||||
output.emplace(instructionByteAddress, std::move(*instruction));
|
||||
|
||||
dataIt += instructionSize;
|
||||
instructionByteAddress += instructionSize;
|
||||
opcodeMatched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opcodeMatched) {
|
||||
if (throwOnFailure) {
|
||||
throw Exceptions::DecodeFailure{
|
||||
instructionByteAddress,
|
||||
static_cast<std::uint32_t>(*(dataIt + 1) << 8) | *dataIt
|
||||
};
|
||||
}
|
||||
|
||||
output.emplace(instructionByteAddress, std::nullopt);
|
||||
|
||||
dataIt += 2;
|
||||
instructionByteAddress += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Decoder::OpcodeDecoders Decoder::opcodeDecoders() {
|
||||
/*
|
||||
* The decoders will be used in the order given here.
|
||||
*
|
||||
* I've used the same order that is used in the AVR implementation of GDB.
|
||||
*/
|
||||
return Decoder::OpcodeDecoders{
|
||||
std::bind(&Opcodes::UndefinedOrErased::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Clc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Clh::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cli::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cln::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cls::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Clt::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Clv::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Clz::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sec::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Seh::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sei::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sen::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Ses::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Set::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sev::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sez::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Bclr::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Bset::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Icall::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Ijmp::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lpm1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lpm2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lpm3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Elpm1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Elpm2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Elpm3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Nop::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Ret::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Reti::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sleep::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Break::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Wdr::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Spm1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Spm2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Adc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Add::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::And::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cp::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cpc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cpse::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Eor::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Mov::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Mul::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Or::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sub::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Clr::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lsl::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Rol::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Tst::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Andi::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cbr::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Ldi::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Ser::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Ori::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbr::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cpi::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbci::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Subi::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbrc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbrs::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Bld::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Bst::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::In::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Out::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Adiw::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbiw::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Cbi::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbi::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbic::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sbis::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brcc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brcs::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Breq::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brge::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brhc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brhs::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brid::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brie::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brlo::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brlt::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brmi::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brne::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brpl::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brsh::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brtc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brts::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brvc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brvs::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brbc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Brbs::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Rcall::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Rjmp::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Call::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Jmp::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Asr::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Com::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Dec::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Inc::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lsr::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Neg::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Pop::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Push::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Ror::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Swap::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Xch::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Las::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lac::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lat::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Movw::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Muls::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Mulsu::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Fmul::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Fmuls::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Fmulsu::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sts1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Sts2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lds1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Lds2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LddY::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LddZ::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdX1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdX2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdX3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdY1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdY2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdY3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdZ1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdZ2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::LdZ3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StdY::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StdZ::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StX1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StX2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StX3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StY1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StY2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StY3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StZ1::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StZ2::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::StZ3::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Eicall::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Eijmp::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&Opcodes::Des::decode, std::placeholders::_1, std::placeholders::_2),
|
||||
};
|
||||
}
|
||||
}
|
||||
56
src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp
Normal file
56
src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include "Instruction.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Services/BitsetService.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8::OpcodeDecoder
|
||||
{
|
||||
class Decoder
|
||||
{
|
||||
public:
|
||||
using InstructionMapping = std::map<Targets::TargetMemoryAddress, std::optional<Instruction>>;
|
||||
|
||||
/**
|
||||
* Attempts to decode AVR8 opcodes.
|
||||
*
|
||||
* @param startByteAddress
|
||||
* The start (byte) address of the memory given via the `data` param. This is used to calculate the address
|
||||
* of each instruction.
|
||||
*
|
||||
* @param data
|
||||
* The opcodes to decode. This is expected to be in LSB form, which is how opcodes are stored in AVR program
|
||||
* memory.
|
||||
*
|
||||
* @param throwOnFailure
|
||||
* If true, this function will throw a DecodeFailure exception, upon the first decode failure.
|
||||
*
|
||||
* @return
|
||||
* A mapping of std::optional<Instruction>, by their byte address. std::nullopt will be used for decode
|
||||
* failures (assuming `throwOnFailure` is false)
|
||||
*/
|
||||
static InstructionMapping decode(
|
||||
Targets::TargetMemoryAddress startByteAddress,
|
||||
const Targets::TargetMemoryBuffer& data,
|
||||
bool throwOnFailure = false
|
||||
);
|
||||
|
||||
private:
|
||||
using OpcodeDecoderFunction = std::function<
|
||||
std::optional<Instruction>(
|
||||
const Targets::TargetMemoryBuffer::const_iterator&,
|
||||
const Targets::TargetMemoryBuffer::const_iterator&
|
||||
)
|
||||
>;
|
||||
using OpcodeDecoders = std::array<Decoder::OpcodeDecoderFunction, 145>;
|
||||
|
||||
static OpcodeDecoders opcodeDecoders();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8::OpcodeDecoder::Exceptions
|
||||
{
|
||||
class DecodeFailure: public ::Exceptions::Exception
|
||||
{
|
||||
public:
|
||||
Targets::TargetMemoryAddress byteAddress;
|
||||
std::uint32_t opcode;
|
||||
|
||||
explicit DecodeFailure(Targets::TargetMemoryAddress byteAddress, std::uint32_t opcode)
|
||||
: ::Exceptions::Exception("Failed to decode AVR opcode")
|
||||
, byteAddress(byteAddress)
|
||||
, opcode(opcode)
|
||||
{}
|
||||
};
|
||||
}
|
||||
90
src/Targets/Microchip/AVR8/OpcodeDecoder/Instruction.hpp
Normal file
90
src/Targets/Microchip/AVR8/OpcodeDecoder/Instruction.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8::OpcodeDecoder
|
||||
{
|
||||
struct Instruction
|
||||
{
|
||||
enum class Mnemonic: std::uint8_t
|
||||
{
|
||||
ADC, ADD, ADIW, AND, ANDI, ASR, BCLR, BLD, BRBC, BRBS, BRCC, BRCS, BREAK, BREQ, BRGE, BRHC, BRHS, BRID,
|
||||
BRIE, BRLO, BRLT, BRMI, BRNE, BRPL, BRSH, BRTC, BRTS, BRVC, BRVS, BSET, BST, CALL, CBI, CBR, CLC, CLH,
|
||||
CLI, CLN, CLR, CLS, CLT, CLV, CLZ, COM, CP, CPC, CPI, CPSE, DEC, DES, EICALL, EIJMP, ELPM, EOR, FMUL,
|
||||
FMULS, FMULSU, ICALL, IJMP, IN, INC, JMP, LAC, LAS, LAT, LD, LDD, LDI, LDS, LPM, LSL, LSR, MOV, MOVW,
|
||||
MUL, MULS, MULSU, NEG, NOP, OR, ORI, OUT, POP, PUSH, RCALL, RET, RETI, RJMP, ROL, ROR, SBC, SBCI, SBI,
|
||||
SBIC, SBIS, SBIW, SBR, SBRC, SBRS, SEC, SEH, SEI, SEN, SER, SES, SET, SEV, SEZ, SLEEP, SPM, ST, STD, STS,
|
||||
SUB, SUBI, SWAP, TST, WDR, XCH,
|
||||
|
||||
/*
|
||||
* For undefined opcodes that we've had to define, because the hardware doesn't treat them as undefined.
|
||||
*
|
||||
* For example, see the 0xFFFF opcode.
|
||||
*/
|
||||
UNDEFINED,
|
||||
};
|
||||
|
||||
const std::string& name;
|
||||
std::uint32_t opcode;
|
||||
std::uint8_t byteSize;
|
||||
Mnemonic mnemonic;
|
||||
bool canChangeProgramFlow;
|
||||
|
||||
std::optional<std::uint32_t> data;
|
||||
|
||||
std::optional<std::uint8_t> sourceRegister;
|
||||
std::optional<std::uint8_t> destinationRegister;
|
||||
|
||||
std::optional<Targets::TargetMemoryAddress> programWordAddress;
|
||||
std::optional<std::int16_t> programWordAddressOffset;
|
||||
bool canSkipNextInstruction;
|
||||
|
||||
std::optional<std::uint8_t> registerBitPosition;
|
||||
std::optional<std::uint8_t> statusRegisterBitPosition;
|
||||
|
||||
std::optional<Targets::TargetMemoryAddress> ioSpaceAddress;
|
||||
std::optional<Targets::TargetMemoryAddress> dataSpaceAddress;
|
||||
|
||||
std::optional<std::uint8_t> displacement;
|
||||
|
||||
Instruction(
|
||||
const std::string& name,
|
||||
std::uint32_t opcode,
|
||||
std::uint8_t byteSize,
|
||||
Mnemonic mnemonic,
|
||||
bool canChangeProgramFlow,
|
||||
std::optional<std::uint32_t> data = std::nullopt,
|
||||
std::optional<std::uint8_t> sourceRegister = std::nullopt,
|
||||
std::optional<std::uint8_t> destinationRegister = std::nullopt,
|
||||
std::optional<Targets::TargetMemoryAddress> programWordAddress = std::nullopt,
|
||||
std::optional<std::int16_t> programWordAddressOffset = std::nullopt,
|
||||
bool canSkipNextInstruction = false,
|
||||
std::optional<std::uint8_t> registerBitPosition = std::nullopt,
|
||||
std::optional<std::uint8_t> statusRegisterBitPosition = std::nullopt,
|
||||
std::optional<Targets::TargetMemoryAddress> ioSpaceAddress = std::nullopt,
|
||||
std::optional<Targets::TargetMemoryAddress> dataSpaceAddress = std::nullopt,
|
||||
std::optional<std::uint8_t> displacement = std::nullopt
|
||||
)
|
||||
: name(name)
|
||||
, opcode(opcode)
|
||||
, byteSize(byteSize)
|
||||
, mnemonic(mnemonic)
|
||||
, canChangeProgramFlow(canChangeProgramFlow)
|
||||
, data(data)
|
||||
, sourceRegister(sourceRegister)
|
||||
, destinationRegister(destinationRegister)
|
||||
, programWordAddress(programWordAddress)
|
||||
, programWordAddressOffset(programWordAddressOffset)
|
||||
, canSkipNextInstruction(canSkipNextInstruction)
|
||||
, registerBitPosition(registerBitPosition)
|
||||
, statusRegisterBitPosition(statusRegisterBitPosition)
|
||||
, ioSpaceAddress(ioSpaceAddress)
|
||||
, dataSpaceAddress(dataSpaceAddress)
|
||||
, displacement(displacement)
|
||||
{}
|
||||
};
|
||||
}
|
||||
780
src/Targets/Microchip/AVR8/OpcodeDecoder/Opcode.hpp
Normal file
780
src/Targets/Microchip/AVR8/OpcodeDecoder/Opcode.hpp
Normal file
@@ -0,0 +1,780 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <iterator>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
#include "Instruction.hpp"
|
||||
#include "src/Helpers/FixedString.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Services/BitsetService.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8::OpcodeDecoder
|
||||
{
|
||||
struct InstructionParameterBase
|
||||
{};
|
||||
|
||||
template<std::size_t bitFieldRangeCount = 1>
|
||||
struct InstructionParameter: public InstructionParameterBase
|
||||
{
|
||||
/*
|
||||
* Not all instruction parameters are encoded in a consecutive manner. Some are scattered all over the place.
|
||||
*
|
||||
* For example, the CALL instruction accepts a single address parameter, but the 32-bit opcode looks
|
||||
* like this: 0b1001010kkkkk111kkkkkkkkkkkkkkkkk. Where the 'k's are the address parameter bits, which are not
|
||||
* encoded consecutively.
|
||||
*
|
||||
* There is one variation of the LD instruction that is even worse, where a single parameter is scattered in
|
||||
* three different locations of the opcode.
|
||||
*
|
||||
* Because parameter bits can be scattered, we must accept a set of BitFieldRanges, as opposed to just one.
|
||||
*/
|
||||
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount> bitFieldRanges;
|
||||
|
||||
const std::uint8_t length;
|
||||
const std::uint32_t mask;
|
||||
|
||||
constexpr InstructionParameter(
|
||||
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>& bitFieldRanges
|
||||
)
|
||||
: bitFieldRanges(bitFieldRanges)
|
||||
, length(Services::BitsetService::totalBitRangeLength(bitFieldRanges))
|
||||
, mask(Services::BitsetService::setBitField(std::uint32_t{0}, bitFieldRanges))
|
||||
{}
|
||||
|
||||
constexpr InstructionParameter(const Services::BitsetService::BitFieldRange& bitFieldRange)
|
||||
: InstructionParameter(
|
||||
std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>({bitFieldRange})
|
||||
)
|
||||
{}
|
||||
};
|
||||
|
||||
template<std::size_t bitFieldRangeCount = 1>
|
||||
struct RegisterParameter: public InstructionParameter<bitFieldRangeCount>
|
||||
{
|
||||
const std::uint8_t offset = 0;
|
||||
const bool pair = false;
|
||||
|
||||
constexpr RegisterParameter(
|
||||
std::uint8_t offset,
|
||||
bool pair,
|
||||
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>& bitFieldRanges
|
||||
)
|
||||
: InstructionParameter<bitFieldRangeCount>(bitFieldRanges)
|
||||
, offset(offset)
|
||||
, pair(pair)
|
||||
{}
|
||||
|
||||
constexpr RegisterParameter(
|
||||
std::uint8_t offset,
|
||||
bool pair,
|
||||
const Services::BitsetService::BitFieldRange& bitFieldRange
|
||||
)
|
||||
: InstructionParameter<bitFieldRangeCount>(bitFieldRange)
|
||||
, offset(offset)
|
||||
, pair(pair)
|
||||
{}
|
||||
|
||||
constexpr RegisterParameter(
|
||||
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>& bitFieldRanges
|
||||
)
|
||||
: InstructionParameter<bitFieldRangeCount>(bitFieldRanges)
|
||||
{}
|
||||
|
||||
constexpr RegisterParameter(const Services::BitsetService::BitFieldRange& bitFieldRange)
|
||||
: InstructionParameter<bitFieldRangeCount>(bitFieldRange)
|
||||
{}
|
||||
};
|
||||
|
||||
template<typename>
|
||||
struct IsRegisterParam: std::false_type {};
|
||||
|
||||
template<std::size_t bitFieldRangeCount>
|
||||
struct IsRegisterParam<RegisterParameter<bitFieldRangeCount>>: std::true_type {};
|
||||
|
||||
template<typename ParamType>
|
||||
requires (std::is_same_v<ParamType, std::nullopt_t> || std::is_base_of_v<InstructionParameterBase, ParamType>)
|
||||
class OptionalInstructionParameter
|
||||
{
|
||||
public:
|
||||
ParamType value;
|
||||
|
||||
constexpr OptionalInstructionParameter(ParamType value)
|
||||
: value(value)
|
||||
{};
|
||||
|
||||
static constexpr bool hasValue() {
|
||||
if constexpr (std::is_base_of_v<InstructionParameterBase, ParamType>) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Base for InstructionParameterMask(ParamType param, ParamPackType... params).
|
||||
*/
|
||||
template<typename ParamType>
|
||||
static constexpr std::uint32_t InstructionParameterMask(ParamType param) {
|
||||
if constexpr (!decltype(param)::hasValue()) {
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
return param.value.mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a combined (ORed) mask of the given params.
|
||||
*/
|
||||
template<typename ParamType, typename... ParamPackType>
|
||||
static constexpr std::uint32_t InstructionParameterMask(ParamType param, ParamPackType... params) {
|
||||
if constexpr (!decltype(param)::hasValue()) {
|
||||
return InstructionParameterMask(params...);
|
||||
|
||||
} else {
|
||||
return param.value.mask | InstructionParameterMask(params...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This template class is used to describe a particular AVR8 instruction opcode.
|
||||
*
|
||||
* It provides decoding abilities, as well as compile-time error checks.
|
||||
*
|
||||
* @tparam instructionName
|
||||
* The name of the AVR8 instruction, in the form of a string literal.
|
||||
*
|
||||
* @tparam expectedOpcode
|
||||
* The expected opcode of the instruction, in the form of a binary literal, with all parameter bits cleared (which
|
||||
* is enforced by template constraints).
|
||||
*
|
||||
* For example, consider the AVR8 OR instruction opcode: 0b001010rdddddrrrr, where 'r' and 'd' are source and
|
||||
* destination register parameters, respectively. The expectedOpcode value would be 0b0010100000000000.
|
||||
*
|
||||
* @tparam wordSize
|
||||
* The word size of the instruction. AVR8 instructions are either 1 or 2 words.
|
||||
*
|
||||
* @tparam mnemonic
|
||||
* The instruction's mnemonic.
|
||||
*
|
||||
* @tparam canChangeProgramFlow
|
||||
* Whether the instruction **can** change program flow.
|
||||
*
|
||||
* If the instruction can change the program counter to anything other than PC + n, where n is the word size of
|
||||
* the instruction, then this should be set to true.
|
||||
*
|
||||
* @tparam sourceRegisterParameter
|
||||
* If the instruction encodes a source register parameter (like the OR instruction), this should be provided here.
|
||||
* Otherwise, std::nullopt.
|
||||
*
|
||||
* Register parameters have some additional information we need (e.g. offsets, or if they should be interpreted as
|
||||
* register pairs). For this reason, register parameters should be provided as instances of RegisterParameter (as
|
||||
* opposed to instances of InstructionParameter).
|
||||
*
|
||||
* @tparam destinationRegisterParameter
|
||||
* If the instruction encodes a destination register parameter (like the OR instruction), this should be provided
|
||||
* here. Otherwise, std::nullopt.
|
||||
*
|
||||
* This should be an instance of RegisterParameter. See above.
|
||||
*
|
||||
* @tparam dataParameter
|
||||
* If the instruction encodes a constant data parameter (like the ADIW instruction), this should be provided here.
|
||||
* Otherwise, std::nullopt.
|
||||
*
|
||||
* @tparam programAddressParameter
|
||||
* If the instruction encodes a constant program word address parameter (like the CALL instruction), this should
|
||||
* be provided here. Otherwise, std::nullopt.
|
||||
*
|
||||
* If this parameter is provided, Opcode::canChangeProgramFlow must be set to true. This is enforced by template
|
||||
* constraints.
|
||||
*
|
||||
* @tparam programAddressOffsetParameter
|
||||
* If the instruction encodes a constant program word address offset parameter (like the RJMP instruction), this
|
||||
* should be provided here. Otherwise, std::nullopt.
|
||||
*
|
||||
* If this parameter is provided, Opcode::canChangeProgramFlow must be set to true. This is enforced by template
|
||||
* constraints.
|
||||
*
|
||||
* @tparam registerBitPositionParameter
|
||||
* If the instruction encodes a register bit position parameter (like the SBIS instruction), this should be
|
||||
* provided here. Otherwise, std::nullopt.
|
||||
*
|
||||
* @tparam statusRegisterBitPositionParameter
|
||||
* If the instruction encodes a status register bit position parameter (like the BRBS instruction), this should be
|
||||
* provided here. Otherwise, std::nullopt.
|
||||
*
|
||||
* @tparam ioSpaceAddressParameter
|
||||
* If the instruction encodes an I/O space parameter (like the IN instruction), this should be provided here.
|
||||
* Otherwise, std::nullopt.
|
||||
*
|
||||
* @tparam dataSpaceAddressParameter
|
||||
* If the instruction encodes a data space address parameter (like the STS instruction), this should be provided
|
||||
* here. Otherwise, std::nullopt.
|
||||
*
|
||||
* @tparam displacementParameter
|
||||
* If the instruction encodes a displacement parameter (like the STD instruction), this should be provided here.
|
||||
* Otherwise, std::nullopt.
|
||||
*
|
||||
* @tparam canSkipNextInstruction
|
||||
* Whether the instruction can skip the following instruction.
|
||||
*
|
||||
* If this is set to true, Opcode::canChangeProgramFlow must also be set to true. This is enforced by template
|
||||
* constraints.
|
||||
*/
|
||||
template <
|
||||
FixedString instructionName,
|
||||
std::uint32_t expectedOpcode,
|
||||
int wordSize,
|
||||
Instruction::Mnemonic mnemonic,
|
||||
bool canChangeProgramFlow,
|
||||
OptionalInstructionParameter sourceRegisterParameter = std::nullopt,
|
||||
OptionalInstructionParameter destinationRegisterParameter = std::nullopt,
|
||||
OptionalInstructionParameter dataParameter = std::nullopt,
|
||||
OptionalInstructionParameter programAddressParameter = std::nullopt,
|
||||
OptionalInstructionParameter programAddressOffsetParameter = std::nullopt,
|
||||
OptionalInstructionParameter registerBitPositionParameter = std::nullopt,
|
||||
OptionalInstructionParameter statusRegisterBitPositionParameter = std::nullopt,
|
||||
OptionalInstructionParameter ioSpaceAddressParameter = std::nullopt,
|
||||
OptionalInstructionParameter dataSpaceAddressParameter = std::nullopt,
|
||||
OptionalInstructionParameter displacementParameter = std::nullopt,
|
||||
bool canSkipNextInstruction = false
|
||||
>
|
||||
requires
|
||||
(wordSize == 1 || wordSize == 2)
|
||||
&& (wordSize == 2 || expectedOpcode <= 0x0000FFFF)
|
||||
/*
|
||||
* All parameter bits in expectedOpcode should be cleared. We enforce this here.
|
||||
*/
|
||||
&& (
|
||||
!decltype(sourceRegisterParameter)::hasValue()
|
||||
|| ((expectedOpcode & sourceRegisterParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(destinationRegisterParameter)::hasValue()
|
||||
|| ((expectedOpcode & destinationRegisterParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(dataParameter)::hasValue()
|
||||
|| ((expectedOpcode & dataParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(programAddressParameter)::hasValue()
|
||||
|| ((expectedOpcode & programAddressParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(programAddressOffsetParameter)::hasValue()
|
||||
|| ((expectedOpcode & programAddressOffsetParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(registerBitPositionParameter)::hasValue()
|
||||
|| ((expectedOpcode & registerBitPositionParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(statusRegisterBitPositionParameter)::hasValue()
|
||||
|| ((expectedOpcode & statusRegisterBitPositionParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(ioSpaceAddressParameter)::hasValue()
|
||||
|| ((expectedOpcode & ioSpaceAddressParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(dataSpaceAddressParameter)::hasValue()
|
||||
|| ((expectedOpcode & dataSpaceAddressParameter.value.mask) == 0)
|
||||
)
|
||||
&& (
|
||||
!decltype(displacementParameter)::hasValue()
|
||||
|| ((expectedOpcode & displacementParameter.value.mask) == 0)
|
||||
)
|
||||
/*
|
||||
* We need additional info for register parameters, which should be provided via the RegisterParameter type.
|
||||
*/
|
||||
&& (
|
||||
!decltype(sourceRegisterParameter)::hasValue()
|
||||
|| IsRegisterParam<decltype(sourceRegisterParameter.value)>::value
|
||||
)
|
||||
&& (
|
||||
!decltype(destinationRegisterParameter)::hasValue()
|
||||
|| IsRegisterParam<decltype(destinationRegisterParameter.value)>::value
|
||||
)
|
||||
/*
|
||||
* Parameters should not collide (share opcode bits) with others. We detect any collisions here.
|
||||
*/
|
||||
&& (
|
||||
!decltype(sourceRegisterParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & sourceRegisterParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(destinationRegisterParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & destinationRegisterParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(dataParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & dataParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(programAddressParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & programAddressParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(programAddressOffsetParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & programAddressOffsetParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(registerBitPositionParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & registerBitPositionParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(statusRegisterBitPositionParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & statusRegisterBitPositionParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(ioSpaceAddressParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & ioSpaceAddressParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(dataSpaceAddressParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
displacementParameter
|
||||
) & dataSpaceAddressParameter.value.mask) == 0
|
||||
)
|
||||
&& (
|
||||
!decltype(displacementParameter)::hasValue()
|
||||
|| (InstructionParameterMask(
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter
|
||||
) & displacementParameter.value.mask) == 0
|
||||
)
|
||||
/*
|
||||
* Instructions that encode a program address/offset, or skip the next instruction, can change program flow
|
||||
* and should be marked as such. This is enforced here.
|
||||
*/
|
||||
&& (!decltype(programAddressParameter)::hasValue() || canChangeProgramFlow)
|
||||
&& (!decltype(programAddressOffsetParameter)::hasValue() || canChangeProgramFlow)
|
||||
&& (!canSkipNextInstruction || canChangeProgramFlow)
|
||||
class Opcode
|
||||
{
|
||||
using SelfType = Opcode<
|
||||
instructionName,
|
||||
expectedOpcode,
|
||||
wordSize,
|
||||
mnemonic,
|
||||
canChangeProgramFlow,
|
||||
sourceRegisterParameter,
|
||||
destinationRegisterParameter,
|
||||
dataParameter,
|
||||
programAddressParameter,
|
||||
programAddressOffsetParameter,
|
||||
registerBitPositionParameter,
|
||||
statusRegisterBitPositionParameter,
|
||||
ioSpaceAddressParameter,
|
||||
dataSpaceAddressParameter,
|
||||
displacementParameter,
|
||||
canSkipNextInstruction
|
||||
>;
|
||||
using OpcodeDataType = std::conditional_t<(wordSize > 1), std::uint32_t, std::uint16_t>;
|
||||
|
||||
/**
|
||||
* We hold a single instance of the instruction name here, as a const static member, and then pass it by
|
||||
* reference to any new instances of the OpcodeDecoder::Instruction struct (which holds a const reference).
|
||||
*/
|
||||
static const inline std::string name = instructionName.value;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Attempts to match and decode the first opcode that resides between the two iterators.
|
||||
*
|
||||
* @param dataBegin
|
||||
* An iterator addressing some byte in the target's program memory. Keep in mind that AVR opcodes are stored
|
||||
* in LSB form, so this iterator should address the least significant byte of the opcode.
|
||||
*
|
||||
* @param dataEnd
|
||||
* An iterator addressing the byte at which decoding should stop, or just the end of the program memory.
|
||||
*
|
||||
* @return
|
||||
* An instance of the OpcodeDecoder::Instruction struct, if the opcode was successfully matched and decoded.
|
||||
* Otherwise, std::nullopt.
|
||||
*/
|
||||
static std::optional<Instruction> decode(
|
||||
const Targets::TargetMemoryBuffer::const_iterator& dataBegin,
|
||||
const Targets::TargetMemoryBuffer::const_iterator& dataEnd
|
||||
) {
|
||||
using Services::BitsetService;
|
||||
|
||||
constexpr auto byteSize = wordSize * 2;
|
||||
if (std::distance(dataBegin, dataEnd) < byteSize) {
|
||||
// There isn't enough data to hold this instruction.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto opcode = static_cast<OpcodeDataType>(*(dataBegin + 1) << 8 | *dataBegin);
|
||||
|
||||
if constexpr (wordSize == 2) {
|
||||
opcode = (opcode << 16) | static_cast<OpcodeDataType>(*(dataBegin + 3) << 8 | *(dataBegin + 2));
|
||||
}
|
||||
|
||||
if ((opcode & SelfType::opcodeMask()) != static_cast<OpcodeDataType>(expectedOpcode)) {
|
||||
// Opcode mismatch
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto output = Instruction{
|
||||
SelfType::name,
|
||||
opcode,
|
||||
byteSize,
|
||||
mnemonic,
|
||||
canChangeProgramFlow
|
||||
};
|
||||
|
||||
if constexpr (decltype(sourceRegisterParameter)::hasValue()) {
|
||||
constexpr auto param = sourceRegisterParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::sourceRegister)::value_type) * 8,
|
||||
"Invalid instruction sourceRegisterParameter - param length exceeds Instruction::sourceRegister size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction sourceRegisterParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
auto value = BitsetService::extractBitField(opcode, param.bitFieldRanges);
|
||||
|
||||
if constexpr (param.pair) {
|
||||
value = value * 2;
|
||||
}
|
||||
|
||||
output.sourceRegister = static_cast<decltype(Instruction::sourceRegister)::value_type>(
|
||||
value + param.offset
|
||||
);
|
||||
}
|
||||
|
||||
if constexpr (decltype(destinationRegisterParameter)::hasValue()) {
|
||||
constexpr auto param = destinationRegisterParameter.value;
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction destinationRegisterParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
auto value = BitsetService::extractBitField(opcode, param.bitFieldRanges);
|
||||
|
||||
if constexpr (param.pair) {
|
||||
value = value * 2;
|
||||
}
|
||||
|
||||
output.destinationRegister = static_cast<decltype(Instruction::destinationRegister)::value_type>(
|
||||
value + param.offset
|
||||
);
|
||||
}
|
||||
|
||||
if constexpr (decltype(dataParameter)::hasValue()) {
|
||||
constexpr auto param = dataParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::data)::value_type) * 8,
|
||||
"Invalid instruction dataParameter - param length exceeds Instruction::data size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction dataParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
output.data = static_cast<decltype(Instruction::data)::value_type>(
|
||||
BitsetService::extractBitField(opcode, param.bitFieldRanges)
|
||||
);
|
||||
}
|
||||
|
||||
if constexpr (decltype(programAddressParameter)::hasValue()) {
|
||||
constexpr auto param = programAddressParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::programWordAddress)::value_type) * 8,
|
||||
"Invalid instruction programAddressParameter - param length exceeds "
|
||||
"Instruction::destinationWordAddress size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction programAddressParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
output.programWordAddress = BitsetService::extractBitField(opcode, param.bitFieldRanges);
|
||||
}
|
||||
|
||||
if constexpr (decltype(programAddressOffsetParameter)::hasValue()) {
|
||||
constexpr auto param = programAddressOffsetParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::programWordAddressOffset)::value_type) * 8,
|
||||
"Invalid instruction programAddressOffsetParameter - param length exceeds "
|
||||
"Instruction::destinationWordAddressOffset size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction programAddressOffsetParameter - param length exceeds available bits"
|
||||
);
|
||||
|
||||
auto addressOffset = static_cast<std::int16_t>(
|
||||
BitsetService::extractBitField(opcode, param.bitFieldRanges)
|
||||
);
|
||||
|
||||
const auto signBitMask = static_cast<std::int16_t>(0x01 << (param.length - 1));
|
||||
if (addressOffset & signBitMask) {
|
||||
// Sign extending required
|
||||
addressOffset |= static_cast<std::int16_t>(-1LL) << param.length;
|
||||
}
|
||||
|
||||
output.programWordAddressOffset = addressOffset;
|
||||
}
|
||||
|
||||
if constexpr (decltype(registerBitPositionParameter)::hasValue()) {
|
||||
constexpr auto param = registerBitPositionParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::registerBitPosition)::value_type) * 8,
|
||||
"Invalid instruction registerBitPositionParameter - param length exceeds "
|
||||
"Instruction::registerBitPosition size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction registerBitPositionParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
output.registerBitPosition = static_cast<decltype(Instruction::registerBitPosition)::value_type>(
|
||||
BitsetService::extractBitField(opcode, param.bitFieldRanges)
|
||||
);
|
||||
}
|
||||
|
||||
if constexpr (decltype(statusRegisterBitPositionParameter)::hasValue()) {
|
||||
constexpr auto param = statusRegisterBitPositionParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::statusRegisterBitPosition)::value_type) * 8,
|
||||
"Invalid instruction statusRegisterBitPositionParameter - param length exceeds "
|
||||
"Instruction::statusRegisterBitPosition size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction statusRegisterBitPositionParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
output.statusRegisterBitPosition = static_cast<
|
||||
decltype(Instruction::statusRegisterBitPosition)::value_type
|
||||
>(
|
||||
BitsetService::extractBitField(opcode, param.bitFieldRanges)
|
||||
);
|
||||
}
|
||||
|
||||
if constexpr (decltype(ioSpaceAddressParameter)::hasValue()) {
|
||||
constexpr auto param = ioSpaceAddressParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::ioSpaceAddress)::value_type) * 8,
|
||||
"Invalid instruction ioSpaceAddressParameter - param length exceeds Instruction::ioSpaceAddress "
|
||||
"size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction ioSpaceAddressParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
output.ioSpaceAddress = BitsetService::extractBitField(opcode, param.bitFieldRanges);
|
||||
}
|
||||
|
||||
if constexpr (decltype(dataSpaceAddressParameter)::hasValue()) {
|
||||
constexpr auto param = dataSpaceAddressParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::dataSpaceAddress)::value_type) * 8,
|
||||
"Invalid instruction dataSpaceAddressParameter - param length exceeds Instruction::dataSpaceAddress "
|
||||
"size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction dataSpaceAddressParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
output.dataSpaceAddress = BitsetService::extractBitField(opcode, param.bitFieldRanges);
|
||||
}
|
||||
|
||||
if constexpr (decltype(displacementParameter)::hasValue()) {
|
||||
constexpr auto param = displacementParameter.value;
|
||||
|
||||
static_assert(
|
||||
param.length <= sizeof(decltype(Instruction::displacement)::value_type) * 8,
|
||||
"Invalid instruction displacementParameter - param length exceeds Instruction::displacement size"
|
||||
);
|
||||
|
||||
static_assert(
|
||||
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
|
||||
"Invalid instruction displacementParameter - parameter length exceeds available bits"
|
||||
);
|
||||
|
||||
output.displacement = BitsetService::extractBitField(opcode, param.bitFieldRanges);
|
||||
}
|
||||
|
||||
output.canSkipNextInstruction = canSkipNextInstruction;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private:
|
||||
static OpcodeDataType opcodeMask() {
|
||||
using Services::BitsetService;
|
||||
|
||||
auto opcodeMask = static_cast<OpcodeDataType>(-1LL);
|
||||
|
||||
if constexpr (decltype(sourceRegisterParameter)::hasValue()) {
|
||||
opcodeMask &= ~(sourceRegisterParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(destinationRegisterParameter)::hasValue()) {
|
||||
opcodeMask &= ~(destinationRegisterParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(dataParameter)::hasValue()) {
|
||||
opcodeMask &= ~(dataParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(programAddressParameter)::hasValue()) {
|
||||
opcodeMask &= ~(programAddressParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(programAddressOffsetParameter)::hasValue()) {
|
||||
opcodeMask &= ~(programAddressOffsetParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(registerBitPositionParameter)::hasValue()) {
|
||||
opcodeMask &= ~(registerBitPositionParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(statusRegisterBitPositionParameter)::hasValue()) {
|
||||
opcodeMask &= ~(statusRegisterBitPositionParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(ioSpaceAddressParameter)::hasValue()) {
|
||||
opcodeMask &= ~(ioSpaceAddressParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(dataSpaceAddressParameter)::hasValue()) {
|
||||
opcodeMask &= ~(dataSpaceAddressParameter.value.mask);
|
||||
}
|
||||
|
||||
if constexpr (decltype(displacementParameter)::hasValue()) {
|
||||
opcodeMask &= ~(displacementParameter.value.mask);
|
||||
}
|
||||
|
||||
return opcodeMask;
|
||||
}
|
||||
};
|
||||
}
|
||||
1734
src/Targets/Microchip/AVR8/OpcodeDecoder/Opcodes.hpp
Normal file
1734
src/Targets/Microchip/AVR8/OpcodeDecoder/Opcodes.hpp
Normal file
File diff suppressed because it is too large
Load Diff
12
src/Targets/Microchip/AVR8/ProgramMemorySection.hpp
Normal file
12
src/Targets/Microchip/AVR8/ProgramMemorySection.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
enum class ProgramMemorySection: std::uint8_t
|
||||
{
|
||||
APPLICATION,
|
||||
BOOT,
|
||||
};
|
||||
}
|
||||
23
src/Targets/Microchip/AVR8/ProgrammingSession.hpp
Normal file
23
src/Targets/Microchip/AVR8/ProgrammingSession.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* Information relating to a specific AVR8 programming session.
|
||||
*
|
||||
* Whenever an AVR8 target is put into programming mode, a new programming session is started.
|
||||
*/
|
||||
struct ProgrammingSession
|
||||
{
|
||||
/**
|
||||
* This flag indicates whether we need to manage the EESAVE fuse for this programming session. It will only be
|
||||
* set to true if the user has chosen to preserve EEPROM and the EESAVE fuse wasn't already programmed when the
|
||||
* programming session started.
|
||||
*/
|
||||
bool managingEesaveFuseBit = false;
|
||||
|
||||
ProgrammingSession() = default;
|
||||
};
|
||||
}
|
||||
195
src/Targets/Microchip/AVR8/TargetDescriptionFile.cpp
Normal file
195
src/Targets/Microchip/AVR8/TargetDescriptionFile.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include "TargetDescriptionFile.hpp"
|
||||
|
||||
#include "src/Services/PathService.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Targets/TargetDescription/Exceptions/TargetDescriptionParsingFailureException.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
using Targets::TargetDescription::RegisterGroup;
|
||||
using Targets::TargetDescription::AddressSpace;
|
||||
using Targets::TargetDescription::MemorySegment;
|
||||
using Targets::TargetDescription::Register;
|
||||
using Targets::TargetDescription::Exceptions::InvalidTargetDescriptionDataException;
|
||||
using Targets::TargetRegisterDescriptor;
|
||||
using Services::StringService;
|
||||
|
||||
TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath)
|
||||
: Targets::TargetDescription::TargetDescriptionFile(xmlFilePath)
|
||||
{}
|
||||
|
||||
TargetSignature TargetDescriptionFile::getTargetSignature() const {
|
||||
const auto& signatureGroup = this->getPropertyGroup("signatures");
|
||||
|
||||
return {
|
||||
StringService::toUint8(signatureGroup.getProperty("signature0").value, 16),
|
||||
StringService::toUint8(signatureGroup.getProperty("signature1").value, 16),
|
||||
StringService::toUint8(signatureGroup.getProperty("signature2").value, 16)
|
||||
};
|
||||
}
|
||||
|
||||
Family TargetDescriptionFile::getAvrFamily() const {
|
||||
static const auto targetFamiliesByName = std::map<std::string, Family>{
|
||||
{"MEGA", Family::MEGA},
|
||||
{"XMEGA", Family::XMEGA},
|
||||
{"TINY", Family::TINY},
|
||||
{"DA", Family::DA},
|
||||
{"DB", Family::DB},
|
||||
{"DD", Family::DD},
|
||||
{"EA", Family::EA},
|
||||
};
|
||||
|
||||
const auto familyIt = targetFamiliesByName.find(this->getDeviceAttribute("avr-family"));
|
||||
if (familyIt == targetFamiliesByName.end()) {
|
||||
throw InvalidTargetDescriptionDataException{"Unknown AVR family name in target description file"};
|
||||
}
|
||||
|
||||
return familyIt->second;
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getRegisterFileAddressSpace() const {
|
||||
/*
|
||||
* On some AVRs, the register file is accessible via the data address space. On the newer UPDI and PDI AVRs,
|
||||
* it has a dedicated address space.
|
||||
*/
|
||||
const auto addressSpace = this->tryGetAddressSpace("register_file");
|
||||
return addressSpace.has_value()
|
||||
? addressSpace->get()
|
||||
: this->getAddressSpace("data");
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getProgramAddressSpace() const {
|
||||
return this->getAddressSpace("prog");
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getDataAddressSpace() const {
|
||||
return this->getAddressSpace("data");
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getEepromAddressSpace() const {
|
||||
const auto addressSpace = this->tryGetAddressSpace("eeprom");
|
||||
return addressSpace.has_value()
|
||||
? addressSpace->get()
|
||||
: this->getAddressSpace("data");
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getIoAddressSpace() const {
|
||||
return this->getAddressSpace("data");
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getSignatureAddressSpace() const {
|
||||
const auto addressSpace = this->tryGetAddressSpace("signatures");
|
||||
return addressSpace.has_value()
|
||||
? addressSpace->get()
|
||||
: this->getAddressSpace("data");
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getFuseAddressSpace() const {
|
||||
const auto addressSpace = this->tryGetAddressSpace("fuses");
|
||||
return addressSpace.has_value()
|
||||
? addressSpace->get()
|
||||
: this->getAddressSpace("data");
|
||||
}
|
||||
|
||||
const TargetDescription::AddressSpace& TargetDescriptionFile::getLockbitAddressSpace() const {
|
||||
const auto addressSpace = this->tryGetAddressSpace("lockbits");
|
||||
return addressSpace.has_value()
|
||||
? addressSpace->get()
|
||||
: this->getAddressSpace("data");
|
||||
}
|
||||
|
||||
const TargetDescription::MemorySegment& TargetDescriptionFile::getProgramMemorySegment() const {
|
||||
return this->getProgramAddressSpace().getMemorySegment("internal_program_memory");
|
||||
}
|
||||
|
||||
const TargetDescription::MemorySegment& TargetDescriptionFile::getRamMemorySegment() const {
|
||||
return this->getDataAddressSpace().getMemorySegment("internal_ram");
|
||||
}
|
||||
|
||||
const TargetDescription::MemorySegment& TargetDescriptionFile::getEepromMemorySegment() const {
|
||||
return this->getEepromAddressSpace().getMemorySegment("internal_eeprom");
|
||||
}
|
||||
|
||||
const TargetDescription::MemorySegment& TargetDescriptionFile::getIoMemorySegment() const {
|
||||
const auto addressSpace = this->getIoAddressSpace();
|
||||
const auto segment = addressSpace.tryGetMemorySegment("io");
|
||||
return segment.has_value()
|
||||
? segment->get()
|
||||
: addressSpace.getMemorySegment("mapped_io");
|
||||
}
|
||||
|
||||
const TargetDescription::MemorySegment& TargetDescriptionFile::getSignatureMemorySegment() const {
|
||||
return this->getSignatureAddressSpace().getMemorySegment("signatures");
|
||||
}
|
||||
|
||||
const TargetDescription::MemorySegment& TargetDescriptionFile::getFuseMemorySegment() const {
|
||||
return this->getFuseAddressSpace().getMemorySegment("fuses");
|
||||
}
|
||||
|
||||
const TargetDescription::MemorySegment& TargetDescriptionFile::getLockbitMemorySegment() const {
|
||||
return this->getLockbitAddressSpace().getMemorySegment("lockbits");
|
||||
}
|
||||
|
||||
TargetAddressSpaceDescriptor TargetDescriptionFile::getDataAddressSpaceDescriptor() const {
|
||||
return this->targetAddressSpaceDescriptorFromAddressSpace(this->getDataAddressSpace());
|
||||
}
|
||||
|
||||
TargetAddressSpaceDescriptor TargetDescriptionFile::getFuseAddressSpaceDescriptor() const {
|
||||
return this->targetAddressSpaceDescriptorFromAddressSpace(this->getFuseAddressSpace());
|
||||
}
|
||||
|
||||
TargetMemorySegmentDescriptor TargetDescriptionFile::getRamMemorySegmentDescriptor() const {
|
||||
return this->targetMemorySegmentDescriptorFromMemorySegment(
|
||||
this->getRamMemorySegment(),
|
||||
this->getDataAddressSpace()
|
||||
);
|
||||
}
|
||||
|
||||
TargetMemorySegmentDescriptor TargetDescriptionFile::getFuseMemorySegmentDescriptor() const {
|
||||
return this->targetMemorySegmentDescriptorFromMemorySegment(
|
||||
this->getFuseMemorySegment(),
|
||||
this->getFuseAddressSpace()
|
||||
);
|
||||
}
|
||||
|
||||
TargetMemorySegmentDescriptor TargetDescriptionFile::getIoMemorySegmentDescriptor() const {
|
||||
return this->targetMemorySegmentDescriptorFromMemorySegment(
|
||||
this->getIoMemorySegment(),
|
||||
this->getIoAddressSpace()
|
||||
);
|
||||
}
|
||||
|
||||
TargetPeripheralDescriptor TargetDescriptionFile::getFuseTargetPeripheralDescriptor() const {
|
||||
return this->getTargetPeripheralDescriptor("fuse");
|
||||
}
|
||||
|
||||
Pair<
|
||||
TargetRegisterDescriptor,
|
||||
TargetBitFieldDescriptor
|
||||
> TargetDescriptionFile::getFuseRegisterBitFieldDescriptorPair(const std::string& fuseBitFieldKey) const {
|
||||
const auto peripheralDescriptor = this->getFuseTargetPeripheralDescriptor();
|
||||
const auto pair = peripheralDescriptor.getRegisterGroupDescriptor("fuse").getRegisterBitFieldDescriptorPair(
|
||||
fuseBitFieldKey
|
||||
);
|
||||
|
||||
return {pair.first.clone(), pair.second.clone()};
|
||||
}
|
||||
|
||||
std::optional<FuseEnableStrategy> TargetDescriptionFile::getFuseEnableStrategy() const {
|
||||
const auto fuseEnabledValueProperty = this->tryGetProperty("programming_info", "fuse_enabled_value");
|
||||
if (fuseEnabledValueProperty.has_value()) {
|
||||
if (fuseEnabledValueProperty->get().value == "0") {
|
||||
return FuseEnableStrategy::CLEAR;
|
||||
}
|
||||
|
||||
if (fuseEnabledValueProperty->get().value == "1") {
|
||||
return FuseEnableStrategy::SET;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
85
src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp
Normal file
85
src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp"
|
||||
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR8/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Fuse.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR8/Family.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* AVR8 TDF
|
||||
*
|
||||
* For more information of TDFs, see src/Targets/TargetDescription/README.md
|
||||
*/
|
||||
class TargetDescriptionFile: public TargetDescription::TargetDescriptionFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Extends TDF initialisation to include the loading of physical interfaces for debugging AVR8 targets, among
|
||||
* other things.
|
||||
*
|
||||
* @param xml
|
||||
*/
|
||||
explicit TargetDescriptionFile(const std::string& xmlFilePath);
|
||||
|
||||
/**
|
||||
* Extracts the AVR8 target signature from the TDF.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] TargetSignature getTargetSignature() const;
|
||||
|
||||
/**
|
||||
* Extracts the AVR8 target family from the TDF.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] Family getAvrFamily() const;
|
||||
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getRegisterFileAddressSpace() const;
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getProgramAddressSpace() const;
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getDataAddressSpace() const;
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getEepromAddressSpace() const;
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getIoAddressSpace() const;
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getSignatureAddressSpace() const;
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getFuseAddressSpace() const;
|
||||
[[nodiscard]] const TargetDescription::AddressSpace& getLockbitAddressSpace() const;
|
||||
|
||||
[[nodiscard]] const TargetDescription::MemorySegment& getProgramMemorySegment() const;
|
||||
[[nodiscard]] const TargetDescription::MemorySegment& getRamMemorySegment() const;
|
||||
[[nodiscard]] const TargetDescription::MemorySegment& getEepromMemorySegment() const;
|
||||
[[nodiscard]] const TargetDescription::MemorySegment& getIoMemorySegment() const;
|
||||
[[nodiscard]] const TargetDescription::MemorySegment& getSignatureMemorySegment() const;
|
||||
[[nodiscard]] const TargetDescription::MemorySegment& getFuseMemorySegment() const;
|
||||
[[nodiscard]] const TargetDescription::MemorySegment& getLockbitMemorySegment() const;
|
||||
|
||||
[[nodiscard]] TargetAddressSpaceDescriptor getDataAddressSpaceDescriptor() const;
|
||||
[[nodiscard]] TargetAddressSpaceDescriptor getFuseAddressSpaceDescriptor() const;
|
||||
|
||||
[[nodiscard]] TargetMemorySegmentDescriptor getRamMemorySegmentDescriptor() const;
|
||||
[[nodiscard]] TargetMemorySegmentDescriptor getFuseMemorySegmentDescriptor() const;
|
||||
[[nodiscard]] TargetMemorySegmentDescriptor getIoMemorySegmentDescriptor() const;
|
||||
|
||||
[[nodiscard]] TargetPeripheralDescriptor getFuseTargetPeripheralDescriptor() const;
|
||||
[[nodiscard]] Pair<
|
||||
TargetRegisterDescriptor,
|
||||
TargetBitFieldDescriptor
|
||||
> getFuseRegisterBitFieldDescriptorPair(const std::string& fuseBitFieldKey) 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;
|
||||
};
|
||||
}
|
||||
64
src/Targets/Microchip/AVR8/TargetSignature.hpp
Normal file
64
src/Targets/Microchip/AVR8/TargetSignature.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
namespace Targets::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* All AVR8 targets carry a three-byte signature that is *usually* unique to the target.
|
||||
*
|
||||
* The AVR target signature consists of three bytes: 0xAABBCC
|
||||
* Byte AA (byteZero) identifies the manufacture of the target (usually 1E for Atmel/Microchip)
|
||||
* Byte BB (byteOne) sometimes indicates the flash/ram capacity of the target
|
||||
* Byte CC (byteTwo) identifies the target
|
||||
*
|
||||
* Some AVR targets have been found to carry identical signatures. For example, the AT90PWM1, AT90PWM2B
|
||||
* and the AT90PWM3B all carry a signature of 0x1E9383.
|
||||
*/
|
||||
struct TargetSignature
|
||||
{
|
||||
unsigned char byteZero = 0x00;
|
||||
unsigned char byteOne = 0x00;
|
||||
unsigned char byteTwo = 0x00;
|
||||
|
||||
TargetSignature() = default;
|
||||
TargetSignature(unsigned char byteZero, unsigned char byteOne, unsigned char byteTwo)
|
||||
: byteZero(byteZero)
|
||||
, byteOne(byteOne)
|
||||
, byteTwo(byteTwo)
|
||||
{};
|
||||
|
||||
explicit TargetSignature(const std::string& hex) {
|
||||
const auto signature = Services::StringService::toUint32(hex, 16);
|
||||
|
||||
this->byteZero = static_cast<unsigned char>(signature >> 16);
|
||||
this->byteOne = static_cast<unsigned char>(signature >> 8);
|
||||
this->byteTwo = static_cast<unsigned char>(signature);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toHex() const {
|
||||
auto stream = std::stringstream{};
|
||||
stream << std::hex << std::setfill('0');
|
||||
stream << std::setw(2) << static_cast<unsigned int>(this->byteZero);
|
||||
stream << std::setw(2) << static_cast<unsigned int>(this->byteOne);
|
||||
stream << std::setw(2) << static_cast<unsigned int>(this->byteTwo);
|
||||
|
||||
return "0x" + stream.str();
|
||||
}
|
||||
|
||||
bool operator == (const TargetSignature& signature) const {
|
||||
return
|
||||
signature.byteZero == this->byteZero
|
||||
&& signature.byteOne == this->byteOne
|
||||
&& signature.byteTwo == this->byteTwo;
|
||||
}
|
||||
|
||||
bool operator != (const TargetSignature& signature) const {
|
||||
return !(*this == signature);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user