pyedb 0.36.0__py3-none-any.whl → 0.38.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyedb might be problematic. Click here for more details.
- pyedb/__init__.py +1 -1
- pyedb/configuration/cfg_common.py +1 -1
- pyedb/configuration/cfg_components.py +228 -200
- pyedb/configuration/cfg_data.py +3 -1
- pyedb/configuration/cfg_modeler.py +6 -6
- pyedb/configuration/cfg_padstacks.py +345 -289
- pyedb/configuration/cfg_ports_sources.py +191 -42
- pyedb/configuration/configuration.py +5 -2
- pyedb/dotnet/edb.py +1 -1
- pyedb/dotnet/edb_core/cell/layout.py +24 -8
- pyedb/dotnet/edb_core/edb_data/nets_data.py +1 -1
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +62 -26
- pyedb/dotnet/edb_core/geometry/polygon_data.py +5 -0
- pyedb/dotnet/edb_core/modeler.py +1 -2
- pyedb/dotnet/edb_core/padstack.py +2 -1
- pyedb/dotnet/edb_core/sim_setup_data/data/settings.py +15 -0
- pyedb/dotnet/edb_core/sim_setup_data/data/sweep_data.py +3 -3
- pyedb/dotnet/edb_core/utilities/simulation_setup.py +1 -0
- pyedb/extensions/pre_layout_design_toolkit/via_design.py +1151 -0
- pyedb/ipc2581/ecad/cad_data/step.py +1 -1
- {pyedb-0.36.0.dist-info → pyedb-0.38.0.dist-info}/METADATA +5 -5
- {pyedb-0.36.0.dist-info → pyedb-0.38.0.dist-info}/RECORD +24 -23
- {pyedb-0.36.0.dist-info → pyedb-0.38.0.dist-info}/LICENSE +0 -0
- {pyedb-0.36.0.dist-info → pyedb-0.38.0.dist-info}/WHEEL +0 -0
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
|
+
import os
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
22
25
|
|
|
23
26
|
from pyedb.configuration.cfg_common import CfgBase
|
|
24
27
|
from pyedb.dotnet.edb_core.edb_data.ports import WavePort
|
|
@@ -33,6 +36,9 @@ class CfgTerminalInfo(CfgBase):
|
|
|
33
36
|
self._pedb = pedb
|
|
34
37
|
self.type = list(kwargs.keys())[0]
|
|
35
38
|
self.value = kwargs[self.type]
|
|
39
|
+
self.contact_type = kwargs.get("contact_type", "default") # options are full, center, quad, inline
|
|
40
|
+
self.contact_radius = self._pedb.edb_value(kwargs.get("contact_radius", "0.1mm")).ToDouble()
|
|
41
|
+
self.num_of_contact = kwargs.get("num_of_contact", 4)
|
|
36
42
|
|
|
37
43
|
def export_properties(self):
|
|
38
44
|
return {self.type: self.value}
|
|
@@ -46,7 +52,6 @@ class CfgCoordianteTerminalInfo(CfgTerminalInfo):
|
|
|
46
52
|
self.point_x = self.value["point"][0]
|
|
47
53
|
self.point_y = self.value["point"][1]
|
|
48
54
|
self.net = self.value["net"]
|
|
49
|
-
self.contact_radius = self.value.get("contact_radius", None)
|
|
50
55
|
|
|
51
56
|
def export_properties(self):
|
|
52
57
|
return {"coordinates": {"layer": self.layer, "point": [self.point_x, self.point_y], "net": self.net}}
|
|
@@ -206,6 +211,16 @@ class CfgPorts:
|
|
|
206
211
|
return ports
|
|
207
212
|
|
|
208
213
|
|
|
214
|
+
class CfgProbes:
|
|
215
|
+
def __init__(self, pedb, data):
|
|
216
|
+
self._pedb = pedb
|
|
217
|
+
self.probes = [CfgProbe(self._pedb, **probe) for probe in data]
|
|
218
|
+
|
|
219
|
+
def apply(self):
|
|
220
|
+
for probe in self.probes:
|
|
221
|
+
probe.api.set_parameters_to_edb()
|
|
222
|
+
|
|
223
|
+
|
|
209
224
|
class CfgCircuitElement(CfgBase):
|
|
210
225
|
def __init__(self, pedb, **kwargs):
|
|
211
226
|
self._pedb = pedb
|
|
@@ -213,6 +228,7 @@ class CfgCircuitElement(CfgBase):
|
|
|
213
228
|
self.type = kwargs["type"]
|
|
214
229
|
self.reference_designator = kwargs.get("reference_designator", None)
|
|
215
230
|
self.distributed = kwargs.get("distributed", False)
|
|
231
|
+
self._elem_num = 1
|
|
216
232
|
|
|
217
233
|
pos = kwargs["positive_terminal"] # {"pin" : "A1"}
|
|
218
234
|
if list(pos.keys())[0] == "coordinates":
|
|
@@ -230,9 +246,10 @@ class CfgCircuitElement(CfgBase):
|
|
|
230
246
|
else:
|
|
231
247
|
self.negative_terminal_info = CfgTerminalInfo(self._pedb, **neg)
|
|
232
248
|
|
|
233
|
-
def
|
|
249
|
+
def create_terminals(self):
|
|
234
250
|
"""Create step 1. Collect positive and negative terminals."""
|
|
235
251
|
|
|
252
|
+
# Collect all positive terminals
|
|
236
253
|
pos_type, pos_value = self.positive_terminal_info.type, self.positive_terminal_info.value
|
|
237
254
|
pos_objs = dict()
|
|
238
255
|
pos_coor_terminal = dict()
|
|
@@ -245,35 +262,58 @@ class CfgCircuitElement(CfgBase):
|
|
|
245
262
|
point = [self.positive_terminal_info.point_x, self.positive_terminal_info.point_y]
|
|
246
263
|
net_name = self.positive_terminal_info.net
|
|
247
264
|
pos_coor_terminal[self.name] = self._pedb.get_point_terminal(self.name, net_name, point, layer)
|
|
248
|
-
if self.positive_terminal_info.contact_radius:
|
|
249
|
-
pos_coor_terminal[self.name].contact_radius = self.positive_terminal_info.contact_radius
|
|
250
265
|
|
|
266
|
+
elif pos_type == "pin":
|
|
267
|
+
pins = {pos_value: self._pedb.components.instances[self.reference_designator].pins[pos_value]}
|
|
268
|
+
if self.positive_terminal_info.contact_type in ["quad", "inline"]:
|
|
269
|
+
for _, pin in pins.items():
|
|
270
|
+
contact_type = self.positive_terminal_info.contact_type
|
|
271
|
+
radius = self.positive_terminal_info.contact_radius
|
|
272
|
+
num_of_contact = self.positive_terminal_info.num_of_contact
|
|
273
|
+
virtual_pins = self._create_virtual_pins_on_pin(pin, contact_type, radius, num_of_contact)
|
|
274
|
+
pos_objs.update(virtual_pins)
|
|
275
|
+
self._elem_num = len(pos_objs)
|
|
276
|
+
else:
|
|
277
|
+
pos_objs.update(pins)
|
|
251
278
|
elif pos_type == "pin_group":
|
|
279
|
+
pins = self._get_pins(pos_type, pos_value)
|
|
252
280
|
if self.distributed:
|
|
253
|
-
pins = self._get_pins(pos_type, pos_value)
|
|
254
281
|
pos_objs.update(pins)
|
|
255
282
|
self._elem_num = len(pos_objs)
|
|
283
|
+
elif self.positive_terminal_info.contact_type in ["quad", "inline"]:
|
|
284
|
+
for _, pin in pins.items():
|
|
285
|
+
contact_type = self.positive_terminal_info.contact_type
|
|
286
|
+
radius = self.positive_terminal_info.contact_radius
|
|
287
|
+
num_of_contact = self.positive_terminal_info.num_of_contact
|
|
288
|
+
virtual_pins = self._create_virtual_pins_on_pin(pin, contact_type, radius, num_of_contact)
|
|
289
|
+
pos_objs.update(virtual_pins)
|
|
290
|
+
self._elem_num = len(pos_objs)
|
|
256
291
|
else:
|
|
257
292
|
pos_objs[pos_value] = self._pedb.siwave.pin_groups[pos_value]
|
|
258
293
|
elif pos_type == "net":
|
|
294
|
+
pins = self._get_pins(pos_type, pos_value)
|
|
259
295
|
if self.distributed:
|
|
260
|
-
pins = self._get_pins(pos_type, pos_value)
|
|
261
296
|
pos_objs.update(pins)
|
|
262
297
|
self._elem_num = len(pos_objs)
|
|
298
|
+
elif self.positive_terminal_info.contact_type in ["quad", "inline"]:
|
|
299
|
+
for _, pin in pins.items():
|
|
300
|
+
contact_type = self.positive_terminal_info.contact_type
|
|
301
|
+
radius = self.positive_terminal_info.contact_radius
|
|
302
|
+
num_of_contact = self.positive_terminal_info.num_of_contact
|
|
303
|
+
virtual_pins = self._create_virtual_pins_on_pin(pin, contact_type, radius, num_of_contact)
|
|
304
|
+
pos_objs.update(virtual_pins)
|
|
305
|
+
self._elem_num = len(pos_objs)
|
|
263
306
|
else:
|
|
264
|
-
pins = self._get_pins(pos_type, pos_value)
|
|
265
307
|
# create pin group
|
|
266
308
|
neg_obj = self._create_pin_group(pins)
|
|
267
309
|
pos_objs.update(neg_obj)
|
|
268
|
-
elif pos_type == "pin":
|
|
269
|
-
pins = {pos_value: self._pedb.components.instances[self.reference_designator].pins[pos_value]}
|
|
270
|
-
pos_objs.update(pins)
|
|
271
310
|
else:
|
|
272
311
|
raise Exception(f"Wrong positive terminal type {pos_type}.")
|
|
273
312
|
|
|
274
313
|
self.pos_terminals = {i: j.create_terminal(i) for i, j in pos_objs.items()}
|
|
275
314
|
self.pos_terminals.update(pos_coor_terminal)
|
|
276
315
|
|
|
316
|
+
# Collect all negative terminals
|
|
277
317
|
self.neg_terminal = None
|
|
278
318
|
if self.negative_terminal_info:
|
|
279
319
|
neg_type, neg_value = self.negative_terminal_info.type, self.negative_terminal_info.value
|
|
@@ -330,6 +370,66 @@ class CfgCircuitElement(CfgBase):
|
|
|
330
370
|
pins.update({f"{self.reference_designator}_{terminal_value[0]}_{i}": j for i, j in temp.items()})
|
|
331
371
|
return pins
|
|
332
372
|
|
|
373
|
+
def _create_virtual_pins_on_pin(self, pin, contact_type, radius, num_of_contact=4):
|
|
374
|
+
component = pin.component
|
|
375
|
+
placement_layer = component.placement_layer
|
|
376
|
+
pos_x, pos_y = pin.position
|
|
377
|
+
comp_rotation = self._pedb.edb_value(component.rotation).ToDouble() % 3.141592653589793
|
|
378
|
+
|
|
379
|
+
pad = pin.definition.pad_by_layer[placement_layer]
|
|
380
|
+
if pad.shape.lower() in ["rectangle", "oval"]:
|
|
381
|
+
width, height = pad.parameters_values[0:2]
|
|
382
|
+
elif pad.shape.lower() == "nogeometry":
|
|
383
|
+
polygon_data = pad.polygon_data
|
|
384
|
+
if polygon_data:
|
|
385
|
+
p1, p2 = polygon_data.bounding_box
|
|
386
|
+
width = p2[0] - p1[0]
|
|
387
|
+
height = p2[1] - p1[1]
|
|
388
|
+
else:
|
|
389
|
+
raise AttributeError(f"Unsupported pad shape {pad.shape.lower()}")
|
|
390
|
+
else: # pragma no cover
|
|
391
|
+
raise AttributeError(f"Unsupported pad shape {pad.shape.lower()}")
|
|
392
|
+
|
|
393
|
+
positions = []
|
|
394
|
+
if contact_type.lower() == "inline":
|
|
395
|
+
if width > height:
|
|
396
|
+
offset = (width - radius * 2) / (num_of_contact - 1)
|
|
397
|
+
else:
|
|
398
|
+
offset = (height - radius * 2) / (num_of_contact - 1)
|
|
399
|
+
|
|
400
|
+
start_pos = (num_of_contact - 1) / 2
|
|
401
|
+
offset = [offset * i for i in np.arange(start_pos * -1, start_pos + 1)]
|
|
402
|
+
|
|
403
|
+
if (width > height and comp_rotation == 0) or (width < height and comp_rotation != 0):
|
|
404
|
+
positions.extend(list(zip(offset, [0] * num_of_contact)))
|
|
405
|
+
else:
|
|
406
|
+
positions.extend(list(zip([0] * num_of_contact, offset)))
|
|
407
|
+
else: # quad
|
|
408
|
+
x_offset = width / 2 - radius if comp_rotation == 0 else height / 2 - radius
|
|
409
|
+
y_offset = height / 2 - radius if comp_rotation == 0 else width / 2 - radius
|
|
410
|
+
|
|
411
|
+
for x, y in [[1, 1], [-1, 1], [1, -1], [-1, -1]]:
|
|
412
|
+
positions.append([x_offset * x, y_offset * y])
|
|
413
|
+
|
|
414
|
+
pdef_name = f"{self.name}_{pin.pin_number}"
|
|
415
|
+
self._pedb.padstacks.create(padstackname=pdef_name, has_hole=False, paddiam=radius * 2, antipaddiam=0)
|
|
416
|
+
instances = {}
|
|
417
|
+
for idx, xy in enumerate(positions):
|
|
418
|
+
x = xy[0] + pos_x
|
|
419
|
+
y = xy[1] + pos_y
|
|
420
|
+
pin_name = f"{pdef_name}_{idx}"
|
|
421
|
+
p_inst = self._pedb.padstacks.place(
|
|
422
|
+
position=[x, y],
|
|
423
|
+
definition_name=pdef_name,
|
|
424
|
+
net_name=pin.net_name,
|
|
425
|
+
via_name=pin_name,
|
|
426
|
+
fromlayer=placement_layer,
|
|
427
|
+
tolayer=placement_layer,
|
|
428
|
+
is_pin=True,
|
|
429
|
+
)
|
|
430
|
+
instances[pin_name] = p_inst
|
|
431
|
+
return instances
|
|
432
|
+
|
|
333
433
|
def _create_pin_group(self, pins, is_ref=False):
|
|
334
434
|
if is_ref:
|
|
335
435
|
pg_name = f"pg_{self.name}_{self.reference_designator}_ref"
|
|
@@ -350,7 +450,7 @@ class CfgPort(CfgCircuitElement):
|
|
|
350
450
|
|
|
351
451
|
def set_parameters_to_edb(self):
|
|
352
452
|
"""Create port."""
|
|
353
|
-
self.
|
|
453
|
+
self.create_terminals()
|
|
354
454
|
is_circuit_port = True if self.type == "circuit" else False
|
|
355
455
|
circuit_elements = []
|
|
356
456
|
for name, j in self.pos_terminals.items():
|
|
@@ -382,52 +482,66 @@ class CfgSource(CfgCircuitElement):
|
|
|
382
482
|
super().__init__(pedb, **kwargs)
|
|
383
483
|
|
|
384
484
|
self.magnitude = kwargs.get("magnitude", 0.001)
|
|
385
|
-
self.equipotential = kwargs.get("equipotential", False)
|
|
386
485
|
|
|
387
486
|
def set_parameters_to_edb(self):
|
|
388
487
|
"""Create sources."""
|
|
389
|
-
self.
|
|
488
|
+
self.create_terminals()
|
|
390
489
|
# is_circuit_port = True if self.type == "circuit" else False
|
|
391
490
|
circuit_elements = []
|
|
392
|
-
|
|
491
|
+
create_xxx_source = (
|
|
492
|
+
self._pedb.create_current_source if self.type == "current" else self._pedb.create_voltage_source
|
|
493
|
+
)
|
|
393
494
|
for name, j in self.pos_terminals.items():
|
|
394
495
|
if isinstance(self.neg_terminal, dict):
|
|
395
|
-
elem =
|
|
496
|
+
elem = create_xxx_source(j, self.neg_terminal[name])
|
|
396
497
|
else:
|
|
397
|
-
elem =
|
|
398
|
-
if
|
|
498
|
+
elem = create_xxx_source(j, self.neg_terminal)
|
|
499
|
+
if self._elem_num == 1:
|
|
399
500
|
elem.name = self.name
|
|
400
501
|
elem.magnitude = self.magnitude
|
|
401
502
|
else:
|
|
402
|
-
elem.name =
|
|
503
|
+
elem.name = name
|
|
403
504
|
elem.magnitude = self.magnitude / self._elem_num
|
|
505
|
+
elem = self._pedb.terminals[elem.name]
|
|
404
506
|
circuit_elements.append(elem)
|
|
405
|
-
for terminal in circuit_elements:
|
|
406
|
-
if self.equipotential:
|
|
407
|
-
terms = [terminal, terminal.ref_terminal] if terminal.ref_terminal else [terminal]
|
|
408
|
-
for t in terms:
|
|
409
|
-
pads = []
|
|
410
|
-
if t.terminal_type == "PadstackInstanceTerminal":
|
|
411
|
-
pads.append(t.reference_object)
|
|
412
|
-
t._edb_object.dcir_equipotential_region = True
|
|
413
|
-
elif t.terminal_type == "PinGroupTerminal":
|
|
414
|
-
name = t._edb_object.GetPinGroup().GetName()
|
|
415
|
-
pg = self._pedb.siwave.pin_groups[name]
|
|
416
|
-
pads.extend([i for _, i in pg.pins.items()])
|
|
417
|
-
elif t.terminal_type == "PointTerminal":
|
|
418
|
-
temp = [i for i in self._pedb.layout.terminals if i.name == t.name][0]
|
|
419
|
-
if not temp.is_reference_terminal:
|
|
420
|
-
radius = self.positive_terminal_info.contact_radius
|
|
421
|
-
else:
|
|
422
|
-
radius = self.negative_terminal_info.contact_radius
|
|
423
|
-
if radius is not None:
|
|
424
|
-
prim = self._pedb.modeler.create_circle(
|
|
425
|
-
temp.layer.name, temp.location[0], temp.location[1], radius, temp.net_name
|
|
426
|
-
)
|
|
427
|
-
prim.dcir_equipotential_region = True
|
|
428
507
|
|
|
508
|
+
for terminal in circuit_elements:
|
|
509
|
+
# Get reference terminal
|
|
510
|
+
terms = [terminal, terminal.ref_terminal] if terminal.ref_terminal else [terminal]
|
|
511
|
+
for t in terms:
|
|
512
|
+
if not t.is_reference_terminal:
|
|
513
|
+
radius = self.positive_terminal_info.contact_radius
|
|
514
|
+
contact_type = self.positive_terminal_info.contact_type
|
|
515
|
+
else:
|
|
516
|
+
radius = self.negative_terminal_info.contact_radius
|
|
517
|
+
contact_type = self.negative_terminal_info.contact_type
|
|
518
|
+
if t.terminal_type == "PointTerminal":
|
|
519
|
+
temp = [i for i in self._pedb.layout.terminals if i.name == t.name][0]
|
|
520
|
+
prim = self._pedb.modeler.create_circle(
|
|
521
|
+
temp.layer.name, temp.location[0], temp.location[1], radius, temp.net_name
|
|
522
|
+
)
|
|
523
|
+
prim.dcir_equipotential_region = True
|
|
524
|
+
continue
|
|
525
|
+
elif contact_type.lower() == "default":
|
|
526
|
+
continue
|
|
527
|
+
elif t.terminal_type == "PadstackInstanceTerminal":
|
|
528
|
+
if contact_type.lower() in ["full", "quad", "inline"]:
|
|
529
|
+
t.padstack_instance._set_equipotential()
|
|
530
|
+
elif contact_type.lower() == "center":
|
|
531
|
+
t.padstack_instance._set_equipotential(contact_radius=radius)
|
|
532
|
+
elif t.terminal_type == "PinGroupTerminal":
|
|
533
|
+
name = t._edb_object.GetPinGroup().GetName()
|
|
534
|
+
pg = self._pedb.siwave.pin_groups[name]
|
|
535
|
+
pads = [i for _, i in pg.pins.items()]
|
|
429
536
|
for i in pads:
|
|
430
|
-
|
|
537
|
+
if contact_type.lower() in ["full", "quad", "inline"]:
|
|
538
|
+
i._set_equipotential()
|
|
539
|
+
elif contact_type.lower() == "center":
|
|
540
|
+
i._set_equipotential(contact_radius=radius)
|
|
541
|
+
elif t.is_reference_terminal:
|
|
542
|
+
continue
|
|
543
|
+
else:
|
|
544
|
+
raise AttributeError("Unsupported terminal type.")
|
|
431
545
|
|
|
432
546
|
return circuit_elements
|
|
433
547
|
|
|
@@ -442,6 +556,41 @@ class CfgSource(CfgCircuitElement):
|
|
|
442
556
|
}
|
|
443
557
|
|
|
444
558
|
|
|
559
|
+
class CfgProbe(CfgCircuitElement):
|
|
560
|
+
class Common:
|
|
561
|
+
def __init__(self, parent):
|
|
562
|
+
self.parent = parent
|
|
563
|
+
self.pedb = parent._pedb
|
|
564
|
+
|
|
565
|
+
def set_parameters_to_edb(self):
|
|
566
|
+
self.parent.create_terminals()
|
|
567
|
+
circuit_elements = []
|
|
568
|
+
for name, j in self.parent.pos_terminals.items():
|
|
569
|
+
if isinstance(self.parent.neg_terminal, dict):
|
|
570
|
+
elem = self.pedb.create_voltage_probe(j, self.parent.neg_terminal[name])
|
|
571
|
+
else:
|
|
572
|
+
elem = self.pedb.create_voltage_probe(j, self.parent.neg_terminal)
|
|
573
|
+
elem.name = self.parent.name
|
|
574
|
+
circuit_elements.append(elem)
|
|
575
|
+
return circuit_elements
|
|
576
|
+
|
|
577
|
+
class Grpc(Common):
|
|
578
|
+
def __init__(self, parent):
|
|
579
|
+
super().__init__(parent)
|
|
580
|
+
|
|
581
|
+
class DotNet(Grpc):
|
|
582
|
+
def __init__(self, parent):
|
|
583
|
+
super().__init__(parent)
|
|
584
|
+
|
|
585
|
+
def __init__(self, pedb, **kwargs):
|
|
586
|
+
kwargs["type"] = "probe"
|
|
587
|
+
super().__init__(pedb, **kwargs)
|
|
588
|
+
if os.environ["PYEDB_USE_DOTNET"] == "0":
|
|
589
|
+
self.api = self.Grpc(self)
|
|
590
|
+
else:
|
|
591
|
+
self.api = self.DotNet(self)
|
|
592
|
+
|
|
593
|
+
|
|
445
594
|
class CfgWavePort:
|
|
446
595
|
def __init__(self, pedb, **kwargs):
|
|
447
596
|
self._pedb = pedb
|
|
@@ -140,11 +140,11 @@ class Configuration:
|
|
|
140
140
|
temp = []
|
|
141
141
|
for _, pdef in pedb_defs.items():
|
|
142
142
|
cfg_def = CfgPadstackDefinition(self._pedb, pdef)
|
|
143
|
-
cfg_def.retrieve_parameters_from_edb()
|
|
143
|
+
cfg_def.api.retrieve_parameters_from_edb()
|
|
144
144
|
temp.append(cfg_def)
|
|
145
145
|
self.cfg_data.stackup.apply()
|
|
146
146
|
for cfg_pdef in temp:
|
|
147
|
-
cfg_pdef.set_parameters_to_edb()
|
|
147
|
+
cfg_pdef.api.set_parameters_to_edb()
|
|
148
148
|
else:
|
|
149
149
|
self.cfg_data.stackup.apply()
|
|
150
150
|
|
|
@@ -171,6 +171,9 @@ class Configuration:
|
|
|
171
171
|
# Configure ports
|
|
172
172
|
self.cfg_data.ports.apply()
|
|
173
173
|
|
|
174
|
+
# Configure probes
|
|
175
|
+
self.cfg_data.probes.apply()
|
|
176
|
+
|
|
174
177
|
return True
|
|
175
178
|
|
|
176
179
|
def _load_stackup(self):
|
pyedb/dotnet/edb.py
CHANGED
|
@@ -4106,7 +4106,7 @@ class Edb(Database):
|
|
|
4106
4106
|
term.boundary_type = "kVoltageSource"
|
|
4107
4107
|
|
|
4108
4108
|
ref_term = Terminal(self, ref_terminal._edb_object)
|
|
4109
|
-
ref_term.boundary_type = "
|
|
4109
|
+
ref_term.boundary_type = "kVoltageSource"
|
|
4110
4110
|
|
|
4111
4111
|
term.ref_terminal = ref_terminal
|
|
4112
4112
|
return self.sources[term.name]
|
|
@@ -66,7 +66,7 @@ def primitive_cast(pedb, edb_object):
|
|
|
66
66
|
elif edb_object.GetPrimitiveType().ToString() == "Bondwire":
|
|
67
67
|
return Bondwire(pedb, edb_object)
|
|
68
68
|
elif edb_object.GetPrimitiveType().ToString() == "Text":
|
|
69
|
-
return EdbText(
|
|
69
|
+
return EdbText(pedb, edb_object)
|
|
70
70
|
elif edb_object.GetPrimitiveType().ToString() == "PrimitivePlugin":
|
|
71
71
|
return
|
|
72
72
|
elif edb_object.GetPrimitiveType().ToString() == "Path3D":
|
|
@@ -152,7 +152,7 @@ class Layout(ObjBase):
|
|
|
152
152
|
is_pins : bool, optional
|
|
153
153
|
True for pins, false for vias (default).
|
|
154
154
|
"""
|
|
155
|
-
self._edb_object.
|
|
155
|
+
self._edb_object.ConvertPrimitivestoVias(convert_py_list_to_net_list(primitives), is_pins)
|
|
156
156
|
|
|
157
157
|
@property
|
|
158
158
|
def zone_primitives(self):
|
|
@@ -220,7 +220,7 @@ class Layout(ObjBase):
|
|
|
220
220
|
-------
|
|
221
221
|
"""
|
|
222
222
|
|
|
223
|
-
return [EDBNetsData(net, self._pedb) for net in self._edb_object.Nets]
|
|
223
|
+
return [EDBNetsData(net, self._pedb) for net in self._edb_object.Nets if net]
|
|
224
224
|
|
|
225
225
|
@property
|
|
226
226
|
def primitives(self):
|
|
@@ -230,7 +230,7 @@ class Layout(ObjBase):
|
|
|
230
230
|
-------
|
|
231
231
|
list of :class:`dotnet.edb_core.dotnet.primitive.PrimitiveDotNet` cast objects.
|
|
232
232
|
"""
|
|
233
|
-
return [primitive_cast(self._pedb, p) for p in self._edb_object.Primitives]
|
|
233
|
+
return [primitive_cast(self._pedb, p) for p in self._edb_object.Primitives if p]
|
|
234
234
|
|
|
235
235
|
@property
|
|
236
236
|
def bondwires(self):
|
|
@@ -331,16 +331,32 @@ class Layout(ObjBase):
|
|
|
331
331
|
obj = self._pedb._edb.Cell.Hierarchy.Component.FindByName(self._edb_object, value)
|
|
332
332
|
return EDBComponent(self._pedb, obj) if obj is not None else None
|
|
333
333
|
|
|
334
|
-
def find_primitive(
|
|
334
|
+
def find_primitive(
|
|
335
|
+
self, layer_name: Union[str, list] = None, name: Union[str, list] = None, net_name: Union[str, list] = None
|
|
336
|
+
) -> list:
|
|
335
337
|
"""Find a primitive objects by layer name.
|
|
336
338
|
|
|
337
339
|
Parameters
|
|
338
340
|
----------
|
|
339
|
-
layer_name : str, list
|
|
341
|
+
layer_name : str, list, optional
|
|
340
342
|
Name of the layer.
|
|
343
|
+
name : str, list, optional
|
|
344
|
+
Name of the primitive
|
|
345
|
+
net_name : str, list, optional
|
|
346
|
+
Name of the primitive
|
|
341
347
|
Returns
|
|
342
348
|
-------
|
|
343
349
|
list
|
|
344
350
|
"""
|
|
345
|
-
|
|
346
|
-
|
|
351
|
+
if layer_name is not None:
|
|
352
|
+
layer_name = layer_name if isinstance(layer_name, list) else [layer_name]
|
|
353
|
+
if name is not None:
|
|
354
|
+
name = name if isinstance(name, list) else [name]
|
|
355
|
+
if net_name is not None:
|
|
356
|
+
net_name = net_name if isinstance(net_name, list) else [net_name]
|
|
357
|
+
|
|
358
|
+
prims = self.primitives
|
|
359
|
+
prims = [i for i in prims if i.aedt_name in name] if name is not None else prims
|
|
360
|
+
prims = [i for i in prims if i.layer_name in layer_name] if layer_name is not None else prims
|
|
361
|
+
prims = [i for i in prims if i.net_name in net_name] if net_name is not None else prims
|
|
362
|
+
return prims
|
|
@@ -59,7 +59,7 @@ class EDBNetsData(NetDotNet):
|
|
|
59
59
|
"""
|
|
60
60
|
from pyedb.dotnet.edb_core.cell.layout import primitive_cast
|
|
61
61
|
|
|
62
|
-
return [primitive_cast(self._app, i) for i in self.net_object.Primitives]
|
|
62
|
+
return [primitive_cast(self._app, i) for i in self.net_object.Primitives if i]
|
|
63
63
|
# return [self._app.layout.find_object_by_id(i.GetId()) for i in self.net_object.Primitives]
|
|
64
64
|
|
|
65
65
|
@property
|
|
@@ -34,6 +34,7 @@ from pyedb.dotnet.edb_core.general import (
|
|
|
34
34
|
pascal_to_snake,
|
|
35
35
|
snake_to_pascal,
|
|
36
36
|
)
|
|
37
|
+
from pyedb.dotnet.edb_core.geometry.polygon_data import PolygonData
|
|
37
38
|
from pyedb.generic.general_methods import generate_unique_name
|
|
38
39
|
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
39
40
|
|
|
@@ -155,6 +156,24 @@ class EDBPadProperties(object):
|
|
|
155
156
|
def polygon_data(self):
|
|
156
157
|
"""Parameters.
|
|
157
158
|
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
list
|
|
162
|
+
List of parameters.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
flag, edb_object, _, _, _ = self._edb_padstack.GetData().GetPolygonalPadParameters(
|
|
166
|
+
self.layer_name, self.int_to_pad_type(self.pad_type)
|
|
167
|
+
)
|
|
168
|
+
if flag:
|
|
169
|
+
return PolygonData(self._edb._app, edb_object)
|
|
170
|
+
else: # pragma no cover
|
|
171
|
+
raise AttributeError("No polygon data.")
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def _polygon_data_dotnet(self):
|
|
175
|
+
"""Parameters.
|
|
176
|
+
|
|
158
177
|
Returns
|
|
159
178
|
-------
|
|
160
179
|
list
|
|
@@ -780,7 +799,7 @@ class EDBPadstack(object):
|
|
|
780
799
|
pdef_data.SetHoleRange(getattr(self._edb.definition.PadstackHoleRange, snake_to_pascal(value)))
|
|
781
800
|
self._padstack_def_data = pdef_data
|
|
782
801
|
|
|
783
|
-
def convert_to_3d_microvias(self, convert_only_signal_vias=True, hole_wall_angle=
|
|
802
|
+
def convert_to_3d_microvias(self, convert_only_signal_vias=True, hole_wall_angle=75, delete_padstack_def=True):
|
|
784
803
|
"""Convert actual padstack instance to microvias 3D Objects with a given aspect ratio.
|
|
785
804
|
|
|
786
805
|
Parameters
|
|
@@ -812,22 +831,9 @@ class EDBPadstack(object):
|
|
|
812
831
|
layer_names = [i for i in list(layers.keys())]
|
|
813
832
|
if convert_only_signal_vias:
|
|
814
833
|
signal_nets = [i for i in list(self._ppadstack._pedb.nets.signal_nets.keys())]
|
|
815
|
-
topl, topz, bottoml, bottomz = self._ppadstack._pedb.stackup.limits(True)
|
|
816
|
-
if self.via_start_layer in layers:
|
|
817
|
-
start_elevation = layers[self.via_start_layer].lower_elevation
|
|
818
|
-
else:
|
|
819
|
-
start_elevation = layers[self.instances[0].start_layer].lower_elevation
|
|
820
|
-
if self.via_stop_layer in layers:
|
|
821
|
-
stop_elevation = layers[self.via_stop_layer].upper_elevation
|
|
822
|
-
else:
|
|
823
|
-
stop_elevation = layers[self.instances[0].stop_layer].upper_elevation
|
|
824
834
|
|
|
825
|
-
|
|
826
|
-
rad1 = self.hole_properties[0] / 2 - math.tan(hole_wall_angle * diel_thick * math.pi / 180)
|
|
827
|
-
rad2 = self.hole_properties[0] / 2
|
|
835
|
+
layer_count = len(self._ppadstack._pedb.stackup.signal_layers)
|
|
828
836
|
|
|
829
|
-
if start_elevation < (topz + bottomz) / 2:
|
|
830
|
-
rad1, rad2 = rad2, rad1
|
|
831
837
|
i = 0
|
|
832
838
|
for via in list(self.padstack_instances.values()):
|
|
833
839
|
if convert_only_signal_vias and via.net_name in signal_nets or not convert_only_signal_vias:
|
|
@@ -863,18 +869,33 @@ class EDBPadstack(object):
|
|
|
863
869
|
self._get_edb_value(pos[1]),
|
|
864
870
|
self._get_edb_value(self.pad_by_layer[self.via_stop_layer].parameters_values[0] / 2),
|
|
865
871
|
)
|
|
866
|
-
for layer_name in layer_names:
|
|
872
|
+
for layer_idx, layer_name in enumerate(layer_names):
|
|
867
873
|
stop = ""
|
|
868
874
|
if layer_name == via.start_layer or started:
|
|
869
875
|
start = layer_name
|
|
870
876
|
stop = layer_names[layer_names.index(layer_name) + 1]
|
|
877
|
+
|
|
878
|
+
start_elevation = layers[start].lower_elevation
|
|
879
|
+
stop_elevation = layers[stop].upper_elevation
|
|
880
|
+
diel_thick = abs(start_elevation - stop_elevation)
|
|
881
|
+
|
|
882
|
+
rad_large = self.hole_diameter / 2
|
|
883
|
+
rad_small = rad_large - diel_thick * 1 / math.tan(math.radians(hole_wall_angle))
|
|
884
|
+
|
|
885
|
+
if layer_idx + 1 < layer_count / 2: # upper half of stack
|
|
886
|
+
rad_u = rad_large
|
|
887
|
+
rad_l = rad_small
|
|
888
|
+
else:
|
|
889
|
+
rad_u = rad_small
|
|
890
|
+
rad_l = rad_large
|
|
891
|
+
|
|
871
892
|
cloned_circle = self._edb.cell.primitive.circle.create(
|
|
872
893
|
layout,
|
|
873
894
|
start,
|
|
874
895
|
via._edb_padstackinstance.GetNet(),
|
|
875
896
|
self._get_edb_value(pos[0]),
|
|
876
897
|
self._get_edb_value(pos[1]),
|
|
877
|
-
self._get_edb_value(
|
|
898
|
+
self._get_edb_value(rad_u),
|
|
878
899
|
)
|
|
879
900
|
cloned_circle2 = self._edb.cell.primitive.circle.create(
|
|
880
901
|
layout,
|
|
@@ -882,7 +903,7 @@ class EDBPadstack(object):
|
|
|
882
903
|
via._edb_padstackinstance.GetNet(),
|
|
883
904
|
self._get_edb_value(pos[0]),
|
|
884
905
|
self._get_edb_value(pos[1]),
|
|
885
|
-
self._get_edb_value(
|
|
906
|
+
self._get_edb_value(rad_l),
|
|
886
907
|
)
|
|
887
908
|
s3d = self._edb.cell.hierarchy._hierarchy.Structure3D.Create(
|
|
888
909
|
layout, generate_unique_name("via3d_" + via.aedt_name.replace("via_", ""), n=3)
|
|
@@ -1247,10 +1268,18 @@ class EDBPadstackInstance(Primitive):
|
|
|
1247
1268
|
|
|
1248
1269
|
return self._pedb.create_port(terminal, ref_terminal, is_circuit_port)
|
|
1249
1270
|
|
|
1250
|
-
def _set_equipotential(self):
|
|
1271
|
+
def _set_equipotential(self, contact_radius=None):
|
|
1251
1272
|
"""Workaround solution. Remove when EDBAPI bug is fixed for dcir_equipotential_region."""
|
|
1252
1273
|
pad = self.definition.pad_by_layer[self.start_layer]
|
|
1253
|
-
|
|
1274
|
+
|
|
1275
|
+
pos_x, pos_y = self.position
|
|
1276
|
+
|
|
1277
|
+
if contact_radius is not None:
|
|
1278
|
+
prim = self._pedb.modeler.create_circle(pad.layer_name, pos_x, pos_y, contact_radius, self.net_name)
|
|
1279
|
+
prim.dcir_equipotential_region = True
|
|
1280
|
+
return
|
|
1281
|
+
|
|
1282
|
+
elif pad.shape.lower() == "circle":
|
|
1254
1283
|
ra = self._pedb.edb_value(pad.parameters_values[0] / 2)
|
|
1255
1284
|
pos = self.position
|
|
1256
1285
|
prim = self._pedb.modeler.create_circle(pad.layer_name, pos[0], pos[1], ra, self.net_name)
|
|
@@ -1265,9 +1294,16 @@ class EDBPadstackInstance(Primitive):
|
|
|
1265
1294
|
center_point=self.position,
|
|
1266
1295
|
rotation=self.component.rotation,
|
|
1267
1296
|
)
|
|
1268
|
-
|
|
1297
|
+
elif pad.shape.lower() == "oval":
|
|
1298
|
+
width, height, _ = pad.parameters_values
|
|
1299
|
+
prim = self._pedb.modeler.create_circle(
|
|
1300
|
+
pad.layer_name, self.position[0], self.position[1], height / 2, self.net_name
|
|
1301
|
+
)
|
|
1269
1302
|
elif pad.polygon_data:
|
|
1270
|
-
prim = self._pedb.modeler.create_polygon(
|
|
1303
|
+
prim = self._pedb.modeler.create_polygon(
|
|
1304
|
+
pad.polygon_data._edb_object, self.start_layer, net_name=self.net_name
|
|
1305
|
+
)
|
|
1306
|
+
prim.move(self.position)
|
|
1271
1307
|
else:
|
|
1272
1308
|
return
|
|
1273
1309
|
prim.dcir_equipotential_region = True
|
|
@@ -1579,7 +1615,7 @@ class EDBPadstackInstance(Primitive):
|
|
|
1579
1615
|
layer_list = []
|
|
1580
1616
|
start_layer_name = start_layer.GetName()
|
|
1581
1617
|
stop_layer_name = stop_layer.GetName()
|
|
1582
|
-
for layer_name in list(self._pedb.stackup.
|
|
1618
|
+
for layer_name in list(self._pedb.stackup.signal_layers.keys()):
|
|
1583
1619
|
if started:
|
|
1584
1620
|
layer_list.append(layer_name)
|
|
1585
1621
|
if layer_name == stop_layer_name or layer_name == start_layer_name:
|
|
@@ -1921,7 +1957,7 @@ class EDBPadstackInstance(Primitive):
|
|
|
1921
1957
|
|
|
1922
1958
|
pad_shape = padstack_pad.geometry_type
|
|
1923
1959
|
params = padstack_pad.parameters_values
|
|
1924
|
-
polygon_data = padstack_pad.
|
|
1960
|
+
polygon_data = padstack_pad._polygon_data_dotnet
|
|
1925
1961
|
|
|
1926
1962
|
def _rotate(p):
|
|
1927
1963
|
x = p[0] * math.cos(rotation) - p[1] * math.sin(rotation)
|
|
@@ -2038,8 +2074,8 @@ class EDBPadstackInstance(Primitive):
|
|
|
2038
2074
|
# Polygon
|
|
2039
2075
|
points = []
|
|
2040
2076
|
i = 0
|
|
2041
|
-
while i < polygon_data.
|
|
2042
|
-
point = polygon_data.
|
|
2077
|
+
while i < polygon_data._edb_object.Count:
|
|
2078
|
+
point = polygon_data._edb_object.GetPoint(i)
|
|
2043
2079
|
i += 1
|
|
2044
2080
|
if point.IsArc():
|
|
2045
2081
|
continue
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
|
+
from typing import Union
|
|
22
23
|
|
|
23
24
|
from pyedb.dotnet.edb_core.general import convert_py_list_to_net_list
|
|
24
25
|
from pyedb.dotnet.edb_core.geometry.point_data import PointData
|
|
@@ -128,3 +129,7 @@ class PolygonData:
|
|
|
128
129
|
arcs = convert_py_list_to_net_list(arcs)
|
|
129
130
|
poly = self._edb_object.CreateFromArcs(arcs, flag)
|
|
130
131
|
return PolygonData(self._pedb, poly)
|
|
132
|
+
|
|
133
|
+
def point_in_polygon(self, x: Union[str, float], y: Union[str, float]) -> bool:
|
|
134
|
+
"""Determines whether a point is inside the polygon."""
|
|
135
|
+
return self._edb_object.PointInPolygon(self._pedb.point_data(x, y))
|
pyedb/dotnet/edb_core/modeler.py
CHANGED
|
@@ -661,8 +661,7 @@ class Modeler(object):
|
|
|
661
661
|
else:
|
|
662
662
|
polygonData = main_shape
|
|
663
663
|
if not polygonData or polygonData.IsNull():
|
|
664
|
-
|
|
665
|
-
return False
|
|
664
|
+
raise RuntimeError("Failed to create main shape polygon data")
|
|
666
665
|
for void in voids:
|
|
667
666
|
if isinstance(void, list):
|
|
668
667
|
void = self.Shape("polygon", points=void)
|