diff --git a/scripts/create_drc_samples.rb b/scripts/create_drc_samples.rb index 02be4140d..f7963be5d 100644 --- a/scripts/create_drc_samples.rb +++ b/scripts/create_drc_samples.rb @@ -269,6 +269,7 @@ gen = Gen::new run_demo gen, "input1.separation(input2, 1.2, euclidian)", "drc_separation1.png" run_demo gen, "input1.drc(separation(input2, euclidian) < 1.2)", "drc_separation1u.png" +run_demo gen, "input1.drc((separation(input2) >= 1.2).first_edges)", "drc_separation1un.png" class Gen def produce(s1, s2) diff --git a/src/buddies/src/bd/bdWriterOptions.cc b/src/buddies/src/bd/bdWriterOptions.cc index 2be048149..7a7db29cb 100644 --- a/src/buddies/src/bd/bdWriterOptions.cc +++ b/src/buddies/src/bd/bdWriterOptions.cc @@ -38,6 +38,7 @@ GenericWriterOptions::GenericWriterOptions () m_gds2_max_vertex_count (8000), m_gds2_no_zero_length_paths (false), m_gds2_multi_xy_records (false), + m_gds2_resolve_skew_arrays (false), m_gds2_max_cellname_length (32000), m_gds2_libname ("LIB"), m_gds2_user_units (1.0), @@ -150,6 +151,12 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin "If this option is given, multiple XY records will be written to accommodate an unlimited number " "of points per polygon or path. However, such files may not be compatible with some consumers." ) + << tl::arg (group + + "-ow|--resolve-skew-arrays", &m_gds2_resolve_skew_arrays, "Resolve skew (non-orthogonal) arrays", + "If this option is given, skew arrays are resolved into single instances. Skew arrays " + "are ones where the row or column vectors are not horizontal or vertical. Such arrays can cause problems " + "in legacy software. This option will eliminate them at the expense of bigger files and loss of the array instance property." + ) << tl::arg (group + "#--no-zero-length-paths", &m_gds2_no_zero_length_paths, "Converts zero-length paths to polygons", "If this option is given, zero-length paths (such with one point) are not written as paths " @@ -349,6 +356,7 @@ GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db:: save_options.set_option_by_name ("gds2_max_vertex_count", m_gds2_max_vertex_count); save_options.set_option_by_name ("gds2_no_zero_length_paths", m_gds2_no_zero_length_paths); save_options.set_option_by_name ("gds2_multi_xy_records", m_gds2_multi_xy_records); + save_options.set_option_by_name ("gds2_resolve_skew_arrays", m_gds2_resolve_skew_arrays); save_options.set_option_by_name ("gds2_max_cellname_length", m_gds2_max_cellname_length); save_options.set_option_by_name ("gds2_libname", m_gds2_libname); save_options.set_option_by_name ("gds2_user_units", m_gds2_user_units); diff --git a/src/buddies/src/bd/bdWriterOptions.h b/src/buddies/src/bd/bdWriterOptions.h index 5413133ec..4980daa3c 100644 --- a/src/buddies/src/bd/bdWriterOptions.h +++ b/src/buddies/src/bd/bdWriterOptions.h @@ -116,6 +116,7 @@ private: unsigned int m_gds2_max_vertex_count; bool m_gds2_no_zero_length_paths; bool m_gds2_multi_xy_records; + bool m_gds2_resolve_skew_arrays; unsigned int m_gds2_max_cellname_length; std::string m_gds2_libname; double m_gds2_user_units; diff --git a/src/db/db/db.pro b/src/db/db/db.pro index b22167cff..58fe54140 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -24,6 +24,7 @@ SOURCES = \ dbCompoundOperation.cc \ dbEdge.cc \ dbEdgePair.cc \ + dbEdgePairFilters.cc \ dbEdgePairRelations.cc \ dbEdgePairs.cc \ dbEdgeProcessor.cc \ @@ -233,6 +234,7 @@ HEADERS = \ dbCompoundOperation.h \ dbEdge.h \ dbEdgePair.h \ + dbEdgePairFilters.h \ dbEdgePairRelations.h \ dbEdgePairs.h \ dbEdgeProcessor.h \ diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index 47e54c130..127409275 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -46,7 +46,17 @@ AsIfFlatEdgePairs::AsIfFlatEdgePairs () AsIfFlatEdgePairs::AsIfFlatEdgePairs (const AsIfFlatEdgePairs &other) : EdgePairsDelegate (other), m_bbox_valid (other.m_bbox_valid), m_bbox (other.m_bbox) { - // .. nothing yet .. + operator= (other); +} + +AsIfFlatEdgePairs & +AsIfFlatEdgePairs::operator= (const AsIfFlatEdgePairs &other) +{ + if (this != &other) { + m_bbox_valid = other.m_bbox_valid; + m_bbox = other.m_bbox; + } + return *this; } AsIfFlatEdgePairs::~AsIfFlatEdgePairs () diff --git a/src/db/db/dbAsIfFlatEdgePairs.h b/src/db/db/dbAsIfFlatEdgePairs.h index 5fe1e3f1d..938beaeea 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.h +++ b/src/db/db/dbAsIfFlatEdgePairs.h @@ -81,6 +81,8 @@ protected: void invalidate_bbox (); private: + friend class DeepEdgePairs; + AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other); mutable bool m_bbox_valid; diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index f2fa65d1b..c2e9f2f08 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -167,22 +167,6 @@ AsIfFlatRegion::in (const Region &other, bool invert) const return new_region.release (); } -size_t -AsIfFlatRegion::count () const -{ - size_t n = 0; - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - ++n; - } - return n; -} - -size_t -AsIfFlatRegion::hier_count () const -{ - return count (); -} - bool AsIfFlatRegion::is_box () const { @@ -1077,8 +1061,14 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, others.push_back (begin_merged ()); } else { foreign.push_back (false); - others.push_back (other->begin ()); - other_is_merged = other->is_merged (); + if (options.whole_edges) { + // NOTE: whole edges needs both inputs merged + others.push_back (other->begin_merged ()); + other_is_merged = true; + } else { + others.push_back (other->begin ()); + other_is_merged = other->is_merged (); + } has_other = true; } diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index d2b8ff9e3..e7b54659b 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -47,8 +47,6 @@ public: virtual ~AsIfFlatRegion (); virtual bool is_box () const; - virtual size_t count () const; - virtual size_t hier_count () const; virtual area_type area (const db::Box &box) const; virtual perimeter_type perimeter (const db::Box &box) const; diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index 8792a6505..9f59c5da6 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -346,16 +346,87 @@ EdgePairsDelegate *DeepEdgePairs::add (const EdgePairs &other) const } } -EdgePairsDelegate *DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter) +EdgePairsDelegate * +DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter) { - // TODO: implement - return AsIfFlatEdgePairs::filter_in_place (filter); + // TODO: implement to be really in-place + *this = *apply_filter (filter); + return this; } -EdgePairsDelegate *DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const +EdgePairsDelegate * +DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const { - // TODO: implement - return AsIfFlatEdgePairs::filtered (filter); + return apply_filter (filter); +} + +DeepEdgePairs * +DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const +{ + const db::DeepLayer &edge_pairs = deep_layer (); + + std::unique_ptr vars; + if (filter.vars ()) { + + vars.reset (new db::VariantsCollectorBase (filter.vars ())); + + vars->collect (edge_pairs.layout (), edge_pairs.initial_cell ()); + + if (filter.wants_variants ()) { + const_cast (edge_pairs).separate_variants (*vars); + } + + } + + db::Layout &layout = const_cast (edge_pairs.layout ()); + std::map > to_commit; + + std::unique_ptr res (new db::DeepEdgePairs (edge_pairs.derived ())); + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const db::Shapes &s = c->shapes (edge_pairs.layer ()); + + if (vars.get ()) { + + const std::map &vv = vars->variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *st; + if (vv.size () == 1) { + st = & c->shapes (res->deep_layer ().layer ()); + } else { + st = & to_commit [c->cell_index ()] [v->first]; + } + + const db::ICplxTrans &tr = v->first; + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) { + if (filter.selected (si->edge_pair ().transformed (tr))) { + st->insert (*si); + } + } + + } + + } else { + + db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) { + if (filter.selected (si->edge_pair ())) { + st.insert (*si); + } + } + + } + + } + + if (! to_commit.empty () && vars.get ()) { + res->deep_layer ().commit_shapes (*vars, to_commit); + } + + return res.release (); } RegionDelegate * @@ -461,4 +532,14 @@ void DeepEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type deep_layer ().insert_into_as_polygons (layout, into_cell, into_layer, enl); } +DeepEdgePairs &DeepEdgePairs::operator= (const DeepEdgePairs &other) +{ + if (this != &other) { + AsIfFlatEdgePairs::operator= (other); + DeepShapeCollectionDelegateBase::operator= (other); + } + + return *this; +} + } diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index 75408ea94..b66d45420 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -104,6 +104,7 @@ private: void init (); EdgesDelegate *generic_edges (bool first, bool second) const; + DeepEdgePairs *apply_filter (const EdgePairFilterBase &filter) const; }; } diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 9660c10b9..3d9504a9f 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -273,24 +273,33 @@ void DeepEdges::reserve (size_t) // Not implemented for deep regions } -void DeepEdges::flatten () +static +void flatten_layer (db::DeepLayer &deep_layer) { - db::Layout &layout = deep_layer ().layout (); + db::Layout &layout = deep_layer.layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); db::Shapes flat_shapes (layout.is_editable ()); - for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) { flat_shapes.insert (iter->edge ().transformed (iter.trans ())); } - layout.clear_layer (deep_layer ().layer ()); - top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); + layout.clear_layer (deep_layer.layer ()); + top_cell.shapes (deep_layer.layer ()).swap (flat_shapes); } } +void DeepEdges::flatten () +{ + flatten_layer (deep_layer ()); + if (m_merged_edges_valid) { + flatten_layer (const_cast (merged_deep_layer ())); + } +} + EdgesIteratorDelegate * DeepEdges::begin () const { @@ -518,7 +527,7 @@ DeepEdges::ensure_merged_edges_valid () const m_merged_edges = deep_layer ().derived (); - tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged polygons"); + tl::SelfTimer timer (tl::verbosity () > base_verbosity (), "Ensure merged edges"); db::Layout &layout = const_cast (deep_layer ().layout ()); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index c869ac5af..b412fedf5 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -284,26 +284,37 @@ void DeepRegion::reserve (size_t) // Not implemented for deep regions } -void DeepRegion::flatten () +static void +flatten_layer (db::DeepLayer &deep_layer) { - db::Layout &layout = deep_layer ().layout (); + db::Layout &layout = deep_layer.layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); db::Shapes flat_shapes (layout.is_editable ()); - for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { - db::Polygon poly; - iter->polygon (poly); - flat_shapes.insert (poly.transformed (iter.trans ())); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) { + if (iter->is_polygon ()) { + db::Polygon poly; + iter->polygon (poly); + flat_shapes.insert (db::PolygonRef (poly.transformed (iter.trans ()), layout.shape_repository ())); + } } - layout.clear_layer (deep_layer ().layer ()); - top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); + layout.clear_layer (deep_layer.layer ()); + top_cell.shapes (deep_layer.layer ()).swap (flat_shapes); } } +void DeepRegion::flatten () +{ + flatten_layer (deep_layer ()); + if (m_merged_polygons_valid) { + flatten_layer (const_cast (merged_deep_layer ())); + } +} + RegionIteratorDelegate * DeepRegion::begin () const { @@ -651,6 +662,13 @@ DeepRegion::not_with (const Region &other) const } } +RegionDelegate * +DeepRegion::or_with (const Region &other) const +{ + // NOTE: this is somewhat different from the as if flat case because it does not merge + return add (other); +} + std::pair DeepRegion::andnot_with (const Region &other) const { @@ -1090,6 +1108,7 @@ public: virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const { db::EdgeProcessor ep; + ep.set_base_verbosity (50); for (shape_interactions::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { ep.insert (s->second); @@ -1637,8 +1656,14 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons if (! other_deep) { return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options); } - other_layer = other_deep->deep_layer ().layer (); - other_is_merged = other->is_merged (); + if (options.whole_edges) { + // NOTE: whole edges needs both inputs merged + other_layer = other_deep->merged_deep_layer ().layer (); + other_is_merged = true; + } else { + other_layer = other_deep->deep_layer ().layer (); + other_is_merged = other->is_merged (); + } } const db::DeepLayer &polygons = merged_deep_layer (); diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 38252620d..175255389 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -99,6 +99,7 @@ public: virtual RegionDelegate *and_with (const Region &other) const; virtual RegionDelegate *not_with (const Region &other) const; virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; virtual std::pair andnot_with (const Region &) const; virtual RegionDelegate *add_in_place (const Region &other); diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 733f8ec3e..74e8f6e8b 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -403,9 +403,15 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter { unsigned int layer_index = layout.insert_layer (); - if (si.layout () && si.layer () < si.layout ()->layers ()) { + if (si.layout ()) { // try to preserve the layer properties - layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ())); + if (! si.multiple_layers ()) { + if (si.layer () < si.layout ()->layers ()) { + layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ())); + } + } else if (! si.layers ().empty ()) { + layout.set_properties (layer_index, si.layout ()->get_properties (si.layers ().front ())); + } } return layer_index; diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index f9d357535..389e81677 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -79,18 +79,6 @@ bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) cons return false; } -bool EqualDeviceParameters::equal (const db::Device &a, const db::Device &b) const -{ - for (std::vector > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) { - int cmp = compare_parameters (a.parameter_value (c->first), b.parameter_value (c->first), c->second.first, c->second.second); - if (cmp != 0) { - return false; - } - } - - return true; -} - EqualDeviceParameters &EqualDeviceParameters::operator+= (const EqualDeviceParameters &other) { for (std::vector > >::const_iterator c = other.m_compare_set.begin (); c != other.m_compare_set.end (); ++c) { @@ -121,19 +109,6 @@ bool AllDeviceParametersAreEqual::less (const db::Device &a, const db::Device &b return false; } -bool AllDeviceParametersAreEqual::equal (const db::Device &a, const db::Device &b) const -{ - const std::vector ¶meters = a.device_class ()->parameter_definitions (); - for (std::vector::const_iterator c = parameters.begin (); c != parameters.end (); ++c) { - int cmp = compare_parameters (a.parameter_value (c->id ()), b.parameter_value (c->id ()), 0.0, m_relative); - if (cmp != 0) { - return false; - } - } - - return true; -} - // -------------------------------------------------------------------------------- // DeviceClass class implementation @@ -293,7 +268,7 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b) } if (pcd != 0) { - return pcd->equal (a, b); + return ! pcd->less (a, b) && ! pcd->less (b, a); } else { const std::vector &pd = a.device_class ()->parameter_definitions (); diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h index 66ba8d5f6..24c8b5aec 100644 --- a/src/db/db/dbDeviceClass.h +++ b/src/db/db/dbDeviceClass.h @@ -297,7 +297,6 @@ public: virtual DeviceParameterCompareDelegate *clone () const = 0; virtual bool less (const db::Device &a, const db::Device &b) const = 0; - virtual bool equal (const db::Device &a, const db::Device &b) const = 0; }; /** @@ -315,7 +314,6 @@ public: EqualDeviceParameters (size_t parameter_id, double relative, double absolute); virtual bool less (const db::Device &a, const db::Device &b) const; - virtual bool equal (const db::Device &a, const db::Device &b) const; virtual DeviceParameterCompareDelegate *clone () const { @@ -345,7 +343,6 @@ public: AllDeviceParametersAreEqual (double relative); virtual bool less (const db::Device &a, const db::Device &b) const; - virtual bool equal (const db::Device &a, const db::Device &b) const; virtual DeviceParameterCompareDelegate *clone () const { diff --git a/src/db/db/dbEdge.h b/src/db/db/dbEdge.h index 27ed72d3f..39ddf359f 100644 --- a/src/db/db/dbEdge.h +++ b/src/db/db/dbEdge.h @@ -856,6 +856,9 @@ public: * line through the edge. If the edge is degenerated, the distance * is not defined. * + * The distance is through as a distance of the point from the line + * through the edge. + * * @param p The point to test. * * @return The distance @@ -877,6 +880,27 @@ public: } } + /** + * @brief Gets the distance of the point from the edge. + * + * The distance is computed as the minimum distance of the point to any of the edge's + * points. + * + * @param p The point whose distance is to be computed + * + * @return The distance + */ + distance_type euclidian_distance (const db::point &p) + { + if (db::sprod_sign (p - p1 (), d ()) < 0) { + return p1 ().distance (p); + } else if (db::sprod_sign (p - p2 (), d ()) > 0) { + return p2 ().distance (p); + } else { + return std::abs (distance (p)); + } + } + /** * @brief Side of the point * diff --git a/src/db/db/dbEdgePair.h b/src/db/db/dbEdgePair.h index 805fb8cbe..0b6209725 100644 --- a/src/db/db/dbEdgePair.h +++ b/src/db/db/dbEdgePair.h @@ -204,6 +204,24 @@ public: return !equal (b); } + /** + * @brief Computes the distance of the edges in the edge pair + * + * The distance is the minimum distance of any of the points from + * each edge. + */ + distance_type distance () const + { + db::edge e1 = first (), e2 = second (); + if (! e1.intersect (e2)) { + distance_type d12 = std::min (e2.euclidian_distance (e1.p1 ()), e2.euclidian_distance (e1.p2 ())); + distance_type d21 = std::min (e1.euclidian_distance (e2.p1 ()), e1.euclidian_distance (e2.p2 ())); + return std::min (d12, d21); + } else { + return 0; + } + } + /** * @brief A method binding of operator* (mainly for automation purposes) */ diff --git a/src/db/db/dbEdgePairFilters.cc b/src/db/db/dbEdgePairFilters.cc new file mode 100644 index 000000000..541f33731 --- /dev/null +++ b/src/db/db/dbEdgePairFilters.cc @@ -0,0 +1,81 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbCommon.h" + +#include "dbEdgePairFilters.h" +#include "dbEdges.h" + +namespace db +{ + +// --------------------------------------------------------------------------------------------------- +// EdgeFilterBasedEdgePairFilter implementation + +EdgeFilterBasedEdgePairFilter::EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match) + : mp_edge_filter (edge_filter), m_one_must_match (one_must_match) +{ + // .. nothing yet .. +} + +EdgeFilterBasedEdgePairFilter::~EdgeFilterBasedEdgePairFilter () +{ + // .. nothing yet .. +} + +bool EdgeFilterBasedEdgePairFilter::selected (const db::EdgePair &edge_pair) const +{ + if (m_one_must_match) { + return mp_edge_filter->selected (edge_pair.first ()) || mp_edge_filter->selected (edge_pair.second ()); + } else { + return mp_edge_filter->selected (edge_pair.first ()) && mp_edge_filter->selected (edge_pair.second ()); + } +} + +const TransformationReducer *EdgeFilterBasedEdgePairFilter::vars () const +{ + return mp_edge_filter->vars (); +} + +bool EdgeFilterBasedEdgePairFilter::wants_variants () const +{ + return mp_edge_filter->wants_variants (); +} + +// --------------------------------------------------------------------------------------------------- +// EdgePairFilterByDistance implementation + +EdgePairFilterByDistance::EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted) + : m_min_distance (min_distance), m_max_distance (max_distance), m_inverted (inverted) +{ + // .. nothing yet .. +} + +bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair) const +{ + distance_type dist = edge_pair.distance (); + bool sel = (dist >= m_min_distance && dist < m_max_distance); + return m_inverted ? !sel : sel; +} + +} diff --git a/src/db/db/dbEdgePairFilters.h b/src/db/db/dbEdgePairFilters.h new file mode 100644 index 000000000..9f25858e8 --- /dev/null +++ b/src/db/db/dbEdgePairFilters.h @@ -0,0 +1,82 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbEdgePairFilters +#define HDR_dbEdgePairFilters + +#include "dbEdgePairs.h" + +namespace db +{ + +class EdgeFilterBase; + +/** + * @brief A base class for edge pair filters based on edge filters + * + * If "one_must_match" is true, it is sufficient for one edge to be selected. + * If it is false, both edges need to be selected to make the edge pair selected. + * + * NOTE: the edge filter is not owned by the edge pair filter object. + */ +class DB_PUBLIC EdgeFilterBasedEdgePairFilter + : public EdgePairFilterBase +{ +public: + EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match); + virtual ~EdgeFilterBasedEdgePairFilter (); + + virtual bool selected (const db::EdgePair &edge_pair) const; + virtual const TransformationReducer *vars () const; + virtual bool wants_variants () const; + +private: + EdgeFilterBase *mp_edge_filter; + bool m_one_must_match; +}; + +/** + * @brief Filters edge pairs based on the distance of the edges. + * + * The distance is measured as the smallest distance between each of the points of the two edges. + */ +class DB_PUBLIC EdgePairFilterByDistance + : public EdgePairFilterBase +{ +public: + typedef db::coord_traits::distance_type distance_type; + + EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted); + + virtual bool selected (const db::EdgePair &edge_pair) const; + virtual const TransformationReducer *vars () const { return 0; } + virtual bool wants_variants () const { return false; } + +private: + distance_type m_min_distance, m_max_distance; + bool m_inverted; +}; + +} + +#endif diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 9412eb815..449131cbd 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -38,6 +38,9 @@ namespace db { +// --------------------------------------------------------------------------------------------------- +// EdgePairs implementation + EdgePairs::EdgePairs () : mp_delegate (new EmptyEdgePairs ()) { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index e2200e465..73a82c40a 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -42,17 +42,13 @@ class Edges; class Region; class DeepShapeStore; class TransformationReducer; +class EdgeFilterBase; /** * @brief An edge pair set iterator * * The iterator delivers the edge pairs of the edge pair set */ -/** - * @brief An edge set iterator - * - * The iterator delivers the edges of the edge set - */ class DB_PUBLIC EdgePairsIterator : public generic_shape_iterator { @@ -117,7 +113,7 @@ public: EdgePairFilterBase () { } virtual ~EdgePairFilterBase () { } - virtual bool selected (const db::EdgePair &edge) const = 0; + virtual bool selected (const db::EdgePair &edge_pair) const = 0; virtual const TransformationReducer *vars () const = 0; virtual bool wants_variants () const = 0; }; diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 2954f81e9..4944ef3ed 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -32,6 +32,7 @@ #include "dbShapeCollection.h" #include "dbShapeCollectionUtils.h" #include "dbGenericShapeIterator.h" +#include "dbHash.h" #include #include diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index ac8d0adfe..205c3f892 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -32,6 +32,7 @@ #include "dbLayoutVsSchematic.h" #include "dbLayoutToNetlistFormatDefs.h" #include "dbLayoutVsSchematicFormatDefs.h" +#include "dbShapeProcessor.h" #include "tlGlobPattern.h" namespace db @@ -246,11 +247,21 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters, m_device_scaling); } -void LayoutToNetlist::connect (const db::Region &l) +void LayoutToNetlist::reset_extracted () { if (m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + + m_net_clusters.clear (); + mp_netlist.reset (0); + + m_netlist_extracted = false; + } +} + +void LayoutToNetlist::connect (const db::Region &l) +{ + reset_extracted (); if (! is_persisted (l)) { register_layer (l, make_new_name ()); @@ -265,9 +276,8 @@ void LayoutToNetlist::connect (const db::Region &l) void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b) { - if (m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); - } + reset_extracted (); + if (! is_persisted (a)) { register_layer (a, make_new_name ()); } @@ -286,9 +296,8 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn) { - if (m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); - } + reset_extracted (); + if (! is_persisted (l)) { register_layer (l, make_new_name ()); } @@ -1293,6 +1302,93 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin } } +namespace +{ + +class PolygonAreaAndPerimeterCollector + : public db::PolygonSink +{ +public: + typedef db::Polygon polygon_type; + typedef polygon_type::perimeter_type perimeter_type; + typedef polygon_type::area_type area_type; + + PolygonAreaAndPerimeterCollector () + : m_area (0), m_perimeter (0) + { } + + area_type area () const + { + return m_area; + } + + perimeter_type perimeter () const + { + return m_perimeter; + } + + virtual void put (const db::Polygon &poly) + { + m_area += poly.area (); + m_perimeter += poly.perimeter (); + } + +public: + area_type m_area; + perimeter_type m_perimeter; +}; + +} + +static void +compute_area_and_perimeter_of_net_shapes (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Polygon::area_type &area, db::Polygon::perimeter_type &perimeter) +{ + db::EdgeProcessor ep; + + // count vertices and reserve space + size_t n = 0; + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { + n += rci->polygon_ref ().vertices (); + } + ep.reserve (n); + + size_t p = 0; + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { + ep.insert (rci.trans () * rci->polygon_ref (), ++p); + } + + PolygonAreaAndPerimeterCollector ap_collector; + db::PolygonGenerator pg (ap_collector, false); + db::SimpleMerge op; + ep.process (pg, op); + + area = ap_collector.area (); + perimeter = ap_collector.perimeter (); +} + +static void +get_merged_shapes_of_net (const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Shapes &shapes) +{ + db::EdgeProcessor ep; + + // count vertices and reserve space + size_t n = 0; + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { + n += rci->polygon_ref ().vertices (); + } + ep.reserve (n); + + size_t p = 0; + for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { + ep.insert (rci.trans () * rci->polygon_ref (), ++p); + } + + db::ShapeGenerator sg (shapes); + db::PolygonGenerator pg (sg, false); + db::SimpleMerge op; + ep.process (pg, op); +} + db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector > &diodes) { // TODO: that's basically too much .. we only need the clusters @@ -1318,56 +1414,65 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a continue; } - db::Region rgate, rmetal; - - deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0); - deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0); - - double agate = 0.0; - if (fabs (gate_area_factor) > 1e-6) { - agate += rgate.area () * dbu * dbu * gate_area_factor; - } - if (fabs (gate_perimeter_factor) > 1e-6) { - agate += rgate.perimeter () * dbu * gate_perimeter_factor; - } - - double ametal = 0.0; - if (fabs (metal_area_factor) > 1e-6) { - ametal += rmetal.area () * dbu * dbu * metal_area_factor; - } - if (fabs (metal_perimeter_factor) > 1e-6) { - ametal += rmetal.perimeter () * dbu * metal_perimeter_factor; - } - double r = ratio; bool skip = false; for (std::vector >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) { - db::Region rdiode; - deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode, 0); + db::Polygon::area_type adiode_int = 0; + db::Polygon::perimeter_type pdiode_int = 0; + + compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (*d->first), adiode_int, pdiode_int); if (fabs (d->second) < db::epsilon) { - if (rdiode.area () > 0) { + if (adiode_int > 0) { skip = true; } } else { - r += rdiode.area () * dbu * dbu * d->second; + r += adiode_int * dbu * dbu * d->second; } } if (! skip) { - if (tl::verbosity () >= 50) { - tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r); + db::Polygon::area_type agate_int = 0; + db::Polygon::perimeter_type pgate_int = 0; + + compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (gate), agate_int, pgate_int); + + double agate = 0.0; + if (fabs (gate_area_factor) > 1e-6) { + agate += agate_int * dbu * dbu * gate_area_factor; + } + if (fabs (gate_perimeter_factor) > 1e-6) { + agate += pgate_int * dbu * gate_perimeter_factor; } - if (agate > dbu * dbu && ametal / agate > r + db::epsilon) { - db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ()); - for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) { - shapes.insert (*r); + if (agate > dbu * dbu) { + + db::Polygon::area_type ametal_int = 0; + db::Polygon::perimeter_type pmetal_int = 0; + + compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (metal), ametal_int, pmetal_int); + + double ametal = 0.0; + if (fabs (metal_area_factor) > 1e-6) { + ametal += ametal_int * dbu * dbu * metal_area_factor; } + if (fabs (metal_perimeter_factor) > 1e-6) { + ametal += pmetal_int * dbu * metal_perimeter_factor; + } + + if (tl::verbosity () >= 50) { + tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r); + } + + if (ametal / agate > r + db::epsilon) { + db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ()); + get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), shapes); + } + } } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 08cb3eb46..00cacb774 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -359,6 +359,14 @@ public: */ void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); + /** + * @brief Resets the extracted netlist + * + * This method will invalidate the netlist and extraction. It is called automatically when + * cone of the connect methods is called. + */ + void reset_extracted (); + /** * @brief Defines an intra-layer connection for the given layer. * The layer is either an original layer created with "make_layer" and it's variants or @@ -532,6 +540,14 @@ public: */ void set_netlist_extracted (); + /** + * @brief Gets a value indicating whether the netlist has been extracted + */ + bool is_netlist_extracted () const + { + return m_netlist_extracted; + } + /** * @brief Gets the internal DeepShapeStore object * diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 1e1d3140d..a59d2ec35 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -56,7 +56,6 @@ void local_operation::compute_local (db::Layout *layout, const shape } for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const TS &subject_shape = interactions.subject_shape (i->first); shape_interactions single_interactions; diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 94d26a944..ed68821fa 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -3052,7 +3052,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const db::DeviceClass *db = const_cast (i->second.second); const db::DeviceParameterCompareDelegate *cmp = da->parameter_compare_delegate (); - db->set_parameter_compare_delegate (cmp ? cmp->clone () : 0); + db->set_parameter_compare_delegate (const_cast (cmp)); } diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index b1a1c84ed..5bf03d678 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -130,7 +130,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const os << format_name (dev.device_class ()->name ()); } - } else if (res) { + } else if (res || res3) { os << "R"; os << format_name (dev.expanded_name ()); diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index 57b2a4380..2671686ac 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -29,6 +29,7 @@ #include "dbDeepEdges.h" #include "dbDeepRegion.h" #include "dbDeepShapeStore.h" +#include "dbCellGraphUtils.h" #include "tlGlobPattern.h" namespace db @@ -186,6 +187,96 @@ OriginalLayerRegion::min_coherence_changed () m_merged_polygons_valid = false; } +size_t +OriginalLayerRegion::count () const +{ + // NOTE: we should to make sure the iterator isn't validated as this would spoil the usability or OriginalLayerRegion upon + // layout changes + db::RecursiveShapeIterator iter = m_iter; + + if (iter.has_complex_region () || iter.region () != db::Box::world () || ! iter.enables ().empty () || ! iter.disables ().empty ()) { + + // complex case with a search region - use the iterator to determine the count (expensive) + size_t n = 0; + for (db::RecursiveShapeIterator i = iter; ! i.at_end (); ++i) { + ++n; + } + + return n; + + } else { + + // otherwise we can utilize the CellCounter + + size_t n = 0; + + const db::Layout &layout = *iter.layout (); + + std::set cells; + iter.top_cell ()->collect_called_cells (cells); + cells.insert (iter.top_cell ()->cell_index ()); + + db::CellCounter cc (&layout); + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + if (cells.find (*c) == cells.end ()) { + continue; + } + size_t nn = 0; + if (iter.multiple_layers ()) { + for (std::vector::const_iterator l = iter.layers ().begin (); l != iter.layers ().end (); ++l) { + nn += layout.cell (*c).shapes (*l).size (iter.shape_flags () & db::ShapeIterator::Regions); + } + } else if (iter.layer () < layout.layers ()) { + nn += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & db::ShapeIterator::Regions); + } + n += cc.weight (*c) * nn; + } + + return n; + + } +} + +size_t +OriginalLayerRegion::hier_count () const +{ + // NOTE: we should to make sure the iterator isn't validated as this would spoil the usability or OriginalLayerRegion upon + // layout changes + db::RecursiveShapeIterator iter = m_iter; + + if (iter.has_complex_region () || iter.region () != db::Box::world ()) { + + // TODO: how to establish a "hierarchical" interpretation in this case? + return count (); + + } else { + + size_t n = 0; + + const db::Layout &layout = *iter.layout (); + + std::set cells; + iter.top_cell ()->collect_called_cells (cells); + cells.insert (iter.top_cell ()->cell_index ()); + + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + if (cells.find (*c) == cells.end ()) { + continue; + } + if (iter.multiple_layers ()) { + for (std::vector::const_iterator l = iter.layers ().begin (); l != iter.layers ().end (); ++l) { + n += layout.cell (*c).shapes (*l).size (iter.shape_flags () & db::ShapeIterator::Regions); + } + } else if (iter.layer () < layout.layers ()) { + n += layout.cell (*c).shapes (iter.layer ()).size (iter.shape_flags () & db::ShapeIterator::Regions); + } + } + + return n; + + } +} + RegionIteratorDelegate * OriginalLayerRegion::begin () const { diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h index 23af03bc9..1ea81d966 100644 --- a/src/db/db/dbOriginalLayerRegion.h +++ b/src/db/db/dbOriginalLayerRegion.h @@ -58,6 +58,8 @@ public: virtual bool empty () const; virtual bool is_merged () const; + virtual size_t count () const; + virtual size_t hier_count () const; virtual const db::Polygon *nth (size_t n) const; virtual bool has_valid_polygons () const; diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 95c2b0e99..e96512e2d 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -550,11 +550,13 @@ public: } /** - * @brief Get the layer of the current shape + * @brief Gets the layer of the current shape */ unsigned int layer () const { - validate (0); + if (m_has_layers) { + validate (0); + } return m_layer; } @@ -562,7 +564,7 @@ public: * @brief Gets the layers from which the shapes are taken from * * The returned layers are useful only if \multiple_layers is - * true. + * true. Otherwise use \layer to get the iterated layer. */ const std::vector &layers () const { diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index a5178f606..3924f7ab4 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -34,8 +34,10 @@ #include "dbShapeCollection.h" #include "dbGenericShapeIterator.h" #include "dbRegionLocalOperations.h" +#include "dbHash.h" #include +#include namespace db { diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 4ce824442..3fcc09bf5 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -240,6 +240,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape // the processor. Reason: the search range is limited, hence not all necessary components may have been // captured. db::EdgeProcessor ep; + ep.set_base_verbosity (50); ep.clear (); size_t i = 0; @@ -561,6 +562,7 @@ void interacting_local_operation::do_compute_local (db::Layout * /*l } db::EdgeProcessor ep; + ep.set_base_verbosity (50); std::set others; for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { @@ -597,6 +599,7 @@ void interacting_local_operation::do_compute_local (db::Layout * /*l // in counted mode we need to merge the shapes because they might overlap db::EdgeProcessor ep_merge; + ep_merge.set_base_verbosity (50); size_t i = 0; for (typename std::set::const_iterator o = others.begin (); o != others.end (); ++o) { @@ -730,6 +733,7 @@ void pull_local_operation::do_compute_local (db::Layout * /*layout*/ std::unordered_set &result = results.front (); db::EdgeProcessor ep; + ep.set_base_verbosity (50); std::set others; for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index 9134dc12f..89c90d84c 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -52,7 +52,7 @@ void CornerDetectorCore::detect_corners (const db::Polygon &poly, const CornerPo db::Point pn = ctr [j]; if (m_checker (pt - pp, pn - pt)) { - delivery.make_point (pt); + delivery.make_point (pt, db::Edge (pp, pt), db::Edge (pt, pn)); } pp = pt; diff --git a/src/db/db/dbRegionProcessors.h b/src/db/db/dbRegionProcessors.h index d91613148..516529144 100644 --- a/src/db/db/dbRegionProcessors.h +++ b/src/db/db/dbRegionProcessors.h @@ -41,7 +41,7 @@ namespace db class DB_PUBLIC CornerPointDelivery { public: - virtual void make_point (const db::Point &pt) const = 0; + virtual void make_point (const db::Point &pt, const db::Edge &e1, const db::Edge &e2) const = 0; }; /** @@ -55,7 +55,7 @@ public: : m_d (dim, dim), mp_result (&result) { } - virtual void make_point (const db::Point &pt) const + virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const { mp_result->push_back (db::Polygon (db::Box (pt - m_d, pt + m_d))); } @@ -76,7 +76,7 @@ public: : mp_result (&result) { } - virtual void make_point (const db::Point &pt) const + virtual void make_point (const db::Point &pt, const db::Edge &, const db::Edge &) const { mp_result->push_back (db::Edge (pt, pt)); } @@ -86,6 +86,26 @@ private: std::vector *mp_result; }; +/** + * @brief An interface to accept corners and turns them into edge pairs + */ +class DB_PUBLIC CornerEdgePairDelivery + : public CornerPointDelivery +{ +public: + CornerEdgePairDelivery (std::vector &result) + : mp_result (&result) + { } + + virtual void make_point (const db::Point &, const db::Edge &e1, const db::Edge &e2) const + { + mp_result->push_back (db::EdgePair (e1, e2)); + } + +private: + std::vector *mp_result; +}; + /** * @brief A helper class implementing the core corner detection algorithm */ @@ -155,6 +175,31 @@ public: virtual bool wants_variants () const { return false; } }; +/** + * @brief A corner detector delivering edge pairs for the corners + */ +class DB_PUBLIC CornersAsEdgePairs + : public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore +{ +public: + CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) + : CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end) + { + // .. nothing yet .. + } + + void process (const db::Polygon &poly, std::vector &result) const + { + detect_corners (poly, CornerEdgePairDelivery (result)); + } + + virtual const TransformationReducer *vars () const { return 0; } + virtual bool result_is_merged () const { return false; } + virtual bool result_must_not_be_merged () const { return true; } // to preserve dots + virtual bool requires_raw_input () const { return false; } + virtual bool wants_variants () const { return false; } +}; + // ----------------------------------------------------------------------------------- // Extents diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index 9f0e6f513..b5f60cb05 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -154,13 +154,16 @@ Edge2EdgeCheckBase::finish (const Edge *o, const size_t &p) } } -void +bool Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner &scanner) { if (m_pass == 1) { for (std::set >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) { scanner.insert (&e->first, e->second); } + return ! m_pseudo_edges.empty (); + } else { + return false; } } @@ -442,11 +445,33 @@ poly2poly_check::add (const PolygonType *o1, size_t p1, const Polyg enter (*o1, p1, *o2, p2); } +static bool interact (const db::Box &box, const db::Edge &e) +{ + if (! e.bbox ().touches (box)) { + return false; + } else if (e.is_ortho ()) { + return true; + } else { + return e.clipped (box).first; + } +} + template void poly2poly_check::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2) { - if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { + if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { + + bool take_all = mp_output->has_negative_edge_output (); + + db::Box common_box; + if (! take_all) { + db::Vector e (mp_output->distance (), mp_output->distance ()); + common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e); + if (common_box.empty ()) { + return; + } + } m_scanner.clear (); m_scanner.reserve (vertices (o1) + vertices (o2)); @@ -454,20 +479,30 @@ poly2poly_check::enter (const PolygonType &o1, size_t p1, const Pol m_edges.clear (); m_edges.reserve (vertices (o1) + vertices (o2)); + bool any_o1 = false, any_o2 = false; + for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p1); + if (take_all || interact (common_box, *e)) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p1); + any_o1 = true; + } } for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p2); + if (take_all || interact (common_box, *e)) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p2); + any_o2 = true; + } + } + + if (! take_all && (! any_o1 || ! any_o2)) { + return; } mp_output->feed_pseudo_edges (m_scanner); - tl_assert (m_edges.size () == vertices (o1) + vertices (o2)); - // temporarily disable intra-polygon check in that step .. we do that later in finish() // if required (#650). bool no_intra = mp_output->different_polygons (); diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index a7e67ce61..cd759cb4f 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -601,7 +601,7 @@ public: * @brief Before the scanner is run, this method must be called to feed additional edges into the scanner * (required for negative edge output - cancellation of perpendicular edges) */ - void feed_pseudo_edges (db::box_scanner &scanner); + bool feed_pseudo_edges (db::box_scanner &scanner); /** * @brief Reimplementation of the box_scanner_receiver interface @@ -641,6 +641,14 @@ public: m_has_negative_edge_output = f; } + /** + * @brief Gets a flag indicating that this class wants negative edge output + */ + bool has_negative_edge_output () const + { + return m_has_negative_edge_output; + } + /** * @brief Sets a flag indicating that this class wants normal edge pair output */ @@ -649,6 +657,14 @@ public: m_has_edge_pair_output = f; } + /** + * @brief Gets a flag indicating that this class wants normal edge pair output + */ + bool has_edge_pair_output () const + { + return m_has_edge_pair_output; + } + /** * @brief Gets the distance value */ @@ -828,9 +844,13 @@ protected: if (layer == 0) { edge2edge_check::put (db::EdgePair (edge, edge.swapped_points ()), false); } +#if 0 + // NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence + // the outer edges to not represent the actual contour. if (layer == 1) { edge2edge_check::put (db::EdgePair (edge.swapped_points (), edge), false); } +#endif } }; diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index 0fba8e9d8..41b05af42 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -1250,6 +1250,20 @@ public: return n; } + /** + * @brief Report the number of shapes stored for a given type mask + */ + size_t size (unsigned int flags) const + { + size_t n = 0; + for (tl::vector::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { + if ((flags & (*l)->type_mask ()) != 0) { + n += (*l)->size (); + } + } + return n; + } + /** * @brief Report the shape count for a certain type */ diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 294a2d9cd..39a212729 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -226,6 +226,12 @@ static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionO return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/); } +static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end) +{ + check_non_null (input, "input"); + return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/); +} + static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e) { check_non_null (input, "input"); @@ -603,6 +609,12 @@ Class decl_CompoundRegionOperationNode ("db", " gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), "@brief Creates a node turning corners into dots (single-point edges).\n" ) + + gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), + "@brief Creates a node turning corners into edge pairs containing the two edges adjacent to the corner.\n" + "The first edge will be the incoming edge and the second one the outgoing edge.\n" + "\n" + "This feature has been introduced in version 0.27.1.\n" + ) + gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0), "@brief Creates a node returning the extents of the objects.\n" "The 'e' parameter provides a generic enlargement which is applied to the boxes. This is helpful to cover dot-like edges or edge pairs in the input." diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 9dc661e15..0b220e499 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -27,6 +27,8 @@ #include "dbEdges.h" #include "dbRegion.h" #include "dbDeepEdgePairs.h" +#include "dbEdgesUtils.h" +#include "dbEdgePairFilters.h" namespace gsi { @@ -176,6 +178,74 @@ static size_t id (const db::EdgePairs *ep) return tl::id_of (ep->delegate ()); } +static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) +{ + db::EdgePairFilterByDistance ef (length, length + 1, inverse); + return r->filtered (ef); +} + +static db::EdgePairs with_distance2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) +{ + db::EdgePairFilterByDistance ef (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); + return r->filtered (ef); +} + +static db::EdgePairs with_length1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) +{ + db::EdgeLengthFilter f (length, length + 1, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_length2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) +{ + db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_length_both1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) +{ + db::EdgeLengthFilter f (length, length + 1, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) +{ + db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse) +{ + db::EdgeOrientationFilter f (a, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse) +{ + db::EdgeOrientationFilter f (a, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + +static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + extern Class decl_dbShapeCollection; Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", @@ -502,6 +572,112 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), + "@brief Filters the edge pairs by length of one of their edges\n" + "Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only " + "edge pairs with at least one edge having the given length are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_length", with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"), + "@brief Filters the edge pairs by length of one of their edges\n" + "Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only " + "edge pairs with at least one edge having a length between min_length and max_length (excluding max_length itself) are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_length_both", with_length_both1, gsi::arg ("length"), gsi::arg ("inverse"), + "@brief Filters the edge pairs by length of both of their edges\n" + "Filters the edge pairs in the edge pair collection by length of both of their edges. If \"inverse\" is false, only " + "edge pairs where both edges have the given length are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_length_both", with_length_both2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"), + "@brief Filters the edge pairs by length of both of their edges\n" + "Filters the edge pairs in the edge pair collection by length of both of their edges. If \"inverse\" is false, only " + "edge pairs with both edges having a length between min_length and max_length (excluding max_length itself) are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_distance", with_distance1, gsi::arg ("distance"), gsi::arg ("inverse"), + "@brief Filters the edge pairs by the distance of the edges\n" + "Filters the edge pairs in the edge pair collection by distance of the edges. If \"inverse\" is false, only " + "edge pairs where both edges have the given distance are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "Distance is measured as the shortest distance between any of the points on the edges.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_distance", with_distance2, gsi::arg ("min_distance"), gsi::arg ("max_distance"), gsi::arg ("inverse"), + "@brief Filters the edge pairs by the distance of the edges\n" + "Filters the edge pairs in the edge pair collection by distance of the edges. If \"inverse\" is false, only " + "edge pairs where both edges have a distance between min_distance and max_distance (max_distance itself is excluded). If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "Distance is measured as the shortest distance between any of the points on the edges.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with at least one edge having the given angle to the x-axis are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "This will filter edge pairs with at least one horizontal edge:\n" + "\n" + "@code\n" + "horizontal = edge_pairs.with_orientation(0, false)\n" + "@/code\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Filter the edge pairs by orientation of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with at least one edge having an angle between min_angle and max_angle are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " + "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of both of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with both edges having the given angle to the x-axis are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "This will filter edge pairs with at least one horizontal edge:\n" + "\n" + "@code\n" + "horizontal = edge_pairs.with_orientation(0, false)\n" + "@/code\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + + method_ext ("with_angle_both", with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Filter the edge pairs by orientation of both of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with both edges having an angle between min_angle and max_angle are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " + "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" + "\n" + "This method has been added in version 0.27.1.\n" + ) + method_ext ("polygons", &polygons1, "@brief Converts the edge pairs to polygons\n" "This method creates polygons from the edge pairs. Each polygon will be a triangle or quadrangle " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 41a6bdf81..d12ef3c21 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -575,10 +575,10 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "edges which have the given angle to the x-axis are returned. If \"inverse\" is true, " "edges not having the given angle are returned.\n" "\n" - "This will filter horizontal edges:\n" + "This will select horizontal edges:\n" "\n" "@code\n" - "horizontal = edges.with_orientation(0, true)\n" + "horizontal = edges.with_orientation(0, false)\n" "@/code\n" ) + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index cd28a2a06..3213c0510 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -394,6 +394,18 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "If errors occur, the device extractor will contain theses errors.\n" ) + + gsi::method ("reset_extracted", &db::LayoutToNetlist::reset_extracted, + "@brief Resets the extracted netlist and enables re-extraction\n" + "This method is implicitly called when using \\connect or \\connect_global after a netlist has been extracted.\n" + "This enables incremental connect with re-extraction.\n" + "\n" + "This method has been introduced in version 0.27.1.\n" + ) + + gsi::method ("is_extracted?", &db::LayoutToNetlist::is_netlist_extracted, + "@brief Gets a value indicating whether the netlist has been extracted\n" + "\n" + "This method has been introduced in version 0.27.1.\n" + ) + gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"), "@brief Defines an intra-layer connection for the given layer.\n" "The layer is either an original layer created with \\make_includelayer and it's variants or\n" diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 052201b7c..1a47fac77 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -843,15 +843,6 @@ public: } } - virtual bool equal (const db::Device &a, const db::Device &b) const - { - if (cb_equal.can_issue ()) { - return cb_equal.issue (&db::EqualDeviceParameters::equal, a, b); - } else { - return db::EqualDeviceParameters::equal (a, b); - } - } - gsi::Callback cb_less, cb_equal; }; @@ -903,13 +894,12 @@ Class decl_dbEqualDeviceParameters ("db", "EqualDevic ); Class decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare", - gsi::callback ("equal", &GenericDeviceParameterCompare::equal, &GenericDeviceParameterCompare::cb_equal, gsi::arg ("device_a"), gsi::arg ("device_b"), - "@brief Compares the parameters of two devices for equality. " - "Returns true, if the parameters of device a and b are considered equal." - ) + gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"), "@brief Compares the parameters of two devices for a begin less than b. " "Returns true, if the parameters of device a are considered less than those of device b." + "The 'less' implementation needs to ensure strict weak ordering. Specifically, less(a,b) == false and less(b,a) implies that a is equal to b and " + "less(a,b) == true implies that less(b,a) is false and vice versa. If not, an internal error " + "will be encountered on netlist compare." ), "@brief A class implementing the comparison of device parameters.\n" "Reimplement this class to provide a custom device parameter compare scheme.\n" @@ -919,7 +909,7 @@ Class decl_GenericDeviceParameterCompare (decl_db "This class is intended for special cases. In most scenarios it is easier to use \\EqualDeviceParameters instead of " "implementing a custom comparer class.\n" "\n" - "This class has been added in version 0.26." + "This class has been added in version 0.26. The 'equal' method has been dropped in 0.27.1 as it can be expressed as !less(a,b) && !less(b,a)." ); static tl::id_type id_of_device_class (const db::DeviceClass *cls) diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 0932f3f01..f25c4f8ed 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -123,6 +123,11 @@ static db::Region corners_to_boxes (const db::Region *r, double angle_start, dou return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim)); } +static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end) +{ + return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end)); +} + static db::Region *new_si (const db::RecursiveShapeIterator &si) { return new db::Region (si); @@ -1373,14 +1378,22 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "A similar function that reports corners as point-like edges is \\corners_dots.\n" "\n" - "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" ) + method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" "\n" "This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n" "\n" - "This function has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + "This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n" + ) + + method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), + "@brief This method will select all corners whose attached edges satisfy the angle condition.\n" + "\n" + "This method is similar to \\corners, but delivers an \\EdgePairs collection with an edge pairs for each corner.\n" + "The first edge is the incoming edge of the corner, the second one the outgoing edge.\n" + "\n" + "This method has been introduced in version 0.27.1.\n" ) + method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge, "@brief Merge the region\n" @@ -2589,7 +2602,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param max_projection The upper limit of the projected length of one edge onto another\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" - "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param negative Negative output from the first input\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2617,9 +2630,15 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\n" + "If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the distance is " + "larger or equal to the limit. This is a way to flag the parts of the first input where the distance to the second " + "input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair " + "contains one edge from the original input and the reverse version of the edge as the second edge.\n" + "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." + "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " + "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" ) + method_ext ("overlap_check", &overlap2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false), "@brief Performs an overlap check with options\n" @@ -2632,7 +2651,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param max_projection The upper limit of the projected length of one edge onto another\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" - "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param negative Negative output from the first input\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2660,9 +2679,15 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\n" + "If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the overlap is " + "larger or equal to the limit. This is a way to flag the parts of the first input where the overlap vs. the second " + "input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair " + "contains one edge from the original input and the reverse version of the edge as the second edge.\n" + "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." + "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " + "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" ) + method_ext ("enclosing_check", &enclosing2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false), "@brief Performs an enclosing check with options\n" @@ -2675,7 +2700,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param max_projection The upper limit of the projected length of one edge onto another\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" - "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param negative Negative output from the first input\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2703,9 +2728,15 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\n" + "If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the enclosure is " + "larger or equal to the limit. This is a way to flag the parts of the first input where the enclosure vs. the second " + "input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair " + "contains one edge from the original input and the reverse version of the edge as the second edge.\n" + "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." + "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " + "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" ) + method_ext ("separation_check", &separation2, gsi::arg ("other"), gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("opposite_filter", db::NoOppositeFilter, "NoOppositeFilter"), gsi::arg ("rect_filter", db::NoSideAllowed, "NoSideAllowed"), gsi::arg ("negative", false), "@brief Performs a separation check with options\n" @@ -2718,7 +2749,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@param max_projection The upper limit of the projected length of one edge onto another\n" "@param opposite_filter Specifies a filter mode for errors happening on opposite sides of inputs shapes\n" "@param rect_filter Specifies an error filter for rectangular input shapes\n" - "@param negative If true, edges not violation the condition will be output as pseudo-edge pairs\n" + "@param negative Negative output from the first input\n" "\n" "If \"whole_edges\" is true, the resulting \\EdgePairs collection will receive the whole " "edges which contribute in the width check.\n" @@ -2746,9 +2777,15 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\"opposite_filter\" specifies whether to require or reject errors happening on opposite sides of a figure. " "\"rect_filter\" allows suppressing specific error configurations on rectangular input figures.\n" "\n" + "If \"negative\" is true, only edges from the first input are output as pseudo edge-pairs where the separation is " + "larger or equal to the limit. This is a way to flag the parts of the first input where the distance to the second " + "input is bigger. Note that only the first input's edges are output. The output is still edge pairs, but each edge pair " + "contains one edge from the original input and the reverse version of the edge as the second edge.\n" + "\n" "Merged semantics applies for the input of this method (see \\merged_semantics= for a description of this concept)\n" "\n" - "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27." + "The 'shielded', 'negative', 'not_opposite' and 'rect_sides' options have been introduced in version 0.27. " + "The interpretation of the 'negative' flag has been restriced to first-layout only output in 0.27.1.\n" ) + method_ext ("area", &area1, "@brief The area of the region\n" diff --git a/src/db/unit_tests/dbEdgePairTests.cc b/src/db/unit_tests/dbEdgePairTests.cc index cd24fc074..9a27fa8f0 100644 --- a/src/db/unit_tests/dbEdgePairTests.cc +++ b/src/db/unit_tests/dbEdgePairTests.cc @@ -202,3 +202,17 @@ TEST(3_symmetric) eh.insert (db::EdgePair (e2, e1, true)); EXPECT_EQ (int (eh.size ()), 1); } + +TEST(4_distance) +{ + db::Edge e1 (db::Point (0, 0), db::Point (0, 100)); + db::Edge e2 (db::Point (200, 100), db::Point (200, 0)); + db::Edge e3 (db::Point (0, 0), db::Point (100, 0)); + db::Edge e4 (db::Point (200, 0), db::Point (300, 0)); + db::Edge e5 (db::Point (200, 100), db::Point (300, 100)); + + EXPECT_EQ (db::EdgePair (e1, e1).distance (), 0); + EXPECT_EQ (db::EdgePair (e1, e2).distance (), 200); + EXPECT_EQ (db::EdgePair (e3, e2).distance (), 100); + EXPECT_EQ (db::EdgePair (e3, e5).distance (), 141); +} diff --git a/src/db/unit_tests/dbEdgeTests.cc b/src/db/unit_tests/dbEdgeTests.cc index a64880a86..d47932ced 100644 --- a/src/db/unit_tests/dbEdgeTests.cc +++ b/src/db/unit_tests/dbEdgeTests.cc @@ -106,6 +106,17 @@ TEST(2) EXPECT_EQ (db::Edge (10,20,110,222).distance_abs (db::Point (100, 200)), db::Edge::distance_type (1)); EXPECT_EQ (db::Edge (10,20,110,222).contains (db::Point (0, 0)), false); EXPECT_EQ (db::Edge (10,20,110,222).contains (db::Point (100, 200)), false); + + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (100, 120)), 100); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (100, -80)), 100); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (-90, 120)), 141); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (-90, -80)), 141); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (210, 120)), 141); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (210, -80)), 141); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (-90, 20)), 100); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (10, 20)), 0); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (50, 20)), 0); + EXPECT_EQ (db::Edge (10,20,110,20).euclidian_distance (db::Point (110, 20)), 0); } TEST(3) diff --git a/src/drc/drc/built-in-macros/_drc_complex_ops.rb b/src/drc/drc/built-in-macros/_drc_complex_ops.rb index 6e24ce748..e0d2ea017 100644 --- a/src/drc/drc/built-in-macros/_drc_complex_ops.rb +++ b/src/drc/drc/built-in-macros/_drc_complex_ops.rb @@ -787,14 +787,14 @@ CODE # The "corners" method is available as a plain function or as a method on \DRC# expressions. # The plain function is equivalent to "primary.corners". - def corners(as_dots = DRCAsDots::new(false)) + def corners(output_mode = DRCOutputMode::new(:dots)) @engine._context("corners") do - if as_dots.is_a?(DRCAsDots) - as_dots = as_dots.value + if output_mode.is_a?(DRCOutputMode) + output_mode = output_mode.value else raise("Invalid argument (#{as_dots.inspect}) for 'corners' method") end - DRCOpNodeCornersFilter::new(@engine, as_dots, self) + DRCOpNodeCornersFilter::new(@engine, output_mode, self) end end @@ -859,8 +859,8 @@ CODE args.each_with_index do |a,ia| if a.is_a?(1.0.class) && :#{f} != :middle f << a - elsif a.is_a?(DRCAsDots) - as_edges = a.value + elsif a.is_a?(DRCOutputMode) + as_edges = (a.value == :edges || a.value == :dots) elsif @@std_refs[a] && :#{f} != :middle f = @@std_refs[a] else @@ -1996,11 +1996,11 @@ end class DRCOpNodeCornersFilter < DRCOpNodeWithCompare attr_accessor :input - attr_accessor :as_dots + attr_accessor :output_mode - def initialize(engine, as_dots, input) + def initialize(engine, output_mode, input) super(engine) - self.as_dots = as_dots + self.output_mode = output_mode self.input = input self.description = "corners" end @@ -2011,8 +2011,10 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare args << (self.gt ? false : true) args << (self.lt ? self.lt : (self.le ? self.le : 180.0)) args << (self.lt ? false : true) - if self.as_dots + if self.output_mode == :dots || self.output_mode == :edges RBA::CompoundRegionOperationNode::new_corners_as_dots(*args) + elsif self.output_mode == :edge_pairs + RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args) else args << 2 # dimension is 2x2 DBU RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args) diff --git a/src/drc/drc/built-in-macros/_drc_cop_integration.rb b/src/drc/drc/built-in-macros/_drc_cop_integration.rb index 2a7566ff9..58b5518a4 100644 --- a/src/drc/drc/built-in-macros/_drc_cop_integration.rb +++ b/src/drc/drc/built-in-macros/_drc_cop_integration.rb @@ -781,7 +781,7 @@ CODE # %DRC% # @name corners - # @brief Selects all polygons which are rectilinear + # @brief Selects corners of polygons # @synopsis corners([ options ]) (in condition) # @synopsis corners(layer [, options ]) # @@ -791,15 +791,16 @@ CODE # \DRC# expressions (see \Layer#drc and \DRC#corners for more details). # # Like the layer-based version, the "corners" operator accepts the - # output type option: "as_dots" for dot-like edges and "as_boxes" for - # small (2x2 DBU) box markers. + # output type option: "as_dots" for dot-like edges, "as_boxes" for + # small (2x2 DBU) box markers and "as_edge_pairs" for edge pairs. + # The default output type is "as_boxes". # # The "corners" operator can be put into a condition which means it's # applied to corners meeting a particular angle constraint. - def _cop_corners(as_dots = DRCAsDots::new(false)) + def _cop_corners(output_mode = DRCOutputMode::new(:boxes)) # NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here. - return primary.corners(as_dots) + return primary.corners(output_mode) end # %DRC% @@ -1024,7 +1025,7 @@ CODE # # With a lower and upper limit, the results are edges marking the positions on the # primary shape where the condition is met. - # With a lower limit alone, these markers are formed by two, identical but opposite edges attached to + # With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to # the primary shape. Without an upper limit only, the first edge of the marker is attached to the # primary shape while the second edge is attached to the shape of the "other" layer. # @@ -1034,6 +1035,12 @@ CODE # @td @img(/images/drc_enc2u.png) @/td # @/tr # @/table + # + # When "larger than" constraints are used, this function will produce the edges from the + # first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from + # the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the + # actual edges from the first input (see \separation for an example). + # # %DRC% # @name separation @@ -1048,7 +1055,7 @@ CODE # as metrics, projection and angle constraints etc. This check also features # opposite and rectangle filtering. See \Layer#separation for details about opposite and # rectangle error filtering. - # + # # @h3 Classic mode @/h3 # # Like \enclosing, this function is available as a classic DRC function with a layer as the first @@ -1081,6 +1088,23 @@ CODE # @td @img(/images/drc_separation1u.png) @/td # @/tr # @/table + # + # When "larger than" constraints are used, this function will produce the edges from the + # first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from + # the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the + # actual edges from the first input: + # + # @code + # l1_edges_without_l2 = l1.drc((separation(l2) >= 1.0).first_edges) + # @/code + # + # The following image shows the effect of such a negative-output separation check: + # + # @table + # @tr + # @td @img(/images/drc_separation1un.png) @/td + # @/tr + # @/table # %DRC% # @name overlap @@ -1124,6 +1148,12 @@ CODE # @td @img(/images/drc_overlap2u.png) @/td # @/tr # @/table + # + # When "larger than" constraints are used, this function will produce the edges from the + # first layer only. The result will still be edge pairs for consistency, but each edge pair holds one edge from + # the original polygon plus a reverse copy of that edge in the second member. Use "first_edges" to extract the + # actual edges from the first input (see \separation for an example). + # # %DRC% # @name width @@ -1180,7 +1210,7 @@ CODE # # With a lower and upper limit or with the "equal" condition, the results are edges marking the positions on the # primary shape where the condition is met. - # With a lower limit alone, these markers are formed by two, identical but opposite edges attached to + # With a lower limit alone, the results are edge pairs which are formed by two identical, but opposite edges attached to # the primary shape. Without an upper limit only, both edges are attached to different sides of the primary # shape. # diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 387664b40..51f44c266 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -64,6 +64,10 @@ module DRC end + def both + DRCBothEdges::new + end + def shift(x, y) self._context("shift") do RBA::DCplxTrans::new(RBA::DVector::new(_make_value(x) * self.dbu, _make_value(y) * self.dbu)) @@ -98,6 +102,14 @@ module DRC DRCJoinFlag::new(true) end + def padding_zero + DRCDensityPadding::new(:zero) + end + + def padding_ignore + DRCDensityPadding::new(:ignore) + end + def diamond_limit DRCSizingMode::new(0) end @@ -214,15 +226,19 @@ module DRC end def as_dots - DRCAsDots::new(true) + DRCOutputMode::new(:dots) end def as_edges - DRCAsDots::new(true) + DRCOutputMode::new(:edges) end def as_boxes - DRCAsDots::new(false) + DRCOutputMode::new(:boxes) + end + + def as_edge_pairs + DRCOutputMode::new(:edge_pairs) end def area_only(r) @@ -1793,6 +1809,7 @@ CODE with_bbox_min with_bbox_width with_length + with_distance without_angle without_area without_area_ratio diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index fa0735ba2..041ad60c5 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -276,16 +276,37 @@ module DRC end # %DRC% - # @name size + # @name count # @brief Returns the number of objects on the layer - # @synopsis layer.size + # @synopsis layer.count # - # The number of objects is the number of raw objects, not merged - # regions or edges. It is more efficent to call this method on output layers than - # on input layers. + # The count is the number of raw objects, not merged + # regions or edges. This is the flat count - the number of polygons, + # edges or edge pairs seen from the top cell. + # "count" can be computationally expensive for original layers with + # clip regions or cell tree filters. + # + # See \hier_count for a hierarchical (each cell counts once) count. - def size - self.data.size + def count + self.data.count + end + + # %DRC% + # @name hier_count + # @brief Returns the hierarchical number of objects on the layer + # @synopsis layer.hier_count + # + # The hier_count is the number of raw objects, not merged + # regions or edges, with each cell counting once. + # A high \count to hier_count (flat to hierarchical) ratio is an indication + # of a good hierarchical compression. + # "hier_count" applies only to original layers without clip regions or + # cell filters and to layers in \deep mode. Otherwise, hier_count gives + # the same value than \count. + + def hier_count + self.data.hier_count end # %DRC% @@ -678,13 +699,28 @@ CODE # @synopsis layer.with_length(min .. max) # @synopsis layer.with_length(value) # @synopsis layer.with_length(min, max) - # The method selects edges by their length. The first version selected + # @synopsis edge_pairlayer.with_length(min, max [, both]) + # The method selects edges by their length. The first version selects # edges with a length larger or equal to min and less than max (but not equal). # The second version selects edges with exactly the given length. The third # version is similar to the first one, but allows specification of nil for min or # max indicating that there is no lower or upper limit. # - # This method is available for edge layers only. + # This method is available for edge and edge pair layers. + # + # When called on an edge pair layer, this method will select edge pairs if one + # or both of the edges meet the length criterion. Use the additional argument + # and pass "both" (plain word) to specify that both edges need to be within the given interval. + # By default, it's sufficient for one edge to meet the criterion. + # + # Here are examples for "with_length" on edge pair layers: + # + # @code + # # at least one edge needs to have a length of 1.0 <= l < 2.0 + # ep1 = edge_pairs.with_length(1.um .. 2.um) + # # both edges need to have a length of exactly 2 um + # ep2 = edge_pairs.with_length(2.um, both) + # @/code # %DRC% # @name without_length @@ -692,11 +728,21 @@ CODE # @synopsis layer.without_length(min .. max) # @synopsis layer.without_length(value) # @synopsis layer.without_length(min, max) + # @synopsis edge_pairlayer.with_length(min, max [, both]) # The method basically is the inverse of \with_length. It selects all edges # of the edge layer which do not have the given length (second form) or are # not inside the given interval (first and third form). # - # This method is available for edge layers only. + # This method is available for edge and edge pair layers. + # + # A note on the "both" modifier (without_length called on edge pairs): "both" means that + # both edges need to be "without_length". For example + # + # @code + # # both edges are not exactly 1 um in length, or: + # # the edge pair is skipped if one edge has a length of exactly 1 um + # ep = edge_pairs.without_length(1.um, both) + # @/code %w(length).each do |f| [true, false].each do |inv| @@ -706,18 +752,96 @@ CODE @engine._context("#{mn}") do - requires_edges + requires_edges_or_edge_pairs + + result_class = self.data.class + + f = :with_#{f} + args = args.select do |a| + if a.is_a?(DRCBothEdges) + if !self.data.is_a?(RBA::EdgePairs) + raise("'both' keyword only available for edge pair layers") + end + f = :with_#{f}_both + false + else + true + end + end + if args.size == 1 a = args[0] if a.is_a?(Range) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect})) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect})) else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value(a), #{inv.inspect})) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value(a), #{inv.inspect})) end elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Edges, :with_#{f}, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect})) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect})) else - raise("Invalid number of arguments (1 or 2 expected)") + raise("Invalid number of range arguments (1 or 2 expected)") + end + + end + + end +CODE + end + end + + # %DRC% + # @name with_distance + # @brief Selects edge pairs by the distance of the edges + # @synopsis layer.with_distance(min .. max) + # @synopsis layer.with_distance(value) + # @synopsis layer.with_distance(min, max) + # The method selects edge pairs by the distance of their edges. The first version selects + # edge pairs with a distance larger or equal to min and less than max (but not equal). + # The second version selects edge pairs with exactly the given distance. The third + # version is similar to the first one, but allows specification of nil for min or + # max indicating that there is no lower or upper limit. + # + # The distance of the edges is defined by the minimum distance of all points from the + # edges involved. For edge pairs generated in geometrical checks this is equivalent + # to the actual distance of the original edges. + # + # This method is available for edge pair layers only. + + # %DRC% + # @name without_distance + # @brief Selects edge pairs by the distance of the edges + # @synopsis layer.without_distance(min .. max) + # @synopsis layer.without_distance(value) + # @synopsis layer.without_distance(min, max) + # The method basically is the inverse of \with_distance. It selects all edge pairs + # of the edge pair layer which do not have the given distance (second form) or are + # not inside the given interval (first and third form). + # + # This method is available for edge pair layers only. + + %w(distance).each do |f| + [true, false].each do |inv| + mn = (inv ? "without" : "with") + "_" + f + eval <<"CODE" + def #{mn}(*args) + + @engine._context("#{mn}") do + + requires_edge_pairs + + result_class = RBA::EdgePairs + + if args.size == 1 + a = args[0] + if a.is_a?(Range) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value_with_nil(a.begin), @engine._make_value_with_nil(a.end), #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value(a), #{inv.inspect})) + end + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_#{f}, @engine._make_value_with_nil(args[0]), @engine._make_value_with_nil(args[1]), #{inv.inspect})) + else + raise("Invalid number of range arguments (1 or 2 expected)") end end @@ -733,6 +857,7 @@ CODE # @synopsis layer.with_angle(min .. max) # @synopsis layer.with_angle(value) # @synopsis layer.with_angle(min, max) + # @synopsis edge_pairlayer.with_angle(min, max [, both]) # # When called on an edge layer, the method selects edges by their angle, # measured against the horizontal axis in the mathematical sense. @@ -750,6 +875,20 @@ CODE # given angle or is within the given angle interval. The angle is measured between the edges forming the corner. # For each corner, an edge pair containing the edges forming in the angle is returned. # + # When called on an edge pair layer, this method selects edge pairs with one or both edges + # meeting the angle criterion. In this case an additional argument is accepted which can be + # either "both" (plain word) to indicate that both edges have to be within the given interval. + # Without this argument, it is sufficient for one edge to meet the criterion. + # + # Here are examples for "with_angle" on edge pair layers: + # + # @code + # # at least one edge needs to be horizontal + # ep1 = edge_pairs.with_angle(0) + # # both edges need to vertical + # ep2 = edge_pairs.with_angle(90, both) + # @/code + # # A method delivering all objects not matching the angle criterion is \without_angle. # # The following images demonstrate some use cases of \with_angle and \without_angle: @@ -771,37 +910,62 @@ CODE # @synopsis layer.without_angle(min .. max) # @synopsis layer.without_angle(value) # @synopsis layer.without_angle(min, max) + # @synopsis edge_pairlayer.without_angle(min, max [, both]) # # The method basically is the inverse of \with_angle. It selects all edges # of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle # is not inside the given interval (first and third form). + # + # A note on the "both" modifier (without_angle called on edge pairs): "both" means that + # both edges need to be "without_angle". For example + # + # @code + # # both edges are not horizontal or: + # # the edge pair is skipped if one edge is horizontal + # ep = edge_pairs.without_angle(0, both) + # @/code + # - [true, false].each do |inv| - mn = (inv ? "without" : "with") + "_angle" - eval <<"CODE" - def #{mn}(*args) + %w(angle).each do |f| + [true, false].each do |inv| + mn = (inv ? "without" : "with") + "_" + f + eval <<"CODE" + def #{mn}(*args) - @engine._context("#{mn}") do + @engine._context("#{mn}") do - requires_edges_or_region - result_class = self.data.is_a?(RBA::Region) ? RBA::EdgePairs : RBA::Edges - if args.size == 1 - a = args[0] - if a.is_a?(Range) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a.begin, a.end, #{inv.inspect})) - else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, a, #{inv.inspect})) + f = :with_#{f} + args = args.select do |a| + if a.is_a?(DRCBothEdges) + if !self.data.is_a?(RBA::EdgePairs) + raise("'both' keyword only available for edge pair layers") + end + f = :with_#{f}_both + false + else + true + end end - elsif args.size == 2 - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, :with_angle, args[0], args[1], #{inv.inspect})) - else - raise("Invalid number of arguments (1 or 2 expected)") + + result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs + if args.size == 1 + a = args[0] + if a.is_a?(Range) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect})) + end + elsif args.size == 2 + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, args[0], args[1], #{inv.inspect})) + else + raise("Invalid number of range arguments (1 or 2 expected)") + end + end end - - end CODE + end end # %DRC% @@ -954,8 +1118,8 @@ CODE elsif a.is_a?(DRCPattern) as_pattern = a.as_pattern pattern = a.pattern - elsif a.is_a?(DRCAsDots) - as_dots = a.value + elsif a.is_a?(DRCOutputMode) + as_dots = (a.value == :edges || a.value == :dots) else raise("Invalid argument type #{a.inspect}") end @@ -1003,6 +1167,8 @@ CODE # @ul # @li @b as_boxes @/b: with this option, small boxes will be produced as markers @/li # @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li + # @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge + # is the incoming edge to the corner, the second edge the outgoing edge. @/li # @/ul # # The following images show the effect of this method: @@ -1021,7 +1187,7 @@ CODE requires_region - as_dots = false + output_mode = :boxes amin = -180.0 amax = 180.0 @@ -1035,14 +1201,23 @@ CODE elsif a.is_a?(1.0.class) || a.is_a?(1.class) amin = a.to_f amax = a.to_f - elsif a.is_a?(DRCAsDots) - as_dots = a.value + elsif a.is_a?(DRCOutputMode) + output_mode = a.value else raise("Invalid argument #{a.inspect}") end end - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, as_dots ? :corners_dots : :corners, amin, amax)) + f = :corners + cls = RBA::Region + if output_mode == :edges || output_mode == :dots + f = :corners_dots + cls = RBA::Edges + elsif output_mode == :edge_pairs + f = :corners_edge_pairs + cls = RBA::EdgePairs + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax)) end @@ -1211,8 +1386,8 @@ CODE args.each do |a| if a.is_a?(1.0.class) && :#{f} != :middle f << a - elsif a.is_a?(DRCAsDots) - as_edges = a.value + elsif a.is_a?(DRCOutputMode) + as_edges = (a.value == :edges || a.value == :dots) elsif @@std_refs[a] && :#{f} != :middle f = @@std_refs[a] else @@ -1335,7 +1510,7 @@ CODE # %DRC% # @name collect_to_region # @brief Transforms a layer into polygon objects - # @synopsis layer.collect { |object| ... } + # @synopsis layer.collect_to_region { |object| ... } # This method is similar to \collect, but creates a polygon layer. It expects the block to # deliver objects that can be converted into polygons. Such objects are of class RBA::DPolygon, # RBA::DBox, RBA::DPath, RBA::Polygon, RBA::Path, RBA::Box and RBA::Region. @@ -1343,7 +1518,7 @@ CODE # %DRC% # @name collect_to_edges # @brief Transforms a layer into edge objects - # @synopsis layer.collect { |object| ... } + # @synopsis layer.collect_to_edges { |object| ... } # This method is similar to \collect, but creates an edge layer. It expects the block to # deliver objects that can be converted into edges. If polygon-like objects are returned, their # contours will be turned into edge sequences. @@ -1351,7 +1526,7 @@ CODE # %DRC% # @name collect_to_edge_pairs # @brief Transforms a layer into edge pair objects - # @synopsis layer.collect { |object| ... } + # @synopsis layer.collect_to_edge_pairs { |object| ... } # This method is similar to \collect, but creates an edge pair layer. It expects the block to # deliver RBA::EdgePair, RBA::DEdgePair or RBA::EdgePairs objects. @@ -1613,6 +1788,9 @@ CODE # @td @img(/images/drc_or2.png) @/td # @/tr # @/table + # + # In deep mode, "or" or "|" does not imply merging. In deep mode, + # "or" is an alias for "+" ("add"). def or(other) @engine._context("or") do @@ -3344,15 +3522,15 @@ CODE # %DRC% # @name isolated - # @brief An isolation check + # @brief An inter-polygon isolation check # @synopsis layer.isolated(value [, options]) # @synopsis layer.iso(value [, options]) # # @b Note: @/b "isolated" and "iso" are available as operators for the "universal DRC" function \Layer#drc within # the \DRC framework. These variants have more options and are more intuitive to use. See \global#isolated for more details. # - # See \space for a description of this method. - # In contrast to \space, this + # See \space for a description of this method. "isolated" is the space check variant which checks different polygons only. + # In contrast to \space, the "isolated" # method is available for polygon layers only, since only on such layers # different polygons can be identified. # @@ -3368,14 +3546,15 @@ CODE # %DRC% # @name notch - # @brief An intra-region spacing check + # @brief An intra-polygon spacing check # @synopsis layer.notch(value [, options]) # # @b Note: @/b "notch" is available as an operator for the "universal DRC" function \Layer#drc within # the \DRC framework. This variant has more options and is more intuitive to use. See \global#notch for more details. # - # See \space for a description of this method. - # In contrast to \space, this + # See \space for a description of this method. + # "notch" is the space check variant which finds space violations within a single polygon, but not against other polygons. + # In contrast to \space, the "notch" # method is available for polygon layers only, since only on such layers # different polygons can be identified. Also, opposite and rectangle error # filtering is not available for this method. @@ -3729,6 +3908,20 @@ CODE # # (100 and 150 tiles of 20 um each are used in horizontal and vertical direction): # low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), tile_origin(0.0, 0.0), tile_count(100, 150)) # @/code + # + # The "padding mode" indicates how the area outside the layout's bounding box is considered. + # There are two modes: + # + # @ul + # @li @b padding_zero @/b: the outside area is considered zero density. This is the default mode. @/li + # @li @b padding_ignore @/b: the outside area is ignored for the density computation. @/li + # @/ul + # + # Example: + # + # @code + # low_density = input(1, 0).density(0.0 .. 0.1, tile_size(20.um), padding_ignore) + # @/code # # The complementary version of "with_density" is \without_density. @@ -3752,6 +3945,7 @@ CODE tile_origin = nil tile_count = nil tile_boundary = nil + padding_mode = :zero n = 1 args.each do |a| @@ -3765,6 +3959,8 @@ CODE tile_count = a.get elsif a.is_a?(DRCTileBoundary) tile_boundary = a.get + elsif a.is_a?(DRCDensityPadding) + padding_mode = a.value elsif a.is_a?(Float) || a.is_a?(1.class) || a == nil nlimits < 2 || raise("Too many values specified") limits[nlimits] = @engine._make_numeric_value_with_nil(a) @@ -3809,18 +4005,34 @@ CODE tp.threads = (@engine.threads || 1) if tile_boundary tp.input("boundary", tile_boundary.data) + else + tp.input("boundary", RBA::Region::new(self.data.bbox)) end tp.var("vmin", limits[0] || 0.0) tp.var("vmax", limits[1] || 1.0) tp.var("inverse", inverse) - tp.queue(<<"TP_SCRIPT") - _tile && ( - var bx = _tile.bbox.enlarged(xoverlap, yoverlap); - var d = to_f(input.area(bx)) / to_f(bx.area); - ((d > vmin - 1e-10 && d < vmax + 1e-10) != inverse) && _output(res, bx, false) - ) + + if padding_mode == :zero + tp.queue(<<"TP_SCRIPT") + _tile && ( + var bx = _tile.bbox.enlarged(xoverlap, yoverlap); + var d = to_f(input.area(bx)) / to_f(bx.area); + ((d > vmin - 1e-10 && d < vmax + 1e-10) != inverse) && _output(res, bx, false) + ) TP_SCRIPT + elsif padding_mode == :ignore + tp.queue(<<"TP_SCRIPT") + _tile && ( + var bx = _tile.bbox.enlarged(xoverlap, yoverlap); + var ba = boundary.area(bx); + ba > 0 && ( + var d = to_f(input.area(bx)) / to_f(ba); + ((d > vmin - 1e-10 && d < vmax + 1e-10) != inverse) && _output(res, bx, false) + ) + ) +TP_SCRIPT + end @engine.run_timed("\"#{method}\" in: #{@engine.src_line}", self.data) do tp.execute("Tiled \"#{method}\" in: #{@engine.src_line}") @@ -4535,13 +4747,17 @@ END end def requires_edge_pairs - self.data.is_a?(RBA::EdgePairs) || raise("Requires a edge pair layer") + self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge pair layer") end def requires_edges self.data.is_a?(RBA::Edges) || raise("Requires an edge layer") end + def requires_edges_or_edge_pairs + self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge or edge pair layer") + end + def requires_edges_or_region self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("Requires an edge or polygon layer") end diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 05b7fb6c2..978e6c723 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -64,7 +64,6 @@ module DRC def initialize(engine) @engine = engine - @netlisted = false @connect_implicit = [] @connect_implicit_per_cell = {} @connect_explicit = [] @@ -240,7 +239,6 @@ module DRC # See \connect for more details. def clear_connections - @netlisted = false @connect_implicit = [] @connect_implicit_per_cell = {} @connect_explicit = [] @@ -387,6 +385,25 @@ module DRC # errors = antenna_check(gate, metal1, 50.0) # @/code # + # Usually antenna checks apply to multiple metal layers. In this case, + # the connectivity needs to be extended after the first check to include + # the next metal layers. This can be achieved with incremental connects: + # + # @code + # # provide connections up to metal1 + # connect(gate, poly) + # connect(poly, contact) + # connect(contact, metal1) + # metal1_errors = antenna_check(gate, metal1, 50.0) + # + # # now *add* connections up to metal2 + # connect(metal1, via1) + # connect(via1, metal2) + # metal2_errors = antenna_check(gate, metal2, 50.0) + # + # ... continue this scheme with further metal layers ... + # @/code + # # Plasma induced damage can be rectified by including diodes # which create a safe current path for discharging the metal # islands. Such diodes can be identified with a recognition layer @@ -544,7 +561,7 @@ module DRC ensure_data # run extraction in a timed environment - if ! @netlisted + if ! @l2n.is_extracted? # configure implicit net connections @l2n.clear_join_net_names @@ -569,7 +586,6 @@ module DRC end @engine._cmd(@l2n, :extract_netlist) - @netlisted = true end @@ -609,13 +625,13 @@ module DRC end def _l2n_data - @netlisted && self.l2n_data + @l2n && @l2n.is_extracted? && self.l2n_data end private def cleanup - @netlisted && clear_connections + @l2n && @l2n.is_extracted? && clear_connections end def ensure_data @@ -644,14 +660,13 @@ module DRC def register_layer(data) id = data.data_id + ensure_data if @layers && @layers[id] # already registered return end - ensure_data - @layers[id] = data @lnum += 1 diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 0da810678..dedd797ef 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -16,6 +16,18 @@ module DRC end end + # A wrapper for the "both edges" flag for EdgePair#with_length or EdgePair#with_angle + class DRCBothEdges + end + + # A wrapper for the padding modes of with_density + class DRCDensityPadding + attr_accessor :value + def initialize(v) + self.value = v + end + end + # A wrapper for the sizing mode value class DRCSizingMode attr_accessor :value @@ -65,7 +77,7 @@ module DRC # A wrapper for the "as_dots" or "as_boxes" flag for # some DRC functions. The purpose of this class # is to identify the value by the class. - class DRCAsDots + class DRCOutputMode attr_accessor :value def initialize(v) self.value = v diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 15b6d54d4..11b7e6413 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -225,6 +225,46 @@ TEST(5_FlatAntenna) db::compare_layouts (_this, layout, au, db::NoNormalization); } +TEST(5_FlatAntennaIncremental) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_5i.drc"; + + std::string input = tl::testdata (); + input += "/drc/antenna_l1.gds"; + + std::string au = tl::testdata (); + au += "/drc/drcSimpleTests_au5.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + TEST(6_HierarchicalAntenna) { std::string rs = tl::testdata (); @@ -1232,3 +1272,23 @@ TEST(47_fillWithOverlappingBoxesTiled) { run_test (_this, "47", false); } + +TEST(48_drcWithFragments) +{ + run_test (_this, "48", false); +} + +TEST(48d_drcWithFragments) +{ + run_test (_this, "48", true); +} + +TEST(49_drcWithFragments) +{ + run_test (_this, "49", false); +} + +TEST(49d_drcWithFragments) +{ + run_test (_this, "49", true); +} diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index d383f6227..ef4aa3e2c 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -342,7 +342,7 @@ See Netter#connect_global

See Netter#connect_implicit for a description of that function.

-

"corners" - Selects all polygons which are rectilinear

+

"corners" - Selects corners of polygons

Usage:

The method basically is the inverse of with_angle. It selects all edges of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle is not inside the given interval (first and third form). +

+A note on the "both" modifier (without_angle called on edge pairs): "both" means that +both edges need to be "without_angle". For example +

+

+# both edges are not horizontal or:
+# the edge pair is skipped if one edge is horizontal
+ep = edge_pairs.without_angle(0, both)
+

"without_area" - Selects polygons by area

@@ -3507,6 +3623,21 @@ This method is available for polygon layers only. For details about the operations and the operation see
with_density. This version will return the tiles where the density is not within the given range.

+

"without_distance" - Selects edge pairs by the distance of the edges

+ +

Usage:

+
    +
  • layer.without_distance(min .. max)
  • +
  • layer.without_distance(value)
  • +
  • layer.without_distance(min, max)
  • +
+

+The method basically is the inverse of with_distance. It selects all edge pairs +of the edge pair layer which do not have the given distance (second form) or are +not inside the given interval (first and third form). +

+This method is available for edge pair layers only. +

"without_holes" - Selects all polygons with the specified number of holes

Usage:

@@ -3526,13 +3657,23 @@ which do not have the specified number of holes.
  • layer.without_length(min .. max)
  • layer.without_length(value)
  • layer.without_length(min, max)
  • +
  • edge_pairlayer.with_length(min, max [, both])
  • The method basically is the inverse of with_length. It selects all edges of the edge layer which do not have the given length (second form) or are not inside the given interval (first and third form).

    -This method is available for edge layers only. +This method is available for edge and edge pair layers. +

    +A note on the "both" modifier (without_length called on edge pairs): "both" means that +both edges need to be "without_length". For example +

    +

    +# both edges are not exactly 1 um in length, or:
    +# the edge pair is skipped if one edge has a length of exactly 1 um
    +ep = edge_pairs.without_length(1.um, both)
    +

    "without_perimeter" - Selects polygons by perimeter

    diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml index 25d84a583..8e8cf429d 100644 --- a/src/lay/lay/doc/about/drc_ref_netter.xml +++ b/src/lay/lay/doc/about/drc_ref_netter.xml @@ -99,6 +99,25 @@ connect(contact, metal1) errors = antenna_check(gate, metal1, 50.0)

    +Usually antenna checks apply to multiple metal layers. In this case, +the connectivity needs to be extended after the first check to include +the next metal layers. This can be achieved with incremental connects: +

    +

    +# provide connections up to metal1
    +connect(gate, poly)
    +connect(poly, contact)
    +connect(contact, metal1)
    +metal1_errors = antenna_check(gate, metal1, 50.0)
    +
    +# now *add* connections up to metal2
    +connect(metal1, via1)
    +connect(via1, metal2)
    +metal2_errors = antenna_check(gate, metal2, 50.0)
    +
    +... continue this scheme with further metal layers ...
    +
    +

    Plasma induced damage can be rectified by including diodes which create a safe current path for discharging the metal islands. Such diodes can be identified with a recognition layer diff --git a/src/lay/lay/doc/images/drc_separation1un.png b/src/lay/lay/doc/images/drc_separation1un.png new file mode 100644 index 000000000..26a5fbd91 Binary files /dev/null and b/src/lay/lay/doc/images/drc_separation1un.png differ diff --git a/src/lay/lay/layDRCLVSHelpResources.qrc b/src/lay/lay/layDRCLVSHelpResources.qrc index cdc914da3..d11b89b5e 100644 --- a/src/lay/lay/layDRCLVSHelpResources.qrc +++ b/src/lay/lay/layDRCLVSHelpResources.qrc @@ -18,6 +18,7 @@ doc/images/drc_space3u.png doc/images/drc_separation1.png doc/images/drc_separation1u.png + doc/images/drc_separation1un.png doc/images/drc_separation2.png doc/images/drc_separation3.png doc/images/drc_separation4.png diff --git a/src/lay/lay/layLogViewerDialog.cc b/src/lay/lay/layLogViewerDialog.cc index c5e3abe33..2ae7fc61b 100644 --- a/src/lay/lay/layLogViewerDialog.cc +++ b/src/lay/lay/layLogViewerDialog.cc @@ -120,9 +120,8 @@ LogFile::LogFile (size_t max_entries, bool register_global) { connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ())); - m_timer.setSingleShot (false); - m_timer.setInterval (100); - m_timer.start (); + m_timer.setSingleShot (true); + m_timer.setInterval (0); if (register_global) { tl::info.add (&m_info_receiver, false); @@ -253,30 +252,10 @@ LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continu void LogFile::yield () { -#if 0 - // This looked like a good idea, but in fact it introduces a hell lot of instability - // as it potentially leads to a recursion of events inside innocent functions. Remember - // that log output may be generated from every function called in response of an event - // and not every such function may process further events - - bool can_yield = false; - - { - QMutexLocker locker (&m_lock); - - if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread () && (tl::Clock::current () - m_last_yield).seconds () > 0.1) { - m_last_yield = tl::Clock::current (); - can_yield = true; - } + // will update on next processEvents + if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread ()) { + m_timer.start (); } - - // use this opportunity to process events - // NOTE: as process events may trigger further log output, it's necessary to do process events outside any other - // method (e.g. add) which is subject to locking. Hence we avoid deadlocks. - if (can_yield) { - lay::ApplicationBase::instance ()->process_events (QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers, true /*silent*/); - } -#endif } int diff --git a/src/lay/lay/layLogViewerDialog.h b/src/lay/lay/layLogViewerDialog.h index a449c7c08..55c9737a6 100644 --- a/src/lay/lay/layLogViewerDialog.h +++ b/src/lay/lay/layLogViewerDialog.h @@ -26,7 +26,6 @@ #include "ui_LogViewerDialog.h" #include "tlLog.h" -#include "tlTimer.h" #include "layCommon.h" #include @@ -233,7 +232,6 @@ private: size_t m_last_generation_id; bool m_has_errors, m_has_warnings; bool m_last_attn; - tl::Clock m_last_yield; /** * @brief Adds an error diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index 48e64cede..6d054e461 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -206,3 +206,8 @@ TEST(23_issue709) run_test (_this, "empty_subcells", "empty_subcells.gds"); } +// empty gds +TEST(24_issue806) +{ + run_test (_this, "custom_compare", "custom_compare.gds"); +} diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index c7aea7d6b..50fb183c5 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -152,7 +152,7 @@ TEST(16_private) TEST(17_private) { test_is_long_runner (); - run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17.lvsdb"); + run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17b.lvsdb"); } TEST(18_private) @@ -170,6 +170,6 @@ TEST(19_private) TEST(20_private) { // test_is_long_runner (); - run_test (_this, "test_20.lylvs", "test_20.cir.gz", "test_20.gds.gz", true, "test_20.lvsdb"); + run_test (_this, "test_20.lylvs", "test_20.cir.gz", "test_20.gds.gz", true, "test_20b.lvsdb"); } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc index cb1db602d..21f25c7e1 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2.cc @@ -74,6 +74,7 @@ class GDS2FormatDeclaration tl::make_member (&db::GDS2WriterOptions::write_file_properties, "write-file-properties") + tl::make_member (&db::GDS2WriterOptions::no_zero_length_paths, "no-zero-length-paths") + tl::make_member (&db::GDS2WriterOptions::multi_xy_records, "multi-xy-records") + + tl::make_member (&db::GDS2WriterOptions::resolve_skew_arrays, "resolve-skew-arrays") + tl::make_member (&db::GDS2WriterOptions::max_vertex_count, "max-vertex-count") + tl::make_member (&db::GDS2WriterOptions::max_cellname_length, "max-cellname-length") + tl::make_member (&db::GDS2WriterOptions::libname, "libname") diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h index 79592d62a..30941c142 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h @@ -109,6 +109,7 @@ public: : max_vertex_count (8000), no_zero_length_paths (false), multi_xy_records (false), + resolve_skew_arrays (false), max_cellname_length (32000), libname ("LIB"), user_units (1.0), @@ -146,6 +147,13 @@ public: */ bool multi_xy_records; + /** + * @brief Resolve skew arrays into single instances + * + * Setting this property to true will resolve skew (non-orthogonal) arrays into single instances. + */ + bool resolve_skew_arrays; + /** * @brief Maximum length of cell names * diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index fad889519..0ed5d14ae 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -130,6 +130,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8); size_t max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4); bool no_zero_length_paths = gds2_options.no_zero_length_paths; + bool resolve_skew_arrays = gds2_options.resolve_skew_arrays; m_cell_name_map = db::WriterCellNameMap (max_cellname_length); m_cell_name_map.replacement ('$'); @@ -281,7 +282,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) { progress_checkpoint (); - write_inst (sf, *inst, true /*normalize*/, layout, inst->prop_id ()); + write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ()); } @@ -346,14 +347,24 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S progress_checkpoint (); } +static bool is_orthogonal (const db::Vector &rv, const db::Vector &cv) +{ + return (rv.x () == 0 && cv.y () == 0) || (rv.y () == 0 && cv.x () == 0); +} + void -GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normalize, const db::Layout &layout, db::properties_id_type prop_id) +GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normalize, bool resolve_skew_arrays, const db::Layout &layout, db::properties_id_type prop_id) { db::Vector a, b; unsigned long amax, bmax; bool is_reg = instance.is_regular_array (a, b, amax, bmax); + // skew arrays are resolved if required + if (is_reg && ! is_orthogonal (a, b) != 0 && resolve_skew_arrays) { + is_reg = false; + } + for (db::CellInstArray::iterator ii = instance.begin (); ! ii.at_end (); ++ii) { db::Trans t = *ii; diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index cc15680e6..3dada7332 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -122,7 +122,7 @@ protected: /** * @brief Write an instance */ - void write_inst (double sf, const db::Instance &instance, bool normalize, const db::Layout &layout, db::properties_id_type prop_id); + void write_inst (double sf, const db::Instance &instance, bool normalize, bool resolve_skew_arrays, const db::Layout &layout, db::properties_id_type prop_id); /** * @brief Write a shape as box diff --git a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc index 8b732eba1..c8e62120e 100644 --- a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc +++ b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc @@ -65,6 +65,16 @@ static bool get_gds2_multi_xy_records (const db::SaveLayoutOptions *options) return options->get_options ().multi_xy_records; } +static void set_gds2_resolve_skew_arrays (db::SaveLayoutOptions *options, bool n) +{ + options->get_options ().resolve_skew_arrays = n; +} + +static bool get_gds2_resolve_skew_arrays (const db::SaveLayoutOptions *options) +{ + return options->get_options ().resolve_skew_arrays; +} + static void set_gds2_write_file_properties (db::SaveLayoutOptions *options, bool n) { options->get_options ().write_file_properties = n; @@ -129,7 +139,7 @@ static double get_gds2_user_units (const db::SaveLayoutOptions *options) static gsi::ClassExt gds2_writer_options ( gsi::method_ext ("gds2_max_vertex_count=", &set_gds2_max_vertex_count, gsi::arg ("count"), - "@brief Set the maximum number of vertices for polygons to write\n" + "@brief Sets the maximum number of vertices for polygons to write\n" "This property describes the maximum number of point for polygons in GDS2 files.\n" "Polygons with more points will be split.\n" "The minimum value for this property is 4. The maximum allowed value is about 4000 or 8000, depending on the\n" @@ -138,24 +148,37 @@ gsi::ClassExt gds2_writer_options ( "\nThis property has been added in version 0.18.\n" ) + gsi::method_ext ("gds2_max_vertex_count", &get_gds2_max_vertex_count, - "@brief Get the maximum number of vertices for polygons to write\n" + "@brief Gets the maximum number of vertices for polygons to write\n" "See \\gds2_max_vertex_count= method for a description of the maximum vertex count." "\nThis property has been added in version 0.18.\n" ) + gsi::method_ext ("gds2_multi_xy_records=", &set_gds2_multi_xy_records, gsi::arg ("flag"), - "@brief Use multiple XY records in BOUNDARY elements for unlimited large polygons\n" + "@brief Uses multiple XY records in BOUNDARY elements for unlimited large polygons\n" "\n" "Setting this property to true allows producing polygons with an unlimited number of points \n" "at the cost of incompatible formats. Setting it to true disables the \\gds2_max_vertex_count setting.\n" "\nThis property has been added in version 0.18.\n" ) + gsi::method_ext ("gds2_multi_xy_records?", &get_gds2_multi_xy_records, - "@brief Get the property enabling multiple XY records for BOUNDARY elements\n" + "@brief Gets the property enabling multiple XY records for BOUNDARY elements\n" "See \\gds2_multi_xy_records= method for a description of this property." "\nThis property has been added in version 0.18.\n" ) + + gsi::method_ext ("gds2_resolve_skew_arrays=", &set_gds2_resolve_skew_arrays, gsi::arg ("flag"), + "@brief Resolves skew arrays into single instances\n" + "\n" + "Setting this property to true will make skew (non-orthongonal) arrays being resolved into single instances.\n" + "Skew arrays happen if either the row or column vector isn't paralell to x or y axis. Such arrays can cause problems with " + "some legacy software and can be disabled with this option.\n" + "\nThis property has been added in version 0.27.1.\n" + ) + + gsi::method_ext ("gds2_resolve_skew_arrays?", &get_gds2_resolve_skew_arrays, + "@brief Gets a value indicating whether to resolve skew arrays into single instances\n" + "See \\gds2_resolve_skew_arrays= method for a description of this property." + "\nThis property has been added in version 0.27.1.\n" + ) + gsi::method_ext ("gds2_write_timestamps=", &set_gds2_write_timestamps, gsi::arg ("flag"), - "@brief Write the current time into the GDS2 timestamps if set to true\n" + "@brief Writes the current time into the GDS2 timestamps if set to true\n" "\n" "If this property is set to false, the time fields will all be zero. This somewhat simplifies compare and diff " "applications.\n" diff --git a/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui b/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui index 4cc503688..72c6020f4 100644 --- a/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui +++ b/src/plugins/streamers/gds2/lay_plugin/GDS2WriterOptionPage.ui @@ -50,58 +50,13 @@ 6 - - - - Max. vertices - - - - + Write current time to time stamps (BGNLIB, BGNSTR) - - - - - - - (<4000 recommended, absolute limit 8191) - - - - - - - - - - (keep empty for unspecified limit) - - - - - - - Multi-XY record mode for boundaries -(enables infinitely large polygons/paths at the cost of compatibility) - - - - - - - - - - Library name - - - @@ -109,13 +64,16 @@ - - + + - Eliminate zero-length paths (convert to BOUNDARY) + (<4000 recommended, absolute limit 8191) + + + @@ -181,6 +139,55 @@ + + + + + + + (keep empty for unspecified limit) + + + + + + + Multi-XY record mode for boundaries +(enables infinitely large polygons/paths at the cost of compatibility) + + + + + + + Library name + + + + + + + Max. vertices + + + + + + + + + + Eliminate zero-length paths (convert to BOUNDARY) + + + + + + + Resolve skew (non-orthogonal) arrays into single instances + + + diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc index c24eda9b8..948b211a7 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc @@ -61,6 +61,7 @@ GDS2WriterOptionPage::setup (const db::FormatSpecificWriterOptions *o, const db: mp_ui->write_file_properties->setChecked (options->write_file_properties); mp_ui->no_zero_length_paths->setChecked (options->no_zero_length_paths); mp_ui->multi_xy_cbx->setChecked (options->multi_xy_records); + mp_ui->resolve_skew_arrays_cbx->setChecked (options->resolve_skew_arrays); mp_ui->max_vertex_le->setEnabled (! options->multi_xy_records); mp_ui->max_vertex_le->setText (tl::to_qstring (tl::to_string (options->max_vertex_count))); mp_ui->cell_name_length_le->setText (tl::to_qstring (tl::to_string (options->max_cellname_length))); @@ -76,6 +77,7 @@ GDS2WriterOptionPage::commit (db::FormatSpecificWriterOptions *o, const db::Tech unsigned int n; options->multi_xy_records = mp_ui->multi_xy_cbx->isChecked (); + options->resolve_skew_arrays = mp_ui->resolve_skew_arrays_cbx->isChecked (); options->write_timestamps = mp_ui->write_timestamps->isChecked (); options->write_cell_properties = mp_ui->write_cell_properties->isChecked (); options->write_file_properties = mp_ui->write_file_properties->isChecked (); diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index 77c902223..35e25883c 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -83,6 +83,19 @@ TEST(1) run_test (_this, "arefs.gds", "arefs_ref.gds"); } +TEST(1a) +{ + db::GDS2WriterOptions opt; + run_test (_this, "arefs_skew.gds", "arefs_skew1.gds", false, opt); +} + +TEST(1b) +{ + db::GDS2WriterOptions opt; + opt.resolve_skew_arrays = true; + run_test (_this, "arefs_skew.gds", "arefs_skew2.gds", false, opt); +} + TEST(2) { db::Manager m (false); diff --git a/src/rba/rba/rbaInspector.cc b/src/rba/rba/rbaInspector.cc index 65a32a9f8..25979b66d 100644 --- a/src/rba/rba/rbaInspector.cc +++ b/src/rba/rba/rbaInspector.cc @@ -338,7 +338,7 @@ public: : m_obj (obj), mp_cls (0), m_members (Qnil) { rb_gc_register_address (&m_obj); - mp_cls = find_cclass (rb_class_of (m_obj)); + mp_cls = find_cclass_maybe_null (rb_class_of (m_obj)); m_members = rb_obj_instance_variables (m_obj); rb_gc_register_address (&m_members); diff --git a/src/rba/rba/rbaInternal.cc b/src/rba/rba/rbaInternal.cc index cfab92efe..65624843d 100644 --- a/src/rba/rba/rbaInternal.cc +++ b/src/rba/rba/rbaInternal.cc @@ -940,6 +940,13 @@ bool is_registered (const gsi::ClassBase *cls) } const gsi::ClassBase *find_cclass (VALUE k) +{ + const gsi::ClassBase *cls = find_cclass_maybe_null (k); + tl_assert (cls != 0); + return cls; +} + +const gsi::ClassBase *find_cclass_maybe_null (VALUE k) { std::map ::const_iterator cls; @@ -954,8 +961,7 @@ const gsi::ClassBase *find_cclass (VALUE k) } } - tl_assert (cls != cls_map.end ()); - return cls->second; + return cls != cls_map.end () ? cls->second : 0; } } diff --git a/src/rba/rba/rbaInternal.h b/src/rba/rba/rbaInternal.h index ca885968d..73a1a0011 100644 --- a/src/rba/rba/rbaInternal.h +++ b/src/rba/rba/rbaInternal.h @@ -218,6 +218,11 @@ void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls); */ const gsi::ClassBase *find_cclass (VALUE k); +/** + * @brief Find the class declaration from the Ruby object + */ +const gsi::ClassBase *find_cclass_maybe_null (VALUE k); + /** * @brief Finds the Ruby class for a gsi class */ diff --git a/testdata/algo/lvs_test1_au.lvsdb.1 b/testdata/algo/lvs_test1_au.lvsdb.1 index a16b88d6d..a73ab07a0 100644 --- a/testdata/algo/lvs_test1_au.lvsdb.1 +++ b/testdata/algo/lvs_test1_au.lvsdb.1 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/algo/lvs_test1_au.lvsdb.2 b/testdata/algo/lvs_test1_au.lvsdb.2 index 42c2187d5..20133d556 100644 --- a/testdata/algo/lvs_test1_au.lvsdb.2 +++ b/testdata/algo/lvs_test1_au.lvsdb.2 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/algo/lvs_test1b_au.lvsdb.1 b/testdata/algo/lvs_test1b_au.lvsdb.1 index 83dedd5e2..3b206b741 100644 --- a/testdata/algo/lvs_test1b_au.lvsdb.1 +++ b/testdata/algo/lvs_test1b_au.lvsdb.1 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/algo/lvs_test1b_au.lvsdb.2 b/testdata/algo/lvs_test1b_au.lvsdb.2 index a8c999396..455337d3b 100644 --- a/testdata/algo/lvs_test1b_au.lvsdb.2 +++ b/testdata/algo/lvs_test1b_au.lvsdb.2 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/algo/lvs_test2_au.lvsdb.1 b/testdata/algo/lvs_test2_au.lvsdb.1 index 52e53e669..cecb9d910 100644 --- a/testdata/algo/lvs_test2_au.lvsdb.1 +++ b/testdata/algo/lvs_test2_au.lvsdb.1 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/algo/lvs_test2_au.lvsdb.2 b/testdata/algo/lvs_test2_au.lvsdb.2 index a73de534d..0ce0bf4d9 100644 --- a/testdata/algo/lvs_test2_au.lvsdb.2 +++ b/testdata/algo/lvs_test2_au.lvsdb.2 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/algo/lvs_test2b_au.lvsdb.1 b/testdata/algo/lvs_test2b_au.lvsdb.1 index a3bbd637b..47263e0e2 100644 --- a/testdata/algo/lvs_test2b_au.lvsdb.1 +++ b/testdata/algo/lvs_test2b_au.lvsdb.1 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/algo/lvs_test2b_au.lvsdb.2 b/testdata/algo/lvs_test2b_au.lvsdb.2 index 8a9639fac..cbe6175f5 100644 --- a/testdata/algo/lvs_test2b_au.lvsdb.2 +++ b/testdata/algo/lvs_test2b_au.lvsdb.2 @@ -9,7 +9,7 @@ layout( # This section lists the mask layers (drawing or derived) and their connections. # Mask layers - layer(bulk '1/0') + layer(bulk) layer(nwell '1/0') layer(poly '3/0') layer(poly_lbl '3/1') diff --git a/testdata/drc/drcGenericTests_6.drc b/testdata/drc/drcGenericTests_6.drc index 7e345876f..34eeb7d0c 100644 --- a/testdata/drc/drcGenericTests_6.drc +++ b/testdata/drc/drcGenericTests_6.drc @@ -22,6 +22,7 @@ l1.drc(-90 < corners(as_dots) <= 90.0).output(101, 0) l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners l1.drc(corners(as_boxes) <= -90).output(104, 0) +l1.drc(corners(as_edge_pairs) == 90).output(105, 0) l1.drc(middle).output(110, 0) l1.drc(middle(as_dots)).output(111, 0) diff --git a/testdata/drc/drcGenericTests_au6.gds b/testdata/drc/drcGenericTests_au6.gds index 1cdcbf50a..6674e7552 100644 Binary files a/testdata/drc/drcGenericTests_au6.gds and b/testdata/drc/drcGenericTests_au6.gds differ diff --git a/testdata/drc/drcGenericTests_au6d.gds b/testdata/drc/drcGenericTests_au6d.gds index 0eedf1c58..8bf276f1d 100644 Binary files a/testdata/drc/drcGenericTests_au6d.gds and b/testdata/drc/drcGenericTests_au6d.gds differ diff --git a/testdata/drc/drcSimpleTests_1.drc b/testdata/drc/drcSimpleTests_1.drc index 28f5bd3a5..35e022c1d 100644 --- a/testdata/drc/drcSimpleTests_1.drc +++ b/testdata/drc/drcSimpleTests_1.drc @@ -3,7 +3,17 @@ dbu 0.001 target($drc_test_target, "TOP") +def self.expect_count(layer, c, hc, where) + if layer.count != c + raise(where + ": Layer count #{layer.count} does not equal #{c}") + end + if layer.hier_count != hc + raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}") + end +end + x = polygon_layer +expect_count(x, 0, 0, "empty layer") x.is_empty? == true || raise("unexpected value") x.is_box? == false || raise("unexpected value") x.insert(box(4.0, 0, 4.7, 0.7)) @@ -12,6 +22,7 @@ x.is_box? == true || raise("unexpected value") x.insert(polygon([ p(0, 0), p(2.0, 0), p(1.0, 1.0) ])) x.insert(polygon([ p(0, -5.0), p(2.0, -5.0), p(1.0, -6.0) ])) x.insert(path([ p(0, -2), p(2.0, -2) ], 0.2)) +expect_count(x, 4, 4, "after 3x insert") x.is_box? == false || raise("unexpected value") x.output(10, 0) diff --git a/testdata/drc/drcSimpleTests_2.drc b/testdata/drc/drcSimpleTests_2.drc index 66faa4c55..89de3aa2e 100644 --- a/testdata/drc/drcSimpleTests_2.drc +++ b/testdata/drc/drcSimpleTests_2.drc @@ -2,6 +2,15 @@ target($drc_test_target, "TOP") source($drc_test_source, "TOP") +def self.expect_count(layer, c, hc, where) + if layer.count != c + raise(where + ": Layer count #{layer.count} does not equal #{c}") + end + if layer.hier_count != hc + raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}") + end +end + a1 = input(1) b1 = input(2) c1 = input(3) @@ -57,6 +66,9 @@ a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0) a1.corners.sized(0.05).output(1060, 0) a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0) a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0) +a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0) +a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0) +a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0) a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0) a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0) @@ -89,3 +101,6 @@ a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.transformed(RBA::VCplx a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0) a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0) +expect_count(a1.edges, 9, 9, "a1.edges") +expect_count(a1.width(1.5), 5, 5, "a1.width(1.5)") + diff --git a/testdata/drc/drcSimpleTests_30.drc b/testdata/drc/drcSimpleTests_30.drc index de3b78fee..09fb0ea68 100644 --- a/testdata/drc/drcSimpleTests_30.drc +++ b/testdata/drc/drcSimpleTests_30.drc @@ -9,10 +9,18 @@ b.output(0, 0) a.output(1, 0) a.with_density(0..0.1, tile_size(10.um), tile_boundary(b)).output(100, 0) -a.without_density(0..0.1, tile_size(10.um)).output(101, 0) +a.without_density(0..0.1, tile_size(10.um), padding_zero).output(101, 0) a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um)).output(102, 0) a.with_density(0.1, nil, tile_size(10.um, 20.um)).output(103, 0) a.with_density(0.1, 1.0, tile_size(10.um), tile_step(10.um, 20.um)).output(104, 0) a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), tile_count(10, 15)).output(105, 0) -a.with_density(0..0.1, tile_size(100.um), tile_step(10.um)).output(110, 0) +a.with_density(0..0.1, tile_size(100.um), tile_step(10.um), padding_zero).output(110, 0) + +a.with_density(0..0.1, tile_size(10.um), tile_boundary(b), padding_ignore).output(200, 0) +a.without_density(0..0.1, tile_size(10.um), padding_ignore).output(201, 0) +a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), padding_ignore).output(202, 0) +a.with_density(0.1, nil, tile_size(10.um, 20.um), padding_ignore).output(203, 0) +a.with_density(0.1, 1.0, tile_size(10.um), tile_step(10.um, 20.um), padding_ignore).output(204, 0) +a.with_density(0.1, nil, tile_size(10.um), tile_origin(25.um, 10.um), tile_count(10, 15), padding_ignore).output(205, 0) +a.with_density(0..0.1, tile_size(100.um), tile_step(10.um), padding_ignore).output(210, 0) diff --git a/testdata/drc/drcSimpleTests_30.gds b/testdata/drc/drcSimpleTests_30.gds index 14b2f86b8..da42ce5a8 100644 Binary files a/testdata/drc/drcSimpleTests_30.gds and b/testdata/drc/drcSimpleTests_30.gds differ diff --git a/testdata/drc/drcSimpleTests_4.drc b/testdata/drc/drcSimpleTests_4.drc index b729ee968..37f2e53f5 100644 --- a/testdata/drc/drcSimpleTests_4.drc +++ b/testdata/drc/drcSimpleTests_4.drc @@ -4,6 +4,15 @@ source($drc_test_source, "TOPTOP_SMALL") target($drc_test_target) +def self.expect_count(layer, c, hc, where) + if layer.count != c + raise(where + ": Layer count #{layer.count} does not equal #{c}") + end + if layer.hier_count != hc + raise(where + ": Layer hier count #{layer.hier_count} does not equal #{c}") + end +end + cell("TOPTOP_SMALL") l1_flat = input(1) @@ -66,12 +75,16 @@ r.output(1023, 0) r.extents.output(1123, 0) r = l1.space(0.5) +expect_count(r, 3, 1, "r on l1") r.output(1010, 0) r.extents.output(1110, 0) +expect_count(l1, 15, 5, "l1 before flatten") l1.flatten +expect_count(l1, 15, 15, "l1 after flatten") r = l1.space(0.5) r.output(1011, 0) r.extents.output(1111, 0) +expect_count(r, 3, 3, "r on l1.flatten") diff --git a/testdata/drc/drcSimpleTests_48.drc b/testdata/drc/drcSimpleTests_48.drc new file mode 100644 index 000000000..672fe9270 --- /dev/null +++ b/testdata/drc/drcSimpleTests_48.drc @@ -0,0 +1,44 @@ + +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.drc(separation(l2, projection) < 1.0).output(100, 0) +l1.drc(separation(l2, whole_edges, projection) < 1.0).output(101, 0) +l1.drc(separation(l2, projection) >= 1.0).output(102, 0) +l1.separation(l2, projection, 1.0).output(110, 0) +l1.separation(l2, projection, 1.0, whole_edges).output(111, 0) + +l2.drc(separation(l1, projection) < 1.0).output(200, 0) +l2.drc(separation(l1, whole_edges, projection) < 1.0).output(201, 0) +l2.drc(separation(l1, projection) >= 1.0).output(202, 0) +l2.separation(l1, projection, 1.0).output(210, 0) +l2.separation(l1, projection, 1.0, whole_edges).output(211, 0) + +(l1 + l2).drc(space(projection) < 1.0).output(300, 0) +(l1 + l2).drc(space(whole_edges, projection) < 1.0).output(301, 0) +(l1 + l2).drc(space(projection) >= 1.0).output(302, 0) +(l1 + l2).space(projection, 1.0).output(310, 0) +(l1 + l2).space(projection, 1.0, whole_edges).output(311, 0) + +l1.drc(enclosing(l2, projection) < 1.0).output(400, 0) +l1.drc(enclosing(l2, whole_edges, projection) < 1.0).output(401, 0) +l1.drc(enclosing(l2, projection) >= 1.0).output(402, 0) +l1.enclosing(l2, projection, 1.0).output(410, 0) +l1.enclosing(l2, projection, 1.0, whole_edges).output(411, 0) + +l1.drc(overlap(l2, projection) < 1.0).output(500, 0) +l1.drc(overlap(l2, whole_edges, projection) < 1.0).output(501, 0) +l1.drc(overlap(l2, projection) >= 1.0).output(502, 0) +l1.overlap(l2, projection, 1.0).output(510, 0) +l1.overlap(l2, projection, 1.0, whole_edges).output(511, 0) + diff --git a/testdata/drc/drcSimpleTests_48.gds b/testdata/drc/drcSimpleTests_48.gds new file mode 100644 index 000000000..8742b1c65 Binary files /dev/null and b/testdata/drc/drcSimpleTests_48.gds differ diff --git a/testdata/drc/drcSimpleTests_49.drc b/testdata/drc/drcSimpleTests_49.drc new file mode 100644 index 000000000..706171bc8 --- /dev/null +++ b/testdata/drc/drcSimpleTests_49.drc @@ -0,0 +1,41 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +ep = input(1, 0).drc(space(projection) < 0.5) + +ep.polygons(0).output(100, 0) + +ep.with_distance(0..0.1).polygons(0).output(110, 0) +ep.with_distance(0.25, nil).polygons(0).output(111, 0) +ep.without_distance(0..0.1).polygons(0).output(120, 0) +ep.without_distance(0.25, nil).polygons(0).output(121, 0) + +ep.with_angle(45.0).polygons(0).output(200, 0) +ep.with_angle(0.0).polygons(0).output(201, 0) +ep.with_angle(45.0..91.0).polygons(0).output(202, 0) +ep.with_angle(45.0, both).polygons(0).output(210, 0) +ep.with_angle(0.0, both).polygons(0).output(211, 0) +ep.with_angle(45.0..91.0, both).polygons(0).output(212, 0) + +ep.without_angle(45.0).polygons(0).output(220, 0) +ep.without_angle(0.0).polygons(0).output(221, 0) +ep.without_angle(45.0..91.0).polygons(0).output(222, 0) +ep.without_angle(45.0, both).polygons(0).output(230, 0) +ep.without_angle(0.0, both).polygons(0).output(231, 0) +ep.without_angle(45.0..91.0, both).polygons(0).output(232, 0) + +ep.with_length(0.5).polygons(0).output(300, 0) +ep.with_length(0.4..0.51).polygons(0).output(301, 0) +ep.with_length(0.5, both).polygons(0).output(310, 0) +ep.with_length(0.4..0.51, both).polygons(0).output(311, 0) + +ep.without_length(0.5).polygons(0).output(320, 0) +ep.without_length(0.4..0.51).polygons(0).output(321, 0) +ep.without_length(0.5, both).polygons(0).output(330, 0) +ep.without_length(0.4..0.51, both).polygons(0).output(331, 0) + diff --git a/testdata/drc/drcSimpleTests_49.gds b/testdata/drc/drcSimpleTests_49.gds new file mode 100644 index 000000000..7c45ebc9e Binary files /dev/null and b/testdata/drc/drcSimpleTests_49.gds differ diff --git a/testdata/drc/drcSimpleTests_5.drc b/testdata/drc/drcSimpleTests_5.drc index 8f46068f5..28b655ea6 100644 --- a/testdata/drc/drcSimpleTests_5.drc +++ b/testdata/drc/drcSimpleTests_5.drc @@ -1,5 +1,5 @@ -# Hierarchical antenna check +# Flat antenna check source($drc_test_source, "RINGO") target($drc_test_target) @@ -14,6 +14,17 @@ metal2 = input(8, 0) gate = diff & poly +connect(gate, poly) +connect(poly, poly_cont) +connect(poly_cont, metal1) + +antenna_check(gate, metal1, 1.0).output(201) +antenna_check(gate, metal1, 2.0).output(202) +antenna_check(gate, metal1, 3.0).output(203) +antenna_check(gate, metal1, 4.0).output(204) + +clear_connections + connect(gate, poly) connect(poly, poly_cont) connect(poly_cont, metal1) @@ -24,3 +35,4 @@ antenna_check(gate, metal2, 1.0).output(101) antenna_check(gate, metal2, 5.0).output(105) antenna_check(gate, metal2, 10.0).output(110) antenna_check(gate, metal2, 50.0).output(150) + diff --git a/testdata/drc/drcSimpleTests_5i.drc b/testdata/drc/drcSimpleTests_5i.drc new file mode 100644 index 000000000..973cdfa7e --- /dev/null +++ b/testdata/drc/drcSimpleTests_5i.drc @@ -0,0 +1,33 @@ + +# Flat antenna check + +source($drc_test_source, "RINGO") +target($drc_test_target) + +diff = input(2, 0) +poly = input(3, 0) +contact = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +via1 = input(7, 0) +metal2 = input(8, 0) + +gate = diff & poly + +connect(gate, poly) +connect(poly, poly_cont) +connect(poly_cont, metal1) + +antenna_check(gate, metal1, 1.0).output(201) +antenna_check(gate, metal1, 2.0).output(202) +antenna_check(gate, metal1, 3.0).output(203) +antenna_check(gate, metal1, 4.0).output(204) + +connect(metal1, via1) +connect(via1, metal2) + +antenna_check(gate, metal2, 1.0).output(101) +antenna_check(gate, metal2, 5.0).output(105) +antenna_check(gate, metal2, 10.0).output(110) +antenna_check(gate, metal2, 50.0).output(150) + diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds index 76c8b8afe..b916b5cae 100644 Binary files a/testdata/drc/drcSimpleTests_au2.gds and b/testdata/drc/drcSimpleTests_au2.gds differ diff --git a/testdata/drc/drcSimpleTests_au30.gds b/testdata/drc/drcSimpleTests_au30.gds index f09ab94b3..7caad645c 100644 Binary files a/testdata/drc/drcSimpleTests_au30.gds and b/testdata/drc/drcSimpleTests_au30.gds differ diff --git a/testdata/drc/drcSimpleTests_au4.gds b/testdata/drc/drcSimpleTests_au4.gds index 6920b9a33..5f4090148 100644 Binary files a/testdata/drc/drcSimpleTests_au4.gds and b/testdata/drc/drcSimpleTests_au4.gds differ diff --git a/testdata/drc/drcSimpleTests_au48.gds b/testdata/drc/drcSimpleTests_au48.gds new file mode 100644 index 000000000..4f81f169a Binary files /dev/null and b/testdata/drc/drcSimpleTests_au48.gds differ diff --git a/testdata/drc/drcSimpleTests_au48d.gds b/testdata/drc/drcSimpleTests_au48d.gds new file mode 100644 index 000000000..4f81f169a Binary files /dev/null and b/testdata/drc/drcSimpleTests_au48d.gds differ diff --git a/testdata/drc/drcSimpleTests_au49.gds b/testdata/drc/drcSimpleTests_au49.gds new file mode 100644 index 000000000..74b0bf9ca Binary files /dev/null and b/testdata/drc/drcSimpleTests_au49.gds differ diff --git a/testdata/drc/drcSimpleTests_au49d.gds b/testdata/drc/drcSimpleTests_au49d.gds new file mode 100644 index 000000000..cbbd8c6cb Binary files /dev/null and b/testdata/drc/drcSimpleTests_au49d.gds differ diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds index 3578cc1c0..064e177b1 100644 Binary files a/testdata/drc/drcSimpleTests_au5.gds and b/testdata/drc/drcSimpleTests_au5.gds differ diff --git a/testdata/drc/drcSuiteTests_au5.oas b/testdata/drc/drcSuiteTests_au5.oas index ccb803594..ca77bf7fc 100644 Binary files a/testdata/drc/drcSuiteTests_au5.oas and b/testdata/drc/drcSuiteTests_au5.oas differ diff --git a/testdata/drc/drcSuiteTests_au6.oas b/testdata/drc/drcSuiteTests_au6.oas index e9a693a4d..9ef67198b 100644 Binary files a/testdata/drc/drcSuiteTests_au6.oas and b/testdata/drc/drcSuiteTests_au6.oas differ diff --git a/testdata/gds/arefs_skew.gds b/testdata/gds/arefs_skew.gds new file mode 100644 index 000000000..e478b422d Binary files /dev/null and b/testdata/gds/arefs_skew.gds differ diff --git a/testdata/gds/arefs_skew1.gds b/testdata/gds/arefs_skew1.gds new file mode 100644 index 000000000..a7738e3c7 Binary files /dev/null and b/testdata/gds/arefs_skew1.gds differ diff --git a/testdata/gds/arefs_skew2.gds b/testdata/gds/arefs_skew2.gds new file mode 100644 index 000000000..6183fcbd8 Binary files /dev/null and b/testdata/gds/arefs_skew2.gds differ diff --git a/testdata/lvs/custom_compare.cir b/testdata/lvs/custom_compare.cir new file mode 100644 index 000000000..e558a4c04 --- /dev/null +++ b/testdata/lvs/custom_compare.cir @@ -0,0 +1,7 @@ +* Extracted by KLayout + +* cell TOP +.SUBCKT TOP +* device instance $1 r0 *1 7.52,4.175 RES +R$1 2 1 51 RES +.ENDS TOP diff --git a/testdata/lvs/custom_compare.gds b/testdata/lvs/custom_compare.gds new file mode 100644 index 000000000..7f476cbca Binary files /dev/null and b/testdata/lvs/custom_compare.gds differ diff --git a/testdata/lvs/custom_compare.lvs b/testdata/lvs/custom_compare.lvs new file mode 100644 index 000000000..25e60364c --- /dev/null +++ b/testdata/lvs/custom_compare.lvs @@ -0,0 +1,72 @@ + +source($lvs_test_source) +report_lvs($lvs_test_target_lvsdb, true) +target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") + +schematic("custom_compare_sch.cir") + +deep + +# ------------------------------------------------------------------- +# Layers + +# Drawing layers +nwell = input(1, 0) +active = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) # includes labels +via1 = input(10, 0) +metal2 = input(11, 0) # includes labels + +# Bulk layer for terminal provisioning +bulk = polygon_layer + +# Computed layers +poly_not_res = poly - polyres +poly_in_res = poly & polyres + +# Resistor Definition +res_ex = resistor("RES", 1.0) +extract_devices(res_ex, { "C" => poly_not_res, "R" => poly_in_res }) + + +# ------------------------------------------------------------------- +# Connectivity + +# Inter-layer +connect(poly_not_res, contact) + +# ------------------------------------------------------------------- +# Netlist and compare + +class ResistorComparator < RBA::GenericDeviceParameterCompare + + def less(device_a, device_b) + delta = 1 + param_id = RBA::DeviceClassResistor::PARAM_R + param = "R" + if (device_a.parameter(param_id) - device_b.parameter(param_id)).abs > delta + result = device_a.parameter(param_id) < device_b.parameter(param_id) + return result + else + return false + end + end + +end + +netlist.device_class_by_name("RES").equal_parameters = ResistorComparator::new() + +# Netlist normalization +netlist.simplify + +# Hierarchy alignment (flatten out unmatched cells) +align + +# Netlist vs. netlist +compare diff --git a/testdata/lvs/custom_compare.lvsdb b/testdata/lvs/custom_compare.lvsdb new file mode 100644 index 000000000..c54b0fb98 --- /dev/null +++ b/testdata/lvs/custom_compare.lvsdb @@ -0,0 +1,111 @@ +#%lvsdb-klayout + +# Layout +layout( + top(TOP) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l3 '8/0') + layer(l1) + + # Mask layer connectivity + connect(l3 l3 l1) + connect(l1 l3 l1) + + # Device class section + class(RES RES) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$RES RES + terminal(A + rect(l1 (2225 -675) (200 250)) + ) + terminal(B + rect(l1 (-2425 425) (200 250)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(TOP + + # Circuit boundary + rect((0 0) (10255 5900)) + + # Nets with their geometries + net(1 + rect(l3 (4850 4600) (180 180)) + rect(l1 (-245 -250) (310 320)) + rect(l1 (0 -250) (200 250)) + ) + net(2 + rect(l3 (10010 3500) (180 180)) + rect(l1 (-245 -250) (310 320)) + rect(l1 (-510 -250) (200 250)) + ) + + # Devices and their connections + device(1 D$RES + location(7520 4175) + param(R 51) + param(L 25.5) + param(W 0.5) + param(A 3.1875) + param(P 26) + terminal(A 2) + terminal(B 1) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(RES RES) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(TOP + + # Nets + net(1 name(R1)) + net(2 name(R2)) + + # Outgoing pins and their connections to nets + pin(1 name(R1)) + pin(2 name(R2)) + + # Devices and their connections + device(1 RES + name('5') + param(R 50.1) + param(L 0) + param(W 0) + param(A 0) + param(P 0) + terminal(A 1) + terminal(B 2) + ) + + ) +) + +# Cross reference +xref( + circuit(TOP TOP match + xref( + net(1 1 warning) + net(2 2 warning) + pin(() 0 match) + pin(() 1 match) + device(1 1 match) + ) + ) +) diff --git a/testdata/lvs/custom_compare_sch.cir b/testdata/lvs/custom_compare_sch.cir new file mode 100644 index 000000000..3424c602d --- /dev/null +++ b/testdata/lvs/custom_compare_sch.cir @@ -0,0 +1,3 @@ +.subckt top r1 r2 +r5 r1 r2 50.1 +.ends diff --git a/testdata/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb index b75ed63bc..6922163e3 100644 --- a/testdata/lvs/double_height2.lvsdb +++ b/testdata/lvs/double_height2.lvsdb @@ -5,7 +5,7 @@ J( L(l3 '3/0') L(l11 '3/1') L(l6 '4/0') - L(l7 '2/0') + L(l7) L(l8 '6/0') L(l12 '6/1') L(l9 '7/0') diff --git a/testdata/lvs/double_height2_texts.lvsdb b/testdata/lvs/double_height2_texts.lvsdb index d62e4e90c..4aab34e12 100644 --- a/testdata/lvs/double_height2_texts.lvsdb +++ b/testdata/lvs/double_height2_texts.lvsdb @@ -5,7 +5,7 @@ J( L(l3 '3/0') L(l11 '3/1') L(l6 '4/0') - L(l7 '2/0') + L(l7) L(l8 '6/0') L(l12 '6/1') L(l9 '7/0') diff --git a/testdata/lvs/floating.lvsdb b/testdata/lvs/floating.lvsdb index e57702118..29042b22a 100644 --- a/testdata/lvs/floating.lvsdb +++ b/testdata/lvs/floating.lvsdb @@ -13,8 +13,8 @@ layout( layer(l4 '5/0') layer(l8 '8/0') layer(l11 '9/0') - layer(l12 '5/0') - layer(l13 '5/0') + layer(l12) + layer(l13) layer(l7) layer(l2) layer(l9) diff --git a/testdata/python/dbLayoutToNetlist.py b/testdata/python/dbLayoutToNetlist.py index 05f0e62d0..669398afe 100644 --- a/testdata/python/dbLayoutToNetlist.py +++ b/testdata/python/dbLayoutToNetlist.py @@ -576,6 +576,54 @@ end; self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "") self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "") + # --- simple incremental antenna check with metal1 + metal2 + + l2n._destroy() + l2n = pya.LayoutToNetlist(dss) + + l2n.register(rdiode, "diode") + l2n.register(rpoly, "poly") + l2n.register(rcont, "cont") + l2n.register(rmetal1, "metal1") + l2n.register(rvia1, "via1") + l2n.register(rmetal2, "metal2") + + l2n.connect(rpoly) + l2n.connect(rcont) + l2n.connect(rmetal1) + l2n.connect(rmetal2) + l2n.connect(rpoly, rcont) + l2n.connect(rcont, rmetal1) + + self.assertEqual(l2n.is_extracted(), False) + l2n.extract_netlist() + self.assertEqual(l2n.is_extracted(), True) + + a1_3 = l2n.antenna_check(rpoly, rmetal1, 3) + a1_10 = l2n.antenna_check(rpoly, rmetal1, 10) + a1_30 = l2n.antenna_check(rpoly, rmetal1, 30) + + # Note: flatten.merged performs some normalization + self.assertEqual(str(a1_3.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(100, 0)))), "") + self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "") + self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "") + + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + + self.assertEqual(l2n.is_extracted(), False) + l2n.extract_netlist() + self.assertEqual(l2n.is_extracted(), True) + + a2_5 = l2n.antenna_check(rpoly, rmetal2, 5) + a2_10 = l2n.antenna_check(rpoly, rmetal2, 10) + a2_17 = l2n.antenna_check(rpoly, rmetal2, 17) + + # Note: flatten.merged performs some normalization + self.assertEqual(str(a2_5.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(200, 0)))), "") + self.assertEqual(str(a2_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(201, 0)))), "") + self.assertEqual(str(a2_17.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(202, 0)))), "") + # --- simple antenna check with metal2 l2n._destroy() diff --git a/testdata/python/dbReaders.py b/testdata/python/dbReaders.py index 426b6f17f..d7f2d5c44 100644 --- a/testdata/python/dbReaders.py +++ b/testdata/python/dbReaders.py @@ -64,6 +64,11 @@ class DBReadersTests(unittest.TestCase): opt.gds2_allow_multi_xy_records = False self.assertEqual(opt.gds2_allow_multi_xy_records, False) + opt.gds2_resolve_skew_arrays = True + self.assertEqual(opt.gds2_resolve_skew_arrays, True) + opt.gds2_resolve_skew_arrays = False + self.assertEqual(opt.gds2_resolve_skew_arrays, False) + opt.gds2_allow_big_records = True self.assertEqual(opt.gds2_allow_big_records, True) opt.gds2_allow_big_records = False diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 7b179a732..d2beb1c9e 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -170,7 +170,8 @@ class DBLayoutToNetlist_TestClass < TestBase # Perform netlist extraction l2n.extract_netlist - assert_equal(l2n.netlist.to_s, <