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 ",".
This commit is contained in:
Matthias Koefferlein 2026-02-05 23:21:04 +01:00
parent 4c58a66fce
commit 65fd3f3e50
10 changed files with 151 additions and 70 deletions

View File

@ -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);

View File

@ -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<std::string> m_lefdef_map_files;
int m_lefdef_macro_resolution_mode;
std::vector<std::string> m_lefdef_lef_layout_files;
};

View File

@ -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<int> &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<std::string> split_file_list (const std::string &infile)
{
std::vector<std::string> 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<std::string> &paths, db::Layout &layout, const std::string &base_path)
{
m_has_explicit_layer_mapping = true;
std::vector<std::string> paths = split_file_list (filename);
std::map<std::pair<std::string, LayerDetailsKey>, std::vector<db::LayerProperties> > layer_map;
for (std::vector<std::string>::const_iterator p = paths.begin (); p != paths.end (); ++p) {

View File

@ -886,14 +886,27 @@ public:
m_joined_paths = f;
}
const std::string &map_file () const
const std::vector<std::string> &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<std::string> &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<unsigned int, int> m_special_routing_datatypes;
bool m_separate_groups;
bool m_joined_paths;
std::string m_map_file;
std::vector<std::string> m_map_files;
unsigned int m_macro_resolution_mode;
bool m_read_lef_with_def;
std::vector<std::string> 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<std::string> &filename, db::Layout &layout, const std::string &base_path);
/**
* @brief Gets the layer map

View File

@ -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")
);
}
};

View File

@ -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<std::string> 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<db::LEFDEFReaderOptions> 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<db::LEFDEFReaderOptions> 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<db::LEFDEFReaderOptions> 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"

View File

@ -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 ();

View File

@ -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<std::string> ());
EXPECT_EQ (tc.single_map_file (), "");
EXPECT_EQ (tc.map_files ().empty (), true);
std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> 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);

View File

@ -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);

View File

@ -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" ]