From 9e9344fdfafe3689ac8777420473d7c3ebe9e139 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jul 2025 21:13:19 +0200 Subject: [PATCH 1/4] Introducing a check whether DBU is compatible with a given one in most readers (except PCB) --- src/db/db/dbReader.cc | 17 ++++++++++- src/db/db/dbReader.h | 28 ++++++++++++++++++ .../streamers/cif/db_plugin/dbCIFReader.cc | 1 + .../streamers/dxf/db_plugin/dbDXFReader.cc | 1 + .../gds2/db_plugin/dbGDS2ReaderBase.cc | 1 + .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 1 + .../streamers/magic/db_plugin/dbMAGReader.cc | 1 + .../streamers/maly/db_plugin/dbMALYReader.cc | 6 ++-- .../streamers/maly/db_plugin/dbMALYReader.h | 1 - .../maly/unit_tests/dbMALYReaderTests.cc | 1 + .../oasis/db_plugin/dbOASISReader.cc | 4 ++- testdata/maly/maly_test10_dbu10nm_au.oas | Bin 0 -> 1656 bytes 12 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 testdata/maly/maly_test10_dbu10nm_au.oas diff --git a/src/db/db/dbReader.cc b/src/db/db/dbReader.cc index 6561c6d9a..aa5a096f8 100644 --- a/src/db/db/dbReader.cc +++ b/src/db/db/dbReader.cc @@ -61,7 +61,7 @@ join_layer_names (std::string &s, const std::string &n) // ReaderBase implementation ReaderBase::ReaderBase () - : m_warnings_as_errors (false), m_warn_level (1), m_warn_count_for_same_message (0), m_first_warning (true) + : m_warnings_as_errors (false), m_warn_level (1), m_warn_count_for_same_message (0), m_first_warning (true), m_expected_dbu (0.0) { } @@ -82,6 +82,7 @@ ReaderBase::init (const db::LoadLayoutOptions &options) m_last_warning.clear (); m_warn_count_for_same_message = 0; m_first_warning = true; + m_expected_dbu = 0.0; } bool @@ -114,6 +115,20 @@ ReaderBase::compress_warning (const std::string &msg) } } +void +ReaderBase::set_expected_dbu (double dbu) +{ + m_expected_dbu = dbu; +} + +void +ReaderBase::check_dbu (double dbu) const +{ + if (m_expected_dbu > db::epsilon && fabs (dbu - m_expected_dbu) > db::epsilon) { + throw ReaderException (tl::sprintf (tl::to_string (tr ("Former and present database units are not compatible: %.12g (former) vs. %.12g (present)")), m_expected_dbu, dbu)); + } +} + // --------------------------------------------------------------- // Reader implementation diff --git a/src/db/db/dbReader.h b/src/db/db/dbReader.h index b13025a56..76b92cc17 100644 --- a/src/db/db/dbReader.h +++ b/src/db/db/dbReader.h @@ -138,6 +138,33 @@ public: */ int compress_warning (const std::string &msg); + /** + * @brief Sets the expected database unit + * + * With this value set, the reader can check if the present database unit is + * compatible with the expected one and either take actions to scale the layouts + * or to reject the file. + * + * Setting the value to 0 resets the expected DBU and will disable all checks + * or scaling. + */ + void set_expected_dbu (double dbu); + + /** + * @brief Gets the expected database unit + */ + double expected_dbu () const + { + return m_expected_dbu; + } + + /** + * @brief Checks the given DBU against the expected one + * + * This method will raise an exception if the database units do not match. + */ + void check_dbu (double dbu) const; + protected: virtual void init (const db::LoadLayoutOptions &options); @@ -147,6 +174,7 @@ private: std::string m_last_warning; int m_warn_count_for_same_message; bool m_first_warning; + double m_expected_dbu; }; /** diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc index 1d101f041..9b338c833 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc +++ b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc @@ -836,6 +836,7 @@ CIFReader::do_read (db::Layout &layout) db::LayoutLocker locker (&layout); double sf = 0.01 / m_dbu; + check_dbu (m_dbu); layout.dbu (m_dbu); m_cellname = "{CIF top level}"; diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc index 3abe3501f..cf7a00912 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc @@ -350,6 +350,7 @@ DXFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) db::cell_index_type top = layout.add_cell("TOP"); // TODO: make variable .. + check_dbu (m_dbu); layout.dbu (m_dbu); do_read (layout, top); cleanup (layout, top); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index dad91a563..f6a9f8889 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -236,6 +236,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) m_dbuu = dbuu; m_dbu = dbum * 1e6; /*in micron*/ + check_dbu (m_dbu); layout.dbu (m_dbu); } else { diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index e4e7dfb80..253b7b6db 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -119,6 +119,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti effective_options = *lefdef_options; } + check_dbu (effective_options.dbu ()); layout.dbu (effective_options.dbu ()); std::string base_path; diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc index 78e6420c4..56abcf8d3 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc @@ -98,6 +98,7 @@ MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) top_cell = layout.add_cell (top_cellname.c_str ()); } + check_dbu (m_dbu); layout.dbu (m_dbu); m_cells_to_read.clear (); diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index a474874a7..07c0e1de4 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -51,7 +51,6 @@ namespace db MALYReader::MALYReader (tl::InputStream &s) : m_stream (s), m_progress (tl::to_string (tr ("Reading MALY file")), 1000), - m_dbu (0.001), m_last_record_line (0) { m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); @@ -89,7 +88,10 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) init (options); const db::MALYReaderOptions &specific_options = options.get_options (); - m_dbu = specific_options.dbu; + double dbu = specific_options.dbu; + + check_dbu (dbu); + layout.dbu (dbu); set_layer_map (specific_options.layer_map); set_create_layers (specific_options.create_other_layers); diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index ea4feabbb..be63c9ccd 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -223,7 +223,6 @@ private: tl::TextInputStream m_stream; tl::AbsoluteProgress m_progress; - double m_dbu; unsigned int m_last_record_line; std::string m_record; std::string m_record_returned; diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index 95a6ff447..6068c082f 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -135,6 +135,7 @@ TEST(10_BasicLayout) { run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_lm_au.oas", "A: 10, B: 11, C: 12, D: 13"); + run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_dbu10nm_au.oas", 0, 0.01); } TEST(11_Titles) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 74fc56034..865110228 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -659,7 +659,9 @@ OASISReader::do_read (db::Layout &layout) } // compute database unit in pixel per meter - layout.dbu (1.0 / res); + double dbu = 1.0 / res; + check_dbu (dbu); + layout.dbu (dbu); // read over table offsets if required bool table_offsets_at_end = get_uint (); diff --git a/testdata/maly/maly_test10_dbu10nm_au.oas b/testdata/maly/maly_test10_dbu10nm_au.oas new file mode 100644 index 0000000000000000000000000000000000000000..f8193382b0bc249405d42be58552e67c5e80b781 GIT binary patch literal 1656 zcmY!lcJ=kt^>+;R4CduxWH!_@U`Sz<5oh9Hk>O{S5oh6NP+~~+$vtDu)nFjNdZ5Vh z0&~Z7!OfkGe@;XPt}g!{&2cM=zo+NHGR=)u{H8MxJUDPI`*Pm)GYN6}N^Oh!HqKys zYqNNB?6mWJpH?$xY~A~lnUlxY(I+zA%iqbx)!AE#!9wM6T1iS;O4@_QfIB^YXM7ao zW;{wONlRny2m3MxUKvqk76wM91#=jIh*yeNf>%^jnHeD>%qz&tC#uYZ z5aHqF;$;_AW<-dv@-p*E3;vKw;cuInZaS>wT`!@o z^Msv?Nsxh8MDT|ws#`?vGow3LNbrX+sv=QqVBnDGU;(Iucc3}=3`q{=75u@6>K2jv zOy~~g7W~13sz}rt7*kiuw=dj6+WUxO2}S z8`V&I%}1ajlRv!XL;s6LSsw;o4x#V73``7L7!RCRY1k?u_?P?OEFK092IiK1lNg&; z9A*@H&dEM&CJ4N=2Py-q;bl}}u+bO(b?g|}KKApgO>(9R{5e>ZAjZ$E%>23ifSBL`e!&AAf(L*G{9!uJ=sICHAETp$TW`I-N>y)= z?z`zl`cm1Og}Ai%7hkPnnCqmeB(%0;#gvW;hKX;tJo|n9P1*C=#>HI==j4A?zZ{pa zJNCHtli-=I(|GPnRRwduo~n3PtG9{q*l`8JG=Y>)nH!sS8g(2K+?O1Dbk4@any&im z$D&TJTf2*0c6-)=_L_Y4r|a2*+3zJC-l;vC?Zcu|d|?@r-mUmFC*QO&{pUT;Q|?Wf zjQ$%RewySfy(2tTJX`tnI<5PccU)zB_oUifQ~%PEllL{6w{Lt@q7mUbU#*UbaWN+_ T`6w|KF*YzVGBk`Vz`y_iyJ(K! literal 0 HcmV?d00001 From 0ba6064507a2d4a28ec4ddd712015a8f138d858c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jul 2025 21:17:46 +0200 Subject: [PATCH 2/4] Also supporting DBU check for PCB --- src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc index 0472b9a34..85c29c90f 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc @@ -1142,6 +1142,8 @@ public: db::GerberImporter importer (warn_level ()); data.setup_importer (&importer); + check_dbu (data.dbu); + importer.read (layout); std::string lyr_file = data.get_layer_properties_file (); From d382629e8ee060548a88630dd2663751f8286d20 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Jul 2025 22:48:05 +0200 Subject: [PATCH 3/4] Proposal for a fix of issue #2044 and #2066 The solution is to separate the meaning of "," and "+" layout file combination operators in strm2x tools: * "+" does "blending" * "," does merge "+" has priority over "," ('a+b,c' acts as '(a+b),c'). NOTE: LEF caching only happens across "+". --- src/buddies/src/bd/bdConverterMain.cc | 9 ++- src/buddies/src/bd/bdReaderOptions.cc | 72 ++++++++++++++---- src/buddies/unit_tests/bdConverterTests.cc | 84 ++++++++++++++++++++- src/db/db/dbReader.cc | 1 - src/db/db/dbReader.h | 16 ++++ testdata/bd/strm2oas_1.oas | Bin 0 -> 400 bytes testdata/bd/strm2oas_2.oas | Bin 0 -> 400 bytes testdata/bd/strm2oas_2_10nm.oas | Bin 0 -> 398 bytes testdata/bd/strm2oas_au_1.oas | Bin 0 -> 425 bytes testdata/bd/strm2oas_au_3.oas | Bin 0 -> 458 bytes 10 files changed, 165 insertions(+), 17 deletions(-) create mode 100644 testdata/bd/strm2oas_1.oas create mode 100644 testdata/bd/strm2oas_2.oas create mode 100644 testdata/bd/strm2oas_2_10nm.oas create mode 100644 testdata/bd/strm2oas_au_1.oas create mode 100644 testdata/bd/strm2oas_au_3.oas diff --git a/src/buddies/src/bd/bdConverterMain.cc b/src/buddies/src/bd/bdConverterMain.cc index d3f418b06..6c247445a 100644 --- a/src/buddies/src/bd/bdConverterMain.cc +++ b/src/buddies/src/bd/bdConverterMain.cc @@ -43,8 +43,13 @@ int converter_main (int argc, char *argv[], const std::string &format) generic_reader_options.add_options (cmd); cmd << tl::arg ("input", &infile, "The input file (any format, may be gzip compressed)", - "You can use '+' or ',' to supply multiple files which will be read after each other into the same layout. " - "This provides some cheap, but risky way of merging files. Beware of cell name conflicts.") + "Multiple files can be combined using '+' or ','. '+' will combine the files in 'blending' mode. " + "In this mode it is possible to combine identically named cells into one cell for example. This mode " + "needs to be used with care and there some constraints - e.g. the database unit of the involved " + "layouts needs to be the same. When using ',' as a separator, blending is not used, but the layouts " + "are merged by first creating two layouts and then combining them into one. This mode is more robust " + "but does not allow cell merging. '+' combination has higher priority than ',' - i.e. 'a+b,c' is " + "understood as '(a+b),c'.") << tl::arg ("output", &outfile, tl::sprintf ("The output file (%s format)", format)) ; diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 958bdb723..568e367ae 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -22,6 +22,8 @@ #include "bdReaderOptions.h" #include "dbLoadLayoutOptions.h" +#include "dbLayerMapping.h" +#include "dbCellMapping.h" #include "tlCommandLineParser.h" #include "tlStream.h" @@ -831,15 +833,28 @@ static std::string::size_type find_file_sep (const std::string &s, std::string:: } } -static std::vector split_file_list (const std::string &infile) +static std::vector > split_file_list (const std::string &infile) { - std::vector files; + std::vector > files; + files.push_back (std::vector ()); size_t p = 0; - for (size_t pp = 0; (pp = find_file_sep (infile, p)) != std::string::npos; p = pp + 1) { - files.push_back (std::string (infile, p, pp - p)); + while (true) { + + size_t sep = find_file_sep (infile, p); + if (sep == std::string::npos) { + files.back ().push_back (std::string (infile, p)); + return files; + } + + files.back ().push_back (std::string (infile, p, sep - p)); + if (infile [sep] == ',') { + files.push_back (std::vector ()); + } + + p = sep + 1; + } - files.push_back (std::string (infile, p)); return files; } @@ -850,16 +865,47 @@ void read_files (db::Layout &layout, const std::string &infile, const db::LoadLa // db::LayoutLocker locker (&layout); // but there are yet unknown side effects - // enter a LEF caching context for chaining multiple DEF with the same LEF - db::LoadLayoutOptions local_options (options); - local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true); + std::vector > files = split_file_list (infile); - std::vector files = split_file_list (infile); + for (auto ff = files.begin (); ff != files.end (); ++ff) { + + // enter a LEF caching context for chaining multiple DEF with the same LEF + db::LoadLayoutOptions local_options (options); + local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true); + + db::Layout tmp; + db::Layout *ly = (ff == files.begin () ? &layout : &tmp); + + for (auto f = ff->begin (); f != ff->end (); ++f) { + tl::InputStream stream (*f); + db::Reader reader (stream); + if (f != ff->begin ()) { + reader.set_expected_dbu (ly->dbu ()); + } + reader.read (*ly, local_options); + } + + if (ly != &layout) { + + // Move over cells from read layout to destination ("," separated blocks). + // This path does not imply limitations in terms of DBU compatibility etc. + + std::vector cells_target; + std::vector cells_source; + for (auto c = tmp.begin_top_down (); c != tmp.end_top_cells (); ++c) { + cells_source.push_back (*c); + cells_target.push_back (layout.add_cell (tmp.cell_name (*c))); + } + + db::LayerMapping lm; + lm.create_full (layout, tmp); + db::CellMapping cm; + cm.create_multi_mapping_full (layout, cells_target, tmp, cells_source); + + layout.move_tree_shapes (tmp, cm, lm); + + } - for (std::vector::const_iterator f = files.begin (); f != files.end (); ++f) { - tl::InputStream stream (*f); - db::Reader reader (stream); - reader.read (layout, local_options); } } diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index 03ebc7155..2193a4ecf 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -483,7 +483,7 @@ TEST(10) std::string input; for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) { if (i > 0) { - input += ","; + input += "+"; } input += def_dir + "/" + def_files[i]; } @@ -510,3 +510,85 @@ TEST(10) db::compare_layouts (this, layout, input_au, db::WriteOAS); } + +// Merging with + +TEST(11_1) +{ + std::string input_dir = tl::testdata (); + input_dir += "/bd"; + + std::string input_au = input_dir + "/strm2oas_au_1.oas"; + std::string input = input_dir + "/strm2oas_1.oas+" + input_dir + "/strm2oas_2.oas"; + + std::string output = this->tmp_file ("strm2oas_1.oas"); + const char *argv[] = { "x", + "--blend-mode=0", + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} + +// Merging with + not allowed on different DBUs +TEST(11_2) +{ + std::string input_dir = tl::testdata (); + input_dir += "/bd"; + + std::string input_au = input_dir + "/strm2oas_au_1.oas"; + std::string input = input_dir + "/strm2oas_1.oas+" + input_dir + "/strm2oas_2_10nm.oas"; + + std::string output = this->tmp_file ("strm2oas_1.oas"); + const char *argv[] = { "x", + "--blend-mode=0", + input.c_str (), + output.c_str () + }; + + try { + bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name); + EXPECT_EQ (1, 0); + } catch (tl::Exception &ex) { + EXPECT_EQ (ex.msg (), "Former and present database units are not compatible: 0.001 (former) vs. 0.01 (present)"); + } +} + +// Merging with + not allowed on different DBUs +TEST(11_3) +{ + std::string input_dir = tl::testdata (); + input_dir += "/bd"; + + std::string input_au = input_dir + "/strm2oas_au_3.oas"; + std::string input = input_dir + "/strm2oas_1.oas," + input_dir + "/strm2oas_2_10nm.oas"; + + std::string output = this->tmp_file ("strm2oas_3.oas"); + const char *argv[] = { "x", + "--blend-mode=0", + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} diff --git a/src/db/db/dbReader.cc b/src/db/db/dbReader.cc index aa5a096f8..5f5177e4d 100644 --- a/src/db/db/dbReader.cc +++ b/src/db/db/dbReader.cc @@ -82,7 +82,6 @@ ReaderBase::init (const db::LoadLayoutOptions &options) m_last_warning.clear (); m_warn_count_for_same_message = 0; m_first_warning = true; - m_expected_dbu = 0.0; } bool diff --git a/src/db/db/dbReader.h b/src/db/db/dbReader.h index 76b92cc17..e9a2c23c6 100644 --- a/src/db/db/dbReader.h +++ b/src/db/db/dbReader.h @@ -259,6 +259,22 @@ public: return mp_actual_reader->warnings_as_errors (); } + /** + * @brief Sets the expected database unit (see ReaderBase) + */ + void set_expected_dbu (double dbu) + { + return mp_actual_reader->set_expected_dbu (dbu); + } + + /** + * @brief Gets the expected database unit + */ + double expected_dbu () const + { + return mp_actual_reader->expected_dbu (); + } + private: ReaderBase *mp_actual_reader; tl::InputStream &m_stream; diff --git a/testdata/bd/strm2oas_1.oas b/testdata/bd/strm2oas_1.oas new file mode 100644 index 0000000000000000000000000000000000000000..ea1ce8c00c3ea69a88493fd5bcfa376d50cab16e GIT binary patch literal 400 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqf+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqf+;R4CduxWH!_@U`Sz<5oh9Hk>O{S5oh6NP+~~+$vtDu)nFjNdZ5Vh z0&~Z7!OfkGe@;XPt}g!{&2cM=zo+NHGR=)u{H8MxJUDPI`*Pm)GYN6}N^Oh!HqKys zYqNNB?6mWJpH?$xY~A~lnUguhKY*7(q?(bTi7}^<@d_g|NJK`Qfuo#>v4xS5fl-OE N2uLzCj4;5!005eTs(%0g literal 0 HcmV?d00001 diff --git a/testdata/bd/strm2oas_au_1.oas b/testdata/bd/strm2oas_au_1.oas new file mode 100644 index 0000000000000000000000000000000000000000..9cd21228b6b903802f29a07db434269e44bad713 GIT binary patch literal 425 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&mbel#K|GU$1EcT$gJfmI88})QnHc9WGBPk4FgAcFhK7*^7#IM!=&lj~ literal 0 HcmV?d00001 diff --git a/testdata/bd/strm2oas_au_3.oas b/testdata/bd/strm2oas_au_3.oas new file mode 100644 index 0000000000000000000000000000000000000000..7977695c1c502094e0535c2c40f434f137e81955 GIT binary patch literal 458 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&mben&nP3t#K|GU$1EcT z6i{MF^~pVB&DCHaz Date: Mon, 7 Jul 2025 21:52:57 +0200 Subject: [PATCH 4/4] Handling of ghost cells in strm2x merge Ghost cells act as anchors for top cells of the other layout in "," merges. Unlike other cells which are renamed. --- src/buddies/src/bd/bdReaderOptions.cc | 32 +++++- src/buddies/unit_tests/bdConverterTests.cc | 116 +++++++++++++++++++++ testdata/bd/strm2oas_a.oas | Bin 0 -> 393 bytes testdata/bd/strm2oas_au_12_1.oas | Bin 0 -> 425 bytes testdata/bd/strm2oas_au_12_2.oas | Bin 0 -> 431 bytes testdata/bd/strm2oas_au_12_3.oas | Bin 0 -> 450 bytes testdata/bd/strm2oas_au_12_4.oas | Bin 0 -> 477 bytes testdata/bd/strm2oas_b.oas | Bin 0 -> 395 bytes testdata/bd/strm2oas_c.oas | Bin 0 -> 407 bytes testdata/bd/strm2oas_cc.oas | Bin 0 -> 433 bytes 10 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 testdata/bd/strm2oas_a.oas create mode 100644 testdata/bd/strm2oas_au_12_1.oas create mode 100644 testdata/bd/strm2oas_au_12_2.oas create mode 100644 testdata/bd/strm2oas_au_12_3.oas create mode 100644 testdata/bd/strm2oas_au_12_4.oas create mode 100644 testdata/bd/strm2oas_b.oas create mode 100644 testdata/bd/strm2oas_c.oas create mode 100644 testdata/bd/strm2oas_cc.oas diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 568e367ae..1cab8c0c3 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -892,15 +892,41 @@ void read_files (db::Layout &layout, const std::string &infile, const db::LoadLa std::vector cells_target; std::vector cells_source; + for (auto c = tmp.begin_top_down (); c != tmp.end_top_cells (); ++c) { + cells_source.push_back (*c); - cells_target.push_back (layout.add_cell (tmp.cell_name (*c))); + + // as a special rule, join ghost cells if the source top cell fits into + // a ghost cell of the target. + auto cell_target = layout.cell_by_name (tmp.cell_name (*c)); + if (cell_target.first && layout.cell (cell_target.second).is_ghost_cell ()) { + cells_target.push_back (cell_target.second); + } else { + cells_target.push_back (layout.add_cell (tmp.cell_name (*c))); + } + } + // ghost cell joining also works the other way around: a top cell of destination + // can match a ghost cell of the source + for (auto c = tmp.end_top_cells (); c != tmp.end_top_down (); ++c) { + + const db::Cell &cell_source = tmp.cell (*c); + auto cell_target = layout.cell_by_name (tmp.cell_name (*c)); + + if (cell_source.is_ghost_cell () && cell_target.first) { + cells_source.push_back (*c); + cells_target.push_back (cell_target.second); + } + + } + + db::CellMapping cm; + cm.create_multi_mapping_full (layout, cells_target, tmp, cells_source); + db::LayerMapping lm; lm.create_full (layout, tmp); - db::CellMapping cm; - cm.create_multi_mapping_full (layout, cells_target, tmp, cells_source); layout.move_tree_shapes (tmp, cm, lm); diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index 2193a4ecf..cfbd51e03 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -592,3 +592,119 @@ TEST(11_3) db::compare_layouts (this, layout, input_au, db::WriteOAS); } + +// Merging with + and , under the presence of ghost cells: test+test,top->(test) +TEST(12_1) +{ + std::string input_dir = tl::testdata (); + input_dir += "/bd"; + + std::string input_au = input_dir + "/strm2oas_au_12_1.oas"; + std::string input = input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas," + input_dir + "/strm2oas_c.oas"; + + std::string output = this->tmp_file ("strm2oas_12_1.oas"); + const char *argv[] = { "x", + "--blend-mode=0", + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} + +// Merging with + and , under the presence of ghost cells: top->(test),test+test +TEST(12_2) +{ + std::string input_dir = tl::testdata (); + input_dir += "/bd"; + + std::string input_au = input_dir + "/strm2oas_au_12_2.oas"; + std::string input = input_dir + "/strm2oas_c.oas," + input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas"; + + std::string output = this->tmp_file ("strm2oas_12_2.oas"); + const char *argv[] = { "x", + "--blend-mode=0", + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} + +// Merging with + and , under the presence of ghost cells: test+test,toptop->top->(test) +TEST(12_3) +{ + std::string input_dir = tl::testdata (); + input_dir += "/bd"; + + std::string input_au = input_dir + "/strm2oas_au_12_3.oas"; + std::string input = input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas," + input_dir + "/strm2oas_cc.oas"; + + std::string output = this->tmp_file ("strm2oas_12_3.oas"); + const char *argv[] = { "x", + "--blend-mode=0", + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} + +// Merging with + and , under the presence of ghost cells: toptop->top->(test),test+test +TEST(12_4) +{ + std::string input_dir = tl::testdata (); + input_dir += "/bd"; + + std::string input_au = input_dir + "/strm2oas_au_12_4.oas"; + std::string input = input_dir + "/strm2oas_cc.oas," + input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas"; + + std::string output = this->tmp_file ("strm2oas_12_4.oas"); + const char *argv[] = { "x", + "--blend-mode=0", + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} diff --git a/testdata/bd/strm2oas_a.oas b/testdata/bd/strm2oas_a.oas new file mode 100644 index 0000000000000000000000000000000000000000..7d970af984974249d5234d066200f0497a5634fc GIT binary patch literal 393 zcmY!lcJ=kt^>+;R4CduxWH!_@VE+#S_WcfwybL0T85vTP7#bPFa?dz&H7E$MURWw{ zg>lj8j6So3WRs|O|NiC+Ue(I@`(S3AVX)T;MGaA7t+3k*WLj+^xLj`)Zqo0b8Mpe= z>bZ-aygm8u=u@qEs)wGsaaBk7T~jqWd|i*3r6jeuL`Ix}Lxzd5j+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqf<4lOpKEm85tOr8H<4=L&FFI3=9B|yRYZ~ literal 0 HcmV?d00001 diff --git a/testdata/bd/strm2oas_au_12_2.oas b/testdata/bd/strm2oas_au_12_2.oas new file mode 100644 index 0000000000000000000000000000000000000000..1eb2217657c4213d86047ec5173d69de358000b9 GIT binary patch literal 431 zcmY!lcJ=kt^>+;R4CduxWH!_@V7S1~C?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqf+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqfqRl9A{)m;bt(B;AIp%z#wv%iD4Qe zFO%Q_MnNY*7eO~A1{K-lG)+%ky@`V94_2h5>7CSP*tkn_=FJ8ljeIF(1}4UZjEoG7 P%8bQ8lA&RQ0R{#DO7yv> literal 0 HcmV?d00001 diff --git a/testdata/bd/strm2oas_au_12_4.oas b/testdata/bd/strm2oas_au_12_4.oas new file mode 100644 index 0000000000000000000000000000000000000000..0900f1f3fb7217ddd7de6a87f520b6283bb5ec52 GIT binary patch literal 477 zcmY!lcJ=kt^>+;R4CduxWH!_@V7S1~C?n3q!6L)YEF;ds&mbel#K|GU$1EcT~D8Es~q8E{X_je&`AHzOkhqXA+;R4CduxWH!_@VE+#S_WcfwybL0@85vSiQj{1P8N+hVIC3>82(VsQ zDsY8y(dmpnvxH=msCWPV<_lic%J}+;R4CduxWH!_@VE+#S_VXPWc^L!`Fp3;zVpzkZ#L&nXmV3sLt3g44 z^}K+C~AlrYlYojAk%6S!R30ZaFc%b z%(&H`R?l7ZkvMn-i81|Y@2@D_+i5&!^~tUS{I literal 0 HcmV?d00001 diff --git a/testdata/bd/strm2oas_cc.oas b/testdata/bd/strm2oas_cc.oas new file mode 100644 index 0000000000000000000000000000000000000000..ddf4638d720aee8351c79e5c7bb4eebc71a0476f GIT binary patch literal 433 zcmY!lcJ=kt^>+;R4CduxWH!_@V7S1~C?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqf