Merge pull request #2413 from KLayout/feature/issue-2374

Feature/issue 2374
This commit is contained in:
Matthias Köfferlein 2026-08-14 17:58:51 +02:00 committed by GitHub
commit 6d9fcd529a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 221 additions and 14 deletions

View File

@ -112,6 +112,7 @@ GenericReaderOptions::GenericReaderOptions ()
m_lefdef_produce_special_routing = load_options.get_option_by_name ("lefdef_config.produce_special_routing").to_bool (); m_lefdef_produce_special_routing = load_options.get_option_by_name ("lefdef_config.produce_special_routing").to_bool ();
m_lefdef_special_routing_suffix = load_options.get_option_by_name ("lefdef_config.special_routing_suffix_str").to_string (); m_lefdef_special_routing_suffix = load_options.get_option_by_name ("lefdef_config.special_routing_suffix_str").to_string ();
m_lefdef_special_routing_datatype = load_options.get_option_by_name ("lefdef_config.special_routing_datatype_str").to_string (); m_lefdef_special_routing_datatype = load_options.get_option_by_name ("lefdef_config.special_routing_datatype_str").to_string ();
m_lefdef_skip_duplicate_macros = load_options.get_option_by_name ("lefdef_config.skip_duplicate_macros").to_bool ();
tl::Variant lef_files = load_options.get_option_by_name ("lefdef_config.lef_files"); tl::Variant lef_files = load_options.get_option_by_name ("lefdef_config.lef_files");
for (tl::Variant::const_iterator i = lef_files.begin (); i != lef_files.end (); ++i) { for (tl::Variant::const_iterator i = lef_files.begin (); i != lef_files.end (); ++i) {
@ -653,9 +654,9 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd)
"\n" "\n"
"The following values are accepted for this option:\n" "The following values are accepted for this option:\n"
"\n" "\n"
"* 0: produce LEF geometry unless a FOREIGN cell is specified (the default)\n" "* 0: produce LEF geometry unless a FOREIGN cell is specified\n"
"* 1: produce LEF geometry always and ignore FOREIGN\n" "* 1: produce LEF geometry always and ignore FOREIGN\n"
"* 2: Never produce LEF geometry and assume FOREIGN always\n" "* 2: Never produce LEF geometry and assume FOREIGN always (the default)\n"
"\n" "\n"
"In case of FOREIGN macros in mode 0 or always in mode 2, the '--" + m_long_prefix + "lefdef-lef-layouts' option is available to specify " "In case of FOREIGN macros in mode 0 or always in mode 2, the '--" + m_long_prefix + "lefdef-lef-layouts' option is available to specify "
"external layout files for providing the LEF macro layouts.\n" "external layout files for providing the LEF macro layouts.\n"
@ -686,6 +687,15 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd)
"See also '--" + m_long_prefix + "lefdef-read-lef-with-def' for an option to implicitly read all LEF files in the same " "See also '--" + m_long_prefix + "lefdef-read-lef-with-def' for an option to implicitly read all LEF files in the same "
"place than the DEF file.\n" "place than the DEF file.\n"
) )
<< tl::arg (group +
"#--" + m_long_prefix + "lefdef-skip-duplicate-macros", &m_lefdef_skip_duplicate_macros, "Skip duplicate LEF macros",
"This option applies when reading DEF files.\n"
"\n"
"If this option is present, having the same macro in different LEF files is a warning rather than being an error. "
"In that case, the first occurance is used. Use this option with care, as it may render invalid layouts when "
"the versions of the macro are defined differently. It is intended for cases, when macros with the same name are guaranteed "
"to be identical. KLayout does not check, if that is actually the case."
)
; ;
} }
@ -814,6 +824,7 @@ GenericReaderOptions::configure (db::LoadLayoutOptions &load_options)
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.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); load_options.set_option_by_name ("lefdef_config.paths_relative_to_cwd", true);
load_options.set_option_by_name ("lefdef_config.skip_duplicate_macros", m_lefdef_skip_duplicate_macros);
tl::Variant lef_layout_files = tl::Variant::empty_list (); tl::Variant lef_layout_files = tl::Variant::empty_list ();
for (std::vector<std::string>::const_iterator l = m_lefdef_lef_layout_files.begin (); l != m_lefdef_lef_layout_files.end (); ++l) { for (std::vector<std::string>::const_iterator l = m_lefdef_lef_layout_files.begin (); l != m_lefdef_lef_layout_files.end (); ++l) {

View File

@ -189,6 +189,7 @@ private:
std::vector<std::string> m_lefdef_map_files; std::vector<std::string> m_lefdef_map_files;
int m_lefdef_macro_resolution_mode; int m_lefdef_macro_resolution_mode;
std::vector<std::string> m_lefdef_lef_layout_files; std::vector<std::string> m_lefdef_lef_layout_files;
bool m_lefdef_skip_duplicate_macros;
}; };
/** /**

View File

@ -597,6 +597,7 @@ LEFDEFReaderOptions::LEFDEFReaderOptions ()
m_special_routing_datatype (0), m_special_routing_datatype (0),
m_separate_groups (false), m_separate_groups (false),
m_joined_paths (false), m_joined_paths (false),
m_skip_duplicate_macros (false),
m_macro_resolution_mode (0), m_macro_resolution_mode (0),
m_read_lef_with_def (true), m_read_lef_with_def (true),
m_paths_relative_to_cwd (false), m_paths_relative_to_cwd (false),
@ -682,6 +683,7 @@ LEFDEFReaderOptions &LEFDEFReaderOptions::operator= (const LEFDEFReaderOptions &
m_macro_layout_files = d.m_macro_layout_files; m_macro_layout_files = d.m_macro_layout_files;
m_read_lef_with_def = d.m_read_lef_with_def; m_read_lef_with_def = d.m_read_lef_with_def;
m_paths_relative_to_cwd = d.m_paths_relative_to_cwd; m_paths_relative_to_cwd = d.m_paths_relative_to_cwd;
m_skip_duplicate_macros = d.m_skip_duplicate_macros;
set_macro_layouts (d.macro_layouts ()); set_macro_layouts (d.macro_layouts ());
} }
return *this; return *this;
@ -1070,6 +1072,9 @@ LEFDEFReaderState::ensure_lef_importer (int warn_level)
{ {
if (! mp_lef_importer.get ()) { if (! mp_lef_importer.get ()) {
mp_lef_importer.reset (new db::LEFImporter (warn_level)); mp_lef_importer.reset (new db::LEFImporter (warn_level));
if (mp_tech_comp) {
mp_lef_importer->set_skip_duplicate_macros (mp_tech_comp->skip_duplicate_macros ());
}
} }
} }

View File

@ -896,6 +896,16 @@ public:
m_map_files = f; m_map_files = f;
} }
bool skip_duplicate_macros () const
{
return m_skip_duplicate_macros;
}
void set_skip_duplicate_macros (bool v)
{
m_skip_duplicate_macros = v;
}
std::string single_map_file () const std::string single_map_file () const
{ {
return m_map_files.empty () ? std::string () : m_map_files.front (); return m_map_files.empty () ? std::string () : m_map_files.front ();
@ -1055,6 +1065,7 @@ private:
bool m_separate_groups; bool m_separate_groups;
bool m_joined_paths; bool m_joined_paths;
std::vector<std::string> m_map_files; std::vector<std::string> m_map_files;
bool m_skip_duplicate_macros;
unsigned int m_macro_resolution_mode; unsigned int m_macro_resolution_mode;
bool m_read_lef_with_def; bool m_read_lef_with_def;
std::vector<std::string> m_lef_files; std::vector<std::string> m_lef_files;

View File

@ -34,7 +34,8 @@ namespace db
// LEFImporter implementation // LEFImporter implementation
LEFImporter::LEFImporter (int warn_level) LEFImporter::LEFImporter (int warn_level)
: LEFDEFImporter (warn_level) : LEFDEFImporter (warn_level),
m_skip_duplicate_macros (false)
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -884,15 +885,20 @@ void
LEFImporter::read_macro (Layout &layout) LEFImporter::read_macro (Layout &layout)
{ {
std::string mn = get (); std::string mn = get ();
if (m_macros.find (mn) != m_macros.end ()) {
error (tl::to_string (tr ("Duplicate MACRO name: ")) + mn);
}
set_cellname (mn); set_cellname (mn);
GeometryBasedLayoutGenerator *mg = new GeometryBasedLayoutGenerator (); GeometryBasedLayoutGenerator *mg = 0;
reader_state ()->register_macro_cell (mn, mg);
if (m_macros.find (mn) != m_macros.end ()) {
if (m_skip_duplicate_macros) {
warn (tl::to_string (tr ("Skipping duplicate MACRO: : ")) + mn + tl::to_string (tr (" (skip option is enabled by user)")));
} else {
error (tl::to_string (tr ("Duplicate MACRO name: ")) + mn);
}
} else {
mg = new GeometryBasedLayoutGenerator ();
reader_state ()->register_macro_cell (mn, mg);
}
db::Trans foreign_trans; db::Trans foreign_trans;
std::string foreign_name; std::string foreign_name;
@ -965,7 +971,7 @@ LEFImporter::read_macro (Layout &layout)
read_geometries (mg, layout.dbu (), LEFPins, &boxes_for_labels, prop_id); read_geometries (mg, layout.dbu (), LEFPins, &boxes_for_labels, prop_id);
for (std::map <std::string, db::Box>::const_iterator b = boxes_for_labels.begin (); b != boxes_for_labels.end (); ++b) { for (std::map <std::string, db::Box>::const_iterator b = boxes_for_labels.begin (); b != boxes_for_labels.end (); ++b) {
if (! b->second.empty ()) { if (mg && ! b->second.empty ()) {
mg->add_text (b->first, LEFLabel, db::Text (label.c_str (), db::Trans (b->second.center () - db::Point ())), 0, 0); mg->add_text (b->first, LEFLabel, db::Text (label.c_str (), db::Trans (b->second.center () - db::Point ())), 0, 0);
} }
} }
@ -1052,7 +1058,9 @@ LEFImporter::read_macro (Layout &layout)
} else if (test ("FIXEDMASK")) { } else if (test ("FIXEDMASK")) {
mg->set_fixedmask (true); if (mg) {
mg->set_fixedmask (true);
}
expect (";"); expect (";");
} else { } else {
@ -1075,8 +1083,10 @@ LEFImporter::read_macro (Layout &layout)
} }
mg->add_box (std::string (), Outline, db::Box (-origin, -origin + size), 0, 0); if (mg) {
mg->subtract_overlap_from_outline (m_overlap_layers); mg->add_box (std::string (), Outline, db::Box (-origin, -origin + size), 0, 0);
mg->subtract_overlap_from_outline (m_overlap_layers);
}
MacroDesc macro_desc; MacroDesc macro_desc;
macro_desc.foreign_name = foreign_name; macro_desc.foreign_name = foreign_name;

View File

@ -147,6 +147,31 @@ public:
*/ */
void finish_lef (db::Layout &layout); void finish_lef (db::Layout &layout);
/**
* @brief Sets a flag indicating whether to skip duplicate macros
*
* With this flag set to true, macros are ignored when there already
* is a macro with the same name. Use this option with caution, as
* skipping is only permitted if both macros are identical. The
* LEF reader does not check this condition.
*
* The default is "false" (raise an error on duplicate macros).
*/
void set_skip_duplicate_macros (bool f)
{
m_skip_duplicate_macros = f;
}
/**
* @brief Gets a flag indicating whether to skip duplicate macros
*
* See set_skip_duplicate_macros for an explanation of this flag.
*/
bool skip_duplicate_macros () const
{
return m_skip_duplicate_macros;
}
protected: protected:
void do_read (db::Layout &layout); void do_read (db::Layout &layout);
@ -159,6 +184,7 @@ private:
std::map<std::string, ViaDesc> m_vias; std::map<std::string, ViaDesc> m_vias;
std::set<std::string> m_routing_layers, m_cut_layers, m_overlap_layers; std::set<std::string> m_routing_layers, m_cut_layers, m_overlap_layers;
std::map<std::string, unsigned int> m_num_masks; std::map<std::string, unsigned int> m_num_masks;
bool m_skip_duplicate_macros;
std::vector <db::Trans> get_iteration (double dbu); std::vector <db::Trans> get_iteration (double dbu);
void read_geometries (GeometryBasedLayoutGenerator *lg, double dbu, LayerPurpose purpose, std::map<std::string, db::Box> *collect_bboxes = 0, properties_id_type prop_id = 0); void read_geometries (GeometryBasedLayoutGenerator *lg, double dbu, LayerPurpose purpose, std::map<std::string, db::Box> *collect_bboxes = 0, properties_id_type prop_id = 0);

View File

@ -967,6 +967,24 @@ gsi::Class<db::LEFDEFReaderOptions> decl_lefdef_config ("db", "LEFDEFReaderConfi
"\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" "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 ("skip_duplicate_macros", &db::LEFDEFReaderOptions::skip_duplicate_macros,
"@brief Gets a value indicating wether to skip duplicate LEF Macro definitions.\n"
"If this property is 'true', having the same macro in different LEF files is a warning rather than being an error. "
"In that case, the first occurance is used. Use this option with care, as it may render invalid layouts when "
"the versions of the macro are defined differently. It is intended for cases, when macros with the same name are guaranteed "
"to be identical. KLayout does not check, if that is actually the case."
"\n"
"The default is 'false' (duplicate macro names are an error).\n"
"\n"
"This property has been added in version 0.30.x.\n"
) +
gsi::method ("skip_duplicate_macros=", &db::LEFDEFReaderOptions::set_skip_duplicate_macros, gsi::arg ("skip_duplicate_macros"),
"@brief Sets a value indicating wether to skip duplicate LEF Macro definitions.\n"
"\n"
"See \\skip_duplicate_macros for a description of this property.\n"
"\n"
"This property has been added in version 0.30.11.\n"
) +
gsi::method ("macro_resolution_mode", &db::LEFDEFReaderOptions::macro_resolution_mode, gsi::method ("macro_resolution_mode", &db::LEFDEFReaderOptions::macro_resolution_mode,
"@brief Gets the macro resolution mode (LEF macros into DEF).\n" "@brief Gets the macro resolution mode (LEF macros into DEF).\n"
"This property describes the way LEF macros are turned into layout cells when reading DEF. There " "This property describes the way LEF macros are turned into layout cells when reading DEF. There "

View File

@ -1199,3 +1199,54 @@ TEST(216_line_extensions)
run_test (_this, "issue-2075", "map:test.map+lef:test.lef+def:test.def", "au.oas", default_options (), false); run_test (_this, "issue-2075", "map:test.map+lef:test.lef+def:test.def", "au.oas", default_options (), false);
} }
// issue-2374 (skip duplicate LEF macro option)
TEST(217_skip_duplicate_macro)
{
std::string fn_path (tl::testdata ());
fn_path += "/lefdef/issue-2374/";
db::LEFDEFReaderOptions lefdef_opt = default_options ();
lefdef_opt.set_single_map_file ("tech.map");
std::vector<std::string> lf;
lf.push_back ("tech.lef");
lf.push_back ("lib1.lef"); // macros a,b
lf.push_back ("lib2.lef"); // macros b,c
lefdef_opt.set_lef_files (lf);
lefdef_opt.set_read_lef_with_def (false);
db::Layout ly;
try {
db::LoadLayoutOptions opt;
opt.set_options (lefdef_opt);
EXPECT_EQ (lefdef_opt.skip_duplicate_macros (), false);
tl::InputStream is (fn_path + "top.def");
db::Reader reader (is);
reader.read (ly, opt);
// must throw an exception because of duplicate macros
EXPECT_EQ (1, 0);
} catch (...) { }
ly = db::Layout ();
{
db::LEFDEFReaderOptions lo = lefdef_opt;
lo.set_skip_duplicate_macros (true);
EXPECT_EQ (lo.skip_duplicate_macros (), true);
db::LoadLayoutOptions opt;
opt.set_options (lo);
tl::InputStream is (fn_path + "top.def");
db::Reader reader (is);
reader.read (ly, opt);
}
db::compare_layouts (_this, ly, fn_path + "au.oas", db::WriteOAS);
}

BIN
testdata/lefdef/issue-2374/au.oas vendored Normal file

Binary file not shown.

20
testdata/lefdef/issue-2374/lib1.lef vendored Normal file
View File

@ -0,0 +1,20 @@
VERSION 5.8 ;
MACRO a
ORIGIN 0 0 ;
SIZE 600 BY 600 ;
OBS
LAYER M1 ;
RECT 10 10 590 590 ;
END
END a
MACRO b
ORIGIN -600 0 ;
SIZE 400 BY 500 ;
OBS
LAYER M1 ;
RECT 610 10 990 490 ;
END
END b

22
testdata/lefdef/issue-2374/lib2.lef vendored Normal file
View File

@ -0,0 +1,22 @@
VERSION 5.8 ;
MACRO b
ORIGIN -600 0 ;
SIZE 400 BY 500 ;
OBS
LAYER M1 ;
RECT 610 10 990 590 ;
END
END b
MACRO c
ORIGIN -500 -500 ;
SIZE 500 BY 500 ;
OBS
LAYER M1 ;
POLYGON 510 610 610 610 610 510 990 510 990 990 510 990 ;
LAYER overlap ;
RECT 500 700 1000 1000 ;
POLYGON 500 600 600 600 600 500 1000 500 1000 700 500 700 ;
END
END c

12
testdata/lefdef/issue-2374/tech.lef vendored Normal file
View File

@ -0,0 +1,12 @@
VERSION 5.8 ;
LAYER M1
TYPE ROUTING ;
DIRECTION HORIZONTAL ;
WIDTH 0.1 ;
PITCH 0.1 ;
END M1
LAYER overlap
TYPE OVERLAP ;
END overlap

6
testdata/lefdef/issue-2374/tech.map vendored Normal file
View File

@ -0,0 +1,6 @@
DIEAREA ALL 1 0
#BOUNDARY DIEAREA 1 0
BOUNDARY MACRO 1 0
#M1 LEFOBS 2 0
M1 LEFOBS 3 0
M1 BLOCKAGE 3 0

10
testdata/lefdef/issue-2374/top.def vendored Normal file
View File

@ -0,0 +1,10 @@
VERSION 5.8 ;
DESIGN top ;
UNITS DISTANCE MICRONS 1000 ;
DIEAREA ( 0 0 ) ( 1000000 1000000 ) ;
COMPONENTS 3 ;
- a a + PLACED ( 0 0 ) N ;
- b b + PLACED ( 600000 0 ) N ;
- c c + PLACED ( 500000 500000 ) N ;
END COMPONENTS
END DESIGN

View File

@ -392,6 +392,10 @@ class DBReaders_TestClass < TestBase
conf.read_lef_with_def = false conf.read_lef_with_def = false
assert_equal(conf.read_lef_with_def, false) assert_equal(conf.read_lef_with_def, false)
assert_equal(conf.skip_duplicate_macros, false)
conf.skip_duplicate_macros = true
assert_equal(conf.skip_duplicate_macros, true)
end end
# MAG Options # MAG Options