diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index a8175d2dc..382923fd4 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -317,6 +317,87 @@ void Circuit::unregister_ref (SubCircuit *r) m_refs.erase (r); } +void Circuit::flatten_subcircuit (SubCircuit *subcircuit) +{ + const db::Circuit *c = subcircuit->circuit_ref (); + + // copy the nets, produce a net map + + std::map net2net; + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + // TODO: cannot join pins through subcircuits currently + tl_assert (n->pin_count () <= 1); + + db::Net *outside_net = 0; + + if (n->pin_count () > 0) { + size_t pin_id = n->begin_pins ()->pin_id (); + outside_net = subcircuit->net_for_pin (pin_id); + } else { + outside_net = new db::Net (); + if (! n->name ().empty ()) { + outside_net->set_name (subcircuit->expanded_name () + "." + n->name ()); + } + add_net (outside_net); + } + + net2net.insert (std::make_pair (n.operator-> (), outside_net)); + + } + + // copy the devices + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + + db::Device *device = new db::Device (*d); + if (! d->name ().empty ()) { + device->set_name (subcircuit->expanded_name () + "." + d->name ()); + } + add_device (device); + + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::Net *tnet = d->net_for_terminal (t->id ()); + if (tnet) { + std::map::const_iterator n2n = net2net.find (tnet); + tl_assert (n2n != net2net.end ()); + device->connect_terminal (t->id (), n2n->second); + } + + } + + } + + // copy the subcircuits + + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + + db::SubCircuit *new_subcircuit = new db::SubCircuit (*sc); + if (! new_subcircuit->name ().empty ()) { + new_subcircuit->set_name (subcircuit->expanded_name () + "." + new_subcircuit->name ()); + } + add_subcircuit (new_subcircuit); + + const db::Circuit *cr = sc->circuit_ref (); + for (db::Circuit::const_pin_iterator p = cr->begin_pins (); p != cr->end_pins (); ++p) { + + const db::Net *pnet = sc->net_for_pin (p->id ()); + if (pnet) { + std::map::const_iterator n2n = net2net.find (pnet); + tl_assert (n2n != net2net.end ()); + new_subcircuit->connect_pin (p->id (), n2n->second); + } + + } + + } + + remove_subcircuit (subcircuit); +} + void Circuit::translate_circuits (const std::map &map) { for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 9f0b362b7..042a3e144 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -589,6 +589,15 @@ public: */ void combine_devices (); + /** + * @brief Flattens the given subcircuit + * + * The subcircuit is resolved into the parent circuit and finally removed. + * Net, device and subcircuit names are decorated with the subcircuit's name + * if required. + */ + void flatten_subcircuit (SubCircuit *subcircuit); + private: friend class Netlist; friend class Net; diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 4396c8e4d..c0550767c 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -369,6 +369,20 @@ void Netlist::remove_circuit (Circuit *circuit) m_circuits.erase (circuit); } +void Netlist::flatten_circuit (Circuit *circuit) +{ + std::vector refs; + for (db::Circuit::refs_iterator sc = circuit->begin_refs (); sc != circuit->end_refs (); ++sc) { + refs.push_back (sc.operator-> ()); + } + + for (std::vector::const_iterator r = refs.begin (); r != refs.end (); ++r) { + (*r)->circuit ()->flatten_subcircuit (*r); + } + + remove_circuit (circuit); +} + DeviceClass *Netlist::device_class_by_name (const std::string &name) { for (device_class_iterator d = begin_device_classes (); d != end_device_classes (); ++d) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 98d47f84b..95555d29c 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -138,6 +138,12 @@ public: */ void remove_circuit (Circuit *circuit); + /** + * @brief Flattens the given circuit + * All subcircuit references are replaced by the content of this circuit. + */ + void flatten_circuit (Circuit *circuit); + /** * @brief Begin iterator for the circuits of the netlist (non-const version) */ diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 40a5968b9..e65c03e04 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -956,6 +956,11 @@ Class decl_dbCircuit ("db", "Circuit", gsi::method ("remove_subcircuit", &db::Circuit::remove_subcircuit, gsi::arg ("subcircuit"), "@brief Removes the given subcircuit from the circuit\n" ) + + gsi::method ("flatten_subcircuit", &db::Circuit::flatten_subcircuit, gsi::arg ("subcircuit"), + "@brief Flattens a subcircuit\n" + "This method will substitute the given subcircuit by it's contents. The subcircuit is removed " + "after this." + ) + gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits, "@brief Iterates over the subcircuits of the circuit" ) + @@ -1084,6 +1089,11 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Removes the given circuit object from the netlist\n" "After the object has been removed, it becomes invalid and cannot be used further." ) + + gsi::method ("flatten_circuit", &db::Netlist::flatten_circuit, gsi::arg ("circuit"), + "@brief Flattens a subcircuit\n" + "This method will substitute all instances (subcircuits) of the given circuit by it's " + "contents. After this, the circuit is removed." + ) + gsi::method ("circuit_by_cell_index", (db::Circuit *(db::Netlist::*) (db::cell_index_type)) &db::Netlist::circuit_by_cell_index, gsi::arg ("cell_index"), "@brief Gets the circuit object for a given cell index.\n" "If the cell index is not valid or no circuit is registered with this index, nil is returned." diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 8169d05a3..3ab084247 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -22,6 +22,7 @@ #include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" #include "tlUnitTest.h" #include "tlString.h" @@ -1104,3 +1105,176 @@ TEST(13_DeviceAbstract) EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true); } +TEST(20_FlattenSubCircuit) +{ + db::Netlist nl; + + 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" + ); + + db::Netlist nl2; + + nl2 = nl; + db::Circuit *inv2 = nl2.circuit_by_name ("INV2"); + inv2->flatten_subcircuit (inv2->subcircuit_by_name ("SC1")); + + EXPECT_EQ (nl2.to_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" + " 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,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" + ); + + nl2.flatten_circuit (nl2.circuit_by_name ("PTRANS")); + nl2.flatten_circuit (nl2.circuit_by_name ("NTRANS")); + + EXPECT_EQ (nl2.to_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" + ); +} + +TEST(21_FlattenSubCircuit2) +{ + db::Netlist nl; + + 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 RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "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" + ); + + db::Netlist nl2; + + nl2 = nl; + db::Circuit *inv2 = nl2.circuit_by_name ("RINGO"); + inv2->flatten_subcircuit (inv2->subcircuit_by_name ("INV2_SC1")); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + " subcircuit PTRANS INV2_SC1.SC1 ($1=VDD,$2=FB,$3=$I8);\n" + " subcircuit NTRANS INV2_SC1.SC2 ($1=VSS,$2=FB,$3=$I8);\n" + " subcircuit PTRANS INV2_SC1.SC3 ($1=VDD,$2=OSC,$3=FB);\n" + " subcircuit NTRANS INV2_SC1.SC4 ($1=VSS,$2=OSC,$3=FB);\n" + "end;\n" + "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,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" + ); + + inv2->flatten_subcircuit (inv2->subcircuit_by_name ("INV2_SC2")); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit PTRANS INV2_SC1.SC1 ($1=VDD,$2=FB,$3=$I8);\n" + " subcircuit NTRANS INV2_SC1.SC2 ($1=VSS,$2=FB,$3=$I8);\n" + " subcircuit PTRANS INV2_SC1.SC3 ($1=VDD,$2=OSC,$3=FB);\n" + " subcircuit NTRANS INV2_SC1.SC4 ($1=VSS,$2=OSC,$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC1 ($1=VDD,$2=(null),$3=FB);\n" + " subcircuit NTRANS INV2_SC2.SC2 ($1=VSS,$2=(null),$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC3 ($1=VDD,$2=$I8,$3=(null));\n" + " subcircuit NTRANS INV2_SC2.SC4 ($1=VSS,$2=$I8,$3=(null));\n" + "end;\n" + "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,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" + ); + + nl2 = nl; + nl2.flatten_circuit (nl2.circuit_by_name ("INV2")); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit PTRANS INV2_SC1.SC1 ($1=VDD,$2=FB,$3=$I8);\n" + " subcircuit NTRANS INV2_SC1.SC2 ($1=VSS,$2=FB,$3=$I8);\n" + " subcircuit PTRANS INV2_SC1.SC3 ($1=VDD,$2=OSC,$3=FB);\n" + " subcircuit NTRANS INV2_SC1.SC4 ($1=VSS,$2=OSC,$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC1 ($1=VDD,$2=(null),$3=FB);\n" + " subcircuit NTRANS INV2_SC2.SC2 ($1=VSS,$2=(null),$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC3 ($1=VDD,$2=$I8,$3=(null));\n" + " subcircuit NTRANS INV2_SC2.SC4 ($1=VSS,$2=$I8,$3=(null));\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" + ); +} diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 0897f9ad2..ebdd87456 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -714,6 +714,66 @@ END end + def test_11_FlattenCircuits + + 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 + + nl2 = nl.dup + inv2 = nl2.circuit_by_name("INV2") + inv2.flatten_subcircuit(inv2.subcircuit_by_name("SC1")) + + assert_equal(nl2.to_s, <<"END") +circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); + device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + 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,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 + + nl2.flatten_circuit(nl2.circuit_by_name("PTRANS")) + nl2.flatten_circuit(nl2.circuit_by_name("NTRANS")) + + assert_equal(nl2.to_s, <<"END") +circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); + device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); +end; +END + + end + end load("test_epilogue.rb")