From 239c82bfb00bfe040e3730f42b583a7f6417f5d8 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Sun, 28 Jul 2019 23:32:33 +0300 Subject: [PATCH] Debugger: Add peripheral registers description file support This feature is useful for the bare-metal programming. It allows to view the peripheral registers of the debugged device using the GDB. An information about the peripheral registers for a concrete device contains in a special SVD file. A format of this file described e.g. here: * https://www.keil.com/pack/doc/CMSIS/SVD/html/svd_Format_pg.html This feature supported only for ARM devices, and an appropriate SVD files can be found in the Internet, also this files provides by KEIL or IAR EW IDE's. A use case in QtC is that the user should to choose desired SVD file and set its path to the bare-metal device configuration widget. After this, the user can enable the "Peripheral Registers" view, choose a desired register group and to see a peripheral register values. Currently the following basic features are implemented: * Choosing SVD file for a target bare-metal device. * Choosing any peripheral register group, which is available for this device. * Seeing the info about the each peripheral register and its fields. * Seeing the value for the each peripheral register and its fields. * Changing the value for the each peripheral register and its fields (if it is allowed by access for a concrete register or field). * Changing the format of the values (hexadecimal, decimal, octal, binary). Fixes: QTCREATORBUG-18729 Change-Id: I3c38ea50ccd2e128746458f9b918095b4c2d644a Reviewed-by: hjk --- .../baremetaldeviceconfigurationwidget.cpp | 21 + .../baremetaldeviceconfigurationwidget.h | 4 + src/plugins/debugger/debugger.pro | 58 +- src/plugins/debugger/debuggerengine.cpp | 41 + src/plugins/debugger/debuggerengine.h | 6 + src/plugins/debugger/gdb/gdbengine.cpp | 53 + src/plugins/debugger/gdb/gdbengine.h | 4 + .../debugger/peripheralregisterhandler.cpp | 956 ++++++++++++++++++ .../debugger/peripheralregisterhandler.h | 185 ++++ .../projectexplorer/devicesupport/idevice.cpp | 15 + .../projectexplorer/devicesupport/idevice.h | 3 + 11 files changed, 1318 insertions(+), 28 deletions(-) create mode 100644 src/plugins/debugger/peripheralregisterhandler.cpp create mode 100644 src/plugins/debugger/peripheralregisterhandler.h diff --git a/src/plugins/baremetal/baremetaldeviceconfigurationwidget.cpp b/src/plugins/baremetal/baremetaldeviceconfigurationwidget.cpp index a528bd4b8ea..e82e9f16c81 100644 --- a/src/plugins/baremetal/baremetaldeviceconfigurationwidget.cpp +++ b/src/plugins/baremetal/baremetaldeviceconfigurationwidget.cpp @@ -30,6 +30,7 @@ #include "gdbserverprovider.h" #include "gdbserverproviderchooser.h" +#include #include #include @@ -56,6 +57,19 @@ BareMetalDeviceConfigurationWidget::BareMetalDeviceConfigurationWidget( connect(m_gdbServerProviderChooser, &GdbServerProviderChooser::providerChanged, this, &BareMetalDeviceConfigurationWidget::gdbServerProviderChanged); + + m_peripheralDescriptionFileChooser = new Utils::PathChooser(this); + m_peripheralDescriptionFileChooser->setExpectedKind(Utils::PathChooser::File); + m_peripheralDescriptionFileChooser->setPromptDialogFilter( + tr("Peripheral description files (*.svd)")); + m_peripheralDescriptionFileChooser->setPromptDialogTitle( + tr("Select Peripheral Description File")); + m_peripheralDescriptionFileChooser->setPath(dev->peripheralDescriptionFilePath()); + formLayout->addRow(tr("Peripheral description file:"), + m_peripheralDescriptionFileChooser); + + connect(m_peripheralDescriptionFileChooser, &Utils::PathChooser::pathChanged, + this, &BareMetalDeviceConfigurationWidget::peripheralDescriptionFileChanged); } void BareMetalDeviceConfigurationWidget::gdbServerProviderChanged() @@ -65,6 +79,13 @@ void BareMetalDeviceConfigurationWidget::gdbServerProviderChanged() dev->setGdbServerProviderId(m_gdbServerProviderChooser->currentProviderId()); } +void BareMetalDeviceConfigurationWidget::peripheralDescriptionFileChanged() +{ + const auto dev = qSharedPointerCast(device()); + QTC_ASSERT(dev, return); + dev->setPeripheralDescriptionFilePath(m_peripheralDescriptionFileChooser->path()); +} + void BareMetalDeviceConfigurationWidget::updateDeviceFromUi() { gdbServerProviderChanged(); diff --git a/src/plugins/baremetal/baremetaldeviceconfigurationwidget.h b/src/plugins/baremetal/baremetaldeviceconfigurationwidget.h index ad8017f53f0..249fea691e1 100644 --- a/src/plugins/baremetal/baremetaldeviceconfigurationwidget.h +++ b/src/plugins/baremetal/baremetaldeviceconfigurationwidget.h @@ -28,6 +28,8 @@ #include +namespace Utils { class PathChooser; } + namespace BareMetal { namespace Internal { @@ -46,9 +48,11 @@ public: private: void gdbServerProviderChanged(); + void peripheralDescriptionFileChanged(); void updateDeviceFromUi() final; GdbServerProviderChooser *m_gdbServerProviderChooser = nullptr; + Utils::PathChooser *m_peripheralDescriptionFileChooser = nullptr; }; } // namespace Internal diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 0f91ac1a92d..52bfd05eade 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -17,29 +17,36 @@ HEADERS += \ commonoptionspage.h \ debugger_global.h \ debuggeractions.h \ - debuggercore.h \ debuggerconstants.h \ + debuggercore.h \ + debuggerdialogs.h \ + debuggerengine.h \ + debuggericons.h \ debuggerinternalconstants.h \ debuggeritem.h \ debuggeritemmanager.h \ - debuggerdialogs.h \ - debuggerengine.h \ + debuggerkitinformation.h \ debuggermainwindow.h \ debuggerplugin.h \ debuggerprotocol.h \ debuggerrunconfigurationaspect.h \ debuggerruncontrol.h \ - debuggerkitinformation.h \ + debuggersourcepathmappingwidget.h \ + debuggertooltipmanager.h \ disassembleragent.h \ disassemblerlines.h \ + enginemanager.h \ + imageviewer.h \ loadcoredialog.h \ + localsandexpressionswindow.h \ logwindow.h \ memoryagent.h \ moduleshandler.h \ outputcollector.h \ + peripheralregisterhandler.h \ procinterrupt.h \ registerhandler.h \ - enginemanager.h \ + simplifytype.h \ sourceagent.h \ sourcefileshandler.h \ sourceutils.h \ @@ -47,19 +54,13 @@ HEADERS += \ stackhandler.h \ stackwindow.h \ terminal.h \ - watchhandler.h \ - watchutils.h \ - watchwindow.h \ threaddata.h \ threadshandler.h \ - watchdelegatewidgets.h \ - debuggertooltipmanager.h \ - debuggersourcepathmappingwidget.h \ - localsandexpressionswindow.h \ - imageviewer.h \ - simplifytype.h \ unstartedappwatcherdialog.h \ - debuggericons.h + watchdelegatewidgets.h \ + watchhandler.h \ + watchutils.h \ + watchwindow.h SOURCES += \ breakhandler.cpp \ @@ -68,44 +69,45 @@ SOURCES += \ debuggeractions.cpp \ debuggerdialogs.cpp \ debuggerengine.cpp \ + debuggericons.cpp \ debuggeritem.cpp \ debuggeritemmanager.cpp \ + debuggerkitinformation.cpp \ debuggermainwindow.cpp \ debuggerplugin.cpp \ debuggerprotocol.cpp \ debuggerrunconfigurationaspect.cpp \ debuggerruncontrol.cpp \ - debuggerkitinformation.cpp \ + debuggersourcepathmappingwidget.cpp \ + debuggertooltipmanager.cpp \ disassembleragent.cpp \ disassemblerlines.cpp \ + enginemanager.cpp \ + imageviewer.cpp \ loadcoredialog.cpp \ + localsandexpressionswindow.cpp \ logwindow.cpp \ memoryagent.cpp \ moduleshandler.cpp \ outputcollector.cpp \ + peripheralregisterhandler.cpp \ procinterrupt.cpp \ registerhandler.cpp \ - enginemanager.cpp \ + simplifytype.cpp \ sourceagent.cpp \ sourcefileshandler.cpp \ sourceutils.cpp \ + stackframe.cpp \ stackhandler.cpp \ stackwindow.cpp \ - threadshandler.cpp \ terminal.cpp \ + threadshandler.cpp \ + unstartedappwatcherdialog.cpp \ watchdata.cpp \ + watchdelegatewidgets.cpp \ watchhandler.cpp \ watchutils.cpp \ - watchwindow.cpp \ - stackframe.cpp \ - watchdelegatewidgets.cpp \ - debuggertooltipmanager.cpp \ - debuggersourcepathmappingwidget.cpp \ - localsandexpressionswindow.cpp \ - imageviewer.cpp \ - simplifytype.cpp \ - unstartedappwatcherdialog.cpp \ - debuggericons.cpp + watchwindow.cpp RESOURCES += debugger.qrc diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index e661fd8ba96..624440c5faa 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -43,6 +43,7 @@ #include "memoryagent.h" #include "moduleshandler.h" #include "registerhandler.h" +#include "peripheralregisterhandler.h" #include "sourcefileshandler.h" #include "sourceutils.h" #include "stackhandler.h" @@ -274,6 +275,7 @@ public: m_breakHandler(engine), m_modulesHandler(engine), m_registerHandler(engine), + m_peripheralRegisterHandler(engine), m_sourceFilesHandler(engine), m_stackHandler(engine), m_threadsHandler(engine), @@ -343,6 +345,7 @@ public: delete m_watchersWindow; delete m_inspectorWindow; delete m_registerWindow; + delete m_peripheralRegisterWindow; delete m_modulesWindow; delete m_sourceFilesWindow; delete m_stackWindow; @@ -354,6 +357,7 @@ public: delete m_watchersView; delete m_inspectorView; delete m_registerView; + delete m_peripheralRegisterView; delete m_modulesView; delete m_sourceFilesView; delete m_stackView; @@ -469,6 +473,7 @@ public: BreakHandler m_breakHandler; ModulesHandler m_modulesHandler; RegisterHandler m_registerHandler; + PeripheralRegisterHandler m_peripheralRegisterHandler; SourceFilesHandler m_sourceFilesHandler; StackHandler m_stackHandler; ThreadsHandler m_threadsHandler; @@ -493,6 +498,7 @@ public: QPointer m_watchersView; QPointer m_inspectorView; QPointer m_registerView; + QPointer m_peripheralRegisterView; QPointer m_modulesView; QPointer m_sourceFilesView; QPointer m_stackView; @@ -503,6 +509,7 @@ public: QPointer m_watchersWindow; QPointer m_inspectorWindow; QPointer m_registerWindow; + QPointer m_peripheralRegisterWindow; QPointer m_modulesWindow; QPointer m_sourceFilesWindow; QPointer m_stackWindow; @@ -643,6 +650,17 @@ void DebuggerEnginePrivate::setupViews() m_registerWindow->setObjectName("Debugger.Dock.Register." + engineId); m_registerWindow->setWindowTitle(tr("Reg&isters")); + m_peripheralRegisterView = new BaseTreeView; + m_peripheralRegisterView->setModel(m_peripheralRegisterHandler.model()); + m_peripheralRegisterView->setRootIsDecorated(true); + m_peripheralRegisterView->setSettings(settings, "Debugger.PeripheralRegisterView"); + connect(m_peripheralRegisterView, &BaseTreeView::aboutToShow, + m_engine, &DebuggerEngine::reloadPeripheralRegisters, + Qt::QueuedConnection); + m_peripheralRegisterWindow = addSearch(m_peripheralRegisterView); + m_peripheralRegisterWindow->setObjectName("Debugger.Dock.PeripheralRegister." + engineId); + m_peripheralRegisterWindow->setWindowTitle(tr("Peripheral Reg&isters")); + m_stackView = new StackTreeView; m_stackView->setModel(m_stackHandler.model()); m_stackView->setSettings(settings, "Debugger.StackView"); @@ -816,6 +834,7 @@ void DebuggerEnginePrivate::setupViews() m_modulesWindow->setFont(font); //m_consoleWindow->setFont(font); m_registerWindow->setFont(font); + m_peripheralRegisterWindow->setFont(font); m_returnWindow->setFont(font); m_sourceFilesWindow->setFont(font); m_stackWindow->setFont(font); @@ -832,6 +851,7 @@ void DebuggerEnginePrivate::setupViews() m_perspective->addWindow(m_localsAndInspectorWindow, Perspective::AddToTab, nullptr, true, Qt::RightDockWidgetArea); m_perspective->addWindow(m_watchersWindow, Perspective::SplitVertical, m_localsAndInspectorWindow, true, Qt::RightDockWidgetArea); m_perspective->addWindow(m_registerWindow, Perspective::AddToTab, m_localsAndInspectorWindow, false, Qt::RightDockWidgetArea); + m_perspective->addWindow(m_peripheralRegisterWindow, Perspective::AddToTab, m_localsAndInspectorWindow, false, Qt::RightDockWidgetArea); m_perspective->addWindow(m_logWindow, Perspective::AddToTab, nullptr, false, Qt::TopDockWidgetArea); m_perspective->select(); @@ -909,6 +929,11 @@ bool DebuggerEngine::isRegistersWindowVisible() const return d->m_registerWindow->isVisible(); } +bool DebuggerEngine::isPeripheralRegistersWindowVisible() const +{ + return d->m_peripheralRegisterWindow->isVisible(); +} + bool DebuggerEngine::isModulesWindowVisible() const { return d->m_modulesWindow->isVisible(); @@ -940,6 +965,11 @@ RegisterHandler *DebuggerEngine::registerHandler() const return &d->m_registerHandler; } +PeripheralRegisterHandler *DebuggerEngine::peripheralRegisterHandler() const +{ + return &d->m_peripheralRegisterHandler; +} + StackHandler *DebuggerEngine::stackHandler() const { return &d->m_stackHandler; @@ -993,6 +1023,12 @@ void DebuggerEngine::setRegisterValue(const QString &name, const QString &value) Q_UNUSED(value) } +void DebuggerEngine::setPeripheralRegisterValue(quint64 address, quint64 value) +{ + Q_UNUSED(address) + Q_UNUSED(value) +} + void DebuggerEngine::setRunParameters(const DebuggerRunParameters &runParameters) { d->m_runParameters = runParameters; @@ -1593,6 +1629,7 @@ void DebuggerEnginePrivate::setBusyCursor(bool busy) m_modulesWindow->setCursor(cursor); m_logWindow->setCursor(cursor); m_registerWindow->setCursor(cursor); + m_peripheralRegisterWindow->setCursor(cursor); m_returnWindow->setCursor(cursor); m_sourceFilesWindow->setCursor(cursor); m_stackWindow->setCursor(cursor); @@ -2075,6 +2112,10 @@ void DebuggerEngine::reloadRegisters() { } +void DebuggerEngine::reloadPeripheralRegisters() +{ +} + void DebuggerEngine::reloadSourceFiles() { } diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index f4465751cec..4cbb7569952 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -101,6 +101,7 @@ class LocationMark; class LogWindow; class ModulesHandler; class RegisterHandler; +class PeripheralRegisterHandler; class StackHandler; class StackFrame; class SourceFilesHandler; @@ -316,12 +317,14 @@ public: virtual void requestModuleSections(const QString &moduleName); virtual void reloadRegisters(); + virtual void reloadPeripheralRegisters(); virtual void reloadSourceFiles(); virtual void reloadFullStack(); virtual void loadAdditionalQmlStack(); virtual void reloadDebuggingHelpers(); virtual void setRegisterValue(const QString &name, const QString &value); + virtual void setPeripheralRegisterValue(quint64 address, quint64 value); virtual void addOptionPages(QList *) const; virtual bool hasCapability(unsigned cap) const = 0; virtual void debugLastCommand() {} @@ -356,6 +359,7 @@ public: ModulesHandler *modulesHandler() const; RegisterHandler *registerHandler() const; + PeripheralRegisterHandler *peripheralRegisterHandler() const; StackHandler *stackHandler() const; ThreadsHandler *threadsHandler() const; WatchHandler *watchHandler() const; @@ -453,6 +457,7 @@ public: QString debuggerName() const; bool isRegistersWindowVisible() const; + bool isPeripheralRegistersWindowVisible() const; bool isModulesWindowVisible() const; void openMemoryEditor(); @@ -551,6 +556,7 @@ private: friend class DebuggerPluginPrivate; friend class DebuggerEnginePrivate; friend class LocationMark; + friend class PeripheralRegisterHandler; DebuggerEnginePrivate *d; }; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index f17410ac091..5a40aa2ee0c 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -1030,6 +1030,7 @@ void GdbEngine::updateAll() stackHandler()->setCurrentIndex(0); runCommand({"-thread-info", CB(handleThreadInfo)}); reloadRegisters(); + reloadPeripheralRegisters(); updateLocals(); } @@ -2903,6 +2904,7 @@ void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isF // logStreamOutput: "Previous frame identical to this frame (corrupt stack?)\n" //qDebug() << "LISTING STACK FAILED: " << response.toString(); reloadRegisters(); + reloadPeripheralRegisters(); return; } @@ -2943,6 +2945,7 @@ void GdbEngine::activateFrame(int frameIndex) updateLocals(); reloadRegisters(); + reloadPeripheralRegisters(); } void GdbEngine::handleThreadInfo(const DebuggerResponse &response) @@ -3062,6 +3065,22 @@ void GdbEngine::reloadRegisters() } } +void GdbEngine::reloadPeripheralRegisters() +{ + if (!isPeripheralRegistersWindowVisible()) + return; + + const QList addresses = peripheralRegisterHandler()->activeRegisters(); + if (addresses.isEmpty()) + return; // Nothing to update. + + for (const quint64 address : addresses) { + const QString fun = QStringLiteral("x/1u 0x%1") + .arg(QString::number(address, 16)); + runCommand({fun, CB(handlePeripheralRegisterListValues)}); + } +} + static QString readWord(const QString &ba, int *pos) { const int n = ba.size(); @@ -3127,6 +3146,15 @@ void GdbEngine::setRegisterValue(const QString &name, const QString &value) reloadRegisters(); } +void GdbEngine::setPeripheralRegisterValue(quint64 address, quint64 value) +{ + const QString fun = QStringLiteral("set {int}0x%1=%2") + .arg(QString::number(address, 16)) + .arg(value); + runCommand({fun}); + reloadPeripheralRegisters(); +} + void GdbEngine::handleRegisterListNames(const DebuggerResponse &response) { if (response.resultClass != ResultDone) { @@ -3227,6 +3255,28 @@ void GdbEngine::handleRegisterListValues(const DebuggerResponse &response) handler->commitUpdates(); } +void GdbEngine::handlePeripheralRegisterListValues( + const DebuggerResponse &response) +{ + if (response.resultClass != ResultDone) + return; + + const QString output = response.consoleStreamOutput; + // Regexp to match for '0x50060800:\t0\n'. + const QRegularExpression re("^(0x[0-9A-F]+):\\t(\\d+)\\n$"); + const QRegularExpressionMatch m = re.match(output); + if (!m.hasMatch()) + return; + enum { AddressMatch = 1, ValueMatch = 2 }; + bool aok = false; + bool vok = false; + const quint64 address = m.captured(AddressMatch).toULongLong(&aok, 16); + const quint64 value = m.captured(ValueMatch).toULongLong(&vok, 10); + if (!aok || !vok) + return; + + peripheralRegisterHandler()->updateRegister(address, value); +} ////////////////////////////////////////////////////////////////////// // @@ -3703,6 +3753,9 @@ void GdbEngine::setupEngine() runCommand({commands}); runCommand({"loadDumpers", CB(handlePythonSetup)}); + + // Reload peripheral register description. + peripheralRegisterHandler()->updateRegisterGroups(); } void GdbEngine::handleGdbStartFailed() diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 097b5ea81c4..8f94a9ed7e7 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -266,10 +267,13 @@ private: ////////// General Interface ////////// // Register specific stuff // void reloadRegisters() final; + void reloadPeripheralRegisters() final; void setRegisterValue(const QString &name, const QString &value) final; + void setPeripheralRegisterValue(quint64 address, quint64 value) final; void handleRegisterListNames(const DebuggerResponse &response); void handleRegisterListing(const DebuggerResponse &response); void handleRegisterListValues(const DebuggerResponse &response); + void handlePeripheralRegisterListValues(const DebuggerResponse &response); void handleMaintPrintRegisters(const DebuggerResponse &response); QHash m_registers; // Map GDB register numbers to indices diff --git a/src/plugins/debugger/peripheralregisterhandler.cpp b/src/plugins/debugger/peripheralregisterhandler.cpp new file mode 100644 index 00000000000..1c1c6ff5584 --- /dev/null +++ b/src/plugins/debugger/peripheralregisterhandler.cpp @@ -0,0 +1,956 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "peripheralregisterhandler.h" + +#include "debuggeractions.h" +#include "debuggercore.h" +#include "debuggerdialogs.h" + +#include +#include + +#include +#include +#include +#include + +using namespace Utils; + +namespace Debugger { +namespace Internal { + +namespace { +// Keys of a properties in SVD file. +constexpr char kAccess[] = "access"; +constexpr char kAddressOffset[] = "addressOffset"; +constexpr char kBaseAddress[] = "baseAddress"; +constexpr char kBitOffset[] = "bitOffset"; +constexpr char kBitRange[] = "bitRange"; +constexpr char kBitWidth[] = "bitWidth"; +constexpr char kDerivedFrom[] = "derivedFrom"; +constexpr char kDescription[] = "description"; +constexpr char kField[] = "field"; +constexpr char kFields[] = "fields"; +constexpr char kGroupName[] = "groupName"; +constexpr char kName[] = "name"; +constexpr char kPeripheral[] = "peripheral"; +constexpr char kPeripherals[] = "peripherals"; +constexpr char kReadOnly[] = "read-only"; +constexpr char kReadWrite[] = "read-write"; +constexpr char kRegister[] = "register"; +constexpr char kRegisters[] = "registers"; +constexpr char kResetvalue[] = "resetValue"; +constexpr char kSize[] = "size"; +constexpr char kWritOnlye[] = "write-only"; +} // namespace + +enum PeripheralRegisterColumns +{ + PeripheralRegisterNameColumn, + PeripheralRegisterValueColumn, + PeripheralRegisterAccessColumn, + PeripheralRegisterColumnCount +}; + +enum PeripheralRegisterDataRole +{ + PeripheralRegisterChangedRole = Qt::UserRole +}; + +static QString accessName(PeripheralRegisterAccess access) +{ + switch (access) { + case PeripheralRegisterAccess::ReadOnly: + return PeripheralRegisterHandler::tr("RO"); + case PeripheralRegisterAccess::WriteOnly: + return PeripheralRegisterHandler::tr("WO"); + case PeripheralRegisterAccess::ReadWrite: + return PeripheralRegisterHandler::tr("RW"); + default: + return PeripheralRegisterHandler::tr("N/A"); + } +} + +static PeripheralRegisterAccess decodeAccess(const QString &accessText) +{ + if (accessText == QLatin1String(kReadWrite)) + return PeripheralRegisterAccess::ReadWrite; + if (accessText == QLatin1String(kReadOnly)) + return PeripheralRegisterAccess::ReadOnly; + if (accessText == QLatin1String(kWritOnlye)) + return PeripheralRegisterAccess::WriteOnly; + return PeripheralRegisterAccess::Unknown; +} + +static quint64 decodeNumeric(const QString &text) +{ + bool ok = false; + const quint64 result = text.toULongLong(&ok, 10); + if (ok) + return result; + return text.toUInt(&ok, 16); +} + +static QString valueToString(quint64 value, int size, PeripheralRegisterFormat fmt) +{ + QString result; + if (fmt == PeripheralRegisterFormat::Hexadecimal) { + result = QString::number(value, 16); + result.prepend("0x" + QString(size / 4 - result.size(), '0')); + } else if (fmt == PeripheralRegisterFormat::Decimal) { + result = QString::number(value, 10); + } else if (fmt == PeripheralRegisterFormat::Octal) { + result = QString::number(value, 8); + result.prepend('0' + QString(size / 2 - result.size(), '0')); + } else if (fmt == PeripheralRegisterFormat::Binary) { + result = QString::number(value, 2); + result.prepend("0b" + QString(size - result.size(), '0')); + } + return result; +} + +static quint64 valueFromString(const QString &string, PeripheralRegisterFormat fmt, + bool *ok) +{ + if (fmt == PeripheralRegisterFormat::Hexadecimal) { + const QRegularExpression re("^(0x)?([0-9A-F]+)$"); + const QRegularExpressionMatch m = re.match(string); + if (m.hasMatch()) + return m.captured(2).toULongLong(ok, 16); + } else if (fmt == PeripheralRegisterFormat::Decimal) { + const QRegularExpression re("^([0-9]+)$"); + const QRegularExpressionMatch m = re.match(string); + if (m.hasMatch()) + return m.captured(1).toULongLong(ok, 10); + } else if (fmt == PeripheralRegisterFormat::Octal) { + const QRegularExpression re("^(0)?([0-7]+)$"); + const QRegularExpressionMatch m = re.match(string); + if (m.hasMatch()) + return m.captured(2).toULongLong(ok, 8); + } else if (fmt == PeripheralRegisterFormat::Binary) { + const QRegularExpression re("^(0b)?([0-1]+)$"); + const QRegularExpressionMatch m = re.match(string); + if (m.hasMatch()) + return m.captured(2).toULongLong(ok, 2); + } + + *ok = false; + return 0; +} + +// PeripheralRegisterField + +QString PeripheralRegisterField::bitRangeString() const +{ + const int from = bitOffset; + const int to = bitOffset + bitWidth - 1; + return PeripheralRegisterHandler::tr("[%1..%2]").arg(from).arg(to); +} + +QString PeripheralRegisterField::bitValueString(quint64 regValue) const +{ + const quint64 value = bitValue(regValue); + return valueToString(value, bitWidth, format); +} + +quint64 PeripheralRegisterField::bitValue(quint64 regValue) const +{ + const quint64 mask = bitMask(); + regValue &= mask; + regValue >>= bitOffset; + return regValue; +} + +quint64 PeripheralRegisterField::bitMask() const +{ + quint64 mask = 0; + for (auto pos = bitOffset; pos < bitOffset + bitWidth; ++pos) + mask |= quint64(quint64(1) << pos); + return mask; +} + +// PeripheralRegisterValue + +bool PeripheralRegisterValue::fromString(const QString &string, + PeripheralRegisterFormat fmt) +{ + bool ok = false; + const quint64 newVal = valueFromString(string, fmt, &ok); + if (!ok) + return false; + v = newVal; + return true; +} + +QString PeripheralRegisterValue::toString( + int size, PeripheralRegisterFormat fmt) const +{ + return valueToString(v, size, fmt); +} + +// PeripheralRegister + +QString PeripheralRegister::currentValueString() const +{ + return currentValue.toString(size, format); +} + +QString PeripheralRegister::previousValueString() const +{ + return previousValue.toString(size, format); +} + +QString PeripheralRegister::resetValueString() const +{ + return resetValue.toString(size, format); +} + +QString PeripheralRegister::addressString(quint64 baseAddress) const +{ + return "0x" + QString::number(address(baseAddress), 16); +} + +quint64 PeripheralRegister::address(quint64 baseAddress) const +{ + return baseAddress + addressOffset; +} + +// PeripheralRegisterFieldItem + +class PeripheralRegisterFieldItem final + : public TypedTreeItem +{ +public: + explicit PeripheralRegisterFieldItem( + DebuggerEngine *engine, const PeripheralRegisterGroup &group, + PeripheralRegister ®, PeripheralRegisterField &fld); + + QVariant data(int column, int role) const final; + bool setData(int column, const QVariant &value, int role) final; + Qt::ItemFlags flags(int column) const final; + + void triggerChange(); + + DebuggerEngine *m_engine = nullptr; + const PeripheralRegisterGroup &m_group; + PeripheralRegister &m_reg; + PeripheralRegisterField &m_fld; +}; + +PeripheralRegisterFieldItem::PeripheralRegisterFieldItem( + DebuggerEngine *engine, const PeripheralRegisterGroup &group, + PeripheralRegister ®, PeripheralRegisterField &fld) + : m_engine(engine), m_group(group), m_reg(reg), m_fld(fld) +{ +} + +QVariant PeripheralRegisterFieldItem::data(int column, int role) const +{ + switch (role) { + case PeripheralRegisterChangedRole: + return m_fld.bitValue(m_reg.currentValue.v) + != m_fld.bitValue(m_reg.previousValue.v); + + case Qt::DisplayRole: + switch (column) { + case PeripheralRegisterNameColumn: + return m_fld.name; + case PeripheralRegisterValueColumn: + return m_fld.bitValueString(m_reg.currentValue.v); + case PeripheralRegisterAccessColumn: + return accessName(m_fld.access); + } + break; + + case Qt::ToolTipRole: + switch (column) { + case PeripheralRegisterNameColumn: + return QString("%1.%2\n%3\nBits: %4, %5") + .arg(m_reg.name) + .arg(m_fld.name) + .arg(m_fld.description) + .arg(m_fld.bitRangeString()) + .arg(m_fld.bitWidth); + case PeripheralRegisterValueColumn: + return QString("Value: %1").arg( + m_fld.bitValueString(m_reg.currentValue.v)); + } + break; + + case Qt::EditRole: + return m_fld.bitValueString(m_reg.currentValue.v); + + case Qt::TextAlignmentRole: { + if (column == PeripheralRegisterValueColumn) + return Qt::AlignRight; + return {}; + } + + default: + break; + } + return {}; +} + +bool PeripheralRegisterFieldItem::setData( + int column, const QVariant &value, int role) +{ + if (column == PeripheralRegisterValueColumn && role == Qt::EditRole) { + bool ok = false; + quint64 bitValue = valueFromString(value.toString(), m_fld.format, &ok); + if (!ok) + return false; + bitValue <<= m_fld.bitOffset; + const quint64 mask = m_fld.bitMask(); + m_reg.currentValue.v &= ~mask; + m_reg.currentValue.v |= bitValue; + triggerChange(); + return true; + } + return false; +} + +Qt::ItemFlags PeripheralRegisterFieldItem::flags(int column) const +{ + const Qt::ItemFlags notEditable = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (column != PeripheralRegisterValueColumn) + return notEditable; + if (m_fld.access == PeripheralRegisterAccess::ReadWrite + || m_fld.access == PeripheralRegisterAccess::WriteOnly) { + return notEditable | Qt::ItemIsEditable; + } + return notEditable; +} + +void PeripheralRegisterFieldItem::triggerChange() +{ + m_engine->setPeripheralRegisterValue(m_reg.address(m_group.baseAddress), + m_reg.currentValue.v); +} + +// PeripheralRegisterItem + +class PeripheralRegisterItem final + : public TypedTreeItem +{ +public: + explicit PeripheralRegisterItem( + DebuggerEngine *engine, + const PeripheralRegisterGroup &group, + PeripheralRegister ®); + + QVariant data(int column, int role) const final; + bool setData(int column, const QVariant &value, int role) final; + Qt::ItemFlags flags(int column) const final; + + void triggerChange(); + + DebuggerEngine *m_engine = nullptr; + const PeripheralRegisterGroup &m_group; + PeripheralRegister &m_reg; +}; + +PeripheralRegisterItem::PeripheralRegisterItem( + DebuggerEngine *engine, + const PeripheralRegisterGroup &group, + PeripheralRegister ®) + : m_engine(engine), m_group(group), m_reg(reg) +{ + for (auto &fld : m_reg.fields) { + const auto item = new PeripheralRegisterFieldItem( + m_engine, m_group, m_reg, fld); + appendChild(item); + } +} + +QVariant PeripheralRegisterItem::data(int column, int role) const +{ + switch (role) { + case PeripheralRegisterChangedRole: + return m_reg.currentValue != m_reg.previousValue; + + case Qt::DisplayRole: + switch (column) { + case PeripheralRegisterNameColumn: + return m_reg.name; + case PeripheralRegisterValueColumn: + return m_reg.currentValueString(); + case PeripheralRegisterAccessColumn: + return accessName(m_reg.access); + } + break; + + case Qt::ToolTipRole: + switch (column) { + case PeripheralRegisterNameColumn: + return QStringLiteral("%1 / %2\n%3\n%4 @ %5, %6") + .arg(m_group.name) + .arg(m_reg.name) + .arg(m_reg.description) + .arg(accessName(m_reg.access)) + .arg(m_reg.addressString(m_group.baseAddress)) + .arg(m_reg.size); + case PeripheralRegisterValueColumn: + return QStringLiteral("Current value: %1\n" + "Previous value: %2\n" + "Reset value: %3") + .arg(m_reg.currentValueString(), + m_reg.previousValueString(), + m_reg.resetValueString()); + } + break; + + case Qt::EditRole: + return m_reg.currentValueString(); + + case Qt::TextAlignmentRole: { + if (column == PeripheralRegisterValueColumn) + return Qt::AlignRight; + return {}; + } + + default: + break; + } + return {}; +} + +bool PeripheralRegisterItem::setData(int column, const QVariant &value, int role) +{ + if (column == PeripheralRegisterValueColumn && role == Qt::EditRole) { + if (m_reg.currentValue.fromString(value.toString(), m_reg.format)) { + triggerChange(); + return true; + } + } + return false; +} + +Qt::ItemFlags PeripheralRegisterItem::flags(int column) const +{ + const Qt::ItemFlags notEditable = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (column != PeripheralRegisterValueColumn) + return notEditable; + if (m_reg.access == PeripheralRegisterAccess::ReadWrite + || m_reg.access == PeripheralRegisterAccess::WriteOnly) { + return notEditable | Qt::ItemIsEditable; + } + return notEditable; +} + +void PeripheralRegisterItem::triggerChange() +{ + m_engine->setPeripheralRegisterValue(m_reg.address(m_group.baseAddress), + m_reg.currentValue.v); +} + +// PeripheralRegisterDelegate + +class PeripheralRegisterDelegate final : public QItemDelegate +{ +public: + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, + const QModelIndex &index) const final + { + if (index.column() == PeripheralRegisterValueColumn) { + const auto lineEdit = new QLineEdit(parent); + lineEdit->setAlignment(Qt::AlignLeft); + lineEdit->setFrame(false); + return lineEdit; + } + return nullptr; + } + + void setEditorData(QWidget *editor, const QModelIndex &index) const final + { + const auto lineEdit = qobject_cast(editor); + QTC_ASSERT(lineEdit, return); + lineEdit->setText(index.data(Qt::EditRole).toString()); + } + + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const final + { + if (index.column() == PeripheralRegisterValueColumn) { + const auto lineEdit = qobject_cast(editor); + QTC_ASSERT(lineEdit, return); + model->setData(index, lineEdit->text(), Qt::EditRole); + } + } + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &) const final + { + editor->setGeometry(option.rect); + } + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const final + { + if (index.column() == PeripheralRegisterValueColumn) { + const bool paintRed = index.data(PeripheralRegisterChangedRole).toBool(); + const QPen oldPen = painter->pen(); + const QColor lightColor(140, 140, 140); + if (paintRed) + painter->setPen(QColor(200, 0, 0)); + else + painter->setPen(lightColor); + // FIXME: performance? this changes only on real font changes. + const QFontMetrics fm(option.font); + const int charWidth = qMax(fm.horizontalAdvance('x'), + fm.horizontalAdvance('0')); + const QString str = index.data(Qt::DisplayRole).toString(); + int x = option.rect.x(); + bool light = !paintRed; + for (int i = 0; i < str.size(); ++i) { + const QChar c = str.at(i); + const int uc = c.unicode(); + if (light && (uc != 'x' && uc != '0')) { + light = false; + painter->setPen(oldPen.color()); + } + if (uc == ' ') { + light = true; + painter->setPen(lightColor); + } else { + QRect r = option.rect; + r.setX(x); + r.setWidth(charWidth); + painter->drawText(r, Qt::AlignHCenter, c); + } + x += charWidth; + } + painter->setPen(oldPen); + } else { + QItemDelegate::paint(painter, option, index); + } + } +}; + +// PeripheralRegisterHandler + +PeripheralRegisterHandler::PeripheralRegisterHandler(DebuggerEngine *engine) + : m_engine(engine) +{ + setObjectName("PeripheralRegisterModel"); + setHeader({tr("Name"), tr("Value"), tr("Access")}); +} + +static PeripheralRegisterGroups availablePeripheralRegisterGroups( + const QString &filePath) +{ + QFile f(filePath); + if (!f.open(QIODevice::ReadOnly)) + return {}; + + QXmlStreamReader in(&f); + + PeripheralRegisterGroups foundGroups; + + while (!in.atEnd()) { + const auto token = in.readNext(); + if (token == QXmlStreamReader::EndElement + && in.name() == QLatin1String(kPeripherals)) { + break; + } else if (token != QXmlStreamReader::StartElement + || in.name() != QLatin1String(kPeripheral)) { + continue; + } + + PeripheralRegisterGroup group; + + const auto fromGroupName = in.attributes().value( + QLatin1String(kDerivedFrom)); + const auto foundGroupEnd = foundGroups.cend(); + const auto foundGroupIt = std::find_if( + foundGroups.cbegin(), foundGroupEnd, + [fromGroupName](const PeripheralRegisterGroup &foundGroup) { + return fromGroupName == foundGroup.name; + }); + if (foundGroupIt != foundGroupEnd) + group = *foundGroupIt; + + while (!in.atEnd()) { + const auto token = in.readNext(); + if (token == QXmlStreamReader::EndElement + && in.name() == QLatin1String(kPeripheral)) { + foundGroups.push_back(group); + break; + } else if (token == QXmlStreamReader::StartElement) { + const auto elementName = in.name(); + if (elementName == QLatin1String(kName)) { + group.name = in.readElementText(); + } else if (elementName == QLatin1String(kDescription)) { + group.description = in.readElementText(); + } else if (elementName == QLatin1String(kGroupName)) { + group.displayName = in.readElementText(); + } else if (elementName == QLatin1String(kBaseAddress)) { + group.baseAddress = decodeNumeric(in.readElementText()); + } else if (elementName == QLatin1String(kSize)) { + group.size = int(decodeNumeric(in.readElementText())); + } else if (elementName == QLatin1String(kAccess)) { + group.access = decodeAccess(in.readElementText()); + } else if (elementName == QLatin1String(kRegisters) + || elementName == QLatin1String(kRegister)) { + PeripheralRegister reg; + while (!in.atEnd()) { + const auto token = in.readNext(); + if (token == QXmlStreamReader::EndElement + && in.name() == QLatin1String(kRegister)) { + group.registers.push_back(reg); + break; + } else if (token == QXmlStreamReader::StartElement) { + const auto elementName = in.name(); + if (elementName == QLatin1String(kRegister)) { + continue; + } else if (elementName == QLatin1String(kName)) { + reg.name = in.readElementText(); + } else if (elementName == QLatin1String(kDescription)) { + reg.description = in.readElementText(); + } else if (elementName == QLatin1String(kAddressOffset)) { + reg.addressOffset = decodeNumeric(in.readElementText()); + } else if (elementName == QLatin1String(kSize)) { + reg.size = int(decodeNumeric(in.readElementText())); + } else if (elementName == QLatin1String(kAccess)) { + reg.access = decodeAccess(in.readElementText()); + } else if (elementName == QLatin1String(kResetvalue)) { + reg.resetValue = decodeNumeric(in.readElementText()); + } else if (elementName == QLatin1String(kFields) + || elementName == QLatin1String(kField)) { + PeripheralRegisterField fld; + while (!in.atEnd()) { + const auto token = in.readNext(); + if (token == QXmlStreamReader::EndElement + && in.name() == QLatin1String(kField)) { + reg.fields.push_back(fld); + break; + } else if (token == QXmlStreamReader::StartElement) { + const auto elementName = in.name(); + if (elementName == QLatin1String(kField)) { + continue; + } else if (elementName == QLatin1String(kName)) { + fld.name = in.readElementText(); + } else if (elementName == QLatin1String(kDescription)) { + fld.description = in.readElementText(); + } else if (elementName == QLatin1String(kAccess)) { + fld.access = decodeAccess(in.readElementText()); + } else if (elementName == QLatin1String(kBitRange)) { + const QString elementText = in.readElementText(); + const int startBracket = elementText.indexOf('['); + const int endBracket = elementText.indexOf(']'); + if (startBracket == -1 || endBracket == -1 + || (endBracket - startBracket) <= 0) { + continue; + } + const QString range = elementText.mid( + startBracket + 1, endBracket - 1); + const QStringList items = range.split(':'); + enum { MaxBit, MinBit, BitsCount }; + if (items.count() != BitsCount) + continue; + const int from = int(decodeNumeric(items.at(MinBit))); + const int to = int(decodeNumeric(items.at(MaxBit))); + fld.bitOffset = from; + fld.bitWidth = to - from + 1; + } else if (elementName == QLatin1String(kBitOffset)) { + fld.bitOffset = int(decodeNumeric(in.readElementText())); + } else if (elementName == QLatin1String(kBitWidth)) { + fld.bitWidth = int(decodeNumeric(in.readElementText())); + } else { + in.skipCurrentElement(); + } + } + } + } else { + in.skipCurrentElement(); + } + } + } + } else { + in.skipCurrentElement(); + } + } + } + } + + return foundGroups; +} + +void PeripheralRegisterHandler::updateRegisterGroups() +{ + clear(); + + const auto dev = m_engine->device(); + QTC_ASSERT(dev, return); + + const QString filePath = dev->peripheralDescriptionFilePath(); + if (filePath.isEmpty()) + return; + + m_peripheralRegisterGroups = availablePeripheralRegisterGroups(filePath); +} + +void PeripheralRegisterHandler::updateRegister(quint64 address, quint64 value) +{ + const auto regItem = m_activeRegisters.value(address); + if (!regItem) + return; + + regItem->m_reg.previousValue = regItem->m_reg.currentValue; + regItem->m_reg.currentValue = value; + + commitUpdates(); +} + +QList PeripheralRegisterHandler::activeRegisters() const +{ + return m_activeRegisters.keys(); +} + +QVariant PeripheralRegisterHandler::data(const QModelIndex &idx, int role) const +{ + if (role == BaseTreeView::ItemDelegateRole) { + return QVariant::fromValue(static_cast( + new PeripheralRegisterDelegate)); + } + return PeripheralRegisterModel::data(idx, role); +} + +bool PeripheralRegisterHandler::setData(const QModelIndex &idx, + const QVariant &data, int role) +{ + if (role == BaseTreeView::ItemViewEventRole) { + const auto ev = data.value(); + if (ev.type() == QEvent::ContextMenu) + return contextMenuEvent(ev); + } + return PeripheralRegisterModel::setData(idx, data, role); +} + +bool PeripheralRegisterHandler::contextMenuEvent(const ItemViewEvent &ev) +{ + const DebuggerState state = m_engine->state(); + const auto menu = new QMenu; + + QMenu *groupMenu = createRegisterGroupsMenu(state); + menu->addMenu(groupMenu); + + if (const auto regItem = itemForIndexAtLevel( + ev.sourceModelIndex())) { + // Show the register value format menu only + // if a register item chose. + QMenu *fmtMenu = createRegisterFormatMenu(state, regItem); + menu->addMenu(fmtMenu); + } else if (const auto fldItem = itemForIndexAtLevel( + ev.sourceModelIndex())) { + // Show the register field value format menu only + // if a register field item chose. + QMenu *fmtMenu = createRegisterFieldFormatMenu(state, fldItem); + menu->addMenu(fmtMenu); + } + + menu->addSeparator(); + menu->addAction(action(SettingsDialog)); + menu->popup(ev.globalPos()); + return true; +} + +QMenu *PeripheralRegisterHandler::createRegisterGroupsMenu(DebuggerState state) const +{ + const auto groupMenu = new QMenu(tr("View Groups")); + const auto actionGroup = new QActionGroup(groupMenu); + bool hasActions = false; + for (const PeripheralRegisterGroup &group : qAsConst(m_peripheralRegisterGroups)) { + const QString actName = QStringLiteral("%1: %2") + .arg(group.name, group.description); + QAction *act = groupMenu->addAction(actName); + const bool on = m_engine->hasCapability(RegisterCapability) + && (state == InferiorStopOk || state == InferiorUnrunnable); + act->setEnabled(on); + act->setData(group.name); + act->setCheckable(true); + act->setChecked(group.active); + actionGroup->addAction(act); + QObject::connect(act, &QAction::triggered, + this, &PeripheralRegisterHandler::setActiveGroup); + hasActions = true; + } + groupMenu->setEnabled(hasActions); + return groupMenu; +} + +QMenu *PeripheralRegisterHandler::createRegisterFormatMenu( + DebuggerState state, PeripheralRegisterItem *item) const +{ + const auto fmtMenu = new QMenu(tr("Format")); + const auto actionGroup = new QActionGroup(fmtMenu); + + const bool on = m_engine->hasCapability(RegisterCapability) + && (state == InferiorStopOk || state == InferiorUnrunnable); + + const PeripheralRegisterFormat fmt = item->m_reg.format; + + // Hexadecimal action. + const auto hexAct = addCheckableAction( + fmtMenu, tr("Hexadecimal"), on, + fmt == PeripheralRegisterFormat::Hexadecimal, + [item] { + item->m_reg.format = PeripheralRegisterFormat::Hexadecimal; + item->update(); + }); + actionGroup->addAction(hexAct); + + // Decimal action. + const auto decAct = addCheckableAction( + fmtMenu, tr("Decimal"), on, + fmt == PeripheralRegisterFormat::Decimal, + [item] { + item->m_reg.format = PeripheralRegisterFormat::Decimal; + item->update(); + }); + actionGroup->addAction(decAct); + + // Octal action. + const auto octAct = addCheckableAction( + fmtMenu, tr("Octal"), on, + fmt == PeripheralRegisterFormat::Octal, + [item] { + item->m_reg.format = PeripheralRegisterFormat::Octal; + item->update(); + }); + actionGroup->addAction(octAct); + + // Binary action. + const auto binAct = addCheckableAction( + fmtMenu, tr("Binary"), on, + fmt == PeripheralRegisterFormat::Binary, + [item] { + item->m_reg.format = PeripheralRegisterFormat::Binary; + item->update(); + }); + actionGroup->addAction(binAct); + + return fmtMenu; +} + +QMenu *PeripheralRegisterHandler::createRegisterFieldFormatMenu( + DebuggerState state, PeripheralRegisterFieldItem *item) const +{ + const auto fmtMenu = new QMenu(tr("Format")); + const auto actionGroup = new QActionGroup(fmtMenu); + + const bool on = m_engine->hasCapability(RegisterCapability) + && (state == InferiorStopOk || state == InferiorUnrunnable); + + const PeripheralRegisterFormat fmt = item->m_fld.format; + + // Hexadecimal action. + const auto hexAct = addCheckableAction( + fmtMenu, tr("Hexadecimal"), on, + fmt == PeripheralRegisterFormat::Hexadecimal, + [item] { + item->m_fld.format = PeripheralRegisterFormat::Hexadecimal; + item->update(); + }); + actionGroup->addAction(hexAct); + + // Decimal action. + const auto decAct = addCheckableAction( + fmtMenu, tr("Decimal"), on, + fmt == PeripheralRegisterFormat::Decimal, + [item] { + item->m_fld.format = PeripheralRegisterFormat::Decimal; + item->update(); + }); + actionGroup->addAction(decAct); + + // Octal action. + const auto octAct = addCheckableAction( + fmtMenu, tr("Octal"), on, + fmt == PeripheralRegisterFormat::Octal, + [item] { + item->m_fld.format = PeripheralRegisterFormat::Octal; + item->update(); + }); + actionGroup->addAction(octAct); + + // Binary action. + const auto binAct = addCheckableAction( + fmtMenu, tr("Binary"), on, + fmt == PeripheralRegisterFormat::Binary, + [item] { + item->m_fld.format = PeripheralRegisterFormat::Binary; + item->update(); + }); + actionGroup->addAction(binAct); + + return fmtMenu; +} + +void PeripheralRegisterHandler::setActiveGroup(bool checked) +{ + if (!checked) + return; + deactivateGroups(); + if (const auto act = qobject_cast(sender())) { + const QString groupName = act->data().toString(); + const auto groupEnd = m_peripheralRegisterGroups.end(); + const auto groupIt = std::find_if( + m_peripheralRegisterGroups.begin(), groupEnd, + [groupName](const PeripheralRegisterGroup &group){ + return group.name == groupName; + }); + if (groupIt == groupEnd) + return; // Group not found. + // Set active group. + groupIt->active = true; + + // Add all register items of active register group. + m_activeRegisters.reserve(groupIt->registers.count()); + for (PeripheralRegister ® : groupIt->registers) { + const auto item = new PeripheralRegisterItem(m_engine, *groupIt, reg); + rootItem()->appendChild(item); + + const quint64 address = reg.address(groupIt->baseAddress); + m_activeRegisters.insert(address, item); + } + + m_engine->reloadPeripheralRegisters(); + } +} + +void PeripheralRegisterHandler::deactivateGroups() +{ + clear(); + + for (PeripheralRegisterGroup &group : m_peripheralRegisterGroups) + group.active = false; + + m_activeRegisters.clear(); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/peripheralregisterhandler.h b/src/plugins/debugger/peripheralregisterhandler.h new file mode 100644 index 00000000000..a7f8924113e --- /dev/null +++ b/src/plugins/debugger/peripheralregisterhandler.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "debuggerengine.h" + +#include + +#include + +QT_BEGIN_NAMESPACE +class QMenu; +QT_END_NAMESPACE + +namespace Utils { class ItemViewEvent; } + +namespace Debugger { +namespace Internal { + +class DebuggerEngine; + +enum class PeripheralRegisterAccess +{ + Unknown, + ReadOnly, + WriteOnly, + ReadWrite +}; + +enum class PeripheralRegisterFormat +{ + Hexadecimal, + Decimal, + Octal, + Binary +}; + +// PeripheralRegisterField + +class PeripheralRegisterField final +{ +public: + QString bitRangeString() const; + QString bitValueString(quint64 regValue) const; + quint64 bitValue(quint64 regValue) const; + quint64 bitMask() const; + + QString name; + QString description; + int bitOffset = 0; + int bitWidth = 0; + PeripheralRegisterAccess access = PeripheralRegisterAccess::Unknown; + PeripheralRegisterFormat format = PeripheralRegisterFormat::Hexadecimal; +}; + +// PeripheralRegisterValue + +class PeripheralRegisterValue final +{ +public: + PeripheralRegisterValue(quint64 v = 0) : v(v) {} + bool operator==(const PeripheralRegisterValue &other) { return v == other.v; } + bool operator!=(const PeripheralRegisterValue &other) { return !operator==(other); } + + bool fromString(const QString &string, PeripheralRegisterFormat fmt); + QString toString(int size, PeripheralRegisterFormat fmt) const; + + quint64 v = 0; +}; + +// PeripheralRegister + +class PeripheralRegister final +{ +public: + QString currentValueString() const; + QString previousValueString() const; + QString resetValueString() const; + + QString addressString(quint64 baseAddress) const; + quint64 address(quint64 baseAddress) const; + + QString name; + QString displayName; + QString description; + quint64 addressOffset = 0; + int size = 0; // in bits + PeripheralRegisterAccess access = PeripheralRegisterAccess::Unknown; + PeripheralRegisterFormat format = PeripheralRegisterFormat::Hexadecimal; + + QVector fields; + + PeripheralRegisterValue currentValue; + PeripheralRegisterValue previousValue; + PeripheralRegisterValue resetValue; +}; + +// PeripheralRegisterGroup + +class PeripheralRegisterGroup final +{ +public: + QString name; + QString displayName; + QString description; + quint64 baseAddress = 0; + int size = 0; // in bits + PeripheralRegisterAccess access = PeripheralRegisterAccess::Unknown; + bool active = false; + QVector registers; +}; + +// PeripheralRegisterGroups + +using PeripheralRegisterGroups = QVector; + +// PeripheralRegisterItem's + +enum { PeripheralRegisterLevel = 1, PeripheralRegisterFieldLevel = 2 }; + +class PeripheralRegisterFieldItem; +class PeripheralRegisterItem; +using PeripheralRegisterRootItem = Utils::TypedTreeItem; +using PeripheralRegisterModel = Utils::TreeModel; + +// PeripheralRegisterHandler + +class PeripheralRegisterHandler final : public PeripheralRegisterModel +{ + Q_OBJECT +public: + explicit PeripheralRegisterHandler(DebuggerEngine *engine); + + QAbstractItemModel *model() { return this; } + + void updateRegisterGroups(); + void updateRegister(quint64 address, quint64 value); + void commitUpdates() { emit layoutChanged(); } + QList activeRegisters() const; + +private: + QVariant data(const QModelIndex &idx, int role) const final; + bool setData(const QModelIndex &idx, const QVariant &data, int role) final; + + bool contextMenuEvent(const Utils::ItemViewEvent &ev); + QMenu *createRegisterGroupsMenu(DebuggerState state) const; + QMenu *createRegisterFormatMenu(DebuggerState state, + PeripheralRegisterItem *item) const; + QMenu *createRegisterFieldFormatMenu(DebuggerState state, + PeripheralRegisterFieldItem *item) const; + void setActiveGroup(bool checked); + void deactivateGroups(); + + PeripheralRegisterGroups m_peripheralRegisterGroups; + QHash m_activeRegisters; + DebuggerEngine * const m_engine; +}; + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 07d85970e07..a5fd595ae4d 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -123,6 +123,7 @@ const char TimeoutKey[] = "Timeout"; const char HostKeyCheckingKey[] = "HostKeyChecking"; const char DebugServerKey[] = "DebugServerKey"; +const char PeripheralDescriptionFileKey[] = "PeripheralDescriptionFileKey"; const char QmlsceneKey[] = "QmlsceneKey"; using AuthType = QSsh::SshConnectionParameters::AuthenticationType; @@ -149,6 +150,7 @@ public: QSsh::SshConnectionParameters sshParameters; Utils::PortList freePorts; QString debugServerPath; + QString peripheralDescriptionFilePath; QString qmlsceneCommand; QList deviceIcons; @@ -378,6 +380,7 @@ void IDevice::fromMap(const QVariantMap &map) d->version = map.value(QLatin1String(VersionKey), 0).toInt(); d->debugServerPath = map.value(QLatin1String(DebugServerKey)).toString(); + d->peripheralDescriptionFilePath = map.value(QLatin1String(PeripheralDescriptionFileKey)).toString(); d->qmlsceneCommand = map.value(QLatin1String(QmlsceneKey)).toString(); d->extraData = map.value(ExtraDataKey).toMap(); } @@ -409,6 +412,7 @@ QVariantMap IDevice::toMap() const map.insert(QLatin1String(VersionKey), d->version); map.insert(QLatin1String(DebugServerKey), d->debugServerPath); + map.insert(QLatin1String(PeripheralDescriptionFileKey), d->peripheralDescriptionFilePath); map.insert(QLatin1String(QmlsceneKey), d->qmlsceneCommand); map.insert(ExtraDataKey, d->extraData); @@ -488,6 +492,17 @@ void IDevice::setDebugServerPath(const QString &path) d->debugServerPath = path; } + +QString IDevice::peripheralDescriptionFilePath() const +{ + return d->peripheralDescriptionFilePath; +} + +void IDevice::setPeripheralDescriptionFilePath(const QString &path) +{ + d->peripheralDescriptionFilePath = path; +} + QString IDevice::qmlsceneCommand() const { return d->qmlsceneCommand; diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 246d17687c2..b0e0be9a878 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -212,6 +212,9 @@ public: QString debugServerPath() const; void setDebugServerPath(const QString &path); + QString peripheralDescriptionFilePath() const; + void setPeripheralDescriptionFilePath(const QString &path); + QString qmlsceneCommand() const; void setQmlsceneCommand(const QString &path);