You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Device terminal defined with several polygons connects to only one of them #2525
When a GenericDeviceExtractor defines a terminal with several unconnected polygons, the terminal is connected to the net of only one of them. The other nets lose the terminal, without a warning. define_terminal keeps all polygons, but NetlistExtractor::make_device_abstract_connections (src/db/db/dbNetlistExtractor.cc) keeps one cluster per terminal: each set_cluster_id_for_terminal call overwrites the previous one.
importklayout.dbaskdbclassExtractor(kdb.GenericDeviceExtractor):
defsetup(self):
self.name='DEV'self.define_layer('MK', 'marker')
self.define_layer('A', 'terminal A')
device_class=kdb.DeviceClass()
device_class.add_terminal(kdb.DeviceTerminalDefinition('A'))
self.register_device_class(device_class)
defget_connectivity(self, layout, layers):
conn=kdb.Connectivity()
conn.connect(layers[0], layers[1])
returnconndefextract_devices(self, layer_geometry):
marker, a=layer_geometrydevice=self.create_device()
forpina.each():
self.define_terminal(device, self.device_class().terminal_id('A'), 1, p)
layout=kdb.Layout()
top=layout.create_cell('TOP')
mk, a, m1= (layout.layer(n, 0) fornin (1, 2, 3))
top.shapes(mk).insert(kdb.Box(0, 0, 1000, 1000))
forxin (100, 500): # two polygons of terminal A, each under a wire of its owntop.shapes(a).insert(kdb.Box(x, 100, x+100, 200))
top.shapes(m1).insert(kdb.Box(x-50, 50, x+150, 250))
l2n=kdb.LayoutToNetlist(kdb.RecursiveShapeIterator(layout, top, []))
r_mk, r_a, r_m1= (l2n.make_layer(li) forliin (mk, a, m1))
l2n.extract_devices(Extractor(), {'MK': r_mk, 'A': r_a})
l2n.connect(r_a)
l2n.connect(r_m1)
l2n.connect(r_a, r_m1)
l2n.extract_netlist()
fornetinl2n.netlist().top_circuit().each_net():
print(net.expanded_name(), [str(p.bbox()) forpinl2n.shapes_of_net(net, r_m1).each()],
[t.terminal_def().namefortinnet.each_terminal()])
Expected: both wires on the net of A, or at least a warning.
Seen with the sg13g2 LVS deck of IHP-Open-PDK, which defines the emitter E of an npn13G2 with Nx > 1 with one polygon per emitter. LVS passes there because metal joins the emitters, but parasitic extraction puts E on one emitter only (iic-jku/klayout-pex#364).
KLayout 0.30.12 (Python module), the code is the same on master
Nice testcase, thanks (or maybe thanks to Claude :) ).
A small remark first: inside "get_connectivity" you should also include self-interactions, since otherwise devices with a split marker shape are not considered as one device:
However, I'm afraid what you brought up is a complex topic.
The important piece of information here is that the feature targets bipolar devices. So let's consider "marker" to be collector, base is skipped for simplicity and I think it goes over the whole marker area, and "A" is actually the emitter. You layout is then essentially a multi-emitter device. The issue seen here comes from the fact, that your device has basically two emitter terminals while the class only has one. The problem is not about polygons not being attached, but about which terminal to attach them.
There are basically three ways to look at this situation and all require a different solution:
The terminals are alternatives: This means, you can attach to any of them and the device is somehow supposed to make a connection between them internally. From that perspective, the current behavior is a bug, since you connect to both of them. However, if I allow that, it could mask a potential wiring problem: you could make a connection through the device - i.a. running a wire to one terminal and continuing the wire from the other. Formally that would be a connection, since both wires connect to the same terminal, but physically that may be a pretty bad one, as the internal connection may go through wells or diffusion areas. We have soft connections now to detect such problems, so basically I can implement the behavior now. I'll take care of that here.
However, if your intention is to have the device always connected at both emitters, then the approach is not the right one. Instead you should treat the emitter areas as separate devices (pull the create_device call inside the loop over the polygons of a) and establish a device combination rule that connects these parallel devices. This gives you the opportunity to add up the finger parameters ("nx", "ny", whatever ...) and check that in LVS. There is a pitfall however: as you don't want to combine parallel devices from different collection regions, you have to "stamp" them: create a new parameter - i.e. "id", assign it a unique integer for all devices created inside the same loop and then combine only those devices with the same id. This case is also called a "must-connect" terminal, because the requirement in the end is to make a connection on every terminal, hence shorting them.
As a third and most complex option, you want that device to be extracted as a multi-emitter transistor with two emitter terminals. There is no out-of-the box solution right now. The device classes have a fixed number of terminals and a device extractor is associated with a single class. So no variable number of terminals. A solution is to proceed similar to case 2., but not combine the devices. Instead, replace parallel devices by multi-emitter devices with emitter-count specific classes in a post-extraction sweep of the netlist. Another option is to have multiple device classes available in one extractor. That would be an extension. If this option is required, let's discuss the implementation path separately.
Activity
klayoutmatthias commentedon Oct 29, 2026
Hi @martinjankoehler,
Nice testcase, thanks (or maybe thanks to Claude :) ).
A small remark first: inside "get_connectivity" you should also include self-interactions, since otherwise devices with a split marker shape are not considered as one device:
However, I'm afraid what you brought up is a complex topic.
The important piece of information here is that the feature targets bipolar devices. So let's consider "marker" to be collector, base is skipped for simplicity and I think it goes over the whole marker area, and "A" is actually the emitter. You layout is then essentially a multi-emitter device. The issue seen here comes from the fact, that your device has basically two emitter terminals while the class only has one. The problem is not about polygons not being attached, but about which terminal to attach them.
There are basically three ways to look at this situation and all require a different solution:
The terminals are alternatives: This means, you can attach to any of them and the device is somehow supposed to make a connection between them internally. From that perspective, the current behavior is a bug, since you connect to both of them. However, if I allow that, it could mask a potential wiring problem: you could make a connection through the device - i.a. running a wire to one terminal and continuing the wire from the other. Formally that would be a connection, since both wires connect to the same terminal, but physically that may be a pretty bad one, as the internal connection may go through wells or diffusion areas. We have soft connections now to detect such problems, so basically I can implement the behavior now. I'll take care of that here.
However, if your intention is to have the device always connected at both emitters, then the approach is not the right one. Instead you should treat the emitter areas as separate devices (pull the
create_devicecall inside the loop over the polygons ofa) and establish a device combination rule that connects these parallel devices. This gives you the opportunity to add up the finger parameters ("nx", "ny", whatever ...) and check that in LVS. There is a pitfall however: as you don't want to combine parallel devices from different collection regions, you have to "stamp" them: create a new parameter - i.e. "id", assign it a unique integer for all devices created inside the same loop and then combine only those devices with the same id. This case is also called a "must-connect" terminal, because the requirement in the end is to make a connection on every terminal, hence shorting them.As a third and most complex option, you want that device to be extracted as a multi-emitter transistor with two emitter terminals. There is no out-of-the box solution right now. The device classes have a fixed number of terminals and a device extractor is associated with a single class. So no variable number of terminals. A solution is to proceed similar to case 2., but not combine the devices. Instead, replace parallel devices by multi-emitter devices with emitter-count specific classes in a post-extraction sweep of the netlist. Another option is to have multiple device classes available in one extractor. That would be an extension. If this option is required, let's discuss the implementation path separately.
I'll see what I can do about case 1. now.
Matthias