diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp index 66066319..adc06014 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp @@ -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, }; } diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractCommandAutoExecuteRegister.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractCommandAutoExecuteRegister.hpp new file mode 100644 index 00000000..d01b17e4 --- /dev/null +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractCommandAutoExecuteRegister.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include + +#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(value & 0x01U), + .onData1Access = static_cast(value & (0x01U << 1)), + .onData2Access = static_cast(value & (0x01U << 2)), + .onData3Access = static_cast(value & (0x01U << 3)), + .onData4Access = static_cast(value & (0x01U << 4)), + .onData5Access = static_cast(value & (0x01U << 5)), + .onData6Access = static_cast(value & (0x01U << 6)), + .onData7Access = static_cast(value & (0x01U << 7)), + .onData8Access = static_cast(value & (0x01U << 8)), + .onData9Access = static_cast(value & (0x01U << 9)), + .onData10Access = static_cast(value & (0x01U << 10)), + .onData11Access = static_cast(value & (0x01U << 11)), + .onProgramBuffer0Access = static_cast(value & (0x01U << 16)), + .onProgramBuffer1Access = static_cast(value & (0x01U << 17)), + .onProgramBuffer2Access = static_cast(value & (0x01U << 18)), + .onProgramBuffer3Access = static_cast(value & (0x01U << 19)), + .onProgramBuffer4Access = static_cast(value & (0x01U << 20)), + .onProgramBuffer5Access = static_cast(value & (0x01U << 21)), + .onProgramBuffer6Access = static_cast(value & (0x01U << 22)), + .onProgramBuffer7Access = static_cast(value & (0x01U << 23)), + .onProgramBuffer8Access = static_cast(value & (0x01U << 24)), + .onProgramBuffer9Access = static_cast(value & (0x01U << 25)), + .onProgramBuffer10Access = static_cast(value & (0x01U << 26)), + .onProgramBuffer11Access = static_cast(value & (0x01U << 27)), + .onProgramBuffer12Access = static_cast(value & (0x01U << 28)), + .onProgramBuffer13Access = static_cast(value & (0x01U << 29)), + .onProgramBuffer14Access = static_cast(value & (0x01U << 30)), + .onProgramBuffer15Access = static_cast(value & (0x01U << 31)), + }; + } + + [[nodiscard]] constexpr RegisterValue value() const { + return RegisterValue{0} + | static_cast(this->onData0Access) + | static_cast(this->onData1Access) << 1 + | static_cast(this->onData2Access) << 2 + | static_cast(this->onData3Access) << 3 + | static_cast(this->onData4Access) << 4 + | static_cast(this->onData5Access) << 5 + | static_cast(this->onData6Access) << 6 + | static_cast(this->onData7Access) << 7 + | static_cast(this->onData8Access) << 8 + | static_cast(this->onData9Access) << 9 + | static_cast(this->onData10Access) << 10 + | static_cast(this->onData11Access) << 11 + | static_cast(this->onProgramBuffer0Access) << 16 + | static_cast(this->onProgramBuffer1Access) << 17 + | static_cast(this->onProgramBuffer2Access) << 18 + | static_cast(this->onProgramBuffer3Access) << 19 + | static_cast(this->onProgramBuffer4Access) << 20 + | static_cast(this->onProgramBuffer5Access) << 21 + | static_cast(this->onProgramBuffer6Access) << 22 + | static_cast(this->onProgramBuffer7Access) << 23 + | static_cast(this->onProgramBuffer8Access) << 24 + | static_cast(this->onProgramBuffer9Access) << 25 + | static_cast(this->onProgramBuffer10Access) << 26 + | static_cast(this->onProgramBuffer11Access) << 27 + | static_cast(this->onProgramBuffer12Access) << 28 + | static_cast(this->onProgramBuffer13Access) << 29 + | static_cast(this->onProgramBuffer14Access) << 30 + | static_cast(this->onProgramBuffer15Access) << 31 + ; + } + }; +} diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractControlStatusRegister.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractControlStatusRegister.hpp index 56cd8b4d..bb845be0 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractControlStatusRegister.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractControlStatusRegister.hpp @@ -33,10 +33,5 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers | static_cast(this->programBufferSize & 0x1F) << 24 ; } - - constexpr void clearCommandError() { - // Setting all of the bits will clear the field - this->commandError = AbstractCommandError::OTHER; - } }; } diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAccessControlField.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAccessControlField.hpp index 79e29881..e1843f09 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAccessControlField.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAccessControlField.hpp @@ -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(value & 0xFFFF), .write = static_cast(value & (0x01 << 16)), .transfer = static_cast(value & (0x01 << 17)), - .postExecute = static_cast(value & (0x01 << 18)), - .postIncrement = static_cast(value & (0x01 << 19)), + .flags = { + .postExecute = static_cast(value & (0x01 << 18)), + .postIncrement = static_cast(value & (0x01 << 19)), + }, .size = static_cast((value >> 20) & 0x07), }; } @@ -38,8 +45,8 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers | static_cast(this->registerNumber) | static_cast(this->write) << 16 | static_cast(this->transfer) << 17 - | static_cast(this->postExecute) << 18 - | static_cast(this->postIncrement) << 19 + | static_cast(this->flags.postExecute) << 18 + | static_cast(this->flags.postIncrement) << 19 | static_cast(this->size) << 20 ; } diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAddresses.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAddresses.hpp index e13a01da..2801a459 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAddresses.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAddresses.hpp @@ -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, }; } diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModuleDescriptor.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModuleDescriptor.hpp new file mode 100644 index 00000000..416f5629 --- /dev/null +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModuleDescriptor.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include + +#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 hartIndices; + + std::unordered_set memoryAccessStrategies; + std::uint8_t programBufferSize = 0; + + std::unordered_map triggerDescriptorsByIndex; + }; +} diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp index 6a43043b..fa163403 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp @@ -6,11 +6,19 @@ #include #include #include +#include #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(this->triggerDescriptorsByIndex.size()); + return static_cast(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(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 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(writeSelectError)) @@ -614,35 +686,49 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec } } - Expected 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 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 DebugTranslator::tryReadCpuRegister( - Registers::CpuRegisterNumber number + Expected DebugTranslator::tryReadCpuRegister( + Registers::CpuRegisterNumber number, + const RegisterAccessControlField::Flags& flags ) { - return this->tryReadCpuRegister(static_cast(number)); + return this->tryReadCpuRegister(static_cast(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(number)); + RegisterValue DebugTranslator::readCpuRegister( + Registers::CpuRegisterNumber number, + const RegisterAccessControlField::Flags& flags + ) { + return this->readCpuRegister(static_cast(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(number), value); + return this->tryWriteCpuRegister(static_cast(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(number), value); + void DebugTranslator::writeCpuRegister( + Registers::CpuRegisterNumber number, + RegisterValue value, + const RegisterAccessControlField::Flags& flags + ) { + this->writeCpuRegister(static_cast(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(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( @@ -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::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(word)); + output.emplace_back(static_cast(word >> 8)); + output.emplace_back(static_cast(word >> 16)); + output.emplace_back(static_cast(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(word)); + output.emplace_back(static_cast(word >> 8)); + output.emplace_back(static_cast(word >> 16)); + output.emplace_back(static_cast(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(lastWord)); + output.emplace_back(static_cast(lastWord >> 8)); + output.emplace_back(static_cast(lastWord >> 16)); + output.emplace_back(static_cast(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::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( + (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( + (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 opcodes) { + assert(opcodes.size() <= 16); + assert(opcodes.size() <= this->debugModuleDescriptor.programBufferSize); + + auto programBufferAddress = static_cast(RegisterAddress::PROGRAM_BUFFER_0); + for (const auto& opcode : opcodes) { + this->dtmInterface.writeDebugModuleRegister(programBufferAddress, opcode); + ++programBufferAddress; + } + } + std::optional< std::reference_wrapper > 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(); + } } diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp index d34c4e4e..993bfdad 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -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 hartIndices; - DebugModule::HartIndex selectedHartIndex = 0; + DebugModuleDescriptor debugModuleDescriptor = {}; - std::unordered_map triggerDescriptorsByIndex; + DebugModule::HartIndex selectedHartIndex = 0; + DebugModule::MemoryAccessStrategy memoryAccessStrategy = DebugModule::MemoryAccessStrategy::ABSTRACT_COMMAND; std::unordered_set allocatedTriggerIndices; std::unordered_map triggerIndicesByBreakpointAddress; @@ -114,26 +121,57 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec void enableDebugModule(); void disableDebugModule(); - Expected tryReadCpuRegister(RegisterNumber number); - Expected 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 tryReadCpuRegister( + RegisterNumber number, + const DebugModule::Registers::RegisterAccessControlField::Flags& flags = {} + ); + Expected 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 opcodes); + std::optional> 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; + }; }; } diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.cpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.cpp index 4084005c..a6ec11e5 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.cpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.cpp @@ -1,6 +1,9 @@ #include "DebugTranslatorConfig.hpp" #include +#include + +#include "src/Logger/Logger.hpp" namespace DebugToolDrivers::Protocols::RiscVDebugSpec { @@ -10,5 +13,22 @@ namespace DebugToolDrivers::Protocols::RiscVDebugSpec configNode["targetResponseTimeout"].as(this->targetResponseTimeout.count()) }; } + + if (configNode["preferredMemoryAccessStrategy"]) { + const auto strategy = configNode["preferredMemoryAccessStrategy"].as(); + + 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." + ); + } + } } } diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.hpp index 574c9fe6..3725900e 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.hpp @@ -2,6 +2,9 @@ #include #include +#include + +#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 preferredMemoryAccessStrategy = std::nullopt; + DebugTranslatorConfig() = default; explicit DebugTranslatorConfig(const YAML::Node& configNode); }; diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp index cf1e455e..56879cb6 100644 --- a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp @@ -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, }; } diff --git a/src/Services/StringService.hpp b/src/Services/StringService.hpp index 3f015b9f..231d9872 100644 --- a/src/Services/StringService.hpp +++ b/src/Services/StringService.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace Services { @@ -26,6 +28,12 @@ namespace Services static std::string toHex(const std::vector& data); static std::string toHex(const std::string& data); + template + requires std::is_enum_v + static std::string toHex(Type value) { + return StringService::toHex(static_cast>(value)); + } + static std::string toBinaryStringWithMask(std::uint64_t value, std::uint64_t mask); static std::vector dataFromHex(const std::string& hexData); diff --git a/src/Targets/RiscV/Opcodes/Addi.hpp b/src/Targets/RiscV/Opcodes/Addi.hpp new file mode 100644 index 00000000..1e117fac --- /dev/null +++ b/src/Targets/RiscV/Opcodes/Addi.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#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(this->destinationRegister) << 7 + | static_cast(this->sourceRegister) << 15 + | static_cast(this->value & 0xFFF) << 20 + ; + } + }; +} diff --git a/src/Targets/RiscV/Opcodes/Lb.hpp b/src/Targets/RiscV/Opcodes/Lb.hpp new file mode 100644 index 00000000..2d8ba330 --- /dev/null +++ b/src/Targets/RiscV/Opcodes/Lb.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#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(this->destinationRegister) << 7 + | static_cast(this->baseAddressRegister) << 15 + | static_cast(this->addressOffset & 0xFFF) << 20 + ; + } + }; +} diff --git a/src/Targets/RiscV/Opcodes/Lw.hpp b/src/Targets/RiscV/Opcodes/Lw.hpp new file mode 100644 index 00000000..33051359 --- /dev/null +++ b/src/Targets/RiscV/Opcodes/Lw.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#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(this->destinationRegister) << 7 + | static_cast(this->baseAddressRegister) << 15 + | static_cast(this->addressOffset & 0xFFF) << 20 + ; + } + }; +} diff --git a/src/Targets/RiscV/Opcodes/Opcode.hpp b/src/Targets/RiscV/Opcodes/Opcode.hpp new file mode 100644 index 00000000..9f8b0977 --- /dev/null +++ b/src/Targets/RiscV/Opcodes/Opcode.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +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}; +} diff --git a/src/Targets/RiscV/Opcodes/Sb.hpp b/src/Targets/RiscV/Opcodes/Sb.hpp new file mode 100644 index 00000000..bd6b99f1 --- /dev/null +++ b/src/Targets/RiscV/Opcodes/Sb.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#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(this->addressOffset & 0x1F) << 7 + | static_cast(this->baseAddressRegister) << 15 + | static_cast(this->valueRegister) << 20 + | static_cast((this->addressOffset >> 5) & 0x7F) << 25 + ; + } + }; +} diff --git a/src/Targets/RiscV/Opcodes/Sw.hpp b/src/Targets/RiscV/Opcodes/Sw.hpp new file mode 100644 index 00000000..59162328 --- /dev/null +++ b/src/Targets/RiscV/Opcodes/Sw.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#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(this->addressOffset & 0x1F) << 7 + | static_cast(this->baseAddressRegister) << 15 + | static_cast(this->valueRegister) << 20 + | static_cast((this->addressOffset >> 5) & 0x7F) << 25 + ; + } + }; +}