/* 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 "bdWriterOptions.h" #include "dbLayout.h" #include "dbSaveLayoutOptions.h" #include "tlCommandLineParser.h" #include "tlGlobPattern.h" namespace bd { GenericWriterOptions::GenericWriterOptions () : m_scale_factor (1.0), m_dbu (0.0), m_dont_write_empty_cells (false), m_keep_instances (false), m_write_context_info (false) { // .. nothing yet .. } void GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::string &format) { const std::string gds2_format_name = m_gds2_writer_options.format_name (); const std::string gds2text_format_name = gds2_format_name + "Text"; // no special options const std::string oasis_format_name = m_oasis_writer_options.format_name (); const std::string dxf_format_name = m_dxf_writer_options.format_name (); const std::string cif_format_name = m_cif_writer_options.format_name (); std::string group ("[Output options - General]"); cmd << tl::arg (group + "-os|--scale-factor=factor", &m_scale_factor, "Scales the layout upon writing", "Specifies layout scaling. If given, the saved layout will be scaled by the " "given factor." ); if (format.empty () || format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) { cmd << tl::arg (group + "-od|--dbu-out=dbu", &m_dbu, "Uses the specified database unit", "Specifies the database unit to save the layout in. The database unit is given " "in micron units. By default, the original unit is used. The layout will not " "change physically because internally, the coordinates are scaled to match the " "new database unit." ); } cmd << tl::arg (group + "#--drop-empty-cells", &m_dont_write_empty_cells, "Drops empty cells", "If given, empty cells won't be written. See --keep-instances for more options." ); if (format.empty () || format == gds2_format_name || format == gds2text_format_name) { cmd << tl::arg (group + "#--keep-instances", &m_keep_instances, "Keeps instances of dropped cells", "If given, instances of dropped cell's won't be removed. Hence, ghost cells are " "produced. The resulting layout may not be readable by consumers that require " "all instantiated cells to be present as actual cells.\n" "Dropped cells are those which are removed by a negative cell selection (see " "--write-cells) " ); } if (format.empty () || format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) { cmd << tl::arg (group + "#--write-context-info", &m_write_context_info, "Writes context information", "Include context information for PCell instances and other information in a format-specific " "way. The resulting layout may show unexpected features for other consumers." ); } cmd << tl::arg (group + "#--write-cells=sel", &m_cell_selection, "Specifies cells to write", "This option specifies the cells to write. The value of this option is a sequence of " "positive and negative cell select operations. " "A select operation is an optional plus (+) or minus sign (-), followed by " "a cell filter. By default a select operation is positive, with a minus sign, the " "select operation is negative and will unselect the matching cells." "A cell filter is a plain cell name or a glob pattern (using '*' and '?' for placeholders). " "If a cell filter is enclosed in round brackets, it will apply only to the matching cells. " "Otherwise it will apply to these cells plus their children.\n" "\n" "Multiple operations can be specified by combining them with a comma. " "Positive and negative selection happens in the order given. Hence it's possible " "to select a cell with it's children and then unselect some children of this cell.\n" "\n" "Examples:\n\n" "* \"TOP1,TOP2\" - Select cells TOP1 and TOP2 with all of their children\n" "* \"(TOP)\" - Select only cell TOP, but none of it's child cells\n" "* \"TOP,-A\" - Select cell TOP (plus children), then remove A (with children)" ); if (format.empty () || format == gds2_format_name || format == gds2text_format_name) { // Add GDS2 and GDS2Text format options std::string group = "[Output options - GDS2 specific]"; cmd << tl::arg (group + "-ov|--max-vertex-count=count", &m_gds2_writer_options.max_vertex_count, "Specifies the maximum number of points per polygon", "If this number is given, polygons are cut into smaller parts if they have more " "than the specified number of points. If not given, the maximum number of points will be used. " "This is 8190 unless --multi-xy-records is given." ) << tl::arg (group + "#--multi-xy-records", &m_gds2_writer_options.multi_xy_records, "Allows unlimited number of points", "If this option is given, multiple XY records will be written to accomodate an unlimited number " "of points per polygon or path. However, such files may not be compatible with some consumers." ) << tl::arg (group + "#--no-zero-length-paths", &m_gds2_writer_options.no_zero_length_paths, "Converts zero-length paths to polygons", "If this option is given, zero-length paths (such with one point) are not written as paths " "but converted to polygons. This avoids compatibility issues with consumers of this layout file." ) << tl::arg (group + "-on|--cellname-length=length", &m_gds2_writer_options.max_cellname_length, "Limits cell names to the given length", "If this option is given, long cell names will truncated if their length exceeds the given length." ) << tl::arg (group + "-ol|--libname=libname", &m_gds2_writer_options.libname, "Uses the given library name", "This option can specify the GDS2 LIBNAME for the output file. By default, the original LIBNAME is " "written." ) << tl::arg (group + "#--user-units=unit", &m_gds2_writer_options.user_units, "Specifies the user unit to use", "Specifies the GDS2 user unit. By default micrometers are used for the user unit." ) << tl::arg (group + "#!--no-timestamps", &m_gds2_writer_options.write_timestamps, "Don't write timestamps", "Writes a dummy time stamp instead of the actual time. With this option, GDS2 files become " "bytewise indentical even if written at different times. This option is useful if binary " "identity is important (i.e. in regression scenarios)." ) << tl::arg (group + "#--write-cell-properties", &m_gds2_writer_options.write_cell_properties, "Write cell properties", "This option enables a GDS2 extension that allows writing of cell properties to GDS2 files. " "Consumers that don't support this feature, may not be able to read such a GDS2 files." ) << tl::arg (group + "#--write-file-properties", &m_gds2_writer_options.write_file_properties, "Write file properties", "This option enables a GDS2 extension that allows writing of file properties to GDS2 files. " "Consumers that don't support this feature, may not be able to read such a GDS2 files." ) ; } if (format.empty () || format == oasis_format_name) { // Add OASIS format options std::string group = "[Output options - OASIS specific]"; cmd << tl::arg (group + "-ok|--compression-level=level", &m_oasis_writer_options.compression_level, "Specifies the OASIS compression level", "This level describes how hard the OASIS writer will try to compress the shapes " "using shape arrays. Building shape arrays may take some time and requires some memory.\n" "* 0 - no shape array building\n" "* 1 - nearest neighbor shape array formation\n" "* 2++ - enhanced shape array search algorithm using 2nd and further neighbor distances as well\n" ) << tl::arg (group + "-ob|--cblocks", &m_oasis_writer_options.write_cblocks, "Uses CBLOCK compression" ) << tl::arg (group + "-ot|--strict-mode", &m_oasis_writer_options.strict_mode, "Uses strict mode" ) << tl::arg (group + "#--recompress", &m_oasis_writer_options.recompress, "Compresses shape arrays again", "With this option, shape arrays will be expanded and recompressed. This may result in a better " "compression ratio, but at the cost of slower execution." ) << tl::arg (group + "#--write-std-properties", &m_oasis_writer_options.write_std_properties, "Writes some global standard properties", "This is an integer describing what standard properties shall be written. 0 is \"none\" (the default), " "1 means \"global standard properties such as S_TOP_CELL\" are produced. With 2 also per-cell bounding " "boxes are produced." ) << tl::arg (group + "#--subst-char=char", this, &GenericWriterOptions::set_oasis_substitution_char, "Specifies the substitution character for non-standard characters", "The first character of the string specified with this option will be used in placed of illegal " "characters in n-strings and a-strings." ) ; } if (format.empty () || format == dxf_format_name) { // Add DXF format options std::string group = "[Output options - DXF specific]"; cmd << tl::arg (group + "-op|--polygon-mode=mode", &m_dxf_writer_options.polygon_mode, "Specifies how to write polygons", "This option specifies how to write polygons:\n" "* 0: create POLYLINE\n" "* 1: create LWPOLYLINE\n" "* 2: decompose into SOLID\n" "* 3: create HATCH" ) ; } if (format.empty () || format == cif_format_name) { // Add CIF format options std::string group = "[Output options - CIF specific]"; cmd << tl::arg (group + "#--dummy-calls", &m_cif_writer_options.dummy_calls, "Produces dummy calls", "If this option is given, the writer will produce dummy cell calls on global level for all top cells" ) << tl::arg (group + "#--blank-separator", &m_cif_writer_options.blank_separator, "Uses blanks as x/y separators", "If this option is given, blank characters will be used to separate x and y values. " "Otherwise comma characters will be used.\n" "Use this option if your CIF consumer cannot read comma characters as x/y separators." ) ; } } void GenericWriterOptions::set_oasis_substitution_char (const std::string &text) { if (! text.empty ()) { m_oasis_writer_options.subst_char = text[0]; } } static void get_selected_cells (tl::Extractor &ex, const db::Layout &layout, std::set &selected) { while (! ex.at_end ()) { bool remove = ex.test ("-"); ex.test ("+"); bool without_children = ex.test ("("); std::string filter; ex.read_word_or_quoted (filter, "_-.*?{}$[]"); if (without_children) { ex.expect (")"); } if (! ex.at_end ()) { ex.expect (","); } tl::GlobPattern pat (filter); for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { if (pat.match (layout.cell_name (c->cell_index ()))) { std::set cells; cells.insert (c->cell_index ()); if (! without_children) { c->collect_called_cells (cells); } if (! remove) { selected.insert (cells.begin (), cells.end ()); } else { selected.erase (cells.begin (), cells.end ()); } } } } } void GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db::Layout &layout) const { save_options.set_scale_factor (m_scale_factor); save_options.set_dbu (m_dbu); save_options.set_dont_write_empty_cells (m_dont_write_empty_cells); save_options.set_keep_instances (m_keep_instances); save_options.set_write_context_info (m_write_context_info); save_options.set_options (m_gds2_writer_options); save_options.set_options (m_oasis_writer_options); save_options.set_options (m_dxf_writer_options); save_options.set_options (m_cif_writer_options); if (!m_cell_selection.empty ()) { std::set selected; tl::Extractor ex (m_cell_selection.c_str ()); get_selected_cells (ex, layout, selected); save_options.clear_cells (); for (std::set::const_iterator s = selected.begin (); s != selected.end (); ++s) { save_options.add_this_cell (*s); } } } }