WIP: added one more level of abstraction to layout-to-netlist extraction (db::LayoutToNetlist) for easier use.

This commit is contained in:
Matthias Koefferlein 2018-12-30 17:53:46 +01:00
parent b512f628bc
commit 16a2b1982d
12 changed files with 1005 additions and 95 deletions

View File

@ -151,7 +151,8 @@ SOURCES = \
gsiDeclDbNetlistDeviceClasses.cc \
gsiDeclDbNetlistDeviceExtractor.cc \
gsiDeclDbHierNetworkProcessor.cc \
dbNetlistDeviceExtractorClasses.cc
dbNetlistDeviceExtractorClasses.cc \
dbLayoutToNetlist.cc
HEADERS = \
dbArray.h \
@ -266,7 +267,8 @@ HEADERS = \
dbNetlistDeviceClasses.h \
dbNetlistDeviceExtractor.h \
dbNetlistExtractor.h \
dbNetlistDeviceExtractorClasses.h
dbNetlistDeviceExtractorClasses.h \
dbLayoutToNetlist.h
!equals(HAVE_QT, "0") {

View File

@ -113,6 +113,34 @@ DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell,
const_cast<db::DeepShapeStore *> (mp_store.get ())->insert (*this, into_layout, into_cell, into_layer);
}
bool DeepLayer::operator< (const DeepLayer &other) const
{
if (mp_store.get () != other.mp_store.get ()) {
return mp_store.get () < other.mp_store.get ();
}
if (m_layout != other.m_layout) {
return m_layout < other.m_layout;
}
if (m_layer != other.m_layer) {
return m_layer < other.m_layer;
}
return false;
}
bool DeepLayer::operator== (const DeepLayer &other) const
{
if (mp_store.get () != other.mp_store.get ()) {
return false;
}
if (m_layout != other.m_layout) {
return false;
}
if (m_layer != other.m_layer) {
return false;
}
return true;
}
db::Layout &
DeepLayer::layout ()
{

View File

@ -77,6 +77,16 @@ public:
*/
DeepLayer &operator= (const DeepLayer &other);
/**
* @brief Less operator
*/
bool operator< (const DeepLayer &other) const;
/**
* @brief Equality operator
*/
bool operator== (const DeepLayer &other) const;
/**
* @brief Gets the layout object
* The return value is guaranteed to be non-null.

View File

@ -0,0 +1,291 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbCommon.h"
#include "dbLayoutToNetlist.h"
#include "dbDeepRegion.h"
#include "dbShapeRepository.h"
#include "dbCellMapping.h"
namespace db
{
static bool is_deep (const db::Region &r)
{
return dynamic_cast<const db::DeepRegion *> (r.delegate ()) != 0;
}
// the iterator provides the hierarchical selection (enabling/disabling cells etc.)
LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter)
: m_iter (iter), m_netlist_extracted (false)
{
// check the iterator
if (iter.has_complex_region () || iter.region () != db::Box::world ()) {
throw tl::Exception (tl::to_string (tr ("The netlist extractor cannot work on clipped layouts")));
}
m_dss.set_text_enlargement (1);
m_dss.set_text_property_name (tl::Variant ("LABEL"));
}
void LayoutToNetlist::set_threads (int n)
{
m_dss.set_threads (n);
}
void LayoutToNetlist::set_area_ratio (double ar)
{
m_dss.set_max_area_ratio (ar);
}
void LayoutToNetlist::set_max_vertex_count (size_t n)
{
m_dss.set_max_vertex_count (n);
}
db::Region *LayoutToNetlist::make_layer (unsigned int layer_index)
{
db::RecursiveShapeIterator si (m_iter);
si.set_layer (layer_index);
si.shape_flags (db::ShapeIterator::All);
return new db::Region (si, m_dss);
}
db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index)
{
db::RecursiveShapeIterator si (m_iter);
si.set_layer (layer_index);
si.shape_flags (db::ShapeIterator::Texts);
return new db::Region (si, m_dss);
}
db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index)
{
db::RecursiveShapeIterator si (m_iter);
si.set_layer (layer_index);
si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes);
return new db::Region (si, m_dss);
}
void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::Region *> &layers)
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! mp_netlist.get ()) {
mp_netlist.reset (new db::Netlist ());
}
extractor.extract(m_dss, layers, mp_netlist.get ());
}
void LayoutToNetlist::connect (const db::Region &l)
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! is_deep (l)) {
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in intra-layer connectivity for netlist extraction"))));
}
// we need to keep a reference, so we can safely delete the region
db::DeepLayer dl (l);
m_dlrefs.insert (dl);
m_conn.connect (dl.layer ());
}
void LayoutToNetlist::connect (const db::Region &a, const db::Region &b)
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! is_deep (a)) {
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in inter-layer connectivity (first layer) for netlist extraction"))));
}
if (! is_deep (b)) {
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in inter-layer connectivity (second layer) for netlist extraction"))));
}
// we need to keep a reference, so we can safely delete the region
db::DeepLayer dla (a), dlb (b);
m_dlrefs.insert (dla);
m_dlrefs.insert (dlb);
m_conn.connect (dla.layer (), dlb.layer ());
}
void LayoutToNetlist::extract_netlist ()
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! mp_netlist.get ()) {
mp_netlist.reset (new db::Netlist ());
}
m_netex.extract_nets(m_dss, m_conn, mp_netlist.get ());
m_netlist_extracted = true;
}
const db::Layout *LayoutToNetlist::internal_layout () const
{
return &m_dss.const_layout ();
}
const db::Cell *LayoutToNetlist::internal_top_cell () const
{
return &m_dss.const_initial_cell ();
}
unsigned int LayoutToNetlist::layer_of (const db::Region &region) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (region.delegate ());
if (! dr) {
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in netlist extraction"))));
}
return dr->deep_layer ().layer ();
}
db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell)
{
return m_dss.cell_mapping_to_original (0, &layout, cell.cell_index ());
}
db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell)
{
db::CellMapping cm;
if (layout.cells () == 1) {
cm.create_single_mapping (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ());
} else {
cm.create_from_geometry (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ());
}
return cm;
}
db::Netlist *LayoutToNetlist::netlist () const
{
return mp_netlist.get ();
}
const db::hier_clusters<db::PolygonRef> &LayoutToNetlist::net_clusters () const
{
if (! m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
}
return m_netex.clusters ();
}
db::Region LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const
{
unsigned int lid = layer_of (of_layer);
db::Region res;
if (! recursive) {
const db::Circuit *circuit = net.circuit ();
tl_assert (circuit != 0);
db::cell_index_type ci = circuit->cell_index ();
const db::local_cluster<db::PolygonRef> &lc = m_netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ());
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (lid); !s.at_end (); ++s) {
res.insert (*s);
}
} else {
const db::Circuit *circuit = net.circuit ();
tl_assert (circuit != 0);
db::cell_index_type ci = circuit->cell_index ();
for (db::recursive_cluster_shape_iterator<db::PolygonRef> rci (m_netex.clusters (), lid, ci, net.cluster_id ()); !rci.at_end (); ++rci) {
res.insert (rci->obj ().transformed (db::ICplxTrans (rci->trans ()) * rci.trans ()));
}
}
return res;
}
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point)
{
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point);
}
size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, db::cell_index_type &cell_index_found)
{
db::Box local_box = trans.inverted () * test_cluster.bbox ();
const db::local_clusters<db::PolygonRef> &lcc = net_clusters ().clusters_per_cell (cell->cell_index ());
for (db::local_clusters<db::PolygonRef>::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) {
const db::local_cluster<db::PolygonRef> &lc = *i;
if (lc.interacts (test_cluster, trans, m_conn)) {
cell_index_found = cell->cell_index ();
return lc.id ();
}
}
for (db::Cell::touching_iterator i = cell->begin_touching (local_box); ! i.at_end (); ++i) {
db::ICplxTrans t = trans * i->complex_trans ();
size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, cell_index_found);
if (cluster_id > 0) {
return cluster_id;
}
}
return 0;
}
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point)
{
if (! m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
}
tl_assert (mp_netlist.get ());
unsigned int layer = layer_of (of_region);
// Prepare a test cluster
db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1));
db::GenericRepository sr;
db::local_cluster<db::PolygonRef> test_cluster;
test_cluster.add (db::PolygonRef (db::Polygon (box), sr), layer);
db::cell_index_type ci = 0;
size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, ci);
if (cluster_id > 0) {
db::Circuit *circuit = mp_netlist->circuit_by_cell_index (ci);
tl_assert (circuit != 0);
db::Net *net = circuit->net_by_cluster_id (cluster_id);
tl_assert (net != 0);
return net;
} else {
return 0;
}
}
}

View File

@ -0,0 +1,260 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 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_dbLayout2Netlist
#define _HDR_dbLayout2Netlist
#include "dbCommon.h"
#include "dbCellMapping.h"
#include "dbNetlistExtractor.h"
#include "dbNetlistDeviceExtractor.h"
namespace db
{
/**
* @brief A generic framework for extracting netlists from layouts
*
* This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor
* and more. It is supposed to provide a framework for extracting a netlist from a layout.
*
* The use model of this class consists of five steps which need to be executed in this order.
*
* @ul
* @li Configuration: in this step, the LayoutToNetlist object is created and
* if required, configured. Methods to be used in this step are "set_threads",
* "set_area_ratio" or "set_max_vertex_count". The constructor for the LayoutToNetlist
* object receives a db::RecursiveShapeIterator object which basically supplies the
* hierarchy and the layout taken as input.
* @/li
* @li Preparation
* In this step, the device recognitions and extraction layers are drawn from
* the framework. Derived can now be computed using boolean operations.
* Methods to use in this step are "make_layer" and it's variants.
* Layer preparation is not necessarily required to happen before all
* other steps. Layers can be computed shortly before they are required.
* @/li
* @li Following the preparation, the devices can be extracted using "extract_devices".
* This method needs to be called for each device extractor required. Each time,
* a device extractor needs to be given plus a map of device layers. The device
* layers are device extractor specific. Either original or derived layers
* may be specified here. Layer preparation may happen between calls to "extract_devices".
* @/li
* @li Once the devices are derived, the netlist connectivity can be defined and the
* netlist extracted. The connectivity is defined with "connect" and it's
* flavours. The actual netlist extraction happens with "extract_netlist".
* @/li
* @li After netlist extraction, the information is ready to be retrieved.
* The produced netlist is available with "netlist". The Shapes of a
* specific net are available with "shapes_of_net". "probe_net" allows
* finding a net by probing a specific location.
* @li
*/
class DB_PUBLIC LayoutToNetlist
: public gsi::ObjectBase, public tl::Object
{
public:
/**
* @brief The constructor
*
* See the class description for details.
*/
LayoutToNetlist (const db::RecursiveShapeIterator &iter);
/**
* @brief Sets the number of threads to use for operations which support multiple threads
*/
void set_threads (int n);
/**
* @brief Sets the area_ratio parameter for the hierarchical network processor
* This parameter controls splitting of large polygons in order to reduce the
* error made by the bounding box approximation.
*/
void set_area_ratio (double ar);
/**
* @brief Sets the max_vertex_count parameter for the hierarchical network processor
* This parameter controls splitting of large polygons in order to enhance performance
* for very big polygons.
*/
void set_max_vertex_count (size_t n);
/**
* @brief Creates a new region representing an original layer
* "layer_index" is the layer index of the desired layer in the original layout.
* The Region object returned is a new object and must be deleted by the caller.
* This variant produces polygons and takes texts for net name annotation.
* A variant not taking texts is "make_polygon_layer". A Variant only taking
* texts is "make_text_layer".
*/
db::Region *make_layer (unsigned int layer_index);
/**
* @brief Creates a new region representing an original layer taking texts only
* See "make_layer" for details.
*/
db::Region *make_text_layer (unsigned int layer_index);
/**
* @brief Creates a new region representing an original layer taking polygons and texts
* See "make_layer" for details.
*/
db::Region *make_polygon_layer (unsigned int layer_index);
/**
* @brief Extracts devices
* See the class description for more details.
* This method will run device extraction for the given extractor. The layer map is specific
* for the extractor and uses the region objects derived with "make_layer" and it's variants.
*
* In addition, derived regions can be passed too. Certain limitations apply. It's safe to use
* boolean operations for deriving layers. Other operations are applicable as long as they are
* capable of delivering hierarchical layers.
*
* If errors occur, the device extractor will contain theses errors.
*/
void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::Region *> &layers);
/**
* @brief Defines an intra-layer connection for the given layer.
* The layer is either an original layer created with "make_layer" and it's variants or
* a derived layer. Certain limitations apply. It's safe to use
* boolean operations for deriving layers. Other operations are applicable as long as they are
* capable of delivering hierarchical layers.
*/
void connect (const db::Region &l);
/**
* @brief Defines an inter-layer connection for the given layers.
* The conditions mentioned with intra-layer "connect" apply for this method too.
*/
void connect (const db::Region &a, const db::Region &b);
/**
* @brief Runs the netlist extraction
* See the class description for more details.
*/
void extract_netlist ();
/**
* @brief Gets the internal layout
*/
const db::Layout *internal_layout () const;
/**
* @brief Gets the internal top cell
*/
const db::Cell *internal_top_cell () const;
/**
* @brief Gets the internal layer for a given extraction layer
* This method is required to derive the internal layer index - for example for
* investigating the cluster tree.
*/
unsigned int layer_of (const db::Region &region) const;
/**
* @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.
* CAUTION: may create new cells in "layout".
*/
db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell);
/**
* @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.
* This version will not create new cells in the target layout.
* If the required cells do not exist there yet, flatting will happen.
*/
db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell);
/**
* @brief gets the netlist extracted (0 if no extraction happened yet)
*/
db::Netlist *netlist () const;
/**
* @brief Gets the hierarchical shape clusters derived in the net extraction.
* NOTE: the layer and cell indexes used inside this structure refer to the
* internal layout.
*/
const db::hier_clusters<db::PolygonRef> &net_clusters () const;
/**
* @brief Returns all shapes of a specific net and layer.
* If "recursive" is true, the returned region will contain the shapes of
* all subcircuits too.
*/
db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const;
/**
* @brief Finds the net by probing a specific location on the given layer
*
* This method will find a net looking at the given layer at the specific position.
* It will traverse the hierarchy below if no shape in the requested layer is found
* in the specified location.
*
* If no net is found at all, 0 is returned.
*
* This variant accepts a micrometer-unit location. The location is given in the
* coordinate space of the initial cell.
*/
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point);
/**
* @brief Finds the net by probing a specific location on the given layer
* See the description of the other "probe_net" variant.
* This variant accepts a database-unit location. The location is given in the
* coordinate space of the initial cell.
*/
db::Net *probe_net (const db::Region &of_region, const db::Point &point);
private:
// no copying
LayoutToNetlist (const db::LayoutToNetlist &other);
LayoutToNetlist &operator= (const db::LayoutToNetlist &other);
db::RecursiveShapeIterator m_iter;
db::DeepShapeStore m_dss;
db::Connectivity m_conn;
db::NetlistExtractor m_netex;
std::auto_ptr<db::Netlist> mp_netlist;
std::set<db::DeepLayer> m_dlrefs;
bool m_netlist_extracted;
size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, db::cell_index_type &cell_index_found);
};
}
namespace tl
{
template<> struct tl::type_traits<db::LayoutToNetlist> : public tl::type_traits<void>
{
// mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
}
#endif

View File

@ -186,7 +186,7 @@ public:
* device extraction. See the virtual methods below.
*/
class DB_PUBLIC NetlistDeviceExtractor
: public gsi::ObjectBase
: public gsi::ObjectBase, public tl::Object
{
public:
typedef std::list<db::NetlistDeviceExtractorError> error_list;

View File

@ -360,6 +360,28 @@ RecursiveShapeIterator::confine_region (const region_type &region)
m_needs_reinit = true;
}
void
RecursiveShapeIterator::set_layer (unsigned int layer)
{
if (m_has_layers || m_layer != layer) {
m_has_layers = false;
m_layers.clear ();
m_layer = layer;
m_needs_reinit = true;
}
}
void
RecursiveShapeIterator::set_layers (const std::vector<unsigned int> &layers)
{
if (! m_has_layers || m_layers != layers) {
m_has_layers = true;
m_layers = layers;
m_layer = 0;
m_needs_reinit = true;
}
}
namespace {
struct BoxTreePusher

View File

@ -474,6 +474,20 @@ public:
}
}
/**
* @brief Changes the layer to be traversed
*
* This method must be used before shapes are being retrieved. Using this method may reset the iterator.
*/
void set_layer (unsigned int layer);
/**
* @brief Changes the layers to be traversed
*
* This method must be used before shapes are being retrieved. Using this method may reset the iterator.
*/
void set_layers (const std::vector<unsigned int> &layers);
/**
* @brief Specify the property selector
*

View File

@ -0,0 +1,351 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 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 "dbNetlistDeviceExtractorClasses.h"
#include "dbLayoutToNetlist.h"
#include "dbStream.h"
#include "dbDeepRegion.h"
#include "dbDeepShapeStore.h"
#include "dbReader.h"
#include "dbWriter.h"
#include "dbCommonReader.h"
#include "dbTestSupport.h"
#include "tlUnitTest.h"
#include "tlString.h"
#include "tlFileUtils.h"
#include <memory>
#include <limits>
namespace
{
static std::string device_name (const db::Device &device)
{
if (device.name ().empty ()) {
return "$" + tl::to_string (device.id ());
} else {
return device.name ();
}
}
class MOSFETExtractor
: public db::NetlistDeviceExtractorMOS3Transistor
{
public:
MOSFETExtractor (const std::string &name, db::Layout *debug_out)
: db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0)
{
if (mp_debug_out) {
m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0));
m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0));
}
}
private:
db::Layout *mp_debug_out;
unsigned int m_ldiff, m_lgate;
void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate)
{
if (! mp_debug_out) {
return;
}
std::string cn = layout ()->cell_name (cell_index ());
std::pair<bool, db::cell_index_type> target_cp = mp_debug_out->cell_by_name (cn.c_str ());
tl_assert (target_cp.first);
db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ());
mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ()));
db::Cell &device_cell = mp_debug_out->cell (dci);
for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) {
device_cell.shapes (m_ldiff).insert (*p);
}
for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) {
device_cell.shapes (m_lgate).insert (*p);
}
std::string ps;
const std::vector<db::DeviceParameterDefinition> &pd = device->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (! ps.empty ()) {
ps += ",";
}
ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ()));
}
device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ())));
}
};
}
static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<db::PolygonRef> &clusters, db::Layout &ly, const std::map<unsigned int, unsigned int> &lmap, const db::CellMapping &cmap)
{
for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) {
db::Cell &cell = ly.cell (cmap.cell_mapping (c->cell_index ()));
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
const db::local_cluster<db::PolygonRef> &lc = clusters.clusters_per_cell (c->cell_index ()).cluster_by_id (n->cluster_id ());
bool any_shapes = false;
for (std::map<unsigned int, unsigned int>::const_iterator m = lmap.begin (); m != lmap.end () && !any_shapes; ++m) {
any_shapes = ! lc.begin (m->first).at_end ();
}
if (any_shapes) {
std::string nn = "NET_" + c->name () + "_" + n->expanded_name ();
db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ()));
cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ()));
for (std::map<unsigned int, unsigned int>::const_iterator m = lmap.begin (); m != lmap.end (); ++m) {
db::Shapes &target = net_cell.shapes (m->second);
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (m->first); !s.at_end (); ++s) {
target.insert (*s);
}
}
}
}
}
}
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_Basic)
{
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;
// return the computed layers into the original layout and write it for debugging purposes
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
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);
// NOTE: the device extractor will add more debug layers for the transistors:
// 20/0 -> Diffusion
// 21/0 -> Gate
MOSFETExtractor pmos_ex ("PMOS", &ly);
MOSFETExtractor nmos_ex ("NMOS", &ly);
// 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);
// 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 ();
// debug layers produced for nets
// 202/0 -> Active
// 203/0 -> Poly
// 204/0 -> Diffusion contacts
// 205/0 -> Poly contacts
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
std::map<unsigned int, unsigned int> dump_map;
dump_map [l2n.layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [l2n.layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [l2n.layer_of (*rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [l2n.layer_of (*rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [l2n.layer_of (*rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0));
dump_map [l2n.layer_of (*rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0));
dump_map [l2n.layer_of (*rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0));
dump_map [l2n.layer_of (*rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0));
// write nets to layout
db::CellMapping cm = l2n.cell_mapping_into (ly, tc);
dump_nets_to_layout (*l2n.netlist (), l2n.net_clusters (), ly, dump_map, cm);
// compare netlist as string
EXPECT_EQ (l2n.netlist ()->to_string (),
"Circuit RINGO ():\n"
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n"
" XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n"
" XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n"
" XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n"
" XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n"
" XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n"
" XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n"
" XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n"
" XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n"
" XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n"
"Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n"
" DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
" DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
" XTRANS $1 ($1=$2,$2=$4,$3=IN)\n"
" XTRANS $2 ($1=$2,$2=$5,$3=IN)\n"
" XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n"
" XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n"
"Circuit TRANS ($1=$1,$2=$2,$3=$3):\n"
);
// doesn't do anything here, but we test that this does not destroy anything:
l2n.netlist ()->combine_devices ();
// make pins for named nets of top-level circuits - this way they are not purged
l2n.netlist ()->make_top_level_pins ();
l2n.netlist ()->purge ();
// compare netlist as string
EXPECT_EQ (l2n.netlist ()->to_string (),
"Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n"
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n"
" XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n"
" XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n"
" XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n"
" XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n"
" XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n"
" XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n"
" XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n"
" XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n"
" XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n"
"Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n"
" DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
" DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_au1.gds");
db::compare_layouts (_this, ly, au);
}

View File

@ -46,7 +46,9 @@
static unsigned int layer_of (const db::Region &region)
{
return db::DeepLayer (region).layer ();
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (region.delegate ());
tl_assert (dr != 0);
return dr->deep_layer ().layer ();
}
static std::string device_name (const db::Device &device)
@ -58,6 +60,9 @@ static std::string device_name (const db::Device &device)
}
}
namespace
{
class MOSFETExtractor
: public db::NetlistDeviceExtractorMOS3Transistor
{
@ -108,6 +113,8 @@ private:
}
};
}
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));
@ -362,93 +369,3 @@ TEST(1_DeviceAndNetExtraction)
db::compare_layouts (_this, ly, au);
}
#if 0
// --------------------------------------------------------------------------------------
// An attempt to simplify things.
/*
TODO:
- netlist query functions such as net_by_name, device_by_name, circuit_by_name
- terminal geometry (Polygon) for device, combined device geometry (all terminals)
- netlist manipulation methods (i.e. flatten certain cells, purging etc.)
*/
#include "tlGlobPattern.h"
#include "dbHierNetworkProcessor.h"
namespace db
{
class DB_PUBLIC LayoutToNetlist
{
public:
// the iterator provides the hierarchical selection (enabling/disabling cells etc.)
LayoutToNetlist (const db::RecursiveShapeIterator &iter);
// --- Step 0: configuration
void set_threads (unsigned int n);
void set_area_ratio (double ar);
void set_max_vertex_count (size_t n);
// --- Step 1: preparation
// returns new'd regions
db::Region *make_layer (unsigned int layer_index);
db::Region *make_text_layer (unsigned int layer_index);
db::Region *make_polygon_layer (unsigned int layer_index);
// --- Step 2: device extraction
// after this, the device extractor will have errors if some occured.
void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::Region *> &layers);
// --- Step 3: net extraction
// define connectivity for the netlist extraction
void connect (const db::Region &l);
void connect (const db::Region &a, const db::Region &b);
// runs the netlist extraction
void extract_netlist ();
// --- Step 4: retrieval
// gets the internal layout and cell (0 if not available)
const db::Layout *internal_layout () const;
const db::Cell *internal_top_cell () const;
// gets the internal layer index of the given region
unsigned int layer_of (const db::Region &region) const;
// creates a cell mapping for copying the internal hierarchy to the given layout
// CAUTION: may create new cells in "layout".
db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell);
// creates a cell mapping for copying the internal hierarchy to the given layout
// This version will not create new cells in the target layout.
db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell);
// gets the netlist extracted (0 if no extraction happened yet)
db::Netlist *netlist () const;
// gets the hierarchical clusters of the nets (CAUTION: the layer indexes therein are
// internal layer indexes), same for cell indexes.
// -> NOT GSI
const db::hier_clusters<db::PolygonRef> &net_clusters () const;
// copies the shapes of the given net from a given layer
// (recursive true: include nets from subcircuits)
db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive);
// finds the net by probing a specific location on the given layer looking through the
// hierarchy. Returns 0 if no net was found.
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point);
db::Net *probe_net (const db::Region &of_region, const db::Point &point);
};
}
#endif

View File

@ -428,6 +428,12 @@ TEST(1a)
x = collect(i5, g);
EXPECT_EQ (x, "[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)");
i5.set_layer (1);
x = collect_with_copy(i5, g);
EXPECT_EQ (x, "[$3](101,1;1101,1101)");
x = collect(i5, g);
EXPECT_EQ (x, "[$3](101,1;1101,1101)");
std::set<unsigned int> ll;
db::RecursiveShapeIterator i5a (g, c0, ll, db::Box::world ());
@ -454,6 +460,14 @@ TEST(1a)
EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0");
x = collect(i5cc, g, true);
EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$3](101,1;1101,1101)*1/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0");
std::vector<unsigned int> ll_new;
ll_new.push_back (0);
i5c.set_layers (ll_new);
x = collect_with_copy(i5c, g, true);
EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0");
x = collect(i5c, g, true);
EXPECT_EQ (x, "[$2](0,100;1000,1200)*0/[$3](100,0;1100,1100)*0/[$4](1200,0;2200,1100)*0/[$4](-1200,0;-100,1000)*0");
}
TEST(1b)

View File

@ -63,7 +63,8 @@ SOURCES = \
dbNetlistTests.cc \
dbNetlistExtractorTests.cc \
dbNetlistDeviceExtractorTests.cc \
dbNetlistDeviceClassesTests.cc
dbNetlistDeviceClassesTests.cc \
dbLayoutToNetlistTests.cc
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC