diff --git a/src/db/db/db.pro b/src/db/db/db.pro index bd6371526..b22167cff 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -50,6 +50,10 @@ SOURCES = \ dbManager.cc \ dbMatrix.cc \ dbMemStatistics.cc \ + dbMutableEdgePairs.cc \ + dbMutableEdges.cc \ + dbMutableRegion.cc \ + dbMutableTexts.cc \ dbObject.cc \ dbPath.cc \ dbPCellDeclaration.cc \ @@ -258,6 +262,10 @@ HEADERS = \ dbMatrix.h \ dbMemStatistics.h \ dbMetaInfo.h \ + dbMutableEdgePairs.h \ + dbMutableEdges.h \ + dbMutableRegion.h \ + dbMutableTexts.h \ dbObject.h \ dbObjectTag.h \ dbObjectWithProperties.h \ diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 49b6a1842..9fef782d0 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -186,8 +186,10 @@ void Circuit::rename_pin (size_t id, const std::string &name) const Pin *Circuit::pin_by_name (const std::string &name) const { + std::string nn = mp_netlist ? mp_netlist->normalize_name (name) : name; + for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) { - if (p->name () == name) { + if (p->name () == nn) { return p.operator-> (); } } @@ -331,6 +333,11 @@ void Circuit::remove_pin (size_t id) } } +Net *Circuit::net_by_name (const std::string &name) +{ + return m_net_by_name.object_by (mp_netlist ? mp_netlist->normalize_name (name) : name); +} + void Circuit::add_net (Net *net) { if (! net) { @@ -423,6 +430,11 @@ void Circuit::remove_device (Device *device) m_devices.erase (device); } +Device *Circuit::device_by_name (const std::string &name) +{ + return m_device_by_name.object_by (mp_netlist ? mp_netlist->normalize_name (name) : name); +} + void Circuit::add_subcircuit (SubCircuit *subcircuit) { if (! subcircuit) { @@ -456,6 +468,11 @@ void Circuit::remove_subcircuit (SubCircuit *subcircuit) m_subcircuits.erase (subcircuit); } +SubCircuit *Circuit::subcircuit_by_name (const std::string &name) +{ + return m_subcircuit_by_name.object_by (mp_netlist ? mp_netlist->normalize_name (name) : name); +} + void Circuit::register_ref (SubCircuit *r) { m_refs.push_back (r); diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index e4e459b80..a33be121f 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -496,10 +496,7 @@ public: * * If the name is not valid, null is returned. */ - Net *net_by_name (const std::string &name) - { - return m_net_by_name.object_by (name); - } + Net *net_by_name (const std::string &name); /** * @brief Adds a device to this circuit @@ -556,10 +553,7 @@ public: * * If the name is not valid, null is returned. */ - Device *device_by_name (const std::string &name) - { - return m_device_by_name.object_by (name); - } + Device *device_by_name (const std::string &name); /** * @brief Begin iterator for the devices of the circuit (non-const version) @@ -648,10 +642,7 @@ public: * * If the name is not valid, null is returned. */ - SubCircuit *subcircuit_by_name (const std::string &name) - { - return m_subcircuit_by_name.object_by (name); - } + SubCircuit *subcircuit_by_name (const std::string &name); /** * @brief Begin iterator for the subcircuits of the circuit (non-const version) diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index 96316416a..8792a6505 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -112,31 +112,31 @@ private: DeepEdgePairs::DeepEdgePairs () - : AsIfFlatEdgePairs () + : MutableEdgePairs () { // .. nothing yet .. } DeepEdgePairs::DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss) - : AsIfFlatEdgePairs () + : MutableEdgePairs () { set_deep_layer (dss.create_edge_pair_layer (si)); } DeepEdgePairs::DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans) - : AsIfFlatEdgePairs () + : MutableEdgePairs () { set_deep_layer (dss.create_edge_pair_layer (si, trans)); } DeepEdgePairs::DeepEdgePairs (const DeepEdgePairs &other) - : AsIfFlatEdgePairs (other), db::DeepShapeCollectionDelegateBase (other) + : MutableEdgePairs (other), db::DeepShapeCollectionDelegateBase (other) { // .. nothing yet .. } DeepEdgePairs::DeepEdgePairs (const DeepLayer &dl) - : AsIfFlatEdgePairs () + : MutableEdgePairs () { set_deep_layer (dl); } @@ -151,6 +151,86 @@ EdgePairsDelegate *DeepEdgePairs::clone () const return new DeepEdgePairs (*this); } +void DeepEdgePairs::do_insert (const db::EdgePair &edge_pair) +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + top_cell.shapes (deep_layer ().layer ()).insert (edge_pair); + } + + invalidate_bbox (); + set_is_merged (false); +} + +template +static void transform_deep_layer (db::DeepLayer &deep_layer, const Trans &t) +{ + // TODO: this is a pretty cheap implementation. At least a plain move can be done with orientation variants. + + db::Layout &layout = deep_layer.layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) { + flat_shapes.insert (iter->edge_pair ().transformed (iter.trans ()).transformed (t)); + } + + layout.clear_layer (deep_layer.layer ()); + top_cell.shapes (deep_layer.layer ()).swap (flat_shapes); + + } +} + +void DeepEdgePairs::do_transform (const db::Trans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdgePairs::do_transform (const db::ICplxTrans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdgePairs::do_transform (const db::IMatrix2d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdgePairs::do_transform (const db::IMatrix3d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdgePairs::reserve (size_t) +{ + // Not implemented for deep regions +} + +void DeepEdgePairs::flatten () +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { + flat_shapes.insert (iter->edge_pair ().transformed (iter.trans ())); + } + + layout.clear_layer (deep_layer ().layer ()); + top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); + + } +} + EdgePairsIteratorDelegate *DeepEdgePairs::begin () const { return new DeepEdgePairsIterator (begin_iter ().first); diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index c423407b7..75408ea94 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatEdgePairs.h" +#include "dbMutableEdgePairs.h" #include "dbDeepShapeStore.h" #include "dbEdgePairs.h" @@ -36,7 +36,7 @@ namespace db { * @brief Provides hierarchical edges implementation */ class DB_PUBLIC DeepEdgePairs - : public db::AsIfFlatEdgePairs, public db::DeepShapeCollectionDelegateBase + : public db::MutableEdgePairs, public db::DeepShapeCollectionDelegateBase { public: DeepEdgePairs (); @@ -50,6 +50,17 @@ public: EdgePairsDelegate *clone () const; + virtual void do_insert (const db::EdgePair &edge_pair); + + virtual void do_transform (const db::Trans &t); + virtual void do_transform (const db::ICplxTrans &t); + virtual void do_transform (const db::IMatrix2d &t); + virtual void do_transform (const db::IMatrix3d &t); + + virtual void flatten (); + + virtual void reserve (size_t n); + virtual EdgePairsIteratorDelegate *begin () const; virtual std::pair begin_iter () const; diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index a6d9daea3..9660c10b9 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -123,14 +123,14 @@ private: // DeepEdges implementation DeepEdges::DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, bool as_edges) - : AsIfFlatEdges (), m_merged_edges () + : MutableEdges (), m_merged_edges () { set_deep_layer (dss.create_edge_layer (si, as_edges)); init (); } DeepEdges::DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges, bool merged_semantics) - : AsIfFlatEdges (), m_merged_edges () + : MutableEdges (), m_merged_edges () { set_deep_layer (dss.create_edge_layer (si, as_edges, trans)); init (); @@ -138,7 +138,7 @@ DeepEdges::DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, con } DeepEdges::DeepEdges (const db::Edges &other, DeepShapeStore &dss) - : AsIfFlatEdges (), m_merged_edges () + : MutableEdges (), m_merged_edges () { set_deep_layer (dss.create_from_flat (other)); @@ -147,13 +147,13 @@ DeepEdges::DeepEdges (const db::Edges &other, DeepShapeStore &dss) } DeepEdges::DeepEdges () - : AsIfFlatEdges () + : MutableEdges () { init (); } DeepEdges::DeepEdges (const DeepLayer &dl) - : AsIfFlatEdges () + : MutableEdges () { set_deep_layer (dl); init (); @@ -165,7 +165,7 @@ DeepEdges::~DeepEdges () } DeepEdges::DeepEdges (const DeepEdges &other) - : AsIfFlatEdges (other), DeepShapeCollectionDelegateBase (other), + : MutableEdges (other), DeepShapeCollectionDelegateBase (other), m_merged_edges_valid (other.m_merged_edges_valid), m_is_merged (other.m_is_merged) { @@ -211,6 +211,86 @@ void DeepEdges::merged_semantics_changed () // .. nothing yet .. } +void DeepEdges::do_insert (const db::Edge &edge) +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + top_cell.shapes (deep_layer ().layer ()).insert (edge); + } + + invalidate_bbox (); + set_is_merged (false); +} + +template +static void transform_deep_layer (db::DeepLayer &deep_layer, const Trans &t) +{ + // TODO: this is a pretty cheap implementation. At least a plain move can be done with orientation variants. + + db::Layout &layout = deep_layer.layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) { + flat_shapes.insert (iter->edge ().transformed (iter.trans ()).transformed (t)); + } + + layout.clear_layer (deep_layer.layer ()); + top_cell.shapes (deep_layer.layer ()).swap (flat_shapes); + + } +} + +void DeepEdges::do_transform (const db::Trans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdges::do_transform (const db::ICplxTrans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdges::do_transform (const db::IMatrix2d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdges::do_transform (const db::IMatrix3d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepEdges::reserve (size_t) +{ + // Not implemented for deep regions +} + +void DeepEdges::flatten () +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { + flat_shapes.insert (iter->edge ().transformed (iter.trans ())); + } + + layout.clear_layer (deep_layer ().layer ()); + top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); + + } +} + EdgesIteratorDelegate * DeepEdges::begin () const { diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index a9637a573..08fbda231 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatEdges.h" +#include "dbMutableEdges.h" #include "dbDeepShapeStore.h" #include "dbEdgeBoolean.h" #include "dbEdgePairs.h" @@ -40,7 +40,7 @@ class DeepRegion; * @brief Provides hierarchical edges implementation */ class DB_PUBLIC DeepEdges - : public db::AsIfFlatEdges, public db::DeepShapeCollectionDelegateBase + : public db::MutableEdges, public db::DeepShapeCollectionDelegateBase { public: DeepEdges (); @@ -53,6 +53,17 @@ public: virtual ~DeepEdges (); + virtual void do_transform (const db::Trans &t); + virtual void do_transform (const db::ICplxTrans &t); + virtual void do_transform (const db::IMatrix2d &t); + virtual void do_transform (const db::IMatrix3d &t); + + virtual void flatten (); + + virtual void reserve (size_t n); + + virtual void do_insert (const db::Edge &edge); + EdgesDelegate *clone () const; virtual EdgesIteratorDelegate *begin () const; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index db74b18ff..a4100ef19 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -127,14 +127,14 @@ private: // DeepRegion implementation DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) - : AsIfFlatRegion (), m_merged_polygons () + : MutableRegion (), m_merged_polygons () { set_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count)); init (); } DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics, double area_ratio, size_t max_vertex_count) - : AsIfFlatRegion (), m_merged_polygons () + : MutableRegion (), m_merged_polygons () { set_deep_layer (dss.create_polygon_layer (si, area_ratio, max_vertex_count, trans)); init (); @@ -142,7 +142,7 @@ DeepRegion::DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, c } DeepRegion::DeepRegion (const db::Region &other, DeepShapeStore &dss) - : AsIfFlatRegion (), m_merged_polygons () + : MutableRegion (), m_merged_polygons () { set_deep_layer (dss.create_from_flat (other, false)); @@ -151,13 +151,13 @@ DeepRegion::DeepRegion (const db::Region &other, DeepShapeStore &dss) } DeepRegion::DeepRegion () - : AsIfFlatRegion () + : MutableRegion () { init (); } DeepRegion::DeepRegion (const DeepLayer &dl) - : AsIfFlatRegion () + : MutableRegion () { set_deep_layer (dl); init (); @@ -169,7 +169,7 @@ DeepRegion::~DeepRegion () } DeepRegion::DeepRegion (const DeepRegion &other) - : AsIfFlatRegion (other), DeepShapeCollectionDelegateBase (other), + : MutableRegion (other), DeepShapeCollectionDelegateBase (other), m_merged_polygons_valid (other.m_merged_polygons_valid), m_is_merged (other.m_is_merged) { @@ -220,6 +220,90 @@ void DeepRegion::min_coherence_changed () set_is_merged (false); } +void DeepRegion::do_insert (const db::Polygon &polygon) +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + top_cell.shapes (deep_layer ().layer ()).insert (db::PolygonRef (polygon, layout.shape_repository ())); + } + + invalidate_bbox (); + set_is_merged (false); +} + +template +static void transform_deep_layer (db::DeepLayer &deep_layer, const Trans &t) +{ + // TODO: this is a pretty cheap implementation. At least a plain move can be done with orientation variants. + + db::Layout &layout = deep_layer.layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) { + db::Polygon poly; + iter->polygon (poly); + flat_shapes.insert (poly.transformed (iter.trans ()).transformed (t)); + } + + layout.clear_layer (deep_layer.layer ()); + top_cell.shapes (deep_layer.layer ()).swap (flat_shapes); + + } +} + +void DeepRegion::do_transform (const db::Trans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepRegion::do_transform (const db::ICplxTrans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepRegion::do_transform (const db::IMatrix2d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepRegion::do_transform (const db::IMatrix3d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepRegion::reserve (size_t) +{ + // Not implemented for deep regions +} + +void DeepRegion::flatten () +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { + db::Polygon poly; + iter->polygon (poly); + flat_shapes.insert (poly.transformed (iter.trans ())); + } + + layout.clear_layer (deep_layer ().layer ()); + top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); + + } +} + RegionIteratorDelegate * DeepRegion::begin () const { diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index c15913f2c..38252620d 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatRegion.h" +#include "dbMutableRegion.h" #include "dbDeepShapeStore.h" namespace db { @@ -35,7 +35,7 @@ namespace db { * @brief A deep, polygon-set delegate */ class DB_PUBLIC DeepRegion - : public AsIfFlatRegion, public DeepShapeCollectionDelegateBase + : public MutableRegion, public DeepShapeCollectionDelegateBase { public: typedef db::layer polygon_layer_type; @@ -53,6 +53,17 @@ public: RegionDelegate *clone () const; + virtual void do_insert (const db::Polygon &polygon); + + virtual void do_transform (const db::Trans &t); + virtual void do_transform (const db::ICplxTrans &t); + virtual void do_transform (const db::IMatrix2d &t); + virtual void do_transform (const db::IMatrix3d &t); + + virtual void flatten (); + + virtual void reserve (size_t); + virtual RegionIteratorDelegate *begin () const; virtual RegionIteratorDelegate *begin_merged () const; diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index d57c364e4..733f8ec3e 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -399,6 +399,18 @@ DeepShapeStoreState::max_vertex_count () const static size_t s_instance_count = 0; +static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIterator &si) +{ + unsigned int layer_index = layout.insert_layer (); + + if (si.layout () && si.layer () < si.layout ()->layers ()) { + // try to preserve the layer properties + layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ())); + } + + return layer_index; +} + DeepShapeStore::DeepShapeStore () { ++s_instance_count; @@ -433,7 +445,9 @@ DeepLayer DeepShapeStore::create_from_flat (const db::Region ®ion, bool for_n require_singular (); - unsigned int layer = layout ().insert_layer (); + unsigned int layer = init_layer (layout (), region.iter ()); + + // attempt to initialize the layer properties if (max_area_ratio == 0.0) { max_area_ratio = m_state.max_area_ratio (); @@ -480,7 +494,7 @@ DeepLayer DeepShapeStore::create_from_flat (const db::Edges &edges, const db::IC require_singular (); - unsigned int layer = layout ().insert_layer (); + unsigned int layer = init_layer (layout (), edges.iter ()); db::Shapes *shapes = &initial_cell ().shapes (layer); db::Box world = db::Box::world (); @@ -510,7 +524,7 @@ DeepLayer DeepShapeStore::create_from_flat (const db::Texts &texts, const db::IC require_singular (); - unsigned int layer = layout ().insert_layer (); + unsigned int layer = init_layer (layout (), texts.iter ()); db::Shapes *shapes = &initial_cell ().shapes (layer); db::Box world = db::Box::world (); @@ -780,18 +794,6 @@ void DeepShapeStore::make_layout (unsigned int layout_index, const db::Recursive m_layout_map[std::make_pair (si, trans)] = layout_index; } -static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIterator &si) -{ - unsigned int layer_index = layout.insert_layer (); - - if (si.layout () && si.layer () < si.layout ()->layers ()) { - // try to preserve the layer properties - layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ())); - } - - return layer_index; -} - DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans) { if (max_area_ratio == 0.0) { diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index 1cffe1fdf..9dd0361a9 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -116,31 +116,31 @@ private: }; DeepTexts::DeepTexts () - : AsIfFlatTexts () + : MutableTexts () { // .. nothing yet .. } DeepTexts::DeepTexts (const db::Texts &other, DeepShapeStore &dss) - : AsIfFlatTexts () + : MutableTexts () { set_deep_layer (dss.create_from_flat (other)); } DeepTexts::DeepTexts (const RecursiveShapeIterator &si, DeepShapeStore &dss) - : AsIfFlatTexts () + : MutableTexts () { set_deep_layer (dss.create_text_layer (si)); } DeepTexts::DeepTexts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans) - : AsIfFlatTexts () + : MutableTexts () { set_deep_layer (dss.create_text_layer (si, trans)); } DeepTexts::DeepTexts (const DeepTexts &other) - : AsIfFlatTexts (other), DeepShapeCollectionDelegateBase (other) + : MutableTexts (other), DeepShapeCollectionDelegateBase (other) { // .. nothing yet .. } @@ -156,7 +156,7 @@ DeepTexts::operator= (const DeepTexts &other) } DeepTexts::DeepTexts (const DeepLayer &dl) - : AsIfFlatTexts () + : MutableTexts () { set_deep_layer (dl); } @@ -171,6 +171,90 @@ TextsDelegate *DeepTexts::clone () const return new DeepTexts (*this); } +void DeepTexts::do_insert (const db::Text &text) +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + top_cell.shapes (deep_layer ().layer ()).insert (db::TextRef (text, layout.shape_repository ())); + } + + invalidate_bbox (); + set_is_merged (false); +} + +template +static void transform_deep_layer (db::DeepLayer &deep_layer, const Trans &t) +{ + // TODO: this is a pretty cheap implementation. At least a plain move can be done with orientation variants. + + db::Layout &layout = deep_layer.layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer.layer ()); !iter.at_end (); ++iter) { + db::Text text; + iter->text (text); + flat_shapes.insert (text.transformed (iter.trans ()).transformed (t)); + } + + layout.clear_layer (deep_layer.layer ()); + top_cell.shapes (deep_layer.layer ()).swap (flat_shapes); + + } +} + +void DeepTexts::do_transform (const db::Trans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepTexts::do_transform (const db::ICplxTrans &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepTexts::do_transform (const db::IMatrix2d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepTexts::do_transform (const db::IMatrix3d &t) +{ + transform_deep_layer (deep_layer (), t); + invalidate_bbox (); +} + +void DeepTexts::reserve (size_t) +{ + // Not implemented for deep regions +} + +void DeepTexts::flatten () +{ + db::Layout &layout = deep_layer ().layout (); + if (layout.begin_top_down () != layout.end_top_down ()) { + + db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); + + db::Shapes flat_shapes (layout.is_editable ()); + for (db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); !iter.at_end (); ++iter) { + db::Text text; + iter->text (text); + flat_shapes.insert (db::TextRef (text.transformed (iter.trans ()), layout.shape_repository ())); + } + + layout.clear_layer (deep_layer ().layer ()); + top_cell.shapes (deep_layer ().layer ()).swap (flat_shapes); + + } +} + TextsIteratorDelegate *DeepTexts::begin () const { return new DeepTextsIterator (begin_iter ().first); diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index 8c1057bc0..bda0cf452 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatTexts.h" +#include "dbMutableTexts.h" #include "dbDeepShapeStore.h" #include "dbTexts.h" @@ -36,7 +36,7 @@ namespace db { * @brief Provides hierarchical edges implementation */ class DB_PUBLIC DeepTexts - : public db::AsIfFlatTexts, public db::DeepShapeCollectionDelegateBase + : public db::MutableTexts, public db::DeepShapeCollectionDelegateBase { public: DeepTexts (); @@ -51,6 +51,17 @@ public: TextsDelegate *clone () const; + virtual void do_insert (const db::Text &text); + + virtual void do_transform (const db::Trans &t); + virtual void do_transform (const db::ICplxTrans &t); + virtual void do_transform (const db::IMatrix2d &t); + virtual void do_transform (const db::IMatrix3d &t); + + virtual void flatten (); + + virtual void reserve (size_t n); + virtual TextsIteratorDelegate *begin () const; virtual std::pair begin_iter () const; diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index a3d5d5ea8..9412eb815 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -93,20 +93,20 @@ EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, con template void EdgePairs::insert (const Sh &shape) { - flat_edge_pairs ()->insert (shape); + mutable_edge_pairs ()->insert (shape); } template DB_PUBLIC void EdgePairs::insert (const db::EdgePair &); void EdgePairs::insert (const db::Shape &shape) { - flat_edge_pairs ()->insert (shape); + mutable_edge_pairs ()->insert (shape); } template void EdgePairs::insert (const db::Shape &shape, const T &trans) { - flat_edge_pairs ()->insert (shape, trans); + mutable_edge_pairs ()->insert (shape, trans); } template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::ICplxTrans &); @@ -120,13 +120,18 @@ void EdgePairs::clear () void EdgePairs::reserve (size_t n) { - flat_edge_pairs ()->reserve (n); + mutable_edge_pairs ()->reserve (n); +} + +void EdgePairs::flatten () +{ + mutable_edge_pairs ()->flatten (); } template EdgePairs &EdgePairs::transform (const T &trans) { - flat_edge_pairs ()->transform (trans); + mutable_edge_pairs ()->transform (trans); return *this; } @@ -183,9 +188,9 @@ void EdgePairs::set_delegate (EdgePairsDelegate *delegate) } } -FlatEdgePairs *EdgePairs::flat_edge_pairs () +MutableEdgePairs *EdgePairs::mutable_edge_pairs () { - FlatEdgePairs *edge_pairs = dynamic_cast (mp_delegate); + MutableEdgePairs *edge_pairs = dynamic_cast (mp_delegate); if (! edge_pairs) { edge_pairs = new FlatEdgePairs (); if (mp_delegate) { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index c80d622d7..e2200e465 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -36,7 +36,7 @@ namespace db { class EdgePairFilterBase; -class FlatEdgePairs; +class MutableEdgePairs; class EmptyEdgePairs; class Edges; class Region; @@ -469,10 +469,7 @@ public: * * This method will turn any edge pair collection into a flat one. */ - void flatten () - { - flat_edge_pairs (); - } + void flatten (); /** * @brief Returns true, if the edge pair set has valid edges stored within itself @@ -615,7 +612,7 @@ private: EdgePairsDelegate *mp_delegate; void set_delegate (EdgePairsDelegate *delegate); - FlatEdgePairs *flat_edge_pairs (); + MutableEdgePairs *mutable_edge_pairs (); }; } diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 6e7ffc185..862b1f9ca 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -24,6 +24,7 @@ #include "dbDeepEdges.h" #include "dbOriginalLayerEdges.h" #include "dbEmptyEdges.h" +#include "dbMutableEdges.h" #include "dbFlatEdges.h" #include "dbEdgesUtils.h" #include "dbRegion.h" @@ -133,7 +134,7 @@ Edges::clear () void Edges::reserve (size_t n) { - flat_edges ()->reserve (n); + mutable_edges ()->reserve (n); } void Edges::processed (Region &output, const EdgeToPolygonProcessorBase &filter) const @@ -166,10 +167,16 @@ Edges Edges::centers (length_type length, double fraction) const return Edges (mp_delegate->processed (EdgeSegmentSelector (0, length, fraction))); } +void +Edges::flatten () +{ + mutable_edges ()->flatten (); +} + template Edges &Edges::transform (const T &trans) { - flat_edges ()->transform (trans); + mutable_edges ()->transform (trans); return *this; } @@ -183,7 +190,7 @@ template DB_PUBLIC Edges &Edges::transform (const db::IMatrix3d &); template void Edges::insert (const Sh &shape) { - flat_edges ()->insert (shape); + mutable_edges ()->insert (shape); } template DB_PUBLIC void Edges::insert (const db::Box &); @@ -194,24 +201,25 @@ template DB_PUBLIC void Edges::insert (const db::Edge &); void Edges::insert (const db::Shape &shape) { - flat_edges ()->insert (shape); + mutable_edges ()->insert (shape); } template void Edges::insert (const db::Shape &shape, const T &trans) { - flat_edges ()->insert (shape, trans); + mutable_edges ()->insert (shape, trans); } template DB_PUBLIC void Edges::insert (const db::Shape &, const db::ICplxTrans &); template DB_PUBLIC void Edges::insert (const db::Shape &, const db::Trans &); template DB_PUBLIC void Edges::insert (const db::Shape &, const db::Disp &); -FlatEdges * -Edges::flat_edges () +MutableEdges * +Edges::mutable_edges () { - FlatEdges *edges = dynamic_cast (mp_delegate); + MutableEdges *edges = dynamic_cast (mp_delegate); if (! edges) { + edges = new FlatEdges (); if (mp_delegate) { edges->EdgesDelegate::operator= (*mp_delegate); diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 561a5d9a0..a4cb05ae7 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -35,7 +35,7 @@ namespace db { class EdgeFilterBase; -class FlatEdges; +class MutableEdges; class EmptyEdges; class DeepShapeStore; @@ -1112,10 +1112,7 @@ public: * * This method will turn any edge collection into a flat one. */ - void flatten () - { - flat_edges (); - } + void flatten (); /** * @brief Returns true, if the edge set has valid edges stored within itself @@ -1208,7 +1205,7 @@ private: EdgesDelegate *mp_delegate; void set_delegate (EdgesDelegate *delegate, bool keep_attributes = true); - FlatEdges *flat_edges (); + MutableEdges *mutable_edges(); }; } // namespace db diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index 60a79f167..561aa4829 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -32,7 +32,7 @@ namespace db // FlatEdgePairs implementation FlatEdgePairs::FlatEdgePairs () - : AsIfFlatEdgePairs (), mp_edge_pairs (new db::Shapes (false)) + : MutableEdgePairs (), mp_edge_pairs (new db::Shapes (false)) { // .. nothing yet .. } @@ -43,13 +43,13 @@ FlatEdgePairs::~FlatEdgePairs () } FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other) - : AsIfFlatEdgePairs (other), mp_edge_pairs (other.mp_edge_pairs) + : MutableEdgePairs (other), mp_edge_pairs (other.mp_edge_pairs) { // .. nothing yet .. } FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs) - : AsIfFlatEdgePairs (), mp_edge_pairs (new db::Shapes (edge_pairs)) + : MutableEdgePairs (), mp_edge_pairs (new db::Shapes (edge_pairs)) { // .. nothing yet .. } @@ -205,23 +205,11 @@ FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsig } void -FlatEdgePairs::insert (const db::EdgePair &ep) +FlatEdgePairs::do_insert (const db::EdgePair &ep) { mp_edge_pairs->insert (ep); invalidate_cache (); } -void -FlatEdgePairs::insert (const db::Shape &shape) -{ - if (shape.is_edge_pair ()) { - - db::EdgePair ep; - shape.edge_pair (ep); - insert (ep); - - } -} - } diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h index 9b31fb0f2..f5ef4ffdd 100644 --- a/src/db/db/dbFlatEdgePairs.h +++ b/src/db/db/dbFlatEdgePairs.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatEdgePairs.h" +#include "dbMutableEdgePairs.h" #include "dbShapes.h" #include "dbGenericShapeIterator.h" #include "tlCopyOnWrite.h" @@ -42,7 +42,7 @@ typedef generic_shapes_iterator_delegate FlatEdgePairsIterator; * @brief The delegate for the actual edge pair set implementation */ class DB_PUBLIC FlatEdgePairs - : public AsIfFlatEdgePairs + : public MutableEdgePairs { public: typedef db::layer edge_pair_layer_type; @@ -82,42 +82,30 @@ public: virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; - void insert (const db::EdgePair &edge_pair); - void insert (const db::Shape &shape); + virtual void do_insert (const db::EdgePair &edge_pair); - template - void insert (const db::Shape &shape, const T &trans) + virtual void do_transform (const db::Trans &t) { - if (shape.is_edge_pair ()) { - - db::EdgePair ep; - shape.edge_pair (ep); - ep.transform (trans); - insert (ep); - - } + transform_generic (t); } - template - void insert_seq (const Iter &seq) + virtual void do_transform (const db::ICplxTrans &t) { - for (Iter i = seq; ! i.at_end (); ++i) { - insert (*i); - } + transform_generic (t); } - template - void transform (const Trans &trans) + virtual void do_transform (const db::IMatrix2d &t) { - if (! trans.is_unity ()) { - db::Shapes &ep = *mp_edge_pairs; - for (edge_pair_iterator_type p = ep.template get_layer ().begin (); p != ep.template get_layer ().end (); ++p) { - ep.get_layer ().replace (p, p->transformed (trans)); - } - invalidate_cache (); - } + transform_generic (t); } + virtual void do_transform (const db::IMatrix3d &t) + { + transform_generic (t); + } + + virtual void flatten () { } + db::Shapes &raw_edge_pairs () { return *mp_edge_pairs; } const db::Shapes &raw_edge_pairs () const { return *mp_edge_pairs; } @@ -131,6 +119,18 @@ private: FlatEdgePairs &operator= (const FlatEdgePairs &other); mutable tl::copy_on_write_ptr mp_edge_pairs; + + template + void transform_generic (const Trans &trans) + { + if (! trans.is_unity ()) { + db::Shapes &ep = *mp_edge_pairs; + for (edge_pair_iterator_type p = ep.template get_layer ().begin (); p != ep.template get_layer ().end (); ++p) { + ep.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } }; } diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 689cc5e93..1421f1353 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -33,7 +33,7 @@ namespace db // FlatEdges implementation FlatEdges::FlatEdges () - : AsIfFlatEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)) + : MutableEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)) { init (); } @@ -44,7 +44,7 @@ FlatEdges::~FlatEdges () } FlatEdges::FlatEdges (const FlatEdges &other) - : AsIfFlatEdges (other), mp_edges (other.mp_edges), mp_merged_edges (other.mp_merged_edges) + : MutableEdges (other), mp_edges (other.mp_edges), mp_merged_edges (other.mp_merged_edges) { init (); @@ -53,7 +53,7 @@ FlatEdges::FlatEdges (const FlatEdges &other) } FlatEdges::FlatEdges (const db::Shapes &edges, bool is_merged) - : AsIfFlatEdges (), mp_edges (new db::Shapes (edges)), mp_merged_edges (new db::Shapes (false)) + : MutableEdges (), mp_edges (new db::Shapes (edges)), mp_merged_edges (new db::Shapes (false)) { init (); @@ -61,7 +61,7 @@ FlatEdges::FlatEdges (const db::Shapes &edges, bool is_merged) } FlatEdges::FlatEdges (bool is_merged) - : AsIfFlatEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)) + : MutableEdges (), mp_edges (new db::Shapes (false)), mp_merged_edges (new db::Shapes (false)) { init (); @@ -320,69 +320,7 @@ const db::RecursiveShapeIterator *FlatEdges::iter () const } void -FlatEdges::insert (const db::Box &box) -{ - if (! box.empty () && box.width () > 0 && box.height () > 0) { - - bool was_empty = empty (); - - db::Shapes &e = *mp_edges; - e.insert (db::Edge (box.lower_left (), box.upper_left ())); - e.insert (db::Edge (box.upper_left (), box.upper_right ())); - e.insert (db::Edge (box.upper_right (), box.lower_right ())); - e.insert (db::Edge (box.lower_right (), box.lower_left ())); - - if (was_empty) { - - m_is_merged = true; - update_bbox (box); - - } else { - - m_is_merged = false; - invalidate_cache (); - - } - - } -} - -void -FlatEdges::insert (const db::Path &path) -{ - if (path.points () > 0) { - insert (path.polygon ()); - } -} - -void -FlatEdges::insert (const db::Polygon &polygon) -{ - if (polygon.holes () > 0 || polygon.vertices () > 0) { - db::Shapes &edges = *mp_edges; - for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); - } -} - -void -FlatEdges::insert (const db::SimplePolygon &polygon) -{ - if (polygon.vertices () > 0) { - db::Shapes &edges = *mp_edges; - for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { - edges.insert (*e); - } - m_is_merged = false; - invalidate_cache (); - } -} - -void -FlatEdges::insert (const db::Edge &edge) +FlatEdges::do_insert (const db::Edge &edge) { if (! empty ()) { m_is_merged = false; @@ -392,23 +330,5 @@ FlatEdges::insert (const db::Edge &edge) invalidate_cache (); } -void -FlatEdges::insert (const db::Shape &shape) -{ - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - - db::Polygon poly; - shape.polygon (poly); - insert (poly); - - } else if (shape.is_edge ()) { - - db::Edge edge; - shape.edge (edge); - insert (edge); - - } -} - } diff --git a/src/db/db/dbFlatEdges.h b/src/db/db/dbFlatEdges.h index 353511179..384250641 100644 --- a/src/db/db/dbFlatEdges.h +++ b/src/db/db/dbFlatEdges.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatEdges.h" +#include "dbMutableEdges.h" #include "dbShapes.h" #include "dbShapes2.h" #include "dbGenericShapeIterator.h" @@ -43,7 +43,7 @@ typedef generic_shapes_iterator_delegate FlatEdgesIterator; * @brief A flat, edge-set delegate */ class DB_PUBLIC FlatEdges - : public AsIfFlatEdges + : public MutableEdges { public: typedef db::Edge value_type; @@ -65,6 +65,7 @@ public: } void reserve (size_t); + void flatten () { } virtual EdgesIteratorDelegate *begin () const; virtual EdgesIteratorDelegate *begin_merged () const; @@ -91,60 +92,26 @@ public: virtual const db::RecursiveShapeIterator *iter () const; - void insert (const db::Box &box); - void insert (const db::Path &path); - void insert (const db::SimplePolygon &polygon); - void insert (const db::Polygon &polygon); - void insert (const db::Edge &edge); - void insert (const db::Shape &shape); + void do_insert (const db::Edge &edge); - template - void insert (const db::Shape &shape, const T &trans) + void do_transform (const db::Trans &t) { - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - - db::Polygon poly; - shape.polygon (poly); - poly.transform (trans); - insert (poly); - - } else if (shape.is_edge ()) { - - db::Edge edge; - shape.edge (edge); - edge.transform (trans); - insert (edge); - - } + transform_generic (t); } - template - void insert (const Iter &b, const Iter &e) + void do_transform (const db::ICplxTrans &t) { - reserve (count () + (e - b)); - for (Iter i = b; i != e; ++i) { - insert (*i); - } + transform_generic (t); } - template - void insert_seq (const Iter &seq) + void do_transform (const db::IMatrix2d &t) { - for (Iter i = seq; ! i.at_end (); ++i) { - insert (*i); - } + transform_generic (t); } - template - void transform (const Trans &trans) + void do_transform (const db::IMatrix3d &t) { - if (! trans.is_unity ()) { - db::Shapes &e = *mp_edges; - for (edge_iterator_type p = e.template get_layer ().begin (); p != e.get_layer ().end (); ++p) { - e.get_layer ().replace (p, p->transformed (trans)); - } - invalidate_cache (); - } + transform_generic (t); } db::Shapes &raw_edges () { return *mp_edges; } @@ -168,6 +135,18 @@ private: void init (); void ensure_merged_edges_valid () const; + + template + void transform_generic (const Trans &trans) + { + if (! trans.is_unity ()) { + db::Shapes &e = *mp_edges; + for (edge_iterator_type p = e.template get_layer ().begin (); p != e.get_layer ().end (); ++p) { + e.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } }; } diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 01f3a954a..516fd8a31 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -33,7 +33,7 @@ namespace db // FlatRegion implementation FlatRegion::FlatRegion () - : AsIfFlatRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)) + : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)) { init (); } @@ -44,7 +44,7 @@ FlatRegion::~FlatRegion () } FlatRegion::FlatRegion (const FlatRegion &other) - : AsIfFlatRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons) + : MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons) { init (); @@ -53,7 +53,7 @@ FlatRegion::FlatRegion (const FlatRegion &other) } FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) - : AsIfFlatRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)) + : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)) { init (); @@ -61,7 +61,7 @@ FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) } FlatRegion::FlatRegion (bool is_merged) - : AsIfFlatRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)) + : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)) { init (); @@ -410,68 +410,17 @@ void FlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, uns } void -FlatRegion::insert (const db::Box &box) -{ - if (! box.empty () && box.width () > 0 && box.height () > 0) { - - if (empty ()) { - - mp_polygons->insert (db::Polygon (box)); - m_is_merged = true; - update_bbox (box); - - } else { - - mp_polygons->insert (db::Polygon (box)); - m_is_merged = false; - invalidate_cache (); - - } - - } -} - -void -FlatRegion::insert (const db::Path &path) -{ - if (path.points () > 0) { - mp_polygons->insert (path.polygon ()); - m_is_merged = false; - invalidate_cache (); - } -} - -void -FlatRegion::insert (const db::Polygon &polygon) +FlatRegion::do_insert (const db::Polygon &polygon) { if (polygon.holes () > 0 || polygon.vertices () > 0) { + + bool is_box = (empty () && polygon.is_box ()); + mp_polygons->insert (polygon); - m_is_merged = false; - invalidate_cache (); - } -} + set_is_merged (is_box); -void -FlatRegion::insert (const db::SimplePolygon &polygon) -{ - if (polygon.vertices () > 0) { - db::Polygon poly; - poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); - mp_polygons->insert (poly); - m_is_merged = false; invalidate_cache (); - } -} -void -FlatRegion::insert (const db::Shape &shape) -{ - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon poly; - shape.polygon (poly); - mp_polygons->insert (poly); - m_is_merged = false; - invalidate_cache (); } } diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index 87342eb13..1f0e2fc93 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatRegion.h" +#include "dbMutableRegion.h" #include "dbShapes.h" #include "dbShapes2.h" #include "tlCopyOnWrite.h" @@ -42,7 +42,7 @@ typedef generic_shapes_iterator_delegate FlatRegionIterator; * @brief A flat, polygon-set delegate */ class DB_PUBLIC FlatRegion - : public AsIfFlatRegion + : public MutableRegion { public: typedef db::Polygon value_type; @@ -62,7 +62,7 @@ public: return new FlatRegion (*this); } - void reserve (size_t); + virtual void reserve (size_t); virtual RegionIteratorDelegate *begin () const; virtual RegionIteratorDelegate *begin_merged () const; @@ -97,52 +97,30 @@ public: virtual const db::RecursiveShapeIterator *iter () const; - void insert (const db::Box &box); - void insert (const db::Path &path); - void insert (const db::SimplePolygon &polygon); - void insert (const db::Polygon &polygon); - void insert (const db::Shape &shape); + void do_insert (const db::Polygon &polygon); - template - void insert (const db::Shape &shape, const T &trans) + void do_transform (const db::Trans &t) { - if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { - db::Polygon poly; - shape.polygon (poly); - poly.transform (trans); - insert (poly); - } + transform_generic (t); } - template - void insert (const Iter &b, const Iter &e) + void do_transform (const db::ICplxTrans &t) { - reserve (count () + (e - b)); - for (Iter i = b; i != e; ++i) { - insert (*i); - } + transform_generic (t); } - template - void insert_seq (const Iter &seq) + virtual void do_transform (const db::IMatrix2d &t) { - for (Iter i = seq; ! i.at_end (); ++i) { - insert (*i); - } + transform_generic (t); } - template - void transform (const Trans &trans) + virtual void do_transform (const db::IMatrix3d &t) { - if (! trans.is_unity ()) { - db::Shapes &polygons = *mp_polygons; - for (polygon_iterator_type p = polygons.get_layer ().begin (); p != polygons.get_layer ().end (); ++p) { - polygons.get_layer ().replace (p, p->transformed (trans)); - } - invalidate_cache (); - } + transform_generic (t); } + void flatten () { } + db::Shapes &raw_polygons () { return *mp_polygons; } const db::Shapes &raw_polygons () const { return *mp_polygons; } @@ -166,6 +144,18 @@ private: void init (); void ensure_merged_polygons_valid () const; + + template + void transform_generic (const Trans &trans) + { + if (! trans.is_unity ()) { + db::Shapes &polygons = *mp_polygons; + for (polygon_iterator_type p = polygons.get_layer ().begin (); p != polygons.get_layer ().end (); ++p) { + polygons.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } }; } diff --git a/src/db/db/dbFlatTexts.cc b/src/db/db/dbFlatTexts.cc index 8fc167004..59d0ec2c9 100644 --- a/src/db/db/dbFlatTexts.cc +++ b/src/db/db/dbFlatTexts.cc @@ -32,7 +32,7 @@ namespace db // FlatTexts implementation FlatTexts::FlatTexts () - : AsIfFlatTexts (), mp_texts (new db::Shapes (false)) + : MutableTexts (), mp_texts (new db::Shapes (false)) { // .. nothing yet .. } @@ -43,13 +43,13 @@ FlatTexts::~FlatTexts () } FlatTexts::FlatTexts (const FlatTexts &other) - : AsIfFlatTexts (other), mp_texts (other.mp_texts) + : MutableTexts (other), mp_texts (other.mp_texts) { // .. nothing yet .. } FlatTexts::FlatTexts (const db::Shapes &texts) - : AsIfFlatTexts (), mp_texts (new db::Shapes (texts)) + : MutableTexts (), mp_texts (new db::Shapes (texts)) { // .. nothing yet .. } @@ -207,23 +207,11 @@ FlatTexts::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned } void -FlatTexts::insert (const db::Text &t) +FlatTexts::do_insert (const db::Text &t) { mp_texts->insert (t); invalidate_cache (); } -void -FlatTexts::insert (const db::Shape &shape) -{ - if (shape.is_text ()) { - - db::Text t; - shape.text (t); - insert (t); - - } -} - } diff --git a/src/db/db/dbFlatTexts.h b/src/db/db/dbFlatTexts.h index 90b2d7bbd..0b4839226 100644 --- a/src/db/db/dbFlatTexts.h +++ b/src/db/db/dbFlatTexts.h @@ -26,7 +26,7 @@ #include "dbCommon.h" -#include "dbAsIfFlatTexts.h" +#include "dbMutableTexts.h" #include "dbShapes.h" #include "tlCopyOnWrite.h" @@ -41,7 +41,7 @@ typedef generic_shapes_iterator_delegate FlatTextsIterator; * @brief The delegate for the actual text set implementation */ class DB_PUBLIC FlatTexts - : public AsIfFlatTexts + : public MutableTexts { public: typedef db::Text value_type; @@ -83,40 +83,28 @@ public: virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const; - void insert (const db::Text &text); - void insert (const db::Shape &shape); + virtual void flatten () { } - template - void insert (const db::Shape &shape, const T &trans) + void do_insert (const db::Text &text); + + virtual void do_transform (const db::Trans &t) { - if (shape.is_edge_pair ()) { - - db::Text t; - shape.text (t); - t.transform (trans); - insert (t); - - } + transform_generic (t); } - template - void insert_seq (const Iter &seq) + virtual void do_transform (const db::ICplxTrans &t) { - for (Iter i = seq; ! i.at_end (); ++i) { - insert (*i); - } + transform_generic (t); } - template - void transform (const Trans &trans) + virtual void do_transform (const db::IMatrix2d &t) { - if (! trans.is_unity ()) { - db::Shapes &texts = *mp_texts; - for (text_iterator_type p = texts.template get_layer ().begin (); p != texts.template get_layer ().end (); ++p) { - texts.get_layer ().replace (p, p->transformed (trans)); - } - invalidate_cache (); - } + transform_generic (t); + } + + virtual void do_transform (const db::IMatrix3d &t) + { + transform_generic (t); } db::Shapes &raw_texts () { return *mp_texts; } @@ -132,6 +120,18 @@ private: FlatTexts &operator= (const FlatTexts &other); mutable tl::copy_on_write_ptr mp_texts; + + template + void transform_generic (const Trans &trans) + { + if (! trans.is_unity ()) { + db::Shapes &texts = *mp_texts; + for (text_iterator_type p = texts.template get_layer ().begin (); p != texts.template get_layer ().end (); ++p) { + texts.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } }; } diff --git a/src/db/db/dbGlyphs.h b/src/db/db/dbGlyphs.h index 2fa6d69a2..acb1dd49c 100644 --- a/src/db/db/dbGlyphs.h +++ b/src/db/db/dbGlyphs.h @@ -121,13 +121,13 @@ public: * @param char_spacing Additional spacing between the lines in µm * @param The resulting polygons will be put here (the vector will be cleared before) */ - void text (const std::string &t, double target_dbu, double mag, bool inv, double bias, double char_spacing, double line_spacing, std::vector &polygons) const; + void text (const std::string &t, double target_dbu, double mag2, bool inv, double bias, double char_spacing, double line_spacing, std::vector &polygons) const; /** * @brief Creates the given text as a region * For the parameters see "text" */ - db::Region text_as_region (const std::string &t, double target_dbu, double mag, bool inv, double bias, double char_spacing, double line_spacing) const; + db::Region text_as_region (const std::string &t, double target_dbu, double mag2, bool inv, double bias, double char_spacing, double line_spacing) const; /** * @brief Gets the glyph for a given character diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 56eb28675..4f3331992 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -40,7 +40,7 @@ namespace db // the iterator provides the hierarchical selection (enabling/disabling cells etc.) LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) - : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0) + : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false) { // check the iterator if (iter.has_complex_region () || iter.region () != db::Box::world ()) { @@ -60,7 +60,7 @@ LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) } LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index) - : mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0) + : mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false) { if (dss->is_valid_layout_index (m_layout_index)) { m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set ()); @@ -68,7 +68,7 @@ LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_i } LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) - : m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0) + : m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0), m_include_floating_subcircuits (false) { mp_internal_dss.reset (new db::DeepShapeStore (topcell_name, dbu)); mp_dss.reset (mp_internal_dss.get ()); @@ -79,7 +79,7 @@ LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) LayoutToNetlist::LayoutToNetlist () : m_iter (), mp_internal_dss (new db::DeepShapeStore ()), mp_dss (mp_internal_dss.get ()), m_layout_index (0), - m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0) + m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false) { init (); } @@ -310,37 +310,91 @@ size_t LayoutToNetlist::global_net_id (const std::string &name) return m_conn.global_net_id (name); } -void LayoutToNetlist::extract_netlist (const std::string &joined_net_names, bool include_floating_subcircuits) +void LayoutToNetlist::set_include_floating_subcircuits (bool f) { - extract_netlist (joined_net_names, std::map (), include_floating_subcircuits); + m_include_floating_subcircuits = f; } -void LayoutToNetlist::extract_netlist (const std::string &joined_net_names, const std::map &joined_net_names_per_cell, bool include_floating_subcircuits) +void LayoutToNetlist::clear_join_net_names () +{ + m_joined_net_names.clear (); + m_joined_net_names_per_cell.clear (); +} + +void LayoutToNetlist::join_net_names (const tl::GlobPattern &gp) +{ + m_joined_net_names.push_back (gp); +} + +void LayoutToNetlist::join_net_names (const tl::GlobPattern &cell, const tl::GlobPattern &gp) +{ + m_joined_net_names_per_cell.push_back (std::make_pair (cell, gp)); +} + +void LayoutToNetlist::clear_join_nets () +{ + m_joined_nets.clear (); + m_joined_nets_per_cell.clear (); +} + +void LayoutToNetlist::join_nets (const std::set &jn) +{ + m_joined_nets.push_back (jn); +} + +void LayoutToNetlist::join_nets (const tl::GlobPattern &cell, const std::set &gp) +{ + m_joined_nets_per_cell.push_back (std::make_pair (cell, gp)); +} + +void LayoutToNetlist::extract_netlist () { if (m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); } ensure_netlist (); + const db::Layout &layout = dss ().layout (m_layout_index); + db::NetlistExtractor netex; - netex.set_joined_net_names (joined_net_names); + netex.set_joined_net_names (m_joined_net_names); - const db::Layout &layout = dss ().layout (m_layout_index); - for (std::map::const_iterator j = joined_net_names_per_cell.begin (); j != joined_net_names_per_cell.end (); ++j) { - tl::GlobPattern pat (j->first); - if (pat.is_const ()) { - netex.set_joined_net_names (j->first, j->second); + std::map > jp_per_cell; + for (std::list >::const_iterator j = m_joined_net_names_per_cell.begin (); j != m_joined_net_names_per_cell.end (); ++j) { + if (j->first.is_const ()) { + jp_per_cell [j->first.pattern ()].push_back (j->second); } else { for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { - if (pat.match (layout.cell_name (c->cell_index ()))) { - netex.set_joined_net_names (layout.cell_name (c->cell_index ()), j->second); + if (j->first.match (layout.cell_name (c->cell_index ()))) { + jp_per_cell [layout.cell_name (c->cell_index ())].push_back (j->second); } } } } + for (std::map >::const_iterator i = jp_per_cell.begin (); i != jp_per_cell.end (); ++i) { + netex.set_joined_net_names (i->first, i->second); + } - netex.set_include_floating_subcircuits (include_floating_subcircuits); + netex.set_joined_nets (m_joined_nets); + + std::map > > jn_per_cell; + for (std::list > >::const_iterator j = m_joined_nets_per_cell.begin (); j != m_joined_nets_per_cell.end (); ++j) { + if (j->first.is_const ()) { + jn_per_cell [j->first.pattern ()].push_back (j->second); + } else { + for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { + if (j->first.match (layout.cell_name (c->cell_index ()))) { + jn_per_cell [layout.cell_name (c->cell_index ())].push_back (j->second); + } + } + } + } + for (std::map > >::const_iterator i = jn_per_cell.begin (); i != jn_per_cell.end (); ++i) { + netex.set_joined_nets (i->first, i->second); + } + + netex.set_include_floating_subcircuits (m_include_floating_subcircuits); netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters); m_netlist_extracted = true; diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 7faf276ec..a1bf8716f 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -27,6 +27,7 @@ #include "dbCellMapping.h" #include "dbNetlistExtractor.h" #include "dbNetlistDeviceExtractor.h" +#include "tlGlobPattern.h" namespace db { @@ -423,20 +424,106 @@ public: size_t global_net_id (const std::string &name); /** - * @brief Runs the netlist extraction - * See the class description for more details. + * @brief Sets a flag indicating whether to include floating subcircuits */ - void extract_netlist (const std::string &joined_net_names = std::string (), bool include_floating_subcircuits = false); + void set_include_floating_subcircuits (bool f); + + /** + * @brief Sets a flag indicating whether to include floating subcircuits + */ + bool include_floating_subcircuits () const + { + return m_include_floating_subcircuits; + } + + /** + * @brief Clears the "join net names" settings + */ + void clear_join_net_names (); + + /** + * @brief Joins net names matching the given expression + * + * Using this function will *add* one more rule. To clear all registered rules, use "clear_join_net_names". + * These pattern will only act on top level cells. + */ + void join_net_names (const tl::GlobPattern &gp); + + /** + * @brief Joins net names matching the given expression + * + * Using this function will *add* one more rule specific to cells matching the first glob pattern. To clear all registered rules, use "clear_join_net_names". + * Pattern registered with this function will act on the given cells, regardless of whether it's top level or not. + */ + void join_net_names (const tl::GlobPattern &cell, const tl::GlobPattern &gp); + + /** + * @brief Gets the joined net names for top level + * + * This method is mainly provided to test purposes. + */ + const std::list &joined_net_names () const + { + return m_joined_net_names; + } + + /** + * @brief Gets the joined net names per cell + * + * This method is mainly provided to test purposes. + */ + const std::list > &joined_net_names_per_cell () const + { + return m_joined_net_names_per_cell; + } + + /** + * @brief Clears the "join nets" settings + */ + void clear_join_nets (); + + /** + * @brief Joins the given nets for the top level cell + * + * This method will make an explicit connection between the nets given in the name set. + * This applies implicit joining of different nets with the same label (intra-net joining) + * and of nets with different names (inter-net joining). Intra-net joining is implied always. + */ + void join_nets (const std::set &jn); + + /** + * @brief Joins the given nets for cells matching the given pattern + * + * Using this function will *add* one more rule specific to cells matching the first glob pattern. To clear all registered rules, use "clear_join_nets". + * Pattern registered with this function will act on the given cells, regardless of whether it's top level or not. + */ + void join_nets (const tl::GlobPattern &cell, const std::set &gp); + + /** + * @brief Gets the joined nets for top level + * + * This method is mainly provided to test purposes. + */ + const std::list > &joined_nets () const + { + return m_joined_nets; + } + + /** + * @brief Gets the joined nets per cell + * + * This method is mainly provided to test purposes. + */ + const std::list > > &joined_nets_per_cell () const + { + return m_joined_nets_per_cell; + } /** * @brief Runs the netlist extraction - * In addition to the previous version, this extraction method allows specification of a per-cell list of - * joined (labelled) net names. - * The key of the "joined_net_names_per_cell" is a cell name or a glob expression for cells. On all matching cells, - * the value is applied as a label selector for labels that are joined together. The "joined_net_names" expressions - * is only applied to the top cell. + * See the class description for more details. */ - void extract_netlist (const std::string &joined_net_names, const std::map &joined_net_names_per_cell, bool include_floating_subcircuits = false); + void extract_netlist (); /** * @brief Marks the netlist as extracted @@ -830,6 +917,11 @@ private: double m_device_scaling; db::DeepLayer m_dummy_layer; std::string m_generator; + bool m_include_floating_subcircuits; + std::list m_joined_net_names; + std::list > m_joined_net_names_per_cell; + std::list > m_joined_nets; + std::list > > m_joined_nets_per_cell; struct CellReuseTableKey { diff --git a/src/db/db/dbMatrix.cc b/src/db/db/dbMatrix.cc index 5c78bb240..20c492d6f 100644 --- a/src/db/db/dbMatrix.cc +++ b/src/db/db/dbMatrix.cc @@ -46,7 +46,7 @@ matrix_2d::to_string () const template std::pair -matrix_2d::mag () const +matrix_2d::mag2 () const { double s1 = sqrt (m_m11 * m_m11 + m_m21 * m_m21); double s2 = sqrt (m_m12 * m_m12 + m_m22 * m_m22); @@ -65,7 +65,7 @@ template double matrix_2d::angle () const { - std::pair m = mag (); + std::pair m = mag2 (); double u1 = m.first; double u2 = is_mirror () ? -m.second : m.second; double n11 = m_m11 / u1; @@ -98,7 +98,7 @@ template bool matrix_2d::has_shear () const { - std::pair m = mag (); + std::pair m = mag2 (); double u1 = m.first; double u2 = is_mirror () ? -m.second : m.second; double n11 = m_m11 / u1; @@ -114,7 +114,7 @@ template double matrix_2d::shear_angle () const { - std::pair m = mag (); + std::pair m = mag2 (); double u1 = m.first; double u2 = is_mirror () ? -m.second : m.second; double n11 = m_m11 / u1; diff --git a/src/db/db/dbMatrix.h b/src/db/db/dbMatrix.h index 8e5ab74c2..835134228 100644 --- a/src/db/db/dbMatrix.h +++ b/src/db/db/dbMatrix.h @@ -309,7 +309,15 @@ public: * into the geometrical base transformations. This member returns the x and y magnification * components. The order of the execution is mirror, magnification, shear and rotation. */ - std::pair mag () const; + std::pair mag2 () const; + + /** + * @brief For compatibility with other transformations + */ + double mag () const + { + return mag2 ().first; + } /** * @brief Return the x magnification component of the matrix @@ -320,7 +328,7 @@ public: */ double mag_x () const { - return mag ().first; + return mag2 ().first; } /** @@ -332,7 +340,7 @@ public: */ double mag_y () const { - return mag ().second; + return mag2 ().second; } /** @@ -356,6 +364,22 @@ public: return matrix_2d (m, 0.0, 0.0, m); } + /** + * @brief A dummy displacement accessor (matrix2d does not have a displacement) + */ + db::vector disp () const + { + return db::vector (); + } + + /** + * @brief For compatibility with other transformations + */ + coord_type ctrans (coord_type c) const + { + return db::coord_traits::rounded (mag2 ().first * c); + } + /** * @brief Return the mirror component of the matrix * @@ -759,9 +783,25 @@ public: * into the geometrical base transformations. This member returns the magnification * component for both x and y direction (anisotropic magnification). The order of the execution is mirror, magnification, shear, rotation, perspective and displacement. */ - std::pair mag () const + std::pair mag2 () const { - return m2d ().mag (); + return m2d ().mag2 (); + } + + /** + * @brief For compatibility with other transformations + */ + double mag () const + { + return mag2 ().first; + } + + /** + * @brief For compatibility with other transformations + */ + coord_type ctrans (coord_type c) const + { + return db::coord_traits::rounded (mag2 ().first * c); } /** @@ -769,7 +809,7 @@ public: */ double mag_x () const { - return mag ().first; + return mag2 ().first; } /** @@ -777,7 +817,7 @@ public: */ double mag_y () const { - return mag ().second; + return mag2 ().second; } /** diff --git a/src/db/db/dbMutableEdgePairs.cc b/src/db/db/dbMutableEdgePairs.cc new file mode 100644 index 000000000..876d609c9 --- /dev/null +++ b/src/db/db/dbMutableEdgePairs.cc @@ -0,0 +1,58 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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 "dbMutableEdgePairs.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// MutableEdgePairs implementation + +MutableEdgePairs::MutableEdgePairs () + : AsIfFlatEdgePairs () +{ + // .. nothing yet .. +} + +MutableEdgePairs::MutableEdgePairs (const MutableEdgePairs &other) + : AsIfFlatEdgePairs (other) +{ + // .. nothing yet .. +} + +MutableEdgePairs::~MutableEdgePairs () +{ + // .. nothing yet .. +} + +void +MutableEdgePairs::insert (const db::Shape &shape) +{ + if (shape.is_edge_pair ()) { + insert (shape.edge_pair ()); + } +} + +} + diff --git a/src/db/db/dbMutableEdgePairs.h b/src/db/db/dbMutableEdgePairs.h new file mode 100644 index 000000000..854d61eb0 --- /dev/null +++ b/src/db/db/dbMutableEdgePairs.h @@ -0,0 +1,100 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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_dbMutableEdgePairs +#define HDR_dbMutableEdgePairs + +#include "dbCommon.h" + +#include "dbAsIfFlatEdgePairs.h" + +#include + +namespace db { + +/** + * @brief An interface representing mutable edge pair collections + * + * Mutable edge pair collections offer insert, transform, flatten and other manipulation functions. + */ +class DB_PUBLIC MutableEdgePairs + : public AsIfFlatEdgePairs +{ +public: + MutableEdgePairs (); + MutableEdgePairs (const MutableEdgePairs &other); + virtual ~MutableEdgePairs (); + + virtual void do_insert (const db::EdgePair &edge_pair) = 0; + + virtual void do_transform (const db::Trans &t) = 0; + virtual void do_transform (const db::ICplxTrans &t) = 0; + virtual void do_transform (const db::IMatrix2d &t) = 0; + virtual void do_transform (const db::IMatrix3d &t) = 0; + + virtual void flatten () = 0; + + virtual void reserve (size_t n) = 0; + + void transform (const db::UnitTrans &) { } + void transform (const db::Disp &t) { do_transform (db::Trans (t)); } + void transform (const db::Trans &t) { do_transform (t); } + void transform (const db::ICplxTrans &t) { do_transform (t); } + void transform (const db::IMatrix2d &t) { do_transform (t); } + void transform (const db::IMatrix3d &t) { do_transform (t); } + + void insert (const db::EdgePair &edge_pair) { do_insert (edge_pair); } + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_edge_pair ()) { + db::EdgePair ep = shape.edge_pair (); + ep.transform (trans); + insert (ep); + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (count () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } +}; + +} + +#endif + diff --git a/src/db/db/dbMutableEdges.cc b/src/db/db/dbMutableEdges.cc new file mode 100644 index 000000000..b0859ac1f --- /dev/null +++ b/src/db/db/dbMutableEdges.cc @@ -0,0 +1,107 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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 "dbMutableEdges.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// MutableEdges implementation + +MutableEdges::MutableEdges () + : AsIfFlatEdges () +{ + // .. nothing yet .. +} + +MutableEdges::MutableEdges (const MutableEdges &other) + : AsIfFlatEdges (other) +{ + // .. nothing yet .. +} + +MutableEdges::~MutableEdges () +{ + // .. nothing yet .. +} + +void +MutableEdges::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + do_insert (db::Edge (box.lower_left (), box.upper_left ())); + do_insert (db::Edge (box.upper_left (), box.upper_right ())); + do_insert (db::Edge (box.upper_right (), box.lower_right ())); + do_insert (db::Edge (box.lower_right (), box.lower_left ())); + } +} + +void +MutableEdges::insert (const db::Path &path) +{ + if (path.points () > 0) { + insert (path.polygon ()); + } +} + +void +MutableEdges::insert (const db::Polygon &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + do_insert (*e); + } + } +} + +void +MutableEdges::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + do_insert (*e); + } + } +} + +void +MutableEdges::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + + db::Polygon poly; + shape.polygon (poly); + insert (poly); + + } else if (shape.is_edge ()) { + + db::Edge edge; + shape.edge (edge); + do_insert (edge); + + } +} + +} + diff --git a/src/db/db/dbMutableEdges.h b/src/db/db/dbMutableEdges.h new file mode 100644 index 000000000..e24bd57e2 --- /dev/null +++ b/src/db/db/dbMutableEdges.h @@ -0,0 +1,114 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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_dbMutableEdges +#define HDR_dbMutableEdges + +#include "dbCommon.h" + +#include "dbAsIfFlatEdges.h" + +#include + +namespace db { + +/** + * @brief An interface representing mutable edge collections + * + * Mutable edge collections offer insert, transform, flatten and other manipulation functions. + */ +class DB_PUBLIC MutableEdges + : public AsIfFlatEdges +{ +public: + MutableEdges (); + MutableEdges (const MutableEdges &other); + virtual ~MutableEdges (); + + virtual void do_transform (const db::Trans &t) = 0; + virtual void do_transform (const db::ICplxTrans &t) = 0; + virtual void do_transform (const db::IMatrix2d &t) = 0; + virtual void do_transform (const db::IMatrix3d &t) = 0; + + virtual void flatten () = 0; + + virtual void reserve (size_t n) = 0; + + virtual void do_insert (const db::Edge &edge) = 0; + + void transform (const db::UnitTrans &) { } + void transform (const db::Disp &t) { do_transform (db::Trans (t)); } + void transform (const db::Trans &t) { do_transform (t); } + void transform (const db::ICplxTrans &t) { do_transform (t); } + void transform (const db::IMatrix2d &t) { do_transform (t); } + void transform (const db::IMatrix3d &t) { do_transform (t); } + + void insert (const db::Edge &edge) { do_insert (edge); } + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + void insert (const db::Polygon &polygon); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); + + } else if (shape.is_edge ()) { + + db::Edge edge; + shape.edge (edge); + edge.transform (trans); + insert (edge); + + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (count () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } +}; + +} + +#endif + diff --git a/src/db/db/dbMutableRegion.cc b/src/db/db/dbMutableRegion.cc new file mode 100644 index 000000000..8f55558f5 --- /dev/null +++ b/src/db/db/dbMutableRegion.cc @@ -0,0 +1,90 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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 "dbMutableRegion.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// FlatRegion implementation + +MutableRegion::MutableRegion () + : AsIfFlatRegion () +{ + // .. nothing yet .. +} + +MutableRegion::MutableRegion (const MutableRegion &other) + : AsIfFlatRegion (other) +{ + // .. nothing yet .. +} + +MutableRegion::~MutableRegion () +{ + // .. nothing yet .. +} + +void +MutableRegion::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + do_insert (db::Polygon (box)); + } +} + +void +MutableRegion::insert (const db::Path &path) +{ + if (path.points () > 0) { + do_insert (path.polygon ()); + } +} + +void +MutableRegion::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + db::Polygon poly; + poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + do_insert (poly); + } +} + +void +MutableRegion::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + insert (poly); + } else if (shape.is_path ()) { + insert (shape.path ()); + } else if (shape.is_box ()) { + insert (shape.box ()); + } +} + +} + diff --git a/src/db/db/dbMutableRegion.h b/src/db/db/dbMutableRegion.h new file mode 100644 index 000000000..68c90a45b --- /dev/null +++ b/src/db/db/dbMutableRegion.h @@ -0,0 +1,105 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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_dbMutableRegion +#define HDR_dbMutableRegion + +#include "dbCommon.h" + +#include "dbAsIfFlatRegion.h" + +#include + +namespace db { + +/** + * @brief An interface representing mutable regions + * + * Mutable regions offer insert, transform, flatten and other manipulation functions. + */ +class DB_PUBLIC MutableRegion + : public AsIfFlatRegion +{ +public: + MutableRegion (); + MutableRegion (const MutableRegion &other); + virtual ~MutableRegion (); + + virtual void do_insert (const db::Polygon &polygon) = 0; + + void transform (const db::UnitTrans &) { } + void transform (const db::Disp &t) { do_transform (db::Trans (t)); } + void transform (const db::Trans &t) { do_transform (t); } + void transform (const db::ICplxTrans &t) { do_transform (t); } + void transform (const db::IMatrix2d &t) { do_transform (t); } + void transform (const db::IMatrix3d &t) { do_transform (t); } + + virtual void do_transform (const db::Trans &t) = 0; + virtual void do_transform (const db::ICplxTrans &t) = 0; + virtual void do_transform (const db::IMatrix2d &t) = 0; + virtual void do_transform (const db::IMatrix3d &t) = 0; + + virtual void flatten () = 0; + + virtual void reserve (size_t n) = 0; + + void insert (const db::Polygon &polygon) { do_insert (polygon); } + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (count () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } +}; + +} + +#endif + diff --git a/src/db/db/dbMutableTexts.cc b/src/db/db/dbMutableTexts.cc new file mode 100644 index 000000000..d8789e591 --- /dev/null +++ b/src/db/db/dbMutableTexts.cc @@ -0,0 +1,60 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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 "dbMutableTexts.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------------------------------- +// MutableTexts implementation + +MutableTexts::MutableTexts () + : AsIfFlatTexts () +{ + // .. nothing yet .. +} + +MutableTexts::MutableTexts (const MutableTexts &other) + : AsIfFlatTexts (other) +{ + // .. nothing yet .. +} + +MutableTexts::~MutableTexts () +{ + // .. nothing yet .. +} + +void +MutableTexts::insert (const db::Shape &shape) +{ + if (shape.is_text ()) { + db::Text text; + shape.text (text); + insert (text); + } +} + +} + diff --git a/src/db/db/dbMutableTexts.h b/src/db/db/dbMutableTexts.h new file mode 100644 index 000000000..eadd63129 --- /dev/null +++ b/src/db/db/dbMutableTexts.h @@ -0,0 +1,100 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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_dbMutableTexts +#define HDR_dbMutableTexts + +#include "dbCommon.h" + +#include "dbAsIfFlatTexts.h" + +#include + +namespace db { + +/** + * @brief An interface representing mutable text collections + * + * Mutable text collections offer insert, transform, flatten and other manipulation functions. + */ +class DB_PUBLIC MutableTexts + : public AsIfFlatTexts +{ +public: + MutableTexts (); + MutableTexts (const MutableTexts &other); + virtual ~MutableTexts (); + + virtual void do_insert (const db::Text &text) = 0; + + virtual void do_transform (const db::Trans &t) = 0; + virtual void do_transform (const db::ICplxTrans &t) = 0; + virtual void do_transform (const db::IMatrix3d &t) = 0; + virtual void do_transform (const db::IMatrix2d &t) = 0; + + virtual void flatten () = 0; + + virtual void reserve (size_t n) = 0; + + void transform (const db::UnitTrans &) { } + void transform (const db::Disp &t) { do_transform (db::Trans (t)); } + void transform (const db::Trans &t) { do_transform (t); } + void transform (const db::ICplxTrans &t) { do_transform (t); } + void transform (const db::IMatrix3d &t) { do_transform (t); } + void transform (const db::IMatrix2d &t) { do_transform (t); } + + void insert (const db::Text &text) { do_insert (text); } + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_text ()) { + db::Text text = shape.text (); + text.transform (trans); + insert (text); + } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (count () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } +}; + +} + +#endif + diff --git a/src/db/db/dbNet.cc b/src/db/db/dbNet.cc index 1f0488e60..f3f74f914 100644 --- a/src/db/db/dbNet.cc +++ b/src/db/db/dbNet.cc @@ -204,6 +204,16 @@ Net::~Net () clear (); } +Netlist *Net::netlist () +{ + return mp_circuit ? mp_circuit->netlist () : 0; +} + +const Netlist *Net::netlist () const +{ + return mp_circuit ? mp_circuit->netlist () : 0; +} + void Net::clear () { m_name.clear (); diff --git a/src/db/db/dbNet.h b/src/db/db/dbNet.h index fdd060928..66cf97d36 100644 --- a/src/db/db/dbNet.h +++ b/src/db/db/dbNet.h @@ -41,6 +41,7 @@ class Circuit; class DeviceTerminalDefinition; class DeviceClass; class Pin; +class Netlist; /** * @brief A reference to a terminal of a device @@ -418,6 +419,18 @@ public: return mp_circuit; } + /** + * @brief Gets the netlist the net lives in + * This pointer is 0 if the net is not part of a netlist. + */ + Netlist *netlist (); + + /** + * @brief Gets the netlist the net lives in (const version) + * This pointer is 0 if the net is not part of a netlist. + */ + const Netlist *netlist () const; + /** * @brief Clears the circuit */ diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 5989b952d..c369b4c47 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -31,7 +31,7 @@ namespace db // Netlist class implementation Netlist::Netlist (NetlistManipulationCallbacks *callbacks) - : mp_callbacks (callbacks), + : m_case_sensitive (true), mp_callbacks (callbacks), m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), @@ -44,7 +44,8 @@ Netlist::Netlist (NetlistManipulationCallbacks *callbacks) } Netlist::Netlist (const Netlist &other) - : gsi::ObjectBase (other), tl::Object (other), m_valid_topology (false), m_lock_count (0), + : gsi::ObjectBase (other), tl::Object (other), m_case_sensitive (true), + m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), m_device_abstract_by_name (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts), @@ -69,6 +70,8 @@ Netlist &Netlist::operator= (const Netlist &other) clear (); + set_case_sensitive (other.is_case_sensitive ()); + std::map dct; for (const_device_class_iterator dc = other.begin_device_classes (); dc != other.end_device_classes (); ++dc) { DeviceClass *dc_new = dc->clone (); @@ -100,6 +103,34 @@ Netlist &Netlist::operator= (const Netlist &other) return *this; } +void Netlist::set_case_sensitive (bool f) +{ + m_case_sensitive = f; +} + +int Netlist::name_compare (bool case_sensitive, const std::string &n1, const std::string &n2) +{ + // TODO: unicode support? + if (case_sensitive) { + return strcmp (n1.c_str (), n2.c_str ()); + } else { +#if defined(_WIN32) + return _stricmp (n1.c_str (), n2.c_str ()); +#else + return strcasecmp (n1.c_str (), n2.c_str ()); +#endif + } +} + +std::string Netlist::normalize_name (bool case_sensitive, const std::string &n) +{ + if (case_sensitive) { + return n; + } else { + return tl::to_upper_case (n); + } +} + void Netlist::circuits_changed () { m_circuit_by_cell_index.invalidate (); @@ -472,8 +503,10 @@ void Netlist::flatten () DeviceClass *Netlist::device_class_by_name (const std::string &name) { + std::string nn = m_case_sensitive ? name : normalize_name (name); + for (device_class_iterator d = begin_device_classes (); d != end_device_classes (); ++d) { - if (d->name () == name) { + if (d->name () == nn) { return d.operator-> (); } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 79ff6bb2e..bfac2d402 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -102,6 +102,19 @@ public: */ void clear (); + /** + * @brief Returns a value indicating whether the netlist names are case sensitive + */ + bool is_case_sensitive () const + { + return m_case_sensitive; + } + + /** + * @brief Sets a value indicating whether the netlist names are case sensitive + */ + void set_case_sensitive (bool f); + /** * @brief Returns a parsable string representation of the netlist * @@ -225,7 +238,7 @@ public: */ Circuit *circuit_by_name (const std::string &name) { - return m_circuit_by_name.object_by (name); + return m_circuit_by_name.object_by (normalize_name (name)); } /** @@ -235,7 +248,7 @@ public: */ const Circuit *circuit_by_name (const std::string &name) const { - return m_circuit_by_name.object_by (name); + return m_circuit_by_name.object_by (normalize_name (name)); } /** @@ -429,7 +442,7 @@ public: */ DeviceAbstract *device_abstract_by_name (const std::string &name) { - return m_device_abstract_by_name.object_by (name); + return m_device_abstract_by_name.object_by (normalize_name (name)); } /** @@ -439,7 +452,7 @@ public: */ const DeviceAbstract *device_abstract_by_name (const std::string &name) const { - return m_device_abstract_by_name.object_by (name); + return m_device_abstract_by_name.object_by (normalize_name (name)); } /** @@ -502,10 +515,29 @@ public: */ void combine_devices (); + /** + * @brief Compares two names with the given case sensitivity + */ + static int name_compare (bool case_sensitive, const std::string &n1, const std::string &n2); + + /** + * @brief Normalizes a name with the given case sensitivity + */ + static std::string normalize_name (bool case_sensitive, const std::string &n); + + /** + * @brief Normalizes a name with the given case sensitivity of the netlist + */ + std::string normalize_name (const std::string &n) const + { + return normalize_name (is_case_sensitive (), n); + } + private: friend class Circuit; friend class DeviceAbstract; + bool m_case_sensitive; tl::weak_ptr mp_callbacks; circuit_list m_circuits; device_class_list m_device_classes; diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 99ad08829..4efcd9871 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -48,15 +48,12 @@ struct GlobalCompareOptions debug_netcompare = tl::app_flag ("netlist-compare-debug-netcompare"); // $KLAYOUT_NETLIST_COMPARE_DEBUG_NETGRAPH debug_netgraph = tl::app_flag ("netlist-compare-debug-netgraph"); - // $KLAYOUT_NETLIST_COMPARE_CASE_SENSITIVE - compare_case_sensitive = tl::app_flag ("netlist-compare-case-sensitive"); m_is_initialized = true; } } bool debug_netcompare; bool debug_netgraph; - bool compare_case_sensitive; private: bool m_is_initialized; @@ -90,29 +87,19 @@ namespace db // -------------------------------------------------------------------------------------------------------------------- // A generic string compare -static int name_compare (const std::string &n1, const std::string &n2) +bool combined_case_sensitive (const db::Netlist *a, const db::Netlist *b) { - // TODO: unicode support? - if (options ()->compare_case_sensitive) { - return strcmp (n1.c_str (), n2.c_str ()); - } else { -#if defined(_WIN32) - return _stricmp (n1.c_str (), n2.c_str ()); -#else - return strcasecmp (n1.c_str (), n2.c_str ()); -#endif - } + bool csa = a ? a->is_case_sensitive () : true; + bool csb = b ? b->is_case_sensitive () : true; + return csa && csb; } -static inline std::string normalize_name (const std::string &n) +int name_compare (const db::Net *a, const db::Net *b) { - if (options ()->compare_case_sensitive) { - return n; - } else { - return tl::to_upper_case (n); - } + return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), a->name (), b->name ()); } + // -------------------------------------------------------------------------------------------------------------------- // DeviceCompare definition and implementation @@ -404,11 +391,16 @@ class generic_categorizer { public: generic_categorizer (bool with_name = true) - : m_next_cat (0), m_with_name (with_name) + : m_next_cat (0), m_with_name (with_name), m_case_sensitive (true) { // .. nothing yet .. } + void set_case_sensitive (bool f) + { + m_case_sensitive = f; + } + void same (const Obj *ca, const Obj *cb) { if (! ca && ! cb) { @@ -471,7 +463,7 @@ public: if (m_with_name) { - std::string cls_name = normalize_name (cls->name ()); + std::string cls_name = db::Netlist::normalize_name (m_case_sensitive, cls->name ()); std::map::const_iterator c = m_cat_by_name.find (cls_name); if (c != m_cat_by_name.end ()) { @@ -498,6 +490,7 @@ public: std::map m_cat_by_name; size_t m_next_cat; bool m_with_name; + bool m_case_sensitive; }; // -------------------------------------------------------------------------------------------------------------------- @@ -559,6 +552,11 @@ public: return m_strict_device_categories.find (cat) != m_strict_device_categories.end (); } + void set_case_sensitive (bool f) + { + generic_categorizer::set_case_sensitive (f); + } + private: std::set m_strict_device_categories; }; @@ -606,6 +604,11 @@ public: { return generic_categorizer::cat_for (cr); } + + void set_case_sensitive (bool f) + { + generic_categorizer::set_case_sensitive (f); + } }; // -------------------------------------------------------------------------------------------------------------------- @@ -1461,7 +1464,7 @@ NetGraphNode::net_less (const db::Net *a, const db::Net *b) const std::string &pna = a->begin_pins ()->pin ()->name (); const std::string &pnb = b->begin_pins ()->pin ()->name (); if (! pna.empty () && ! pnb.empty ()) { - return name_compare (pna, pnb) < 0; + return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), pna, pnb) < 0; } } return false; @@ -1484,7 +1487,7 @@ NetGraphNode::edge_equal (const db::Net *a, const db::Net *b) const std::string &pna = a->begin_pins ()->pin ()->name (); const std::string &pnb = b->begin_pins ()->pin ()->name (); if (! pna.empty () && ! pnb.empty ()) { - return name_compare (pna, pnb) == 0; + return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), pna, pnb) == 0; } } return true; @@ -2028,7 +2031,7 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr } - // propagate pairing in picky mode: this means we only accept exact a match if the node set + // propagate pairing in picky mode: this means we only accept a match if the node set // is exactly identical and no ambiguous nodes are present when ambiguous nodes are forbidden size_t bt_count = derive_node_identities_from_node_set (nodes, other_nodes, depth, n_branch, tentative, data); @@ -2256,7 +2259,7 @@ namespace { bool operator() (const std::pair &a, const std::pairb) const { tl_assert (a.first->net () && b.first->net ()); - return name_compare (a.first->net ()->name (), b.first->net ()->name ()) < 0; + return name_compare (a.first->net (), b.first->net ()) < 0; } }; @@ -2317,7 +2320,7 @@ static bool net_names_are_different (const db::Net *a, const db::Net *b) if (! a || ! b || a->name ().empty () || b->name ().empty ()) { return false; } else { - return name_compare (a->name (), b->name ()) != 0; + return name_compare (a, b) != 0; } } @@ -2326,7 +2329,7 @@ static bool net_names_are_equal (const db::Net *a, const db::Net *b) if (! a || ! b || a->name ().empty () || b->name ().empty ()) { return false; } else { - return name_compare (a->name (), b->name ()) == 0; + return name_compare (a, b) == 0; } } @@ -2354,7 +2357,7 @@ NetGraph::derive_node_identities_from_ambiguity_group (const NodeRange &nr, Devi { // marks the nodes from the ambiguity group as unknown so we don't revisit them (causing deep recursion) - TentativeNodeMapping tn2unknown; + TentativeNodeMapping tn_temp; // collect and mark the ambiguity combinations to consider std::vector >::const_iterator> iters1, iters2; @@ -2363,7 +2366,7 @@ NetGraph::derive_node_identities_from_ambiguity_group (const NodeRange &nr, Devi if (! i1->first->has_any_other ()) { iters1.push_back (i1); size_t ni = node_index_for_net (i1->first->net ()); - TentativeNodeMapping::map_to_unknown (&tn2unknown, this, ni); + TentativeNodeMapping::map_to_unknown (&tn_temp, this, ni); } } @@ -2371,7 +2374,7 @@ NetGraph::derive_node_identities_from_ambiguity_group (const NodeRange &nr, Devi if (! i2->first->has_any_other ()) { iters2.push_back (i2); size_t other_ni = data->other->node_index_for_net (i2->first->net ()); - TentativeNodeMapping::map_to_unknown (&tn2unknown, data->other, other_ni); + TentativeNodeMapping::map_to_unknown (&tn_temp, data->other, other_ni); } } @@ -2460,7 +2463,22 @@ NetGraph::derive_node_identities_from_ambiguity_group (const NodeRange &nr, Devi } if (to_remove != iters2.end ()) { + + // Add the new pair to the temporary mapping (even in tentative mode) + // Reasoning: doing the mapping may render other nets incompatible, so to ensure "edges_are_compatible" works properly we + // need to lock the current pairs resources such as devices by listing them in the mapping. This is doing by "derive_*_equivalence" inside + // TentativeNodeMapping::map_pair + + std::vector >::const_iterator i2 = *to_remove; + + size_t ni = node_index_for_net (i1->first->net ()); + size_t other_ni = data->other->node_index_for_net (i2->first->net ()); + + TentativeNodeMapping::map_pair (&tn_temp, this, ni, data->other, other_ni, dm, dm_other, *data->device_equivalence, scm, scm_other, *data->subcircuit_equivalence, depth); + + // now we can get rid of the node and reduce the "other" list of ambiguous nodes iters2.erase (to_remove); + } if (! any && tentative) { @@ -2631,7 +2649,7 @@ NetGraph::derive_node_identities_from_node_set (std::vector data->max_depth) { + if (data->max_depth != std::numeric_limits::max() && depth > data->max_depth) { if (options ()->debug_netcompare) { tl::info << indent_s << "max. depth exhausted (" << depth + 1 << ">" << data->max_depth << ")"; } @@ -2765,7 +2783,7 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum * n_branch > data->max_n_branch) { + } else if (data->max_n_branch != std::numeric_limits::max () && double (nr->num) * double (n_branch) > double (data->max_n_branch)) { if (options ()->debug_netcompare) { tl::info << indent_s << "max. complexity exhausted (" << nr->num << "*" << n_branch << ">" << data->max_n_branch << ") - mismatch."; @@ -2810,11 +2828,18 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger) m_cap_threshold = -1.0; // not set m_res_threshold = -1.0; // not set - m_max_depth = 50; - m_max_n_branch = 500; + // NOTE: as the backtracking algorithm is recursive, we need to limit the number of steps to follow + // Long chains can happen in case of depth-first because the backtracking algorithm will follow + // each successful path further to the very end. Depending on the circuit's complexity a long chain of + // jumps is possible leading to a deep stack. A value of 500 is compatible with 4M stack depth on a + // 64bit machine which is considered acceptable for now. + m_max_depth = 500; + + m_max_n_branch = std::numeric_limits::max (); m_depth_first = true; m_dont_consider_net_names = false; + m_case_sensitive = false; } NetlistComparer::~NetlistComparer () @@ -2887,6 +2912,7 @@ NetlistComparer::unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector { // we need to create a copy because this method is supposed to be const. db::CircuitCategorizer circuit_categorizer = *mp_circuit_categorizer; + circuit_categorizer.set_case_sensitive (m_case_sensitive); std::map, std::vector > > cat2circuits; @@ -2928,11 +2954,16 @@ NetlistComparer::unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector bool NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const { + m_case_sensitive = combined_case_sensitive (a, b); + // we need to create a copy because this method is supposed to be const. db::CircuitCategorizer circuit_categorizer = *mp_circuit_categorizer; db::DeviceCategorizer device_categorizer = *mp_device_categorizer; db::CircuitPinMapper circuit_pin_mapper = *mp_circuit_pin_mapper; + circuit_categorizer.set_case_sensitive (m_case_sensitive); + device_categorizer.set_case_sensitive (m_case_sensitive); + bool good = true; std::map, std::vector > > cat2circuits; @@ -3008,7 +3039,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const } - // device whether to use a device category in strict mode + // decide whether to use a device category in strict mode device_categorizer.clear_strict_device_categories (); @@ -3745,14 +3776,14 @@ NetlistComparer::do_pin_assignment (const db::Circuit *c1, const db::NetGraph &g for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) { const db::Net *net = c2->net_for_pin (p->id ()); if (!net && !p->name ().empty ()) { - abstract_pins_by_name.insert (std::make_pair (normalize_name (p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.second = p.operator-> (); + abstract_pins_by_name.insert (std::make_pair (db::Netlist::normalize_name (m_case_sensitive, p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.second = p.operator-> (); } } for (db::Circuit::const_pin_iterator p = c1->begin_pins (); p != c1->end_pins (); ++p) { const db::Net *net = c1->net_for_pin (p->id ()); if (!net && !p->name ().empty ()) { - abstract_pins_by_name.insert (std::make_pair (normalize_name (p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.first = p.operator-> (); + abstract_pins_by_name.insert (std::make_pair (db::Netlist::normalize_name (m_case_sensitive, p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.first = p.operator-> (); } } diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index 3876cc31b..e437c8987 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -364,6 +364,7 @@ protected: size_t m_max_depth; bool m_depth_first; bool m_dont_consider_net_names; + mutable bool m_case_sensitive; }; } diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 9277124b9..8ebeaa720 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -35,33 +35,44 @@ NetlistExtractor::NetlistExtractor () // .. nothing yet .. } -void NetlistExtractor::set_joined_net_names (const std::string &jnn) +void NetlistExtractor::set_joined_net_names (const std::list &jnn) { m_joined_net_names = jnn; } -void NetlistExtractor::set_joined_net_names (const std::string &cellname, const std::string &jnn) +void NetlistExtractor::set_joined_net_names (const std::string &cellname, const std::list &jnn) { m_joined_net_names_per_cell.push_back (std::make_pair (cellname, jnn)); } +void NetlistExtractor::set_joined_nets (const std::list > &jnn) +{ + m_joined_nets = jnn; +} + +void NetlistExtractor::set_joined_nets (const std::string &cell_name, const std::list > &jnn) +{ + m_joined_nets_per_cell.push_back (std::make_pair (cell_name, jnn)); +} + void NetlistExtractor::set_include_floating_subcircuits (bool f) { m_include_floating_subcircuits = f; } static void -build_net_name_equivalence (const db::Layout *layout, const db::Connectivity &conn, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters &eq) +build_net_name_equivalence (const db::Layout *layout, const db::Connectivity &conn, db::property_names_id_type net_name_id, const std::list &jn_pattern, tl::equivalence_clusters &eq) { std::map > prop_by_name; - tl::GlobPattern jn_pattern (joined_net_names); for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) { for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) { if (p->first == net_name_id) { std::string nn = p->second.to_string (); - if (jn_pattern.match (nn)) { - prop_by_name [nn].insert (db::prop_id_to_attr (i->first)); + for (std::list::const_iterator jp = jn_pattern.begin (); jp != jn_pattern.end (); ++jp) { + if (jp->match (nn)) { + prop_by_name [nn].insert (db::prop_id_to_attr (i->first)); + } } } } @@ -70,16 +81,20 @@ build_net_name_equivalence (const db::Layout *layout, const db::Connectivity &co // include pseudo-attributes for global nets to implement "join_with" for global nets for (size_t gid = 0; gid < conn.global_nets (); ++gid) { const std::string &gn = conn.global_net_name (gid); - if (jn_pattern.match (gn)) { - prop_by_name [gn].insert (db::global_net_id_to_attr (gid)); + for (std::list::const_iterator jp = jn_pattern.begin (); jp != jn_pattern.end (); ++jp) { + if (jp->match (gn)) { + prop_by_name [gn].insert (db::global_net_id_to_attr (gid)); + } } } const db::repository &text_repository = layout->shape_repository ().repository (db::object_tag ()); for (db::repository::iterator t = text_repository.begin (); t != text_repository.end (); ++t) { std::string nn = t->string (); - if (jn_pattern.match (nn)) { - prop_by_name [nn].insert (db::text_ref_to_attr (t.operator-> ())); + for (std::list::const_iterator jp = jn_pattern.begin (); jp != jn_pattern.end (); ++jp) { + if (jp->match (nn)) { + prop_by_name [nn].insert (db::text_ref_to_attr (t.operator-> ())); + } } } @@ -93,6 +108,58 @@ build_net_name_equivalence (const db::Layout *layout, const db::Connectivity &co } } +static void +build_net_name_equivalence_for_explicit_connections (const db::Layout *layout, const db::Connectivity &conn, db::property_names_id_type net_name_id, const std::set &nets_to_join, tl::equivalence_clusters &eq) +{ + std::map > prop_by_name; + + for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) { + for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) { + if (p->first == net_name_id) { + std::string nn = p->second.to_string (); + if (nets_to_join.find (nn) != nets_to_join.end ()) { + prop_by_name [nn].insert (db::prop_id_to_attr (i->first)); + } + } + } + } + + // include pseudo-attributes for global nets to implement "join_with" for global nets + for (size_t gid = 0; gid < conn.global_nets (); ++gid) { + const std::string &gn = conn.global_net_name (gid); + if (nets_to_join.find (gn) != nets_to_join.end ()) { + prop_by_name [gn].insert (db::global_net_id_to_attr (gid)); + } + } + + const db::repository &text_repository = layout->shape_repository ().repository (db::object_tag ()); + for (db::repository::iterator t = text_repository.begin (); t != text_repository.end (); ++t) { + std::string nn = t->string (); + if (nets_to_join.find (nn) != nets_to_join.end ()) { + prop_by_name [nn].insert (db::text_ref_to_attr (t.operator-> ())); + } + } + + // first inter-name equivalence (this implies implicit connections for all n1 and n2 labels) + for (std::map >::const_iterator pn = prop_by_name.begin (); pn != prop_by_name.end (); ++pn) { + std::set::const_iterator p = pn->second.begin (); + std::set::const_iterator p0 = p; + while (p != pn->second.end ()) { + eq.same (*p0, *p); + ++p; + } + } + + // second intra-name equivalence + for (std::map >::const_iterator pn1 = prop_by_name.begin (); pn1 != prop_by_name.end (); ++pn1) { + std::map >::const_iterator pn2 = pn1; + ++pn2; + for ( ; pn2 != prop_by_name.end (); ++pn2) { + eq.same (*pn1->second.begin (), *pn2->second.begin ()); + } + } +} + void NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters) { @@ -114,15 +181,31 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo std::map > net_name_equivalence; if (m_text_annot_name_id.first) { + if (! m_joined_net_names.empty ()) { build_net_name_equivalence (mp_layout, conn, m_text_annot_name_id.second, m_joined_net_names, net_name_equivalence [hier_clusters_type::top_cell_index]); } - for (std::list >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) { + for (std::list > >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) { std::pair cp = mp_layout->cell_by_name (m->first.c_str ()); if (cp.first) { build_net_name_equivalence (mp_layout, conn, m_text_annot_name_id.second, m->second, net_name_equivalence [cp.second]); } } + + if (! m_joined_nets.empty ()) { + for (std::list >::const_iterator n = m_joined_nets.begin (); n != m_joined_nets.end (); ++n) { + build_net_name_equivalence_for_explicit_connections (mp_layout, conn, m_text_annot_name_id.second, *n, net_name_equivalence [hier_clusters_type::top_cell_index]); + } + } + for (std::list > > >::const_iterator m = m_joined_nets_per_cell.begin (); m != m_joined_nets_per_cell.end (); ++m) { + std::pair cp = mp_layout->cell_by_name (m->first.c_str ()); + if (cp.first) { + for (std::list >::const_iterator n = m->second.begin (); n != m->second.end (); ++n) { + build_net_name_equivalence_for_explicit_connections (mp_layout, conn, m_text_annot_name_id.second, *n, net_name_equivalence [cp.second]); + } + } + } + } // the big part: actually extract the nets diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index a2be88166..06c8d4d29 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -26,6 +26,7 @@ #include "dbCommon.h" #include "dbHierNetworkProcessor.h" #include "dbNetShape.h" +#include "tlGlobPattern.h" #include #include @@ -104,23 +105,30 @@ public: * @brief Sets the joined net names attribute * This is a glob expression rendering net names where partial nets with the * same name are joined even without explicit connection. + * The cell-less version applies to top level cells only. */ - void set_joined_net_names (const std::string &jnn); + void set_joined_net_names (const std::list &jnn); /** * @brief Sets the joined net names attribute for a given cell name * While the single-parameter set_joined_net_names only acts on the top cell, this * version will act on the cell with the given name. */ - void set_joined_net_names (const std::string &cell_name, const std::string &jnn); + void set_joined_net_names (const std::string &cell_name, const std::list &jnn); /** - * @brief Gets the joined net names expression + * @brief Sets the joined nets attribute + * This specifies a list of net names to join. Each join group is a set of names which specifies the net + * names that are to be connected. Multiple such groups can be specified. Each net name listed in a + * group implies implicit joining of the corresponding labels into one net. + * The cell-less version applies to top level cells only. */ - const std::string &joined_net_names () const - { - return m_joined_net_names; - } + void set_joined_nets (const std::list > &jnn); + + /** + * @brief Sets the joined nets attribute per cell + */ + void set_joined_nets (const std::string &cell_name, const std::list > &jnn); /** * @brief Extract the nets @@ -135,8 +143,10 @@ private: std::pair m_text_annot_name_id; std::pair m_device_annot_name_id; std::pair m_terminal_annot_name_id; - std::string m_joined_net_names; - std::list > m_joined_net_names_per_cell; + std::list m_joined_net_names; + std::list > > m_joined_net_names_per_cell; + std::list > m_joined_nets; + std::list > > > m_joined_nets_per_cell; bool m_include_floating_subcircuits; bool instance_is_device (db::properties_id_type prop_id) const; diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index e5f1da83b..8431400ba 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -299,6 +299,9 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist) m_global_nets.clear (); m_circuits_read.clear (); + // SPICE netlists are case insensitive + netlist.set_case_sensitive (false); + try { mp_delegate->start (&netlist); @@ -775,13 +778,7 @@ std::string NetlistSpiceReader::read_name_with_case (tl::Extractor &ex) std::string NetlistSpiceReader::read_name (tl::Extractor &ex) { - // TODO: allow configuring Spice reader as case sensitive? - // this is easy to do: just avoid to_upper here: -#if 1 - return tl::to_upper_case (read_name_with_case (ex)); -#else - return read_name_with_case (ex); -#endif + return mp_netlist->normalize_name (read_name_with_case (ex)); } bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &element, const std::string &name) diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 98b53e97c..d01d37c2b 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -131,13 +131,13 @@ Region::clear () void Region::reserve (size_t n) { - flat_region ()->reserve (n); + mutable_region ()->reserve (n); } template Region &Region::transform (const T &trans) { - flat_region ()->transform (trans); + mutable_region ()->transform (trans); return *this; } @@ -151,7 +151,7 @@ template DB_PUBLIC Region &Region::transform (const db::IMatrix3d &); template void Region::insert (const Sh &shape) { - flat_region ()->insert (shape); + mutable_region ()->insert (shape); } template DB_PUBLIC void Region::insert (const db::Box &); @@ -161,31 +161,36 @@ template DB_PUBLIC void Region::insert (const db::Path &); void Region::insert (const db::Shape &shape) { - flat_region ()->insert (shape); + mutable_region ()->insert (shape); } template void Region::insert (const db::Shape &shape, const T &trans) { - flat_region ()->insert (shape, trans); + mutable_region ()->insert (shape, trans); } template DB_PUBLIC void Region::insert (const db::Shape &, const db::ICplxTrans &); template DB_PUBLIC void Region::insert (const db::Shape &, const db::Trans &); template DB_PUBLIC void Region::insert (const db::Shape &, const db::Disp &); -FlatRegion * -Region::flat_region () +MutableRegion * +Region::mutable_region () { - FlatRegion *region = dynamic_cast (mp_delegate); + MutableRegion *region = dynamic_cast (mp_delegate); if (! region) { - region = new FlatRegion (); + + FlatRegion *flat_region = new FlatRegion (); + region = flat_region; + if (mp_delegate) { - region->RegionDelegate::operator= (*mp_delegate); // copy basic flags - region->insert_seq (begin ()); - region->set_is_merged (mp_delegate->is_merged ()); + flat_region->RegionDelegate::operator= (*mp_delegate); // copy basic flags + flat_region->insert_seq (begin ()); + flat_region->set_is_merged (mp_delegate->is_merged ()); } - set_delegate (region); + + set_delegate (flat_region); + } return region; @@ -276,6 +281,13 @@ Region::smoothed (coord_type d, bool keep_hv) const return processed (SmoothingProcessor (d, keep_hv)); } +db::Region & +Region::flatten () +{ + mutable_region ()->flatten (); + return *this; +} + void Region::snap (db::Coord gx, db::Coord gy) { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index fea1d8e8b..a14761c42 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -37,7 +37,7 @@ namespace db { class EdgeFilterBase; -class FlatRegion; +class MutableRegion; class EmptyRegion; class DeepShapeStore; class TransformationReducer; @@ -1644,11 +1644,7 @@ public: * * This method will turn any region into a flat shape collection. */ - db::Region &flatten () - { - flat_region (); - return *this; - } + db::Region &flatten (); /** * @brief Returns true, if the region has valid polygons stored within itself @@ -1776,7 +1772,7 @@ private: RegionDelegate *mp_delegate; void set_delegate (RegionDelegate *delegate, bool keep_attributes = true); - FlatRegion *flat_region (); + db::MutableRegion *mutable_region(); }; /** diff --git a/src/db/db/dbText.h b/src/db/db/dbText.h index 20eab0e0f..5d11d9511 100644 --- a/src/db/db/dbText.h +++ b/src/db/db/dbText.h @@ -688,7 +688,8 @@ public: text &transform (const Tr &t) { typedef typename Tr::target_coord_type target_coord_type; - m_trans = simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()); + fixpoint_trans fp (t); + m_trans = simple_trans ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()); m_size = t.ctrans (m_size); return *this; } @@ -708,13 +709,14 @@ public: text transformed (const Tr &t) const { typedef typename Tr::target_coord_type target_coord_type; + fixpoint_trans fp (t); size_t p = (size_t) mp_ptr; if (p & 1) { - return text (reinterpret_cast (p - 1), simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign); + return text (reinterpret_cast (p - 1), simple_trans ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign); } else if (mp_ptr) { - return text (mp_ptr, simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign); + return text (mp_ptr, simple_trans ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign); } else { - return text (simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign); + return text (simple_trans ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign); } } diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index f0e5d4ee8..8bf367a23 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -93,25 +93,27 @@ Texts::Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I template void Texts::insert (const Sh &shape) { - flat_texts ()->insert (shape); + mutable_texts ()->insert (shape); } template DB_PUBLIC void Texts::insert (const db::Text &); void Texts::insert (const db::Shape &shape) { - flat_texts ()->insert (shape); + mutable_texts ()->insert (shape); } template void Texts::insert (const db::Shape &shape, const T &trans) { - flat_texts ()->insert (shape, trans); + mutable_texts ()->insert (shape, trans); } template DB_PUBLIC void Texts::insert (const db::Shape &, const db::ICplxTrans &); template DB_PUBLIC void Texts::insert (const db::Shape &, const db::Trans &); template DB_PUBLIC void Texts::insert (const db::Shape &, const db::Disp &); +template DB_PUBLIC void Texts::insert (const db::Shape &, const db::IMatrix2d &); +template DB_PUBLIC void Texts::insert (const db::Shape &, const db::IMatrix3d &); void Texts::clear () { @@ -120,13 +122,18 @@ void Texts::clear () void Texts::reserve (size_t n) { - flat_texts ()->reserve (n); + mutable_texts ()->reserve (n); +} + +void Texts::flatten () +{ + mutable_texts ()->flatten (); } template Texts &Texts::transform (const T &trans) { - flat_texts ()->transform (trans); + mutable_texts ()->transform (trans); return *this; } @@ -161,9 +168,9 @@ void Texts::set_delegate (TextsDelegate *delegate) } } -FlatTexts *Texts::flat_texts () +MutableTexts *Texts::mutable_texts () { - FlatTexts *texts = dynamic_cast (mp_delegate); + MutableTexts *texts = dynamic_cast (mp_delegate); if (! texts) { texts = new FlatTexts (); if (mp_delegate) { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 2f9266024..c5cf5eec3 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -36,7 +36,7 @@ namespace db { class TextFilterBase; -class FlatTexts; +class MutableTexts; class EmptyTexts; class Edges; class Region; @@ -491,10 +491,7 @@ public: * * This method will turn any edge pair collection into a flat one. */ - void flatten () - { - flat_texts (); - } + void flatten (); /** * @brief Returns true, if the text set has valid texts stored within itself @@ -616,7 +613,7 @@ private: TextsDelegate *mp_delegate; void set_delegate (TextsDelegate *delegate); - FlatTexts *flat_texts (); + MutableTexts *mutable_texts(); }; } diff --git a/src/db/db/dbTrans.h b/src/db/db/dbTrans.h index db83aa018..7b11e57ba 100644 --- a/src/db/db/dbTrans.h +++ b/src/db/db/dbTrans.h @@ -376,6 +376,32 @@ public: // .. nothing else .. } + /** + * @brief Reduction + */ + template + explicit fixpoint_trans (const T &t) + : m_f (0) + { + *this = t.fp_trans (); + } + + /** + * @brief Reduction from a matrix2d + */ + explicit fixpoint_trans (const db::matrix_2d &t) + { + m_f = ((int (floor (t.angle () / 90.0 + 0.5) + 4)) % 4) + (t.is_mirror () ? 4 : 0); + } + + /** + * @brief Reduction from a matrix3d + */ + explicit fixpoint_trans (const db::matrix_3d &t) + { + m_f = ((int (floor (t.angle () / 90.0 + 0.5) + 4)) % 4) + (t.is_mirror () ? 4 : 0); + } + /** * @brief Returns true, if the transformation is unity */ @@ -420,7 +446,18 @@ public: // .. nothing else .. } - /** + /** + * @brief The standard constructor using a code rather than angle and mirror and no displacement + * + * @param f The rotation/mirror code (r0 .. m135 constants) + */ + explicit fixpoint_trans (unsigned int f) + : m_f (f) + { + // .. nothing else .. + } + + /** * @brief The rotation/mirror codes */ static const int r0 = 0; // No rotation @@ -1651,7 +1688,7 @@ public: { tl_assert (! m.has_shear ()); tl_assert (! m.has_perspective ()); - std::pair mag = m.mag (); + std::pair mag = m.mag2 (); tl_assert (fabs (mag.first - mag.second) < 1e-10); double rot = m.angle () * M_PI / 180.0; m_mag = m.is_mirror () ? -mag.first : mag.first; @@ -1674,7 +1711,7 @@ public: : m_u (u) { tl_assert (! m.has_shear ()); - std::pair mag = m.mag (); + std::pair mag = m.mag2 (); tl_assert (fabs (mag.first - mag.second) < 1e-10); double rot = m.angle () * M_PI / 180.0; m_mag = m.is_mirror () ? -mag.first : mag.first; diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 5b53b0095..468be697c 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -140,6 +140,68 @@ static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &pol return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes); } +static void join_net_names (db::LayoutToNetlist *l2n, const std::string &s) +{ + l2n->join_net_names (tl::GlobPattern (s)); +} + +static std::string dump_joined_net_names (const db::LayoutToNetlist *l2n) +{ + const std::list &jn = l2n->joined_net_names (); + std::vector s; + for (std::list::const_iterator j = jn.begin (); j != jn.end (); ++j) { + s.push_back (j->pattern ()); + } + return tl::join (s, ","); +} + +static void join_net_names2 (db::LayoutToNetlist *l2n, const std::string &c, const std::string &s) +{ + l2n->join_net_names (tl::GlobPattern (c), tl::GlobPattern (s)); +} + +static std::string dump_joined_net_names_per_cell (const db::LayoutToNetlist *l2n) +{ + const std::list > &jn = l2n->joined_net_names_per_cell (); + std::vector s; + for (std::list >::const_iterator i = jn.begin (); i != jn.end (); ++i) { + s.push_back (i->first.pattern () + ":" + i->second.pattern ()); + } + return tl::join (s, ","); +} + +static void join_nets (db::LayoutToNetlist *l2n, const std::set &s) +{ + l2n->join_nets (s); +} + +static std::string dump_joined_nets (const db::LayoutToNetlist *l2n) +{ + const std::list > &jn = l2n->joined_nets (); + std::vector s; + for (std::list >::const_iterator j = jn.begin (); j != jn.end (); ++j) { + std::vector t (j->begin (), j->end ()); + s.push_back (tl::join (t, "+")); + } + return tl::join (s, ","); +} + +static void join_nets2 (db::LayoutToNetlist *l2n, const std::string &c, const std::set &s) +{ + l2n->join_nets (tl::GlobPattern (c), s); +} + +static std::string dump_joined_nets_per_cell (const db::LayoutToNetlist *l2n) +{ + const std::list > > &jn = l2n->joined_nets_per_cell (); + std::vector s; + for (std::list > >::const_iterator i = jn.begin (); i != jn.end (); ++i) { + std::vector t (i->second.begin (), i->second.end ()); + s.push_back (i->first.pattern () + ":" + tl::join (t, "+")); + } + return tl::join (s, ","); +} + Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), "@brief Creates a new extractor connected to an original layout\n" @@ -334,7 +396,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", ) + gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"), "@brief Defines an intra-layer connection for the given layer.\n" - "The layer is either an original layer created with \\make_layer and it's variants or\n" + "The layer is either an original layer created with \\make_incluidelayer and it's variants or\n" "a derived layer. Certain limitations apply. It's safe to use\n" "boolean operations for deriving layers. Other operations are applicable as long as they are\n" "capable of delivering hierarchical layers.\n" @@ -372,13 +434,38 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"), "@brief Gets the global net name for the given global net ID." ) + - gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_net_names", std::string (), "\"\""), gsi::arg ("include_floating_subcircuits", false), - "@brief Runs the netlist extraction\n" - "'join_net_names' is a glob expression for labels. Nets on top level carrying the same label which matches this glob " - "expression will be connected implicitly even if there is no physical connection. This feature is useful to simulate a connection " - "which will be made later when integrating the component.\n" + gsi::method ("include_floating_subcircuits=", &db::LayoutToNetlist::set_include_floating_subcircuits, gsi::arg ("flag"), + "@brief Sets a flag indicating whether to include floating subcircuits in the netlist.\n" "\n" - "Valid glob expressions are:\n" + "With 'include_floating_subcircuits' set to true, subcircuits with no connection to their parent " + "circuit are still included in the circuit as floating subcircuits. Specifically on flattening this " + "means that these subcircuits are properly propagated to their parent instead of appearing as " + "additional top circuits.\n" + "\n" + "This attribute has been introduced in version 0.27 and replaces the arguments of \\extract_netlist." + ) + + gsi::method ("include_floating_subcircuits", &db::LayoutToNetlist::include_floating_subcircuits, + "@brief Gets a flag indicating whether to include floating subcircuits in the netlist.\n" + "See \\include_floating_subcircuits= for details.\n" + "\n" + "This attribute has been introduced in version 0.27.\n" + ) + + gsi::method ("clear_join_net_names", &db::LayoutToNetlist::clear_join_net_names, + "@brief Clears all implicit net joining expressions.\n" + "See \\extract_netlist for more details about this feature.\n" + "\n" + "This method has been introduced in version 0.27 and replaces the arguments of \\extract_netlist." + ) + + gsi::method_ext ("join_net_names", &join_net_names, gsi::arg ("pattern"), + "@brief Specifies another pattern for implicit joining of nets for the top level cell.\n" + "Use this method to register a pattern for net labels considered in implicit net joining. Implicit net joining " + "allows connecting multiple parts of the same nets (e.g. supply rails) without need for a physical connection. " + "The pattern specifies labels to look for. When parts are labelled with a name matching the expression, " + "the parts carrying the same name are joined.\n" + "\n" + "This method adds a new pattern. Use \\clear_join_net_names to clear the registered pattern.\n" + "\n" + "Each pattern is a glob expression. Valid glob expressions are:\n" "@ul\n" "@li \"\" no implicit connections.@/li\n" "@li \"*\" to make all labels candidates for implicit connections.@/li\n" @@ -389,27 +476,45 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "Label matching is case sensitive.\n" "\n" - "With 'include_floating_subcircuits' set to true, subcircuits with no connection to their parent " - "circuit are still included in the circuit as floating subcircuits. Specifically on flattening this " - "means that these subcircuits are property propagated to their parent instead of appearing as " - "additional top circuits.\n" + "This method has been introduced in version 0.27 and replaces the arguments of \\extract_netlist." + ) + + gsi::method_ext ("join_net_names", &join_net_names2, gsi::arg ("cell_pattern"), gsi::arg ("pattern"), + "@brief Specifies another pattern for implicit joining of nets for the cells from the given cell pattern.\n" + "This method allows applying implicit net joining for specific cells, not only for the top cell.\n" + "\n" + "This method adds a new pattern. Use \\clear_join_net_names to clear the registered pattern.\n" + "\n" + "This method has been introduced in version 0.27 and replaces the arguments of \\extract_netlist." + ) + + gsi::method ("clear_join_nets", &db::LayoutToNetlist::clear_join_nets, + "@brief Clears all explicit net joining expressions.\n" + "See \\extract_netlist for more details about this feature.\n" + "\n" + "Explicit net joining has been introduced in version 0.27." + ) + + gsi::method_ext ("join_nets", &join_nets, gsi::arg ("net_names"), + "@brief Specifies another name list for explicit joining of nets for the top level cell.\n" + "Use this method to join nets from the set of net names. All these nets will be connected together forming a single net.\n" + "Explicit joining will imply implicit joining for the involved nets - partial nets involved will be connected too (intra-net joining).\n" + "\n" + "This method adds a new name list. Use \\clear_join_nets to clear the registered pattern.\n" + "\n" + "Explicit net joining has been introduced in version 0.27." + ) + + gsi::method_ext ("join_nets", &join_nets2, gsi::arg ("cell_pattern"), gsi::arg ("net_names"), + "@brief Specifies another name list for explicit joining of nets for the cells from the given cell pattern.\n" + "This method allows applying explicit net joining for specific cells, not only for the top cell.\n" + "\n" + "This method adds a new name list. Use \\clear_join_nets to clear the registered pattern.\n" + "\n" + "Explicit net joining has been introduced in version 0.27." + ) + + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, + "@brief Runs the netlist extraction\n" "\n" "See the class description for more details.\n" "\n" - "The 'include_floating_subcircuits' argument has been introduced in version 0.26.2." - ) + - gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_net_names"), gsi::arg ("join_net_names_per_cell"), gsi::arg ("include_floating_subcircuits", false), - "@brief Runs the netlist extraction\n" - "This method runs the netlist extraction like the two-parameter version. In addition to the latter, this method " - "can be given a per-cell net label joining specification in 'join_net_names_per_cell'. The keys of this array " - "are cell names or cell names or cell name match expressions (glob style). The values are label match expressions.\n" - "\n" - "If not an empty string, the 'join_net_names' label match expression is applied to the top cell. For all non-top cells " - "the per-cell label match expression is applied and determines what labels are joined into single nets. " - "As the keys of 'join_net_names_per_cell' are glob expressions, a single cell may fall into more than one category. In this " - "case, the label match pattern are combined. In any case, the 'join_net_names' has priority for the top cell.\n" - "\n" - "This variant of 'extract_netlist' has been introduced in version 0.26.2." + "This method has been made parameter-less in version 0.27. Use \\include_floating_subcircuits= and \\join_net_names as substitutes for the arguments of previous versions." ) + gsi::method_ext ("internal_layout", &l2n_internal_layout, "@brief Gets the internal layout\n" @@ -658,7 +763,13 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "considered.\n" "\n" "This variant has been introduced in version 0.26.6.\n" - ), + ) + + // test API + gsi::method_ext ("dump_joined_net_names", &dump_joined_net_names, "@hide") + + gsi::method_ext ("dump_joined_net_names_per_cell", &dump_joined_net_names_per_cell, "@hide") + + gsi::method_ext ("dump_joined_nets", &dump_joined_nets, "@hide") + + gsi::method_ext ("dump_joined_nets_per_cell", &dump_joined_nets_per_cell, "@hide") + , "@brief A generic framework for extracting netlists from layouts\n" "\n" "This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor\n" @@ -674,7 +785,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", " hierarchy and the layout taken as input.\n" "@/li\n" "@li Preparation\n" - " In this step, the device recognitions and extraction layers are drawn from\n" + " In this step, the device recognition and extraction layers are drawn from\n" " the framework. Derived can now be computed using boolean operations.\n" " Methods to use in this step are \\make_layer and it's variants.\n" " Layer preparation is not necessarily required to happen before all\n" diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index ee10d9365..3b886efcf 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -519,6 +519,9 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "@brief Sets the maximum seach depth\n" "This value limits the search depth of the backtracking algorithm to the\n" "given number of jumps.\n" + "\n" + "By default, from version 0.27 on the depth is unlimited and can be reduced in cases where runtimes need to be limited at the cost " + "less elaborate matching evaluation.\n" ) + gsi::method ("max_depth", &db::NetlistComparer::max_depth, "@brief Gets the maximum seach depth\n" @@ -531,6 +534,9 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "net matches. Backtracking will stop when the maximum number of options\n" "has been exceeded.\n" "\n" + "By default, from version 0.27 on the complexity is unlimited and can be reduced in cases where runtimes need to be limited at the cost " + "less elaborate matching evaluation.\n" + "\n" "As the computational complexity is the square of the branch count,\n" "this value should be adjusted carefully.\n" ) + diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 15740ccde..543b60704 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -55,7 +55,7 @@ TEST(1) EXPECT_EQ (r.bbox ().to_string (), "(0,0;100,200)"); EXPECT_EQ (r.transformed (db::Trans (db::Vector (1, 2))).bbox ().to_string (), "(1,2;101,202)"); EXPECT_EQ (r.empty (), false); - EXPECT_EQ (r.is_merged (), true); + EXPECT_EQ (r.is_merged (), false); EXPECT_EQ (r.begin ().at_end (), false); db::Edges r1 = r; diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 4718c6a2b..3e05c7e97 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -3156,7 +3156,7 @@ TEST(12_FlattenCircuitDoesFlattenLayout) db::compare_layouts (_this, ly, au); } -TEST(13_JoinNets) +TEST(13_JoinNetNames) { db::Layout ly; db::LayerMap lmap; @@ -3299,7 +3299,8 @@ TEST(13_JoinNets) l2n.connect_global (*rbulk, "VSS"); // Extract with joining VSS and VDD - l2n.extract_netlist ("{VSS,VDD}"); + l2n.join_net_names (tl::GlobPattern ("{VSS,VDD}")); + l2n.extract_netlist (); // debug layers produced for nets // 201/0 -> Well @@ -3385,3 +3386,249 @@ TEST(13_JoinNets) db::compare_layouts (_this, ly, au); } + +TEST(14_JoinNets) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int nwell_lbl = define_layer (ly, lmap, 1, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 10); + unsigned int nplus = define_layer (ly, lmap, 11); + 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 ().layer_map = lmap; + options.get_options ().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_l14.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 ())); + + std::unique_ptr rbulk (l2n.make_layer ("bulk")); + std::unique_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::unique_ptr rnwell_lbl (l2n.make_layer (nwell_lbl, "nwell_lbl")); + std::unique_ptr ractive (l2n.make_layer (active, "active")); + std::unique_ptr rpplus (l2n.make_layer (pplus, "pplus")); + std::unique_ptr rnplus (l2n.make_layer (nplus, "nplus")); + std::unique_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::unique_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::unique_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::unique_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::unique_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::unique_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::unique_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::unique_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::unique_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + db::Region ractive_in_nwell = *ractive & *rnwell; + db::Region rpactive = ractive_in_nwell & *rpplus; + db::Region rntie = ractive_in_nwell & *rnplus; + db::Region rpgate = rpactive & *rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region ractive_outside_nwell = *ractive - *rnwell; + db::Region rnactive = ractive_outside_nwell & *rnplus; + db::Region rptie = ractive_outside_nwell & *rpplus; + 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 (20, 0)); // 20/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + 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); + rpsd.insert_into (&ly, tc.cell_index (), lptie); + rnsd.insert_into (&ly, tc.cell_index (), lntie); + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + 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 + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (rntie, *rnwell); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rnwell, *rnwell_lbl); // attaches labels + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + l2n.connect_global (*rbulk, "BULK"); + l2n.connect_global (rptie, "BULK"); + + // Extract while joining VSS with BULK and VDD with NWELL + std::set jn; + jn.insert ("VDD"); + jn.insert ("NWELL"); + l2n.join_nets (tl::GlobPattern ("INV2"), jn); + jn.clear (); + jn.insert ("VSS"); + jn.insert ("BULK"); + l2n.join_nets (tl::GlobPattern ("INV2"), jn); + + // This will trigger an implicit connection on top level (side effect of explicit connections) + jn.clear (); + jn.insert ("VDD"); + l2n.join_nets (jn); + + l2n.extract_netlist (); + + // debug layers produced for nets + // 201/0 -> Well + // 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 + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = 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, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); + dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // compare netlist as string + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), + "circuit RINGO ();\n" + " subcircuit INV2 $1 ('NWELL,VDD'=VDD,IN=$I15,$3=FB,OUT=OSC,VSS=VSS);\n" + " subcircuit INV2 $2 ('NWELL,VDD'=VDD,IN=FB,$3=$I26,OUT=$I25,VSS=VSS);\n" + " subcircuit INV2 $3 ('NWELL,VDD'=VDD,IN=$3,$3=$I30,OUT=$I12,VSS=VSS);\n" + " subcircuit INV2 $4 ('NWELL,VDD'=VDD,IN=$I10,$3=$I29,OUT=$3,VSS=VSS);\n" + " subcircuit INV2 $5 ('NWELL,VDD'=VDD,IN=$I12,$3=$I31,OUT=$I13,VSS=VSS);\n" + " subcircuit INV2 $6 ('NWELL,VDD'=VDD,IN=$I13,$3=$I32,OUT=$I14,VSS=VSS);\n" + " subcircuit INV2 $7 ('NWELL,VDD'=VDD,IN=$I14,$3=$I33,OUT=$I15,VSS=VSS);\n" + " subcircuit INV2 $8 ('NWELL,VDD'=VDD,IN=$I25,$3=$I27,OUT=$I9,VSS=VSS);\n" + " subcircuit INV2 $9 ('NWELL,VDD'=VDD,IN=$I9,$3=$I28,OUT=$I10,VSS=VSS);\n" + "end;\n" + "circuit INV2 ('NWELL,VDD'='NWELL,VDD',IN=IN,$3=$3,OUT=OUT,VSS=VSS);\n" + " device PMOS $1 (S=$3,G=IN,D='NWELL,VDD',B='NWELL,VDD') (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S='NWELL,VDD',G=$3,D=OUT,B='NWELL,VDD') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$3,G=IN,D=VSS,B=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=$3,D=OUT,B=VSS) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$3,$2='NWELL,VDD',$3=IN);\n" + " subcircuit TRANS $2 ($1='NWELL,VDD',$2=OUT,$3=$3);\n" + " subcircuit TRANS $3 ($1=$3,$2=VSS,$3=IN);\n" + " subcircuit TRANS $4 ($1=VSS,$2=OUT,$3=$3);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\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_au14_circuits.gds"); + + db::compare_layouts (_this, ly, au); +} + diff --git a/src/db/unit_tests/dbMatrixTests.cc b/src/db/unit_tests/dbMatrixTests.cc index 605b41f04..d64e249a2 100644 --- a/src/db/unit_tests/dbMatrixTests.cc +++ b/src/db/unit_tests/dbMatrixTests.cc @@ -114,10 +114,10 @@ TEST(1) EXPECT_EQ (tl::to_string (db::Matrix2d::shear (17).has_shear ()), "true"); EXPECT_EQ (tl::to_string (db::Matrix2d::shear (40).shear_angle ()), "40"); EXPECT_EQ (tl::to_string (db::Matrix2d::shear (-40).shear_angle ()), "-40"); - EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (17.5).inverted ().mag ().first), "17.5"); - EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (17.5).inverted ().mag ().second), "17.5"); - EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (27.5, 7.5).inverted ().mag ().first), "27.5"); - EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (27.5, 7.5).inverted ().mag ().second), "7.5"); + EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (17.5).inverted ().mag2 ().first), "17.5"); + EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (17.5).inverted ().mag2 ().second), "17.5"); + EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (27.5, 7.5).inverted ().mag2 ().first), "27.5"); + EXPECT_EQ (tl::to_string (1.0 / db::Matrix2d::mag (27.5, 7.5).inverted ().mag2 ().second), "7.5"); EXPECT_EQ (tl::to_string (db::Matrix2d::mirror (true).inverted ().is_mirror ()), "true"); EXPECT_EQ (tl::to_string (db::Matrix2d::mirror (false).inverted ().is_mirror ()), "false"); EXPECT_EQ (tl::to_string (db::Matrix2d::rotation (25).inverted ().angle ()), "-25"); diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 5ee34afc5..6f0bbf378 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -958,19 +958,38 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) // extract the nets + std::list gp; + db::Netlist nl2 = nl; - net_ex.set_joined_net_names ("{VDDZ,VSSZ,NEXT,FB}"); + gp.clear (); + gp.push_back (tl::GlobPattern ("{VDDZ,VSSZ,NEXT,FB}")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl2, cl); EXPECT_EQ (all_net_names_unique (nl2), true); nl2 = nl; - net_ex.set_joined_net_names ("{VDDZ,VSSZ,NEXT}"); + gp.clear (); + gp.push_back (tl::GlobPattern ("VDDZ")); + gp.push_back (tl::GlobPattern ("VSSZ")); + gp.push_back (tl::GlobPattern ("NEXT")); + gp.push_back (tl::GlobPattern ("FB")); + net_ex.set_joined_net_names (gp); + net_ex.extract_nets (dss, 0, conn, nl2, cl); + + EXPECT_EQ (all_net_names_unique (nl2), true); + + nl2 = nl; + gp.clear (); + gp.push_back (tl::GlobPattern ("{VDDZ,VSSZ,NEXT}")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl2, cl); EXPECT_EQ (all_net_names_unique (nl2), false); - net_ex.set_joined_net_names ("*"); + gp.clear (); + gp.push_back (tl::GlobPattern ("*")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl, cl); EXPECT_EQ (all_net_names_unique (nl), true); @@ -1233,7 +1252,9 @@ TEST(4_ResAndCapExtraction) // extract the nets - net_ex.set_joined_net_names ("*"); + std::list gp; + gp.push_back (tl::GlobPattern ("*")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl, cl); // Flatten device circuits @@ -1507,7 +1528,9 @@ TEST(5_ResAndCapWithBulkExtraction) // extract the nets - net_ex.set_joined_net_names ("*"); + std::list gp; + gp.push_back (tl::GlobPattern ("*")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl, cl); // Flatten device circuits @@ -1749,7 +1772,9 @@ TEST(6_BJT3TransistorExtraction) // extract the nets - net_ex.set_joined_net_names ("*"); + std::list gp; + gp.push_back (tl::GlobPattern ("*")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl, cl); // Flatten device circuits @@ -1915,7 +1940,9 @@ TEST(7_DiodeExtraction) // extract the nets - net_ex.set_joined_net_names ("*"); + std::list gp; + gp.push_back (tl::GlobPattern ("*")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl, cl); // cleanup + completion @@ -2048,7 +2075,9 @@ TEST(8_DiodeExtractionScaled) // extract the nets - net_ex.set_joined_net_names ("*"); + std::list gp; + gp.push_back (tl::GlobPattern ("*")); + net_ex.set_joined_net_names (gp); net_ex.extract_nets (dss, 0, conn, nl, cl); // cleanup + completion @@ -2922,3 +2951,255 @@ TEST(13_RemoveDummyPins) ); } +TEST(14_JoinNets) +{ + 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 ().layer_map = lmap; + options.get_options ().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_join_nets.gds"); + + 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 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; + + // Global + + db::Region bulk (dss); + + // 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); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["W"] = &rnwell; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, 0, dl, nl, cl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["W"] = &bulk; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, 0, dl, nl, cl); + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + + // Global nets + conn.connect_global (bulk, "BULK"); + conn.connect_global (rnwell, "NWELL"); + + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels + + // extract the nets + + std::list > jn; + + jn.push_back (std::set ()); + jn.back ().insert ("BULK"); + jn.back ().insert ("VSS"); + + jn.push_back (std::set ()); + jn.back ().insert ("NWELL"); + jn.back ().insert ("VDD"); + + net_ex.set_joined_nets ("INV2", jn); + + std::list gp; + gp.push_back (tl::GlobPattern ("NEXT")); + gp.push_back (tl::GlobPattern ("FB")); + net_ex.set_joined_net_names (gp); + + net_ex.extract_nets (dss, 0, conn, nl, cl); + + EXPECT_EQ (all_net_names_unique (nl), true); + + // 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 + // 212/0 -> Bulk + // 213/0 -> NWell + std::map dump_map; + dump_map [layer_of (bulk) ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [layer_of (rnwell) ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = 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)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm, true); + + // compare netlist as string + CHECKPOINT (); + db::compare_netlist (_this, nl, + "circuit RINGO ();\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $2 (IN=FB,$2=$I20,OUT=$I19,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $3 (IN=NEXT,$2=$I25,OUT=$I5,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $4 (IN=$I3,$2=$I24,OUT=NEXT,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $5 (IN=$I5,$2=$I26,OUT=$I6,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $6 (IN=$I6,$2=$I27,OUT=$I7,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $7 (IN=$I7,$2=$I28,OUT=$I8,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $8 (IN=$I19,$2=$I21,OUT=$I1,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $9 (IN=$I1,$2=$I22,OUT=$I2,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $10 (IN=$I2,$2=$I23,OUT=$I3,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,VDD=VDD,VSS=VSS);\n" + " device PMOS $1 (S=$2,G=IN,D=VDD,B=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$2,D=OUT,B=VDD) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=VSS,B=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=$2,D=OUT,B=VSS) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$2,$2=VSS,$3=IN);\n" + " subcircuit TRANS $2 ($1=$2,$2=VDD,$3=IN);\n" + " subcircuit TRANS $3 ($1=VDD,$2=OUT,$3=$2);\n" + " subcircuit TRANS $4 ($1=VSS,$2=OUT,$3=$2);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" + ); + + // doesn't do anything here, but we test that this does not destroy anything: + nl.combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + nl.make_top_level_pins (); + nl.purge (); + + // compare netlist as string + CHECKPOINT (); + db::compare_netlist (_this, nl, + "circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VDD,VDDZ'='VDD,VDDZ','VSS,VSSZ'='VSS,VSSZ');\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $2 (IN=FB,$2=$I20,OUT=$I19,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $3 (IN=NEXT,$2=$I25,OUT=$I5,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $4 (IN=$I3,$2=$I24,OUT=NEXT,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $5 (IN=$I5,$2=$I26,OUT=$I6,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $6 (IN=$I6,$2=$I27,OUT=$I7,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $7 (IN=$I7,$2=$I28,OUT=$I8,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $8 (IN=$I19,$2=$I21,OUT=$I1,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $9 (IN=$I1,$2=$I22,OUT=$I2,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + " subcircuit INV2 $10 (IN=$I2,$2=$I23,OUT=$I3,VDD='VDD,VDDZ',VSS='VSS,VSSZ');\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,VDD=VDD,VSS=VSS);\n" + " device PMOS $1 (S=$2,G=IN,D=VDD,B=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$2,D=OUT,B=VDD) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=VSS,B=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=$2,D=OUT,B=VSS) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\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_join_nets.gds"); + + db::compare_layouts (_this, ly, au); +} + diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index fd045c68e..d7cd228ed 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1854,6 +1854,14 @@ CODE # @synopsis connect_implicit(cell_pattern, label_pattern) # See \Netter#connect_implicit for a description of that function. + # %DRC% + # @name connect_explicit + # @brief Specifies explicit net connections + # @synopsis connect_explicit(net_names) + # @synopsis connect_explicit(cell_pattern, net_names) + # See \Netter#connect_explicit for a description of that function. + # Net names is an array (use square brackets to list the net names). + # %DRC% # @name antenna_check # @brief Performs an antenna check @@ -1892,6 +1900,7 @@ CODE connect connect_global connect_implicit + connect_explicit device_scaling extract_devices l2n_data diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 747179495..05b7fb6c2 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -67,6 +67,8 @@ module DRC @netlisted = false @connect_implicit = [] @connect_implicit_per_cell = {} + @connect_explicit = [] + @connect_explicit_per_cell = {} @l2n = nil @lnum = 0 @device_scaling = 1.0 @@ -241,6 +243,8 @@ module DRC @netlisted = false @connect_implicit = [] @connect_implicit_per_cell = {} + @connect_explicit = [] + @connect_explicit_per_cell = {} _clear_data end @@ -286,6 +290,67 @@ module DRC end + # %DRC% + # @name connect_explicit + # @brief Specifies a list of net names for nets to connect explicitly + # @synopsis connect_explicit(net_names) + # @synopsis connect_explicit(cell_pattern, net_names) + # Use this method to explicitly connect nets even if there is no physical connection. + # As this breaks with the concept of physical verification, this feature should be used + # with care. + # + # The first version of this function will connect all nets listed in the "net_names" array + # in the top level cell. The second version takes a cell name pattern and connects all nets listed + # in "net_names" for cells matching this pattern. + # + # A use case for this method is the following: consider a set of standard cells. These do not have a bulk + # or n-well pin in the schematics. They also do not have build in tie-down diodes for the + # substrate connections. In this case there is a build-in discrepancy between the + # schematics and the layout: bulk and VSS are separate nets within the layout, but the + # schematic does not list them as separate. The solution is to make an explicit connection + # between VDD and n-well and VSS and bulk, provided VDD and VSS are properly labelled as "VDD" and "VSS" + # and n-well and bulk are accessible as named nets (for bulk you can use "connect_global"). + # + # The following code will establish an explicit connection for all cells called "INV.." between + # BULK and VSS nets: + # + # @code + # connect_global(bulk, "BULK") + # ... + # connect_explicit("INV*", [ "BULK", "VSS" ]) + # @/code + # + # Explicit connections also imply implicit connections between different parts of + # one of the nets. In the example before, "VSS" pieces without a physical connection + # will also be connected. + # + # When you use explicit connections you should make sure by other ways that the connection + # is made physically. For example, for the bulk/n-well pin example above, by enforcing at least one + # tie-down diode per n-well island and in the substrate by means of a DRC rule. + # + # The explicit connections are applied on the next net extraction and cleared + # on "clear_connections". + + def connect_explicit(arg1, arg2 = nil) + + @engine._context("connect_explicit") do + + cleanup + if arg2 + arg2.is_a?(Array) || raise("The second argument has to be an array of strings") + arg2.find { |a| !a.is_a?(String) } && raise("The second argument has to be an array of strings") + arg1.is_a?(String) || raise("The first argument has to be a string") + @connect_explicit_per_cell[arg1] ||= [] + @connect_explicit_per_cell[arg1] << arg2 + else + arg1.is_a?(String) || raise("The argument has to be a string") + @connect_explicit << arg1 + end + + end + + end + # %DRC% # @brief Performs an antenna check # @name antenna_check @@ -481,16 +546,29 @@ module DRC # run extraction in a timed environment if ! @netlisted - # build a glob expression from the parts - expr = _join_glob_pattern(@connect_implicit) - - # build cell-pattern specific glob expressions from the parts - per_cell_expr = {} + # configure implicit net connections + @l2n.clear_join_net_names + @connect_implicit.each do |label_pattern| + @l2n.join_net_names(label_pattern) + end @connect_implicit_per_cell.each do |cell_pattern,label_pattern| - per_cell_expr[cell_pattern] = _join_glob_pattern(label_pattern) + label_pattern.each do |lp| + @l2n.join_net_names(cell_pattern, lp) + end end - @engine._cmd(@l2n, :extract_netlist, expr, per_cell_expr) + # configure explicit net connections + @l2n.clear_join_nets + @connect_explicit.each do |names| + @l2n.join_nets(names) + end + @connect_explicit_per_cell.each do |cell_pattern,name_lists| + name_lists.each do |names| + @l2n.join_nets(cell_pattern, names) + end + end + + @engine._cmd(@l2n, :extract_netlist) @netlisted = true end @@ -548,18 +626,6 @@ module DRC end end - def _join_glob_pattern(exprs) - - if exprs.size > 1 - expr = "{" + exprs.join(",") + "}" - else - expr = exprs[0] || "" - end - - expr - - end - def _make_data if @engine._dss diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index 52f6d583b..d383f6227 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -312,6 +312,17 @@ l1 = input(1, 0)

See Netter#connect for a description of that function.

+

"connect_explicit" - Specifies explicit net connections

+ +

Usage:

+
    +
  • connect_explicit(net_names)
  • +
  • connect_explicit(cell_pattern, net_names)
  • +
+

+See Netter#connect_explicit for a description of that function. +Net names is an array (use square brackets to list the net names). +

"connect_global" - Specifies a connection to a global net

Usage:

diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml index f57ed7a06..25d84a583 100644 --- a/src/lay/lay/doc/about/drc_ref_netter.xml +++ b/src/lay/lay/doc/about/drc_ref_netter.xml @@ -219,6 +219,50 @@ joins. Connections are accumulated. The connections defined so far can be cleared with
clear_connections.

+

"connect_explicit" - Specifies a list of net names for nets to connect explicitly

+ +

Usage:

+
    +
  • connect_explicit(net_names)
  • +
  • connect_explicit(cell_pattern, net_names)
  • +
+

+Use this method to explicitly connect nets even if there is no physical connection. +As this breaks with the concept of physical verification, this feature should be used +with care. +

+The first version of this function will connect all nets listed in the "net_names" array +in the top level cell. The second version takes a cell name pattern and connects all nets listed +in "net_names" for cells matching this pattern. +

+A use case for this method is the following: consider a set of standard cells. These do not have a bulk +or n-well pin in the schematics. They also do not have build in tie-down diodes for the +substrate connections. In this case there is a build-in discrepancy between the +schematics and the layout: bulk and VSS are separate nets within the layout, but the +schematic does not list them as separate. The solution is to make an explicit connection +between VDD and n-well and VSS and bulk, provided VDD and VSS are properly labelled as "VDD" and "VSS" +and n-well and bulk are accessible as named nets (for bulk you can use "connect_global"). +

+The following code will establish an explicit connection for all cells called "INV.." between +BULK and VSS nets: +

+

+connect_global(bulk, "BULK")
+...
+connect_explicit("INV*", [ "BULK", "VSS" ])
+
+

+Explicit connections also imply implicit connections between different parts of +one of the nets. In the example before, "VSS" pieces without a physical connection +will also be connected. +

+When you use explicit connections you should make sure by other ways that the connection +is made physically. For example, for the bulk/n-well pin example above, by enforcing at least one +tie-down diode per n-well island and in the substrate by means of a DRC rule. +

+The explicit connections are applied on the next net extraction and cleared +on "clear_connections". +

"connect_global" - Connects a layer with a global net

Usage:

diff --git a/src/lay/lay/doc/about/lvs_ref_netter.xml b/src/lay/lay/doc/about/lvs_ref_netter.xml index 91e11b964..04244ff71 100644 --- a/src/lay/lay/doc/about/lvs_ref_netter.xml +++ b/src/lay/lay/doc/about/lvs_ref_netter.xml @@ -114,13 +114,15 @@ netter.equivalent_pins("NAND2", 0, 1)

The circuit argument is either a circuit name (a string) or a Circuit object -from the schematic netlist. +from the schematic netlist. +

+Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists.

The pin arguments are zero-based pin numbers, where 0 is the first number, 1 the second etc. -If the netlist provides named pins, names can be used instead of numbers. +If the netlist provides named pins, names can be used instead of numbers. Again, use upper +case pin names for SPICE netlists.

-Before this method can be used, a schematic netlist needs to be loaded with -schematic. +Use this method andwhere in the script before the compare call.

"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist

@@ -174,6 +176,12 @@ with inherent ambiguity such as decoders, the complexity can be increased at the expense of potentially larger runtimes. The runtime penality is roughly proportional to the branch complexity. +

+By default, the branch complexity is unlimited, but it may +be reduced in order to limit the compare runtimes at the cost +of a less elaborate compare attempt. The preferred solution +however is to use labels for net name hints which also reduces +the depth.

"max_depth" - Configures the maximum search depth for net match deduction

@@ -191,6 +199,12 @@ the next net. With higher values for the depth, the algorithm pursues this "deduction path" in greater depth while with smaller values, the algorithm prefers picking nets in a random fashion as the seeds for this deduction path. The default value is 8. +

+By default, the depth is unlimited, but it may +be reduced in order to limit the compare runtimes at the cost +of a less elaborate compare attempt. The preferred solution +however is to use labels for net name hints which also reduces +the branch complexity.

"max_res" - Ignores resistors with a resistance above a certain value

@@ -223,11 +237,13 @@ This method will force an equivalence between the two circuits. By default, circuits are identified by name. If names are different, this method allows establishing an explicit correspondence.

+circuit_a is for the layout netlist, circuit_b for the schematic netlist. +Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists. +

One of the circuits may be nil. In this case, the corresponding other circuit is mapped to "nothing", i.e. ignored.

-Before this method can be used, a schematic netlist needs to be loaded with -schematic. +Use this method andwhere in the script before the compare call.

"same_device_classes" - Establishes an equivalence between the device classes

@@ -244,6 +260,9 @@ method allows establishing an explicit correspondence. Before this method can be used, a schematic netlist needs to be loaded with
schematic.

+class_a is for the layout netlist, class_b for the schematic netlist. +Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists. +

One of the device classes may be "nil". In this case, the corresponding other device class is mapped to "nothing", i.e. ignored.

@@ -260,7 +279,11 @@ schematic side.

Once a device class is mentioned with "same_device_classes", matching by name is disabled for this class. So after using 'same_device_classes("A", "B")' -"A" is no longer equivalent to "A" on the other side. +"A" is no longer equivalent to "A" on the other side. If you want "A" to +stay equivalent to "A" too, you need to use 'same_device_classes("A", "A")' +in addition. +

+Use this method andwhere in the script before the compare call.

"same_nets" - Establishes an equivalence between the nets

@@ -281,8 +304,10 @@ After using this function, the compare algorithm will consider these nets equiva Use this method to provide hints for the comparer in cases which are difficult to resolve otherwise.

-Before this method can be used, a schematic netlist needs to be loaded with -schematic. +circuit_a and net_a are for the layout netlist, circuit_b and net_b for the schematic netlist. +Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists. +

+Use this method andwhere in the script before the compare call.

"schematic" - Gets, sets or reads the reference netlist

diff --git a/src/lay/lay/doc/manual/bjt3_schematic.png b/src/lay/lay/doc/manual/bjt3_schematic.png index 3873fb729..503657f86 100644 Binary files a/src/lay/lay/doc/manual/bjt3_schematic.png and b/src/lay/lay/doc/manual/bjt3_schematic.png differ diff --git a/src/lay/lay/doc/manual/bjt4_schematic.png b/src/lay/lay/doc/manual/bjt4_schematic.png index 54c0565db..dbffef5e3 100644 Binary files a/src/lay/lay/doc/manual/bjt4_schematic.png and b/src/lay/lay/doc/manual/bjt4_schematic.png differ diff --git a/src/lay/lay/doc/manual/bjt_lateral.png b/src/lay/lay/doc/manual/bjt_lateral.png index 37e812d25..6ce72b9a9 100644 Binary files a/src/lay/lay/doc/manual/bjt_lateral.png and b/src/lay/lay/doc/manual/bjt_lateral.png differ diff --git a/src/lay/lay/doc/manual/bjt_vertical.png b/src/lay/lay/doc/manual/bjt_vertical.png index 4084da77f..c92fa8250 100644 Binary files a/src/lay/lay/doc/manual/bjt_vertical.png and b/src/lay/lay/doc/manual/bjt_vertical.png differ diff --git a/src/lay/lay/doc/manual/cap_schematic.png b/src/lay/lay/doc/manual/cap_schematic.png index de10904f2..a57ac7515 100644 Binary files a/src/lay/lay/doc/manual/cap_schematic.png and b/src/lay/lay/doc/manual/cap_schematic.png differ diff --git a/src/lay/lay/doc/manual/cap_with_bulk_schematic.png b/src/lay/lay/doc/manual/cap_with_bulk_schematic.png index 8390a4b3a..7013d757c 100644 Binary files a/src/lay/lay/doc/manual/cap_with_bulk_schematic.png and b/src/lay/lay/doc/manual/cap_with_bulk_schematic.png differ diff --git a/src/lay/lay/doc/manual/diode_schematic.png b/src/lay/lay/doc/manual/diode_schematic.png index f08b70d9f..92f842cab 100644 Binary files a/src/lay/lay/doc/manual/diode_schematic.png and b/src/lay/lay/doc/manual/diode_schematic.png differ diff --git a/src/lay/lay/doc/manual/inv_explicit.png b/src/lay/lay/doc/manual/inv_explicit.png new file mode 100644 index 000000000..16c68a272 Binary files /dev/null and b/src/lay/lay/doc/manual/inv_explicit.png differ diff --git a/src/lay/lay/doc/manual/inv_schematic.png b/src/lay/lay/doc/manual/inv_schematic.png index 083183604..64f82deca 100644 Binary files a/src/lay/lay/doc/manual/inv_schematic.png and b/src/lay/lay/doc/manual/inv_schematic.png differ diff --git a/src/lay/lay/doc/manual/inv_schematic2.png b/src/lay/lay/doc/manual/inv_schematic2.png index 2867dea7a..ec5ffae41 100644 Binary files a/src/lay/lay/doc/manual/inv_schematic2.png and b/src/lay/lay/doc/manual/inv_schematic2.png differ diff --git a/src/lay/lay/doc/manual/lvs_connect.xml b/src/lay/lay/doc/manual/lvs_connect.xml index 2fb0ff02c..5757e5336 100644 --- a/src/lay/lay/doc/manual/lvs_connect.xml +++ b/src/lay/lay/doc/manual/lvs_connect.xml @@ -183,4 +183,76 @@ connect(metal2, metal2_labels) statements.

+

Explicit connections

+ +

+ Explicit connections can be useful to enforce a connection in the layout + which is made in the schematic, but not physically on the level of the cell. + For example consider the following layout for an inverter: +

+ +

+ +

+ +

+ In the layout there are no tie-down diodes, hence there is no physical + connection to the n-well region and no physical connection to the bulk + substrate. This saves space, but these diodes need to be added by other + ways. + Usually this is done when the standard cells are combined into + macros. Filler cells will be added which include these substrate and + well contacts. +

+ +

+ On the inverter level however, there is no such connection. Therefore + the inverter has separate bulk and n-well pins. The schematic sometimes + is a simplified version which does not offer these pins. Hence there is + an intrinsic mismatch between layout and schematic. +

+ +

+ +

+ +

+ To align layout and schematics, bulk and VSS pins can be connected + explicitly. Same for n-well and VDD. + There is a certain risk to forget making these connections later. + But this risk can be mitigated by implementing DRC rules which + demand at least one tie-down diode for each isolated n-well island + or the bulk. +

+ +

+ To establish an explicit connection, make sure that n-well and + bulk have proper names. For the n-well this can be done by creating + labels on the n-well islands giving them a proper name - e.g. "NWELL". + The bulk isn't a real layout layer with polygons on it. Using "connect_global" + will both connect everthing on this layer and give it a name. +

+ +

+ The following code will connect the bulk net with "VSS" inside the cell "INV": +

+ +
connect_global(bulk, "BULK")
+...
+connect_explicit("INV", [ "BULK", "VSS" ])
+
+ +

+ The cell name can be a pattern. For example "INV*" will apply this rule on all + cells starting with "INV". + The cell is not mandatory: if it is omitted, the rule is applied to top level only + to avoid introducing rules in subcells where they would mask layout errors. +

+ +

+ An explicit connection will also imply implicit connections on the nets + listed in the net names. So in the example above, different pieces of "VSS" + are connected even if they are not physically connected. +

+ diff --git a/src/lay/lay/doc/manual/metal_connections.png b/src/lay/lay/doc/manual/metal_connections.png index a5e184dfd..707ed7677 100644 Binary files a/src/lay/lay/doc/manual/metal_connections.png and b/src/lay/lay/doc/manual/metal_connections.png differ diff --git a/src/lay/lay/doc/manual/mos3_schematic.png b/src/lay/lay/doc/manual/mos3_schematic.png index 7070986cc..e27e5935e 100644 Binary files a/src/lay/lay/doc/manual/mos3_schematic.png and b/src/lay/lay/doc/manual/mos3_schematic.png differ diff --git a/src/lay/lay/doc/manual/mos4_schematic.png b/src/lay/lay/doc/manual/mos4_schematic.png index bdd40b412..29ec88ef6 100644 Binary files a/src/lay/lay/doc/manual/mos4_schematic.png and b/src/lay/lay/doc/manual/mos4_schematic.png differ diff --git a/src/lay/lay/doc/manual/net_graph.png b/src/lay/lay/doc/manual/net_graph.png index fc581b922..0454d82ea 100644 Binary files a/src/lay/lay/doc/manual/net_graph.png and b/src/lay/lay/doc/manual/net_graph.png differ diff --git a/src/lay/lay/doc/manual/res_schematic.png b/src/lay/lay/doc/manual/res_schematic.png index 9f6dd0c59..76505460e 100644 Binary files a/src/lay/lay/doc/manual/res_schematic.png and b/src/lay/lay/doc/manual/res_schematic.png differ diff --git a/src/lay/lay/doc/manual/res_with_bulk_schematic.png b/src/lay/lay/doc/manual/res_with_bulk_schematic.png index 26859d3fd..2e5d71604 100644 Binary files a/src/lay/lay/doc/manual/res_with_bulk_schematic.png and b/src/lay/lay/doc/manual/res_with_bulk_schematic.png differ diff --git a/src/lay/lay/doc/manual/substrate_connections.png b/src/lay/lay/doc/manual/substrate_connections.png new file mode 100644 index 000000000..a73abfbd4 Binary files /dev/null and b/src/lay/lay/doc/manual/substrate_connections.png differ diff --git a/src/lay/lay/doc/manual/symm_nodes.png b/src/lay/lay/doc/manual/symm_nodes.png new file mode 100644 index 000000000..74581bc9a Binary files /dev/null and b/src/lay/lay/doc/manual/symm_nodes.png differ diff --git a/src/lay/lay/layHelpResources.qrc b/src/lay/lay/layHelpResources.qrc index 12e304bcb..52e9fc2f7 100644 --- a/src/lay/lay/layHelpResources.qrc +++ b/src/lay/lay/layHelpResources.qrc @@ -92,6 +92,7 @@ doc/manual/lvs_symm_nodes.png doc/manual/lvs_connect.xml doc/manual/inv.png + doc/manual/inv_explicit.png doc/manual/inv_no_transistors.png doc/manual/inv_transistors.png doc/manual/inv_with_diodes.png diff --git a/src/laybasic/laybasic/layNetlistBrowserModel.cc b/src/laybasic/laybasic/layNetlistBrowserModel.cc index 697bb1c5e..b160ac73a 100644 --- a/src/laybasic/laybasic/layNetlistBrowserModel.cc +++ b/src/laybasic/laybasic/layNetlistBrowserModel.cc @@ -513,40 +513,90 @@ static std::pairterminal_def () : 0, termrefs.second ? termrefs.second->terminal_def () : 0); } -static std::pair terminal_defs_from_device_classes (IndexedNetlistModel *model, const std::pair &device_classes, const std::pair &devices, size_t terminal_id) +static std::vector > terminal_defs_from_device_classes (IndexedNetlistModel *model, const std::pair &device_classes, const std::pair &devices) { - const db::DeviceTerminalDefinition *td1 = 0, *td2 = 0; - if (device_classes.first && device_classes.first->terminal_definitions ().size () > terminal_id) { - td1 = &device_classes.first->terminal_definitions () [terminal_id]; + std::vector > result; + + std::map >, std::vector > > > nets; + + size_t n1 = 0; + if (device_classes.first) { + n1 = device_classes.first->terminal_definitions ().size (); } + size_t n2 = 0; if (device_classes.second) { + n2 = device_classes.second->terminal_definitions ().size (); + } - size_t second_terminal_id = terminal_id; + for (size_t i = 0; i < n1 || i < n2; ++i) { - // Because of terminal swapping we need to look up the second terminal by looking for equivalent terminals which carry the corresponding net - if (td1 && devices.first && devices.second) { + if (i < n2) { + const db::DeviceTerminalDefinition &td = device_classes.second->terminal_definitions () [i]; + size_t id = td.id (); + size_t id_norm = device_classes.second->normalize_terminal_id (id); + nets [id_norm].second.push_back (std::make_pair (&td, devices.second->net_for_terminal (id))); + } - size_t td1_id_norm = device_classes.first->normalize_terminal_id (td1->id ()); - const db::Net *n2 = model->second_net_for (devices.first->net_for_terminal (terminal_id)); + if (i < n1) { + const db::DeviceTerminalDefinition &td = device_classes.first->terminal_definitions () [i]; + size_t id = td.id (); + size_t id_norm = device_classes.first->normalize_terminal_id (id); + nets [id_norm].first.push_back (std::make_pair (&td, devices.first->net_for_terminal (id))); + } - for (size_t i = 0; i < device_classes.second->terminal_definitions ().size (); ++i) { - const db::DeviceTerminalDefinition &td = device_classes.second->terminal_definitions () [i]; - if (device_classes.second->normalize_terminal_id (td.id ()) == td1_id_norm) { - if (devices.second->net_for_terminal (i) == n2) { - second_terminal_id = i; - break; + } + + for (std::map >, std::vector > > >::iterator n = nets.begin (); n != nets.end (); ++n) { + + std::vector > &nn1 = n->second.first; + std::vector > &nn2 = n->second.second; + + if (nn2.empty ()) { + + for (std::vector >::const_iterator i = nn1.begin (); i != nn1.end (); ++i) { + result.push_back (std::make_pair (i->first, (const db::DeviceTerminalDefinition *) 0)); + } + + } else if (nn1.empty ()) { + + for (std::vector >::const_iterator j = nn2.begin (); j != nn2.end (); ++j) { + result.push_back (std::make_pair ((const db::DeviceTerminalDefinition *) 0, j->first)); + } + + } else { + + std::vector >::iterator w = nn1.begin (); + for (std::vector >::const_iterator i = nn1.begin (); i != nn1.end (); ++i) { + + bool found = false; + + for (std::vector >::iterator j = nn2.begin (); j != nn2.end () && ! found; ++j) { + const db::Net *n2 = model->second_net_for (i->second); + if (n2 == j->second) { + result.push_back (std::make_pair (i->first, j->first)); + nn2.erase (j); + found = true; } } + + if (! found) { + *w++ = *i; + } + + } + + nn1.erase (w, nn1.end ()); + + for (size_t i = 0; i < nn1.size () && i < nn2.size (); ++i) { + result.push_back (std::make_pair (nn1 [i].first, nn2 [i].first)); } } - td2 = &device_classes.second->terminal_definitions () [second_terminal_id]; - } - return std::make_pair (td1, td2); + return result; } static @@ -585,24 +635,6 @@ static std::string combine_search_strings (const std::string &s1, const std::str } } -static size_t rows_for (const db::Device *device) -{ - if (! device || ! device->device_class ()) { - return 0; - } else { - return device->device_class ()->terminal_definitions ().size (); - } -} - -static size_t rows_for (const db::NetTerminalRef *ref) -{ - if (! ref || ! ref->device_class ()) { - return 0; - } else { - return ref->device_class ()->terminal_definitions ().size (); - } -} - static QIcon icon_for_net () { static QIcon icon; @@ -1965,12 +1997,14 @@ CircuitNetDeviceTerminalItemData::do_ensure_children (NetlistBrowserModel *model return; } - size_t n = std::max (rows_for (m_tp.first), rows_for (m_tp.second)); - for (size_t i = 0; i < n; ++i) { - std::pair device_classes = device_classes_from_devices (dp ()); - std::pair termdefs = terminal_defs_from_device_classes (model->indexer (), device_classes, dp (), i); - IndexedNetlistModel::net_pair nets = nets_from_device_terminals (dp (), termdefs); - push_back (new CircuitNetDeviceTerminalOthersItemData (this, nets, termdefs)); + std::pair devices = dp (); + std::pair device_classes = device_classes_from_devices (devices); + + std::vector > tp = terminal_defs_from_device_classes (model->indexer (), device_classes, devices); + + for (std::vector >::const_iterator i = tp.begin (); i != tp.end (); ++i) { + IndexedNetlistModel::net_pair nets = nets_from_device_terminals (dp (), *i); + push_back (new CircuitNetDeviceTerminalOthersItemData (this, nets, *i)); } } @@ -2320,10 +2354,13 @@ CircuitDeviceItemData::CircuitDeviceItemData (NetlistModelItemData *parent, cons void CircuitDeviceItemData::do_ensure_children (NetlistBrowserModel *model) { - size_t n = std::max (rows_for (dp ().first), rows_for (dp ().second)); - for (size_t i = 0; i < n; ++i) { - std::pair tp = terminal_defs_from_device_classes (model->indexer (), device_classes_from_devices (dp ()), dp (), i); - push_back (new CircuitDeviceTerminalItemData (this, tp)); + std::pair devices = dp (); + std::pair device_classes = device_classes_from_devices (devices); + + std::vector > tp = terminal_defs_from_device_classes (model->indexer (), device_classes, devices); + + for (std::vector >::const_iterator i = tp.begin (); i != tp.end (); ++i) { + push_back (new CircuitDeviceTerminalItemData (this, *i)); } } diff --git a/src/laybasic/unit_tests/layNetlistBrowserModelTests.cc b/src/laybasic/unit_tests/layNetlistBrowserModelTests.cc index 83a7d059c..d57531088 100644 --- a/src/laybasic/unit_tests/layNetlistBrowserModelTests.cc +++ b/src/laybasic/unit_tests/layNetlistBrowserModelTests.cc @@ -330,10 +330,10 @@ TEST (2) EXPECT_EQ (model->rowCount (inv2Net0TerminalIndex), 4); EXPECT_EQ (model->parent (inv2Net0TerminalIndex) == inv2Net0Index, true); // .. whose second terminal is gate - EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::UserRole).toString ()), "G|G|IN|2"); - EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "G"); - EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "IN (3)"); - EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "2 (3)"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::UserRole).toString ()), "D|D|VDD|5"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "D"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "VDD (2)"); + EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "5 (2)"); // The Pin EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0Index), Qt::UserRole).toString ()), ""); diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index 2f4219f69..ca20ae82f 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -297,8 +297,10 @@ module LVS # Use this method to provide hints for the comparer in cases which are difficult to # resolve otherwise. # - # Before this method can be used, a schematic netlist needs to be loaded with - # \schematic. + # circuit_a and net_a are for the layout netlist, circuit_b and net_b for the schematic netlist. + # Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists. + # + # Use this method andwhere in the script before the \compare call. def same_nets(*args) @@ -373,12 +375,14 @@ module LVS # This method will force an equivalence between the two circuits. # By default, circuits are identified by name. If names are different, this # method allows establishing an explicit correspondence. + # + # circuit_a is for the layout netlist, circuit_b for the schematic netlist. + # Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists. # # One of the circuits may be nil. In this case, the corresponding # other circuit is mapped to "nothing", i.e. ignored. # - # Before this method can be used, a schematic netlist needs to be loaded with - # \schematic. + # Use this method andwhere in the script before the \compare call. def same_circuits(a, b) @@ -413,6 +417,9 @@ module LVS # Before this method can be used, a schematic netlist needs to be loaded with # \schematic. # + # class_a is for the layout netlist, class_b for the schematic netlist. + # Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists. + # # One of the device classes may be "nil". In this case, the corresponding # other device class is mapped to "nothing", i.e. ignored. # @@ -429,7 +436,11 @@ module LVS # # Once a device class is mentioned with "same_device_classes", matching by # name is disabled for this class. So after using 'same_device_classes("A", "B")' - # "A" is no longer equivalent to "A" on the other side. + # "A" is no longer equivalent to "A" on the other side. If you want "A" to + # stay equivalent to "A" too, you need to use 'same_device_classes("A", "A")' + # in addition. + # + # Use this method andwhere in the script before the \compare call. def same_device_classes(a, b) @@ -468,13 +479,15 @@ module LVS # @/code # # The circuit argument is either a circuit name (a string) or a Circuit object - # from the schematic netlist. + # from the schematic netlist. + # + # Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists. # # The pin arguments are zero-based pin numbers, where 0 is the first number, 1 the second etc. - # If the netlist provides named pins, names can be used instead of numbers. + # If the netlist provides named pins, names can be used instead of numbers. Again, use upper + # case pin names for SPICE netlists. # - # Before this method can be used, a schematic netlist needs to be loaded with - # \schematic. + # Use this method andwhere in the script before the \compare call. def equivalent_pins(circuit, *pins) @@ -603,6 +616,12 @@ module LVS # pursues this "deduction path" in greater depth while with # smaller values, the algorithm prefers picking nets in a random fashion # as the seeds for this deduction path. The default value is 8. + # + # By default, the depth is unlimited, but it may + # be reduced in order to limit the compare runtimes at the cost + # of a less elaborate compare attempt. The preferred solution + # however is to use labels for net name hints which also reduces + # the branch complexity. def max_depth(value) v = value.to_i @@ -625,6 +644,12 @@ module LVS # can be increased at the expense of potentially larger runtimes. # The runtime penality is roughly proportional to the branch # complexity. + # + # By default, the branch complexity is unlimited, but it may + # be reduced in order to limit the compare runtimes at the cost + # of a less elaborate compare attempt. The preferred solution + # however is to use labels for net name hints which also reduces + # the depth. def max_branch_complexity(value) v = value.to_i diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index f9e07d3a2..66cdcb4a7 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -28,7 +28,7 @@ #include "lymMacro.h" #include "tlFileUtils.h" -void run_test (tl::TestBase *_this, const std::string &suffix, const std::string &layout, bool with_l2n = false, const std::string &top = std::string ()) +void run_test (tl::TestBase *_this, const std::string &suffix, const std::string &layout, bool with_l2n = false, const std::string &top = std::string (), bool change_case = false) { std::string rs = tl::testsrc (); rs += "/testdata/lvs/" + suffix + ".lvs"; @@ -58,7 +58,8 @@ void run_test (tl::TestBase *_this, const std::string &suffix, const std::string "$lvs_test_target_cir = '%s'\n" "$lvs_test_target_l2n = '%s'\n" "$lvs_test_top = '%s'\n" - , src, output_lvsdb, output_cir, output_l2n, top) + "$change_case = %s\n" + , src, output_lvsdb, output_cir, output_l2n, top, change_case ? "true" : "false") ); config.set_interpreter (lym::Macro::Ruby); EXPECT_EQ (config.run (), 0); @@ -108,11 +109,15 @@ TEST(5_simple_same_device_classes) TEST(6_simple_pin_swapping) { run_test (_this, "ringo_simple_pin_swapping", "ringo.gds"); + // change case + run_test (_this, "ringo_simple_pin_swapping", "ringo.gds", false, std::string (), true); } TEST(7_net_and_circuit_equivalence) { run_test (_this, "ringo_simple_net_and_circuit_equivalence", "ringo_renamed.gds"); + // change case + run_test (_this, "ringo_simple_net_and_circuit_equivalence", "ringo_renamed.gds", false, std::string (), true); } TEST(8_simplification) @@ -143,6 +148,8 @@ TEST(12_simple_dmos) TEST(13_simple_ringo_device_subcircuits) { run_test (_this, "ringo_device_subcircuits", "ringo.gds"); + // change case + run_test (_this, "ringo_device_subcircuits", "ringo.gds", false, std::string (), true); } TEST(14_simple_ringo_mixed_hierarchy) diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index b4aa56f4e..f4938f530 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -154,3 +154,16 @@ TEST(17_private) test_is_long_runner (); run_test (_this, "test_17.lylvs", "test_17.cir.gz", "test_17.gds.gz", true, "test_17.lvsdb"); } + +TEST(18_private) +{ + // test_is_long_runner (); + run_test (_this, "test_18.lvs", "test_18.cir.gz", "test_18.gds.gz", true); +} + +TEST(19_private) +{ + // test_is_long_runner (); + run_test (_this, "test_19.lvs", "test_19.cir.gz", "test_19.gds.gz", true); +} + diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index 8c1421bf7..d701b3774 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -1704,7 +1704,12 @@ NetTracerDialog::trace_all_nets (db::LayoutToNetlist *l2ndb, const lay::CellView tracer_data.configure_l2n (*l2ndb); - l2ndb->extract_netlist (std::string (), flat /*include floating subcircuits for netlist to flatten*/); + l2ndb->clear_join_nets (); + l2ndb->clear_join_net_names (); + + // include floating subcircuits for netlist to flatten + l2ndb->set_include_floating_subcircuits (flat); + l2ndb->extract_netlist (); if (flat) { l2ndb->netlist ()->flatten (); diff --git a/src/tl/tl/tlGlobPattern.h b/src/tl/tl/tlGlobPattern.h index 6e9dd22b4..308390c2b 100644 --- a/src/tl/tl/tlGlobPattern.h +++ b/src/tl/tl/tlGlobPattern.h @@ -68,6 +68,30 @@ public: */ GlobPattern &operator= (const std::string &s); + /** + * @brief Equality + */ + bool operator== (const tl::GlobPattern &other) const + { + return m_p == other.m_p; + } + + /** + * @brief Less + */ + bool operator< (const tl::GlobPattern &other) const + { + return m_p < other.m_p; + } + + /** + * @brief Pattern is empty + */ + bool empty () const + { + return m_p.empty (); + } + /** * @brief Sets a value indicating whether to treat the match case sensitive */ diff --git a/testdata/algo/device_extract_au14_circuits.gds b/testdata/algo/device_extract_au14_circuits.gds new file mode 100644 index 000000000..7cf932fc8 Binary files /dev/null and b/testdata/algo/device_extract_au14_circuits.gds differ diff --git a/testdata/algo/device_extract_au1_join_nets.gds b/testdata/algo/device_extract_au1_join_nets.gds new file mode 100644 index 000000000..0de18dc61 Binary files /dev/null and b/testdata/algo/device_extract_au1_join_nets.gds differ diff --git a/testdata/algo/device_extract_l14.gds b/testdata/algo/device_extract_l14.gds new file mode 100644 index 000000000..740a561b4 Binary files /dev/null and b/testdata/algo/device_extract_l14.gds differ diff --git a/testdata/algo/device_extract_l1_join_nets.gds b/testdata/algo/device_extract_l1_join_nets.gds new file mode 100644 index 000000000..72ddfa900 Binary files /dev/null and b/testdata/algo/device_extract_l1_join_nets.gds differ diff --git a/testdata/drc/drcSimpleTests_au4.gds b/testdata/drc/drcSimpleTests_au4.gds index 843f2b33c..6920b9a33 100644 Binary files a/testdata/drc/drcSimpleTests_au4.gds and b/testdata/drc/drcSimpleTests_au4.gds differ diff --git a/testdata/lvs/ringo_device_subcircuits.lvs b/testdata/lvs/ringo_device_subcircuits.lvs index 83b63cec8..f12f62d8d 100644 --- a/testdata/lvs/ringo_device_subcircuits.lvs +++ b/testdata/lvs/ringo_device_subcircuits.lvs @@ -116,7 +116,7 @@ connect_global(bulk, "SUBSTRATE") connect_global(ptie, "SUBSTRATE") # Test same_device_classes -same_device_classes("PMOS", "XPMOS") +same_device_classes("PMOS", $change_case ? "xpMos" : "XPMOS") # Compare section diff --git a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs index 7ff601882..02724522a 100644 --- a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs +++ b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs @@ -8,7 +8,7 @@ target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") schematic("ringo.cir") # preempt configuration (see below) -same_nets("top", "ENABLE", "RINGO", "ENABLE") +same_nets("top", "ENABLE", $change_case ? "Ringo" : "RINGO", $change_case ? "enable" : "ENABLE") deep @@ -71,8 +71,8 @@ connect_global(ptie, "SUBSTRATE") # Compare section -same_circuits("top", "RINGO") -same_circuits("INV", "INVX1") +same_circuits("top", $change_case ? "ringo" : "RINGO") +same_circuits("INV", $change_case ? "invX1" : "INVX1") same_circuits("DOESNOTEXIST", "DOESNOTEXIST2") same_nets("DOESNOTEXIST", "ENABLE", "DOESNOTEXIST2", "ENABLE") diff --git a/testdata/lvs/ringo_simple_pin_swapping.lvs b/testdata/lvs/ringo_simple_pin_swapping.lvs index d54d3f1fa..166fa320f 100644 --- a/testdata/lvs/ringo_simple_pin_swapping.lvs +++ b/testdata/lvs/ringo_simple_pin_swapping.lvs @@ -8,7 +8,7 @@ target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") schematic("ringo_pin_swapping.cir") # preempt configuration -equivalent_pins("ND2X1", 4, 5) +equivalent_pins($change_case ? "nd2X1" : "ND2X1", 4, 5) deep diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index 021dbb358..5a27c58fa 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -179,12 +179,11 @@ class DBEdgePairs_TestClass < TestBase assert_equal(r.is_deep?, true) r.flatten - assert_equal(r.has_valid_edge_pairs?, true) - assert_equal(r[1].to_s, "(0,101;2,103)/(10,111;12,113)") - assert_equal(r[100].inspect, "nil") + assert_equal(r.has_valid_edge_pairs?, false) + assert_equal(r.to_s, "(0,1;2,3)/(10,11;12,13);(0,101;2,103)/(10,111;12,113);(200,101;202,103)/(210,111;212,113)") assert_equal(r.bbox.to_s, "(0,1;212,113)") - assert_equal(r.is_deep?, false) + assert_equal(r.is_deep?, true) end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 4c7f29c17..ffc0e0d36 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -81,7 +81,7 @@ class DBEdges_TestClass < TestBase assert_equal(r.count, 4) assert_equal(r.hier_count, 4) assert_equal(r.bbox.to_s, "(10,20;100,200)") - assert_equal(r.is_merged?, true) + assert_equal(r.is_merged?, false) assert_equal(r.moved(RBA::Point::new(10, 20)).bbox.to_s, "(20,40;110,220)") assert_equal(r.moved(10, 20).bbox.to_s, "(20,40;110,220)") @@ -615,12 +615,11 @@ class DBEdges_TestClass < TestBase r.flatten - assert_equal(r.is_deep?, false) + assert_equal(r.is_deep?, true) assert_equal(r.count, 12) assert_equal(r.hier_count, 12) - assert_equal(r[1].to_s, "(-10,20;10,20)") - assert_equal(r[100].to_s, "") + assert_equal(r.to_s, "(-10,-20;-10,20);(-10,20;10,20);(10,20;10,-20);(10,-20;-10,-20);(-10,80;-10,120);(-10,120;10,120);(10,120;10,80);(10,80;-10,80);(190,80;190,120);(190,120;210,120)...") end diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index ab41460aa..7b179a732 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -99,6 +99,46 @@ class DBLayoutToNetlist_TestClass < TestBase end assert_equal(map.select { |i| i }.join(","), "RINGO=>TOP,INV2=>INV2") + # extended attributes for extract_netlist + + l2n = RBA::LayoutToNetlist::new + l2n.include_floating_subcircuits = true + assert_equal(l2n.include_floating_subcircuits, true) + l2n.include_floating_subcircuits = false + assert_equal(l2n.include_floating_subcircuits, false) + + assert_equal(l2n.dump_joined_nets, "") + l2n.join_nets([ "VDD", "NWELL" ]) + l2n.join_nets([ "VSS", "BULK" ]) + assert_equal(l2n.dump_joined_nets, "NWELL+VDD,BULK+VSS") + l2n.clear_join_nets + assert_equal(l2n.dump_joined_nets, "") + assert_equal(l2n.dump_joined_nets_per_cell, "") + + l2n.join_nets("INV*", [ "VDD", "NWELL" ]) + l2n.join_nets("ND2*", [ "VSS", "BULK" ]) + assert_equal(l2n.dump_joined_nets_per_cell, "INV*:NWELL+VDD,ND2*:BULK+VSS") + + l2n.clear_join_nets + assert_equal(l2n.dump_joined_nets, "") + assert_equal(l2n.dump_joined_nets_per_cell, "") + + assert_equal(l2n.dump_joined_net_names, "") + l2n.join_net_names("VDD") + l2n.join_net_names("VSS") + assert_equal(l2n.dump_joined_net_names, "VDD,VSS") + l2n.clear_join_net_names + assert_equal(l2n.dump_joined_net_names, "") + assert_equal(l2n.dump_joined_net_names_per_cell, "") + + l2n.join_net_names("INV*", "VDD") + l2n.join_net_names("ND2*", "VSS") + assert_equal(l2n.dump_joined_net_names_per_cell, "INV*:VDD,ND2*:VSS") + + l2n.clear_join_net_names + assert_equal(l2n.dump_joined_net_names, "") + assert_equal(l2n.dump_joined_net_names_per_cell, "") + end def test_2_ShapesFromNet diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 79985b676..144d5cffd 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -95,7 +95,7 @@ class DBRegion_TestClass < TestBase assert_equal(r.count, 1) assert_equal(r.hier_count, 1) assert_equal(r.bbox.to_s, "(10,20;100,200)") - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) r = RBA::Region::new(RBA::SimplePolygon::new(RBA::Box::new(10, 20, 100, 200))) assert_equal(r.to_s, "(10,20;10,200;100,200;100,20)") @@ -107,7 +107,7 @@ class DBRegion_TestClass < TestBase assert_equal(r.area, 90*180) assert_equal(r.perimeter, 2*90+2*180) assert_equal(r.perimeter(RBA::Box::new(0, 0, 50, 50)), 30+40) - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) r = RBA::Region::new(RBA::Path::new([ RBA::Point::new(0, 0), RBA::Point::new(100, 0) ], 20)) assert_equal(r.to_s, "(0,-10;0,10;100,10;100,-10)") @@ -116,7 +116,7 @@ class DBRegion_TestClass < TestBase assert_equal(r.count, 1) assert_equal(r.hier_count, 1) assert_equal(r.bbox.to_s, "(0,-10;100,10)") - assert_equal(r.is_merged?, false) + assert_equal(r.is_merged?, true) r = RBA::Region::new( [ RBA::Polygon::new(RBA::Box::new(10, 20, 100, 200)), @@ -1002,7 +1002,7 @@ class DBRegion_TestClass < TestBase assert_equal(RBA::Region::new(target.cell("TRANS").shapes(target_li)).to_s, "") r.flatten - assert_equal(r.is_deep?, false) + assert_equal(r.is_deep?, true) assert_equal(r.area, 53120000) # force destroy, so the unit tests pass on the next iteration diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index bff5640d2..50d7fb8e2 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -186,12 +186,10 @@ class DBTexts_TestClass < TestBase assert_equal(r.is_deep?, true) r.flatten - assert_equal(r.has_valid_texts?, true) - assert_equal(r[1].to_s, "('abc',r0 100,-100)") - assert_equal(r[100].inspect, "nil") + assert_equal(r.to_s, "('abc',r0 100,-200);('abc',r0 100,-100);('abc',r0 300,-100)") assert_equal(r.bbox.to_s, "(100,-200;300,-100)") - assert_equal(r.is_deep?, false) + assert_equal(r.is_deep?, true) end