diff --git a/Changelog b/Changelog index 5282df801..789a91314 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,31 @@ +0.29.3 (2024-07-02): +* Enhancement: %GITHUB%/issues/1655 Marker object lifetime management +* Bug: %GITHUB%/issues/1743 strmxor shows no difference, klayout xor shows 85 +* Bug: %GITHUB%/issues/1757 Fixed memory issue +* Enhancement: %GITHUB%/issues/1741 Tooltip strings for PCell parameters +* Enhancement: %GITHUB%/issues/1747 Transformations on negative floats/ints +* Bug: %GITHUB%/issues/1751 Code sanity issue fixed +* Bug: %GITHUB%/issues/1750 Code sanity issue fixed +* Bug: %GITHUB%/issues/1733 Instance selecion in object properties does not match with view port object highlight +* Enhancement: DRC - step-wise size with "inside" and "outside" constraint + This feature is handy for implementing latch-up rules more efficiently. +* Bugfix: OASIS reader - avoiding slight rounding of DBU In python read/write cycle (discussion-2526) +* Bugfix: Proper tracking of references of RecursiveShapeIterator. + Related to issue #1742, but not directly. +* Bugfix: Symlinks in the salt paths might have lead to macro duplication +* Bugfix: Ruby binding - "return" inside block was behaving like "break". + Now, "return" will leave the current function. +* Enhancement: LVS layer naming now also accepts an optional layer/datatype information. +* Enhancement: XOR performance enhanced in deep mode for "almost same" + inputs. +* Bugfix: Macro debugger now does not prevent paint events and + screen refresh should work while debugging. In addition, the debugger + does not deadlock the desktop when using the help browser's search + function. Side effects are yet unknown - maybe debugging Qt event + handlers now becomes less stable. +* Bugfix: During modal dialogs, the debugger's run/stop and step buttons + were not working. + 0.29.2 (2024-06-06): * Enhancement: %GITHUB%/issues/1724 Don't read duplicate LEF files * Bug: %GITHUB%/issues/1722 [macOS] Crash when opening layout having Custom Macro Menus diff --git a/Changelog.Debian b/Changelog.Debian index 1f9bd8eee..b724bce97 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.29.3-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Tue, 02 Jul 2024 12:00:00 +0200 + klayout (0.29.2-1) unstable; urgency=low * New features and bugfixes diff --git a/scripts/drc_lvs_doc/create_drc_samples.rb b/scripts/drc_lvs_doc/create_drc_samples.rb index 2799b88e1..18450e1df 100644 --- a/scripts/drc_lvs_doc/create_drc_samples.rb +++ b/scripts/drc_lvs_doc/create_drc_samples.rb @@ -914,6 +914,36 @@ run_demo gen, "input.sized(1.um, octagon_limit)", "drc_sized4.png" run_demo gen, "input.sized(1.um, square_limit)", "drc_sized5.png" run_demo gen, "input.sized(1.um, acute_limit)", "drc_sized6.png" +class Gen + def produce(s1, s2) + pts = [ + RBA::Point::new(1000, 1000), + RBA::Point::new(1000, 2000), + RBA::Point::new(2000, 2000), + RBA::Point::new(2000, 1000) + ]; + s1.insert(RBA::Polygon::new(pts)) + pts = [ + RBA::Point::new(1000, 1000), + RBA::Point::new(1000, 7000), + RBA::Point::new(6000, 7000), + RBA::Point::new(6000, 1000), + RBA::Point::new(5000, 1000), + RBA::Point::new(5000, 6000), + RBA::Point::new(2000, 6000), + RBA::Point::new(2000, 1000) + ]; + s2.insert(RBA::Polygon::new(pts)) + end +end + +gen = Gen::new + +run_demo gen, "input1.sized(1.um, steps(1), size_inside(input2))", "drc_sized_inside1.png" +run_demo gen, "input1.sized(2.um, steps(2), size_inside(input2))", "drc_sized_inside2.png" +run_demo gen, "input1.sized(3.um, steps(3), size_inside(input2))", "drc_sized_inside3.png" +run_demo gen, "input1.sized(10.um, steps(10), size_inside(input2))", "drc_sized_inside4.png" + class Gen def produce(s1, s2) pts = [ diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 2171c4f37..a62894e47 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1377,6 +1377,97 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } } +RegionDelegate * +AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const +{ + return sized_inside (inside, outside, d, d, steps, mode); +} + +RegionDelegate * +AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const +{ + // empirical value + const int max_steps = 25; + + if (steps <= 0 || empty ()) { + // Nothing to do - NOTE: don't return EmptyRegion because we want to + // maintain "deepness" + return clone (); + } + + if (dx < 0 || dy < 0) { + throw tl::Exception (tl::to_string (tr ("'sized_inside' operation does not make sense with negative sizing"))); + } + + if (dx == 0 && dy == 0) { + steps = 1; + } + + // NOTE: it does not provide benefits to merge the outside region, so just don't + auto inside_polygons = outside ? inside.begin () : inside.begin_merged (); + bool inside_polygons_is_merged = outside ? inside.is_merged () : true; + + auto polygons = begin_merged (); + + std::unique_ptr res (new FlatRegion ()); + std::unique_ptr prev; + + int steps_from = 0; + + while (steps > 0) { + + db::Coord dx_chunk = dx, dy_chunk = dy; + int steps_chunk = steps; + + // We perform at most max_steps in one chunk. + // This is supposed to limit the search range and merge shapes instead of creating + // heavily overlapping ones. + if (steps > max_steps) { + steps_chunk = max_steps; + dx_chunk = db::coord_traits::rounded (dx * max_steps / double (steps)); + dy_chunk = db::coord_traits::rounded (dy * max_steps / double (steps)); + } + + steps -= steps_chunk; + dx -= dx_chunk; + dy -= dy_chunk; + + // NOTE: as we merge the inside region in the inside case, we can use distance 0 + db::Coord dist = outside ? std::max (dx_chunk, dy_chunk) : 0; + db::sized_inside_local_operation op (dx_chunk, dy_chunk, steps_chunk, mode, dist, outside, inside_polygons_is_merged); + + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); + + // indicate chunk in the progress description + proc.set_description (proc.description (&op) + tl::sprintf (tl::to_string (tr (" (steps %d..%d)")), steps_from + 1, steps_from + steps_chunk + 1)); + steps_from += steps_chunk; + + std::vector > others; + others.push_back (inside_polygons); + + std::vector results; + db::FlatRegion *res_flat = dynamic_cast (res.get ()); + tl_assert (res_flat != 0); + results.push_back (&res_flat->raw_polygons ()); + proc.run_flat (prev.get () ? prev->begin () : polygons, others, std::vector (), &op, results); + + // NOTE: in the last step we apply a polygon breaker in addition to "merge" so the + // result is granular for better deep mode performance + if (steps > 0) { + prev.reset (res->merged ()); + res.reset (new db::FlatRegion ()); + } else { + res.reset (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ()))); + } + + } + + return res.release (); +} + RegionDelegate * AsIfFlatRegion::and_with (const Region &other, PropertyConstraint property_constraint) const { diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index bc59155a1..f06c4c362 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -121,6 +121,8 @@ public: virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const; + virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const; virtual RegionDelegate *and_with (const Region &other, PropertyConstraint property_constraint) const; virtual RegionDelegate *not_with (const Region &other, PropertyConstraint property_constraint) const; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 8001acb73..5acb010f8 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1884,6 +1884,98 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const return res.release (); } +RegionDelegate * +DeepRegion::sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const +{ + return sized_inside (inside, outside, d, d, steps, mode); +} + +RegionDelegate * +DeepRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const +{ + // empirical value + const int max_steps = 25; + + if (steps <= 0 || empty ()) { + // Nothing to do - NOTE: don't return EmptyRegion because we want to + // maintain "deepness" + return clone (); + } + + if (dx < 0 || dy < 0) { + throw tl::Exception (tl::to_string (tr ("'sized_inside' operation does not make sense with negative sizing"))); + } + + if (dx == 0 && dy == 0) { + steps = 1; + } + + const db::DeepRegion *inside_deep = dynamic_cast (inside.delegate ()); + if (! inside_deep) { + return db::AsIfFlatRegion::sized_inside (inside, outside, dx, dy, steps, mode); + } + + // NOTE: it does not provide benefits to merge the outside region, so just don't + const db::DeepLayer &inside_polygons = outside ? inside_deep->deep_layer () : inside_deep->merged_deep_layer (); + bool inside_polygons_is_merged = outside ? inside_deep->is_merged () : true; + + const db::DeepLayer &polygons = merged_deep_layer (); + + std::unique_ptr res (new db::DeepRegion (polygons.derived ())); + std::unique_ptr prev; + + int steps_from = 0; + + while (steps > 0) { + + db::Coord dx_chunk = dx, dy_chunk = dy; + int steps_chunk = steps; + + // We perform at most max_steps in one chunk. + // This is supposed to limit the search range and merge shapes instead of creating + // heavily overlapping ones. + if (steps > max_steps) { + steps_chunk = max_steps; + dx_chunk = db::coord_traits::rounded (dx * max_steps / double (steps)); + dy_chunk = db::coord_traits::rounded (dy * max_steps / double (steps)); + } + + steps -= steps_chunk; + dx -= dx_chunk; + dy -= dy_chunk; + + // NOTE: as we merge the inside region in the inside case, we can use distance 0 + db::Coord dist = outside ? std::max (dx_chunk, dy_chunk) : 0; + db::sized_inside_local_operation op (dx_chunk, dy_chunk, steps_chunk, mode, dist, outside, inside_polygons_is_merged); + + db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), &inside_polygons.layout (), &inside_polygons.initial_cell (), polygons.breakout_cells (), inside_polygons.breakout_cells ()); + configure_proc (proc); + proc.set_threads (polygons.store ()->threads ()); + proc.set_area_ratio (polygons.store ()->max_area_ratio ()); + proc.set_max_vertex_count (polygons.store ()->max_vertex_count ()); + + // indicate chunk in the progress description + proc.set_description (proc.description (&op) + tl::sprintf (tl::to_string (tr (" (steps %d..%d)")), steps_from + 1, steps_from + steps_chunk + 1)); + steps_from += steps_chunk; + + proc.run (&op, prev.get () ? prev->deep_layer ().layer () : polygons.layer (), inside_polygons.layer (), res->deep_layer ().layer ()); + + // NOTE: in the last step we apply a polygon breaker in addition to "merge" so the + // result is granular for better deep mode performance + if (steps > 0) { + prev.reset (dynamic_cast (res->merged ())); + tl_assert (prev.get () != 0); + res.reset (new db::DeepRegion (polygons.derived ())); + } else { + res.reset (dynamic_cast (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ())))); + tl_assert (res.get () != 0); + } + + } + + return res.release (); +} + template static Output *region_cop_impl (DeepRegion *region, db::CompoundRegionOperationNode &node) diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 6ddda9e14..80065a931 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -136,6 +136,8 @@ public: virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const; + virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const; virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index 30de5b35d..ff9f74c5f 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -61,7 +61,7 @@ public: /** * @brief Destructor */ - virtual ~EdgeSink () { }; + virtual ~EdgeSink () { } /** * @brief Start event @@ -1095,6 +1095,34 @@ private: void redo_or_process (const std::vector > &gen, bool redo); }; +/** + * @brief An edge sink feeding into an EdgeProcessor + */ +class DB_PUBLIC EdgesToEdgeProcessor + : public EdgeSink +{ +public: + EdgesToEdgeProcessor (db::EdgeProcessor &ep, db::EdgeProcessor::property_type prop) + : mp_ep (&ep), m_prop (prop) + { + // .. nothing yet .. + } + + virtual void put (const db::Edge &edge) + { + mp_ep->insert (edge, m_prop); + } + + virtual void put (const db::Edge &edge, int /*tag*/) + { + mp_ep->insert (edge, m_prop); + } + +private: + db::EdgeProcessor *mp_ep; + db::EdgeProcessor::property_type m_prop; +}; + } #endif diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 086ea9837..4529c159c 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -97,6 +97,8 @@ public: virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); } virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *sized_inside (const Region &, bool, coord_type, int, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *sized_inside (const Region &, bool, coord_type, coord_type, int, unsigned int) const { return new EmptyRegion (); } virtual RegionDelegate *and_with (const Region &, db::PropertyConstraint) const { return new EmptyRegion (); } virtual RegionDelegate *not_with (const Region &, db::PropertyConstraint) const { return new EmptyRegion (); } diff --git a/src/db/db/dbPath.h b/src/db/db/dbPath.h index d30bb5b29..ff1887bd4 100644 --- a/src/db/db/dbPath.h +++ b/src/db/db/dbPath.h @@ -294,6 +294,22 @@ public: } } + /** + * @brief Compatibility with path_ref + */ + path instantiate () const + { + return *this; + } + + /** + * @brief Compatibility with path_ref + */ + void instantiate (path &p) const + { + p = *this; + } + /** * @brief The (dummy) translation operator */ diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 68e758f61..550564174 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -1667,6 +1667,22 @@ public: return !equal (b); } + /** + * @brief Compatibility with polygon_ref + */ + polygon instantiate () const + { + return *this; + } + + /** + * @brief Compatibility with polygon_ref + */ + void instantiate (polygon &poly) const + { + poly = *this; + } + /** * @brief Returns true, if the polygon is a simple box */ @@ -2508,6 +2524,22 @@ public: m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, true /*normalize*/, remove_reflected); } + /** + * @brief Compatibility with polygon_ref + */ + simple_polygon instantiate () const + { + return *this; + } + + /** + * @brief Compatibility with polygon_ref + */ + void instantiate (simple_polygon &poly) const + { + poly = *this; + } + /** * @brief The (dummy) translation operator */ diff --git a/src/db/db/dbPolygonGenerators.h b/src/db/db/dbPolygonGenerators.h index d56bc99d7..cc34c8fce 100644 --- a/src/db/db/dbPolygonGenerators.h +++ b/src/db/db/dbPolygonGenerators.h @@ -531,6 +531,30 @@ private: unsigned int m_mode; }; +/** + * @brief A polygon sink feeding into an EdgeProcessor + */ +class DB_PUBLIC PolygonsToEdgeProcessor + : public PolygonSink +{ +public: + PolygonsToEdgeProcessor (db::EdgeProcessor &ep, db::EdgeProcessor::property_type prop, db::EdgeProcessor::property_type prop_step) + : mp_ep (&ep), m_prop (prop), m_prop_step (prop_step) + { + // .. nothing yet .. + } + + virtual void put (const db::Polygon &polygon) + { + mp_ep->insert (polygon, m_prop); + m_prop += m_prop_step; + } + +private: + db::EdgeProcessor *mp_ep; + db::EdgeProcessor::property_type m_prop, m_prop_step; +}; + } #endif diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 2d04a2758..a8294dffa 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -309,6 +309,32 @@ Region::sized (coord_type dx, coord_type dy, unsigned int mode) const return Region (mp_delegate->sized (dx, dy, mode)); } +Region & +Region::size_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode) +{ + set_delegate (mp_delegate->sized_inside (inside, outside, d, steps, mode)); + return *this; +} + +Region & +Region::size_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) +{ + set_delegate (mp_delegate->sized_inside (inside, outside, dx, dy, steps, mode)); + return *this; +} + +Region +Region::sized_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const +{ + return Region (mp_delegate->sized_inside (inside, outside, d, steps, mode)); +} + +Region +Region::sized_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const +{ + return Region (mp_delegate->sized_inside (inside, outside, dx, dy, steps, mode)); +} + void Region::round_corners (double rinner, double router, unsigned int n) { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 145018ef0..3ed7d96ae 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1027,6 +1027,59 @@ public: */ Region sized (coord_type dx, coord_type dy, unsigned int mode = 2) const; + /** + * @brief Size the region incrementally + * + * This method applies an incremental sizing to the region. Before the sizing is done, the + * region is merged if this is not the case already. Incremental sizing is confined to be inside a certain region. + * Only positive or zero sizing values are supported. + * + * @param inside The confinement region + * @param outside If true, "inside" is negative - i.e. sizing is performed outside the "inside" region + * @param d The (isotropic) sizing value + * @param steps The number of steps to take + * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. + * @return A reference to self + */ + Region &size_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode = 2); + + /** + * @brief Size the region incrementally and anisotropically + * + * This method applies an incremental sizing to the region. Before the sizing is done, the + * region is merged if this is not the case already. Incremental sizing is confined to be inside a certain region. + * Only positive or zero sizing values are supported. + * + * @param inside The confinement region + * @param outside If true, "inside" is negative - i.e. sizing is performed outside the "inside" region + * @param dx The x sizing value + * @param dy The y sizing value + * @param steps The number of steps to take + * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. + * @return A reference to self + */ + Region &size_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2); + + /** + * @brief Returns the sized region + * + * This is an out-of-place version of the size method with isotropic sizing + * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). + * + * Merged semantics applies. + */ + Region sized_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode = 2) const; + + /** + * @brief Returns the sized region + * + * This is an out-of-place version of the size method with anisotropic sizing + * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). + * + * Merged semantics applies. + */ + Region sized_inside (const db::Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2) const; + /** * @brief Boolean AND operator */ diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index cfc627cf7..7bbc64c9d 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -226,6 +226,8 @@ public: virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0; virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0; + virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const = 0; + virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const = 0; virtual RegionDelegate *and_with (const Region &other, PropertyConstraint prop_constraint) const = 0; virtual RegionDelegate *not_with (const Region &other, PropertyConstraint prop_constraint) const = 0; diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index bd82acc6f..291f78ecb 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1944,6 +1944,165 @@ std::string two_bool_and_not_local_operation_with_properties::descri template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; +// --------------------------------------------------------------------------------------------- +// sized_inside_local_operation implementation + +template +sized_inside_local_operation::sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, db::Coord dist, bool outside, bool inside_is_merged) + : m_dx (dx), m_dy (dy), m_dist (dist), m_steps (steps), m_mode (mode), m_outside (outside), m_inside_is_merged (inside_is_merged) +{ + // .. nothing yet .. +} + +template +db::Coord +sized_inside_local_operation::dist () const +{ + return m_dist; +} + +template +OnEmptyIntruderHint +sized_inside_local_operation::on_empty_intruder_hint () const +{ + return m_outside ? Ignore : Drop; +} + +template +std::string +sized_inside_local_operation::description () const +{ + return tl::to_string (tr ("Sized inside")); +} + +template +void +sized_inside_local_operation::do_compute_local (db::Layout *layout, db::Cell *subject_cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + double mag = 1.0; + double angle = 1.0; + if (proc->vars ()) { + const db::ICplxTrans &tr = proc->vars ()->single_variant_transformation (subject_cell->cell_index ()); + mag = tr.mag (); + angle = tr.angle (); + } + + double dx_with_mag = m_dx / mag; + double dy_with_mag = m_dy / mag; + if (fabs (angle - 90.0) < 45.0) { + // TODO: how to handle x/y swapping on arbitrary angles? + std::swap (dx_with_mag, dy_with_mag); + } + + // collect subjects and intruder shapes + + std::vector subjects; + std::set inside; + + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (typename shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + inside.insert (&interactions.intruder_shape (*j).second); + } + } + + // For empty intruders we can optimize the size sequence to a single step + // (NOTE: this is not strictly true as it ignores effects due to corner clipping + // which can be accumulative) + int steps = inside.empty () ? 1 : m_steps; + + // Merge the inside region shapes as we are going to use them multiple times + std::vector inside_merged; + if (inside.size () > 1 && ! m_inside_is_merged && m_steps > 1) { + + db::EdgeProcessor ep; + db::SimpleMerge op; + db::EdgeContainer ec (inside_merged); + + db::EdgeProcessor::property_type p = 0; + for (auto i = inside.begin (); i != inside.end (); ++i, ++p) { + ep.insert (**i, p); + } + ep.process (ec, op); + + inside.clear (); + + } + + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + subjects.push_back (db::Polygon ()); + interactions.subject_shape (i->first).instantiate (subjects.back ()); + } + + // prepare the edge processors involved + + db::EdgeProcessor ep_and; + ep_and.set_base_verbosity (50); + + // the main sizing loop + + db::Coord sx_last = 0, sy_last = 0; + + for (int step = 0; step < steps && ! subjects.empty (); ++step) { + + db::Coord sx = db::coord_traits::rounded (dx_with_mag * (step + 1) / double (steps)); + db::Coord sy = db::coord_traits::rounded (dy_with_mag * (step + 1) / double (steps)); + db::Coord dx = sx - sx_last; + db::Coord dy = sy - sy_last; + sx_last = sx; + sy_last = sy; + + if (! inside.empty () || ! inside_merged.empty ()) { + + ep_and.clear (); + + db::EdgesToEdgeProcessor e2ep (ep_and, 0); + db::SizingPolygonFilter siz (e2ep, dx, dy, m_mode); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + siz.put (*i); + } + + db::EdgeProcessor::property_type p = 1; + for (auto i = inside.begin (); i != inside.end (); ++i) { + ep_and.insert (**i, p); + p += 2; + } + for (auto i = inside_merged.begin (); i != inside_merged.end (); ++i) { + ep_and.insert (*i, p); + } + + db::PolygonContainer pc (subjects, true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, false /*min. coherence*/); + db::BooleanOp op (m_outside ? db::BooleanOp::ANotB : db::BooleanOp::And); + ep_and.process (pg, op); + + } else { + + std::vector sized_subjects; + db::PolygonContainer pc (sized_subjects, true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, false /*min. coherence*/); + db::SizingPolygonFilter siz (pg, dx, dy, m_mode); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + siz.put (*i); + } + + sized_subjects.swap (subjects); + + } + + } + + db::polygon_ref_generator gen (layout, result); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + gen.put (*i); + } +} + +template class DB_PUBLIC sized_inside_local_operation; +template class DB_PUBLIC sized_inside_local_operation; + // --------------------------------------------------------------------------------------------- SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index 27b09b187..ba8129696 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -463,6 +463,38 @@ private: typedef two_bool_and_not_local_operation_with_properties TwoBoolAndNotLocalOperationWithProperties; +/** + * @brief Implements "sized_inside" + */ +template +class DB_PUBLIC sized_inside_local_operation + : public local_operation +{ +public: + sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, db::Coord dist, bool outside, bool inside_is_merged); + + virtual db::Coord dist () const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + + virtual void do_compute_local (db::Layout *layout, db::Cell *subject_cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase * /*proc*/) const; + + virtual const db::TransformationReducer *vars () const + { + return m_dx != m_dy ? (const db::TransformationReducer *) &m_vars_anisotropic : (const db::TransformationReducer *) &m_vars_isotropic; + } + +private: + db::Coord m_dx, m_dy; + db::Coord m_dist; + int m_steps; + unsigned int m_mode; + bool m_outside; + bool m_inside_is_merged; + db::MagnificationAndOrientationReducer m_vars_anisotropic; + db::MagnificationReducer m_vars_isotropic; +}; + /** * @brief Implements a merge operation with an overlap count * With a given wrap_count, the result will only contains shapes where diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index cfd5205e2..8e98a9656 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1008,6 +1008,84 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) return *region; } +static db::Region +sized_inside_ddm (const db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode) +{ + return region->sized_inside (inside, false, dx, dy, steps, mode); +} + +static db::Region +sized_inside_dvm (const db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode) +{ + return region->sized_inside (inside, false, dv.x (), dv.y (), steps, mode); +} + +static db::Region +sized_inside_dm (const db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode) +{ + return region->sized_inside (inside, false, d, steps, mode); +} + +static db::Region & +size_inside_ddm (db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode) +{ + region->size_inside (inside, false, dx, dy, steps, mode); + return *region; +} + +static db::Region & +size_inside_dvm (db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode) +{ + region->size_inside (inside, false, dv.x (), dv.y (), steps, mode); + return *region; +} + +static db::Region & +size_inside_dm (db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode) +{ + region->size_inside (inside, false, d, steps, mode); + return *region; +} + +static db::Region +sized_outside_ddm (const db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode) +{ + return region->sized_inside (inside, true, dx, dy, steps, mode); +} + +static db::Region +sized_outside_dvm (const db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode) +{ + return region->sized_inside (inside, true, dv.x (), dv.y (), steps, mode); +} + +static db::Region +sized_outside_dm (const db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode) +{ + return region->sized_inside (inside, true, d, steps, mode); +} + +static db::Region & +size_outside_ddm (db::Region *region, const db::Region &inside, db::Coord dx, db::Coord dy, int steps, unsigned int mode) +{ + region->size_inside (inside, true, dx, dy, steps, mode); + return *region; +} + +static db::Region & +size_outside_dvm (db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode) +{ + region->size_inside (inside, true, dv.x (), dv.y (), steps, mode); + return *region; +} + +static db::Region & +size_outside_dm (db::Region *region, const db::Region &inside, db::Coord d, int steps, unsigned int mode) +{ + region->size_inside (inside, true, d, steps, mode); + return *region; +} + static db::Edges edges (const db::Region *region, db::PolygonToEdgeProcessor::EdgeMode mode) { @@ -1929,6 +2007,130 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("size_inside", &size_inside_ddm, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), + "@brief Incremental, anisotropic sizing inside of another region\n" + "\n" + "@param inside The region the incremental sizing will stay inside.\n" + "@param dx The x sizing value\n" + "@param dy The y sizing value\n" + "@param steps The number of steps to take\n" + "@param mode The sizing mode (see \\size)\n" + "\n" + "@return The region after the sizing has been applied (self)\n" + "\n" + "Sizes the region, keeping inside another region and performing the size in discrete steps.\n" + "\n" + "Using this method is equivalent to applying a single-step size and consecutively doing a boolean AND with the 'inside' region. " + "This is repeated until the full sizing value is applied.\n" + "\n" + "This operation is employed to implement latch-up rules, where a device needs to be close to a well tap within the " + "same well. For this, the tap footprint is incrementally sized, with the well as the 'inside' region. The steps is chosen so " + "the per-step sizing is somewhat less than the minimum well space. Sizing the tap shape results in a growing footprint that " + "follows the well contours and a small enough per-step sizing value ensures the sized contour does not cross well gaps.\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("size_outside", &size_outside_ddm, gsi::arg ("outside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), + "@brief Incremental, anisotropic sizing outside of another region\n" + "\n" + "This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. " + "Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("size_inside", &size_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Incremental, anisotropic sizing inside of another region\n" + "\n" + "@return The region after the sizing has applied (self)\n" + "\n" + "This method is equivalent to \"size_inside(dv.x, dv.y, steps, mode)\".\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("size_outside", &size_outside_dvm, gsi::arg ("outside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Incremental, anisotropic sizing outside of another region\n" + "\n" + "This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. " + "Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("size_inside", &size_inside_dm, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Incremental, isotropic sizing inside of another region\n" + "\n" + "@return The region after the sizing has applied (self)\n" + "\n" + "This method is equivalent to \"size_inside(d, d, steps, mode)\".\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("size_outside", &size_outside_dm, gsi::arg ("outside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Incremental, anisotropic sizing outside of another region\n" + "\n" + "This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. " + "Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("sized_inside", &sized_inside_ddm, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), + "@brief Returns the incrementally and anisotropically sized region\n" + "\n" + "@return The sized region\n" + "\n" + "This method returns the incrementally sized region (see \\size_inside), but does not modify self.\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + ) + + method_ext ("sized_outside", &sized_outside_ddm, gsi::arg ("outside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), + "@brief Incremental, anisotropic sizing outside of another region\n" + "\n" + "This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. " + "Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("sized_inside", &sized_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Returns the incrementally and anisotropically sized region\n" + "\n" + "@return The sized region\n" + "\n" + "This method returns the incrementally sized region (see \\size_inside), but does not modify self.\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + "\n" + "This variant has been introduced in version 0.28." + ) + + method_ext ("sized_outside", &sized_outside_dvm, gsi::arg ("outside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Incremental, anisotropic sizing outside of another region\n" + "\n" + "This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. " + "Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + + method_ext ("sized_inside", &sized_inside_dm, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Returns the incrementally sized region\n" + "\n" + "@return The sized region\n" + "\n" + "This method returns the incrementally sized region (see \\size_inside), but does not modify self.\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" + ) + + method_ext ("sized_outside", &sized_outside_dm, gsi::arg ("outside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + "@brief Incremental, anisotropic sizing outside of another region\n" + "\n" + "This method is equivalent to \\size_inside, except that sizing is performed outside the given 'outside' region. " + "Technically this corresponds to a boolean 'NOT' operation instead of a boolean 'AND'.\n" + "\n" + "This method has been introduced in version 0.29.3." + ) + method_ext ("andnot", &andnot, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Returns the boolean AND and NOT between self and the other region\n" "\n" diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index eaaee9e8c..126dda6cc 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2566,6 +2566,292 @@ TEST(56_RegionsFromShapes) EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5), false).area (), 10000); } +TEST(60_sized_inside) +{ + db::Region r, inside; + r.insert (db::Box (-10, 20, 20, 60)); + r.insert (db::Box (20, 20, 30, 60)); + inside.insert (db::Box (-10, 10, 100, 100)); + + EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 40, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (inside, false, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (inside, false, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (inside, false, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (inside, false, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (inside, false, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)"); + + db::Region d; + d = r; + d.size_inside (inside, false, 0, 20, 10); + EXPECT_EQ (d.to_string (), "(-10,10;-10,80;30,80;30,10)"); + d = r; + d.size_inside (inside, false, 1, 2, 0); + EXPECT_EQ (d.to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + d = r; + d.size_inside (inside, false, 10, 20, 10); + EXPECT_EQ (d.to_string (), "(-10,10;-10,80;40,80;40,10)"); + + EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 40).to_string (), ""); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 0, 40).to_string (), ""); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 20, 10).to_string (), ""); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 10, 20, 10).to_string (), ""); + + try { + // no negative sizing + r.sized_inside (inside, false, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (inside, false, -1, 1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (inside, false, 1, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } +} + +TEST(61_sized_outside) +{ + db::Region r, outside; + r.insert (db::Box (-10, 20, 20, 60)); + r.insert (db::Box (20, 20, 30, 60)); + outside.insert (db::Box (-20, 0, -10, 110)); + outside.insert (db::Box (100, 0, 110, 110)); + outside.insert (db::Box (-20, 0, 110, 10)); + outside.insert (db::Box (-20, 100, 110, 110)); + + EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 40, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (outside, true, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (outside, true, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (outside, true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (outside, true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (outside, true, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)"); + + EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)"); + + EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)"); + + EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 20, 10).to_string (), "(-10,0;-10,80;30,80;30,0)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 10, 20, 10).to_string (), "(-20,0;-20,80;40,80;40,0)"); + + try { + // no negative sizing + r.sized_inside (outside, true, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (outside, true, -1, 1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (outside, true, 1, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } +} + +TEST(62_sized_inside_deep) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Region r (dss), inside (dss), empty (dss); + + r.insert (db::Box (-10, 20, 20, 60)); + r.insert (db::Box (20, 20, 30, 60)); + inside.insert (db::Box (-10, 10, 100, 100)); + + EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (db::Region (), false, 40, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (inside, false, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (inside, false, 40, 40).to_string (), ""); + + EXPECT_EQ (empty.sized_inside (empty, false, 0, 40).to_string (), ""); + EXPECT_EQ (empty.sized_inside (empty, false, 40, 40).to_string (), ""); + EXPECT_EQ (empty.sized_inside (inside, false, 0, 40).to_string (), ""); + EXPECT_EQ (empty.sized_inside (inside, false, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (inside, false, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (inside, false, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (inside, false, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)"); + EXPECT_EQ (r.sized_inside (inside, false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (inside, false, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)"); + + EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 40).to_string (), ""); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 0, 40).to_string (), ""); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 40, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (db::Region (), false, 0, 20, 10).to_string (), ""); + EXPECT_EQ (r.sized_inside (db::Region (), false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), false, 10, 20, 10).to_string (), ""); + + EXPECT_EQ (r.sized_inside (empty, false, 0, 40).to_string (), ""); + EXPECT_EQ (r.sized_inside (empty, false, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, false, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, false, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (empty, false, 0, 0, 40).to_string (), ""); + EXPECT_EQ (r.sized_inside (empty, false, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, false, 40, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (empty, false, 0, 20, 10).to_string (), ""); + EXPECT_EQ (r.sized_inside (empty, false, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, false, 10, 20, 10).to_string (), ""); + + try { + // no negative sizing + r.sized_inside (inside, false, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (inside, false, -1, 1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (inside, false, 1, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } +} + +TEST(63_sized_outside_deep) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Region r (dss), outside (dss), empty (dss); + + r.insert (db::Box (-10, 20, 20, 60)); + r.insert (db::Box (20, 20, 30, 60)); + outside.insert (db::Box (-20, 0, -10, 110)); + outside.insert (db::Box (100, 0, 110, 110)); + outside.insert (db::Box (-20, 0, 110, 10)); + outside.insert (db::Box (-20, 100, 110, 110)); + + EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (db::Region (), true, 40, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (outside, true, 0, 40).to_string (), ""); + EXPECT_EQ (db::Region ().sized_inside (outside, true, 40, 40).to_string (), ""); + + EXPECT_EQ (empty.sized_inside (empty, true, 0, 40).to_string (), ""); + EXPECT_EQ (empty.sized_inside (empty, true, 40, 40).to_string (), ""); + EXPECT_EQ (empty.sized_inside (outside, true, 0, 40).to_string (), ""); + EXPECT_EQ (empty.sized_inside (outside, true, 40, 40).to_string (), ""); + + EXPECT_EQ (r.sized_inside (outside, true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, -1).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (outside, true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 40, 40, 40).to_string (), "(-10,10;-10,100;70,100;70,10)"); + + EXPECT_EQ (r.sized_inside (outside, true, 0, 20, 10).to_string (), "(-10,10;-10,80;30,80;30,10)"); + EXPECT_EQ (r.sized_inside (outside, true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (outside, true, 10, 20, 10).to_string (), "(-10,10;-10,80;40,80;40,10)"); + + EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)"); + + EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 40, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)"); + + EXPECT_EQ (r.sized_inside (db::Region (), true, 0, 20, 10).to_string (), "(-10,0;-10,80;30,80;30,0)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (db::Region (), true, 10, 20, 10).to_string (), "(-20,0;-20,80;40,80;40,0)"); + + EXPECT_EQ (r.sized_inside (empty, true, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, true, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, true, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)"); + + EXPECT_EQ (r.sized_inside (empty, true, 0, 0, 40).to_string (), "(-10,20;-10,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, true, 1, 1, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, true, 40, 40, 40).to_string (), "(-50,-20;-50,100;70,100;70,-20)"); + + EXPECT_EQ (r.sized_inside (empty, true, 0, 20, 10).to_string (), "(-10,0;-10,80;30,80;30,0)"); + EXPECT_EQ (r.sized_inside (empty, true, 1, 2, 0).to_string (), "(-10,20;-10,60;20,60;20,20);(20,20;20,60;30,60;30,20)"); + EXPECT_EQ (r.sized_inside (empty, true, 10, 20, 10).to_string (), "(-20,0;-20,80;40,80;40,0)"); + + try { + // no negative sizing + r.sized_inside (outside, true, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (outside, true, -1, 1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } + try { + // no negative sizing + r.sized_inside (outside, true, 1, -1, 1); + EXPECT_EQ (true, false); + } catch (...) { + } +} + TEST(100_Processors) { db::Region r; diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 9dd40b6ca..4a3f99e18 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -3056,23 +3056,34 @@ The following images shows the effect of some rectangle filter modes:

Usage:

  • layer.size(d [, mode])
  • -
  • layer.size(dx, dy [, mode]))
  • +
  • layer.size(d, size_inside(l) [, steps(n)] [, mode])
  • +
  • layer.size(d, size_outside(l) [, steps(n)] [, mode])
  • +
  • layer.size(dx, dy [, mode])
  • +
  • layer.size(dx, dy, size_inside(l) [, steps(n)] [, mode])
  • +
  • layer.size(dx, dy, size_outside(l) [, steps(n)] [, mode])

-See sized. The size method basically does the same but modifies the layer +See sized for a description of the options. +The size method basically does the same but modifies the layer it is called on. The input layer is returned and available for further processing.

"sized" - Polygon sizing (per-edge biasing)

Usage:

    -
  • layer.sized(d [, mode])
  • -
  • layer.sized(dx, dy [, mode]))
  • +
  • layer.sized(d [, mode] [, size_inside(l) [, steps(n)]])
  • +
  • layer.sized(d, size_inside(l) [, steps(n)] [, mode])
  • +
  • layer.sized(d, size_outside(l) [, steps(n)] [, mode])
  • +
  • layer.sized(dx, dy [, mode])
  • +
  • layer.sized(dx, dy, size_inside(l) [, steps(n)] [, mode])
  • +
  • layer.sized(dx, dy, size_outside(l) [, steps(n)] [, mode])

This method requires a polygon layer. It will apply a bias per edge of the polygons and return the biased layer. The layer that this method is called on is not modified.

+The alternative method size works like sized but modifies the layer it is called on. +

In the single-value form, that bias is applied both in horizontal or vertical direction. In the two-value form, the horizontal and vertical bias can be specified separately.

@@ -3100,8 +3111,6 @@ layer.sized(300.nm).raw.merged(2) Bias values can be given as floating-point values (in micron) or integer values (in database units). To explicitly specify the unit, use the unit denominators.

-size is working like sized but modifies the layer it is called on. -

The following images show the effect of various forms of the "sized" method:

@@ -3118,6 +3127,44 @@ The following images show the effect of various forms of the "sized" method:
+

+The "size_inside" option and the "steps" option implement incremental size. Incremental +size means that the sizing value is applied in n steps. Between the steps, the sized +shape is confined to the "size_inside" layer by means of a boolean "AND" operation. +

+This scheme is used to implement latch-up rules where a device active region has to +be close to a well tap. By using the well layer as the "size_inside" layer, the size function +follows the well contours. The steps have to selected such that the per-step size value +is smaller than the minimum space of the well shapes. With that, the sized shapes will +not cross over to neighbor well regions. Specifically, the per-step size has to be less +than about 70% of the minimum space to account for the minimum corner-to-corner case +with Euclidian space measurements. +

+"size_inside" and "steps" can be used with positive sizing values only. +A steps value of 0 will not execute any sizing at all. +

+"size_outside" acts like "size_inside", but instead of confining the sized region to the +inside of the given layer, it is confined to be outside of that layer. Technically, +a boolean "NOT" is performed instead of a boolean "AND". +

+An example for the "size_inside" option is this: +

+

+ntap.sized(30.um, size_inside(nwell), steps(100))
+
+

+The effect of the "size_inside" option is shown here: +

+ + + + + + + + + +

"smoothed" - Smoothes the polygons of the region

diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index e299ae380..dac86f2ed 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -427,6 +427,8 @@ the netter object.

Usage:

  • name(layer, name)
  • +
  • name(layer, name, layer_number, datatype_number)
  • +
  • name(layer, name, layer_info)

Layer names are listed in the LayoutToNetlist (L2N) or LVS database. They @@ -455,6 +457,12 @@ first time.

name can only be used once on a layer and the layer names must be unique (not taken by another layer). +

+The layer/datatype or LayerInfo specification is optional and will +be used to configure the internal layout. This information is also +persisted inside database files. Specifying a layer/datatype information +is useful, if a layer is not an original layer, but is to be restored +to an actual layout layer later.

"name_prefix" - Specifies the name prefix for auto-generated layer names

diff --git a/src/doc/doc/images/drc_sized_inside1.png b/src/doc/doc/images/drc_sized_inside1.png new file mode 100644 index 000000000..c0b69096a Binary files /dev/null and b/src/doc/doc/images/drc_sized_inside1.png differ diff --git a/src/doc/doc/images/drc_sized_inside2.png b/src/doc/doc/images/drc_sized_inside2.png new file mode 100644 index 000000000..0db6f427d Binary files /dev/null and b/src/doc/doc/images/drc_sized_inside2.png differ diff --git a/src/doc/doc/images/drc_sized_inside3.png b/src/doc/doc/images/drc_sized_inside3.png new file mode 100644 index 000000000..03fa77f63 Binary files /dev/null and b/src/doc/doc/images/drc_sized_inside3.png differ diff --git a/src/doc/doc/images/drc_sized_inside4.png b/src/doc/doc/images/drc_sized_inside4.png new file mode 100644 index 000000000..4845ec9f1 Binary files /dev/null and b/src/doc/doc/images/drc_sized_inside4.png differ diff --git a/src/doc/docDRCLVSResources.qrc b/src/doc/docDRCLVSResources.qrc index 905f12d19..8aa37dbed 100644 --- a/src/doc/docDRCLVSResources.qrc +++ b/src/doc/docDRCLVSResources.qrc @@ -125,6 +125,10 @@ doc/images/drc_sized4.png doc/images/drc_sized5.png doc/images/drc_sized6.png + doc/images/drc_sized_inside1.png + doc/images/drc_sized_inside2.png + doc/images/drc_sized_inside3.png + doc/images/drc_sized_inside4.png doc/images/drc_with_angle1.png doc/images/drc_with_angle2.png doc/images/drc_with_angle3.png diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 073956432..bcf27580b 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -313,6 +313,18 @@ module DRC DRCEdgeMode::new(RBA::EdgeMode::NotStep) end + def steps(arg) + DRCSizingSteps::new(arg) + end + + def size_inside(arg) + DRCSizingInside::new(arg) + end + + def size_outside(arg) + DRCSizingOutside::new(arg) + end + def padding_zero DRCDensityPadding::new(:zero) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 1fe0b10e9..0bb26948d 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -4333,7 +4333,7 @@ CODE value && raise("Value already specified") value = @engine._make_value(a) else - raise("Parameter #" + n.to_s + " does not have an expected type") + raise("Parameter #" + n.to_s + " if of unexpected type") end n += 1 end @@ -4518,7 +4518,7 @@ CODE limits = [ @engine._make_numeric_value_with_nil(a.begin), @engine._make_numeric_value_with_nil(a.end) ] nlimits = 2 else - raise("Parameter #" + n.to_s + " does not have an expected type") + raise("Parameter #" + n.to_s + " is of unexpected type") end n += 1 end @@ -4678,12 +4678,18 @@ TP_SCRIPT # %DRC% # @name sized # @brief Polygon sizing (per-edge biasing) - # @synopsis layer.sized(d [, mode]) - # @synopsis layer.sized(dx, dy [, mode])) + # @synopsis layer.sized(d [, mode] [, size_inside(l) [, steps(n)]]) + # @synopsis layer.sized(d, size_inside(l) [, steps(n)] [, mode]) + # @synopsis layer.sized(d, size_outside(l) [, steps(n)] [, mode]) + # @synopsis layer.sized(dx, dy [, mode]) + # @synopsis layer.sized(dx, dy, size_inside(l) [, steps(n)] [, mode]) + # @synopsis layer.sized(dx, dy, size_outside(l) [, steps(n)] [, mode]) # # This method requires a polygon layer. It will apply a bias per edge of the polygons # and return the biased layer. The layer that this method is called on is not modified. # + # The alternative method \size works like \sized but modifies the layer it is called on. + # # In the single-value form, that bias is applied both in horizontal or vertical direction. # In the two-value form, the horizontal and vertical bias can be specified separately. # @@ -4711,8 +4717,6 @@ TP_SCRIPT # Bias values can be given as floating-point values (in micron) or integer values (in # database units). To explicitly specify the unit, use the unit denominators. # - # \size is working like \sized but modifies the layer it is called on. - # # The following images show the effect of various forms of the "sized" method: # # @table @@ -4729,14 +4733,58 @@ TP_SCRIPT # @td @img(/images/drc_sized6.png) @/td # @/tr # @/table + # + # The "size_inside" option and the "steps" option implement incremental size. Incremental + # size means that the sizing value is applied in n steps. Between the steps, the sized + # shape is confined to the "size_inside" layer by means of a boolean "AND" operation. + # + # This scheme is used to implement latch-up rules where a device active region has to + # be close to a well tap. By using the well layer as the "size_inside" layer, the size function + # follows the well contours. The steps have to selected such that the per-step size value + # is smaller than the minimum space of the well shapes. With that, the sized shapes will + # not cross over to neighbor well regions. Specifically, the per-step size has to be less + # than about 70% of the minimum space to account for the minimum corner-to-corner case + # with Euclidian space measurements. + # + # "size_inside" and "steps" can be used with positive sizing values only. + # A steps value of 0 will not execute any sizing at all. + # + # "size_outside" acts like "size_inside", but instead of confining the sized region to the + # inside of the given layer, it is confined to be outside of that layer. Technically, + # a boolean "NOT" is performed instead of a boolean "AND". + # + # An example for the "size_inside" option is this: + # + # @code + # ntap.sized(30.um, size_inside(nwell), steps(100)) + # @/code + # + # The effect of the "size_inside" option is shown here: + # + # @table + # @tr + # @td @img(/images/drc_sized_inside1.png) @/td + # @td @img(/images/drc_sized_inside2.png) @/td + # @/tr + # @tr + # @td @img(/images/drc_sized_inside3.png) @/td + # @td @img(/images/drc_sized_inside4.png) @/td + # @/tr + # @/table + # # %DRC% # @name size # @brief Polygon sizing (per-edge biasing, modifies the layer) # @synopsis layer.size(d [, mode]) - # @synopsis layer.size(dx, dy [, mode])) + # @synopsis layer.size(d, size_inside(l) [, steps(n)] [, mode]) + # @synopsis layer.size(d, size_outside(l) [, steps(n)] [, mode]) + # @synopsis layer.size(dx, dy [, mode]) + # @synopsis layer.size(dx, dy, size_inside(l) [, steps(n)] [, mode]) + # @synopsis layer.size(dx, dy, size_outside(l) [, steps(n)] [, mode]) # - # See \sized. The size method basically does the same but modifies the layer + # See \sized for a description of the options. + # The size method basically does the same but modifies the layer # it is called on. The input layer is returned and available for further processing. %w(size sized).each do |f| @@ -4749,8 +4797,13 @@ TP_SCRIPT dist = 0 + steps = nil + inside = nil + outside = nil mode = 2 values = [] + + n = 1 args.each do |a| if a.is_a?(1.class) || a.is_a?(Float) v = @engine._make_value(a) @@ -4758,10 +4811,62 @@ TP_SCRIPT values.push(v) elsif a.is_a?(DRCSizingMode) mode = a.value + elsif a.is_a?(DRCSizingSteps) + steps = a.value + elsif a.is_a?(DRCSizingInside) + inside = a.value + elsif a.is_a?(DRCSizingOutside) + outside = a.value + else + raise("Parameter #" + n.to_s + " is of unexpected type") end + n += 1 end aa = [] + + f_size = :size + f_sized = :sized + + if inside && outside + raise "Cannot use 'inside' and 'outside' together" + end + + if steps + if !inside && !outside + raise "'steps' is only allowed with 'inside'" + end + if !steps.is_a?(1.class) + raise "'steps' must be an integer value" + end + end + + if inside || outside + + if inside + + inside.is_a?(DRCLayer) || raise("'inside' argument needs to be a DRC layer") + inside.data.is_a?(RBA::Region) || raise("'inside' requires a polygon layer") + aa.push(inside.data) + + f_size = :size_inside + f_sized = :sized_inside + + else + + outside.is_a?(DRCLayer) || raise("'outside' argument needs to be a DRC layer") + outside.data.is_a?(RBA::Region) || raise("'outside' requires a polygon layer") + aa.push(outside.data) + + f_size = :size_outside + f_sized = :sized_outside + + end + + steps ||= 1 + + end + if values.size < 1 raise "Method requires one or two sizing values" elsif values.size > 2 @@ -4771,17 +4876,21 @@ TP_SCRIPT aa.push(values[-1]) end + if inside || outside + aa.push(steps) + end + aa.push(mode) - + if :#{f} == :size && @engine.is_tiled? # in tiled mode, no modifying versions are available - self.data = @engine._tcmd(self.data, dist, RBA::Region, :sized, *aa) + self.data = @engine._tcmd(self.data, dist, RBA::Region, f_sized, *aa) self elsif :#{f} == :size - @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa) + @engine._tcmd(self.data, dist, RBA::Region, f_size, *aa) self else - DRCLayer::new(@engine, @engine._tcmd(self.data, dist, RBA::Region, :#{f}, *aa)) + DRCLayer::new(@engine, @engine._tcmd(self.data, dist, RBA::Region, f_sized, *aa)) end end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 11d826943..56cf18023 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -57,6 +57,30 @@ module DRC end end + # A wrapper for the sizing steps value + class DRCSizingSteps + attr_accessor :value + def initialize(v) + self.value = v + end + end + + # A wrapper for the sizing "inside" value + class DRCSizingInside + attr_accessor :value + def initialize(v) + self.value = v + end + end + + # A wrapper for the sizing "outside" value + class DRCSizingOutside + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for the edge mode value for Region#edges class DRCEdgeMode attr_accessor :value diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index b6a4da348..a561ad241 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1943,3 +1943,13 @@ TEST(122_NamedLayers) compare_text_files (output, au_output); } + +TEST(130_size_inside_outside) +{ + run_test (_this, "130", false); +} + +TEST(130d_size_inside_outside) +{ + run_test (_this, "130", true); +} diff --git a/testdata/drc/drcSimpleTests_130.drc b/testdata/drc/drcSimpleTests_130.drc new file mode 100644 index 000000000..a8bf10a8c --- /dev/null +++ b/testdata/drc/drcSimpleTests_130.drc @@ -0,0 +1,54 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l1.sized(0.0, steps(50), size_inside(l2)).output(100, 0) +l1.sized(20.0, steps(0), size_inside(l2)).output(101, 0) + +l1.sized(20.0, steps(50), size_inside(l2)).output(110, 0) +l1.sized(50.0, steps(50), size_outside(l2)).output(111, 0) + +l1d = l1.dup +l1d.size(20.0, steps(50), size_inside(l2)) +l1d.output(120, 0) + +l1d = l1.dup +l1d.size(50.0, steps(50), size_outside(l2)) +l1d.output(121, 0) + +l1.sized(10.0, 20.0, steps(50), size_inside(l2)).output(130, 0) +l1.sized(25.0, 50.0, steps(50), size_outside(l2)).output(131, 0) + +error = nil +begin + l2.sized(-1.0, steps(50), size_outside(l2)) +rescue + error = true +end +if !error + raise "error expected!" +end + +error = nil +begin + l2.sized(-1.0, 2.0, steps(50), size_outside(l2)) +rescue + error = true +end +if !error + raise "error expected!" +end + + + + diff --git a/testdata/drc/drcSimpleTests_130.gds b/testdata/drc/drcSimpleTests_130.gds new file mode 100644 index 000000000..3871953a1 Binary files /dev/null and b/testdata/drc/drcSimpleTests_130.gds differ diff --git a/testdata/drc/drcSimpleTests_au130.gds b/testdata/drc/drcSimpleTests_au130.gds new file mode 100644 index 000000000..4255d481c Binary files /dev/null and b/testdata/drc/drcSimpleTests_au130.gds differ diff --git a/testdata/drc/drcSimpleTests_au130d.gds b/testdata/drc/drcSimpleTests_au130d.gds new file mode 100644 index 000000000..eee1ba69f Binary files /dev/null and b/testdata/drc/drcSimpleTests_au130d.gds differ diff --git a/version.sh b/version.sh index 265eb5848..a7afe8328 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.29.2" +KLAYOUT_VERSION="0.29.3" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.29.2" +KLAYOUT_PYPI_VERSION="0.29.3" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")