diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 2baec09f3..9c20d77d2 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -30,12 +30,34 @@ #include "dbDeepShapeStore.h" #include "gsiExpression.h" #include "tlCommandLineParser.h" +#include "tlThreads.h" -class CountingInserter +namespace { + +// --------------------------------------------------------------------- + +class HealingCountingReceiver + : public db::TileOutputReceiver { public: - CountingInserter () - : m_count (0) + HealingCountingReceiver (size_t *count, bool healing); + + virtual void put (size_t /*ix*/, size_t /*iy*/, const db::Box &tile, size_t /*id*/, const tl::Variant &obj, double /*dbu*/, const db::ICplxTrans & /*trans*/, bool clip); + virtual void finish (bool); + + void keep_for_healing (const db::Polygon &poly); + +private: + size_t *mp_count; + db::Region m_for_healing; + bool m_healing; +}; + +class HealingCountingInserter +{ +public: + HealingCountingInserter (const db::Box &tile, bool healing, HealingCountingReceiver *rec) + : m_count (0), mp_tile (&tile), m_healing (healing), mp_receiver (rec) { // .. nothing yet .. } @@ -46,6 +68,15 @@ public: m_count += 1; } + void operator() (const db::Polygon &poly) + { + if (m_healing && ! poly.box ().inside (mp_tile->enlarged (db::Vector (-1, -1)))) { + mp_receiver->keep_for_healing (poly); + } else { + m_count += 1; + } + } + size_t count () const { return m_count; @@ -53,29 +84,140 @@ public: private: size_t m_count; + const db::Box *mp_tile; + bool m_healing; + HealingCountingReceiver *mp_receiver; }; -class CountingReceiver +HealingCountingReceiver::HealingCountingReceiver (size_t *count, bool healing) + : mp_count (count), m_healing (healing) +{ + // .. nothing yet .. +} + +void +HealingCountingReceiver::put (size_t /*ix*/, size_t /*iy*/, const db::Box &tile, size_t /*id*/, const tl::Variant &obj, double /*dbu*/, const db::ICplxTrans & /*trans*/, bool clip) +{ + HealingCountingInserter inserter (tile, m_healing, this); + db::insert_var (inserter, obj, tile, clip); + *mp_count += inserter.count (); +} + +void +HealingCountingReceiver::keep_for_healing (const db::Polygon &poly) +{ + m_for_healing.insert (poly); +} + +void +HealingCountingReceiver::finish (bool) +{ + if (m_healing) { + *mp_count += m_for_healing.merged ().count (); + } +} + +// --------------------------------------------------------------------- + +class HealingTileLayoutOutputReceiver : public db::TileOutputReceiver { public: - CountingReceiver (size_t *count) - : mp_count (count) + HealingTileLayoutOutputReceiver (db::Layout *layout, db::Cell *cell, unsigned int layer, bool healing); + + void put (size_t /*ix*/, size_t /*iy*/, const db::Box &tile, size_t /*id*/, const tl::Variant &obj, double dbu, const db::ICplxTrans &trans, bool clip); + + void begin (size_t /*nx*/, size_t /*ny*/, const db::DPoint & /*p0*/, double /*dx*/, double /*dy*/, const db::DBox & /*frame*/); + void finish (bool /*success*/); + + void keep_for_healing (const db::Polygon &poly); + void output (const db::Polygon &poly); + +private: + db::Layout *mp_layout; + db::Cell *mp_cell; + unsigned int m_layer; + db::Region m_for_healing; + bool m_healing; + tl::Mutex m_mutex; +}; + +class HealingTileLayoutOutputInserter +{ +public: + HealingTileLayoutOutputInserter (const db::Box &tile, bool healing, const db::ICplxTrans &trans, HealingTileLayoutOutputReceiver *rec) + : mp_tile (&tile), m_healing (healing), mp_trans (&trans), mp_receiver (rec) { // .. nothing yet .. } - virtual void put (size_t /*ix*/, size_t /*iy*/, const db::Box &tile, size_t /*id*/, const tl::Variant &obj, double /*dbu*/, const db::ICplxTrans & /*trans*/, bool clip) + template + void operator() (const T & /*t*/) { - CountingInserter inserter; - db::insert_var (inserter, obj, tile, clip); - *mp_count += inserter.count (); + // .. ignore other shapes + } + + void operator() (const db::Polygon &poly) + { + if (m_healing && ! poly.box ().inside (mp_tile->enlarged (db::Vector (-1, -1)))) { + mp_receiver->keep_for_healing (*mp_trans * poly); + } else { + mp_receiver->output (*mp_trans * poly); + } } private: - size_t *mp_count; + const db::Box *mp_tile; + bool m_healing; + const db::ICplxTrans *mp_trans; + HealingTileLayoutOutputReceiver *mp_receiver; }; +HealingTileLayoutOutputReceiver::HealingTileLayoutOutputReceiver (db::Layout *layout, db::Cell *cell, unsigned int layer, bool healing) + : mp_layout (layout), mp_cell (cell), m_layer (layer), m_healing (healing) +{ + // .. nothing yet .. +} + +void +HealingTileLayoutOutputReceiver::put (size_t /*ix*/, size_t /*iy*/, const db::Box &tile, size_t /*id*/, const tl::Variant &obj, double dbu, const db::ICplxTrans &trans, bool clip) +{ + db::ICplxTrans tr (db::ICplxTrans (dbu / mp_layout->dbu ()) * trans); + HealingTileLayoutOutputInserter inserter (tile, m_healing, tr, this); + db::insert_var (inserter, obj, tile, clip); +} + +void +HealingTileLayoutOutputReceiver::begin (size_t /*nx*/, size_t /*ny*/, const db::DPoint & /*p0*/, double /*dx*/, double /*dy*/, const db::DBox & /*frame*/) +{ + mp_layout->start_changes (); +} + +void +HealingTileLayoutOutputReceiver::finish (bool /*success*/) +{ + // heal the polygons + m_for_healing.merge (); + m_for_healing.insert_into (mp_layout, mp_cell->cell_index (), m_layer); + m_for_healing.clear (); + + mp_layout->end_changes (); +} + +void +HealingTileLayoutOutputReceiver::keep_for_healing (const db::Polygon &poly) +{ + m_for_healing.insert (poly); +} + +void +HealingTileLayoutOutputReceiver::output (const db::Polygon &poly) +{ + mp_cell->shapes (m_layer).insert (poly); +} + +// --------------------------------------------------------------------- + struct ResultDescriptor { ResultDescriptor () @@ -119,6 +261,8 @@ struct ResultDescriptor } }; +// --------------------------------------------------------------------- + struct XORData { XORData () @@ -126,7 +270,8 @@ struct XORData tolerance_bump (0), dont_summarize_missing_layers (false), silent (false), no_summary (false), threads (0), - tile_size (0.0), output_layout (0), output_cell (0) + tile_size (0.0), heal_results (false), + output_layout (0), output_cell (0) { } db::Layout *layout_a, *layout_b; @@ -138,12 +283,17 @@ struct XORData bool no_summary; int threads; double tile_size; + bool heal_results; db::Layout *output_layout; db::cell_index_type output_cell; std::map, db::LPLogicalLessFunc> l2l_map; std::map, ResultDescriptor> *results; }; +} + +// --------------------------------------------------------------------- + static bool run_tiled_xor (const XORData &xor_data); static bool run_deep_xor (const XORData &xor_data); @@ -171,6 +321,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) int tolerance_bump = 10000; int threads = 1; double tile_size = 0.0; + bool heal_results = false; tl::CommandLineOptions cmd; generic_reader_options_a.add_options (cmd); @@ -219,6 +370,10 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) "In tiling mode, the layout is divided into tiles of the given size. Each tile is computed " "individually. Multiple tiles can be processed in parallel on multiple cores." ) + << tl::arg ("-m|--heal", &heal_results, "Heal results in tiling mode", + "This options runs a post-XOR merge to remove cuts implied by the tile formation. The resulting " + "feature count is closer to the real number of differences." + ) << tl::arg ("-b|--layer-bump=offset", &tolerance_bump, "Specifies the layer number offset to add for every tolerance", "This value is the number added to the original layer number to form a layer set for each tolerance " "value. If this value is set to 1000, the first tolerance value will produce XOR results on the " @@ -335,6 +490,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) xor_data.no_summary = no_summary; xor_data.threads = threads; xor_data.tile_size = tile_size; + xor_data.heal_results = heal_results; xor_data.output_layout = output_layout.get (); xor_data.output_cell = output_top; xor_data.l2l_map = l2l_map; @@ -413,7 +569,6 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) return result ? 0 : 1; } - bool run_tiled_xor (const XORData &xor_data) { db::TilingProcessor proc; @@ -422,6 +577,7 @@ bool run_tiled_xor (const XORData &xor_data) if (xor_data.tile_size > db::epsilon) { if (tl::verbosity () >= 20) { tl::log << "Tile size: " << xor_data.tile_size; + tl::log << "Healing: " << (xor_data.heal_results ? "on" : "off"); } proc.tile_size (xor_data.tile_size, xor_data.tile_size); } @@ -445,8 +601,6 @@ bool run_tiled_xor (const XORData &xor_data) int index = 1; - std::list > counters; - for (std::map >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) { if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) { @@ -509,10 +663,10 @@ bool run_tiled_xor (const XORData &xor_data) if (result.layout) { result.layer_output = result.layout->insert_layer (lp); - proc.output (out, *result.layout, result.top_cell, result.layer_output); + HealingTileLayoutOutputReceiver *receiver = new HealingTileLayoutOutputReceiver (result.layout, &result.layout->cell (result.top_cell), result.layer_output, xor_data.heal_results); + proc.output (out, 0, receiver, db::ICplxTrans ()); } else { - CountingReceiver *counter = new CountingReceiver (&result.shape_count); - counters.push_back (tl::shared_ptr (counter)); + HealingCountingReceiver *counter = new HealingCountingReceiver (&result.shape_count, xor_data.heal_results); proc.output (out, 0, counter, db::ICplxTrans ()); } diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index 2ac927059..4282b529f 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -393,6 +393,105 @@ TEST(3_Flat) ); } +TEST(3_FlatCount) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_in1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_in2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au3.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + EXPECT_EQ (cap.captured_text (), + "Layer 10/0 is not present in first layout, but in second\n" + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (shape count)\n" + " -------------------------------------------------------\n" + " 3/0 - 31\n" + " 6/0 - 217\n" + " 8/1 - 168\n" + " 10/0 - (no such layer in first layout)\n" + "\n" + ); +} + +TEST(3_FlatHeal) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_in1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_in2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au3_heal.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "--heal", "--no-summary", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (this, layout, au, db::NoNormalization); + EXPECT_EQ (cap.captured_text (), + "Layer 10/0 is not present in first layout, but in second\n" + ); +} + +TEST(3_FlatCountHeal) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_in1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_in2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au3.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "-m", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + EXPECT_EQ (cap.captured_text (), + "Layer 10/0 is not present in first layout, but in second\n" + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (shape count)\n" + " -------------------------------------------------------\n" + " 3/0 - 30\n" + " 6/0 - 41\n" + " 8/1 - 1\n" + " 10/0 - (no such layer in first layout)\n" + "\n" + ); +} + TEST(3_Deep) { tl::CaptureChannel cap; @@ -460,6 +559,39 @@ TEST(4_Flat) ); } +TEST(4_FlatHeal) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_in1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_in2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au4_heal.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "--heal", "--no-summary", "-p=1.0", "-n=4", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (this, layout, au, db::NoNormalization); + EXPECT_EQ (cap.captured_text (), + "Layer 10/0 is not present in first layout, but in second\n" + ); +} + TEST(4_Deep) { tl::CaptureChannel cap; diff --git a/testdata/bd/strmxor_au3_heal.oas b/testdata/bd/strmxor_au3_heal.oas new file mode 100644 index 000000000..0faf2da27 Binary files /dev/null and b/testdata/bd/strmxor_au3_heal.oas differ diff --git a/testdata/bd/strmxor_au4_heal.oas b/testdata/bd/strmxor_au4_heal.oas new file mode 100644 index 000000000..b6301286e Binary files /dev/null and b/testdata/bd/strmxor_au4_heal.oas differ