Implemented implicit joining of nets with the same label.

This commit is contained in:
Matthias Koefferlein 2019-02-03 21:34:23 +01:00
parent 3f1cd226a5
commit 9c0123df20
15 changed files with 860 additions and 22 deletions

View File

@ -817,7 +817,7 @@ private:
template <class T>
void
local_clusters<T>::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
local_clusters<T>::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
{
bool report_progress = tl::verbosity () >= 50;
static std::string desc = tl::to_string (tr ("Building local clusters"));
@ -836,6 +836,57 @@ local_clusters<T>::build_clusters (const db::Cell &cell, db::ShapeIterator::flag
cluster_building_receiver<T, box_type> rec (conn);
bs.process (rec, 1 /*==touching*/, bc);
rec.generate_clusters (*this);
if (attr_equivalence && attr_equivalence->size () > 0) {
apply_attr_equivalences (*attr_equivalence);
}
}
template <class T>
void
local_clusters<T>::apply_attr_equivalences (const tl::equivalence_clusters<unsigned int> &attr_equivalence)
{
tl::equivalence_clusters<unsigned int> eq;
// collect all local attributes (the ones which are present in attr_equivalence) into "eq"
// and form equivalences for multi-attribute clusters.
for (const_iterator c = begin (); c != end (); ++c) {
typename local_cluster<T>::attr_iterator a0;
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
if (attr_equivalence.has_attribute (*a)) {
if (a0 == typename local_cluster<T>::attr_iterator ()) {
a0 = a;
}
eq.same (*a0, *a);
}
}
}
// apply the equivalences implied by attr_equivalence
eq.apply_equivalences (attr_equivalence);
// identify the layout clusters joined into one attribute cluster and join them
std::map<tl::equivalence_clusters<unsigned int>::cluster_id_type, std::set<size_t> > c2c;
for (const_iterator c = begin (); c != end (); ++c) {
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
tl::equivalence_clusters<unsigned int>::cluster_id_type cl = attr_equivalence.cluster_id (*a);
if (cl > 0) {
c2c [cl].insert (c->id ());
}
}
}
for (std::map<tl::equivalence_clusters<unsigned int>::cluster_id_type, std::set<size_t> >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) {
if (c->second.size () > 1) {
std::set<size_t>::const_iterator cl0 = c->second.begin ();
std::set<size_t>::const_iterator cl = cl0;
while (++cl != c->second.end ()) {
join_cluster_with (*cl0, *cl);
}
}
}
}
// explicit instantiations
@ -992,11 +1043,11 @@ void hier_clusters<T>::clear ()
template <class T>
void
hier_clusters<T>::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
hier_clusters<T>::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
{
clear ();
cell_clusters_box_converter<T> cbc (layout, *this);
do_build (cbc, layout, cell, shape_flags, conn);
do_build (cbc, layout, cell, shape_flags, conn, attr_equivalence);
}
namespace
@ -1649,7 +1700,7 @@ hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, siz
template <class T>
void
hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
{
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing shape clusters")));
@ -1664,7 +1715,7 @@ hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layou
tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1);
for (std::set<db::cell_index_type>::const_iterator c = called.begin (); c != called.end (); ++c) {
build_local_cluster (layout, layout.cell (*c), shape_flags, conn);
build_local_cluster (layout, layout.cell (*c), shape_flags, conn, attr_equivalence);
++progress;
}
}
@ -1707,7 +1758,7 @@ hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layou
template <class T>
void
hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
{
std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ()));
if (tl::verbosity () >= 40) {
@ -1716,7 +1767,7 @@ hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell
tl::SelfTimer timer (tl::verbosity () >= 41, msg);
connected_clusters<T> &local = m_per_cell_clusters [cell.cell_index ()];
local.build_clusters (cell, shape_flags, conn);
local.build_clusters (cell, shape_flags, conn, attr_equivalence);
}
template <class T>

View File

@ -30,6 +30,7 @@
#include "dbBoxTree.h"
#include "dbCell.h"
#include "dbInstElement.h"
#include "tlEquivalenceClusters.h"
#include <map>
#include <set>
@ -463,8 +464,13 @@ public:
* This method will only build the local clusters. Child cells
* are not taken into account. Only the shape types listed in
* shape_flags are taken.
*
* If attr_equivalence is non-null, all clusters with attributes
* listed as equivalent in this object are joined. Additional
* cluster joining may happen in this case, because multi-attribute
* assignment might create connections too.
*/
void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence = 0);
/**
* @brief Creates and inserts a new clusters
@ -490,6 +496,8 @@ private:
box_type m_bbox;
tree_type m_clusters;
size_t m_next_dummy_id;
void apply_attr_equivalences (const tl::equivalence_clusters<unsigned int> &attr_equivalence);
};
/**
@ -740,7 +748,7 @@ public:
/**
* @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity
*/
void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence = 0);
/**
* @brief Gets the connected clusters for a given cell
@ -778,10 +786,10 @@ public:
ClusterInstance make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector<db::InstElement> &path);
private:
void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence);
void build_hier_connections (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn);
void build_hier_connections_for_cells (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const std::vector<db::cell_index_type> &cells, const db::Connectivity &conn, tl::RelativeProgress &progress);
void do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
void do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence = 0);
std::map<db::cell_index_type, connected_clusters<T> > m_per_cell_clusters;
};

View File

@ -226,7 +226,7 @@ size_t LayoutToNetlist::global_net_id (const std::string &name)
return m_conn.global_net_id (name);
}
void LayoutToNetlist::extract_netlist ()
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")));
@ -236,7 +236,7 @@ void LayoutToNetlist::extract_netlist ()
}
db::NetlistExtractor netex;
netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters);
netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label);
m_netlist_extracted = true;
}

View File

@ -269,7 +269,7 @@ public:
* @brief Runs the netlist extraction
* See the class description for more details.
*/
void extract_netlist ();
void extract_netlist (bool join_nets_by_label = true);
/**
* @brief Marks the netlist as extracted

View File

@ -33,8 +33,32 @@ NetlistExtractor::NetlistExtractor ()
// .. 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, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters)
NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label)
{
mp_clusters = &clusters;
mp_layout = &dss.const_layout ();
@ -52,7 +76,11 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
// the big part: actually extract the nets
mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn);
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;

View File

@ -82,7 +82,7 @@ public:
* @brief Extract the nets
* See the class description for more details.
*/
void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters);
void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true);
private:
hier_clusters_type *mp_clusters;

View File

@ -213,8 +213,10 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"),
"@brief Gets the global net name for the given global net ID."
) +
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist,
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_nets_by_label", true),
"@brief Runs the netlist extraction\n"
"If join_nets_by_label is true, nets on the same hierarchy level carrying the same label will be connected "
"implicitly even if there is no physical connection.\n"
"See the class description for more details.\n"
) +
gsi::method_ext ("internal_layout", &l2n_internal_layout,

View File

@ -465,7 +465,8 @@ TEST(2_DeviceAndNetExtractionFlat)
// extract the nets
net_ex.extract_nets (dss, conn, nl, cl);
// don't use "join_nets_by_label" because the flattened texts will spoil everything
net_ex.extract_nets (dss, conn, nl, cl, false);
// debug layers produced for nets
// 202/0 -> Active
@ -547,3 +548,244 @@ TEST(2_DeviceAndNetExtractionFlat)
db::compare_layouts (_this, ly, au);
}
static bool
all_net_names_unique (const db::Circuit &c)
{
std::set<std::string> names;
for (db::Circuit::const_net_iterator n = c.begin_nets (); n != c.end_nets (); ++n) {
if (! n->name ().empty ()) {
if (names.find (n->name ()) != names.end ()) {
return false;
} else {
names.insert (n->name ());
}
}
}
return true;
}
static bool
all_net_names_unique (const db::Netlist &nl)
{
for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) {
if (! all_net_names_unique (*c)) {
return false;
}
}
return true;
}
TEST(3_DeviceAndNetExtractionWithImplicitConnections)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "device_extract_l1_implicit_nets.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("LABEL"));
// original layers
db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss);
db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss);
db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss);
db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss);
db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss);
db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss);
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss);
db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss);
db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss);
db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss);
// derived regions
db::Region rpactive = ractive & rnwell;
db::Region rpgate = rpactive & rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region rnactive = ractive - rnwell;
db::Region rngate = rnactive & rpoly;
db::Region rnsd = rnactive - rngate;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::PolygonRef> cl;
db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS");
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
pmos_ex.extract (dss, dl, nl, cl);
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
nmos_ex.extract (dss, dl, nl, cl);
// perform the net extraction
db::NetlistExtractor net_ex;
db::Connectivity conn;
// Intra-layer
conn.connect (rpsd);
conn.connect (rnsd);
conn.connect (rpoly);
conn.connect (rdiff_cont);
conn.connect (rpoly_cont);
conn.connect (rmetal1);
conn.connect (rvia1);
conn.connect (rmetal2);
// Inter-layer
conn.connect (rpsd, rdiff_cont);
conn.connect (rnsd, rdiff_cont);
conn.connect (rpoly, rpoly_cont);
conn.connect (rpoly_cont, rmetal1);
conn.connect (rdiff_cont, rmetal1);
conn.connect (rmetal1, rvia1);
conn.connect (rvia1, rmetal2);
conn.connect (rpoly, rpoly_lbl); // attaches labels
conn.connect (rmetal1, rmetal1_lbl); // attaches labels
conn.connect (rmetal2, rmetal2_lbl); // attaches labels
// extract the nets
net_ex.extract_nets (dss, conn, nl, cl);
EXPECT_EQ (all_net_names_unique (nl), true);
// debug layers produced for nets
// 202/0 -> Active
// 203/0 -> Poly
// 204/0 -> Diffusion contacts
// 205/0 -> Poly contacts
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
std::map<unsigned int, unsigned int> dump_map;
dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0));
dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0));
dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0));
dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0));
// write nets to layout
db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ());
dump_nets_to_layout (nl, cl, ly, dump_map, cm);
// compare netlist as string
EXPECT_EQ (nl.to_string (),
"Circuit RINGO ():\n"
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $3 (IN=NEXT,$2=$I43,OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $4 (IN=$I3,$2=$I42,OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $5 (IN=$I5,$2=$I44,OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $6 (IN=$I6,$2=$I45,OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $7 (IN=$I7,$2=$I46,OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $8 (IN=$I19,$2=$I39,OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $9 (IN=$I1,$2=$I40,OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $10 (IN=$I2,$2=$I41,OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
"Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n"
" DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
" DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
" DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
" DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
" XTRANS $1 ($1=$2,$2=$4,$3=IN)\n"
" XTRANS $2 ($1=$2,$2=$5,$3=IN)\n"
" XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n"
" XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n"
"Circuit TRANS ($1=$1,$2=$2,$3=$3):\n"
);
// doesn't do anything here, but we test that this does not destroy anything:
nl.combine_devices ();
// make pins for named nets of top-level circuits - this way they are not purged
nl.make_top_level_pins ();
nl.purge ();
// compare netlist as string
EXPECT_EQ (nl.to_string (),
"Circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD'):\n"
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $3 (IN=NEXT,$2=(null),OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $4 (IN=$I3,$2=(null),OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $5 (IN=$I5,$2=(null),OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $6 (IN=$I6,$2=(null),OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $7 (IN=$I7,$2=(null),OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $8 (IN=$I19,$2=(null),OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $9 (IN=$I1,$2=(null),OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
" XINV2 $10 (IN=$I2,$2=(null),OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
"Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n"
" DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
" DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
" DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
" DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_au1_implicit_nets.gds");
db::compare_layouts (_this, ly, au);
}

View File

@ -43,7 +43,8 @@ SOURCES = \
tlUri.cc \
tlLongInt.cc \
tlUniqueId.cc \
tlList.cc
tlList.cc \
tlEquivalenceClusters.cc
HEADERS = \
tlAlgorithm.h \
@ -96,7 +97,8 @@ HEADERS = \
tlUri.h \
tlLongInt.h \
tlUniqueId.h \
tlList.h
tlList.h \
tlEquivalenceClusters.h
equals(HAVE_CURL, "1") {

View File

@ -0,0 +1,30 @@
/*
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 "tlEquivalenceClusters.h"
namespace tl
{
// .. nothing yet ..
}

View File

@ -0,0 +1,263 @@
/*
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_tlEquivalenceClusters
#define HDR_tlEquivalenceClusters
#include "tlCommon.h"
#include "tlAssert.h"
#include <cstddef>
#include <vector>
#include <map>
#include <set>
namespace tl
{
/**
* @brief A utility class forming clusters based on equivalence of a certain attribute
*
* To use this class, feed it with equivalences using "is_same", e.g.
*
* @code
* equivalence_clusters<int> eq;
* // forms two clusters: 1,2,5 and 3,4
* eq.same (1, 2);
* eq.same (3, 4);
* eq.same (1, 5);
* @endcode
*
* Self-equivalence is a way of introduction an attribute without
* an equivalence:
*
* @code
* equivalence_clusters<int> eq;
* // after this, 1 is a known attribute
* eq.same (1, 1);
* eq.has_attribute (1); // ->true
* @endcode
*
* Equivalence clusters can be merged, forming new and bigger clusters.
*
* Eventually, each cluster is represented by a non-zero integer ID.
* The cluster ID can obtained per attribute value using "cluster_id".
* In the above example this will be:
*
* @code
* eq.cluster_id (1); // ->1
* eq.cluster_id (2); // ->1
* eq.cluster_id (3); // ->2
* eq.cluster_id (4); // ->2
* eq.cluster_id (5); // ->1
* eq.cluster_id (6); // ->0 (unknown)
* @endcode
*
* "size" will give the maximum cluster ID.
*/
template <class T>
class equivalence_clusters
{
public:
typedef size_t cluster_id_type;
typedef T attribute_type;
typedef typename std::vector<typename std::map<T, size_t>::iterator>::const_iterator cluster_iterator;
/**
* @brief Creates an empty equivalence cluster
*/
equivalence_clusters ()
{
// .. nothing yet ..
}
/**
* @brief Makes attr1 and attr2 equivalent
*/
void same (const T &attr1, const T &attr2)
{
cluster_id_type cl1 = cluster_id (attr1);
if (attr1 == attr2) {
// special case of "self-identity"
if (! cl1) {
cluster_id_type cl = new_cluster ();
insert (attr1, cl);
}
return;
}
cluster_id_type cl2 = cluster_id (attr2);
if (! cl1 || ! cl2) {
if (cl1) {
insert (attr2, cl1);
} else if (cl2) {
insert (attr1, cl2);
} else {
cluster_id_type cl = new_cluster ();
insert (attr1, cl);
insert (attr2, cl);
}
} else if (cl1 != cl2) {
join (cl1, cl2);
}
}
/**
* @brief Returns true, if attr is part of the equivalence clusters
*/
bool has_attribute (const T &attr) const
{
return m_cluster_id_by_attr.find (attr) != m_cluster_id_by_attr.end ();
}
/**
* @brief Returns the cluster ID for the given attribute of 0 if the attribute is not assigned to a cluster
*/
cluster_id_type cluster_id (const T &attr) const
{
typename std::map<T, size_t>::const_iterator c = m_cluster_id_by_attr.find (attr);
if (c != m_cluster_id_by_attr.end ()) {
return c->second;
} else {
return 0;
}
}
/**
* @brief Applies the equivalences of the other clusters
*
* This method will join clusters already within this equivalence cluster collection
* based on the equivalences listed in the other clusters collection.
*
* In contrast to merge, his method will not introduce new attributes.
*/
void apply_equivalences (const equivalence_clusters &other)
{
std::vector<T> attrs;
for (typename std::map<T, size_t>::const_iterator a = m_cluster_id_by_attr.begin (); a != m_cluster_id_by_attr.end (); ++a) {
if (other.has_attribute (a->first)) {
attrs.push_back (a->first);
}
}
for (typename std::vector<T>::const_iterator a = attrs.begin (); a != attrs.end (); ++a) {
cluster_id_type cl = other.cluster_id (*a);
cluster_iterator b = other.begin_cluster (cl);
cluster_iterator e = other.end_cluster (cl);
for (cluster_iterator i = b; i != e; ++i) {
if ((*i)->first != *a && has_attribute ((*i)->first)) {
same ((*i)->first, *a);
}
}
}
}
/**
* @brief Merges the equivalences of the other clusters into this
*
* This method will add all attributes from the other cluster collection and join
* clusters based on the equivalences there.
*/
void merge (const equivalence_clusters &other)
{
for (cluster_id_type cl = 1; cl <= other.size (); ++cl) {
cluster_iterator b = other.begin_cluster (cl);
cluster_iterator e = other.end_cluster (cl);
for (cluster_iterator i = b; i != e; ++i) {
same ((*b)->first, (*i)->first);
}
}
}
/**
* @brief Returns the number of clusters kept inside this collection
*/
size_t size () const
{
return m_clusters.size ();
}
/**
* @brief Begin iterator for the cluster elements for a given cluster ID.
* To access, the attribute, use "(*i)->first".
*/
cluster_iterator begin_cluster (size_t cluster_id) const
{
tl_assert (cluster_id > 0);
return m_clusters [cluster_id - 1].begin ();
}
/**
* @brief End iterator for the cluster elements for a given cluster ID.
*/
cluster_iterator end_cluster (size_t cluster_id) const
{
tl_assert (cluster_id > 0);
return m_clusters [cluster_id - 1].end ();
}
private:
void insert (const T &attr, cluster_id_type into)
{
typename std::map<T, size_t>::iterator c = m_cluster_id_by_attr.insert (std::make_pair (attr, into)).first;
m_clusters [into - 1].push_back (c);
}
void join (cluster_id_type id, cluster_id_type with_id)
{
tl_assert (id > 0);
tl_assert (with_id > 0);
std::vector<typename std::map<T, size_t>::iterator> &cnew = m_clusters [id - 1];
std::vector<typename std::map<T, size_t>::iterator> &c = m_clusters [with_id - 1];
for (typename std::vector<typename std::map<T, size_t>::iterator>::iterator i = c.begin (); i != c.end (); ++i) {
(*i)->second = id;
cnew.push_back (*i);
}
c.clear ();
m_free_slots.push_back (with_id);
}
cluster_id_type new_cluster ()
{
if (! m_free_slots.empty ()) {
cluster_id_type cl = m_free_slots.back ();
m_free_slots.pop_back ();
return cl;
} else {
m_clusters.push_back (std::vector<typename std::map<T, size_t>::iterator> ());
return m_clusters.size ();
}
}
std::map<T, size_t> m_cluster_id_by_attr;
std::vector<std::vector<typename std::map<T, size_t>::iterator> > m_clusters;
std::vector<size_t> m_free_slots;
};
}
#endif

View File

@ -0,0 +1,211 @@
/*
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 "tlEquivalenceClusters.h"
#include "tlUnitTest.h"
// basics
TEST(1_basics)
{
tl::equivalence_clusters<int> eq;
eq.same (1, 5);
eq.same (2, 3);
eq.same (5, 4);
EXPECT_EQ (eq.cluster_id (1), size_t (1));
EXPECT_EQ (eq.cluster_id (2), size_t (2));
EXPECT_EQ (eq.cluster_id (3), size_t (2));
EXPECT_EQ (eq.cluster_id (4), size_t (1));
EXPECT_EQ (eq.cluster_id (5), size_t (1));
EXPECT_EQ (eq.cluster_id (6), size_t (0));
EXPECT_EQ (eq.size (), size_t (2));
eq.same (2, 6);
EXPECT_EQ (eq.cluster_id (6), size_t (2));
EXPECT_EQ (eq.cluster_id (7), size_t (0));
EXPECT_EQ (eq.size (), size_t (2));
eq.same (7, 8);
EXPECT_EQ (eq.size (), size_t (3));
EXPECT_EQ (eq.cluster_id (6), size_t (2));
EXPECT_EQ (eq.cluster_id (7), size_t (3));
EXPECT_EQ (eq.cluster_id (8), size_t (3));
}
// joining of clusters
TEST(2_join)
{
tl::equivalence_clusters<int> eq;
eq.same (1, 2);
eq.same (3, 4);
eq.same (5, 6);
EXPECT_EQ (eq.cluster_id (1), size_t (1));
EXPECT_EQ (eq.cluster_id (2), size_t (1));
EXPECT_EQ (eq.cluster_id (3), size_t (2));
EXPECT_EQ (eq.cluster_id (4), size_t (2));
EXPECT_EQ (eq.cluster_id (5), size_t (3));
EXPECT_EQ (eq.cluster_id (6), size_t (3));
eq.same (3, 2);
EXPECT_EQ (eq.cluster_id (1), size_t (2));
EXPECT_EQ (eq.cluster_id (2), size_t (2));
EXPECT_EQ (eq.cluster_id (3), size_t (2));
EXPECT_EQ (eq.cluster_id (4), size_t (2));
EXPECT_EQ (eq.cluster_id (5), size_t (3));
EXPECT_EQ (eq.cluster_id (6), size_t (3));
eq.same (4, 5);
EXPECT_EQ (eq.cluster_id (1), size_t (2));
EXPECT_EQ (eq.cluster_id (2), size_t (2));
EXPECT_EQ (eq.cluster_id (3), size_t (2));
EXPECT_EQ (eq.cluster_id (4), size_t (2));
EXPECT_EQ (eq.cluster_id (5), size_t (2));
EXPECT_EQ (eq.cluster_id (6), size_t (2));
eq.same (10, 11);
eq.same (12, 13);
EXPECT_EQ (eq.cluster_id (10), size_t (3));
EXPECT_EQ (eq.cluster_id (11), size_t (3));
EXPECT_EQ (eq.cluster_id (12), size_t (1));
EXPECT_EQ (eq.cluster_id (13), size_t (1));
}
// size
TEST(3_size)
{
tl::equivalence_clusters<int> eq;
eq.same (1, 2);
EXPECT_EQ (eq.size (), size_t (1));
eq.same (2, 4);
EXPECT_EQ (eq.size (), size_t (1));
eq.same (5, 6);
EXPECT_EQ (eq.size (), size_t (2));
}
// has_attribute
TEST(4_has_attribute)
{
tl::equivalence_clusters<int> eq;
eq.same (1, 1);
EXPECT_EQ (eq.has_attribute (1), true);
EXPECT_EQ (eq.has_attribute (2), false);
eq.same (1, 2);
EXPECT_EQ (eq.has_attribute (1), true);
EXPECT_EQ (eq.has_attribute (2), true);
EXPECT_EQ (eq.has_attribute (3), false);
}
std::string eq2string (const tl::equivalence_clusters<int> &eq)
{
std::string res;
for (size_t c = 1; c <= eq.size (); ++c) {
for (tl::equivalence_clusters<int>::cluster_iterator i = eq.begin_cluster (c); i != eq.end_cluster (c); ++i) {
if (i != eq.begin_cluster (c)) {
res += ",";
} else if (! res.empty ()) {
res += ";";
}
res += tl::to_string ((*i)->first);
}
}
return res;
}
// iterator
TEST(5_iterator)
{
tl::equivalence_clusters<int> eq;
eq.same (1, 1);
EXPECT_EQ (eq2string (eq), "1");
eq.same (1, 2);
EXPECT_EQ (eq2string (eq), "1,2");
eq.same (3, 4);
EXPECT_EQ (eq2string (eq), "1,2;3,4");
eq.same (10, 11);
EXPECT_EQ (eq2string (eq), "1,2;3,4;10,11");
eq.same (1, 10);
EXPECT_EQ (eq2string (eq), "1,2,10,11;3,4");
}
// apply_other_equivalences
TEST(6_apply_equivalences)
{
tl::equivalence_clusters<int> eq;
eq.same (1, 1);
eq.same (2, 2);
eq.same (3, 4);
eq.same (5, 6);
EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6");
tl::equivalence_clusters<int> eq2;
eq2.same (2, 2);
eq2.same (4, 5);
eq2.same (4, 10);
eq2.same (11, 11);
EXPECT_EQ (eq2string (eq2), "2;4,5,10;11");
eq.apply_equivalences (eq2);
EXPECT_EQ (eq2string (eq), "1;2;5,6,3,4");
}
// merge
TEST(7_merge)
{
tl::equivalence_clusters<int> eq;
eq.same (1, 1);
eq.same (2, 2);
eq.same (3, 4);
eq.same (5, 6);
EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6");
tl::equivalence_clusters<int> eq2;
eq2.same (2, 2);
eq2.same (4, 5);
eq2.same (4, 10);
eq2.same (11, 11);
EXPECT_EQ (eq2string (eq2), "2;4,5,10;11");
eq.merge (eq2);
EXPECT_EQ (eq2string (eq), "1;2;3,4,5,6,10;11");
}

View File

@ -36,7 +36,8 @@ SOURCES = \
tlInt128Support.cc \
tlLongInt.cc \
tlUniqueIdTests.cc \
tlListTests.cc
tlListTests.cc \
tlEquivalenceClustersTests.cc
!equals(HAVE_QT, "0") {

Binary file not shown.

Binary file not shown.