From ed7f77a86d1074f75d9cdcc6de7b4f6f7009217f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?=
Date: Sat, 5 Mar 2022 14:56:52 +0100
Subject: [PATCH] Split gates (#1018)
* First implementation, first basic tests.
* WIP: more tests, bug fixes
* split_gates feature, added test case
* Documentation
---
src/db/db/dbNetlistDeviceClasses.cc | 240 ++++++-
src/db/db/dbNetlistDeviceClasses.h | 21 +
src/db/db/gsiDeclDbNetlistDeviceClasses.cc | 4 +
.../unit_tests/dbNetlistDeviceClassesTests.cc | 587 ++++++++++++++++++
src/lay/lay/doc/about/lvs_ref_global.xml | 10 +
src/lay/lay/doc/about/lvs_ref_netter.xml | 22 +
src/lay/lay/doc/manual/lvs_tweaks.xml | 48 +-
src/lvs/lvs/built-in-macros/_lvs_engine.rb | 9 +-
src/lvs/lvs/built-in-macros/_lvs_netter.rb | 49 ++
src/lvs/unit_tests/lvsTests.cc | 4 +
10 files changed, 982 insertions(+), 12 deletions(-)
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");
+}