diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index 65cacbad4..e7a6b1cd0 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -75,6 +75,15 @@ Device &Device::operator= (const Device &other) return *this; } +std::string Device::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + void Device::set_circuit (Circuit *circuit) { mp_circuit = circuit; diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index b3f6109c9..3a64f492a 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -99,6 +99,7 @@ public: { mp_device_class = dc; } + /** * @brief Gets the device model */ @@ -157,6 +158,12 @@ public: return m_name; } + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + /** * @brief Sets the device position * The device position should be the center of the recognition shape or something similar. diff --git a/src/db/db/dbDeviceModel.cc b/src/db/db/dbDeviceModel.cc index e33d9dc3d..69245cc8f 100644 --- a/src/db/db/dbDeviceModel.cc +++ b/src/db/db/dbDeviceModel.cc @@ -29,19 +29,25 @@ namespace db // -------------------------------------------------------------------------------- // DeviceModel class implementation +DeviceModel::DeviceModel () + : m_name (), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +{ + // .. nothing yet .. +} + DeviceModel::~DeviceModel () { // .. nothing yet .. } -DeviceModel::DeviceModel (const std::string &name) - : m_name (name), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) +DeviceModel::DeviceModel (db::DeviceClass *device_class, const std::string &name) + : m_name (name), mp_device_class (device_class), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) { // .. nothing yet .. } DeviceModel::DeviceModel (const DeviceModel &other) - : tl::Object (other), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) + : tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits::max ()), mp_netlist (0) { operator= (other); } @@ -50,6 +56,7 @@ DeviceModel &DeviceModel::operator= (const DeviceModel &other) { if (this != &other) { m_name = other.m_name; + mp_device_class = other.mp_device_class; m_cell_index = other.m_cell_index; m_terminal_cluster_ids = other.m_terminal_cluster_ids; } diff --git a/src/db/db/dbDeviceModel.h b/src/db/db/dbDeviceModel.h index 1b0406179..ac515befe 100644 --- a/src/db/db/dbDeviceModel.h +++ b/src/db/db/dbDeviceModel.h @@ -46,10 +46,15 @@ class DB_PUBLIC DeviceModel : public tl::Object { public: + /** + * @brief Default constructor + */ + DeviceModel (); + /** * @brief The constructor */ - DeviceModel (const std::string &name = std::string ()); + DeviceModel (db::DeviceClass *device_class, const std::string &name = std::string ()); /** * @brief Copy constructor @@ -66,6 +71,22 @@ public: */ ~DeviceModel (); + /** + * @brief Gets the device class + */ + const DeviceClass *device_class () const + { + return mp_device_class; + } + + /** + * @brief Sets the device class + */ + void set_device_class (DeviceClass *dc) + { + mp_device_class = dc; + } + /** * @brief Gets the netlist the device lives in (const version) * This pointer is 0 if the device model isn't added to a netlist @@ -127,6 +148,7 @@ private: friend class Netlist; std::string m_name; + db::DeviceClass *mp_device_class; db::cell_index_type m_cell_index; std::vector m_terminal_cluster_ids; Netlist *mp_netlist; diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 8bad67fe8..0c824e1f4 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -39,49 +39,66 @@ namespace db * 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 * description() - an arbitrary description text - * dbu() - specifies the database unit [short key: D] + * unit() - specifies the database unit [short key: U] * top() - specifies the name of the top circuit [short key: T] * layer() - define a layer [short key: L] * connect( ...) - connects layer1 with the following layers [short key: C] * global( ...) - connects a layer with the given global nets [short key: G] - * circuit( ...) - defines a circuit (cell) [short key: X] + * circuit( [circuit-def]) - circuit (cell) [short key: X] + * device( [device-footprint-def]) + * - device footprint [short key: D] * - * Inside the circuit: + * [circuit-def]: * - * net( ...) - specifies net geometry [short key: N] - * device( ...) - defines a device [short key: D] - * subcircuit( ...) - defines a subcircuit [short key: X] + * net( [geometry-def]) - net geometry [short key: N] + * A net declaration shall be there also if no geometry + * is present + * pin( ) - outgoing pin connection [short key: P] + * device( [device-def]) + * - device with connections [short key: D] + * subcircuit( [subcircuit-def]) + * - subcircuit with connections [short key: X] * - * Inside a net: + * [geometry-def]: * * polygon( ...) - defines a polygon [short key: P] * rect( ) * - defines a rectangle [short key: R] * - * Inside a device: + * [device-footprint-def]: + * + * terminal( [geometry-def]) + * - specifies the terminal geometry [short key: empty] + * + * [device-def]: * * param( ) - defines a parameter [short key P] + * footprint() - links to a geometrical device footprint on top level [short key F] * terminal( ) * - specifies connection of the terminal with * a net (short key: empty) * location( ) - location of the device [short key L] * - * Inside a subcircuit: + * [subcircuit-def]: * * location( ) - location of the subcircuit [short key L] * rotation() - rotation angle [short key O] * mirror - if specified, the instance is mirrored before rotation [short key M] * scale() - magnification [short key *] - * pin( ) - specifies connection of the pin with a net + * pin( ) - specifies connection of the pin with a net [short key: P] */ +static std::string version_key ("version"); static std::string description_key ("description"); static std::string top_key ("top"); -static std::string dbu_key ("dbu"); +static std::string unit_key ("unit"); static std::string layer_key ("layer"); static std::string text_key ("text"); static std::string connect_key ("connect"); @@ -93,6 +110,7 @@ static std::string subcircuit_key ("subcircuit"); static std::string polygon_key ("polygon"); static std::string rect_key ("rect"); static std::string terminal_key ("terminal"); +static std::string footprint_key ("footprint"); static std::string label_key ("label"); static std::string param_key ("param"); static std::string location_key ("location"); @@ -122,12 +140,17 @@ static std::string name_for_layer (const db::Layout *layout, unsigned int l) void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) { + const int version = 1; + const db::Layout *ly = l2n->internal_layout (); const db::Netlist *nl = l2n->netlist (); + *mp_stream << "# General" << endl; + *mp_stream << version_key << "(" << version << ")" << endl; *mp_stream << top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; - *mp_stream << dbu_key << "(" << ly->dbu () << ")" << endl; + *mp_stream << unit_key << "(" << ly->dbu () << ")" << endl; + *mp_stream << endl << "# Layers" << endl; for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) { *mp_stream << layer_key << "(" << name_for_layer (ly, *l) << ")" << endl; @@ -137,7 +160,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) if (cb != ce) { *mp_stream << connect_key << "(" << name_for_layer (ly, *l); for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) { - *mp_stream << " " << name_for_layer (ly, *l); + *mp_stream << " " << name_for_layer (ly, *c); } *mp_stream << ")" << endl; } @@ -154,7 +177,18 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n) } - for (db::Netlist::const_circuit_iterator x = nl->begin_circuits (); x != nl->end_circuits (); ++x) { + *mp_stream << endl << "# Device footprints" << endl; + for (db::Netlist::const_device_model_iterator m = nl->begin_device_models (); m != nl->end_device_models (); ++m) { + if (m->device_class ()) { + *mp_stream << 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; + } + } + + for (db::Netlist::const_top_down_circuit_iterator i = nl->begin_top_down (); i != nl->end_top_down (); ++i) { + const db::Circuit *x = *i; + *mp_stream << endl << "# Circuit " << x->name () << endl; *mp_stream << circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; write (l2n, *x); *mp_stream << ")" << endl; @@ -167,6 +201,13 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const write (l2n, *n); } + 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 << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl; + } + } + for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) { write (l2n, *d); } @@ -185,6 +226,31 @@ void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr) } } +void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const std::string &lname) +{ + const db::Polygon &poly = s->obj (); + if (poly.is_box ()) { + + db::Box box = s->trans () * poly.box (); + *mp_stream << rect_key << "(" << lname; + *mp_stream << " " << box.left () << " " << box.bottom (); + *mp_stream << " " << box.right () << " " << box.top (); + *mp_stream << ")"; + + } else { + + *mp_stream << polygon_key << "(" << lname; + if (poly.holes () > 0) { + db::SimplePolygon sp (poly); + write_points (*mp_stream, sp, s->trans ()); + } else { + write_points (*mp_stream, poly, s->trans ()); + } + *mp_stream << ")"; + + } +} + void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Net &net) { const db::Layout *ly = l2n->internal_layout (); @@ -192,47 +258,39 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const const db::Circuit *circuit = net.circuit (); const db::Connectivity &conn = l2n->connectivity (); - *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + bool any = false; for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { const db::local_cluster &lc = clusters.clusters_per_cell (circuit->cell_index ()).cluster_by_id (net.cluster_id ()); for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { - *mp_stream << indent2; - - const db::Polygon &poly = s->obj (); - if (poly.is_box ()) { - - db::Box box = s->trans () * poly.box (); - *mp_stream << rect_key << "(" << name_for_layer (ly, *l); - *mp_stream << " " << box.left () << " " << box.bottom (); - *mp_stream << " " << box.right () << " " << box.top (); - *mp_stream << ")"; - - } else { - - *mp_stream << polygon_key << "(" << name_for_layer (ly, *l); - if (poly.holes () > 0) { - db::SimplePolygon sp (poly); - write_points (*mp_stream, sp, s->trans ()); - } else { - write_points (*mp_stream, poly, s->trans ()); - } - *mp_stream << ")"; - + if (! any) { + *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl; + any = true; } + *mp_stream << indent2; + write (s.operator-> (), name_for_layer (ly, *l)); + *mp_stream << endl; + } } - *mp_stream << indent1 << ")" << endl; + if (any) { + *mp_stream << indent1 << ")" << endl; + } else { + *mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl; + } } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::SubCircuit &subcircuit) +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit) { - *mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()); + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + + *mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ()); const db::DCplxTrans &tr = subcircuit.trans (); if (tr.is_mag ()) { @@ -244,24 +302,27 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, if (fabs (tr.angle ()) > 1e-6) { *mp_stream << " " << rotation_key << "(" << tr.angle () << ")"; } - *mp_stream << " " << location_key << "(" << tr.disp ().x () << " " << tr.disp ().y () << ")"; + *mp_stream << " " << location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")"; - // each pin in one line for more than 16 pins - bool separate_lines = (subcircuit.circuit ()->pin_count () > 16); + // 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 ()->begin_pins (); p != subcircuit.circuit ()->end_pins (); ++p) { - if (separate_lines) { - *mp_stream << indent2; - } else { - *mp_stream << " "; - } - *mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_word_or_quoted_string (subcircuit.net_for_pin (p->id ())->expanded_name ()) << ")"; - 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 << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")"; + if (separate_lines) { + *mp_stream << endl; + } } } @@ -272,24 +333,57 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, *mp_stream << ")" << endl; } -void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::Device &device) +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model) { - // @@@ TODO: add location + const std::vector &td = device_model.device_class ()->terminal_definitions (); - *mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.name ()); - *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()); + const db::Layout *ly = l2n->internal_layout (); + const db::hier_clusters &clusters = l2n->net_clusters (); + const db::Connectivity &conn = l2n->connectivity (); + + for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::local_cluster &lc = clusters.clusters_per_cell (device_model.cell_index ()).cluster_by_id (device_model.cluster_id_for_terminal (t->id ())); + for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { + + *mp_stream << indent1; + write (s.operator-> (), name_for_layer (ly, *l)); + *mp_stream << endl; + + } + + } + + } +} + +void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Device &device) +{ + const db::Layout *ly = l2n->internal_layout (); + double dbu = ly->dbu (); + + *mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl; + + *mp_stream << indent2 << location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl; + + if (device.device_model ()) { + *mp_stream << indent2 << footprint_key << "(" << tl::to_word_or_quoted_string (device.device_model ()->name ()) << ")" << endl; + } const std::vector &pd = device.device_class ()->parameter_definitions (); for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - *mp_stream << " " << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")"; + *mp_stream << indent2 << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl; } const std::vector &td = device.device_class ()->terminal_definitions (); for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { - *mp_stream << " " << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")"; + *mp_stream << indent2 << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")" << endl; } - *mp_stream << ")" << endl; + *mp_stream << indent1 << ")" << endl; } } diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h index f158f8911..2955ba66b 100644 --- a/src/db/db/dbLayoutToNetlistWriter.h +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -24,6 +24,7 @@ #define HDR_dbLayoutToNetlistWriter #include "dbCommon.h" +#include "dbPolygon.h" #include "tlStream.h" namespace db @@ -34,6 +35,7 @@ class Net; class Circuit; class SubCircuit; class Device; +class DeviceModel; /** * @brief The base class for a LayoutToNetlist writer @@ -65,6 +67,8 @@ private: void write (const db::LayoutToNetlist *l2n, const db::Net &net); void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit); void write (const db::LayoutToNetlist *l2n, const db::Device &device); + void write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model); + void write (const db::PolygonRef *s, const std::string &lname); }; } diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 0f12dd3c8..87db486d6 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -287,7 +287,7 @@ void NetlistDeviceExtractor::push_new_devices () std::string cell_name = "D$" + mp_device_class->name (); db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ())); - db::DeviceModel *dm = new db::DeviceModel (mp_layout->cell_name (device_cell.cell_index ())); + db::DeviceModel *dm = new db::DeviceModel (mp_device_class, mp_layout->cell_name (device_cell.cell_index ())); m_netlist->add_device_model (dm); dm->set_cell_index (device_cell.cell_index ()); diff --git a/src/db/db/dbPin.cc b/src/db/db/dbPin.cc index 74b98b51d..e223ec8ae 100644 --- a/src/db/db/dbPin.cc +++ b/src/db/db/dbPin.cc @@ -21,6 +21,7 @@ */ #include "dbPin.h" +#include "tlString.h" namespace db { @@ -40,4 +41,13 @@ Pin::Pin (const std::string &name) // .. nothing yet .. } +std::string Pin::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + } diff --git a/src/db/db/dbPin.h b/src/db/db/dbPin.h index 925d6e023..2ee33cf6c 100644 --- a/src/db/db/dbPin.h +++ b/src/db/db/dbPin.h @@ -56,6 +56,12 @@ public: return m_name; } + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + /** * @brief Gets the ID of the pin (only pins inside circuits have valid ID's) */ diff --git a/src/db/db/dbSubCircuit.cc b/src/db/db/dbSubCircuit.cc index 9de235b97..4db7dfa6c 100644 --- a/src/db/db/dbSubCircuit.cc +++ b/src/db/db/dbSubCircuit.cc @@ -74,6 +74,15 @@ void SubCircuit::set_name (const std::string &n) } } +std::string SubCircuit::expanded_name () const +{ + if (name ().empty ()) { + return "$" + tl::to_string (id ()); + } else { + return name (); + } +} + void SubCircuit::set_trans (const db::DCplxTrans &t) { m_trans = t; diff --git a/src/db/db/dbSubCircuit.h b/src/db/db/dbSubCircuit.h index a28b8211a..6a25001c0 100644 --- a/src/db/db/dbSubCircuit.h +++ b/src/db/db/dbSubCircuit.h @@ -134,6 +134,12 @@ public: return m_name; } + /** + * @brief Gets a name which always non-empty + * This method will pick a name like "$" if the explicit name is empty. + */ + std::string expanded_name () const; + /** * @brief Sets the transformation describing the subcircuit * diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 884ad8f94..3974dddc5 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -22,6 +22,8 @@ #include "gsiDecl.h" #include "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistWriter.h" +#include "tlStream.h" namespace gsi { @@ -58,6 +60,13 @@ static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMappin l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } +static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path) +{ + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream); + writer.write (l2n); +} + Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), "@brief The constructor\n" @@ -254,6 +263,10 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "See the description of the other \\probe_net variant.\n" "This variant accepts a database-unit location. The location is given in the\n" "coordinate space of the initial cell.\n" + ) + + gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), + "@brief Writes the extracted netlist to a file.\n" + "This method employs the native format of KLayout.\n" ), "@brief A generic framework for extracting netlists from layouts\n" "\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc new file mode 100644 index 000000000..0213af5dd --- /dev/null +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -0,0 +1,174 @@ + +/* + + 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 "dbLayoutToNetlist.h" +#include "dbLayoutToNetlistWriter.h" +#include "dbStream.h" +#include "dbCommonReader.h" +#include "dbNetlistDeviceExtractorClasses.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +TEST(1_WriterBasic) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell)); + std::auto_ptr ractive (l2n.make_layer (active)); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly)); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl)); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont)); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont)); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1)); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl)); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1)); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2)); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl)); + + // derived regions + + db::Region rpactive = *ractive & *rnwell; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = *ractive - *rnwell; + db::Region rngate = rnactive & *rpoly; + db::Region rnsd = rnactive - rngate; + + db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // return the computed layers into the original layout and write it for debugging purposes + // NOTE: this will include the device layers too + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + unsigned int lpoly = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rpoly->insert_into (&ly, tc.cell_index (), lpoly); + + // net extraction + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + tl::OutputMemoryStream mem; + { + tl::OutputStream stream (mem); + db::LayoutToNetlistStandardWriter writer (stream); + writer.write (&l2n); + } + +// TODO: too big for inlined text ... +#if 0 + EXPECT_EQ (std::string (mem.data (), mem.size ()), + "" + ); +#endif +} diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 91a2f5058..799d1c791 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -502,6 +502,8 @@ TEST(4_NetlistSubcircuits) nl->add_device_class (dc); db::DeviceModel *dm = new db::DeviceModel (); + dm->set_device_class (dc); + EXPECT_EQ (dm->device_class () == dc, true); dm->set_name ("dm2"); dm->set_cell_index (42); dm->set_cluster_id_for_terminal (0, 17); @@ -1044,7 +1046,8 @@ TEST(13_DeviceModel) { db::Netlist nl; - db::DeviceModel *dm = new db::DeviceModel ("name"); + db::DeviceModel *dm = new db::DeviceModel (0, "name"); + EXPECT_EQ (dm->device_class () == 0, true); EXPECT_EQ (dm->name (), "name"); dm->set_name ("name2"); EXPECT_EQ (dm->name (), "name2"); diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index c0701254d..5bed164ca 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -63,7 +63,8 @@ SOURCES = \ dbNetlistExtractorTests.cc \ dbNetlistDeviceExtractorTests.cc \ dbNetlistDeviceClassesTests.cc \ - dbLayoutToNetlistTests.cc + dbLayoutToNetlistTests.cc \ + dbLayoutToNetlistWriterTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC