From cfb0ee6eee42c8c2251ee1a4b279df2eb5b19fe7 Mon Sep 17 00:00:00 2001 From: Nav Date: Tue, 30 Aug 2022 02:51:10 +0100 Subject: [PATCH] New GenerateSvd GDB `monitor` command --- resources/gdbHelpMonitorInfo.txt | 4 + src/DebugServer/CMakeLists.txt | 1 + .../Gdb/CommandPackets/GenerateSvd.cpp | 227 ++++++++++++++++++ .../Gdb/CommandPackets/GenerateSvd.hpp | 35 +++ src/DebugServer/Gdb/GdbRspDebugServer.cpp | 4 + src/Targets/Microchip/AVR/AVR8/Avr8.cpp | 1 + src/Targets/TargetDescriptor.hpp | 1 + 7 files changed, 273 insertions(+) create mode 100644 src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp create mode 100644 src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp diff --git a/resources/gdbHelpMonitorInfo.txt b/resources/gdbHelpMonitorInfo.txt index 20c327bd..df2747fe 100644 --- a/resources/gdbHelpMonitorInfo.txt +++ b/resources/gdbHelpMonitorInfo.txt @@ -4,6 +4,10 @@ Supported Bloom commands: version Outputs Bloom's version information. 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. reset Resets the target and holds it in a stopped state. diff --git a/src/DebugServer/CMakeLists.txt b/src/DebugServer/CMakeLists.txt index 1414c1b7..8f0310cb 100755 --- a/src/DebugServer/CMakeLists.txt +++ b/src/DebugServer/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.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 # AVR GDB RSP Server diff --git a/src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp b/src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp new file mode 100644 index 00000000..51b1158b --- /dev/null +++ b/src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp @@ -0,0 +1,227 @@ +#include "GenerateSvd.hpp" + +#include +#include + +#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(); + + 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; + } +} diff --git a/src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp b/src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp new file mode 100644 index 00000000..6bbd26c9 --- /dev/null +++ b/src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#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 + ); + }; +} diff --git a/src/DebugServer/Gdb/GdbRspDebugServer.cpp b/src/DebugServer/Gdb/GdbRspDebugServer.cpp index 38f50987..c9b20e15 100644 --- a/src/DebugServer/Gdb/GdbRspDebugServer.cpp +++ b/src/DebugServer/Gdb/GdbRspDebugServer.cpp @@ -31,6 +31,7 @@ #include "CommandPackets/BloomVersion.hpp" #include "CommandPackets/BloomVersionMachine.hpp" #include "CommandPackets/TargetInfoMachine.hpp" +#include "CommandPackets/GenerateSvd.hpp" // Response packets #include "ResponsePackets/TargetStopped.hpp" @@ -318,6 +319,9 @@ namespace Bloom::DebugServer::Gdb if (monitorCommand->command == "target-info machine") { return std::make_unique(std::move(*(monitorCommand.release()))); } + + if (monitorCommand->command.find("svd") == 0) { + return std::make_unique(std::move(*(monitorCommand.release()))); } return monitorCommand; diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp index cd877f9f..6e012e02 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp @@ -257,6 +257,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit auto descriptor = TargetDescriptor(); descriptor.id = this->getHumanReadableId(); descriptor.name = this->getName(); + descriptor.vendorName = std::string("Microchip"); descriptor.programMemoryType = Targets::TargetMemoryType::FLASH; descriptor.registerDescriptorsByType = this->targetRegisterDescriptorsByType; descriptor.memoryDescriptorsByType = this->targetMemoryDescriptorsByType; diff --git a/src/Targets/TargetDescriptor.hpp b/src/Targets/TargetDescriptor.hpp index ce6227f2..543887ef 100644 --- a/src/Targets/TargetDescriptor.hpp +++ b/src/Targets/TargetDescriptor.hpp @@ -16,6 +16,7 @@ namespace Bloom::Targets { std::string name; std::string id; + std::string vendorName; std::map memoryDescriptorsByType; std::map registerDescriptorsByType; std::vector variants;