WIP: writer functionality debugged and enhanced (triangles, correct arrays etc.)

This commit is contained in:
Matthias Koefferlein 2019-11-28 23:58:19 +01:00
parent cef868d774
commit 3283219b0f
2 changed files with 219 additions and 40 deletions

View File

@ -28,6 +28,8 @@
#include "tlUtils.h" #include "tlUtils.h"
#include "tlFileUtils.h" #include "tlFileUtils.h"
#include "tlUri.h" #include "tlUri.h"
#include "tlLog.h"
#include "tlUniqueName.h"
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
@ -104,32 +106,50 @@ MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
std::string std::string
MAGWriter::layer_name (unsigned int li, const Layout &layout) MAGWriter::layer_name (unsigned int li, const Layout &layout)
{ {
// @@@ TODO: avoid built-in names, like "end", "space", "labels" ...
std::map<unsigned int, std::string>::const_iterator i = m_layer_names.find (li); std::map<unsigned int, std::string>::const_iterator i = m_layer_names.find (li);
if (i != m_layer_names.end ()) { if (i != m_layer_names.end ()) {
return i->second; return i->second;
} }
// Avoid built-in names, like "end", "space", "labels" and "checkpaint"
std::set<std::string> used_names;
used_names.insert ("end");
used_names.insert ("space");
used_names.insert ("labels");
used_names.insert ("checkpaint");
if (m_layer_names.empty ()) { if (m_layer_names.empty ()) {
for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers (); ++i) { for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers (); ++i) {
db::LayerProperties lp = layout.get_properties ((*i).first); db::LayerProperties lp = layout.get_properties ((*i).first);
if (lp.is_named ()) { if (! lp.name.empty ()) {
m_layer_names.insert (std::make_pair ((*i).first, lp.name));
std::string lp_name = lp.name;
lp_name = tl::unique_name (lp_name, used_names, "_");
used_names.insert (lp_name);
m_layer_names.insert (std::make_pair ((*i).first, lp_name));
} }
} }
for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers (); ++i) { for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers (); ++i) {
db::LayerProperties lp = layout.get_properties ((*i).first); db::LayerProperties lp = layout.get_properties ((*i).first);
if (! lp.is_named ()) { if (lp.name.empty ()) {
// @@@ TODO: avoid duplicates
std::string ld_name = std::string ("L") + tl::to_string (lp.layer); std::string ld_name = std::string ("L") + tl::to_string (lp.layer);
if (lp.datatype) { if (lp.datatype) {
ld_name += "D"; ld_name += "D";
ld_name += tl::to_string (lp.datatype); ld_name += tl::to_string (lp.datatype);
} }
ld_name = tl::unique_name (ld_name, used_names, "_");
used_names.insert (ld_name);
m_layer_names.insert (std::make_pair ((*i).first, ld_name)); m_layer_names.insert (std::make_pair ((*i).first, ld_name));
} }
} }
@ -152,6 +172,17 @@ MAGWriter::filename_for_cell (db::cell_index_type ci, db::Layout &layout)
void void
MAGWriter::write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStream &os) MAGWriter::write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStream &os)
{
m_cellname = layout.cell_name (ci);
try {
do_write_cell (ci, layout, os);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (" when writing cell ")) + m_cellname);
}
}
void
MAGWriter::do_write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStream &os)
{ {
os.set_as_text (true); os.set_as_text (true);
os << "magic\n"; os << "magic\n";
@ -163,6 +194,9 @@ MAGWriter::write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStr
db::Cell &cell = layout.cell (ci); db::Cell &cell = layout.cell (ci);
os << "<< checkpaint >>\n";
write_polygon (db::Polygon (cell.bbox ()), layout, os);
for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers (); ++i) { for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers (); ++i) {
unsigned int li = (*i).first; unsigned int li = (*i).first;
@ -198,40 +232,79 @@ MAGWriter::write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStr
} }
write_instance (i->cell_inst (), layout, os); write_instance (i->cell_inst (), layout, os);
} }
os << "<< end >>\n";
} }
namespace { namespace {
/**
* @brief A simple polygon receiver that writes triangles and rectangles from trapezoids
*/
class TrapezoidWriter class TrapezoidWriter
: public SimplePolygonSink : public SimplePolygonSink
{ {
public: public:
TrapezoidWriter (tl::OutputStream &os, double scale) TrapezoidWriter (tl::OutputStream &os)
: mp_os (&os), m_scale (scale) : mp_os (&os)
{ } { }
virtual void put (const db::SimplePolygon &polygon) virtual void put (const db::SimplePolygon &polygon)
{ {
if (! polygon.is_box ()) { db::Box b = polygon.box ();
// @@@ TODO: handle non-boxes if (b.empty () || b.height () == 0 || b.width () == 0) {
// safe fallback for degenerated polygons
return;
} }
db::DBox b = db::DBox (polygon.box ()) * m_scale; // Determine the border triangles left and right
// @@@ TODO: floating coords on output?
(*mp_os) << "rect " << b.left () << " " << b.bottom () << " " << b.right () << " " << b.top () << "\n"; // tl, tr describe the two triangles: tl left and tr right one.
// If sl/sr indicate south half of the rectangle.
db::Box tl, tr;
bool sl = false, sr = false;
for (db::SimplePolygon::polygon_edge_iterator ie = polygon.begin_edge (); ! ie.at_end (); ++ie) {
db::Edge e = *ie;
if (e.dy () > 0 /*left side*/) {
tl = e.bbox ();
sl = e.dx () > 0;
} else if (e.dy () < 0 /*right side*/) {
tr = e.bbox ();
sr = e.dx () > 0;
}
}
// outputs the parts
if (tl.width () > 0) {
(*mp_os) << "tri " << tl.left () << " " << tl.bottom () << " " << tl.right () << " " << tl.top () << " " << (sl ? "s" : "") << "e\n";
}
db::Box ib (tl.right (), tl.bottom (), tr.left (), tr.top ());
if (ib.width () > 0) {
(*mp_os) << "rect " << ib.left () << " " << ib.bottom () << " " << ib.right () << " " << ib.top () << "\n";
}
if (tr.width () > 0) {
(*mp_os) << "tri " << tr.left () << " " << tr.bottom () << " " << tr.right () << " " << tr.top () << " " << (sr ? "s" : "") << "\n";
}
} }
private: private:
tl::OutputStream *mp_os; tl::OutputStream *mp_os;
double m_scale;
}; };
} }
void void
MAGWriter::write_polygon (const db::Polygon &poly, const db::Layout & /*layout*/, tl::OutputStream &os) MAGWriter::write_polygon (const db::Polygon &poly, const db::Layout & /*layout*/, tl::OutputStream &os)
{ {
TrapezoidWriter writer (os, m_sf); db::EdgeProcessor ep;
db::decompose_trapezoids (poly, TD_simple, writer); ep.insert (scaled (poly));
db::MergeOp op;
TrapezoidWriter writer (os);
db::TrapezoidGenerator tpgen (writer);
ep.process (tpgen, op);
} }
void void
@ -250,42 +323,120 @@ MAGWriter::write_label (const std::string &layer, const db::Text &text, const db
void void
MAGWriter::write_instance (const db::CellInstArray &inst, const db::Layout &layout, tl::OutputStream &os) MAGWriter::write_instance (const db::CellInstArray &inst, const db::Layout &layout, tl::OutputStream &os)
{ {
int id = (m_cell_id [inst.object ().cell_index ()] += 1); db::Vector a, b;
std::string cn = layout.cell_name (inst.object ().cell_index ()); unsigned long na = 0, nb = 0;
os << "use " << tl::to_word_or_quoted_string (cn) << " " << tl::to_word_or_quoted_string (cn + "_" + tl::to_string (id)); if (inst.is_regular_array (a, b, na, nb) && ((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) && !needs_rounding (a) && !needs_rounding (b)) {
db::ICplxTrans tr = inst.complex_trans ();
write_single_instance (inst.object ().cell_index (), tr, a, b, na, nb, layout, os);
} else {
for (db::CellInstArray::iterator ia = inst.begin (); ! ia.at_end (); ++ia) {
db::ICplxTrans tr = inst.complex_trans (*ia);
write_single_instance (inst.object ().cell_index (), tr, db::Vector (), db::Vector (), 1, 1, layout, os);
}
}
}
void
MAGWriter::write_single_instance (db::cell_index_type ci, db::ICplxTrans trans, db::Vector a, db::Vector b, unsigned long na, unsigned long nb, const db::Layout &layout, tl::OutputStream &os)
{
if (trans.is_mag ()) {
throw tl::Exception (tl::to_string (tr ("Cannot write magnified instance to MAG files: ")) + trans.to_string () + tl::to_string (tr (" of cell ")) + layout.cell_name (ci));
}
int id = (m_cell_id [ci] += 1);
std::string cn = layout.cell_name (ci);
os << "use " << tl::to_word_or_quoted_string (cn) << " " << tl::to_word_or_quoted_string (cn + "_" + tl::to_string (id)) << "\n";
if (na > 1 || nb > 1) {
na = std::max ((unsigned long) 1, na);
nb = std::max ((unsigned long) 1, nb);
db::ICplxTrans trinv = trans.inverted ();
a = trinv * a;
b = trinv * b;
if (a.y () != 0) {
std::swap (a, b);
std::swap (na, nb);
}
db::Vector da = scaled (a);
db::Vector db = scaled (b);
os << "array " << 0 << " " << (na - 1) << " " << da.x () << " " << 0 << " " << (nb - 1) << " " << db.y () << "\n";
}
os << "timestamp " << m_timestamp << "\n"; os << "timestamp " << m_timestamp << "\n";
db::ICplxTrans tr = inst.complex_trans (); db::Matrix2d m = trans.to_matrix2d ();
db::Matrix2d m = tr.to_matrix2d ();
db::DVector d = db::DVector (tr.disp ()) * m_sf; db::Vector d = scaled (trans.disp ());
os << "transform " << m.m11 () << " " << m.m12 () << " " << d.x () << " " << m.m21 () << " " << m.m22 () << " " << d.y () << "\n"; os << "transform " << m.m11 () << " " << m.m12 () << " " << d.x () << " " << m.m21 () << " " << m.m22 () << " " << d.y () << "\n";
db::Box bx = scaled (trans * layout.cell (ci).bbox ());
os << "box " << bx.left () << " " << bx.bottom () << " " << bx.right () << " " << bx.top () << "\n";
}
namespace {
struct ScalingOp
{ {
db::Vector a, b; ScalingOp (MAGWriter *wr) : mp_wr (wr) { }
unsigned long na = 0, nb = 0; db::Point operator() (const db::Point &pt) const { return mp_wr->scaled (pt); }
if (inst.is_regular_array (a, b, na, nb) && ((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0))) { private:
MAGWriter *mp_wr;
};
na = std::max ((unsigned long) 1, na); }
nb = std::max ((unsigned long) 1, nb);
if (a.y () != 0) { db::Polygon
std::swap (a, b); MAGWriter::scaled (const db::Polygon &poly)
std::swap (na, nb); {
} db::Polygon spoly;
spoly.assign_hull (poly.begin_hull (), poly.end_hull (), ScalingOp (this));
db::DVector da = db::DVector (a) * m_sf; for (unsigned int h = 0; h < poly.holes (); ++h) {
db::DVector db = db::DVector (b) * m_sf; spoly.assign_hole (h, poly.begin_hole (h), poly.end_hole (h), ScalingOp (this));
os << "array " << 0 << " " << (na - 1) << " " << da.x () << " " << 0 << " " << (nb - 1) << " " << db.y () << "\n";
}
} }
return spoly;
}
{ db::Box
db::DBox b = db::DBox (inst.bbox (db::box_convert<db::CellInst> (layout))) * m_sf; MAGWriter::scaled (const db::Box &bx) const
os << "box " << b.left () << " " << b.bottom () << " " << b.right () << " " << b.top () << "\n"; {
return db::Box (scaled (bx.p1 ()), scaled (bx.p2 ()));
}
db::Vector
MAGWriter::scaled (const db::Vector &v) const
{
db::Vector res (db::DVector (v) * m_sf);
if (! db::DVector (res).equal (db::DVector (v) * m_sf)) {
tl::warn << tl::sprintf (tl::to_string (tr ("Vector rounding occured at %s in cell %s - not a multiple of lambda (%.12g)")), v.to_string (), m_cellname, m_options.lambda);
} }
return res;
}
db::Point
MAGWriter::scaled (const db::Point &p) const
{
db::Point res (db::DPoint (p) * m_sf);
if (! db::DPoint (res).equal (db::DPoint (p) * m_sf)) {
tl::warn << tl::sprintf (tl::to_string (tr ("Coordinate rounding occured at %s in cell %s - not a multiple of lambda (%.12g)")), p.to_string (), m_cellname, m_options.lambda);
}
return res;
}
bool
MAGWriter::needs_rounding (const db::Vector &v) const
{
db::Vector res (db::DVector (v) * m_sf);
return ! db::DVector (res).equal (db::DVector (v) * m_sf);
} }
} }

View File

@ -61,6 +61,31 @@ public:
*/ */
void write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options); void write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options);
/**
* @brief Scales the polygon to Magic lambda space
*/
db::Polygon scaled (const db::Polygon &poly);
/**
* @brief Scales the box to Magic lambda space
*/
db::Box scaled (const Box &bx) const;
/**
* @brief Scales the vector to Magic lambda space
*/
db::Vector scaled (const db::Vector &v) const;
/**
* @brief Scales the point to Magic lambda space
*/
db::Point scaled (const db::Point &p) const;
/**
* @brief Returns true if the vector can be scaled to Magic rounding space without loss
*/
bool needs_rounding (const db::Vector &v) const;
private: private:
tl::OutputStream *mp_stream; tl::OutputStream *mp_stream;
MAGWriterOptions m_options; MAGWriterOptions m_options;
@ -73,13 +98,16 @@ private:
size_t m_timestamp; size_t m_timestamp;
std::map<db::cell_index_type, size_t> m_cell_id; std::map<db::cell_index_type, size_t> m_cell_id;
double m_sf; double m_sf;
std::string m_cellname;
std::string filename_for_cell (db::cell_index_type ci, db::Layout &layout); std::string filename_for_cell (db::cell_index_type ci, db::Layout &layout);
void write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStream &os); void write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStream &os);
void do_write_cell (db::cell_index_type ci, db::Layout &layout, tl::OutputStream &os);
std::string layer_name (unsigned int li, const db::Layout &layout); std::string layer_name (unsigned int li, const db::Layout &layout);
void write_polygon (const db::Polygon &poly, const db::Layout &layout, tl::OutputStream &os); void write_polygon (const db::Polygon &poly, const db::Layout &layout, tl::OutputStream &os);
void write_label (const std::string &layer, const db::Text &text, const Layout &layout, tl::OutputStream &os); void write_label (const std::string &layer, const db::Text &text, const Layout &layout, tl::OutputStream &os);
void write_instance (const db::CellInstArray &inst, const db::Layout &layout, tl::OutputStream &os); void write_instance (const db::CellInstArray &inst, const db::Layout &layout, tl::OutputStream &os);
void write_single_instance (db::cell_index_type ci, ICplxTrans tr, db::Vector a, db::Vector b, unsigned long na, unsigned long nb, const db::Layout &layout, tl::OutputStream &os);
}; };
} // namespace db } // namespace db