Merge pull request #613 from KLayout/issue-588

Issue 588 (Netlist browser enhancements)
This commit is contained in:
Matthias Köfferlein 2020-07-17 23:38:16 -07:00 committed by GitHub
commit 644394eea8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 3994 additions and 2443 deletions

View File

@ -1041,9 +1041,9 @@ LayoutToNetlist::build_nets (const std::vector<const db::Net *> *nets, const db:
}
}
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point)
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point, std::vector<db::SubCircuit *> *sc_path_out, db::Circuit *initial_circuit)
{
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point);
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point, sc_path_out, initial_circuit);
}
size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::NetShape> &test_cluster, std::vector<db::InstElement> &rev_inst_path)
@ -1077,7 +1077,7 @@ size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell
return 0;
}
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point)
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point, std::vector<db::SubCircuit *> *sc_path_out, db::Circuit *initial_circuit)
{
if (! m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
@ -1089,6 +1089,14 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
unsigned int layer = layer_of (of_region);
const db::Cell *top_cell = internal_top_cell ();
if (initial_circuit && internal_layout ()->is_valid_cell_index (initial_circuit->cell_index ())) {
top_cell = &internal_layout ()->cell (initial_circuit->cell_index ());
}
if (! top_cell) {
return 0;
}
// Prepare a test cluster
db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1));
db::GenericRepository sr;
@ -1097,7 +1105,7 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
std::vector<db::InstElement> inst_path;
size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, inst_path);
size_t cluster_id = search_net (db::ICplxTrans (), top_cell, test_cluster, inst_path);
if (cluster_id > 0) {
// search_net delivers the path in reverse order
@ -1105,7 +1113,7 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
std::vector<db::cell_index_type> cell_indexes;
cell_indexes.reserve (inst_path.size () + 1);
cell_indexes.push_back (internal_top_cell ()->cell_index ());
cell_indexes.push_back (top_cell->cell_index ());
for (std::vector<db::InstElement>::const_iterator i = inst_path.begin (); i != inst_path.end (); ++i) {
cell_indexes.push_back (i->inst_ptr.cell_index ());
}
@ -1142,38 +1150,57 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
}
std::vector<db::SubCircuit *> sc_path;
db::Net *topmost_net = net;
// follow the path up in the net hierarchy using the transformation and the upper cell index as the
// guide line
while (! inst_path.empty () && net->pin_count () > 0) {
while (circuit && ! inst_path.empty ()) {
cell_indexes.pop_back ();
const db::Pin *pin = circuit->pin_by_id (net->begin_pins ()->pin_id ());
tl_assert (pin != 0);
const db::Pin *pin = 0;
if (net && net->pin_count () > 0) {
pin = circuit->pin_by_id (net->begin_pins ()->pin_id ());
tl_assert (pin != 0);
}
db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv;
// try to find a parent circuit which connects to this net
db::Circuit *upper_circuit = 0;
db::SubCircuit *subcircuit = 0;
db::Net *upper_net = 0;
for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++r) {
for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_circuit; ++r) {
if (r->trans ().equal (dtrans) && r->circuit () && r->circuit ()->cell_index () == cell_indexes.back ()) {
upper_net = r->net_for_pin (pin->id ());
upper_circuit = r->circuit ();
subcircuit = r.operator-> ();
if (pin) {
upper_net = subcircuit->net_for_pin (pin->id ());
}
upper_circuit = subcircuit->circuit ();
}
}
net = upper_net;
if (upper_net) {
circuit = upper_circuit;
net = upper_net;
inst_path.pop_back ();
topmost_net = upper_net;
} else {
break;
sc_path.push_back (subcircuit);
}
circuit = upper_circuit;
inst_path.pop_back ();
}
return net;
if (sc_path_out) {
std::reverse (sc_path.begin (), sc_path.end ());
*sc_path_out = sc_path;
}
return topmost_net;
} else {
return 0;

View File

@ -709,8 +709,11 @@ public:
*
* This variant accepts a micrometer-unit location. The location is given in the
* coordinate space of the initial cell.
*
* The subcircuit path leading to the topmost net is stored in *sc_path_out if this
* pointer is non-null.
*/
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point);
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point, std::vector<SubCircuit *> *sc_path_out = 0, Circuit *initial_circuit = 0);
/**
* @brief Finds the net by probing a specific location on the given layer
@ -718,7 +721,7 @@ public:
* This variant accepts a database-unit location. The location is given in the
* coordinate space of the initial cell.
*/
db::Net *probe_net (const db::Region &of_region, const db::Point &point);
db::Net *probe_net (const db::Region &of_region, const db::Point &point, std::vector<SubCircuit *> *sc_path_out = 0, Circuit *initial_circuit = 0);
/**
* @brief Runs an antenna check on the extracted clusters

View File

@ -55,6 +55,39 @@ NetlistCrossReference::per_circuit_data_for (const std::pair<const db::Circuit *
return 0;
}
const db::Pin *
NetlistCrossReference::other_pin_for (const db::Pin *pin) const
{
std::map<const db::Pin *, const db::Pin *>::const_iterator i = m_other_pin.find (pin);
if (i != m_other_pin.end ()) {
return i->second;
} else {
return 0;
}
}
const db::Device *
NetlistCrossReference::other_device_for (const db::Device *device) const
{
std::map<const db::Device *, const db::Device *>::const_iterator i = m_other_device.find (device);
if (i != m_other_device.end ()) {
return i->second;
} else {
return 0;
}
}
const db::SubCircuit *
NetlistCrossReference::other_subcircuit_for (const db::SubCircuit *subcircuit) const
{
std::map<const db::SubCircuit *, const db::SubCircuit *>::const_iterator i = m_other_subcircuit.find (subcircuit);
if (i != m_other_subcircuit.end ()) {
return i->second;
} else {
return 0;
}
}
const db::Circuit *
NetlistCrossReference::other_circuit_for (const db::Circuit *circuit) const
{
@ -568,6 +601,16 @@ NetlistCrossReference::build_subcircuit_pin_refs (const std::pair<const db::Net
}
// Fallback for swappable pins: match based on the subcircuit alone
if (! pb) {
std::map<std::pair<const db::SubCircuit *, size_t>, const db::NetSubcircuitPinRef *>::iterator b = s2t_b.lower_bound (std::make_pair (sb, 0));
if (b != s2t_b.end () && b->first.first == sb) {
pb = b->second;
// remove the entry so we won't find it again
s2t_b.erase (b);
}
}
}
data.subcircuit_pins.push_back (std::make_pair (a->second, pb));

View File

@ -256,6 +256,9 @@ public:
return m_circuits.end ();
}
const db::Pin *other_pin_for (const db::Pin *pin) const;
const db::Device *other_device_for (const db::Device *device) const;
const db::SubCircuit *other_subcircuit_for (const db::SubCircuit *subcircuit) const;
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

@ -119,6 +119,17 @@ const Net *SubCircuit::net_for_pin (size_t pin_id) const
return 0;
}
const NetSubcircuitPinRef *SubCircuit::netref_for_pin (size_t pin_id) const
{
if (pin_id < m_pin_refs.size ()) {
Net::subcircuit_pin_iterator p = m_pin_refs [pin_id];
if (p != Net::subcircuit_pin_iterator ()) {
return p.operator-> ();
}
}
return 0;
}
void SubCircuit::connect_pin (size_t pin_id, Net *net)
{
if (net_for_pin (pin_id) == net) {

View File

@ -171,6 +171,21 @@ public:
return const_cast<Net *> (((const SubCircuit *) this)->net_for_pin (pin_id));
}
/**
* @brief Gets the net attached to a specific pin as a subcircuit pin ref object
* Returns 0 if no net is attached.
*/
const NetSubcircuitPinRef *netref_for_pin (size_t pin_id) const;
/**
* @brief Gets the net attached to a specific pin as a subcircuit pin ref object (non-const version)
* Returns 0 if no net is attached.
*/
NetSubcircuitPinRef *netref_for_pin (size_t pin_id)
{
return const_cast<NetSubcircuitPinRef *> (((const SubCircuit *) this)->netref_for_pin (pin_id));
}
/**
* @brief Connects the given pin to the given net
* If the net is 0 the pin is disconnected.

View File

@ -543,7 +543,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"),
"@brief Like \\build_all_nets, but with the ability to select some nets."
) +
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"),
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &, std::vector<db::SubCircuit *> *, db::Circuit *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector<db::SubCircuit *> *) 0, "nil"), gsi::arg ("initial_circuit", (db::Circuit *) 0, "nil"),
"@brief Finds the net by probing a specific location on the given layer\n"
"\n"
"This method will find a net looking at the given layer at the specific position.\n"
@ -551,20 +551,30 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"in the specified location. The function will report the topmost net from far above the\n"
"hierarchy of circuits as possible.\n"
"\n"
"If \\initial_circuit is given, the probing will start from this circuit and from the "
"cell this circuit represents. By default, the probing will start from the top circuit.\n"
"\n"
"If no net is found at all, 0 is returned.\n"
"\n"
"It is recommended to use \\probe on the netlist right after extraction.\n"
"It is recommended to use \\probe_net on the netlist right after extraction.\n"
"Optimization functions such as \\Netlist#purge will remove parts of the net which means\n"
"shape to net probing may no longer work for these nets.\n"
"\n"
"If non-null and an array, 'sc_path_out' will receive a list of \\SubCircuits objects which lead to the "
"net from the top circuit of the database.\n"
"\n"
"This variant accepts a micrometer-unit location. The location is given in the\n"
"coordinate space of the initial cell.\n"
"\n"
"The \\sc_path_out and \\initial_circuit parameters have been added in version 0.27.\n"
) +
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"),
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &, std::vector<db::SubCircuit *> *, db::Circuit *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector<db::SubCircuit *> *) 0, "nil"), gsi::arg ("initial_circuit", (db::Circuit *) 0, "nil"),
"@brief Finds the net by probing a specific location on the given layer\n"
"See the description of the other \\probe_net variant.\n"
"This variant accepts a database-unit location. The location is given in the\n"
"coordinate space of the initial cell.\n"
"\n"
"The \\sc_path_out and \\initial_circuit parameters have been added in version 0.27.\n"
) +
gsi::method ("write|write_l2n", &db::LayoutToNetlist::save, gsi::arg ("path"), gsi::arg ("short_format", false),
"@brief Writes the extracted netlist to a file.\n"

View File

@ -466,6 +466,21 @@ Class<db::SubCircuit> decl_dbSubCircuit (decl_dbNetlistObject, "db", "SubCircuit
gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin1, gsi::arg ("pin"),
"@brief Disconnects the given pin from any net.\n"
"This version takes a \\Pin reference instead of a pin ID."
) +
gsi::method ("trans", &db::SubCircuit::trans,
"@brief Gets the physical transformation for the subcircuit.\n"
"\n"
"This property applies to subcircuits derived from a layout. It specifies the "
"placement of the respective cell.\n"
"\n"
"This property has been introduced in version 0.27."
) +
gsi::method ("trans=", &db::SubCircuit::set_trans, gsi::arg ("trans"),
"@brief Sets the physical transformation for the subcircuit.\n"
"\n"
"See \\trans for details about this property.\n"
"\n"
"This property has been introduced in version 0.27."
),
"@brief A subcircuit inside a circuit.\n"
"Circuits may instantiate other circuits as subcircuits similar to cells "

View File

@ -403,6 +403,34 @@ Class<db::NetlistCrossReference> decl_dbNetlistCrossReference (decl_dbNetlistCom
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' net for nets from the 'a' netlist and vice versa."
) +
gsi::method ("other_circuit_for", &db::NetlistCrossReference::other_circuit_for, gsi::arg ("circuit"),
"@brief Gets the matching other circuit for a given primary circuit.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' circuit for circuits from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("other_device_for", &db::NetlistCrossReference::other_device_for, gsi::arg ("device"),
"@brief Gets the matching other device for a given primary device.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' device for devices from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("other_pin_for", &db::NetlistCrossReference::other_pin_for, gsi::arg ("pin"),
"@brief Gets the matching other pin for a given primary pin.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' pin for pins from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("other_subcircuit_for", &db::NetlistCrossReference::other_subcircuit_for, gsi::arg ("subcircuit"),
"@brief Gets the matching other subcircuit for a given primary subcircuit.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' subcircuit for subcircuits from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("clear", &db::NetlistCrossReference::clear,
"@hide\n"
) +

View File

@ -2167,17 +2167,14 @@ TEST(14_Subcircuit2NandMismatchNoSwap)
" pin $4:$4 [Match]\n"
" net IN1:INT [Mismatch]\n"
" pin $0:(null)\n"
" subcircuit_pin (null):$2[$2]\n"
" subcircuit_pin $1[$0]:(null)\n"
" subcircuit_pin $1[$0]:$2[$2]\n"
" subcircuit_pin $2[$0]:$1[$0]\n"
" net IN2:IN2 [Mismatch]\n"
" pin $1:$1\n"
" subcircuit_pin (null):$2[$0]\n"
" subcircuit_pin $1[$1]:(null)\n"
" subcircuit_pin $1[$1]:$2[$0]\n"
" net INT:IN1 [Mismatch]\n"
" pin (null):$0\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$2]:(null)\n"
" subcircuit_pin $1[$2]:$2[$1]\n"
" subcircuit_pin $2[$1]:$1[$1]\n"
" net OUT:OUT [Match]\n"
" pin $2:$2\n"

View File

@ -285,16 +285,6 @@ static DocumentationParser &cls_documentation (const gsi::ClassBase *cls)
}
}
static const std::string &aliased_name (const gsi::ClassBase *cls)
{
const std::string &alias = cls_documentation (cls).alias;
if (alias.empty ()) {
return cls->name ();
} else {
return alias;
}
}
static std::string make_qualified_name (const gsi::ClassBase *cls)
{
std::string qname;
@ -703,9 +693,9 @@ type_to_s (const gsi::ArgType &a, bool linked, bool for_return)
s += "new ";
}
if (linked) {
s += "<a href=\"" + escape_xml (class_doc_url (aliased_name (a.cls ()))) + "\">" + escape_xml (aliased_name (a.cls ())) + "</a>";
s += "<a href=\"" + escape_xml (class_doc_url (make_qualified_name (a.cls ()))) + "\">" + escape_xml (make_qualified_name (a.cls ())) + "</a>";
} else {
s += aliased_name (a.cls ());
s += make_qualified_name (a.cls ());
}
break;
case gsi::T_vector:

View File

@ -161,7 +161,10 @@ void MacroTreeModel::macro_changed ()
void MacroTreeModel::update_data ()
{
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex()) - 1, 0, QModelIndex ()));
int rc = rowCount (QModelIndex());
if (rc > 0) {
emit dataChanged (index (0, 0, QModelIndex ()), index (rc - 1, 0, QModelIndex ()));
}
}
void MacroTreeModel::about_to_change ()

View File

@ -0,0 +1,237 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "gsiSignals.h"
#include "layNetlistBrowserDialog.h"
#include "layLayoutView.h"
namespace tl
{
// disable copy and default constructor for NetlistBrowserDialog
template <> struct type_traits<lay::NetlistBrowserDialog> : public type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
}
namespace gsi
{
static void set_root (lay::NetlistObjectPath *path, db::Circuit *r)
{
path->root = r;
}
static db::Circuit *root (const lay::NetlistObjectPath *path)
{
return const_cast<db::Circuit *> (path->root);
}
static void set_device (lay::NetlistObjectPath *path, db::Device *r)
{
path->device = r;
}
static db::Device *device (const lay::NetlistObjectPath *path)
{
return const_cast<db::Device *> (path->device);
}
static void set_net (lay::NetlistObjectPath *path, db::Net *r)
{
path->net = r;
}
static db::Net *net (const lay::NetlistObjectPath *path)
{
return const_cast<db::Net *> (path->net);
}
static std::vector<db::SubCircuit *> path (const lay::NetlistObjectPath *p)
{
std::vector<db::SubCircuit *> pp;
pp.reserve (p->path.size ());
for (lay::NetlistObjectPath::path_iterator i = p->path.begin (); i != p->path.end (); ++i) {
pp.push_back (const_cast<db::SubCircuit *> (*i));
}
return pp;
}
static void set_path (lay::NetlistObjectPath *p, const std::vector<db::SubCircuit *> &path)
{
p->path = lay::NetlistObjectPath::path_type (path.begin (), path.end ());
}
Class<lay::NetlistObjectPath> decl_NetlistObjectPath ("lay", "NetlistObjectPath",
gsi::method_ext ("root=", &set_root, gsi::arg ("root"),
"@brief Sets the root circuit of the path.\n"
"The root circuit is the circuit from which the path starts.\n"
) +
gsi::method_ext ("root", &root,
"@brief Gets the root circuit of the path.\n"
) +
gsi::method_ext ("path=", &set_path, gsi::arg ("path"),
"@brief Sets the path.\n"
"The path is a list of subcircuits leading from the root to the final object. "
"The final (net, device) object is located in the circuit called by the last subcircuit "
"of the subcircuit chain. If the subcircuit list is empty, the final object is located inside "
"the root object."
) +
gsi::method_ext ("path", &path,
"@brief Gets the path.\n"
) +
gsi::method_ext ("net=", &set_net, gsi::arg ("net"),
"@brief Sets the net the path points to.\n"
"If the path describes the location of a net, this member will indicate it.\n"
"The other way to describe a final object is \\device=. If neither a device nor "
"net is given, the path describes a circuit and how it is referenced from the root."
) +
gsi::method_ext ("net", &net,
"@brief Gets the net the path points to.\n"
) +
gsi::method_ext ("device=", &set_device, gsi::arg ("device"),
"@brief Sets the device the path points to.\n"
"If the path describes the location of a device, this member will indicate it.\n"
"The other way to describe a final object is \\net=. If neither a device nor "
"net is given, the path describes a circuit and how it is referenced from the root."
) +
gsi::method_ext ("device", &device,
"@brief Gets the device the path points to.\n"
) +
gsi::method ("is_null?", &lay::NetlistObjectPath::is_null,
"@brief Returns a value indicating whether the path is an empty one.\n"
),
"@brief An object describing the instantiation of a netlist object.\n"
"This class describes the instantiation of a net or a device or a circuit in terms of "
"a root circuit and a subcircuit chain leading to the indicated object.\n"
"\n"
"See \\net= or \\device= for the indicated object, \\path= for the subcircuit chain.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistObjectPath first (const lay::NetlistObjectsPath *pp)
{
return pp->first ();
}
static lay::NetlistObjectPath second (const lay::NetlistObjectsPath *pp)
{
return pp->second ();
}
Class<lay::NetlistObjectsPath> decl_NetlistObjectsPath ("lay", "NetlistObjectsPath",
gsi::method_ext ("first", &first,
"@brief Gets the first object's path.\n"
"In cases of paired netlists (LVS database), the first path points to the layout netlist object.\n"
"For the single netlist, the first path is the only path supplied."
) +
gsi::method_ext ("second", &second,
"@brief Gets the second object's path.\n"
"In cases of paired netlists (LVS database), the first path points to the schematic netlist object.\n"
"For the single netlist, the scecond path is always a null path."
),
"@brief An object describing the instantiation of a single netlist object or a pair of those.\n"
"This class is basically a pair of netlist object paths (see \\NetlistObjectPath). When derived from a single netlist view, "
"only the first path is valid and will point to the selected object (a net, a device or a circuit). The second path is null.\n"
"\n"
"If the path is derived from a paired netlist view (a LVS report view), the first path corresponds to the object in the layout netlist, "
"the second one to the object in the schematic netlist.\n"
"If the selected object isn't a matched one, either the first or second path may be a null or a partial path without a final net or device object "
"or a partial path.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistObjectPath current_path_first (lay::NetlistBrowserDialog *dialog)
{
return dialog->current_path ().first ();
}
static lay::NetlistObjectPath current_path_second (lay::NetlistBrowserDialog *dialog)
{
return dialog->current_path ().second ();
}
Class<lay::NetlistBrowserDialog> decl_NetlistBrowserDialog ("lay", "NetlistBrowserDialog",
gsi::event ("on_current_db_changed", &lay::NetlistBrowserDialog::current_db_changed_event,
"@brief This event is triggered when the current database is changed.\n"
"The current database can be obtained with \\db."
) +
gsi::event ("on_selection_changed", &lay::NetlistBrowserDialog::selection_changed_event,
"@brief This event is triggered when the selection changed.\n"
"The selection can be obtained with \\current_path_first, \\current_path_second, \\selected_nets, \\selected_devices, \\selected_subcircuits and \\selected_circuits."
) +
gsi::event ("on_probe", &lay::NetlistBrowserDialog::probe_event, gsi::arg ("first_path"), gsi::arg ("second_path"),
"@brief This event is triggered when a net is probed.\n"
"The first path will indicate the location of the probed net in terms of two paths: one describing the instantiation of the "
"net in layout space and one in schematic space. Both objects are \\NetlistObjectPath objects which hold the root circuit, the "
"chain of subcircuits leading to the circuit containing the net and the net itself."
) +
gsi::method ("db", &lay::NetlistBrowserDialog::db,
"@brief Gets the database the browser is connected to.\n"
) +
gsi::method_ext ("current_path_first", &current_path_first,
"@brief Gets the path of the current object on the first (layout in case of LVS database) side.\n"
) +
gsi::method_ext ("current_path_second", &current_path_second,
"@brief Gets the path of the current object on the second (schematic in case of LVS database) side.\n"
) +
gsi::method ("current_path", &lay::NetlistBrowserDialog::current_path,
"@brief Gets the path of the current object as a path pair (combines layout and schematic object paths in case of a LVS database view).\n"
) +
gsi::method ("selected_paths", &lay::NetlistBrowserDialog::selected_paths,
"@brief Gets the nets currently selected objects (paths) in the netlist database browser.\n"
"The result is an array of path pairs. See \\NetlistObjectsPath for details about these pairs."
),
"@brief Represents the netlist browser dialog.\n"
"This dialog is a part of the \\LayoutView class and can be obtained through \\LayoutView#netlist_browser.\n"
"This interface allows to interact with the browser - mainly to get information about state changes.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistBrowserDialog *netlist_browser (lay::LayoutView *lv)
{
return lv->get_plugin<lay::NetlistBrowserDialog> ();
}
// extend lay::LayoutView with the getter for the netlist browser
static
gsi::ClassExt<lay::LayoutView> decl_ext_layout_view (
gsi::method_ext ("netlist_browser", &netlist_browser,
"@brief Gets the netlist browser object for the given layout view\n"
"\n"
"\nThis method has been added in version 0.27.\n"
)
);
}

View File

@ -236,67 +236,73 @@ SingleIndexedNetlistModel::top_circuit_count () const
size_t
SingleIndexedNetlistModel::net_count (const circuit_pair &circuits) const
{
return circuits.first->net_count ();
return circuits.first ? circuits.first->net_count () : 0;
}
size_t
SingleIndexedNetlistModel::net_terminal_count (const net_pair &nets) const
{
return nets.first->terminal_count ();
return nets.first ? nets.first->terminal_count () : 0;
}
size_t
SingleIndexedNetlistModel::net_subcircuit_pin_count (const net_pair &nets) const
{
return nets.first->subcircuit_pin_count ();
return nets.first ? nets.first->subcircuit_pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::net_pin_count (const net_pair &nets) const
{
return nets.first->pin_count ();
return nets.first ? nets.first->pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::device_count (const circuit_pair &circuits) const
{
return circuits.first->device_count ();
return circuits.first ? circuits.first->device_count () : 0;
}
size_t
SingleIndexedNetlistModel::subcircuit_pin_count (const subcircuit_pair &subcircuits) const
{
return subcircuits.first ? subcircuits.first->circuit_ref ()->pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::pin_count (const circuit_pair &circuits) const
{
return circuits.first->pin_count ();
return circuits.first ? circuits.first->pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::subcircuit_count (const circuit_pair &circuits) const
{
return circuits.first->subcircuit_count ();
return circuits.first ? circuits.first->subcircuit_count () : 0;
}
size_t
SingleIndexedNetlistModel::child_circuit_count (const circuit_pair &circuits) const
{
return circuits.first->end_children () - circuits.first->begin_children ();
return circuits.first ? (circuits.first->end_children () - circuits.first->begin_children ()) : 0;
}
IndexedNetlistModel::circuit_pair
SingleIndexedNetlistModel::parent_of (const net_pair &nets) const
{
return std::make_pair (nets.first->circuit (), (const db::Circuit *) 0);
return std::make_pair (nets.first ? nets.first->circuit () : 0, (const db::Circuit *) 0);
}
IndexedNetlistModel::circuit_pair
SingleIndexedNetlistModel::parent_of (const device_pair &devices) const
{
return std::make_pair (devices.first->circuit (), (const db::Circuit *) 0);
return std::make_pair (devices.first ? devices.first->circuit () : 0, (const db::Circuit *) 0);
}
IndexedNetlistModel::circuit_pair
SingleIndexedNetlistModel::parent_of (const subcircuit_pair &subcircuits) const
{
return std::make_pair (subcircuits.first->circuit (), (const db::Circuit *) 0);
return std::make_pair (subcircuits.first ? subcircuits.first->circuit () : 0, (const db::Circuit *) 0);
}
std::pair<IndexedNetlistModel::circuit_pair, IndexedNetlistModel::Status>
@ -346,6 +352,34 @@ SingleIndexedNetlistModel::net_subcircuit_pinref_from_index (const net_pair &net
return attr_by_object_and_index (nets, index, nets.first->begin_subcircuit_pins (), nets.first->end_subcircuit_pins (), none, none, m_subcircuit_pinref_by_net_and_index, sort_by_pin_name<db::NetSubcircuitPinRef> ());
}
IndexedNetlistModel::net_subcircuit_pin_pair
SingleIndexedNetlistModel::subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const
{
if (! subcircuits.first) {
return IndexedNetlistModel::net_subcircuit_pin_pair (0, 0);
}
std::map<subcircuit_pair, std::vector<net_subcircuit_pin_pair> >::iterator i = m_subcircuit_pins_by_index.find (subcircuits);
if (i == m_subcircuit_pins_by_index.end ()) {
i = m_subcircuit_pins_by_index.insert (std::make_pair (subcircuits, std::vector<net_subcircuit_pin_pair> ())).first;
std::vector<net_subcircuit_pin_pair> &refs = i->second;
const db::Circuit *circuit = subcircuits.first->circuit_ref ();
for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) {
const db::NetSubcircuitPinRef *ref = subcircuits.first->netref_for_pin (p->id ());
if (! ref) {
m_synthetic_pinrefs.push_back (db::NetSubcircuitPinRef (const_cast<db::SubCircuit *> (subcircuits.first), p->id ()));
ref = & m_synthetic_pinrefs.back ();
}
refs.push_back (net_subcircuit_pin_pair (ref, 0));
}
}
return index < i->second.size () ? i->second [index] : IndexedNetlistModel::net_subcircuit_pin_pair (0, 0);
}
IndexedNetlistModel::net_terminal_pair
SingleIndexedNetlistModel::net_terminalref_from_index (const net_pair &nets, size_t index) const
{

View File

@ -80,6 +80,7 @@ public:
virtual size_t net_subcircuit_pin_count (const net_pair &nets) const = 0;
virtual size_t net_pin_count (const net_pair &nets) const = 0;
virtual size_t device_count (const circuit_pair &circuits) const = 0;
virtual size_t subcircuit_pin_count (const subcircuit_pair &subcircuits) 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;
@ -95,6 +96,7 @@ public:
virtual const db::Net *second_net_for (const db::Net *first) const = 0;
virtual const db::Circuit *second_circuit_for (const db::Circuit *first) const = 0;
virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const = 0;
virtual net_subcircuit_pin_pair subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const = 0;
virtual net_terminal_pair net_terminalref_from_index (const net_pair &nets, size_t index) const = 0;
virtual net_pin_pair net_pinref_from_index (const net_pair &nets, size_t index) const = 0;
virtual std::pair<device_pair, Status> device_from_index (const circuit_pair &circuits, size_t index) const = 0;
@ -149,6 +151,7 @@ public:
virtual size_t net_pin_count (const net_pair &nets) const;
virtual size_t device_count (const circuit_pair &circuits) const;
virtual size_t pin_count (const circuit_pair &circuits) const;
virtual size_t subcircuit_pin_count (const subcircuit_pair &subcircuits) const;
virtual size_t subcircuit_count (const circuit_pair &circuits) const;
virtual size_t child_circuit_count (const circuit_pair &circuits) const;
@ -163,6 +166,7 @@ public:
virtual const db::Net *second_net_for (const db::Net *first) const;
virtual const db::Circuit *second_circuit_for (const db::Circuit *first) const;
virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const;
virtual net_subcircuit_pin_pair subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const;
virtual net_terminal_pair net_terminalref_from_index (const net_pair &nets, size_t index) const;
virtual net_pin_pair net_pinref_from_index (const net_pair &nets, size_t index) const;
virtual std::pair<device_pair, Status> device_from_index (const circuit_pair &circuits, size_t index) const;
@ -194,6 +198,8 @@ private:
mutable std::map<pin_pair, size_t> m_pin_index_by_object;
mutable std::map<subcircuit_pair, size_t> m_subcircuit_index_by_object;
mutable std::map<device_pair, size_t> m_device_index_by_object;
mutable std::map<subcircuit_pair, std::vector<net_subcircuit_pin_pair> > m_subcircuit_pins_by_index;
mutable std::list<db::NetSubcircuitPinRef> m_synthetic_pinrefs;
};
}

View File

@ -120,6 +120,8 @@ NetlistBrowserDialog::NetlistBrowserDialog (lay::Dispatcher *root, lay::LayoutVi
connect (sticky_cbx, SIGNAL (clicked ()), this, SLOT (sticky_mode_clicked ()));
cellviews_changed ();
browser_page->selection_changed_event.add (this, &NetlistBrowserDialog::selection_changed);
}
NetlistBrowserDialog::~NetlistBrowserDialog ()
@ -127,6 +129,34 @@ NetlistBrowserDialog::~NetlistBrowserDialog ()
tl::Object::detach_from_all_events ();
}
db::LayoutToNetlist *
NetlistBrowserDialog::db ()
{
return browser_page->db ();
}
const lay::NetlistObjectsPath &
NetlistBrowserDialog::current_path () const
{
if (browser_page) {
return browser_page->current_path ();
} else {
static lay::NetlistObjectsPath empty;
return empty;
}
}
const std::vector<lay::NetlistObjectsPath> &
NetlistBrowserDialog::selected_paths () const
{
if (browser_page) {
return browser_page->selected_paths ();
} else {
static std::vector<lay::NetlistObjectsPath> empty;
return empty;
}
}
void
NetlistBrowserDialog::configure_clicked ()
{
@ -254,40 +284,60 @@ NetlistBrowserDialog::probe_net (const db::DPoint &p, bool trace_path)
}
const db::Net *net = 0;
db::Net *net = 0;
db::Circuit *root = 0;
std::vector<db::SubCircuit *> sc_path;
db::LayoutToNetlist *l2ndb = view ()->get_l2ndb (m_l2n_index);
if (l2ndb) {
// determines the corresponding layer inside the database and probe the net from this region and the
// start point.
root = l2ndb->netlist ()->circuit_by_name (cv->layout ().cell_name (cv.cell_index ()));
if (root) {
std::vector<db::Region *> regions;
// determines the corresponding layer inside the database and probe the net from this region and the
// start point.
const db::Connectivity &conn = l2ndb->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::LayerProperties lp = l2ndb->internal_layout ()->get_properties (*layer);
if (! lp.is_null ()) {
db::Region *region = l2ndb->layer_by_index (*layer);
if (lp == cv->layout ().get_properties (start_layer)) {
// a matching original layer is looked up with higher prio
regions.insert (regions.begin (), region);
} else {
regions.push_back (region);
std::vector<db::Region *> regions;
const db::Connectivity &conn = l2ndb->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::LayerProperties lp = l2ndb->internal_layout ()->get_properties (*layer);
if (! lp.is_null ()) {
db::Region *region = l2ndb->layer_by_index (*layer);
if (lp == cv->layout ().get_properties (start_layer)) {
// a matching original layer is looked up with higher prio
regions.insert (regions.begin (), region);
} else {
regions.push_back (region);
}
}
}
}
// probe the net
// probe the net
for (std::vector<db::Region *>::const_iterator r = regions.begin (); r != regions.end () && !net; ++r) {
sc_path.clear ();
net = l2ndb->probe_net (**r, start_point, &sc_path, root);
}
for (std::vector<db::Region *>::const_iterator r = regions.begin (); r != regions.end () && !net; ++r) {
net = l2ndb->probe_net (**r, start_point);
}
}
// select the net if one was found
browser_page->select_net (net);
lay::NetlistObjectPath path;
if (net) {
path.root = root;
path.net = net;
path.path = lay::NetlistObjectPath::path_type (sc_path.begin (), sc_path.end ());
}
browser_page->select_path (path);
// emits the probe event
// NOTE: browser_page->current_path () will hold the paired path with the schematic side being
// expanded.
probe_event (browser_page->current_path ().first (), browser_page->current_path ().second ());
}
void
@ -421,9 +471,15 @@ BEGIN_PROTECTED
tl::log << tl::to_string (QObject::tr ("Loading file: ")) << l2ndb->filename ();
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading")));
browser_page->set_l2ndb (0);
l2ndb->load (l2ndb->filename ());
browser_page->set_l2ndb (l2ndb);
browser_page->set_db (0);
try {
l2ndb->load (l2ndb->filename ());
browser_page->set_db (l2ndb);
current_db_changed_event ();
} catch (...) {
current_db_changed_event ();
throw;
}
}
@ -707,6 +763,8 @@ NetlistBrowserDialog::update_content ()
central_stack->setCurrentIndex (1);
}
bool db_changed = false;
m_saveas_action->setEnabled (l2ndb != 0);
m_export_action->setEnabled (l2ndb != 0);
m_unload_action->setEnabled (l2ndb != 0);
@ -714,7 +772,10 @@ NetlistBrowserDialog::update_content ()
m_reload_action->setEnabled (l2ndb != 0);
browser_page->enable_updates (false); // Avoid building the internal lists several times ...
browser_page->set_l2ndb (l2ndb);
if (browser_page->db () != l2ndb) {
db_changed = true;
browser_page->set_db (l2ndb);
}
browser_page->set_max_shape_count (m_max_shape_count);
browser_page->set_highlight_style (m_marker_color, m_marker_line_width, m_marker_vertex_size, m_marker_halo, m_marker_dither_pattern, m_marker_intensity, m_use_original_colors, m_auto_color_enabled ? &m_auto_colors : 0);
browser_page->set_window (m_window, m_window_dim);
@ -740,6 +801,10 @@ NetlistBrowserDialog::update_content ()
if (l2ndb_cb->currentIndex () != m_l2n_index) {
l2ndb_cb->setCurrentIndex (m_l2n_index);
}
if (db_changed) {
current_db_changed_event ();
}
}
void
@ -751,8 +816,16 @@ NetlistBrowserDialog::deactivated ()
lay::Dispatcher::instance ()->config_set (cfg_l2ndb_window_state, lay::save_dialog_state (this, false /*don't store the section sizes*/).c_str ());
}
browser_page->set_l2ndb (0);
bool db_changed = false;
if (browser_page->db () != 0) {
db_changed = true;
browser_page->set_db (0);
}
browser_page->set_view (0, 0);
if (db_changed) {
current_db_changed_event ();
}
}
void

View File

@ -29,6 +29,7 @@
#include "layNetlistBrowser.h"
#include "layViewObject.h"
#include "layColorPalette.h"
#include "tlEvents.h"
namespace lay
{
@ -46,6 +47,38 @@ public:
void load (int lay_index, int cv_index);
/**
* @brief This event is emitted after the current database changed
*/
tl::Event current_db_changed_event;
/**
* @brief This event is emitted when a shape is probed
* The first path is that of the layout, the second that of the schematic in case of a
* LVS database.
*/
tl::event<lay::NetlistObjectPath, lay::NetlistObjectPath> probe_event;
/**
* @brief Gets the current database
*/
db::LayoutToNetlist *db ();
/**
* @brief Gets the current object's path
*/
const lay::NetlistObjectsPath &current_path () const;
/**
* @brief Gets the selected nets
*/
const std::vector<lay::NetlistObjectsPath> &selected_paths () const;
/**
* @brief An event indicating that the selection has changed
*/
tl::Event selection_changed_event;
private:
// implementation of the lay::Browser interface
virtual void activated ();
@ -64,6 +97,11 @@ private:
void cellview_changed (int index);
void l2ndbs_changed ();
void selection_changed ()
{
selection_changed_event ();
}
public slots:
void cv_index_changed (int);
void l2ndb_index_changed (int);

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,9 @@
#include "dbLayoutToNetlist.h"
#include "dbLayoutVsSchematic.h"
#include "tlList.h"
#include "tlTypeTraits.h"
#include <QAbstractItemModel>
#include <QColor>
@ -88,6 +91,167 @@ private:
// ----------------------------------------------------------------------------------
// NetlistBrowserModel definition
class NetlistBrowserModel;
class NetlistModelItemData;
class RootItemData;
class CircuitItemData;
class CircuitNetItemData;
class CircuitDeviceItemData;
class CircuitSubCircuitItemData;
}
namespace tl {
// disable copying for NetlistModelItemData
template<> struct type_traits<lay::NetlistModelItemData>
{
typedef false_tag has_copy_constructor;
};
}
namespace lay
{
/**
* @brief A base class for the item data object
*/
class NetlistModelItemData
: public tl::list_node<NetlistModelItemData>
{
public:
typedef tl::list<NetlistModelItemData>::iterator iterator;
NetlistModelItemData ();
NetlistModelItemData (NetlistModelItemData *parent);
virtual ~NetlistModelItemData ();
virtual NetlistModelItemData *parent () { return mp_parent; }
virtual QIcon icon (NetlistBrowserModel *model) = 0;
virtual QString text (int column, NetlistBrowserModel *model) = 0;
virtual QString search_text () = 0;
virtual std::string tooltip (NetlistBrowserModel *model) = 0;
virtual db::NetlistCrossReference::Status status (NetlistBrowserModel *model) = 0;
void ensure_children (NetlistBrowserModel *model);
void push_back (NetlistModelItemData *child);
iterator begin () { return m_children.begin (); }
iterator end () { return m_children.end (); }
size_t child_count () { return m_children_per_index.size (); }
size_t index () { return m_index; }
NetlistModelItemData *child (size_t n);
virtual std::pair<const db::Circuit *, const db::Circuit *> circuits_of_this ();
std::pair<const db::Circuit *, const db::Circuit *> circuits ();
bool derived_from_circuits (const std::pair<const db::Circuit *, const db::Circuit *> &sp);
virtual std::pair<const db::Device *, const db::Device *> devices_of_this ();
std::pair<const db::Device *, const db::Device *> devices ();
bool derived_from_devices (const std::pair<const db::Device *, const db::Device *> &sp);
virtual std::pair<const db::Pin *, const db::Pin *> pins_of_this ();
std::pair<const db::Pin *, const db::Pin *> pins ();
bool derived_from_pins (const std::pair<const db::Pin *, const db::Pin *> &sp);
virtual std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuits_of_this ();
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuits ();
bool derived_from_subcircuits (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sp);
virtual std::pair<const db::Net *, const db::Net *> nets_of_this ();
std::pair<const db::Net *, const db::Net *> nets ();
bool derived_from_nets (const std::pair<const db::Net *, const db::Net *> &np);
private:
NetlistModelItemData *mp_parent;
tl::list<NetlistModelItemData> m_children;
std::vector<NetlistModelItemData *> m_children_per_index;
bool m_children_made;
size_t m_index;
void set_index (size_t index) { m_index = index; }
virtual void do_ensure_children (NetlistBrowserModel *model) = 0;
};
/**
* @brief An object describing the instantiation path of a net, a device or a (sub)circuit pair
*
* This object applies to pairs of these objects. A class providing a path for a single
* object is NetlistObjectPath
*/
struct LAYBASIC_PUBLIC NetlistObjectPath
{
typedef std::list<const db::SubCircuit *> path_type;
typedef path_type::const_iterator path_iterator;
NetlistObjectPath () : root (0), net (0), device (0) { }
bool is_null () const
{
return ! root;
}
bool operator== (const NetlistObjectPath &other) const
{
return root == other.root && path == other.path && net == other.net && device == other.device;
}
bool operator!= (const NetlistObjectPath &other) const
{
return ! operator== (other);
}
const db::Circuit *root;
std::list<const db::SubCircuit *> path;
const db::Net *net;
const db::Device *device;
};
/**
* @brief An object describing the instantiation path of a net, a device or a (sub)circuit pair
*
* This object applies to pairs of these objects. A class providing a path for a single
* object is NetlistObjectPath
*/
struct LAYBASIC_PUBLIC NetlistObjectsPath
{
typedef std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> > path_type;
typedef path_type::const_iterator path_iterator;
NetlistObjectsPath () { }
bool is_null () const
{
return ! root.first && ! root.second;
}
static NetlistObjectsPath from_first(const NetlistObjectPath &p);
static NetlistObjectsPath from_second (const NetlistObjectPath &p);
NetlistObjectPath first () const;
NetlistObjectPath second () const;
bool operator== (const NetlistObjectsPath &other) const
{
return root == other.root && path == other.path && net == other.net && device == other.device;
}
bool operator!= (const NetlistObjectsPath &other) const
{
return ! operator== (other);
}
std::pair<const db::Circuit *, const db::Circuit *> root;
std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> > path;
std::pair<const db::Net *, const db::Net *> net;
std::pair<const db::Device *, const db::Device *> device;
};
/**
* @brief The NetlistBrowserModel
*
@ -125,31 +289,70 @@ public:
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::Net *, const db::Net *> net_from_index (const QModelIndex &index) const;
QModelIndex index_from_net (const std::pair<const db::Net *, const db::Net *> &net) const;
QModelIndex index_from_net (const db::Net *net) const;
std::pair<const db::Circuit *, const db::Circuit *> circuit_from_index (const QModelIndex &index) const;
QModelIndex index_from_circuit (const std::pair<const db::Circuit *, const db::Circuit *> &circuit) const;
QModelIndex index_from_circuit (const db::Circuit *circuit) const;
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuit_from_index (const QModelIndex &index) const;
std::pair<const db::Device *, const db::Device *> device_from_index (const QModelIndex &index) const;
bool is_circuit_index (const QModelIndex &index) const
int object_column () const
{
return is_id_circuit (index.internalPointer ());
return m_object_column;
}
int first_column () const
{
return m_first_column;
}
int second_column () const
{
return m_second_column;
}
IndexedNetlistModel *indexer ()
{
return mp_indexer.get ();
}
std::pair<const db::Net *, const db::Net *> net_from_index (const QModelIndex &index, bool include_parents = true) const;
QModelIndex index_from_net (const std::pair<const db::Net *, const db::Net *> &net) const;
QModelIndex index_from_net (const db::Net *net) const;
std::pair<const db::Circuit *, const db::Circuit *> circuit_from_index (const QModelIndex &index, bool include_parents = true) const;
QModelIndex index_from_circuit (const std::pair<const db::Circuit *, const db::Circuit *> &circuit) const;
QModelIndex index_from_circuit (const db::Circuit *circuit) const;
QModelIndex index_from_subcircuit (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &subcircuits) const;
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuit_from_index (const QModelIndex &index, bool include_parents = true) const;
std::pair<const db::Device *, const db::Device *> device_from_index (const QModelIndex &index, bool include_parents = true) const;
void set_item_visibility (QTreeView *view, bool show_all, bool with_warnings);
QString make_link_to (const std::pair<const db::Net *, const db::Net *> &nets, int column = 0) const;
QString make_link_to (const std::pair<const db::Device *, const db::Device *> &devices, int column = 0) const;
QString make_link_to (const std::pair<const db::Pin *, const db::Pin *> &pins, const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sub_circuits, int column = 0) const;
bool is_valid_net_pair (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_nets (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_connection (const std::pair<const db::Net *, const db::Net *> &net) const;
QModelIndex index_from_url (const QString &url) const;
NetlistObjectsPath path_from_index (const QModelIndex &index) const;
NetlistObjectPath spath_from_index (const QModelIndex &index) const
{
return path_from_index (index).first ();
}
QModelIndex index_from_path (const NetlistObjectsPath &path);
QModelIndex index_from_path (const NetlistObjectPath &path)
{
return index_from_path (NetlistObjectsPath::from_first (path));
}
private slots:
void colors_changed ();
@ -157,73 +360,18 @@ private:
NetlistBrowserModel (const NetlistBrowserModel &);
NetlistBrowserModel &operator= (const NetlistBrowserModel &);
void *make_id_circuit (size_t circuit_index) const;
void *make_id_circuit_pin (size_t circuit_index, size_t pin_index) const;
void *make_id_circuit_pin_net (size_t circuit_index, size_t pin_index, size_t net_index) const;
void *make_id_circuit_net (size_t circuit_index, size_t net_index) const;
void *make_id_circuit_net_device_terminal (size_t circuit_index, size_t net_index, size_t terminal_ref_index) const;
void *make_id_circuit_net_device_terminal_others (size_t circuit_index, size_t net_index, size_t terminal_ref_index, size_t other_index) const;
void *make_id_circuit_net_pin (size_t circuit_index, size_t net_index, size_t pin_index) const;
void *make_id_circuit_net_subcircuit_pin (size_t circuit_index, size_t net_index, size_t pin_ref_index) const;
void *make_id_circuit_net_subcircuit_pin_others (size_t circuit_index, size_t net_index, size_t pin_ref_index, size_t other_index) const;
void *make_id_circuit_subcircuit (size_t circuit_index, size_t subcircuit_index) const;
void *make_id_circuit_subcircuit_pin (size_t circuit_index, size_t subcircuit_index, size_t pin_index) const;
void *make_id_circuit_device (size_t circuit_index, size_t device_index) const;
void *make_id_circuit_device_terminal (size_t circuit_index, size_t device_index, size_t terminal_index) const;
bool is_id_circuit (void *id) const;
bool is_id_circuit_pin (void *id) const;
bool is_id_circuit_pin_net (void *id) const;
bool is_id_circuit_net (void *id) const;
bool is_id_circuit_net_device_terminal (void *id) const;
bool is_id_circuit_net_device_terminal_others (void *id) const;
bool is_id_circuit_net_pin (void *id) const;
bool is_id_circuit_net_subcircuit_pin (void *id) const;
bool is_id_circuit_net_subcircuit_pin_others (void *id) const;
bool is_id_circuit_subcircuit (void *id) const;
bool is_id_circuit_subcircuit_pin (void *id) const;
bool is_id_circuit_device (void *id) const;
bool is_id_circuit_device_terminal (void *id) const;
size_t circuit_index_from_id (void *id) const;
size_t circuit_pin_index_from_id (void *id) const;
size_t circuit_device_index_from_id (void *id) const;
size_t circuit_device_terminal_index_from_id (void *id) const;
size_t circuit_subcircuit_index_from_id (void *id) const;
size_t circuit_subcircuit_pin_index_from_id (void *id) const;
size_t circuit_net_index_from_id (void *id) const;
size_t circuit_net_pin_index_from_id (void *id) const;
size_t circuit_net_subcircuit_pin_index_from_id (void *id) const;
size_t circuit_net_subcircuit_pin_other_index_from_id (void *id) const;
size_t circuit_net_device_terminal_index_from_id (void *id) const;
size_t circuit_net_device_terminal_other_index_from_id (void *id) const;
std::pair<const db::Circuit *, const db::Circuit *> circuits_from_id (void *id) const;
std::pair<const db::Net *, const db::Net *> nets_from_id (void *id) const;
std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> net_subcircuit_pinrefs_from_id (void *id) const;
std::pair<const db::NetTerminalRef *, const db::NetTerminalRef *> net_terminalrefs_from_id (void *id) const;
std::pair<const db::NetPinRef *, const db::NetPinRef *> net_pinrefs_from_id (void *id) const;
std::pair<const db::Device *, const db::Device *> devices_from_id (void *id) const;
std::pair<const db::Pin *, const db::Pin *> pins_from_id (void *id) const;
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuits_from_id (void *id) const;
QString text (const QModelIndex &index) const;
QVariant tooltip (const QModelIndex &index) const;
QString search_text (const QModelIndex &index) const;
db::NetlistCrossReference::Status status (const QModelIndex &index) const;
QIcon icon (const QModelIndex &index) const;
QString make_link_to (const std::pair<const db::Net *, const db::Net *> &nets, int column = 0) const;
QString make_link_to (const std::pair<const db::Device *, const db::Device *> &devices, int column = 0) const;
QString make_link_to (const std::pair<const db::Pin *, const db::Pin *> &pins, const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sub_circuits, int column = 0) const;
QString build_url (const QModelIndex &index, const std::string &title) const;
std::pair<const db::Netlist *, const db::Netlist *> netlists () const
{
return std::pair<const db::Netlist *, const db::Netlist *> (mp_l2ndb->netlist (), (const db::Netlist *)0);
}
bool is_valid_net_pair (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_nets (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_connection (const std::pair<const db::Net *, const db::Net *> &net) const;
void show_or_hide_items (QTreeView *view, const QModelIndex &parent, bool show_all, bool with_warnings, bool with_children);
db::LayoutToNetlist *mp_l2ndb;
@ -236,6 +384,9 @@ private:
int m_status_column;
int m_first_column;
int m_second_column;
std::auto_ptr<NetlistModelItemData> mp_root;
RootItemData *root () const;
};
} // namespace lay

View File

@ -69,7 +69,7 @@ inline const db::Circuit *deref_circuit (const db::Circuit *obj)
}
template <class Obj>
static db::ICplxTrans
static db::DCplxTrans
trans_for (const Obj *objs, const db::Layout &ly, const db::Cell &cell, db::ContextCache &cc, const db::DCplxTrans &initial = db::DCplxTrans ())
{
db::DCplxTrans t = initial;
@ -89,7 +89,6 @@ trans_for (const Obj *objs, const db::Layout &ly, const db::Cell &cell, db::Cont
}
db::CplxTrans dbu_trans (ly.dbu ());
db::ICplxTrans it = dbu_trans.inverted () * t * dbu_trans;
// The circuit may not be instantiated and still not be the top cell.
// This happens if the subcell does not have connections. In this case
@ -98,11 +97,11 @@ trans_for (const Obj *objs, const db::Layout &ly, const db::Cell &cell, db::Cont
if (circuit && ly.is_valid_cell_index (circuit->cell_index ())) {
std::pair<bool, db::ICplxTrans> tc = cc.find_layout_context (circuit->cell_index (), cell.cell_index ());
if (tc.first) {
it = tc.second * it;
t = dbu_trans * tc.second * dbu_trans.inverted () * t;
}
}
return it;
return t;
}
NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
@ -142,7 +141,8 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
QAction *sep;
directory_tree->addAction (m_show_all_action);
directory_tree->addAction (actionCollapseAll);
directory_tree->addAction (actionExpandAll);
// TODO: this gives a too big tree - confine to single branches?
// directory_tree->addAction (actionExpandAll);
sep = new QAction (directory_tree);
sep->setSeparator (true);
directory_tree->addAction (sep);
@ -306,21 +306,10 @@ NetlistBrowserPage::layer_list_changed (int)
void
NetlistBrowserPage::anchor_clicked (const QString &a)
{
QUrl url (a);
QString ids;
#if QT_VERSION >= 0x050000
ids = QUrlQuery (url.query ()).queryItemValue (QString::fromUtf8 ("id"));
#else
ids = url.queryItemValue (QString::fromUtf8 ("id"));
#endif
if (ids.isEmpty ()) {
return;
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
if (netlist_model) {
navigate_to (netlist_model->index_from_url (a), true);
}
void *id = reinterpret_cast<void *> (ids.toULongLong ());
navigate_to (id, true);
}
void
@ -334,6 +323,7 @@ NetlistBrowserPage::current_tree_index_changed (const QModelIndex &index)
return;
}
// TODO: this could give a path ...
std::pair<const db::Circuit *, const db::Circuit *> circuits = tree_model->circuits_from_index (index);
QModelIndex circuit_index = netlist_model->index_from_circuit (circuits);
@ -355,11 +345,10 @@ NetlistBrowserPage::current_index_changed (const QModelIndex &index)
return;
}
void *id = index.internalPointer ();
add_to_history (id, true);
add_to_history (index, true);
std::pair<const db::Circuit *, const db::Circuit *> circuits = netlist_model->circuit_from_index (index);
QModelIndex circuit_index = tree_model->index_from_circuits (circuits);
NetlistObjectsPath path = netlist_model->path_from_index (index);
QModelIndex circuit_index = tree_model->index_from_netpath (path);
m_signals_enabled = false;
hierarchy_tree->setCurrentIndex (circuit_index);
@ -380,6 +369,18 @@ NetlistBrowserPage::select_net (const db::Net *net)
}
}
void
NetlistBrowserPage::select_path (const lay::NetlistObjectsPath &path)
{
if (path.is_null ()) {
directory_tree->clearSelection ();
} else {
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
tl_assert (model != 0);
directory_tree->setCurrentIndex (model->index_from_path (path));
}
}
std::vector<const db::Net *>
NetlistBrowserPage::selected_nets ()
{
@ -411,7 +412,7 @@ NetlistBrowserPage::selected_circuits ()
QModelIndexList selection = directory_tree->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) {
if (i->column () == 0 && model->is_circuit_index (*i)) {
if (i->column () == 0) {
const db::Circuit *circuit = model->circuit_from_index (*i).first;
if (circuit) {
circuits.push_back (circuit);
@ -467,18 +468,23 @@ NetlistBrowserPage::selected_devices ()
void
NetlistBrowserPage::selection_changed ()
{
std::vector<const db::Net *> nets = selected_nets ();
if (mp_info_dialog) {
mp_info_dialog->set_nets (mp_database.get (), nets);
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
tl_assert (model != 0);
QModelIndexList selected = directory_tree->selectionModel ()->selectedIndexes ();
std::vector<lay::NetlistObjectsPath> selected_paths;
selected_paths.reserve (selected.size ());
for (QModelIndexList::const_iterator i = selected.begin (); i != selected.end (); ++i) {
if (i->column () == 0) {
selected_paths.push_back (model->path_from_index (*i));
}
}
std::vector<const db::Device *> devices = selected_devices ();
QModelIndex current = directory_tree->selectionModel ()->currentIndex ();
highlight (model->path_from_index (current), selected_paths);
std::vector<const db::SubCircuit *> subcircuits = selected_subcircuits ();
std::vector<const db::Circuit *> circuits = selected_circuits ();
highlight (nets, devices, subcircuits, circuits);
selection_changed_event ();
}
void
@ -518,15 +524,8 @@ NetlistBrowserPage::select_color_for_net ()
}
void
NetlistBrowserPage::navigate_to (void *id, bool fwd)
NetlistBrowserPage::navigate_to (const QModelIndex &index, bool fwd)
{
NetlistBrowserTreeModel *tree_model = dynamic_cast<NetlistBrowserTreeModel *> (hierarchy_tree->model ());
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
if (! tree_model || ! netlist_model) {
return;
}
QModelIndex index = netlist_model->index_from_id (id, 0);
if (! index.isValid ()) {
return;
}
@ -536,35 +535,42 @@ NetlistBrowserPage::navigate_to (void *id, bool fwd)
directory_tree->setCurrentIndex (index);
std::pair<const db::Circuit *, const db::Circuit *> circuits = netlist_model->circuit_from_index (index);
QModelIndex circuit_index = tree_model->index_from_circuits (circuits);
NetlistBrowserTreeModel *tree_model = dynamic_cast<NetlistBrowserTreeModel *> (hierarchy_tree->model ());
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
if (! tree_model || ! netlist_model) {
return;
}
lay::NetlistObjectsPath path = netlist_model->path_from_index (index);
QModelIndex circuit_index = tree_model->index_from_netpath (path);
hierarchy_tree->setCurrentIndex (circuit_index);
} catch (...) {
}
m_signals_enabled = true;
add_to_history (id, fwd);
add_to_history (index, fwd);
selection_changed ();
}
void
NetlistBrowserPage::add_to_history (void *id, bool fwd)
NetlistBrowserPage::add_to_history (const QModelIndex &index, bool fwd)
{
if (! fwd) {
if (m_history_ptr > 1) {
--m_history_ptr;
m_history [m_history_ptr - 1] = id;
m_history [m_history_ptr - 1] = index;
}
} else if (m_history_ptr >= m_history.size ()) {
m_history.push_back (id);
m_history.push_back (index);
m_history_ptr = m_history.size ();
} else {
if (m_history [m_history_ptr] != id) {
if (m_history [m_history_ptr] != index) {
m_history.erase (m_history.begin () + m_history_ptr + 1, m_history.end ());
}
m_history [m_history_ptr] = id;
m_history [m_history_ptr] = index;
++m_history_ptr;
}
@ -730,7 +736,7 @@ NetlistBrowserPage::find_button_pressed ()
QModelIndex next = find_next (directory_tree, directory_tree->model (), re, directory_tree->currentIndex ());
if (next.isValid ()) {
navigate_to (next.internalPointer ());
navigate_to (next);
}
}
@ -795,11 +801,12 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
m_signals_enabled = se;
clear_markers ();
highlight (std::vector<const db::Net *> (), std::vector<const db::Device *> (), std::vector<const db::SubCircuit *> (), std::vector<const db::Circuit *> ());
m_cell_context_cache = db::ContextCache (mp_database.get () ? mp_database->internal_layout () : 0);
setup_trees ();
selection_changed_event ();
}
void
@ -886,21 +893,16 @@ NetlistBrowserPage::setup_trees ()
}
void
NetlistBrowserPage::highlight (const std::vector<const db::Net *> &nets, const std::vector<const db::Device *> &devices, const std::vector<const db::SubCircuit *> &subcircuits, const std::vector<const db::Circuit *> &circuits)
NetlistBrowserPage::highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths)
{
if (nets != m_current_nets || devices != m_current_devices || subcircuits != m_current_subcircuits || circuits != m_current_circuits) {
if (current_path != m_current_path && selected_paths != m_selected_paths) {
m_current_nets = nets;
m_current_devices = devices;
m_current_subcircuits = subcircuits;
m_current_circuits = circuits;
m_current_path = current_path;
m_selected_paths = selected_paths;
clear_markers ();
if (! nets.empty () || ! devices.empty () || ! subcircuits.empty () || ! circuits.empty ()) {
adjust_view ();
update_highlights ();
}
adjust_view ();
update_highlights ();
}
}
@ -928,7 +930,8 @@ bbox_for_device_abstract (const db::Layout *layout, const db::DeviceAbstract *de
return db::Box ();
}
return layout->cell (device_abstract->cell_index ()).bbox ().transformed (db::CplxTrans (layout->dbu ()).inverted () * trans * db::CplxTrans (layout->dbu ()));}
return layout->cell (device_abstract->cell_index ()).bbox ().transformed (db::CplxTrans (layout->dbu ()).inverted () * trans * db::CplxTrans (layout->dbu ()));
}
static db::Box
bbox_for_circuit (const db::Layout *layout, const db::Circuit *circuit)
@ -945,12 +948,6 @@ bbox_for_circuit (const db::Layout *layout, const db::Circuit *circuit)
return layout->cell (circuit->cell_index ()).bbox ();
}
static db::Box
bbox_for_subcircuit (const db::Layout *layout, const db::SubCircuit *subcircuit)
{
return bbox_for_circuit (layout, subcircuit->circuit_ref ());
}
void
NetlistBrowserPage::adjust_view ()
{
@ -967,57 +964,80 @@ NetlistBrowserPage::adjust_view ()
return;
}
const db::Layout &original_layout = mp_view->cellview (m_cv_index)->layout ();
const db::Circuit *top_circuit = mp_database->netlist ()->circuit_by_name (original_layout.cell_name (mp_view->cellview (m_cv_index).cell_index ()));
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
const db::Cell *cell = (top_circuit && layout->is_valid_cell_index (top_circuit->cell_index ()) ? &layout->cell (top_circuit->cell_index ()) : mp_database->internal_top_cell ());
if (! layout || ! cell) {
return;
}
db::DBox bbox;
db::Box bbox;
for (std::vector<lay::NetlistObjectsPath>::const_iterator path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) {
for (std::vector<const db::Net *>::const_iterator net = m_current_nets.begin (); net != m_current_nets.end (); ++net) {
const db::Circuit *circuit = path->root.first;
if (! circuit) {
continue;
}
db::ICplxTrans net_trans = trans_for (*net, *layout, *cell, m_cell_context_cache);
db::DCplxTrans trans;
db::cell_index_type cell_index = (*net)->circuit ()->cell_index ();
size_t cluster_id = (*net)->cluster_id ();
trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
const db::Connectivity &conn = mp_database->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = path->path.begin (); p != path->path.end () && circuit; ++p) {
if (p->first) {
circuit = p->first->circuit_ref ();
trans = trans * p->first->trans ();
} else {
circuit = 0;
}
}
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
while (! shapes.at_end ()) {
layer_bbox += shapes->bbox ();
++shapes;
if (! circuit) {
continue;
}
db::Box ebox;
const db::Device *device = path->device.first;
const db::Net *net = path->net.first;
if (device) {
ebox += bbox_for_device_abstract (layout, device->device_abstract (), db::DCplxTrans ());
const std::vector<db::DeviceAbstractRef> &oda = device->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
ebox += bbox_for_device_abstract (layout, a->device_abstract, a->trans);
}
bbox += net_trans * layer_bbox;
} else if (net) {
db::cell_index_type cell_index = net->circuit ()->cell_index ();
size_t cluster_id = net->cluster_id ();
const db::Connectivity &conn = mp_database->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
while (! shapes.at_end ()) {
layer_bbox += shapes->bbox ().transformed (shapes.trans ());
++shapes;
}
ebox += layer_bbox;
}
} else if (circuit) {
ebox += bbox_for_circuit (layout, circuit);
}
}
bbox += trans * db::CplxTrans (layout->dbu ()) * ebox;
for (std::vector<const db::Device *>::const_iterator device = m_current_devices.begin (); device != m_current_devices.end (); ++device) {
db::ICplxTrans trans = trans_for (*device, *layout, *cell, m_cell_context_cache, (*device)->trans ());
bbox += trans * bbox_for_device_abstract (layout, (*device)->device_abstract (), db::DCplxTrans ());
const std::vector<db::DeviceAbstractRef> &oda = (*device)->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
bbox += trans * bbox_for_device_abstract (layout, a->device_abstract, a->trans);
}
}
for (std::vector<const db::SubCircuit *>::const_iterator subcircuit = m_current_subcircuits.begin (); subcircuit != m_current_subcircuits.end (); ++subcircuit) {
bbox += trans_for (*subcircuit, *layout, *cell, m_cell_context_cache, (*subcircuit)->trans ()) * bbox_for_subcircuit (layout, *subcircuit);
}
for (std::vector<const db::Circuit *>::const_iterator circuit = m_current_circuits.begin (); circuit != m_current_circuits.end (); ++circuit) {
bbox += trans_for (*circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ()) * bbox_for_circuit (layout, *circuit);
}
if (! bbox.empty ()) {
@ -1025,9 +1045,8 @@ NetlistBrowserPage::adjust_view ()
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
db::DBox tv_bbox;
db::DBox dbu_bbox = db::CplxTrans (layout->dbu ()) * bbox;
for (std::vector<db::DCplxTrans>::const_iterator t = tv.begin (); t != tv.end (); ++t) {
tv_bbox += *t * dbu_bbox;
tv_bbox += *t * bbox;
}
if (m_window == lay::NetlistBrowserConfig::FitNet) {
@ -1065,12 +1084,10 @@ bool
NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans device_trans = trans_for (device, *layout, *cell, m_cell_context_cache, device->trans ());
QColor color = make_valid_color (m_colorizer.marker_color ());
db::Box device_bbox = bbox_for_device_abstract (layout, device->device_abstract (), db::DCplxTrans ());
db::Box device_bbox = bbox_for_device_abstract (layout, device->device_abstract (), device->trans ());
if (! device_bbox.empty ()) {
if (n_markers == m_max_shape_count) {
@ -1080,7 +1097,7 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (device_bbox, device_trans, tv);
mp_markers.back ()->set (device_bbox, db::ICplxTrans (), tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
@ -1090,7 +1107,7 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
const std::vector<db::DeviceAbstractRef> &oda = device->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
db::Box da_box = bbox_for_device_abstract (layout, a->device_abstract, a->trans);
db::Box da_box = bbox_for_device_abstract (layout, a->device_abstract, device->trans () * a->trans);
if (! da_box.empty ()) {
if (n_markers == m_max_shape_count) {
@ -1100,7 +1117,7 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (da_box, device_trans, tv);
mp_markers.back ()->set (da_box, db::ICplxTrans (), tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
@ -1112,40 +1129,10 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
return false;
}
bool
NetlistBrowserPage::produce_highlights_for_subcircuit (const db::SubCircuit *subcircuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans subcircuit_trans = trans_for (subcircuit, *layout, *cell, m_cell_context_cache, subcircuit->trans ());
QColor color = make_valid_color (m_colorizer.marker_color ());
db::Box circuit_bbox = bbox_for_subcircuit (layout, subcircuit);
if (circuit_bbox.empty ()) {
return false;
}
if (n_markers == m_max_shape_count) {
return true;
}
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (circuit_bbox, subcircuit_trans, tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
return false;
}
bool
NetlistBrowserPage::produce_highlights_for_circuit (const db::Circuit *circuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans circuit_trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
QColor color = make_valid_color (m_colorizer.marker_color ());
db::Box circuit_bbox = bbox_for_circuit (layout, circuit);
@ -1160,7 +1147,7 @@ NetlistBrowserPage::produce_highlights_for_circuit (const db::Circuit *circuit,
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (circuit_bbox, circuit_trans, tv);
mp_markers.back ()->set (circuit_bbox, db::ICplxTrans (), tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
@ -1172,8 +1159,6 @@ bool
NetlistBrowserPage::produce_highlights_for_net (const db::Net *net, size_t &n_markers, const std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> &display_by_lp, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans net_trans = trans_for (net, *layout, *cell, m_cell_context_cache);
db::cell_index_type cell_index = net->circuit ()->cell_index ();
size_t cluster_id = net->cluster_id ();
@ -1199,7 +1184,7 @@ NetlistBrowserPage::produce_highlights_for_net (const db::Net *net, size_t &n_ma
}
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (shapes->polygon_ref (), net_trans * shapes.trans (), tv);
mp_markers.back ()->set (shapes->polygon_ref (), shapes.trans (), tv);
if (net_color.isValid ()) {
@ -1272,14 +1257,14 @@ NetlistBrowserPage::update_highlights ()
}
const db::Layout &original_layout = mp_view->cellview (m_cv_index)->layout ();
const db::Circuit *top_circuit = mp_database->netlist ()->circuit_by_name (original_layout.cell_name (mp_view->cellview (m_cv_index).cell_index ()));
const db::Layout *layout = mp_database->internal_layout ();
if (! layout) {
const db::Cell *cell = (top_circuit && layout->is_valid_cell_index (top_circuit->cell_index ()) ? &layout->cell (top_circuit->cell_index ()) : mp_database->internal_top_cell ());
if (! layout || ! cell) {
return;
}
// a map of display properties vs. layer properties
std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> display_by_lp;
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
if (! lp->has_children () && lp->cellview_index () == int (m_cv_index) && lp->layer_index () >= 0 && (unsigned int) lp->layer_index () < original_layout.layers ()) {
@ -1287,36 +1272,55 @@ NetlistBrowserPage::update_highlights ()
}
}
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
// correct DBU differences between the storage layout and the original layout
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
*t = *t * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
size_t n_markers = 0;
bool not_all_shapes_are_shown = false;
for (std::vector<const db::Net *>::const_iterator net = m_current_nets.begin (); net != m_current_nets.end () && ! not_all_shapes_are_shown; ++net) {
if ((*net)->circuit ()) {
not_all_shapes_are_shown = produce_highlights_for_net (*net, n_markers, display_by_lp, tv);
}
}
for (std::vector<lay::NetlistObjectsPath>::const_iterator path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) {
for (std::vector<const db::Device *>::const_iterator device = m_current_devices.begin (); device != m_current_devices.end () && ! not_all_shapes_are_shown; ++device) {
if ((*device)->circuit ()) {
not_all_shapes_are_shown = produce_highlights_for_device (*device, n_markers, tv);
const db::Circuit *circuit = path->root.first;
if (! circuit) {
continue;
}
}
for (std::vector<const db::SubCircuit *>::const_iterator subcircuit = m_current_subcircuits.begin (); subcircuit != m_current_subcircuits.end () && ! not_all_shapes_are_shown; ++subcircuit) {
if ((*subcircuit)->circuit ()) {
not_all_shapes_are_shown = produce_highlights_for_subcircuit (*subcircuit, n_markers, tv);
// computes the transformation supplied by the path
db::DCplxTrans trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = path->path.begin (); p != path->path.end () && circuit; ++p) {
if (p->first) {
circuit = p->first->circuit_ref ();
trans = trans * p->first->trans ();
} else {
circuit = 0;
}
}
if (! circuit) {
continue;
}
// a map of display properties vs. layer properties
// correct DBU differences between the storage layout and the original layout
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
*t = *t * trans * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
if (path->net.first) {
if (produce_highlights_for_net (path->net.first, n_markers, display_by_lp, tv)) {
not_all_shapes_are_shown = true;
}
} else if (path->device.first) {
if (produce_highlights_for_device (path->device.first, n_markers, tv)) {
not_all_shapes_are_shown = true;
}
} else if (circuit) {
if (produce_highlights_for_circuit (circuit, n_markers, tv)) {
not_all_shapes_are_shown = true;
}
}
}
for (std::vector<const db::Circuit *>::const_iterator circuit = m_current_circuits.begin (); circuit != m_current_circuits.end () && ! not_all_shapes_are_shown; ++circuit) {
not_all_shapes_are_shown = produce_highlights_for_circuit (*circuit, n_markers, tv);
}
if (not_all_shapes_are_shown) {
@ -1327,7 +1331,6 @@ NetlistBrowserPage::update_highlights ()
} else {
info_label->hide ();
}
}
void

View File

@ -33,6 +33,7 @@
#include "dbLayoutUtils.h"
#include "tlObject.h"
#include "tlEvents.h"
#include <QFrame>
@ -90,25 +91,14 @@ public:
/**
* @brief Attaches the page to a L2N DB
*/
void set_l2ndb (db::LayoutToNetlist *database)
{
set_db (database);
}
void set_db (db::LayoutToNetlist *database);
/**
* @brief Attaches the page to a LVS DB
* @brief Gets the database the page is connected to
*/
void set_lvsdb (db::LayoutVsSchematic *database)
db::LayoutToNetlist *db ()
{
set_db (database);
}
/**
* @brief Detaches the page from any DB
*/
void reset_db ()
{
set_db (0);
return mp_database.get ();
}
/**
@ -116,6 +106,22 @@ public:
*/
void select_net (const db::Net *net);
/**
* @brief Selects a netlist object (a circuit, a subcircuit, a net or a device)
*/
void select_path (const lay::NetlistObjectPath &path)
{
select_path (lay::NetlistObjectsPath::from_first (path));
}
/**
* @brief Selects a netlist object (a circuit, a subcircuit, a net or a device)
*
* This variant allows specifying a paired path using either an object from the first,
* the second netlist of both.
*/
void select_path (const lay::NetlistObjectsPath &path);
/**
* @brief Set the window type and window dimensions
*/
@ -162,6 +168,27 @@ public:
*/
void update_highlights ();
/**
* @brief Gets the current object's path
*/
const lay::NetlistObjectsPath &current_path () const
{
return m_current_path;
}
/**
* @brief Gets the selected nets
*/
const std::vector<lay::NetlistObjectsPath> &selected_paths () const
{
return m_selected_paths;
}
/**
* @brief An event indicating that the selection has changed
*/
tl::Event selection_changed_event;
public slots:
void export_all ();
void export_selected ();
@ -201,28 +228,25 @@ private:
unsigned int m_cv_index;
lay::Dispatcher *mp_plugin_root;
tl::weak_ptr<db::LayoutToNetlist> mp_database;
std::vector<void *> m_history;
std::vector<QModelIndex> m_history;
size_t m_history_ptr;
bool m_signals_enabled;
std::vector <lay::Marker *> mp_markers;
bool m_enable_updates;
bool m_update_needed;
std::vector<const db::Net *> m_current_nets;
std::vector<const db::Device *> m_current_devices;
std::vector<const db::SubCircuit *> m_current_subcircuits;
std::vector<const db::Circuit *> m_current_circuits;
lay::NetlistObjectsPath m_current_path;
std::vector<lay::NetlistObjectsPath> m_selected_paths;
lay::NetInfoDialog *mp_info_dialog;
tl::DeferredMethod<NetlistBrowserPage> dm_update_highlights;
tl::DeferredMethod<NetlistBrowserPage> dm_rerun_macro;
db::ContextCache m_cell_context_cache;
void set_db (db::LayoutToNetlist *l2ndb);
void setup_trees ();
void add_to_history (void *id, bool fwd);
void navigate_to (void *id, bool forward = true);
void add_to_history (const QModelIndex &index, bool fwd);
void navigate_to (const QModelIndex &index, bool forward = true);
void adjust_view ();
void clear_markers ();
void highlight (const std::vector<const db::Net *> &nets, const std::vector<const db::Device *> &devices, const std::vector<const db::SubCircuit *> &subcircuits, const std::vector<const db::Circuit *> &circuits);
void highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths);
std::vector<const db::Net *> selected_nets ();
std::vector<const db::Device *> selected_devices ();
std::vector<const db::SubCircuit *> selected_subcircuits ();
@ -232,7 +256,6 @@ private:
QColor make_valid_color (const QColor &color);
bool produce_highlights_for_net(const db::Net *net, size_t &n_markers, const std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> &display_by_lp, const std::vector<db::DCplxTrans> &tv);
bool produce_highlights_for_device (const db::Device *device, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
bool produce_highlights_for_subcircuit (const db::SubCircuit *subcircuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
bool produce_highlights_for_circuit (const db::Circuit *circuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
void configure_marker (lay::Marker *marker, bool with_fill);
void rerun_macro ();

View File

@ -24,6 +24,7 @@
#include "layNetlistBrowserTreeModel.h"
#include "layIndexedNetlistModel.h"
#include "layNetlistCrossReferenceModel.h"
#include "layNetlistBrowserModel.h"
#include <QPainter>
#include <QIcon>
@ -257,6 +258,45 @@ NetlistBrowserTreeModel::build_circuits_to_index (size_t nprod, const std::pair<
}
}
static bool is_compatible (const std::pair<const db::Circuit *, const db::Circuit *> &a, const std::pair<const db::Circuit *, const db::Circuit *> &b)
{
if (a.first && b.first && a.first == b.first) {
return true;
} else if (a.second && b.second && a.second == b.second) {
return true;
} else {
return false;
}
}
QModelIndex
NetlistBrowserTreeModel::index_from_netpath (const NetlistObjectsPath &path) const
{
QModelIndex idx;
idx = index_from_circuits (path.root);
for (NetlistObjectsPath::path_iterator p = path.path.begin (); p != path.path.end () && idx.isValid (); ++p) {
std::pair<const db::Circuit *, const db::Circuit *> sc (p->first ? p->first->circuit_ref () : 0, p->second ? p->second->circuit_ref (): 0);
std::pair<const db::Circuit *, const db::Circuit *> circuit = circuits_from_index (idx);
size_t count = mp_indexer->child_circuit_count (circuit);
for (size_t n = count; n > 0; ) {
--n;
std::pair<const db::Circuit *, const db::Circuit *> cc = mp_indexer->child_circuit_from_index (circuit, n).first;
if (is_compatible (sc, cc)) {
circuit = cc;
idx = index (n, 0, idx);
break;
}
}
}
return idx;
}
QModelIndex
NetlistBrowserTreeModel::index_from_circuits (const std::pair<const db::Circuit *, const db::Circuit *> &circuits) const
{
@ -382,7 +422,7 @@ NetlistBrowserTreeModel::parent (const QModelIndex &index) const
nprod /= nnlast;
return createIndex (int (ids / nprod - 1), index.column (), reinterpret_cast<void *> (ids));
return createIndex (int (ids / nprod - 1), 0, reinterpret_cast<void *> (ids));
}

View File

@ -42,6 +42,7 @@ namespace lay
{
class IndexedNetlistModel;
struct NetlistObjectsPath;
// ----------------------------------------------------------------------------------
// NetlistBrowserTreeModel definition
@ -77,6 +78,7 @@ public:
std::pair<const db::Circuit *, const db::Circuit *> circuits_from_index (const QModelIndex &index) const;
QModelIndex index_from_circuits (const std::pair<const db::Circuit *, const db::Circuit *> &circuits) const;
QModelIndex index_from_netpath (const NetlistObjectsPath &path) const;
private:
NetlistBrowserTreeModel (const NetlistBrowserTreeModel &);

View File

@ -308,6 +308,161 @@ const db::Circuit *NetlistCrossReferenceModel::second_circuit_for (const db::Cir
return mp_cross_ref->other_circuit_for (first);
}
namespace {
// TODO: borrowed from dbNetlistCrossReference.cc
static int string_value_compare (const std::string &a, const std::string &b)
{
return a == b ? 0 : (a < b ? -1 : 1);
}
template <class Obj>
struct by_expanded_name_value_compare
{
int operator() (const Obj &a, const Obj &b) const
{
return string_value_compare (a.expanded_name (), b.expanded_name ());
}
};
template <class Obj> struct net_object_compare;
template <>
struct net_object_compare<db::NetSubcircuitPinRef>
{
int operator() (const db::NetSubcircuitPinRef &a, const db::NetSubcircuitPinRef &b) const
{
int ct = by_expanded_name_value_compare<db::SubCircuit> () (*a.subcircuit (), *b.subcircuit ());
if (ct == 0) {
return by_expanded_name_value_compare<db::Pin> () (*a.pin (), *b.pin ());
} else {
return ct;
}
}
};
template <class Obj, class ValueCompare>
struct two_pointer_compare
{
int operator() (const Obj *a, const Obj *b) const
{
if ((a == 0) != (b == 0)) {
return (a == 0) > (b == 0) ? -1 : 1;
}
if (a != 0) {
return ValueCompare () (*a, *b);
} else {
return 0;
}
}
};
template <class Obj, class ValueCompare>
struct two_pair_compare
{
bool operator() (const std::pair<const Obj *, const Obj *> &a, const std::pair<const Obj *, const Obj *> &b)
{
int ct = two_pointer_compare<Obj, ValueCompare> () (a.first, b.first);
if (ct != 0) {
return ct < 0;
}
return two_pointer_compare<Obj, ValueCompare> () (a.second, b.second) < 0;
}
};
struct SortNetSubCircuitPins
: public two_pair_compare<db::NetSubcircuitPinRef, net_object_compare<db::NetSubcircuitPinRef> >
{
// .. nothing yet ..
};
}
void NetlistCrossReferenceModel::ensure_subcircuit_data_built () const
{
if (! m_per_subcircuit_data.empty ()) {
return;
}
// Build the net to subcircuit ref table
for (db::NetlistCrossReference::circuits_iterator c = mp_cross_ref->begin_circuits (); c != mp_cross_ref->end_circuits (); ++c) {
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (*c);
if (! data) {
continue;
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_type::const_iterator sc = data->subcircuits.begin (); sc != data->subcircuits.end (); ++sc) {
const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sc_pair = sc->pair;
if (sc_pair.first && sc_pair.second) {
PerSubCircuitCacheData &sc_data = m_per_subcircuit_data [sc_pair];
std::multimap<const db::Net *, const db::NetSubcircuitPinRef *> first_net_to_other_netref;
for (size_t i = 0; i < sc_pair.second->circuit_ref ()->pin_count (); ++i) {
const db::NetSubcircuitPinRef *n2 = sc_pair.second->netref_for_pin (i);
if (n2) {
const db::Net *n1 = mp_cross_ref->other_net_for (n2->net ());
if (n1) {
first_net_to_other_netref.insert (std::make_pair (n1, n2));
} else {
sc_data.nets_per_pins.push_back (std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> (0, n2));
}
}
}
for (size_t i = 0; i < sc_pair.first->circuit_ref ()->pin_count (); ++i) {
const db::NetSubcircuitPinRef *n1 = sc_pair.first->netref_for_pin (i);
if (n1) {
const db::NetSubcircuitPinRef *n2 = 0;
std::multimap<const db::Net *, const db::NetSubcircuitPinRef *>::iterator m = first_net_to_other_netref.find (n1->net ());
if (m != first_net_to_other_netref.end () && m->first == n1->net ()) {
n2 = m->second;
first_net_to_other_netref.erase (m);
}
sc_data.nets_per_pins.push_back (std::make_pair (n1, n2));
}
}
std::sort (sc_data.nets_per_pins.begin (), sc_data.nets_per_pins.end (), SortNetSubCircuitPins ());
}
}
}
}
size_t NetlistCrossReferenceModel::subcircuit_pin_count (const subcircuit_pair &subcircuits) const
{
ensure_subcircuit_data_built ();
std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, PerSubCircuitCacheData>::const_iterator sc = m_per_subcircuit_data.find (subcircuits);
if (sc != m_per_subcircuit_data.end ()) {
return sc->second.nets_per_pins.size ();
} else {
return std::max (subcircuits.first ? subcircuits.first->circuit_ref ()->pin_count () : 0, subcircuits.second ? subcircuits.second->circuit_ref ()->pin_count () : 0);
}
}
IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const
{
ensure_subcircuit_data_built ();
std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, PerSubCircuitCacheData>::const_iterator sc = m_per_subcircuit_data.find (subcircuits);
if (sc != m_per_subcircuit_data.end ()) {
if (index < sc->second.nets_per_pins.size ()) {
return sc->second.nets_per_pins [index];
} else {
return IndexedNetlistModel::net_subcircuit_pin_pair (0, 0);
}
} else {
return IndexedNetlistModel::net_subcircuit_pin_pair (subcircuits.first ? subcircuits.first->netref_for_pin (index) : 0, subcircuits.second ? subcircuits.second->netref_for_pin (index) : 0);
}
}
IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const
{
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);

View File

@ -48,6 +48,7 @@ public:
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;
virtual size_t subcircuit_pin_count (const subcircuit_pair &subcircuits) const;
virtual size_t net_pin_count (const net_pair &nets) const;
virtual size_t device_count (const circuit_pair &circuits) const;
virtual size_t pin_count (const circuit_pair &circuits) const;
@ -65,6 +66,7 @@ public:
virtual const db::Net *second_net_for (const db::Net *first) const;
virtual const db::Circuit *second_circuit_for (const db::Circuit *first) const;
virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const;
virtual net_subcircuit_pin_pair subcircuit_pinref_from_index (const subcircuit_pair &nets, size_t index) const;
virtual net_terminal_pair net_terminalref_from_index (const net_pair &nets, size_t index) const;
virtual net_pin_pair net_pinref_from_index (const net_pair &nets, size_t index) const;
virtual std::pair<device_pair, Status> device_from_index (const circuit_pair &circuits, size_t index) const;
@ -95,6 +97,11 @@ public:
std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, size_t> index_of_subcircuits;
};
struct PerSubCircuitCacheData
{
std::vector<std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> > nets_per_pins;
};
tl::weak_ptr<db::NetlistCrossReference> mp_cross_ref;
mutable std::map<net_pair, circuit_pair> m_parents_of_nets;
mutable std::map<device_pair, circuit_pair> m_parents_of_devices;
@ -104,6 +111,9 @@ public:
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;
mutable std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, PerSubCircuitCacheData> m_per_subcircuit_data;
void ensure_subcircuit_data_built () const;
};
}

View File

@ -183,7 +183,8 @@ SOURCES = \
layGenericSyntaxHighlighter.cc \
layDispatcher.cc \
laySelectCellViewForm.cc \
layLayoutStatisticsForm.cc
layLayoutStatisticsForm.cc \
gsiDeclLayNetlistBrowserDialog.cc
HEADERS = \
gtf.h \

View File

@ -44,90 +44,98 @@ TEST (1)
QModelIndex inv2Index = model->index (0, 0, QModelIndex ());
EXPECT_EQ (model->hasChildren (inv2Index), true);
// 4 subnodes
EXPECT_EQ (model->rowCount (inv2Index), 3);
// 5 pins, 5 nets, 0 subcircuits, 4 devices
EXPECT_EQ (model->rowCount (inv2Index), 14);
QModelIndex sn_pins = model->index (0, 0, inv2Index);
QModelIndex sn_nets = model->index (1, 0, inv2Index);
QModelIndex sn_devices = model->index (2, 0, inv2Index);
// Pins
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::UserRole).toString ()), "IN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::DisplayRole).toString ()), "IN");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Index), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2Index), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, inv2Index), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, inv2Index), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "IN|NIN");
EXPECT_EQ (model->parent (model->index (0, 0, sn_pins)) == model->parent (model->index (0, 3, sn_pins)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "IN");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_pins), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, sn_pins), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, sn_pins), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, sn_pins), Qt::DisplayRole).toString ()), "$4");
// Nets
EXPECT_EQ (tl::to_string (model->data (model->index (5, 0, inv2Index), Qt::UserRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 0, inv2Index), Qt::DisplayRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 2, inv2Index), Qt::DisplayRole).toString ()), "NIN (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 0, inv2Index), Qt::DisplayRole).toString ()), "NOUT");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, inv2Index), Qt::DisplayRole).toString ()), "NOUT (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 0, inv2Index), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, inv2Index), Qt::DisplayRole).toString ()), "$2 (5)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 0, inv2Index), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, inv2Index), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 0, inv2Index), Qt::DisplayRole).toString ()), "$5");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, inv2Index), Qt::DisplayRole).toString ()), "$5 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "NIN");
EXPECT_EQ (model->parent (model->index (0, 0, sn_nets)) == model->parent (model->index (0, 3, sn_nets)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::DisplayRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "NIN (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_nets), Qt::DisplayRole).toString ()), "NOUT");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_nets), Qt::DisplayRole).toString ()), "NOUT (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, sn_nets), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, sn_nets), Qt::DisplayRole).toString ()), "$2 (5)");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, sn_nets), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, sn_nets), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, sn_nets), Qt::DisplayRole).toString ()), "$5");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, sn_nets), Qt::DisplayRole).toString ()), "$5 (3)");
// No Subcircuits
// Devices
EXPECT_EQ (tl::to_string (model->data (model->index (10, 0, inv2Index), Qt::UserRole).toString ()), "$1|PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 0, inv2Index), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 2, inv2Index), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 0, inv2Index), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 2, inv2Index), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, inv2Index), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 2, inv2Index), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (13, 0, inv2Index), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (13, 2, inv2Index), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::UserRole).toString ()), "$1|PMOS");
EXPECT_EQ (model->parent (model->index (0, 0, sn_devices)) == model->parent (model->index (0, 3, sn_devices)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_devices), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_devices), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_devices), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, sn_devices), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, sn_devices), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, sn_devices), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, sn_devices), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (model->hasChildren (ringoIndex), true);
// 0 pins, 12 nets, 10 subcircuits, 0 devices
EXPECT_EQ (model->rowCount (ringoIndex), 22);
EXPECT_EQ (model->rowCount (ringoIndex), 2);
sn_nets = model->index (0, 0, ringoIndex);
QModelIndex sn_subcircuits = model->index (1, 0, ringoIndex);
// Pins
// Nets
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoIndex), Qt::UserRole).toString ()), "FB");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoIndex), Qt::DisplayRole).toString ()), "FB (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoIndex), Qt::DisplayRole).toString ()), "VDD (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoIndex), Qt::DisplayRole).toString ()), "VSS (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, ringoIndex), Qt::DisplayRole).toString ()), "$4 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, ringoIndex), Qt::DisplayRole).toString ()), "$5 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 2, ringoIndex), Qt::DisplayRole).toString ()), "$6 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, ringoIndex), Qt::DisplayRole).toString ()), "$7 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, ringoIndex), Qt::DisplayRole).toString ()), "$8 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, ringoIndex), Qt::DisplayRole).toString ()), "$9 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, ringoIndex), Qt::DisplayRole).toString ()), "$10 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 2, ringoIndex), Qt::DisplayRole).toString ()), "$11 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 2, ringoIndex), Qt::DisplayRole).toString ()), "$12 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "FB");
EXPECT_EQ (model->parent (model->index (0, 0, sn_nets)) == model->parent (model->index (0, 3, sn_nets)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "FB (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_nets), Qt::DisplayRole).toString ()), "VDD (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, sn_nets), Qt::DisplayRole).toString ()), "VSS (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, sn_nets), Qt::DisplayRole).toString ()), "$4 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, sn_nets), Qt::DisplayRole).toString ()), "$5 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 2, sn_nets), Qt::DisplayRole).toString ()), "$6 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, sn_nets), Qt::DisplayRole).toString ()), "$7 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, sn_nets), Qt::DisplayRole).toString ()), "$8 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, sn_nets), Qt::DisplayRole).toString ()), "$9 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, sn_nets), Qt::DisplayRole).toString ()), "$10 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 2, sn_nets), Qt::DisplayRole).toString ()), "$11 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 2, sn_nets), Qt::DisplayRole).toString ()), "$12 (2)");
// Subcircuits
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, ringoIndex), Qt::UserRole).toString ()), "INV2|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, ringoIndex), Qt::DisplayRole).toString ()), "<a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 2, ringoIndex), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (21, 0, ringoIndex), Qt::DisplayRole).toString ()), "<a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (21, 2, ringoIndex), Qt::DisplayRole).toString ()), "$10");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_subcircuits), Qt::UserRole).toString ()), "INV2|$1");
EXPECT_EQ (model->parent (model->index (0, 0, sn_subcircuits)) == model->parent (model->index (0, 3, sn_subcircuits)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_subcircuits), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_subcircuits), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 0, sn_subcircuits), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, sn_subcircuits), Qt::DisplayRole).toString ()), "$10");
// Devices
// OUT pin of INV2 has a single child node which is the "NOUT" net
QModelIndex inv2PinOutIndex = model->index (2, 0, inv2Index);
EXPECT_EQ (model->parent (inv2PinOutIndex) == inv2Index, true);
// OUT pin of INV2 is identical with the "NOUT" net
QModelIndex inv2PinOutIndex = model->index (2, 0, model->index (0, 0, inv2Index));
EXPECT_EQ (model->parent (inv2PinOutIndex) == model->index (0, 0, inv2Index), true);
EXPECT_EQ (model->hasChildren (inv2PinOutIndex), true);
EXPECT_EQ (model->rowCount (inv2PinOutIndex), 1);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PinOutIndex), Qt::DisplayRole).toString ()), "NOUT");
QModelIndex inv2PinOutIndexNet = model->index (0, 0, inv2PinOutIndex);
EXPECT_EQ (model->parent (inv2PinOutIndexNet) == inv2PinOutIndex, true);
EXPECT_EQ (model->hasChildren (inv2PinOutIndexNet), false);
EXPECT_EQ (model->rowCount (inv2PinOutIndexNet), 0);
EXPECT_EQ (model->rowCount (inv2PinOutIndex), 3);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PinOutIndex), Qt::DisplayRole).toString ()), "D / PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
// NOUT net has 1 pin, 2 devices, 0 subcircuits
QModelIndex inv2NOutIndex = model->index (6, 0, inv2Index);
EXPECT_EQ (model->parent (inv2NOutIndex) == inv2Index, true);
QModelIndex inv2NOutIndex = model->index (1, 0, model->index (1, 0, inv2Index));
EXPECT_EQ (model->parent (inv2NOutIndex) == model->index (1, 0, inv2Index), true);
EXPECT_EQ (model->hasChildren (inv2NOutIndex), true);
EXPECT_EQ (model->rowCount (inv2NOutIndex), 3);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutIndex), Qt::UserRole).toString ()), "D|PMOS|$2");
EXPECT_EQ (model->parent (model->index (0, 0, inv2NOutIndex)) == model->parent (model->index (0, 3, inv2NOutIndex)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "D / PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "<a href='int:device?id=24'>$2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "D / NMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "<a href='int:device?id=56'>$4</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "<a href='int:pin?id=18'>OUT</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (model->parent (model->index (2, 0, inv2NOutIndex)) == model->parent (model->index (2, 3, inv2NOutIndex)), true);
// no children for pins on nets
QModelIndex inv2NOutPinOutIndex = model->index (2, 0, inv2NOutIndex);
@ -137,6 +145,9 @@ TEST (1)
// a MOS3 transistor has three other terminals
QModelIndex inv2NOutDeviceIndex = model->index (0, 0, inv2NOutIndex);
QModelIndex b = model->index (0, 0, inv2NOutIndex);
EXPECT_EQ (b.parent () == inv2NOutDeviceIndex.parent (), true);
EXPECT_EQ (b.model () == inv2NOutDeviceIndex.model (), true);
EXPECT_EQ (model->parent (inv2NOutDeviceIndex) == inv2NOutIndex, true);
EXPECT_EQ (model->hasChildren (inv2NOutDeviceIndex), true);
EXPECT_EQ (model->rowCount (inv2NOutDeviceIndex), 3);
@ -144,67 +155,76 @@ TEST (1)
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutDeviceIndex), Qt::UserRole).toString ()), "S|$5");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "S");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "G");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "D");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "D (already seen)");
QModelIndex inv2NOutDeviceGateIndex = model->index (1, 0, inv2NOutDeviceIndex);
EXPECT_EQ (model->parent (inv2NOutDeviceGateIndex) == inv2NOutDeviceIndex, true);
EXPECT_EQ (model->hasChildren (inv2NOutDeviceGateIndex), false);
EXPECT_EQ (model->rowCount (inv2NOutDeviceGateIndex), 0);
EXPECT_EQ (model->hasChildren (inv2NOutDeviceGateIndex), true);
EXPECT_EQ (model->rowCount (inv2NOutDeviceGateIndex), 5);
// FB net has 0 pin, 0 devices, 2 subcircuits
QModelIndex ringoFbIndex = model->index (0, 0, ringoIndex);
EXPECT_EQ (model->parent (ringoFbIndex) == ringoIndex, true);
QModelIndex ringoFbIndex = model->index (0, 0, sn_nets);
EXPECT_EQ (model->parent (ringoFbIndex) == sn_nets, true);
EXPECT_EQ (model->hasChildren (ringoFbIndex), true);
EXPECT_EQ (model->rowCount (ringoFbIndex), 2);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbIndex), Qt::UserRole).toString ()), "IN|INV2|$2");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:pin?id=2'>IN</a> / <a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:subcircuit?id=23'>$2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:pin?id=34'>$1</a> / <a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:subcircuit?id=7'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "IN / <a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=1,1,1'>$2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "$1 / <a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=1,1,0'>$1</a>");
QModelIndex ringoFbSubcircuit2Index = model->index (0, 0, ringoFbIndex);
EXPECT_EQ (model->parent (ringoFbSubcircuit2Index) == ringoFbIndex, true);
EXPECT_EQ (model->hasChildren (ringoFbSubcircuit2Index), true);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2Index), 5);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2Index), 1);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::UserRole).toString ()), "IN|NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=2'>IN</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=5'>FB</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=34'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=18'>OUT</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=53'>$4</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=50'>$3</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=37'>VSS</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=66'>$4</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=21'>VDD</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::UserRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "NIN (3)");
QModelIndex ringoFbSubcircuit2InPinIndex = model->index (1, 0, ringoFbSubcircuit2Index);
EXPECT_EQ (model->parent (ringoFbSubcircuit2InPinIndex) == ringoFbSubcircuit2Index, true);
EXPECT_EQ (model->hasChildren (ringoFbSubcircuit2InPinIndex), false);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2InPinIndex), 0);
QModelIndex ringoFbSubcircuit2InsideNetIndex = model->index (0, 0, ringoFbSubcircuit2Index);
EXPECT_EQ (model->parent (ringoFbSubcircuit2InsideNetIndex) == ringoFbSubcircuit2Index, true);
EXPECT_EQ (model->hasChildren (ringoFbSubcircuit2InsideNetIndex), true);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2InsideNetIndex), 3);
// Subcircuit 1 of RINGO has 5 pins
QModelIndex ringoSubcircuit1Index = model->index (12, 0, ringoIndex);
EXPECT_EQ (model->parent (ringoSubcircuit1Index) == ringoIndex, true);
QModelIndex ringoSubcircuit1Index = model->index (0, 0, sn_subcircuits);
EXPECT_EQ (model->parent (ringoSubcircuit1Index) == sn_subcircuits, true);
EXPECT_EQ (model->hasChildren (ringoSubcircuit1Index), true);
EXPECT_EQ (model->rowCount (ringoSubcircuit1Index), 5);
EXPECT_EQ (model->rowCount (ringoSubcircuit1Index), 2);
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1Index), Qt::UserRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=18'>OUT</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoSubcircuit1Index), Qt::DisplayRole).toString ()), "");
QModelIndex ringoSubcircuit1PinsIndex = model->index (0, 0, ringoSubcircuit1Index);
EXPECT_EQ (model->rowCount (ringoSubcircuit1PinsIndex), 5);
QModelIndex ringoSubcircuit1OutPinIndex = model->index (2, 0, ringoSubcircuit1Index);
EXPECT_EQ (model->parent (ringoSubcircuit1OutPinIndex) == ringoSubcircuit1Index, true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoSubcircuit1PinsIndex), Qt::UserRole).toString ()), "IN|$5");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoSubcircuit1PinsIndex), Qt::UserRole).toString ()), "FB");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1PinsIndex), Qt::UserRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1PinsIndex), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoSubcircuit1PinsIndex), Qt::DisplayRole).toString ()), "");
QModelIndex ringoSubcircuit1NodeIndex = model->index (1, 0, ringoSubcircuit1Index);
EXPECT_EQ (model->rowCount (ringoSubcircuit1NodeIndex), 3);
QModelIndex ringoSubcircuit1InsidePinsIndex = model->index (0, 0, ringoSubcircuit1NodeIndex);
EXPECT_EQ (model->rowCount (ringoSubcircuit1InsidePinsIndex), 5);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoSubcircuit1InsidePinsIndex), Qt::UserRole).toString ()), "IN|NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoSubcircuit1InsidePinsIndex), Qt::UserRole).toString ()), "$1|$2");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1InsidePinsIndex), Qt::UserRole).toString ()), "OUT|NOUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1InsidePinsIndex), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoSubcircuit1InsidePinsIndex), Qt::DisplayRole).toString ()), "NOUT (3)");
QModelIndex ringoSubcircuit1OutPinIndex = model->index (2, 0, ringoSubcircuit1PinsIndex);
EXPECT_EQ (model->parent (ringoSubcircuit1OutPinIndex) == ringoSubcircuit1PinsIndex, true);
EXPECT_EQ (model->hasChildren (ringoSubcircuit1OutPinIndex), false);
EXPECT_EQ (model->rowCount (ringoSubcircuit1OutPinIndex), 0);
// Device 1 of INV2 has 3 pins
// Device 1 of INV2 has 3 terminals
QModelIndex inv2Device1Index = model->index (10, 0, inv2Index);
EXPECT_EQ (model->parent (inv2Device1Index) == inv2Index, true);
QModelIndex inv2Device1Index = model->index (0, 0, sn_devices);
EXPECT_EQ (model->parent (inv2Device1Index) == sn_devices, true);
EXPECT_EQ (model->hasChildren (inv2Device1Index), true);
EXPECT_EQ (model->rowCount (inv2Device1Index), 3);
@ -213,8 +233,11 @@ TEST (1)
QModelIndex inv2Device1GateIndex = model->index (1, 0, inv2Device1Index);
EXPECT_EQ (model->parent (inv2Device1GateIndex) == inv2Device1Index, true);
EXPECT_EQ (model->hasChildren (inv2Device1GateIndex), false);
EXPECT_EQ (model->rowCount (inv2Device1GateIndex), 0);
EXPECT_EQ (model->hasChildren (inv2Device1GateIndex), true);
EXPECT_EQ (model->rowCount (inv2Device1GateIndex), 3);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Device1GateIndex), Qt::UserRole).toString ()), "G|PMOS|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Device1GateIndex), Qt::DisplayRole).toString ()), "G / PMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5] (already seen)");
}
TEST (2)
@ -228,6 +251,7 @@ TEST (2)
EXPECT_EQ (model->hasChildren (QModelIndex ()), true);
// two circuits
EXPECT_EQ (model->rowCount (QModelIndex ()), 4);
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 (0, 2, QModelIndex ()), Qt::DisplayRole).toString ()), "");
@ -245,26 +269,33 @@ TEST (2)
// INV2 circuit node
EXPECT_EQ (model->hasChildren (inv2Index), true);
EXPECT_EQ (model->rowCount (inv2Index), 14);
EXPECT_EQ (model->rowCount (inv2Index), 3);
EXPECT_EQ (model->parent (inv2Index).isValid (), false);
QModelIndex sn_pins = model->index (0, 0, inv2Index);
QModelIndex sn_nets = model->index (1, 0, inv2Index);
QModelIndex sn_devices = model->index (2, 0, inv2Index);
EXPECT_EQ (model->rowCount (sn_pins), 6);
EXPECT_EQ (model->rowCount (sn_nets), 6);
EXPECT_EQ (model->rowCount (sn_devices), 2);
// first of pins in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::UserRole).toString ()), "$0|$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "$0|$0|$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_pins), Qt::DisplayRole).toString ()), "$1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_pins), Qt::DisplayRole).toString ()), "1 (2)");
// INV2, pin 0 node
QModelIndex inv2Pin0Index = model->index (0, 0, inv2Index);
QModelIndex inv2Pin0Index = model->index (0, 0, sn_pins);
EXPECT_EQ (model->hasChildren (inv2Pin0Index), true);
EXPECT_EQ (model->rowCount (inv2Pin0Index), 1);
EXPECT_EQ (model->parent (inv2Pin0Index) == inv2Index, true);
EXPECT_EQ (model->rowCount (inv2Pin0Index), 2);
EXPECT_EQ (model->parent (inv2Pin0Index) == sn_pins, true);
// INV2, pin 0 has one net node
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Pin0Index), Qt::UserRole).toString ()), "$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Pin0Index), Qt::DisplayRole).toString ()), "$1 ⇔ 1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Pin0Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=9'>$1</a>");
std::pair<const db::Net *, const db::Net *> nets = model->net_from_index (model->index_from_id ((void *) 9, 0));
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "$0|$0|$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_pins), Qt::DisplayRole).toString ()), "$1 (2)");
std::pair<const db::Net *, const db::Net *> nets = model->net_from_index (model->index (0, 0, sn_pins));
EXPECT_EQ (nets.first != 0, true);
if (nets.first != 0) {
EXPECT_EQ (nets.first->expanded_name (), "$1");
@ -273,25 +304,25 @@ TEST (2)
if (nets.second != 0) {
EXPECT_EQ (nets.second->expanded_name (), "1");
}
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Pin0Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=9'>1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_pins), Qt::DisplayRole).toString ()), "1 (2)");
// first of nets in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (6, 0, inv2Index), Qt::UserRole).toString ()), "$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 0, inv2Index), Qt::DisplayRole).toString ()), "$1 ⇔ 1");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, inv2Index), Qt::DisplayRole).toString ()), "$1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 3, inv2Index), Qt::DisplayRole).toString ()), "1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::DisplayRole).toString ()), "$1 ⇔ 1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "$1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_nets), Qt::DisplayRole).toString ()), "1 (2)");
// INV2, net 1 node
QModelIndex inv2Net0Index = model->index (6, 0, inv2Index);
QModelIndex inv2Net0Index = model->index (0, 0, sn_nets);
EXPECT_EQ (model->hasChildren (inv2Net0Index), true);
EXPECT_EQ (model->rowCount (inv2Net0Index), 2);
EXPECT_EQ (model->parent (inv2Net0Index) == inv2Index, true);
EXPECT_EQ (model->parent (inv2Net0Index) == sn_nets, true);
// INV2, net 1 has one pin and one terminal at BULK
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Net0Index), Qt::UserRole).toString ()), "B|B|PMOS|PMOS|$1|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Net0Index), Qt::DisplayRole).toString ()), "B / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:device?id=17'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:device?id=17'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "$1");
// This terminal connects to a device with four other terminals ..
QModelIndex inv2Net0TerminalIndex = model->index (0, 0, inv2Net0Index);
@ -301,14 +332,14 @@ TEST (2)
// .. whose second terminal is gate
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::UserRole).toString ()), "G|G|IN|2");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "G");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "<a href='int:net?id=73'>IN</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "<a href='int:net?id=73'>2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "IN (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "2 (3)");
// The Pin
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0Index), Qt::UserRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=5'>$0</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=5'>$0</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "$0");
// This pin does not have children
QModelIndex inv2Net0PinIndex = model->index (1, 0, inv2Net0Index);
@ -317,59 +348,49 @@ TEST (2)
EXPECT_EQ (model->parent (inv2Net0PinIndex) == inv2Net0Index, true);
// second of nets in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (7, 0, inv2Index), Qt::UserRole).toString ()), "BULK|6");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 0, inv2Index), Qt::DisplayRole).toString ()), "BULK ⇔ 6");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, inv2Index), Qt::DisplayRole).toString ()), "BULK (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 3, inv2Index), Qt::DisplayRole).toString ()), "6 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_nets), Qt::UserRole).toString ()), "BULK|6");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_nets), Qt::DisplayRole).toString ()), "BULK ⇔ 6");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_nets), Qt::DisplayRole).toString ()), "BULK (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, sn_nets), Qt::DisplayRole).toString ()), "6 (2)");
// first of devices in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, inv2Index), Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, inv2Index), Qt::DisplayRole).toString ()), "PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 2, inv2Index), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 3, inv2Index), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::DisplayRole).toString ()), "PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_devices), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_devices), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
QModelIndex inv2PairIndex = model->index (2, 0, QModelIndex ());
EXPECT_EQ (model->parent (inv2PairIndex).isValid (), false);
// INV2PAIR circuit node
EXPECT_EQ (model->hasChildren (inv2PairIndex), true);
EXPECT_EQ (model->rowCount (inv2PairIndex), 18);
EXPECT_EQ (model->rowCount (inv2PairIndex), 3);
sn_pins = model->index (0, 0, inv2PairIndex);
sn_nets = model->index (1, 0, inv2PairIndex);
// first of pins in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairIndex), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairIndex), Qt::DisplayRole).toString ()), "- ⇔ $4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairIndex), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairIndex), Qt::DisplayRole).toString ()), "$4");
// INV2, pin 0 node
QModelIndex inv2PairPin0Index = model->index (0, 0, inv2PairIndex);
EXPECT_EQ (model->hasChildren (inv2PairPin0Index), true);
EXPECT_EQ (model->rowCount (inv2PairPin0Index), 1);
EXPECT_EQ (model->parent (inv2PairPin0Index) == inv2PairIndex, true);
// INV2, pin 0 has one net node
// The pin isnt't connected to any net, left side because there is no match, right side because the pin isn't connected
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairPin0Index), Qt::UserRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairPin0Index), Qt::DisplayRole).toString ()), "-");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairPin0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairPin0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "- ⇔ $4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_pins), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_pins), Qt::DisplayRole).toString ()), "");
// first of nets in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (8, 0, inv2PairIndex), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 0, inv2PairIndex), Qt::DisplayRole).toString ()), "$4 ⇔ -");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, inv2PairIndex), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 3, inv2PairIndex), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::DisplayRole).toString ()), "$4 ⇔ -");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_nets), Qt::DisplayRole).toString ()), "");
// This net has only left side which has one pin and two subcircuits
QModelIndex inv2PairNet0Index = model->index (8, 0, inv2PairIndex);
QModelIndex inv2PairNet0Index = model->index (0, 0, sn_nets);
EXPECT_EQ (model->hasChildren (inv2PairNet0Index), true);
EXPECT_EQ (model->rowCount (inv2PairNet0Index), 3);
EXPECT_EQ (model->parent (inv2PairNet0Index) == inv2PairIndex, true);
EXPECT_EQ (model->parent (inv2PairNet0Index) == sn_nets, true);
// The pin
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0Index), Qt::UserRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=38'>$3</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairNet0Index), Qt::DisplayRole).toString ()), "");
// This pin does not have children
@ -380,18 +401,179 @@ TEST (2)
// The first subcircuit
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairNet0Index), Qt::UserRole).toString ()), "OUT|INV2|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=101'>OUT ⇔ -</a> / <a href='int:circuit?id=1'>INV2 ⇔ -</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:subcircuit?id=46'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairNet0Index), Qt::DisplayRole).toString ()), "OUT ⇔ - / <a href='int:netlist?path=1'>INV2 ⇔ -</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=2,2,1'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2PairNet0Index), Qt::DisplayRole).toString ()), "");
// This subcircuit has 6 other pins
QModelIndex inv2PairNet0SubCircuit0Index = model->index (1, 0, inv2PairNet0Index);
EXPECT_EQ (model->hasChildren (inv2PairNet0SubCircuit0Index), true);
EXPECT_EQ (model->rowCount (inv2PairNet0SubCircuit0Index), 6);
EXPECT_EQ (model->parent (inv2PairNet0SubCircuit0Index) == inv2PairNet0Index, true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0SubCircuit0Index), Qt::UserRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0SubCircuit0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=5'>$0</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairNet0SubCircuit0Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=170'>$7</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairNet0SubCircuit0Index), Qt::DisplayRole).toString ()), "");
}
TEST (3)
{
db::LayoutToNetlist l2n;
l2n.load (tl::testsrc () + "/testdata/lay/l2n_browser.l2n");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &l2n, &colorizer));
db::Circuit *root = l2n.netlist ()->circuit_by_name ("RINGO");
EXPECT_EQ (root != 0, true);
lay::NetlistObjectsPath path;
EXPECT_EQ (model->index_from_path (path).isValid (), false);
path.root.first = root;
db::Net *net = root->net_by_name ("FB");
EXPECT_EQ (net != 0, true);
path.net.first = net;
QModelIndex index = model->index_from_path (path);
EXPECT_EQ (index.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (index, Qt::UserRole).toString ()), "FB");
}
TEST (4)
{
db::LayoutToNetlist l2n;
l2n.load (tl::testsrc () + "/testdata/lay/l2n_browser.l2n");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &l2n, &colorizer));
db::Circuit *root = l2n.netlist ()->circuit_by_name ("RINGO");
EXPECT_EQ (root != 0, true);
lay::NetlistObjectsPath path;
path.root.first = root;
db::SubCircuit *sc1 = root->begin_subcircuits ().operator-> ();
EXPECT_EQ (sc1 != 0, true);
path.path.push_back (std::make_pair (sc1, (db::SubCircuit *) 0));
db::Net *net = sc1->circuit_ref ()->net_by_name ("NOUT");
EXPECT_EQ (net != 0, true);
path.net.first = net;
QModelIndex index = model->index_from_path (path);
EXPECT_EQ (index.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (index, Qt::UserRole).toString ()), "NOUT");
}
// Netlist object path: single vs. pairs - first
TEST (5)
{
db::LayoutVsSchematic lvs;
lvs.load (tl::testsrc () + "/testdata/lay/lvsdb_browser.lvsdb");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &lvs, &colorizer));
QModelIndex idx;
db::Circuit *root = lvs.netlist ()->circuit_by_name ("INV2PAIR");
EXPECT_EQ (root != 0, true);
db::Circuit *sc = lvs.netlist ()->circuit_by_name ("INV2");
EXPECT_EQ (sc != 0, true);
lay::NetlistObjectPath path;
EXPECT_EQ (path.is_null (), true);
path.root = root;
EXPECT_EQ (path.is_null (), false);
idx = model->index_from_path (path);
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "INV2PAIR|INV2PAIR");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.net = root->net_by_cluster_id (5);
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$5|4");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.path.push_back (root->subcircuit_by_id (1));
EXPECT_EQ (path.path.back () != 0, true);
EXPECT_EQ (path.path.back ()->expanded_name (), "$1");
EXPECT_EQ (path.path.back ()->circuit_ref ()->name (), "INV2");
path.net = 0;
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
// A pure subcircuit path addresses the "Circuit" representative node of the subcircuit
EXPECT_EQ (tl::to_string (model->data (idx, Qt::DisplayRole).toString ()), "Circuit");
EXPECT_EQ (tl::to_string (model->data (model->parent (idx), Qt::UserRole).toString ()), "INV2|$1");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.net = sc->net_by_cluster_id (2);
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "IN|2");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.net = 0;
path.device = sc->device_by_id (1);
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
}
// Netlist object path: single vs. pairs - second
TEST (6)
{
db::LayoutVsSchematic lvs;
lvs.load (tl::testsrc () + "/testdata/lay/lvsdb_browser.lvsdb");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &lvs, &colorizer));
QModelIndex idx;
db::Circuit *root = lvs.reference_netlist ()->circuit_by_name ("INV2PAIR");
EXPECT_EQ (root != 0, true);
db::Circuit *sc = lvs.reference_netlist ()->circuit_by_name ("INV2");
EXPECT_EQ (sc != 0, true);
lay::NetlistObjectPath path;
EXPECT_EQ (path.is_null (), true);
path.root = root;
EXPECT_EQ (path.is_null (), false);
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "INV2PAIR|INV2PAIR");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.net = root->net_by_name ("4");
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$5|4");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.path.push_back (root->subcircuit_by_name ("$2"));
EXPECT_EQ (path.path.back () != 0, true);
EXPECT_EQ (path.path.back ()->expanded_name (), "$2");
EXPECT_EQ (path.path.back ()->circuit_ref ()->name (), "INV2");
path.net = 0;
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
// A pure subcircuit path addresses the "Circuit" representative node of the subcircuit
EXPECT_EQ (tl::to_string (model->data (idx, Qt::DisplayRole).toString ()), "Circuit");
EXPECT_EQ (tl::to_string (model->data (model->parent (idx), Qt::UserRole).toString ()), "INV2|$2");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.net = sc->net_by_name ("2");
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "IN|2");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.net = 0;
path.device = sc->device_by_id (1);
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
}

View File

@ -935,6 +935,12 @@ struct test_arg_func<gsi::VectorType>
{
void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose)
{
if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) {
// for ptr or cptr, null is an allowed value
*ret = true;
return;
}
if (! PyTuple_Check (arg) && ! PyList_Check (arg)) {
*ret = false;
return;
@ -971,6 +977,12 @@ struct test_arg_func<gsi::MapType>
{
void operator () (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose)
{
if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) {
// for ptr or cptr, null is an allowed value
*ret = true;
return;
}
if (! PyDict_Check (arg)) {
*ret = false;
return;
@ -999,6 +1011,7 @@ struct test_arg_func<gsi::MapType>
}
}
};
template <>
struct test_arg_func<gsi::ObjectType>
{

View File

@ -138,7 +138,10 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs
std::vector<PythonRef> callables;
callables.reserve (m_cbfuncs.size ());
for (std::vector<CallbackFunction>::const_iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
callables.push_back (c->callable ());
PythonRef callable = c->callable ();
if (callable) {
callables.push_back (c->callable ());
}
}
PythonRef result;

View File

@ -956,7 +956,7 @@ struct test_arg_func<gsi::VectorType>
if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) {
// for pointers to vectors, nil is a valid value
*ret = true;
} if (TYPE (arg) != T_ARRAY) {
} else if (TYPE (arg) != T_ARRAY) {
*ret = false;
} else {

Binary file not shown.

View File

@ -69,7 +69,7 @@ class DBLayoutToNetlistTests(unittest.TestCase):
ut_testsrc = os.getenv("TESTSRC")
ly = pya.Layout()
ly.read(os.path.join(ut_testsrc, "testdata", "algo", "device_extract_l1.gds"))
ly.read(os.path.join(ut_testsrc, "testdata", "algo", "device_extract_l1_with_inv_nodes.gds"))
l2n = pya.LayoutToNetlist(pya.RecursiveShapeIterator(ly, ly.top_cell(), []))
@ -97,40 +97,56 @@ class DBLayoutToNetlistTests(unittest.TestCase):
self.assertEqual(str(l2n.netlist()), """circuit TRANS ($1=$1,$2=$2);
end;
circuit INV2 (OUT=OUT,$2=$2,$3=$3,$4=$4);
circuit INV2 (OUT=OUT,$2=$3,$3=$4);
subcircuit TRANS $1 ($1=$4,$2=OUT);
subcircuit TRANS $2 ($1=$3,$2=OUT);
subcircuit TRANS $3 ($1=$2,$2=$4);
subcircuit TRANS $4 ($1=$2,$2=$3);
end;
circuit RINGO ();
subcircuit INV2 $1 (OUT=OSC,$2=FB,$3=VSS,$4=VDD);
subcircuit INV2 $2 (OUT=$I29,$2=$I20,$3=VSS,$4=VDD);
subcircuit INV2 $3 (OUT=$I28,$2=$I19,$3=VSS,$4=VDD);
subcircuit INV2 $4 (OUT=$I30,$2=$I21,$3=VSS,$4=VDD);
subcircuit INV2 $5 (OUT=$I31,$2=$I22,$3=VSS,$4=VDD);
subcircuit INV2 $6 (OUT=$I32,$2=$I23,$3=VSS,$4=VDD);
subcircuit INV2 $7 (OUT=$I33,$2=$I24,$3=VSS,$4=VDD);
subcircuit INV2 $8 (OUT=$I34,$2=$I25,$3=VSS,$4=VDD);
subcircuit INV2 $9 (OUT=$I35,$2=$I26,$3=VSS,$4=VDD);
subcircuit INV2 $10 (OUT=$I36,$2=$I27,$3=VSS,$4=VDD);
subcircuit INV2 $1 (OUT='FB,OSC',$2=VSS,$3=VDD);
subcircuit INV2 $2 (OUT=$I20,$2=VSS,$3=VDD);
subcircuit INV2 $3 (OUT=$I19,$2=VSS,$3=VDD);
subcircuit INV2 $4 (OUT=$I21,$2=VSS,$3=VDD);
subcircuit INV2 $5 (OUT=$I22,$2=VSS,$3=VDD);
subcircuit INV2 $6 (OUT=$I23,$2=VSS,$3=VDD);
subcircuit INV2 $7 (OUT=$I24,$2=VSS,$3=VDD);
subcircuit INV2 $8 (OUT=$I25,$2=VSS,$3=VDD);
subcircuit INV2 $9 (OUT=$I26,$2=VSS,$3=VDD);
subcircuit INV2 $10 (OUT=$I27,$2=VSS,$3=VDD);
end;
""")
self.assertEqual(str(l2n.probe_net(rmetal2, pya.DPoint(0.0, 1.8))), "RINGO:FB")
self.assertEqual(str(l2n.probe_net(rmetal2, pya.DPoint(0.0, 1.8))), "RINGO:FB,OSC")
sc_path = []
self.assertEqual(str(l2n.probe_net(rmetal2, pya.DPoint(0.0, 1.8), sc_path)), "RINGO:FB,OSC")
self.assertEqual(len(sc_path), 0)
self.assertEqual(repr(l2n.probe_net(rmetal2, pya.DPoint(-2.0, 1.8))), "None")
n = l2n.probe_net(rmetal1, pya.Point(2600, 1000))
self.assertEqual(str(n), "RINGO:$I20")
n = l2n.probe_net(rmetal1, pya.Point(2600, 1000), None)
self.assertEqual(str(n), "INV2:$2")
sc_path = []
n = l2n.probe_net(rmetal1, pya.Point(2600, 1000), sc_path)
self.assertEqual(str(n), "INV2:$2")
self.assertEqual(len(sc_path), 1)
a = []
t = pya.DCplxTrans()
for sc in sc_path:
a.append(sc.expanded_name())
t = t * sc.trans
self.assertEqual(",".join(a), "$2")
self.assertEqual(str(t), "r0 *1 2.64,0")
self.assertEqual(str(l2n.shapes_of_net(n, rmetal1, True)), "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)")
self.assertEqual(str(l2n.shapes_of_net(n, rmetal1, True)),
"(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)")
shapes = pya.Shapes()
l2n.shapes_of_net(n, rmetal1, True, shapes)
r = pya.Region()
for s in shapes.each():
r.insert(s.polygon)
self.assertEqual(str(r), "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)")
for s in shapes.each():
r.insert(s.polygon)
self.assertEqual(str(r),
"(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)")
def test_10_LayoutToNetlistExtractionWithoutDevices(self):

View File

@ -104,7 +104,7 @@ class DBLayoutToNetlist_TestClass < TestBase
def test_2_ShapesFromNet
ly = RBA::Layout::new
ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds"))
ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1_with_inv_nodes.gds"))
l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, []))
@ -133,39 +133,50 @@ class DBLayoutToNetlist_TestClass < TestBase
assert_equal(l2n.netlist.to_s, <<END)
circuit TRANS ($1=$1,$2=$2);
end;
circuit INV2 (OUT=OUT,$2=$2,$3=$3,$4=$4);
circuit INV2 (OUT=OUT,$2=$3,$3=$4);
subcircuit TRANS $1 ($1=$4,$2=OUT);
subcircuit TRANS $2 ($1=$3,$2=OUT);
subcircuit TRANS $3 ($1=$2,$2=$4);
subcircuit TRANS $4 ($1=$2,$2=$3);
end;
circuit RINGO ();
subcircuit INV2 $1 (OUT=OSC,$2=FB,$3=VSS,$4=VDD);
subcircuit INV2 $2 (OUT=$I29,$2=$I20,$3=VSS,$4=VDD);
subcircuit INV2 $3 (OUT=$I28,$2=$I19,$3=VSS,$4=VDD);
subcircuit INV2 $4 (OUT=$I30,$2=$I21,$3=VSS,$4=VDD);
subcircuit INV2 $5 (OUT=$I31,$2=$I22,$3=VSS,$4=VDD);
subcircuit INV2 $6 (OUT=$I32,$2=$I23,$3=VSS,$4=VDD);
subcircuit INV2 $7 (OUT=$I33,$2=$I24,$3=VSS,$4=VDD);
subcircuit INV2 $8 (OUT=$I34,$2=$I25,$3=VSS,$4=VDD);
subcircuit INV2 $9 (OUT=$I35,$2=$I26,$3=VSS,$4=VDD);
subcircuit INV2 $10 (OUT=$I36,$2=$I27,$3=VSS,$4=VDD);
subcircuit INV2 $1 (OUT='FB,OSC',$2=VSS,$3=VDD);
subcircuit INV2 $2 (OUT=$I20,$2=VSS,$3=VDD);
subcircuit INV2 $3 (OUT=$I19,$2=VSS,$3=VDD);
subcircuit INV2 $4 (OUT=$I21,$2=VSS,$3=VDD);
subcircuit INV2 $5 (OUT=$I22,$2=VSS,$3=VDD);
subcircuit INV2 $6 (OUT=$I23,$2=VSS,$3=VDD);
subcircuit INV2 $7 (OUT=$I24,$2=VSS,$3=VDD);
subcircuit INV2 $8 (OUT=$I25,$2=VSS,$3=VDD);
subcircuit INV2 $9 (OUT=$I26,$2=VSS,$3=VDD);
subcircuit INV2 $10 (OUT=$I27,$2=VSS,$3=VDD);
end;
END
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(0.0, 1.8)).to_s, "RINGO:FB")
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(0.0, 1.8)).to_s, "RINGO:FB,OSC")
sc_path = []
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(0.0, 1.8), sc_path).to_s, "RINGO:FB,OSC")
assert_equal(sc_path.size, 0)
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(-2.0, 1.8)).inspect, "nil")
n = l2n.probe_net(rmetal1, RBA::Point::new(2600, 1000))
assert_equal(n.to_s, "RINGO:$I20")
n = l2n.probe_net(rmetal1, RBA::Point::new(2600, 1000), nil)
assert_equal(n.to_s, "INV2:$2")
sc_path = []
n = l2n.probe_net(rmetal1, RBA::Point::new(2600, 1000), sc_path)
assert_equal(n.to_s, "INV2:$2")
assert_equal(sc_path.size, 1)
assert_equal(sc_path.collect(&:expanded_name).join(","), "$2")
assert_equal(sc_path.collect(&:trans).inject(&:*).to_s, "r0 *1 2.64,0")
assert_equal(l2n.shapes_of_net(n, rmetal1, true).to_s, "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)")
assert_equal(l2n.shapes_of_net(n, rmetal1, true).to_s,
"(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)")
shapes = RBA::Shapes::new
l2n.shapes_of_net(n, rmetal1, true, shapes)
r = RBA::Region::new
shapes.each { |s| r.insert(s.polygon) }
assert_equal(r.to_s, "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)")
assert_equal(r.to_s,
"(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)")
end