mirror of https://github.com/KLayout/klayout.git
1122 lines
26 KiB
C++
1122 lines
26 KiB
C++
|
|
/*
|
|
|
|
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 <cctype>
|
|
|
|
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<std::string, double>::const_iterator l = m_default_ext.find (layer);
|
|
if (l != m_default_ext.end ()) {
|
|
return l->second;
|
|
} else {
|
|
return def_ext;
|
|
}
|
|
}
|
|
|
|
std::pair<double, double>
|
|
LEFImporter::min_layer_width (const std::string &layer) const
|
|
{
|
|
std::map<std::string, std::pair<double, double> >::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<double, double>
|
|
LEFImporter::layer_width (const std::string &layer, const std::string &nondefaultrule, const std::pair<double, double> &def_width) const
|
|
{
|
|
std::map<std::string, std::map<std::string, std::pair<double, double> > >::const_iterator nd = m_nondefault_widths.find (nondefaultrule);
|
|
|
|
std::map<std::string, std::pair<double, double> >::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 <db::Trans>
|
|
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 <db::Trans> 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 <class Shape, class Trans>
|
|
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> (shape.transformed (trans), prop_id));
|
|
}
|
|
}
|
|
|
|
template <class Shape>
|
|
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> (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<std::string, db::Box> *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<std::string, std::pair<double, double> >::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<db::Point> 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<db::Coord>::rounded (w / dbu);
|
|
db::Path p (points.begin (), points.end (), iw, iw / 2, iw / 2, false);
|
|
|
|
if (iterate) {
|
|
|
|
std::vector<db::Trans> ti = get_iteration (dbu);
|
|
|
|
for (std::vector<db::Trans>::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<db::Point> 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<db::Trans> ti = get_iteration (dbu);
|
|
for (std::vector<db::Trans>::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<db::Point> 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<db::Trans> ti = get_iteration (dbu);
|
|
|
|
for (std::vector<db::Trans>::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<db::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<db::Trans> ti = get_iteration (dbu);
|
|
for (std::vector<db::Trans>::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 <class Shape>
|
|
static db::DVector
|
|
via_size (double dbu, const Shape &shape)
|
|
{
|
|
db::Box box = db::box_convert<Shape> () (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<std::string> seen_layers;
|
|
std::vector<std::string> 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<db::Point> 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<db::Point> 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<RuleBasedViaGenerator> 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<GeometryBasedLayoutGenerator> 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 <std::string, db::Box> boxes_for_labels;
|
|
read_geometries (mg, layout.dbu (), LEFPins, &boxes_for_labels, prop_id);
|
|
|
|
for (std::map <std::string, db::Box>::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<std::string, MacroDesc>::const_iterator m = m_macros.begin (); m != m_macros.end (); ++m) {
|
|
reader_state ()->macro_cell (m->first, layout, std::vector<std::string> (), std::vector<unsigned int> (), m->second, this);
|
|
}
|
|
}
|
|
|
|
}
|
|
|