mirror of https://github.com/KLayout/klayout.git
WIP: added one more level of abstraction to layout-to-netlist extraction (db::LayoutToNetlist) for easier use.
This commit is contained in:
parent
b512f628bc
commit
16a2b1982d
|
|
@ -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") {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 ®ion) 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 ®ion) 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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -360,6 +360,28 @@ RecursiveShapeIterator::confine_region (const region_type ®ion)
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -46,7 +46,9 @@
|
|||
|
||||
static unsigned int layer_of (const db::Region ®ion)
|
||||
{
|
||||
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 ®ion) 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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue