WIP: some refactoring, added label recognition to net extraction, test enhancements.

This commit is contained in:
Matthias Koefferlein 2018-12-27 00:20:21 +01:00
parent 024907e7ef
commit 8f568641e0
11 changed files with 738 additions and 61 deletions

View File

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

View File

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

View File

@ -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
}
/**

View File

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

View File

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

View File

@ -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;
};
/**

View File

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

View File

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

View File

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

View File

@ -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 &region)
{
// 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 &region)
{
// 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 *> &regions)
{
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 &region)
{
// 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 *> &regions)
{
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.