Merge pull request #760 from KLayout/lvs-enhancements

Lvs enhancements
This commit is contained in:
Matthias Köfferlein 2021-04-01 00:12:41 +02:00 committed by GitHub
commit a9b414a2f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 3115 additions and 703 deletions

View File

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

View File

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

View File

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

View File

@ -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 <class Trans>
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);

View File

@ -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<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;

View File

@ -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 <class Trans>
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
{

View File

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

View File

@ -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 <class Trans>
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
{

View File

@ -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<db::Polygon, db::unstable_layer_tag> 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;

View File

@ -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 &region, 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) {

View File

@ -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 <class Trans>
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);

View File

@ -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<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;

View File

@ -93,20 +93,20 @@ EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, con
template <class Sh>
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 <class T>
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 <class T>
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<FlatEdgePairs *> (mp_delegate);
MutableEdgePairs *edge_pairs = dynamic_cast<MutableEdgePairs *> (mp_delegate);
if (! edge_pairs) {
edge_pairs = new FlatEdgePairs ();
if (mp_delegate) {

View File

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

View File

@ -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 <class T>
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 <class Sh>
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 <class T>
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<FlatEdges *> (mp_delegate);
MutableEdges *edges = dynamic_cast<MutableEdges *> (mp_delegate);
if (! edges) {
edges = new FlatEdges ();
if (mp_delegate) {
edges->EdgesDelegate::operator= (*mp_delegate);

View File

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

View File

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

View File

@ -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<db::EdgePair> FlatEdgePairsIterator;
* @brief The delegate for the actual edge pair set implementation
*/
class DB_PUBLIC FlatEdgePairs
: public AsIfFlatEdgePairs
: public MutableEdgePairs
{
public:
typedef db::layer<db::EdgePair, db::unstable_layer_tag> 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 <class T>
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 <class Iter>
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 <class Trans>
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<db::EdgePair, db::unstable_layer_tag> ().begin (); p != ep.template get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); ++p) {
ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().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<db::Shapes> mp_edge_pairs;
template <class Trans>
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<db::EdgePair, db::unstable_layer_tag> ().begin (); p != ep.template get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); ++p) {
ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
}
invalidate_cache ();
}
}
};
}

View File

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

View File

@ -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<db::Edge> 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 <class T>
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 <class Iter>
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 <class Iter>
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 <class Trans>
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<db::Edge, db::unstable_layer_tag> ().begin (); p != e.get_layer<db::Edge, db::unstable_layer_tag> ().end (); ++p) {
e.get_layer<db::Edge, db::unstable_layer_tag> ().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 <class Trans>
void transform_generic (const Trans &trans)
{
if (! trans.is_unity ()) {
db::Shapes &e = *mp_edges;
for (edge_iterator_type p = e.template get_layer<db::Edge, db::unstable_layer_tag> ().begin (); p != e.get_layer<db::Edge, db::unstable_layer_tag> ().end (); ++p) {
e.get_layer<db::Edge, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
}
invalidate_cache ();
}
}
};
}

View File

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

View File

@ -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<db::Polygon> 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 <class T>
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 <class Iter>
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 <class Iter>
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 <class Trans>
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<db::Polygon, db::unstable_layer_tag> ().begin (); p != polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end (); ++p) {
polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().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 <class Trans>
void transform_generic (const Trans &trans)
{
if (! trans.is_unity ()) {
db::Shapes &polygons = *mp_polygons;
for (polygon_iterator_type p = polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().begin (); p != polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end (); ++p) {
polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
}
invalidate_cache ();
}
}
};
}

View File

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

View File

@ -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<db::Text> 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 <class T>
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 <class Iter>
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 <class Trans>
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<db::Text, db::unstable_layer_tag> ().begin (); p != texts.template get_layer<db::Text, db::unstable_layer_tag> ().end (); ++p) {
texts.get_layer<db::Text, db::unstable_layer_tag> ().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<db::Shapes> mp_texts;
template <class Trans>
void transform_generic (const Trans &trans)
{
if (! trans.is_unity ()) {
db::Shapes &texts = *mp_texts;
for (text_iterator_type p = texts.template get_layer<db::Text, db::unstable_layer_tag> ().begin (); p != texts.template get_layer<db::Text, db::unstable_layer_tag> ().end (); ++p) {
texts.get_layer<db::Text, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
}
invalidate_cache ();
}
}
};
}

View File

@ -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<db::Polygon> &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<db::Polygon> &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

View File

@ -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<unsigned int> ());
@ -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<std::string, std::string> (), include_floating_subcircuits);
m_include_floating_subcircuits = f;
}
void LayoutToNetlist::extract_netlist (const std::string &joined_net_names, const std::map<std::string, std::string> &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<std::string> &jn)
{
m_joined_nets.push_back (jn);
}
void LayoutToNetlist::join_nets (const tl::GlobPattern &cell, const std::set<std::string> &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<std::string, std::string>::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<std::string, std::list<tl::GlobPattern> > jp_per_cell;
for (std::list<std::pair<tl::GlobPattern, tl::GlobPattern> >::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<std::string, std::list<tl::GlobPattern> >::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<std::string, std::list<std::set<std::string> > > jn_per_cell;
for (std::list<std::pair<tl::GlobPattern, std::set<std::string> > >::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<std::string, std::list<std::set<std::string> > >::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;

View File

@ -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<tl::GlobPattern> &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<std::pair<tl::GlobPattern, tl::GlobPattern> > &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<std::string> &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<std::string> &gp);
/**
* @brief Gets the joined nets for top level
*
* This method is mainly provided to test purposes.
*/
const std::list<std::set<std::string> > &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<std::pair<tl::GlobPattern, std::set<std::string> > > &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<std::string, std::string> &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<tl::GlobPattern> m_joined_net_names;
std::list<std::pair<tl::GlobPattern, tl::GlobPattern> > m_joined_net_names_per_cell;
std::list<std::set<std::string> > m_joined_nets;
std::list<std::pair<tl::GlobPattern, std::set<std::string> > > m_joined_nets_per_cell;
struct CellReuseTableKey
{

View File

@ -46,7 +46,7 @@ matrix_2d<C>::to_string () const
template <class C>
std::pair<double, double>
matrix_2d<C>::mag () const
matrix_2d<C>::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 <class C>
double
matrix_2d<C>::angle () const
{
std::pair <double, double> m = mag ();
std::pair <double, double> m = mag2 ();
double u1 = m.first;
double u2 = is_mirror () ? -m.second : m.second;
double n11 = m_m11 / u1;
@ -98,7 +98,7 @@ template <class C>
bool
matrix_2d<C>::has_shear () const
{
std::pair <double, double> m = mag ();
std::pair <double, double> m = mag2 ();
double u1 = m.first;
double u2 = is_mirror () ? -m.second : m.second;
double n11 = m_m11 / u1;
@ -114,7 +114,7 @@ template <class C>
double
matrix_2d<C>::shear_angle () const
{
std::pair <double, double> m = mag ();
std::pair <double, double> m = mag2 ();
double u1 = m.first;
double u2 = is_mirror () ? -m.second : m.second;
double n11 = m_m11 / u1;

View File

@ -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<double, double> mag () const;
std::pair<double, double> 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<C> (m, 0.0, 0.0, m);
}
/**
* @brief A dummy displacement accessor (matrix2d does not have a displacement)
*/
db::vector<coord_type> disp () const
{
return db::vector<coord_type> ();
}
/**
* @brief For compatibility with other transformations
*/
coord_type ctrans (coord_type c) const
{
return db::coord_traits<coord_type>::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<double, double> mag () const
std::pair<double, double> 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<coord_type>::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;
}
/**

View File

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

View File

@ -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 <set>
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 <class T>
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 <class Iter>
void insert (const Iter &b, const Iter &e)
{
reserve (count () + (e - b));
for (Iter i = b; i != e; ++i) {
insert (*i);
}
}
template <class Iter>
void insert_seq (const Iter &seq)
{
for (Iter i = seq; ! i.at_end (); ++i) {
insert (*i);
}
}
};
}
#endif

107
src/db/db/dbMutableEdges.cc Normal file
View File

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

114
src/db/db/dbMutableEdges.h Normal file
View File

@ -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 <set>
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 <class T>
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 <class Iter>
void insert (const Iter &b, const Iter &e)
{
reserve (count () + (e - b));
for (Iter i = b; i != e; ++i) {
insert (*i);
}
}
template <class Iter>
void insert_seq (const Iter &seq)
{
for (Iter i = seq; ! i.at_end (); ++i) {
insert (*i);
}
}
};
}
#endif

View File

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

105
src/db/db/dbMutableRegion.h Normal file
View File

@ -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 <set>
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 <class T>
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 <class Iter>
void insert (const Iter &b, const Iter &e)
{
reserve (count () + (e - b));
for (Iter i = b; i != e; ++i) {
insert (*i);
}
}
template <class Iter>
void insert_seq (const Iter &seq)
{
for (Iter i = seq; ! i.at_end (); ++i) {
insert (*i);
}
}
};
}
#endif

View File

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

100
src/db/db/dbMutableTexts.h Normal file
View File

@ -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 <set>
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 <class T>
void insert (const db::Shape &shape, const T &trans)
{
if (shape.is_text ()) {
db::Text text = shape.text ();
text.transform (trans);
insert (text);
}
}
template <class Iter>
void insert (const Iter &b, const Iter &e)
{
reserve (count () + (e - b));
for (Iter i = b; i != e; ++i) {
insert (*i);
}
}
template <class Iter>
void insert_seq (const Iter &seq)
{
for (Iter i = seq; ! i.at_end (); ++i) {
insert (*i);
}
}
};
}
#endif

View File

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

View File

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

View File

@ -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<const DeviceClass *, DeviceClass *> 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-> ();
}
}

View File

@ -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<db::NetlistManipulationCallbacks> mp_callbacks;
circuit_list m_circuits;
device_class_list m_device_classes;

View File

@ -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<std::string, size_t>::const_iterator c = m_cat_by_name.find (cls_name);
if (c != m_cat_by_name.end ()) {
@ -498,6 +490,7 @@ public:
std::map<std::string, size_t> 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<size_t> m_strict_device_categories;
};
@ -606,6 +604,11 @@ public:
{
return generic_categorizer<db::Circuit>::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<const NetGraphNode *, NetGraphNode::edge_iterator> &a, const std::pair<const NetGraphNode *, NetGraphNode::edge_iterator>b) 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<std::vector<std::pair<const NetGraphNode *, NetGraphNode::edge_iterator> >::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<std::pair<const NetGraphNode *, NetGraphNode::edge_iterator> >::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<std::pair<const NetG
indent_s += "*" + tl::to_string (n_branch) + " ";
}
if (depth > data->max_depth) {
if (data->max_depth != std::numeric_limits<size_t>::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::vector<std::pair<const NetG
new_nodes += n;
} else if (nr->num * n_branch > data->max_n_branch) {
} else if (data->max_n_branch != std::numeric_limits<size_t>::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<size_t>::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<size_t, std::pair<std::vector<db::Circuit *>, std::vector<db::Circuit *> > > 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<size_t, std::pair<std::vector<const db::Circuit *>, std::vector<const db::Circuit *> > > 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-> ();
}
}

View File

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

View File

@ -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<tl::GlobPattern> &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<tl::GlobPattern> &jnn)
{
m_joined_net_names_per_cell.push_back (std::make_pair (cellname, jnn));
}
void NetlistExtractor::set_joined_nets (const std::list<std::set<std::string> > &jnn)
{
m_joined_nets = jnn;
}
void NetlistExtractor::set_joined_nets (const std::string &cell_name, const std::list<std::set<std::string> > &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<size_t> &eq)
build_net_name_equivalence (const db::Layout *layout, const db::Connectivity &conn, db::property_names_id_type net_name_id, const std::list<tl::GlobPattern> &jn_pattern, tl::equivalence_clusters<size_t> &eq)
{
std::map<std::string, std::set<size_t> > 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<tl::GlobPattern>::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<tl::GlobPattern>::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<db::Text> &text_repository = layout->shape_repository ().repository (db::object_tag<db::Text> ());
for (db::repository<db::Text>::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<tl::GlobPattern>::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<std::string> &nets_to_join, tl::equivalence_clusters<size_t> &eq)
{
std::map<std::string, std::set<size_t> > 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<db::Text> &text_repository = layout->shape_repository ().repository (db::object_tag<db::Text> ());
for (db::repository<db::Text>::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<std::string, std::set<size_t> >::const_iterator pn = prop_by_name.begin (); pn != prop_by_name.end (); ++pn) {
std::set<size_t>::const_iterator p = pn->second.begin ();
std::set<size_t>::const_iterator p0 = p;
while (p != pn->second.end ()) {
eq.same (*p0, *p);
++p;
}
}
// second intra-name equivalence
for (std::map<std::string, std::set<size_t> >::const_iterator pn1 = prop_by_name.begin (); pn1 != prop_by_name.end (); ++pn1) {
std::map<std::string, std::set<size_t> >::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<db::cell_index_type, tl::equivalence_clusters<size_t> > 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<std::pair<std::string, std::string> >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) {
for (std::list<std::pair<std::string, std::list<tl::GlobPattern> > >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) {
std::pair<bool, db::cell_index_type> 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<std::set<std::string> >::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<std::pair<std::string, std::list<std::set<std::string> > > >::const_iterator m = m_joined_nets_per_cell.begin (); m != m_joined_nets_per_cell.end (); ++m) {
std::pair<bool, db::cell_index_type> cp = mp_layout->cell_by_name (m->first.c_str ());
if (cp.first) {
for (std::list<std::set<std::string> >::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

View File

@ -26,6 +26,7 @@
#include "dbCommon.h"
#include "dbHierNetworkProcessor.h"
#include "dbNetShape.h"
#include "tlGlobPattern.h"
#include <map>
#include <set>
@ -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<tl::GlobPattern> &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<tl::GlobPattern> &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<std::set<std::string> > &jnn);
/**
* @brief Sets the joined nets attribute per cell
*/
void set_joined_nets (const std::string &cell_name, const std::list<std::set<std::string> > &jnn);
/**
* @brief Extract the nets
@ -135,8 +143,10 @@ private:
std::pair<bool, db::property_names_id_type> m_text_annot_name_id;
std::pair<bool, db::property_names_id_type> m_device_annot_name_id;
std::pair<bool, db::property_names_id_type> m_terminal_annot_name_id;
std::string m_joined_net_names;
std::list<std::pair<std::string, std::string> > m_joined_net_names_per_cell;
std::list<tl::GlobPattern> m_joined_net_names;
std::list<std::pair<std::string, std::list<tl::GlobPattern> > > m_joined_net_names_per_cell;
std::list<std::set<std::string> > m_joined_nets;
std::list<std::pair<std::string, std::list<std::set<std::string> > > > m_joined_nets_per_cell;
bool m_include_floating_subcircuits;
bool instance_is_device (db::properties_id_type prop_id) const;

View File

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

View File

@ -131,13 +131,13 @@ Region::clear ()
void
Region::reserve (size_t n)
{
flat_region ()->reserve (n);
mutable_region ()->reserve (n);
}
template <class T>
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 <class Sh>
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 <class T>
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<FlatRegion *> (mp_delegate);
MutableRegion *region = dynamic_cast<MutableRegion *> (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)
{

View File

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

View File

@ -688,7 +688,8 @@ public:
text<C> &transform (const Tr &t)
{
typedef typename Tr::target_coord_type target_coord_type;
m_trans = simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ());
fixpoint_trans<coord_type> fp (t);
m_trans = simple_trans<target_coord_type> ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ());
m_size = t.ctrans (m_size);
return *this;
}
@ -708,13 +709,14 @@ public:
text<typename Tr::target_coord_type> transformed (const Tr &t) const
{
typedef typename Tr::target_coord_type target_coord_type;
fixpoint_trans<coord_type> fp (t);
size_t p = (size_t) mp_ptr;
if (p & 1) {
return text<target_coord_type> (reinterpret_cast<StringRef *> (p - 1), simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ()), t.ctrans (m_size), m_font, m_halign, m_valign);
return text<target_coord_type> (reinterpret_cast<StringRef *> (p - 1), simple_trans<target_coord_type> ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ()), t.ctrans (m_size), m_font, m_halign, m_valign);
} else if (mp_ptr) {
return text<target_coord_type> (mp_ptr, simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ()), t.ctrans (m_size), m_font, m_halign, m_valign);
return text<target_coord_type> (mp_ptr, simple_trans<target_coord_type> ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ()), t.ctrans (m_size), m_font, m_halign, m_valign);
} else {
return text<target_coord_type> (simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ()), t.ctrans (m_size), m_font, m_halign, m_valign);
return text<target_coord_type> (simple_trans<target_coord_type> ((fp * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point<target_coord_type> ()), t.ctrans (m_size), m_font, m_halign, m_valign);
}
}

View File

@ -93,25 +93,27 @@ Texts::Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I
template <class Sh>
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 <class T>
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 <class T>
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<FlatTexts *> (mp_delegate);
MutableTexts *texts = dynamic_cast<MutableTexts *> (mp_delegate);
if (! texts) {
texts = new FlatTexts ();
if (mp_delegate) {

View File

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

View File

@ -376,6 +376,32 @@ public:
// .. nothing else ..
}
/**
* @brief Reduction
*/
template <class T>
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<coord_type> &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<coord_type> &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<double, double> mag = m.mag ();
std::pair<double, double> 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<double, double> mag = m.mag ();
std::pair<double, double> 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;

View File

@ -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<tl::GlobPattern> &jn = l2n->joined_net_names ();
std::vector<std::string> s;
for (std::list<tl::GlobPattern>::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<std::pair<tl::GlobPattern, tl::GlobPattern> > &jn = l2n->joined_net_names_per_cell ();
std::vector<std::string> s;
for (std::list<std::pair<tl::GlobPattern, tl::GlobPattern> >::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<std::string> &s)
{
l2n->join_nets (s);
}
static std::string dump_joined_nets (const db::LayoutToNetlist *l2n)
{
const std::list<std::set<std::string> > &jn = l2n->joined_nets ();
std::vector<std::string> s;
for (std::list<std::set<std::string> >::const_iterator j = jn.begin (); j != jn.end (); ++j) {
std::vector<std::string> 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<std::string> &s)
{
l2n->join_nets (tl::GlobPattern (c), s);
}
static std::string dump_joined_nets_per_cell (const db::LayoutToNetlist *l2n)
{
const std::list<std::pair<tl::GlobPattern, std::set<std::string> > > &jn = l2n->joined_nets_per_cell ();
std::vector<std::string> s;
for (std::list<std::pair<tl::GlobPattern, std::set<std::string> > >::const_iterator i = jn.begin (); i != jn.end (); ++i) {
std::vector<std::string> t (i->second.begin (), i->second.end ());
s.push_back (i->first.pattern () + ":" + tl::join (t, "+"));
}
return tl::join (s, ",");
}
Class<db::LayoutToNetlist> 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<db::LayoutToNetlist> 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<db::LayoutToNetlist> 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<db::LayoutToNetlist> 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<db::LayoutToNetlist> 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<db::LayoutToNetlist> 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"

View File

@ -519,6 +519,9 @@ Class<db::NetlistComparer> 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<db::NetlistComparer> 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"
) +

View File

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

View File

@ -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<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "device_extract_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<unsigned int> ()));
std::unique_ptr<db::Region> rbulk (l2n.make_layer ("bulk"));
std::unique_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
std::unique_ptr<db::Region> rnwell_lbl (l2n.make_layer (nwell_lbl, "nwell_lbl"));
std::unique_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
std::unique_ptr<db::Region> rpplus (l2n.make_layer (pplus, "pplus"));
std::unique_ptr<db::Region> rnplus (l2n.make_layer (nplus, "nplus"));
std::unique_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
std::unique_ptr<db::Texts> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
std::unique_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
std::unique_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
std::unique_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
std::unique_ptr<db::Texts> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
std::unique_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
std::unique_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
std::unique_ptr<db::Texts> 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<std::string> 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<const db::Region *, unsigned int> 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);
}

View File

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

View File

@ -958,19 +958,38 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections)
// extract the nets
std::list<tl::GlobPattern> 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<tl::GlobPattern> 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<tl::GlobPattern> 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<tl::GlobPattern> 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<tl::GlobPattern> 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<tl::GlobPattern> 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<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "device_extract_l1_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<db::NetShape> 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<std::set<std::string> > jn;
jn.push_back (std::set<std::string> ());
jn.back ().insert ("BULK");
jn.back ().insert ("VSS");
jn.push_back (std::set<std::string> ());
jn.back ().insert ("NWELL");
jn.back ().insert ("VDD");
net_ex.set_joined_nets ("INV2", jn);
std::list<tl::GlobPattern> 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<unsigned int, unsigned int> 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);
}

View File

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

View File

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

View File

@ -312,6 +312,17 @@ l1 = input(1, 0)
<p>
See <a href="/about/drc_ref_netter.xml#connect">Netter#connect</a> for a description of that function.
</p>
<a name="connect_explicit"/><h2>"connect_explicit" - Specifies explicit net connections</h2>
<keyword name="connect_explicit"/>
<p>Usage:</p>
<ul>
<li><tt>connect_explicit(net_names)</tt></li>
<li><tt>connect_explicit(cell_pattern, net_names)</tt></li>
</ul>
<p>
See <a href="/about/drc_ref_netter.xml#connect_explicit">Netter#connect_explicit</a> for a description of that function.
Net names is an array (use square brackets to list the net names).
</p>
<a name="connect_global"/><h2>"connect_global" - Specifies a connection to a global net</h2>
<keyword name="connect_global"/>
<p>Usage:</p>

View File

@ -219,6 +219,50 @@ joins.
Connections are accumulated. The connections defined so far
can be cleared with <a href="#clear_connections">clear_connections</a>.
</p>
<a name="connect_explicit"/><h2>"connect_explicit" - Specifies a list of net names for nets to connect explicitly</h2>
<keyword name="connect_explicit"/>
<p>Usage:</p>
<ul>
<li><tt>connect_explicit(net_names)</tt></li>
<li><tt>connect_explicit(cell_pattern, net_names)</tt></li>
</ul>
<p>
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.
</p><p>
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.
</p><p>
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").
</p><p>
The following code will establish an explicit connection for all cells called "INV.." between
BULK and VSS nets:
</p><p>
<pre>
connect_global(bulk, "BULK")
...
connect_explicit("INV*", [ "BULK", "VSS" ])
</pre>
</p><p>
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.
</p><p>
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.
</p><p>
The explicit connections are applied on the next net extraction and cleared
on "clear_connections".
</p>
<a name="connect_global"/><h2>"connect_global" - Connects a layer with a global net</h2>
<keyword name="connect_global"/>
<p>Usage:</p>

View File

@ -114,13 +114,15 @@ netter.equivalent_pins("NAND2", 0, 1)
</pre>
</p><p>
The circuit argument is either a circuit name (a string) or a Circuit object
from the schematic netlist.
from the schematic netlist.
</p><p>
Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists.
</p><p>
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.
</p><p>
Before this method can be used, a schematic netlist needs to be loaded with
<a href="#schematic">schematic</a>.
Use this method andwhere in the script before the <a href="#compare">compare</a> call.
</p>
<a name="join_symmetric_nets"/><h2>"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist</h2>
<keyword name="join_symmetric_nets"/>
@ -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.
</p><p>
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.
</p>
<a name="max_depth"/><h2>"max_depth" - Configures the maximum search depth for net match deduction</h2>
<keyword name="max_depth"/>
@ -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.
</p><p>
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.
</p>
<a name="max_res"/><h2>"max_res" - Ignores resistors with a resistance above a certain value</h2>
<keyword name="max_res"/>
@ -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.
</p><p>
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.
</p><p>
One of the circuits may be nil. In this case, the corresponding
other circuit is mapped to "nothing", i.e. ignored.
</p><p>
Before this method can be used, a schematic netlist needs to be loaded with
<a href="#schematic">schematic</a>.
Use this method andwhere in the script before the <a href="#compare">compare</a> call.
</p>
<a name="same_device_classes"/><h2>"same_device_classes" - Establishes an equivalence between the device classes</h2>
<keyword name="same_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
<a href="#schematic">schematic</a>.
</p><p>
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.
</p><p>
One of the device classes may be "nil". In this case, the corresponding
other device class is mapped to "nothing", i.e. ignored.
</p><p>
@ -260,7 +279,11 @@ schematic side.
</p><p>
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.
</p><p>
Use this method andwhere in the script before the <a href="#compare">compare</a> call.
</p>
<a name="same_nets"/><h2>"same_nets" - Establishes an equivalence between the nets</h2>
<keyword name="same_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.
</p><p>
Before this method can be used, a schematic netlist needs to be loaded with
<a href="#schematic">schematic</a>.
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.
</p><p>
Use this method andwhere in the script before the <a href="#compare">compare</a> call.
</p>
<a name="schematic"/><h2>"schematic" - Gets, sets or reads the reference netlist</h2>
<keyword name="schematic"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -183,4 +183,76 @@ connect(metal2, metal2_labels)</pre>
statements.
</p>
<h2>Explicit connections</h2>
<p>
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:
</p>
<p>
<img src="/manual/inv.png"/>
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
<img src="/manual/inv_explicit.png"/>
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
The following code will connect the bulk net with "VSS" inside the cell "INV":
</p>
<pre>connect_global(bulk, "BULK")
...
connect_explicit("INV", [ "BULK", "VSS" ])
</pre>
<p>
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.
</p>
<p>
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.
</p>
</doc>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -92,6 +92,7 @@
<file alias="lvs_symm_nodes.png">doc/manual/lvs_symm_nodes.png</file>
<file alias="lvs_connect.xml">doc/manual/lvs_connect.xml</file>
<file alias="inv.png">doc/manual/inv.png</file>
<file alias="inv_explicit.png">doc/manual/inv_explicit.png</file>
<file alias="inv_no_transistors.png">doc/manual/inv_no_transistors.png</file>
<file alias="inv_transistors.png">doc/manual/inv_transistors.png</file>
<file alias="inv_with_diodes.png">doc/manual/inv_with_diodes.png</file>

View File

@ -513,40 +513,90 @@ static std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalD
return std::make_pair (termrefs.first ? termrefs.first->terminal_def () : 0, termrefs.second ? termrefs.second->terminal_def () : 0);
}
static std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> terminal_defs_from_device_classes (IndexedNetlistModel *model, const std::pair<const db::DeviceClass *, const db::DeviceClass *> &device_classes, const std::pair<const db::Device *, const db::Device *> &devices, size_t terminal_id)
static std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> > terminal_defs_from_device_classes (IndexedNetlistModel *model, const std::pair<const db::DeviceClass *, const db::DeviceClass *> &device_classes, const std::pair<const db::Device *, const db::Device *> &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<std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> > result;
std::map<size_t, std::pair<std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> >, std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> > > > 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<size_t, std::pair<std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> >, std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> > > >::iterator n = nets.begin (); n != nets.end (); ++n) {
std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> > &nn1 = n->second.first;
std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> > &nn2 = n->second.second;
if (nn2.empty ()) {
for (std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> >::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<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> >::const_iterator j = nn2.begin (); j != nn2.end (); ++j) {
result.push_back (std::make_pair ((const db::DeviceTerminalDefinition *) 0, j->first));
}
} else {
std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> >::iterator w = nn1.begin ();
for (std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> >::const_iterator i = nn1.begin (); i != nn1.end (); ++i) {
bool found = false;
for (std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::Net *> >::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<const db::DeviceClass *, const db::DeviceClass *> device_classes = device_classes_from_devices (dp ());
std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> 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<const db::Device *, const db::Device *> devices = dp ();
std::pair<const db::DeviceClass *, const db::DeviceClass *> device_classes = device_classes_from_devices (devices);
std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> > tp = terminal_defs_from_device_classes (model->indexer (), device_classes, devices);
for (std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> >::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<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> tp = terminal_defs_from_device_classes (model->indexer (), device_classes_from_devices (dp ()), dp (), i);
push_back (new CircuitDeviceTerminalItemData (this, tp));
std::pair<const db::Device *, const db::Device *> devices = dp ();
std::pair<const db::DeviceClass *, const db::DeviceClass *> device_classes = device_classes_from_devices (devices);
std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> > tp = terminal_defs_from_device_classes (model->indexer (), device_classes, devices);
for (std::vector<std::pair<const db::DeviceTerminalDefinition *, const db::DeviceTerminalDefinition *> >::const_iterator i = tp.begin (); i != tp.end (); ++i) {
push_back (new CircuitDeviceTerminalItemData (this, *i));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

BIN
testdata/algo/device_extract_l14.gds vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More