From f53a3be51a92e6db7df35afeb4513fdb89f1bdf6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 21 Aug 2017 23:41:52 +0200 Subject: [PATCH] strmxor enhancements: summary, bug fixes. --- src/buddies/src/bd/strmxor.cc | 212 ++++++++++++++++++++--- src/buddies/unit_tests/bdStrmxorTests.cc | 111 +++++++++++- 2 files changed, 290 insertions(+), 33 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 43ad0185f..c21aa7b3f 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -29,6 +29,98 @@ #include "gsiExpression.h" #include "tlCommandLineParser.h" +class CountingInserter +{ +public: + CountingInserter () + : m_count (0) + { + // .. nothing yet .. + } + + template + void operator() (const T & /*t*/) + { + m_count += 1; + } + + size_t count () const + { + return m_count; + } + +private: + size_t m_count; +}; + +class CountingReceiver + : public db::TileOutputReceiver +{ +public: + CountingReceiver () + : m_count (0) + { + // .. 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) + { + CountingInserter inserter; + db::insert_var (inserter, obj, tile, clip); + m_count += inserter.count (); + } + + size_t count () const + { + return m_count; + } + +private: + size_t m_count; +}; + +struct ResultDescriptor +{ + ResultDescriptor () + : layer_a (-1), layer_b (-1), layer_output (-1), layout (0), top_cell (0) + { + // .. nothing yet .. + } + + tl::shared_ptr counter; + int layer_a; + int layer_b; + int layer_output; + db::Layout *layout; + db::cell_index_type top_cell; + + size_t count () const + { + if (layout && layer_output >= 0) { + // NOTE: this assumes the output is flat + tl_assert (layout->cells () == 1); + return layout->cell (top_cell).shapes (layer_output).size (); + } else if (counter) { + return counter->count (); + } else { + return 0; + } + } + + bool is_empty () const + { + if (layout && layer_output >= 0) { + // NOTE: this assumes the output is flat + tl_assert (layout->cells () == 1); + return layout->cell (top_cell).shapes (layer_output).empty (); + } else if (counter) { + return counter->count () == 0; + } else { + return true; + } + } +}; + BD_PUBLIC int strmxor (int argc, char *argv[]) { gsi::initialize_expressions (); @@ -47,6 +139,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) std::string top_a, top_b; bool dont_summarize_missing_layers = false; bool silent = false; + bool no_summary = false; std::vector tolerances; int tolerance_bump = 10000; int threads = 1; @@ -74,6 +167,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) "In silent mode, no summary is printed, but the exit code indicates whether " "the layouts are the same (0) or differences exist (> 0)." ) + << tl::arg ("#--no-summary", &no_summary, "Don't print a summary") << tl::arg ("-l|--layer-details", &dont_summarize_missing_layers, "Treats missing layers as empty", "With this option, missing layers are treated as \"empty\" and the whole layer of the other " "layout is output. Without this option, a message is printed for missing layers instead and the " @@ -183,8 +277,6 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) l2l_map.insert (std::make_pair (*(*l).second, std::make_pair (-1, -1))).first->second.second = (*l).first; } - bool result = true; - db::TilingProcessor proc; proc.set_dbu (std::min (layout_a.dbu (), layout_b.dbu ())); proc.set_threads (std::max (1, threads)); @@ -206,26 +298,45 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) tl::log << "Layer bump for tolerance: " << tolerance_bump; } - db::Layout output_layout; - output_layout.dbu (proc.dbu ()); + std::auto_ptr output_layout; + db::cell_index_type output_top = 0; - db::cell_index_type output_top = output_layout.add_cell ("XOR"); + if (! output.empty ()) { + output_layout.reset (new db::Layout ()); + output_layout->dbu (proc.dbu ()); + output_top = output_layout->add_cell ("XOR"); + } - std::vector output_layers; + std::map, ResultDescriptor> results; + + bool result = true; int index = 1; for (std::map >::const_iterator ll = l2l_map.begin (); ll != l2l_map.end (); ++ll) { - if (ll->second.first < 0 && ! dont_summarize_missing_layers) { + if ((ll->second.first < 0 || ll->second.second < 0) && ! dont_summarize_missing_layers) { + + if (ll->second.first < 0) { + (silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second"; + } else { + (silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first"; + } - tl::warn << "Layer " << ll->first.to_string () << " is not present in first layout, but in second"; result = false; - } else if (ll->second.second < 0 && ! dont_summarize_missing_layers) { + int tol_index = 0; + for (std::vector::const_iterator t = tolerances.begin (); t != tolerances.end (); ++t) { - tl::warn << "Layer " << ll->first.to_string () << " is not present in second layout, but in first"; - result = false; + ResultDescriptor &result = results.insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; + result.layer_a = ll->second.first; + result.layer_b = ll->second.second; + result.layout = output_layout.get (); + result.top_cell = output_top; + + ++tol_index; + + } } else { @@ -246,21 +357,30 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) std::string expr = "var x=" + in_a + "^" + in_b + "; "; - int tol_index = 1; + int tol_index = 0; for (std::vector::const_iterator t = tolerances.begin (); t != tolerances.end (); ++t) { - std::string out = "o" + tl::to_string (index) + "_" + tl::to_string (tol_index); + std::string out = "o" + tl::to_string (index) + "_" + tl::to_string (tol_index + 1); db::LayerProperties lp = ll->first; if (lp.layer >= 0) { - lp.layer += (tol_index - 1) * tolerance_bump; + lp.layer += tol_index * tolerance_bump; } - unsigned int output_layer = output_layout.insert_layer (lp); - output_layers.push_back (output_layer); + ResultDescriptor &result = results.insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; + result.layer_a = ll->second.first; + result.layer_b = ll->second.second; + result.layout = output_layout.get (); + result.top_cell = output_top; - // @@@ TODO: silent mode - proc.output (out, output_layout, output_top, output_layer); + if (result.layout) { + result.layer_output = result.layout->insert_layer (lp); + proc.output (out, *result.layout, result.top_cell, result.layer_output); + } else { + CountingReceiver *counter = new CountingReceiver (); + result.counter = counter; + proc.output (out, 0, counter, db::ICplxTrans ()); + } if (*t > db::epsilon) { expr += "x=x.sized(-round(" + tl::to_string (*t) + "/_dbu)/2).sized(round(" + tl::to_string (*t) + "/_dbu)/2); "; @@ -284,28 +404,66 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) // Runs the processor - proc.execute ("Running XOR"); + if ((! silent && ! no_summary) || result || output_layout.get ()) { + proc.execute ("Running XOR"); + } - // Write the output layout + // Writes the output layout - if (! output.empty()) { + if (output_layout.get ()) { db::SaveLayoutOptions save_options; save_options.set_format_from_filename (output); tl::OutputStream stream (output); db::Writer writer (save_options); - writer.write (output_layout, stream); + writer.write (*output_layout, stream); } - // Determine the output status based on the output top cell's emptyness - - for (std::vector::const_iterator l = output_layers.begin (); l != output_layers.end () && result; ++l) { - result = output_layout.cell (output_top).bbox (*l).empty (); + // Determines the output status + for (std::map, ResultDescriptor>::const_iterator r = results.begin (); r != results.end () && result; ++r) { + result = r->second.is_empty (); } - // @@@ TODO: print a nice summary unless "silent" is set + if (! silent && ! no_summary) { + + if (result) { + tl::info << "No differences found"; + } else { + + const char *line_format = " %-10s %s"; + const char *sep = " -------------------------------------------------------"; + + tl::info << "Result summary (layers without differences are not shown):" << tl::endl; + tl::info << tl::sprintf (line_format, "Layer", "Differences (shape count)") << tl::endl << sep; + + int ti = -1; + for (std::map, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) { + + if (r->first.first != ti) { + ti = r->first.first; + if (tolerances[ti] > db::epsilon) { + tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; + tl::info << tl::sprintf (line_format, "Layer", "Differences (shape count)") << tl::endl << sep; + } + } + + if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) { + tl::info << tl::sprintf (line_format, r->first.second.to_string (), "(no such layer in first layout)"); + } else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) { + tl::info << tl::sprintf (line_format, r->first.second.to_string (), "(no such layer in second layout)"); + } else if (! r->second.is_empty ()) { + tl::info << tl::sprintf (line_format, r->first.second.to_string (), tl::to_string (r->second.count ())); + } + + } + + tl::info << ""; + + } + + } return result ? 0 : 1; } diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index 5cb181d60..baa81d647 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -29,7 +29,28 @@ BD_PUBLIC int strmxor (int argc, char *argv[]); -TEST(1) +TEST(0) +{ + ut::CaptureChannel cap; + + std::string input_a = ut::testsrc (); + input_a += "/testdata/bd/strmxor_in1.gds"; + + std::string input_b = ut::testsrc (); + input_b += "/testdata/bd/strmxor_in1.gds"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", input_a.c_str (), input_b.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 0); + + EXPECT_EQ (cap.captured_text (), + "No differences found\n" + ); +} + +TEST(1A) { ut::CaptureChannel cap; @@ -59,9 +80,87 @@ TEST(1) this->compare_layouts (layout, au, ut::NoNormalization); 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 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(1B) +{ + ut::CaptureChannel cap; + + std::string input_a = ut::testsrc (); + input_a += "/testdata/bd/strmxor_in1.gds"; + + std::string input_b = ut::testsrc (); + input_b += "/testdata/bd/strmxor_in2.gds"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", 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 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(1C) +{ + ut::CaptureChannel cap; + + std::string input_a = ut::testsrc (); + input_a += "/testdata/bd/strmxor_in1.gds"; + + std::string input_b = ut::testsrc (); + input_b += "/testdata/bd/strmxor_in2.gds"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "--no-summary", 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" + ); +} + +TEST(1D) +{ + ut::CaptureChannel cap; + + std::string input_a = ut::testsrc (); + input_a += "/testdata/bd/strmxor_in1.gds"; + + std::string input_b = ut::testsrc (); + input_b += "/testdata/bd/strmxor_in2.gds"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "-s", input_a.c_str (), input_b.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + EXPECT_EQ (cap.captured_text (), ""); +} + TEST(2) { ut::CaptureChannel cap; @@ -77,7 +176,7 @@ TEST(2) std::string output = this->tmp_file ("tmp.oas"); - const char *argv[] = { "x", "-l", input_a.c_str (), input_b.c_str (), output.c_str () }; + const char *argv[] = { "x", "--no-summary", "-l", input_a.c_str (), input_b.c_str (), output.c_str () }; EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); @@ -110,7 +209,7 @@ TEST(3) 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 (), output.c_str () }; + const char *argv[] = { "x", "--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); @@ -143,7 +242,7 @@ TEST(4) std::string output = this->tmp_file ("tmp.oas"); - const char *argv[] = { "x", "-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 () }; + const char *argv[] = { "x", "--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); @@ -176,7 +275,7 @@ TEST(5) std::string output = this->tmp_file ("tmp.oas"); - const char *argv[] = { "x", "-b=1000", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () }; + const char *argv[] = { "x", "--no-summary", "-b=1000", "-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); @@ -209,7 +308,7 @@ TEST(6) std::string output = this->tmp_file ("tmp.oas"); - const char *argv[] = { "x", "-ta=INV2", "-tb=2VNI", input_a.c_str (), input_b.c_str (), output.c_str () }; + const char *argv[] = { "x", "--no-summary", "-ta=INV2", "-tb=2VNI", input_a.c_str (), input_b.c_str (), output.c_str () }; EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);