diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index b02a5e4f1..6ee3172bf 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -461,13 +461,20 @@ unsigned int DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, trans)); - if (l == m_layout_map.end ()) { + if (l == m_layout_map.end () || m_layouts[l->second] == 0) { - unsigned int layout_index = (unsigned int) m_layouts.size (); + unsigned int layout_index; - m_layouts.push_back (new LayoutHolder (trans)); + if (l != m_layout_map.end ()) { + // reuse discarded entry + layout_index = l->second; + m_layouts[layout_index] = new LayoutHolder (trans); + } else { + layout_index = (unsigned int) m_layouts.size (); + m_layouts.push_back (new LayoutHolder (trans)); + } - db::Layout &layout = m_layouts.back ()->layout; + db::Layout &layout = m_layouts[layout_index]->layout; layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier); if (si.layout ()) { layout.dbu (si.layout ()->dbu () / trans.mag ()); @@ -577,6 +584,32 @@ DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator return DeepLayer (this, layout_index, layer_index); } +DeepLayer DeepShapeStore::create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe) +{ + tl_assert (source.store () == this); + + unsigned int from_layer_index = source.layer (); + db::Layout &ly = layout (); + + unsigned int layer_index = ly.insert_layer (); + + // Build the working hierarchy from the recursive shape iterator + tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy"))); + + db::Box region = db::Box::world (); + db::ICplxTrans trans; + + for (db::Layout::iterator c = ly.begin (); c != ly.end (); ++c) { + db::Shapes &into = c->shapes (layer_index); + const db::Shapes &from = c->shapes (from_layer_index); + for (db::Shapes::shape_iterator s = from.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + pipe->push (*s, trans, region, 0, &into); + } + } + + return DeepLayer (this, source.layout_index (), layer_index); +} + DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const db::ICplxTrans &trans) { unsigned int layout_index = layout_for_iter (si, trans); @@ -725,6 +758,74 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout return cm->second; } +namespace +{ + class DeepShapeStoreToShapeTransformer + : public ShapesTransformer + { + public: + DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout) + : mp_layout (& layout) + { + // gets the text annotation property ID - + // this is how the texts are passed for annotating the net names + m_text_annot_name_id = std::pair (false, 0); + if (! dss.text_property_name ().is_nil ()) { + m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ()); + } + } + + void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const + { + if (! m_text_annot_name_id.first) { + + // fast shortcut + into.insert_transformed (from, trans, pm); + + } else { + + for (db::Shapes::shape_iterator i = from.begin (db::ShapeIterator::All); ! i.at_end (); ++i) { + + bool is_text = false; + + if (i->prop_id () > 0) { + + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (i->prop_id ()); + + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) { + if (j->first == m_text_annot_name_id.second) { + + db::Text text (j->second.to_string (), db::Trans (i->bbox ().center () - db::Point ())); + text.transform (trans); + if (into.layout ()) { + into.insert (db::TextRef (text, into.layout ()->shape_repository ())); + } else { + into.insert (text); + } + + is_text = true; + + } + } + + } + + if (! is_text) { + into.insert (*i, trans, pm); + } + + } + + } + + } + + private: + std::pair m_text_annot_name_id; + const db::Layout *mp_layout; + }; +} + void DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) { @@ -750,8 +851,11 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db std::vector source_cells; source_cells.push_back (*source_layout.begin_top_down()); + // prepare a transformer to convert text-annotated markers back to texts (without transformation however) + DeepShapeStoreToShapeTransformer dsst (*this, source_layout); + // actually copy the shapes - db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm); + db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm, &dsst); } void diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 425aa467c..9c51a14a7 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -334,6 +334,11 @@ public: */ DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ()); + /** + * @brief Creates a deep layer as a copy from an existing one + */ + DeepLayer create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe); + /** * @brief Gets the empty working layer * diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index a820acb9e..9028e5abe 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -58,8 +58,6 @@ LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_i if (dss->is_valid_layout_index (m_layout_index)) { m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set ()); } - - init (); } LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) @@ -131,9 +129,7 @@ db::Region *LayoutToNetlist::make_layer (const std::string &n) si.shape_flags (db::ShapeIterator::Nothing); std::auto_ptr region (new db::Region (si, dss ())); - if (! n.empty ()) { - register_layer (*region, n); - } + register_layer (*region, n); return region.release (); } @@ -144,9 +140,7 @@ db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::st si.shape_flags (db::ShapeIterator::All); std::auto_ptr region (new db::Region (si, dss ())); - if (! n.empty ()) { - register_layer (*region, n); - } + register_layer (*region, n); return region.release (); } @@ -157,9 +151,7 @@ db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const st si.shape_flags (db::ShapeIterator::Texts); std::auto_ptr region (new db::Region (si, dss ())); - if (! n.empty ()) { - register_layer (*region, n); - } + register_layer (*region, n); return region.release (); } @@ -170,9 +162,7 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); std::auto_ptr region (new db::Region (si, dss ())); - if (! n.empty ()) { - register_layer (*region, n); - } + register_layer (*region, n); return region.release (); } diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 503ab35bc..2d3f754a5 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -259,7 +259,8 @@ copy_or_propagate_shapes (db::Layout &target, db::cell_index_type source_parent_cell_index, unsigned int target_layer, unsigned int source_layer, const std::set &all_cells_to_copy, - const std::map &cell_mapping) + const std::map &cell_mapping, + const ShapesTransformer *transformer) { const db::Cell &source_cell = source.cell (source_cell_index); const db::Cell &source_parent_cell = source.cell (source_parent_cell_index); @@ -273,7 +274,7 @@ copy_or_propagate_shapes (db::Layout &target, const db::CellInstArray &cell_inst = p->child_inst ().cell_inst (); for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) { db::ICplxTrans t = db::ICplxTrans (cell_inst.complex_trans (*a)) * propagate_trans; - copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping); + copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping, transformer); } } @@ -282,8 +283,7 @@ copy_or_propagate_shapes (db::Layout &target, } else if (cm->second != DropCell) { db::Cell &target_cell = target.cell (cm->second); - target_cell.shapes (target_layer).insert_transformed (source_cell.shapes (source_layer), trans * propagate_trans, pm); - + transformer->insert_transformed (target_cell.shapes (target_layer), source_cell.shapes (source_layer), trans * propagate_trans, pm); } } @@ -294,7 +294,9 @@ copy_or_move_shapes (db::Layout &target, const std::vector &source_cells, const std::map &cell_mapping, const std::map &layer_mapping, - bool move) + const ShapesTransformer *transformer, + bool move + ) { // collect all called cells and all top level cells std::set all_top_level_cells; @@ -311,7 +313,7 @@ copy_or_move_shapes (db::Layout &target, for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { for (std::map::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) { ++progress; - copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping); + copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping, transformer); if (move) { source.cell (*c).shapes (lm->first).clear (); } @@ -319,15 +321,33 @@ copy_or_move_shapes (db::Layout &target, } } +namespace +{ + class StandardShapesTransformer + : public ShapesTransformer + { + public: + void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const + { + into.insert_transformed (from, trans, pm); + } + }; +} + void copy_shapes (db::Layout &target, const db::Layout &source, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping) + const std::map &layer_mapping, + const ShapesTransformer *transformer) { - copy_or_move_shapes (target, const_cast (source), trans, source_cells, cell_mapping, layer_mapping, false); + StandardShapesTransformer st; + if (! transformer) { + transformer = &st; + } + copy_or_move_shapes (target, const_cast (source), trans, source_cells, cell_mapping, layer_mapping, transformer, false); } void @@ -336,9 +356,14 @@ move_shapes (db::Layout &target, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping) + const std::map &layer_mapping, + const ShapesTransformer *transformer) { - copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, true); + StandardShapesTransformer st; + if (! transformer) { + transformer = &st; + } + copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, transformer, true); } // ------------------------------------------------------------ diff --git a/src/db/db/dbLayoutUtils.h b/src/db/db/dbLayoutUtils.h index ae27790f2..374413772 100644 --- a/src/db/db/dbLayoutUtils.h +++ b/src/db/db/dbLayoutUtils.h @@ -140,6 +140,21 @@ merge_layouts (db::Layout &target, const db::Layout &source, const db::ICplxTran const std::map &layer_mapping, std::map *final_cell_mapping = 0); +/** + * @brief An interface for the shape inserter + * + * This interface is used by copy_shapes and move_shapes to insert + * a shape collection into a another one. By reimplementing this interface, + * more shape transformations can be provided. + */ +class DB_PUBLIC ShapesTransformer +{ +public: + ShapesTransformer () { } + virtual ~ShapesTransformer () { } + virtual void insert_transformed (db::Shapes &into, const db::Shapes &from, const db::ICplxTrans &trans, db::PropertyMapper &pm) const = 0; +}; + /** * @brief Copy shapes from one layout to another * @@ -155,7 +170,8 @@ copy_shapes (db::Layout &target, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping); + const std::map &layer_mapping, + const ShapesTransformer *transformer = 0); /** * @brief Move shapes from one layout to another @@ -172,7 +188,8 @@ move_shapes (db::Layout &target, const db::ICplxTrans &trans, const std::vector &source_cells, const std::map &cell_mapping, - const std::map &layer_mapping); + const std::map &layer_mapping, + const ShapesTransformer *transformer = 0); /** * @brief Find an example cell instance from a child to a top cell diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 50d265cb3..b2ff51302 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -149,13 +149,13 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layo // provide a substitute empty layer layers.push_back (dss.empty_layer (layout_index).layer ()); } else { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: must be of deep region kind")), ld->name)); + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): must be of deep region kind")), ld->name, name ())); } } else { if (&dr->deep_layer ().layout () != &dss.layout (layout_index) || &dr->deep_layer ().initial_cell () != &dss.initial_cell (layout_index)) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: not originating from the same source")), ld->name)); + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): not originating from the same source")), ld->name, name ())); } layers.push_back (dr->deep_layer ().layer ()); diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index f796fa801..73f9aa83f 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -250,6 +250,20 @@ OriginalLayerRegion::init () m_merged_polygons_valid = false; } +void +OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const +{ + db::Shapes &sh = layout->cell (into_cell).shapes (into_layer); + + // NOTE: if the source (r) is from the same layout than the shapes live in, we better + // lock the layout against updates while inserting + db::LayoutLocker locker (layout); + for (db::RecursiveShapeIterator i = m_iter; !i.at_end (); ++i) { + tl::ident_map pm; + sh.insert (*i, i.trans (), pm); + } +} + void OriginalLayerRegion::ensure_merged_polygons_valid () const { @@ -284,165 +298,4 @@ OriginalLayerRegion::ensure_merged_polygons_valid () const } } -namespace -{ - -template -struct dot_delivery -{ - typedef Container container_type; - - dot_delivery () - { - // .. nothing yet .. - } - - void insert (const db::Point &pt, Container *container) const - { - container->insert (db::Edge (pt, pt)); - } -}; - -template -struct box_delivery -{ - typedef Container container_type; - - box_delivery (db::Coord enl) - : m_d (enl, enl) - { - // .. nothing yet .. - } - - void insert (const db::Point &pt, Container *container) const - { - container->insert (db::Box (pt - m_d, pt + m_d)); - } - -private: - db::Vector m_d; -}; - -template -static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const Delivery &delivery, typename Delivery::container_type *container) -{ - tl::GlobPattern glob_pat; - bool all = false; - if (pattern) { - if (pat == "*") { - all = true; - } else { - glob_pat = tl::GlobPattern (pat); - } - } - - for (Iter si = iter; ! si.at_end (); ++si) { - if (si->is_text () && - (all || (pattern && glob_pat.match (si->text_string ())) || (!pattern && si->text_string () == pat))) { - db::Text t; - si->text (t); - t.transform (si.trans ()); - delivery.insert (t.box ().center (), container); - } - } -} - -template -class text_shape_receiver - : public db::HierarchyBuilderShapeReceiver -{ -public: - text_shape_receiver (const Delivery &delivery, const std::string &pat, bool pattern) - : m_delivery (delivery), m_glob_pat (), m_all (false), m_pattern (pattern), m_pat (pat) - { - if (pattern) { - if (m_pat == "*") { - m_all = true; - } else { - m_glob_pat = tl::GlobPattern (pat); - } - } - } - - virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) - { - if (shape.is_text () && - (m_all || (m_pattern && m_glob_pat.match (shape.text_string ())) || (!m_pattern && shape.text_string () == m_pat))) { - - db::Point pt = shape.bbox ().center (); - - if (! complex_region) { - if (region.contains (pt)) { - m_delivery.insert (pt.transformed (trans), target); - } - } else { - if (! complex_region->begin_overlapping (db::Box (pt, pt), db::box_convert ()).at_end ()) { - m_delivery.insert (pt.transformed (trans), target); - } - } - - } - } - - virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } - virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } - -private: - Delivery m_delivery; - tl::GlobPattern m_glob_pat; - bool m_all; - bool m_pattern; - std::string m_pat; -}; - -} - -db::EdgesDelegate * -OriginalLayerRegion::texts_as_dots (const std::string &pat, bool pattern) const -{ - db::RecursiveShapeIterator iter (m_iter); - iter.shape_flags (db::ShapeIterator::Texts); - - std::auto_ptr res (new db::FlatEdges ()); - res->set_merged_semantics (false); - - fill_texts (iter, pat, pattern, dot_delivery (), res.get ()); - - return res.release (); -} - -db::EdgesDelegate * -OriginalLayerRegion::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const -{ - db::RecursiveShapeIterator iter (m_iter); - iter.shape_flags (db::ShapeIterator::Texts); - - text_shape_receiver > pipe = text_shape_receiver > (dot_delivery (), pat, pattern); - return new db::DeepEdges (store.create_custom_layer (iter, &pipe)); -} - -db::RegionDelegate * -OriginalLayerRegion::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const -{ - db::RecursiveShapeIterator iter (m_iter); - iter.shape_flags (db::ShapeIterator::Texts); - - std::auto_ptr res (new db::FlatRegion ()); - res->set_merged_semantics (false); - - fill_texts (iter, pat, pattern, box_delivery (enl), res.get ()); - - return res.release (); -} - -db::RegionDelegate * -OriginalLayerRegion::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const -{ - db::RecursiveShapeIterator iter (m_iter); - iter.shape_flags (db::ShapeIterator::Texts); - - text_shape_receiver > pipe = text_shape_receiver > (box_delivery (enl), pat, pattern); - return new db::DeepRegion (store.create_custom_layer (iter, &pipe)); -} - } diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h index 363e2f1ea..b99a651ec 100644 --- a/src/db/db/dbOriginalLayerRegion.h +++ b/src/db/db/dbOriginalLayerRegion.h @@ -68,10 +68,7 @@ public: virtual bool equals (const Region &other) const; virtual bool less (const Region &other) const; - db::EdgesDelegate *texts_as_dots (const std::string &pat, bool pattern) const; - db::EdgesDelegate *texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const; - db::RegionDelegate *texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const; - db::RegionDelegate *texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; protected: virtual void merged_semantics_changed (); diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 7d64f0768..94234ea91 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -26,7 +26,10 @@ #include "dbEmptyRegion.h" #include "dbFlatRegion.h" #include "dbDeepRegion.h" +#include "dbDeepEdges.h" +#include "dbFlatEdges.h" #include "dbPolygonTools.h" +#include "tlGlobPattern.h" namespace db { @@ -398,6 +401,291 @@ Region::hulls () const return Region (processed (HullExtractionProcessor ())); } +namespace +{ + +template +struct dot_delivery +{ + typedef Container container_type; + + dot_delivery () + { + // .. nothing yet .. + } + + void insert (const db::Point &pt, Container *container) const + { + container->insert (db::Edge (pt, pt)); + } +}; + +template +struct box_delivery +{ + typedef Container container_type; + + box_delivery (db::Coord enl) + : m_d (enl, enl) + { + // .. nothing yet .. + } + + void insert (const db::Point &pt, Container *container) const + { + container->insert (db::Box (pt - m_d, pt + m_d)); + } + +private: + db::Vector m_d; +}; + +template +static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const Delivery &delivery, typename Delivery::container_type *container, const db::ICplxTrans &trans, const db::DeepRegion *org_deep) +{ + std::pair text_annot_name_id; + const db::Layout *layout = 0; + + if (org_deep) { + layout = &org_deep->deep_layer ().layout (); + const db::DeepShapeStore *store = org_deep->deep_layer ().store (); + if (! store->text_property_name ().is_nil ()) { + text_annot_name_id = layout->properties_repository ().get_id_of_name (store->text_property_name ()); + } + } + + tl::GlobPattern glob_pat; + bool all = false; + if (pattern) { + if (pat == "*") { + all = true; + } else { + glob_pat = tl::GlobPattern (pat); + } + } + + for (Iter si = iter; ! si.at_end (); ++si) { + + bool is_text = false; + std::string text_string; + + if (si->is_text ()) { + + // a raw text + is_text = true; + text_string = si->text_string (); + + } else if (layout && text_annot_name_id.first && si->prop_id () > 0) { + + // a text marker + const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (si->prop_id ()); + + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) { + if (j->first == text_annot_name_id.second) { + text_string = j->second.to_string (); + is_text = true; + } + } + + } + + if (is_text && + (all || (pattern && glob_pat.match (text_string)) || (!pattern && text_string == pat))) { + delivery.insert (si.trans () * (trans * si->bbox ().center ()), container); + } + + } +} + +template +class text_shape_receiver + : public db::HierarchyBuilderShapeReceiver +{ +public: + text_shape_receiver (const Delivery &delivery, const std::string &pat, bool pattern, const db::DeepRegion *org_deep) + : m_delivery (delivery), m_glob_pat (), m_all (false), m_pattern (pattern), m_pat (pat), m_text_annot_name_id (false, 0), mp_layout (0) + { + if (org_deep) { + mp_layout = & org_deep->deep_layer ().layout (); + const db::DeepShapeStore *store = org_deep->deep_layer ().store (); + if (! store->text_property_name ().is_nil ()) { + m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (store->text_property_name ()); + } + } + + if (pattern) { + if (m_pat == "*") { + m_all = true; + } else { + m_glob_pat = tl::GlobPattern (pat); + } + } + } + + virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) + { + bool is_text = false; + std::string text_string; + + if (shape.is_text ()) { + + // a raw text + is_text = true; + text_string = shape.text_string (); + + } else if (mp_layout && m_text_annot_name_id.first && shape.prop_id () > 0) { + + // a text marker + const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (shape.prop_id ()); + + for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) { + if (j->first == m_text_annot_name_id.second) { + text_string = j->second.to_string (); + is_text = true; + } + } + + } + + if (is_text && + (m_all || (m_pattern && m_glob_pat.match (text_string)) || (!m_pattern && text_string == m_pat))) { + + db::Point pt = shape.bbox ().center (); + + if (! complex_region) { + if (region.contains (pt)) { + m_delivery.insert (pt.transformed (trans), target); + } + } else { + if (! complex_region->begin_overlapping (db::Box (pt, pt), db::box_convert ()).at_end ()) { + m_delivery.insert (pt.transformed (trans), target); + } + } + + } + } + + virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { } + +private: + Delivery m_delivery; + tl::GlobPattern m_glob_pat; + bool m_all; + bool m_pattern; + std::string m_pat; + std::pair m_text_annot_name_id; + const db::Layout *mp_layout; +}; + +} + +Edges +Region::texts_as_dots (const std::string &pat, bool pattern) const +{ + const db::DeepRegion *dr = dynamic_cast (delegate ()); + if (dr) { + return texts_as_dots (pat, pattern, const_cast (*dr->deep_layer ().store ())); + } + + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (db::ShapeIterator::Texts); + } + + std::auto_ptr res (new db::FlatEdges ()); + res->set_merged_semantics (false); + + fill_texts (si.first, pat, pattern, dot_delivery (), res.get (), si.second, dr); + + return Edges (res.release ()); +} + +Edges +Region::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const +{ + const db::DeepRegion *dr = dynamic_cast (delegate ()); + + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (db::ShapeIterator::Texts); + } + + if (! si.first.layout ()) { + + // flat fallback if the source isn't a deep or original layer + std::auto_ptr res (new db::FlatEdges ()); + res->set_merged_semantics (false); + + fill_texts (si.first, pat, pattern, dot_delivery (), res.get (), si.second, dr); + + return Edges (res.release ()); + + } + + text_shape_receiver > pipe = text_shape_receiver > (dot_delivery (), pat, pattern, dr); + if (dr && dr->deep_layer ().store () == &store) { + return Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe))); + } else { + return Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second))); + } +} + +Region +Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const +{ + const db::DeepRegion *dr = dynamic_cast (delegate ()); + if (dr) { + return texts_as_boxes (pat, pattern, enl, const_cast (*dr->deep_layer ().store ())); + } + + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (db::ShapeIterator::Texts); + } + + std::auto_ptr res (new db::FlatRegion ()); + res->set_merged_semantics (false); + + fill_texts (si.first, pat, pattern, box_delivery (enl), res.get (), si.second, dr); + + return Region (res.release ()); +} + +Region +Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const +{ + const db::DeepRegion *dr = dynamic_cast (delegate ()); + + std::pair si = begin_iter (); + if (! dr) { + // some optimization + si.first.shape_flags (db::ShapeIterator::Texts); + } + + if (! si.first.layout ()) { + + // flat fallback if the source isn't a deep or original layer + std::auto_ptr res (new db::FlatRegion ()); + res->set_merged_semantics (false); + + fill_texts (si.first, pat, pattern, box_delivery (enl), res.get (), si.second, dr); + + return Region (res.release ()); + + } + + text_shape_receiver > pipe = text_shape_receiver > (box_delivery (enl), pat, pattern, dr); + if (dr && dr->deep_layer ().store () == &store) { + return Region (new db::DeepRegion (store.create_copy (dr->deep_layer (), &pipe))); + } else { + return Region (new db::DeepRegion (store.create_custom_layer (si.first, &pipe, si.second))); + } +} + } namespace tl diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 0a9f1a857..30a322d88 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1516,6 +1516,40 @@ public: return mp_delegate->insert_into (layout, into_cell, into_layer); } + /** + * @brief Delivers texts as dots (degenerated edges) + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.. + */ + db::Edges texts_as_dots (const std::string &pat, bool as_pattern) const; + + /** + * @brief Delivers texts as dots (degenerated edges) in a deep edge collection + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.. + */ + db::Edges texts_as_dots (const std::string &pat, bool as_pattern, db::DeepShapeStore &store) const; + + /** + * @brief Delivers texts as boxes + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text. + * "enl" is the half size of the box (the box is 2*enl wide and 2*enl high). + */ + db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl) const; + + /** + * @brief Delivers texts as boxes in a deep region + * + * "pat" is a text selector. If "as_pattern" is true, this pattern will be + * treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text. + * "enl" is the half size of the box (the box is 2*enl wide and 2*enl high). + */ + db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl, db::DeepShapeStore &store) const; + private: friend class Edges; friend class EdgePairs; diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 39163911a..31a44b39e 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -392,12 +392,12 @@ static bool is_deep (const db::Edges *e) static db::Edges *new_texts_as_dots1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern) { - return new db::Edges (db::OriginalLayerRegion (si, false).texts_as_dots (pat, pattern)); + return new db::Edges (db::Region (si).texts_as_dots (pat, pattern)); } static db::Edges *new_texts_as_dots2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern) { - return new db::Edges (db::OriginalLayerRegion (si, false).texts_as_dots (pat, pattern, dss)); + return new db::Edges (db::Region (si).texts_as_dots (pat, pattern, dss)); } static size_t id (const db::Edges *e) diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 32296d87f..5b37d1790 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -80,41 +80,32 @@ static db::Region *new_shapes (const db::Shapes &s) static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl) { - return new db::Region (db::OriginalLayerRegion (si, false).texts_as_boxes (pat, pattern, enl)); + return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl)); } static db::Region *new_texts_as_boxes2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl) { - return new db::Region (db::OriginalLayerRegion (si, false).texts_as_boxes (pat, pattern, enl, dss)); -} - -static const db::OriginalLayerRegion *org_layer (const db::Region *r) -{ - const db::OriginalLayerRegion *org_layer = dynamic_cast (r->delegate ()); - if (! org_layer) { - throw tl::Exception (tl::to_string (tr ("Texts can only be identified on an original layer"))); - } - return org_layer; + return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl, dss)); } static db::Edges *texts_as_dots1 (const db::Region *r, const std::string &pat, bool pattern) { - return new db::Edges (org_layer (r)->texts_as_dots (pat, pattern)); + return new db::Edges (r->texts_as_dots (pat, pattern)); } static db::Edges *texts_as_dots2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern) { - return new db::Edges (org_layer (r)->texts_as_dots (pat, pattern, dss)); + return new db::Edges (r->texts_as_dots (pat, pattern, dss)); } static db::Region *texts_as_boxes1 (const db::Region *r, const std::string &pat, bool pattern, db::Coord enl) { - return new db::Region (org_layer (r)->texts_as_boxes (pat, pattern, enl)); + return new db::Region (r->texts_as_boxes (pat, pattern, enl)); } static db::Region *texts_as_boxes2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl) { - return new db::Region (org_layer (r)->texts_as_boxes (pat, pattern, enl, dss)); + return new db::Region (r->texts_as_boxes (pat, pattern, enl, dss)); } static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end) @@ -142,6 +133,11 @@ static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICpl return new db::Region (si, trans); } +static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, trans, area_ratio, max_vertex_count); +} + static std::string to_string0 (const db::Region *r) { return r->to_string (); @@ -653,9 +649,8 @@ Class decl_Region ("db", "Region", "\n" "This constructor has been introduced in version 0.25." ) + - constructor ("new", &new_si, + constructor ("new", &new_si, gsi::arg ("shape_iterator"), "@brief Constructor from a hierarchical shape set\n" - "@args shape_iterator\n" "\n" "This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n" "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" @@ -668,9 +663,8 @@ Class decl_Region ("db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer))\n" "@/code\n" ) + - constructor ("new", &new_si2, + constructor ("new", &new_si2, gsi::arg ("shape_iterator"), gsi::arg ("trans"), "@brief Constructor from a hierarchical shape set with a transformation\n" - "@args shape_iterator, trans\n" "\n" "This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n" "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" @@ -698,7 +692,24 @@ Class decl_Region ("db", "Region", "@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n" "@param deep_shape_store The hierarchical heap (see there)\n" "@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n" - "@param" + "\n" + "This method has been introduced in version 0.26.\n" + ) + + constructor ("new", &new_sid2, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("trans"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), + "@brief Constructor for a deep region from a hierarchical shape set\n" + "\n" + "This constructor creates a hierarchical region. Use a \\DeepShapeStore object to " + "supply the hierarchical heap. See \\DeepShapeStore for more details.\n" + "\n" + "'area_ratio' and 'max_vertex' supply two optimization parameters which control how " + "big polygons are split to reduce the region's polygon complexity.\n" + "\n" + "The transformation is useful to scale to a specific database unit for example.\n" + "\n" + "@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n" + "@param deep_shape_store The hierarchical heap (see there)\n" + "@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n" + "@param trans The transformation to apply when storing the layout data\n" "\n" "This method has been introduced in version 0.26.\n" ) + diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index f439edf2a..ed2607462 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -1198,19 +1198,19 @@ TEST(23_Texts) unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1)); unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1)); - db::Region r6boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100, dss)); + db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100, dss)); db::Region r6dots; - db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20); - db::Region r8boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100, dss)); + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20); + db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100, dss)); db::Region r8dots; - db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20); + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20); - db::Region rf6boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100)); + db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100)); db::Region rf6dots; - db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20); - db::Region rf8boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100)); + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20); + db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100)); db::Region rf8dots; - db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20); + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20); { db::Layout target; @@ -1230,6 +1230,59 @@ TEST(23_Texts) } } +TEST(24_TextsFromDeep) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("textstring")); + + unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1)); + unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1)); + + db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100, dss)); + db::Region r6dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20); + db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100, dss)); + db::Region r8dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20); + + db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100)); + db::Region rf6dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20); + db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100)); + db::Region rf8dots; + db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20); + + { + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), rf6boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rf6dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r8boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r8dots); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rf8boxes); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rf8dots); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au24.gds"); + } +} + TEST(100_Integration) { db::Layout ly; diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index bfe9eca5d..d5044ec66 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -3488,10 +3488,21 @@ CODE # @li @tt METAL (17/0) @/tt: A layer named "METAL" or layer 17, datatype 0 (for GDS, which does # not have names)@/li # @/ul + # + # Layers created with "input" contain both texts and polygons. There is a subtle + # difference between flat and deep mode: in flat mode, texts are not visible in polygon + # operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode, + # some operations such as clipping are not fully supported for texts. Also, texts will + # vanish in most polygon operations such as booleans etc. + # + # Texts can alway be selected by using the \texts method. + # + # If you don't want to see texts, use \polygons to create an input layer with polygon data + # only. If you only want to see texts, use \labels to create an input layer with texts only. def input(*args) layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, false)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll)) end # %DRC% @@ -3501,26 +3512,45 @@ CODE # @synopsis source.labels(layer, datatype) # @synopsis source.labels(layer_into) # @synopsis source.labels(filter, ...) - # Creates a layer with the labels from the given layer of the source. - # The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo - # object or by a sequence of filters. - # Filters are expressions describing ranges - # of layers and/or datatype numbers or layer names. Multiple filters - # can be given and all layers matching at least one of these filter - # expressions are joined to render the label collection. See "input" for - # more details about the input layer specification. - # - # Label layers currently can only be passed to an output layer. - # Processing of labels is not supported. See "texts" for a way to filter - # texts and use the text locations in geometrical operations. # - # @code - # labels(1, 0).output(100, 0) - # @/code + # Creates a layer with the labels from the given layer of the source. + # + # This method is identical to \input, but takes only texts from the given input + # layer. def labels(*args) layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, true)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts)) + end + + # %DRC% + # @name polygons + # @brief Gets the polygon shapes (or shapes that can be converted polygons) from an input layer + # @synopsis source.polygons(layer) + # @synopsis source.polygons(layer, datatype) + # @synopsis source.polygons(layer_into) + # @synopsis source.polygons(filter, ...) + # + # Creates a layer with the polygon shapes from the given layer of the source. + # With "polygon shapes" we mean all kind of shapes that can be converted to polygons. + # Those are boxes, paths and real polygons. + # + # This method is identical to \input with respect to the options supported. + + def polygons(*args) + layers = parse_input_layers(*args) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons)) + end + + # %DRC% + # @name make_layer + # @brief Creates an empty polygon layer based on the hierarchy of the layout + # @synopsis make_layer + # This method delivers a new empty original layer. + + def make_layer + layers = [] + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll)) end # %DRC% @@ -3689,6 +3719,73 @@ CODE @connections << [ a, b ].collect { |l| l.data.data_id } modified end + + # %DRC% + # @name connect_global + # @brief Connects a layer with a global net + # @synopsis connect_global(l, name) + # Connects the shapes from the given layer l to a global net with the given name. + # Global nets are common to all cells. Global nets automatically connect to parent + # cells throughs implied pins. An example is the substrate (bulk) net which connects + # to shapes belonging to tie-down diodes. + + def connect_global(l, name) + l.is_a?(DRC::DRCLayer) || raise("Layer argument of Netter#connect_global must be a layer") + l.requires_region("Netter#connect_global (layer argument)") + @layers[l.data.data_id] = l.data + @global_connections << [ l.data.data_id, name.to_s ] + end + + # %DRC% + # @name extract_devices + # @brief Extracts devices based on the given extractor class, name and device layer selection + # @synopsis extract_devices(extractor, layer_hash) + # Runs the device extraction for given device extractor class. + # + # The device extractor is either an instance of one of the predefined extractor + # classes (e.g. RBA::DeviceExtractorMOS4Transistor) or a custom class. It provides the + # algorithms for deriving the device parameters from the device geometry. It needs + # several device recognition layers which are passed in the layer hash. + # + # Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance + # of device extractor. The device extractor beside the algorithm and specific + # extraction settings defines the name of the device to be built. + # + # The layer hash is a map of device type specific functional names (key) and + # polygon layers (value). Here is an example: + # + # @code + # deep + # + # nwell = input(1, 0) + # active = input(2, 0) + # poly = input(3, 0) + # bulk = make_layer # renders an empty layer used for putting the terminals on + # + # nactive = active - nwell # active area of NMOS + # nsd = nactive - poly # source/drain area + # gate = nactive & poly # gate area + # + # mos4_ex = RBA::DeviceExtractorMOS4Transistor::new("NMOS4") + # extract_devices(mos4_ex, { :SD => nsd, :G => gate, :P => poly, :W => bulk }) + # @/code + + def extract_devices(devex, layer_selection) + + devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance") + layer_selection.is_a?(Hash) || raise("Second argument of Netter#extract_devices must be a hash") + + ls = {} + layer_selection.each do |n,l| + l.requires_region("Netter#extract_devices (#{n} layer)") + @layers[l.data.data_id] = l.data + ls[n.to_s] = l.data + end + + @devices_to_extract << [ devex, ls ] + modified + + end # %DRC% # @name clear_connections @@ -3697,7 +3794,9 @@ CODE # See \connect for more details. def clear_connections + @devices_to_extract = [] @connections = [] + @global_connections = [] @layers = {} modified end @@ -3803,6 +3902,18 @@ CODE DRC::DRCLayer::new(@engine, @engine._cmd(@l2n, :antenna_check, gate.data, metal.data, ratio, dl)) end + + # %DRC% + # @name l2n_data + # @brief Gets the internal RBA::LayoutToNetlist object + # @synopsis l2n_data + # The RBA::LayoutToNetlist object provides access to the internal details of + # the netter object. + + def l2n_data + @l2n || make_l2n + @l2n + end def _finish clear_connections @@ -3830,8 +3941,14 @@ CODE end @layers.each { |id,l| @l2n.register(l, "l" + id.to_s) } + + @devices_to_extract.each do |devex,ls| + @engine._cmd(@l2n, :extract_devices, devex, ls) + end + @layers.each { |id,l| @l2n.connect(l) } @connections.each { |a,b| @l2n.connect(@layers[a], @layers[b]) } + @global_connections.each { |l,n| @l2n.connect_global(@layers[l], n) } # run extraction in a timed environment @engine._cmd(@l2n, :extract_netlist) @@ -4200,12 +4317,33 @@ CODE @tt = n.to_i end + # %DRC% + # @name make_layer + # @brief Creates an empty polygon layer based on the hierarchical scheme selected + # @synopsis make_layer + # The intention of this method is to provide an empty polygon layer based on the + # hierarchical scheme selected. This will create a new layer with the hierarchy + # of the current layout in deep mode and a flat layer in flat mode. + # This method is similar to \polygon_layer, but the latter does not create + # a hierarchical layer. Hence the layer created by \make_layer is suitable + # for use in device extraction for example, while the one + # delivered by \polygon_layer is not. + # + # On the other hand, a layer created by the \make_layer method is not intended to be + # filled with \Layer#insert. + + def make_layer + layout.make_layer + end + # %DRC% # @name polygon_layer # @brief Creates an empty polygon layer # @synopsis polygon_layer # The intention of that method is to create an empty layer which can be # filled with polygon-like objects using \Layer#insert. + # A similar method which creates a hierarchical layer in deep mode is + # \make_layer. This other layer is better suited for use with device extraction. def polygon_layer DRCLayer::new(self, RBA::Region::new) @@ -4621,12 +4759,24 @@ CODE # @name input # @brief Fetches the shapes from the specified input from the default source # @synopsis input(args) - # See \Source#input for a description of that function. + # See \Source#input for a description of that function. This method will fetch + # polygons and labels. See \polygons and \labels for more specific versions of + # this method. def input(*args) layout.input(*args) end + # %DRC% + # @name polygons + # @brief Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source + # @synopsis polygons(args) + # See \Source#polygons for a description of that function. + + def polygons(*args) + layout.polygons(*args) + end + # %DRC% # @name labels # @brief Gets the labels (text) from an original layer @@ -4754,6 +4904,12 @@ CODE # @synopsis connect(a, b) # See \Netter#connect for a description of that function. + # %DRC% + # @name connect_global + # @brief Specifies a connection to a global net + # @synopsis connect_global(l, name) + # See \Netter#connect_global for a description of that function. + # %DRC% # @name clear_connections # @brief Clears all connections stored so far @@ -4766,7 +4922,19 @@ CODE # @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ]) # See \Netter#antenna_check for a description of that function - %w(connect clear_connections antenna_check).each do |f| + # %DRC% + # @name l2n_data + # @brief Gets the internal RBA::LayoutToNetlist object for the default \Netter + # @synopsis l2n_data + # See \Netter#l2n_data for a description of that function + + # %DRC% + # @name extract_devices + # @brief Extracts devices for a given device extractor and device layer selection + # @synopsis extract_devices(extractor, layer_hash) + # See \Netter#extract_devices for a description of that function + + %w(connect connect_global clear_connections antenna_check l2n_data extract_devices).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) @@ -5033,10 +5201,12 @@ CODE end end - def _input(layout, cell_index, layers, sel, box, clip, overlapping, labels_only) + def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags) - if layers.empty? + if layers.empty? && ! @deep + r = RBA::Region::new + else if box @@ -5044,9 +5214,7 @@ CODE else iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers) end - if labels_only - iter.shape_flags = RBA::Shapes::STexts - end + iter.shape_flags = shape_flags sel.each do |s| if s == "-" @@ -5064,31 +5232,17 @@ CODE end end - if labels_only - - # for labels layers, the iterator is the data (no region) - r = iter - + sf = layout.dbu / self.dbu + if @deep + @dss ||= RBA::DeepShapeStore::new + r = RBA::Region::new(iter, @dss, RBA::ICplxTrans::new(sf.to_f)) else + r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f)) + end - sf = layout.dbu / self.dbu - if (sf - 1.0).abs > 1e-6 - if @deep - raise("DBU scaling (" + ("%.12g" % layout.dbu) + " to " + ("%.12g" % self.dbu) + ") is not supported in deep mode currently") - end - r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f)) - elsif @deep - @dss ||= RBA::DeepShapeStore::new - r = RBA::Region::new(iter, @dss) - else - r = RBA::Region::new(iter) - end - - # clip if a box is specified - if box && clip - r &= RBA::Region::new(box) - end - + # clip if a box is specified + if box && clip + r &= RBA::Region::new(box) end end @@ -5112,11 +5266,7 @@ CODE cat = @output_rdb.create_category(args[0].to_s) args[1] && cat.description = args[1] - if data.is_a?(RBA::RecursiveShapeIterator) - cat.scan_shapes(data) - else - cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data) - end + cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data) else @@ -5173,24 +5323,11 @@ CODE @used_output_layers[li] = true end - if data.respond_to?(:is_deep?) && data.is_deep? - - # insert the data into the output layer - if data.is_a?(RBA::EdgePairs) - data.insert_into_as_polygons(output, output_cell.cell_index, tmp, 1) - else - data.insert_into(output, output_cell.cell_index, tmp) - end - + # insert the data into the output layer + if data.is_a?(RBA::EdgePairs) + data.insert_into_as_polygons(output, output_cell.cell_index, tmp, 1) else - - # insert the data into the output layer - if data.is_a?(RBA::EdgePairs) - output_cell.shapes(tmp).insert_as_polygons(data, 1) - else - output_cell.shapes(tmp).insert(data) - end - + data.insert_into(output, output_cell.cell_index, tmp) end # make the temp layer the output layer diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index d55f8a7ac..87e8590ae 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -302,3 +302,43 @@ TEST(7_AntennaWithDiodes) db::compare_layouts (_this, layout, au, db::NoNormalization); } +TEST(8_FlatTextsAndPolygons) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_8.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/texts.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au8.oas"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + diff --git a/testdata/algo/deep_region_au24.gds b/testdata/algo/deep_region_au24.gds new file mode 100644 index 000000000..751949da0 Binary files /dev/null and b/testdata/algo/deep_region_au24.gds differ diff --git a/testdata/drc/texts.gds b/testdata/drc/texts.gds new file mode 100644 index 000000000..241b8f894 Binary files /dev/null and b/testdata/drc/texts.gds differ