From 25165c46a3de059acb1168397efa4f6e5ceb19d3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Jun 2024 00:49:33 +0200 Subject: [PATCH 01/16] WIP --- src/db/db/dbAsIfFlatRegion.cc | 17 +++++++ src/db/db/dbAsIfFlatRegion.h | 2 + src/db/db/dbDeepRegion.cc | 30 +++++++++++ src/db/db/dbDeepRegion.h | 2 + src/db/db/dbEmptyRegion.h | 2 + src/db/db/dbRegion.cc | 26 ++++++++++ src/db/db/dbRegion.h | 59 ++++++++++++++++++++++ src/db/db/dbRegionDelegate.h | 2 + src/db/db/gsiDeclDbRegion.cc | 93 +++++++++++++++++++++++++++++++++++ 9 files changed, 233 insertions(+) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 2171c4f37..0c1c4245d 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1377,6 +1377,23 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } } +RegionDelegate * +AsIfFlatRegion::sized_inside (const Region *inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const +{ + return sized_inside (inside, d, d, steps, mode, stop_at); +} + +RegionDelegate * +AsIfFlatRegion::sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const +{ + if (steps <= 0) { + return clone (); + } + + return 0; // @@@ + +} + 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..dffcb3af0 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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) 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 d52df7f9c..132d47e4d 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1781,6 +1781,36 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const return res.release (); } +RegionDelegate * +DeepRegion::sized_inside (const Region *inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const +{ + if (steps <= 0) { + return clone (); + } + + + return 0; // @@@ + +} + +RegionDelegate * +DeepRegion::sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const +{ + if (steps <= 0 || empty ()) { + // Nothing to do - NOTE: don't return EmptyRegion because we want to + // maintain "deepness" + return clone (); + } + + if (dx == dy) { + return sized_inside (inside, dx, steps, mode, stop_at); + } + + + return 0; // @@@ + +} + 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 1d5537554..a7e2e78f6 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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const; virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 086ea9837..62e405543 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 *, coord_type, int, unsigned int, const Region *) const { return new EmptyRegion (); } + virtual RegionDelegate *sized_inside (const Region *, coord_type, coord_type, int, unsigned int, const Region *) 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/dbRegion.cc b/src/db/db/dbRegion.cc index 2d04a2758..37c668350 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, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) +{ + set_delegate (mp_delegate->sized_inside (inside, d, steps, mode, stop_at)); + return *this; +} + +Region & +Region::size_inside (const db::Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) +{ + set_delegate (mp_delegate->sized_inside (inside, dx, dy, steps, mode, stop_at)); + return *this; +} + +Region +Region::sized_inside (const db::Region *inside, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) const +{ + return Region (mp_delegate->sized_inside (inside, d, steps, mode, stop_at)); +} + +Region +Region::sized_inside (const db::Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) const +{ + return Region (mp_delegate->sized_inside (inside, dx, dy, steps, mode, stop_at)); +} + 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..b4f0f3524 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1027,6 +1027,65 @@ 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 can be confined to be inside a certain region and a stop condition + * can be supplied. With a stop condition, sizing will stop when the sized region touches + * a shape on the "stop_at" region. + * + * @param inside The confinement region or 0 for "no confinement" + * @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. + * @param stop_at The stop condition or 0 for "not stopping" + * @return A reference to self + */ + Region &size_inside (const db::Region *inside, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); + + /** + * @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 can be confined to be inside a certain region and a stop condition + * can be supplied. With a stop condition, sizing will stop when the sized region touches + * a shape on the "stop_at" region. + * + * @param inside The confinement region or 0 for "no confinement" + * @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. + * @param stop_at The stop condition or 0 for "not stopping" + * @return A reference to self + */ + Region &size_inside (const db::Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); + + /** + * @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, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) 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, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) const; + /** * @brief Boolean AND operator */ diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index cfc627cf7..d8c4a1330 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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const = 0; + virtual RegionDelegate *sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) 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/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index cfd5205e2..7526b1ded 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1008,6 +1008,19 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) return *region; } +static db::Region +sized_inside_dvm (const db::Region *region, const db::Region *inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) +{ + return region->sized_inside (inside, dv.x (), dv.y (), steps, mode, stop_at); +} + +static db::Region & +size_inside_dvm (db::Region *region, const db::Region *inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) +{ + region->sized_inside (inside, dv.x (), dv.y (), steps, mode, stop_at); + return *region; +} + static db::Edges edges (const db::Region *region, db::PolygonToEdgeProcessor::EdgeMode mode) { @@ -1929,6 +1942,86 @@ 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 ("size_inside", (db::Region & (db::Region::*) (const db::Region *, db::Coord, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + "@brief Incremental, anisotropic sizing inside of another region\n" + "\n" + "@param inside The region the incremental sizing will stay inside. Can be nil to skip the inside condition.\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" + "@param stop_at The optional stop layer\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' regiuon. " + "This is repeated until the full sizing value is applied.\n" + "\n" + "This operaton is employed to implement latch-up rules where a device needs to be close to a well tap within the " + "same will. For this, the incremental size of the device active region with the well as the 'inside' region is applied. The step is chosen as " + "somewhat less than the minimum well space, so sizing the active region results in a growing footprint that " + "follows the well contours.\n" + "\n" + "A stop region can be specified, meaning that the sizing propagation will stop if the sized region " + "touches a shape from the stop region. In case of the latch-up rule application this will be the well tap layer.\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_inside", &size_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + "@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, stop_at)\".\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 ("size_inside", (db::Region & (db::Region::*) (const db::Region *, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + "@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, stop_at)\".\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 ("sized_inside", (db::Region (db::Region::*) (const db::Region *, db::Coord, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + "@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_inside", &sized_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + "@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 ("sized_inside", (db::Region (db::Region::*) (const db::Region *, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + "@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 ("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" From 55fc4a8728d40ae1e6aa15bf3392bf40df115e49 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Jun 2024 17:05:49 +0200 Subject: [PATCH 02/16] WIP --- src/db/db/dbAsIfFlatRegion.cc | 4 +- src/db/db/dbAsIfFlatRegion.h | 4 +- src/db/db/dbDeepRegion.cc | 54 +++++++-- src/db/db/dbDeepRegion.h | 4 +- src/db/db/dbEdgeProcessor.h | 30 ++++- src/db/db/dbEmptyRegion.h | 4 +- src/db/db/dbPath.h | 16 +++ src/db/db/dbPolygon.h | 32 ++++++ src/db/db/dbPolygonGenerators.h | 24 ++++ src/db/db/dbRegion.cc | 8 +- src/db/db/dbRegion.h | 12 +- src/db/db/dbRegionDelegate.h | 4 +- src/db/db/dbRegionLocalOperations.cc | 166 +++++++++++++++++++++++++++ src/db/db/dbRegionLocalOperations.h | 29 +++++ src/db/db/gsiDeclDbRegion.cc | 21 ++-- 15 files changed, 369 insertions(+), 43 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 0c1c4245d..312fac48a 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1378,13 +1378,13 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } RegionDelegate * -AsIfFlatRegion::sized_inside (const Region *inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const +AsIfFlatRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const { return sized_inside (inside, d, d, steps, mode, stop_at); } RegionDelegate * -AsIfFlatRegion::sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const +AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const { if (steps <= 0) { return clone (); diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index dffcb3af0..dedbec91a 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -121,8 +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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; - virtual RegionDelegate *sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) 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 132d47e4d..38ebbf9f2 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1782,19 +1782,13 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } RegionDelegate * -DeepRegion::sized_inside (const Region *inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const +DeepRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const { - if (steps <= 0) { - return clone (); - } - - - return 0; // @@@ - + return sized_inside (inside, d, d, steps, mode, stop_at); } RegionDelegate * -DeepRegion::sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const +DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const { if (steps <= 0 || empty ()) { // Nothing to do - NOTE: don't return EmptyRegion because we want to @@ -1802,13 +1796,49 @@ DeepRegion::sized_inside (const Region *inside, coord_type dx, coord_type dy, in return clone (); } - if (dx == dy) { - return sized_inside (inside, dx, steps, mode, stop_at); + const db::DeepRegion *inside_deep = dynamic_cast (inside.delegate ()); + if (! inside_deep) { + return db::AsIfFlatRegion::sized_inside (inside, dx, dy, steps, mode, stop_at); } + const db::DeepRegion *stop_at_deep = 0; + if (stop_at) { - return 0; // @@@ + stop_at_deep = dynamic_cast (stop_at->delegate ()); + if (! stop_at_deep) { + return db::AsIfFlatRegion::sized_inside (inside, dx, dy, steps, mode, stop_at); + } + if (&inside_deep->deep_layer ().layout () != &stop_at_deep->deep_layer ().layout () + || &inside_deep->deep_layer ().initial_cell () != &stop_at_deep->deep_layer ().initial_cell ()) { + throw tl::Exception (tl::to_string (tr ("'sized_inside' operation needs to use the same layout and top cell " + "for 'inside' and 'stop_at' arguments"))); + } + + } + + const db::DeepLayer &polygons = merged_deep_layer (); + const db::DeepLayer &inside_polygons = inside_deep->deep_layer (); + + db::sized_inside_local_operation op (dx, dy, steps, mode); + + 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 ()); + + std::unique_ptr res (new db::DeepRegion (polygons.derived ())); + + std::vector other_layers; + other_layers.push_back (inside_polygons.layer ()); + if (stop_at_deep) { + other_layers.push_back (stop_at_deep->deep_layer ().layer ()); + } + + proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ()); + + return res.release (); } template diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index a7e2e78f6..b4868a29f 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -136,8 +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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; - virtual RegionDelegate *sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) 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 62e405543..d9e6a850c 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -97,8 +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 *, coord_type, int, unsigned int, const Region *) const { return new EmptyRegion (); } - virtual RegionDelegate *sized_inside (const Region *, coord_type, coord_type, int, unsigned int, const Region *) const { return new EmptyRegion (); } + virtual RegionDelegate *sized_inside (const Region &, coord_type, int, unsigned int, const Region *) const { return new EmptyRegion (); } + virtual RegionDelegate *sized_inside (const Region &, coord_type, coord_type, int, unsigned int, const Region *) 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 37c668350..f7bc80213 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -310,27 +310,27 @@ Region::sized (coord_type dx, coord_type dy, unsigned int mode) const } Region & -Region::size_inside (const db::Region *inside, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) +Region::size_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) { set_delegate (mp_delegate->sized_inside (inside, d, steps, mode, stop_at)); return *this; } Region & -Region::size_inside (const db::Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) +Region::size_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) { set_delegate (mp_delegate->sized_inside (inside, dx, dy, steps, mode, stop_at)); return *this; } Region -Region::sized_inside (const db::Region *inside, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) const +Region::sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) const { return Region (mp_delegate->sized_inside (inside, d, steps, mode, stop_at)); } Region -Region::sized_inside (const db::Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) const +Region::sized_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) const { return Region (mp_delegate->sized_inside (inside, dx, dy, steps, mode, stop_at)); } diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index b4f0f3524..a1a485b43 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1037,14 +1037,14 @@ public: * can be supplied. With a stop condition, sizing will stop when the sized region touches * a shape on the "stop_at" region. * - * @param inside The confinement region or 0 for "no confinement" + * @param inside The confinement 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. * @param stop_at The stop condition or 0 for "not stopping" * @return A reference to self */ - Region &size_inside (const db::Region *inside, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); + Region &size_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); /** * @brief Size the region incrementally and anisotropically @@ -1056,7 +1056,7 @@ public: * can be supplied. With a stop condition, sizing will stop when the sized region touches * a shape on the "stop_at" region. * - * @param inside The confinement region or 0 for "no confinement" + * @param inside The confinement region * @param dx The x sizing value * @param dy The y sizing value * @param steps The number of steps to take @@ -1064,7 +1064,7 @@ public: * @param stop_at The stop condition or 0 for "not stopping" * @return A reference to self */ - Region &size_inside (const db::Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); + Region &size_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); /** * @brief Returns the sized region @@ -1074,7 +1074,7 @@ public: * * Merged semantics applies. */ - Region sized_inside (const db::Region *inside, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) const; + Region sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) const; /** * @brief Returns the sized region @@ -1084,7 +1084,7 @@ public: * * Merged semantics applies. */ - Region sized_inside (const db::Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) const; + Region sized_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) const; /** * @brief Boolean AND operator diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index d8c4a1330..5928b1f5a 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -226,8 +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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const = 0; - virtual RegionDelegate *sized_inside (const Region *inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const = 0; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const = 0; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) 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..427f857f7 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1944,6 +1944,172 @@ 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) + : m_dx (dx), m_dy (dy), m_steps (steps), m_mode (mode) +{ + // .. nothing yet .. +} + +template +db::Coord +sized_inside_local_operation::dist () const +{ + return std::max (0, std::max (m_dx, m_dy)); +} + +template +OnEmptyIntruderHint +sized_inside_local_operation::on_empty_intruder_hint () const +{ + return 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 (); + + const db::ICplxTrans &tr = proc->vars ()->single_variant_transformation (subject_cell->cell_index ()); + double mag = tr.mag (); + double 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; + std::set stop_at; + + 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) { + const std::pair &is = interactions.intruder_shape (*j); + if (is.first == 0) { + inside.insert (&is.second); + } else if (is.first == 1) { + stop_at.insert (&is.second); + } + } + } + + 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); + + db::EdgeProcessor ep_interact; + ep_interact.set_base_verbosity (50); + + // the main sizing loop + + db::Coord sx_last = 0, sy_last = 0; + + for (int step = 0; step < m_steps && ! subjects.empty (); ++step) { + + db::Coord sx = db::coord_traits::rounded (dx_with_mag * (step + 1) / double (m_steps)); + db::Coord sy = db::coord_traits::rounded (dy_with_mag * (step + 1) / double (m_steps)); + db::Coord dx = sx - sx_last; + db::Coord dy = sy - sy_last; + sx_last = sx; + sy_last = sy; + + 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; + } + + db::PolygonContainer pc (subjects, true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, false /*min. coherence*/); + db::BooleanOp op (db::BooleanOp::And); + ep_and.process (pg, op); + + if (! subjects.empty () && ! stop_at.empty ()) { + + // compute interations with "stop_at" + + ep_interact.clear (); + + db::EdgeProcessor::property_type p = 0; + for (auto i = subjects.begin (); i != subjects.end (); ++i, ++p) { + ep_interact.insert (*i, p); + } + + db::EdgeProcessor::property_type nstart = p; + for (auto i = stop_at.begin (); i != stop_at.end (); ++i, ++p) { + ep_interact.insert (**i, p); + } + + db::InteractionDetector id (0 /*interacting*/, nstart - 1); + id.set_include_touching (true); + db::EdgeSink es; + ep_interact.process (es, id); + id.finish (); + + std::set interacting; + for (db::InteractionDetector::iterator i = id.begin (); i != id.end (); ++i) { + interacting.insert (i->first); + } + + // drop interacting subjects + + if (! interacting.empty ()) { + std::vector::iterator iw = subjects.begin (); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + if (interacting.find (i - subjects.begin ()) == interacting.end ()) { + if (iw != i) { + iw->swap (*i); + } + ++iw; + } + } + subjects.erase (iw, subjects.end ()); + } + + } + + } + + 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..76f5ba4b4 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -463,6 +463,35 @@ 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); + + 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; + int m_steps; + unsigned int m_mode; + 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 7526b1ded..25c4ef3f4 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1009,13 +1009,13 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) } static db::Region -sized_inside_dvm (const db::Region *region, const db::Region *inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) +sized_inside_dvm (const db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) { return region->sized_inside (inside, dv.x (), dv.y (), steps, mode, stop_at); } static db::Region & -size_inside_dvm (db::Region *region, const db::Region *inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) +size_inside_dvm (db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) { region->sized_inside (inside, dv.x (), dv.y (), steps, mode, stop_at); return *region; @@ -1942,10 +1942,10 @@ 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 ("size_inside", (db::Region & (db::Region::*) (const db::Region *, db::Coord, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), "@brief Incremental, anisotropic sizing inside of another region\n" "\n" - "@param inside The region the incremental sizing will stay inside. Can be nil to skip the inside condition.\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" @@ -1960,12 +1960,13 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This is repeated until the full sizing value is applied.\n" "\n" "This operaton is employed to implement latch-up rules where a device needs to be close to a well tap within the " - "same will. For this, the incremental size of the device active region with the well as the 'inside' region is applied. The step is chosen as " + "same well. For this, the incremental size of the device active region with the well as the 'inside' region is applied. The step is chosen as " "somewhat less than the minimum well space, so sizing the active region results in a growing footprint that " "follows the well contours.\n" "\n" - "A stop region can be specified, meaning that the sizing propagation will stop if the sized region " - "touches a shape from the stop region. In case of the latch-up rule application this will be the well tap layer.\n" + "A stop region can be specified, meaning that the sizing propagation will stop if the sized shape " + "touches a shape from the stop region. If that happens, the sized shape is discarded. " + "In case of the latch-up rule application, the stop region will be the well tap layer.\n" "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" "\n" @@ -1982,7 +1983,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.29.3." ) + - method ("size_inside", (db::Region & (db::Region::*) (const db::Region *, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), "@brief Incremental, isotropic sizing inside of another region\n" "\n" "@return The region after the sizing has applied (self)\n" @@ -1993,7 +1994,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.29.3." ) + - method ("sized_inside", (db::Region (db::Region::*) (const db::Region *, db::Coord, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), "@brief Returns the incrementally and anisotropically sized region\n" "\n" "@return The sized region\n" @@ -2013,7 +2014,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This variant has been introduced in version 0.28." ) + - method ("sized_inside", (db::Region (db::Region::*) (const db::Region *, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), "@brief Returns the incrementally sized region\n" "\n" "@return The sized region\n" From d2479c7159cf25f17a9ad61f069150d638609f37 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Jun 2024 17:20:32 +0200 Subject: [PATCH 03/16] WIP --- src/db/db/dbAsIfFlatRegion.cc | 20 +++++++++++++++++++- src/db/db/dbRegionLocalOperations.cc | 10 +++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 312fac48a..75fe7a90c 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1390,8 +1390,26 @@ AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy return clone (); } - return 0; // @@@ + std::unique_ptr output (new FlatRegion ()); + std::vector results; + results.push_back (&output->raw_polygons ()); + db::sized_inside_local_operation op (dx, dy, steps, mode); + + db::local_processor proc; + proc.set_base_verbosity (base_verbosity ()); + proc.set_description (progress_desc ()); + proc.set_report_progress (report_progress ()); + + std::vector > others; + others.push_back (inside.begin ()); + if (stop_at) { + others.push_back (stop_at->begin ()); + } + + proc.run_flat (begin_merged (), others, std::vector (), &op, results); + + return output.release (); } RegionDelegate * diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 427f857f7..a38b068b4 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1982,9 +1982,13 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, tl_assert (results.size () == 1); std::unordered_set &result = results.front (); - const db::ICplxTrans &tr = proc->vars ()->single_variant_transformation (subject_cell->cell_index ()); - double mag = tr.mag (); - double angle = tr.angle (); + 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; From e63a7b59409d976b4d4138e0d36407ff231d4a74 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Jun 2024 18:21:30 +0200 Subject: [PATCH 04/16] WIP: optimization of sized_inside - distance can be reduced to 0 if 'inside' is merged --- src/db/db/dbAsIfFlatRegion.cc | 12 +++++++++--- src/db/db/dbDeepRegion.cc | 8 ++++++-- src/db/db/dbRegionLocalOperations.cc | 11 +++++++---- src/db/db/dbRegionLocalOperations.h | 3 ++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 75fe7a90c..235f8e02c 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1386,15 +1386,21 @@ AsIfFlatRegion::sized_inside (const Region &inside, coord_type d, int steps, uns RegionDelegate * AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const { - if (steps <= 0) { + 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"))); + } + std::unique_ptr output (new FlatRegion ()); std::vector results; results.push_back (&output->raw_polygons ()); - db::sized_inside_local_operation op (dx, dy, steps, mode); + db::sized_inside_local_operation op (dx, dy, steps, mode, true /*inside layer is merged*/); db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); @@ -1402,7 +1408,7 @@ AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy proc.set_report_progress (report_progress ()); std::vector > others; - others.push_back (inside.begin ()); + others.push_back (inside.begin_merged ()); if (stop_at) { others.push_back (stop_at->begin ()); } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 38ebbf9f2..5dbca4150 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1796,6 +1796,10 @@ DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, in return clone (); } + if (dx < 0 || dy < 0) { + throw tl::Exception (tl::to_string (tr ("'sized_inside' operation does not make sense with negative sizing"))); + } + const db::DeepRegion *inside_deep = dynamic_cast (inside.delegate ()); if (! inside_deep) { return db::AsIfFlatRegion::sized_inside (inside, dx, dy, steps, mode, stop_at); @@ -1818,9 +1822,9 @@ DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, in } const db::DeepLayer &polygons = merged_deep_layer (); - const db::DeepLayer &inside_polygons = inside_deep->deep_layer (); + const db::DeepLayer &inside_polygons = inside_deep->merged_deep_layer (); - db::sized_inside_local_operation op (dx, dy, steps, mode); + db::sized_inside_local_operation op (dx, dy, steps, mode, true /*inside layer 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); diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index a38b068b4..e0799dbe0 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1948,17 +1948,20 @@ template class DB_PUBLIC two_bool_and_not_local_operation_with_properties -sized_inside_local_operation::sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode) - : m_dx (dx), m_dy (dy), m_steps (steps), m_mode (mode) +sized_inside_local_operation::sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, bool inside_is_merged) + : m_dx (dx), m_dy (dy), m_steps (steps), m_mode (mode), m_dist (0) { - // .. nothing yet .. + m_dist = 0; + if (! inside_is_merged) { + m_dist = std::max (0, std::max (m_dx, m_dy)); + } } template db::Coord sized_inside_local_operation::dist () const { - return std::max (0, std::max (m_dx, m_dy)); + return m_dist; } template diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index 76f5ba4b4..46ff57814 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -471,7 +471,7 @@ 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); + sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, bool inside_is_merged); virtual db::Coord dist () const; virtual OnEmptyIntruderHint on_empty_intruder_hint () const; @@ -486,6 +486,7 @@ public: private: db::Coord m_dx, m_dy; + db::Coord m_dist; int m_steps; unsigned int m_mode; db::MagnificationAndOrientationReducer m_vars_anisotropic; From a54365a9a7bbc1c742700ec5f6798f57f22f4afd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Jun 2024 19:30:01 +0200 Subject: [PATCH 05/16] WIP: DRC integration --- src/db/db/dbAsIfFlatRegion.cc | 9 +-- src/db/db/dbAsIfFlatRegion.h | 4 +- src/db/db/dbDeepRegion.cc | 32 ++------- src/db/db/dbDeepRegion.h | 4 +- src/db/db/dbEmptyRegion.h | 4 +- src/db/db/dbRegion.cc | 16 ++--- src/db/db/dbRegion.h | 24 +++---- src/db/db/dbRegionDelegate.h | 4 +- src/db/db/dbRegionLocalOperations.cc | 52 +-------------- src/db/db/gsiDeclDbRegion.cc | 39 +++++------ src/drc/drc/built-in-macros/_drc_engine.rb | 8 +++ src/drc/drc/built-in-macros/_drc_layer.rb | 75 +++++++++++++++++++--- src/drc/drc/built-in-macros/_drc_tags.rb | 16 +++++ 13 files changed, 140 insertions(+), 147 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 235f8e02c..d731f059d 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1378,13 +1378,13 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } RegionDelegate * -AsIfFlatRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const +AsIfFlatRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode) const { - return sized_inside (inside, d, d, steps, mode, stop_at); + return sized_inside (inside, d, d, steps, mode); } RegionDelegate * -AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const +AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) const { if (steps <= 0 || empty ()) { // Nothing to do - NOTE: don't return EmptyRegion because we want to @@ -1409,9 +1409,6 @@ AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy std::vector > others; others.push_back (inside.begin_merged ()); - if (stop_at) { - others.push_back (stop_at->begin ()); - } proc.run_flat (begin_merged (), others, std::vector (), &op, results); diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index dedbec91a..b517ddc90 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -121,8 +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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; - virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode) const; + virtual RegionDelegate *sized_inside (const Region &inside, 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 5dbca4150..52578aba8 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1782,13 +1782,13 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } RegionDelegate * -DeepRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode, const Region *stop_at) const +DeepRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode) const { - return sized_inside (inside, d, d, steps, mode, stop_at); + return sized_inside (inside, d, d, steps, mode); } RegionDelegate * -DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const +DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) const { if (steps <= 0 || empty ()) { // Nothing to do - NOTE: don't return EmptyRegion because we want to @@ -1802,23 +1802,7 @@ DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, in const db::DeepRegion *inside_deep = dynamic_cast (inside.delegate ()); if (! inside_deep) { - return db::AsIfFlatRegion::sized_inside (inside, dx, dy, steps, mode, stop_at); - } - - const db::DeepRegion *stop_at_deep = 0; - if (stop_at) { - - stop_at_deep = dynamic_cast (stop_at->delegate ()); - if (! stop_at_deep) { - return db::AsIfFlatRegion::sized_inside (inside, dx, dy, steps, mode, stop_at); - } - - if (&inside_deep->deep_layer ().layout () != &stop_at_deep->deep_layer ().layout () - || &inside_deep->deep_layer ().initial_cell () != &stop_at_deep->deep_layer ().initial_cell ()) { - throw tl::Exception (tl::to_string (tr ("'sized_inside' operation needs to use the same layout and top cell " - "for 'inside' and 'stop_at' arguments"))); - } - + return db::AsIfFlatRegion::sized_inside (inside, dx, dy, steps, mode); } const db::DeepLayer &polygons = merged_deep_layer (); @@ -1834,13 +1818,7 @@ DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, in std::unique_ptr res (new db::DeepRegion (polygons.derived ())); - std::vector other_layers; - other_layers.push_back (inside_polygons.layer ()); - if (stop_at_deep) { - other_layers.push_back (stop_at_deep->deep_layer ().layer ()); - } - - proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ()); + proc.run (&op, polygons.layer (), inside_polygons.layer (), res->deep_layer ().layer ()); return res.release (); } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index b4868a29f..1102c194a 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -136,8 +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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const; - virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode) const; + virtual RegionDelegate *sized_inside (const Region &inside, 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/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index d9e6a850c..2170b5546 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -97,8 +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 &, coord_type, int, unsigned int, const Region *) const { return new EmptyRegion (); } - virtual RegionDelegate *sized_inside (const Region &, coord_type, coord_type, int, unsigned int, const Region *) const { return new EmptyRegion (); } + virtual RegionDelegate *sized_inside (const Region &, coord_type, int, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *sized_inside (const Region &, 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/dbRegion.cc b/src/db/db/dbRegion.cc index f7bc80213..f68f5ca89 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -310,29 +310,29 @@ Region::sized (coord_type dx, coord_type dy, unsigned int mode) const } Region & -Region::size_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) +Region::size_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode) { - set_delegate (mp_delegate->sized_inside (inside, d, steps, mode, stop_at)); + set_delegate (mp_delegate->sized_inside (inside, d, steps, mode)); return *this; } Region & -Region::size_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) +Region::size_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) { - set_delegate (mp_delegate->sized_inside (inside, dx, dy, steps, mode, stop_at)); + set_delegate (mp_delegate->sized_inside (inside, dx, dy, steps, mode)); return *this; } Region -Region::sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode, const db::Region *stop_at) const +Region::sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode) const { - return Region (mp_delegate->sized_inside (inside, d, steps, mode, stop_at)); + return Region (mp_delegate->sized_inside (inside, d, steps, mode)); } Region -Region::sized_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const db::Region *stop_at) const +Region::sized_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) const { - return Region (mp_delegate->sized_inside (inside, dx, dy, steps, mode, stop_at)); + return Region (mp_delegate->sized_inside (inside, dx, dy, steps, mode)); } void diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index a1a485b43..8e7ad5678 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1031,40 +1031,32 @@ public: * @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 can be confined to be inside a certain region and a stop condition - * can be supplied. With a stop condition, sizing will stop when the sized region touches - * a shape on the "stop_at" region. + * 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 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. - * @param stop_at The stop condition or 0 for "not stopping" * @return A reference to self */ - Region &size_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); + Region &size_inside (const db::Region &inside, 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 can be confined to be inside a certain region and a stop condition - * can be supplied. With a stop condition, sizing will stop when the sized region touches - * a shape on the "stop_at" region. + * 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 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. - * @param stop_at The stop condition or 0 for "not stopping" * @return A reference to self */ - Region &size_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0); + Region &size_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2); /** * @brief Returns the sized region @@ -1074,7 +1066,7 @@ public: * * Merged semantics applies. */ - Region sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) const; + Region sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode = 2) const; /** * @brief Returns the sized region @@ -1084,7 +1076,7 @@ public: * * Merged semantics applies. */ - Region sized_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2, const db::Region *stop_at = 0) const; + Region sized_inside (const db::Region &inside, 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 5928b1f5a..137aa7ac4 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -226,8 +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, coord_type d, int steps, unsigned int mode, const Region *stop_at) const = 0; - virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode, const Region *stop_at) const = 0; + virtual RegionDelegate *sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode) const = 0; + virtual RegionDelegate *sized_inside (const Region &inside, 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 e0799dbe0..a7142ad9c 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -2004,16 +2004,10 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, std::vector subjects; std::set inside; - std::set stop_at; 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) { - const std::pair &is = interactions.intruder_shape (*j); - if (is.first == 0) { - inside.insert (&is.second); - } else if (is.first == 1) { - stop_at.insert (&is.second); - } + inside.insert (&interactions.intruder_shape (*j).second); } } @@ -2062,50 +2056,6 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, db::BooleanOp op (db::BooleanOp::And); ep_and.process (pg, op); - if (! subjects.empty () && ! stop_at.empty ()) { - - // compute interations with "stop_at" - - ep_interact.clear (); - - db::EdgeProcessor::property_type p = 0; - for (auto i = subjects.begin (); i != subjects.end (); ++i, ++p) { - ep_interact.insert (*i, p); - } - - db::EdgeProcessor::property_type nstart = p; - for (auto i = stop_at.begin (); i != stop_at.end (); ++i, ++p) { - ep_interact.insert (**i, p); - } - - db::InteractionDetector id (0 /*interacting*/, nstart - 1); - id.set_include_touching (true); - db::EdgeSink es; - ep_interact.process (es, id); - id.finish (); - - std::set interacting; - for (db::InteractionDetector::iterator i = id.begin (); i != id.end (); ++i) { - interacting.insert (i->first); - } - - // drop interacting subjects - - if (! interacting.empty ()) { - std::vector::iterator iw = subjects.begin (); - for (auto i = subjects.begin (); i != subjects.end (); ++i) { - if (interacting.find (i - subjects.begin ()) == interacting.end ()) { - if (iw != i) { - iw->swap (*i); - } - ++iw; - } - } - subjects.erase (iw, subjects.end ()); - } - - } - } db::polygon_ref_generator gen (layout, result); diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 25c4ef3f4..dab1e9ed8 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1009,15 +1009,15 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode) } static db::Region -sized_inside_dvm (const db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) +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, dv.x (), dv.y (), steps, mode, stop_at); + return region->sized_inside (inside, dv.x (), dv.y (), steps, mode); } static db::Region & -size_inside_dvm (db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode, const db::Region *stop_at) +size_inside_dvm (db::Region *region, const db::Region &inside, const db::Vector &dv, int steps, unsigned int mode) { - region->sized_inside (inside, dv.x (), dv.y (), steps, mode, stop_at); + region->sized_inside (inside, dv.x (), dv.y (), steps, mode); return *region; } @@ -1942,7 +1942,7 @@ 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 ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int)) &db::Region::size_inside, 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" @@ -1950,51 +1950,46 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param dy The y sizing value\n" "@param steps The number of steps to take\n" "@param mode The sizing mode (see \\size)\n" - "@param stop_at The optional stop layer\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' regiuon. " + "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 operaton 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 incremental size of the device active region with the well as the 'inside' region is applied. The step is chosen as " - "somewhat less than the minimum well space, so sizing the active region results in a growing footprint that " - "follows the well contours.\n" - "\n" - "A stop region can be specified, meaning that the sizing propagation will stop if the sized shape " - "touches a shape from the stop region. If that happens, the sized shape is discarded. " - "In case of the latch-up rule application, the stop region will be the well tap layer.\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_inside", &size_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + 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, stop_at)\".\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 ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, int, unsigned int, const db::Region *)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, int, unsigned int)) &db::Region::size_inside, 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, stop_at)\".\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 ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int) const) &db::Region::sized_inside, 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" @@ -2003,7 +1998,7 @@ 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 ("sized_inside", &sized_inside_dvm, gsi::arg ("inside"), gsi::arg ("dv"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + 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" @@ -2014,7 +2009,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This variant has been introduced in version 0.28." ) + - method ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, int, unsigned int, const db::Region *) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), gsi::arg ("stop_at", (db::Region *) 0, "nil"), + method ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, int, unsigned int) const) &db::Region::sized_inside, 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" diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 073956432..0ec05992a 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -313,6 +313,14 @@ module DRC DRCEdgeMode::new(RBA::EdgeMode::NotStep) end + def steps(arg) + DRCSizingSteps::new(arg) + end + + def inside(arg) + DRCSizingInside::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..faa10e974 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -4678,8 +4678,8 @@ 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] [, inside(l) [, steps(n)]]) + # @synopsis layer.sized(dx, dy [, mode] [, inside(l) [, steps(n)]])) # # 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. @@ -4711,6 +4711,26 @@ 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. # + # The "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 "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 "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. + # + # "inside" and "steps" can be used with positive sizing values only. + # + # An example for the "inside" option is this: + # + # @code + # ntap.sized(30.um, inside(nwell), steps(100)) + # @/code + # # \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: @@ -4733,10 +4753,11 @@ TP_SCRIPT # %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 [, mode] [, inside(l) [, steps(n)]]) + # @synopsis layer.size(dx, dy [, mode] [, inside(l) [, steps(n)]])) # - # 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,6 +4770,8 @@ TP_SCRIPT dist = 0 + steps = nil + inside = nil mode = 2 values = [] args.each do |a| @@ -4758,10 +4781,40 @@ 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 end end aa = [] + + f_size = :size + f_sized = :sized + + if steps + if !inside + 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 + + 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) + + steps ||= 1 + + f_size = :size_inside + f_sized = :sized_inside + + end + if values.size < 1 raise "Method requires one or two sizing values" elsif values.size > 2 @@ -4771,17 +4824,21 @@ TP_SCRIPT aa.push(values[-1]) end + if inside + 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..e2a32f3f1 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -57,6 +57,22 @@ 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 edge mode value for Region#edges class DRCEdgeMode attr_accessor :value From 8d630e723afa562840838a24b46945afe14384f2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Jun 2024 23:52:47 +0200 Subject: [PATCH 06/16] WIP --- src/db/db/dbAsIfFlatRegion.cc | 16 ++- src/db/db/dbAsIfFlatRegion.h | 4 +- src/db/db/dbDeepRegion.cc | 67 ++++++++--- src/db/db/dbDeepRegion.h | 4 +- src/db/db/dbEmptyRegion.h | 4 +- src/db/db/dbRegion.cc | 16 +-- src/db/db/dbRegion.h | 10 +- src/db/db/dbRegionDelegate.h | 4 +- src/db/db/dbRegionLocalOperations.cc | 39 +++++-- src/db/db/dbRegionLocalOperations.h | 5 +- src/db/db/gsiDeclDbRegion.cc | 125 ++++++++++++++++++++- src/drc/drc/built-in-macros/_drc_engine.rb | 4 + src/drc/drc/built-in-macros/_drc_layer.rb | 65 ++++++++--- src/drc/drc/built-in-macros/_drc_tags.rb | 8 ++ 14 files changed, 301 insertions(+), 70 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index d731f059d..14c4c0f71 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1378,14 +1378,16 @@ AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } RegionDelegate * -AsIfFlatRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode) const +AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const { - return sized_inside (inside, d, d, steps, mode); + return sized_inside (inside, outside, d, d, steps, mode); } RegionDelegate * -AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) const +AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const { + // @@@ TODO: restrict max. number of steps in "outside" mode like for DeepRegion + if (steps <= 0 || empty ()) { // Nothing to do - NOTE: don't return EmptyRegion because we want to // maintain "deepness" @@ -1400,7 +1402,10 @@ AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy std::vector results; results.push_back (&output->raw_polygons ()); - db::sized_inside_local_operation op (dx, dy, steps, mode, true /*inside layer is merged*/); + // NOTE: as we merge the inside region in the inside case, we can use distance 0 + db::Coord dist = outside ? std::max (dx, dy) : 0; + bool inside_is_merged = outside ? inside.is_merged () : true; + db::sized_inside_local_operation op (dx, dy, steps, mode, dist, outside, inside_is_merged, true /*split after*/); db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); @@ -1408,7 +1413,8 @@ AsIfFlatRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy proc.set_report_progress (report_progress ()); std::vector > others; - others.push_back (inside.begin_merged ()); + // NOTE: it does not provide benefits to merge the outside region, so just don't + others.push_back (outside ? inside.begin () : inside.begin_merged ()); proc.run_flat (begin_merged (), others, std::vector (), &op, results); diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index b517ddc90..f06c4c362 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -121,8 +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, coord_type d, int steps, unsigned int mode) const; - virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, 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 52578aba8..3435f6c2d 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1782,14 +1782,17 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const } RegionDelegate * -DeepRegion::sized_inside (const Region &inside, coord_type d, int steps, unsigned int mode) const +DeepRegion::sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const { - return sized_inside (inside, d, d, steps, mode); + return sized_inside (inside, outside, d, d, steps, mode); } RegionDelegate * -DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) const +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" @@ -1805,20 +1808,58 @@ DeepRegion::sized_inside (const Region &inside, coord_type dx, coord_type dy, in return db::AsIfFlatRegion::sized_inside (inside, 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 (); - const db::DeepLayer &inside_polygons = inside_deep->merged_deep_layer (); - - db::sized_inside_local_operation op (dx, dy, steps, mode, true /*inside layer 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 ()); std::unique_ptr res (new db::DeepRegion (polygons.derived ())); + std::unique_ptr prev; - proc.run (&op, polygons.layer (), inside_polygons.layer (), res->deep_layer ().layer ()); + int steps_from = 0; + + while (steps > 0) { + + db::Coord dx_chunk = dx, dy_chunk = dy; + int steps_chunk = steps; + + // In outside mode, we perform at most max_steps in one chunk. + // This is supposed to limit the search range. + if (outside && 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; + bool split_after = (steps == 0); + db::sized_inside_local_operation op (dx_chunk, dy_chunk, steps_chunk, mode, dist, outside, inside_polygons_is_merged, split_after); + + 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)); + + proc.run (&op, prev.get () ? prev->deep_layer ().layer () : polygons.layer (), inside_polygons.layer (), res->deep_layer ().layer ()); + + // @@@ TODO: should we also merge in the last step and consider splitting? + if (steps > 0) { + prev.reset (dynamic_cast (res->merged ())); + tl_assert (prev.get () != 0); + res.reset (new db::DeepRegion (polygons.derived ())); + } + + } return res.release (); } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 1102c194a..2c56edb12 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -136,8 +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, coord_type d, int steps, unsigned int mode) const; - virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, 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/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 2170b5546..4529c159c 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -97,8 +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 &, coord_type, int, unsigned int) const { return new EmptyRegion (); } - virtual RegionDelegate *sized_inside (const Region &, coord_type, coord_type, int, 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/dbRegion.cc b/src/db/db/dbRegion.cc index f68f5ca89..a8294dffa 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -310,29 +310,29 @@ Region::sized (coord_type dx, coord_type dy, unsigned int mode) const } Region & -Region::size_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode) +Region::size_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode) { - set_delegate (mp_delegate->sized_inside (inside, d, steps, mode)); + set_delegate (mp_delegate->sized_inside (inside, outside, d, steps, mode)); return *this; } Region & -Region::size_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) +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, dx, dy, steps, mode)); + set_delegate (mp_delegate->sized_inside (inside, outside, dx, dy, steps, mode)); return *this; } Region -Region::sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode) const +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, d, steps, mode)); + return Region (mp_delegate->sized_inside (inside, outside, d, steps, mode)); } Region -Region::sized_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode) const +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, dx, dy, steps, mode)); + return Region (mp_delegate->sized_inside (inside, outside, dx, dy, steps, mode)); } void diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 8e7ad5678..3ed7d96ae 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1035,12 +1035,13 @@ public: * 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, coord_type d, int steps, unsigned int mode = 2); + 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 @@ -1050,13 +1051,14 @@ public: * 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, coord_type dx, coord_type dy, int steps, unsigned int mode = 2); + 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 @@ -1066,7 +1068,7 @@ public: * * Merged semantics applies. */ - Region sized_inside (const db::Region &inside, coord_type d, int steps, unsigned int mode = 2) const; + Region sized_inside (const db::Region &inside, bool outside, coord_type d, int steps, unsigned int mode = 2) const; /** * @brief Returns the sized region @@ -1076,7 +1078,7 @@ public: * * Merged semantics applies. */ - Region sized_inside (const db::Region &inside, coord_type dx, coord_type dy, int steps, unsigned int mode = 2) const; + 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 137aa7ac4..7bbc64c9d 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -226,8 +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, coord_type d, int steps, unsigned int mode) const = 0; - virtual RegionDelegate *sized_inside (const Region &inside, coord_type dx, coord_type dy, int steps, 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 a7142ad9c..c475089ea 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1948,13 +1948,10 @@ template class DB_PUBLIC two_bool_and_not_local_operation_with_properties -sized_inside_local_operation::sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, bool inside_is_merged) - : m_dx (dx), m_dy (dy), m_steps (steps), m_mode (mode), m_dist (0) +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, bool split_after) + : 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), m_split_after (split_after) { - m_dist = 0; - if (! inside_is_merged) { - m_dist = std::max (0, std::max (m_dx, m_dy)); - } + // .. nothing yet .. } template @@ -2011,6 +2008,24 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, } } + // 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 ()); @@ -2021,9 +2036,6 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, db::EdgeProcessor ep_and; ep_and.set_base_verbosity (50); - db::EdgeProcessor ep_interact; - ep_interact.set_base_verbosity (50); - // the main sizing loop db::Coord sx_last = 0, sy_last = 0; @@ -2050,10 +2062,15 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, 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 (db::BooleanOp::And); + db::PolygonSplitter splitter (pc, proc->area_ratio (), proc->max_vertex_count ()); + // NOTE: we split in the last step if requested + db::PolygonGenerator pg ((! m_split_after || step + 1 < m_steps) ? (PolygonSink &) pc : (PolygonSink &) splitter, false /*don't resolve holes*/, false /*min. coherence*/); + db::BooleanOp op (m_outside ? db::BooleanOp::ANotB : db::BooleanOp::And); ep_and.process (pg, op); } diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index 46ff57814..c89bd8474 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -471,7 +471,7 @@ 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, bool inside_is_merged); + sized_inside_local_operation (db::Coord dx, db::Coord dy, int steps, unsigned int mode, db::Coord dist, bool outside, bool inside_is_merged, bool split_after); virtual db::Coord dist () const; virtual OnEmptyIntruderHint on_empty_intruder_hint () const; @@ -489,6 +489,9 @@ private: db::Coord m_dist; int m_steps; unsigned int m_mode; + bool m_outside; + bool m_inside_is_merged; + bool m_split_after; db::MagnificationAndOrientationReducer m_vars_anisotropic; db::MagnificationReducer m_vars_isotropic; }; diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index dab1e9ed8..8e98a9656 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1008,16 +1008,81 @@ 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, dv.x (), dv.y (), steps, 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->sized_inside (inside, dv.x (), dv.y (), steps, 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; } @@ -1942,7 +2007,7 @@ 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 ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), + 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" @@ -1967,6 +2032,14 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\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" @@ -1978,7 +2051,15 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.29.3." ) + - method ("size_inside", (db::Region & (db::Region::*) (const db::Region &, db::Coord, int, unsigned int)) &db::Region::size_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + 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" @@ -1989,7 +2070,15 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.29.3." ) + - method ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, db::Coord, int, unsigned int) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("steps"), gsi::arg ("mode"), + 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" @@ -1998,6 +2087,14 @@ 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 ("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" @@ -2009,7 +2106,15 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This variant has been introduced in version 0.28." ) + - method ("sized_inside", (db::Region (db::Region::*) (const db::Region &, db::Coord, int, unsigned int) const) &db::Region::sized_inside, gsi::arg ("inside"), gsi::arg ("d"), gsi::arg ("steps"), gsi::arg ("mode", (unsigned int) 2), + 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" @@ -2018,6 +2123,14 @@ 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 ("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/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 0ec05992a..90a63eb43 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -321,6 +321,10 @@ module DRC DRCSizingInside::new(arg) end + def 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 faa10e974..71cb01ccb 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 @@ -4679,7 +4679,11 @@ TP_SCRIPT # @name sized # @brief Polygon sizing (per-edge biasing) # @synopsis layer.sized(d [, mode] [, inside(l) [, steps(n)]]) - # @synopsis layer.sized(dx, dy [, mode] [, inside(l) [, steps(n)]])) + # @synopsis layer.sized(d, inside(l) [, steps(n)] [, mode]) + # @synopsis layer.sized(d, outside(l) [, steps(n)] [, mode]) + # @synopsis layer.sized(dx, dy [, mode]) + # @synopsis layer.sized(dx, dy, inside(l) [, steps(n)] [, mode]) + # @synopsis layer.sized(dx, dy, 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. @@ -4725,6 +4729,10 @@ TP_SCRIPT # # "inside" and "steps" can be used with positive sizing values only. # + # "outside" acts like "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 "inside" option is this: # # @code @@ -4753,8 +4761,12 @@ TP_SCRIPT # %DRC% # @name size # @brief Polygon sizing (per-edge biasing, modifies the layer) - # @synopsis layer.size(d [, mode] [, inside(l) [, steps(n)]]) - # @synopsis layer.size(dx, dy [, mode] [, inside(l) [, steps(n)]])) + # @synopsis layer.size(d [, mode]) + # @synopsis layer.size(d, inside(l) [, steps(n)] [, mode]) + # @synopsis layer.size(d, outside(l) [, steps(n)] [, mode]) + # @synopsis layer.size(dx, dy [, mode]) + # @synopsis layer.size(dx, dy, inside(l) [, steps(n)] [, mode]) + # @synopsis layer.size(dx, dy, outside(l) [, steps(n)] [, mode]) # # See \sized for a description of the options. # The size method basically does the same but modifies the layer @@ -4772,8 +4784,11 @@ TP_SCRIPT 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) @@ -4785,7 +4800,12 @@ TP_SCRIPT 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 = [] @@ -4793,8 +4813,12 @@ TP_SCRIPT f_size = :size f_sized = :sized + if inside && outside + raise "Cannot use 'inside' and 'outside' together" + end + if steps - if !inside + if !inside && !outside raise "'steps' is only allowed with 'inside'" end if !steps.is_a?(1.class) @@ -4802,17 +4826,30 @@ TP_SCRIPT end end - if inside + if inside || outside - 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) + 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 - f_size = :size_inside - f_sized = :sized_inside - end if values.size < 1 @@ -4824,7 +4861,7 @@ TP_SCRIPT aa.push(values[-1]) end - if inside + if inside || outside aa.push(steps) end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index e2a32f3f1..56cf18023 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -73,6 +73,14 @@ module DRC 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 From c3d3dd6239ebc84ae3b8e8cda4df1a0e50ec554c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 00:30:20 +0200 Subject: [PATCH 07/16] WIP: Optimizing performance of deep mode sized inside/outside --- src/db/db/dbDeepRegion.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 3435f6c2d..95c4d0761 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1824,9 +1824,10 @@ DeepRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coo db::Coord dx_chunk = dx, dy_chunk = dy; int steps_chunk = steps; - // In outside mode, we perform at most max_steps in one chunk. - // This is supposed to limit the search range. - if (outside && steps > max_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)); @@ -1852,11 +1853,15 @@ DeepRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coo proc.run (&op, prev.get () ? prev->deep_layer ().layer () : polygons.layer (), inside_polygons.layer (), res->deep_layer ().layer ()); - // @@@ TODO: should we also merge in the last step and consider splitting? + // 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); } } From 128efd45e41a33929ad063d2f9f0452656beee71 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 18:21:21 +0200 Subject: [PATCH 08/16] WIP: Implementing flat mode for sized inside/outside --- src/db/db/dbAsIfFlatRegion.cc | 80 +++++++++++++++++++++------- src/db/db/dbDeepRegion.cc | 3 +- src/db/db/dbRegionLocalOperations.cc | 8 ++- src/db/db/dbRegionLocalOperations.h | 3 +- 4 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 14c4c0f71..63686cb65 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1386,7 +1386,8 @@ AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type d, RegionDelegate * AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const { - // @@@ TODO: restrict max. number of steps in "outside" mode like for DeepRegion + // empirical value + const int max_steps = 25; if (steps <= 0 || empty ()) { // Nothing to do - NOTE: don't return EmptyRegion because we want to @@ -1398,27 +1399,68 @@ AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, throw tl::Exception (tl::to_string (tr ("'sized_inside' operation does not make sense with negative sizing"))); } - std::unique_ptr output (new FlatRegion ()); - std::vector results; - results.push_back (&output->raw_polygons ()); - - // NOTE: as we merge the inside region in the inside case, we can use distance 0 - db::Coord dist = outside ? std::max (dx, dy) : 0; - bool inside_is_merged = outside ? inside.is_merged () : true; - db::sized_inside_local_operation op (dx, dy, steps, mode, dist, outside, inside_is_merged, true /*split after*/); - - db::local_processor proc; - proc.set_base_verbosity (base_verbosity ()); - proc.set_description (progress_desc ()); - proc.set_report_progress (report_progress ()); - - std::vector > others; // NOTE: it does not provide benefits to merge the outside region, so just don't - others.push_back (outside ? inside.begin () : inside.begin_merged ()); + auto inside_polygons = outside ? inside.begin () : inside.begin_merged (); + bool inside_polygons_is_merged = outside ? inside.is_merged () : true; - proc.run_flat (begin_merged (), others, std::vector (), &op, results); + auto polygons = begin_merged (); - return output.release (); + 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)); + + std::vector > others; + others.push_back (inside_polygons); + + std::vector results; + results.push_back (&res->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 (dynamic_cast (res->merged ())); + tl_assert (prev.get () != 0); + res.reset (new db::FlatRegion ()); + } else { + res.reset (dynamic_cast (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ())))); + tl_assert (res.get () != 0); + } + + } + + return res.release (); } RegionDelegate * diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 95c4d0761..15e2074ce 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1839,8 +1839,7 @@ DeepRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coo // 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; - bool split_after = (steps == 0); - db::sized_inside_local_operation op (dx_chunk, dy_chunk, steps_chunk, mode, dist, outside, inside_polygons_is_merged, split_after); + 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); diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index c475089ea..b69bd33ff 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1948,8 +1948,8 @@ template class DB_PUBLIC two_bool_and_not_local_operation_with_properties -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, bool split_after) - : 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), m_split_after (split_after) +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 .. } @@ -2067,9 +2067,7 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, } db::PolygonContainer pc (subjects, true /*clear*/); - db::PolygonSplitter splitter (pc, proc->area_ratio (), proc->max_vertex_count ()); - // NOTE: we split in the last step if requested - db::PolygonGenerator pg ((! m_split_after || step + 1 < m_steps) ? (PolygonSink &) pc : (PolygonSink &) splitter, false /*don't resolve holes*/, false /*min. coherence*/); + 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); diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index c89bd8474..ba8129696 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -471,7 +471,7 @@ 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, bool split_after); + 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; @@ -491,7 +491,6 @@ private: unsigned int m_mode; bool m_outside; bool m_inside_is_merged; - bool m_split_after; db::MagnificationAndOrientationReducer m_vars_anisotropic; db::MagnificationReducer m_vars_isotropic; }; From e8784d17632b898ba8747e600bb818f5000d86e9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 19:26:35 +0200 Subject: [PATCH 09/16] WIP: added basic tests --- src/db/db/dbAsIfFlatRegion.cc | 18 +- src/db/db/dbDeepRegion.cc | 6 +- src/db/db/dbRegionLocalOperations.cc | 2 +- src/db/unit_tests/dbRegionTests.cc | 275 +++++++++++++++++++++++++++ 4 files changed, 292 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 63686cb65..d97e764ff 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1399,14 +1399,18 @@ AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, 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; + std::unique_ptr res (new FlatRegion ()); + std::unique_ptr prev; int steps_from = 0; @@ -1444,18 +1448,18 @@ AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, others.push_back (inside_polygons); std::vector results; - results.push_back (&res->raw_polygons ()); + 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 (dynamic_cast (res->merged ())); - tl_assert (prev.get () != 0); + prev.reset (res->merged ()); res.reset (new db::FlatRegion ()); } else { - res.reset (dynamic_cast (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ())))); - tl_assert (res.get () != 0); + res.reset (res->processed (db::PolygonBreaker (proc.max_vertex_count (), proc.area_ratio ()))); } } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 15e2074ce..076e9f46b 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1803,9 +1803,13 @@ DeepRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coo 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, dx, dy, steps, mode); + 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 diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index b69bd33ff..d0bf5b91c 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1965,7 +1965,7 @@ template OnEmptyIntruderHint sized_inside_local_operation::on_empty_intruder_hint () const { - return Drop; + return m_outside ? Ignore : Drop; } template diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index eaaee9e8c..2056f924f 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2566,6 +2566,281 @@ 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)"); + + 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; From 4c28d9f6a9cb03c8e929f61ad3d599a8b7cfbf0e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 19:35:47 +0200 Subject: [PATCH 10/16] WIP: optimization --- src/db/db/dbRegionLocalOperations.cc | 63 ++++++++++++++++++---------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index d0bf5b91c..291f78ecb 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -2008,6 +2008,11 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, } } + // 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) { @@ -2040,36 +2045,52 @@ sized_inside_local_operation::do_compute_local (db::Layout *layout, db::Coord sx_last = 0, sy_last = 0; - for (int step = 0; step < m_steps && ! subjects.empty (); ++step) { + for (int step = 0; step < steps && ! subjects.empty (); ++step) { - db::Coord sx = db::coord_traits::rounded (dx_with_mag * (step + 1) / double (m_steps)); - db::Coord sy = db::coord_traits::rounded (dy_with_mag * (step + 1) / double (m_steps)); + 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; - ep_and.clear (); + if (! inside.empty () || ! inside_merged.empty ()) { - 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); - } + ep_and.clear (); - 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::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::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); + 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); + + } } From 58d53636cbd0423cce41fae8962a324bb9895e96 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 21:01:23 +0200 Subject: [PATCH 11/16] DRC tests for 'size/sized inside --- src/db/unit_tests/dbRegionTests.cc | 11 +++++ src/drc/unit_tests/drcSimpleTests.cc | 11 +++++ testdata/drc/drcSimpleTests_130.drc | 54 +++++++++++++++++++++++++ testdata/drc/drcSimpleTests_130.gds | Bin 0 -> 1442 bytes testdata/drc/drcSimpleTests_au130.gds | Bin 0 -> 4170 bytes testdata/drc/drcSimpleTests_au130d.gds | Bin 0 -> 4408 bytes 6 files changed, 76 insertions(+) create mode 100644 testdata/drc/drcSimpleTests_130.drc create mode 100644 testdata/drc/drcSimpleTests_130.gds create mode 100644 testdata/drc/drcSimpleTests_au130.gds create mode 100644 testdata/drc/drcSimpleTests_au130d.gds diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 2056f924f..126dda6cc 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2591,6 +2591,17 @@ TEST(60_sized_inside) 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)"); diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 6cc8d2fb5..7f4dff7b7 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1911,3 +1911,14 @@ TEST(121_ShapesOfTerminal) db::compare_layouts (_this, layout, au, db::NoNormalization); } + +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..3d538f929 --- /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), inside(l2)).output(100, 0) +l1.sized(20.0, steps(0), inside(l2)).output(101, 0) + +l1.sized(20.0, steps(50), inside(l2)).output(110, 0) +l1.sized(50.0, steps(50), outside(l2)).output(111, 0) + +l1d = l1.dup +l1d.size(20.0, steps(50), inside(l2)) +l1d.output(120, 0) + +l1d = l1.dup +l1d.size(50.0, steps(50), outside(l2)) +l1d.output(121, 0) + +l1.sized(10.0, 20.0, steps(50), inside(l2)).output(130, 0) +l1.sized(25.0, 50.0, steps(50), outside(l2)).output(131, 0) + +error = nil +begin + l2.sized(-1.0, steps(50), outside(l2)) +rescue + error = true +end +if !error + raise "error expected!" +end + +error = nil +begin + l2.sized(-1.0, 2.0, steps(50), 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 0000000000000000000000000000000000000000..3871953a1cdc7051417b66cb9d7d386ee30a1cf9 GIT binary patch literal 1442 zcmai!O=uHA6vzLwNv2C0HPwJ0tk9!}paBCWpvBhU2L`GohvLVrc=F&uq)>!F4pdbj|ig*z5BnXNDgHDHfz2Q!sc4z=!IXduWW>$KLaO8bD`=0QPk z_W%_0DgKUpvOU1R2M8mt(j%-N!dAPCZ@LT7ml9C*9gklp>UIF3#H&8+E}=^=ba^zN zBKq7UT3I#(PVFK3wkjn0-2%pfKHWOA-sRiEwXbz!@*Mjf=I-p&T!G%Fxbj?(nOF=v z;O{bBQwU7Qh8w`{Ya3|2Z_IHbzwDAkau7ZK2wZtSNay!|j+o-<0?`BRV4MRAcHZP8 zjJiF^r5U#av~IJuVxEkg>G6|4z*IYS2C+%W9^lCcp8{tc;q^j^Goxo1)324?`5Sg~ zz;`}Vp_z~HeN-Qr0bukhQG42upEl9kN}q=pfD+G?qpY*sEFVuk(fkb2SAOPafMX4! z&O@QZ4P*6)AA57=xCV1u4C}vu7p*=e&P>=JSZWcyW8X`xyU8=stqUb?nW$ec5xt)U z*12~V-#~d~p~Nkd^lAs+qaQw)Ttf$He@`fJ%S3$iC!-PhNh0G(|t;uY4!Xcl1}3v DvMWpH literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au130.gds b/testdata/drc/drcSimpleTests_au130.gds new file mode 100644 index 0000000000000000000000000000000000000000..4255d481c983584603691c0b97b184e363479f11 GIT binary patch literal 4170 zcmd6qPiRzE6vn?d#z~M^Bh5lfge(-Y7%*bMv{*q2V(CCNWFf8YqI40OO^^{S3{BAr zqbMk})I|hA7Y&pml0^_fCwzc5K*p7+`5Scn-?M=6-q*UR^?{5lQ}RV79stED&*LSLM|pr`yf~Gff9|@--3Afk z#8Hv+-GPkXZAi=a`{q8Y+5e!{yK_{;xL@~h^>iTPoui5MGjsjhYW-{O5gFZU)JNy} zqdjo5&eb{wcygR$yq-%qpU9~ppk=_bv{mGJ86c00iCicEB*(Q3q@7pfjP~l7_S^Wd z+42K_x-2sNiwMba$CCX|o>yz;>No1^^u5;6>Sb)!*d|v`BgduIPv0kze!V~c=+oZ~ zeA*>4c9CSf@vuZY%_NyZ&h^K(!=XhW3Kz3;z{sJ%!s z?wFe2p?!Kbi>O2Urst3LG?9!urscc39opyXcf92DNye3_`FGBWj9dYXc8<nogfAdwN}@4yvY}*KY;&C(``O z^wQIhGkv!_qv`tKOd#hyS+2N#{{Q|6eE8Vwx-s{IYV%NFMZNd_8h>AV=j!FoXZziJ zI_SLlndv@#&HT>lMc*?&w0au*Zi*Df=NZ+MfWr?!&d;lURB3&8zRA4P7pL>8OHOym zb$PGfr0O|!UCBJhO7oYjf4_Bz>>T$rKQ(_Ptsm?Q`!sgZ_sjQDYu)4P&F>WYD1Fs@ z&gxFz8`R&;=LYM;x;w`c=P$hF`^x!TKi|Q9<@&CltP|_@7VhiecYI&D{!-mn&i}sN z*YD=)zi%OWc%#qj;f=nadvX1{H01Tqn4*7Nui}0BDCgfdqv_w4Lf``RkMrm3Jj3?x zs=gWN6C~sM9T?Y_{&+nWtDk@U>en%jOap7$1F6IR(MzuP+WVJ#?Ddjdpk8wRqgIbw m1+QC1>Lh7;dAaEOXSBX9Rxb|@`~Dpq4(m`KYxRHh8oUIB(%1z6 literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au130d.gds b/testdata/drc/drcSimpleTests_au130d.gds new file mode 100644 index 0000000000000000000000000000000000000000..eee1ba69f710ac9cd0885ad23eb99e1334885e97 GIT binary patch literal 4408 zcmd6qe`wTo7{}k=+wJR@x$4Y8CMP7!FgR#)f?KFK9jox59EXVMe@j9}>yJ^!42~vg zFu@=}$v+kaO0+)`k^M1ZAP8E~z(ZGv9gNXZtII_EWAOAmpU*w_{oLKY+?f|#FveJjvZSHHRQ(+%4%o-TBBjlDeb z#qJD}_0f91?{@<60PvrR!Gr&F3_vPTNAz0@&=FW}86cX;0ZNs31c)yS_1}I-{g|u& zYnEs_Pb9oI0&Gr(RQYti%zu^sy%Es|UR^h zoClQOjRQY$lq^HLY~S_&;S=^hRP?{+!Ossgk8xG^S$@zA?B4=dR-d;uUB>ZJI?vomP^SOKdV5z0XBvS&c@in*E-5~D^_}}7^ew*BuQI}a znf@aD>m*IfB$-JQU0p8(-roc7nH3>drQyZ+newe4fjfLAOVu-U&!_f>rT)%#5p_>l zelkmRmnXOKNzuG@2`M<*XmHZ1e?@G!08SWS5V)DO_Z*R%J zOi}eyk#Fb!5wGQc15!s?S1mcK-;z_gp$J|Ci5+E}a8}Ja@gG*FvhC>FW2o?+4R) zAj@-9czBrTRyw50v$N6q$0vxcCy0d82Z>H`Eh<%hJrSi3tjY3z=PYl$!!J$EL`s!2 z-Tl8?-2XC+c$A6m!SZDwM literal 0 HcmV?d00001 From 10880e078b3826488b0061adc8f93be6ada66e02 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 22:20:48 +0200 Subject: [PATCH 12/16] Added documentation for new DRC feature --- scripts/drc_lvs_doc/create_drc_samples.rb | 30 ++++++++++ src/doc/doc/about/drc_ref_layer.xml | 54 +++++++++++++++-- src/doc/doc/images/drc_sized_inside1.png | Bin 0 -> 6696 bytes src/doc/doc/images/drc_sized_inside2.png | Bin 0 -> 6900 bytes src/doc/doc/images/drc_sized_inside3.png | Bin 0 -> 7080 bytes src/doc/doc/images/drc_sized_inside4.png | Bin 0 -> 6697 bytes src/doc/docDRCLVSResources.qrc | 4 ++ src/drc/drc/built-in-macros/_drc_layer.rb | 67 +++++++++++++--------- 8 files changed, 125 insertions(+), 30 deletions(-) create mode 100644 src/doc/doc/images/drc_sized_inside1.png create mode 100644 src/doc/doc/images/drc_sized_inside2.png create mode 100644 src/doc/doc/images/drc_sized_inside3.png create mode 100644 src/doc/doc/images/drc_sized_inside4.png diff --git a/scripts/drc_lvs_doc/create_drc_samples.rb b/scripts/drc_lvs_doc/create_drc_samples.rb index 2799b88e1..5a9c1c332 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), inside(input2))", "drc_sized_inside1.png" +run_demo gen, "input1.sized(2.um, steps(2), inside(input2))", "drc_sized_inside2.png" +run_demo gen, "input1.sized(3.um, steps(3), inside(input2))", "drc_sized_inside3.png" +run_demo gen, "input1.sized(10.um, steps(10), inside(input2))", "drc_sized_inside4.png" + class Gen def produce(s1, s2) pts = [ diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 9dd40b6ca..7b03a95e1 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -3056,18 +3056,27 @@ The following images shows the effect of some rectangle filter modes:

Usage:

  • layer.size(d [, mode])
  • -
  • layer.size(dx, dy [, mode]))
  • +
  • layer.size(d, inside(l) [, steps(n)] [, mode])
  • +
  • layer.size(d, outside(l) [, steps(n)] [, mode])
  • +
  • layer.size(dx, dy [, mode])
  • +
  • layer.size(dx, dy, inside(l) [, steps(n)] [, mode])
  • +
  • layer.size(dx, dy, 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] [, inside(l) [, steps(n)]])
  • +
  • layer.sized(d, inside(l) [, steps(n)] [, mode])
  • +
  • layer.sized(d, outside(l) [, steps(n)] [, mode])
  • +
  • layer.sized(dx, dy [, mode])
  • +
  • layer.sized(dx, dy, inside(l) [, steps(n)] [, mode])
  • +
  • layer.sized(dx, dy, outside(l) [, steps(n)] [, mode])

This method requires a polygon layer. It will apply a bias per edge of the polygons @@ -3100,6 +3109,30 @@ 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.

+The "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 "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 "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. +

+"inside" and "steps" can be used with positive sizing values only. +

+"outside" acts like "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 "inside" option is this: +

+

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

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 +3151,19 @@ The following images show the effect of various forms of the "sized" method: +

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

+ + + + + + + + + +

"smoothed" - Smoothes the polygons of the region

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 0000000000000000000000000000000000000000..e013d4407f38172b5b6dc2d5fc59227a059a9d66 GIT binary patch literal 6696 zcmdT}dstFwx5shHtO+Okx~C3_I*mD~bn(t4O-h-X(_~hPnMtLofntV;QMcialm+nZ3-}jK%QsSu-RT-2O4fFEj)xz5KRS* zRUWp(tuOfU=Bue9H$Jro?sh9%noGyHj5aQ*XnirS$K!D}Y?csVds3K&*zdydjJUI^ zG9vl8mTqweofF)1RfRU8MU`OZo~4EF99tzCOSE25v@(iooDD4D{aEElRH;x$3_OU{_JYy8=;u3qc)(D6*+~8*L=G*nN`DXW{8&* zA~_m6{6L9!69Nhqk+MIo^X$yG|e+&)%*-MK(+CC63}+BE|nl~iQ)L5*u-h*ZQ3 zW?p&*=doi{rO$Lz=dqzzPnF_qlI~!kuTXi0RnG+`L#Y-LIa2+c;r-85Z!ag*98t=C zN807+&?3p+?uHAm7O^tj&Csg7r#&jaGKm@B$*cB+vX{5ve5siwXx$i!M#$4@ zHKd|sia2p|%~zQj93+WnMx})(BHPHN<)|blx&OoLUA^~kIjn`6*LYIzp3yd`;GCKs zm;mdgbdgp35`F+Gh(he%5RyS3Rc5yx6e~pwusc;RQ_64I$+_X9Yzl?TJn6z8YEBE5 zRXw@Nx7R6A`+IvbDX-2~EwmKV=TRcxbZ1`BUF?c^ny|EFYj5%eP>nGQFQ{Ze z2DR$8eL3B7)pi%V7F#i={{iQNPJ+&s6HBiBxHd%b5q$H*D!zSaXjH zg+PWbXIJY!MXuV&^9+VCje(Z)x63m&1z@axY+Y~SGeyr1I7e{*Zy*-1cBrv4Ne9E! z?M?;BsHdC{z|V7MKL;MB7~sp(JgLW;rhzQib6GD5OO5qbT~GZhe<036^rsevK(=`D zo>9C_9TvhsmV;aBCBe$d05mEEAoHhyfhbW3s~pQrmkr-psftM9U0S(woZ6@v?0ZKF{l0ZDyenCfz{%fz)BmX)}#J<3_ZiX zu(%PJIm6M-uLWT!cdJ4}g$M>=^IS)~?NZ}(b7)qo-W_vC*1eS-CRrK4m5Ph}$ta@6 zxw(XVpps)2VvqCtp;FO9oZ0|Nw5e&gOX$&`pJw|xqTA6P9Bv=LX!r1*%>kyS!Be?I z3)WOBIEoC8S$VGfsCT8J5Og+l90hQ-2_TRP^+^D87&P*!_cniQ{-$ESZ6A%B0Zf0l zVq+2^KUg)SUvCF5=Q2^sk=7c1@E-&WOtaKFATX(Cl1Qtk_8KdfzhOcXx+VMK(nWUKyDZuI;1j~?Fd{j6Ri)w1P>JBbO(83 z({w3}8Gcl4_u%+=5Nt}9jXT>YXlX5RXPF&%?h@L(wwNg00J;~u!2T!8Abqx&e}f}^ z4IXE_MHkNr(b}-Kzac+hloL0?JUCzUW%{aq_gouoWd&=BugyQT1xPbJYF&<@pLyMU zXWgSjbbc^tQzeJIpY2=F($>~C@>+V2m#f5;Bzj-z?NX}A01HrYd)O4}fU+^SrH?<7 z7~!F7Ym*5#9gQxQE87lfwB4$Hh4bk0sYaK>_~yPv=x2HK4&=*XX$R~bh{k6KsNc}f zryxa(i;g%gi*$XOFEq%Tdx-Re=;L(1Rj9`{=y>d{t;aHwi?ZNPh^K%t9}e$lM~0NS zhrepRUVeB(z>L+*+h$SCL$h2Ht#<|q9bn^lesTLt@aGez0l&oZG|!bTwbBO@ztjpdpN zskK91!g7`qomOB+hLi}g$l~BzbwNv?;0{c_)eQM#iRu9gr_ii)OOZUrYRTJL+@I3JxAvU)3V3@WS3Hjd%t- zICM#jtC*0;@q_PNeh=PkJn=Pnyw3Lsv*B+K@@8-HF^#NT3yi?&RYWLwuN}^#hh0Ap z`Bg6u);&=sM}Wg1ckV@iVv|#CB1hk|{q%Tw= z{Leb|4!Pw?b-p>AEg$sr-(q5X?&FOnU^h_-{{;GYO*1*@|DO2Y0G(7LKUo96RV-B$ z3(DE#r_RfD+!ZF6cvbAMkM=vsZI`Gk5&PN}AAI|xSU!5srYc8NHOv)Li0x|G%PjY& zlw`#o;D@!Z)_dA^xs!B0$r9kLZD>E6qN`DNCus>Dx*AkhiHC7Rd&MgsmzTlNHSS0Oz&edKgpUqC@VDOQx+?+BSS_?vE^V%}5 zqJ-2z(LH%e*W9s_4`izB8l+LKu@NXHCDDuRBy?1X-Cb!9c#=}_uJattF|MS;u}ntg zXky2|oio{lS^|GieV8wC)=+76++^DV>@&Zc+#c=7$nAQ06#}d|9^Az#*7av*!$+a6 zWq|?gDrQ|dcVi&|ciNIKaoveWeI7r^&E=8(BrM7r?~^l-svmvXJ#f|=&R(mL6WFj9 zMFR+jU|l))+6o+zUH6Xxmm5%~An)>9@^w4H8lxn>@53QF!_S^&ahlH97NMwiWLcb` zGmg^9rAfbz&4M{Bj8N!(m(?RlzU;SFmp6l_NJT3PFs28-XK`XgX@2tS^*-E-3#rbn zELl2-tm8_!U9`uH8)7Z6z_(mUAn0JvKe@?gS`dnkZD76cM&Qz`sbf|)7`h;)XZVDB zLJ09Gosq_skapowbCIqyk*W}She;imw>UyU9{Q=@3n&gR@y6$LKi3;`HN!1l6$pE{ z89jD21*`Yr<3;s z7V$|$>!PC_7Btl?x`4GSLS#i#b!F+l*;*7P1HmI1F$Dwwl61OeGXS+navMA$cP~x? zAqLVqbUKG@6lko#>!ccf5LY+J<=7tDpw6`|&(L zgWFc}DXQ$o$oL$cAh1yva$0bhfqvpu8PD(;kcmWM0%15wEBCwBI+&Ri7A#t4QcAxO zY)A+eA(*}eveMF)Q>CS{2Go;1G0#}EZf&q!(-hw><<@SmpsA#45+gA%auw^L3zI!N zLc!ak&We8&-KpcdXtVj00jA2&3H2;`oAu+N&MXd6>A>-P)5Vi<<%w16N&0bBj93_rI_|2AhDR!CrHK0?fc zVOm+M^hLs#UJO?nK%cBBP0>Jb19;IVHT3%e0_sbZgu8<|A@$Th>)*DF9a>qU^{+Bd zWWf7776EnedN`;_@3;I#@NUaFS-(|_oxeC4k&FG-lUhiK0gf>Np#Tl+ zj3@4t@R#v&R6jk%;h-vsDin{J4k*ixh_tx=4thS1VN4qPN7yW5hms%mBIO}s(c^Z4 z^=N7`YZ}I}AY>N+FdZ@0AOVMtXqyj(oQUiGYvnN1wK!nov z`HOk*_(9iRuUFAZ(x4AO$b=~RK6=aPZ*=p36!@WIMVGgec9v!P`czX~l<$wnL1Y`xXG5j4j)F z9x1->f%Z#P>Zkg59G;uBq5NUf>9yYY#hkfFZLnzV}xpy5;% zA=D~%h##quZEo;$K$C0ENVDM&V9N&r!RX7n|7hL+k4>zPty*>Yx4# zXf?$=8>6wE=PgNN;~?;-7zU~SY=42-moH-3xzl4mw@yegYZvyI-*ot6?tX1i81hoW zN`o6fqirk-d(~M6kd#p8m~8al(%XcVPOM+8fIR=cdboCz&ggg!?ZL`&jY^;H-}|m9 I@eDomFO~Y7q5uE@ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4600f454ab35cf7c4cb7550975c3bd59ebd427a4 GIT binary patch literal 6900 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 S7sdPGW88gUSFX*Em;MX=yaS8? literal 0 HcmV?d00001 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_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 71cb01ccb..3887691a6 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -4688,6 +4688,8 @@ TP_SCRIPT # 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. # @@ -4715,32 +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. # - # The "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 "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 "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. - # - # "inside" and "steps" can be used with positive sizing values only. - # - # "outside" acts like "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 "inside" option is this: - # - # @code - # ntap.sized(30.um, inside(nwell), steps(100)) - # @/code - # - # \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 @@ -4757,6 +4733,45 @@ TP_SCRIPT # @td @img(/images/drc_sized6.png) @/td # @/tr # @/table + # + # The "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 "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 "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. + # + # "inside" and "steps" can be used with positive sizing values only. + # A steps value of 0 will not execute any sizing at all. + # + # "outside" acts like "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 "inside" option is this: + # + # @code + # ntap.sized(30.um, inside(nwell), steps(100)) + # @/code + # + # The effect of the "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 From 100760e219cfe6e0840902ad072993e06c74f532 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 23:41:35 +0200 Subject: [PATCH 13/16] Fixed progress description --- src/db/db/dbAsIfFlatRegion.cc | 1 + src/db/db/dbDeepRegion.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index d97e764ff..a62894e47 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1443,6 +1443,7 @@ AsIfFlatRegion::sized_inside (const Region &inside, bool outside, coord_type dx, // 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); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 076e9f46b..f4d6466a1 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1853,6 +1853,7 @@ DeepRegion::sized_inside (const Region &inside, bool outside, coord_type dx, coo // 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 ()); From 9865b14b0c1e37afa6adeb4f3035b88781d05c3d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Jun 2024 23:45:09 +0200 Subject: [PATCH 14/16] Update doc --- src/doc/doc/about/drc_ref_layer.xml | 53 +++++++++++++++-------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 7b03a95e1..1ebe933d4 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -3082,6 +3082,8 @@ it is called on. The input layer is returned and available for further processin 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.

@@ -3109,32 +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.

-The "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 "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 "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. -

-"inside" and "steps" can be used with positive sizing values only. -

-"outside" acts like "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 "inside" option is this: -

-

-ntap.sized(30.um, inside(nwell), steps(100))
-
-

-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:

@@ -3152,6 +3128,31 @@ The following images show the effect of various forms of the "sized" method:

+The "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 "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 "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. +

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

+"outside" acts like "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 "inside" option is this: +

+

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

The effect of the "inside" option is shown here:

From 726996433e602a20e52fca3e9978412341d05b43 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Jun 2024 19:54:30 +0200 Subject: [PATCH 15/16] Preparations for 0.29.3 --- Changelog | 28 ++++++++++++++++++++++++++++ Changelog.Debian | 7 +++++++ version.sh | 4 ++-- 3 files changed, 37 insertions(+), 2 deletions(-) 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/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") From 264f1e0586627639bfcaa672385fe34a6fe0f5cf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 Jul 2024 21:10:11 +0200 Subject: [PATCH 16/16] Stepwise sizing: had to rename options "inside" and "outside" was already taken. Now it is called "size_inside" and "size_outside". --- scripts/drc_lvs_doc/create_drc_samples.rb | 8 ++--- src/doc/doc/about/drc_ref_layer.xml | 34 ++++++++++----------- src/doc/doc/about/drc_ref_netter.xml | 8 +++++ src/doc/doc/images/drc_sized_inside1.png | Bin 6696 -> 6736 bytes src/doc/doc/images/drc_sized_inside2.png | Bin 6900 -> 6942 bytes src/doc/doc/images/drc_sized_inside3.png | Bin 7080 -> 7124 bytes src/doc/doc/images/drc_sized_inside4.png | Bin 6697 -> 6740 bytes src/drc/drc/built-in-macros/_drc_engine.rb | 4 +-- src/drc/drc/built-in-macros/_drc_layer.rb | 34 ++++++++++----------- testdata/drc/drcSimpleTests_130.drc | 20 ++++++------ 10 files changed, 58 insertions(+), 50 deletions(-) diff --git a/scripts/drc_lvs_doc/create_drc_samples.rb b/scripts/drc_lvs_doc/create_drc_samples.rb index 5a9c1c332..18450e1df 100644 --- a/scripts/drc_lvs_doc/create_drc_samples.rb +++ b/scripts/drc_lvs_doc/create_drc_samples.rb @@ -939,10 +939,10 @@ end gen = Gen::new -run_demo gen, "input1.sized(1.um, steps(1), inside(input2))", "drc_sized_inside1.png" -run_demo gen, "input1.sized(2.um, steps(2), inside(input2))", "drc_sized_inside2.png" -run_demo gen, "input1.sized(3.um, steps(3), inside(input2))", "drc_sized_inside3.png" -run_demo gen, "input1.sized(10.um, steps(10), inside(input2))", "drc_sized_inside4.png" +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) diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 1ebe933d4..4a3f99e18 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -3056,11 +3056,11 @@ The following images shows the effect of some rectangle filter modes:

Usage:

  • layer.size(d [, mode])
  • -
  • layer.size(d, inside(l) [, steps(n)] [, mode])
  • -
  • layer.size(d, outside(l) [, steps(n)] [, 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, inside(l) [, steps(n)] [, mode])
  • -
  • layer.size(dx, dy, outside(l) [, steps(n)] [, mode])
  • +
  • layer.size(dx, dy, size_inside(l) [, steps(n)] [, mode])
  • +
  • layer.size(dx, dy, size_outside(l) [, steps(n)] [, mode])

See sized for a description of the options. @@ -3071,12 +3071,12 @@ it is called on. The input layer is returned and available for further processin

Usage:

    -
  • layer.sized(d [, mode] [, inside(l) [, steps(n)]])
  • -
  • layer.sized(d, inside(l) [, steps(n)] [, mode])
  • -
  • layer.sized(d, outside(l) [, steps(n)] [, 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, inside(l) [, steps(n)] [, mode])
  • -
  • layer.sized(dx, dy, outside(l) [, steps(n)] [, 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 @@ -3128,32 +3128,32 @@ The following images show the effect of various forms of the "sized" method:

-The "inside" option and the "steps" option implement incremental size. Incremental +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 "inside" layer by means of a boolean "AND" operation. +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 "inside" layer, the size function +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.

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

-"outside" acts like "inside", but instead of confining the sized region to the +"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 "inside" option is this: +An example for the "size_inside" option is this:

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

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

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 index e013d4407f38172b5b6dc2d5fc59227a059a9d66..c0b69096af0dd5cc0578bab1273476c053202708 100644 GIT binary patch literal 6736 zcmd5>c~nzZw-2SShyx%N1!X7_piW4LieX9(6o?`y;(!BDP$DvE!lZ#j3yL@}1d%~P zYQX{yAV%Pe48etpNJy#y84>{{5E(*Lh5#YSyZ3@tTj^Tg`__Bw{gHKY@5$L`@890P zefHT&*yirCVD8enFc@qB*44=q22&XUKXbkT-Zds&pcywnOxl%i!L=%cvUm*j{PQ_jZS7oN zYTToO47f333nzRQh%f>t2u|zqo94$4CH@ z%eBdW&y(D18j#{dcOunUM{%b0SY2a0>8L-|UAg8(=|y^Sofpxfv6bw7Xv!|;BCqdz ztM0h4#tK9)y&%$~Z@IZ}r>Mitka9W2k`=$-n9pO)dC5fKNA&W`8_`2d<+KWc=ouny?B#Ty?t4Xtvo z+c$xu(Ph?E3Q}(cI_9DoKSsv0AlWH)DkFT9X`BgW_2VQKkDPO(D861wL;7?yqVna7 zQ0Yrs08C!uj6pwH;&tfmjjWC_sdd6N;T2zoiZ+mdk@+7rlqz*G9EiT_Htk< z>f1Z$qD4jV#%1@{3_m+;JylKb^>C)`jv8>x@2qU8D7I9NqE85g>6EwGmCO7JO+xFG zyI6uknk&ed7kmT;fBaC zpZrv1v&#gUql_YxCC$>&kyo-hk1+|an{txeG1@oqIxVuUp#p_FU=E-hcS5e47NA-l zpOrJOtza_jQYP{JJp_4JwW|($wcT`}ZV%>D{8c`awhaMJ$a6@_`!%{L3m4Ku(*1B1Cz85fOa!aAr9w+#Tb{PbG_lK_P2eAAAeQFAq3IfJ!kEHwHvXy#gEY_n zpC<-b4GhJ1oGs(PV2MKmVKxUW()r!8cv^n15wmxP=Xy=9Nb<|&jdWI#vX!T+pK-yy zf-b;q9ceE$FIV*teCO7FzPYF>z?xaRtVq@DI6SbWHYcDV=X)omlX=VxzEz8l!h)cPBfW!+S#9P~GWF!L* zGPcPXbN>$gfEI_!S3<*;cnKN=8%4?*xE~))J!wP3_}@?VuyUNd!K=QPoU#v#bq!l) z8DO%jV(fSO!-V+Ye`#dYe3BX#a{hi`(lKjh{IfR0xqvVI#?MzVbCO@5%p&V$i28I0 zt>iv^#*Cnmup0QK!{YX9W6AXfWv@@v#DPzELv=gEaIb5{*lV#_WK?ao{Z?^%DqnB) zjBfnXdJeushx+*eniFtjJ`Q6hozcs{vKFr;Za3#Unnkvr<19#t5O0S>yjyq+urwmo zw(@n~X{>I2d#Sb9Ou7v^95B8R6m~c5g`tRDFU&KI#m1?mr%&(X!QojaiFpTP?;!OK zadLi-5Z8-q7L}`{ZtyJ)3~k>!bR9ZE;vNAY#HB=2FyHm6Pwcy-Gpvl8+n3H^teU6C zOuG{2L|e98+@9MGsr4F!E)!CVr;=((ofh=&T%F1yE8u~C5+2(Fo@wd{00>9Vj)CAS z7W+Mprs};|ON-sT*ov@fg?d8oV8F4K9D#=2iI3iRCDaK0%o&<#4EyiFQ&Dv8XwO(O zt97(>t+5F2MRH|@Y3%WyHgKr}Uc#zQjYHnSwFK43H#@ea9 z)TQ*AV2L!`hGvo8Ab$U6+Q^C*4NE7cM#q~<ZOib5;BvrG+@I}i-r;eYkSq4OP;n|I=18rOL zWGcCfe{p#k;kNPdG-(jt7mnZGdp?<5m;rwoaTyo^Z})euF9x6p3)sVdXjXXkS8lt; zhJf+&G&^d^ZE;5OG@R%5o_BU-dAVr^R)8ZwG2oYLr*~|^{DBWvyOy{37YAThc&XNi zHy>d<9k%mJwZLsY+s<>|CVHts(2I8qZBN4eyg%-ls19WijBml&qj4i!9>eRIhWm{# zXT#w$3#fbNOx_kBO#NavGpG(yPq@DLw}mg(>ZCquF9Jp|_G(=Qg(;Vge5qH@e$67S zs8~o6V42hi2$Zi{24a9+^KHz_h`ZQh#%~#Aei+W!HZTStxvS>&rC-4M2Di-VOW(X1 zvm7!R0`F^(%&sIPI(F+{ApyVx&IhdPSHZ(^M!V*ou`ZZ))E7$f8UB2Q;*h9t!lh{9 zidSQ8ZxIvqfxK}&Zgga{xDM?A3?BKsTj7qqOdJ23zbBD9C8NB7W4$`Rsdb);iP$n@ z@icZfI0iEqxIJxqs}5u}$2CqSc{t#}0#yhQ!i`ft`h>~*HzELFpLx<@FhZwe^01wp?yXxDHUq2_p45dCP z`7yjp$^wBfH#Pl);kl&NdK0~dPk{%34jm;QvG_aa{}%0L2Yq#MTN{(odh(beCD6fi z=kQ$$G0gTuw1PajyrtWS{-DZzxJf=>jqtH=rs#fXO59;z1t;?)Qf_tlfyPSANY}n% zZpcjD0K>6gaOiI&eFG)-=tu5`%zJ-H2O@Vc9f0g66shf=Q@IHW!oOPoHHZG0Z62Bd z2Y#xH)zwmiMNoAV_vovR{ha?ZnFMgC_&QjJebu3#_#eCVuK@ln!-3AvOG%A$`jXYt zdFvnZ=ocCH^9%=ttoBTG^3`nc5xZG4BjT%|Pv_wO5cIKVkzUN^quzY6HzSy$ygS@9 zWzrJ8`KByB7yrTBm)m@3$NIpE-okubxB~?p^N|0(aUVBYzB|5A>66$ND{sNmow-7f zOAe;@wTIedE+71Wo89RonM}eeR&Jy#QFJBQu8SvDP7QNO33~m}3I{stWhmZTsb_FH z?NU}77mt)(&1j4C%dEG@=bD2R+_f`E8EZwhWwMi*{si=Lq^*=aSaFzu)`z~4 zdQ_W}<|Mp-+l3^#vxIO2qqCtez0=&=pTOF0aj}vkISUr08^_r`SUELS`1}xlY;!;G zgOWAG@B0Or-A|D+PKH2p6U~-bK+M?nSiuUiQBZ^aa}`NjpRNOASlvuoi(nAB?CRZSQa=QPXUUMH zX?mfT!<}o{IiNs8gOmQ;z9pfl*xu)HsKDYHLWElBzSu{TE*K)?Ga1~ zJ(GrtzV`q*CIAFHNz0U*<>Ls{{h4jWgvrM_q*WlSyAx52-JXEmE08i;Hn~K^;cM73 zhOcDC6uS-8`TFM{^6y-gDm#c54u?~BuVyC5bkFHjM?ORtf&8&ElTdo zrKS7G=@~ESZDM9!e^5lR9Yf81WFLNC?;Z^jm$OP8X-hvyKx-jwi5YFuhe9$4J)qFC zX@#tB8Ur=~kzr5a+UMhTArMG`UpK9|SDMQ?vr58DJk+FtB8EVVSTS}m~ z4Y?fl7v*Heq`M8p`$6A|qko-TXpAs40IdQ*^&calg8HZOeBE&fq;mJA)|<#EQ&D80~@Y}z!4cxbm91=!V29l#AXc4wZ%NDti zj8=B|cQgZKeF;Y-iOZ}a>Z1J(?(a)dG7>2q5qh+x+dSJSw0uqKvGEYLFl8zO=Q|0w z_cAbp8^z1V^ifd6r`$3o0*0%f$O_I(leS6t!3~d23FfP}-B9s)Ko_)h;#r~A%(^V* z`zlODeawmL7fr~;N~xelKq(g)Hgpm@5!0+V_SAvYtAJ?6d^T}7{5BW?82#`eH&2lU zKbJ$p_6s6cAKmom9suSFytP>{#b0e|=TlWwuY;|`rrkm2Gx5`?3C34Yp%{I|0&$Th zMc?_Ox-0Vi7Esqbz)67r2~;f?nr&(mQ?h3$X&V_k{XN58D&HaE zsf)UtphI<{k*4dB6Tr?`nk>;YC)h}V zy8DWKq|LO=PZaDogG{6IF%i9Yxz(Cx(%fw#j=q0{Yd!=|=G}Y`B2q zrM5vyfSHBF&XOaGo$`4r4le{ckALC<2b^%2VwQ(HmH>y+h;w&_Y)}euEjL#@lPkB2 zAU*=A&^9P`S{woZ0q{JY$`V!kH3T(eM|e%JfY{pIF9?NJ@8p=@^6MGd;;#Ls0Qi|r zWUC<*2i1O?<}JSp5DSZ{Km^E;$pg0Bw>vNceew^l)1H6sn|}vY-2bz_o8*gx4&dbZbJb{#7X;K#)a(OOv$WJU#euIjZ1ztk2s<&bNp+mrY-gBI3|CjaGq363RAIM1)$6@Hv&gUu{1|QrY z+R(Bnu=!H@a}{SR165KfTbRRGfk4)WoAeNoq^Ihi{|zwH!fXQ?$b0czc{3Vz&Q?&y z4h?DD+F7$p|GjFne_~GdKxiG#V#%;=jbTRPnKd?V)o#+w{@#(S==ok1u(6&nX7TsG z6o6#FnoMP(%$LWr@Gy8QmPUGdr9a<5>&pgym4$g1P7RzXRA~Ner0Mdo Sf_NQtBlc%^ry|V3U;Z1ME}K#S literal 6696 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 I@eDomFO~Y7q5uE@ diff --git a/src/doc/doc/images/drc_sized_inside2.png b/src/doc/doc/images/drc_sized_inside2.png index 4600f454ab35cf7c4cb7550975c3bd59ebd427a4..0db6f427d2a20a4d58272e68dc7715712a62165f 100644 GIT binary patch literal 6942 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 r&bHlsHT|KYN)!R&TkHLim36+8eZIYa`?rU) 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 literal 7080 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 literal 6697 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 S7sdPGW88gUSFX*Em;MX=yaS8? diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 90a63eb43..bcf27580b 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -317,11 +317,11 @@ module DRC DRCSizingSteps::new(arg) end - def inside(arg) + def size_inside(arg) DRCSizingInside::new(arg) end - def outside(arg) + def size_outside(arg) DRCSizingOutside::new(arg) end diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 3887691a6..0bb26948d 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -4678,12 +4678,12 @@ TP_SCRIPT # %DRC% # @name sized # @brief Polygon sizing (per-edge biasing) - # @synopsis layer.sized(d [, mode] [, inside(l) [, steps(n)]]) - # @synopsis layer.sized(d, inside(l) [, steps(n)] [, mode]) - # @synopsis layer.sized(d, outside(l) [, steps(n)] [, 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, inside(l) [, steps(n)] [, mode]) - # @synopsis layer.sized(dx, dy, outside(l) [, steps(n)] [, 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. @@ -4734,32 +4734,32 @@ TP_SCRIPT # @/tr # @/table # - # The "inside" option and the "steps" option implement incremental size. Incremental + # 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 "inside" layer by means of a boolean "AND" operation. + # 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 "inside" layer, the size function + # 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. # - # "inside" and "steps" can be used with positive sizing values only. + # "size_inside" and "steps" can be used with positive sizing values only. # A steps value of 0 will not execute any sizing at all. # - # "outside" acts like "inside", but instead of confining the sized region to the + # "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 "inside" option is this: + # An example for the "size_inside" option is this: # # @code - # ntap.sized(30.um, inside(nwell), steps(100)) + # ntap.sized(30.um, size_inside(nwell), steps(100)) # @/code # - # The effect of the "inside" option is shown here: + # The effect of the "size_inside" option is shown here: # # @table # @tr @@ -4777,11 +4777,11 @@ TP_SCRIPT # @name size # @brief Polygon sizing (per-edge biasing, modifies the layer) # @synopsis layer.size(d [, mode]) - # @synopsis layer.size(d, inside(l) [, steps(n)] [, mode]) - # @synopsis layer.size(d, outside(l) [, steps(n)] [, 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, inside(l) [, steps(n)] [, mode]) - # @synopsis layer.size(dx, dy, outside(l) [, steps(n)] [, 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 for a description of the options. # The size method basically does the same but modifies the layer diff --git a/testdata/drc/drcSimpleTests_130.drc b/testdata/drc/drcSimpleTests_130.drc index 3d538f929..a8bf10a8c 100644 --- a/testdata/drc/drcSimpleTests_130.drc +++ b/testdata/drc/drcSimpleTests_130.drc @@ -12,26 +12,26 @@ l2 = input(2, 0) l1.output(1, 0) l2.output(2, 0) -l1.sized(0.0, steps(50), inside(l2)).output(100, 0) -l1.sized(20.0, steps(0), inside(l2)).output(101, 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), inside(l2)).output(110, 0) -l1.sized(50.0, steps(50), outside(l2)).output(111, 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), inside(l2)) +l1d.size(20.0, steps(50), size_inside(l2)) l1d.output(120, 0) l1d = l1.dup -l1d.size(50.0, steps(50), outside(l2)) +l1d.size(50.0, steps(50), size_outside(l2)) l1d.output(121, 0) -l1.sized(10.0, 20.0, steps(50), inside(l2)).output(130, 0) -l1.sized(25.0, 50.0, steps(50), outside(l2)).output(131, 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), outside(l2)) + l2.sized(-1.0, steps(50), size_outside(l2)) rescue error = true end @@ -41,7 +41,7 @@ end error = nil begin - l2.sized(-1.0, 2.0, steps(50), outside(l2)) + l2.sized(-1.0, 2.0, steps(50), size_outside(l2)) rescue error = true end