From 65fd3f3e501fcac9956f2b8530fd02b68c50fd73 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 5 Feb 2026 23:21:04 +0100 Subject: [PATCH] Providing a fix for issue #2267 1. The script API "map_file" attribute has been changed to accept and supply nil (for no file), a string (for a file, empty is equivalent to "no file") and a list for multiple files which are merged. The split at "+" or "," has been dropped. 2. The strm* tools option ("--lefdef-map") has been changed to allow multiple occurances instead of split at "+" or ",". --- src/buddies/src/bd/bdReaderOptions.cc | 18 +++-- src/buddies/src/bd/bdReaderOptions.h | 2 +- .../lefdef/db_plugin/dbLEFDEFImporter.cc | 38 ++--------- .../lefdef/db_plugin/dbLEFDEFImporter.h | 31 ++++++--- .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 3 +- .../lefdef/db_plugin/gsiDeclDbLEFDEF.cc | 66 +++++++++++++++++-- .../lay_plugin/layLEFDEFImportDialogs.cc | 6 +- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 45 ++++++++++--- .../unit_tests/dbLEFDEFReaderOptionsTests.cc | 4 +- testdata/ruby/dbReaders.rb | 8 ++- 10 files changed, 151 insertions(+), 70 deletions(-) diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index a52acfa09..2d5d0408c 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -121,7 +121,17 @@ GenericReaderOptions::GenericReaderOptions () m_lefdef_read_lef_with_def = load_options.get_option_by_name ("lefdef_config.read_lef_with_def").to_bool (); m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool (); m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool (); - m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string (); + + tl::Variant map_files = load_options.get_option_by_name ("lefdef_config.map_file").to_string (); + m_lefdef_map_files.clear (); + if (map_files.is_list ()) { + for (tl::Variant::const_iterator i = map_files.begin (); i != map_files.end (); ++i) { + m_lefdef_map_files.push_back (i->to_string ()); + } + } else if (! map_files.is_nil ()) { + m_lefdef_map_files.push_back (map_files.to_string ()); + } + // Don't take the default, as in practice, it's more common to substitute LEF macros by layouts // m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always" @@ -613,7 +623,7 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd) "See '--" + m_long_prefix + "lefdef-via-geometry-suffix' for a description of the mapping scheme.\n" ) << tl::arg (group + - "--" + m_long_prefix + "lefdef-map", &m_lefdef_map_file, "Specifies to use a layer map file", + "--" + m_long_prefix + "lefdef-map", &m_lefdef_map_files, "Specifies to use a layer map file", "Use this option to turn off pattern-based layer mapping and to use an explicit mapping file instead. " "See '--" + m_long_prefix + "lefdef-via-geometry-suffix' for a description of the pattern-based mapping scheme.\n" "\n" @@ -642,7 +652,7 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd) "\n" "If a map file is used, only the layers present in the map file are generated. No other layers are produced.\n" "\n" - "Multiple map files can be given, separated by '+' or ','. In that case, these files are concatenated." + "This option can be given multiple times. The corresponing map files are merged." ) << tl::arg (group + "!--" + m_long_prefix + "lefdef-macro-resolution-mode", &m_lefdef_macro_resolution_mode, "Specify how to generate layout from LEF macros", @@ -807,7 +817,7 @@ GenericReaderOptions::configure (db::LoadLayoutOptions &load_options) load_options.set_option_by_name ("lefdef_config.read_lef_with_def", m_lefdef_read_lef_with_def); load_options.set_option_by_name ("lefdef_config.separate_groups", m_lefdef_separate_groups); load_options.set_option_by_name ("lefdef_config.joined_paths", m_lefdef_joined_paths); - load_options.set_option_by_name ("lefdef_config.map_file", m_lefdef_map_file); + load_options.set_option_by_name ("lefdef_config.map_file", m_lefdef_map_files); load_options.set_option_by_name ("lefdef_config.macro_resolution_mode", m_lefdef_macro_resolution_mode); load_options.set_option_by_name ("lefdef_config.macro_resolution_mode", m_lefdef_macro_resolution_mode); load_options.set_option_by_name ("lefdef_config.paths_relative_to_cwd", true); diff --git a/src/buddies/src/bd/bdReaderOptions.h b/src/buddies/src/bd/bdReaderOptions.h index fe1569931..0773b56cb 100644 --- a/src/buddies/src/bd/bdReaderOptions.h +++ b/src/buddies/src/bd/bdReaderOptions.h @@ -186,7 +186,7 @@ private: bool m_lefdef_read_lef_with_def; bool m_lefdef_separate_groups; bool m_lefdef_joined_paths; - std::string m_lefdef_map_file; + std::vector m_lefdef_map_files; int m_lefdef_macro_resolution_mode; std::vector m_lefdef_lef_layout_files; }; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index dbbe55cf1..a41cbf220 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -597,7 +597,6 @@ LEFDEFReaderOptions::LEFDEFReaderOptions () m_special_routing_datatype (0), m_separate_groups (false), m_joined_paths (false), - m_map_file (), m_macro_resolution_mode (0), m_read_lef_with_def (true), m_paths_relative_to_cwd (false), @@ -677,7 +676,7 @@ LEFDEFReaderOptions &LEFDEFReaderOptions::operator= (const LEFDEFReaderOptions & m_special_routing_datatypes = d.m_special_routing_datatypes; m_separate_groups = d.m_separate_groups; m_joined_paths = d.m_joined_paths; - m_map_file = d.m_map_file; + m_map_files = d.m_map_files; m_macro_resolution_mode = d.m_macro_resolution_mode; m_lef_files = d.m_lef_files; m_macro_layout_files = d.m_macro_layout_files; @@ -1006,9 +1005,9 @@ LEFDEFReaderState::init (Layout &layout, const std::string &base_path, const Loa // use default options - } else if (! mp_tech_comp->map_file ().empty ()) { + } else if (! mp_tech_comp->map_files ().empty ()) { - read_map_file (mp_tech_comp->map_file (), layout, base_path); + read_map_files (mp_tech_comp->map_files (), layout, base_path); } else { @@ -1123,40 +1122,11 @@ static bool try_read_layers (tl::Extractor &ex, std::vector &layers) return true; } -static std::string::size_type find_file_sep (const std::string &s, std::string::size_type from) -{ - std::string::size_type p1 = s.find ("+", from); - std::string::size_type p2 = s.find (",", from); - - if (p1 == std::string::npos) { - return p2; - } else if (p2 == std::string::npos) { - return p1; - } else { - return p1 < p2 ? p1 : p2; - } -} - -static std::vector split_file_list (const std::string &infile) -{ - std::vector files; - - 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)); - } - files.push_back (std::string (infile, p)); - - return files; -} - void -LEFDEFReaderState::read_map_file (const std::string &filename, db::Layout &layout, const std::string &base_path) +LEFDEFReaderState::read_map_files (const std::vector &paths, db::Layout &layout, const std::string &base_path) { m_has_explicit_layer_mapping = true; - std::vector paths = split_file_list (filename); - std::map, std::vector > layer_map; for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index d801c04b2..42d11487d 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -886,14 +886,27 @@ public: m_joined_paths = f; } - const std::string &map_file () const + const std::vector &map_files () const { - return m_map_file; + return m_map_files; } - void set_map_file (const std::string &f) + void set_map_files (const std::vector &f) { - m_map_file = f; + m_map_files = f; + } + + std::string single_map_file () const + { + return m_map_files.empty () ? std::string () : m_map_files.front (); + } + + void set_single_map_file (const std::string &f) + { + m_map_files.clear (); + if (! f.empty ()) { + m_map_files.push_back (f); + } } /** @@ -1041,7 +1054,7 @@ private: std::map m_special_routing_datatypes; bool m_separate_groups; bool m_joined_paths; - std::string m_map_file; + std::vector m_map_files; unsigned int m_macro_resolution_mode; bool m_read_lef_with_def; std::vector m_lef_files; @@ -1292,13 +1305,11 @@ public: } /** - * @brief Reads the given map file + * @brief Reads the given list of map files * - * Usually this file is read by the constructor. This method is provided for test purposes. - * The filename can be a list of files, separated by "+" or ",". They are loaded together into - * the same map like they were concatenated. + * If multiple files are given, they are concatenated. */ - void read_map_file (const std::string &filename, db::Layout &layout, const std::string &base_path); + void read_map_files (const std::vector &filename, db::Layout &layout, const std::string &base_path); /** * @brief Gets the layer map diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 253b7b6db..b455bc9fa 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -406,7 +406,8 @@ class LEFDEFFormatDeclaration tl::make_member (&LEFDEFReaderOptions::macro_resolution_mode, &LEFDEFReaderOptions::set_macro_resolution_mode, "macro-resolution-mode", MacroResolutionModeConverter ()) + tl::make_member (&LEFDEFReaderOptions::separate_groups, &LEFDEFReaderOptions::set_separate_groups, "separate-groups") + tl::make_member (&LEFDEFReaderOptions::joined_paths, &LEFDEFReaderOptions::set_joined_paths, "joined-paths") + - tl::make_member (&LEFDEFReaderOptions::map_file, &LEFDEFReaderOptions::set_map_file, "map-file") + // NOTE: technology options do not support multiple files currently + tl::make_member (&LEFDEFReaderOptions::single_map_file, &LEFDEFReaderOptions::set_single_map_file, "map-file") ); } }; diff --git a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc index b2dfae449..65da74f91 100644 --- a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc +++ b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc @@ -106,6 +106,36 @@ static void set_pin_property_name (db::LEFDEFReaderOptions *config, const tl::Va config->set_pin_property_name (name); } +static tl::Variant get_map_files (const db::LEFDEFReaderOptions *config) +{ + const auto &mf = config->map_files (); + if (mf.empty ()) { + return tl::Variant (); + } else if (mf.size () == 1) { + return tl::Variant (mf.front ()); + } else { + return tl::Variant (mf.begin (), mf.end ()); + } +} + +static void set_map_files (db::LEFDEFReaderOptions *config, tl::Variant &value) +{ + std::vector mf; + if (value.is_nil ()) { + // empty list + } else if (value.is_list ()) { + for (auto i = value.begin (); i != value.end (); ++i) { + mf.push_back (i->to_string ()); + } + } else { + std::string s = value.to_string (); + if (! s.empty ()) { + mf.push_back (s); + } + } + config->set_map_files (mf); +} + static gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfiguration", gsi::method ("paths_relative_to_cwd=", &db::LEFDEFReaderOptions::set_paths_relative_to_cwd, gsi::arg ("f"), @@ -894,8 +924,8 @@ gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfi "\n" "This property has been added in version 0.28.8.\n" ) + - gsi::method ("map_file", &db::LEFDEFReaderOptions::map_file, - "@brief Gets the layer map file to use.\n" + gsi::method_ext ("map_file", &get_map_files, + "@brief Gets the layer map file or the map files to use.\n" "If a layer map file is given, the reader will pull the layer mapping from this file. The layer mapping rules " "specified in the reader options are ignored in this case. These are the name suffix rules for vias, blockages, routing, " "special routing, pins etc. and the corresponding datatype rules. The \\layer_map attribute will also be ignored. " @@ -903,13 +933,39 @@ gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfi "The layer map file path will be resolved relative to the technology base path if the LEF/DEF reader options are " "used in the context of a technology.\n" "\n" - "This property has been added in version 0.27.\n" + "The mapping file is one layer mapping entry per line. Each line is a layer name, followed by a list of purposes (VIA, PIN ...) " + "and a layer and datatype number. In addition, 'DIEAREA', 'REGION' and 'BLOCKAGE' can be used to map the design outline, regions and blockages to a layer. " + "'REGION' can have a detailed specifier which is 'FENCE', 'GUIDE' or 'NONE' for fence, guide or other type regions (e.g. 'REGION FENCE 99/0').\n" + "\n" + "'NAME' in place of the layer name and using layer/purpose in the purpose column allows mapping labels to specific layers.\n" + "\n" + "This is an example for a layer map file:\n" + "\n" + "@code\n" + "DIEAREA ALL 100 0\n" + "M1 LEFPIN 12 0\n" + "M1 PIN 12 2\n" + "M1 NET 12 3\n" + "M1 SPNET 12 4\n" + "M1 VIA 12 5\n" + "M1 BLOCKAGE 12 10\n" + "NAME M1/PIN 12 10\n" + "VIA1 LEFPIN,VIA,PIN,NET,SPNET 13 0\n" + "M2 LEFPIN,PIN,NET,SPNET,VIA 14 0\n" + "@/code\n" + "\n" + "If a map file is used, only the layers present in the map file are generated. No other layers are produced.\n" + "\n" + "The 'map_file' attribute can either be a single file name, or a list of file names. If a list is given, the " + "corresponding files are merged. If the attribute is nil, no map file is used.\n" + "\n" + "This property has been added in version 0.27. The ability to supply multiple files has been added in version 0.30.6.\n" ) + - gsi::method ("map_file=", &db::LEFDEFReaderOptions::set_map_file, gsi::arg ("file"), + gsi::method_ext ("map_file=", &set_map_files, gsi::arg ("file"), "@brief Sets the layer map file to use.\n" "See \\map_file for details about this property.\n" "\n" - "This property has been added in version 0.27.\n" + "This property has been added in version 0.27. The ability to supply multiple files has been added in version 0.30.6.\n" ) + gsi::method ("macro_resolution_mode", &db::LEFDEFReaderOptions::macro_resolution_mode, "@brief Gets the macro resolution mode (LEF macros into DEF).\n" diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc index fafa5c671..36307aefa 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc @@ -523,7 +523,7 @@ LEFDEFReaderOptionsEditor::commit (db::FormatSpecificReaderOptions *options, con data->set_separate_groups (separate_groups->isChecked ()); data->set_joined_paths (joined_paths->isChecked ()); data->set_read_lef_with_def (read_lef_with_def->isChecked ()); - data->set_map_file (tl::to_string (mapfile_path->text ())); + data->set_single_map_file (tl::to_string (mapfile_path->text ())); data->set_macro_resolution_mode (macro_resolution_mode->currentIndex ()); data->clear_lef_files (); @@ -598,8 +598,8 @@ LEFDEFReaderOptionsEditor::setup (const db::FormatSpecificReaderOptions *options separate_groups->setChecked (data->separate_groups ()); joined_paths->setChecked (data->joined_paths ()); read_lef_with_def->setChecked (data->read_lef_with_def ()); - mapfile_path->setText (tl::to_qstring (data->map_file ())); - layer_map_mode->setCurrentIndex (data->map_file ().empty () ? 1 : 0); + mapfile_path->setText (tl::to_qstring (data->single_map_file ())); + layer_map_mode->setCurrentIndex (data->single_map_file ().empty () ? 1 : 0); macro_resolution_mode->setCurrentIndex (data->macro_resolution_mode ()); checkbox_changed (); diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 4f03243c7..3b5c967df 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -77,7 +77,7 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f std::string f; ex.read_word_or_quoted (f); - ld.read_map_file (f, layout, fn_path); + ld.read_map_files (tl::split (f, ","), layout, fn_path); } else if (ex.test ("def:")) { @@ -280,6 +280,33 @@ static void run_test2 (tl::TestBase *_this, const char *lef_dir, const char *fil } } +TEST(reader_options) +{ + db::LEFDEFReaderOptions tc; + + tc.set_single_map_file (std::string ()); + EXPECT_EQ (tc.single_map_file (), ""); + EXPECT_EQ (tc.map_files ().empty (), true); + + tc.set_single_map_file ("abc"); + EXPECT_EQ (tc.single_map_file (), "abc"); + EXPECT_EQ (tc.map_files ().size (), size_t (1)); + + tc.set_map_files (std::vector ()); + EXPECT_EQ (tc.single_map_file (), ""); + EXPECT_EQ (tc.map_files ().empty (), true); + + std::vector mf; + mf.push_back ("abc"); + mf.push_back ("xyz"); + tc.set_map_files (mf); + + EXPECT_EQ (tc.single_map_file (), "abc"); + EXPECT_EQ (tc.map_files ().size (), size_t (2)); + EXPECT_EQ (tc.map_files ().front (), "abc"); + EXPECT_EQ (tc.map_files ().back (), "xyz"); +} + TEST(lef1) { run_test (_this, "lef1", "lef:in.lef", 0, default_options ()); @@ -543,11 +570,11 @@ TEST(110_lefpins) TEST(111_mapfile) { db::LEFDEFReaderOptions options = default_options (); - options.set_map_file ("test.map"); + options.set_single_map_file ("test.map"); run_test (_this, "mapfile", "read:in.def", "au.oas.gz", options, false); - options.set_map_file ("test-nonames.map"); + options.set_single_map_file ("test-nonames.map"); run_test (_this, "mapfile", "read:in.def", "au.oas.gz", options, false); } @@ -658,7 +685,7 @@ TEST(114_lef_skips_end_library) TEST(115_componentmaskshift) { db::LEFDEFReaderOptions options = default_options (); - options.set_map_file ("in.map"); + options.set_single_map_file ("in.map"); run_test (_this, "masks-2", "lef:in_tech.lef+lef:in.lef+def:in.def", "au.oas.gz", options, false); } @@ -924,7 +951,7 @@ TEST(200_lefdef_plugin) fn_path += "/lefdef/masks-1/"; db::LEFDEFReaderOptions lefdef_opt = default_options (); - lefdef_opt.set_map_file ("in.map"); + lefdef_opt.set_single_map_file ("in.map"); db::LoadLayoutOptions opt; opt.set_options (lefdef_opt); @@ -945,7 +972,7 @@ TEST(201_lefdef_plugin_explicit_lef) fn_path += "/lefdef/masks-1/"; db::LEFDEFReaderOptions lefdef_opt = default_options (); - lefdef_opt.set_map_file ("in.map"); + lefdef_opt.set_single_map_file ("in.map"); std::vector lf; lf.push_back ("hidden/in_tech.lef"); lefdef_opt.set_lef_files (lf); @@ -1047,7 +1074,7 @@ TEST(211_symlinks) fn_path += "/lefdef/issue-1531/"; db::LEFDEFReaderOptions lefdef_opt = default_options (); - lefdef_opt.set_map_file ("tech.map"); + lefdef_opt.set_single_map_file ("tech.map"); std::vector lf; lf.push_back ("tech.lef"); lf.push_back ("blocks.lef"); @@ -1080,7 +1107,7 @@ TEST(213_no_duplicate_LEF) fn_path += "/lefdef/issue-1724/"; db::LEFDEFReaderOptions lefdef_opt = default_options (); - lefdef_opt.set_map_file ("tech.map"); + lefdef_opt.set_single_map_file ("tech.map"); std::vector lf; lf.push_back ("d/tech.lef"); lf.push_back ("blocks.lef"); @@ -1107,7 +1134,7 @@ TEST(214_issue1877) fn_path += "/lefdef/issue-1877/"; db::LEFDEFReaderOptions lefdef_opt = default_options (); - lefdef_opt.set_map_file ("tech.map"); + lefdef_opt.set_single_map_file ("tech.map"); lefdef_opt.set_read_lef_with_def (true); db::LoadLayoutOptions opt; opt.set_options (lefdef_opt); diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFReaderOptionsTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFReaderOptionsTests.cc index eb564b306..6e39dc910 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFReaderOptionsTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFReaderOptionsTests.cc @@ -317,8 +317,8 @@ TEST(1) EXPECT_EQ (options.read_lef_with_def (), false); - options.set_map_file ("ABC.map"); - EXPECT_EQ (options.map_file (), "ABC.map"); + options.set_single_map_file ("ABC.map"); + EXPECT_EQ (options.single_map_file (), "ABC.map"); options.set_macro_resolution_mode (2); diff --git a/testdata/ruby/dbReaders.rb b/testdata/ruby/dbReaders.rb index 22604bc2c..04375e172 100644 --- a/testdata/ruby/dbReaders.rb +++ b/testdata/ruby/dbReaders.rb @@ -370,9 +370,15 @@ class DBReaders_TestClass < TestBase conf.separate_groups = true assert_equal(conf.separate_groups, true) - assert_equal(conf.map_file, "") + assert_equal(conf.map_file, nil) conf.map_file = "xyz.map" assert_equal(conf.map_file, "xyz.map") + conf.map_file = nil + assert_equal(conf.map_file, nil) + conf.map_file = "" + assert_equal(conf.map_file, nil) + conf.map_file = [ "abc.map", "uvw.map" ] + assert_equal(conf.map_file, [ "abc.map", "uvw.map" ]) assert_equal(conf.lef_files.join(","), "") conf.lef_files = [ "u.lef", "v.lef" ]