mirror of https://github.com/KLayout/klayout.git
Merge remote-tracking branch 'origin/dvb'
This commit is contained in:
commit
566746823c
|
|
@ -72,6 +72,8 @@ class DocItem
|
|||
end
|
||||
end
|
||||
|
||||
self.name || raise("Doc block without name")
|
||||
|
||||
para && @paragraphs.push(para)
|
||||
|
||||
end
|
||||
|
|
@ -247,16 +249,25 @@ collector = Collector::new
|
|||
File.open($infile, "r") do |file|
|
||||
|
||||
block = nil
|
||||
line = 0
|
||||
|
||||
file.each_line do |l|
|
||||
l = unescape(l)
|
||||
if l =~ /^\s*#\s*#{$key}/
|
||||
block = []
|
||||
elsif l =~ /^\s*#\s*(.*)\s*$/
|
||||
block && block.push($1)
|
||||
elsif l =~ /^\s*$/
|
||||
block && collector.add_block(block)
|
||||
block = nil
|
||||
|
||||
line += 1
|
||||
|
||||
begin
|
||||
l = unescape(l)
|
||||
if l =~ /^\s*#\s*#{$key}/
|
||||
block = []
|
||||
elsif l =~ /^\s*#\s*(.*)\s*$/
|
||||
block && block.push($1)
|
||||
elsif l =~ /^\s*$/
|
||||
block && collector.add_block(block)
|
||||
block = nil
|
||||
end
|
||||
rescue => ex
|
||||
puts "ERROR in line #{line}:\n" + ex.to_s
|
||||
exit 1
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ include($$PWD/../../app.pri)
|
|||
# On Mac OSX, these buddy tools have to be built as ordinary command line tools;
|
||||
# not as bundles (*.app)
|
||||
mac {
|
||||
CONFIG -= app_bundle
|
||||
CONFIG -= app_bundle
|
||||
}
|
||||
|
||||
# Since the main function is entirely unspecific, we can put it into a common
|
||||
|
|
@ -42,3 +42,8 @@ equals(HAVE_PYTHON, "1") {
|
|||
DEFINES += BD_TARGET=$$TARGET
|
||||
|
||||
LIBS += $$RUBYLIBFILE
|
||||
|
||||
if(mac|linux*) {
|
||||
LIBS += -ldl
|
||||
}
|
||||
|
||||
|
|
|
|||
103
src/db/db/db.pro
103
src/db/db/db.pro
|
|
@ -119,7 +119,60 @@ SOURCES = \
|
|||
gsiDeclDbVector.cc \
|
||||
gsiDeclDbLayoutDiff.cc \
|
||||
gsiDeclDbGlyphs.cc \
|
||||
dbConverters.cc
|
||||
dbConverters.cc \
|
||||
dbAsIfFlatRegion.cc \
|
||||
dbEmptyRegion.cc \
|
||||
dbFlatRegion.cc \
|
||||
dbOriginalLayerRegion.cc \
|
||||
dbRegionDelegate.cc \
|
||||
dbEdgesDelegate.cc \
|
||||
dbEmptyEdges.cc \
|
||||
dbAsIfFlatEdges.cc \
|
||||
dbFlatEdges.cc \
|
||||
dbEdgeBoolean.cc \
|
||||
dbOriginalLayerEdges.cc \
|
||||
dbAsIfFlatEdgePairs.cc \
|
||||
dbEmptyEdgePairs.cc \
|
||||
dbFlatEdgePairs.cc \
|
||||
dbOriginalLayerEdgePairs.cc \
|
||||
dbEdgePairsDelegate.cc \
|
||||
dbDeepShapeStore.cc \
|
||||
dbHierarchyBuilder.cc \
|
||||
dbLocalOperation.cc \
|
||||
dbHierProcessor.cc \
|
||||
dbDeepRegion.cc \
|
||||
dbHierNetworkProcessor.cc \
|
||||
dbNetlist.cc \
|
||||
gsiDeclDbNetlist.cc \
|
||||
dbNetlistDeviceClasses.cc \
|
||||
dbNetlistDeviceExtractor.cc \
|
||||
dbNetlistExtractor.cc \
|
||||
gsiDeclDbNetlistDeviceClasses.cc \
|
||||
gsiDeclDbNetlistDeviceExtractor.cc \
|
||||
gsiDeclDbHierNetworkProcessor.cc \
|
||||
dbNetlistDeviceExtractorClasses.cc \
|
||||
dbLayoutToNetlist.cc \
|
||||
gsiDeclDbLayoutToNetlist.cc \
|
||||
dbCircuit.cc \
|
||||
dbDevice.cc \
|
||||
dbDeviceClass.cc \
|
||||
dbNet.cc \
|
||||
dbSubCircuit.cc \
|
||||
dbPin.cc \
|
||||
dbLayoutToNetlistReader.cc \
|
||||
dbLayoutToNetlistWriter.cc \
|
||||
dbLayoutToNetlistFormatDefs.cc \
|
||||
dbDeviceAbstract.cc \
|
||||
dbLocalOperationUtils.cc \
|
||||
gsiDeclDbDeepShapeStore.cc \
|
||||
dbNetlistSpiceWriter.cc \
|
||||
dbNetlistWriter.cc \
|
||||
dbCellVariants.cc \
|
||||
dbDeepEdges.cc \
|
||||
dbDeepEdgePairs.cc \
|
||||
dbRegionUtils.cc \
|
||||
dbEdgesUtils.cc \
|
||||
dbRegionProcessors.cc
|
||||
|
||||
HEADERS = \
|
||||
dbArray.h \
|
||||
|
|
@ -209,6 +262,54 @@ HEADERS = \
|
|||
dbPlugin.h \
|
||||
dbInit.h \
|
||||
dbConverters.h \
|
||||
dbAsIfFlatRegion.h \
|
||||
dbEmptyRegion.h \
|
||||
dbFlatRegion.h \
|
||||
dbOriginalLayerRegion.h \
|
||||
dbRegionDelegate.h \
|
||||
dbEdgesDelegate.h \
|
||||
dbEmptyEdges.h \
|
||||
dbAsIfFlatEdges.h \
|
||||
dbFlatEdges.h \
|
||||
dbEdgeBoolean.h \
|
||||
dbOriginalLayerEdges.h \
|
||||
dbAsIfFlatEdgePairs.h \
|
||||
dbEmptyEdgePairs.h \
|
||||
dbFlatEdgePairs.h \
|
||||
dbOriginalLayerEdgePairs.h \
|
||||
dbEdgePairsDelegate.h \
|
||||
dbDeepShapeStore.h \
|
||||
dbHierarchyBuilder.h \
|
||||
dbLocalOperation.h \
|
||||
dbHierProcessor.h \
|
||||
dbNetlist.h \
|
||||
dbNetlistDeviceClasses.h \
|
||||
dbNetlistDeviceExtractor.h \
|
||||
dbNetlistExtractor.h \
|
||||
dbNetlistDeviceExtractorClasses.h \
|
||||
dbLayoutToNetlist.h \
|
||||
dbHierNetworkProcessor.h \
|
||||
dbNetlistUtils.h \
|
||||
dbNet.h \
|
||||
dbCircuit.h \
|
||||
dbDevice.h \
|
||||
dbDeviceClass.h \
|
||||
dbPin.h \
|
||||
dbSubCircuit.h \
|
||||
dbLayoutToNetlistReader.h \
|
||||
dbLayoutToNetlistWriter.h \
|
||||
dbLayoutToNetlistFormatDefs.h \
|
||||
dbDeviceAbstract.h \
|
||||
dbLocalOperationUtils.h \
|
||||
dbDeepRegion.h \
|
||||
dbNetlistSpiceWriter.h \
|
||||
dbNetlistWriter.h \
|
||||
dbCellVariants.h \
|
||||
dbDeepEdges.h \
|
||||
dbDeepEdgePairs.h \
|
||||
dbRegionUtils.h \
|
||||
dbEdgesUtils.h \
|
||||
dbRegionProcessors.h \
|
||||
gsiDeclDbHelpers.h
|
||||
|
||||
!equals(HAVE_QT, "0") {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,17 @@ struct ArrayBase
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
ArrayBase (const ArrayBase &)
|
||||
: in_repository (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
ArrayBase &operator= (const ArrayBase &)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~ArrayBase ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
|
|||
|
|
@ -0,0 +1,300 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbAsIfFlatEdgePairs.h"
|
||||
#include "dbFlatEdgePairs.h"
|
||||
#include "dbFlatRegion.h"
|
||||
#include "dbFlatEdges.h"
|
||||
#include "dbEmptyEdgePairs.h"
|
||||
#include "dbEdgePairs.h"
|
||||
#include "dbBoxConvert.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// AsIfFlagEdgePairs implementation
|
||||
|
||||
AsIfFlatEdgePairs::AsIfFlatEdgePairs ()
|
||||
: EdgePairsDelegate (), m_bbox_valid (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AsIfFlatEdgePairs::~AsIfFlatEdgePairs ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
std::string
|
||||
AsIfFlatEdgePairs::to_string (size_t nmax) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
EdgePairsIterator p (begin ());
|
||||
bool first = true;
|
||||
for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) {
|
||||
if (! first) {
|
||||
os << ";";
|
||||
}
|
||||
first = false;
|
||||
os << p->to_string ();
|
||||
}
|
||||
if (! p.at_end ()) {
|
||||
os << "...";
|
||||
}
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
AsIfFlatEdgePairs::in (const EdgePairs &other, bool invert) const
|
||||
{
|
||||
std::set <db::EdgePair> op;
|
||||
for (EdgePairsIterator o (other.begin ()); ! o.at_end (); ++o) {
|
||||
op.insert (*o);
|
||||
}
|
||||
|
||||
std::auto_ptr<FlatEdgePairs> new_edge_pairs (new FlatEdgePairs (false));
|
||||
|
||||
for (EdgePairsIterator o (begin ()); ! o.at_end (); ++o) {
|
||||
if ((op.find (*o) == op.end ()) == invert) {
|
||||
new_edge_pairs->insert (*o);
|
||||
}
|
||||
}
|
||||
|
||||
return new_edge_pairs.release ();
|
||||
}
|
||||
|
||||
size_t
|
||||
AsIfFlatEdgePairs::size () const
|
||||
{
|
||||
size_t n = 0;
|
||||
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
Box AsIfFlatEdgePairs::bbox () const
|
||||
{
|
||||
if (! m_bbox_valid) {
|
||||
m_bbox = compute_bbox ();
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
return m_bbox;
|
||||
}
|
||||
|
||||
Box AsIfFlatEdgePairs::compute_bbox () const
|
||||
{
|
||||
db::Box b;
|
||||
for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
b += e->bbox ();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
void AsIfFlatEdgePairs::update_bbox (const db::Box &b)
|
||||
{
|
||||
m_bbox = b;
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
|
||||
void AsIfFlatEdgePairs::invalidate_bbox ()
|
||||
{
|
||||
m_bbox_valid = false;
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
AsIfFlatEdgePairs::filtered (const EdgePairFilterBase &filter) const
|
||||
{
|
||||
std::auto_ptr<FlatEdgePairs> new_edge_pairs (new FlatEdgePairs ());
|
||||
|
||||
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
if (filter.selected (*p)) {
|
||||
new_edge_pairs->insert (*p);
|
||||
}
|
||||
}
|
||||
|
||||
return new_edge_pairs.release ();
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
AsIfFlatEdgePairs::polygons (db::Coord e) const
|
||||
{
|
||||
std::auto_ptr<FlatRegion> output (new FlatRegion ());
|
||||
|
||||
for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) {
|
||||
db::Polygon poly = ep->normalized ().to_polygon (e);
|
||||
if (poly.vertices () >= 3) {
|
||||
output->insert (poly);
|
||||
}
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdgePairs::edges () const
|
||||
{
|
||||
std::auto_ptr<FlatEdges> output (new FlatEdges ());
|
||||
|
||||
for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) {
|
||||
output->insert (ep->first ());
|
||||
output->insert (ep->second ());
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdgePairs::first_edges () const
|
||||
{
|
||||
std::auto_ptr<FlatEdges> output (new FlatEdges ());
|
||||
|
||||
for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) {
|
||||
output->insert (ep->first ());
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdgePairs::second_edges () const
|
||||
{
|
||||
std::auto_ptr<FlatEdges> output (new FlatEdges ());
|
||||
|
||||
for (EdgePairsIterator ep (begin ()); ! ep.at_end (); ++ep) {
|
||||
output->insert (ep->second ());
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
AsIfFlatEdgePairs::add (const EdgePairs &other) const
|
||||
{
|
||||
FlatEdgePairs *other_flat = dynamic_cast<FlatEdgePairs *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
std::auto_ptr<FlatEdgePairs> new_edge_pairs (new FlatEdgePairs (*other_flat));
|
||||
new_edge_pairs->invalidate_cache ();
|
||||
|
||||
size_t n = new_edge_pairs->raw_edge_pairs ().size () + size ();
|
||||
|
||||
new_edge_pairs->reserve (n);
|
||||
|
||||
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
new_edge_pairs->raw_edge_pairs ().insert (*p);
|
||||
}
|
||||
|
||||
return new_edge_pairs.release ();
|
||||
|
||||
} else {
|
||||
|
||||
std::auto_ptr<FlatEdgePairs> new_edge_pairs (new FlatEdgePairs ());
|
||||
|
||||
size_t n = size () + other.size ();
|
||||
|
||||
new_edge_pairs->reserve (n);
|
||||
|
||||
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
new_edge_pairs->raw_edge_pairs ().insert (*p);
|
||||
}
|
||||
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
new_edge_pairs->raw_edge_pairs ().insert (*p);
|
||||
}
|
||||
|
||||
return new_edge_pairs.release ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AsIfFlatEdgePairs::equals (const EdgePairs &other) const
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return false;
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return false;
|
||||
}
|
||||
EdgePairsIterator o1 (begin ());
|
||||
EdgePairsIterator o2 (other.begin ());
|
||||
while (! o1.at_end () && ! o2.at_end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return false;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AsIfFlatEdgePairs::less (const EdgePairs &other) const
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return empty () < other.empty ();
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return (size () < other.size ());
|
||||
}
|
||||
EdgePairsIterator o1 (begin ());
|
||||
EdgePairsIterator o2 (other.begin ());
|
||||
while (! o1.at_end () && ! o2.at_end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return *o1 < *o2;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
AsIfFlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
// improves performance when inserting an original layout into the same layout
|
||||
db::LayoutLocker locker (layout);
|
||||
|
||||
db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer);
|
||||
for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
shapes.insert (*e);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsIfFlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const
|
||||
{
|
||||
// improves performance when inserting an original layout into the same layout
|
||||
db::LayoutLocker locker (layout);
|
||||
|
||||
db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer);
|
||||
for (EdgePairsIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
shapes.insert (e->normalized ().to_simple_polygon (enl));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbAsIfFlatEdgePairs
|
||||
#define HDR_dbAsIfFlatEdgePairs
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdgePairsDelegate.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief Provides default flat implementations
|
||||
*/
|
||||
class DB_PUBLIC AsIfFlatEdgePairs
|
||||
: public EdgePairsDelegate
|
||||
{
|
||||
public:
|
||||
AsIfFlatEdgePairs ();
|
||||
virtual ~AsIfFlatEdgePairs ();
|
||||
|
||||
virtual size_t size () const;
|
||||
virtual std::string to_string (size_t) const;
|
||||
virtual Box bbox () const;
|
||||
|
||||
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter)
|
||||
{
|
||||
return filtered (filter);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const;
|
||||
|
||||
virtual EdgePairsDelegate *add_in_place (const EdgePairs &other)
|
||||
{
|
||||
return add (other);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *add (const EdgePairs &other) const;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||
virtual EdgesDelegate *edges () const;
|
||||
virtual EdgesDelegate *first_edges () const;
|
||||
virtual EdgesDelegate *second_edges () const;
|
||||
|
||||
virtual EdgePairsDelegate *in (const EdgePairs &, bool) const;
|
||||
|
||||
virtual bool equals (const EdgePairs &other) const;
|
||||
virtual bool less (const EdgePairs &other) const;
|
||||
|
||||
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;
|
||||
|
||||
protected:
|
||||
void update_bbox (const db::Box &box);
|
||||
void invalidate_bbox ();
|
||||
|
||||
private:
|
||||
AsIfFlatEdgePairs &operator= (const AsIfFlatEdgePairs &other);
|
||||
|
||||
mutable bool m_bbox_valid;
|
||||
mutable db::Box m_bbox;
|
||||
|
||||
virtual db::Box compute_bbox () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,627 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbAsIfFlatEdges.h"
|
||||
#include "dbFlatEdges.h"
|
||||
#include "dbFlatEdgePairs.h"
|
||||
#include "dbFlatRegion.h"
|
||||
#include "dbEmptyEdges.h"
|
||||
#include "dbEdges.h"
|
||||
#include "dbEdgesUtils.h"
|
||||
#include "dbEdgeBoolean.h"
|
||||
#include "dbBoxConvert.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbFlatRegion.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbShapeProcessor.h"
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
#include "dbPolygon.h"
|
||||
#include "dbPath.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// AsIfFlagEdges implementation
|
||||
|
||||
AsIfFlatEdges::AsIfFlatEdges ()
|
||||
: EdgesDelegate (), m_bbox_valid (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AsIfFlatEdges::~AsIfFlatEdges ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
std::string
|
||||
AsIfFlatEdges::to_string (size_t nmax) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
EdgesIterator p (begin ());
|
||||
bool first = true;
|
||||
for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) {
|
||||
if (! first) {
|
||||
os << ";";
|
||||
}
|
||||
first = false;
|
||||
os << p->to_string ();
|
||||
}
|
||||
if (! p.at_end ()) {
|
||||
os << "...";
|
||||
}
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::selected_interacting_generic (const Region &other, bool inverse) const
|
||||
{
|
||||
// shortcuts
|
||||
if (other.empty () || empty ()) {
|
||||
return new EmptyEdges ();
|
||||
}
|
||||
|
||||
db::box_scanner2<db::Edge, size_t, db::Polygon, size_t> scanner (report_progress (), progress_desc ());
|
||||
|
||||
AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ());
|
||||
|
||||
for ( ; ! e.at_end (); ++e) {
|
||||
scanner.insert1 (e.operator-> (), 0);
|
||||
}
|
||||
|
||||
AddressablePolygonDelivery p = other.addressable_polygons ();
|
||||
|
||||
for ( ; ! p.at_end (); ++p) {
|
||||
scanner.insert2 (p.operator-> (), 1);
|
||||
}
|
||||
|
||||
std::auto_ptr<FlatEdges> output (new FlatEdges (true));
|
||||
|
||||
if (! inverse) {
|
||||
|
||||
edge_to_region_interaction_filter<FlatEdges> filter (*output);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
} else {
|
||||
|
||||
std::set<db::Edge> interacting;
|
||||
edge_to_region_interaction_filter<std::set<db::Edge> > filter (interacting);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> (), db::box_convert<db::Polygon> ());
|
||||
|
||||
for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
|
||||
if (interacting.find (*o) == interacting.end ()) {
|
||||
output->insert (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::selected_interacting (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, false);
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::selected_not_interacting (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, true);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct JoinEdgesClusterCollector
|
||||
: public db::cluster_collector<db::Edge, size_t, JoinEdgesCluster>
|
||||
{
|
||||
typedef db::Edge::coord_type coord_type;
|
||||
|
||||
JoinEdgesClusterCollector (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i)
|
||||
: db::cluster_collector<db::Edge, size_t, JoinEdgesCluster> (JoinEdgesCluster (output, ext_b, ext_e, ext_o, ext_i), true)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
if (o1->p2 () == o2->p1 () || o1->p1 () == o2->p2 ()) {
|
||||
db::cluster_collector<db::Edge, size_t, JoinEdgesCluster>::add (o1, p1, o2, p2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
AsIfFlatEdges::extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const
|
||||
{
|
||||
if (join) {
|
||||
|
||||
std::auto_ptr<FlatRegion> output (new FlatRegion ());
|
||||
db::ShapeGenerator sg (output->raw_polygons (), false);
|
||||
JoinEdgesClusterCollector cluster_collector (&sg, ext_b, ext_e, ext_o, ext_i);
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
||||
scanner.reserve (size ());
|
||||
|
||||
AddressableEdgeDelivery e (begin (), has_valid_edges ());
|
||||
|
||||
size_t n = 0;
|
||||
for ( ; ! e.at_end (); ++e) {
|
||||
scanner.insert (e.operator-> (), n);
|
||||
++n;
|
||||
}
|
||||
|
||||
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
return output.release ();
|
||||
|
||||
} else {
|
||||
|
||||
std::auto_ptr<FlatRegion> output (new FlatRegion ());
|
||||
for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) {
|
||||
output->insert (extended_edge (*e, ext_b, ext_e, ext_o, ext_i));
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::selected_interacting_generic (const Edges &edges, bool inverse) const
|
||||
{
|
||||
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
||||
|
||||
AddressableEdgeDelivery e (begin_merged (), has_valid_merged_edges ());
|
||||
|
||||
for ( ; ! e.at_end (); ++e) {
|
||||
scanner.insert (e.operator-> (), 0);
|
||||
}
|
||||
|
||||
AddressableEdgeDelivery ee = edges.addressable_edges ();
|
||||
|
||||
for ( ; ! ee.at_end (); ++ee) {
|
||||
scanner.insert (ee.operator-> (), 1);
|
||||
}
|
||||
|
||||
std::auto_ptr<FlatEdges> output (new FlatEdges (true));
|
||||
|
||||
if (! inverse) {
|
||||
|
||||
edge_interaction_filter<FlatEdges> filter (*output);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
} else {
|
||||
|
||||
std::set<db::Edge> interacting;
|
||||
edge_interaction_filter<std::set<db::Edge> > filter (interacting);
|
||||
scanner.process (filter, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
|
||||
if (interacting.find (*o) == interacting.end ()) {
|
||||
output->insert (*o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::selected_interacting (const Edges &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, false);
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::selected_not_interacting (const Edges &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, true);
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::in (const Edges &other, bool invert) const
|
||||
{
|
||||
std::set <db::Edge> op;
|
||||
for (EdgesIterator o (other.begin_merged ()); ! o.at_end (); ++o) {
|
||||
op.insert (*o);
|
||||
}
|
||||
|
||||
std::auto_ptr<FlatEdges> new_region (new FlatEdges (false));
|
||||
|
||||
for (EdgesIterator o (begin_merged ()); ! o.at_end (); ++o) {
|
||||
if ((op.find (*o) == op.end ()) == invert) {
|
||||
new_region->insert (*o);
|
||||
}
|
||||
}
|
||||
|
||||
return new_region.release ();
|
||||
}
|
||||
|
||||
size_t
|
||||
AsIfFlatEdges::size () const
|
||||
{
|
||||
size_t n = 0;
|
||||
for (EdgesIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
AsIfFlatEdges::length_type
|
||||
AsIfFlatEdges::length (const db::Box &box) const
|
||||
{
|
||||
distance_type l = 0;
|
||||
|
||||
for (EdgesIterator e (begin_merged ()); ! e.at_end (); ++e) {
|
||||
|
||||
if (box.empty () || (box.contains (e->p1 ()) && box.contains (e->p2 ()))) {
|
||||
l += e->length ();
|
||||
} else {
|
||||
|
||||
std::pair<bool, db::Edge> ce = e->clipped (box);
|
||||
if (ce.first) {
|
||||
|
||||
db::Coord dx = ce.second.dx ();
|
||||
db::Coord dy = ce.second.dy ();
|
||||
db::Coord x = ce.second.p1 ().x ();
|
||||
db::Coord y = ce.second.p1 ().y ();
|
||||
if ((dx == 0 && x == box.left () && dy < 0) ||
|
||||
(dx == 0 && x == box.right () && dy > 0) ||
|
||||
(dy == 0 && y == box.top () && dx < 0) ||
|
||||
(dy == 0 && y == box.bottom () && dx > 0)) {
|
||||
// not counted -> box is at outside side of the edge
|
||||
} else {
|
||||
l += ce.second.length ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Box AsIfFlatEdges::bbox () const
|
||||
{
|
||||
if (! m_bbox_valid) {
|
||||
m_bbox = compute_bbox ();
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
return m_bbox;
|
||||
}
|
||||
|
||||
Box AsIfFlatEdges::compute_bbox () const
|
||||
{
|
||||
db::Box b;
|
||||
for (EdgesIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
b += e->bbox ();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
void AsIfFlatEdges::update_bbox (const db::Box &b)
|
||||
{
|
||||
m_bbox = b;
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
|
||||
void AsIfFlatEdges::invalidate_bbox ()
|
||||
{
|
||||
m_bbox_valid = false;
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::processed (const EdgeProcessorBase &filter) const
|
||||
{
|
||||
std::auto_ptr<FlatEdges> edges (new FlatEdges ());
|
||||
|
||||
if (filter.result_must_not_be_merged ()) {
|
||||
edges->set_merged_semantics (false);
|
||||
}
|
||||
|
||||
std::vector<db::Edge> res_edges;
|
||||
|
||||
for (EdgesIterator e (filter.requires_raw_input () ? begin () : begin_merged ()); ! e.at_end (); ++e) {
|
||||
res_edges.clear ();
|
||||
filter.process (*e, res_edges);
|
||||
for (std::vector<db::Edge>::const_iterator er = res_edges.begin (); er != res_edges.end (); ++er) {
|
||||
edges->insert (*er);
|
||||
}
|
||||
}
|
||||
|
||||
return edges.release ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
AsIfFlatEdges::processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const
|
||||
{
|
||||
std::auto_ptr<FlatEdgePairs> edge_pairs (new FlatEdgePairs ());
|
||||
|
||||
if (filter.result_must_not_be_merged ()) {
|
||||
edge_pairs->set_merged_semantics (false);
|
||||
}
|
||||
|
||||
std::vector<db::EdgePair> res_edge_pairs;
|
||||
|
||||
for (EdgesIterator e (filter.requires_raw_input () ? begin () : begin_merged ()); ! e.at_end (); ++e) {
|
||||
res_edge_pairs.clear ();
|
||||
filter.process (*e, res_edge_pairs);
|
||||
for (std::vector<db::EdgePair>::const_iterator epr = res_edge_pairs.begin (); epr != res_edge_pairs.end (); ++epr) {
|
||||
edge_pairs->insert (*epr);
|
||||
}
|
||||
}
|
||||
|
||||
return edge_pairs.release ();
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
AsIfFlatEdges::processed_to_polygons (const EdgeToPolygonProcessorBase &filter) const
|
||||
{
|
||||
std::auto_ptr<FlatRegion> region (new FlatRegion ());
|
||||
|
||||
if (filter.result_must_not_be_merged ()) {
|
||||
region->set_merged_semantics (false);
|
||||
}
|
||||
|
||||
std::vector<db::Polygon> res_polygons;
|
||||
|
||||
for (EdgesIterator e (filter.requires_raw_input () ? begin () : begin_merged ()); ! e.at_end (); ++e) {
|
||||
res_polygons.clear ();
|
||||
filter.process (*e, res_polygons);
|
||||
for (std::vector<db::Polygon>::const_iterator pr = res_polygons.begin (); pr != res_polygons.end (); ++pr) {
|
||||
region->insert (*pr);
|
||||
}
|
||||
}
|
||||
|
||||
return region.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::filtered (const EdgeFilterBase &filter) const
|
||||
{
|
||||
std::auto_ptr<FlatEdges> new_region (new FlatEdges ());
|
||||
|
||||
for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
||||
if (filter.selected (*p)) {
|
||||
new_region->insert (*p);
|
||||
}
|
||||
}
|
||||
|
||||
return new_region.release ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
std::auto_ptr<FlatEdgePairs> result (new FlatEdgePairs ());
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
||||
scanner.reserve (size () + (other ? other->size () : 0));
|
||||
|
||||
AddressableEdgeDelivery e (begin_merged (), has_valid_edges ());
|
||||
|
||||
size_t n = 0;
|
||||
for ( ; ! e.at_end (); ++e) {
|
||||
scanner.insert (e.operator-> (), n);
|
||||
n += 2;
|
||||
}
|
||||
|
||||
AddressableEdgeDelivery ee;
|
||||
|
||||
if (other) {
|
||||
ee = other->addressable_merged_edges ();
|
||||
n = 1;
|
||||
for ( ; ! ee.at_end (); ++ee) {
|
||||
scanner.insert (ee.operator-> (), n);
|
||||
n += 2;
|
||||
}
|
||||
}
|
||||
|
||||
EdgeRelationFilter check (rel, d, metrics);
|
||||
check.set_include_zero (other != 0);
|
||||
check.set_whole_edges (whole_edges);
|
||||
check.set_ignore_angle (ignore_angle);
|
||||
check.set_min_projection (min_projection);
|
||||
check.set_max_projection (max_projection);
|
||||
|
||||
edge2edge_check_for_edges<db::FlatEdgePairs> edge_check (check, *result, other != 0);
|
||||
scanner.process (edge_check, d, db::box_convert<db::Edge> ());
|
||||
|
||||
return result.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::boolean (const Edges *other, EdgeBoolOp op) const
|
||||
{
|
||||
std::auto_ptr<FlatEdges> output (new FlatEdges (true));
|
||||
EdgeBooleanClusterCollector<db::Shapes> cluster_collector (&output->raw_edges (), op);
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
||||
scanner.reserve (size () + (other ? other->size () : 0));
|
||||
|
||||
AddressableEdgeDelivery e (begin (), has_valid_edges ());
|
||||
|
||||
for ( ; ! e.at_end (); ++e) {
|
||||
if (! e->is_degenerate ()) {
|
||||
scanner.insert (e.operator-> (), 0);
|
||||
}
|
||||
}
|
||||
|
||||
AddressableEdgeDelivery ee;
|
||||
|
||||
if (other) {
|
||||
ee = other->addressable_edges ();
|
||||
for ( ; ! ee.at_end (); ++ee) {
|
||||
if (! ee->is_degenerate ()) {
|
||||
scanner.insert (ee.operator-> (), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::edge_region_op (const Region &other, bool outside, bool include_borders) const
|
||||
{
|
||||
// shortcuts
|
||||
if (other.empty ()) {
|
||||
if (! outside) {
|
||||
return new EmptyEdges ();
|
||||
} else {
|
||||
return clone ();
|
||||
}
|
||||
} else if (empty ()) {
|
||||
return new EmptyEdges ();
|
||||
}
|
||||
|
||||
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
||||
|
||||
for (db::Region::const_iterator p = other.begin (); ! p.at_end (); ++p) {
|
||||
if (p->box ().touches (bbox ())) {
|
||||
ep.insert (*p, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (EdgesIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
ep.insert (*e, 1);
|
||||
}
|
||||
|
||||
std::auto_ptr<FlatEdges> output (new FlatEdges (false));
|
||||
db::EdgeShapeGenerator cc (output->raw_edges (), true /*clear*/);
|
||||
db::EdgePolygonOp op (outside, include_borders);
|
||||
ep.process (cc, op);
|
||||
|
||||
return output.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
AsIfFlatEdges::add (const Edges &other) const
|
||||
{
|
||||
FlatEdges *other_flat = dynamic_cast<FlatEdges *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
std::auto_ptr<FlatEdges> new_edges (new FlatEdges (*other_flat));
|
||||
new_edges->set_is_merged (false);
|
||||
new_edges->invalidate_cache ();
|
||||
|
||||
size_t n = new_edges->raw_edges ().size () + size ();
|
||||
|
||||
new_edges->reserve (n);
|
||||
|
||||
for (EdgesIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
new_edges->raw_edges ().insert (*p);
|
||||
}
|
||||
|
||||
return new_edges.release ();
|
||||
|
||||
} else {
|
||||
|
||||
std::auto_ptr<FlatEdges> new_edges (new FlatEdges (false /*not merged*/));
|
||||
|
||||
size_t n = size () + other.size ();
|
||||
|
||||
new_edges->reserve (n);
|
||||
|
||||
for (EdgesIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
new_edges->raw_edges ().insert (*p);
|
||||
}
|
||||
for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
new_edges->raw_edges ().insert (*p);
|
||||
}
|
||||
|
||||
return new_edges.release ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AsIfFlatEdges::equals (const Edges &other) const
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return false;
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return false;
|
||||
}
|
||||
EdgesIterator o1 (begin ());
|
||||
EdgesIterator o2 (other.begin ());
|
||||
while (! o1.at_end () && ! o2.at_end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return false;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AsIfFlatEdges::less (const Edges &other) const
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return empty () < other.empty ();
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return (size () < other.size ());
|
||||
}
|
||||
EdgesIterator o1 (begin ());
|
||||
EdgesIterator o2 (other.begin ());
|
||||
while (! o1.at_end () && ! o2.at_end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return *o1 < *o2;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
AsIfFlatEdges::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
// improves performance when inserting an original layout into the same layout
|
||||
db::LayoutLocker locker (layout);
|
||||
|
||||
db::Shapes &shapes = layout->cell (into_cell).shapes (into_layer);
|
||||
for (EdgesIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
shapes.insert (*e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbAsIfFlatEdges
|
||||
#define HDR_dbAsIfFlatEdges
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbBoxScanner.h"
|
||||
#include "dbEdgesDelegate.h"
|
||||
#include "dbBoxScanner.h"
|
||||
#include "dbPolygonTools.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace db {
|
||||
|
||||
class PolygonSink;
|
||||
|
||||
/**
|
||||
* @brief Provides default flat implementations
|
||||
*/
|
||||
class DB_PUBLIC AsIfFlatEdges
|
||||
: public EdgesDelegate
|
||||
{
|
||||
public:
|
||||
AsIfFlatEdges ();
|
||||
virtual ~AsIfFlatEdges ();
|
||||
|
||||
virtual size_t size () const;
|
||||
virtual std::string to_string (size_t) const;
|
||||
virtual distance_type length (const db::Box &) const;
|
||||
virtual Box bbox () const;
|
||||
|
||||
virtual EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &filter)
|
||||
{
|
||||
return processed (filter);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *processed (const EdgeProcessorBase &filter) const;
|
||||
virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &) const;
|
||||
virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &) const;
|
||||
|
||||
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter)
|
||||
{
|
||||
return filtered (filter);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *filtered (const EdgeFilterBase &) const;
|
||||
|
||||
virtual EdgesDelegate *merged_in_place ()
|
||||
{
|
||||
return merged ();
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *merged () const
|
||||
{
|
||||
return boolean (0, EdgeOr);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *and_with (const Edges &other) const
|
||||
{
|
||||
return boolean (&other, EdgeAnd);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *and_with (const Region &other) const
|
||||
{
|
||||
return edge_region_op (other, false /*inside*/, true /*include borders*/);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *not_with (const Edges &other) const
|
||||
{
|
||||
return boolean (&other, EdgeNot);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *not_with (const Region &other) const
|
||||
{
|
||||
return edge_region_op (other, true /*outside*/, true /*include borders*/);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *xor_with (const Edges &other) const
|
||||
{
|
||||
return boolean (&other, EdgeXor);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *or_with (const Edges &other) const
|
||||
{
|
||||
return boolean (&other, EdgeOr);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *add_in_place (const Edges &other)
|
||||
{
|
||||
return add (other);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *add (const Edges &other) const;
|
||||
|
||||
virtual EdgesDelegate *inside_part (const Region &other) const
|
||||
{
|
||||
return edge_region_op (other, false /*inside*/, false /*don't include borders*/);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *outside_part (const Region &other) const
|
||||
{
|
||||
return edge_region_op (other, true /*outside*/, false /*don't include borders*/);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const;
|
||||
|
||||
virtual EdgesDelegate *selected_interacting (const Edges &) const;
|
||||
virtual EdgesDelegate *selected_not_interacting (const Edges &) const;
|
||||
virtual EdgesDelegate *selected_interacting (const Region &) const;
|
||||
virtual EdgesDelegate *selected_not_interacting (const Region &) const;
|
||||
|
||||
virtual EdgesDelegate *in (const Edges &, bool) const;
|
||||
|
||||
virtual bool equals (const Edges &other) const;
|
||||
virtual bool less (const Edges &other) const;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
|
||||
|
||||
protected:
|
||||
void update_bbox (const db::Box &box);
|
||||
void invalidate_bbox ();
|
||||
EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const;
|
||||
virtual EdgesDelegate *selected_interacting_generic (const Edges &edges, bool inverse) const;
|
||||
virtual EdgesDelegate *selected_interacting_generic (const Region ®ion, bool inverse) const;
|
||||
|
||||
private:
|
||||
AsIfFlatEdges &operator= (const AsIfFlatEdges &other);
|
||||
|
||||
mutable bool m_bbox_valid;
|
||||
mutable db::Box m_bbox;
|
||||
|
||||
virtual db::Box compute_bbox () const;
|
||||
EdgesDelegate *boolean (const Edges *other, EdgeBoolOp op) const;
|
||||
EdgesDelegate *edge_region_op (const Region &other, bool outside, bool include_borders) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,243 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbAsIfFlatRegion
|
||||
#define HDR_dbAsIfFlatRegion
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbRegionDelegate.h"
|
||||
#include "dbPolygon.h"
|
||||
#include "dbEdge.h"
|
||||
#include "dbBoxScanner.h"
|
||||
#include "dbEdgePairRelations.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief Provides default flat implementations
|
||||
*/
|
||||
class DB_PUBLIC AsIfFlatRegion
|
||||
: public RegionDelegate
|
||||
{
|
||||
public:
|
||||
AsIfFlatRegion ();
|
||||
virtual ~AsIfFlatRegion ();
|
||||
|
||||
virtual bool is_box () const;
|
||||
virtual size_t size () const;
|
||||
|
||||
virtual area_type area (const db::Box &box) const;
|
||||
virtual perimeter_type perimeter (const db::Box &box) const;
|
||||
virtual Box bbox () const;
|
||||
|
||||
virtual std::string to_string (size_t nmax) const;
|
||||
|
||||
EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *grid_check (db::Coord gx, db::Coord gy) const;
|
||||
virtual EdgePairsDelegate *angle_check (double min, double max, bool inverse) const;
|
||||
|
||||
virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy)
|
||||
{
|
||||
return snapped (gx, gy);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy);
|
||||
|
||||
virtual EdgesDelegate *edges (const EdgeFilterBase *) const;
|
||||
|
||||
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter)
|
||||
{
|
||||
return processed (filter);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const;
|
||||
virtual EdgesDelegate *processed_to_edges (const PolygonToEdgeProcessorBase &filter) const;
|
||||
virtual EdgePairsDelegate *processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const;
|
||||
|
||||
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter)
|
||||
{
|
||||
return filtered (filter);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const;
|
||||
|
||||
virtual RegionDelegate *merged_in_place ()
|
||||
{
|
||||
return merged ();
|
||||
}
|
||||
|
||||
virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc)
|
||||
{
|
||||
return merged (min_coherence, min_wc);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *merged () const
|
||||
{
|
||||
return merged (min_coherence (), 0);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const;
|
||||
|
||||
virtual RegionDelegate *sized (coord_type d, unsigned int mode) const;
|
||||
virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const;
|
||||
|
||||
virtual RegionDelegate *and_with (const Region &other) const;
|
||||
virtual RegionDelegate *not_with (const Region &other) const;
|
||||
virtual RegionDelegate *xor_with (const Region &other) const;
|
||||
virtual RegionDelegate *or_with (const Region &other) const;
|
||||
|
||||
virtual RegionDelegate *add_in_place (const Region &other)
|
||||
{
|
||||
return add (other);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *add (const Region &other) const;
|
||||
|
||||
virtual RegionDelegate *selected_outside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 1, false, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_outside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 1, false, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_inside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, -1, true, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_inside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, -1, true, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_interacting (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, true, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_interacting (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, true, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_interacting (const Edges &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_interacting (const Edges &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_overlapping (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, false, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_overlapping (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, false, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *in (const Region &other, bool invert) const;
|
||||
|
||||
virtual bool equals (const Region &other) const;
|
||||
virtual bool less (const Region &other) const;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
|
||||
|
||||
protected:
|
||||
void update_bbox (const db::Box &box);
|
||||
void invalidate_bbox ();
|
||||
|
||||
EdgePairsDelegate *run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const;
|
||||
EdgePairsDelegate *run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const;
|
||||
RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const;
|
||||
RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const;
|
||||
|
||||
template <class Trans>
|
||||
static void produce_markers_for_grid_check (const db::Polygon &poly, const Trans &tr, db::Coord gx, db::Coord gy, db::Shapes &shapes);
|
||||
template <class Trans>
|
||||
static void produce_markers_for_angle_check (const db::Polygon &poly, const Trans &tr, double min, double max, bool inverse, db::Shapes &shapes);
|
||||
static db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector<db::Point> &heap);
|
||||
|
||||
private:
|
||||
AsIfFlatRegion &operator= (const AsIfFlatRegion &other);
|
||||
|
||||
mutable bool m_bbox_valid;
|
||||
mutable db::Box m_bbox;
|
||||
|
||||
virtual db::Box compute_bbox () const;
|
||||
static RegionDelegate *region_from_box (const db::Box &b);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -122,13 +122,21 @@ struct box_scanner_receiver
|
|||
*/
|
||||
void finish (const Obj * /*obj*/, const Prop & /*prop*/) { }
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief Callback for an interaction of o1 with o2.
|
||||
*
|
||||
* This method is called when the object o1 interacts with o2 within the current
|
||||
* definition.
|
||||
*/
|
||||
void add (const Obj * /*o1*/, const Prop & /*p1*/, const Obj * /*o2*/, const Prop & /*p2*/) { }
|
||||
|
||||
/**
|
||||
* @brief Indicates whether the scanner may stop
|
||||
*
|
||||
* The scanner will stop if this method returns true. This feature can be used to
|
||||
* terminate the scan process early if the outcome is known.
|
||||
*/
|
||||
bool stop () const { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -250,9 +258,13 @@ public:
|
|||
*
|
||||
* The box converter must be capable of converting the Obj object into a box.
|
||||
* It must provide a box_type typedef.
|
||||
*
|
||||
* The scanner process can be terminated early by making the receiver's
|
||||
* stop() method return true. In this case, this method will return false.
|
||||
* Otherwise it will return true.
|
||||
*/
|
||||
template <class Rec, class BoxConvert>
|
||||
void process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ())
|
||||
bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ())
|
||||
{
|
||||
typedef typename BoxConvert::box_type box_type;
|
||||
typedef typename box_type::coord_type coord_type;
|
||||
|
|
@ -261,14 +273,37 @@ public:
|
|||
typedef bs_side_compare_vs_const_func<BoxConvert, Obj, Prop, box_top<Box> > below_func;
|
||||
typedef bs_side_compare_vs_const_func<BoxConvert, Obj, Prop, box_right<Box> > left_func;
|
||||
|
||||
// sort out the entries with an empty bbox (we must not put that into sort)
|
||||
|
||||
typename container_type::iterator wi = m_pp.begin ();
|
||||
for (typename container_type::iterator ri = m_pp.begin (); ri != m_pp.end (); ++ri) {
|
||||
if (! bc (*ri->first).empty ()) {
|
||||
if (wi != ri) {
|
||||
*wi = *ri;
|
||||
}
|
||||
++wi;
|
||||
} else {
|
||||
// we call finish on empty elements though
|
||||
rec.finish (ri->first, ri->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (wi != m_pp.end ()) {
|
||||
m_pp.erase (wi, m_pp.end ());
|
||||
}
|
||||
|
||||
if (m_pp.size () <= m_scanner_thr) {
|
||||
|
||||
// below m_scanner_thr elements use the brute force approach which is faster in that case
|
||||
|
||||
for (iterator_type i = m_pp.begin (); i != m_pp.end (); ++i) {
|
||||
box_type b1 = bc (*i->first);
|
||||
for (iterator_type j = i + 1; j != m_pp.end (); ++j) {
|
||||
if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) {
|
||||
if (bs_boxes_overlap (b1, bc (*j->first), enl)) {
|
||||
rec.add (i->first, i->second, j->first, j->second);
|
||||
if (rec.stop ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -355,6 +390,9 @@ public:
|
|||
if (seen.insert (std::make_pair (i->first, j->first)).second) {
|
||||
seen.insert (std::make_pair (j->first, i->first));
|
||||
rec.add (i->first, i->second, j->first, j->second);
|
||||
if (rec.stop ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -379,6 +417,8 @@ public:
|
|||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -389,6 +429,432 @@ private:
|
|||
std::string m_progress_desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A template for the twofold box scanner output receiver
|
||||
*
|
||||
* This template specifies the methods or provides a default implementation for them
|
||||
* for use as the output receiver of the twofold box scanner.
|
||||
*/
|
||||
template <class Obj1, class Prop1, class Obj2, class Prop2>
|
||||
struct box_scanner_receiver2
|
||||
{
|
||||
/**
|
||||
* @brief Indicates that the given object of first type is no longer used
|
||||
*
|
||||
* The finish1 method is called when an object of the first type is no longer in the queue and can be
|
||||
* discarded.
|
||||
*/
|
||||
void finish1 (const Obj1 * /*obj*/, const Prop1 & /*prop*/) { }
|
||||
|
||||
/**
|
||||
* @brief Indicates that the given object of second type is no longer used
|
||||
*
|
||||
* The finish method is called when an object of the second type is no longer in the queue and can be
|
||||
* discarded.
|
||||
*/
|
||||
void finish2 (const Obj2 * /*obj*/, const Prop2 & /*prop*/) { }
|
||||
|
||||
/**
|
||||
* @brief Callback for an interaction of o1 with o2.
|
||||
*
|
||||
* This method is called when the object o1 interacts with o2 within the current
|
||||
* definition.
|
||||
*/
|
||||
void add (const Obj1 * /*o1*/, const Prop1 & /*p1*/, const Obj2 * /*o2*/, const Prop2 & /*p2*/) { }
|
||||
|
||||
/**
|
||||
* @brief Indicates whether the scanner may stop
|
||||
*
|
||||
* The scanner will stop if this method returns true. This feature can be used to
|
||||
* terminate the scan process early if the outcome is known.
|
||||
*/
|
||||
bool stop () const { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A box scanner framework (twofold version)
|
||||
*
|
||||
* This implementation provides a box scanner for two different types. Apart from
|
||||
* that it is similar to the uniform-type box scanner.
|
||||
*
|
||||
* It will not report interactions within the Obj1 or Obj2 group, but only
|
||||
* interactions between Obj1 and Obj2 objects.
|
||||
*/
|
||||
template <class Obj1, class Prop1, class Obj2, class Prop2>
|
||||
class box_scanner2
|
||||
{
|
||||
public:
|
||||
typedef Obj1 object_type1;
|
||||
typedef Obj2 object_type2;
|
||||
typedef std::vector<std::pair<const Obj1 *, Prop1> > container_type1;
|
||||
typedef std::vector<std::pair<const Obj2 *, Prop2> > container_type2;
|
||||
typedef typename container_type1::iterator iterator_type1;
|
||||
typedef typename container_type2::iterator iterator_type2;
|
||||
|
||||
/**
|
||||
* @brief Default ctor
|
||||
*/
|
||||
box_scanner2 (bool report_progress = false, const std::string &progress_desc = std::string ())
|
||||
: m_fill_factor (2), m_scanner_thr (100),
|
||||
m_report_progress (report_progress), m_progress_desc (progress_desc)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the scanner threshold
|
||||
*
|
||||
* This value determines for how many elements the implementation switches to the scanner
|
||||
* implementation instead of the plain element-by-element interaction test.
|
||||
* The default value is 100.
|
||||
*/
|
||||
void set_scanner_threshold (size_t n)
|
||||
{
|
||||
m_scanner_thr = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the scanner threshold
|
||||
*/
|
||||
size_t scanner_threshold () const
|
||||
{
|
||||
return m_scanner_thr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the fill factor
|
||||
*
|
||||
* The fill factor determines how many new entries will be collected for a band.
|
||||
* A fill factor of 2 means that the number of elements in the band will be
|
||||
* doubled after elements outside of the band have been removed.
|
||||
* The default fill factor is 2.
|
||||
*/
|
||||
void set_fill_factor (double ff)
|
||||
{
|
||||
m_fill_factor = ff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the fill factor
|
||||
*/
|
||||
double fill_factor () const
|
||||
{
|
||||
return m_fill_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserve for n elements of Obj1 type
|
||||
*/
|
||||
void reserve1 (size_t n)
|
||||
{
|
||||
m_pp1.reserve (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserve for n elements of Obj2 type
|
||||
*/
|
||||
void reserve2 (size_t n)
|
||||
{
|
||||
m_pp2.reserve (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the container
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_pp1.clear ();
|
||||
m_pp2.clear ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a new object of type Obj1 into the scanner
|
||||
*
|
||||
* The object's pointer is stored, so the object must remain valid until the
|
||||
* scanner does not need it any longer. An additional property can be attached to
|
||||
* the object which will be stored along with the object.
|
||||
*/
|
||||
void insert1 (const Obj1 *obj, const Prop1 &prop)
|
||||
{
|
||||
m_pp1.push_back (std::make_pair (obj, prop));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a new object of type Obj2 into the scanner
|
||||
*
|
||||
* The object's pointer is stored, so the object must remain valid until the
|
||||
* scanner does not need it any longer. An additional property can be attached to
|
||||
* the object which will be stored along with the object.
|
||||
*/
|
||||
void insert2 (const Obj2 *obj, const Prop2 &prop)
|
||||
{
|
||||
m_pp2.push_back (std::make_pair (obj, prop));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the interactions between the stored objects
|
||||
*
|
||||
* Two objects interact if the boxes of the objects enlarged by the given value overlap.
|
||||
* The enlargement is specified in units of width and height, i.e. half of the enlargement
|
||||
* is applied to one side before the overlap check.
|
||||
*
|
||||
* An enlargement of 1 means that boxes have to touch only in order to get an interaction.
|
||||
*
|
||||
* The box scanner will report all interactions of type Obj1 and Obj2 objects to the receiver object.
|
||||
* See box_scanner_receiver2 for details about the methods that this object must provide.
|
||||
*
|
||||
* The box converter 1 must be capable of converting the Obj1 object into a box.
|
||||
* It must provide a box_type typedef. The box converter 2 must be able to convert Obj2 to
|
||||
* a box. The box type of both box converters must be identical.
|
||||
*
|
||||
* The scanner process can be terminated early by making the receiver's
|
||||
* stop() method return true. In this case, this method will return false.
|
||||
* Otherwise it will return true.
|
||||
*/
|
||||
template <class Rec, class BoxConvert1, class BoxConvert2>
|
||||
bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ())
|
||||
{
|
||||
typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type
|
||||
typedef typename box_type::coord_type coord_type;
|
||||
typedef bs_side_compare_func<BoxConvert1, Obj1, Prop1, box_bottom<Box> > bottom_side_compare_func1;
|
||||
typedef bs_side_compare_func<BoxConvert1, Obj1, Prop1, box_left<Box> > left_side_compare_func1;
|
||||
typedef bs_side_compare_vs_const_func<BoxConvert1, Obj1, Prop1, box_top<Box> > below_func1;
|
||||
typedef bs_side_compare_vs_const_func<BoxConvert1, Obj1, Prop1, box_right<Box> > left_func1;
|
||||
typedef bs_side_compare_func<BoxConvert2, Obj2, Prop2, box_bottom<Box> > bottom_side_compare_func2;
|
||||
typedef bs_side_compare_func<BoxConvert2, Obj2, Prop2, box_left<Box> > left_side_compare_func2;
|
||||
typedef bs_side_compare_vs_const_func<BoxConvert2, Obj2, Prop2, box_top<Box> > below_func2;
|
||||
typedef bs_side_compare_vs_const_func<BoxConvert2, Obj2, Prop2, box_right<Box> > left_func2;
|
||||
|
||||
// sort out the entries with an empty bbox (we must not put that into sort)
|
||||
|
||||
typename container_type1::iterator wi1 = m_pp1.begin ();
|
||||
for (typename container_type1::iterator ri1 = m_pp1.begin (); ri1 != m_pp1.end (); ++ri1) {
|
||||
if (! bc1 (*ri1->first).empty ()) {
|
||||
if (wi1 != ri1) {
|
||||
*wi1 = *ri1;
|
||||
}
|
||||
++wi1;
|
||||
} else {
|
||||
// we call finish on empty elements though
|
||||
rec.finish1 (ri1->first, ri1->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (wi1 != m_pp1.end ()) {
|
||||
m_pp1.erase (wi1, m_pp1.end ());
|
||||
}
|
||||
|
||||
typename container_type2::iterator wi2 = m_pp2.begin ();
|
||||
for (typename container_type2::iterator ri2 = m_pp2.begin (); ri2 != m_pp2.end (); ++ri2) {
|
||||
if (! bc2 (*ri2->first).empty ()) {
|
||||
if (wi2 != ri2) {
|
||||
*wi2 = *ri2;
|
||||
}
|
||||
++wi2;
|
||||
} else {
|
||||
// we call finish on empty elements though
|
||||
rec.finish2 (ri2->first, ri2->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (wi2 != m_pp2.end ()) {
|
||||
m_pp2.erase (wi2, m_pp2.end ());
|
||||
}
|
||||
|
||||
if (m_pp1.empty () || m_pp2.empty ()) {
|
||||
|
||||
// trivial case
|
||||
|
||||
for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) {
|
||||
rec.finish1 (i->first, i->second);
|
||||
}
|
||||
for (iterator_type2 i = m_pp2.begin (); i != m_pp2.end (); ++i) {
|
||||
rec.finish2 (i->first, i->second);
|
||||
}
|
||||
|
||||
} else if (m_pp1.size () + m_pp2.size () <= m_scanner_thr) {
|
||||
|
||||
// below m_scanner_thr elements use the brute force approach which is faster in that case
|
||||
|
||||
for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) {
|
||||
box_type b1 = bc1 (*i->first);
|
||||
for (iterator_type2 j = m_pp2.begin (); j != m_pp2.end (); ++j) {
|
||||
if (bs_boxes_overlap (b1, bc2 (*j->first), enl)) {
|
||||
rec.add (i->first, i->second, j->first, j->second);
|
||||
if (rec.stop ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) {
|
||||
rec.finish1 (i->first, i->second);
|
||||
}
|
||||
for (iterator_type2 i = m_pp2.begin (); i != m_pp2.end (); ++i) {
|
||||
rec.finish2 (i->first, i->second);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
std::set<std::pair<const Obj1 *, const Obj2 *> > seen1;
|
||||
std::set<std::pair<const Obj2 *, const Obj1 *> > seen2;
|
||||
|
||||
std::sort (m_pp1.begin (), m_pp1.end (), bottom_side_compare_func1 (bc1));
|
||||
std::sort (m_pp2.begin (), m_pp2.end (), bottom_side_compare_func2 (bc2));
|
||||
|
||||
coord_type y = std::min (bc1 (*m_pp1.front ().first).bottom (), bc2 (*m_pp2.front ().first).bottom ());
|
||||
|
||||
iterator_type1 current1 = m_pp1.begin ();
|
||||
iterator_type1 future1 = m_pp1.begin ();
|
||||
iterator_type2 current2 = m_pp2.begin ();
|
||||
iterator_type2 future2 = m_pp2.begin ();
|
||||
|
||||
std::auto_ptr<tl::RelativeProgress> progress (0);
|
||||
if (m_report_progress) {
|
||||
if (m_progress_desc.empty ()) {
|
||||
progress.reset (new tl::RelativeProgress (tl::to_string (tr ("Processing")), m_pp1.size () + m_pp2.size (), 1000));
|
||||
} else {
|
||||
progress.reset (new tl::RelativeProgress (m_progress_desc, m_pp1.size () + m_pp2.size (), 1000));
|
||||
}
|
||||
}
|
||||
|
||||
while (future1 != m_pp1.end () || future2 != m_pp2.end ()) {
|
||||
|
||||
iterator_type1 cc1 = current1;
|
||||
iterator_type2 cc2 = current2;
|
||||
current1 = std::partition (current1, future1, below_func1 (bc1, y + 1 - enl));
|
||||
current2 = std::partition (current2, future2, below_func2 (bc2, y + 1 - enl));
|
||||
|
||||
while (cc1 != current1) {
|
||||
rec.finish1 (cc1->first, cc1->second);
|
||||
typename std::set<std::pair<const Obj1 *, const Obj2 *> >::iterator s;
|
||||
s = seen1.lower_bound (std::make_pair (cc1->first, (const Obj2 *)0));
|
||||
while (s != seen1.end () && s->first == cc1->first) {
|
||||
seen1.erase (s++);
|
||||
}
|
||||
++cc1;
|
||||
}
|
||||
|
||||
while (cc2 != current2) {
|
||||
rec.finish2 (cc2->first, cc2->second);
|
||||
typename std::set<std::pair<const Obj2 *, const Obj1 *> >::iterator s;
|
||||
s = seen2.lower_bound (std::make_pair (cc2->first, (const Obj1 *)0));
|
||||
while (s != seen2.end () && s->first == cc2->first) {
|
||||
seen2.erase (s++);
|
||||
}
|
||||
++cc2;
|
||||
}
|
||||
|
||||
// add at least the required items per band
|
||||
size_t min_band_size = size_t ((future1 - current1) * m_fill_factor) + size_t ((future2 - current2) * m_fill_factor);
|
||||
coord_type yy = y;
|
||||
do {
|
||||
if (future1 != m_pp1.end () && future2 != m_pp2.end ()) {
|
||||
yy = std::min (bc1 (*future1->first).bottom (), bc2 (*future2->first).bottom ());
|
||||
} else if (future1 != m_pp1.end ()) {
|
||||
yy = bc1 (*future1->first).bottom ();
|
||||
} else {
|
||||
yy = bc2 (*future2->first).bottom ();
|
||||
}
|
||||
while (future1 != m_pp1.end () && bc1 (*future1->first).bottom () == yy) {
|
||||
++future1;
|
||||
}
|
||||
while (future2 != m_pp2.end () && bc2 (*future2->first).bottom () == yy) {
|
||||
++future2;
|
||||
}
|
||||
} while ((future1 != m_pp1.end () || future2 != m_pp2.end ()) && size_t (future1 - current1) + size_t (future2 - current2) < min_band_size);
|
||||
|
||||
if (current1 != future1 && current2 != future2) {
|
||||
|
||||
std::sort (current1, future1, left_side_compare_func1 (bc1));
|
||||
std::sort (current2, future2, left_side_compare_func2 (bc2));
|
||||
|
||||
iterator_type1 c1 = current1;
|
||||
iterator_type1 f1 = current1;
|
||||
iterator_type2 c2 = current2;
|
||||
iterator_type2 f2 = current2;
|
||||
|
||||
coord_type x = std::min (bc1 (*c1->first).left (), bc2 (*c2->first).left ());
|
||||
|
||||
while (f1 != future1 || f2 != future2) {
|
||||
|
||||
c1 = std::partition (c1, f1, left_func1 (bc1, x + 1 - enl));
|
||||
c2 = std::partition (c2, f2, left_func2 (bc2, x + 1 - enl));
|
||||
|
||||
// add at least the required items per band
|
||||
size_t min_box_size = size_t ((f1 - c1) * m_fill_factor) + size_t ((f2 - c2) * m_fill_factor);
|
||||
coord_type xx = x;
|
||||
do {
|
||||
if (f1 != future1 && f2 != future2) {
|
||||
xx = std::min (bc1 (*f1->first).left (), bc2 (*f2->first).left ());
|
||||
} else if (f1 != future1) {
|
||||
xx = bc1 (*f1->first).left ();
|
||||
} else if (f2 != future2) {
|
||||
xx = bc2 (*f2->first).left ();
|
||||
}
|
||||
while (f1 != future1 && bc1 (*f1->first).left () == xx) {
|
||||
++f1;
|
||||
}
|
||||
while (f2 != future2 && bc2 (*f2->first).left () == xx) {
|
||||
++f2;
|
||||
}
|
||||
} while ((f1 != future1 || f2 != future2) && size_t (f1 - c1) + size_t (f2 - c2) < min_box_size);
|
||||
|
||||
if (c1 != f1 && c2 != f2) {
|
||||
for (iterator_type1 i = c1; i != f1; ++i) {
|
||||
for (iterator_type2 j = c2; j < f2; ++j) {
|
||||
if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) {
|
||||
if (seen1.insert (std::make_pair (i->first, j->first)).second) {
|
||||
seen2.insert (std::make_pair (j->first, i->first));
|
||||
rec.add (i->first, i->second, j->first, j->second);
|
||||
if (rec.stop ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x = xx;
|
||||
|
||||
if (m_report_progress) {
|
||||
progress->set (std::min (f1 - m_pp1.begin (), f2 - m_pp2.begin ()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
y = yy;
|
||||
|
||||
}
|
||||
|
||||
while (current1 != m_pp1.end ()) {
|
||||
rec.finish1 (current1->first, current1->second);
|
||||
++current1;
|
||||
}
|
||||
while (current2 != m_pp2.end ()) {
|
||||
rec.finish2 (current2->first, current2->second);
|
||||
++current2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
container_type1 m_pp1;
|
||||
container_type2 m_pp2;
|
||||
double m_fill_factor;
|
||||
size_t m_scanner_thr;
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A cluster template that stores properties
|
||||
*
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ CellMapping::create_from_names (const db::Layout &layout_a, db::cell_index_type
|
|||
}
|
||||
|
||||
std::vector<db::cell_index_type>
|
||||
CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set<db::cell_index_type> *exclude_cells)
|
||||
{
|
||||
std::vector<db::cell_index_type> new_cells;
|
||||
std::vector<db::cell_index_type> new_cells_b;
|
||||
|
|
@ -305,7 +305,7 @@ CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /
|
|||
called_b.insert (cell_index_b);
|
||||
|
||||
for (std::set<db::cell_index_type>::const_iterator b = called_b.begin (); b != called_b.end (); ++b) {
|
||||
if (m_b2a_mapping.find (*b) == m_b2a_mapping.end ()) {
|
||||
if (m_b2a_mapping.find (*b) == m_b2a_mapping.end () && (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ())) {
|
||||
db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b));
|
||||
new_cells.push_back (new_cell);
|
||||
new_cells_b.push_back (*b);
|
||||
|
|
@ -335,7 +335,7 @@ CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /
|
|||
|
||||
db::CellInstArray bci = bi.cell_inst ();
|
||||
bci.object ().cell_index (new_cells [i]);
|
||||
bci.transform_into (db::ICplxTrans (mag));
|
||||
bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ());
|
||||
|
||||
if (bi.has_prop_id ()) {
|
||||
pa.insert (db::CellInstArrayWithProperties (bci, pm (bi.prop_id ())));
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#include "dbTypes.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
|
@ -179,6 +181,19 @@ public:
|
|||
return m_b2a_mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates mappings for all cells not mapped yet
|
||||
*
|
||||
* When constructing a cell mapping by explicit mapping (map (a, b)), some cells may be
|
||||
* left unmapped. This method allows creating mappings for these missing cells by adding
|
||||
* new cells and the corresponding instances into the target layout_a.
|
||||
*
|
||||
* If given, "exclude_cells" can specify a list of cells not to map.
|
||||
*
|
||||
* The returned vector lists the new cells.
|
||||
*/
|
||||
std::vector<db::cell_index_type> create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set<db::cell_index_type> *exclude_cells = 0);
|
||||
|
||||
private:
|
||||
void extract_unique (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand,
|
||||
std::map<db::cell_index_type, db::cell_index_type> &unique_mapping,
|
||||
|
|
@ -187,8 +202,6 @@ private:
|
|||
void dump_mapping (const std::map <db::cell_index_type, std::vector<db::cell_index_type> > &candidates,
|
||||
const db::Layout &layout_a, const db::Layout &layout_b);
|
||||
|
||||
std::vector<db::cell_index_type> create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b);
|
||||
|
||||
std::map <db::cell_index_type, db::cell_index_type> m_b2a_mapping;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,436 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbCellVariants.h"
|
||||
#include "tlUtils.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
VariantsCollectorBase::VariantsCollectorBase ()
|
||||
: mp_red ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
VariantsCollectorBase::VariantsCollectorBase (const TransformationReducer *red)
|
||||
: mp_red (red)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::collect (const db::Layout &layout, const db::Cell &top_cell)
|
||||
{
|
||||
tl_assert (mp_red != 0);
|
||||
|
||||
// The top cell gets a "variant" with unit transformation
|
||||
m_variants [top_cell.cell_index ()].insert (std::make_pair (db::ICplxTrans (), 1));
|
||||
|
||||
std::set<db::cell_index_type> called;
|
||||
top_cell.collect_called_cells (called);
|
||||
|
||||
for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
|
||||
|
||||
if (called.find (*c) == called.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// collect the parent variants per parent cell
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, size_t> > variants_per_parent_cell;
|
||||
for (db::Cell::parent_inst_iterator pi = layout.cell (*c).begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
std::map<db::ICplxTrans, size_t> &variants = variants_per_parent_cell [pi->inst ().object ().cell_index ()];
|
||||
add_variant (variants, pi->child_inst ().cell_inst (), mp_red->is_translation_invariant ());
|
||||
}
|
||||
|
||||
// compute the resulting variants
|
||||
|
||||
std::map<db::ICplxTrans, size_t> &new_variants = m_variants [*c];
|
||||
|
||||
for (std::map<db::cell_index_type, std::map<db::ICplxTrans, size_t> >::const_iterator pv = variants_per_parent_cell.begin (); pv != variants_per_parent_cell.end (); ++pv) {
|
||||
product (variants (pv->first), pv->second, new_variants);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::separate_variants (db::Layout &layout, db::Cell &top_cell, std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > *var_table)
|
||||
{
|
||||
tl_assert (mp_red != 0);
|
||||
|
||||
db::LayoutLocker locker (&layout);
|
||||
|
||||
std::set<db::cell_index_type> called;
|
||||
top_cell.collect_called_cells (called);
|
||||
called.insert (top_cell.cell_index ());
|
||||
|
||||
// create new cells for the variants
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > var_table_intern;
|
||||
if (! var_table) {
|
||||
var_table = &var_table_intern;
|
||||
}
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) {
|
||||
|
||||
if (called.find (*c) == called.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
db::Cell &cell = layout.cell (*c);
|
||||
|
||||
std::map<db::ICplxTrans, size_t> &vv = m_variants [*c];
|
||||
if (vv.size () > 1) {
|
||||
|
||||
std::map<db::ICplxTrans, db::cell_index_type> &vt = (*var_table) [*c];
|
||||
|
||||
std::vector<db::CellInstArrayWithProperties> inst;
|
||||
inst.reserve (cell.cell_instances ());
|
||||
|
||||
for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
||||
inst.push_back (db::CellInstArrayWithProperties (i->cell_inst (), i->prop_id ()));
|
||||
}
|
||||
|
||||
cell.clear_insts ();
|
||||
|
||||
int index = 0;
|
||||
for (std::map<db::ICplxTrans, size_t>::const_iterator v = vv.begin (); v != vv.end (); ++v, ++index) {
|
||||
|
||||
db::cell_index_type ci_var;
|
||||
|
||||
if (v != vv.begin ()) {
|
||||
|
||||
std::string var_name = layout.cell_name (*c);
|
||||
var_name += "$VAR" + tl::to_string (index);
|
||||
|
||||
ci_var = layout.add_cell (var_name.c_str ());
|
||||
copy_shapes (layout, ci_var, *c);
|
||||
|
||||
// a new entry for the variant
|
||||
m_variants [ci_var].insert (*v);
|
||||
|
||||
} else {
|
||||
ci_var = *c;
|
||||
}
|
||||
|
||||
vt.insert (std::make_pair (v->first, ci_var));
|
||||
create_var_instances (layout.cell (ci_var), inst, v->first, *var_table, mp_red->is_translation_invariant ());
|
||||
|
||||
}
|
||||
|
||||
// correct the first (remaining) entry
|
||||
std::pair<db::ICplxTrans, size_t> v1 = *vt.begin ();
|
||||
vv.clear ();
|
||||
vv.insert (v1);
|
||||
|
||||
} else {
|
||||
|
||||
// if the children of this cell are separated, map the instances to the new variants
|
||||
bool needs_update = false;
|
||||
for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end () && ! needs_update; ++cc) {
|
||||
if (var_table->find (*cc) != var_table->end ()) {
|
||||
needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_update) {
|
||||
|
||||
std::vector<db::CellInstArrayWithProperties> inst;
|
||||
inst.reserve (cell.cell_instances ());
|
||||
|
||||
for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
||||
inst.push_back (db::CellInstArrayWithProperties (i->cell_inst (), i->prop_id ()));
|
||||
}
|
||||
|
||||
cell.clear_insts ();
|
||||
create_var_instances (cell, inst, vv.begin ()->first, *var_table, mp_red->is_translation_invariant ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::commit_shapes (db::Layout &layout, db::Cell &top_cell, unsigned int layer, std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > &to_commit)
|
||||
{
|
||||
tl_assert (mp_red != 0);
|
||||
|
||||
if (to_commit.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: this implementation suffers from accumulation of propagated shapes: we add more levels of propagated
|
||||
// shapes if required. We don't clean up, because we do not know when a shape collection stops being required.
|
||||
|
||||
db::LayoutLocker locker (&layout);
|
||||
|
||||
std::set<db::cell_index_type> called;
|
||||
top_cell.collect_called_cells (called);
|
||||
called.insert (top_cell.cell_index ());
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) {
|
||||
|
||||
if (called.find (*c) == called.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
db::Cell &cell = layout.cell (*c);
|
||||
|
||||
std::map<db::ICplxTrans, size_t> &vvc = m_variants [*c];
|
||||
if (vvc.size () > 1) {
|
||||
|
||||
for (std::map<db::ICplxTrans, size_t>::const_iterator vc = vvc.begin (); vc != vvc.end (); ++vc) {
|
||||
|
||||
for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> >::const_iterator tc = to_commit.find (i->cell_index ());
|
||||
if (tc != to_commit.end ()) {
|
||||
|
||||
const std::map<db::ICplxTrans, db::Shapes> &vt = tc->second;
|
||||
|
||||
// NOTE: this will add one more commit slot for propagation ... but we don't clean up.
|
||||
// When would a cleanup happen?
|
||||
std::map<db::ICplxTrans, db::Shapes> &propagated = to_commit [*c];
|
||||
|
||||
for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) {
|
||||
|
||||
db::ICplxTrans t = i->complex_trans (*ia);
|
||||
db::ICplxTrans rt = mp_red->reduce (vc->first * t);
|
||||
std::map<db::ICplxTrans, db::Shapes>::const_iterator v = vt.find (rt);
|
||||
if (v != vt.end ()) {
|
||||
|
||||
db::Shapes &ps = propagated [vc->first];
|
||||
tl::ident_map<db::Layout::properties_id_type> pm;
|
||||
|
||||
for (db::Shapes::shape_iterator si = v->second.begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
|
||||
ps.insert (*si, t, pm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// single variant -> we can commit any shapes we have kept for this cell directly to the cell
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> >::iterator l = to_commit.find (*c);
|
||||
if (l != to_commit.end ()) {
|
||||
tl_assert (l->second.size () == 1);
|
||||
cell.shapes (layer).insert (l->second.begin ()->second);
|
||||
to_commit.erase (l);
|
||||
}
|
||||
|
||||
// for child cells, pull everything that needs to be committed to the parent
|
||||
|
||||
for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> >::const_iterator tc = to_commit.find (i->cell_index ());
|
||||
if (tc != to_commit.end ()) {
|
||||
|
||||
const std::map<db::ICplxTrans, db::Shapes> &vt = tc->second;
|
||||
|
||||
for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) {
|
||||
|
||||
db::ICplxTrans t = i->complex_trans (*ia);
|
||||
db::ICplxTrans rt = mp_red->reduce (vvc.begin ()->first * t);
|
||||
std::map<db::ICplxTrans, db::Shapes>::const_iterator v = vt.find (rt);
|
||||
|
||||
if (v != vt.end ()) {
|
||||
|
||||
tl::ident_map<db::Layout::properties_id_type> pm;
|
||||
|
||||
for (db::Shapes::shape_iterator si = v->second.begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
|
||||
cell.shapes (layer).insert (*si, t, pm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<db::ICplxTrans, size_t> &
|
||||
VariantsCollectorBase::variants (db::cell_index_type ci) const
|
||||
{
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, size_t> >::const_iterator v = m_variants.find (ci);
|
||||
static std::map<db::ICplxTrans, size_t> empty_set;
|
||||
if (v == m_variants.end ()) {
|
||||
return empty_set;
|
||||
} else {
|
||||
return v->second;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
VariantsCollectorBase::has_variants () const
|
||||
{
|
||||
for (std::map<db::cell_index_type, std::map<db::ICplxTrans, size_t> >::const_iterator i = m_variants.begin (); i != m_variants.end (); ++i) {
|
||||
if (i->second.size () > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::add_variant (std::map<db::ICplxTrans, size_t> &variants, const db::CellInstArray &inst, bool tl_invariant) const
|
||||
{
|
||||
if (tl_invariant) {
|
||||
add_variant_tl_invariant (variants, inst);
|
||||
} else {
|
||||
add_variant_non_tl_invariant (variants, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::add_variant_non_tl_invariant (std::map<db::ICplxTrans, size_t> &variants, const db::CellInstArray &inst) const
|
||||
{
|
||||
if (inst.is_complex ()) {
|
||||
for (db::CellInstArray::iterator i = inst.begin (); ! i.at_end (); ++i) {
|
||||
variants [mp_red->reduce (inst.complex_trans (*i))] += 1;
|
||||
}
|
||||
} else {
|
||||
for (db::CellInstArray::iterator i = inst.begin (); ! i.at_end (); ++i) {
|
||||
variants [db::ICplxTrans (mp_red->reduce (*i))] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::add_variant_tl_invariant (std::map<db::ICplxTrans, size_t> &variants, const db::CellInstArray &inst) const
|
||||
{
|
||||
if (inst.is_complex ()) {
|
||||
variants [mp_red->reduce (inst.complex_trans ())] += inst.size ();
|
||||
} else {
|
||||
variants [db::ICplxTrans (mp_red->reduce (inst.front ()))] += inst.size ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::product (const std::map<db::ICplxTrans, size_t> &v1, const std::map<db::ICplxTrans, size_t> &v2, std::map<db::ICplxTrans, size_t> &prod) const
|
||||
{
|
||||
for (std::map<db::ICplxTrans, size_t>::const_iterator i = v1.begin (); i != v1.end (); ++i) {
|
||||
for (std::map<db::ICplxTrans, size_t>::const_iterator j = v2.begin (); j != v2.end (); ++j) {
|
||||
prod [mp_red->reduce (i->first * j->first)] += i->second * j->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::copy_shapes (db::Layout &layout, db::cell_index_type ci_to, db::cell_index_type ci_from) const
|
||||
{
|
||||
db::Cell &to = layout.cell (ci_to);
|
||||
const db::Cell &from = layout.cell (ci_from);
|
||||
for (db::Layout::layer_iterator li = layout.begin_layers (); li != layout.end_layers (); ++li) {
|
||||
to.shapes ((*li).first) = from.shapes ((*li).first);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::create_var_instances (db::Cell &in_cell, std::vector<db::CellInstArrayWithProperties> &inst, const db::ICplxTrans &for_var, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_table, bool tl_invariant) const
|
||||
{
|
||||
if (tl_invariant) {
|
||||
create_var_instances_tl_invariant (in_cell, inst, for_var, var_table);
|
||||
} else {
|
||||
create_var_instances_non_tl_invariant (in_cell, inst, for_var, var_table);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::create_var_instances_non_tl_invariant (db::Cell &in_cell, std::vector<db::CellInstArrayWithProperties> &inst, const db::ICplxTrans &for_var, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_table) const
|
||||
{
|
||||
for (std::vector<db::CellInstArrayWithProperties>::const_iterator i = inst.begin (); i != inst.end (); ++i) {
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::const_iterator f = var_table.find (i->object ().cell_index ());
|
||||
if (f == var_table.end ()) {
|
||||
|
||||
in_cell.insert (*i);
|
||||
|
||||
} else {
|
||||
|
||||
const std::map<db::ICplxTrans, db::cell_index_type> &vt = f->second;
|
||||
|
||||
for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) {
|
||||
|
||||
db::ICplxTrans rt = mp_red->reduce (for_var * i->complex_trans (*ia));
|
||||
std::map<db::ICplxTrans, db::cell_index_type>::const_iterator v = vt.find (rt);
|
||||
tl_assert (v != vt.end ());
|
||||
|
||||
in_cell.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (v->second), i->complex_trans (*ia)), i->properties_id ()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VariantsCollectorBase::create_var_instances_tl_invariant (db::Cell &in_cell, std::vector<db::CellInstArrayWithProperties> &inst, const db::ICplxTrans &for_var, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_table) const
|
||||
{
|
||||
for (std::vector<db::CellInstArrayWithProperties>::const_iterator i = inst.begin (); i != inst.end (); ++i) {
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::const_iterator f = var_table.find (i->object ().cell_index ());
|
||||
if (f == var_table.end ()) {
|
||||
|
||||
in_cell.insert (*i);
|
||||
|
||||
} else {
|
||||
|
||||
const std::map<db::ICplxTrans, db::cell_index_type> &vt = f->second;
|
||||
|
||||
std::map<db::ICplxTrans, db::cell_index_type>::const_iterator v;
|
||||
|
||||
db::ICplxTrans rt = mp_red->reduce (for_var * i->complex_trans ());
|
||||
v = vt.find (rt);
|
||||
tl_assert (v != vt.end ());
|
||||
|
||||
db::CellInstArrayWithProperties new_inst = *i;
|
||||
new_inst.object ().cell_index (v->second);
|
||||
in_cell.insert (new_inst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbCellVariants
|
||||
#define HDR_dbCellVariants
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbTrans.h"
|
||||
#include "dbLayout.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The reducer interface
|
||||
*
|
||||
* The transformation reducer is used by the variant builder to provide a
|
||||
* reduced version of the transformation. Variants are built based on this
|
||||
* reduced transformation.
|
||||
*
|
||||
* Reduction must satisfy the modulo condition:
|
||||
*
|
||||
* reduce(A*B) = reduce(reduce(A)*reduce(B))
|
||||
*/
|
||||
class DB_PUBLIC TransformationReducer
|
||||
{
|
||||
public:
|
||||
TransformationReducer () { }
|
||||
virtual ~TransformationReducer () { }
|
||||
|
||||
virtual db::Trans reduce (const db::Trans &trans) const = 0;
|
||||
virtual db::ICplxTrans reduce (const db::ICplxTrans &trans) const = 0;
|
||||
virtual bool is_translation_invariant () const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An orientation reducer
|
||||
*
|
||||
* This reducer incarnation reduces the transformation to it's rotation/mirror part.
|
||||
*/
|
||||
struct DB_PUBLIC OrientationReducer
|
||||
: public TransformationReducer
|
||||
{
|
||||
db::ICplxTrans reduce (const db::ICplxTrans &trans) const
|
||||
{
|
||||
db::ICplxTrans res (trans);
|
||||
res.disp (db::Vector ());
|
||||
res.mag (1.0);
|
||||
return res;
|
||||
}
|
||||
|
||||
db::Trans reduce (const db::Trans &trans) const
|
||||
{
|
||||
return db::Trans (trans.fp_trans ());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A magnification reducer
|
||||
*
|
||||
* This reducer incarnation reduces the transformation to it's scaling part.
|
||||
*/
|
||||
struct DB_PUBLIC MagnificationReducer
|
||||
: public TransformationReducer
|
||||
{
|
||||
db::ICplxTrans reduce (const db::ICplxTrans &trans) const
|
||||
{
|
||||
return db::ICplxTrans (trans.mag ());
|
||||
}
|
||||
|
||||
db::Trans reduce (const db::Trans &) const
|
||||
{
|
||||
return db::Trans ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A magnification and orientation reducer
|
||||
*
|
||||
* This reducer incarnation reduces the transformation to it's rotation/mirror/magnification part (2d matrix)
|
||||
*/
|
||||
struct DB_PUBLIC MagnificationAndOrientationReducer
|
||||
: public TransformationReducer
|
||||
{
|
||||
db::ICplxTrans reduce (const db::ICplxTrans &trans) const
|
||||
{
|
||||
db::ICplxTrans res (trans);
|
||||
res.disp (db::Vector ());
|
||||
return res;
|
||||
}
|
||||
|
||||
db::Trans reduce (const db::Trans &trans) const
|
||||
{
|
||||
return db::Trans (trans.fp_trans ());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A grid reducer
|
||||
*
|
||||
* This reducer incarnation reduces the transformation to it's displacement modulo a grid
|
||||
*/
|
||||
struct DB_PUBLIC GridReducer
|
||||
: public TransformationReducer
|
||||
{
|
||||
GridReducer (db::Coord grid)
|
||||
: m_grid (grid)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
db::ICplxTrans reduce (const db::ICplxTrans &trans) const
|
||||
{
|
||||
// NOTE: we need to keep magnification, angle and mirror so when combining the
|
||||
// reduced transformations, the result will be equivalent to reducing the combined
|
||||
// transformation.
|
||||
db::ICplxTrans res (trans);
|
||||
res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ())));
|
||||
return res;
|
||||
}
|
||||
|
||||
db::Trans reduce (const db::Trans &trans) const
|
||||
{
|
||||
db::Trans res (trans);
|
||||
res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ())));
|
||||
return res;
|
||||
}
|
||||
|
||||
bool is_translation_invariant () const { return false; }
|
||||
|
||||
private:
|
||||
db::Coord m_grid;
|
||||
|
||||
inline db::Coord mod (db::Coord c) const
|
||||
{
|
||||
if (c < 0) {
|
||||
c = m_grid - (-c) % m_grid;
|
||||
if (c == m_grid) {
|
||||
return 0;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
} else {
|
||||
return c % m_grid;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class computing variants for cells according to a given criterion
|
||||
*
|
||||
* The cell variants are build from the cell instances and are accumulated over
|
||||
* the hierarchy path.
|
||||
*/
|
||||
class DB_PUBLIC VariantsCollectorBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a variant collector without a transformation reducer
|
||||
*/
|
||||
VariantsCollectorBase ();
|
||||
|
||||
/**
|
||||
* @brief Creates a variant collector with the given reducer
|
||||
*/
|
||||
VariantsCollectorBase (const TransformationReducer *red);
|
||||
|
||||
/**
|
||||
* @brief Collects cell variants for the given layout starting from the top cell
|
||||
*/
|
||||
void collect (const db::Layout &layout, const db::Cell &top_cell);
|
||||
|
||||
/**
|
||||
* @brief Creates cell variants for singularization of the different variants
|
||||
*
|
||||
* After this method can been used, all cells with more than one variant are separated and
|
||||
* the corresponding instances are updated.
|
||||
*
|
||||
* If given, *var_table will be filled with a map giving the new cell and variant against
|
||||
* the old cell for all cells with more than one variant.
|
||||
*/
|
||||
void separate_variants (db::Layout &layout, db::Cell &top_cell, std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > *var_table = 0);
|
||||
|
||||
/**
|
||||
* @brief Commits the shapes for different variants to the current cell hierarchy
|
||||
*
|
||||
* This is an alternative approach and will push the variant shapes into the parent hierarchy.
|
||||
* "to_commit" initially is a set of shapes to commit for the given cell and variant.
|
||||
* This map is modified during the algorithm and should be discarded later.
|
||||
*/
|
||||
void commit_shapes (db::Layout &layout, db::Cell &top_cell, unsigned int layer, std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > &to_commit);
|
||||
|
||||
/**
|
||||
* @brief Gets the variants for a given cell
|
||||
*
|
||||
* The keys of the map are the variants, the values is the instance count of the variant
|
||||
* (as seen from the top cell).
|
||||
*/
|
||||
const std::map<db::ICplxTrans, size_t> &variants (db::cell_index_type ci) const;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if variants have been built
|
||||
*/
|
||||
bool has_variants () const;
|
||||
|
||||
private:
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, size_t> > m_variants;
|
||||
const TransformationReducer *mp_red;
|
||||
|
||||
void add_variant (std::map<db::ICplxTrans, size_t> &variants, const db::CellInstArray &inst, bool tl_invariant) const;
|
||||
void add_variant_non_tl_invariant (std::map<db::ICplxTrans, size_t> &variants, const db::CellInstArray &inst) const;
|
||||
void add_variant_tl_invariant (std::map<db::ICplxTrans, size_t> &variants, const db::CellInstArray &inst) const;
|
||||
void product (const std::map<db::ICplxTrans, size_t> &v1, const std::map<db::ICplxTrans, size_t> &v2, std::map<db::ICplxTrans, size_t> &prod) const;
|
||||
void copy_shapes (db::Layout &layout, db::cell_index_type ci_to, db::cell_index_type ci_from) const;
|
||||
void create_var_instances (db::Cell &in_cell, std::vector<db::CellInstArrayWithProperties> &inst, const db::ICplxTrans &for_var, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_table, bool tl_invariant) const;
|
||||
void create_var_instances_non_tl_invariant (db::Cell &in_cell, std::vector<db::CellInstArrayWithProperties> &inst, const db::ICplxTrans &for_var, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_table) const;
|
||||
void create_var_instances_tl_invariant (db::Cell &in_cell, std::vector<db::CellInstArrayWithProperties> &inst, const db::ICplxTrans &for_var, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_table) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A template using a specific transformation reducer
|
||||
*/
|
||||
template <class RED>
|
||||
class DB_PUBLIC_TEMPLATE cell_variants_collector
|
||||
: public VariantsCollectorBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a variant collector without a transformation reducer
|
||||
*/
|
||||
cell_variants_collector ()
|
||||
: VariantsCollectorBase (&m_red)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a variant collector with the given reducer
|
||||
*
|
||||
* The collector will take ownership over the reducer
|
||||
*/
|
||||
cell_variants_collector (const RED &red)
|
||||
: VariantsCollectorBase (&m_red), m_red (red)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
private:
|
||||
RED m_red;
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,578 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbCircuit.h"
|
||||
#include "dbNetlist.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Circuit class implementation
|
||||
|
||||
Circuit::Circuit ()
|
||||
: mp_netlist (0),
|
||||
m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices),
|
||||
m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
|
||||
m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets),
|
||||
m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices),
|
||||
m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
|
||||
m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets),
|
||||
m_index (0)
|
||||
{
|
||||
m_devices.changed ().add (this, &Circuit::devices_changed);
|
||||
m_nets.changed ().add (this, &Circuit::nets_changed);
|
||||
m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed);
|
||||
}
|
||||
|
||||
Circuit::Circuit (const Circuit &other)
|
||||
: gsi::ObjectBase (other), tl::Object (other), mp_netlist (0),
|
||||
m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices),
|
||||
m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
|
||||
m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets),
|
||||
m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices),
|
||||
m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
|
||||
m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets),
|
||||
m_index (0)
|
||||
{
|
||||
operator= (other);
|
||||
m_devices.changed ().add (this, &Circuit::devices_changed);
|
||||
m_nets.changed ().add (this, &Circuit::nets_changed);
|
||||
m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed);
|
||||
}
|
||||
|
||||
Circuit::~Circuit ()
|
||||
{
|
||||
m_devices.changed ().remove (this, &Circuit::devices_changed);
|
||||
m_nets.changed ().remove (this, &Circuit::nets_changed);
|
||||
m_subcircuits.changed ().remove (this, &Circuit::subcircuits_changed);
|
||||
|
||||
// the default destructor will make the nets access "this" to unregister the
|
||||
// objects - hence we have to do this explicitly.
|
||||
m_nets.clear ();
|
||||
m_subcircuits.clear ();
|
||||
m_devices.clear ();
|
||||
}
|
||||
|
||||
Circuit &Circuit::operator= (const Circuit &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
|
||||
clear ();
|
||||
|
||||
m_name = other.m_name;
|
||||
m_pins = other.m_pins;
|
||||
|
||||
std::map<const Device *, Device *> device_table;
|
||||
for (const_device_iterator i = other.begin_devices (); i != other.end_devices (); ++i) {
|
||||
Device *d = new Device (*i);
|
||||
device_table [i.operator-> ()] = d;
|
||||
add_device (d);
|
||||
}
|
||||
|
||||
std::map<const SubCircuit *, SubCircuit *> sc_table;
|
||||
for (const_subcircuit_iterator i = other.begin_subcircuits (); i != other.end_subcircuits (); ++i) {
|
||||
SubCircuit *sc = new SubCircuit (*i);
|
||||
sc_table [i.operator-> ()] = sc;
|
||||
add_subcircuit (sc);
|
||||
}
|
||||
|
||||
for (const_net_iterator i = other.begin_nets (); i != other.end_nets (); ++i) {
|
||||
|
||||
// translate the net
|
||||
Net *n = new Net ();
|
||||
n->set_cluster_id (i->cluster_id ());
|
||||
n->set_name (i->name ());
|
||||
add_net (n);
|
||||
|
||||
for (Net::const_terminal_iterator p = i->begin_terminals (); p != i->end_terminals (); ++p) {
|
||||
std::map<const Device *, Device *>::const_iterator m = device_table.find (p->device ());
|
||||
tl_assert (m != device_table.end ());
|
||||
n->add_terminal (NetTerminalRef (m->second, p->terminal_id ()));
|
||||
}
|
||||
|
||||
for (Net::const_pin_iterator p = i->begin_pins (); p != i->end_pins (); ++p) {
|
||||
n->add_pin (NetPinRef (p->pin_id ()));
|
||||
}
|
||||
|
||||
for (Net::const_subcircuit_pin_iterator p = i->begin_subcircuit_pins (); p != i->end_subcircuit_pins (); ++p) {
|
||||
std::map<const SubCircuit *, SubCircuit *>::const_iterator m = sc_table.find (p->subcircuit ());
|
||||
tl_assert (m != sc_table.end ());
|
||||
n->add_subcircuit_pin (NetSubcircuitPinRef (m->second, p->pin_id ()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Circuit::set_netlist (Netlist *netlist)
|
||||
{
|
||||
mp_netlist = netlist;
|
||||
}
|
||||
|
||||
const Pin *Circuit::pin_by_id (size_t id) const
|
||||
{
|
||||
if (id >= m_pins.size ()) {
|
||||
return 0;
|
||||
} else {
|
||||
return &m_pins [id];
|
||||
}
|
||||
}
|
||||
|
||||
const Pin *Circuit::pin_by_name (const std::string &name) const
|
||||
{
|
||||
for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) {
|
||||
if (p->name () == name) {
|
||||
return p.operator-> ();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Circuit::devices_changed ()
|
||||
{
|
||||
m_device_by_id.invalidate ();
|
||||
m_device_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::subcircuits_changed ()
|
||||
{
|
||||
m_subcircuit_by_id.invalidate ();
|
||||
m_subcircuit_by_name.invalidate ();
|
||||
|
||||
if (mp_netlist) {
|
||||
mp_netlist->invalidate_topology ();
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::nets_changed ()
|
||||
{
|
||||
m_net_by_cluster_id.invalidate ();
|
||||
m_net_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Circuit::clear ()
|
||||
{
|
||||
m_name.clear ();
|
||||
m_pins.clear ();
|
||||
m_devices.clear ();
|
||||
m_nets.clear ();
|
||||
m_subcircuits.clear ();
|
||||
}
|
||||
|
||||
void Circuit::set_name (const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
if (mp_netlist) {
|
||||
mp_netlist->m_circuit_by_name.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::set_cell_index (const db::cell_index_type ci)
|
||||
{
|
||||
m_cell_index = ci;
|
||||
if (mp_netlist) {
|
||||
mp_netlist->m_circuit_by_cell_index.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
Circuit::child_circuit_iterator Circuit::begin_children ()
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return mp_netlist->child_circuits (this).begin ();
|
||||
}
|
||||
|
||||
Circuit::child_circuit_iterator Circuit::end_children ()
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return mp_netlist->child_circuits (this).end ();
|
||||
}
|
||||
|
||||
Circuit::const_child_circuit_iterator Circuit::begin_children () const
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (mp_netlist->child_circuits (const_cast <Circuit *> (this))).begin ();
|
||||
}
|
||||
|
||||
Circuit::const_child_circuit_iterator Circuit::end_children () const
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (mp_netlist->child_circuits (const_cast <Circuit *> (this))).end ();
|
||||
}
|
||||
|
||||
Circuit::child_circuit_iterator Circuit::begin_parents ()
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return mp_netlist->parent_circuits (this).begin ();
|
||||
}
|
||||
|
||||
Circuit::child_circuit_iterator Circuit::end_parents ()
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return mp_netlist->parent_circuits (this).end ();
|
||||
}
|
||||
|
||||
Circuit::const_child_circuit_iterator Circuit::begin_parents () const
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (mp_netlist->parent_circuits (const_cast <Circuit *> (this))).begin ();
|
||||
}
|
||||
|
||||
Circuit::const_child_circuit_iterator Circuit::end_parents () const
|
||||
{
|
||||
tl_assert (mp_netlist != 0);
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (mp_netlist->parent_circuits (const_cast <Circuit *> (this))).end ();
|
||||
}
|
||||
|
||||
const Pin &Circuit::add_pin (const std::string &name)
|
||||
{
|
||||
m_pins.push_back (Pin (name));
|
||||
m_pins.back ().set_id (m_pins.size () - 1);
|
||||
return m_pins.back ();
|
||||
}
|
||||
|
||||
void Circuit::add_net (Net *net)
|
||||
{
|
||||
m_nets.push_back (net);
|
||||
net->set_circuit (this);
|
||||
}
|
||||
|
||||
void Circuit::remove_net (Net *net)
|
||||
{
|
||||
m_nets.erase (net);
|
||||
}
|
||||
|
||||
void Circuit::add_device (Device *device)
|
||||
{
|
||||
device->set_circuit (this);
|
||||
|
||||
size_t id = 0;
|
||||
if (! m_devices.empty ()) {
|
||||
tl_assert (m_devices.back () != 0);
|
||||
id = m_devices.back ()->id ();
|
||||
}
|
||||
device->set_id (id + 1);
|
||||
|
||||
m_devices.push_back (device);
|
||||
}
|
||||
|
||||
void Circuit::remove_device (Device *device)
|
||||
{
|
||||
m_devices.erase (device);
|
||||
}
|
||||
|
||||
void Circuit::add_subcircuit (SubCircuit *subcircuit)
|
||||
{
|
||||
subcircuit->set_circuit (this);
|
||||
|
||||
size_t id = 0;
|
||||
if (! m_subcircuits.empty ()) {
|
||||
tl_assert (m_subcircuits.back () != 0);
|
||||
id = m_subcircuits.back ()->id ();
|
||||
}
|
||||
subcircuit->set_id (id + 1);
|
||||
|
||||
m_subcircuits.push_back (subcircuit);
|
||||
}
|
||||
|
||||
void Circuit::remove_subcircuit (SubCircuit *subcircuit)
|
||||
{
|
||||
m_subcircuits.erase (subcircuit);
|
||||
}
|
||||
|
||||
void Circuit::register_ref (SubCircuit *r)
|
||||
{
|
||||
m_refs.push_back (r);
|
||||
}
|
||||
|
||||
void Circuit::unregister_ref (SubCircuit *r)
|
||||
{
|
||||
m_refs.erase (r);
|
||||
}
|
||||
|
||||
void Circuit::translate_circuits (const std::map<const Circuit *, Circuit *> &map)
|
||||
{
|
||||
for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) {
|
||||
std::map<const Circuit *, Circuit *>::const_iterator m = map.find (i->circuit_ref ());
|
||||
tl_assert (m != map.end ());
|
||||
i->set_circuit_ref (m->second);
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::translate_device_classes (const std::map<const DeviceClass *, DeviceClass *> &map)
|
||||
{
|
||||
for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) {
|
||||
std::map<const DeviceClass *, DeviceClass *>::const_iterator m = map.find (i->device_class ());
|
||||
tl_assert (m != map.end ());
|
||||
i->set_device_class (m->second);
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::translate_device_abstracts (const std::map<const DeviceAbstract *, DeviceAbstract *> &map)
|
||||
{
|
||||
for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) {
|
||||
if (i->device_abstract ()) {
|
||||
std::map<const DeviceAbstract *, DeviceAbstract *>::const_iterator m = map.find (i->device_abstract ());
|
||||
tl_assert (m != map.end ());
|
||||
i->set_device_abstract (m->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter)
|
||||
{
|
||||
if (m_pin_refs.size () < pin_id + 1) {
|
||||
m_pin_refs.resize (pin_id + 1, Net::pin_iterator ());
|
||||
}
|
||||
m_pin_refs [pin_id] = iter;
|
||||
}
|
||||
|
||||
const Net *Circuit::net_for_pin (size_t pin_id) const
|
||||
{
|
||||
if (pin_id < m_pin_refs.size ()) {
|
||||
Net::pin_iterator p = m_pin_refs [pin_id];
|
||||
if (p != Net::pin_iterator ()) {
|
||||
return p->net ();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Circuit::connect_pin (size_t pin_id, Net *net)
|
||||
{
|
||||
if (net_for_pin (pin_id) == net) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pin_id < m_pin_refs.size ()) {
|
||||
Net::pin_iterator p = m_pin_refs [pin_id];
|
||||
if (p != Net::pin_iterator () && p->net ()) {
|
||||
p->net ()->erase_pin (p);
|
||||
}
|
||||
m_pin_refs [pin_id] = Net::pin_iterator ();
|
||||
}
|
||||
|
||||
if (net) {
|
||||
net->add_pin (NetPinRef (pin_id));
|
||||
}
|
||||
}
|
||||
|
||||
void Circuit::purge_nets ()
|
||||
{
|
||||
std::vector<db::Net *> nets_to_be_purged;
|
||||
for (net_iterator n = begin_nets (); n != end_nets (); ++n) {
|
||||
if (n->is_floating ()) {
|
||||
nets_to_be_purged.push_back (n.operator-> ());
|
||||
}
|
||||
}
|
||||
for (std::vector<db::Net *>::const_iterator n = nets_to_be_purged.begin (); n != nets_to_be_purged.end (); ++n) {
|
||||
delete *n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sanity check for device to be removed
|
||||
*/
|
||||
static void check_device_before_remove (db::Circuit *c, const db::Device *d)
|
||||
{
|
||||
if (d->device_class () == 0) {
|
||||
throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ());
|
||||
}
|
||||
const std::vector<db::DeviceTerminalDefinition> &pd = d->device_class ()->terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
|
||||
if (d->net_for_terminal (p->id ()) != 0) {
|
||||
throw tl::Exception (tl::to_string (tr ("Internal error: Terminal still connected after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name () + ", terminal=" + p->name ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Circuit::combine_parallel_devices (const db::DeviceClass &cls)
|
||||
{
|
||||
typedef std::vector<const db::Net *> key_type;
|
||||
std::map<key_type, std::vector<db::Device *> > combination_candidates;
|
||||
|
||||
bool any = false;
|
||||
|
||||
// identify the candidates for combination - all devices sharing the same nets
|
||||
// are candidates for combination in parallel mode
|
||||
for (device_iterator d = begin_devices (); d != end_devices (); ++d) {
|
||||
|
||||
if (tl::id_of (d->device_class ()) != tl::id_of (&cls)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
key_type k;
|
||||
const std::vector<db::DeviceTerminalDefinition> &terminals = cls.terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator p = terminals.begin (); p != terminals.end (); ++p) {
|
||||
const db::Net *n = d->net_for_terminal (p->id ());
|
||||
if (n) {
|
||||
k.push_back (n);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (k.begin (), k.end ());
|
||||
k.erase (std::unique (k.begin (), k.end ()), k.end ());
|
||||
combination_candidates[k].push_back (d.operator-> ());
|
||||
|
||||
}
|
||||
|
||||
// actually combine the devices
|
||||
for (std::map<key_type, std::vector<db::Device *> >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) {
|
||||
|
||||
std::vector<db::Device *> &cl = cc->second;
|
||||
for (size_t i = 0; i < cl.size () - 1; ++i) {
|
||||
for (size_t j = i + 1; j < cl.size (); ) {
|
||||
if (cls.combine_devices (cl [i], cl [j])) {
|
||||
check_device_before_remove (this, cl [j]); // sanity check
|
||||
delete cl [j];
|
||||
cl.erase (cl.begin () + j);
|
||||
any = true;
|
||||
} else {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
static std::pair<db::Device *, db::Device *> attached_two_devices (db::Net &net, const db::DeviceClass &cls)
|
||||
{
|
||||
if (net.begin_pins () != net.end_pins ()) {
|
||||
return std::make_pair ((db::Device *) 0, (db::Device *) 0);
|
||||
}
|
||||
|
||||
db::Device *d1 = 0, *d2 = 0;
|
||||
|
||||
Net::terminal_iterator p = net.begin_terminals ();
|
||||
if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) {
|
||||
return std::make_pair ((db::Device *) 0, (db::Device *) 0);
|
||||
} else {
|
||||
d1 = p->device ();
|
||||
}
|
||||
|
||||
++p;
|
||||
if (p == net.end_terminals () || tl::id_of (p->device_class ()) != tl::id_of (&cls)) {
|
||||
return std::make_pair ((db::Device *) 0, (db::Device *) 0);
|
||||
} else {
|
||||
d2 = p->device ();
|
||||
}
|
||||
|
||||
++p;
|
||||
if (p != net.end_terminals () || d1 == d2 || !d1 || !d2) {
|
||||
return std::make_pair ((db::Device *) 0, (db::Device *) 0);
|
||||
} else {
|
||||
return std::make_pair (d1, d2);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static bool same_or_swapped (const std::pair<T, T> &p1, const std::pair<T, T> &p2)
|
||||
{
|
||||
return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first);
|
||||
}
|
||||
|
||||
bool Circuit::combine_serial_devices(const db::DeviceClass &cls)
|
||||
{
|
||||
bool any = false;
|
||||
|
||||
for (net_iterator n = begin_nets (); n != end_nets (); ++n) {
|
||||
|
||||
std::pair<db::Device *, db::Device *> dd = attached_two_devices (*n, cls);
|
||||
if (! dd.first) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The net is an internal node: the devices attached to this internal node are
|
||||
// combination candidates if the number of nets emerging from the attached device pair (not counting
|
||||
// the internal node we just found) does not exceed the number of pins available for the
|
||||
// new device.
|
||||
|
||||
std::vector<const db::Net *> other_nets;
|
||||
|
||||
const std::vector<db::DeviceTerminalDefinition> &terminals = cls.terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator p = terminals.begin (); p != terminals.end (); ++p) {
|
||||
db::Net *on;
|
||||
on = dd.first->net_for_terminal (p->id ());
|
||||
if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) {
|
||||
other_nets.push_back (on);
|
||||
}
|
||||
on = dd.second->net_for_terminal (p->id ());
|
||||
if (on && ! same_or_swapped (dd, attached_two_devices (*on, cls))) {
|
||||
other_nets.push_back (on);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (other_nets.begin (), other_nets.end ());
|
||||
other_nets.erase (std::unique (other_nets.begin (), other_nets.end ()), other_nets.end ());
|
||||
|
||||
if (other_nets.size () <= cls.terminal_definitions().size ()) {
|
||||
|
||||
// found a combination candidate
|
||||
if (cls.combine_devices (dd.first, dd.second)) {
|
||||
check_device_before_remove (this, dd.second); // sanity check
|
||||
delete dd.second;
|
||||
any = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
void Circuit::combine_devices ()
|
||||
{
|
||||
tl_assert (netlist () != 0);
|
||||
|
||||
for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) {
|
||||
|
||||
// repeat the combination step unless no combination happens - this is required to take care of combinations that arise after
|
||||
// other combinations have been realized.
|
||||
bool any = true;
|
||||
while (any) {
|
||||
|
||||
any = false;
|
||||
|
||||
if (dc->supports_parallel_combination ()) {
|
||||
if (combine_parallel_devices (*dc)) {
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
if (dc->supports_serial_combination ()) {
|
||||
if (combine_serial_devices (*dc)) {
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,635 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbCircuit
|
||||
#define _HDR_dbCircuit
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbTypes.h"
|
||||
#include "dbNet.h"
|
||||
#include "dbDevice.h"
|
||||
#include "dbPin.h"
|
||||
#include "dbSubCircuit.h"
|
||||
#include "dbNetlistUtils.h"
|
||||
|
||||
#include "tlObject.h"
|
||||
#include "tlObjectCollection.h"
|
||||
#include "tlVector.h"
|
||||
#include "gsiObject.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
|
||||
/**
|
||||
* @brief A circuit
|
||||
*
|
||||
* A circuit is a list of nets, of subcircuit references and actual
|
||||
* devices.
|
||||
*/
|
||||
class DB_PUBLIC Circuit
|
||||
: public gsi::ObjectBase, public tl::Object
|
||||
{
|
||||
public:
|
||||
typedef tl::vector<Pin> pin_list;
|
||||
typedef pin_list::const_iterator const_pin_iterator;
|
||||
typedef pin_list::iterator pin_iterator;
|
||||
typedef tl::shared_collection<Device> device_list;
|
||||
typedef device_list::const_iterator const_device_iterator;
|
||||
typedef device_list::iterator device_iterator;
|
||||
typedef tl::shared_collection<Net> net_list;
|
||||
typedef net_list::const_iterator const_net_iterator;
|
||||
typedef net_list::iterator net_iterator;
|
||||
typedef tl::shared_collection<SubCircuit> subcircuit_list;
|
||||
typedef subcircuit_list::const_iterator const_subcircuit_iterator;
|
||||
typedef subcircuit_list::iterator subcircuit_iterator;
|
||||
typedef tl::weak_collection<SubCircuit>::const_iterator const_refs_iterator;
|
||||
typedef tl::weak_collection<SubCircuit>::iterator refs_iterator;
|
||||
typedef tl::vector<Circuit *>::const_iterator child_circuit_iterator;
|
||||
typedef tl::vector<const Circuit *>::const_iterator const_child_circuit_iterator;
|
||||
typedef tl::vector<Circuit *>::const_iterator parent_circuit_iterator;
|
||||
typedef tl::vector<const Circuit *>::const_iterator const_parent_circuit_iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* Creates an empty circuit.
|
||||
*/
|
||||
Circuit ();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
Circuit (const Circuit &other);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~Circuit ();
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
Circuit &operator= (const Circuit &other);
|
||||
|
||||
/**
|
||||
* @brief Gets the netlist the circuit lives in
|
||||
* This pointer is 0 if the circuit is not part of a netlist.
|
||||
*/
|
||||
Netlist *netlist ()
|
||||
{
|
||||
return mp_netlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the netlist the circuit lives in (const version)
|
||||
* This pointer is 0 if the circuit is not part of a netlist.
|
||||
*/
|
||||
const Netlist *netlist () const
|
||||
{
|
||||
return mp_netlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the circuit
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Sets the name of the circuit
|
||||
*/
|
||||
void set_name (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the circuit
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The index of the circuit in the netlist
|
||||
* CAUTION: this attribute is used for internal purposes and may not be valid always.
|
||||
*/
|
||||
size_t index () const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the layout cell reference for this circuit
|
||||
*
|
||||
* The layout cell reference links a circuit to a layout cell.
|
||||
*/
|
||||
void set_cell_index (const db::cell_index_type ci);
|
||||
|
||||
/**
|
||||
* @brief Gets the layout cell index
|
||||
*/
|
||||
db::cell_index_type cell_index () const
|
||||
{
|
||||
return m_cell_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the references to this circuit (begin, non-const version)
|
||||
* This iterator will deliver all subcircuits referencing this circuit
|
||||
*/
|
||||
refs_iterator begin_refs ()
|
||||
{
|
||||
return m_refs.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the references to this circuit (end, non-const version)
|
||||
* This iterator will deliver all subcircuits referencing this circuit
|
||||
*/
|
||||
refs_iterator end_refs ()
|
||||
{
|
||||
return m_refs.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the references to this circuit (begin, const version)
|
||||
* This iterator will deliver all subcircuits referencing this circuit
|
||||
*/
|
||||
const_refs_iterator begin_refs () const
|
||||
{
|
||||
return m_refs.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the child circuits iterator (begin)
|
||||
* The child circuits are the circuits referenced by all subcircuits
|
||||
* in the circuit.
|
||||
*/
|
||||
child_circuit_iterator begin_children ();
|
||||
|
||||
/**
|
||||
* @brief Gets the child circuits iterator (end)
|
||||
*/
|
||||
child_circuit_iterator end_children ();
|
||||
|
||||
/**
|
||||
* @brief Gets the child circuits iterator (begin, const version)
|
||||
* The child circuits are the circuits referenced by all subcircuits
|
||||
* in the circuit.
|
||||
*/
|
||||
const_child_circuit_iterator begin_children () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the child circuits iterator (end, const version)
|
||||
*/
|
||||
const_child_circuit_iterator end_children () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the parent circuits iterator (begin)
|
||||
* The parents circuits are the circuits referencing this circuit via subcircuits.
|
||||
*/
|
||||
parent_circuit_iterator begin_parents ();
|
||||
|
||||
/**
|
||||
* @brief Gets the parent circuits iterator (end)
|
||||
*/
|
||||
parent_circuit_iterator end_parents ();
|
||||
|
||||
/**
|
||||
* @brief Gets the parent circuits iterator (begin, const version)
|
||||
* The parents circuits are the circuits referencing this circuit via subcircuits.
|
||||
*/
|
||||
const_parent_circuit_iterator begin_parents () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the parent circuits iterator (end, const version)
|
||||
*/
|
||||
const_parent_circuit_iterator end_parents () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the references to this circuit (end, const version)
|
||||
* This iterator will deliver all subcircuits referencing this circuit
|
||||
*/
|
||||
const_refs_iterator end_refs () const
|
||||
{
|
||||
return m_refs.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a pin to this circuit
|
||||
* The circuit takes over ownership of the object.
|
||||
*/
|
||||
const Pin &add_pin(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the pins of the circuit (non-const version)
|
||||
*/
|
||||
pin_iterator begin_pins ()
|
||||
{
|
||||
return m_pins.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the pins of the circuit (non-const version)
|
||||
*/
|
||||
pin_iterator end_pins ()
|
||||
{
|
||||
return m_pins.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the pin count
|
||||
*/
|
||||
size_t pin_count () const
|
||||
{
|
||||
return m_pins.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the pin by ID (the ID is basically the index)
|
||||
*/
|
||||
const Pin *pin_by_id (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the pin by name
|
||||
*
|
||||
* If there is no pin with that name, null is returned.
|
||||
* NOTE: this is a linear search, so it's performance may not be good for many pins.
|
||||
*/
|
||||
const Pin *pin_by_name (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the pins of the circuit (const version)
|
||||
*/
|
||||
const_pin_iterator begin_pins () const
|
||||
{
|
||||
return m_pins.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the pins of the circuit (const version)
|
||||
*/
|
||||
const_pin_iterator end_pins () const
|
||||
{
|
||||
return m_pins.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a net to this circuit
|
||||
*
|
||||
* The circuit takes over ownership of the object.
|
||||
*/
|
||||
void add_net (Net *net);
|
||||
|
||||
/**
|
||||
* @brief Deletes a net from the circuit
|
||||
*/
|
||||
void remove_net (Net *net);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the nets of the circuit (non-const version)
|
||||
*/
|
||||
net_iterator begin_nets ()
|
||||
{
|
||||
return m_nets.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the nets of the circuit (non-const version)
|
||||
*/
|
||||
net_iterator end_nets ()
|
||||
{
|
||||
return m_nets.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the nets of the circuit (const version)
|
||||
*/
|
||||
const_net_iterator begin_nets () const
|
||||
{
|
||||
return m_nets.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the nets of the circuit (const version)
|
||||
*/
|
||||
const_net_iterator end_nets () const
|
||||
{
|
||||
return m_nets.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net from a given cluster ID (const version)
|
||||
*
|
||||
* If the cluster ID is not valid, null is returned.
|
||||
*/
|
||||
const Net *net_by_cluster_id (size_t cluster_id) const
|
||||
{
|
||||
return (const_cast<Circuit *> (this)->net_by_cluster_id (cluster_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net from a given cluster ID (non-const version)
|
||||
*
|
||||
* If the cluster ID is not valid, null is returned.
|
||||
*/
|
||||
Net *net_by_cluster_id (size_t cluster_id)
|
||||
{
|
||||
return m_net_by_cluster_id.object_by (cluster_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net from a given name (const version)
|
||||
*
|
||||
* If the name is not valid, null is returned.
|
||||
*/
|
||||
const Net *net_by_name (const std::string &name) const
|
||||
{
|
||||
return (const_cast<Circuit *> (this)->net_by_name (name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net from a given name (non-const version)
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a device to this circuit
|
||||
*
|
||||
* The circuit takes over ownership of the object.
|
||||
*/
|
||||
void add_device (Device *device);
|
||||
|
||||
/**
|
||||
* @brief Deletes a device from the circuit
|
||||
*/
|
||||
void remove_device (Device *device);
|
||||
|
||||
/**
|
||||
* @brief Gets the device from a given ID (const version)
|
||||
*
|
||||
* If the ID is not valid, null is returned.
|
||||
*/
|
||||
const Device *device_by_id (size_t id) const
|
||||
{
|
||||
return (const_cast<Circuit *> (this)->device_by_id (id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device from a given ID (non-const version)
|
||||
*
|
||||
* If the ID is not valid, null is returned.
|
||||
*/
|
||||
Device *device_by_id (size_t id)
|
||||
{
|
||||
return m_device_by_id.object_by (id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device from a given name (const version)
|
||||
*
|
||||
* If the name is not valid, null is returned.
|
||||
*/
|
||||
const Device *device_by_name (const std::string &name) const
|
||||
{
|
||||
return (const_cast<Circuit *> (this)->device_by_name (name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device from a given name (non-const version)
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the devices of the circuit (non-const version)
|
||||
*/
|
||||
device_iterator begin_devices ()
|
||||
{
|
||||
return m_devices.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the devices of the circuit (non-const version)
|
||||
*/
|
||||
device_iterator end_devices ()
|
||||
{
|
||||
return m_devices.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the devices of the circuit (const version)
|
||||
*/
|
||||
const_device_iterator begin_devices () const
|
||||
{
|
||||
return m_devices.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the devices of the circuit (const version)
|
||||
*/
|
||||
const_device_iterator end_devices () const
|
||||
{
|
||||
return m_devices.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a subcircuit to this circuit
|
||||
*
|
||||
* The circuit takes over ownership of the object.
|
||||
*/
|
||||
void add_subcircuit (SubCircuit *subcircuit);
|
||||
|
||||
/**
|
||||
* @brief Deletes a subcircuit from the circuit
|
||||
*/
|
||||
void remove_subcircuit (SubCircuit *subcircuit);
|
||||
|
||||
/**
|
||||
* @brief Gets the subcircuit from a given ID (const version)
|
||||
*
|
||||
* If the ID is not valid, null is returned.
|
||||
*/
|
||||
const SubCircuit *subcircuit_by_id (size_t id) const
|
||||
{
|
||||
return (const_cast<Circuit *> (this)->subcircuit_by_id (id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the subcircuit from a given ID (non-const version)
|
||||
*
|
||||
* If the ID is not valid, null is returned.
|
||||
*/
|
||||
SubCircuit *subcircuit_by_id (size_t id)
|
||||
{
|
||||
return m_subcircuit_by_id.object_by (id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the subcircuit from a given name (const version)
|
||||
*
|
||||
* If the name is not valid, null is returned.
|
||||
*/
|
||||
const SubCircuit *subcircuit_by_name (const std::string &name) const
|
||||
{
|
||||
return (const_cast<Circuit *> (this)->subcircuit_by_name (name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the subcircuit from a given name (non-const version)
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the subcircuits of the circuit (non-const version)
|
||||
*/
|
||||
subcircuit_iterator begin_subcircuits ()
|
||||
{
|
||||
return m_subcircuits.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the subcircuits of the circuit (non-const version)
|
||||
*/
|
||||
subcircuit_iterator end_subcircuits ()
|
||||
{
|
||||
return m_subcircuits.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the subcircuits of the circuit (const version)
|
||||
*/
|
||||
const_subcircuit_iterator begin_subcircuits () const
|
||||
{
|
||||
return m_subcircuits.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the subcircuits of the circuit (const version)
|
||||
*/
|
||||
const_subcircuit_iterator end_subcircuits () const
|
||||
{
|
||||
return m_subcircuits.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the connected net for a pin with the given id
|
||||
*
|
||||
* Returns 0 if the pin is not connected to a net.
|
||||
*/
|
||||
const Net *net_for_pin (size_t pin_id) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the connected net for a pin with the given id (non-const version)
|
||||
*
|
||||
* Returns 0 if the pin is not connected to a net.
|
||||
*/
|
||||
Net *net_for_pin (size_t pin_id)
|
||||
{
|
||||
return const_cast<Net *> (((const Circuit *) this)->net_for_pin (pin_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects the given pin to the given net
|
||||
* If the net is 0 the pin is disconnected.
|
||||
* If non-null, a NetPinRef object will be inserted into the
|
||||
* net and connected with the given pin.
|
||||
*/
|
||||
void connect_pin (size_t pin_id, Net *net);
|
||||
|
||||
/**
|
||||
* @brief Purge unused nets
|
||||
*
|
||||
* This method will purge all nets which return "floating".
|
||||
*/
|
||||
void purge_nets ();
|
||||
|
||||
/**
|
||||
* @brief Combine devices
|
||||
*
|
||||
* This method will combine devices that can be combined according
|
||||
* to their device classes "combine_devices" method.
|
||||
*/
|
||||
void combine_devices ();
|
||||
|
||||
private:
|
||||
friend class Netlist;
|
||||
friend class Net;
|
||||
friend class SubCircuit;
|
||||
friend class Device;
|
||||
|
||||
std::string m_name;
|
||||
db::cell_index_type m_cell_index;
|
||||
net_list m_nets;
|
||||
pin_list m_pins;
|
||||
device_list m_devices;
|
||||
subcircuit_list m_subcircuits;
|
||||
Netlist *mp_netlist;
|
||||
std::vector<Net::pin_iterator> m_pin_refs;
|
||||
object_by_attr<Circuit, Circuit::device_iterator, id_attribute<Device> > m_device_by_id;
|
||||
object_by_attr<Circuit, Circuit::subcircuit_iterator, id_attribute<SubCircuit> > m_subcircuit_by_id;
|
||||
object_by_attr<Circuit, Circuit::net_iterator, cluster_id_attribute<Net> > m_net_by_cluster_id;
|
||||
object_by_attr<Circuit, Circuit::device_iterator, name_attribute<Device> > m_device_by_name;
|
||||
object_by_attr<Circuit, Circuit::subcircuit_iterator, name_attribute<SubCircuit> > m_subcircuit_by_name;
|
||||
object_by_attr<Circuit, Circuit::net_iterator, name_attribute<Net> > m_net_by_name;
|
||||
tl::weak_collection<SubCircuit> m_refs;
|
||||
size_t m_index;
|
||||
|
||||
void set_index (size_t index)
|
||||
{
|
||||
m_index = index;
|
||||
}
|
||||
|
||||
void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter);
|
||||
|
||||
void register_ref (SubCircuit *sc);
|
||||
void unregister_ref (SubCircuit *sc);
|
||||
|
||||
void translate_circuits (const std::map<const Circuit *, Circuit *> &map);
|
||||
void translate_device_classes (const std::map<const DeviceClass *, DeviceClass *> &map);
|
||||
void translate_device_abstracts (const std::map<const DeviceAbstract *, DeviceAbstract *> &map);
|
||||
void set_netlist (Netlist *netlist);
|
||||
bool combine_parallel_devices (const db::DeviceClass &cls);
|
||||
bool combine_serial_devices (const db::DeviceClass &cls);
|
||||
|
||||
void devices_changed ();
|
||||
void subcircuits_changed ();
|
||||
void nets_changed ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbDeepEdgePairs.h"
|
||||
#include "dbCellGraphUtils.h"
|
||||
#include "dbDeepEdges.h"
|
||||
#include "dbDeepRegion.h"
|
||||
#include "dbCellMapping.h"
|
||||
#include "dbLayoutUtils.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief An iterator delegate for the deep edge pair collection
|
||||
* TODO: this is kind of redundant with OriginalLayerIterator ..
|
||||
*/
|
||||
class DB_PUBLIC DeepEdgePairsIterator
|
||||
: public EdgePairsIteratorDelegate
|
||||
{
|
||||
public:
|
||||
DeepEdgePairsIterator (const db::RecursiveShapeIterator &iter)
|
||||
: m_iter (iter)
|
||||
{
|
||||
set ();
|
||||
}
|
||||
|
||||
virtual ~DeepEdgePairsIterator () { }
|
||||
|
||||
virtual bool at_end () const
|
||||
{
|
||||
return m_iter.at_end ();
|
||||
}
|
||||
|
||||
virtual void increment ()
|
||||
{
|
||||
++m_iter;
|
||||
set ();
|
||||
}
|
||||
|
||||
virtual const value_type *get () const
|
||||
{
|
||||
return &m_edge_pair;
|
||||
}
|
||||
|
||||
virtual EdgePairsIteratorDelegate *clone () const
|
||||
{
|
||||
return new DeepEdgePairsIterator (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class EdgePairs;
|
||||
|
||||
db::RecursiveShapeIterator m_iter;
|
||||
mutable value_type m_edge_pair;
|
||||
|
||||
void set () const {
|
||||
if (! m_iter.at_end ()) {
|
||||
m_iter.shape ().edge_pair (m_edge_pair);
|
||||
m_edge_pair.transform (m_iter.trans ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
DeepEdgePairs::DeepEdgePairs ()
|
||||
: AsIfFlatEdgePairs (), m_deep_layer ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeepEdgePairs::DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss)
|
||||
: AsIfFlatEdgePairs (), m_deep_layer (dss.create_edge_pair_layer (si))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeepEdgePairs::DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans)
|
||||
: AsIfFlatEdgePairs (), m_deep_layer (dss.create_edge_pair_layer (si, trans))
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeepEdgePairs::DeepEdgePairs (const DeepEdgePairs &other)
|
||||
: AsIfFlatEdgePairs (other),
|
||||
m_deep_layer (other.m_deep_layer.copy ())
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeepEdgePairs::DeepEdgePairs (const DeepLayer &dl)
|
||||
: AsIfFlatEdgePairs (), m_deep_layer (dl)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeepEdgePairs::~DeepEdgePairs ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EdgePairsDelegate *DeepEdgePairs::clone () const
|
||||
{
|
||||
return new DeepEdgePairs (*this);
|
||||
}
|
||||
|
||||
EdgePairsIteratorDelegate *DeepEdgePairs::begin () const
|
||||
{
|
||||
return new DeepEdgePairsIterator (begin_iter ().first);
|
||||
}
|
||||
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> DeepEdgePairs::begin_iter () const
|
||||
{
|
||||
const db::Layout &layout = m_deep_layer.layout ();
|
||||
if (layout.cells () == 0) {
|
||||
|
||||
return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ());
|
||||
|
||||
} else {
|
||||
|
||||
const db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
|
||||
db::RecursiveShapeIterator iter (m_deep_layer.layout (), top_cell, m_deep_layer.layer ());
|
||||
return std::make_pair (iter, db::ICplxTrans ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
size_t DeepEdgePairs::size () const
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
const db::Layout &layout = m_deep_layer.layout ();
|
||||
db::CellCounter cc (&layout);
|
||||
for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) {
|
||||
n += cc.weight (*c) * layout.cell (*c).shapes (m_deep_layer.layer ()).size ();
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
std::string DeepEdgePairs::to_string (size_t nmax) const
|
||||
{
|
||||
return db::AsIfFlatEdgePairs::to_string (nmax);
|
||||
}
|
||||
|
||||
Box DeepEdgePairs::bbox () const
|
||||
{
|
||||
return m_deep_layer.initial_cell ().bbox (m_deep_layer.layer ());
|
||||
}
|
||||
|
||||
bool DeepEdgePairs::empty () const
|
||||
{
|
||||
return begin_iter ().first.at_end ();
|
||||
}
|
||||
|
||||
const db::EdgePair *DeepEdgePairs::nth (size_t) const
|
||||
{
|
||||
throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections")));
|
||||
}
|
||||
|
||||
bool DeepEdgePairs::has_valid_edge_pairs () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const db::RecursiveShapeIterator *DeepEdgePairs::iter () const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
DeepEdgePairs::add_in_place (const EdgePairs &other)
|
||||
{
|
||||
if (other.empty ()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const DeepEdgePairs *other_deep = dynamic_cast <const DeepEdgePairs *> (other.delegate ());
|
||||
if (other_deep) {
|
||||
|
||||
deep_layer ().add_from (other_deep->deep_layer ());
|
||||
|
||||
} else {
|
||||
|
||||
// non-deep to deep merge (flat)
|
||||
|
||||
db::Shapes &shapes = deep_layer ().initial_cell ().shapes (deep_layer ().layer ());
|
||||
for (db::EdgePairs::const_iterator p = other.begin (); ! p.at_end (); ++p) {
|
||||
shapes.insert (*p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
EdgePairsDelegate *DeepEdgePairs::add (const EdgePairs &other) const
|
||||
{
|
||||
if (other.empty ()) {
|
||||
return clone ();
|
||||
} else if (empty ()) {
|
||||
return other.delegate ()->clone ();
|
||||
} else {
|
||||
DeepEdgePairs *new_edge_pairs = dynamic_cast<DeepEdgePairs *> (clone ());
|
||||
new_edge_pairs->add_in_place (other);
|
||||
return new_edge_pairs;
|
||||
}
|
||||
}
|
||||
|
||||
EdgePairsDelegate *DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
|
||||
{
|
||||
// TODO: implement
|
||||
return AsIfFlatEdgePairs::filter_in_place (filter);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const
|
||||
{
|
||||
// TODO: implement
|
||||
return AsIfFlatEdgePairs::filtered (filter);
|
||||
}
|
||||
|
||||
RegionDelegate *DeepEdgePairs::polygons (db::Coord e) const
|
||||
{
|
||||
db::DeepLayer new_layer = m_deep_layer.derived ();
|
||||
db::Layout &layout = const_cast<db::Layout &> (m_deep_layer.layout ());
|
||||
|
||||
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
|
||||
db::Shapes &output = c->shapes (new_layer.layer ());
|
||||
for (db::Shapes::shape_iterator s = c->shapes (m_deep_layer.layer ()).begin (db::ShapeIterator::EdgePairs); ! s.at_end (); ++s) {
|
||||
db::Polygon poly = s->edge_pair ().normalized ().to_polygon (e);
|
||||
if (poly.vertices () >= 3) {
|
||||
output.insert (poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new db::DeepRegion (new_layer);
|
||||
}
|
||||
|
||||
EdgesDelegate *DeepEdgePairs::generic_edges (bool first, bool second) const
|
||||
{
|
||||
db::DeepLayer new_layer = m_deep_layer.derived ();
|
||||
db::Layout &layout = const_cast<db::Layout &> (m_deep_layer.layout ());
|
||||
|
||||
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
|
||||
db::Shapes &output = c->shapes (new_layer.layer ());
|
||||
for (db::Shapes::shape_iterator s = c->shapes (m_deep_layer.layer ()).begin (db::ShapeIterator::EdgePairs); ! s.at_end (); ++s) {
|
||||
db::EdgePair ep = s->edge_pair ();
|
||||
if (first) {
|
||||
output.insert (ep.first ());
|
||||
}
|
||||
if (second) {
|
||||
output.insert (ep.second ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new db::DeepEdges (new_layer);
|
||||
}
|
||||
|
||||
EdgesDelegate *DeepEdgePairs::edges () const
|
||||
{
|
||||
return generic_edges (true, true);
|
||||
}
|
||||
|
||||
EdgesDelegate *DeepEdgePairs::first_edges () const
|
||||
{
|
||||
return generic_edges (true, false);
|
||||
}
|
||||
|
||||
EdgesDelegate *DeepEdgePairs::second_edges () const
|
||||
{
|
||||
return generic_edges (false, true);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *DeepEdgePairs::in (const EdgePairs &other, bool invert) const
|
||||
{
|
||||
// TODO: implement
|
||||
return AsIfFlatEdgePairs::in (other, invert);
|
||||
}
|
||||
|
||||
bool DeepEdgePairs::equals (const EdgePairs &other) const
|
||||
{
|
||||
const DeepEdgePairs *other_delegate = dynamic_cast<const DeepEdgePairs *> (other.delegate ());
|
||||
if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout ()
|
||||
&& other_delegate->m_deep_layer.layer () == m_deep_layer.layer ()) {
|
||||
return true;
|
||||
} else {
|
||||
return AsIfFlatEdgePairs::equals (other);
|
||||
}
|
||||
}
|
||||
|
||||
bool DeepEdgePairs::less (const EdgePairs &other) const
|
||||
{
|
||||
const DeepEdgePairs *other_delegate = dynamic_cast<const DeepEdgePairs *> (other.delegate ());
|
||||
if (other_delegate && &other_delegate->m_deep_layer.layout () == &m_deep_layer.layout ()) {
|
||||
return other_delegate->m_deep_layer.layer () < m_deep_layer.layer ();
|
||||
} else {
|
||||
return AsIfFlatEdgePairs::less (other);
|
||||
}
|
||||
}
|
||||
|
||||
void DeepEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
m_deep_layer.insert_into (layout, into_cell, into_layer);
|
||||
}
|
||||
|
||||
void DeepEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const
|
||||
{
|
||||
m_deep_layer.insert_into_as_polygons (layout, into_cell, into_layer, enl);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbDeepEdgePairs
|
||||
#define HDR_dbDeepEdgePairs
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbAsIfFlatEdgePairs.h"
|
||||
#include "dbDeepShapeStore.h"
|
||||
#include "dbEdgePairs.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief Provides hierarchical edges implementation
|
||||
*/
|
||||
class DB_PUBLIC DeepEdgePairs
|
||||
: public db::AsIfFlatEdgePairs
|
||||
{
|
||||
public:
|
||||
DeepEdgePairs ();
|
||||
DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss);
|
||||
DeepEdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans);
|
||||
|
||||
DeepEdgePairs (const DeepEdgePairs &other);
|
||||
DeepEdgePairs (const DeepLayer &dl);
|
||||
|
||||
virtual ~DeepEdgePairs ();
|
||||
|
||||
EdgePairsDelegate *clone () const;
|
||||
|
||||
virtual EdgePairsIteratorDelegate *begin () const;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
|
||||
|
||||
virtual size_t size () const;
|
||||
virtual std::string to_string (size_t) const;
|
||||
virtual Box bbox () const;
|
||||
virtual bool empty () const;
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
||||
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter);
|
||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const;
|
||||
|
||||
virtual EdgePairsDelegate *add_in_place (const EdgePairs &other);
|
||||
virtual EdgePairsDelegate *add (const EdgePairs &other) const;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||
virtual EdgesDelegate *edges () const;
|
||||
virtual EdgesDelegate *first_edges () const;
|
||||
virtual EdgesDelegate *second_edges () const;
|
||||
|
||||
virtual EdgePairsDelegate *in (const EdgePairs &, bool) const;
|
||||
|
||||
virtual bool equals (const EdgePairs &other) const;
|
||||
virtual bool less (const EdgePairs &other) const;
|
||||
|
||||
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;
|
||||
|
||||
const DeepLayer &deep_layer () const
|
||||
{
|
||||
return m_deep_layer;
|
||||
}
|
||||
|
||||
DeepLayer &deep_layer ()
|
||||
{
|
||||
return m_deep_layer;
|
||||
}
|
||||
|
||||
private:
|
||||
DeepEdgePairs &operator= (const DeepEdgePairs &other);
|
||||
|
||||
DeepLayer m_deep_layer;
|
||||
|
||||
void init ();
|
||||
EdgesDelegate *generic_edges (bool first, bool second) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,185 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbDeepEdges
|
||||
#define HDR_dbDeepEdges
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbAsIfFlatEdges.h"
|
||||
#include "dbDeepShapeStore.h"
|
||||
#include "dbEdgePairs.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
class Edges;
|
||||
class DeepRegion;
|
||||
|
||||
/**
|
||||
* @brief Provides hierarchical edges implementation
|
||||
*/
|
||||
class DB_PUBLIC DeepEdges
|
||||
: public db::AsIfFlatEdges
|
||||
{
|
||||
public:
|
||||
DeepEdges ();
|
||||
DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, bool as_edges = true);
|
||||
DeepEdges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true);
|
||||
|
||||
DeepEdges (const DeepEdges &other);
|
||||
DeepEdges (const DeepLayer &dl);
|
||||
|
||||
virtual ~DeepEdges ();
|
||||
|
||||
EdgesDelegate *clone () const;
|
||||
|
||||
virtual EdgesIteratorDelegate *begin () const;
|
||||
virtual EdgesIteratorDelegate *begin_merged () const;
|
||||
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const;
|
||||
|
||||
virtual bool empty () const;
|
||||
virtual bool is_merged () const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
||||
virtual bool equals (const Edges &other) const;
|
||||
virtual bool less (const Edges &other) const;
|
||||
|
||||
virtual size_t size () const;
|
||||
virtual Box bbox () const;
|
||||
|
||||
virtual DeepEdges::length_type length (const db::Box &) const;
|
||||
|
||||
virtual std::string to_string (size_t nmax) const;
|
||||
|
||||
virtual EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::WidthRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::OverlapRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::WidthRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::InsideRelation, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter);
|
||||
virtual EdgesDelegate *filtered (const EdgeFilterBase &) const;
|
||||
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &);
|
||||
virtual EdgesDelegate *processed (const EdgeProcessorBase &) const;
|
||||
virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const;
|
||||
virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &filter) const;
|
||||
|
||||
virtual EdgesDelegate *merged_in_place ();
|
||||
virtual EdgesDelegate *merged () const;
|
||||
|
||||
virtual EdgesDelegate *and_with (const Edges &other) const;
|
||||
virtual EdgesDelegate *and_with (const Region &other) const;
|
||||
|
||||
virtual EdgesDelegate *not_with (const Edges &other) const;
|
||||
virtual EdgesDelegate *not_with (const Region &other) const;
|
||||
|
||||
virtual EdgesDelegate *xor_with (const Edges &other) const;
|
||||
|
||||
virtual EdgesDelegate *or_with (const Edges &other) const;
|
||||
|
||||
virtual EdgesDelegate *add_in_place (const Edges &other);
|
||||
virtual EdgesDelegate *add (const Edges &other) const;
|
||||
|
||||
virtual EdgesDelegate *inside_part (const Region &other) const;
|
||||
virtual EdgesDelegate *outside_part (const Region &other) const;
|
||||
|
||||
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const;
|
||||
|
||||
virtual EdgesDelegate *selected_interacting (const Edges &) const;
|
||||
virtual EdgesDelegate *selected_not_interacting (const Edges &) const;
|
||||
virtual EdgesDelegate *selected_interacting (const Region &) const;
|
||||
virtual EdgesDelegate *selected_not_interacting (const Region &) const;
|
||||
|
||||
virtual EdgesDelegate *in (const Edges &, bool) const;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
|
||||
|
||||
const DeepLayer &deep_layer () const
|
||||
{
|
||||
return m_deep_layer;
|
||||
}
|
||||
|
||||
DeepLayer &deep_layer ()
|
||||
{
|
||||
return m_deep_layer;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void merged_semantics_changed ();
|
||||
void set_is_merged (bool f);
|
||||
|
||||
private:
|
||||
friend class DeepRegion;
|
||||
|
||||
DeepEdges &operator= (const DeepEdges &other);
|
||||
|
||||
DeepLayer m_deep_layer;
|
||||
mutable DeepLayer m_merged_edges;
|
||||
mutable bool m_merged_edges_valid;
|
||||
bool m_is_merged;
|
||||
|
||||
void init ();
|
||||
void ensure_merged_edges_valid () const;
|
||||
DeepLayer and_or_not_with(const DeepEdges *other, bool and_op) const;
|
||||
DeepLayer edge_region_op (const DeepRegion *other, bool outside, bool include_borders) const;
|
||||
EdgePairsDelegate *run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const;
|
||||
EdgesDelegate *selected_interacting_generic (const Edges &edges, bool invert) const;
|
||||
EdgesDelegate *selected_interacting_generic (const Region ®ion, bool invert) const;
|
||||
template <class Result, class OutputContainer> OutputContainer *processed_impl (const edge_processor<Result> &filter) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,254 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbDeepRegion
|
||||
#define HDR_dbDeepRegion
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbAsIfFlatRegion.h"
|
||||
#include "dbDeepShapeStore.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief A deep, polygon-set delegate
|
||||
*/
|
||||
class DB_PUBLIC DeepRegion
|
||||
: public AsIfFlatRegion
|
||||
{
|
||||
public:
|
||||
typedef db::layer<db::Polygon, db::unstable_layer_tag> polygon_layer_type;
|
||||
typedef polygon_layer_type::iterator polygon_iterator_type;
|
||||
|
||||
DeepRegion ();
|
||||
DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio = 0.0, size_t max_vertex_count = 0);
|
||||
DeepRegion (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 0.0, size_t max_vertex_count = 0);
|
||||
|
||||
DeepRegion (const DeepRegion &other);
|
||||
DeepRegion (const DeepLayer &dl);
|
||||
|
||||
virtual ~DeepRegion ();
|
||||
|
||||
RegionDelegate *clone () const;
|
||||
|
||||
virtual RegionIteratorDelegate *begin () const;
|
||||
virtual RegionIteratorDelegate *begin_merged () const;
|
||||
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const;
|
||||
|
||||
virtual bool empty () const;
|
||||
virtual bool is_merged () const;
|
||||
|
||||
virtual const db::Polygon *nth (size_t n) const;
|
||||
virtual bool has_valid_polygons () const;
|
||||
virtual bool has_valid_merged_polygons () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
||||
virtual bool equals (const Region &other) const;
|
||||
virtual bool less (const Region &other) const;
|
||||
|
||||
virtual bool is_box () const;
|
||||
virtual size_t size () const;
|
||||
|
||||
virtual area_type area (const db::Box &box) const;
|
||||
virtual perimeter_type perimeter (const db::Box &box) const;
|
||||
virtual Box bbox () const;
|
||||
|
||||
virtual std::string to_string (size_t nmax) const;
|
||||
|
||||
virtual RegionDelegate *and_with (const Region &other) const;
|
||||
virtual RegionDelegate *not_with (const Region &other) const;
|
||||
virtual RegionDelegate *xor_with (const Region &other) const;
|
||||
|
||||
virtual RegionDelegate *add_in_place (const Region &other);
|
||||
virtual RegionDelegate *add (const Region &other) const;
|
||||
|
||||
EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const
|
||||
{
|
||||
return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection);
|
||||
}
|
||||
|
||||
virtual EdgePairsDelegate *grid_check (db::Coord gx, db::Coord gy) const;
|
||||
virtual EdgePairsDelegate *angle_check (double min, double max, bool inverse) const;
|
||||
|
||||
virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy)
|
||||
{
|
||||
return snapped (gx, gy);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy);
|
||||
|
||||
virtual EdgesDelegate *edges (const EdgeFilterBase *) const;
|
||||
|
||||
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter);
|
||||
virtual RegionDelegate *processed (const PolygonProcessorBase &filter) const;
|
||||
virtual EdgesDelegate *processed_to_edges (const PolygonToEdgeProcessorBase &filter) const;
|
||||
virtual EdgePairsDelegate *processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &filter) const;
|
||||
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter);
|
||||
virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const;
|
||||
|
||||
virtual RegionDelegate *merged_in_place ();
|
||||
virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc);
|
||||
|
||||
virtual RegionDelegate *merged () const;
|
||||
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const;
|
||||
|
||||
virtual RegionDelegate *sized (coord_type d, unsigned int mode) const;
|
||||
virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const;
|
||||
|
||||
virtual RegionDelegate *selected_outside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 1, false, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_outside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 1, false, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_inside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, -1, true, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_inside (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, -1, true, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_interacting (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, true, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_interacting (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, true, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_interacting (const Edges &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_interacting (const Edges &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_overlapping (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, false, false);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *selected_not_overlapping (const Region &other) const
|
||||
{
|
||||
return selected_interacting_generic (other, 0, false, true);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *in (const Region &other, bool invert) const;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
|
||||
|
||||
const DeepLayer &deep_layer () const
|
||||
{
|
||||
return m_deep_layer;
|
||||
}
|
||||
|
||||
DeepLayer &deep_layer ()
|
||||
{
|
||||
return m_deep_layer;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void merged_semantics_changed ();
|
||||
void set_is_merged (bool f);
|
||||
|
||||
private:
|
||||
friend class DeepEdges;
|
||||
|
||||
DeepRegion &operator= (const DeepRegion &other);
|
||||
|
||||
DeepLayer m_deep_layer;
|
||||
mutable DeepLayer m_merged_polygons;
|
||||
mutable bool m_merged_polygons_valid;
|
||||
bool m_is_merged;
|
||||
|
||||
void init ();
|
||||
void ensure_merged_polygons_valid () const;
|
||||
DeepLayer and_or_not_with(const DeepRegion *other, bool and_op) const;
|
||||
EdgePairsDelegate *run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const;
|
||||
EdgePairsDelegate *run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const;
|
||||
RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const;
|
||||
RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const;
|
||||
|
||||
const DeepLayer &merged_deep_layer () const
|
||||
{
|
||||
return m_merged_polygons;
|
||||
}
|
||||
|
||||
template <class Result, class OutputContainer> OutputContainer *processed_impl (const polygon_processor<Result> &filter) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,902 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbDeepShapeStore.h"
|
||||
#include "dbCellMapping.h"
|
||||
#include "dbLayoutUtils.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbDeepRegion.h"
|
||||
|
||||
#include "tlTimer.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
DeepLayer::DeepLayer ()
|
||||
: mp_store (), m_layout (0), m_layer (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeepLayer::DeepLayer (const Region ®ion)
|
||||
: mp_store (), m_layout (0), m_layer (0)
|
||||
{
|
||||
const db::DeepRegion *dr = dynamic_cast<db::DeepRegion *> (region.delegate ());
|
||||
tl_assert (dr != 0);
|
||||
*this = dr->deep_layer ();
|
||||
}
|
||||
|
||||
DeepLayer::DeepLayer (const DeepLayer &x)
|
||||
: mp_store (x.mp_store), m_layout (x.m_layout), m_layer (x.m_layer)
|
||||
{
|
||||
if (mp_store.get ()) {
|
||||
mp_store->add_ref (m_layout, m_layer);
|
||||
}
|
||||
}
|
||||
|
||||
DeepLayer::DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer)
|
||||
: mp_store (store), m_layout (layout), m_layer (layer)
|
||||
{
|
||||
if (store) {
|
||||
store->add_ref (layout, layer);
|
||||
}
|
||||
}
|
||||
|
||||
DeepLayer &DeepLayer::operator= (const DeepLayer &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
if (mp_store.get ()) {
|
||||
mp_store->remove_ref (m_layout, m_layer);
|
||||
}
|
||||
mp_store = other.mp_store;
|
||||
m_layout = other.m_layout;
|
||||
m_layer = other.m_layer;
|
||||
if (mp_store.get ()) {
|
||||
mp_store->add_ref (m_layout, m_layer);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DeepLayer::~DeepLayer ()
|
||||
{
|
||||
if (mp_store.get ()) {
|
||||
mp_store->remove_ref (m_layout, m_layer);
|
||||
}
|
||||
}
|
||||
|
||||
DeepLayer
|
||||
DeepLayer::derived () const
|
||||
{
|
||||
return DeepLayer (const_cast <db::DeepShapeStore *> (mp_store.get ()), m_layout, const_cast <db::Layout &> (layout ()).insert_layer ());
|
||||
}
|
||||
|
||||
DeepLayer
|
||||
DeepLayer::copy () const
|
||||
{
|
||||
DeepLayer new_layer (derived ());
|
||||
|
||||
db::DeepShapeStore *non_const_store = const_cast<db::DeepShapeStore *> (mp_store.get ());
|
||||
non_const_store->layout (m_layout).copy_layer (m_layer, new_layer.layer ());
|
||||
|
||||
return new_layer;
|
||||
}
|
||||
|
||||
void
|
||||
DeepLayer::add_from (const DeepLayer &dl)
|
||||
{
|
||||
if (&dl.layout () == &layout ()) {
|
||||
|
||||
// intra-layout merge
|
||||
|
||||
layout ().copy_layer (dl.layer (), layer ());
|
||||
|
||||
} else {
|
||||
|
||||
// inter-layout merge
|
||||
|
||||
db::cell_index_type into_cell = initial_cell ().cell_index ();
|
||||
db::Layout *into_layout = &layout ();
|
||||
db::cell_index_type source_cell = dl.initial_cell ().cell_index ();
|
||||
const db::Layout *source_layout = &dl.layout ();
|
||||
|
||||
db::CellMapping cm;
|
||||
cm.create_from_geometry_full (*into_layout, into_cell, *source_layout, source_cell);
|
||||
|
||||
// Actually copy the shapes
|
||||
|
||||
std::map<unsigned int, unsigned int> lm;
|
||||
lm.insert (std::make_pair (dl.layer (), layer ()));
|
||||
|
||||
std::vector <db::cell_index_type> source_cells;
|
||||
source_cells.push_back (source_cell);
|
||||
db::copy_shapes (*into_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeepLayer::insert_into (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
check_dss ();
|
||||
const_cast<db::DeepShapeStore *> (mp_store.get ())->insert (*this, into_layout, into_cell, into_layer);
|
||||
}
|
||||
|
||||
void
|
||||
DeepLayer::insert_into_as_polygons (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const
|
||||
{
|
||||
check_dss ();
|
||||
const_cast<db::DeepShapeStore *> (mp_store.get ())->insert_as_polygons (*this, into_layout, into_cell, into_layer, enl);
|
||||
}
|
||||
|
||||
bool DeepLayer::operator< (const DeepLayer &other) const
|
||||
{
|
||||
if (mp_store.get () != other.mp_store.get ()) {
|
||||
return mp_store.get () < other.mp_store.get ();
|
||||
}
|
||||
if (m_layout != other.m_layout) {
|
||||
return m_layout < other.m_layout;
|
||||
}
|
||||
if (m_layer != other.m_layer) {
|
||||
return m_layer < other.m_layer;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeepLayer::operator== (const DeepLayer &other) const
|
||||
{
|
||||
if (mp_store.get () != other.mp_store.get ()) {
|
||||
return false;
|
||||
}
|
||||
if (m_layout != other.m_layout) {
|
||||
return false;
|
||||
}
|
||||
if (m_layer != other.m_layer) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
db::Layout &
|
||||
DeepLayer::layout ()
|
||||
{
|
||||
check_dss ();
|
||||
return mp_store->layout (m_layout);
|
||||
}
|
||||
|
||||
const db::Layout &
|
||||
DeepLayer::layout () const
|
||||
{
|
||||
check_dss ();
|
||||
return const_cast<db::DeepShapeStore *> (mp_store.get ())->layout (m_layout);
|
||||
}
|
||||
|
||||
db::Cell &
|
||||
DeepLayer::initial_cell ()
|
||||
{
|
||||
check_dss ();
|
||||
return mp_store->initial_cell (m_layout);
|
||||
}
|
||||
|
||||
const db::Cell &
|
||||
DeepLayer::initial_cell () const
|
||||
{
|
||||
check_dss ();
|
||||
return mp_store->const_initial_cell (m_layout);
|
||||
}
|
||||
|
||||
void
|
||||
DeepLayer::check_dss () const
|
||||
{
|
||||
if (mp_store.get () == 0) {
|
||||
throw tl::Exception (tl::to_string (tr ("Heap lost: the DeepShapeStore container no longer exists")));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
struct DeepShapeStore::LayoutHolder
|
||||
{
|
||||
LayoutHolder (const db::ICplxTrans &trans)
|
||||
: refs (0), layout (false), builder (&layout, trans), m_empty_layer (std::numeric_limits<unsigned int>::max ())
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
unsigned int empty_layer () const
|
||||
{
|
||||
if (m_empty_layer == std::numeric_limits<unsigned int>::max ()) {
|
||||
const_cast<LayoutHolder *> (this)->make_empty_layer ();
|
||||
}
|
||||
return m_empty_layer;
|
||||
}
|
||||
|
||||
void add_layer_ref (unsigned int layer)
|
||||
{
|
||||
layer_refs [layer] += 1;
|
||||
}
|
||||
|
||||
bool remove_layer_ref (unsigned int layer)
|
||||
{
|
||||
if ((layer_refs[layer] -= 1) <= 0) {
|
||||
layout.delete_layer (layer);
|
||||
layer_refs.erase (layer);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int refs;
|
||||
db::Layout layout;
|
||||
db::HierarchyBuilder builder;
|
||||
std::map<unsigned int, int> layer_refs;
|
||||
|
||||
private:
|
||||
unsigned int m_empty_layer;
|
||||
|
||||
void make_empty_layer ()
|
||||
{
|
||||
m_empty_layer = layout.insert_layer ();
|
||||
layer_refs [m_empty_layer] += 1; // the empty layer is not deleted
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
static size_t s_instance_count = 0;
|
||||
|
||||
DeepShapeStore::DeepShapeStore ()
|
||||
: m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_text_property_name (), m_text_enlargement (-1)
|
||||
{
|
||||
++s_instance_count;
|
||||
}
|
||||
|
||||
DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu)
|
||||
: m_threads (1), m_max_area_ratio (3.0), m_max_vertex_count (16), m_text_property_name (), m_text_enlargement (-1)
|
||||
{
|
||||
++s_instance_count;
|
||||
|
||||
m_layouts.push_back (new LayoutHolder (db::ICplxTrans ()));
|
||||
m_layouts.back ()->layout.dbu (dbu);
|
||||
m_layouts.back ()->layout.add_cell (topcell_name.c_str ());
|
||||
}
|
||||
|
||||
DeepShapeStore::~DeepShapeStore ()
|
||||
{
|
||||
--s_instance_count;
|
||||
|
||||
for (std::vector<LayoutHolder *>::iterator h = m_layouts.begin (); h != m_layouts.end (); ++h) {
|
||||
delete *h;
|
||||
}
|
||||
m_layouts.clear ();
|
||||
}
|
||||
|
||||
DeepLayer DeepShapeStore::create_from_flat (const db::Region ®ion, bool for_netlist, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans)
|
||||
{
|
||||
// reuse existing layer
|
||||
std::pair<bool, DeepLayer> lff = layer_for_flat (region);
|
||||
if (lff.first) {
|
||||
return lff.second;
|
||||
}
|
||||
|
||||
require_singular ();
|
||||
|
||||
unsigned int layer = layout ().insert_layer ();
|
||||
|
||||
if (max_area_ratio == 0.0) {
|
||||
max_area_ratio = m_max_area_ratio;
|
||||
}
|
||||
if (max_vertex_count == 0) {
|
||||
max_vertex_count = m_max_vertex_count;
|
||||
}
|
||||
|
||||
db::Shapes *shapes = &initial_cell ().shapes (layer);
|
||||
db::Box world = db::Box::world ();
|
||||
|
||||
// The chain of operators for producing clipped and reduced polygon references
|
||||
db::PolygonReferenceHierarchyBuilderShapeReceiver refs (&layout (), m_text_enlargement, m_text_property_name);
|
||||
db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count);
|
||||
|
||||
// try to maintain the texts on top level - go through shape iterator
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> ii = region.begin_iter ();
|
||||
db::ICplxTrans ttop = trans * ii.second;
|
||||
while (! ii.first.at_end ()) {
|
||||
|
||||
if (for_netlist && ii.first->is_text () && ii.first.layout () && ii.first.cell () != ii.first.top_cell ()) {
|
||||
// Skip texts on levels below top cell. For the reasoning see the description of this method.
|
||||
} else {
|
||||
red.push (*ii.first, ttop * ii.first.trans (), world, 0, shapes);
|
||||
}
|
||||
|
||||
++ii.first;
|
||||
|
||||
}
|
||||
|
||||
DeepLayer dl (this, 0 /*singular layout index*/, layer);
|
||||
m_layers_for_flat [tl::id_of (region.delegate ())] = std::make_pair (dl.layout_index (), dl.layer ());
|
||||
m_flat_region_id [std::make_pair (dl.layout_index (), dl.layer ())] = tl::id_of (region.delegate ());
|
||||
return dl;
|
||||
}
|
||||
|
||||
std::pair<bool, DeepLayer> DeepShapeStore::layer_for_flat (const db::Region ®ion) const
|
||||
{
|
||||
return layer_for_flat (tl::id_of (region.delegate ()));
|
||||
}
|
||||
|
||||
std::pair<bool, DeepLayer> DeepShapeStore::layer_for_flat (size_t region_id) const
|
||||
{
|
||||
std::map<size_t, std::pair<unsigned int, unsigned int> >::const_iterator lff = m_layers_for_flat.find (region_id);
|
||||
if (lff == m_layers_for_flat.end ()) {
|
||||
return std::make_pair (false, DeepLayer ());
|
||||
} else {
|
||||
return std::make_pair (true, DeepLayer (const_cast<DeepShapeStore *> (this), lff->second.first, lff->second.second));
|
||||
}
|
||||
}
|
||||
|
||||
bool DeepShapeStore::is_singular () const
|
||||
{
|
||||
return m_layouts.size () == 1;
|
||||
}
|
||||
|
||||
void DeepShapeStore::require_singular () const
|
||||
{
|
||||
if (! is_singular ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Internal error: deep shape store isn't singular. This may happen if you try to mix hierarchical layers from different sources our you use clipping.")));
|
||||
}
|
||||
}
|
||||
|
||||
Cell &DeepShapeStore::initial_cell(unsigned int n)
|
||||
{
|
||||
db::Layout &ly = layout (n);
|
||||
tl_assert (ly.cells () > 0);
|
||||
return ly.cell (*ly.begin_top_down ());
|
||||
}
|
||||
|
||||
const db::Cell &DeepShapeStore::const_initial_cell (unsigned int n) const
|
||||
{
|
||||
const db::Layout &ly = const_layout (n);
|
||||
tl_assert (ly.cells () > 0);
|
||||
return ly.cell (*ly.begin_top_down ());
|
||||
}
|
||||
|
||||
void DeepShapeStore::set_text_enlargement (int enl)
|
||||
{
|
||||
m_text_enlargement = enl;
|
||||
}
|
||||
|
||||
void DeepShapeStore::set_text_property_name (const tl::Variant &pn)
|
||||
{
|
||||
m_text_property_name = pn;
|
||||
}
|
||||
|
||||
bool DeepShapeStore::is_valid_layout_index (unsigned int n) const
|
||||
{
|
||||
return (n < (unsigned int) m_layouts.size () && m_layouts[n] != 0);
|
||||
}
|
||||
|
||||
const db::Layout &DeepShapeStore::const_layout (unsigned int n) const
|
||||
{
|
||||
tl_assert (is_valid_layout_index (n));
|
||||
return m_layouts [n]->layout;
|
||||
}
|
||||
|
||||
db::Layout &DeepShapeStore::layout (unsigned int n)
|
||||
{
|
||||
tl_assert (is_valid_layout_index (n));
|
||||
return m_layouts [n]->layout;
|
||||
}
|
||||
|
||||
size_t DeepShapeStore::instance_count ()
|
||||
{
|
||||
return s_instance_count;
|
||||
}
|
||||
|
||||
void DeepShapeStore::set_threads (int n)
|
||||
{
|
||||
m_threads = n;
|
||||
}
|
||||
|
||||
void DeepShapeStore::set_max_area_ratio (double ar)
|
||||
{
|
||||
m_max_area_ratio = ar;
|
||||
}
|
||||
|
||||
void DeepShapeStore::set_max_vertex_count (size_t n)
|
||||
{
|
||||
m_max_vertex_count = n;
|
||||
}
|
||||
|
||||
void DeepShapeStore::add_ref (unsigned int layout, unsigned int layer)
|
||||
{
|
||||
tl::MutexLocker locker (&m_lock);
|
||||
|
||||
tl_assert (layout < (unsigned int) m_layouts.size () && m_layouts[layout] != 0);
|
||||
|
||||
m_layouts[layout]->refs += 1;
|
||||
m_layouts[layout]->add_layer_ref (layer);
|
||||
}
|
||||
|
||||
void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer)
|
||||
{
|
||||
tl::MutexLocker locker (&m_lock);
|
||||
|
||||
tl_assert (layout < (unsigned int) m_layouts.size () && m_layouts[layout] != 0);
|
||||
|
||||
if (m_layouts[layout]->remove_layer_ref (layer)) {
|
||||
|
||||
// remove from flat region cross ref if required
|
||||
std::map<std::pair<unsigned int, unsigned int>, size_t>::iterator fri = m_flat_region_id.find (std::make_pair (layout, layer));
|
||||
if (fri != m_flat_region_id.end ()) {
|
||||
m_layers_for_flat.erase (fri->second);
|
||||
m_flat_region_id.erase (fri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((m_layouts[layout]->refs -= 1) <= 0) {
|
||||
delete m_layouts[layout];
|
||||
m_layouts[layout] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int
|
||||
DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans)
|
||||
{
|
||||
layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, trans));
|
||||
if (l == m_layout_map.end () || m_layouts[l->second] == 0) {
|
||||
|
||||
unsigned int layout_index;
|
||||
|
||||
if (l != m_layout_map.end ()) {
|
||||
// reuse discarded entry
|
||||
layout_index = l->second;
|
||||
m_layouts[layout_index] = new LayoutHolder (trans);
|
||||
} else {
|
||||
layout_index = (unsigned int) m_layouts.size ();
|
||||
m_layouts.push_back (new LayoutHolder (trans));
|
||||
}
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
|
||||
if (si.layout ()) {
|
||||
layout.dbu (si.layout ()->dbu () / trans.mag ());
|
||||
}
|
||||
|
||||
m_layout_map[std::make_pair (si, trans)] = layout_index;
|
||||
return layout_index;
|
||||
|
||||
} else {
|
||||
return l->second;
|
||||
}
|
||||
}
|
||||
|
||||
void DeepShapeStore::make_layout (unsigned int layout_index, const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans)
|
||||
{
|
||||
tl_assert (m_layout_map.find (std::make_pair (si, trans)) == m_layout_map.end ());
|
||||
|
||||
while (m_layouts.size () <= layout_index) {
|
||||
m_layouts.push_back (0);
|
||||
}
|
||||
|
||||
m_layouts[layout_index] = new LayoutHolder (trans);
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
|
||||
if (si.layout ()) {
|
||||
layout.dbu (si.layout ()->dbu () / trans.mag ());
|
||||
}
|
||||
|
||||
m_layout_map[std::make_pair (si, trans)] = layout_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) {
|
||||
max_area_ratio = m_max_area_ratio;
|
||||
}
|
||||
if (max_vertex_count == 0) {
|
||||
max_vertex_count = m_max_vertex_count;
|
||||
}
|
||||
|
||||
unsigned int layout_index = layout_for_iter (si, trans);
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
|
||||
|
||||
unsigned int layer_index = layout.insert_layer ();
|
||||
builder.set_target_layer (layer_index);
|
||||
|
||||
// The chain of operators for producing clipped and reduced polygon references
|
||||
db::PolygonReferenceHierarchyBuilderShapeReceiver refs (& layout, m_text_enlargement, m_text_property_name);
|
||||
db::ReducingHierarchyBuilderShapeReceiver red (&refs, max_area_ratio, max_vertex_count);
|
||||
db::ClippingHierarchyBuilderShapeReceiver clip (&red);
|
||||
|
||||
// Build the working hierarchy from the recursive shape iterator
|
||||
try {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
|
||||
|
||||
builder.set_shape_receiver (&clip);
|
||||
db::RecursiveShapeIterator (si).push (& builder);
|
||||
builder.set_shape_receiver (0);
|
||||
|
||||
} catch (...) {
|
||||
builder.set_shape_receiver (0);
|
||||
throw;
|
||||
}
|
||||
|
||||
return DeepLayer (this, layout_index, layer_index);
|
||||
}
|
||||
|
||||
DeepLayer DeepShapeStore::empty_layer (unsigned int layout_index) const
|
||||
{
|
||||
return DeepLayer (const_cast<DeepShapeStore *> (this), layout_index, m_layouts[layout_index]->empty_layer ());
|
||||
}
|
||||
|
||||
DeepLayer DeepShapeStore::empty_layer () const
|
||||
{
|
||||
require_singular ();
|
||||
return empty_layer (0);
|
||||
}
|
||||
|
||||
DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const db::ICplxTrans &trans)
|
||||
{
|
||||
unsigned int layout_index = layout_for_iter (si, trans);
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
|
||||
|
||||
unsigned int layer_index = layout.insert_layer ();
|
||||
builder.set_target_layer (layer_index);
|
||||
|
||||
// Build the working hierarchy from the recursive shape iterator
|
||||
try {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
|
||||
|
||||
builder.set_shape_receiver (pipe);
|
||||
db::RecursiveShapeIterator (si).push (& builder);
|
||||
builder.set_shape_receiver (0);
|
||||
|
||||
} catch (...) {
|
||||
builder.set_shape_receiver (0);
|
||||
throw;
|
||||
}
|
||||
|
||||
return DeepLayer (this, layout_index, layer_index);
|
||||
}
|
||||
|
||||
DeepLayer DeepShapeStore::create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe)
|
||||
{
|
||||
tl_assert (source.store () == this);
|
||||
|
||||
unsigned int from_layer_index = source.layer ();
|
||||
db::Layout &ly = layout ();
|
||||
|
||||
unsigned int layer_index = ly.insert_layer ();
|
||||
|
||||
// Build the working hierarchy from the recursive shape iterator
|
||||
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
|
||||
|
||||
db::Box region = db::Box::world ();
|
||||
db::ICplxTrans trans;
|
||||
|
||||
for (db::Layout::iterator c = ly.begin (); c != ly.end (); ++c) {
|
||||
db::Shapes &into = c->shapes (layer_index);
|
||||
const db::Shapes &from = c->shapes (from_layer_index);
|
||||
for (db::Shapes::shape_iterator s = from.begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
|
||||
pipe->push (*s, trans, region, 0, &into);
|
||||
}
|
||||
}
|
||||
|
||||
return DeepLayer (this, source.layout_index (), layer_index);
|
||||
}
|
||||
|
||||
DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const db::ICplxTrans &trans)
|
||||
{
|
||||
unsigned int layout_index = layout_for_iter (si, trans);
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
|
||||
|
||||
unsigned int layer_index = layout.insert_layer ();
|
||||
builder.set_target_layer (layer_index);
|
||||
|
||||
// The chain of operators for producing edges
|
||||
db::EdgeBuildingHierarchyBuilderShapeReceiver refs (as_edges);
|
||||
|
||||
// Build the working hierarchy from the recursive shape iterator
|
||||
try {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
|
||||
|
||||
builder.set_shape_receiver (&refs);
|
||||
db::RecursiveShapeIterator (si).push (& builder);
|
||||
builder.set_shape_receiver (0);
|
||||
|
||||
} catch (...) {
|
||||
builder.set_shape_receiver (0);
|
||||
throw;
|
||||
}
|
||||
|
||||
return DeepLayer (this, layout_index, layer_index);
|
||||
}
|
||||
|
||||
DeepLayer DeepShapeStore::create_edge_pair_layer (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans)
|
||||
{
|
||||
unsigned int layout_index = layout_for_iter (si, trans);
|
||||
|
||||
db::Layout &layout = m_layouts[layout_index]->layout;
|
||||
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
|
||||
|
||||
unsigned int layer_index = layout.insert_layer ();
|
||||
builder.set_target_layer (layer_index);
|
||||
|
||||
// The chain of operators for producing the edge pairs
|
||||
db::EdgePairBuildingHierarchyBuilderShapeReceiver refs;
|
||||
|
||||
// Build the working hierarchy from the recursive shape iterator
|
||||
try {
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
|
||||
|
||||
builder.set_shape_receiver (&refs);
|
||||
db::RecursiveShapeIterator (si).push (& builder);
|
||||
builder.set_shape_receiver (0);
|
||||
|
||||
} catch (...) {
|
||||
builder.set_shape_receiver (0);
|
||||
throw;
|
||||
}
|
||||
|
||||
return DeepLayer (this, layout_index, layer_index);
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::invalidate_hier ()
|
||||
{
|
||||
m_delivery_mapping_cache.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_map)
|
||||
{
|
||||
invalidate_hier ();
|
||||
|
||||
db::HierarchyBuilder &builder = m_layouts [layout_index]->builder;
|
||||
for (std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::const_iterator i = var_map.begin (); i != var_map.end (); ++i) {
|
||||
for (std::map<db::ICplxTrans, db::cell_index_type>::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
builder.register_variant (i->first, j->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const db::CellMapping &
|
||||
DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells)
|
||||
{
|
||||
const db::Layout *source_layout = &m_layouts [layout_index]->layout;
|
||||
if (source_layout->begin_top_down () == source_layout->end_top_cells ()) {
|
||||
// empty source - nothing to do.
|
||||
static db::CellMapping cm;
|
||||
return cm;
|
||||
}
|
||||
|
||||
db::cell_index_type source_top = *source_layout->begin_top_down();
|
||||
|
||||
db::HierarchyBuilder &original_builder = m_layouts [layout_index]->builder;
|
||||
|
||||
// Derive a cell mapping for source to target. We reuse any existing mapping for returning the
|
||||
// shapes into the original layout.
|
||||
|
||||
DeliveryMappingCacheKey key (layout_index, tl::id_of (into_layout), into_cell);
|
||||
|
||||
std::map<DeliveryMappingCacheKey, db::CellMapping>::iterator cm = m_delivery_mapping_cache.find (key);
|
||||
if (cm == m_delivery_mapping_cache.end ()) {
|
||||
|
||||
cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first;
|
||||
|
||||
if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell ()) {
|
||||
|
||||
// This is the case of mapping back to the original. In this case we can use the information
|
||||
// provided inside the original hierarchy builders. They list the source cells and the target cells
|
||||
// create from them. We need to consider however, that the hierarchy builder is allowed to create
|
||||
// variants which we cannot map.
|
||||
|
||||
for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ++m) {
|
||||
|
||||
HierarchyBuilder::cell_map_type::const_iterator mm = m;
|
||||
++mm;
|
||||
bool skip = original_builder.is_variant (m->second); // skip variant cells
|
||||
while (mm != original_builder.end_cell_map () && mm->first.first == m->first.first && ! skip) {
|
||||
// we have cell variants and cannot simply map
|
||||
++mm;
|
||||
++m;
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (! skip) {
|
||||
cm->second.map (m->second, m->first.first);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (into_layout->cells () == 1) {
|
||||
|
||||
// Another simple case is mapping into an empty (or single-top-cell-only) layout, where we can use "create_from_single_full".
|
||||
cm->second.create_single_mapping (*into_layout, into_cell, *source_layout, source_top);
|
||||
|
||||
} else {
|
||||
|
||||
cm->second.create_from_geometry (*into_layout, into_cell, *source_layout, source_top);
|
||||
|
||||
}
|
||||
|
||||
// Add new cells for the variants and (possible) devices which are cells added during the device
|
||||
// extraction process
|
||||
cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top, excluded_cells);
|
||||
|
||||
}
|
||||
|
||||
return cm->second;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class DeepShapeStoreToShapeTransformer
|
||||
: public ShapesTransformer
|
||||
{
|
||||
public:
|
||||
DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout)
|
||||
: mp_layout (& layout)
|
||||
{
|
||||
// gets the text annotation property ID -
|
||||
// this is how the texts are passed for annotating the net names
|
||||
m_text_annot_name_id = std::pair<bool, db::property_names_id_type> (false, 0);
|
||||
if (! dss.text_property_name ().is_nil ()) {
|
||||
m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ());
|
||||
}
|
||||
}
|
||||
|
||||
void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const
|
||||
{
|
||||
if (! m_text_annot_name_id.first) {
|
||||
|
||||
// fast shortcut
|
||||
into.insert_transformed (from, trans, pm);
|
||||
|
||||
} else {
|
||||
|
||||
for (db::Shapes::shape_iterator i = from.begin (db::ShapeIterator::All); ! i.at_end (); ++i) {
|
||||
|
||||
bool is_text = false;
|
||||
|
||||
if (i->prop_id () > 0) {
|
||||
|
||||
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (i->prop_id ());
|
||||
|
||||
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) {
|
||||
if (j->first == m_text_annot_name_id.second) {
|
||||
|
||||
db::Text text (j->second.to_string (), db::Trans (i->bbox ().center () - db::Point ()));
|
||||
text.transform (trans);
|
||||
if (into.layout ()) {
|
||||
into.insert (db::TextRef (text, into.layout ()->shape_repository ()));
|
||||
} else {
|
||||
into.insert (text);
|
||||
}
|
||||
|
||||
is_text = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! is_text) {
|
||||
into.insert (*i, trans, pm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<bool, db::property_names_id_type> m_text_annot_name_id;
|
||||
const db::Layout *mp_layout;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer)
|
||||
{
|
||||
db::LayoutLocker locker (into_layout);
|
||||
|
||||
const db::Layout &source_layout = deep_layer.layout ();
|
||||
if (source_layout.begin_top_down () == source_layout.end_top_cells ()) {
|
||||
// empty source - nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare the transformation
|
||||
db::ICplxTrans trans (source_layout.dbu () / into_layout->dbu ());
|
||||
|
||||
// prepare a layer map
|
||||
std::map<unsigned int, unsigned int> lm;
|
||||
lm.insert (std::make_pair (deep_layer.layer (), into_layer));
|
||||
|
||||
// prepare a cell mapping
|
||||
const db::CellMapping &cm = cell_mapping_to_original (deep_layer.layout_index (), into_layout, into_cell);
|
||||
|
||||
// prepare a vector with the source cells
|
||||
std::vector <db::cell_index_type> source_cells;
|
||||
source_cells.push_back (*source_layout.begin_top_down());
|
||||
|
||||
// prepare a transformer to convert text-annotated markers back to texts (without transformation however)
|
||||
DeepShapeStoreToShapeTransformer dsst (*this, source_layout);
|
||||
|
||||
// actually copy the shapes
|
||||
db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm, &dsst);
|
||||
}
|
||||
|
||||
void
|
||||
DeepShapeStore::insert_as_polygons (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl)
|
||||
{
|
||||
// prepare a temporary layer with the polygons
|
||||
DeepLayer tmp = deep_layer.derived ();
|
||||
|
||||
db::Layout &layout = const_cast<db::Layout &> (deep_layer.layout ());
|
||||
|
||||
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
|
||||
|
||||
db::Shapes &out = c->shapes (tmp.layer ());
|
||||
for (db::Shapes::shape_iterator s = c->shapes (deep_layer.layer ()); ! s.at_end (); ++s) {
|
||||
|
||||
if (s->is_edge_pair ()) {
|
||||
|
||||
out.insert (s->edge_pair ().normalized ().to_simple_polygon (enl));
|
||||
|
||||
} else if (s->is_path () || s->is_polygon () || s->is_box ()) {
|
||||
|
||||
db::Polygon poly;
|
||||
s->polygon (poly);
|
||||
out.insert (poly);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// and insert this one
|
||||
insert (tmp, into_layout, into_cell, into_layer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,679 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbDeepShapeStore
|
||||
#define HDR_dbDeepShapeStore
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "tlObject.h"
|
||||
#include "tlStableVector.h"
|
||||
#include "tlThreads.h"
|
||||
#include "dbLayout.h"
|
||||
#include "dbRecursiveShapeIterator.h"
|
||||
#include "dbHierarchyBuilder.h"
|
||||
#include "gsiObject.h"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace db {
|
||||
|
||||
class DeepShapeStore;
|
||||
class Region;
|
||||
|
||||
/**
|
||||
* @brief Represents a shape collection from the deep shape store
|
||||
*
|
||||
* This is a lightweight class pointing into the deep shape store.
|
||||
* DeepLayer objects are issued by the DeepShapeStore class.
|
||||
*/
|
||||
class DB_PUBLIC DeepLayer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
DeepLayer ();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~DeepLayer ();
|
||||
|
||||
/**
|
||||
* @brief The constructor from the detailed information
|
||||
* Use this constructor if you know what you're doing.
|
||||
*/
|
||||
DeepLayer (DeepShapeStore *store, unsigned int layout, unsigned int layer);
|
||||
|
||||
/**
|
||||
* @brief Conversion operator from Region to DeepLayer
|
||||
* This requires the Region to be a DeepRegion. Otherwise, this constructor will assert
|
||||
*/
|
||||
DeepLayer (const Region ®ion);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
DeepLayer (const DeepLayer &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
DeepLayer &operator= (const DeepLayer &other);
|
||||
|
||||
/**
|
||||
* @brief Less operator
|
||||
*/
|
||||
bool operator< (const DeepLayer &other) const;
|
||||
|
||||
/**
|
||||
* @brief Equality operator
|
||||
*/
|
||||
bool operator== (const DeepLayer &other) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the layout object
|
||||
* The return value is guaranteed to be non-null.
|
||||
*/
|
||||
Layout &layout();
|
||||
|
||||
/**
|
||||
* @brief Gets the layout object (const version)
|
||||
*/
|
||||
const db::Layout &layout () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the layout object
|
||||
* The return value is guaranteed to be non-null.
|
||||
*/
|
||||
Cell &initial_cell();
|
||||
|
||||
/**
|
||||
* @brief Gets the initial cell object (const version)
|
||||
*/
|
||||
const db::Cell &initial_cell () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the layer
|
||||
*/
|
||||
unsigned int layer () const
|
||||
{
|
||||
return m_layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the layout index
|
||||
*/
|
||||
unsigned int layout_index () const
|
||||
{
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts the layer into the given layout, starting from the given cell and into the given layer
|
||||
*/
|
||||
void insert_into (Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer) const;
|
||||
|
||||
/**
|
||||
* @brief Inserts the edge pairs layer into the given layout, starting from the given cell and into the given layer
|
||||
* The edge pairs are converted to polygons with the given enlargement.
|
||||
*/
|
||||
void insert_into_as_polygons (db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const;
|
||||
|
||||
/**
|
||||
* @brief Creates a derived new deep layer
|
||||
* Derived layers use the same layout and context, but are initially
|
||||
* empty layers for use as output layers on the same hierarchy.
|
||||
*/
|
||||
DeepLayer derived () const;
|
||||
|
||||
/**
|
||||
* @brief Creates a copy of this layer
|
||||
*/
|
||||
DeepLayer copy () const;
|
||||
|
||||
/**
|
||||
* @brief Adds shapes from another deep layer to this one
|
||||
*/
|
||||
void add_from (const DeepLayer &dl);
|
||||
|
||||
/**
|
||||
* @brief Separates cell variants (see DeepShapeStore::separate_variants)
|
||||
*/
|
||||
template <class VarCollector>
|
||||
void separate_variants (VarCollector &collector);
|
||||
|
||||
/**
|
||||
* @brief Commits shapes for variants to the existing cell hierarchy
|
||||
*
|
||||
* The "to_propagate" collection is a set of shapes per cell and variant. The
|
||||
* algorithm will put these shapes into the existing hierarchy putting the
|
||||
* shapes into the proper parent cells to resolve variants.
|
||||
*
|
||||
* This map will be modified by the algorithm and should be discarded
|
||||
* later.
|
||||
*/
|
||||
template <class VarCollector>
|
||||
void commit_shapes (VarCollector &collector, std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > &to_propagate);
|
||||
|
||||
/**
|
||||
* @brief Gets the shape store object
|
||||
* This is a pure const version to prevent manipulation of the store.
|
||||
* This method is intended to fetch configuration options from the store.
|
||||
*/
|
||||
const DeepShapeStore *store () const
|
||||
{
|
||||
check_dss ();
|
||||
return mp_store.get ();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DeepShapeStore;
|
||||
|
||||
void check_dss () const;
|
||||
|
||||
tl::weak_ptr<DeepShapeStore> mp_store;
|
||||
unsigned int m_layout;
|
||||
unsigned int m_layer;
|
||||
};
|
||||
|
||||
struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy
|
||||
{
|
||||
bool operator () (const std::pair<db::RecursiveShapeIterator, db::ICplxTrans> &a, const std::pair<db::RecursiveShapeIterator, db::ICplxTrans> &b) const
|
||||
{
|
||||
int cmp_iter = db::compare_iterators_with_respect_to_target_hierarchy (a.first, b.first);
|
||||
if (cmp_iter != 0) {
|
||||
return cmp_iter < 0;
|
||||
}
|
||||
return a.second < b.second;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The "deep shape store" is a working model for the hierarchical ("deep") processor
|
||||
*
|
||||
* The deep shape store keeps temporary data for the deep shape processor.
|
||||
* It mainly consists of layout objects holding the hierarchy trees and layers
|
||||
* for the actual shapes.
|
||||
*
|
||||
* The deep shape store provides the basis for working with deep regions. On preparation,
|
||||
* shapes are copied into the deep shape store. After fininishing, the shapes are copied
|
||||
* back into the original layout. The deep shape store provides the methods and
|
||||
* algorithms for doing the preparation and transfer.
|
||||
*/
|
||||
class DB_PUBLIC DeepShapeStore
|
||||
: public tl::Object, public gsi::ObjectBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The default constructor
|
||||
*/
|
||||
DeepShapeStore ();
|
||||
|
||||
/**
|
||||
* @brief The constructor for a singular DSS
|
||||
*
|
||||
* This DSS will be initialized with one layout and the given database unit
|
||||
* and top level cell name.
|
||||
*/
|
||||
DeepShapeStore (const std::string &topcell_name, double dbu);
|
||||
|
||||
/**
|
||||
* @brief The destructor
|
||||
*/
|
||||
~DeepShapeStore ();
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the DeepShapeStore is singular
|
||||
*
|
||||
* A "singular" shape store needs a single layout to keep the information.
|
||||
* This is the case, if all Regions derived from it share the same origin
|
||||
* and do not use clipping or region selection. Singular shape stores are
|
||||
* required for netlist extraction for example.
|
||||
*
|
||||
* For a singular shape store, "layout()" will return the layout
|
||||
* object and "initial_cell()" will return the initial cell of the
|
||||
* only layout.
|
||||
*/
|
||||
bool is_singular () const;
|
||||
|
||||
/**
|
||||
* @brief Creates a new layer from a flat region (or the region is made flat)
|
||||
*
|
||||
* This method is intended for use with singular-created DSS objects (see
|
||||
* singular constructor).
|
||||
*
|
||||
* After a flat layer has been created for a region, it can be retrieved
|
||||
* from the region later with layer_for_flat (region).
|
||||
*
|
||||
* If for_netlist is true, texts will be skipped except on top level. The
|
||||
* reasoning is that texts below top level may create name clashes if they
|
||||
* are used for net names.
|
||||
*/
|
||||
DeepLayer create_from_flat (const db::Region ®ion, bool for_netlist, double max_area_ratio = 0.0, size_t max_vertex_count = 0, const db::ICplxTrans &trans = db::ICplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Gets the layer for a given flat region.
|
||||
*
|
||||
* If a layer has been created for a flat region with create_from_flat, it can be retrieved with this method.
|
||||
* The first return value is true in this case.
|
||||
*/
|
||||
std::pair<bool, DeepLayer> layer_for_flat (const db::Region ®ion) const;
|
||||
|
||||
/**
|
||||
* @brief Same as layer_for_flat, but takes a region Id
|
||||
*/
|
||||
std::pair<bool, DeepLayer> layer_for_flat (size_t region_id) const;
|
||||
|
||||
/**
|
||||
* @brief Creates a layout with the given iterator and transformation for the given index
|
||||
*
|
||||
* This method is intended for classes that need more control over the layouts per index
|
||||
* (LayoutToNetlist).
|
||||
*/
|
||||
void make_layout (unsigned int layout_index, const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans = db::ICplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Inserts a polygon layer into the deep shape store
|
||||
*
|
||||
* This method will create a new layer inside the deep shape store as a
|
||||
* working copy of the original layer. Preparation involves re-shaping
|
||||
* the polygons so their bounding box is a better approximation and the
|
||||
* polygon complexity is reduced. For this, the polygons are split
|
||||
* into parts satisfying the area ratio (bounding box vs. polygon area)
|
||||
* and maximum vertex count constraints.
|
||||
*/
|
||||
DeepLayer create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio = 0.0, size_t max_vertex_count = 0, const ICplxTrans &trans = db::ICplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Inserts an edge layer into the deep shape store
|
||||
*
|
||||
* This method will create a new layer inside the deep shape store as a
|
||||
* working copy of the original layer. This method creates a layer
|
||||
* for edges.
|
||||
*
|
||||
* If "as_edges" is true, polygons and boxes will be converted into edges. Otherwise
|
||||
* only edge objects are taken from the shape iterator. Note that the shape iterator
|
||||
* must be configured to deliver all shape types if "as_edges" is true.
|
||||
*/
|
||||
DeepLayer create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const ICplxTrans &trans = db::ICplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Inserts an edge pair layer into the deep shape store
|
||||
*
|
||||
* This method will create a new layer inside the deep shape store as a
|
||||
* working copy of the original layer. This method creates a layer
|
||||
* for edge pairs.
|
||||
*/
|
||||
DeepLayer create_edge_pair_layer (const db::RecursiveShapeIterator &si, const ICplxTrans &trans = db::ICplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Inserts a polygon layer into the deep shape store using a custom preparation pipeline
|
||||
*
|
||||
* This method will create a new layer inside the deep shapes store and
|
||||
* feed it through the given pipeline. The pipeline may perform shape translations and
|
||||
* finally will feed the target hierarchy.
|
||||
*/
|
||||
DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Creates a deep layer as a copy from an existing one
|
||||
*/
|
||||
DeepLayer create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe);
|
||||
|
||||
/**
|
||||
* @brief Gets the empty working layer
|
||||
*
|
||||
* This method will deliver an empty layer for the given layout index. CAUTION: don't modify this layer as it may
|
||||
* be reused.
|
||||
*/
|
||||
DeepLayer empty_layer (unsigned int layout_index) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the empty working layer for the singular layout
|
||||
*/
|
||||
DeepLayer empty_layer () const;
|
||||
|
||||
/**
|
||||
* @brief Inserts the deep layer's shapes into some target layout
|
||||
*/
|
||||
void insert (const DeepLayer &layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer);
|
||||
|
||||
/**
|
||||
* @brief Inserts the deep layer's edge pairs into some target layout
|
||||
*
|
||||
* The edge pairs are converted to polygons with the given enlargement.
|
||||
*/
|
||||
void insert_as_polygons (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl);
|
||||
|
||||
/**
|
||||
* @brief Gets the cell mapping suitable to returning a layout from the deep shape store into the original layout hierarchy
|
||||
*
|
||||
* If necessary, this method will modify the original layout and add new cells.
|
||||
* "layout_index" is the layout to return to it's original. "into_layout" is the original layout, "into_cell"
|
||||
* the original cell.
|
||||
*
|
||||
* "excluded_cells" - if not 0 - will exclude the given cells (and flatten them).
|
||||
*/
|
||||
const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells = 0);
|
||||
|
||||
/**
|
||||
* @brief Create cell variants from the given variant collector
|
||||
*
|
||||
* To use this method, first create a variant collector (db::cell_variant_collector) with the required
|
||||
* reducer and collect the variants. Then call this method on the desired layout index to create the variants.
|
||||
*/
|
||||
template <class VarCollector>
|
||||
void separate_variants (unsigned int layout_index, VarCollector &coll)
|
||||
{
|
||||
tl_assert (is_valid_layout_index (layout_index));
|
||||
|
||||
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > var_map;
|
||||
coll.separate_variants (layout (layout_index), initial_cell (layout_index), &var_map);
|
||||
if (var_map.empty ()) {
|
||||
// nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
issue_variants (layout_index, var_map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Commits shapes for variants to the existing cell hierarchy
|
||||
*
|
||||
* To use this method, first create a variant collector (db::cell_variant_collector) with the required
|
||||
* reducer and collect the variants. Then call this method on the desired layout index to commit the shapes for the
|
||||
* respective variants.
|
||||
*/
|
||||
template <class VarCollector>
|
||||
void commit_shapes (unsigned int layout_index, VarCollector &coll, unsigned int layer, std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > &to_commit)
|
||||
{
|
||||
tl_assert (is_valid_layout_index (layout_index));
|
||||
|
||||
coll.commit_shapes (layout (layout_index), initial_cell (layout_index), layer, to_commit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For testing
|
||||
*/
|
||||
static size_t instance_count ();
|
||||
|
||||
/**
|
||||
* @brief Gets the nth layout (const version)
|
||||
*/
|
||||
const db::Layout &const_layout (unsigned int n) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the nth layout (non-const version)
|
||||
*
|
||||
* Don't try to mess too much with the layout object, you'll screw up the internals.
|
||||
*/
|
||||
db::Layout &layout (unsigned int n);
|
||||
|
||||
/**
|
||||
* @brief Gets the initial cell of the nth layout (const version)
|
||||
*/
|
||||
const db::Cell &const_initial_cell (unsigned int n) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the initial cell of the nth layout (non-const version)
|
||||
*
|
||||
* Don't try to mess too much with the cell object, you'll screw up the internals.
|
||||
*/
|
||||
db::Cell &initial_cell (unsigned int n);
|
||||
|
||||
/**
|
||||
* @brief Gets the singular layout (const version)
|
||||
*
|
||||
* This method will throw an exception if the deep shape store is not singular.
|
||||
*/
|
||||
const db::Layout &const_layout () const
|
||||
{
|
||||
require_singular ();
|
||||
return const_layout (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the singular layout (non-const version)
|
||||
*
|
||||
* This method will throw an exception if the deep shape store is not singular.
|
||||
* Don't try to mess too much with the layout object, you'll screw up the internals.
|
||||
*/
|
||||
db::Layout &layout ()
|
||||
{
|
||||
require_singular ();
|
||||
return layout (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the initial cell of the singular layout (const version)
|
||||
*
|
||||
* This method will throw an exception if the deep shape store is not singular.
|
||||
*/
|
||||
const db::Cell &const_initial_cell () const
|
||||
{
|
||||
require_singular ();
|
||||
return const_initial_cell (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the initial cell of the singular layout (non-const version)
|
||||
*
|
||||
* This method will throw an exception if the deep shape store is not singular.
|
||||
* Don't try to mess too much with the cell object, you'll screw up the internals.
|
||||
*/
|
||||
db::Cell &initial_cell ()
|
||||
{
|
||||
require_singular ();
|
||||
return initial_cell (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of layouts
|
||||
*/
|
||||
unsigned int layouts () const
|
||||
{
|
||||
return (unsigned int) m_layouts.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the given index is a valid layout index
|
||||
*/
|
||||
bool is_valid_layout_index (unsigned int n) const;
|
||||
|
||||
/**
|
||||
* @brief The deep shape store also keeps the number of threads to allocate for the hierarchical processor
|
||||
*
|
||||
* This is a kind of hack, but it's convenient.
|
||||
*/
|
||||
void set_threads (int n);
|
||||
|
||||
/**
|
||||
* @brief Gets the number of threads
|
||||
*/
|
||||
int threads () const
|
||||
{
|
||||
return m_threads;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum vertex count default value
|
||||
*
|
||||
* This parameter is used to simplify complex polygons. It is used by
|
||||
* create_polygon_layer with the default parameters. It's also used by
|
||||
* boolean operations when they deliver their output.
|
||||
*/
|
||||
void set_max_vertex_count (size_t n);
|
||||
|
||||
/**
|
||||
* @brief Gets the maximum vertex count
|
||||
*/
|
||||
size_t max_vertex_count () const
|
||||
{
|
||||
return m_max_vertex_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the max. area ratio for bounding box vs. polygon area
|
||||
*
|
||||
* This parameter is used to simplify complex polygons. It is used by
|
||||
* create_polygon_layer with the default parameters. It's also used by
|
||||
* boolean operations when they deliver their output.
|
||||
*/
|
||||
void set_max_area_ratio (double ar);
|
||||
|
||||
/**
|
||||
* @brief Gets the max. area ratio
|
||||
*/
|
||||
double max_area_ratio () const
|
||||
{
|
||||
return m_max_area_ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the text property name
|
||||
*
|
||||
* If set to a non-null variant, text strings are attached to the generated boxes
|
||||
* as properties with this particular name. This option has an effect only if the
|
||||
* text_enlargement property is not negative.
|
||||
* By default, the name is empty.
|
||||
*/
|
||||
void set_text_property_name (const tl::Variant &pn);
|
||||
|
||||
/**
|
||||
* @brief Gets the text property name
|
||||
*/
|
||||
const tl::Variant &text_property_name () const
|
||||
{
|
||||
return m_text_property_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the text enlargement value
|
||||
*
|
||||
* If set to a non-negative value, text objects are converted to boxes with the
|
||||
* given enlargement (width = 2 * enlargement). The box centers are identical
|
||||
* to the original location of the text.
|
||||
* If this value is negative (the default), texts are ignored.
|
||||
*/
|
||||
void set_text_enlargement (int enl);
|
||||
|
||||
/**
|
||||
* @brief Gets the text enlargement value
|
||||
*/
|
||||
int text_enlargement () const
|
||||
{
|
||||
return m_text_enlargement;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DeepLayer;
|
||||
|
||||
struct LayoutHolder;
|
||||
|
||||
void invalidate_hier ();
|
||||
void add_ref (unsigned int layout, unsigned int layer);
|
||||
void remove_ref (unsigned int layout, unsigned int layer);
|
||||
|
||||
unsigned int layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans);
|
||||
|
||||
void require_singular () const;
|
||||
|
||||
void issue_variants (unsigned int layout, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_map);
|
||||
|
||||
typedef std::map<std::pair<db::RecursiveShapeIterator, db::ICplxTrans>, unsigned int, RecursiveShapeIteratorCompareForTargetHierarchy> layout_map_type;
|
||||
|
||||
// no copying
|
||||
DeepShapeStore (const DeepShapeStore &);
|
||||
DeepShapeStore &operator= (const DeepShapeStore &);
|
||||
|
||||
std::vector<LayoutHolder *> m_layouts;
|
||||
std::map<size_t, std::pair<unsigned int, unsigned int> > m_layers_for_flat;
|
||||
std::map<std::pair<unsigned int, unsigned int>, size_t> m_flat_region_id;
|
||||
layout_map_type m_layout_map;
|
||||
int m_threads;
|
||||
double m_max_area_ratio;
|
||||
size_t m_max_vertex_count;
|
||||
tl::Variant m_text_property_name;
|
||||
int m_text_enlargement;
|
||||
tl::Mutex m_lock;
|
||||
|
||||
struct DeliveryMappingCacheKey
|
||||
{
|
||||
// NOTE: we shouldn't keep pointers here as the layouts may get deleted and recreated with the same address.
|
||||
// But as we don't access these objects that's fairly safe.
|
||||
DeliveryMappingCacheKey (unsigned int _from_index, tl::id_type _into_layout, db::cell_index_type _into_cell)
|
||||
: from_index (_from_index), into_layout (_into_layout), into_cell (_into_cell)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool operator< (const DeliveryMappingCacheKey &other) const
|
||||
{
|
||||
if (from_index != other.from_index) {
|
||||
return from_index < other.from_index;
|
||||
}
|
||||
if (into_layout != other.into_layout) {
|
||||
return into_layout < other.into_layout;
|
||||
}
|
||||
return into_cell <other.into_cell;
|
||||
}
|
||||
|
||||
unsigned int from_index;
|
||||
tl::id_type into_layout;
|
||||
db::cell_index_type into_cell;
|
||||
};
|
||||
|
||||
std::map<DeliveryMappingCacheKey, db::CellMapping> m_delivery_mapping_cache;
|
||||
};
|
||||
|
||||
template <class VarCollector>
|
||||
void DeepLayer::separate_variants (VarCollector &collector)
|
||||
{
|
||||
check_dss ();
|
||||
mp_store->separate_variants (m_layout, collector);
|
||||
}
|
||||
|
||||
template <class VarCollector>
|
||||
void DeepLayer::commit_shapes (VarCollector &collector, std::map<db::cell_index_type, std::map<db::ICplxTrans, db::Shapes> > &to_commit)
|
||||
{
|
||||
check_dss ();
|
||||
mp_store->commit_shapes (m_layout, collector, layer (), to_commit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
// disable copying of the deep shape store object
|
||||
template <> struct type_traits <db::DeepShapeStore> : public type_traits<void> {
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbDevice.h"
|
||||
#include "dbCircuit.h"
|
||||
#include "dbDeviceClass.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Device class implementation
|
||||
|
||||
Device::Device ()
|
||||
: mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Device::~Device ()
|
||||
{
|
||||
for (std::vector<Net::terminal_iterator>::const_iterator t = m_terminal_refs.begin (); t != m_terminal_refs.end (); ++t) {
|
||||
if (*t != Net::terminal_iterator () && (*t)->net ()) {
|
||||
(*t)->net ()->erase_terminal (*t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Device::Device (DeviceClass *device_class, const std::string &name)
|
||||
: mp_device_class (device_class), mp_device_abstract (0), m_name (name), m_id (0), mp_circuit (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Device::Device (DeviceClass *device_class, DeviceAbstract *device_abstract, const std::string &name)
|
||||
: mp_device_class (device_class), mp_device_abstract (device_abstract), m_name (name), m_id (0), mp_circuit (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Device::Device (const Device &other)
|
||||
: tl::Object (other), mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
Device &Device::operator= (const Device &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_name = other.m_name;
|
||||
m_position = other.m_position;
|
||||
m_parameters = other.m_parameters;
|
||||
mp_device_class = other.mp_device_class;
|
||||
mp_device_abstract = other.mp_device_abstract;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Device::expanded_name () const
|
||||
{
|
||||
if (name ().empty ()) {
|
||||
return "$" + tl::to_string (id ());
|
||||
} else {
|
||||
return name ();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::set_circuit (Circuit *circuit)
|
||||
{
|
||||
mp_circuit = circuit;
|
||||
}
|
||||
|
||||
void Device::set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
if (mp_circuit) {
|
||||
mp_circuit->m_device_by_name.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::set_position (const db::DPoint &pt)
|
||||
{
|
||||
m_position = pt;
|
||||
}
|
||||
|
||||
void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter)
|
||||
{
|
||||
if (m_terminal_refs.size () < terminal_id + 1) {
|
||||
m_terminal_refs.resize (terminal_id + 1, Net::terminal_iterator ());
|
||||
}
|
||||
m_terminal_refs [terminal_id] = iter;
|
||||
}
|
||||
|
||||
const Net *Device::net_for_terminal (size_t terminal_id) const
|
||||
{
|
||||
if (terminal_id < m_terminal_refs.size ()) {
|
||||
Net::terminal_iterator p = m_terminal_refs [terminal_id];
|
||||
if (p != Net::terminal_iterator ()) {
|
||||
return p->net ();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Device::connect_terminal (size_t terminal_id, Net *net)
|
||||
{
|
||||
if (net_for_terminal (terminal_id) == net) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (terminal_id < m_terminal_refs.size ()) {
|
||||
Net::terminal_iterator p = m_terminal_refs [terminal_id];
|
||||
if (p != Net::terminal_iterator () && p->net ()) {
|
||||
p->net ()->erase_terminal (p);
|
||||
}
|
||||
m_terminal_refs [terminal_id] = Net::terminal_iterator ();
|
||||
}
|
||||
|
||||
if (net) {
|
||||
net->add_terminal (NetTerminalRef (this, terminal_id));
|
||||
}
|
||||
}
|
||||
|
||||
double Device::parameter_value (size_t param_id) const
|
||||
{
|
||||
if (m_parameters.size () > param_id) {
|
||||
return m_parameters [param_id];
|
||||
} else if (mp_device_class) {
|
||||
const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (param_id);
|
||||
if (pd) {
|
||||
return pd->default_value ();
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void Device::set_parameter_value (size_t param_id, double v)
|
||||
{
|
||||
if (m_parameters.size () <= param_id) {
|
||||
|
||||
// resize the parameter vector with default values
|
||||
size_t from_size = m_parameters.size ();
|
||||
m_parameters.resize (param_id + 1, 0.0);
|
||||
|
||||
if (mp_device_class) {
|
||||
for (size_t n = from_size; n < param_id; ++n) {
|
||||
const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (n);
|
||||
if (pd) {
|
||||
m_parameters [n] = pd->default_value ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_parameters [param_id] = v;
|
||||
}
|
||||
|
||||
double Device::parameter_value (const std::string &name) const
|
||||
{
|
||||
return device_class () ? parameter_value (device_class ()->parameter_id_for_name (name)) : 0.0;
|
||||
}
|
||||
|
||||
void Device::set_parameter_value (const std::string &name, double v)
|
||||
{
|
||||
if (device_class ()) {
|
||||
set_parameter_value (device_class ()->parameter_id_for_name (name), v);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbDevice
|
||||
#define _HDR_dbDevice
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbNet.h"
|
||||
#include "dbPoint.h"
|
||||
|
||||
#include "tlObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Circuit;
|
||||
class DeviceClass;
|
||||
class DeviceAbstract;
|
||||
|
||||
/**
|
||||
* @brief An actual device
|
||||
*
|
||||
* This class represents the incarnation of a specific device.
|
||||
* The device has a class which specifies a type. This class
|
||||
* is intended for subclassing.
|
||||
* A specific device subclass is supposed to correspond to
|
||||
* a specific device class.
|
||||
*/
|
||||
class DB_PUBLIC Device
|
||||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
typedef std::vector<std::pair<size_t, size_t> > global_connections;
|
||||
typedef global_connections::const_iterator global_connections_iterator;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
Device ();
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
Device (DeviceClass *device_class, const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
Device (DeviceClass *device_class, DeviceAbstract *device_abstract, const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
Device (const Device &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
Device &operator= (const Device &other);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~Device ();
|
||||
|
||||
/**
|
||||
* @brief Gets the device class
|
||||
*/
|
||||
const DeviceClass *device_class () const
|
||||
{
|
||||
return mp_device_class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the device class
|
||||
*/
|
||||
void set_device_class (DeviceClass *dc)
|
||||
{
|
||||
mp_device_class = dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device abstract
|
||||
*/
|
||||
const DeviceAbstract *device_abstract () const
|
||||
{
|
||||
return mp_device_abstract;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the device abstract
|
||||
*/
|
||||
void set_device_abstract (DeviceAbstract *dm)
|
||||
{
|
||||
mp_device_abstract = dm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device ID
|
||||
* The ID is a unique integer which identifies the device.
|
||||
* It can be used to retrieve the device from the circuit using Circuit::device_by_id.
|
||||
* When assigned, the device ID is not 0.
|
||||
*/
|
||||
size_t id () const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit the device lives in (const version)
|
||||
* This pointer is 0 if the device isn't added to a circuit
|
||||
*/
|
||||
const Circuit *circuit () const
|
||||
{
|
||||
return mp_circuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit the device lives in (non-const version)
|
||||
* This pointer is 0 if the device isn't added to a circuit
|
||||
*/
|
||||
Circuit *circuit ()
|
||||
{
|
||||
return mp_circuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the name
|
||||
*/
|
||||
void set_name (const std::string &n);
|
||||
|
||||
/**
|
||||
* @brief Gets the name
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a name which always non-empty
|
||||
* This method will pick a name like "$<id>" if the explicit name is empty.
|
||||
*/
|
||||
std::string expanded_name () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the device position
|
||||
* The device position should be the center of the recognition shape or something similar.
|
||||
* Giving the device a position allows combining multiple devices with the same
|
||||
* relative geometry into a single cell.
|
||||
* The position has to be given in micrometer units.
|
||||
*/
|
||||
void set_position (const db::DPoint &pos);
|
||||
|
||||
/**
|
||||
* @brief Gets the device position
|
||||
*/
|
||||
const db::DPoint &position () const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net attached to a specific terminal
|
||||
* Returns 0 if no net is attached.
|
||||
*/
|
||||
const Net *net_for_terminal (size_t terminal_id) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the net attached to a specific terminal (non-const version)
|
||||
* Returns 0 if no net is attached.
|
||||
*/
|
||||
Net *net_for_terminal (size_t terminal_id)
|
||||
{
|
||||
return const_cast<Net *> (((const Device *) this)->net_for_terminal (terminal_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects the given terminal to the given net
|
||||
* If the net is 0 the terminal is disconnected.
|
||||
* If non-null, a NetTerminalRef object will be inserted into the
|
||||
* net and connected with the given terminal.
|
||||
* If the terminal is connected to a global net, it will be
|
||||
* disconnected from there.
|
||||
*/
|
||||
void connect_terminal (size_t terminal_id, Net *net);
|
||||
|
||||
/**
|
||||
* @brief Gets the value for the parameter with the given ID
|
||||
*/
|
||||
double parameter_value (size_t param_id) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the value for the parameter with the given ID
|
||||
*/
|
||||
void set_parameter_value (size_t param_id, double v);
|
||||
|
||||
/**
|
||||
* @brief Gets the value for the parameter with the given name
|
||||
* If the name is not valid, an exception is thrown.
|
||||
*/
|
||||
double parameter_value (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the value for the parameter with the given name
|
||||
* If the name is not valid, an exception is thrown.
|
||||
*/
|
||||
void set_parameter_value (const std::string &name, double v);
|
||||
|
||||
private:
|
||||
friend class Circuit;
|
||||
friend class Net;
|
||||
|
||||
DeviceClass *mp_device_class;
|
||||
DeviceAbstract *mp_device_abstract;
|
||||
std::string m_name;
|
||||
db::DPoint m_position;
|
||||
std::vector<Net::terminal_iterator> m_terminal_refs;
|
||||
std::vector<double> m_parameters;
|
||||
size_t m_id;
|
||||
Circuit *mp_circuit;
|
||||
|
||||
/**
|
||||
* @brief Sets the terminal reference for a specific terminal
|
||||
*/
|
||||
void set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter);
|
||||
|
||||
/**
|
||||
* @brief Sets the device ID
|
||||
*/
|
||||
void set_id (size_t id)
|
||||
{
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the circuit
|
||||
*/
|
||||
void set_circuit (Circuit *circuit);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbDeviceAbstract.h"
|
||||
#include "dbCircuit.h"
|
||||
#include "dbNetlist.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// DeviceAbstract class implementation
|
||||
|
||||
DeviceAbstract::DeviceAbstract ()
|
||||
: m_name (), mp_device_class (0), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeviceAbstract::~DeviceAbstract ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeviceAbstract::DeviceAbstract (db::DeviceClass *device_class, const std::string &name)
|
||||
: m_name (name), mp_device_class (device_class), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeviceAbstract::DeviceAbstract (const DeviceAbstract &other)
|
||||
: tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
DeviceAbstract &DeviceAbstract::operator= (const DeviceAbstract &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_name = other.m_name;
|
||||
mp_device_class = other.mp_device_class;
|
||||
m_cell_index = other.m_cell_index;
|
||||
m_terminal_cluster_ids = other.m_terminal_cluster_ids;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DeviceAbstract::set_netlist (Netlist *netlist)
|
||||
{
|
||||
mp_netlist = netlist;
|
||||
}
|
||||
|
||||
void DeviceAbstract::set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
if (mp_netlist) {
|
||||
mp_netlist->m_device_abstract_by_name.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceAbstract::set_cell_index (db::cell_index_type ci)
|
||||
{
|
||||
m_cell_index = ci;
|
||||
if (mp_netlist) {
|
||||
mp_netlist->m_device_abstract_by_cell_index.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
size_t DeviceAbstract::cluster_id_for_terminal (size_t terminal_id) const
|
||||
{
|
||||
return terminal_id < m_terminal_cluster_ids.size () ? m_terminal_cluster_ids [terminal_id] : 0;
|
||||
}
|
||||
|
||||
void DeviceAbstract::set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id)
|
||||
{
|
||||
if (m_terminal_cluster_ids.size () <= terminal_id) {
|
||||
m_terminal_cluster_ids.resize (terminal_id + 1, 0);
|
||||
}
|
||||
m_terminal_cluster_ids [terminal_id] = cluster_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbDeviceAbstract
|
||||
#define _HDR_dbDeviceAbstract
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbNet.h"
|
||||
#include "dbPoint.h"
|
||||
|
||||
#include "tlObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
|
||||
/**
|
||||
* @brief A device abstract
|
||||
*
|
||||
* A device abstract represents the geometrical properties of a device. It basically links
|
||||
* to a cell and clusters for indicating the terminal geometry of the device.
|
||||
*/
|
||||
class DB_PUBLIC DeviceAbstract
|
||||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
DeviceAbstract ();
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
DeviceAbstract (db::DeviceClass *device_class, const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
DeviceAbstract (const DeviceAbstract &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
DeviceAbstract &operator= (const DeviceAbstract &other);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~DeviceAbstract ();
|
||||
|
||||
/**
|
||||
* @brief Gets the device class
|
||||
*/
|
||||
const DeviceClass *device_class () const
|
||||
{
|
||||
return mp_device_class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the device class
|
||||
*/
|
||||
void set_device_class (DeviceClass *dc)
|
||||
{
|
||||
mp_device_class = dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the netlist the device lives in (const version)
|
||||
* This pointer is 0 if the device abstract isn't added to a netlist
|
||||
*/
|
||||
const Netlist *netlist () const
|
||||
{
|
||||
return mp_netlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the netlist the device lives in (non-const version)
|
||||
* This pointer is 0 if the device abstract isn't added to a netlist
|
||||
*/
|
||||
Netlist *netlist ()
|
||||
{
|
||||
return mp_netlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the name
|
||||
*/
|
||||
void set_name (const std::string &n);
|
||||
|
||||
/**
|
||||
* @brief Gets the name
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the device cell index
|
||||
* In the layout, a device is represented by a cell. This attribute gives the index of this
|
||||
* cell.
|
||||
*/
|
||||
void set_cell_index (db::cell_index_type ci);
|
||||
|
||||
/**
|
||||
* @brief Gets the device cell index
|
||||
*/
|
||||
db::cell_index_type cell_index () const
|
||||
{
|
||||
return m_cell_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the cluster ID for a given terminal
|
||||
* This attribute connects the device terminal with a terminal cluster
|
||||
*/
|
||||
size_t cluster_id_for_terminal (size_t terminal_id) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the cluster ID for a given terminal
|
||||
*/
|
||||
void set_cluster_id_for_terminal (size_t terminal_id, size_t cluster_id);
|
||||
|
||||
private:
|
||||
friend class Netlist;
|
||||
|
||||
std::string m_name;
|
||||
db::DeviceClass *mp_device_class;
|
||||
db::cell_index_type m_cell_index;
|
||||
std::vector<size_t> m_terminal_cluster_ids;
|
||||
Netlist *mp_netlist;
|
||||
|
||||
/**
|
||||
* @brief Sets the netlist
|
||||
*/
|
||||
void set_netlist (Netlist *netlist);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbDeviceClass.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// DeviceClass class implementation
|
||||
|
||||
DeviceClass::DeviceClass ()
|
||||
: mp_netlist (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeviceClass::DeviceClass (const DeviceClass &other)
|
||||
: gsi::ObjectBase (other), tl::Object (other), tl::UniqueId (other), mp_netlist (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
DeviceClass &DeviceClass::operator= (const DeviceClass &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_terminal_definitions = other.m_terminal_definitions;
|
||||
m_name = other.m_name;
|
||||
m_description = other.m_description;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const DeviceTerminalDefinition &DeviceClass::add_terminal_definition (const DeviceTerminalDefinition &pd)
|
||||
{
|
||||
m_terminal_definitions.push_back (pd);
|
||||
m_terminal_definitions.back ().set_id (m_terminal_definitions.size () - 1);
|
||||
return m_terminal_definitions.back ();
|
||||
}
|
||||
|
||||
void DeviceClass::clear_terminal_definitions ()
|
||||
{
|
||||
m_terminal_definitions.clear ();
|
||||
}
|
||||
|
||||
const DeviceTerminalDefinition *DeviceClass::terminal_definition (size_t id) const
|
||||
{
|
||||
if (id < m_terminal_definitions.size ()) {
|
||||
return & m_terminal_definitions [id];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const DeviceParameterDefinition &DeviceClass::add_parameter_definition (const DeviceParameterDefinition &pd)
|
||||
{
|
||||
m_parameter_definitions.push_back (pd);
|
||||
m_parameter_definitions.back ().set_id (m_parameter_definitions.size () - 1);
|
||||
return m_parameter_definitions.back ();
|
||||
}
|
||||
|
||||
void DeviceClass::clear_parameter_definitions ()
|
||||
{
|
||||
m_parameter_definitions.clear ();
|
||||
}
|
||||
|
||||
const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) const
|
||||
{
|
||||
if (id < m_parameter_definitions.size ()) {
|
||||
return & m_parameter_definitions [id];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceClass::has_parameter_with_name (const std::string &name) const
|
||||
{
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
|
||||
if (i->name () == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t DeviceClass::parameter_id_for_name (const std::string &name) const
|
||||
{
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
|
||||
if (i->name () == name) {
|
||||
return i->id ();
|
||||
}
|
||||
}
|
||||
throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'");
|
||||
}
|
||||
|
||||
bool DeviceClass::has_terminal_with_name (const std::string &name) const
|
||||
{
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
|
||||
if (i->name () == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t DeviceClass::terminal_id_for_name (const std::string &name) const
|
||||
{
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
|
||||
if (i->name () == name) {
|
||||
return i->id ();
|
||||
}
|
||||
}
|
||||
throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,422 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbDeviceClass
|
||||
#define _HDR_dbDeviceClass
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "gsiObject.h"
|
||||
#include "tlObject.h"
|
||||
#include "tlUniqueId.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
class Device;
|
||||
|
||||
/**
|
||||
* @brief A device terminal definition
|
||||
*/
|
||||
class DB_PUBLIC DeviceTerminalDefinition
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates an empty device terminal definition
|
||||
*/
|
||||
DeviceTerminalDefinition ()
|
||||
: m_name (), m_description (), m_id (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a device terminal definition with the given name and description
|
||||
*/
|
||||
DeviceTerminalDefinition (const std::string &name, const std::string &description)
|
||||
: m_name (name), m_description (description), m_id (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the terminal name
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the terminal name
|
||||
*/
|
||||
void set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the terminal description
|
||||
*/
|
||||
const std::string &description () const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the terminal description
|
||||
*/
|
||||
void set_description (const std::string &d)
|
||||
{
|
||||
m_description = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the terminal ID
|
||||
*/
|
||||
size_t id () const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DeviceClass;
|
||||
|
||||
std::string m_name, m_description;
|
||||
size_t m_id;
|
||||
|
||||
void set_id (size_t id)
|
||||
{
|
||||
m_id = id;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A device parameter definition
|
||||
*/
|
||||
class DB_PUBLIC DeviceParameterDefinition
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates an empty device parameter definition
|
||||
*/
|
||||
DeviceParameterDefinition ()
|
||||
: m_name (), m_description (), m_default_value (0.0), m_id (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a device parameter definition with the given name and description
|
||||
*/
|
||||
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0)
|
||||
: m_name (name), m_description (description), m_default_value (default_value), m_id (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter name
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the parameter name
|
||||
*/
|
||||
void set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter description
|
||||
*/
|
||||
const std::string &description () const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the parameter description
|
||||
*/
|
||||
void set_description (const std::string &d)
|
||||
{
|
||||
m_description = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter default value
|
||||
*/
|
||||
double default_value () const
|
||||
{
|
||||
return m_default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the parameter description
|
||||
*/
|
||||
void set_default_value (double d)
|
||||
{
|
||||
m_default_value = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter ID
|
||||
*/
|
||||
size_t id () const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DeviceClass;
|
||||
|
||||
std::string m_name, m_description;
|
||||
double m_default_value;
|
||||
size_t m_id;
|
||||
|
||||
void set_id (size_t id)
|
||||
{
|
||||
m_id = id;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A device class
|
||||
*
|
||||
* A device class describes a type of device.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClass
|
||||
: public gsi::ObjectBase, public tl::Object, public tl::UniqueId
|
||||
{
|
||||
public:
|
||||
typedef size_t terminal_id_type;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* Creates an empty circuit.
|
||||
*/
|
||||
DeviceClass ();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
* NOTE: do not use this copy constructor as the device class
|
||||
* is intended to subclassing.
|
||||
*/
|
||||
DeviceClass (const DeviceClass &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
* NOTE: do not use this copy constructor as the device class
|
||||
* is intended to subclassing.
|
||||
*/
|
||||
DeviceClass &operator= (const DeviceClass &other);
|
||||
|
||||
/**
|
||||
* @brief Gets the netlist the device class lives in
|
||||
*/
|
||||
db::Netlist *netlist ()
|
||||
{
|
||||
return mp_netlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the netlist the device class lives in (const version)
|
||||
*/
|
||||
const db::Netlist *netlist () const
|
||||
{
|
||||
return mp_netlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the device class
|
||||
*
|
||||
* The name is a formal name which identifies the class.
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the device name
|
||||
*/
|
||||
void set_name (const std::string &n)
|
||||
{
|
||||
m_name = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the description text for the device class
|
||||
*
|
||||
* The description text is a human-readable text that
|
||||
* identifies the device class.
|
||||
*/
|
||||
const std::string &description () const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the description text
|
||||
*/
|
||||
void set_description (const std::string &d)
|
||||
{
|
||||
m_description = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the terminal definitions
|
||||
*
|
||||
* The terminal definitions indicate what terminals the device offers.
|
||||
* The number of terminals is constant per class. The index of the terminal
|
||||
* is used as an ID of the terminal, hence the order must be static.
|
||||
*/
|
||||
const std::vector<DeviceTerminalDefinition> &terminal_definitions () const
|
||||
{
|
||||
return m_terminal_definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a terminal definition
|
||||
*/
|
||||
const DeviceTerminalDefinition &add_terminal_definition (const DeviceTerminalDefinition &pd);
|
||||
|
||||
/**
|
||||
* @brief Clears the terminal definition
|
||||
*/
|
||||
void clear_terminal_definitions ();
|
||||
|
||||
/**
|
||||
* @brief Gets the terminal definition from the ID
|
||||
*/
|
||||
const DeviceTerminalDefinition *terminal_definition (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter definitions
|
||||
*/
|
||||
const std::vector<DeviceParameterDefinition> ¶meter_definitions () const
|
||||
{
|
||||
return m_parameter_definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a parameter definition
|
||||
*/
|
||||
const DeviceParameterDefinition &add_parameter_definition (const DeviceParameterDefinition &pd);
|
||||
|
||||
/**
|
||||
* @brief Clears the parameter definition
|
||||
*/
|
||||
void clear_parameter_definitions ();
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter definition from the ID
|
||||
*/
|
||||
const DeviceParameterDefinition *parameter_definition (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the device has a parameter with the given name
|
||||
*/
|
||||
bool has_parameter_with_name (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the parameter ID for the parameter with the given name
|
||||
* If the name is invalid, an exception is thrown.
|
||||
*/
|
||||
size_t parameter_id_for_name (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the device has a terminal with the given name
|
||||
*/
|
||||
bool has_terminal_with_name (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the parameter ID for the terminal with the given name
|
||||
* If the name is invalid, an exception is thrown.
|
||||
*/
|
||||
size_t terminal_id_for_name (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Clears the circuit
|
||||
*/
|
||||
virtual DeviceClass *clone () const
|
||||
{
|
||||
return new DeviceClass (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines two devices
|
||||
*
|
||||
* This method shall test, whether the two devices can be combined. Both devices
|
||||
* are guaranteed to share the same device class (this).
|
||||
* If they cannot be combined, this method shall do nothing and return false.
|
||||
* If they can be combined, this method shall reconnect the nets of the first
|
||||
* device and entirely disconnect the nets of the second device.
|
||||
* The second device will be deleted afterwards.
|
||||
*/
|
||||
virtual bool combine_devices (db::Device * /*a*/, db::Device * /*b*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the device class supports device combination in parallel mode
|
||||
*/
|
||||
virtual bool supports_parallel_combination () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the device class supports device combination in serial mode
|
||||
*/
|
||||
virtual bool supports_serial_combination () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Netlist;
|
||||
|
||||
std::string m_name, m_description;
|
||||
std::vector<DeviceTerminalDefinition> m_terminal_definitions;
|
||||
std::vector<DeviceParameterDefinition> m_parameter_definitions;
|
||||
db::Netlist *mp_netlist;
|
||||
|
||||
void set_netlist (db::Netlist *nl)
|
||||
{
|
||||
mp_netlist = nl;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -336,7 +336,14 @@ public:
|
|||
template <class Tr>
|
||||
edge<C> &transform (const Tr &t)
|
||||
{
|
||||
*this = edge<C> (t * m_p1, t * m_p2);
|
||||
if (t.is_mirror ()) {
|
||||
// NOTE: in case of mirroring transformations we swap p1 and p2. The reasoning is that
|
||||
// this way we maintain the orientation semantics: "right" of the edge is "inside" of
|
||||
// an area.
|
||||
*this = edge<C> (t * m_p2, t * m_p1);
|
||||
} else {
|
||||
*this = edge<C> (t * m_p1, t * m_p2);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +360,14 @@ public:
|
|||
template <class Tr>
|
||||
edge<typename Tr::target_coord_type> transformed (const Tr &t) const
|
||||
{
|
||||
return edge<typename Tr::target_coord_type> (t * m_p1, t * m_p2);
|
||||
if (t.is_mirror ()) {
|
||||
// NOTE: in case of mirroring transformations we swap p1 and p2. The reasoning is that
|
||||
// this way we maintain the orientation semantics: "right" of the edge is "inside" of
|
||||
// an area.
|
||||
return edge<typename Tr::target_coord_type> (t * m_p2, t * m_p1);
|
||||
} else {
|
||||
return edge<typename Tr::target_coord_type> (t * m_p1, t * m_p2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -914,7 +928,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Swap the points of the edge
|
||||
* @brief Swaps the points of the edge
|
||||
*/
|
||||
edge<C> &swap_points ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbEdgeBoolean.h"
|
||||
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbEdgeBoolean
|
||||
#define HDR_dbEdgeBoolean
|
||||
|
||||
#include "dbEdge.h"
|
||||
#include "dbEdgesDelegate.h"
|
||||
#include "dbBoxScanner.h"
|
||||
|
||||
#include "tlIntervalMap.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
struct OrJoinOp
|
||||
{
|
||||
void operator() (int &v, int n)
|
||||
{
|
||||
v += n;
|
||||
}
|
||||
};
|
||||
|
||||
struct AndJoinOp
|
||||
{
|
||||
void operator() (int &v, int n)
|
||||
{
|
||||
if (n == 0) {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct NotJoinOp
|
||||
{
|
||||
void operator() (int &v, int n)
|
||||
{
|
||||
if (n != 0) {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct XorJoinOp
|
||||
{
|
||||
void operator() (int &v, int n)
|
||||
{
|
||||
if (n != 0) {
|
||||
if (v == 0) {
|
||||
v = (n > 0 ? 1 : -1);
|
||||
} else {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class OutputContainer>
|
||||
struct EdgeBooleanCluster
|
||||
: public db::cluster<db::Edge, size_t>
|
||||
{
|
||||
typedef db::Edge::coord_type coord_type;
|
||||
|
||||
EdgeBooleanCluster (OutputContainer *output, EdgeBoolOp op)
|
||||
: mp_output (output), m_op (op)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void finish ()
|
||||
{
|
||||
// determine base edge (longest overall edge)
|
||||
|
||||
// shortcut for single edge
|
||||
if (begin () + 1 == end ()) {
|
||||
if (begin ()->second == 0) {
|
||||
if (m_op != EdgeAnd) {
|
||||
mp_output->insert (*(begin ()->first));
|
||||
}
|
||||
} else {
|
||||
if (m_op != EdgeAnd && m_op != EdgeNot) {
|
||||
mp_output->insert (*(begin ()->first));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
db::Edge r = *begin ()->first;
|
||||
double l1 = 0.0, l2 = r.double_length ();
|
||||
double n = 1.0 / l2;
|
||||
db::Point p1 = r.p1 (), p2 = r.p2 ();
|
||||
|
||||
for (iterator o = begin () + 1; o != end (); ++o) {
|
||||
double ll1 = db::sprod (db::Vector (o->first->p1 () - r.p1 ()), r.d ()) * n;
|
||||
double ll2 = db::sprod (db::Vector (o->first->p2 () - r.p1 ()), r.d ()) * n;
|
||||
if (ll1 < l1) {
|
||||
p1 = o->first->p1 ();
|
||||
l1 = ll1;
|
||||
}
|
||||
if (ll2 < l1) {
|
||||
p1 = o->first->p2 ();
|
||||
l1 = ll2;
|
||||
}
|
||||
if (ll1 > l2) {
|
||||
p2 = o->first->p1 ();
|
||||
l2 = ll1;
|
||||
}
|
||||
if (ll2 > l2) {
|
||||
p2 = o->first->p2 ();
|
||||
l2 = ll2;
|
||||
}
|
||||
}
|
||||
|
||||
db::Vector d = db::Vector (p2 - p1);
|
||||
n = 1.0 / d.double_length ();
|
||||
|
||||
OrJoinOp or_jop;
|
||||
AndJoinOp and_jop;
|
||||
NotJoinOp not_jop;
|
||||
XorJoinOp xor_jop;
|
||||
|
||||
tl::interval_map<db::Coord, int> a, b;
|
||||
a.add (0, db::coord_traits<db::Coord>::rounded (d.double_length ()), 0, or_jop);
|
||||
b.add (0, db::coord_traits<db::Coord>::rounded (d.double_length ()), 0, or_jop);
|
||||
|
||||
for (iterator o = begin (); o != end (); ++o) {
|
||||
db::Coord l1 = db::coord_traits<db::Coord>::rounded (db::sprod (db::Vector (o->first->p1 () - p1), d) * n);
|
||||
db::Coord l2 = db::coord_traits<db::Coord>::rounded (db::sprod (db::Vector (o->first->p2 () - p1), d) * n);
|
||||
if (o->second == 0 || m_op == EdgeOr) {
|
||||
if (l1 < l2) {
|
||||
a.add (l1, l2, 1, or_jop);
|
||||
} else if (l1 > l2) {
|
||||
a.add (l2, l1, -1, or_jop);
|
||||
}
|
||||
} else {
|
||||
if (l1 < l2) {
|
||||
b.add (l1, l2, 1, or_jop);
|
||||
} else {
|
||||
b.add (l2, l1, -1, or_jop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tl::interval_map<db::Coord, int> q;
|
||||
for (tl::interval_map<db::Coord, int>::const_iterator ia = a.begin (); ia != a.end (); ++ia) {
|
||||
q.add (ia->first.first, ia->first.second, ia->second > 0 ? 1 : (ia->second < 0 ? -1 : 0), or_jop);
|
||||
}
|
||||
|
||||
if (b.begin () == b.end ()) {
|
||||
|
||||
// optimize for empty b
|
||||
if (m_op != EdgeAnd) {
|
||||
for (tl::interval_map<db::Coord, int>::const_iterator ib = b.begin (); ib != b.end (); ++ib) {
|
||||
if (ib->second > 0) {
|
||||
mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.first * n)), p1 + db::Vector (d * (ib->first.second * n))));
|
||||
} else if (ib->second < 0) {
|
||||
mp_output->insert (db::Edge (p1 + db::Vector (d * (ib->first.second * n)), p1 + db::Vector (d * (ib->first.first * n))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (m_op == EdgeAnd) {
|
||||
for (tl::interval_map<db::Coord, int>::const_iterator ib = b.begin (); ib != b.end (); ++ib) {
|
||||
q.add (ib->first.first, ib->first.second, ib->second, and_jop);
|
||||
}
|
||||
} else if (m_op == EdgeNot) {
|
||||
for (tl::interval_map<db::Coord, int>::const_iterator ib = b.begin (); ib != b.end (); ++ib) {
|
||||
q.add (ib->first.first, ib->first.second, ib->second, not_jop);
|
||||
}
|
||||
} else if (m_op == EdgeXor) {
|
||||
for (tl::interval_map<db::Coord, int>::const_iterator ib = b.begin (); ib != b.end (); ++ib) {
|
||||
q.add (ib->first.first, ib->first.second, ib->second, xor_jop);
|
||||
}
|
||||
}
|
||||
|
||||
for (tl::interval_map<db::Coord, int>::const_iterator iq = q.begin (); iq != q.end (); ++iq) {
|
||||
if (iq->second > 0) {
|
||||
mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.first * n)), p1 + db::Vector (d * (iq->first.second * n))));
|
||||
} else if (iq->second < 0) {
|
||||
mp_output->insert (db::Edge (p1 + db::Vector (d * (iq->first.second * n)), p1 + db::Vector (d * (iq->first.first * n))));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
db::EdgeBoolOp m_op;
|
||||
};
|
||||
|
||||
template <class OutputContainer>
|
||||
struct EdgeBooleanClusterCollector
|
||||
: public db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> >
|
||||
{
|
||||
EdgeBooleanClusterCollector (OutputContainer *output, EdgeBoolOp op)
|
||||
: db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> > (EdgeBooleanCluster<OutputContainer> (output, op), op != EdgeAnd /*report single*/)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
// Select edges which are:
|
||||
// 1.) not degenerate
|
||||
// 2.) parallel with some tolerance of roughly 1 dbu
|
||||
// 3.) connected
|
||||
if (! o1->is_degenerate () && ! o2->is_degenerate ()
|
||||
&& fabs ((double) db::vprod (*o1, *o2)) < db::coord_traits<db::Coord>::prec_distance () * std::min (o1->double_length (), o2->double_length ())
|
||||
&& (o1->p1 () == o2->p1 () || o1->p1 () == o2->p2 () || o1->p2 () == o2->p1 () || o1->p2 () == o2->p2 () || o1->coincident (*o2))) {
|
||||
db::cluster_collector<db::Edge, size_t, EdgeBooleanCluster<OutputContainer> >::add (o1, p1, o2, p2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ public:
|
|||
typedef db::coord_traits<C> coord_traits;
|
||||
typedef typename coord_traits::distance_type distance_type;
|
||||
typedef typename coord_traits::area_type area_type;
|
||||
typedef db::object_tag< edge<C> > tag;
|
||||
typedef db::object_tag< edge_pair<C> > tag;
|
||||
|
||||
/**
|
||||
* @brief The default constructor.
|
||||
|
|
@ -89,6 +89,24 @@ public:
|
|||
// .. nothing else ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The (dummy) translation operator
|
||||
*/
|
||||
void translate (const edge_pair<C> &d, db::generic_repository<C> &, db::ArrayRepository &)
|
||||
{
|
||||
*this = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The (dummy) translation operator
|
||||
*/
|
||||
template <class T>
|
||||
void translate (const edge_pair<C> &d, const T &t, db::generic_repository<C> &, db::ArrayRepository &)
|
||||
{
|
||||
*this = d;
|
||||
transform (t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A less operator to establish a sorting order.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@
|
|||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdgePairs.h"
|
||||
#include "dbEmptyEdgePairs.h"
|
||||
#include "dbFlatEdgePairs.h"
|
||||
#include "dbDeepEdgePairs.h"
|
||||
#include "dbOriginalLayerEdgePairs.h"
|
||||
#include "dbEdges.h"
|
||||
#include "dbRegion.h"
|
||||
|
||||
|
|
@ -34,164 +38,152 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
void
|
||||
EdgePairs::insert (const db::Edge &e1, const db::Edge &e2)
|
||||
EdgePairs::EdgePairs ()
|
||||
: mp_delegate (new EmptyEdgePairs ())
|
||||
{
|
||||
m_edge_pairs.push_back (db::EdgePair (e1, e2));
|
||||
m_bbox_valid = false;
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::insert (const edge_pair_type &ep)
|
||||
EdgePairs::~EdgePairs ()
|
||||
{
|
||||
m_edge_pairs.push_back (ep);
|
||||
m_bbox_valid = false;
|
||||
delete mp_delegate;
|
||||
mp_delegate = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
EdgePairs::operator== (const db::EdgePairs &other) const
|
||||
EdgePairs::EdgePairs (EdgePairsDelegate *delegate)
|
||||
: mp_delegate (delegate)
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return false;
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return false;
|
||||
}
|
||||
db::EdgePairs::const_iterator o1 = begin ();
|
||||
db::EdgePairs::const_iterator o2 = other.begin ();
|
||||
while (o1 != end () && o2 != other.end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return false;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return true;
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool
|
||||
EdgePairs::operator< (const db::EdgePairs &other) const
|
||||
EdgePairs::EdgePairs (const EdgePairs &other)
|
||||
: gsi::ObjectBase (), mp_delegate (other.mp_delegate->clone ())
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return empty () < other.empty ();
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return (size () < other.size ());
|
||||
}
|
||||
db::EdgePairs::const_iterator o1 = begin ();
|
||||
db::EdgePairs::const_iterator o2 = other.begin ();
|
||||
while (o1 != end () && o2 != other.end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return *o1 < *o2;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return false;
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
db::EdgePairs &
|
||||
EdgePairs::operator+= (const db::EdgePairs &other)
|
||||
EdgePairs &EdgePairs::operator= (const EdgePairs &other)
|
||||
{
|
||||
if (! other.empty ()) {
|
||||
m_edge_pairs.insert (m_edge_pairs.end (), other.begin (), other.end ());
|
||||
m_bbox_valid = false;
|
||||
if (this != &other) {
|
||||
set_delegate (other.mp_delegate->clone ());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string
|
||||
EdgePairs::to_string (size_t nmax) const
|
||||
EdgePairs::EdgePairs (const RecursiveShapeIterator &si)
|
||||
{
|
||||
std::ostringstream os;
|
||||
const_iterator ep;
|
||||
for (ep = begin (); ep != end () && nmax != 0; ++ep, --nmax) {
|
||||
if (ep != begin ()) {
|
||||
os << ";";
|
||||
mp_delegate = new OriginalLayerEdgePairs (si);
|
||||
}
|
||||
|
||||
EdgePairs::EdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans)
|
||||
{
|
||||
mp_delegate = new OriginalLayerEdgePairs (si, trans);
|
||||
}
|
||||
|
||||
EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss)
|
||||
{
|
||||
mp_delegate = new DeepEdgePairs (si, dss);
|
||||
}
|
||||
|
||||
EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans)
|
||||
{
|
||||
mp_delegate = new DeepEdgePairs (si, dss, trans);
|
||||
}
|
||||
|
||||
template <class Sh>
|
||||
void EdgePairs::insert (const Sh &shape)
|
||||
{
|
||||
flat_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);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void EdgePairs::insert (const db::Shape &shape, const T &trans)
|
||||
{
|
||||
flat_edge_pairs ()->insert (shape, trans);
|
||||
}
|
||||
|
||||
template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::ICplxTrans &);
|
||||
template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::Trans &);
|
||||
template DB_PUBLIC void EdgePairs::insert (const db::Shape &, const db::Disp &);
|
||||
|
||||
void EdgePairs::clear ()
|
||||
{
|
||||
set_delegate (new EmptyEdgePairs ());
|
||||
}
|
||||
|
||||
void EdgePairs::reserve (size_t n)
|
||||
{
|
||||
flat_edge_pairs ()->reserve (n);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
EdgePairs &EdgePairs::transform (const T &trans)
|
||||
{
|
||||
flat_edge_pairs ()->transform (trans);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::ICplxTrans &);
|
||||
template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Trans &);
|
||||
template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Disp &);
|
||||
|
||||
const db::RecursiveShapeIterator &
|
||||
EdgePairs::iter () const
|
||||
{
|
||||
static db::RecursiveShapeIterator def_iter;
|
||||
const db::RecursiveShapeIterator *i = mp_delegate->iter ();
|
||||
return *(i ? i : &def_iter);
|
||||
}
|
||||
|
||||
void EdgePairs::polygons (Region &output, db::Coord e) const
|
||||
{
|
||||
output.set_delegate (mp_delegate->polygons (e));
|
||||
}
|
||||
|
||||
void EdgePairs::edges (Edges &output) const
|
||||
{
|
||||
output.set_delegate (mp_delegate->edges ());
|
||||
}
|
||||
|
||||
void EdgePairs::first_edges (Edges &output) const
|
||||
{
|
||||
output.set_delegate (mp_delegate->first_edges ());
|
||||
}
|
||||
|
||||
void EdgePairs::second_edges (Edges &output) const
|
||||
{
|
||||
output.set_delegate (mp_delegate->second_edges ());
|
||||
}
|
||||
|
||||
void EdgePairs::set_delegate (EdgePairsDelegate *delegate)
|
||||
{
|
||||
if (delegate != mp_delegate) {
|
||||
delete mp_delegate;
|
||||
mp_delegate = delegate;
|
||||
}
|
||||
}
|
||||
|
||||
FlatEdgePairs *EdgePairs::flat_edge_pairs ()
|
||||
{
|
||||
FlatEdgePairs *edge_pairs = dynamic_cast<FlatEdgePairs *> (mp_delegate);
|
||||
if (! edge_pairs) {
|
||||
edge_pairs = new FlatEdgePairs ();
|
||||
if (mp_delegate) {
|
||||
edge_pairs->EdgePairsDelegate::operator= (*mp_delegate);
|
||||
edge_pairs->insert_seq (begin ());
|
||||
}
|
||||
os << ep->to_string ();
|
||||
set_delegate (edge_pairs);
|
||||
}
|
||||
if (ep != end ()) {
|
||||
os << "...";
|
||||
}
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::clear ()
|
||||
{
|
||||
m_edge_pairs.clear ();
|
||||
m_bbox = db::Box ();
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::polygons (Region &output, db::Coord e) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
db::Polygon poly = ep->normalized ().to_polygon (e);
|
||||
if (poly.vertices () >= 3) {
|
||||
output.insert (poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::edges (Edges &output) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
output.insert (ep->first ());
|
||||
output.insert (ep->second ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::first_edges (Edges &output) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
output.insert (ep->first ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::second_edges (Edges &output) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
output.insert (ep->second ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::init ()
|
||||
{
|
||||
m_bbox_valid = false;
|
||||
m_report_progress = false;
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::ensure_bbox_valid () const
|
||||
{
|
||||
if (! m_bbox_valid) {
|
||||
m_bbox = db::Box ();
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
m_bbox += db::Box (ep->first ().p1 (), ep->first ().p2 ());
|
||||
m_bbox += db::Box (ep->second ().p1 (), ep->second ().p2 ());
|
||||
}
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::disable_progress ()
|
||||
{
|
||||
m_report_progress = false;
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::enable_progress (const std::string &progress_desc)
|
||||
{
|
||||
m_report_progress = true;
|
||||
m_progress_desc = progress_desc;
|
||||
return edge_pairs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,198 @@
|
|||
#ifndef HDR_dbEdgePairs
|
||||
#define HDR_dbEdgePairs
|
||||
|
||||
#include "dbEdge.h"
|
||||
#include "dbEdgePair.h"
|
||||
#include "dbEdgePairsDelegate.h"
|
||||
#include "dbShape.h"
|
||||
#include "dbRecursiveShapeIterator.h"
|
||||
|
||||
#include "gsiObject.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Region;
|
||||
class EdgePairFilterBase;
|
||||
class FlatEdgePairs;
|
||||
class EmptyEdgePairs;
|
||||
class Edges;
|
||||
class Region;
|
||||
class DeepShapeStore;
|
||||
class TransformationReducer;
|
||||
|
||||
/**
|
||||
* @brief An edge pair set iterator
|
||||
*
|
||||
* The iterator delivers the edge pairs of the edge pair set
|
||||
*/
|
||||
class DB_PUBLIC EdgePairsIterator
|
||||
{
|
||||
public:
|
||||
typedef EdgePairsIteratorDelegate::value_type value_type;
|
||||
typedef const value_type &reference;
|
||||
typedef const value_type *pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef void difference_type;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
EdgePairsIterator ()
|
||||
: mp_delegate (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor from a delegate
|
||||
* The iterator will take ownership over the delegate
|
||||
*/
|
||||
EdgePairsIterator (EdgePairsIteratorDelegate *delegate)
|
||||
: mp_delegate (delegate)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~EdgePairsIterator ()
|
||||
{
|
||||
delete mp_delegate;
|
||||
mp_delegate = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor and assignment
|
||||
*/
|
||||
EdgePairsIterator (const EdgePairsIterator &other)
|
||||
: mp_delegate (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
EdgePairsIterator &operator= (const EdgePairsIterator &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
delete mp_delegate;
|
||||
mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Returns true, if the iterator is at the end
|
||||
*/
|
||||
bool at_end () const
|
||||
{
|
||||
return mp_delegate == 0 || mp_delegate->at_end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increment
|
||||
*/
|
||||
EdgePairsIterator &operator++ ()
|
||||
{
|
||||
if (mp_delegate) {
|
||||
mp_delegate->increment ();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access
|
||||
*/
|
||||
reference operator* () const
|
||||
{
|
||||
const value_type *value = operator-> ();
|
||||
tl_assert (value != 0);
|
||||
return *value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access
|
||||
*/
|
||||
pointer operator-> () const
|
||||
{
|
||||
return mp_delegate ? mp_delegate->get () : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
EdgePairsIteratorDelegate *mp_delegate;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class allowing delivery of addressable edges
|
||||
*
|
||||
* In some applications (i.e. box scanner), edges need to be taken
|
||||
* by address. The edge set cannot always deliver adressable edges.
|
||||
* This class help providing this ability by keeping a temporary copy
|
||||
* if required.
|
||||
*/
|
||||
|
||||
class DB_PUBLIC AddressableEdgePairDelivery
|
||||
{
|
||||
public:
|
||||
AddressableEdgePairDelivery ()
|
||||
: m_iter (), m_valid (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AddressableEdgePairDelivery (const EdgePairsIterator &iter, bool valid)
|
||||
: m_iter (iter), m_valid (valid)
|
||||
{
|
||||
if (! m_valid && ! m_iter.at_end ()) {
|
||||
m_heap.push_back (*m_iter);
|
||||
}
|
||||
}
|
||||
|
||||
bool at_end () const
|
||||
{
|
||||
return m_iter.at_end ();
|
||||
}
|
||||
|
||||
AddressableEdgePairDelivery &operator++ ()
|
||||
{
|
||||
++m_iter;
|
||||
if (! m_valid && ! m_iter.at_end ()) {
|
||||
m_heap.push_back (*m_iter);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const db::EdgePair *operator-> () const
|
||||
{
|
||||
if (m_valid) {
|
||||
return m_iter.operator-> ();
|
||||
} else {
|
||||
return &m_heap.back ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EdgePairsIterator m_iter;
|
||||
bool m_valid;
|
||||
std::list<db::EdgePair> m_heap;
|
||||
};
|
||||
|
||||
class EdgePairs;
|
||||
|
||||
/**
|
||||
* @brief A base class for edge pair filters
|
||||
*/
|
||||
class DB_PUBLIC EdgePairFilterBase
|
||||
{
|
||||
public:
|
||||
EdgePairFilterBase () { }
|
||||
virtual ~EdgePairFilterBase () { }
|
||||
|
||||
virtual bool selected (const db::EdgePair &edge) const = 0;
|
||||
virtual const TransformationReducer *vars () const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A set of edge pairs
|
||||
|
|
@ -46,92 +230,173 @@ class Edges;
|
|||
* can be converted to polygons or to individual edges.
|
||||
*/
|
||||
class DB_PUBLIC EdgePairs
|
||||
: public gsi::ObjectBase
|
||||
{
|
||||
public:
|
||||
typedef db::Coord coord_type;
|
||||
typedef db::coord_traits<db::Coord> coord_traits;
|
||||
typedef db::EdgePair edge_pair_type;
|
||||
typedef std::vector<edge_pair_type>::const_iterator const_iterator;
|
||||
typedef db::Vector vector_type;
|
||||
typedef db::Point point_type;
|
||||
typedef db::Box box_type;
|
||||
typedef coord_traits::distance_type distance_type;
|
||||
typedef EdgePairsIterator const_iterator;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* This constructor creates an empty edge pair set.
|
||||
*/
|
||||
EdgePairs ()
|
||||
{
|
||||
init ();
|
||||
}
|
||||
EdgePairs ();
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
* @brief Destructor
|
||||
*/
|
||||
bool operator== (const db::EdgePairs &other) const;
|
||||
~EdgePairs ();
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
* @brief Constructor from a delegate
|
||||
*
|
||||
* The region will take ownership of the delegate.
|
||||
*/
|
||||
bool operator!= (const db::EdgePairs &other) const
|
||||
EdgePairs (EdgePairsDelegate *delegate);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
EdgePairs (const EdgePairs &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
EdgePairs &operator= (const EdgePairs &other);
|
||||
|
||||
/**
|
||||
* @brief Constructor from an object
|
||||
*
|
||||
* Creates an edge pair set representing a single instance of that object
|
||||
*/
|
||||
explicit EdgePairs (const db::EdgePair &s)
|
||||
: mp_delegate (0)
|
||||
{
|
||||
return !operator== (other);
|
||||
insert (s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Less operator
|
||||
*/
|
||||
bool operator< (const db::EdgePairs &other) const;
|
||||
|
||||
/**
|
||||
* @brief Joining of edge pair sets
|
||||
* @brief Constructor from an object
|
||||
*
|
||||
* This method will combine the edge pairs from "other" with the egdes of "this".
|
||||
* Creates an edge pair set representing a single instance of that object
|
||||
*/
|
||||
db::EdgePairs operator+ (const db::EdgePairs &other) const
|
||||
explicit EdgePairs (const db::Shape &s)
|
||||
: mp_delegate (0)
|
||||
{
|
||||
db::EdgePairs d (*this);
|
||||
d += other;
|
||||
return d;
|
||||
insert (s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief In-place joining of edge pair sets
|
||||
* @brief Sequence constructor
|
||||
*
|
||||
* Creates an edge set from a sequence of objects. The objects need to be edge pairs.
|
||||
* This version accepts iterators of the begin ... end style.
|
||||
*/
|
||||
db::EdgePairs &operator+= (const db::EdgePairs &other);
|
||||
template <class Iter>
|
||||
explicit EdgePairs (const Iter &b, const Iter &e)
|
||||
: mp_delegate (0)
|
||||
{
|
||||
reserve (e - b);
|
||||
for (Iter i = b; i != e; ++i) {
|
||||
insert (*i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator of the edge pair set
|
||||
* @brief Constructor from a RecursiveShapeIterator
|
||||
*
|
||||
* The iterator delivers the edge pairs of the edge pair set.
|
||||
* Creates an edge pair set from a recursive shape iterator. This allows to feed an edge pair set
|
||||
* from a hierarchy of cells.
|
||||
*/
|
||||
explicit EdgePairs (const RecursiveShapeIterator &si);
|
||||
|
||||
/**
|
||||
* @brief Constructor from a RecursiveShapeIterator with a transformation
|
||||
*
|
||||
* Creates an edge pair set from a recursive shape iterator. This allows to feed an edge pair set
|
||||
* from a hierarchy of cells. The transformation is useful to scale to a specific
|
||||
* DBU for example.
|
||||
*/
|
||||
explicit EdgePairs (const RecursiveShapeIterator &si, const db::ICplxTrans &trans);
|
||||
|
||||
/**
|
||||
* @brief Constructor from a RecursiveShapeIterator providing a deep representation
|
||||
*
|
||||
* This version will create a hierarchical edge pair collection. The DeepShapeStore needs to be provided
|
||||
* during the lifetime of the collection and acts as a heap for optimized data.
|
||||
*/
|
||||
explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss);
|
||||
|
||||
/**
|
||||
* @brief Constructor from a RecursiveShapeIterator providing a deep representation with transformation
|
||||
*/
|
||||
explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans);
|
||||
|
||||
/**
|
||||
* @brief Gets the underlying delegate object
|
||||
*/
|
||||
EdgePairsDelegate *delegate () const
|
||||
{
|
||||
return mp_delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterator of the edge pair set
|
||||
*
|
||||
* The iterator delivers the edges of the edge pair set.
|
||||
* It follows the at_end semantics.
|
||||
*/
|
||||
const_iterator begin () const
|
||||
{
|
||||
return m_edge_pairs.begin ();
|
||||
return EdgePairsIterator (mp_delegate->begin ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator of the edge pair set
|
||||
*
|
||||
* The iterator delivers the edge pairs of the edge pair set.
|
||||
* @brief Delivers a RecursiveShapeIterator pointing to the edge pairs plus the necessary transformation
|
||||
*/
|
||||
const_iterator end () const
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const
|
||||
{
|
||||
return m_edge_pairs.end ();
|
||||
return mp_delegate->begin_iter ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert an edge pair into the edge pair set
|
||||
* @brief Inserts the given shape (working object) into the edge pair set
|
||||
*/
|
||||
void insert (const db::Edge &e1, const db::Edge &e2);
|
||||
template <class Sh>
|
||||
void insert (const Sh &shape);
|
||||
|
||||
/**
|
||||
* @brief Insert an edge pair into the edge pair set
|
||||
* @brief Inserts an edge pair made from two edges
|
||||
*/
|
||||
void insert (const edge_pair_type &ep);
|
||||
void insert (const db::Edge &e1, const db::Edge &e2)
|
||||
{
|
||||
insert (db::EdgePair (e1, e2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert a shape reference into the edge pair set
|
||||
*/
|
||||
void insert (const db::Shape &shape);
|
||||
|
||||
/**
|
||||
* @brief Insert a transformed shape into the edge pair set
|
||||
*/
|
||||
template <class T>
|
||||
void insert (const db::Shape &shape, const T &trans);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the edge pair set is empty
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_edge_pairs.empty ();
|
||||
return mp_delegate->empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -139,87 +404,68 @@ public:
|
|||
*/
|
||||
size_t size () const
|
||||
{
|
||||
return m_edge_pairs.size ();
|
||||
return mp_delegate->size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a string representing the edge pair set
|
||||
*
|
||||
* nmax specifies how many edge pairs are included (set to std::numeric_limits<size_t>::max() for "all".
|
||||
*/
|
||||
std::string to_string (size_t nmax = 10) const;
|
||||
std::string to_string (size_t nmax = 10) const
|
||||
{
|
||||
return mp_delegate->to_string (nmax);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear the edge pair set
|
||||
* @brief Clears the edge set
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Reserve memory for the given number of polygons
|
||||
* @brief Reserve memory for the given number of edges
|
||||
*/
|
||||
void reserve (size_t n)
|
||||
{
|
||||
m_edge_pairs.reserve (n);
|
||||
}
|
||||
void reserve (size_t n);
|
||||
|
||||
/**
|
||||
* @brief Returns the bounding box of the edge pair set
|
||||
*/
|
||||
Box bbox () const
|
||||
{
|
||||
ensure_bbox_valid ();
|
||||
return m_bbox;
|
||||
return mp_delegate->bbox ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Filters the edge pair set
|
||||
* @brief Filters the edge pairs
|
||||
*
|
||||
* This method will keep all edge pairs for which the filter returns true.
|
||||
* Merged semantics applies.
|
||||
*/
|
||||
template <class F>
|
||||
EdgePairs &filter (F &filter)
|
||||
EdgePairs &filter (const EdgePairFilterBase &filter)
|
||||
{
|
||||
std::vector <edge_pair_type>::iterator ew = m_edge_pairs.begin ();
|
||||
for (const_iterator e = begin (); e != end (); ++e) {
|
||||
if (filter (*e)) {
|
||||
*ew++ = *e;
|
||||
}
|
||||
}
|
||||
m_edge_pairs.erase (ew, m_edge_pairs.end ());
|
||||
set_delegate (mp_delegate->filter_in_place (filter));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the filtered edge pairs
|
||||
*
|
||||
* This method will return a new region with only those edge pairs which
|
||||
* This method will return a new region with only those edge pairs which
|
||||
* conform to the filter criterion.
|
||||
*/
|
||||
template <class F>
|
||||
EdgePairs filtered (F &filter) const
|
||||
EdgePairs filtered (const EdgePairFilterBase &filter) const
|
||||
{
|
||||
EdgePairs d;
|
||||
for (const_iterator e = begin (); e != end (); ++e) {
|
||||
if (filter (*e)) {
|
||||
d.insert (*e);
|
||||
}
|
||||
}
|
||||
return d;
|
||||
return EdgePairs (mp_delegate->filtered (filter));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transform the edge pair set
|
||||
* @brief Transforms the edge pair set
|
||||
*/
|
||||
template <class T>
|
||||
EdgePairs &transform (const T &trans)
|
||||
{
|
||||
for (std::vector <edge_pair_type>::iterator e = m_edge_pairs.begin (); e != m_edge_pairs.end (); ++e) {
|
||||
e->transform (trans);
|
||||
}
|
||||
m_bbox_valid = false;
|
||||
return *this;
|
||||
}
|
||||
EdgePairs &transform (const T &trans);
|
||||
|
||||
/**
|
||||
* @brief Returns the transformed edge set
|
||||
* @brief Returns the transformed edge pair set
|
||||
*/
|
||||
template <class T>
|
||||
EdgePairs transformed (const T &trans) const
|
||||
|
|
@ -230,17 +476,124 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Swap with the other region
|
||||
* @brief Swaps with the other edge set
|
||||
*/
|
||||
void swap (db::EdgePairs &other)
|
||||
{
|
||||
std::swap (m_edge_pairs, other.m_edge_pairs);
|
||||
std::swap (m_bbox, other.m_bbox);
|
||||
std::swap (m_bbox_valid, other.m_bbox_valid);
|
||||
std::swap (other.mp_delegate, mp_delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert to polygons
|
||||
* @brief Joining of edge pair set
|
||||
*
|
||||
* This method joins the edge pair sets.
|
||||
*/
|
||||
EdgePairs operator+ (const EdgePairs &other) const
|
||||
{
|
||||
return EdgePairs (mp_delegate->add (other));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief In-place edge pair set joining
|
||||
*/
|
||||
EdgePairs &operator+= (const EdgePairs &other)
|
||||
{
|
||||
set_delegate (mp_delegate->add_in_place (other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns all edge pairs which are in the other edge pair set
|
||||
*
|
||||
* This method will return all edge pairs which are part of another edge pair set.
|
||||
* The match is done exactly.
|
||||
* The "invert" flag can be used to invert the sense, i.e. with
|
||||
* "invert" set to true, this method will return all edge pairs not
|
||||
* in the other edge pair set.
|
||||
*/
|
||||
EdgePairs in (const EdgePairs &other, bool invert = false) const
|
||||
{
|
||||
return EdgePairs (mp_delegate->in (other, invert));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the nth edge pair
|
||||
*
|
||||
* This operation is available only for flat regions - i.e. such for which
|
||||
* "has_valid_edge_pairs" is true.
|
||||
*/
|
||||
const db::EdgePair *nth (size_t n) const
|
||||
{
|
||||
return mp_delegate->nth (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forces flattening of the edge pair collection
|
||||
*
|
||||
* This method will turn any edge pair collection into a flat one.
|
||||
*/
|
||||
void flatten ()
|
||||
{
|
||||
flat_edge_pairs ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the edge pair set has valid edges stored within itself
|
||||
*
|
||||
* If the region has valid edges, it is permissable to use the edge pair's addresses
|
||||
* from the iterator. Furthermore, the random access operator nth() is available.
|
||||
*/
|
||||
bool has_valid_edge_pairs () const
|
||||
{
|
||||
return mp_delegate->has_valid_edge_pairs ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an addressable delivery for edge pairs
|
||||
*
|
||||
* This object allows accessing the edge pairs by address, even if they
|
||||
* are not delivered from a container. The magic is a heap object
|
||||
* inside the delivery object. Hence, the deliver object must persist
|
||||
* as long as the addresses are required.
|
||||
*/
|
||||
AddressableEdgePairDelivery addressable_edge_pairs () const
|
||||
{
|
||||
return AddressableEdgePairDelivery (begin (), has_valid_edge_pairs ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the internal iterator
|
||||
*
|
||||
* This method is intended for users who know what they are doing
|
||||
*/
|
||||
const db::RecursiveShapeIterator &iter () const;
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const db::EdgePairs &other) const
|
||||
{
|
||||
return mp_delegate->equals (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
*/
|
||||
bool operator!= (const db::EdgePairs &other) const
|
||||
{
|
||||
return ! mp_delegate->equals (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Less operator
|
||||
*/
|
||||
bool operator< (const db::EdgePairs &other) const
|
||||
{
|
||||
return mp_delegate->less (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts to polygons
|
||||
*
|
||||
* Note: because of the include hierarchy we can't use a direct return value.
|
||||
*
|
||||
|
|
@ -263,7 +616,7 @@ public:
|
|||
*/
|
||||
void edges (Edges &output) const;
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief Returns the first edges
|
||||
*
|
||||
* Note: because of the include hierarchy we can't use a direct return value.
|
||||
|
|
@ -273,7 +626,7 @@ public:
|
|||
*/
|
||||
void first_edges (Edges &output) const;
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief Returns the second edges
|
||||
*
|
||||
* Note: because of the include hierarchy we can't use a direct return value.
|
||||
|
|
@ -288,22 +641,44 @@ public:
|
|||
*
|
||||
* @param progress_text The description text of the progress object
|
||||
*/
|
||||
void enable_progress (const std::string &progress_desc = std::string ());
|
||||
void enable_progress (const std::string &progress_desc = std::string ())
|
||||
{
|
||||
mp_delegate->enable_progress (progress_desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable progress reporting
|
||||
*/
|
||||
void disable_progress ();
|
||||
void disable_progress ()
|
||||
{
|
||||
mp_delegate->disable_progress ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts the edge pair collection into the given layout, cell and layer
|
||||
* If the edge pair collection is a hierarchical region, the hierarchy is copied into the
|
||||
* layout's hierarchy.
|
||||
*/
|
||||
void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
return mp_delegate->insert_into (layout, into_cell, into_layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts the edge pair collection into the given layout, cell and layer as polygons with the given enlargement
|
||||
* If the edge pair collection is a hierarchical region, the hierarchy is copied into the
|
||||
* layout's hierarchy.
|
||||
*/
|
||||
void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const
|
||||
{
|
||||
return mp_delegate->insert_into_as_polygons (layout, into_cell, into_layer, enl);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<edge_pair_type> m_edge_pairs;
|
||||
mutable db::Box m_bbox;
|
||||
mutable bool m_bbox_valid;
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
EdgePairsDelegate *mp_delegate;
|
||||
|
||||
void init ();
|
||||
void ensure_bbox_valid () const;
|
||||
void set_delegate (EdgePairsDelegate *delegate);
|
||||
FlatEdgePairs *flat_edge_pairs ();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbEdgePairsDelegate.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
|
||||
EdgePairsDelegate::EdgePairsDelegate ()
|
||||
{
|
||||
m_report_progress = false;
|
||||
}
|
||||
|
||||
EdgePairsDelegate::EdgePairsDelegate (const EdgePairsDelegate &other)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
EdgePairsDelegate &
|
||||
EdgePairsDelegate::operator= (const EdgePairsDelegate &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_report_progress = other.m_report_progress;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EdgePairsDelegate::~EdgePairsDelegate ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void EdgePairsDelegate::enable_progress (const std::string &progress_desc)
|
||||
{
|
||||
m_report_progress = true;
|
||||
m_progress_desc = progress_desc;
|
||||
}
|
||||
|
||||
void EdgePairsDelegate::disable_progress ()
|
||||
{
|
||||
m_report_progress = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbEdgePairsDelegate
|
||||
#define HDR_dbEdgePairsDelegate
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdgePair.h"
|
||||
#include "tlUniqueId.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
class RecursiveShapeIterator;
|
||||
class EdgePairs;
|
||||
class EdgePairFilterBase;
|
||||
class RegionDelegate;
|
||||
class EdgesDelegate;
|
||||
class Layout;
|
||||
|
||||
/**
|
||||
* @brief The edge pair set iterator delegate
|
||||
*/
|
||||
class DB_PUBLIC EdgePairsIteratorDelegate
|
||||
{
|
||||
public:
|
||||
EdgePairsIteratorDelegate () { }
|
||||
virtual ~EdgePairsIteratorDelegate () { }
|
||||
|
||||
typedef db::EdgePair value_type;
|
||||
|
||||
virtual bool at_end () const = 0;
|
||||
virtual void increment () = 0;
|
||||
virtual const value_type *get () const = 0;
|
||||
virtual EdgePairsIteratorDelegate *clone () const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The delegate for the actual edge set implementation
|
||||
*/
|
||||
class DB_PUBLIC EdgePairsDelegate
|
||||
: public tl::UniqueId
|
||||
{
|
||||
public:
|
||||
typedef db::Coord coord_type;
|
||||
typedef db::coord_traits<db::Coord> coord_traits;
|
||||
typedef db::EdgePair edge_pair_type;
|
||||
typedef db::Vector vector_type;
|
||||
typedef db::Point point_type;
|
||||
typedef db::Box box_type;
|
||||
|
||||
EdgePairsDelegate ();
|
||||
virtual ~EdgePairsDelegate ();
|
||||
|
||||
EdgePairsDelegate (const EdgePairsDelegate &other);
|
||||
EdgePairsDelegate &operator= (const EdgePairsDelegate &other);
|
||||
|
||||
virtual EdgePairsDelegate *clone () const = 0;
|
||||
|
||||
void enable_progress (const std::string &progress_desc);
|
||||
void disable_progress ();
|
||||
|
||||
// dummy features to harmonize the interface of region, edges and edge pair delegates
|
||||
void set_merged_semantics (bool) { }
|
||||
bool merged_semantics () const { return false; }
|
||||
void set_is_merged (bool) { }
|
||||
bool is_merged () const { return false; }
|
||||
|
||||
virtual std::string to_string (size_t nmax) const = 0;
|
||||
|
||||
virtual EdgePairsIteratorDelegate *begin () const = 0;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const = 0;
|
||||
|
||||
virtual bool empty () const = 0;
|
||||
virtual size_t size () const = 0;
|
||||
|
||||
virtual Box bbox () const = 0;
|
||||
|
||||
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) = 0;
|
||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const = 0;
|
||||
virtual EdgesDelegate *edges () const = 0;
|
||||
virtual EdgesDelegate *first_edges () const = 0;
|
||||
virtual EdgesDelegate *second_edges () const = 0;
|
||||
|
||||
virtual EdgePairsDelegate *add_in_place (const EdgePairs &other) = 0;
|
||||
virtual EdgePairsDelegate *add (const EdgePairs &other) const = 0;
|
||||
|
||||
virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const = 0;
|
||||
virtual bool has_valid_edge_pairs () const = 0;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const = 0;
|
||||
|
||||
virtual bool equals (const EdgePairs &other) const = 0;
|
||||
virtual bool less (const EdgePairs &other) const = 0;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const = 0;
|
||||
virtual void insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const = 0;
|
||||
|
||||
protected:
|
||||
const std::string &progress_desc () const
|
||||
{
|
||||
return m_progress_desc;
|
||||
}
|
||||
|
||||
bool report_progress () const
|
||||
{
|
||||
return m_report_progress;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -975,7 +975,7 @@ BooleanOp2::compare_ns () const
|
|||
// EdgeProcessor implementation
|
||||
|
||||
EdgeProcessor::EdgeProcessor (bool report_progress, const std::string &progress_desc)
|
||||
: m_report_progress (report_progress), m_progress_desc (progress_desc)
|
||||
: m_report_progress (report_progress), m_progress_desc (progress_desc), m_base_verbosity (30)
|
||||
{
|
||||
mp_work_edges = new std::vector <WorkEdge> ();
|
||||
mp_cpvector = new std::vector <CutPoints> ();
|
||||
|
|
@ -1006,6 +1006,12 @@ EdgeProcessor::enable_progress (const std::string &progress_desc)
|
|||
m_progress_desc = progress_desc;
|
||||
}
|
||||
|
||||
void
|
||||
EdgeProcessor::set_base_verbosity (int bv)
|
||||
{
|
||||
m_base_verbosity = bv;
|
||||
}
|
||||
|
||||
void
|
||||
EdgeProcessor::reserve (size_t n)
|
||||
{
|
||||
|
|
@ -1534,7 +1540,7 @@ get_intersections_per_band_any (std::vector <CutPoints> &cutpoints, std::vector
|
|||
void
|
||||
EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, "EdgeProcessor: process");
|
||||
tl::SelfTimer timer (tl::verbosity () >= m_base_verbosity, "EdgeProcessor: process");
|
||||
|
||||
bool prefer_touch = op.prefer_touch ();
|
||||
bool selects_edges = op.selects_edges ();
|
||||
|
|
@ -1749,7 +1755,7 @@ EdgeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op)
|
|||
#endif
|
||||
|
||||
|
||||
tl::SelfTimer timer2 (tl::verbosity () >= 41, "EdgeProcessor: production");
|
||||
tl::SelfTimer timer2 (tl::verbosity () >= m_base_verbosity + 10, "EdgeProcessor: production");
|
||||
|
||||
// step 4: compute the result edges
|
||||
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ private:
|
|||
* based on a generic operator.
|
||||
*/
|
||||
template <class F>
|
||||
class DB_PUBLIC GenericMerge
|
||||
class DB_PUBLIC_TEMPLATE GenericMerge
|
||||
: public EdgeEvaluatorBase
|
||||
{
|
||||
public:
|
||||
|
|
@ -606,6 +606,14 @@ public:
|
|||
*/
|
||||
void disable_progress ();
|
||||
|
||||
/**
|
||||
* @brief Base verbosity for timer reporting
|
||||
*
|
||||
* The default value is 30. Basic timing will be reported for > base_verbosity, detailed timing
|
||||
* for > base_verbosity + 10.
|
||||
*/
|
||||
void set_base_verbosity (int bv);
|
||||
|
||||
/**
|
||||
* @brief Reserve space for at least n edges
|
||||
*/
|
||||
|
|
@ -943,6 +951,7 @@ private:
|
|||
std::vector <CutPoints> *mp_cpvector;
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
int m_base_verbosity;
|
||||
|
||||
static size_t count_edges (const db::Polygon &q)
|
||||
{
|
||||
|
|
|
|||
1477
src/db/db/dbEdges.cc
1477
src/db/db/dbEdges.cc
File diff suppressed because it is too large
Load Diff
1528
src/db/db/dbEdges.h
1528
src/db/db/dbEdges.h
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbEdgesDelegate.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
|
||||
EdgesDelegate::EdgesDelegate ()
|
||||
{
|
||||
m_base_verbosity = 30;
|
||||
m_report_progress = false;
|
||||
m_merged_semantics = true;
|
||||
m_strict_handling = false;
|
||||
}
|
||||
|
||||
EdgesDelegate::EdgesDelegate (const EdgesDelegate &other)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
EdgesDelegate &
|
||||
EdgesDelegate::operator= (const EdgesDelegate &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_base_verbosity = other.m_base_verbosity;
|
||||
m_report_progress = other.m_report_progress;
|
||||
m_merged_semantics = other.m_merged_semantics;
|
||||
m_strict_handling = other.m_strict_handling;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EdgesDelegate::~EdgesDelegate ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void EdgesDelegate::set_base_verbosity (int vb)
|
||||
{
|
||||
m_base_verbosity = vb;
|
||||
}
|
||||
|
||||
void EdgesDelegate::enable_progress (const std::string &progress_desc)
|
||||
{
|
||||
m_report_progress = true;
|
||||
m_progress_desc = progress_desc;
|
||||
}
|
||||
|
||||
void EdgesDelegate::disable_progress ()
|
||||
{
|
||||
m_report_progress = false;
|
||||
}
|
||||
|
||||
void EdgesDelegate::set_merged_semantics (bool f)
|
||||
{
|
||||
if (f != m_merged_semantics) {
|
||||
m_merged_semantics = f;
|
||||
merged_semantics_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void EdgesDelegate::set_strict_handling (bool f)
|
||||
{
|
||||
m_strict_handling = f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbEdgesDelegate
|
||||
#define HDR_dbEdgesDelegate
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdge.h"
|
||||
#include "dbEdgePairs.h"
|
||||
#include "dbEdgePairRelations.h"
|
||||
#include "tlUniqueId.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief A base class for edge filters
|
||||
*/
|
||||
class DB_PUBLIC EdgeFilterBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
EdgeFilterBase () { }
|
||||
|
||||
virtual ~EdgeFilterBase () { }
|
||||
|
||||
/**
|
||||
* @brief Filters the edge
|
||||
* If this method returns true, the polygon is kept. Otherwise it's discarded.
|
||||
*/
|
||||
virtual bool selected (const db::Edge &edge) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the transformation reducer for building cell variants
|
||||
* This method may return 0. In this case, not cell variants are built.
|
||||
*/
|
||||
virtual const TransformationReducer *vars () const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the filter wants raw (not merged) input
|
||||
*/
|
||||
virtual bool requires_raw_input () const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the filter wants to build variants
|
||||
* If not true, the filter accepts shape propagation as variant resolution.
|
||||
*/
|
||||
virtual bool wants_variants () const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A template base class for edge processors
|
||||
*
|
||||
* A polygon processor can turn a edge into something else.
|
||||
*/
|
||||
template <class Result>
|
||||
class DB_PUBLIC edge_processor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
edge_processor () { }
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~edge_processor () { }
|
||||
|
||||
/**
|
||||
* @brief Performs the actual processing
|
||||
* This method will take the input edge from "edge" and puts the results into "res".
|
||||
* "res" can be empty - in this case, the edge will be skipped.
|
||||
*/
|
||||
virtual void process (const db::Edge &edge, std::vector<Result> &res) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the transformation reducer for building cell variants
|
||||
* This method may return 0. In this case, not cell variants are built.
|
||||
*/
|
||||
virtual const TransformationReducer *vars () const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the result of this operation can be regarded "merged" always.
|
||||
*/
|
||||
virtual bool result_is_merged () const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the result of this operation must not be merged.
|
||||
* This feature can be used, if the result represents "degenerated" objects such
|
||||
* as point-like edges. These must not be merged. Otherwise they disappear.
|
||||
*/
|
||||
virtual bool result_must_not_be_merged () const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the processor wants raw (not merged) input
|
||||
*/
|
||||
virtual bool requires_raw_input () const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the processor wants to build variants
|
||||
* If not true, the processor accepts shape propagation as variant resolution.
|
||||
*/
|
||||
virtual bool wants_variants () const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A edge processor base class
|
||||
*/
|
||||
class DB_PUBLIC EdgeProcessorBase
|
||||
: public edge_processor<db::Edge>
|
||||
{
|
||||
// .. nothing yet ..
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An edge-to-polygon processor base class
|
||||
*/
|
||||
class DB_PUBLIC EdgeToPolygonProcessorBase
|
||||
: public edge_processor<db::Polygon>
|
||||
{
|
||||
// .. nothing yet ..
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An edge-to-edge pair processor base class
|
||||
*/
|
||||
class DB_PUBLIC EdgeToEdgePairProcessorBase
|
||||
: public edge_processor<db::EdgePair>
|
||||
{
|
||||
// .. nothing yet ..
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A common definition for the boolean operations available on edges
|
||||
*/
|
||||
enum EdgeBoolOp { EdgeOr, EdgeNot, EdgeXor, EdgeAnd };
|
||||
|
||||
class RecursiveShapeIterator;
|
||||
class EdgeFilterBase;
|
||||
class EdgePairsDelegate;
|
||||
class RegionDelegate;
|
||||
|
||||
/**
|
||||
* @brief The edge set iterator delegate
|
||||
*/
|
||||
class DB_PUBLIC EdgesIteratorDelegate
|
||||
{
|
||||
public:
|
||||
EdgesIteratorDelegate () { }
|
||||
virtual ~EdgesIteratorDelegate () { }
|
||||
|
||||
typedef db::Edge value_type;
|
||||
|
||||
virtual bool at_end () const = 0;
|
||||
virtual void increment () = 0;
|
||||
virtual const value_type *get () const = 0;
|
||||
virtual EdgesIteratorDelegate *clone () const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The delegate for the actual edge set implementation
|
||||
*/
|
||||
class DB_PUBLIC EdgesDelegate
|
||||
: public tl::UniqueId
|
||||
{
|
||||
public:
|
||||
typedef db::Coord coord_type;
|
||||
typedef db::coord_traits<db::Coord> coord_traits;
|
||||
typedef db::Edge edge_type;
|
||||
typedef db::Vector vector_type;
|
||||
typedef db::Point point_type;
|
||||
typedef db::Box box_type;
|
||||
typedef coord_traits::distance_type distance_type;
|
||||
typedef coord_traits::distance_type length_type;
|
||||
|
||||
EdgesDelegate ();
|
||||
virtual ~EdgesDelegate ();
|
||||
|
||||
EdgesDelegate (const EdgesDelegate &other);
|
||||
EdgesDelegate &operator= (const EdgesDelegate &other);
|
||||
|
||||
virtual EdgesDelegate *clone () const = 0;
|
||||
|
||||
void set_base_verbosity (int vb);
|
||||
int base_verbosity () const
|
||||
{
|
||||
return m_base_verbosity;
|
||||
}
|
||||
|
||||
void enable_progress (const std::string &progress_desc);
|
||||
void disable_progress ();
|
||||
|
||||
void set_merged_semantics (bool f);
|
||||
bool merged_semantics () const
|
||||
{
|
||||
return m_merged_semantics;
|
||||
}
|
||||
|
||||
void set_strict_handling (bool f);
|
||||
bool strict_handling () const
|
||||
{
|
||||
return m_strict_handling;
|
||||
}
|
||||
|
||||
virtual std::string to_string (size_t nmax) const = 0;
|
||||
|
||||
virtual EdgesIteratorDelegate *begin () const = 0;
|
||||
virtual EdgesIteratorDelegate *begin_merged () const = 0;
|
||||
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const = 0;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const = 0;
|
||||
|
||||
virtual bool empty () const = 0;
|
||||
virtual bool is_merged () const = 0;
|
||||
virtual size_t size () const = 0;
|
||||
|
||||
virtual distance_type length (const db::Box &box) const = 0;
|
||||
virtual Box bbox () const = 0;
|
||||
|
||||
virtual EdgePairsDelegate *width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0;
|
||||
virtual EdgePairsDelegate *space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0;
|
||||
virtual EdgePairsDelegate *enclosing_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0;
|
||||
virtual EdgePairsDelegate *overlap_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0;
|
||||
virtual EdgePairsDelegate *separation_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0;
|
||||
virtual EdgePairsDelegate *inside_check (const Edges &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0;
|
||||
|
||||
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter) = 0;
|
||||
virtual EdgesDelegate *filtered (const EdgeFilterBase &filter) const = 0;
|
||||
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &filter) = 0;
|
||||
virtual EdgesDelegate *processed (const EdgeProcessorBase &filter) const = 0;
|
||||
virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const = 0;
|
||||
virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &filter) const = 0;
|
||||
|
||||
virtual EdgesDelegate *merged_in_place () = 0;
|
||||
virtual EdgesDelegate *merged () const = 0;
|
||||
|
||||
virtual EdgesDelegate *and_with (const Edges &other) const = 0;
|
||||
virtual EdgesDelegate *and_with (const Region &other) const = 0;
|
||||
virtual EdgesDelegate *not_with (const Edges &other) const = 0;
|
||||
virtual EdgesDelegate *not_with (const Region &other) const = 0;
|
||||
virtual EdgesDelegate *xor_with (const Edges &other) const = 0;
|
||||
virtual EdgesDelegate *or_with (const Edges &other) const = 0;
|
||||
virtual EdgesDelegate *add_in_place (const Edges &other) = 0;
|
||||
virtual EdgesDelegate *add (const Edges &other) const = 0;
|
||||
|
||||
virtual RegionDelegate *extended (coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i, bool join) const = 0;
|
||||
|
||||
virtual EdgesDelegate *inside_part (const Region &other) const = 0;
|
||||
virtual EdgesDelegate *outside_part (const Region &other) const = 0;
|
||||
virtual EdgesDelegate *selected_interacting (const Region &other) const = 0;
|
||||
virtual EdgesDelegate *selected_not_interacting (const Region &other) const = 0;
|
||||
virtual EdgesDelegate *selected_interacting (const Edges &other) const = 0;
|
||||
virtual EdgesDelegate *selected_not_interacting (const Edges &other) const = 0;
|
||||
|
||||
virtual EdgesDelegate *in (const Edges &other, bool invert) const = 0;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const = 0;
|
||||
virtual bool has_valid_edges () const = 0;
|
||||
virtual bool has_valid_merged_edges () const = 0;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const = 0;
|
||||
|
||||
virtual bool equals (const Edges &other) const = 0;
|
||||
virtual bool less (const Edges &other) const = 0;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const = 0;
|
||||
|
||||
protected:
|
||||
const std::string &progress_desc () const
|
||||
{
|
||||
return m_progress_desc;
|
||||
}
|
||||
|
||||
bool report_progress () const
|
||||
{
|
||||
return m_report_progress;
|
||||
}
|
||||
|
||||
virtual void merged_semantics_changed () { }
|
||||
|
||||
private:
|
||||
bool m_merged_semantics;
|
||||
bool m_strict_handling;
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
int m_base_verbosity;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbEdgesUtils.h"
|
||||
#include "dbRegion.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// JoinEdgesCluster implementation
|
||||
|
||||
JoinEdgesCluster::JoinEdgesCluster (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i)
|
||||
: mp_output (output), m_ext_b (ext_b), m_ext_e (ext_e), m_ext_o (ext_o), m_ext_i (ext_i)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
JoinEdgesCluster::finish ()
|
||||
{
|
||||
std::multimap<db::Point, iterator> objects_by_p1;
|
||||
std::multimap<db::Point, iterator> objects_by_p2;
|
||||
for (iterator o = begin (); o != end (); ++o) {
|
||||
if (o->first->p1 () != o->first->p2 ()) {
|
||||
objects_by_p1.insert (std::make_pair (o->first->p1 (), o));
|
||||
objects_by_p2.insert (std::make_pair (o->first->p2 (), o));
|
||||
}
|
||||
}
|
||||
|
||||
while (! objects_by_p2.empty ()) {
|
||||
|
||||
tl_assert (! objects_by_p1.empty ());
|
||||
|
||||
// Find the beginning of a new sequence
|
||||
std::multimap<db::Point, iterator>::iterator j0 = objects_by_p1.begin ();
|
||||
std::multimap<db::Point, iterator>::iterator j = j0;
|
||||
do {
|
||||
std::multimap<db::Point, iterator>::iterator jj = objects_by_p2.find (j->first);
|
||||
if (jj == objects_by_p2.end ()) {
|
||||
break;
|
||||
} else {
|
||||
j = objects_by_p1.find (jj->second->first->p1 ());
|
||||
tl_assert (j != objects_by_p1.end ());
|
||||
}
|
||||
} while (j != j0);
|
||||
|
||||
iterator i = j->second;
|
||||
|
||||
// determine a sequence
|
||||
// TODO: this chooses any solution in case of forks. Choose a specific one?
|
||||
std::vector<db::Point> pts;
|
||||
pts.push_back (i->first->p1 ());
|
||||
|
||||
do {
|
||||
|
||||
// record the next point
|
||||
pts.push_back (i->first->p2 ());
|
||||
|
||||
// remove the edge as it's taken
|
||||
std::multimap<db::Point, iterator>::iterator jj;
|
||||
for (jj = objects_by_p2.find (i->first->p2 ()); jj != objects_by_p2.end () && jj->first == i->first->p2 (); ++jj) {
|
||||
if (jj->second == i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tl_assert (jj != objects_by_p2.end () && jj->second == i);
|
||||
objects_by_p2.erase (jj);
|
||||
objects_by_p1.erase (j);
|
||||
|
||||
// process along the edge to the next one
|
||||
// TODO: this chooses any solution in case of forks. Choose a specific one?
|
||||
j = objects_by_p1.find (i->first->p2 ());
|
||||
if (j != objects_by_p1.end ()) {
|
||||
i = j->second;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
bool cyclic = (pts.back () == pts.front ());
|
||||
|
||||
if (! cyclic) {
|
||||
|
||||
// non-cyclic sequence
|
||||
db::Path path (pts.begin (), pts.end (), 0, m_ext_b, m_ext_e, false);
|
||||
std::vector<db::Point> hull;
|
||||
path.hull (hull, m_ext_o, m_ext_i);
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (hull.begin (), hull.end ());
|
||||
mp_output->put (poly);
|
||||
|
||||
} else {
|
||||
|
||||
// we have a loop: form a contour by using the polygon size functions and a "Not" to form the hole
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (pts.begin (), pts.end ());
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
db::PolygonGenerator pg (*mp_output, false, true);
|
||||
|
||||
int mode_a = -1, mode_b = -1;
|
||||
|
||||
if (m_ext_o == 0) {
|
||||
ep.insert (poly, 0);
|
||||
} else {
|
||||
db::Polygon sized_poly (poly);
|
||||
sized_poly.size (m_ext_o, m_ext_o, 2 /*sizing mode*/);
|
||||
ep.insert (sized_poly, 0);
|
||||
mode_a = 1;
|
||||
}
|
||||
|
||||
if (m_ext_i == 0) {
|
||||
ep.insert (poly, 1);
|
||||
} else {
|
||||
db::Polygon sized_poly (poly);
|
||||
sized_poly.size (-m_ext_i, -m_ext_i, 2 /*sizing mode*/);
|
||||
ep.insert (sized_poly, 1);
|
||||
mode_b = 1;
|
||||
}
|
||||
|
||||
db::BooleanOp2 op (db::BooleanOp::ANotB, mode_a, mode_b);
|
||||
ep.process (pg, op);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// extended_edge implementation
|
||||
|
||||
db::Polygon
|
||||
extended_edge (const db::Edge &edge, db::Coord ext_b, db::Coord ext_e, db::Coord ext_o, db::Coord ext_i)
|
||||
{
|
||||
db::DVector d;
|
||||
if (edge.is_degenerate ()) {
|
||||
d = db::DVector (1.0, 0.0);
|
||||
} else {
|
||||
d = db::DVector (edge.d ()) * (1.0 / edge.double_length ());
|
||||
}
|
||||
|
||||
db::DVector n (-d.y (), d.x ());
|
||||
|
||||
db::Point pts[4] = {
|
||||
db::Point (db::DPoint (edge.p1 ()) - d * double (ext_b) + n * double (ext_o)),
|
||||
db::Point (db::DPoint (edge.p2 ()) + d * double (ext_e) + n * double (ext_o)),
|
||||
db::Point (db::DPoint (edge.p2 ()) + d * double (ext_e) - n * double (ext_i)),
|
||||
db::Point (db::DPoint (edge.p1 ()) - d * double (ext_b) - n * double (ext_i)),
|
||||
};
|
||||
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (pts + 0, pts + 4);
|
||||
return poly;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbEdgesUtils
|
||||
#define HDR_dbEdgesUtils
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbEdges.h"
|
||||
#include "dbBoxScanner.h"
|
||||
#include "dbPolygonTools.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
class PolygonSink;
|
||||
|
||||
/**
|
||||
* @brief An edge length filter for use with Edges::filter or Edges::filtered
|
||||
*
|
||||
* This filter has two parameters: lmin and lmax.
|
||||
* It will filter all edges for which the length is >= lmin and < lmax.
|
||||
* There is an "invert" flag which allows to select all edges not
|
||||
* matching the criterion.
|
||||
*/
|
||||
|
||||
struct DB_PUBLIC EdgeLengthFilter
|
||||
: public EdgeFilterBase
|
||||
{
|
||||
typedef db::Edge::distance_type length_type;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param lmin The minimum length
|
||||
* @param lmax The maximum length
|
||||
* @param inverse If set to true, only polygons not matching this criterion will be filtered
|
||||
*/
|
||||
EdgeLengthFilter (length_type lmin, length_type lmax, bool inverse)
|
||||
: m_lmin (lmin), m_lmax (lmax), m_inverse (inverse)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the edge length matches the criterion
|
||||
*/
|
||||
virtual bool selected (const db::Edge &edge) const
|
||||
{
|
||||
length_type l = edge.length ();
|
||||
if (! m_inverse) {
|
||||
return l >= m_lmin && l < m_lmax;
|
||||
} else {
|
||||
return ! (l >= m_lmin && l < m_lmax);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This filter is isotropic
|
||||
*/
|
||||
virtual const TransformationReducer *vars () const
|
||||
{
|
||||
return &m_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Requires merged input
|
||||
*/
|
||||
virtual bool requires_raw_input () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wants to build variants
|
||||
*/
|
||||
virtual bool wants_variants () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
length_type m_lmin, m_lmax;
|
||||
bool m_inverse;
|
||||
db::MagnificationReducer m_vars;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An edge orientation filter for use with Edges::filter or Edges::filtered
|
||||
*
|
||||
* This filter has two parameters: amin and amax.
|
||||
* It will filter all edges for which the orientation angle is >= amin and < amax.
|
||||
* The orientation angle is measured in degree against the x axis in the mathematical sense.
|
||||
* There is an "invert" flag which allows to select all edges not
|
||||
* matching the criterion.
|
||||
*/
|
||||
|
||||
struct DB_PUBLIC EdgeOrientationFilter
|
||||
: public EdgeFilterBase
|
||||
{
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param amin The minimum angle (measured against the x axis)
|
||||
* @param amax The maximum angle (measured against the x axis)
|
||||
* @param inverse If set to true, only edges not matching this criterion will be filtered
|
||||
*
|
||||
* This filter will filter out all edges whose angle against x axis
|
||||
* is larger or equal to amin and less than amax.
|
||||
*/
|
||||
EdgeOrientationFilter (double amin, double amax, bool inverse)
|
||||
: m_inverse (inverse), m_exact (false)
|
||||
{
|
||||
m_emin = db::DVector (cos (amin * M_PI / 180.0), sin (amin * M_PI / 180.0));
|
||||
m_emax = db::DVector (cos (amax * M_PI / 180.0), sin (amax * M_PI / 180.0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param a The angle (measured against the x axis)
|
||||
* @param inverse If set to true, only edges not matching this criterion will be filtered
|
||||
*
|
||||
* This filter will filter out all edges whose angle against x axis
|
||||
* is equal to a.
|
||||
*/
|
||||
EdgeOrientationFilter (double a, bool inverse)
|
||||
: m_inverse (inverse), m_exact (true)
|
||||
{
|
||||
m_emin = db::DVector (cos (a * M_PI / 180.0), sin (a * M_PI / 180.0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the edge orientation matches the criterion
|
||||
*/
|
||||
virtual bool selected (const db::Edge &edge) const
|
||||
{
|
||||
int smin = db::vprod_sign (m_emin, db::DVector (edge.d ()));
|
||||
if (m_exact) {
|
||||
if (! m_inverse) {
|
||||
return smin == 0;
|
||||
} else {
|
||||
return smin != 0;
|
||||
}
|
||||
} else {
|
||||
int smax = db::vprod_sign (m_emax, db::DVector (edge.d ()));
|
||||
if (! m_inverse) {
|
||||
return (smin >= 0 && smax < 0) || (smax > 0 && smin <= 0);
|
||||
} else {
|
||||
return ! ((smin >= 0 && smax < 0) || (smax > 0 && smin <= 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This filter is not isotropic
|
||||
*/
|
||||
virtual const TransformationReducer *vars () const
|
||||
{
|
||||
return &m_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Requires merged input
|
||||
*/
|
||||
virtual bool requires_raw_input () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wants to build variants
|
||||
*/
|
||||
virtual bool wants_variants () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
db::DVector m_emin, m_emax;
|
||||
bool m_inverse;
|
||||
bool m_exact;
|
||||
db::MagnificationAndOrientationReducer m_vars;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the edge interaction functionality which acts as an edge pair receiver
|
||||
*/
|
||||
template <class OutputContainer>
|
||||
class edge_interaction_filter
|
||||
: public db::box_scanner_receiver<db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
edge_interaction_filter (OutputContainer &output)
|
||||
: mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
// Select the edges which intersect
|
||||
if (p1 != p2) {
|
||||
const db::Edge *o = p1 > p2 ? o2 : o1;
|
||||
const db::Edge *oo = p1 > p2 ? o1 : o2;
|
||||
if (o->intersect (*oo)) {
|
||||
if (m_seen.insert (o).second) {
|
||||
mp_output->insert (*o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
std::set<const db::Edge *> m_seen;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver
|
||||
*
|
||||
* Note: This special scanner uses pointers to two different objects: edges and polygons.
|
||||
* It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate
|
||||
* pointers to edges.
|
||||
*
|
||||
* There is a special box converter which is able to sort that out as well.
|
||||
*/
|
||||
template <class OutputContainer>
|
||||
class edge_to_region_interaction_filter
|
||||
: public db::box_scanner_receiver2<db::Edge, size_t, db::Polygon, size_t>
|
||||
{
|
||||
public:
|
||||
edge_to_region_interaction_filter (OutputContainer &output)
|
||||
: mp_output (&output)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void add (const db::Edge *e, size_t, const db::Polygon *p, size_t)
|
||||
{
|
||||
if (m_seen.find (e) == m_seen.end ()) {
|
||||
if (db::interact (*p, *e)) {
|
||||
m_seen.insert (e);
|
||||
mp_output->insert (*e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
OutputContainer *mp_output;
|
||||
std::set<const db::Edge *> m_seen;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class for the DRC functionality which acts as an edge pair receiver
|
||||
*
|
||||
* If will perform a edge by edge check using the provided EdgeRelationFilter
|
||||
*/
|
||||
template <class Output>
|
||||
class edge2edge_check_for_edges
|
||||
: public db::box_scanner_receiver<db::Edge, size_t>
|
||||
{
|
||||
public:
|
||||
edge2edge_check_for_edges (const EdgeRelationFilter &check, Output &output, bool requires_different_layers)
|
||||
: mp_check (&check), mp_output (&output)
|
||||
{
|
||||
m_requires_different_layers = requires_different_layers;
|
||||
}
|
||||
|
||||
void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2)
|
||||
{
|
||||
// Overlap or inside checks require input from different layers
|
||||
if (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0) {
|
||||
|
||||
// ensure that the first check argument is of layer 1 and the second of
|
||||
// layer 2 (unless both are of the same layer)
|
||||
int l1 = int (p1 & size_t (1));
|
||||
int l2 = int (p2 & size_t (1));
|
||||
|
||||
db::EdgePair ep;
|
||||
if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) {
|
||||
mp_output->insert (ep);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const EdgeRelationFilter *mp_check;
|
||||
Output *mp_output;
|
||||
bool m_requires_different_layers;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class to turn joined edge sequences into polygons
|
||||
*
|
||||
* This object is an edge cluster so it can connect to a cluster collector
|
||||
* driven by a box scanner.
|
||||
*/
|
||||
struct JoinEdgesCluster
|
||||
: public db::cluster<db::Edge, size_t>
|
||||
{
|
||||
typedef db::Edge::coord_type coord_type;
|
||||
|
||||
JoinEdgesCluster (db::PolygonSink *output, coord_type ext_b, coord_type ext_e, coord_type ext_o, coord_type ext_i);
|
||||
void finish ();
|
||||
|
||||
private:
|
||||
db::PolygonSink *mp_output;
|
||||
coord_type m_ext_b, m_ext_e, m_ext_o, m_ext_i;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements the extension algorithm to turn an edge into a polygon
|
||||
*/
|
||||
db::Polygon extended_edge (const db::Edge &edge, db::Coord ext_b, db::Coord ext_e, db::Coord ext_o, db::Coord ext_i);
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbEmptyEdgePairs.h"
|
||||
#include "dbEmptyRegion.h"
|
||||
#include "dbEmptyEdges.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
|
||||
EmptyEdgePairs::EmptyEdgePairs ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EmptyEdgePairs::EmptyEdgePairs (const EmptyEdgePairs &other)
|
||||
: EdgePairsDelegate (other)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdgePairs::clone () const
|
||||
{
|
||||
return new EmptyEdgePairs (*this);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyEdgePairs::polygons (db::Coord) const
|
||||
{
|
||||
return new EmptyRegion ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdgePairs::edges () const
|
||||
{
|
||||
return new EmptyEdges ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdgePairs::first_edges () const
|
||||
{
|
||||
return new EmptyEdges ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdgePairs::second_edges () const
|
||||
{
|
||||
return new EmptyEdges ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdgePairs::add_in_place (const EdgePairs &other)
|
||||
{
|
||||
return add (other);
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdgePairs::add (const EdgePairs &other) const
|
||||
{
|
||||
return other.delegate ()->clone ();
|
||||
}
|
||||
|
||||
bool
|
||||
EmptyEdgePairs::equals (const EdgePairs &other) const
|
||||
{
|
||||
return other.empty ();
|
||||
}
|
||||
|
||||
bool
|
||||
EmptyEdgePairs::less (const EdgePairs &other) const
|
||||
{
|
||||
return other.empty () ? false : true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbEmptyEdgePairs
|
||||
#define HDR_dbEmptyEdgePairs
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdgePairsDelegate.h"
|
||||
#include "dbRecursiveShapeIterator.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief The delegate for the actual edge set implementation
|
||||
*/
|
||||
class DB_PUBLIC EmptyEdgePairs
|
||||
: public EdgePairsDelegate
|
||||
{
|
||||
public:
|
||||
EmptyEdgePairs ();
|
||||
EmptyEdgePairs (const EmptyEdgePairs &other);
|
||||
|
||||
virtual EdgePairsDelegate *clone () const;
|
||||
|
||||
virtual std::string to_string (size_t) const { return std::string (); }
|
||||
|
||||
virtual EdgePairsIteratorDelegate *begin () const { return 0; }
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
|
||||
|
||||
virtual bool empty () const { return true; }
|
||||
virtual size_t size () const { return 0; }
|
||||
|
||||
virtual Box bbox () const { return Box (); }
|
||||
|
||||
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; }
|
||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const { return new EmptyEdgePairs (); }
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||
virtual EdgesDelegate *edges () const;
|
||||
virtual EdgesDelegate *first_edges () const;
|
||||
virtual EdgesDelegate *second_edges () const;
|
||||
|
||||
virtual EdgePairsDelegate *add_in_place (const EdgePairs &other);
|
||||
virtual EdgePairsDelegate *add (const EdgePairs &other) const;
|
||||
|
||||
virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); }
|
||||
|
||||
virtual const db::EdgePair *nth (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_edge_pairs () const { return true; }
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const { return 0; }
|
||||
|
||||
virtual bool equals (const EdgePairs &other) const;
|
||||
virtual bool less (const EdgePairs &other) const;
|
||||
|
||||
virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { }
|
||||
virtual void insert_into_as_polygons (Layout *, db::cell_index_type, unsigned int, db::Coord) const { }
|
||||
|
||||
private:
|
||||
EmptyEdgePairs &operator= (const EmptyEdgePairs &other);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbEmptyEdges.h"
|
||||
#include "dbEmptyEdgePairs.h"
|
||||
#include "dbEmptyRegion.h"
|
||||
#include "dbEdges.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// EmptyEdges implementation
|
||||
|
||||
EmptyEdges::EmptyEdges ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EmptyEdges::~EmptyEdges ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EmptyEdges::EmptyEdges (const EmptyEdges &other)
|
||||
: EdgesDelegate (other)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdges::clone () const
|
||||
{
|
||||
return new EmptyEdges (*this);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyEdges::extended (coord_type, coord_type, coord_type, coord_type, bool) const
|
||||
{
|
||||
return new EmptyRegion ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdges::processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyEdges::processed_to_polygons (const EdgeToPolygonProcessorBase &) const
|
||||
{
|
||||
return new EmptyRegion ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdges::width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdges::space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdges::enclosing_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdges::overlap_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdges::separation_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyEdges::inside_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdges::add_in_place (const Edges &other)
|
||||
{
|
||||
return add (other);
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdges::add (const Edges &other) const
|
||||
{
|
||||
return other.delegate ()->clone ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdges::xor_with (const Edges &other) const
|
||||
{
|
||||
return or_with (other);
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyEdges::or_with (const Edges &other) const
|
||||
{
|
||||
if (other.empty ()) {
|
||||
return new EmptyEdges ();
|
||||
} else if (! other.strict_handling ()) {
|
||||
return other.delegate ()->clone ();
|
||||
} else {
|
||||
return other.delegate ()->merged ();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
EmptyEdges::equals (const Edges &other) const
|
||||
{
|
||||
return other.empty ();
|
||||
}
|
||||
|
||||
bool
|
||||
EmptyEdges::less (const Edges &other) const
|
||||
{
|
||||
return other.empty () ? false : true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbEmptyEdges
|
||||
#define HDR_dbEmptyEdges
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbEdgesDelegate.h"
|
||||
#include "dbRecursiveShapeIterator.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief An empty Edges
|
||||
*/
|
||||
class DB_PUBLIC EmptyEdges
|
||||
: public EdgesDelegate
|
||||
{
|
||||
public:
|
||||
EmptyEdges ();
|
||||
virtual ~EmptyEdges ();
|
||||
|
||||
EmptyEdges (const EmptyEdges &other);
|
||||
EdgesDelegate *clone () const;
|
||||
|
||||
virtual EdgesIteratorDelegate *begin () const { return 0; }
|
||||
virtual EdgesIteratorDelegate *begin_merged () const { return 0; }
|
||||
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
|
||||
|
||||
virtual bool empty () const { return true; }
|
||||
virtual size_t size () const { return 0; }
|
||||
virtual std::string to_string (size_t) const { return std::string (); }
|
||||
virtual bool is_merged () const { return true; }
|
||||
virtual distance_type length (const db::Box &) const { return 0; }
|
||||
virtual Box bbox () const { return db::Box (); }
|
||||
|
||||
virtual EdgePairsDelegate *width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *enclosing_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *overlap_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *separation_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *inside_check (const Edges &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
|
||||
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &) { return this; }
|
||||
virtual EdgesDelegate *filtered (const EdgeFilterBase &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &) { return this; }
|
||||
virtual EdgesDelegate *processed (const EdgeProcessorBase &) const { return new EmptyEdges (); }
|
||||
virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &) const;
|
||||
virtual RegionDelegate *processed_to_polygons (const EdgeToPolygonProcessorBase &) const;
|
||||
|
||||
virtual EdgesDelegate *merged_in_place () { return this; }
|
||||
virtual EdgesDelegate *merged () const { return new EmptyEdges (); }
|
||||
|
||||
virtual EdgesDelegate *and_with (const Edges &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *and_with (const Region &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *not_with (const Edges &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *not_with (const Region &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *xor_with (const Edges &other) const;
|
||||
virtual EdgesDelegate *or_with (const Edges &other) const;
|
||||
virtual EdgesDelegate *add_in_place (const Edges &other);
|
||||
virtual EdgesDelegate *add (const Edges &other) const;
|
||||
|
||||
virtual RegionDelegate *extended (coord_type, coord_type, coord_type, coord_type, bool) const;
|
||||
|
||||
virtual EdgesDelegate *inside_part (const Region &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *outside_part (const Region &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *selected_interacting (const Edges &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *selected_not_interacting (const Edges &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *selected_interacting (const Region &) const { return new EmptyEdges (); }
|
||||
virtual EdgesDelegate *selected_not_interacting (const Region &) const { return new EmptyEdges (); }
|
||||
|
||||
virtual EdgesDelegate *in (const Edges &, bool) const { return new EmptyEdges (); }
|
||||
|
||||
virtual const db::Edge *nth (size_t) const { tl_assert (false); }
|
||||
virtual bool has_valid_edges () const { return true; }
|
||||
virtual bool has_valid_merged_edges () const { return true; }
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const { return 0; }
|
||||
|
||||
virtual bool equals (const Edges &other) const;
|
||||
virtual bool less (const Edges &other) const;
|
||||
|
||||
virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { }
|
||||
|
||||
private:
|
||||
EmptyEdges &operator= (const EmptyEdges &other);
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbEmptyRegion.h"
|
||||
#include "dbEmptyEdges.h"
|
||||
#include "dbEmptyEdgePairs.h"
|
||||
#include "dbRegion.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// EmptyRegion implementation
|
||||
|
||||
EmptyRegion::EmptyRegion ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EmptyRegion::~EmptyRegion ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
EmptyRegion::EmptyRegion (const EmptyRegion &other)
|
||||
: RegionDelegate (other)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyRegion::clone () const
|
||||
{
|
||||
return new EmptyRegion (*this);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyRegion::add_in_place (const Region &other)
|
||||
{
|
||||
return add (other);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyRegion::add (const Region &other) const
|
||||
{
|
||||
return other.delegate ()->clone ();
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyRegion::xor_with (const Region &other) const
|
||||
{
|
||||
return or_with (other);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
EmptyRegion::or_with (const Region &other) const
|
||||
{
|
||||
if (other.empty ()) {
|
||||
return new EmptyRegion ();
|
||||
} else if (! other.strict_handling ()) {
|
||||
return other.delegate ()->clone ();
|
||||
} else {
|
||||
return other.delegate ()->merged ();
|
||||
}
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyRegion::processed_to_edges (const PolygonToEdgeProcessorBase &) const
|
||||
{
|
||||
return new EmptyEdges ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::enclosing_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::overlap_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::grid_check (db::Coord, db::Coord) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
EmptyRegion::angle_check (double, double, bool) const
|
||||
{
|
||||
return new EmptyEdgePairs ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
EmptyRegion::edges (const EdgeFilterBase *) const
|
||||
{
|
||||
return new EmptyEdges ();
|
||||
}
|
||||
|
||||
bool
|
||||
EmptyRegion::equals (const Region &other) const
|
||||
{
|
||||
return other.empty ();
|
||||
}
|
||||
|
||||
bool
|
||||
EmptyRegion::less (const Region &other) const
|
||||
{
|
||||
return other.empty () ? false : true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbEmptyRegion
|
||||
#define HDR_dbEmptyRegion
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbRegionDelegate.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief An empty Region
|
||||
*/
|
||||
class DB_PUBLIC EmptyRegion
|
||||
: public RegionDelegate
|
||||
{
|
||||
public:
|
||||
EmptyRegion ();
|
||||
virtual ~EmptyRegion ();
|
||||
|
||||
EmptyRegion (const EmptyRegion &other);
|
||||
RegionDelegate *clone () const;
|
||||
|
||||
virtual RegionIteratorDelegate *begin () const { return 0; }
|
||||
virtual RegionIteratorDelegate *begin_merged () const { return 0; }
|
||||
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
|
||||
|
||||
virtual bool empty () const { return true; }
|
||||
virtual size_t size () const { return 0; }
|
||||
virtual std::string to_string (size_t) const { return std::string (); }
|
||||
|
||||
virtual bool is_box () const { return false; }
|
||||
virtual bool is_merged () const { return true; }
|
||||
virtual area_type area (const db::Box &) const { return 0; }
|
||||
virtual perimeter_type perimeter (const db::Box &) const { return 0; }
|
||||
|
||||
virtual Box bbox () const { return Box (); }
|
||||
|
||||
virtual EdgePairsDelegate *width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *enclosing_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *overlap_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const;
|
||||
virtual EdgePairsDelegate *grid_check (db::Coord, db::Coord) const;
|
||||
virtual EdgePairsDelegate *angle_check (double, double, bool) const;
|
||||
|
||||
virtual RegionDelegate *snapped_in_place (db::Coord, db::Coord) { return this; }
|
||||
virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); }
|
||||
|
||||
virtual EdgesDelegate *edges (const EdgeFilterBase *) const;
|
||||
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; }
|
||||
virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &) { return this; }
|
||||
virtual RegionDelegate *processed (const PolygonProcessorBase &) const { return new EmptyRegion (); }
|
||||
virtual EdgesDelegate *processed_to_edges (const PolygonToEdgeProcessorBase &) const;
|
||||
virtual EdgePairsDelegate *processed_to_edge_pairs (const PolygonToEdgePairProcessorBase &) const;
|
||||
|
||||
virtual RegionDelegate *merged_in_place () { return this; }
|
||||
virtual RegionDelegate *merged_in_place (bool, unsigned int) { return this; }
|
||||
virtual RegionDelegate *merged () const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *merged (bool, unsigned int) const { return new EmptyRegion (); }
|
||||
|
||||
virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); }
|
||||
|
||||
virtual RegionDelegate *and_with (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *not_with (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *xor_with (const Region &other) const;
|
||||
virtual RegionDelegate *or_with (const Region &other) const;
|
||||
virtual RegionDelegate *add_in_place (const Region &other);
|
||||
virtual RegionDelegate *add (const Region &other) const;
|
||||
|
||||
virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_inside (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_not_inside (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_interacting (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_not_interacting (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_interacting (const Edges &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_not_interacting (const Edges &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_overlapping (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *selected_not_overlapping (const Region &) const { return new EmptyRegion (); }
|
||||
virtual RegionDelegate *in (const Region &, bool) const { return new EmptyRegion (); }
|
||||
|
||||
virtual bool has_valid_polygons () const { return true; }
|
||||
virtual bool has_valid_merged_polygons () const { return true; }
|
||||
virtual const db::Polygon *nth (size_t) const { tl_assert (false); }
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const { return 0; }
|
||||
|
||||
virtual bool equals (const Region &other) const;
|
||||
virtual bool less (const Region &other) const;
|
||||
|
||||
virtual void insert_into (Layout *, db::cell_index_type, unsigned int) const { }
|
||||
|
||||
private:
|
||||
EmptyRegion &operator= (const EmptyRegion &other);
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbFlatEdgePairs.h"
|
||||
#include "dbEmptyEdgePairs.h"
|
||||
#include "dbEdgePairs.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// FlatEdgePairs implementation
|
||||
|
||||
FlatEdgePairs::FlatEdgePairs ()
|
||||
: AsIfFlatEdgePairs (), m_edge_pairs (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
FlatEdgePairs::~FlatEdgePairs ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other)
|
||||
: AsIfFlatEdgePairs (other), m_edge_pairs (false)
|
||||
{
|
||||
m_edge_pairs = other.m_edge_pairs;
|
||||
}
|
||||
|
||||
FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs)
|
||||
: AsIfFlatEdgePairs (), m_edge_pairs (edge_pairs)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void FlatEdgePairs::invalidate_cache ()
|
||||
{
|
||||
invalidate_bbox ();
|
||||
}
|
||||
|
||||
void FlatEdgePairs::reserve (size_t n)
|
||||
{
|
||||
m_edge_pairs.reserve (db::EdgePair::tag (), n);
|
||||
}
|
||||
|
||||
EdgePairsIteratorDelegate *FlatEdgePairs::begin () const
|
||||
{
|
||||
return new FlatEdgePairsIterator (m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (), m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
|
||||
}
|
||||
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatEdgePairs::begin_iter () const
|
||||
{
|
||||
return std::make_pair (db::RecursiveShapeIterator (m_edge_pairs), db::ICplxTrans ());
|
||||
}
|
||||
|
||||
bool FlatEdgePairs::empty () const
|
||||
{
|
||||
return m_edge_pairs.empty ();
|
||||
}
|
||||
|
||||
size_t FlatEdgePairs::size () const
|
||||
{
|
||||
return m_edge_pairs.size ();
|
||||
}
|
||||
|
||||
Box FlatEdgePairs::compute_bbox () const
|
||||
{
|
||||
m_edge_pairs.update_bbox ();
|
||||
return m_edge_pairs.bbox ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *
|
||||
FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
|
||||
{
|
||||
edge_pair_iterator_type pw = m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin ();
|
||||
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
if (filter.selected (*p)) {
|
||||
if (pw == m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ()) {
|
||||
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().insert (*p);
|
||||
pw = m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ();
|
||||
} else {
|
||||
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (pw++, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().erase (pw, m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
EdgePairsDelegate *FlatEdgePairs::add (const EdgePairs &other) const
|
||||
{
|
||||
std::auto_ptr<FlatEdgePairs> new_edge_pairs (new FlatEdgePairs (*this));
|
||||
new_edge_pairs->invalidate_cache ();
|
||||
|
||||
FlatEdgePairs *other_flat = dynamic_cast<FlatEdgePairs *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
new_edge_pairs->raw_edge_pairs ().insert (other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (), other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
|
||||
|
||||
} else {
|
||||
|
||||
size_t n = new_edge_pairs->raw_edge_pairs ().size ();
|
||||
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
|
||||
new_edge_pairs->raw_edge_pairs ().reserve (db::EdgePair::tag (), n);
|
||||
|
||||
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
new_edge_pairs->raw_edge_pairs ().insert (*p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new_edge_pairs.release ();
|
||||
}
|
||||
|
||||
EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
|
||||
{
|
||||
invalidate_cache ();
|
||||
|
||||
FlatEdgePairs *other_flat = dynamic_cast<FlatEdgePairs *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
m_edge_pairs.insert (other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (), other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
|
||||
|
||||
} else {
|
||||
|
||||
size_t n = m_edge_pairs.size ();
|
||||
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
|
||||
m_edge_pairs.reserve (db::EdgePair::tag (), n);
|
||||
|
||||
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
m_edge_pairs.insert (*p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
const db::EdgePair *FlatEdgePairs::nth (size_t n) const
|
||||
{
|
||||
return n < m_edge_pairs.size () ? &m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
}
|
||||
|
||||
bool FlatEdgePairs::has_valid_edge_pairs () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const db::RecursiveShapeIterator *FlatEdgePairs::iter () const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
FlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer, db::Coord enl) const
|
||||
{
|
||||
db::Shapes &out = layout->cell (into_cell).shapes (into_layer);
|
||||
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
out.insert (p->normalized ().to_simple_polygon (enl));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
layout->cell (into_cell).shapes (into_layer).insert (m_edge_pairs);
|
||||
}
|
||||
|
||||
void
|
||||
FlatEdgePairs::insert (const db::EdgePair &ep)
|
||||
{
|
||||
m_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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbFlatEdgePairs
|
||||
#define HDR_dbFlatEdgePairs
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbAsIfFlatEdgePairs.h"
|
||||
#include "dbShapes.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief An iterator delegate for the flat edge pair set
|
||||
*/
|
||||
class DB_PUBLIC FlatEdgePairsIterator
|
||||
: public EdgePairsIteratorDelegate
|
||||
{
|
||||
public:
|
||||
typedef db::layer<db::EdgePair, db::unstable_layer_tag> edge_pair_layer_type;
|
||||
typedef edge_pair_layer_type::iterator iterator_type;
|
||||
|
||||
FlatEdgePairsIterator (iterator_type from, iterator_type to)
|
||||
: m_from (from), m_to (to)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual bool at_end () const
|
||||
{
|
||||
return m_from == m_to;
|
||||
}
|
||||
|
||||
virtual void increment ()
|
||||
{
|
||||
++m_from;
|
||||
}
|
||||
|
||||
virtual const value_type *get () const
|
||||
{
|
||||
return m_from.operator-> ();
|
||||
}
|
||||
|
||||
virtual EdgePairsIteratorDelegate *clone () const
|
||||
{
|
||||
return new FlatEdgePairsIterator (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class EdgePairs;
|
||||
|
||||
iterator_type m_from, m_to;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The delegate for the actual edge pair set implementation
|
||||
*/
|
||||
class DB_PUBLIC FlatEdgePairs
|
||||
: public AsIfFlatEdgePairs
|
||||
{
|
||||
public:
|
||||
typedef db::layer<db::EdgePair, db::unstable_layer_tag> edge_pair_layer_type;
|
||||
typedef edge_pair_layer_type::iterator edge_pair_iterator_type;
|
||||
|
||||
FlatEdgePairs ();
|
||||
FlatEdgePairs (const db::Shapes &edges);
|
||||
|
||||
FlatEdgePairs (const FlatEdgePairs &other);
|
||||
|
||||
virtual ~FlatEdgePairs ();
|
||||
|
||||
EdgePairsDelegate *clone () const
|
||||
{
|
||||
return new FlatEdgePairs (*this);
|
||||
}
|
||||
|
||||
void reserve (size_t);
|
||||
|
||||
virtual EdgePairsIteratorDelegate *begin () const;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
|
||||
|
||||
virtual bool empty () const;
|
||||
virtual size_t size () const;
|
||||
|
||||
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter);
|
||||
|
||||
virtual EdgePairsDelegate *add_in_place (const EdgePairs &other);
|
||||
virtual EdgePairsDelegate *add (const EdgePairs &other) const;
|
||||
|
||||
virtual const db::EdgePair *nth (size_t n) const;
|
||||
virtual bool has_valid_edge_pairs () const;
|
||||
|
||||
virtual const db::RecursiveShapeIterator *iter () const;
|
||||
|
||||
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);
|
||||
|
||||
template <class T>
|
||||
void insert (const db::Shape &shape, const T &trans)
|
||||
{
|
||||
if (shape.is_edge_pair ()) {
|
||||
|
||||
db::EdgePair ep;
|
||||
shape.edge_pair (ep);
|
||||
ep.transform (trans);
|
||||
insert (ep);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
void insert_seq (const Iter &seq)
|
||||
{
|
||||
for (Iter i = seq; ! i.at_end (); ++i) {
|
||||
insert (*i);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Trans>
|
||||
void transform (const Trans &trans)
|
||||
{
|
||||
if (! trans.is_unity ()) {
|
||||
for (edge_pair_iterator_type p = m_edge_pairs.template get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (); p != m_edge_pairs.template get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); ++p) {
|
||||
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
|
||||
}
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
db::Shapes &raw_edge_pairs () { return m_edge_pairs; }
|
||||
|
||||
protected:
|
||||
virtual Box compute_bbox () const;
|
||||
void invalidate_cache ();
|
||||
|
||||
private:
|
||||
friend class AsIfFlatEdgePairs;
|
||||
|
||||
FlatEdgePairs &operator= (const FlatEdgePairs &other);
|
||||
|
||||
mutable db::Shapes m_edge_pairs;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbFlatEdges.h"
|
||||
#include "dbEmptyEdges.h"
|
||||
#include "dbEdgeBoolean.h"
|
||||
#include "dbEdges.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// FlatEdges implementation
|
||||
|
||||
FlatEdges::FlatEdges ()
|
||||
: AsIfFlatEdges (), m_edges (false), m_merged_edges (false)
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
FlatEdges::~FlatEdges ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
FlatEdges::FlatEdges (const FlatEdges &other)
|
||||
: AsIfFlatEdges (other), m_edges (false), m_merged_edges (false)
|
||||
{
|
||||
init ();
|
||||
|
||||
m_is_merged = other.m_is_merged;
|
||||
m_edges = other.m_edges;
|
||||
m_merged_edges = other.m_merged_edges;
|
||||
m_merged_edges_valid = other.m_merged_edges_valid;
|
||||
}
|
||||
|
||||
FlatEdges::FlatEdges (const db::Shapes &edges, bool is_merged)
|
||||
: AsIfFlatEdges (), m_edges (edges), m_merged_edges (false)
|
||||
{
|
||||
init ();
|
||||
|
||||
m_is_merged = is_merged;
|
||||
}
|
||||
|
||||
FlatEdges::FlatEdges (bool is_merged)
|
||||
: AsIfFlatEdges (), m_edges (false), m_merged_edges (false)
|
||||
{
|
||||
init ();
|
||||
|
||||
m_is_merged = is_merged;
|
||||
}
|
||||
|
||||
void FlatEdges::set_is_merged (bool m)
|
||||
{
|
||||
m_is_merged = m;
|
||||
}
|
||||
|
||||
void FlatEdges::invalidate_cache ()
|
||||
{
|
||||
invalidate_bbox ();
|
||||
m_merged_edges.clear ();
|
||||
m_merged_edges_valid = false;
|
||||
}
|
||||
|
||||
void FlatEdges::init ()
|
||||
{
|
||||
m_is_merged = true;
|
||||
m_merged_edges_valid = false;
|
||||
}
|
||||
|
||||
void FlatEdges::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
layout->cell (into_cell).shapes (into_layer).insert (m_edges);
|
||||
}
|
||||
|
||||
void FlatEdges::merged_semantics_changed ()
|
||||
{
|
||||
m_merged_edges.clear ();
|
||||
m_merged_edges_valid = false;
|
||||
}
|
||||
|
||||
void FlatEdges::reserve (size_t n)
|
||||
{
|
||||
m_edges.reserve (db::Edge::tag (), n);
|
||||
}
|
||||
|
||||
void
|
||||
FlatEdges::ensure_merged_edges_valid () const
|
||||
{
|
||||
if (! m_merged_edges_valid) {
|
||||
|
||||
m_merged_edges.clear ();
|
||||
|
||||
db::Shapes tmp (false);
|
||||
EdgeBooleanClusterCollector<db::Shapes> cluster_collector (&tmp, EdgeOr);
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner (report_progress (), progress_desc ());
|
||||
scanner.reserve (m_edges.size ());
|
||||
|
||||
for (EdgesIterator e (begin ()); ! e.at_end (); ++e) {
|
||||
if (! e->is_degenerate ()) {
|
||||
scanner.insert (&*e, 0);
|
||||
}
|
||||
}
|
||||
|
||||
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
m_merged_edges.swap (tmp);
|
||||
m_merged_edges_valid = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIteratorDelegate *FlatEdges::begin () const
|
||||
{
|
||||
return new FlatEdgesIterator (m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().begin (), m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ());
|
||||
}
|
||||
|
||||
EdgesIteratorDelegate *FlatEdges::begin_merged () const
|
||||
{
|
||||
if (! merged_semantics () || m_is_merged) {
|
||||
return begin ();
|
||||
} else {
|
||||
ensure_merged_edges_valid ();
|
||||
return new FlatEdgesIterator (m_merged_edges.get_layer<db::Edge, db::unstable_layer_tag> ().begin (), m_merged_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatEdges::begin_iter () const
|
||||
{
|
||||
return std::make_pair (db::RecursiveShapeIterator (m_edges), db::ICplxTrans ());
|
||||
}
|
||||
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatEdges::begin_merged_iter () const
|
||||
{
|
||||
if (! merged_semantics () || m_is_merged) {
|
||||
return begin_iter ();
|
||||
} else {
|
||||
ensure_merged_edges_valid ();
|
||||
return std::make_pair (db::RecursiveShapeIterator (m_merged_edges), db::ICplxTrans ());
|
||||
}
|
||||
}
|
||||
|
||||
bool FlatEdges::empty () const
|
||||
{
|
||||
return m_edges.empty ();
|
||||
}
|
||||
|
||||
size_t FlatEdges::size () const
|
||||
{
|
||||
return m_edges.size ();
|
||||
}
|
||||
|
||||
bool FlatEdges::is_merged () const
|
||||
{
|
||||
return m_is_merged;
|
||||
}
|
||||
|
||||
Box FlatEdges::compute_bbox () const
|
||||
{
|
||||
m_edges.update_bbox ();
|
||||
return m_edges.bbox ();
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
FlatEdges::processed_in_place (const EdgeProcessorBase &filter)
|
||||
{
|
||||
std::vector<db::Edge> edge_res;
|
||||
|
||||
edge_iterator_type pw = m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().begin ();
|
||||
for (EdgesIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) {
|
||||
|
||||
edge_res.clear ();
|
||||
filter.process (*p, edge_res);
|
||||
|
||||
for (std::vector<db::Edge>::const_iterator pr = edge_res.begin (); pr != edge_res.end (); ++pr) {
|
||||
if (pw == m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ()) {
|
||||
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().insert (*pr);
|
||||
pw = m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ();
|
||||
} else {
|
||||
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().replace (pw++, *pr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().erase (pw, m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ());
|
||||
m_merged_edges.clear ();
|
||||
m_is_merged = filter.result_is_merged () && merged_semantics ();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
FlatEdges::filter_in_place (const EdgeFilterBase &filter)
|
||||
{
|
||||
edge_iterator_type pw = m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().begin ();
|
||||
for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
||||
if (filter.selected (*p)) {
|
||||
if (pw == m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ()) {
|
||||
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().insert (*p);
|
||||
pw = m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ();
|
||||
} else {
|
||||
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().replace (pw++, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().erase (pw, m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end ());
|
||||
m_merged_edges.clear ();
|
||||
m_is_merged = merged_semantics ();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
EdgesDelegate *FlatEdges::add (const Edges &other) const
|
||||
{
|
||||
std::auto_ptr<FlatEdges> new_region (new FlatEdges (*this));
|
||||
new_region->invalidate_cache ();
|
||||
new_region->set_is_merged (false);
|
||||
|
||||
FlatEdges *other_flat = dynamic_cast<FlatEdges *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
new_region->raw_edges ().insert (other_flat->raw_edges ().get_layer<db::Edge, db::unstable_layer_tag> ().begin (), other_flat->raw_edges ().get_layer<db::Edge, db::unstable_layer_tag> ().end ());
|
||||
|
||||
} else {
|
||||
|
||||
size_t n = new_region->raw_edges ().size ();
|
||||
for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
|
||||
new_region->raw_edges ().reserve (db::Edge::tag (), n);
|
||||
|
||||
for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
new_region->raw_edges ().insert (*p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new_region.release ();
|
||||
}
|
||||
|
||||
EdgesDelegate *FlatEdges::add_in_place (const Edges &other)
|
||||
{
|
||||
invalidate_cache ();
|
||||
m_is_merged = false;
|
||||
|
||||
FlatEdges *other_flat = dynamic_cast<FlatEdges *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
m_edges.insert (other_flat->raw_edges ().get_layer<db::Edge, db::unstable_layer_tag> ().begin (), other_flat->raw_edges ().get_layer<db::Edge, db::unstable_layer_tag> ().end ());
|
||||
|
||||
} else {
|
||||
|
||||
size_t n = m_edges.size ();
|
||||
for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
|
||||
m_edges.reserve (db::Edge::tag (), n);
|
||||
|
||||
for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
m_edges.insert (*p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
const db::Edge *FlatEdges::nth (size_t n) const
|
||||
{
|
||||
return n < m_edges.size () ? &m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
}
|
||||
|
||||
bool FlatEdges::has_valid_edges () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlatEdges::has_valid_merged_edges () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const db::RecursiveShapeIterator *FlatEdges::iter () const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
FlatEdges::insert (const db::Box &box)
|
||||
{
|
||||
if (! box.empty () && box.width () > 0 && box.height () > 0) {
|
||||
|
||||
bool was_empty = empty ();
|
||||
|
||||
m_edges.insert (db::Edge (box.lower_left (), box.upper_left ()));
|
||||
m_edges.insert (db::Edge (box.upper_left (), box.upper_right ()));
|
||||
m_edges.insert (db::Edge (box.upper_right (), box.lower_right ()));
|
||||
m_edges.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) {
|
||||
for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edges.insert (*e);
|
||||
}
|
||||
m_is_merged = false;
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlatEdges::insert (const db::SimplePolygon &polygon)
|
||||
{
|
||||
if (polygon.vertices () > 0) {
|
||||
for (db::SimplePolygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) {
|
||||
m_edges.insert (*e);
|
||||
}
|
||||
m_is_merged = false;
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlatEdges::insert (const db::Edge &edge)
|
||||
{
|
||||
if (! empty ()) {
|
||||
m_is_merged = false;
|
||||
}
|
||||
|
||||
m_edges.insert (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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbFlatEdges
|
||||
#define HDR_dbFlatEdges
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbAsIfFlatEdges.h"
|
||||
#include "dbShapes.h"
|
||||
#include "dbShapes2.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief An iterator delegate for the flat region
|
||||
*/
|
||||
class DB_PUBLIC FlatEdgesIterator
|
||||
: public EdgesIteratorDelegate
|
||||
{
|
||||
public:
|
||||
typedef db::layer<db::Edge, db::unstable_layer_tag> edge_layer_type;
|
||||
typedef edge_layer_type::iterator iterator_type;
|
||||
|
||||
FlatEdgesIterator (iterator_type from, iterator_type to)
|
||||
: m_from (from), m_to (to)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual bool at_end () const
|
||||
{
|
||||
return m_from == m_to;
|
||||
}
|
||||
|
||||
virtual void increment ()
|
||||
{
|
||||
++m_from;
|
||||
}
|
||||
|
||||
virtual const value_type *get () const
|
||||
{
|
||||
return m_from.operator-> ();
|
||||
}
|
||||
|
||||
virtual EdgesIteratorDelegate *clone () const
|
||||
{
|
||||
return new FlatEdgesIterator (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Edges;
|
||||
|
||||
iterator_type m_from, m_to;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A flat, edge-set delegate
|
||||
*/
|
||||
class DB_PUBLIC FlatEdges
|
||||
: public AsIfFlatEdges
|
||||
{
|
||||
public:
|
||||
typedef db::layer<db::Edge, db::unstable_layer_tag> edge_layer_type;
|
||||
typedef edge_layer_type::iterator edge_iterator_type;
|
||||
|
||||
FlatEdges ();
|
||||
FlatEdges (const db::Shapes &edges, bool is_merged);
|
||||
FlatEdges (bool is_merged);
|
||||
|
||||
FlatEdges (const FlatEdges &other);
|
||||
|
||||
virtual ~FlatEdges ();
|
||||
|
||||
EdgesDelegate *clone () const
|
||||
{
|
||||
return new FlatEdges (*this);
|
||||
}
|
||||
|
||||
void reserve (size_t);
|
||||
|
||||
virtual EdgesIteratorDelegate *begin () const;
|
||||
virtual EdgesIteratorDelegate *begin_merged () const;
|
||||
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const;
|
||||
|
||||
virtual bool empty () const;
|
||||
virtual size_t size () const;
|
||||
virtual bool is_merged () const;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
|
||||
|
||||
virtual EdgesDelegate *processed_in_place (const EdgeProcessorBase &filter);
|
||||
virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter);
|
||||
|
||||
virtual EdgesDelegate *add_in_place (const Edges &other);
|
||||
virtual EdgesDelegate *add (const Edges &other) const;
|
||||
|
||||
virtual const db::Edge *nth (size_t n) const;
|
||||
virtual bool has_valid_edges () const;
|
||||
virtual bool has_valid_merged_edges () const;
|
||||
|
||||
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);
|
||||
|
||||
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 (size () + (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);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Trans>
|
||||
void transform (const Trans &trans)
|
||||
{
|
||||
if (! trans.is_unity ()) {
|
||||
for (edge_iterator_type p = m_edges.template get_layer<db::Edge, db::unstable_layer_tag> ().begin (); p != m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().end (); ++p) {
|
||||
m_edges.get_layer<db::Edge, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
|
||||
}
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void merged_semantics_changed ();
|
||||
virtual Box compute_bbox () const;
|
||||
void invalidate_cache ();
|
||||
void set_is_merged (bool m);
|
||||
|
||||
private:
|
||||
friend class AsIfFlatEdges;
|
||||
|
||||
db::Shapes &raw_edges () { return m_edges; }
|
||||
|
||||
FlatEdges &operator= (const FlatEdges &other);
|
||||
|
||||
bool m_is_merged;
|
||||
mutable db::Shapes m_edges;
|
||||
mutable db::Shapes m_merged_edges;
|
||||
mutable bool m_merged_edges_valid;
|
||||
|
||||
void init ();
|
||||
void ensure_merged_edges_valid () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbFlatRegion.h"
|
||||
#include "dbEmptyRegion.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbShapeProcessor.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// FlatRegion implementation
|
||||
|
||||
FlatRegion::FlatRegion ()
|
||||
: AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false)
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
FlatRegion::~FlatRegion ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
FlatRegion::FlatRegion (const FlatRegion &other)
|
||||
: AsIfFlatRegion (other), m_polygons (false), m_merged_polygons (false)
|
||||
{
|
||||
init ();
|
||||
|
||||
m_is_merged = other.m_is_merged;
|
||||
m_polygons = other.m_polygons;
|
||||
m_merged_polygons = other.m_merged_polygons;
|
||||
m_merged_polygons_valid = other.m_merged_polygons_valid;
|
||||
}
|
||||
|
||||
FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged)
|
||||
: AsIfFlatRegion (), m_polygons (polygons), m_merged_polygons (false)
|
||||
{
|
||||
init ();
|
||||
|
||||
m_is_merged = is_merged;
|
||||
}
|
||||
|
||||
FlatRegion::FlatRegion (bool is_merged)
|
||||
: AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false)
|
||||
{
|
||||
init ();
|
||||
|
||||
m_is_merged = is_merged;
|
||||
}
|
||||
|
||||
void FlatRegion::set_is_merged (bool m)
|
||||
{
|
||||
m_is_merged = m;
|
||||
}
|
||||
|
||||
void FlatRegion::invalidate_cache ()
|
||||
{
|
||||
invalidate_bbox ();
|
||||
m_merged_polygons.clear ();
|
||||
m_merged_polygons_valid = false;
|
||||
}
|
||||
|
||||
void FlatRegion::init ()
|
||||
{
|
||||
m_is_merged = true;
|
||||
m_merged_polygons_valid = false;
|
||||
}
|
||||
|
||||
void FlatRegion::merged_semantics_changed ()
|
||||
{
|
||||
m_merged_polygons.clear ();
|
||||
m_merged_polygons_valid = false;
|
||||
}
|
||||
|
||||
void FlatRegion::reserve (size_t n)
|
||||
{
|
||||
m_polygons.reserve (db::Polygon::tag (), n);
|
||||
}
|
||||
|
||||
void
|
||||
FlatRegion::ensure_merged_polygons_valid () const
|
||||
{
|
||||
if (! m_merged_polygons_valid) {
|
||||
|
||||
m_merged_polygons.clear ();
|
||||
|
||||
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
||||
ep.set_base_verbosity (base_verbosity ());
|
||||
|
||||
// count edges and reserve memory
|
||||
size_t n = 0;
|
||||
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
n += p->vertices ();
|
||||
}
|
||||
ep.reserve (n);
|
||||
|
||||
// insert the polygons into the processor
|
||||
n = 0;
|
||||
for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) {
|
||||
ep.insert (*p, n);
|
||||
}
|
||||
|
||||
// and run the merge step
|
||||
db::MergeOp op (0);
|
||||
db::ShapeGenerator pc (m_merged_polygons);
|
||||
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
|
||||
ep.process (pg, op);
|
||||
|
||||
m_merged_polygons_valid = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RegionIteratorDelegate *FlatRegion::begin () const
|
||||
{
|
||||
return new FlatRegionIterator (m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().begin (), m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
|
||||
}
|
||||
|
||||
RegionIteratorDelegate *FlatRegion::begin_merged () const
|
||||
{
|
||||
if (! merged_semantics () || m_is_merged) {
|
||||
return begin ();
|
||||
} else {
|
||||
ensure_merged_polygons_valid ();
|
||||
return new FlatRegionIterator (m_merged_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().begin (), m_merged_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_iter () const
|
||||
{
|
||||
return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ());
|
||||
}
|
||||
|
||||
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_merged_iter () const
|
||||
{
|
||||
if (! merged_semantics () || m_is_merged) {
|
||||
return begin_iter ();
|
||||
} else {
|
||||
ensure_merged_polygons_valid ();
|
||||
return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ());
|
||||
}
|
||||
}
|
||||
|
||||
bool FlatRegion::empty () const
|
||||
{
|
||||
return m_polygons.empty ();
|
||||
}
|
||||
|
||||
size_t FlatRegion::size () const
|
||||
{
|
||||
return m_polygons.size ();
|
||||
}
|
||||
|
||||
bool FlatRegion::is_merged () const
|
||||
{
|
||||
return m_is_merged;
|
||||
}
|
||||
|
||||
Box FlatRegion::compute_bbox () const
|
||||
{
|
||||
m_polygons.update_bbox ();
|
||||
return m_polygons.bbox ();
|
||||
}
|
||||
|
||||
RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter)
|
||||
{
|
||||
polygon_iterator_type pw = m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().begin ();
|
||||
for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) {
|
||||
if (filter.selected (*p)) {
|
||||
if (pw == m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ()) {
|
||||
m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().insert (*p);
|
||||
pw = m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ();
|
||||
} else {
|
||||
m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().replace (pw++, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().erase (pw, m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
|
||||
m_merged_polygons.clear ();
|
||||
m_is_merged = merged_semantics ();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter)
|
||||
{
|
||||
std::vector<db::Polygon> poly_res;
|
||||
|
||||
polygon_iterator_type pw = m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().begin ();
|
||||
for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) {
|
||||
|
||||
poly_res.clear ();
|
||||
filter.process (*p, poly_res);
|
||||
|
||||
for (std::vector<db::Polygon>::const_iterator pr = poly_res.begin (); pr != poly_res.end (); ++pr) {
|
||||
if (pw == m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ()) {
|
||||
m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().insert (*pr);
|
||||
pw = m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ();
|
||||
} else {
|
||||
m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().replace (pw++, *pr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().erase (pw, m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
|
||||
m_merged_polygons.clear ();
|
||||
m_is_merged = filter.result_is_merged () && merged_semantics ();
|
||||
|
||||
if (filter.result_must_not_be_merged ()) {
|
||||
set_merged_semantics (false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
RegionDelegate *FlatRegion::merged_in_place ()
|
||||
{
|
||||
if (! m_is_merged) {
|
||||
|
||||
if (m_merged_polygons_valid) {
|
||||
|
||||
m_polygons.swap (m_merged_polygons);
|
||||
m_merged_polygons.clear ();
|
||||
m_is_merged = true;
|
||||
return this;
|
||||
|
||||
} else {
|
||||
return merged_in_place (min_coherence (), 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc)
|
||||
{
|
||||
if (empty ()) {
|
||||
|
||||
// ignore empty
|
||||
return new EmptyRegion ();
|
||||
|
||||
} else if (is_box ()) {
|
||||
|
||||
// take box only if min_wc == 0, otherwise clear
|
||||
if (min_wc > 0) {
|
||||
return new EmptyRegion ();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
invalidate_cache ();
|
||||
|
||||
db::EdgeProcessor ep (report_progress (), progress_desc ());
|
||||
ep.set_base_verbosity (base_verbosity ());
|
||||
|
||||
// count edges and reserve memory
|
||||
size_t n = 0;
|
||||
for (RegionIterator p (begin ()); ! p.at_end (); ++p) {
|
||||
n += p->vertices ();
|
||||
}
|
||||
ep.reserve (n);
|
||||
|
||||
// insert the polygons into the processor
|
||||
n = 0;
|
||||
for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) {
|
||||
ep.insert (*p, n);
|
||||
}
|
||||
|
||||
// and run the merge step
|
||||
db::MergeOp op (min_wc);
|
||||
db::ShapeGenerator pc (m_polygons, true /*clear*/);
|
||||
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence);
|
||||
ep.process (pg, op);
|
||||
|
||||
m_is_merged = true;
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
RegionDelegate *FlatRegion::merged () const
|
||||
{
|
||||
if (! m_is_merged) {
|
||||
|
||||
if (m_merged_polygons_valid) {
|
||||
return new FlatRegion (m_merged_polygons, true);
|
||||
} else {
|
||||
return AsIfFlatRegion::merged (min_coherence (), 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
return clone ();
|
||||
}
|
||||
}
|
||||
|
||||
RegionDelegate *FlatRegion::add (const Region &other) const
|
||||
{
|
||||
std::auto_ptr<FlatRegion> new_region (new FlatRegion (*this));
|
||||
new_region->invalidate_cache ();
|
||||
new_region->set_is_merged (false);
|
||||
|
||||
FlatRegion *other_flat = dynamic_cast<FlatRegion *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer<db::Polygon, db::unstable_layer_tag> ().begin (), other_flat->raw_polygons ().get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
|
||||
|
||||
} else {
|
||||
|
||||
size_t n = new_region->raw_polygons ().size ();
|
||||
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
|
||||
new_region->raw_polygons ().reserve (db::Polygon::tag (), n);
|
||||
|
||||
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
new_region->raw_polygons ().insert (*p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new_region.release ();
|
||||
}
|
||||
|
||||
RegionDelegate *FlatRegion::add_in_place (const Region &other)
|
||||
{
|
||||
invalidate_cache ();
|
||||
m_is_merged = false;
|
||||
|
||||
FlatRegion *other_flat = dynamic_cast<FlatRegion *> (other.delegate ());
|
||||
if (other_flat) {
|
||||
|
||||
m_polygons.insert (other_flat->raw_polygons ().get_layer<db::Polygon, db::unstable_layer_tag> ().begin (), other_flat->raw_polygons ().get_layer<db::Polygon, db::unstable_layer_tag> ().end ());
|
||||
|
||||
} else {
|
||||
|
||||
size_t n = m_polygons.size ();
|
||||
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
++n;
|
||||
}
|
||||
|
||||
m_polygons.reserve (db::Polygon::tag (), n);
|
||||
|
||||
for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) {
|
||||
m_polygons.insert (*p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
const db::Polygon *FlatRegion::nth (size_t n) const
|
||||
{
|
||||
return n < m_polygons.size () ? &m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().begin () [n] : 0;
|
||||
}
|
||||
|
||||
bool FlatRegion::has_valid_polygons () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlatRegion::has_valid_merged_polygons () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const db::RecursiveShapeIterator *FlatRegion::iter () const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FlatRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
|
||||
{
|
||||
layout->cell (into_cell).shapes (into_layer).insert (m_polygons);
|
||||
}
|
||||
|
||||
void
|
||||
FlatRegion::insert (const db::Box &box)
|
||||
{
|
||||
if (! box.empty () && box.width () > 0 && box.height () > 0) {
|
||||
|
||||
if (empty ()) {
|
||||
|
||||
m_polygons.insert (db::Polygon (box));
|
||||
m_is_merged = true;
|
||||
update_bbox (box);
|
||||
|
||||
} else {
|
||||
|
||||
m_polygons.insert (db::Polygon (box));
|
||||
m_is_merged = false;
|
||||
invalidate_cache ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlatRegion::insert (const db::Path &path)
|
||||
{
|
||||
if (path.points () > 0) {
|
||||
m_polygons.insert (path.polygon ());
|
||||
m_is_merged = false;
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlatRegion::insert (const db::Polygon &polygon)
|
||||
{
|
||||
if (polygon.holes () > 0 || polygon.vertices () > 0) {
|
||||
m_polygons.insert (polygon);
|
||||
m_is_merged = false;
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlatRegion::insert (const db::SimplePolygon &polygon)
|
||||
{
|
||||
if (polygon.vertices () > 0) {
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (polygon.begin_hull (), polygon.end_hull ());
|
||||
m_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);
|
||||
m_polygons.insert (poly);
|
||||
m_is_merged = false;
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbFlatRegion
|
||||
#define HDR_dbFlatRegion
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbAsIfFlatRegion.h"
|
||||
#include "dbShapes.h"
|
||||
#include "dbShapes2.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief An iterator delegate for the flat region
|
||||
*/
|
||||
class DB_PUBLIC FlatRegionIterator
|
||||
: public RegionIteratorDelegate
|
||||
{
|
||||
public:
|
||||
typedef db::layer<db::Polygon, db::unstable_layer_tag> polygon_layer_type;
|
||||
typedef polygon_layer_type::iterator iterator_type;
|
||||
|
||||
FlatRegionIterator (iterator_type from, iterator_type to)
|
||||
: m_from (from), m_to (to)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual bool at_end () const
|
||||
{
|
||||
return m_from == m_to;
|
||||
}
|
||||
|
||||
virtual void increment ()
|
||||
{
|
||||
++m_from;
|
||||
}
|
||||
|
||||
virtual const value_type *get () const
|
||||
{
|
||||
return m_from.operator-> ();
|
||||
}
|
||||
|
||||
virtual RegionIteratorDelegate *clone () const
|
||||
{
|
||||
return new FlatRegionIterator (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Region;
|
||||
|
||||
iterator_type m_from, m_to;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A flat, polygon-set delegate
|
||||
*/
|
||||
class DB_PUBLIC FlatRegion
|
||||
: public AsIfFlatRegion
|
||||
{
|
||||
public:
|
||||
typedef db::layer<db::Polygon, db::unstable_layer_tag> polygon_layer_type;
|
||||
typedef polygon_layer_type::iterator polygon_iterator_type;
|
||||
|
||||
FlatRegion ();
|
||||
FlatRegion (const db::Shapes &polygons, bool is_merged);
|
||||
FlatRegion (bool is_merged);
|
||||
|
||||
FlatRegion (const FlatRegion &other);
|
||||
|
||||
virtual ~FlatRegion ();
|
||||
|
||||
RegionDelegate *clone () const
|
||||
{
|
||||
return new FlatRegion (*this);
|
||||
}
|
||||
|
||||
void reserve (size_t);
|
||||
|
||||
virtual RegionIteratorDelegate *begin () const;
|
||||
virtual RegionIteratorDelegate *begin_merged () const;
|
||||
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
|
||||
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const;
|
||||
|
||||
virtual bool empty () const;
|
||||
virtual size_t size () const;
|
||||
virtual bool is_merged () const;
|
||||
|
||||
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
|
||||
|
||||
virtual RegionDelegate *merged_in_place ();
|
||||
virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc);
|
||||
virtual RegionDelegate *merged () const;
|
||||
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const
|
||||
{
|
||||
return db::AsIfFlatRegion::merged (min_coherence, min_wc);
|
||||
}
|
||||
|
||||
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter);
|
||||
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter);
|
||||
|
||||
virtual RegionDelegate *add_in_place (const Region &other);
|
||||
virtual RegionDelegate *add (const Region &other) const;
|
||||
|
||||
virtual const db::Polygon *nth (size_t n) const;
|
||||
virtual bool has_valid_polygons () const;
|
||||
virtual bool has_valid_merged_polygons () const;
|
||||
|
||||
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);
|
||||
|
||||
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 (size () + (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);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Trans>
|
||||
void transform (const Trans &trans)
|
||||
{
|
||||
if (! trans.is_unity ()) {
|
||||
for (polygon_iterator_type p = m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().begin (); p != m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().end (); ++p) {
|
||||
m_polygons.get_layer<db::Polygon, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
|
||||
}
|
||||
invalidate_cache ();
|
||||
}
|
||||
}
|
||||
|
||||
db::Shapes &raw_polygons () { return m_polygons; }
|
||||
|
||||
protected:
|
||||
virtual void merged_semantics_changed ();
|
||||
virtual Box compute_bbox () const;
|
||||
void invalidate_cache ();
|
||||
void set_is_merged (bool m);
|
||||
|
||||
private:
|
||||
friend class AsIfFlatRegion;
|
||||
friend class Region;
|
||||
|
||||
FlatRegion &operator= (const FlatRegion &other);
|
||||
|
||||
bool m_is_merged;
|
||||
mutable db::Shapes m_polygons;
|
||||
mutable db::Shapes m_merged_polygons;
|
||||
mutable bool m_merged_polygons_valid;
|
||||
|
||||
void init ();
|
||||
void ensure_merged_polygons_valid () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -238,6 +238,18 @@ namespace std
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a displacement transformation
|
||||
*/
|
||||
template <class C>
|
||||
struct hash <db::disp_trans<C> >
|
||||
{
|
||||
size_t operator() (const db::disp_trans<C> &t) const
|
||||
{
|
||||
return hfunc (t.disp ());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Hash value for a complex transformation
|
||||
*/
|
||||
|
|
@ -295,6 +307,54 @@ namespace std
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a shape reference
|
||||
*/
|
||||
template <class Shape, class Trans>
|
||||
struct hash<db::shape_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::shape_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return hfunc (std::hash<Shape> () (*o.ptr ()), std::hash<Trans> () (o.trans ()));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a polygon reference
|
||||
*/
|
||||
template <class Shape, class Trans>
|
||||
struct hash<db::polygon_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::polygon_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return std::hash<db::shape_ref<Shape, Trans> > () (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a path reference
|
||||
*/
|
||||
template <class Shape, class Trans>
|
||||
struct hash<db::path_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::path_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return std::hash<db::shape_ref<Shape, Trans> > () (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash function for a text reference
|
||||
*/
|
||||
template <class Shape, class Trans>
|
||||
struct hash<db::text_ref<Shape, Trans> >
|
||||
{
|
||||
size_t operator() (const db::text_ref<Shape, Trans> &o) const
|
||||
{
|
||||
return std::hash<db::shape_ref<Shape, Trans> > () (o);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A hash value for a db::LayerProperties object
|
||||
*/
|
||||
|
|
@ -328,6 +388,38 @@ namespace std
|
|||
return hfunc (hf2 (p.second), h);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Generic hash for an unordered set
|
||||
*/
|
||||
template <class T>
|
||||
struct hash<std::unordered_set<T> >
|
||||
{
|
||||
size_t operator() (const std::unordered_set<T> &o) const
|
||||
{
|
||||
size_t hf = 0;
|
||||
for (typename std::unordered_set<T>::const_iterator i = o.begin (); i != o.end (); ++i) {
|
||||
hf = hfunc (hf, std::hash <T> () (*i));
|
||||
}
|
||||
return hf;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Generic hash for an ordered set
|
||||
*/
|
||||
template <class T>
|
||||
struct hash<std::set<T> >
|
||||
{
|
||||
size_t operator() (const std::set<T> &o) const
|
||||
{
|
||||
size_t hf = 0;
|
||||
for (typename std::set<T>::const_iterator i = o.begin (); i != o.end (); ++i) {
|
||||
hf = hfunc (hf, std::hash <T> () (*i));
|
||||
}
|
||||
return hf;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,455 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbHierProcessor
|
||||
#define HDR_dbHierProcessor
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbLocalOperation.h"
|
||||
#include "tlThreadedWorkers.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "dbHash.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
template <class TS, class TI, class TR> class local_processor;
|
||||
template <class TS, class TI, class TR> class local_processor_cell_context;
|
||||
template <class TS, class TI, class TR> class local_processor_contexts;
|
||||
|
||||
// TODO: move this somewhere else?
|
||||
template <class TS, class TI>
|
||||
class DB_PUBLIC shape_interactions
|
||||
{
|
||||
public:
|
||||
typedef std::unordered_map<unsigned int, std::vector<unsigned int> > container;
|
||||
typedef container::const_iterator iterator;
|
||||
typedef container::value_type::second_type::const_iterator iterator2;
|
||||
typedef typename std::unordered_map<unsigned int, TS>::const_iterator subject_iterator;
|
||||
typedef typename std::unordered_map<unsigned int, TI>::const_iterator intruder_iterator;
|
||||
|
||||
shape_interactions ();
|
||||
|
||||
iterator begin () const
|
||||
{
|
||||
return m_interactions.begin ();
|
||||
}
|
||||
|
||||
iterator end () const
|
||||
{
|
||||
return m_interactions.end ();
|
||||
}
|
||||
|
||||
subject_iterator begin_subjects () const
|
||||
{
|
||||
return m_subject_shapes.begin ();
|
||||
}
|
||||
|
||||
subject_iterator end_subjects () const
|
||||
{
|
||||
return m_subject_shapes.end ();
|
||||
}
|
||||
|
||||
intruder_iterator begin_intruders () const
|
||||
{
|
||||
return m_intruder_shapes.begin ();
|
||||
}
|
||||
|
||||
intruder_iterator end_intruders () const
|
||||
{
|
||||
return m_intruder_shapes.end ();
|
||||
}
|
||||
|
||||
bool has_intruder_shape_id (unsigned int id) const;
|
||||
bool has_subject_shape_id (unsigned int id) const;
|
||||
void add_intruder_shape (unsigned int id, const TI &shape);
|
||||
void add_subject_shape (unsigned int id, const TS &shape);
|
||||
void add_subject (unsigned int id, const TS &shape);
|
||||
void add_interaction (unsigned int subject_id, unsigned int intruder_id);
|
||||
const std::vector<unsigned int> &intruders_for (unsigned int subject_id) const;
|
||||
const TS &subject_shape (unsigned int id) const;
|
||||
const TI &intruder_shape (unsigned int id) const;
|
||||
|
||||
unsigned int next_id ()
|
||||
{
|
||||
return ++m_id;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<unsigned int, std::vector<unsigned int> > m_interactions;
|
||||
std::unordered_map<unsigned int, TS> m_subject_shapes;
|
||||
std::unordered_map<unsigned int, TI> m_intruder_shapes;
|
||||
unsigned int m_id;
|
||||
};
|
||||
|
||||
// TODO: should be hidden (private data?)
|
||||
template <class TS, class TI, class TR>
|
||||
struct DB_PUBLIC local_processor_cell_drop
|
||||
{
|
||||
local_processor_cell_drop (db::local_processor_cell_context<TS, TI, TR> *_parent_context, db::Cell *_parent, const db::ICplxTrans &_cell_inst)
|
||||
: parent_context (_parent_context), parent (_parent), cell_inst (_cell_inst)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
db::local_processor_cell_context<TS, TI, TR> *parent_context;
|
||||
db::Cell *parent;
|
||||
db::ICplxTrans cell_inst;
|
||||
};
|
||||
|
||||
// TODO: should be hidden (private data?)
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor_cell_context
|
||||
{
|
||||
public:
|
||||
typedef std::pair<const db::Cell *, db::ICplxTrans> parent_inst_type;
|
||||
typedef typename std::vector<local_processor_cell_drop<TS, TI, TR> >::const_iterator drop_iterator;
|
||||
|
||||
local_processor_cell_context ();
|
||||
local_processor_cell_context (const local_processor_cell_context &other);
|
||||
|
||||
void add (db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *parent, const db::ICplxTrans &cell_inst);
|
||||
void propagate (const std::unordered_set<TR> &res);
|
||||
|
||||
std::unordered_set<TR> &propagated ()
|
||||
{
|
||||
return m_propagated;
|
||||
}
|
||||
|
||||
const std::unordered_set<TR> &propagated () const
|
||||
{
|
||||
return m_propagated;
|
||||
}
|
||||
|
||||
size_t size () const
|
||||
{
|
||||
return m_drops.size ();
|
||||
}
|
||||
|
||||
tl::Mutex &lock ()
|
||||
{
|
||||
return m_lock;
|
||||
}
|
||||
|
||||
// used for debugging purposes only
|
||||
drop_iterator begin_drops () const
|
||||
{
|
||||
return m_drops.begin ();
|
||||
}
|
||||
|
||||
// used for debugging purposes only
|
||||
drop_iterator end_drops () const
|
||||
{
|
||||
return m_drops.end ();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_set<TR> m_propagated;
|
||||
std::vector<local_processor_cell_drop<TS, TI, TR> > m_drops;
|
||||
tl::Mutex m_lock;
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor_cell_contexts
|
||||
{
|
||||
public:
|
||||
typedef std::pair<std::set<CellInstArray>, std::set<TI> > context_key_type;
|
||||
typedef std::unordered_map<context_key_type, db::local_processor_cell_context<TS, TI, TR> > context_map_type;
|
||||
typedef typename context_map_type::const_iterator iterator;
|
||||
|
||||
local_processor_cell_contexts ();
|
||||
local_processor_cell_contexts (const db::Cell *intruder_cell);
|
||||
|
||||
db::local_processor_cell_context<TS, TI, TR> *find_context (const context_key_type &intruders);
|
||||
db::local_processor_cell_context<TS, TI, TR> *create (const context_key_type &intruders);
|
||||
void compute_results (const local_processor_contexts<TS, TI, TR> &contexts, db::Cell *cell, const local_operation<TS, TI, TR> *op, unsigned int output_layer, const local_processor<TS, TI, TR> *proc);
|
||||
|
||||
iterator begin () const
|
||||
{
|
||||
return m_contexts.begin ();
|
||||
}
|
||||
|
||||
iterator end () const
|
||||
{
|
||||
return m_contexts.end ();
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Cell *mp_intruder_cell;
|
||||
std::unordered_map<context_key_type, db::local_processor_cell_context<TS, TI, TR> > m_contexts;
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor_contexts
|
||||
{
|
||||
public:
|
||||
typedef std::unordered_map<db::Cell *, local_processor_cell_contexts<TS, TI, TR> > contexts_per_cell_type;
|
||||
typedef typename contexts_per_cell_type::iterator iterator;
|
||||
|
||||
local_processor_contexts ()
|
||||
: m_subject_layer (0), m_intruder_layer (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
local_processor_contexts (const local_processor_contexts &other)
|
||||
: m_contexts_per_cell (other.m_contexts_per_cell), m_subject_layer (other.m_subject_layer), m_intruder_layer (other.m_intruder_layer)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
m_contexts_per_cell.clear ();
|
||||
}
|
||||
|
||||
local_processor_cell_contexts<TS, TI, TR> &contexts_per_cell (db::Cell *subject_cell, const db::Cell *intruder_cell)
|
||||
{
|
||||
typename contexts_per_cell_type::iterator ctx = m_contexts_per_cell.find (subject_cell);
|
||||
if (ctx == m_contexts_per_cell.end ()) {
|
||||
ctx = m_contexts_per_cell.insert (std::make_pair (subject_cell, local_processor_cell_contexts<TS, TI, TR> (intruder_cell))).first;
|
||||
}
|
||||
return ctx->second;
|
||||
}
|
||||
|
||||
contexts_per_cell_type &context_map ()
|
||||
{
|
||||
return m_contexts_per_cell;
|
||||
}
|
||||
|
||||
iterator begin ()
|
||||
{
|
||||
return m_contexts_per_cell.begin ();
|
||||
}
|
||||
|
||||
iterator end ()
|
||||
{
|
||||
return m_contexts_per_cell.end ();
|
||||
}
|
||||
|
||||
void set_subject_layer (unsigned int l)
|
||||
{
|
||||
m_subject_layer = l;
|
||||
}
|
||||
|
||||
unsigned int subject_layer () const
|
||||
{
|
||||
return m_subject_layer;
|
||||
}
|
||||
|
||||
void set_intruder_layer (unsigned int l)
|
||||
{
|
||||
m_intruder_layer = l;
|
||||
}
|
||||
|
||||
unsigned int intruder_layer () const
|
||||
{
|
||||
return m_intruder_layer;
|
||||
}
|
||||
|
||||
tl::Mutex &lock () const
|
||||
{
|
||||
return m_lock;
|
||||
}
|
||||
|
||||
private:
|
||||
contexts_per_cell_type m_contexts_per_cell;
|
||||
unsigned int m_subject_layer, m_intruder_layer;
|
||||
mutable tl::Mutex m_lock;
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor_context_computation_task
|
||||
: public tl::Task
|
||||
{
|
||||
public:
|
||||
local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist);
|
||||
void perform ();
|
||||
|
||||
private:
|
||||
const local_processor<TS, TI, TR> *mp_proc;
|
||||
local_processor_contexts<TS, TI, TR> *mp_contexts;
|
||||
db::local_processor_cell_context<TS, TI, TR> *mp_parent_context;
|
||||
db::Cell *mp_subject_parent;
|
||||
db::Cell *mp_subject_cell;
|
||||
db::ICplxTrans m_subject_cell_inst;
|
||||
const db::Cell *mp_intruder_cell;
|
||||
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type m_intruders;
|
||||
db::Coord m_dist;
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor_context_computation_worker
|
||||
: public tl::Worker
|
||||
{
|
||||
public:
|
||||
local_processor_context_computation_worker ()
|
||||
: tl::Worker ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void perform_task (tl::Task *task)
|
||||
{
|
||||
static_cast<local_processor_context_computation_task<TS, TI, TR> *> (task)->perform ();
|
||||
}
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor_result_computation_task
|
||||
: public tl::Task
|
||||
{
|
||||
public:
|
||||
local_processor_result_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::Cell *cell, local_processor_cell_contexts<TS, TI, TR> *cell_contexts, const local_operation<TS, TI, TR> *op, unsigned int output_layer);
|
||||
void perform ();
|
||||
|
||||
private:
|
||||
const local_processor<TS, TI, TR> *mp_proc;
|
||||
local_processor_contexts<TS, TI, TR> *mp_contexts;
|
||||
db::Cell *mp_cell;
|
||||
local_processor_cell_contexts<TS, TI, TR> *mp_cell_contexts;
|
||||
const local_operation<TS, TI, TR> *mp_op;
|
||||
unsigned int m_output_layer;
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor_result_computation_worker
|
||||
: public tl::Worker
|
||||
{
|
||||
public:
|
||||
local_processor_result_computation_worker ()
|
||||
: tl::Worker ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void perform_task (tl::Task *task)
|
||||
{
|
||||
static_cast<local_processor_result_computation_task<TS, TI, TR> *> (task)->perform ();
|
||||
}
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_processor
|
||||
{
|
||||
public:
|
||||
local_processor (db::Layout *layout, db::Cell *top);
|
||||
local_processor (db::Layout *subject_layout, db::Cell *subject_top, const db::Layout *intruder_layout, const db::Cell *intruder_cell);
|
||||
void run (local_operation<TS, TI, TR> *op, unsigned int subject_layer, unsigned int intruder_layer, unsigned int output_layer);
|
||||
void compute_contexts (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, unsigned int subject_layer, unsigned int intruder_layer) const;
|
||||
void compute_results (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, unsigned int output_layer) const;
|
||||
|
||||
void set_description (const std::string &d)
|
||||
{
|
||||
m_description = d;
|
||||
}
|
||||
|
||||
void set_base_verbosity (int vb)
|
||||
{
|
||||
m_base_verbosity = vb;
|
||||
}
|
||||
|
||||
int base_verbosity () const
|
||||
{
|
||||
return m_base_verbosity;
|
||||
}
|
||||
|
||||
void set_threads (unsigned int nthreads)
|
||||
{
|
||||
m_nthreads = nthreads;
|
||||
}
|
||||
|
||||
unsigned int threads () const
|
||||
{
|
||||
return m_nthreads;
|
||||
}
|
||||
|
||||
void set_max_vertex_count (size_t max_vertex_count)
|
||||
{
|
||||
m_max_vertex_count = max_vertex_count;
|
||||
}
|
||||
|
||||
size_t max_vertex_count () const
|
||||
{
|
||||
return m_max_vertex_count;
|
||||
}
|
||||
|
||||
void set_area_ratio (double area_ratio)
|
||||
{
|
||||
m_area_ratio = area_ratio;
|
||||
}
|
||||
|
||||
double area_ratio () const
|
||||
{
|
||||
return m_area_ratio;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename, typename, typename> friend class local_processor_cell_contexts;
|
||||
template<typename, typename, typename> friend class local_processor_context_computation_task;
|
||||
|
||||
db::Layout *mp_subject_layout;
|
||||
const db::Layout *mp_intruder_layout;
|
||||
db::Cell *mp_subject_top;
|
||||
const db::Cell *mp_intruder_top;
|
||||
std::string m_description;
|
||||
unsigned int m_nthreads;
|
||||
size_t m_max_vertex_count;
|
||||
double m_area_ratio;
|
||||
int m_base_verbosity;
|
||||
mutable std::auto_ptr<tl::Job<local_processor_context_computation_worker<TS, TI, TR> > > mp_cc_job;
|
||||
|
||||
std::string description (const local_operation<TS, TI, TR> *op) const;
|
||||
void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
|
||||
void do_compute_contexts (db::local_processor_cell_context<TS, TI, TR> *cell_context, const db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
|
||||
void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
|
||||
void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set<TR> &result) const;
|
||||
void compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::unordered_set<TR> &result) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
struct type_traits<db::local_processor<TS, TI, TR> > : public tl::type_traits<void>
|
||||
{
|
||||
// mark "LocalProcessor" as not having a default ctor and no copy ctor
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,663 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbRecursiveShapeIterator.h"
|
||||
#include "dbHierarchyBuilder.h"
|
||||
#include "dbClip.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbPolygonTools.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
static HierarchyBuilderShapeInserter def_inserter;
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
int
|
||||
compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2)
|
||||
{
|
||||
if ((iter1.layout () == 0) != (iter2.layout () == 0)) {
|
||||
return (iter1.layout () == 0) < (iter2.layout () == 0);
|
||||
}
|
||||
if ((iter1.top_cell () == 0) != (iter2.top_cell () == 0)) {
|
||||
return (iter1.top_cell () == 0) < (iter2.top_cell () == 0);
|
||||
}
|
||||
|
||||
// basic source (layout, top_cell) needs to be the same of course
|
||||
if (iter1.layout () != iter2.layout ()) {
|
||||
// NOTE: pointer compare :-(
|
||||
return iter1.layout () < iter2.layout () ? -1 : 1;
|
||||
}
|
||||
if (iter1.top_cell ()) {
|
||||
if (iter1.top_cell ()->cell_index () != iter2.top_cell ()->cell_index ()) {
|
||||
return iter1.top_cell ()->cell_index () < iter2.top_cell ()->cell_index () ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// max depth controls the main hierarchical appearance
|
||||
if (iter1.max_depth () != iter2.max_depth ()) {
|
||||
return iter1.max_depth () < iter2.max_depth () ? -1 : 1;
|
||||
}
|
||||
|
||||
// if a region is set, the hierarchical appearance is the same only if the layers and
|
||||
// complex region are identical
|
||||
if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) {
|
||||
return (iter1.region () == db::Box::world ()) < (iter2.region () == db::Box::world ()) ? -1 : 1;
|
||||
}
|
||||
|
||||
if (iter1.region () != db::Box::world ()) {
|
||||
if (iter1.has_complex_region () != iter2.has_complex_region ()) {
|
||||
return iter1.has_complex_region () < iter2.has_complex_region () ? -1 : 1;
|
||||
}
|
||||
if (iter1.has_complex_region () && iter1.complex_region () != iter2.complex_region ()) {
|
||||
return iter1.complex_region () < iter2.complex_region () ? -1 : 1;
|
||||
}
|
||||
if (iter1.region () != iter2.region ()) {
|
||||
return iter1.region () < iter2.region () ? -1 : 1;
|
||||
}
|
||||
if (iter1.multiple_layers () != iter2.multiple_layers ()) {
|
||||
return iter1.multiple_layers () < iter2.multiple_layers () ? -1 : 1;
|
||||
}
|
||||
if (iter1.multiple_layers ()) {
|
||||
if (iter1.layers () != iter2.layers ()) {
|
||||
return iter1.layers () < iter2.layers () ? -1 : 1;
|
||||
}
|
||||
} else {
|
||||
if (iter1.layer () != iter2.layer ()) {
|
||||
return iter1.layer () < iter2.layer () ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Computes the clip variant (a box set) from a cell bbox, a region and a complex region (optional)
|
||||
*/
|
||||
static std::pair<bool, std::set<db::Box> > compute_clip_variant (const db::Box &cell_bbox, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region)
|
||||
{
|
||||
if (region == db::Box::world ()) {
|
||||
return std::make_pair (true, std::set<db::Box> ());
|
||||
}
|
||||
|
||||
db::ICplxTrans trans_inv (trans.inverted ());
|
||||
db::Box region_in_cell = region.transformed (trans_inv);
|
||||
|
||||
std::set<db::Box> clip_variant;
|
||||
if (! cell_bbox.overlaps (region_in_cell)) {
|
||||
// an empty clip variant should not happen, but who knows
|
||||
return std::make_pair (false, std::set<db::Box> ());
|
||||
}
|
||||
|
||||
db::Box rect_box = region_in_cell & cell_bbox;
|
||||
|
||||
if (complex_region) {
|
||||
|
||||
for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert<db::Box> ()); ! cr.at_end (); ++cr) {
|
||||
db::Box cr_in_cell = (*cr).transformed (trans_inv);
|
||||
if (rect_box.overlaps (cr_in_cell)) {
|
||||
clip_variant.insert (rect_box * cr_in_cell);
|
||||
}
|
||||
}
|
||||
|
||||
if (clip_variant.empty ()) {
|
||||
// an empty clip variant should not happen, but who knows
|
||||
return std::make_pair (false, std::set<db::Box> ());
|
||||
}
|
||||
|
||||
} else {
|
||||
clip_variant.insert (rect_box);
|
||||
}
|
||||
|
||||
return std::make_pair (true, clip_variant);
|
||||
}
|
||||
|
||||
HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
|
||||
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_trans (trans)
|
||||
{
|
||||
set_shape_receiver (pipe);
|
||||
}
|
||||
|
||||
HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
|
||||
: mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_trans (trans)
|
||||
{
|
||||
set_shape_receiver (pipe);
|
||||
}
|
||||
|
||||
HierarchyBuilder::~HierarchyBuilder ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::set_shape_receiver (HierarchyBuilderShapeReceiver *pipe)
|
||||
{
|
||||
mp_pipe = pipe ? pipe : &def_inserter;
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::reset ()
|
||||
{
|
||||
m_initial_pass = true;
|
||||
mp_initial_cell = 0;
|
||||
|
||||
m_cells_to_be_filled.clear ();
|
||||
m_cell_map.clear ();
|
||||
m_cells_seen.clear ();
|
||||
m_cell_stack.clear ();
|
||||
m_cm_entry = cell_map_type::const_iterator ();
|
||||
m_cm_new_entry = false;
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_type var)
|
||||
{
|
||||
// non_var (despite it's name) may be a variant created previously.
|
||||
variant_to_original_target_map_type::const_iterator v = m_variants_to_original_target_map.find (non_var);
|
||||
if (v != m_variants_to_original_target_map.end ()) {
|
||||
non_var = v->second;
|
||||
}
|
||||
|
||||
m_original_targets_to_variants_map [non_var].push_back (var);
|
||||
m_variants_to_original_target_map.insert (std::make_pair (var, non_var));
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::begin (const RecursiveShapeIterator *iter)
|
||||
{
|
||||
if (m_initial_pass) {
|
||||
m_source = *iter;
|
||||
} else {
|
||||
tl_assert (compare_iterators_with_respect_to_target_hierarchy (m_source, *iter) == 0);
|
||||
}
|
||||
|
||||
m_cell_stack.clear ();
|
||||
m_cells_seen.clear ();
|
||||
|
||||
if (! iter->layout () || ! iter->top_cell ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::pair<db::cell_index_type, std::set<db::Box> > key (iter->top_cell ()->cell_index (), std::set<db::Box> ());
|
||||
m_cm_entry = m_cell_map.find (key);
|
||||
|
||||
if (m_cm_entry == m_cell_map.end ()) {
|
||||
db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first));
|
||||
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first;
|
||||
}
|
||||
|
||||
db::Cell &new_top = mp_target->cell (m_cm_entry->second);
|
||||
m_cells_seen.insert (key);
|
||||
|
||||
// NOTE: we consider the top cell "new" if it does not have instances.
|
||||
// We can do so as the recursive shape iterator will always deliver all instances
|
||||
// and not a partial set of instances.
|
||||
m_cm_new_entry = new_top.begin ().at_end ();
|
||||
m_cell_stack.push_back (std::make_pair (m_cm_new_entry, std::vector<db::Cell *> ()));
|
||||
m_cell_stack.back ().second.push_back (&new_top);
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::end (const RecursiveShapeIterator *iter)
|
||||
{
|
||||
tl_assert (! iter->layout () || ! iter->top_cell () || m_cell_stack.size () == 1);
|
||||
|
||||
m_initial_pass = false;
|
||||
m_cells_seen.clear ();
|
||||
mp_initial_cell = m_cell_stack.empty () ? 0 : m_cell_stack.front ().second.front ();
|
||||
m_cell_stack.clear ();
|
||||
m_cm_entry = cell_map_type::const_iterator ();
|
||||
m_cm_new_entry = false;
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/)
|
||||
{
|
||||
tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ());
|
||||
|
||||
m_cells_seen.insert (m_cm_entry->first);
|
||||
|
||||
bool new_cell = (m_cells_to_be_filled.find (m_cm_entry->second) != m_cells_to_be_filled.end ());
|
||||
if (new_cell) {
|
||||
m_cells_to_be_filled.erase (m_cm_entry->second);
|
||||
}
|
||||
|
||||
m_cell_stack.push_back (std::make_pair (new_cell, std::vector<db::Cell *> ()));
|
||||
|
||||
original_target_to_variants_map_type::const_iterator v = m_original_targets_to_variants_map.find (m_cm_entry->second);
|
||||
if (v != m_original_targets_to_variants_map.end ()) {
|
||||
for (std::vector<db::cell_index_type>::const_iterator i = v->second.begin (); i != v->second.end (); ++i) {
|
||||
m_cell_stack.back ().second.push_back (&mp_target->cell (*i));
|
||||
}
|
||||
} else {
|
||||
m_cell_stack.back ().second.push_back (&mp_target->cell (m_cm_entry->second));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/)
|
||||
{
|
||||
m_cell_stack.pop_back ();
|
||||
}
|
||||
|
||||
HierarchyBuilder::new_inst_mode
|
||||
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
|
||||
{
|
||||
if (all) {
|
||||
|
||||
std::pair<db::cell_index_type, std::set<db::Box> > key (inst.object ().cell_index (), std::set<db::Box> ());
|
||||
m_cm_entry = m_cell_map.find (key);
|
||||
m_cm_new_entry = false;
|
||||
|
||||
if (m_cm_entry == m_cell_map.end ()) {
|
||||
db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ()));
|
||||
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
|
||||
m_cm_new_entry = true;
|
||||
m_cells_to_be_filled.insert (new_cell);
|
||||
}
|
||||
|
||||
// for new cells, create this instance
|
||||
if (m_cell_stack.back ().first) {
|
||||
db::CellInstArray new_inst (inst, &mp_target->array_repository ());
|
||||
new_inst.object () = db::CellInst (m_cm_entry->second);
|
||||
new_inst.transform_into (m_trans);
|
||||
for (std::vector<db::Cell *>::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
|
||||
(*c)->insert (new_inst);
|
||||
}
|
||||
}
|
||||
|
||||
// To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array.
|
||||
return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip;
|
||||
|
||||
} else {
|
||||
|
||||
// Iterate by instance array members
|
||||
return NI_all;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all)
|
||||
{
|
||||
if (all) {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
db::Box cell_bbox = iter->layout ()->cell (inst.object ().cell_index ()).bbox ();
|
||||
std::pair<bool, std::set<db::Box> > clip_variant = compute_clip_variant (cell_bbox, trans, region, complex_region);
|
||||
if (! clip_variant.first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<db::cell_index_type, std::set<db::Box> > key (inst.object ().cell_index (), clip_variant.second);
|
||||
m_cm_entry = m_cell_map.find (key);
|
||||
m_cm_new_entry = false;
|
||||
|
||||
if (m_cm_entry == m_cell_map.end ()) {
|
||||
std::string suffix;
|
||||
if (! key.second.empty ()) {
|
||||
suffix = "$CLIP_VAR";
|
||||
}
|
||||
db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ());
|
||||
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
|
||||
m_cm_new_entry = true;
|
||||
m_cells_to_be_filled.insert (new_cell);
|
||||
}
|
||||
|
||||
// for a new cell, create this instance
|
||||
if (m_cell_stack.back ().first) {
|
||||
db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans);
|
||||
new_inst.transform_into (m_trans);
|
||||
for (std::vector<db::Cell *>::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
|
||||
(*c)->insert (new_inst);
|
||||
}
|
||||
}
|
||||
|
||||
return (m_cells_seen.find (key) == m_cells_seen.end ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region)
|
||||
{
|
||||
for (std::vector<db::Cell *>::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
|
||||
db::Shapes &shapes = (*c)->shapes (m_target_layer);
|
||||
mp_pipe->push (shape, m_trans, region, complex_region, &shapes);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
ClippingHierarchyBuilderShapeReceiver::ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe)
|
||||
: mp_pipe (pipe ? pipe : &def_inserter)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
ClippingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
static db::Box world = db::Box::world ();
|
||||
|
||||
if (region == world || is_inside (shape.bbox (), region, complex_region)) {
|
||||
|
||||
mp_pipe->push (shape, trans, world, 0, target);
|
||||
|
||||
} else if (! is_outside (shape.bbox (), region, complex_region)) {
|
||||
|
||||
// clip the shape if required
|
||||
if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) {
|
||||
mp_pipe->push (shape, trans, world, 0, target);
|
||||
} else if (shape.is_box ()) {
|
||||
insert_clipped (shape.box (), trans, region, complex_region, target);
|
||||
} else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) {
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
insert_clipped (poly, trans, region, complex_region, target);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClippingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
static db::Box world = db::Box::world ();
|
||||
|
||||
if (! complex_region) {
|
||||
db::Box r = shape & region;
|
||||
if (! r.empty()) {
|
||||
mp_pipe->push (r, trans, world, 0, target);
|
||||
}
|
||||
} else {
|
||||
insert_clipped (shape, trans, region, complex_region, target);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClippingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
static db::Box world = db::Box::world ();
|
||||
|
||||
if (region == world || (shape.box ().inside (region) && ! complex_region)) {
|
||||
mp_pipe->push (shape, trans, world, 0, target);
|
||||
} else {
|
||||
insert_clipped (shape, trans, region, complex_region, target);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ClippingHierarchyBuilderShapeReceiver::is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region)
|
||||
{
|
||||
if (region == db::Box::world ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (box.inside (region)) {
|
||||
|
||||
db::Box rect_box = region & box;
|
||||
|
||||
if (complex_region) {
|
||||
|
||||
// TODO: this is not a real test for being inside a complex region
|
||||
for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert<db::Box> ()); ! cr.at_end (); ++cr) {
|
||||
if (rect_box.inside (*cr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ClippingHierarchyBuilderShapeReceiver::is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region)
|
||||
{
|
||||
if (region == db::Box::world ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (box.overlaps (region)) {
|
||||
|
||||
db::Box rect_box = region & box;
|
||||
|
||||
if (complex_region) {
|
||||
for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (rect_box, db::box_convert<db::Box> ()); ! cr.at_end (); ++cr) {
|
||||
if (rect_box.overlaps (*cr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
db::Box bb = box & region;
|
||||
static db::Box world = db::Box::world ();
|
||||
|
||||
if (complex_region) {
|
||||
for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (bb, db::box_convert<db::Box> ()); ! cr.at_end (); ++cr) {
|
||||
mp_pipe->push (*cr & bb, trans, world, 0, target);
|
||||
}
|
||||
} else {
|
||||
mp_pipe->push (bb, trans, world, 0, target);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
std::vector<db::Polygon> clipped_poly;
|
||||
static db::Box world = db::Box::world ();
|
||||
|
||||
if (complex_region) {
|
||||
// TODO: is this good way to clip a polygon at a complex boundary?
|
||||
for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (region, db::box_convert<db::Box> ()); ! cr.at_end (); ++cr) {
|
||||
db::clip_poly (poly, *cr & region, clipped_poly);
|
||||
}
|
||||
} else {
|
||||
db::clip_poly (poly, region, clipped_poly);
|
||||
}
|
||||
|
||||
for (std::vector<db::Polygon>::const_iterator p = clipped_poly.begin (); p != clipped_poly.end (); ++p) {
|
||||
mp_pipe->push (*p, trans, world, 0, target);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
ReducingHierarchyBuilderShapeReceiver::ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe, double area_ratio, size_t max_vertex_count)
|
||||
: mp_pipe (pipe ? pipe : &def_inserter), m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
ReducingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
if (shape.is_text () || shape.is_edge () || shape.is_edge_pair ()) {
|
||||
mp_pipe->push (shape, trans, region, complex_region, target);
|
||||
} else if (shape.is_box ()) {
|
||||
mp_pipe->push (shape.box (), trans, region, complex_region, target);
|
||||
} else if (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) {
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
reduce (poly, trans, region, complex_region, target);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ReducingHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
mp_pipe->push (shape, trans, region, complex_region, target);
|
||||
}
|
||||
|
||||
void
|
||||
ReducingHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
reduce (shape, trans, region, complex_region, target);
|
||||
}
|
||||
|
||||
void
|
||||
ReducingHierarchyBuilderShapeReceiver::reduce (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
if (poly.vertices () > m_max_vertex_count || poly.area_ratio () > m_area_ratio) {
|
||||
|
||||
std::vector <db::Polygon> split_polygons;
|
||||
db::split_polygon (poly, split_polygons);
|
||||
for (std::vector <db::Polygon>::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) {
|
||||
reduce (*sp, trans, region, complex_region, target);
|
||||
}
|
||||
|
||||
} else {
|
||||
mp_pipe->push (poly, trans, region, complex_region, target);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PolygonReferenceHierarchyBuilderShapeReceiver::PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement, const tl::Variant &text_prop_name)
|
||||
: mp_layout (layout), m_text_enlargement (text_enlargement), m_make_text_prop (false), m_text_prop_id (0)
|
||||
{
|
||||
if (! text_prop_name.is_nil ()) {
|
||||
m_text_prop_id = layout->properties_repository ().prop_name_id (text_prop_name);
|
||||
m_make_text_prop = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
if (shape.is_box () || shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ()) {
|
||||
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
if (! trans.is_unity ()) {
|
||||
poly.transform (trans);
|
||||
}
|
||||
target->insert (db::PolygonRef (poly, mp_layout->shape_repository ()));
|
||||
|
||||
} else if (shape.is_text () && m_text_enlargement >= 0) {
|
||||
|
||||
db::Polygon poly (shape.text_trans () * db::Box (-m_text_enlargement, -m_text_enlargement, m_text_enlargement, m_text_enlargement));
|
||||
if (! trans.is_unity ()) {
|
||||
poly.transform (trans);
|
||||
}
|
||||
db::PolygonRef pref (poly, mp_layout->shape_repository ());
|
||||
|
||||
if (m_make_text_prop) {
|
||||
|
||||
db::PropertiesRepository::properties_set ps;
|
||||
ps.insert (std::make_pair (m_text_prop_id, tl::Variant (shape.text_string ())));
|
||||
db::properties_id_type pid = mp_layout->properties_repository ().properties_id (ps);
|
||||
|
||||
target->insert (db::PolygonRefWithProperties (pref, pid));
|
||||
|
||||
} else {
|
||||
target->insert (pref);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
target->insert (db::PolygonRef (db::Polygon (shape.transformed (trans)), mp_layout->shape_repository ()));
|
||||
}
|
||||
|
||||
void PolygonReferenceHierarchyBuilderShapeReceiver::push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
target->insert (db::PolygonRef (shape.transformed (trans), mp_layout->shape_repository ()));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
EdgeBuildingHierarchyBuilderShapeReceiver::EdgeBuildingHierarchyBuilderShapeReceiver (bool as_edges)
|
||||
: m_as_edges (as_edges)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
|
||||
{
|
||||
if (m_as_edges && (shape.is_polygon () || shape.is_simple_polygon () || shape.is_path ())) {
|
||||
db::Polygon poly;
|
||||
shape.polygon (poly);
|
||||
push (poly, trans, region, complex_region, target);
|
||||
} else if (m_as_edges && shape.is_box ()) {
|
||||
push (shape.box (), trans, region, complex_region, target);
|
||||
} else if (shape.is_edge ()) {
|
||||
target->insert (shape.edge ());
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Box &box, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
if (m_as_edges && ! box.empty ()) {
|
||||
target->insert (db::Edge (box.p1 (), box.upper_left ()).transformed (trans));
|
||||
target->insert (db::Edge (box.upper_left (), box.p2 ()).transformed (trans));
|
||||
target->insert (db::Edge (box.p2 (), box.lower_right ()).transformed (trans));
|
||||
target->insert (db::Edge (box.lower_right (), box.p1 ()).transformed (trans));
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeBuildingHierarchyBuilderShapeReceiver::push (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
if (m_as_edges) {
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
target->insert ((*e).transformed (trans));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
EdgePairBuildingHierarchyBuilderShapeReceiver::EdgePairBuildingHierarchyBuilderShapeReceiver ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void EdgePairBuildingHierarchyBuilderShapeReceiver::push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const db::RecursiveShapeReceiver::box_tree_type * /*complex_region*/, db::Shapes *target)
|
||||
{
|
||||
if (shape.is_edge_pair ()) {
|
||||
target->insert (shape.edge_pair ().transformed (trans));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbHierarchyBuilder
|
||||
#define HDR_dbHierarchyBuilder
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbRecursiveShapeIterator.h"
|
||||
#include "dbLayout.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A helper function comparing two recursive shape iterators for compatibility with respect to hierarchy building
|
||||
*
|
||||
* This function will return -1, 0 or 1 depending on whether the two iterators
|
||||
* can be used with the same builder (0) or whether they are less (-1) or greater (1).
|
||||
*/
|
||||
int DB_PUBLIC compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2);
|
||||
|
||||
/**
|
||||
* @brief A class to receive shapes from the hierarchy builder
|
||||
*
|
||||
* This class can be reimplemented to implement clipping and/or
|
||||
* simplification.
|
||||
*/
|
||||
class DB_PUBLIC HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
HierarchyBuilderShapeReceiver () { }
|
||||
virtual ~HierarchyBuilderShapeReceiver () { }
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0;
|
||||
virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0;
|
||||
virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A shape receiver that simply pushes into the target
|
||||
*/
|
||||
class DB_PUBLIC HierarchyBuilderShapeInserter
|
||||
: public HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
HierarchyBuilderShapeInserter () { }
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
tl::ident_map<db::Layout::properties_id_type> pm;
|
||||
target->insert (shape, trans, pm);
|
||||
}
|
||||
|
||||
virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
if (trans.is_ortho ()) {
|
||||
target->insert (shape.transformed (trans));
|
||||
} else {
|
||||
db::Polygon poly (shape);
|
||||
target->insert (poly.transformed (trans));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target)
|
||||
{
|
||||
if (trans.is_unity ()) {
|
||||
target->insert (shape);
|
||||
} else {
|
||||
target->insert (shape.transformed (trans));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A clipping shape receiver that forwards to another one
|
||||
*/
|
||||
class DB_PUBLIC ClippingHierarchyBuilderShapeReceiver
|
||||
: public HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
ClippingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0);
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
|
||||
private:
|
||||
void insert_clipped (const db::Box &box, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target);
|
||||
void insert_clipped (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target);
|
||||
static bool is_inside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region);
|
||||
static bool is_outside (const db::Box &box, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region);
|
||||
|
||||
HierarchyBuilderShapeReceiver *mp_pipe;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A polygon reducing shape receiver that forwards to another one
|
||||
*/
|
||||
class DB_PUBLIC ReducingHierarchyBuilderShapeReceiver
|
||||
: public HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
ReducingHierarchyBuilderShapeReceiver (HierarchyBuilderShapeReceiver *pipe = 0, double area_ratio = 3.0, size_t max_vertex_count = 16);
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
|
||||
private:
|
||||
void reduce (const db::Polygon &poly, const db::ICplxTrans &trans, const db::Box ®ion, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target);
|
||||
|
||||
HierarchyBuilderShapeReceiver *mp_pipe;
|
||||
double m_area_ratio;
|
||||
size_t m_max_vertex_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A polygon reference generating shape receiver that feeds a shapes array after turning the shapes into PolygonRefs
|
||||
*/
|
||||
class DB_PUBLIC PolygonReferenceHierarchyBuilderShapeReceiver
|
||||
: public HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
PolygonReferenceHierarchyBuilderShapeReceiver (db::Layout *layout, int text_enlargement = -1, const tl::Variant &text_prop_name = tl::Variant ());
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
|
||||
private:
|
||||
db::Layout *mp_layout;
|
||||
int m_text_enlargement;
|
||||
bool m_make_text_prop;
|
||||
db::property_names_id_type m_text_prop_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An edge-generating shape receiver that feeds a shapes array after turning the shapes into edges
|
||||
*/
|
||||
class DB_PUBLIC EdgeBuildingHierarchyBuilderShapeReceiver
|
||||
: public HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
EdgeBuildingHierarchyBuilderShapeReceiver (bool as_edges);
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Box &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Polygon &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
|
||||
private:
|
||||
bool m_as_edges;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An edge pair-generating shape receiver that feeds a shapes array after turning the shapes into edges
|
||||
*/
|
||||
class DB_PUBLIC EdgePairBuildingHierarchyBuilderShapeReceiver
|
||||
: public HierarchyBuilderShapeReceiver
|
||||
{
|
||||
public:
|
||||
EdgePairBuildingHierarchyBuilderShapeReceiver ();
|
||||
|
||||
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *target);
|
||||
virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
|
||||
virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class building a hierarchy from a recursive shape iterator in push mode
|
||||
*
|
||||
* This class is a RecursiveShapeReceiver which acts on the hierarchy events and
|
||||
* uses them to rebuild a hierarchy in the target layout. In can be used multiple
|
||||
* times and will reuse the hierarchy as far as possible.
|
||||
*
|
||||
* The hierarchy builder can form clip variants for cells and clip the shapes
|
||||
* according to the selected region.
|
||||
*
|
||||
* NOTE: the hierarchy build should not be used in multiple passes with regions
|
||||
* as the hierarchy is sampled in the first pass and the hierarchy builder will
|
||||
* rely on precisely the same hierarchy arrangement. This is not given with
|
||||
* region selections.
|
||||
*/
|
||||
class DB_PUBLIC HierarchyBuilder
|
||||
: public db::RecursiveShapeReceiver
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::map<std::pair<db::cell_index_type, std::set<db::Box> >, db::cell_index_type> cell_map_type;
|
||||
typedef std::map<db::cell_index_type, std::vector<db::cell_index_type> > original_target_to_variants_map_type;
|
||||
typedef std::map<db::cell_index_type, db::cell_index_type> variant_to_original_target_map_type;
|
||||
|
||||
HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans = db::ICplxTrans (), HierarchyBuilderShapeReceiver *pipe = 0);
|
||||
HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans = db::ICplxTrans (), HierarchyBuilderShapeReceiver *pipe = 0);
|
||||
virtual ~HierarchyBuilder ();
|
||||
|
||||
/**
|
||||
* @brief Installs a custom shape receiver
|
||||
* The hierarchy builder will *NOT* take ownership of this object.
|
||||
*/
|
||||
void set_shape_receiver (HierarchyBuilderShapeReceiver *pipe);
|
||||
|
||||
virtual void begin (const RecursiveShapeIterator *iter);
|
||||
virtual void end (const RecursiveShapeIterator *iter);
|
||||
virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region);
|
||||
virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell);
|
||||
virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all);
|
||||
virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all);
|
||||
virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region);
|
||||
|
||||
/**
|
||||
* @brief Sets the target layer - shapes will be put there
|
||||
*/
|
||||
void set_target_layer (unsigned int target_layer)
|
||||
{
|
||||
m_target_layer = target_layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the builder - performs a new initial pass
|
||||
*/
|
||||
void reset ();
|
||||
|
||||
/**
|
||||
* @brief Gets the initial cell the builder produced in the target layout
|
||||
*/
|
||||
db::Cell *initial_cell ()
|
||||
{
|
||||
return mp_initial_cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the target layout
|
||||
*/
|
||||
db::Layout *target ()
|
||||
{
|
||||
return mp_target.get ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the recursive shape iterator the data was taken from
|
||||
*/
|
||||
const db::RecursiveShapeIterator &source () const
|
||||
{
|
||||
return m_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the iterator for the cell map
|
||||
*/
|
||||
cell_map_type::const_iterator begin_cell_map () const
|
||||
{
|
||||
return m_cell_map.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the iterator for the cell map (end)
|
||||
*/
|
||||
cell_map_type::const_iterator end_cell_map () const
|
||||
{
|
||||
return m_cell_map.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Marks a cell as a variant of another
|
||||
*
|
||||
* The first cell is either the original, non-variant target cell or itself a variant.
|
||||
* The second cell will be registered as a variant of the first one.
|
||||
*/
|
||||
void register_variant (db::cell_index_type non_var, db::cell_index_type var);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the given cell is a variant cell
|
||||
*/
|
||||
bool is_variant (db::cell_index_type ci) const
|
||||
{
|
||||
return m_variants_to_original_target_map.find (ci) != m_variants_to_original_target_map.end ();
|
||||
}
|
||||
|
||||
private:
|
||||
tl::weak_ptr<db::Layout> mp_target;
|
||||
HierarchyBuilderShapeReceiver *mp_pipe;
|
||||
bool m_initial_pass;
|
||||
db::RecursiveShapeIterator m_source;
|
||||
cell_map_type m_cell_map;
|
||||
original_target_to_variants_map_type m_original_targets_to_variants_map;
|
||||
variant_to_original_target_map_type m_variants_to_original_target_map;
|
||||
|
||||
std::set<cell_map_type::key_type> m_cells_seen;
|
||||
std::set<db::cell_index_type> m_cells_to_be_filled;
|
||||
cell_map_type::const_iterator m_cm_entry;
|
||||
bool m_cm_new_entry;
|
||||
unsigned int m_target_layer;
|
||||
std::vector<std::pair<bool, std::vector<db::Cell *> > > m_cell_stack;
|
||||
db::Cell *mp_initial_cell;
|
||||
|
||||
db::ICplxTrans m_trans;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
#include "dbLibraryProxy.h"
|
||||
#include "dbLibraryManager.h"
|
||||
#include "dbLibrary.h"
|
||||
#include "dbRegion.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlInternational.h"
|
||||
|
|
@ -41,6 +42,8 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
static const int layout_base_verbosity = 30;
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// The undo/redo operations
|
||||
|
||||
|
|
@ -638,7 +641,25 @@ Layout::delete_cell (cell_index_type id)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
Layout::insert (db::cell_index_type cell, int layer, const db::Region ®ion)
|
||||
{
|
||||
region.insert_into (this, cell, layer);
|
||||
}
|
||||
|
||||
void
|
||||
Layout::insert (db::cell_index_type cell, int layer, const db::Edges &edges)
|
||||
{
|
||||
edges.insert_into (this, cell, layer);
|
||||
}
|
||||
|
||||
void
|
||||
Layout::insert (db::cell_index_type cell, int layer, const db::EdgePairs &edge_pairs)
|
||||
{
|
||||
edge_pairs.insert_into (this, cell, layer);
|
||||
}
|
||||
|
||||
void
|
||||
Layout::flatten (const db::Cell &source_cell, db::Cell &target_cell, const db::ICplxTrans &t, int levels)
|
||||
{
|
||||
db::ICplxTrans tt = t;
|
||||
|
|
@ -1271,7 +1292,7 @@ Layout::update () const
|
|||
void
|
||||
Layout::do_update ()
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Sorting")));
|
||||
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity, tl::to_string (tr ("Sorting")));
|
||||
|
||||
// establish a progress report since this operation can take some time.
|
||||
// HINT: because of some gcc bug, automatic destruction of the tl::Progress
|
||||
|
|
@ -1286,12 +1307,12 @@ Layout::do_update ()
|
|||
// the hierarchy management informations
|
||||
if (hier_dirty ()) {
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, "Updating relations");
|
||||
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating relations");
|
||||
pr->set_desc (tl::to_string (tr ("Updating relations")));
|
||||
update_relations ();
|
||||
}
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, "Topological sort");
|
||||
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Topological sort");
|
||||
pr->set_desc (tl::to_string (tr ("Topological sorting")));
|
||||
tl_assert (topological_sort ());
|
||||
}
|
||||
|
|
@ -1309,7 +1330,7 @@ Layout::do_update ()
|
|||
if (bboxes_dirty ()) {
|
||||
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, "Updating bounding boxes");
|
||||
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating bounding boxes");
|
||||
unsigned int layers = 0;
|
||||
pr->set (0);
|
||||
pr->set_desc (tl::to_string (tr ("Updating bounding boxes")));
|
||||
|
|
@ -1331,7 +1352,7 @@ Layout::do_update ()
|
|||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, "Sorting shapes");
|
||||
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting shapes");
|
||||
pr->set (0);
|
||||
pr->set_desc (tl::to_string (tr ("Sorting shapes")));
|
||||
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
||||
|
|
@ -1344,7 +1365,7 @@ Layout::do_update ()
|
|||
|
||||
// sort the instance trees now, since we have computed the bboxes
|
||||
if (hier_dirty () || ! dirty_parents.empty ()) {
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, "Sorting instances");
|
||||
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting instances");
|
||||
size_t layers = 0;
|
||||
pr->set (0);
|
||||
pr->set_desc (tl::to_string (tr ("Sorting instances")));
|
||||
|
|
@ -1511,6 +1532,24 @@ Layout::insert_layer (unsigned int index, const LayerProperties &props)
|
|||
layer_properties_changed ();
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Layout::get_layer (const db::LayerProperties &lp)
|
||||
{
|
||||
if (lp.is_null ()) {
|
||||
// for a null layer info always create a layer
|
||||
return insert_layer ();
|
||||
} else {
|
||||
// if we have a layer with the requested properties already, return this.
|
||||
for (db::Layout::layer_iterator li = begin_layers (); li != end_layers (); ++li) {
|
||||
if ((*li).second->log_equal (lp)) {
|
||||
return (*li).first;
|
||||
}
|
||||
}
|
||||
// otherwise create a new layer
|
||||
return insert_layer (lp);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Layout::waste_layer () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@
|
|||
#include "tlException.h"
|
||||
#include "tlVector.h"
|
||||
#include "tlString.h"
|
||||
#include "tlThreads.h"
|
||||
#include "tlObject.h"
|
||||
#include "tlUniqueId.h"
|
||||
#include "gsi.h"
|
||||
|
||||
#include <cstring>
|
||||
|
|
@ -59,6 +62,9 @@ class Library;
|
|||
class LibraryProxy;
|
||||
class CellMapping;
|
||||
class LayerMapping;
|
||||
class Region;
|
||||
class Edges;
|
||||
class EdgePairs;
|
||||
|
||||
template <class Coord> class generic_repository;
|
||||
typedef generic_repository<db::Coord> GenericRepository;
|
||||
|
|
@ -456,7 +462,9 @@ public:
|
|||
class DB_PUBLIC Layout
|
||||
: public db::Object,
|
||||
public db::LayoutStateModel,
|
||||
public gsi::ObjectBase
|
||||
public gsi::ObjectBase,
|
||||
public tl::Object,
|
||||
public tl::UniqueId
|
||||
{
|
||||
public:
|
||||
typedef db::Box box_type;
|
||||
|
|
@ -572,6 +580,15 @@ public:
|
|||
return m_properties_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the lock for the layout object
|
||||
* This is a generic lock that can be used to lock modifications against multiple threads.
|
||||
*/
|
||||
tl::Mutex &lock ()
|
||||
{
|
||||
return m_lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Collect memory statistics
|
||||
*/
|
||||
|
|
@ -1078,6 +1095,33 @@ public:
|
|||
*/
|
||||
void flatten (db::Cell &cell, int levels, bool prune = false);
|
||||
|
||||
/**
|
||||
* @brief Inserts a region (potentially hierarchical) into the given cell and layer
|
||||
*
|
||||
* If the region is flat (conceptionally), it will be put into the cell.
|
||||
* If the region is hierarchical, a cell hierarchy will be built below the
|
||||
* given cell.
|
||||
*/
|
||||
void insert (db::cell_index_type cell, int layer, const db::Region ®ion);
|
||||
|
||||
/**
|
||||
* @brief Inserts a edge collection (potentially hierarchical) into the given cell and layer
|
||||
*
|
||||
* If the edge collection is flat (conceptionally), it will be put into the cell.
|
||||
* If the edge collection is hierarchical, a cell hierarchy will be built below the
|
||||
* given cell.
|
||||
*/
|
||||
void insert (db::cell_index_type cell, int layer, const db::Edges &edges);
|
||||
|
||||
/**
|
||||
* @brief Inserts a edge pair collection (potentially hierarchical) into the given cell and layer
|
||||
*
|
||||
* If the edge pair collection is flat (conceptionally), it will be put into the cell.
|
||||
* If the edge pair collection is hierarchical, a cell hierarchy will be built below the
|
||||
* given cell.
|
||||
*/
|
||||
void insert (db::cell_index_type cell, int layer, const db::EdgePairs &edge_pairs);
|
||||
|
||||
/**
|
||||
* @brief Delete a cell plus all subcells
|
||||
*
|
||||
|
|
@ -1435,6 +1479,14 @@ public:
|
|||
*/
|
||||
void insert_layer (unsigned int index, const LayerProperties &props = LayerProperties ());
|
||||
|
||||
/**
|
||||
* @brief Gets or creates a layer with the given properties
|
||||
*
|
||||
* If there already is a layer matching the given properties, it's index will be
|
||||
* returned. Otherwise a new layer with these properties is created.
|
||||
*/
|
||||
unsigned int get_layer (const db::LayerProperties &props);
|
||||
|
||||
/**
|
||||
* @brief Insert a new special layer with the given properties
|
||||
*
|
||||
|
|
@ -1630,6 +1682,7 @@ private:
|
|||
int m_waste_layer;
|
||||
bool m_editable;
|
||||
meta_info m_meta_info;
|
||||
tl::Mutex m_lock;
|
||||
|
||||
/**
|
||||
* @brief Sort the cells topologically
|
||||
|
|
|
|||
|
|
@ -0,0 +1,942 @@
|
|||
|
||||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbLayoutToNetlist.h"
|
||||
#include "dbDeepRegion.h"
|
||||
#include "dbShapeRepository.h"
|
||||
#include "dbCellMapping.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// 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)
|
||||
{
|
||||
// check the iterator
|
||||
if (iter.has_complex_region () || iter.region () != db::Box::world ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist extractor cannot work on clipped layouts")));
|
||||
}
|
||||
|
||||
mp_internal_dss.reset (new db::DeepShapeStore ());
|
||||
mp_dss.reset (mp_internal_dss.get ());
|
||||
|
||||
// the dummy layer acts as a reference holder for the layout
|
||||
// NOTE: this probably can be done better
|
||||
db::RecursiveShapeIterator empty_iter = iter;
|
||||
empty_iter.set_layers (std::vector<unsigned int> ());
|
||||
m_dummy_layer = dss ().create_polygon_layer (empty_iter);
|
||||
|
||||
init ();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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> ());
|
||||
}
|
||||
}
|
||||
|
||||
LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu)
|
||||
: m_iter (), m_netlist_extracted (false), m_is_flat (true)
|
||||
{
|
||||
mp_internal_dss.reset (new db::DeepShapeStore (topcell_name, dbu));
|
||||
mp_dss.reset (mp_internal_dss.get ());
|
||||
m_layout_index = 0 ;
|
||||
|
||||
init ();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
LayoutToNetlist::~LayoutToNetlist ()
|
||||
{
|
||||
// NOTE: do this in this order because of unregistration of the layers
|
||||
m_named_regions.clear ();
|
||||
m_dlrefs.clear ();
|
||||
mp_internal_dss.reset (0);
|
||||
mp_netlist.reset (0);
|
||||
m_net_clusters.clear ();
|
||||
}
|
||||
|
||||
void LayoutToNetlist::init ()
|
||||
{
|
||||
dss ().set_text_enlargement (1);
|
||||
dss ().set_text_property_name (tl::Variant ("LABEL"));
|
||||
}
|
||||
|
||||
void LayoutToNetlist::set_threads (int n)
|
||||
{
|
||||
dss ().set_threads (n);
|
||||
}
|
||||
|
||||
int LayoutToNetlist::threads () const
|
||||
{
|
||||
return dss ().threads ();
|
||||
}
|
||||
|
||||
void LayoutToNetlist::set_area_ratio (double ar)
|
||||
{
|
||||
dss ().set_max_area_ratio (ar);
|
||||
}
|
||||
|
||||
double LayoutToNetlist::area_ratio () const
|
||||
{
|
||||
return dss ().max_area_ratio ();
|
||||
}
|
||||
|
||||
void LayoutToNetlist::set_max_vertex_count (size_t n)
|
||||
{
|
||||
dss ().set_max_vertex_count (n);
|
||||
}
|
||||
|
||||
size_t LayoutToNetlist::max_vertex_count () const
|
||||
{
|
||||
return dss ().max_vertex_count ();
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::make_layer (const std::string &n)
|
||||
{
|
||||
db::RecursiveShapeIterator si (m_iter);
|
||||
si.shape_flags (db::ShapeIterator::Nothing);
|
||||
|
||||
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
||||
if (! n.empty ()) {
|
||||
register_layer (*region, n);
|
||||
}
|
||||
return region.release ();
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::string &n)
|
||||
{
|
||||
db::RecursiveShapeIterator si (m_iter);
|
||||
si.set_layer (layer_index);
|
||||
si.shape_flags (db::ShapeIterator::All);
|
||||
|
||||
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
||||
if (! n.empty ()) {
|
||||
register_layer (*region, n);
|
||||
}
|
||||
return region.release ();
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const std::string &n)
|
||||
{
|
||||
db::RecursiveShapeIterator si (m_iter);
|
||||
si.set_layer (layer_index);
|
||||
si.shape_flags (db::ShapeIterator::Texts);
|
||||
|
||||
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
||||
register_layer (*region, n);
|
||||
return region.release ();
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const std::string &n)
|
||||
{
|
||||
db::RecursiveShapeIterator si (m_iter);
|
||||
si.set_layer (layer_index);
|
||||
si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes);
|
||||
|
||||
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
||||
register_layer (*region, n);
|
||||
return region.release ();
|
||||
}
|
||||
|
||||
void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::Region *> &layers)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
}
|
||||
if (! mp_netlist.get ()) {
|
||||
mp_netlist.reset (new db::Netlist ());
|
||||
}
|
||||
extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters);
|
||||
}
|
||||
|
||||
void LayoutToNetlist::connect (const db::Region &l)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
}
|
||||
|
||||
if (! is_persisted (l)) {
|
||||
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in intra-layer connectivity for netlist extraction"))));
|
||||
}
|
||||
|
||||
// we need to keep a reference, so we can safely delete the region
|
||||
db::DeepLayer dl = deep_layer_of (l);
|
||||
m_dlrefs.insert (dl);
|
||||
|
||||
m_conn.connect (dl.layer ());
|
||||
}
|
||||
|
||||
void LayoutToNetlist::connect (const db::Region &a, const db::Region &b)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
}
|
||||
if (! is_persisted (a)) {
|
||||
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (first layer) for netlist extraction"))));
|
||||
}
|
||||
if (! is_persisted (b)) {
|
||||
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (second layer) for netlist extraction"))));
|
||||
}
|
||||
|
||||
// we need to keep a reference, so we can safely delete the region
|
||||
db::DeepLayer dla = deep_layer_of (a);
|
||||
db::DeepLayer dlb = deep_layer_of (b);
|
||||
m_dlrefs.insert (dla);
|
||||
m_dlrefs.insert (dlb);
|
||||
|
||||
m_conn.connect (dla.layer (), dlb.layer ());
|
||||
}
|
||||
|
||||
size_t LayoutToNetlist::connect_global (const db::Region &l, const std::string &gn)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
}
|
||||
if (! is_persisted (l)) {
|
||||
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in global connectivity for netlist extraction"))));
|
||||
}
|
||||
|
||||
// we need to keep a reference, so we can safely delete the region
|
||||
db::DeepLayer dl = deep_layer_of (l);
|
||||
m_dlrefs.insert (dl);
|
||||
|
||||
return m_conn.connect_global (dl.layer (), gn);
|
||||
}
|
||||
|
||||
const std::string &LayoutToNetlist::global_net_name (size_t id) const
|
||||
{
|
||||
return m_conn.global_net_name (id);
|
||||
}
|
||||
|
||||
size_t LayoutToNetlist::global_net_id (const std::string &name)
|
||||
{
|
||||
return m_conn.global_net_id (name);
|
||||
}
|
||||
|
||||
void LayoutToNetlist::extract_netlist (bool join_nets_by_label)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
}
|
||||
if (! mp_netlist.get ()) {
|
||||
mp_netlist.reset (new db::Netlist ());
|
||||
}
|
||||
|
||||
db::NetlistExtractor netex;
|
||||
netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label);
|
||||
|
||||
m_netlist_extracted = true;
|
||||
}
|
||||
|
||||
void LayoutToNetlist::set_netlist_extracted ()
|
||||
{
|
||||
m_netlist_extracted = true;
|
||||
}
|
||||
|
||||
const db::Layout *LayoutToNetlist::internal_layout () const
|
||||
{
|
||||
ensure_layout ();
|
||||
return &dss ().const_layout (m_layout_index);
|
||||
}
|
||||
|
||||
const db::Cell *LayoutToNetlist::internal_top_cell () const
|
||||
{
|
||||
ensure_layout ();
|
||||
return &dss ().const_initial_cell (m_layout_index);
|
||||
}
|
||||
|
||||
db::Layout *LayoutToNetlist::internal_layout ()
|
||||
{
|
||||
ensure_layout ();
|
||||
return &dss ().layout (m_layout_index);
|
||||
}
|
||||
|
||||
db::Cell *LayoutToNetlist::internal_top_cell ()
|
||||
{
|
||||
ensure_layout ();
|
||||
return &dss ().initial_cell (m_layout_index);
|
||||
}
|
||||
|
||||
void LayoutToNetlist::ensure_layout () const
|
||||
{
|
||||
if (! dss ().is_valid_layout_index (m_layout_index)) {
|
||||
|
||||
LayoutToNetlist *non_const_this = const_cast<LayoutToNetlist *> (this);
|
||||
non_const_this->dss ().make_layout (m_layout_index, db::RecursiveShapeIterator ());
|
||||
|
||||
// the dummy layer acts as a reference holder for the layout
|
||||
unsigned int dummy_layer_index = non_const_this->dss ().layout (m_layout_index).insert_layer ();
|
||||
non_const_this->m_dummy_layer = db::DeepLayer (& non_const_this->dss (), m_layout_index, dummy_layer_index);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutToNetlist::register_layer (const db::Region ®ion, const std::string &n)
|
||||
{
|
||||
if (m_named_regions.find (n) != m_named_regions.end ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Layer name is already used: ")) + n);
|
||||
}
|
||||
|
||||
db::DeepLayer dl;
|
||||
|
||||
if (m_is_flat) {
|
||||
|
||||
dl = dss ().create_from_flat (region, true);
|
||||
|
||||
} else {
|
||||
|
||||
db::DeepRegion *delegate = dynamic_cast<db::DeepRegion *> (region.delegate());
|
||||
if (! delegate) {
|
||||
|
||||
if (region.empty ()) {
|
||||
dl = dss ().empty_layer (m_layout_index);
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Layer is not a deep region and cannot be registered with name: ")) + n);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (is_persisted (region)) {
|
||||
std::string prev_name = name (region);
|
||||
m_named_regions.erase (prev_name);
|
||||
}
|
||||
|
||||
dl = delegate->deep_layer ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_named_regions [n] = dl;
|
||||
m_name_of_layer [dl.layer ()] = n;
|
||||
}
|
||||
|
||||
std::string LayoutToNetlist::name (const db::Region ®ion) const
|
||||
{
|
||||
std::map<unsigned int, std::string>::const_iterator n = m_name_of_layer.find (layer_of (region));
|
||||
if (n != m_name_of_layer.end ()) {
|
||||
return n->second;
|
||||
} else {
|
||||
return std::string ();
|
||||
}
|
||||
}
|
||||
|
||||
std::string LayoutToNetlist::name (unsigned int l) const
|
||||
{
|
||||
std::map<unsigned int, std::string>::const_iterator n = m_name_of_layer.find (l);
|
||||
if (n != m_name_of_layer.end ()) {
|
||||
return n->second;
|
||||
} else {
|
||||
return std::string ();
|
||||
}
|
||||
}
|
||||
|
||||
bool LayoutToNetlist::is_persisted (const db::Region ®ion) const
|
||||
{
|
||||
return m_name_of_layer.find (layer_of (region)) != m_name_of_layer.end ();
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::layer_by_name (const std::string &name)
|
||||
{
|
||||
std::map<std::string, db::DeepLayer>::const_iterator l = m_named_regions.find (name);
|
||||
if (l == m_named_regions.end ()) {
|
||||
return 0;
|
||||
} else {
|
||||
return new db::Region (new db::DeepRegion (l->second));
|
||||
}
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::layer_by_index (unsigned int index)
|
||||
{
|
||||
std::map<unsigned int, std::string>::const_iterator n = m_name_of_layer.find (index);
|
||||
if (n == m_name_of_layer.end ()) {
|
||||
return 0;
|
||||
} else {
|
||||
return layer_by_name (n->second);
|
||||
}
|
||||
}
|
||||
|
||||
db::DeepLayer LayoutToNetlist::deep_layer_of (const db::Region ®ion) const
|
||||
{
|
||||
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (region.delegate ());
|
||||
if (! dr) {
|
||||
|
||||
std::pair<bool, db::DeepLayer> lff = dss ().layer_for_flat (region);
|
||||
if (lff.first) {
|
||||
return lff.second;
|
||||
} else if (region.empty ()) {
|
||||
// provide a substitute empty layer for empty
|
||||
return dss ().empty_layer (m_layout_index);
|
||||
} else {
|
||||
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in netlist extraction"))));
|
||||
}
|
||||
|
||||
} else {
|
||||
return dr->deep_layer ();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const
|
||||
{
|
||||
return deep_layer_of (region).layer ();
|
||||
}
|
||||
|
||||
db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells)
|
||||
{
|
||||
std::set<db::cell_index_type> device_cells;
|
||||
if (! with_device_cells && mp_netlist.get ()) {
|
||||
for (db::Netlist::device_abstract_iterator i = mp_netlist->begin_device_abstracts (); i != mp_netlist->end_device_abstracts (); ++i) {
|
||||
device_cells.insert (i->cell_index ());
|
||||
}
|
||||
}
|
||||
|
||||
return dss ().cell_mapping_to_original (m_layout_index, &layout, cell.cell_index (), &device_cells);
|
||||
}
|
||||
|
||||
db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell)
|
||||
{
|
||||
db::CellMapping cm;
|
||||
if (layout.cells () == 1) {
|
||||
cm.create_single_mapping (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ());
|
||||
} else {
|
||||
cm.create_from_geometry (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ());
|
||||
}
|
||||
return cm;
|
||||
}
|
||||
|
||||
db::Netlist *LayoutToNetlist::netlist () const
|
||||
{
|
||||
return mp_netlist.get ();
|
||||
}
|
||||
|
||||
db::Netlist *LayoutToNetlist::make_netlist ()
|
||||
{
|
||||
if (! mp_netlist.get ()) {
|
||||
mp_netlist.reset (new db::Netlist ());
|
||||
}
|
||||
return mp_netlist.get ();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct StopOnFirst { };
|
||||
}
|
||||
|
||||
template <class Tr>
|
||||
static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Tr>
|
||||
static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr)
|
||||
{
|
||||
if (pr.obj ().is_box ()) {
|
||||
region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr));
|
||||
} else {
|
||||
region.insert (pr.obj ().transformed (pr.trans ()).transformed (tr));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Tr>
|
||||
static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr)
|
||||
{
|
||||
if (pr.obj ().is_box ()) {
|
||||
shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr));
|
||||
} else {
|
||||
db::Layout *layout = shapes.layout ();
|
||||
if (layout) {
|
||||
shapes.insert (db::PolygonRef (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ()));
|
||||
} else {
|
||||
shapes.insert (pr.obj ().transformed (pr.trans ()).transformed (tr));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class To>
|
||||
static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters<db::PolygonRef> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to)
|
||||
{
|
||||
// deliver the net shapes
|
||||
for (db::recursive_cluster_shape_iterator<db::PolygonRef> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
|
||||
if (! deliver_shape (*rci, to, tr * rci.trans ())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class To>
|
||||
static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db::hier_clusters<db::PolygonRef> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to)
|
||||
{
|
||||
// NOTE: this scheme will deliver the shapes from the cell, including (!)
|
||||
// subcells that are purged
|
||||
|
||||
db::cell_index_type prev_ci = ci;
|
||||
|
||||
// deliver the net shapes
|
||||
for (db::recursive_cluster_shape_iterator<db::PolygonRef> rci (clusters, layer_id, ci, cid); !rci.at_end (); ) {
|
||||
|
||||
db::cell_index_type cci = rci.cell_index ();
|
||||
if (cci != prev_ci && cci != ci && (! nl || nl->circuit_by_cell_index (cci) || nl->device_abstract_by_cell_index (cci))) {
|
||||
|
||||
rci.skip_cell ();
|
||||
|
||||
} else {
|
||||
|
||||
if (! deliver_shape (*rci, to, tr * rci.trans ())) {
|
||||
return false;
|
||||
}
|
||||
prev_ci = cci;
|
||||
|
||||
++rci;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const
|
||||
{
|
||||
unsigned int lid = layer_of (of_layer);
|
||||
const db::Circuit *circuit = net.circuit ();
|
||||
tl_assert (circuit != 0);
|
||||
|
||||
if (! recursive) {
|
||||
deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to);
|
||||
} else {
|
||||
deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to);
|
||||
}
|
||||
}
|
||||
|
||||
db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const
|
||||
{
|
||||
unsigned int lid = layer_of (of_layer);
|
||||
const db::Circuit *circuit = net.circuit ();
|
||||
tl_assert (circuit != 0);
|
||||
|
||||
std::auto_ptr<db::Region> res (new db::Region ());
|
||||
|
||||
if (! recursive) {
|
||||
deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res);
|
||||
} else {
|
||||
deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res);
|
||||
}
|
||||
|
||||
return res.release ();
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const
|
||||
{
|
||||
const db::Circuit *circuit = net.circuit ();
|
||||
tl_assert (circuit != 0);
|
||||
|
||||
build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, cell_name_prefix, device_cell_name_prefix, cmap, tr);
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map<unsigned int, const db::Region *> &lmap, const db::Net *net, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const
|
||||
{
|
||||
db::Cell *target_cell = &tc;
|
||||
|
||||
if (net_cell_name_prefix) {
|
||||
|
||||
const db::connected_clusters<db::PolygonRef> &ccl = m_net_clusters.clusters_per_cell (ci);
|
||||
|
||||
bool any_connections = circuit_cell_name_prefix && ! ccl.connections_for_cluster (cid).empty ();
|
||||
if (! any_connections) {
|
||||
|
||||
bool consider_cell = any_connections;
|
||||
for (std::map<unsigned int, const db::Region *>::const_iterator l = lmap.begin (); l != lmap.end () && !consider_cell; ++l) {
|
||||
if (l->second) {
|
||||
StopOnFirst sof;
|
||||
consider_cell = !deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof);
|
||||
}
|
||||
}
|
||||
|
||||
if (! consider_cell) {
|
||||
// shortcut if cell is empty -> no net cell will be produced
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// make a specific cell for the net if requested
|
||||
|
||||
target_cell = &target.cell (target.add_cell ((std::string (net_cell_name_prefix) + net->expanded_name ()).c_str ()));
|
||||
tc.insert (db::CellInstArray (db::CellInst (target_cell->cell_index ()), db::Trans ()));
|
||||
|
||||
}
|
||||
|
||||
for (std::map<unsigned int, const db::Region *>::const_iterator l = lmap.begin (); l != lmap.end (); ++l) {
|
||||
if (l->second) {
|
||||
deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first));
|
||||
}
|
||||
}
|
||||
|
||||
if (! circuit_cell_name_prefix && ! device_cell_name_prefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: we propagate the magnification part of tr down, but keep the rotation/translation part in the instance
|
||||
// (we want to avoid magnified instances)
|
||||
db::ICplxTrans tr_wo_mag = tr * db::ICplxTrans (1.0 / tr.mag ());
|
||||
db::ICplxTrans tr_mag (tr.mag ());
|
||||
|
||||
const db::connected_clusters<db::PolygonRef> &clusters = m_net_clusters.clusters_per_cell (ci);
|
||||
typedef db::connected_clusters<db::PolygonRef>::connections_type connections_type;
|
||||
const connections_type &connections = clusters.connections_for_cluster (cid);
|
||||
for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) {
|
||||
|
||||
db::cell_index_type subci = c->inst_cell_index ();
|
||||
size_t subcid = c->id ();
|
||||
|
||||
std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type>::const_iterator cm = cmap.find (std::make_pair (subci, subcid));
|
||||
if (cm == cmap.end ()) {
|
||||
|
||||
const char *name_prefix = 0;
|
||||
if (mp_netlist->device_abstract_by_cell_index (subci)) {
|
||||
name_prefix = device_cell_name_prefix;
|
||||
} else {
|
||||
name_prefix = circuit_cell_name_prefix;
|
||||
}
|
||||
|
||||
if (name_prefix) {
|
||||
|
||||
std::string cell_name = internal_layout ()->cell_name (subci);
|
||||
|
||||
db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ());
|
||||
cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), target_ci)).first;
|
||||
|
||||
build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, circuit_cell_name_prefix, device_cell_name_prefix, cmap, tr_mag);
|
||||
|
||||
} else {
|
||||
cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), std::numeric_limits<db::cell_index_type>::max ())).first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (cm->second != std::numeric_limits<db::cell_index_type>::max ()) {
|
||||
db::CellInstArray ci (db::CellInst (cm->second), tr_wo_mag * c->inst_trans ());
|
||||
ci.transform_into (tr_mag);
|
||||
target_cell->insert (ci);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const
|
||||
{
|
||||
if (! m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
||||
}
|
||||
|
||||
std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> cell_map;
|
||||
|
||||
double mag = internal_layout ()->dbu () / target.dbu ();
|
||||
build_net_rec (net, target, target_cell, lmap, 0, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag));
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const
|
||||
{
|
||||
if (! m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
||||
}
|
||||
|
||||
std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> cell_map;
|
||||
double mag = internal_layout ()->dbu () / target.dbu ();
|
||||
|
||||
const db::Netlist *netlist = mp_netlist.get ();
|
||||
for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) {
|
||||
|
||||
if (! cmap.has_mapping (c->cell_index ())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool is_top_circuit = c->begin_parents () == c->end_parents ();
|
||||
|
||||
db::cell_index_type target_ci = cmap.cell_mapping (c->cell_index ());
|
||||
|
||||
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
|
||||
|
||||
// exlude local nets in recursive mode
|
||||
if (circuit_cell_name_prefix && ! is_top_circuit && n->pin_count () > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag));
|
||||
|
||||
}
|
||||
|
||||
if (circuit_cell_name_prefix) {
|
||||
|
||||
// with recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will
|
||||
// get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from
|
||||
// subcircuits as part of the circuit which calls the subcircuit - but NOT in a subcircuit cell, because
|
||||
// this will just apply to nets from certain instances. But the net cell name will be formed as "subcircuit:net"
|
||||
|
||||
const db::Circuit &circuit = *c;
|
||||
for (db::Circuit::const_subcircuit_iterator sc = circuit.begin_subcircuits (); sc != circuit.end_subcircuits (); ++sc) {
|
||||
|
||||
const db::SubCircuit &subcircuit = *sc;
|
||||
for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) {
|
||||
|
||||
if (! subcircuit.net_for_pin (p->id ())) {
|
||||
|
||||
const db::Net *n = subcircuit.circuit_ref ()->net_for_pin (p->id ());
|
||||
if (n) {
|
||||
|
||||
double dbu = target.dbu ();
|
||||
db::ICplxTrans tr = db::ICplxTrans (mag) * (db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu));
|
||||
|
||||
if (net_cell_name_prefix) {
|
||||
std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":";
|
||||
build_net_rec (*n, target, target.cell (target_ci), lmap, ncn.c_str (), circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr);
|
||||
} else {
|
||||
build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point)
|
||||
{
|
||||
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point);
|
||||
}
|
||||
|
||||
size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, std::vector<db::InstElement> &rev_inst_path)
|
||||
{
|
||||
db::Box local_box = trans * test_cluster.bbox ();
|
||||
|
||||
const db::local_clusters<db::PolygonRef> &lcc = net_clusters ().clusters_per_cell (cell->cell_index ());
|
||||
for (db::local_clusters<db::PolygonRef>::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) {
|
||||
const db::local_cluster<db::PolygonRef> &lc = *i;
|
||||
if (lc.interacts (test_cluster, trans, m_conn)) {
|
||||
return lc.id ();
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Cell::touching_iterator i = cell->begin_touching (local_box); ! i.at_end (); ++i) {
|
||||
|
||||
for (db::CellInstArray::iterator ia = i->begin_touching (local_box, internal_layout ()); ! ia.at_end (); ++ia) {
|
||||
|
||||
db::ICplxTrans trans_inst = i->complex_trans (*ia);
|
||||
db::ICplxTrans t = trans_inst.inverted () * trans;
|
||||
size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, rev_inst_path);
|
||||
if (cluster_id > 0) {
|
||||
rev_inst_path.push_back (db::InstElement (*i, ia));
|
||||
return cluster_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point)
|
||||
{
|
||||
if (! m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
||||
}
|
||||
tl_assert (mp_netlist.get ());
|
||||
|
||||
db::CplxTrans dbu_trans (internal_layout ()->dbu ());
|
||||
db::VCplxTrans dbu_trans_inv = dbu_trans.inverted ();
|
||||
|
||||
unsigned int layer = layer_of (of_region);
|
||||
|
||||
// Prepare a test cluster
|
||||
db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1));
|
||||
db::GenericRepository sr;
|
||||
db::local_cluster<db::PolygonRef> test_cluster;
|
||||
test_cluster.add (db::PolygonRef (db::Polygon (box), sr), layer);
|
||||
|
||||
std::vector<db::InstElement> inst_path;
|
||||
|
||||
size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, inst_path);
|
||||
if (cluster_id > 0) {
|
||||
|
||||
// search_net delivers the path in reverse order
|
||||
std::reverse (inst_path.begin (), inst_path.end ());
|
||||
|
||||
std::vector<db::cell_index_type> cell_indexes;
|
||||
cell_indexes.reserve (inst_path.size () + 1);
|
||||
cell_indexes.push_back (internal_top_cell ()->cell_index ());
|
||||
for (std::vector<db::InstElement>::const_iterator i = inst_path.begin (); i != inst_path.end (); ++i) {
|
||||
cell_indexes.push_back (i->inst_ptr.cell_index ());
|
||||
}
|
||||
|
||||
db::Circuit *circuit = mp_netlist->circuit_by_cell_index (cell_indexes.back ());
|
||||
if (! circuit) {
|
||||
// the circuit has probably been optimized away
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::Net *net = circuit->net_by_cluster_id (cluster_id);
|
||||
if (! net) {
|
||||
// the net has probably been optimized away
|
||||
return 0;
|
||||
}
|
||||
|
||||
// follow the path up in the net hierarchy using the transformation and the upper cell index as the
|
||||
// guide line
|
||||
while (! inst_path.empty () && net->pin_count () > 0) {
|
||||
|
||||
cell_indexes.pop_back ();
|
||||
|
||||
const db::Pin *pin = circuit->pin_by_id (net->begin_pins ()->pin_id ());
|
||||
tl_assert (pin != 0);
|
||||
|
||||
db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv;
|
||||
|
||||
// try to find a parent circuit which connects to this net
|
||||
db::Circuit *upper_circuit = 0;
|
||||
db::Net *upper_net = 0;
|
||||
for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++r) {
|
||||
if (r->trans ().equal (dtrans) && r->circuit () && r->circuit ()->cell_index () == cell_indexes.back ()) {
|
||||
upper_net = r->net_for_pin (pin->id ());
|
||||
upper_circuit = r->circuit ();
|
||||
}
|
||||
}
|
||||
|
||||
if (upper_net) {
|
||||
circuit = upper_circuit;
|
||||
net = upper_net;
|
||||
inst_path.pop_back ();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return net;
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes)
|
||||
{
|
||||
// TODO: that's basically too much .. we only need the clusters
|
||||
if (! m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
||||
}
|
||||
|
||||
db::Layout &ly = dss ().layout (m_layout_index);
|
||||
double dbu = ly.dbu ();
|
||||
|
||||
db::DeepLayer dl (&dss (), m_layout_index, ly.insert_layer ());
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator cid = ly.begin_bottom_up (); cid != ly.end_bottom_up (); ++cid) {
|
||||
|
||||
const connected_clusters<db::PolygonRef> &clusters = m_net_clusters.clusters_per_cell (*cid);
|
||||
if (clusters.empty ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (connected_clusters<db::PolygonRef>::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) {
|
||||
|
||||
if (! clusters.is_root (*c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
db::Region rgate, rmetal;
|
||||
|
||||
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate);
|
||||
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal);
|
||||
|
||||
double agate = rgate.area () * dbu * dbu;
|
||||
double ametal = rmetal.area () * dbu * dbu;
|
||||
|
||||
double r = ratio;
|
||||
bool skip = false;
|
||||
|
||||
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
|
||||
|
||||
db::Region rdiode;
|
||||
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode);
|
||||
|
||||
if (fabs (d->second) < db::epsilon) {
|
||||
if (rdiode.area () > 0) {
|
||||
skip = true;
|
||||
}
|
||||
} else {
|
||||
r += rdiode.area () * dbu * dbu * d->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! skip) {
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r);
|
||||
}
|
||||
|
||||
if (agate > dbu * dbu && ametal / agate > r + db::epsilon) {
|
||||
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
|
||||
for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) {
|
||||
shapes.insert (*r);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return db::Region (new db::DeepRegion (dl));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,575 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HDR_dbLayout2Netlist
|
||||
#define _HDR_dbLayout2Netlist
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbCellMapping.h"
|
||||
#include "dbNetlistExtractor.h"
|
||||
#include "dbNetlistDeviceExtractor.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A generic framework for extracting netlists from layouts
|
||||
*
|
||||
* This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor
|
||||
* and more. It is supposed to provide a framework for extracting a netlist from a layout.
|
||||
*
|
||||
* The use model of this class consists of five steps which need to be executed in this order.
|
||||
*
|
||||
* @ul
|
||||
* @li Configuration: in this step, the LayoutToNetlist object is created and
|
||||
* if required, configured. Methods to be used in this step are "set_threads",
|
||||
* "set_area_ratio" or "set_max_vertex_count". The constructor for the LayoutToNetlist
|
||||
* object receives a db::RecursiveShapeIterator object which basically supplies the
|
||||
* hierarchy and the layout taken as input.
|
||||
* @/li
|
||||
* @li Preparation
|
||||
* In this step, the device recognitions and extraction layers are drawn from
|
||||
* the framework. Derived can now be computed using boolean operations.
|
||||
* Methods to use in this step are "make_layer" and it's variants.
|
||||
* Layer preparation is not necessarily required to happen before all
|
||||
* other steps. Layers can be computed shortly before they are required.
|
||||
* @/li
|
||||
* @li Following the preparation, the devices can be extracted using "extract_devices".
|
||||
* This method needs to be called for each device extractor required. Each time,
|
||||
* a device extractor needs to be given plus a map of device layers. The device
|
||||
* layers are device extractor specific. Either original or derived layers
|
||||
* may be specified here. Layer preparation may happen between calls to "extract_devices".
|
||||
* @/li
|
||||
* @li Once the devices are derived, the netlist connectivity can be defined and the
|
||||
* netlist extracted. The connectivity is defined with "connect" and it's
|
||||
* flavours. The actual netlist extraction happens with "extract_netlist".
|
||||
* @/li
|
||||
* @li After netlist extraction, the information is ready to be retrieved.
|
||||
* The produced netlist is available with "netlist". The Shapes of a
|
||||
* specific net are available with "shapes_of_net". "probe_net" allows
|
||||
* finding a net by probing a specific location.
|
||||
* @li
|
||||
*/
|
||||
class DB_PUBLIC LayoutToNetlist
|
||||
: public gsi::ObjectBase, public tl::Object
|
||||
{
|
||||
public:
|
||||
typedef std::map<unsigned int, std::string>::const_iterator layer_iterator;
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*
|
||||
* See the class description for details.
|
||||
*/
|
||||
LayoutToNetlist (const db::RecursiveShapeIterator &iter);
|
||||
|
||||
/**
|
||||
* @brief Alternative constructor using an external deep shape storage
|
||||
*
|
||||
* This constructor allows using an external DSS. It's intended to be used
|
||||
* with existing DSS instances. Existing layers can be registered with
|
||||
* "register_layer". The LayoutToNetlist object will hold a weak reference
|
||||
* to the DSS but not own the DSS.
|
||||
*
|
||||
* NOTE: if using make_layer, these new layers will be created in the DSS
|
||||
* given in this constructor.
|
||||
*/
|
||||
LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index = 0);
|
||||
|
||||
/**
|
||||
* @brief Alternative constructor for flat mode
|
||||
*
|
||||
* In flat mode, the internal DSS will be initialized to a top-level only
|
||||
* layout. All layers entered through "register_layer" or created with
|
||||
* "make_layer" will become flat ones.
|
||||
*/
|
||||
LayoutToNetlist (const std::string &topcell_name, double dbu);
|
||||
|
||||
/**
|
||||
* @brief The default constructor
|
||||
*/
|
||||
LayoutToNetlist ();
|
||||
|
||||
/**
|
||||
* @brief The destructor
|
||||
*/
|
||||
~LayoutToNetlist ();
|
||||
|
||||
/**
|
||||
* @brief Sets the number of threads to use for operations which support multiple threads
|
||||
*/
|
||||
void set_threads (int n);
|
||||
|
||||
/**
|
||||
* @brief Gets the number of threads to use
|
||||
*/
|
||||
int threads () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the area_ratio parameter for the hierarchical network processor
|
||||
* This parameter controls splitting of large polygons in order to reduce the
|
||||
* error made by the bounding box approximation.
|
||||
*/
|
||||
void set_area_ratio (double ar);
|
||||
|
||||
/**
|
||||
* @brief Gets the area ratio
|
||||
*/
|
||||
double area_ratio () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the max_vertex_count parameter for the hierarchical network processor
|
||||
* This parameter controls splitting of large polygons in order to enhance performance
|
||||
* for very big polygons.
|
||||
*/
|
||||
void set_max_vertex_count (size_t n);
|
||||
|
||||
/**
|
||||
* @brief Gets the max vertex count
|
||||
*/
|
||||
size_t max_vertex_count () const;
|
||||
|
||||
/**
|
||||
* @brief Register a layer under the given name
|
||||
* This is a formal name for the layer. Using a name or layer properties
|
||||
* (see below) enhances readability of backannotated information
|
||||
* if layers are involved. Use this method to attach a name to a region
|
||||
* derived by boolean operations for example.
|
||||
* Named regions are persisted inside the LayoutToNetlist object. Only
|
||||
* named regions can be put into "connect".
|
||||
*/
|
||||
void register_layer (const db::Region ®ion, const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the given region
|
||||
* Returns an empty string if the region does not have a name.
|
||||
*/
|
||||
std::string name (const db::Region ®ion) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the given layer by index
|
||||
* Returns an empty string if the layer does not have a name.
|
||||
*/
|
||||
std::string name (unsigned int) const;
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the region is a persisted region
|
||||
* Persisted regions have a name and are kept inside the LayoutToNetlist
|
||||
* object.
|
||||
*/
|
||||
bool is_persisted (const db::Region ®ion) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the region (layer) with the given name
|
||||
* If the name is not valid, this method returns 0. Otherwise it
|
||||
* will return a new'd Region object referencing the layer with
|
||||
* the given name. It must be deleted by the caller.
|
||||
*/
|
||||
db::Region *layer_by_name (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the region (layer) by index
|
||||
* If the index is not valid, this method returns 0. Otherwise it
|
||||
* will return a new'd Region object referencing the layer with
|
||||
* the given name. It must be deleted by the caller.
|
||||
* Only named layers are managed by LayoutToNetlist and can
|
||||
* be retrieved with this method.
|
||||
*/
|
||||
db::Region *layer_by_index (unsigned int index);
|
||||
|
||||
/**
|
||||
* @brief Iterates over the layer indexes and names managed by this object (begin)
|
||||
*/
|
||||
layer_iterator begin_layers () const
|
||||
{
|
||||
return m_name_of_layer.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates over the layer indexes and names managed by this object (end)
|
||||
*/
|
||||
layer_iterator end_layers () const
|
||||
{
|
||||
return m_name_of_layer.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new empty region
|
||||
* This method returns a new'd object which must be deleted by the caller.
|
||||
*/
|
||||
db::Region *make_layer (const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Creates a new region representing an original layer
|
||||
* "layer_index" is the layer index of the desired layer in the original layout.
|
||||
* This variant produces polygons and takes texts for net name annotation.
|
||||
* A variant not taking texts is "make_polygon_layer". A Variant only taking
|
||||
* texts is "make_text_layer".
|
||||
* All variants return a new'd object which must be deleted by the caller.
|
||||
* Named regions are considered "precious". The LayoutToNetlist object will
|
||||
* keep a reference on all named layers, so they persist during the lifetime
|
||||
* of the LayoutToNetlist object.
|
||||
* Only named layers can be used for connect (see below).
|
||||
*/
|
||||
db::Region *make_layer (unsigned int layer_index, const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Creates a new region representing an original layer taking texts only
|
||||
* See "make_layer" for details.
|
||||
*/
|
||||
db::Region *make_text_layer (unsigned int layer_index, const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Creates a new region representing an original layer taking polygons and texts
|
||||
* See "make_layer" for details.
|
||||
*/
|
||||
db::Region *make_polygon_layer (unsigned int layer_index, const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Extracts devices
|
||||
* See the class description for more details.
|
||||
* This method will run device extraction for the given extractor. The layer map is specific
|
||||
* for the extractor and uses the region objects derived with "make_layer" and it's variants.
|
||||
*
|
||||
* In addition, derived regions can be passed too. Certain limitations apply. It's safe to use
|
||||
* boolean operations for deriving layers. Other operations are applicable as long as they are
|
||||
* capable of delivering hierarchical layers.
|
||||
*
|
||||
* If errors occur, the device extractor will contain theses errors.
|
||||
*/
|
||||
void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::Region *> &layers);
|
||||
|
||||
/**
|
||||
* @brief Defines an intra-layer connection for the given layer.
|
||||
* The layer is either an original layer created with "make_layer" and it's variants or
|
||||
* a derived layer. Certain limitations apply. It's safe to use
|
||||
* boolean operations for deriving layers. Other operations are applicable as long as they are
|
||||
* capable of delivering hierarchical layers.
|
||||
* Regions put into "connect" need to be named.
|
||||
*/
|
||||
void connect (const db::Region &l);
|
||||
|
||||
/**
|
||||
* @brief Defines an inter-layer connection for the given layers.
|
||||
* The conditions mentioned with intra-layer "connect" apply for this method too.
|
||||
* Regions put into "connect" need to be named.
|
||||
*/
|
||||
void connect (const db::Region &a, const db::Region &b);
|
||||
|
||||
/**
|
||||
* @brief Connects the given layer with a global net with the given name
|
||||
* Returns the global net ID
|
||||
* Regions put into "connect" need to be named.
|
||||
*/
|
||||
size_t connect_global (const db::Region &l, const std::string &gn);
|
||||
|
||||
/**
|
||||
* @brief Gets the global net name for a given global net ID
|
||||
*/
|
||||
const std::string &global_net_name (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the global net ID for a given name name
|
||||
*/
|
||||
size_t global_net_id (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Runs the netlist extraction
|
||||
* See the class description for more details.
|
||||
*/
|
||||
void extract_netlist (bool join_nets_by_label = true);
|
||||
|
||||
/**
|
||||
* @brief Marks the netlist as extracted
|
||||
* NOTE: this method is provided for special cases such as netlist readers. Don't
|
||||
* use it.
|
||||
*/
|
||||
void set_netlist_extracted ();
|
||||
|
||||
/**
|
||||
* @brief Gets the internal DeepShapeStore object
|
||||
*
|
||||
* This method is intended for special cases, i.e. for the master
|
||||
* LayoutToNetlist object in the DRC environment. The DSS provided
|
||||
* for DRC needs to be initialized properly for text representation.
|
||||
*/
|
||||
db::DeepShapeStore &dss ()
|
||||
{
|
||||
tl_assert (mp_dss.get () != 0);
|
||||
return *mp_dss;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the internal DeepShapeStore object (const version)
|
||||
*
|
||||
* See the non-const version for details.
|
||||
*/
|
||||
const db::DeepShapeStore &dss () const
|
||||
{
|
||||
tl_assert (mp_dss.get () != 0);
|
||||
return *mp_dss;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the internal layout
|
||||
*/
|
||||
const db::Layout *internal_layout () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the internal top cell
|
||||
*/
|
||||
const db::Cell *internal_top_cell () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the internal layout (non-const version)
|
||||
*/
|
||||
db::Layout *internal_layout ();
|
||||
|
||||
/**
|
||||
* @brief Gets the internal top cell (non-const version)
|
||||
*/
|
||||
db::Cell *internal_top_cell ();
|
||||
|
||||
/**
|
||||
* @brief Gets the connectivity object
|
||||
*/
|
||||
const db::Connectivity &connectivity () const
|
||||
{
|
||||
return m_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the internal layer for a given extraction layer
|
||||
* This method is required to derive the internal layer index - for example for
|
||||
* investigating the cluster tree.
|
||||
*/
|
||||
unsigned int layer_of (const db::Region ®ion) const;
|
||||
|
||||
/**
|
||||
* @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.
|
||||
* If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally.
|
||||
* Use this option, if you want to access device terminal shapes per device.
|
||||
* CAUTION: This function may create new cells in "layout".
|
||||
*/
|
||||
db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells = false);
|
||||
|
||||
/**
|
||||
* @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.
|
||||
* This version will not create new cells in the target layout.
|
||||
* If the required cells do not exist there yet, flatting will happen.
|
||||
*/
|
||||
db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell);
|
||||
|
||||
/**
|
||||
* @brief gets the netlist extracted (0 if no extraction happened yet)
|
||||
*/
|
||||
db::Netlist *netlist () const;
|
||||
|
||||
/**
|
||||
* @brief gets the netlist extracted or make on if none exists yet.
|
||||
* NOTE: this method is provided for special cases like readers of persisted
|
||||
* layout to netlist data.
|
||||
*/
|
||||
db::Netlist *make_netlist ();
|
||||
|
||||
/**
|
||||
* @brief Gets the hierarchical shape clusters derived in the net extraction.
|
||||
* NOTE: the layer and cell indexes used inside this structure refer to the
|
||||
* internal layout.
|
||||
*/
|
||||
const db::hier_clusters<db::PolygonRef> &net_clusters () const
|
||||
{
|
||||
return m_net_clusters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the hierarchical shape clusters derived in the net extraction (non-conver version)
|
||||
*/
|
||||
db::hier_clusters<db::PolygonRef> &net_clusters ()
|
||||
{
|
||||
return m_net_clusters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns all shapes of a specific net and layer.
|
||||
*
|
||||
* If "recursive" is true, the returned region will contain the shapes of
|
||||
* all subcircuits too.
|
||||
*
|
||||
* This methods returns a new'd Region. It's the responsibility of the caller
|
||||
* to delete this object.
|
||||
*/
|
||||
db::Region *shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const;
|
||||
|
||||
/**
|
||||
* @brief Delivers all shapes of a specific net and layer to the given Shapes container.
|
||||
*
|
||||
* If "recursive" is true, the returned region will contain the shapes of
|
||||
* all subcircuits too.
|
||||
*
|
||||
* This methods returns a new'd Region. It's the responsibility of the caller
|
||||
* to delete this object.
|
||||
*/
|
||||
void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const;
|
||||
|
||||
/**
|
||||
* @brief Builds a net representation in the given layout and cell
|
||||
*
|
||||
* This method has two modes: recursive and top-level mode. In recursive mode,
|
||||
* it will create a proper hierarchy below the given target cell to hold all subcircuits the
|
||||
* net connects to. It will copy the net's parts from this subcircuits into these cells.
|
||||
*
|
||||
* In top-level mode, only the shapes from the net inside it's circuit are copied to
|
||||
* the given target cell. No other cells are created.
|
||||
*
|
||||
* Recursive mode is picked when a cell name prefix is given. The new cells will be
|
||||
* named like cell_name_prefix + circuit name.
|
||||
*
|
||||
* If a device cell name prefix is given, cells will be produced for each device abstract
|
||||
* using a name like device_cell_name_prefix + device name.
|
||||
*
|
||||
* @param target The target layout
|
||||
* @param target_cell The target cell
|
||||
* @param lmap Target layer indexes (keys) and net regions (values)
|
||||
* @param cell_name_prefix Chooses recursive mode if non-null
|
||||
* @param device_cell_name_prefix See above
|
||||
*/
|
||||
void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const;
|
||||
|
||||
/**
|
||||
* @brief Builds a full hierarchical representation of the nets
|
||||
*
|
||||
* This method copies all nets into cells corresponding to the circuits. It uses the cmap
|
||||
* object to determine the target cell (create them with "cell_mapping_into" or "const_cell_mapping_into").
|
||||
* If no mapping is requested, the specific circuit it skipped.
|
||||
*
|
||||
* The method has two net annotation modes:
|
||||
* * No annotation (net_cell_name_prefix == 0): the shapes will be put into the target cell simply
|
||||
* * Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created
|
||||
* and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name).
|
||||
*
|
||||
* In addition, net hierarchy is covered in two ways:
|
||||
* * No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their
|
||||
* respective circuits. The connections are not indicated.
|
||||
* * Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built
|
||||
* to accomodate the subnets (see build_net in recursive mode).
|
||||
*
|
||||
* If a device cell name prefix is given, cells will be produced for each device abstract
|
||||
* using a name like device_cell_name_prefix + device name.
|
||||
*
|
||||
* @param cmap The mapping of internal layout to target layout for the circuit mapping
|
||||
* @param target The target layout
|
||||
* @param lmap Target layer indexes (keys) and net regions (values)
|
||||
* @param circuit_cell_name_prefix See method description
|
||||
* @param net_cell_name_prefix See method description
|
||||
* @param device_cell_name_prefix See above
|
||||
*/
|
||||
void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const;
|
||||
|
||||
/**
|
||||
* @brief Finds the net by probing a specific location on the given layer
|
||||
*
|
||||
* This method will find a net looking at the given layer at the specific position.
|
||||
* It will traverse the hierarchy below if no shape in the requested layer is found
|
||||
* in the specified location.
|
||||
*
|
||||
* If no net is found at all, 0 is returned.
|
||||
*
|
||||
* This variant accepts a micrometer-unit location. The location is given in the
|
||||
* coordinate space of the initial cell.
|
||||
*/
|
||||
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point);
|
||||
|
||||
/**
|
||||
* @brief Finds the net by probing a specific location on the given layer
|
||||
* See the description of the other "probe_net" variant.
|
||||
* This variant accepts a database-unit location. The location is given in the
|
||||
* coordinate space of the initial cell.
|
||||
*/
|
||||
db::Net *probe_net (const db::Region &of_region, const db::Point &point);
|
||||
|
||||
/**
|
||||
* @brief Runs an antenna check on the extracted clusters
|
||||
*
|
||||
* The antenna check will traverse all clusters and run an antenna check
|
||||
* for all root clusters. The antenna ratio is defined by the total
|
||||
* area of all "metal" shapes divided by the total area of all "gate" shapes
|
||||
* on the cluster. Of all clusters where the antenna ratio is larger than
|
||||
* the limit ratio all metal shapes are copied to the output region as
|
||||
* error markers.
|
||||
*
|
||||
* The limit ratio can be modified by the presence of connections to
|
||||
* other layers (specifically designating diodes for charge removal).
|
||||
* Each of these layers will modify the ratio by adding a value of
|
||||
* A(diode) / Ared[um^2] to the ratio. A(diode) is the area of the
|
||||
* diode layer per cluster. Both the diode layer and the Ared value
|
||||
* are specified as pairs in "diodes".
|
||||
*
|
||||
* A special case is Ared = 0: in this case, the presence of any shapes
|
||||
* on the diode layer will entirely disable the check on a cluster,
|
||||
* regardless of the diode's area.
|
||||
* In other words: any diode will make the net safe against antenna discharge.
|
||||
*/
|
||||
db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ());
|
||||
|
||||
private:
|
||||
// no copying
|
||||
LayoutToNetlist (const db::LayoutToNetlist &other);
|
||||
LayoutToNetlist &operator= (const db::LayoutToNetlist &other);
|
||||
|
||||
db::RecursiveShapeIterator m_iter;
|
||||
std::auto_ptr<db::DeepShapeStore> mp_internal_dss;
|
||||
tl::weak_ptr<db::DeepShapeStore> mp_dss;
|
||||
unsigned int m_layout_index;
|
||||
db::Connectivity m_conn;
|
||||
db::hier_clusters<db::PolygonRef> m_net_clusters;
|
||||
std::auto_ptr<db::Netlist> mp_netlist;
|
||||
std::set<db::DeepLayer> m_dlrefs;
|
||||
std::map<std::string, db::DeepLayer> m_named_regions;
|
||||
std::map<unsigned int, std::string> m_name_of_layer;
|
||||
bool m_netlist_extracted;
|
||||
bool m_is_flat;
|
||||
db::DeepLayer m_dummy_layer;
|
||||
|
||||
void init ();
|
||||
size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, std::vector<db::InstElement> &rev_inst_path);
|
||||
void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const ICplxTrans &tr) const;
|
||||
void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const Net *net, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const ICplxTrans &tr) const;
|
||||
db::DeepLayer deep_layer_of (const db::Region ®ion) const;
|
||||
void ensure_layout () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template<> struct type_traits<db::LayoutToNetlist> : public tl::type_traits<void>
|
||||
{
|
||||
// mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbLayoutToNetlistFormatDefs.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
namespace l2n_std_format
|
||||
{
|
||||
template<> DB_PUBLIC const std::string keys<false>::version_key ("version");
|
||||
template<> DB_PUBLIC const std::string keys<false>::description_key ("description");
|
||||
template<> DB_PUBLIC const std::string keys<false>::top_key ("top");
|
||||
template<> DB_PUBLIC const std::string keys<false>::unit_key ("unit");
|
||||
template<> DB_PUBLIC const std::string keys<false>::layer_key ("layer");
|
||||
template<> DB_PUBLIC const std::string keys<false>::connect_key ("connect");
|
||||
template<> DB_PUBLIC const std::string keys<false>::global_key ("global");
|
||||
template<> DB_PUBLIC const std::string keys<false>::circuit_key ("circuit");
|
||||
template<> DB_PUBLIC const std::string keys<false>::net_key ("net");
|
||||
template<> DB_PUBLIC const std::string keys<false>::name_key ("name");
|
||||
template<> DB_PUBLIC const std::string keys<false>::device_key ("device");
|
||||
template<> DB_PUBLIC const std::string keys<false>::polygon_key ("polygon");
|
||||
template<> DB_PUBLIC const std::string keys<false>::rect_key ("rect");
|
||||
template<> DB_PUBLIC const std::string keys<false>::terminal_key ("terminal");
|
||||
template<> DB_PUBLIC const std::string keys<false>::abstract_key ("abstract");
|
||||
template<> DB_PUBLIC const std::string keys<false>::param_key ("param");
|
||||
template<> DB_PUBLIC const std::string keys<false>::location_key ("location");
|
||||
template<> DB_PUBLIC const std::string keys<false>::rotation_key ("rotation");
|
||||
template<> DB_PUBLIC const std::string keys<false>::mirror_key ("mirror");
|
||||
template<> DB_PUBLIC const std::string keys<false>::scale_key ("scale");
|
||||
template<> DB_PUBLIC const std::string keys<false>::pin_key ("pin");
|
||||
|
||||
template<> DB_PUBLIC const std::string keys<true>::version_key ("V");
|
||||
template<> DB_PUBLIC const std::string keys<true>::description_key ("B");
|
||||
template<> DB_PUBLIC const std::string keys<true>::top_key ("W");
|
||||
template<> DB_PUBLIC const std::string keys<true>::unit_key ("U");
|
||||
template<> DB_PUBLIC const std::string keys<true>::layer_key ("L");
|
||||
template<> DB_PUBLIC const std::string keys<true>::connect_key ("C");
|
||||
template<> DB_PUBLIC const std::string keys<true>::global_key ("G");
|
||||
template<> DB_PUBLIC const std::string keys<true>::circuit_key ("X");
|
||||
template<> DB_PUBLIC const std::string keys<true>::net_key ("N");
|
||||
template<> DB_PUBLIC const std::string keys<true>::name_key ("I");
|
||||
template<> DB_PUBLIC const std::string keys<true>::device_key ("D");
|
||||
template<> DB_PUBLIC const std::string keys<true>::polygon_key ("Q");
|
||||
template<> DB_PUBLIC const std::string keys<true>::rect_key ("R");
|
||||
template<> DB_PUBLIC const std::string keys<true>::terminal_key ("T");
|
||||
template<> DB_PUBLIC const std::string keys<true>::abstract_key ("A");
|
||||
template<> DB_PUBLIC const std::string keys<true>::param_key ("E");
|
||||
template<> DB_PUBLIC const std::string keys<true>::location_key ("Y");
|
||||
template<> DB_PUBLIC const std::string keys<true>::rotation_key ("O");
|
||||
template<> DB_PUBLIC const std::string keys<true>::mirror_key ("M");
|
||||
template<> DB_PUBLIC const std::string keys<true>::scale_key ("S");
|
||||
template<> DB_PUBLIC const std::string keys<true>::pin_key ("P");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbLayoutToNetlistFormatDefs
|
||||
#define HDR_dbLayoutToNetlistFormatDefs
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* This is the internal persistency format for LayoutToNetlist
|
||||
*
|
||||
* It's intentionally *not* XML to keep the overhead low.
|
||||
*
|
||||
* Comments are introduced by hash: # ...
|
||||
* Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes.
|
||||
* Escape character is backslash.
|
||||
* Separator is either , or whitespace. Keywords and names are case sensitive.
|
||||
* Short keys are provided for compacter representation. Short keys can be
|
||||
* non-alpha (e.g. "*") or empty.
|
||||
* Single-valued attributes can be given without brackets.
|
||||
* All dimensions are in units of database unit.
|
||||
* The file follows the declaration-before-use principle
|
||||
* (circuits before subcircuits, nets before use ...)
|
||||
*
|
||||
* Global statements:
|
||||
*
|
||||
* version(<number>) - file format version [short key: V]
|
||||
* description(<text>) - an arbitrary description text [short key: B]
|
||||
* unit(<unit>) - specifies the database unit [short key: U]
|
||||
* top(<circuit>) - specifies the name of the top circuit [short key: W]
|
||||
* layer(<name>) - define a layer [short key: L]
|
||||
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
|
||||
* global(<layer> <net-name> ...)
|
||||
* - connects the shapes of the layer with the given global
|
||||
* nets [short key: G]
|
||||
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
|
||||
* device(<name> <class> [device-abstract-def])
|
||||
* - device abstract [short key: D]
|
||||
*
|
||||
* [circuit-def]:
|
||||
*
|
||||
* net(<id> [net-name]? [geometry-def]*)
|
||||
* - net geometry [short key: N]
|
||||
* A net declaration shall be there also if no geometry
|
||||
* is present. The ID is a numerical shortcut for the net.
|
||||
* pin(<name> <net-id>) - outgoing pin connection [short key: P]
|
||||
* device(<name> <abstract> [device-def])
|
||||
* - device with connections [short key: D]
|
||||
* circuit(<name> [circuit-def]) - subcircuit with connections [short key: X]
|
||||
*
|
||||
* [net-name]:
|
||||
* name(<net-name>) - specify net name [short key:
|
||||
*
|
||||
* [geometry-def]:
|
||||
*
|
||||
* polygon(<layer> <x> <y> ...) - defines a polygon [short key: Q]
|
||||
* "*" for <x> or <y> means take previous
|
||||
* rect(<layer> <left> <bottom> <right> <top>)
|
||||
* - defines a rectangle [short key: R]
|
||||
*
|
||||
* [device-abstract-def]:
|
||||
*
|
||||
* terminal(<terminal-name> [geometry-def]*)
|
||||
* - specifies the terminal geometry [short key: T]
|
||||
*
|
||||
* [device-def]:
|
||||
*
|
||||
* location(<x> <y>) - location of the device [short key Y]
|
||||
* must be before terminal
|
||||
* param(<name> <value>) - defines a parameter [short key E]
|
||||
* terminal(<terminal-name> <net-id>)
|
||||
* - specifies connection of the terminal with
|
||||
* a net (short key: T)
|
||||
*
|
||||
* [subcircuit-def]:
|
||||
*
|
||||
* location(<x> <y>) - location of the subcircuit [short key Y]
|
||||
* rotation(<angle>) - rotation angle (in degree, default is 0) [short key O]
|
||||
* mirror - if specified, the instance is mirrored before rotation [short key M]
|
||||
* scale(<mag>) - magnification (default is 1) [short key S]
|
||||
* pin(<pin-name> <net-id>) - specifies connection of the pin with a net [short key: P]
|
||||
*/
|
||||
|
||||
namespace l2n_std_format
|
||||
{
|
||||
template <bool Short>
|
||||
struct DB_PUBLIC keys
|
||||
{
|
||||
static const std::string version_key;
|
||||
static const std::string description_key;
|
||||
static const std::string top_key;
|
||||
static const std::string unit_key;
|
||||
static const std::string layer_key;
|
||||
static const std::string connect_key;
|
||||
static const std::string global_key;
|
||||
static const std::string circuit_key;
|
||||
static const std::string net_key;
|
||||
static const std::string name_key;
|
||||
static const std::string device_key;
|
||||
static const std::string subcircuit_key;
|
||||
static const std::string polygon_key;
|
||||
static const std::string rect_key;
|
||||
static const std::string terminal_key;
|
||||
static const std::string abstract_key;
|
||||
static const std::string param_key;
|
||||
static const std::string location_key;
|
||||
static const std::string rotation_key;
|
||||
static const std::string mirror_key;
|
||||
static const std::string scale_key;
|
||||
static const std::string pin_key;
|
||||
static const std::string indent1;
|
||||
static const std::string indent2;
|
||||
|
||||
inline static bool is_short () { return Short; }
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,708 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbLayoutToNetlistReader.h"
|
||||
#include "dbLayoutToNetlistFormatDefs.h"
|
||||
#include "dbLayoutToNetlist.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
namespace l2n_std_reader {
|
||||
|
||||
class Brace
|
||||
{
|
||||
public:
|
||||
Brace (LayoutToNetlistStandardReader *reader) : mp_reader (reader), m_checked (false)
|
||||
{
|
||||
m_has_brace = reader->test ("(");
|
||||
}
|
||||
|
||||
operator bool ()
|
||||
{
|
||||
if (! m_has_brace) {
|
||||
m_checked = true;
|
||||
return false;
|
||||
} else if (mp_reader->test (")")) {
|
||||
m_checked = true;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void done ()
|
||||
{
|
||||
if (m_has_brace && ! m_checked) {
|
||||
mp_reader->expect (")");
|
||||
m_checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
LayoutToNetlistStandardReader *mp_reader;
|
||||
bool m_checked;
|
||||
bool m_has_brace;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
typedef l2n_std_format::keys<true> skeys;
|
||||
typedef l2n_std_format::keys<false> lkeys;
|
||||
|
||||
LayoutToNetlistStandardReader::LayoutToNetlistStandardReader (tl::InputStream &stream)
|
||||
: m_stream (stream), m_path (stream.absolute_path ())
|
||||
{
|
||||
skip ();
|
||||
}
|
||||
|
||||
bool
|
||||
LayoutToNetlistStandardReader::test (const std::string &token)
|
||||
{
|
||||
skip ();
|
||||
return ! at_end () && m_ex.test (token.c_str ());
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlistStandardReader::expect (const std::string &token)
|
||||
{
|
||||
m_ex.expect (token.c_str ());
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlistStandardReader::read_word_or_quoted (std::string &s)
|
||||
{
|
||||
m_ex.read_word_or_quoted (s);
|
||||
}
|
||||
|
||||
int
|
||||
LayoutToNetlistStandardReader::read_int ()
|
||||
{
|
||||
int i = 0;
|
||||
m_ex.read (i);
|
||||
return i;
|
||||
}
|
||||
|
||||
db::Coord
|
||||
LayoutToNetlistStandardReader::read_coord ()
|
||||
{
|
||||
db::Coord i = 0;
|
||||
m_ex.read (i);
|
||||
return i;
|
||||
}
|
||||
|
||||
double
|
||||
LayoutToNetlistStandardReader::read_double ()
|
||||
{
|
||||
double d = 0;
|
||||
m_ex.read (d);
|
||||
return d;
|
||||
}
|
||||
|
||||
bool
|
||||
LayoutToNetlistStandardReader::at_end ()
|
||||
{
|
||||
return (m_ex.at_end () && m_stream.at_end ());
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlistStandardReader::skip ()
|
||||
{
|
||||
while (m_ex.at_end () || *m_ex.skip () == '#') {
|
||||
if (m_stream.at_end ()) {
|
||||
return;
|
||||
}
|
||||
m_line = m_stream.get_line ();
|
||||
m_ex = tl::Extractor (m_line.c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardReader::read (db::LayoutToNetlist *l2n)
|
||||
{
|
||||
try {
|
||||
do_read (l2n);
|
||||
} catch (tl::Exception &ex) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("%s in line: %d of %s")), ex.msg (), m_stream.line_number (), m_path));
|
||||
}
|
||||
}
|
||||
|
||||
static db::Region &layer_by_name (db::LayoutToNetlist *l2n, const std::string &name)
|
||||
{
|
||||
db::Region *l = l2n->layer_by_name (name);
|
||||
if (! l) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid layer name: ")) + name);
|
||||
}
|
||||
return *l;
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n)
|
||||
{
|
||||
int version = 0;
|
||||
std::string description;
|
||||
|
||||
tl_assert (l2n->internal_layout ());
|
||||
l2n->internal_layout ()->dbu (1.0); // mainly for testing
|
||||
|
||||
if (l2n->internal_layout ()->cells () == 0) {
|
||||
l2n->internal_layout ()->add_cell ("TOP");
|
||||
}
|
||||
tl_assert (l2n->internal_top_cell () != 0);
|
||||
|
||||
l2n->make_netlist ();
|
||||
|
||||
while (! at_end ()) {
|
||||
|
||||
if (test (skeys::version_key) || test (lkeys::version_key)) {
|
||||
|
||||
Brace br (this);
|
||||
version = read_int ();
|
||||
br.done ();
|
||||
|
||||
} else if (test (skeys::description_key) || test (lkeys::description_key)) {
|
||||
|
||||
Brace br (this);
|
||||
read_word_or_quoted (description);
|
||||
br.done ();
|
||||
|
||||
} else if (test (skeys::unit_key) || test (lkeys::unit_key)) {
|
||||
|
||||
Brace br (this);
|
||||
double dbu = read_double ();
|
||||
l2n->internal_layout ()->dbu (dbu);
|
||||
br.done ();
|
||||
|
||||
} else if (test (skeys::top_key) || test (lkeys::top_key)) {
|
||||
|
||||
Brace br (this);
|
||||
std::string top;
|
||||
read_word_or_quoted (top);
|
||||
l2n->internal_layout ()->rename_cell (l2n->internal_top_cell ()->cell_index (), top.c_str ());
|
||||
br.done ();
|
||||
|
||||
} else if (test (skeys::layer_key) || test (lkeys::layer_key)) {
|
||||
|
||||
Brace br (this);
|
||||
std::string layer;
|
||||
read_word_or_quoted (layer);
|
||||
delete l2n->make_layer (layer);
|
||||
br.done ();
|
||||
|
||||
} else if (test (skeys::connect_key) || test (lkeys::connect_key)) {
|
||||
|
||||
Brace br (this);
|
||||
std::string l1;
|
||||
read_word_or_quoted (l1);
|
||||
while (br) {
|
||||
std::string l2;
|
||||
read_word_or_quoted (l2);
|
||||
l2n->connect (layer_by_name (l2n, l1), layer_by_name (l2n, l2));
|
||||
}
|
||||
br.done ();
|
||||
|
||||
} else if (test (skeys::global_key) || test (lkeys::global_key)) {
|
||||
|
||||
Brace br (this);
|
||||
std::string l1;
|
||||
read_word_or_quoted (l1);
|
||||
while (br) {
|
||||
std::string g;
|
||||
read_word_or_quoted (g);
|
||||
l2n->connect_global (layer_by_name (l2n, l1), g);
|
||||
}
|
||||
br.done ();
|
||||
|
||||
} else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) {
|
||||
|
||||
Brace br (this);
|
||||
std::string name;
|
||||
read_word_or_quoted (name);
|
||||
|
||||
db::Circuit *circuit = new db::Circuit ();
|
||||
circuit->set_name (name);
|
||||
l2n->netlist ()->add_circuit (circuit);
|
||||
|
||||
db::Layout *ly = l2n->internal_layout ();
|
||||
std::pair<bool, db::cell_index_type> ci_old = ly->cell_by_name (name.c_str ());
|
||||
db::cell_index_type ci = ci_old.first ? ci_old.second : ly->add_cell (name.c_str ());
|
||||
circuit->set_cell_index (ci);
|
||||
|
||||
std::map<db::CellInstArray, std::list<Connections> > connections;
|
||||
std::map<unsigned int, Net *> id2net;
|
||||
|
||||
while (br) {
|
||||
|
||||
if (test (skeys::net_key) || test (lkeys::net_key)) {
|
||||
read_net (l2n, circuit, id2net);
|
||||
} else if (test (skeys::pin_key) || test (lkeys::pin_key)) {
|
||||
read_pin (l2n, circuit, id2net);
|
||||
} else if (test (skeys::device_key) || test (lkeys::device_key)) {
|
||||
std::list<Connections> conn;
|
||||
db::CellInstArray ia = read_device (l2n, circuit, conn, id2net);
|
||||
connections[ia] = conn;
|
||||
} else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) {
|
||||
std::list<Connections> conn;
|
||||
db::CellInstArray ia = read_subcircuit (l2n, circuit, conn, id2net);
|
||||
connections[ia] = conn;
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)")));
|
||||
}
|
||||
|
||||
}
|
||||
br.done ();
|
||||
|
||||
db::Cell &ccell = ly->cell (ci);
|
||||
|
||||
// connections needs to be made after the instances (because in a readonly Instances container
|
||||
// the Instance pointers will invalidate when new instances are added)
|
||||
for (db::Cell::const_iterator i = ccell.begin (); ! i.at_end (); ++i) {
|
||||
std::map<db::CellInstArray, std::list<Connections> >::const_iterator c = connections.find (i->cell_inst ());
|
||||
if (c != connections.end ()) {
|
||||
for (std::list<Connections>::const_iterator j = c->second.begin (); j != c->second.end (); ++j) {
|
||||
l2n->net_clusters ().clusters_per_cell (ci).add_connection (j->from_cluster, db::ClusterInstance (j->to_cluster, i->cell_index (), i->complex_trans (), i->prop_id ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (test (skeys::device_key) || test (lkeys::device_key)) {
|
||||
|
||||
Brace br (this);
|
||||
std::string name;
|
||||
read_word_or_quoted (name);
|
||||
|
||||
db::DeviceAbstract *dm = new db::DeviceAbstract ();
|
||||
dm->set_name (name);
|
||||
l2n->netlist ()->add_device_abstract (dm);
|
||||
|
||||
db::cell_index_type ci = l2n->internal_layout ()->add_cell (name.c_str ());
|
||||
dm->set_cell_index (ci);
|
||||
|
||||
std::string cls;
|
||||
read_word_or_quoted (cls);
|
||||
|
||||
db::DeviceClass *dc = 0;
|
||||
for (db::Netlist::device_class_iterator i = l2n->netlist ()->begin_device_classes (); i != l2n->netlist ()->end_device_classes (); ++i) {
|
||||
if (i->name () == cls) {
|
||||
dc = i.operator-> ();
|
||||
}
|
||||
}
|
||||
|
||||
// use a generic device class unless the right one is registered already.
|
||||
bool gen_dc = (dc == 0);
|
||||
if (gen_dc) {
|
||||
dc = new db::DeviceClass ();
|
||||
dc->set_name (cls);
|
||||
l2n->netlist ()->add_device_class (dc);
|
||||
}
|
||||
|
||||
dm->set_device_class (dc);
|
||||
|
||||
while (br) {
|
||||
|
||||
if (test (skeys::terminal_key) || test (lkeys::terminal_key)) {
|
||||
read_abstract_terminal (l2n, dm, gen_dc ? dc : 0);
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device abstract definition (terminal expected)")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
br.done ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
l2n->set_netlist_extracted ();
|
||||
}
|
||||
|
||||
std::pair<unsigned int, db::PolygonRef>
|
||||
LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n)
|
||||
{
|
||||
std::string lname;
|
||||
|
||||
if (test (skeys::rect_key) || test (lkeys::rect_key)) {
|
||||
|
||||
Brace br (this);
|
||||
|
||||
read_word_or_quoted (lname);
|
||||
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
|
||||
|
||||
db::Coord l = read_coord ();
|
||||
db::Coord b = read_coord ();
|
||||
db::Coord r = read_coord ();
|
||||
db::Coord t = read_coord ();
|
||||
db::Box box (l, b, r, t);
|
||||
|
||||
br.done ();
|
||||
|
||||
return std::make_pair (lid, db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ()));
|
||||
|
||||
} else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) {
|
||||
|
||||
Brace br (this);
|
||||
|
||||
read_word_or_quoted (lname);
|
||||
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
|
||||
|
||||
std::vector<db::Point> pt;
|
||||
|
||||
db::Coord x = 0, y = 0;
|
||||
while (br) {
|
||||
if (! test ("*")) {
|
||||
x = read_coord ();
|
||||
}
|
||||
if (! test ("*")) {
|
||||
y = read_coord ();
|
||||
}
|
||||
pt.push_back (db::Point (x, y));
|
||||
}
|
||||
|
||||
br.done ();
|
||||
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (pt.begin (), pt.end ());
|
||||
return std::make_pair (lid, db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ()));
|
||||
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside net or terminal definition (polygon or rect expected)")));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlistStandardReader::read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map<unsigned int, Net *> &id2net)
|
||||
{
|
||||
Brace br (this);
|
||||
|
||||
unsigned int id = (unsigned int) read_int ();
|
||||
std::string name;
|
||||
|
||||
if (test (skeys::name_key) || test (lkeys::name_key)) {
|
||||
Brace br_name (this);
|
||||
read_word_or_quoted (name);
|
||||
br_name.done ();
|
||||
}
|
||||
|
||||
db::Net *net = new db::Net ();
|
||||
net->set_name (name);
|
||||
circuit->add_net (net);
|
||||
|
||||
id2net.insert (std::make_pair (id, net));
|
||||
|
||||
db::connected_clusters<db::PolygonRef> &cc = l2n->net_clusters ().clusters_per_cell (circuit->cell_index ());
|
||||
db::local_cluster<db::PolygonRef> &lc = *cc.insert ();
|
||||
net->set_cluster_id (lc.id ());
|
||||
|
||||
db::Cell &cell = l2n->internal_layout ()->cell (circuit->cell_index ());
|
||||
|
||||
while (br) {
|
||||
std::pair<unsigned int, db::PolygonRef> pr = read_geometry (l2n);
|
||||
lc.add (pr.second, pr.first);
|
||||
cell.shapes (pr.first).insert (pr.second);
|
||||
}
|
||||
|
||||
br.done ();
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlistStandardReader::read_pin (db::LayoutToNetlist * /*l2n*/, db::Circuit *circuit, std::map<unsigned int, Net *> &id2net)
|
||||
{
|
||||
Brace br (this);
|
||||
std::string name;
|
||||
read_word_or_quoted (name);
|
||||
unsigned int netid = (unsigned int) read_int ();
|
||||
br.done ();
|
||||
|
||||
const db::Pin &pin = circuit->add_pin (name);
|
||||
|
||||
db::Net *net = id2net [netid];
|
||||
if (!net) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid));
|
||||
}
|
||||
|
||||
circuit->connect_pin (pin.id (), net);
|
||||
}
|
||||
|
||||
db::CellInstArray
|
||||
LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs, std::map<unsigned int, Net *> &id2net)
|
||||
{
|
||||
Brace br (this);
|
||||
|
||||
std::string name;
|
||||
read_word_or_quoted (name);
|
||||
|
||||
std::string dmname;
|
||||
read_word_or_quoted (dmname);
|
||||
|
||||
db::DeviceAbstract *dm = 0;
|
||||
for (db::Netlist::device_abstract_iterator i = l2n->netlist ()->begin_device_abstracts (); i != l2n->netlist ()->end_device_abstracts (); ++i) {
|
||||
if (i->name () == dmname) {
|
||||
dm = i.operator-> ();
|
||||
}
|
||||
}
|
||||
|
||||
if (! dm) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid device abstract name: ")) + dmname);
|
||||
}
|
||||
|
||||
db::Device *device = new db::Device ();
|
||||
device->set_device_class (const_cast<db::DeviceClass *> (dm->device_class ()));
|
||||
device->set_device_abstract (dm);
|
||||
device->set_name (name);
|
||||
circuit->add_device (device);
|
||||
|
||||
db::Coord x = 0, y = 0;
|
||||
|
||||
while (br) {
|
||||
|
||||
if (test (skeys::location_key) || test (lkeys::location_key)) {
|
||||
|
||||
Brace br2 (this);
|
||||
x = read_coord ();
|
||||
y = read_coord ();
|
||||
br2.done ();
|
||||
|
||||
} else if (test (skeys::terminal_key) || test (lkeys::terminal_key)) {
|
||||
|
||||
Brace br2 (this);
|
||||
std::string tname;
|
||||
read_word_or_quoted (tname);
|
||||
unsigned int netid = (unsigned int) read_int ();
|
||||
br2.done ();
|
||||
|
||||
size_t tid = std::numeric_limits<size_t>::max ();
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = dm->device_class ()->terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
|
||||
if (t->name () == tname) {
|
||||
tid = t->id ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tid == std::numeric_limits<size_t>::max ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + tname + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ());
|
||||
}
|
||||
|
||||
db::Net *net = id2net [netid];
|
||||
if (!net) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid));
|
||||
}
|
||||
|
||||
device->connect_terminal (tid, net);
|
||||
refs.push_back (Connections (net->cluster_id (), dm->cluster_id_for_terminal (tid)));
|
||||
|
||||
} else if (test (skeys::param_key) || test (lkeys::param_key)) {
|
||||
|
||||
Brace br2 (this);
|
||||
std::string pname;
|
||||
read_word_or_quoted (pname);
|
||||
double value = read_double ();
|
||||
br2.done ();
|
||||
|
||||
size_t pid = std::numeric_limits<size_t>::max ();
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = dm->device_class ()->parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
|
||||
if (p->name () == pname) {
|
||||
pid = p->id ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no parameter with this name exists, create one
|
||||
if (pid == std::numeric_limits<size_t>::max ()) {
|
||||
// TODO: this should only happen for generic devices
|
||||
db::DeviceClass *dc = const_cast<db::DeviceClass *> (dm->device_class ());
|
||||
pid = dc->add_parameter_definition (db::DeviceParameterDefinition (pname, std::string ())).id ();
|
||||
}
|
||||
|
||||
device->set_parameter_value (pid, value);
|
||||
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device definition (location, param or terminal expected)")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
double dbu = l2n->internal_layout ()->dbu ();
|
||||
device->set_position (db::DPoint (dbu * x, dbu * y));
|
||||
|
||||
br.done ();
|
||||
|
||||
// make device cell instance
|
||||
db::CellInstArray inst (db::CellInst (dm->cell_index ()), db::Trans (db::Vector (x, y)));
|
||||
db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ());
|
||||
ccell.insert (inst);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
db::CellInstArray
|
||||
LayoutToNetlistStandardReader::read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs, std::map<unsigned int, Net *> &id2net)
|
||||
{
|
||||
Brace br (this);
|
||||
|
||||
std::string name;
|
||||
read_word_or_quoted (name);
|
||||
|
||||
std::string xname;
|
||||
read_word_or_quoted (xname);
|
||||
|
||||
db::Circuit *circuit_ref = l2n->netlist ()->circuit_by_name (xname);
|
||||
if (! circuit_ref) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid device circuit name: ")) + xname);
|
||||
}
|
||||
|
||||
db::SubCircuit *subcircuit = new db::SubCircuit (circuit_ref);
|
||||
subcircuit->set_name (name);
|
||||
circuit->add_subcircuit (subcircuit);
|
||||
|
||||
db::Coord x = 0, y = 0;
|
||||
bool mirror = false;
|
||||
double angle = 0;
|
||||
double mag = 1.0;
|
||||
|
||||
db::InstElement ie;
|
||||
bool inst_made = false;
|
||||
|
||||
while (br) {
|
||||
|
||||
if (test (skeys::location_key) || test (lkeys::location_key)) {
|
||||
|
||||
Brace br2 (this);
|
||||
x = read_coord ();
|
||||
y = read_coord ();
|
||||
br2.done ();
|
||||
|
||||
if (inst_made) {
|
||||
throw tl::Exception (tl::to_string (tr ("location key must come before pin key in subcircuit definition")));
|
||||
}
|
||||
|
||||
} else if (test (skeys::rotation_key) || test (lkeys::rotation_key)) {
|
||||
|
||||
Brace br2 (this);
|
||||
angle = read_double ();
|
||||
br2.done ();
|
||||
|
||||
if (inst_made) {
|
||||
throw tl::Exception (tl::to_string (tr ("rotation key must come before pin key in subcircuit definition")));
|
||||
}
|
||||
|
||||
} else if (test (skeys::mirror_key) || test (lkeys::mirror_key)) {
|
||||
|
||||
mirror = true;
|
||||
if (inst_made) {
|
||||
throw tl::Exception (tl::to_string (tr ("mirror key must come before pin key in subcircuit definition")));
|
||||
}
|
||||
|
||||
} else if (test (skeys::scale_key) || test (lkeys::scale_key)) {
|
||||
|
||||
Brace br2 (this);
|
||||
mag = read_double ();
|
||||
br2.done ();
|
||||
|
||||
if (inst_made) {
|
||||
throw tl::Exception (tl::to_string (tr ("scale key must come before pin key in subcircuit definition")));
|
||||
}
|
||||
|
||||
} else if (test (skeys::pin_key) || test (lkeys::pin_key)) {
|
||||
|
||||
Brace br2 (this);
|
||||
std::string pname;
|
||||
read_word_or_quoted (pname);
|
||||
unsigned int netid = (unsigned int) read_int ();
|
||||
br2.done ();
|
||||
|
||||
const db::Pin *sc_pin = circuit_ref->pin_by_name (pname);
|
||||
if (! sc_pin) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid pin name: ")) + pname + tl::to_string (tr (" for circuit: ")) + circuit_ref->name ());
|
||||
}
|
||||
|
||||
db::Net *net = id2net [netid];
|
||||
if (!net) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid));
|
||||
}
|
||||
|
||||
subcircuit->connect_pin (sc_pin->id (), net);
|
||||
db::Net *sc_net = circuit_ref->net_for_pin (sc_pin->id ());
|
||||
if (sc_net) {
|
||||
refs.push_back (Connections (net->cluster_id (), sc_net->cluster_id ()));
|
||||
}
|
||||
|
||||
} else {
|
||||
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside subcircuit definition (location, rotation, mirror, scale or pin expected)")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
br.done ();
|
||||
|
||||
double dbu = l2n->internal_layout ()->dbu ();
|
||||
subcircuit->set_trans (db::DCplxTrans (mag, angle, mirror, db::DVector (dbu * x, dbu * y)));
|
||||
|
||||
db::CellInstArray inst (db::CellInst (circuit_ref->cell_index ()), db::ICplxTrans (mag, angle, mirror, db::Vector (x, y)));
|
||||
db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ());
|
||||
ccell.insert (inst);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void
|
||||
LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc)
|
||||
{
|
||||
Brace br (this);
|
||||
|
||||
std::string name;
|
||||
read_word_or_quoted (name);
|
||||
|
||||
size_t tid = std::numeric_limits<size_t>::max ();
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = dm->device_class ()->terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
|
||||
if (t->name () == name) {
|
||||
tid = t->id ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create a terminal unless one with this name already exists
|
||||
if (tid == std::numeric_limits<size_t>::max ()) {
|
||||
if (! dc) {
|
||||
throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + name + tl::to_string (tr (" for device class: ")) + dm->device_class ()->name ());
|
||||
}
|
||||
db::DeviceTerminalDefinition new_td (name, std::string ());
|
||||
tid = dc->add_terminal_definition (new_td).id ();
|
||||
}
|
||||
|
||||
db::connected_clusters<db::PolygonRef> &cc = l2n->net_clusters ().clusters_per_cell (dm->cell_index ());
|
||||
db::local_cluster<db::PolygonRef> &lc = *cc.insert ();
|
||||
dm->set_cluster_id_for_terminal (tid, lc.id ());
|
||||
|
||||
db::Cell &cell = l2n->internal_layout ()->cell (dm->cell_index ());
|
||||
|
||||
while (br) {
|
||||
std::pair<unsigned int, db::PolygonRef> pr = read_geometry (l2n);
|
||||
lc.add (pr.second, pr.first);
|
||||
cell.shapes (pr.first).insert (pr.second);
|
||||
}
|
||||
|
||||
br.done ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbLayoutToNetlistReader
|
||||
#define HDR_dbLayoutToNetlistReader
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbPolygon.h"
|
||||
#include "dbCell.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
namespace l2n_std_reader {
|
||||
class Layers;
|
||||
class Brace;
|
||||
}
|
||||
|
||||
class LayoutToNetlist;
|
||||
class Circuit;
|
||||
class Cell;
|
||||
class DeviceAbstract;
|
||||
class DeviceClass;
|
||||
class Net;
|
||||
class Region;
|
||||
|
||||
/**
|
||||
* @brief The base class for a LayoutToNetlist writer
|
||||
*/
|
||||
class DB_PUBLIC LayoutToNetlistReaderBase
|
||||
{
|
||||
public:
|
||||
LayoutToNetlistReaderBase () { }
|
||||
virtual ~LayoutToNetlistReaderBase () { }
|
||||
|
||||
virtual void read (db::LayoutToNetlist *l2n) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The standard writer
|
||||
*/
|
||||
class DB_PUBLIC LayoutToNetlistStandardReader
|
||||
: public LayoutToNetlistReaderBase
|
||||
{
|
||||
public:
|
||||
LayoutToNetlistStandardReader (tl::InputStream &stream);
|
||||
|
||||
void read (db::LayoutToNetlist *l2n);
|
||||
|
||||
private:
|
||||
friend class l2n_std_reader::Brace;
|
||||
typedef l2n_std_reader::Brace Brace;
|
||||
typedef l2n_std_reader::Layers Layers;
|
||||
|
||||
struct Connections
|
||||
{
|
||||
Connections (size_t _from_cluster, size_t _to_cluster)
|
||||
: from_cluster (_from_cluster), to_cluster (_to_cluster)
|
||||
{ }
|
||||
|
||||
size_t from_cluster, to_cluster;
|
||||
};
|
||||
|
||||
tl::TextInputStream m_stream;
|
||||
std::string m_path;
|
||||
std::string m_line;
|
||||
tl::Extractor m_ex;
|
||||
|
||||
void do_read (db::LayoutToNetlist *l2n);
|
||||
|
||||
bool test (const std::string &token);
|
||||
void expect (const std::string &token);
|
||||
void read_word_or_quoted(std::string &s);
|
||||
int read_int ();
|
||||
db::Coord read_coord ();
|
||||
double read_double ();
|
||||
bool at_end ();
|
||||
void skip ();
|
||||
|
||||
void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map<unsigned int, db::Net *> &id2net);
|
||||
void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map<unsigned int, db::Net *> &id2net);
|
||||
db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs, std::map<unsigned int, db::Net *> &id2net);
|
||||
db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs, std::map<unsigned int, db::Net *> &id2net);
|
||||
void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc);
|
||||
std::pair<unsigned int, db::PolygonRef> read_geometry (db::LayoutToNetlist *l2n);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,478 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbLayoutToNetlistWriter.h"
|
||||
#include "dbLayoutToNetlist.h"
|
||||
#include "dbLayoutToNetlistFormatDefs.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
namespace l2n_std_format
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// std_writer_impl<Keys> implementation
|
||||
|
||||
template <class Keys>
|
||||
class std_writer_impl
|
||||
{
|
||||
public:
|
||||
std_writer_impl (tl::OutputStream &stream);
|
||||
|
||||
void write (const db::LayoutToNetlist *l2n);
|
||||
|
||||
private:
|
||||
tl::OutputStream *mp_stream;
|
||||
|
||||
void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit);
|
||||
void write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id);
|
||||
void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id);
|
||||
void write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map<const Net *, unsigned int> &net2id);
|
||||
void write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract);
|
||||
void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname);
|
||||
};
|
||||
|
||||
static const std::string endl ("\n");
|
||||
static const std::string indent1 (" ");
|
||||
static const std::string indent2 (" ");
|
||||
|
||||
template <class Keys>
|
||||
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream)
|
||||
: mp_stream (&stream)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
static std::string name_for_layer (const db::LayoutToNetlist *l2n, unsigned int l)
|
||||
{
|
||||
std::string n = l2n->name (l);
|
||||
if (n.empty ()) {
|
||||
n = "L" + tl::to_string (l);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n)
|
||||
{
|
||||
bool any = false;
|
||||
|
||||
const int version = 0;
|
||||
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
const db::Netlist *nl = l2n->netlist ();
|
||||
|
||||
*mp_stream << "#%l2n-klayout" << endl;
|
||||
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << "# General section" << endl << endl;
|
||||
}
|
||||
|
||||
if (version > 0) {
|
||||
*mp_stream << Keys::version_key << "(" << version << ")" << endl;
|
||||
}
|
||||
*mp_stream << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl;
|
||||
*mp_stream << Keys::unit_key << "(" << ly->dbu () << ")" << endl;
|
||||
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << "# Layer section" << endl;
|
||||
*mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
|
||||
}
|
||||
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << "# Mask layers" << endl;
|
||||
}
|
||||
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
|
||||
*mp_stream << Keys::layer_key << "(" << name_for_layer (l2n, *l) << ")" << endl;
|
||||
}
|
||||
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << "# Mask layer connectivity" << endl;
|
||||
}
|
||||
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
|
||||
|
||||
db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l);
|
||||
db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l);
|
||||
if (cb != ce) {
|
||||
*mp_stream << Keys::connect_key << "(" << name_for_layer (l2n, *l);
|
||||
for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
|
||||
*mp_stream << " " << name_for_layer (l2n, *c);
|
||||
}
|
||||
*mp_stream << ")" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
any = false;
|
||||
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
|
||||
|
||||
db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l);
|
||||
db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l);
|
||||
if (gb != ge) {
|
||||
if (! any) {
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << "# Global nets and connectivity" << endl;
|
||||
}
|
||||
any = true;
|
||||
}
|
||||
*mp_stream << Keys::global_key << "(" << name_for_layer (l2n, *l);
|
||||
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
|
||||
*mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g));
|
||||
}
|
||||
*mp_stream << ")" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (nl->begin_device_abstracts () != nl->end_device_abstracts () && ! Keys::is_short ()) {
|
||||
*mp_stream << endl << "# Device abstracts section" << endl;
|
||||
*mp_stream << "# Device abstracts list the pin shapes of the devices." << endl;
|
||||
}
|
||||
for (db::Netlist::const_abstract_model_iterator m = nl->begin_device_abstracts (); m != nl->end_device_abstracts (); ++m) {
|
||||
if (m->device_class ()) {
|
||||
*mp_stream << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
|
||||
write (l2n, *m);
|
||||
*mp_stream << ")" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << "# Circuit section" << endl;
|
||||
*mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl;
|
||||
}
|
||||
for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) {
|
||||
const db::Circuit *x = *i;
|
||||
*mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
|
||||
write (l2n, *x);
|
||||
*mp_stream << ")" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit)
|
||||
{
|
||||
std::map<const db::Net *, unsigned int> net2id;
|
||||
unsigned int id = 0;
|
||||
|
||||
if (circuit.begin_nets () != circuit.end_nets ()) {
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << indent1 << "# Nets with their geometries" << endl;
|
||||
}
|
||||
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
|
||||
net2id.insert (std::make_pair (n.operator-> (), ++id));
|
||||
write (l2n, *n, id);
|
||||
}
|
||||
}
|
||||
|
||||
if (circuit.begin_pins () != circuit.end_pins ()) {
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << indent1 << "# Outgoing pins and their connections to nets" << endl;
|
||||
}
|
||||
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
|
||||
const db::Net *net = circuit.net_for_pin (p->id ());
|
||||
if (net) {
|
||||
*mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (circuit.begin_devices () != circuit.end_devices ()) {
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << indent1 << "# Devices and their connections" << endl;
|
||||
}
|
||||
for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) {
|
||||
write (l2n, *d, net2id);
|
||||
}
|
||||
}
|
||||
|
||||
if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) {
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl;
|
||||
}
|
||||
for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) {
|
||||
write (l2n, *x, net2id);
|
||||
}
|
||||
}
|
||||
|
||||
if (! Keys::is_short ()) {
|
||||
*mp_stream << endl;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class Tr>
|
||||
void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr)
|
||||
{
|
||||
db::Coord x = 0, y = 0;
|
||||
bool first = true;
|
||||
for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) {
|
||||
|
||||
typename T::point_type pt = tr * *c;
|
||||
|
||||
stream << " ";
|
||||
|
||||
if (first || pt.x () != x) {
|
||||
stream << pt.x ();
|
||||
} else {
|
||||
stream << "*";
|
||||
}
|
||||
|
||||
stream << " ";
|
||||
|
||||
if (first || pt.y () != y) {
|
||||
stream << pt.y ();
|
||||
} else {
|
||||
stream << "*";
|
||||
}
|
||||
|
||||
first = false;
|
||||
x = pt.x (); y = pt.y ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname)
|
||||
{
|
||||
db::ICplxTrans t = tr * db::ICplxTrans (s->trans ());
|
||||
|
||||
const db::Polygon &poly = s->obj ();
|
||||
if (poly.is_box ()) {
|
||||
|
||||
db::Box box = t * poly.box ();
|
||||
*mp_stream << Keys::rect_key << "(" << lname;
|
||||
*mp_stream << " " << box.left () << " " << box.bottom ();
|
||||
*mp_stream << " " << box.right () << " " << box.top ();
|
||||
*mp_stream << ")";
|
||||
|
||||
} else {
|
||||
|
||||
*mp_stream << Keys::polygon_key << "(" << lname;
|
||||
if (poly.holes () > 0) {
|
||||
db::SimplePolygon sp (poly);
|
||||
write_points (*mp_stream, sp, t);
|
||||
} else {
|
||||
write_points (*mp_stream, poly, t);
|
||||
}
|
||||
*mp_stream << ")";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id)
|
||||
{
|
||||
if (! l2n->netlist ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done")));
|
||||
}
|
||||
|
||||
const db::hier_clusters<db::PolygonRef> &clusters = l2n->net_clusters ();
|
||||
const db::Circuit *circuit = net.circuit ();
|
||||
const db::Connectivity &conn = l2n->connectivity ();
|
||||
|
||||
bool any = false;
|
||||
|
||||
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
|
||||
|
||||
db::cell_index_type cci = circuit->cell_index ();
|
||||
db::cell_index_type prev_ci = cci;
|
||||
|
||||
for (db::recursive_cluster_shape_iterator<db::PolygonRef> si (clusters, *l, cci, net.cluster_id ()); ! si.at_end (); ) {
|
||||
|
||||
// NOTE: we don't recursive into circuits which will later be output. However, as circuits may
|
||||
// vanish in "purge" but the clusters will still be there we need to recursive into clusters from
|
||||
// unknown cells.
|
||||
db::cell_index_type ci = si.cell_index ();
|
||||
if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_abstract_by_cell_index (ci))) {
|
||||
|
||||
si.skip_cell ();
|
||||
|
||||
} else {
|
||||
|
||||
if (! any) {
|
||||
*mp_stream << indent1 << Keys::net_key << "(" << id;
|
||||
if (! net.name ().empty ()) {
|
||||
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
|
||||
}
|
||||
*mp_stream << endl;
|
||||
any = true;
|
||||
}
|
||||
|
||||
*mp_stream << indent2;
|
||||
write (si.operator-> (), si.trans (), name_for_layer (l2n, *l));
|
||||
*mp_stream << endl;
|
||||
|
||||
prev_ci = ci;
|
||||
|
||||
++si;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (any) {
|
||||
*mp_stream << indent1 << ")" << endl;
|
||||
} else {
|
||||
|
||||
*mp_stream << indent1 << Keys::net_key << "(" << id;
|
||||
if (! net.name ().empty ()) {
|
||||
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
|
||||
}
|
||||
*mp_stream << ")" << endl;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id)
|
||||
{
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
double dbu = ly->dbu ();
|
||||
|
||||
*mp_stream << indent1 << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ());
|
||||
*mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ());
|
||||
|
||||
const db::DCplxTrans &tr = subcircuit.trans ();
|
||||
if (tr.is_mag ()) {
|
||||
*mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")";
|
||||
}
|
||||
if (tr.is_mirror ()) {
|
||||
*mp_stream << " " << Keys::mirror_key;
|
||||
}
|
||||
if (fabs (tr.angle ()) > 1e-6) {
|
||||
*mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")";
|
||||
}
|
||||
*mp_stream << " " << Keys::location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")";
|
||||
|
||||
// each pin in one line for more than a few pins
|
||||
bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1);
|
||||
|
||||
if (separate_lines) {
|
||||
*mp_stream << endl;
|
||||
}
|
||||
|
||||
for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) {
|
||||
const db::Net *net = subcircuit.net_for_pin (p->id ());
|
||||
if (net) {
|
||||
if (separate_lines) {
|
||||
*mp_stream << indent2;
|
||||
} else {
|
||||
*mp_stream << " ";
|
||||
}
|
||||
*mp_stream << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")";
|
||||
if (separate_lines) {
|
||||
*mp_stream << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (separate_lines) {
|
||||
*mp_stream << indent1;
|
||||
}
|
||||
|
||||
*mp_stream << ")" << endl;
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract)
|
||||
{
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = device_abstract.device_class ()->terminal_definitions ();
|
||||
|
||||
const db::hier_clusters<db::PolygonRef> &clusters = l2n->net_clusters ();
|
||||
const db::Connectivity &conn = l2n->connectivity ();
|
||||
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
|
||||
|
||||
*mp_stream << indent1 << Keys::terminal_key << "(" << t->name () << endl;
|
||||
|
||||
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
|
||||
|
||||
const db::local_cluster<db::PolygonRef> &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (device_abstract.cluster_id_for_terminal (t->id ()));
|
||||
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) {
|
||||
|
||||
*mp_stream << indent2;
|
||||
write (s.operator-> (), db::ICplxTrans (), name_for_layer (l2n, *l));
|
||||
*mp_stream << endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*mp_stream << indent1 << ")" << endl;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class Keys>
|
||||
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map<const Net *, unsigned int> &net2id)
|
||||
{
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
double dbu = ly->dbu ();
|
||||
|
||||
*mp_stream << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ());
|
||||
|
||||
tl_assert (device.device_abstract () != 0);
|
||||
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
|
||||
|
||||
*mp_stream << indent2 << Keys::location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl;
|
||||
|
||||
const std::vector<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
|
||||
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
|
||||
*mp_stream << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl;
|
||||
}
|
||||
|
||||
const std::vector<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
|
||||
for (std::vector<DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
|
||||
const db::Net *net = device.net_for_terminal (i->id ());
|
||||
if (net) {
|
||||
*mp_stream << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
*mp_stream << indent1 << ")" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// LayoutToNetlistStandardWriter implementation
|
||||
|
||||
LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version)
|
||||
: mp_stream (&stream), m_short_version (short_version)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
|
||||
{
|
||||
if (m_short_version) {
|
||||
l2n_std_format::std_writer_impl<l2n_std_format::keys<true> > writer (*mp_stream);
|
||||
writer.write (l2n);
|
||||
} else {
|
||||
l2n_std_format::std_writer_impl<l2n_std_format::keys<false> > writer (*mp_stream);
|
||||
writer.write (l2n);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbLayoutToNetlistWriter
|
||||
#define HDR_dbLayoutToNetlistWriter
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class LayoutToNetlist;
|
||||
|
||||
/**
|
||||
* @brief The base class for a LayoutToNetlist writer
|
||||
*/
|
||||
class DB_PUBLIC LayoutToNetlistWriterBase
|
||||
{
|
||||
public:
|
||||
LayoutToNetlistWriterBase () { }
|
||||
virtual ~LayoutToNetlistWriterBase () { }
|
||||
|
||||
virtual void write (const db::LayoutToNetlist *l2n) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The standard writer
|
||||
*/
|
||||
class DB_PUBLIC LayoutToNetlistStandardWriter
|
||||
: public LayoutToNetlistWriterBase
|
||||
{
|
||||
public:
|
||||
LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version);
|
||||
|
||||
void write (const db::LayoutToNetlist *l2n);
|
||||
|
||||
private:
|
||||
tl::OutputStream *mp_stream;
|
||||
bool m_short_version;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -259,7 +259,8 @@ copy_or_propagate_shapes (db::Layout &target,
|
|||
db::cell_index_type source_parent_cell_index,
|
||||
unsigned int target_layer, unsigned int source_layer,
|
||||
const std::set<db::cell_index_type> &all_cells_to_copy,
|
||||
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping)
|
||||
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
|
||||
const ShapesTransformer *transformer)
|
||||
{
|
||||
const db::Cell &source_cell = source.cell (source_cell_index);
|
||||
const db::Cell &source_parent_cell = source.cell (source_parent_cell_index);
|
||||
|
|
@ -273,7 +274,7 @@ copy_or_propagate_shapes (db::Layout &target,
|
|||
const db::CellInstArray &cell_inst = p->child_inst ().cell_inst ();
|
||||
for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) {
|
||||
db::ICplxTrans t = db::ICplxTrans (cell_inst.complex_trans (*a)) * propagate_trans;
|
||||
copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping);
|
||||
copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping, transformer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,8 +283,7 @@ copy_or_propagate_shapes (db::Layout &target,
|
|||
} else if (cm->second != DropCell) {
|
||||
|
||||
db::Cell &target_cell = target.cell (cm->second);
|
||||
target_cell.shapes (target_layer).insert_transformed (source_cell.shapes (source_layer), trans * propagate_trans, pm);
|
||||
|
||||
transformer->insert_transformed (target_cell.shapes (target_layer), source_cell.shapes (source_layer), trans * propagate_trans, pm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,7 +294,9 @@ copy_or_move_shapes (db::Layout &target,
|
|||
const std::vector<db::cell_index_type> &source_cells,
|
||||
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping,
|
||||
bool move)
|
||||
const ShapesTransformer *transformer,
|
||||
bool move
|
||||
)
|
||||
{
|
||||
// collect all called cells and all top level cells
|
||||
std::set<db::cell_index_type> all_top_level_cells;
|
||||
|
|
@ -311,7 +313,7 @@ copy_or_move_shapes (db::Layout &target,
|
|||
for (std::set<db::cell_index_type>::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) {
|
||||
for (std::map<unsigned int, unsigned int>::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) {
|
||||
++progress;
|
||||
copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping);
|
||||
copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping, transformer);
|
||||
if (move) {
|
||||
source.cell (*c).shapes (lm->first).clear ();
|
||||
}
|
||||
|
|
@ -319,15 +321,33 @@ copy_or_move_shapes (db::Layout &target,
|
|||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class StandardShapesTransformer
|
||||
: public ShapesTransformer
|
||||
{
|
||||
public:
|
||||
void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const
|
||||
{
|
||||
into.insert_transformed (from, trans, pm);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
copy_shapes (db::Layout &target,
|
||||
const db::Layout &source,
|
||||
const db::ICplxTrans &trans,
|
||||
const std::vector<db::cell_index_type> &source_cells,
|
||||
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping)
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping,
|
||||
const ShapesTransformer *transformer)
|
||||
{
|
||||
copy_or_move_shapes (target, const_cast<db::Layout &> (source), trans, source_cells, cell_mapping, layer_mapping, false);
|
||||
StandardShapesTransformer st;
|
||||
if (! transformer) {
|
||||
transformer = &st;
|
||||
}
|
||||
copy_or_move_shapes (target, const_cast<db::Layout &> (source), trans, source_cells, cell_mapping, layer_mapping, transformer, false);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -336,9 +356,14 @@ move_shapes (db::Layout &target,
|
|||
const db::ICplxTrans &trans,
|
||||
const std::vector<db::cell_index_type> &source_cells,
|
||||
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping)
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping,
|
||||
const ShapesTransformer *transformer)
|
||||
{
|
||||
copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, true);
|
||||
StandardShapesTransformer st;
|
||||
if (! transformer) {
|
||||
transformer = &st;
|
||||
}
|
||||
copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, transformer, true);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -140,6 +140,21 @@ merge_layouts (db::Layout &target, const db::Layout &source, const db::ICplxTran
|
|||
const std::map<unsigned int, unsigned int> &layer_mapping,
|
||||
std::map<db::cell_index_type, db::cell_index_type> *final_cell_mapping = 0);
|
||||
|
||||
/**
|
||||
* @brief An interface for the shape inserter
|
||||
*
|
||||
* This interface is used by copy_shapes and move_shapes to insert
|
||||
* a shape collection into a another one. By reimplementing this interface,
|
||||
* more shape transformations can be provided.
|
||||
*/
|
||||
class DB_PUBLIC ShapesTransformer
|
||||
{
|
||||
public:
|
||||
ShapesTransformer () { }
|
||||
virtual ~ShapesTransformer () { }
|
||||
virtual void insert_transformed (db::Shapes &into, const db::Shapes &from, const db::ICplxTrans &trans, db::PropertyMapper &pm) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Copy shapes from one layout to another
|
||||
*
|
||||
|
|
@ -155,7 +170,8 @@ copy_shapes (db::Layout &target,
|
|||
const db::ICplxTrans &trans,
|
||||
const std::vector<db::cell_index_type> &source_cells,
|
||||
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping);
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping,
|
||||
const ShapesTransformer *transformer = 0);
|
||||
|
||||
/**
|
||||
* @brief Move shapes from one layout to another
|
||||
|
|
@ -172,7 +188,8 @@ move_shapes (db::Layout &target,
|
|||
const db::ICplxTrans &trans,
|
||||
const std::vector<db::cell_index_type> &source_cells,
|
||||
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping);
|
||||
const std::map<unsigned int, unsigned int> &layer_mapping,
|
||||
const ShapesTransformer *transformer = 0);
|
||||
|
||||
/**
|
||||
* @brief Find an example cell instance from a child to a top cell
|
||||
|
|
|
|||
|
|
@ -0,0 +1,310 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbHierProcessor.h"
|
||||
#include "dbBoxScanner.h"
|
||||
#include "dbRecursiveShapeIterator.h"
|
||||
#include "dbBoxConvert.h"
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbLocalOperationUtils.h"
|
||||
#include "dbEdgeBoolean.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlInternational.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// BoolAndOrNotLocalOperation implementation
|
||||
|
||||
BoolAndOrNotLocalOperation::BoolAndOrNotLocalOperation (bool is_and)
|
||||
: m_is_and (is_and)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
local_operation<db::PolygonRef, db::PolygonRef, db::PolygonRef>::on_empty_intruder_mode
|
||||
BoolAndOrNotLocalOperation::on_empty_intruder_hint () const
|
||||
{
|
||||
return m_is_and ? local_operation::Drop : local_operation::Copy;
|
||||
}
|
||||
|
||||
std::string
|
||||
BoolAndOrNotLocalOperation::description () const
|
||||
{
|
||||
return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation"));
|
||||
}
|
||||
|
||||
void
|
||||
BoolAndOrNotLocalOperation::compute_local (db::Layout *layout, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::unordered_set<db::PolygonRef> &result, size_t max_vertex_count, double area_ratio) const
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
size_t p1 = 0, p2 = 1;
|
||||
|
||||
std::set<db::PolygonRef> others;
|
||||
for (shape_interactions<db::PolygonRef, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::PolygonRef, db::PolygonRef>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j));
|
||||
}
|
||||
}
|
||||
|
||||
for (shape_interactions<db::PolygonRef, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
const db::PolygonRef &subject = interactions.subject_shape (i->first);
|
||||
if (others.find (subject) != others.end ()) {
|
||||
if (m_is_and) {
|
||||
result.insert (subject);
|
||||
}
|
||||
} else if (i->second.empty ()) {
|
||||
// shortcut (not: keep, and: drop)
|
||||
if (! m_is_and) {
|
||||
result.insert (subject);
|
||||
}
|
||||
} else {
|
||||
for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) {
|
||||
ep.insert (*e, p1);
|
||||
}
|
||||
p1 += 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! others.empty () || p1 > 0) {
|
||||
|
||||
for (std::set<db::PolygonRef>::const_iterator o = others.begin (); o != others.end (); ++o) {
|
||||
for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end(); ++e) {
|
||||
ep.insert (*e, p2);
|
||||
}
|
||||
p2 += 2;
|
||||
}
|
||||
|
||||
db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB);
|
||||
db::PolygonRefGenerator pr (layout, result);
|
||||
db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count);
|
||||
db::PolygonGenerator pg (splitter, true, true);
|
||||
ep.set_base_verbosity (50);
|
||||
ep.process (pg, op);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count)
|
||||
: m_wrap_count (wrap_count)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void SelfOverlapMergeLocalOperation::compute_local (db::Layout *layout, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::unordered_set<db::PolygonRef> &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const
|
||||
{
|
||||
if (m_wrap_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
size_t p1 = 0, p2 = 1;
|
||||
std::set<unsigned int> seen;
|
||||
|
||||
for (shape_interactions<db::PolygonRef, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
if (seen.find (i->first) == seen.end ()) {
|
||||
seen.insert (i->first);
|
||||
const db::PolygonRef &subject = interactions.subject_shape (i->first);
|
||||
for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) {
|
||||
ep.insert (*e, p1);
|
||||
}
|
||||
p1 += 2;
|
||||
}
|
||||
|
||||
for (db::shape_interactions<db::PolygonRef, db::PolygonRef>::iterator2 o = i->second.begin (); o != i->second.end (); ++o) {
|
||||
// don't take the same (really the same, not an identical one) shape twice - the interaction
|
||||
// set does not take care to list just one copy of the same item on the intruder side.
|
||||
if (seen.find (*o) == seen.end ()) {
|
||||
seen.insert (*o);
|
||||
const db::PolygonRef &intruder = interactions.intruder_shape (*o);
|
||||
for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) {
|
||||
ep.insert (*e, p2);
|
||||
}
|
||||
p2 += 2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
db::MergeOp op (m_wrap_count - 1);
|
||||
db::PolygonRefGenerator pr (layout, result);
|
||||
db::PolygonGenerator pg (pr, true, true);
|
||||
ep.set_base_verbosity (50);
|
||||
ep.process (pg, op);
|
||||
}
|
||||
|
||||
SelfOverlapMergeLocalOperation::on_empty_intruder_mode SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const
|
||||
{
|
||||
return m_wrap_count > 1 ? local_operation::Drop : local_operation::Copy;
|
||||
}
|
||||
|
||||
std::string SelfOverlapMergeLocalOperation::description () const
|
||||
{
|
||||
return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// EdgeBoolAndOrNotLocalOperation implementation
|
||||
|
||||
EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (bool is_and)
|
||||
: m_is_and (is_and)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
local_operation<db::Edge, db::Edge, db::Edge>::on_empty_intruder_mode
|
||||
EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const
|
||||
{
|
||||
return m_is_and ? local_operation::Drop : local_operation::Copy;
|
||||
}
|
||||
|
||||
std::string
|
||||
EdgeBoolAndOrNotLocalOperation::description () const
|
||||
{
|
||||
return m_is_and ? tl::to_string (tr ("Edge AND operation")) : tl::to_string (tr ("Edge NOT operation"));
|
||||
}
|
||||
|
||||
void
|
||||
EdgeBoolAndOrNotLocalOperation::compute_local (db::Layout * /*layout*/, const shape_interactions<db::Edge, db::Edge> &interactions, std::unordered_set<db::Edge> &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const
|
||||
{
|
||||
EdgeBooleanClusterCollector<std::unordered_set<db::Edge> > cluster_collector (&result, m_is_and ? EdgeAnd : EdgeNot);
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner;
|
||||
|
||||
std::set<db::Edge> others;
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j));
|
||||
}
|
||||
}
|
||||
|
||||
bool any_subject = false;
|
||||
|
||||
for (shape_interactions<db::Edge, db::Edge>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
if (others.find (subject) != others.end ()) {
|
||||
if (m_is_and) {
|
||||
result.insert (subject);
|
||||
}
|
||||
} else if (i->second.empty ()) {
|
||||
// shortcut (not: keep, and: drop)
|
||||
if (! m_is_and) {
|
||||
result.insert (subject);
|
||||
}
|
||||
} else {
|
||||
scanner.insert (&subject, 0);
|
||||
any_subject = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! others.empty () || any_subject) {
|
||||
|
||||
for (std::set<db::Edge>::const_iterator o = others.begin (); o != others.end (); ++o) {
|
||||
scanner.insert (o.operator-> (), 1);
|
||||
}
|
||||
|
||||
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// EdgeToPolygonLocalOperation implementation
|
||||
|
||||
EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (bool outside, bool include_borders)
|
||||
: m_outside (outside), m_include_borders (include_borders)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
local_operation<db::Edge, db::PolygonRef, db::Edge>::on_empty_intruder_mode
|
||||
EdgeToPolygonLocalOperation::on_empty_intruder_hint () const
|
||||
{
|
||||
return m_outside ? local_operation::Copy : local_operation::Drop;
|
||||
}
|
||||
|
||||
std::string
|
||||
EdgeToPolygonLocalOperation::description () const
|
||||
{
|
||||
return tl::to_string (m_outside ? tr ("Edge to polygon AND/INSIDE") : tr ("Edge to polygons NOT/OUTSIDE"));
|
||||
}
|
||||
|
||||
void
|
||||
EdgeToPolygonLocalOperation::compute_local (db::Layout * /*layout*/, const shape_interactions<db::Edge, db::PolygonRef> &interactions, std::unordered_set<db::Edge> &result, size_t /*max_vertex_count*/, double /*area_ratio*/) const
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
std::set<db::PolygonRef> others;
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator2 j = i->second.begin (); j != i->second.end (); ++j) {
|
||||
others.insert (interactions.intruder_shape (*j));
|
||||
}
|
||||
}
|
||||
|
||||
bool any_subject = false;
|
||||
|
||||
for (shape_interactions<db::Edge, db::PolygonRef>::iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
const db::Edge &subject = interactions.subject_shape (i->first);
|
||||
if (i->second.empty ()) {
|
||||
// shortcut (outside: keep, otherwise: drop)
|
||||
if (m_outside) {
|
||||
result.insert (subject);
|
||||
}
|
||||
} else {
|
||||
ep.insert (subject, 1);
|
||||
any_subject = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! others.empty () || any_subject) {
|
||||
|
||||
for (std::set<db::PolygonRef>::const_iterator o = others.begin (); o != others.end (); ++o) {
|
||||
for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) {
|
||||
ep.insert (*e, 0);
|
||||
}
|
||||
}
|
||||
|
||||
db::EdgeToEdgeSetGenerator cc (result);
|
||||
db::EdgePolygonOp op (m_outside, m_include_borders);
|
||||
ep.process (cc, op);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbLocalOperation
|
||||
#define HDR_dbLocalOperation
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
template <class TS, class TI> class shape_interactions;
|
||||
|
||||
/**
|
||||
* @brief A base class for "local operations"
|
||||
* A local operation is any operation whose result can be computed by
|
||||
* combining the results derived from individual shape pairs.
|
||||
* The shape pairs can originate from different or the same layer.
|
||||
* If the layers are different, one layer is the subject layer, the
|
||||
* other layer is the "intruder" layer. Subject shapes are always
|
||||
* considered, intruder shapes only if they interact with subject shapes.
|
||||
* This class implements the actual operation. It receives a
|
||||
* cluster of subject shapes vs. corresponding intruder shapes.
|
||||
*/
|
||||
template <class TS, class TI, class TR>
|
||||
class DB_PUBLIC local_operation
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Indicates the desired behaviour for subject shapes for which there is no intruder
|
||||
*/
|
||||
enum on_empty_intruder_mode {
|
||||
/**
|
||||
* @brief Don't imply a specific behaviour
|
||||
*/
|
||||
Ignore = 0,
|
||||
|
||||
/**
|
||||
* @brief Copy the subject shape
|
||||
*/
|
||||
Copy,
|
||||
|
||||
/**
|
||||
* @brief Drop the subject shape
|
||||
*/
|
||||
Drop
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
local_operation () { }
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~local_operation () { }
|
||||
|
||||
/**
|
||||
* @brief Computes the results from a given set of interacting shapes
|
||||
* @param layout The layout to which the shapes belong
|
||||
* @param interactions The interaction set
|
||||
* @param result The container to which the results are written
|
||||
*/
|
||||
virtual void compute_local (db::Layout *layout, const shape_interactions<TS, TI> &interactions, std::unordered_set<TR> &result, size_t max_vertex_count, double area_ratio) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Indicates the desired behaviour when a shape does not have an intruder
|
||||
*/
|
||||
virtual on_empty_intruder_mode on_empty_intruder_hint () const { return Ignore; }
|
||||
|
||||
/**
|
||||
* @brief Gets a description text for this operation
|
||||
*/
|
||||
virtual std::string description () const = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets the interaction distance
|
||||
* A distance of means the shapes must overlap in order to interact.
|
||||
*/
|
||||
virtual db::Coord dist () const { return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a boolean AND or NOT operation
|
||||
*/
|
||||
class DB_PUBLIC BoolAndOrNotLocalOperation
|
||||
: public local_operation<db::PolygonRef, db::PolygonRef, db::PolygonRef>
|
||||
{
|
||||
public:
|
||||
BoolAndOrNotLocalOperation (bool is_and);
|
||||
|
||||
virtual void compute_local (db::Layout *layout, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::unordered_set<db::PolygonRef> &result, size_t max_vertex_count, double area_ratio) const;
|
||||
virtual on_empty_intruder_mode on_empty_intruder_hint () const;
|
||||
virtual std::string description () const;
|
||||
|
||||
private:
|
||||
bool m_is_and;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a merge operation with an overlap count
|
||||
* With a given wrap_count, the result will only contains shapes where
|
||||
* the original shapes overlap at least "wrap_count" times.
|
||||
*/
|
||||
class DB_PUBLIC SelfOverlapMergeLocalOperation
|
||||
: public local_operation<db::PolygonRef, db::PolygonRef, db::PolygonRef>
|
||||
{
|
||||
public:
|
||||
SelfOverlapMergeLocalOperation (unsigned int wrap_count);
|
||||
|
||||
virtual void compute_local (db::Layout *layout, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::unordered_set<db::PolygonRef> &result, size_t max_vertex_count, double area_ratio) const;
|
||||
virtual on_empty_intruder_mode on_empty_intruder_hint () const;
|
||||
virtual std::string description () const;
|
||||
|
||||
private:
|
||||
unsigned int m_wrap_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a boolean AND or NOT operation between edges
|
||||
*/
|
||||
class DB_PUBLIC EdgeBoolAndOrNotLocalOperation
|
||||
: public local_operation<db::Edge, db::Edge, db::Edge>
|
||||
{
|
||||
public:
|
||||
EdgeBoolAndOrNotLocalOperation (bool is_and);
|
||||
|
||||
virtual void compute_local (db::Layout *layout, const shape_interactions<db::Edge, db::Edge> &interactions, std::unordered_set<db::Edge> &result, size_t max_vertex_count, double area_ratio) const;
|
||||
virtual on_empty_intruder_mode on_empty_intruder_hint () const;
|
||||
virtual std::string description () const;
|
||||
|
||||
// edge interaction distance is 1 to force overlap between edges and edge/boxes
|
||||
virtual db::Coord dist () const { return 1; }
|
||||
|
||||
private:
|
||||
bool m_is_and;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a boolean AND or NOT operation between edges and polygons (polygons as intruders)
|
||||
*
|
||||
* "AND" is implemented by "outside == false", "NOT" by "outside == true" with "include_borders == true".
|
||||
* With "include_borders == false" the operations are "INSIDE" and "OUTSIDE".
|
||||
*/
|
||||
class DB_PUBLIC EdgeToPolygonLocalOperation
|
||||
: public local_operation<db::Edge, db::PolygonRef, db::Edge>
|
||||
{
|
||||
public:
|
||||
EdgeToPolygonLocalOperation (bool outside, bool include_borders);
|
||||
|
||||
virtual void compute_local (db::Layout *layout, const shape_interactions<db::Edge, db::PolygonRef> &interactions, std::unordered_set<db::Edge> &result, size_t max_vertex_count, double area_ratio) const;
|
||||
virtual on_empty_intruder_mode on_empty_intruder_hint () const;
|
||||
virtual std::string description () const;
|
||||
|
||||
// edge interaction distance is 1 to force overlap between edges and edge/boxes
|
||||
virtual db::Coord dist () const { return m_include_borders ? 1 : 0; }
|
||||
|
||||
private:
|
||||
bool m_outside;
|
||||
bool m_include_borders;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbLocalOperationUtils.h"
|
||||
#include "dbPolygonTools.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// class PolygonRefGenerator
|
||||
|
||||
PolygonRefGenerator::PolygonRefGenerator (db::Layout *layout, std::unordered_set<db::PolygonRef> &polyrefs)
|
||||
: PolygonSink (), mp_layout (layout), mp_polyrefs (&polyrefs)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void PolygonRefGenerator::put (const db::Polygon &polygon)
|
||||
{
|
||||
tl::MutexLocker locker (&mp_layout->lock ());
|
||||
mp_polyrefs->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// class EdgeToEdgeSetGenerator
|
||||
|
||||
EdgeToEdgeSetGenerator::EdgeToEdgeSetGenerator (std::unordered_set<db::Edge> &edges)
|
||||
: mp_edges (&edges)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void EdgeToEdgeSetGenerator::put (const db::Edge &edge)
|
||||
{
|
||||
mp_edges->insert (edge);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// class PolygonRefGenerator
|
||||
|
||||
PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes)
|
||||
: PolygonSink (), mp_layout (layout), mp_shapes (shapes)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void PolygonRefToShapesGenerator::put (const db::Polygon &polygon)
|
||||
{
|
||||
tl::MutexLocker locker (&mp_layout->lock ());
|
||||
mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ()));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// class PolygonSplitter
|
||||
|
||||
PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count)
|
||||
: mp_sink (&sink), m_max_area_ratio (max_area_ratio), m_max_vertex_count (max_vertex_count)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
PolygonSplitter::put (const db::Polygon &poly)
|
||||
{
|
||||
if ((m_max_vertex_count > 0 && poly.vertices () > m_max_vertex_count) || (m_max_area_ratio > 0.0 && poly.area_ratio () > m_max_area_ratio)) {
|
||||
|
||||
std::vector <db::Polygon> split_polygons;
|
||||
db::split_polygon (poly, split_polygons);
|
||||
for (std::vector <db::Polygon>::const_iterator sp = split_polygons.begin (); sp != split_polygons.end (); ++sp) {
|
||||
put (*sp);
|
||||
}
|
||||
|
||||
} else {
|
||||
mp_sink->put (poly);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbLocalOperationUtils
|
||||
#define HDR_dbLocalOperationUtils
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
#include "dbHash.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
template <class Trans>
|
||||
class polygon_transformation_filter
|
||||
: public PolygonSink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor specifying an external vector for storing the polygons
|
||||
*/
|
||||
polygon_transformation_filter (PolygonSink *output, const Trans &tr)
|
||||
: mp_output (output), m_trans (tr)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of the PolygonSink interface
|
||||
*/
|
||||
virtual void put (const db::Polygon &polygon)
|
||||
{
|
||||
mp_output->put (polygon.transformed (m_trans));
|
||||
}
|
||||
|
||||
private:
|
||||
db::PolygonSink *mp_output;
|
||||
const Trans m_trans;
|
||||
};
|
||||
|
||||
class PolygonRefGenerator
|
||||
: public PolygonSink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
PolygonRefGenerator (db::Layout *layout, std::unordered_set<db::PolygonRef> &polyrefs);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the PolygonSink interface
|
||||
*/
|
||||
virtual void put (const db::Polygon &polygon);
|
||||
|
||||
private:
|
||||
db::Layout *mp_layout;
|
||||
std::unordered_set<db::PolygonRef> *mp_polyrefs;
|
||||
};
|
||||
|
||||
class EdgeToEdgeSetGenerator
|
||||
: public EdgeSink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
EdgeToEdgeSetGenerator (std::unordered_set<db::Edge> &edges);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the PolygonSink interface
|
||||
*/
|
||||
virtual void put (const db::Edge &edge);
|
||||
|
||||
private:
|
||||
std::unordered_set<db::Edge> *mp_edges;
|
||||
};
|
||||
|
||||
class PolygonRefToShapesGenerator
|
||||
: public PolygonSink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor specifying an external vector for storing the polygons
|
||||
*/
|
||||
PolygonRefToShapesGenerator (db::Layout *layout, db::Shapes *shapes);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the PolygonSink interface
|
||||
*/
|
||||
virtual void put (const db::Polygon &polygon);
|
||||
|
||||
private:
|
||||
db::Layout *mp_layout;
|
||||
db::Shapes *mp_shapes;
|
||||
};
|
||||
|
||||
class PolygonSplitter
|
||||
: public PolygonSink
|
||||
{
|
||||
public:
|
||||
PolygonSplitter (PolygonSink &sink, double max_area_ratio, size_t max_vertex_count);
|
||||
|
||||
virtual void put (const db::Polygon &poly);
|
||||
|
||||
virtual void start () { mp_sink->start (); }
|
||||
virtual void flush () { mp_sink->flush (); }
|
||||
|
||||
private:
|
||||
PolygonSink *mp_sink;
|
||||
double m_max_area_ratio;
|
||||
size_t m_max_vertex_count;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbNet.h"
|
||||
#include "dbDevice.h"
|
||||
#include "dbDeviceClass.h"
|
||||
#include "dbCircuit.h"
|
||||
#include "dbSubCircuit.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// NetTerminalRef class implementation
|
||||
|
||||
NetTerminalRef::NetTerminalRef ()
|
||||
: m_terminal_id (0), mp_device (0), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetTerminalRef::NetTerminalRef (Device *device, size_t terminal_id)
|
||||
: m_terminal_id (terminal_id), mp_device (device), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetTerminalRef::NetTerminalRef (const NetTerminalRef &other)
|
||||
: m_terminal_id (other.m_terminal_id), mp_device (other.mp_device), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetTerminalRef &NetTerminalRef::operator= (const NetTerminalRef &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_terminal_id = other.m_terminal_id;
|
||||
mp_device = other.mp_device;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const DeviceTerminalDefinition *
|
||||
NetTerminalRef::terminal_def () const
|
||||
{
|
||||
const DeviceClass *dc = device_class ();
|
||||
if (dc) {
|
||||
return dc->terminal_definition (m_terminal_id);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const DeviceClass *
|
||||
NetTerminalRef::device_class () const
|
||||
{
|
||||
return mp_device ? mp_device->device_class () : 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// NetPinRef class implementation
|
||||
|
||||
NetPinRef::NetPinRef ()
|
||||
: m_pin_id (0), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetPinRef::NetPinRef (size_t pin_id)
|
||||
: m_pin_id (pin_id), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetPinRef::NetPinRef (const NetPinRef &other)
|
||||
: m_pin_id (other.m_pin_id), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetPinRef &NetPinRef::operator= (const NetPinRef &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_pin_id = other.m_pin_id;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Pin *NetPinRef::pin () const
|
||||
{
|
||||
if (mp_net && mp_net->circuit ()) {
|
||||
return mp_net->circuit ()->pin_by_id (m_pin_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// NetSubcircuitPinRef class implementation
|
||||
|
||||
NetSubcircuitPinRef::NetSubcircuitPinRef ()
|
||||
: m_pin_id (0), mp_subcircuit (0), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetSubcircuitPinRef::NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id)
|
||||
: m_pin_id (pin_id), mp_subcircuit (circuit), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetSubcircuitPinRef::NetSubcircuitPinRef (const NetSubcircuitPinRef &other)
|
||||
: m_pin_id (other.m_pin_id), mp_subcircuit (other.mp_subcircuit), mp_net (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetSubcircuitPinRef &NetSubcircuitPinRef::operator= (const NetSubcircuitPinRef &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_pin_id = other.m_pin_id;
|
||||
mp_subcircuit = other.mp_subcircuit;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Pin *NetSubcircuitPinRef::pin () const
|
||||
{
|
||||
if (mp_subcircuit && mp_subcircuit->circuit_ref ()) {
|
||||
return mp_subcircuit->circuit_ref ()->pin_by_id (m_pin_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Net class implementation
|
||||
|
||||
Net::Net ()
|
||||
: m_cluster_id (0), mp_circuit (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Net::Net (const std::string &name)
|
||||
: m_cluster_id (0), mp_circuit (0)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
Net::Net (const Net &other)
|
||||
: tl::Object (other), m_cluster_id (0), mp_circuit (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
Net &Net::operator= (const Net &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
|
||||
clear ();
|
||||
|
||||
m_name = other.m_name;
|
||||
m_cluster_id = other.m_cluster_id;
|
||||
|
||||
for (const_subcircuit_pin_iterator i = other.begin_subcircuit_pins (); i != other.end_subcircuit_pins (); ++i) {
|
||||
add_subcircuit_pin (*i);
|
||||
}
|
||||
|
||||
for (const_pin_iterator i = other.begin_pins (); i != other.end_pins (); ++i) {
|
||||
add_pin (*i);
|
||||
}
|
||||
|
||||
for (const_terminal_iterator i = other.begin_terminals (); i != other.end_terminals (); ++i) {
|
||||
add_terminal (*i);
|
||||
}
|
||||
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Net::~Net ()
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
void Net::clear ()
|
||||
{
|
||||
m_name.clear ();
|
||||
m_cluster_id = 0;
|
||||
|
||||
while (! m_terminals.empty ()) {
|
||||
erase_terminal (begin_terminals ());
|
||||
}
|
||||
|
||||
while (! m_pins.empty ()) {
|
||||
erase_pin (begin_pins ());
|
||||
}
|
||||
|
||||
while (! m_subcircuit_pins.empty ()) {
|
||||
erase_subcircuit_pin (begin_subcircuit_pins ());
|
||||
}
|
||||
}
|
||||
|
||||
void Net::set_name (const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
if (mp_circuit) {
|
||||
mp_circuit->m_net_by_name.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Net::qname () const
|
||||
{
|
||||
if (circuit ()) {
|
||||
return circuit ()->name () + ":" + expanded_name ();
|
||||
} else {
|
||||
return expanded_name ();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Net::expanded_name () const
|
||||
{
|
||||
if (name ().empty ()) {
|
||||
if (cluster_id () > std::numeric_limits<size_t>::max () / 2) {
|
||||
// avoid printing huge ID numbers for internal cluster IDs
|
||||
return "$I" + tl::to_string ((std::numeric_limits<size_t>::max () - cluster_id ()) + 1);
|
||||
} else {
|
||||
return "$" + tl::to_string (cluster_id ());
|
||||
}
|
||||
} else {
|
||||
return name ();
|
||||
}
|
||||
}
|
||||
|
||||
void Net::set_cluster_id (size_t ci)
|
||||
{
|
||||
m_cluster_id = ci;
|
||||
if (mp_circuit) {
|
||||
mp_circuit->m_net_by_cluster_id.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
void Net::add_pin (const NetPinRef &pin)
|
||||
{
|
||||
m_pins.push_back (pin);
|
||||
NetPinRef &new_pin = m_pins.back ();
|
||||
new_pin.set_net (this);
|
||||
|
||||
if (mp_circuit) {
|
||||
mp_circuit->set_pin_ref_for_pin (new_pin.pin_id (), --m_pins.end ());
|
||||
}
|
||||
}
|
||||
|
||||
void Net::add_subcircuit_pin (const NetSubcircuitPinRef &pin)
|
||||
{
|
||||
m_subcircuit_pins.push_back (pin);
|
||||
NetSubcircuitPinRef &new_pin = m_subcircuit_pins.back ();
|
||||
new_pin.set_net (this);
|
||||
|
||||
tl_assert (pin.subcircuit () != 0);
|
||||
new_pin.subcircuit ()->set_pin_ref_for_pin (new_pin.pin_id (), --m_subcircuit_pins.end ());
|
||||
}
|
||||
|
||||
void Net::erase_pin (pin_iterator iter)
|
||||
{
|
||||
if (mp_circuit) {
|
||||
mp_circuit->set_pin_ref_for_pin (iter->pin_id (), pin_iterator ());
|
||||
}
|
||||
m_pins.erase (iter);
|
||||
}
|
||||
|
||||
void Net::erase_subcircuit_pin (subcircuit_pin_iterator iter)
|
||||
{
|
||||
if (iter->subcircuit ()) {
|
||||
iter->subcircuit ()->set_pin_ref_for_pin (iter->pin_id (), subcircuit_pin_iterator ());
|
||||
}
|
||||
m_subcircuit_pins.erase (iter);
|
||||
}
|
||||
|
||||
void Net::add_terminal (const NetTerminalRef &terminal)
|
||||
{
|
||||
if (! terminal.device ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_terminals.push_back (terminal);
|
||||
NetTerminalRef &new_terminal = m_terminals.back ();
|
||||
new_terminal.set_net (this);
|
||||
new_terminal.device ()->set_terminal_ref_for_terminal (new_terminal.terminal_id (), --m_terminals.end ());
|
||||
}
|
||||
|
||||
void Net::erase_terminal (terminal_iterator iter)
|
||||
{
|
||||
if (iter->device ()) {
|
||||
iter->device ()->set_terminal_ref_for_terminal (iter->terminal_id (), terminal_iterator ());
|
||||
}
|
||||
m_terminals.erase (iter);
|
||||
}
|
||||
|
||||
void Net::set_circuit (Circuit *circuit)
|
||||
{
|
||||
mp_circuit = circuit;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,651 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNet
|
||||
#define _HDR_dbNet
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "tlObject.h"
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Device;
|
||||
class Net;
|
||||
class SubCircuit;
|
||||
class Circuit;
|
||||
class DeviceTerminalDefinition;
|
||||
class DeviceClass;
|
||||
class Pin;
|
||||
|
||||
/**
|
||||
* @brief A reference to a terminal of a device
|
||||
*
|
||||
* A terminal must always refer to a device inside the current circuit.
|
||||
*/
|
||||
class DB_PUBLIC NetTerminalRef
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
NetTerminalRef ();
|
||||
|
||||
/**
|
||||
* @brief Creates a pin reference to the given pin of the current circuit
|
||||
*/
|
||||
NetTerminalRef (Device *device, size_t terminal_id);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
NetTerminalRef (const NetTerminalRef &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
NetTerminalRef &operator= (const NetTerminalRef &other);
|
||||
|
||||
/**
|
||||
* @brief Comparison
|
||||
*/
|
||||
bool operator< (const NetTerminalRef &other) const
|
||||
{
|
||||
if (mp_device != other.mp_device) {
|
||||
return mp_device < other.mp_device;
|
||||
}
|
||||
return m_terminal_id < other.m_terminal_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const NetTerminalRef &other) const
|
||||
{
|
||||
return (mp_device == other.mp_device && m_terminal_id == other.m_terminal_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device reference
|
||||
*/
|
||||
Device *device ()
|
||||
{
|
||||
return mp_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device reference (const version)
|
||||
*/
|
||||
const Device *device () const
|
||||
{
|
||||
return mp_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the terminal index
|
||||
*/
|
||||
size_t terminal_id () const
|
||||
{
|
||||
return m_terminal_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the terminal definition
|
||||
*
|
||||
* Returns 0 if the terminal is not a valid terminal reference.
|
||||
*/
|
||||
const DeviceTerminalDefinition *terminal_def () const;
|
||||
|
||||
/**
|
||||
* @brief Returns the device class
|
||||
*/
|
||||
const DeviceClass *device_class () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the net the terminal lives in
|
||||
*/
|
||||
Net *net ()
|
||||
{
|
||||
return mp_net;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net the terminal lives in (const version)
|
||||
*/
|
||||
const Net *net () const
|
||||
{
|
||||
return mp_net;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Net;
|
||||
|
||||
size_t m_terminal_id;
|
||||
Device *mp_device;
|
||||
Net *mp_net;
|
||||
|
||||
/**
|
||||
* @brief Sets the net the terminal lives in
|
||||
*/
|
||||
void set_net (Net *net)
|
||||
{
|
||||
mp_net = net;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A reference to a pin inside a net
|
||||
*
|
||||
* This object describes a connection to an outgoing pin.
|
||||
*/
|
||||
class DB_PUBLIC NetPinRef
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
NetPinRef ();
|
||||
|
||||
/**
|
||||
* @brief Creates a pin reference to the given pin of the current circuit
|
||||
*/
|
||||
NetPinRef (size_t pin_id);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
NetPinRef (const NetPinRef &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
NetPinRef &operator= (const NetPinRef &other);
|
||||
|
||||
/**
|
||||
* @brief Comparison
|
||||
*/
|
||||
bool operator< (const NetPinRef &other) const
|
||||
{
|
||||
return m_pin_id < other.m_pin_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const NetPinRef &other) const
|
||||
{
|
||||
return (m_pin_id == other.m_pin_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the pin reference (const version)
|
||||
*/
|
||||
size_t pin_id () const
|
||||
{
|
||||
return m_pin_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the pin reference from the pin id
|
||||
* If the pin cannot be resolved, null is returned.
|
||||
*/
|
||||
const Pin *pin () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the net the pin lives in
|
||||
*/
|
||||
Net *net ()
|
||||
{
|
||||
return mp_net;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net the pin lives in (const version)
|
||||
*/
|
||||
const Net *net () const
|
||||
{
|
||||
return mp_net;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Net;
|
||||
|
||||
size_t m_pin_id;
|
||||
Net *mp_net;
|
||||
|
||||
/**
|
||||
* @brief Sets the net the terminal lives in
|
||||
*/
|
||||
void set_net (Net *net)
|
||||
{
|
||||
mp_net = net;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A reference to a pin inside a net
|
||||
*
|
||||
* This object describes a connection to a pin of a subcircuit.
|
||||
*/
|
||||
class DB_PUBLIC NetSubcircuitPinRef
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
NetSubcircuitPinRef ();
|
||||
|
||||
/**
|
||||
* @brief Creates a pin reference to the given pin of the given subcircuit
|
||||
*/
|
||||
NetSubcircuitPinRef (SubCircuit *circuit, size_t pin_id);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
NetSubcircuitPinRef (const NetSubcircuitPinRef &other);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
NetSubcircuitPinRef &operator= (const NetSubcircuitPinRef &other);
|
||||
|
||||
/**
|
||||
* @brief Comparison
|
||||
*/
|
||||
bool operator< (const NetSubcircuitPinRef &other) const
|
||||
{
|
||||
if (mp_subcircuit != other.mp_subcircuit) {
|
||||
return mp_subcircuit < other.mp_subcircuit;
|
||||
}
|
||||
return m_pin_id < other.m_pin_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const NetSubcircuitPinRef &other) const
|
||||
{
|
||||
return (mp_subcircuit == other.mp_subcircuit && m_pin_id == other.m_pin_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the pin reference (const version)
|
||||
*/
|
||||
size_t pin_id () const
|
||||
{
|
||||
return m_pin_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the pin reference from the pin id
|
||||
* If the pin cannot be resolved, null is returned.
|
||||
*/
|
||||
const Pin *pin () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the subcircuit reference
|
||||
*/
|
||||
SubCircuit *subcircuit ()
|
||||
{
|
||||
return mp_subcircuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the subcircuit reference (const version)
|
||||
*/
|
||||
const SubCircuit *subcircuit () const
|
||||
{
|
||||
return mp_subcircuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net the pin lives in
|
||||
*/
|
||||
Net *net ()
|
||||
{
|
||||
return mp_net;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the net the pin lives in (const version)
|
||||
*/
|
||||
const Net *net () const
|
||||
{
|
||||
return mp_net;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Net;
|
||||
|
||||
size_t m_pin_id;
|
||||
SubCircuit *mp_subcircuit;
|
||||
Net *mp_net;
|
||||
|
||||
/**
|
||||
* @brief Sets the net the terminal lives in
|
||||
*/
|
||||
void set_net (Net *net)
|
||||
{
|
||||
mp_net = net;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A net
|
||||
*
|
||||
* A net connects terminals of devices and pins or circuits
|
||||
*/
|
||||
class DB_PUBLIC Net
|
||||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
typedef std::list<NetTerminalRef> terminal_list;
|
||||
typedef terminal_list::const_iterator const_terminal_iterator;
|
||||
typedef terminal_list::iterator terminal_iterator;
|
||||
typedef std::list<NetPinRef> pin_list;
|
||||
typedef pin_list::const_iterator const_pin_iterator;
|
||||
typedef pin_list::iterator pin_iterator;
|
||||
typedef std::list<NetSubcircuitPinRef> subcircuit_pin_list;
|
||||
typedef subcircuit_pin_list::const_iterator const_subcircuit_pin_iterator;
|
||||
typedef subcircuit_pin_list::iterator subcircuit_pin_iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* Creates an empty circuit.
|
||||
*/
|
||||
Net ();
|
||||
|
||||
/**
|
||||
* @brief Creates a empty net with the give name
|
||||
*/
|
||||
Net (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
Net (const Net &other);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~Net ();
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
Net &operator= (const Net &other);
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit the net lives in
|
||||
* This pointer is 0 if the net is not part of a circuit.
|
||||
*/
|
||||
Circuit *circuit ()
|
||||
{
|
||||
return mp_circuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit the net lives in (const version)
|
||||
* This pointer is 0 if the net is not part of a circuit.
|
||||
*/
|
||||
const Circuit *circuit () const
|
||||
{
|
||||
return mp_circuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the circuit
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Sets the name of the circuit
|
||||
*/
|
||||
void set_name (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the circuit
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the expanded name
|
||||
*
|
||||
* The "expanded name" is a non-empty name for the net. It uses the
|
||||
* cluster ID if no name is set.
|
||||
*/
|
||||
std::string expanded_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the qualified name
|
||||
*
|
||||
* The qualified name is like the expanded name, but preceeded with the
|
||||
* Circuit name if known (e.g. "CIRCUIT:NET")
|
||||
*/
|
||||
std::string qname () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the cluster ID of this net
|
||||
*
|
||||
* The cluster ID links the net to a cluster from the
|
||||
* hierarchical layout netlist extractor.
|
||||
*/
|
||||
void set_cluster_id (size_t ci);
|
||||
|
||||
/**
|
||||
* @brief Gets the cluster ID
|
||||
*/
|
||||
size_t cluster_id () const
|
||||
{
|
||||
return m_cluster_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a pin to this net
|
||||
*/
|
||||
void add_pin (const NetPinRef &pin);
|
||||
|
||||
/**
|
||||
* @brief Erases the given pin from this net
|
||||
*/
|
||||
void erase_pin (pin_iterator iter);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the pins of the net (const version)
|
||||
*/
|
||||
const_pin_iterator begin_pins () const
|
||||
{
|
||||
return m_pins.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the pins of the net (const version)
|
||||
*/
|
||||
const_pin_iterator end_pins () const
|
||||
{
|
||||
return m_pins.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the pins of the net (non-const version)
|
||||
*/
|
||||
pin_iterator begin_pins ()
|
||||
{
|
||||
return m_pins.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the pins of the net (non-const version)
|
||||
*/
|
||||
pin_iterator end_pins ()
|
||||
{
|
||||
return m_pins.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a subcircuit pin to this net
|
||||
*/
|
||||
void add_subcircuit_pin (const NetSubcircuitPinRef &pin);
|
||||
|
||||
/**
|
||||
* @brief Erases the given subcircuit pin from this net
|
||||
*/
|
||||
void erase_subcircuit_pin (subcircuit_pin_iterator iter);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the pins of the net (const version)
|
||||
*/
|
||||
const_subcircuit_pin_iterator begin_subcircuit_pins () const
|
||||
{
|
||||
return m_subcircuit_pins.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the pins of the net (const version)
|
||||
*/
|
||||
const_subcircuit_pin_iterator end_subcircuit_pins () const
|
||||
{
|
||||
return m_subcircuit_pins.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the pins of the net (non-const version)
|
||||
*/
|
||||
subcircuit_pin_iterator begin_subcircuit_pins ()
|
||||
{
|
||||
return m_subcircuit_pins.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the pins of the net (non-const version)
|
||||
*/
|
||||
subcircuit_pin_iterator end_subcircuit_pins ()
|
||||
{
|
||||
return m_subcircuit_pins.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a terminal to this net
|
||||
*/
|
||||
void add_terminal (const NetTerminalRef &terminal);
|
||||
|
||||
/**
|
||||
* @brief Erases the given terminal from this net
|
||||
*/
|
||||
void erase_terminal (terminal_iterator iter);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the terminals of the net (const version)
|
||||
*/
|
||||
const_terminal_iterator begin_terminals () const
|
||||
{
|
||||
return m_terminals.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the terminals of the net (const version)
|
||||
*/
|
||||
const_terminal_iterator end_terminals () const
|
||||
{
|
||||
return m_terminals.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the terminals of the net (non-const version)
|
||||
*/
|
||||
terminal_iterator begin_terminals ()
|
||||
{
|
||||
return m_terminals.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the terminals of the net (non-const version)
|
||||
*/
|
||||
terminal_iterator end_terminals ()
|
||||
{
|
||||
return m_terminals.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the net is floating (has no or only a single connection)
|
||||
*/
|
||||
bool is_floating () const
|
||||
{
|
||||
return (m_pins.size () + m_subcircuit_pins.size () + m_terminals.size ()) < 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the net is an internal node (connects two terminals only)
|
||||
*/
|
||||
bool is_internal () const
|
||||
{
|
||||
return m_pins.size () == 0 && m_subcircuit_pins.size () == 0 && m_terminals.size () == 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of outgoing pins connected
|
||||
*/
|
||||
size_t pin_count () const
|
||||
{
|
||||
return m_pins.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of subcircuit pins connected
|
||||
*/
|
||||
size_t subcircuit_pin_count () const
|
||||
{
|
||||
return m_subcircuit_pins.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of terminals connected
|
||||
*/
|
||||
size_t terminal_count () const
|
||||
{
|
||||
return m_terminals.size ();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Circuit;
|
||||
|
||||
terminal_list m_terminals;
|
||||
pin_list m_pins;
|
||||
subcircuit_pin_list m_subcircuit_pins;
|
||||
std::string m_name;
|
||||
size_t m_cluster_id;
|
||||
Circuit *mp_circuit;
|
||||
|
||||
void set_circuit (Circuit *circuit);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,549 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbNetlist.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Netlist class implementation
|
||||
|
||||
Netlist::Netlist ()
|
||||
: 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),
|
||||
m_device_abstract_by_cell_index (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts)
|
||||
{
|
||||
m_circuits.changed ().add (this, &Netlist::invalidate_topology);
|
||||
m_circuits.changed ().add (this, &Netlist::circuits_changed);
|
||||
m_device_abstracts.changed ().add (this, &Netlist::device_abstracts_changed);
|
||||
}
|
||||
|
||||
Netlist::Netlist (const Netlist &other)
|
||||
: gsi::ObjectBase (other), tl::Object (other), 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),
|
||||
m_device_abstract_by_cell_index (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts)
|
||||
{
|
||||
operator= (other);
|
||||
m_circuits.changed ().add (this, &Netlist::invalidate_topology);
|
||||
m_circuits.changed ().add (this, &Netlist::circuits_changed);
|
||||
m_device_abstracts.changed ().add (this, &Netlist::device_abstracts_changed);
|
||||
}
|
||||
|
||||
Netlist::~Netlist ()
|
||||
{
|
||||
m_circuits.changed ().remove (this, &Netlist::invalidate_topology);
|
||||
m_circuits.changed ().remove (this, &Netlist::circuits_changed);
|
||||
m_device_abstracts.changed ().remove (this, &Netlist::device_abstracts_changed);
|
||||
}
|
||||
|
||||
Netlist &Netlist::operator= (const Netlist &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
|
||||
clear ();
|
||||
|
||||
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 ();
|
||||
dct [dc.operator-> ()] = dc_new;
|
||||
m_device_classes.push_back (dc_new);
|
||||
}
|
||||
|
||||
std::map<const DeviceAbstract *, DeviceAbstract *> dmt;
|
||||
for (const_abstract_model_iterator dm = other.begin_device_abstracts (); dm != other.end_device_abstracts (); ++dm) {
|
||||
DeviceAbstract *dm_new = new DeviceAbstract (*dm);
|
||||
dmt [dm.operator-> ()] = dm_new;
|
||||
m_device_abstracts.push_back (dm_new);
|
||||
}
|
||||
|
||||
std::map<const Circuit *, Circuit *> ct;
|
||||
for (const_circuit_iterator i = other.begin_circuits (); i != other.end_circuits (); ++i) {
|
||||
Circuit *ct_new = new Circuit (*i);
|
||||
ct_new->translate_device_classes (dct);
|
||||
ct_new->translate_device_abstracts (dmt);
|
||||
ct [i.operator-> ()] = ct_new;
|
||||
add_circuit (ct_new);
|
||||
}
|
||||
|
||||
for (circuit_iterator i = begin_circuits (); i != end_circuits (); ++i) {
|
||||
i->translate_circuits (ct);
|
||||
}
|
||||
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Netlist::circuits_changed ()
|
||||
{
|
||||
m_circuit_by_cell_index.invalidate ();
|
||||
m_circuit_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Netlist::device_abstracts_changed ()
|
||||
{
|
||||
m_device_abstract_by_cell_index.invalidate ();
|
||||
m_device_abstract_by_name.invalidate ();
|
||||
}
|
||||
|
||||
void Netlist::invalidate_topology ()
|
||||
{
|
||||
if (m_valid_topology) {
|
||||
|
||||
m_valid_topology = false;
|
||||
|
||||
if (m_lock_count == 0) {
|
||||
m_top_circuits = 0;
|
||||
m_top_down_circuits.clear ();
|
||||
m_child_circuits.clear ();
|
||||
m_parent_circuits.clear ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct sort_by_index
|
||||
{
|
||||
bool operator () (const Circuit *a, const Circuit *b) const
|
||||
{
|
||||
return a->index () < b->index ();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void Netlist::validate_topology ()
|
||||
{
|
||||
if (m_valid_topology) {
|
||||
return;
|
||||
} else if (m_lock_count > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_child_circuits.clear ();
|
||||
m_parent_circuits.clear ();
|
||||
|
||||
size_t max_index = 0;
|
||||
for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
|
||||
c->set_index (max_index);
|
||||
++max_index;
|
||||
}
|
||||
|
||||
// build the child circuit list ... needed for the topology sorting
|
||||
|
||||
m_child_circuits.reserve (max_index);
|
||||
m_parent_circuits.reserve (max_index);
|
||||
|
||||
for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
|
||||
|
||||
std::set<Circuit *> children;
|
||||
for (Circuit::subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) {
|
||||
if (sc->circuit_ref ()) {
|
||||
children.insert (sc->circuit_ref ());
|
||||
}
|
||||
}
|
||||
|
||||
m_child_circuits.push_back (tl::vector<Circuit *> ());
|
||||
tl::vector<Circuit *> &cc = m_child_circuits.back ();
|
||||
cc.reserve (children.size ());
|
||||
cc.insert (cc.end (), children.begin (), children.end ());
|
||||
// sort by index for better reproducibility
|
||||
std::sort (cc.begin (), cc.end (), sort_by_index ());
|
||||
|
||||
std::set<Circuit *> parents;
|
||||
for (Circuit::refs_iterator sc = c->begin_refs (); sc != c->end_refs (); ++sc) {
|
||||
if (sc->circuit ()) {
|
||||
parents.insert (sc->circuit ());
|
||||
}
|
||||
}
|
||||
|
||||
m_parent_circuits.push_back (tl::vector<Circuit *> ());
|
||||
tl::vector<Circuit *> &pc = m_parent_circuits.back ();
|
||||
pc.reserve (parents.size ());
|
||||
pc.insert (pc.end (), parents.begin (), parents.end ());
|
||||
// sort by index for better reproducibility
|
||||
std::sort (pc.begin (), pc.end (), sort_by_index ());
|
||||
|
||||
}
|
||||
|
||||
// do topology sorting
|
||||
|
||||
m_top_circuits = 0;
|
||||
m_top_down_circuits.clear ();
|
||||
m_top_down_circuits.reserve (max_index);
|
||||
|
||||
std::vector<size_t> num_parents (max_index, 0);
|
||||
|
||||
// while there are cells to treat ..
|
||||
while (m_top_down_circuits.size () != max_index) {
|
||||
|
||||
size_t n_top_down_circuits = m_top_down_circuits.size ();
|
||||
|
||||
// Treat all circuits that do not have all parents reported.
|
||||
// For all such a circuits, disable the parent counting,
|
||||
// add the circuits's index to the top-down sorted list and
|
||||
// increment the reported parent count in all the
|
||||
// child circuits.
|
||||
|
||||
for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
|
||||
if (m_parent_circuits [c->index ()].size () == num_parents [c->index ()]) {
|
||||
m_top_down_circuits.push_back (c.operator-> ());
|
||||
num_parents [c->index ()] = std::numeric_limits<cell_index_type>::max ();
|
||||
}
|
||||
}
|
||||
|
||||
// For all these a circuits, increment the reported parent instance
|
||||
// count in all the child circuits.
|
||||
for (tl::vector<Circuit *>::const_iterator ii = m_top_down_circuits.begin () + n_top_down_circuits; ii != m_top_down_circuits.end (); ++ii) {
|
||||
const tl::vector<Circuit *> &cc = m_child_circuits [(*ii)->index ()];
|
||||
for (tl::vector<Circuit *>::const_iterator icc = cc.begin (); icc != cc.end (); ++icc) {
|
||||
tl_assert (num_parents [(*icc)->index ()] != std::numeric_limits<cell_index_type>::max ());
|
||||
num_parents [(*icc)->index ()] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If no new cells have been reported this is basically a
|
||||
// sign of recursion in the graph.
|
||||
if (n_top_down_circuits == m_top_down_circuits.size ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Recursive hierarchy detected in netlist")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Determine the number of top cells
|
||||
for (tl::vector<Circuit *>::const_iterator e = m_top_down_circuits.begin (); e != m_top_down_circuits.end () && m_parent_circuits [(*e)->index ()].empty (); ++e) {
|
||||
++m_top_circuits;
|
||||
}
|
||||
|
||||
m_valid_topology = true;
|
||||
}
|
||||
|
||||
void Netlist::lock ()
|
||||
{
|
||||
if (m_lock_count == 0) {
|
||||
validate_topology ();
|
||||
}
|
||||
++m_lock_count;
|
||||
}
|
||||
|
||||
void Netlist::unlock ()
|
||||
{
|
||||
if (m_lock_count > 0) {
|
||||
--m_lock_count;
|
||||
}
|
||||
}
|
||||
|
||||
const tl::vector<Circuit *> &Netlist::child_circuits (Circuit *circuit)
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
validate_topology ();
|
||||
}
|
||||
|
||||
tl_assert (circuit->index () < m_child_circuits.size ());
|
||||
return m_child_circuits [circuit->index ()];
|
||||
}
|
||||
|
||||
const tl::vector<Circuit *> &Netlist::parent_circuits (Circuit *circuit)
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
validate_topology ();
|
||||
}
|
||||
|
||||
tl_assert (circuit->index () < m_parent_circuits.size ());
|
||||
return m_parent_circuits [circuit->index ()];
|
||||
}
|
||||
|
||||
Netlist::top_down_circuit_iterator Netlist::begin_top_down ()
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
validate_topology ();
|
||||
}
|
||||
return m_top_down_circuits.begin ();
|
||||
}
|
||||
|
||||
Netlist::top_down_circuit_iterator Netlist::end_top_down ()
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
validate_topology ();
|
||||
}
|
||||
return m_top_down_circuits.end ();
|
||||
}
|
||||
|
||||
Netlist::const_top_down_circuit_iterator Netlist::begin_top_down () const
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
const_cast<Netlist *> (this)->validate_topology ();
|
||||
}
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (m_top_down_circuits).begin ();
|
||||
}
|
||||
|
||||
Netlist::const_top_down_circuit_iterator Netlist::end_top_down () const
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
const_cast<Netlist *> (this)->validate_topology ();
|
||||
}
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (m_top_down_circuits).end ();
|
||||
}
|
||||
|
||||
size_t Netlist::top_circuit_count () const
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
const_cast<Netlist *> (this)->validate_topology ();
|
||||
}
|
||||
return m_top_circuits;
|
||||
}
|
||||
|
||||
Netlist::bottom_up_circuit_iterator Netlist::begin_bottom_up ()
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
validate_topology ();
|
||||
}
|
||||
return m_top_down_circuits.rbegin ();
|
||||
}
|
||||
|
||||
Netlist::bottom_up_circuit_iterator Netlist::end_bottom_up ()
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
validate_topology ();
|
||||
}
|
||||
return m_top_down_circuits.rend ();
|
||||
}
|
||||
|
||||
Netlist::const_bottom_up_circuit_iterator Netlist::begin_bottom_up () const
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
const_cast<Netlist *> (this)->validate_topology ();
|
||||
}
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (m_top_down_circuits).rbegin ();
|
||||
}
|
||||
|
||||
Netlist::const_bottom_up_circuit_iterator Netlist::end_bottom_up () const
|
||||
{
|
||||
if (! m_valid_topology) {
|
||||
const_cast<Netlist *> (this)->validate_topology ();
|
||||
}
|
||||
return reinterpret_cast<const tl::vector<const Circuit *> &> (m_top_down_circuits).rend ();
|
||||
}
|
||||
|
||||
void Netlist::clear ()
|
||||
{
|
||||
m_device_classes.clear ();
|
||||
m_device_abstracts.clear ();
|
||||
m_circuits.clear ();
|
||||
}
|
||||
|
||||
void Netlist::add_circuit (Circuit *circuit)
|
||||
{
|
||||
m_circuits.push_back (circuit);
|
||||
circuit->set_netlist (this);
|
||||
}
|
||||
|
||||
void Netlist::remove_circuit (Circuit *circuit)
|
||||
{
|
||||
circuit->set_netlist (0);
|
||||
m_circuits.erase (circuit);
|
||||
}
|
||||
|
||||
void Netlist::add_device_class (DeviceClass *device_class)
|
||||
{
|
||||
m_device_classes.push_back (device_class);
|
||||
device_class->set_netlist (this);
|
||||
}
|
||||
|
||||
void Netlist::remove_device_class (DeviceClass *device_class)
|
||||
{
|
||||
device_class->set_netlist (0);
|
||||
m_device_classes.erase (device_class);
|
||||
}
|
||||
|
||||
void Netlist::add_device_abstract (DeviceAbstract *device_abstract)
|
||||
{
|
||||
m_device_abstracts.push_back (device_abstract);
|
||||
device_abstract->set_netlist (this);
|
||||
}
|
||||
|
||||
void Netlist::remove_device_abstract (DeviceAbstract *device_abstract)
|
||||
{
|
||||
device_abstract->set_netlist (0);
|
||||
m_device_abstracts.erase (device_abstract);
|
||||
}
|
||||
|
||||
void Netlist::purge_nets ()
|
||||
{
|
||||
for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
|
||||
c->purge_nets ();
|
||||
}
|
||||
}
|
||||
|
||||
void Netlist::make_top_level_pins ()
|
||||
{
|
||||
size_t ntop = top_circuit_count ();
|
||||
for (top_down_circuit_iterator c = begin_top_down (); c != end_top_down () && ntop > 0; ++c, --ntop) {
|
||||
|
||||
Circuit *circuit = *c;
|
||||
|
||||
if (circuit->pin_count () == 0) {
|
||||
|
||||
// create pins for the named nets and connect them
|
||||
for (Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) {
|
||||
if (! n->name ().empty () && n->terminal_count () + n->subcircuit_pin_count () > 0) {
|
||||
Pin pin = circuit->add_pin (n->name ());
|
||||
circuit->connect_pin (pin.id (), n.operator-> ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Netlist::purge ()
|
||||
{
|
||||
// This locking is very important as we do not want to recompute the bottom-up list
|
||||
// while iterating.
|
||||
NetlistLocker locker (this);
|
||||
|
||||
for (bottom_up_circuit_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
||||
|
||||
Circuit *circuit = *c;
|
||||
|
||||
circuit->purge_nets ();
|
||||
if (circuit->begin_nets () == circuit->end_nets ()) {
|
||||
|
||||
// No nets left: delete the subcircuits that refer to us and finally delete the circuit
|
||||
while (circuit->begin_refs () != circuit->end_refs ()) {
|
||||
delete circuit->begin_refs ().operator-> ();
|
||||
}
|
||||
delete circuit;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Netlist::combine_devices ()
|
||||
{
|
||||
for (circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
|
||||
c->combine_devices ();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string net2string (const db::Net *net)
|
||||
{
|
||||
return net ? tl::to_word_or_quoted_string (net->expanded_name ()) : "(null)";
|
||||
}
|
||||
|
||||
static std::string device2string (const db::Device &device)
|
||||
{
|
||||
if (device.name ().empty ()) {
|
||||
return "$" + tl::to_string (device.id ());
|
||||
} else {
|
||||
return tl::to_word_or_quoted_string (device.name ());
|
||||
}
|
||||
}
|
||||
|
||||
static std::string subcircuit2string (const db::SubCircuit &subcircuit)
|
||||
{
|
||||
if (subcircuit.name ().empty ()) {
|
||||
return "$" + tl::to_string (subcircuit.id ());
|
||||
} else {
|
||||
return tl::to_word_or_quoted_string (subcircuit.name ());
|
||||
}
|
||||
}
|
||||
|
||||
static std::string pin2string (const db::Pin &pin)
|
||||
{
|
||||
if (pin.name ().empty ()) {
|
||||
// the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs
|
||||
return "$" + tl::to_string (pin.id () + 1);
|
||||
} else {
|
||||
return tl::to_word_or_quoted_string (pin.name ());
|
||||
}
|
||||
}
|
||||
|
||||
std::string Netlist::to_string () const
|
||||
{
|
||||
std::string res;
|
||||
for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
|
||||
|
||||
std::string ps;
|
||||
for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) {
|
||||
if (! ps.empty ()) {
|
||||
ps += ",";
|
||||
}
|
||||
ps += pin2string (*p) + "=" + net2string (c->net_for_pin (p->id ()));
|
||||
}
|
||||
|
||||
res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n";
|
||||
|
||||
#if 0 // for debugging
|
||||
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
|
||||
res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " sc_pins=" + tl::to_string (n->subcircuit_pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) {
|
||||
std::string ts;
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
|
||||
if (t != td.begin ()) {
|
||||
ts += ",";
|
||||
}
|
||||
ts += t->name () + "=" + net2string (d->net_for_terminal (t->id ()));
|
||||
}
|
||||
std::string ps;
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = d->device_class ()->parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
|
||||
if (p != pd.begin ()) {
|
||||
ps += ",";
|
||||
}
|
||||
ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ()));
|
||||
}
|
||||
res += std::string (" D") + d->device_class ()->name () + " " + device2string (*d) + " (" + ts + ") [" + ps + "]\n";
|
||||
}
|
||||
|
||||
for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) {
|
||||
std::string ps;
|
||||
const db::SubCircuit &subcircuit = *sc;
|
||||
const db::Circuit *circuit = sc->circuit_ref ();
|
||||
for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) {
|
||||
if (p != circuit->begin_pins ()) {
|
||||
ps += ",";
|
||||
}
|
||||
const db::Pin &pin = *p;
|
||||
ps += pin2string (pin) + "=" + net2string (subcircuit.net_for_pin (pin.id ()));
|
||||
}
|
||||
res += std::string (" X") + circuit->name () + " " + subcircuit2string (*sc) + " (" + ps + ")\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,478 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlist
|
||||
#define _HDR_dbNetlist
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbCircuit.h"
|
||||
#include "dbDeviceClass.h"
|
||||
#include "dbDeviceAbstract.h"
|
||||
|
||||
#include "tlVector.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The netlist class
|
||||
*
|
||||
* This class represents a hierarchical netlist.
|
||||
* The main components of a netlist are circuits and device classes.
|
||||
* The circuits represent cells, the device classes type of devices.
|
||||
*/
|
||||
class DB_PUBLIC Netlist
|
||||
: public gsi::ObjectBase, public tl::Object
|
||||
{
|
||||
public:
|
||||
typedef tl::shared_collection<Circuit> circuit_list;
|
||||
typedef circuit_list::const_iterator const_circuit_iterator;
|
||||
typedef circuit_list::iterator circuit_iterator;
|
||||
typedef tl::shared_collection<DeviceClass> device_class_list;
|
||||
typedef device_class_list::const_iterator const_device_class_iterator;
|
||||
typedef device_class_list::iterator device_class_iterator;
|
||||
typedef tl::shared_collection<DeviceAbstract> device_abstract_list;
|
||||
typedef device_abstract_list::const_iterator const_abstract_model_iterator;
|
||||
typedef device_abstract_list::iterator device_abstract_iterator;
|
||||
typedef tl::vector<Circuit *>::const_iterator top_down_circuit_iterator;
|
||||
typedef tl::vector<const Circuit *>::const_iterator const_top_down_circuit_iterator;
|
||||
typedef tl::vector<Circuit *>::const_reverse_iterator bottom_up_circuit_iterator;
|
||||
typedef tl::vector<const Circuit *>::const_reverse_iterator const_bottom_up_circuit_iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* This constructor creates an empty hierarchical netlist
|
||||
*/
|
||||
Netlist ();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
Netlist (const Netlist &other);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~Netlist ();
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
Netlist &operator= (const Netlist &other);
|
||||
|
||||
/**
|
||||
* @brief Clears the netlist
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Returns a string representation of the netlist
|
||||
*
|
||||
* This method is basically intended to testing.
|
||||
*/
|
||||
std::string to_string () const;
|
||||
|
||||
/**
|
||||
* @brief Starts a sequence of operations during which topology updates are not desired
|
||||
*
|
||||
* If the hierarchy is modified, the topology information (top-down order, children
|
||||
* and parent information) may be recomputed frequently. This may cause performance issues
|
||||
* and may not be desired.
|
||||
*
|
||||
* Calling this method will bring the netlist into a state in which updates on the
|
||||
* list will not happen. Using "unlock" will end this state.
|
||||
*
|
||||
* "lock" and "unlock" are incremental and can be nested. Use "NetlistLocker" for safe locking
|
||||
* and unlocking.
|
||||
*
|
||||
* Before lock, the state will be validated, so inside the locked operation, the topology
|
||||
* information will be valid with respect to the initial state.
|
||||
*/
|
||||
void lock ();
|
||||
|
||||
/**
|
||||
* @brief Ends a sequence of operations during which topology updates are not desired
|
||||
* See "lock" for more details.
|
||||
*/
|
||||
void unlock ();
|
||||
|
||||
/**
|
||||
* @brief Adds a circuit to this netlist
|
||||
*
|
||||
* The netlist takes over ownership of the object.
|
||||
*/
|
||||
void add_circuit (Circuit *circuit);
|
||||
|
||||
/**
|
||||
* @brief Deletes a circuit from the netlist
|
||||
*/
|
||||
void remove_circuit (Circuit *circuit);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the circuits of the netlist (non-const version)
|
||||
*/
|
||||
circuit_iterator begin_circuits ()
|
||||
{
|
||||
return m_circuits.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the circuits of the netlist (non-const version)
|
||||
*/
|
||||
circuit_iterator end_circuits ()
|
||||
{
|
||||
return m_circuits.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the circuits of the netlist (const version)
|
||||
*/
|
||||
const_circuit_iterator begin_circuits () const
|
||||
{
|
||||
return m_circuits.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the circuits of the netlist (const version)
|
||||
*/
|
||||
const_circuit_iterator end_circuits () const
|
||||
{
|
||||
return m_circuits.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit with the given name
|
||||
*
|
||||
* If no circuit with that name exists, null is returned.
|
||||
*/
|
||||
Circuit *circuit_by_name (const std::string &name)
|
||||
{
|
||||
return m_circuit_by_name.object_by (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit with the given name (const version)
|
||||
*
|
||||
* If no circuit with that name exists, null is returned.
|
||||
*/
|
||||
const Circuit *circuit_by_name (const std::string &name) const
|
||||
{
|
||||
return m_circuit_by_name.object_by (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit with the given cell index
|
||||
*
|
||||
* If no circuit with that cell index exists, null is returned.
|
||||
*/
|
||||
Circuit *circuit_by_cell_index (db::cell_index_type cell_index)
|
||||
{
|
||||
return m_circuit_by_cell_index.object_by (cell_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the circuit with the given cell index (const version)
|
||||
*
|
||||
* If no circuit with that cell index exists, null is returned.
|
||||
*/
|
||||
const Circuit *circuit_by_cell_index (db::cell_index_type cell_index) const
|
||||
{
|
||||
return m_circuit_by_cell_index.object_by (cell_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the top-down circuits iterator (begin)
|
||||
* This iterator will deliver the circuits in a top-down way - i.e. child circuits
|
||||
* will always come after parent circuits.
|
||||
* The first "top_circuit_count" elements will be top circuits (those which are not
|
||||
* referenced by other circuits).
|
||||
*/
|
||||
top_down_circuit_iterator begin_top_down ();
|
||||
|
||||
/**
|
||||
* @brief Gets the top-down circuits iterator (end)
|
||||
*/
|
||||
top_down_circuit_iterator end_top_down ();
|
||||
|
||||
/**
|
||||
* @brief Gets the top-down circuits iterator (begin, const version)
|
||||
* This iterator will deliver the circuits in a top-down way - i.e. child circuits
|
||||
* will always come after parent circuits.
|
||||
* The first "top_circuit_count" elements will be top circuits (those which are not
|
||||
* referenced by other circuits).
|
||||
*/
|
||||
const_top_down_circuit_iterator begin_top_down () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the top-down circuits iterator (end, const version)
|
||||
*/
|
||||
const_top_down_circuit_iterator end_top_down () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the number of top circuits
|
||||
* Top circuits are those which are not referenced by other circuits.
|
||||
* In a well-formed netlist there is a single top-level circuit.
|
||||
*/
|
||||
size_t top_circuit_count () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the bottom-up circuits iterator (begin)
|
||||
* This iterator will deliver the circuits in a bottom-up way - i.e. child circuits
|
||||
* will always come before parent circuits.
|
||||
*/
|
||||
bottom_up_circuit_iterator begin_bottom_up ();
|
||||
|
||||
/**
|
||||
* @brief Gets the bottom-up circuits iterator (end)
|
||||
*/
|
||||
bottom_up_circuit_iterator end_bottom_up ();
|
||||
|
||||
/**
|
||||
* @brief Gets the bottom-up circuits iterator (begin, const version)
|
||||
* This iterator will deliver the circuits in a bottom-up way - i.e. child circuits
|
||||
* will always come before parent circuits.
|
||||
*/
|
||||
const_bottom_up_circuit_iterator begin_bottom_up () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the bottom-up circuits iterator (end, const version)
|
||||
*/
|
||||
const_bottom_up_circuit_iterator end_bottom_up () const;
|
||||
|
||||
/**
|
||||
* @brief Adds a device class to this netlist
|
||||
*
|
||||
* The netlist takes over ownership of the object.
|
||||
*/
|
||||
void add_device_class (DeviceClass *device_class);
|
||||
|
||||
/**
|
||||
* @brief Deletes a device class from the netlist
|
||||
*/
|
||||
void remove_device_class (DeviceClass *device_class);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the device classes of the netlist (non-const version)
|
||||
*/
|
||||
device_class_iterator begin_device_classes ()
|
||||
{
|
||||
return m_device_classes.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the device classes of the netlist (non-const version)
|
||||
*/
|
||||
device_class_iterator end_device_classes ()
|
||||
{
|
||||
return m_device_classes.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the device classes of the netlist (const version)
|
||||
*/
|
||||
const_device_class_iterator begin_device_classes () const
|
||||
{
|
||||
return m_device_classes.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the device classes of the netlist (const version)
|
||||
*/
|
||||
const_device_class_iterator end_device_classes () const
|
||||
{
|
||||
return m_device_classes.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a device abstract to this netlist
|
||||
*
|
||||
* The netlist takes over ownership of the object.
|
||||
*/
|
||||
void add_device_abstract (DeviceAbstract *device_abstract);
|
||||
|
||||
/**
|
||||
* @brief Deletes a device abstract from the netlist
|
||||
*/
|
||||
void remove_device_abstract (DeviceAbstract *device_abstract);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the device abstracts of the netlist (non-const version)
|
||||
*/
|
||||
device_abstract_iterator begin_device_abstracts ()
|
||||
{
|
||||
return m_device_abstracts.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the device abstracts of the netlist (non-const version)
|
||||
*/
|
||||
device_abstract_iterator end_device_abstracts ()
|
||||
{
|
||||
return m_device_abstracts.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the device abstracts of the netlist (const version)
|
||||
*/
|
||||
const_abstract_model_iterator begin_device_abstracts () const
|
||||
{
|
||||
return m_device_abstracts.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the device abstracts of the netlist (const version)
|
||||
*/
|
||||
const_abstract_model_iterator end_device_abstracts () const
|
||||
{
|
||||
return m_device_abstracts.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device abstract with the given name
|
||||
*
|
||||
* If no device abstract with that name exists, null is returned.
|
||||
*/
|
||||
DeviceAbstract *device_abstract_by_name (const std::string &name)
|
||||
{
|
||||
return m_device_abstract_by_name.object_by (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device abstract with the given name (const version)
|
||||
*
|
||||
* If no device abstract with that name exists, null is returned.
|
||||
*/
|
||||
const DeviceAbstract *device_abstract_by_name (const std::string &name) const
|
||||
{
|
||||
return m_device_abstract_by_name.object_by (name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device abstract with the given cell index
|
||||
*
|
||||
* If no device abstract with that cell index exists, null is returned.
|
||||
*/
|
||||
DeviceAbstract *device_abstract_by_cell_index (db::cell_index_type cell_index)
|
||||
{
|
||||
return m_device_abstract_by_cell_index.object_by (cell_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device abstract with the given cell index (const version)
|
||||
*
|
||||
* If no device abstract with that cell index exists, null is returned.
|
||||
*/
|
||||
const DeviceAbstract *device_abstract_by_cell_index (db::cell_index_type cell_index) const
|
||||
{
|
||||
return m_device_abstract_by_cell_index.object_by (cell_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Purge unused nets
|
||||
*
|
||||
* This method will purge all nets which return "floating".
|
||||
*/
|
||||
void purge_nets ();
|
||||
|
||||
/**
|
||||
* @brief Creates pins for top-level circuits
|
||||
*
|
||||
* This method will turn all named nets of top-level circuits (such that are not
|
||||
* referenced by subcircuits) into pins. This method can be used before purge to
|
||||
* avoid that purge will remove nets which are directly connecting to subcircuits.
|
||||
*/
|
||||
void make_top_level_pins ();
|
||||
|
||||
/**
|
||||
* @brief Purge unused nets, circuits and subcircuits
|
||||
*
|
||||
* This method will purge all nets which return "floating". Circuits which don't have any
|
||||
* nets (or only floating ones) and removed. Their subcircuits are disconnected.
|
||||
*/
|
||||
void purge ();
|
||||
|
||||
/**
|
||||
* @brief Combine devices
|
||||
*
|
||||
* This method will combine devices that can be combined according
|
||||
* to their device classes "combine_devices" method.
|
||||
*/
|
||||
void combine_devices ();
|
||||
|
||||
private:
|
||||
friend class Circuit;
|
||||
friend class DeviceAbstract;
|
||||
|
||||
circuit_list m_circuits;
|
||||
device_class_list m_device_classes;
|
||||
device_abstract_list m_device_abstracts;
|
||||
bool m_valid_topology;
|
||||
int m_lock_count;
|
||||
tl::vector<Circuit *> m_top_down_circuits;
|
||||
tl::vector<tl::vector<Circuit *> > m_child_circuits;
|
||||
tl::vector<tl::vector<Circuit *> > m_parent_circuits;
|
||||
size_t m_top_circuits;
|
||||
object_by_attr<Netlist, Netlist::circuit_iterator, name_attribute<Circuit> > m_circuit_by_name;
|
||||
object_by_attr<Netlist, Netlist::circuit_iterator, cell_index_attribute<Circuit> > m_circuit_by_cell_index;
|
||||
object_by_attr<Netlist, Netlist::device_abstract_iterator, name_attribute<DeviceAbstract> > m_device_abstract_by_name;
|
||||
object_by_attr<Netlist, Netlist::device_abstract_iterator, cell_index_attribute<DeviceAbstract> > m_device_abstract_by_cell_index;
|
||||
|
||||
void invalidate_topology ();
|
||||
void validate_topology ();
|
||||
void circuits_changed ();
|
||||
void device_abstracts_changed ();
|
||||
|
||||
const tl::vector<Circuit *> &child_circuits (Circuit *circuit);
|
||||
const tl::vector<Circuit *> &parent_circuits (Circuit *circuit);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class using RAII for safe locking/unlocking
|
||||
*/
|
||||
class DB_PUBLIC NetlistLocker
|
||||
{
|
||||
public:
|
||||
NetlistLocker (Netlist *netlist)
|
||||
: mp_netlist (netlist)
|
||||
{
|
||||
if (mp_netlist.get ()) {
|
||||
mp_netlist->lock ();
|
||||
}
|
||||
}
|
||||
|
||||
~NetlistLocker ()
|
||||
{
|
||||
if (mp_netlist.get ()) {
|
||||
mp_netlist->unlock ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tl::weak_ptr<Netlist> mp_netlist;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbNetlistDeviceClasses.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassTwoTerminalDevice implementation
|
||||
|
||||
bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const
|
||||
{
|
||||
db::Net *na1 = a->net_for_terminal (0);
|
||||
db::Net *na2 = a->net_for_terminal (1);
|
||||
db::Net *nb1 = b->net_for_terminal (0);
|
||||
db::Net *nb2 = b->net_for_terminal (1);
|
||||
|
||||
bool res = true;
|
||||
|
||||
if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) {
|
||||
|
||||
parallel (a, b);
|
||||
|
||||
} else if ((na2 == nb1 || na2 == nb2) && na2->is_internal ()) {
|
||||
|
||||
// serial a(B) to b(A or B)
|
||||
serial (a, b);
|
||||
a->connect_terminal (1, (na2 == nb1 ? nb2 : nb1));
|
||||
|
||||
} else if ((na1 == nb1 || na1 == nb2) && na1->is_internal ()) {
|
||||
|
||||
// serial a(A) to b(A or B)
|
||||
serial (a, b);
|
||||
a->connect_terminal (0, (na1 == nb1 ? nb2 : nb1));
|
||||
|
||||
}
|
||||
|
||||
if (res) {
|
||||
b->connect_terminal (0, 0);
|
||||
b->connect_terminal (1, 0);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassResistor implementation
|
||||
|
||||
DB_PUBLIC size_t DeviceClassResistor::param_id_R = 0;
|
||||
|
||||
DB_PUBLIC size_t DeviceClassResistor::terminal_id_A = 0;
|
||||
DB_PUBLIC size_t DeviceClassResistor::terminal_id_B = 1;
|
||||
|
||||
DeviceClassResistor::DeviceClassResistor ()
|
||||
{
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B"));
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0));
|
||||
}
|
||||
|
||||
void DeviceClassResistor::parallel (Device *a, Device *b) const
|
||||
{
|
||||
double va = a->parameter_value (0);
|
||||
double vb = b->parameter_value (0);
|
||||
a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb));
|
||||
}
|
||||
|
||||
void DeviceClassResistor::serial (Device *a, Device *b) const
|
||||
{
|
||||
double va = a->parameter_value (0);
|
||||
double vb = b->parameter_value (0);
|
||||
a->set_parameter_value (0, va + vb);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassCapacitor implementation
|
||||
|
||||
DB_PUBLIC size_t DeviceClassCapacitor::param_id_C = 0;
|
||||
|
||||
DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_A = 0;
|
||||
DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_B = 1;
|
||||
|
||||
DeviceClassCapacitor::DeviceClassCapacitor ()
|
||||
{
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B"));
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0));
|
||||
}
|
||||
|
||||
void DeviceClassCapacitor::serial (Device *a, Device *b) const
|
||||
{
|
||||
double va = a->parameter_value (0);
|
||||
double vb = b->parameter_value (0);
|
||||
a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb));
|
||||
}
|
||||
|
||||
void DeviceClassCapacitor::parallel (Device *a, Device *b) const
|
||||
{
|
||||
double va = a->parameter_value (0);
|
||||
double vb = b->parameter_value (0);
|
||||
a->set_parameter_value (0, va + vb);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassInductor implementation
|
||||
|
||||
DB_PUBLIC size_t DeviceClassInductor::param_id_L = 0;
|
||||
|
||||
DB_PUBLIC size_t DeviceClassInductor::terminal_id_A = 0;
|
||||
DB_PUBLIC size_t DeviceClassInductor::terminal_id_B = 1;
|
||||
|
||||
DeviceClassInductor::DeviceClassInductor ()
|
||||
{
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B"));
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("L", "Inductance (Henry)", 0.0));
|
||||
}
|
||||
|
||||
void DeviceClassInductor::parallel (Device *a, Device *b) const
|
||||
{
|
||||
double va = a->parameter_value (0);
|
||||
double vb = b->parameter_value (0);
|
||||
a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb));
|
||||
}
|
||||
|
||||
void DeviceClassInductor::serial (Device *a, Device *b) const
|
||||
{
|
||||
double va = a->parameter_value (0);
|
||||
double vb = b->parameter_value (0);
|
||||
a->set_parameter_value (0, va + vb);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassInductor implementation
|
||||
|
||||
DB_PUBLIC size_t DeviceClassDiode::param_id_A = 0;
|
||||
|
||||
DB_PUBLIC size_t DeviceClassDiode::terminal_id_A = 0;
|
||||
DB_PUBLIC size_t DeviceClassDiode::terminal_id_C = 1;
|
||||
|
||||
DeviceClassDiode::DeviceClassDiode ()
|
||||
{
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode"));
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0));
|
||||
}
|
||||
|
||||
bool DeviceClassDiode::combine_devices (Device *a, Device *b) const
|
||||
{
|
||||
const db::Net *na1 = a->net_for_terminal (0);
|
||||
const db::Net *na2 = a->net_for_terminal (1);
|
||||
const db::Net *nb1 = b->net_for_terminal (0);
|
||||
const db::Net *nb2 = b->net_for_terminal (1);
|
||||
|
||||
// only parallel diodes can be combined and their areas will add
|
||||
if (na1 == nb1 && na2 == nb2) {
|
||||
|
||||
a->set_parameter_value (0, a->parameter_value (0) + b->parameter_value (0));
|
||||
b->connect_terminal (0, 0);
|
||||
b->connect_terminal (1, 0);
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassMOS3Transistor implementation
|
||||
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_L = 0;
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_W = 1;
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AS = 2;
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_AD = 3;
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_PS = 4;
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::param_id_PD = 5;
|
||||
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_S = 0;
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_G = 1;
|
||||
DB_PUBLIC size_t DeviceClassMOS3Transistor::terminal_id_D = 2;
|
||||
|
||||
DeviceClassMOS3Transistor::DeviceClassMOS3Transistor ()
|
||||
{
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain"));
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0));
|
||||
}
|
||||
|
||||
bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const
|
||||
{
|
||||
const db::Net *nas = a->net_for_terminal (0);
|
||||
const db::Net *nag = a->net_for_terminal (1);
|
||||
const db::Net *nad = a->net_for_terminal (2);
|
||||
const db::Net *nbs = b->net_for_terminal (0);
|
||||
const db::Net *nbg = b->net_for_terminal (1);
|
||||
const db::Net *nbd = b->net_for_terminal (2);
|
||||
|
||||
// parallel transistors can be combined into one
|
||||
if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg) {
|
||||
|
||||
// for combination the gate length must be identical
|
||||
if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) {
|
||||
|
||||
combine_parameters (a, b);
|
||||
|
||||
b->connect_terminal (0, 0);
|
||||
b->connect_terminal (1, 0);
|
||||
b->connect_terminal (2, 0);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeviceClassMOS3Transistor::combine_parameters (Device *a, Device *b) const
|
||||
{
|
||||
a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1));
|
||||
a->set_parameter_value (2, a->parameter_value (2) + b->parameter_value (2));
|
||||
a->set_parameter_value (3, a->parameter_value (3) + b->parameter_value (3));
|
||||
a->set_parameter_value (4, a->parameter_value (4) + b->parameter_value (4));
|
||||
a->set_parameter_value (5, a->parameter_value (5) + b->parameter_value (5));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassMOS4Transistor implementation
|
||||
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_B = 3;
|
||||
|
||||
DeviceClassMOS4Transistor::DeviceClassMOS4Transistor ()
|
||||
{
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Bulk"));
|
||||
}
|
||||
|
||||
bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const
|
||||
{
|
||||
const db::Net *nas = a->net_for_terminal (0);
|
||||
const db::Net *nag = a->net_for_terminal (1);
|
||||
const db::Net *nad = a->net_for_terminal (2);
|
||||
const db::Net *nab = a->net_for_terminal (3);
|
||||
const db::Net *nbs = b->net_for_terminal (0);
|
||||
const db::Net *nbg = b->net_for_terminal (1);
|
||||
const db::Net *nbd = b->net_for_terminal (2);
|
||||
const db::Net *nbb = b->net_for_terminal (3);
|
||||
|
||||
// parallel transistors can be combined into one
|
||||
if (((nas == nbs && nad == nbd) || (nas == nbd && nad == nbs)) && nag == nbg && nab == nbb) {
|
||||
|
||||
// for combination the gate length must be identical
|
||||
if (fabs (a->parameter_value (0) - b->parameter_value (0)) < 1e-6) {
|
||||
|
||||
combine_parameters (a, b);
|
||||
|
||||
b->connect_terminal (0, 0);
|
||||
b->connect_terminal (1, 0);
|
||||
b->connect_terminal (2, 0);
|
||||
b->connect_terminal (3, 0);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlistDeviceClasses
|
||||
#define _HDR_dbNetlistDeviceClasses
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbNetlist.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A basic two-terminal device class
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassTwoTerminalDevice
|
||||
: public db::DeviceClass
|
||||
{
|
||||
public:
|
||||
virtual bool combine_devices (Device *a, Device *b) const;
|
||||
|
||||
virtual void parallel (Device *a, Device *b) const = 0;
|
||||
virtual void serial (Device *a, Device *b) const = 0;
|
||||
virtual bool supports_parallel_combination () const { return true; }
|
||||
virtual bool supports_serial_combination () const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A basic resistor device class
|
||||
* A resistor defines a single parameter, "R", which is the resistance in Ohm.
|
||||
* It defines two terminals, "A" and "B" for the two terminals.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassResistor
|
||||
: public db::DeviceClassTwoTerminalDevice
|
||||
{
|
||||
public:
|
||||
DeviceClassResistor ();
|
||||
|
||||
virtual db::DeviceClass *clone () const
|
||||
{
|
||||
return new DeviceClassResistor (*this);
|
||||
}
|
||||
|
||||
static size_t param_id_R;
|
||||
|
||||
static size_t terminal_id_A;
|
||||
static size_t terminal_id_B;
|
||||
|
||||
virtual void parallel (Device *a, Device *b) const;
|
||||
virtual void serial (Device *a, Device *b) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A basic capacitor device class
|
||||
* A capacitor defines a single parameter, "C", which is the capacitance in Farad.
|
||||
* It defines two terminals, "A" and "B" for the two terminals.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassCapacitor
|
||||
: public db::DeviceClassTwoTerminalDevice
|
||||
{
|
||||
public:
|
||||
DeviceClassCapacitor ();
|
||||
|
||||
virtual db::DeviceClass *clone () const
|
||||
{
|
||||
return new DeviceClassCapacitor (*this);
|
||||
}
|
||||
|
||||
static size_t param_id_C;
|
||||
|
||||
static size_t terminal_id_A;
|
||||
static size_t terminal_id_B;
|
||||
|
||||
virtual void parallel (Device *a, Device *b) const;
|
||||
virtual void serial (Device *a, Device *b) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A basic inductor device class
|
||||
* An inductor defines a single parameter, "L", which is the inductance in Henry.
|
||||
* It defines two terminals, "A" and "B" for the two terminals.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassInductor
|
||||
: public db::DeviceClassTwoTerminalDevice
|
||||
{
|
||||
public:
|
||||
DeviceClassInductor ();
|
||||
|
||||
virtual db::DeviceClass *clone () const
|
||||
{
|
||||
return new DeviceClassInductor (*this);
|
||||
}
|
||||
|
||||
static size_t param_id_L;
|
||||
|
||||
static size_t terminal_id_A;
|
||||
static size_t terminal_id_B;
|
||||
|
||||
virtual void parallel (Device *a, Device *b) const;
|
||||
virtual void serial (Device *a, Device *b) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A basic diode device class
|
||||
* A diode defines a single parameter, "A", which is the area in square micrometers (YES: micrometers, as this is the basic unit of measure
|
||||
* in KLayout).
|
||||
* It defines two terminals, "A" and "C" for anode and cathode.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassDiode
|
||||
: public db::DeviceClass
|
||||
{
|
||||
public:
|
||||
DeviceClassDiode ();
|
||||
|
||||
static size_t param_id_A;
|
||||
|
||||
static size_t terminal_id_A;
|
||||
static size_t terminal_id_C;
|
||||
|
||||
virtual db::DeviceClass *clone () const
|
||||
{
|
||||
return new DeviceClassDiode (*this);
|
||||
}
|
||||
|
||||
virtual bool combine_devices (Device *a, Device *b) const;
|
||||
virtual bool supports_parallel_combination () const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A basic MOSFET device class with three terminals
|
||||
* A MOSFET defines six parameters: "W" for the gate width in micrometers, "L" for the gate length in micrometers,
|
||||
* "AS" for the source area and "AD" for the drain area and "PS" and "PD" for the source and drain perimeter.
|
||||
* The MOSFET device defines three terminals, "S", "D" and "G" for source, drain and gate.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassMOS3Transistor
|
||||
: public db::DeviceClass
|
||||
{
|
||||
public:
|
||||
DeviceClassMOS3Transistor ();
|
||||
|
||||
static size_t param_id_L;
|
||||
static size_t param_id_W;
|
||||
static size_t param_id_AS;
|
||||
static size_t param_id_AD;
|
||||
static size_t param_id_PS;
|
||||
static size_t param_id_PD;
|
||||
|
||||
static size_t terminal_id_S;
|
||||
static size_t terminal_id_G;
|
||||
static size_t terminal_id_D;
|
||||
|
||||
virtual db::DeviceClass *clone () const
|
||||
{
|
||||
return new DeviceClassMOS3Transistor (*this);
|
||||
}
|
||||
|
||||
virtual bool combine_devices (Device *a, Device *b) const;
|
||||
virtual bool supports_parallel_combination () const { return true; }
|
||||
|
||||
protected:
|
||||
void combine_parameters (Device *a, Device *b) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A basic MOSFET device class with four terminals
|
||||
* The four-terminal MOSFET behaves identical to the three-terminal one but adds one more
|
||||
* terminal for the bulk.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassMOS4Transistor
|
||||
: public DeviceClassMOS3Transistor
|
||||
{
|
||||
public:
|
||||
DeviceClassMOS4Transistor ();
|
||||
|
||||
static size_t terminal_id_B;
|
||||
|
||||
virtual db::DeviceClass *clone () const
|
||||
{
|
||||
return new DeviceClassMOS4Transistor (*this);
|
||||
}
|
||||
|
||||
virtual bool combine_devices (Device *a, Device *b) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,567 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbNetlistDeviceExtractor.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbHierNetworkProcessor.h"
|
||||
#include "dbDeepRegion.h"
|
||||
|
||||
#include "tlProgress.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlInternational.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// NetlistDeviceExtractorError implementation
|
||||
|
||||
NetlistDeviceExtractorError::NetlistDeviceExtractorError ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetlistDeviceExtractorError::NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg)
|
||||
: m_cell_name (cell_name), m_message (msg)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
std::string NetlistDeviceExtractorError::to_string () const
|
||||
{
|
||||
std::string res;
|
||||
|
||||
if (! m_category_name.empty ()) {
|
||||
if (m_category_description.empty ()) {
|
||||
res += "[" + m_category_name + "] ";
|
||||
} else {
|
||||
res += "[" + m_category_description + "] ";
|
||||
}
|
||||
}
|
||||
|
||||
res += m_message;
|
||||
|
||||
if (! m_cell_name.empty ()) {
|
||||
res += tl::to_string (tr (", in cell: ")) + m_cell_name;
|
||||
}
|
||||
|
||||
if (! m_geometry.box ().empty ()) {
|
||||
res += tl::to_string (tr (", shape: ")) + m_geometry.to_string ();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// NetlistDeviceExtractor implementation
|
||||
|
||||
NetlistDeviceExtractor::NetlistDeviceExtractor (const std::string &name)
|
||||
: mp_layout (0), m_cell_index (0), mp_circuit (0)
|
||||
{
|
||||
m_name = name;
|
||||
m_terminal_id_propname_id = 0;
|
||||
m_device_class_propname_id = 0;
|
||||
m_device_id_propname_id = 0;
|
||||
}
|
||||
|
||||
NetlistDeviceExtractor::~NetlistDeviceExtractor ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
const tl::Variant &NetlistDeviceExtractor::terminal_id_property_name ()
|
||||
{
|
||||
static tl::Variant name ("TERMINAL_ID");
|
||||
return name;
|
||||
}
|
||||
|
||||
const tl::Variant &NetlistDeviceExtractor::device_id_property_name ()
|
||||
{
|
||||
static tl::Variant name ("DEVICE_ID");
|
||||
return name;
|
||||
}
|
||||
|
||||
const tl::Variant &NetlistDeviceExtractor::device_class_property_name ()
|
||||
{
|
||||
static tl::Variant name ("DEVICE_CLASS");
|
||||
return name;
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::initialize (db::Netlist *nl)
|
||||
{
|
||||
m_layer_definitions.clear ();
|
||||
mp_device_class = 0;
|
||||
m_terminal_id_propname_id = 0;
|
||||
m_device_id_propname_id = 0;
|
||||
m_device_class_propname_id = 0;
|
||||
m_netlist.reset (nl);
|
||||
|
||||
setup ();
|
||||
}
|
||||
|
||||
static void insert_into_region (const db::PolygonRef &s, const db::ICplxTrans &tr, db::Region ®ion)
|
||||
{
|
||||
region.insert (s.obj ().transformed (tr * db::ICplxTrans (s.trans ())));
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layout_index, const NetlistDeviceExtractor::input_layers &layer_map, db::Netlist &nl, hier_clusters_type &clusters)
|
||||
{
|
||||
initialize (&nl);
|
||||
|
||||
std::vector<unsigned int> layers;
|
||||
layers.reserve (m_layer_definitions.size ());
|
||||
|
||||
for (layer_definitions::const_iterator ld = begin_layer_definitions (); ld != end_layer_definitions (); ++ld) {
|
||||
|
||||
input_layers::const_iterator l = layer_map.find (ld->name);
|
||||
if (l == layer_map.end ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Missing input layer for device extraction: ")) + ld->name);
|
||||
}
|
||||
|
||||
tl_assert (l->second != 0);
|
||||
db::DeepRegion *dr = dynamic_cast<db::DeepRegion *> (l->second->delegate ());
|
||||
if (dr == 0) {
|
||||
|
||||
std::pair<bool, db::DeepLayer> alias = dss.layer_for_flat (tl::id_of (l->second->delegate ()));
|
||||
if (alias.first) {
|
||||
// use deep layer alias for a given flat one (if found)
|
||||
layers.push_back (alias.second.layer ());
|
||||
} else if (l->second->empty ()) {
|
||||
// provide a substitute empty layer
|
||||
layers.push_back (dss.empty_layer (layout_index).layer ());
|
||||
} else {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): must be of deep region kind")), ld->name, name ()));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (&dr->deep_layer ().layout () != &dss.layout (layout_index) || &dr->deep_layer ().initial_cell () != &dss.initial_cell (layout_index)) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): not originating from the same source")), ld->name, name ()));
|
||||
}
|
||||
|
||||
layers.push_back (dr->deep_layer ().layer ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extract_without_initialize (dss.layout (layout_index), dss.initial_cell (layout_index), clusters, layers);
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector<unsigned int> &layers, db::Netlist *nl, hier_clusters_type &clusters)
|
||||
{
|
||||
initialize (nl);
|
||||
extract_without_initialize (layout, cell, clusters, layers);
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector<unsigned int> &layers)
|
||||
{
|
||||
tl_assert (layers.size () == m_layer_definitions.size ());
|
||||
|
||||
typedef db::PolygonRef shape_type;
|
||||
db::ShapeIterator::flags_type shape_iter_flags = db::ShapeIterator::Polygons;
|
||||
|
||||
mp_layout = &layout;
|
||||
m_layers = layers;
|
||||
mp_clusters = &clusters;
|
||||
|
||||
// terminal properties are kept in a property with the terminal_property_name name
|
||||
m_terminal_id_propname_id = mp_layout->properties_repository ().prop_name_id (terminal_id_property_name ());
|
||||
m_device_id_propname_id = mp_layout->properties_repository ().prop_name_id (device_id_property_name ());
|
||||
m_device_class_propname_id = mp_layout->properties_repository ().prop_name_id (device_class_property_name ());
|
||||
|
||||
tl_assert (m_netlist.get () != 0);
|
||||
|
||||
// build a cell-id-to-circuit lookup table
|
||||
std::map<db::cell_index_type, db::Circuit *> circuits_by_cell;
|
||||
for (db::Netlist::circuit_iterator c = m_netlist->begin_circuits (); c != m_netlist->end_circuits (); ++c) {
|
||||
circuits_by_cell.insert (std::make_pair (c->cell_index (), c.operator-> ()));
|
||||
}
|
||||
|
||||
// collect the cells below the top cell
|
||||
std::set<db::cell_index_type> all_called_cells;
|
||||
all_called_cells.insert (cell.cell_index ());
|
||||
cell.collect_called_cells (all_called_cells);
|
||||
|
||||
// ignore device cells from previous extractions
|
||||
std::set<db::cell_index_type> called_cells;
|
||||
for (std::set<db::cell_index_type>::const_iterator ci = all_called_cells.begin (); ci != all_called_cells.end (); ++ci) {
|
||||
if (! m_netlist->device_abstract_by_cell_index (*ci)) {
|
||||
called_cells.insert (*ci);
|
||||
}
|
||||
}
|
||||
all_called_cells.clear ();
|
||||
|
||||
// build the device clusters
|
||||
|
||||
db::Connectivity device_conn = get_connectivity (layout, layers);
|
||||
db::hier_clusters<shape_type> device_clusters;
|
||||
device_clusters.build (layout, cell, shape_iter_flags, device_conn);
|
||||
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Extracting devices")));
|
||||
|
||||
// count effort and make a progress reporter
|
||||
|
||||
size_t n = 0;
|
||||
for (std::set<db::cell_index_type>::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) {
|
||||
db::connected_clusters<shape_type> cc = device_clusters.clusters_per_cell (*ci);
|
||||
for (db::connected_clusters<shape_type>::all_iterator c = cc.begin_all (); !c.at_end(); ++c) {
|
||||
if (cc.is_root (*c)) {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tl::RelativeProgress progress (tl::to_string (tr ("Extracting devices")), n, 1);
|
||||
|
||||
struct ExtractorCacheValueType {
|
||||
ExtractorCacheValueType () { }
|
||||
db::Vector disp;
|
||||
tl::vector<db::Device *> devices;
|
||||
};
|
||||
|
||||
typedef std::map<std::vector<db::Region>, ExtractorCacheValueType> extractor_cache_type;
|
||||
extractor_cache_type extractor_cache;
|
||||
|
||||
// for each cell investigate the clusters
|
||||
for (std::set<db::cell_index_type>::const_iterator ci = called_cells.begin (); ci != called_cells.end (); ++ci) {
|
||||
|
||||
m_cell_index = *ci;
|
||||
|
||||
std::map<db::cell_index_type, db::Circuit *>::const_iterator c2c = circuits_by_cell.find (*ci);
|
||||
if (c2c != circuits_by_cell.end ()) {
|
||||
|
||||
// reuse existing circuit
|
||||
mp_circuit = c2c->second;
|
||||
|
||||
} else {
|
||||
|
||||
// create a new circuit for this cell
|
||||
mp_circuit = new db::Circuit ();
|
||||
mp_circuit->set_cell_index (*ci);
|
||||
mp_circuit->set_name (layout.cell_name (*ci));
|
||||
m_netlist->add_circuit (mp_circuit);
|
||||
|
||||
}
|
||||
|
||||
// investigate each cluster
|
||||
db::connected_clusters<shape_type> cc = device_clusters.clusters_per_cell (*ci);
|
||||
for (db::connected_clusters<shape_type>::all_iterator c = cc.begin_all (); !c.at_end(); ++c) {
|
||||
|
||||
// take only root clusters - others have upward connections and are not "whole"
|
||||
if (! cc.is_root (*c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++progress;
|
||||
|
||||
// build layer geometry from the cluster found
|
||||
|
||||
std::vector<db::Region> layer_geometry;
|
||||
layer_geometry.resize (layers.size ());
|
||||
|
||||
for (std::vector<unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
db::Region &r = layer_geometry [l - layers.begin ()];
|
||||
for (db::recursive_cluster_shape_iterator<shape_type> si (device_clusters, *l, *ci, *c); ! si.at_end(); ++si) {
|
||||
insert_into_region (*si, si.trans (), r);
|
||||
}
|
||||
r.set_base_verbosity (50);
|
||||
}
|
||||
|
||||
db::Box box;
|
||||
for (std::vector<db::Region>::const_iterator g = layer_geometry.begin (); g != layer_geometry.end (); ++g) {
|
||||
box += g->bbox ();
|
||||
}
|
||||
|
||||
db::Vector disp = box.p1 () - db::Point ();
|
||||
for (std::vector<db::Region>::iterator g = layer_geometry.begin (); g != layer_geometry.end (); ++g) {
|
||||
g->transform (db::Disp (-disp));
|
||||
}
|
||||
|
||||
extractor_cache_type::const_iterator ec = extractor_cache.find (layer_geometry);
|
||||
if (ec == extractor_cache.end ()) {
|
||||
|
||||
// do the actual device extraction
|
||||
extract_devices (layer_geometry);
|
||||
|
||||
// push the new devices to the layout
|
||||
push_new_devices (disp);
|
||||
|
||||
ExtractorCacheValueType &ecv = extractor_cache [layer_geometry];
|
||||
ecv.disp = disp;
|
||||
|
||||
for (std::map<size_t, std::pair<db::Device *, geometry_per_terminal_type> >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) {
|
||||
ecv.devices.push_back (d->second.first);
|
||||
}
|
||||
|
||||
m_new_devices.clear ();
|
||||
|
||||
} else {
|
||||
|
||||
push_cached_devices (ec->second.devices, ec->second.disp, disp);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache)
|
||||
{
|
||||
db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ());
|
||||
db::VCplxTrans dbu_inv = dbu.inverted ();
|
||||
|
||||
for (std::map<size_t, std::pair<db::Device *, geometry_per_terminal_type> >::const_iterator d = m_new_devices.begin (); d != m_new_devices.end (); ++d) {
|
||||
|
||||
db::Device *device = d->second.first;
|
||||
|
||||
db::Vector disp = dbu_inv * device->position () - db::Point ();
|
||||
device->set_position (device->position () + dbu * disp_cache);
|
||||
|
||||
DeviceCellKey key;
|
||||
|
||||
for (geometry_per_terminal_type::const_iterator t = d->second.second.begin (); t != d->second.second.end (); ++t) {
|
||||
std::map<unsigned int, std::set<db::PolygonRef> > > = key.geometry [t->first];
|
||||
for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) {
|
||||
std::set<db::PolygonRef> &gl = gt [l->first];
|
||||
for (std::vector<db::PolygonRef>::const_iterator p = l->second.begin (); p != l->second.end (); ++p) {
|
||||
db::PolygonRef pr = *p;
|
||||
pr.transform (db::PolygonRef::trans_type (-disp));
|
||||
gl.insert (pr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = mp_device_class->parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
|
||||
key.parameters.insert (std::make_pair (p->id (), device->parameter_value (p->id ())));
|
||||
}
|
||||
|
||||
db::PropertiesRepository::properties_set ps;
|
||||
|
||||
std::map<DeviceCellKey, std::pair<db::cell_index_type, db::DeviceAbstract *> >::iterator c = m_device_cells.find (key);
|
||||
if (c == m_device_cells.end ()) {
|
||||
|
||||
std::string cell_name = "D$" + mp_device_class->name ();
|
||||
db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ()));
|
||||
|
||||
db::DeviceAbstract *dm = new db::DeviceAbstract (mp_device_class, mp_layout->cell_name (device_cell.cell_index ()));
|
||||
m_netlist->add_device_abstract (dm);
|
||||
dm->set_cell_index (device_cell.cell_index ());
|
||||
|
||||
c = m_device_cells.insert (std::make_pair (key, std::make_pair (device_cell.cell_index (), dm))).first;
|
||||
|
||||
// attach the device class ID to the cell
|
||||
ps.clear ();
|
||||
ps.insert (std::make_pair (m_device_class_propname_id, tl::Variant (mp_device_class->name ())));
|
||||
device_cell.prop_id (mp_layout->properties_repository ().properties_id (ps));
|
||||
|
||||
for (geometry_per_terminal_type::const_iterator t = d->second.second.begin (); t != d->second.second.end (); ++t) {
|
||||
|
||||
// Build a property set for the device terminal ID
|
||||
ps.clear ();
|
||||
ps.insert (std::make_pair (m_terminal_id_propname_id, tl::Variant (t->first)));
|
||||
db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps);
|
||||
|
||||
// build the cell shapes
|
||||
for (geometry_per_layer_type::const_iterator l = t->second.begin (); l != t->second.end (); ++l) {
|
||||
|
||||
db::Shapes &shapes = device_cell.shapes (l->first);
|
||||
for (std::vector<db::PolygonRef>::const_iterator s = l->second.begin (); s != l->second.end (); ++s) {
|
||||
db::PolygonRef pr = *s;
|
||||
pr.transform (db::PolygonRef::trans_type (-disp));
|
||||
shapes.insert (db::PolygonRefWithProperties (pr, pi));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// make the cell index known to the device
|
||||
device->set_device_abstract (c->second.second);
|
||||
|
||||
// Build a property set for the device ID
|
||||
ps.clear ();
|
||||
ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (d->first)));
|
||||
db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps);
|
||||
|
||||
db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (c->second.first), db::Trans (disp_cache + disp)), pi);
|
||||
mp_layout->cell (m_cell_index).insert (inst);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::push_cached_devices (const tl::vector<db::Device *> &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp)
|
||||
{
|
||||
db::CplxTrans dbu = db::CplxTrans (mp_layout->dbu ());
|
||||
db::VCplxTrans dbu_inv = dbu.inverted ();
|
||||
db::PropertiesRepository::properties_set ps;
|
||||
|
||||
for (std::vector<db::Device *>::const_iterator d = cached_devices.begin (); d != cached_devices.end (); ++d) {
|
||||
|
||||
db::Device *cached_device = *d;
|
||||
db::Vector disp = dbu_inv * cached_device->position () - disp_cache - db::Point ();
|
||||
|
||||
db::Device *device = new db::Device (*cached_device);
|
||||
mp_circuit->add_device (device);
|
||||
device->set_position (device->position () + dbu * (new_disp - disp_cache));
|
||||
|
||||
// Build a property set for the device ID
|
||||
ps.clear ();
|
||||
ps.insert (std::make_pair (m_device_id_propname_id, tl::Variant (device->id ())));
|
||||
db::properties_id_type pi = mp_layout->properties_repository ().properties_id (ps);
|
||||
|
||||
db::CellInstArrayWithProperties inst (db::CellInstArray (db::CellInst (device->device_abstract ()->cell_index ()), db::Trans (new_disp + disp)), pi);
|
||||
mp_layout->cell (m_cell_index).insert (inst);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::setup ()
|
||||
{
|
||||
// .. the default implementation does nothing ..
|
||||
}
|
||||
|
||||
db::Connectivity NetlistDeviceExtractor::get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> & /*layers*/) const
|
||||
{
|
||||
// .. the default implementation does nothing ..
|
||||
return db::Connectivity ();
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::extract_devices (const std::vector<db::Region> & /*layer_geometry*/)
|
||||
{
|
||||
// .. the default implementation does nothing ..
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class)
|
||||
{
|
||||
if (mp_device_class != 0) {
|
||||
throw tl::Exception (tl::to_string (tr ("Device class already set")));
|
||||
}
|
||||
if (m_name.empty ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("No device extractor/device class name set")));
|
||||
}
|
||||
|
||||
tl_assert (device_class != 0);
|
||||
mp_device_class = device_class;
|
||||
mp_device_class->set_name (m_name);
|
||||
|
||||
tl_assert (m_netlist.get () != 0);
|
||||
m_netlist->add_device_class (device_class);
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::define_layer (const std::string &name, const std::string &description)
|
||||
{
|
||||
m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size ()));
|
||||
}
|
||||
|
||||
Device *NetlistDeviceExtractor::create_device ()
|
||||
{
|
||||
if (mp_device_class == 0) {
|
||||
throw tl::Exception (tl::to_string (tr ("No device class registered")));
|
||||
}
|
||||
|
||||
tl_assert (mp_circuit != 0);
|
||||
Device *device = new Device (mp_device_class);
|
||||
mp_circuit->add_device (device);
|
||||
return device;
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t geometry_index, const db::Polygon &polygon)
|
||||
{
|
||||
tl_assert (mp_layout != 0);
|
||||
tl_assert (geometry_index < m_layers.size ());
|
||||
unsigned int layer_index = m_layers [geometry_index];
|
||||
|
||||
db::PolygonRef pr (polygon, mp_layout->shape_repository ());
|
||||
std::pair<db::Device *, geometry_per_terminal_type> &dd = m_new_devices[device->id ()];
|
||||
dd.first = device;
|
||||
dd.second[terminal_id][layer_index].push_back (pr);
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box)
|
||||
{
|
||||
define_terminal (device, terminal_id, layer_index, db::Polygon (box));
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Point &point)
|
||||
{
|
||||
// NOTE: we add one DBU to the "point" to prevent it from vanishing
|
||||
db::Vector dv (1, 1);
|
||||
define_terminal (device, terminal_id, layer_index, db::Polygon (db::Box (point - dv, point + dv)));
|
||||
}
|
||||
|
||||
std::string NetlistDeviceExtractor::cell_name () const
|
||||
{
|
||||
if (layout ()) {
|
||||
return layout ()->cell_name (cell_index ());
|
||||
} else {
|
||||
return std::string ();
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::error (const std::string &msg)
|
||||
{
|
||||
m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg));
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::error << m_errors.back ().to_string ();
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::error (const std::string &msg, const db::DPolygon &poly)
|
||||
{
|
||||
m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg));
|
||||
m_errors.back ().set_geometry (poly);
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::error << m_errors.back ().to_string ();
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg)
|
||||
{
|
||||
m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg));
|
||||
m_errors.back ().set_category_name (category_name);
|
||||
m_errors.back ().set_category_description (category_description);
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::error << m_errors.back ().to_string ();
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly)
|
||||
{
|
||||
m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg));
|
||||
m_errors.back ().set_category_name (category_name);
|
||||
m_errors.back ().set_category_description (category_description);
|
||||
m_errors.back ().set_geometry (poly);
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::error << m_errors.back ().to_string ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,546 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlistDeviceExtractor
|
||||
#define _HDR_dbNetlistDeviceExtractor
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbLayout.h"
|
||||
#include "dbHierNetworkProcessor.h"
|
||||
#include "dbDeepShapeStore.h"
|
||||
#include "dbRegion.h"
|
||||
|
||||
#include "gsiObject.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief An error object for the netlist device extractor
|
||||
*
|
||||
* The device extractor will keep errors using objects of this kind.
|
||||
*/
|
||||
class DB_PUBLIC NetlistDeviceExtractorError
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates an error
|
||||
*/
|
||||
NetlistDeviceExtractorError ();
|
||||
|
||||
/**
|
||||
* @brief Creates an error with a cell name and a message (the minimum information)
|
||||
*/
|
||||
NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg);
|
||||
|
||||
/**
|
||||
* @brief The category name of the error
|
||||
* Specifying the category name is optional. If a category is given, it will be used for
|
||||
* the report.
|
||||
*/
|
||||
const std::string &category_name () const
|
||||
{
|
||||
return m_category_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the category name
|
||||
*/
|
||||
void set_category_name (const std::string &s)
|
||||
{
|
||||
m_category_name = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The category description of the error
|
||||
* Specifying the category description is optional. If a category is given, this attribute will
|
||||
* be used for the category description.
|
||||
*/
|
||||
const std::string &category_description () const
|
||||
{
|
||||
return m_category_description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the category description
|
||||
*/
|
||||
void set_category_description (const std::string &s)
|
||||
{
|
||||
m_category_description = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the geometry for this error
|
||||
* Not all errors may specify a geometry. In this case, the polygon is empty.
|
||||
*/
|
||||
const db::DPolygon &geometry () const
|
||||
{
|
||||
return m_geometry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the geometry
|
||||
*/
|
||||
void set_geometry (const db::DPolygon &g)
|
||||
{
|
||||
m_geometry = g;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the message for this error
|
||||
*/
|
||||
const std::string &message () const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the message
|
||||
*/
|
||||
void set_message (const std::string &n)
|
||||
{
|
||||
m_message = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the cell name the error occured in
|
||||
*/
|
||||
const std::string &cell_name () const
|
||||
{
|
||||
return m_cell_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the cell name
|
||||
*/
|
||||
void set_cell_name (const std::string &n)
|
||||
{
|
||||
m_cell_name = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Formats this message for printing
|
||||
*/
|
||||
std::string to_string () const;
|
||||
|
||||
private:
|
||||
std::string m_cell_name;
|
||||
std::string m_message;
|
||||
db::DPolygon m_geometry;
|
||||
std::string m_category_name, m_category_description;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Specifies a single layer from the device extractor
|
||||
*/
|
||||
class DB_PUBLIC NetlistDeviceExtractorLayerDefinition
|
||||
{
|
||||
public:
|
||||
NetlistDeviceExtractorLayerDefinition ()
|
||||
: index (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetlistDeviceExtractorLayerDefinition (const std::string &_name, const std::string &_description, size_t _index)
|
||||
: name (_name), description (_description), index (_index)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The formal name
|
||||
*/
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* @brief The human-readable description
|
||||
*/
|
||||
std::string description;
|
||||
|
||||
/**
|
||||
* @brief The index of the layer
|
||||
*/
|
||||
size_t index;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements the device extraction for a specific setup
|
||||
*
|
||||
* This class can be reimplemented to provide the basic algorithms for
|
||||
* device extraction. See the virtual methods below.
|
||||
*/
|
||||
class DB_PUBLIC NetlistDeviceExtractor
|
||||
: public gsi::ObjectBase, public tl::Object
|
||||
{
|
||||
public:
|
||||
typedef std::list<db::NetlistDeviceExtractorError> error_list;
|
||||
typedef error_list::const_iterator error_iterator;
|
||||
typedef std::vector<db::NetlistDeviceExtractorLayerDefinition> layer_definitions;
|
||||
typedef layer_definitions::const_iterator layer_definitions_iterator;
|
||||
typedef std::map<std::string, db::Region *> input_layers;
|
||||
typedef db::hier_clusters<db::PolygonRef> hier_clusters_type;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* The name is the name of the device class of the devices generated.
|
||||
*/
|
||||
NetlistDeviceExtractor (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~NetlistDeviceExtractor ();
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the extractor and the device class
|
||||
*/
|
||||
const std::string &name ()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the property name for the device terminal annotation
|
||||
* This name is used to attach the terminal ID to terminal shapes.
|
||||
*/
|
||||
static const tl::Variant &terminal_id_property_name ();
|
||||
|
||||
/**
|
||||
* @brief Gets the property name for the device id annotation
|
||||
* This name is used to attach the device ID to instances.
|
||||
*/
|
||||
static const tl::Variant &device_id_property_name ();
|
||||
|
||||
/**
|
||||
* @brief Gets the property name for the device class annotation
|
||||
* This name is used to attach the device class name to cells.
|
||||
*/
|
||||
static const tl::Variant &device_class_property_name ();
|
||||
|
||||
/**
|
||||
* @brief Performs the extraction
|
||||
*
|
||||
* layout and cell specify the layout and the top cell from which to perform the
|
||||
* extraction.
|
||||
*
|
||||
* The netlist will be filled with circuits (unless not present yet) to represent the
|
||||
* cells from the layout.
|
||||
*
|
||||
* Devices will be generated inside the netlist's circuits as they are extracted
|
||||
* from the layout. Inside the layout, device terminal annotation shapes are created with the
|
||||
* corresponding DeviceTerminalProperty objects attached. The will be used when extracting
|
||||
* the nets later to associate nets with device terminals.
|
||||
*
|
||||
* The definition of the input layers is device class specific.
|
||||
*
|
||||
* NOTE: The extractor expects "PolygonRef" type layers.
|
||||
*/
|
||||
void extract (Layout &layout, Cell &cell, const std::vector<unsigned int> &layers, Netlist *netlist, hier_clusters_type &clusters);
|
||||
|
||||
/**
|
||||
* @brief Extracts the devices from a list of regions
|
||||
*
|
||||
* This method behaves identical to the other "extract" method, but accepts
|
||||
* named regions for input. These regions need to be of deep region type and
|
||||
* originate from the same layout than the DeepShapeStore.
|
||||
*/
|
||||
void extract (DeepShapeStore &dss, unsigned int layout_index, const input_layers &layers, Netlist &netlist, hier_clusters_type &clusters);
|
||||
|
||||
/**
|
||||
* @brief Gets the error iterator, begin
|
||||
*/
|
||||
error_iterator begin_errors ()
|
||||
{
|
||||
return m_errors.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the error iterator, end
|
||||
*/
|
||||
error_iterator end_errors ()
|
||||
{
|
||||
return m_errors.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if there are errors
|
||||
*/
|
||||
bool has_errors () const
|
||||
{
|
||||
return ! m_errors.empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the layer definition iterator, begin
|
||||
*/
|
||||
layer_definitions_iterator begin_layer_definitions () const
|
||||
{
|
||||
return m_layer_definitions.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the layer definition iterator, end
|
||||
*/
|
||||
layer_definitions_iterator end_layer_definitions () const
|
||||
{
|
||||
return m_layer_definitions.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the name of the device class and the device extractor
|
||||
*/
|
||||
void set_name (const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets up the extractor
|
||||
*
|
||||
* This method is supposed to set up the device extractor. This involves two basic steps:
|
||||
* defining the device classe and setting up the device layers.
|
||||
*
|
||||
* Use "register_device_class" to register the device class you need.
|
||||
*
|
||||
* The device layers need to be defined by calling "define_layer" once or several times.
|
||||
*/
|
||||
virtual void setup ();
|
||||
|
||||
/**
|
||||
* @brief Gets the connectivity object used to extract the device geometry
|
||||
* This method shall raise an error, if the input layer are not properly defined (e.g.
|
||||
* too few etc.)
|
||||
*/
|
||||
virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector<unsigned int> &layers) const;
|
||||
|
||||
/**
|
||||
* @brief Extracts the devices from the given shape cluster
|
||||
*
|
||||
* The shape cluster is a set of geometries belonging together in terms of the
|
||||
* connectivity defined by "get_connectivity". The cluster might cover multiple devices,
|
||||
* so the implementation needs to consider this case. The geometries are already merged.
|
||||
*
|
||||
* The implementation of this method shall use "create_device" to create new
|
||||
* devices based on the geometry found. It shall use "define_terminal" to define
|
||||
* terminals by which the nets extracted in the network extraction step connect
|
||||
* to the new devices.
|
||||
*/
|
||||
virtual void extract_devices (const std::vector<db::Region> &layer_geometry);
|
||||
|
||||
/**
|
||||
* @brief Registers a device class
|
||||
* The device class object will become owned by the netlist and must not be deleted by
|
||||
* the caller. The name of the device class will be changed to the name given to
|
||||
* the device extractor.
|
||||
* This method shall be used inside the implementation of "setup" to register
|
||||
* the device classes.
|
||||
*/
|
||||
void register_device_class (DeviceClass *device_class);
|
||||
|
||||
/**
|
||||
* @brief Defines a layer
|
||||
* Each call will define one more layer for the device extraction.
|
||||
* This method shall be used inside the implementation of "setup" to define
|
||||
* the device layers. The actual geometries are later available to "extract_devices"
|
||||
* in the order the layers are defined.
|
||||
*/
|
||||
void define_layer (const std::string &name, const std::string &description = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Creates a device
|
||||
* The device object returned can be configured by the caller, e.g. set parameters.
|
||||
* It will be owned by the netlist and must not be deleted by the caller.
|
||||
*/
|
||||
Device *create_device ();
|
||||
|
||||
/**
|
||||
* @brief Defines a device terminal in the layout (a polygon)
|
||||
*/
|
||||
void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Polygon &polygon);
|
||||
|
||||
/**
|
||||
* @brief Defines a device terminal in the layout (a box)
|
||||
*/
|
||||
void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Box &box);
|
||||
|
||||
/**
|
||||
* @brief Defines a point-like device terminal in the layout
|
||||
*/
|
||||
void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Point &point);
|
||||
|
||||
/**
|
||||
* @brief Gets the database unit
|
||||
*/
|
||||
double dbu () const
|
||||
{
|
||||
return mp_layout->dbu ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the layout the shapes are taken from
|
||||
* NOTE: this method is provided for testing purposes mainly.
|
||||
*/
|
||||
db::Layout *layout ()
|
||||
{
|
||||
return mp_layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the layout the shapes are taken from (const version)
|
||||
* NOTE: this method is provided for testing purposes mainly.
|
||||
*/
|
||||
const db::Layout *layout () const
|
||||
{
|
||||
return mp_layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the cell index of the current cell
|
||||
* NOTE: this method is provided for testing purposes mainly.
|
||||
*/
|
||||
db::cell_index_type cell_index () const
|
||||
{
|
||||
return m_cell_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Issues an error with the given message
|
||||
*/
|
||||
void error (const std::string &msg);
|
||||
|
||||
/**
|
||||
* @brief Issues an error with the given message and error shape
|
||||
*/
|
||||
void error (const std::string &msg, const db::DPolygon &poly);
|
||||
|
||||
/**
|
||||
* @brief Issues an error with the given message and error shape
|
||||
*/
|
||||
void error (const std::string &msg, const db::Polygon &poly)
|
||||
{
|
||||
error (msg, poly.transformed (db::CplxTrans (dbu ())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Issues an error with the given category name, description and message
|
||||
*/
|
||||
void error (const std::string &category_name, const std::string &category_description, const std::string &msg);
|
||||
|
||||
/**
|
||||
* @brief Issues an error with the given category name, description and message and error shape
|
||||
*/
|
||||
void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly);
|
||||
|
||||
/**
|
||||
* @brief Issues an error with the given category name, description and message and error shape
|
||||
*/
|
||||
void error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly)
|
||||
{
|
||||
error (category_name, category_description, msg, poly.transformed (db::CplxTrans (dbu ())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the current cell
|
||||
*/
|
||||
std::string cell_name () const;
|
||||
|
||||
private:
|
||||
struct DeviceCellKey
|
||||
{
|
||||
DeviceCellKey () { }
|
||||
|
||||
bool operator== (const DeviceCellKey &other) const
|
||||
{
|
||||
if (geometry != other.geometry) {
|
||||
return false;
|
||||
}
|
||||
if (parameters != other.parameters) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator< (const DeviceCellKey &other) const
|
||||
{
|
||||
if (geometry != other.geometry) {
|
||||
return geometry < other.geometry;
|
||||
}
|
||||
if (parameters != other.parameters) {
|
||||
return parameters < other.parameters;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<size_t, std::map<unsigned int, std::set<db::PolygonRef> > > geometry;
|
||||
std::map<size_t, double> parameters;
|
||||
};
|
||||
|
||||
typedef std::map<unsigned int, std::vector<db::PolygonRef> > geometry_per_layer_type;
|
||||
typedef std::map<size_t, geometry_per_layer_type> geometry_per_terminal_type;
|
||||
|
||||
tl::weak_ptr<db::Netlist> m_netlist;
|
||||
db::Layout *mp_layout;
|
||||
db::properties_id_type m_terminal_id_propname_id, m_device_id_propname_id, m_device_class_propname_id;
|
||||
hier_clusters_type *mp_clusters;
|
||||
db::cell_index_type m_cell_index;
|
||||
db::Circuit *mp_circuit;
|
||||
db::DeviceClass *mp_device_class;
|
||||
std::string m_name;
|
||||
layer_definitions m_layer_definitions;
|
||||
std::vector<unsigned int> m_layers;
|
||||
error_list m_errors;
|
||||
std::map<size_t, std::pair<db::Device *, geometry_per_terminal_type> > m_new_devices;
|
||||
std::map<DeviceCellKey, std::pair<db::cell_index_type, db::DeviceAbstract *> > m_device_cells;
|
||||
|
||||
// no copying
|
||||
NetlistDeviceExtractor (const NetlistDeviceExtractor &);
|
||||
NetlistDeviceExtractor &operator= (const NetlistDeviceExtractor &);
|
||||
|
||||
/**
|
||||
* @brief Initializes the extractor
|
||||
* This method will produce the device classes required for the device extraction.
|
||||
*/
|
||||
void initialize (db::Netlist *nl);
|
||||
|
||||
void extract_without_initialize (db::Layout &layout, db::Cell &cell, hier_clusters_type &clusters, const std::vector<unsigned int> &layers);
|
||||
void push_new_devices (const Vector &disp_cache);
|
||||
void push_cached_devices (const tl::vector<Device *> &cached_devices, const db::Vector &disp_cache, const db::Vector &new_disp);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template<> struct type_traits<db::NetlistDeviceExtractor> : public tl::type_traits<void>
|
||||
{
|
||||
// mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbNetlistDeviceExtractorClasses.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// NetlistDeviceExtractorMOS3Transistor implementation
|
||||
|
||||
NetlistDeviceExtractorMOS3Transistor::NetlistDeviceExtractorMOS3Transistor (const std::string &name)
|
||||
: db::NetlistDeviceExtractor (name)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractorMOS3Transistor::setup ()
|
||||
{
|
||||
define_layer ("SD", "Source/drain diffusion");
|
||||
define_layer ("G", "Gate");
|
||||
define_layer ("P", "Poly");
|
||||
|
||||
register_device_class (new db::DeviceClassMOS3Transistor ());
|
||||
}
|
||||
|
||||
db::Connectivity NetlistDeviceExtractorMOS3Transistor::get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
|
||||
{
|
||||
tl_assert (layers.size () >= 3);
|
||||
|
||||
unsigned int diff = layers [0];
|
||||
unsigned int gate = layers [1];
|
||||
// not used for device recognition: poly (2), but used for producing the gate terminals
|
||||
|
||||
// The layer definition is diff, gate
|
||||
db::Connectivity conn;
|
||||
// collect all connected diffusion shapes
|
||||
conn.connect (diff, diff);
|
||||
// collect all connected gate shapes
|
||||
conn.connect (gate, gate);
|
||||
// connect gate with diff to detect gate/diffusion boundary
|
||||
conn.connect (diff, gate);
|
||||
return conn;
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db::Region> &layer_geometry)
|
||||
{
|
||||
const db::Region &rdiff = layer_geometry [0];
|
||||
const db::Region &rgates = layer_geometry [1];
|
||||
|
||||
for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) {
|
||||
|
||||
db::Region rgate (*p);
|
||||
rgate.set_base_verbosity (rgates.base_verbosity ());
|
||||
|
||||
db::Region rdiff2gate = rdiff.selected_interacting (rgate);
|
||||
rdiff2gate.set_base_verbosity (rdiff.base_verbosity ());
|
||||
|
||||
if (rdiff2gate.empty ()) {
|
||||
error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
|
||||
} else {
|
||||
|
||||
unsigned int terminal_geometry_index = 0;
|
||||
unsigned int gate_geometry_index = 2;
|
||||
|
||||
if (rdiff2gate.size () != 2) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
db::Edges edges (rgate.edges () & rdiff2gate.edges ());
|
||||
if (edges.size () != 2) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! p->is_box ()) {
|
||||
error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p);
|
||||
}
|
||||
|
||||
db::Device *device = create_device ();
|
||||
|
||||
device->set_position (db::CplxTrans (dbu ()) * p->box ().center ());
|
||||
|
||||
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5);
|
||||
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5);
|
||||
|
||||
int diff_index = 0;
|
||||
for (db::Region::const_iterator d = rdiff2gate.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) {
|
||||
|
||||
// count the number of gate shapes attached to this shape and distribute the area of the
|
||||
// diffusion region to the number of gates
|
||||
size_t n = rgates.selected_interacting (db::Region (*d)).size ();
|
||||
tl_assert (n > 0);
|
||||
|
||||
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n));
|
||||
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, dbu () * d->perimeter () / double (n));
|
||||
|
||||
define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, terminal_geometry_index, *d);
|
||||
|
||||
}
|
||||
|
||||
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_geometry_index, *p);
|
||||
|
||||
// allow derived classes to modify the device
|
||||
modify_device (*p, layer_geometry, device);
|
||||
|
||||
// output the device for debugging
|
||||
device_out (device, rdiff2gate, rgate);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// NetlistDeviceExtractorMOS4Transistor implementation
|
||||
|
||||
NetlistDeviceExtractorMOS4Transistor::NetlistDeviceExtractorMOS4Transistor (const std::string &name)
|
||||
: NetlistDeviceExtractorMOS3Transistor (name)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractorMOS4Transistor::setup ()
|
||||
{
|
||||
define_layer ("SD", "Source/drain diffusion");
|
||||
define_layer ("G", "Gate");
|
||||
define_layer ("P", "Poly");
|
||||
define_layer ("W", "Well");
|
||||
|
||||
register_device_class (new db::DeviceClassMOS4Transistor ());
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractorMOS4Transistor::modify_device (const db::Polygon &rgate, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device)
|
||||
{
|
||||
unsigned int well_geometry_index = 3;
|
||||
define_terminal (device, db::DeviceClassMOS4Transistor::terminal_id_B, well_geometry_index, rgate);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlistDeviceExtractorClasses
|
||||
#define _HDR_dbNetlistDeviceExtractorClasses
|
||||
|
||||
#include "dbNetlistDeviceExtractor.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A device extractor for a three-terminal MOS transistor
|
||||
*
|
||||
* This class supplies the generic extractor for a MOS device.
|
||||
* The device is defined by two basic input layers: the diffusion area
|
||||
* (source and drain) and the gate area. It requires a third layer
|
||||
* (poly) to put the gate terminals on. The separation between poly
|
||||
* and gate allows separating the device recognition layer (gate) from the
|
||||
* conductive layer.
|
||||
*
|
||||
* The device class produced by this extractor is DeviceClassMOS3Transistor.
|
||||
* The extractor extracts the four parameters of this class: L, W, AS and AD.
|
||||
*
|
||||
* The diffusion area is distributed on the number of gates connecting to
|
||||
* the particular source or drain area.
|
||||
*/
|
||||
class DB_PUBLIC NetlistDeviceExtractorMOS3Transistor
|
||||
: public db::NetlistDeviceExtractor
|
||||
{
|
||||
public:
|
||||
NetlistDeviceExtractorMOS3Transistor (const std::string &name);
|
||||
|
||||
virtual void setup ();
|
||||
virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector<unsigned int> &layers) const;
|
||||
virtual void extract_devices (const std::vector<db::Region> &layer_geometry);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief A callback when the device is produced
|
||||
* This callback is provided as a debugging port
|
||||
*/
|
||||
virtual void device_out (const db::Device * /*device*/, const db::Region & /*diff*/, const db::Region & /*gate*/)
|
||||
{
|
||||
// .. no specific implementation ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allow derived classes to modify the device
|
||||
*/
|
||||
virtual void modify_device (const db::Polygon & /*rgate*/, const std::vector<db::Region> & /*layer_geometry*/, db::Device * /*device*/)
|
||||
{
|
||||
// .. no specific implementation ..
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A device extractor for a four-terminal MOS transistor
|
||||
*
|
||||
* This class is like the MOS3Transistor extractor, but requires a forth
|
||||
* input layer (Well). This layer will be used to output the bulk terminal.
|
||||
*
|
||||
* The device class produced by this extractor is DeviceClassMOS4Transistor.
|
||||
* The extractor extracts the four parameters of this class: L, W, AS and AD.
|
||||
*/
|
||||
class DB_PUBLIC NetlistDeviceExtractorMOS4Transistor
|
||||
: public NetlistDeviceExtractorMOS3Transistor
|
||||
{
|
||||
public:
|
||||
NetlistDeviceExtractorMOS4Transistor (const std::string &name);
|
||||
|
||||
virtual void setup ();
|
||||
|
||||
private:
|
||||
virtual void modify_device (const db::Polygon &rgate, const std::vector<db::Region> &layer_geometry, db::Device *device);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template<> struct type_traits<db::NetlistDeviceExtractorMOS3Transistor> : public tl::type_traits<void>
|
||||
{
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
};
|
||||
|
||||
template<> struct type_traits<db::NetlistDeviceExtractorMOS4Transistor> : public tl::type_traits<void>
|
||||
{
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbNetlistExtractor.h"
|
||||
#include "dbDeepShapeStore.h"
|
||||
#include "dbNetlistDeviceExtractor.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
NetlistExtractor::NetlistExtractor ()
|
||||
: mp_clusters (0), mp_layout (0), mp_cell (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
static void
|
||||
build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, tl::equivalence_clusters<unsigned int> &eq)
|
||||
{
|
||||
std::map<std::string, std::set<unsigned int> > 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 ();
|
||||
prop_by_name [nn].insert (i->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<std::string, std::set<unsigned int> >::const_iterator pn = prop_by_name.begin (); pn != prop_by_name.end (); ++pn) {
|
||||
std::set<unsigned int>::const_iterator p = pn->second.begin ();
|
||||
std::set<unsigned int>::const_iterator p0 = p;
|
||||
while (p != pn->second.end ()) {
|
||||
eq.same (*p0, *p);
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label)
|
||||
{
|
||||
mp_clusters = &clusters;
|
||||
mp_layout = &dss.const_layout (layout_index);
|
||||
mp_cell = &dss.const_initial_cell (layout_index);
|
||||
|
||||
// gets the text annotation property ID -
|
||||
// this is how the texts are passed for annotating the net names
|
||||
m_text_annot_name_id = std::pair<bool, db::property_names_id_type> (false, 0);
|
||||
if (! dss.text_property_name ().is_nil ()) {
|
||||
m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ());
|
||||
}
|
||||
|
||||
m_terminal_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::terminal_id_property_name ());
|
||||
m_device_annot_name_id = mp_layout->properties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_id_property_name ());
|
||||
|
||||
// the big part: actually extract the nets
|
||||
|
||||
tl::equivalence_clusters<unsigned int> net_name_equivalence;
|
||||
if (m_text_annot_name_id.first && join_nets_by_label) {
|
||||
build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, net_name_equivalence);
|
||||
}
|
||||
mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn, &net_name_equivalence);
|
||||
|
||||
// reverse lookup for Circuit vs. cell index
|
||||
std::map<db::cell_index_type, db::Circuit *> circuits;
|
||||
|
||||
// some circuits may be there because of device extraction
|
||||
for (db::Netlist::circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) {
|
||||
tl_assert (mp_layout->is_valid_cell_index (c->cell_index ()));
|
||||
circuits.insert (std::make_pair (c->cell_index (), c.operator-> ()));
|
||||
}
|
||||
|
||||
std::map<db::cell_index_type, std::map<size_t, size_t> > pins_per_cluster_per_cell;
|
||||
for (db::Layout::bottom_up_const_iterator cid = mp_layout->begin_bottom_up (); cid != mp_layout->end_bottom_up (); ++cid) {
|
||||
|
||||
const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid);
|
||||
if (clusters.empty ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
db::DeviceAbstract *dm = nl.device_abstract_by_cell_index (*cid);
|
||||
if (dm) {
|
||||
// make the terminal to cluster ID connections for the device abstract from the device cells
|
||||
make_device_abstract_connections (dm, clusters);
|
||||
continue;
|
||||
}
|
||||
|
||||
// a cell makes a new circuit (or uses an existing one)
|
||||
|
||||
db::Circuit *circuit = 0;
|
||||
|
||||
std::map<db::cell_index_type, db::Circuit *>::const_iterator k = circuits.find (*cid);
|
||||
if (k == circuits.end ()) {
|
||||
circuit = new db::Circuit ();
|
||||
nl.add_circuit (circuit);
|
||||
circuit->set_name (mp_layout->cell_name (*cid));
|
||||
circuit->set_cell_index (*cid);
|
||||
circuits.insert (std::make_pair (*cid, circuit));
|
||||
} else {
|
||||
circuit = k->second;
|
||||
}
|
||||
|
||||
std::map<size_t, size_t> &c2p = pins_per_cluster_per_cell [*cid];
|
||||
|
||||
std::map<std::pair<db::cell_index_type, db::ICplxTrans>, db::SubCircuit *> subcircuits;
|
||||
|
||||
for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) {
|
||||
|
||||
db::Net *net = new db::Net ();
|
||||
net->set_cluster_id (*c);
|
||||
circuit->add_net (net);
|
||||
|
||||
const db::local_cluster<db::PolygonRef>::global_nets &gn = clusters.cluster_by_id (*c).get_global_nets ();
|
||||
for (db::local_cluster<db::PolygonRef>::global_nets::const_iterator g = gn.begin (); g != gn.end (); ++g) {
|
||||
assign_net_name (conn.global_net_name (*g), net);
|
||||
}
|
||||
|
||||
// make subcircuit connections (also make the subcircuits if required) from the connections of the clusters
|
||||
make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell);
|
||||
|
||||
// connect devices
|
||||
connect_devices (circuit, clusters, *c, net);
|
||||
|
||||
// collect labels to net names
|
||||
collect_labels (clusters, *c, net);
|
||||
|
||||
if (! clusters.is_root (*c)) {
|
||||
// a non-root cluster makes a pin
|
||||
size_t pin_id = make_pin (circuit, net);
|
||||
c2p.insert (std::make_pair (*c, pin_id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters)
|
||||
{
|
||||
// make the terminal to cluster ID connections for the device abstract from the device cells
|
||||
|
||||
if (m_terminal_annot_name_id.first) {
|
||||
|
||||
for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) {
|
||||
|
||||
for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) {
|
||||
|
||||
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a);
|
||||
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
|
||||
if (j->first == m_terminal_annot_name_id.second) {
|
||||
dm->set_cluster_id_for_terminal (j->second.to<size_t> (), dc->id ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistExtractor::collect_labels (const connected_clusters_type &clusters,
|
||||
size_t cid,
|
||||
db::Net *net)
|
||||
{
|
||||
// collect the properties - we know that the cluster attributes are property ID's because the
|
||||
// cluster processor converts shape property IDs to attributes
|
||||
|
||||
const local_cluster_type &lc = clusters.cluster_by_id (cid);
|
||||
for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) {
|
||||
|
||||
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a);
|
||||
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
|
||||
|
||||
if (m_text_annot_name_id.first && j->first == m_text_annot_name_id.second) {
|
||||
assign_net_name (j->second.to_string (), net);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistExtractor::instance_is_device (db::properties_id_type prop_id) const
|
||||
{
|
||||
if (! prop_id || ! m_device_annot_name_id.first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id);
|
||||
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
|
||||
if (j->first == m_device_annot_name_id.second) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
db::Device *NetlistExtractor::device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const
|
||||
{
|
||||
if (! prop_id || ! m_device_annot_name_id.first) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (prop_id);
|
||||
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
|
||||
if (j->first == m_device_annot_name_id.second) {
|
||||
return circuit->device_by_id (j->second.to<size_t> ());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NetlistExtractor::connect_devices (db::Circuit *circuit,
|
||||
const connected_clusters_type &clusters,
|
||||
size_t cid,
|
||||
db::Net *net)
|
||||
{
|
||||
const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid);
|
||||
for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) {
|
||||
|
||||
db::cell_index_type inst_cell_index = i->inst_cell_index ();
|
||||
db::properties_id_type inst_prop_id = i->inst_prop_id ();
|
||||
|
||||
// only consider devices in this pass
|
||||
db::Device *device = device_from_instance (inst_prop_id, circuit);
|
||||
if (! device) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const db::local_cluster<db::PolygonRef> &dc = mp_clusters->clusters_per_cell (inst_cell_index).cluster_by_id (i->id ());
|
||||
|
||||
// connect the net to the terminal of the device: take the terminal ID from the properties on the
|
||||
// device cluster
|
||||
for (local_cluster_type::attr_iterator a = dc.begin_attr (); a != dc.end_attr (); ++a) {
|
||||
|
||||
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (*a);
|
||||
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
|
||||
|
||||
if (m_terminal_annot_name_id.first && j->first == m_terminal_annot_name_id.second) {
|
||||
|
||||
size_t tid = j->second.to<size_t> ();
|
||||
device->connect_terminal (tid, net);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistExtractor::make_and_connect_subcircuits (db::Circuit *circuit,
|
||||
const connected_clusters_type &clusters,
|
||||
size_t cid,
|
||||
db::Net *net,
|
||||
std::map<std::pair<db::cell_index_type, db::ICplxTrans>, db::SubCircuit *> &subcircuits,
|
||||
const std::map<db::cell_index_type, db::Circuit *> &circuits,
|
||||
const std::map<db::cell_index_type, std::map<size_t, size_t> > &pins_per_cluster)
|
||||
{
|
||||
const connected_clusters_type::connections_type &connections = clusters.connections_for_cluster (cid);
|
||||
for (connected_clusters_type::connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) {
|
||||
|
||||
db::cell_index_type inst_cell_index = i->inst_cell_index ();
|
||||
db::properties_id_type inst_prop_id = i->inst_prop_id ();
|
||||
const db::ICplxTrans &inst_trans = i->inst_trans ();
|
||||
|
||||
// skip devices in this pass
|
||||
if (instance_is_device (inst_prop_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
db::SubCircuit *subcircuit = 0;
|
||||
|
||||
std::pair<db::cell_index_type, db::ICplxTrans> subcircuit_key (inst_cell_index, inst_trans);
|
||||
|
||||
std::map<std::pair<db::cell_index_type, db::ICplxTrans>, db::SubCircuit *>::const_iterator j = subcircuits.find (subcircuit_key);
|
||||
if (j == subcircuits.end ()) {
|
||||
|
||||
// make subcircuit if required
|
||||
|
||||
std::map<db::cell_index_type, db::Circuit *>::const_iterator k = circuits.find (inst_cell_index);
|
||||
tl_assert (k != circuits.end ()); // because we walk bottom-up
|
||||
|
||||
subcircuit = new db::SubCircuit (k->second);
|
||||
db::CplxTrans dbu_trans (mp_layout->dbu ());
|
||||
subcircuit->set_trans (dbu_trans * inst_trans * dbu_trans.inverted ());
|
||||
circuit->add_subcircuit (subcircuit);
|
||||
subcircuits.insert (std::make_pair (subcircuit_key, subcircuit));
|
||||
|
||||
} else {
|
||||
subcircuit = j->second;
|
||||
}
|
||||
|
||||
// create the pin connection to the subcircuit
|
||||
std::map<db::cell_index_type, std::map<size_t, size_t> >::const_iterator icc2p = pins_per_cluster.find (inst_cell_index);
|
||||
tl_assert (icc2p != pins_per_cluster.end ());
|
||||
std::map<size_t, size_t>::const_iterator ip = icc2p->second.find (i->id ());
|
||||
tl_assert (ip != icc2p->second.end ());
|
||||
subcircuit->connect_pin (ip->second, net);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
size_t NetlistExtractor::make_pin (db::Circuit *circuit, db::Net *net)
|
||||
{
|
||||
size_t pin_id = circuit->add_pin (net->name ()).id ();
|
||||
net->add_pin (db::NetPinRef (pin_id));
|
||||
|
||||
circuit->connect_pin (pin_id, net);
|
||||
|
||||
return pin_id;
|
||||
}
|
||||
|
||||
void NetlistExtractor::assign_net_name (const std::string &n, db::Net *net)
|
||||
{
|
||||
std::string nn = n;
|
||||
if (! nn.empty ()) {
|
||||
if (! net->name ().empty ()) {
|
||||
nn = net->name () + "," + nn;
|
||||
}
|
||||
net->set_name (nn);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlistExtractor
|
||||
#define _HDR_dbNetlistExtractor
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbHierNetworkProcessor.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class DeepShapeStore;
|
||||
class Netlist;
|
||||
class Circuit;
|
||||
class SubCircuit;
|
||||
class Net;
|
||||
class Device;
|
||||
class DeviceAbstract;
|
||||
|
||||
/**
|
||||
* @brief The Netlist Extractor
|
||||
*
|
||||
* This is the main object responsible for extracting nets from a layout.
|
||||
*
|
||||
* The layout needs to be present as a DeepShapeStore shadow layout. Use hierarchical regions
|
||||
* (db::Region build with a DeepShapeStore) to populate the shape store.
|
||||
*
|
||||
* The extraction requires a connectivity definition through db::Connectivity.
|
||||
*
|
||||
* In addition, the device extraction needs to happen before net extraction.
|
||||
* Device extraction will pre-fill the netlist with circuits and devices and
|
||||
* annotate the layout with terminal shapes, so the net extraction can connect
|
||||
* to the device terminals.
|
||||
*
|
||||
* If the deep shape store has been configured to supply text label annotated
|
||||
* markers (DeepShapeStore::set_text_property_name and DeepShapeStore::set_text_enlargement
|
||||
* to at least 1), texts from layers included in the connectivity will be extracted
|
||||
* as net names. If multiple texts are present, the names will be concatenated using
|
||||
* comma separators.
|
||||
*
|
||||
* Upon extraction, the given netlist is filled with circuits (unless present already),
|
||||
* subcircuits, pins and of course nets. This object also supplies access to the net's
|
||||
* geometries through the clusters() method. This method delivers a hierarchical
|
||||
* cluster object. The nets refer to specific clusters through their "cluster_id"
|
||||
* attribute.
|
||||
*/
|
||||
class DB_PUBLIC NetlistExtractor
|
||||
{
|
||||
public:
|
||||
typedef db::hier_clusters<db::PolygonRef> hier_clusters_type;
|
||||
typedef db::connected_clusters<db::PolygonRef> connected_clusters_type;
|
||||
typedef db::local_cluster<db::PolygonRef> local_cluster_type;
|
||||
|
||||
/**
|
||||
* @brief NetExtractor constructor
|
||||
*/
|
||||
NetlistExtractor ();
|
||||
|
||||
/**
|
||||
* @brief Extract the nets
|
||||
* See the class description for more details.
|
||||
*/
|
||||
void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true);
|
||||
|
||||
private:
|
||||
hier_clusters_type *mp_clusters;
|
||||
const db::Layout *mp_layout;
|
||||
const db::Cell *mp_cell;
|
||||
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;
|
||||
|
||||
void assign_net_name (const std::string &n, db::Net *net);
|
||||
bool instance_is_device (db::properties_id_type prop_id) const;
|
||||
db::Device *device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const;
|
||||
|
||||
/**
|
||||
* @brief Make a pin connection from clusters
|
||||
*
|
||||
* Non-root clusters make a pin. This function creates the pin inside the given circuit. It
|
||||
* returns the new pin's ID.
|
||||
*/
|
||||
size_t make_pin (db::Circuit *circuit, db::Net *net);
|
||||
|
||||
/**
|
||||
* @brief Turns the connections of a cluster into subcircuit instances
|
||||
*
|
||||
* Walks through the connections of a cluster and turns the connections into subcircuit pin
|
||||
* connections. This will also create new subcircuit instances.
|
||||
*
|
||||
* Needs:
|
||||
* - circuit: the current circuit that is worked on
|
||||
* - clusters: the connected clusters from the net extraction step (nets plus
|
||||
* connections to child cell clusters inside the current cell)
|
||||
* - cid: the current cluster ID in "clusters"
|
||||
* - net: the net being built
|
||||
* - circuits: a lookup table of circuits vs. cell index (reverse lookup)
|
||||
* - pins_per_cluster: a per-cell, reverse lookup table for the pin id per child cell clusters
|
||||
* (used to find the pin ID of a subcircuit from the child cell cluster ID)
|
||||
* Updates:
|
||||
* - subcircuits: An cell instance to SubCircuit lookup table
|
||||
*/
|
||||
void make_and_connect_subcircuits (db::Circuit *circuit,
|
||||
const connected_clusters_type &clusters,
|
||||
size_t cid,
|
||||
db::Net *net,
|
||||
std::map<std::pair<cell_index_type, ICplxTrans>, SubCircuit *> &subcircuits,
|
||||
const std::map<db::cell_index_type, db::Circuit *> &circuits,
|
||||
const std::map<db::cell_index_type, std::map<size_t, size_t> > &pins_per_cluster);
|
||||
|
||||
/**
|
||||
* @brief Connects the devices
|
||||
*
|
||||
* Devices are identified by special cells. These carry a property with the
|
||||
* device class name. Inside these cells, the terminals are identified by special clusters.
|
||||
* The terminal IDs are coded on these clusters are a property.
|
||||
*/
|
||||
void connect_devices (db::Circuit *circuit,
|
||||
const connected_clusters_type &clusters,
|
||||
size_t cid,
|
||||
db::Net *net);
|
||||
|
||||
/**
|
||||
* @brief Attaches net names from text properties
|
||||
*
|
||||
* Texts (labels) are represented by special shapes. The texts are kept as properties.
|
||||
* This method will collect all these labels and attach them to the nets as (alternative)
|
||||
* names.
|
||||
*/
|
||||
void collect_labels (const connected_clusters_type &clusters,
|
||||
size_t cid,
|
||||
db::Net *net);
|
||||
|
||||
/**
|
||||
* @brief Makes the terminal to cluster ID connections of the device abstract
|
||||
*/
|
||||
void make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,404 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbNetlistSpiceWriter.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
#include "tlStream.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceWriterDelegate::NetlistSpiceWriterDelegate ()
|
||||
: mp_writer (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetlistSpiceWriterDelegate::~NetlistSpiceWriterDelegate ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
std::string NetlistSpiceWriterDelegate::net_to_string (const db::Net *net) const
|
||||
{
|
||||
tl_assert (mp_writer != 0);
|
||||
return mp_writer->net_to_string (net);
|
||||
}
|
||||
|
||||
std::string NetlistSpiceWriterDelegate::format_name (const std::string &name) const
|
||||
{
|
||||
tl_assert (mp_writer != 0);
|
||||
return mp_writer->format_name (name);
|
||||
}
|
||||
|
||||
void NetlistSpiceWriterDelegate::emit_line (const std::string &line) const
|
||||
{
|
||||
tl_assert (mp_writer != 0);
|
||||
mp_writer->emit_line (line);
|
||||
}
|
||||
|
||||
void NetlistSpiceWriterDelegate::emit_comment (const std::string &comment) const
|
||||
{
|
||||
tl_assert (mp_writer != 0);
|
||||
mp_writer->emit_comment (comment);
|
||||
}
|
||||
|
||||
void NetlistSpiceWriterDelegate::attach_writer (NetlistSpiceWriter *writer)
|
||||
{
|
||||
mp_writer = writer;
|
||||
}
|
||||
|
||||
void NetlistSpiceWriterDelegate::write_header () const
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistSpiceWriterDelegate::write_device_intro (const db::DeviceClass &) const
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
|
||||
{
|
||||
const db::DeviceClass *dc = dev.device_class ();
|
||||
const db::DeviceClassCapacitor *cap = dynamic_cast<const db::DeviceClassCapacitor *> (dc);
|
||||
const db::DeviceClassInductor *ind = dynamic_cast<const db::DeviceClassInductor *> (dc);
|
||||
const db::DeviceClassResistor *res = dynamic_cast<const db::DeviceClassResistor *> (dc);
|
||||
const db::DeviceClassDiode *diode = dynamic_cast<const db::DeviceClassDiode *> (dc);
|
||||
const db::DeviceClassMOS3Transistor *mos3 = dynamic_cast<const db::DeviceClassMOS3Transistor *> (dc);
|
||||
const db::DeviceClassMOS4Transistor *mos4 = dynamic_cast<const db::DeviceClassMOS4Transistor *> (dc);
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
if (cap) {
|
||||
|
||||
os << "C";
|
||||
os << format_name (dev.expanded_name ());
|
||||
os << format_terminals (dev);
|
||||
os << " ";
|
||||
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassCapacitor::param_id_C));
|
||||
|
||||
} else if (ind) {
|
||||
|
||||
os << "L";
|
||||
os << format_name (dev.expanded_name ());
|
||||
os << format_terminals (dev);
|
||||
os << " ";
|
||||
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassInductor::param_id_L));
|
||||
|
||||
} else if (res) {
|
||||
|
||||
os << "R";
|
||||
os << format_name (dev.expanded_name ());
|
||||
os << format_terminals (dev);
|
||||
os << " ";
|
||||
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassResistor::param_id_R));
|
||||
|
||||
} else if (diode) {
|
||||
|
||||
os << "D";
|
||||
os << format_name (dev.expanded_name ());
|
||||
os << format_terminals (dev);
|
||||
|
||||
// Use "D" + device class name for the model
|
||||
os << " D";
|
||||
os << format_name (dev.device_class ()->name ());
|
||||
|
||||
} else if (mos3 || mos4) {
|
||||
|
||||
os << "M";
|
||||
os << format_name (dev.expanded_name ());
|
||||
os << format_terminals (dev);
|
||||
|
||||
if (! mos4) {
|
||||
// we assume for the MOS3 type the bulk is connected to Source
|
||||
os << " ";
|
||||
os << net_to_string (dev.net_for_terminal (db::DeviceClassMOS3Transistor::terminal_id_S));
|
||||
}
|
||||
|
||||
// Use "M" + device class name for the model
|
||||
os << " M";
|
||||
os << format_name (dev.device_class ()->name ());
|
||||
|
||||
os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L));
|
||||
os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W));
|
||||
os << " AS=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS));
|
||||
os << " AD=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD));
|
||||
os << " PS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PS));
|
||||
os << " PD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PD));
|
||||
|
||||
} else {
|
||||
|
||||
// Write unknown devices as subcircuits (CAUTION: potential name clash)
|
||||
os << "XD_" << format_name (dev.expanded_name ());
|
||||
os << format_terminals (dev);
|
||||
os << " ";
|
||||
os << format_name (dev.device_class ()->name ());
|
||||
os << " PARAMS:";
|
||||
os << format_params (dev);
|
||||
|
||||
}
|
||||
|
||||
emit_line (os.str ());
|
||||
}
|
||||
|
||||
std::string NetlistSpiceWriterDelegate::format_terminals (const db::Device &dev) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = dev.device_class ()->terminal_definitions ();
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
|
||||
os << " " << net_to_string (dev.net_for_terminal (i->id ()));
|
||||
}
|
||||
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
const std::vector<db::DeviceParameterDefinition> &pd = dev.device_class ()->parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
|
||||
os << " " << i->name () << "=" << tl::to_string (dev.parameter_value (i->id ()));
|
||||
}
|
||||
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceWriter::NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate)
|
||||
: mp_netlist (0), mp_stream (0), mp_delegate (delegate)
|
||||
{
|
||||
static NetlistSpiceWriterDelegate std_delegate;
|
||||
if (! delegate) {
|
||||
mp_delegate.reset (&std_delegate);
|
||||
}
|
||||
}
|
||||
|
||||
NetlistSpiceWriter::~NetlistSpiceWriter ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description)
|
||||
{
|
||||
mp_stream = &stream;
|
||||
mp_netlist = &netlist;
|
||||
mp_delegate->attach_writer (this);
|
||||
|
||||
try {
|
||||
|
||||
do_write (description);
|
||||
|
||||
mp_stream = 0;
|
||||
mp_netlist = 0;
|
||||
mp_delegate->attach_writer (0);
|
||||
|
||||
} catch (...) {
|
||||
|
||||
mp_stream = 0;
|
||||
mp_netlist = 0;
|
||||
mp_delegate->attach_writer (0);
|
||||
throw;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const
|
||||
{
|
||||
std::map<const db::Net *, size_t>::const_iterator n = m_net_to_spice_id.find (net);
|
||||
if (! net || n == m_net_to_spice_id.end ()) {
|
||||
// TODO: this should assert or similar
|
||||
return "0";
|
||||
} else {
|
||||
return tl::to_string (n->second);
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistSpiceWriter::emit_line (const std::string &line) const
|
||||
{
|
||||
tl_assert (mp_stream != 0);
|
||||
|
||||
int max_length = 80;
|
||||
bool first = true;
|
||||
|
||||
const char *cp = line.c_str ();
|
||||
do {
|
||||
|
||||
const char *cpn = cp;
|
||||
const char *cspc = 0;
|
||||
int c = 0;
|
||||
|
||||
int l = first ? max_length : max_length - 2;
|
||||
while (*cpn && (c < l || ! cspc)) {
|
||||
if (isspace (*cpn)) {
|
||||
cspc = cpn;
|
||||
}
|
||||
++c;
|
||||
++cpn;
|
||||
}
|
||||
|
||||
if (! first) {
|
||||
*mp_stream << "+ ";
|
||||
}
|
||||
|
||||
if (! *cpn) {
|
||||
*mp_stream << cp << "\n";
|
||||
break;
|
||||
} else {
|
||||
while (*cp && (cp != cspc || ! cspc)) {
|
||||
*mp_stream << *cp++;
|
||||
}
|
||||
*mp_stream << "\n";
|
||||
}
|
||||
|
||||
first = false;
|
||||
|
||||
while (*cp && isspace (*cp)) {
|
||||
++cp;
|
||||
}
|
||||
|
||||
} while (*cp);
|
||||
}
|
||||
|
||||
void NetlistSpiceWriter::emit_comment (const std::string &comment) const
|
||||
{
|
||||
tl_assert (mp_stream != 0);
|
||||
|
||||
// TODO: should do some line breaking or reduction for long lines
|
||||
// or when lines contain newlines
|
||||
*mp_stream << "* " << comment << "\n";
|
||||
}
|
||||
|
||||
std::string NetlistSpiceWriter::format_name (const std::string &s) const
|
||||
{
|
||||
// TODO: escape or replace special chars
|
||||
return s;
|
||||
}
|
||||
|
||||
void NetlistSpiceWriter::do_write (const std::string &description)
|
||||
{
|
||||
if (! description.empty ()) {
|
||||
emit_comment (description);
|
||||
}
|
||||
|
||||
mp_delegate->write_header ();
|
||||
|
||||
for (db::Netlist::const_device_class_iterator dc = mp_netlist->begin_device_classes (); dc != mp_netlist->end_device_classes (); ++dc) {
|
||||
mp_delegate->write_device_intro (*dc);
|
||||
}
|
||||
|
||||
for (db::Netlist::const_top_down_circuit_iterator c = mp_netlist->begin_top_down (); c != mp_netlist->end_top_down (); ++c) {
|
||||
|
||||
const db::Circuit &circuit = **c;
|
||||
|
||||
// assign internal node numbers to the nets
|
||||
m_net_to_spice_id.clear ();
|
||||
size_t nid = 0;
|
||||
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
|
||||
m_net_to_spice_id.insert (std::make_pair (n.operator-> (), ++nid));
|
||||
}
|
||||
|
||||
write_circuit_header (circuit);
|
||||
|
||||
for (db::Circuit::const_subcircuit_iterator i = circuit.begin_subcircuits (); i != circuit.end_subcircuits (); ++i) {
|
||||
write_subcircuit_call (*i);
|
||||
}
|
||||
|
||||
for (db::Circuit::const_device_iterator i = circuit.begin_devices (); i != circuit.end_devices (); ++i) {
|
||||
|
||||
// TODO: make this configurable?
|
||||
std::string comment = "device instance " + i->expanded_name () + " " + i->position ().to_string () + " " + i->device_class ()->name ();
|
||||
emit_comment (comment);
|
||||
|
||||
mp_delegate->write_device (*i);
|
||||
|
||||
}
|
||||
|
||||
write_circuit_end (circuit);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistSpiceWriter::write_subcircuit_call (const db::SubCircuit &subcircuit) const
|
||||
{
|
||||
// TODO: make this configurable?
|
||||
std::string comment = "cell instance " + subcircuit.expanded_name() + " " + subcircuit.trans ().to_string ();
|
||||
emit_comment (comment);
|
||||
|
||||
std::ostringstream os;
|
||||
os << "X";
|
||||
os << format_name (subcircuit.expanded_name ());
|
||||
|
||||
for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) {
|
||||
os << " ";
|
||||
os << net_to_string (subcircuit.net_for_pin (p->id ()));
|
||||
}
|
||||
|
||||
os << " ";
|
||||
os << format_name (subcircuit.circuit_ref ()->name ());
|
||||
|
||||
emit_line (os.str ());
|
||||
}
|
||||
|
||||
void NetlistSpiceWriter::write_circuit_header (const db::Circuit &circuit) const
|
||||
{
|
||||
emit_line ("");
|
||||
|
||||
emit_comment ("cell " + circuit.name ());
|
||||
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
|
||||
emit_comment ("pin " + p->name ());
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
os << ".SUBCKT ";
|
||||
os << format_name (circuit.name ());
|
||||
|
||||
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
|
||||
os << " ";
|
||||
os << net_to_string (circuit.net_for_pin (p->id ()));
|
||||
}
|
||||
|
||||
emit_line (os.str ());
|
||||
|
||||
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
|
||||
if (! n->name ().empty ()) {
|
||||
emit_comment ("net " + net_to_string (n.operator-> ()) + " " + n->name ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistSpiceWriter::write_circuit_end (const db::Circuit &circuit) const
|
||||
{
|
||||
emit_line (".ENDS " + format_name (circuit.name ()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlistSpiceWriter
|
||||
#define HDR_dbNetlistSpiceWriter
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbNetlistWriter.h"
|
||||
#include "tlObject.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class DeviceClass;
|
||||
class Device;
|
||||
class Net;
|
||||
class NetlistSpiceWriter;
|
||||
class Circuit;
|
||||
class SubCircuit;
|
||||
|
||||
/**
|
||||
* @brief A device writer delegate for the SPICE writer
|
||||
*
|
||||
* This delegate is supposed to provide the mapping of devices to parametrized SPICE subcircuits.
|
||||
* It is generic, so it can be used for other cases of device mapping.
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceWriterDelegate
|
||||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
NetlistSpiceWriterDelegate ();
|
||||
virtual ~NetlistSpiceWriterDelegate ();
|
||||
|
||||
virtual void write_header () const;
|
||||
virtual void write_device_intro (const db::DeviceClass &cls) const;
|
||||
virtual void write_device (const db::Device &dev) const;
|
||||
|
||||
std::string net_to_string (const db::Net *net) const;
|
||||
void emit_line (const std::string &line) const;
|
||||
void emit_comment (const std::string &comment) const;
|
||||
std::string format_name (const std::string &s) const;
|
||||
std::string format_terminals (const db::Device &dev) const;
|
||||
std::string format_params (const db::Device &dev) const;
|
||||
|
||||
private:
|
||||
friend class NetlistSpiceWriter;
|
||||
|
||||
NetlistSpiceWriter *mp_writer;
|
||||
|
||||
void attach_writer (NetlistSpiceWriter *writer);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A SPICE format writer for netlists
|
||||
*
|
||||
* Specialization happens through the device writer delegate.
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceWriter
|
||||
: public NetlistWriter
|
||||
{
|
||||
public:
|
||||
NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate = 0);
|
||||
virtual ~NetlistSpiceWriter ();
|
||||
|
||||
virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description);
|
||||
|
||||
private:
|
||||
friend class NetlistSpiceWriterDelegate;
|
||||
|
||||
const db::Netlist *mp_netlist;
|
||||
tl::OutputStream *mp_stream;
|
||||
tl::weak_ptr<NetlistSpiceWriterDelegate> mp_delegate;
|
||||
std::map<const db::Net *, size_t> m_net_to_spice_id;
|
||||
|
||||
void do_write (const std::string &description);
|
||||
|
||||
std::string net_to_string (const db::Net *net) const;
|
||||
void emit_line (const std::string &line) const;
|
||||
void emit_comment (const std::string &comment) const;
|
||||
std::string format_name (const std::string &name) const;
|
||||
void write_subcircuit_call (const db::SubCircuit &subcircuit) const;
|
||||
void write_circuit_header (const db::Circuit &circuit) const;
|
||||
void write_circuit_end (const db::Circuit &circuit) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlistUtils
|
||||
#define _HDR_dbNetlistUtils
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A getter for the ID of an object
|
||||
*/
|
||||
template <class T>
|
||||
struct id_attribute
|
||||
{
|
||||
typedef size_t attr_type;
|
||||
size_t operator() (const T *t) const { return t->id (); }
|
||||
bool has (const T * /*t*/) const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A getter for the cluster ID of an object
|
||||
*/
|
||||
template <class T>
|
||||
struct cluster_id_attribute
|
||||
{
|
||||
typedef size_t attr_type;
|
||||
attr_type operator() (const T *t) const { return t->cluster_id (); }
|
||||
bool has (const T * /*t*/) const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A getter for the cluster ID of an object
|
||||
*/
|
||||
template <class T>
|
||||
struct cell_index_attribute
|
||||
{
|
||||
typedef db::cell_index_type attr_type;
|
||||
attr_type operator() (const T *t) const { return t->cell_index (); }
|
||||
bool has (const T * /*t*/) const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A getter for the name of an object
|
||||
*/
|
||||
template <class T>
|
||||
struct name_attribute
|
||||
{
|
||||
typedef std::string attr_type;
|
||||
const attr_type &operator() (const T *t) const { return t->name (); }
|
||||
bool has (const T *t) const { return ! t->name ().empty (); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An id-to-object translation table
|
||||
*/
|
||||
template <class T, class I, class ATTR>
|
||||
class object_by_attr
|
||||
{
|
||||
public:
|
||||
typedef typename ATTR::attr_type attr_type;
|
||||
typedef typename I::value_type value_type;
|
||||
|
||||
object_by_attr (T *self, I (T::*bi) (), I (T::*ei) ()) : mp_self (self), m_bi (bi), m_ei (ei), m_valid (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void invalidate ()
|
||||
{
|
||||
m_valid = false;
|
||||
m_map.clear ();
|
||||
}
|
||||
|
||||
value_type *object_by (const attr_type &attr) const
|
||||
{
|
||||
if (! m_valid) {
|
||||
validate ();
|
||||
}
|
||||
typename std::map<attr_type, value_type *>::const_iterator m = m_map.find (attr);
|
||||
return m == m_map.end () ? 0 : m->second;
|
||||
}
|
||||
|
||||
private:
|
||||
T *mp_self;
|
||||
I (T::*m_bi) ();
|
||||
I (T::*m_ei) ();
|
||||
mutable bool m_valid;
|
||||
mutable std::map<attr_type, value_type *> m_map;
|
||||
|
||||
void validate () const
|
||||
{
|
||||
ATTR attr;
|
||||
m_map.clear ();
|
||||
for (I i = (mp_self->*m_bi) (); i != (mp_self->*m_ei) (); ++i) {
|
||||
if (attr.has (i.operator-> ())) {
|
||||
m_map.insert (std::make_pair (attr (i.operator-> ()), i.operator-> ()));
|
||||
}
|
||||
}
|
||||
m_valid = true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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 "dbNetlistWriter.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// .. nothing yet ..
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 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_dbNetlistWriter
|
||||
#define HDR_dbNetlistWriter
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "tlTypeTraits.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
class OutputStream;
|
||||
}
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
|
||||
/**
|
||||
* @brief A common base class for netlist writers
|
||||
*/
|
||||
class DB_PUBLIC NetlistWriter
|
||||
{
|
||||
public:
|
||||
NetlistWriter () { }
|
||||
virtual ~NetlistWriter () { }
|
||||
|
||||
virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description = std::string ()) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template <>
|
||||
struct type_traits<db::NetlistWriter>
|
||||
: public tl::type_traits<void>
|
||||
{
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbPolygon.h"
|
||||
#include "dbPath.h"
|
||||
#include "dbEdge.h"
|
||||
#include "dbEdgePair.h"
|
||||
#include "dbText.h"
|
||||
#include "dbBox.h"
|
||||
#include "dbArray.h"
|
||||
|
|
@ -196,6 +197,9 @@ typedef object_with_properties<DPathRef> DPathRefWithProperties;
|
|||
typedef object_with_properties<Edge> EdgeWithProperties;
|
||||
typedef object_with_properties<DEdge> DEdgeWithProperties;
|
||||
|
||||
typedef object_with_properties<EdgePair> EdgePairWithProperties;
|
||||
typedef object_with_properties<DEdgePair> DEdgePairWithProperties;
|
||||
|
||||
typedef object_with_properties<Text> TextWithProperties;
|
||||
typedef object_with_properties<DText> DTextWithProperties;
|
||||
typedef object_with_properties<TextRef> TextRefWithProperties;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue