diff --git a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui index 6be94aaf0..7e3482704 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui +++ b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui @@ -190,7 +190,7 @@ ... - + :/clear.png:/clear.png @@ -204,7 +204,7 @@ ... - + :/add.png:/add.png @@ -218,7 +218,7 @@ ... - + :/up.png:/up.png @@ -232,7 +232,7 @@ ... - + :/down.png:/down.png @@ -298,7 +298,7 @@ buttonBox - + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAG.cc b/src/plugins/streamers/magic/db_plugin/dbMAG.cc new file mode 100644 index 000000000..4f00a9c18 --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/dbMAG.cc @@ -0,0 +1,118 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbMAG.h" +#include "dbMAGReader.h" +#include "dbMAGWriter.h" +#include "dbStream.h" + +#include "tlClassRegistry.h" + +namespace db +{ + +// --------------------------------------------------------------- +// MAGDiagnostics implementation + +MAGDiagnostics::~MAGDiagnostics () +{ + // .. nothing yet .. +} + +// --------------------------------------------------------------- +// MAG format declaration + +class MAGFormatDeclaration + : public db::StreamFormatDeclaration +{ +public: + MAGFormatDeclaration () + { + // .. nothing yet .. + } + + virtual std::string format_name () const { return "MAG"; } + virtual std::string format_desc () const { return "Magic"; } + virtual std::string format_title () const { return "MAG (Magic layout format)"; } + virtual std::string file_format () const { return "MAG files (*.MAG *.msg *.mag.gz *.MAG.gz)"; } + + virtual bool detect (tl::InputStream &s) const + { + return s.read_all (5) == "magic"; + } + + virtual ReaderBase *create_reader (tl::InputStream &s) const + { + return new db::MAGReader (s); + } + + virtual WriterBase *create_writer () const + { + return new db::MAGWriter (); + } + + virtual bool can_read () const + { + return true; + } + + virtual bool can_write () const + { + return true; + } + + virtual tl::XMLElementBase *xml_reader_options_element () const + { + return new db::ReaderOptionsXMLElement ("cif", + tl::make_member (&db::MAGReaderOptions::lambda, "lambda") + + tl::make_member (&db::MAGReaderOptions::dbu, "dbu") + + tl::make_member (&db::MAGReaderOptions::layer_map, "layer-map") + + tl::make_member (&db::MAGReaderOptions::create_other_layers, "create-other-layers") + + tl::make_member (&db::MAGReaderOptions::keep_layer_names, "keep-layer-names") + + tl::make_element, db::MAGReaderOptions> (&db::MAGReaderOptions::lib_paths, "lib-paths", + tl::make_member::const_iterator, std::vector > (&std::vector::begin, &std::vector::end, &std::vector::push_back, "lib-path") + ) + ); + } + + virtual tl::XMLElementBase *xml_writer_options_element () const + { +// @@@ + return new db::WriterOptionsXMLElement ("cif", + tl::make_member (&db::MAGWriterOptions::dummy_calls, "dummy-calls") + + tl::make_member (&db::MAGWriterOptions::blank_separator, "blank-separator") + ); +// @@@ + } +}; + +// NOTE: Because MAG has such a high degree of syntactic freedom, the detection is somewhat +// fuzzy: do MAG at the very end of the detection chain +static tl::RegisteredClass reader_decl (new MAGFormatDeclaration (), 2200, "MAG"); + +// provide a symbol to force linking against +int force_link_MAG = 0; + +} + + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAG.h b/src/plugins/streamers/magic/db_plugin/dbMAG.h new file mode 100644 index 000000000..988fa6858 --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/dbMAG.h @@ -0,0 +1,62 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + + +#ifndef HDR_dbMAG +#define HDR_dbMAG + +#include "dbPoint.h" + +#include "tlException.h" +#include "tlInternational.h" +#include "tlString.h" +#include "tlAssert.h" + +#include +#include + +namespace db +{ + +/** + * @brief The diagnostics interface for reporting problems in the reader or writer + */ +class MAGDiagnostics +{ +public: + virtual ~MAGDiagnostics (); + + /** + * @brief Issue an error with positional information + */ + virtual void error (const std::string &txt) = 0; + + /** + * @brief Issue a warning with positional information + */ + virtual void warn (const std::string &txt) = 0; +}; + +} + +#endif + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGFormat.h b/src/plugins/streamers/magic/db_plugin/dbMAGFormat.h new file mode 100644 index 000000000..4cf3a4c66 --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/dbMAGFormat.h @@ -0,0 +1,180 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + +#ifndef HDR_dbMAGFormat +#define HDR_dbMAGFormat + +#include "dbSaveLayoutOptions.h" +#include "dbLoadLayoutOptions.h" +#include "dbPluginCommon.h" + +namespace db +{ + +/** + * @brief Structure that holds the MAG specific options for the reader + * NOTE: this structure is non-public linkage by intention. This way it's instantiated + * in all compile units and the shared object does not need to be linked. + */ +class DB_PLUGIN_PUBLIC MAGReaderOptions + : public FormatSpecificReaderOptions +{ +public: + /** + * @brief The constructor + */ + MAGReaderOptions () + : lambda (1.0), + dbu (0.001), + create_other_layers (true), + keep_layer_names (false) + { + // .. nothing yet .. + } + + /** + * @brief Specifies the lambda value + * + * The lambda value is the basic scaling parameter + */ + double lambda; + + /** + * @brief Specify the database unit to produce + * + * Specify the database unit which the resulting layout will receive. + */ + double dbu; + + /** + * @brief Specifies a layer mapping + * + * If a layer mapping is specified, only the given layers are read. + * Otherwise, all layers are read. + * Setting "create_other_layers" to true will make the reader + * create other layers for all layers not given in the layer map. + * Setting an empty layer map and create_other_layers to true effectively + * enables all layers for reading. + */ + db::LayerMap layer_map; + + /** + * @brief A flag indicating that a new layers shall be created + * + * If this flag is set to true, layers not listed in the layer map a created + * too. + */ + bool create_other_layers; + + /** + * @brief A flag indicating whether the names of layers shall be kept as such + * + * If this flag is set to false (the default), layer name translation happens. + * If set to true, translation will not happen. + * Name translation will try to extract GDS layer/datatype numbers from the + * layer names. If this value is set to true, no name translation happens. + */ + bool keep_layer_names; + + /** + * @brief The library paths + * + * The library paths are the places where library references are looked up from. + * Expression interpolation happens inside these paths. + * "tech_dir", "tech_file", "tech_name" are variables by which you can refer to + * technology parameters. Relative paths will be resolved relative to the current + * file read. + */ + std::vector lib_paths; + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual FormatSpecificReaderOptions *clone () const + { + return new MAGReaderOptions (*this); + } + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual const std::string &format_name () const + { + static const std::string n ("MAG"); + return n; + } +}; + +/** + * @brief Structure that holds the MAG specific options for the Writer + * NOTE: this structure is non-public linkage by intention. This way it's instantiated + * in all compile units and the shared object does not need to be linked. + */ +class DB_PLUGIN_PUBLIC MAGWriterOptions + : public FormatSpecificWriterOptions +{ +public: + /** + * @brief The constructor + */ + MAGWriterOptions () + : dummy_calls (false), blank_separator (false) + { + // .. nothing yet .. + } + + /** + * @brief A flag indicating whether dummy calls shall be written + * If this flag is true, the writer will produce dummy cell calls on global + * level for all top cells. + */ + bool dummy_calls; + + /** + * @brief A flag indicating whether to use blanks as x/y separators + * If this flag is true, blank characters will be used to separate x and y values. + * Otherwise comma characters will be used. + */ + bool blank_separator; + + /** + * @brief Implementation of FormatSpecificWriterOptions + */ + virtual FormatSpecificWriterOptions *clone () const + { + return new MAGWriterOptions (*this); + } + + /** + * @brief Implementation of FormatSpecificWriterOptions + */ + virtual const std::string &format_name () const + { + static std::string n ("MAG"); + return n; + } +}; + +} + +#endif + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc new file mode 100644 index 000000000..938d4342e --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc @@ -0,0 +1,862 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbMAGReader.h" +#include "dbStream.h" +#include "dbObjectWithProperties.h" +#include "dbArray.h" +#include "dbStatic.h" + +#include "tlException.h" +#include "tlString.h" +#include "tlClassRegistry.h" + +#include + +namespace db +{ + +// --------------------------------------------------------------- +// MAGReader + + +MAGReader::MAGReader (tl::InputStream &s) + : m_stream (s), + m_progress (tl::to_string (tr ("Reading MAG file")), 1000), + m_lambda (1.0), m_dbu (0.001) +{ + m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); + m_progress.set_format_unit (1000.0); + m_progress.set_unit (100000.0); +} + +MAGReader::~MAGReader () +{ + // .. nothing yet .. +} + +const LayerMap & +MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) +{ + prepare_layers (); + + const db::MAGReaderOptions &specific_options = options.get_options (); + m_lambda = specific_options.lambda; + m_dbu = specific_options.dbu; + m_lib_paths = specific_options.lib_paths; + + db::LayerMap lm = specific_options.layer_map; + lm.prepare (layout); + set_layer_map (lm); + set_create_layers (specific_options.create_other_layers); + set_keep_layer_names (specific_options.keep_layer_names); + + do_read (layout); + + finish_layers (layout); + return layer_map (); +} + +const LayerMap & +MAGReader::read (db::Layout &layout) +{ + return read (layout, db::LoadLayoutOptions ()); +} + +void +MAGReader::error (const std::string &msg) +{ + throw MAGReaderException (msg, m_stream.line_number (), m_stream.source ()); +} + +void +MAGReader::warn (const std::string &msg) +{ + // TODO: compress + tl::warn << msg + << tl::to_string (tr (" (line=")) << m_stream.line_number () + << tl::to_string (tr (", file=")) << m_stream.source () + << ")"; +} + +#if 0 // @@@ +/** + * @brief Skip blanks in the sense of MAG + * A blank in MAG is "any ASCII character except digit, upperChar, '-', '(', ')', or ';'" + */ +void +MAGReader::skip_blanks() +{ + while (! m_stream.at_end ()) { + char c = m_stream.peek_char (); + if (isupper (c) || isdigit (c) || c == '-' || c == '(' || c == ')' || c == ';') { + return; + } + m_stream.get_char (); + } +} + +/** + * @brief Skips separators + */ +void +MAGReader::skip_sep () +{ + while (! m_stream.at_end ()) { + char c = m_stream.peek_char (); + if (isdigit (c) || c == '-' || c == '(' || c == ')' || c == ';') { + return; + } + m_stream.get_char (); + } +} + +/** + * @brief Skip comments + * This assumes that the reader is after the first '(' and it will stop + * after the final ')'. + */ +void +MAGReader::skip_comment () +{ + char c; + int bl = 0; + while (! m_stream.at_end () && ((c = m_stream.get_char ()) != ')' || bl > 0)) { + // check for nested comments (bl is the nesting level) + if (c == '(') { + ++bl; + } else if (c == ')') { + --bl; + } + } +} + +/** + * @brief Gets a character and issues an error if the stream is at the end + */ +char +MAGReader::get_char () +{ + if (m_stream.at_end ()) { + error ("Unexpected end of file"); + return 0; + } else { + return m_stream.get_char (); + } +} + +/** + * @brief Tests whether the next character is a semicolon (after blanks) + */ +bool +MAGReader::test_semi () +{ + skip_blanks (); + if (! m_stream.at_end () && m_stream.peek_char () == ';') { + return true; + } else { + return false; + } +} + +/** + * @brief Tests whether a semicolon follows and issue an error if not + */ +void +MAGReader::expect_semi () +{ + if (! test_semi ()) { + error ("Expected ';' command terminator"); + } else { + get_char (); + } +} + +/** + * @brief Skips all until the next semicolon + */ +void +MAGReader::skip_to_end () +{ + while (! m_stream.at_end () && m_stream.get_char () != ';') { + ; + } +} + +/** + * @brief Fetches an integer + */ +int +MAGReader::read_integer_digits () +{ + if (m_stream.at_end () || ! isdigit (m_stream.peek_char ())) { + error ("Digit expected"); + } + + int i = 0; + while (! m_stream.at_end () && isdigit (m_stream.peek_char ())) { + + if (i > std::numeric_limits::max () / 10) { + + error ("Integer overflow"); + while (! m_stream.at_end () && isdigit (m_stream.peek_char ())) { + m_stream.get_char (); + } + + return 0; + + } + + char c = m_stream.get_char (); + i = i * 10 + int (c - '0'); + + } + + return i; +} + +/** + * @brief Fetches an integer + */ +int +MAGReader::read_integer () +{ + skip_sep (); + return read_integer_digits (); +} + +/** + * @brief Fetches a signed integer + */ +int +MAGReader::read_sinteger () +{ + skip_sep (); + + bool neg = false; + if (m_stream.peek_char () == '-') { + m_stream.get_char (); + neg = true; + } + + int i = read_integer_digits (); + return neg ? -i : i; +} + +/** + * @brief Fetches a string (layer name) + */ +const std::string & +MAGReader::read_name () +{ + skip_blanks (); + + m_cmd_buffer.clear (); + if (m_stream.at_end ()) { + return m_cmd_buffer; + } + + // Note: officially only upper and digits are allowed in names. But we allow lower case and "_" too ... + while (! m_stream.at_end () && (isupper (m_stream.peek_char ()) || islower (m_stream.peek_char ()) || m_stream.peek_char () == '_' || isdigit (m_stream.peek_char ()))) { + m_cmd_buffer += m_stream.get_char (); + } + + return m_cmd_buffer; +} + +/** + * @brief Fetches a string (in labels, texts) + */ +const std::string & +MAGReader::read_string () +{ + m_stream.skip (); + + m_cmd_buffer.clear (); + if (m_stream.at_end ()) { + return m_cmd_buffer; + } + + char q = m_stream.peek_char (); + if (q == '"' || q == '\'') { + + get_char (); + + // read a quoted string (KLayout extension) + while (! m_stream.at_end () && m_stream.peek_char () != q) { + char c = m_stream.get_char (); + if (c == '\\' && ! m_stream.at_end ()) { + c = m_stream.get_char (); + } + m_cmd_buffer += c; + } + + if (! m_stream.at_end ()) { + get_char (); + } + + } else { + + while (! m_stream.at_end () && !isspace (m_stream.peek_char ()) && m_stream.peek_char () != ';') { + m_cmd_buffer += m_stream.get_char (); + } + + } + + return m_cmd_buffer; +} + +/** + * @brief Reads a double value (extension) + */ +double +MAGReader::read_double () +{ + m_stream.skip (); + + // read a quoted string (KLayout extension) + m_cmd_buffer.clear (); + while (! m_stream.at_end () && (isdigit (m_stream.peek_char ()) || m_stream.peek_char () == '.' || m_stream.peek_char () == '-' || m_stream.peek_char () == 'e' || m_stream.peek_char () == 'E')) { + m_cmd_buffer += m_stream.get_char (); + } + + double v = 0.0; + tl::from_string (m_cmd_buffer, v); + return v; +} + +bool +MAGReader::read_cell (db::Layout &layout, db::Cell &cell, double sf, int level) +{ + if (fabs (sf - floor (sf + 0.5)) > 1e-6) { + warn ("Scaling factor is not an integer - snapping errors may occur in cell '" + m_cellname + "'"); + } + + int nx = 0, ny = 0, dx = 0, dy = 0; + int layer = -2; // no layer defined yet. + int path_mode = -1; + size_t insts = 0; + size_t shapes = 0; + size_t layer_specs = 0; + std::vector poly_pts; + + while (true) { + + skip_blanks (); + + char c = get_char (); + if (c == ';') { + + // empty command + + } else if (c == '(') { + + skip_comment (); + + } else if (c == 'E') { + + if (level > 0) { + error ("'E' command must be outside a cell specification"); + } + + skip_blanks (); + break; + + } else if (c == 'D') { + + skip_blanks (); + + c = get_char (); + if (c == 'S') { + + // DS command: + // "D" blank* "S" integer (sep integer sep integer)? + + unsigned int n = 0, denom = 1, divider = 1; + n = read_integer (); + if (! test_semi ()) { + denom = read_integer (); + divider = read_integer (); + if (divider == 0) { + error ("'DS' command: divider cannot be zero"); + } + } + + expect_semi (); + + std::string outer_cell = "C" + tl::to_string (n); + std::swap (m_cellname, outer_cell); + + std::map ::const_iterator c = m_cells_by_id.find (n); + db::cell_index_type ci; + if (c == m_cells_by_id.end ()) { + ci = layout.add_cell (m_cellname.c_str ()); + m_cells_by_id.insert (std::make_pair (n, ci)); + } else { + ci = c->second; + } + + db::Cell &cell = layout.cell (ci); + + read_cell (layout, cell, sf * double (denom) / double (divider), level + 1); + + std::swap (m_cellname, outer_cell); + + } else if (c == 'F') { + + // DF command: + // "D" blank* "F" + if (level == 0) { + error ("'DF' command must be inside a cell specification"); + } + + // skip the rest of the command + skip_to_end (); + + break; + + } else if (c == 'D') { + + // DD command: + // "D" blank* "D" integer + + read_integer (); + warn ("'DD' command ignored"); + skip_to_end (); + + } else { + + error ("Invalid 'D' sub-command"); + skip_to_end (); + + } + + } else if (c == 'C') { + + // C command: + // "C" integer transformation + // transformation := (blank* ("T" point |"M" blank* "X" |"M" blank* "Y" |"R" point)*)* + + ++insts; + + unsigned int n = read_integer (); + std::map ::const_iterator c = m_cells_by_id.find (n); + if (c == m_cells_by_id.end ()) { + std::string cn = "C" + tl::to_string (n); + c = m_cells_by_id.insert (std::make_pair (n, layout.add_cell (cn.c_str ()))).first; + } + + db::DCplxTrans trans; + + while (! test_semi ()) { + + skip_blanks (); + + char ct = get_char (); + if (ct == 'M') { + + skip_blanks (); + + char ct2 = get_char (); + if (ct2 == 'X') { + trans = db::DCplxTrans (db::FTrans::m90) * trans; + } else if (ct2 == 'Y') { + trans = db::DCplxTrans (db::FTrans::m0) * trans; + } else { + error ("Invalid 'M' transformation specification"); + // skip everything to avoid more errors here + while (! test_semi ()) { + get_char (); + } + } + + } else if (ct == 'T') { + + int x = read_sinteger (); + int y = read_sinteger (); + trans = db::DCplxTrans (db::DVector (x * sf, y * sf)) * trans; + + } else if (ct == 'R') { + + int x = read_sinteger (); + int y = read_sinteger (); + + if (y != 0 || x != 0) { + double a = atan2 (double (y), double (x)) * 180.0 / M_PI; + trans = db::DCplxTrans (1.0, a, false, db::DVector ()) * trans; + } + + } else { + error ("Invalid transformation specification"); + // skip everything to avoid more errors here + while (! test_semi ()) { + get_char (); + } + } + + } + + if (nx > 0 || ny > 0) { + if (trans.is_ortho () && ! trans.is_mag ()) { + cell.insert (db::CellInstArray (db::CellInst (c->second), db::Trans (db::ICplxTrans (trans)), db::Vector (dx * sf, 0.0), db::Vector (0.0, dy * sf), std::max (1, nx), std::max (1, ny))); + } else { + cell.insert (db::CellInstArray (db::CellInst (c->second), db::ICplxTrans (trans), db::Vector (dx * sf, 0.0), db::Vector (0.0, dy * sf), std::max (1, nx), std::max (1, ny))); + } + } else { + if (trans.is_ortho () && ! trans.is_mag ()) { + cell.insert (db::CellInstArray (db::CellInst (c->second), db::Trans (db::ICplxTrans (trans)))); + } else { + cell.insert (db::CellInstArray (db::CellInst (c->second), db::ICplxTrans (trans))); + } + } + + nx = ny = 0; + dx = dy = 0; + + expect_semi (); + + } else if (c == 'L') { + + skip_blanks (); + + ++layer_specs; + + std::string name = read_name (); + if (name.empty ()) { + error ("Missing layer name in 'L' command"); + } + + std::pair ll = open_layer (layout, name); + if (! ll.first) { + layer = -1; // ignore geometric objects on this layer + } else { + layer = int (ll.second); + } + + expect_semi (); + + } else if (c == 'B') { + + ++shapes; + + if (layer < 0) { + + if (layer < -1) { + warn ("'B' command ignored since no layer was selected"); + } + skip_to_end (); + + } else { + + unsigned int w = 0, h = 0; + int x = 0, y = 0; + + w = read_integer (); + h = read_integer (); + x = read_sinteger (); + y = read_sinteger (); + + int rx = 0, ry = 0; + if (! test_semi ()) { + rx = read_sinteger (); + ry = read_sinteger (); + } + + if (rx >= 0 && ry == 0) { + + cell.shapes ((unsigned int) layer).insert (db::Box (sf * (x - 0.5 * w), sf * (y - 0.5 * h), sf * (x + 0.5 * w), sf * (y + 0.5 * h))); + + } else { + + double n = 1.0 / sqrt (double (rx) * double (rx) + double (ry) * double (ry)); + + double xw = sf * w * 0.5 * rx * n, yw = sf * w * 0.5 * ry * n; + double xh = -sf * h * 0.5 * ry * n, yh = sf * h * 0.5 * rx * n; + + db::Point c (sf * x, sf * y); + + db::Point points [4]; + points [0] = c + db::Vector (-xw - xh, -yw - yh); + points [1] = c + db::Vector (-xw + xh, -yw + yh); + points [2] = c + db::Vector (xw + xh, yw + yh); + points [3] = c + db::Vector (xw - xh, yw - yh); + + db::Polygon p; + p.assign_hull (points, points + 4); + cell.shapes ((unsigned int) layer).insert (p); + + } + + expect_semi (); + + } + + } else if (c == 'P') { + + ++shapes; + + if (layer < 0) { + + if (layer < -1) { + warn ("'P' command ignored since no layer was selected"); + } + skip_to_end (); + + } else { + + poly_pts.clear (); + + while (! test_semi ()) { + + int rx = read_sinteger (); + int ry = read_sinteger (); + + poly_pts.push_back (db::Point (sf * rx, sf * ry)); + + } + + db::Polygon p; + p.assign_hull (poly_pts.begin (), poly_pts.end ()); + cell.shapes ((unsigned int) layer).insert (p); + + expect_semi (); + + } + + } else if (c == 'R') { + + ++shapes; + + if (layer < 0) { + + if (layer < -1) { + warn ("'R' command ignored since no layer was selected"); + } + skip_to_end (); + + } else { + + int w = read_integer (); + + poly_pts.clear (); + + int rx = read_sinteger (); + int ry = read_sinteger (); + + poly_pts.push_back (db::Point (sf * rx, sf * ry)); + + db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), db::coord_traits ::rounded (sf * w / 2), db::coord_traits ::rounded (sf * w / 2), true); + cell.shapes ((unsigned int) layer).insert (p); + + expect_semi (); + + } + + } else if (c == 'W') { + + ++shapes; + + if (layer < 0) { + + if (layer < -1) { + warn ("'W' command ignored since no layer was selected"); + } + + skip_to_end (); + + } else { + + int w = read_integer (); + + poly_pts.clear (); + + while (! test_semi ()) { + + int rx = read_sinteger (); + int ry = read_sinteger (); + + poly_pts.push_back (db::Point (sf * rx, sf * ry)); + + } + + if (path_mode == 0 || (path_mode < 0 && m_wire_mode == 1)) { + // Flush-ended paths + db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), 0, 0, false); + cell.shapes ((unsigned int) layer).insert (p); + } else if (path_mode == 1 || (path_mode < 0 && m_wire_mode == 2)) { + // Round-ended paths + db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), db::coord_traits ::rounded (sf * w / 2), db::coord_traits ::rounded (sf * w / 2), true); + cell.shapes ((unsigned int) layer).insert (p); + } else { + // Square-ended paths + db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), db::coord_traits ::rounded (sf * w / 2), db::coord_traits ::rounded (sf * w / 2), false); + cell.shapes ((unsigned int) layer).insert (p); + } + + expect_semi (); + + } + + } else if (isdigit (c)) { + + char cc = m_stream.peek_char (); + if (c == '9' && cc == '3') { + + get_char (); + + nx = read_sinteger (); + dx = read_sinteger (); + ny = read_sinteger (); + dy = read_sinteger (); + + } else if (c == '9' && cc == '4') { + + get_char (); + + // label at location + ++shapes; + + if (layer < 0) { + if (layer < -1) { + warn ("'94' command ignored since no layer was selected"); + } + } else { + + std::string text = read_string (); + + int rx = read_sinteger (); + int ry = read_sinteger (); + + double h = 0.0; + if (! test_semi ()) { + h = read_double (); + } + + db::Text t (text.c_str (), db::Trans (db::Vector (sf * rx, sf * ry)), db::coord_traits ::rounded (h / m_dbu)); + cell.shapes ((unsigned int) layer).insert (t); + + } + + } else if (c == '9' && cc == '5') { + + get_char (); + + // label in box + ++shapes; + + if (layer < 0) { + if (layer < -1) { + warn ("'95' command ignored since no layer was selected"); + } + } else { + + std::string text = read_string (); + + // TODO: box dimensions are ignored currently. + read_sinteger (); + read_sinteger (); + + int rx = read_sinteger (); + int ry = read_sinteger (); + + db::Text t (text.c_str (), db::Trans (db::Vector (sf * rx, sf * ry))); + cell.shapes ((unsigned int) layer).insert (t); + + } + + } else if (c == '9' && cc == '8') { + + get_char (); + + // Path type (0: flush, 1: round, 2: square) + path_mode = read_integer (); + + } else if (c == '9' && ! isdigit (cc)) { + + m_cellname = read_string (); + m_cellname = layout.uniquify_cell_name (m_cellname.c_str ()); + layout.rename_cell (cell.cell_index (), m_cellname.c_str ()); + + } else { + // ignore the command + } + + skip_to_end (); + + } else { + + // ignore the command + warn ("Unknown command ignored"); + skip_to_end (); + + } + + } + + // The cell is considered non-empty if it contains more than one instance, at least one shape or + // has at least one "L" command. + return insts > 1 || shapes > 0 || layer_specs > 0; +} +#endif + +void +MAGReader::do_read (db::Layout &layout) +{ +#if 0 // @@@ + tl::SelfTimer timer (tl::verbosity () >= 21, "File read"); + + try { + + double sf = 0.01 / m_dbu; + layout.dbu (m_dbu); + + m_cellname = "{MAG top level}"; + + db::Cell &cell = layout.cell (layout.add_cell ()); + + if (! read_cell (layout, cell, sf, 0)) { + // The top cell is empty or contains a single instance: discard it. + layout.delete_cell (cell.cell_index ()); + } else { + layout.rename_cell (cell.cell_index (), layout.uniquify_cell_name ("MAG_TOP").c_str ()); + } + + m_cellname = std::string (); + + skip_blanks (); + + if (! m_stream.at_end ()) { + warn ("E command is followed by more text"); + } + + } catch (db::MAGReaderException &ex) { + throw ex; + } catch (tl::Exception &ex) { + error (ex.msg ()); + } +#endif +} + +} + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.h b/src/plugins/streamers/magic/db_plugin/dbMAGReader.h new file mode 100644 index 000000000..1226218ac --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.h @@ -0,0 +1,146 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + + + +#ifndef HDR_dbMAGReader +#define HDR_dbMAGReader + +#include "dbPluginCommon.h" +#include "dbNamedLayerReader.h" +#include "dbLayout.h" +#include "dbMAG.h" +#include "dbMAGFormat.h" +#include "dbStreamLayers.h" +#include "dbPropertiesRepository.h" + +#include "tlException.h" +#include "tlInternational.h" +#include "tlProgress.h" +#include "tlString.h" +#include "tlStream.h" + +#include +#include + +namespace db +{ + +/** + * @brief Generic base class of MAG reader exceptions + */ +class DB_PLUGIN_PUBLIC MAGReaderException + : public ReaderException +{ +public: + MAGReaderException (const std::string &msg, size_t l, const std::string &file) + : ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%ld, file=%s)")), msg, l, file)) + { } +}; + +/** + * @brief The MAG format stream reader + */ +class DB_PLUGIN_PUBLIC MAGReader + : public NamedLayerReader, + public MAGDiagnostics +{ +public: + typedef std::vector property_value_list; + + /** + * @brief Construct a stream reader object + * + * @param s The stream delegate from which to read stream data from + */ + MAGReader (tl::InputStream &s); + + /** + * @brief Destructor + */ + ~MAGReader (); + + /** + * @brief The basic read method + * + * This method will read the stream data and translate this to + * insert calls into the layout object. This will not do much + * on the layout object beside inserting the objects. + * A set of options can be specified with the LoadLayoutOptions + * object. + * The returned map will contain all layers, the passed + * ones and the newly created ones. + * + * @param layout The layout object to write to + * @param map The LayerMap object + * @param create true, if new layers should be created + * @return The LayerMap object that tells where which layer was loaded + */ + virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options); + + /** + * @brief The basic read method (without mapping) + * + * This method will read the stream data and translate this to + * insert calls into the layout object. This will not do much + * on the layout object beside inserting the objects. + * This version will read all input layers and return a map + * which tells which MAG layer has been read into which logical + * layer. + * + * @param layout The layout object to write to + * @return The LayerMap object + */ + virtual const LayerMap &read (db::Layout &layout); + + /** + * @brief Format + */ + virtual const char *format () const { return "MAG"; } + + /** + * @brief Issue an error with positional information + * + * Reimplements MAGDiagnostics + */ + virtual void error (const std::string &txt); + + /** + * @brief Issue a warning with positional information + * + * Reimplements MAGDiagnostics + */ + virtual void warn (const std::string &txt); + +private: + tl::TextInputStream m_stream; + tl::AbsoluteProgress m_progress; + double m_lambda, m_dbu; + std::vector m_lib_paths; + + void do_read (db::Layout &layout); +}; + +} + +#endif + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc new file mode 100644 index 000000000..fbcaec5f0 --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc @@ -0,0 +1,445 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbMAGWriter.h" +#include "dbPolygonGenerators.h" +#include "tlStream.h" +#include "tlUtils.h" + +#include +#include + +namespace db +{ + +// --------------------------------------------------------------------------------- +// MAGWriter implementation + +MAGWriter::MAGWriter () + : mp_stream (0), + m_progress (tl::to_string (tr ("Writing MAG file")), 10000), + m_needs_emit (false) +{ + m_progress.set_format (tl::to_string (tr ("%.0f MB"))); + m_progress.set_unit (1024 * 1024); +} + +MAGWriter & +MAGWriter::operator<<(const char *s) +{ + mp_stream->put(s, strlen(s)); + return *this; +} + +MAGWriter & +MAGWriter::operator<<(const std::string &s) +{ + mp_stream->put(s.c_str(), s.size()); + return *this; +} + +MAGWriter & +MAGWriter::operator<<(endl_tag) +{ +#ifdef _WIN32 + *this << "\r\n"; +#else + *this << "\n"; +#endif + return *this; +} + +const char * +MAGWriter::xy_sep () const +{ + return m_options.blank_separator ? " " : ","; +} + +void +MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) +{ + m_options = options.get_options (); + mp_stream = &stream; + + // compute the scale factor to get to the 10 nm basic database unit of MAG + double tl_scale = options.scale_factor () * layout.dbu () / 0.01; + + std::vector > layers; + options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignName); + + std::set cell_set; + options.get_cells (layout, cell_set, layers); + + // create a cell index vector sorted bottom-up + std::vector cells; + cells.reserve (cell_set.size ()); + + for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) { + if (cell_set.find (*cell) != cell_set.end ()) { + cells.push_back (*cell); + } + } + + time_t t = time(NULL); + struct tm tt = *localtime(&t); + + char timestr[100]; + strftime(timestr, sizeof (timestr), "%F %T", &tt); + + // Write header + *this << "(MAG file written " << (const char *)timestr << " by KLayout);" << endl; + + // TODO: this can be done more intelligently .. + int tl_scale_divider; + int tl_scale_denom; + for (tl_scale_divider = 1; tl_scale_divider < 1000; ++tl_scale_divider) { + tl_scale_denom = int (floor (0.5 + tl_scale * tl_scale_divider)); + if (fabs (tl_scale_denom - tl_scale * tl_scale_divider) < 1e-6) { + break; + } + } + + int cell_index = 0; + std::map db_to_cif_index_map; + std::set called_cells; + + // body + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + + m_progress.set (mp_stream->pos ()); + + // cell body + const db::Cell &cref (layout.cell (*cell)); + + ++cell_index; + db_to_cif_index_map.insert (std::make_pair (*cell, cell_index)); + + double sf = 1.0; + + *this << "DS " << cell_index << " " << tl_scale_denom << " " << tl_scale_divider << ";" << endl; + *this << "9 " << tl::to_word_or_quoted_string (layout.cell_name (*cell)) << ";" << endl; + + // instances + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + + // write only instances to selected cells + if (cell_set.find (inst->cell_index ()) != cell_set.end ()) { + + called_cells.insert (inst->cell_index ()); + + m_progress.set (mp_stream->pos ()); + + std::map::const_iterator cif_index = db_to_cif_index_map.find (inst->cell_index ()); + tl_assert(cif_index != db_to_cif_index_map.end ()); + + // resolve instance arrays + for (db::Cell::cell_inst_array_type::iterator pp = inst->begin (); ! pp.at_end (); ++pp) { + + *this << "C" << cif_index->second; + + // convert the transformation into MAG's notation + + db::CplxTrans t (inst->complex_trans (*pp)); + db::Vector d (t.disp() * sf); + + if (t.is_mirror()) { + *this << " MY"; + } + + double a = t.angle(); + while (a < 0) { + a += 360.0; + } + double ya = 0.0, xa = 0.0; + if (a < 45 || a > 315) { + xa = 1.0; + ya = tan(a / 180.0 * M_PI); + } else if (a < 135) { + xa = 1.0 / tan(a / 180.0 * M_PI); + ya = 1.0; + } else if (a < 225) { + xa = -1.0; + ya = tan(a / 180.0 * M_PI); + } else { + xa = 1.0 / tan(a / 180.0 * M_PI); + ya = -1.0; + } + + // TODO: that can be done smarter ... + while (fabs (xa - floor (0.5 + xa)) > 1e-3 || fabs (ya - floor (0.5 + ya)) > 1e-3) { + xa *= 2.0; + ya *= 2.0; + } + + *this << " R" << floor (0.5 + xa) << xy_sep () << floor (0.5 + ya); + + *this << " T" << d.x() << xy_sep () << d.y(); + + *this << ";" << endl; + + } + + } + + } + + // shapes + for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { + + m_needs_emit = true; + m_layer = l->second; + + write_texts (layout, cref, l->first, sf); + write_polygons (layout, cref, l->first, sf); + write_paths (layout, cref, l->first, sf); + write_boxes (layout, cref, l->first, sf); + + m_progress.set (mp_stream->pos ()); + + } + + // end of cell + *this << "DF;" << endl; + + } + + if (m_options.dummy_calls) { + + // If requested, write dummy calls for all top cells + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + + if (called_cells.find (*cell) == called_cells.end ()) { + + std::map::const_iterator cif_index = db_to_cif_index_map.find (*cell); + tl_assert(cif_index != db_to_cif_index_map.end ()); + *this << "C" << cif_index->second << ";" << endl; + + } + + } + + } + + // end of file + *this << "E" << endl; + + m_progress.set (mp_stream->pos ()); + +} + +void +MAGWriter::emit_layer() +{ + if (m_needs_emit) { + m_needs_emit = false; + *this << "L " << tl::to_word_or_quoted_string(m_layer.name, "0123456789_.$") << ";" << endl; + } +} + +void +MAGWriter::write_texts (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double sf) +{ + db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + + m_progress.set (mp_stream->pos ()); + + emit_layer (); + + *this << "94 " << tl::to_word_or_quoted_string(shape->text_string(), "0123456789:<>/&%$!.-_#+*?\\[]{}"); + + double h = shape->text_size () * layout.dbu (); + + db::Vector p (shape->text_trans ().disp () * sf); + *this << " " << p.x() << xy_sep () << p.y () << " " << h << ";" << endl; + + ++shape; + + } +} + +void +MAGWriter::write_polygons (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf) +{ + db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Polygons)); + while (! shape.at_end ()) { + + m_progress.set (mp_stream->pos ()); + + db::Polygon poly; + shape->polygon (poly); + + if (poly.holes () > 0) { + + // resolve holes or merge polygon as a preparation step for split_polygon which only works properly + // on merged polygons ... + std::vector polygons; + + db::EdgeProcessor ep; + ep.insert_sequence (poly.begin_edge ()); + db::PolygonContainer pc (polygons); + db::PolygonGenerator out (pc, true /*resolve holes*/, false /*min coherence for splitting*/); + db::SimpleMerge op; + ep.process (out, op); + + for (std::vector::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { + write_polygon (*p, sf); + } + + } else { + write_polygon (poly, sf); + } + + ++shape; + + } +} + +void +MAGWriter::write_polygon (const db::Polygon &polygon, double sf) +{ + emit_layer (); + *this << "P"; + for (db::Polygon::polygon_contour_iterator p = polygon.begin_hull (); p != polygon.end_hull (); ++p) { + db::Point pp (*p * sf); + *this << " " << pp.x () << xy_sep () << pp.y (); + } + *this << ";" << endl; +} + +void +MAGWriter::write_boxes (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf) +{ + db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Boxes)); + while (! shape.at_end ()) { + + m_progress.set (mp_stream->pos ()); + + emit_layer (); + + db::Box b (shape->bbox () * sf); + *this << "B " << b.width () << " " << b.height () << " " << b.center ().x () << xy_sep () << b.center ().y () << ";" << endl; + + ++shape; + + } +} + +void +MAGWriter::write_paths (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf) +{ + db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Paths)); + while (! shape.at_end ()) { + + m_progress.set (mp_stream->pos ()); + +#if 0 + + // "official" code: write only round paths as such - other paths are converted to polygons + if (shape->round_path ()) { + + emit_layer (); + + *this << "W " << long (floor (0.5 + sf * shape->path_width ())); + + for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) { + db::Point pp (*p * sf); + *this << " " << pp.x () << xy_sep () << pp.y (); + } + + *this << ";" << endl; + + } else { + db::Polygon poly; + shape->polygon (poly); + write_polygon (poly, sf); + } + +#else + + // Use 98 extension for path type. Only use polygons for custom extensions. + int path_type = -1; + if (shape->round_path ()) { + if (shape->path_extensions ().first == shape->path_width () / 2 && shape->path_extensions ().second == shape->path_width () / 2) { + path_type = 1; + } + } else { + if (shape->path_extensions ().first == 0 && shape->path_extensions ().second == 0) { + path_type = 0; + } else if (shape->path_extensions ().first == shape->path_width () / 2 && shape->path_extensions ().second == shape->path_width () / 2) { + path_type = 2; + } + } + + size_t npts = 0; + for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point () && npts < 2; ++p) { + ++npts; + } + + if (npts == 0) { + + // ignore paths with zero points + + } else if (path_type == 1 && npts == 1) { + + // produce a round flash for single-point round paths + + emit_layer (); + + *this << "R " << long (floor (0.5 + sf * shape->path_width ())); + + db::Point pp (*shape->begin_point () * sf); + *this << " " << pp.x () << xy_sep () << pp.y (); + + *this << ";" << endl; + + } else if (path_type >= 0 && npts > 1) { + + emit_layer (); + + *this << "98 " << path_type << ";" << endl; + + *this << "W " << long (floor (0.5 + sf * shape->path_width ())); + + for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) { + db::Point pp (*p * sf); + *this << " " << pp.x () << xy_sep () << pp.y (); + } + + *this << ";" << endl; + + } else { + db::Polygon poly; + shape->polygon (poly); + write_polygon (poly, sf); + } + +#endif + + ++shape; + + } +} + +} + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.h b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.h new file mode 100644 index 000000000..813b4d046 --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.h @@ -0,0 +1,96 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + + + +#ifndef HDR_dbMAGWriter +#define HDR_dbMAGWriter + +#include "dbPluginCommon.h" +#include "dbWriter.h" +#include "dbMAG.h" +#include "dbMAGFormat.h" +#include "dbSaveLayoutOptions.h" +#include "tlProgress.h" + +namespace tl +{ + class OutputStream; +} + +namespace db +{ + +class Layout; +class SaveLayoutOptions; + +/** + * @brief A MAG writer abstraction + */ +class DB_PLUGIN_PUBLIC MAGWriter + : public db::WriterBase +{ +public: + /** + * @brief Instantiate the writer + */ + MAGWriter (); + + /** + * @brief Write the layout object + */ + void write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options); + +private: + struct endl_tag { }; + + tl::OutputStream *mp_stream; + MAGWriterOptions m_options; + tl::AbsoluteProgress m_progress; + endl_tag endl; + db::LayerProperties m_layer; + bool m_needs_emit; + + MAGWriter &operator<<(const char *s); + MAGWriter &operator<<(const std::string &s); + MAGWriter &operator<<(endl_tag); + + template MAGWriter &operator<<(const X &x) + { + return (*this << tl::to_string(x)); + } + + void write_texts (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale); + void write_polygons (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale); + void write_polygon (const db::Polygon &polygon, double tl_scale); + void write_boxes (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale); + void write_paths (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale); + void write_edges (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale); + const char *xy_sep () const; + + void emit_layer(); +}; + +} // namespace db + +#endif + diff --git a/src/plugins/streamers/magic/db_plugin/db_plugin.pro b/src/plugins/streamers/magic/db_plugin/db_plugin.pro new file mode 100644 index 000000000..05140614d --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/db_plugin.pro @@ -0,0 +1,17 @@ + +TARGET = cif +DESTDIR = $$OUT_PWD/../../../../db_plugins + +include($$PWD/../../../db_plugin.pri) + +HEADERS = \ + dbMAG.h \ + dbMAGReader.h \ + dbMAGWriter.h \ + dbMAGFormat.h \ + +SOURCES = \ + dbMAG.cc \ + dbMAGReader.cc \ + dbMAGWriter.cc \ + gsiDeclDbMAG.cc \ diff --git a/src/plugins/streamers/magic/db_plugin/gsiDeclDbMAG.cc b/src/plugins/streamers/magic/db_plugin/gsiDeclDbMAG.cc new file mode 100644 index 000000000..0b0671e7f --- /dev/null +++ b/src/plugins/streamers/magic/db_plugin/gsiDeclDbMAG.cc @@ -0,0 +1,265 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbMAG.h" +#include "dbMAGReader.h" +#include "dbMAGWriter.h" +#include "dbLoadLayoutOptions.h" +#include "dbSaveLayoutOptions.h" +#include "gsiDecl.h" + +namespace gsi +{ + +// --------------------------------------------------------------- +// gsi Implementation of specific methods + +static void set_mag_dbu (db::LoadLayoutOptions *options, double dbu) +{ + options->get_options ().dbu = dbu; +} + +static double get_mag_dbu (const db::LoadLayoutOptions *options) +{ + return options->get_options ().dbu; +} + +static void set_mag_lambda (db::LoadLayoutOptions *options, double lambda) +{ + options->get_options ().lambda = lambda; +} + +static double get_mag_lambda (const db::LoadLayoutOptions *options) +{ + return options->get_options ().lambda; +} + +static void set_mag_library_paths (db::LoadLayoutOptions *options, const std::vector &lib_paths) +{ + options->get_options ().lib_paths = lib_paths; +} + +static std::vector get_mag_library_paths (const db::LoadLayoutOptions *options) +{ + return options->get_options ().lib_paths; +} + +static void set_layer_map (db::LoadLayoutOptions *options, const db::LayerMap &lm, bool f) +{ + options->get_options ().layer_map = lm; + options->get_options ().create_other_layers = f; +} + +static void set_layer_map1 (db::LoadLayoutOptions *options, const db::LayerMap &lm) +{ + options->get_options ().layer_map = lm; +} + +static db::LayerMap &get_layer_map (db::LoadLayoutOptions *options) +{ + return options->get_options ().layer_map; +} + +static void select_all_layers (db::LoadLayoutOptions *options) +{ + options->get_options ().layer_map = db::LayerMap (); + options->get_options ().create_other_layers = true; +} + +static bool create_other_layers (const db::LoadLayoutOptions *options) +{ + return options->get_options ().create_other_layers; +} + +static void set_create_other_layers (db::LoadLayoutOptions *options, bool l) +{ + options->get_options ().create_other_layers = l; +} + +static bool keep_layer_names (const db::LoadLayoutOptions *options) +{ + return options->get_options ().keep_layer_names; +} + +static void set_keep_layer_names (db::LoadLayoutOptions *options, bool l) +{ + options->get_options ().keep_layer_names = l; +} + +// extend lay::LoadLayoutOptions with the MAG options +static +gsi::ClassExt mag_reader_options ( + gsi::method_ext ("mag_set_layer_map", &set_layer_map, gsi::arg ("map"), gsi::arg ("create_other_layers"), + "@brief Sets the layer map\n" + "This sets a layer mapping for the reader. The \"create_other_layers\" specifies whether to create layers that are not " + "in the mapping and automatically assign layers to them.\n" + "@param map The layer map to set." + "@param create_other_layers The flag telling whether other layer should be created also. Set to false if just the layers in the mapping table should be read.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_layer_map=", &set_layer_map1, gsi::arg ("map"), + "@brief Sets the layer map\n" + "This sets a layer mapping for the reader. Unlike \\mag_set_layer_map, the 'create_other_layers' flag is not changed.\n" + "@param map The layer map to set." + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_select_all_layers", &select_all_layers, + "@brief Selects all layers and disables the layer map\n" + "\n" + "This disables any layer map and enables reading of all layers.\n" + "New layers will be created when required.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_layer_map", &get_layer_map, + "@brief Gets the layer map\n" + "@return A reference to the layer map\n" + "\n" + "This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions " + "in a format-specific fashion.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_create_other_layers?", &create_other_layers, + "@brief Gets a value indicating whether other layers shall be created\n" + "@return True, if other layers should be created.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_create_other_layers=", &set_create_other_layers, gsi::arg ("create"), + "@brief Specifies whether other layers shall be created\n" + "@param create True, if other layers should be created.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_keep_layer_names?", &keep_layer_names, + "@brief Gets a value indicating whether layer names are kept\n" + "@return True, if layer names are kept.\n" + "\n" + "When set to true, no attempt is made to translate " + "layer names to GDS layer/datatype numbers. If set to false (the default), a layer named \"L2D15\" will be translated " + "to GDS layer 2, datatype 15.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_keep_layer_names=", &set_keep_layer_names, gsi::arg ("keep"), + "@brief Gets a value indicating whether layer names are kept\n" + "@param keep True, if layer names are to be kept.\n" + "\n" + "See \\mag_keep_layer_names? for a description of this property.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("mag_library_paths=", &set_mag_library_paths, gsi::arg ("lib_paths"), + "@brief Specifies the locations where to look up libraries (in this order)\n" + "\n" + "The reader will look up library reference in these paths when it can't find them locally.\n" + "Relative paths in this collection are resolved relative to the initial file's path.\n" + "Expression interpolation is supported in the path strings.\n" + "\nThis property has been added in version 0.26.2.\n" + ) + + gsi::method_ext ("mag_library_paths", &get_mag_library_paths, + "@brief Get the locations where to look up libraries (in this order)\n" + "See \\mag_library_paths= method for a description of this attribute." + "\nThis property has been added in version 0.26.2.\n" + ) + + gsi::method_ext ("mag_lambda=", &set_mag_lambda, gsi::arg ("lambda"), + "@brief Specifies the lambda value to used for reading\n" + "\n" + "The lamdba value is the basic unit of the layout.\n" + "\nThis property has been added in version 0.26.2.\n" + ) + + gsi::method_ext ("mag_lambda", &get_mag_lambda, + "@brief Get the lambda value\n" + "See \\mag_lambda= method for a description of this attribute." + "\nThis property has been added in version 0.26.2.\n" + ) + + gsi::method_ext ("mag_dbu=", &set_mag_dbu, gsi::arg ("dbu"), + "@brief Specifies the database unit which the reader uses and produces\n" + "\nThis property has been added in version 0.26.2.\n" + ) + + gsi::method_ext ("mag_dbu", &get_mag_dbu, + "@brief Specifies the database unit which the reader uses and produces\n" + "See \\mag_dbu= method for a description of this property." + "\nThis property has been added in version 0.26.2.\n" + ), + "" +); + +// --------------------------------------------------------------- +// gsi Implementation of specific methods + +static void set_mag_dummy_calls (db::SaveLayoutOptions *options, bool f) +{ + options->get_options ().dummy_calls = f; +} + +static bool get_mag_dummy_calls (const db::SaveLayoutOptions *options) +{ + return options->get_options ().dummy_calls; +} + +static void set_mag_blank_separator (db::SaveLayoutOptions *options, bool f) +{ + options->get_options ().blank_separator = f; +} + +static bool get_mag_blank_separator (const db::SaveLayoutOptions *options) +{ + return options->get_options ().blank_separator; +} + +// extend lay::SaveLayoutOptions with the GDS2 options +static +gsi::ClassExt mag_writer_options ( + gsi::method_ext ("mag_dummy_calls=", &set_mag_dummy_calls, + "@brief Sets a flag indicating whether dummy calls shall be written\n" + "If this property is set to true, dummy calls will be written in the top level entity " + "of the MAG file calling every top cell.\n" + "This option is useful for enhanced compatibility with other tools.\n" + "\nThis property has been added in version 0.23.10.\n" + ) + + gsi::method_ext ("mag_dummy_calls?|#mag_dummy_calls", &get_mag_dummy_calls, + "@brief Gets a flag indicating whether dummy calls shall be written\n" + "See \\mag_dummy_calls= method for a description of that property." + "\nThis property has been added in version 0.23.10.\n" + "\nThe predicate version (mag_blank_separator?) has been added in version 0.25.1.\n" + ) + + gsi::method_ext ("mag_blank_separator=", &set_mag_blank_separator, + "@brief Sets a flag indicating whether blanks shall be used as x/y separator characters\n" + "If this property is set to true, the x and y coordinates are separated with blank characters " + "rather than comma characters." + "\nThis property has been added in version 0.23.10.\n" + ) + + gsi::method_ext ("mag_blank_separator?|#mag_blank_separator", &get_mag_blank_separator, + "@brief Gets a flag indicating whether blanks shall be used as x/y separator characters\n" + "See \\mag_blank_separator= method for a description of that property." + "\nThis property has been added in version 0.23.10.\n" + "\nThe predicate version (mag_blank_separator?) has been added in version 0.25.1.\n" + ), + "" +); + +} + diff --git a/src/plugins/streamers/magic/lay_plugin/MAGReaderOptionPage.ui b/src/plugins/streamers/magic/lay_plugin/MAGReaderOptionPage.ui new file mode 100644 index 000000000..3a4d528ac --- /dev/null +++ b/src/plugins/streamers/magic/lay_plugin/MAGReaderOptionPage.ui @@ -0,0 +1,319 @@ + + + MAGReaderOptionPage + + + + 0 + 0 + 584 + 530 + + + + Form + + + + + + Input Options + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Micron + + + + + + + Don't attempt to translate into layer/datatype numbers + + + + + + + Keep layer names + + + + + + + + + + Micron + + + + + + + Lambda value + + + + + + + Database unit + + + + + + + + + + Library paths + + + + + + + 0 + 1 + + + + true + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + + + + + Delete selected paths + + + ... + + + + :/clear.png:/clear.png + + + + + + + Move selected paths down + + + ... + + + + :/down.png:/down.png + + + + + + + Add new a directory + + + ... + + + + :/add.png:/add.png + + + + + + + Relative paths are resolved relative to the file read. +You can use expressions inside the path components for variable paths + + + + + + + Move selected paths up + + + ... + + + + :/up.png:/up.png + + + + + + + Add new a directory (file browser) + + + ... + + + true + + + + + + + + + + + 1 + 1 + + + + + + + Layer Subset And Layer Mapping + + + false + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + Read all layers (additionally to the ones in the mapping table) + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + + + + + lay::LayerMappingWidget + QFrame +
layLayerMappingWidget.h
+ 1 + + enable_all_layers(bool) + +
+
+ + lambda_le + dbu_le + keep_names_cbx + lib_path + add_lib_path + add_lib_path_with_choose + del_lib_path + move_lib_path_up + move_lib_path_down + read_all_cbx + + + + + + + layer_map + enable_all_layers(bool) + read_all_cbx + setChecked(bool) + + + 122 + 186 + + + 109 + 147 + + + + +
diff --git a/src/plugins/streamers/magic/lay_plugin/MAGWriterOptionPage.ui b/src/plugins/streamers/magic/lay_plugin/MAGWriterOptionPage.ui new file mode 100644 index 000000000..08ca57d76 --- /dev/null +++ b/src/plugins/streamers/magic/lay_plugin/MAGWriterOptionPage.ui @@ -0,0 +1,105 @@ + + + MAGWriterOptionPage + + + + 0 + 0 + 619 + 209 + + + + Form + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + CIF Writer Options + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + If checked, a blank character is used as x/y coordinate +separator. Otherweise a comma is used. + + + + + + + Blank as x/y separator + + + + + + + Dummy cell calls + + + + + + + If checked, dummy cell calls are added on global level + + + + + + + + + + Qt::Vertical + + + + 601 + 16 + + + + + + + + + diff --git a/src/plugins/streamers/magic/lay_plugin/layMAGReaderPlugin.cc b/src/plugins/streamers/magic/lay_plugin/layMAGReaderPlugin.cc new file mode 100644 index 000000000..3ef663e73 --- /dev/null +++ b/src/plugins/streamers/magic/lay_plugin/layMAGReaderPlugin.cc @@ -0,0 +1,293 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbMAG.h" +#include "dbMAGReader.h" +#include "dbLoadLayoutOptions.h" +#include "layMAGReaderPlugin.h" +#include "ui_MAGReaderOptionPage.h" +#include "gsiDecl.h" + +#include +#include + +namespace lay +{ + +// --------------------------------------------------------------- +// List manipulation utilities +// @@@ TODO: move this to a central place +static void +refresh_item_flags (QListWidget *list) +{ + for (int i = 0; i < list->count (); ++i) { + list->item (i)->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); + } +} + +static void +add_items_to_list (QListWidget *list, const QStringList &items) +{ + for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) { + list->addItem (*f); + } + refresh_item_flags (list); +} + +static void +delete_selected_items_from_list (QListWidget *list) +{ + QStringList items; + for (int i = 0; i < list->count (); ++i) { + if (! list->item (i)->isSelected ()) { + items.push_back (list->item (i)->text ()); + } + } + + list->clear (); + for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) { + list->addItem (*f); + } + refresh_item_flags (list); +} + +static void +move_selected_items_up (QListWidget *list) +{ + std::set selected; + for (int i = 0; i < list->count (); ++i) { + if (list->item (i)->isSelected ()) { + selected.insert (list->item (i)->text ()); + } + } + + QStringList items; + int j = -1; + for (int i = 0; i < list->count (); ++i) { + if (list->item (i)->isSelected ()) { + items.push_back (list->item (i)->text ()); + } else { + if (j >= 0) { + items.push_back (list->item (j)->text ()); + } + j = i; + } + } + if (j >= 0) { + items.push_back (list->item (j)->text ()); + } + + list->clear (); + for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) { + list->addItem (*f); + if (selected.find (*f) != selected.end ()) { + list->item (list->count () - 1)->setSelected (true); + } + } + refresh_item_flags (list); +} + +static void +move_selected_items_down (QListWidget *list) +{ + std::set selected; + for (int i = 0; i < list->count (); ++i) { + if (list->item (i)->isSelected ()) { + selected.insert (list->item (i)->text ()); + } + } + + QStringList items; + int j = -1; + for (int i = list->count (); i > 0; ) { + --i; + if (list->item (i)->isSelected ()) { + items.push_back (list->item (i)->text ()); + } else { + if (j >= 0) { + items.push_back (list->item (j)->text ()); + } + j = i; + } + } + if (j >= 0) { + items.push_back (list->item (j)->text ()); + } + + list->clear (); + for (QStringList::const_iterator f = items.end (); f != items.begin (); ) { + --f; + list->addItem (*f); + if (selected.find (*f) != selected.end ()) { + list->item (list->count () - 1)->setSelected (true); + } + } + refresh_item_flags (list); +} + +// --------------------------------------------------------------- +// MAGReaderOptionPage definition and implementation + +MAGReaderOptionPage::MAGReaderOptionPage (QWidget *parent) + : StreamReaderOptionsPage (parent) +{ + mp_ui = new Ui::MAGReaderOptionPage (); + mp_ui->setupUi (this); + + connect (mp_ui->add_lib_path, SIGNAL (clicked ()), this, SLOT (add_lib_path_clicked ())); + connect (mp_ui->add_lib_path_with_choose, SIGNAL (clicked ()), this, SLOT (add_lib_path_clicked_with_choose ())); + connect (mp_ui->del_lib_path, SIGNAL (clicked ()), this, SLOT (del_lib_paths_clicked ())); + connect (mp_ui->move_lib_path_up, SIGNAL (clicked ()), this, SLOT (move_lib_paths_up_clicked ())); + connect (mp_ui->move_lib_path_down, SIGNAL (clicked ()), this, SLOT (move_lib_paths_down_clicked ())); +} + +MAGReaderOptionPage::~MAGReaderOptionPage () +{ + delete mp_ui; + mp_ui = 0; +} + +void +MAGReaderOptionPage::setup (const db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/) +{ + static const db::MAGReaderOptions default_options; + const db::MAGReaderOptions *options = dynamic_cast (o); + if (!options) { + options = &default_options; + } + + mp_ui->dbu_le->setText (tl::to_qstring (tl::to_string (options->dbu))); + mp_ui->lambda_le->setText (tl::to_qstring (tl::to_string (options->lambda))); + mp_ui->layer_map->set_layer_map (options->layer_map); + mp_ui->read_all_cbx->setChecked (options->create_other_layers); + mp_ui->keep_names_cbx->setChecked (options->keep_layer_names); + + mp_ui->lib_path->clear (); + QStringList items; + for (std::vector ::const_iterator f = options->lib_paths.begin (); f != options->lib_paths.end (); ++f) { + items << tl::to_qstring (*f); + } + add_items_to_list (mp_ui->lib_path, items); +} + +void +MAGReaderOptionPage::commit (db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/) +{ + db::MAGReaderOptions *options = dynamic_cast (o); + if (options) { + + tl::from_string (tl::to_string (mp_ui->dbu_le->text ()), options->dbu); + if (options->dbu > 1000.0 || options->dbu < 1e-9) { + throw tl::Exception (tl::to_string (QObject::tr ("Invalid value for database unit"))); + } + + tl::from_string (tl::to_string (mp_ui->lambda_le->text ()), options->lambda); + if (options->lambda > 10000000.0 || options->lambda < 1e-9) { + throw tl::Exception (tl::to_string (QObject::tr ("Invalid value for lambda"))); + } + + options->layer_map = mp_ui->layer_map->get_layer_map (); + options->create_other_layers = mp_ui->read_all_cbx->isChecked (); + options->keep_layer_names = mp_ui->keep_names_cbx->isChecked (); + + options->lib_paths.clear (); + options->lib_paths.reserve (mp_ui->lib_path->count ()); + for (int i = 0; i < mp_ui->lib_path->count (); ++i) { + options->lib_paths.push_back (tl::to_string (mp_ui->lib_path->item (i)->text ())); + } + + } +} + +void +MAGReaderOptionPage::add_lib_path_clicked () +{ + QStringList dirs; + dirs << tr ("Enter your path here .."); + add_items_to_list (mp_ui->lib_path, dirs); + mp_ui->lib_path->clearSelection (); + mp_ui->lib_path->setCurrentItem (mp_ui->lib_path->item (mp_ui->lib_path->count () - 1)); +} + +void +MAGReaderOptionPage::add_lib_path_clicked_with_choose () +{ + QString dir = QFileDialog::getExistingDirectory (this, QObject::tr ("Add library path")); + if (! dir.isNull ()) { + QStringList dirs; + dirs << dir; + add_items_to_list (mp_ui->lib_path, dirs); + mp_ui->lib_path->clearSelection (); + mp_ui->lib_path->setCurrentItem (mp_ui->lib_path->item (mp_ui->lib_path->count () - 1)); + } +} + +void +MAGReaderOptionPage::del_lib_paths_clicked () +{ + delete_selected_items_from_list (mp_ui->lib_path); +} + +void +MAGReaderOptionPage::move_lib_paths_up_clicked () +{ + move_selected_items_up (mp_ui->lib_path); +} + +void +MAGReaderOptionPage::move_lib_paths_down_clicked () +{ + move_selected_items_down (mp_ui->lib_path); +} + +// --------------------------------------------------------------- +// MAGReaderPluginDeclaration definition and implementation + +class MAGReaderPluginDeclaration + : public StreamReaderPluginDeclaration +{ +public: + MAGReaderPluginDeclaration () + : StreamReaderPluginDeclaration (db::MAGReaderOptions ().format_name ()) + { + // .. nothing yet .. + } + + StreamReaderOptionsPage *format_specific_options_page (QWidget *parent) const + { + return new MAGReaderOptionPage (parent); + } + + db::FormatSpecificReaderOptions *create_specific_options () const + { + return new db::MAGReaderOptions (); + } +}; + +static tl::RegisteredClass plugin_decl (new lay::MAGReaderPluginDeclaration (), 10000, "MAGReader"); + +} + + + + + diff --git a/src/plugins/streamers/magic/lay_plugin/layMAGReaderPlugin.h b/src/plugins/streamers/magic/lay_plugin/layMAGReaderPlugin.h new file mode 100644 index 000000000..a0658cada --- /dev/null +++ b/src/plugins/streamers/magic/lay_plugin/layMAGReaderPlugin.h @@ -0,0 +1,66 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + + + +#ifndef HDR_layMAGReaderPlugin_h +#define HDR_layMAGReaderPlugin_h + +#include "layStream.h" +#include + +namespace Ui +{ + class MAGReaderOptionPage; +} + +namespace lay +{ + +class MAGReaderOptionPage + : public StreamReaderOptionsPage +{ +Q_OBJECT + +public: + MAGReaderOptionPage (QWidget *parent); + ~MAGReaderOptionPage (); + + void setup (const db::FormatSpecificReaderOptions *options, const db::Technology *tech); + void commit (db::FormatSpecificReaderOptions *options, const db::Technology *tech); + +private slots: + void add_lib_path_clicked (); + void add_lib_path_clicked_with_choose (); + void del_lib_paths_clicked (); + void move_lib_paths_up_clicked (); + void move_lib_paths_down_clicked (); + +private: + Ui::MAGReaderOptionPage *mp_ui; +}; + +} + +#endif + + diff --git a/src/plugins/streamers/magic/lay_plugin/layMAGWriterPlugin.cc b/src/plugins/streamers/magic/lay_plugin/layMAGWriterPlugin.cc new file mode 100644 index 000000000..20f59473d --- /dev/null +++ b/src/plugins/streamers/magic/lay_plugin/layMAGWriterPlugin.cc @@ -0,0 +1,100 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbMAG.h" +#include "dbMAGWriter.h" +#include "dbSaveLayoutOptions.h" +#include "layMAGWriterPlugin.h" +#include "ui_MAGWriterOptionPage.h" + +#include + +namespace lay +{ + +// --------------------------------------------------------------- +// MAGWriterOptionPage definition and implementation + +MAGWriterOptionPage::MAGWriterOptionPage (QWidget *parent) + : StreamWriterOptionsPage (parent) +{ + mp_ui = new Ui::MAGWriterOptionPage (); + mp_ui->setupUi (this); + + // .. nothing yet .. +} + +MAGWriterOptionPage::~MAGWriterOptionPage () +{ + delete mp_ui; + mp_ui = 0; +} + +void +MAGWriterOptionPage::setup (const db::FormatSpecificWriterOptions *o, const db::Technology * /*tech*/) +{ + const db::MAGWriterOptions *options = dynamic_cast (o); + if (options) { + mp_ui->dummy_calls_cbx->setChecked (options->dummy_calls); + mp_ui->blank_separator_cbx->setChecked (options->blank_separator); + } +} + +void +MAGWriterOptionPage::commit (db::FormatSpecificWriterOptions *o, const db::Technology * /*tech*/, bool /*gzip*/) +{ + db::MAGWriterOptions *options = dynamic_cast (o); + if (options) { + options->dummy_calls = mp_ui->dummy_calls_cbx->isChecked (); + options->blank_separator = mp_ui->blank_separator_cbx->isChecked (); + } +} + +// --------------------------------------------------------------- +// MAGWriterPluginDeclaration definition and implementation + +class MAGWriterPluginDeclaration + : public StreamWriterPluginDeclaration +{ +public: + MAGWriterPluginDeclaration () + : StreamWriterPluginDeclaration (db::MAGWriterOptions ().format_name ()) + { + // .. nothing yet .. + } + + StreamWriterOptionsPage *format_specific_options_page (QWidget *parent) const + { + return new MAGWriterOptionPage (parent); + } + + db::FormatSpecificWriterOptions *create_specific_options () const + { + return new db::MAGWriterOptions (); + } +}; + +static tl::RegisteredClass plugin_decl (new lay::MAGWriterPluginDeclaration (), 10000, "MAGWriter"); + +} + diff --git a/src/plugins/streamers/magic/lay_plugin/layMAGWriterPlugin.h b/src/plugins/streamers/magic/lay_plugin/layMAGWriterPlugin.h new file mode 100644 index 000000000..5eac8f2fc --- /dev/null +++ b/src/plugins/streamers/magic/lay_plugin/layMAGWriterPlugin.h @@ -0,0 +1,58 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + + +#ifndef HDR_layMAGWriterPlugin_h +#define HDR_layMAGWriterPlugin_h + +#include "layStream.h" +#include + +namespace Ui +{ + class MAGWriterOptionPage; +} + +namespace lay +{ + +class MAGWriterOptionPage + : public StreamWriterOptionsPage +{ +Q_OBJECT + +public: + MAGWriterOptionPage (QWidget *parent); + ~MAGWriterOptionPage (); + + void setup (const db::FormatSpecificWriterOptions *options, const db::Technology *tech); + void commit (db::FormatSpecificWriterOptions *options, const db::Technology *tech, bool gzip); + +private: + Ui::MAGWriterOptionPage *mp_ui; +}; + +} + +#endif + + diff --git a/src/plugins/streamers/magic/lay_plugin/lay_plugin.pro b/src/plugins/streamers/magic/lay_plugin/lay_plugin.pro new file mode 100644 index 000000000..df666b84d --- /dev/null +++ b/src/plugins/streamers/magic/lay_plugin/lay_plugin.pro @@ -0,0 +1,25 @@ + +TARGET = cif_ui +DESTDIR = $$OUT_PWD/../../../../lay_plugins + +include($$PWD/../../../lay_plugin.pri) + +INCLUDEPATH += $$PWD/../db_plugin +DEPENDPATH += $$PWD/../db_plugin +LIBS += -L$$DESTDIR/../db_plugins -lcif + +!isEmpty(RPATH) { + QMAKE_RPATHDIR += $$RPATH/db_plugins +} + +HEADERS = \ + layMAGReaderPlugin.h \ + layMAGWriterPlugin.h \ + +SOURCES = \ + layMAGReaderPlugin.cc \ + layMAGWriterPlugin.cc \ + +FORMS = \ + MAGWriterOptionPage.ui \ + MAGReaderOptionPage.ui \ diff --git a/src/plugins/streamers/magic/magic.pro b/src/plugins/streamers/magic/magic.pro new file mode 100644 index 000000000..0a2501ae9 --- /dev/null +++ b/src/plugins/streamers/magic/magic.pro @@ -0,0 +1,10 @@ + +TEMPLATE = subdirs + +SUBDIRS = db_plugin unit_tests +unit_tests.depends += db_plugin + +!equals(HAVE_QT, "0") { + SUBDIRS += lay_plugin + lay_plugin.depends += db_plugin +} diff --git a/src/plugins/streamers/magic/unit_tests/dbMAGReader.cc b/src/plugins/streamers/magic/unit_tests/dbMAGReader.cc new file mode 100644 index 000000000..5214482cf --- /dev/null +++ b/src/plugins/streamers/magic/unit_tests/dbMAGReader.cc @@ -0,0 +1,196 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbMAGReader.h" +#include "dbLayoutDiff.h" +#include "dbWriter.h" +#include "dbMAGWriter.h" +#include "tlUnitTest.h" + +#include + +static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double dbu = 0.001, bool dummy_calls = false, bool blank_sep = false) +{ + db::MAGReaderOptions *opt = new db::MAGReaderOptions(); + opt->dbu = dbu; + + db::LayerMap lm; + if (map) { + unsigned int ln = 0; + tl::Extractor ex (map); + while (! ex.at_end ()) { + std::string n; + int l; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (l); + ex.test (","); + lm.map (n, ln++, db::LayerProperties (l, 0)); + } + opt->layer_map = lm; + opt->create_other_layers = true; + } + + db::LoadLayoutOptions options; + options.set_options (opt); + + db::Manager m; + db::Layout layout (&m), layout2 (&m), layout2_cif (&m), layout_au (&m); + + { + std::string fn (base); + fn += "/testdata/cif/"; + fn += file; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout, options); + } + + // generate a "unique" name ... + unsigned int hash = 0; + for (const char *cp = file_au; *cp; ++cp) { + hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp); + } + + // normalize the layout by writing to GDS and reading from .. + + std::string tmp_gds_file = _this->tmp_file (tl::sprintf ("tmp_%x.gds", hash)); + std::string tmp_cif_file = _this->tmp_file (tl::sprintf ("tmp_%x.cif", hash)); + + { + tl::OutputStream stream (tmp_gds_file); + db::SaveLayoutOptions options; + options.set_format ("GDS2"); + db::Writer writer (options); + writer.write (layout, stream); + } + + { + tl::InputStream stream (tmp_gds_file); + db::Reader reader (stream); + reader.read (layout2); + } + + // normalize the layout by writing to MAG and reading from .. + + { + tl::OutputStream stream (tmp_cif_file); + + db::MAGWriterOptions *opt = new db::MAGWriterOptions(); + opt->dummy_calls = dummy_calls; + opt->blank_separator = blank_sep; + + db::MAGWriter writer; + db::SaveLayoutOptions options; + options.set_options (opt); + writer.write (layout, stream, options); + } + + { + tl::InputStream stream (tmp_cif_file); + + db::MAGReaderOptions *opt = new db::MAGReaderOptions(); + opt->dbu = dbu; + db::LoadLayoutOptions reread_options; + reread_options.set_options (opt); + + db::Reader reader (stream); + reader.read (layout2_cif, reread_options); + } + + { + std::string fn (base); + fn += "/testdata/cif/"; + fn += file_au; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout_au); + } + + bool equal = db::compare_layouts (layout2, layout_au, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1); + if (! equal) { + _this->raise (tl::sprintf ("Compare failed after reading - see %s vs %s\n", tmp_gds_file, file_au)); + } + + equal = db::compare_layouts (layout, layout2_cif, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1); + if (! equal) { + _this->raise (tl::sprintf ("Compare failed after writing - see %s vs %s\n", file, tmp_cif_file)); + } +} + +TEST(1a) +{ + run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1a_au.gds.gz"); +} + +TEST(1b) +{ + run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01); +} + +TEST(1c) +{ + run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01, true); +} + +TEST(1d) +{ + run_test (_this, tl::testsrc_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01, false, true); +} + +TEST(2) +{ + run_test (_this, tl::testsrc_private (), "t2.cif.gz", "t2_au.gds.gz"); +} + +TEST(3a) +{ + run_test (_this, tl::testsrc_private (), "t3.cif.gz", "t3a_au.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26"); +} + +TEST(3b) +{ + run_test (_this, tl::testsrc_private (), "t3.cif.gz", "t3b_au.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26", 0.00012); +} + +TEST(4) +{ + run_test (_this, tl::testsrc_private (), "t4.cif.gz", "t4_au.gds.gz"); +} + +TEST(5) +{ + run_test (_this, tl::testsrc_private (), "t5.cif.gz", "t5_au.gds.gz"); +} + +// Issue #28 +TEST(lasi) +{ + run_test (_this, tl::testsrc (), "lasi.cif.gz", "lasi_au.gds.gz"); +} + +// Issue #305 +TEST(rot_boxes) +{ + run_test (_this, tl::testsrc (), "issue_305.cif", "issue_305_au.gds"); +} diff --git a/src/plugins/streamers/magic/unit_tests/unit_tests.pro b/src/plugins/streamers/magic/unit_tests/unit_tests.pro new file mode 100644 index 000000000..918f33804 --- /dev/null +++ b/src/plugins/streamers/magic/unit_tests/unit_tests.pro @@ -0,0 +1,19 @@ + +DESTDIR_UT = $$OUT_PWD/../../../.. + +TARGET = cif_tests + +include($$PWD/../../../../lib_ut.pri) + +SOURCES = \ + dbMAGReader.cc \ + +INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common +DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common + +LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi + +PLUGINPATH = $$OUT_PWD/../../../../db_plugins +QMAKE_RPATHDIR += $$PLUGINPATH + +LIBS += -L$$PLUGINPATH -lcif