2023-11-22 00:38:40 +00:00
|
|
|
#include "RiscV.hpp"
|
|
|
|
|
|
|
|
|
|
#include <thread>
|
|
|
|
|
#include <chrono>
|
2023-11-25 07:45:31 +00:00
|
|
|
#include <limits>
|
2023-11-25 19:09:14 +00:00
|
|
|
#include <cmath>
|
2023-11-22 00:38:40 +00:00
|
|
|
|
2023-11-23 23:31:13 +00:00
|
|
|
#include "Registers/RegisterNumbers.hpp"
|
2023-11-22 00:38:40 +00:00
|
|
|
#include "DebugModule/Registers/RegisterAddresses.hpp"
|
2023-11-23 16:32:53 +00:00
|
|
|
#include "DebugModule/Registers/RegisterAccessControlField.hpp"
|
2023-11-25 19:09:14 +00:00
|
|
|
#include "DebugModule/Registers/MemoryAccessControlField.hpp"
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
#include "src/Exceptions/Exception.hpp"
|
2023-11-23 12:59:36 +00:00
|
|
|
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
2023-11-22 00:38:40 +00:00
|
|
|
|
2023-11-23 16:35:09 +00:00
|
|
|
#include "src/Services/StringService.hpp"
|
|
|
|
|
|
2023-11-22 00:38:40 +00:00
|
|
|
#include "src/Logger/Logger.hpp"
|
|
|
|
|
|
|
|
|
|
namespace Targets::RiscV
|
|
|
|
|
{
|
2023-11-23 16:34:35 +00:00
|
|
|
using Registers::DebugControlStatusRegister;
|
|
|
|
|
|
2023-11-23 17:46:32 +00:00
|
|
|
using DebugModule::Registers::RegisterAddress;
|
2023-11-22 00:38:40 +00:00
|
|
|
using DebugModule::Registers::ControlRegister;
|
|
|
|
|
using DebugModule::Registers::StatusRegister;
|
2023-11-23 15:21:46 +00:00
|
|
|
using DebugModule::Registers::AbstractControlStatusRegister;
|
|
|
|
|
using DebugModule::Registers::AbstractCommandRegister;
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
RiscV::RiscV(const TargetConfig& targetConfig)
|
|
|
|
|
: name("CH32X035C8T6") // TODO: TDF
|
2023-11-24 15:19:52 +00:00
|
|
|
, stackPointerRegisterDescriptor(
|
|
|
|
|
RiscVRegisterDescriptor(
|
|
|
|
|
TargetRegisterType::STACK_POINTER,
|
|
|
|
|
static_cast<RegisterNumber>(Registers::RegisterNumber::STACK_POINTER_X2),
|
|
|
|
|
4,
|
|
|
|
|
TargetMemoryType::OTHER,
|
|
|
|
|
"SP",
|
|
|
|
|
"CPU",
|
|
|
|
|
"Stack Pointer Register",
|
|
|
|
|
TargetRegisterAccess(true, true)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
this->loadRegisterDescriptors();
|
|
|
|
|
}
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
bool RiscV::supportsDebugTool(DebugTool* debugTool) {
|
|
|
|
|
return debugTool->getRiscVDebugInterface() != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::setDebugTool(DebugTool* debugTool) {
|
|
|
|
|
this->riscVDebugInterface = debugTool->getRiscVDebugInterface();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::activate() {
|
|
|
|
|
this->riscVDebugInterface->activate({});
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
this->hartIndices = this->discoverHartIndices();
|
|
|
|
|
|
|
|
|
|
if (this->hartIndices.empty()) {
|
|
|
|
|
throw Exceptions::TargetOperationFailure("Failed to discover a single RISC-V hart");
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:56:26 +00:00
|
|
|
Logger::debug("Discovered RISC-V harts: " + std::to_string(this->hartIndices.size()));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We only support MCUs with a single hart, for now. So select the first index and ensure that this is
|
|
|
|
|
* explicitly communicated to the user.
|
|
|
|
|
*/
|
|
|
|
|
if (this->hartIndices.size() > 1) {
|
|
|
|
|
Logger::warning(
|
|
|
|
|
"Bloom only supports debugging a single RISC-V hart - selecting first available hart"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->selectedHartIndex = *(this->hartIndices.begin());
|
|
|
|
|
Logger::info("Selected RISC-V hart index: " + std::to_string(this->selectedHartIndex));
|
2023-11-22 00:38:40 +00:00
|
|
|
|
2023-11-25 19:06:23 +00:00
|
|
|
/*
|
|
|
|
|
* Disabling the debug module before enabling it will clear any state from a previous debug session that
|
|
|
|
|
* wasn't terminated properly.
|
|
|
|
|
*/
|
|
|
|
|
this->disableDebugModule();
|
|
|
|
|
this->enableDebugModule();
|
|
|
|
|
|
2023-11-22 00:38:40 +00:00
|
|
|
this->stop();
|
2023-11-25 21:02:15 +00:00
|
|
|
this->reset();
|
2023-11-23 16:32:53 +00:00
|
|
|
|
|
|
|
|
auto debugControlStatusRegister = this->readDebugControlStatusRegister();
|
|
|
|
|
debugControlStatusRegister.breakUMode = true;
|
|
|
|
|
debugControlStatusRegister.breakSMode = true;
|
|
|
|
|
debugControlStatusRegister.breakMMode = true;
|
|
|
|
|
|
|
|
|
|
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::deactivate() {
|
2023-11-25 19:06:23 +00:00
|
|
|
if (this->getState() != TargetState::RUNNING) {
|
|
|
|
|
this->run();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->disableDebugModule();
|
2023-11-22 00:38:40 +00:00
|
|
|
this->riscVDebugInterface->deactivate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TargetDescriptor RiscV::getDescriptor() {
|
|
|
|
|
return TargetDescriptor(
|
|
|
|
|
"TDF ID",
|
|
|
|
|
TargetFamily::RISC_V,
|
|
|
|
|
this->name,
|
|
|
|
|
"TDF VENDOR NAME",
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
Targets::TargetMemoryType::FLASH,
|
|
|
|
|
TargetMemoryDescriptor(
|
|
|
|
|
Targets::TargetMemoryType::FLASH,
|
|
|
|
|
Targets::TargetMemoryAddressRange(0x00, 0x1000),
|
|
|
|
|
Targets::TargetMemoryAccess(true, true, false)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-11-24 15:19:52 +00:00
|
|
|
{this->registerDescriptorsById.begin(), this->registerDescriptorsById.end()},
|
2023-11-22 00:38:40 +00:00
|
|
|
BreakpointResources(0, 0, 0),
|
|
|
|
|
{},
|
|
|
|
|
TargetMemoryType::FLASH
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::run(std::optional<TargetMemoryAddress> toAddress) {
|
|
|
|
|
auto controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
2023-11-23 12:56:26 +00:00
|
|
|
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
2023-11-22 00:38:40 +00:00
|
|
|
controlRegister.resumeRequest = true;
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
constexpr auto maxAttempts = 10;
|
2023-11-23 12:59:36 +00:00
|
|
|
auto statusRegister = this->readDebugModuleStatusRegister();
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
for (auto attempts = 1; !statusRegister.allResumeAcknowledge && attempts <= maxAttempts; ++attempts) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
2023-11-23 12:59:36 +00:00
|
|
|
statusRegister = this->readDebugModuleStatusRegister();
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controlRegister.resumeRequest = false;
|
2023-11-23 12:59:36 +00:00
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
if (!statusRegister.allResumeAcknowledge) {
|
|
|
|
|
throw Exceptions::Exception("Target took too long to acknowledge resume request");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::stop() {
|
|
|
|
|
auto controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
2023-11-23 12:56:26 +00:00
|
|
|
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
2023-11-22 00:38:40 +00:00
|
|
|
controlRegister.haltRequest = true;
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
constexpr auto maxAttempts = 10;
|
2023-11-23 12:59:36 +00:00
|
|
|
auto statusRegister = this->readDebugModuleStatusRegister();
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
for (auto attempts = 1; !statusRegister.allHalted && attempts <= maxAttempts; ++attempts) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
2023-11-23 12:59:36 +00:00
|
|
|
statusRegister = this->readDebugModuleStatusRegister();
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controlRegister.haltRequest = false;
|
2023-11-23 12:59:36 +00:00
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
2023-11-22 00:38:40 +00:00
|
|
|
|
|
|
|
|
if (!statusRegister.allHalted) {
|
|
|
|
|
throw Exceptions::Exception("Target took too long to halt selected harts");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::step() {
|
2023-11-23 16:34:35 +00:00
|
|
|
auto debugControlStatusRegister = this->readDebugControlStatusRegister();
|
|
|
|
|
debugControlStatusRegister.step = true;
|
|
|
|
|
|
|
|
|
|
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
|
|
|
|
|
|
|
|
|
auto controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
|
|
|
|
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
|
|
|
|
controlRegister.resumeRequest = true;
|
2023-11-22 00:38:40 +00:00
|
|
|
|
2023-11-23 16:34:35 +00:00
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
|
|
|
|
|
controlRegister.resumeRequest = false;
|
|
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
|
|
|
|
|
debugControlStatusRegister.step = false;
|
|
|
|
|
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::reset() {
|
2023-11-25 21:02:15 +00:00
|
|
|
auto controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
|
|
|
|
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
|
|
|
|
controlRegister.setResetHaltRequest = true;
|
|
|
|
|
controlRegister.haltRequest = true;
|
|
|
|
|
controlRegister.ndmReset = true;
|
|
|
|
|
|
|
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
|
|
|
|
|
controlRegister.ndmReset = false;
|
|
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
|
|
|
|
|
constexpr auto maxAttempts = 10;
|
|
|
|
|
auto statusRegister = this->readDebugModuleStatusRegister();
|
2023-11-22 00:38:40 +00:00
|
|
|
|
2023-11-25 21:02:15 +00:00
|
|
|
for (auto attempts = 1; !statusRegister.allHaveReset && attempts <= maxAttempts; ++attempts) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
|
|
|
|
statusRegister = this->readDebugModuleStatusRegister();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
|
|
|
|
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
|
|
|
|
controlRegister.clearResetHaltRequest = true;
|
|
|
|
|
controlRegister.acknowledgeHaveReset = true;
|
|
|
|
|
|
|
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
|
|
|
|
|
if (!statusRegister.allHaveReset) {
|
|
|
|
|
throw Exceptions::Exception("Target took too long to reset");
|
|
|
|
|
}
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::setSoftwareBreakpoint(TargetMemoryAddress address) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::removeSoftwareBreakpoint(TargetMemoryAddress address) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::setHardwareBreakpoint(TargetMemoryAddress address) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::removeHardwareBreakpoint(TargetMemoryAddress address) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::clearAllBreakpoints() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 23:18:50 +00:00
|
|
|
TargetRegisters RiscV::readRegisters(const TargetRegisterDescriptorIds& descriptorIds) {
|
2023-11-25 07:45:31 +00:00
|
|
|
auto output = TargetRegisters();
|
|
|
|
|
|
|
|
|
|
for (const auto& descriptorId : descriptorIds) {
|
|
|
|
|
const auto registerValue = this->readRegister(this->registerDescriptorsById.at(descriptorId).number);
|
|
|
|
|
output.emplace_back(
|
|
|
|
|
descriptorId,
|
|
|
|
|
TargetMemoryBuffer({
|
|
|
|
|
static_cast<unsigned char>(registerValue >> 24),
|
|
|
|
|
static_cast<unsigned char>(registerValue >> 16),
|
|
|
|
|
static_cast<unsigned char>(registerValue >> 8),
|
|
|
|
|
static_cast<unsigned char>(registerValue),
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-11-22 00:38:40 +00:00
|
|
|
|
2023-11-25 07:45:31 +00:00
|
|
|
return output;
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-25 07:45:31 +00:00
|
|
|
void RiscV::writeRegisters(const TargetRegisters& registers) {
|
|
|
|
|
for (const auto& targetRegister : registers) {
|
|
|
|
|
if ((targetRegister.value.size() * 8) > std::numeric_limits<std::uintmax_t>::digits) {
|
|
|
|
|
throw Exceptions::Exception("Register value bit width exceeds that of std::uintmax_t");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto registerValue = std::uintmax_t{0};
|
|
|
|
|
|
|
|
|
|
for (const auto& registerByte : targetRegister.value) {
|
|
|
|
|
registerValue = (registerValue << 8) | registerByte;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->writeRegister(
|
|
|
|
|
this->registerDescriptorsById.at(targetRegister.descriptorId).number,
|
|
|
|
|
static_cast<RegisterValue>(registerValue) // TODO: Support larger register values
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TargetMemoryBuffer RiscV::readMemory(
|
|
|
|
|
TargetMemoryType memoryType,
|
|
|
|
|
TargetMemoryAddress startAddress,
|
|
|
|
|
TargetMemorySize bytes,
|
2023-11-25 23:18:50 +00:00
|
|
|
const std::set<TargetMemoryAddressRange>& excludedAddressRanges
|
2023-11-22 00:38:40 +00:00
|
|
|
) {
|
2023-11-25 19:09:14 +00:00
|
|
|
// TODO: excluded addresses
|
|
|
|
|
|
|
|
|
|
const auto pageSize = 4;
|
|
|
|
|
if ((startAddress % pageSize) != 0 || (bytes % pageSize) != 0) {
|
|
|
|
|
// Alignment required
|
|
|
|
|
const auto alignedStartAddress = static_cast<TargetMemoryAddress>(
|
|
|
|
|
std::floor(static_cast<float>(startAddress) / static_cast<float>(pageSize)) * pageSize
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const auto alignedBytes = static_cast<TargetMemorySize>(
|
|
|
|
|
std::ceil(
|
|
|
|
|
static_cast<float>(bytes + (startAddress - alignedStartAddress))
|
|
|
|
|
/ static_cast<float>(pageSize)
|
|
|
|
|
) * pageSize
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto memoryBuffer = this->readMemory(
|
|
|
|
|
memoryType,
|
|
|
|
|
alignedStartAddress,
|
|
|
|
|
alignedBytes,
|
|
|
|
|
excludedAddressRanges
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const auto offset = memoryBuffer.begin() + (startAddress - alignedStartAddress);
|
|
|
|
|
|
|
|
|
|
auto output = TargetMemoryBuffer();
|
|
|
|
|
output.reserve(bytes);
|
|
|
|
|
std::move(offset, offset + bytes, std::back_inserter(output));
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using DebugModule::Registers::MemoryAccessControlField;
|
|
|
|
|
|
|
|
|
|
auto output = TargetMemoryBuffer();
|
|
|
|
|
output.reserve(bytes);
|
|
|
|
|
|
2023-11-25 19:35:54 +00:00
|
|
|
/*
|
|
|
|
|
* We only need to set the address once. No need to update it as we use the post-increment function to
|
|
|
|
|
* increment the address. See MemoryAccessControlField::postIncrement
|
|
|
|
|
*/
|
|
|
|
|
this->riscVDebugInterface->writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress);
|
|
|
|
|
|
2023-11-25 19:39:34 +00:00
|
|
|
auto command = AbstractCommandRegister();
|
|
|
|
|
command.commandType = AbstractCommandRegister::CommandType::MEMORY_ACCESS;
|
|
|
|
|
command.control = MemoryAccessControlField(
|
|
|
|
|
false,
|
|
|
|
|
true,
|
|
|
|
|
MemoryAccessControlField::MemorySize::SIZE_32,
|
|
|
|
|
false
|
|
|
|
|
).value();
|
2023-11-25 19:09:14 +00:00
|
|
|
|
2023-11-25 19:39:34 +00:00
|
|
|
for (auto address = startAddress; address <= (startAddress + bytes - 1); address += 4) {
|
2023-11-25 19:09:14 +00:00
|
|
|
this->executeAbstractCommand(command);
|
|
|
|
|
|
|
|
|
|
const auto data = this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
|
|
|
|
output.emplace_back(static_cast<unsigned char>(data >> 24));
|
|
|
|
|
output.emplace_back(static_cast<unsigned char>(data >> 16));
|
|
|
|
|
output.emplace_back(static_cast<unsigned char>(data >> 8));
|
|
|
|
|
output.emplace_back(static_cast<unsigned char>(data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output;
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::writeMemory(
|
|
|
|
|
TargetMemoryType memoryType,
|
|
|
|
|
TargetMemoryAddress startAddress,
|
|
|
|
|
const TargetMemoryBuffer& buffer
|
|
|
|
|
) {
|
2023-11-26 15:58:18 +00:00
|
|
|
using DebugModule::Registers::MemoryAccessControlField;
|
|
|
|
|
|
|
|
|
|
const auto pageSize = 4;
|
|
|
|
|
const auto bytes = static_cast<TargetMemorySize>(buffer.size());
|
|
|
|
|
if ((startAddress % pageSize) != 0 || (bytes % pageSize) != 0) {
|
|
|
|
|
/*
|
|
|
|
|
* Alignment required
|
|
|
|
|
*
|
|
|
|
|
* To align the write operation, we read the front and back offset bytes and use them to construct an
|
|
|
|
|
* aligned buffer.
|
|
|
|
|
*/
|
|
|
|
|
const auto alignedStartAddress = this->alignMemoryAddress(startAddress, pageSize);
|
|
|
|
|
const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), pageSize);
|
|
|
|
|
|
|
|
|
|
auto alignedBuffer = TargetMemoryBuffer();
|
|
|
|
|
alignedBuffer.reserve(alignedBytes);
|
|
|
|
|
|
|
|
|
|
if (alignedStartAddress < startAddress) {
|
|
|
|
|
// Read the offset bytes required to align the start address
|
|
|
|
|
alignedBuffer = this->readMemory(
|
|
|
|
|
memoryType,
|
|
|
|
|
alignedStartAddress,
|
|
|
|
|
(startAddress - alignedStartAddress)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
alignedBuffer.insert(alignedBuffer.end(), buffer.begin(), buffer.end());
|
|
|
|
|
assert(alignedBytes > bytes);
|
2023-11-22 00:38:40 +00:00
|
|
|
|
2023-11-26 15:58:18 +00:00
|
|
|
// Read the offset bytes required to align the buffer size
|
|
|
|
|
const auto dataBack = this->readMemory(
|
|
|
|
|
memoryType,
|
|
|
|
|
startAddress + bytes,
|
|
|
|
|
alignedBytes - bytes - (startAddress - alignedStartAddress)
|
|
|
|
|
);
|
|
|
|
|
alignedBuffer.insert(alignedBuffer.end(), dataBack.begin(), dataBack.end());
|
|
|
|
|
|
|
|
|
|
return this->writeMemory(memoryType, alignedStartAddress, alignedBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->riscVDebugInterface->writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress);
|
|
|
|
|
|
|
|
|
|
auto command = AbstractCommandRegister();
|
|
|
|
|
command.commandType = AbstractCommandRegister::CommandType::MEMORY_ACCESS;
|
|
|
|
|
command.control = MemoryAccessControlField(
|
|
|
|
|
true,
|
|
|
|
|
true,
|
|
|
|
|
MemoryAccessControlField::MemorySize::SIZE_32,
|
|
|
|
|
false
|
|
|
|
|
).value();
|
|
|
|
|
|
|
|
|
|
for (TargetMemoryAddress offset = 0; offset < buffer.size(); offset += 4) {
|
|
|
|
|
this->riscVDebugInterface->writeDebugModuleRegister(
|
|
|
|
|
RegisterAddress::ABSTRACT_DATA_0,
|
|
|
|
|
static_cast<RegisterValue>(
|
|
|
|
|
(buffer[offset] << 24)
|
|
|
|
|
| (buffer[offset + 1] << 16)
|
|
|
|
|
| (buffer[offset + 2] << 8)
|
|
|
|
|
| (buffer[offset + 3])
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this->executeAbstractCommand(command);
|
|
|
|
|
}
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::eraseMemory(TargetMemoryType memoryType) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TargetState RiscV::getState() {
|
2023-11-23 12:59:36 +00:00
|
|
|
return this->readDebugModuleStatusRegister().anyRunning ? TargetState::RUNNING : TargetState::STOPPED;
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TargetMemoryAddress RiscV::getProgramCounter() {
|
2023-11-23 23:31:13 +00:00
|
|
|
return this->readRegister(Registers::RegisterNumber::DEBUG_PROGRAM_COUNTER_REGISTER);
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::setProgramCounter(TargetMemoryAddress programCounter) {
|
2023-11-23 19:43:37 +00:00
|
|
|
// TODO: test this
|
2023-11-23 23:31:13 +00:00
|
|
|
this->writeRegister(Registers::RegisterNumber::DEBUG_PROGRAM_COUNTER_REGISTER, programCounter);
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TargetStackPointer RiscV::getStackPointer() {
|
2023-11-24 15:19:07 +00:00
|
|
|
return this->readRegister(Registers::RegisterNumber::STACK_POINTER_X2);
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::map<int, TargetPinState> RiscV::getPinStates(int variantId) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::setPinState(
|
|
|
|
|
const TargetPinDescriptor& pinDescriptor,
|
|
|
|
|
const TargetPinState& state
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::enableProgrammingMode() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::disableProgrammingMode() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool RiscV::programmingModeEnabled() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-24 15:19:52 +00:00
|
|
|
void RiscV::loadRegisterDescriptors() {
|
|
|
|
|
for (std::uint8_t i = 0; i <= 31; i++) {
|
|
|
|
|
auto generalPurposeRegisterDescriptor = RiscVRegisterDescriptor(
|
|
|
|
|
TargetRegisterType::GENERAL_PURPOSE_REGISTER,
|
|
|
|
|
static_cast<RegisterNumber>(Registers::RegisterNumberBase::GPR) + i,
|
|
|
|
|
4,
|
|
|
|
|
TargetMemoryType::OTHER,
|
|
|
|
|
"x" + std::to_string(i),
|
|
|
|
|
"CPU General Purpose",
|
|
|
|
|
std::nullopt,
|
|
|
|
|
TargetRegisterAccess(true, true)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this->registerDescriptorsById.emplace(
|
|
|
|
|
generalPurposeRegisterDescriptor.id,
|
|
|
|
|
std::move(generalPurposeRegisterDescriptor)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
std::set<DebugModule::HartIndex> RiscV::discoverHartIndices() {
|
|
|
|
|
auto hartIndices = std::set<DebugModule::HartIndex>();
|
|
|
|
|
|
2023-11-23 12:56:26 +00:00
|
|
|
/*
|
|
|
|
|
* We can obtain the maximum hart index by setting all of the hartsel bits in the control register and then
|
|
|
|
|
* reading the value back.
|
|
|
|
|
*/
|
|
|
|
|
auto controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
|
|
|
|
controlRegister.selectedHartIndex = 0xFFFFF;
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
controlRegister = this->readDebugModuleControlRegister();
|
2023-11-23 12:56:26 +00:00
|
|
|
|
|
|
|
|
for (DebugModule::HartIndex hartIndex = 0; hartIndex <= controlRegister.selectedHartIndex; ++hartIndex) {
|
|
|
|
|
/*
|
|
|
|
|
* We can't just assume that everything between 0 and the maximum hart index are valid hart indices. We
|
|
|
|
|
* have to test each index until we find one that is non-existent.
|
|
|
|
|
*/
|
|
|
|
|
controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
|
|
|
|
controlRegister.selectedHartIndex = hartIndex;
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
2023-11-23 12:56:26 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* It's worth noting that some RISC-V targets **do not** set the non-existent flags. I'm not sure why.
|
2023-11-23 12:59:36 +00:00
|
|
|
* Has hartsel been hardwired to 0 because they only support a single hart, preventing the selection
|
2023-11-23 12:56:26 +00:00
|
|
|
* of non-existent harts?
|
|
|
|
|
*
|
|
|
|
|
* Relying on the maximum hart index seems to be all we can do in this case.
|
|
|
|
|
*/
|
2023-11-23 12:59:36 +00:00
|
|
|
if (this->readDebugModuleStatusRegister().anyNonExistent) {
|
2023-11-23 12:56:26 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
hartIndices.insert(hartIndex);
|
2023-11-23 12:56:26 +00:00
|
|
|
}
|
2023-11-23 12:59:36 +00:00
|
|
|
|
|
|
|
|
return hartIndices;
|
2023-11-23 12:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
ControlRegister RiscV::readDebugModuleControlRegister() {
|
2023-11-22 00:38:40 +00:00
|
|
|
return ControlRegister(
|
2023-11-23 17:46:32 +00:00
|
|
|
this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::CONTROL_REGISTER)
|
2023-11-22 00:38:40 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:59:36 +00:00
|
|
|
StatusRegister RiscV::readDebugModuleStatusRegister() {
|
2023-11-23 17:46:32 +00:00
|
|
|
return StatusRegister(
|
|
|
|
|
this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::STATUS_REGISTER)
|
|
|
|
|
);
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-23 15:21:46 +00:00
|
|
|
AbstractControlStatusRegister RiscV::readDebugModuleAbstractControlStatusRegister() {
|
|
|
|
|
return AbstractControlStatusRegister(
|
2023-11-23 17:46:32 +00:00
|
|
|
this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::ABSTRACT_CONTROL_STATUS_REGISTER)
|
2023-11-23 15:21:46 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 16:34:35 +00:00
|
|
|
DebugControlStatusRegister RiscV::readDebugControlStatusRegister() {
|
2023-11-23 23:31:13 +00:00
|
|
|
return DebugControlStatusRegister(this->readRegister(Registers::RegisterNumber::DEBUG_CONTROL_STATUS_REGISTER));
|
2023-11-23 16:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-25 19:06:23 +00:00
|
|
|
void RiscV::enableDebugModule() {
|
|
|
|
|
auto controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = true;
|
|
|
|
|
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
|
|
|
|
|
|
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
|
|
|
|
|
constexpr auto maxAttempts = 10;
|
|
|
|
|
controlRegister = this->readDebugModuleControlRegister();
|
|
|
|
|
|
|
|
|
|
for (auto attempts = 1; !controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
|
|
|
|
controlRegister = this->readDebugModuleControlRegister();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!controlRegister.debugModuleActive) {
|
|
|
|
|
throw Exceptions::Exception("Took too long to enable debug module");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::disableDebugModule() {
|
|
|
|
|
auto controlRegister = ControlRegister();
|
|
|
|
|
controlRegister.debugModuleActive = false;
|
|
|
|
|
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
|
|
|
|
|
|
|
|
|
this->writeDebugModuleControlRegister(controlRegister);
|
|
|
|
|
|
|
|
|
|
constexpr auto maxAttempts = 10;
|
|
|
|
|
controlRegister = this->readDebugModuleControlRegister();
|
|
|
|
|
|
|
|
|
|
for (auto attempts = 1; controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
|
|
|
|
controlRegister = this->readDebugModuleControlRegister();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (controlRegister.debugModuleActive) {
|
|
|
|
|
throw Exceptions::Exception("Took too long to disable debug module");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 16:32:53 +00:00
|
|
|
RegisterValue RiscV::readRegister(RegisterNumber number) {
|
|
|
|
|
using DebugModule::Registers::RegisterAccessControlField;
|
|
|
|
|
|
|
|
|
|
auto command = AbstractCommandRegister();
|
|
|
|
|
command.commandType = AbstractCommandRegister::CommandType::REGISTER_ACCESS;
|
|
|
|
|
command.control = RegisterAccessControlField(
|
|
|
|
|
number,
|
|
|
|
|
false,
|
|
|
|
|
true,
|
|
|
|
|
false,
|
|
|
|
|
false,
|
|
|
|
|
RegisterAccessControlField::RegisterSize::SIZE_32
|
|
|
|
|
).value();
|
|
|
|
|
|
|
|
|
|
this->executeAbstractCommand(command);
|
|
|
|
|
|
2023-11-23 17:46:32 +00:00
|
|
|
return this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
2023-11-23 16:32:53 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-23 23:31:13 +00:00
|
|
|
RegisterValue RiscV::readRegister(Registers::RegisterNumber number) {
|
|
|
|
|
return this->readRegister(static_cast<RegisterNumber>(number));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RiscV::writeRegister(RegisterNumber number, RegisterValue value) {
|
2023-11-23 16:32:53 +00:00
|
|
|
using DebugModule::Registers::RegisterAccessControlField;
|
|
|
|
|
|
|
|
|
|
auto command = AbstractCommandRegister();
|
|
|
|
|
command.commandType = AbstractCommandRegister::CommandType::REGISTER_ACCESS;
|
|
|
|
|
command.control = RegisterAccessControlField(
|
|
|
|
|
number,
|
|
|
|
|
true,
|
|
|
|
|
true,
|
|
|
|
|
false,
|
|
|
|
|
false,
|
|
|
|
|
RegisterAccessControlField::RegisterSize::SIZE_32
|
|
|
|
|
).value();
|
|
|
|
|
|
2023-11-23 17:46:32 +00:00
|
|
|
this->riscVDebugInterface->writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0, value);
|
2023-11-23 16:32:53 +00:00
|
|
|
this->executeAbstractCommand(command);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 23:31:13 +00:00
|
|
|
void RiscV::writeRegister(Registers::RegisterNumber number, RegisterValue value) {
|
|
|
|
|
this->writeRegister(
|
|
|
|
|
static_cast<RegisterNumber>(number),
|
|
|
|
|
value
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 16:35:09 +00:00
|
|
|
void RiscV::writeDebugModuleControlRegister(const DebugModule::Registers::ControlRegister& controlRegister) {
|
2023-11-22 00:38:40 +00:00
|
|
|
this->riscVDebugInterface->writeDebugModuleRegister(
|
2023-11-23 17:46:32 +00:00
|
|
|
RegisterAddress::CONTROL_REGISTER,
|
2023-11-22 00:38:40 +00:00
|
|
|
controlRegister.value()
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-11-23 15:21:46 +00:00
|
|
|
|
2023-11-23 16:34:35 +00:00
|
|
|
void RiscV::writeDebugControlStatusRegister(const DebugControlStatusRegister& controlRegister) {
|
2023-11-23 23:31:13 +00:00
|
|
|
this->writeRegister(Registers::RegisterNumber::DEBUG_CONTROL_STATUS_REGISTER, controlRegister.value());
|
2023-11-23 16:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-23 15:21:46 +00:00
|
|
|
void RiscV::executeAbstractCommand(
|
|
|
|
|
const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister
|
|
|
|
|
) {
|
|
|
|
|
this->riscVDebugInterface->writeDebugModuleRegister(
|
2023-11-23 17:46:32 +00:00
|
|
|
RegisterAddress::ABSTRACT_COMMAND_REGISTER,
|
2023-11-23 15:21:46 +00:00
|
|
|
abstractCommandRegister.value()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
|
|
|
|
if (abstractStatusRegister.commandError != AbstractControlStatusRegister::CommandError::NONE) {
|
|
|
|
|
throw Exceptions::Exception(
|
|
|
|
|
"Failed to execute abstract command - error: "
|
|
|
|
|
+ Services::StringService::toHex(abstractStatusRegister.commandError)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr auto maxAttempts = 10;
|
|
|
|
|
for (auto attempts = 1; abstractStatusRegister.busy && attempts <= maxAttempts; ++attempts) {
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
|
|
|
|
abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (abstractStatusRegister.busy) {
|
|
|
|
|
throw Exceptions::Exception("Abstract command took too long to execute");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-26 15:58:18 +00:00
|
|
|
|
|
|
|
|
TargetMemoryAddress RiscV::alignMemoryAddress(TargetMemoryAddress address, TargetMemoryAddress alignTo) {
|
|
|
|
|
return static_cast<TargetMemoryAddress>(
|
|
|
|
|
std::floor(static_cast<float>(address) / static_cast<float>(alignTo))
|
|
|
|
|
) * alignTo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TargetMemorySize RiscV::alignMemorySize(TargetMemorySize size, TargetMemorySize alignTo) {
|
|
|
|
|
return static_cast<TargetMemorySize>(
|
|
|
|
|
std::ceil(static_cast<float>(size) / static_cast<float>(alignTo))
|
|
|
|
|
) * alignTo;
|
|
|
|
|
}
|
2023-11-22 00:38:40 +00:00
|
|
|
}
|