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.
Files changed (73) hide show
  1. adijif/__init__.py +32 -0
  2. adijif/adijif.py +1 -0
  3. adijif/cli.py +21 -0
  4. adijif/clocks/__init__.py +10 -0
  5. adijif/clocks/ad9523.py +321 -0
  6. adijif/clocks/ad9523_1_bf.py +91 -0
  7. adijif/clocks/ad9528.py +444 -0
  8. adijif/clocks/ad9528_bf.py +70 -0
  9. adijif/clocks/ad9545.py +553 -0
  10. adijif/clocks/clock.py +153 -0
  11. adijif/clocks/hmc7044.py +558 -0
  12. adijif/clocks/hmc7044_bf.py +68 -0
  13. adijif/clocks/ltc6952.py +624 -0
  14. adijif/clocks/ltc6952_bf.py +67 -0
  15. adijif/clocks/ltc6953.py +509 -0
  16. adijif/common.py +70 -0
  17. adijif/converters/__init__.py +3 -0
  18. adijif/converters/ad9081.py +679 -0
  19. adijif/converters/ad9081_dp.py +206 -0
  20. adijif/converters/ad9081_util.py +124 -0
  21. adijif/converters/ad9084.py +588 -0
  22. adijif/converters/ad9084_dp.py +111 -0
  23. adijif/converters/ad9084_draw.py +203 -0
  24. adijif/converters/ad9084_util.py +365 -0
  25. adijif/converters/ad9144.py +316 -0
  26. adijif/converters/ad9144_bf.py +44 -0
  27. adijif/converters/ad9680.py +201 -0
  28. adijif/converters/ad9680_bf.py +43 -0
  29. adijif/converters/ad9680_draw.py +184 -0
  30. adijif/converters/adc.py +83 -0
  31. adijif/converters/adrv9009.py +426 -0
  32. adijif/converters/adrv9009_bf.py +43 -0
  33. adijif/converters/adrv9009_util.py +89 -0
  34. adijif/converters/converter.py +399 -0
  35. adijif/converters/dac.py +85 -0
  36. adijif/converters/resources/AD9084_JTX_JRX.xlsx +0 -0
  37. adijif/converters/resources/ad9081_JRx_204B.csv +180 -0
  38. adijif/converters/resources/ad9081_JRx_204C.csv +411 -0
  39. adijif/converters/resources/ad9081_JTx_204B.csv +1488 -0
  40. adijif/converters/resources/ad9081_JTx_204C.csv +1064 -0
  41. adijif/converters/resources/full_rx_mode_table_ad9081.csv +1904 -0
  42. adijif/converters/resources/full_tx_mode_table_ad9081.csv +994 -0
  43. adijif/d2/__init__.py +26 -0
  44. adijif/d2/d2lib.h +81 -0
  45. adijif/draw.py +498 -0
  46. adijif/fpgas/__init__.py +1 -0
  47. adijif/fpgas/fpga.py +64 -0
  48. adijif/fpgas/xilinx/__init__.py +1143 -0
  49. adijif/fpgas/xilinx/bf.py +101 -0
  50. adijif/fpgas/xilinx/pll.py +232 -0
  51. adijif/fpgas/xilinx/sevenseries.py +531 -0
  52. adijif/fpgas/xilinx/ultrascaleplus.py +485 -0
  53. adijif/fpgas/xilinx/xilinx_draw.py +516 -0
  54. adijif/gekko_trans.py +295 -0
  55. adijif/jesd.py +760 -0
  56. adijif/plls/__init__.py +3 -0
  57. adijif/plls/adf4030.py +259 -0
  58. adijif/plls/adf4371.py +419 -0
  59. adijif/plls/adf4382.py +581 -0
  60. adijif/plls/pll.py +103 -0
  61. adijif/solvers.py +54 -0
  62. adijif/sys/__init__.py +1 -0
  63. adijif/sys/s_plls.py +185 -0
  64. adijif/system.py +567 -0
  65. adijif/system_draw.py +65 -0
  66. adijif/types.py +151 -0
  67. adijif/utils.py +191 -0
  68. pyadi_jif-0.1.0.dist-info/METADATA +62 -0
  69. pyadi_jif-0.1.0.dist-info/RECORD +73 -0
  70. pyadi_jif-0.1.0.dist-info/WHEEL +6 -0
  71. pyadi_jif-0.1.0.dist-info/licenses/AUTHORS.rst +13 -0
  72. pyadi_jif-0.1.0.dist-info/licenses/LICENSE +277 -0
  73. 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