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:
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule
|
||||
{
|
||||
using RegisterAddress = std::uint8_t;
|
||||
using RegisterValue = std::uint32_t;
|
||||
using HartIndex = std::uint32_t;
|
||||
|
||||
enum class DmiOperation: std::uint8_t
|
||||
{
|
||||
IGNORE = 0x00,
|
||||
READ = 0x01,
|
||||
WRITE = 0x02,
|
||||
};
|
||||
|
||||
enum class DmiOperationStatus: std::uint8_t
|
||||
{
|
||||
SUCCESS = 0x00,
|
||||
FAILED = 0x02,
|
||||
BUSY = 0x03,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct AbstractCommandRegister
|
||||
{
|
||||
enum CommandType: std::uint8_t
|
||||
{
|
||||
REGISTER_ACCESS = 0x00,
|
||||
QUICK_ACCESS = 0x01,
|
||||
MEMORY_ACCESS = 0x02,
|
||||
};
|
||||
|
||||
std::uint32_t control = 0;
|
||||
CommandType commandType = CommandType::REGISTER_ACCESS;
|
||||
|
||||
AbstractCommandRegister() = default;
|
||||
|
||||
constexpr AbstractCommandRegister(std::uint32_t control, CommandType commandType)
|
||||
: control(control)
|
||||
, commandType(commandType)
|
||||
{}
|
||||
|
||||
constexpr explicit AbstractCommandRegister(RegisterValue registerValue)
|
||||
: control(static_cast<std::uint32_t>(registerValue & 0x00FFFFFF))
|
||||
, commandType(static_cast<CommandType>((registerValue >> 24) & 0xFF))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
assert(this->control <= 0x00FFFFFF);
|
||||
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->control & 0x00FFFFFF)
|
||||
| static_cast<RegisterValue>(this->commandType) << 24
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct AbstractControlStatusRegister
|
||||
{
|
||||
enum CommandError: std::uint8_t
|
||||
{
|
||||
NONE = 0x00,
|
||||
BUSY = 0x01,
|
||||
NOT_SUPPORTED = 0x02,
|
||||
EXCEPTION = 0x03,
|
||||
HALT_RESUME = 0x04,
|
||||
BUS = 0x05,
|
||||
OTHER = 0x07,
|
||||
};
|
||||
|
||||
std::uint8_t dataCount:4 = 0;
|
||||
CommandError commandError:3 = CommandError::NONE;
|
||||
bool relaxedPrivilege:1 = false;
|
||||
bool busy:1 = false;
|
||||
std::uint8_t programBufferSize:5 = 0;
|
||||
|
||||
AbstractControlStatusRegister() = default;
|
||||
|
||||
constexpr explicit AbstractControlStatusRegister(RegisterValue registerValue)
|
||||
: dataCount(static_cast<std::uint8_t>(registerValue & 0x0F))
|
||||
, commandError(static_cast<CommandError>((registerValue >> 8) & 0x07))
|
||||
, relaxedPrivilege(static_cast<bool>(registerValue & (0x01 << 11)))
|
||||
, busy(static_cast<bool>(registerValue & (0x01 << 12)))
|
||||
, programBufferSize(static_cast<std::uint8_t>((registerValue >> 24) & 0x1F))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->dataCount)
|
||||
| static_cast<RegisterValue>(this->commandError) << 8
|
||||
| static_cast<RegisterValue>(this->relaxedPrivilege) << 11
|
||||
| static_cast<RegisterValue>(this->busy) << 12
|
||||
| static_cast<RegisterValue>(this->programBufferSize) << 24
|
||||
;
|
||||
}
|
||||
|
||||
constexpr void clearCommandError() {
|
||||
// Setting all of the bits will clear the field
|
||||
this->commandError = CommandError::OTHER;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct ControlRegister
|
||||
{
|
||||
enum HartSelectionMode: std::uint8_t
|
||||
{
|
||||
SINGLE = 0x00,
|
||||
MULTI = 0x01,
|
||||
};
|
||||
|
||||
bool debugModuleActive:1 = false;
|
||||
bool ndmReset:1 = false;
|
||||
bool clearResetHaltRequest:1 = false;
|
||||
bool setResetHaltRequest:1 = false;
|
||||
bool clearKeepAlive:1 = false;
|
||||
bool setKeepAlive:1 = false;
|
||||
HartIndex selectedHartIndex = 0;
|
||||
HartSelectionMode hartSelectionMode:1 = HartSelectionMode::SINGLE;
|
||||
bool acknowledgeUnavailableHarts:1 = false;
|
||||
bool acknowledgeHaveReset:1 = false;
|
||||
bool hartReset:1 = false;
|
||||
bool resumeRequest:1 = false;
|
||||
bool haltRequest:1 = false;
|
||||
|
||||
ControlRegister() = default;
|
||||
|
||||
constexpr explicit ControlRegister(RegisterValue registerValue)
|
||||
: debugModuleActive(static_cast<bool>(registerValue & 0x01))
|
||||
, ndmReset(static_cast<bool>(registerValue & (0x01 << 1)))
|
||||
, clearResetHaltRequest(static_cast<bool>(registerValue & (0x01 << 2)))
|
||||
, setResetHaltRequest(static_cast<bool>(registerValue & (0x01 << 3)))
|
||||
, clearKeepAlive(static_cast<bool>(registerValue & (0x01 << 4)))
|
||||
, setKeepAlive(static_cast<bool>(registerValue & (0x01 << 5)))
|
||||
, selectedHartIndex((((registerValue >> 6) & 0x3FF) << 10) | ((registerValue >> 16) & 0x3FF))
|
||||
, hartSelectionMode(static_cast<HartSelectionMode>(registerValue & (0x01 << 26)))
|
||||
, acknowledgeUnavailableHarts(static_cast<bool>(registerValue & (0x01 << 27)))
|
||||
, acknowledgeHaveReset(static_cast<bool>(registerValue & (0x01 << 28)))
|
||||
, hartReset(static_cast<bool>(registerValue & (0x01 << 29)))
|
||||
, resumeRequest(static_cast<bool>(registerValue & (0x01 << 30)))
|
||||
, haltRequest(static_cast<bool>(registerValue & static_cast<std::uint32_t>(0x01 << 31)))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
assert(this->selectedHartIndex <= 0xFFFFF);
|
||||
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->debugModuleActive)
|
||||
| static_cast<RegisterValue>(this->ndmReset) << 1
|
||||
| static_cast<RegisterValue>(this->clearResetHaltRequest) << 2
|
||||
| static_cast<RegisterValue>(this->setResetHaltRequest) << 3
|
||||
| static_cast<RegisterValue>(this->clearKeepAlive) << 4
|
||||
| static_cast<RegisterValue>(this->setKeepAlive) << 5
|
||||
| static_cast<RegisterValue>((this->selectedHartIndex & 0xFFFFF) >> 10) << 6
|
||||
| static_cast<RegisterValue>(this->selectedHartIndex & 0x3FF) << 16
|
||||
| static_cast<RegisterValue>(this->hartSelectionMode) << 26
|
||||
| static_cast<RegisterValue>(this->acknowledgeUnavailableHarts) << 27
|
||||
| static_cast<RegisterValue>(this->acknowledgeHaveReset) << 28
|
||||
| static_cast<RegisterValue>(this->hartReset) << 29
|
||||
| static_cast<RegisterValue>(this->resumeRequest) << 30
|
||||
| static_cast<RegisterValue>(this->haltRequest) << 31
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct MemoryAccessControlField
|
||||
{
|
||||
enum class MemorySize: std::uint8_t
|
||||
{
|
||||
SIZE_8 = 0x00,
|
||||
SIZE_16 = 0x01,
|
||||
SIZE_32 = 0x02,
|
||||
SIZE_64 = 0x03,
|
||||
SIZE_128 = 0x04,
|
||||
};
|
||||
|
||||
bool write:1 = false;
|
||||
bool postIncrement:1 = false;
|
||||
MemorySize size:3 = MemorySize::SIZE_32;
|
||||
bool virtualAddress:1 = false;
|
||||
|
||||
constexpr MemoryAccessControlField() = default;
|
||||
|
||||
constexpr MemoryAccessControlField(
|
||||
bool write,
|
||||
bool postIncrement,
|
||||
MemorySize size,
|
||||
bool virtualAddress
|
||||
)
|
||||
: write(write)
|
||||
, postIncrement(postIncrement)
|
||||
, size(size)
|
||||
, virtualAddress(virtualAddress)
|
||||
{}
|
||||
|
||||
constexpr explicit MemoryAccessControlField(std::uint32_t controlValue)
|
||||
: write(static_cast<bool>(controlValue & (0x01 << 16)))
|
||||
, postIncrement(static_cast<bool>(controlValue & (0x01 << 19)))
|
||||
, size(static_cast<MemorySize>((controlValue >> 20) & 0x07))
|
||||
, virtualAddress(static_cast<bool>(controlValue & (0x01 << 23)))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr std::uint32_t value() const {
|
||||
return std::uint32_t{0}
|
||||
| static_cast<std::uint32_t>(this->write) << 16
|
||||
| static_cast<std::uint32_t>(this->postIncrement) << 19
|
||||
| static_cast<std::uint32_t>(this->size) << 20
|
||||
| static_cast<std::uint32_t>(this->virtualAddress) << 23
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct RegisterAccessControlField
|
||||
{
|
||||
enum class RegisterSize: std::uint8_t
|
||||
{
|
||||
SIZE_32 = 0x02,
|
||||
SIZE_64 = 0x03,
|
||||
SIZE_128 = 0x04,
|
||||
};
|
||||
|
||||
RegisterNumber registerNumber;
|
||||
bool write:1 = false;
|
||||
bool transfer:1 = false;
|
||||
bool postExecute:1 = false;
|
||||
bool postIncrement:1 = false;
|
||||
RegisterSize size:3 = RegisterSize::SIZE_32;
|
||||
|
||||
RegisterAccessControlField(
|
||||
RegisterNumber registerNumber,
|
||||
bool write,
|
||||
bool transfer,
|
||||
bool postExecute,
|
||||
bool postIncrement,
|
||||
RegisterSize size
|
||||
)
|
||||
: registerNumber(registerNumber)
|
||||
, write(write)
|
||||
, transfer(transfer)
|
||||
, postExecute(postExecute)
|
||||
, postIncrement(postIncrement)
|
||||
, size(size)
|
||||
{}
|
||||
|
||||
constexpr explicit RegisterAccessControlField(std::uint32_t controlValue)
|
||||
: registerNumber(static_cast<RegisterNumber>(controlValue & 0xFFFF))
|
||||
, write(static_cast<bool>(controlValue & (0x01 << 16)))
|
||||
, transfer(static_cast<bool>(controlValue & (0x01 << 17)))
|
||||
, postExecute(static_cast<bool>(controlValue & (0x01 << 18)))
|
||||
, postIncrement(static_cast<bool>(controlValue & (0x01 << 19)))
|
||||
, size(static_cast<RegisterSize>((controlValue >> 20) & 0x07))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr std::uint32_t value() const {
|
||||
return std::uint32_t{0}
|
||||
| static_cast<std::uint32_t>(this->registerNumber)
|
||||
| static_cast<std::uint32_t>(this->write) << 16
|
||||
| static_cast<std::uint32_t>(this->transfer) << 17
|
||||
| static_cast<std::uint32_t>(this->postExecute) << 18
|
||||
| static_cast<std::uint32_t>(this->postIncrement) << 19
|
||||
| static_cast<std::uint32_t>(this->size) << 20
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
enum class RegisterAddress: RegisterAddress
|
||||
{
|
||||
ABSTRACT_DATA_0 = 0x04,
|
||||
ABSTRACT_DATA_1 = 0x05,
|
||||
ABSTRACT_DATA_2 = 0x06,
|
||||
ABSTRACT_DATA_3 = 0x07,
|
||||
ABSTRACT_DATA_4 = 0x08,
|
||||
ABSTRACT_DATA_5 = 0x09,
|
||||
ABSTRACT_DATA_6 = 0x0a,
|
||||
ABSTRACT_DATA_7 = 0x0b,
|
||||
ABSTRACT_DATA_8 = 0x0c,
|
||||
ABSTRACT_DATA_9 = 0x0d,
|
||||
ABSTRACT_DATA_10 = 0x0e,
|
||||
ABSTRACT_DATA_11 = 0x0f,
|
||||
CONTROL_REGISTER = 0x10,
|
||||
STATUS_REGISTER = 0x11,
|
||||
ABSTRACT_CONTROL_STATUS_REGISTER = 0x16,
|
||||
ABSTRACT_COMMAND_REGISTER = 0x17,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct StatusRegister
|
||||
{
|
||||
std::uint8_t version:4 = 0;
|
||||
bool validConfigStructurePointer:1 = false;
|
||||
bool supportsResetHalt:1 = false;
|
||||
bool authBusy:1 = false;
|
||||
bool authenticated:1 = false;
|
||||
bool anyHalted:1 = false;
|
||||
bool allHalted:1 = false;
|
||||
bool anyRunning:1 = false;
|
||||
bool allRunning:1 = false;
|
||||
bool anyUnavailable:1 = false;
|
||||
bool allUnavailable:1 = false;
|
||||
bool anyNonExistent:1 = false;
|
||||
bool allNonExistent:1 = false;
|
||||
bool anyResumeAcknowledge:1 = false;
|
||||
bool allResumeAcknowledge:1 = false;
|
||||
bool anyHaveReset:1 = false;
|
||||
bool allHaveReset:1 = false;
|
||||
bool implicitBreak:1 = false;
|
||||
bool stickyUnavailableBits:1 = false;
|
||||
bool ndmResetPending:1 = false;
|
||||
|
||||
constexpr explicit StatusRegister(RegisterValue registerValue)
|
||||
: version(static_cast<std::uint8_t>(registerValue & 0x0F))
|
||||
, validConfigStructurePointer(static_cast<bool>(registerValue & (0x01 << 4)))
|
||||
, supportsResetHalt(static_cast<bool>(registerValue & (0x01 << 5)))
|
||||
, authBusy(static_cast<bool>(registerValue & (0x01 << 6)))
|
||||
, authenticated(static_cast<bool>(registerValue & (0x01 << 7)))
|
||||
, anyHalted(static_cast<bool>(registerValue & (0x01 << 8)))
|
||||
, allHalted(static_cast<bool>(registerValue & (0x01 << 9)))
|
||||
, anyRunning(static_cast<bool>(registerValue & (0x01 << 10)))
|
||||
, allRunning(static_cast<bool>(registerValue & (0x01 << 11)))
|
||||
, anyUnavailable(static_cast<bool>(registerValue & (0x01 << 12)))
|
||||
, allUnavailable(static_cast<bool>(registerValue & (0x01 << 13)))
|
||||
, anyNonExistent(static_cast<bool>(registerValue & (0x01 << 14)))
|
||||
, allNonExistent(static_cast<bool>(registerValue & (0x01 << 15)))
|
||||
, anyResumeAcknowledge(static_cast<bool>(registerValue & (0x01 << 16)))
|
||||
, allResumeAcknowledge(static_cast<bool>(registerValue & (0x01 << 17)))
|
||||
, anyHaveReset(static_cast<bool>(registerValue & (0x01 << 18)))
|
||||
, allHaveReset(static_cast<bool>(registerValue & (0x01 << 19)))
|
||||
, implicitBreak(static_cast<bool>(registerValue & (0x01 << 22)))
|
||||
, stickyUnavailableBits(static_cast<bool>(registerValue & (0x01 << 23)))
|
||||
, ndmResetPending(static_cast<bool>(registerValue & (0x01 << 24)))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->version)
|
||||
| static_cast<RegisterValue>(this->validConfigStructurePointer) << 4
|
||||
| static_cast<RegisterValue>(this->supportsResetHalt) << 5
|
||||
| static_cast<RegisterValue>(this->authBusy) << 6
|
||||
| static_cast<RegisterValue>(this->authenticated) << 7
|
||||
| static_cast<RegisterValue>(this->anyHalted) << 8
|
||||
| static_cast<RegisterValue>(this->allHalted) << 9
|
||||
| static_cast<RegisterValue>(this->anyRunning) << 10
|
||||
| static_cast<RegisterValue>(this->allRunning) << 11
|
||||
| static_cast<RegisterValue>(this->anyUnavailable) << 12
|
||||
| static_cast<RegisterValue>(this->allUnavailable) << 13
|
||||
| static_cast<RegisterValue>(this->anyNonExistent) << 14
|
||||
| static_cast<RegisterValue>(this->allNonExistent) << 15
|
||||
| static_cast<RegisterValue>(this->anyResumeAcknowledge) << 16
|
||||
| static_cast<RegisterValue>(this->allResumeAcknowledge) << 17
|
||||
| static_cast<RegisterValue>(this->anyHaveReset) << 18
|
||||
| static_cast<RegisterValue>(this->allHaveReset) << 19
|
||||
| static_cast<RegisterValue>(this->implicitBreak) << 22
|
||||
| static_cast<RegisterValue>(this->stickyUnavailableBits) << 23
|
||||
| static_cast<RegisterValue>(this->ndmResetPending) << 24
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,592 @@
|
||||
#include "DebugTranslator.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
|
||||
#include "Registers/CpuRegisterNumbers.hpp"
|
||||
#include "DebugModule/Registers/RegisterAddresses.hpp"
|
||||
#include "DebugModule/Registers/RegisterAccessControlField.hpp"
|
||||
#include "DebugModule/Registers/MemoryAccessControlField.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Exceptions/InvalidConfig.hpp"
|
||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
using Registers::DebugControlStatusRegister;
|
||||
|
||||
using DebugModule::Registers::RegisterAddress;
|
||||
using DebugModule::Registers::ControlRegister;
|
||||
using DebugModule::Registers::StatusRegister;
|
||||
using DebugModule::Registers::AbstractControlStatusRegister;
|
||||
using DebugModule::Registers::AbstractCommandRegister;
|
||||
|
||||
using Registers::CpuRegisterNumber;
|
||||
|
||||
using namespace ::Targets::RiscV;
|
||||
|
||||
using ::Targets::TargetExecutionState;
|
||||
using ::Targets::TargetMemoryAddress;
|
||||
using ::Targets::TargetMemoryAddressRange;
|
||||
using ::Targets::TargetMemorySize;
|
||||
using ::Targets::TargetMemoryBuffer;
|
||||
using ::Targets::TargetStackPointer;
|
||||
using ::Targets::TargetAddressSpaceDescriptor;
|
||||
using ::Targets::TargetMemorySegmentDescriptor;
|
||||
using ::Targets::TargetRegisterDescriptors;
|
||||
using ::Targets::TargetRegisterDescriptorAndValuePairs;
|
||||
|
||||
DebugTranslator::DebugTranslator(
|
||||
DebugTransportModuleInterface& dtmInterface,
|
||||
const TargetDescriptionFile& targetDescriptionFile,
|
||||
const RiscVTargetConfig& targetConfig
|
||||
)
|
||||
: dtmInterface(dtmInterface)
|
||||
, targetDescriptionFile(targetDescriptionFile)
|
||||
, targetConfig(targetConfig)
|
||||
{}
|
||||
|
||||
void DebugTranslator::init() {
|
||||
// No pre-activation initialisation required.
|
||||
return;
|
||||
}
|
||||
|
||||
void DebugTranslator::activate() {
|
||||
this->dtmInterface.activate();
|
||||
|
||||
this->hartIndices = this->discoverHartIndices();
|
||||
if (this->hartIndices.empty()) {
|
||||
throw Exceptions::TargetOperationFailure{"Failed to discover any RISC-V harts"};
|
||||
}
|
||||
|
||||
Logger::debug("Discovered RISC-V harts: " + std::to_string(this->hartIndices.size()));
|
||||
|
||||
/*
|
||||
* We only support debugging a single RISC-V hart, for now.
|
||||
*
|
||||
* If we discover more than one, we select the first one and ensure that this is explicitly communicated to the
|
||||
* user.
|
||||
*/
|
||||
if (this->hartIndices.size() > 1) {
|
||||
Logger::warning("Bloom only supports debugging a single RISC-V hart - selecting first available");
|
||||
}
|
||||
|
||||
this->selectedHartIndex = this->hartIndices.front();
|
||||
Logger::info("Selected RISC-V hart index: " + std::to_string(this->selectedHartIndex));
|
||||
|
||||
/*
|
||||
* Disabling the debug module before enabling it will clear any state from a previous debug session that
|
||||
* wasn't terminated properly.
|
||||
*/
|
||||
this->disableDebugModule();
|
||||
this->enableDebugModule();
|
||||
|
||||
this->stop();
|
||||
this->reset();
|
||||
|
||||
auto debugControlStatusRegister = this->readDebugControlStatusRegister();
|
||||
debugControlStatusRegister.breakUMode = true;
|
||||
debugControlStatusRegister.breakSMode = true;
|
||||
debugControlStatusRegister.breakMMode = true;
|
||||
|
||||
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
||||
}
|
||||
|
||||
void DebugTranslator::deactivate() {
|
||||
this->disableDebugModule();
|
||||
this->dtmInterface.deactivate();
|
||||
}
|
||||
|
||||
TargetExecutionState DebugTranslator::getExecutionState() {
|
||||
return this->readDebugModuleStatusRegister().anyRunning
|
||||
? TargetExecutionState::RUNNING
|
||||
: TargetExecutionState::STOPPED;
|
||||
}
|
||||
|
||||
void DebugTranslator::stop() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.haltRequest = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
auto statusRegister = this->readDebugModuleStatusRegister();
|
||||
|
||||
for (auto attempts = 1; !statusRegister.allHalted && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
statusRegister = this->readDebugModuleStatusRegister();
|
||||
}
|
||||
|
||||
controlRegister.haltRequest = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
if (!statusRegister.allHalted) {
|
||||
throw Exceptions::Exception{"Target took too long to halt selected harts"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::run() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.resumeRequest = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
auto statusRegister = this->readDebugModuleStatusRegister();
|
||||
|
||||
for (auto attempts = 1; !statusRegister.allResumeAcknowledge && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
statusRegister = this->readDebugModuleStatusRegister();
|
||||
}
|
||||
|
||||
controlRegister.resumeRequest = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
if (!statusRegister.allResumeAcknowledge) {
|
||||
throw Exceptions::Exception{"Target took too long to acknowledge resume request"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::step() {
|
||||
auto debugControlStatusRegister = this->readDebugControlStatusRegister();
|
||||
debugControlStatusRegister.step = true;
|
||||
|
||||
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
||||
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.resumeRequest = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
controlRegister.resumeRequest = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
debugControlStatusRegister.step = false;
|
||||
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
||||
}
|
||||
|
||||
void DebugTranslator::reset() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.setResetHaltRequest = true;
|
||||
controlRegister.haltRequest = true;
|
||||
controlRegister.ndmReset = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
controlRegister.ndmReset = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
auto statusRegister = this->readDebugModuleStatusRegister();
|
||||
|
||||
for (auto attempts = 1; !statusRegister.allHaveReset && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
statusRegister = this->readDebugModuleStatusRegister();
|
||||
}
|
||||
|
||||
controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.clearResetHaltRequest = true;
|
||||
controlRegister.acknowledgeHaveReset = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
if (!statusRegister.allHaveReset) {
|
||||
throw Exceptions::Exception{"Target took too long to reset"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::setSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::clearSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::setHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::clearHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::clearAllBreakpoints() {
|
||||
|
||||
}
|
||||
|
||||
TargetRegisterDescriptorAndValuePairs DebugTranslator::readCpuRegisters(
|
||||
const TargetRegisterDescriptors& descriptors
|
||||
) {
|
||||
auto output = TargetRegisterDescriptorAndValuePairs{};
|
||||
|
||||
for (const auto& descriptor : descriptors) {
|
||||
const auto registerValue = this->readCpuRegister(static_cast<RegisterNumber>(descriptor->startAddress));
|
||||
output.emplace_back(
|
||||
*descriptor,
|
||||
TargetMemoryBuffer{
|
||||
static_cast<unsigned char>(registerValue >> 24),
|
||||
static_cast<unsigned char>(registerValue >> 16),
|
||||
static_cast<unsigned char>(registerValue >> 8),
|
||||
static_cast<unsigned char>(registerValue),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void DebugTranslator::writeCpuRegisters(const TargetRegisterDescriptorAndValuePairs& registers) {
|
||||
for (const auto& [descriptor, value] : registers) {
|
||||
assert((value.size() * 8) > std::numeric_limits<RegisterValue>::digits);
|
||||
|
||||
auto registerValue = RegisterValue{0};
|
||||
for (const auto& registerByte : value) {
|
||||
registerValue = (registerValue << 8) | registerByte;
|
||||
}
|
||||
|
||||
this->writeCpuRegister(static_cast<RegisterNumber>(descriptor.startAddress), registerValue);
|
||||
}
|
||||
}
|
||||
|
||||
TargetMemoryBuffer DebugTranslator::readMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
TargetMemoryAddress startAddress,
|
||||
TargetMemorySize bytes,
|
||||
const std::set<TargetMemoryAddressRange>& excludedAddressRanges
|
||||
) {
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
|
||||
// TODO: excluded addresses
|
||||
|
||||
const auto pageSize = 4;
|
||||
if ((startAddress % pageSize) != 0 || (bytes % pageSize) != 0) {
|
||||
// Alignment required
|
||||
const auto alignedStartAddress = this->alignMemoryAddress(startAddress, pageSize);
|
||||
const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), pageSize);
|
||||
|
||||
const auto memoryBuffer = this->readMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
alignedStartAddress,
|
||||
alignedBytes,
|
||||
excludedAddressRanges
|
||||
);
|
||||
|
||||
const auto offset = memoryBuffer.begin() + (startAddress - alignedStartAddress);
|
||||
return TargetMemoryBuffer{offset, offset + bytes};
|
||||
}
|
||||
|
||||
auto output = TargetMemoryBuffer{};
|
||||
output.reserve(bytes);
|
||||
|
||||
/*
|
||||
* We only need to set the address once. No need to update it as we use the post-increment function to
|
||||
* increment the address. See MemoryAccessControlField::postIncrement
|
||||
*/
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress);
|
||||
|
||||
constexpr auto command = AbstractCommandRegister{
|
||||
MemoryAccessControlField{
|
||||
false,
|
||||
true,
|
||||
MemoryAccessControlField::MemorySize::SIZE_32,
|
||||
false
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::MEMORY_ACCESS
|
||||
};
|
||||
|
||||
for (auto address = startAddress; address <= (startAddress + bytes - 1); address += 4) {
|
||||
this->executeAbstractCommand(command);
|
||||
|
||||
const auto data = this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
||||
output.emplace_back(static_cast<unsigned char>(data));
|
||||
output.emplace_back(static_cast<unsigned char>(data >> 8));
|
||||
output.emplace_back(static_cast<unsigned char>(data >> 16));
|
||||
output.emplace_back(static_cast<unsigned char>(data >> 24));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void DebugTranslator::writeMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
TargetMemoryAddress startAddress,
|
||||
const TargetMemoryBuffer& buffer
|
||||
) {
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
|
||||
constexpr auto alignTo = TargetMemorySize{4};
|
||||
const auto bytes = static_cast<TargetMemorySize>(buffer.size());
|
||||
if ((startAddress % alignTo) != 0 || (bytes % alignTo) != 0) {
|
||||
/*
|
||||
* Alignment required
|
||||
*
|
||||
* To align the write operation, we read the front and back offset bytes and use them to construct an
|
||||
* aligned buffer.
|
||||
*/
|
||||
const auto alignedStartAddress = this->alignMemoryAddress(startAddress, alignTo);
|
||||
const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), alignTo);
|
||||
|
||||
assert(alignedBytes > bytes);
|
||||
|
||||
auto alignedBuffer = (alignedStartAddress < startAddress)
|
||||
? this->readMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
alignedStartAddress,
|
||||
(startAddress - alignedStartAddress)
|
||||
)
|
||||
: TargetMemoryBuffer{};
|
||||
alignedBuffer.reserve(alignedBytes);
|
||||
|
||||
// Read the offset bytes required to align the buffer size
|
||||
const auto dataBack = this->readMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
startAddress + bytes,
|
||||
alignedBytes - bytes - (startAddress - alignedStartAddress)
|
||||
);
|
||||
alignedBuffer.insert(alignedBuffer.end(), dataBack.begin(), dataBack.end());
|
||||
|
||||
return this->writeMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
alignedStartAddress,
|
||||
alignedBuffer
|
||||
);
|
||||
}
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress);
|
||||
|
||||
constexpr auto command = AbstractCommandRegister{
|
||||
MemoryAccessControlField{
|
||||
true,
|
||||
true,
|
||||
MemoryAccessControlField::MemorySize::SIZE_32,
|
||||
false
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::MEMORY_ACCESS
|
||||
};
|
||||
|
||||
for (TargetMemoryAddress offset = 0; offset < buffer.size(); offset += 4) {
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_DATA_0,
|
||||
static_cast<RegisterValue>(
|
||||
(buffer[offset + 3] << 24)
|
||||
| (buffer[offset + 2] << 16)
|
||||
| (buffer[offset + 1] << 8)
|
||||
| (buffer[offset])
|
||||
)
|
||||
);
|
||||
|
||||
this->executeAbstractCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DebugModule::HartIndex> DebugTranslator::discoverHartIndices() {
|
||||
auto hartIndices = std::vector<DebugModule::HartIndex>{};
|
||||
|
||||
/*
|
||||
* We can obtain the maximum hart index by setting all of the hartsel bits in the control register and then
|
||||
* read the value back.
|
||||
*/
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = 0xFFFFF;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
const auto maxHartIndex = this->readDebugModuleControlRegister().selectedHartIndex;
|
||||
|
||||
for (auto hartIndex = DebugModule::HartIndex{0}; hartIndex <= maxHartIndex; ++hartIndex) {
|
||||
/*
|
||||
* We can't just assume that everything between 0 and the maximum hart index are valid hart indices. We
|
||||
* have to test each index until we find one that is non-existent.
|
||||
*/
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = hartIndex;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
/*
|
||||
* It's worth noting that some RISC-V targets **do not** set the non-existent flags. I'm not sure why.
|
||||
* Maybe hartsel has been hardwired to 0 on targets that only support a single hart, preventing the
|
||||
* selection of non-existent harts.
|
||||
*
|
||||
* Relying on the maximum hart index seems to be all we can do in this case.
|
||||
*/
|
||||
if (this->readDebugModuleStatusRegister().anyNonExistent) {
|
||||
break;
|
||||
}
|
||||
|
||||
hartIndices.emplace_back(hartIndex);
|
||||
}
|
||||
|
||||
return hartIndices;
|
||||
}
|
||||
|
||||
ControlRegister DebugTranslator::readDebugModuleControlRegister() {
|
||||
return ControlRegister{this->dtmInterface.readDebugModuleRegister(RegisterAddress::CONTROL_REGISTER)};
|
||||
}
|
||||
|
||||
StatusRegister DebugTranslator::readDebugModuleStatusRegister() {
|
||||
return StatusRegister{this->dtmInterface.readDebugModuleRegister(RegisterAddress::STATUS_REGISTER)};
|
||||
}
|
||||
|
||||
AbstractControlStatusRegister DebugTranslator::readDebugModuleAbstractControlStatusRegister() {
|
||||
return AbstractControlStatusRegister{
|
||||
this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_CONTROL_STATUS_REGISTER)
|
||||
};
|
||||
}
|
||||
|
||||
DebugControlStatusRegister DebugTranslator::readDebugControlStatusRegister() {
|
||||
return DebugControlStatusRegister{
|
||||
this->readCpuRegister(static_cast<RegisterNumber>(CpuRegisterNumber::DEBUG_CONTROL_STATUS_REGISTER))
|
||||
};
|
||||
}
|
||||
|
||||
void DebugTranslator::enableDebugModule() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
|
||||
for (auto attempts = 1; !controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
}
|
||||
|
||||
if (!controlRegister.debugModuleActive) {
|
||||
throw Exceptions::Exception{"Took too long to enable debug module"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::disableDebugModule() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = false;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
|
||||
for (auto attempts = 1; controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
}
|
||||
|
||||
if (controlRegister.debugModuleActive) {
|
||||
throw Exceptions::Exception{"Took too long to disable debug module"};
|
||||
}
|
||||
}
|
||||
|
||||
RegisterValue DebugTranslator::readCpuRegister(RegisterNumber number) {
|
||||
using DebugModule::Registers::RegisterAccessControlField;
|
||||
|
||||
this->executeAbstractCommand(AbstractCommandRegister{
|
||||
RegisterAccessControlField{
|
||||
number,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
RegisterAccessControlField::RegisterSize::SIZE_32
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::REGISTER_ACCESS
|
||||
});
|
||||
|
||||
return this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
||||
}
|
||||
|
||||
void DebugTranslator::writeCpuRegister(RegisterNumber number, RegisterValue value) {
|
||||
using DebugModule::Registers::RegisterAccessControlField;
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0, value);
|
||||
this->executeAbstractCommand(AbstractCommandRegister{
|
||||
RegisterAccessControlField{
|
||||
number,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
RegisterAccessControlField::RegisterSize::SIZE_32
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::REGISTER_ACCESS
|
||||
});
|
||||
}
|
||||
|
||||
void DebugTranslator::writeDebugModuleControlRegister(const ControlRegister& controlRegister) {
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::CONTROL_REGISTER, controlRegister.value());
|
||||
}
|
||||
|
||||
void DebugTranslator::writeDebugControlStatusRegister(const DebugControlStatusRegister& controlRegister) {
|
||||
this->writeCpuRegister(
|
||||
static_cast<RegisterNumber>(CpuRegisterNumber::DEBUG_CONTROL_STATUS_REGISTER),
|
||||
controlRegister.value()
|
||||
);
|
||||
}
|
||||
|
||||
void DebugTranslator::executeAbstractCommand(
|
||||
const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister
|
||||
) {
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_COMMAND_REGISTER,
|
||||
abstractCommandRegister.value()
|
||||
);
|
||||
|
||||
auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
if (abstractStatusRegister.commandError != AbstractControlStatusRegister::CommandError::NONE) {
|
||||
throw Exceptions::Exception{
|
||||
"Failed to execute abstract command - error: "
|
||||
+ Services::StringService::toHex(abstractStatusRegister.commandError)
|
||||
};
|
||||
}
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
for (auto attempts = 1; abstractStatusRegister.busy && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
}
|
||||
|
||||
if (abstractStatusRegister.busy) {
|
||||
throw Exceptions::Exception{"Abstract command took too long to execute"};
|
||||
}
|
||||
}
|
||||
|
||||
TargetMemoryAddress DebugTranslator::alignMemoryAddress(TargetMemoryAddress address, TargetMemoryAddress alignTo) {
|
||||
return static_cast<TargetMemoryAddress>(
|
||||
std::floor(static_cast<float>(address) / static_cast<float>(alignTo))
|
||||
) * alignTo;
|
||||
}
|
||||
|
||||
TargetMemorySize DebugTranslator::alignMemorySize(TargetMemorySize size, TargetMemorySize alignTo) {
|
||||
return static_cast<TargetMemorySize>(
|
||||
std::ceil(static_cast<float>(size) / static_cast<float>(alignTo))
|
||||
) * alignTo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
|
||||
|
||||
#include "DebugTransportModuleInterface.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/RiscV/RiscVTargetConfig.hpp"
|
||||
|
||||
#include "RiscVGeneric.hpp"
|
||||
#include "Registers/CpuRegisterNumbers.hpp"
|
||||
#include "Registers/DebugControlStatusRegister.hpp"
|
||||
|
||||
#include "DebugModule/DebugModule.hpp"
|
||||
#include "DebugModule/Registers/ControlRegister.hpp"
|
||||
#include "DebugModule/Registers/StatusRegister.hpp"
|
||||
#include "DebugModule/Registers/AbstractControlStatusRegister.hpp"
|
||||
#include "DebugModule/Registers/AbstractCommandRegister.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
/**
|
||||
* Implementation of a RISC-V debug translator
|
||||
*/
|
||||
class DebugTranslator: public ::DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface
|
||||
{
|
||||
public:
|
||||
DebugTranslator(
|
||||
DebugTransportModuleInterface& dtmInterface,
|
||||
const ::Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const ::Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
);
|
||||
|
||||
virtual ~DebugTranslator() = default;
|
||||
|
||||
void init() override;
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
Targets::TargetExecutionState getExecutionState() override;
|
||||
|
||||
void stop() override;
|
||||
void run() override;
|
||||
void step() override;
|
||||
void reset() override;
|
||||
|
||||
void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
void setHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void clearAllBreakpoints() override;
|
||||
|
||||
Targets::TargetRegisterDescriptorAndValuePairs readCpuRegisters(
|
||||
const Targets::TargetRegisterDescriptors& descriptors
|
||||
) override;
|
||||
void writeCpuRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) override;
|
||||
|
||||
Targets::TargetMemoryBuffer readMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges = {}
|
||||
) override;
|
||||
void writeMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
const Targets::TargetMemoryBuffer& buffer
|
||||
) override;
|
||||
|
||||
private:
|
||||
DebugTransportModuleInterface& dtmInterface;
|
||||
|
||||
const ::Targets::RiscV::TargetDescriptionFile& targetDescriptionFile;
|
||||
const ::Targets::RiscV::RiscVTargetConfig& targetConfig;
|
||||
|
||||
std::vector<DebugModule::HartIndex> hartIndices;
|
||||
DebugModule::HartIndex selectedHartIndex = 0;
|
||||
|
||||
std::vector<DebugModule::HartIndex> discoverHartIndices();
|
||||
|
||||
DebugModule::Registers::ControlRegister readDebugModuleControlRegister();
|
||||
DebugModule::Registers::StatusRegister readDebugModuleStatusRegister();
|
||||
DebugModule::Registers::AbstractControlStatusRegister readDebugModuleAbstractControlStatusRegister();
|
||||
Registers::DebugControlStatusRegister readDebugControlStatusRegister();
|
||||
|
||||
void enableDebugModule();
|
||||
void disableDebugModule();
|
||||
|
||||
RegisterValue readCpuRegister(RegisterNumber number);
|
||||
void writeCpuRegister(RegisterNumber number, RegisterValue value);
|
||||
|
||||
void writeDebugModuleControlRegister(const DebugModule::Registers::ControlRegister& controlRegister);
|
||||
void writeDebugControlStatusRegister(const Registers::DebugControlStatusRegister& controlRegister);
|
||||
|
||||
void executeAbstractCommand(const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister);
|
||||
|
||||
Targets::TargetMemoryAddress alignMemoryAddress(
|
||||
Targets::TargetMemoryAddress address,
|
||||
Targets::TargetMemoryAddress alignTo
|
||||
);
|
||||
Targets::TargetMemorySize alignMemorySize(Targets::TargetMemorySize size, Targets::TargetMemorySize alignTo);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "RiscVGeneric.hpp"
|
||||
#include "DebugModule/DebugModule.hpp"
|
||||
#include "DebugModule/Registers/RegisterAddresses.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
/**
|
||||
* Provides access to the RISC-V target's debug transport module (DTM).
|
||||
*/
|
||||
class DebugTransportModuleInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Should prepare for and then activate the physical interface between the debug tool and the RISC-V target.
|
||||
*
|
||||
* Should throw an exception if activation fails. The error will be considered fatal, and result in a shutdown.
|
||||
*
|
||||
* Unless otherwise stated, it can be assumed that this function will be called (and must succeed)
|
||||
* before any of the other functions below this point are called. In other words, we can assume that the
|
||||
* interface has been activated in the implementations of any of the functions below this point.
|
||||
*/
|
||||
virtual void activate() = 0;
|
||||
|
||||
/**
|
||||
* Should deactivate the physical interface between the debug tool and the RISC-V target.
|
||||
*
|
||||
* CAUTION: This function **CAN** be called before activate(), or in instances where activate() failed (threw
|
||||
* an exception). Implementations must accommodate this.
|
||||
*/
|
||||
virtual void deactivate() = 0;
|
||||
|
||||
/**
|
||||
* Should read the value of a debug module register.
|
||||
*
|
||||
* @param address
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual DebugModule::RegisterValue readDebugModuleRegister(DebugModule::RegisterAddress address) = 0;
|
||||
|
||||
DebugModule::RegisterValue readDebugModuleRegister(DebugModule::Registers::RegisterAddress address) {
|
||||
return this->readDebugModuleRegister(static_cast<DebugModule::RegisterAddress>(address));
|
||||
};
|
||||
|
||||
/**
|
||||
* Should write a value to a debug module register.
|
||||
*
|
||||
* @param address
|
||||
* @param value
|
||||
*/
|
||||
virtual void writeDebugModuleRegister(
|
||||
DebugModule::RegisterAddress address,
|
||||
DebugModule::RegisterValue value
|
||||
) = 0;
|
||||
|
||||
void writeDebugModuleRegister(
|
||||
DebugModule::Registers::RegisterAddress address,
|
||||
DebugModule::RegisterValue value
|
||||
) {
|
||||
return this->writeDebugModuleRegister(
|
||||
static_cast<DebugModule::RegisterAddress>(address),
|
||||
value
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::Registers
|
||||
{
|
||||
enum class CpuRegisterNumber: RegisterNumber
|
||||
{
|
||||
DEBUG_CONTROL_STATUS_REGISTER = 0x07b0,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::Registers
|
||||
{
|
||||
struct DebugControlStatusRegister
|
||||
{
|
||||
enum class DebugModeCause: std::uint8_t
|
||||
{
|
||||
BREAK = 0x01,
|
||||
TRIGGER = 0x02,
|
||||
HALT_REQUEST = 0x03,
|
||||
STEP = 0x04,
|
||||
RESET_HALT_REQUEST = 0x05,
|
||||
GROUP = 0x06,
|
||||
};
|
||||
|
||||
PrivilegeMode privilegeMode:2;
|
||||
bool step:1 = false;
|
||||
bool nmiPending:1 = false;
|
||||
bool mprvEnabled:1 = false;
|
||||
DebugModeCause debugModeCause:3;
|
||||
bool stopTime:1 = false;
|
||||
bool stopCount:1 = false;
|
||||
bool stepInterruptsEnabled:1 = false;
|
||||
bool breakUMode:1 = false;
|
||||
bool breakSMode:1 = false;
|
||||
bool breakMMode:1 = false;
|
||||
bool breakVUMode:1 = false;
|
||||
bool breakVSMode:1 = false;
|
||||
std::uint8_t debugVersion:4 = 0;
|
||||
|
||||
DebugControlStatusRegister() = default;
|
||||
|
||||
constexpr explicit DebugControlStatusRegister(RegisterValue registerValue)
|
||||
: privilegeMode(static_cast<PrivilegeMode>(registerValue & 0x03))
|
||||
, step(static_cast<bool>(registerValue & (0x01 << 2)))
|
||||
, nmiPending(static_cast<bool>(registerValue & (0x01 << 3)))
|
||||
, mprvEnabled(static_cast<bool>(registerValue & (0x01 << 4)))
|
||||
, debugModeCause(static_cast<DebugModeCause>((registerValue >> 6) & 0x07))
|
||||
, stopTime(static_cast<bool>(registerValue & (0x01 << 9)))
|
||||
, stopCount(static_cast<bool>(registerValue & (0x01 << 10)))
|
||||
, stepInterruptsEnabled(static_cast<bool>(registerValue & (0x01 << 11)))
|
||||
, breakUMode(static_cast<bool>(registerValue & (0x01 << 12)))
|
||||
, breakSMode(static_cast<bool>(registerValue & (0x01 << 13)))
|
||||
, breakMMode(static_cast<bool>(registerValue & (0x01 << 15)))
|
||||
, breakVUMode(static_cast<bool>(registerValue & (0x01 << 16)))
|
||||
, breakVSMode(static_cast<bool>(registerValue & (0x01 << 17)))
|
||||
, debugVersion(static_cast<std::uint8_t>(registerValue >> 28) & 0x0F)
|
||||
{}
|
||||
|
||||
constexpr RegisterValue value() const {
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->privilegeMode)
|
||||
| static_cast<RegisterValue>(this->step) << 2
|
||||
| static_cast<RegisterValue>(this->nmiPending) << 3
|
||||
| static_cast<RegisterValue>(this->mprvEnabled) << 4
|
||||
| static_cast<RegisterValue>(this->debugModeCause) << 6
|
||||
| static_cast<RegisterValue>(this->stopTime) << 9
|
||||
| static_cast<RegisterValue>(this->stopCount) << 10
|
||||
| static_cast<RegisterValue>(this->stepInterruptsEnabled) << 11
|
||||
| static_cast<RegisterValue>(this->breakUMode) << 12
|
||||
| static_cast<RegisterValue>(this->breakSMode) << 13
|
||||
| static_cast<RegisterValue>(this->breakMMode) << 15
|
||||
| static_cast<RegisterValue>(this->breakVUMode) << 16
|
||||
| static_cast<RegisterValue>(this->breakVSMode) << 17
|
||||
| static_cast<RegisterValue>(this->debugVersion) << 28
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
using RegisterValue = std::uint32_t;
|
||||
using RegisterNumber = std::uint16_t;
|
||||
|
||||
enum class PrivilegeMode: std::uint8_t
|
||||
{
|
||||
U_MODE = 0x00,
|
||||
S_MODE = 0x01,
|
||||
M_MODE = 0x03,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user