Skip to content

Commit

Permalink
Created Virtual Compliance example (#4322)
Browse files Browse the repository at this point in the history
Co-authored-by: maxcapodi78 <Shark78>
Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com>
Co-authored-by: Samuel Lopez <85613111+Samuelopez-ansys@users.noreply.github.com>
Co-authored-by: Samuelopez-ansys <samuel.lopez@ansys.com>
  • Loading branch information
4 people authored Mar 7, 2024
1 parent 8532671 commit cab62c5
Show file tree
Hide file tree
Showing 18 changed files with 690 additions and 23 deletions.
2 changes: 2 additions & 0 deletions _unittest/test_21_Circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ def test_42_create_wire(self):
self.aedtapp.insert_design("CreateWireTest")
myind = self.aedtapp.modeler.schematic.create_inductor("L101", location=[0.02, 0.0])
myres = self.aedtapp.modeler.schematic.create_resistor("R101", location=[0.0, 0.0])
myres2 = self.aedtapp.modeler.components.get_component(myres.composed_name)
self.aedtapp.modeler.schematic.create_wire(
[myind.pins[0].location, myres.pins[1].location], wire_name="wire_name_test"
)
Expand Down Expand Up @@ -809,6 +810,7 @@ def test_45_create_circuit_from_multizone_layout(self, add_edb):
self.aedtapp.insert_design("test_45")
self.aedtapp.connect_circuit_models_from_multi_zone_cutout(project_connexions, edb_zones, defined_ports)
assert [mod for mod in list(self.aedtapp.modeler.schematic.components.values()) if "PagePort" in mod.name]
assert self.aedtapp.remove_all_unused_definitions()

def test_46_create_vpwl(self):

Expand Down
Binary file added doc/source/_static/virtual_compliance_class.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/source/_static/virtual_compliance_configs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/source/_static/virtual_compliance_eye.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/source/_static/virtual_compliance_usage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
304 changes: 304 additions & 0 deletions examples/07-Circuit/Virtual_Compliance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
"""
Circuit: PCIE virtual compliance
--------------------------------
This example shows how to generate a compliance report in PyAEDT using
the ``VirtualCompliance`` class.
"""

###############################################################################
# Perform required imports
# ~~~~~~~~~~~~~~~~~~~~~~~~
# Perform required imports and set paths.

import os.path
import pyaedt
from pyaedt.generic.compliance import VirtualCompliance

##########################################################
# Set AEDT version
# ~~~~~~~~~~~~~~~~
# Set AEDT version.

aedt_version = "2024.1"

###############################################################################
# Set non-graphical mode
# ~~~~~~~~~~~~~~~~~~~~~~
# Set non-graphical mode.
# You can set ``non_graphical`` either to ``True`` or ``False``.
# The Boolean parameter ``new_thread`` defines whether to create a new instance
# of AEDT or try to connect to an existing instance of it.

non_graphical = True
new_thread = True

###############################################################################
# Download example files
# ~~~~~~~~~~~~~~~~~~~~~~
# Download the project and files needed to run the example.
workdir = pyaedt.downloads.download_file('pcie_compliance')

projectdir = os.path.join(workdir, "project")

###############################################################################
# Launch AEDT
# ~~~~~~~~~~~
# Launch AEDT.

d = pyaedt.Desktop(aedt_version, new_desktop_session=new_thread, non_graphical=non_graphical)

###############################################################################
# Open and solve layout
# ~~~~~~~~~~~~~~~~~~~~~
# Open the HFSS 3D Layout project and analyze it using the SIwave solver.
# Before solving, this code ensures that the model is solved from DC to 70GHz and that
# causality and passivity are enforced.

h3d = pyaedt.Hfss3dLayout(os.path.join(projectdir, "PCIE_GEN5_only_layout.aedtz"), specified_version=241)
h3d.remove_all_unused_definitions()
h3d.edit_cosim_options(simulate_missing_solution=False)
h3d.setups[0].sweeps[0].props["EnforcePassivity"] = True
h3d.setups[0].sweeps[0].props["Sweeps"]["Data"] = 'LIN 0MHz 70GHz 0.1GHz'
h3d.setups[0].sweeps[0].props["EnforceCausality"] = True
h3d.setups[0].sweeps[0].update()
h3d.analyze()
touchstone_path = h3d.export_touchstone()

###############################################################################
# Create LNA project
# ~~~~~~~~~~~~~~~~~~
# Use the LNA setup to retrieve Touchstone files
# and generate frequency domain reports.

cir = pyaedt.Circuit(projectname=h3d.project_name, designname="Touchstone")

###############################################################################
# Add dynamic link object
# ~~~~~~~~~~~~~~~~~~~~~~~
# Add the layout project as a dynamic link object
# so that if modifications are made, the circuit
# is always linked to the updated version of the results.

sub = cir.modeler.components.add_subcircuit_3dlayout("main_cutout")

###############################################################################
# Add ports and differential pairs
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Add a port for each layout port and rename it for circuit compatibility.
# Then, create differential pairs, which are useful for generating differential S-parameters.

ports = []
for pin in sub.pins:
ports.append(cir.modeler.components.create_interface_port(name=pin.name.replace(".", "_"), location=pin.location))

for pin in ports:
pin_name = pin.name.split("_")
if pin_name[-1] == "P":
component = pin_name[0]
suffix = pin_name[-2] if "X" in pin_name[-2] else pin_name[-3]
for neg_pin in ports:
neg_pin_name = neg_pin.name.split("_")
suffix_neg = neg_pin_name[-2] if "X" in neg_pin_name[-2] else neg_pin_name[-3]
if neg_pin_name[0] == component and suffix_neg == suffix and neg_pin.name[-1] == "N":
cir.set_differential_pair(
positive_terminal=pin.name,
negative_terminal=neg_pin.name,
common_name=f"COMMON_{component}_{suffix}",
diff_name=f"{component}_{suffix}",
common_ref_z=25,
diff_ref_z=100,
active=True,
)
break

###############################################################################
# Create setup, analyze, and plot
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create a setup and then analyze and plot the data.

setup1 = cir.create_setup()
setup1.props["SweepDefinition"]["Data"] = "LINC 0GHz 70GHz 1001"

cir.analyze()

###############################################################################
# Create TDR project
# ~~~~~~~~~~~~~~~~~~
# Create a TDR project to compute transient simulation and retrieve
# the TDR measurement on a differential pair.
# The original circuit schematic is duplicated and modified to achieve this target.

cir.duplicate_design("TDR")
cir.set_active_design("TDR")

###############################################################################
# Replace ports with TDR probe
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Delete the ports on the selected differential pair.
# Place and connect a differential TDR probe to the pins of the layout object.

cir.modeler.components.delete_component("X1_A2_PCIe_Gen4_RX0_P")
cir.modeler.components.delete_component("IPort@X1_A3_PCIe_Gen4_RX0_N")
sub = cir.modeler.components.get_component("main_cutout1")

dif_end = cir.modeler.components.components_catalog["TDR_Differential_Ended"]

new_tdr_comp = dif_end.place("Tdr_probe", [-0.05, 0.01], angle=-90)
p_pin = [i for i in sub.pins if i.name.replace(".", "_") == "X1_A2_PCIe_Gen4_RX0_P"][0]
n_pin = [i for i in sub.pins if i.name.replace(".", "_") == "X1_A3_PCIe_Gen4_RX0_N"][0]
new_tdr_comp.pins[0].connect_to_component(p_pin)
new_tdr_comp.pins[1].connect_to_component(n_pin)
new_tdr_comp.parameters["Pulse_repetition"] = "2ms"
new_tdr_comp.parameters["Rise_time"] = "35ps"

###############################################################################
# Create setup, analyze, and plot
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# First, delete the LNA setup. Next, create a
# transient setup and then analyze and plot the data.

cir.delete_setup(cir.setups[0].name)
setup2 = cir.create_setup(setupname="MyTransient", setuptype=cir.SETUPS.NexximTransient)
setup2.props["TransientData"] = ["0.01ns", "10ns"]
cir.oanalysis.AddAnalysisOptions(["NAME:DataBlock", "DataBlockID:=", 8, "Name:=", "Nexxim Options",
["NAME:ModifiedOptions", "ts_convolution:=", True, ]])
setup2.props["OptionName"] = "Nexxim Options"
tdr_probe_name = f'O(A{new_tdr_comp.id}:zdiff)'
cir.analyze()

###############################################################################
# Create AMI project
# ~~~~~~~~~~~~~~~~~~
# Create an Ibis AMI project to compute an eye diagram simulation and retrieve
# eye mask violations.

cir = pyaedt.Circuit(projectname=h3d.project_name, designname="AMI")
sub = cir.modeler.components.create_touchstone_component(touchstone_path)

###############################################################################
# Replace ports with Ibis models
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Place and connect differential TX and RX Ibis models to the pins of the layout object.

p_pin1 = [i for i in sub.pins if i.name.replace(".", "_") == "U1_AM25_PCIe_Gen4_TX0_CAP_P"][0]
n_pin1 = [i for i in sub.pins if i.name.replace(".", "_") == "U1_AL25_PCIe_Gen4_TX0_CAP_N"][0]
p_pin2 = [i for i in sub.pins if i.name.replace(".", "_") == "X1_B2_PCIe_Gen4_TX0_P"][0]
n_pin2 = [i for i in sub.pins if i.name.replace(".", "_") == "X1_B3_PCIe_Gen4_TX0_N"][0]

ibis = cir.get_ibis_model_from_file(os.path.join(projectdir, "models", "pcieg5_32gt.ibs"), is_ami=True)
tx = ibis.components["Spec_Model"].pins["1p_Spec_Model_pcieg5_32gt_diff"].insert(-0.05, 0.01)
rx = ibis.components["Spec_Model"].pins["2p_Spec_Model_pcieg5_32gt_diff"].insert(0.05, 0.01, 180)

tx_eye_name = tx.parameters["probe_name"]

tx.pins[0].connect_to_component(p_pin1)
tx.pins[1].connect_to_component(n_pin1)
rx.pins[0].connect_to_component(p_pin2)
rx.pins[1].connect_to_component(n_pin2)
tx.parameters["UIorBPSValue"] = "31.25ps"
tx.parameters["BitPattern"] = "random_bit_count=2.5e3 random_seed=1"

###############################################################################
# Create setup, analyze, and plot
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# First delete the LNA setup. Next, create a
# transient setup and then analyze and plot the data.

setup_ami = cir.create_setup("AMI", "NexximAMI")
cir.oanalysis.AddAnalysisOptions(["NAME:DataBlock", "DataBlockID:=", 8, "Name:=", "Nexxim Options",
["NAME:ModifiedOptions", "ts_convolution:=", True, ]])
setup_ami.props["OptionName"] = "Nexxim Options"

setup_ami.props["DataBlockSize"] = 1000
setup_ami.analyze()
cir.save_project()

###############################################################################
# Create virtual compliance report
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize the ``VirtualCompliance`` class
# and set up the main project information needed to generate the report.
#
#
# .. image:: ../../_static/virtual_compliance_class.png
# :width: 400
# :alt: Virtual compliance class description.
#
#
# .. image:: ../../_static/virtual_compliance_configs.png
# :width: 400
# :alt: Virtual compliance configuration files hierarchy.
#
#

template = os.path.join(workdir, "pcie_gen5_templates", "main.json")

v = VirtualCompliance(cir.desktop_class, str(template))

###############################################################################
# Customize project and design
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define the path to the project file and the
# design names to be used in each report generation.
#
#
# .. image:: ../../_static/virtual_compliance_usage.png
# :width: 400
# :alt: Virtual compliance configuration usage example.
#
#

v.project_file = cir.project_file
v.reports["insertion losses"].design_name = "Touchstone"
v.reports["return losses"].design_name = "Touchstone"
v.reports["common mode return losses"].design_name = "Touchstone"
v.reports["tdr from circuit"].design_name = "TDR"
v.reports["eye1"].design_name = "AMI"
v.reports["eye3"].design_name = "AMI"
v.parameters["erl"].design_name = "Touchstone"
v.specs_folder = os.path.join(workdir, 'readme_pictures')

###############################################################################
# Define trace names
# ~~~~~~~~~~~~~~~~~~
# Change the trace name with projects and users.
# Reuse the compliance template and update traces accordingly.

v.reports["insertion losses"].traces = [
"dB(S(U1_RX0,X1_RX0))",
"dB(S(U1_RX1,X1_RX1))",
"dB(S(U1_RX3,X1_RX3))"
]

v.reports["eye1"].traces = [tx_eye_name]
v.reports["eye3"].traces = [tx_eye_name]
v.reports["tdr from circuit"].traces = [tdr_probe_name]
v.parameters["erl"].trace_pins = [
["X1_A5_PCIe_Gen4_RX1_P", "X1_A6_PCIe_Gen4_RX1_N", "U1_AR25_PCIe_Gen4_RX1_P", "U1_AP25_PCIe_Gen4_RX1_N"],
[7, 8, 18, 17]]

###############################################################################
# Generate PDF report
# ~~~~~~~~~~~~~~~~~~~~
# Generate the reports and produce a PDF report.
#
#
# .. image:: ../../_static/virtual_compliance_scattering1.png
# :width: 400
# :alt: Insertion loss output.
#
#
# .. image:: ../../_static/virtual_compliance_scattering2.png
# :width: 400
# :alt: Return loss output.
#
#
# .. image:: ../../_static/virtual_compliance_eye.png
# :width: 400
# :alt: Eye diagram example.
#
#

v.create_compliance_report()

d.release_desktop(True, True)
27 changes: 27 additions & 0 deletions pyaedt/application/AnalysisNexxim.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,33 @@ def __init__(
self._modeler = self.modeler
self._post = self.post

@pyaedt_function_handler()
def delete_setup(self, setupname):
"""Delete a setup.
Parameters
----------
setupname : str
Name of the setup.
Returns
-------
bool
``True`` when successful, ``False`` when failed.
References
----------
>>> oModule.RemoveSimSetup
"""
if setupname in self.existing_analysis_setups:
self.oanalysis.RemoveSimSetup([setupname])
for s in self.setups:
if s.name == setupname:
self.setups.remove(s)
return True
return False

@pyaedt_function_handler()
def push_down(self, component_name):
"""Push-down to the child component and reinitialize the Circuit object.
Expand Down
14 changes: 13 additions & 1 deletion pyaedt/application/Design.py
Original file line number Diff line number Diff line change
Expand Up @@ -1227,9 +1227,21 @@ def desktop_install_dir(self):
"""
return self._desktop_install_dir

@pyaedt_function_handler()
def remove_all_unused_definitions(self):
"""Remove all unused definitions in the project.
Returns
-------
bool
``True`` when successful, ``False`` when failed.
"""
self.oproject.RemoveAllUnusedDefinitions()
return True

@pyaedt_function_handler()
def get_oo_name(self, aedt_object, object_name=None):
"""Return the Object Oriented AEDT Properties names.
"""Return the object-oriented AEDT property names.
Parameters
----------
Expand Down
Loading

0 comments on commit cab62c5

Please sign in to comment.