mirror of https://github.com/KLayout/klayout.git
Added a first version of the layout to netlist extraction feature
The main entry point is RBA::LayoutToNetlist which is the GSI binding for the layout to netlist extractor. For a first impression about the abilities of this extractor see the Ruby tests in testdata/ruby/dbLayoutToNetlist.rb. The framework itself consists of many classes, specifically - RBA::Netlist for the netlist representation - RBA::DeviceClass and superclasses (e.g. RBA::DeviceClassResistor and RBA::DeviceClassMOS3Transistor) for the description of devices. - RBA::DeviceExtractor and superclasses (i.e. RBA::DeviceExtractorMOS3Transistor or the generic RBA::GenericDeviceExtractor) for the implementation of the device extraction. - RBA::Connectivity for the description of inter- and intra-layer connections.
This commit is contained in:
parent
f989a85642
commit
9c607d7663
|
|
@ -152,7 +152,8 @@ SOURCES = \
|
|||
gsiDeclDbNetlistDeviceExtractor.cc \
|
||||
gsiDeclDbHierNetworkProcessor.cc \
|
||||
dbNetlistDeviceExtractorClasses.cc \
|
||||
dbLayoutToNetlist.cc
|
||||
dbLayoutToNetlist.cc \
|
||||
gsiDeclDbLayoutToNetlist.cc
|
||||
|
||||
HEADERS = \
|
||||
dbArray.h \
|
||||
|
|
|
|||
|
|
@ -53,16 +53,31 @@ void LayoutToNetlist::set_threads (int n)
|
|||
m_dss.set_threads (n);
|
||||
}
|
||||
|
||||
int LayoutToNetlist::threads () const
|
||||
{
|
||||
return m_dss.threads ();
|
||||
}
|
||||
|
||||
void LayoutToNetlist::set_area_ratio (double ar)
|
||||
{
|
||||
m_dss.set_max_area_ratio (ar);
|
||||
}
|
||||
|
||||
double LayoutToNetlist::area_ratio () const
|
||||
{
|
||||
return m_dss.max_area_ratio ();
|
||||
}
|
||||
|
||||
void LayoutToNetlist::set_max_vertex_count (size_t n)
|
||||
{
|
||||
m_dss.set_max_vertex_count (n);
|
||||
}
|
||||
|
||||
size_t LayoutToNetlist::max_vertex_count () const
|
||||
{
|
||||
return m_dss.max_vertex_count ();
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::make_layer (unsigned int layer_index)
|
||||
{
|
||||
db::RecursiveShapeIterator si (m_iter);
|
||||
|
|
@ -233,25 +248,32 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoi
|
|||
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point);
|
||||
}
|
||||
|
||||
size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, db::cell_index_type &cell_index_found)
|
||||
size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, std::vector<db::InstElement> &rev_inst_path)
|
||||
{
|
||||
db::Box local_box = trans.inverted () * test_cluster.bbox ();
|
||||
db::Box local_box = trans * test_cluster.bbox ();
|
||||
|
||||
const db::local_clusters<db::PolygonRef> &lcc = net_clusters ().clusters_per_cell (cell->cell_index ());
|
||||
for (db::local_clusters<db::PolygonRef>::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) {
|
||||
const db::local_cluster<db::PolygonRef> &lc = *i;
|
||||
if (lc.interacts (test_cluster, trans, m_conn)) {
|
||||
cell_index_found = cell->cell_index ();
|
||||
return lc.id ();
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Cell::touching_iterator i = cell->begin_touching (local_box); ! i.at_end (); ++i) {
|
||||
db::ICplxTrans t = trans * i->complex_trans ();
|
||||
size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, cell_index_found);
|
||||
if (cluster_id > 0) {
|
||||
return cluster_id;
|
||||
|
||||
for (db::CellInstArray::iterator ia = i->begin_touching (local_box, internal_layout ()); ! ia.at_end (); ++ia) {
|
||||
|
||||
db::ICplxTrans trans_inst = i->complex_trans (*ia);
|
||||
db::ICplxTrans t = trans_inst.inverted () * trans;
|
||||
size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, rev_inst_path);
|
||||
if (cluster_id > 0) {
|
||||
rev_inst_path.push_back (db::InstElement (*i, ia));
|
||||
return cluster_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -264,6 +286,9 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
|
|||
}
|
||||
tl_assert (mp_netlist.get ());
|
||||
|
||||
db::CplxTrans dbu_trans (internal_layout ()->dbu ());
|
||||
db::VCplxTrans dbu_trans_inv = dbu_trans.inverted ();
|
||||
|
||||
unsigned int layer = layer_of (of_region);
|
||||
|
||||
// Prepare a test cluster
|
||||
|
|
@ -272,15 +297,69 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
|
|||
db::local_cluster<db::PolygonRef> test_cluster;
|
||||
test_cluster.add (db::PolygonRef (db::Polygon (box), sr), layer);
|
||||
|
||||
db::cell_index_type ci = 0;
|
||||
size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, ci);
|
||||
std::vector<db::InstElement> inst_path;
|
||||
|
||||
size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, inst_path);
|
||||
if (cluster_id > 0) {
|
||||
|
||||
db::Circuit *circuit = mp_netlist->circuit_by_cell_index (ci);
|
||||
tl_assert (circuit != 0);
|
||||
// search_net delivers the path in reverse order
|
||||
std::reverse (inst_path.begin (), inst_path.end ());
|
||||
|
||||
std::vector<db::cell_index_type> cell_indexes;
|
||||
cell_indexes.reserve (inst_path.size () + 1);
|
||||
cell_indexes.push_back (internal_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 ());
|
||||
}
|
||||
|
||||
db::Circuit *circuit = mp_netlist->circuit_by_cell_index (cell_indexes.back ());
|
||||
if (! circuit) {
|
||||
// the circuit has probably been optimized away
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::Net *net = circuit->net_by_cluster_id (cluster_id);
|
||||
tl_assert (net != 0);
|
||||
if (! net) {
|
||||
// the net has probably been optimized away
|
||||
return 0;
|
||||
}
|
||||
|
||||
// follow the path up in the net hierarchy using the transformation and the upper cell index as the
|
||||
// guide line
|
||||
while (! inst_path.empty () && circuit->is_external_net (net)) {
|
||||
|
||||
cell_indexes.pop_back ();
|
||||
|
||||
db::Pin *pin = 0;
|
||||
for (db::Circuit::pin_iterator p = circuit->begin_pins (); p != circuit->end_pins () && ! pin; ++p) {
|
||||
if (circuit->net_for_pin (p->id ()) == net) {
|
||||
pin = p.operator-> ();
|
||||
}
|
||||
}
|
||||
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::Net *upper_net = 0;
|
||||
for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++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 ();
|
||||
}
|
||||
}
|
||||
|
||||
if (upper_net) {
|
||||
circuit = upper_circuit;
|
||||
net = upper_net;
|
||||
inst_path.pop_back ();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return net;
|
||||
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ public:
|
|||
*/
|
||||
void set_threads (int n);
|
||||
|
||||
/**
|
||||
* @brief Gets the number of threads to use
|
||||
*/
|
||||
int threads () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the area_ratio parameter for the hierarchical network processor
|
||||
* This parameter controls splitting of large polygons in order to reduce the
|
||||
|
|
@ -92,6 +97,11 @@ public:
|
|||
*/
|
||||
void set_area_ratio (double ar);
|
||||
|
||||
/**
|
||||
* @brief Gets the area ratio
|
||||
*/
|
||||
double area_ratio () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the max_vertex_count parameter for the hierarchical network processor
|
||||
* This parameter controls splitting of large polygons in order to enhance performance
|
||||
|
|
@ -99,6 +109,11 @@ public:
|
|||
*/
|
||||
void set_max_vertex_count (size_t n);
|
||||
|
||||
/**
|
||||
* @brief Gets the max vertex count
|
||||
*/
|
||||
size_t max_vertex_count () const;
|
||||
|
||||
/**
|
||||
* @brief Creates a new region representing an original layer
|
||||
* "layer_index" is the layer index of the desired layer in the original layout.
|
||||
|
|
@ -240,7 +255,7 @@ private:
|
|||
std::set<db::DeepLayer> m_dlrefs;
|
||||
bool m_netlist_extracted;
|
||||
|
||||
size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, db::cell_index_type &cell_index_found);
|
||||
size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, std::vector<db::InstElement> &rev_inst_path);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -446,6 +446,15 @@ void Net::set_name (const std::string &name)
|
|||
}
|
||||
}
|
||||
|
||||
std::string Net::qname () const
|
||||
{
|
||||
if (circuit ()) {
|
||||
return circuit ()->name () + ":" + expanded_name ();
|
||||
} else {
|
||||
return expanded_name ();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Net::expanded_name () const
|
||||
{
|
||||
if (name ().empty ()) {
|
||||
|
|
@ -531,7 +540,9 @@ Circuit::Circuit ()
|
|||
m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets),
|
||||
m_index (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
m_devices.changed ().add (this, &Circuit::devices_changed);
|
||||
m_nets.changed ().add (this, &Circuit::nets_changed);
|
||||
m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed);
|
||||
}
|
||||
|
||||
Circuit::Circuit (const Circuit &other)
|
||||
|
|
@ -544,6 +555,10 @@ Circuit::Circuit (const Circuit &other)
|
|||
m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets),
|
||||
m_index (0)
|
||||
{
|
||||
m_devices.changed ().add (this, &Circuit::devices_changed);
|
||||
m_nets.changed ().add (this, &Circuit::nets_changed);
|
||||
m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed);
|
||||
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
|
|
@ -551,14 +566,9 @@ Circuit &Circuit::operator= (const Circuit &other)
|
|||
{
|
||||
if (this != &other) {
|
||||
|
||||
m_name = other.m_name;
|
||||
m_device_by_id.invalidate ();
|
||||
m_subcircuit_by_id.invalidate ();
|
||||
m_net_by_cluster_id.invalidate ();
|
||||
m_device_by_name.invalidate ();
|
||||
m_subcircuit_by_name.invalidate ();
|
||||
m_net_by_name.invalidate ();
|
||||
clear ();
|
||||
|
||||
m_name = other.m_name;
|
||||
m_pins = other.m_pins;
|
||||
|
||||
std::map<const Device *, Device *> device_table;
|
||||
|
|
@ -630,6 +640,28 @@ const Pin *Circuit::pin_by_name (const std::string &name) const
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Circuit::devices_changed ()
|
||||
{
|
||||
m_device_by_id.invalidate ();
|
||||
m_device_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::subcircuits_changed ()
|
||||
{
|
||||
m_subcircuit_by_id.invalidate ();
|
||||
m_subcircuit_by_name.invalidate ();
|
||||
|
||||
if (mp_netlist) {
|
||||
mp_netlist->invalidate_topology ();
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::nets_changed ()
|
||||
{
|
||||
m_net_by_cluster_id.invalidate ();
|
||||
m_net_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::clear ()
|
||||
{
|
||||
m_name.clear ();
|
||||
|
|
@ -637,12 +669,6 @@ void Circuit::clear ()
|
|||
m_devices.clear ();
|
||||
m_nets.clear ();
|
||||
m_subcircuits.clear ();
|
||||
m_device_by_id.invalidate ();
|
||||
m_subcircuit_by_id.invalidate ();
|
||||
m_net_by_cluster_id.invalidate ();
|
||||
m_device_by_name.invalidate ();
|
||||
m_subcircuit_by_name.invalidate ();
|
||||
m_net_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::set_name (const std::string &name)
|
||||
|
|
@ -720,15 +746,11 @@ void Circuit::add_net (Net *net)
|
|||
{
|
||||
m_nets.push_back (net);
|
||||
net->set_circuit (this);
|
||||
m_net_by_cluster_id.invalidate ();
|
||||
m_net_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::remove_net (Net *net)
|
||||
{
|
||||
m_nets.erase (net);
|
||||
m_net_by_cluster_id.invalidate ();
|
||||
m_net_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::add_device (Device *device)
|
||||
|
|
@ -743,15 +765,11 @@ void Circuit::add_device (Device *device)
|
|||
device->set_id (id + 1);
|
||||
|
||||
m_devices.push_back (device);
|
||||
m_device_by_id.invalidate ();
|
||||
m_device_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::remove_device (Device *device)
|
||||
{
|
||||
m_devices.erase (device);
|
||||
m_device_by_id.invalidate ();
|
||||
m_device_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::add_subcircuit (SubCircuit *subcircuit)
|
||||
|
|
@ -766,23 +784,11 @@ void Circuit::add_subcircuit (SubCircuit *subcircuit)
|
|||
subcircuit->set_id (id + 1);
|
||||
|
||||
m_subcircuits.push_back (subcircuit);
|
||||
m_subcircuit_by_id.invalidate ();
|
||||
m_subcircuit_by_name.invalidate ();
|
||||
|
||||
if (mp_netlist) {
|
||||
mp_netlist->invalidate_topology ();
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::remove_subcircuit (SubCircuit *subcircuit)
|
||||
{
|
||||
m_subcircuits.erase (subcircuit);
|
||||
m_subcircuit_by_id.invalidate ();
|
||||
m_subcircuit_by_name.invalidate ();
|
||||
|
||||
if (mp_netlist) {
|
||||
mp_netlist->invalidate_topology ();
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::register_ref (SubCircuit *r)
|
||||
|
|
@ -1182,6 +1188,7 @@ Netlist::Netlist ()
|
|||
m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits)
|
||||
{
|
||||
m_circuits.changed ().add (this, &Netlist::invalidate_topology);
|
||||
m_circuits.changed ().add (this, &Netlist::circuits_changed);
|
||||
}
|
||||
|
||||
Netlist::Netlist (const Netlist &other)
|
||||
|
|
@ -1191,6 +1198,7 @@ Netlist::Netlist (const Netlist &other)
|
|||
{
|
||||
operator= (other);
|
||||
m_circuits.changed ().add (this, &Netlist::invalidate_topology);
|
||||
m_circuits.changed ().add (this, &Netlist::circuits_changed);
|
||||
}
|
||||
|
||||
Netlist &Netlist::operator= (const Netlist &other)
|
||||
|
|
@ -1222,6 +1230,12 @@ Netlist &Netlist::operator= (const Netlist &other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
void Netlist::circuits_changed ()
|
||||
{
|
||||
m_circuit_by_cell_index.invalidate ();
|
||||
m_circuit_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Netlist::invalidate_topology ()
|
||||
{
|
||||
if (m_valid_topology) {
|
||||
|
|
@ -1465,24 +1479,18 @@ void Netlist::clear ()
|
|||
{
|
||||
m_device_classes.clear ();
|
||||
m_circuits.clear ();
|
||||
m_circuit_by_name.invalidate ();
|
||||
m_circuit_by_cell_index.invalidate ();
|
||||
}
|
||||
|
||||
void Netlist::add_circuit (Circuit *circuit)
|
||||
{
|
||||
m_circuits.push_back (circuit);
|
||||
circuit->set_netlist (this);
|
||||
m_circuit_by_name.invalidate ();
|
||||
m_circuit_by_cell_index.invalidate ();
|
||||
}
|
||||
|
||||
void Netlist::remove_circuit (Circuit *circuit)
|
||||
{
|
||||
circuit->set_netlist (0);
|
||||
m_circuits.erase (circuit);
|
||||
m_circuit_by_name.invalidate ();
|
||||
m_circuit_by_cell_index.invalidate ();
|
||||
}
|
||||
|
||||
void Netlist::add_device_class (DeviceClass *device_class)
|
||||
|
|
|
|||
|
|
@ -459,6 +459,14 @@ public:
|
|||
*/
|
||||
std::string expanded_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the qualified name
|
||||
*
|
||||
* The qualified name is like the expanded name, but preceeded with the
|
||||
* Circuit name if known (e.g. "CIRCUIT:NET")
|
||||
*/
|
||||
std::string qname () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the cluster ID of this net
|
||||
*
|
||||
|
|
@ -1578,6 +1586,10 @@ private:
|
|||
void set_netlist (Netlist *netlist);
|
||||
bool combine_parallel_devices (const db::DeviceClass &cls);
|
||||
bool combine_serial_devices (const db::DeviceClass &cls);
|
||||
|
||||
void devices_changed ();
|
||||
void subcircuits_changed ();
|
||||
void nets_changed ();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2269,6 +2281,7 @@ private:
|
|||
|
||||
void invalidate_topology ();
|
||||
void validate_topology ();
|
||||
void circuits_changed ();
|
||||
|
||||
const tl::vector<Circuit *> &child_circuits (Circuit *circuit);
|
||||
const tl::vector<Circuit *> &parent_circuits (Circuit *circuit);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,212 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2018 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 "dbLayoutToNetlist.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
||||
static db::LayoutToNetlist *make_l2n (const db::RecursiveShapeIterator &iter)
|
||||
{
|
||||
return new db::LayoutToNetlist (iter);
|
||||
}
|
||||
|
||||
static db::Layout *l2n_internal_layout (db::LayoutToNetlist *l2n)
|
||||
{
|
||||
// although this isn't very clean, we dare to do so as const references are pretty useless in script languages.
|
||||
return const_cast<db::Layout *> (l2n->internal_layout ());
|
||||
}
|
||||
|
||||
static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n)
|
||||
{
|
||||
// although this isn't very clean, we dare to do so as const references are pretty useless in script languages.
|
||||
return const_cast<db::Cell *> (l2n->internal_top_cell ());
|
||||
}
|
||||
|
||||
Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
||||
gsi::constructor ("new", &make_l2n, gsi::arg ("iter"),
|
||||
"@brief The constructor\n"
|
||||
"See the class description for details.\n"
|
||||
) +
|
||||
gsi::method ("threads=", &db::LayoutToNetlist::set_threads, gsi::arg ("n"),
|
||||
"@brief Sets the number of threads to use for operations which support multiple threads\n"
|
||||
) +
|
||||
gsi::method ("threads", &db::LayoutToNetlist::threads,
|
||||
"@brief Gets the number of threads to use for operations which support multiple threads\n"
|
||||
) +
|
||||
gsi::method ("area_ratio=", &db::LayoutToNetlist::set_area_ratio, gsi::arg ("r"),
|
||||
"@brief Sets the area_ratio parameter for the hierarchical network processor\n"
|
||||
"This parameter controls splitting of large polygons in order to reduce the\n"
|
||||
"error made by the bounding box approximation.\n"
|
||||
) +
|
||||
gsi::method ("area_ratio", &db::LayoutToNetlist::area_ratio,
|
||||
"@brief Gets the area_ratio parameter for the hierarchical network processor\n"
|
||||
"See \\area_ratio= for details about this attribute."
|
||||
) +
|
||||
gsi::method ("max_vertex_count=", &db::LayoutToNetlist::set_max_vertex_count, gsi::arg ("n"),
|
||||
"@brief Sets the max_vertex_count parameter for the hierarchical network processor\n"
|
||||
"This parameter controls splitting of large polygons in order to enhance performance\n"
|
||||
"for very big polygons.\n"
|
||||
) +
|
||||
gsi::method ("max_vertex_count", &db::LayoutToNetlist::max_vertex_count,
|
||||
"See \\max_vertex_count= for details about this attribute."
|
||||
) +
|
||||
gsi::method ("make_layer", &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"),
|
||||
"@brief Creates a new region representing an original layer\n"
|
||||
"'layer_index'' is the layer index of the desired layer in the original layout.\n"
|
||||
"The Region object returned is a new object and must be deleted by the caller.\n"
|
||||
"This variant produces polygons and takes texts for net name annotation.\n"
|
||||
"A variant not taking texts is \\make_polygon_layer. A Variant only taking\n"
|
||||
"texts is \\make_text_layer.\n"""
|
||||
) +
|
||||
gsi::method ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"),
|
||||
"@brief Creates a new region representing an original layer taking texts only\n"
|
||||
"See \\make_layer for details.\n"
|
||||
) +
|
||||
gsi::method ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"),
|
||||
"@brief Creates a new region representing an original layer taking polygons and texts\n"
|
||||
"See \\make_layer for details.\n"
|
||||
) +
|
||||
gsi::method ("extract_devices", &db::LayoutToNetlist::extract_devices, gsi::arg ("extractor"), gsi::arg ("layers"),
|
||||
"@brief Extracts devices\n"
|
||||
"See the class description for more details.\n"
|
||||
"This method will run device extraction for the given extractor. The layer map is specific\n"
|
||||
"for the extractor and uses the region objects derived with \\make_layer and it's variants.\n"
|
||||
"\n"
|
||||
"In addition, derived regions can be passed too. Certain limitations apply. It's safe to use\n"
|
||||
"boolean operations for deriving layers. Other operations are applicable as long as they are\n"
|
||||
"capable of delivering hierarchical layers.\n"
|
||||
"\n"
|
||||
"If errors occur, the device extractor will contain theses errors.\n"
|
||||
) +
|
||||
gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"),
|
||||
"@brief Defines an intra-layer connection for the given layer.\n"
|
||||
"The layer is either an original layer created with \\make_layer and it's variants or\n"
|
||||
"a derived layer. Certain limitations apply. It's safe to use\n"
|
||||
"boolean operations for deriving layers. Other operations are applicable as long as they are\n"
|
||||
"capable of delivering hierarchical layers.\n"
|
||||
) +
|
||||
gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("a"), gsi::arg ("b"),
|
||||
"@brief Defines an inter-layer connection for the given layers.\n"
|
||||
"The conditions mentioned with intra-layer \\connect apply for this method too.\n"
|
||||
) +
|
||||
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist,
|
||||
"@brief Runs the netlist extraction\n"
|
||||
"See the class description for more details.\n"
|
||||
) +
|
||||
gsi::method_ext ("internal_layout", &l2n_internal_layout,
|
||||
"@brief Gets the internal layout\n"
|
||||
"Usually it should not be required to obtain the internal layout. If you need to do so, make sure not to modify the layout as\n"
|
||||
"the functionality of the netlist extractor depends on it."
|
||||
) +
|
||||
gsi::method_ext ("internal_top_cell", &l2n_internal_top_cell,
|
||||
"@brief Gets the internal top cell\n"
|
||||
"Usually it should not be required to obtain the internal cell. If you need to do so, make sure not to modify the cell as\n"
|
||||
"the functionality of the netlist extractor depends on it."
|
||||
) +
|
||||
gsi::method ("layer_of", &db::LayoutToNetlist::layer_of, gsi::arg ("l"),
|
||||
"@brief Gets the internal layer for a given extraction layer\n"
|
||||
"This method is required to derive the internal layer index - for example for\n"
|
||||
"investigating the cluster tree.\n"
|
||||
) +
|
||||
gsi::method ("cell_mapping_into", &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"),
|
||||
"@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n"
|
||||
"CAUTION: may create new cells in 'layout'.\n"
|
||||
) +
|
||||
gsi::method ("const_cell_mapping_into", &db::LayoutToNetlist::const_cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"),
|
||||
"@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n"
|
||||
"This version will not create new cells in the target layout.\n"
|
||||
"If the required cells do not exist there yet, flatting will happen.\n"
|
||||
) +
|
||||
gsi::method ("netlist", &db::LayoutToNetlist::netlist,
|
||||
"@brief gets the netlist extracted (0 if no extraction happened yet)\n"
|
||||
) +
|
||||
gsi::method ("shapes_of_net", &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"),
|
||||
"@brief Returns all shapes of a specific net and layer.\n"
|
||||
"If 'recursive'' is true, the returned region will contain the shapes of\n"
|
||||
"all subcircuits too.\n"
|
||||
) +
|
||||
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"),
|
||||
"@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"
|
||||
"It will traverse the hierarchy below if no shape in the requested layer is found\n"
|
||||
"in the specified location. The function will report the topmost net from far above the\n"
|
||||
"hierarchy of circuits as possible.\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"
|
||||
"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"
|
||||
"This variant accepts a micrometer-unit location. The location is given in the\n"
|
||||
"coordinate space of the initial cell.\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"),
|
||||
"@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"
|
||||
),
|
||||
"@brief A generic framework for extracting netlists from layouts\n"
|
||||
"\n"
|
||||
"This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor\n"
|
||||
"and more. It is supposed to provide a framework for extracting a netlist from a layout.\n"
|
||||
"\n"
|
||||
"The use model of this class consists of five steps which need to be executed in this order.\n"
|
||||
"\n"
|
||||
"@ul\n"
|
||||
"@li Configuration: in this step, the LayoutToNetlist object is created and\n"
|
||||
" if required, configured. Methods to be used in this step are \\threads=,\n"
|
||||
" \\area_ratio= or \\max_vertex_count=. The constructor for the LayoutToNetlist\n"
|
||||
" object receives a \\RecursiveShapeIterator object which basically supplies the\n"
|
||||
" hierarchy and the layout taken as input.\n"
|
||||
"@/li\n"
|
||||
"@li Preparation\n"
|
||||
" In this step, the device recognitions and extraction layers are drawn from\n"
|
||||
" the framework. Derived can now be computed using boolean operations.\n"
|
||||
" Methods to use in this step are \\make_layer and it's variants.\n"
|
||||
" Layer preparation is not necessarily required to happen before all\n"
|
||||
" other steps. Layers can be computed shortly before they are required.\n"
|
||||
"@/li\n"
|
||||
"@li Following the preparation, the devices can be extracted using \\extract_devices.\n"
|
||||
" This method needs to be called for each device extractor required. Each time,\n"
|
||||
" a device extractor needs to be given plus a map of device layers. The device\n"
|
||||
" layers are device extractor specific. Either original or derived layers\n"
|
||||
" may be specified here. Layer preparation may happen between calls to \\extract_devices.\n"
|
||||
"@/li\n"
|
||||
"@li Once the devices are derived, the netlist connectivity can be defined and the\n"
|
||||
" netlist extracted. The connectivity is defined with \\connect and it's\n"
|
||||
" flavours. The actual netlist extraction happens with \\extract_netlist.\n"
|
||||
"@/li\n"
|
||||
"@li After netlist extraction, the information is ready to be retrieved.\n"
|
||||
" The produced netlist is available with \\netlist. The Shapes of a\n"
|
||||
" specific net are available with \\shapes_of_net. \\probe_net allows\n"
|
||||
" finding a net by probing a specific location.\n"
|
||||
"@li\n"
|
||||
"\n"
|
||||
"This class has been introduced in version 0.26."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -269,6 +269,11 @@ Class<db::Net> decl_dbNet ("db", "Net",
|
|||
"@brief Gets the name of the net.\n"
|
||||
"See \\name= for details about the name."
|
||||
) +
|
||||
gsi::method ("qname|to_s", &db::Net::qname,
|
||||
"@brief Gets the qualified name.\n"
|
||||
"The qualified name is like the expanded name, but the circuit's name is preceeded\n"
|
||||
"(i.e. 'CIRCUIT:NET') if available.\n"
|
||||
) +
|
||||
gsi::method ("expanded_name", &db::Net::expanded_name,
|
||||
"@brief Gets the expanded name of the net.\n"
|
||||
"The expanded name takes the name of the net. If the name is empty, the cluster ID will be used to build a name. "
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ namespace {
|
|||
/**
|
||||
* @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods
|
||||
*/
|
||||
class NetlistDeviceExtractorImpl
|
||||
class GenericDeviceExtractor
|
||||
: public db::NetlistDeviceExtractor
|
||||
{
|
||||
public:
|
||||
NetlistDeviceExtractorImpl ()
|
||||
GenericDeviceExtractor ()
|
||||
: db::NetlistDeviceExtractor (std::string ())
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
@ -66,7 +66,7 @@ public:
|
|||
virtual void setup ()
|
||||
{
|
||||
if (cb_setup.can_issue ()) {
|
||||
cb_setup.issue<NetlistDeviceExtractorImpl> (&NetlistDeviceExtractorImpl::setup_fb);
|
||||
cb_setup.issue<GenericDeviceExtractor> (&GenericDeviceExtractor::setup_fb);
|
||||
} else {
|
||||
db::NetlistDeviceExtractor::setup ();
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ public:
|
|||
virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector<unsigned int> &layers) const
|
||||
{
|
||||
if (cb_get_connectivity.can_issue ()) {
|
||||
return cb_get_connectivity.issue<const NetlistDeviceExtractorImpl, db::Connectivity, const db::Layout &, const std::vector<unsigned int> &> (&NetlistDeviceExtractorImpl::get_connectivity_fb, layout, layers);
|
||||
return cb_get_connectivity.issue<const GenericDeviceExtractor, db::Connectivity, const db::Layout &, const std::vector<unsigned int> &> (&GenericDeviceExtractor::get_connectivity_fb, layout, layers);
|
||||
} else {
|
||||
return db::NetlistDeviceExtractor::get_connectivity (layout, layers);
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ public:
|
|||
virtual void extract_devices (const std::vector<db::Region> &layer_geometry)
|
||||
{
|
||||
if (cb_extract_devices.can_issue ()) {
|
||||
cb_extract_devices.issue<NetlistDeviceExtractorImpl, const std::vector<db::Region> &> (&NetlistDeviceExtractorImpl::extract_devices_fb, layer_geometry);
|
||||
cb_extract_devices.issue<GenericDeviceExtractor, const std::vector<db::Region> &> (&GenericDeviceExtractor::extract_devices_fb, layer_geometry);
|
||||
} else {
|
||||
db::NetlistDeviceExtractor::extract_devices (layer_geometry);
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ public:
|
|||
namespace tl
|
||||
{
|
||||
|
||||
template<> struct tl::type_traits<NetlistDeviceExtractorImpl> : public tl::type_traits<void>
|
||||
template<> struct tl::type_traits<GenericDeviceExtractor> : public tl::type_traits<void>
|
||||
{
|
||||
// mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
|
|
@ -235,11 +235,11 @@ Class<db::NetlistDeviceExtractor> decl_dbNetlistDeviceExtractor ("db", "DeviceEx
|
|||
"This class has been introduced in version 0.26."
|
||||
);
|
||||
|
||||
Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractor",
|
||||
gsi::method ("name=", &NetlistDeviceExtractorImpl::set_name,
|
||||
Class<GenericDeviceExtractor> decl_GenericDeviceExtractor (decl_dbNetlistDeviceExtractor, "db", "GenericDeviceExtractor",
|
||||
gsi::method ("name=", &GenericDeviceExtractor::set_name,
|
||||
"@brief Sets the name of the device extractor and the device class."
|
||||
) +
|
||||
gsi::callback ("setup", &NetlistDeviceExtractorImpl::setup, &NetlistDeviceExtractorImpl::cb_setup,
|
||||
gsi::callback ("setup", &GenericDeviceExtractor::setup, &GenericDeviceExtractor::cb_setup,
|
||||
"@brief Sets up the extractor.\n"
|
||||
"This method is supposed to set up the device extractor. This involves three basic steps:\n"
|
||||
"defining the name, the device classe and setting up the device layers.\n"
|
||||
|
|
@ -248,7 +248,7 @@ Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlis
|
|||
"Use \\register_device_class to register the device class you need.\n"
|
||||
"Defined the layers by calling \\define_layer once or several times.\n"
|
||||
) +
|
||||
gsi::callback ("get_connectivity", &NetlistDeviceExtractorImpl::get_connectivity, &NetlistDeviceExtractorImpl::cb_get_connectivity,
|
||||
gsi::callback ("get_connectivity", &GenericDeviceExtractor::get_connectivity, &GenericDeviceExtractor::cb_get_connectivity,
|
||||
gsi::arg ("layout"), gsi::arg ("layers"),
|
||||
"@brief Gets the connectivity object used to extract the device geometry.\n"
|
||||
"This method shall raise an error, if the input layer are not properly defined (e.g.\n"
|
||||
|
|
@ -258,7 +258,7 @@ Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlis
|
|||
"The list of layers corresponds to the number of layers defined. Use the layer indexes from this list "
|
||||
"to build the connectivity with \\Connectivity#connect."
|
||||
) +
|
||||
gsi::callback ("extract_devices", &NetlistDeviceExtractorImpl::extract_devices, &NetlistDeviceExtractorImpl::cb_extract_devices,
|
||||
gsi::callback ("extract_devices", &GenericDeviceExtractor::extract_devices, &GenericDeviceExtractor::cb_extract_devices,
|
||||
gsi::arg ("layer_geometry"),
|
||||
"@brief Extracts the devices from the given shape cluster.\n"
|
||||
"\n"
|
||||
|
|
@ -271,7 +271,7 @@ Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlis
|
|||
"terminals by which the nets extracted in the network extraction step connect\n"
|
||||
"to the new devices.\n"
|
||||
) +
|
||||
gsi::method ("register_device_class", &NetlistDeviceExtractorImpl::register_device_class, gsi::arg ("device_class"),
|
||||
gsi::method ("register_device_class", &GenericDeviceExtractor::register_device_class, gsi::arg ("device_class"),
|
||||
"@brief Registers a device class.\n"
|
||||
"The device class object will become owned by the netlist and must not be deleted by\n"
|
||||
"the caller. The name of the device class will be changed to the name given to\n"
|
||||
|
|
@ -279,19 +279,19 @@ Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlis
|
|||
"This method shall be used inside the implementation of \\setup to register\n"
|
||||
"the device classes.\n"
|
||||
) +
|
||||
gsi::method ("define_layer", &NetlistDeviceExtractorImpl::define_layer, gsi::arg ("name"), gsi::arg ("description"),
|
||||
gsi::method ("define_layer", &GenericDeviceExtractor::define_layer, gsi::arg ("name"), gsi::arg ("description"),
|
||||
"@brief Defines a layer.\n"
|
||||
"Each call will define one more layer for the device extraction.\n"
|
||||
"This method shall be used inside the implementation of \\setup to define\n"
|
||||
"the device layers. The actual geometries are later available to \\extract_devices\n"
|
||||
"in the order the layers are defined.\n"
|
||||
) +
|
||||
gsi::method ("create_device", &NetlistDeviceExtractorImpl::create_device,
|
||||
gsi::method ("create_device", &GenericDeviceExtractor::create_device,
|
||||
"@brief Creates a device.\n"
|
||||
"The device object returned can be configured by the caller, e.g. set parameters.\n"
|
||||
"It will be owned by the netlist and must not be deleted by the caller.\n"
|
||||
) +
|
||||
gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Polygon &)) &NetlistDeviceExtractorImpl::define_terminal,
|
||||
gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Polygon &)) &GenericDeviceExtractor::define_terminal,
|
||||
gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"),
|
||||
"@brief Defines a device terminal.\n"
|
||||
"This method will define a terminal to the given device and the given terminal ID. \n"
|
||||
|
|
@ -301,7 +301,7 @@ Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlis
|
|||
"This version produces a terminal with a shape given by the polygon. Note that the polygon is\n"
|
||||
"specified in database units.\n"
|
||||
) +
|
||||
gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Box &)) &NetlistDeviceExtractorImpl::define_terminal,
|
||||
gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Box &)) &GenericDeviceExtractor::define_terminal,
|
||||
gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("shape"),
|
||||
"@brief Defines a device terminal.\n"
|
||||
"This method will define a terminal to the given device and the given terminal ID. \n"
|
||||
|
|
@ -311,7 +311,7 @@ Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlis
|
|||
"This version produces a terminal with a shape given by the box. Note that the box is\n"
|
||||
"specified in database units.\n"
|
||||
) +
|
||||
gsi::method ("define_terminal", (void (NetlistDeviceExtractorImpl::*) (db::Device *, size_t, size_t, const db::Point &)) &NetlistDeviceExtractorImpl::define_terminal,
|
||||
gsi::method ("define_terminal", (void (GenericDeviceExtractor::*) (db::Device *, size_t, size_t, const db::Point &)) &GenericDeviceExtractor::define_terminal,
|
||||
gsi::arg ("device"), gsi::arg ("terminal_id"), gsi::arg ("layer_index"), gsi::arg ("point"),
|
||||
"@brief Defines a device terminal.\n"
|
||||
"This method will define a terminal to the given device and the given terminal ID. \n"
|
||||
|
|
@ -321,30 +321,30 @@ Class<NetlistDeviceExtractorImpl> decl_NetlistDeviceExtractorImpl (decl_dbNetlis
|
|||
"This version produces a point-like terminal. Note that the point is\n"
|
||||
"specified in database units.\n"
|
||||
) +
|
||||
gsi::method ("dbu", &NetlistDeviceExtractorImpl::dbu,
|
||||
gsi::method ("dbu", &GenericDeviceExtractor::dbu,
|
||||
"@brief Gets the database unit\n"
|
||||
) +
|
||||
gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &)) &NetlistDeviceExtractorImpl::error,
|
||||
gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &)) &GenericDeviceExtractor::error,
|
||||
gsi::arg ("message"),
|
||||
"@brief Issues an error with the given message\n"
|
||||
) +
|
||||
gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const db::DPolygon &)) &NetlistDeviceExtractorImpl::error,
|
||||
gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::error,
|
||||
gsi::arg ("message"), gsi::arg ("geometry"),
|
||||
"@brief Issues an error with the given message and micrometer-units polygon geometry\n"
|
||||
) +
|
||||
gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const db::Polygon &)) &NetlistDeviceExtractorImpl::error,
|
||||
gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const db::Polygon &)) &GenericDeviceExtractor::error,
|
||||
gsi::arg ("message"), gsi::arg ("geometry"),
|
||||
"@brief Issues an error with the given message and databse-unit polygon geometry\n"
|
||||
) +
|
||||
gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &)) &NetlistDeviceExtractorImpl::error,
|
||||
gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &)) &GenericDeviceExtractor::error,
|
||||
gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"),
|
||||
"@brief Issues an error with the given category name and description, message\n"
|
||||
) +
|
||||
gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &, const db::DPolygon &)) &NetlistDeviceExtractorImpl::error,
|
||||
gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::error,
|
||||
gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"),
|
||||
"@brief Issues an error with the given category name and description, message and micrometer-units polygon geometry\n"
|
||||
) +
|
||||
gsi::method ("error", (void (NetlistDeviceExtractorImpl::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &NetlistDeviceExtractorImpl::error,
|
||||
gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &GenericDeviceExtractor::error,
|
||||
gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"),
|
||||
"@brief Issues an error with the given category name and description, message and databse-unit polygon geometry\n"
|
||||
),
|
||||
|
|
|
|||
|
|
@ -172,6 +172,12 @@ static void dump_recursive_nets_to_layout (const db::LayoutToNetlist &l2n, db::L
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: may be useful elsewhere?
|
||||
static std::string qnet_name (const db::Net *net)
|
||||
{
|
||||
return net ? net->qname () : "(null)";
|
||||
}
|
||||
|
||||
static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0)
|
||||
{
|
||||
unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype));
|
||||
|
|
@ -361,6 +367,19 @@ TEST(1_Basic)
|
|||
"Circuit TRANS ($1=$1,$2=$2,$3=$3):\n"
|
||||
);
|
||||
|
||||
// do some probing before purging
|
||||
|
||||
// top level
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS");
|
||||
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2");
|
||||
|
||||
// doesn't do anything here, but we test that this does not destroy anything:
|
||||
l2n.netlist ()->combine_devices ();
|
||||
|
||||
|
|
@ -396,4 +415,18 @@ TEST(1_Basic)
|
|||
au = tl::combine_path (au, "device_extract_au1_with_rec_nets.gds");
|
||||
|
||||
db::compare_layouts (_this, ly, au);
|
||||
|
||||
// do some probing after purging
|
||||
|
||||
// top level
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC");
|
||||
// the transistor which supplies this probe target has been optimized away by "purge".
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)");
|
||||
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2:$2");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ RUBYTEST (dbLayout, "dbLayout.rb")
|
|||
RUBYTEST (dbLayoutTest, "dbLayoutTest.rb")
|
||||
RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb")
|
||||
RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb")
|
||||
RUBYTEST (dbLayoutToNetlist, "dbLayoutToNetlist.rb")
|
||||
RUBYTEST (dbMatrix, "dbMatrix.rb")
|
||||
RUBYTEST (dbNetlist, "dbNetlist.rb")
|
||||
RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,287 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
# KLayout Layout Viewer
|
||||
# Copyright (C) 2006-2018 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
|
||||
|
||||
if !$:.member?(File::dirname($0))
|
||||
$:.push(File::dirname($0))
|
||||
end
|
||||
|
||||
load("test_prologue.rb")
|
||||
|
||||
class DBLayoutToNetlist_TestClass < TestBase
|
||||
|
||||
def test_1_Basic
|
||||
|
||||
ly = RBA::Layout::new
|
||||
ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds"))
|
||||
|
||||
l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, []))
|
||||
|
||||
l2n.threads = 17
|
||||
l2n.max_vertex_count = 42
|
||||
l2n.area_ratio = 7.5
|
||||
assert_equal(l2n.threads, 17)
|
||||
assert_equal(l2n.max_vertex_count, 42)
|
||||
assert_equal(l2n.area_ratio, 7.5)
|
||||
|
||||
r = l2n.make_layer(ly.layer(6, 0))
|
||||
|
||||
assert_not_equal(l2n.internal_layout.object_id, ly.object_id)
|
||||
assert_equal(l2n.internal_layout.top_cell.name, ly.top_cell.name)
|
||||
assert_equal(l2n.internal_top_cell.name, ly.top_cell.name)
|
||||
|
||||
assert_not_equal(l2n.layer_of(r), ly.layer(6, 0)) # would be a strange coincidence ...
|
||||
|
||||
cm = l2n.const_cell_mapping_into(ly, ly.top_cell)
|
||||
(0 .. l2n.internal_layout.cells - 1).each do |ci|
|
||||
assert_equal(l2n.internal_layout.cell(ci).name, ly.cell(cm.cell_mapping(ci)).name)
|
||||
end
|
||||
|
||||
ly2 = RBA::Layout::new
|
||||
ly2.create_cell(ly.top_cell.name)
|
||||
|
||||
cm = l2n.cell_mapping_into(ly2, ly2.top_cell)
|
||||
assert_equal(ly2.cells, ly.cells)
|
||||
(0 .. l2n.internal_layout.cells - 1).each do |ci|
|
||||
assert_equal(l2n.internal_layout.cell(ci).name, ly2.cell(cm.cell_mapping(ci)).name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def test_2_ShapesFromNet
|
||||
|
||||
ly = RBA::Layout::new
|
||||
ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds"))
|
||||
|
||||
l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, []))
|
||||
|
||||
# only plain backend connectivity
|
||||
|
||||
rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) )
|
||||
rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) )
|
||||
rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) )
|
||||
rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) )
|
||||
rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) )
|
||||
|
||||
# Intra-layer
|
||||
l2n.connect(rmetal1)
|
||||
l2n.connect(rvia1)
|
||||
l2n.connect(rmetal2)
|
||||
|
||||
# Inter-layer
|
||||
l2n.connect(rmetal1, rvia1)
|
||||
l2n.connect(rvia1, rmetal2)
|
||||
l2n.connect(rmetal1, rmetal1_lbl) # attaches labels
|
||||
l2n.connect(rmetal2, rmetal2_lbl) # attaches labels
|
||||
|
||||
# Perform netlist extraction
|
||||
l2n.extract_netlist
|
||||
|
||||
assert_equal(l2n.netlist.to_s, <<END)
|
||||
Circuit TRANS ($1=$1,$2=$2):
|
||||
Circuit INV2 (OUT=OUT,$2=$2,$3=$3,$4=$4):
|
||||
XTRANS $1 ($1=$4,$2=OUT)
|
||||
XTRANS $2 ($1=$3,$2=OUT)
|
||||
XTRANS $3 ($1=$2,$2=$4)
|
||||
XTRANS $4 ($1=$2,$2=$3)
|
||||
Circuit RINGO ():
|
||||
XINV2 $1 (OUT=OSC,$2=FB,$3=VSS,$4=VDD)
|
||||
XINV2 $2 (OUT=$I29,$2=$I20,$3=VSS,$4=VDD)
|
||||
XINV2 $3 (OUT=$I28,$2=$I19,$3=VSS,$4=VDD)
|
||||
XINV2 $4 (OUT=$I30,$2=$I21,$3=VSS,$4=VDD)
|
||||
XINV2 $5 (OUT=$I31,$2=$I22,$3=VSS,$4=VDD)
|
||||
XINV2 $6 (OUT=$I32,$2=$I23,$3=VSS,$4=VDD)
|
||||
XINV2 $7 (OUT=$I33,$2=$I24,$3=VSS,$4=VDD)
|
||||
XINV2 $8 (OUT=$I34,$2=$I25,$3=VSS,$4=VDD)
|
||||
XINV2 $9 (OUT=$I35,$2=$I26,$3=VSS,$4=VDD)
|
||||
XINV2 $10 (OUT=$I36,$2=$I27,$3=VSS,$4=VDD)
|
||||
END
|
||||
|
||||
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(0.0, 1.8)).inspect, "RINGO:FB")
|
||||
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.inspect, "RINGO:$I20")
|
||||
|
||||
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)")
|
||||
|
||||
end
|
||||
|
||||
def test_10_LayoutToNetlistExtractionWithoutDevices
|
||||
|
||||
ly = RBA::Layout::new
|
||||
ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds"))
|
||||
|
||||
l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, []))
|
||||
|
||||
# only plain connectivity
|
||||
|
||||
ractive = l2n.make_layer( ly.layer(2, 0) )
|
||||
rpoly = l2n.make_polygon_layer( ly.layer(3, 0) )
|
||||
rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1) )
|
||||
rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0) )
|
||||
rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0) )
|
||||
rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) )
|
||||
rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) )
|
||||
rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) )
|
||||
rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) )
|
||||
rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) )
|
||||
|
||||
rsd = ractive - rpoly
|
||||
|
||||
# Intra-layer
|
||||
l2n.connect(rsd)
|
||||
l2n.connect(rpoly)
|
||||
l2n.connect(rdiff_cont)
|
||||
l2n.connect(rpoly_cont)
|
||||
l2n.connect(rmetal1)
|
||||
l2n.connect(rvia1)
|
||||
l2n.connect(rmetal2)
|
||||
|
||||
# Inter-layer
|
||||
l2n.connect(rsd, rdiff_cont)
|
||||
l2n.connect(rpoly, rpoly_cont)
|
||||
l2n.connect(rpoly_cont, rmetal1)
|
||||
l2n.connect(rdiff_cont, rmetal1)
|
||||
l2n.connect(rmetal1, rvia1)
|
||||
l2n.connect(rvia1, rmetal2)
|
||||
l2n.connect(rpoly, rpoly_lbl) # attaches labels
|
||||
l2n.connect(rmetal1, rmetal1_lbl) # attaches labels
|
||||
l2n.connect(rmetal2, rmetal2_lbl) # attaches labels
|
||||
|
||||
# Perform netlist extraction
|
||||
l2n.extract_netlist
|
||||
|
||||
assert_equal(l2n.netlist.to_s, <<END)
|
||||
Circuit TRANS ($1=$1,$2=$2,$3=$3):
|
||||
Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):
|
||||
XTRANS $1 ($1=$2,$2=$4,$3=IN)
|
||||
XTRANS $2 ($1=$2,$2=$5,$3=IN)
|
||||
XTRANS $3 ($1=$5,$2=OUT,$3=$2)
|
||||
XTRANS $4 ($1=$4,$2=OUT,$3=$2)
|
||||
Circuit RINGO ():
|
||||
XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)
|
||||
XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)
|
||||
XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)
|
||||
XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)
|
||||
XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)
|
||||
XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)
|
||||
XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)
|
||||
XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)
|
||||
XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)
|
||||
XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)
|
||||
END
|
||||
|
||||
end
|
||||
|
||||
def test_11_LayoutToNetlistExtractionWithDevices
|
||||
|
||||
ly = RBA::Layout::new
|
||||
ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds"))
|
||||
|
||||
l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, []))
|
||||
|
||||
rnwell = l2n.make_layer( ly.layer(1, 0) )
|
||||
ractive = l2n.make_layer( ly.layer(2, 0) )
|
||||
rpoly = l2n.make_polygon_layer( ly.layer(3, 0) )
|
||||
rpoly_lbl = l2n.make_text_layer( ly.layer(3, 1) )
|
||||
rdiff_cont = l2n.make_polygon_layer( ly.layer(4, 0) )
|
||||
rpoly_cont = l2n.make_polygon_layer( ly.layer(5, 0) )
|
||||
rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) )
|
||||
rmetal1_lbl = l2n.make_text_layer( ly.layer(6, 1) )
|
||||
rvia1 = l2n.make_polygon_layer( ly.layer(7, 0) )
|
||||
rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0) )
|
||||
rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1) )
|
||||
|
||||
rpactive = ractive & rnwell
|
||||
rpgate = rpactive & rpoly
|
||||
rpsd = rpactive - rpgate
|
||||
|
||||
rnactive = ractive - rnwell
|
||||
rngate = rnactive & rpoly
|
||||
rnsd = rnactive - rngate
|
||||
|
||||
# PMOS transistor device extraction
|
||||
pmos_ex = RBA::DeviceExtractorMOS3Transistor::new("PMOS")
|
||||
l2n.extract_devices(pmos_ex, { "SD" => rpsd, "G" => rpgate, "P" => rpoly })
|
||||
|
||||
# NMOS transistor device extraction
|
||||
nmos_ex = RBA::DeviceExtractorMOS3Transistor::new("NMOS")
|
||||
l2n.extract_devices(nmos_ex, { "SD" => rnsd, "G" => rngate, "P" => rpoly })
|
||||
|
||||
# Define connectivity for netlist extraction
|
||||
|
||||
# Intra-layer
|
||||
l2n.connect(rpsd)
|
||||
l2n.connect(rnsd)
|
||||
l2n.connect(rpoly)
|
||||
l2n.connect(rdiff_cont)
|
||||
l2n.connect(rpoly_cont)
|
||||
l2n.connect(rmetal1)
|
||||
l2n.connect(rvia1)
|
||||
l2n.connect(rmetal2)
|
||||
|
||||
# Inter-layer
|
||||
l2n.connect(rpsd, rdiff_cont)
|
||||
l2n.connect(rnsd, rdiff_cont)
|
||||
l2n.connect(rpoly, rpoly_cont)
|
||||
l2n.connect(rpoly_cont, rmetal1)
|
||||
l2n.connect(rdiff_cont, rmetal1)
|
||||
l2n.connect(rmetal1, rvia1)
|
||||
l2n.connect(rvia1, rmetal2)
|
||||
l2n.connect(rpoly, rpoly_lbl) # attaches labels
|
||||
l2n.connect(rmetal1, rmetal1_lbl) # attaches labels
|
||||
l2n.connect(rmetal2, rmetal2_lbl) # attaches labels
|
||||
|
||||
# Perform netlist extraction
|
||||
l2n.extract_netlist
|
||||
|
||||
assert_equal(l2n.netlist.to_s, <<END)
|
||||
Circuit RINGO ():
|
||||
XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)
|
||||
XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)
|
||||
XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)
|
||||
XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)
|
||||
XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)
|
||||
XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)
|
||||
XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)
|
||||
XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)
|
||||
XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)
|
||||
XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)
|
||||
Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):
|
||||
DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]
|
||||
DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]
|
||||
DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]
|
||||
DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]
|
||||
XTRANS $1 ($1=$2,$2=$4,$3=IN)
|
||||
XTRANS $2 ($1=$2,$2=$5,$3=IN)
|
||||
XTRANS $3 ($1=$5,$2=OUT,$3=$2)
|
||||
XTRANS $4 ($1=$4,$2=OUT,$3=$2)
|
||||
Circuit TRANS ($1=$1,$2=$2,$3=$3):
|
||||
END
|
||||
|
||||
# cleanup now
|
||||
l2n._destroy
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ end
|
|||
|
||||
load("test_prologue.rb")
|
||||
|
||||
class DBNetlist_TestClass < TestBase
|
||||
class DBNetlistDeviceClasses_TestClass < TestBase
|
||||
|
||||
def test_1_Resistors
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue