Split gates (#1018)

* First implementation, first basic tests.

* WIP: more tests, bug fixes

* split_gates feature, added test case

* Documentation
This commit is contained in:
Matthias Köfferlein 2022-03-05 14:56:52 +01:00 committed by GitHub
parent da5e287d9a
commit ed7f77a86d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 982 additions and 12 deletions

View File

@ -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 db::Device *>::const_iterator device_iterator;
typedef std::vector<const db::Net *>::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<db::Net *> (*n), const_cast<db::Net *> (*nn));
}
}
private:
std::vector<const db::Device *> m_devices;
std::vector<const db::Net *> 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<const db::Net *> 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<const db::Net *, std::list<SplitGateDeviceChain> > 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<const db::Net *, std::list<SplitGateDeviceChain> >::iterator cs = chains.begin (); cs != chains.end (); ++cs) {
std::vector<std::list<SplitGateDeviceChain>::iterator> compatibles;
while (! cs->second.empty ()) {
compatibles.clear ();
std::list<SplitGateDeviceChain>::iterator c = cs->second.begin ();
std::list<SplitGateDeviceChain>::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<std::list<SplitGateDeviceChain>::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

View File

@ -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;
};
/**

View File

@ -240,6 +240,10 @@ Class<db::DeviceClassMOS3Transistor> 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. "

View File

@ -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<db::Netlist> nl2;
nl2.reset (new db::Netlist (nl));
(dynamic_cast<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::Netlist> nl2;
nl2.reset (new db::Netlist (nl));
(dynamic_cast<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::Netlist> nl2;
nl2.reset (new db::Netlist (nl));
(dynamic_cast<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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<db::DeviceClassMOS3Transistor *> (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"
);
}

View File

@ -226,6 +226,16 @@ See <a href="/about/lvs_ref_netter.xml#same_nets">Netter#same_nets</a>! for a de
<p>
See <a href="/about/lvs_ref_netter.xml#schematic">Netter#schematic</a> for a description of that function.
</p>
<a name="split_gates"/><h2>"split_gates" - Implements the "split gates" feature for the given device and circuits</h2>
<keyword name="split_gates"/>
<p>Usage:</p>
<ul>
<li><tt>split_gates(device_name)</tt></li>
<li><tt>split_gates(device_name, circuit_filter)</tt></li>
</ul>
<p>
See <a href="/about/lvs_ref_netter.xml#split_gates">Netter#split_gates</a> for a description of that function.
</p>
<a name="tolerance"/><h2>"tolerance" - Specifies compare tolerances for certain device parameters</h2>
<keyword name="tolerance"/>
<p>Usage:</p>

View File

@ -427,6 +427,28 @@ If no reader is provided, Spice format will be assumed. The reader object is a
Alternatively, a <class_doc href="Netlist">Netlist</class_doc> object can be given which is obtained from any other
source.
</p>
<a name="split_gates"/><h2>"split_gates" - Implements the "split gates" feature</h2>
<keyword name="split_gates"/>
<p>Usage:</p>
<ul>
<li><tt>split_gates(device_name)</tt></li>
<li><tt>split_gates(device_name, circuit_filter)</tt></li>
</ul>
<p>
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.
</p><p>
"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.
</p><p>
By default, this method is applied to all circuits. You can specify
a circuit pattern to apply it to certain circuits only.
</p><p>
"device_name" must be a valid device name and denote a MOS3, MOS4, DMOS3
or DMOS4 device.
</p>
<a name="tolerance"/><h2>"tolerance" - Specifies compare tolerances for certain device parameters</h2>
<keyword name="tolerance"/>
<p>Usage:</p>

View File

@ -26,6 +26,7 @@
a model subcircuit called "NMOS", use this <class_doc href="Netlist#flatten_circuit"/>:
</p>
<k name="flatten_circuit"/>
<pre>schematic.flatten_circuit("NMOS")</pre>
<h2>Top level pin generation</h2>
@ -45,6 +46,7 @@
circuit a pin will be created (<class_doc href="Netlist#make_top_level_pins"/>):
</p>
<k name="make_top_level_pins"/>
<pre>netlist.make_top_level_pins</pre>
<h2>Device combination</h2>
@ -81,6 +83,7 @@
capacitors). To run device combination, use <class_doc href="Netlist#combine_devices"/>:
</p>
<k name="combine_devices"/>
<pre>netlist.combine_devices</pre>
<p>
@ -138,6 +141,7 @@
layout cell:
</p>
<k name="align"/>
<pre>align</pre>
<h2>Black boxing (circuit abstraction)</h2>
@ -164,6 +168,7 @@
To wipe out the innards of a circuit, use the <class_doc href="Netlist#blank_circuit"/> method:
</p>
<k name="blank_circuit"/>
<pre>netlist.blank_circuit("CIRCUIT_NAME")
schematic.blank_circuit("CIRCUIT_NAME")</pre>
@ -195,6 +200,23 @@ schematic.blank_circuit("CIRCUIT_NAME")</pre>
be omitted.
</p>
<p>
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.:
</p>
<k name="join_symmetric_nodes"/>
<pre>join_symmetric_nets("NAND2")</pre>
<p>
However, there is a more specific feature available ("split_gates") which covers more cases,
but is specialized on MOS devices.
</p>
<h2>Split gates</h2>
<p>
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")</pre>
</p>
<p>
KLayout provides a feature (<a href="/about/lvs_ref_netter.xml#join_symmetric_nets">join_symmetric_nets</a>)
KLayout provides a feature (<a href="/about/lvs_ref_netter.xml#split_gates">split_gates</a>)
which will add such connections after extraction of the netlist:
</p>
<pre>join_symmetric_nets("NAND2")</pre>
<k name="split_gates"/>
<pre>split_gates("NMOS")</pre>
<p>
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.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
"join_symmetric_nets" can be used anywhere in the LVS script.
"split_gates" can be used anywhere in the LVS script.
</p>
<h2>Purging (elimination of redundancy)</h2>
@ -249,6 +277,7 @@ schematic.blank_circuit("CIRCUIT_NAME")</pre>
Floating nets are nets which don't connect to any device or subcircuit.
</p>
<k name="purge"/>
<pre>netlist.purge
netlist.purge_nets</pre>
@ -259,6 +288,7 @@ netlist.purge_nets</pre>
"purge", "combine_devices" and "purge_nets" in this recommended order:
</p>
<k name="simplify"/>
<pre>netlist.simplify</pre>
<p>

View File

@ -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"

View File

@ -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)

View File

@ -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");
}