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 <hjk@qt.io>
This commit is contained in:
Denis Shienkov
2019-07-28 23:32:33 +03:00
parent 25a3c93cfe
commit 239c82bfb0
11 changed files with 1318 additions and 28 deletions

View File

@@ -30,6 +30,7 @@
#include "gdbserverprovider.h"
#include "gdbserverproviderchooser.h"
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <QFormLayout>
@@ -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<BareMetalDevice>(device());
QTC_ASSERT(dev, return);
dev->setPeripheralDescriptionFilePath(m_peripheralDescriptionFileChooser->path());
}
void BareMetalDeviceConfigurationWidget::updateDeviceFromUi()
{
gdbServerProviderChanged();

View File

@@ -28,6 +28,8 @@
#include <projectexplorer/devicesupport/idevicewidget.h>
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

View File

@@ -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

View File

@@ -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<BaseTreeView> m_watchersView;
QPointer<WatchTreeView> m_inspectorView;
QPointer<BaseTreeView> m_registerView;
QPointer<BaseTreeView> m_peripheralRegisterView;
QPointer<BaseTreeView> m_modulesView;
QPointer<BaseTreeView> m_sourceFilesView;
QPointer<BaseTreeView> m_stackView;
@@ -503,6 +509,7 @@ public:
QPointer<QWidget> m_watchersWindow;
QPointer<QWidget> m_inspectorWindow;
QPointer<QWidget> m_registerWindow;
QPointer<QWidget> m_peripheralRegisterWindow;
QPointer<QWidget> m_modulesWindow;
QPointer<QWidget> m_sourceFilesWindow;
QPointer<QWidget> 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()
{
}

View File

@@ -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<Core::IOptionsPage*> *) 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;
};

View File

@@ -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<quint64> 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()

View File

@@ -29,6 +29,7 @@
#include <debugger/breakhandler.h>
#include <debugger/registerhandler.h>
#include <debugger/peripheralregisterhandler.h>
#include <debugger/watchhandler.h>
#include <debugger/watchutils.h>
#include <debugger/debuggeritem.h>
@@ -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<int, Register> m_registers; // Map GDB register numbers to indices

View File

@@ -0,0 +1,956 @@
/****************************************************************************
**
** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
** 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 <utils/basetreeview.h>
#include <utils/savedaction.h>
#include <QItemDelegate>
#include <QLineEdit>
#include <QMenu>
#include <QPainter>
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<PeripheralRegisterFieldItem>
{
public:
explicit PeripheralRegisterFieldItem(
DebuggerEngine *engine, const PeripheralRegisterGroup &group,
PeripheralRegister &reg, 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 &reg, 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<PeripheralRegisterItem>
{
public:
explicit PeripheralRegisterItem(
DebuggerEngine *engine,
const PeripheralRegisterGroup &group,
PeripheralRegister &reg);
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 &reg)
: 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<QLineEdit *>(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<QLineEdit *>(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<quint64> 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<QAbstractItemDelegate *>(
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<ItemViewEvent>();
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<PeripheralRegisterLevel>(
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<PeripheralRegisterFieldLevel>(
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<QAction *>(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 &reg : 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

View File

@@ -0,0 +1,185 @@
/****************************************************************************
**
** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
** 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 <utils/treemodel.h>
#include <QHash>
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<PeripheralRegisterField> 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<PeripheralRegister> registers;
};
// PeripheralRegisterGroups
using PeripheralRegisterGroups = QVector<PeripheralRegisterGroup>;
// PeripheralRegisterItem's
enum { PeripheralRegisterLevel = 1, PeripheralRegisterFieldLevel = 2 };
class PeripheralRegisterFieldItem;
class PeripheralRegisterItem;
using PeripheralRegisterRootItem = Utils::TypedTreeItem<PeripheralRegisterItem>;
using PeripheralRegisterModel = Utils::TreeModel<PeripheralRegisterRootItem,
PeripheralRegisterItem,
PeripheralRegisterFieldItem>;
// 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<quint64> 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<quint64, PeripheralRegisterItem *> m_activeRegisters;
DebuggerEngine * const m_engine;
};
} // namespace Internal
} // namespace Debugger

View File

@@ -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<Utils::Icon> 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;

View File

@@ -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);