mirror of https://github.com/KLayout/klayout.git
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:
parent
da5e287d9a
commit
ed7f77a86d
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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. "
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue