/* KLayout Layout Viewer Copyright (C) 2006-2022 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 "dbLEFImporter.h" #include "tlStream.h" #include namespace db { // ----------------------------------------------------------------------------------- // LEFImporter implementation LEFImporter::LEFImporter (int warn_level) : LEFDEFImporter (warn_level) { // .. nothing yet .. } LEFImporter::~LEFImporter () { // .. nothing yet .. } double LEFImporter::layer_ext (const std::string &layer, double def_ext) const { std::map::const_iterator l = m_default_ext.find (layer); if (l != m_default_ext.end ()) { return l->second; } else { return def_ext; } } std::pair LEFImporter::min_layer_width (const std::string &layer) const { std::map >::const_iterator l = m_min_widths.find (layer); if (l != m_min_widths.end ()) { return l->second; } else { return std::make_pair (0.0, 0.0); } } std::pair LEFImporter::layer_width (const std::string &layer, const std::string &nondefaultrule, const std::pair &def_width) const { std::map > >::const_iterator nd = m_nondefault_widths.find (nondefaultrule); std::map >::const_iterator l; bool has_width = false; if (! nondefaultrule.empty () && nd != m_nondefault_widths.end ()) { l = nd->second.find (layer); if (l != nd->second.end ()) { has_width = true; } } if (! has_width) { l = m_default_widths.find (layer); if (l != m_default_widths.end ()) { has_width = true; } } if (has_width) { return l->second; } else { return def_width; } } std::vector LEFImporter::get_iteration (double dbu) { test ("DO"); long nx = get_long (); test ("BY"); long ny = get_long (); test ("STEP"); double dx = get_double (); double dy = get_double (); std::vector t; for (long i = 0; i < nx; ++i) { for (long j = 0; j < ny; ++j) { t.push_back (db::Trans (db::Vector (db::DVector (dx * i / dbu, dy * j / dbu)))); } } return t; } template static db::Shape insert_shape (db::Cell &cell, unsigned int layer_id, const Shape &shape, const Trans &trans, db::properties_id_type prop_id) { if (prop_id == 0) { return cell.shapes (layer_id).insert (shape.transformed (trans)); } else { return cell.shapes (layer_id).insert (db::object_with_properties (shape.transformed (trans), prop_id)); } } template static db::Shape insert_shape (db::Cell &cell, unsigned int layer_id, const Shape &shape, db::properties_id_type prop_id) { if (prop_id == 0) { return cell.shapes (layer_id).insert (shape); } else { return cell.shapes (layer_id).insert (db::object_with_properties (shape, prop_id)); } } static db::Box box_for_label (const db::Polygon &p) { if (p.is_box ()) { return p.box (); } else if (p.begin_hull () != p.end_hull ()) { return db::Box (*p.begin_hull (), *p.begin_hull ()); } else { return db::Box (); } } static db::Box box_for_label (const db::Path &p) { if (p.begin () != p.end ()) { return db::Box (*p.begin (), *p.begin ()); } else { return db::Box (); } } void LEFImporter::read_geometries (GeometryBasedLayoutGenerator *lg, double dbu, LayerPurpose purpose, std::map *collect_boxes_for_labels, db::properties_id_type prop_id) { std::string layer_name; double w = 0.0; while (true) { if (test ("CLASS")) { // accept CLASS token for PORT definitions while (! at_end () && ! test (";")) { take (); } } else if (test ("LAYER")) { layer_name = get (); w = 0.0; std::map >::const_iterator dw = m_default_widths.find (layer_name); if (dw != m_default_widths.end ()) { w = dw->second.first; } while (! at_end () && ! test (";")) { take (); } } else if (test ("WIDTH")) { w = get_double (); expect (";"); } else if (test ("PATH")) { std::vector points; unsigned int mask = 0; if (test ("MASK")) { mask = get_mask (get_long ()); } bool iterate = test ("ITERATE"); while (! peek (";") && ! peek ("DO")) { test ("("); double x = get_double (); double y = get_double (); points.push_back (db::Point (db::DPoint (x / dbu, y / dbu))); test (")"); } if (lg) { db::Coord iw = db::coord_traits::rounded (w / dbu); db::Path p (points.begin (), points.end (), iw, iw / 2, iw / 2, false); if (iterate) { std::vector ti = get_iteration (dbu); for (std::vector::const_iterator t = ti.begin (); t != ti.end (); ++t) { db::Path pt = p.transformed (*t); lg->add_path (layer_name, purpose, pt, mask, prop_id); if (collect_boxes_for_labels) { collect_boxes_for_labels->insert (std::make_pair (layer_name, db::Box ())).first->second = box_for_label (pt); } } } else { lg->add_path (layer_name, purpose, p, mask, prop_id); if (collect_boxes_for_labels) { collect_boxes_for_labels->insert (std::make_pair (layer_name, db::Box ())).first->second = box_for_label (p); } } } expect (";"); } else if (test ("POLYGON")) { std::vector points; unsigned int mask = 0; if (test ("MASK")) { mask = get_mask (get_long ()); } bool iterate = test ("ITERATE"); while (! peek (";") && ! peek ("DO")) { test ("("); double x = get_double (); double y = get_double (); points.push_back (db::Point (db::DPoint (x / dbu, y / dbu))); test (")"); } if (lg) { db::Polygon p; p.assign_hull (points.begin (), points.end ()); if (iterate) { std::vector ti = get_iteration (dbu); for (std::vector::const_iterator t = ti.begin (); t != ti.end (); ++t) { db::Polygon pt = p.transformed (*t); lg->add_polygon (layer_name, purpose, pt, mask, prop_id); if (collect_boxes_for_labels) { collect_boxes_for_labels->insert (std::make_pair (layer_name, db::Box ())).first->second = box_for_label (pt); } } } else { lg->add_polygon (layer_name, purpose, p, mask, prop_id); if (collect_boxes_for_labels) { collect_boxes_for_labels->insert (std::make_pair (layer_name, db::Box ())).first->second = box_for_label (p); } } } expect (";"); } else if (test ("RECT")) { std::vector points; unsigned int mask = 0; if (test ("MASK")) { mask = get_mask (get_long ()); } bool iterate = test ("ITERATE"); for (int i = 0; i < 2; ++i) { test ("("); double x = get_double (); double y = get_double (); points.push_back (db::Point (db::DPoint (x / dbu, y / dbu))); test (")"); } if (lg) { db::Box b (points [0], points [1]); if (iterate) { std::vector ti = get_iteration (dbu); for (std::vector::const_iterator t = ti.begin (); t != ti.end (); ++t) { db::Box bt = b.transformed (*t); lg->add_box (layer_name, purpose, bt, mask, prop_id); if (collect_boxes_for_labels) { collect_boxes_for_labels->insert (std::make_pair (layer_name, db::Box ())).first->second = bt; } } } else { lg->add_box (layer_name, purpose, b, mask, prop_id); if (collect_boxes_for_labels) { collect_boxes_for_labels->insert (std::make_pair (layer_name, db::Box ())).first->second = b; } } } expect (";"); } else if (test ("VIA")) { std::vector points; // note: the 5.8 spec says ITERATE comes before MASK for VIA bool iterate = test ("ITERATE"); unsigned int mask = 0; if (test ("MASK")) { mask = get_mask (get_long ()); } unsigned int mask_bottom = mask % 10; unsigned int mask_cut = (mask / 10) % 10; unsigned int mask_top = (mask / 100) % 10; double x = 0.0, y = 0.0; if (test ("(")) { x = get_double (); y = get_double (); test (")"); } else { x = get_double (); y = get_double (); } points.push_back (db::Vector (db::DVector (x / dbu, y / dbu))); std::string vn = get (); if (lg) { if (iterate) { std::vector ti = get_iteration (dbu); for (std::vector::const_iterator t = ti.begin (); t != ti.end (); ++t) { lg->add_via (vn, *t * db::Trans (points [0]), mask_bottom, mask_cut, mask_top); } } else { lg->add_via (vn, db::Trans (points [0]), mask_bottom, mask_cut, mask_top); } } expect (";"); } else if (test ("PROPERTY")) { // skip properties while (! at_end () && ! test (";")) { take (); } } else { // stop at unknown token break; } } } void LEFImporter::read_nondefaultrule (db::Layout &layout) { // read NONDEFAULTRULE sections std::string n = get (); while (! test ("END") || ! test (n)) { if (test ("LAYER")) { std::string l = get (); // read the width for the layer while (! test ("END")) { if (test ("WIDTH")) { double w = get_double (); test (";"); m_nondefault_widths[n][l] = std::make_pair (w, w); } else { while (! at_end () && ! test (";")) { take (); } } } test (l); } else if (test ("VIA")) { read_viadef (layout, n); } else { while (! at_end () && ! test (";")) { take (); } } } } void LEFImporter::read_viadef_by_rule (RuleBasedViaGenerator *vg, ViaDesc &via_desc, const std::string & /*n*/, double dbu) { while (! test ("END")) { double x, y; if (test ("CUTSIZE")) { x = get_double (); y = get_double (); vg->set_cutsize (db::Vector (db::DVector (x / dbu, y / dbu))); test (";"); } else if (test ("CUTSPACING")) { x = get_double (); y = get_double (); vg->set_cutspacing (db::Vector (db::DVector (x / dbu, y / dbu))); test (";"); } else if (test ("ORIGIN")) { x = get_double (); y = get_double (); vg->set_offset (db::Point (db::DPoint (x / dbu, y / dbu))); test (";"); } else if (test ("ENCLOSURE")) { x = get_double (); y = get_double (); vg->set_be (db::Vector (db::DVector (x / dbu, y / dbu))); x = get_double (); y = get_double (); vg->set_te (db::Vector (db::DVector (x / dbu, y / dbu))); test (";"); } else if (test ("OFFSET")) { x = get_double (); y = get_double (); vg->set_bo (db::Vector (db::DVector (x / dbu, y / dbu))); x = get_double (); y = get_double (); vg->set_to (db::Vector (db::DVector (x / dbu, y / dbu))); test (";"); } else if (test ("ROWCOL")) { vg->set_rows (get_long ()); vg->set_columns (get_long ()); test (";"); } else if (test ("PATTERN")) { vg->set_pattern (get ()); test (";"); } else if (test ("LAYERS")) { std::string lb, lc, lt; lb = get (); lc = get (); lt = get (); via_desc.m1 = lb; via_desc.m2 = lt; vg->set_bottom_layer (lb); vg->set_cut_layer (lc); vg->set_top_layer (lt); test (";"); } else { while (! at_end () && ! test (";")) { take (); } } } } template static db::DVector via_size (double dbu, const Shape &shape) { db::Box box = db::box_convert () (shape); return db::DVector (box.width () * dbu, box.height () * dbu); } void LEFImporter::read_viadef_by_geometry (GeometryBasedLayoutGenerator *lg, ViaDesc &via_desc, const std::string &n, double dbu) { std::string layer_name; std::set seen_layers; std::vector routing_layers; while (true) { // ignore resistance spec if (test ("RESISTANCE")) { get_double (); test (";"); } else if (test ("LAYER")) { layer_name = get (); if (m_routing_layers.find (layer_name) != m_routing_layers.end ()) { if (routing_layers.size () == 0) { lg->set_maskshift_layer (0, layer_name); } else if (routing_layers.size () == 1) { lg->set_maskshift_layer (2, layer_name); } if (seen_layers.find (layer_name) == seen_layers.end ()) { seen_layers.insert (layer_name); routing_layers.push_back (layer_name); } } else { lg->set_maskshift_layer (1, layer_name); } while (! at_end () && ! test (";")) { take (); } } else if (test ("POLYGON")) { std::vector points; unsigned int mask = 0; if (test ("MASK")) { mask = get_mask (get_long ()); } while (! peek (";")) { test ("("); double x = get_double (); double y = get_double (); points.push_back (db::Point (db::DPoint (x / dbu, y / dbu))); test (")"); } db::Polygon p; p.assign_hull (points.begin (), points.end ()); lg->add_polygon (layer_name, ViaGeometry, p, mask, 0, via_size (dbu, p)); expect (";"); } else if (test ("RECT")) { std::vector points; unsigned int mask = 0; if (test ("MASK")) { mask = get_mask (get_long ()); } for (int i = 0; i < 2; ++i) { test ("("); double x = get_double (); double y = get_double (); points.push_back (db::Point (db::DPoint (x / dbu, y / dbu))); test (")"); } db::Box b (points [0], points [1]); lg->add_box (layer_name, ViaGeometry, b, mask, 0, via_size (dbu, b)); expect (";"); } else if (test ("PROPERTY")) { // skip properties while (! at_end () && ! test (";")) { take (); } } else { // stop at unknown token break; } } // determine m1 and m2 layers if (routing_layers.size () == 2) { via_desc.m1 = routing_layers[0]; via_desc.m2 = routing_layers[1]; } else { warn (tl::to_string (tr ("Can't determine routing layers for via: ")) + n); } reset_cellname (); expect ("END"); } void LEFImporter::read_viadef (Layout &layout, const std::string &nondefaultrule) { std::string n = get (); ViaDesc &via_desc = m_vias[n]; while (test ("DEFAULT") || test ("TOPOFSTACKONLY")) ; test (";"); if (test ("VIARULE")) { std::unique_ptr vg (new RuleBasedViaGenerator ()); read_viadef_by_rule (vg.get (), via_desc, n, layout.dbu ()); reader_state ()->register_via_cell (n, nondefaultrule, vg.release ()); } else { std::unique_ptr vg (new GeometryBasedLayoutGenerator ()); read_viadef_by_geometry (vg.get (), via_desc, n, layout.dbu ()); reader_state ()->register_via_cell (n, nondefaultrule, vg.release ()); } test ("VIA"); expect (n); } void LEFImporter::read_layer (Layout & /*layout*/) { std::string ln = get (); double wmin = 0.0, wmin_wrongdir = 0.0; double w = 0.0, w_wrongdir = 0.0; bool is_horizontal = false; register_layer (ln); // just extract the width from the layer - we need that as the default width for paths while (! at_end ()) { if (test ("END")) { expect (ln); break; } else if (test ("TYPE")) { std::string type = get (); if (type == "ROUTING" || type == "MASTERSLICE") { m_routing_layers.insert (ln); } else if (type == "CUT") { m_cut_layers.insert (ln); } expect (";"); } else if (test ("MASK")) { unsigned int num = (unsigned int) std::max (1l, get_long ()); test (";"); m_num_masks [ln] = num; } else if (test ("WIDTH")) { w = get_double (); expect (";"); } else if (test ("MINWIDTH")) { wmin = get_double (); expect (";"); } else if (test ("DIRECTION")) { if (test ("HORIZONTAL")) { is_horizontal = true; } else { expect ("VERTICAL", "DIAG45", "DIAG135"); } } else if (test ("WIREEXTENSION")) { double v = get_double (); m_default_ext.insert (std::make_pair (ln, v)); expect (";"); } else if (test ("ACCURRENTDENSITY")) { // ACCURRENTDENSITY needs some special attention because it can contain nested WIDTH // blocks following a semicolon take (); if (test ("FREQUENCY")) { while (! test ("TABLEENTRIES")) { take (); } } while (! at_end () && ! test (";")) { take (); } } else if (test ("PROPERTY")) { std::string name = get (); tl::Variant value = get (); if (name == "LEF58_MINWIDTH") { // Cadence extension tl::Extractor ex (value.to_string ()); double v = 0.0; if (ex.test ("MINWIDTH") && ex.try_read (v)) { if (ex.test ("WRONGDIRECTION")) { wmin_wrongdir = v; } else { wmin = v; } } } else if (name == "LEF58_WIDTH") { // Cadence extension tl::Extractor ex (value.to_string ()); double v = 0.0; if (ex.test ("WIDTH") && ex.try_read (v)) { if (ex.test ("WRONGDIRECTION")) { w_wrongdir = v; } else { w = v; } } } expect (";"); } else { while (! at_end () && ! test (";")) { take (); } } } if (w > 0.0 || w_wrongdir > 0.0) { if (w_wrongdir == 0.0) { w_wrongdir = w; } else if (! is_horizontal) { std::swap (w, w_wrongdir); } m_default_widths.insert (std::make_pair (ln, std::make_pair (w, w_wrongdir))); } if (wmin > 0.0 || wmin_wrongdir > 0.0) { if (wmin_wrongdir == 0.0) { wmin_wrongdir = wmin; } else if (! is_horizontal) { std::swap (wmin, wmin_wrongdir); } m_min_widths.insert (std::make_pair (ln, std::make_pair (wmin, wmin_wrongdir))); } } void LEFImporter::read_macro (Layout &layout) { std::string mn = get (); if (m_macros.find (mn) != m_macros.end ()) { error (tl::to_string (tr ("Duplicate MACRO name: ")) + mn); } set_cellname (mn); GeometryBasedLayoutGenerator *mg = new GeometryBasedLayoutGenerator (); reader_state ()->register_macro_cell (mn, mg); db::Trans foreign_trans; std::string foreign_name; db::Point origin; db::Vector size; // read the macro while (! at_end ()) { if (test ("END")) { expect (mn); break; } else if (test ("ORIGIN")) { origin = get_point (1.0 / layout.dbu ()); expect (";"); } else if (test ("SIZE")) { double x = get_double (); test ("BY"); double y = get_double (); expect (";"); size = db::Vector (db::DVector (x / layout.dbu (), y / layout.dbu ())); } else if (test ("PIN")) { std::string pn = get (); std::string dir; while (! at_end ()) { if (test ("END")) { break; } else if (test ("DIRECTION")) { dir = get (); test (";"); } else if (test ("PORT")) { // produce pin labels // TODO: put a label on every single object? std::string label = pn; /* don't add the direction currently, a name is sufficient if (! dir.empty ()) { label += ":"; label += dir; } */ if (reader_state ()->tech_comp ()->produce_lef_pins ()) { db::properties_id_type prop_id = 0; if (produce_pin_props ()) { db::PropertiesRepository::properties_set props; props.insert (std::make_pair (pin_prop_name_id (), tl::Variant (label))); prop_id = layout.properties_repository ().properties_id (props); } std::map boxes_for_labels; read_geometries (mg, layout.dbu (), LEFPins, &boxes_for_labels, prop_id); for (std::map ::const_iterator b = boxes_for_labels.begin (); b != boxes_for_labels.end (); ++b) { if (! b->second.empty ()) { mg->add_text (b->first, LEFLabel, db::Text (label.c_str (), db::Trans (b->second.center () - db::Point ())), 0, 0); } } } else { read_geometries (0, layout.dbu (), LEFPins, 0, 0); } expect ("END"); } else { while (! at_end () && ! test (";")) { take (); } } } expect (pn); } else if (test ("FOREIGN")) { std::string cn = get (); db::Point vec; db::FTrans ft; if (! peek (";")) { vec = get_point (1.0 / layout.dbu ()); ft = get_orient (true); } expect (";"); if (options ().macro_resolution_mode () != 1) { if (! foreign_name.empty ()) { error (tl::to_string (tr ("Duplicate FOREIGN definition"))); } // What is the definition of the FOREIGN transformation? // Guessing: this transformation moves the lower-left origin to 0,0 foreign_trans = db::Trans (db::Point () - vec) * db::Trans (ft); foreign_name = cn; if (foreign_name != mn) { warn (tl::to_string (tr ("FOREIGN name differs from MACRO name in macro: ")) + mn); } } } else if (test ("OBS")) { if (reader_state ()->tech_comp ()->produce_obstructions ()) { read_geometries (mg, layout.dbu (), Obstructions); } else { read_geometries (0, layout.dbu (), Obstructions); } expect ("END"); } else if (test ("DENSITY")) { // read over DENSITY statements while (! test ("END")) { if (test ("LAYER")) { get (); expect (";"); } else { expect ("RECT"); for (int i = 0; i < 5; ++i) { get_double (); } expect (";"); } } expect ("END"); } else if (test ("FIXEDMASK")) { mg->set_fixedmask (true); expect (";"); } else { while (! at_end () && ! test (";")) { take (); } } } mg->add_box (std::string (), Outline, db::Box (-origin, -origin + size), 0, 0); MacroDesc macro_desc; macro_desc.foreign_name = foreign_name; macro_desc.foreign_trans = foreign_trans; macro_desc.bbox = db::Box (-origin, -origin + size); macro_desc.origin = origin; m_macros.insert (std::make_pair (mn, macro_desc)); reset_cellname (); } void LEFImporter::do_read (db::Layout &layout) { db::LayoutLocker locker (&layout); // TODO: what to do with that value? // double dbu_mic = 1000; while (! at_end ()) { if (test ("END")) { expect ("LIBRARY"); // END LIBRARY should terminate the file, but we allow to continue, so we can cat LEF files: // break; } else if (test ("VERSION")) { // ignore VERSION statement currently take (); expect (";"); } else if (test ("UNITS")) { // read over SPACING sections while (! test ("END")) { if (test ("DATABASE")) { expect ("MICRONS"); // TODO: what to do with that value /* dbu_mic = */ get_double (); expect (";"); } else { while (! at_end () && ! test (";")) { take (); } } } expect ("UNITS"); } else if (test ("SPACING")) { // read over SPACING sections while (! test ("END") || ! test ("SPACING")) { take (); } } else if (test ("PROPERTYDEFINITIONS")) { // read over PROPERTYDEFINITIONS sections while (! test ("END") || ! test ("PROPERTYDEFINITIONS")) { take (); } } else if (test ("NONDEFAULTRULE")) { read_nondefaultrule (layout); } else if (test ("SITE")) { // read over SITE sections std::string n = get (); while (! test ("END") || ! test (n)) { take (); } } else if (test ("VIARULE")) { // read over VIARULE sections std::string n = get (); while (! test ("END") || ! test (n)) { take (); } } else if (test ("VIA")) { read_viadef (layout, std::string ()); } else if (test ("BEGINEXT")) { // read over BEGINEXT sections while (! test ("ENDEXT")) { take (); } } else if (test ("LAYER")) { read_layer (layout); } else if (test ("MACRO")) { read_macro (layout); } else { while (! at_end () && ! test (";")) { take (); } } } } void LEFImporter::finish_lef (db::Layout &layout) { for (std::map::const_iterator m = m_macros.begin (); m != m_macros.end (); ++m) { reader_state ()->macro_cell (m->first, layout, std::vector (), std::vector (), m->second, this); } } }