pyibis-ami 7.3.1__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.
@@ -0,0 +1,84 @@
1
+ """
2
+ IBIS-AMI reserved parameter names, as a Python ``dataclass``.
3
+
4
+ Original author: David Banas <capn.freako@gmail.com>
5
+
6
+ Original date: March 17, 2025
7
+
8
+ Copyright (c) 2025 David Banas; all rights reserved World wide.
9
+ """
10
+
11
+ from dataclasses import dataclass
12
+
13
+
14
+ @dataclass
15
+ class AmiReservedParameterName():
16
+ "IBIS-AMI Reserved Parameter Name"
17
+
18
+ pname: str
19
+
20
+ def __post_init__(self):
21
+ "Validate parameter name."
22
+
23
+ if self.pname not in RESERVED_PARAM_NAMES:
24
+ raise ValueError(f"Parameter name: {self.pname}, is not an IBIS-AMI reserved parameter name!")
25
+
26
+
27
+ RESERVED_PARAM_NAMES = [
28
+ "AMI_Version",
29
+ "Init_Returns_Impulse",
30
+ "GetWave_Exists",
31
+ "Use_Init_Output",
32
+ "Max_Init_Aggressors",
33
+ "Ignore_Bits",
34
+ "Resolve_Exists",
35
+ "Model_Name",
36
+ "Special_Param_Names",
37
+ "Component_Name",
38
+ "Signal_Name",
39
+ "Rx_Decision_Time",
40
+ "DC_Offset",
41
+ "Rx_Use_Clock_Input",
42
+ "Supporting_Files",
43
+ "DLL_Path",
44
+ "DLL_ID",
45
+ "Tx_Jitter",
46
+ "Tx_DCD",
47
+ "Tx_Rj",
48
+ "Tx_Dj",
49
+ "Tx_Sj",
50
+ "Tx_Sj_Frequency",
51
+ "Rx_DCD",
52
+ "Rx_Rj",
53
+ "Rx_Dj",
54
+ "Rx_Sj",
55
+ "Rx_Clock_PDF",
56
+ "Rx_Clock_Recovery_Mean",
57
+ "Rx_Clock_Recovery_Rj",
58
+ "Rx_Clock_Recovery_Dj",
59
+ "Rx_Clock_Recovery_Sj",
60
+ "Rx_Clock_Recovery_DCD",
61
+ "Rx_Receiver_Sensitivity",
62
+ "Rx_Noise",
63
+ "Rx_GaussianNoise",
64
+ "Rx_UniformNoise",
65
+ "Modulation",
66
+ "PAM4_Mapping",
67
+ "PAM4_UpperThreshold",
68
+ "PAM4_CenterThreshold",
69
+ "PAM4_LowerThreshold",
70
+ "PAM4_UpperEyeOffset",
71
+ "PAM4_CenterEyeOffset",
72
+ "PAM4_LowerEyeOffset",
73
+ "Repeater_Type",
74
+ "BCI_Protocol",
75
+ "BCI_ID",
76
+ "BCI_State",
77
+ "BCI_Message_Interval_UI",
78
+ "BCI_Training_UI",
79
+ "BCI_Training_Mode",
80
+ "Ts4file",
81
+ "Tx_V",
82
+ "Tx_R",
83
+ "Rx_R",
84
+ ]
pyibisami/common.py ADDED
@@ -0,0 +1,42 @@
1
+ """
2
+ Definitions common to all PyIBIS-AMI modules.
3
+
4
+ Original author: David Banas <capn.freako@gmail.com>
5
+
6
+ Original date: May 15, 2024
7
+
8
+ Copyright (c) 2024 David Banas; all rights reserved World wide.
9
+ """
10
+
11
+ from typing import Any, TypeAlias, TypeVar
12
+ import numpy.typing as npt # type: ignore
13
+ from scipy.linalg import convolution_matrix, lstsq
14
+
15
+ Real = TypeVar("Real", float, float)
16
+ Comp = TypeVar("Comp", complex, complex)
17
+ Rvec: TypeAlias = npt.NDArray["Real"]
18
+ Cvec: TypeAlias = npt.NDArray["Comp"]
19
+
20
+ PI: float = 3.141592653589793238462643383279502884
21
+ TWOPI: float = 2.0 * PI
22
+
23
+ # TestConfig: TypeAlias = tuple[str, tuple[dict[str, Any], dict[str, Any]]]
24
+ # TestSweep: TypeAlias = tuple[str, str, list[TestConfig]]
25
+ TestConfig = tuple[str, tuple[dict[str, Any], dict[str, Any]]]
26
+ TestSweep = tuple[str, str, list[TestConfig]]
27
+
28
+
29
+ def deconv_same(y: Rvec, x: Rvec) -> Rvec:
30
+ """
31
+ Deconvolve input from output, to recover filter response, for same length I/O.
32
+
33
+ Args:
34
+ y: output signal
35
+ x: input signal
36
+
37
+ Returns:
38
+ h: filter impulse response.
39
+ """
40
+ A = convolution_matrix(x, len(y), "same")
41
+ h, _, _, _ = lstsq(A, y)
42
+ return h
File without changes
pyibisami/ibis/file.py ADDED
@@ -0,0 +1,326 @@
1
+ """A class for encapsulating IBIS model files.
2
+
3
+ Original Author: David Banas <capn.freako@gmail.com>
4
+
5
+ Original Date: November 1, 2019
6
+
7
+ For information regarding the IBIS modeling standard, visit:
8
+ https://ibis.org/
9
+
10
+ **Note:** The ``IBISModel`` class, defined here, needs to be kept separate from the
11
+ other IBIS-related classes, defined in the ``model`` module, in order to
12
+ avoid circular imports.
13
+
14
+ Copyright (c) 2019 by David Banas; All rights reserved World wide.
15
+ """
16
+
17
+ import platform
18
+ from datetime import datetime
19
+
20
+ from traits.api import (
21
+ Any,
22
+ Dict,
23
+ Enum,
24
+ Float,
25
+ HasTraits,
26
+ List,
27
+ Property,
28
+ String,
29
+ Trait,
30
+ cached_property,
31
+ )
32
+ from traitsui.api import HGroup, Item, ModalButtons, VGroup, View, spring
33
+ from traitsui.message import message
34
+
35
+ from pyibisami.ibis.parser import parse_ibis_file
36
+
37
+
38
+ class IBISModel(HasTraits): # pylint: disable=too-many-instance-attributes
39
+ """HasTraits subclass for wrapping and interacting with an IBIS model.
40
+
41
+ This class can be configured to present a customized GUI to the user
42
+ for interacting with a particular IBIS model (i.e. - selecting components,
43
+ pins, and models).
44
+
45
+ The intended use model is as follows:
46
+
47
+ 1. Instantiate this class only once per IBIS model file.
48
+ When instantiating, provide the unprocessed contents of the IBIS
49
+ file, as a single string. This class will take care of getting
50
+ that string parsed properly, and report any errors or warnings
51
+ it encounters, in its ``ibis_parsing_errors`` property.
52
+
53
+ 2. When you want to let the user select a particular component/pin/model,
54
+ call the newly created instance, as if it were a function, passing
55
+ no arguments.
56
+ The instance will then present a GUI to the user,
57
+ allowing him to select a particular component/pin/model, which may then
58
+ be retrieved, via the ``model`` property.
59
+ The latest user selections will be remembered,
60
+ as long as the instance remains in scope.
61
+
62
+ Any errors or warnings encountered while parsing are available, in
63
+ the ``ibis_parsing_errors`` property.
64
+
65
+ The complete dictionary containing all parsed models may be retrieved,
66
+ via the ``model_dict`` property.
67
+ """
68
+
69
+ _log = ""
70
+
71
+ pin_ = Property(Any, depends_on=["pin"])
72
+ pin_rlcs = Property(Dict, depends_on=["pin"])
73
+ model = Property(Any, depends_on=["mod"])
74
+ pins = List # Always holds the list of valid pin selections, given a component selection.
75
+ models = List # Always holds the list of valid model selections, given a pin selection.
76
+
77
+ def get_models(self, mname):
78
+ """Return the list of models associated with a particular name."""
79
+ model_dict = self._model_dict
80
+ if "model_selectors" in model_dict and mname in model_dict["model_selectors"]:
81
+ return list(map(lambda pr: pr[0], model_dict["model_selectors"][mname]))
82
+ return [mname]
83
+
84
+ def get_pins(self):
85
+ """Get the list of appropriate pins, given our type (i.e. - Tx or Rx)."""
86
+ pins = self.comp_.pins
87
+
88
+ def pin_ok(pname):
89
+ (mname, _) = pins[pname]
90
+ mods = self.get_models(mname)
91
+ mod = self._models[mods[0]]
92
+ mod_type = mod.mtype.lower()
93
+ tx_ok = mod_type in ("output", "i/o")
94
+ if self._is_tx:
95
+ return tx_ok
96
+ return not tx_ok
97
+
98
+ return list(filter(pin_ok, list(pins)))
99
+
100
+ def __init__(self, ibis_file_name, is_tx, debug=False, gui=True):
101
+ """
102
+ Args:
103
+ ibis_file_name (str): The name of the IBIS file.
104
+ is_tx (bool): True if this is a Tx model.
105
+
106
+ Keyword Args:
107
+ debug (bool): Output debugging info to console when true.
108
+ Default = False
109
+ gui (bool): Set to `False` for command line and/or script usage.
110
+ Default = True.
111
+ """
112
+
113
+ # Super-class initialization is ABSOLUTELY NECESSARY, in order
114
+ # to get all the Traits/UI machinery setup correctly.
115
+ super().__init__()
116
+
117
+ self.debug = debug
118
+ self.GUI = gui
119
+ if debug:
120
+ self.log("pyibisami.ibis_file.IBISModel initializing in debug mode...")
121
+ else:
122
+ self.log("pyibisami.ibis_file.IBISModel initializing in non-debug mode...")
123
+
124
+ # Parse the IBIS file contents, storing any errors or warnings, and validate it.
125
+ with open(ibis_file_name, "r", encoding="utf-8") as file:
126
+ ibis_file_contents_str = file.read()
127
+ err_str, model_dict = parse_ibis_file(ibis_file_contents_str, debug=debug)
128
+ self.log("IBIS parsing errors/warnings:\n" + err_str)
129
+ if "components" not in model_dict or not model_dict["components"]:
130
+ print(f":\n{model_dict}", flush=True)
131
+ raise ValueError("This IBIS model has no components!")
132
+ components = model_dict["components"]
133
+ if "models" not in model_dict or not model_dict["models"]:
134
+ raise ValueError("This IBIS model has no models!")
135
+ models = model_dict["models"]
136
+ self._model_dict = model_dict
137
+ self._models = models
138
+ self._is_tx = is_tx
139
+
140
+ # Add Traits for various attributes found in the IBIS file.
141
+ self.add_trait("comp", Trait(list(components)[0], components)) # Doesn't need a custom mapper, because
142
+ self.pins = self.get_pins() # the thing above it (file) can't change.
143
+ self.add_trait("pin", Enum(self.pins[0], values="pins"))
144
+ (mname, _) = self.pin_
145
+ self.models = self.get_models(mname)
146
+ self.add_trait("mod", Enum(self.models[0], values="models"))
147
+ self.add_trait("ibis_ver", Float(model_dict["ibis_ver"]))
148
+ self.add_trait("file_name", String(model_dict["file_name"]))
149
+ self.add_trait("file_rev", String(model_dict["file_rev"]))
150
+ if "date" in model_dict:
151
+ self.add_trait("date", String(model_dict["date"]))
152
+ else:
153
+ self.add_trait("date", String("(n/a)"))
154
+
155
+ self._ibis_parsing_errors = err_str
156
+ self._os_type = platform.system() # These 2 are used, to choose
157
+ self._os_bits = platform.architecture()[0] # the correct AMI executable.
158
+
159
+ self._comp_changed(list(components)[0]) # Wasn't being called automatically.
160
+ self._pin_changed(self.pins[0]) # Wasn't being called automatically.
161
+
162
+ self.log("Done.")
163
+
164
+ def __str__(self):
165
+ return f"IBIS Model '{self._model_dict['file_name']}'"
166
+
167
+ def info(self):
168
+ """Basic information about the IBIS model."""
169
+ res = ""
170
+ try:
171
+ for k in ["ibis_ver", "file_name", "file_rev"]:
172
+ res += k + ":\t" + str(self._model_dict[k]) + "\n"
173
+ except Exception as err:
174
+ print(f"{err}")
175
+ print(self._model_dict)
176
+ raise
177
+ res += "date" + ":\t\t" + str(self._model_dict["date"]) + "\n"
178
+ res += "\nComponents:"
179
+ res += "\n=========="
180
+ for c in list(self._model_dict["components"]):
181
+ res += "\n" + c + ":\n" + "---\n" + str(self._model_dict["components"][c]) + "\n"
182
+ res += "\nModel Selectors:"
183
+ res += "\n===============\n"
184
+ for s in list(self._model_dict["model_selectors"]):
185
+ res += f"{s}\n"
186
+ res += "\nModels:"
187
+ res += "\n======"
188
+ for m in list(self._model_dict["models"]):
189
+ res += "\n" + m + ":\n" + "---\n" + str(self._model_dict["models"][m])
190
+ return res
191
+
192
+ def __call__(self):
193
+ """Present a customized GUI to the user, for model selection, etc."""
194
+ # self.configure_traits(kind='modal') # Waiting for Enthought/Traits PR1841 to be accepted.
195
+ self.configure_traits()
196
+
197
+ # Logger & Pop-up
198
+ def log(self, msg, alert=False):
199
+ """Log a message to the console and, optionally, to terminal and/or
200
+ pop-up dialog."""
201
+ _msg = msg.strip()
202
+ txt = f"\n[{datetime.now()}]: IBISModel: {_msg}\n"
203
+ self._log += txt
204
+ if self.debug:
205
+ print(txt, flush=True)
206
+ if alert and self.GUI:
207
+ message(_msg, "PyAMI Alert")
208
+
209
+ def default_traits_view(self):
210
+ "Default Traits/UI view definition."
211
+ view = View(
212
+ VGroup(
213
+ HGroup(
214
+ Item("file_name", label="File name", style="readonly"),
215
+ spring,
216
+ Item("file_rev", label="rev", style="readonly"),
217
+ ),
218
+ HGroup(
219
+ Item("ibis_ver", label="IBIS ver", style="readonly"),
220
+ spring,
221
+ Item("date", label="Date", style="readonly"),
222
+ ),
223
+ HGroup(
224
+ Item("comp", label="Component"),
225
+ Item("pin", label="Pin"),
226
+ Item("mod", label="Model"),
227
+ ),
228
+ ),
229
+ resizable=False,
230
+ buttons=ModalButtons,
231
+ title="PyBERT IBIS Model Selector",
232
+ id="pybert_ibis_model_selector",
233
+ )
234
+ return view
235
+
236
+ @cached_property
237
+ def _get_pin_(self):
238
+ return self.comp_.pins[self.pin]
239
+
240
+ @cached_property
241
+ def _get_pin_rlcs(self):
242
+ (_, pin_rlcs) = self.pin_
243
+ return pin_rlcs
244
+
245
+ @cached_property
246
+ def _get_model(self):
247
+ return self._models[self.mod]
248
+
249
+ @property
250
+ def ibis_parsing_errors(self):
251
+ """Any errors or warnings encountered, while parsing the IBIS file
252
+ contents."""
253
+ return self._ibis_parsing_errors
254
+
255
+ @property
256
+ def log_txt(self):
257
+ """The complete log since instantiation."""
258
+ return self._log
259
+
260
+ @property
261
+ def model_dict(self):
262
+ "Dictionary of all model keywords."
263
+ return self._model_dict
264
+
265
+ @property
266
+ def dll_file(self):
267
+ "Shared object file."
268
+ return self._dll_file
269
+
270
+ @property
271
+ def ami_file(self):
272
+ "AMI file."
273
+ return self._ami_file
274
+
275
+ def _comp_changed(self, new_value):
276
+ del new_value
277
+ self.pins = self.get_pins()
278
+ self.pin = self.pins[0]
279
+
280
+ def _pin_changed(self, new_value):
281
+ # (mname, rlc_dict) = self.pin_ # Doesn't work. Because ``pin_`` is a cached property and hasn't yet been marked "dirty"?
282
+ (mname, _) = self.comp_.pins[new_value]
283
+ self.models = self.get_models(mname)
284
+ self.mod = self.models[0]
285
+
286
+ def _mod_changed(self, new_value):
287
+ model = self._models[new_value]
288
+ os_type = self._os_type
289
+ os_bits = self._os_bits
290
+ fnames = []
291
+ dll_file = ""
292
+ ami_file = ""
293
+ if os_type.lower() == "windows":
294
+ if os_bits == "64bit":
295
+ fnames = model._exec64Wins # pylint: disable=protected-access
296
+ else:
297
+ fnames = model._exec32Wins # pylint: disable=protected-access
298
+ else:
299
+ if os_bits == "64bit":
300
+ fnames = model._exec64Lins # pylint: disable=protected-access
301
+ else:
302
+ fnames = model._exec32Lins # pylint: disable=protected-access
303
+ if fnames:
304
+ dll_file = fnames[0]
305
+ ami_file = fnames[1]
306
+ self.log(
307
+ "There was an [Algorithmic Model] keyword in this model.\n \
308
+ If you wish to use the AMI model associated with this IBIS model,\n \
309
+ please, go the 'Equalization' tab and enable it now.",
310
+ alert=True,
311
+ )
312
+ elif "algorithmic_model" in model._subDict: # pylint: disable=protected-access
313
+ self.log(
314
+ f"There was an [Algorithmic Model] keyword for this model,\n \
315
+ but no executable for your platform: {os_type}-{os_bits};\n \
316
+ PyBERT native equalization modeling being used instead.",
317
+ alert=True,
318
+ )
319
+ else:
320
+ self.log(
321
+ "There was no [Algorithmic Model] keyword for this model;\n \
322
+ PyBERT native equalization modeling being used instead.",
323
+ alert=True,
324
+ )
325
+ self._dll_file = dll_file # pylint: disable=attribute-defined-outside-init
326
+ self._ami_file = ami_file # pylint: disable=attribute-defined-outside-init