diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index a091c7bf0..f3af931ab 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -22,6 +22,8 @@ #include "dbNetlistDeviceClasses.h" #include "tlClassRegistry.h" +#include "tlTimer.h" +#include "tlLog.h" namespace db { @@ -313,7 +315,7 @@ public: 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) { + if (DeviceClassMOS3Transistor::lengths_are_identical (a, b)) { combine_parameters (a, b); @@ -365,7 +367,7 @@ public: 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) { + if (DeviceClassMOS3Transistor::lengths_are_identical (a, b)) { combine_parameters (a, b); @@ -614,6 +616,234 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6)); } +bool +DeviceClassMOS3Transistor::is_source_terminal (size_t tid) const +{ + if (is_strict ()) { + return tid == DeviceClassMOS3Transistor::terminal_id_S; + } else { + return tid == DeviceClassMOS3Transistor::terminal_id_S || tid == DeviceClassMOS3Transistor::terminal_id_D; + } +} + +bool +DeviceClassMOS3Transistor::is_drain_terminal (size_t tid) const +{ + if (is_strict ()) { + return tid == DeviceClassMOS3Transistor::terminal_id_D; + } else { + return tid == DeviceClassMOS3Transistor::terminal_id_S || tid == DeviceClassMOS3Transistor::terminal_id_D; + } +} + +bool +DeviceClassMOS3Transistor::lengths_are_identical (const db::Device *a, const db::Device *b) +{ + return (fabs (a->parameter_value (DeviceClassMOS3Transistor::param_id_L) - b->parameter_value (DeviceClassMOS3Transistor::param_id_L)) < 1e-6); +} + +bool +DeviceClassMOS3Transistor::net_is_source_drain_connection (const db::Net *net) const +{ + if (net->pin_count () > 0) { + return false; + } + if (net->subcircuit_pin_count () > 0) { + return false; + } + if (net->terminal_count () != 2) { + return false; + } + + db::Net::const_terminal_iterator t1 = net->begin_terminals (); + db::Net::const_terminal_iterator t2 = t1; + ++t2; + + if (t1->device_class () != this || t2->device_class () != this) { + return false; + } + + return ((is_source_terminal (t1->terminal_id ()) && is_drain_terminal (t2->terminal_id ())) || + (is_drain_terminal (t1->terminal_id ()) && is_source_terminal (t2->terminal_id ()))); +} + +namespace { + +class SplitGateDeviceChain +{ +public: + typedef std::vector::const_iterator device_iterator; + typedef std::vector::const_iterator net_iterator; + + SplitGateDeviceChain () { } + + void add_device (const db::Device *device) { m_devices.push_back (device); } + void add_net (const db::Net *net) { m_nets.push_back (net); } + + device_iterator begin_devices () const { return m_devices.begin (); } + device_iterator end_devices () const { return m_devices.end (); } + + net_iterator begin_nets () const { return m_nets.begin (); } + net_iterator end_nets () const { return m_nets.end (); } + + void clear () + { + m_devices.clear (); + m_nets.clear (); + } + + bool is_compatible (const SplitGateDeviceChain &other, bool with_bulk) const + { + if (m_devices.size () != other.m_devices.size ()) { + return false; + } + + device_iterator d = begin_devices (), dd = other.begin_devices (); + for ( ; d != end_devices (); ++d, ++dd) { + if ((*d)->net_for_terminal (DeviceClassMOS3Transistor::terminal_id_G) != (*dd)->net_for_terminal (DeviceClassMOS3Transistor::terminal_id_G)) { + return false; + } + if (with_bulk && (*d)->net_for_terminal (DeviceClassMOS4Transistor::terminal_id_B) != (*dd)->net_for_terminal (DeviceClassMOS4Transistor::terminal_id_B)) { + return false; + } + if (! DeviceClassMOS3Transistor::lengths_are_identical (*d, *dd)) { + return false; + } + } + + return true; + } + + void join_nets (const SplitGateDeviceChain &other, db::Circuit *circuit) const + { + net_iterator n = begin_nets (), nn = other.begin_nets (); + for ( ; n != end_nets (); ++n, ++nn) { + if (tl::verbosity () >= 40) { + tl::log << "Joining nets: " << (*n)->expanded_name () << " and " << (*nn)->expanded_name (); + } + circuit->join_nets (const_cast (*n), const_cast (*nn)); + } + } + +private: + std::vector m_devices; + std::vector m_nets; +}; + +} + +void +DeviceClassMOS3Transistor::join_split_gates (db::Circuit *circuit) const +{ + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("join split gates ")) + name () + " (" + circuit->name () + ")"); + + std::set seen_nets; + + for (db::Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { + + if (seen_nets.find (n.operator-> ()) != seen_nets.end ()) { + continue; + } + seen_nets.insert (n.operator-> ()); + + if (net_is_source_drain_connection (n.operator-> ())) { + continue; + } + + std::map > chains; + SplitGateDeviceChain chain; + + for (db::Net::const_terminal_iterator t = n->begin_terminals (); t != n->end_terminals (); ++t) { + + if (t->device_class () == this && is_source_terminal (t->terminal_id ())) { + + // form a new chain + + chain.clear (); + + size_t tid = t->terminal_id (); + const db::Device *d = t->device (); + const db::Net *nn = 0; + + while (true) { + + chain.add_device (d); + + size_t other_tid = (tid == DeviceClassMOS3Transistor::terminal_id_S ? DeviceClassMOS3Transistor::terminal_id_D : DeviceClassMOS3Transistor::terminal_id_S); + nn = d->net_for_terminal (other_tid); + if (! nn || ! net_is_source_drain_connection (nn)) { + break; + } + + const db::Device *other_device = 0; + for (db::Net::const_terminal_iterator tt = nn->begin_terminals (); tt != nn->end_terminals (); ++tt) { + if (tt->device () != d) { + other_tid = tt->terminal_id (); + other_device = tt->device (); + break; + } + } + tl_assert (other_device); + + if (seen_nets.find (nn) != seen_nets.end ()) { + nn = 0; + break; + } + seen_nets.insert (nn); + + tid = other_tid; + d = other_device; + chain.add_net (nn); + + } + + if (nn && chain.begin_nets () != chain.end_nets ()) { + chains [nn].push_back (chain); + } + + } + + } + + // identify compatible chains and join their S/D nodes + + for (std::map >::iterator cs = chains.begin (); cs != chains.end (); ++cs) { + + std::vector::iterator> compatibles; + + while (! cs->second.empty ()) { + + compatibles.clear (); + + std::list::iterator c = cs->second.begin (); + std::list::iterator cc = c; + ++cc; + while (cc != cs->second.end ()) { + if (cc->is_compatible (*c, has_bulk_pin ())) { + compatibles.push_back (cc); + } + ++cc; + } + + for (std::vector::iterator>::const_iterator i = compatibles.begin (); i != compatibles.end (); ++i) { + c->join_nets (**i, circuit); + cs->second.erase (*i); + } + cs->second.erase (c); + + } + + } + + } +} + +bool +DeviceClassMOS3Transistor::has_bulk_pin () const +{ + return false; +} + // ------------------------------------------------------------------------------------ // DeviceClassMOS4Transistor implementation @@ -625,6 +855,12 @@ DeviceClassMOS4Transistor::DeviceClassMOS4Transistor () add_terminal_definition (db::DeviceTerminalDefinition ("B", "Bulk")); } +bool +DeviceClassMOS4Transistor::has_bulk_pin () const +{ + return true; +} + // ------------------------------------------------------------------------------------ // DeviceClassBJT3Transistor implementation diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index 5775b343b..6ee616738 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -191,8 +191,26 @@ public: return new DeviceClassMOS3Transistor (*this); } + /** + * @brief Implements the "split_gates" feature + * This feature will join internal source/drain nodes to form fingered multi-gate + * transistors. + */ + void join_split_gates (db::Circuit *circuit) const; + + /** + * @brief Returns true if device lengths are compatible + */ + static bool lengths_are_identical (const db::Device *a, const db::Device *b); + protected: void combine_parameters (Device *a, Device *b) const; + virtual bool has_bulk_pin () const; + +private: + bool is_source_terminal (size_t tid) const; + bool is_drain_terminal (size_t tid) const; + bool net_is_source_drain_connection (const db::Net *net) const; }; /** @@ -212,6 +230,9 @@ public: { return new DeviceClassMOS4Transistor (*this); } + +protected: + virtual bool has_bulk_pin () const; }; /** diff --git a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc index 758c0dc37..b8036782a 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceClasses.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceClasses.cc @@ -240,6 +240,10 @@ Class decl_dbDeviceClassMOS3Transistor (decl_dbDe ) + gsi::constant ("PARAM_PD", db::DeviceClassMOS3Transistor::param_id_PD, "@brief A constant giving the parameter ID for parameter PD" + ) + + gsi::method ("join_split_gates", &db::DeviceClassMOS3Transistor::join_split_gates, gsi::arg ("circuit"), + "@brief Joins source/drain nets from 'split gate' transistor strings on the given circuit\n" + "This method has been introduced in version 0.27.9\n" ), "@brief A device class for a 3-terminal MOS transistor.\n" "This class describes a MOS transistor without a bulk terminal. " diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index f0da4c173..1ee83316f 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -2202,3 +2202,590 @@ TEST(40_ParallelBJT4Transistors) ); } +TEST(50_SplitGatesSimple) +{ + db::DeviceClassMOS3Transistor *mos = new db::DeviceClassMOS3Transistor (); + + db::Netlist nl; + nl.add_device_class (mos); + + db::Device *m11 = new db::Device (mos, "m11"); + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + db::Device *m21 = new db::Device (mos, "m21"); + m21->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m21->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + db::Device *m12 = new db::Device (mos, "m12"); + m12->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m12->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + db::Device *m22 = new db::Device (mos, "m22"); + m22->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m22->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_g1 = circuit->add_pin ("G1"); + db::Pin pin_g2 = circuit->add_pin ("G2"); + + circuit->add_device (m11); + circuit->add_device (m21); + circuit->add_device (m12); + circuit->add_device (m22); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *sd1 = new db::Net ("sd1"); + circuit->add_net (sd1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd1); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd1); + + db::Net *sd2 = new db::Net ("sd2"); + circuit->add_net (sd2); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd2); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *g1 = new db::Net ("g1"); + circuit->add_net (g1); + circuit->connect_pin (pin_g1.id (), g1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + + db::Net *g2 = new db::Net ("g2"); + circuit->add_net (g2); + circuit->connect_pin (pin_g2.id (), g2); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g2); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + std::unique_ptr nl2; + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + // sd2 -> sd1 + " device '' m12 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + // can switch S/D for non-strict devices ... + + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd1); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=sd1,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + // sd1 -> sd2 + " device '' m11 (S=sd2,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + // different lengths disable split_gate ... + + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 7); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=sd1,G=g1,D=n1) (L=7,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=sd1,G=g1,D=n1) (L=7,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6); + + // split_gates is not bothered by parallel chain + + db::Device *mp1 = new db::Device (mos, "mp1"); + circuit->add_device (mp1); + mp1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + mp1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + + db::Device *mp2 = new db::Device (mos, "mp2"); + circuit->add_device (mp2); + mp2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + mp2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + + mp1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + mp1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + mp2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + mp2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); // NOTE: not g2! + + db::Net *sd3 = new db::Net ("sd3"); + circuit->add_net (sd3); + mp1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd3); + mp2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd3); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=sd1,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + // sd1 -> sd2 + " device '' m11 (S=sd2,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + // different device class can't be split-gated + + db::DeviceClassMOS3Transistor *mos2 = new db::DeviceClassMOS3Transistor (); + mos2->set_name ("X"); + nl.add_device_class (mos2); + m11->set_device_class (mos2); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device X m11 (S=sd1,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device X m11 (S=sd1,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); +} + +TEST(51_SplitGatesStrict) +{ + db::DeviceClassMOS3Transistor *mos = new db::DeviceClassMOS3Transistor (); + mos->set_strict (true); + + db::Netlist nl; + nl.add_device_class (mos); + + db::Device *m11 = new db::Device (mos, "m11"); + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + db::Device *m21 = new db::Device (mos, "m21"); + m21->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m21->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + db::Device *m12 = new db::Device (mos, "m12"); + m12->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m12->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + db::Device *m22 = new db::Device (mos, "m22"); + m22->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m22->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_g1 = circuit->add_pin ("G1"); + db::Pin pin_g2 = circuit->add_pin ("G2"); + + circuit->add_device (m11); + circuit->add_device (m21); + circuit->add_device (m12); + circuit->add_device (m22); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *sd1 = new db::Net ("sd1"); + circuit->add_net (sd1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd1); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd1); + + db::Net *sd2 = new db::Net ("sd2"); + circuit->add_net (sd2); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd2); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *g1 = new db::Net ("g1"); + circuit->add_net (g1); + circuit->connect_pin (pin_g1.id (), g1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + + db::Net *g2 = new db::Net ("g2"); + circuit->add_net (g2); + circuit->connect_pin (pin_g2.id (), g2); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g2); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + std::unique_ptr nl2; + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + // sd2 -> sd1 + " device '' m12 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + // cannot switch S/D for non-strict devices ... + + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd1); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=sd1,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + // no change! + " device '' m11 (S=sd1,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd1); + + // different lengths disable split_gate ... + + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 7); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=n1,G=g1,D=sd1) (L=7,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=n1,G=g1,D=sd1) (L=7,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6); + + // split_gates is not bothered by parallel chain + + db::Device *mp1 = new db::Device (mos, "mp1"); + circuit->add_device (mp1); + mp1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + mp1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + + db::Device *mp2 = new db::Device (mos, "mp2"); + circuit->add_device (mp2); + mp2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + mp2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + + mp1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + mp1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + mp2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + mp2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); // NOTE: not g2! + + db::Net *sd3 = new db::Net ("sd3"); + circuit->add_net (sd3); + mp1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd3); + mp2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd3); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + " device '' m11 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" + // sd1 -> sd2 + " device '' m11 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); +} + +TEST(52_SplitGatesMOS4) +{ + db::DeviceClassMOS4Transistor *mos = new db::DeviceClassMOS4Transistor (); + + db::Netlist nl; + nl.add_device_class (mos); + + db::Device *m11 = new db::Device (mos, "m11"); + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m11->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + db::Device *m21 = new db::Device (mos, "m21"); + m21->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m21->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + db::Device *m12 = new db::Device (mos, "m12"); + m12->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m12->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 6.0); + db::Device *m22 = new db::Device (mos, "m22"); + m22->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 1.0); + m22->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 10.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin ("A"); + db::Pin pin_b = circuit->add_pin ("B"); + db::Pin pin_g1 = circuit->add_pin ("G1"); + db::Pin pin_g2 = circuit->add_pin ("G2"); + db::Pin pin_vss = circuit->add_pin ("VSS"); + + circuit->add_device (m11); + circuit->add_device (m21); + circuit->add_device (m12); + circuit->add_device (m22); + + db::Net *vss = new db::Net ("vss"); + circuit->add_net (vss); + circuit->connect_pin (pin_vss.id (), vss); + m11->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, vss); + m12->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, vss); + m21->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, vss); + m22->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, vss); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, n1); + + db::Net *sd1 = new db::Net ("sd1"); + circuit->add_net (sd1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd1); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd1); + + db::Net *sd2 = new db::Net ("sd2"); + circuit->add_net (sd2); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, sd2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd2); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n2); + + db::Net *g1 = new db::Net ("g1"); + circuit->add_net (g1); + circuit->connect_pin (pin_g1.id (), g1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + m12->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g1); + + db::Net *g2 = new db::Net ("g2"); + circuit->add_net (g2); + circuit->connect_pin (pin_g2.id (), g2); + m21->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g2); + m22->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, g2); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" + " device '' m11 (S=n1,G=g1,D=sd1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + std::unique_ptr nl2; + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" + " device '' m11 (S=n1,G=g1,D=sd1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + // sd2 -> sd1 + " device '' m12 (S=n1,G=g1,D=sd1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + // can switch S/D for non-strict devices ... + + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_D, n1); + m11->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_S, sd1); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" + " device '' m11 (S=sd1,G=g1,D=n1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" + // sd1 -> sd2 + " device '' m11 (S=sd2,G=g1,D=n1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + // different bulk pins disable split_gates ... + + m11->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n1); + + EXPECT_EQ (nl.to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" + " device '' m11 (S=sd1,G=g1,D=n1,B=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.reset (new db::Netlist (nl)); + + (dynamic_cast (nl2->begin_device_classes ().operator-> ()))->join_split_gates (nl2->begin_top_down ().operator-> ()); + + EXPECT_EQ (nl2->to_string (), + "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" + " device '' m11 (S=sd1,G=g1,D=n1,B=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D=sd2,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + +} + diff --git a/src/lay/lay/doc/about/lvs_ref_global.xml b/src/lay/lay/doc/about/lvs_ref_global.xml index 5097c76b0..d685e1a70 100644 --- a/src/lay/lay/doc/about/lvs_ref_global.xml +++ b/src/lay/lay/doc/about/lvs_ref_global.xml @@ -226,6 +226,16 @@ See Netter#same_nets! for a de

See Netter#schematic for a description of that function.

+

"split_gates" - Implements the "split gates" feature for the given device and circuits

+ +

Usage:

+
    +
  • split_gates(device_name)
  • +
  • split_gates(device_name, circuit_filter)
  • +
+

+See Netter#split_gates for a description of that function. +

"tolerance" - Specifies compare tolerances for certain device parameters

Usage:

diff --git a/src/lay/lay/doc/about/lvs_ref_netter.xml b/src/lay/lay/doc/about/lvs_ref_netter.xml index 1d8b68c17..f1164360b 100644 --- a/src/lay/lay/doc/about/lvs_ref_netter.xml +++ b/src/lay/lay/doc/about/lvs_ref_netter.xml @@ -427,6 +427,28 @@ If no reader is provided, Spice format will be assumed. The reader object is a Alternatively, a Netlist object can be given which is obtained from any other source.

+

"split_gates" - Implements the "split gates" feature

+ +

Usage:

+
    +
  • split_gates(device_name)
  • +
  • split_gates(device_name, circuit_filter)
  • +
+

+Multi-fingered, multi-gate MOS transistors can be built without connecting +the source/drain internal nets between the fingers. This will prevent +"combine_devices" from combining the single gate transistors of the +different fingers into single ones. +

+"split_gates" now marks the devices of the given class so that they will +receive a special treatment which joins the internl source/drain nodes. +

+By default, this method is applied to all circuits. You can specify +a circuit pattern to apply it to certain circuits only. +

+"device_name" must be a valid device name and denote a MOS3, MOS4, DMOS3 +or DMOS4 device. +

"tolerance" - Specifies compare tolerances for certain device parameters

Usage:

diff --git a/src/lay/lay/doc/manual/lvs_tweaks.xml b/src/lay/lay/doc/manual/lvs_tweaks.xml index 6a8d505f9..af5550532 100644 --- a/src/lay/lay/doc/manual/lvs_tweaks.xml +++ b/src/lay/lay/doc/manual/lvs_tweaks.xml @@ -26,6 +26,7 @@ a model subcircuit called "NMOS", use this :

+
schematic.flatten_circuit("NMOS")

Top level pin generation

@@ -45,6 +46,7 @@ circuit a pin will be created ():

+
netlist.make_top_level_pins

Device combination

@@ -81,6 +83,7 @@ capacitors). To run device combination, use :

+
netlist.combine_devices

@@ -138,6 +141,7 @@ layout cell:

+
align

Black boxing (circuit abstraction)

@@ -164,6 +168,7 @@ To wipe out the innards of a circuit, use the method:

+
netlist.blank_circuit("CIRCUIT_NAME")
 schematic.blank_circuit("CIRCUIT_NAME")
@@ -195,6 +200,23 @@ schematic.blank_circuit("CIRCUIT_NAME") be omitted.

+

+ This feature can be used to solve the "split_gates" problem (see "split_gates" + below). The internal source/drain nodes are symmetric in the configuration + shown there, so "join_symmetric_nets" can be used to solve make the + required connections, e.g.: +

+ + +
join_symmetric_nets("NAND2")
+ +

+ However, there is a more specific feature available ("split_gates") which covers more cases, + but is specialized on MOS devices. +

+ +

Split gates

+

The following picture describes such a situation known as "split gate configuration". In this case, the N1 and N2 are identical: swapping them will not change the circuit's @@ -208,28 +230,34 @@ schematic.blank_circuit("CIRCUIT_NAME")

- KLayout provides a feature (join_symmetric_nets) + KLayout provides a feature (split_gates) which will add such connections after extraction of the netlist:

-
join_symmetric_nets("NAND2")
+ +
split_gates("NMOS")

- This function will analyze the circuit "NAND2" in the extracted netlist and connect all symmetric - nodes within it. If this function is called before "combine_devices" (e.g. through + This function will analyze all circuits in the extracted netlist with respect to "NMOS" devices and connect all + split gates relevant source/drain nodes inside. If this function is called before "combine_devices" (e.g. through "netlist.simplify"), this connection is already present then and parallel devices will be recognized and combined.

- The argument to "join_symmetric_nets" is a glob-style pattern. "*" will analyze and - modify all circuits, but at the price of potentially introducing unwanted connections. - Hence the recommendation is to use this feature on circuits which are known to - need it. + The device name must denote a MOS3, MOS4, DMOS3 or DMOS4 device. + The gate lengths of all involved devices must be identical. + For MOS4 and DMOS4, all devices on one gate net must share the + same bulk net. +

+ +

+ In addition to the device name, a glob-style circuit pattern can be supplied. In this case, the analysis is restricted + to the circuits matching this pattern.

- "join_symmetric_nets" can be used anywhere in the LVS script. + "split_gates" can be used anywhere in the LVS script.

Purging (elimination of redundancy)

@@ -249,6 +277,7 @@ schematic.blank_circuit("CIRCUIT_NAME") Floating nets are nets which don't connect to any device or subcircuit.

+
netlist.purge
 netlist.purge_nets
@@ -259,6 +288,7 @@ netlist.purge_nets "purge", "combine_devices" and "purge_nets" in this recommended order:

+
netlist.simplify

diff --git a/src/lvs/lvs/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb index 1854b5fcc..64f577886 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_engine.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_engine.rb @@ -100,6 +100,13 @@ module LVS # @synopsis join_symmetric_nets(circuit_filter) # See \Netter#join_symmetric_nets for a description of that function. + # %LVS% + # @name split_gates + # @brief Implements the "split gates" feature for the given device and circuits + # @synopsis split_gates(device_name) + # @synopsis split_gates(device_name, circuit_filter) + # See \Netter#split_gates for a description of that function. + # %LVS% # @name blank_circuit # @brief Removes the content from the given circuits (blackboxing) @@ -207,7 +214,7 @@ module LVS # @synopsis lvs_data # See \Netter#lvs_data for a description of that function. - %w(schematic compare join_symmetric_nets tolerance ignore_parameter enable_parameter disable_parameter + %w(schematic compare split_gates join_symmetric_nets tolerance ignore_parameter enable_parameter disable_parameter blank_circuit align same_nets same_nets! same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity consider_net_names lvs_data).each do |f| eval <<"CODE" diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index c5fc9b3f3..8cc6ece00 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -385,6 +385,55 @@ CODE end + # %LVS% + # @name split_gates + # @brief Implements the "split gates" feature + # @synopsis split_gates(device_name) + # @synopsis split_gates(device_name, circuit_filter) + # Multi-fingered, multi-gate MOS transistors can be built without connecting + # the source/drain internal nets between the fingers. This will prevent + # "combine_devices" from combining the single gate transistors of the + # different fingers into single ones. + # + # "split_gates" now marks the devices of the given class so that they will + # receive a special treatment which joins the internl source/drain nodes. + # + # By default, this method is applied to all circuits. You can specify + # a circuit pattern to apply it to certain circuits only. + # + # "device_name" must be a valid device name and denote a MOS3, MOS4, DMOS3 + # or DMOS4 device. + + def split_gates(device_name, circuit_pattern = "*") + + device_name.is_a?(String) || raise("Device name argument of 'split_gates' must be a string") + circuit_pattern.is_a?(String) || raise("Circuit pattern argument of 'split_gates' must be a string") + + if self._l2n_data + # already extracted + self._split_gates(self._l2n_data, device_name, circuit_pattern) + else + @post_extract_config << lambda { |l2n| self._split_gates(l2n, device_name, circuit_pattern) } + end + + end + + def _split_gates(l2n, device_name, circuit_pattern) + + dc = l2n.netlist.device_class_by_name(device_name) + if ! dc + raise("'#{device_name}' is not a valid device name") + end + if ! dc.respond_to?(:join_split_gates) + raise("Device '#{device_name}' is not a kind supporting 'split_gates'") + end + + l2n.netlist.circuits_by_name(circuit_pattern).each do |c| + dc.join_split_gates(c) + end + + end + # %LVS% # @name blank_circuit # @brief Removes the content from the given circuits (blackboxing) diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index f2fe8e43b..aecfa796d 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -173,3 +173,7 @@ TEST(20_private) run_test (_this, "test_20.lylvs", "test_20.cir.gz", "test_20.gds.gz", true, "test_20b.lvsdb"); } +TEST(21_private) +{ + run_test (_this, "test_21.lylvs", "test_21.cir.gz", "test_21.gds.gz", true, "test_21.lvsdb"); +}