WIP: Refined output format for l2n

This commit is contained in:
Matthias Koefferlein 2019-01-16 00:49:51 +01:00
parent 4cb8982ca2
commit 438f50091f
15 changed files with 431 additions and 66 deletions

View File

@ -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;

View File

@ -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 "$<id>" 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.

View File

@ -29,19 +29,25 @@ namespace db
// --------------------------------------------------------------------------------
// DeviceModel class implementation
DeviceModel::DeviceModel ()
: m_name (), mp_device_class (0), m_cell_index (std::numeric_limits<db::cell_index_type>::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<db::cell_index_type>::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<db::cell_index_type>::max ()), mp_netlist (0)
{
// .. nothing yet ..
}
DeviceModel::DeviceModel (const DeviceModel &other)
: tl::Object (other), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
: tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits<db::cell_index_type>::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;
}

View File

@ -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<size_t> m_terminal_cluster_ids;
Netlist *mp_netlist;

View File

@ -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(<number>) - file format version
* description(<text>) - an arbitrary description text
* dbu(<dbu>) - specifies the database unit [short key: D]
* 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> ...) - defines a circuit (cell) [short key: X]
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
* device(<name> <class> [device-footprint-def])
* - device footprint [short key: D]
*
* Inside the circuit:
* [circuit-def]:
*
* net(<name> ...) - specifies net geometry [short key: N]
* device(<name> <class> ...) - defines a device [short key: D]
* subcircuit(<name> ...) - defines a subcircuit [short key: X]
* 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]
*
* Inside a net:
* [geometry-def]:
*
* polygon(<layer> <x> <y> ...) - defines a polygon [short key: P]
* rect(<layer> <left> <bottom> <right> <top>)
* - defines a rectangle [short key: R]
*
* Inside a device:
* [device-footprint-def]:
*
* terminal(<terminal-name> [geometry-def])
* - specifies the terminal geometry [short key: empty]
*
* [device-def]:
*
* param(<name> <value>) - defines a parameter [short key P]
* footprint(<name>) - links to a geometrical device footprint on top level [short key F]
* 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]
*
* Inside a subcircuit:
* [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
* pin(<pin-name> <net-name>) - 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<db::PolygonRef> &lc = clusters.clusters_per_cell (circuit->cell_index ()).cluster_by_id (net.cluster_id ());
for (db::local_cluster<db::PolygonRef>::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<db::DeviceTerminalDefinition> &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<db::PolygonRef> &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<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
const db::local_cluster<db::PolygonRef> &lc = clusters.clusters_per_cell (device_model.cell_index ()).cluster_by_id (device_model.cluster_id_for_terminal (t->id ()));
for (db::local_cluster<db::PolygonRef>::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<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
for (std::vector<DeviceParameterDefinition>::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<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
for (std::vector<DeviceTerminalDefinition>::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;
}
}

View File

@ -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);
};
}

View File

@ -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 ());

View File

@ -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 ();
}
}
}

View File

@ -56,6 +56,12 @@ public:
return m_name;
}
/**
* @brief Gets a name which always non-empty
* This method will pick a name like "$<id>" 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)
*/

View File

@ -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;

View File

@ -134,6 +134,12 @@ public:
return m_name;
}
/**
* @brief Gets a name which always non-empty
* This method will pick a name like "$<id>" if the explicit name is empty.
*/
std::string expanded_name () const;
/**
* @brief Sets the transformation describing the subcircuit
*

View File

@ -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<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::constructor ("new", &make_l2n, gsi::arg ("iter"),
"@brief The constructor\n"
@ -254,6 +263,10 @@ Class<db::LayoutToNetlist> 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"

View File

@ -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<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().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<unsigned int> ()));
std::auto_ptr<db::Region> rnwell (l2n.make_layer (nwell));
std::auto_ptr<db::Region> ractive (l2n.make_layer (active));
std::auto_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly));
std::auto_ptr<db::Region> rpoly_lbl (l2n.make_text_layer (poly_lbl));
std::auto_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont));
std::auto_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont));
std::auto_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1));
std::auto_ptr<db::Region> rmetal1_lbl (l2n.make_text_layer (metal1_lbl));
std::auto_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1));
std::auto_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2));
std::auto_ptr<db::Region> 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
}

View File

@ -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");

View File

@ -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