diff --git a/src/pyedb/configuration/cfg_operations.py b/src/pyedb/configuration/cfg_operations.py index 6847cf2dba..b2fea2e2a3 100644 --- a/src/pyedb/configuration/cfg_operations.py +++ b/src/pyedb/configuration/cfg_operations.py @@ -24,7 +24,9 @@ class CfgCutout(CfgBase): - def __init__(self, **kwargs): + def __init__(self, pedb, **kwargs): + self._pedb = pedb + self.signal_list = kwargs.get("signal_list") self.reference_list = kwargs.get("reference_list") self.extent_type = kwargs.get("extent_type") @@ -38,7 +40,7 @@ def __init__(self, **kwargs): self.extent_defeature = kwargs.get("extent_defeature") self.remove_single_pin_components = kwargs.get("remove_single_pin_components") self.custom_extent = kwargs.get("custom_extent") - self.custom_extent_units = kwargs.get("custom_extent_units") + self.custom_extent_units = kwargs.get("custom_extent_units", "meter") self.include_partial_instances = kwargs.get("include_partial_instances") self.keep_voids = kwargs.get("keep_voids") self.check_terminals = kwargs.get("check_terminals") @@ -49,13 +51,45 @@ def __init__(self, **kwargs): self.simple_pad_check = kwargs.get("simple_pad_check") self.keep_lines_as_path = kwargs.get("keep_lines_as_path") + def get_data_from_db(self): + if "pyedb_cutout" in self._pedb.stackup.all_layers: + poly = self._pedb.layout.find_primitive(layer_name="pyedb_cutout")[0] + self.custom_extent = poly.polygon_data.points + + net_names = [] + for name, obj in self._pedb.nets.nets.items(): + if obj.primitives[0].layer.name == "pyedb_cutout": + continue + if len(obj.primitives) > 0: + net_names.append(name) + + self.reference_list = [] + self.signal_list = net_names + return self.export_properties() + + def export_properties(self): + return { + "signal_list": self.signal_list, + "reference_list": self.reference_list, + "custom_extent": self.custom_extent, + } + class CfgOperations(CfgBase): def __init__(self, pedb, data): self._pedb = pedb - self.op_cutout = CfgCutout(**data["cutout"]) if "cutout" in data else None + self.op_cutout = CfgCutout(pedb, **data["cutout"]) if "cutout" in data else None def apply(self): """Imports operation information from JSON.""" if self.op_cutout: - self._pedb.cutout(**self.op_cutout.get_attributes()) + polygon_points = self._pedb.cutout(**self.op_cutout.get_attributes()) + if not "pyedb_cutout" in self._pedb.stackup.all_layers: + self._pedb.stackup.add_document_layer(name="pyedb_cutout") + self._pedb.modeler.create_polygon(polygon_points, layer_name="pyedb_cutout", net_name="pyedb_cutout") + + # create a polygon on pyedb layer + + def get_data_from_db(self): + self.op_cutout = CfgCutout(self._pedb) + return {"cutout": self.op_cutout.get_data_from_db()} diff --git a/src/pyedb/configuration/configuration.py b/src/pyedb/configuration/configuration.py index 3da5305085..42d0d28c5f 100644 --- a/src/pyedb/configuration/configuration.py +++ b/src/pyedb/configuration/configuration.py @@ -280,6 +280,8 @@ def get_data_from_db(self, **kwargs): data["nets"] = self.cfg_data.nets.get_data_from_db() if kwargs.get("pin_groups", False): data["pin_groups"] = self.cfg_data.pin_groups.get_data_from_db() + if kwargs.get("operations", False): + data["operations"] = self.cfg_data.operations.get_data_from_db() return data @@ -293,6 +295,7 @@ def export( ports=True, nets=True, pin_groups=True, + operations=True, ): """Export the configuration data from layout to a file. @@ -312,6 +315,10 @@ def export( Whether to export ports or not. nets : bool Whether to export nets. + pin_groups : bool + Whether to export pin groups. + operations : bool + Whether to export operations. Returns ------- bool @@ -326,6 +333,7 @@ def export( ports=ports, nets=nets, pin_groups=pin_groups, + operations=operations, ) with open(file_path, "w") as f: json.dump(data, f, ensure_ascii=False, indent=4) diff --git a/src/pyedb/dotnet/edb_core/cell/layout.py b/src/pyedb/dotnet/edb_core/cell/layout.py index d60e923b68..18a30f5249 100644 --- a/src/pyedb/dotnet/edb_core/cell/layout.py +++ b/src/pyedb/dotnet/edb_core/cell/layout.py @@ -23,6 +23,8 @@ """ This module contains these classes: `EdbLayout` and `Shape`. """ +from typing import Union + from pyedb.dotnet.edb_core.cell.hierarchy.component import EDBComponent from pyedb.dotnet.edb_core.cell.primitive.bondwire import Bondwire from pyedb.dotnet.edb_core.cell.primitive.path import Path @@ -331,3 +333,17 @@ def find_component_by_name(self, value: str): """ obj = self._pedb._edb.Cell.Hierarchy.Component.FindByName(self._edb_object, value) return EDBComponent(self._pedb, obj) if obj is not None else None + + def find_primitive(self, layer_name: Union[str, list]) -> list: + """Find a primitive objects by layer name. + + Parameters + ---------- + layer_name : str, list + Name of the layer. + Returns + ------- + list + """ + layer_name = layer_name if isinstance(layer_name, list) else [layer_name] + return [i for i in self.primitives if i.layer_name in layer_name] diff --git a/tests/legacy/system/test_edb_configuration_2p0.py b/tests/legacy/system/test_edb_configuration_2p0.py index aa06c6c264..8af051bf46 100644 --- a/tests/legacy/system/test_edb_configuration_2p0.py +++ b/tests/legacy/system/test_edb_configuration_2p0.py @@ -407,7 +407,7 @@ def test_08a_operations_cutout(self, edb_examples): } edbapp = edb_examples.get_si_verse() assert edbapp.configuration.load(data, apply_file=True) - assert set(list(edbapp.nets.nets.keys())) == set(["SFPA_RX_P", "SFPA_RX_N", "GND"]) + assert set(list(edbapp.nets.nets.keys())) == set(["SFPA_RX_P", "SFPA_RX_N", "GND", "pyedb_cutout"]) edbapp.close() def test_09_padstacks(self, edb_examples): @@ -876,3 +876,25 @@ def test_16_export_to_external_file(self, edb_examples): assert len(data["nets"]["signal_nets"]) == 342 assert len(data["nets"]["power_ground_nets"]) == 6 edbapp.close() + + def test_16b_export_cutout(self, edb_examples): + data = { + "operations": { + "cutout": { + "signal_list": ["SFPA_RX_P", "SFPA_RX_N"], + "reference_list": ["GND"], + } + } + } + edbapp = edb_examples.get_si_verse() + edbapp.configuration.load(data, apply_file=True) + data_from_db = edbapp.configuration.get_data_from_db(operations=True) + assert len(data_from_db["operations"]["cutout"]["signal_list"]) == 3 + assert len(data_from_db["operations"]["cutout"]["custom_extent"]) > 0 + edbapp.close() + + data_from_db["operations"]["cutout"]["signal_list"].remove("GND") + data_from_db["operations"]["cutout"]["reference_list"].append("GND") + edbapp = edb_examples.get_si_verse() + edbapp.configuration.load(data_from_db, apply_file=True) + edbapp.close() diff --git a/tests/legacy/system/test_edb_layout.py b/tests/legacy/system/test_edb_layout.py new file mode 100644 index 0000000000..e77440b42b --- /dev/null +++ b/tests/legacy/system/test_edb_layout.py @@ -0,0 +1,37 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +import pytest + +pytestmark = [pytest.mark.unit, pytest.mark.legacy] + + +class TestClass: + @pytest.fixture(autouse=True) + def init(self, local_scratch): + pass + + def test_find(self, edb_examples): + edbapp = edb_examples.get_si_verse() + assert edbapp.layout.find_primitive(layer_name="Inner5(PWR2)") + edbapp.close()