From 8b29b30ff9e863fdff44ae57b22aff7365c68f1c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 6 Mar 2019 00:32:29 +0100 Subject: [PATCH] WIP: more consistent text handling Texts are not only kept inside original layers, but also inside deep layers. This enables using texts from DRC. However, texts in deep layers are kept as markers. Mostly they are converted back to texts, but the orientation will be lost. The change eliminates the need to using Iterators in DRC instead of original layers and use of label layers in deep mode. A drawback is the presence of marker shapes in deep mode (unless polygon layers are created). Also, text output to RDB is not supported from deep layers currently. --- src/db/db/dbDeepShapeStore.cc | 114 +++++++++- src/db/db/dbDeepShapeStore.h | 5 + src/db/db/dbLayoutToNetlist.cc | 18 +- src/db/db/dbLayoutUtils.cc | 45 +++- src/db/db/dbLayoutUtils.h | 21 +- src/db/db/dbNetlistDeviceExtractor.cc | 4 +- src/db/db/dbOriginalLayerRegion.cc | 175 ++------------- src/db/db/dbOriginalLayerRegion.h | 5 +- src/db/db/dbRegion.cc | 288 +++++++++++++++++++++++++ src/db/db/dbRegion.h | 34 +++ src/db/db/gsiDeclDbEdges.cc | 4 +- src/db/db/gsiDeclDbRegion.cc | 51 +++-- src/db/unit_tests/dbDeepRegionTests.cc | 69 +++++- src/drc/drc/built-in-macros/drc.lym | 275 +++++++++++++++++------ src/drc/unit_tests/drcSimpleTests.cc | 40 ++++ testdata/algo/deep_region_au24.gds | Bin 0 -> 1242 bytes testdata/drc/texts.gds | Bin 0 -> 586 bytes 17 files changed, 851 insertions(+), 297 deletions(-) create mode 100644 testdata/algo/deep_region_au24.gds create mode 100644 testdata/drc/texts.gds 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 0000000000000000000000000000000000000000..751949da0c32ed0c12119ceb1b8911f1ed4c7a38 GIT binary patch literal 1242 zcmbV~F-yZx5Xb+0NngTCDq5m~1UCmkP*e~H#fqRosS<;TI66A`5jwaC9o*~CLGUX$ zI6M0RItZ=~8JvyRq>tp4zG@sE|8W2N-5t3D2V>{(E6n^&Lh&0m<43_R;2G8J z#@)u@>r=eB_q^QgTtx7zdQNa2g^lJZU|LjlOs36w&^%j(DF9KIlsjo8$@dLSL=~p! zjdn#m!sT<77C;q%%Dtw-P>!r)1H8r{z$X}yOZ^(`g@TWnTGxPa3rLl5(sarJ?-Y5% zCH{*zQp+b@&sD}ri@aOp|IxozrQ%)rVZ288U$Yz7V{HXlzX1_lvkRy)T|bMIrzKUli#&|fe+ zDuh9X6}LV%Ha15F1{Mwm1~y(M21W)pJ|+eR1|0$B|NnpA0pcbQ&Aeld`KHgQIvE7_zNfovXWRtL2-Fks*Tnfw3E|Ns9^F>pw-MMOpc^@8-X zGZ1nm&~GeUK$C?)?uDD|C;+qp1bAdv9l#6_TYwozPGewTlz^Cyu1|*531~4GfL-*3 z3Fs;x(tYwP3#hLSNz!NwNi9{~3Y&{xFx#R%+Ha7dy1MVb{?82tbL^#%h2 LP(3V!Sy&hVda72< literal 0 HcmV?d00001