Implemented memory access via program buffer, in RISC-V debug translator
- Support for multiple memory access strategies (abstract commands and program buffer) - Probing of memory access strategies - Included `preferredMemoryAccessStrategy` debug translator config param - Other bits of tidying in the RISC-V debug translator
This commit is contained in:
@@ -31,5 +31,12 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule
|
||||
HALT_RESUME = 0x04,
|
||||
BUS = 0x05,
|
||||
OTHER = 0x07,
|
||||
CLEAR = 0x07,
|
||||
};
|
||||
|
||||
enum class MemoryAccessStrategy: std::uint8_t
|
||||
{
|
||||
ABSTRACT_COMMAND,
|
||||
PROGRAM_BUFFER,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct AbstractCommandAutoExecuteRegister
|
||||
{
|
||||
bool onData0Access = false;
|
||||
bool onData1Access = false;
|
||||
bool onData2Access = false;
|
||||
bool onData3Access = false;
|
||||
bool onData4Access = false;
|
||||
bool onData5Access = false;
|
||||
bool onData6Access = false;
|
||||
bool onData7Access = false;
|
||||
bool onData8Access = false;
|
||||
bool onData9Access = false;
|
||||
bool onData10Access = false;
|
||||
bool onData11Access = false;
|
||||
|
||||
bool onProgramBuffer0Access = false;
|
||||
bool onProgramBuffer1Access = false;
|
||||
bool onProgramBuffer2Access = false;
|
||||
bool onProgramBuffer3Access = false;
|
||||
bool onProgramBuffer4Access = false;
|
||||
bool onProgramBuffer5Access = false;
|
||||
bool onProgramBuffer6Access = false;
|
||||
bool onProgramBuffer7Access = false;
|
||||
bool onProgramBuffer8Access = false;
|
||||
bool onProgramBuffer9Access = false;
|
||||
bool onProgramBuffer10Access = false;
|
||||
bool onProgramBuffer11Access = false;
|
||||
bool onProgramBuffer12Access = false;
|
||||
bool onProgramBuffer13Access = false;
|
||||
bool onProgramBuffer14Access = false;
|
||||
bool onProgramBuffer15Access = false;
|
||||
|
||||
static constexpr AbstractCommandAutoExecuteRegister fromValue(RegisterValue value) {
|
||||
return {
|
||||
.onData0Access = static_cast<bool>(value & 0x01U),
|
||||
.onData1Access = static_cast<bool>(value & (0x01U << 1)),
|
||||
.onData2Access = static_cast<bool>(value & (0x01U << 2)),
|
||||
.onData3Access = static_cast<bool>(value & (0x01U << 3)),
|
||||
.onData4Access = static_cast<bool>(value & (0x01U << 4)),
|
||||
.onData5Access = static_cast<bool>(value & (0x01U << 5)),
|
||||
.onData6Access = static_cast<bool>(value & (0x01U << 6)),
|
||||
.onData7Access = static_cast<bool>(value & (0x01U << 7)),
|
||||
.onData8Access = static_cast<bool>(value & (0x01U << 8)),
|
||||
.onData9Access = static_cast<bool>(value & (0x01U << 9)),
|
||||
.onData10Access = static_cast<bool>(value & (0x01U << 10)),
|
||||
.onData11Access = static_cast<bool>(value & (0x01U << 11)),
|
||||
.onProgramBuffer0Access = static_cast<bool>(value & (0x01U << 16)),
|
||||
.onProgramBuffer1Access = static_cast<bool>(value & (0x01U << 17)),
|
||||
.onProgramBuffer2Access = static_cast<bool>(value & (0x01U << 18)),
|
||||
.onProgramBuffer3Access = static_cast<bool>(value & (0x01U << 19)),
|
||||
.onProgramBuffer4Access = static_cast<bool>(value & (0x01U << 20)),
|
||||
.onProgramBuffer5Access = static_cast<bool>(value & (0x01U << 21)),
|
||||
.onProgramBuffer6Access = static_cast<bool>(value & (0x01U << 22)),
|
||||
.onProgramBuffer7Access = static_cast<bool>(value & (0x01U << 23)),
|
||||
.onProgramBuffer8Access = static_cast<bool>(value & (0x01U << 24)),
|
||||
.onProgramBuffer9Access = static_cast<bool>(value & (0x01U << 25)),
|
||||
.onProgramBuffer10Access = static_cast<bool>(value & (0x01U << 26)),
|
||||
.onProgramBuffer11Access = static_cast<bool>(value & (0x01U << 27)),
|
||||
.onProgramBuffer12Access = static_cast<bool>(value & (0x01U << 28)),
|
||||
.onProgramBuffer13Access = static_cast<bool>(value & (0x01U << 29)),
|
||||
.onProgramBuffer14Access = static_cast<bool>(value & (0x01U << 30)),
|
||||
.onProgramBuffer15Access = static_cast<bool>(value & (0x01U << 31)),
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->onData0Access)
|
||||
| static_cast<RegisterValue>(this->onData1Access) << 1
|
||||
| static_cast<RegisterValue>(this->onData2Access) << 2
|
||||
| static_cast<RegisterValue>(this->onData3Access) << 3
|
||||
| static_cast<RegisterValue>(this->onData4Access) << 4
|
||||
| static_cast<RegisterValue>(this->onData5Access) << 5
|
||||
| static_cast<RegisterValue>(this->onData6Access) << 6
|
||||
| static_cast<RegisterValue>(this->onData7Access) << 7
|
||||
| static_cast<RegisterValue>(this->onData8Access) << 8
|
||||
| static_cast<RegisterValue>(this->onData9Access) << 9
|
||||
| static_cast<RegisterValue>(this->onData10Access) << 10
|
||||
| static_cast<RegisterValue>(this->onData11Access) << 11
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer0Access) << 16
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer1Access) << 17
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer2Access) << 18
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer3Access) << 19
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer4Access) << 20
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer5Access) << 21
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer6Access) << 22
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer7Access) << 23
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer8Access) << 24
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer9Access) << 25
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer10Access) << 26
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer11Access) << 27
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer12Access) << 28
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer13Access) << 29
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer14Access) << 30
|
||||
| static_cast<RegisterValue>(this->onProgramBuffer15Access) << 31
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -33,10 +33,5 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
| static_cast<RegisterValue>(this->programBufferSize & 0x1F) << 24
|
||||
;
|
||||
}
|
||||
|
||||
constexpr void clearCommandError() {
|
||||
// Setting all of the bits will clear the field
|
||||
this->commandError = AbstractCommandError::OTHER;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct RegisterAccessControlField
|
||||
{
|
||||
struct Flags
|
||||
{
|
||||
bool postExecute = false;
|
||||
bool postIncrement = false;
|
||||
};
|
||||
|
||||
enum class RegisterSize: std::uint8_t
|
||||
{
|
||||
SIZE_32 = 0x02,
|
||||
@@ -18,8 +24,7 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
RegisterNumber registerNumber = 0;
|
||||
bool write = false;
|
||||
bool transfer = false;
|
||||
bool postExecute = false;
|
||||
bool postIncrement = false;
|
||||
Flags flags = {};
|
||||
RegisterSize size = RegisterSize::SIZE_32;
|
||||
|
||||
static constexpr auto fromValue(std::uint32_t value) {
|
||||
@@ -27,8 +32,10 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
.registerNumber = static_cast<RegisterNumber>(value & 0xFFFF),
|
||||
.write = static_cast<bool>(value & (0x01 << 16)),
|
||||
.transfer = static_cast<bool>(value & (0x01 << 17)),
|
||||
.postExecute = static_cast<bool>(value & (0x01 << 18)),
|
||||
.postIncrement = static_cast<bool>(value & (0x01 << 19)),
|
||||
.flags = {
|
||||
.postExecute = static_cast<bool>(value & (0x01 << 18)),
|
||||
.postIncrement = static_cast<bool>(value & (0x01 << 19)),
|
||||
},
|
||||
.size = static_cast<RegisterSize>((value >> 20) & 0x07),
|
||||
};
|
||||
}
|
||||
@@ -38,8 +45,8 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
| 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->flags.postExecute) << 18
|
||||
| static_cast<std::uint32_t>(this->flags.postIncrement) << 19
|
||||
| static_cast<std::uint32_t>(this->size) << 20
|
||||
;
|
||||
}
|
||||
|
||||
@@ -14,15 +14,32 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
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,
|
||||
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,
|
||||
ABSTRACT_COMMAND_AUTO_EXECUTE_REGISTER = 0x18,
|
||||
PROGRAM_BUFFER_0 = 0x20,
|
||||
PROGRAM_BUFFER_1 = 0x21,
|
||||
PROGRAM_BUFFER_2 = 0x22,
|
||||
PROGRAM_BUFFER_3 = 0x23,
|
||||
PROGRAM_BUFFER_4 = 0x24,
|
||||
PROGRAM_BUFFER_5 = 0x25,
|
||||
PROGRAM_BUFFER_6 = 0x26,
|
||||
PROGRAM_BUFFER_7 = 0x27,
|
||||
PROGRAM_BUFFER_8 = 0x28,
|
||||
PROGRAM_BUFFER_9 = 0x29,
|
||||
PROGRAM_BUFFER_10 = 0x2A,
|
||||
PROGRAM_BUFFER_11 = 0x2B,
|
||||
PROGRAM_BUFFER_12 = 0x2C,
|
||||
PROGRAM_BUFFER_13 = 0x2D,
|
||||
PROGRAM_BUFFER_14 = 0x2E,
|
||||
PROGRAM_BUFFER_15 = 0x2F,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "DebugModule/DebugModule.hpp"
|
||||
#include "TriggerModule/TriggerModule.hpp"
|
||||
#include "TriggerModule/TriggerDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
struct DebugModuleDescriptor
|
||||
{
|
||||
std::vector<DebugModule::HartIndex> hartIndices;
|
||||
|
||||
std::unordered_set<DebugModule::MemoryAccessStrategy> memoryAccessStrategies;
|
||||
std::uint8_t programBufferSize = 0;
|
||||
|
||||
std::unordered_map<TriggerModule::TriggerIndex, TriggerModule::TriggerDescriptor> triggerDescriptorsByIndex;
|
||||
};
|
||||
}
|
||||
@@ -6,11 +6,19 @@
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#include "Registers/CpuRegisterNumbers.hpp"
|
||||
#include "DebugModule/Registers/RegisterAddresses.hpp"
|
||||
#include "DebugModule/Registers/RegisterAccessControlField.hpp"
|
||||
#include "DebugModule/Registers/MemoryAccessControlField.hpp"
|
||||
#include "DebugModule/Registers/AbstractCommandAutoExecuteRegister.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/Opcodes/Lb.hpp"
|
||||
#include "src/Targets/RiscV/Opcodes/Lw.hpp"
|
||||
#include "src/Targets/RiscV/Opcodes/Sb.hpp"
|
||||
#include "src/Targets/RiscV/Opcodes/Sw.hpp"
|
||||
#include "src/Targets/RiscV/Opcodes/Addi.hpp"
|
||||
|
||||
#include "TriggerModule/Registers/TriggerSelect.hpp"
|
||||
#include "TriggerModule/Registers/TriggerInfo.hpp"
|
||||
@@ -19,6 +27,8 @@
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Exceptions/InvalidConfig.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
|
||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
|
||||
@@ -33,6 +43,11 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
using DebugModule::Registers::StatusRegister;
|
||||
using DebugModule::Registers::AbstractControlStatusRegister;
|
||||
using DebugModule::Registers::AbstractCommandRegister;
|
||||
using DebugModule::Registers::AbstractCommandAutoExecuteRegister;
|
||||
using DebugModule::Registers::RegisterAccessControlField;
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
using DebugModule::AbstractCommandError;
|
||||
using DebugModule::MemoryAccessStrategy;
|
||||
|
||||
using Registers::CpuRegisterNumber;
|
||||
|
||||
@@ -69,12 +84,12 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
void DebugTranslator::activate() {
|
||||
this->dtmInterface.activate();
|
||||
|
||||
this->hartIndices = this->discoverHartIndices();
|
||||
if (this->hartIndices.empty()) {
|
||||
this->debugModuleDescriptor.hartIndices = this->discoverHartIndices();
|
||||
if (this->debugModuleDescriptor.hartIndices.empty()) {
|
||||
throw Exceptions::TargetOperationFailure{"Failed to discover any RISC-V harts"};
|
||||
}
|
||||
|
||||
Logger::debug("Discovered RISC-V harts: " + std::to_string(this->hartIndices.size()));
|
||||
Logger::debug("Discovered RISC-V harts: " + std::to_string(this->debugModuleDescriptor.hartIndices.size()));
|
||||
|
||||
/*
|
||||
* We only support debugging a single RISC-V hart, for now.
|
||||
@@ -82,11 +97,11 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
* 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) {
|
||||
if (this->debugModuleDescriptor.hartIndices.size() > 1) {
|
||||
Logger::warning("Bloom only supports debugging a single RISC-V hart - selecting first available");
|
||||
}
|
||||
|
||||
this->selectedHartIndex = this->hartIndices.front();
|
||||
this->selectedHartIndex = this->debugModuleDescriptor.hartIndices.front();
|
||||
Logger::info("Selected RISC-V hart index: " + std::to_string(this->selectedHartIndex));
|
||||
|
||||
/*
|
||||
@@ -97,22 +112,63 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
this->enableDebugModule();
|
||||
|
||||
this->stop();
|
||||
this->reset();
|
||||
this->triggerDescriptorsByIndex = this->discoverTriggers();
|
||||
|
||||
Logger::debug("Discovered RISC-V triggers: " + std::to_string(this->triggerDescriptorsByIndex.size()));
|
||||
this->debugModuleDescriptor.triggerDescriptorsByIndex = this->discoverTriggers();
|
||||
|
||||
if (!this->triggerDescriptorsByIndex.empty()) {
|
||||
Logger::debug(
|
||||
"Discovered RISC-V triggers: "
|
||||
+ std::to_string(this->debugModuleDescriptor.triggerDescriptorsByIndex.size())
|
||||
);
|
||||
|
||||
if (!this->debugModuleDescriptor.triggerDescriptorsByIndex.empty()) {
|
||||
// Clear any left-over triggers from the previous debug session
|
||||
this->clearAllHardwareBreakpoints();
|
||||
}
|
||||
|
||||
auto debugControlStatusRegister = this->readDebugControlStatusRegister();
|
||||
debugControlStatusRegister.breakUMode = true;
|
||||
debugControlStatusRegister.breakSMode = true;
|
||||
debugControlStatusRegister.breakMMode = true;
|
||||
this->initDebugControlStatusRegister();
|
||||
|
||||
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
||||
const auto abstractControlStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
this->debugModuleDescriptor.programBufferSize = abstractControlStatusRegister.programBufferSize;
|
||||
|
||||
Logger::debug("Program buffer size: " + std::to_string(this->debugModuleDescriptor.programBufferSize));
|
||||
|
||||
if (this->debugModuleDescriptor.programBufferSize >= 3) {
|
||||
this->debugModuleDescriptor.memoryAccessStrategies.insert(MemoryAccessStrategy::PROGRAM_BUFFER);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to read a single word from the start of the system address space, via a memory access abstract
|
||||
* command.
|
||||
*/
|
||||
constexpr auto probingMemoryAccessCommand = AbstractCommandRegister{
|
||||
.control = MemoryAccessControlField{
|
||||
.postIncrement = true,
|
||||
.size = MemoryAccessControlField::MemorySize::SIZE_32,
|
||||
}.value(),
|
||||
.commandType = AbstractCommandRegister::CommandType::MEMORY_ACCESS
|
||||
};
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_DATA_1,
|
||||
this->targetDescriptionFile.getSystemAddressSpace().startAddress
|
||||
);
|
||||
|
||||
if (this->tryExecuteAbstractCommand(probingMemoryAccessCommand) == AbstractCommandError::NONE) {
|
||||
this->debugModuleDescriptor.memoryAccessStrategies.insert(MemoryAccessStrategy::ABSTRACT_COMMAND);
|
||||
}
|
||||
|
||||
if (this->debugModuleDescriptor.memoryAccessStrategies.empty()) {
|
||||
throw Exceptions::TargetOperationFailure{"Target doesn't support any known memory access strategies"};
|
||||
}
|
||||
|
||||
this->memoryAccessStrategy = this->determineMemoryAccessStrategy();
|
||||
Logger::debug(
|
||||
"Selected memory access strategy: " + (
|
||||
this->memoryAccessStrategy == MemoryAccessStrategy::ABSTRACT_COMMAND
|
||||
? std::string{"ABSTRACT_COMMAND"}
|
||||
: std::string{"PROGRAM_BUFFER"}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DebugTranslator::deactivate() {
|
||||
@@ -241,6 +297,8 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
if (!statusRegister.allHaveReset) {
|
||||
throw Exceptions::Exception{"Target took too long to reset"};
|
||||
}
|
||||
|
||||
this->initDebugControlStatusRegister();
|
||||
}
|
||||
|
||||
void DebugTranslator::setSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
@@ -252,7 +310,7 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
}
|
||||
|
||||
std::uint16_t DebugTranslator::getHardwareBreakpointCount() {
|
||||
return static_cast<std::uint16_t>(this->triggerDescriptorsByIndex.size());
|
||||
return static_cast<std::uint16_t>(this->debugModuleDescriptor.triggerDescriptorsByIndex.size());
|
||||
}
|
||||
|
||||
void DebugTranslator::setHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
@@ -305,7 +363,7 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
throw Exceptions::Exception{"Unknown hardware breakpoint"};
|
||||
}
|
||||
|
||||
const auto& triggerDescriptor = this->triggerDescriptorsByIndex.at(triggerIndexIt->second);
|
||||
const auto& triggerDescriptor = this->debugModuleDescriptor.triggerDescriptorsByIndex.at(triggerIndexIt->second);
|
||||
|
||||
this->clearTrigger(triggerDescriptor);
|
||||
this->triggerIndicesByBreakpointAddress.erase(address);
|
||||
@@ -314,7 +372,7 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
|
||||
void DebugTranslator::clearAllHardwareBreakpoints() {
|
||||
// To ensure that any untracked breakpoints are cleared, we clear all triggers on the target.
|
||||
for (const auto& [triggerIndex, triggerDescriptor] : this->triggerDescriptorsByIndex) {
|
||||
for (const auto& [triggerIndex, triggerDescriptor] : this->debugModuleDescriptor.triggerDescriptorsByIndex) {
|
||||
this->clearTrigger(triggerDescriptor);
|
||||
}
|
||||
|
||||
@@ -365,11 +423,11 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
) {
|
||||
// TODO: excluded addresses
|
||||
|
||||
const auto pageSize = 4;
|
||||
if ((startAddress % pageSize) != 0 || (bytes % pageSize) != 0) {
|
||||
constexpr auto alignTo = DebugTranslator::WORD_BYTE_SIZE;
|
||||
if ((startAddress % alignTo) != 0 || (bytes % alignTo) != 0) {
|
||||
// Alignment required
|
||||
const auto alignedStartAddress = this->alignMemoryAddress(startAddress, pageSize);
|
||||
const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), pageSize);
|
||||
const auto alignedStartAddress = this->alignMemoryAddress(startAddress, alignTo);
|
||||
const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), alignTo);
|
||||
|
||||
const auto memoryBuffer = this->readMemory(
|
||||
addressSpaceDescriptor,
|
||||
@@ -383,7 +441,15 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
return TargetMemoryBuffer{offset, offset + bytes};
|
||||
}
|
||||
|
||||
return this->readMemoryViaAbstractCommand(startAddress, bytes);
|
||||
if (this->memoryAccessStrategy == MemoryAccessStrategy::PROGRAM_BUFFER) {
|
||||
return this->readMemoryViaProgramBuffer(startAddress, bytes);
|
||||
}
|
||||
|
||||
if (this->memoryAccessStrategy == MemoryAccessStrategy::ABSTRACT_COMMAND) {
|
||||
return this->readMemoryViaAbstractCommand(startAddress, bytes);
|
||||
}
|
||||
|
||||
throw Exceptions::InternalFatalErrorException{"Unknown selected memory access strategy"};
|
||||
}
|
||||
|
||||
void DebugTranslator::writeMemory(
|
||||
@@ -392,9 +458,7 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
TargetMemoryAddress startAddress,
|
||||
TargetMemoryBufferSpan buffer
|
||||
) {
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
|
||||
constexpr auto alignTo = TargetMemorySize{4};
|
||||
constexpr auto alignTo = DebugTranslator::WORD_BYTE_SIZE;
|
||||
const auto bytes = static_cast<TargetMemorySize>(buffer.size());
|
||||
if ((startAddress % alignTo) != 0 || (bytes % alignTo) != 0) {
|
||||
/*
|
||||
@@ -447,7 +511,15 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
);
|
||||
}
|
||||
|
||||
return this->writeMemoryViaAbstractCommand(startAddress, buffer);
|
||||
if (this->memoryAccessStrategy == MemoryAccessStrategy::PROGRAM_BUFFER) {
|
||||
return this->writeMemoryViaProgramBuffer(startAddress, buffer);
|
||||
}
|
||||
|
||||
if (this->memoryAccessStrategy == MemoryAccessStrategy::ABSTRACT_COMMAND) {
|
||||
return this->writeMemoryViaAbstractCommand(startAddress, buffer);
|
||||
}
|
||||
|
||||
throw Exceptions::InternalFatalErrorException{"Unknown selected memory access strategy"};
|
||||
}
|
||||
|
||||
std::vector<DebugModule::HartIndex> DebugTranslator::discoverHartIndices() {
|
||||
@@ -500,11 +572,11 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
const auto selectRegValue = TriggerModule::Registers::TriggerSelect{triggerIndex}.value();
|
||||
|
||||
const auto writeSelectError = this->tryWriteCpuRegister(CpuRegisterNumber::TRIGGER_SELECT, selectRegValue);
|
||||
if (writeSelectError == DebugModule::AbstractCommandError::EXCEPTION) {
|
||||
if (writeSelectError == AbstractCommandError::EXCEPTION) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (writeSelectError != DebugModule::AbstractCommandError::NONE) {
|
||||
if (writeSelectError != AbstractCommandError::NONE) {
|
||||
throw Exceptions::Exception{
|
||||
"Failed to write to TRIGGER_SELECT register - abstract command error: 0x"
|
||||
+ Services::StringService::toHex(static_cast<std::uint8_t>(writeSelectError))
|
||||
@@ -614,35 +686,49 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
}
|
||||
}
|
||||
|
||||
Expected<RegisterValue, DebugModule::AbstractCommandError> DebugTranslator::tryReadCpuRegister(
|
||||
RegisterNumber number
|
||||
) {
|
||||
using DebugModule::Registers::RegisterAccessControlField;
|
||||
void DebugTranslator::initDebugControlStatusRegister() {
|
||||
this->writeDebugControlStatusRegister(DebugControlStatusRegister{
|
||||
.breakUMode = true,
|
||||
.breakSMode = true,
|
||||
.breakMMode = true,
|
||||
.breakVUMode = true,
|
||||
.breakVSMode = true,
|
||||
});
|
||||
}
|
||||
|
||||
Expected<RegisterValue, AbstractCommandError> DebugTranslator::tryReadCpuRegister(
|
||||
RegisterNumber number,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
const auto commandError = this->tryExecuteAbstractCommand(AbstractCommandRegister{
|
||||
.control = RegisterAccessControlField{
|
||||
.registerNumber = number,
|
||||
.transfer = true,
|
||||
.flags = flags,
|
||||
.size= RegisterAccessControlField::RegisterSize::SIZE_32
|
||||
}.value(),
|
||||
.commandType = AbstractCommandRegister::CommandType::REGISTER_ACCESS
|
||||
});
|
||||
|
||||
if (commandError != DebugModule::AbstractCommandError::NONE) {
|
||||
if (commandError != AbstractCommandError::NONE) {
|
||||
return commandError;
|
||||
}
|
||||
|
||||
return this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
||||
}
|
||||
|
||||
Expected<RegisterValue, DebugModule::AbstractCommandError> DebugTranslator::tryReadCpuRegister(
|
||||
Registers::CpuRegisterNumber number
|
||||
Expected<RegisterValue, AbstractCommandError> DebugTranslator::tryReadCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
return this->tryReadCpuRegister(static_cast<RegisterNumber>(number));
|
||||
return this->tryReadCpuRegister(static_cast<RegisterNumber>(number), flags);
|
||||
}
|
||||
|
||||
RegisterValue DebugTranslator::readCpuRegister(RegisterNumber number) {
|
||||
const auto result = this->tryReadCpuRegister(number);
|
||||
RegisterValue DebugTranslator::readCpuRegister(
|
||||
RegisterNumber number,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
const auto result = this->tryReadCpuRegister(number, flags);
|
||||
|
||||
if (!result.hasValue()) {
|
||||
throw Exceptions::Exception{
|
||||
@@ -655,38 +741,46 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
return result.value();
|
||||
}
|
||||
|
||||
RegisterValue DebugTranslator::readCpuRegister(Registers::CpuRegisterNumber number) {
|
||||
return this->readCpuRegister(static_cast<RegisterNumber>(number));
|
||||
RegisterValue DebugTranslator::readCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
return this->readCpuRegister(static_cast<RegisterNumber>(number), flags);
|
||||
}
|
||||
|
||||
DebugModule::AbstractCommandError DebugTranslator::tryWriteCpuRegister(
|
||||
AbstractCommandError DebugTranslator::tryWriteCpuRegister(
|
||||
RegisterNumber number,
|
||||
RegisterValue value
|
||||
RegisterValue value,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
using DebugModule::Registers::RegisterAccessControlField;
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0, value);
|
||||
return this->tryExecuteAbstractCommand(AbstractCommandRegister{
|
||||
.control = RegisterAccessControlField{
|
||||
.registerNumber = number,
|
||||
.write = true,
|
||||
.transfer = true,
|
||||
.flags = flags,
|
||||
.size = RegisterAccessControlField::RegisterSize::SIZE_32
|
||||
}.value(),
|
||||
.commandType = AbstractCommandRegister::CommandType::REGISTER_ACCESS
|
||||
});
|
||||
}
|
||||
|
||||
DebugModule::AbstractCommandError DebugTranslator::tryWriteCpuRegister(
|
||||
AbstractCommandError DebugTranslator::tryWriteCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
RegisterValue value
|
||||
RegisterValue value,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
return this->tryWriteCpuRegister(static_cast<RegisterNumber>(number), value);
|
||||
return this->tryWriteCpuRegister(static_cast<RegisterNumber>(number), value, flags);
|
||||
}
|
||||
|
||||
void DebugTranslator::writeCpuRegister(RegisterNumber number, RegisterValue value) {
|
||||
const auto commandError = this->tryWriteCpuRegister(number, value);
|
||||
if (commandError != DebugModule::AbstractCommandError::NONE) {
|
||||
void DebugTranslator::writeCpuRegister(
|
||||
RegisterNumber number,
|
||||
RegisterValue value,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
const auto commandError = this->tryWriteCpuRegister(number, value, flags);
|
||||
if (commandError != AbstractCommandError::NONE) {
|
||||
throw Exceptions::Exception{
|
||||
"Failed to write to CPU register (number: 0x" + Services::StringService::toHex(number)
|
||||
+ ") - abstract command error: 0x"
|
||||
@@ -695,8 +789,12 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::writeCpuRegister(Registers::CpuRegisterNumber number, RegisterValue value) {
|
||||
this->writeCpuRegister(static_cast<RegisterNumber>(number), value);
|
||||
void DebugTranslator::writeCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
RegisterValue value,
|
||||
const RegisterAccessControlField::Flags& flags
|
||||
) {
|
||||
this->writeCpuRegister(static_cast<RegisterNumber>(number), value, flags);
|
||||
}
|
||||
|
||||
void DebugTranslator::writeDebugModuleControlRegister(const ControlRegister& controlRegister) {
|
||||
@@ -710,7 +808,14 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
);
|
||||
}
|
||||
|
||||
DebugModule::AbstractCommandError DebugTranslator::tryExecuteAbstractCommand(
|
||||
void DebugTranslator::clearAbstractCommandError() {
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_CONTROL_STATUS_REGISTER,
|
||||
AbstractControlStatusRegister{.commandError = AbstractCommandError::CLEAR}.value()
|
||||
);
|
||||
}
|
||||
|
||||
AbstractCommandError DebugTranslator::tryExecuteAbstractCommand(
|
||||
const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister
|
||||
) {
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
@@ -719,9 +824,6 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
);
|
||||
|
||||
auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
if (abstractStatusRegister.commandError != DebugModule::AbstractCommandError::NONE) {
|
||||
return abstractStatusRegister.commandError;
|
||||
}
|
||||
|
||||
for (
|
||||
auto attempts = 0;
|
||||
@@ -737,6 +839,10 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
throw Exceptions::Exception{"Abstract command took too long to execute"};
|
||||
}
|
||||
|
||||
if (abstractStatusRegister.commandError != AbstractCommandError::NONE) {
|
||||
this->clearAbstractCommandError();
|
||||
}
|
||||
|
||||
return abstractStatusRegister.commandError;
|
||||
}
|
||||
|
||||
@@ -744,7 +850,7 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister
|
||||
) {
|
||||
const auto commandError = this->tryExecuteAbstractCommand(abstractCommandRegister);
|
||||
if (commandError != DebugModule::AbstractCommandError::NONE) {
|
||||
if (commandError != AbstractCommandError::NONE) {
|
||||
throw Exceptions::Exception{
|
||||
"Failed to execute abstract command - error: 0x"
|
||||
+ Services::StringService::toHex(static_cast<std::uint8_t>(commandError))
|
||||
@@ -752,6 +858,24 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
}
|
||||
}
|
||||
|
||||
MemoryAccessStrategy DebugTranslator::determineMemoryAccessStrategy() {
|
||||
assert(!this->debugModuleDescriptor.memoryAccessStrategies.empty());
|
||||
|
||||
if (
|
||||
this->config.preferredMemoryAccessStrategy.has_value()
|
||||
&& this->debugModuleDescriptor.memoryAccessStrategies.contains(
|
||||
*(this->config.preferredMemoryAccessStrategy)
|
||||
)
|
||||
) {
|
||||
return *(this->config.preferredMemoryAccessStrategy);
|
||||
}
|
||||
|
||||
// Favour the abstract command strategy, as it seems to be faster on the targets currently supported by Bloom.
|
||||
return this->debugModuleDescriptor.memoryAccessStrategies.contains(MemoryAccessStrategy::ABSTRACT_COMMAND)
|
||||
? MemoryAccessStrategy::ABSTRACT_COMMAND
|
||||
: *(this->debugModuleDescriptor.memoryAccessStrategies.begin());
|
||||
}
|
||||
|
||||
TargetMemoryAddress DebugTranslator::alignMemoryAddress(TargetMemoryAddress address, TargetMemoryAddress alignTo) {
|
||||
return (address / alignTo) * alignTo;
|
||||
}
|
||||
@@ -766,10 +890,8 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes
|
||||
) {
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
|
||||
auto output = TargetMemoryBuffer{};
|
||||
output.reserve(bytes);
|
||||
assert(startAddress % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
assert(bytes % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
|
||||
/*
|
||||
* We only need to set the address once. No need to update it as we use the post-increment function to
|
||||
@@ -785,6 +907,9 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
.commandType = AbstractCommandRegister::CommandType::MEMORY_ACCESS
|
||||
};
|
||||
|
||||
auto output = TargetMemoryBuffer{};
|
||||
output.reserve(bytes);
|
||||
|
||||
for (auto address = startAddress; address <= (startAddress + bytes - 1); address += 4) {
|
||||
this->executeAbstractCommand(command);
|
||||
|
||||
@@ -803,10 +928,12 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
) {
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
assert(startAddress % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
assert(buffer.size() % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress);
|
||||
|
||||
constexpr auto command = AbstractCommandRegister{
|
||||
static constexpr auto command = AbstractCommandRegister{
|
||||
.control = MemoryAccessControlField{
|
||||
.write = true,
|
||||
.postIncrement = true,
|
||||
@@ -815,7 +942,7 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
.commandType = AbstractCommandRegister::CommandType::MEMORY_ACCESS
|
||||
};
|
||||
|
||||
for (auto offset = TargetMemoryAddress{0}; offset < buffer.size(); offset += 4) {
|
||||
for (auto offset = std::size_t{0}; offset < buffer.size(); offset += 4) {
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_DATA_0,
|
||||
static_cast<RegisterValue>(
|
||||
@@ -830,10 +957,236 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
}
|
||||
}
|
||||
|
||||
Targets::TargetMemoryBuffer DebugTranslator::readMemoryViaProgramBuffer(
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes
|
||||
) {
|
||||
using namespace Targets::RiscV::Opcodes;
|
||||
assert(startAddress % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
assert(bytes % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
|
||||
static constexpr auto programOpcodes = std::to_array<Opcodes::Opcode>({
|
||||
Opcodes::Lw{
|
||||
.destinationRegister = Opcodes::GprNumber::X9,
|
||||
.baseAddressRegister = Opcodes::GprNumber::X8,
|
||||
.addressOffset = 0
|
||||
}.opcode(),
|
||||
Opcodes::Addi{
|
||||
.destinationRegister = Opcodes::GprNumber::X8,
|
||||
.sourceRegister = Opcodes::GprNumber::X8,
|
||||
.value = DebugTranslator::WORD_BYTE_SIZE
|
||||
}.opcode(),
|
||||
Opcodes::Ebreak,
|
||||
});
|
||||
|
||||
if (programOpcodes.size() > this->debugModuleDescriptor.programBufferSize) {
|
||||
throw Exceptions::Exception{
|
||||
"Cannot read memory via RISC-V debug module program buffer - insufficient program buffer size"
|
||||
};
|
||||
}
|
||||
|
||||
auto preservedX8Register = PreservedCpuRegister{CpuRegisterNumber::GPR_X8, *this};
|
||||
auto preservedX9Register = PreservedCpuRegister{CpuRegisterNumber::GPR_X9, *this};
|
||||
|
||||
try {
|
||||
this->writeProgramBuffer(programOpcodes);
|
||||
this->writeCpuRegister(CpuRegisterNumber::GPR_X8, startAddress, {.postExecute = true});
|
||||
|
||||
auto output = Targets::TargetMemoryBuffer{};
|
||||
output.reserve(bytes);
|
||||
|
||||
if (bytes == DebugTranslator::WORD_BYTE_SIZE) {
|
||||
const auto word = this->readCpuRegister(CpuRegisterNumber::GPR_X9);
|
||||
output.emplace_back(static_cast<unsigned char>(word));
|
||||
output.emplace_back(static_cast<unsigned char>(word >> 8));
|
||||
output.emplace_back(static_cast<unsigned char>(word >> 16));
|
||||
output.emplace_back(static_cast<unsigned char>(word >> 24));
|
||||
|
||||
preservedX8Register.restore();
|
||||
preservedX9Register.restore();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Populate the abstract command register with a register access command, to read X9 into data0.
|
||||
this->readCpuRegister(CpuRegisterNumber::GPR_X9, {.postExecute = true});
|
||||
|
||||
/*
|
||||
* At this point, the program buffer will have already been executed twice, with the first word currently
|
||||
* residing in data0, and the second in X9.
|
||||
*
|
||||
* The abstract command register will be populated with a register access command, to read X9 into data0,
|
||||
* with 'postexec' enabled. Enabling auto execution at this point will mean that the abstract command will
|
||||
* be executed every time we access the data0 register, resulting in the next word being copied into
|
||||
* data0 (from X9) and the program buffer being executed again (filling X9 with another word).
|
||||
*
|
||||
* To avoid reading an excess of words (which could result in an out-of-bounds exception), we only enable
|
||||
* auto execution if we require more data that what has already been read.
|
||||
*/
|
||||
const auto autoExecutionEnabled = bytes > (DebugTranslator::WORD_BYTE_SIZE * 2);
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_COMMAND_AUTO_EXECUTE_REGISTER,
|
||||
AbstractCommandAutoExecuteRegister{.onData0Access = autoExecutionEnabled}.value()
|
||||
);
|
||||
|
||||
while (output.size() < (bytes - DebugTranslator::WORD_BYTE_SIZE)) {
|
||||
if (autoExecutionEnabled && output.size() >= (bytes - DebugTranslator::WORD_BYTE_SIZE * 2)) {
|
||||
/*
|
||||
* We're on the second to last word, which has already been read and currently resides in data0.
|
||||
* The last word has also been read and currently resides in X9.
|
||||
*
|
||||
* Disable auto execution here to prevent any further reads.
|
||||
*/
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_COMMAND_AUTO_EXECUTE_REGISTER,
|
||||
AbstractCommandAutoExecuteRegister{}.value()
|
||||
);
|
||||
}
|
||||
|
||||
const auto word = this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
||||
output.emplace_back(static_cast<unsigned char>(word));
|
||||
output.emplace_back(static_cast<unsigned char>(word >> 8));
|
||||
output.emplace_back(static_cast<unsigned char>(word >> 16));
|
||||
output.emplace_back(static_cast<unsigned char>(word >> 24));
|
||||
}
|
||||
|
||||
const auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
if (abstractStatusRegister.commandError != AbstractCommandError::NONE) {
|
||||
this->clearAbstractCommandError();
|
||||
|
||||
throw Exceptions::Exception{
|
||||
"Program buffer execution failed - abstract command error: 0x"
|
||||
+ Services::StringService::toHex(abstractStatusRegister.commandError)
|
||||
};
|
||||
}
|
||||
|
||||
const auto lastWord = this->readCpuRegister(CpuRegisterNumber::GPR_X9);
|
||||
output.emplace_back(static_cast<unsigned char>(lastWord));
|
||||
output.emplace_back(static_cast<unsigned char>(lastWord >> 8));
|
||||
output.emplace_back(static_cast<unsigned char>(lastWord >> 16));
|
||||
output.emplace_back(static_cast<unsigned char>(lastWord >> 24));
|
||||
|
||||
preservedX8Register.restore();
|
||||
preservedX9Register.restore();
|
||||
|
||||
return output;
|
||||
|
||||
} catch (const Exceptions::Exception& exception) {
|
||||
preservedX8Register.restoreOnce();
|
||||
preservedX9Register.restoreOnce();
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::writeMemoryViaProgramBuffer(
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
) {
|
||||
using namespace Targets::RiscV::Opcodes;
|
||||
assert(startAddress % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
assert(buffer.size() % DebugTranslator::WORD_BYTE_SIZE == 0);
|
||||
|
||||
static constexpr auto programOpcodes = std::to_array<Opcodes::Opcode>({
|
||||
Opcodes::Sw{
|
||||
.baseAddressRegister = Opcodes::GprNumber::X8,
|
||||
.valueRegister = Opcodes::GprNumber::X9,
|
||||
.addressOffset = 0
|
||||
}.opcode(),
|
||||
Opcodes::Addi{
|
||||
.destinationRegister = Opcodes::GprNumber::X8,
|
||||
.sourceRegister = Opcodes::GprNumber::X8,
|
||||
.value = DebugTranslator::WORD_BYTE_SIZE
|
||||
}.opcode(),
|
||||
Opcodes::Ebreak,
|
||||
});
|
||||
|
||||
if (programOpcodes.size() > this->debugModuleDescriptor.programBufferSize) {
|
||||
throw Exceptions::Exception{
|
||||
"Cannot write to memory via RISC-V debug module program buffer - insufficient program buffer size"
|
||||
};
|
||||
}
|
||||
|
||||
auto preservedX8Register = PreservedCpuRegister{CpuRegisterNumber::GPR_X8, *this};
|
||||
auto preservedX9Register = PreservedCpuRegister{CpuRegisterNumber::GPR_X9, *this};
|
||||
|
||||
try {
|
||||
this->writeProgramBuffer(programOpcodes);
|
||||
this->writeCpuRegister(CpuRegisterNumber::GPR_X8, startAddress, {.postExecute = false});
|
||||
|
||||
this->writeCpuRegister(
|
||||
CpuRegisterNumber::GPR_X9,
|
||||
static_cast<RegisterValue>(
|
||||
(buffer[3] << 24)
|
||||
| (buffer[2] << 16)
|
||||
| (buffer[1] << 8)
|
||||
| (buffer[0])
|
||||
),
|
||||
{.postExecute = true}
|
||||
);
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_COMMAND_AUTO_EXECUTE_REGISTER,
|
||||
AbstractCommandAutoExecuteRegister{.onData0Access = true}.value()
|
||||
);
|
||||
|
||||
for (
|
||||
auto offset = std::size_t{DebugTranslator::WORD_BYTE_SIZE};
|
||||
offset < buffer.size();
|
||||
offset += DebugTranslator::WORD_BYTE_SIZE
|
||||
) {
|
||||
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->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_COMMAND_AUTO_EXECUTE_REGISTER,
|
||||
AbstractCommandAutoExecuteRegister{}.value()
|
||||
);
|
||||
|
||||
const auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
if (abstractStatusRegister.commandError != AbstractCommandError::NONE) {
|
||||
this->clearAbstractCommandError();
|
||||
|
||||
throw Exceptions::Exception{
|
||||
"Program buffer execution failed - abstract command error: 0x"
|
||||
+ Services::StringService::toHex(abstractStatusRegister.commandError)
|
||||
};
|
||||
}
|
||||
|
||||
preservedX8Register.restore();
|
||||
preservedX9Register.restore();
|
||||
|
||||
} catch (const Exceptions::Exception& exception) {
|
||||
preservedX8Register.restoreOnce();
|
||||
preservedX9Register.restoreOnce();
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::writeProgramBuffer(std::span<const Targets::RiscV::Opcodes::Opcode> opcodes) {
|
||||
assert(opcodes.size() <= 16);
|
||||
assert(opcodes.size() <= this->debugModuleDescriptor.programBufferSize);
|
||||
|
||||
auto programBufferAddress = static_cast<DebugModule::RegisterAddress>(RegisterAddress::PROGRAM_BUFFER_0);
|
||||
for (const auto& opcode : opcodes) {
|
||||
this->dtmInterface.writeDebugModuleRegister(programBufferAddress, opcode);
|
||||
++programBufferAddress;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<
|
||||
std::reference_wrapper<const TriggerModule::TriggerDescriptor>
|
||||
> DebugTranslator::getAvailableTrigger() {
|
||||
for (const auto& [index, descriptor] : this->triggerDescriptorsByIndex) {
|
||||
for (const auto& [index, descriptor] : this->debugModuleDescriptor.triggerDescriptorsByIndex) {
|
||||
if (this->allocatedTriggerIndices.contains(index)) {
|
||||
continue;
|
||||
}
|
||||
@@ -863,4 +1216,54 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
|
||||
throw Exceptions::Exception{"Unsupported trigger"};
|
||||
}
|
||||
|
||||
DebugTranslator::PreservedCpuRegister::PreservedCpuRegister(
|
||||
Registers::CpuRegisterNumber registerNumber,
|
||||
RegisterValue value,
|
||||
DebugTranslator& debugTranslator
|
||||
)
|
||||
: registerNumber(registerNumber)
|
||||
, value(value)
|
||||
, debugTranslator(debugTranslator)
|
||||
{}
|
||||
|
||||
DebugTranslator::PreservedCpuRegister::PreservedCpuRegister(
|
||||
Registers::CpuRegisterNumber registerNumber,
|
||||
DebugTranslator& debugTranslator
|
||||
)
|
||||
: PreservedCpuRegister(
|
||||
registerNumber,
|
||||
debugTranslator.readCpuRegister(registerNumber),
|
||||
debugTranslator
|
||||
)
|
||||
{}
|
||||
|
||||
void DebugTranslator::PreservedCpuRegister::restore() {
|
||||
try {
|
||||
this->debugTranslator.writeCpuRegister(this->registerNumber, this->value);
|
||||
this->restored = true;
|
||||
|
||||
} catch (const Exceptions::Exception& exception) {
|
||||
/*
|
||||
* If we fail to restore the value of a CPU register, we must raise this as a fatal error, as the target
|
||||
* will be left in an undefined state. More specifically, the state of the program running on the target
|
||||
* may be corrupted. We cannot recover from this.
|
||||
*
|
||||
* DeviceFailure exceptions are considered to be fatal. A clean shutdown will follow.
|
||||
*/
|
||||
throw Exceptions::DeviceFailure{
|
||||
"Failed to restore CPU register (number: 0x"
|
||||
+ Services::StringService::toHex(this->registerNumber) + ") - error: " + exception.getMessage()
|
||||
+ " - the target is now in an undefined state and may require a reset"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::PreservedCpuRegister::restoreOnce() {
|
||||
if (this->restored) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->restore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <optional>
|
||||
@@ -13,9 +14,12 @@
|
||||
|
||||
#include "DebugTransportModuleInterface.hpp"
|
||||
#include "DebugTranslatorConfig.hpp"
|
||||
#include "DebugModuleDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/RiscV/RiscVTargetConfig.hpp"
|
||||
#include "src/Targets/RiscV/Opcodes/Opcode.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "Registers/CpuRegisterNumbers.hpp"
|
||||
@@ -26,6 +30,7 @@
|
||||
#include "DebugModule/Registers/StatusRegister.hpp"
|
||||
#include "DebugModule/Registers/AbstractControlStatusRegister.hpp"
|
||||
#include "DebugModule/Registers/AbstractCommandRegister.hpp"
|
||||
#include "DebugModule/Registers/RegisterAccessControlField.hpp"
|
||||
|
||||
#include "TriggerModule/TriggerModule.hpp"
|
||||
#include "TriggerModule/TriggerDescriptor.hpp"
|
||||
@@ -89,6 +94,8 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
|
||||
private:
|
||||
static constexpr auto DEBUG_MODULE_RESPONSE_DELAY = std::chrono::microseconds{10};
|
||||
static constexpr auto MEMORY_ACCESS_ALIGNMENT_SIZE = ::Targets::TargetMemorySize{4};
|
||||
static constexpr auto WORD_BYTE_SIZE = ::Targets::TargetMemorySize{4};
|
||||
|
||||
DebugTransportModuleInterface& dtmInterface;
|
||||
const DebugTranslatorConfig& config;
|
||||
@@ -96,10 +103,10 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
const ::Targets::RiscV::TargetDescriptionFile& targetDescriptionFile;
|
||||
const ::Targets::RiscV::RiscVTargetConfig& targetConfig;
|
||||
|
||||
std::vector<DebugModule::HartIndex> hartIndices;
|
||||
DebugModule::HartIndex selectedHartIndex = 0;
|
||||
DebugModuleDescriptor debugModuleDescriptor = {};
|
||||
|
||||
std::unordered_map<TriggerModule::TriggerIndex, TriggerModule::TriggerDescriptor> triggerDescriptorsByIndex;
|
||||
DebugModule::HartIndex selectedHartIndex = 0;
|
||||
DebugModule::MemoryAccessStrategy memoryAccessStrategy = DebugModule::MemoryAccessStrategy::ABSTRACT_COMMAND;
|
||||
std::unordered_set<TriggerModule::TriggerIndex> allocatedTriggerIndices;
|
||||
std::unordered_map<Targets::TargetMemoryAddress, TriggerModule::TriggerIndex> triggerIndicesByBreakpointAddress;
|
||||
|
||||
@@ -114,26 +121,57 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
void enableDebugModule();
|
||||
void disableDebugModule();
|
||||
|
||||
Expected<RegisterValue, DebugModule::AbstractCommandError> tryReadCpuRegister(RegisterNumber number);
|
||||
Expected<RegisterValue, DebugModule::AbstractCommandError> tryReadCpuRegister(
|
||||
Registers::CpuRegisterNumber number
|
||||
);
|
||||
RegisterValue readCpuRegister(RegisterNumber number);
|
||||
RegisterValue readCpuRegister(Registers::CpuRegisterNumber number);
|
||||
void initDebugControlStatusRegister();
|
||||
|
||||
DebugModule::AbstractCommandError tryWriteCpuRegister(RegisterNumber number, RegisterValue value);
|
||||
DebugModule::AbstractCommandError tryWriteCpuRegister(Registers::CpuRegisterNumber number, RegisterValue value);
|
||||
void writeCpuRegister(RegisterNumber number, RegisterValue value);
|
||||
void writeCpuRegister(Registers::CpuRegisterNumber number, RegisterValue value);
|
||||
Expected<RegisterValue, DebugModule::AbstractCommandError> tryReadCpuRegister(
|
||||
RegisterNumber number,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
Expected<RegisterValue, DebugModule::AbstractCommandError> tryReadCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
RegisterValue readCpuRegister(
|
||||
RegisterNumber number,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
RegisterValue readCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
|
||||
DebugModule::AbstractCommandError tryWriteCpuRegister(
|
||||
RegisterNumber number,
|
||||
RegisterValue value,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
DebugModule::AbstractCommandError tryWriteCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
RegisterValue value,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
void writeCpuRegister(
|
||||
RegisterNumber number,
|
||||
RegisterValue value,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
void writeCpuRegister(
|
||||
Registers::CpuRegisterNumber number,
|
||||
RegisterValue value,
|
||||
const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {}
|
||||
);
|
||||
|
||||
void writeDebugModuleControlRegister(const DebugModule::Registers::ControlRegister& controlRegister);
|
||||
void writeDebugControlStatusRegister(const Registers::DebugControlStatusRegister& controlRegister);
|
||||
|
||||
void clearAbstractCommandError();
|
||||
DebugModule::AbstractCommandError tryExecuteAbstractCommand(
|
||||
const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister
|
||||
);
|
||||
void executeAbstractCommand(const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister);
|
||||
|
||||
DebugModule::MemoryAccessStrategy determineMemoryAccessStrategy();
|
||||
|
||||
Targets::TargetMemoryAddress alignMemoryAddress(
|
||||
Targets::TargetMemoryAddress address,
|
||||
Targets::TargetMemoryAddress alignTo
|
||||
@@ -149,7 +187,48 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
);
|
||||
|
||||
Targets::TargetMemoryBuffer readMemoryViaProgramBuffer(
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes
|
||||
);
|
||||
void writeMemoryViaProgramBuffer(
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
);
|
||||
|
||||
void writeProgramBuffer(std::span<const Targets::RiscV::Opcodes::Opcode> opcodes);
|
||||
|
||||
std::optional<std::reference_wrapper<const TriggerModule::TriggerDescriptor>> getAvailableTrigger();
|
||||
void clearTrigger(const TriggerModule::TriggerDescriptor& triggerDescriptor);
|
||||
|
||||
struct PreservedCpuRegister
|
||||
{
|
||||
const Registers::CpuRegisterNumber registerNumber;
|
||||
const RegisterValue value;
|
||||
|
||||
PreservedCpuRegister(
|
||||
Registers::CpuRegisterNumber registerNumber,
|
||||
RegisterValue value,
|
||||
DebugTranslator& debugTranslator
|
||||
);
|
||||
|
||||
PreservedCpuRegister(
|
||||
Registers::CpuRegisterNumber registerNumber,
|
||||
DebugTranslator& debugTranslator
|
||||
);
|
||||
|
||||
PreservedCpuRegister(const PreservedCpuRegister& other) = delete;
|
||||
PreservedCpuRegister& operator = (const PreservedCpuRegister& other) = delete;
|
||||
|
||||
PreservedCpuRegister(const PreservedCpuRegister&& other) = delete;
|
||||
PreservedCpuRegister& operator = (const PreservedCpuRegister&& other) = delete;
|
||||
|
||||
void restore();
|
||||
void restoreOnce();
|
||||
|
||||
private:
|
||||
DebugTranslator& debugTranslator;
|
||||
bool restored = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "DebugTranslatorConfig.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
@@ -10,5 +13,22 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
configNode["targetResponseTimeout"].as<std::int64_t>(this->targetResponseTimeout.count())
|
||||
};
|
||||
}
|
||||
|
||||
if (configNode["preferredMemoryAccessStrategy"]) {
|
||||
const auto strategy = configNode["preferredMemoryAccessStrategy"].as<std::string>();
|
||||
|
||||
if (strategy == "abstract-command") {
|
||||
this->preferredMemoryAccessStrategy = DebugModule::MemoryAccessStrategy::ABSTRACT_COMMAND;
|
||||
|
||||
} else if (strategy == "program-buffer") {
|
||||
this->preferredMemoryAccessStrategy = DebugModule::MemoryAccessStrategy::PROGRAM_BUFFER;
|
||||
|
||||
} else {
|
||||
Logger::error(
|
||||
"Invalid value (\"" + strategy + "\") provided for RISC-V debug translator config parameter "
|
||||
"('preferredMemoryAccessStrategy'). Parameter will be ignored."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include <optional>
|
||||
|
||||
#include "DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
@@ -13,6 +16,8 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
*/
|
||||
std::chrono::microseconds targetResponseTimeout = std::chrono::microseconds{100};
|
||||
|
||||
std::optional<DebugModule::MemoryAccessStrategy> preferredMemoryAccessStrategy = std::nullopt;
|
||||
|
||||
DebugTranslatorConfig() = default;
|
||||
explicit DebugTranslatorConfig(const YAML::Node& configNode);
|
||||
};
|
||||
|
||||
@@ -6,11 +6,15 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::Registers
|
||||
{
|
||||
enum class CpuRegisterNumber: RegisterNumber
|
||||
{
|
||||
DEBUG_CONTROL_STATUS_REGISTER = 0x07b0,
|
||||
TRIGGER_SELECT = 0x07a0,
|
||||
TRIGGER_DATA_1 = 0x07a1,
|
||||
TRIGGER_DATA_2 = 0x07a2,
|
||||
TRIGGER_DATA_3 = 0x07a3,
|
||||
TRIGGER_INFO = 0x07a4,
|
||||
DEBUG_CONTROL_STATUS_REGISTER = 0x07B0,
|
||||
TRIGGER_SELECT = 0x07A0,
|
||||
TRIGGER_DATA_1 = 0x07A1,
|
||||
TRIGGER_DATA_2 = 0x07A2,
|
||||
TRIGGER_DATA_3 = 0x07A3,
|
||||
TRIGGER_INFO = 0x07A4,
|
||||
|
||||
// GPRs
|
||||
GPR_X8 = 0x1008,
|
||||
GPR_X9 = 0x1009,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Services
|
||||
{
|
||||
@@ -26,6 +28,12 @@ namespace Services
|
||||
static std::string toHex(const std::vector<unsigned char>& data);
|
||||
static std::string toHex(const std::string& data);
|
||||
|
||||
template <typename Type>
|
||||
requires std::is_enum_v<Type>
|
||||
static std::string toHex(Type value) {
|
||||
return StringService::toHex(static_cast<std::underlying_type_t<Type>>(value));
|
||||
}
|
||||
|
||||
static std::string toBinaryStringWithMask(std::uint64_t value, std::uint64_t mask);
|
||||
|
||||
static std::vector<unsigned char> dataFromHex(const std::string& hexData);
|
||||
|
||||
26
src/Targets/RiscV/Opcodes/Addi.hpp
Normal file
26
src/Targets/RiscV/Opcodes/Addi.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "Opcode.hpp"
|
||||
|
||||
namespace Targets::RiscV::Opcodes
|
||||
{
|
||||
struct Addi
|
||||
{
|
||||
GprNumber destinationRegister;
|
||||
GprNumber sourceRegister;
|
||||
std::uint16_t value;
|
||||
|
||||
[[nodiscard]] constexpr Opcode opcode() const {
|
||||
assert(this->value <= 0xFFF);
|
||||
|
||||
return Opcode{0x13}
|
||||
| static_cast<Opcode>(this->destinationRegister) << 7
|
||||
| static_cast<Opcode>(this->sourceRegister) << 15
|
||||
| static_cast<Opcode>(this->value & 0xFFF) << 20
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
26
src/Targets/RiscV/Opcodes/Lb.hpp
Normal file
26
src/Targets/RiscV/Opcodes/Lb.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "Opcode.hpp"
|
||||
|
||||
namespace Targets::RiscV::Opcodes
|
||||
{
|
||||
struct Lb
|
||||
{
|
||||
GprNumber destinationRegister;
|
||||
GprNumber baseAddressRegister;
|
||||
std::uint16_t addressOffset;
|
||||
|
||||
[[nodiscard]] constexpr Opcode opcode() const {
|
||||
assert(this->addressOffset <= 0xFFF);
|
||||
|
||||
return Opcode{0x03}
|
||||
| static_cast<Opcode>(this->destinationRegister) << 7
|
||||
| static_cast<Opcode>(this->baseAddressRegister) << 15
|
||||
| static_cast<Opcode>(this->addressOffset & 0xFFF) << 20
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
26
src/Targets/RiscV/Opcodes/Lw.hpp
Normal file
26
src/Targets/RiscV/Opcodes/Lw.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "Opcode.hpp"
|
||||
|
||||
namespace Targets::RiscV::Opcodes
|
||||
{
|
||||
struct Lw
|
||||
{
|
||||
GprNumber destinationRegister;
|
||||
GprNumber baseAddressRegister;
|
||||
std::uint16_t addressOffset;
|
||||
|
||||
[[nodiscard]] constexpr Opcode opcode() const {
|
||||
assert(this->addressOffset <= 0xFFF);
|
||||
|
||||
return Opcode{0x2003}
|
||||
| static_cast<Opcode>(this->destinationRegister) << 7
|
||||
| static_cast<Opcode>(this->baseAddressRegister) << 15
|
||||
| static_cast<Opcode>(this->addressOffset & 0xFFF) << 20
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
17
src/Targets/RiscV/Opcodes/Opcode.hpp
Normal file
17
src/Targets/RiscV/Opcodes/Opcode.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Targets::RiscV::Opcodes
|
||||
{
|
||||
using Opcode = std::uint32_t;
|
||||
|
||||
enum class GprNumber: std::uint8_t
|
||||
{
|
||||
X0 = 0, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7, X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13,
|
||||
X14 = 14, X15 = 15, X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23, X24 = 24,
|
||||
X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, X31 = 31,
|
||||
};
|
||||
|
||||
static constexpr auto Ebreak = Opcode{0x00100073};
|
||||
}
|
||||
27
src/Targets/RiscV/Opcodes/Sb.hpp
Normal file
27
src/Targets/RiscV/Opcodes/Sb.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "Opcode.hpp"
|
||||
|
||||
namespace Targets::RiscV::Opcodes
|
||||
{
|
||||
struct Sb
|
||||
{
|
||||
GprNumber baseAddressRegister;
|
||||
GprNumber valueRegister;
|
||||
std::uint16_t addressOffset;
|
||||
|
||||
[[nodiscard]] constexpr Opcode opcode() const {
|
||||
assert(this->addressOffset <= 0xFFF);
|
||||
|
||||
return Opcode{0x23}
|
||||
| static_cast<Opcode>(this->addressOffset & 0x1F) << 7
|
||||
| static_cast<Opcode>(this->baseAddressRegister) << 15
|
||||
| static_cast<Opcode>(this->valueRegister) << 20
|
||||
| static_cast<Opcode>((this->addressOffset >> 5) & 0x7F) << 25
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
27
src/Targets/RiscV/Opcodes/Sw.hpp
Normal file
27
src/Targets/RiscV/Opcodes/Sw.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "Opcode.hpp"
|
||||
|
||||
namespace Targets::RiscV::Opcodes
|
||||
{
|
||||
struct Sw
|
||||
{
|
||||
GprNumber baseAddressRegister;
|
||||
GprNumber valueRegister;
|
||||
std::uint16_t addressOffset;
|
||||
|
||||
[[nodiscard]] constexpr Opcode opcode() const {
|
||||
assert(this->addressOffset <= 0xFFF);
|
||||
|
||||
return Opcode{0x2023}
|
||||
| static_cast<Opcode>(this->addressOffset & 0x1F) << 7
|
||||
| static_cast<Opcode>(this->baseAddressRegister) << 15
|
||||
| static_cast<Opcode>(this->valueRegister) << 20
|
||||
| static_cast<Opcode>((this->addressOffset >> 5) & 0x7F) << 25
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user