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_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_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");
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"
"The following values are accepted for this option:\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"
"* 2: Never produce LEF geometry and assume FOREIGN always\n"
"* 2: Never produce LEF geometry and assume FOREIGN always (the default)\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 "
"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 "
"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.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 ();
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;
int m_lefdef_macro_resolution_mode;
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_separate_groups (false),
m_joined_paths (false),
m_skip_duplicate_macros (false),
m_macro_resolution_mode (0),
m_read_lef_with_def (true),
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_read_lef_with_def = d.m_read_lef_with_def;
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 ());
}
return *this;
@ -1070,6 +1072,9 @@ LEFDEFReaderState::ensure_lef_importer (int warn_level)
{
if (! mp_lef_importer.get ()) {
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;
}
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
{
return m_map_files.empty () ? std::string () : m_map_files.front ();
@ -1055,6 +1065,7 @@ private:
bool m_separate_groups;
bool m_joined_paths;
std::vector<std::string> m_map_files;
bool m_skip_duplicate_macros;
unsigned int m_macro_resolution_mode;
bool m_read_lef_with_def;
std::vector<std::string> m_lef_files;

View File

@ -34,7 +34,8 @@ namespace db
// LEFImporter implementation
LEFImporter::LEFImporter (int warn_level)
: LEFDEFImporter (warn_level)
: LEFDEFImporter (warn_level),
m_skip_duplicate_macros (false)
{
// .. nothing yet ..
}
@ -884,15 +885,20 @@ void
LEFImporter::read_macro (Layout &layout)
{
std::string mn = get ();
if (m_macros.find (mn) != m_macros.end ()) {
error (tl::to_string (tr ("Duplicate MACRO name: ")) + mn);
}
set_cellname (mn);
GeometryBasedLayoutGenerator *mg = new GeometryBasedLayoutGenerator ();
reader_state ()->register_macro_cell (mn, mg);
GeometryBasedLayoutGenerator *mg = 0;
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;
std::string foreign_name;
@ -965,7 +971,7 @@ LEFImporter::read_macro (Layout &layout)
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) {
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);
}
}
@ -1052,7 +1058,9 @@ LEFImporter::read_macro (Layout &layout)
} else if (test ("FIXEDMASK")) {
mg->set_fixedmask (true);
if (mg) {
mg->set_fixedmask (true);
}
expect (";");
} else {
@ -1075,8 +1083,10 @@ LEFImporter::read_macro (Layout &layout)
}
mg->add_box (std::string (), Outline, db::Box (-origin, -origin + size), 0, 0);
mg->subtract_overlap_from_outline (m_overlap_layers);
if (mg) {
mg->add_box (std::string (), Outline, db::Box (-origin, -origin + size), 0, 0);
mg->subtract_overlap_from_outline (m_overlap_layers);
}
MacroDesc macro_desc;
macro_desc.foreign_name = foreign_name;

View File

@ -147,6 +147,31 @@ public:
*/
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:
void do_read (db::Layout &layout);
@ -159,6 +184,7 @@ private:
std::map<std::string, ViaDesc> m_vias;
std::set<std::string> m_routing_layers, m_cut_layers, m_overlap_layers;
std::map<std::string, unsigned int> m_num_masks;
bool m_skip_duplicate_macros;
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);

View File

@ -967,6 +967,24 @@ gsi::Class<db::LEFDEFReaderOptions> decl_lefdef_config ("db", "LEFDEFReaderConfi
"\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,
"@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 "

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);
}
// 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
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
# MAG Options