WIP: l2n reader implementation, some bug fixes, refactoring.

This commit is contained in:
Matthias Koefferlein 2019-01-19 22:19:08 +01:00
parent 56bb39a273
commit 8213e71a79
17 changed files with 1364 additions and 319 deletions

View File

@ -161,7 +161,8 @@ SOURCES = \
dbPin.cc \
dbLayoutToNetlistReader.cc \
dbLayoutToNetlistWriter.cc \
dbDeviceModel.cc
dbDeviceModel.cc \
dbLayoutToNetlistFormatDefs.cc
HEADERS = \
dbArray.h \
@ -287,7 +288,8 @@ HEADERS = \
dbSubCircuit.h \
dbLayoutToNetlistReader.h \
dbLayoutToNetlistWriter.h \
dbDeviceModel.h
dbDeviceModel.h \
dbLayoutToNetlistFormatDefs.h
!equals(HAVE_QT, "0") {

View File

@ -1664,11 +1664,6 @@ template <class T>
void
hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
{
if (m_per_cell_clusters.find (cell.cell_index ()) != m_per_cell_clusters.end ()) {
// skip pre-build clusters (from devices)
return;
}
std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ()));
if (tl::verbosity () >= 40) {
tl::log << msg;

View File

@ -78,6 +78,18 @@ size_t LayoutToNetlist::max_vertex_count () const
return m_dss.max_vertex_count ();
}
db::Region *LayoutToNetlist::make_layer (const std::string &n)
{
db::RecursiveShapeIterator si (m_iter);
si.shape_flags (db::ShapeIterator::Nothing);
db::Region *region = new db::Region (si, m_dss);
if (! n.empty ()) {
name (*region, n);
}
return region;
}
db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::string &n)
{
db::RecursiveShapeIterator si (m_iter);
@ -199,8 +211,6 @@ void LayoutToNetlist::extract_netlist ()
mp_netlist.reset (new db::Netlist ());
}
m_net_clusters.clear ();
db::NetlistExtractor netex;
netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters);
@ -217,6 +227,16 @@ const db::Cell *LayoutToNetlist::internal_top_cell () const
return &m_dss.const_initial_cell ();
}
db::Layout *LayoutToNetlist::internal_layout ()
{
return &m_dss.layout ();
}
db::Cell *LayoutToNetlist::internal_top_cell ()
{
return &m_dss.initial_cell ();
}
void LayoutToNetlist::name (const db::Region &region, const std::string &name)
{
unsigned int li = layer_of (region);
@ -269,6 +289,15 @@ db::Netlist *LayoutToNetlist::netlist () const
return mp_netlist.get ();
}
db::Netlist *LayoutToNetlist::make_netlist ()
{
if (! mp_netlist.get ()) {
mp_netlist.reset (new db::Netlist ());
}
return mp_netlist.get ();
}
template <class Tr>
static void deliver_shape (const db::PolygonRef &pr, db::Region &region, const Tr &tr)
{

View File

@ -133,6 +133,11 @@ public:
return internal_layout ()->get_properties (layer_of (region)).name;
}
/**
* @brief Creates a new empty region
*/
db::Region *make_layer (const std::string &n);
/**
* @brief Creates a new region representing an original layer
* "layer_index" is the layer index of the desired layer in the original layout.
@ -216,6 +221,16 @@ public:
*/
const db::Cell *internal_top_cell () const;
/**
* @brief Gets the internal layout (non-const version)
*/
db::Layout *internal_layout ();
/**
* @brief Gets the internal top cell (non-const version)
*/
db::Cell *internal_top_cell ();
/**
* @brief Gets the connectivity object
*/
@ -251,6 +266,11 @@ public:
*/
db::Netlist *netlist () const;
/**
* @brief gets the netlist extracted or make on if none exists yet.
*/
db::Netlist *make_netlist ();
/**
* @brief Gets the hierarchical shape clusters derived in the net extraction.
* NOTE: the layer and cell indexes used inside this structure refer to the
@ -261,6 +281,14 @@ public:
return m_net_clusters;
}
/**
* @brief Gets the hierarchical shape clusters derived in the net extraction (non-conver version)
*/
db::hier_clusters<db::PolygonRef> &net_clusters ()
{
return m_net_clusters;
}
/**
* @brief Returns all shapes of a specific net and layer.
*

View File

@ -0,0 +1,73 @@
/*
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 "dbLayoutToNetlistFormatDefs.h"
namespace db
{
namespace l2n_std_format
{
template<> DB_PUBLIC const std::string keys<false>::version_key ("version");
template<> DB_PUBLIC const std::string keys<false>::description_key ("description");
template<> DB_PUBLIC const std::string keys<false>::top_key ("top");
template<> DB_PUBLIC const std::string keys<false>::unit_key ("unit");
template<> DB_PUBLIC const std::string keys<false>::layer_key ("layer");
template<> DB_PUBLIC const std::string keys<false>::connect_key ("connect");
template<> DB_PUBLIC const std::string keys<false>::global_key ("global");
template<> DB_PUBLIC const std::string keys<false>::circuit_key ("circuit");
template<> DB_PUBLIC const std::string keys<false>::net_key ("net");
template<> DB_PUBLIC const std::string keys<false>::device_key ("device");
template<> DB_PUBLIC const std::string keys<false>::polygon_key ("polygon");
template<> DB_PUBLIC const std::string keys<false>::rect_key ("rect");
template<> DB_PUBLIC const std::string keys<false>::terminal_key ("terminal");
template<> DB_PUBLIC const std::string keys<false>::abstract_key ("abstract");
template<> DB_PUBLIC const std::string keys<false>::param_key ("param");
template<> DB_PUBLIC const std::string keys<false>::location_key ("location");
template<> DB_PUBLIC const std::string keys<false>::rotation_key ("rotation");
template<> DB_PUBLIC const std::string keys<false>::mirror_key ("mirror");
template<> DB_PUBLIC const std::string keys<false>::scale_key ("scale");
template<> DB_PUBLIC const std::string keys<false>::pin_key ("pin");
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");
template<> DB_PUBLIC const std::string keys<true>::unit_key ("U");
template<> DB_PUBLIC const std::string keys<true>::layer_key ("L");
template<> DB_PUBLIC const std::string keys<true>::connect_key ("C");
template<> DB_PUBLIC const std::string keys<true>::global_key ("G");
template<> DB_PUBLIC const std::string keys<true>::circuit_key ("X");
template<> DB_PUBLIC const std::string keys<true>::net_key ("N");
template<> DB_PUBLIC const std::string keys<true>::device_key ("D");
template<> DB_PUBLIC const std::string keys<true>::polygon_key ("Q");
template<> DB_PUBLIC const std::string keys<true>::rect_key ("R");
template<> DB_PUBLIC const std::string keys<true>::terminal_key ("T");
template<> DB_PUBLIC const std::string keys<true>::abstract_key ("A");
template<> DB_PUBLIC const std::string keys<true>::param_key ("E");
template<> DB_PUBLIC const std::string keys<true>::location_key ("Y");
template<> DB_PUBLIC const std::string keys<true>::rotation_key ("O");
template<> DB_PUBLIC const std::string keys<true>::mirror_key ("M");
template<> DB_PUBLIC const std::string keys<true>::scale_key ("S");
template<> DB_PUBLIC const std::string keys<true>::pin_key ("P");
}
}

View File

@ -0,0 +1,118 @@
/*
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_dbLayoutToNetlistFormatDefs
#define HDR_dbLayoutToNetlistFormatDefs
#include "dbCommon.h"
#include <string>
namespace db
{
/**
* This is the internal persistency format for LayoutToNetlist
*
* 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]
* unit(<unit>) - specifies the database unit [short key: U]
* top(<circuit>) - specifies the name of the top circuit [short key: W]
* layer(<name>) - define a layer [short key: L]
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
* global(<layer> <net> ...) - connects a layer with the given global nets [short key: G]
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
* device(<name> <class> [device-abstract-def])
* - device abstract [short key: D]
*
* [circuit-def]:
*
* net(<name> [geometry-def]) - net geometry [short key: N]
* A net declaration shall be there also if no geometry
* is present
* pin(<name> <net-name>) - outgoing pin connection [short key: P]
* device(<name> <abstract> [device-def])
* - device with connections [short key: D]
* circuit(<name> [circuit-def]) - subcircuit with connections [short key: X]
*
* [geometry-def]:
*
* polygon(<layer> <x> <y> ...) - defines a polygon [short key: Q]
* rect(<layer> <left> <bottom> <right> <top>)
* - defines a rectangle [short key: R]
*
* [device-abstract-def]:
*
* terminal(<terminal-name> [geometry-def])
* - specifies the terminal geometry [short key: T]
*
* [device-def]:
*
* location(<x> <y>) - location of the device [short key Y]
* must be before terminal
* param(<name> <value>) - defines a parameter [short key E]
* terminal(<terminal-name> <net-name>)
* - specifies connection of the terminal with
* a net (short key: T)
*
* [subcircuit-def]:
*
* location(<x> <y>) - location of the subcircuit [short key Y]
* rotation(<angle>) - rotation angle (in degree, default is 0) [short key O]
* mirror - if specified, the instance is mirrored before rotation [short key M]
* scale(<mag>) - magnification (default is 1) [short key S]
* pin(<pin-name> <net-name>) - specifies connection of the pin with a net [short key: P]
*/
namespace l2n_std_format
{
template <bool Short>
struct DB_PUBLIC keys
{
static const std::string version_key, description_key, top_key, unit_key,
layer_key, connect_key, global_key,
circuit_key, net_key, device_key, subcircuit_key,
polygon_key, rect_key, terminal_key, abstract_key,
param_key, location_key, rotation_key,
mirror_key, scale_key, pin_key,
indent1, indent2;
};
}
}
#endif

View File

@ -21,10 +21,698 @@
*/
#include "dbLayoutToNetlistReader.h"
#include "dbLayoutToNetlistFormatDefs.h"
#include "dbLayoutToNetlist.h"
namespace db
{
namespace l2n_std_reader {
class Layers
{
public:
Layers () { }
~Layers ()
{
for (std::map<std::string, db::Region *>::const_iterator i = m_layers.begin (); i != m_layers.end (); ++i) {
delete i->second;
}
m_layers.clear ();
}
void add (const std::string &name, db::Region *region)
{
if (m_layers.find (name) != m_layers.end ()) {
delete region;
throw tl::Exception (tl::to_string (tr ("Duplicate layer name: ")) + name);
}
m_layers.insert (std::make_pair (name, region));
}
db::Region &layer (const std::string &name)
{
std::map<std::string, db::Region *>::const_iterator l = m_layers.find (name);
if (l == m_layers.end ()) {
throw tl::Exception (tl::to_string (tr ("Not a valid layer name: ")) + name);
}
return *l->second;
}
private:
std::map<std::string, db::Region *> m_layers;
};
class Brace
{
public:
Brace (LayoutToNetlistStandardReader *reader) : mp_reader (reader), m_checked (false)
{
m_has_brace = reader->test ("(");
}
operator bool ()
{
if (! m_has_brace) {
m_checked = true;
return false;
} else if (mp_reader->test (")")) {
m_checked = true;
return false;
} else {
return true;
}
}
void done ()
{
if (m_has_brace && ! m_checked) {
mp_reader->expect (")");
m_checked = true;
}
}
private:
LayoutToNetlistStandardReader *mp_reader;
bool m_checked;
bool m_has_brace;
};
}
typedef l2n_std_format::keys<true> skeys;
typedef l2n_std_format::keys<false> lkeys;
LayoutToNetlistStandardReader::LayoutToNetlistStandardReader (tl::InputStream &stream)
: m_stream (stream), m_path (stream.absolute_path ())
{
skip ();
}
bool
LayoutToNetlistStandardReader::test (const std::string &token)
{
skip ();
return ! at_end () && m_ex.test (token.c_str ());
}
void
LayoutToNetlistStandardReader::expect (const std::string &token)
{
m_ex.expect (token.c_str ());
}
void
LayoutToNetlistStandardReader::read_word_or_quoted (std::string &s)
{
m_ex.read_word_or_quoted (s);
}
int
LayoutToNetlistStandardReader::read_int ()
{
int i = 0;
m_ex.read (i);
return i;
}
db::Coord
LayoutToNetlistStandardReader::read_coord ()
{
db::Coord i = 0;
m_ex.read (i);
return i;
}
double
LayoutToNetlistStandardReader::read_double ()
{
double d = 0;
m_ex.read (d);
return d;
}
bool
LayoutToNetlistStandardReader::at_end ()
{
return (m_ex.at_end () && m_stream.at_end ());
}
void
LayoutToNetlistStandardReader::skip ()
{
while (m_ex.at_end () || *m_ex.skip () == '#') {
if (m_stream.at_end ()) {
return;
}
m_line = m_stream.get_line ();
m_ex = tl::Extractor (m_line.c_str ());
}
}
void LayoutToNetlistStandardReader::read (db::LayoutToNetlist *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 (), m_stream.line_number (), m_path));
}
}
void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n)
{
int version = 0;
std::string description;
// TODO: there probably is a more efficient way to force the layout inside l2n to be made
l2n->make_layer (std::string ());
tl_assert (l2n->internal_layout ());
l2n->make_netlist ();
Layers layers;
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::unit_key) || test (lkeys::unit_key)) {
Brace br (this);
double dbu = read_double ();
l2n->internal_layout ()->dbu (dbu);
br.done ();
} else if (test (skeys::top_key) || test (lkeys::top_key)) {
Brace br (this);
std::string top;
read_word_or_quoted (top);
l2n->internal_layout ()->rename_cell (l2n->internal_top_cell ()->cell_index (), top.c_str ());
br.done ();
} else if (test (skeys::layer_key) || test (lkeys::layer_key)) {
Brace br (this);
std::string layer;
read_word_or_quoted (layer);
layers.add (layer, l2n->make_layer (layer));
br.done ();
} else if (test (skeys::connect_key) || test (lkeys::connect_key)) {
Brace br (this);
std::string l1;
read_word_or_quoted (l1);
while (br) {
std::string l2;
read_word_or_quoted (l2);
l2n->connect (layers.layer (l1), layers.layer (l2));
}
br.done ();
} else if (test (skeys::global_key) || test (lkeys::global_key)) {
Brace br (this);
std::string l1;
read_word_or_quoted (l1);
while (br) {
std::string g;
read_word_or_quoted (g);
l2n->connect_global (layers.layer (l1), g);
}
br.done ();
} else 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);
l2n->netlist ()->add_circuit (circuit);
db::Layout *ly = l2n->internal_layout ();
db::cell_index_type ci = ly->add_cell (name.c_str ());
circuit->set_cell_index (ci);
std::map<db::CellInstArray, std::list<Connections> > connections;
while (br) {
if (test (skeys::net_key) || test (lkeys::net_key)) {
read_net (l2n, circuit, layers);
} else if (test (skeys::pin_key) || test (lkeys::pin_key)) {
read_pin (l2n, circuit);
} else if (test (skeys::device_key) || test (lkeys::device_key)) {
std::list<Connections> conn;
db::CellInstArray ia = read_device (l2n, circuit, conn);
connections[ia] = conn;
} else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) {
std::list<Connections> conn;
db::CellInstArray ia = read_subcircuit (l2n, circuit, conn);
connections[ia] = conn;
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)")));
}
}
br.done ();
db::Cell &ccell = ly->cell (ci);
// connections needs to be made after the instances (because in a readonly Instances container
// the Instance pointers will invalidate when new instances are added)
for (db::Cell::const_iterator i = ccell.begin (); ! i.at_end (); ++i) {
std::map<db::CellInstArray, std::list<Connections> >::const_iterator c = connections.find (i->cell_inst ());
if (c != connections.end ()) {
for (std::list<Connections>::const_iterator j = c->second.begin (); j != c->second.end (); ++j) {
l2n->net_clusters ().clusters_per_cell (ci).add_connection (j->from_cluster, db::ClusterInstance (j->to_cluster, *i));
}
}
}
} else if (test (skeys::device_key) || test (lkeys::device_key)) {
Brace br (this);
std::string name;
read_word_or_quoted (name);
db::DeviceModel *dm = new db::DeviceModel ();
dm->set_name (name);
l2n->netlist ()->add_device_model (dm);
db::cell_index_type ci = l2n->internal_layout ()->add_cell (name.c_str ());
dm->set_cell_index (ci);
std::string cls;
read_word_or_quoted (cls);
db::DeviceClass *dc = 0;
for (db::Netlist::device_class_iterator i = l2n->netlist ()->begin_device_classes (); i != l2n->netlist ()->end_device_classes (); ++i) {
if (i->name () == cls) {
dc = i.operator-> ();
}
}
// use a generic device class unless the right one is registered already.
bool gen_dc = (dc == 0);
if (gen_dc) {
dc = new db::DeviceClass ();
dc->set_name (cls);
l2n->netlist ()->add_device_class (dc);
}
dm->set_device_class (dc);
while (br) {
if (test (skeys::terminal_key) || test (lkeys::terminal_key)) {
read_abstract_terminal (l2n, dm, gen_dc ? dc : 0, layers);
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device abstract definition (terminal expected)")));
}
}
br.done ();
}
}
}
std::pair<unsigned int, db::PolygonRef>
LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n, Layers &layers)
{
std::string lname;
if (test (skeys::rect_key) || test (lkeys::rect_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layers.layer (lname));
db::Coord l = read_coord ();
db::Coord b = read_coord ();
db::Coord r = read_coord ();
db::Coord t = read_coord ();
db::Box box (l, b, r, t);
br.done ();
return std::make_pair (lid, db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ()));
} else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layers.layer (lname));
std::vector<db::Point> pt;
while (br) {
db::Coord x = read_coord ();
db::Coord y = read_coord ();
pt.push_back (db::Point (x, y));
}
br.done ();
db::Polygon poly;
poly.assign_hull (pt.begin (), pt.end ());
return std::make_pair (lid, db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ()));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside net or terminal definition (polygon or rect expected)")));
}
}
void
LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, Layers &layers)
{
Brace br (this);
std::string name;
read_word_or_quoted (name);
db::Net *net = new db::Net ();
net->set_name (name);
circuit->add_net (net);
db::connected_clusters<db::PolygonRef> &cc = l2n->net_clusters ().clusters_per_cell (circuit->cell_index ());
db::local_cluster<db::PolygonRef> &lc = *cc.insert ();
net->set_cluster_id (lc.id ());
db::Cell &cell = l2n->internal_layout ()->cell (circuit->cell_index ());
while (br) {
std::pair<unsigned int, db::PolygonRef> pr = read_geometry (l2n, layers);
lc.add (pr.second, pr.first);
cell.shapes (pr.first).insert (pr.second);
}
br.done ();
}
void
LayoutToNetlistStandardReader::read_pin (db::LayoutToNetlist * /*l2n*/, db::Circuit *circuit)
{
Brace br (this);
std::string name;
read_word_or_quoted (name);
std::string netname;
read_word_or_quoted (netname);
br.done ();
const db::Pin &pin = circuit->add_pin (name);
db::Net *net = circuit->net_by_name (netname);
if (!net) {
throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname);
}
circuit->connect_pin (pin.id (), net);
}
db::CellInstArray
LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs)
{
Brace br (this);
std::string name;
read_word_or_quoted (name);
std::string dmname;
read_word_or_quoted (dmname);
db::DeviceModel *dm = 0;
for (db::Netlist::device_model_iterator i = l2n->netlist ()->begin_device_models (); i != l2n->netlist ()->end_device_models (); ++i) {
if (i->name () == dmname) {
dm = i.operator-> ();
}
}
if (! dm) {
throw tl::Exception (tl::to_string (tr ("Not a valid device model name: ")) + dmname);
}
db::Device *device = new db::Device ();
device->set_device_class (const_cast<db::DeviceClass *> (dm->device_class ()));
device->set_device_model (dm);
device->set_name (name);
circuit->add_device (device);
db::Coord x = 0, y = 0;
while (br) {
if (test (skeys::location_key) || test (lkeys::location_key)) {
Brace br2 (this);
x = read_coord ();
y = read_coord ();
br2.done ();
} else if (test (skeys::terminal_key) || test (lkeys::terminal_key)) {
Brace br2 (this);
std::string tname;
read_word_or_quoted (tname);
std::string netname;
read_word_or_quoted (netname);
br2.done ();
size_t tid = std::numeric_limits<size_t>::max ();
const std::vector<db::DeviceTerminalDefinition> &td = dm->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (t->name () == tname) {
tid = t->id ();
break;
}
}
if (tid == std::numeric_limits<size_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + tname + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ());
}
db::Net *net = circuit->net_by_name (netname);
if (!net) {
throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname);
}
device->connect_terminal (tid, net);
refs.push_back (Connections (net->cluster_id (), dm->cluster_id_for_terminal (tid)));
} 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)")));
}
}
double dbu = l2n->internal_layout ()->dbu ();
device->set_position (db::DPoint (dbu * x, dbu * y));
br.done ();
// make device cell instance
db::CellInstArray inst (db::CellInst (dm->cell_index ()), db::Trans (db::Vector (x, y)));
db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ());
ccell.insert (inst);
return inst;
}
db::CellInstArray
LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs)
{
Brace br (this);
std::string name;
read_word_or_quoted (name);
std::string xname;
read_word_or_quoted (xname);
db::Circuit *circuit_ref = l2n->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);
db::Coord x = 0, y = 0;
bool mirror = false;
double angle = 0;
double mag = 1.0;
db::InstElement ie;
bool inst_made = false;
while (br) {
if (test (skeys::location_key) || test (lkeys::location_key)) {
Brace br2 (this);
x = read_coord ();
y = read_coord ();
br2.done ();
if (inst_made) {
throw tl::Exception (tl::to_string (tr ("location key must come before pin key in subcircuit definition")));
}
} else if (test (skeys::rotation_key) || test (lkeys::rotation_key)) {
Brace br2 (this);
angle = read_double ();
br2.done ();
if (inst_made) {
throw tl::Exception (tl::to_string (tr ("rotation key must come before pin key in subcircuit definition")));
}
} else if (test (skeys::mirror_key) || test (lkeys::mirror_key)) {
mirror = true;
if (inst_made) {
throw tl::Exception (tl::to_string (tr ("mirror key must come before pin key in subcircuit definition")));
}
} else if (test (skeys::scale_key) || test (lkeys::scale_key)) {
Brace br2 (this);
mag = read_double ();
br2.done ();
if (inst_made) {
throw tl::Exception (tl::to_string (tr ("scale key must come before pin key in subcircuit definition")));
}
} else if (test (skeys::pin_key) || test (lkeys::pin_key)) {
Brace br2 (this);
std::string pname;
read_word_or_quoted (pname);
std::string netname;
read_word_or_quoted (netname);
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 = circuit->net_by_name (netname);
if (!net) {
throw tl::Exception (tl::to_string (tr ("Not a valid net name: ")) + netname);
}
subcircuit->connect_pin (sc_pin->id (), net);
db::Net *sc_net = circuit_ref->net_for_pin (sc_pin->id ());
if (sc_net) {
refs.push_back (Connections (net->cluster_id (), sc_net->cluster_id ()));
}
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside subcircuit definition (location, rotation, mirror, scale or pin expected)")));
}
}
br.done ();
double dbu = l2n->internal_layout ()->dbu ();
subcircuit->set_trans (db::DCplxTrans (mag, angle, mirror, db::DVector (dbu * x, dbu * y)));
db::CellInstArray inst (db::CellInst (circuit_ref->cell_index ()), db::ICplxTrans (mag, angle, mirror, db::Vector (x, y)));
db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ());
ccell.insert (inst);
return inst;
}
void
LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc, Layers &layers)
{
Brace br (this);
std::string name;
read_word_or_quoted (name);
size_t tid = std::numeric_limits<size_t>::max ();
const std::vector<db::DeviceTerminalDefinition> &td = dm->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (t->name () == name) {
tid = t->id ();
break;
}
}
// create a terminal unless one with this name already exists
if (tid == std::numeric_limits<size_t>::max ()) {
if (! dc) {
throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + name + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ());
}
db::DeviceTerminalDefinition new_td (name, std::string ());
tid = dc->add_terminal_definition (new_td).id ();
}
db::connected_clusters<db::PolygonRef> &cc = l2n->net_clusters ().clusters_per_cell (dm->cell_index ());
db::local_cluster<db::PolygonRef> &lc = *cc.insert ();
dm->set_cluster_id_for_terminal (tid, lc.id ());
db::Cell &cell = l2n->internal_layout ()->cell (dm->cell_index ());
while (br) {
std::pair<unsigned int, db::PolygonRef> pr = read_geometry (l2n, layers);
lc.add (pr.second, pr.first);
cell.shapes (pr.first).insert (pr.second);
}
br.done ();
}
}

View File

@ -24,10 +24,83 @@
#define HDR_dbLayoutToNetlistReader
#include "dbCommon.h"
#include "dbPolygon.h"
#include "dbCell.h"
#include "tlStream.h"
namespace db {
// ...
namespace l2n_std_reader {
class Layers;
class Brace;
}
class LayoutToNetlist;
class Circuit;
class Cell;
class DeviceModel;
class DeviceClass;
/**
* @brief The base class for a LayoutToNetlist writer
*/
class DB_PUBLIC LayoutToNetlistReaderBase
{
public:
LayoutToNetlistReaderBase () { }
virtual ~LayoutToNetlistReaderBase () { }
virtual void read (db::LayoutToNetlist *l2n) = 0;
};
/**
* @brief The standard writer
*/
class DB_PUBLIC LayoutToNetlistStandardReader
: public LayoutToNetlistReaderBase
{
public:
LayoutToNetlistStandardReader (tl::InputStream &stream);
void read (db::LayoutToNetlist *l2n);
private:
friend class l2n_std_reader::Brace;
typedef l2n_std_reader::Brace Brace;
typedef l2n_std_reader::Layers Layers;
struct Connections
{
Connections (size_t _from_cluster, size_t _to_cluster)
: from_cluster (_from_cluster), to_cluster (_to_cluster)
{ }
size_t from_cluster, to_cluster;
};
tl::TextInputStream m_stream;
std::string m_path;
std::string m_line;
tl::Extractor m_ex;
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);
int read_int ();
db::Coord read_coord ();
double read_double ();
bool at_end ();
void skip ();
void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, Layers &layers);
void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit);
db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs);
db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs);
void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceModel *dm, db::DeviceClass *dc, Layers &layers);
std::pair<unsigned int, db::PolygonRef> read_geometry (db::LayoutToNetlist *l2n, Layers &layers);
};
}

View File

@ -22,107 +22,42 @@
#include "dbLayoutToNetlistWriter.h"
#include "dbLayoutToNetlist.h"
#include "dbLayoutToNetlistFormatDefs.h"
namespace db
{
namespace l2n_std_format
{
// -------------------------------------------------------------------------------------------
// LayoutToNetlistStandardWriter implementation
// std_writer_impl<Keys> implementation
/**
* 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
* description(<text>) - an arbitrary description text
* unit(<unit>) - specifies the database unit [short key: U]
* top(<circuit>) - specifies the name of the top circuit [short key: T]
* layer(<name>) - define a layer [short key: L]
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
* global(<layer> <net> ...) - connects a layer with the given global nets [short key: G]
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
* device(<name> <class> [device-abstract-def])
* - device abstract [short key: D]
*
* [circuit-def]:
*
* net(<name> [geometry-def]) - net geometry [short key: N]
* A net declaration shall be there also if no geometry
* is present
* pin(<name> <net-name>) - outgoing pin connection [short key: P]
* device(<name> <class> [device-def])
* - device with connections [short key: D]
* subcircuit(<name> [subcircuit-def])
* - subcircuit with connections [short key: X]
*
* [geometry-def]:
*
* polygon(<layer> <x> <y> ...) - defines a polygon [short key: P]
* rect(<layer> <left> <bottom> <right> <top>)
* - defines a rectangle [short key: R]
*
* [device-abstract-def]:
*
* terminal(<terminal-name> [geometry-def])
* - specifies the terminal geometry [short key: empty]
*
* [device-def]:
*
* param(<name> <value>) - defines a parameter [short key P]
* abstract(<name>) - links to a geometrical device abstract on top level [short key A]
* terminal(<terminal-name> <net-name>)
* - specifies connection of the terminal with
* a net (short key: empty)
* location(<x> <y>) - location of the device [short key L]
*
* [subcircuit-def]:
*
* location(<x> <y>) - location of the subcircuit [short key L]
* rotation(<angle>) - rotation angle [short key O]
* mirror - if specified, the instance is mirrored before rotation [short key M]
* scale(<mag>) - magnification [short key *]
* pin(<pin-name> <net-name>) - specifies connection of the pin with a net [short key: P]
*/
template <class Keys>
class std_writer_impl
{
public:
std_writer_impl (tl::OutputStream &stream);
static std::string version_key ("version");
static std::string description_key ("description");
static std::string top_key ("top");
static std::string unit_key ("unit");
static std::string layer_key ("layer");
static std::string text_key ("text");
static std::string connect_key ("connect");
static std::string global_key ("global");
static std::string circuit_key ("circuit");
static std::string net_key ("net");
static std::string device_key ("device");
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 abstract_key ("abstract");
static std::string label_key ("label");
static std::string param_key ("param");
static std::string location_key ("location");
static std::string rotation_key ("rotation");
static std::string mirror_key ("mirror");
static std::string scale_key ("scale");
static std::string pin_key ("pin");
static std::string indent1 (" ");
static std::string indent2 (" ");
static std::string endl ("\n");
void write (const db::LayoutToNetlist *l2n);
LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream)
private:
tl::OutputStream *mp_stream;
void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit);
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 db::ICplxTrans &tr, const std::string &lname);
};
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 ..
@ -138,7 +73,8 @@ static std::string name_for_layer (const db::Layout *layout, unsigned int l)
}
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n)
{
bool any = false;
@ -150,17 +86,17 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
*mp_stream << "# General section" << endl;
*mp_stream << "# Lists general definitions." << endl << endl;
if (version > 0) {
*mp_stream << version_key << "(" << version << ")" << endl;
*mp_stream << Keys::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 << unit_key << "(" << ly->dbu () << ")" << 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;
*mp_stream << endl << "# Layer section" << endl;
*mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
*mp_stream << endl << "# Mask 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;
*mp_stream << Keys::layer_key << "(" << name_for_layer (ly, *l) << ")" << endl;
}
*mp_stream << endl << "# Mask layer connectivity" << endl;
@ -169,7 +105,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
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 << connect_key << "(" << name_for_layer (ly, *l);
*mp_stream << Keys::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, *c);
}
@ -188,7 +124,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
*mp_stream << endl << "# Global nets and connectivity" << endl;
any = true;
}
*mp_stream << global_key << "(" << name_for_layer (ly, *l);
*mp_stream << Keys::global_key << "(" << name_for_layer (ly, *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));
}
@ -203,7 +139,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
}
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;
*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;
}
@ -211,16 +147,17 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
*mp_stream << endl << "# Circuit section" << endl;
*mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl;
for (db::Netlist::const_top_down_circuit_iterator i = nl->begin_top_down (); i != nl->end_top_down (); ++i) {
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 << endl << "# Circuit " << x->name () << endl;
*mp_stream << circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
*mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
write (l2n, *x);
*mp_stream << ")" << endl;
}
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit)
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit)
{
if (circuit.begin_nets () != circuit.end_nets ()) {
*mp_stream << endl << indent1 << "# Nets with their geometries" << endl;
@ -234,7 +171,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
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;
*mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl;
}
}
}
@ -265,7 +202,8 @@ void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr)
}
}
void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname)
template <class Keys>
void std_writer_impl<Keys>::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname)
{
db::ICplxTrans t = tr * db::ICplxTrans (s->trans ());
@ -273,14 +211,14 @@ void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const db::IC
if (poly.is_box ()) {
db::Box box = t * poly.box ();
*mp_stream << rect_key << "(" << lname;
*mp_stream << Keys::rect_key << "(" << lname;
*mp_stream << " " << box.left () << " " << box.bottom ();
*mp_stream << " " << box.right () << " " << box.top ();
*mp_stream << ")";
} else {
*mp_stream << polygon_key << "(" << lname;
*mp_stream << Keys::polygon_key << "(" << lname;
if (poly.holes () > 0) {
db::SimplePolygon sp (poly);
write_points (*mp_stream, sp, t);
@ -292,7 +230,8 @@ void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const db::IC
}
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Net &net)
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Net &net)
{
const db::Layout *ly = l2n->internal_layout ();
const db::hier_clusters<db::PolygonRef> &clusters = l2n->net_clusters ();
@ -319,7 +258,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
} else {
if (! any) {
*mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl;
*mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl;
any = true;
}
@ -340,28 +279,30 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
if (any) {
*mp_stream << indent1 << ")" << endl;
} else {
*mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl;
*mp_stream << indent1 << Keys::net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl;
}
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit)
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit)
{
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 ());
*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 << " " << scale_key << "(" << tr.mag () << ")";
*mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")";
}
if (tr.is_mirror ()) {
*mp_stream << " " << mirror_key;
*mp_stream << " " << Keys::mirror_key;
}
if (fabs (tr.angle ()) > 1e-6) {
*mp_stream << " " << rotation_key << "(" << tr.angle () << ")";
*mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")";
}
*mp_stream << " " << location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")";
*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);
@ -378,7 +319,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
} 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 ()) << ")";
*mp_stream << Keys::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;
}
@ -392,7 +333,8 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
*mp_stream << ")" << endl;
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model)
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model)
{
const std::vector<db::DeviceTerminalDefinition> &td = device_model.device_class ()->terminal_definitions ();
@ -402,7 +344,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
*mp_stream << indent1 << terminal_key << "(" << t->name () << endl;
*mp_stream << indent1 << Keys::terminal_key << "(" << t->name () << endl;
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
@ -422,31 +364,55 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
}
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Device &device)
template <class Keys>
void std_writer_impl<Keys>::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 << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ());
*mp_stream << indent2 << location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl;
tl_assert (device.device_model () != 0);
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_model ()->name ()) << endl;
if (device.device_model ()) {
*mp_stream << indent2 << abstract_key << "(" << tl::to_word_or_quoted_string (device.device_model ()->name ()) << ")" << endl;
}
*mp_stream << indent2 << Keys::location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl;
const std::vector<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
*mp_stream << indent2 << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl;
*mp_stream << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl;
}
const std::vector<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
for (std::vector<DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
*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;
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 ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl;
}
}
*mp_stream << indent1 << ")" << endl;
}
}
// -------------------------------------------------------------------------------------------
// LayoutToNetlistStandardWriter implementation
LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version)
: mp_stream (&stream), m_short_version (short_version)
{
// .. nothing yet ..
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *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);
}
}
}

View File

@ -24,18 +24,12 @@
#define HDR_dbLayoutToNetlistWriter
#include "dbCommon.h"
#include "dbPolygon.h"
#include "tlStream.h"
namespace db
{
class LayoutToNetlist;
class Net;
class Circuit;
class SubCircuit;
class Device;
class DeviceModel;
/**
* @brief The base class for a LayoutToNetlist writer
@ -56,19 +50,13 @@ class DB_PUBLIC LayoutToNetlistStandardWriter
: public LayoutToNetlistWriterBase
{
public:
LayoutToNetlistStandardWriter (tl::OutputStream &stream);
LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version);
void write (const db::LayoutToNetlist *l2n);
private:
tl::OutputStream *mp_stream;
void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit);
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 db::ICplxTrans &tr, const std::string &lname);
bool m_short_version;
};
}

View File

@ -307,20 +307,16 @@ void NetlistDeviceExtractor::push_new_devices ()
ps.insert (std::make_pair (m_terminal_id_propname_id, tl::Variant (t->first)));
db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps);
// initialize the local cluster (will not be extracted)
db::local_cluster<db::PolygonRef> *lc = cc.insert ();
lc->add_attr (pi);
dm->set_cluster_id_for_terminal (t->first, lc->id ());
// build the cell shapes and local cluster
// build the cell shapes
for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) {
db::Shapes &shapes = device_cell.shapes (l->first);
for (std::vector<db::PolygonRef>::const_iterator s = l->second.begin (); s != l->second.end (); ++s) {
db::PolygonRef pr = *s;
pr.transform (db::PolygonRef::trans_type (-disp));
shapes.insert (db::PolygonRefWithProperties (pr, pi));
lc->add (*s, l->first);
}
}
}

View File

@ -63,18 +63,44 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
circuits.insert (std::make_pair (c->cell_index (), c.operator-> ()));
}
// reverse lookup for DeviceModel vs. cell index
std::map<db::cell_index_type, db::DeviceModel *> device_models;
for (db::Netlist::device_model_iterator dm = nl.begin_device_models (); dm != nl.end_device_models (); ++dm) {
device_models.insert (std::make_pair (dm->cell_index (), dm.operator-> ()));
}
std::map<db::cell_index_type, std::map<size_t, size_t> > pins_per_cluster_per_cell;
for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) {
if (db::NetlistDeviceExtractor::is_device_cell (*mp_layout, *cid)) {
continue;
}
const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid);
if (clusters.empty ()) {
continue;
}
std::map<db::cell_index_type, db::DeviceModel *>::const_iterator dmc = device_models.find (*cid);
if (dmc != device_models.end ()) {
// make the terminal to cluster ID connections for the device model from the device cells
if (m_terminal_annot_name_id.first) {
for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) {
for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) {
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a);
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
if (j->first == m_terminal_annot_name_id.second) {
dmc->second->set_cluster_id_for_terminal (j->second.to<size_t> (), dc->id ());
}
}
}
}
}
continue;
}
// a cell makes a new circuit (or uses an existing one)
db::Circuit *circuit = 0;

View File

@ -60,10 +60,10 @@ 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)
static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path, bool short_format)
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream);
db::LayoutToNetlistStandardWriter writer (stream, short_format);
writer.write (l2n);
}
@ -277,7 +277,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"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"),
gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), gsi::arg ("short_format", false),
"@brief Writes the extracted netlist to a file.\n"
"This method employs the native format of KLayout.\n"
),

View File

@ -0,0 +1,66 @@
/*
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 "dbLayoutToNetlistReader.h"
#include "dbLayoutToNetlistWriter.h"
#include "dbStream.h"
#include "dbCommonReader.h"
#include "dbNetlistDeviceExtractorClasses.h"
#include "tlUnitTest.h"
#include "tlStream.h"
#include "tlFileUtils.h"
TEST(1_ReaderBasic)
{
db::Layout ly;
db::Cell &tc = ly.cell (ly.add_cell ("TOP"));
db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt");
tl::InputStream is_in (in_path);
db::LayoutToNetlistStandardReader reader (is_in);
reader.read (&l2n);
// verify against the input
std::string path = tmp_file ("tmp_l2nreader_1.txt");
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, false);
writer.write (&l2n);
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}

View File

@ -168,7 +168,7 @@ TEST(1_WriterBasic)
std::string path = tmp_file ("tmp_l2nwriter_1.txt");
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream);
db::LayoutToNetlistStandardWriter writer (stream, false);
writer.write (&l2n);
}

View File

@ -64,7 +64,8 @@ SOURCES = \
dbNetlistDeviceExtractorTests.cc \
dbNetlistDeviceClassesTests.cc \
dbLayoutToNetlistTests.cc \
dbLayoutToNetlistWriterTests.cc
dbLayoutToNetlistWriterTests.cc \
dbLayoutToNetlistReaderTests.cc
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC

View File

@ -37,10 +37,10 @@ connect(nsd diff_cont nsd)
# Device abstracts list the pin shapes of the devices.
device(D$PMOS PMOS
terminal(S
rect(poly -125 -475 125 475)
rect(psd -650 -475 -125 475)
)
terminal(G
rect(psd -650 -475 -125 475)
rect(poly -125 -475 125 475)
)
terminal(D
rect(psd 125 -475 675 475)
@ -48,10 +48,10 @@ device(D$PMOS PMOS
)
device(D$PMOS$1 PMOS
terminal(S
rect(poly -125 -475 125 475)
rect(psd -675 -475 -125 475)
)
terminal(G
rect(psd -675 -475 -125 475)
rect(poly -125 -475 125 475)
)
terminal(D
rect(psd 125 -475 650 475)
@ -59,10 +59,10 @@ device(D$PMOS$1 PMOS
)
device(D$NMOS NMOS
terminal(S
rect(poly -125 -475 125 475)
rect(nsd -650 -475 -125 475)
)
terminal(G
rect(nsd -650 -475 -125 475)
rect(poly -125 -475 125 475)
)
terminal(D
rect(nsd 125 -475 675 475)
@ -70,10 +70,10 @@ device(D$NMOS NMOS
)
device(D$NMOS$1 NMOS
terminal(S
rect(poly -125 -475 125 475)
rect(nsd -675 -475 -125 475)
)
terminal(G
rect(nsd -675 -475 -125 475)
rect(poly -125 -475 125 475)
)
terminal(D
rect(nsd 125 -475 650 475)
@ -83,6 +83,135 @@ device(D$NMOS$1 NMOS
# Circuit section
# Circuits are the hierarchical building blocks of the netlist.
# Circuit INV2
circuit(INV2
# Nets with their geometries
net(IN
rect(poly -525 -250 -275 2250)
rect(poly -1700 1620 -400 1980)
rect(poly -525 -800 -275 800)
rect(poly -525 -475 -275 475)
rect(poly -525 2000 -275 3600)
rect(poly -525 2325 -275 3275)
rect(poly_lbl -801 1799 -799 1801)
rect(poly_cont -1630 1690 -1410 1910)
)
net($2
rect(poly 275 -250 525 2250)
rect(poly 220 820 580 1180)
rect(poly 275 2000 525 3600)
rect(poly 275 2325 525 3275)
rect(poly 275 -800 525 800)
rect(poly 275 -475 525 475)
rect(diff_cont -910 2490 -690 2710)
rect(diff_cont -910 2890 -690 3110)
rect(diff_cont -910 -310 -690 -90)
rect(diff_cont -910 90 -690 310)
rect(poly_cont 290 890 510 1110)
rect(metal1 -800 820 580 1180)
rect(metal1 -980 -420 -620 2420)
rect(metal1 -980 2420 -620 3180)
rect(metal1 -980 -380 -620 380)
rect(psd -1050 2325 -525 3275)
rect(psd -1050 2325 -525 3275)
rect(nsd -1050 -475 -525 475)
rect(nsd -1050 -475 -525 475)
)
net(OUT
rect(diff_cont 690 2890 910 3110)
rect(diff_cont 690 2490 910 2710)
rect(diff_cont 690 90 910 310)
rect(diff_cont 690 -310 910 -90)
polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20)
rect(metal1 620 2420 980 3180)
rect(metal1 620 -380 980 380)
rect(metal1_lbl 799 1799 801 1801)
rect(psd 525 2325 1050 3275)
rect(psd 525 2325 1050 3275)
rect(nsd 525 -475 1050 475)
rect(nsd 525 -475 1050 475)
)
net($4
rect(diff_cont -110 -310 110 -90)
rect(diff_cont -110 90 110 310)
rect(diff_cont -110 90 110 310)
rect(diff_cont -110 -310 110 -90)
rect(metal1 -180 -380 180 380)
rect(metal1 -180 -380 180 380)
rect(via1 -125 -325 125 -75)
rect(via1 -125 75 125 325)
rect(metal2 -1400 -450 1400 450)
rect(nsd -275 -475 275 475)
rect(nsd -275 -475 275 475)
rect(nsd -275 -475 275 475)
)
net($5
rect(diff_cont -110 2490 110 2710)
rect(diff_cont -110 2890 110 3110)
rect(diff_cont -110 2890 110 3110)
rect(diff_cont -110 2490 110 2710)
rect(metal1 -180 2420 180 3180)
rect(metal1 -180 2420 180 3180)
rect(via1 -125 2475 125 2725)
rect(via1 -125 2875 125 3125)
rect(metal2 -1400 2350 1400 3250)
rect(psd -275 2325 275 3275)
rect(psd -275 2325 275 3275)
rect(psd -275 2325 275 3275)
)
# Outgoing pins and their connections to nets
pin(IN IN)
pin($1 $2)
pin(OUT OUT)
pin($3 $4)
pin($4 $5)
# Devices and their connections
device($1 D$PMOS
location(-400 2800)
param(L 0.25)
param(W 0.95)
param(AS 0.49875)
param(AD 0.26125)
terminal(S $2)
terminal(G IN)
terminal(D $5)
)
device($2 D$PMOS$1
location(400 2800)
param(L 0.25)
param(W 0.95)
param(AS 0.26125)
param(AD 0.49875)
terminal(S $5)
terminal(G $2)
terminal(D OUT)
)
device($3 D$NMOS
location(-400 0)
param(L 0.25)
param(W 0.95)
param(AS 0.49875)
param(AD 0.26125)
terminal(S $2)
terminal(G IN)
terminal(D $4)
)
device($4 D$NMOS$1
location(400 0)
param(L 0.25)
param(W 0.95)
param(AS 0.26125)
param(AD 0.49875)
terminal(S $4)
terminal(G $2)
terminal(D OUT)
)
)
# Circuit RINGO
circuit(RINGO
@ -280,61 +409,61 @@ circuit(RINGO
)
# Subcircuits and their connections
subcircuit($1 location(23760 0)
circuit($1 INV2 location(23760 0)
pin(IN $I8)
pin($1 FB)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($2 location(0 0)
circuit($2 INV2 location(0 0)
pin(IN FB)
pin(OUT $I19)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($3 location(2640 0)
circuit($3 INV2 location(2640 0)
pin(IN $I19)
pin(OUT $I1)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($4 location(5280 0)
circuit($4 INV2 location(5280 0)
pin(IN $I1)
pin(OUT $I2)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($5 location(7920 0)
circuit($5 INV2 location(7920 0)
pin(IN $I2)
pin(OUT $I3)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($6 location(10560 0)
circuit($6 INV2 location(10560 0)
pin(IN $I3)
pin(OUT $I4)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($7 location(13200 0)
circuit($7 INV2 location(13200 0)
pin(IN $I4)
pin(OUT $I5)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($8 location(15840 0)
circuit($8 INV2 location(15840 0)
pin(IN $I5)
pin(OUT $I6)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($9 location(18480 0)
circuit($9 INV2 location(18480 0)
pin(IN $I6)
pin(OUT $I7)
pin($3 VSS)
pin($4 VDD)
)
subcircuit($10 location(21120 0)
circuit($10 INV2 location(21120 0)
pin(IN $I7)
pin(OUT $I8)
pin($3 VSS)
@ -342,136 +471,3 @@ circuit(RINGO
)
)
# Circuit INV2
circuit(INV2
# Nets with their geometries
net(IN
rect(poly -525 -250 -275 2250)
rect(poly -1700 1620 -400 1980)
rect(poly -525 -800 -275 800)
rect(poly -525 -475 -275 475)
rect(poly -525 2000 -275 3600)
rect(poly -525 2325 -275 3275)
rect(poly_lbl -801 1799 -799 1801)
rect(poly_cont -1630 1690 -1410 1910)
)
net($2
rect(poly 275 -250 525 2250)
rect(poly 220 820 580 1180)
rect(poly 275 2000 525 3600)
rect(poly 275 2325 525 3275)
rect(poly 275 -800 525 800)
rect(poly 275 -475 525 475)
rect(diff_cont -910 2490 -690 2710)
rect(diff_cont -910 2890 -690 3110)
rect(diff_cont -910 -310 -690 -90)
rect(diff_cont -910 90 -690 310)
rect(poly_cont 290 890 510 1110)
rect(metal1 -800 820 580 1180)
rect(metal1 -980 -420 -620 2420)
rect(metal1 -980 2420 -620 3180)
rect(metal1 -980 -380 -620 380)
rect(psd -1050 2325 -525 3275)
rect(psd -1050 2325 -525 3275)
rect(nsd -1050 -475 -525 475)
rect(nsd -1050 -475 -525 475)
)
net(OUT
rect(diff_cont 690 2890 910 3110)
rect(diff_cont 690 2490 910 2710)
rect(diff_cont 690 90 910 310)
rect(diff_cont 690 -310 910 -90)
polygon(metal1 800 20 800 380 940 380 940 1620 620 1620 620 2420 980 2420 980 1980 1300 1980 1300 20)
rect(metal1 620 2420 980 3180)
rect(metal1 620 -380 980 380)
rect(metal1_lbl 799 1799 801 1801)
rect(psd 525 2325 1050 3275)
rect(psd 525 2325 1050 3275)
rect(nsd 525 -475 1050 475)
rect(nsd 525 -475 1050 475)
)
net($4
rect(diff_cont -110 -310 110 -90)
rect(diff_cont -110 90 110 310)
rect(diff_cont -110 90 110 310)
rect(diff_cont -110 -310 110 -90)
rect(metal1 -180 -380 180 380)
rect(metal1 -180 -380 180 380)
rect(via1 -125 -325 125 -75)
rect(via1 -125 75 125 325)
rect(metal2 -1400 -450 1400 450)
rect(nsd -275 -475 275 475)
rect(nsd -275 -475 275 475)
rect(nsd -275 -475 275 475)
)
net($5
rect(diff_cont -110 2490 110 2710)
rect(diff_cont -110 2890 110 3110)
rect(diff_cont -110 2890 110 3110)
rect(diff_cont -110 2490 110 2710)
rect(metal1 -180 2420 180 3180)
rect(metal1 -180 2420 180 3180)
rect(via1 -125 2475 125 2725)
rect(via1 -125 2875 125 3125)
rect(metal2 -1400 2350 1400 3250)
rect(psd -275 2325 275 3275)
rect(psd -275 2325 275 3275)
rect(psd -275 2325 275 3275)
)
# Outgoing pins and their connections to nets
pin(IN IN)
pin($1 $2)
pin(OUT OUT)
pin($3 $4)
pin($4 $5)
# Devices and their connections
device($1 PMOS
location(-400 2800)
abstract(D$PMOS)
param(L 0.25)
param(W 0.95)
param(AS 0.49875)
param(AD 0.26125)
terminal(S $2)
terminal(G IN)
terminal(D $5)
)
device($2 PMOS
location(400 2800)
abstract(D$PMOS$1)
param(L 0.25)
param(W 0.95)
param(AS 0.26125)
param(AD 0.49875)
terminal(S $5)
terminal(G $2)
terminal(D OUT)
)
device($3 NMOS
location(-400 0)
abstract(D$NMOS)
param(L 0.25)
param(W 0.95)
param(AS 0.49875)
param(AD 0.26125)
terminal(S $2)
terminal(G IN)
terminal(D $4)
)
device($4 NMOS
location(400 0)
abstract(D$NMOS$1)
param(L 0.25)
param(W 0.95)
param(AS 0.26125)
param(AD 0.49875)
terminal(S $4)
terminal(G $2)
terminal(D OUT)
)
)