mirror of https://github.com/KLayout/klayout.git
Added tree model for netlist hierarchy browser (LVS/L2N)
This commit is contained in:
parent
23ba97e07b
commit
577edea08b
|
|
@ -55,6 +55,17 @@ NetlistCrossReference::per_circuit_data_for (const std::pair<const db::Circuit *
|
|||
return 0;
|
||||
}
|
||||
|
||||
const db::Circuit *
|
||||
NetlistCrossReference::other_circuit_for (const db::Circuit *circuit) const
|
||||
{
|
||||
std::map<const db::Circuit *, const db::Circuit *>::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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<const db::Net *, const db::Net *> &nets) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<IndexedNetlistModel::circuit_pair, IndexedNetlistModel::Status>
|
||||
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<IndexedNetlistModel::circuit_pair, IndexedNetlistModel::Status>
|
||||
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<IndexedNetlistModel::circuit_pair, IndexedNetlistModel::Status>
|
||||
SingleIndexedNetlistModel::circuit_from_index (size_t index) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public:
|
|||
typedef std::pair<const db::Pin *, const db::Pin *> pin_pair;
|
||||
typedef std::pair<const db::SubCircuit *, const db::SubCircuit *> 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<circuit_pair, Status> top_circuit_from_index (size_t index) const = 0;
|
||||
virtual std::pair<circuit_pair, Status> child_circuit_from_index (const circuit_pair &circuits, size_t index) const = 0;
|
||||
virtual std::pair<circuit_pair, Status> circuit_from_index (size_t index) const = 0;
|
||||
virtual std::pair<net_pair, Status> 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_pair, Status> circuit_from_index (size_t index) const;
|
||||
|
||||
virtual std::pair<circuit_pair, Status> top_circuit_from_index (size_t index) const;
|
||||
virtual std::pair<circuit_pair, Status> circuit_from_index (size_t index) const;
|
||||
virtual std::pair<circuit_pair, Status> child_circuit_from_index (const circuit_pair &circuits, size_t index) const;
|
||||
virtual std::pair<net_pair, Status> 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;
|
||||
|
|
|
|||
|
|
@ -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 <QPainter>
|
||||
#include <QIcon>
|
||||
#include <QWidget>
|
||||
#include <QTreeView>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// NetlistBrowserTreeModel implementation
|
||||
|
||||
const std::string var_sep (" ⇔ ");
|
||||
|
||||
static inline size_t pop (void *&idp, size_t n)
|
||||
{
|
||||
size_t id = reinterpret_cast<size_t> (idp);
|
||||
size_t i = id % n;
|
||||
id /= n;
|
||||
idp = reinterpret_cast<void *> (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 <class Obj>
|
||||
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 <class Obj>
|
||||
static std::string str_from_names (const std::pair<const Obj *, const Obj *> &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 <class Obj>
|
||||
static std::string search_string_from_names (const std::pair<const Obj *, const Obj *> &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<const db::Circuit *, const db::Circuit *> 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<const db::Circuit *, const db::Circuit *> circuits = circuits_from_index (index);
|
||||
return tl::to_qstring (search_string_from_names (circuits));
|
||||
}
|
||||
|
||||
std::pair<std::pair<const db::Circuit *, const db::Circuit *>, db::NetlistCrossReference::Status>
|
||||
NetlistBrowserTreeModel::cp_status_from_index (const QModelIndex &index, size_t &nprod, size_t &nlast) const
|
||||
{
|
||||
typedef std::pair<std::pair<const db::Circuit *, const db::Circuit *>, 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<const db::Circuit *, const db::Circuit *>
|
||||
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<void *> (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<void *> (reinterpret_cast<size_t> (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<size_t> (id);
|
||||
tl_assert (ids >= nprod);
|
||||
return createIndex (int (ids / nprod - 1), index.column (), reinterpret_cast<void *> (ids % nprod));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return QModelIndex ();
|
||||
}
|
||||
|
||||
int
|
||||
NetlistBrowserTreeModel::rowCount (const QModelIndex &parent) const
|
||||
{
|
||||
if (! parent.isValid ()) {
|
||||
return int (mp_indexer->top_circuit_count ());
|
||||
} else {
|
||||
std::pair<const db::Circuit *, const db::Circuit *> circuits = circuits_from_index (parent);
|
||||
return int (mp_indexer->child_circuit_count (circuits));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <QAbstractItemModel>
|
||||
#include <QColor>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
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<const db::Circuit *, const db::Circuit *> 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<std::pair<const db::Circuit *, const db::Circuit *>, 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<IndexedNetlistModel> mp_indexer;
|
||||
int m_object_column;
|
||||
int m_status_column;
|
||||
};
|
||||
|
||||
} // namespace lay
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -26,6 +26,95 @@
|
|||
namespace lay
|
||||
{
|
||||
|
||||
static void build_top_circuit_list (const db::NetlistCrossReference *cross_ref, std::vector<NetlistCrossReferenceModel::circuit_pair> &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<NetlistCrossReferenceModel::circuit_pair> &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<const db::Circuit *> 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<NetlistCrossReferenceModel::circuit_pair> 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<NetlistCrossReferenceModel::circuit_pair, std::vector<NetlistCrossReferenceModel::circuit_pair> > &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<db::NetlistCrossReference *> (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<IndexedNetlistModel::circuit_pair, NetlistCrossReferenceModel::Status> 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<IndexedNetlistModel::circuit_pair, NetlistCrossReferenceModel::Status> 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<IndexedNetlistModel::circuit_pair, NetlistCrossReferenceModel::Status> NetlistCrossReferenceModel::circuit_from_index (size_t index) const
|
||||
{
|
||||
IndexedNetlistModel::circuit_pair cp = mp_cross_ref->begin_circuits () [index];
|
||||
|
|
|
|||
|
|
@ -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<circuit_pair, Status> top_circuit_from_index (size_t index) const;
|
||||
virtual std::pair<circuit_pair, Status> circuit_from_index (size_t index) const;
|
||||
virtual std::pair<circuit_pair, Status> child_circuit_from_index (const circuit_pair &circuits, size_t index) const;
|
||||
virtual std::pair<net_pair, Status> 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<device_pair, circuit_pair> m_parents_of_devices;
|
||||
mutable std::map<pin_pair, circuit_pair> m_parents_of_pins;
|
||||
mutable std::map<subcircuit_pair, circuit_pair> m_parents_of_subcircuits;
|
||||
mutable std::map<circuit_pair, std::vector<circuit_pair> > m_child_circuits;
|
||||
mutable std::vector<circuit_pair> m_top_level_circuits;
|
||||
mutable std::map<std::pair<const db::Circuit *, const db::Circuit *>, PerCircuitCacheData> m_per_circuit_data;
|
||||
mutable std::map<std::pair<const db::Circuit *, const db::Circuit *>, size_t> m_index_of_circuits;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<lay::NetlistBrowserTreeModel> 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<lay::NetlistBrowserTreeModel> 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue