diff --git a/src/pyedb/configuration/cfg_data.py b/src/pyedb/configuration/cfg_data.py index babcbaa011..5a358d1921 100644 --- a/src/pyedb/configuration/cfg_data.py +++ b/src/pyedb/configuration/cfg_data.py @@ -39,30 +39,42 @@ def __init__(self, pedb, **kwargs): self.pedb = pedb self.edb_comps = self.pedb.components.components self.general = CfgGeneral(self, kwargs.get("general", None)) + self.boundaries = {} if kwargs.get("boundaries", None): self.boundaries = CfgBoundaries(self, kwargs.get("boundaries", None)) - self.nets = CfgNets(self) + + self.nets = None if kwargs.get("nets"): self.nets = CfgNets( self, kwargs.get("nets", {}).get("signal_nets", []), kwargs.get("nets", {}).get("power_ground_nets", []) ) + self.components = [CfgComponent(self, **component) for component in kwargs.get("components", [])] + self.padstacks = CfgPadstacks(self, kwargs.get("padstacks", None)) + self.pin_groups = [CfgPinGroup(self, pin_group) for pin_group in kwargs.get("pin_groups", [])] + self.ports = [CfgPort(self, **port) for port in kwargs.get("ports", [])] + self.sources = [CfgSources(self, **source) for source in kwargs.get("sources", [])] - self.setups = [CfgSetup(self)] + + self.setups = [] if kwargs.get("setups", None): self.setups = [CfgSetup(self, setup) for setup in kwargs.get("setups", [])] + self.stackup = None + self.s_parameters = [ CfgSParameterModel(self, self.general.s_parameter_library, sparam_model) for sparam_model in kwargs.get("s_parameters", []) ] + self.spice_models = [ CfgSpiceModel(self, self.general.spice_model_library, spice_model) for spice_model in kwargs.get("spice_models", []) ] + self.package_definition = None self.operations = None diff --git a/src/pyedb/configuration/configuration.py b/src/pyedb/configuration/configuration.py index 84a4484977..0541961190 100644 --- a/src/pyedb/configuration/configuration.py +++ b/src/pyedb/configuration/configuration.py @@ -27,7 +27,6 @@ from pyedb.configuration.cfg_data import CfgData from pyedb.dotnet.edb_core.definition.package_def import PackageDef -from pyedb.dotnet.edb_core.stackup import LayerCollection from pyedb.generic.general_methods import pyedb_function_handler @@ -182,56 +181,44 @@ def _load_stackup(self): layers = data.get("layers") if layers: - lc = self._pedb.stackup input_signal_layers = [i for i in layers if i["type"].lower() == "signal"] - if not len(input_signal_layers) == len(lc.signal_layers): + if not len(input_signal_layers) == len(self._pedb.stackup.signal_layers): self._pedb.logger.error("Input signal layer count do not match.") return False - layer_clones = [] - doc_layer_clones = [] - for name, obj in lc.layers.items(): - if obj.is_stackup_layer: - if obj.type == "signal": # keep signal layers - layer_clones.append(obj) - else: - doc_layer_clones.append(obj) - - lc_new = LayerCollection(self._pedb) - lc_new.auto_refresh = False - signal_layer_ids = {} - top_layer_clone = None - - # add all signal layers + removal_list = [] + lc_signal_layers = [] + for name, obj in self._pedb.stackup.all_layers.items(): + if obj.type == "dielectric": + removal_list.append(name) + elif obj.type == "signal": + lc_signal_layers.append(obj.id) + for l in removal_list: + self._pedb.stackup.remove_layer(l) + + # update all signal layers + id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id} + signal_idx = 0 for l in layers: if l["type"] == "signal": - clone = layer_clones.pop(0) - clone.update(**l) - lc_new.add_layer_bottom(name=clone.name, layer_clone=clone) - signal_layer_ids[clone.name] = clone.id - - # add all document layers at bottom - for l in doc_layer_clones: - doc_layer = lc_new.add_document_layer(name=l.name, layer_clone=l) - first_doc_layer_name = doc_layer.name + layer_id = lc_signal_layers[signal_idx] + layer_name = id_name[layer_id] + self._pedb.stackup.layers[layer_name].update(**l) + signal_idx = signal_idx + 1 # add all dielectric layers. Dielectric layers must be added last. Otherwise, # dielectric layer will occupy signal and document layer id. prev_layer_clone = None l = layers.pop(0) if l["type"] == "signal": - prev_layer_clone = lc_new.layers[l["name"]] + prev_layer_clone = self._pedb.stackup.layers[l["name"]] else: - prev_layer_clone = lc_new.add_layer_top(**l) + prev_layer_clone = self._pedb.stackup.add_layer_top(**l) for idx, l in enumerate(layers): if l["type"] == "dielectric": - prev_layer_clone = lc_new.add_layer_below(base_layer_name=prev_layer_clone.name, **l) - else: - prev_layer_clone = lc_new.layers[l["name"]] - - lc._edb_object = lc_new._edb_object - lc_new.auto_refresh = True - lc.update_layout() + prev_layer_clone = self._pedb.stackup.add_layer_below(base_layer_name=prev_layer_clone.name, **l) + elif l["type"] == "signal": + prev_layer_clone = self._pedb.stackup.layers[l["name"]] @pyedb_function_handler def _load_operations(self): diff --git a/src/pyedb/dotnet/edb.py b/src/pyedb/dotnet/edb.py index 0889c0fb9e..59a86576d0 100644 --- a/src/pyedb/dotnet/edb.py +++ b/src/pyedb/dotnet/edb.py @@ -846,9 +846,7 @@ def stackup(self): >>> edbapp.stackup.layers["TOP"].thickness == 4e-05 >>> edbapp.stackup.add_layer("Diel", "GND", layer_type="dielectric", thickness="0.1mm", material="FR4_epoxy") """ - if not self._stackup2 and self.active_db: - self._stackup2 = Stackup(self, self.layout.layer_collection) - return self._stackup2 + return Stackup(self, self.layout.layer_collection) @property def materials(self): diff --git a/src/pyedb/dotnet/edb_core/edb_data/layer_data.py b/src/pyedb/dotnet/edb_core/edb_data/layer_data.py index 9e2b57021a..21431cf5fe 100644 --- a/src/pyedb/dotnet/edb_core/edb_data/layer_data.py +++ b/src/pyedb/dotnet/edb_core/edb_data/layer_data.py @@ -257,8 +257,8 @@ def __init__(self, pedb, edb_object=None, name="", layer_type="signal", **kwargs self._lower_elevation = 0.0 def _create(self, layer_type): - layer_type = self._layer_name_mapping[layer_type] - layer_type = self._layer_type_mapping[layer_type] + layer_type_edb_name = self._layer_name_mapping[layer_type] + layer_type = self._layer_type_mapping[layer_type_edb_name] self._edb_object = self._pedb.edb_api.cell._cell.StackupLayer( self._name, layer_type, diff --git a/src/pyedb/dotnet/edb_core/stackup.py b/src/pyedb/dotnet/edb_core/stackup.py index 54dc5b4db7..e2320748e4 100644 --- a/src/pyedb/dotnet/edb_core/stackup.py +++ b/src/pyedb/dotnet/edb_core/stackup.py @@ -69,8 +69,6 @@ class LayerCollection(object): - auto_refresh = True - def __init__(self, pedb, edb_object=None): self._pedb = pedb @@ -91,17 +89,21 @@ def __init__(self, pedb, edb_object=None): "multizone": self._pedb.edb_api.cell._cell.LayerCollectionMode.MultiZone, } - def update_layout(self, stackup=None): + def update_layout(self): """Set layer collection into edb. Parameters ---------- stackup """ - if stackup: - self._edb_object = stackup._edb_object self._pedb.layout.layer_collection = self._edb_object + @pyedb_function_handler() + def refresh_layer_collection(self): + """Refresh layer collection from Edb. This method is run on demand after all edit operations on stackup.""" + self._edb_object = self._pedb.edb_api.cell._cell.LayerCollection(self._pedb.layout.layer_collection) + self._lc = self._edb_object + @pyedb_function_handler def _add_layer(self, add_method, base_layer_name="", **kwargs): """Add a layer to edb. @@ -115,7 +117,13 @@ def _add_layer(self, add_method, base_layer_name="", **kwargs): if layer_clone: obj = layer_clone else: - obj = StackupLayerEdbClass(self._pedb, edb_object=None, **kwargs) + layer_type = kwargs.get("layer_type", None) + if not layer_type: + layer_type = kwargs["type"] + if layer_type in ["signal", "dielectric"]: + obj = StackupLayerEdbClass(self._pedb, edb_object=None, **kwargs) + else: + obj = LayerEdbClass(self._pedb, edb_object=None, **kwargs) method_top_bottom = None method_above_below = None if add_method == "add_layer_top": @@ -134,8 +142,7 @@ def _add_layer(self, add_method, base_layer_name="", **kwargs): obj = obj if method_top_bottom(obj._edb_object) else False elif method_above_below: obj = obj if method_above_below(obj._edb_object, base_layer_name) else False - if self.auto_refresh: - self.update_layout() + self.update_layout() return obj @pyedb_function_handler @@ -268,8 +275,7 @@ def set_layer_clone(self, layer_clone): lc.AddLayerBottom(i._edb_object) self._edb_object = lc - if self.auto_refresh: - self.update_layout() + self.update_layout() if not obj: logger.info("Layer clone was not found in stackup or non stackup layers.") @@ -278,33 +284,32 @@ def set_layer_clone(self, layer_clone): @property def stackup_layers(self): """Retrieve the dictionary of signal and dielectric layers.""" - temp = list(self._edb_object.Layers((self._pedb.edb_api.cell.layer_type_set.StackupLayerSet))) - layers = OrderedDict() - for i in temp: - name = i.GetName() - layers[name] = StackupLayerEdbClass(self._pedb, i.Clone(), name=name) - return layers + warnings.warn("Use new property :func:`layers` instead.", DeprecationWarning) + return self.layers @property def non_stackup_layers(self): """Retrieve the dictionary of signal layers.""" - temp = list(self._edb_object.Layers(self._layer_type_set_mapping["non_stackup_layer_set"])) - layers = OrderedDict() - for i in temp: - name = i.GetName() - layers[name] = LayerEdbClass(self._pedb, i, name=name) - return layers + return {name: obj for name, obj in self.all_layers.items() if not obj.is_stackup_layer} @property - def layers_by_id(self): - """Retrieve the list of layers with their ids.""" - layer_list = list(self._layer_collection.Layers(self._pedb.edb_api.cell.layer_type_set.AllLayerSet)) - temp = [] + def all_layers(self): + self.refresh_layer_collection() + layer_list = list(self._edb_object.Layers(self._pedb.edb_api.cell.layer_type_set.AllLayerSet)) + temp = dict() for i in layer_list: - obj = StackupLayerEdbClass(self._pedb, i.Clone(), name=i.GetName) - temp.append([obj.id, obj.name]) + if i.IsStackupLayer(): + obj = StackupLayerEdbClass(self._pedb, i.Clone(), name=i.GetName()) + else: + obj = LayerEdbClass(self._pedb, i.Clone(), name=i.GetName()) + temp[obj.name] = obj return temp + @property + def layers_by_id(self): + """Retrieve the list of layers with their ids.""" + return [[obj.id, name] for name, obj in self.all_layers.items()] + @property def layers(self): """Retrieve the dictionary of layers. @@ -313,22 +318,14 @@ def layers(self): ------- Dict[str, :class:`pyedb.dotnet.edb_core.edb_data.layer_data.LayerEdbClass`] """ - _lays = OrderedDict() - layer_list = list(self._edb_object.Layers(self._pedb.edb_api.cell.layer_type_set.AllLayerSet)) - for l in layer_list: - name = l.GetName() - if not l.IsStackupLayer(): - _lays[name] = LayerEdbClass(self._pedb, l, name=name) - else: - _lays[name] = StackupLayerEdbClass(self._pedb, l, name=name) - return _lays + return {name: obj for name, obj in self.all_layers.items() if obj.is_stackup_layer} class Stackup(LayerCollection): """Manages EDB methods for stackup accessible from `Edb.stackup` property.""" def __getitem__(self, item): - return self.layers[item] + return self.all_layers[item] def __init__(self, pedb, edb_object=None): super().__init__(pedb, edb_object) @@ -576,12 +573,6 @@ def create_symmetric_stackup( ) return True - @pyedb_function_handler() - def refresh_layer_collection(self): - """Refresh layer collection from Edb. This method is run on demand after all edit operations on stackup.""" - self._lc = self._pedb.edb_api.cell._cell.LayerCollection(self._pedb.layout.layer_collection) - self._edb_object = self._lc - @property def _layer_collection(self): """Copy of EDB layer collection. @@ -591,8 +582,7 @@ def _layer_collection(self): :class:`Ansys.Ansoft.Edb.Cell.LayerCollection` Collection of layers. """ - if not self._lc: - self.refresh_layer_collection() + self.refresh_layer_collection() return self._lc @property @@ -620,7 +610,7 @@ def mode(self, value): self._layer_collection.SetMode(mode.Overlapping) elif value == 2 or value == mode.MultiZone or value == "MultiZone": self._layer_collection.SetMode(mode.MultiZone) - self._pedb.layout.layer_collection = self._layer_collection + self.update_layout() @property def stackup_mode(self): @@ -744,9 +734,8 @@ def _set_layout_stackup(self, layer_clone, operation, base_layer=None, method=1) _lc.AddStackupLayerAtElevation(layer_clone) elif operation == "non_stackup": _lc.AddLayerBottom(layer_clone) - if self.auto_refresh: - self._pedb.layout.layer_collection = _lc - self.refresh_layer_collection() + self._pedb.layout.layer_collection = _lc + self.refresh_layer_collection() return True @pyedb_function_handler() @@ -814,17 +803,7 @@ def add_outline_layer(self, outline_name="Outline"): bool "True" if successful, ``False`` if failed. """ - outlineLayer = self._pedb.edb_api.cell.layer.FindByName(self._pedb.layout.layer_collection, outline_name) - if outlineLayer.IsNull(): - return self.add_layer( - outline_name, - layer_type="outline", - material="", - fillMaterial="", - thickness="", - ) - else: - return False + return self.add_document_layer(name="Outline", layer_type="outline") # TODO: Update optional argument material into material_name and fillMaterial into fill_material_name @pyedb_function_handler() diff --git a/tests/legacy/system/test_edb_config_json.py b/tests/legacy/system/test_edb_config_json.py index dc2e3f6cfa..fa5b16a795 100644 --- a/tests/legacy/system/test_edb_config_json.py +++ b/tests/legacy/system/test_edb_config_json.py @@ -292,8 +292,74 @@ def test_12_setup_siwave_dc(self, edb_examples): edbapp.close() def test_13_stackup(self, edb_examples): + data = { + "stackup": { + "layers": [ + { + "fill_material": "Solder Resist", + "material": "copper", + "name": "1_Top", + "thickness": "0.5mm", + "type": "signal", + }, + { + "fill_material": "Megtron4", + "material": "copper", + "name": "Inner1", + "thickness": "0.017mm", + "type": "signal", + }, + {"material": "Megtron4", "name": "DE2", "thickness": "0.088mm", "type": "dielectric"}, + {"material": "Megtron4", "name": "DE3", "thickness": "0.1mm", "type": "dielectric"}, + { + "fill_material": "Megtron4", + "material": "copper", + "name": "Inner2", + "thickness": "0.017mm", + "type": "signal", + }, + { + "fill_material": "Megtron4", + "material": "copper", + "name": "Inner3", + "thickness": "0.017mm", + "type": "signal", + }, + { + "fill_material": "Megtron4", + "material": "copper", + "name": "Inner4", + "thickness": "0.017mm", + "type": "signal", + }, + { + "fill_material": "Megtron4", + "material": "copper", + "name": "Inner5", + "thickness": "0.017mm", + "type": "signal", + }, + { + "fill_material": "Megtron4", + "material": "copper", + "name": "Inner6", + "thickness": "0.017mm", + "type": "signal", + }, + { + "fill_material": "Solder Resist", + "material": "copper", + "name": "16_Bottom", + "thickness": "0.035mm", + "type": "signal", + }, + ] + } + } edbapp = edb_examples.get_si_verse() - assert edbapp.configuration.load(str(self.local_input_folder / "stackup.json"), apply_file=True) + assert edbapp.configuration.load(data, apply_file=True) + assert list(edbapp.stackup.layers.keys())[:4] == ["1_Top", "Inner1", "DE2", "DE3"] + assert edbapp.stackup.layers["1_Top"].thickness == 0.0005 edbapp.close() def test_14_setup_siwave_syz(self, edb_examples): diff --git a/tests/legacy/system/test_edb_stackup.py b/tests/legacy/system/test_edb_stackup.py index f6ef75e822..0fe61bd2b8 100644 --- a/tests/legacy/system/test_edb_stackup.py +++ b/tests/legacy/system/test_edb_stackup.py @@ -57,8 +57,8 @@ def test_stackup_add_outline(self): edbapp = Edb( edbversion=desktop_version, ) - assert edbapp.stackup.add_outline_layer("Outline1") - assert not edbapp.stackup.add_outline_layer("Outline1") + assert edbapp.stackup.add_outline_layer() + assert "Outline" in edbapp.stackup.non_stackup_layers edbapp.stackup.add_layer("1_Top") assert edbapp.stackup.layers["1_Top"].thickness == 3.5e-05 edbapp.stackup.layers["1_Top"].thickness = 4e-5