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 "+".
This commit is contained in:
Matthias Koefferlein 2025-07-05 22:48:05 +02:00
parent 0ba6064507
commit d382629e8e
10 changed files with 165 additions and 17 deletions

View File

@ -43,8 +43,13 @@ int converter_main (int argc, char *argv[], const std::string &format)
generic_reader_options.add_options (cmd); generic_reader_options.add_options (cmd);
cmd << tl::arg ("input", &infile, "The input file (any format, may be gzip compressed)", 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. " "Multiple files can be combined using '+' or ','. '+' will combine the files in 'blending' mode. "
"This provides some cheap, but risky way of merging files. Beware of cell name conflicts.") "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)) << tl::arg ("output", &outfile, tl::sprintf ("The output file (%s format)", format))
; ;

View File

@ -22,6 +22,8 @@
#include "bdReaderOptions.h" #include "bdReaderOptions.h"
#include "dbLoadLayoutOptions.h" #include "dbLoadLayoutOptions.h"
#include "dbLayerMapping.h"
#include "dbCellMapping.h"
#include "tlCommandLineParser.h" #include "tlCommandLineParser.h"
#include "tlStream.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<std::string> split_file_list (const std::string &infile) static std::vector<std::vector<std::string> > split_file_list (const std::string &infile)
{ {
std::vector<std::string> files; std::vector<std::vector<std::string> > files;
files.push_back (std::vector<std::string> ());
size_t p = 0; size_t p = 0;
for (size_t pp = 0; (pp = find_file_sep (infile, p)) != std::string::npos; p = pp + 1) { while (true) {
files.push_back (std::string (infile, p, pp - p));
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<std::string> ());
}
p = sep + 1;
} }
files.push_back (std::string (infile, p));
return files; return files;
} }
@ -850,16 +865,47 @@ void read_files (db::Layout &layout, const std::string &infile, const db::LoadLa
// db::LayoutLocker locker (&layout); // db::LayoutLocker locker (&layout);
// but there are yet unknown side effects // but there are yet unknown side effects
std::vector<std::vector<std::string> > 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 // enter a LEF caching context for chaining multiple DEF with the same LEF
db::LoadLayoutOptions local_options (options); db::LoadLayoutOptions local_options (options);
local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true); local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true);
std::vector<std::string> files = split_file_list (infile); db::Layout tmp;
db::Layout *ly = (ff == files.begin () ? &layout : &tmp);
for (std::vector<std::string>::const_iterator f = files.begin (); f != files.end (); ++f) { for (auto f = ff->begin (); f != ff->end (); ++f) {
tl::InputStream stream (*f); tl::InputStream stream (*f);
db::Reader reader (stream); db::Reader reader (stream);
reader.read (layout, local_options); 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<db::cell_index_type> cells_target;
std::vector<db::cell_index_type> 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);
}
} }
} }

View File

@ -483,7 +483,7 @@ TEST(10)
std::string input; std::string input;
for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) { for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) {
if (i > 0) { if (i > 0) {
input += ","; input += "+";
} }
input += def_dir + "/" + def_files[i]; input += def_dir + "/" + def_files[i];
} }
@ -510,3 +510,85 @@ TEST(10)
db::compare_layouts (this, layout, input_au, db::WriteOAS); 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);
}

View File

@ -82,7 +82,6 @@ ReaderBase::init (const db::LoadLayoutOptions &options)
m_last_warning.clear (); m_last_warning.clear ();
m_warn_count_for_same_message = 0; m_warn_count_for_same_message = 0;
m_first_warning = true; m_first_warning = true;
m_expected_dbu = 0.0;
} }
bool bool

View File

@ -259,6 +259,22 @@ public:
return mp_actual_reader->warnings_as_errors (); 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: private:
ReaderBase *mp_actual_reader; ReaderBase *mp_actual_reader;
tl::InputStream &m_stream; tl::InputStream &m_stream;

BIN
testdata/bd/strm2oas_1.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_2.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_2_10nm.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_1.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_3.oas vendored Normal file

Binary file not shown.