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:
Nav
2024-07-23 21:14:22 +01:00
parent 2986934485
commit 6cdbfbe950
331 changed files with 8815 additions and 8565 deletions

View File

@@ -12,12 +12,13 @@ namespace DebugToolDrivers::Protocols::CmsisDap
void CmsisDapInterface::sendCommand(const Command& cmsisDapCommand) {
if (this->commandDelay.count() > 0) {
using namespace std::chrono;
std::int64_t now = duration_cast<milliseconds>(high_resolution_clock::now().time_since_epoch()).count();
std::int64_t difference = (now - this->lastCommandSentTimeStamp);
const auto now = duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count();
const auto difference = (now - this->lastCommandSentTimeStamp);
if (difference < this->commandDelay.count()) {
std::this_thread::sleep_for(milliseconds(this->commandDelay.count() - difference));
std::this_thread::sleep_for(std::chrono::milliseconds{this->commandDelay.count() - difference});
}
this->lastCommandSentTimeStamp = now;

View File

@@ -65,10 +65,9 @@ namespace DebugToolDrivers::Protocols::CmsisDap
"CMSIS Response type must be derived from the Response class."
);
const auto rawResponse = this->getUsbHidInterface().read(std::chrono::milliseconds(60000));
const auto rawResponse = this->getUsbHidInterface().read(std::chrono::milliseconds{60000});
if (rawResponse.empty()) {
throw Exceptions::DeviceCommunicationFailure("Empty CMSIS-DAP response received");
throw Exceptions::DeviceCommunicationFailure{"Empty CMSIS-DAP response received"};
}
return ResponseType(rawResponse);
@@ -99,7 +98,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap
auto response = this->getResponse<typename CommandType::ExpectedResponseType>();
if (response.id != cmsisDapCommand.id) {
throw Exceptions::DeviceCommunicationFailure("Unexpected response to CMSIS-DAP command.");
throw Exceptions::DeviceCommunicationFailure{"Unexpected response to CMSIS-DAP command."};
}
return response;
@@ -121,7 +120,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap
*
* Because of this, we may need to enforce a minimum time gap between sending CMSIS commands.
*/
std::chrono::milliseconds commandDelay = std::chrono::milliseconds(0);
std::chrono::milliseconds commandDelay = std::chrono::milliseconds{0};
std::int64_t lastCommandSentTimeStamp = 0;
};
}

View File

@@ -6,10 +6,10 @@ namespace DebugToolDrivers::Protocols::CmsisDap
{
Response::Response(const std::vector<unsigned char>& rawResponse) {
if (rawResponse.empty()) {
throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response");
throw Exceptions::Exception{"Failed to process CMSIS-DAP response - invalid response"};
}
this->id = rawResponse[0];
this->data = std::vector<unsigned char>(rawResponse.begin() + 1, rawResponse.end());
this->data = std::vector<unsigned char>{rawResponse.begin() + 1, rawResponse.end()};
}
}

View File

@@ -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,
};
}

View File

@@ -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
;
}
};
}

View File

@@ -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;
}
};
}

View File

@@ -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
;
}
};
}

View File

@@ -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
;
}
};
}

View File

@@ -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
;
}
};
}

View File

@@ -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,
};
}

View File

@@ -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
;
}
};
}

View File

@@ -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;
}
}

View File

@@ -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);
};
}

View File

@@ -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
);
};
};
}

View File

@@ -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,
};
}

View File

@@ -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
;
}
};
}

View File

@@ -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,
};
}