pyedb 0.37.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 -46
- pyedb/configuration/configuration.py +5 -2
- pyedb/dotnet/edb.py +1 -1
- pyedb/dotnet/edb_core/cell/layout.py +2 -2
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +45 -58
- 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.37.0.dist-info → pyedb-0.38.0.dist-info}/METADATA +5 -5
- {pyedb-0.37.0.dist-info → pyedb-0.38.0.dist-info}/RECORD +21 -20
- {pyedb-0.37.0.dist-info → pyedb-0.38.0.dist-info}/LICENSE +0 -0
- {pyedb-0.37.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,9 +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]
|
|
36
|
-
self.
|
|
37
|
-
self.
|
|
38
|
-
self.
|
|
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)
|
|
39
42
|
|
|
40
43
|
def export_properties(self):
|
|
41
44
|
return {self.type: self.value}
|
|
@@ -208,6 +211,16 @@ class CfgPorts:
|
|
|
208
211
|
return ports
|
|
209
212
|
|
|
210
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
|
+
|
|
211
224
|
class CfgCircuitElement(CfgBase):
|
|
212
225
|
def __init__(self, pedb, **kwargs):
|
|
213
226
|
self._pedb = pedb
|
|
@@ -215,6 +228,7 @@ class CfgCircuitElement(CfgBase):
|
|
|
215
228
|
self.type = kwargs["type"]
|
|
216
229
|
self.reference_designator = kwargs.get("reference_designator", None)
|
|
217
230
|
self.distributed = kwargs.get("distributed", False)
|
|
231
|
+
self._elem_num = 1
|
|
218
232
|
|
|
219
233
|
pos = kwargs["positive_terminal"] # {"pin" : "A1"}
|
|
220
234
|
if list(pos.keys())[0] == "coordinates":
|
|
@@ -232,9 +246,10 @@ class CfgCircuitElement(CfgBase):
|
|
|
232
246
|
else:
|
|
233
247
|
self.negative_terminal_info = CfgTerminalInfo(self._pedb, **neg)
|
|
234
248
|
|
|
235
|
-
def
|
|
249
|
+
def create_terminals(self):
|
|
236
250
|
"""Create step 1. Collect positive and negative terminals."""
|
|
237
251
|
|
|
252
|
+
# Collect all positive terminals
|
|
238
253
|
pos_type, pos_value = self.positive_terminal_info.type, self.positive_terminal_info.value
|
|
239
254
|
pos_objs = dict()
|
|
240
255
|
pos_coor_terminal = dict()
|
|
@@ -248,32 +263,57 @@ class CfgCircuitElement(CfgBase):
|
|
|
248
263
|
net_name = self.positive_terminal_info.net
|
|
249
264
|
pos_coor_terminal[self.name] = self._pedb.get_point_terminal(self.name, net_name, point, layer)
|
|
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,56 +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
|
-
if not t.is_reference_terminal:
|
|
410
|
-
radius = self.positive_terminal_info.contact_radius
|
|
411
|
-
num_of_contact = self.positive_terminal_info.num_of_contact
|
|
412
|
-
inline = self.positive_terminal_info.inline
|
|
413
|
-
else:
|
|
414
|
-
radius = self.negative_terminal_info.contact_radius
|
|
415
|
-
num_of_contact = self.negative_terminal_info.num_of_contact
|
|
416
|
-
inline = self.negative_terminal_info.inline
|
|
417
|
-
|
|
418
|
-
pads = []
|
|
419
|
-
if t.terminal_type == "PadstackInstanceTerminal":
|
|
420
|
-
pads.append(t.reference_object)
|
|
421
|
-
elif t.terminal_type == "PinGroupTerminal":
|
|
422
|
-
name = t._edb_object.GetPinGroup().GetName()
|
|
423
|
-
pg = self._pedb.siwave.pin_groups[name]
|
|
424
|
-
pads.extend([i for _, i in pg.pins.items()])
|
|
425
|
-
elif t.terminal_type == "PointTerminal" and radius:
|
|
426
|
-
temp = [i for i in self._pedb.layout.terminals if i.name == t.name][0]
|
|
427
|
-
if radius is not None:
|
|
428
|
-
prim = self._pedb.modeler.create_circle(
|
|
429
|
-
temp.layer.name, temp.location[0], temp.location[1], radius, temp.net_name
|
|
430
|
-
)
|
|
431
|
-
prim.dcir_equipotential_region = True
|
|
432
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()]
|
|
433
536
|
for i in pads:
|
|
434
|
-
|
|
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.")
|
|
435
545
|
|
|
436
546
|
return circuit_elements
|
|
437
547
|
|
|
@@ -446,6 +556,41 @@ class CfgSource(CfgCircuitElement):
|
|
|
446
556
|
}
|
|
447
557
|
|
|
448
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
|
+
|
|
449
594
|
class CfgWavePort:
|
|
450
595
|
def __init__(self, pedb, **kwargs):
|
|
451
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):
|
|
@@ -24,8 +24,6 @@ from collections import OrderedDict
|
|
|
24
24
|
import math
|
|
25
25
|
import warnings
|
|
26
26
|
|
|
27
|
-
import numpy as np
|
|
28
|
-
|
|
29
27
|
from pyedb.dotnet.clr_module import String
|
|
30
28
|
from pyedb.dotnet.edb_core.cell.primitive.primitive import Primitive
|
|
31
29
|
from pyedb.dotnet.edb_core.dotnet.database import PolygonDataDotNet
|
|
@@ -36,6 +34,7 @@ from pyedb.dotnet.edb_core.general import (
|
|
|
36
34
|
pascal_to_snake,
|
|
37
35
|
snake_to_pascal,
|
|
38
36
|
)
|
|
37
|
+
from pyedb.dotnet.edb_core.geometry.polygon_data import PolygonData
|
|
39
38
|
from pyedb.generic.general_methods import generate_unique_name
|
|
40
39
|
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
41
40
|
|
|
@@ -157,6 +156,24 @@ class EDBPadProperties(object):
|
|
|
157
156
|
def polygon_data(self):
|
|
158
157
|
"""Parameters.
|
|
159
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
|
+
|
|
160
177
|
Returns
|
|
161
178
|
-------
|
|
162
179
|
list
|
|
@@ -782,7 +799,7 @@ class EDBPadstack(object):
|
|
|
782
799
|
pdef_data.SetHoleRange(getattr(self._edb.definition.PadstackHoleRange, snake_to_pascal(value)))
|
|
783
800
|
self._padstack_def_data = pdef_data
|
|
784
801
|
|
|
785
|
-
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):
|
|
786
803
|
"""Convert actual padstack instance to microvias 3D Objects with a given aspect ratio.
|
|
787
804
|
|
|
788
805
|
Parameters
|
|
@@ -814,22 +831,9 @@ class EDBPadstack(object):
|
|
|
814
831
|
layer_names = [i for i in list(layers.keys())]
|
|
815
832
|
if convert_only_signal_vias:
|
|
816
833
|
signal_nets = [i for i in list(self._ppadstack._pedb.nets.signal_nets.keys())]
|
|
817
|
-
topl, topz, bottoml, bottomz = self._ppadstack._pedb.stackup.limits(True)
|
|
818
|
-
if self.via_start_layer in layers:
|
|
819
|
-
start_elevation = layers[self.via_start_layer].lower_elevation
|
|
820
|
-
else:
|
|
821
|
-
start_elevation = layers[self.instances[0].start_layer].lower_elevation
|
|
822
|
-
if self.via_stop_layer in layers:
|
|
823
|
-
stop_elevation = layers[self.via_stop_layer].upper_elevation
|
|
824
|
-
else:
|
|
825
|
-
stop_elevation = layers[self.instances[0].stop_layer].upper_elevation
|
|
826
834
|
|
|
827
|
-
|
|
828
|
-
rad1 = self.hole_properties[0] / 2 - math.tan(hole_wall_angle * diel_thick * math.pi / 180)
|
|
829
|
-
rad2 = self.hole_properties[0] / 2
|
|
835
|
+
layer_count = len(self._ppadstack._pedb.stackup.signal_layers)
|
|
830
836
|
|
|
831
|
-
if start_elevation < (topz + bottomz) / 2:
|
|
832
|
-
rad1, rad2 = rad2, rad1
|
|
833
837
|
i = 0
|
|
834
838
|
for via in list(self.padstack_instances.values()):
|
|
835
839
|
if convert_only_signal_vias and via.net_name in signal_nets or not convert_only_signal_vias:
|
|
@@ -865,18 +869,33 @@ class EDBPadstack(object):
|
|
|
865
869
|
self._get_edb_value(pos[1]),
|
|
866
870
|
self._get_edb_value(self.pad_by_layer[self.via_stop_layer].parameters_values[0] / 2),
|
|
867
871
|
)
|
|
868
|
-
for layer_name in layer_names:
|
|
872
|
+
for layer_idx, layer_name in enumerate(layer_names):
|
|
869
873
|
stop = ""
|
|
870
874
|
if layer_name == via.start_layer or started:
|
|
871
875
|
start = layer_name
|
|
872
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
|
+
|
|
873
892
|
cloned_circle = self._edb.cell.primitive.circle.create(
|
|
874
893
|
layout,
|
|
875
894
|
start,
|
|
876
895
|
via._edb_padstackinstance.GetNet(),
|
|
877
896
|
self._get_edb_value(pos[0]),
|
|
878
897
|
self._get_edb_value(pos[1]),
|
|
879
|
-
self._get_edb_value(
|
|
898
|
+
self._get_edb_value(rad_u),
|
|
880
899
|
)
|
|
881
900
|
cloned_circle2 = self._edb.cell.primitive.circle.create(
|
|
882
901
|
layout,
|
|
@@ -884,7 +903,7 @@ class EDBPadstack(object):
|
|
|
884
903
|
via._edb_padstackinstance.GetNet(),
|
|
885
904
|
self._get_edb_value(pos[0]),
|
|
886
905
|
self._get_edb_value(pos[1]),
|
|
887
|
-
self._get_edb_value(
|
|
906
|
+
self._get_edb_value(rad_l),
|
|
888
907
|
)
|
|
889
908
|
s3d = self._edb.cell.hierarchy._hierarchy.Structure3D.Create(
|
|
890
909
|
layout, generate_unique_name("via3d_" + via.aedt_name.replace("via_", ""), n=3)
|
|
@@ -1249,47 +1268,15 @@ class EDBPadstackInstance(Primitive):
|
|
|
1249
1268
|
|
|
1250
1269
|
return self._pedb.create_port(terminal, ref_terminal, is_circuit_port)
|
|
1251
1270
|
|
|
1252
|
-
def _set_equipotential(self, contact_radius=None
|
|
1271
|
+
def _set_equipotential(self, contact_radius=None):
|
|
1253
1272
|
"""Workaround solution. Remove when EDBAPI bug is fixed for dcir_equipotential_region."""
|
|
1254
1273
|
pad = self.definition.pad_by_layer[self.start_layer]
|
|
1255
1274
|
|
|
1256
1275
|
pos_x, pos_y = self.position
|
|
1257
|
-
comp_rotation = self._pedb.edb_value(self.component.rotation).ToDouble() % 3.141592653589793
|
|
1258
1276
|
|
|
1259
1277
|
if contact_radius is not None:
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
prim.dcir_equipotential_region = True
|
|
1263
|
-
else:
|
|
1264
|
-
if pad.shape.lower() in ["rectangle", "oval"]:
|
|
1265
|
-
width, height = pad.parameters_values[0:2]
|
|
1266
|
-
radius = self._pedb.edb_value(contact_radius).ToDouble()
|
|
1267
|
-
else:
|
|
1268
|
-
return
|
|
1269
|
-
|
|
1270
|
-
if inline is False:
|
|
1271
|
-
x_offset = width / 2 - radius if comp_rotation == 0 else height / 2 - radius
|
|
1272
|
-
y_offset = height / 2 - radius if comp_rotation == 0 else width / 2 - radius
|
|
1273
|
-
positions = []
|
|
1274
|
-
for x, y in [[1, 1], [-1, 1], [1, -1], [-1, -1]]:
|
|
1275
|
-
positions.append([x_offset * x, y_offset * y])
|
|
1276
|
-
else:
|
|
1277
|
-
if width > height:
|
|
1278
|
-
offset = (width - radius * 2) / (num_of_contact - 1)
|
|
1279
|
-
else:
|
|
1280
|
-
offset = (height - radius * 2) / (num_of_contact - 1)
|
|
1281
|
-
|
|
1282
|
-
start_pos = (num_of_contact - 1) / 2
|
|
1283
|
-
offset = [offset * i for i in np.arange(start_pos * -1, start_pos + 1)]
|
|
1284
|
-
|
|
1285
|
-
if (width > height and comp_rotation == 0) or (width < height and comp_rotation != 0):
|
|
1286
|
-
positions = list(zip(offset, [0] * num_of_contact))
|
|
1287
|
-
else:
|
|
1288
|
-
positions = list(zip([0] * num_of_contact, offset))
|
|
1289
|
-
|
|
1290
|
-
for x, y in positions:
|
|
1291
|
-
prim = self._pedb.modeler.create_circle(pad.layer_name, pos_x + x, pos_y + y, radius, self.net_name)
|
|
1292
|
-
prim.dcir_equipotential_region = True
|
|
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
|
|
1293
1280
|
return
|
|
1294
1281
|
|
|
1295
1282
|
elif pad.shape.lower() == "circle":
|
|
@@ -1970,7 +1957,7 @@ class EDBPadstackInstance(Primitive):
|
|
|
1970
1957
|
|
|
1971
1958
|
pad_shape = padstack_pad.geometry_type
|
|
1972
1959
|
params = padstack_pad.parameters_values
|
|
1973
|
-
polygon_data = padstack_pad.
|
|
1960
|
+
polygon_data = padstack_pad._polygon_data_dotnet
|
|
1974
1961
|
|
|
1975
1962
|
def _rotate(p):
|
|
1976
1963
|
x = p[0] * math.cos(rotation) - p[1] * math.sin(rotation)
|
|
@@ -2087,8 +2074,8 @@ class EDBPadstackInstance(Primitive):
|
|
|
2087
2074
|
# Polygon
|
|
2088
2075
|
points = []
|
|
2089
2076
|
i = 0
|
|
2090
|
-
while i < polygon_data.
|
|
2091
|
-
point = polygon_data.
|
|
2077
|
+
while i < polygon_data._edb_object.Count:
|
|
2078
|
+
point = polygon_data._edb_object.GetPoint(i)
|
|
2092
2079
|
i += 1
|
|
2093
2080
|
if point.IsArc():
|
|
2094
2081
|
continue
|
|
@@ -1859,8 +1859,9 @@ class EdbPadstacks(object):
|
|
|
1859
1859
|
for _x, _y in zip(x_grid.ravel(), y_grid.ravel())
|
|
1860
1860
|
}
|
|
1861
1861
|
|
|
1862
|
+
all_instances = self.instances
|
|
1862
1863
|
for item in padstacks_inbox:
|
|
1863
1864
|
if item not in to_keep:
|
|
1864
|
-
|
|
1865
|
+
all_instances[item].delete()
|
|
1865
1866
|
|
|
1866
1867
|
return True
|
|
@@ -149,6 +149,21 @@ class AdaptiveSettings(object):
|
|
|
149
149
|
self.adaptive_settings.MinPasses = value
|
|
150
150
|
self._parent._update_setup()
|
|
151
151
|
|
|
152
|
+
@property
|
|
153
|
+
def min_converged_passes(self):
|
|
154
|
+
"""Minimum number of converged passes.
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
int
|
|
159
|
+
"""
|
|
160
|
+
return self.adaptive_settings.MinConvergedPasses
|
|
161
|
+
|
|
162
|
+
@min_converged_passes.setter
|
|
163
|
+
def min_converged_passes(self, value):
|
|
164
|
+
self.adaptive_settings.MinConvergedPasses = value
|
|
165
|
+
self._parent._update_setup()
|
|
166
|
+
|
|
152
167
|
@property
|
|
153
168
|
def save_fields(self):
|
|
154
169
|
"""Whether to turn on save fields.
|
|
@@ -160,11 +160,11 @@ class SweepData(object):
|
|
|
160
160
|
@freq_sweep_type.setter
|
|
161
161
|
def freq_sweep_type(self, value):
|
|
162
162
|
edb_freq_sweep_type = self._edb_object.TFreqSweepType
|
|
163
|
-
if value in [0, "kInterpolatingSweep"]:
|
|
163
|
+
if value in [0, "kInterpolatingSweep", "interpolation"]:
|
|
164
164
|
self._edb_object.FreqSweepType = edb_freq_sweep_type.kInterpolatingSweep
|
|
165
|
-
elif value in [1, "kDiscreteSweep"]:
|
|
165
|
+
elif value in [1, "kDiscreteSweep", "discrete"]:
|
|
166
166
|
self._edb_object.FreqSweepType = edb_freq_sweep_type.kDiscreteSweep
|
|
167
|
-
elif value in [2, "kBroadbandFastSweep"]:
|
|
167
|
+
elif value in [2, "kBroadbandFastSweep", "broadband"]:
|
|
168
168
|
self._edb_object.FreqSweepType = edb_freq_sweep_type.kBroadbandFastSweep
|
|
169
169
|
elif value in [3, "kNumSweepTypes"]:
|
|
170
170
|
self._edb_object.FreqSweepType = edb_freq_sweep_type.kNumSweepTypes
|
|
@@ -265,6 +265,7 @@ class SimulationSetup(object):
|
|
|
265
265
|
for k, v in kwargs.items():
|
|
266
266
|
if k in dir(sweep_data):
|
|
267
267
|
setattr(sweep_data, k, v)
|
|
268
|
+
sweep_data.freq_sweep_type = kwargs.get("sweep_type") if kwargs.get("sweep_type") else "interpolation"
|
|
268
269
|
|
|
269
270
|
if frequency_set is None:
|
|
270
271
|
sweep_type = "linear_scale"
|