From a551300e0d7ebece675b99b7012b85e87b7db207 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Sep 2018 22:32:36 +0200 Subject: [PATCH] WIP: Some refactoring (hier processing in extra file), unit tests, bug fixes --- src/db/db/dbTestSupport.cc | 15 +- src/db/db/dbTestSupport.h | 1 + .../tools/netx/db_plugin/dbHierProcessor.cc | 556 ++++++++++++++++++ .../tools/netx/db_plugin/dbHierProcessor.h | 150 +++++ .../tools/netx/db_plugin/dbNetExtractor.cc | 504 ---------------- .../tools/netx/db_plugin/db_plugin.pro | 2 + src/plugins/tools/netx/testdata/hlp1.oas | Bin 0 -> 583 bytes .../netx/unit_tests/dbHierProcessorTests.cc | 112 ++++ .../tools/netx/unit_tests/unit_tests.pro | 1 + 9 files changed, 832 insertions(+), 509 deletions(-) create mode 100644 src/plugins/tools/netx/db_plugin/dbHierProcessor.cc create mode 100644 src/plugins/tools/netx/db_plugin/dbHierProcessor.h create mode 100644 src/plugins/tools/netx/testdata/hlp1.oas create mode 100644 src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 1698385f6..f8d7516e0 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -50,6 +50,9 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp); } + const db::Layout *subject = 0; + db::Layout layout2; + std::string tmp_file; db::SaveLayoutOptions options; @@ -67,10 +70,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: writer.write (const_cast (layout), stream); } - const db::Layout *subject = 0; - db::Layout layout2; - - if (norm != NoNormalization) { + if (norm == WriteGDS2 || norm == WriteOAS) { // read all layers from the original layout, so the layer table is the same for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { @@ -121,7 +121,12 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: db::Reader reader (stream); reader.read (layout_au, options); - equal = db::compare_layouts (*subject, layout_au, (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | db::layout_diff::f_flatten_array_insts /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/, tolerance, 100 /*max diff lines*/); + equal = db::compare_layouts (*subject, layout_au, + (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) + | (norm == AsPolygons ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) + | db::layout_diff::f_flatten_array_insts + /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ + , tolerance, 100 /*max diff lines*/); if (equal && n > 0) { tl::info << tl::sprintf ("Found match on golden reference variant %s", fn); } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index a79fcc469..82be9cf34 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -46,6 +46,7 @@ class LayerMap; enum NormalizationMode { NoNormalization, // no normalization - take the test subject as it is + AsPolygons, // paths and boxes are treated as polygons WriteGDS2, // normalize subject by writing to GDS2 and reading back WriteOAS // normalize subject by writing to OASIS and reading back }; diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc new file mode 100644 index 000000000..c84e9fae0 --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.cc @@ -0,0 +1,556 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "dbHierProcessor.h" +#include "dbBoxScanner.h" +#include "dbRecursiveShapeIterator.h" +#include "dbBoxConvert.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "tlLog.h" + +namespace db +{ + + +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperation implementation + +namespace { + +class PolygonRefGenerator + : public PolygonSink +{ +public: + /** + * @brief Constructor specifying an external vector for storing the polygons + */ + PolygonRefGenerator (db::Layout *layout, std::set &polyrefs) + : PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs) + { } + + /** + * @brief Implementation of the PolygonSink interface + */ + virtual void put (const db::Polygon &polygon) + { + mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); + } + +private: + db::Layout *mp_layout; + std::set *mp_polyrefs; +}; + +} + +BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +void +BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const +{ + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (std::map >::const_iterator r = interactions.begin (); r != interactions.end (); ++r) { + + // TODO: vector could be set + bool found = false; + for (std::vector::const_iterator i = r->second.begin (); i != r->second.end () && ! found; ++i) { + found = (*i == r->first); + } + if (found) { + // shortcut (and: keep, not: drop) + if (m_is_and) { + result.insert (r->first); + } + } else if (r->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (r->first); + } + } else { + for (db::PolygonRef::polygon_edge_iterator e = r->first.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + others.insert (r->second.begin (), r->second.end ()); + } + + } + + if (! others.empty () || p1 > 0) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.process (pg, op); + + } +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContext implementation + +LocalProcessorCellContext::LocalProcessorCellContext () +{ + // .. nothing yet .. +} + +void +LocalProcessorCellContext::add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) +{ + m_drops.push_back (LocalProcessorCellDrop (parent_context, parent, cell_inst)); +} + +void +LocalProcessorCellContext::propagate (const std::set &res) +{ + if (res.empty ()) { + return; + } + + for (std::vector::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { + + tl_assert (d->parent_context != 0); + tl_assert (d->parent != 0); + + db::Layout *layout = d->parent->layout (); + + for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { + db::Polygon poly = r->obj ().transformed (d->cell_inst * db::ICplxTrans (r->trans ())); + d->parent_context->propagated ().insert (db::PolygonRef (poly, layout->shape_repository ())); + } + + } + +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessorCellContexts implementation + +LocalProcessorCellContexts::LocalProcessorCellContexts () +{ + // .. nothing yet .. +} + +db::LocalProcessorCellContext * +LocalProcessorCellContexts::find_context (const key_type &intruders) +{ + std::map::iterator c = m_contexts.find (intruders); + return c != m_contexts.end () ? &c->second : 0; +} + +db::LocalProcessorCellContext * +LocalProcessorCellContexts::create (const key_type &intruders) +{ + return &m_contexts[intruders]; +} + +void +LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *proc) +{ + bool first = true; + std::set common; + + for (std::map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { + + if (first) { + + common = c->second.propagated (); + proc->compute_local_cell (cell, c->first, common); + first = false; + + } else { + + std::set res = c->second.propagated (); + proc->compute_local_cell (cell, c->first, res); + + if (common.empty ()) { + + c->second.propagate (res); + + } else if (res != common) { + + std::set lost; + std::set_difference (common.begin (), common.end (), res.begin (), res.end (), std::inserter (lost, lost.end ())); + + if (! lost.empty ()) { + + std::set new_common; + std::set_intersection (common.begin (), common.end (), res.begin (), res.end (), std::inserter (new_common, new_common.end ())); + common.swap (new_common); + + for (std::map::iterator cc = m_contexts.begin (); cc != c; ++cc) { + cc->second.propagate (lost); + } + + } + + std::set gained; + std::set_difference (res.begin (), res.end (), common.begin (), common.end (), std::inserter (gained, gained.end ())); + c->second.propagate (gained); + + } + + } + + } + + proc->push_results (cell, common); +} + +// --------------------------------------------------------------------------------------------- +// Helper classes for the LocalProcessor + +namespace +{ + +inline unsigned int polygon_ref_flags () +{ + return 1 << db::ShapeIterator::PolygonRef; +} + +struct InteractionRegistrationShape2Shape + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationShape2Shape (std::map > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::PolygonRef *ref1, int, const db::PolygonRef *ref2, int) + { + (*mp_result) [*ref1].push_back (*ref2); + } + +private: + std::map > *mp_result; +}; + +struct InteractionRegistrationShape2Inst + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, std::map > *result) + : mp_layout (layout), m_intruder_layer (intruder_layer), m_inst_bc (*layout, intruder_layer), mp_result (result) + { + // nothing yet .. + } + + void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) + { + const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); + db::Box region = ref->box () & m_inst_bc (*inst); + if (! region.empty ()) { + + // @@@ TODO: should be lighter, cache, handle arrays .. + db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); + si.shape_flags (polygon_ref_flags ()); + while (! si.at_end ()) { + + // @@@ should be easier to transform references + const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); + db::Polygon poly = ref2->obj ().transformed (si.trans () * db::ICplxTrans (ref->trans ())); + (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); + + ++si; + + } + + } + } + +private: + db::Layout *mp_layout; + unsigned int m_intruder_layer; + db::box_convert m_inst_bc; + std::map > *mp_result; +}; + +struct InteractionRegistrationInst2Inst + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationInst2Inst (std::map, std::set > > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) + { + (*mp_result) [inst1].first.insert (inst2); + } + +private: + std::map, std::set > > *mp_result; +}; + +struct InteractionRegistrationInst2Shape + : db::box_scanner_receiver2 +{ +public: + InteractionRegistrationInst2Shape (std::map, std::set > > *result) + : mp_result (result) + { + // nothing yet .. + } + + void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) + { + (*mp_result) [inst].second.insert (*ref); + } + +private: + std::map, std::set > > *mp_result; +}; + +} + +// --------------------------------------------------------------------------------------------- +// LocalProcessor implementation + +LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer) + : mp_layout (layout), mp_top (top), m_scope_layer (scope_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) +{ + // .. nothing yet .. +} + +void LocalProcessor::run () +{ + try { + + mp_layout->update (); + mp_layout->start_changes (); + + std::pair, std::set > intruders; + compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); + compute_results (); + + mp_layout->end_changes (); + + } catch (...) { + mp_layout->end_changes (); + throw; + } +} + +void LocalProcessor::push_results (db::Cell *cell, const std::set &result) +{ + if (! result.empty ()) { + cell->shapes (m_output_layer).insert (result.begin (), result.end ()); + } +} + +void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) +{ + db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; + + db::LocalProcessorCellContext *context = contexts.find_context (intruders); + if (context) { + context->add (parent_context, parent, cell_inst); + return; + } + + context = contexts.create (intruders); + context->add (parent_context, parent, cell_inst); + + const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + + db::box_convert inst_bcs (*mp_layout, m_scope_layer); + db::box_convert inst_bci (*mp_layout, m_intruder_layer); + db::box_convert inst_bcii (*mp_layout, m_intruder_layer); + + if (! cell->begin ().at_end ()) { + + typedef std::pair, std::set > interaction_value_type; + + std::map interactions; + + // @@@ TODO: don't do this in AND more for instances without an intruder + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); + } + + { + db::box_scanner2 scanner; + InteractionRegistrationInst2Inst rec (&interactions); + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert1 (&i->cell_inst (), 0); + scanner.insert2 (&i->cell_inst (), 0); + } + + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + + scanner.process (rec, 0, inst_bcs, inst_bci); + } + + { + db::box_scanner2 scanner; + InteractionRegistrationInst2Shape rec (&interactions); + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert1 (&i->cell_inst (), 0); + } + + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + for (db::Shapes::shape_iterator i = shapes_intruders.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + scanner.process (rec, 0, inst_bcs, db::box_convert ()); + } + + for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { + + std::pair, std::set > intruders_below; + intruders_below.second = i->second.second; + + db::Cell &child_cell = mp_layout->cell (i->first->object ().cell_index ()); + + for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { + + db::ICplxTrans tn = i->first->complex_trans (*n); + db::ICplxTrans tni = tn.inverted (); + db::Box nbox = tn * child_cell.bbox (m_intruder_layer); + + if (! nbox.empty ()) { + + intruders_below.first.clear (); + + // @@@ TODO: in some cases, it may be possible to optimize this for arrays + + for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { + for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { + intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * (*j)->complex_trans (*k))); + } + } + + compute_contexts (context, cell, &child_cell, tn, intruders_below); + + } + + } + + } + + } +} + +void +LocalProcessor::compute_results () +{ + for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { + + contexts_per_cell_type::iterator cpc = m_contexts_per_cell.find (&mp_layout->cell (*bu)); + if (cpc != m_contexts_per_cell.end ()) { + cpc->second.compute_results (cpc->first, this); + m_contexts_per_cell.erase (cpc); + } + + } + +} + +void +LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const +{ + const db::Shapes &shapes_scope = cell->shapes (m_scope_layer); + const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); + + // local shapes vs. child cell + + std::map > interactions; + db::box_convert inst_bci (*mp_layout, m_intruder_layer); + + // @@@ TODO: don't do this in AND mode (we don't need interactions without an intruder) + for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); + } + + if (! shapes_scope.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { + + db::box_scanner2 scanner; + InteractionRegistrationShape2Shape rec (&interactions); + + for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + for (db::Shapes::shape_iterator i = shapes_intruders.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + scanner.process (rec, 0, db::box_convert (), db::box_convert ()); + + } + + if (! shapes_scope.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { + + db::box_scanner2 scanner; + InteractionRegistrationShape2Inst rec (mp_layout, m_intruder_layer, &interactions); + + for (db::Shapes::shape_iterator i = shapes_scope.begin (polygon_ref_flags ()); !i.at_end (); ++i) { + scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); + } + + for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { + scanner.insert2 (&i->cell_inst (), 0); + } + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + scanner.insert2 (i.operator-> (), 0); + } + + scanner.process (rec, 0, db::box_convert (), inst_bci); + + } + + mp_op->compute_local (mp_layout, interactions, result); +} + +} + diff --git a/src/plugins/tools/netx/db_plugin/dbHierProcessor.h b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h new file mode 100644 index 000000000..e8b6707ee --- /dev/null +++ b/src/plugins/tools/netx/db_plugin/dbHierProcessor.h @@ -0,0 +1,150 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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_dbHierProcessor +#define HDR_dbHierProcessor + +#include "dbLayout.h" +#include "dbPluginCommon.h" + +#include +#include +#include + +namespace db +{ + +class LocalProcessor; +class LocalProcessorCellContext; + +class DB_PLUGIN_PUBLIC LocalOperation +{ +public: + LocalOperation () { } + virtual ~LocalOperation () { } + + virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; +}; + +class DB_PLUGIN_PUBLIC BoolAndOrNotLocalOperation + : public LocalOperation +{ +public: + BoolAndOrNotLocalOperation (bool is_and); + + virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const; + +private: + bool m_is_and; +}; + +// @@@ TODO: should be hidden (private data?) +struct DB_PLUGIN_PUBLIC LocalProcessorCellDrop +{ + LocalProcessorCellDrop (db::LocalProcessorCellContext *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) + : parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst) + { + // .. nothing yet .. + } + + db::LocalProcessorCellContext *parent_context; + db::Cell *parent; + db::ICplxTrans cell_inst; +}; + +// @@@ TODO: should be hidden (private data?) +class DB_PLUGIN_PUBLIC LocalProcessorCellContext +{ +public: + typedef std::pair parent_inst_type; + + LocalProcessorCellContext (); + + void add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst); + void propagate (const std::set &res); + + std::set &propagated () + { + return m_propagated; + } + +private: + std::set m_propagated; + std::vector m_drops; +}; + +class DB_PLUGIN_PUBLIC LocalProcessorCellContexts +{ +public: + typedef std::pair, std::set > key_type; + + LocalProcessorCellContexts (); + + db::LocalProcessorCellContext *find_context (const key_type &intruders); + db::LocalProcessorCellContext *create (const key_type &intruders); + void compute_results (db::Cell *cell, LocalProcessor *proc); + +private: + std::map m_contexts; +}; + +class DB_PLUGIN_PUBLIC LocalProcessor +{ +public: + LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer); + void run (); + +private: + friend class LocalProcessorCellContexts; + typedef std::map contexts_per_cell_type; + + db::Layout *mp_layout; + db::Cell *mp_top; + unsigned int m_scope_layer, m_intruder_layer, m_output_layer; + contexts_per_cell_type m_contexts_per_cell; + LocalOperation *mp_op; + + void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); + void compute_results (); + void push_results (db::Cell *cell, const std::set &result); + void compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const; +}; + +} + +namespace tl +{ + +template <> +struct type_traits : public tl::type_traits +{ + // mark "LocalProcessor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif + diff --git a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc index 3bdb4b23d..fe0521e23 100644 --- a/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc +++ b/src/plugins/tools/netx/db_plugin/dbNetExtractor.cc @@ -33,510 +33,6 @@ namespace db { -// ------------------------------------------------------------------------------------------ - -class LocalOperation -{ -public: - LocalOperation () { } - - virtual void compute_local (db::Layout *layout, const std::map > &interactions, std::set &result) const = 0; -}; - -// ------------------------------------------------------------------------------------------ - -class LocalProcessorCellContext; - -struct LocalProcessorCellDrop -{ - LocalProcessorCellDrop (db::LocalProcessorCellContext *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst) - : parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst) - { - // .. nothing yet .. - } - - db::LocalProcessorCellContext *parent_context; - db::Cell *parent; - db::ICplxTrans cell_inst; -}; - -// --------------------------------------------------------------------------------------------- - -class LocalProcessorCellContext -{ -public: - typedef std::pair parent_inst_type; - - LocalProcessorCellContext () - { - // .. nothing yet .. - } - - void add (db::LocalProcessorCellContext *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst) - { - m_drops.push_back (LocalProcessorCellDrop (parent_context, parent, cell_inst)); - } - - std::set &propagated () - { - return m_propagated; - } - - void propagate (const std::set &res) - { - if (res.empty ()) { - return; - } - - for (std::vector::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { - - tl_assert (d->parent_context != 0); - tl_assert (d->parent != 0); - - db::Layout *layout = d->parent->layout (); - - for (std::set::const_iterator r = res.begin (); r != res.end (); ++r) { - db::Polygon poly = r->obj ().transformed (d->cell_inst * db::ICplxTrans (r->trans ())); - d->parent_context->propagated ().insert (db::PolygonRef (poly, layout->shape_repository ())); - } - - } - - } - -private: - std::set m_propagated; - std::vector m_drops; -}; - -// --------------------------------------------------------------------------------------------- - -class LocalProcessor; - -class LocalProcessorCellContexts -{ -public: - typedef std::pair, std::set > key_type; - - LocalProcessorCellContexts (); - - db::LocalProcessorCellContext *find_context (const key_type &intruders); - db::LocalProcessorCellContext *create (const key_type &intruders); - void compute_results (db::Cell *cell, LocalProcessor *proc); - -private: - std::map m_contexts; -}; - -// --------------------------------------------------------------------------------------------- - -class LocalProcessor -{ -public: - LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer); - void run (); - -private: - friend class LocalProcessorCellContexts; - typedef std::map contexts_per_cell_type; - - db::Layout *mp_layout; - db::Cell *mp_top; - unsigned int m_scope_layer, m_intruder_layer, m_output_layer; - contexts_per_cell_type m_contexts_per_cell; - LocalOperation *mp_op; - - void compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders); - void compute_results (); - void push_results (db::Cell *cell, const std::set &result); - void compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const; -}; - -// --------------------------------------------------------------------------------------------- - -LocalProcessorCellContexts::LocalProcessorCellContexts () -{ - // .. nothing yet .. -} - -db::LocalProcessorCellContext * -LocalProcessorCellContexts::find_context (const key_type &intruders) -{ - std::map::iterator c = m_contexts.find (intruders); - return c != m_contexts.end () ? &c->second : 0; -} - -db::LocalProcessorCellContext * -LocalProcessorCellContexts::create (const key_type &intruders) -{ - return &m_contexts[intruders]; -} - -void -LocalProcessorCellContexts::compute_results (db::Cell *cell, LocalProcessor *proc) -{ - bool first = true; - std::set common; - - for (std::map::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) { - - if (first) { - - common = c->second.propagated (); - proc->compute_local_cell (cell, c->first, common); - first = false; - - } else { - - std::set res = c->second.propagated (); - proc->compute_local_cell (cell, c->first, res); - - if (common.empty ()) { - - c->second.propagate (res); - - } else if (res != common) { - - std::set lost; - std::set_difference (common.begin (), common.end (), res.begin (), res.end (), std::inserter (lost, lost.end ())); - - if (! lost.empty ()) { - - std::set new_common; - std::set_intersection (common.begin (), common.end (), res.begin (), res.end (), std::inserter (new_common, new_common.end ())); - common.swap (new_common); - - for (std::map::iterator cc = m_contexts.begin (); cc != c; ++cc) { - cc->second.propagate (lost); - } - - } - - std::set gained; - std::set_difference (res.begin (), res.end (), common.begin (), common.end (), std::inserter (gained, gained.end ())); - c->second.propagate (gained); - - } - - } - - } - - proc->push_results (cell, common); -} - -// --------------------------------------------------------------------------------------------- - -namespace -{ - -struct InteractionRegistrationShape2Shape - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationShape2Shape (std::map > *result) - : mp_result (result) - { - // nothing yet .. - } - - void add (const db::PolygonRef *ref1, int, const db::PolygonRef *ref2, int) - { - (*mp_result) [*ref1].push_back (*ref2); - } - -private: - std::map > *mp_result; -}; - -struct InteractionRegistrationShape2Inst - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationShape2Inst (db::Layout *layout, unsigned int intruder_layer, std::map > *result) - : mp_layout (layout), m_intruder_layer (intruder_layer), m_inst_bc (*layout, intruder_layer), mp_result (result) - { - // nothing yet .. - } - - void add (const db::PolygonRef *ref, int, const db::CellInstArray *inst, int) - { - const db::Cell &intruder_cell = mp_layout->cell (inst->object ().cell_index ()); - db::Box region = ref->box () & m_inst_bc (*inst); - if (! region.empty ()) { - - // @@@ TODO: should be lighter, cache, handle arrays .. - db::RecursiveShapeIterator si (*mp_layout, intruder_cell, m_intruder_layer, region); - si.shape_flags (db::ShapeIterator::PolygonRef); - while (! si.at_end ()) { - - // @@@ should be easier to transform references - const db::PolygonRef *ref2 = si.shape ().basic_ptr (db::PolygonRef::tag ()); - db::Polygon poly = ref2->obj ().transformed (si.trans () * db::ICplxTrans (ref->trans ())); - (*mp_result)[*ref].push_back (db::PolygonRef (poly, mp_layout->shape_repository())); - - ++si; - - } - - } - } - -private: - db::Layout *mp_layout; - unsigned int m_intruder_layer; - db::box_convert m_inst_bc; - std::map > *mp_result; -}; - -struct InteractionRegistrationInst2Inst - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationInst2Inst (std::map, std::set > > *result) - : mp_result (result) - { - // nothing yet .. - } - - void add (const db::CellInstArray *inst1, int, const db::CellInstArray *inst2, int) - { - (*mp_result) [inst1].first.insert (inst2); - } - -private: - std::map, std::set > > *mp_result; -}; - -struct InteractionRegistrationInst2Shape - : db::box_scanner_receiver2 -{ -public: - InteractionRegistrationInst2Shape (std::map, std::set > > *result) - : mp_result (result) - { - // nothing yet .. - } - - void add (const db::CellInstArray *inst, int, const db::PolygonRef *ref, int) - { - (*mp_result) [inst].second.insert (*ref); - } - -private: - std::map, std::set > > *mp_result; -}; - -} - -// --------------------------------------------------------------------------------------------- - -LocalProcessor::LocalProcessor (db::Layout *layout, db::Cell *top, LocalOperation *op, unsigned int scope_layer, unsigned int intruder_layer, unsigned int output_layer) - : mp_layout (layout), mp_top (top), m_scope_layer (scope_layer), m_intruder_layer (intruder_layer), m_output_layer (output_layer), mp_op (op) -{ - // .. nothing yet .. -} - -void LocalProcessor::run () -{ - try { - - mp_layout->update (); - mp_layout->start_changes (); - - std::pair, std::set > intruders; - compute_contexts (0, 0, mp_top, db::ICplxTrans (), intruders); - compute_results (); - - mp_layout->end_changes (); - - } catch (...) { - mp_layout->end_changes (); - throw; - } -} - -void LocalProcessor::push_results (db::Cell *cell, const std::set &result) -{ - if (! result.empty ()) { - cell->shapes (m_output_layer).insert (result.begin (), result.end ()); - } -} - -void LocalProcessor::compute_contexts (db::LocalProcessorCellContext *parent_context, db::Cell *parent, db::Cell *cell, const db::ICplxTrans &cell_inst, const std::pair, std::set > &intruders) -{ - db::LocalProcessorCellContexts &contexts = m_contexts_per_cell [cell]; - - db::LocalProcessorCellContext *context = contexts.find_context (intruders); - if (context) { - context->add (parent_context, parent, cell_inst); - return; - } - - context = contexts.create (intruders); - context->add (parent_context, parent, cell_inst); - - const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); - - db::box_convert inst_bcs (*mp_layout, m_scope_layer); - db::box_convert inst_bci (*mp_layout, m_intruder_layer); - db::box_convert inst_bcii (*mp_layout, m_intruder_layer); - - if (! cell->begin ().at_end ()) { - - typedef std::pair, std::set > interaction_value_type; - - std::map interactions; - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - interactions.insert (std::make_pair (&i->cell_inst (), interaction_value_type ())); - } - - { - db::box_scanner2 scanner; - InteractionRegistrationInst2Inst rec (&interactions); - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert1 (&i->cell_inst (), 0); - scanner.insert2 (&i->cell_inst (), 0); - } - - for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - - scanner.process (rec, 0, inst_bcs, inst_bci); - } - - { - db::box_scanner2 scanner; - InteractionRegistrationInst2Shape rec (&interactions); - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert1 (&i->cell_inst (), 0); - } - - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - for (db::Shapes::shape_iterator i = shapes_intruders.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - scanner.process (rec, 0, inst_bcs, db::box_convert ()); - } - - for (std::map, std::set > >::const_iterator i = interactions.begin (); i != interactions.end (); ++i) { - - std::pair, std::set > intruders_below; - intruders_below.second = i->second.second; - - db::Cell &child_cell = mp_layout->cell (i->first->object ().cell_index ()); - - for (db::CellInstArray::iterator n = i->first->begin (); ! n.at_end (); ++n) { - - db::ICplxTrans tn = i->first->complex_trans (*n); - db::ICplxTrans tni = tn.inverted (); - db::Box nbox = tn * child_cell.bbox (m_intruder_layer); - - if (! nbox.empty ()) { - - intruders_below.first.clear (); - - // @@@ TODO: in some cases, it may be possible to optimize this for arrays - - for (std::set::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) { - for (db::CellInstArray::iterator k = (*j)->begin_touching (nbox.enlarged (db::Vector (-1, -1)), inst_bcii); ! k.at_end (); ++k) { - intruders_below.first.insert (db::CellInstArray (db::CellInst ((*j)->object ().cell_index ()), tni * (*j)->complex_trans (*k))); - } - } - - compute_contexts (context, cell, &child_cell, tn, intruders_below); - - } - - } - - } - - } -} - -void -LocalProcessor::compute_results () -{ - for (db::Layout::bottom_up_const_iterator bu = mp_layout->begin_bottom_up (); bu != mp_layout->end_bottom_up (); ++bu) { - - contexts_per_cell_type::iterator cpc = m_contexts_per_cell.find (&mp_layout->cell (*bu)); - if (cpc != m_contexts_per_cell.end ()) { - cpc->second.compute_results (cpc->first, this); - m_contexts_per_cell.erase (cpc); - } - - } - -} - -void -LocalProcessor::compute_local_cell (db::Cell *cell, const std::pair, std::set > &intruders, std::set &result) const -{ - const db::Shapes &shapes_scope = cell->shapes (m_scope_layer); - const db::Shapes &shapes_intruders = cell->shapes (m_intruder_layer); - - // local shapes vs. child cell - - std::map > interactions; - db::box_convert inst_bci (*mp_layout, m_intruder_layer); - - for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - interactions.insert (std::make_pair (*i->basic_ptr (db::PolygonRef::tag ()), std::vector ())); - } - - if (! shapes_scope.empty () && ! (shapes_intruders.empty () && intruders.second.empty ())) { - - db::box_scanner2 scanner; - InteractionRegistrationShape2Shape rec (&interactions); - - for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - for (std::set::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - for (db::Shapes::shape_iterator i = shapes_intruders.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert2 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - scanner.process (rec, 0, db::box_convert (), db::box_convert ()); - - } - - if (! shapes_scope.empty () && ! (cell->begin ().at_end () && intruders.first.empty ())) { - - db::box_scanner2 scanner; - InteractionRegistrationShape2Inst rec (mp_layout, m_intruder_layer, &interactions); - - for (db::Shapes::shape_iterator i = shapes_scope.begin (db::ShapeIterator::PolygonRef); !i.at_end (); ++i) { - scanner.insert1 (i->basic_ptr (db::PolygonRef::tag ()), 0); - } - - for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) { - scanner.insert2 (&i->cell_inst (), 0); - } - for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { - scanner.insert2 (i.operator-> (), 0); - } - - scanner.process (rec, 0, db::box_convert (), inst_bci); - - } - - mp_op->compute_local (mp_layout, interactions, result); -} - -// ------------------------------------------------------------------------------------------ - NetExtractor::NetExtractor() : mp_orig_layout (0), mp_layout (0) { diff --git a/src/plugins/tools/netx/db_plugin/db_plugin.pro b/src/plugins/tools/netx/db_plugin/db_plugin.pro index 9126b6af1..089fef33e 100644 --- a/src/plugins/tools/netx/db_plugin/db_plugin.pro +++ b/src/plugins/tools/netx/db_plugin/db_plugin.pro @@ -6,9 +6,11 @@ include($$PWD/../../../db_plugin.pri) HEADERS = \ dbNetExtractor.h \ + dbHierProcessor.h \ SOURCES = \ dbNetExtractor.cc \ + dbHierProcessor.cc \ dbNetExtractorPlugin.cc \ gsiDeclDbNetExtractor.cc \ diff --git a/src/plugins/tools/netx/testdata/hlp1.oas b/src/plugins/tools/netx/testdata/hlp1.oas new file mode 100644 index 0000000000000000000000000000000000000000..17754711a3ba196f11516e58c629422d70438e83 GIT binary patch literal 583 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4AcB`cq?(bTL2`x21eqJMA{8G* zX9zYNW%{5eQn5q)gXoKYOgqFyYOaW_5P48`LzbK2jG};uCDR3s4b4mpA_WbSJ)%JB zgXj)%peAH36>s?7s6RN!$RHB%Kz@~#Z2Y5b+ zg6tPH0I52_1JaPn$h1M}fi~DNqBbD$7wkWF0(EmUJP;Cqs9d4@pbW@jIl%J~sQ-X! MgAr51NCpfH0QW-V3jhEB literal 0 HcmV?d00001 diff --git a/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc new file mode 100644 index 000000000..b06beba68 --- /dev/null +++ b/src/plugins/tools/netx/unit_tests/dbHierProcessorTests.cc @@ -0,0 +1,112 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 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 "tlUnitTest.h" +#include "tlStream.h" +#include "dbHierProcessor.h" +#include "dbTestSupport.h" +#include "dbReader.h" +#include "dbCommonReader.h" + +static std::string testdata (const std::string &fn) +{ + return tl::testsrc () + "/src/plugins/tools/netx/testdata/" + fn; +} + +enum TestMode +{ + TMAnd = 0, + TMNot = 1 +}; + +/** + * @brief Turns a layer into polygons and polygon references + * The hierarchical processor needs polygon references and can't work on polygons directly. + */ +void normalize_layer (db::Layout &layout, unsigned int layer) +{ + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + db::Shapes s; + s.swap (c->shapes (layer)); + for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); !i.at_end (); ++i) { + db::Polygon poly; + i->polygon (poly); + c->shapes (layer).insert (db::PolygonRef (poly, layout.shape_repository ())); + } + } +} + +void run_test_bool (tl::TestBase *_this, const char *file, TestMode mode, int out_layer_num) +{ + db::Layout layout_org; + + unsigned int l1 = 0, l2 = 0, lout = 0; + db::LayerMap lmap; + + { + tl::InputStream stream (testdata (file)); + db::Reader reader (stream); + + db::LayerProperties p; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (1, 0), l1 = layout_org.insert_layer ()); + layout_org.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (2, 0), l2 = layout_org.insert_layer ()); + layout_org.set_properties (l2, p); + + p.layer = out_layer_num; + p.datatype = 0; + lmap.map (db::LDPair (out_layer_num, 0), lout = layout_org.insert_layer ()); + layout_org.set_properties (lout, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (layout_org, options); + } + + layout_org.clear_layer (lout); + normalize_layer (layout_org, l1); + normalize_layer (layout_org, l2); + + db::BoolAndOrNotLocalOperation op (mode == TMAnd); + db::LocalProcessor proc (&layout_org, &layout_org.cell (*layout_org.begin_top_down ()), &op, l1, l2, lout); + proc.run (); + + db::compare_layouts (_this, layout_org, testdata (file), lmap, false /*skip other layers*/, db::AsPolygons); +} + +TEST(BasicAnd1) +{ + run_test_bool (_this, "hlp1.oas", TMAnd, 100); +} + +TEST(BasicNot1) +{ + run_test_bool (_this, "hlp1.oas", TMNot, 101); +} diff --git a/src/plugins/tools/netx/unit_tests/unit_tests.pro b/src/plugins/tools/netx/unit_tests/unit_tests.pro index 5f51b3fff..a5e77b073 100644 --- a/src/plugins/tools/netx/unit_tests/unit_tests.pro +++ b/src/plugins/tools/netx/unit_tests/unit_tests.pro @@ -7,6 +7,7 @@ include($$PWD/../../../../lib_ut.pri) SOURCES = \ dbNetExtractorTests.cc \ + dbHierProcessorTests.cc \ INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common