pyibis-ami 7.2.3__py3-none-any.whl → 7.2.4__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.
pyibisami/ibis/model.py CHANGED
@@ -1,399 +1,399 @@
1
- """Classes for encapsulating IBIS model constituents.
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
- Copyright (c) 2019 by David Banas; All rights reserved World wide.
11
- """
12
-
13
- import numpy as np
14
- from chaco.api import ArrayPlotData, Plot
15
- from enable.component_editor import ComponentEditor
16
- from traits.api import HasTraits, String, Trait
17
- from traitsui.api import Group, Item, ModalButtons, View
18
-
19
-
20
- class Component(HasTraits):
21
- """Encapsulation of a particular component from an IBIS model file."""
22
-
23
- def __init__(self, subDict):
24
- """
25
- Args:
26
- subDict(dict): Dictionary of [Component] sub-keywords/params.
27
- """
28
-
29
- # Super-class initialization is ABSOLUTELY NECESSARY, in order
30
- # to get all the Traits/UI machinery setup correctly.
31
- super().__init__()
32
-
33
- # Stash the sub-keywords/parameters.
34
- self._subDict = subDict
35
-
36
- # Fetch available keyword/parameter definitions.
37
- def maybe(name):
38
- return subDict[name] if name in subDict else None
39
-
40
- self._mfr = maybe("manufacturer")
41
- self._pkg = maybe("package")
42
- self._pins = maybe("pin")
43
- self._diffs = maybe("diff_pin")
44
-
45
- # Check for the required keywords.
46
- if not self._mfr:
47
- raise LookupError("Missing [Manufacturer]!")
48
- if not self._pkg:
49
- print(self._mfr)
50
- raise LookupError("Missing [Package]!")
51
- if not self._pins:
52
- raise LookupError("Missing [Pin]!")
53
-
54
- # Set up the GUI.
55
- self.add_trait("manufacturer", String(self._mfr))
56
- self.add_trait("package", String(self._pkg))
57
- self.add_trait("_pin", Trait(list(self._pins)[0], self._pins))
58
- self._content = [
59
- Group(
60
- Item("manufacturer", label="Manufacturer", style="readonly"),
61
- Item("package", label="Package", style="readonly"),
62
- Item("_pin", label="Pin"),
63
- label="Component",
64
- show_border=True,
65
- ),
66
- ]
67
-
68
- def __str__(self):
69
- res = "Manufacturer:\t" + self._mfr + "\n"
70
- res += "Package: \t" + str(self._pkg) + "\n"
71
- res += "Pins:\n"
72
- for pname in self._pins:
73
- res += " " + pname + ":\t" + str(self._pins[pname]) + "\n"
74
- return res
75
-
76
- def __call__(self):
77
- self.edit_traits()
78
-
79
- def default_traits_view(self):
80
- "Default Traits/UI view definition."
81
- view = View(
82
- resizable=False,
83
- buttons=ModalButtons,
84
- title="PyBERT IBIS Component Viewer",
85
- id="pyibisami.ibis_parser.Component",
86
- )
87
- view.set_content(self._content)
88
- return view
89
-
90
- @property
91
- def pin(self):
92
- """The pin selected most recently by the user.
93
-
94
- Returns the first pin in the list, if the user hasn't made a
95
- selection yet.
96
- """
97
- return self._pin_
98
-
99
- @property
100
- def pins(self):
101
- "The list of component pins."
102
- return self._pins
103
-
104
-
105
- class Model(HasTraits): # pylint: disable=too-many-instance-attributes
106
- """Encapsulation of a particular I/O model from an IBIS model file."""
107
-
108
- def __init__(self, subDict): # pylint: disable=too-many-locals,too-many-statements,too-many-branches
109
- """
110
- Args:
111
- subDict (dict): Dictionary of sub-keywords/params.
112
- """
113
-
114
- # Super-class initialization is ABSOLUTELY NECESSARY, in order
115
- # to get all the Traits/UI machinery setup correctly.
116
- super().__init__()
117
-
118
- # Stash the sub-keywords/parameters.
119
- self._subDict = subDict
120
-
121
- # Fetch available keyword/parameter definitions.
122
- def maybe(name):
123
- return subDict[name] if name in subDict else None
124
-
125
- self._mtype = maybe("model_type")
126
- self._ccomp = maybe("c_comp")
127
- self._cref = maybe("cref")
128
- self._vref = maybe("vref")
129
- self._vmeas = maybe("vmeas")
130
- self._rref = maybe("rref")
131
- self._trange = maybe("temperature_range")
132
- self._vrange = maybe("voltage_range")
133
- self._ramp = maybe("ramp")
134
-
135
- # Check for the required keywords.
136
- if not self._mtype:
137
- raise LookupError("Missing Model_type!")
138
- if not self._vrange:
139
- raise LookupError("Missing [Voltage Range]!")
140
-
141
- def proc_iv(xs):
142
- """Process an I/V table."""
143
- if len(xs) < 2:
144
- raise ValueError("Insufficient number of I-V data points!")
145
- try:
146
- vs, iss = zip(*(xs)) # Idiomatic Python for ``unzip``.
147
- except Exception as exc:
148
- raise ValueError(f"xs: {xs}") from exc
149
- ityps, imins, imaxs = zip(*iss)
150
- vmeas = self._vmeas
151
-
152
- def calcZ(x):
153
- (vs, ivals) = x
154
- if vmeas:
155
- ix = np.where(np.array(vs) >= vmeas)[0][0]
156
- else:
157
- ix = np.where(np.array(vs) >= max(vs) / 2)[0][0]
158
- try:
159
- return abs((vs[ix] - vs[ix - 1]) / (ivals[ix] - ivals[ix - 1]))
160
- except ZeroDivisionError:
161
- return 1e7 # Use 10 MOhms in place of infinity.
162
-
163
- zs = map(calcZ, zip([vs, vs, vs], [ityps, imins, imaxs]))
164
- return vs, ityps, imins, imaxs, zs
165
-
166
- # Infer impedance and/or rise/fall time, as per model type.
167
- mtype = self._mtype.lower()
168
- if mtype in ("output", "i/o"):
169
- if "pulldown" not in subDict or "pullup" not in subDict:
170
- raise LookupError("Missing I-V curves!")
171
- plotdata = ArrayPlotData()
172
- pd_vs, pd_ityps, pd_imins, pd_imaxs, pd_zs = proc_iv(subDict["pulldown"])
173
- pu_vs, pu_ityps, pu_imins, pu_imaxs, pu_zs = proc_iv(subDict["pullup"])
174
- pu_vs = self._vrange[0] - np.array(pu_vs) # Correct for Vdd-relative pull-up voltages.
175
- pu_ityps = -np.array(pu_ityps) # Correct for current sense, for nicer plot.
176
- pu_imins = -np.array(pu_imins)
177
- pu_imaxs = -np.array(pu_imaxs)
178
- self._zout = (list(pd_zs)[0] + list(pu_zs)[0]) / 2
179
- plotdata.set_data("pd_vs", pd_vs)
180
- plotdata.set_data("pd_ityps", pd_ityps)
181
- plotdata.set_data("pd_imins", pd_imins)
182
- plotdata.set_data("pd_imaxs", pd_imaxs)
183
- plotdata.set_data("pu_vs", pu_vs)
184
- plotdata.set_data("pu_ityps", pu_ityps)
185
- plotdata.set_data("pu_imins", pu_imins)
186
- plotdata.set_data("pu_imaxs", pu_imaxs)
187
- plot_iv = Plot(plotdata) # , padding_left=75)
188
- # The 'line_style' trait of a LinePlot instance must be 'dash' or 'dot dash' or 'dot' or 'long dash' or 'solid'.
189
- plot_iv.plot(("pd_vs", "pd_ityps"), type="line", color="blue", line_style="solid", name="PD-Typ")
190
- plot_iv.plot(("pd_vs", "pd_imins"), type="line", color="blue", line_style="dot", name="PD-Min")
191
- plot_iv.plot(("pd_vs", "pd_imaxs"), type="line", color="blue", line_style="dash", name="PD-Max")
192
- plot_iv.plot(("pu_vs", "pu_ityps"), type="line", color="red", line_style="solid", name="PU-Typ")
193
- plot_iv.plot(("pu_vs", "pu_imins"), type="line", color="red", line_style="dot", name="PU-Min")
194
- plot_iv.plot(("pu_vs", "pu_imaxs"), type="line", color="red", line_style="dash", name="PU-Max")
195
- plot_iv.title = "Pull-Up/Down I-V Curves"
196
- plot_iv.index_axis.title = "Vout (V)"
197
- plot_iv.value_axis.title = "Iout (A)"
198
- plot_iv.index_range.low_setting = 0
199
- plot_iv.index_range.high_setting = self._vrange[0]
200
- plot_iv.value_range.low_setting = 0
201
- plot_iv.value_range.high_setting = 0.1
202
- plot_iv.legend.visible = True
203
- plot_iv.legend.align = "ul"
204
- self.plot_iv = plot_iv
205
-
206
- if not self._ramp:
207
- raise LookupError("Missing [Ramp]!")
208
- ramp = subDict["ramp"]
209
- self._slew = (ramp["rising"][0] + ramp["falling"][0]) / 2e9 # (V/ns)
210
- elif mtype == "input":
211
- if "gnd_clamp" not in subDict and "power_clamp" not in subDict:
212
- raise LookupError("Missing clamp curves!")
213
-
214
- plotdata = ArrayPlotData()
215
-
216
- if "gnd_clamp" in subDict:
217
- gc_vs, gc_ityps, gc_imins, gc_imaxs, gc_zs = proc_iv(subDict["gnd_clamp"])
218
- gc_z = list(gc_zs)[0] # Use typical value for Zin calc.
219
- plotdata.set_data("gc_vs", gc_vs)
220
- plotdata.set_data("gc_ityps", gc_ityps)
221
- plotdata.set_data("gc_imins", gc_imins)
222
- plotdata.set_data("gc_imaxs", gc_imaxs)
223
-
224
- if "power_clamp" in subDict:
225
- pc_vs, pc_ityps, pc_imins, pc_imaxs, pc_zs = proc_iv(subDict["power_clamp"])
226
- pc_z = list(pc_zs)[0]
227
- pc_vs = self._vrange[0] - np.array(pc_vs) # Correct for Vdd-relative pull-up voltages.
228
- pc_ityps = -np.array(pc_ityps) # Correct for current sense, for nicer plot.
229
- pc_imins = -np.array(pc_imins)
230
- pc_imaxs = -np.array(pc_imaxs)
231
- plotdata.set_data("pc_vs", pc_vs)
232
- plotdata.set_data("pc_ityps", pc_ityps)
233
- plotdata.set_data("pc_imins", pc_imins)
234
- plotdata.set_data("pc_imaxs", pc_imaxs)
235
-
236
- plot_iv = Plot(plotdata) # , padding_left=75)
237
- # The 'line_style' trait of a LinePlot instance must be 'dash' or 'dot dash' or 'dot' or 'long dash' or 'solid'.
238
- if "gnd_clamp" in subDict:
239
- plot_iv.plot(("gc_vs", "gc_ityps"), type="line", color="blue", line_style="solid", name="PD-Typ")
240
- plot_iv.plot(("gc_vs", "gc_imins"), type="line", color="blue", line_style="dot", name="PD-Min")
241
- plot_iv.plot(("gc_vs", "gc_imaxs"), type="line", color="blue", line_style="dash", name="PD-Max")
242
- if "power_clamp" in subDict:
243
- plot_iv.plot(("pc_vs", "pc_ityps"), type="line", color="red", line_style="solid", name="PU-Typ")
244
- plot_iv.plot(("pc_vs", "pc_imins"), type="line", color="red", line_style="dot", name="PU-Min")
245
- plot_iv.plot(("pc_vs", "pc_imaxs"), type="line", color="red", line_style="dash", name="PU-Max")
246
- plot_iv.title = "Power/GND Clamp I-V Curves"
247
- plot_iv.index_axis.title = "Vin (V)"
248
- plot_iv.value_axis.title = "Iin (A)"
249
- plot_iv.index_range.low_setting = 0
250
- plot_iv.index_range.high_setting = self._vrange[0]
251
- plot_iv.value_range.low_setting = 0
252
- plot_iv.value_range.high_setting = 0.1
253
- plot_iv.legend.visible = True
254
- plot_iv.legend.align = "ul"
255
- self.plot_iv = plot_iv
256
-
257
- if "gnd_clamp" in subDict and "power_clamp" in subDict:
258
- # Parallel combination, as both clamps are always active.
259
- self._zin = (gc_z * pc_z) / (gc_z + pc_z) # pylint: disable=possibly-used-before-assignment
260
- elif "gnd_clamp" in subDict:
261
- self._zin = gc_z
262
- else:
263
- self._zin = pc_z
264
-
265
- # Separate AMI executables by OS.
266
- def is64(x):
267
- ((_, b), _) = x
268
- return int(b) == 64
269
-
270
- def isWin(x):
271
- ((os, _), _) = x
272
- return os.lower() == "windows"
273
-
274
- def partition(p, xs):
275
- ts, fs = [], []
276
- for x in xs:
277
- if p(x):
278
- ts.append(x)
279
- else:
280
- fs.append(x)
281
- return ts, fs
282
-
283
- def getFiles(x):
284
- if x:
285
- ((_, _), fs) = x[0]
286
- return fs
287
- return []
288
-
289
- def splitExecs(fs):
290
- wins, lins = partition(isWin, fs)
291
- return (getFiles(wins), getFiles(lins))
292
-
293
- self._exec32Wins, self._exec32Lins = [], []
294
- self._exec64Wins, self._exec64Lins = [], []
295
- if "algorithmic_model" in subDict:
296
- execs = subDict["algorithmic_model"]
297
- exec64s, exec32s = partition(is64, execs)
298
- self._exec32Wins, self._exec32Lins = splitExecs(exec32s)
299
- self._exec64Wins, self._exec64Lins = splitExecs(exec64s)
300
-
301
- # Set up the GUI.
302
- self.add_trait("model_type", String(self._mtype))
303
- self.add_trait("c_comp", String(self._ccomp))
304
- self.add_trait("cref", String(self._cref))
305
- self.add_trait("vref", String(self._vref))
306
- self.add_trait("vmeas", String(self._vmeas))
307
- self.add_trait("rref", String(self._rref))
308
- self.add_trait("trange", String(self._trange))
309
- self.add_trait("vrange", String(self._vrange))
310
- if mtype in ("output", "i/o"):
311
- self.add_trait("zout", String(self._zout))
312
- self.add_trait("slew", String(self._slew))
313
- elif mtype == "input":
314
- self.add_trait("zin", String(self._zin))
315
- self._content = [
316
- Group(
317
- Item("model_type", label="Model type", style="readonly"),
318
- Item("c_comp", label="Ccomp", style="readonly"),
319
- Item("trange", label="Temperature Range", style="readonly"),
320
- Item("vrange", label="Voltage Range", style="readonly"),
321
- Group(
322
- Item("cref", label="Cref", style="readonly"),
323
- Item("vref", label="Vref", style="readonly"),
324
- Item("vmeas", label="Vmeas", style="readonly"),
325
- Item("rref", label="Rref", style="readonly"),
326
- orientation="horizontal",
327
- ),
328
- label="Model",
329
- show_border=True,
330
- ),
331
- ]
332
- if mtype in ("output", "i/o"):
333
- self._content.append(Item("zout", label="Impedance (Ohms)", style="readonly", format_str="%4.1f"))
334
- self._content.append(Item("slew", label="Slew Rate (V/ns)", style="readonly", format_str="%4.1f"))
335
- self._content.append(Item("plot_iv", editor=ComponentEditor(), show_label=False))
336
- elif mtype == "input":
337
- self._content.append(Item("zin", label="Impedance (Ohms)", style="readonly", format_str="%4.1f"))
338
- self._content.append(Item("plot_iv", editor=ComponentEditor(), show_label=False))
339
-
340
- def __str__(self):
341
- res = "Model Type:\t" + self._mtype + "\n"
342
- res += "C_comp: \t" + str(self._ccomp) + "\n"
343
- res += "Cref: \t" + str(self._cref) + "\n"
344
- res += "Vref: \t" + str(self._vref) + "\n"
345
- res += "Vmeas: \t" + str(self._vmeas) + "\n"
346
- res += "Rref: \t" + str(self._rref) + "\n"
347
- res += "Temperature Range:\t" + str(self._trange) + "\n"
348
- res += "Voltage Range: \t" + str(self._vrange) + "\n"
349
- if "algorithmic_model" in self._subDict:
350
- res += "Algorithmic Model:\n" + "\t32-bit:\n"
351
- if self._exec32Lins:
352
- res += "\t\tLinux: " + str(self._exec32Lins) + "\n"
353
- if self._exec32Wins:
354
- res += "\t\tWindows: " + str(self._exec32Wins) + "\n"
355
- res += "\t64-bit:\n"
356
- if self._exec64Lins:
357
- res += "\t\tLinux: " + str(self._exec64Lins) + "\n"
358
- if self._exec64Wins:
359
- res += "\t\tWindows: " + str(self._exec64Wins) + "\n"
360
- return res
361
-
362
- def __call__(self):
363
- self.edit_traits(kind="livemodal")
364
-
365
- def default_traits_view(self):
366
- "Default Traits/UI view definition."
367
- view = View(
368
- resizable=False,
369
- buttons=ModalButtons,
370
- title="PyBERT IBIS Model Viewer",
371
- id="pyibisami.ibis_parser.Model",
372
- )
373
- view.set_content(self._content)
374
- return view
375
-
376
- @property
377
- def zout(self):
378
- "The driver impedance."
379
- return self._zout
380
-
381
- @property
382
- def slew(self):
383
- "The driver slew rate."
384
- return self._slew
385
-
386
- @property
387
- def zin(self):
388
- "The input impedance."
389
- return self._zin
390
-
391
- @property
392
- def ccomp(self):
393
- "The parasitic capacitance."
394
- return self._ccomp
395
-
396
- @property
397
- def mtype(self):
398
- """Model type."""
399
- return self._mtype
1
+ """Classes for encapsulating IBIS model constituents.
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
+ Copyright (c) 2019 by David Banas; All rights reserved World wide.
11
+ """
12
+
13
+ import numpy as np
14
+ from chaco.api import ArrayPlotData, Plot
15
+ from enable.component_editor import ComponentEditor
16
+ from traits.api import HasTraits, String, Trait
17
+ from traitsui.api import Group, Item, ModalButtons, View
18
+
19
+
20
+ class Component(HasTraits):
21
+ """Encapsulation of a particular component from an IBIS model file."""
22
+
23
+ def __init__(self, subDict):
24
+ """
25
+ Args:
26
+ subDict(dict): Dictionary of [Component] sub-keywords/params.
27
+ """
28
+
29
+ # Super-class initialization is ABSOLUTELY NECESSARY, in order
30
+ # to get all the Traits/UI machinery setup correctly.
31
+ super().__init__()
32
+
33
+ # Stash the sub-keywords/parameters.
34
+ self._subDict = subDict
35
+
36
+ # Fetch available keyword/parameter definitions.
37
+ def maybe(name):
38
+ return subDict[name] if name in subDict else None
39
+
40
+ self._mfr = maybe("manufacturer")
41
+ self._pkg = maybe("package")
42
+ self._pins = maybe("pin")
43
+ self._diffs = maybe("diff_pin")
44
+
45
+ # Check for the required keywords.
46
+ if not self._mfr:
47
+ raise LookupError("Missing [Manufacturer]!")
48
+ if not self._pkg:
49
+ print(self._mfr)
50
+ raise LookupError("Missing [Package]!")
51
+ if not self._pins:
52
+ raise LookupError("Missing [Pin]!")
53
+
54
+ # Set up the GUI.
55
+ self.add_trait("manufacturer", String(self._mfr))
56
+ self.add_trait("package", String(self._pkg))
57
+ self.add_trait("_pin", Trait(list(self._pins)[0], self._pins))
58
+ self._content = [
59
+ Group(
60
+ Item("manufacturer", label="Manufacturer", style="readonly"),
61
+ Item("package", label="Package", style="readonly"),
62
+ Item("_pin", label="Pin"),
63
+ label="Component",
64
+ show_border=True,
65
+ ),
66
+ ]
67
+
68
+ def __str__(self):
69
+ res = "Manufacturer:\t" + self._mfr + "\n"
70
+ res += "Package: \t" + str(self._pkg) + "\n"
71
+ res += "Pins:\n"
72
+ for pname in self._pins:
73
+ res += " " + pname + ":\t" + str(self._pins[pname]) + "\n"
74
+ return res
75
+
76
+ def __call__(self):
77
+ self.edit_traits()
78
+
79
+ def default_traits_view(self):
80
+ "Default Traits/UI view definition."
81
+ view = View(
82
+ resizable=False,
83
+ buttons=ModalButtons,
84
+ title="PyBERT IBIS Component Viewer",
85
+ id="pyibisami.ibis_parser.Component",
86
+ )
87
+ view.set_content(self._content)
88
+ return view
89
+
90
+ @property
91
+ def pin(self):
92
+ """The pin selected most recently by the user.
93
+
94
+ Returns the first pin in the list, if the user hasn't made a
95
+ selection yet.
96
+ """
97
+ return self._pin_
98
+
99
+ @property
100
+ def pins(self):
101
+ "The list of component pins."
102
+ return self._pins
103
+
104
+
105
+ class Model(HasTraits): # pylint: disable=too-many-instance-attributes
106
+ """Encapsulation of a particular I/O model from an IBIS model file."""
107
+
108
+ def __init__(self, subDict): # pylint: disable=too-many-locals,too-many-statements,too-many-branches
109
+ """
110
+ Args:
111
+ subDict (dict): Dictionary of sub-keywords/params.
112
+ """
113
+
114
+ # Super-class initialization is ABSOLUTELY NECESSARY, in order
115
+ # to get all the Traits/UI machinery setup correctly.
116
+ super().__init__()
117
+
118
+ # Stash the sub-keywords/parameters.
119
+ self._subDict = subDict
120
+
121
+ # Fetch available keyword/parameter definitions.
122
+ def maybe(name):
123
+ return subDict[name] if name in subDict else None
124
+
125
+ self._mtype = maybe("model_type")
126
+ self._ccomp = maybe("c_comp")
127
+ self._cref = maybe("cref")
128
+ self._vref = maybe("vref")
129
+ self._vmeas = maybe("vmeas")
130
+ self._rref = maybe("rref")
131
+ self._trange = maybe("temperature_range")
132
+ self._vrange = maybe("voltage_range")
133
+ self._ramp = maybe("ramp")
134
+
135
+ # Check for the required keywords.
136
+ if not self._mtype:
137
+ raise LookupError("Missing Model_type!")
138
+ if not self._vrange:
139
+ raise LookupError("Missing [Voltage Range]!")
140
+
141
+ def proc_iv(xs):
142
+ """Process an I/V table."""
143
+ if len(xs) < 2:
144
+ raise ValueError("Insufficient number of I-V data points!")
145
+ try:
146
+ vs, iss = zip(*(xs)) # Idiomatic Python for ``unzip``.
147
+ except Exception as exc:
148
+ raise ValueError(f"xs: {xs}") from exc
149
+ ityps, imins, imaxs = zip(*iss)
150
+ vmeas = self._vmeas
151
+
152
+ def calcZ(x):
153
+ (vs, ivals) = x
154
+ if vmeas:
155
+ ix = np.where(np.array(vs) >= vmeas)[0][0]
156
+ else:
157
+ ix = np.where(np.array(vs) >= max(vs) / 2)[0][0]
158
+ try:
159
+ return abs((vs[ix] - vs[ix - 1]) / (ivals[ix] - ivals[ix - 1]))
160
+ except ZeroDivisionError:
161
+ return 1e7 # Use 10 MOhms in place of infinity.
162
+
163
+ zs = map(calcZ, zip([vs, vs, vs], [ityps, imins, imaxs]))
164
+ return vs, ityps, imins, imaxs, zs
165
+
166
+ # Infer impedance and/or rise/fall time, as per model type.
167
+ mtype = self._mtype.lower()
168
+ if mtype in ("output", "i/o"):
169
+ if "pulldown" not in subDict or "pullup" not in subDict:
170
+ raise LookupError("Missing I-V curves!")
171
+ plotdata = ArrayPlotData()
172
+ pd_vs, pd_ityps, pd_imins, pd_imaxs, pd_zs = proc_iv(subDict["pulldown"])
173
+ pu_vs, pu_ityps, pu_imins, pu_imaxs, pu_zs = proc_iv(subDict["pullup"])
174
+ pu_vs = self._vrange[0] - np.array(pu_vs) # Correct for Vdd-relative pull-up voltages.
175
+ pu_ityps = -np.array(pu_ityps) # Correct for current sense, for nicer plot.
176
+ pu_imins = -np.array(pu_imins)
177
+ pu_imaxs = -np.array(pu_imaxs)
178
+ self._zout = (list(pd_zs)[0] + list(pu_zs)[0]) / 2
179
+ plotdata.set_data("pd_vs", pd_vs)
180
+ plotdata.set_data("pd_ityps", pd_ityps)
181
+ plotdata.set_data("pd_imins", pd_imins)
182
+ plotdata.set_data("pd_imaxs", pd_imaxs)
183
+ plotdata.set_data("pu_vs", pu_vs)
184
+ plotdata.set_data("pu_ityps", pu_ityps)
185
+ plotdata.set_data("pu_imins", pu_imins)
186
+ plotdata.set_data("pu_imaxs", pu_imaxs)
187
+ plot_iv = Plot(plotdata) # , padding_left=75)
188
+ # The 'line_style' trait of a LinePlot instance must be 'dash' or 'dot dash' or 'dot' or 'long dash' or 'solid'.
189
+ plot_iv.plot(("pd_vs", "pd_ityps"), type="line", color="blue", line_style="solid", name="PD-Typ")
190
+ plot_iv.plot(("pd_vs", "pd_imins"), type="line", color="blue", line_style="dot", name="PD-Min")
191
+ plot_iv.plot(("pd_vs", "pd_imaxs"), type="line", color="blue", line_style="dash", name="PD-Max")
192
+ plot_iv.plot(("pu_vs", "pu_ityps"), type="line", color="red", line_style="solid", name="PU-Typ")
193
+ plot_iv.plot(("pu_vs", "pu_imins"), type="line", color="red", line_style="dot", name="PU-Min")
194
+ plot_iv.plot(("pu_vs", "pu_imaxs"), type="line", color="red", line_style="dash", name="PU-Max")
195
+ plot_iv.title = "Pull-Up/Down I-V Curves"
196
+ plot_iv.index_axis.title = "Vout (V)"
197
+ plot_iv.value_axis.title = "Iout (A)"
198
+ plot_iv.index_range.low_setting = 0
199
+ plot_iv.index_range.high_setting = self._vrange[0]
200
+ plot_iv.value_range.low_setting = 0
201
+ plot_iv.value_range.high_setting = 0.1
202
+ plot_iv.legend.visible = True
203
+ plot_iv.legend.align = "ul"
204
+ self.plot_iv = plot_iv
205
+
206
+ if not self._ramp:
207
+ raise LookupError("Missing [Ramp]!")
208
+ ramp = subDict["ramp"]
209
+ self._slew = (ramp["rising"][0] + ramp["falling"][0]) / 2e9 # (V/ns)
210
+ elif mtype == "input":
211
+ if "gnd_clamp" not in subDict and "power_clamp" not in subDict:
212
+ raise LookupError("Missing clamp curves!")
213
+
214
+ plotdata = ArrayPlotData()
215
+
216
+ if "gnd_clamp" in subDict:
217
+ gc_vs, gc_ityps, gc_imins, gc_imaxs, gc_zs = proc_iv(subDict["gnd_clamp"])
218
+ gc_z = list(gc_zs)[0] # Use typical value for Zin calc.
219
+ plotdata.set_data("gc_vs", gc_vs)
220
+ plotdata.set_data("gc_ityps", gc_ityps)
221
+ plotdata.set_data("gc_imins", gc_imins)
222
+ plotdata.set_data("gc_imaxs", gc_imaxs)
223
+
224
+ if "power_clamp" in subDict:
225
+ pc_vs, pc_ityps, pc_imins, pc_imaxs, pc_zs = proc_iv(subDict["power_clamp"])
226
+ pc_z = list(pc_zs)[0]
227
+ pc_vs = self._vrange[0] - np.array(pc_vs) # Correct for Vdd-relative pull-up voltages.
228
+ pc_ityps = -np.array(pc_ityps) # Correct for current sense, for nicer plot.
229
+ pc_imins = -np.array(pc_imins)
230
+ pc_imaxs = -np.array(pc_imaxs)
231
+ plotdata.set_data("pc_vs", pc_vs)
232
+ plotdata.set_data("pc_ityps", pc_ityps)
233
+ plotdata.set_data("pc_imins", pc_imins)
234
+ plotdata.set_data("pc_imaxs", pc_imaxs)
235
+
236
+ plot_iv = Plot(plotdata) # , padding_left=75)
237
+ # The 'line_style' trait of a LinePlot instance must be 'dash' or 'dot dash' or 'dot' or 'long dash' or 'solid'.
238
+ if "gnd_clamp" in subDict:
239
+ plot_iv.plot(("gc_vs", "gc_ityps"), type="line", color="blue", line_style="solid", name="PD-Typ")
240
+ plot_iv.plot(("gc_vs", "gc_imins"), type="line", color="blue", line_style="dot", name="PD-Min")
241
+ plot_iv.plot(("gc_vs", "gc_imaxs"), type="line", color="blue", line_style="dash", name="PD-Max")
242
+ if "power_clamp" in subDict:
243
+ plot_iv.plot(("pc_vs", "pc_ityps"), type="line", color="red", line_style="solid", name="PU-Typ")
244
+ plot_iv.plot(("pc_vs", "pc_imins"), type="line", color="red", line_style="dot", name="PU-Min")
245
+ plot_iv.plot(("pc_vs", "pc_imaxs"), type="line", color="red", line_style="dash", name="PU-Max")
246
+ plot_iv.title = "Power/GND Clamp I-V Curves"
247
+ plot_iv.index_axis.title = "Vin (V)"
248
+ plot_iv.value_axis.title = "Iin (A)"
249
+ plot_iv.index_range.low_setting = 0
250
+ plot_iv.index_range.high_setting = self._vrange[0]
251
+ plot_iv.value_range.low_setting = 0
252
+ plot_iv.value_range.high_setting = 0.1
253
+ plot_iv.legend.visible = True
254
+ plot_iv.legend.align = "ul"
255
+ self.plot_iv = plot_iv
256
+
257
+ if "gnd_clamp" in subDict and "power_clamp" in subDict:
258
+ # Parallel combination, as both clamps are always active.
259
+ self._zin = (gc_z * pc_z) / (gc_z + pc_z) # pylint: disable=possibly-used-before-assignment
260
+ elif "gnd_clamp" in subDict:
261
+ self._zin = gc_z
262
+ else:
263
+ self._zin = pc_z
264
+
265
+ # Separate AMI executables by OS.
266
+ def is64(x):
267
+ ((_, b), _) = x
268
+ return int(b) == 64
269
+
270
+ def isWin(x):
271
+ ((os, _), _) = x
272
+ return os.lower() == "windows"
273
+
274
+ def partition(p, xs):
275
+ ts, fs = [], []
276
+ for x in xs:
277
+ if p(x):
278
+ ts.append(x)
279
+ else:
280
+ fs.append(x)
281
+ return ts, fs
282
+
283
+ def getFiles(x):
284
+ if x:
285
+ ((_, _), fs) = x[0]
286
+ return fs
287
+ return []
288
+
289
+ def splitExecs(fs):
290
+ wins, lins = partition(isWin, fs)
291
+ return (getFiles(wins), getFiles(lins))
292
+
293
+ self._exec32Wins, self._exec32Lins = [], []
294
+ self._exec64Wins, self._exec64Lins = [], []
295
+ if "algorithmic_model" in subDict:
296
+ execs = subDict["algorithmic_model"]
297
+ exec64s, exec32s = partition(is64, execs)
298
+ self._exec32Wins, self._exec32Lins = splitExecs(exec32s)
299
+ self._exec64Wins, self._exec64Lins = splitExecs(exec64s)
300
+
301
+ # Set up the GUI.
302
+ self.add_trait("model_type", String(self._mtype))
303
+ self.add_trait("c_comp", String(self._ccomp))
304
+ self.add_trait("cref", String(self._cref))
305
+ self.add_trait("vref", String(self._vref))
306
+ self.add_trait("vmeas", String(self._vmeas))
307
+ self.add_trait("rref", String(self._rref))
308
+ self.add_trait("trange", String(self._trange))
309
+ self.add_trait("vrange", String(self._vrange))
310
+ if mtype in ("output", "i/o"):
311
+ self.add_trait("zout", String(self._zout))
312
+ self.add_trait("slew", String(self._slew))
313
+ elif mtype == "input":
314
+ self.add_trait("zin", String(self._zin))
315
+ self._content = [
316
+ Group(
317
+ Item("model_type", label="Model type", style="readonly"),
318
+ Item("c_comp", label="Ccomp", style="readonly"),
319
+ Item("trange", label="Temperature Range", style="readonly"),
320
+ Item("vrange", label="Voltage Range", style="readonly"),
321
+ Group(
322
+ Item("cref", label="Cref", style="readonly"),
323
+ Item("vref", label="Vref", style="readonly"),
324
+ Item("vmeas", label="Vmeas", style="readonly"),
325
+ Item("rref", label="Rref", style="readonly"),
326
+ orientation="horizontal",
327
+ ),
328
+ label="Model",
329
+ show_border=True,
330
+ ),
331
+ ]
332
+ if mtype in ("output", "i/o"):
333
+ self._content.append(Item("zout", label="Impedance (Ohms)", style="readonly", format_str="%4.1f"))
334
+ self._content.append(Item("slew", label="Slew Rate (V/ns)", style="readonly", format_str="%4.1f"))
335
+ self._content.append(Item("plot_iv", editor=ComponentEditor(), show_label=False))
336
+ elif mtype == "input":
337
+ self._content.append(Item("zin", label="Impedance (Ohms)", style="readonly", format_str="%4.1f"))
338
+ self._content.append(Item("plot_iv", editor=ComponentEditor(), show_label=False))
339
+
340
+ def __str__(self):
341
+ res = "Model Type:\t" + self._mtype + "\n"
342
+ res += "C_comp: \t" + str(self._ccomp) + "\n"
343
+ res += "Cref: \t" + str(self._cref) + "\n"
344
+ res += "Vref: \t" + str(self._vref) + "\n"
345
+ res += "Vmeas: \t" + str(self._vmeas) + "\n"
346
+ res += "Rref: \t" + str(self._rref) + "\n"
347
+ res += "Temperature Range:\t" + str(self._trange) + "\n"
348
+ res += "Voltage Range: \t" + str(self._vrange) + "\n"
349
+ if "algorithmic_model" in self._subDict:
350
+ res += "Algorithmic Model:\n" + "\t32-bit:\n"
351
+ if self._exec32Lins:
352
+ res += "\t\tLinux: " + str(self._exec32Lins) + "\n"
353
+ if self._exec32Wins:
354
+ res += "\t\tWindows: " + str(self._exec32Wins) + "\n"
355
+ res += "\t64-bit:\n"
356
+ if self._exec64Lins:
357
+ res += "\t\tLinux: " + str(self._exec64Lins) + "\n"
358
+ if self._exec64Wins:
359
+ res += "\t\tWindows: " + str(self._exec64Wins) + "\n"
360
+ return res
361
+
362
+ def __call__(self):
363
+ self.edit_traits(kind="livemodal")
364
+
365
+ def default_traits_view(self):
366
+ "Default Traits/UI view definition."
367
+ view = View(
368
+ resizable=False,
369
+ buttons=ModalButtons,
370
+ title="PyBERT IBIS Model Viewer",
371
+ id="pyibisami.ibis_parser.Model",
372
+ )
373
+ view.set_content(self._content)
374
+ return view
375
+
376
+ @property
377
+ def zout(self):
378
+ "The driver impedance."
379
+ return self._zout
380
+
381
+ @property
382
+ def slew(self):
383
+ "The driver slew rate."
384
+ return self._slew
385
+
386
+ @property
387
+ def zin(self):
388
+ "The input impedance."
389
+ return self._zin
390
+
391
+ @property
392
+ def ccomp(self):
393
+ "The parasitic capacitance."
394
+ return self._ccomp
395
+
396
+ @property
397
+ def mtype(self):
398
+ """Model type."""
399
+ return self._mtype