mirror of https://github.com/KLayout/klayout.git
Added more tests for strmcmp, first version of better strmxor
This commit is contained in:
parent
2a6c2ee735
commit
6e3bf68da9
|
|
@ -21,6 +21,7 @@ SOURCES = \
|
||||||
strm2gdstxt.cc \
|
strm2gdstxt.cc \
|
||||||
strm2txt.cc \
|
strm2txt.cc \
|
||||||
strmcmp.cc \
|
strmcmp.cc \
|
||||||
|
strmxor.cc \
|
||||||
|
|
||||||
HEADERS = \
|
HEADERS = \
|
||||||
bdCommon.h \
|
bdCommon.h \
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
|
||||||
)
|
)
|
||||||
<< tl::arg ("-s|--silent", &silent, "Enables silent mode",
|
<< tl::arg ("-s|--silent", &silent, "Enables silent mode",
|
||||||
"In silent mode, no differences are printed, but the exit code indicates whether "
|
"In silent mode, no differences are printed, but the exit code indicates whether "
|
||||||
"the layout are the same (0) or differences exist (> 0)."
|
"the layouts are the same (0) or differences exist (> 0)."
|
||||||
)
|
)
|
||||||
<< tl::arg ("#!--with-text-orientation", &no_text_orientation, "Compares orientations for texts",
|
<< tl::arg ("#!--with-text-orientation", &no_text_orientation, "Compares orientations for texts",
|
||||||
"With this option, text orientation is compared too. The position of the "
|
"With this option, text orientation is compared too. The position of the "
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,290 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
KLayout Layout Viewer
|
||||||
|
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bdReaderOptions.h"
|
||||||
|
#include "dbLayout.h"
|
||||||
|
#include "dbTilingProcessor.h"
|
||||||
|
#include "dbReader.h"
|
||||||
|
#include "dbWriter.h"
|
||||||
|
#include "dbSaveLayoutOptions.h"
|
||||||
|
#include "tlCommandLineParser.h"
|
||||||
|
|
||||||
|
BD_PUBLIC int strmxor (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
bd::GenericReaderOptions generic_reader_options_a;
|
||||||
|
generic_reader_options_a.set_prefix ("a");
|
||||||
|
generic_reader_options_a.set_long_prefix ("a-");
|
||||||
|
generic_reader_options_a.set_group_prefix ("Input A");
|
||||||
|
|
||||||
|
bd::GenericReaderOptions generic_reader_options_b;
|
||||||
|
generic_reader_options_b.set_prefix ("b");
|
||||||
|
generic_reader_options_b.set_long_prefix ("b-");
|
||||||
|
generic_reader_options_b.set_group_prefix ("Input B");
|
||||||
|
|
||||||
|
std::string infile_a, infile_b, output;
|
||||||
|
std::string top_a, top_b;
|
||||||
|
bool dont_summarize_missing_layers = false;
|
||||||
|
bool silent = false;
|
||||||
|
std::vector<double> tolerances;
|
||||||
|
int tolerance_bump = 10000;
|
||||||
|
int threads = 1;
|
||||||
|
double tile_size = 0.0;
|
||||||
|
|
||||||
|
tl::CommandLineOptions cmd;
|
||||||
|
generic_reader_options_a.add_options (cmd);
|
||||||
|
generic_reader_options_b.add_options (cmd);
|
||||||
|
|
||||||
|
cmd << tl::arg ("input_a", &infile_a, "The first input file (any format, may be gzip compressed)")
|
||||||
|
<< tl::arg ("input_b", &infile_b, "The second input file (any format, may be gzip compressed)")
|
||||||
|
<< tl::arg ("?output", &output, "The output file to which the XOR differences are written",
|
||||||
|
"This argument is optional. If not given, the exit status along indicates whether the layouts "
|
||||||
|
"are identical or not."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-ta|--top-a=name", &top_a, "Specifies the cell to take as top cell from the first layout",
|
||||||
|
"Use this option to take a specific cell as the top cell from the first layout. All "
|
||||||
|
"cells not called directly or indirectly from this cell are ignored. If you use this option, "
|
||||||
|
"--top-b must be specified too and can be different from the first layout's top cell."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-tb|--top-b=name", &top_b, "Specifies the cell to take as top cell from the second layout",
|
||||||
|
"See --top-a for details."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-s|--silent", &silent, "Enables silent mode",
|
||||||
|
"In silent mode, no summary is printed, but the exit code indicates whether "
|
||||||
|
"the layouts are the same (0) or differences exist (> 0)."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-l|--layer-details", &dont_summarize_missing_layers, "Output details about differences for missing layers",
|
||||||
|
"With this option, missing layers are treated as \"empty\" and the whole layer of the other "
|
||||||
|
"layout is output. Without this option, a message is printed for missing layers instead."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-t|--tolerance=values", &tolerances, "Specifies tolerances for the geometry compare",
|
||||||
|
"This option can take multiple tolerance values. The values are given in micrometer units and "
|
||||||
|
"are separated by a comma. If a tolerance is given, XOR differences are "
|
||||||
|
"only reported when they are larger than the tolerance value. Tolerance values must be given in "
|
||||||
|
"ascending order."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-n|--threads=threads", &threads, "Specifies the number of threads to use",
|
||||||
|
"If given, multiple threads are used for the XOR computation. This way, multiple cores can "
|
||||||
|
"be utilized."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-p|--tiles=size", &tile_size, "Specifies tiling mode",
|
||||||
|
"In tiling mode, the layout is divided into tiles of the given size. Each tile is computed "
|
||||||
|
"individually. Multiple tiles can be processed in parallel on multiple cores."
|
||||||
|
)
|
||||||
|
<< tl::arg ("-b|--layer-bump=offset", &tolerance_bump, "Specifies the layer number offset to add for every tolerance",
|
||||||
|
"This value is the number added to the original layer number to form a layer set for each tolerance "
|
||||||
|
"value. If this value is set to 1000, the first tolerance value will produce XOR results on the "
|
||||||
|
"original layers. A second tolerance value will produce XOR results on the original layers + 1000. "
|
||||||
|
"A third tolerance value will produce XOR results on the original layers + 2000."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
cmd.brief ("This program will compare two layout files with a geometrical XOR operation");
|
||||||
|
|
||||||
|
cmd.parse (argc, argv);
|
||||||
|
|
||||||
|
if (top_a.empty () != top_b.empty ()) {
|
||||||
|
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tolerances.empty ()) {
|
||||||
|
tolerances.push_back (0.0);
|
||||||
|
} else {
|
||||||
|
for (std::vector<double>::const_iterator t = tolerances.begin () + 1; t != tolerances.end (); ++t) {
|
||||||
|
if (*(t - 1) > *t - db::epsilon) {
|
||||||
|
throw tl::Exception ("Tolerance values (-t|--tolerances) must be given in ascending order");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db::Layout layout_a;
|
||||||
|
db::Layout layout_b;
|
||||||
|
|
||||||
|
{
|
||||||
|
db::LoadLayoutOptions load_options;
|
||||||
|
generic_reader_options_a.configure (load_options);
|
||||||
|
|
||||||
|
tl::InputStream stream (infile_a);
|
||||||
|
db::Reader reader (stream);
|
||||||
|
reader.read (layout_a, load_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
db::LoadLayoutOptions load_options;
|
||||||
|
generic_reader_options_b.configure (load_options);
|
||||||
|
|
||||||
|
tl::InputStream stream (infile_b);
|
||||||
|
db::Reader reader (stream);
|
||||||
|
reader.read (layout_b, load_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (top_a.empty ()) {
|
||||||
|
|
||||||
|
db::Layout::top_down_const_iterator t;
|
||||||
|
|
||||||
|
t = layout_a.begin_top_down ();
|
||||||
|
if (t != layout_a.end_top_cells ()) {
|
||||||
|
top_a = layout_a.cell_name (*t);
|
||||||
|
++t;
|
||||||
|
if (t != layout_a.end_top_cells ()) {
|
||||||
|
throw tl::Exception ("Top cell of first layout is not unique and cannot be determined automatically");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t = layout_b.begin_top_down ();
|
||||||
|
if (t != layout_b.end_top_cells ()) {
|
||||||
|
top_b = layout_b.cell_name (*t);
|
||||||
|
++t;
|
||||||
|
if (t != layout_b.end_top_cells ()) {
|
||||||
|
throw tl::Exception ("Top cell of second layout is not unique and cannot be determined automatically");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<bool, db::cell_index_type> index_a = layout_a.cell_by_name (top_a.c_str ());
|
||||||
|
std::pair<bool, db::cell_index_type> index_b = layout_b.cell_by_name (top_b.c_str ());
|
||||||
|
|
||||||
|
if (! index_a.first) {
|
||||||
|
throw tl::Exception ("'" + top_a + "' is not a valid cell name in first layout");
|
||||||
|
}
|
||||||
|
if (! index_b.first) {
|
||||||
|
throw tl::Exception ("'" + top_b + "' is not a valid cell name in second layout");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<db::LayerProperties, std::pair<int, int> > l2l_map;
|
||||||
|
|
||||||
|
for (db::Layout::layer_iterator l = layout_a.begin_layers (); l != layout_a.end_layers (); ++l) {
|
||||||
|
l2l_map.insert (std::make_pair (*(*l).second, std::make_pair (-1, -1))).first->second.first = (*l).first;
|
||||||
|
}
|
||||||
|
for (db::Layout::layer_iterator l = layout_b.begin_layers (); l != layout_b.end_layers (); ++l) {
|
||||||
|
l2l_map.insert (std::make_pair (*(*l).second, std::make_pair (-1, -1))).first->second.second = (*l).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
db::TilingProcessor proc;
|
||||||
|
proc.set_dbu (std::min (layout_a.dbu (), layout_b.dbu ()));
|
||||||
|
proc.set_threads (std::max (1, threads));
|
||||||
|
if (tile_size > db::epsilon) {
|
||||||
|
proc.tile_size (tile_size, tile_size);
|
||||||
|
}
|
||||||
|
proc.tile_border (tolerances.back () * 2.0, tolerances.back () * 2.0);
|
||||||
|
|
||||||
|
db::Layout output_layout;
|
||||||
|
output_layout.dbu (proc.dbu ());
|
||||||
|
|
||||||
|
db::cell_index_type output_top = output_layout.add_cell ("XOR");
|
||||||
|
|
||||||
|
std::vector<unsigned int> output_layers;
|
||||||
|
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator ll = l2l_map.begin (); ll != l2l_map.end (); ++ll) {
|
||||||
|
|
||||||
|
if (ll->second.first < 0 && ! dont_summarize_missing_layers) {
|
||||||
|
|
||||||
|
tl::warn << "Layer " << ll->first.to_string () << " is not present in first layout, but in second";
|
||||||
|
result = false;
|
||||||
|
|
||||||
|
} else if (ll->second.second < 0 && ! dont_summarize_missing_layers) {
|
||||||
|
|
||||||
|
tl::warn << "Layer " << ll->first.to_string () << " is not present in second layout, but in first";
|
||||||
|
result = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
std::string in_a = "a" + tl::to_string (index);
|
||||||
|
std::string in_b = "b" + tl::to_string (index);
|
||||||
|
|
||||||
|
if (ll->second.first < 0) {
|
||||||
|
proc.input (in_a, db::RecursiveShapeIterator ());
|
||||||
|
} else {
|
||||||
|
proc.input (in_a, db::RecursiveShapeIterator (layout_a, layout_a.cell (index_a.second), ll->second.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ll->second.second < 0) {
|
||||||
|
proc.input (in_b, db::RecursiveShapeIterator ());
|
||||||
|
} else {
|
||||||
|
proc.input (in_b, db::RecursiveShapeIterator (layout_b, layout_b.cell (index_b.second), ll->second.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string expr = "var x=" + in_a + "^" + in_b + "; ";
|
||||||
|
|
||||||
|
int tol_index = 1;
|
||||||
|
for (std::vector<double>::const_iterator t = tolerances.begin (); t != tolerances.end (); ++t) {
|
||||||
|
|
||||||
|
std::string out = "o" + tl::to_string (index) + "_" + tl::to_string (tol_index);
|
||||||
|
|
||||||
|
db::LayerProperties lp = ll->first;
|
||||||
|
if (lp.layer >= 0) {
|
||||||
|
lp.layer += (tol_index - 1) * tolerance_bump;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int output_layer = output_layout.insert_layer (lp);
|
||||||
|
output_layers.push_back (output_layer);
|
||||||
|
|
||||||
|
// @@@ TODO: silent mode
|
||||||
|
proc.output (out, output_layout, output_top, output_layer);
|
||||||
|
|
||||||
|
if (*t > db::epsilon) {
|
||||||
|
expr += "x=x.sized(-int(" + tl::to_string (*t) + "/_dbu)/2).sized(int(" + tl::to_string (*t) + "/_dbu)/2); ";
|
||||||
|
}
|
||||||
|
expr += "_output(" + out + ",x); ";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
proc.queue (expr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs the processor
|
||||||
|
|
||||||
|
proc.execute ("Running XOR");
|
||||||
|
|
||||||
|
// Write the output layout
|
||||||
|
|
||||||
|
if (! output.empty()) {
|
||||||
|
|
||||||
|
db::SaveLayoutOptions save_options;
|
||||||
|
save_options.set_format_from_filename (output);
|
||||||
|
|
||||||
|
tl::OutputStream stream (output);
|
||||||
|
db::Writer writer (save_options);
|
||||||
|
writer.write (output_layout, stream);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the output status based on the output top cell's emptyness
|
||||||
|
|
||||||
|
for (std::vector<unsigned int>::const_iterator l = output_layers.begin (); l != output_layers.end () && result; ++l) {
|
||||||
|
result = output_layout.cell (output_top).bbox (*l).empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@@ TODO: print a nice summary unless "silent" is set
|
||||||
|
|
||||||
|
return result ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,236 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
KLayout Layout Viewer
|
|
||||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "bdInit.h"
|
|
||||||
#include "dbLayout.h"
|
|
||||||
#include "dbReader.h"
|
|
||||||
#include "dbWriter.h"
|
|
||||||
#include "dbShapeProcessor.h"
|
|
||||||
#include "tlString.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
syntax ()
|
|
||||||
{
|
|
||||||
printf ("Syntax: strmxor [-u <undersize>] [-topa <topcell-a>] [-topb <topcell-b>] [-oasis|-oas] [-gds2|-gds] <infile-a> <infile-b> [<outfile>]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char *argv [])
|
|
||||||
{
|
|
||||||
std::string topcell_a;
|
|
||||||
std::string topcell_b;
|
|
||||||
std::string infile_a;
|
|
||||||
std::string infile_b;
|
|
||||||
std::string outfile;
|
|
||||||
double undersize = 0.0;
|
|
||||||
bool format_set = false;
|
|
||||||
std::string format;
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
|
||||||
std::string o (argv[i]);
|
|
||||||
if (o == "-u") {
|
|
||||||
if (i < argc - 1) {
|
|
||||||
++i;
|
|
||||||
tl::from_string (argv[i], undersize);
|
|
||||||
}
|
|
||||||
} else if (o == "-topa") {
|
|
||||||
if (i < argc - 1) {
|
|
||||||
++i;
|
|
||||||
topcell_a = argv[i];
|
|
||||||
}
|
|
||||||
} else if (o == "-topb") {
|
|
||||||
if (i < argc - 1) {
|
|
||||||
++i;
|
|
||||||
topcell_b = argv[i];
|
|
||||||
}
|
|
||||||
} else if (o == "-oasis" || o == "-oas") {
|
|
||||||
format_set = true;
|
|
||||||
format = "OASIS";
|
|
||||||
} else if (o == "-gds2" || o == "-gds") {
|
|
||||||
format_set = true;
|
|
||||||
format = "GDS2";
|
|
||||||
} else if (o == "-h" || o == "-help" || o == "--help") {
|
|
||||||
syntax ();
|
|
||||||
return 0;
|
|
||||||
} else if (argv[i][0] == '-') {
|
|
||||||
throw tl::Exception("Unknown option: %s - use '-h' for help", (const char *) argv[i]);
|
|
||||||
} else if (infile_a.empty ()) {
|
|
||||||
infile_a = argv[i];
|
|
||||||
} else if (infile_b.empty ()) {
|
|
||||||
infile_b = argv[i];
|
|
||||||
} else if (outfile.empty ()) {
|
|
||||||
outfile = argv[i];
|
|
||||||
} else {
|
|
||||||
throw tl::Exception("Superfluous argument: %s - use '-h' for help", (const char *) argv[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (infile_a.empty () || infile_b.empty ()) {
|
|
||||||
throw tl::Exception("Both input files must be specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
db::Manager m;
|
|
||||||
db::Layout layout_a (&m);
|
|
||||||
db::Layout layout_b (&m);
|
|
||||||
|
|
||||||
{
|
|
||||||
tl::InputStream stream (infile_a);
|
|
||||||
db::Reader reader (stream);
|
|
||||||
reader.read (layout_a);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
tl::InputStream stream (infile_b);
|
|
||||||
db::Reader reader (stream);
|
|
||||||
reader.read (layout_b);
|
|
||||||
}
|
|
||||||
|
|
||||||
db::cell_index_type top_a;
|
|
||||||
if (topcell_a.empty ()) {
|
|
||||||
db::Layout::top_down_iterator t = layout_a.begin_top_down ();
|
|
||||||
if (t == layout_a.end_top_cells ()) {
|
|
||||||
throw tl::Exception ("Layout A (%s) does not have a top cell", infile_a);
|
|
||||||
}
|
|
||||||
top_a = *t++;
|
|
||||||
if (t != layout_a.end_top_cells ()) {
|
|
||||||
throw tl::Exception ("Layout A (%s) has multiple top cells", infile_a);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::pair<bool, db::cell_index_type> cn = layout_a.cell_by_name (topcell_a.c_str ());
|
|
||||||
if (! cn.first) {
|
|
||||||
throw tl::Exception ("Layout A (%s) does not have a topcell called '%s'", infile_a, topcell_a);
|
|
||||||
}
|
|
||||||
top_a = cn.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
db::cell_index_type top_b;
|
|
||||||
if (topcell_b.empty ()) {
|
|
||||||
db::Layout::top_down_iterator t = layout_b.begin_top_down ();
|
|
||||||
if (t == layout_b.end_top_cells ()) {
|
|
||||||
throw tl::Exception ("Layout B (%s) does not have a top cell", infile_b);
|
|
||||||
}
|
|
||||||
top_b = *t++;
|
|
||||||
if (t != layout_b.end_top_cells ()) {
|
|
||||||
throw tl::Exception ("Layout B (%s) has multiple top cells", infile_b);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::pair<bool, db::cell_index_type> cn = layout_b.cell_by_name (topcell_b.c_str ());
|
|
||||||
if (! cn.first) {
|
|
||||||
throw tl::Exception ("Layout B (%s) does not have a topcell called '%s'", infile_b, topcell_b);
|
|
||||||
}
|
|
||||||
top_b = cn.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fabs (layout_a.dbu () - layout_b.dbu ()) > 1e-6) {
|
|
||||||
throw tl::Exception("Input file database units differ (A:%g vs. B:%g)", layout_a.dbu (), layout_b.dbu ());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<db::LayerProperties, std::pair<int, int> > all_layers;
|
|
||||||
for (unsigned int i = 0; i < layout_a.layers (); ++i) {
|
|
||||||
if (layout_a.is_valid_layer (i)) {
|
|
||||||
all_layers.insert (std::make_pair(layout_a.get_properties (i), std::make_pair(-1, -1))).first->second.first = int (i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; i < layout_b.layers (); ++i) {
|
|
||||||
if (layout_b.is_valid_layer (i)) {
|
|
||||||
all_layers.insert (std::make_pair(layout_b.get_properties (i), std::make_pair(-1, -1))).first->second.second = int (i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db::Layout output;
|
|
||||||
output.dbu (layout_a.dbu ());
|
|
||||||
db::cell_index_type top_id = output.add_cell (layout_a.cell_name (top_a));
|
|
||||||
|
|
||||||
db::Coord us = db::coord_traits<db::Coord>::rounded (undersize / layout_a.dbu ());
|
|
||||||
|
|
||||||
db::ShapeProcessor sp;
|
|
||||||
|
|
||||||
size_t ndiff = 0;
|
|
||||||
|
|
||||||
for (std::map<db::LayerProperties, std::pair<int, int> >::const_iterator l = all_layers.begin (); l != all_layers.end (); ++l) {
|
|
||||||
|
|
||||||
int layer_id = output.insert_layer (l->first);
|
|
||||||
|
|
||||||
if (l->second.first >= 0 && l->second.second >= 0) {
|
|
||||||
|
|
||||||
sp.boolean (layout_a, layout_a.cell (top_a), l->second.first, layout_b, layout_b.cell (top_b), l->second.second,
|
|
||||||
output.cell (top_id).shapes (layer_id), db::BooleanOp::Xor, true /*recursive*/);
|
|
||||||
|
|
||||||
sp.size (output, output.cell (top_id), layer_id, output.cell (top_id).shapes (layer_id), -us, (unsigned int) 2, true /*recursive*/);
|
|
||||||
|
|
||||||
} else if (l->second.first >= 0) {
|
|
||||||
|
|
||||||
sp.size (layout_a, layout_a.cell (top_a), l->second.first, output.cell (top_id).shapes (layer_id), -us, (unsigned int) 2, true /*recursive*/);
|
|
||||||
|
|
||||||
} else if (l->second.second >= 0) {
|
|
||||||
|
|
||||||
sp.size (layout_b, layout_b.cell (top_b), l->second.second, output.cell (top_id).shapes (layer_id), -us, (unsigned int) 2, true /*recursive*/);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t n = output.cell (top_id).shapes (layer_id).size ();
|
|
||||||
// if (n > 0) {
|
|
||||||
ndiff += n;
|
|
||||||
tl::info << " " << l->first.to_string () << ": " << n;
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ndiff > 0) {
|
|
||||||
tl::info << "----------------------------------------------------";
|
|
||||||
tl::info << " Total differences: " << ndiff;
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! outfile.empty ()) {
|
|
||||||
|
|
||||||
db::SaveLayoutOptions options;
|
|
||||||
options.set_format_from_filename (outfile);
|
|
||||||
if (format_set) {
|
|
||||||
options.set_format (format);
|
|
||||||
}
|
|
||||||
|
|
||||||
db::Writer writer (options);
|
|
||||||
tl::OutputStream file (outfile, tl::OutputStream::OM_Auto);
|
|
||||||
writer.write (output, file);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (std::exception &ex) {
|
|
||||||
tl::error << ex.what ();
|
|
||||||
return 1;
|
|
||||||
} catch (tl::Exception &ex) {
|
|
||||||
tl::error << ex.msg ();
|
|
||||||
return 1;
|
|
||||||
} catch (...) {
|
|
||||||
tl::error << "unspecific error";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,13 +1,2 @@
|
||||||
|
|
||||||
include($$PWD/../../../klayout.pri)
|
include($$PWD/../buddy_app.pri)
|
||||||
|
|
||||||
TEMPLATE = app
|
|
||||||
|
|
||||||
TARGET = strmxor
|
|
||||||
DESTDIR = $$OUT_PWD/../../..
|
|
||||||
|
|
||||||
SOURCES = strmxor.cc
|
|
||||||
|
|
||||||
INCLUDEPATH += ../bd ../../../tl ../../../db ../../../gsi
|
|
||||||
DEPENDPATH += ../bd ../../../tl ../../../db ../../../gsi
|
|
||||||
LIBS += -L$$DESTDIR -lklayout_bd -lklayout_tl -lklayout_db -lklayout_gsi
|
|
||||||
|
|
|
||||||
|
|
@ -533,3 +533,57 @@ TEST(8B)
|
||||||
"Cell TRANS in a is renamed to SNART in b\n"
|
"Cell TRANS in a is renamed to SNART in b\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(9A)
|
||||||
|
{
|
||||||
|
CaptureChannel cap;
|
||||||
|
|
||||||
|
tl::warn.add (&cap, false);
|
||||||
|
tl::info.add (&cap, false);
|
||||||
|
tl::error.add (&cap, false);
|
||||||
|
|
||||||
|
std::string input_a = ut::testsrc ();
|
||||||
|
input_a += "/testdata/bd/strmcmp_in.gds";
|
||||||
|
|
||||||
|
std::string input_b = ut::testsrc ();
|
||||||
|
input_b += "/testdata/bd/strmcmp_ref9.gds";
|
||||||
|
|
||||||
|
char *argv[] = { "x", const_cast<char *> (input_a.c_str ()), const_cast<char *> (input_b.c_str ()) };
|
||||||
|
|
||||||
|
EXPECT_EQ (strmcmp (sizeof (argv) / sizeof (argv[0]), argv), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ (cap.captured_text (),
|
||||||
|
"Layer 8/1 is not present in layout b, but in a\n"
|
||||||
|
"Layouts differ\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(9B)
|
||||||
|
{
|
||||||
|
CaptureChannel cap;
|
||||||
|
|
||||||
|
tl::warn.add (&cap, false);
|
||||||
|
tl::info.add (&cap, false);
|
||||||
|
tl::error.add (&cap, false);
|
||||||
|
|
||||||
|
std::string input_a = ut::testsrc ();
|
||||||
|
input_a += "/testdata/bd/strmcmp_in.gds";
|
||||||
|
|
||||||
|
std::string input_b = ut::testsrc ();
|
||||||
|
input_b += "/testdata/bd/strmcmp_ref9.gds";
|
||||||
|
|
||||||
|
char *argv[] = { "x", "-l", const_cast<char *> (input_a.c_str ()), const_cast<char *> (input_b.c_str ()) };
|
||||||
|
|
||||||
|
EXPECT_EQ (strmcmp (sizeof (argv) / sizeof (argv[0]), argv), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ (cap.captured_text (),
|
||||||
|
"Texts differ for layer 8/1 in cell RINGO\n"
|
||||||
|
"Not in b but in a:\n"
|
||||||
|
" ('FB',r0 0,1800)\n"
|
||||||
|
" ('OSC',r0 24560,1800)\n"
|
||||||
|
" ('VDD',r0 0,2800)\n"
|
||||||
|
" ('VSS',r0 0,0)\n"
|
||||||
|
"Not in a but in b:\n"
|
||||||
|
"Layouts differ\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue