WIP: join_nets implemented, join_symmetric_nets: enhanced detection of symmetric nets.

This commit is contained in:
Matthias Koefferlein 2020-02-26 23:55:23 +01:00
parent 08af8d85c4
commit b35429291e
14 changed files with 318 additions and 32 deletions

View File

@ -342,6 +342,30 @@ void Circuit::remove_net (Net *net)
m_nets.erase (net);
}
void Circuit::join_nets (Net *net, Net *with)
{
while (with->begin_terminals () != with->end_terminals ()) {
db::Device *device = const_cast<db::Device *> (with->begin_terminals ()->device ());
device->connect_terminal (with->begin_terminals ()->terminal_id (), net);
}
while (with->begin_subcircuit_pins () != with->end_subcircuit_pins ()) {
db::SubCircuit *subcircuit = const_cast<db::SubCircuit *> (with->begin_subcircuit_pins ()->subcircuit ());
subcircuit->connect_pin (with->begin_subcircuit_pins ()->pin_id (), net);
}
while (with->begin_pins () != with->end_pins ()) {
connect_pin (with->begin_pins ()->pin_id (), net);
}
// TODO: join clusters too, join net properties(?)
if (netlist ()->callbacks ()) {
netlist ()->callbacks ()->link_nets (net, with);
}
remove_net (with);
}
void Circuit::add_device (Device *device)
{
device->set_circuit (this);

View File

@ -416,6 +416,11 @@ public:
*/
void remove_net (Net *net);
/**
* @brief Joins the second net with the first one and removes the second net
*/
void join_nets (Net *net, Net *with);
/**
* @brief Gets the number of nets
*/

View File

@ -198,9 +198,23 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const
return region.release ();
}
void LayoutToNetlist::link_nets (const db::Net *net, const db::Net *with)
{
if (! net->circuit () || net->circuit () != with->circuit () || ! internal_layout ()
|| ! internal_layout ()->is_valid_cell_index (net->circuit ()->cell_index ())
|| net->cluster_id () == 0 || with->cluster_id () == 0) {
return;
}
connected_clusters<db::PolygonRef> &clusters = m_net_clusters.clusters_per_cell (net->circuit ()->cell_index ());
clusters.join_cluster_with (net->cluster_id (), with->cluster_id ());
}
size_t LayoutToNetlist::link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &dtrans)
{
if (! subcircuit_net->circuit () || ! has_internal_layout () || ! internal_layout ()->is_valid_cell_index (parent_circuit->cell_index ())) {
if (! subcircuit_net->circuit () || ! has_internal_layout ()
|| ! internal_layout ()->is_valid_cell_index (parent_circuit->cell_index ())
|| subcircuit_net->cluster_id () == 0) {
return 0;
}

View File

@ -798,6 +798,7 @@ private:
// implementation of NetlistManipulationCallbacks
virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans);
virtual void link_nets (const db::Net *net, const db::Net *with);
};
}

View File

@ -47,6 +47,7 @@ public:
virtual ~NetlistManipulationCallbacks () { }
virtual size_t link_net_to_parent_circuit (const db::Net *subcircuit_net, db::Circuit *parent_circuit, const db::DCplxTrans &trans) = 0;
virtual void link_nets (const db::Net *net, const db::Net *with) = 0;
};
/**

View File

@ -28,6 +28,7 @@
#include "tlEquivalenceClusters.h"
#include "tlLog.h"
#include "tlEnv.h"
#include "tlInternational.h"
#include <cstring>
@ -3626,17 +3627,19 @@ NetlistComparer::do_subcircuit_assignment (const db::Circuit *c1, const db::NetG
}
}
// @@@
bool derive_symmetry_groups (const db::NetGraph &graph, const tl::equivalence_clusters<const NetGraphNode *> &identical_nodes, std::set<size_t> &considered_nodes, const std::set<size_t> &symmetry_group, std::list<std::set<size_t> > &symmetry_groups)
static bool derive_symmetry_groups (const db::NetGraph &graph, const tl::equivalence_clusters<const NetGraphNode *> &identical_nodes, std::set<size_t> &considered_nodes, const std::set<size_t> &symmetry_group, std::list<std::set<size_t> > &symmetry_groups)
{
std::set<size_t> cids;
std::set<size_t> new_symmetry_group;
NetGraphNode::edge_type::first_type edge;
std::vector<NetGraphNode::edge_type> common_nodes_first;
bool has_candidate = true;
for (std::set<size_t>::const_iterator g = symmetry_group.begin (); g != symmetry_group.end (); ++g) {
for (std::set<size_t>::const_iterator g = symmetry_group.begin (); g != symmetry_group.end () && has_candidate; ++g) {
std::vector<NetGraphNode::edge_type> common_nodes;
const NetGraphNode &n = graph.node (*g);
for (NetGraphNode::edge_iterator e = n.begin (); e != n.end () && has_candidate; ++e) {
@ -3646,7 +3649,8 @@ bool derive_symmetry_groups (const db::NetGraph &graph, const tl::equivalence_cl
}
const NetGraphNode *other = &graph.node (e->second.first);
if (identical_nodes.has_attribute (other)) {
// NOTE: nodes with pins don't go into a symmetry group as we don't know what the pins connect to
if (other->net ()->pin_count () == 0 && identical_nodes.has_attribute (other)) {
if (cids.empty ()) {
edge = e->first;
@ -3663,6 +3667,21 @@ bool derive_symmetry_groups (const db::NetGraph &graph, const tl::equivalence_cl
}
}
} else {
common_nodes.push_back (*e);
}
}
if (has_candidate) {
// all other edges need to have identical destinations for the symmetry group to be
// actually symmetric
std::sort (common_nodes.begin (), common_nodes.end ());
if (g == symmetry_group.begin ()) {
common_nodes_first.swap (common_nodes);
} else if (common_nodes_first != common_nodes) {
has_candidate = false;
}
}
@ -3692,16 +3711,17 @@ bool derive_symmetry_groups (const db::NetGraph &graph, const tl::equivalence_cl
return has_candidate;
}
void join_symmetric_nodes (db::Circuit *circuit)
void
NetlistComparer::join_symmetric_nodes (db::Circuit *circuit)
{
db::CircuitCategorizer circuit_categorizer;
db::DeviceCategorizer device_categorizer;
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Join symmetric nodes for circuit: ")) + circuit->name ());
db::DeviceFilter device_filter (m_cap_threshold, m_res_threshold);
db::CircuitPinMapper circuit_pin_mapper;
db::DeviceFilter device_filter (0.0, 0.0);
std::map<const db::Circuit *, CircuitMapper> circuit_and_pin_mapping;
db::NetGraph graph;
graph.build (circuit, device_categorizer, circuit_categorizer, device_filter, &circuit_and_pin_mapping, &circuit_pin_mapper);
graph.build (circuit, *mp_device_categorizer, *mp_circuit_categorizer, device_filter, &circuit_and_pin_mapping, &circuit_pin_mapper);
// sort the nodes so we can easily identify the identical ones (in terms of topology)
// nodes are identical if the attached devices and circuits are of the same kind and with the same parameters
@ -3746,16 +3766,29 @@ void join_symmetric_nodes (db::Circuit *circuit)
}
printf("@@@ symmetry groups:\n");
// join the nets
for (std::list<std::set<size_t> >::const_iterator g = symmetry_groups.begin (); g != symmetry_groups.end (); ++g) {
printf("@@@ group:\n");
for (std::set<size_t>::const_iterator i = g->begin (); i != g->end (); ++i) {
printf("@@@ %s\n", graph.node (*i).net () ? graph.node (*i).net ()->expanded_name ().c_str () : "(null)");
if (i != g->begin ()) {
circuit->join_nets (const_cast<db::Net *> (graph.net_by_node_index (*g->begin ())), const_cast<db::Net *> (graph.net_by_node_index (*i)));
}
}
}
if (! symmetry_groups.empty () && tl::verbosity () >= 30) {
tl::info << tl::to_string (tr ("Symmetry groups:"));
int index = 0;
for (std::list<std::set<size_t> >::const_iterator g = symmetry_groups.begin (); g != symmetry_groups.end (); ++g) {
tl::info << " [" << index << "] " << tl::noendl;
for (std::set<size_t>::const_iterator i = g->begin (); i != g->end (); ++i) {
tl::info << (i == g->begin () ? "" : ",") << (graph.node (*i).net () ? graph.node (*i).net ()->expanded_name ().c_str () : "(null)") << tl::noendl;
}
++index;
tl::info << "";
}
}
fflush(stdout);
}
// @@@
}

View File

@ -302,6 +302,20 @@ public:
*/
bool compare (const db::Netlist *a, const db::Netlist *b, db::NetlistCompareLogger *logger) const;
/**
* @brief Joins symmetric nodes in the given circuit
*
* Nodes are symmetric if their exchanging would not modify the circuit.
* Hence they will carry the same potential and can be connected (joined).
* This will simplify the circuit and can be applied before device combination
* to render a schematic-equivalent netlist in some cases (split gate option).
*
* This algorithm will apply the comparer's settings to the symmetry
* condition (device filtering, device compare tolerances, device class
* equivalence etc.).
*/
void join_symmetric_nodes (db::Circuit *circuit);
private:
// No copying
NetlistComparer (const NetlistComparer &);
@ -328,9 +342,6 @@ protected:
bool m_dont_consider_net_names;
};
// @@@
void DB_PUBLIC join_symmetric_nodes (db::Circuit *circuit);
}
namespace tl

View File

@ -1201,6 +1201,12 @@ Class<db::Circuit> decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit",
gsi::method ("remove_net", &db::Circuit::remove_net, gsi::arg ("net"),
"@brief Removes the given net from the circuit\n"
) +
gsi::method ("join_nets", &db::Circuit::join_nets, gsi::arg ("net"), gsi::arg ("with"),
"@brief Joins (connects) two nets into one\n"
"This method will connect the 'with' net with 'net' and remove 'with'.\n"
"\n"
"This method has been introduced in version 0.26.4."
) +
gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets,
"@brief Iterates over the nets of the circuit"
) +

View File

@ -584,6 +584,75 @@ TEST(1_BasicExtraction)
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2");
// use this opportunity to check joining of nets with cluster joining
db::Circuit *top = l2n.netlist ()->circuit_by_name ("RINGO");
top->join_nets (top->net_by_name ("VSS"), top->net_by_name ("VDD"));
top->join_nets (top->net_by_name ("FB"), top->net_by_name ("OSC"));
CHECKPOINT ();
db::compare_netlist (_this, *l2n.netlist (),
"circuit RINGO (FB=FB,OSC=FB,VSS=VSS,VDD=VDD);\n"
" subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=FB,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VSS);\n"
" subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VSS);\n"
"end;\n"
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n"
" device PMOS $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"
" device PMOS $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"
" device NMOS $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"
" device NMOS $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"
"end;\n"
);
// do some probing after purging
// top level
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:FB");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2");
// compare the collected test data
{
db::Layout ly2;
ly2.dbu (ly.dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/);
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd;
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd;
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get ();
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get ();
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get ();
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get ();
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
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_joined_nets.gds");
db::compare_layouts (_this, ly2, au);
}
}
TEST(2_Probing)

View File

@ -3745,11 +3745,9 @@ TEST(25_SplitGates)
prep_nl (nl1, nls1);
prep_nl (nl2, nls2);
db::join_symmetric_nodes (nl1.circuit_by_name ("NAND2")); // @@@
db::NetlistComparer comp;
comp.join_symmetric_nodes (nl1.circuit_by_name ("NAND2")); // @@@
#if 0
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
bool good = comp.compare (&nl1, &nl2);
std::string txt = logger.text ();

View File

@ -371,15 +371,6 @@ TEST(1_DeviceAndNetExtraction)
" device NMOS $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"
"end;\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_au1.gds");
db::compare_layouts (_this, ly, au);
}
TEST(2_DeviceAndNetExtractionFlat)

View File

@ -1525,3 +1525,89 @@ TEST(23_NetlistObject)
pin3 = pin2;
EXPECT_EQ (pin3.property (1).to_string (), "hello");
}
TEST(24_JoinNets)
{
db::Netlist nl;
db::Circuit *c;
db::DeviceClass *dc;
dc = new db::DeviceClassMOS3Transistor ();
dc->set_name ("NMOS");
nl.add_device_class (dc);
dc = new db::DeviceClassMOS3Transistor ();
dc->set_name ("PMOS");
nl.add_device_class (dc);
nl.from_string (
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n"
" subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n"
" subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n"
" subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n"
" subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n"
"end;\n"
"circuit PTRANS ($1=$1,$2=$2,$3=$3);\n"
" device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n"
"end;\n"
"circuit NTRANS ($1=$1,$2=$2,$3=$3);\n"
" device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n"
"end;\n"
);
c = nl.circuit_by_name ("INV2");
c->join_nets (c->net_by_name ("IN"), c->net_by_name ("OUT"));
EXPECT_EQ (nl.to_string (),
"circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5);\n"
" subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n"
" subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n"
" subcircuit PTRANS SC3 ($1=$5,$2=IN,$3=$2);\n"
" subcircuit NTRANS SC4 ($1=$4,$2=IN,$3=$2);\n"
"end;\n"
"circuit PTRANS ($1=$1,$2=$2,$3=$3);\n"
" device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
"circuit NTRANS ($1=$1,$2=$2,$3=$3);\n"
" device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
}
TEST(25_JoinNets)
{
db::Netlist nl;
db::Circuit *c;
db::DeviceClass *dc;
dc = new db::DeviceClassMOS3Transistor ();
dc->set_name ("NMOS");
nl.add_device_class (dc);
dc = new db::DeviceClassMOS3Transistor ();
dc->set_name ("PMOS");
nl.add_device_class (dc);
nl.from_string (
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n"
" device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
c = nl.circuit_by_name ("INV2");
c->join_nets (c->net_by_name ("IN"), c->net_by_name ("OUT"));
EXPECT_EQ (nl.to_string (),
"circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5);\n"
" device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $2 (S=$5,G=$2,D=IN) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $4 (S=$4,G=$2,D=IN) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
}

Binary file not shown.

View File

@ -1015,6 +1015,53 @@ END
assert_equal(nl2.to_s, <<"END")
circuit RINGO (IN=(null),OSC=(null),VSS=(null),VDD=(null));
end;
END
end
def test_13_JoinNets
nl = RBA::Netlist::new
dc = RBA::DeviceClassMOS3Transistor::new
dc.name = "NMOS"
nl.add(dc)
dc = RBA::DeviceClassMOS3Transistor::new
dc.name = "PMOS"
nl.add(dc)
nl.from_s(<<"END")
circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);
subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);
subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);
subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);
subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);
end;
circuit PTRANS ($1=$1,$2=$2,$3=$3);
device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);
end;
circuit NTRANS ($1=$1,$2=$2,$3=$3);
device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);
end;
END
c = nl.circuit_by_name("INV2")
c.join_nets(c.net_by_name("IN"), c.net_by_name("OUT"))
assert_equal(nl.to_s, <<"END")
circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5);
subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);
subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);
subcircuit PTRANS SC3 ($1=$5,$2=IN,$3=$2);
subcircuit NTRANS SC4 ($1=$4,$2=IN,$3=$2);
end;
circuit PTRANS ($1=$1,$2=$2,$3=$3);
device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);
end;
circuit NTRANS ($1=$1,$2=$2,$3=$3);
device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);
end;
END
end