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:
Matthias Koefferlein 2018-12-30 22:37:31 +01:00
parent f989a85642
commit 9c607d7663
12 changed files with 733 additions and 79 deletions

View File

@ -152,7 +152,8 @@ SOURCES = \
gsiDeclDbNetlistDeviceExtractor.cc \
gsiDeclDbHierNetworkProcessor.cc \
dbNetlistDeviceExtractorClasses.cc \
dbLayoutToNetlist.cc
dbLayoutToNetlist.cc \
gsiDeclDbLayoutToNetlist.cc
HEADERS = \
dbArray.h \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"
),

View File

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

View File

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

287
testdata/ruby/dbLayoutToNetlist.rb vendored Normal file
View File

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

View File

@ -23,7 +23,7 @@ end
load("test_prologue.rb")
class DBNetlist_TestClass < TestBase
class DBNetlistDeviceClasses_TestClass < TestBase
def test_1_Resistors