New GenerateSvd GDB monitor command

This commit is contained in:
Nav
2022-08-30 02:51:10 +01:00
parent 590c6ecb33
commit cfb0ee6eee
7 changed files with 273 additions and 0 deletions

View File

@@ -4,6 +4,10 @@ Supported Bloom commands:
version Outputs Bloom's version information. version Outputs Bloom's version information.
version machine Outputs Bloom's version information in JSON format. version machine Outputs Bloom's version information in JSON format.
svd Generates the System View Description (SVD) XML for the current target and saves it into a
file located in the current project directory.
svd --out Generates the System View Description (SVD) XML for the current target and sends it to GDB, as
command output.
target-info machine Outputs information on the connected target, in JSON format. target-info machine Outputs information on the connected target, in JSON format.
reset Resets the target and holds it in a stopped state. reset Resets the target and holds it in a stopped state.

View File

@@ -23,6 +23,7 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/TargetInfoMachine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/TargetInfoMachine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/GenerateSvd.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp
# AVR GDB RSP Server # AVR GDB RSP Server

View File

@@ -0,0 +1,227 @@
#include "GenerateSvd.hpp"
#include <QString>
#include <QFile>
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Application.hpp"
#include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp"
#include "src/Logger/Logger.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using TargetController::TargetControllerConsole;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
GenerateSvd::GenerateSvd(Monitor&& monitorPacket)
: Monitor(std::move(monitorPacket))
, sendOutput(this->command.find("--out") != std::string::npos)
{}
void GenerateSvd::handle(DebugSession& debugSession, TargetControllerConsole&) {
Logger::debug("Handling GenerateSvd packet");
try {
Logger::info("Generating SVD XML for current target");
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
const auto svdXml = this->generateSvd(
targetDescriptor,
debugSession.gdbTargetDescriptor.getMemoryOffset(Targets::TargetMemoryType::RAM)
);
if (this->sendOutput) {
debugSession.connection.writePacket(ResponsePacket(Packet::toHex(svdXml.toString().toStdString())));
return;
}
const auto svdOutputFilePath = Paths::projectDirPath() + "/" + targetDescriptor.name + ".svd";
auto outputFile = QFile(QString::fromStdString(svdOutputFilePath));
if (!outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
throw Exception(
"Failed to open/create SVD output file (" + svdOutputFilePath + "). Check file permissions."
);
}
outputFile.write(svdXml.toByteArray());
outputFile.close();
debugSession.connection.writePacket(ResponsePacket(Packet::toHex(
"SVD output saved to " + svdOutputFilePath + "\n"
)));
Logger::info("SVD output saved to " + svdOutputFilePath);
} catch (const Exception& exception) {
Logger::error(exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
QDomDocument GenerateSvd::generateSvd(
const Targets::TargetDescriptor& targetDescriptor,
std::uint32_t baseAddressOffset
) {
auto document = QDomDocument();
const auto createElement = [&document] (const QString& tagName, const QString& value) {
auto element = document.createElement(tagName);
auto textNode = document.createTextNode(value);
element.appendChild(textNode);
return element;
};
document.appendChild(document.createComment(
" This SVD was generated by Bloom (https://bloom.oscillate.io/). "
"Please report any issues via https://bloom.oscillate.io/report-issue "
));
if (baseAddressOffset != 0) {
document.appendChild(document.createComment(
" Base addresses in this SVD have been offset by 0x" + QString::number(baseAddressOffset, 16)
+ ". This offset is required for access via avr-gdb. "
));
}
auto deviceElement = document.createElement("device");
deviceElement.setAttribute("schemaVersion", "1.3");
deviceElement.setAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance");
deviceElement.setAttribute(
"xs:noNamespaceSchemaLocation",
QString::fromStdString(Paths::homeDomainName() + "/assets/svd-schema.xsd")
);
deviceElement.appendChild(createElement("vendor", QString::fromStdString(targetDescriptor.vendorName)));
deviceElement.appendChild(createElement("name", QString::fromStdString(targetDescriptor.name)));
deviceElement.appendChild(document.createComment(
" The version number below is that of the Bloom binary which generated this SVD. "
));
deviceElement.appendChild(createElement("version", QString::fromStdString(Application::VERSION.toString())));
deviceElement.appendChild(
createElement(
"description",
QString::fromStdString(targetDescriptor.name) + " from "
+ QString::fromStdString(targetDescriptor.vendorName)
)
);
/*
* TODO: These values should be part of the TargetDescriptor, but given that Bloom only supports 8-bit AVRs,
* it really doesn't matter ATM. Will fix it later (lol no I won't).
*/
deviceElement.appendChild(createElement("addressUnitBits", "8"));
deviceElement.appendChild(createElement("width", "8"));
deviceElement.appendChild(createElement("size", "8"));
deviceElement.appendChild(createElement("access", "read-only"));
struct Peripheral {
QString name;
std::uint32_t baseAddress;
Targets::TargetRegisterDescriptors registerDescriptors;
};
auto peripheralsByName = std::map<std::string, Peripheral>();
for (const auto& [registerType, registerDescriptors] : targetDescriptor.registerDescriptorsByType) {
if (registerDescriptors.empty()) {
continue;
}
for (const auto& registerDescriptor : registerDescriptors) {
if (
!registerDescriptor.startAddress.has_value()
|| !registerDescriptor.name.has_value()
|| registerDescriptor.name->empty()
|| !registerDescriptor.groupName.has_value()
|| (
registerDescriptor.type != Targets::TargetRegisterType::GENERAL_PURPOSE_REGISTER
&& registerDescriptor.type != Targets::TargetRegisterType::OTHER
&& registerDescriptor.type != Targets::TargetRegisterType::PORT_REGISTER
)
) {
continue;
}
auto peripheralIt = peripheralsByName.find(*registerDescriptor.groupName);
if (peripheralIt == peripheralsByName.end()) {
auto peripheral = Peripheral{
.name = QString::fromStdString(
*registerDescriptor.groupName
).replace(QChar(' '), QChar('_')).toUpper(),
.baseAddress = baseAddressOffset
};
peripheralIt = peripheralsByName.insert(std::pair(*registerDescriptor.groupName, peripheral)).first;
}
peripheralIt->second.registerDescriptors.insert(registerDescriptor);
}
}
auto peripheralsElement = document.createElement("peripherals");
for (const auto& [peripheralName, peripheral] : peripheralsByName) {
auto peripheralElement = document.createElement("peripheral");
peripheralElement.appendChild(createElement("name", peripheral.name));
peripheralElement.appendChild(createElement("baseAddress", "0x" + QString::number(peripheral.baseAddress, 16)));
auto registersElement = document.createElement("registers");
for (const auto& registerDescriptor : peripheral.registerDescriptors) {
auto registerElement = document.createElement("register");
registerElement.appendChild(
createElement(
"name",
QString::fromStdString(*registerDescriptor.name).replace(QChar(' '), QChar('_')).toUpper()
)
);
if (registerDescriptor.description.has_value()) {
registerElement.appendChild(
createElement("description", QString::fromStdString(*registerDescriptor.description))
);
}
registerElement.appendChild(
createElement("addressOffset", "0x" + QString::number(*registerDescriptor.startAddress, 16))
);
registerElement.appendChild(
createElement("size", QString::number(registerDescriptor.size * 8))
);
registerElement.appendChild(
createElement("access", registerDescriptor.writable ? "read-write" : "read-only")
);
registersElement.appendChild(registerElement);
}
peripheralElement.appendChild(registersElement);
peripheralsElement.appendChild(peripheralElement);
}
deviceElement.appendChild(peripheralsElement);
document.appendChild(deviceElement);
return document;
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <cstdint>
#include "Monitor.hpp"
#include "src/Targets/TargetDescriptor.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The GenerateSvd class implements a structure for the "monitor svd" GDB command.
*
* This command generates XML conforming to the CMSIS-SVD schema, for the connected target. Will output the XML to
* a file or send it to GDB.
*/
class GenerateSvd: public Monitor
{
public:
explicit GenerateSvd(Monitor&& monitorPacket);
void handle(
DebugSession& debugSession,
TargetController::TargetControllerConsole& targetControllerConsole
) override;
private:
bool sendOutput = false;
QDomDocument generateSvd(
const Targets::TargetDescriptor& targetDescriptor,
std::uint32_t baseAddressOffset = 0
);
};
}

View File

@@ -31,6 +31,7 @@
#include "CommandPackets/BloomVersion.hpp" #include "CommandPackets/BloomVersion.hpp"
#include "CommandPackets/BloomVersionMachine.hpp" #include "CommandPackets/BloomVersionMachine.hpp"
#include "CommandPackets/TargetInfoMachine.hpp" #include "CommandPackets/TargetInfoMachine.hpp"
#include "CommandPackets/GenerateSvd.hpp"
// Response packets // Response packets
#include "ResponsePackets/TargetStopped.hpp" #include "ResponsePackets/TargetStopped.hpp"
@@ -318,6 +319,9 @@ namespace Bloom::DebugServer::Gdb
if (monitorCommand->command == "target-info machine") { if (monitorCommand->command == "target-info machine") {
return std::make_unique<CommandPackets::TargetInfoMachine>(std::move(*(monitorCommand.release()))); return std::make_unique<CommandPackets::TargetInfoMachine>(std::move(*(monitorCommand.release())));
} }
if (monitorCommand->command.find("svd") == 0) {
return std::make_unique<CommandPackets::GenerateSvd>(std::move(*(monitorCommand.release())));
} }
return monitorCommand; return monitorCommand;

View File

@@ -257,6 +257,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
auto descriptor = TargetDescriptor(); auto descriptor = TargetDescriptor();
descriptor.id = this->getHumanReadableId(); descriptor.id = this->getHumanReadableId();
descriptor.name = this->getName(); descriptor.name = this->getName();
descriptor.vendorName = std::string("Microchip");
descriptor.programMemoryType = Targets::TargetMemoryType::FLASH; descriptor.programMemoryType = Targets::TargetMemoryType::FLASH;
descriptor.registerDescriptorsByType = this->targetRegisterDescriptorsByType; descriptor.registerDescriptorsByType = this->targetRegisterDescriptorsByType;
descriptor.memoryDescriptorsByType = this->targetMemoryDescriptorsByType; descriptor.memoryDescriptorsByType = this->targetMemoryDescriptorsByType;

View File

@@ -16,6 +16,7 @@ namespace Bloom::Targets
{ {
std::string name; std::string name;
std::string id; std::string id;
std::string vendorName;
std::map<TargetMemoryType, TargetMemoryDescriptor> memoryDescriptorsByType; std::map<TargetMemoryType, TargetMemoryDescriptor> memoryDescriptorsByType;
std::map<TargetRegisterType, TargetRegisterDescriptors> registerDescriptorsByType; std::map<TargetRegisterType, TargetRegisterDescriptors> registerDescriptorsByType;
std::vector<TargetVariant> variants; std::vector<TargetVariant> variants;