Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/pyedb/dotnet/database/edb_data/padstacks_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -1586,10 +1586,12 @@ def backdrill_parameters(self, params):
False,
)

def set_back_drill_by_layer(self, drill_to_layer, diameter, offset):
def set_back_drill_by_layer(self, drill_to_layer, diameter, offset, from_bottom=True):
"""Method added to bring compatibility with grpc."""

self.set_backdrill_bottom(drill_depth=drill_to_layer.name, drill_diameter=diameter, offset=offset)
if from_bottom:
if not isinstance(drill_to_layer, str):
drill_to_layer = drill_to_layer.name
self.set_backdrill_bottom(drill_depth=drill_to_layer, drill_diameter=diameter, offset=offset)

def set_backdrill_bottom(self, drill_depth, drill_diameter, offset=0.0):
"""Set backdrill from bottom.
Expand Down
188 changes: 187 additions & 1 deletion src/pyedb/dotnet/database/padstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

from collections import defaultdict
import math
from typing import Dict, List
from typing import Dict, List, Optional, Union
import warnings

import numpy as np
Expand Down Expand Up @@ -442,6 +442,192 @@ def create_circular_padstack(
)
PadStack.SetData(new_PadStackData)

def create_dielectric_filled_backdrills(
self,
layer: str,
diameter: Union[float, str],
material: str,
permittivity: float,
padstack_instances: Optional[List[EDBPadstackInstance]] = None,
padstack_definition: Optional[Union[str, List[str]]] = None,
dielectric_loss_tangent: Optional[float] = None,
nets: Optional[Union[str, List[str]]] = None,
) -> bool:
r"""Create dielectric-filled back-drills for through-hole vias.

Back-drilling (a.k.a. controlled-depth drilling) is used to remove the
unused via stub that acts as an unterminated transmission-line segment,
thereby improving signal-integrity at high frequencies. This routine
goes one step further: after the stub is removed the resulting cylindrical
cavity is **completely filled** with a user-specified dielectric. The
fill material restores mechanical rigidity, prevents solder-wicking, and
keeps the original via’s electrical characteristics intact on the
remaining, still-plated, portion.

Selection criteria
------------------
A via is processed only when **all** of the following are true:

1. It is a through-hole structure (spans at least three metal layers).
2. It includes the requested ``layer`` somewhere in its layer span.
3. It belongs to one of the supplied ``padstack_definition`` names
(or to *any* definition if the argument is omitted).
4. It is attached to one of the supplied ``nets`` (or to *any* net if
the argument is omitted).

Geometry that is created
------------------------
For every qualified via the routine

* Generates a new pad-stack definition named ``<original_name>_BD``.
The definition is drilled from the **bottom-most signal layer** up to
and **including** ``layer``, uses the exact ``diameter`` supplied, and
is plated at 100 %.
* Places an additional pad-stack instance on top of the original via,
thereby filling the newly drilled cavity with the requested
``material``.
* Leaves the original via untouched—only its unused stub is removed.

The back-drill is **not** subtracted from anti-pads or plane clearances;
the filling material is assumed to be electrically invisible at the
frequencies of interest.

Parameters
----------
layer : :class:`str`
Signal layer name up to which the back-drill is performed (inclusive).
The drill always starts on the bottom-most signal layer of the stack-up.
diameter : :class:`float` or :class:`str`
Finished hole diameter for the back-drill. A numeric value is
interpreted in the database length unit; a string such as
``"0.3mm"`` is evaluated with units.
material : :class:`str`
Name of the dielectric material that fills the drilled cavity. If the
material does not yet exist in the central material library it is
created on the fly.
permittivity : :class:`float`
Relative permittivity :math:`\varepsilon_{\mathrm{r}}` used when the
material has to be created. Must be positive.
padstack_instances : :class:`list` [:class:`PadstackInstance` ], optional
Explicit list of via instances to process. When provided,
``padstack_definition`` and ``nets`` are ignored for filtering.
padstack_definition : :class:`str` or :class:`list` [:class:`str` ], optional
Pad-stack definition(s) to process. If omitted, **all** through-hole
definitions are considered.
dielectric_loss_tangent : :class:`float`, optional
Loss tangent :math:`\tan\delta` used when the material has to be
created. Defaults to ``0.0``.
nets : :class:`str` or :class:`list` [:class:`str` ], optional
Net name(s) used to filter vias. If omitted, vias belonging to
**any** net are processed.

Returns
-------
:class:`bool`
``True`` when at least one back-drill was successfully created.
``False`` if no suitable via was found or any error occurred.

Raises
------
ValueError
If ``material`` is empty or if ``permittivity`` is non-positive when a
new material must be created.

Notes
-----
* The routine is safe to call repeatedly: existing back-drills are **not**
duplicated because the ``*_BD`` definition name is deterministic.
* The original via keeps its pad-stack definition and net assignment; only
its unused stub is removed.
* The back-drill is **not** subtracted from anti-pads or plane clearances;
the filling material is assumed to be electrically invisible at the
frequencies of interest.

Examples
--------
Create back-drills on all vias belonging to two specific pad-stack
definitions and two DDR4 nets:

>>> edb.padstacks.create_dielectric_filled_backdrills(
... layer="L3",
... diameter="0.25mm",
... material="EPON_827",
... permittivity=3.8,
... dielectric_loss_tangent=0.015,
... padstack_definition=["VIA_10MIL", "VIA_16MIL"],
... nets=["DDR4_DQ0", "DDR4_DQ1"],
... )
True
"""
_padstack_instances = defaultdict(list)
if padstack_instances:
for inst in padstack_instances:
_padstack_instances[inst.padstack_definition].append(inst)
else:
if padstack_definition:
if isinstance(padstack_definition, str):
padstack_definition = [padstack_definition]

padstack_definitions = [
self.definitions.get(padstack_def, None) for padstack_def in padstack_definition
]
if nets:
if isinstance(nets, str):
nets = [nets]
for padstack_definition in padstack_definitions:
_padstack_instances[padstack_definition.name] = self.get_instances(
definition_name=padstack_definition.name, net_name=nets
)
else:
for padstack_definition in padstack_definitions:
_padstack_instances[padstack_definition.name] = padstack_definition.instances
elif nets:
instances = self.get_instances(net_name=nets)
for inst in instances:
padsatck_def_name = inst.padstack_definition
padstack_def_layers = inst.layer_range_names
if layer in padstack_def_layers and len(padstack_def_layers) >= 3:
if not padsatck_def_name in _padstack_instances:
_padstack_instances[padsatck_def_name] = [inst]
else:
_padstack_instances[padsatck_def_name].append(inst)
else:
self._pedb.logger.info(
f"Drill layer {layer} not in padstack definition layers "
f"or layer number = {len(padstack_def_layers)} "
f"for padstack definition {padsatck_def_name}, skipping for backdrills"
)
if not material:
raise ValueError("`material` must be specified")
if not material in self._pedb.materials:
if not dielectric_loss_tangent:
dielectric_loss_tangent = 0.0
self._pedb.materials.add_dielectric_material(
name=material, permittivity=permittivity, dielectric_loss_tangent=dielectric_loss_tangent
)
for def_name, instances in _padstack_instances.items():
padstack_def_backdrill_name = f"{def_name}_BD"
start_layer = list(self._pedb.stackup.signal_layers.keys())[-1] # bottom layer
self.create(
padstackname=padstack_def_backdrill_name,
holediam=self._pedb.value(diameter),
paddiam="0.0",
antipaddiam="0.0",
start_layer=start_layer,
stop_layer=layer,
)
self.definitions[padstack_def_backdrill_name].material = material
self.definitions[padstack_def_backdrill_name].hole_plating_ratio = 100.0
for inst in instances:
inst.set_back_drill_by_layer(drill_to_layer=layer, offset=0.0, diameter=self._pedb.value(diameter))
self.place(
position=inst.position,
definition_name=padstack_def_backdrill_name,
fromlayer=start_layer,
tolayer=layer,
)
return True

def delete_padstack_instances(self, net_names): # pragma: no cover
"""Delete padstack instances by net names.

Expand Down
2 changes: 1 addition & 1 deletion src/pyedb/grpc/database/definition/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ def add_dielectric_material(self, name, permittivity, dielectric_loss_tangent, *
:class:`Material <pyedb.grpc.database.definition.materials.Material>`
Material object.
"""
extended_kwargs = {key: value for (key, value) in kwargs.items()}
extended_kwargs = {key: value for (key, value) in kwargs.items() if not key == "name"}
extended_kwargs["permittivity"] = permittivity
extended_kwargs["dielectric_loss_tangent"] = dielectric_loss_tangent
material = self.add_material(name, **extended_kwargs)
Expand Down
25 changes: 16 additions & 9 deletions src/pyedb/grpc/database/definition/padstack_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,22 @@ def via_stop_layer(self):
warnings.warn("via_stop_layer is deprecated. Use stop_layer instead.", DeprecationWarning)
return self.stop_layer

@property
def material(self):
"""Return hole material name.

Returns
-------
str
Hole material name.
"""
return self.data.material.value

@material.setter
def material(self, value):
if isinstance(value, str):
self.data.material = value

@property
def hole_diameter(self) -> float:
"""Hole diameter.
Expand Down Expand Up @@ -647,15 +663,6 @@ def hole_range(self, value):
else: # pragma no cover
self.data.hole_range = GrpcPadstackHoleRange.UNKNOWN_RANGE

@property
def material(self) -> str:
"""Return hole material name."""
return self.data.material.value

@material.setter
def material(self, value):
self.data.material.value = value

def convert_to_3d_microvias(
self, convert_only_signal_vias=True, hole_wall_angle=15, delete_padstack_def=True
) -> bool:
Expand Down
Loading
Loading