pyadi-jif 0.1.0__py2.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.
- adijif/__init__.py +32 -0
- adijif/adijif.py +1 -0
- adijif/cli.py +21 -0
- adijif/clocks/__init__.py +10 -0
- adijif/clocks/ad9523.py +321 -0
- adijif/clocks/ad9523_1_bf.py +91 -0
- adijif/clocks/ad9528.py +444 -0
- adijif/clocks/ad9528_bf.py +70 -0
- adijif/clocks/ad9545.py +553 -0
- adijif/clocks/clock.py +153 -0
- adijif/clocks/hmc7044.py +558 -0
- adijif/clocks/hmc7044_bf.py +68 -0
- adijif/clocks/ltc6952.py +624 -0
- adijif/clocks/ltc6952_bf.py +67 -0
- adijif/clocks/ltc6953.py +509 -0
- adijif/common.py +70 -0
- adijif/converters/__init__.py +3 -0
- adijif/converters/ad9081.py +679 -0
- adijif/converters/ad9081_dp.py +206 -0
- adijif/converters/ad9081_util.py +124 -0
- adijif/converters/ad9084.py +588 -0
- adijif/converters/ad9084_dp.py +111 -0
- adijif/converters/ad9084_draw.py +203 -0
- adijif/converters/ad9084_util.py +365 -0
- adijif/converters/ad9144.py +316 -0
- adijif/converters/ad9144_bf.py +44 -0
- adijif/converters/ad9680.py +201 -0
- adijif/converters/ad9680_bf.py +43 -0
- adijif/converters/ad9680_draw.py +184 -0
- adijif/converters/adc.py +83 -0
- adijif/converters/adrv9009.py +426 -0
- adijif/converters/adrv9009_bf.py +43 -0
- adijif/converters/adrv9009_util.py +89 -0
- adijif/converters/converter.py +399 -0
- adijif/converters/dac.py +85 -0
- adijif/converters/resources/AD9084_JTX_JRX.xlsx +0 -0
- adijif/converters/resources/ad9081_JRx_204B.csv +180 -0
- adijif/converters/resources/ad9081_JRx_204C.csv +411 -0
- adijif/converters/resources/ad9081_JTx_204B.csv +1488 -0
- adijif/converters/resources/ad9081_JTx_204C.csv +1064 -0
- adijif/converters/resources/full_rx_mode_table_ad9081.csv +1904 -0
- adijif/converters/resources/full_tx_mode_table_ad9081.csv +994 -0
- adijif/d2/__init__.py +26 -0
- adijif/d2/d2lib.h +81 -0
- adijif/draw.py +498 -0
- adijif/fpgas/__init__.py +1 -0
- adijif/fpgas/fpga.py +64 -0
- adijif/fpgas/xilinx/__init__.py +1143 -0
- adijif/fpgas/xilinx/bf.py +101 -0
- adijif/fpgas/xilinx/pll.py +232 -0
- adijif/fpgas/xilinx/sevenseries.py +531 -0
- adijif/fpgas/xilinx/ultrascaleplus.py +485 -0
- adijif/fpgas/xilinx/xilinx_draw.py +516 -0
- adijif/gekko_trans.py +295 -0
- adijif/jesd.py +760 -0
- adijif/plls/__init__.py +3 -0
- adijif/plls/adf4030.py +259 -0
- adijif/plls/adf4371.py +419 -0
- adijif/plls/adf4382.py +581 -0
- adijif/plls/pll.py +103 -0
- adijif/solvers.py +54 -0
- adijif/sys/__init__.py +1 -0
- adijif/sys/s_plls.py +185 -0
- adijif/system.py +567 -0
- adijif/system_draw.py +65 -0
- adijif/types.py +151 -0
- adijif/utils.py +191 -0
- pyadi_jif-0.1.0.dist-info/METADATA +62 -0
- pyadi_jif-0.1.0.dist-info/RECORD +73 -0
- pyadi_jif-0.1.0.dist-info/WHEEL +6 -0
- pyadi_jif-0.1.0.dist-info/licenses/AUTHORS.rst +13 -0
- pyadi_jif-0.1.0.dist-info/licenses/LICENSE +277 -0
- pyadi_jif-0.1.0.dist-info/top_level.txt +1 -0
adijif/jesd.py
ADDED
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
"""JESD parameterization definitions and helper functions."""
|
|
2
|
+
|
|
3
|
+
from abc import ABCMeta, abstractmethod
|
|
4
|
+
from typing import Dict, List, Union
|
|
5
|
+
|
|
6
|
+
from adijif.solvers import CpoSolveResult
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class jesd(metaclass=ABCMeta):
|
|
10
|
+
"""JESD interface class to manage JESD notations and definitions."""
|
|
11
|
+
|
|
12
|
+
# Lane rate min/max defaulting to JESD spec (parts may differ)
|
|
13
|
+
bit_clock_min_available = {"jesd204b": 312.5e6, "jesd204c": 312.5e6}
|
|
14
|
+
bit_clock_max_available = {"jesd204b": 12.5e9, "jesd204c": 32e9}
|
|
15
|
+
|
|
16
|
+
solver = "CPLEX"
|
|
17
|
+
|
|
18
|
+
_parameters_to_return = [
|
|
19
|
+
"bit_clock",
|
|
20
|
+
"multiframe_clock",
|
|
21
|
+
"sample_clock",
|
|
22
|
+
"F",
|
|
23
|
+
"HD",
|
|
24
|
+
"K",
|
|
25
|
+
"L",
|
|
26
|
+
"M",
|
|
27
|
+
"Np",
|
|
28
|
+
"S",
|
|
29
|
+
"CS",
|
|
30
|
+
"jesd_class",
|
|
31
|
+
"converter_clock",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
_skip_clock_validation = False
|
|
35
|
+
|
|
36
|
+
def __init__(self, sample_clock: int, M: int, L: int, Np: int, K: int) -> None:
|
|
37
|
+
"""Initialize JESD device through link parameterization.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
sample_clock (int): Human readable string describing the exception.
|
|
41
|
+
M (int): Number of virtual converters
|
|
42
|
+
L (int): Number of lanes
|
|
43
|
+
Np (int): Number of bits per sample
|
|
44
|
+
K (int): Frames per multiframe
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
self.sample_clock = sample_clock
|
|
48
|
+
self.K = K
|
|
49
|
+
self.L = L
|
|
50
|
+
self.M = M
|
|
51
|
+
self.Np = Np
|
|
52
|
+
# self.S = S
|
|
53
|
+
|
|
54
|
+
def _check_clock_relations(self) -> None:
|
|
55
|
+
"""Check clock relations between clocks and JESD parameters."""
|
|
56
|
+
sc = self.sample_clock
|
|
57
|
+
assert sc == self.frame_clock * self.S, "sample_clock != S * frame_clock"
|
|
58
|
+
if self.jesd_class == "jesd204b":
|
|
59
|
+
assert sc == (self.bit_clock / 10 / self.F) * self.S
|
|
60
|
+
assert sc == (self.multiframe_clock * self.K * self.S)
|
|
61
|
+
|
|
62
|
+
def get_jesd_config(self, solution: CpoSolveResult = None) -> Dict:
|
|
63
|
+
"""Extract configurations from solver results.
|
|
64
|
+
|
|
65
|
+
Collect JESD related parameters, includes modes and clocks.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dict: Dictionary of JESD parameters
|
|
72
|
+
"""
|
|
73
|
+
if solution: # type: ignore
|
|
74
|
+
self.solution = solution
|
|
75
|
+
cfg = {p: getattr(self, p) for p in self._parameters_to_return}
|
|
76
|
+
cfg["jesd_mode"] = self._check_valid_jesd_mode()
|
|
77
|
+
return cfg
|
|
78
|
+
|
|
79
|
+
def validate_clocks(self) -> None:
|
|
80
|
+
"""Validate all clocks clock settings are within range."""
|
|
81
|
+
if not self._skip_clock_validation:
|
|
82
|
+
for name in ["bit", "sample"]:
|
|
83
|
+
clk = getattr(self, name + "_clock")
|
|
84
|
+
lim = getattr(self, name + "_clock_max")
|
|
85
|
+
assert clk <= lim, (
|
|
86
|
+
name + f" clock too fast for device {clk} (limit: {lim})"
|
|
87
|
+
)
|
|
88
|
+
lim = getattr(self, name + "_clock_min")
|
|
89
|
+
assert clk >= lim, (
|
|
90
|
+
name + f" clock too slow for device {clk} (limit: {lim})"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def bit_clock_min(self) -> Union[int, float]:
|
|
95
|
+
"""Get bit clock (lane rate) minimum based on JESD mode.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
int: bit clock in bits per second
|
|
99
|
+
"""
|
|
100
|
+
if isinstance(self.jesd_class, list):
|
|
101
|
+
return self.bit_clock_min_available["jesd204b"]
|
|
102
|
+
return self.bit_clock_min_available[self.jesd_class]
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def bit_clock_max(self) -> Union[int, float]:
|
|
106
|
+
"""Get bit clock (lane rate) maximum based on JESD mode.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
int: bit clock in bits per second
|
|
110
|
+
"""
|
|
111
|
+
if isinstance(self.jesd_class, list):
|
|
112
|
+
return self.bit_clock_max_available["jesd204b"]
|
|
113
|
+
return self.bit_clock_max_available[self.jesd_class]
|
|
114
|
+
|
|
115
|
+
_jesd_class = "jesd204b"
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def jesd_class(self) -> Union[str, List[str]]:
|
|
119
|
+
"""Get JESD selected mode. Wil be either jesd204b or jesd204c."""
|
|
120
|
+
return self._jesd_class
|
|
121
|
+
|
|
122
|
+
@jesd_class.setter
|
|
123
|
+
def jesd_class(self, value: str) -> None:
|
|
124
|
+
"""Set JESD selected mode and must be either jesd204b or jesd204c.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
value (str): String of JESD class and must be jesd204b or jesd204c
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
Exception: Invalid JESD class selected
|
|
131
|
+
"""
|
|
132
|
+
if value not in self.available_jesd_modes:
|
|
133
|
+
raise Exception(
|
|
134
|
+
f"Invalid JESD class. Valid are: {self.available_jesd_modes}"
|
|
135
|
+
)
|
|
136
|
+
self._jesd_class = value
|
|
137
|
+
if value == "jesd204b":
|
|
138
|
+
self._encoding = "8b10b"
|
|
139
|
+
else:
|
|
140
|
+
self._encoding = "64b66b"
|
|
141
|
+
|
|
142
|
+
def _check_jesd_config(self) -> None:
|
|
143
|
+
"""Check if bit clock is within JESD limits based on supported standard.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
Exception: bit clock (lane rate) too high for JESD mode or invalid
|
|
147
|
+
"""
|
|
148
|
+
if "jesd204c" in self.available_jesd_modes:
|
|
149
|
+
if self.bit_clock > 32e9:
|
|
150
|
+
raise Exception(
|
|
151
|
+
f"bit clock (lane rate) {self.bit_clock} too high for JESD204C"
|
|
152
|
+
)
|
|
153
|
+
elif "jesd204b" in self.available_jesd_modes:
|
|
154
|
+
if self.bit_clock > 12.5e9:
|
|
155
|
+
raise Exception(
|
|
156
|
+
f"bit clock (lane rate) {self.bit_clock} too high for JESD204B"
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
raise Exception(f"JESD mode(s) {self.available_jesd_modes}")
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
@abstractmethod
|
|
163
|
+
def available_jesd_modes(self) -> List[str]:
|
|
164
|
+
"""Available JESD modes supported by device.
|
|
165
|
+
|
|
166
|
+
Must be a list of strings
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
NotImplementedError: If child classes do not implement method/property
|
|
170
|
+
"""
|
|
171
|
+
raise NotImplementedError # pragma: no cover
|
|
172
|
+
|
|
173
|
+
""" CS: Control bits per conversion sample 0-3"""
|
|
174
|
+
_CS = 0
|
|
175
|
+
CS_available = [0, 1, 2, 3]
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def CS(self) -> Union[int, float]:
|
|
179
|
+
"""Get Control bits per conversion sample.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
int: Control bits per conversion sample
|
|
183
|
+
"""
|
|
184
|
+
return self._CS
|
|
185
|
+
|
|
186
|
+
@CS.setter
|
|
187
|
+
def CS(self, value: int) -> None:
|
|
188
|
+
"""Set Control bits per conversion sample.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
value (int): Control bits per conversion sample
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
Exception: CS not an integer or not in range
|
|
195
|
+
"""
|
|
196
|
+
if int(value) != value:
|
|
197
|
+
raise Exception("CS must be an integer")
|
|
198
|
+
if value not in self.CS_available:
|
|
199
|
+
raise Exception("CS not in range for device")
|
|
200
|
+
self._CS = value
|
|
201
|
+
|
|
202
|
+
""" CF: Control word per frame clock period per link 0-32 """
|
|
203
|
+
_CF = 0
|
|
204
|
+
CF_available = [0, 1]
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def CF(self) -> Union[int, float]:
|
|
208
|
+
"""Get Control words per frame clock period per link.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
int: Control words per frame clock period per link
|
|
212
|
+
"""
|
|
213
|
+
return self._CF
|
|
214
|
+
|
|
215
|
+
@CF.setter
|
|
216
|
+
def CF(self, value: int) -> None:
|
|
217
|
+
"""Set Control words per frame clock period per link.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
value (int): Control words per frame clock period per link
|
|
221
|
+
|
|
222
|
+
Raises:
|
|
223
|
+
Exception: CF not an integer or not in range
|
|
224
|
+
"""
|
|
225
|
+
if int(value) != value:
|
|
226
|
+
raise Exception("CF must be an integer")
|
|
227
|
+
if value not in self.CF_available:
|
|
228
|
+
raise Exception("CF not in range for device")
|
|
229
|
+
self._CF = value
|
|
230
|
+
|
|
231
|
+
# Encoding functions
|
|
232
|
+
|
|
233
|
+
encodings_n = {"8b10b": 8, "64b66b": 64}
|
|
234
|
+
encodings_d = {"8b10b": 10, "64b66b": 66}
|
|
235
|
+
_encoding = "8b10b"
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def encoding(self) -> str:
|
|
239
|
+
"""Get JESD FEC encoding.
|
|
240
|
+
|
|
241
|
+
Current options are: "8b10b", "64b66b"
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
str: String of supported encodings.
|
|
245
|
+
"""
|
|
246
|
+
return self._encoding
|
|
247
|
+
|
|
248
|
+
@encoding.setter
|
|
249
|
+
def encoding(self, value: str) -> None:
|
|
250
|
+
"""Set JESD FEC encoding.
|
|
251
|
+
|
|
252
|
+
Current options are: "8b10b", "64b66b"
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
value (str): String of desired encoding to use
|
|
256
|
+
|
|
257
|
+
Raises:
|
|
258
|
+
Exception: If encoding selected that is not supported
|
|
259
|
+
"""
|
|
260
|
+
if self._check_encoding(value):
|
|
261
|
+
raise Exception(
|
|
262
|
+
"JESD encoding not possible due to available modes: {}".format(
|
|
263
|
+
self.available_jesd_modes
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
self._encoding = value
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def encoding_d(self) -> Union[int, float]:
|
|
270
|
+
"""Get JESD FEC encoding denominator.
|
|
271
|
+
|
|
272
|
+
Current options are: 10 or 66
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
int: Denominator of link encoding.
|
|
276
|
+
"""
|
|
277
|
+
return self.encodings_d[self._encoding]
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def encoding_n(self) -> Union[int, float]:
|
|
281
|
+
"""Get JESD FEC encoding numerator.
|
|
282
|
+
|
|
283
|
+
Current options are: 8 or 64
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
int: Numerator of link encoding.
|
|
287
|
+
"""
|
|
288
|
+
return self.encodings_n[self._encoding]
|
|
289
|
+
|
|
290
|
+
def _check_encoding(self, encode: str) -> bool:
|
|
291
|
+
if "jesd204c" in self.available_jesd_modes:
|
|
292
|
+
allowed_encodings = ["8b10b", "64b66b"]
|
|
293
|
+
else:
|
|
294
|
+
allowed_encodings = ["8b10b"]
|
|
295
|
+
return encode not in allowed_encodings
|
|
296
|
+
|
|
297
|
+
# SCALERS
|
|
298
|
+
@property
|
|
299
|
+
@abstractmethod
|
|
300
|
+
def K_available(self) -> List[int]:
|
|
301
|
+
"""Allowable K settings for device.
|
|
302
|
+
|
|
303
|
+
Must be a list ints
|
|
304
|
+
|
|
305
|
+
Raises:
|
|
306
|
+
NotImplementedError: If child classes do not implement method/property
|
|
307
|
+
"""
|
|
308
|
+
raise NotImplementedError # pragma: no cover
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
@abstractmethod
|
|
312
|
+
def L_available(self) -> List[int]:
|
|
313
|
+
"""Allowable L settings for device.
|
|
314
|
+
|
|
315
|
+
Must be a list ints
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
NotImplementedError: If child classes do not implement method/property
|
|
319
|
+
"""
|
|
320
|
+
raise NotImplementedError # pragma: no cover
|
|
321
|
+
|
|
322
|
+
@property
|
|
323
|
+
@abstractmethod
|
|
324
|
+
def M_available(self) -> List[int]:
|
|
325
|
+
"""Allowable M settings for device.
|
|
326
|
+
|
|
327
|
+
Must be a list ints
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
NotImplementedError: If child classes do not implement method/property
|
|
331
|
+
"""
|
|
332
|
+
raise NotImplementedError # pragma: no cover
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
@abstractmethod
|
|
336
|
+
def N_available(self) -> List[int]:
|
|
337
|
+
"""Allowable N settings for device.
|
|
338
|
+
|
|
339
|
+
Must be a list ints
|
|
340
|
+
|
|
341
|
+
Raises:
|
|
342
|
+
NotImplementedError: If child classes do not implement method/property
|
|
343
|
+
"""
|
|
344
|
+
raise NotImplementedError # pragma: no cover
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
@abstractmethod
|
|
348
|
+
def Np_available(self) -> List[int]:
|
|
349
|
+
"""Allowable Np settings for device.
|
|
350
|
+
|
|
351
|
+
Must be a list ints
|
|
352
|
+
|
|
353
|
+
Raises:
|
|
354
|
+
NotImplementedError: If child classes do not implement method/property
|
|
355
|
+
"""
|
|
356
|
+
raise NotImplementedError # pragma: no cover
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
@abstractmethod
|
|
360
|
+
def F_available(self) -> List[int]:
|
|
361
|
+
"""Allowable F settings for device.
|
|
362
|
+
|
|
363
|
+
Must be a list ints
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
NotImplementedError: If child classes do not implement method/property
|
|
367
|
+
"""
|
|
368
|
+
raise NotImplementedError # pragma: no cover
|
|
369
|
+
|
|
370
|
+
""" bits
|
|
371
|
+
Usually:
|
|
372
|
+
32 for JESD204B
|
|
373
|
+
64 for JESD204C
|
|
374
|
+
"""
|
|
375
|
+
_data_path_width = 32
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def data_path_width(self) -> Union[int, float]:
|
|
379
|
+
"""Get JESD data path width in bits.
|
|
380
|
+
|
|
381
|
+
Current options are: 32 (204B) and 64 (204C)
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
int: Numerator of link encoding.
|
|
385
|
+
"""
|
|
386
|
+
return self._data_path_width
|
|
387
|
+
|
|
388
|
+
@data_path_width.setter
|
|
389
|
+
def data_path_width(self, value: int) -> None:
|
|
390
|
+
"""Set JESD data path width in bits.
|
|
391
|
+
|
|
392
|
+
Current options are: 32 (204B) and 64 (204C)
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
value (int): Data path width in bits
|
|
396
|
+
|
|
397
|
+
Raises:
|
|
398
|
+
Exception: If DMA width is not an integer
|
|
399
|
+
"""
|
|
400
|
+
if int(value) != value:
|
|
401
|
+
raise Exception("data_path_width must be an integer")
|
|
402
|
+
self._data_path_width = value
|
|
403
|
+
|
|
404
|
+
""" HD: High-density mode (Single sample split over multiple lanes)"""
|
|
405
|
+
# HD_min = 0
|
|
406
|
+
# HD_max = 1
|
|
407
|
+
_HD = 0
|
|
408
|
+
HD_available = [0, 1]
|
|
409
|
+
|
|
410
|
+
@property
|
|
411
|
+
def HD(self) -> Union[int, float]:
|
|
412
|
+
"""Get High density mode.
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
int: High density mode
|
|
416
|
+
"""
|
|
417
|
+
return self._HD
|
|
418
|
+
|
|
419
|
+
@HD.setter
|
|
420
|
+
def HD(self, value: int) -> None:
|
|
421
|
+
"""Set High density mode.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
value (int): High density mode
|
|
425
|
+
|
|
426
|
+
Raises:
|
|
427
|
+
Exception: HD not an integer or not in range
|
|
428
|
+
"""
|
|
429
|
+
if int(value) != value:
|
|
430
|
+
raise Exception("HD must be an integer")
|
|
431
|
+
if value not in self.HD_available:
|
|
432
|
+
raise Exception("HD not in range for device")
|
|
433
|
+
self._HD = value
|
|
434
|
+
|
|
435
|
+
""" K: Frames per multiframe
|
|
436
|
+
17/F <= K <= 32
|
|
437
|
+
"""
|
|
438
|
+
# K_min = 4
|
|
439
|
+
# K_max = 32
|
|
440
|
+
# K_available = [4, 8, 12, 16, 20, 24, 28, 32]
|
|
441
|
+
_K = 4
|
|
442
|
+
|
|
443
|
+
@property
|
|
444
|
+
def K(self) -> Union[int, float]:
|
|
445
|
+
"""Get Frames per multiframe.
|
|
446
|
+
|
|
447
|
+
17/F <= K <= 32, is generally a multiple of 2
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
int: Number of frames per multiframe
|
|
451
|
+
"""
|
|
452
|
+
return self._K
|
|
453
|
+
|
|
454
|
+
@K.setter
|
|
455
|
+
def K(self, value: int) -> None:
|
|
456
|
+
"""Set Frames per multiframe.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
value (int): Frames per multiframe
|
|
460
|
+
|
|
461
|
+
Raises:
|
|
462
|
+
Exception: K not an integer or not in range
|
|
463
|
+
"""
|
|
464
|
+
if int(value) != value:
|
|
465
|
+
raise Exception("K must be an integer")
|
|
466
|
+
if value not in self.K_available:
|
|
467
|
+
raise Exception("K not in range for device")
|
|
468
|
+
self._K = value
|
|
469
|
+
|
|
470
|
+
@property
|
|
471
|
+
def D(self) -> Union[int, float]:
|
|
472
|
+
"""FIXME."""
|
|
473
|
+
return self._data_path_width * self.encoding_d / self.encoding_n
|
|
474
|
+
|
|
475
|
+
""" S: Samples per converter per frame"""
|
|
476
|
+
_S = 1
|
|
477
|
+
|
|
478
|
+
@property
|
|
479
|
+
def S(self) -> Union[int, float]:
|
|
480
|
+
"""Get Samples per converter per frame.
|
|
481
|
+
|
|
482
|
+
S == F/(M*Np) * encoding_p * L
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
int: Samples per converter per frame
|
|
486
|
+
"""
|
|
487
|
+
return self._S
|
|
488
|
+
|
|
489
|
+
@S.setter
|
|
490
|
+
def S(self, value: int) -> None:
|
|
491
|
+
"""Set samples per converter per frame.
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
value (int): Samples per converter per frame
|
|
495
|
+
|
|
496
|
+
Raises:
|
|
497
|
+
Exception: S not an integer or not in range
|
|
498
|
+
"""
|
|
499
|
+
if int(value) != value:
|
|
500
|
+
raise Exception("S must be an integer")
|
|
501
|
+
if value not in self.S_available:
|
|
502
|
+
raise Exception(f"S not in range for device. Got {value}")
|
|
503
|
+
self._S = value
|
|
504
|
+
|
|
505
|
+
""" L: Lanes per link """
|
|
506
|
+
# L_min = 1
|
|
507
|
+
# L_max = 8
|
|
508
|
+
# L_available = [1, 2, 4, 8]
|
|
509
|
+
_L = 1
|
|
510
|
+
|
|
511
|
+
@property
|
|
512
|
+
def L(self) -> Union[int, float]:
|
|
513
|
+
"""Get lanes per link.
|
|
514
|
+
|
|
515
|
+
Generally a multiple of 2
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
int: Number of frames per multiframe
|
|
519
|
+
"""
|
|
520
|
+
return self._L
|
|
521
|
+
|
|
522
|
+
@L.setter
|
|
523
|
+
def L(self, value: int) -> None:
|
|
524
|
+
"""Set lanes per link.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
value (int): Lanes per link
|
|
528
|
+
|
|
529
|
+
Raises:
|
|
530
|
+
Exception: L not an integer or not in range
|
|
531
|
+
"""
|
|
532
|
+
if int(value) != value:
|
|
533
|
+
raise Exception("L must be an integer")
|
|
534
|
+
if value not in self.L_available:
|
|
535
|
+
raise Exception("L not in range for device")
|
|
536
|
+
self._L = value
|
|
537
|
+
|
|
538
|
+
""" M: Number of virtual converters """
|
|
539
|
+
# M_min = 1
|
|
540
|
+
# M_max = 8
|
|
541
|
+
# M_available = [1, 2, 4, 8, 16, 32]
|
|
542
|
+
_M = 1
|
|
543
|
+
|
|
544
|
+
@property
|
|
545
|
+
def M(self) -> Union[int, float]:
|
|
546
|
+
"""Get number of virtual converters.
|
|
547
|
+
|
|
548
|
+
Generally a power of 2
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
int: Number of frames per multiframe
|
|
552
|
+
"""
|
|
553
|
+
return self._M
|
|
554
|
+
|
|
555
|
+
@M.setter
|
|
556
|
+
def M(self, value: int) -> None:
|
|
557
|
+
"""Set number of virtual converters.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
value (int): Number of virtual converters
|
|
561
|
+
|
|
562
|
+
Raises:
|
|
563
|
+
Exception: M not an integer or not in range
|
|
564
|
+
"""
|
|
565
|
+
if int(value) != value:
|
|
566
|
+
raise Exception("M must be an integer")
|
|
567
|
+
if value not in self.M_available:
|
|
568
|
+
raise Exception("M not in range for device")
|
|
569
|
+
self._M = value
|
|
570
|
+
|
|
571
|
+
""" N: Number of non-dummy bits per sample """
|
|
572
|
+
# N_min = 12
|
|
573
|
+
# N_max = 16
|
|
574
|
+
# N_available = [12, 14, 16]
|
|
575
|
+
_N = 12
|
|
576
|
+
|
|
577
|
+
@property
|
|
578
|
+
def N(self) -> Union[int, float]:
|
|
579
|
+
"""Get number of non-dummy bits per sample.
|
|
580
|
+
|
|
581
|
+
Generally a multiple of 2
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
int: Number of non-dummy bits per sample
|
|
585
|
+
"""
|
|
586
|
+
return self._N
|
|
587
|
+
|
|
588
|
+
@N.setter
|
|
589
|
+
def N(self, value: int) -> None:
|
|
590
|
+
"""Set number of non-dummy bits per sample.
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
value (int): Number of non-dummy bits per sample
|
|
594
|
+
|
|
595
|
+
Raises:
|
|
596
|
+
Exception: N not an integer or not in range
|
|
597
|
+
"""
|
|
598
|
+
if int(value) != value:
|
|
599
|
+
raise Exception("N must be an integer")
|
|
600
|
+
if value not in self.N_available:
|
|
601
|
+
raise Exception("N not in range for device")
|
|
602
|
+
self._N = value
|
|
603
|
+
|
|
604
|
+
""" Np: Number of bits per sample """
|
|
605
|
+
# Np_min = 12
|
|
606
|
+
# Np_max = 16
|
|
607
|
+
# Np_available = [12, 14, 16]
|
|
608
|
+
_Np = 16
|
|
609
|
+
|
|
610
|
+
@property
|
|
611
|
+
def Np(self) -> Union[int, float]:
|
|
612
|
+
"""Get number of bits per sample.
|
|
613
|
+
|
|
614
|
+
Generally a multiple of 2
|
|
615
|
+
|
|
616
|
+
Returns:
|
|
617
|
+
int: Number of bits per sample
|
|
618
|
+
"""
|
|
619
|
+
return self._Np
|
|
620
|
+
|
|
621
|
+
@Np.setter
|
|
622
|
+
def Np(self, value: int) -> None:
|
|
623
|
+
"""Set number of bits per sample.
|
|
624
|
+
|
|
625
|
+
Args:
|
|
626
|
+
value (int): Number of bits per sample
|
|
627
|
+
|
|
628
|
+
Raises:
|
|
629
|
+
Exception: Np not an integer or not in range
|
|
630
|
+
"""
|
|
631
|
+
if int(value) != value:
|
|
632
|
+
raise Exception("Np must be an integer")
|
|
633
|
+
if value not in self.Np_available:
|
|
634
|
+
raise Exception("Np not in range for device")
|
|
635
|
+
self._Np = value
|
|
636
|
+
|
|
637
|
+
# DERIVED SCALERS
|
|
638
|
+
""" F: Octets per frame per link
|
|
639
|
+
This is read-only since it depends on L,M,Np,S, and encoding
|
|
640
|
+
"""
|
|
641
|
+
# F_min = 1
|
|
642
|
+
# F_max = 16
|
|
643
|
+
# F_available = [1, 2, 4, 8, 16]
|
|
644
|
+
_F = 1
|
|
645
|
+
|
|
646
|
+
@property
|
|
647
|
+
def F(self) -> Union[int, float]:
|
|
648
|
+
"""Get octets per frame per link.
|
|
649
|
+
|
|
650
|
+
Generally a power of 2
|
|
651
|
+
|
|
652
|
+
Returns:
|
|
653
|
+
int: Number of octets per frame per link
|
|
654
|
+
"""
|
|
655
|
+
return self._F
|
|
656
|
+
|
|
657
|
+
@F.setter
|
|
658
|
+
def F(self, value: int) -> None:
|
|
659
|
+
"""Set octets per frame per link.
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
value (int): Number of octets per frame per link
|
|
663
|
+
|
|
664
|
+
Raises:
|
|
665
|
+
Exception: F not an integer or not in range
|
|
666
|
+
"""
|
|
667
|
+
if int(value) != value:
|
|
668
|
+
raise Exception("F must be an integer")
|
|
669
|
+
if value not in self.F_available:
|
|
670
|
+
raise Exception("F not in range for device")
|
|
671
|
+
self._F = value
|
|
672
|
+
|
|
673
|
+
# CLOCKS
|
|
674
|
+
""" sample_clock: Data rate after decimation stages in Samples/second """
|
|
675
|
+
|
|
676
|
+
_sample_clock = 122.88e6
|
|
677
|
+
|
|
678
|
+
@property
|
|
679
|
+
def sample_clock(self) -> Union[int, float]:
|
|
680
|
+
"""Data rate after decimation stages in Samples/second.
|
|
681
|
+
|
|
682
|
+
Returns:
|
|
683
|
+
int: Data rate in samples per second
|
|
684
|
+
"""
|
|
685
|
+
return self._sample_clock
|
|
686
|
+
|
|
687
|
+
@sample_clock.setter
|
|
688
|
+
def sample_clock(self, value: int) -> None:
|
|
689
|
+
"""Data rate after decimation stages in Samples/second.
|
|
690
|
+
|
|
691
|
+
Args:
|
|
692
|
+
value (int): Number of octets per frame per link
|
|
693
|
+
"""
|
|
694
|
+
self._sample_clock = value
|
|
695
|
+
|
|
696
|
+
@property
|
|
697
|
+
def frame_clock(self) -> Union[int, float]:
|
|
698
|
+
"""frame_clock in frames per second.
|
|
699
|
+
|
|
700
|
+
frame_clock == sample_clock / S
|
|
701
|
+
|
|
702
|
+
Returns:
|
|
703
|
+
int: Data rate in samples per second
|
|
704
|
+
"""
|
|
705
|
+
return self.sample_clock / self.S
|
|
706
|
+
|
|
707
|
+
@property
|
|
708
|
+
def multiframe_clock(self) -> Union[int, float]:
|
|
709
|
+
"""multiframe_clock: aka LMFC in frames per multiframe.
|
|
710
|
+
|
|
711
|
+
multiframe_clock == frame_clock / K
|
|
712
|
+
|
|
713
|
+
Returns:
|
|
714
|
+
int: Frames per multiframe
|
|
715
|
+
"""
|
|
716
|
+
return self.frame_clock / self.K
|
|
717
|
+
|
|
718
|
+
@property
|
|
719
|
+
def bit_clock(self) -> Union[int, float]:
|
|
720
|
+
"""bit_clock: aka line rate aka lane rate.
|
|
721
|
+
|
|
722
|
+
bit_clock == (M * S * Np * encoding_d/encoding_n * frame_clock) / L
|
|
723
|
+
|
|
724
|
+
Returns:
|
|
725
|
+
int: Bits per second aka lane rate
|
|
726
|
+
"""
|
|
727
|
+
return (
|
|
728
|
+
(self.M / self.L)
|
|
729
|
+
* self.Np
|
|
730
|
+
* (self.encoding_d / self.encoding_n)
|
|
731
|
+
* self.sample_clock
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
@bit_clock.setter
|
|
735
|
+
def bit_clock(self, value: int) -> None:
|
|
736
|
+
"""bit_clock: aka line rate aka lane rate.
|
|
737
|
+
|
|
738
|
+
bit_clock == (M * S * Np * encoding_d/encoding_n * frame_clock) / L
|
|
739
|
+
|
|
740
|
+
Args:
|
|
741
|
+
value (int): Bits per second aka lane rate
|
|
742
|
+
"""
|
|
743
|
+
# This actually sets sample_clock
|
|
744
|
+
# frame_clock = bit_clock*L*encoding_n/encoding_d / (M*S*Np)
|
|
745
|
+
# sample_clock = bit_clock*L*encoding_n/encoding_d / (M*Np)
|
|
746
|
+
value_cal = (
|
|
747
|
+
value * self.L * self.encoding_n / self.encoding_d / (self.M * self.Np)
|
|
748
|
+
)
|
|
749
|
+
self._sample_clock = value_cal
|
|
750
|
+
|
|
751
|
+
@property
|
|
752
|
+
def device_clock(self) -> Union[int, float]:
|
|
753
|
+
"""device_clock is the lane rate over D.
|
|
754
|
+
|
|
755
|
+
device_clock == bit_clock / D
|
|
756
|
+
|
|
757
|
+
Returns:
|
|
758
|
+
int: bits per second per device
|
|
759
|
+
"""
|
|
760
|
+
return self.bit_clock / self.D
|