From 577edea08bab467a56fb5c9d6dc3a3374a9f0098 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2019 22:38:27 +0200 Subject: [PATCH] Added tree model for netlist hierarchy browser (LVS/L2N) --- src/db/db/dbNetlistCrossReference.cc | 11 + src/db/db/dbNetlistCrossReference.h | 1 + .../laybasic/layIndexedNetlistModel.cc | 26 ++ .../laybasic/layIndexedNetlistModel.h | 10 +- .../laybasic/layNetlistBrowserTreeModel.cc | 325 ++++++++++++++++++ .../laybasic/layNetlistBrowserTreeModel.h | 99 ++++++ .../laybasic/layNetlistCrossReferenceModel.cc | 125 +++++++ .../laybasic/layNetlistCrossReferenceModel.h | 6 + src/laybasic/laybasic/laybasic.pro | 6 +- .../layNetlistBrowserTreeModelTests.cc | 101 ++++++ src/laybasic/unit_tests/unit_tests.pro | 3 +- 11 files changed, 709 insertions(+), 4 deletions(-) create mode 100644 src/laybasic/laybasic/layNetlistBrowserTreeModel.cc create mode 100644 src/laybasic/laybasic/layNetlistBrowserTreeModel.h create mode 100644 src/laybasic/unit_tests/layNetlistBrowserTreeModelTests.cc diff --git a/src/db/db/dbNetlistCrossReference.cc b/src/db/db/dbNetlistCrossReference.cc index 4a0c43166..5b6ad9ee7 100644 --- a/src/db/db/dbNetlistCrossReference.cc +++ b/src/db/db/dbNetlistCrossReference.cc @@ -55,6 +55,17 @@ NetlistCrossReference::per_circuit_data_for (const std::pair::const_iterator i = m_other_circuit.find (circuit); + if (i != m_other_circuit.end ()) { + return i->second; + } else { + return 0; + } +} + const db::Net * NetlistCrossReference::other_net_for (const db::Net *net) const { diff --git a/src/db/db/dbNetlistCrossReference.h b/src/db/db/dbNetlistCrossReference.h index bae832c52..5438187a4 100644 --- a/src/db/db/dbNetlistCrossReference.h +++ b/src/db/db/dbNetlistCrossReference.h @@ -254,6 +254,7 @@ public: return m_circuits.end (); } + const db::Circuit *other_circuit_for (const db::Circuit *circuit) const; const db::Net *other_net_for (const db::Net *net) const; const PerNetData *per_net_data_for (const std::pair &nets) const; diff --git a/src/laybasic/laybasic/layIndexedNetlistModel.cc b/src/laybasic/laybasic/layIndexedNetlistModel.cc index 17aa04166..be78b40f6 100644 --- a/src/laybasic/laybasic/layIndexedNetlistModel.cc +++ b/src/laybasic/laybasic/layIndexedNetlistModel.cc @@ -197,6 +197,12 @@ SingleIndexedNetlistModel::circuit_count () const return mp_netlist->circuit_count (); } +size_t +SingleIndexedNetlistModel::top_circuit_count () const +{ + return mp_netlist->top_circuit_count (); +} + size_t SingleIndexedNetlistModel::net_count (const circuit_pair &circuits) const { @@ -239,6 +245,12 @@ SingleIndexedNetlistModel::subcircuit_count (const circuit_pair &circuits) const return circuits.first->subcircuit_count (); } +size_t +SingleIndexedNetlistModel::child_circuit_count (const circuit_pair &circuits) const +{ + return circuits.first->end_children () - circuits.first->begin_children (); +} + IndexedNetlistModel::circuit_pair SingleIndexedNetlistModel::parent_of (const net_pair &nets) const { @@ -257,6 +269,20 @@ SingleIndexedNetlistModel::parent_of (const subcircuit_pair &subcircuits) const return std::make_pair (subcircuits.first->circuit (), (const db::Circuit *) 0); } +std::pair +SingleIndexedNetlistModel::top_circuit_from_index (size_t index) const +{ + const db::Circuit *c = mp_netlist->begin_top_down () [index]; + return std::make_pair (std::make_pair (c, (const db::Circuit *) 0), db::NetlistCrossReference::None); +} + +std::pair +SingleIndexedNetlistModel::child_circuit_from_index (const circuit_pair &circuits, size_t index) const +{ + const db::Circuit *c = circuits.first->begin_children () [index]; + return std::make_pair (std::make_pair (c, (const db::Circuit *) 0), db::NetlistCrossReference::None); +} + std::pair SingleIndexedNetlistModel::circuit_from_index (size_t index) const { diff --git a/src/laybasic/laybasic/layIndexedNetlistModel.h b/src/laybasic/laybasic/layIndexedNetlistModel.h index 57da1d065..8e347b260 100644 --- a/src/laybasic/laybasic/layIndexedNetlistModel.h +++ b/src/laybasic/laybasic/layIndexedNetlistModel.h @@ -70,6 +70,7 @@ public: typedef std::pair pin_pair; typedef std::pair subcircuit_pair; + virtual size_t top_circuit_count () const = 0; virtual size_t circuit_count () const = 0; virtual size_t net_count (const circuit_pair &circuits) const = 0; virtual size_t net_terminal_count (const net_pair &nets) const = 0; @@ -78,11 +79,14 @@ public: virtual size_t device_count (const circuit_pair &circuits) const = 0; virtual size_t pin_count (const circuit_pair &circuits) const = 0; virtual size_t subcircuit_count (const circuit_pair &circuits) const = 0; + virtual size_t child_circuit_count (const circuit_pair &circuits) const = 0; virtual circuit_pair parent_of (const net_pair &net_pair) const = 0; virtual circuit_pair parent_of (const device_pair &device_pair) const = 0; virtual circuit_pair parent_of (const subcircuit_pair &subcircuit_pair) const = 0; + virtual std::pair top_circuit_from_index (size_t index) const = 0; + virtual std::pair child_circuit_from_index (const circuit_pair &circuits, size_t index) const = 0; virtual std::pair circuit_from_index (size_t index) const = 0; virtual std::pair net_from_index (const circuit_pair &circuits, size_t index) const = 0; virtual const db::Net *second_net_for (const db::Net *first) const = 0; @@ -125,6 +129,7 @@ public: } virtual size_t circuit_count () const; + virtual size_t top_circuit_count () const; virtual size_t net_count (const circuit_pair &circuits) const; virtual size_t net_terminal_count (const net_pair &nets) const; virtual size_t net_subcircuit_pin_count (const net_pair &nets) const; @@ -132,12 +137,15 @@ public: virtual size_t device_count (const circuit_pair &circuits) const; virtual size_t pin_count (const circuit_pair &circuits) const; virtual size_t subcircuit_count (const circuit_pair &circuits) const; + virtual size_t child_circuit_count (const circuit_pair &circuits) const; virtual circuit_pair parent_of (const net_pair &nets) const; virtual circuit_pair parent_of (const device_pair &devices) const; virtual circuit_pair parent_of (const subcircuit_pair &subcircuits) const; - virtual std::pair circuit_from_index (size_t index) const; + virtual std::pair top_circuit_from_index (size_t index) const; + virtual std::pair circuit_from_index (size_t index) const; + virtual std::pair child_circuit_from_index (const circuit_pair &circuits, size_t index) const; virtual std::pair net_from_index (const circuit_pair &circuits, size_t index) const; virtual const db::Net *second_net_for (const db::Net * /*first*/) const; virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const; diff --git a/src/laybasic/laybasic/layNetlistBrowserTreeModel.cc b/src/laybasic/laybasic/layNetlistBrowserTreeModel.cc new file mode 100644 index 000000000..8af3fe397 --- /dev/null +++ b/src/laybasic/laybasic/layNetlistBrowserTreeModel.cc @@ -0,0 +1,325 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "layNetlistBrowserTreeModel.h" +#include "layIndexedNetlistModel.h" +#include "layNetlistCrossReferenceModel.h" + +#include +#include +#include +#include + +namespace lay +{ + +// ---------------------------------------------------------------------------------- +// NetlistBrowserTreeModel implementation + +const std::string var_sep (" ⇔ "); + +static inline size_t pop (void *&idp, size_t n) +{ + size_t id = reinterpret_cast (idp); + size_t i = id % n; + id /= n; + idp = reinterpret_cast (id); + return i; +} + +static QIcon icon_for_circuit () +{ + QIcon icon; + icon.addPixmap (QPixmap (QString::fromUtf8 (":/images/icon_circuit_48.png"))); + icon.addPixmap (QPixmap (QString::fromUtf8 (":/images/icon_circuit_32.png"))); + icon.addPixmap (QPixmap (QString::fromUtf8 (":/images/icon_circuit_24.png"))); + icon.addPixmap (QPixmap (QString::fromUtf8 (":/images/icon_circuit_16.png"))); + return icon; +} + +static QIcon icon_for_status (db::NetlistCrossReference::Status status) +{ + if (status == db::NetlistCrossReference::NoMatch || status == db::NetlistCrossReference::Mismatch) { + return QIcon (":/error2_16.png"); + } else if (status == db::NetlistCrossReference::MatchWithWarning || status == db::NetlistCrossReference::Skipped) { + return QIcon (":/warn_16.png"); + } else { + return QIcon (); + } +} + +template +static std::string str_from_name (const Obj *obj, bool dash_for_empty = false) +{ + if (obj) { + return obj->name (); + } else if (dash_for_empty) { + return std::string ("-"); + } else { + return std::string (); + } +} + +template +static std::string str_from_names (const std::pair &objs, bool is_single) +{ + std::string s = str_from_name (objs.first, ! is_single); + if (! is_single) { + std::string t = str_from_name (objs.second, ! is_single); + if (t != s) { + s += var_sep; + s += t; + } + } + return s; +} + +static std::string combine_search_strings (const std::string &s1, const std::string &s2) +{ + if (s1.empty ()) { + return s2; + } else if (s2.empty ()) { + return s1; + } else { + return s1 + "|" + s2; + } +} + +template +static std::string search_string_from_names (const std::pair &objs) +{ + if (objs.first && objs.second) { + return combine_search_strings (objs.first->name (), objs.second->name ()); + } else if (objs.first) { + return objs.first->name (); + } else if (objs.second) { + return objs.second->name (); + } else { + return std::string (); + } +} + + +NetlistBrowserTreeModel::NetlistBrowserTreeModel (QWidget *parent, db::LayoutToNetlist *l2ndb) + : QAbstractItemModel (parent), mp_l2ndb (l2ndb), mp_lvsdb (0) +{ + mp_indexer.reset (new SingleIndexedNetlistModel (l2ndb->netlist ())); + + m_object_column = 0; + m_status_column = -1; +} + +NetlistBrowserTreeModel::NetlistBrowserTreeModel (QWidget *parent, db::LayoutVsSchematic *lvsdb) + : QAbstractItemModel (parent), mp_l2ndb (0), mp_lvsdb (lvsdb) +{ + mp_indexer.reset (new NetlistCrossReferenceModel (lvsdb->cross_ref ())); + + m_object_column = 0; + m_status_column = 1; +} + +NetlistBrowserTreeModel::~NetlistBrowserTreeModel () +{ + // .. nothing yet .. +} + +int +NetlistBrowserTreeModel::columnCount (const QModelIndex & /*parent*/) const +{ + // Text and status for twoway indexer + return mp_indexer->is_single () ? 1 : 2; +} + +QVariant +NetlistBrowserTreeModel::data (const QModelIndex &index, int role) const +{ + if (! index.isValid ()) { + return QVariant (); + } + + if (role == Qt::DecorationRole && index.column () == m_object_column) { + return QVariant (icon_for_circuit ()); + } else if (role == Qt::DecorationRole && index.column () == m_status_column) { + return QVariant (icon_for_status (status (index))); + } else if (role == Qt::DisplayRole) { + return QVariant (text (index)); + } else if (role == Qt::UserRole) { + return QVariant (search_text (index)); + } else if (role == Qt::FontRole) { + db::NetlistCrossReference::Status st = status (index); + if (st == db::NetlistCrossReference::NoMatch || st == db::NetlistCrossReference::Mismatch || st == db::NetlistCrossReference::Skipped) { + QFont font; + font.setWeight (QFont::Bold); + return QVariant (font); + } + } else if (role == Qt::ForegroundRole) { + db::NetlistCrossReference::Status st = status (index); + if (st == db::NetlistCrossReference::Match || st == db::NetlistCrossReference::MatchWithWarning) { + // taken from marker browser: + return QVariant (QColor (0, 192, 0)); + } + } + return QVariant (); +} + +QString +NetlistBrowserTreeModel::text (const QModelIndex &index) const +{ + std::pair circuits = circuits_from_index (index); + + if (index.column () == m_object_column) { + return tl::to_qstring (str_from_names (circuits, mp_indexer->is_single ())); + } else { + return QString (); + } +} + +QString +NetlistBrowserTreeModel::search_text (const QModelIndex &index) const +{ + std::pair circuits = circuits_from_index (index); + return tl::to_qstring (search_string_from_names (circuits)); +} + +std::pair, db::NetlistCrossReference::Status> +NetlistBrowserTreeModel::cp_status_from_index (const QModelIndex &index, size_t &nprod, size_t &nlast) const +{ + typedef std::pair, db::NetlistCrossReference::Status> cp_status; + + void *id = index.internalPointer (); + tl_assert (id != 0); + + nprod = 1; + + nlast = mp_indexer->top_circuit_count () + 1; + size_t i = pop (id, nlast); + nprod *= nlast; + 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); + nprod *= nlast; + cps = mp_indexer->child_circuit_from_index (cps.first, i - 1); + } + + return cps; +} + +std::pair +NetlistBrowserTreeModel::circuits_from_index (const QModelIndex &index) const +{ + size_t nprod = 0, nlast = 0; + return cp_status_from_index (index, nprod, nlast).first; +} + +db::NetlistCrossReference::Status +NetlistBrowserTreeModel::status (const QModelIndex &index) const +{ + size_t nprod = 0, nlast = 0; + return cp_status_from_index (index, nprod, nlast).second; +} + +Qt::ItemFlags +NetlistBrowserTreeModel::flags (const QModelIndex & /*index*/) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +bool +NetlistBrowserTreeModel::hasChildren (const QModelIndex &parent) const +{ + return rowCount (parent) > 0; +} + +QVariant +NetlistBrowserTreeModel::headerData (int section, Qt::Orientation /*orientation*/, int role) const +{ + if (role == Qt::DisplayRole && section == m_object_column) { + if (mp_indexer->is_single ()) { + return tr ("Circuit"); + } else { + return tr ("Circuits"); + } + } else if (role == Qt::DecorationRole && section == m_status_column) { + return QIcon (":/info_16.png"); + } + return QVariant (); +} + +QModelIndex +NetlistBrowserTreeModel::index (int row, int column, const QModelIndex &parent) const +{ + if (! parent.isValid ()) { + + return createIndex (row, column, reinterpret_cast (row + 1)); + + } else { + + size_t nprod = 0, nlast = 0; + cp_status_from_index (parent, nprod, nlast); + + void *id = parent.internalPointer (); + return createIndex (row, column, reinterpret_cast (reinterpret_cast (id) + size_t (row + 1) * nprod)); + + } +} + +QModelIndex +NetlistBrowserTreeModel::parent (const QModelIndex &index) const +{ + if (index.isValid ()) { + + size_t nprod = 0, nlast = 0; + cp_status_from_index (index, nprod, nlast); + + tl_assert (nlast != 0); + + if (nprod > nlast) { + + nprod /= nlast; + + void *id = index.internalPointer (); + size_t ids = reinterpret_cast (id); + tl_assert (ids >= nprod); + return createIndex (int (ids / nprod - 1), index.column (), reinterpret_cast (ids % nprod)); + + } + + } + + return QModelIndex (); +} + +int +NetlistBrowserTreeModel::rowCount (const QModelIndex &parent) const +{ + if (! parent.isValid ()) { + return int (mp_indexer->top_circuit_count ()); + } else { + std::pair circuits = circuits_from_index (parent); + return int (mp_indexer->child_circuit_count (circuits)); + } +} + +} diff --git a/src/laybasic/laybasic/layNetlistBrowserTreeModel.h b/src/laybasic/laybasic/layNetlistBrowserTreeModel.h new file mode 100644 index 000000000..e3a12b21b --- /dev/null +++ b/src/laybasic/laybasic/layNetlistBrowserTreeModel.h @@ -0,0 +1,99 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layNetlistBrowserTreeModel +#define HDR_layNetlistBrowserTreeModel + +#include "layColorPalette.h" +#include "laybasicCommon.h" + +#include "dbLayoutToNetlist.h" +#include "dbLayoutVsSchematic.h" + +#include +#include + +#include +#include + +class QTreeView; + +namespace lay +{ + +class IndexedNetlistModel; + +// ---------------------------------------------------------------------------------- +// NetlistBrowserTreeModel definition + +/** + * @brief The model for the circuit hierarchy tree + */ +class LAYBASIC_PUBLIC NetlistBrowserTreeModel + : public QAbstractItemModel +{ +Q_OBJECT + +public: + NetlistBrowserTreeModel (QWidget *parent, db::LayoutToNetlist *l2ndb); + NetlistBrowserTreeModel (QWidget *parent, db::LayoutVsSchematic *lvsdb); + ~NetlistBrowserTreeModel (); + + virtual int columnCount (const QModelIndex &parent) const; + virtual QVariant data (const QModelIndex &index, int role) const; + virtual Qt::ItemFlags flags (const QModelIndex &index) const; + virtual bool hasChildren (const QModelIndex &parent) const; + virtual QVariant headerData (int section, Qt::Orientation orientation, int role) const; + virtual QModelIndex index (int row, int column, const QModelIndex &parent) const; + virtual QModelIndex parent (const QModelIndex &index) const; + virtual int rowCount (const QModelIndex &parent) const; + + QModelIndex index_from_id (void *id, int column) const; + + int status_column () const + { + return m_status_column; + } + + std::pair circuits_from_index (const QModelIndex &index) const; + +private: + NetlistBrowserTreeModel (const NetlistBrowserTreeModel &); + NetlistBrowserTreeModel &operator= (const NetlistBrowserTreeModel &); + + QString text (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) const; + + db::LayoutToNetlist *mp_l2ndb; + db::LayoutVsSchematic *mp_lvsdb; + std::auto_ptr mp_indexer; + int m_object_column; + int m_status_column; +}; + +} // namespace lay + +#endif + diff --git a/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc b/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc index 94c35d962..d28eb029e 100644 --- a/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc +++ b/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc @@ -26,6 +26,95 @@ namespace lay { +static void build_top_circuit_list (const db::NetlistCrossReference *cross_ref, std::vector &top_level_circuits) +{ + if (! top_level_circuits.empty ()) { + return; + } + + for (db::NetlistCrossReference::circuits_iterator c = cross_ref->begin_circuits(); c != cross_ref->end_circuits (); ++c) { + const db::Circuit *cfirst = c->first; + const db::Circuit *csecond = c->first; + if ((! cfirst || cfirst->begin_refs () == cfirst->end_refs ()) && (! csecond || csecond->begin_refs () == csecond->end_refs ())) { + top_level_circuits.push_back (*c); + } + } +} + +static void build_child_circuit_list (const db::NetlistCrossReference *cross_ref, const NetlistCrossReferenceModel::circuit_pair &cp, std::vector &child_circuits) +{ + const db::NetlistCrossReference::PerCircuitData *data = cross_ref->per_circuit_data_for (cp); + if (! data) { + return; + } + + if (data->status == db::NetlistCrossReference::Skipped) { + + // For skipped circuits there is no subcircuit event list, so we have to create our own + + std::set seen; + + if (cp.first) { + + for (db::Circuit::const_subcircuit_iterator s = cp.first->begin_subcircuits (); s != cp.first->end_subcircuits (); ++s) { + + const db::Circuit *cr = s->circuit_ref (); + if (seen.find (cr) == seen.end ()) { + seen.insert (cr); + const db::Circuit *cro = cross_ref->other_circuit_for (cr); + NetlistCrossReferenceModel::circuit_pair cp (cr, cro); + child_circuits.push_back (cp); + } + + } + + } + + if (cp.second) { + + for (db::Circuit::const_subcircuit_iterator s = cp.second->begin_subcircuits (); s != cp.second->end_subcircuits (); ++s) { + + const db::Circuit *cr = s->circuit_ref (); + if (seen.find (cr) == seen.end ()) { + seen.insert (cr); + const db::Circuit *cro = cross_ref->other_circuit_for (cr); + if (! cro) { + NetlistCrossReferenceModel::circuit_pair cp (cro, cr); + child_circuits.push_back (cp); + } + } + + } + + } + + } else { + + std::set seen; + for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator s = data->subcircuits.begin (); s != data->subcircuits.end (); ++s) { + const db::Circuit *cfirst = s->pair.first ? s->pair.first->circuit_ref () : 0; + const db::Circuit *csecond = s->pair.second ? s->pair.second->circuit_ref () : 0; + NetlistCrossReferenceModel::circuit_pair cp (cfirst, csecond); + if (seen.find (cp) == seen.end ()) { + seen.insert (cp); + child_circuits.push_back (cp); + } + } + + } +} + +static void build_child_circuit_map (const db::NetlistCrossReference *cross_ref, std::map > &child_circuit_map) +{ + if (! child_circuit_map.empty ()) { + return; + } + + for (db::NetlistCrossReference::circuits_iterator c = cross_ref->begin_circuits(); c != cross_ref->end_circuits (); ++c) { + build_child_circuit_list (cross_ref, *c, child_circuit_map [*c]); + } +} + NetlistCrossReferenceModel::NetlistCrossReferenceModel (const db::NetlistCrossReference *cross_ref) : mp_cross_ref (const_cast (cross_ref)) { @@ -37,6 +126,22 @@ size_t NetlistCrossReferenceModel::circuit_count () const return mp_cross_ref.get () ? mp_cross_ref->circuit_count () : 0; } +size_t NetlistCrossReferenceModel::top_circuit_count () const +{ + if (mp_cross_ref.get ()) { + build_top_circuit_list (mp_cross_ref.get (), m_top_level_circuits); + return m_top_level_circuits.size (); + } else { + return 0; + } +} + +size_t NetlistCrossReferenceModel::child_circuit_count (const circuit_pair &circuits) const +{ + build_child_circuit_map (mp_cross_ref.get (), m_child_circuits); + return m_child_circuits [circuits].size (); +} + size_t NetlistCrossReferenceModel::net_count (const circuit_pair &circuits) const { const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); @@ -154,6 +259,26 @@ IndexedNetlistModel::circuit_pair NetlistCrossReferenceModel::parent_of (const I return get_parent_of (subcircuit_pair, mp_cross_ref.get (), m_parents_of_subcircuits); } +std::pair NetlistCrossReferenceModel::top_circuit_from_index (size_t index) const +{ + build_top_circuit_list (mp_cross_ref.get (), m_top_level_circuits); + + IndexedNetlistModel::circuit_pair cp = m_top_level_circuits [index]; + const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (cp); + tl_assert (data != 0); + return std::make_pair (cp, data->status); +} + +std::pair NetlistCrossReferenceModel::child_circuit_from_index (const circuit_pair &circuits, size_t index) const +{ + build_child_circuit_map (mp_cross_ref.get (), m_child_circuits); + + IndexedNetlistModel::circuit_pair cp = m_child_circuits [circuits][index]; + const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (cp); + tl_assert (data != 0); + return std::make_pair (cp, data->status); +} + std::pair NetlistCrossReferenceModel::circuit_from_index (size_t index) const { IndexedNetlistModel::circuit_pair cp = mp_cross_ref->begin_circuits () [index]; diff --git a/src/laybasic/laybasic/layNetlistCrossReferenceModel.h b/src/laybasic/laybasic/layNetlistCrossReferenceModel.h index f474afe8f..f51a8530d 100644 --- a/src/laybasic/laybasic/layNetlistCrossReferenceModel.h +++ b/src/laybasic/laybasic/layNetlistCrossReferenceModel.h @@ -44,6 +44,7 @@ public: virtual bool is_single () const { return false; } virtual size_t circuit_count () const; + virtual size_t top_circuit_count () const; virtual size_t net_count (const circuit_pair &circuits) const; virtual size_t net_terminal_count (const net_pair &nets) const; virtual size_t net_subcircuit_pin_count (const net_pair &nets) const; @@ -51,12 +52,15 @@ public: virtual size_t device_count (const circuit_pair &circuits) const; virtual size_t pin_count (const circuit_pair &circuits) const; virtual size_t subcircuit_count (const circuit_pair &circuits) const; + virtual size_t child_circuit_count (const circuit_pair &circuits) const; virtual circuit_pair parent_of (const net_pair &net_pair) const; virtual circuit_pair parent_of (const device_pair &device_pair) const; virtual circuit_pair parent_of (const subcircuit_pair &subcircuit_pair) const; + virtual std::pair top_circuit_from_index (size_t index) const; virtual std::pair circuit_from_index (size_t index) const; + virtual std::pair child_circuit_from_index (const circuit_pair &circuits, size_t index) const; virtual std::pair net_from_index (const circuit_pair &circuits, size_t index) const; virtual const db::Net *second_net_for (const db::Net *first) const; virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const; @@ -86,6 +90,8 @@ public: mutable std::map m_parents_of_devices; mutable std::map m_parents_of_pins; mutable std::map m_parents_of_subcircuits; + mutable std::map > m_child_circuits; + mutable std::vector m_top_level_circuits; mutable std::map, PerCircuitCacheData> m_per_circuit_data; mutable std::map, size_t> m_index_of_circuits; }; diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 610b9f87a..b285677ca 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -174,7 +174,8 @@ SOURCES = \ layNetExportDialog.cc \ layNetlistBrowserModel.cc \ layIndexedNetlistModel.cc \ - layNetlistCrossReferenceModel.cc + layNetlistCrossReferenceModel.cc \ + layNetlistBrowserTreeModel.cc HEADERS = \ gtf.h \ @@ -269,7 +270,8 @@ HEADERS = \ layNetExportDialog.h \ layNetlistBrowserModel.h \ layIndexedNetlistModel.h \ - layNetlistCrossReferenceModel.h + layNetlistCrossReferenceModel.h \ + layNetlistBrowserTreeModel.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC diff --git a/src/laybasic/unit_tests/layNetlistBrowserTreeModelTests.cc b/src/laybasic/unit_tests/layNetlistBrowserTreeModelTests.cc new file mode 100644 index 000000000..0584f135b --- /dev/null +++ b/src/laybasic/unit_tests/layNetlistBrowserTreeModelTests.cc @@ -0,0 +1,101 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layNetlistBrowserTreeModel.h" +#include "tlUnitTest.h" + +TEST (1) +{ + db::LayoutToNetlist l2n; + l2n.load (tl::testsrc () + "/testdata/lay/l2n_browser.l2n"); + + std::auto_ptr model (new lay::NetlistBrowserTreeModel (0, &l2n)); + + EXPECT_EQ (model->hasChildren (QModelIndex ()), true); + // two circuits + EXPECT_EQ (model->rowCount (QModelIndex ()), 1); + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, QModelIndex ()), Qt::UserRole).toString ()), "RINGO"); + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, QModelIndex ()), Qt::DisplayRole).toString ()), "RINGO"); + EXPECT_EQ (model->parent (model->index (0, 0, QModelIndex ())).isValid (), false); + + QModelIndex ringoIndex = model->index (0, 0, QModelIndex ()); + + EXPECT_EQ (model->hasChildren (ringoIndex), true); + EXPECT_EQ (model->rowCount (ringoIndex), 1); + + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoIndex), Qt::UserRole).toString ()), "INV2"); + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoIndex), Qt::DisplayRole).toString ()), "INV2"); + EXPECT_EQ (model->parent (model->index (0, 0, ringoIndex)).isValid (), true); + EXPECT_EQ (model->parent (model->index (0, 0, ringoIndex)).internalId () == ringoIndex.internalId (), true); + + QModelIndex inv2Index = model->index (0, 0, ringoIndex); + + EXPECT_EQ (model->hasChildren (inv2Index), false); + EXPECT_EQ (model->rowCount (inv2Index), 0); +} + +TEST (2) +{ + db::LayoutVsSchematic lvs; + lvs.load (tl::testsrc () + "/testdata/lay/lvsdb_browser.lvsdb"); + + std::auto_ptr model (new lay::NetlistBrowserTreeModel (0, &lvs)); + + EXPECT_EQ (model->hasChildren (QModelIndex ()), true); + // two top circuits + EXPECT_EQ (model->rowCount (QModelIndex ()), 2); + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, QModelIndex ()), Qt::UserRole).toString ()), "INV2PAIRX"); + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, QModelIndex ()), Qt::DisplayRole).toString ()), "- ⇔ INV2PAIRX"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, QModelIndex ()), Qt::UserRole).toString ()), "RINGO|RINGO"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, QModelIndex ()), Qt::DisplayRole).toString ()), "RINGO"); + EXPECT_EQ (model->parent (model->index (0, 0, QModelIndex ())).isValid (), false); + EXPECT_EQ (model->parent (model->index (1, 0, QModelIndex ())).isValid (), false); + + EXPECT_EQ (model->hasChildren (model->index (0, 0, QModelIndex ())), false); + EXPECT_EQ (model->rowCount (model->index (0, 0, QModelIndex ())), 0); + + QModelIndex ringoIndex = model->index (1, 0, QModelIndex ()); + EXPECT_EQ (model->hasChildren (model->index (1, 0, QModelIndex ())), true); + EXPECT_EQ (model->rowCount (model->index (1, 0, QModelIndex ())), 1); + + EXPECT_EQ (model->parent (model->index (0, 0, ringoIndex)).isValid (), true); + EXPECT_EQ (model->parent (model->index (0, 0, ringoIndex)).internalId () == ringoIndex.internalId (), true); + + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoIndex), Qt::UserRole).toString ()), "INV2PAIR|INV2PAIR"); + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoIndex), Qt::DisplayRole).toString ()), "INV2PAIR"); + + QModelIndex inv2PairIndex = model->index (0, 0, ringoIndex); + EXPECT_EQ (model->hasChildren (model->index (0, 0, ringoIndex)), true); + EXPECT_EQ (model->rowCount (model->index (0, 0, ringoIndex)), 2); + + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairIndex), Qt::UserRole).toString ()), "INV2"); + EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairIndex), Qt::DisplayRole).toString ()), "- ⇔ INV2"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairIndex), Qt::UserRole).toString ()), "INV2"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairIndex), Qt::DisplayRole).toString ()), "INV2 ⇔ -"); + + EXPECT_EQ (model->hasChildren (model->index (0, 0, inv2PairIndex)), false); + EXPECT_EQ (model->rowCount (model->index (0, 0, inv2PairIndex)), 0); + EXPECT_EQ (model->parent (model->index (0, 0, inv2PairIndex)).isValid (), true); + EXPECT_EQ (model->parent (model->index (0, 0, inv2PairIndex)).internalId () == inv2PairIndex.internalId (), true); + EXPECT_EQ (model->parent (model->index (1, 0, inv2PairIndex)).isValid (), true); + EXPECT_EQ (model->parent (model->index (1, 0, inv2PairIndex)).internalId () == inv2PairIndex.internalId (), true); +} diff --git a/src/laybasic/unit_tests/unit_tests.pro b/src/laybasic/unit_tests/unit_tests.pro index d886cd865..a7392b824 100644 --- a/src/laybasic/unit_tests/unit_tests.pro +++ b/src/laybasic/unit_tests/unit_tests.pro @@ -15,7 +15,8 @@ SOURCES = \ layRenderer.cc \ laySnap.cc \ layAbstractMenu.cc \ - layNetlistBrowserModelTests.cc + layNetlistBrowserModelTests.cc \ + layNetlistBrowserTreeModelTests.cc INCLUDEPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic DEPENDPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic