Refining device extraction for smart device propagation mode

Problem in this mode is that partial devices may be
extracted. This means for example, "half" a FET many be
seen with a gate having only one source or drain shape.

In this case, we want to suppress warnings, yet ignore this
device. The upper-level extraction will take care that
nothing is lost.

In addition, the half gate should not be considered when
distributing the source/drain areas of the devices. Or it could,
but these parameters are nonsense anyway in smart device
propagation mode. We should rather skip then in that mode.
This commit is contained in:
Matthias Koefferlein 2024-07-16 21:39:45 +02:00
parent 23ec24fe87
commit 06a2f5d4e2
4 changed files with 235 additions and 124 deletions

View File

@ -127,88 +127,122 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
const db::Region &rdiff = layer_geometry [diff_geometry_index];
const db::Region &rgates = layer_geometry [gate_geometry_index];
// pair<gate shape, pair<diffusion shapes, width>>
std::list<std::pair<db::Polygon, std::pair<std::set<db::Polygon>, double> > > cores;
// counts, how many times a diffusion polygon is used
std::map<db::Polygon, int> diffcount;
// collect valid gates (cores) in the first step -> gives gate polygons, diffusion shapes attached and widths
for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) {
db::Region rgate (*p);
rgate.set_merged_semantics (false);
rgate.set_base_verbosity (rgates.base_verbosity ());
db::Region rdiff2gate = rdiff.selected_interacting (rgate);
rdiff2gate.set_base_verbosity (rdiff.base_verbosity ());
if (rdiff2gate.empty ()) {
warn (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
} else {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
}
continue;
}
if (rdiff2gate.count () != 2) {
if (rdiff2gate.count () != 2) {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting with one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.count ())), *p);
continue;
}
continue;
}
// normalize the diffusion polygons so that the S/D assignment is more predictable
std::vector<db::Polygon> diffpoly;
diffpoly.reserve (2);
for (db::Region::const_iterator d2g = rdiff2gate.begin (); ! d2g.at_end (); ++d2g) {
diffpoly.push_back (*d2g);
}
std::sort (diffpoly.begin (), diffpoly.end ());
double w = 0;
int nw = 0;
for (db::Region::const_iterator d2g = rdiff2gate.begin (); ! d2g.at_end (); ++d2g) {
std::vector<db::Edges::length_type> widths;
for (std::vector<db::Polygon>::const_iterator d2g = diffpoly.begin (); d2g != diffpoly.end (); ++d2g) {
db::Edges edges (rgate.edges () & db::Edges (*d2g));
db::Edges::length_type l = edges.length ();
if (l == 0) {
db::Edges edges (rgate.edges () & db::Edges (*d2g));
db::Edges::length_type l = edges.length ();
if (l == 0) {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Vanishing edges for interaction gate/diff (corner interaction) - gate shape ignored")));
} else {
widths.push_back (l);
}
} else {
w += l;
nw += 1;
}
if (widths.size () != 2) {
continue;
}
// Computation of the gate length and width - this scheme is compatible with
// non-rectangular gates and circular gates. The computation is based on the
// relationship: A(gate) = L(gate) * W(gate). W(gate) is determined from the
// accumulated edge lengths (average of left and right length).
double param_w = sdbu () * (widths[0] + widths[1]) * 0.5;
double param_l = sdbu () * sdbu () * double (rgate.area ()) / param_w;
db::Device *device = create_device ();
device->set_trans (db::DCplxTrans ((p->box ().center () - db::Point ()) * dbu ()));
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, param_w);
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, param_l);
int diff_index = 0;
for (std::vector<db::Polygon>::const_iterator d2g = diffpoly.begin (); d2g != diffpoly.end () && diff_index < 2; ++d2g, ++diff_index) {
// count the number of gate shapes attached to this shape and distribute the area of the
// diffusion region to the number of gates
size_t n = rgates.selected_interacting (db::Region (*d2g)).count ();
tl_assert (n > 0);
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, sdbu () * sdbu () * d2g->area () / double (n));
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, sdbu () * d2g->perimeter () / double (n));
unsigned int sd_index = diff_index == 0 ? source_terminal_geometry_index : drain_terminal_geometry_index;
define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, sd_index, *d2g);
}
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_terminal_geometry_index, *p);
// allow derived classes to modify the device
modify_device (*p, layer_geometry, device);
// output the device for debugging
device_out (device, rdiff2gate, rgate);
}
if (nw != 2) {
continue;
}
w /= nw;
// normalize the diffusion polygons so that the S/D assignment is more predictable
std::set<db::Polygon> diffpoly;
for (db::Region::const_iterator d2g = rdiff2gate.begin (); ! d2g.at_end (); ++d2g) {
diffpoly.insert (*d2g);
diffcount [*d2g] += 1;
}
cores.push_back (std::make_pair (*p, std::make_pair (diffpoly, w)));
}
// generate the devices
for (auto c = cores.begin (); c != cores.end (); ++c) {
const db::Polygon &gate = c->first;
const std::set<db::Polygon> &diff = c->second.first;
double w = c->second.second;
// Computation of the gate length and width - this scheme is compatible with
// non-rectangular gates and circular gates. The computation is based on the
// relationship: A(gate) = L(gate) * W(gate). W(gate) is determined from the
// accumulated edge lengths (average of left and right length).
double param_w = sdbu () * w;
double param_l = sdbu () * double (gate.area ()) / w;
db::Device *device = create_device ();
device->set_trans (db::DCplxTrans ((gate.box ().center () - db::Point ()) * dbu ()));
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, param_w);
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, param_l);
int diff_index = 0;
for (auto d2g = diff.begin (); d2g != diff.end () && diff_index < 2; ++d2g, ++diff_index) {
// count the number of gate shapes attached to this shape and distribute the area of the
// diffusion region to the number of gates
size_t n = diffcount [*d2g];
tl_assert (n > 0);
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, sdbu () * sdbu () * d2g->area () / double (n));
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, sdbu () * d2g->perimeter () / double (n));
unsigned int sd_index = diff_index == 0 ? source_terminal_geometry_index : drain_terminal_geometry_index;
define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, sd_index, *d2g);
}
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_terminal_geometry_index, gate);
// allow derived classes to modify the device
modify_device (gate, layer_geometry, device);
// output the device for debugging
auto dp = diff.begin ();
auto sp = dp++;
device_out (device, *sp, *dp, gate);
}
} else {
@ -225,9 +259,18 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
const db::Region &ddiff = layer_geometry [drain_geometry_index];
const db::Region &rgates = layer_geometry [gate_geometry_index];
// pair<gate shape, pair<diffusion shapes, width>>
std::list<std::pair<db::Polygon, std::pair<std::pair<db::Polygon, db::Polygon>, double> > > cores;
// counts, how many times a diffusion polygon is used
std::map<db::Polygon, int> diffcount;
// collect valid gates (cores) in the first step -> gives gate polygons, diffusion shapes attached and widths
for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) {
db::Region rgate (*p);
rgate.set_merged_semantics (false);
rgate.set_base_verbosity (rgates.base_verbosity ());
db::Region sdiff2gate = sdiff.selected_interacting (rgate);
@ -237,83 +280,119 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
ddiff2gate.set_base_verbosity (ddiff.base_verbosity ());
if (sdiff2gate.empty () && ddiff2gate.empty ()) {
warn (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
} else if (sdiff2gate.empty () || ddiff2gate.empty ()) {
warn (tl::to_string (tr ("Gate shape touches a single diffusion only - ignored")), *p);
} else {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
}
continue;
}
if (sdiff2gate.count () != 1) {
if (sdiff2gate.empty () || ddiff2gate.empty ()) {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Gate shape touches a single diffusion only - ignored")), *p);
}
continue;
}
if (sdiff2gate.count () != 1) {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::sprintf (tl::to_string (tr ("Expected one polygons on source diff interacting with one gate shape (found %d) - gate shape ignored")), int (sdiff2gate.count ())), *p);
continue;
}
continue;
}
if (ddiff2gate.count () != 1) {
if (ddiff2gate.count () != 1) {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::sprintf (tl::to_string (tr ("Expected one polygons on drain diff interacting with one gate shape (found %d) - gate shape ignored")), int (ddiff2gate.count ())), *p);
}
continue;
}
db::Edges::length_type sdwidth = 0, ddwidth = 0;
{
db::Edges edges (rgate.edges () & sdiff2gate.edges ());
sdwidth = edges.length ();
if (sdwidth == 0) {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Vanishing edges for interaction gate/source diff (corner interaction) - gate shape ignored")));
}
continue;
}
}
db::Edges::length_type sdwidth = 0, ddwidth = 0;
{
db::Edges edges (rgate.edges () & sdiff2gate.edges ());
sdwidth = edges.length ();
if (sdwidth == 0) {
warn (tl::to_string (tr ("Vanishing edges for interaction gate/source diff (corner interaction) - gate shape ignored")));
continue;
}
}
{
db::Edges edges (rgate.edges () & ddiff2gate.edges ());
ddwidth = edges.length ();
if (ddwidth == 0) {
{
db::Edges edges (rgate.edges () & ddiff2gate.edges ());
ddwidth = edges.length ();
if (ddwidth == 0) {
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Vanishing edges for interaction gate/drain diff (corner interaction) - gate shape ignored")));
continue;
}
continue;
}
}
// Computation of the gate length and width - this scheme is compatible with
// non-rectangular gates and circular gates. The computation is based on the
// relationship: A(gate) = L(gate) * W(gate). W(gate) is determined from the
// accumulated edge lengths (average of left and right length).
double param_w = sdbu () * (sdwidth + ddwidth) * 0.5;
double param_l = sdbu () * sdbu () * double (rgate.area ()) / param_w;
diffcount [*ddiff2gate.begin ()] += 1;
diffcount [*sdiff2gate.begin ()] += 1;
db::Device *device = create_device ();
double w = (sdwidth + ddwidth) * 0.5;
device->set_trans (db::DCplxTrans ((p->box ().center () - db::Point ()) * dbu ()));
cores.push_back (std::make_pair (*p, std::make_pair (std::make_pair (*sdiff2gate.begin (), *ddiff2gate.begin ()), w)));
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, param_w);
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, param_l);
}
for (int diff_index = 0; diff_index < 2; ++diff_index) {
// generate the devices
const db::Region *diff = diff_index == 0 ? &sdiff2gate : &ddiff2gate;
for (auto c = cores.begin (); c != cores.end (); ++c) {
// count the number of gate shapes attached to this shape and distribute the area of the
// diffusion region to the number of gates
size_t n = rgates.selected_interacting (*diff).count ();
tl_assert (n > 0);
const db::Polygon &gate = c->first;
const db::Polygon &sdiff = c->second.first.first;
const db::Polygon &ddiff = c->second.first.second;
double w = c->second.second;
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, sdbu () * sdbu () * diff->area () / double (n));
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, sdbu () * diff->perimeter () / double (n));
// Computation of the gate length and width - this scheme is compatible with
// non-rectangular gates and circular gates. The computation is based on the
// relationship: A(gate) = L(gate) * W(gate). W(gate) is determined from the
// accumulated edge lengths (average of left and right length).
double param_w = sdbu () * w;
double param_l = sdbu () * double (gate.area ()) / w;
unsigned int sd_index = diff_index == 0 ? source_terminal_geometry_index : drain_terminal_geometry_index;
define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, sd_index, *diff);
db::Device *device = create_device ();
}
device->set_trans (db::DCplxTrans ((gate.box ().center () - db::Point ()) * dbu ()));
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_terminal_geometry_index, *p);
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, param_w);
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, param_l);
// allow derived classes to modify the device
modify_device (*p, layer_geometry, device);
for (int diff_index = 0; diff_index < 2; ++diff_index) {
// output the device for debugging
db::Region diff2gate = sdiff2gate + ddiff2gate;
device_out (device, diff2gate, rgate);
const db::Polygon *diff = diff_index == 0 ? &sdiff : &ddiff;
// count the number of gate shapes attached to this shape and distribute the area of the
// diffusion region to the number of gates
size_t n = diffcount [*diff];
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, sdbu () * sdbu () * diff->area () / double (n));
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, sdbu () * diff->perimeter () / double (n));
unsigned int sd_index = diff_index == 0 ? source_terminal_geometry_index : drain_terminal_geometry_index;
define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, sd_index, *diff);
}
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_terminal_geometry_index, gate);
// allow derived classes to modify the device
modify_device (gate, layer_geometry, device);
// output the device for debugging
device_out (device, sdiff, ddiff, gate);
}
}
@ -438,7 +517,10 @@ void NetlistDeviceExtractorResistor::extract_devices (const std::vector<db::Regi
db::Region contacts_per_res = contact_wo_res.selected_interacting (rres);
if (contacts_per_res.count () != 2) {
warn (tl::sprintf (tl::to_string (tr ("Expected two polygons on contacts interacting with one resistor shape (found %d) - resistor shape ignored")), int (contacts_per_res.count ())), *p);
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::sprintf (tl::to_string (tr ("Expected two polygons on contacts interacting with one resistor shape (found %d) - resistor shape ignored")), int (contacts_per_res.count ())), *p);
}
continue;
}
@ -458,7 +540,10 @@ void NetlistDeviceExtractorResistor::extract_devices (const std::vector<db::Regi
db::Coord width2 = eperp.length ();
if (width2 < 1) {
warn (tl::to_string (tr ("Invalid contact geometry - resistor shape ignored")), *p);
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Invalid contact geometry - resistor shape ignored")), *p);
}
continue;
}
@ -681,7 +766,10 @@ void NetlistDeviceExtractorBJT3Transistor::extract_devices (const std::vector<db
db::Region remitter2base = rbase & remitters;
if (remitter2base.empty ()) {
warn (tl::to_string (tr ("Base shape without emitters - ignored")), *p);
if (! smart_device_propagation ()) {
// NOTE: in smart device propagation mode we may encounter partial devices on local cell level - ingore them, but do not warn
warn (tl::to_string (tr ("Base shape without emitters - ignored")), *p);
}
} else {
// collectors inside base

View File

@ -118,7 +118,7 @@ protected:
* @brief A callback when the device is produced
* This callback is provided as a debugging port
*/
virtual void device_out (const db::Device * /*device*/, const db::Region & /*diff*/, const db::Region & /*gate*/)
virtual void device_out (const db::Device * /*device*/, const db::Polygon & /*sdiff*/, const db::Polygon & /*ddiff*/, const db::Polygon & /*gate*/)
{
// .. no specific implementation ..
}

23
testdata/lvs/invchain_nocheat.cir vendored Normal file
View File

@ -0,0 +1,23 @@
* Extracted by KLayout
.SUBCKT INVCHAIN IN OUT VSS VDD
X$1 IN \$2 \$3 \$2 \$3 \$4 VDD VSS INV3
X$2 \$5 \$6 \$4 \$5 VDD VSS INV2
X$3 VSS VDD \$6 OUT INV
.ENDS INVCHAIN
.SUBCKT INV2 \$I8 \$I7 \$I6 \$I5 \$I4 \$I2
X$1 \$I2 \$I4 \$I6 \$I8 INV
X$2 \$I2 \$I4 \$I5 \$I7 INV
.ENDS INV2
.SUBCKT INV3 3 5 7 4 6 8 \$I4 \$I2
X$1 \$I2 \$I4 3 4 INV
X$2 \$I2 \$I4 5 6 INV
X$3 \$I2 \$I4 7 8 INV
.ENDS INV3
.SUBCKT INV \$1 \$2 \$3 \$4
M$1 \$2 \$3 \$4 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.5225P PS=3.45U PD=3U
M$2 \$1 \$3 \$4 \$4 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.5225P PS=3.45U PD=3U
.ENDS INV

View File

@ -37,7 +37,7 @@ J(
R(l3 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
R(l2 (125 -475) (550 950))
)
)
D(D$NMOS NMOS
@ -48,7 +48,7 @@ J(
R(l3 (-125 -475) (250 950))
)
T(D
R(l5 (125 -475) (775 950))
R(l5 (125 -475) (550 950))
)
)
X(INV
@ -60,7 +60,7 @@ J(
R(l9 (-305 -705) (250 250))
R(l9 (-250 150) (250 250))
R(l10 (-2025 -775) (3000 900))
R(l5 (-1375 -925) (775 950))
R(l5 (-1375 -925) (550 950))
)
N(2
R(l6 (290 2490) (220 220))
@ -69,7 +69,7 @@ J(
R(l9 (-305 -705) (250 250))
R(l9 (-250 150) (250 250))
R(l10 (-2025 -775) (3000 900))
R(l2 (-1375 -925) (775 950))
R(l2 (-1375 -925) (550 950))
)
N(3
R(l3 (-125 -250) (250 2500))
@ -96,9 +96,9 @@ J(
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(AD 0.5225)
E(PS 3.45)
E(PD 3.45)
E(PD 3)
T(S 4)
T(G 3)
T(D 2)
@ -108,9 +108,9 @@ J(
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(AD 0.5225)
E(PS 3.45)
E(PD 3.45)
E(PD 3)
T(S 4)
T(G 3)
T(D 1)