diff --git a/src/db/db/db.pro b/src/db/db/db.pro index ba81bd02b..13ac344f1 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -151,7 +151,8 @@ SOURCES = \ gsiDeclDbNetlistDeviceClasses.cc \ gsiDeclDbNetlistDeviceExtractor.cc \ gsiDeclDbHierNetworkProcessor.cc \ - dbNetlistDeviceExtractorClasses.cc + dbNetlistDeviceExtractorClasses.cc \ + dbLayoutToNetlist.cc HEADERS = \ dbArray.h \ @@ -266,7 +267,8 @@ HEADERS = \ dbNetlistDeviceClasses.h \ dbNetlistDeviceExtractor.h \ dbNetlistExtractor.h \ - dbNetlistDeviceExtractorClasses.h + dbNetlistDeviceExtractorClasses.h \ + dbLayoutToNetlist.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index c0e900a17..1ca9af602 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -113,6 +113,34 @@ DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, const_cast (mp_store.get ())->insert (*this, into_layout, into_cell, into_layer); } +bool DeepLayer::operator< (const DeepLayer &other) const +{ + if (mp_store.get () != other.mp_store.get ()) { + return mp_store.get () < other.mp_store.get (); + } + if (m_layout != other.m_layout) { + return m_layout < other.m_layout; + } + if (m_layer != other.m_layer) { + return m_layer < other.m_layer; + } + return false; +} + +bool DeepLayer::operator== (const DeepLayer &other) const +{ + if (mp_store.get () != other.mp_store.get ()) { + return false; + } + if (m_layout != other.m_layout) { + return false; + } + if (m_layer != other.m_layer) { + return false; + } + return true; +} + db::Layout & DeepLayer::layout () { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 7fcc7ec07..7dac3f9d1 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -77,6 +77,16 @@ public: */ DeepLayer &operator= (const DeepLayer &other); + /** + * @brief Less operator + */ + bool operator< (const DeepLayer &other) const; + + /** + * @brief Equality operator + */ + bool operator== (const DeepLayer &other) const; + /** * @brief Gets the layout object * The return value is guaranteed to be non-null. diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc new file mode 100644 index 000000000..d0437d16d --- /dev/null +++ b/src/db/db/dbLayoutToNetlist.cc @@ -0,0 +1,291 @@ + + +/* + + 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 "dbCommon.h" +#include "dbLayoutToNetlist.h" +#include "dbDeepRegion.h" +#include "dbShapeRepository.h" +#include "dbCellMapping.h" + +namespace db +{ + +static bool is_deep (const db::Region &r) +{ + return dynamic_cast (r.delegate ()) != 0; +} + +// the iterator provides the hierarchical selection (enabling/disabling cells etc.) +LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) + : m_iter (iter), m_netlist_extracted (false) +{ + // check the iterator + if (iter.has_complex_region () || iter.region () != db::Box::world ()) { + throw tl::Exception (tl::to_string (tr ("The netlist extractor cannot work on clipped layouts"))); + } + + m_dss.set_text_enlargement (1); + m_dss.set_text_property_name (tl::Variant ("LABEL")); +} + +void LayoutToNetlist::set_threads (int n) +{ + m_dss.set_threads (n); +} + +void LayoutToNetlist::set_area_ratio (double ar) +{ + m_dss.set_max_area_ratio (ar); +} + +void LayoutToNetlist::set_max_vertex_count (size_t n) +{ + m_dss.set_max_vertex_count (n); +} + +db::Region *LayoutToNetlist::make_layer (unsigned int layer_index) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::All); + return new db::Region (si, m_dss); +} + +db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::Texts); + return new db::Region (si, m_dss); +} + +db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index) +{ + db::RecursiveShapeIterator si (m_iter); + si.set_layer (layer_index); + si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); + return new db::Region (si, m_dss); +} + +void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + extractor.extract(m_dss, layers, mp_netlist.get ()); +} + +void LayoutToNetlist::connect (const db::Region &l) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! is_deep (l)) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in intra-layer connectivity for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dl (l); + m_dlrefs.insert (dl); + + m_conn.connect (dl.layer ()); +} + +void LayoutToNetlist::connect (const db::Region &a, const db::Region &b) +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! is_deep (a)) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in inter-layer connectivity (first layer) for netlist extraction")))); + } + if (! is_deep (b)) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in inter-layer connectivity (second layer) for netlist extraction")))); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dla (a), dlb (b); + m_dlrefs.insert (dla); + m_dlrefs.insert (dlb); + + m_conn.connect (dla.layer (), dlb.layer ()); +} + +void LayoutToNetlist::extract_netlist () +{ + if (m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + } + if (! mp_netlist.get ()) { + mp_netlist.reset (new db::Netlist ()); + } + m_netex.extract_nets(m_dss, m_conn, mp_netlist.get ()); + m_netlist_extracted = true; +} + +const db::Layout *LayoutToNetlist::internal_layout () const +{ + return &m_dss.const_layout (); +} + +const db::Cell *LayoutToNetlist::internal_top_cell () const +{ + return &m_dss.const_initial_cell (); +} + +unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const +{ + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + if (! dr) { + throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in netlist extraction")))); + } + return dr->deep_layer ().layer (); +} + +db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell) +{ + return m_dss.cell_mapping_to_original (0, &layout, cell.cell_index ()); +} + +db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell) +{ + db::CellMapping cm; + if (layout.cells () == 1) { + cm.create_single_mapping (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ()); + } else { + cm.create_from_geometry (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ()); + } + return cm; +} + +db::Netlist *LayoutToNetlist::netlist () const +{ + return mp_netlist.get (); +} + +const db::hier_clusters &LayoutToNetlist::net_clusters () const +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + return m_netex.clusters (); +} + +db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const +{ + unsigned int lid = layer_of (of_layer); + db::Region res; + + if (! recursive) { + + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + db::cell_index_type ci = circuit->cell_index (); + + const db::local_cluster &lc = m_netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); + + for (db::local_cluster::shape_iterator s = lc.begin (lid); !s.at_end (); ++s) { + res.insert (*s); + } + + } else { + + const db::Circuit *circuit = net.circuit (); + tl_assert (circuit != 0); + + db::cell_index_type ci = circuit->cell_index (); + + for (db::recursive_cluster_shape_iterator rci (m_netex.clusters (), lid, ci, net.cluster_id ()); !rci.at_end (); ++rci) { + res.insert (rci->obj ().transformed (db::ICplxTrans (rci->trans ()) * rci.trans ())); + } + + } + + return res; +} + +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point) +{ + 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 &test_cluster, db::cell_index_type &cell_index_found) +{ + db::Box local_box = trans.inverted () * test_cluster.bbox (); + + const db::local_clusters &lcc = net_clusters ().clusters_per_cell (cell->cell_index ()); + for (db::local_clusters::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) { + const db::local_cluster &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; + } + } + + return 0; +} + +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point) +{ + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + tl_assert (mp_netlist.get ()); + + unsigned int layer = layer_of (of_region); + + // Prepare a test cluster + db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1)); + db::GenericRepository sr; + db::local_cluster 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); + if (cluster_id > 0) { + + db::Circuit *circuit = mp_netlist->circuit_by_cell_index (ci); + tl_assert (circuit != 0); + + db::Net *net = circuit->net_by_cluster_id (cluster_id); + tl_assert (net != 0); + return net; + + } else { + return 0; + } +} + +} diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h new file mode 100644 index 000000000..d1678e433 --- /dev/null +++ b/src/db/db/dbLayoutToNetlist.h @@ -0,0 +1,260 @@ + +/* + + 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 + +*/ + +#ifndef _HDR_dbLayout2Netlist +#define _HDR_dbLayout2Netlist + +#include "dbCommon.h" +#include "dbCellMapping.h" +#include "dbNetlistExtractor.h" +#include "dbNetlistDeviceExtractor.h" + +namespace db +{ + +/** + * @brief A generic framework for extracting netlists from layouts + * + * This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor + * and more. It is supposed to provide a framework for extracting a netlist from a layout. + * + * The use model of this class consists of five steps which need to be executed in this order. + * + * @ul + * @li Configuration: in this step, the LayoutToNetlist object is created and + * if required, configured. Methods to be used in this step are "set_threads", + * "set_area_ratio" or "set_max_vertex_count". The constructor for the LayoutToNetlist + * object receives a db::RecursiveShapeIterator object which basically supplies the + * hierarchy and the layout taken as input. + * @/li + * @li Preparation + * In this step, the device recognitions and extraction layers are drawn from + * the framework. Derived can now be computed using boolean operations. + * Methods to use in this step are "make_layer" and it's variants. + * Layer preparation is not necessarily required to happen before all + * other steps. Layers can be computed shortly before they are required. + * @/li + * @li Following the preparation, the devices can be extracted using "extract_devices". + * This method needs to be called for each device extractor required. Each time, + * a device extractor needs to be given plus a map of device layers. The device + * layers are device extractor specific. Either original or derived layers + * may be specified here. Layer preparation may happen between calls to "extract_devices". + * @/li + * @li Once the devices are derived, the netlist connectivity can be defined and the + * netlist extracted. The connectivity is defined with "connect" and it's + * flavours. The actual netlist extraction happens with "extract_netlist". + * @/li + * @li After netlist extraction, the information is ready to be retrieved. + * The produced netlist is available with "netlist". The Shapes of a + * specific net are available with "shapes_of_net". "probe_net" allows + * finding a net by probing a specific location. + * @li + */ +class DB_PUBLIC LayoutToNetlist + : public gsi::ObjectBase, public tl::Object +{ +public: + /** + * @brief The constructor + * + * See the class description for details. + */ + LayoutToNetlist (const db::RecursiveShapeIterator &iter); + + /** + * @brief Sets the number of threads to use for operations which support multiple threads + */ + void set_threads (int n); + + /** + * @brief Sets the area_ratio parameter for the hierarchical network processor + * This parameter controls splitting of large polygons in order to reduce the + * error made by the bounding box approximation. + */ + void set_area_ratio (double ar); + + /** + * @brief Sets the max_vertex_count parameter for the hierarchical network processor + * This parameter controls splitting of large polygons in order to enhance performance + * for very big polygons. + */ + void set_max_vertex_count (size_t n); + + /** + * @brief Creates a new region representing an original layer + * "layer_index" is the layer index of the desired layer in the original layout. + * The Region object returned is a new object and must be deleted by the caller. + * This variant produces polygons and takes texts for net name annotation. + * A variant not taking texts is "make_polygon_layer". A Variant only taking + * texts is "make_text_layer". + */ + db::Region *make_layer (unsigned int layer_index); + + /** + * @brief Creates a new region representing an original layer taking texts only + * See "make_layer" for details. + */ + db::Region *make_text_layer (unsigned int layer_index); + + /** + * @brief Creates a new region representing an original layer taking polygons and texts + * See "make_layer" for details. + */ + db::Region *make_polygon_layer (unsigned int layer_index); + + /** + * @brief Extracts devices + * See the class description for more details. + * This method will run device extraction for the given extractor. The layer map is specific + * for the extractor and uses the region objects derived with "make_layer" and it's variants. + * + * In addition, derived regions can be passed too. Certain limitations apply. It's safe to use + * boolean operations for deriving layers. Other operations are applicable as long as they are + * capable of delivering hierarchical layers. + * + * If errors occur, the device extractor will contain theses errors. + */ + void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); + + /** + * @brief Defines an intra-layer connection for the given layer. + * The layer is either an original layer created with "make_layer" and it's variants or + * a derived layer. Certain limitations apply. It's safe to use + * boolean operations for deriving layers. Other operations are applicable as long as they are + * capable of delivering hierarchical layers. + */ + void connect (const db::Region &l); + + /** + * @brief Defines an inter-layer connection for the given layers. + * The conditions mentioned with intra-layer "connect" apply for this method too. + */ + void connect (const db::Region &a, const db::Region &b); + + /** + * @brief Runs the netlist extraction + * See the class description for more details. + */ + void extract_netlist (); + + /** + * @brief Gets the internal layout + */ + const db::Layout *internal_layout () const; + + /** + * @brief Gets the internal top cell + */ + const db::Cell *internal_top_cell () const; + + /** + * @brief Gets the internal layer for a given extraction layer + * This method is required to derive the internal layer index - for example for + * investigating the cluster tree. + */ + unsigned int layer_of (const db::Region ®ion) const; + + /** + * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. + * CAUTION: may create new cells in "layout". + */ + db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell); + + /** + * @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout. + * This version will not create new cells in the target layout. + * If the required cells do not exist there yet, flatting will happen. + */ + db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell); + + /** + * @brief gets the netlist extracted (0 if no extraction happened yet) + */ + db::Netlist *netlist () const; + + /** + * @brief Gets the hierarchical shape clusters derived in the net extraction. + * NOTE: the layer and cell indexes used inside this structure refer to the + * internal layout. + */ + const db::hier_clusters &net_clusters () const; + + /** + * @brief Returns all shapes of a specific net and layer. + * If "recursive" is true, the returned region will contain the shapes of + * all subcircuits too. + */ + db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const; + + /** + * @brief Finds the net by probing a specific location on the given layer + * + * This method will find a net looking at the given layer at the specific position. + * It will traverse the hierarchy below if no shape in the requested layer is found + * in the specified location. + * + * If no net is found at all, 0 is returned. + * + * This variant accepts a micrometer-unit location. The location is given in the + * coordinate space of the initial cell. + */ + db::Net *probe_net (const db::Region &of_region, const db::DPoint &point); + + /** + * @brief Finds the net by probing a specific location on the given layer + * See the description of the other "probe_net" variant. + * This variant accepts a database-unit location. The location is given in the + * coordinate space of the initial cell. + */ + db::Net *probe_net (const db::Region &of_region, const db::Point &point); + +private: + // no copying + LayoutToNetlist (const db::LayoutToNetlist &other); + LayoutToNetlist &operator= (const db::LayoutToNetlist &other); + + db::RecursiveShapeIterator m_iter; + db::DeepShapeStore m_dss; + db::Connectivity m_conn; + db::NetlistExtractor m_netex; + std::auto_ptr mp_netlist; + std::set m_dlrefs; + bool m_netlist_extracted; + + size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, db::cell_index_type &cell_index_found); +}; + +} + +namespace tl +{ + +template<> struct tl::type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index ab089c80d..8684cd651 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -186,7 +186,7 @@ public: * device extraction. See the virtual methods below. */ class DB_PUBLIC NetlistDeviceExtractor - : public gsi::ObjectBase + : public gsi::ObjectBase, public tl::Object { public: typedef std::list error_list; diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 18173f2ff..6c1bfd0ed 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -360,6 +360,28 @@ RecursiveShapeIterator::confine_region (const region_type ®ion) m_needs_reinit = true; } +void +RecursiveShapeIterator::set_layer (unsigned int layer) +{ + if (m_has_layers || m_layer != layer) { + m_has_layers = false; + m_layers.clear (); + m_layer = layer; + m_needs_reinit = true; + } +} + +void +RecursiveShapeIterator::set_layers (const std::vector &layers) +{ + if (! m_has_layers || m_layers != layers) { + m_has_layers = true; + m_layers = layers; + m_layer = 0; + m_needs_reinit = true; + } +} + namespace { struct BoxTreePusher diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 59cc5679f..6f0a205e0 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -474,6 +474,20 @@ public: } } + /** + * @brief Changes the layer to be traversed + * + * This method must be used before shapes are being retrieved. Using this method may reset the iterator. + */ + void set_layer (unsigned int layer); + + /** + * @brief Changes the layers to be traversed + * + * This method must be used before shapes are being retrieved. Using this method may reset the iterator. + */ + void set_layers (const std::vector &layers); + /** * @brief Specify the property selector * diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc new file mode 100644 index 000000000..287f874b5 --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -0,0 +1,351 @@ + +/* + + 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 "dbNetlistDeviceExtractorClasses.h" +#include "dbLayoutToNetlist.h" +#include "dbStream.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" +#include "dbTestSupport.h" + +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlFileUtils.h" + +#include +#include + +namespace +{ + +static std::string device_name (const db::Device &device) +{ + if (device.name ().empty ()) { + return "$" + tl::to_string (device.id ()); + } else { + return device.name (); + } +} + +class MOSFETExtractor + : public db::NetlistDeviceExtractorMOS3Transistor +{ +public: + MOSFETExtractor (const std::string &name, db::Layout *debug_out) + : db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) + { + if (mp_debug_out) { + m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); + m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); + } + } + +private: + db::Layout *mp_debug_out; + unsigned int m_ldiff, m_lgate; + + void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) + { + if (! mp_debug_out) { + return; + } + + std::string cn = layout ()->cell_name (cell_index ()); + std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); + tl_assert (target_cp.first); + + db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ()); + mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); + + db::Cell &device_cell = mp_debug_out->cell (dci); + for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_ldiff).insert (*p); + } + for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_lgate).insert (*p); + } + + std::string ps; + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); + } + device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); + } +}; + +} + +static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +{ + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ())); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + const db::local_cluster &lc = clusters.clusters_per_cell (c->cell_index ()).cluster_by_id (n->cluster_id ()); + + bool any_shapes = false; + for (std::map::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) { + any_shapes = ! lc.begin (m->first).at_end (); + } + + if (any_shapes) { + + std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); + db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); + cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = net_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = lc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } + } + + } + + } + + } +} + +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)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +TEST(1_Basic) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + + // NOTE: the device extractor will add more debug layers for the transistors: + // 20/0 -> Diffusion + // 21/0 -> Gate + MOSFETExtractor pmos_ex ("PMOS", &ly); + MOSFETExtractor nmos_ex ("NMOS", &ly); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // net 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 + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 202/0 -> Active + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + std::map dump_map; + dump_map [l2n.layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [l2n.layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [l2n.layer_of (*rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [l2n.layer_of (*rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [l2n.layer_of (*rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [l2n.layer_of (*rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [l2n.layer_of (*rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [l2n.layer_of (*rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc); + dump_nets_to_layout (*l2n.netlist (), l2n.net_clusters (), ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO ():\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" + " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" + " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" + " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" + "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + ); + + // doesn't do anything here, but we test that this does not destroy anything: + l2n.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->purge (); + + // compare netlist as string + EXPECT_EQ (l2n.netlist ()->to_string (), + "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" + " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" + " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n" + " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1.gds"); + + db::compare_layouts (_this, ly, au); +} diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 2f50910af..6625e2c30 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -46,7 +46,9 @@ static unsigned int layer_of (const db::Region ®ion) { - return db::DeepLayer (region).layer (); + const db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + return dr->deep_layer ().layer (); } static std::string device_name (const db::Device &device) @@ -58,6 +60,9 @@ static std::string device_name (const db::Device &device) } } +namespace +{ + class MOSFETExtractor : public db::NetlistDeviceExtractorMOS3Transistor { @@ -108,6 +113,8 @@ private: } }; +} + 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)); @@ -362,93 +369,3 @@ TEST(1_DeviceAndNetExtraction) db::compare_layouts (_this, ly, au); } - -#if 0 - -// -------------------------------------------------------------------------------------- -// An attempt to simplify things. - -/* -TODO: -- netlist query functions such as net_by_name, device_by_name, circuit_by_name -- terminal geometry (Polygon) for device, combined device geometry (all terminals) -- netlist manipulation methods (i.e. flatten certain cells, purging etc.) -*/ - -#include "tlGlobPattern.h" -#include "dbHierNetworkProcessor.h" - -namespace db -{ - -class DB_PUBLIC LayoutToNetlist -{ -public: - // the iterator provides the hierarchical selection (enabling/disabling cells etc.) - LayoutToNetlist (const db::RecursiveShapeIterator &iter); - - // --- Step 0: configuration - - void set_threads (unsigned int n); - void set_area_ratio (double ar); - void set_max_vertex_count (size_t n); - - // --- Step 1: preparation - - // returns new'd regions - db::Region *make_layer (unsigned int layer_index); - db::Region *make_text_layer (unsigned int layer_index); - db::Region *make_polygon_layer (unsigned int layer_index); - - // --- Step 2: device extraction - - // after this, the device extractor will have errors if some occured. - void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); - - // --- Step 3: net extraction - - // define connectivity for the netlist extraction - void connect (const db::Region &l); - void connect (const db::Region &a, const db::Region &b); - - // runs the netlist extraction - void extract_netlist (); - - // --- Step 4: retrieval - - // gets the internal layout and cell (0 if not available) - const db::Layout *internal_layout () const; - const db::Cell *internal_top_cell () const; - - // gets the internal layer index of the given region - unsigned int layer_of (const db::Region ®ion) const; - - // creates a cell mapping for copying the internal hierarchy to the given layout - // CAUTION: may create new cells in "layout". - db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell); - - // creates a cell mapping for copying the internal hierarchy to the given layout - // This version will not create new cells in the target layout. - db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell); - - // gets the netlist extracted (0 if no extraction happened yet) - db::Netlist *netlist () const; - - // gets the hierarchical clusters of the nets (CAUTION: the layer indexes therein are - // internal layer indexes), same for cell indexes. - // -> NOT GSI - const db::hier_clusters &net_clusters () const; - - // copies the shapes of the given net from a given layer - // (recursive true: include nets from subcircuits) - db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive); - - // finds the net by probing a specific location on the given layer looking through the - // hierarchy. Returns 0 if no net was found. - db::Net *probe_net (const db::Region &of_region, const db::DPoint &point); - db::Net *probe_net (const db::Region &of_region, const db::Point &point); -}; - -} - -#endif diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 18d308d82..67ab0f993 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -428,6 +428,12 @@ TEST(1a) x = collect(i5, g); EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + i5.set_layer (1); + x = collect_with_copy(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + x = collect(i5, g); + EXPECT_EQ (x, "[$3](101,1;1101,1101)"); + std::set ll; db::RecursiveShapeIterator i5a (g, c0, ll, db::Box::world ()); @@ -454,6 +460,14 @@ TEST(1a) EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); x = collect(i5cc, g, true); EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + + std::vector ll_new; + ll_new.push_back (0); + i5c.set_layers (ll_new); + x = collect_with_copy(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); + x = collect(i5c, g, true); + EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0"); } TEST(1b) diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 693acf642..d1b455c68 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -63,7 +63,8 @@ SOURCES = \ dbNetlistTests.cc \ dbNetlistExtractorTests.cc \ dbNetlistDeviceExtractorTests.cc \ - dbNetlistDeviceClassesTests.cc + dbNetlistDeviceClassesTests.cc \ + dbLayoutToNetlistTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC