diff --git a/utils/regtools/qeditor/backend.cpp b/utils/regtools/qeditor/backend.cpp index 75c504a6f6..fa107ec26c 100644 --- a/utils/regtools/qeditor/backend.cpp +++ b/utils/regtools/qeditor/backend.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "backend.h" /** @@ -99,9 +100,38 @@ bool FileIoBackend::Reload() else if(ok) m_map[key] = val; } + + m_readonly = !QFileInfo(file).isWritable(); + m_dirty = false; return true; } +bool FileIoBackend::WriteRegister(const QString& name, soc_word_t value) +{ + m_dirty = true; + m_map[name] = value; + return true; +} + +bool FileIoBackend::Commit() +{ + if(!m_dirty) + return true; + QFile file(m_filename); + if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + return false; + QTextStream out(&file); + out << "HW = " << m_soc << "\n"; + QMapIterator< QString, soc_word_t > it(m_map); + while(it.hasNext()) + { + it.next(); + out << it.key() << " = " << it.value() << "\n"; + } + out.flush(); + return file.flush(); +} + #ifdef HAVE_HWSTUB /** * HWStubDevice @@ -195,6 +225,14 @@ bool HWStubDevice::ReadMem(soc_addr_t addr, size_t length, void *buffer) return ret >= 0 && (size_t)ret == length; } +bool HWStubDevice::WriteMem(soc_addr_t addr, size_t length, void *buffer) +{ + if(!m_hwdev) + return false; + int ret = hwstub_rw_mem(m_hwdev, 0, addr, buffer, length); + return ret >= 0 && (size_t)ret == length; +} + bool HWStubDevice::IsValid() { return m_valid; @@ -243,6 +281,11 @@ bool HWStubIoBackend::ReadRegister(soc_addr_t addr, soc_word_t& value) return m_dev->ReadMem(addr, sizeof(value), &value); } +bool HWStubIoBackend:: WriteRegister(soc_addr_t addr, soc_word_t value) +{ + return m_dev->WriteMem(addr, sizeof(value), &value); +} + bool HWStubIoBackend::Reload() { return true; diff --git a/utils/regtools/qeditor/backend.h b/utils/regtools/qeditor/backend.h index 879b88c42c..72a19b6ec1 100644 --- a/utils/regtools/qeditor/backend.h +++ b/utils/regtools/qeditor/backend.h @@ -32,6 +32,17 @@ public: virtual bool ReadRegister(soc_addr_t addr, soc_word_t& value) = 0; /* reload content (if it makes sense) */ virtual bool Reload() = 0; + /* check whether backend supports writing */ + virtual bool IsReadOnly() = 0; + /* write a register by name or address + * NOTE: even on a read-only backend, a write is allowed be successful as long + * as commit fails */ + virtual bool WriteRegister(const QString& name, soc_word_t value) = 0; + virtual bool WriteRegister(soc_addr_t addr, soc_word_t value) = 0; + /* check whether backend contains uncommitted (ie cached) writes */ + virtual bool IsDirty() = 0; + /* commit all writes */ + virtual bool Commit() = 0; }; class DummyIoBackend : public IoBackend @@ -47,8 +58,17 @@ public: virtual bool ReadRegister(soc_addr_t addr, soc_word_t& value) { (void) addr; (void) value; return false; } virtual bool Reload() { return false; } + virtual bool IsReadOnly() { return true; } + virtual bool WriteRegister(const QString& name, soc_word_t value) + { (void) name; (void) value; return false; } + virtual bool WriteRegister(soc_addr_t addr, soc_word_t value) + { (void) addr; (void) value; return false; } + virtual bool IsDirty() { return false; } + virtual bool Commit() { return false; } }; +/** NOTE the File backend makes a difference between writes and commits: + * a write will *never* touch the underlying file unless it was committed. */ class FileIoBackend : public IoBackend { Q_OBJECT @@ -61,10 +81,18 @@ public: virtual bool ReadRegister(soc_addr_t addr, soc_word_t& value) { (void) addr; (void) value; return false; } virtual bool Reload(); + virtual bool IsReadOnly() { return m_readonly; } + virtual bool WriteRegister(const QString& name, soc_word_t value); + virtual bool WriteRegister(soc_addr_t addr, soc_word_t value) + { (void) addr; (void) value; return false; } + virtual bool IsDirty() { return m_dirty; } + virtual bool Commit(); protected: QString m_filename; QString m_soc; + bool m_readonly; + bool m_dirty; QMap< QString, soc_word_t > m_map; }; @@ -85,6 +113,7 @@ public: inline struct hwstub_stmp_desc_t GetSTMPInfo() { return m_hwdev_stmp; } /* Calls below require the device to be opened */ bool ReadMem(soc_addr_t addr, size_t length, void *buffer); + bool WriteMem(soc_addr_t addr, size_t length, void *buffer); protected: bool Probe(); @@ -98,6 +127,7 @@ protected: struct hwstub_stmp_desc_t m_hwdev_stmp; }; +/** NOTE the HWStub backend is never dirty: all writes are immediately committed */ class HWStubIoBackend : public IoBackend { Q_OBJECT @@ -111,6 +141,12 @@ public: { (void) name; (void) value; return false; } virtual bool ReadRegister(soc_addr_t addr, soc_word_t& value); virtual bool Reload(); + virtual bool IsReadOnly() { return false; } + virtual bool WriteRegister(const QString& name, soc_word_t value) + { (void) name; (void) value; return false; } + virtual bool WriteRegister(soc_addr_t addr, soc_word_t value); + virtual bool IsDirty() { return false; } + virtual bool Commit() { return true; } protected: QString m_soc; diff --git a/utils/regtools/qeditor/regtab.cpp b/utils/regtools/qeditor/regtab.cpp index e4adecf203..1e9846ef33 100644 --- a/utils/regtools/qeditor/regtab.cpp +++ b/utils/regtools/qeditor/regtab.cpp @@ -17,17 +17,100 @@ #include "backend.h" #include "analyser.h" -RegTreeItem::RegTreeItem(const QString& string, int type) - :QTreeWidgetItem(QStringList(string), type) +SocFieldValidator::SocFieldValidator(QObject *parent) + :QValidator(parent) +{ + m_field.first_bit = 0; + m_field.last_bit = 31; +} + +SocFieldValidator::SocFieldValidator(const soc_reg_field_t& field, QObject *parent) + :QValidator(parent), m_field(field) { } -void RegTreeItem::SetPath(int dev_idx, int dev_addr_idx, int reg_idx, int reg_addr_idx) +void SocFieldValidator::fixup(QString& input) const { - m_dev_idx = dev_idx; - m_dev_addr_idx = dev_addr_idx; - m_reg_idx = reg_idx; - m_reg_addr_idx = reg_addr_idx; + input = input.trimmed(); +} + +QValidator::State SocFieldValidator::validate(QString& input, int& pos) const +{ + (void) pos; + soc_word_t val; + State state = parse(input, val); + qDebug() << "validate(" << input << "): " << state; + return state; +} + +QValidator::State SocFieldValidator::parse(const QString& input, soc_word_t& val) const +{ + // the empty string is all alwats intermediate + if(input.size() == 0) + return Intermediate; + // first check named values + State state = Invalid; + foreach(const soc_reg_field_value_t& value, m_field.value) + { + QString name = QString::fromLocal8Bit(value.name.c_str()); + // cannot be a substring if too long or empty + if(input.size() > name.size()) + continue; + // check equal string + if(input == name) + { + state = Acceptable; + val = value.value; + break; + } + // check substring + if(name.startsWith(input)) + state = Intermediate; + } + // early return for exact match + if(state == Acceptable) + return state; + // do a few special cases for convenience + if(input.compare("0x", Qt::CaseInsensitive) == 0 || + input.compare("0b", Qt::CaseInsensitive) == 0) + return Intermediate; + // try by parsing + unsigned basis, pos; + if(input.size() >= 2 && input.startsWith("0x", Qt::CaseInsensitive)) + { + basis = 16; + pos = 2; + } + else if(input.size() >= 2 && input.startsWith("0b", Qt::CaseInsensitive)) + { + basis = 2; + pos = 2; + } + else if(input.size() >= 2 && input.startsWith("0")) + { + basis = 8; + pos = 1; + } + else + { + basis = 10; + pos = 0; + } + bool ok = false; + unsigned long v = input.mid(pos).toULong(&ok, basis); + // if not ok, return result of name parsing + if(!ok) + return state; + // if ok, check if it fits in the number of bits + unsigned nr_bits = m_field.last_bit - m_field.first_bit + 1; + unsigned long max = nr_bits == 32 ? 0xffffffff : (1 << nr_bits) - 1; + if(v <= max) + { + val = v; + return Acceptable; + } + + return state; } RegTab::RegTab(Backend *backend) @@ -167,6 +250,7 @@ void RegTab::OnDataSelChanged(int index) OnDataSocActivated(m_io_backend->GetSocName()); } Settings::Get()->setValue("regtab/loaddatadir", fd->directory().absolutePath()); + SetReadOnlyIndicator(); } #ifdef HAVE_HWSTUB else if(var == DataSelDevice) @@ -189,6 +273,10 @@ void RegTab::OnDataSelChanged(int index) OnDataChanged(); } +void RegTab::SetReadOnlyIndicator() +{ +} + void RegTab::OnDataChanged() { OnRegItemChanged(m_reg_tree->currentItem(), m_reg_tree->currentItem()); @@ -230,6 +318,8 @@ void RegTab::DisplayRegister(const SocRegRef& ref) { delete m_right_content; + bool read_only = m_io_backend->IsReadOnly(); + QVBoxLayout *right_layout = new QVBoxLayout; const soc_dev_addr_t& dev_addr = ref.GetDevAddr(); @@ -286,9 +376,11 @@ void RegTab::DisplayRegister(const SocRegRef& ref) QLabel *raw_val_name = new QLabel; raw_val_name->setText("Raw value:"); QLineEdit *raw_val_edit = new QLineEdit; - raw_val_edit->setReadOnly(true); + raw_val_edit->setReadOnly(read_only); raw_val_edit->setText(QString().sprintf("0x%08x", value)); raw_val_edit->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + raw_val_edit->setValidator(new SocFieldValidator(raw_val_edit)); + connect(raw_val_edit, SIGNAL(returnPressed()), this, SLOT(OnRawRegValueReturnPressed())); raw_val_layout = new QHBoxLayout; raw_val_layout->addStretch(); raw_val_layout->addWidget(raw_val_name); @@ -445,3 +537,12 @@ void RegTab::OnSocChanged(const QString& soc) FillRegTree(); FillAnalyserList(); } + +void RegTab::OnRawRegValueReturnPressed() +{ + QObject *obj = sender(); + QLineEdit *edit = dynamic_cast< QLineEdit* >(obj); + const SocFieldValidator *validator = dynamic_cast< const SocFieldValidator* >(edit->validator()); + soc_word_t val; + QValidator::State state = validator->parse(edit->text(), val); +} diff --git a/utils/regtools/qeditor/regtab.h b/utils/regtools/qeditor/regtab.h index 107e4e3986..72f00a31a2 100644 --- a/utils/regtools/qeditor/regtab.h +++ b/utils/regtools/qeditor/regtab.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "backend.h" #include "settings.h" @@ -52,6 +53,22 @@ private: SocRegRef m_ref; }; +class SocFieldValidator : public QValidator +{ + Q_OBJECT +public: + SocFieldValidator(QObject *parent = 0); + SocFieldValidator(const soc_reg_field_t& field, QObject *parent = 0); + + virtual void fixup(QString& input) const; + virtual State validate(QString& input, int& pos) const; + /* validate and return the interpreted value */ + State parse(const QString& input, soc_word_t& val) const; + +protected: + soc_reg_field_t m_field; +}; + }; class RegTab : public QSplitter @@ -90,6 +107,7 @@ private slots: void OnDevListChanged(); void OnDevChanged(int index); #endif + void SetReadOnlyIndicator(); void OnSocChanged(const QString& text); void OnSocListChanged(); void OnRegItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); @@ -99,6 +117,7 @@ private slots: void OnDataSocActivated(const QString&); void OnAnalyserChanged(QListWidgetItem *current, QListWidgetItem *previous); void OnAnalyserClicked(QListWidgetItem *clicked); + void OnRawRegValueReturnPressed(); }; #endif /* REGTAB_H */ \ No newline at end of file