From e1882b0de7765d556301802ee774c136f275fb50 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Aug 2022 16:56:43 +0200 Subject: [PATCH] WIP: refactoring of L2N/LVSDB writer --- src/db/db/dbLayoutToNetlistWriter.cc | 444 +++++++++++---------- src/db/db/dbLayoutToNetlistWriter.h | 49 ++- src/db/db/dbLayoutVsSchematicFormatDefs.cc | 12 +- src/db/db/dbLayoutVsSchematicFormatDefs.h | 28 +- src/db/db/dbLayoutVsSchematicReader.cc | 55 +++ src/db/db/dbLayoutVsSchematicReader.h | 3 + src/db/db/dbLayoutVsSchematicWriter.cc | 126 +++--- src/db/db/dbNetlistCompare.h | 23 ++ src/db/db/dbNetlistCrossReference.cc | 6 + src/db/db/dbNetlistCrossReference.h | 18 + src/db/db/gsiDeclDbNetlistCompare.cc | 77 +++- 11 files changed, 558 insertions(+), 283 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 317906a0d..fc0b7ea2a 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -29,6 +29,9 @@ namespace db { +static const std::string endl ("\n"); +static const std::string indent1 (" "); + // ------------------------------------------------------------------------------------------- // LayoutToNetlistWriterBase implementation @@ -47,15 +50,89 @@ void LayoutToNetlistWriterBase::write (const db::LayoutToNetlist *l2n) do_write (l2n); } +// ------------------------------------------------------------------------------------------- +// TokenizedOutput implementation + +TokenizedOutput::TokenizedOutput (tl::OutputStream &s) + : mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false), m_indent (-1) +{ + // .. nothing yet .. +} + +TokenizedOutput::TokenizedOutput (tl::OutputStream &s, const std::string &token) + : mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false), m_indent (0) +{ + stream () << token << "("; +} + +TokenizedOutput::TokenizedOutput (tl::OutputStream &s, int indent, const std::string &token) + : mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false) +{ + m_indent = indent; + for (int i = 0; i < m_indent; ++i) { + stream () << indent1; + } + stream () << token << "("; +} + +TokenizedOutput::TokenizedOutput (TokenizedOutput &output, const std::string &token, bool inl) + : mp_stream (&output.stream ()), mp_parent (&output), m_first (true), m_inline (inl), m_newline (false) +{ + m_indent = output.indent () + 1; + output.emit_sep (); + stream () << token << "("; +} + +TokenizedOutput::~TokenizedOutput () +{ + if (m_newline) { + for (int i = 0; i < m_indent; ++i) { + stream () << indent1; + } + } + if (m_indent >= 0) { + stream () << ")"; + if (! m_inline) { + if (mp_parent) { + *mp_parent << endl; + } else { + stream () << endl; + } + } + } +} + +void TokenizedOutput::emit_sep () +{ + if (m_newline) { + for (int i = 0; i <= m_indent; ++i) { + stream () << indent1; + } + m_newline = false; + } else if (! m_first) { + stream () << " "; + } + m_first = false; +} + +TokenizedOutput &TokenizedOutput::operator<< (const std::string &s) +{ + if (s == endl) { + m_newline = true; + stream () << s; + } else if (! s.empty ()) { + emit_sep (); + stream () << s; + } + + return *this; +} + // ------------------------------------------------------------------------------------------- namespace l2n_std_format { -static const std::string endl ("\n"); -static const std::string indent1 (" "); -static const std::string indent2 (" "); - template std_writer_impl::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description) : mp_stream (&stream), m_dbu (dbu), mp_netlist (0), @@ -82,7 +159,10 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) mp_netlist = l2n->netlist (); mp_l2n = l2n; - write (false, 0); + { + TokenizedOutput stream (*mp_stream); + write (false, stream, 0); + } mp_netlist = 0; mp_l2n = 0; @@ -95,14 +175,14 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n) } template -void std_writer_impl::write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map > *net2id_per_circuit) +void std_writer_impl::write (TokenizedOutput &stream, bool nested, const db::Netlist *netlist, const db::LayoutToNetlist *l2n, std::map > *net2id_per_circuit) { try { mp_netlist = netlist; mp_l2n = l2n; - write (nested, net2id_per_circuit); + write (nested, stream, net2id_per_circuit); mp_netlist = 0; mp_l2n = 0; @@ -126,16 +206,20 @@ static bool same_parameter (const DeviceParameterDefinition &a, const DevicePara } template -void std_writer_impl::write_device_class (const std::string &indent, const db::DeviceClass *cls, const std::string &temp_name, const db::DeviceClass *temp_class) +void std_writer_impl::write_device_class (TokenizedOutput &stream, const db::DeviceClass *cls, const std::string &temp_name, const db::DeviceClass *temp_class) { - *mp_stream << indent << Keys::class_key << "(" << tl::to_word_or_quoted_string (cls->name ()) << " " << tl::to_word_or_quoted_string (temp_name); + TokenizedOutput out (stream, Keys::class_key); + out << tl::to_word_or_quoted_string (cls->name ()) << tl::to_word_or_quoted_string (temp_name); bool any_def = false; const std::vector &pd = cls->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { if (! temp_class->has_parameter_with_name (p->name ()) || !same_parameter (*p, *temp_class->parameter_definition (temp_class->parameter_id_for_name (p->name ())))) { - *mp_stream << endl << indent << indent1 << Keys::param_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_string (p->is_primary () ? 1 : 0) << " " << tl::to_string (p->default_value ()) << ")"; + if (! any_def) { + out << endl; + } + TokenizedOutput (out, Keys::param_key) << tl::to_word_or_quoted_string (p->name ()) << tl::to_string (p->is_primary () ? 1 : 0) << tl::to_string (p->default_value ()); any_def = true; } } @@ -143,73 +227,70 @@ void std_writer_impl::write_device_class (const std::string &indent, const const std::vector &td = cls->terminal_definitions (); for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { if (! temp_class->has_terminal_with_name (t->name ())) { - *mp_stream << endl << indent << indent1 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (t->name ()) << ")"; + if (! any_def) { + out << endl; + } + TokenizedOutput (out, Keys::terminal_key) << tl::to_word_or_quoted_string (t->name ()); any_def = true; } } - - if (any_def) { - *mp_stream << endl << indent << ")" << endl; - } else { - *mp_stream << ")" << endl; - } } template -void std_writer_impl::write (bool nested, std::map > *net2id_per_circuit) +void std_writer_impl::write (bool nested, TokenizedOutput &stream, std::map > *net2id_per_circuit) { - bool any = false; - const int version = 0; const db::Layout *ly = mp_l2n ? mp_l2n->internal_layout () : 0; - const std::string indent (nested ? indent1 : ""); if (! nested) { - *mp_stream << Keys::l2n_magic_string << endl; + stream << Keys::l2n_magic_string << endl; } if (version > 0) { - *mp_stream << indent << Keys::version_key << "(" << version << ")" << endl; + TokenizedOutput (stream, Keys::version_key) << tl::to_string (version); + stream << endl; } if (ly) { - *mp_stream << indent << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (mp_l2n->internal_top_cell ()->cell_index ())) << ")" << endl; - *mp_stream << indent << Keys::unit_key << "(" << m_dbu << ")" << endl; + TokenizedOutput (stream, Keys::top_key) << tl::to_word_or_quoted_string (ly->cell_name (mp_l2n->internal_top_cell ()->cell_index ())); + TokenizedOutput (stream, Keys::unit_key) << tl::to_string (m_dbu); } + bool any = false; + if (mp_l2n) { if (! Keys::is_short ()) { - *mp_stream << endl << indent << "# Layer section" << endl; - *mp_stream << indent << "# This section lists the mask layers (drawing or derived) and their connections." << endl; + stream << endl << "# Layer section" << endl; + stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl; } if (! Keys::is_short ()) { - *mp_stream << endl << indent << "# Mask layers" << endl; + stream << endl << "# Mask layers" << endl; } for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { - *mp_stream << indent << Keys::layer_key << "(" << name_for_layer (mp_l2n, *l); + TokenizedOutput out (stream, Keys::layer_key); + out << name_for_layer (mp_l2n, *l); db::LayerProperties lp = ly->get_properties (*l); if (! lp.is_null ()) { - *mp_stream << " " << tl::to_word_or_quoted_string (lp.to_string ()); + out << tl::to_word_or_quoted_string (lp.to_string ()); } - *mp_stream << ")" << endl; m_progress.set (mp_stream->pos ()); } if (! Keys::is_short ()) { - *mp_stream << endl << indent << "# Mask layer connectivity" << endl; + stream << endl << "# Mask layer connectivity" << endl; } for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { db::Connectivity::layer_iterator ce = mp_l2n->connectivity ().end_connected (*l); db::Connectivity::layer_iterator cb = mp_l2n->connectivity ().begin_connected (*l); if (cb != ce) { - *mp_stream << indent << Keys::connect_key << "(" << name_for_layer (mp_l2n, *l); + TokenizedOutput out (stream, Keys::connect_key); + out << name_for_layer (mp_l2n, *l); for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) { - *mp_stream << " " << name_for_layer (mp_l2n, *c); + out << name_for_layer (mp_l2n, *c); } - *mp_stream << ")" << endl; m_progress.set (mp_stream->pos ()); } @@ -223,15 +304,15 @@ void std_writer_impl::write (bool nested, std::mapconnectivity ().global_net_name (*g)); + out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (*g)); } - *mp_stream << ")" << endl; m_progress.set (mp_stream->pos ()); } @@ -240,68 +321,64 @@ void std_writer_impl::write (bool nested, std::mapbegin_device_classes () != mp_netlist->end_device_classes () && ! Keys::is_short ()) { - *mp_stream << endl << indent << "# Device class section" << endl; + stream << endl << "# Device class section" << endl; } for (db::Netlist::const_device_class_iterator c = mp_netlist->begin_device_classes (); c != mp_netlist->end_device_classes (); ++c) { db::DeviceClassTemplateBase *temp = db::DeviceClassTemplateBase::is_a (c.operator-> ()); if (temp) { std::unique_ptr temp_class (temp->create ()); - write_device_class (indent, c.operator-> (), temp->name (), temp_class.get ()); + write_device_class (stream, c.operator-> (), temp->name (), temp_class.get ()); } else { db::DeviceClass empty; - write_device_class (indent, c.operator-> (), std::string (), &empty); + write_device_class (stream, c.operator-> (), std::string (), &empty); } m_progress.set (mp_stream->pos ()); } if (mp_netlist->begin_device_abstracts () != mp_netlist->end_device_abstracts () && ! Keys::is_short ()) { - *mp_stream << endl << indent << "# Device abstracts section" << endl; - *mp_stream << indent << "# Device abstracts list the pin shapes of the devices." << endl; + stream << endl << "# Device abstracts section" << endl; + stream << "# Device abstracts list the pin shapes of the devices." << endl; } for (db::Netlist::const_abstract_model_iterator m = mp_netlist->begin_device_abstracts (); m != mp_netlist->end_device_abstracts (); ++m) { if (m->device_class ()) { - *mp_stream << indent << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; - write (*m, indent); - *mp_stream << indent << ")" << endl; + TokenizedOutput out (stream, Keys::device_key); + out << tl::to_word_or_quoted_string (m->name ()) << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; + write (out, *m); m_progress.set (mp_stream->pos ()); } } if (! Keys::is_short ()) { - *mp_stream << endl << indent << "# Circuit section" << endl; - *mp_stream << indent << "# Circuits are the hierarchical building blocks of the netlist." << endl; + stream << endl << "# Circuit section" << endl; + stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; } for (db::Netlist::const_bottom_up_circuit_iterator i = mp_netlist->begin_bottom_up (); i != mp_netlist->end_bottom_up (); ++i) { const db::Circuit *x = i.operator-> (); - *mp_stream << indent << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; - write (*x, indent, net2id_per_circuit); - *mp_stream << indent << ")" << endl; + TokenizedOutput out (stream, Keys::circuit_key); + out << tl::to_word_or_quoted_string (x->name ()) << endl; + write (out, *x, net2id_per_circuit); m_progress.set (mp_stream->pos ()); } } -void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref, bool relative) +static void write_point (TokenizedOutput &out, const db::Point &pt, db::Point &ref, bool relative) { if (relative) { - stream << "("; - stream << pt.x () - ref.x (); - stream << " "; - stream << pt.y () - ref.y (); - stream << ")"; + TokenizedOutput (out, std::string (), true) << tl::to_string (pt.x () - ref.x ()) << tl::to_string (pt.y () - ref.y ()); } else { if (pt.x () == 0 || pt.x () != ref.x ()) { - stream << pt.x (); + out << tl::to_string (pt.x ()); } else { - stream << "*"; + out << "*"; } if (pt.y () == 0 || pt.y () != ref.y ()) { - stream << pt.y (); + out << tl::to_string (pt.y ()); } else { - stream << "*"; + out << "*"; } } @@ -310,25 +387,20 @@ void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref, } template -void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr, db::Point &ref, bool relative) +static void write_points (TokenizedOutput &out, const T &poly, const Tr &tr, db::Point &ref, bool relative) { for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) { - - typename T::point_type pt = tr * *c; - - stream << " "; - write_point (stream, pt, ref, relative); - + write_point (out, tr * *c, ref, relative); } } template -void std_writer_impl::write (const db::Circuit &circuit, const std::string &indent, std::map > *net2id_per_circuit) +void std_writer_impl::write (TokenizedOutput &stream, const db::Circuit &circuit, std::map > *net2id_per_circuit) { if (circuit.boundary ().vertices () > 0) { if (! Keys::is_short ()) { - *mp_stream << endl << indent << indent1 << "# Circuit boundary" << endl; + stream << endl << "# Circuit boundary" << endl; } reset_geometry_ref (); @@ -337,22 +409,20 @@ void std_writer_impl::write (const db::Circuit &circuit, const std::string if (poly.is_box ()) { db::Box box = poly.box (); - *mp_stream << indent << indent1 << Keys::rect_key << "("; - write_point (*mp_stream, box.p1 (), m_ref, true); - *mp_stream << " "; - write_point (*mp_stream, box.p2 (), m_ref, true); - *mp_stream << ")" << endl; + + TokenizedOutput out (stream, Keys::rect_key); + write_point (out, box.p1 (), m_ref, true); + write_point (out, box.p2 (), m_ref, true); } else { - *mp_stream << indent << indent1 << Keys::polygon_key << "("; + TokenizedOutput out (stream, Keys::polygon_key); if (poly.holes () > 0) { db::SimplePolygon sp = db::polygon_to_simple_polygon (poly); - write_points (*mp_stream, sp, db::UnitTrans (), m_ref, true); + write_points (out, sp, db::UnitTrans (), m_ref, true); } else { - write_points (*mp_stream, poly, db::UnitTrans (), m_ref, true); + write_points (out, poly, db::UnitTrans (), m_ref, true); } - *mp_stream << ")" << endl; } @@ -360,9 +430,9 @@ void std_writer_impl::write (const db::Circuit &circuit, const std::string for (db::NetlistObject::property_iterator p = circuit.begin_properties (); p != circuit.end_properties (); ++p) { if (p == circuit.begin_properties() && ! Keys::is_short ()) { - *mp_stream << endl << indent << indent1 << "# Properties" << endl; + stream << endl << "# Properties" << endl; } - *mp_stream << indent << indent1 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + TokenizedOutput (stream, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string (); } std::map net2id_local; @@ -379,60 +449,56 @@ void std_writer_impl::write (const db::Circuit &circuit, const std::string if (circuit.begin_nets () != circuit.end_nets ()) { if (! Keys::is_short ()) { if (mp_l2n) { - *mp_stream << endl << indent << indent1 << "# Nets with their geometries" << endl; + stream << endl << "# Nets with their geometries" << endl; } else { - *mp_stream << endl << indent << indent1 << "# Nets" << endl; + stream << endl << "# Nets" << endl; } } for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { - write (*n, (*net2id) [n.operator-> ()], indent); + write (stream, *n, (*net2id) [n.operator-> ()]); m_progress.set (mp_stream->pos ()); } } if (circuit.begin_pins () != circuit.end_pins ()) { if (! Keys::is_short ()) { - *mp_stream << endl << indent << indent1 << "# Outgoing pins and their connections to nets" << endl; + stream << endl << "# Outgoing pins and their connections to nets" << endl; } for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { - *mp_stream << indent << indent1 << Keys::pin_key << "("; + TokenizedOutput out (stream, Keys::pin_key); const db::Net *net = circuit.net_for_pin (p->id ()); if (net) { - *mp_stream << (*net2id) [net]; + out << tl::to_string ((*net2id) [net]); } if (! p->name ().empty ()) { - if (net) { - *mp_stream << " "; - } - *mp_stream << Keys::name_key << "(" << tl::to_word_or_quoted_string (p->name ()) << ")"; + TokenizedOutput (out, Keys::name_key, true) << tl::to_word_or_quoted_string (p->name ()); } - *mp_stream << ")" << endl; m_progress.set (mp_stream->pos ()); } } if (circuit.begin_devices () != circuit.end_devices ()) { if (! Keys::is_short ()) { - *mp_stream << endl << indent << indent1 << "# Devices and their connections" << endl; + stream << endl << "# Devices and their connections" << endl; } for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { - write (*d, *net2id, indent); + write (stream, *d, *net2id); m_progress.set (mp_stream->pos ()); } } if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) { if (! Keys::is_short ()) { - *mp_stream << endl << indent << indent1 << "# Subcircuits and their connections" << endl; + stream << endl << "# Subcircuits and their connections" << endl; } for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { - write (*x, *net2id, indent); + write (stream, *x, *net2id); m_progress.set (mp_stream->pos ()); } } if (! Keys::is_short ()) { - *mp_stream << endl; + stream << endl; } } @@ -443,7 +509,7 @@ void std_writer_impl::reset_geometry_ref () } template -void std_writer_impl::write (const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative) +void std_writer_impl::write (TokenizedOutput &stream, const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative) { if (s->type () == db::NetShape::Polygon) { @@ -454,39 +520,36 @@ void std_writer_impl::write (const db::NetShape *s, const db::ICplxTrans & if (poly.is_box ()) { db::Box box = t * poly.box (); - *mp_stream << Keys::rect_key << "(" << lname; - *mp_stream << " "; - write_point (*mp_stream, box.p1 (), m_ref, relative); - *mp_stream << " "; - write_point (*mp_stream, box.p2 (), m_ref, relative); - *mp_stream << ")"; + TokenizedOutput out (stream, Keys::rect_key); + out << lname; + write_point (out, box.p1 (), m_ref, relative); + write_point (out, box.p2 (), m_ref, relative); } else { - *mp_stream << Keys::polygon_key << "(" << lname; + TokenizedOutput out (stream, Keys::polygon_key); + out << lname; if (poly.holes () > 0) { db::SimplePolygon sp = db::polygon_to_simple_polygon (poly); - write_points (*mp_stream, sp, t, m_ref, relative); + write_points (out, sp, t, m_ref, relative); } else { - write_points (*mp_stream, poly, t, m_ref, relative); + write_points (out, poly, t, m_ref, relative); } - *mp_stream << ")"; } } else if (s->type () == db::NetShape::Text) { - *mp_stream << Keys::text_key << "(" << lname; + TokenizedOutput out (stream, Keys::text_key); + out << lname; db::TextRef txtr = s->text_ref (); db::ICplxTrans t = tr * db::ICplxTrans (txtr.trans ()); - *mp_stream << " " << tl::to_word_or_quoted_string (txtr.obj ().string ()) << " "; + out << tl::to_word_or_quoted_string (txtr.obj ().string ()); db::Point pt = t * (db::Point () + txtr.obj ().trans ().disp ()); - write_point (*mp_stream, pt, m_ref, relative); - - *mp_stream << ")"; + write_point (out, pt, m_ref, relative); } } @@ -498,13 +561,13 @@ bool std_writer_impl::new_cell (cell_index_type ci) const } template -void std_writer_impl::write (const db::Net &net, unsigned int id, const std::string &indent) +void std_writer_impl::write (TokenizedOutput &stream, const db::Net &net, unsigned int id) { const db::hier_clusters &clusters = mp_l2n->net_clusters (); const db::Circuit *circuit = net.circuit (); const db::Connectivity &conn = mp_l2n->connectivity (); - bool any = false; + std::unique_ptr outp; if (mp_l2n) { @@ -527,25 +590,24 @@ void std_writer_impl::write (const db::Net &net, unsigned int id, const st } else { - if (! any) { + if (! outp) { - *mp_stream << indent << indent1 << Keys::net_key << "(" << id; + outp.reset (new TokenizedOutput (stream, Keys::net_key)); + + *outp << tl::to_string (id); if (! net.name ().empty ()) { - *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ()); } - *mp_stream << endl; + + *outp << endl; for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) { - *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + TokenizedOutput (*outp, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string (); } - any = true; - } - *mp_stream << indent << indent2; - write (si.operator-> (), si.trans (), name_for_layer (mp_l2n, *l), true); - *mp_stream << endl; + write (*outp, si.operator-> (), si.trans (), name_for_layer (mp_l2n, *l), true); m_progress.set (mp_stream->pos ()); prev_ci = ci; @@ -560,80 +622,66 @@ void std_writer_impl::write (const db::Net &net, unsigned int id, const st } - if (any) { - *mp_stream << indent << indent1 << ")" << endl; - } else { + if (! outp) { + + outp.reset (new TokenizedOutput (stream, Keys::net_key)); + *outp << tl::to_string (id); - *mp_stream << indent << indent1 << Keys::net_key << "(" << id; if (! net.name ().empty ()) { - *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ()); } + if (net.begin_properties () != net.end_properties ()) { - *mp_stream << endl; + *outp << endl; for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) { - *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + TokenizedOutput (*outp, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string (); } - *mp_stream << indent << ")" << endl; - } else { - *mp_stream << ")" << endl; } } } template -void std_writer_impl::write (const db::SubCircuit &subcircuit, std::map &net2id, const std::string &indent) +void std_writer_impl::write (TokenizedOutput &stream, const db::SubCircuit &subcircuit, std::map &net2id) { - *mp_stream << indent << indent1 << Keys::circuit_key << "(" << tl::to_string (subcircuit.id ()); - *mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ()); + TokenizedOutput out (stream, Keys::circuit_key); + out << tl::to_string (subcircuit.id ()); + out << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ()); if (! subcircuit.name ().empty ()) { - *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()) << ")"; + TokenizedOutput (out, Keys::name_key, true) << tl::to_word_or_quoted_string (subcircuit.name ()); } if (mp_l2n) { - *mp_stream << " "; - write (subcircuit.trans ()); + write (out, subcircuit.trans ()); } // each pin in one line for more than a few pins bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1) || subcircuit.begin_properties () != subcircuit.end_properties (); if (separate_lines) { - *mp_stream << endl; + out << endl; } for (db::NetlistObject::property_iterator p = subcircuit.begin_properties (); p != subcircuit.end_properties (); ++p) { - *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + TokenizedOutput (out, Keys::property_key, ! separate_lines) << p->first.to_parsable_string () << p->second.to_parsable_string (); } unsigned int pin_id = 0; for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p, ++pin_id) { const db::Net *net = subcircuit.net_for_pin (p->id ()); if (net) { - if (separate_lines) { - *mp_stream << indent << indent2; - } else { - *mp_stream << " "; - } - *mp_stream << Keys::pin_key << "(" << tl::to_string (pin_id) << " " << net2id [net] << ")"; - if (separate_lines) { - *mp_stream << endl; - } + TokenizedOutput (out, Keys::pin_key, ! separate_lines) << tl::to_string (pin_id) << tl::to_string (net2id [net]); m_progress.set (mp_stream->pos ()); } } - - if (separate_lines) { - *mp_stream << indent << indent1; - } - - *mp_stream << ")" << endl; } template -void std_writer_impl::write (const db::DeviceAbstract &device_abstract, const std::string &indent) +void std_writer_impl::write (TokenizedOutput &stream, const db::DeviceAbstract &device_abstract) { + tl_assert (mp_l2n); + const std::vector &td = device_abstract.device_class ()->terminal_definitions (); const db::hier_clusters &clusters = mp_l2n->net_clusters (); @@ -641,10 +689,13 @@ void std_writer_impl::write (const db::DeviceAbstract &device_abstract, co for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - *mp_stream << indent << indent1 << Keys::terminal_key << "(" << t->name () << endl; + TokenizedOutput out (stream, Keys::terminal_key); + out << t->name (); reset_geometry_ref (); + bool any = false; + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { size_t cid = device_abstract.cluster_id_for_terminal (t->id ()); @@ -656,72 +707,62 @@ void std_writer_impl::write (const db::DeviceAbstract &device_abstract, co const db::local_cluster &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (cid); for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { - *mp_stream << indent << indent2; - write (s.operator-> (), db::ICplxTrans (), name_for_layer (mp_l2n, *l), true); - *mp_stream << endl; + if (! any) { + out << endl; + } + + write (out, s.operator-> (), db::ICplxTrans (), name_for_layer (mp_l2n, *l), true); m_progress.set (mp_stream->pos ()); + any = true; + } } - *mp_stream << indent << indent1 << ")" << endl; m_progress.set (mp_stream->pos ()); } } template -void std_writer_impl::write (const db::DCplxTrans &tr) +void std_writer_impl::write (TokenizedOutput &stream, const db::DCplxTrans &tr) { - bool first = true; - if (tr.is_mag ()) { - *mp_stream << Keys::scale_key << "(" << tr.mag () << ")"; - first = false; + TokenizedOutput (stream, Keys::scale_key, true) << tl::to_string (tr.mag ()); } if (tr.is_mirror ()) { - if (! first) { - *mp_stream << " "; - } - *mp_stream << Keys::mirror_key; - first = false; + stream << Keys::mirror_key; } if (fabs (tr.angle ()) > 1e-6) { - if (! first) { - *mp_stream << " "; - } - *mp_stream << Keys::rotation_key << "(" << tr.angle () << ")"; - first = false; + TokenizedOutput (stream, Keys::rotation_key, true) << tl::to_string (tr.angle ()); } - if (! first) { - *mp_stream << " "; - } - *mp_stream << Keys::location_key << "(" << floor (0.5 + tr.disp ().x () / m_dbu) << " " << floor (0.5 + tr.disp ().y () / m_dbu) << ")"; + TokenizedOutput (stream, Keys::location_key, true) << tl::to_string (floor (0.5 + tr.disp ().x () / m_dbu)) << tl::to_string (floor (0.5 + tr.disp ().y () / m_dbu)); } template -void std_writer_impl::write (const db::Device &device, std::map &net2id, const std::string &indent) +void std_writer_impl::write (TokenizedOutput &stream, const db::Device &device, std::map &net2id) { tl_assert (device.device_class () != 0); const std::vector &td = device.device_class ()->terminal_definitions (); const std::vector &pd = device.device_class ()->parameter_definitions (); - *mp_stream << indent << indent1 << Keys::device_key << "(" << tl::to_string (device.id ()); + TokenizedOutput out (stream, Keys::device_key); + out << tl::to_string (device.id ()); if (device.device_abstract ()) { - *mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl; + out << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl; const std::vector &other_abstracts = device.other_abstracts (); for (std::vector::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) { - *mp_stream << indent << indent2 << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " "; - write (a->trans); - *mp_stream << ")" << endl; + TokenizedOutput o (out, Keys::device_key); + o << tl::to_word_or_quoted_string (a->device_abstract->name ()); + write (o, a->trans); } @@ -729,41 +770,38 @@ void std_writer_impl::write (const db::Device &device, std::map >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) { for (std::vector::const_iterator c = t->second.begin (); c != t->second.end (); ++c) { - *mp_stream << indent << indent2 << Keys::connect_key << "(" << c->device_index << " " << tl::to_word_or_quoted_string (td [t->first].name ()) << " " << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()) << ")" << endl; + TokenizedOutput (out, Keys::connect_key) << tl::to_string (c->device_index) << tl::to_word_or_quoted_string (td [t->first].name ()) << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()); } } - *mp_stream << indent << indent2; - write (device.trans ()); - *mp_stream << endl; + write (out, device.trans ()); + out << endl; } else { - *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl; + out << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl; } if (! device.name ().empty ()) { - *mp_stream << indent << indent2 << Keys::name_key << "(" << tl::to_word_or_quoted_string (device.name ()) << ")" << endl; + TokenizedOutput (out, Keys::name_key) << tl::to_word_or_quoted_string (device.name ()); } for (db::NetlistObject::property_iterator p = device.begin_properties (); p != device.end_properties (); ++p) { - *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + TokenizedOutput (out, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string (); } for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - *mp_stream << indent << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::sprintf ("%.12g", device.parameter_value (i->id ())) << ")" << endl; + TokenizedOutput (out, Keys::param_key) << tl::to_word_or_quoted_string (i->name ()) << tl::sprintf ("%.12g", device.parameter_value (i->id ())); } for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { const db::Net *net = device.net_for_terminal (i->id ()); + TokenizedOutput o (out, Keys::terminal_key); + o << tl::to_word_or_quoted_string (i->name ()); if (net) { - *mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl; - } else { - *mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << ")" << endl; + o << tl::to_string (net2id [net]); } } - - *mp_stream << indent << indent1 << ")" << endl; } // explicit instantiation diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h index 55964cb2d..8d960fe36 100644 --- a/src/db/db/dbLayoutToNetlistWriter.h +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -44,6 +44,35 @@ class Netlist; class LayoutToNetlist; class NetShape; +/** + * @brief A helper class to produce token/list lines + * Such lines are like: + * token(a b c) + * This class takes care of properly handling separation blanks + */ +class TokenizedOutput +{ +public: + TokenizedOutput (tl::OutputStream &stream); + TokenizedOutput (tl::OutputStream &stream, const std::string &token); + TokenizedOutput (tl::OutputStream &stream, int indent, const std::string &token); + TokenizedOutput (TokenizedOutput &output, const std::string &token, bool inl = false); + ~TokenizedOutput (); + + TokenizedOutput &operator<< (const std::string &s); + + tl::OutputStream &stream () { return *mp_stream; } + +private: + tl::OutputStream *mp_stream; + TokenizedOutput *mp_parent; + bool m_first, m_inline, m_newline; + int m_indent; + + void emit_sep (); + int indent () const { return m_indent; } +}; + namespace l2n_std_format { @@ -55,7 +84,7 @@ public: std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ()); void write (const db::LayoutToNetlist *l2n); - void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map > *net2id_per_circuit); + void write (TokenizedOutput &stream, bool nested, const db::Netlist *netlist, const db::LayoutToNetlist *l2n, std::map > *net2id_per_circuit); protected: tl::OutputStream &stream () @@ -71,15 +100,15 @@ private: const db::LayoutToNetlist *mp_l2n; tl::AbsoluteProgress m_progress; - void write (bool nested, std::map > *net2id_per_circuit); - void write (const db::Circuit &circuit, const std::string &indent, std::map > *net2id_per_circuit); - void write (const db::Net &net, unsigned int id, const std::string &indent); - void write (const db::SubCircuit &subcircuit, std::map &net2id, const std::string &indent); - void write (const db::Device &device, std::map &net2id, const std::string &indent); - void write (const db::DeviceAbstract &device_abstract, const std::string &indent); - void write (const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative); - void write (const db::DCplxTrans &trans); - void write_device_class (const std::string &indent, const db::DeviceClass *cls, const std::string &name, const db::DeviceClass *temp_class); + void write (bool nested, TokenizedOutput &stream, std::map > *net2id_per_circuit); + void write (TokenizedOutput &stream, const db::Circuit &circuit, std::map > *net2id_per_circuit); + void write (TokenizedOutput &stream, const db::Net &net, unsigned int id); + void write (TokenizedOutput &stream, const db::SubCircuit &subcircuit, std::map &net2id); + void write (TokenizedOutput &stream, const db::Device &device, std::map &net2id); + void write (TokenizedOutput &stream, const db::DeviceAbstract &device_abstract); + void write (TokenizedOutput &stream, const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative); + void write (TokenizedOutput &stream, const db::DCplxTrans &trans); + void write_device_class (TokenizedOutput &stream, const db::DeviceClass *cls, const std::string &name, const db::DeviceClass *temp_class); void reset_geometry_ref (); // implementation of CircuitCallback diff --git a/src/db/db/dbLayoutVsSchematicFormatDefs.cc b/src/db/db/dbLayoutVsSchematicFormatDefs.cc index 8545bf3f7..37ad75db1 100644 --- a/src/db/db/dbLayoutVsSchematicFormatDefs.cc +++ b/src/db/db/dbLayoutVsSchematicFormatDefs.cc @@ -43,9 +43,9 @@ namespace lvs_std_format DB_PUBLIC std::string LongKeys::warning_key ("warning"); DB_PUBLIC std::string LongKeys::skipped_key ("skipped"); - DB_PUBLIC std::string LongKeys::info_level_key ("info"); - DB_PUBLIC std::string LongKeys::warning_level_key ("warning"); - DB_PUBLIC std::string LongKeys::error_level_key ("error"); + DB_PUBLIC std::string LongKeys::info_severity_key ("info"); + DB_PUBLIC std::string LongKeys::warning_severity_key ("warning"); + DB_PUBLIC std::string LongKeys::error_severity_key ("error"); // E, H, I, J, L, M, S, W, X, Z, 0, 1 @@ -61,9 +61,9 @@ namespace lvs_std_format DB_PUBLIC std::string ShortKeys::warning_key ("W"); DB_PUBLIC std::string ShortKeys::skipped_key ("S"); - DB_PUBLIC std::string ShortKeys::info_level_key ("I"); - DB_PUBLIC std::string ShortKeys::warning_level_key ("W"); - DB_PUBLIC std::string ShortKeys::error_level_key ("E"); + DB_PUBLIC std::string ShortKeys::info_severity_key ("I"); + DB_PUBLIC std::string ShortKeys::warning_severity_key ("W"); + DB_PUBLIC std::string ShortKeys::error_severity_key ("E"); } } diff --git a/src/db/db/dbLayoutVsSchematicFormatDefs.h b/src/db/db/dbLayoutVsSchematicFormatDefs.h index 17a7a4101..338a0ec43 100644 --- a/src/db/db/dbLayoutVsSchematicFormatDefs.h +++ b/src/db/db/dbLayoutVsSchematicFormatDefs.h @@ -80,9 +80,9 @@ namespace db * log([log-entry]*) - log entries [short key: L] * * [log-entry]: - * entry([level] [any]*) - log entry [short key: M] + * entry([severity] [message|any]*) - log entry [short key: M] * - * [level]: + * [severity]: * info | - [short key: I] * warning | - [short key: W] * error - [short key: E] @@ -92,16 +92,20 @@ namespace db * - circuit cross-reference part [short key: Z] * * [xref-pin]: - * pin([ion] [ion] [status]? [message]? [any]*) - a pin pair [short key: P] + * pin([ion] [ion] [status]? [message]? [any]*) + * - a pin pair [short key: P] * * [xref-device]: - * device([ion] [ion] [status]? [message]? [any]*) - a device pair [short key: D] + * device([ion] [ion] [status]? [message]? [any]*) + * - a device pair [short key: D] * * [xref-circuit]: - * circuit([ion] [ion] [status]? [message]? [any]*) - a subcircuit pair [short key: X] + * circuit([ion] [ion] [status]? [message]? [any]*) + * - a subcircuit pair [short key: X] * * [xref-net]: - * net([ion] [ion] [status]? [message]? [any]*) - a net pair [short key: N] + * net([ion] [ion] [status]? [message]? [any]*) + * - a net pair [short key: N] * * [ion]: * | () @@ -142,9 +146,9 @@ namespace lvs_std_format static std::string warning_key; static std::string skipped_key; - static std::string info_level_key; - static std::string warning_level_key; - static std::string error_level_key; + static std::string info_severity_key; + static std::string warning_severity_key; + static std::string error_severity_key; }; struct DB_PUBLIC LongKeys @@ -163,9 +167,9 @@ namespace lvs_std_format static std::string warning_key; static std::string skipped_key; - static std::string info_level_key; - static std::string warning_level_key; - static std::string error_level_key; + static std::string info_severity_key; + static std::string warning_severity_key; + static std::string error_severity_key; }; template struct DB_PUBLIC keys; diff --git a/src/db/db/dbLayoutVsSchematicReader.cc b/src/db/db/dbLayoutVsSchematicReader.cc index f7924742a..bdc875a34 100644 --- a/src/db/db/dbLayoutVsSchematicReader.cc +++ b/src/db/db/dbLayoutVsSchematicReader.cc @@ -150,6 +150,59 @@ bool LayoutVsSchematicStandardReader::read_status (db::NetlistCrossReference::St } } +bool LayoutVsSchematicStandardReader::read_severity (db::NetlistCrossReference::Severity &severity) +{ + if (test (skeys::info_severity_key) || test (lkeys::info_severity_key)) { + severity = db::NetlistCrossReference::Info; + return true; + } else if (test (skeys::warning_severity_key) || test (lkeys::warning_severity_key)) { + severity = db::NetlistCrossReference::Warning; + return true; + } else if (test (skeys::error_severity_key) || test (lkeys::error_severity_key)) { + severity = db::NetlistCrossReference::Error; + return true; + } else { + return false; + } +} + +void LayoutVsSchematicStandardReader::read_log_entry (db::NetlistCrossReference *xref) +{ + db::NetlistCrossReference::Severity severity = db::NetlistCrossReference::NoSeverity; + std::string msg; + + Brace br (this); + while (br) { + if (read_severity (severity)) { + // continue + } else if (read_message (msg)) { + // continue + } else { + skip_element (); + } + } + br.done (); + + xref->log_entry (severity, msg); +} + +void LayoutVsSchematicStandardReader::read_logs_for_circuits (db::NetlistCrossReference *xref) +{ + Brace br (this); + while (br) { + + if (test (skeys::log_entry_key) || test (lkeys::log_entry_key)) { + read_log_entry (xref); + } else if (at_end ()) { + throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (net, pin, device or circuit expected)"))); + } else { + skip_element (); + } + + } + br.done (); +} + void LayoutVsSchematicStandardReader::read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b) { Brace br (this); @@ -215,6 +268,8 @@ void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref // continue } else if (test (skeys::xref_key) || test (lkeys::xref_key)) { read_xrefs_for_circuits (xref, circuit_a, circuit_b); + } else if (test (skeys::log_key) || test (lkeys::log_key)) { + read_logs_for_circuits (xref); } else if (at_end ()) { throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (status keyword of xrefs expected)"))); } else { diff --git a/src/db/db/dbLayoutVsSchematicReader.h b/src/db/db/dbLayoutVsSchematicReader.h index 6e8a3b679..0f9c65b92 100644 --- a/src/db/db/dbLayoutVsSchematicReader.h +++ b/src/db/db/dbLayoutVsSchematicReader.h @@ -79,6 +79,9 @@ private: bool read_status (db::NetlistCrossReference::Status &status); bool read_message (std::string &msg); + void read_log_entry (db::NetlistCrossReference *xref); + void read_logs_for_circuits (db::NetlistCrossReference *xref); + bool read_severity (db::NetlistCrossReference::Severity &severity); void read_xref (db::NetlistCrossReference *xref); void read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b); void read_net_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b); diff --git a/src/db/db/dbLayoutVsSchematicWriter.cc b/src/db/db/dbLayoutVsSchematicWriter.cc index d92b7c2d8..5cb57cf09 100644 --- a/src/db/db/dbLayoutVsSchematicWriter.cc +++ b/src/db/db/dbLayoutVsSchematicWriter.cc @@ -28,6 +28,8 @@ namespace db { +static const std::string endl ("\n"); + // ------------------------------------------------------------------------------------------- // LayoutVsSchematicWriterBase implementation @@ -61,25 +63,22 @@ class std_writer_impl public: std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ()); - void write (const db::LayoutVsSchematic *l2n); + void write (const db::LayoutVsSchematic *lvs); private: - tl::OutputStream &stream () + tl::OutputStream &ostream () { return l2n_std_format::std_writer_impl::stream (); } std::string status_to_s (const db::NetlistCrossReference::Status status); + std::string severity_to_s (const db::NetlistCrossReference::Severity severity); std::string message_to_s (const std::string &msg); - void write (const db::NetlistCrossReference *xref); + void write (TokenizedOutput &stream, const db::NetlistCrossReference *xref); std::map > m_net2id_per_circuit_a, m_net2id_per_circuit_b; }; -static const std::string endl ("\n"); -static const std::string indent1 (" "); -static const std::string indent2 (" "); - template std_writer_impl::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description) : l2n_std_format::std_writer_impl (stream, dbu, progress_description.empty () ? tl::to_string (tr ("Writing LVS database")) : progress_description) @@ -90,39 +89,40 @@ std_writer_impl::std_writer_impl (tl::OutputStream &stream, double dbu, co template void std_writer_impl::write (const db::LayoutVsSchematic *lvs) { + TokenizedOutput out (ostream ()); + out << Keys::lvs_magic_string << endl; + const int version = 0; - stream () << Keys::lvs_magic_string << endl; - if (version > 0) { - stream () << Keys::version_key << "(" << version << ")" << endl; + TokenizedOutput (out, Keys::version_key) << tl::to_string (version); } if (lvs->netlist ()) { if (! Keys::is_short ()) { - stream () << endl << "# Layout" << endl; + out << endl << "# Layout" << endl; } - stream () << Keys::layout_key << "(" << endl; - l2n_std_format::std_writer_impl::write (lvs->netlist (), lvs, true, &m_net2id_per_circuit_a); - stream () << ")" << endl; + TokenizedOutput o (out, Keys::layout_key); + o << endl; + l2n_std_format::std_writer_impl::write (o, true, lvs->netlist (), lvs, &m_net2id_per_circuit_a); } if (lvs->reference_netlist ()) { if (! Keys::is_short ()) { - stream () << endl << "# Reference netlist" << endl; + out << endl << "# Reference netlist" << endl; } - stream () << Keys::reference_key << "(" << endl; - l2n_std_format::std_writer_impl::write (lvs->reference_netlist (), 0, true, &m_net2id_per_circuit_b); - stream () << ")" << endl; + TokenizedOutput o (out, Keys::reference_key); + o << endl; + l2n_std_format::std_writer_impl::write (o, true, lvs->reference_netlist (), 0, &m_net2id_per_circuit_b); } if (lvs->cross_ref ()) { if (! Keys::is_short ()) { - stream () << endl << "# Cross reference" << endl; + out << endl << "# Cross reference" << endl; } - stream () << Keys::xref_key << "(" << endl; - write (lvs->cross_ref ()); - stream () << ")" << endl; + TokenizedOutput o (out, Keys::xref_key); + o << endl; + write (o, lvs->cross_ref ()); } } @@ -184,7 +184,7 @@ std::string std_writer_impl::message_to_s (const std::string &msg) if (msg.empty ()) { return std::string (); } else { - return " " + Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")"; + return Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")"; } } @@ -192,54 +192,82 @@ template std::string std_writer_impl::status_to_s (const db::NetlistCrossReference::Status status) { if (status == db::NetlistCrossReference::Match) { - return " " + Keys::match_key; + return Keys::match_key; } else if (status == db::NetlistCrossReference::NoMatch) { - return " " + Keys::nomatch_key; + return Keys::nomatch_key; } else if (status == db::NetlistCrossReference::Mismatch) { - return " " + Keys::mismatch_key; + return Keys::mismatch_key; } else if (status == db::NetlistCrossReference::MatchWithWarning) { - return " " + Keys::warning_key; + return Keys::warning_key; } else if (status == db::NetlistCrossReference::Skipped) { - return " " + Keys::skipped_key; + return Keys::skipped_key; } else { return std::string (); } } template -void std_writer_impl::write (const db::NetlistCrossReference *xref) +std::string std_writer_impl::severity_to_s (const db::NetlistCrossReference::Severity severity) +{ + if (severity == db::NetlistCrossReference::Info) { + return Keys::info_severity_key; + } else if (severity == db::NetlistCrossReference::Warning) { + return Keys::warning_severity_key; + } else if (severity == db::NetlistCrossReference::Error) { + return Keys::error_severity_key; + } else { + return std::string (); + } +} + +template +void std_writer_impl::write (TokenizedOutput &stream, const db::NetlistCrossReference *xref) { for (db::NetlistCrossReference::circuits_iterator c = xref->begin_circuits (); c != xref->end_circuits (); ++c) { const db::NetlistCrossReference::PerCircuitData *pcd = xref->per_circuit_data_for (*c); tl_assert (pcd != 0); - stream () << indent1 << Keys::circuit_key << "(" << name_to_s (c->first) << " " << name_to_s (c->second) << status_to_s (pcd->status) << message_to_s (pcd->msg) << endl; - stream () << indent2 << Keys::xref_key << "(" << endl; + TokenizedOutput out (stream, Keys::circuit_key); + out << name_to_s (c->first) << name_to_s (c->second) << status_to_s (pcd->status) << message_to_s (pcd->msg); + out << endl; + + if (! pcd->log_entries.empty ()) { + + TokenizedOutput o (out, Keys::log_key); + o << endl; + + for (db::NetlistCrossReference::PerCircuitData::log_entries_const_iterator l = pcd->log_entries.begin (); l != pcd->log_entries.end (); ++l) { + TokenizedOutput (o, Keys::log_entry_key, true) << severity_to_s (l->severity) << message_to_s (l->msg); + } - for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) { - stream () << indent1 << indent2 << Keys::net_key << "(" << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << " " << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl; } - std::map pin2index_a, pin2index_b; - build_pin_index_map (c->first, pin2index_a); - build_pin_index_map (c->second, pin2index_b); + { + TokenizedOutput o (out, Keys::xref_key); + o << endl; - for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) { - stream () << indent1 << indent2 << Keys::pin_key << "(" << pin_id_to_s (n->pair.first, pin2index_a) << " " << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl; + for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) { + TokenizedOutput (o, Keys::net_key) << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << message_to_s (n->msg); + } + + std::map pin2index_a, pin2index_b; + build_pin_index_map (c->first, pin2index_a); + build_pin_index_map (c->second, pin2index_b); + + for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) { + TokenizedOutput (o, Keys::pin_key) << pin_id_to_s (n->pair.first, pin2index_a) << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << message_to_s (n->msg); + } + + for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) { + TokenizedOutput (o, Keys::device_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg); + } + + for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) { + TokenizedOutput (o, Keys::circuit_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg); + } } - for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) { - stream () << indent1 << indent2 << Keys::device_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl; - } - - for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) { - stream () << indent1 << indent2 << Keys::circuit_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl; - } - - stream () << indent2 << ")" << endl; - stream () << indent1 << ")" << endl; - } } diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index 9160a45a8..3424cff9b 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -50,6 +50,16 @@ public: NetlistCompareLogger () { } virtual ~NetlistCompareLogger () { } + /** + * @brief An enum describing the severity for the log_entry function + */ + enum Severity { + NoSeverity = 0, // unspecific + Info = 1, // information only + Warning = 2, // a warning + Error = 3 // an error + }; + /** * @brief Begin logging for netlist a and b */ @@ -88,6 +98,19 @@ public: */ virtual void circuit_mismatch (const db::Circuit * /*a*/, const db::Circuit * /*b*/, const std::string & /*msg*/ = std::string ()) { } + /** + * @brief Receives log entries for the current circuit pair + * These events are only generated when the "wants_log_entry" hint returns true. + */ + virtual void log_entry (Severity /*level*/, const std::string & /*msg*/) { } + + /** + * @brief Returns a value indicating whether log entries need to be generated + * Log entries may include hints which are expensive to compute. This method tells the + * matching algorithm whether to create such entries or not. + */ + virtual bool wants_log_entries () const { return false; } + /** * @brief Nets a and b match exactly */ diff --git a/src/db/db/dbNetlistCrossReference.cc b/src/db/db/dbNetlistCrossReference.cc index 6363323ec..65fc77923 100644 --- a/src/db/db/dbNetlistCrossReference.cc +++ b/src/db/db/dbNetlistCrossReference.cc @@ -416,6 +416,12 @@ NetlistCrossReference::gen_end_circuit (const db::Circuit *, const db::Circuit * mp_per_circuit_data = 0; } +void +NetlistCrossReference::gen_log_entry (Severity severity, const std::string &msg) +{ + mp_per_circuit_data->log_entries.push_back (LogEntryData (severity, msg)); +} + void NetlistCrossReference::gen_nets (const db::Net *a, const db::Net *b, Status status, const std::string &msg) { diff --git a/src/db/db/dbNetlistCrossReference.h b/src/db/db/dbNetlistCrossReference.h index 2ea8997a2..9a81519c5 100644 --- a/src/db/db/dbNetlistCrossReference.h +++ b/src/db/db/dbNetlistCrossReference.h @@ -105,6 +105,15 @@ public: std::string msg; }; + struct LogEntryData + { + LogEntryData (Severity s, const std::string &m) : severity (s), msg (m) { } + LogEntryData () : severity (NoSeverity) { } + + Severity severity; + std::string msg; + }; + struct PerCircuitData { PerCircuitData () : status (None) { } @@ -117,6 +126,8 @@ public: typedef pin_pairs_type::const_iterator pin_pairs_const_iterator; typedef std::vector subcircuit_pairs_type; typedef subcircuit_pairs_type::const_iterator subcircuit_pairs_const_iterator; + typedef std::vector log_entries_type; + typedef log_entries_type::const_iterator log_entries_const_iterator; Status status; std::string msg; @@ -124,6 +135,7 @@ public: device_pairs_type devices; pin_pairs_type pins; subcircuit_pairs_type subcircuits; + log_entries_type log_entries; }; struct PerNetData @@ -145,6 +157,7 @@ public: void gen_end_netlist (const db::Netlist *a, const db::Netlist *b); void gen_begin_circuit (const db::Circuit *a, const db::Circuit *b); void gen_end_circuit (const db::Circuit *a, const db::Circuit *b, Status status, const std::string &msg); + void gen_log_entry (Severity severity, const std::string &msg); void gen_nets (const db::Net *a, const db::Net *b, Status status, const std::string &msg); void gen_devices (const db::Device *a, const db::Device *b, Status status, const std::string &msg); void gen_pins (const db::Pin *a, const db::Pin *b, Status status, const std::string &msg); @@ -185,6 +198,11 @@ public: gen_end_circuit (a, b, Mismatch, msg); } + virtual void log_entry (Severity severity, const std::string &msg) + { + gen_log_entry (severity, msg); + } + virtual void match_nets (const db::Net *a, const db::Net *b) { gen_nets (a, b, Match, std::string ()); diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 1d1a13b7b..5edd6ed3c 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -21,6 +21,7 @@ */ #include "gsiDecl.h" +#include "gsiEnums.h" #include "dbNetlistCompare.h" namespace { @@ -33,7 +34,7 @@ class GenericNetlistCompareLogger { public: GenericNetlistCompareLogger () - : db::NetlistCompareLogger () + : db::NetlistCompareLogger (), m_wants_log (false) { // .. nothing yet .. } @@ -127,7 +128,7 @@ public: if (cb_circuit_mismatch.can_issue ()) { cb_circuit_mismatch.issue (&GenericNetlistCompareLogger::circuit_mismatch_fb, a, b, msg); } else { - db::NetlistCompareLogger::circuit_mismatch (a, b); + db::NetlistCompareLogger::circuit_mismatch (a, b, msg); } } @@ -136,6 +137,35 @@ public: db::NetlistCompareLogger::circuit_mismatch (a, b, msg); } + virtual void log_entry (db::NetlistCompareLogger::Severity severity, const std::string &msg) + { + if (cb_log_entry.can_issue ()) { + cb_log_entry.issue (&GenericNetlistCompareLogger::log_entry, severity, msg); + } else { + db::NetlistCompareLogger::log_entry (severity, msg); + } + } + + void log_entry_fb (db::NetlistCompareLogger::Severity severity, const std::string &msg) + { + db::NetlistCompareLogger::log_entry (severity, msg); + } + + virtual bool wants_log () const + { + return m_wants_log; + } + + bool get_wants_log () const + { + return m_wants_log; + } + + void set_wants_log (bool f) + { + m_wants_log = f; + } + virtual void match_nets (const db::Net *a, const db::Net *b) { if (cb_match_nets.can_issue ()) { @@ -297,8 +327,10 @@ public: gsi::Callback cb_end_circuit; gsi::Callback cb_circuit_skipped; gsi::Callback cb_match_nets; - gsi::Callback cb_net_mismatch; gsi::Callback cb_circuit_mismatch; + gsi::Callback cb_log_entry; + gsi::Callback cb_wants_log; + gsi::Callback cb_net_mismatch; gsi::Callback cb_match_ambiguous_nets; gsi::Callback cb_match_devices; gsi::Callback cb_match_devices_with_different_parameters; @@ -312,6 +344,8 @@ public: private: GenericNetlistCompareLogger (const GenericNetlistCompareLogger &d); GenericNetlistCompareLogger &operator= (const GenericNetlistCompareLogger &d); + + bool m_wants_log; }; } @@ -368,6 +402,26 @@ Class decl_GenericNetlistCompareLogger (decl_dbNetl "\n" "This method is called instead of \\begin_circuit and \\end_circuit." ) + + gsi::callback ("log_entry", &GenericNetlistCompareLogger::log_entry, &GenericNetlistCompareLogger::cb_log_entry, gsi::arg ("level"), gsi::arg ("msg"), + "@brief Issues an entry for the compare log.\n" + "This method delivers a log message generated during the compare of two circuits.\n" + "It is called between of \\begin_circuit and \\end_circuit and only if \\wants_log returns true.\n" + "\n" + "This method has been added in version 0.28." + ) + + gsi::method ("wants_log=", &GenericNetlistCompareLogger::set_wants_log, gsi::arg ("value"), + "@brief Sets a value indicating whether the receiver wants log messages.\n" + "Log messages may include compare hints which are expensive to compute. Hence, by default, log generation is turned off. " + "Set this attribute to true in order to receive log messages.\n" + "\n" + "This method has been added in version 0.28." + ) + + gsi::method ("wants_log", &GenericNetlistCompareLogger::get_wants_log, + "@brief Gets a value indicating whether the receiver wants log messages.\n" + "See \\wants_log= for details about this flag.\n" + "\n" + "This method has been added in version 0.28." + ) + gsi::callback ("match_nets", &GenericNetlistCompareLogger::match_nets, &GenericNetlistCompareLogger::cb_match_nets, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called when two nets are identified.\n" "If two nets are identified as a corresponding pair, this method will be called with both nets.\n" @@ -621,4 +675,21 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "This class has been introduced in version 0.26." ); +gsi::EnumIn decl_CompareLoggerSeverity ("db", "Severity", + gsi::enum_const ("NoSeverity", db::NetlistCompareLogger::NoSeverity, + "@brief Unspecific severity\n" + ) + + gsi::enum_const ("Info", db::NetlistCompareLogger::Info, + "@brief Information only\n" + ) + + gsi::enum_const ("Warning", db::NetlistCompareLogger::Warning, + "@brief A warning\n" + ) + + gsi::enum_const ("Error", db::NetlistCompareLogger::Error, + "@brief An error\n" + ), + "@brief This class represents the log severity level for \\GenericNetlistCompareLogger#log_entry.\n" + "This enum has been introduced in version 0.28." +); + }