diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 32614a4c6..cd4242493 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -177,7 +177,11 @@ SOURCES = \ dbNetlistReader.cc \ dbNetlistSpiceReader.cc \ gsiDeclDbNetlistCompare.cc \ - dbNetlistCrossReference.cc + dbNetlistCrossReference.cc \ + dbLayoutVsSchematicWriter.cc \ + dbLayoutVsSchematicReader.cc \ + dbLayoutVsSchematicFormatDefs.cc \ + dbLayoutVsSchematic.cc HEADERS = \ dbArray.h \ @@ -319,7 +323,11 @@ HEADERS = \ dbNetlistCompare.h \ dbNetlistReader.h \ dbNetlistSpiceReader.h \ - dbNetlistCrossReference.h + dbNetlistCrossReference.h \ + dbLayoutVsSchematicWriter.h \ + dbLayoutVsSchematicReader.h \ + dbLayoutVsSchematicFormatDefs.h \ + dbLayoutVsSchematic.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 00727568e..543245922 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -20,8 +20,8 @@ */ -#ifndef _HDR_dbLayout2Netlist -#define _HDR_dbLayout2Netlist +#ifndef _HDR_dbLayoutToNetlist +#define _HDR_dbLayoutToNetlist #include "dbCommon.h" #include "dbCellMapping.h" diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc index 8b781f315..97027d6c6 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.cc +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -50,6 +50,7 @@ namespace l2n_std_format template<> DB_PUBLIC const std::string keys::scale_key ("scale"); template<> DB_PUBLIC const std::string keys::pin_key ("pin"); + // A, B, C, D, E, G, I, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y template<> DB_PUBLIC const std::string keys::version_key ("V"); template<> DB_PUBLIC const std::string keys::description_key ("B"); template<> DB_PUBLIC const std::string keys::top_key ("W"); diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index 2a3a9faac..ab0a8e079 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -71,7 +71,8 @@ namespace db * pin( ) - outgoing pin connection [short key: P] * device( [combined-device]* [terminal-route]* [device-def]) * - device with connections [short key: D] - * circuit( [circuit-def]) - subcircuit with connections [short key: X] + * circuit( [subcircuit-def]) + * - subcircuit with connections [short key: X] * * [combined-device]: * diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index bb9cd39ec..47d56f2f4 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -28,15 +28,12 @@ namespace db namespace l2n_std_reader { -class Brace -{ -public: - Brace (LayoutToNetlistStandardReader *reader) : mp_reader (reader), m_checked (false) + Brace::Brace (db::LayoutToNetlistStandardReader *reader) : mp_reader (reader), m_checked (false) { m_has_brace = reader->test ("("); } - operator bool () + Brace::operator bool () { if (! m_has_brace) { m_checked = true; @@ -49,7 +46,7 @@ public: } } - void done () + void Brace::done () { if (m_has_brace && ! m_checked) { mp_reader->expect (")"); @@ -57,12 +54,6 @@ public: } } -private: - LayoutToNetlistStandardReader *mp_reader; - bool m_checked; - bool m_has_brace; -}; - } typedef l2n_std_format::keys skeys; @@ -153,7 +144,7 @@ static db::Region &layer_by_name (db::LayoutToNetlist *l2n, const std::string &n return *l; } -void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) +void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n, bool nested) { int version = 0; std::string description; @@ -350,6 +341,10 @@ void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) br.done (); + } else if (nested) { + break; + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword"))); } } @@ -482,8 +477,8 @@ LayoutToNetlistStandardReader::read_pin (db::LayoutToNetlist * /*l2n*/, db::Circ circuit->connect_pin (pin.id (), net); } -static size_t -terminal_id (const db::DeviceClass *device_class, const std::string &tname) +size_t +LayoutToNetlistStandardReader::terminal_id (const db::DeviceClass *device_class, const std::string &tname) { const std::vector &td = device_class->terminal_definitions (); for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { @@ -495,8 +490,8 @@ terminal_id (const db::DeviceClass *device_class, const std::string &tname) throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + tname + tl::to_string (tr (" for device class: ")) + device_class->name ()); } -static db::DeviceAbstract * -device_model_by_name (db::Netlist *netlist, const std::string &dmname) +db::DeviceAbstract * +LayoutToNetlistStandardReader::device_model_by_name (db::Netlist *netlist, const std::string &dmname) { for (db::Netlist::device_abstract_iterator i = netlist->begin_device_abstracts (); i != netlist->end_device_abstracts (); ++i) { if (i->name () == dmname) { diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index ad1d43b21..88965b764 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -31,9 +31,24 @@ namespace db { +class LayoutToNetlistStandardReader; + namespace l2n_std_reader { - class Layers; - class Brace; + + class Brace + { + public: + Brace (db::LayoutToNetlistStandardReader *reader); + + operator bool (); + void done (); + + private: + db::LayoutToNetlistStandardReader *mp_reader; + bool m_checked; + bool m_has_brace; + }; + } class LayoutToNetlist; @@ -67,10 +82,15 @@ public: void read (db::LayoutToNetlist *l2n); -private: +protected: friend class l2n_std_reader::Brace; typedef l2n_std_reader::Brace Brace; - typedef l2n_std_reader::Layers Layers; + + void do_read (db::LayoutToNetlist *l2n, bool nested = false); + static size_t terminal_id (const db::DeviceClass *device_class, const std::string &tname); + static db::DeviceAbstract *device_model_by_name (db::Netlist *netlist, const std::string &dmname); + tl::TextInputStream &stream (); + const std::string &path () const; struct Connections { @@ -81,14 +101,6 @@ private: size_t from_cluster, to_cluster; }; - tl::TextInputStream m_stream; - std::string m_path; - std::string m_line; - tl::Extractor m_ex; - db::Point m_ref; - - void do_read (db::LayoutToNetlist *l2n); - bool test (const std::string &token); void expect (const std::string &token); void read_word_or_quoted(std::string &s); @@ -106,6 +118,13 @@ private: std::pair read_geometry (db::LayoutToNetlist *l2n); void read_geometries (Brace &br, db::LayoutToNetlist *l2n, db::local_cluster &lc, db::Cell &cell); db::Point read_point (); + +private: + tl::TextInputStream m_stream; + std::string m_path; + std::string m_line; + tl::Extractor m_ex; + db::Point m_ref; }; } diff --git a/src/db/db/dbLayoutVsSchematic.cc b/src/db/db/dbLayoutVsSchematic.cc new file mode 100644 index 000000000..1c61e153f --- /dev/null +++ b/src/db/db/dbLayoutVsSchematic.cc @@ -0,0 +1,106 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbCommon.h" +#include "dbLayoutVsSchematic.h" +#include "dbLayoutVsSchematicWriter.h" +#include "dbLayoutVsSchematicReader.h" + +namespace db +{ + +LayoutVsSchematic::LayoutVsSchematic (const db::RecursiveShapeIterator &iter) + : LayoutToNetlist (iter) +{ + // .. nothing yet .. +} + +LayoutVsSchematic::LayoutVsSchematic (db::DeepShapeStore *dss, unsigned int layout_index) + : LayoutToNetlist (dss, layout_index) +{ + // .. nothing yet .. +} + +LayoutVsSchematic::LayoutVsSchematic (const std::string &topcell_name, double dbu) + : LayoutToNetlist (topcell_name, dbu) +{ + // .. nothing yet .. +} + +LayoutVsSchematic::LayoutVsSchematic () + : LayoutToNetlist () +{ + // .. nothing yet .. +} + +~LayoutVsSchematic () +{ + // .. nothing yet .. +} + +void LayoutVsSchematic::set_reference_netlist (db::Netlist *ref_netlist) +{ + mp_reference_netlist.reset (ref_netlist); + mp_cross_ref.reset (0); +} + +bool LayoutVsSchematic::compare_netlists (db::NetlistComparer *compare) +{ + if (! netlist ()) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + if (! reference_netlist ()) { + throw tl::Exception (tl::to_string (tr ("The reference netlist has not been set yet"))); + } + + return compare->compare (netlist (), reference_netlist (), make_cross_ref ()); +} + +db::NetlistCrossReference *LayoutVsSchematic::make_cross_ref () +{ + if (! mp_cross_ref.get ()) { + mp_cross_ref.reset (new db::NetlistCrossReference ()); + } + return mp_cross_ref.get (); +} + + +void db::LayoutVsSchematic::save (const std::string &path, bool short_format) +{ + tl::OutputStream stream (path); + db::LayoutVsSchematicStandardWriter writer (stream, short_format); + set_filename (path); + writer.write (this); +} + +void db::LayoutVsSchematic::load (const std::string &path) +{ + tl::InputStream stream (path); + db::LayoutVsSchematicStandardReader reader (stream); + set_filename (path); + set_name (stream.filename ()); + reader.read (this); +} + +} diff --git a/src/db/db/dbLayoutVsSchematic.h b/src/db/db/dbLayoutVsSchematic.h new file mode 100644 index 000000000..20184463e --- /dev/null +++ b/src/db/db/dbLayoutVsSchematic.h @@ -0,0 +1,187 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbLayoutVsSchematic +#define _HDR_dbLayoutVsSchematic + +#include "dbCommon.h" +#include "dbLayoutToNetlist.h" +#include "dbNetlistCompare.h" +#include "dbNetlistCrossReference.h" + +namespace db +{ + +/** + * @brief An extension of the LayoutToNetlist framework towards comparision vs. schematic + * + * This aggregate holds the following entities in addition to the ones provided by + * the LayoutToNetlist entity: + * + * * A reference netlist + * * A cross-reference object + * + * The cross-reference object connects the extracted netlist with the reference netlist. + * + * In addition to the steps required to create a LayoutToNetlist object, the following + * has to be provided: + * + * * A reference netlist has to be loaded using "set_reference_netlist" + * * Netlist comparison has to be performed using the NetlistCompare object provided. + * This will establish the cross-reference between the two netlists. + */ +class DB_PUBLIC LayoutVsSchematic + : public db::LayoutToNetlist +{ +public: + /** + * @brief The constructor + * + * See the LayoutToNetlist for details. + */ + LayoutVsSchematic (const db::RecursiveShapeIterator &iter); + + /** + * @brief Alternative constructor using an external deep shape storage + * + * See the LayoutToNetlist for details. + */ + LayoutVsSchematic (db::DeepShapeStore *dss, unsigned int layout_index = 0); + + /** + * @brief Alternative constructor for flat mode + * + * See the LayoutToNetlist for details. + */ + LayoutVsSchematic (const std::string &topcell_name, double dbu); + + /** + * @brief The default constructor + */ + LayoutVsSchematic (); + + /** + * @brief The destructor + */ + ~LayoutVsSchematic (); + + /** + * @brief Sets the reference netlist + * + * This will establish the reference netlist for the comparison. + * The LayoutVsSchematic object will take ownership over the netlist + * object. + * + * Setting the reference netlist will reset the cross-reference + * object. + */ + void set_reference_netlist (db::Netlist *ref_netlist); + + /** + * @brief Gets the reference netlist + */ + const db::Netlist *reference_netlist () const + { + return mp_reference_netlist.get (); + } + + /** + * @brief Gets the reference netlist (non-const version) + */ + db::Netlist *reference_netlist () + { + return mp_reference_netlist.get (); + } + + /** + * @brief Performs the comparison + */ + bool compare_netlists(NetlistComparer *compare); + + /** + * @brief Gets the cross-reference object + * + * This reference is 0 if the netlist compare has not been performed yet. + */ + const db::NetlistCrossReference *cross_ref () const + { + return mp_cross_ref.get (); + } + + /** + * @brief Gets the cross-reference object (non-const version) + * + * This reference is 0 if the netlist compare has not been performed yet. + */ + db::NetlistCrossReference *cross_ref () + { + return mp_cross_ref.get (); + } + + /** + * @brief Creates the cross-reference object if it isn't created yet + * + * This method is provided for special purposes such as the reader. + */ + db::NetlistCrossReference *make_cross_ref (); + + /** + * @brief Saves the database to the given path + * + * Currently, the internal format will be used. If "short_format" is true, the short version + * of the format is used. + * + * This is a convenience method. The low-level functionality is the LayoutVsSchematicWriter. + */ + void save (const std::string &path, bool short_format); + + /** + * @brief Loads the database from the given path + * + * This is a convenience method. The low-level functionality is the LayoutVsSchematicReader. + */ + void load (const std::string &path); + +private: + // no copying + LayoutVsSchematic (const db::LayoutVsSchematic &other); + LayoutVsSchematic &operator= (const db::LayoutVsSchematic &other); + + tl::shared_ptr mp_reference_netlist; + tl::shared_ptr mp_cross_ref; +}; + +} + +namespace tl +{ + +template<> struct type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbLayoutVsSchematicFormatDefs.cc b/src/db/db/dbLayoutVsSchematicFormatDefs.cc new file mode 100644 index 000000000..22557a490 --- /dev/null +++ b/src/db/db/dbLayoutVsSchematicFormatDefs.cc @@ -0,0 +1,51 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutVsSchematicFormatDefs.h" + +namespace db +{ + +namespace lvs_std_format +{ + template<> DB_PUBLIC const std::string keys::reference_key ("reference"); + template<> DB_PUBLIC const std::string keys::layout_key ("layout"); + template<> DB_PUBLIC const std::string keys::xref_key ("xref"); + + template<> DB_PUBLIC const std::string keys::mismatch_key ("mismatch"); + template<> DB_PUBLIC const std::string keys::match_key ("match"); + template<> DB_PUBLIC const std::string keys::nomatch_key ("nomatch"); + template<> DB_PUBLIC const std::string keys::warning_key ("warning"); + template<> DB_PUBLIC const std::string keys::skipped_key ("skipped"); + + template<> DB_PUBLIC const std::string keys::reference_key ("H"); + template<> DB_PUBLIC const std::string keys::layout_key ("J"); + template<> DB_PUBLIC const std::string keys::xref_key ("Z"); + + template<> DB_PUBLIC const std::string keys::mismatch_key ("0"); + template<> DB_PUBLIC const std::string keys::match_key ("1"); + template<> DB_PUBLIC const std::string keys::nomatch_key ("X"); + template<> DB_PUBLIC const std::string keys::warning_key ("W"); + template<> DB_PUBLIC const std::string keys::skipped_key ("S"); +} + +} diff --git a/src/db/db/dbLayoutVsSchematicFormatDefs.h b/src/db/db/dbLayoutVsSchematicFormatDefs.h new file mode 100644 index 000000000..de47bc1ba --- /dev/null +++ b/src/db/db/dbLayoutVsSchematicFormatDefs.h @@ -0,0 +1,139 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutVsSchematicFormatDefs +#define HDR_dbLayoutVsSchematicFormatDefs + +#include "dbCommon.h" +#include "dbLayoutToNetlistFormatDefs.h" + +#include + +namespace db +{ + +/** + * This is the internal persistency format for LayoutVsSchematic + * + * It's intentionally *not* XML to keep the overhead low. + * + * Comments are introduced by hash: # ... + * Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes. + * Escape character is backslash. + * Separator is either , or whitespace. Keywords and names are case sensitive. + * Short keys are provided for compacter representation. Short keys can be + * non-alpha (e.g. "*") or empty. + * Single-valued attributes can be given without brackets. + * All dimensions are in units of database unit. + * The file follows the declaration-before-use principle + * (circuits before subcircuits, nets before use ...) + * + * Global statements: + * + * version() - file format version [short key: V] + * description() - an arbitrary description text [short key: B] + * layout([layout]) - layout part [short key: J] + * reference([reference-def]*) - reference netlist part [short key: H] + * xref([xref-def]*) - cross-reference part [short key: Z] + * + * [layout]: + * + * ... - the LayoutToNetlist dump without version and description + * + * [reference-def]: + * + * circuit( [netlist-circuit-def]*) + * - circuit [short key: X] + * [netlist-circuit-def]: + * + * net( [net-name]?) - a net declaration [short key: N] + * pin( ) - outgoing pin connection [short key: P] + * device( [device-def]*) - device with connections [short key: D] + * circuit( [subcircuit-def]*) + * - subcircuit with connections [short key: X] + * + * [net-name]: + * + * name() - specify net name [short key: I] + * + * [device-def]: + * + * terminal( ) + * - specifies connection of the terminal with + * a net [short key: T] + * + * [subcircuit-def]: + * + * pin( ) - specifies connection of the pin with a net [short key: P] + * + * [xref-def]: + * + * circuit([non] [non] [status]? [circuit-xrefs]) + * - circuit pair [short key: X] + * + * [circuit-xrefs]: + * + * xref([pair]*) + * + * [pair] + * + * pin([non] [non] [status]?) - a pin pair [short key: P] + * device([non] [non] [status]?) - a device pair [short key: D] + * circuit([non] [non] [status]?) - a subcircuit pair [short key: X] + * net([non] [non] [status]?) - a net pair [short key: N] + * + * [non] + * + * | () + * + * [status] + * + * mismatch | - [short key: 0] + * match | - [short key: 1] + * nomatch | - [short key: X] + * warning | - [short key: W] + * skipped - [short key: S] + */ + +namespace lvs_std_format +{ + template + struct DB_PUBLIC keys + : public l2n_std_format::keys + { + static const std::string reference_key; + static const std::string layout_key; + static const std::string xref_key; + + static const std::string mismatch_key; + static const std::string match_key; + static const std::string nomatch_key; + static const std::string warning_key; + static const std::string skipped_key; + + inline static bool is_short () { return Short; } + }; +} + +} + +#endif diff --git a/src/db/db/dbLayoutVsSchematicReader.cc b/src/db/db/dbLayoutVsSchematicReader.cc new file mode 100644 index 000000000..fb6bda526 --- /dev/null +++ b/src/db/db/dbLayoutVsSchematicReader.cc @@ -0,0 +1,548 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutVsSchematicReader.h" +#include "dbLayoutVsSchematicFormatDefs.h" + +namespace db +{ + +typedef lvs_std_format::keys skeys; +typedef lvs_std_format::keys lkeys; + +LayoutVsSchematicStandardReader::LayoutVsSchematicStandardReader (tl::InputStream &stream) + : LayoutToNetlistStandardReader (stream) +{ + // .. nothing yet .. +} + +void LayoutVsSchematicStandardReader::read_lvs (db::LayoutVsSchematic *l2n) +{ + try { + do_read (l2n); + } catch (tl::Exception &ex) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("%s in line: %d of %s")), ex.msg (), stream ().line_number (), path ())); + } +} + +void LayoutVsSchematicStandardReader::do_read (db::LayoutVsSchematic *lvs) +{ + int version = 0; + std::string description; + + tl_assert (lvs->internal_layout ()); + lvs->internal_layout ()->dbu (1.0); // mainly for testing + + if (lvs->internal_layout ()->cells () == 0) { + lvs->internal_layout ()->add_cell ("TOP"); + } + tl_assert (lvs->internal_top_cell () != 0); + + lvs->make_netlist (); + + while (! at_end ()) { + + if (test (skeys::version_key) || test (lkeys::version_key)) { + + Brace br (this); + version = read_int (); + br.done (); + + } else if (test (skeys::description_key) || test (lkeys::description_key)) { + + Brace br (this); + read_word_or_quoted (description); + br.done (); + + } else if (test (skeys::layout_key) || test (lkeys::layout_key)) { + + Brace br (this); + LayoutToNetlistStandardReader::do_read (lvs, true /*nested*/); + br.done (); + + } else if (test (skeys::reference_key) || test (lkeys::reference_key)) { + + Brace br (this); + std::auto_ptr netlist (new db::Netlist ()); + read_netlist (netlist.get ()); + lvs->set_reference_netlist (netlist.release ()); + br.done (); + + } else if (test (skeys::xref_key) || test (lkeys::xref_key)) { + + if (! lvs->reference_netlist () || ! lvs->netlist ()) { + throw tl::Exception (tl::to_string (tr ("xref section before reference or layout netlist"))); + } + + db::NetlistCrossReference *xref = lvs->make_cross_ref (); + xref->gen_begin_netlist (lvs->netlist (), lvs->reference_netlist ()); + read_xref (xref); + xref->gen_end_netlist (lvs->netlist (), lvs->reference_netlist ()); + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword"))); + } + + } +} + +void LayoutVsSchematicStandardReader::read_netlist (db::Netlist *netlist) +{ + Brace br (this); + while (br) { + + if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + + Brace br (this); + std::string name; + read_word_or_quoted (name); + + db::Circuit *circuit = new db::Circuit (); + circuit->set_name (name); + netlist->add_circuit (circuit); + + std::map id2net; + + while (br) { + + if (test (skeys::net_key) || test (lkeys::net_key)) { + read_net (netlist, circuit, id2net); + } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { + read_pin (netlist, circuit, id2net); + } else if (test (skeys::device_key) || test (lkeys::device_key)) { + read_device (netlist, circuit, id2net); + } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + read_subcircuit (netlist, circuit, id2net); + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)"))); + } + + } + br.done (); + + } + + } + br.done (); +} + +void +LayoutVsSchematicStandardReader::read_net (db::Netlist * /*netlist*/, db::Circuit *circuit, std::map &id2net) +{ + Brace br (this); + + unsigned int id = (unsigned int) read_int (); + std::string name; + + if (test (skeys::name_key) || test (lkeys::name_key)) { + Brace br_name (this); + read_word_or_quoted (name); + br_name.done (); + } + + db::Net *net = new db::Net (); + net->set_name (name); + circuit->add_net (net); + + id2net.insert (std::make_pair (id, net)); + + br.done (); +} + +void +LayoutVsSchematicStandardReader::read_pin (db::Netlist * /*netlist*/, db::Circuit *circuit, std::map &id2net) +{ + Brace br (this); + std::string name; + read_word_or_quoted (name); + unsigned int netid = (unsigned int) read_int (); + br.done (); + + const db::Pin &pin = circuit->add_pin (name); + + db::Net *net = id2net [netid]; + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); + } + + circuit->connect_pin (pin.id (), net); +} + + +void +LayoutVsSchematicStandardReader::read_device (db::Netlist *netlist, db::Circuit *circuit, std::map &id2net) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + std::string dmname; + read_word_or_quoted (dmname); + + db::DeviceAbstract *dm = device_model_by_name (netlist, dmname); + + db::Device *device = new db::Device (); + device->set_device_class (const_cast (dm->device_class ())); + device->set_device_abstract (dm); + device->set_name (name); + circuit->add_device (device); + + size_t max_tid = 0; + + while (br) { + + if (test (skeys::terminal_key) || test (lkeys::terminal_key)) { + + Brace br2 (this); + std::string tname; + read_word_or_quoted (tname); + unsigned int netid = (unsigned int) read_int (); + br2.done (); + + size_t tid = terminal_id (dm->device_class (), tname); + max_tid = std::max (max_tid, tid + 1); + + db::Net *net = id2net [netid]; + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); + } + + device->connect_terminal (tid, net); + + } else if (test (skeys::param_key) || test (lkeys::param_key)) { + + Brace br2 (this); + std::string pname; + read_word_or_quoted (pname); + double value = read_double (); + br2.done (); + + size_t pid = std::numeric_limits::max (); + const std::vector &pd = dm->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p->name () == pname) { + pid = p->id (); + break; + } + } + + // if no parameter with this name exists, create one + if (pid == std::numeric_limits::max ()) { + // TODO: this should only happen for generic devices + db::DeviceClass *dc = const_cast (dm->device_class ()); + pid = dc->add_parameter_definition (db::DeviceParameterDefinition (pname, std::string ())).id (); + } + + device->set_parameter_value (pid, value); + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device definition (location, param or terminal expected)"))); + } + + } + + br.done (); +} + +void +LayoutVsSchematicStandardReader::read_subcircuit (db::Netlist *netlist, db::Circuit *circuit, std::map &id2net) +{ + Brace br (this); + + std::string name; + read_word_or_quoted (name); + + std::string xname; + read_word_or_quoted (xname); + + db::Circuit *circuit_ref = netlist->circuit_by_name (xname); + if (! circuit_ref) { + throw tl::Exception (tl::to_string (tr ("Not a valid device circuit name: ")) + xname); + } + + db::SubCircuit *subcircuit = new db::SubCircuit (circuit_ref); + subcircuit->set_name (name); + circuit->add_subcircuit (subcircuit); + + while (br) { + + if (test (skeys::pin_key) || test (lkeys::pin_key)) { + + Brace br2 (this); + std::string pname; + read_word_or_quoted (pname); + unsigned int netid = (unsigned int) read_int (); + br2.done (); + + const db::Pin *sc_pin = circuit_ref->pin_by_name (pname); + if (! sc_pin) { + throw tl::Exception (tl::to_string (tr ("Not a valid pin name: ")) + pname + tl::to_string (tr (" for circuit: ")) + circuit_ref->name ()); + } + + db::Net *net = id2net [netid]; + if (!net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid)); + } + + subcircuit->connect_pin (sc_pin->id (), net); + + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside subcircuit definition (location, rotation, mirror, scale or pin expected)"))); + } + + } + + br.done (); +} + +bool LayoutVsSchematicStandardReader::read_status (db::NetlistCrossReference::Status &status) +{ + if (test (skeys::match_key) || test (lkeys::match_key)) { + status = db::NetlistCrossReference::Match; + return true; + } else if (test (skeys::nomatch_key) || test (lkeys::nomatch_key)) { + status = db::NetlistCrossReference::NoMatch; + return true; + } else if (test (skeys::mismatch_key) || test (lkeys::mismatch_key)) { + status = db::NetlistCrossReference::Mismatch; + return true; + } else if (test (skeys::warning_key) || test (lkeys::warning_key)) { + status = db::NetlistCrossReference::MatchWithWarning; + return true; + } else if (test (skeys::skipped_key) || test (lkeys::skipped_key)) { + status = db::NetlistCrossReference::Skipped; + return true; + } else { + return false; + } +} + +void LayoutVsSchematicStandardReader::read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b) +{ + Brace br (this); + while (br) { + + if (test (skeys::net_key) || test (lkeys::net_key)) { + read_net_pair (xref, circuit_a, circuit_b); + } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { + read_pin_pair (xref, circuit_a, circuit_b); + } else if (test (skeys::device_key) || test (lkeys::device_key)) { + read_device_pair (xref, circuit_a, circuit_b); + } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + read_subcircuit_pair (xref, circuit_a, circuit_b); + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)"))); + } + + } + br.done (); +} + +void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref) +{ + Brace br (this); + while (br) { + + if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { + + Brace br (this); + + std::string name_a, name_b; + read_word_or_quoted (name_a); + read_word_or_quoted (name_b); + + const db::Circuit *circuit_a = xref->netlist_a ()->circuit_by_name (name_a); + if (! circuit_a) { + throw tl::Exception (tl::to_string (tr ("Not a valid circuit name: ")) + name_a); + } + + const db::Circuit *circuit_b = xref->netlist_b ()->circuit_by_name (name_b); + if (! circuit_b) { + throw tl::Exception (tl::to_string (tr ("Not a valid circuit name: ")) + name_b); + } + + xref->gen_begin_circuit (circuit_a, circuit_b); + + db::NetlistCrossReference::Status status = db::NetlistCrossReference::None; + + while (br) { + + if (read_status (status)) { + // continue + } else if (test (skeys::xref_key) || test (lkeys::xref_key)) { + read_xrefs_for_circuits (xref, circuit_a, circuit_b); + } else { + throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (status keyword of xrefs expected)"))); + } + + } + + xref->gen_end_circuit (circuit_a, circuit_b, status); + + br.done (); + + } + + } + br.done (); +} + +std::pair LayoutVsSchematicStandardReader::read_non_string () +{ + if (test ("(")) { + expect (")"); + return std::make_pair (std::string (), false); + } else { + std::string s; + read_word_or_quoted (s); + return std::make_pair (s, true); + } +} + +std::pair LayoutVsSchematicStandardReader::read_non_numerical () +{ + if (test ("(")) { + expect (")"); + return std::make_pair (0, false); + } else { + return std::make_pair ((unsigned int) read_int (), true); + } +} + +static const db::Net *net_by_numerical_id (const db::Circuit *circuit, const std::pair &non) +{ + if (non.second && circuit) { + const db::Net *net = 0; // @@@ circuit->net_by_numerical_id (non.first); + if (! net) { + throw tl::Exception (tl::to_string (tr ("Not a valid net id: ")) + tl::to_string (non.first)); + } + return net; + } else { + return 0; + } +} + +void LayoutVsSchematicStandardReader::read_net_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b) +{ + Brace br (this); + + std::pair non_a, non_b; + non_a = read_non_numerical (); + non_b = read_non_numerical (); + + db::NetlistCrossReference::Status status = db::NetlistCrossReference::None; + read_status (status); + + br.done (); + + xref->gen_nets (net_by_numerical_id (circuit_a, non_a), net_by_numerical_id (circuit_b, non_b), status); +} + +static const db::Pin *pin_by_name (const db::Circuit *circuit, const std::pair &non) +{ + if (non.second && circuit) { + const db::Pin *pin = circuit->pin_by_name (non.first); + if (! pin) { + throw tl::Exception (tl::to_string (tr ("Not a valid pin name: ")) + non.first); + } + return pin; + } else { + return 0; + } +} + +void LayoutVsSchematicStandardReader::read_pin_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b) +{ + Brace br (this); + + std::pair non_a, non_b; + non_a = read_non_string (); + non_b = read_non_string (); + + db::NetlistCrossReference::Status status = db::NetlistCrossReference::None; + read_status (status); + + br.done (); + + xref->gen_pins (pin_by_name (circuit_a, non_a), pin_by_name (circuit_b, non_b), status); +} + +static const db::Device *device_by_name (const db::Circuit *circuit, const std::pair &non) +{ + if (non.second && circuit) { + const db::Device *device = circuit->device_by_name (non.first); + if (! device) { + throw tl::Exception (tl::to_string (tr ("Not a valid device name: ")) + non.first); + } + return device; + } else { + return 0; + } +} + +void LayoutVsSchematicStandardReader::read_device_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b) +{ + Brace br (this); + + std::pair non_a, non_b; + non_a = read_non_string (); + non_b = read_non_string (); + + db::NetlistCrossReference::Status status = db::NetlistCrossReference::None; + read_status (status); + + br.done (); + + xref->gen_devices (device_by_name (circuit_a, non_a), device_by_name (circuit_b, non_b), status); +} + +static const db::SubCircuit *subcircuit_by_name (const db::Circuit *circuit, const std::pair &non) +{ + if (non.second && circuit) { + const db::SubCircuit *subcircuit = circuit->subcircuit_by_name (non.first); + if (! subcircuit) { + throw tl::Exception (tl::to_string (tr ("Not a valid subcircuit name: ")) + non.first); + } + return subcircuit; + } else { + return 0; + } +} + +void LayoutVsSchematicStandardReader::read_subcircuit_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b) +{ + Brace br (this); + + std::pair non_a, non_b; + non_a = read_non_string (); + non_b = read_non_string (); + + db::NetlistCrossReference::Status status = db::NetlistCrossReference::None; + read_status (status); + + br.done (); + + xref->gen_subcircuits (subcircuit_by_name (circuit_a, non_a), subcircuit_by_name (circuit_b, non_b), status); +} + +} diff --git a/src/db/db/dbLayoutVsSchematicReader.h b/src/db/db/dbLayoutVsSchematicReader.h new file mode 100644 index 000000000..66d8849ac --- /dev/null +++ b/src/db/db/dbLayoutVsSchematicReader.h @@ -0,0 +1,88 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutVsSchematicReader +#define HDR_dbLayoutVsSchematicReader + +#include "dbCommon.h" +#include "dbPolygon.h" +#include "dbCell.h" +#include "dbLayoutVsSchematic.h" +#include "dbLayoutToNetlistReader.h" +#include "tlStream.h" + +namespace db { + +class LayoutVsSchematic; +class Circuit; +class Cell; +class DeviceAbstract; +class DeviceClass; +class Net; +class Region; + +/** + * @brief The base class for a LayoutVsSchematic writer + */ +class DB_PUBLIC LayoutVsSchematicReaderBase +{ +public: + LayoutVsSchematicReaderBase () { } + virtual ~LayoutVsSchematicReaderBase () { } + + virtual void read_lvs (db::LayoutVsSchematic *lvs) = 0; +}; + +/** + * @brief The standard writer + */ +class DB_PUBLIC LayoutVsSchematicStandardReader + : public LayoutVsSchematicReaderBase, private LayoutToNetlistStandardReader +{ +public: + LayoutVsSchematicStandardReader (tl::InputStream &stream); + + void read_lvs (db::LayoutVsSchematic *lvs); + +private: + void do_read (db::LayoutVsSchematic *lvs); + + void read_netlist (db::Netlist *netlist); + bool read_status (db::NetlistCrossReference::Status &status); + void read_net (db::Netlist *netlist, db::Circuit *circuit, std::map &id2net); + void read_subcircuit (db::Netlist *netlist, db::Circuit *circuit, std::map &id2net); + void read_device (db::Netlist *netlist, db::Circuit *circuit, std::map &id2net); + void read_pin (db::Netlist *netlist, db::Circuit *circuit, std::map &id2net); + 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); + void read_pin_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b); + void read_device_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b); + void read_subcircuit_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b); + std::pair read_non_string (); + std::pair read_non_numerical (); +}; + +} + +#endif + diff --git a/src/db/db/dbLayoutVsSchematicWriter.cc b/src/db/db/dbLayoutVsSchematicWriter.cc new file mode 100644 index 000000000..9c12ddb9f --- /dev/null +++ b/src/db/db/dbLayoutVsSchematicWriter.cc @@ -0,0 +1,561 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbLayoutVsSchematicWriter.h" +#include "dbLayoutVsSchematic.h" +#include "dbLayoutVsSchematicFormatDefs.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------- +// LayoutVsSchematicWriterBase implementation + +LayoutVsSchematicWriterBase::LayoutVsSchematicWriterBase () +{ + // .. nothing yet .. +} + +LayoutVsSchematicWriterBase::~LayoutVsSchematicWriterBase () +{ + // .. nothing yet .. +} + +void LayoutVsSchematicWriterBase::write (const db::LayoutVsSchematic *l2n) +{ + do_write (l2n); +} + +// ------------------------------------------------------------------------------------------- + +namespace l2n_std_format +{ + +// ------------------------------------------------------------------------------------------- +// std_writer_impl implementation + +template +class std_writer_impl +{ +public: + std_writer_impl (tl::OutputStream &stream); + + void write (const db::LayoutVsSchematic *l2n); + +private: + tl::OutputStream *mp_stream; + db::Point m_ref; + + void write (const db::LayoutVsSchematic *l2n, const db::Circuit &circuit); + void write (const db::LayoutVsSchematic *l2n, const db::Net &net, unsigned int id); + void write (const db::LayoutVsSchematic *l2n, const db::SubCircuit &subcircuit, std::map &net2id); + void write (const db::LayoutVsSchematic *l2n, const db::Device &device, std::map &net2id); + void write (const db::LayoutVsSchematic *l2n, const db::DeviceAbstract &device_abstract); + void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname, bool relative); + void reset_geometry_ref (); +}; + +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) + : mp_stream (&stream) +{ + // .. nothing yet .. +} + +static std::string name_for_layer (const db::LayoutVsSchematic *l2n, unsigned int l) +{ + std::string n = l2n->name (l); + if (n.empty ()) { + n = "L" + tl::to_string (l); + } + return n; +} + +template +void std_writer_impl::write (const db::LayoutVsSchematic *l2n) +{ + bool any = false; + + const int version = 0; + + const db::Layout *ly = l2n->internal_layout (); + const db::Netlist *nl = l2n->netlist (); + + *mp_stream << "#%l2n-klayout" << endl; + + if (! Keys::is_short ()) { + *mp_stream << endl << "# General section" << endl << endl; + } + + if (version > 0) { + *mp_stream << Keys::version_key << "(" << version << ")" << endl; + } + *mp_stream << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; + *mp_stream << Keys::unit_key << "(" << ly->dbu () << ")" << endl; + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Layer section" << endl; + *mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl; + } + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Mask layers" << endl; + } + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + *mp_stream << Keys::layer_key << "(" << name_for_layer (l2n, *l); + db::LayerProperties lp = ly->get_properties (*l); + if (! lp.is_null ()) { + *mp_stream << " " << tl::to_word_or_quoted_string (lp.to_string ()); + } + *mp_stream << ")" << endl; + } + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Mask layer connectivity" << endl; + } + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + + db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l); + db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l); + if (cb != ce) { + *mp_stream << Keys::connect_key << "(" << name_for_layer (l2n, *l); + for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { + *mp_stream << " " << name_for_layer (l2n, *c); + } + *mp_stream << ")" << endl; + } + + } + + any = false; + for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { + + db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l); + db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l); + if (gb != ge) { + if (! any) { + if (! Keys::is_short ()) { + *mp_stream << endl << "# Global nets and connectivity" << endl; + } + any = true; + } + *mp_stream << Keys::global_key << "(" << name_for_layer (l2n, *l); + for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { + *mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g)); + } + *mp_stream << ")" << endl; + } + + } + + if (nl->begin_device_classes () != nl->end_device_classes () && ! Keys::is_short ()) { + *mp_stream << endl << "# Device class section" << endl; + for (db::Netlist::const_device_class_iterator c = nl->begin_device_classes (); c != nl->end_device_classes (); ++c) { + db::DeviceClassTemplateBase *temp = db::DeviceClassTemplateBase::is_a (c.operator-> ()); + if (temp) { + *mp_stream << Keys::class_key << "(" << tl::to_word_or_quoted_string (c->name ()) << " " << tl::to_word_or_quoted_string (temp->name ()) << ")" << endl; + } + } + } + + if (nl->begin_device_abstracts () != nl->end_device_abstracts () && ! Keys::is_short ()) { + *mp_stream << endl << "# Device abstracts section" << endl; + *mp_stream << "# Device abstracts list the pin shapes of the devices." << endl; + } + for (db::Netlist::const_abstract_model_iterator m = nl->begin_device_abstracts (); m != nl->end_device_abstracts (); ++m) { + if (m->device_class ()) { + *mp_stream << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl; + write (l2n, *m); + *mp_stream << ")" << endl; + } + } + + if (! Keys::is_short ()) { + *mp_stream << endl << "# Circuit section" << endl; + *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; + } + for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) { + const db::Circuit *x = *i; + *mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; + write (l2n, *x); + *mp_stream << ")" << endl; + } +} + +template +void std_writer_impl::write (const db::LayoutVsSchematic *l2n, const db::Circuit &circuit) +{ + std::map net2id; + unsigned int id = 0; + + if (circuit.begin_nets () != circuit.end_nets ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Nets with their geometries" << endl; + } + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + net2id.insert (std::make_pair (n.operator-> (), ++id)); + write (l2n, *n, id); + } + } + + if (circuit.begin_pins () != circuit.end_pins ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Outgoing pins and their connections to nets" << endl; + } + for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) { + const db::Net *net = circuit.net_for_pin (p->id ()); + if (net) { + *mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")" << endl; + } + } + } + + if (circuit.begin_devices () != circuit.end_devices ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Devices and their connections" << endl; + } + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { + write (l2n, *d, net2id); + } + } + + if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) { + if (! Keys::is_short ()) { + *mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl; + } + for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) { + write (l2n, *x, net2id); + } + } + + if (! Keys::is_short ()) { + *mp_stream << endl; + } +} + +void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref, bool relative) +{ + if (relative) { + + stream << "("; + stream << pt.x () - ref.x (); + stream << " "; + stream << pt.y () - ref.y (); + stream << ")"; + + } else { + + if (pt.x () == 0 || pt.x () != ref.x ()) { + stream << pt.x (); + } else { + stream << "*"; + } + + if (pt.y () == 0 || pt.y () != ref.y ()) { + stream << pt.y (); + } else { + stream << "*"; + } + + } + + ref = pt; +} + +template +void write_points (tl::OutputStream &stream, 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); + + } +} + +template +void std_writer_impl::reset_geometry_ref () +{ + m_ref = db::Point (); +} + +template +void std_writer_impl::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname, bool relative) +{ + db::ICplxTrans t = tr * db::ICplxTrans (s->trans ()); + + const db::Polygon &poly = s->obj (); + 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 << ")"; + + } else { + + *mp_stream << Keys::polygon_key << "(" << lname; + if (poly.holes () > 0) { + db::SimplePolygon sp (poly); + write_points (*mp_stream, sp, t, m_ref, relative); + } else { + write_points (*mp_stream, poly, t, m_ref, relative); + } + *mp_stream << ")"; + + } +} + +template +void std_writer_impl::write (const db::LayoutVsSchematic *l2n, const db::Net &net, unsigned int id) +{ + if (! l2n->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done"))); + } + + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Circuit *circuit = net.circuit (); + const db::Connectivity &conn = l2n->connectivity (); + + bool any = false; + + reset_geometry_ref (); + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + db::cell_index_type cci = circuit->cell_index (); + db::cell_index_type prev_ci = cci; + + for (db::recursive_cluster_shape_iterator si (clusters, *l, cci, net.cluster_id ()); ! si.at_end (); ) { + + // NOTE: we don't recursive into circuits which will later be output. However, as circuits may + // vanish in "purge" but the clusters will still be there we need to recursive into clusters from + // unknown cells. + db::cell_index_type ci = si.cell_index (); + if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_abstract_by_cell_index (ci))) { + + si.skip_cell (); + + } else { + + if (! any) { + *mp_stream << indent1 << Keys::net_key << "(" << id; + if (! net.name ().empty ()) { + *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + } + *mp_stream << endl; + any = true; + } + + *mp_stream << indent2; + write (si.operator-> (), si.trans (), name_for_layer (l2n, *l), true); + *mp_stream << endl; + + prev_ci = ci; + + ++si; + + } + + } + + } + + if (any) { + *mp_stream << indent1 << ")" << endl; + } else { + + *mp_stream << indent1 << Keys::net_key << "(" << id; + if (! net.name ().empty ()) { + *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; + } + *mp_stream << ")" << endl; + + } +} + +template +void std_writer_impl::write (const db::LayoutVsSchematic *l2n, const db::SubCircuit &subcircuit, std::map &net2id) +{ + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + + *mp_stream << indent1 << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ()); + *mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ()); + + const db::DCplxTrans &tr = subcircuit.trans (); + if (tr.is_mag ()) { + *mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")"; + } + if (tr.is_mirror ()) { + *mp_stream << " " << Keys::mirror_key; + } + if (fabs (tr.angle ()) > 1e-6) { + *mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")"; + } + *mp_stream << " " << Keys::location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")"; + + // each pin in one line for more than a few pins + bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1); + + if (separate_lines) { + *mp_stream << endl; + } + + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { + const db::Net *net = subcircuit.net_for_pin (p->id ()); + if (net) { + if (separate_lines) { + *mp_stream << indent2; + } else { + *mp_stream << " "; + } + *mp_stream << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")"; + if (separate_lines) { + *mp_stream << endl; + } + } + } + + if (separate_lines) { + *mp_stream << indent1; + } + + *mp_stream << ")" << endl; +} + +template +void std_writer_impl::write (const db::LayoutVsSchematic *l2n, const db::DeviceAbstract &device_abstract) +{ + const std::vector &td = device_abstract.device_class ()->terminal_definitions (); + + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Connectivity &conn = l2n->connectivity (); + + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + *mp_stream << indent1 << Keys::terminal_key << "(" << t->name () << endl; + + reset_geometry_ref (); + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + const db::local_cluster &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (device_abstract.cluster_id_for_terminal (t->id ())); + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + + *mp_stream << indent2; + write (s.operator-> (), db::ICplxTrans (), name_for_layer (l2n, *l), true); + *mp_stream << endl; + + } + + } + + *mp_stream << indent1 << ")" << endl; + + } +} + +template +void std_writer_impl::write (const db::LayoutVsSchematic *l2n, const db::Device &device, std::map &net2id) +{ + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + db::VCplxTrans dbu_inv (1.0 / dbu); + + 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 << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); + + tl_assert (device.device_abstract () != 0); + *mp_stream << " " << 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) { + + db::Vector pos = dbu_inv * a->offset; + + *mp_stream << " " << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " " << pos.x () << " " << pos.y () << ")" << endl; + + } + + const std::map > &reconnected_terminals = device.reconnected_terminals (); + for (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 << " " << 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; + } + + } + + db::Point pos = dbu_inv * device.position (); + + *mp_stream << indent2 << Keys::location_key << "(" << pos.x () << " " << pos.y () << ")" << endl; + + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + *mp_stream << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl; + } + + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + const db::Net *net = device.net_for_terminal (i->id ()); + if (net) { + *mp_stream << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl; + } + } + + *mp_stream << indent1 << ")" << endl; +} + +} + +// ------------------------------------------------------------------------------------------- +// LayoutVsSchematicStandardWriter implementation + +LayoutVsSchematicStandardWriter::LayoutVsSchematicStandardWriter (tl::OutputStream &stream, bool short_version) + : mp_stream (&stream), m_short_version (short_version) +{ + // .. nothing yet .. +} + +void LayoutVsSchematicStandardWriter::do_write (const db::LayoutVsSchematic *l2n) +{ + if (m_short_version) { + l2n_std_format::std_writer_impl > writer (*mp_stream); + writer.write (l2n); + } else { + l2n_std_format::std_writer_impl > writer (*mp_stream); + writer.write (l2n); + } +} + +} diff --git a/src/db/db/dbLayoutVsSchematicWriter.h b/src/db/db/dbLayoutVsSchematicWriter.h new file mode 100644 index 000000000..4f4d01485 --- /dev/null +++ b/src/db/db/dbLayoutVsSchematicWriter.h @@ -0,0 +1,71 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbLayoutVsSchematicWriter +#define HDR_dbLayoutVsSchematicWriter + +#include "dbCommon.h" +#include "tlStream.h" + +namespace db +{ + +class LayoutVsSchematic; + +/** + * @brief The base class for a LayoutVsSchematic writer + */ +class DB_PUBLIC LayoutVsSchematicWriterBase +{ +public: + LayoutVsSchematicWriterBase (); + virtual ~LayoutVsSchematicWriterBase (); + + void write (const db::LayoutVsSchematic *lvs); + +protected: + virtual void do_write (const db::LayoutVsSchematic *lvs) = 0; + +private: + std::string m_filename; +}; + +/** + * @brief The standard writer + */ +class DB_PUBLIC LayoutVsSchematicStandardWriter + : public LayoutVsSchematicWriterBase +{ +public: + LayoutVsSchematicStandardWriter (tl::OutputStream &stream, bool short_version); + +protected: + void do_write (const db::LayoutVsSchematic *lvs); + +private: + tl::OutputStream *mp_stream; + bool m_short_version; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 801101980..e2890d0ec 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -1650,6 +1650,24 @@ NetlistComparer::same_circuits (const db::Circuit *ca, const db::Circuit *cb) mp_circuit_categorizer->same_circuit (ca, cb); } +bool +NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b, NetlistCompareLogger *logger) const +{ + db::NetlistCompareLogger *org_logger = mp_logger; + mp_logger = logger; + bool res = false; + + try { + res = compare (a, b); + } catch (...) { + mp_logger = org_logger; + throw; + } + + mp_logger = org_logger; + return res; +} + bool NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const { diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index 1de9af01c..a7d447892 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -158,7 +158,7 @@ public: /** * @brief Constructor */ - NetlistComparer (NetlistCompareLogger *logger); + NetlistComparer (NetlistCompareLogger *logger = 0); /** * @brief Mark two nets as identical @@ -261,12 +261,17 @@ public: */ bool compare (const db::Netlist *a, const db::Netlist *b) const; + /** + * @brief Actually compares the two netlists using the given logger + */ + bool compare (const db::Netlist *a, const db::Netlist *b, db::NetlistCompareLogger *logger) const; + protected: bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, db::CircuitPinMapper &circuit_pin_mapper, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const; bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; static void derive_pin_equivalence (const db::Circuit *ca, const db::Circuit *cb, CircuitPinMapper *circuit_pin_mapper); - NetlistCompareLogger *mp_logger; + mutable NetlistCompareLogger *mp_logger; std::map, std::vector > > m_same_nets; std::auto_ptr mp_circuit_pin_mapper; std::auto_ptr mp_device_categorizer; diff --git a/src/db/db/dbNetlistCrossReference.h b/src/db/db/dbNetlistCrossReference.h index 964e012a1..27cfa2978 100644 --- a/src/db/db/dbNetlistCrossReference.h +++ b/src/db/db/dbNetlistCrossReference.h @@ -269,6 +269,16 @@ public: const db::Net *other_net_for (const db::Net *net) const; const PerNetData *per_net_data_for (const std::pair &nets) const; + const db::Netlist *netlist_a () const + { + return mp_netlist_a.get (); + } + + const db::Netlist *netlist_b () const + { + return mp_netlist_b.get (); + } + private: tl::weak_ptr mp_netlist_a, mp_netlist_b; std::vector > m_circuits; diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 773b7cccc..8ee371663 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -429,7 +429,7 @@ Class decl_GenericNetlistCompareLogger ("db", "Gene static db::NetlistComparer *make_comparer0 () { - return new db::NetlistComparer (0); + return new db::NetlistComparer (); } static db::NetlistComparer *make_comparer1 (GenericNetlistCompareLogger *logger) @@ -509,11 +509,17 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "@brief Gets the maximum branch complexity\n" "See \\max_branch_complexity= for details." ) + - gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), + gsi::method ("compare", (bool (db::NetlistComparer::*) (const db::Netlist *, const db::Netlist *) const) &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), "@brief Compares two netlists.\n" "This method will perform the actual netlist compare. It will return true if both netlists are identical. " "If the comparer has been configured with \\same_nets or similar methods, the objects given there must " "be located inside 'circuit_a' and 'circuit_b' respectively." + ) + + gsi::method ("compare", (bool (db::NetlistComparer::*) (const db::Netlist *, const db::Netlist *, db::NetlistCompareLogger *) const) &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), gsi::arg ("logger"), + "@brief Compares two netlists.\n" + "This method will perform the actual netlist compare using the given logger. It will return true if both netlists are identical. " + "If the comparer has been configured with \\same_nets or similar methods, the objects given there must " + "be located inside 'circuit_a' and 'circuit_b' respectively." ), "@brief Compares two netlists\n" "This class performs a comparison of two netlists.\n"