diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 7a30f7331..0da3ca0ca 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -159,7 +159,9 @@ SOURCES = \ dbDeviceClass.cc \ dbNet.cc \ dbSubCircuit.cc \ - dbPin.cc + dbPin.cc \ + dbLayoutToNetlistReader.cc \ + dbLayoutToNetlistWriter.cc HEADERS = \ dbArray.h \ @@ -283,7 +285,9 @@ HEADERS = \ dbDevice.h \ dbDeviceClass.h \ dbPin.h \ - dbSubCircuit.h + dbSubCircuit.h \ + dbLayoutToNetlistReader.h \ + dbLayoutToNetlistWriter.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 70f23be24..b677bcfff 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -340,7 +340,10 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator layout_index = (unsigned int) m_layouts.size (); m_layouts.push_back (new LayoutHolder ()); - m_layouts.back ()->layout.dbu (si.layout ()->dbu ()); + + db::Layout &layout = m_layouts.back ()->layout; + layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier); + layout.dbu (si.layout ()->dbu ()); m_layout_map[si] = layout_index; @@ -378,6 +381,12 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator return DeepLayer (this, layout_index, layer_index); } +void +DeepShapeStore::invalidate_hier () +{ + m_delivery_mapping_cache.clear (); +} + const db::CellMapping & DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell) { @@ -409,8 +418,6 @@ DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_ // create from them. We need to consider however, that the hierarchy builder is allowed to create // variants which we cannot map. - bool any_skipped = false; - for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ++m) { HierarchyBuilder::cell_map_type::const_iterator mm = m; @@ -425,16 +432,13 @@ DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_ if (! skip) { cm->second.map (m->first.first, m->second); - } else { - any_skipped = true; } } - if (any_skipped) { - // Add new cells for the variants - cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top); - } + // Add new cells for the variants and (possible) devices which are cells added during the device + // extraction process + cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top); } else if (into_layout->cells () == 1) { diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 89d109cdd..0c658eb17 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -420,6 +420,7 @@ private: struct LayoutHolder; + void invalidate_hier (); void add_ref (unsigned int layout, unsigned int layer); void remove_ref (unsigned int layout, unsigned int layer); diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index add76bec0..8e0f6ce54 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -31,7 +31,7 @@ namespace db // Device class implementation Device::Device () - : mp_device_class (0), m_id (0), mp_circuit (0) + : mp_device_class (0), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) { // .. nothing yet .. } @@ -46,13 +46,13 @@ Device::~Device () } Device::Device (DeviceClass *device_class, const std::string &name) - : mp_device_class (device_class), m_name (name), m_id (0), mp_circuit (0) + : mp_device_class (device_class), m_name (name), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) { // .. nothing yet .. } Device::Device (const Device &other) - : tl::Object (other), mp_device_class (0), m_id (0), mp_circuit (0) + : tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), m_id (0), mp_circuit (0) { operator= (other); } @@ -61,6 +61,10 @@ Device &Device::operator= (const Device &other) { if (this != &other) { m_name = other.m_name; + m_position = other.m_position; + m_cell_index = other.m_cell_index; + m_terminal_cluster_ids = other.m_terminal_cluster_ids; + m_parameters = other.m_parameters; mp_device_class = other.mp_device_class; } return *this; @@ -79,6 +83,29 @@ void Device::set_name (const std::string &n) } } +void Device::set_position (const db::DPoint &pt) +{ + m_position = pt; +} + +void Device::set_cell_index (db::cell_index_type ci) +{ + m_cell_index = ci; +} + +size_t Device::cluster_id_for_terminal (size_t terminal_id) const +{ + return terminal_id < m_terminal_cluster_ids.size () ? m_terminal_cluster_ids [terminal_id] : 0; +} + +void Device::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id) +{ + if (m_terminal_cluster_ids.size () <= terminal_id) { + m_terminal_cluster_ids.resize (terminal_id + 1, 0); + } + m_terminal_cluster_ids [terminal_id] = cluster_id; +} + void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter) { if (m_terminal_refs.size () < terminal_id + 1) { diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index c918cbe35..8074e66fb 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -25,6 +25,7 @@ #include "dbCommon.h" #include "dbNet.h" +#include "dbPoint.h" #include "tlObject.h" @@ -127,6 +128,49 @@ public: return m_name; } + /** + * @brief Sets the device position + * The device position should be the center of the recognition shape or something similar. + * Giving the device a position allows combining multiple devices with the same + * relative geometry into a single cell. + * The position has to be given in micrometer units. + */ + void set_position (const db::DPoint &pos); + + /** + * @brief Gets the device position + */ + const db::DPoint &position () const + { + return m_position; + } + + /** + * @brief Sets the device cell index + * In the layout, a device is represented by a cell. This attribute gives the index of this + * cell. + */ + void set_cell_index (db::cell_index_type ci); + + /** + * @brief Gets the device cell index + */ + db::cell_index_type cell_index () const + { + return m_cell_index; + } + + /** + * @brief Gets the cluster ID for a given terminal + * This attribute connects the device terminal with a terminal cluster + */ + size_t cluster_id_for_terminal (size_t terminal_id) const; + + /** + * @brief Sets the cluster ID for a given terminal + */ + void set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id); + /** * @brief Gets the net attached to a specific terminal * Returns 0 if no net is attached. @@ -180,7 +224,10 @@ private: DeviceClass *mp_device_class; std::string m_name; + db::DPoint m_position; + db::cell_index_type m_cell_index; std::vector m_terminal_refs; + std::vector m_terminal_cluster_ids; std::vector m_parameters; size_t m_id; Circuit *mp_circuit; diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 71b74530b..2f41ea28e 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1664,6 +1664,11 @@ template void hier_clusters::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn) { + if (m_per_cell_clusters.find (cell.cell_index ()) != m_per_cell_clusters.end ()) { + // skip pre-build clusters (from devices) + return; + } + std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ())); if (tl::verbosity () >= 40) { tl::log << msg; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index c70fcef69..57896e388 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -110,7 +110,7 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co if (! mp_netlist.get ()) { mp_netlist.reset (new db::Netlist ()); } - extractor.extract(m_dss, layers, mp_netlist.get ()); + extractor.extract(m_dss, layers, *mp_netlist, m_net_clusters); } void LayoutToNetlist::connect (const db::Region &l) @@ -183,7 +183,12 @@ void LayoutToNetlist::extract_netlist () if (! mp_netlist.get ()) { mp_netlist.reset (new db::Netlist ()); } - m_netex.extract_nets(m_dss, m_conn, mp_netlist.get ()); + + m_net_clusters.clear (); + + db::NetlistExtractor netex; + netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters); + m_netlist_extracted = true; } @@ -227,14 +232,6 @@ 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 (); -} - template static void deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) { @@ -261,26 +258,26 @@ static void deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const T } template -static void deliver_shapes_of_net_recursive (const db::NetlistExtractor &netex, const db::Net &net, unsigned int layer_id, To &to) +static void deliver_shapes_of_net_recursive (const db::hier_clusters &clusters, const db::Net &net, unsigned int layer_id, To &to) { 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 (netex.clusters (), layer_id, ci, net.cluster_id ()); !rci.at_end (); ++rci) { + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, net.cluster_id ()); !rci.at_end (); ++rci) { deliver_shape (*rci, to, rci.trans ()); } } template -static void deliver_shapes_of_net_nonrecursive (const db::NetlistExtractor &netex, const db::Net &net, unsigned int layer_id, To &to) +static void deliver_shapes_of_net_nonrecursive (const db::hier_clusters &clusters, const db::Net &net, unsigned int layer_id, To &to) { const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); db::cell_index_type ci = circuit->cell_index (); - const db::local_cluster &lc = netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); + const db::local_cluster &lc = clusters.clusters_per_cell (ci).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (layer_id); !s.at_end (); ++s) { deliver_shape (*s, to, db::UnitTrans ()); @@ -292,9 +289,9 @@ void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_la unsigned int lid = layer_of (of_layer); if (! recursive) { - deliver_shapes_of_net_nonrecursive (m_netex, net, lid, to); + deliver_shapes_of_net_nonrecursive (m_net_clusters, net, lid, to); } else { - deliver_shapes_of_net_recursive (m_netex, net, lid, to); + deliver_shapes_of_net_recursive (m_net_clusters, net, lid, to); } } @@ -304,9 +301,9 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region std::auto_ptr res (new db::Region ()); if (! recursive) { - deliver_shapes_of_net_nonrecursive (m_netex, net, lid, *res); + deliver_shapes_of_net_nonrecursive (m_net_clusters, net, lid, *res); } else { - deliver_shapes_of_net_recursive (m_netex, net, lid, *res); + deliver_shapes_of_net_recursive (m_net_clusters, net, lid, *res); } return res.release (); @@ -326,7 +323,7 @@ LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); - const db::connected_clusters &clusters = m_netex.clusters ().clusters_per_cell (circuit->cell_index ()); + const db::connected_clusters &clusters = m_net_clusters.clusters_per_cell (circuit->cell_index ()); typedef db::connected_clusters::connections_type connections_type; const connections_type &connections = clusters.connections_for_cluster (net.cluster_id ()); for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) { @@ -390,7 +387,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target continue; } - const db::connected_clusters &ccl = m_netex.clusters ().clusters_per_cell (c->cell_index ()); + const db::connected_clusters &ccl = m_net_clusters.clusters_per_cell (c->cell_index ()); const db::local_cluster &cl = ccl.cluster_by_id (n->cluster_id ()); bool any_connections = ! ccl.connections_for_cluster (n->cluster_id ()).empty (); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4668bb9e6..af30a3436 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -197,6 +197,14 @@ public: */ const db::Cell *internal_top_cell () const; + /** + * @brief Gets the connectivity object + */ + const db::Connectivity &connectivity () const + { + return m_conn; + } + /** * @brief Gets the internal layer for a given extraction layer * This method is required to derive the internal layer index - for example for @@ -227,7 +235,10 @@ public: * NOTE: the layer and cell indexes used inside this structure refer to the * internal layout. */ - const db::hier_clusters &net_clusters () const; + const db::hier_clusters &net_clusters () const + { + return m_net_clusters; + } /** * @brief Returns all shapes of a specific net and layer. @@ -327,7 +338,7 @@ private: db::RecursiveShapeIterator m_iter; db::DeepShapeStore m_dss; db::Connectivity m_conn; - db::NetlistExtractor m_netex; + db::hier_clusters m_net_clusters; std::auto_ptr mp_netlist; std::set m_dlrefs; bool m_netlist_extracted; diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc new file mode 100644 index 000000000..192a6057d --- /dev/null +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -0,0 +1,30 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutToNetlistReader.h" + +namespace db +{ + + + +} diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h new file mode 100644 index 000000000..038820ac2 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -0,0 +1,35 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutToNetlistReader +#define HDR_dbLayoutToNetlistReader + +#include "dbCommon.h" + +namespace db { + +// ... + +} + +#endif + diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc new file mode 100644 index 000000000..8bad67fe8 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -0,0 +1,295 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutToNetlistWriter.h" +#include "dbLayoutToNetlist.h" + +namespace db +{ + + +// ------------------------------------------------------------------------------------------- +// LayoutToNetlistStandardWriter implementation + +/** + * Comments are introduced by hash: # ... + * Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes. + * Escape character is backslash. + * Separator is either , or whitespace. Keywords and names are case sensitive. + * Short keys are provided for compacter representation. Short keys can be + * non-alpha (e.g. "*") or empty. + * Single-valued attributes can be given without brackets. + * All dimensions are in units of database unit. + * + * Global statements: + * + * description() - an arbitrary description text + * dbu() - specifies the database unit [short key: D] + * top() - specifies the name of the top circuit [short key: T] + * layer() - define a layer [short key: L] + * connect( ...) - connects layer1 with the following layers [short key: C] + * global( ...) - connects a layer with the given global nets [short key: G] + * circuit( ...) - defines a circuit (cell) [short key: X] + * + * Inside the circuit: + * + * net( ...) - specifies net geometry [short key: N] + * device( ...) - defines a device [short key: D] + * subcircuit( ...) - defines a subcircuit [short key: X] + * + * Inside a net: + * + * polygon( ...) - defines a polygon [short key: P] + * rect( ) + * - defines a rectangle [short key: R] + * + * Inside a device: + * + * param( ) - defines a parameter [short key P] + * terminal( ) + * - specifies connection of the terminal with + * a net (short key: empty) + * location( ) - location of the device [short key L] + * + * Inside a subcircuit: + * + * location( ) - location of the subcircuit [short key L] + * rotation() - rotation angle [short key O] + * mirror - if specified, the instance is mirrored before rotation [short key M] + * scale() - magnification [short key *] + * pin( ) - specifies connection of the pin with a net + */ + +static std::string description_key ("description"); +static std::string top_key ("top"); +static std::string dbu_key ("dbu"); +static std::string layer_key ("layer"); +static std::string text_key ("text"); +static std::string connect_key ("connect"); +static std::string global_key ("global"); +static std::string circuit_key ("circuit"); +static std::string net_key ("net"); +static std::string device_key ("device"); +static std::string subcircuit_key ("subcircuit"); +static std::string polygon_key ("polygon"); +static std::string rect_key ("rect"); +static std::string terminal_key ("terminal"); +static std::string label_key ("label"); +static std::string param_key ("param"); +static std::string location_key ("location"); +static std::string rotation_key ("rotation"); +static std::string mirror_key ("mirror"); +static std::string scale_key ("scale"); +static std::string pin_key ("pin"); +static std::string indent1 (" "); +static std::string indent2 (" "); +static std::string endl ("\n"); + +LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream) + : mp_stream (&stream) +{ + // .. nothing yet .. +} + +static std::string name_for_layer (const db::Layout *layout, unsigned int l) +{ + const db::LayerProperties &lp = layout->get_properties (l); + if (lp.is_named ()) { + return tl::to_word_or_quoted_string (lp.name); + } else { + return "L" + tl::to_string (l); + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) +{ + const db::Layout *ly = l2n->internal_layout (); + const db::Netlist *nl = l2n->netlist (); + + *mp_stream << top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; + *mp_stream << dbu_key << "(" << ly->dbu () << ")" << endl; + + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + + *mp_stream << layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; + + db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); + db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l); + if (cb != ce) { + *mp_stream << connect_key << "(" << name_for_layer (ly, *l); + for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { + *mp_stream << " " << name_for_layer (ly, *l); + } + *mp_stream << ")" << endl; + } + + db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l); + db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l); + if (gb != ge) { + *mp_stream << global_key << "(" << name_for_layer (ly, *l); + for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { + *mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g)); + } + *mp_stream << ")" << endl; + } + + } + + for (db::Netlist::const_circuit_iterator x = nl->begin_circuits (); x != nl->end_circuits (); ++x) { + *mp_stream << circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; + write (l2n, *x); + *mp_stream << ")" << endl; + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit) +{ + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + write (l2n, *n); + } + + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { + write (l2n, *d); + } + + for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { + write (l2n, *x); + } +} + +template +void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) +{ + for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) { + typename T::point_type pt = tr * *c; + stream << " " << pt.x () << " " << pt.y (); + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Net &net) +{ + const db::Layout *ly = l2n->internal_layout (); + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Circuit *circuit = net.circuit (); + const db::Connectivity &conn = l2n->connectivity (); + + *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + const db::local_cluster &lc = clusters.clusters_per_cell (circuit->cell_index ()).cluster_by_id (net.cluster_id ()); + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + + *mp_stream << indent2; + + const db::Polygon &poly = s->obj (); + if (poly.is_box ()) { + + db::Box box = s->trans () * poly.box (); + *mp_stream << rect_key << "(" << name_for_layer (ly, *l); + *mp_stream << " " << box.left () << " " << box.bottom (); + *mp_stream << " " << box.right () << " " << box.top (); + *mp_stream << ")"; + + } else { + + *mp_stream << polygon_key << "(" << name_for_layer (ly, *l); + if (poly.holes () > 0) { + db::SimplePolygon sp (poly); + write_points (*mp_stream, sp, s->trans ()); + } else { + write_points (*mp_stream, poly, s->trans ()); + } + *mp_stream << ")"; + + } + + } + + } + + *mp_stream << indent1 << ")" << endl; +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::SubCircuit &subcircuit) +{ + *mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()); + + const db::DCplxTrans &tr = subcircuit.trans (); + if (tr.is_mag ()) { + *mp_stream << " " << scale_key << "(" << tr.mag () << ")"; + } + if (tr.is_mirror ()) { + *mp_stream << " " << mirror_key; + } + if (fabs (tr.angle ()) > 1e-6) { + *mp_stream << " " << rotation_key << "(" << tr.angle () << ")"; + } + *mp_stream << " " << location_key << "(" << tr.disp ().x () << " " << tr.disp ().y () << ")"; + + // each pin in one line for more than 16 pins + bool separate_lines = (subcircuit.circuit ()->pin_count () > 16); + + if (separate_lines) { + *mp_stream << endl; + } + + for (db::Circuit::const_pin_iterator p = subcircuit.circuit ()->begin_pins (); p != subcircuit.circuit ()->end_pins (); ++p) { + if (separate_lines) { + *mp_stream << indent2; + } else { + *mp_stream << " "; + } + *mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_word_or_quoted_string (subcircuit.net_for_pin (p->id ())->expanded_name ()) << ")"; + if (separate_lines) { + *mp_stream << endl; + } + } + + if (separate_lines) { + *mp_stream << indent1; + } + + *mp_stream << ")" << endl; +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::Device &device) +{ + // @@@ TODO: add location + + *mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.name ()); + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()); + + const std::vector &pd = device.device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + *mp_stream << " " << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")"; + } + + const std::vector &td = device.device_class ()->terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + *mp_stream << " " << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")"; + } + + *mp_stream << ")" << endl; +} + +} diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h new file mode 100644 index 000000000..f158f8911 --- /dev/null +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -0,0 +1,72 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutToNetlistWriter +#define HDR_dbLayoutToNetlistWriter + +#include "dbCommon.h" +#include "tlStream.h" + +namespace db +{ + +class LayoutToNetlist; +class Net; +class Circuit; +class SubCircuit; +class Device; + +/** + * @brief The base class for a LayoutToNetlist writer + */ +class DB_PUBLIC LayoutToNetlistWriterBase +{ +public: + LayoutToNetlistWriterBase () { } + virtual ~LayoutToNetlistWriterBase () { } + + virtual void write (const db::LayoutToNetlist *l2n) = 0; +}; + +/** + * @brief The standard writer + */ +class DB_PUBLIC LayoutToNetlistStandardWriter + : public LayoutToNetlistWriterBase +{ +public: + LayoutToNetlistStandardWriter (tl::OutputStream &stream); + + void write (const db::LayoutToNetlist *l2n); + +private: + tl::OutputStream *mp_stream; + + void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit); + void write (const db::LayoutToNetlist *l2n, const db::Net &net); + void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); + void write (const db::LayoutToNetlist *l2n, const db::Device &device); +}; + +} + +#endif diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 16bac404e..f8c44f331 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -50,7 +50,9 @@ NetlistDeviceExtractor::NetlistDeviceExtractor (const std::string &name) : mp_layout (0), m_cell_index (0), mp_circuit (0) { m_name = name; - m_propname_id = 0; + m_terminal_id_propname_id = 0; + m_device_class_propname_id = 0; + m_device_id_propname_id = 0; } NetlistDeviceExtractor::~NetlistDeviceExtractor () @@ -58,9 +60,21 @@ NetlistDeviceExtractor::~NetlistDeviceExtractor () // .. nothing yet .. } -const tl::Variant &NetlistDeviceExtractor::terminal_property_name () +const tl::Variant &NetlistDeviceExtractor::terminal_id_property_name () { - static tl::Variant name ("TERMINAL"); + static tl::Variant name ("TERMINAL_ID"); + return name; +} + +const tl::Variant &NetlistDeviceExtractor::device_id_property_name () +{ + static tl::Variant name ("DEVICE_ID"); + return name; +} + +const tl::Variant &NetlistDeviceExtractor::device_class_property_name () +{ + static tl::Variant name ("DEVICE_CLASS"); return name; } @@ -68,7 +82,9 @@ void NetlistDeviceExtractor::initialize (db::Netlist *nl) { m_layer_definitions.clear (); mp_device_class = 0; - m_propname_id = 0; + m_terminal_id_propname_id = 0; + m_device_id_propname_id = 0; + m_device_class_propname_id = 0; m_netlist.reset (nl); setup (); @@ -79,9 +95,9 @@ static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &t region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ()))); } -void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDeviceExtractor::input_layers &layer_map, db::Netlist *nl) +void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDeviceExtractor::input_layers &layer_map, db::Netlist &nl, hier_clusters_type &clusters) { - initialize (nl); + initialize (&nl); std::vector layers; layers.reserve (m_layer_definitions.size ()); @@ -107,16 +123,16 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDevi } - extract_without_initialize (dss.layout (), dss.initial_cell (), layers); + extract_without_initialize (dss.layout (), dss.initial_cell (), clusters, layers); } -void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl) +void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector &layers, db::Netlist *nl, hier_clusters_type &clusters) { initialize (nl); - extract_without_initialize (layout, cell, layers); + extract_without_initialize (layout, cell, clusters, layers); } -void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers) +void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers) { tl_assert (layers.size () == m_layer_definitions.size ()); @@ -125,9 +141,12 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: mp_layout = &layout; m_layers = layers; + mp_clusters = &clusters; // terminal properties are kept in a property with the terminal_property_name name - m_propname_id = mp_layout->properties_repository ().prop_name_id (terminal_property_name ()); + m_terminal_id_propname_id = mp_layout->properties_repository ().prop_name_id (terminal_id_property_name ()); + m_device_id_propname_id = mp_layout->properties_repository ().prop_name_id (device_id_property_name ()); + m_device_class_propname_id = mp_layout->properties_repository ().prop_name_id (device_class_property_name ()); tl_assert (m_netlist.get () != 0); @@ -150,6 +169,11 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: // for each cell investigate the clusters for (std::set::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) { + // skip device cells from previous extractions + if (is_device_cell (*ci)) { + continue; + } + m_cell_index = *ci; std::map::const_iterator c2c = circuits_by_cell.find (*ci); @@ -192,11 +216,116 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db: // do the actual device extraction extract_devices (layer_geometry); + // push the new devices to the layout + push_new_devices (); + } } } +bool NetlistDeviceExtractor::is_device_cell (db::cell_index_type ci) const +{ + db::properties_id_type pi = mp_layout->cell (ci).prop_id (); + if (pi == 0) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_class_propname_id) { + return true; + } + } + + return false; +} + +void NetlistDeviceExtractor::push_new_devices () +{ + db::VCplxTrans dbu_inv = db::CplxTrans (mp_layout->dbu ()).inverted (); + + for (std::map::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) { + + db::Vector disp = dbu_inv * d->first->position () - db::Point (); + + DeviceCellKey key; + + for (geometry_per_terminal_type::const_iterator t = d->second.begin (); t != d->second.end (); ++t) { + std::map > > = key.geometry [t->first]; + for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { + std::set &gl = gt [l->first]; + for (std::vector::const_iterator p = l->second.begin (); p != l->second.end (); ++p) { + db::PolygonRef pr = *p; + pr.transform (db::PolygonRef::trans_type (-disp)); + gl.insert (pr); + } + } + } + + const std::vector &pd = mp_device_class->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + key.parameters.insert (std::make_pair (p->id (), d->first->parameter_value (p->id ()))); + } + + db::PropertiesRepository::properties_set ps; + + std::map::iterator c = m_device_cells.find (key); + if (c == m_device_cells.end ()) { + + std::string cell_name = "D$" + mp_device_class->name (); + db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ())); + c = m_device_cells.insert (std::make_pair (key, device_cell.cell_index ())).first; + + // attach the device class ID to the cell + ps.clear (); + ps.insert (std::make_pair (m_device_class_propname_id, tl::Variant (mp_device_class->name ()))); + device_cell.prop_id (mp_layout->properties_repository ().properties_id (ps)); + + db::connected_clusters &cc = mp_clusters->clusters_per_cell (device_cell.cell_index ()); + + for (geometry_per_terminal_type::const_iterator t = d->second.begin (); t != d->second.end (); ++t) { + + // Build a property set for the device terminal ID + ps.clear (); + ps.insert (std::make_pair (m_terminal_id_propname_id, tl::Variant (t->first))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + // initialize the local cluster (will not be extracted) + db::local_cluster *lc = cc.insert (); + lc->add_attr (pi); + + // build the cell shapes and local cluster + for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) { + db::Shapes &shapes = device_cell.shapes (l->first); + for (std::vector::const_iterator s = l->second.begin (); s != l->second.end (); ++s) { + db::PolygonRef pr = *s; + pr.transform (db::PolygonRef::trans_type (-disp)); + shapes.insert (db::PolygonRefWithProperties (pr, pi)); + lc->add (*s, l->first); + } + } + + } + + } + + // make the cell index known to the device + d->first->set_cell_index (c->second); + + // Build a property set for the device ID + ps.clear (); + ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first->id ()))); + db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); + + db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second), db::Trans (disp)), pi); + mp_layout->cell (m_cell_index).insert (inst); + + } + + m_new_devices.clear (); +} + void NetlistDeviceExtractor::setup () { // .. the default implementation does nothing .. @@ -253,14 +382,8 @@ void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id tl_assert (geometry_index < m_layers.size ()); unsigned int layer_index = m_layers [geometry_index]; - // Build a property set for the DeviceTerminalProperty - db::PropertiesRepository::properties_set ps; - tl::Variant &v = ps.insert (std::make_pair (m_propname_id, tl::Variant ()))->second; - v = tl::Variant (new db::DeviceTerminalProperty (device->id (), terminal_id), db::NetlistProperty::variant_class (), true); - db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps); - db::PolygonRef pr (polygon, mp_layout->shape_repository ()); - mp_layout->cell (m_cell_index).shapes (layer_index).insert (db::PolygonRefWithProperties (pr, pi)); + m_new_devices[device][terminal_id][layer_index].push_back (pr); } void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box) diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index a2a845eb5..bf6281919 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -194,6 +194,7 @@ public: typedef std::vector layer_definitions; typedef layer_definitions::const_iterator layer_definitions_iterator; typedef std::map input_layers; + typedef db::hier_clusters hier_clusters_type; /** * @brief Constructor @@ -217,11 +218,21 @@ public: /** * @brief Gets the property name for the device terminal annotation - * Annotation happens through db::DeviceTerminalProperty objects attached to - * the terminal shapes. - * The name used for the property is the one returned by this function. + * This name is used to attach the terminal ID to terminal shapes. */ - static const tl::Variant &terminal_property_name (); + static const tl::Variant &terminal_id_property_name (); + + /** + * @brief Gets the property name for the device id annotation + * This name is used to attach the device ID to instances. + */ + static const tl::Variant &device_id_property_name (); + + /** + * @brief Gets the property name for the device class annotation + * This name is used to attach the device class name to cells. + */ + static const tl::Variant &device_class_property_name (); /** * @brief Performs the extraction @@ -241,7 +252,7 @@ public: * * NOTE: The extractor expects "PolygonRef" type layers. */ - void extract (Layout &layout, Cell &cell, const std::vector &layers, Netlist *netlist); + void extract (Layout &layout, Cell &cell, const std::vector &layers, Netlist *netlist, hier_clusters_type &clusters); /** * @brief Extracts the devices from a list of regions @@ -250,7 +261,7 @@ public: * named regions for input. These regions need to be of deep region type and * originate from the same layout than the DeepShapeStore. */ - void extract (DeepShapeStore &dss, const input_layers &layers, Netlist *netlist); + void extract (DeepShapeStore &dss, const input_layers &layers, Netlist &netlist, hier_clusters_type &clusters); /** * @brief Gets the error iterator, begin @@ -452,9 +463,43 @@ protected: std::string cell_name () const; private: + struct DeviceCellKey + { + DeviceCellKey () { } + + bool operator== (const DeviceCellKey &other) const + { + if (geometry != other.geometry) { + return false; + } + if (parameters != other.parameters) { + return false; + } + return true; + } + + bool operator< (const DeviceCellKey &other) const + { + if (geometry != other.geometry) { + return geometry < other.geometry; + } + if (parameters != other.parameters) { + return parameters < other.parameters; + } + return false; + } + + std::map > > geometry; + std::map parameters; + }; + + typedef std::map > geometry_per_layer_type; + typedef std::map geometry_per_terminal_type; + tl::weak_ptr m_netlist; db::Layout *mp_layout; - db::properties_id_type m_propname_id; + db::properties_id_type m_terminal_id_propname_id, m_device_id_propname_id, m_device_class_propname_id; + hier_clusters_type *mp_clusters; db::cell_index_type m_cell_index; db::Circuit *mp_circuit; db::DeviceClass *mp_device_class; @@ -462,6 +507,8 @@ private: layer_definitions m_layer_definitions; std::vector m_layers; error_list m_errors; + std::map m_new_devices; + std::map m_device_cells; // no copying NetlistDeviceExtractor (const NetlistDeviceExtractor &); @@ -473,7 +520,9 @@ private: */ void initialize (db::Netlist *nl); - void extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector &layers); + void extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector &layers); + void push_new_devices (); + bool is_device_cell (db::cell_index_type ci) const; }; } diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 712ae0b9f..79f344f94 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -98,6 +98,8 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vectorset_position (db::CplxTrans (dbu ()) * p->box ().center ()); + device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5); device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5); diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 538e46d39..de0e4c118 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -29,45 +29,50 @@ namespace db { NetlistExtractor::NetlistExtractor () + : mp_clusters (0), mp_layout (0), mp_cell (0) { // .. nothing yet .. } void -NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) +NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters) { - const db::Layout &layout = dss.const_layout (); - const db::Cell &cell = dss.const_initial_cell (); + mp_clusters = &clusters; + mp_layout = &dss.const_layout (); + mp_cell = &dss.const_initial_cell (); // gets the text annotation property ID - // this is how the texts are passed for annotating the net names - std::pair text_annot_name_id (false, 0); + m_text_annot_name_id = std::pair (false, 0); if (! dss.text_property_name ().is_nil ()) { - text_annot_name_id = layout.properties_repository ().get_id_of_name (dss.text_property_name ()); + m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ()); } - // gets the device terminal annotation property ID - - // this is how the device extractor conveys terminal shape annotations. - std::pair terminal_annot_name_id; - terminal_annot_name_id = layout.properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_property_name ()); + m_terminal_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_id_property_name ()); + m_device_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_id_property_name ()); + m_device_class_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_class_property_name ()); // the big part: actually extract the nets - m_net_clusters.build (layout, cell, db::ShapeIterator::Polygons, conn); + mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn); // reverse lookup for Circuit vs. cell index std::map circuits; // some circuits may be there because of device extraction - for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { - tl_assert (layout.is_valid_cell_index (c->cell_index ())); + for (db::Netlist::circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + tl_assert (mp_layout->is_valid_cell_index (c->cell_index ())); circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); } std::map > pins_per_cluster_per_cell; - for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) { + for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) { - const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); + if (cell_is_device_cell (*cid)) { + continue; + } + + const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid); if (clusters.empty ()) { continue; } @@ -79,8 +84,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect std::map::const_iterator k = circuits.find (*cid); if (k == circuits.end ()) { circuit = new db::Circuit (); - nl->add_circuit (circuit); - circuit->set_name (layout.cell_name (*cid)); + nl.add_circuit (circuit); + circuit->set_name (mp_layout->cell_name (*cid)); circuit->set_cell_index (*cid); circuits.insert (std::make_pair (*cid, circuit)); } else { @@ -103,21 +108,13 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters - make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell, layout.dbu ()); + make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell); - // collect the properties - we know that the cluster attributes are property ID's because the - // cluster processor converts shape property IDs to attributes - const local_cluster_type &lc = clusters.cluster_by_id (*c); - for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { - const db::PropertiesRepository::properties_set &ps = layout.properties_repository ().properties (*a); - for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { - if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) { - make_device_terminal_from_property (j->second, circuit, net); - } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { - assign_net_name (j->second.to_string (), net); - } - } - } + // connect devices + connect_devices (circuit, clusters, *c, net); + + // collect labels to net names + collect_labels (clusters, *c, net); if (! clusters.is_root (*c)) { // a non-root cluster makes a pin @@ -130,18 +127,137 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect } } +void NetlistExtractor::collect_labels (const connected_clusters_type &clusters, + size_t cid, + db::Net *net) +{ + // collect the properties - we know that the cluster attributes are property ID's because the + // cluster processor converts shape property IDs to attributes + + const local_cluster_type &lc = clusters.cluster_by_id (cid); + for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (m_text_annot_name_id.first && j->first == m_text_annot_name_id.second) { + assign_net_name (j->second.to_string (), net); + } + + } + + } +} + +bool NetlistExtractor::cell_is_device_cell (db::cell_index_type ci) const +{ + if (! m_device_class_annot_name_id.first) { + return false; + } + + const db::Cell &cell = mp_layout->cell (ci); + if (cell.prop_id () == 0) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (cell.prop_id ()); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_class_annot_name_id.second) { + return true; + } + } + + return false; +} + +bool NetlistExtractor::instance_is_device (db::properties_id_type prop_id) const +{ + if (! prop_id || ! m_device_annot_name_id.first) { + return false; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_annot_name_id.second) { + return true; + } + } + + return false; +} + +db::Device *NetlistExtractor::device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const +{ + if (! prop_id || ! m_device_annot_name_id.first) { + return 0; + } + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + if (j->first == m_device_annot_name_id.second) { + return circuit->device_by_id (j->second.to ()); + } + } + + return 0; +} + +void NetlistExtractor::connect_devices (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net) +{ + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + const db::Instance &inst = i->inst ().inst_ptr; + + // only consider devices in this pass + db::Device *device = device_from_instance (inst.prop_id (), circuit); + if (! device) { + continue; + } + + const db::local_cluster &dc = mp_clusters->clusters_per_cell (inst.cell_index ()).cluster_by_id (i->id ()); + + // connect the net to the terminal of the device: take the terminal ID from the properties on the + // device cluster + for (local_cluster_type::attr_iterator a = dc.begin_attr (); a != dc.end_attr (); ++a) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a); + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { + + if (m_terminal_annot_name_id.first && j->first == m_terminal_annot_name_id.second) { + + size_t tid = j->second.to (); + device->connect_terminal (tid, net); + device->set_cluster_id_for_terminal (tid, i->id ()); + + } + + } + + } + + } +} + void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit, const connected_clusters_type &clusters, size_t cid, db::Net *net, std::map &subcircuits, const std::map &circuits, - const std::map > &pins_per_cluster, - double dbu) + const std::map > &pins_per_cluster) { const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid); for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + // skip devices in this pass + if (instance_is_device (i->inst ().inst_ptr.prop_id ())) { + continue; + } + db::SubCircuit *subcircuit = 0; db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); @@ -154,7 +270,7 @@ void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit, tl_assert (k != circuits.end ()); // because we walk bottom-up subcircuit = new db::SubCircuit (k->second); - db::CplxTrans dbu_trans (dbu); + db::CplxTrans dbu_trans (mp_layout->dbu ()); subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); circuit->add_subcircuit (subcircuit); subcircuits.insert (std::make_pair (i->inst (), subcircuit)); @@ -183,19 +299,6 @@ size_t NetlistExtractor::make_pin (db::Circuit *circuit, db::Net *net) return pin_id; } -void NetlistExtractor::make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net) -{ - if (v.is_user ()) { - const db::NetlistProperty *np = &v.to_user (); - const db::DeviceTerminalProperty *tp = dynamic_cast (np); - if (tp) { - db::Device *device = circuit->device_by_id (tp->device_id ()); - tl_assert (device != 0); - device->connect_terminal (tp->terminal_id (), net); - } - } -} - void NetlistExtractor::assign_net_name (const std::string &n, db::Net *net) { std::string nn = n; diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 30a48047f..70d6f4565 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -36,6 +36,7 @@ class Netlist; class Circuit; class SubCircuit; class Net; +class Device; /** * @brief The Netlist Extractor @@ -80,22 +81,21 @@ public: * @brief Extract the nets * See the class description for more details. */ - void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl); - - /** - * @brief Gets the shape clusters - * See the class description for more details. - */ - const hier_clusters_type &clusters () const - { - return m_net_clusters; - } + void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters); private: - hier_clusters_type m_net_clusters; + hier_clusters_type *mp_clusters; + const db::Layout *mp_layout; + const db::Cell *mp_cell; + std::pair m_text_annot_name_id; + std::pair m_device_annot_name_id; + std::pair m_terminal_annot_name_id; + std::pair m_device_class_annot_name_id; - void make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net); void assign_net_name (const std::string &n, db::Net *net); + bool instance_is_device (db::properties_id_type prop_id) const; + bool cell_is_device_cell (db::cell_index_type ci) const; + db::Device *device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const; /** * @brief Make a pin connection from clusters @@ -120,8 +120,6 @@ private: * - circuits: a lookup table of circuits vs. cell index (reverse lookup) * - pins_per_cluster: a per-cell, reverse lookup table for the pin id per child cell clusters * (used to find the pin ID of a subcircuit from the child cell cluster ID) - * - dbu: the database unit (used to compute the micron-unit transformation of the child - * cell) * Updates: * - subcircuits: An cell instance to SubCircuit lookup table */ @@ -131,8 +129,30 @@ private: db::Net *net, std::map &subcircuits, const std::map &circuits, - const std::map > &pins_per_cluster, - double dbu); + const std::map > &pins_per_cluster); + + /** + * @brief Connects the devices + * + * Devices are identified by special cells. These carry a property with the + * device class name. Inside these cells, the terminals are identified by special clusters. + * The terminal IDs are coded on these clusters are a property. + */ + void connect_devices (db::Circuit *circuit, + const connected_clusters_type &clusters, + size_t cid, + db::Net *net); + + /** + * @brief Attaches net names from text properties + * + * Texts (labels) are represented by special shapes. The texts are kept as properties. + * This method will collect all these labels and attach them to the nets as (alternative) + * names. + */ + void collect_labels (const connected_clusters_type &clusters, + size_t cid, + db::Net *net); }; } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 36c1d1ffd..28219f41b 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -51,70 +51,6 @@ static unsigned int layer_of (const db::Region ®ion) return dr->deep_layer ().layer (); } -static std::string device_name (const db::Device &device) -{ - if (device.name ().empty ()) { - return "$" + tl::to_string (device.id ()); - } else { - return device.name (); - } -} - -namespace -{ - -class MOSFET3Extractor - : public db::NetlistDeviceExtractorMOS3Transistor -{ -public: - MOSFET3Extractor (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 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)); @@ -124,6 +60,8 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la 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) { + std::set device_cells_seen; + 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 ())); @@ -154,6 +92,40 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< } + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + + if (device_cells_seen.find (d->cell_index ()) != device_cells_seen.end ()) { + continue; + } + + db::Cell &device_cell = ly.cell (cmap.cell_mapping (d->cell_index ())); + device_cells_seen.insert (d->cell_index ()); + + std::string ps; + const std::vector &pd = d->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 (d->parameter_value (i->id ())); + } + + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::local_cluster &dc = clusters.clusters_per_cell (d->cell_index ()).cluster_by_id (d->cluster_id_for_terminal (t->id ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = device_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = dc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } + } + + } + + } + } } @@ -235,24 +207,22 @@ TEST(1_DeviceAndNetExtraction) // perform the extraction db::Netlist nl; + db::hier_clusters cl; - // NOTE: the device extractor will add more debug layers for the transistors: - // 20/0 -> Diffusion - // 21/0 -> Gate - MOSFET3Extractor pmos_ex ("PMOS", &ly); - MOSFET3Extractor nmos_ex ("NMOS", &ly); + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); db::NetlistDeviceExtractor::input_layers dl; dl["SD"] = &rpsd; dl["G"] = &rpgate; dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes - pmos_ex.extract (dss, dl, &nl); + pmos_ex.extract (dss, dl, nl, cl); dl["SD"] = &rnsd; dl["G"] = &rngate; dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes - nmos_ex.extract (dss, dl, &nl); + nmos_ex.extract (dss, dl, nl, cl); // perform the net extraction @@ -282,7 +252,7 @@ TEST(1_DeviceAndNetExtraction) // extract the nets - net_ex.extract_nets (dss, conn, &nl); + net_ex.extract_nets (dss, conn, nl, cl); // debug layers produced for nets // 202/0 -> Active @@ -306,7 +276,7 @@ TEST(1_DeviceAndNetExtraction) // write nets to layout db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); - dump_nets_to_layout (nl, net_ex.clusters (), ly, dump_map, cm); + dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string EXPECT_EQ (nl.to_string (), diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds index a970a662c..903b6bf0d 100644 Binary files a/testdata/algo/device_extract_au1.gds and b/testdata/algo/device_extract_au1.gds differ