/* 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 "tlStream.h" #include "tlAssert.h" #include "tlException.h" #include "dbLayout.h" #include "dbShape.h" #include "dbPolygonTools.h" #include "dbGDS2Writer.h" #include "dbGDS2.h" #include "dbGDS2Format.h" #include "dbClip.h" #include "dbSaveLayoutOptions.h" #include "dbPolygonGenerators.h" #include #include #include #include namespace db { // ------------------------------------------------------------------ // GDS2WriterBase implementation GDS2WriterBase::GDS2WriterBase () { // .. nothing yet .. } static int safe_scale (double sf, int value) { double i = floor (sf * value + 0.5); if (i < double (std::numeric_limits::min ())) { throw tl::Exception ("Scaling failed: coordinate underflow"); } if (i > double (std::numeric_limits::max ())) { throw tl::Exception ("Scaling failed: coordinate overflow"); } return int (i); } inline int scale (double sf, int value) { if (sf == 1.0) { return value; } else { return safe_scale (sf, value); } } void GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { set_stream (stream); double dbu = (options.dbu () == 0.0) ? layout.dbu () : options.dbu (); double sf = options.scale_factor () * (layout.dbu () / dbu); if (fabs (sf - 1.0) < 1e-9) { // to avoid rounding problems, set to 1.0 exactly if possible. sf = 1.0; } db::GDS2WriterOptions gds2_options = options.get_options (); layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), gds2_options.libname)); std::vector > layers; options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignNumber); 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); } } // get current time short time_data [6] = { 0, 0, 0, 0, 0, 0 }; if (gds2_options.write_timestamps) { time_t ti = 0; time (&ti); const struct tm *t = localtime (&ti); if (t) { time_data[0] = t->tm_year + 1900; time_data[1] = t->tm_mon + 1; time_data[2] = t->tm_mday; time_data[3] = t->tm_hour; time_data[4] = t->tm_min; time_data[5] = t->tm_sec; } } std::string str_time = tl::sprintf ("%d/%d/%d %d:%02d:%02d", time_data[1], time_data[2], time_data[0], time_data[3], time_data[4], time_data[5]); layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), str_time)); layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), str_time)); bool multi_xy = gds2_options.multi_xy_records; size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8); size_t max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4); bool no_zero_length_paths = gds2_options.no_zero_length_paths; m_cell_name_map = db::WriterCellNameMap (max_cellname_length); m_cell_name_map.replacement ('$'); m_cell_name_map.disallow_all (); // TODO: restrict character set, i.e allow_standard and "$" m_cell_name_map.allow_all_printing (); // For keep instances we need to map all cells since all can be present as instances. // We use top-down assignment to make "upper cells less modified". if (options.keep_instances ()) { for (db::Layout::bottom_up_const_iterator cell = layout.end_bottom_up (); cell != layout.begin_bottom_up (); ) { --cell; m_cell_name_map.insert(*cell, layout.cell_name (*cell)); } } else { for (std::vector::const_iterator cell = cells.end (); cell != cells.begin (); ) { --cell; m_cell_name_map.insert(*cell, layout.cell_name (*cell)); } } // write header write_record_size (6); write_record (sHEADER); write_short (600); write_record_size (4 + 12 * 2); write_record (sBGNLIB); write_time (time_data); write_time (time_data); write_string_record (sLIBNAME, gds2_options.libname); write_record_size (4 + 8 * 2); write_record (sUNITS); write_double (dbu / std::max (1e-9, gds2_options.user_units)); write_double (dbu * 1e-6); // layout properties if (gds2_options.write_file_properties && layout.prop_id () != 0) { write_properties (layout, layout.prop_id ()); } // write context info bool any_proxy = false; if (options.write_context_info ()) { for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !any_proxy; ++cell) { const db::Cell &cref = layout.cell (*cell); if (cref.is_proxy () && ! cref.is_top ()) { any_proxy = true; } } } if (any_proxy) { write_record_size (4 + 12 * 2); write_record (sBGNSTR); write_time (time_data); write_time (time_data); write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$"); std::vector context_prop_strings; for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { const db::Cell &cref = layout.cell (*cell); if (cref.is_proxy () && ! cref.is_top ()) { write_record_size (4); write_record (sSREF); write_string_record (sSNAME, m_cell_name_map.cell_name (*cell)); write_record_size (12); write_record (sXY); write_int (0); write_int (0); context_prop_strings.clear (); if (layout.get_context_info (*cell, context_prop_strings)) { // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings // will arrive) for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { --s; write_record_size (6); write_record (sPROPATTR); write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string write_string_record (sPROPVALUE, *s); } } write_record_size (4); write_record (sENDEL); } } write_record_size (4); write_record (sENDSTR); } // body for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { progress_checkpoint (); const db::Cell &cref (layout.cell (*cell)); // don't write ghost cells unless they are not empty (any more) // also don't write proxy cells which are not employed if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) { // cell header write_record_size (4 + 12 * 2); write_record (sBGNSTR); write_time (time_data); write_time (time_data); write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell)); // cell body if (gds2_options.write_cell_properties && cref.prop_id () != 0) { write_properties (layout, cref.prop_id ()); } // instances for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { // write only instances to selected cells if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) { progress_checkpoint (); write_inst (sf, *inst, true /*normalize*/, layout, inst->prop_id ()); } } // shapes for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { if (layout.is_valid_layer (l->first)) { int layer = l->second.layer; int datatype = l->second.datatype; db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); while (! shape.at_end ()) { progress_checkpoint (); if (shape->is_text ()) { write_text (layer, datatype, sf, dbu, *shape, layout, shape->prop_id ()); } else if (shape->is_polygon ()) { write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ()); } else if (shape->is_edge ()) { write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ()); } else if (shape->is_edge_pair ()) { write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ()); write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ()); } else if (shape->is_path ()) { if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) { // eliminate the zero-width path db::Polygon poly; shape->polygon (poly); write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false); } else { write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ()); } } else if (shape->is_box ()) { write_box (layer, datatype, sf, *shape, layout, shape->prop_id ()); } ++shape; } } } // end of cell write_record_size (4); write_record (sENDSTR); } } write_record_size (4); write_record (sENDLIB); progress_checkpoint (); } void GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normalize, const db::Layout &layout, db::properties_id_type prop_id) { db::Vector a, b; unsigned long amax, bmax; bool is_reg = instance.is_regular_array (a, b, amax, bmax); db::Trans t = instance.front (); if (normalize) { // try to normalize orthogonal arrays into "Cadence notation", that is // column and row vectors are positive in the coordinate system of the // rotated array. if (is_reg) { if (amax < 2) { a = db::Vector (); } if (bmax < 2) { b = db::Vector (); } // normalisation only works for orthogonal vectors, parallel to x or y axis, which are not parallel if ((a.x () == 0 || a.y () == 0) && (b.x () == 0 || b.y () == 0) && !((a.x () != 0 && b.x () != 0) || (a.y () != 0 && b.y () != 0))) { db::FTrans fp = db::FTrans(t.rot ()).inverted (); a.transform (fp); b.transform (fp); db::Vector p; for (int i = 0; i < 2; ++i) { db::Vector *q = (i == 0) ? &a : &b; unsigned long n = (i == 0) ? amax : bmax; if (n == 0) { *q = db::Vector (); } else { if (q->x () < 0) { p += db::Vector ((n - 1) * q->x (), 0); q->set_x (-q->x ()); } if (q->y () < 0) { p += db::Vector (0, (n - 1) * q->y ()); q->set_y (-q->y ()); } } } if (a.x () != 0 || b.y () != 0) { std::swap (a, b); std::swap (amax, bmax); } fp = db::FTrans (t.rot ()); a.transform (fp); b.transform (fp); t = t * db::Trans (p); } } } write_record_size (4); write_record (is_reg ? sAREF : sSREF); write_string_record (sSNAME, m_cell_name_map.cell_name (instance.cell_index ())); if (t.rot () != 0 || instance.is_complex ()) { write_record_size (6); write_record (sSTRANS); write_short (t.is_mirror () ? 0x8000 : 0); if (instance.is_complex ()) { write_record_size (4 + 8); write_record (sMAG); write_double (instance.complex_trans ().mag ()); write_record_size (4 + 8); write_record (sANGLE); write_double (instance.complex_trans ().angle ()); } else { if ((t.rot () % 4) != 0) { write_record_size (4 + 8); write_record (sANGLE); write_double ((t.rot () % 4) * 90.0); } } } if (is_reg) { write_record_size (4 + 2 * 2); write_record (sCOLROW); if (amax > 32767 || bmax > 32767) { throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams"))); } write_short (std::max ((unsigned long) 1, bmax)); write_short (std::max ((unsigned long) 1, amax)); } write_record_size (4 + (is_reg ? 3 : 1) * 2 * 4); write_record (sXY); write_int (scale (sf, t.disp ().x ())); write_int (scale (sf, t.disp ().y ())); if (is_reg) { write_int (scale (sf, t.disp ().x () + b.x () * bmax)); write_int (scale (sf, t.disp ().y () + b.y () * bmax)); write_int (scale (sf, t.disp ().x () + a.x () * amax)); write_int (scale (sf, t.disp ().y () + a.y () * amax)); } finish (layout, prop_id); } void GDS2WriterBase::write_box (int layer, int datatype, double sf, const db::Shape &shape, const db::Layout &layout, db::properties_id_type prop_id) { db::Box box (shape.box ()); write_record_size (4); write_record (sBOUNDARY); write_record_size (4 + 2); write_record (sLAYER); write_short (layer); write_record_size (4 + 2); write_record (sDATATYPE); write_short (datatype); write_record_size (4 + 5 * 2 * 4); write_record (sXY); write_int (scale (sf, box.left ())); write_int (scale (sf, box.bottom ())); write_int (scale (sf, box.left ())); write_int (scale (sf, box.top ())); write_int (scale (sf, box.right ())); write_int (scale (sf, box.top ())); write_int (scale (sf, box.right ())); write_int (scale (sf, box.bottom ())); write_int (scale (sf, box.left ())); write_int (scale (sf, box.bottom ())); finish (layout, prop_id); } void GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape &shape, bool multi_xy, const db::Layout &layout, db::properties_id_type prop_id) { // instantiate the path and draw db::Path path; shape.path (path); write_record_size (4); write_record (sPATH); write_record_size (4 + 2); write_record (sLAYER); write_short (layer); write_record_size (4 + 2); write_record (sDATATYPE); write_short (datatype); short type = 0; db::Coord w = path.width (); db::Coord se = path.extensions ().first; db::Coord ee = path.extensions ().second; if (se == w / 2 && ee == w / 2) { type = path.round () ? 1 : 2; } else if (se == 0 && ee == 0) { type = 0; } else { type = 4; } write_record_size (4 + 2); write_record (sPATHTYPE); write_short (type); write_record_size (4 + 4); write_record (sWIDTH); write_int (scale (sf, w)); if (type == 4) { write_record_size (4 + 4); write_record (sBGNEXTN); write_int (scale (sf, se)); write_record_size (4 + 4); write_record (sENDEXTN); write_int (scale (sf, ee)); } size_t n = path.points (); db::Path::iterator p = path.begin (); while (n > 0) { // determine number of points to write (all or slice for multi XY mode) size_t nxy = n; if (n > 8100 && multi_xy) { nxy = 8000; } write_record_size (4 + int16_t (nxy) * 2 * 4); write_record (sXY); // write path .. for ( ; p != path.end () && nxy > 0; ++p) { write_int (scale (sf, (*p).x ())); write_int (scale (sf, (*p).y ())); --nxy; --n; } } finish (layout, prop_id); } void GDS2WriterBase::write_edge (int layer, int datatype, double sf, const db::Shape &shape, const db::Layout &layout, db::properties_id_type prop_id) { write_edge (layer, datatype, sf, shape.edge (), layout, prop_id); } void GDS2WriterBase::write_edge (int layer, int datatype, double sf, const db::Edge &e, const db::Layout &layout, db::properties_id_type prop_id) { write_record_size (4); write_record (sPATH); write_record_size (4 + 2); write_record (sLAYER); write_short (layer); write_record_size (4 + 2); write_record (sDATATYPE); write_short (datatype); write_record_size (4 + 2); write_record (sPATHTYPE); write_short (0); write_record_size (4 + 4); write_record (sWIDTH); write_int (0); write_record_size (4 + 2 * 2 * 4); write_record (sXY); write_int (scale (sf, e.p1 ().x ())); write_int (scale (sf, e.p1 ().y ())); write_int (scale (sf, e.p2 ().x ())); write_int (scale (sf, e.p2 ().y ())); finish (layout, prop_id); } void GDS2WriterBase::write_text (int layer, int datatype, double sf, double dbu, const db::Shape &shape, const db::Layout &layout, db::properties_id_type prop_id) { db::Trans trans = shape.text_trans (); write_record_size (4); write_record (sTEXT); write_record_size (4 + 2); write_record (sLAYER); write_short (layer); write_record_size (4 + 2); write_record (sTEXTTYPE); write_short (datatype); if (shape.text_halign () != db::NoHAlign || shape.text_valign () != db::NoVAlign || shape.text_font () != db::NoFont) { short ha = short (shape.text_halign () == db::NoHAlign ? db::HAlignLeft : shape.text_halign ()); short va = short (shape.text_valign () == db::NoVAlign ? db::VAlignBottom : shape.text_valign ()); // HINT: currently we don't write the font since the font is not well standardized // short f = (shape.text_font () == db::NoFont ? 0 : (short (shape.text_font ()) & 0xfff)); short f = 0; write_record_size (4 + 2); write_record (sPRESENTATION); write_short (ha + va * 4 + f * 16); } if (trans.rot () != 0 || shape.text_size () != 0) { write_record_size (6); write_record (sSTRANS); write_short (trans.is_mirror () ? 0x8000 : 0); if (shape.text_size () != 0) { write_record_size (4 + 8); write_record (sMAG); write_double (shape.text_size () * sf * dbu); } if ((trans.rot () % 4) != 0) { write_record_size (4 + 8); write_record (sANGLE); write_double ((trans.rot () % 4) * 90.0); } } write_record_size (4 + 2 * 4); write_record (sXY); write_int (scale (sf, trans.disp ().x ())); write_int (scale (sf, trans.disp ().y ())); write_string_record (sSTRING, shape.text_string ()); finish (layout, prop_id); } void GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Polygon &polygon, bool multi_xy, size_t max_vertex, const db::Layout &layout, db::properties_id_type prop_id, bool merged) { bool needs_split = (polygon.vertices () > 4 && polygon.vertices () > max_vertex && !multi_xy); if (polygon.holes () > 0 || (! merged && needs_split)) { // 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 (polygon.begin_edge ()); db::PolygonContainer pc (polygons); db::PolygonGenerator out (pc, true /*resolve holes*/, needs_split /*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 (layer, datatype, sf, *p, multi_xy, max_vertex, layout, prop_id, true); } } else if (needs_split) { std::vector polygons; db::split_polygon (polygon, polygons); for (std::vector::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { write_polygon (layer, datatype, sf, *p, multi_xy, max_vertex, layout, prop_id, true /*parts need not to be merged again*/); } } else if (polygon.vertices () > 0) { write_record_size (4); write_record (sBOUNDARY); write_record_size (4 + 2); write_record (sLAYER); write_short (layer); write_record_size (4 + 2); write_record (sDATATYPE); write_short (datatype); size_t n = polygon.vertices (); db::Polygon::polygon_contour_iterator e = polygon.begin_hull (); while (n > 0) { // determine number of points to write (all or slice for multi XY mode) size_t nxy = n + 1; if (n > 8100 && multi_xy) { nxy = 8000; } write_record_size (4 + int16_t (nxy) * 2 * 4); write_record (sXY); // write polygon .. for ( ; e != polygon.end_hull () && nxy > 0; ++e) { write_int (scale (sf, (*e).x ())); write_int (scale (sf, (*e).y ())); --nxy; --n; } // .. and closing point if (nxy > 0) { e = polygon.begin_hull (); write_int (scale (sf, (*e).x ())); write_int (scale (sf, (*e).y ())); tl_assert (n == 0); } } finish (layout, prop_id); } } void GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Shape &shape, bool multi_xy, size_t max_vertex, const db::Layout &layout, db::properties_id_type prop_id) { if (shape.holes () > 0) { db::Polygon polygon; shape.polygon (polygon); write_polygon (layer, datatype, sf, polygon, multi_xy, max_vertex, layout, prop_id, false); } else { // There is no other way to determine the actual number of points of a generic shape // without instantiating a polygon: size_t n = 0; for (db::Shape::point_iterator e = shape.begin_hull (); e != shape.end_hull (); ++e) { ++n; } if (n > 4 && n > max_vertex && !multi_xy) { // split polygons ... db::Polygon polygon; shape.polygon (polygon); write_polygon (layer, datatype, sf, polygon, multi_xy, max_vertex, layout, prop_id, false); } else if (n > 0) { write_record_size (4); write_record (sBOUNDARY); write_record_size (4 + 2); write_record (sLAYER); write_short (layer); write_record_size (4 + 2); write_record (sDATATYPE); write_short (datatype); db::Shape::point_iterator e (shape.begin_hull ()); while (n > 0) { // determine number of points to write (all or slice for multi XY mode) size_t nxy = n + 1; if (n > 8100 && multi_xy) { nxy = 8000; } write_record_size (4 + int16_t (nxy) * 2 * 4); write_record (sXY); // write polygon .. for ( ; e != shape.end_hull () && nxy > 0; ++e) { write_int (scale (sf, (*e).x ())); write_int (scale (sf, (*e).y ())); --nxy; --n; } // .. and closing point if (nxy > 0) { e = shape.begin_hull (); write_int (scale (sf, (*e).x ())); write_int (scale (sf, (*e).y ())); tl_assert (n == 0); } } finish (layout, prop_id); } } } void GDS2WriterBase::write_properties (const db::Layout &layout, db::properties_id_type prop_id) { const db::PropertiesRepository::properties_set &props = layout.properties_repository ().properties (prop_id); for (db::PropertiesRepository::properties_set::const_iterator p = props.begin (); p != props.end (); ++p) { const tl::Variant &name = layout.properties_repository ().prop_name (p->first); long attr = -1; if (name.can_convert_to_long ()) { attr = name.to_long (); } if (attr >= 0 && attr < 65535) { write_record_size (6); write_record (sPROPATTR); write_short (attr); write_string_record (sPROPVALUE, p->second.to_string ()); } } } void GDS2WriterBase::finish (const db::Layout &layout, db::properties_id_type prop_id) { if (prop_id != 0) { write_properties (layout, prop_id); } write_record_size (4); write_record (sENDEL); } void GDS2WriterBase::write_string_record (short record, const std::string &t) { write_record_size (4 + (int16_t (t.size () + 1) / 2) * 2); write_record (record); write_string (t); } } // namespace db