mirror of https://github.com/KLayout/klayout.git
WIP: LVS DB structure, reader
This commit is contained in:
parent
d006d0c91e
commit
3a11951175
|
|
@ -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") {
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
*/
|
||||
|
||||
#ifndef _HDR_dbLayout2Netlist
|
||||
#define _HDR_dbLayout2Netlist
|
||||
#ifndef _HDR_dbLayoutToNetlist
|
||||
#define _HDR_dbLayoutToNetlist
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbCellMapping.h"
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ namespace l2n_std_format
|
|||
template<> DB_PUBLIC const std::string keys<false>::scale_key ("scale");
|
||||
template<> DB_PUBLIC const std::string keys<false>::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<true>::version_key ("V");
|
||||
template<> DB_PUBLIC const std::string keys<true>::description_key ("B");
|
||||
template<> DB_PUBLIC const std::string keys<true>::top_key ("W");
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ namespace db
|
|||
* pin(<name> <net-id>) - outgoing pin connection [short key: P]
|
||||
* device(<name> <abstract> [combined-device]* [terminal-route]* [device-def])
|
||||
* - device with connections [short key: D]
|
||||
* circuit(<name> [circuit-def]) - subcircuit with connections [short key: X]
|
||||
* circuit(<name> [subcircuit-def])
|
||||
* - subcircuit with connections [short key: X]
|
||||
*
|
||||
* [combined-device]:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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<true> 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<db::DeviceTerminalDefinition> &td = device_class->terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::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) {
|
||||
|
|
|
|||
|
|
@ -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<unsigned int, db::PolygonRef> read_geometry (db::LayoutToNetlist *l2n);
|
||||
void read_geometries (Brace &br, db::LayoutToNetlist *l2n, db::local_cluster<db::PolygonRef> &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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<db::Netlist> mp_reference_netlist;
|
||||
tl::shared_ptr<db::NetlistCrossReference> mp_cross_ref;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template<> struct type_traits<db::LayoutVsSchematic> : public tl::type_traits<void>
|
||||
{
|
||||
// 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
|
||||
|
|
@ -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<false>::reference_key ("reference");
|
||||
template<> DB_PUBLIC const std::string keys<false>::layout_key ("layout");
|
||||
template<> DB_PUBLIC const std::string keys<false>::xref_key ("xref");
|
||||
|
||||
template<> DB_PUBLIC const std::string keys<false>::mismatch_key ("mismatch");
|
||||
template<> DB_PUBLIC const std::string keys<false>::match_key ("match");
|
||||
template<> DB_PUBLIC const std::string keys<false>::nomatch_key ("nomatch");
|
||||
template<> DB_PUBLIC const std::string keys<false>::warning_key ("warning");
|
||||
template<> DB_PUBLIC const std::string keys<false>::skipped_key ("skipped");
|
||||
|
||||
template<> DB_PUBLIC const std::string keys<true>::reference_key ("H");
|
||||
template<> DB_PUBLIC const std::string keys<true>::layout_key ("J");
|
||||
template<> DB_PUBLIC const std::string keys<true>::xref_key ("Z");
|
||||
|
||||
template<> DB_PUBLIC const std::string keys<true>::mismatch_key ("0");
|
||||
template<> DB_PUBLIC const std::string keys<true>::match_key ("1");
|
||||
template<> DB_PUBLIC const std::string keys<true>::nomatch_key ("X");
|
||||
template<> DB_PUBLIC const std::string keys<true>::warning_key ("W");
|
||||
template<> DB_PUBLIC const std::string keys<true>::skipped_key ("S");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <string>
|
||||
|
||||
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(<number>) - file format version [short key: V]
|
||||
* description(<text>) - 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(<name> [netlist-circuit-def]*)
|
||||
* - circuit [short key: X]
|
||||
* [netlist-circuit-def]:
|
||||
*
|
||||
* net(<id> [net-name]?) - a net declaration [short key: N]
|
||||
* pin(<name> <net-id>) - outgoing pin connection [short key: P]
|
||||
* device(<name> [device-def]*) - device with connections [short key: D]
|
||||
* circuit(<name> [subcircuit-def]*)
|
||||
* - subcircuit with connections [short key: X]
|
||||
*
|
||||
* [net-name]:
|
||||
*
|
||||
* name(<net-name>) - specify net name [short key: I]
|
||||
*
|
||||
* [device-def]:
|
||||
*
|
||||
* terminal(<terminal-name> <net-id>)
|
||||
* - specifies connection of the terminal with
|
||||
* a net [short key: T]
|
||||
*
|
||||
* [subcircuit-def]:
|
||||
*
|
||||
* pin(<pin-name> <net-id>) - 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]
|
||||
*
|
||||
* <name> | ()
|
||||
*
|
||||
* [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 <bool Short>
|
||||
struct DB_PUBLIC keys
|
||||
: public l2n_std_format::keys<Short>
|
||||
{
|
||||
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
|
||||
|
|
@ -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<true> skeys;
|
||||
typedef lvs_std_format::keys<false> 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<db::Netlist> 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<unsigned int, Net *> 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<unsigned int, Net *> &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<unsigned int, Net *> &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<unsigned int, Net *> &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<db::DeviceClass *> (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<size_t>::max ();
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = dm->device_class ()->parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::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<size_t>::max ()) {
|
||||
// TODO: this should only happen for generic devices
|
||||
db::DeviceClass *dc = const_cast<db::DeviceClass *> (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<unsigned int, Net *> &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<std::string, bool> 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<unsigned int, bool> 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<unsigned int, bool> &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<unsigned int, bool> 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<std::string, bool> &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<std::string, bool> 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<std::string, bool> &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<std::string, bool> 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<std::string, bool> &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<std::string, bool> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<unsigned int, Net *> &id2net);
|
||||
void read_subcircuit (db::Netlist *netlist, db::Circuit *circuit, std::map<unsigned int, Net *> &id2net);
|
||||
void read_device (db::Netlist *netlist, db::Circuit *circuit, std::map<unsigned int, Net *> &id2net);
|
||||
void read_pin (db::Netlist *netlist, db::Circuit *circuit, std::map<unsigned int, Net *> &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<std::string, bool> read_non_string ();
|
||||
std::pair<unsigned int, bool> read_non_numerical ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -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<Keys> implementation
|
||||
|
||||
template <class Keys>
|
||||
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<const Net *, unsigned int> &net2id);
|
||||
void write (const db::LayoutVsSchematic *l2n, const db::Device &device, std::map<const Net *, unsigned int> &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 <class Keys>
|
||||
std_writer_impl<Keys>::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 <class Keys>
|
||||
void std_writer_impl<Keys>::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 <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutVsSchematic *l2n, const db::Circuit &circuit)
|
||||
{
|
||||
std::map<const db::Net *, unsigned int> 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 <class T, class Tr>
|
||||
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 <class Keys>
|
||||
void std_writer_impl<Keys>::reset_geometry_ref ()
|
||||
{
|
||||
m_ref = db::Point ();
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::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 <class Keys>
|
||||
void std_writer_impl<Keys>::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<db::PolygonRef> &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<db::PolygonRef> 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 <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutVsSchematic *l2n, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &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 <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutVsSchematic *l2n, const db::DeviceAbstract &device_abstract)
|
||||
{
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = device_abstract.device_class ()->terminal_definitions ();
|
||||
|
||||
const db::hier_clusters<db::PolygonRef> &clusters = l2n->net_clusters ();
|
||||
const db::Connectivity &conn = l2n->connectivity ();
|
||||
|
||||
for (std::vector<db::DeviceTerminalDefinition>::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<db::PolygonRef> &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (device_abstract.cluster_id_for_terminal (t->id ()));
|
||||
for (db::local_cluster<db::PolygonRef>::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 <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutVsSchematic *l2n, const db::Device &device, std::map<const Net *, unsigned int> &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<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
|
||||
const std::vector<DeviceParameterDefinition> &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<db::DeviceAbstractRef> &other_abstracts = device.other_abstracts ();
|
||||
for (std::vector<db::DeviceAbstractRef>::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<unsigned int, std::vector<db::DeviceReconnectedTerminal> > &reconnected_terminals = device.reconnected_terminals ();
|
||||
for (std::map<unsigned int, std::vector<db::DeviceReconnectedTerminal> >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) {
|
||||
|
||||
for (std::vector<db::DeviceReconnectedTerminal>::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<DeviceParameterDefinition>::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<DeviceTerminalDefinition>::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<l2n_std_format::keys<true> > writer (*mp_stream);
|
||||
writer.write (l2n);
|
||||
} else {
|
||||
l2n_std_format::std_writer_impl<l2n_std_format::keys<false> > writer (*mp_stream);
|
||||
writer.write (l2n);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<std::pair<const Net *, const Net *> > &net_identity, bool &pin_mismatch, std::map<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &c22_circuit_and_pin_mapping) const;
|
||||
bool all_subcircuits_verified (const db::Circuit *c, const std::set<const db::Circuit *> &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::pair<const db::Circuit *, const db::Circuit *>, std::vector<std::pair<const Net *, const Net *> > > m_same_nets;
|
||||
std::auto_ptr<CircuitPinMapper> mp_circuit_pin_mapper;
|
||||
std::auto_ptr<DeviceCategorizer> mp_device_categorizer;
|
||||
|
|
|
|||
|
|
@ -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<const db::Net *, const db::Net *> &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<db::Netlist> mp_netlist_a, mp_netlist_b;
|
||||
std::vector<std::pair<const db::Circuit *, const db::Circuit *> > m_circuits;
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ Class<GenericNetlistCompareLogger> 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<db::NetlistComparer> 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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue