mirror of https://github.com/KLayout/klayout.git
WIP: some refactoring, added label recognition to net extraction, test enhancements.
This commit is contained in:
parent
024907e7ef
commit
8f568641e0
|
|
@ -177,7 +177,7 @@ struct DeepShapeStore::LayoutHolder
|
|||
static size_t s_instance_count = 0;
|
||||
|
||||
DeepShapeStore::DeepShapeStore ()
|
||||
: m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16)
|
||||
: m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_text_property_name (), m_text_enlargement (-1)
|
||||
{
|
||||
++s_instance_count;
|
||||
}
|
||||
|
|
@ -192,6 +192,16 @@ DeepShapeStore::~DeepShapeStore ()
|
|||
m_layouts.clear ();
|
||||
}
|
||||
|
||||
void DeepShapeStore::set_text_enlargement (int enl)
|
||||
{
|
||||
m_text_enlargement = enl;
|
||||
}
|
||||
|
||||
void DeepShapeStore::set_text_property_name (const tl::Variant &pn)
|
||||
{
|
||||
m_text_property_name = pn;
|
||||
}
|
||||
|
||||
bool DeepShapeStore::is_valid_layout_index (unsigned int n) const
|
||||
{
|
||||
return (n < (unsigned int) m_layouts.size () && m_layouts[n] != 0);
|
||||
|
|
@ -280,11 +290,14 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator
|
|||
|
||||
}
|
||||
|
||||
unsigned int layer_index = m_layouts[layout_index]->layout.insert_layer ();
|
||||
m_layouts[layout_index]->builder.set_target_layer (layer_index);
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
|
||||
|
||||
unsigned int layer_index = layout.insert_layer ();
|
||||
builder.set_target_layer (layer_index);
|
||||
|
||||
// The chain of operators for producing clipped and reduced polygon references
|
||||
db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& m_layouts[layout_index]->layout);
|
||||
db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& layout, m_text_enlargement, m_text_property_name);
|
||||
db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count);
|
||||
db::ClippingHierarchyBuilderShapeReceiver clip (&red);
|
||||
|
||||
|
|
@ -293,35 +306,36 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator
|
|||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Building working hierarchy")));
|
||||
|
||||
m_layouts[layout_index]->builder.set_shape_receiver (&clip);
|
||||
db::RecursiveShapeIterator (si).push (& m_layouts[layout_index]->builder);
|
||||
m_layouts[layout_index]->builder.set_shape_receiver (0);
|
||||
builder.set_shape_receiver (&clip);
|
||||
db::RecursiveShapeIterator (si).push (& builder);
|
||||
builder.set_shape_receiver (0);
|
||||
|
||||
} catch (...) {
|
||||
m_layouts[layout_index]->builder.set_shape_receiver (0);
|
||||
builder.set_shape_receiver (0);
|
||||
throw;
|
||||
}
|
||||
|
||||
return DeepLayer (this, layout_index, layer_index);
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer)
|
||||
const db::CellMapping &
|
||||
DeepShapeStore::cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell)
|
||||
{
|
||||
const db::Layout *source_layout = deep_layer.layout ();
|
||||
const db::Layout *source_layout = &m_layouts [layout_index]->layout;
|
||||
if (source_layout->begin_top_down () == source_layout->end_top_cells ()) {
|
||||
// empty source - nothing to do.
|
||||
return;
|
||||
static db::CellMapping cm;
|
||||
return cm;
|
||||
}
|
||||
|
||||
db::cell_index_type source_top = *source_layout->begin_top_down();
|
||||
|
||||
db::HierarchyBuilder &original_builder = m_layouts [deep_layer.layout_index ()]->builder;
|
||||
db::HierarchyBuilder &original_builder = m_layouts [layout_index]->builder;
|
||||
|
||||
// Derive a cell mapping for source to target. We reuse any existing mapping for returning the
|
||||
// shapes into the original layout.
|
||||
|
||||
DeliveryMappingCacheKey key (deep_layer.layout_index (), tl::id_of (into_layout), into_cell);
|
||||
DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell);
|
||||
|
||||
std::map<DeliveryMappingCacheKey, db::CellMapping>::iterator cm = m_delivery_mapping_cache.find (key);
|
||||
if (cm == m_delivery_mapping_cache.end ()) {
|
||||
|
|
@ -375,16 +389,34 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db
|
|||
|
||||
}
|
||||
|
||||
// Actually copy the shapes
|
||||
return cm->second;
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer)
|
||||
{
|
||||
const db::Layout *source_layout = deep_layer.layout ();
|
||||
if (source_layout->begin_top_down () == source_layout->end_top_cells ()) {
|
||||
// empty source - nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare the transformation
|
||||
db::ICplxTrans trans (source_layout->dbu () / into_layout->dbu ());
|
||||
|
||||
// prepare a layer map
|
||||
std::map<unsigned int, unsigned int> lm;
|
||||
lm.insert (std::make_pair (deep_layer.layer (), into_layer));
|
||||
|
||||
// prepare a cell mapping
|
||||
const db::CellMapping &cm = cell_mapping_to_original (deep_layer.layout_index (), into_layout, into_cell);
|
||||
|
||||
// prepare a vector with the source cells
|
||||
std::vector <db::cell_index_type> source_cells;
|
||||
source_cells.push_back (source_top);
|
||||
db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm->second.table (), lm);
|
||||
source_cells.push_back (*source_layout->begin_top_down());
|
||||
|
||||
// actually copy the shapes
|
||||
db::copy_shapes (*into_layout, *source_layout, trans, source_cells, cm.table (), lm);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,15 @@ public:
|
|||
*/
|
||||
void insert (const DeepLayer &layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer);
|
||||
|
||||
/**
|
||||
* @brief Gets the cell mapping suitable to returning a layout from the deep shape store into the original layout hierarchy
|
||||
*
|
||||
* If necessary, this method will modify the original layout and add new cells.
|
||||
* "layout_index" is the layout to return to it's original. "into_layout" is the original layout, "into_cell"
|
||||
* the original cell.
|
||||
*/
|
||||
const db::CellMapping &cell_mapping_to_original (size_t layout_index, db::Layout *into_layout, db::cell_index_type into_cell);
|
||||
|
||||
/**
|
||||
* @brief For testing
|
||||
*/
|
||||
|
|
@ -274,6 +283,42 @@ public:
|
|||
return m_max_area_ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the text property name
|
||||
*
|
||||
* If set to a non-null variant, text strings are attached to the generated boxes
|
||||
* as properties with this particular name. This option has an effect only if the
|
||||
* text_enlargement property is not negative.
|
||||
* By default, the name is empty.
|
||||
*/
|
||||
void set_text_property_name (const tl::Variant &pn);
|
||||
|
||||
/**
|
||||
* @brief Gets the text property name
|
||||
*/
|
||||
const tl::Variant &text_property_name () const
|
||||
{
|
||||
return m_text_property_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the text enlargement value
|
||||
*
|
||||
* If set to a non-negative value, text objects are converted to boxes with the
|
||||
* given enlargement (width = 2 * enlargement). The box centers are identical
|
||||
* to the original location of the text.
|
||||
* If this value is negative (the default), texts are ignored.
|
||||
*/
|
||||
void set_text_enlargement (int enl);
|
||||
|
||||
/**
|
||||
* @brief Gets the text enlargement value
|
||||
*/
|
||||
int text_enlargement () const
|
||||
{
|
||||
return m_text_enlargement;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DeepLayer;
|
||||
|
||||
|
|
@ -295,6 +340,8 @@ private:
|
|||
int m_threads;
|
||||
double m_max_area_ratio;
|
||||
size_t m_max_vertex_count;
|
||||
tl::Variant m_text_property_name;
|
||||
int m_text_enlargement;
|
||||
tl::Mutex m_lock;
|
||||
|
||||
struct DeliveryMappingCacheKey
|
||||
|
|
|
|||
|
|
@ -1016,13 +1016,37 @@ private:
|
|||
} else if (x->second != y->second) {
|
||||
|
||||
// join two superclusters
|
||||
x->second->insert (y->second->begin (), y->second->end ());
|
||||
for (typename std::set<id_type>::const_iterator i = y->second->begin (); i != y->second->end (); ++i) {
|
||||
std::set<id_type> &yset = *y->second;
|
||||
x->second->insert (yset.begin (), yset.end ());
|
||||
for (typename std::set<id_type>::const_iterator i = yset.begin (); i != yset.end (); ++i) {
|
||||
m_cm2join_map [*i] = x->second;
|
||||
}
|
||||
y->second->clear ();
|
||||
yset.clear (); // TODO: no longer required, but we can't delete it, as we just have a pointer .. replace pointer by iterator!
|
||||
|
||||
}
|
||||
|
||||
#if defined(DEBUG_HIER_NETWORK_PROCESSOR)
|
||||
// concistency check for debugging
|
||||
for (typename std::map<id_type, std::set<id_type> *>::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) {
|
||||
tl_assert (j->second->find (j->first) != j->second->end ());
|
||||
}
|
||||
|
||||
for (typename std::list<std::set<id_type> >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
|
||||
for (typename std::set<id_type>::const_iterator j = i->begin(); j != i->end(); ++j) {
|
||||
tl_assert(m_cm2join_map.find (*j) != m_cm2join_map.end ());
|
||||
tl_assert(m_cm2join_map[*j] == i.operator->());
|
||||
}
|
||||
}
|
||||
|
||||
// the sets must be disjunct
|
||||
std::set<id_type> all;
|
||||
for (typename std::list<std::set<id_type> >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
|
||||
for (typename std::set<id_type>::const_iterator j = i->begin(); j != i->end(); ++j) {
|
||||
tl_assert(all.find (*j) == all.end());
|
||||
all.insert(*j);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ public:
|
|||
typedef typename T::box_type box_type;
|
||||
typedef db::unstable_box_tree<box_type, T, db::box_convert<T> > tree_type;
|
||||
typedef typename tree_type::flat_iterator shape_iterator;
|
||||
typedef unsigned int attr_id;
|
||||
typedef size_t attr_id;
|
||||
typedef std::set<attr_id> attr_set;
|
||||
typedef attr_set::const_iterator attr_iterator;
|
||||
|
||||
|
|
@ -317,6 +317,14 @@ public:
|
|||
return m_clusters.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the cluster set is empty
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_clusters.empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the clusters touching a given region
|
||||
*/
|
||||
|
|
@ -554,6 +562,14 @@ public:
|
|||
return m_connections.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the cluster set is empty
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return local_clusters<T>::empty () && m_connections.empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the given cluster ID is a root cluster
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -492,18 +492,40 @@ ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db
|
|||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout)
|
||||
: mp_layout (layout)
|
||||
PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement, const tl::Variant &text_prop_name)
|
||||
: mp_layout (layout), m_text_enlargement (text_enlargement), m_make_text_prop (false), m_text_prop_id (0)
|
||||
{
|
||||
// nothing yet ..
|
||||
if (! text_prop_name.is_nil ()) {
|
||||
m_text_prop_id = layout->properties_repository ().prop_name_id (text_prop_name);
|
||||
m_make_text_prop = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) {
|
||||
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
target->insert (db::PolygonRef (poly, mp_layout->shape_repository ()));
|
||||
|
||||
} else if (shape.is_text () && m_text_enlargement >= 0) {
|
||||
|
||||
db::Polygon poly (shape.text_trans () * db::Box (-m_text_enlargement, -m_text_enlargement, m_text_enlargement, m_text_enlargement));
|
||||
db::PolygonRef pref (poly, mp_layout->shape_repository ());
|
||||
|
||||
if (m_make_text_prop) {
|
||||
|
||||
db::PropertiesRepository::properties_set ps;
|
||||
ps.insert (std::make_pair (m_text_prop_id, tl::Variant (shape.text_string ())));
|
||||
db::properties_id_type pid = mp_layout->properties_repository ().properties_id (ps);
|
||||
|
||||
target->insert (db::PolygonRefWithProperties (pref, pid));
|
||||
|
||||
} else {
|
||||
target->insert (pref);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver
|
|||
: public HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout);
|
||||
PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement = -1, const tl::Variant &text_prop_name = tl::Variant ());
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Box &shape, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
|
|
@ -143,6 +143,9 @@ public:
|
|||
|
||||
private:
|
||||
db::Layout *mp_layout;
|
||||
int m_text_enlargement;
|
||||
bool m_make_text_prop;
|
||||
db::property_names_id_type m_text_prop_id;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const
|
|||
db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons;
|
||||
|
||||
mp_layout = &layout;
|
||||
m_layers = layers;
|
||||
|
||||
// terminal properties are kept in property index 0
|
||||
m_propname_id = mp_layout->properties_repository ().prop_name_id (tl::Variant (int (0)));
|
||||
|
|
@ -207,9 +208,11 @@ Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index)
|
|||
return device;
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Polygon &polygon)
|
||||
void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t geometry_index, const db::Polygon &polygon)
|
||||
{
|
||||
tl_assert (mp_layout != 0);
|
||||
tl_assert (geometry_index < m_layers.size ());
|
||||
unsigned int layer_index = m_layers [geometry_index];
|
||||
|
||||
// Build a property set for the DeviceTerminalProperty
|
||||
db::PropertiesRepository::properties_set ps;
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ private:
|
|||
db::cell_index_type m_cell_index;
|
||||
db::Circuit *mp_circuit;
|
||||
std::vector<db::DeviceClass *> m_device_classes;
|
||||
std::vector<unsigned int> m_layers;
|
||||
unsigned int m_device_name_index;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -149,3 +149,38 @@ TEST(2_RefCounting)
|
|||
dl2 = db::DeepLayer ();
|
||||
EXPECT_EQ (store.is_valid_layout_index (lyi1), false);
|
||||
}
|
||||
|
||||
TEST(3_TextTreatment)
|
||||
{
|
||||
db::DeepShapeStore store;
|
||||
db::Layout layout;
|
||||
|
||||
unsigned int l1 = layout.insert_layer ();
|
||||
db::cell_index_type c1 = layout.add_cell ("C1");
|
||||
layout.cell (c1).shapes (l1).insert (db::Text ("TEXT", db::Trans (db::Vector (1000, 2000))));
|
||||
|
||||
db::DeepLayer dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1));
|
||||
EXPECT_EQ (store.layouts (), (unsigned int) 1);
|
||||
|
||||
EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).empty (), true);
|
||||
|
||||
store.set_text_enlargement (1);
|
||||
dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1));
|
||||
EXPECT_EQ (store.layouts (), (unsigned int) 1);
|
||||
|
||||
EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1));
|
||||
EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999)");
|
||||
|
||||
store.set_text_property_name (tl::Variant ("text"));
|
||||
dl1 = store.create_polygon_layer (db::RecursiveShapeIterator (layout, layout.cell (c1), l1));
|
||||
EXPECT_EQ (store.layouts (), (unsigned int) 1);
|
||||
|
||||
EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).size (), size_t (1));
|
||||
EXPECT_EQ (dl1.initial_cell ()->shapes (dl1.layer ()).begin (db::ShapeIterator::All)->to_string (), "polygon (999,1999;999,2001;1001,2001;1001,1999) prop_id=1");
|
||||
|
||||
const db::Layout *dss_layout = store.const_layout (0);
|
||||
db::PropertiesRepository::properties_set ps = dss_layout->properties_repository ().properties (1);
|
||||
EXPECT_EQ (ps.size (), size_t (1));
|
||||
EXPECT_EQ (dss_layout->properties_repository ().prop_name (ps.begin ()->first).to_string (), "text");
|
||||
EXPECT_EQ (ps.begin ()->second.to_string (), "TEXT");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,20 +34,24 @@
|
|||
#include "dbWriter.h"
|
||||
#include "dbCommonReader.h"
|
||||
#include "dbTestSupport.h"
|
||||
#include "dbNetlistProperty.h"
|
||||
#include "dbCellMapping.h"
|
||||
|
||||
#include "tlUnitTest.h"
|
||||
#include "tlString.h"
|
||||
#include "tlFileUtils.h"
|
||||
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
|
||||
class MOSFETExtractor
|
||||
: public db::NetlistDeviceExtractor
|
||||
{
|
||||
public:
|
||||
MOSFETExtractor (db::Layout *debug_out)
|
||||
MOSFETExtractor (db::Netlist &nl, db::Layout *debug_out)
|
||||
: db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0)
|
||||
{
|
||||
initialize (&nl);
|
||||
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));
|
||||
|
|
@ -67,11 +71,12 @@ public:
|
|||
|
||||
virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
|
||||
{
|
||||
tl_assert (layers.size () == 3);
|
||||
tl_assert (layers.size () == 4);
|
||||
|
||||
unsigned int lpdiff = layers [0];
|
||||
unsigned int lndiff = layers [1];
|
||||
unsigned int gate = layers [2];
|
||||
// not used for device recognition: poly (3), but used for producing the gate terminals
|
||||
|
||||
// The layer definition is pdiff, ndiff, gate
|
||||
db::Connectivity conn;
|
||||
|
|
@ -109,7 +114,8 @@ public:
|
|||
bool is_pmos = ! rpdiff_on_gate.empty ();
|
||||
|
||||
db::Region &diff = (is_pmos ? rpdiff_on_gate : rndiff_on_gate);
|
||||
unsigned int terminal_layer_index = (is_pmos ? 0 : 1);
|
||||
unsigned int terminal_geometry_index = (is_pmos ? 0 : 1);
|
||||
unsigned int gate_geometry_index = 3;
|
||||
unsigned int device_class_index = (is_pmos ? 0 /*PMOS*/ : 1 /*NMOS*/);
|
||||
|
||||
if (diff.size () != 2) {
|
||||
|
|
@ -142,10 +148,12 @@ public:
|
|||
|
||||
device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n));
|
||||
|
||||
define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_layer_index, *d);
|
||||
define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d);
|
||||
|
||||
}
|
||||
|
||||
define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p);
|
||||
|
||||
// output the device for debugging
|
||||
device_out (device, diff, rgate);
|
||||
|
||||
|
|
@ -215,52 +223,456 @@ 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));
|
||||
lmap.map (ly.get_properties (lid), lid);
|
||||
return lid;
|
||||
}
|
||||
|
||||
// @@@ TODO: move somewhere else
|
||||
static unsigned int layer_of (const db::Region ®ion)
|
||||
{
|
||||
// TODO: this is clumsy ...
|
||||
db::DeepRegion *dr = dynamic_cast<db::DeepRegion *> (region.delegate ());
|
||||
tl_assert (dr != 0);
|
||||
|
||||
return dr->deep_layer ().layer ();
|
||||
}
|
||||
|
||||
// @@@ TODO: move somewhere else
|
||||
static db::Layout &layout_of (const db::Region ®ion)
|
||||
{
|
||||
// TODO: this is clumsy ...
|
||||
db::DeepRegion *dr = dynamic_cast<db::DeepRegion *> (region.delegate ());
|
||||
tl_assert (dr != 0);
|
||||
|
||||
db::DeepLayer dl = dr->deep_layer ();
|
||||
tl_assert (dl.layout () != 0);
|
||||
return *dl.layout ();
|
||||
}
|
||||
|
||||
// @@@ TODO: move somewhere else
|
||||
static db::Layout &layout_of (const std::vector<db::Region *> ®ions)
|
||||
{
|
||||
db::Layout *layout = 0;
|
||||
for (std::vector<db::Region *>::const_iterator r = regions.begin (); r != regions.end (); ++r) {
|
||||
db::Layout &l = layout_of (**r);
|
||||
if (! layout) {
|
||||
layout = &l;
|
||||
} else {
|
||||
tl_assert (layout == &l);
|
||||
}
|
||||
}
|
||||
|
||||
tl_assert (layout != 0);
|
||||
return *layout;
|
||||
}
|
||||
|
||||
// @@@ TODO: move somewhere else
|
||||
static db::Cell &cell_of (const db::Region ®ion)
|
||||
{
|
||||
// TODO: this is clumsy ...
|
||||
db::DeepRegion *dr = dynamic_cast<db::DeepRegion *> (region.delegate ());
|
||||
tl_assert (dr != 0);
|
||||
|
||||
db::DeepLayer dl = dr->deep_layer ();
|
||||
tl_assert (dl.initial_cell () != 0);
|
||||
return *dl.initial_cell ();
|
||||
}
|
||||
|
||||
// @@@ TODO: move somewhere else
|
||||
static db::Cell &cell_of (const std::vector<db::Region *> ®ions)
|
||||
{
|
||||
db::Cell *cell = 0;
|
||||
|
||||
for (std::vector<db::Region *>::const_iterator r = regions.begin (); r != regions.end (); ++r) {
|
||||
db::Cell &c = cell_of (**r);
|
||||
if (! cell) {
|
||||
cell = &c;
|
||||
} else {
|
||||
tl_assert (cell == &c);
|
||||
}
|
||||
}
|
||||
|
||||
tl_assert (cell != 0);
|
||||
return *cell;
|
||||
}
|
||||
|
||||
// @@@ TODO: move somewhere else
|
||||
class NetExtractor
|
||||
{
|
||||
public:
|
||||
typedef db::hier_clusters<db::PolygonRef> hier_clusters_type;
|
||||
typedef db::connected_clusters<db::PolygonRef> connected_clusters_type;
|
||||
typedef db::local_cluster<db::PolygonRef> local_cluster_type;
|
||||
|
||||
NetExtractor ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist *nl)
|
||||
{
|
||||
tl::Variant terminal_property_name (0); // @@@ take somewhere else
|
||||
|
||||
// only works for singular-layout stores currently. This rules out layers from different sources
|
||||
// and clipping.
|
||||
tl_assert (dss.layouts () == 1);
|
||||
const db::Layout *layout = dss.const_layout (0);
|
||||
|
||||
tl_assert (layout->cells () != 0);
|
||||
const db::Cell &cell = layout->cell (*layout->begin_top_down ());
|
||||
|
||||
// gets the text annotation property ID
|
||||
std::pair<bool, db::property_names_id_type> text_annot_name_id (false, 0);
|
||||
if (! dss.text_property_name ().is_nil ()) {
|
||||
text_annot_name_id = layout->properties_repository ().get_id_of_name (dss.text_property_name ());
|
||||
}
|
||||
|
||||
// gets the device terminal annotation property ID
|
||||
std::pair<bool, db::property_names_id_type> terminal_annot_name_id (false, 0);
|
||||
if (! terminal_property_name.is_nil ()) {
|
||||
terminal_annot_name_id = layout->properties_repository ().get_id_of_name (terminal_property_name);
|
||||
}
|
||||
|
||||
m_net_clusters.build (*layout, cell, db::ShapeIterator::Polygons, conn);
|
||||
|
||||
std::map<db::cell_index_type, db::Circuit *> circuits;
|
||||
// some circuits may be there because of device extraction
|
||||
for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) {
|
||||
// @@@ TODO: what if the circuits don't have a cell index?
|
||||
circuits.insert (std::make_pair (c->cell_index (), c.operator-> ()));
|
||||
}
|
||||
|
||||
std::map<db::cell_index_type, std::map<size_t, size_t> > pins_per_cluster;
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator cid = layout->begin_bottom_up (); cid != layout->end_bottom_up (); ++cid) {
|
||||
|
||||
const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid);
|
||||
if (clusters.empty ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// a cell makes a new circuit (or uses an existing one)
|
||||
|
||||
db::Circuit *circuit = 0;
|
||||
|
||||
std::map<db::cell_index_type, db::Circuit *>::const_iterator k = circuits.find (*cid);
|
||||
if (k == circuits.end ()) {
|
||||
circuit = new db::Circuit ();
|
||||
nl->add_circuit (circuit);
|
||||
circuit->set_name (layout->cell_name (*cid));
|
||||
circuit->set_cell_index (*cid);
|
||||
circuits.insert (std::make_pair (*cid, circuit));
|
||||
} else {
|
||||
circuit = k->second;
|
||||
}
|
||||
|
||||
std::map<size_t, size_t> &c2p = pins_per_cluster [*cid];
|
||||
|
||||
std::map<db::InstElement, db::SubCircuit *> subcircuits;
|
||||
|
||||
for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) {
|
||||
|
||||
db::Net *net = new db::Net ();
|
||||
net->set_cluster_id (*c);
|
||||
// @@@ TODO: set name
|
||||
circuit->add_net (net);
|
||||
|
||||
if (! clusters.is_root (*c)) {
|
||||
|
||||
// a non-root cluster makes a pin
|
||||
db::Pin pin (net->name ());
|
||||
size_t pin_id = circuit->add_pin (pin).id ();
|
||||
net->add_pin (db::NetPinRef (pin_id));
|
||||
c2p.insert (std::make_pair (*c, pin_id));
|
||||
circuit->connect_pin (pin_id, net);
|
||||
|
||||
}
|
||||
|
||||
const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (*c);
|
||||
for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) {
|
||||
|
||||
db::SubCircuit *subcircuit = 0;
|
||||
db::cell_index_type ccid = i->inst ().inst_ptr.cell_index ();
|
||||
|
||||
std::map<db::InstElement, db::SubCircuit *>::const_iterator j = subcircuits.find (i->inst ());
|
||||
if (j == subcircuits.end ()) {
|
||||
|
||||
// make subcircuit if required
|
||||
|
||||
std::map<db::cell_index_type, db::Circuit *>::const_iterator k = circuits.find (ccid);
|
||||
tl_assert (k != circuits.end ()); // because we walk bottom-up
|
||||
|
||||
// @@@ name?
|
||||
subcircuit = new db::SubCircuit (k->second);
|
||||
db::CplxTrans dbu_trans (layout->dbu ());
|
||||
subcircuit->set_trans (dbu_trans * i->inst ().complex_trans () * dbu_trans.inverted ());
|
||||
circuit->add_sub_circuit (subcircuit);
|
||||
subcircuits.insert (std::make_pair (i->inst (), subcircuit));
|
||||
|
||||
} else {
|
||||
subcircuit = j->second;
|
||||
}
|
||||
|
||||
// create the pin connection to the subcircuit
|
||||
std::map<db::cell_index_type, std::map<size_t, size_t> >::const_iterator icc2p = pins_per_cluster.find (ccid);
|
||||
tl_assert (icc2p != pins_per_cluster.end ());
|
||||
std::map<size_t, size_t>::const_iterator ip = icc2p->second.find (i->id ());
|
||||
tl_assert (ip != icc2p->second.end ());
|
||||
subcircuit->connect_pin (ip->second, net);
|
||||
|
||||
}
|
||||
|
||||
// collect the properties - we know that the cluster attributes are property ID's because the
|
||||
// cluster processor converts shape property IDs to attributes
|
||||
const local_cluster_type &lc = clusters.cluster_by_id (*c);
|
||||
for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) {
|
||||
|
||||
// @@@ TODO: needs refactoring!!!
|
||||
// -> use two distinct and reserved property name ID's for names (=string) and device terminal refs (=single number) instead
|
||||
// of the scary DeviceTerminalProperty (pointer!!!)
|
||||
const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (*a);
|
||||
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
|
||||
|
||||
if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) {
|
||||
|
||||
if (j->second.is_user<db::NetlistProperty> ()) {
|
||||
const db::NetlistProperty *np = &j->second.to_user<db::NetlistProperty> ();
|
||||
const db::DeviceTerminalProperty *tp = dynamic_cast<const db::DeviceTerminalProperty *> (np);
|
||||
const db::NetNameProperty *nnp = dynamic_cast<const db::NetNameProperty *> (np);
|
||||
if (tp) {
|
||||
const_cast<db::Device *> (tp->terminal_ref ().device ())->connect_terminal (tp->terminal_ref ().terminal_id (), net);
|
||||
} else if (nnp) {
|
||||
net->set_name (nnp->name ());
|
||||
}
|
||||
}
|
||||
|
||||
} else if (text_annot_name_id.first && j->first == text_annot_name_id.second) {
|
||||
|
||||
std::string n = j->second.to_string ();
|
||||
if (! n.empty ()) {
|
||||
if (! net->name ().empty ()) {
|
||||
n = net->name () + "," + n;
|
||||
}
|
||||
net->set_name (n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const hier_clusters_type clusters () const
|
||||
{
|
||||
return m_net_clusters;
|
||||
}
|
||||
|
||||
private:
|
||||
hier_clusters_type m_net_clusters;
|
||||
};
|
||||
|
||||
// @@@ TODO: move this somewhere else
|
||||
static std::string net_name (const db::Net *net)
|
||||
{
|
||||
if (! net) {
|
||||
return "(null)";
|
||||
} else if (net->name ().empty ()) {
|
||||
if (net->cluster_id () > std::numeric_limits<size_t>::max () / 2) {
|
||||
return "$I" + tl::to_string ((std::numeric_limits<size_t>::max () - net->cluster_id ()) + 1);
|
||||
} else {
|
||||
return "$" + tl::to_string (net->cluster_id ());
|
||||
}
|
||||
} else {
|
||||
return net->name ();
|
||||
}
|
||||
}
|
||||
|
||||
// @@@ TODO: refactor. This is inefficient. Give an ID automatically.
|
||||
static std::string device_name (const db::Device &device, const db::Circuit &circuit)
|
||||
{
|
||||
if (device.name ().empty ()) {
|
||||
int id = 1;
|
||||
for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices () && d.operator-> () != &device; ++d, ++id)
|
||||
;
|
||||
return "$" + tl::to_string (id);
|
||||
} else {
|
||||
return device.name ();
|
||||
}
|
||||
}
|
||||
|
||||
// @@@ TODO: refactor. This is inefficient. Give an ID automatically.
|
||||
static std::string subcircuit_name (const db::SubCircuit &subcircuit, const db::Circuit &circuit)
|
||||
{
|
||||
if (subcircuit.name ().empty ()) {
|
||||
int id = 1;
|
||||
for (db::Circuit::const_sub_circuit_iterator d = circuit.begin_sub_circuits (); d != circuit.end_sub_circuits () && d.operator-> () != &subcircuit; ++d, ++id)
|
||||
;
|
||||
return "$" + tl::to_string (id);
|
||||
} else {
|
||||
return subcircuit.name ();
|
||||
}
|
||||
}
|
||||
|
||||
// @@@ TODO: refactor. This is inefficient. Give an ID automatically.
|
||||
static std::string pin_name (const db::Pin &pin, const db::Circuit &circuit)
|
||||
{
|
||||
if (pin.name ().empty ()) {
|
||||
int id = 1;
|
||||
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins () && p.operator-> () != &pin; ++p, ++id)
|
||||
;
|
||||
return "$" + tl::to_string (id);
|
||||
} else {
|
||||
return pin.name ();
|
||||
}
|
||||
}
|
||||
|
||||
// @@@ TODO: move this somewhere else
|
||||
|
||||
static void dump_nets (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 () + "_" + net_name (n.operator-> ());
|
||||
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 std::string netlist2string (const db::Netlist &nl)
|
||||
{
|
||||
std::string res;
|
||||
for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) {
|
||||
|
||||
std::string ps;
|
||||
for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) {
|
||||
if (! ps.empty ()) {
|
||||
ps += ",";
|
||||
}
|
||||
ps += pin_name (*p, *c) + "=" + net_name (c->net_for_pin (p->id ()));
|
||||
}
|
||||
|
||||
res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n";
|
||||
|
||||
// @@@ good for debugging
|
||||
#if 0
|
||||
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
|
||||
res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) {
|
||||
std::string ts;
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
|
||||
if (t != td.begin ()) {
|
||||
ts += ",";
|
||||
}
|
||||
ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ()));
|
||||
}
|
||||
res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d, *c) + " (" + ts + ")\n";
|
||||
}
|
||||
|
||||
for (db::Circuit::const_sub_circuit_iterator sc = c->begin_sub_circuits (); sc != c->end_sub_circuits (); ++sc) {
|
||||
std::string ps;
|
||||
const db::SubCircuit &subcircuit = *sc;
|
||||
for (db::Circuit::const_pin_iterator p = sc->circuit ()->begin_pins (); p != sc->circuit ()->end_pins (); ++p) {
|
||||
if (p != sc->circuit ()->begin_pins ()) {
|
||||
ps += ",";
|
||||
}
|
||||
const db::Pin &pin = *p;
|
||||
ps += pin_name (pin, *subcircuit.circuit ()) + "=" + net_name (subcircuit.net_for_pin (pin.id ()));
|
||||
}
|
||||
res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc, *c) + " (" + ps + ")\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TEST(1_DeviceNetExtraction)
|
||||
{
|
||||
bool write_debug = true;
|
||||
|
||||
db::Layout ly;
|
||||
unsigned int nwell, active, poly;
|
||||
|
||||
db::LayerProperties p;
|
||||
db::LayerMap lmap;
|
||||
|
||||
p.layer = 1;
|
||||
p.datatype = 0;
|
||||
lmap.map (db::LDPair (p.layer, p.datatype), nwell = ly.insert_layer ());
|
||||
ly.set_properties (nwell, p);
|
||||
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);
|
||||
|
||||
p.layer = 2;
|
||||
p.datatype = 0;
|
||||
lmap.map (db::LDPair (p.layer, p.datatype), active = ly.insert_layer ());
|
||||
ly.set_properties (active, p);
|
||||
{
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
|
||||
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
|
||||
|
||||
p.layer = 3;
|
||||
p.datatype = 0;
|
||||
lmap.map (db::LDPair (p.layer, p.datatype), poly = ly.insert_layer ());
|
||||
ly.set_properties (poly, p);
|
||||
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");
|
||||
|
||||
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);
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (ly, options);
|
||||
}
|
||||
|
||||
db::Cell &tc = ly.cell (*ly.begin_top_down ());
|
||||
|
||||
db::DeepShapeStore dss;
|
||||
dss.set_text_enlargement (1);
|
||||
dss.set_text_property_name (tl::Variant ("LABEL"));
|
||||
|
||||
// original layers
|
||||
db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss);
|
||||
db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss);
|
||||
db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss);
|
||||
db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss);
|
||||
db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss);
|
||||
db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss);
|
||||
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
|
||||
db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss);
|
||||
db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss);
|
||||
db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss);
|
||||
db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss);
|
||||
|
||||
// derived regions
|
||||
db::Region rgate = ractive & rpoly;
|
||||
|
|
@ -287,16 +699,98 @@ TEST(1_DeviceNetExtraction)
|
|||
// NOTE: the device extractor will add more debug layers for the transistors:
|
||||
// 20/0 -> Diffusion
|
||||
// 21/0 -> Gate
|
||||
MOSFETExtractor ex (write_debug ? &ly : 0);
|
||||
ex.initialize (&nl);
|
||||
MOSFETExtractor ex (nl, &ly);
|
||||
|
||||
std::vector<db::Region *> region_ptrs;
|
||||
region_ptrs.push_back (&rpdiff);
|
||||
region_ptrs.push_back (&rndiff);
|
||||
region_ptrs.push_back (&rgate);
|
||||
region_ptrs.push_back (&rpoly);
|
||||
|
||||
ex.extract (region_ptrs);
|
||||
|
||||
// perform the net extraction
|
||||
|
||||
NetExtractor net_ex;
|
||||
|
||||
db::Connectivity conn;
|
||||
// Intra-layer
|
||||
conn.connect (layer_of (rpdiff));
|
||||
conn.connect (layer_of (rndiff));
|
||||
conn.connect (layer_of (rpoly));
|
||||
conn.connect (layer_of (rdiff_cont));
|
||||
conn.connect (layer_of (rpoly_cont));
|
||||
conn.connect (layer_of (rmetal1));
|
||||
conn.connect (layer_of (rvia1));
|
||||
conn.connect (layer_of (rmetal2));
|
||||
// Inter-layer
|
||||
conn.connect (layer_of (rpdiff), layer_of (rdiff_cont));
|
||||
conn.connect (layer_of (rndiff), layer_of (rdiff_cont));
|
||||
conn.connect (layer_of (rpoly), layer_of (rpoly_cont));
|
||||
conn.connect (layer_of (rpoly_cont), layer_of (rmetal1));
|
||||
conn.connect (layer_of (rdiff_cont), layer_of (rmetal1));
|
||||
conn.connect (layer_of (rmetal1), layer_of (rvia1));
|
||||
conn.connect (layer_of (rvia1), layer_of (rmetal2));
|
||||
conn.connect (layer_of (rpoly), layer_of (rpoly_lbl)); // attaches labels
|
||||
conn.connect (layer_of (rmetal1), layer_of (rmetal1_lbl)); // attaches labels
|
||||
conn.connect (layer_of (rmetal2), layer_of (rmetal2_lbl)); // attaches labels
|
||||
|
||||
// extract the nets
|
||||
|
||||
net_ex.extract_nets (dss, conn, &nl);
|
||||
|
||||
// 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
|
||||
std::map<unsigned int, unsigned int> dump_map;
|
||||
dump_map [layer_of (rpdiff) ] = ly.insert_layer (db::LayerProperties (210, 0));
|
||||
dump_map [layer_of (rndiff) ] = ly.insert_layer (db::LayerProperties (211, 0));
|
||||
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
|
||||
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
|
||||
dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0));
|
||||
dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0));
|
||||
dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0));
|
||||
dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0));
|
||||
|
||||
// @@@ we should not have to do this -> can be taken from dss?
|
||||
db::CellMapping cm;
|
||||
const db::Layout &ex_layout = layout_of (rpoly);
|
||||
const db::Cell &ex_cell = cell_of (rpoly);
|
||||
cm.create_from_names_full (ly, tc.cell_index (), ex_layout, ex_cell.cell_index ());
|
||||
|
||||
// write nets to layout
|
||||
dump_nets (nl, net_ex.clusters (), ly, dump_map, cm);
|
||||
|
||||
// compare netlist as string
|
||||
EXPECT_EQ (netlist2string (nl),
|
||||
"Circuit RINGO ():\n"
|
||||
" XINV2 $1 ($1=$I8,$2=FB,$3=OSC,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $2 ($1=FB,$2=$I38,$3=$I19,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $3 ($1=$I19,$2=$I39,$3=$I1,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $4 ($1=$I1,$2=$I40,$3=$I2,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $5 ($1=$I2,$2=$I41,$3=$I3,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $6 ($1=$I3,$2=$I42,$3=$I4,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $7 ($1=$I4,$2=$I43,$3=$I5,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $8 ($1=$I5,$2=$I44,$3=$I6,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $9 ($1=$I6,$2=$I45,$3=$I7,$4=VSS,$5=VDD)\n"
|
||||
" XINV2 $10 ($1=$I7,$2=$I46,$3=$I8,$4=VSS,$5=VDD)\n"
|
||||
"Circuit INV2 ($1=IN,$2=$2,$3=OUT,$4=$4,$5=$5):\n"
|
||||
" DPMOS 1 (S=$2,G=IN,D=$5)\n"
|
||||
" DPMOS 2 (S=$5,G=$2,D=OUT)\n"
|
||||
" DNMOS 3 (S=$2,G=IN,D=$4)\n"
|
||||
" DNMOS 4 (S=$4,G=$2,D=OUT)\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"
|
||||
);
|
||||
|
||||
// compare the collected test data
|
||||
|
||||
std::string au = tl::testsrc ();
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue