diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 358d07c6e..eea29866c 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -177,7 +177,7 @@ struct DeepShapeStore::LayoutHolder static size_t s_instance_count = 0; DeepShapeStore::DeepShapeStore () - : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16) + : m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_text_property_name (), m_text_enlargement (-1) { ++s_instance_count; } @@ -192,6 +192,16 @@ DeepShapeStore::~DeepShapeStore () m_layouts.clear (); } +void DeepShapeStore::set_text_enlargement (int enl) +{ + m_text_enlargement = enl; +} + +void DeepShapeStore::set_text_property_name (const tl::Variant &pn) +{ + m_text_property_name = pn; +} + bool DeepShapeStore::is_valid_layout_index (unsigned int n) const { return (n < (unsigned int) m_layouts.size () && m_layouts[n] != 0); @@ -280,11 +290,14 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator } - unsigned int layer_index = m_layouts[layout_index]->layout.insert_layer (); - m_layouts[layout_index]->builder.set_target_layer (layer_index); + db::Layout &layout = m_layouts[layout_index]->layout; + db::HierarchyBuilder &builder = m_layouts[layout_index]->builder; + + unsigned int layer_index = layout.insert_layer (); + builder.set_target_layer (layer_index); // The chain of operators for producing clipped and reduced polygon references - db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& m_layouts[layout_index]->layout); + db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& layout, m_text_enlargement, m_text_property_name); db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count); db::ClippingHierarchyBuilderShapeReceiver clip (&red); @@ -293,35 +306,36 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Building working hierarchy"))); - m_layouts[layout_index]->builder.set_shape_receiver (&clip); - db::RecursiveShapeIterator (si).push (& m_layouts[layout_index]->builder); - m_layouts[layout_index]->builder.set_shape_receiver (0); + builder.set_shape_receiver (&clip); + db::RecursiveShapeIterator (si).push (& builder); + builder.set_shape_receiver (0); } catch (...) { - m_layouts[layout_index]->builder.set_shape_receiver (0); + builder.set_shape_receiver (0); throw; } return DeepLayer (this, layout_index, layer_index); } -void -DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) +const db::CellMapping & +DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell) { - const db::Layout *source_layout = deep_layer.layout (); + const db::Layout *source_layout = &m_layouts [layout_index]->layout; if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { // empty source - nothing to do. - return; + static db::CellMapping cm; + return cm; } db::cell_index_type source_top = *source_layout->begin_top_down(); - db::HierarchyBuilder &original_builder = m_layouts [deep_layer.layout_index ()]->builder; + db::HierarchyBuilder &original_builder = m_layouts [layout_index]->builder; // Derive a cell mapping for source to target. We reuse any existing mapping for returning the // shapes into the original layout. - DeliveryMappingCacheKey key (deep_layer.layout_index (), tl::id_of (into_layout), into_cell); + DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell); std::map::iterator cm = m_delivery_mapping_cache.find (key); if (cm == m_delivery_mapping_cache.end ()) { @@ -375,16 +389,34 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db } - // Actually copy the shapes + return cm->second; +} +void +DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) +{ + const db::Layout *source_layout = deep_layer.layout (); + if (source_layout->begin_top_down () == source_layout->end_top_cells ()) { + // empty source - nothing to do. + return; + } + + // prepare the transformation db::ICplxTrans trans (source_layout->dbu () / into_layout->dbu ()); + // prepare a layer map std::map lm; lm.insert (std::make_pair (deep_layer.layer (), into_layer)); + // prepare a cell mapping + const db::CellMapping &cm = cell_mapping_to_original (deep_layer.layout_index (), into_layout, into_cell); + + // prepare a vector with the source cells std::vector source_cells; - source_cells.push_back (source_top); - db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm->second.table (), lm); + source_cells.push_back (*source_layout->begin_top_down()); + + // actually copy the shapes + db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm.table (), lm); } } diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 10553c268..d944260a9 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -202,6 +202,15 @@ public: */ void insert (const DeepLayer &layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer); + /** + * @brief Gets the cell mapping suitable to returning a layout from the deep shape store into the original layout hierarchy + * + * If necessary, this method will modify the original layout and add new cells. + * "layout_index" is the layout to return to it's original. "into_layout" is the original layout, "into_cell" + * the original cell. + */ + const db::CellMapping &cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell); + /** * @brief For testing */ @@ -274,6 +283,42 @@ public: return m_max_area_ratio; } + /** + * @brief Sets the text property name + * + * If set to a non-null variant, text strings are attached to the generated boxes + * as properties with this particular name. This option has an effect only if the + * text_enlargement property is not negative. + * By default, the name is empty. + */ + void set_text_property_name (const tl::Variant &pn); + + /** + * @brief Gets the text property name + */ + const tl::Variant &text_property_name () const + { + return m_text_property_name; + } + + /** + * @brief Sets the text enlargement value + * + * If set to a non-negative value, text objects are converted to boxes with the + * given enlargement (width = 2 * enlargement). The box centers are identical + * to the original location of the text. + * If this value is negative (the default), texts are ignored. + */ + void set_text_enlargement (int enl); + + /** + * @brief Gets the text enlargement value + */ + int text_enlargement () const + { + return m_text_enlargement; + } + private: friend class DeepLayer; @@ -295,6 +340,8 @@ private: int m_threads; double m_max_area_ratio; size_t m_max_vertex_count; + tl::Variant m_text_property_name; + int m_text_enlargement; tl::Mutex m_lock; struct DeliveryMappingCacheKey diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 22463d780..8096572e0 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1016,13 +1016,37 @@ private: } else if (x->second != y->second) { // join two superclusters - x->second->insert (y->second->begin (), y->second->end ()); - for (typename std::set::const_iterator i = y->second->begin (); i != y->second->end (); ++i) { + std::set &yset = *y->second; + x->second->insert (yset.begin (), yset.end ()); + for (typename std::set::const_iterator i = yset.begin (); i != yset.end (); ++i) { m_cm2join_map [*i] = x->second; } - y->second->clear (); + yset.clear (); // TODO: no longer required, but we can't delete it, as we just have a pointer .. replace pointer by iterator! } + +#if defined(DEBUG_HIER_NETWORK_PROCESSOR) + // concistency check for debugging + for (typename std::map *>::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) { + tl_assert (j->second->find (j->first) != j->second->end ()); + } + + for (typename std::list >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) { + for (typename std::set::const_iterator j = i->begin(); j != i->end(); ++j) { + tl_assert(m_cm2join_map.find (*j) != m_cm2join_map.end ()); + tl_assert(m_cm2join_map[*j] == i.operator->()); + } + } + + // the sets must be disjunct + std::set all; + for (typename std::list >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) { + for (typename std::set::const_iterator j = i->begin(); j != i->end(); ++j) { + tl_assert(all.find (*j) == all.end()); + all.insert(*j); + } + } +#endif } /** diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d3afe5412..d27a39174 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -123,7 +123,7 @@ public: typedef typename T::box_type box_type; typedef db::unstable_box_tree > tree_type; typedef typename tree_type::flat_iterator shape_iterator; - typedef unsigned int attr_id; + typedef size_t attr_id; typedef std::set attr_set; typedef attr_set::const_iterator attr_iterator; @@ -317,6 +317,14 @@ public: return m_clusters.end (); } + /** + * @brief Gets a value indicating whether the cluster set is empty + */ + bool empty () const + { + return m_clusters.empty (); + } + /** * @brief Gets the clusters touching a given region */ @@ -554,6 +562,14 @@ public: return m_connections.end (); } + /** + * @brief Gets a value indicating whether the cluster set is empty + */ + bool empty () const + { + return local_clusters::empty () && m_connections.empty (); + } + /** * @brief Returns true, if the given cluster ID is a root cluster */ diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 2f8b999e0..4a64de4d8 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -492,18 +492,40 @@ ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db // --------------------------------------------------------------------------------------------- -PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout) - : mp_layout (layout) +PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement, const tl::Variant &text_prop_name) + : mp_layout (layout), m_text_enlargement (text_enlargement), m_make_text_prop (false), m_text_prop_id (0) { - // nothing yet .. + if (! text_prop_name.is_nil ()) { + m_text_prop_id = layout->properties_repository ().prop_name_id (text_prop_name); + m_make_text_prop = true; + } } void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target) { if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) { + db::Polygon poly; shape.polygon (poly); target->insert (db::PolygonRef (poly, mp_layout->shape_repository ())); + + } else if (shape.is_text () && m_text_enlargement >= 0) { + + db::Polygon poly (shape.text_trans () * db::Box (-m_text_enlargement, -m_text_enlargement, m_text_enlargement, m_text_enlargement)); + db::PolygonRef pref (poly, mp_layout->shape_repository ()); + + if (m_make_text_prop) { + + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (m_text_prop_id, tl::Variant (shape.text_string ()))); + db::properties_id_type pid = mp_layout->properties_repository ().properties_id (ps); + + target->insert (db::PolygonRefWithProperties (pref, pid)); + + } else { + target->insert (pref); + } + } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 37176b340..6b7cd6448 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -135,7 +135,7 @@ class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver : public HierarchyBuilderShapeReceiver { public: - PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout); + PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement = -1, const tl::Variant &text_prop_name = tl::Variant ()); virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target); @@ -143,6 +143,9 @@ public: private: db::Layout *mp_layout; + int m_text_enlargement; + bool m_make_text_prop; + db::property_names_id_type m_text_prop_id; }; /** diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index ca5f738ab..e0134c619 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -103,6 +103,7 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons; mp_layout = &layout; + m_layers = layers; // terminal properties are kept in property index 0 m_propname_id = mp_layout->properties_repository ().prop_name_id (tl::Variant (int (0))); @@ -207,9 +208,11 @@ Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index) return device; } -void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Polygon &polygon) +void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t geometry_index, const db::Polygon &polygon) { tl_assert (mp_layout != 0); + 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; diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index a80e788a2..e98ab6def 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -195,6 +195,7 @@ private: db::cell_index_type m_cell_index; db::Circuit *mp_circuit; std::vector m_device_classes; + std::vector m_layers; unsigned int m_device_name_index; }; diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index eb21e8020..cc3ee008d 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -149,3 +149,38 @@ TEST(2_RefCounting) dl2 = db::DeepLayer (); EXPECT_EQ (store.is_valid_layout_index (lyi1), false); } + +TEST(3_TextTreatment) +{ + db::DeepShapeStore store; + db::Layout layout; + + unsigned int l1 = layout.insert_layer (); + db::cell_index_type c1 = layout.add_cell ("C1"); + layout.cell (c1).shapes (l1).insert (db::Text ("TEXT", db::Trans (db::Vector (1000, 2000)))); + + db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).empty (), true); + + store.set_text_enlargement (1); + dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999)"); + + store.set_text_property_name (tl::Variant ("text")); + dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1)); + EXPECT_EQ (store.layouts (), (unsigned int) 1); + + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1)); + EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999) prop_id=1"); + + const db::Layout *dss_layout = store.const_layout (0); + db::PropertiesRepository::properties_set ps = dss_layout->properties_repository ().properties (1); + EXPECT_EQ (ps.size (), size_t (1)); + EXPECT_EQ (dss_layout->properties_repository ().prop_name (ps.begin ()->first).to_string (), "text"); + EXPECT_EQ (ps.begin ()->second.to_string (), "TEXT"); +} diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index de6fae79f..273f5d218 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -34,20 +34,24 @@ #include "dbWriter.h" #include "dbCommonReader.h" #include "dbTestSupport.h" +#include "dbNetlistProperty.h" +#include "dbCellMapping.h" #include "tlUnitTest.h" #include "tlString.h" #include "tlFileUtils.h" #include +#include class MOSFETExtractor : public db::NetlistDeviceExtractor { public: - MOSFETExtractor (db::Layout *debug_out) + MOSFETExtractor (db::Netlist &nl, db::Layout *debug_out) : db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) { + initialize (&nl); 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)); @@ -67,11 +71,12 @@ public: virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const { - tl_assert (layers.size () == 3); + tl_assert (layers.size () == 4); unsigned int lpdiff = layers [0]; unsigned int lndiff = layers [1]; unsigned int gate = layers [2]; + // not used for device recognition: poly (3), but used for producing the gate terminals // The layer definition is pdiff, ndiff, gate db::Connectivity conn; @@ -109,7 +114,8 @@ public: bool is_pmos = ! rpdiff_on_gate.empty (); db::Region &diff = (is_pmos ? rpdiff_on_gate : rndiff_on_gate); - unsigned int terminal_layer_index = (is_pmos ? 0 : 1); + unsigned int terminal_geometry_index = (is_pmos ? 0 : 1); + unsigned int gate_geometry_index = 3; unsigned int device_class_index = (is_pmos ? 0 /*PMOS*/ : 1 /*NMOS*/); if (diff.size () != 2) { @@ -142,10 +148,12 @@ public: device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); - define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_layer_index, *d); + define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d); } + define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p); + // output the device for debugging device_out (device, diff, rgate); @@ -215,52 +223,456 @@ 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)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +// @@@ TODO: move somewhere else +static unsigned int layer_of (const db::Region ®ion) +{ + // TODO: this is clumsy ... + db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + + return dr->deep_layer ().layer (); +} + +// @@@ TODO: move somewhere else +static db::Layout &layout_of (const db::Region ®ion) +{ + // TODO: this is clumsy ... + db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + + db::DeepLayer dl = dr->deep_layer (); + tl_assert (dl.layout () != 0); + return *dl.layout (); +} + +// @@@ TODO: move somewhere else +static db::Layout &layout_of (const std::vector ®ions) +{ + db::Layout *layout = 0; + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + db::Layout &l = layout_of (**r); + if (! layout) { + layout = &l; + } else { + tl_assert (layout == &l); + } + } + + tl_assert (layout != 0); + return *layout; +} + +// @@@ TODO: move somewhere else +static db::Cell &cell_of (const db::Region ®ion) +{ + // TODO: this is clumsy ... + db::DeepRegion *dr = dynamic_cast (region.delegate ()); + tl_assert (dr != 0); + + db::DeepLayer dl = dr->deep_layer (); + tl_assert (dl.initial_cell () != 0); + return *dl.initial_cell (); +} + +// @@@ TODO: move somewhere else +static db::Cell &cell_of (const std::vector ®ions) +{ + db::Cell *cell = 0; + + for (std::vector::const_iterator r = regions.begin (); r != regions.end (); ++r) { + db::Cell &c = cell_of (**r); + if (! cell) { + cell = &c; + } else { + tl_assert (cell == &c); + } + } + + tl_assert (cell != 0); + return *cell; +} + +// @@@ TODO: move somewhere else +class NetExtractor +{ +public: + typedef db::hier_clusters hier_clusters_type; + typedef db::connected_clusters connected_clusters_type; + typedef db::local_cluster local_cluster_type; + + NetExtractor () + { + // .. nothing yet .. + } + + void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl) + { + tl::Variant terminal_property_name (0); // @@@ take somewhere else + + // only works for singular-layout stores currently. This rules out layers from different sources + // and clipping. + tl_assert (dss.layouts () == 1); + const db::Layout *layout = dss.const_layout (0); + + tl_assert (layout->cells () != 0); + const db::Cell &cell = layout->cell (*layout->begin_top_down ()); + + // gets the text annotation property ID + std::pair text_annot_name_id (false, 0); + if (! dss.text_property_name ().is_nil ()) { + text_annot_name_id = layout->properties_repository ().get_id_of_name (dss.text_property_name ()); + } + + // gets the device terminal annotation property ID + std::pair terminal_annot_name_id (false, 0); + if (! terminal_property_name.is_nil ()) { + terminal_annot_name_id = layout->properties_repository ().get_id_of_name (terminal_property_name); + } + + m_net_clusters.build (*layout, cell, db::ShapeIterator::Polygons, conn); + + 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) { + // @@@ TODO: what if the circuits don't have a cell index? + circuits.insert (std::make_pair (c->cell_index (), c.operator-> ())); + } + + std::map > pins_per_cluster; + + for (db::Layout::bottom_up_const_iterator cid = layout->begin_bottom_up (); cid != layout->end_bottom_up (); ++cid) { + + const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid); + if (clusters.empty ()) { + continue; + } + + // a cell makes a new circuit (or uses an existing one) + + db::Circuit *circuit = 0; + + 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)); + circuit->set_cell_index (*cid); + circuits.insert (std::make_pair (*cid, circuit)); + } else { + circuit = k->second; + } + + std::map &c2p = pins_per_cluster [*cid]; + + std::map subcircuits; + + for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + + db::Net *net = new db::Net (); + net->set_cluster_id (*c); + // @@@ TODO: set name + circuit->add_net (net); + + if (! clusters.is_root (*c)) { + + // a non-root cluster makes a pin + db::Pin pin (net->name ()); + size_t pin_id = circuit->add_pin (pin).id (); + net->add_pin (db::NetPinRef (pin_id)); + c2p.insert (std::make_pair (*c, pin_id)); + circuit->connect_pin (pin_id, net); + + } + + const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (*c); + for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { + + db::SubCircuit *subcircuit = 0; + db::cell_index_type ccid = i->inst ().inst_ptr.cell_index (); + + std::map::const_iterator j = subcircuits.find (i->inst ()); + if (j == subcircuits.end ()) { + + // make subcircuit if required + + std::map::const_iterator k = circuits.find (ccid); + tl_assert (k != circuits.end ()); // because we walk bottom-up + + // @@@ name? + subcircuit = new db::SubCircuit (k->second); + db::CplxTrans dbu_trans (layout->dbu ()); + subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ()); + circuit->add_sub_circuit (subcircuit); + subcircuits.insert (std::make_pair (i->inst (), subcircuit)); + + } else { + subcircuit = j->second; + } + + // create the pin connection to the subcircuit + std::map >::const_iterator icc2p = pins_per_cluster.find (ccid); + tl_assert (icc2p != pins_per_cluster.end ()); + std::map::const_iterator ip = icc2p->second.find (i->id ()); + tl_assert (ip != icc2p->second.end ()); + subcircuit->connect_pin (ip->second, 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 (*c); + for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) { + + // @@@ TODO: needs refactoring!!! + // -> use two distinct and reserved property name ID's for names (=string) and device terminal refs (=single number) instead + // of the scary DeviceTerminalProperty (pointer!!!) + 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) { + + if (j->second.is_user ()) { + const db::NetlistProperty *np = &j->second.to_user (); + const db::DeviceTerminalProperty *tp = dynamic_cast (np); + const db::NetNameProperty *nnp = dynamic_cast (np); + if (tp) { + const_cast (tp->terminal_ref ().device ())->connect_terminal (tp->terminal_ref ().terminal_id (), net); + } else if (nnp) { + net->set_name (nnp->name ()); + } + } + + } else if (text_annot_name_id.first && j->first == text_annot_name_id.second) { + + std::string n = j->second.to_string (); + if (! n.empty ()) { + if (! net->name ().empty ()) { + n = net->name () + "," + n; + } + net->set_name (n); + } + + } + + } + + } + + } + + } + } + + const hier_clusters_type clusters () const + { + return m_net_clusters; + } + +private: + hier_clusters_type m_net_clusters; +}; + +// @@@ TODO: move this somewhere else +static std::string net_name (const db::Net *net) +{ + if (! net) { + return "(null)"; + } else if (net->name ().empty ()) { + if (net->cluster_id () > std::numeric_limits::max () / 2) { + return "$I" + tl::to_string ((std::numeric_limits::max () - net->cluster_id ()) + 1); + } else { + return "$" + tl::to_string (net->cluster_id ()); + } + } else { + return net->name (); + } +} + +// @@@ TODO: refactor. This is inefficient. Give an ID automatically. +static std::string device_name (const db::Device &device, const db::Circuit &circuit) +{ + if (device.name ().empty ()) { + int id = 1; + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices () && d.operator-> () != &device; ++d, ++id) + ; + return "$" + tl::to_string (id); + } else { + return device.name (); + } +} + +// @@@ TODO: refactor. This is inefficient. Give an ID automatically. +static std::string subcircuit_name (const db::SubCircuit &subcircuit, const db::Circuit &circuit) +{ + if (subcircuit.name ().empty ()) { + int id = 1; + for (db::Circuit::const_sub_circuit_iterator d = circuit.begin_sub_circuits (); d != circuit.end_sub_circuits () && d.operator-> () != &subcircuit; ++d, ++id) + ; + return "$" + tl::to_string (id); + } else { + return subcircuit.name (); + } +} + +// @@@ TODO: refactor. This is inefficient. Give an ID automatically. +static std::string pin_name (const db::Pin &pin, const db::Circuit &circuit) +{ + if (pin.name ().empty ()) { + int id = 1; + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins () && p.operator-> () != &pin; ++p, ++id) + ; + return "$" + tl::to_string (id); + } else { + return pin.name (); + } +} + +// @@@ TODO: move this somewhere else + +static void dump_nets (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 () + "_" + net_name (n.operator-> ()); + 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 std::string netlist2string (const db::Netlist &nl) +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { + + std::string ps; + for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) { + if (! ps.empty ()) { + ps += ","; + } + ps += pin_name (*p, *c) + "=" + net_name (c->net_for_pin (p->id ())); + } + + res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; + +// @@@ good for debugging +#if 0 + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; + } +#endif + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + std::string ts; + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t != td.begin ()) { + ts += ","; + } + ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ())); + } + res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d, *c) + " (" + ts + ")\n"; + } + + for (db::Circuit::const_sub_circuit_iterator sc = c->begin_sub_circuits (); sc != c->end_sub_circuits (); ++sc) { + std::string ps; + const db::SubCircuit &subcircuit = *sc; + for (db::Circuit::const_pin_iterator p = sc->circuit ()->begin_pins (); p != sc->circuit ()->end_pins (); ++p) { + if (p != sc->circuit ()->begin_pins ()) { + ps += ","; + } + const db::Pin &pin = *p; + ps += pin_name (pin, *subcircuit.circuit ()) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); + } + res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc, *c) + " (" + ps + ")\n"; + } + + } + + return res; +} + TEST(1_DeviceNetExtraction) { - bool write_debug = true; - db::Layout ly; - unsigned int nwell, active, poly; - - db::LayerProperties p; db::LayerMap lmap; - p.layer = 1; - p.datatype = 0; - lmap.map (db::LDPair (p.layer, p.datatype), nwell = ly.insert_layer ()); - ly.set_properties (nwell, p); + 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); - p.layer = 2; - p.datatype = 0; - lmap.map (db::LDPair (p.layer, p.datatype), active = ly.insert_layer ()); - ly.set_properties (active, p); + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; - p.layer = 3; - p.datatype = 0; - lmap.map (db::LDPair (p.layer, p.datatype), poly = ly.insert_layer ()); - ly.set_properties (poly, p); + 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"); - 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); + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } db::Cell &tc = ly.cell (*ly.begin_top_down ()); db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); // original layers db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); // derived regions db::Region rgate = ractive & rpoly; @@ -287,16 +699,98 @@ TEST(1_DeviceNetExtraction) // NOTE: the device extractor will add more debug layers for the transistors: // 20/0 -> Diffusion // 21/0 -> Gate - MOSFETExtractor ex (write_debug ? &ly : 0); - ex.initialize (&nl); + MOSFETExtractor ex (nl, &ly); std::vector region_ptrs; region_ptrs.push_back (&rpdiff); region_ptrs.push_back (&rndiff); region_ptrs.push_back (&rgate); + region_ptrs.push_back (&rpoly); ex.extract (region_ptrs); + // perform the net extraction + + NetExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (layer_of (rpdiff)); + conn.connect (layer_of (rndiff)); + conn.connect (layer_of (rpoly)); + conn.connect (layer_of (rdiff_cont)); + conn.connect (layer_of (rpoly_cont)); + conn.connect (layer_of (rmetal1)); + conn.connect (layer_of (rvia1)); + conn.connect (layer_of (rmetal2)); + // Inter-layer + conn.connect (layer_of (rpdiff), layer_of (rdiff_cont)); + conn.connect (layer_of (rndiff), layer_of (rdiff_cont)); + conn.connect (layer_of (rpoly), layer_of (rpoly_cont)); + conn.connect (layer_of (rpoly_cont), layer_of (rmetal1)); + conn.connect (layer_of (rdiff_cont), layer_of (rmetal1)); + conn.connect (layer_of (rmetal1), layer_of (rvia1)); + conn.connect (layer_of (rvia1), layer_of (rmetal2)); + conn.connect (layer_of (rpoly), layer_of (rpoly_lbl)); // attaches labels + conn.connect (layer_of (rmetal1), layer_of (rmetal1_lbl)); // attaches labels + conn.connect (layer_of (rmetal2), layer_of (rmetal2_lbl)); // attaches labels + + // extract the nets + + net_ex.extract_nets (dss, conn, &nl); + + // 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 + std::map dump_map; + dump_map [layer_of (rpdiff) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rndiff) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // @@@ we should not have to do this -> can be taken from dss? + db::CellMapping cm; + const db::Layout &ex_layout = layout_of (rpoly); + const db::Cell &ex_cell = cell_of (rpoly); + cm.create_from_names_full (ly, tc.cell_index (), ex_layout, ex_cell.cell_index ()); + + // write nets to layout + dump_nets (nl, net_ex.clusters (), ly, dump_map, cm); + + // compare netlist as string + EXPECT_EQ (netlist2string (nl), + "Circuit RINGO ():\n" + " XINV2 $1 ($1=$I8,$2=FB,$3=OSC,$4=VSS,$5=VDD)\n" + " XINV2 $2 ($1=FB,$2=$I38,$3=$I19,$4=VSS,$5=VDD)\n" + " XINV2 $3 ($1=$I19,$2=$I39,$3=$I1,$4=VSS,$5=VDD)\n" + " XINV2 $4 ($1=$I1,$2=$I40,$3=$I2,$4=VSS,$5=VDD)\n" + " XINV2 $5 ($1=$I2,$2=$I41,$3=$I3,$4=VSS,$5=VDD)\n" + " XINV2 $6 ($1=$I3,$2=$I42,$3=$I4,$4=VSS,$5=VDD)\n" + " XINV2 $7 ($1=$I4,$2=$I43,$3=$I5,$4=VSS,$5=VDD)\n" + " XINV2 $8 ($1=$I5,$2=$I44,$3=$I6,$4=VSS,$5=VDD)\n" + " XINV2 $9 ($1=$I6,$2=$I45,$3=$I7,$4=VSS,$5=VDD)\n" + " XINV2 $10 ($1=$I7,$2=$I46,$3=$I8,$4=VSS,$5=VDD)\n" + "Circuit INV2 ($1=IN,$2=$2,$3=OUT,$4=$4,$5=$5):\n" + " DPMOS 1 (S=$2,G=IN,D=$5)\n" + " DPMOS 2 (S=$5,G=$2,D=OUT)\n" + " DNMOS 3 (S=$2,G=IN,D=$4)\n" + " DNMOS 4 (S=$4,G=$2,D=OUT)\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" + ); + // compare the collected test data std::string au = tl::testsrc (); diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds index 612c2c662..05c3b013f 100644 Binary files a/testdata/algo/device_extract_au1.gds and b/testdata/algo/device_extract_au1.gds differ