From 3310d34cf3c7ea51c44da940f6794972f3aeaf69 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Jun 2019 00:14:18 +0200 Subject: [PATCH] WIP: better tooltips and comments for LVS browser. --- src/db/db/dbNetlistCompare.cc | 6 +- .../laybasic/layIndexedNetlistModel.h | 9 ++ .../laybasic/layNetlistBrowserModel.cc | 94 ++++++++++++++ .../laybasic/layNetlistBrowserModel.h | 1 + .../laybasic/layNetlistBrowserTreeModel.cc | 31 +++++ .../laybasic/layNetlistBrowserTreeModel.h | 1 + .../laybasic/layNetlistCrossReferenceModel.cc | 118 ++++++++++++++++++ .../laybasic/layNetlistCrossReferenceModel.h | 9 ++ 8 files changed, 266 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 29a8cba22..5b6927c6d 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -32,13 +32,13 @@ // TODO: make this a feature? // #define PRINT_DEBUG_NETCOMPARE -namespace db -{ - // Add this define for case insensitive compare // (applies to circuits, device classes) #define COMPARE_CASE_INSENSITIVE +namespace db +{ + // -------------------------------------------------------------------------------------------------------------------- // DeviceCompare definition and implementation diff --git a/src/laybasic/laybasic/layIndexedNetlistModel.h b/src/laybasic/laybasic/layIndexedNetlistModel.h index db11b3d47..129eaf561 100644 --- a/src/laybasic/laybasic/layIndexedNetlistModel.h +++ b/src/laybasic/laybasic/layIndexedNetlistModel.h @@ -98,6 +98,15 @@ public: virtual std::pair pin_from_index (const circuit_pair &circuits, size_t index) const = 0; virtual std::pair subcircuit_from_index (const circuit_pair &circuits, size_t index) const = 0; + virtual std::string top_circuit_status_hint (size_t /*index*/) const { return std::string (); } + virtual std::string circuit_status_hint (size_t /*index*/) const { return std::string (); } + virtual std::string child_circuit_status_hint (const circuit_pair &/*circuits*/, size_t /*index*/) const { return std::string (); } + virtual std::string circuit_pair_status_hint (const std::pair & /*cp*/) const { return std::string (); } + virtual std::string net_status_hint (const circuit_pair &/*circuits*/, size_t /*index*/) const { return std::string (); } + virtual std::string device_status_hint (const circuit_pair &/*circuits*/, size_t /*index*/) const { return std::string (); } + virtual std::string pin_status_hint (const circuit_pair &/*circuits*/, size_t /*index*/) const { return std::string (); } + virtual std::string subcircuit_status_hint (const circuit_pair & /*circuits*/, size_t /*index*/) const { return std::string (); } + virtual size_t circuit_index (const circuit_pair &circuits) const = 0; virtual size_t net_index (const net_pair &nets) const = 0; virtual size_t device_index (const device_pair &devices) const = 0; diff --git a/src/laybasic/laybasic/layNetlistBrowserModel.cc b/src/laybasic/laybasic/layNetlistBrowserModel.cc index 77c77c696..70ea45411 100644 --- a/src/laybasic/laybasic/layNetlistBrowserModel.cc +++ b/src/laybasic/laybasic/layNetlistBrowserModel.cc @@ -664,6 +664,8 @@ NetlistBrowserModel::data (const QModelIndex &index, int role) const return QVariant (icon_for_status (status (index))); } else if (role == Qt::DisplayRole) { return QVariant (text (index)); + } else if (role == Qt::ToolTipRole && index.column () == m_status_column) { + return tooltip (index); } else if (role == Qt::UserRole) { return QVariant (search_text (index)); } else if (role == Qt::FontRole) { @@ -1339,17 +1341,109 @@ NetlistBrowserModel::status (const QModelIndex &index) const size_t index = circuit_subcircuit_index_from_id (id); return mp_indexer->subcircuit_from_index (circuits, index).second; + } else if (is_id_circuit_subcircuit_pin (id)) { + + IndexedNetlistModel::subcircuit_pair subcircuits = subcircuits_from_id (id); + IndexedNetlistModel::circuit_pair circuit_refs = circuit_refs_from_subcircuits (subcircuits); + IndexedNetlistModel::pin_pair pins = pins_from_id (id); + + return mp_indexer->pin_from_index (circuit_refs, mp_indexer->pin_index (pins, circuit_refs)).second; + } else if (is_id_circuit_net (id)) { IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); size_t index = circuit_net_index_from_id (id); return mp_indexer->net_from_index (circuits, index).second; + } else if (is_id_circuit_net_device_terminal (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + IndexedNetlistModel::net_terminal_pair termrefs = net_terminalrefs_from_id (id); + IndexedNetlistModel::device_pair devices = devices_from_termrefs (termrefs); + + return mp_indexer->device_from_index (circuits, mp_indexer->device_index (devices)).second; + + } else if (is_id_circuit_net_subcircuit_pin (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + IndexedNetlistModel::net_subcircuit_pin_pair pinrefs = net_subcircuit_pinrefs_from_id (id); + IndexedNetlistModel::subcircuit_pair subcircuits = subcircuits_from_pinrefs (pinrefs); + + return mp_indexer->subcircuit_from_index (circuits, mp_indexer->subcircuit_index (subcircuits)).second; + } return db::NetlistCrossReference::None; } +QVariant +NetlistBrowserModel::tooltip (const QModelIndex &index) const +{ + void *id = index.internalPointer (); + std::string hint; + + if (is_id_circuit (id)) { + + size_t index = circuit_index_from_id (id); + hint = mp_indexer->circuit_status_hint (index); + + } else if (is_id_circuit_pin (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + size_t index = circuit_pin_index_from_id (id); + hint = mp_indexer->pin_status_hint (circuits, index); + + } else if (is_id_circuit_device (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + size_t index = circuit_device_index_from_id (id); + hint = mp_indexer->device_status_hint (circuits, index); + + } else if (is_id_circuit_subcircuit (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + size_t index = circuit_subcircuit_index_from_id (id); + hint = mp_indexer->subcircuit_status_hint (circuits, index); + + } else if (is_id_circuit_subcircuit_pin (id)) { + + IndexedNetlistModel::subcircuit_pair subcircuits = subcircuits_from_id (id); + IndexedNetlistModel::circuit_pair circuit_refs = circuit_refs_from_subcircuits (subcircuits); + IndexedNetlistModel::pin_pair pins = pins_from_id (id); + + hint = mp_indexer->pin_status_hint (circuit_refs, mp_indexer->pin_index (pins, circuit_refs)); + + } else if (is_id_circuit_net (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + size_t index = circuit_net_index_from_id (id); + hint = mp_indexer->net_status_hint (circuits, index); + + } else if (is_id_circuit_net_device_terminal (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + IndexedNetlistModel::net_terminal_pair termrefs = net_terminalrefs_from_id (id); + IndexedNetlistModel::device_pair devices = devices_from_termrefs (termrefs); + + hint = mp_indexer->device_status_hint (circuits, mp_indexer->device_index (devices)); + + } else if (is_id_circuit_net_subcircuit_pin (id)) { + + IndexedNetlistModel::circuit_pair circuits = circuits_from_id (id); + IndexedNetlistModel::net_subcircuit_pin_pair pinrefs = net_subcircuit_pinrefs_from_id (id); + IndexedNetlistModel::subcircuit_pair subcircuits = subcircuits_from_pinrefs (pinrefs); + + hint = mp_indexer->subcircuit_status_hint (circuits, mp_indexer->subcircuit_index (subcircuits)); + + } + + if (hint.empty ()) { + return QVariant (); + } else { + return QVariant (tl::to_qstring (hint)); + } +} + QString NetlistBrowserModel::search_text (const QModelIndex &index) const { diff --git a/src/laybasic/laybasic/layNetlistBrowserModel.h b/src/laybasic/laybasic/layNetlistBrowserModel.h index 6f8eb805e..1a8deb05a 100644 --- a/src/laybasic/laybasic/layNetlistBrowserModel.h +++ b/src/laybasic/laybasic/layNetlistBrowserModel.h @@ -204,6 +204,7 @@ private: std::pair pins_from_id (void *id) const; std::pair subcircuits_from_id (void *id) const; QString text (const QModelIndex &index) const; + QVariant tooltip (const QModelIndex &index) const; QString search_text (const QModelIndex &index) const; db::NetlistCrossReference::Status status (const QModelIndex &index) const; QIcon icon (const QModelIndex &index) const; diff --git a/src/laybasic/laybasic/layNetlistBrowserTreeModel.cc b/src/laybasic/laybasic/layNetlistBrowserTreeModel.cc index f2e3b9bd9..eb7683936 100644 --- a/src/laybasic/laybasic/layNetlistBrowserTreeModel.cc +++ b/src/laybasic/laybasic/layNetlistBrowserTreeModel.cc @@ -163,6 +163,8 @@ NetlistBrowserTreeModel::data (const QModelIndex &index, int role) const return QVariant (icon_for_status (status (index))); } else if (role == Qt::DisplayRole) { return QVariant (text (index)); + } else if (role == Qt::ToolTipRole && index.column () == m_status_column) { + return tooltip (index); } else if (role == Qt::UserRole) { return QVariant (search_text (index)); } else if (role == Qt::FontRole) { @@ -285,6 +287,35 @@ NetlistBrowserTreeModel::status (const QModelIndex &index) const return cp_status_from_index (index, nprod, nlast, nnlast).second; } +QVariant +NetlistBrowserTreeModel::tooltip (const QModelIndex &index) const +{ + typedef std::pair, db::NetlistCrossReference::Status> cp_status; + size_t nlast = 0; + std::string hint; + + void *id = index.internalPointer (); + tl_assert (id != 0); + + nlast = mp_indexer->top_circuit_count () + 1; + size_t i = pop (id, nlast); + hint = mp_indexer->top_circuit_status_hint (i - 1); + cp_status cps = mp_indexer->top_circuit_from_index (i - 1); + + while (id != 0) { + nlast = mp_indexer->child_circuit_count (cps.first) + 1; + i = pop (id, nlast); + hint = mp_indexer->child_circuit_status_hint (cps.first, i - 1); + cps = mp_indexer->child_circuit_from_index (cps.first, i - 1); + } + + if (! hint.empty ()) { + return QVariant (tl::to_qstring (hint)); + } else { + return QVariant (); + } +} + Qt::ItemFlags NetlistBrowserTreeModel::flags (const QModelIndex & /*index*/) const { diff --git a/src/laybasic/laybasic/layNetlistBrowserTreeModel.h b/src/laybasic/laybasic/layNetlistBrowserTreeModel.h index 3dbf17e5f..fdd413363 100644 --- a/src/laybasic/laybasic/layNetlistBrowserTreeModel.h +++ b/src/laybasic/laybasic/layNetlistBrowserTreeModel.h @@ -83,6 +83,7 @@ private: NetlistBrowserTreeModel &operator= (const NetlistBrowserTreeModel &); QString text (const QModelIndex &index) const; + QVariant tooltip (const QModelIndex &index) const; QString search_text (const QModelIndex &index) const; db::NetlistCrossReference::Status status (const QModelIndex &index) const; std::pair, db::NetlistCrossReference::Status> cp_status_from_index (const QModelIndex &index, size_t &nprod, size_t &nlast, size_t &nnlast) const; diff --git a/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc b/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc index 3ac23075b..518183179 100644 --- a/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc +++ b/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc @@ -431,4 +431,122 @@ size_t NetlistCrossReferenceModel::subcircuit_index (const subcircuit_pair &subc return get_index_of (subcircuits, org_data->subcircuits.begin (), org_data->subcircuits.end (), data->index_of_subcircuits); } +std::string NetlistCrossReferenceModel::circuit_pair_status_hint (const std::pair &cps) const +{ + if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) { + if (! cps.first.first || ! cps.first.second) { + return tl::to_string (tr ("No matching circuit found in the other netlist.\n" + "By default, circuits are identified by their name. " + "A missing circuit probably means there is no circuit in the other netlist with this name.\n" + "If circuits with different names need to be associated, use 'same_circuits' in the " + "LVS script to establish such an association.")); + } else { + return tl::to_string (tr ("Circuits could be paired, but there is a mismatch inside.\n" + "Browse the circuit's component list to identify the mismatching elements.")); + } + } else if (cps.second == db::NetlistCrossReference::Skipped) { + return tl::to_string (tr ("Circuits can only be matched if their child circuits have a known counterpart and a pin-to-pin " + "correspondence could be established for each child circuit.\n" + "This is not the case here. Browse the child circuits to identify the blockers.\n" + "Potential blockers are subcircuits without a corresponding other circuit or circuits " + "where some pins could not be mapped to pins from the corresponding other circuit.")); + } + return std::string (); +} + +std::string NetlistCrossReferenceModel::top_circuit_status_hint (size_t index) const +{ + return circuit_pair_status_hint (top_circuit_from_index (index)); +} + +std::string NetlistCrossReferenceModel::circuit_status_hint (size_t index) const +{ + return circuit_pair_status_hint (circuit_from_index (index)); +} + +std::string NetlistCrossReferenceModel::child_circuit_status_hint (const circuit_pair &circuits, size_t index) const +{ + std::pair cps = child_circuit_from_index (circuits, index); + if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) { + if (!cps.first.first || !cps.first.second) { + return tl::to_string (tr ("No matching subcircuit was found in the other netlist - this is likely because pin assignment " + "could not be derived from the nets connected to the pins.\n" + "Check, if the pins are attached properly. If pins need to be swappable, consider using 'equivalent_pins' " + "in the LVS script.")); + } else { + return tl::to_string (tr ("Two different subcircuits fit here in the same way, but they are not " + "originating from equivalent circuits.\n" + "If the circuits behind the subcircuits are identical, using 'same_circuits' in the LVS script " + "helps to associate them.")); + } + } + return std::string (); +} + +std::string NetlistCrossReferenceModel::net_status_hint (const circuit_pair &circuits, size_t index) const +{ + std::pair cps = net_from_index (circuits, index); + if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) { + return tl::to_string (tr ("Nets don't match. Nets match, if connected subcircuit pins and device terminals match to a counterpart in " + "the other netlist (component-wise and pin/terminal-wise).\n" + "If there already is a net candidate from the other netlist, scan the net members for " + "mismatching items (with errors or warnings) and fix these issues.\n" + "Otherwise, look for the corresponding other net.\n" + "Net items not found in the reference netlist indicate additional connections.\n" + "Net items only found in the reference netlist indicate missing connections.")); + } + return std::string (); +} + +std::string NetlistCrossReferenceModel::device_status_hint (const circuit_pair &circuits, size_t index) const +{ + std::pair cps = device_from_index (circuits, index); + if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) { + if (!cps.first.first || !cps.first.second) { + return tl::to_string (tr ("No matching device was found in the other netlist.\n" + "Devices are identified by the nets they are attached to. Unmatched devices mean that " + "at least one terminal net isn't matched with a corresponding net from the other netlist.\n" + "Make all terminal nets match and the devices will match too.")); + } + } else if (cps.second == db::NetlistCrossReference::MatchWithWarning) { + return tl::to_string (tr ("Topologically matching devices are found here but either the parameters or the " + "device classes don't match.\n" + "If the device class is different but should be considered the same, " + "using 'same_device_class' in the LVS script will solve this issue.")); + } + return std::string (); +} + +std::string NetlistCrossReferenceModel::pin_status_hint (const circuit_pair &circuits, size_t index) const +{ + std::pair cps = pin_from_index (circuits, index); + if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) { + if (!cps.first.first || !cps.first.second) { + return tl::to_string (tr ("No matching pin was found in the other netlist.\n" + "Pins are identified by the nets they are attached to - pins on equivalent nets are also equivalent.\n" + "Making the nets match will make the pins match too.")); + } + } + return std::string (); +} + +std::string NetlistCrossReferenceModel::subcircuit_status_hint (const circuit_pair &circuits, size_t index) const +{ + std::pair cps = subcircuit_from_index (circuits, index); + if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) { + if (!cps.first.first || !cps.first.second) { + return tl::to_string (tr ("No matching subcircuit was found in the other netlist - this is likely because pin assignment " + "could not be derived from the nets connected to the pins.\n" + "Check, if the pins are attached properly. If pins need to be swappable, consider using 'equivalent_pins' " + "in the LVS script.")); + } else { + return tl::to_string (tr ("Two different subcircuits fit here in the same way, but they are not " + "originating from equivalent circuits.\n" + "If the circuits behind the subcircuits are identical, using 'same_circuits' in the LVS script " + "helps to associate them.")); + } + } + return std::string (); +} + } diff --git a/src/laybasic/laybasic/layNetlistCrossReferenceModel.h b/src/laybasic/laybasic/layNetlistCrossReferenceModel.h index 7d3161ad1..a7b0e9c91 100644 --- a/src/laybasic/laybasic/layNetlistCrossReferenceModel.h +++ b/src/laybasic/laybasic/layNetlistCrossReferenceModel.h @@ -71,6 +71,15 @@ public: virtual std::pair pin_from_index (const circuit_pair &circuits, size_t index) const; virtual std::pair subcircuit_from_index (const circuit_pair &circuits, size_t index) const; + virtual std::string top_circuit_status_hint (size_t index) const; + virtual std::string circuit_status_hint (size_t index) const; + virtual std::string child_circuit_status_hint (const circuit_pair &circuits, size_t index) const; + virtual std::string circuit_pair_status_hint (const std::pair &cp) const; + virtual std::string net_status_hint (const circuit_pair &circuits, size_t index) const; + virtual std::string device_status_hint (const circuit_pair &circuits, size_t index) const; + virtual std::string pin_status_hint (const circuit_pair &circuits, size_t index) const; + virtual std::string subcircuit_status_hint (const circuit_pair &circuits, size_t index) const; + virtual size_t circuit_index (const circuit_pair &circuits) const; virtual size_t net_index (const net_pair &nets) const; virtual size_t device_index (const device_pair &devices) const;