Added tree model for netlist hierarchy browser (LVS/L2N)

This commit is contained in:
Matthias Koefferlein 2019-06-01 22:38:27 +02:00
parent 23ba97e07b
commit 577edea08b
11 changed files with 709 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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