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);
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. "
"This provides some cheap, but risky way of merging files. Beware of cell name conflicts.")
"Multiple files can be combined using '+' or ','. '+' will combine the files in 'blending' mode. "
"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))
;

View File

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

View File

@ -483,7 +483,7 @@ TEST(10)
std::string input;
for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) {
if (i > 0) {
input += ",";
input += "+";
}
input += def_dir + "/" + def_files[i];
}
@ -510,3 +510,85 @@ TEST(10)
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_warn_count_for_same_message = 0;
m_first_warning = true;
m_expected_dbu = 0.0;
}
bool

View File

@ -259,6 +259,22 @@ public:
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:
ReaderBase *mp_actual_reader;
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.