From fd90e66ee1a46d1f026da63ce596a53d8c0e3352 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Jan 2021 14:38:44 +0100 Subject: [PATCH] WIP (shielding, various bug fixes) --- src/db/db/dbCompoundOperation.cc | 8 +-- src/db/db/dbCompoundOperation.h | 6 +- src/db/db/dbLocalOperation.cc | 64 +++++++++++++++++++++ src/db/db/dbLocalOperation.h | 49 +--------------- src/db/db/dbRegionLocalOperations.cc | 39 ++----------- src/db/db/dbRegionLocalOperations.h | 4 +- src/db/db/dbRegionUtils.cc | 17 +++++- src/db/db/dbRegionUtils.h | 72 ++++++++++++++---------- src/db/db/gsiDeclDbCompoundOperation.cc | 11 ++-- src/drc/unit_tests/drcGenericTests.cc | 20 +++++++ src/drc/unit_tests/drcSimpleTests.cc | 10 ++++ testdata/drc/drcGenericTests_16.drc | 19 +++++++ testdata/drc/drcGenericTests_16.gds | Bin 0 -> 234 bytes testdata/drc/drcGenericTests_17.drc | 24 ++++++++ testdata/drc/drcGenericTests_17.gds | Bin 0 -> 986 bytes testdata/drc/drcGenericTests_au16.gds | Bin 0 -> 490 bytes testdata/drc/drcGenericTests_au16d.gds | Bin 0 -> 490 bytes testdata/drc/drcGenericTests_au17.gds | Bin 0 -> 6618 bytes testdata/drc/drcGenericTests_au17d.gds | Bin 0 -> 6698 bytes testdata/drc/drcSimpleTests_27.drc | 24 ++++++++ testdata/drc/drcSimpleTests_27.gds | Bin 0 -> 986 bytes testdata/drc/drcSimpleTests_au27.gds | Bin 0 -> 6298 bytes testdata/drc/drcSimpleTests_au27d.gds | Bin 0 -> 6378 bytes testdata/drc/drcSuiteTests.drc | 23 ++++---- testdata/drc/drcSuiteTests_au2.oas | Bin 62220 -> 82267 bytes 25 files changed, 247 insertions(+), 143 deletions(-) create mode 100644 testdata/drc/drcGenericTests_16.drc create mode 100644 testdata/drc/drcGenericTests_16.gds create mode 100644 testdata/drc/drcGenericTests_17.drc create mode 100644 testdata/drc/drcGenericTests_17.gds create mode 100644 testdata/drc/drcGenericTests_au16.gds create mode 100644 testdata/drc/drcGenericTests_au16d.gds create mode 100644 testdata/drc/drcGenericTests_au17.gds create mode 100644 testdata/drc/drcGenericTests_au17d.gds create mode 100644 testdata/drc/drcSimpleTests_27.drc create mode 100644 testdata/drc/drcSimpleTests_27.gds create mode 100644 testdata/drc/drcSimpleTests_au27.gds create mode 100644 testdata/drc/drcSimpleTests_au27d.gds diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index 29f1a37dd..d0d175149 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -1601,11 +1601,11 @@ CompoundRegionCheckOperationNode::do_compute_local (db::Layout *layout, const sh tl_assert (results.size () == 1); if (results.front ().empty ()) { - op.compute_local (layout, interactions, results, max_vertex_count, area_ratio); + op.do_compute_local (layout, interactions, results, max_vertex_count, area_ratio); } else { std::vector > r; r.resize (1); - op.compute_local (layout, interactions, r, max_vertex_count, area_ratio); + op.do_compute_local (layout, interactions, r, max_vertex_count, area_ratio); results.front ().insert (r.front ().begin (), r.front ().end ()); } } @@ -1617,11 +1617,11 @@ CompoundRegionCheckOperationNode::do_compute_local (db::Layout *layout, const sh tl_assert (results.size () == 1); if (results.front ().empty ()) { - op.compute_local (layout, interactions, results, max_vertex_count, area_ratio); + op.do_compute_local (layout, interactions, results, max_vertex_count, area_ratio); } else { std::vector > r; r.resize (1); - op.compute_local (layout, interactions, r, max_vertex_count, area_ratio); + op.do_compute_local (layout, interactions, r, max_vertex_count, area_ratio); results.front ().insert (r.front ().begin (), r.front ().end ()); } } diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index 0d0563704..4f211a239 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -680,7 +680,11 @@ public: virtual ResultType result_type () const { return compound_operation_type_traits::type (); } virtual const db::TransformationReducer *vars () const { return mp_vars; } virtual bool wants_variants () const { return m_wants_variants; } - virtual db::Coord computed_dist () const { return m_op->dist (); } + + virtual db::Coord computed_dist () const + { + return CompoundRegionMultiInputOperationNode::computed_dist () + m_op->dist (); + } virtual std::vector inputs () const { diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index 80b492460..c75205b1d 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -33,10 +33,74 @@ #include "tlLog.h" #include "tlTimer.h" #include "tlInternational.h" +#include "tlProgress.h" namespace db { +// --------------------------------------------------------------------------------------------- +// local_operations implementation + +template +void local_operation::compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio, bool report_progress, const std::string &progress_desc) const +{ + if (interactions.num_subjects () <= 1 || ! requests_single_subjects ()) { + + do_compute_local (layout, interactions, results, max_vertex_count, area_ratio); + + } else { + + std::auto_ptr progress; + if (report_progress) { + progress.reset (new tl::RelativeProgress (progress_desc, interactions.size ())); + } + + 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; + + if (on_empty_intruder_hint () == OnEmptyIntruderHint::Drop) { + single_interactions.add_subject_shape (i->first, subject_shape); + } else { + // this includes the subject-without-intruder "interaction" + single_interactions.add_subject (i->first, subject_shape); + } + + const std::vector &intruders = interactions.intruders_for (i->first); + for (typename std::vector::const_iterator ii = intruders.begin (); ii != intruders.end (); ++ii) { + const std::pair &is = interactions.intruder_shape (*ii); + single_interactions.add_intruder_shape (*ii, is.first, is.second); + single_interactions.add_interaction (i->first, *ii); + } + + do_compute_local (layout, single_interactions, results, max_vertex_count, area_ratio); + + if (progress.get ()) { + ++*progress; + } + + } + + } +} + + +// explicit instantiations +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; +template class DB_PUBLIC local_operation; + // --------------------------------------------------------------------------------------------- // BoolAndOrNotLocalOperation implementation diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index eb488bede..abf6b4165 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -29,8 +29,6 @@ #include "dbLayout.h" #include "dbEdgeBoolean.h" -#include "tlProgress.h" -#include "tlInternational.h" #include #include @@ -89,55 +87,10 @@ public: /** * @brief Computes the results from a given set of interacting shapes - * @param layout The layout to which the shapes belong - * @param interactions The interaction set - * @param result The container to which the results are written * * If the operation requests single subject mode, the interactions will be split into single subject/intruder clusters */ - void compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio, bool report_progress = false, const std::string &progress_desc = std::string ()) const - { - if (interactions.num_subjects () <= 1 || ! requests_single_subjects ()) { - - do_compute_local (layout, interactions, results, max_vertex_count, area_ratio); - - } else { - - std::auto_ptr progress; - if (report_progress) { - progress.reset (new tl::RelativeProgress (progress_desc, interactions.size ())); - } - - 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; - - if (on_empty_intruder_hint () == OnEmptyIntruderHint::Drop) { - single_interactions.add_subject_shape (i->first, subject_shape); - } else { - // this includes the subject-without-intruder "interaction" - single_interactions.add_subject (i->first, subject_shape); - } - - const std::vector &intruders = interactions.intruders_for (i->first); - for (typename std::vector::const_iterator ii = intruders.begin (); ii != intruders.end (); ++ii) { - const std::pair &is = interactions.intruder_shape (*ii); - single_interactions.add_intruder_shape (*ii, is.first, is.second); - single_interactions.add_interaction (i->first, *ii); - } - - do_compute_local (layout, single_interactions, results, max_vertex_count, area_ratio); - - if (progress.get ()) { - ++*progress; - } - - } - - } - } + void compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio, bool report_progress = false, const std::string &progress_desc = std::string ()) const; /** * @brief Indicates the desired behaviour when a shape does not have an intruder diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 1ffb85f74..24307a99b 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -200,9 +200,9 @@ void check_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const { tl_assert (results.size () == 1); - std::unordered_set result; + std::unordered_set result, intra_polygon_result; - edge2edge_check_negative_or_positive > edge_check (m_check, result, m_options.negative, true, true, m_options.shielded); + edge2edge_check_negative_or_positive > edge_check (m_check, result, intra_polygon_result, m_options.negative, m_different_polygons, m_has_other, m_options.shielded); poly2poly_check poly_check (edge_check); std::list heap; @@ -302,30 +302,6 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape scanner.process (poly_check, m_check.distance (), db::box_convert ()); } while (edge_check.prepare_next_pass ()); - // now also handle the intra-polygon interactions if required - - std::unordered_set intra_polygon_result; - - if (! m_different_polygons && ! m_has_other) { - - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - std::list heap; - - const TS &subject = interactions.subject_shape (i->first); - scanner.clear (); - scanner.insert (push_polygon_to_heap (layout, subject, heap), 0); - - edge2edge_check_negative_or_positive > edge_check_intra (m_check, intra_polygon_result, m_options.negative, false, false, m_options.shielded); - poly2poly_check poly_check_intra (edge_check_intra); - - do { - scanner.process (poly_check_intra, m_check.distance (), db::box_convert ()); - } while (edge_check_intra.prepare_next_pass ()); - - } - - } // detect and remove parts of the result which have or do not have results "opposite" // ("opposite" is defined by the projection of edges "through" the subject shape) @@ -497,18 +473,13 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape for (std::unordered_set::const_iterator i = result.begin (); i != result.end (); ++i) { results.front ().insert (db::EdgePair (i->first (), i->first ().swapped_points ())); } - - } else { - - results.front ().insert (result.begin (), result.end ()); + result.clear (); } - } else { - - results.front ().insert (result.begin (), result.end ()); - } + + results.front ().insert (result.begin (), result.end ()); } template diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index 73da44aad..2b3977c25 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -210,14 +210,14 @@ public: virtual bool requests_single_subjects () const { return true; } virtual std::string description () const; + virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; + private: EdgeRelationFilter m_check; bool m_different_polygons; bool m_has_other; bool m_other_is_merged; db::RegionCheckOptions m_options; - - virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; }; typedef check_local_operation CheckLocalOperation; diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index 086bbd21c..31f2ccc18 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -67,6 +67,7 @@ Edge2EdgeCheckBase::prepare_next_pass () if (! m_ep.empty () && m_has_edge_pair_output) { std::vector::const_iterator d = m_ep_discarded.begin (); + std::vector::const_iterator i = m_ep_intra_polygon.begin (); std::vector::const_iterator ep = m_ep.begin (); while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) { bool use_result = true; @@ -75,9 +76,10 @@ Edge2EdgeCheckBase::prepare_next_pass () ++d; } if (use_result) { - put (*ep); + put (*ep, *i); } ++ep; + ++i; } } @@ -182,7 +184,10 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size // pass we will eliminate those which are shielded completely (with shielding) // and/or compute the negative edges. size_t n = m_ep.size (); + m_ep.push_back (ep); + m_ep_intra_polygon.push_back (p1 == p2); + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); @@ -253,9 +258,12 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size } // for negative output edges are cancelled by short interactions perpendicular to them + // For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real + // edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the + // negative case this means we cancel a real edge. + if (m_has_negative_edge_output && - (m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end () || m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ()) && - ! (m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end () && m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) { + (m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) { // Overlap or inside checks require input from different layers if ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { @@ -274,7 +282,10 @@ Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size if (mp_check->check (*o1, *o2, &ep)) { size_t n = m_ep.size (); + m_ep.push_back (ep); + m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index bcaa65645..a05727604 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -612,7 +612,7 @@ protected: /** * @brief Normal edge pair output (violations) */ - virtual void put (const db::EdgePair & /*edge*/) const { } + virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { } /** * @brief Negative edge output @@ -628,7 +628,7 @@ private: std::multimap, size_t> m_e2ep; std::set > m_pseudo_edges; size_t m_first_pseudo; - std::vector m_ep_discarded; + std::vector m_ep_discarded, m_ep_intra_polygon; bool m_with_shielding; bool m_has_edge_pair_output; bool m_has_negative_edge_output; @@ -646,19 +646,30 @@ class DB_PUBLIC_TEMPLATE edge2edge_check { public: edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding), mp_output (&output) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding), mp_output_inter (&output), mp_output_intra (0) + { + // .. nothing yet .. + } + + edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding), mp_output_inter (&output_inter), mp_output_intra (&output_intra) { // .. nothing yet .. } protected: - void put (const db::EdgePair &edge) const + void put (const db::EdgePair &edge, bool inter_polygon) const { - mp_output->insert (edge); + if (! inter_polygon || ! mp_output_intra) { + mp_output_inter->insert (edge); + } else { + mp_output_intra->insert (edge); + } } private: - Output *mp_output; + Output *mp_output_inter; + Output *mp_output_intra; }; /** @@ -669,24 +680,26 @@ private: */ template class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output - : public Edge2EdgeCheckBase + : public edge2edge_check { public: edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding), - mp_output (&output), + : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding), mp_l1_negative_output (&l1_negative_output), mp_l2_negative_output (&l2_negative_output) { - set_has_negative_edge_output (true); + edge2edge_check::set_has_negative_edge_output (true); + } + + edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding) + : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding), + mp_l1_negative_output (&l1_negative_output), + mp_l2_negative_output (&l2_negative_output) + { + edge2edge_check::set_has_negative_edge_output (true); } protected: - void put (const db::EdgePair &ep) const - { - mp_output->insert (ep); - } - void put_negative (const db::Edge &edge, int layer) const { if (layer == 0) { @@ -698,7 +711,6 @@ protected: } private: - Output *mp_output; NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output; }; @@ -746,35 +758,33 @@ private: */ template class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive - : public Edge2EdgeCheckBase + : public edge2edge_check { public: edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding), - mp_output (&output) + : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding) { - set_has_negative_edge_output (negative_output); - set_has_edge_pair_output (! negative_output); + edge2edge_check::set_has_negative_edge_output (negative_output); + edge2edge_check::set_has_edge_pair_output (! negative_output); + } + + edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding) + : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding) + { + edge2edge_check::set_has_negative_edge_output (negative_output); + edge2edge_check::set_has_edge_pair_output (! negative_output); } protected: void put_negative (const db::Edge &edge, int layer) const { if (layer == 0) { - mp_output->insert (db::EdgePair (edge, edge.swapped_points ())); + edge2edge_check::put (db::EdgePair (edge, edge.swapped_points ()), false); } if (layer == 1) { - mp_output->insert (db::EdgePair (edge.swapped_points (), edge)); + edge2edge_check::put (db::EdgePair (edge.swapped_points (), edge), false); } } - - void put (const db::EdgePair &edge) const - { - mp_output->insert (edge); - } - -private: - Output *mp_output; }; /** diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index b838eae10..0af08a715 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -426,13 +426,10 @@ static db::CompoundRegionOperationNode *new_width_check (db::Coord d, bool whole static db::CompoundRegionOperationNode *new_space_or_isolated_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative, bool isolated) { - if (opposite_filter != db::NoOppositeFilter || rect_filter != db::NoSideAllowed || shielded) { - // NOTE: we have to use the "foreign" scheme with a filter because only this scheme - // guarantees that all subject shapes are visited and receive all intruders. - return new_check_node (new_foreign (), db::SpaceRelation, isolated, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); - } else { - return new_check_node (db::SpaceRelation, isolated, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); - } + // NOTE: we have to use the "foreign" scheme with a filter because only this scheme + // guarantees that all subject shapes are visited and receive all intruders. Having all intruders is crucial for the + // semantics of the "drc" feature + return new_check_node (new_foreign (), db::SpaceRelation, isolated, d, whole_edges, metrics, ignore_angle, min_projection, max_projection, shielded, opposite_filter, rect_filter, negative); } static db::CompoundRegionOperationNode *new_space_check (db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) diff --git a/src/drc/unit_tests/drcGenericTests.cc b/src/drc/unit_tests/drcGenericTests.cc index f9521822b..a175c6b7b 100644 --- a/src/drc/unit_tests/drcGenericTests.cc +++ b/src/drc/unit_tests/drcGenericTests.cc @@ -218,3 +218,23 @@ TEST(15d) { run_test (_this, "15", true); } + +TEST(16) +{ + run_test (_this, "16", false); +} + +TEST(16d) +{ + run_test (_this, "16", true); +} + +TEST(17) +{ + run_test (_this, "17", false); +} + +TEST(17d) +{ + run_test (_this, "17", true); +} diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 81a76a140..b52c4213f 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1137,3 +1137,13 @@ TEST(26d_attributes) { run_test (_this, "26", true); } + +TEST(27_advancedShielding) +{ + run_test (_this, "27", false); +} + +TEST(27d_advancedShielding) +{ + run_test (_this, "27", true); +} diff --git a/testdata/drc/drcGenericTests_16.drc b/testdata/drc/drcGenericTests_16.drc new file mode 100644 index 000000000..0f3dc6b40 --- /dev/null +++ b/testdata/drc/drcGenericTests_16.drc @@ -0,0 +1,19 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) + +l1.drc(primary.interacting((space(shielded) < 1.0).polygons)).output(100, 0) +l1.drc(primary.interacting((space(transparent) < 1.0).polygons)).output(101, 0) + diff --git a/testdata/drc/drcGenericTests_16.gds b/testdata/drc/drcGenericTests_16.gds new file mode 100644 index 0000000000000000000000000000000000000000..3c12248402e9f84ba41cfe8e71b2a3b9f3095762 GIT binary patch literal 234 zcmZQzV_;&6V31*CVt>lO$RNnT#~{QYh0JE)U}E#}bYfr-VP>^+>@@d2w)~By%MSeo zv!enSWLWX&V`B^P4`5(m;b353<7EPx&c?^Yz`&p*!2JLJKOP{90YWo=0kIhvU^Gk| dD8wR&RX;*4NDl}zzd+DPW`Xsy1I=Jz000W$A%_3} literal 0 HcmV?d00001 diff --git a/testdata/drc/drcGenericTests_17.drc b/testdata/drc/drcGenericTests_17.drc new file mode 100644 index 000000000..d514e859b --- /dev/null +++ b/testdata/drc/drcGenericTests_17.drc @@ -0,0 +1,24 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) + +# advanced shielding (self, symmetric space) + +l1.drc(width(projection, shielded) < 1.0).output(100, 0) +l1.drc(width(projection, transparent) < 1.0).output(101, 0) + +l1.drc(space(projection, shielded) < 1.0).output(110, 0) +l1.drc(space(projection, transparent) < 1.0).output(111, 0) + diff --git a/testdata/drc/drcGenericTests_17.gds b/testdata/drc/drcGenericTests_17.gds new file mode 100644 index 0000000000000000000000000000000000000000..ed70cc86992309f53db7f88ed381a2fcea287c7c GIT binary patch literal 986 zcmaLVze)o^5C-rsm&@gh$>mOiO;Y%;5D~NxR1i@~BMPQS5yZmM2k-%G1xt&7jh#&% zAP*o`7M2!4ETUKhi6|IE9CtJMR4^C_zhmcScjji{!Eq`m#GK?kVu%nRi;>^Y@Evb! zV;#uF6Dw;+3!9hct)*gp_Uv@0ghJwXX%`R)4MkgXKO|ySb_L*q({T^ms_FI@CuBxdiofq}zfx)`%PYpti({w@esIB8O z4Saj1@odkiqw}OZ*Vu1f-TTxrcfhZTSaT(vHx2joJUX;1@;(XB^r6OSaG?2gu&n*i xR9oYe=la#Y_08D(Wcvlw*sJDKuA9|<{h54#?^^9Y{`jNatrzd+C(S-Cz5uGv`U3y} literal 0 HcmV?d00001 diff --git a/testdata/drc/drcGenericTests_au16.gds b/testdata/drc/drcGenericTests_au16.gds new file mode 100644 index 0000000000000000000000000000000000000000..65c08d8df35c38e6f8056d438e7e177f3617ee75 GIT binary patch literal 490 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 literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_27.drc b/testdata/drc/drcSimpleTests_27.drc new file mode 100644 index 000000000..a068af1b1 --- /dev/null +++ b/testdata/drc/drcSimpleTests_27.drc @@ -0,0 +1,24 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) + +# advanced shielding (self, symmetric space) + +l1.width(1.0, projection, shielded).output(100, 0) +l1.width(1.0, projection, transparent).output(101, 0) + +l1.space(1.0, projection, shielded).output(110, 0) +l1.space(1.0, projection, transparent).output(111, 0) + diff --git a/testdata/drc/drcSimpleTests_27.gds b/testdata/drc/drcSimpleTests_27.gds new file mode 100644 index 0000000000000000000000000000000000000000..ed70cc86992309f53db7f88ed381a2fcea287c7c GIT binary patch literal 986 zcmaLVze)o^5C-rsm&@gh$>mOiO;Y%;5D~NxR1i@~BMPQS5yZmM2k-%G1xt&7jh#&% zAP*o`7M2!4ETUKhi6|IE9CtJMR4^C_zhmcScjji{!Eq`m#GK?kVu%nRi;>^Y@Evb! zV;#uF6Dw;+3!9hct)*gp_Uv@0ghJwXX%`R)4MkgXKO|ySb_L*q({T^ms_FI@CuBxdiofq}zfx)`%PYpti({w@esIB8O z4Saj1@odkiqw}OZ*Vu1f-TTxrcfhZTSaT(vHx2joJUX;1@;(XB^r6OSaG?2gu&n*i xR9oYe=la#Y_08D(Wcvlw*sJDKuA9|<{h54#?^^9Y{`jNatrzd+C(S-Cz5uGv`U3y} literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au27.gds b/testdata/drc/drcSimpleTests_au27.gds new file mode 100644 index 0000000000000000000000000000000000000000..e36adc82a839e7e86afd039504f4cac46db86799 GIT binary patch literal 6298 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 S=!x_1__tEO%lv=P0s0R`pHD0R literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSuiteTests.drc b/testdata/drc/drcSuiteTests.drc index 26e7d5e01..9574e8e96 100644 --- a/testdata/drc/drcSuiteTests.drc +++ b/testdata/drc/drcSuiteTests.drc @@ -321,19 +321,16 @@ def run_testsuite(dm, ic, tiled = false, hier = false) lb += 10 #230 message "--- isolated #{lb}" - b.isolated(0.4).polygons.output(lb, dm) - b.isolated(0.4).polygons.xor(b.iso(0.4).polygons).is_empty? == true || raise("xor not empty") - b.isolated(0.4, euclidian).polygons.output(lb + 1, dm) - b.isolated(0.4, square).polygons.output(lb + 2, dm) - b.isolated(0.4, projection).polygons.output(lb + 3, dm) - b.isolated(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) - b.isolated(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) - b.isolated(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) - if has_float_range - b.isolated(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) - else - b.isolated(0.4, euclidian, projection_limits(0, 0.4)).polygons.output(lb + 7, dm) - end + b.isolated(0.4, shielded).polygons.output(lb, dm) + b.isolated(0.4, shielded).polygons.xor(b.iso(0.4, shielded).polygons).is_empty? == true || raise("xor not empty") + b.isolated(0.4, shielded, euclidian).polygons.output(lb + 1, dm) + b.isolated(0.4, shielded, square).polygons.output(lb + 2, dm) + b.isolated(0.4, shielded, projection).polygons.output(lb + 3, dm) + b.isolated(0.4, shielded, euclidian, whole_edges).polygons.output(lb + 4, dm) + b.isolated(0.4, shielded, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + b.isolated(0.4, shielded, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + b.isolated(0.4, shielded, euclidian, projecting < 0.4).polygons.output(lb + 7, dm) + b.isolated(0.4, transparent).polygons.output(lb + 8, dm) lb += 10 #240 message "--- notch #{lb}" diff --git a/testdata/drc/drcSuiteTests_au2.oas b/testdata/drc/drcSuiteTests_au2.oas index 6c57f6f2189c4161ce8a9f96621e46d3638b5217..8a55289758cf39aa8ca1221b7cb99771546e442c 100644 GIT binary patch literal 82267 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